pax_global_header00006660000000000000000000000064150165437200014515gustar00rootroot0000000000000052 comment=5d5c4ca9ec1ed92607799bafa2b0d82a24881995 qdmr-0.12.3/000077500000000000000000000000001501654372000125435ustar00rootroot00000000000000qdmr-0.12.3/.github/000077500000000000000000000000001501654372000141035ustar00rootroot00000000000000qdmr-0.12.3/.github/workflows/000077500000000000000000000000001501654372000161405ustar00rootroot00000000000000qdmr-0.12.3/.github/workflows/clang.yml000066400000000000000000000027071501654372000177550ustar00rootroot00000000000000name: build-linux-clang-14 on: push: branches: [ "master" ] pull_request: branches: [ "master" ] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release CC: /usr/bin/clang-14 CXX: /usr/bin/clang++-14 jobs: build: # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. # You can convert this to a matrix build if you need cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Update APT run: sudo apt-get update - name: Install Dependencies run: sudo apt-get install clang-14 qttools5-dev qtbase5-dev qtbase5-dev-tools libqt5serialport5-dev qtpositioning5-dev libusb-1.0-0-dev libyaml-cpp-dev - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} qdmr-0.12.3/.github/workflows/flatpak.yml000066400000000000000000000007361501654372000203130ustar00rootroot00000000000000on: push: branches: [master] name: Flatpak jobs: flatpak: name: "Flatpak" runs-on: ubuntu-latest container: image: ghcr.io/flathub-infra/flatpak-github-actions:kde-5.15-24.08 options: --privileged steps: - uses: actions/checkout@v4 - uses: flatpak/flatpak-github-actions/flatpak-builder@v6 with: bundle: qdmr.flatpak manifest-path: de.darc.dm3mat.qdmr.yaml cache-key: flatpak-builder-${{ github.sha }} qdmr-0.12.3/.github/workflows/unittests.yml000066400000000000000000000032501501654372000207250ustar00rootroot00000000000000name: unittest-linux-gcc on: push: branches: [ "master", "devel" ] pull_request: branches: [ "master", "devel" ] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release jobs: build: # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. # You can convert this to a matrix build if you need cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Update APT run: sudo apt-get update - name: Install Dependencies run: sudo apt-get install qttools5-dev qtbase5-dev qtbase5-dev-tools libqt5serialport5-dev qtpositioning5-dev libusb-1.0-0-dev libyaml-cpp-dev - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_TESTS=On - name: Build # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - name: Test working-directory: ${{github.workspace}}/build # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C ${{env.BUILD_TYPE}} qdmr-0.12.3/.gitignore000066400000000000000000000005761501654372000145430ustar00rootroot00000000000000/doc/html/ /doc/latex/ /doc/reveng/cotre/*.pcapng.gz /doc/reveng/gd77/*.pcapng.gz /doc/manual/manual.pdf /doc/manual/manual_combined.xml /doc/manual/intro/fig/*.aux /doc/manual/intro/fig/*.log /doc/manual/intro/fig/*.png /doc/manual/html/*.html /doc/manual/html/manual.xml /doc/manual/html/docbook.css /doc/manual/html/fig /parts/ /prime/ /snap/ /stage/ CMakeLists.txt.user /build/ qdmr-0.12.3/.gitmodules000066400000000000000000000000001501654372000147060ustar00rootroot00000000000000qdmr-0.12.3/CMakeLists.txt000066400000000000000000000103471501654372000153100ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0.0) project(qdmr VERSION 0.12.3) set(RELEASE_SUFFIX "") option(BUILD_TESTS "Build test programs" OFF) option(BUILD_DOCS "Build API documentation" OFF) option(BUILD_MAN "Build man page for dmrconf" OFF) option(INSTALL_UDEV_RULES "Install udev rules file." ON) option(INSTALL_UDEV_PATH "Install path of udev rules file." "/etc/udev/rules.d") option(INSTALL_APPSTREAM_DATA "Install AppStream metainfo file." ON) option(INSTALL_BUNDLE "Installs QDMR as an AppBundle under MacOS X" OFF) option(BUNDLE_PATH "Where to install the MacOS X application bundle." "~/Applications") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake) find_package(Qt5Core REQUIRED) find_package(Qt5Widgets REQUIRED) find_package(Qt5UiTools REQUIRED) find_package(Qt5Network REQUIRED) find_package(Qt5SerialPort REQUIRED) find_package(Qt5Positioning REQUIRED) find_package(Qt5LinguistTools REQUIRED) find_package(LIBUSB_1 REQUIRED) find_package(YAMLCPP REQUIRED) if (${BUILD_MAN}) find_program(XSLTPROC_EXECUTABLE xsltproc DOC "xsltproc for man-page generation." REQUIRED) endif(${BUILD_MAN}) if (${BUILD_DOCS}) find_package(Doxygen REQUIRED dot) endif(${BUILD_DOCS}) if (${BUILD_TESTS}) find_package(Qt5Test REQUIRED) endif(${BUILD_TESTS}) ADD_DEFINITIONS(${Qt5Widgets_DEFINITIONS}) #add_definitions("-DQT_EVENT_DISPATCHER_CORE_FOUNDATION=1") set(CMAKE_C_STANDARD 99) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) INCLUDE_DIRECTORIES(${Qt5Core_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${Qt5Widgets_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${Qt5UiTools_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${Qt5Network_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${Qt5SerialPort_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${Qt5Positioning_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${Qt5Test_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${LIBUSB_1_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${YAMLCPP_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/lib) INCLUDE_DIRECTORIES(${PROJECT_BINARY_DIR}/lib) LINK_DIRECTORIES(${PROJECT_BINARY_DIR}/src) set(CORE_LIBS ${Qt5Core_LIBRARIES} ${Qt5Core_QTMAIN_LIBRARIES} ${Qt5Network_LIBRARIES} ${Qt5Positioning_LIBRARIES} ${Qt5SerialPort_LIBRARIES} ${LIBUSB_1_LIBRARIES} ${YAMLCPP_LIBRARIES}) if (${BUILD_TESTS}) set(CORE_LIBS ${CORE_LIBS} ${Qt5Test_LIBRARIES}) endif(${BUILD_TESTS}) set(LIBS ${CORE_LIBS} ${Qt5Widgets_LIBRARIES} ${Qt5UiTools_LIBRARIES}) IF (UNIX AND APPLE) SET(CORE_LIBS ${CORE_LIBS} "-framework IOKit -framework CoreFoundation") ENDIF(UNIX AND APPLE) set(PROJECT_VERSION_STRING "\"${PROJECT_VERSION}\"") message(STATUS "Build version ${PROJECT_VERSION}") # Set compiler flags set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS} -Wall -Wsign-compare") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -ggdb -fstack-protector -Wextra") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -Wextra") # Get default install directories under Linux IF(UNIX AND NOT APPLE) INCLUDE(GNUInstallDirs) ENDIF(UNIX AND NOT APPLE) IF(UNIX AND APPLE) SET(CMAKE_INSTALL_LIBDIR "lib") SET(CMAKE_INSTALL_FULL_LIBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") SET(CMAKE_INSTALL_INCLUDEDIR "include") SET(CMAKE_INSTALL_FULL_INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") # Set RPATH under MacOS SET(CMAKE_SKIP_RPATH FALSE) SET(CMAKE_SKIP_BUILD_RPATH FALSE) SET(CMAKE_SKIP_INSTALL_RPATH FALSE) SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) message(STATUS "RPATH: ${CMAKE_INSTALL_RPATH}") ENDIF(UNIX AND APPLE) # Directory to install translations to SET(LOCALE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/share/locale/") # Sources... add_subdirectory(lib) add_subdirectory(cli) add_subdirectory(src) add_subdirectory(doc) add_subdirectory(dist) if(BUILD_TESTS) enable_testing() add_subdirectory(test) endif(BUILD_TESTS) # Source distribution packages: set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}${RELEASE_SUFFIX}") set(CPACK_SOURCE_IGNORE_FILES "/build/;/doc/html;/doc/latex;/doc/reveng;/doc/dmr-intro;/examples/*.dfu;/.git/;~$;.qm;*.user$;${CPACK_SOURCE_IGNORE_FILES}") include(CPack) qdmr-0.12.3/LICENSE000066400000000000000000001045151501654372000135560ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state 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 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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. But first, please read . qdmr-0.12.3/README.md000066400000000000000000000133301501654372000140220ustar00rootroot00000000000000# QDMR, a GUI application and command line tool to program DMR radios Translation status ![qdmr channel editor](https://raw.githubusercontent.com/hmatuschek/qdmr/master/doc/fig/qdmr-channels.png "The qdmr CPS software.") *qdmr* is a graphical user interface (GUI) application that allows to program several types of DMR radios. To this end, it aims at being a more universal codeplug programming software (CPS) compared to the device and even revision specific CPSs provided by the manufacturers. The goal of this project is to provide a **single**, **comfortable**, [**well-documented**](https://dm3mat.darc.de/qdmr/manual/) and **platform-independent** CPS for several types of (mainly Chinese) DMR radios. ## Supported Radios Currently, there are only few radios that are supported * Open GD77 firmware (since version 0.4.0) * Radioddity GD77 (since version 0.8.1) * Radioddity GD73 (since version 0.12.0) * Baofeng/Radioddity RD-5R & RD-5R+ (since version 0.2.0) * TYT MD-390 / Retevis RT8 (since version 0.9.0) * TYT MD-UV380 (since version 0.9.0) * TYT MD-UV390 / Retevis RT3S (since version 0.3.0) * TYT MD-2017 / Retevis RT82 (since version 0.9.0) * Anytone AT-D878UV (since version 0.5.0) * Anytone AT-D868UVE (since version 0.7.0) * Anytone AT-D878UVII (since version 0.8.0) * Anytone AT-D578UV (since version 0.8.0) * BTECH DM-1701 / Retevis RT84 (since version 0.10.0) * BTECH BF-1801A6 (since version 0.12.0) * BTECH DMR-6x2 (since version 0.11.0) A more [detailed list](https://dm3mat.darc.de/qdmr/#dev) is also available. ## Questions? * If you find any bugs or have suggestions to improve qdmr, consider [opening an issue](https://github.com/hmatuschek/qdmr/issues/new) or participate in one of the [discussions](https://github.com/hmatuschek/qdmr/discussions). * If you want to help translating qdmr in your language, checkout [qdmr's weblate project](https://translate.codeberg.org/projects/qdmr/graphical-user-interface/). * There is also a *Matrix* chat at [#qdmr:darc.de](https://matrix.to/#/#qdmr:darc.de). * You can also follow me at [mastodon](https://mastodon.radio/@dm3mat), where I usually announce new releases. ## Ecosystem As *qdmr* gets more and more popular with Linux HAMs, the ecosystem around it grows too. These are tools, that make your day-to-day useage of *qdmr* easier, by providing features, not covered in qdmr or dmrconf. * **[dmrfill](https://github.com/jancona/dmrfill)** -- Automatically extemds a qdmr YAML file with repeaters from a selected region. Get them all with one single command. * **[anytone-emu](https://github.com/dmr-tools/anytone-emu)** -- A tool for emulating radios, reverse engineering and documenting codeplugs. Also generates some [codeplug documentation](https://dmr-tools.github.io/codeplugs/). ## Releases Packaging status * **[Version 0.12.3](https://github.com/hmatuschek/qdmr/releases/tag/v0.12.3)** -- Bugfix release * **[Version 0.12.0](https://github.com/hmatuschek/qdmr/releases/tag/v0.12.0)** -- Added support BTech DR-1801UV (A6, still unstable) and Radioddity GD-73. * **[Version 0.11.3](https://github.com/hmatuschek/qdmr/releases/tag/v0.11.3)** -- Added proper support for BTech DMR-6X2UV, device specific settings for AnyTone devices, some bugfixes. * **[Version 0.10.4](https://github.com/hmatuschek/qdmr/releases/tag/v0.10.4)** -- Added support for BTech DM1701, some bugfixes. * **[Version 0.9.3](https://github.com/hmatuschek/qdmr/releases/tag/v0.9.3)** -- Reworked core library, added support for TyT MD-2017/Retevis RT82, TyT MD-390/Retevis RT8 & TyT MD-UV380. * **[Version 0.8.1](https://github.com/hmatuschek/qdmr/releases/tag/v0.8.1)** -- Fixed Radioddity GD-77 support (callsign db still buggy). * **[Version 0.7.0](https://github.com/hmatuschek/qdmr/releases/tag/v0.7.0)** -- Added AT-D868UVE support and many bugfixes. * **[Version 0.6.0](https://github.com/hmatuschek/qdmr/releases/tag/v0.6.4)** -- Added APRS & roaming for AT-D878UV. * **[Version 0.5.0](https://github.com/hmatuschek/qdmr/releases/tag/v0.5.0)** -- Added support for Anytone AT-D878UV. * **[Version 0.4.0](https://github.com/hmatuschek/qdmr/releases/tag/v0.4.0)** -- Added Open GD77 support. * **[Version 0.2.1](https://github.com/hmatuschek/qdmr/releases/tag/v0.2.1)** -- First public release. ## Install There are several ways to install qdmr on your system ranging from simple app-package downloads to building qdmr from its sources. For a detailed list of instructions for your system, read the [install instructions](https://dm3mat.darc.de/qdmr/install.html). Some distributions (see badge above) already added qdmr, thus easing the install using the system package manager. ## License qdmr - A GUI application and command-line-tool to program DMR radios. Copyright (C) 2019-2025 Hannes Matuschek, DM3MAT This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. qdmr-0.12.3/cli/000077500000000000000000000000001501654372000133125ustar00rootroot00000000000000qdmr-0.12.3/cli/CMakeLists.txt000066400000000000000000000013341501654372000160530ustar00rootroot00000000000000set(dmrconf_SOURCES main.cc printprogress.cc detect.cc verify.cc readcodeplug.cc writecodeplug.cc encodecodeplug.cc decodecodeplug.cc infofile.cc writecallsigndb.cc encodecallsigndb.cc progressbar.cc autodetect.cc) set(dmrconf_MOC_HEADERS ) set(dmrconf_HEADERS printprogress.hh detect.hh verify.hh readcodeplug.hh writecodeplug.hh encodecodeplug.hh decodecodeplug.hh infofile.hh writecallsigndb.hh encodecallsigndb.hh progressbar.hh autodetect.hh ${dmrconf_MOC_HEADERS}) qt5_wrap_cpp(dmrconf_MOC_SOURCES ${dmrconf_MOC_HEADERS}) add_executable(dmrconf WIN32 ${dmrconf_SOURCES} ${dmrconf_MOC_SOURCES}) target_link_libraries(dmrconf ${CORE_LIBS} libdmrconf) install(TARGETS dmrconf DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) qdmr-0.12.3/cli/autodetect.cc000066400000000000000000000106411501654372000157640ustar00rootroot00000000000000#include "autodetect.hh" #include "logger.hh" #include "radioinfo.hh" #include "usbdevice.hh" QVariant parseDeviceHandle(const QString &device) { QRegExp pattern("([0-9]+):([0-9]+)"); if (pattern.exactMatch(device.simplified())) { return QVariant::fromValue(USBDeviceHandle(pattern.cap(1).toUInt(), pattern.cap(2).toUInt())); } return QVariant(device.simplified()); } void printDevices(QTextStream &out, const QList &devices) { foreach (USBDeviceDescriptor device, devices) { if (USBDeviceInfo::Class::None == device.interfaceClass()) continue; out << "Device '"; if (USBDeviceInfo::Class::Serial == device.interfaceClass()) { out << device.device().toString() << "'\n"; } else if (USBDeviceInfo::Class::DFU == device.interfaceClass()) { USBDeviceHandle addr = device.device().value(); out << QString("%1:%2").arg(addr.bus).arg(addr.device) << "'\n"; } else if (USBDeviceInfo::Class::HID == device.interfaceClass()) { USBDeviceHandle addr = device.device().value(); out << QString("%1:%2").arg(addr.bus).arg(addr.device) << "'\n"; } out << " Type: " << device.description() << "\n"; out << " Description: " << device.longDescription() << "\n"; } } Radio * autoDetect(QCommandLineParser &parser, QCoreApplication &app, const ErrorStack &err) { Q_UNUSED(app) logDebug() << "Autodetect radios."; QList interfaces = USBDeviceDescriptor::detect(); if (interfaces.isEmpty()) interfaces = USBDeviceDescriptor::detect(false); if (interfaces.isEmpty()) { errMsg(err) << "No matching USB devices are found. Check connection?"; return nullptr; } logInfo() << "Found " << interfaces.count() << " device(s):"; foreach (USBDeviceDescriptor d, interfaces) { logInfo() << " " << d.description() << "."; } USBDeviceDescriptor device; if (parser.isSet("device")) { // If a device is passed by option, search for matching handle QVariant devHandle = parseDeviceHandle(parser.value("device")); foreach (USBDeviceDescriptor dev, interfaces) { if (dev.device() == devHandle) { device = dev; break; } } if (! device.isValid()) { ErrorStack::MessageStream msg(err, __FILE__, __LINE__); msg << "Device handle '" << parser.value("device") << "' not found in:\n"; printDevices(msg, interfaces); return nullptr; } } else if (1 != interfaces.size()) { // If no device is specified, there should only be one interface ErrorStack::MessageStream msg(err, __FILE__, __LINE__); msg << "Cannot auto-detect radio, more than one matching USB devices found:" << " Use --device option to specify to which device to talk to. Devices found:\n"; printDevices(msg, interfaces); return nullptr; } else if (! interfaces.first().isSave()) { ErrorStack::MessageStream msg(err, __FILE__, __LINE__); msg << "It is not save to assume that the device:\n"; printDevices(msg, interfaces); msg << "is a DMR radio. Please specify the device explicitly to verify correctness."; return nullptr; } else { // The first device is save to use device = interfaces.first(); } logDebug() << "Using device " << device.deviceHandle() << "."; // Handle identifiability of radio if (parser.isSet("radio")) { RadioInfo radio = RadioInfo::byKey(parser.value("radio").toLower()); if (! radio.isValid()) { errMsg(err) << "Unknown radio '" << parser.value("radio").toLower() << "'."; return nullptr; } Radio *rad = Radio::detect(device, radio, err); if (nullptr == rad) { logError() << "Cannot detect radio."; return nullptr; } return rad; } else if (! device.isSave()) { // Collect all radio keys for the device QStringList radios; foreach (RadioInfo info, RadioInfo::allRadios(device)) { radios.append(info.key()); } errMsg(err) << "It is not save or possible to identify the radio connected to the device '" << device.deviceHandle() << ". You have to specify which radio to use using " << "the --radio option. Possible radios for this device are " << radios.join(", ") << "."; return nullptr; } // Try auto-detect: Radio *rad = Radio::detect(device, RadioInfo(), err); if (nullptr == rad) { errMsg(err) << "Cannot auto-detect radio."; return nullptr; } return rad; } qdmr-0.12.3/cli/autodetect.hh000066400000000000000000000006031501654372000157730ustar00rootroot00000000000000#ifndef AUTODETECT_HH #define AUTODETECT_HH #include "radio.hh" #include #include QVariant parseDeviceHandle(const QString &device); void printDevices(QTextStream &out, const QList &devices); Radio *autoDetect(QCommandLineParser &parser, QCoreApplication &app, const ErrorStack &err=ErrorStack()); #endif // AUTODETECT_HH qdmr-0.12.3/cli/decodecodeplug.cc000066400000000000000000000174331501654372000165770ustar00rootroot00000000000000#include "decodecodeplug.hh" #include #include #include #include #include "logger.hh" #include "config.hh" #include "radioinfo.hh" #include "dummyfilereader.hh" #include "md390_codeplug.hh" #include "md390_filereader.hh" #include "uv390_codeplug.hh" #include "uv390_filereader.hh" #include "md2017_codeplug.hh" #include "md2017_filereader.hh" #include "dm1701_codeplug.hh" #include "dm1701_filereader.hh" #include "rd5r_codeplug.hh" #include "rd5r_filereader.hh" #include "gd77_codeplug.hh" #include "gd77_filereader.hh" #include "gd73_codeplug.hh" #include "gd73_filereader.hh" #include "opengd77_codeplug.hh" #include "openuv380_codeplug.hh" #include "openrtx_codeplug.hh" #include "anytone_filereader.hh" #include "d868uv_codeplug.hh" #include "d878uv_codeplug.hh" #include "d878uv2_codeplug.hh" #include "d578uv_codeplug.hh" #include "dmr6x2uv_codeplug.hh" #include "dr1801uv_codeplug.hh" #include "dr1801uv_filereader.hh" template bool decode(Config &config, const QString &filename, QCommandLineParser &parser, const ErrorStack &err=ErrorStack()) { Cpl codeplug; if (parser.isSet("manufacturer")) { if (! Rdr::read(filename, &codeplug, err)) { errMsg(err) << "Cannot decode manufacturer codeplug file '" << filename << "'."; return false; } } else if (! codeplug.read(filename, err)) { errMsg(err) << "Cannot decode binary codeplug file '" << filename << "'."; return false; } if (! codeplug.decode(&config, err)) { errMsg(err) << "Cannot decode binary codeplug file '" << filename << "'."; return false; } if (! codeplug.postprocess(&config, err)) { logError() << "Cannot post-process binary codeplug file '" << filename << "'."; return false; } return true; } int decodeCodeplug(QCommandLineParser &parser, QCoreApplication &app) { Q_UNUSED(app); if (2 > parser.positionalArguments().size()) parser.showHelp(-1); QString filename = parser.positionalArguments().at(1); ErrorStack err; if (! parser.isSet("radio")) { logError() << "No radio type is specified! Use the --radio option."; return -1; } if (! RadioInfo::hasRadioKey(parser.value("radio").toLower())) { QStringList radios; foreach (RadioInfo info, RadioInfo::allRadios()) radios.append(info.key()); logError() << "Unknown radio '" << parser.value("radio").toLower() << "."; logError() << "Known radios " << radios.join(", ") << "."; return -1; } RadioInfo::Radio radio = RadioInfo::byKey(parser.value("radio").toLower()).id(); Config config; switch (radio) { case RadioInfo::MD390: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::UV390: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::MD2017: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::DM1701: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::RD5R: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::GD73: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::GD77: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::OpenGD77: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::OpenUV380: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::OpenRTX: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::D868UVE: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::D878UV: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::D878UVII: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::D578UV: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::DMR6X2UV: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; case RadioInfo::DR1801UV: if (! decode(config, filename, parser, err)) { logError() << "Cannot decode codeplug '" << filename << "': " << err.format(); return -1; } break; default: logError() << "Decoding not implemented for " << RadioInfo::byID(radio).name() << "."; return -1; } if (3 <= parser.positionalArguments().size()) { QFileInfo info(parser.positionalArguments().at(2)); if (("conf" == info.suffix()) || ("csv" == info.suffix()) || parser.isSet("csv")) { logError() << "Export of the old table based format was disabled with 0.9.0. " "Import still works."; return -1; } else if (("yaml" == info.suffix()) || parser.isSet("yaml")) { QFile outfile(info.filePath()); if (! outfile.open(QIODevice::WriteOnly)) { logError() << "Cannot write CSV codeplug file '" << outfile.fileName() << "':\n" << outfile.errorString(); return -1; } QTextStream stream(&outfile); if (! config.toYAML(stream, err)) { logError() << "Cannot serialize codeplug to YAML:\n" << err.format(" "); } outfile.close(); } else { logError() << "Cannot determine codeplug output file format. Consider using --csv or --yaml."; return -1; } } else { QTextStream stream(stdout); if (parser.isSet("csv")) { logError() << "Export of the old table based format was disabled with 0.9.0. " "Import still works."; return -1; } else if (parser.isSet("yaml")) { if (! config.toYAML(stream, err)) { logError() << "Cannot serialize codeplug into YAML:\n" << err.format(" "); return -1; } } else { logError() << "Cannot determine codeplug output file format. Consider using --csv or --yaml."; return -1; } } return 0; } qdmr-0.12.3/cli/decodecodeplug.hh000066400000000000000000000003141501654372000165770ustar00rootroot00000000000000#ifndef DECODECODEPLUG_HH #define DECODECODEPLUG_HH class QCoreApplication; class QCommandLineParser; int decodeCodeplug(QCommandLineParser &parser, QCoreApplication &app); #endif // DECODECODEPLUG_HH qdmr-0.12.3/cli/detect.cc000066400000000000000000000010211501654372000150630ustar00rootroot00000000000000#include "detect.hh" #include #include #include "logger.hh" #include "radio.hh" #include "radiointerface.hh" #include "autodetect.hh" int detect(QCommandLineParser &parser, QCoreApplication &app) { // Try to detect a radio ErrorStack err; Radio *radio = autoDetect(parser, app, err); if (nullptr == radio) { logError() << "Cannot detect radio: \n" << err.format(" "); return -1; } logInfo() << "Found: '" << radio->name() << "'."; delete radio; return 0; } qdmr-0.12.3/cli/detect.hh000066400000000000000000000002551501654372000151050ustar00rootroot00000000000000#ifndef DETECT_HH #define DETECT_HH class QCommandLineParser; class QCoreApplication; int detect(QCommandLineParser &parser, QCoreApplication &app); #endif // DETECT_HH qdmr-0.12.3/cli/encodecallsigndb.cc000066400000000000000000000154531501654372000171110ustar00rootroot00000000000000#include "encodecodeplug.hh" #include #include #include #include "logger.hh" #include "config.hh" #include "radioinfo.hh" #include "dm1701_callsigndb.hh" #include "uv390_callsigndb.hh" #include "md2017_callsigndb.hh" #include "opengd77_callsigndb.hh" #include "gd77_callsigndb.hh" #include "d868uv_callsigndb.hh" #include "d878uv2_callsigndb.hh" #include "crc32.hh" int encodeCallsignDB(QCommandLineParser &parser, QCoreApplication &app) { Q_UNUSED(app); if (2 > parser.positionalArguments().size()) parser.showHelp(-1); UserDatabase userdb; if (parser.isSet("database")) { if (! userdb.load(parser.value("database"))) { logError() << "Cannot load user-db from '" << parser.value("database") << "'."; return -1; } } else if (0 == userdb.count()) { logInfo() << "Downloading call-sign DB..."; // Wait for download to finish QEventLoop loop; QObject::connect(&userdb, SIGNAL(loaded()), &loop, SLOT(quit())); QObject::connect(&userdb, SIGNAL(error(QString)), &loop, SLOT(quit())); loop.exec(); // Check if call-sign DB has been loaded if (0 == userdb.count()) { logError() << "Could not download/load call-sign DB."; return -1; } } if (parser.isSet("id")) { QStringList prefixes_text = parser.value("id").split(","); QSet prefixes; foreach (QString prefix_text, prefixes_text) { bool ok=true; uint32_t prefix = prefix_text.toUInt(&ok); if (ok) prefixes.insert(prefix); } if (prefixes.isEmpty()) { logError() << "Please specify a valid DMR ID or a list of DMR prefixes for --id option."; return -1; } prefixes_text.clear(); foreach (unsigned prefix, prefixes) { prefixes_text.append(QString::number(prefix)); } logDebug() << "Sort call-sign DB w.r.t. DMR ID(s) {" << prefixes_text.join(", ") << "}."; userdb.sortUsers(prefixes); } else { logWarn() << "No ID is specified, a more or less random set of call-signs will be used " << "if the radio cannot hold the entire call-sign DB of " << userdb.count() << " entries. Specify your DMR ID with --id=YOUR_DMR_ID. dmrconf will then " << "select those entries 'closest' to you. I.e., DMR IDs with the same prefix."; } CallsignDB::Selection selection; if (parser.isSet("limit")) { bool ok=true; selection.setCountLimit(parser.value("limit").toUInt(&ok)); if (! ok) { logError() << "Please specify a valid limit for the number of callsign db entries using the -n/--limit option."; return -1; } } if (! parser.isSet("radio")) { logError() << "You have to specify the radio using the --radio option."; parser.showHelp(-1); return -1; } if (! RadioInfo::hasRadioKey(parser.value("radio").toLower())) { QStringList radios; foreach (RadioInfo info, RadioInfo::allRadios()) radios.append(info.key()); logError() << "Unknown radio '" << parser.value("radio").toLower() << "."; logError() << "Known radios " << radios.join(", ") << "."; return -1; } RadioInfo::Radio radio = RadioInfo::byKey(parser.value("radio").toLower()).id(); ErrorStack err; if (RadioInfo::UV390 == radio) { UV390CallsignDB db; if (! db.encode(&userdb, selection, err)) { logError() << "Cannot encode call-sign DB: " << err.format(); return -1; } if (! db.write(parser.positionalArguments().at(1), err)) { logError() << "Cannot write output call-sign DB file '" << parser.positionalArguments().at(1) << "': " << err.format(); return -1; } } else if (RadioInfo::MD2017 == radio) { MD2017CallsignDB db; if (! db.encode(&userdb, selection, err)) { logError() << "Cannot encode call-sign DB: " << err.format(); return -1; } if (! db.write(parser.positionalArguments().at(1), err)) { logError() << "Cannot write output call-sign DB file '" << parser.positionalArguments().at(1) << "': " << err.format(); return -1; } } else if (RadioInfo::DM1701 == radio) { DM1701CallsignDB db; if (! db.encode(&userdb, selection, err)) { logError() << "Cannot encode call-sign DB: " << err.format(); return -1; } if (! db.write(parser.positionalArguments().at(1), err)) { logError() << "Cannot write output call-sign DB file '" << parser.positionalArguments().at(1) << "': " << err.format(); return -1; } } else if (RadioInfo::OpenGD77 == radio) { OpenGD77CallsignDB db; if (! db.encode(&userdb, selection, err)) { logError() << "Cannot encode call-sign DB: " << err.format(); return -1; } if (! db.write(parser.positionalArguments().at(1), err)) { logError() << "Cannot write output call-sign DB file '" << parser.positionalArguments().at(1) << "': " << err.format(); return -1; } } else if (RadioInfo::OpenUV380 == radio) { OpenGD77CallsignDB db; if (! db.encode(&userdb, selection, err)) { logError() << "Cannot encode call-sign DB: " << err.format(); return -1; } if (! db.write(parser.positionalArguments().at(1), err)) { logError() << "Cannot write output call-sign DB file '" << parser.positionalArguments().at(1) << "': " << err.format(); return -1; } } else if (RadioInfo::GD77 == radio) { GD77CallsignDB db; if (! db.encode(&userdb, selection, err)) { logError() << "Cannot encode call-sign DB: " << err.format(); return -1; } if (! db.write(parser.positionalArguments().at(1), err)) { logError() << "Cannot write output call-sign DB file '" << parser.positionalArguments().at(1) << "': " << err.format(); return -1; } } else if ((RadioInfo::D868UVE == radio) || (RadioInfo::D878UV == radio)){ D868UVCallsignDB db; if (! db.encode(&userdb, selection, err)) { logError() << "Cannot encode call-sign DB: " << err.format(); return -1; } if (! db.write(parser.positionalArguments().at(1), err)) { logError() << "Cannot write output call-sign DB file '" << parser.positionalArguments().at(1) << "': " << err.format(); return -1; } } else if ((RadioInfo::D878UVII == radio) || (RadioInfo::D578UV == radio)){ D878UV2CallsignDB db; if (! db.encode(&userdb, selection, err)) { logError() << "Cannot encode call-sign DB: " << err.format(); return -1; } if (! db.write(parser.positionalArguments().at(1), err)) { logError() << "Cannot write output call-sign DB file '" << parser.positionalArguments().at(1) << "': " << err.format(); return -1; } } else { logError() << "Cannot encode calls-sign DB: Not implemented for '" << parser.value("radio") << "'."; return -1; } return 0; } qdmr-0.12.3/cli/encodecallsigndb.hh000066400000000000000000000003241501654372000171120ustar00rootroot00000000000000#ifndef ENCODECALLSIGNDB_HH #define ENCODECALLSIGNDB_HH class QCoreApplication; class QCommandLineParser; int encodeCallsignDB(QCommandLineParser &parser, QCoreApplication &app); #endif // ENCODECALLSIGNDB_HH qdmr-0.12.3/cli/encodecodeplug.cc000066400000000000000000000122211501654372000165770ustar00rootroot00000000000000#include "encodecodeplug.hh" #include #include #include #include #include "logger.hh" #include "config.hh" #include "radioinfo.hh" #include "rd5r_codeplug.hh" #include "gd73_codeplug.hh" #include "gd77_codeplug.hh" #include "opengd77_codeplug.hh" #include "openuv380_codeplug.hh" #include "openrtx_codeplug.hh" #include "md390_codeplug.hh" #include "uv390_codeplug.hh" #include "md2017_codeplug.hh" #include "d868uv_codeplug.hh" #include "d878uv_codeplug.hh" #include "d878uv2_codeplug.hh" #include "d578uv_codeplug.hh" #include "dmr6x2uv_codeplug.hh" #include "dr1801uv_codeplug.hh" #include "crc32.hh" template bool encode(Config &config, Codeplug::Flags flags, QCommandLineParser &parser) { ErrorStack err; T codeplug; Config *intermediate = codeplug.preprocess(&config, err); if (nullptr == intermediate) { logError() << "Cannot pre-process codeplug: " << err.format(); return false; } if (! codeplug.encode(intermediate, flags, err)) { logError() << "Cannot encode codeplug: " << err.format(); delete intermediate; return false; } delete intermediate; codeplug.image(0).sort(); if (! codeplug.write(parser.positionalArguments().at(2), err)) { logError() << "Cannot write output codeplug file '" << parser.positionalArguments().at(1) << "': " << err.format(); return false; } return true; } int encodeCodeplug(QCommandLineParser &parser, QCoreApplication &app) { Q_UNUSED(app); if (3 > parser.positionalArguments().size()) parser.showHelp(-1); QFileInfo fileinfo(parser.positionalArguments().at(1)); if (! parser.isSet("radio")) { logError() << "You have to specify the radio using the --radio option."; parser.showHelp(-1); return -1; } if (! RadioInfo::hasRadioKey(parser.value("radio").toLower())) { QStringList radios; foreach (RadioInfo info, RadioInfo::allRadios()) radios.append(info.key()); logError() << "Unknown radio '" << parser.value("radio").toLower() << "."; logError() << "Known radios " << radios.join(", ") << "."; return -1; } RadioInfo::Radio radio = RadioInfo::byKey(parser.value("radio").toLower()).id(); Codeplug::Flags flags; flags.updateCodePlug = false; if (parser.isSet("auto-enable-gps")) flags.autoEnableGPS = true; if (parser.isSet("auto-enable-roaming")) flags.autoEnableRoaming = true; Config config; ErrorStack err; if (parser.isSet("csv") || ("conf" == fileinfo.suffix()) || ("csv" == fileinfo.suffix())) { QString errorMessage; QFile infile(fileinfo.canonicalFilePath()); if (! infile.open(QIODevice::ReadOnly)) { logError() << "Cannot encode CSV codeplug file '" << fileinfo.fileName() << "': " << infile.errorString(); return -1; } QTextStream stream(&infile); if (! config.readCSV(stream, errorMessage)) { logError() << "Cannot parse CSV codeplug '" << infile.fileName() << "': " << errorMessage; return -1; } } else if (parser.isSet("yaml") || ("yaml" == fileinfo.suffix())) { if (! config.readYAML(fileinfo.canonicalFilePath(), err)) { logError() << "Cannot parse YAML codeplug '" << fileinfo.fileName() << "':\n" << err.format(" "); return -1; } } else { logError() << "Cannot determine input file type, consider using --csv or --yaml."; return -1; } switch (radio) { case RadioInfo::MD390: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::UV390: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::MD2017: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::RD5R: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::GD73: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::GD77: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::OpenGD77: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::OpenUV380: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::OpenRTX: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::D868UVE: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::D878UV: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::D878UVII: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::D578UV: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::DMR6X2UV: if (! encode(config, flags, parser)) return -1; break; case RadioInfo::DR1801UV: if (! encode(config, flags, parser)) return -1; break; default: logError() << "Cannot encode codeplug: Unknown radio '" << parser.value("radio") << "'."; return -1; } return 0; } qdmr-0.12.3/cli/encodecodeplug.hh000066400000000000000000000003141501654372000166110ustar00rootroot00000000000000#ifndef ENCODECODEPLUG_HH #define ENCODECODEPLUG_HH class QCoreApplication; class QCommandLineParser; int encodeCodeplug(QCommandLineParser &parser, QCoreApplication &app); #endif // ENCODECODEPLUG_HH qdmr-0.12.3/cli/infofile.cc000066400000000000000000000011461501654372000154160ustar00rootroot00000000000000#include "infofile.hh" #include #include #include #include "logger.hh" #include "dfufile.hh" int infoFile(QCommandLineParser &parser, QCoreApplication &app) { Q_UNUSED(app) if (2 > parser.positionalArguments().size()) parser.showHelp(-1); QString filename = parser.positionalArguments().at(1); DFUFile file; ErrorStack err; if (! file.read(filename, err)) { logError() << "Cannot read codeplug file '" << filename << "': " << err.format(); return -1; } QTextStream out(stdout); file.dump(out); return 0; } qdmr-0.12.3/cli/infofile.hh000066400000000000000000000002651501654372000154310ustar00rootroot00000000000000#ifndef INFOFILE_HH #define INFOFILE_HH class QCoreApplication; class QCommandLineParser; int infoFile(QCommandLineParser &parser, QCoreApplication &app); #endif // INFOFILE_HH qdmr-0.12.3/cli/main.cc000066400000000000000000000227571501654372000145620ustar00rootroot00000000000000#include #include #include #include "logger.hh" #include "config.h" #include "detect.hh" #include "verify.hh" #include "radioinfo.hh" #include "readcodeplug.hh" #include "writecodeplug.hh" #include "writecallsigndb.hh" #include "encodecodeplug.hh" #include "encodecallsigndb.hh" #include "decodecodeplug.hh" #include "infofile.hh" #include "uv390_codeplug.hh" int main(int argc, char *argv[]) { // Install log handler to stderr. QTextStream out(stderr); StreamLogHandler *handler = new StreamLogHandler(out, LogMessage::WARNING, true); Logger::get().addHandler(handler); // Instantiate core application QCoreApplication app(argc, argv); app.setApplicationName("dmrconf"); app.setOrganizationName("DM3MAT"); app.setOrganizationDomain("dm3mat.darc.de"); app.setApplicationVersion(VERSION_STRING); QCommandLineParser parser; parser.setApplicationDescription( QCoreApplication::translate( "main", "Up- and download codeplugs for cheap Chinese DMR radios.")); parser.addHelpOption(); parser.addVersionOption(); parser.addOption({ {"V","verbose"}, QCoreApplication::translate("main", "Verbose output.") }); parser.addOption({ {"c", "csv"}, QCoreApplication::translate("main", "Up- and download codeplugs in CSV format.") }); parser.addOption({ {"y", "yaml"}, QCoreApplication::translate("main", "Up- and download codeplugs in extensible YAML format.") }); parser.addOption({ {"b", "bin"}, QCoreApplication::translate("main", "Up- and download codeplugs in binary format.") }); parser.addOption({ {"m", "manufacturer"}, QCoreApplication::translate("main", "Given file is manufacturer codeplug file. " " Can be used with 'decode'.") }); parser.addOption({ {"D","device"}, QCoreApplication::translate("main", "Specifies the device to use to talk to " "the radio. If not specified, the dmrconf will try to detect the radio " "automatically. Please note, that for some radios the device must be specified."), QCoreApplication::translate("main", "DEVICE") }); parser.addOption({ {"R", "radio"}, QCoreApplication::translate("main", "Specifies the radio. This option can also " "be used to override the auto-detection of radios. Be careful using this " "option when writing to the device. A incompatible code-plug might be written."), QCoreApplication::translate("main", "RADIO") }); parser.addOption({ {"i", "id"}, QCoreApplication::translate("main", "Specifies the DMR id."), QCoreApplication::translate("main", "ID") }); parser.addOption({ {"n", "limit"}, QCoreApplication::translate("main", "Limits several amonuts, depending on the " "context. When encoding/writing the callsign db, this option specifies the " "maximum number of callsigns to encode."), QCoreApplication::translate("main", "N") }); parser.addOption({ {"B","database"}, QCoreApplication::translate("main", "Specifies the user DB json file when " "writing the callsign db."), "FILENAME" }); parser.addOption(QCommandLineOption( "init-codeplug", QCoreApplication::translate( "main", "Initializes the code-plug in the radio. If not present (default) " "the code-plug gets updated, maintining all settings made earlier."))); parser.addOption(QCommandLineOption( "auto-enable-gps", QCoreApplication::translate("main", "Automatically enables GPS if there is a " "GPS/APRS system used by any channel."))); parser.addOption(QCommandLineOption( "auto-enable-roaming", QCoreApplication::translate("main", "Automatically enables roaming if there is a " "roaming zone used by any channel."))); parser.addOption(QCommandLineOption( "ignore-limits", QCoreApplication::translate("main", "Disables some limit checks."))); parser.addOption(QCommandLineOption( "list-radios", QCoreApplication::translate("main", "Lists all supported radios including the " "keys to be used with the --radio option."))); parser.addPositionalArgument( "command", QCoreApplication::translate( "main", "Specifies the command to perform. Either detect, verify, read, write, " "write-db, encode, encode-db, decode or info. Consult the man-page of dmrconf for a " "detailed description of these commands."), QCoreApplication::translate("main", "[command]")); parser.addPositionalArgument( "file", QCoreApplication::translate( "main", "The code-plug file. Either binary (extension .dfu), text/csv (extension .conf " "or .csv) or YAML format (extension .yaml). The format can be forced using the --csv, " "--yaml or --binary options."), QCoreApplication::translate("main", "[filename]")); parser.process(app); if (parser.isSet("list-radios")) { QList radios = RadioInfo::allRadios(); QTextStream out(stdout); out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(18); out << " Key"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << "| "; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(18); out << "Name"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << "| "; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(60); out << "Manufacturer"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << "\n"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar('-'); out.setFieldWidth(18); out << "-"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << "+-"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar('-'); out.setFieldWidth(18); out << "-"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << "+-"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar('-'); out.setFieldWidth(60); out << "-"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << "\n"; foreach (RadioInfo radio, radios) { out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(18); out << (" " +radio.key()); out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << "| "; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(18); out << radio.name(); out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << "| "; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << radio.manufacturer() << "\n"; } out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar('-'); out.setFieldWidth(18); out << "-"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << "+-"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar('-'); out.setFieldWidth(18); out << "-"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << "+-"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar('-'); out.setFieldWidth(60); out << "-"; out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << "\n"; out.flush(); return 0; } if (1 > parser.positionalArguments().size()) parser.showHelp(-1); if (parser.isSet("verbose")) handler->setMinLevel(LogMessage::DEBUG); int res = -1; QString command = parser.positionalArguments().at(0); if ("detect" == command) res = detect(parser, app); else if ("verify" == command) res = verify(parser, app); else if ("read" == command) res = readCodeplug(parser, app); else if ("write" == command) res = writeCodeplug(parser, app); else if ("write-db" == command) res = writeCallsignDB(parser, app); else if ("encode" == command) res = encodeCodeplug(parser, app); else if ("encode-db" == command) res = encodeCallsignDB(parser, app); else if ("decode" == command) res = decodeCodeplug(parser, app); else if ("info" == command) res = infoFile(parser, app); else parser.showHelp(-1); // Allow some pending events to be processed (e.g., deleteLater()) QEventLoop loop; while(loop.processEvents()) {} return res; } qdmr-0.12.3/cli/printprogress.cc000066400000000000000000000005371501654372000165470ustar00rootroot00000000000000#include "printprogress.hh" #include void print_progress(int prog) { QTextStream out(stdout); out << "\r"; out.setFieldWidth(2); out << prog; out.setFieldWidth(0); out << "%"; prog = (prog*20)/100; out << "["; for (int i=0; i i) std::cerr << "="; else std::cerr << " "; } std::cerr << "] " << percent <<"%" << std::endl; } void updateProgress(unsigned percent) { std::cerr << "\033[1A\033[K"; showProgress(percent); } qdmr-0.12.3/cli/progressbar.hh000066400000000000000000000002541501654372000161650ustar00rootroot00000000000000#ifndef PROGRESSBAR_HH #define PROGRESSBAR_HH #include void showProgress(unsigned percent=0); void updateProgress(unsigned percent); #endif // PROGRESSBAR_HH qdmr-0.12.3/cli/readcodeplug.cc000066400000000000000000000052201501654372000162560ustar00rootroot00000000000000#include "readcodeplug.hh" #include #include #include #include #include "logger.hh" #include "radio.hh" #include "printprogress.hh" #include "config.hh" #include "codeplug.hh" #include "progressbar.hh" #include "autodetect.hh" int readCodeplug(QCommandLineParser &parser, QCoreApplication &app) { Q_UNUSED(app); if (2 > parser.positionalArguments().size()) parser.showHelp(-1); ErrorStack err; Radio *radio = autoDetect(parser, app, err); if (nullptr == radio) { logError() << "Cannot detect radio: " << err.format(); return -1; } QString filename = parser.positionalArguments().at(1); showProgress(); QObject::connect(radio, &Radio::downloadProgress, updateProgress); Config config; if (! radio->startDownload(true, err)) { logError() << "Codeplug download error: " << err.format(); return -1; } if (Radio::StatusError == radio->status()) { logError() << "Codeplug download error: " << err.format(); return -1; } logDebug() << "Save codeplug at '" << filename << "'."; // If output is CSV -> decode code-plug if (parser.isSet("csv") || (filename.endsWith(".conf") || filename.endsWith(".csv"))) { logError() << "Export of the old table based format was disabled with 0.9.0. " "Import still works."; return -1; } else if (parser.isSet("yaml") || filename.endsWith(".yaml")) { // decode codeplug if (! radio->codeplug().decode(&config, err)) { logError() << "Cannot decode codeplug: " << err.format(); return -1; } // post-process decoded codeplug if (! radio->codeplug().postprocess(&config, err)) { logError() << "Cannot post-process codeplug: " << err.format(); return -1; } // try to write YAML file QFile file(filename); if (! file.open(QIODevice::WriteOnly)) { logError() << "Cannot write YAML file '" << filename << "': " << file.errorString(); return -1; } QTextStream stream(&file); if (! config.toYAML(stream)) { logError() << "Cannot serialize config to YAML file '" << filename << "'."; return -1; } stream.flush(); file.close(); } else if (parser.isSet("bin") || filename.endsWith(".bin") || filename.endsWith(".dfu")) { // otherwise write binary code-plug if (! radio->codeplug().write(filename, err)) { logError() << "Cannot dump codplug into file '" << filename << "': " << err.format(); return -1; } } else { logError() << "Cannot determine file output type from '" << filename << "'. " << "Consider using --csv, --yaml or --bin."; return -1; } return 0; } qdmr-0.12.3/cli/readcodeplug.hh000066400000000000000000000003041501654372000162660ustar00rootroot00000000000000#ifndef READCODEPLUG_HH #define READCODEPLUG_HH class QCommandLineParser; class QCoreApplication; int readCodeplug(QCommandLineParser &parser, QCoreApplication &app); #endif // READCODEPLUG_HH qdmr-0.12.3/cli/verify.cc000066400000000000000000000145071501654372000151340ustar00rootroot00000000000000#include "verify.hh" #include #include #include #include #include #include #include "logger.hh" #include "config.hh" #include "csvreader.hh" #include "dfufile.hh" #include "radiolimits.hh" #include "rd5r.hh" #include "uv390.hh" #include "md2017.hh" #include "gd77.hh" #include "opengd77.hh" #include "openuv380.hh" #include "d868uv.hh" #include "d878uv.hh" #include "d878uv2.hh" #include "d578uv.hh" int verify(QCommandLineParser &parser, QCoreApplication &app) { Q_UNUSED(app); if (2 > parser.positionalArguments().size()) parser.showHelp(-1); QString filename = parser.positionalArguments().at(1); QFile file(filename); if (! file.open(QIODevice::ReadOnly)) { logError() << "Cannot open file" << filename; return -1; } Config config; // Determine type by ending or flag if (parser.isSet("csv") || (filename.endsWith(".conf") || filename.endsWith(".csv"))) { QTextStream stream(&file); QString errorMessage; if (! CSVReader::read(&config, stream, errorMessage)) { logError() << "Cannot read config file '" << filename << "': " << errorMessage; return -1; } logInfo() << "Verify '" << filename << "': No syntax issues found."; } else if (parser.isSet("bin") || (filename.endsWith(".bin") || filename.endsWith(".dfu"))) { logError() << "Verification of binary code-plugs makes no sense."; return -1; } else if (parser.isSet("yaml") || (filename.endsWith(".yaml") || filename.endsWith(".yml"))) { ErrorStack err; if (! config.readYAML(filename,err)) { logError() << "Cannot read codeplug file '" << filename << "': " << err.format(); return -1; } } else { logError() << "Cannot determine filetype from filename '" << filename << "': Consider using --csv."; return -1; } if (! parser.isSet("radio")) { logInfo() << "To verify the codeplug against a specific radio, conser using the --radio=RADIO option."; return 0; } RadioLimitContext ctx; RadioInfo::Radio radio = RadioInfo::byKey(parser.value("radio").toLower()).id(); switch (radio) { case RadioInfo::RD5R: { RD5R radio; ErrorStack err; Config *intermediate = radio.codeplug().preprocess(&config, err); if (nullptr == intermediate) { logError() << "Cannot pre-process codeplug: " << err.format(); return -1; } radio.limits().verifyConfig(intermediate, ctx); delete intermediate; } break; case RadioInfo::UV390: { UV390 radio; ErrorStack err; Config *intermediate = radio.codeplug().preprocess(&config, err); if (nullptr == intermediate) { logError() << "Cannot pre-process codeplug: " << err.format(); return -1; } radio.limits().verifyConfig(intermediate, ctx); delete intermediate; } break; case RadioInfo::MD2017: { MD2017 radio; ErrorStack err; Config *intermediate = radio.codeplug().preprocess(&config, err); if (nullptr == intermediate) { logError() << "Cannot pre-process codeplug: " << err.format(); return -1; } radio.limits().verifyConfig(intermediate, ctx); delete intermediate; } break; case RadioInfo::GD77: { GD77 radio; ErrorStack err; Config *intermediate = radio.codeplug().preprocess(&config, err); if (nullptr == intermediate) { logError() << "Cannot pre-process codeplug: " << err.format(); return -1; } radio.limits().verifyConfig(intermediate, ctx); delete intermediate; } break; case RadioInfo::OpenGD77: { OpenGD77 radio; ErrorStack err; Config *intermediate = radio.codeplug().preprocess(&config, err); if (nullptr == intermediate) { logError() << "Cannot pre-process codeplug: " << err.format(); return -1; } radio.limits().verifyConfig(intermediate, ctx); delete intermediate; } break; case RadioInfo::OpenUV380: { OpenUV380 radio; ErrorStack err; Config *intermediate = radio.codeplug().preprocess(&config, err); if (nullptr == intermediate) { logError() << "Cannot pre-process codeplug: " << err.format(); return -1; } radio.limits().verifyConfig(intermediate, ctx); delete intermediate; } break; case RadioInfo::D868UV: { D868UV radio; ErrorStack err; Config *intermediate = radio.codeplug().preprocess(&config, err); if (nullptr == intermediate) { logError() << "Cannot pre-process codeplug: " << err.format(); return -1; } radio.limits().verifyConfig(intermediate, ctx); delete intermediate; } break; case RadioInfo::D878UV: { D878UV radio; ErrorStack err; Config *intermediate = radio.codeplug().preprocess(&config, err); if (nullptr == intermediate) { logError() << "Cannot pre-process codeplug: " << err.format(); return -1; } radio.limits().verifyConfig(intermediate, ctx); delete intermediate; } break; case RadioInfo::D878UVII: { D878UV2 radio; ErrorStack err; Config *intermediate = radio.codeplug().preprocess(&config, err); if (nullptr == intermediate) { logError() << "Cannot pre-process codeplug: " << err.format(); return -1; } radio.limits().verifyConfig(intermediate, ctx); delete intermediate; } break; case RadioInfo::D578UV: { D578UV radio; ErrorStack err; Config *intermediate = radio.codeplug().preprocess(&config, err); if (nullptr == intermediate) { logError() << "Cannot pre-process codeplug: " << err.format(); return -1; } radio.limits().verifyConfig(intermediate, ctx); delete intermediate; } break; default: logError() << "Cannot verify code-plug against unknown radio '" << radio << "'."; return -1; } bool valid = true; for (int i=0; i #include #include "logger.hh" #include "radio.hh" #include "userdatabase.hh" #include "progressbar.hh" #include "callsigndb.hh" #include "autodetect.hh" int writeCallsignDB(QCommandLineParser &parser, QCoreApplication &app) { UserDatabase userdb; if (parser.isSet("database")) { if (! userdb.load(parser.value("database"))) { logError() << "Cannot load user-db from '" << parser.value("database") << "'."; return -1; } } else if (0 == userdb.count()) { logInfo() << "Downloading call-sign DB..."; // Wait for download to finish QEventLoop loop; QObject::connect(&userdb, SIGNAL(loaded()), &loop, SLOT(quit())); QObject::connect(&userdb, SIGNAL(error(QString)), &loop, SLOT(quit())); loop.exec(); // Check if call-sign DB has been loaded if (0 == userdb.count()) { logError() << "Could not download/load call-sign DB."; return -1; } } if (parser.isSet("id")) { QStringList prefixes_text = parser.value("id").split(","); QSet prefixes; foreach (QString prefix_text, prefixes_text) { bool ok=true; uint32_t prefix = prefix_text.toUInt(&ok); if (ok) prefixes.insert(prefix); } if (prefixes.isEmpty()) { logError() << "Please specify a valid DMR ID or a list of DMR prefixes for --id option."; return -1; } prefixes_text.clear(); foreach (unsigned prefix, prefixes) { prefixes_text.append(QString::number(prefix)); } logDebug() << "Sort call-sign DB w.r.t. DMR ID(s) {" << prefixes_text.join(", ") << "}."; userdb.sortUsers(prefixes); } else { logWarn() << "No ID is specified, a more or less random set of call-signs will be used " << "if the radio cannot hold the entire call-sign DB of " << userdb.count() << " entries. Specify your DMR ID with --id=YOUR_DMR_ID. dmrconf will then " << "select those entries 'closest' to you. I.e., DMR IDs with the same prefix."; } CallsignDB::Selection selection; if (parser.isSet("limit")) { bool ok=true; selection.setCountLimit(parser.value("limit").toUInt(&ok)); if (! ok) { logError() << "Please specify a valid limit for the number of callsign db entries using the -n/--limit option."; return -1; } } ErrorStack err; Radio *radio = autoDetect(parser, app, err); if (nullptr == radio) { logError() << "Could not detect radio: " << err.format(); return -1; } showProgress(); QObject::connect(radio, &Radio::uploadProgress, updateProgress); if (! radio->startUploadCallsignDB(&userdb, true, selection, err)) { logError() << "Could not upload call-sign DB to radio: " << err.format(); return -1; } return 0; } qdmr-0.12.3/cli/writecallsigndb.hh000066400000000000000000000003201501654372000170030ustar00rootroot00000000000000#ifndef WRITECALLSIGNDB_HH #define WRITECALLSIGNDB_HH class QCoreApplication; class QCommandLineParser; int writeCallsignDB(QCommandLineParser &parser, QCoreApplication &app); #endif // WRITECALLSIGNDB_HH qdmr-0.12.3/cli/writecodeplug.cc000066400000000000000000000054561501654372000165100ustar00rootroot00000000000000#include "writecodeplug.hh" #include #include #include #include "logger.hh" #include "radio.hh" #include "config.hh" #include "progressbar.hh" #include "autodetect.hh" #include "radiolimits.hh" int writeCodeplug(QCommandLineParser &parser, QCoreApplication &app) { if (2 > parser.positionalArguments().size()) parser.showHelp(-1); QString filename = parser.positionalArguments().at(1); QFileInfo fileinfo(filename); QString errorMessage; Config config; if (parser.isSet("csv") || ("csv" == fileinfo.suffix()) || ("conf"==fileinfo.suffix())) { if (! config.readCSV(filename, errorMessage)) { logError() << "Cannot read CSV file '" << filename << "': " << errorMessage; return -1; } } else if (parser.isSet("yaml") || ("yaml" == fileinfo.suffix())) { ErrorStack err; if (! config.readYAML(fileinfo.canonicalFilePath(), err)) { logError() << "Cannot parse YAML codeplug '" << fileinfo.fileName() << "': " << err.format(); return -1; } } logDebug() << "Read codeplug from '" << filename << "'."; ErrorStack err; Radio *radio = autoDetect(parser, app, err); if (nullptr == radio) { logError() << "Cannot detect radio:" << err.format(); return -1; } RadioLimitContext ctx(parser.isSet("ignore-limits")); Config *intermediate = radio->codeplug().preprocess(&config, err); if (nullptr == intermediate) { logError() << "Cannot pre-process codeplug: " << err.format(); return -1; } bool verified = true; radio->limits().verifyConfig(intermediate, ctx); // Only print warnings for (int i=0; iname() << "."; if (! radio->startUpload(intermediate, true, flags, err)) { logError() << "Codeplug upload error: " << err.format(); return -1; } if (Radio::StatusError == radio->status()) { logError() << "Codeplug upload error: " << err.format(); return -1; } logDebug() << "Upload completed."; return 0; } qdmr-0.12.3/cli/writecodeplug.hh000066400000000000000000000003101501654372000165020ustar00rootroot00000000000000#ifndef WRITECODEPLUG_HH #define WRITECODEPLUG_HH class QCoreApplication; class QCommandLineParser; int writeCodeplug(QCommandLineParser &parser, QCoreApplication &app); #endif // WRITECODEPLUG_HH qdmr-0.12.3/cmake/000077500000000000000000000000001501654372000136235ustar00rootroot00000000000000qdmr-0.12.3/cmake/FindLIBUSB_1.cmake000066400000000000000000000064511501654372000166340ustar00rootroot00000000000000# - Try to find libusb-1.0 # Once done this will define # # LIBUSB_1_FOUND - system has libusb # LIBUSB_1_INCLUDE_DIRS - the libusb include directory # LIBUSB_1_LIBRARIES - Link these to use libusb # LIBUSB_1_DEFINITIONS - Compiler switches required for using libusb # # Adapted from cmake-modules Google Code project # # Copyright (c) 2006 Andreas Schneider # # (Changes for libusb) Copyright (c) 2008 Kyle Machulis # # Redistribution and use is allowed according to the terms of the New BSD license. # # CMake-Modules Project New BSD License # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # * Neither the name of the CMake-Modules Project nor the names of its # contributors may be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # if (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS) # in cache already set(LIBUSB_FOUND TRUE) else (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS) find_path(LIBUSB_1_INCLUDE_DIR NAMES libusb.h PATHS /usr/include /usr/local/include /sw/include PATH_SUFFIXES libusb-1.0 ) find_library(LIBUSB_1_LIBRARY NAMES usb-1.0 usb PATHS /usr/lib /usr/local/lib /sw/lib ) set(LIBUSB_1_INCLUDE_DIRS ${LIBUSB_1_INCLUDE_DIR} ) set(LIBUSB_1_LIBRARIES ${LIBUSB_1_LIBRARY} ) if (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES) set(LIBUSB_1_FOUND TRUE) endif (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES) if (LIBUSB_1_FOUND) if (NOT libusb_1_FIND_QUIETLY) message(STATUS "Found libusb-1.0:") message(STATUS " - Includes: ${LIBUSB_1_INCLUDE_DIRS}") message(STATUS " - Libraries: ${LIBUSB_1_LIBRARIES}") endif (NOT libusb_1_FIND_QUIETLY) else (LIBUSB_1_FOUND) if (libusb_1_FIND_REQUIRED) message(FATAL_ERROR "Could not find libusb") endif (libusb_1_FIND_REQUIRED) endif (LIBUSB_1_FOUND) # show the LIBUSB_1_INCLUDE_DIRS and LIBUSB_1_LIBRARIES variables only in the advanced view mark_as_advanced(LIBUSB_1_INCLUDE_DIRS LIBUSB_1_LIBRARIES) endif (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS) qdmr-0.12.3/cmake/FindYAMLCPP.cmake000066400000000000000000000030421501654372000165320ustar00rootroot00000000000000# - Try to find yaml-cpp # Once done this will define # # YAMLCPP_FOUND - system has yaml-cpp # YAMLCPP_INCLUDE_DIRS - the yaml-cpp include directory # YAMLCPP_LIBRARIES - Link these to use yaml-cpp # YAMLCPP_DEFINITIONS - Compiler switches required for using yaml-cpp if (YAMLCPP_LIBRARIES AND YAMLCPP_INCLUDE_DIRS) # in cache already set(YAMLCPP_FOUND TRUE) else (YAMLCPP_LIBRARIES AND YAMLCPP_INCLUDE_DIRS) find_path(YAMLCPP_INCLUDE_DIR NAMES yaml-cpp/yaml.h PATHS /usr/include /usr/local/include /opt/local /sw/include ) find_library(YAMLCPP_LIBRARY NAMES yaml-cpp PATHS /usr/lib /usr/local/lib /opt/local /sw/lib ) set(YAMLCPP_INCLUDE_DIRS ${YAMLCPP_INCLUDE_DIR} ) set(YAMLCPP_LIBRARIES ${YAMLCPP_LIBRARY} ) if (YAMLCPP_INCLUDE_DIRS AND YAMLCPP_LIBRARIES) set(YAMLCPP_FOUND TRUE) endif (YAMLCPP_INCLUDE_DIRS AND YAMLCPP_LIBRARIES) if (YAMLCPP_FOUND) if (NOT YAMLCPP_FIND_QUIETLY) message(STATUS "Found yaml-cpp:") message(STATUS " - Includes: ${YAMLCPP_INCLUDE_DIRS}") message(STATUS " - Libraries: ${YAMLCPP_LIBRARIES}") endif (NOT YAMLCPP_FIND_QUIETLY) else (YAMLCPP_FOUND) if (YAMLCPP_FIND_REQUIRED) message(FATAL_ERROR "Could not find yaml-cpp") endif (YAMLCPP_FIND_REQUIRED) endif (YAMLCPP_FOUND) # show the YAMLCPP_INCLUDE_DIRS and YAMLCPP_LIBRARIES variables only in the advanced view mark_as_advanced(YAMLCPP_INCLUDE_DIRS YAMLCPP_LIBRARIES) endif (YAMLCPP_LIBRARIES AND YAMLCPP_INCLUDE_DIRS) qdmr-0.12.3/de.darc.dm3mat.qdmr.yaml000066400000000000000000000017421501654372000170610ustar00rootroot00000000000000app-id: de.darc.dm3mat.qdmr runtime: org.kde.Platform runtime-version: '5.15-24.08' sdk: org.kde.Sdk command: qdmr rename-desktop-file: qdmr.desktop rename-icon: qdmr finish-args: - --device=all - --share=network - --share=ipc - --socket=fallback-x11 - --socket=wayland cleanup: - /include - /lib/pkgconfig - /share/aclocal - /share/doc - /share/man - /man - "*.a" - "*.la" modules: - name: yaml-cpp buildsystem: cmake-ninja config-opts: - -DBUILD_SHARED_LIBS=ON sources: - type: git url: https://github.com/jbeder/yaml-cpp commit: f7320141120f720aecc4c32be25586e7da9eb978 # tag 0.8.0 - name: libusb1 buildsystem: autotools config-opts: - --disable-static sources: - type: git url: https://github.com/libusb/libusb.git tag: v1.0.28 commit: a61afe5f75d969c4561a1d0ad753aa23cee6329a - name: qdmr buildsystem: cmake-ninja sources: - type: dir path: . qdmr-0.12.3/dist/000077500000000000000000000000001501654372000135065ustar00rootroot00000000000000qdmr-0.12.3/dist/99-qdmr.rules000066400000000000000000000011571501654372000157700ustar00rootroot00000000000000# Radioddity RD-73 SUBSYSTEM=="usb", ATTRS{idVendor}=="1206", ATTRS{idProduct}=="0227", MODE="660", GROUP="dialout" # TYT MD-UV380 SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="660", GROUP="dialout" # Baofeng RD-5R, TD-5R # Radioddity GD-77 SUBSYSTEM=="usb", ATTRS{idVendor}=="15a2", ATTRS{idProduct}=="0073", MODE="660", GROUP="dialout" # Anytone AT-D868UV, AT-D878UV, etc # Ignore this device in Modem Manager ATTRS{idVendor}=="28e9" ATTRS{idProduct}=="018a", ENV{ID_MM_DEVICE_IGNORE}="1" # also for DR-1801UV ATTRS{idVendor}=="067b" ATTRS{idProduct}=="23a3", ENV{ID_MM_DEVICE_IGNORE}="1" qdmr-0.12.3/dist/CMakeLists.txt000066400000000000000000000017261501654372000162540ustar00rootroot00000000000000if (UNIX AND NOT APPLE) configure_file("qdmr.desktop.in" "qdmr.desktop") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qdmr.desktop DESTINATION share/applications/) install(FILES ${PROJECT_SOURCE_DIR}/dist/qdmr32.png DESTINATION share/icons/hicolor/32x32/apps/ RENAME qdmr.png) install(FILES ${PROJECT_SOURCE_DIR}/dist/qdmr48.png DESTINATION share/icons/hicolor/48x48/apps/ RENAME qdmr.png) install(FILES ${PROJECT_SOURCE_DIR}/dist/qdmr64.png DESTINATION share/icons/hicolor/64x64/apps/ RENAME qdmr.png) install(FILES ${PROJECT_SOURCE_DIR}/dist/qdmr72.png DESTINATION share/icons/hicolor/72x72/apps/ RENAME qdmr.png) install(FILES ${PROJECT_SOURCE_DIR}/dist/qdmr96.png DESTINATION share/icons/hicolor/96x96/apps/ RENAME qdmr.png) endif(UNIX AND NOT APPLE) if (UNIX AND NOT APPLE AND ${INSTALL_APPSTREAM_DATA}) install(FILES ${PROJECT_SOURCE_DIR}/dist/de.darc.dm3mat.qdmr.metainfo.xml DESTINATION share/metainfo/) endif(UNIX AND NOT APPLE AND ${INSTALL_APPSTREAM_DATA}) qdmr-0.12.3/dist/de.darc.dm3mat.qdmr.metainfo.xml000066400000000000000000000026541501654372000214660ustar00rootroot00000000000000 de.darc.dm3mat.qdmr qdmr.desktop QDMR Hannes Matuschek GPL-3.0+ CC-BY-SA-3.0 Codeplug programming software for DMR radios

QDMR is a Qt5 application to program DMR radios. DMR is a digital modulation standard used in amateur and commercial radio. To this end, QDMR is an alternative codeplug programming software (CPS) that supports several radios of several manufacturers. Including AnyTone AT-D868UVE, AT-D878UV, AT-878UVII, AT-D578UV, TyT MD-390, MD-UV380, MD-UV390, MD-2017, Retevis RT8, RT3S, RT82, Radioddity RD5R, GD-77, Baofeng DMR-6X2 and radio running the OpenGD77 firmware.

https://raw.githubusercontent.com/hmatuschek/qdmr/master/doc/fig/qdmr-channels.png https://dm3mat.darc.de/qdmr/ https://github.com/hmatuschek/qdmr/issues
qdmr-0.12.3/dist/fedora/000077500000000000000000000000001501654372000147465ustar00rootroot00000000000000qdmr-0.12.3/dist/fedora/fix-docbook-xsl-path.patch000066400000000000000000000006641501654372000217370ustar00rootroot00000000000000diff --git a/doc/docbook_man.debian.xsl b/doc/docbook_man.debian.xsl index f6027aa..6c498bc 100644 --- a/doc/docbook_man.debian.xsl +++ b/doc/docbook_man.debian.xsl @@ -5,5 +5,5 @@ - + qdmr-0.12.3/dist/fedora/qdmr.spec000066400000000000000000000102501501654372000165630ustar00rootroot00000000000000# # spec file for package qdmr # # Copyright (c) 2021-2023, Martin Hauke # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. %define sover 0 %define realver 0.12.0 Name: qdmr Version: %{realver} Release: 0 Summary: Graphical code-plug programming tool for DMR radios License: GPL-3.0-or-later Group: Productivity/Hamradio/Other URL: https://dm3mat.darc.de/qdmr/ #Git-Clone: https://github.com/hmatuschek/qdmr.git Source: https://github.com/hmatuschek/qdmr/archive/refs/tags/v%{realver}.tar.gz Patch0: fix-docbook-xsl-path.patch BuildRequires: cmake BuildRequires: docbook5-style-xsl BuildRequires: xsltproc BuildRequires: gcc-c++ BuildRequires: hicolor-icon-theme BuildRequires: pkgconfig BuildRequires: pkgconfig(Qt5Core) BuildRequires: pkgconfig(Qt5Network) BuildRequires: pkgconfig(Qt5Positioning) BuildRequires: pkgconfig(Qt5SerialPort) BuildRequires: pkgconfig(Qt5Test) BuildRequires: pkgconfig(Qt5UiTools) BuildRequires: pkgconfig(Qt5Widgets) BuildRequires: pkgconfig(libusb-1.0) BuildRequires: pkgconfig(yaml-cpp) %description qdmr is a Qt5 application to program DMR radios. DMR is a digital modulation standard used in amateur and commercial radio. To this end, qdmr is an alternative codeplug programming software (CPS) that supports several radios across several manufacturers. Currently supported devices are: * Radioddity/Baofeng RD-5R, GD-73, GD-77 * TYT MD-380, MD-390, MD-UV380, MD-UV390, MD-2017 * Retevis RT8, RT3S, RT82 * Open GD77 firmware (GD77, RD-5R & DM-1801) * AnyTone AT-D868UVE, AT-D878UV, AT-878UVII, AT-D578UV * BTech DMR-6X2, DM-1701, DR-1801UV %package -n libdmrconf%{sover} Summary: Graphical code-plug programming tool for DMR radios Group: System/Libraries %description -n libdmrconf%{sover} qDMR is a simple to use and feature-rich code-plug programming software (CPS) for cheap DMR radios. This subpackage contains shared library part of libdmrconf. %package devel Summary: Development files for dmrconf Group: Development/Libraries/C and C++ Requires: libdmrconf%{sover} = %{version} %description devel qDMR is a simple to use and feature-rich code-plug programming software (CPS) for cheap DMR radios. This subpackage contains libraries and header files for developing applications that want to make use of libdmrconf. %prep %setup -q -n %{name}-%{realver} %patch 0 -p1 %build # with 0.12.1: -DBUILD_MAN=ON -DINSTALL_UDEV_PATH=%{buildroot}%{_udevrulesdir} %cmake -DBUILD_MAN=ON %cmake_build %install %cmake_install install -d %{buildroot}%{_udevrulesdir} # FIXME: would be nice to have udev-rules install path configurable via cmake # (will be there with 0.12.1). mv %{buildroot}%{_sysconfdir}/udev/rules.d/99-qdmr.rules %{buildroot}%{_udevrulesdir} %post -n libdmrconf%{sover} -p /sbin/ldconfig %postun -n libdmrconf%{sover} -p /sbin/ldconfig %files %license LICENSE %doc README.md %{_bindir}/dmrconf %{_bindir}/qdmr %{_udevrulesdir}/99-qdmr.rules %{_datadir}/applications/qdmr.desktop %{_metainfodir}/de.darc.dm3mat.qdmr.metainfo.xml %{_datadir}/icons/hicolor/*/apps/qdmr.png %{_mandir}/man1/dmrconf.1%{?ext_man} %{_mandir}/man1/qdmr.1%{?ext_man} %{_datadir}/locale/de/LC_MESSAGES/qdmr_qt.qm %{_datadir}/locale/en_US/LC_MESSAGES/qdmr_qt.qm %{_datadir}/locale/sv/LC_MESSAGES/qdmr_qt.qm %{_datadir}/locale/fr/LC_MESSAGES/qdmr_qt.qm %files -n libdmrconf%{sover} %{_libdir}/libdmrconf.so.* %files devel %{_includedir}/libdmrconf/ %{_libdir}/libdmrconf.so %changelog * Thu Jul 25 2024 Hannes Matuschek - Updated to 0.12.0 - added translations - updated description - fixed so-version - adapted from openSUSE spec file qdmr-0.12.3/dist/macosx/000077500000000000000000000000001501654372000150005ustar00rootroot00000000000000qdmr-0.12.3/dist/macosx/Info.plist000066400000000000000000000014271501654372000167540ustar00rootroot00000000000000 CFBundleExecutable qdmr CFBundleGetInfoString QDMR 0.4.1 CFBundleIdentifier com.github.hmatuschek.qdmr CFBundleIconFile qdmr.icns CFBundleInfoDictionaryVersion 1.0 CFBundleName QDMR CFBundlePackageType APPL CFBundleShortVersionString 0.4 CFBundleVersion 0.4.1 LSMinimumSystemVersion 10.9.0 qdmr-0.12.3/dist/macosx/qdmr.icns000066400000000000000000003335211501654372000166300ustar00rootroot00000000000000icnsQic12oPNG  IHDR@@PeiCCPICC Profilec``H,(a``+)rrR`> 8kuAfV'-@\\PTd!@HvH3dAՃsbNfRQbIj{Qbs~N~QqAbr* VhʢGoSv(x%10s 8\$Aad2f`]4yӑPIDATxZ lE=KAhh |*+(ZԨ($&A?/*m DPHH@h))B(RJ߻ -y}Q$ݝ={;wܙ;GJԫـH5] OHAԾ8r)xNGhd ^awD4@7/=@HA@zx)lɯq-w_xwៜ:=S<0 ԔQ[Kp e4s^M xMnQz7; [4)}@=1_P1*W!Z|G @PhMUB7 T (QPQ|(˸#:icߢ+կ?ǀ[qoljRN#XM1|0zY@a1A*2u5s>¸2WJ/_Uj , fF|`Hi(a@ 33`Kamѝ/?h[,%(r}RHό̔p:E5T=GR3O⋉; PZ0,p#=<kl%|=s,?-)V&b˪lH?fl8OxӜ'k}n:f`')_ 8v SA 0: [@Xb-(ouc êmpgLi]oXl[Ǡ+۵w[bw7`{Id4l3[#4J]=ρ!|XK+O;k n~.94o1XlBr&d%J;_('>Lz_@r Oc{ dYUžKDo$ }AGwhўIUQsR  ?lrAʠwlṐ,bq*.9l@;W{1TBOWo-EQs"D" Q"HT\j}*T6M(OW%8-M_ӣb@3חQ<,DKїǒ`1eUtdфU [2yuHȗu9]2>7'3xFŝ(^:G.l"pMy7Y$ @o|,2Is"o%د8 NjOoy.gnX *D@37L\_+E.]dR}!Vf|B%i!b@g .xQřyT#^x@-7t>SR)&=Iˇ ~9 ^Ⱦ8/YnqMM"mI!?qh9h6#2΋BK)d?:-m/6Fw? .xNw' =$C> |tIST\-`"mWHqiK$fHT\S"GW1MZ@@T?"8FVbiVkMSoJ D00j0Q_cRN2tp!_;sCN'\U:Uq7\MQ7KCsKs.̜Uz qpf`$;̡| `.c.>E˫oxPH>"};t%Ro~V>MNdNoKX"vȠT\Y-vř&@7=Cϰ)&pҫF8۸5gTQRS6 8kuAfV'-@\\PTd!@HvH3dAՃsbNfRQbIj{Qbs~N~QqAbr* VhʢGoSv(x%10s 8\$Aad2f`]4yӑIDATx] xE'IPȡЀ+EPFqY`Dt`EBT\\@Tt]SeuUTTQA9D |ͼ7鞼ꚪuWtj]yˁD,[8_ݢQ6jwρ{eZ끚j^v 뀁l907ZFU`]'QUT* QمT j$dZs&Xf@4B |<PR1l`?$ 7S"8z[vXAe?\卖F LU j@Ox>2:֗_n+OxK92@t2b^d饎M"ծ;7k`p^uwع|eo?=t1(^C!fH(/ dg]leph l3P%\U&9 N.(, |`DL;{y/+BdjmGD *hp%e1=q\eJ`z7mOWk`w?yg2hGe:ŎW{ ݙk?A>*O`7u 5Mzh5E3kY@7*5z^[Xs;k 4wxmah 3š7A^g൅5oVk k.@sy\Z=-|z^[Xs;k 4wxmah yWymȖ5Nly}Z w!&V~)Е@kj>g & T~[g>Z6|zLe뀾 L0Z`=g'˽]5NriI^#x[J횡%gV&6Ol,qz|ޞ֊<5 F>YY5xYn?~rc?}?:{a F?{/ ~agAw0" 0n#YГ+qs6b-ܧĚF1!be3~!_c]C~;W^-GH39Ĺ-exkm Qz+10ՆdZxme{C,|ǐC"wd"Ͼ(7mWcVSD.YCQ<>{et\ϟ!k&vn^Ucp`y`+ӝڭm''pLr qh+/qϖ]OAb󁾥)PI?mݜ[*Nm94qvh|OμQLHYs3j+Y?m;r݊ȝoB;?Fp ~َdh ՅIQ-@A!G&9[øPG hqS:=zGzChkgrm`qΆYu=0Ph,#>N4ྲྀL#/C|\|hxȲ7E+9::2X]Y%ZmjH喠z"k\Ym8oEĕ&EQ0N2=_\ >)N0c%5:Ǘ4 GwS- 13sD3 V7'2β-tluab봁ý].|,>1m2ft[lYmZI;ryWhu;\s%$9Epiaps8FDk`N`sfqe,K,{;/08Z0^d)G=K9J~zIQs@TGoLI'E>F*hCq }"M8I =29^cHEbX~7D4< 2n,ť"Ltz6Q3Q-%e<[ۛ+&db9K^d0{9 52 G]oCש=:q}!o 7\o(?~-ʁn@{y^r.#yTݼ e~RC`sghA3\ 8kuAfV'-@\\PTd!@HvH3dAՃsbNfRQbIj{Qbs~N~QqAbr* VhʢGoSv(x%10s 8\$Aad2f`]4yӑIDATx߻KSzGP@$v,ĮQI4F-?Dر15*6, 齳ttaw̜{s=swow~;sΜR to_xPX;]B`b IvCD ~ t~xy4P֌|->k>k[n:Q9>D@<8r`FR>0Ɠ0@ķ+Þ@:ΕKL\{74Vcy}bjr'_VD`h@'ooy>@Etjhw_6WjʟʴV<МV3RA@ ʡGs{GTb_=rIM?O3>h"@Xylu +6=Um[&NTP(@XoW', YFkD+޻)m&ZtJ@U"ȴz.OہØ`Y@+6J*% YdCc7lOT6jzt.|dBll d!yj!䜖}D۲ 񏗾O"].cGp{&w"}dZdEVv]GCE dPM  @H%7E $ɠ*"Jn@2HAU6E $$! dМQݏbŽ)RU#4[zXcon^Z'cJ|=e/͡wr ь_"6tGĄ">ȴ8YvP]TRZ(~XK^ S~ϹKkqi妜 ,^x5/R55^dܼb=pDa a8cZwrNR9,\$ 7i )u8-NvfC[՜B'~0X1*iIP _ gNc Mgo>GPt1]6~mW$Rs\<]#q3bu\_qݻ"|,D͹UFл-`+cmRҗH+#?՘9uX'X{?GT&#!o$?Hк ZE܅|$j._G ?.B1WQwhr7Ќ]$h8x"S v Б?YKY~Q-r}PG G;,Á+>Ku}%%oV.'Q4]{')ewe: y9pY5Wwة^sJv0қQ t:hQ>To= pغ%+C_ ?(`$j_Z>K\+!eDx)Jxpҽ|ˎJ#ՋU.%$m٫瘹%eEZ&Ј&P|ݍYQgě_VKKek奾 @jlϞ1М}\._uX@]5mI|D'[P}d.#KׁϻH1>?7ۅm6ٿ_y{T{܍J H3C{fƵ>2EJ2~ubǟgv,_}wqk/*$>"Wѯ0>2eƓEZ+0:ٰY^l9ӝ͎Įģ䝀;3ljBȲ~s5h 2v뢱H׋:\FuiO=)y&2ͰP|"7s众LVQ\ћیwAj!4hwqF܂u},OUb}9㮝OOel=}6GEud_(6J {9{`/§Xѧ#edGcOru+RVsnQ2J;7v"CWyHijIpa1ŸO*Oro[oǼItNXhR-cg7GMEbt7m}ti}S9Jl%knλCZZ\c@Xhp)aWgq. ̦( ӞRsRX;cr|wŖVq'.nLIտċeJ .@>KQpua«пKJב_WZ' |_JRĻg7c;G|ӭSOaR4c&~PrծkO:lf9ߦml}g!r 5YU6%ySEo-@no4y+kc3ͪ?ZyE?ÎG6`ÀmxzlVw nE 'O=QU/ ޚWn{*_9l+Ǝ4lD:|/a/=KqϵB֓_d=y-bv?~s«0[Gq<6ǦZv\z%`w<{xtV;N#,gYmSFwGWӏ/t՟}ݶ}^EoYs3nbvrN1y?]A_.|628kǵ 5e,Klj٭ ў8Oj/Er#MjݲlN$-l .N)f7p*%O%|5ܷ|\iol3 KYo_/gLm\~ΫV}^ʏk׹>@}Pcʅ{,37Jal^z󯨧\#p@uV~ yP@?f( _?p2\ڃCT;xH\EX=diX~7p!S/)kpڵsYԶ"@;S6[ە aЫUN$DVq\(ʰGvE`ǩ7)mY߾rHx{" f 'Y ? X{_-Tf']P>[NTY%;?y׭?Є\f7܍}.Z3(y& l[\6g.RISk6 umп*31S5S֡w̐3xs>LKX bg*e#NPte8j丱Wyv䝀;3Tbكy#sY7J-"U46e,oOO+/n)-bM#[2տbƋ|d.#.%ie?zz|v=cr67>\(:4k kY#ԛ_Fv3}d.#+h.Rqai"h>$ޡg ؊ gd5 օANlG%ެ`]O!IOeD#=jٛmEfOCoۋ|U[۝>;Q$AzQfruU E'&6&o9Ϭum?Y_߽~3֬kI2P^;W6{-N@^bVϘlc}'zY68FG^_yj|JT{'x( Z=ޏp"iT9>3,5 i:O\ʉ>᳻?Ws֛QR0 zz_zyŒ^H./ÿ/R({_%}W ?ZKWPG^ 0欖pƠDm] i16¦)m/\{9's%^|3!Rb-r³sֽ}ƎCmYlL+`cRן]tSb؎h8=`J}ʿYف{,lʢ섆;]2fVkY\]mJfr~w݅$WbCmV]ry5oGQWМstq8_']Ӧ9~"]6)!(%$L@{$ peZҝ #$D $I+"$!'I$u&[{%1R2-I uL_IW d4S`K]NL)WXU온_g;U8 qTrbA@FE  :@m/1l$@h?꥞;e |?#GD n?ᔧ3"{]WrJNסrn5S]K*3ZyW<&s M%VxO8%lDSt4 ptF~U3Ow;`cНsu'q }zyGX :koyd{]U?F3 f﹄w%5sƓ0lJ"P.j!xD; sN l]?N9l8'[lҖ"mIDWo>3z' hp;g\@tD9.\";o=: IENDB`ic08 ډPNG  IHDR뼮iCCPICC Profilec``H,(a``+)rrR`> 8kuAfV'-@\\PTd!@HvH3dAՃsbNfRQbIj{Qbs~N~QqAbr* VhʢGoSv(x%10s 8\$Aad2f`]4yӑIDATx߻KSzGP@$v,ĮQI4F-?Dر15*6, 齳ttaw̜{s=swow~;sΜR to_xPX;]B`b IvCD ~ t~xy4P֌|->k>k[n:Q9>D@<8r`FR>0Ɠ0@ķ+Þ@:ΕKL\{74Vcy}bjr'_VD`h@'ooy>@Etjhw_6WjʟʴV<МV3RA@ ʡGs{GTb_=rIM?O3>h"@Xylu +6=Um[&NTP(@XoW', YFkD+޻)m&ZtJ@U"ȴz.OہØ`Y@+6J*% YdCc7lOT6jzt.|dBll d!yj!䜖}D۲ 񏗾O"].cGp{&w"}dZdEVv]GCE dPM  @H%7E $ɠ*"Jn@2HAU6E $$! dМQݏbŽ)RU#4[zXcon^Z'cJ|=e/͡wr ь_"6tGĄ">ȴ8YvP]TRZ(~XK^ S~ϹKkqi妜 ,^x5/R55^dܼb=pDa a8cZwrNR9,\$ 7i )u8-NvfC[՜B'~0X1*iIP _ gNc Mgo>GPt1]6~mW$Rs\<]#q3bu\_qݻ"|,D͹UFл-`+cmRҗH+#?՘9uX'X{?GT&#!o$?Hк ZE܅|$j._G ?.B1WQwhr7Ќ]$h8x"S v Б?YKY~Q-r}PG G;,Á+>Ku}%%oV.'Q4]{')ewe: y9pY5Wwة^sJv0қQ t:hQ>To= pغ%+C_ ?(`$j_Z>K\+!eDx)Jxpҽ|ˎJ#ՋU.%$m٫瘹%eEZ&Ј&P|ݍYQgě_VKKek奾 @jlϞ1М}\._uX@]5mI|D'[P}d.#KׁϻH1>?7ۅm6ٿ_y{T{܍J H3C{fƵ>2EJ2~ubǟgv,_}wqk/*$>"Wѯ0>2eƓEZ+0:ٰY^l9ӝ͎Įģ䝀;3ljBȲ~s5h 2v뢱H׋:\FuiO=)y&2ͰP|"7s众LVQ\ћیwAj!4hwqF܂u},OUb}9㮝OOel=}6GEud_(6J {9{`/§Xѧ#edGcOru+RVsnQ2J;7v"CWyHijIpa1ŸO*Oro[oǼItNXhR-cg7GMEbt7m}ti}S9Jl%knλCZZ\c@Xhp)aWgq. ̦( ӞRsRX;cr|wŖVq'.nLIտċeJ .@>KQpua«пKJב_WZ' |_JRĻg7c;G|ӭSOaR4c&~PrծkO:lf9ߦml}g!r 5YU6%ySEo-@no4y+kc3ͪ?ZyE?ÎG6`ÀmxzlVw nE 'O=QU/ ޚWn{*_9l+Ǝ4lD:|/a/=KqϵB֓_d=y-bv?~s«0[Gq<6ǦZv\z%`w<{xtV;N#,gYmSFwGWӏ/t՟}ݶ}^EoYs3nbvrN1y?]A_.|628kǵ 5e,Klj٭ ў8Oj/Er#MjݲlN$-l .N)f7p*%O%|5ܷ|\iol3 KYo_/gLm\~ΫV}^ʏk׹>@}Pcʅ{,37Jal^z󯨧\#p@uV~ yP@?f( _?p2\ڃCT;xH\EX=diX~7p!S/)kpڵsYԶ"@;S6[ە aЫUN$DVq\(ʰGvE`ǩ7)mY߾rHx{" f 'Y ? X{_-Tf']P>[NTY%;?y׭?Є\f7܍}.Z3(y& l[\6g.RISk6 umп*31S5S֡w̐3xs>LKX bg*e#NPte8j丱Wyv䝀;3Tbكy#sY7J-"U46e,oOO+/n)-bM#[2տbƋ|d.#.%ie?zz|v=cr67>\(:4k kY#ԛ_Fv3}d.#+h.Rqai"h>$ޡg ؊ gd5 օANlG%ެ`]O!IOeD#=jٛmEfOCoۋ|U[۝>;Q$AzQfruU E'&6&o9Ϭum?Y_߽~3֬kI2P^;W6{-N@^bVϘlc}'zY68FG^_yj|JT{'x( Z=ޏp"iT9>3,5 i:O\ʉ>᳻?Ws֛QR0 zz_zyŒ^H./ÿ/R({_%}W ?ZKWPG^ 0欖pƠDm] i16¦)m/\{9's%^|3!Rb-r³sֽ}ƎCmYlL+`cRן]tSb؎h8=`J}ʿYف{,lʢ섆;]2fVkY\]mJfr~w݅$WbCmV]ry5oGQWМstq8_']Ӧ9~"]6)!(%$L@{$ peZҝ #$D $I+"$!'I$u&[{%1R2-I uL_IW d4S`K]NL)WXU온_g;U8 qTrbA@FE  :@m/1l$@h?꥞;e |?#GD n?ᔧ3"{]WrJNסrn5S]K*3ZyW<&s M%VxO8%lDSt4 ptF~U3Ow;`cНsu'q }zyGX :koyd{]U?F3 f﹄w%5sƓ0lJ"P.j!xD; sN l]?N9l8'[lҖ"mIDWo>3z' hp;g\@tD9.\";o=: IENDB`ic04"ARGBr ^¾  9nppnp8 {{ 0! KNdg,,fr 4AQ8=]V*,>$)9nppnp8 {{ 0! KNdg,,fr 4AQ8=]V*,>$)9nppnp8 {{ 0! KNdg,,fr 4AQ8=]V*,>$)ic14KPNG  IHDR2iCCPICC Profilec``H,(a``+)rrR`> 8kuAfV'-@\\PTd!@HvH3dAՃsbNfRQbIj{Qbs~N~QqAbr* VhʢGoSv(x%10s 8\$Aad2f`]4yӑ@IDATxҫ "bC=j4Ѩ^b/Qc KlQcW(E"*,{=|\{˜Μh"5)u_6LZ8im ݲ͖tM|y~OGJ_;kK_]%M8SX @ T,%tcyTqPX}ȏ7H/]UR.  @i}S|7(冾uUI.G(]T#jA@ 3 {&I3J|DG*Nm*5.! WxoCIhXG?/F_@({v&*d`ƯS둀M˾kh CNu[iO  BZ/!*ʿ@ȽaIeo_̺S:@轪Kܣxr#(@TC-U2Z?NCίU!  owPq*{,ubD ˫m}_.mo u6Cj#@P->AnGD}S%P ^^M^ao3bkyQ7_b.!+@R|.@uVPy*_w{rD| 䫿ˬ v6{r\^}&yi-Dx$쩹f㥖kX>r"ڂi /D @QH@HS ~!*@*@PT^ @$i Q! PTp@ H_ @ # @BT U,@4HB@$Ee )@f E (*/ G@ M4@( @QyY8 i /D @QH@HS ~!*@*@PT^ @$i Q! PTp@ H_ @ # @BT U,@4HB@$Ee )@f E (*/ G@ M4@( @QyY8 i /D @QH@HS ~!*@*@PT^ @$i Q! PTp@ H_ @ # @BT U,@4HB@$Ee )@f E (*/ G@ M4@( @QyY8 i /D @QH@HS ~!*@*@PT^ @$i Q! PTFE]: G t<"5RK?wKkBNTyCϯW<*D 1:p(?f/uEp>Hݏxωnj捽~g҈>~e~*Mq5U"] z(Pkǿ'K!m{+?vsHe+8Mr2p[T=ݎn^Z/Q h4SW>cvGI边QrOzJ:a|Ԣ훤Od @.Hn'x8}}i7qvD@P9hS%t۽xMƏ}dA H(@󍤣o2.˅tx,'h~B'`x| TL^ґgHΧZ&gIm8Epa\Փ$|tH"CDK~"Ojve*@5=ba 4&X ǝON`[On[;[Kt];\%N&@0^C Wk'%u[7/S<(@Rw_R{M!@F?c#U^նS˼cw>MGe QW&5ʀu{QHS9ZU_uI͹2`bLԍ@\C|s Bas>*{~H< gytgQP(?3"F@ }R?/rVkK 4|K T`O`6 v*P(?FʯψB ja,Kh@( @AY %@P^E D ,@ ("Z@ "@PF @y W-  (# A@Hʫ@  P^$_D @AH B(K1R8,#'hw7! @oXuվUi?gwIm0v9-u=MIs/ei~~Z6?4{Ui:i~Z.#P':h4E+I6ڟ=/y7}e6RZ&~e6Wft y6vKZz#D@ӶHs6vҔ&_IJ#>z85 FA[xr}ivG{=e) M YB /J㪤!K_^% K<0ɣgU+~@HǷ 4 wi۽\# PJ~]‰@_ <:p>sHHo*~']@^}^%u\ _C T&[Vˤ}ՖN.㼊Hû?-I>Ջ3J~ڊw(a>v@iBW-0ͣuhxR|I@SKED6+J}{ +ڣ~,@ X]'{W@?ϻ P|40iHT) ǰץY=P ,0g H+7 0s{a*6fK׍`? OH^/tүT|E {TZY>ը/KAʷ˷z`Nyu\ɢ%(uU<G| [n C=<m>K+&@0\d}McȽrM!$9jing u`z]i\Fs$͹ON{fYէR],3_>- K%/4ZGY3@. u\>f 9 [ΑZN< 0^ }~  _gū [L7iB6E+L`Tie.{4#' ߐ%@|GjS7^/~RAP UI/!x4/&~ ;laŎgP&@/~DZ-]|rj(o^Ɓ?s24%8XYzTg}t kAci ˨M=?yn֝};}[(D4{=} ճv~<(gH7@BPgwJ+^#]!f]d(3n>4o#Wz#_}@JĢ&6zw5WvI}\t7Ҙ%˷=D-F՟eߚ<۾? l˾3i\Λ"tԤܗy@TT>JY*Ȇ@]#o;/@P}QoX?2B:,ӿ,Yۼ@IHJMe?%O^?.#KK{$ki-)O&ge*<[m}3,Bl-_~(#qEge!ʚ@Jiyb 0Kz>ϙ 7IKz <>o 0LW3NHx(@L},VޔHRƲ/9l8Mν@C}W`O C :uό4~ŧ^Z=&X) /]}Q}jڊwW=+ AT@-6@ 4۾}~O u <}& @4hT6 @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ @G3 1HbSai:` hRDZ`Z?iX{?v/϶FPTLnO.pN jP!h,X#@fymUvZFѝ6ʭ ϫ@Ҡnyݱ9ĵCr@s)0ͭ^.N H#ƴko(,4> @z.O #0,韷x'@^.6.cf5P "t9l[LBع?CڽO\l<#Iv AU t>UڳҚ>v~c|HS`o.5c_K< M:ivQAUa3'xD&7Hm){xjIU-MEͲʿW=bʁ}c/>>y7{>3m"LWf!3M` R=G@WT U8(byUwh  X &.PWkQSKxq[$ v*MB@&x@ dSi 5 $ dP J@I&!G@ $T @M$5 >  `$@j I@Ƞ @;&! P @MB @H2ة4 @Hj}@2(@NI $@P# A v*MB@&x@ dSi 5 $ dP J@I&!G@ $T @M$5 >  `$@j I@Ƞ @;&! P @MB @H2ة4 @Hj}@2(@NI $@P# A v*MB@&x@ dSi 5 $ dP J@I&!G@ $T @M$5 >  `$@j I@Ƞ @;&! P @MB @H2ة4 @Hj}@2(@NI $@P# A vjnTq[{LnZ\؆ߵ^]lV1ŭ=3O-YhЖl Tl!Ix,tiGFl F&*U]m [MZjW) o"I>^u! .uX~>*{7wyWo4ns-h^&H2эm7JϖzARqҊ;J R-}1Nze^_)}rv4kiֳҪ~,iy儩b[o]?*D^[K|4zMiқu{y,-'K}ηGߣNg#l7ݏ `=7/X. iC`}8/oCGO-Chzst aBkZC[0kZ=CxBTot!r(^ 'C􆻾elBJq-+?zK=v aݮ /;N'VN^b/!|hGS y}8oBϪM͏ +}tF}C=3ڝ_ ;˷Ch#FW†˄72ӟ^<%ެez#Υ!=[lf}; ᷝCh&%?9kBE i$?4J}|==N><}SD!}R+' ayWZu'PӀ\ȿƮdžУ{MR0$J�G ~|'iC eU> aRX?{uns!ߙw7 a+{ IDC&\H^V!ᑨOO`Κȡ@(8%ϻZnZޖJ14<&fXy[VUcB5Py :!C'0IMKN!tp2P 2DÕ&s!O"Z\S{Fim wM/r/qBhL:\iqo|1껤}ph[i_Kۧ?IpNbF aۘVN 3"+鈳-RJWU<{K9IFTS%"cm*o7 UN; PjRS߷[s tV\x#p}r*tG|"5w˯[J{xOZ>S.e,'$Yγ7c9/Jfۜy迋'XanYri{>4ʇVx,Ȉ)$YDu<9_R_PIξ&@vI67_PUsLInX=:AAD$%:D%h'ԺJH7:&(Ϋ ?tWyrGWt! diavOtLH.?z}hL+-7{q>q8ZdIԣJ!@P eVVk'jiur}Wmӎonl 8eJ-˨R|=WOn C^HӉs&H0? cOG/&_I/m'OC7L39QD/47Dʿ_0[kBBOS\ʥjo )~M2 :#(KM# WJz6wKR]+$m뾈b}s&) &MU S+o7 P*/KACFz)~{O)EX.뉀^W'})GkWo:h}؟Xz),t$^t]7/5x\sϣa=4 HyߏOMQ|zt0Vx4*/I&C(xGaH Rj!0{SzO{C)ΐ.WM1T--:^w\͟-'>@ޠRS-+zAA$Ԧ_y_ȣ| K=cO*2ƳٯSQO8O:sqmS[9Ƴ;YYzei, (+|FQX vң/ǓЫJW_cW tNHʭ;A)|&'!al6$YGx/g$z^}M*GaM,o?,]KP}Cw7?‡. H8S+$ Ṙ|a[?ݥ>㏅^7uٚ{?[Rwo!t89ω[W`^ GxCGyGz({,U8Y:b//fM[ӥC71M%׾!4szyN:)B !F7}+i-tEċ Pz49&3C^sBRdV!Z1oqhM:+*B$Pe!xE(Ns? a_p\tn~K$#V0g8?x% OWFpZ+ޫR[/[y4xy* ӿ{BŋwNo!x;ۭQ쪿r0?HYG'bQ-Pz-W6RVŶMN[0oI^U}|ugX?>i?Wz>No7RslJjȂNu?E}Hz F[Wn.vXr7ş췍-=_E;C?Y{jF8٧ =cߚ5cke:1I"AS>]Bx*o}˗B]Jя8U@Y4j!L e!;16 WE{{\H={OBJ!4YXSI捣zxBeafs}=rNMɏ,V@qH=‘ˆ.]λ{$;Ki%\Xlm8hZ6o |!y˽]~~Qw.|K;҇d䢛'nwgR( RDžpe!TyϲeyKqn_̜GMB:HJmw CewϿzI$$e&H#7hKop /,X[B8lo{q!T6ucW.,uoSZP.v*V( @x# ٕK5os ] [m 5^Imk [ك{ݭ|LFzYzwUPCF"td"_4^׾M}崘eS<䒘Qԡ}F{Jخ9ɿϯ}7אŘE(Z6S?_Y-jyTɗp{Qumۥ¾O|E]oIgDU#=+9ϝ @<~;eRҩ)NH:/M1tT_>Ң2hjҟΖϖ9hRF @J$p_~Uլb~8zMAZyHˡ푧m< At1HڹG|  (4gO*9DjW,2u<\4s;L XiE͆9jܨUSa\7r\Z־S^Ӌm|;ڟyf9ht,2n{f P*RISϷm=a,߆~)-s^)-wU͉o#{)VN@HJ%M= Ekd_,'}Q> )>ob&Rt" Β%-@v>N=K+WK!>w<ɲ>IF67[H/;ϤYuNR!LH2)5b'ϴ픢!6w.C~ZzٰYT:W'PS'jYuha(n|D-DWB5 {n &z'aK>"_ǾӍۍ6Fmn|D-lg4ٳS,%zzg0J^i}^R,cH/2J%@P*iV`}R7Ę ҌmNTߜH a5pe&P8r[Hjo7!0޸ (W[ JaUmQx?F,B YK_':~.Ҩ+X\$ tBBǤ_3^lEt1i'j|u4O,2lH7 N(JT3W`h_iL&d+xzJ,2A{y"SHߜmJ^m*|!OF]K3+}W $ B!ݴWv(vMgHz~B9!_HW_D=۶e҉;v<d_ }\ gzjx}`ވM>7xjbgҝJό铥y# &?4ջ[/KK>b?OR @pK:%雡Zbݖ3qe鯞n{=<0ԉK2{wv?t7UF F龆zVBf\P=/ XH_̇.)W>-jx1Ps~H[>s+fuPx"žyʹqoUr뻤}y睺}?7wM9 N$#}~abPι~ܗJdg[%hi{v/S2wջ{M)eAE*e9UfO< HbS\ >C IDAT'<{T'/jk^5y$XKW\r>D:Ӊ۝g|˛H>s>V[s/tOn)@ M!tBh(J$up{G$l<„Ufƒy!4l=C8&W>12m~nRq*ȡ@" 6!7&/0Ͼ mChe*+‰nCxN0~0zNrraJq⎞~kv_; 3sS2|N"eWr)pO#VV_1/ aj'L^<4hҊxTlBwoĮ2QoGc(^k e5pQo9-? H o<66'.;c6a=Hޭ{b[T+ss$}?|Hϩx6n|s%=B-g#4/*U[U>7N"5>?7f?[:/DjS+csyD>]{Ko_Gt2gy@"9Z-&"\#xy-o]Zkv~ UFm+E=bi@饛Ӫ㾾Χy%/mBji=yo/m9ݝ0]$=k%=Ul9ֲmKcO l_7>,mډ@U{#sn'џ|tˌ{|Hd P$0\v axB/=-!buMa͏-Bh>~פX$>m|eq!x:kv(Ҋ,@YtS1L|Mg^<@ i7 @Hr4@ $yyڍ k\w?Gȫ @^{v#Z O@*@מ  ux@ i7 @Hr4@ $yyڍ k\w?Gȫ @^{v#Z O@*@מ  ux@ i7 M3B% gj@I@O|;F,ՕҌȧ @>V#}ȧ @>}VO9b?xs+rd;W`.E^ <_~^[Oȷ.6oI߷eIr)0|Iil: @CN{ȝ@zҸٹk6 +h3T`Gu4 >L|W^[O?KV$0x+'~P*@^{~nK/>(ͮ\ i*}d97ͤqX仼S~Ii&9>PZR$wM>@d[҇>Gɳ @{nfK5% @Ff*)3@XL o*-D )= Hr;,w͟#}}&p06Y:aW2+0+}F@53L@n^(EKc+sK@ 0`.O|I{^A4@ k<}̬Mnn[HWw n$MC o}_  p_K,%܏O(K!s?`_BEқV8[`|iU B 0Ph,o4i}Ҧ3־NO`Y)U>T~q1H[=>K= GI-zws ̘ ]^"iԼ,@PQ?Q >v3YPC( ߹~t+N$q˨֪=`gi֝YRߔQ@!]9Z7>n /tjiW@&5.Vb iB#$tH/p@S{xЄ@Nm),ߊO'0/GVF^/&yM1@ ̸ݗJ'x0N.MT.;ButOO-dV`[v/qY8O_(q?|A~&@P77p |^ݤ>mIZ8HA ='p ҈f^G(OL ;IENDB`ic09KPNG  IHDR2iCCPICC Profilec``H,(a``+)rrR`> 8kuAfV'-@\\PTd!@HvH3dAՃsbNfRQbIj{Qbs~N~QqAbr* VhʢGoSv(x%10s 8\$Aad2f`]4yӑ@IDATxҫ "bC=j4Ѩ^b/Qc KlQcW(E"*,{=|\{˜Μh"5)u_6LZ8im ݲ͖tM|y~OGJ_;kK_]%M8SX @ T,%tcyTqPX}ȏ7H/]UR.  @i}S|7(冾uUI.G(]T#jA@ 3 {&I3J|DG*Nm*5.! WxoCIhXG?/F_@({v&*d`ƯS둀M˾kh CNu[iO  BZ/!*ʿ@ȽaIeo_̺S:@轪Kܣxr#(@TC-U2Z?NCίU!  owPq*{,ubD ˫m}_.mo u6Cj#@P->AnGD}S%P ^^M^ao3bkyQ7_b.!+@R|.@uVPy*_w{rD| 䫿ˬ v6{r\^}&yi-Dx$쩹f㥖kX>r"ڂi /D @QH@HS ~!*@*@PT^ @$i Q! PTp@ H_ @ # @BT U,@4HB@$Ee )@f E (*/ G@ M4@( @QyY8 i /D @QH@HS ~!*@*@PT^ @$i Q! PTp@ H_ @ # @BT U,@4HB@$Ee )@f E (*/ G@ M4@( @QyY8 i /D @QH@HS ~!*@*@PT^ @$i Q! PTp@ H_ @ # @BT U,@4HB@$Ee )@f E (*/ G@ M4@( @QyY8 i /D @QH@HS ~!*@*@PT^ @$i Q! PTFE]: G t<"5RK?wKkBNTyCϯW<*D 1:p(?f/uEp>Hݏxωnj捽~g҈>~e~*Mq5U"] z(Pkǿ'K!m{+?vsHe+8Mr2p[T=ݎn^Z/Q h4SW>cvGI边QrOzJ:a|Ԣ훤Od @.Hn'x8}}i7qvD@P9hS%t۽xMƏ}dA H(@󍤣o2.˅tx,'h~B'`x| TL^ґgHΧZ&gIm8Epa\Փ$|tH"CDK~"Ojve*@5=ba 4&X ǝON`[On[;[Kt];\%N&@0^C Wk'%u[7/S<(@Rw_R{M!@F?c#U^նS˼cw>MGe QW&5ʀu{QHS9ZU_uI͹2`bLԍ@\C|s Bas>*{~H< gytgQP(?3"F@ }R?/rVkK 4|K T`O`6 v*P(?FʯψB ja,Kh@( @AY %@P^E D ,@ ("Z@ "@PF @y W-  (# A@Hʫ@  P^$_D @AH B(K1R8,#'hw7! @oXuվUi?gwIm0v9-u=MIs/ei~~Z6?4{Ui:i~Z.#P':h4E+I6ڟ=/y7}e6RZ&~e6Wft y6vKZz#D@ӶHs6vҔ&_IJ#>z85 FA[xr}ivG{=e) M YB /J㪤!K_^% K<0ɣgU+~@HǷ 4 wi۽\# PJ~]‰@_ <:p>sHHo*~']@^}^%u\ _C T&[Vˤ}ՖN.㼊Hû?-I>Ջ3J~ڊw(a>v@iBW-0ͣuhxR|I@SKED6+J}{ +ڣ~,@ X]'{W@?ϻ P|40iHT) ǰץY=P ,0g H+7 0s{a*6fK׍`? OH^/tүT|E {TZY>ը/KAʷ˷z`Nyu\ɢ%(uU<G| [n C=<m>K+&@0\d}McȽrM!$9jing u`z]i\Fs$͹ON{fYէR],3_>- K%/4ZGY3@. u\>f 9 [ΑZN< 0^ }~  _gū [L7iB6E+L`Tie.{4#' ߐ%@|GjS7^/~RAP UI/!x4/&~ ;laŎgP&@/~DZ-]|rj(o^Ɓ?s24%8XYzTg}t kAci ˨M=?yn֝};}[(D4{=} ճv~<(gH7@BPgwJ+^#]!f]d(3n>4o#Wz#_}@JĢ&6zw5WvI}\t7Ҙ%˷=D-F՟eߚ<۾? l˾3i\Λ"tԤܗy@TT>JY*Ȇ@]#o;/@P}QoX?2B:,ӿ,Yۼ@IHJMe?%O^?.#KK{$ki-)O&ge*<[m}3,Bl-_~(#qEge!ʚ@Jiyb 0Kz>ϙ 7IKz <>o 0LW3NHx(@L},VޔHRƲ/9l8Mν@C}W`O C :uό4~ŧ^Z=&X) /]}Q}jڊwW=+ AT@-6@ 4۾}~O u <}& @4hT6 @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ ԉ @d@  N D T @ @G3 1HbSai:` hRDZ`Z?iX{?v/϶FPTLnO.pN jP!h,X#@fymUvZFѝ6ʭ ϫ@Ҡnyݱ9ĵCr@s)0ͭ^.N H#ƴko(,4> @z.O #0,韷x'@^.6.cf5P "t9l[LBع?CڽO\l<#Iv AU t>UڳҚ>v~c|HS`o.5c_K< M:ivQAUa3'xD&7Hm){xjIU-MEͲʿW=bʁ}c/>>y7{>3m"LWf!3M` R=G@WT U8(byUwh  X &.PWkQSKxq[$ v*MB@&x@ dSi 5 $ dP J@I&!G@ $T @M$5 >  `$@j I@Ƞ @;&! P @MB @H2ة4 @Hj}@2(@NI $@P# A v*MB@&x@ dSi 5 $ dP J@I&!G@ $T @M$5 >  `$@j I@Ƞ @;&! P @MB @H2ة4 @Hj}@2(@NI $@P# A v*MB@&x@ dSi 5 $ dP J@I&!G@ $T @M$5 >  `$@j I@Ƞ @;&! P @MB @H2ة4 @Hj}@2(@NI $@P# A vjnTq[{LnZ\؆ߵ^]lV1ŭ=3O-YhЖl Tl!Ix,tiGFl F&*U]m [MZjW) o"I>^u! .uX~>*{7wyWo4ns-h^&H2эm7JϖzARqҊ;J R-}1Nze^_)}rv4kiֳҪ~,iy儩b[o]?*D^[K|4zMiқu{y,-'K}ηGߣNg#l7ݏ `=7/X. iC`}8/oCGO-Chzst aBkZC[0kZ=CxBTot!r(^ 'C􆻾elBJq-+?zK=v aݮ /;N'VN^b/!|hGS y}8oBϪM͏ +}tF}C=3ڝ_ ;˷Ch#FW†˄72ӟ^<%ެez#Υ!=[lf}; ᷝCh&%?9kBE i$?4J}|==N><}SD!}R+' ayWZu'PӀ\ȿƮdžУ{MR0$J�G ~|'iC eU> aRX?{uns!ߙw7 a+{ IDC&\H^V!ᑨOO`Κȡ@(8%ϻZnZޖJ14<&fXy[VUcB5Py :!C'0IMKN!tp2P 2DÕ&s!O"Z\S{Fim wM/r/qBhL:\iqo|1껤}ph[i_Kۧ?IpNbF aۘVN 3"+鈳-RJWU<{K9IFTS%"cm*o7 UN; PjRS߷[s tV\x#p}r*tG|"5w˯[J{xOZ>S.e,'$Yγ7c9/Jfۜy迋'XanYri{>4ʇVx,Ȉ)$YDu<9_R_PIξ&@vI67_PUsLInX=:AAD$%:D%h'ԺJH7:&(Ϋ ?tWyrGWt! diavOtLH.?z}hL+-7{q>q8ZdIԣJ!@P eVVk'jiur}Wmӎonl 8eJ-˨R|=WOn C^HӉs&H0? cOG/&_I/m'OC7L39QD/47Dʿ_0[kBBOS\ʥjo )~M2 :#(KM# WJz6wKR]+$m뾈b}s&) &MU S+o7 P*/KACFz)~{O)EX.뉀^W'})GkWo:h}؟Xz),t$^t]7/5x\sϣa=4 HyߏOMQ|zt0Vx4*/I&C(xGaH Rj!0{SzO{C)ΐ.WM1T--:^w\͟-'>@ޠRS-+zAA$Ԧ_y_ȣ| K=cO*2ƳٯSQO8O:sqmS[9Ƴ;YYzei, (+|FQX vң/ǓЫJW_cW tNHʭ;A)|&'!al6$YGx/g$z^}M*GaM,o?,]KP}Cw7?‡. H8S+$ Ṙ|a[?ݥ>㏅^7uٚ{?[Rwo!t89ω[W`^ GxCGyGz({,U8Y:b//fM[ӥC71M%׾!4szyN:)B !F7}+i-tEċ Pz49&3C^sBRdV!Z1oqhM:+*B$Pe!xE(Ns? a_p\tn~K$#V0g8?x% OWFpZ+ޫR[/[y4xy* ӿ{BŋwNo!x;ۭQ쪿r0?HYG'bQ-Pz-W6RVŶMN[0oI^U}|ugX?>i?Wz>No7RslJjȂNu?E}Hz F[Wn.vXr7ş췍-=_E;C?Y{jF8٧ =cߚ5cke:1I"AS>]Bx*o}˗B]Jя8U@Y4j!L e!;16 WE{{\H={OBJ!4YXSI捣zxBeafs}=rNMɏ,V@qH=‘ˆ.]λ{$;Ki%\Xlm8hZ6o |!y˽]~~Qw.|K;҇d䢛'nwgR( RDžpe!TyϲeyKqn_̜GMB:HJmw CewϿzI$$e&H#7hKop /,X[B8lo{q!T6ucW.,uoSZP.v*V( @x# ٕK5os ] [m 5^Imk [ك{ݭ|LFzYzwUPCF"td"_4^׾M}崘eS<䒘Qԡ}F{Jخ9ɿϯ}7אŘE(Z6S?_Y-jyTɗp{Qumۥ¾O|E]oIgDU#=+9ϝ @<~;eRҩ)NH:/M1tT_>Ң2hjҟΖϖ9hRF @J$p_~Uլb~8zMAZyHˡ푧m< At1HڹG|  (4gO*9DjW,2u<\4s;L XiE͆9jܨUSa\7r\Z־S^Ӌm|;ڟyf9ht,2n{f P*RISϷm=a,߆~)-s^)-wU͉o#{)VN@HJ%M= Ekd_,'}Q> )>ob&Rt" Β%-@v>N=K+WK!>w<ɲ>IF67[H/;ϤYuNR!LH2)5b'ϴ픢!6w.C~ZzٰYT:W'PS'jYuha(n|D-DWB5 {n &z'aK>"_ǾӍۍ6Fmn|D-lg4ٳS,%zzg0J^i}^R,cH/2J%@P*iV`}R7Ę ҌmNTߜH a5pe&P8r[Hjo7!0޸ (W[ JaUmQx?F,B YK_':~.Ҩ+X\$ tBBǤ_3^lEt1i'j|u4O,2lH7 N(JT3W`h_iL&d+xzJ,2A{y"SHߜmJ^m*|!OF]K3+}W $ B!ݴWv(vMgHz~B9!_HW_D=۶e҉;v<d_ }\ gzjx}`ވM>7xjbgҝJό铥y# &?4ջ[/KK>b?OR @pK:%雡Zbݖ3qe鯞n{=<0ԉK2{wv?t7UF F龆zVBf\P=/ XH_̇.)W>-jx1Ps~H[>s+fuPx"žyʹqoUr뻤}y睺}?7wM9 N$#}~abPι~ܗJdg[%hi{v/S2wջ{M)eAE*e9UfO< HbS\ >C IDAT'<{T'/jk^5y$XKW\r>D:Ӊ۝g|˛H>s>V[s/tOn)@ M!tBh(J$up{G$l<„Ufƒy!4l=C8&W>12m~nRq*ȡ@" 6!7&/0Ͼ mChe*+‰nCxN0~0zNrraJq⎞~kv_; 3sS2|N"eWr)pO#VV_1/ aj'L^<4hҊxTlBwoĮ2QoGc(^k e5pQo9-? H o<66'.;c6a=Hޭ{b[T+ss$}?|Hϩx6n|s%=B-g#4/*U[U>7N"5>?7f?[:/DjS+csyD>]{Ko_Gt2gy@"9Z-&"\#xy-o]Zkv~ UFm+E=bi@饛Ӫ㾾Χy%/mBji=yo/m9ݝ0]$=k%=Ul9ֲmKcO l_7>,mډ@U{#sn'џ|tˌ{|Hd P$0\v axB/=-!buMa͏-Bh>~פX$>m|eq!x:kv(Ҋ,@YtS1L|Mg^<@ i7 @Hr4@ $yyڍ k\w?Gȫ @^{v#Z O@*@מ  ux@ i7 @Hr4@ $yyڍ k\w?Gȫ @^{v#Z O@*@מ  ux@ i7 M3B% gj@I@O|;F,ՕҌȧ @>V#}ȧ @>}VO9b?xs+rd;W`.E^ <_~^[Oȷ.6oI߷eIr)0|Iil: @CN{ȝ@zҸٹk6 +h3T`Gu4 >L|W^[O?KV$0x+'~P*@^{~nK/>(ͮ\ i*}d97ͤqX仼S~Ii&9>PZR$wM>@d[҇>Gɳ @{nfK5% @Ff*)3@XL o*-D )= Hr;,w͟#}}&p06Y:aW2+0+}F@53L@n^(EKc+sK@ 0`.O|I{^A4@ k<}̬Mnn[HWw n$MC o}_  p_K,%܏O(K!s?`_BEқV8[`|iU B 0Ph,o4i}Ҧ3־NO`Y)U>T~q1H[=>K= GI-zws ̘ ]^"iԼ,@PQ?Q >v3YPC( ߹~t+N$q˨֪=`gi֝YRߔQ@!]9Z7>n /tjiW@&5.Vb iB#$tH/p@S{xЄ@Nm),ߊO'0/GVF^/&yM1@ ̸ݗJ'x0N.MT.;ButOO-dV`[v/qY8O_(q?|A~&@P77p |^ݤ>mIZ8HA ='p ҈f^G(OL ;IENDB`ic05ARGBQ<WAWAWADnpZWAE aS0mց+w+---------------------!=ԅg       S = = ?!  76LeNa;r  DC C" n 74`u[z P =ԅg       S = = ?!  76LeNa;r  DC C" n 74`u[z P =ԅg       S = = ?!  76LeNa;r  DC C" n 74`u[z P ic109PNG  IHDR?KiCCPICC Profilec``H,(a``+)rrR`> 8kuAfV'-@\\PTd!@HvH3dAՃsbNfRQbIj{Qbs~N~QqAbr* VhʢGoSv(x%10s 8\$Aad2f`]4yӑ@IDATxde.'10䜑dI((" `U 5gEU15UDQD,`/(ArsafSwNuu9{S$n qHF'oWnNn2k~\bŝ_41)Z'K<< @@':*"@@Y Lf~.jo{ɆV/Kp9}>m+Q]Ԃ}}|}'krwZ򫃒wheגU @PB6 pƞlSOڼo/jW51mzۿ \\“ZH|=Dq#@ @X{SNY'{8?ުp[_ߚ<;ɒ̈- @J lyYv8/¿5;'~jK O @I$go{|׶n}[;?Y_L9`?O @ZNxir=kz׋_,ډ7.kn @ @`\f?o<_/h?thwKm @ =yW^+Hd߷/U @z.0b3/"q/[:i=  @ @@E ~oתSWIޮO @@] uKN~qrԃϽ ZݓSw  @4'm\s5u @ @ظsW)1w6?Cz9hF*oqY,&۾kdv篙L> @X',\ܿ0k{-.99`E @ @@m-J~y|_*%9M6lt#@l%#@3zr˲lWJDH&B @+1?OhEv^%8n_VN?QXx>%xɅgvW`Vkj @ ,fq|`!)C#Ɇ r-  4_`C7kNeV=f8n @ (l+HV9>C)oەr| @#YJB@= ޏnoQؓ} @%0_;l%Q#yAG(!@_ ._`&30 @@'FKuz%d3[&F:5eǴ׵)]|w%5MT 0'$SMflduI6I_{rw۱;g}+wnhf>fxkߎ .I _'.{};R;9n PP``f  _K~do`c6IߴU] C)K'l{zijuON6iK[cSsڑOvt?|䤃?xi{w>=eGte#@ޟ΃_ߖy\{Zv(?o/_G`; KEq@+`ʗo=QY @@aS 0O&_ޓͪ3F>ve2{#~/khGvbA7%(MS Wk';dvnXiI@oE3jtv7 R  0xIoK{jgdsn%0e~.V쟼p=\9' @EXgo͏X xzsl?5y'ߟ\J7f üxk'@.l2ɱN^)_H =Oo{{1ijza!@8`vIFC`ⓒ}&&/k%Nmo4rI1Gv;&.y @`  @@WƶM;/yCrc]]uuA`V\. @` x_N^^-1[`}a\ @A(n @Kߘ<v?7 +E,~ @@' 0Dj'w;&[wmxk&Sr,(PC!3L>EXr'{cѿw @x (S׶  @ Lk/^ACt jWA2-R P^@PG#0#9h{o#y%r7/ַ&(FN޶φq5iǶsN؊7肀 S 2~N,"FD`)H1 0  @+_;ߒؚmC`o'-*`3 ߖ,FN`}Q+\, 0 !% @ ,Ю۾Cokkf Liox_ @` A7[K'Fn 0H  \`b;COV}b#,0idw$2!E#@@ @dsM [d׍% @Nld?vb)1+}5q):-x,ėh<` ,j}=觀E:%z.Ԓ,f6~Y #R<tV@Xo_cwx zF+Ξ{ OIܙ} @]XvtiE2K=0YoQO)*Nƺ @ LU˲=qXa @ (Ɲ @@?]vyY7M턀n o@tB`7'3R,VKXhq  @'` @+\>{?ɪ%KmE/$7G N D%|*ЂE&@0X*%Sg.0adM%'@@? 5$ӝӓMOjWn @`0 ?L8g c$ݔL5iύ&Q'wi5RM`Z^ Z@0 ? @2#PU.`{ @@=jGlZ/`N((0| J" @` 9 @菀?B @*( @ @(l/ @ @` 9 @菀?B @*( @ @(l/ @ @` 9 @菀?B @*( @ @(l/ @ @` 9 @菀?B @*( @ @(l/ @ @` 9 @菀?B @*( @ @(l/ @ @` 9 @菀?B @*( @ @(l/ @ @` 9 @菀?B @*( @ @(l/ @ @` 9 @菀?B @*0i{sL`%S>L:>Yud&=)yW!dlk|v{aMGύ VY#ɭǵO /4[ɥ&lrڿk'tY[kz>8K[0X&w~I-ɖ;$3_L/nW~= @%o{/I~uO&$|og%m=_"@@ `%+^;$0d>x+ @`kv$#ڋ-&g=9\[ 賀vG }}C5YYSukOz'3sKzFVӎo'0HЍ8+XڿO; G%*mμ<č vlm_JvDj-ُ= 0|Y1f;i&, &ە^|'_~OrKOc`hm Lnߒ236s=JFQ%cvisl( x @fbEFX`.?3®4E#@B]+ٻ0p]=D 86F&$ۼ"ya߁[$SXz^rv^m @ EvG;n;1GjS`eg]>kG  po7$ r|o5,s,!@l^l3/4n @ L<^jӒK.;ޟX4G,g pcǶk1ɶFn @ vKrr;gkINi@mw=`>~eFQIGq2.Hdv $;ۮ}?FA`(~ lx`ݒ= ĻI.<7,~' @+lɮ^ @ظ e?MVve `ff(0'K> ] @&GL]F@0JC@OraծC|  @`sm% p#@ 8 *skqë:!w @ L[7Yv9p{:.tC`ǒ}XU @s=.y}=S4xj{/I&_ I.59do; K @.Xn lv 7 @ZQ?:E7hU \[> e&yE͉]^ @^#9dJ+ pO{>%戮 GKtY'%=9]^ 0G ^ $GKy?òD p Muvߐ @ ;}Bݓq @&%G=fVm` ,%3ڥ @Xӓ5ډ| 0W@0=d#| 0d~6璶YCp%@`kS`ےG\U @ɄNm9P%_ؾwѯA ;$;dSX?PҖW%O`~*#F P\`}7I&gq S@Gy&_ q k%[o8^ (z%i;FBvɠD! @&7~~S`$[_H @Q}d-G),,`|fln @h OP!X bMFQ`Wb2 @@m%ݮvj;HOM`&;I&+LHP,4'e ('Xvd kHO&'ۯL=< 0>*y#O0J`B=˓aiKi=`CF Lv#O(!>ce NNIN`H @QVԮ;mSF`6BZ&KV$ @fmG|0 ( Ɍ9 @.0ɆzJX`6B.ӗ-VL P\`), > l)Kf> PE`K%JZ9 Wz@1NI=Xhq  @@a Igz P'@//Iɚ[g@l.U@>#PL`=^W, @N(8u vF{u{ %5JIw9Rg+,(/0d'c;@E&0W`#@F_`ld÷' pw]_8N\I  @&pz _lHZ Z<nIjq,l+[ M[Su&:qV @w,}; @#\bw LH 7 k)i~ @R-C^vn}XvyvG@%&K~0I%˵_V~k|;[&;-{͢w#@@״7kж ;$%EW|$uo_*紽|gdtK`[˱''<Օ)Ykd5^ld~ڇ 77J ܾMK|{'\w%1M /$()4dkZ%F@%GTU%C>[,Mu#-0?L3{_0q#@@_~O=;H];:hLyZv2#' D@6Z!y#Xد Ln%M>)qJj=  p:n @'mh䈗(]P N`dj;7J>qq{+%r(a5 0v ^xlvڤ6khO`SIU{NNKr7? pЎ o$>2Oh/h{ogv#@=ygo`a&0MZ :IްJ;ɎG+u ˰S@+ej'<$k+>:ˍ+/  p$_o/^vVj7*JqfTvɮo]PteA@6.8ɤwnyD=Zv  @ w 0W`ڴ Ο{ }?ގc2a_ @ ~C!OJ&l:˵HK]$8Q]"%@ w p:hW;" 07|v7+k2vYg&: vl'/6n O#ѝۡg 0tc&$9mrn ~Dž@wl}R|./9YwQH## 0sv0" @mNH|I2U; (^rCmy%@pJ{kc( @~ L[ ׷E/,lC>I'@_Nvi?>o(*8”e$@^ln{ݽ>  @`6Gy'@Pz`',*GNק"1Ώk}F,`|#"Ò_ @`]SB,H@  PB`//+UHC% PX@Px(/pDjɄ=K @@ eP"ţA;MhWp#@.fSB,H 8#/dZ/|T  @N7&K+ @e&@LKfԍ]!uJJyjO@)vLTda  P^`eMPEE/6.e ŏJ ,d͓KEss(!@V$SZ^Xydҡ PO@Po p؇P @@E5wj'l% (M\^ @@q['o* >%%.4 @W/9uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pwc@IDAT @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @P @ PW@Pw @ @@!@aJ @uug/9 @-% M@qIO PLKV)Z\4G1 @@7'p%לQ/P @ϏOnl|  PV-k/8E'P]'\dUu\0N^I  0`^ (%%7\*(/pYi [@@A@LW%v@" PEVI+'U9giO*ZTVN, >eeG/8\Y @@_H x ]<Q7'?=qSG7sk;HO@m@KO@c/8 @`.=19fv_b:  LFX[N>m2) vPv}`x  p7@|J@M[L}fi5KMQ8s>9 "@ (3 qpK_ @`P^3Y$ @@/P FF뇷CEg%=dd" Boo,_@0 PTঋcnQ  0"?)ɍ7H 1 &- L219qdF+4 p]^ڮqH"8`<:er,)FA`[j?b@ (zikM.9F$("5JnJb @`8˿/.-M`?J^Vr~;ˍ)G 0G$jL}]r sv:$paNArsth2B@k:'=%9ȷ$Lpbɂ'0ے׷K}YhHLEphy.enp$O|[L+n䭫&|d/> @`(0cH:!pJvV闭Ԯ/ݟVN" PBYiHPv[^R"艀#zh#TkMv|C۹>X)7kGa5yϓ_&a `g VOӒW>(9v.z*p)'&Dvn fsn|}vmld/']w~fܷmOLڹﴳWoN^dr+k_ᾷS__Nb)?qd BN (:=#@`$apB @`![x \UzNj @ @{ Ċ @ @@='A @ =@fbE @ @  @螀{3" @ s@Im @tO@нX @蹀6H @'L @\@sR$@ @Pto&VD @z.9  @ @ (7+"@ @=P  @ @@ݛ @ @ (zNj @ @{ Ċ @ @@='A @ =@fbE @ @  @螀{3" @ s@Im @tO@нX @蹀6H @'L @\@sR$@ @Pto&VD @z.9  @ @ (7+"@ @=P  @ @@ݛ @ @ (zNj @ @{ Ċ @ @@='A @ =@fbE @ @  @螀{3" @ s@Im @tO@нX @蹀6H @'L @\@sR$@ @Pto&VD @z.9  @ @ (7+"@ @=P  @ @@ݛ @ @ (zNj @ @{ Ċ @ @@='A @ =@fbE @ @  @螀{3" @ s@Im @tO@нX @蹀6H @'L @\@sR$@ @Pto&VD @z.9  @ @ (7+"@ @=P  @ @@ݛ @ @ (zNj @ @{ Ċ @ @@='A @ =@fbE @ @  @螀{3" @ s@Im @tO@нX @蹀6H @'L @\@sR$@ @Pto&VD @z.9  @ @ (7+"@ @=P  @ @@ݛ @ @ (zNj @ @{ Ċ @ @@='A @ =@fbE @ @  @螀{3" @ s@Im @tO@нX @蹀6H @'L @\@sR$@ @Pto&VD @z.9  @ @ (7+"@ @=P  @ @@ݛ @ @ (zNj @ @{ Ċ @ @@='A @ =@fbE @ @  @-Ɋ 0B&cg$JzD6&=v{rIμ9`s[?ze392-߻ڛZOI y /Kni?3EGS_Y͟d{!)w|[l6-Yojٻ 09㿍+q#!BB%r!/ra Be-nvfg鮮jWU۟/ERֲ9   j /P/"5.uXڰDxM<6iXf!Mv`/91^Ҡӥѝx_fKq! ZUj>(>N8}(m𞴥L˸5> 'W/J}$xN}q?Uo)j5s[}fRQf=VOM|g"WI[S{?ྷ<|lY!@ Zʆa K0iײ0!upfi{Jkm.~Lj/V.W-hs` w޻ /Jobn[kN04頻Jd w2ñR~?_hvuOm)D/xEP/w|.o}yyi44:2+Pj N|.{?Vs,~RuY۟Wj<r߶NH#o5#y95w0)2٥яKt pJveH@IJ",R@DK١/6]1WΪ/еRᭉ2/'|we We5o{HC;}}L'I_x4<'PwpcO籪0ɅtVD y#Q); v2/[*[$2bO%P+]  ?FvT!O7}~ntG LMiuj=,Bu^Rr[2# (_/ݚD;EE?2|a;8]چJm8-]|ibyvm6<ņ-9tsS|ԤyҊtDG@5E<49ɏ϶X䆾;SNL`Rͥlg~3?i(xeݤc<)m֟GU4#+@PHč^!JbFm?mpbNr;ΔYYuEko_WںneRZn;Nt ^`ӣP "ZD* P! /"@+x8^GuC~=|9f+{ =pu(ę[߻nW.~ y?ik: !RĊ.@w ~B*rX߯*ȏ5&P= u.?!F_tr;ȋO[ԯy~;8;D +@ $kki2p*PGM)pE=^u`.ovZkռ枂 ( BS6 U@ T ^-~|񗝆7YS#x(4DS?^ö? l|W@ H[,)W_[ګo>Gm}'-~6qƫ;V׺R{Z=~!պzhGFH` o$~H@R/t㉫;Mh\_twoU']_z\fGp@)@ JT/Pw7͖@'Q%wmG{J+.2B?r5?˾ +}'onr tIo!9 Md}Ne/YE,[n%CΊQR}~w w H 4*1!1vҸr<~Wg^Ҥ?#~($~7?e.0׏-(&ZrYοL] GufwXüvO?08J #yb?r?ʿ&0oKۃ@ PG@ wbVmo6z?PG{.l*U-0Pݫޏ=2(2?=gzB vA@L.!Ҝi#d uG P{ @ D5?ߙ!| /Ǥq_vZJ|hA>{>(Q{(~~ =Z hO@ X*o"@Zz5+n ㏧%xݜdscɥäb}5nOOkN]փs^q@$ jP5ĝ??C<D‹ƞz~EZ"N>=|^dcuCzԣ  Z@7|_x8, YbqGLE y. v-](CcF@eh ~tO_tOM2 Ŧa;tNέ3'|ӝ|q>?4YJ{m$]t[|*l'.=S<8-N| Hу!@z$? `f^xtB 1Szң!4>T[atB`S2,.0[5O eCB j7$0Fp^ m1뷉G=Bɏq?=nɯk5ʫKLRee`C~c{TFXht@#X4rH4oismN( Z@F//'9NcEӹ_[:m4|TDZt~^Kى &(KCJ=U#?'gS֕+isI~NC @( W_Hhy=e>1E}? i5rZN^|lYݿG<JwѓsKbEӬ+@}N3nOd>Xʉ66@@ Pwy\$dj(:(zTQ4(:K5s=Hq k])UQQQz}]Kf]zCOf+Q+H#`@͞oÞݰ7=:F57-BewPz?f»ۼ@ y|L^P#H@$i+fIH%t4iwWK3ߟ{ݞ2)@ɿ>n>gK6DPOi^8me{ogI? m|7j{kHH 5A|[v;_jw+ҚIMῳT/EҢ}iʎMa;iҤe0.Pc=M=ߺ͕6}qT}t?ߍ-VrCK&Ƿ[ZNP 5;o?V$mwo}~IsI4EE1aϵi!@%5@K0iWߛaI`'_LY{znn//C\/͡cN'&glx9!8S-nZ3UVqUg$Y_P}/rΣt[B؉ч9yhK>l }|q? [=ɣ{I'':f!uP7ztls'K?>ϖhVw+k )@8ۍZ# 6*m/ǟ&_zE/\VRr'׵_Ɇ׏SroR?(qqV_dx4|J x(iJcڃSILPKwG8!t׾|Y?~6ڟ{!0 _;0}:&K׹b?[  %H,A "@ z/]r*-/¾xN%P)Ho&8 'mT|ҝ_S:p֚/SM<=I|PN?]I\O}aF@6Z@H5Hdz}n\V/hQؾ+{U{镁7B8(< # 9UC㻱)$Ԉ:C?p}~<ߣ m%dčK/Y4 ,T IU4C`\ًyd۵qʬ;Ew`jज"|1 cHgm~Ov?`i]d#bSGι(ayx֓<5N P؅W@ujyw^7wKhE+V3s8|y9u=|/SgzO=լR>I{iU0Tj[l  ݂@u//Lp]티[3^rfKrss!7ỚŸ5 b":wPųGlukA^ Oz]x?խ8c  @LOx3'?QT{(3; J6(z(~(<^((z/Q4uEQf(7jGWEѢ1)[Fk3h]La4ŦN!@h< '^P`G?JyR]/r=abl íoV8㱆ydCF҉'J52'lB[]c!7O@eXn=^reKrc.{˽:&+[ GtUW?/t i_ m3w',"?"  ~hxm}/NBV:|=O&?zҪ&qpc_4ʨ@'OBnIq~w1 8Z"@^H䅕B@(3ֽ~;Emcd==wv(_;-uh+ 6an@xh|]^Ѹ =ݡm\R P|$ͨ1$B^A4NtC${}L`X֞o~3RxRm*wM/q3U.KUXd,@ c*vD4 4`G^q4Ey,[Nu<zi~Ff^;m0Co 1\CQ _B`]:I -9' PX P _/jS <`?߁bW$o{IRNaӤ]Z?#B;X곲6 Y@ D!:1#@!(EH[{pv=v 5^־#pi-u @G'.9TjRM9Yk/%ɪA-@ ”h;>gF'Z~]7*X:q#FgvnVS!X.@~ qWqQ=T=@PHĉ? YM_t ?im~S`ҁޞ qmwֶ.k9b zG^ E@(-M U=X\]?R1q t.kSQznQq$}X<MO9 @zHm hI>z1? x$РW蓕]*o"Oh5)tʍ÷ q{qrx͋ @_A`@B <}^E%/bX{hz f_`_|/;-%MD8}#񥯒vH#SQ*eH/HSk T(x?i)·y^RS/@zԌ!KEm껰BYSoo[;N6>) ^1 ,AjlU l%wT{d.7!`' Y _nK\#:yM 6@ $ܺĆ? t |aV@=|IG-Vzx;[H]ߨz?\=^{by~Dʏ,@  Z\=5p ïu wf:dk;"/2ܣ-sv' Pl$Ũ/d%絳e&Pk$L2b=J>bGMO?MVmS<N  `@S׾ƣײj}&:+h/&M^PҲc*|hSY|g8#@Hy VQZ hw>#ط2.GJ h;V$!ٳr^c%Sjy  iiI@ |/s+|+hKZS'ت//Z\PrB*۹\"_-<_i,L,8 zV$?~Cٲh 3;+Xjǫ MXS@|/@Tejձ[K΄_'@ M$ԚĂhK#Ce<cc]A*+=--Ó($m:#Vi~N  0ڙ(R'bN9BXc]Gg{{ @7r:~hG@M#68yc.Gr  |o#j9 L0,ǃ?lݤ= -w}zq! T?Մ٠ @ $DTr8谏k,[]6r?>#kMvcv:kJxeN\c[t(! & @wnW/$s6/'Qز(yCgG,P5RU`ͷ=95PIdA |o#j9_9\z?ұ^}Y_IJ&PqEE: =Q>E 8C@ wK#6G^FGNKCW-yK?XHZ9 |X.0@ $ٮD@ ecwW·}qMP_X\ =jI[!t(! & eҷwN/$زr/`yBp}{3(ߜgg|;I`PiR??3N @ (& 3ݛ~֋ XQlbr^n,#P3N ;4p'rH t/!Dv9ݲp`·s_[ ɯr$*0@"rdgV3 P4$(d+0eSNaT34{W`z4oK ?tIeb).Bΰ[_iL  ڛhJ`A$M8&c vn[L,E_I ^͞-;y@ڟ. )z G Tc_OZRwM@hHmQqD{_a&- *|\}_DL9ygI=ubђJxN|@ fin]bC}e.Pꋯo|\`V,Ζ/{ڙϞ Lsٽ=[3>U@Z[@'1^i%`d#0>_4 L`z]i~ #8aǤ~9!0SOXN@pMN%0_g+Dkث۱ αg/)KPt0sP;vF38f+;"0S̃H -!/C}Gq+:g&IپNU,,ʞ}};OJ F\eJÌJ ^u1aJ  0۝J`@4svs[ϥosBBS ^Y`hOA8{T  NO-?܎娊XNJ? zNi/\QiP|#P^hWvBV@6GL"M"#w:x 2ڕ2t~b;ۏ~n}G9HGId+;!E+@h#@|uGkg_\l5bSW§30u49~+cқatʖ".@[#@N҃J}WA̶&`XAd'H/k+U;a^Bm6ң^ %oEO[$ݳY+! P dxR%0Of4+l׏eMRVSъ.찍[R:'_mknB$3|d֏Z!q S@ ̔n}CoJt5krW'_LSua;J{.BYC&\"}^1 ,]ܩc.ȋtF҇ @ V&FI Z!^aҽ^x0ɫ=QY>'~1P{b+S{@ԵjN}@HQ P3^Pol{4E\4sAS\ ^;^rGoW~k,]HtG=L~N$B@"J @M |[-j3= 3?Sw%`rO`CB b3$]a{ďg i{~> )ƺ) /J,=OyOB <ꧡٯJ %NL}(2skQ\DCr-8u>^ @HKr@x|q2)4muP=z<#_ x%ϔNX#Q<<^uTwj'/袛10毈φrr %?"$C̪iN 2 P(9%iS*m:o{yb?kJ_ snCS:҃]] i]"ǃݜz ICTƟ]ׁ?ʿW7#.@# աX] j&}dcHxCV*V{n#.=z]ATa{}|vR=>-^<N<+[5dW񘫜?O Xip@]awxEV]}}e.kNRWI+2L6G+x~;r6ʥN3A׺9:1ۼ^;co Il%I  XP-8z]D/v?Q Dt'_#yGMJxbz#Nsl ȿp/;DzWY/M=X}nEws㒥2q?a¿G5rz%~H A L6z4@o?)bیcX?/Rz-*`e8u6h[K񢎥K=<_>7%8eRw"/z! K{mI?^pn@@ k:6aQ4m:0NE/~K~oev9R(tD|?FЭg/u赉Qt(jyqu5IB@ v "=. D*XJ*v}wd;Y|}:W̟ޔ֫?$~4*?Bs)n')s}4I?`ޟԹ^j;Tt_#鼍/T+7o,Iڂ<ۋꙃ7TA;Jݟ?Bw00K^Sjw|FI-##;tM{G[eL Ͽu?ܧn+ۨwv,?#@R6R@^/;K6?pئҺ3~mKZRgmg.P֋M,}/jHs.w^'Hĉ;Η?~_/[o4_7qV 1ؠy5WdM|dž@E%{B9^'܇֛9Y߫7\ٞ2Ey?^xKb'2zS 6N(u3rlq Kr(6bHFYEA%s}G_~)]B   @$hdBD@@@}@@@HȄ       @@@@@@ @#"   $    @ F&D@@@H@@@@@L        @@@ @@@@@ 42!"   @>   @$hdBD@@@}@@@HȄ    v @@K@XM,.]  !D8!DJ G_$h48" {@HD/ O6@B7^Zt`hQ/H$+]x= @0.&>Q#@'zBX B v@&5rb'b`}`f 6|G@ LS)uG5a >h2{ @p%t?B'`` a "v z@nJr{:##-0i:A< a aaL _% F=.{' @ =)c @)Sۀ@I`gҏSBX@w?!s= 'd@Mib@'l` @]Q@s[m@ $G!@$BlubFw/˟|/ W`7Rtkz#2X AXO`@mM a L/# ۟p? pfw(@@ O=JCHTn;"0} 0@X`D@$ 0w,@@ eIC(T-@j#@ GK_@@p&,=UnD$ `H  U?w}i@ 39)Uܐ @@'zN6"@Xqk18 @Hi;E/-2AHdX '  4}6,&@`1 ~D l Q^ @ }4vEE< 8 8V:s ' $C#n^YdԇZ @H$u)ȸ;.B P3$jƙ  = `EߏZm^@@ uїsHyu9W.DBD HtP9R/U{\6QI+>+5 @ $n{uC VHLN[PBh"ݏ~]cn^JS@&04^l}ژx(.FW{Q[h y?*56@I7!'Jv/+S@:ەH@qzo9 v~r@R;)@%J?&n~Cf0]@ G ͤzJ-oV"h:MZދ6@ "p4 i/G;Q[^vbiNU T#@ޗZO6 Vӣ6ױ9ig aC@ ]])kUAKJe}8e  O]F< :EjmG uVZwcNs+QGC`4FC`4FL|e`8>pY?|8k:z`\10 a a}IENDB`ic11PNG  IHDR n<iCCPICC Profilec``H,(a``+)rrR`> 8kuAfV'-@\\PTd!@HvH3dAՃsbNfRQbIj{Qbs~N~QqAbr* VhʢGoSv(x%10s 8\$Aad2f`]4yӑIDATX mlg}qzTi6FTKua0Q C&"dk,Ki5 Llx}h$JVUi9uryz9\\`<#߃5 ^B|H,5Vܙ2OXO}<=vc xClz@>Kh9ZڐNg@ # :/+L`wPL\l^ WP}N|A^9l"=adPPSfv!o*`h;! (kHmU?vw:(KpzںԨLOZlGʔ?rh^Ӂ-RN;RWuOڸݰwHʖ5S?dYsUfN+ /ygE#զq5ŲXX[6@\FFb0W\Dm!r/"Icvl&M%2F|5B`@pY=n/G̯i܂\xW=F*KAD`4bxOG[ F[lƿ'֭s}cZfwIENDB`info bplist00X$versionX$objectsY$archiverT$topU$null WNS.keysZNS.objectsV$class TnameTiconZ$classnameX$classes\NSDictionaryXNSObject_NSKeyedArchiverTroot#-27=CJR]dfhjlnsx}qdmr-0.12.3/dist/qdmr.desktop.in000066400000000000000000000004131501654372000164470ustar00rootroot00000000000000[Desktop Entry] Name=QDMR GenericName=Codeplug programming software for DMR radios. Comment=A universal codeplug programming software for DMR radios. Exec=qdmr Icon=qdmr Type=Application Categories=Network;HamRadio; Terminal=false X-Desktop-File-Install-Version=0.24 qdmr-0.12.3/dist/qdmr.png000066400000000000000000000030331501654372000151560ustar00rootroot00000000000000PNG  IHDRyyFgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs  w5tIME AaIDATxOlTE?v%iBHRSE4K"!<4/&^&DDh*(T# ?MӀ4BZ?vj뼷۵o+3w}3{;3;oE0xQcM5Df;Ye:h&Y8a:ih.TsO95s y! l r6! \tmʗ,7ͦP=۲pMiwf0>ы9(ן3,ӳMruAE!vVOd!rrֲUx$g)<Tn1DQnԚ؝#.(" IR.X.ia[ RjPC6pȕj`+=jGamafg(Yѯ4-&i2>$A&hEkD^KU+c "GI`# E9dmCA9dmCA9dmCA'"Ls<}܍xD'y;Hg&ߦ밺FZKԼ6*Ij+o& Ir r6! l <\GY~e+I;qme\WMGNerfm7NȡY9(@W)iRkݩy95j]ŵ3ȗ438V\Rs-1u1(}Jd|\Շ\VP4,,}ڐDs]- Y%r! ȉ43(BB!Ӭ+!y:. 싓qdR~򸎩CY\zl&m-R@?|iviҘcTFRyY:[NIltcZ8r6! l,DּTp|gV\!'vPE?'t+y̾̋T9&u8W#0o)7]Gx"+iN2JU\#Rs=ˆ$|( AY+Q}ـD2|۷7k>eFpBy&H/w⇌ﵙ-><9Ãa>R"K3A^n AJcOtTJ]gJ:H{HOl` q&$xૼzXA?'M=#[^# l!c(PC9_ !x1CT&O78bg@3*/tSy*qE[{cFn |:&6O %tEXtdate:create2019-12-16T14:19:21+01:00-5Y%tEXtdate:modify2019-12-16T14:19:21+01:00\h.tEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/dist/qdmr32.png000066400000000000000000000012001501654372000153150ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs  w5tIME ,NIDATHݕ1KP?P1A C!Zp.flעR"Ru0Đ$ϊ"y{r_SjP챿Z湒bq1N_@``]N8L1NmBAxE<N0LR A s >~jItEEmM," +GFRJV(^cȐuz],5TtAmTmHUVGՅēX"%8X/#RD ~7Ab,ӫT@3u9t&ԛ%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/dist/qdmr48.png000066400000000000000000000016231501654372000153350ustar00rootroot00000000000000PNG  IHDR00 1 gAMA a cHRMz&u0`:pQ<bKGD#2 pHYs  w5tIME ,aIDATXOkQ$PiBi 4 ܉Ҋ A+]ln[DѪE I6ZBj34$3ә$HgV7;w}y=pG!re[| <~C_ 'T@.e"qqXXXTxd-:- pf) 1s 8,4~-X=}zن^O۪v b !8%\ở/CBZzB abȉVrefw p c s1=" Tk7J5l$wLADSMZYEZVA;oRZWҎ&m?@.JA66el ϯkt;#%<;ZxY Hk̒Q@'fTTԌ(%IzPQLd ~:3ig[@exY2d("HQ'!@VJhJF#6ubuȣ(5E5>jP᧯/Aş@#_+oQ6Qimq2_̖%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/dist/qdmr64.png000066400000000000000000000017161501654372000153360ustar00rootroot00000000000000PNG  IHDR@@`UgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs  w5tIME ,IDAThOkQ3hҐZ Ip.MQhIŕ_؍K@Ev)ݩP(j]H'%.2 3!s'3I&ss##1K\R?2lZ[FTG#m!;h}B, 0A㜶~S>XsOy1FfOԟ{&T 0xхпbX@,@mA3c}R!@*# 5l6!,PVV@\PuiJS%>vysW\0K3mn׊+ڸH ICf[ŽspNη~o\(0q 箝J/@WTC?8r=6'lH [ '41;dH ]]m p9]ϗ~ B xO%@'`b`r.~2gB=gT%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/dist/qdmr72.png000066400000000000000000000024501501654372000153310ustar00rootroot00000000000000PNG  IHDRHH{gAMA a cHRMz&u0`:pQ<bKGD#2 pHYs  w5tIME ,IDAThoE?ۣQRT "M Q^F_bxa$!$*&ԇHDcգ>л>л⶛(;w{|v73{fZg",NF!.luW T?3BZ- ,;gyf(,$Px5Dt%#>pYo=W VT %%RѶP@ɲkH-\IQԏxčXl/cQ0EO&S%[ 8L/R<&g4lr!B_L{ZbE9V5L~b3iyGK$(6Xn7Y1[y, qzQ :SA ZvF o#8w7%ீ[etRyIMpO$e}UN(VQu;J:7F`4I;xڮf7ʹaI])WGYh.7.8,I洫 \qJe9rW L&p;K_.}˳tDTWfY(Rƭ󺾂}bHYL~2 (C!z*j} FUU|Mw_2(V*G#~^ 60T%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/dist/qdmr96.png000066400000000000000000000027541501654372000153460ustar00rootroot00000000000000PNG  IHDR``HgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs  w5tIME ,IDATxkU?{me74MjEUlI@D黯7,b *|JUH/ֆ4B5n'73ifΞ]:y39g~ߙss~3 j.]ָGc-uϏj>Es}T{S devices = AnytoneInterface::detect(); if (1 != devices.count()) { // Either none or more than one device found... return -1; } // A place to put error messages ErrorStack err; Radio *radio = AnytoneRadio::detect(devices.first(), RadioInfo(), err); if (nullptr == radio) { // There went something wrong, check err. return -1; } // Read codeplug from device blocking. if (! radio->startDownload(true, err)) { // Some download error, check err. delete radio; return -1; } // Decode codeplug into genericCodeplug Config genericCodeplug; if (! radio->codeplug().decode(&genericCodeplug, err)) { // Some decoding error, check err. delete radio; return -1; } // Do whatever you like with the codeplug. return 0; } qdmr-0.12.3/doc/code/dm1701_buttonsettings.txt000066400000000000000000000045271501654372000210600ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Unused, set to 0 | Side button 1 short press | Side button 1 long press | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Side button 2 short press | Side button 2 long press | Side button 3 short press | Siden button 3 long press | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | P1 short press | P1 long press | P2 short press | P2 long press | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | Unused set to 0x00000000 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Unknown set to 0x01 | Long press dur x250ms | Unused set to 0xffff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | One-touch setting 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | One-touch setting 5 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dm1701_channel.txt000066400000000000000000000106171501654372000173710ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 |LWK| 1 |SQT|ASC| BandW | ChMod | ColorCode |TimeSlt|RXO|ALT|DCC|PCC| PRIV | PrivIdx |DPD| 1 | 1 0 |EAA| 0 |RXFreqR| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 |AdimtCr|PWR|VOX| 0 |RVB|TXFreqR| Unknown set to 0xc3 | TX Contact name index + 1, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | 0 0 | Tx Timeout | Tx Timeout Rekey Delay | Emergency System index + 1 | Scan List index + 1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | RX Group List index + 1 | GPS System index +1 | DTMF decode bitmap | Unused, set to 0xff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | RX Frequency, 8 digits BCD, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | TX Frequency, 8 digits BCD, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | RX CTCSS/DCS | TX CTCSS/DCS | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | RX Signaling System index +1 | TX Signaling System index +1 | Unused set to 0xff | 1 1 1 1 1 1 |RXG|TXG| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Name 16 x 16bit unicode characters ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Field description LWK = Lone Worker, default=0; SQT = Squelch type, 0=tight, 1=normal, default=1; ASC = Auto scan, default=0; BandW = Bandwidth, 0=12.5kHz, 2=25kHz, default = 0; ChMod = Channel Mode, 1=Analog or 2=Digital, default=1; ColorCode = ColorCode [0..15], default=1; TimeSlt = Repeater/time-slot, 2=TS2, 1=TS1, default=1; RXO = RX Only, 0=off, 1=on, default=0; ALT = Allow Talkaround (inverted), default=1 (off); DCC = Data Call Confirmed, default=0; PCC = Private Call Confirmed, default=0; PRIV = Privacy type, 0=None, 1=Basic or 2=Enhanced, default=0 (off); PrivIdx = Privacy Index, [0,15], default=0. DPD = Display PTT ID (inverted), default = 1. EAA = Emergency Alarm Ack, default = 0; RXFreqR = RX reference frequency, 0=Low, 1=Medium, 2=High, default=0 (low); AdimtCr = Admit Criterion, 0=Always, 1=Channel Free or 2=Correct CTS/DCS, 3=Colorcode, default=0. PWR = Specifies the power, 0=low, 1=high. VOX = VOX Enable, 0=Disable, 1=Enable, default = 0; RVB = Reverse burst 0=disable, 1=enable, default=1; TXFreqR = TX reference frequency: 0=Low, 1=Medium, 2=High, default=0; TOffFre = Non-QT/DQT Turn-off Freq., 3=none, 0=259.2Hz, 1=55.2Hz, default=3; InCallC = In Call Criteria, 0=Always, 1=Follow Admit Criteria, 2=TX Interrupt, default=0; Tx Timeout = Tx Timeout x 15sec, 0-Infinite, 1=15s, etc, 37=555s, default=0; Power = Power, 0=low, 1=middle, 2=high, default=high; ALI = Allow interrupt (inverted), 0=allow, 1=Disabled, default=1; RXG = Receive GSP info (inverted), default=1; TXG = Send GSP info (inverted), default=1; qdmr-0.12.3/doc/code/dm1701_settings.txt000066400000000000000000000150521501654372000176170ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Intro Line 1, 10 x 16bit unicode characters ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | Intro Line 2, 10 x 16bit unicode characters ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 26 | Reserved 24 bytes, set to 0xff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 40 | 1 1 1 |MNT| 1 |DAL| 1 0 |TPA|TPD|PWE|CIT| 1 |DAT|SMR|SPR| 1 1 1 |INP| 1 0 1 | 0 |MSB| 1 1 1 1 |MSA| 1 1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 44 | DMR ID 24bit, little endian | Unused 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 48 | TX Preamble Duration N x 60ms | Grp Call Hang Time N x 100ms | Prv. Call Hang Time N x 100ms | VOX sensitivity [1..10] | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 4c | Unused, set 0x00 | Unused, set 0x00 | RX Low Bat. Intv N x 5s | Call Alert Tone Dur N x 5s | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 50 | Lone Worker Resp. Time in min | Lone Worker Rem. Time in sec | Unused, set to 0x00 | Scan Dig. Hang Time N x 100ms | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 54 | Scan Anal. Hang Time N x 100ms| 0 0 0 0 0 0 |BcLTime| Keypad Lock Time N x 5s | Channel Mode 0xff=on 0x00=off | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 58 | Power-on password 8 x BCD numbers, 0x00000000=default | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 5c | Radio prog. password 8 x BCD numbers, 0x00000000=disabled | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 60 | PC Programming password 8 x ASCII, 0x00 terminated, filled with 0xff=disabled ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 64 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 68 | Unused set to 0xffffff | Timezone,0=UTC-12 | 1 |PCM|GCM| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 6c | Unused set to 0xffffffff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 70 | Radio name 16 x 16bit unicode chars ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 8c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 90 | Channel hang time N x 100ms | Unused, filled with 0xff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ac ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Field description: MNT = Monitor type: 1 = Open Squelch, 0 = Silent, default=1; DAL = Disable all LEDs (inverted); TPD = Talk permit tone digital; TPA = Talk permit tone analog; PWE = Password and lock enable (inverted); CIT = Ch. free indication tone (inverted); DAT = Disable all tones (inverted); SMR = Save Mode Receive; SPR = Save Preamble; INP = Intro Picture; MSB = Mode select B (0=VFO, 1=MR); MSA = Mode select A (0=VFO, 1=MR); BcLTime = Backlight time, 0=Always, t=n*5s; Keypad Lock Time = 0xff=manual otherwise PCM = Private call match; GCM = Group call match; qdmr-0.12.3/doc/code/dm1701_zoneext.txt000066400000000000000000000024561501654372000174570ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Member Channel index+1 16 VFO A | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 5c ... | Member Channel index+1 63 VFO A | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 60 | Member Channel index+1 00 VFO B | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ dc ... | Member Channel index+1 63 VFO B | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dmr6x2uv_aprssetting.txt000066400000000000000000000254271501654372000211150ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Unknown, set to 0x000 | TX frequency as 8 digit BDC, big-endian in 10Hz (unused) ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 ... | FM APRS TX delay in 20ms | Signaling type | CTCSS tone | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | DCS code, little-endian | Manual TX interval in seconds | Auto TX interval in 30s | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | FM APRS Monitor enable | Fixed location flag | Latitude degrees | Latitude minutes | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Latitude seconds | South flag | Longitude degrees | Longitude minutes | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | Longitude seconds | West flag | Destination call, 6 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | Destination SSID | Source call, 6 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 ... | Source SSID | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 | Path, 20 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 34 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 38 | Unused set to 0x00 | ASCII APRS Symbol Table | ASCII APRS Map Icon | Transmit power | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c | Prewave delay in 10ms | Unknown set to 0x01 | Unknown set to 0x03 | Unknown set to 0xff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 40 | DMR APRS Sys 0 channel index, uint16, litte-endian | DMR APRS Sys 1 channel index, uint16, litte-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 44 | DMR APRS Sys 2 channel index, uint16, litte-endian | DMR APRS Sys 3 channel index, uint16, litte-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 48 | DMR APRS Sys 4 channel index, uint16, litte-endian | DMR APRS Sys 5 channel index, uint16, litte-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 4c | DMR APRS Sys 6 channel index, uint16, litte-endian | DMR APRS Sys 7 channel index, uint16, litte-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 50 | DMR APRS Sys 0 destination ID, 8-digit BCD, big-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 54 | DMR APRS Sys 1 destination ID, 8-digit BCD, big-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 58 | DMR APRS Sys 2 destination ID, 8-digit BCD, big-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 5c | DMR APRS Sys 3 destination ID, 8-digit BCD, big-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 60 | DMR APRS Sys 4 destination ID, 8-digit BCD, big-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 64 | DMR APRS Sys 5 destination ID, 8-digit BCD, big-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 68 | DMR APRS Sys 6 destination ID, 8-digit BCD, big-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 6c | DMR APRS Sys 7 destination ID, 8-digit BCD, big-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 70 | DMR APRS Sys 0 call type | DMR APRS Sys 1 call type | DMR APRS Sys 2 call type | DMR APRS Sys 3 call type | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 74 | DMR APRS Sys 4 call type | DMR APRS Sys 5 call type | DMR APRS Sys 6 call type | DMR APRS Sys 7 call type | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 78 | Enable roaming support | DMR APRS Sys 0 time slot | DMR APRS Sys 1 time slot | DMR APRS Sys 2 time slot | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 7c | DMR APRS Sys 3 time slot | DMR APRS Sys 4 time slot | DMR APRS Sys 5 time slot | DMR APRS Sys 6 time slot | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 80 | DMR APRS Sys 7 time slot | DMR APRS pre-wave delay | Unknown ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 9c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Field description: - FM APRS TX Frequency 8 digit BCD big endian in 10Hz. - FM APRS TX delay: 0=0ms, 1=20ms, ..., 255=5100ms - Signaling type: 0=off, 1=CTCSS, 2=DCS, default=off - CTCSS Tone: 0= 61.2Hz, 1= 67.0Hz, 2= 69.3Hz, 3= 71.9Hz, 4= 74.4Hz, 5= 77.0Hz, 6= 79.7Hz, 7=82.5Hz, 8= 85.4Hz, 9= 88.5Hz, 10= 91.5Hz, 11= 94.8Hz, 12= 97.4Hz, 13=100.0Hz, 14=103.5Hz, 15=107.2Hz, 16=110.9Hz, 17=114.8Hz, 18=118.8Hz, 19=123.0Hz, 20=127.3Hz, 21=131.8Hz, 22=136.5Hz, 23=141.3Hz, 24=146.2Hz, 25=151.4Hz, 26=156.7Hz, 27=159.8Hz, 28=162.2Hz, 29=165.5Hz, 30=167.9Hz, 31=171.3Hz, 32=173.8Hz. 33=179.9Hz, 34=183.5Hz, 35=186.2Hz, 36=189.9Hz, 37=192.8Hz, 38=196.6Hz, 39=199.5Hz, 40=203.5Hz, 41=206.5Hz, 42=210.7Hz, 43=218.1Hz, 44=225.7Hz, 45=229.1Hz, 46=233.6Hz, 47=241.8Hz, 48=250.3Hz, 49=254.1Hz - Path: 20 x ASCII, 0-padded path string. Format is comma-separated CALL1-SSID,CALL2-SSID,... - DMR APRS Sys N channel index: uint16 channel index, 0-based, little-endian, [0,4000], 0x0fa0=VFO A, 0x0fa1=VFO B, 0x0fa2=Selected - DMR APRS Sys N time slot: 0 = Channel, 1 = Timeslot 1, 2 = Timeslot 2 - Transmit power: 0=low, 1=mid, 2=high, 3=turbo. - FM prewave delay: in multiples of 10ms [0,2550ms], default=0ms. - Manual TX interval: n+1 seconds. [0,255], default=0s - Auto TX interval: 0=Off, 1=30s, ..., 255=7650s, default=off - FM APRS Monitor enable: If enabled, the radio will monitor send FM APRS transmissions. default=off - Rep. activation delay: 0=Off, 1=100ms, ..., 10=1000ms. - APRS display time: 0=3s, 1=4s, ..., 12=15s, 13=infinite, default=3s - RPO: Report position flag. - RME: Report MIC-E flag. - ROB: Report object flag. - RIT: Report item flag. - RMS: Report message flag. - RWX: Report weather flag. - RNF: Report NEMA flag. - RST: Report status flag. - ROF: Report other flag. - FM APRS width: 0=narrow, 1=wide. - Pass all: 0=Off, 1=On, no idea. qdmr-0.12.3/doc/code/dmr6x2uv_channel.txt000066400000000000000000000116701501654372000201550ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | RX Frequency 32bit BCD encoded in big-endian as MMMkkkhh | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Tx Frequency Offset 32bit BCD encoded in big-endian as MMMkkkhh | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | RMode | 0 |BWd| PWR | CMode |TAr|CaC|RXO|CTR|TDC|TCT|RDC|RCT| CTCSS transmit | CTCSS receive | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | DCS transmit code, little endian | DCS receive code, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Custom CTCSS frequency in 0.1Hz, little endian | 2-tone decode index, 0-based, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | Contact index 0-based, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | Radio ID table index. | 0 |SquelchMode| 0 0 0 0 | 0 0 |OptSig | 0 0 | TxPer | 0 0 0 0 0 |XFR| 0 |RNG| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | RX Group list index | 2-tone ID | 5-tone ID | DTMF ID | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Color code |LWK|EEE|???|EAT| 0 |EST|SMC|TSL| AES Encryption key | Name 16 x ASCII 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 ... | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 34 | 0 0 0 0 0 |DAD|ETM| 0 | Unused, set to 0x00 | Scanlist idx 0, 0xff=unset | Scanlist idx 1, 0xff=unset | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 38 | Scanlist idx 2, 0xff=unset | Scanlist idx 3, 0xff=unset | Scanlist idx 4, 0xff=unset | Scanlist idx 5, 0xff=unset | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c | Scanlist idx 6, 0xff=unset | Scanlist idx 7, 0xff=unset | ARPS Report Channel Index | 0 0 |RGP|DAP|APRSPTT|APRSTyp| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Field description: - RMode: Repater mode where 0=simplex, 1=positive TX offset, 2=negative TX offset. - BWd: Band width where 0=narrow (12.5kHz), 1=wide (25kHz). - PWR: Power where 0=low, 1=mid, 2=high, 3=turbo. - CMode: Channel mode, 0=analog, 1=digital, 2=analog + digi RX, 3=digital + analog RX. - TAr: Enable talkaround. - CaC: Enable call confirm. - RXO: Enable RX only. - CTR: Enable CTCSS phase reversal. - TDC: Enable TX DCS code. - TCT: Enable TX CTCSS tone. - RDC: Enable RX DCS code. - RCT: Enable RX CTCSS tone. - SquelchMode: Squelch mode 0=Carrier, 1=CTCSS/DCS, 2=Optional Signaling, 3=CTCSS/DCS and Optional Signaling, 4 = CTCSS/DCS or Optional Signaling - OptSig: Optional signalling where 0=off, 1=DTMF, 2=2-tone, 3=5-tone - TxPer: TX permit/admit criterion, 0=always, 1=colorcode, 2=channel free. - XFR: Exclude channel from roaming - LWK: Enable lone worker. - EEE: Enable enhanced encryption - EAT: Enable adaptive TDMA - EST: Enable simplex TDMA - SMC: SMS confirmation, - TSL: Time slot where 0=TS1, 1=TS2 - DAD: Data ACK disable (inverted!) - ETM: Enable through mode - RNG: Ranging - RGP: Enable DMR-APRS RX - DAP: Enable DMR-APRS PTT - APRSPTT: FM APRS PTT mode 0=Off, 1=start of transmission, 2 = end of transmission - ATy: APRS Type: 0=Off, 1=Analog APRS, 2=DMR-APRS qdmr-0.12.3/doc/code/dmr6x2uv_generalsettings.txt000066400000000000000000000434351501654372000217470ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Idle channel tone | Display mode | Enable automatic key lock | Automatic shutdown time | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Transmit timeout in 30s | Language | Boot display | Enable boot password | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | VFO frequency step | Squelch level VFO A | Squelch level VFO B | Power save mode | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | VOX sensitivity | VOX delay in 100+500*n ms | VFO scan type | MIC gain | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | PF1 short press function | PF2 short press function | PF3 short press function | P1 short press function | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | P2 short press function | Work mode B | Work mode A | STE Type | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | STE freq. no signal | Group call hang time in sec | Private call hang time in sec | Prewave time in 20ms | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | Wake head period in 20ms | WFM channel index | WFM VFO Mode enable | Work mode MEM zone A idx | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Work mode MEM zone B | Unused set to 0x00 | Enable recording | DTMF duration | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 | Man down enable | Unused set to 0x00 | Display brightness | Backlight duration | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | Enable GPS | Enable SMS alert | Unused set to 0x00 | WFM monitor enable | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c | Work mode main channel | Enable sub channel | TBST frquency | Enable call alert | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 | GPS Time zone | Talk permit tone | Digital call reset tone | VOX source | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 34 | Enable pro mode | Unused set to 0x00 | Enable key tone | Menu exit time | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 38 | Filter own ID enable | Startup tone | Enable call end prompt | Max speaker volume | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c | Remote stun/kill enable | Unused set to 0x00 | Remote monitor enable | GPS RX positions | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 40 | Enable select TX contact | PF1 long press function | PF2 long press function | PF3 long press function | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 44 | P1 long press function | P2 long press function | Long press duration | Enable Volume change prompt | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 48 | Auto repeater A direction | Digital monitor slot | Digital monitor color code | Digital monitor match ID | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 4c | Digital monitor hold slot | Display last caller | Unknown | Man down delay in sec | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 50 | Analog call hold in seconds | Enable display clock | Max headphone volume | Enable GPS range message | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 54 | Unknown settings | Enable enhanced audio | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 58 | VFO Scan UHF minimum frequency in 10Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 5c | VFO Scan UHF maximum frequency in 10Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 60 | VFO Scan VHF minimum frequency in 10Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 64 | VFO Scan VHF maximum frequency in 10Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 68 | Autorep. off. idx UHF, ffh=off| Autorep. off. idx VHF, ffh=off| Unknown ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 6c ... | Enable maintain call channel | Priority zone A idx, 0xff=off | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 70 | Priority zone B idx, 0xff=off | Enable SMS confirmation | Call tone frequency 1 in Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 74 | Call tone frequency 2 in Hz, little endian | Call tone frequency 3 in Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 78 | Call tone frequency 4 in Hz, little endian | Call tone frequency 5 in Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 7c | Call tone duration 1 in ms, little endian | Call tone duration 2 in ms, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 80 | Call tone duration 3 in ms, little endian | Call tone duration 4 in ms, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 84 | Call tone duration 5 in ms, little endian | Idle tone frequency 1 in Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 88 | Idle tone frequency 2 in Hz, little endian | Idle tone frequency 3 in Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 8c | Idle tone frequency 4 in Hz, little endian | Idle tone frequency 5 in Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 90 | Idle tone duration 1 in ms, little endian | Idle tone duration 2 in ms, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 94 | Idle tone duration 3 in ms, little endian | Idle tone duration 4 in ms, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 98 | Idle tone duration 5 in ms, little endian | Reset tone frequency 1 in Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 9c | Reset tone frequency 2 in Hz, little endian | Reset tone frequency 3 in Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ a0 | Reset tone frequency 4 in Hz, little endian | Reset tone frequency 5 in Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ a4 | Reset tone duration 1 in ms, little endian | Reset tone duration 2 in ms, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ a8 | Reset tone duration 3 in ms, little endian | Reset tone duration 4 in ms, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ac | Reset tone duration 5 in ms, little endian | Record delay in 200ms | Call display mode | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ b0 | Call-sign display color | Enable simplex repeater | GPS ranging interv. in sec. | Enable speaker in simpl. rep. | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ b4 | Show current contact | Key sound adjustable | 0 0 0 0 |KNL|KBL|SKL|PKL| SimplRepSlot 0=TS1 1=TS2 2=CH | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ b8 | Show last call on startup | SMS format | GPS units | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ bc | Auto repeater lower VHF frequency, in 10Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ c0 | Auto repeater upper VHF frequency, in 10Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ c4 | Auto repeater lower UHF frequency, in 10Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ c8 | Auto repeater upper UHF frequency, in 10Hz, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ cc | Auto repeater B direction | AddrBk is sent with own code | Enable default boot channel | Boot zone A idx | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ d0 | Boot zone B idx | Boot ch A idx, 0xff=VFO | Boot ch B idx, 0xff=VFO | Enable keep last caller | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ d4 | RX Backlight delay | Display mode | ManDial GrpCall hgn time sec. | ManDial PrvCall hgn time sec. | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ d8 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ dc | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Field description: - Display mode: 0x00=Channel, 0x01=Frequency - VFO frequency step: 0x00=2.5kHz, 0x01=5kHz, 0x02=6.25kHz, 0x03=10kHz, 0x04=12.5kHz, 0x05=20kHz, 0x06=25kHz, 0x07=30kHz, 0x08=50kHz - Squelch level: 0x00=Off, 0x01=1, ..., 0x05=5 - Automatic shutdown time: 0x00=Off, 0x01=10min, 0x02=30min, 0x03=60min, 0x04=120min - Power save mode: 0x00=Off, 0x01=1:1, 0x02=2:1 - VOX sensitivity: 0x00=Off, 0x01=1, ..., 0x03=3 - Work mode A/B: 0x00=VFO, 0x01=MEM - Work mode main channel: 0x00=A, 0x01=B - TBST frquency: 0x00=1000Hz, 0x01=1450Hz, 0x02=1750Hz, 0x03=2100Hz - GPS Time zone: 0x00=GMT-12, ..., 0x0c=GMT, 0x19=GMT+13 - Boot display: 0x00=Default, 0x01=Custom Text, 0x02=Custom Image - VFO scan type: 0x00=TO, 0x01=CO, 0x02=SE - Key press function: 0x00=Off, 0x01=Voltage, 0x02=Power, 0x03=Repeater, 0x04=Reverse, 0x05=Digital Encryption, 0x06=Call, 0x07=VOX, 0x08=V/M, 0x09=Sub PTT, 0x0a=Scan, 0x0b=FM, 0x0c=Alarm, 0x0d=Record Switch, 0x0e=Record, 0x0f=SMS, 0x10=Dial, 0x11=GPS Information, 0x12=Monitor, 0x13=Main Ch. switch, 0x14=HotKey 1, ..., 0x19=HotKey 6, 0x1a=Work Alone, 0x1b=Nuisance Delete, 0x1c=Digital Monitor, 0x1d=Sub Ch Switch, 0x1e=Priority Zone, 0x1f=Programming Scan, 0x20=MIC sound quality, 0x21=Last call reply, 0x22=Channel type switch, 0x23=Simplex repeater, 0x24=Max. volume, 0x25=Ranging, 0x26=Channel ranging, 0x27=Slot switch, 0x28=Analog squelch, 0x29=Roaming, 0x2a=Zone select, 0x2b=Roaming settings, 0x2c=FixTime Mute, 0x2d=CTCSS/DCS settings, 0x2e=APRS type, 0x2f=APRS settings - Talk permit tone: 0x00=Off, 0x01=Digital, 0x02=Analog, 0x03=Both - STE Type: 0x00=Off, 0x01=Silent, 0x02=120deg, 0x03=180deg, 0x04=240deg - STE freq. no signal: 0x00=Off, 0x01=55.2Hz, 0x02=259.2Hz - VOX source: 0x00=Internal, 0x01=External, 0x02=Both - Display brightness: 0x00=1, ..., 0x04=5 - Backlight duration: 0x00=Always, 0x01=5s, ..., 0x06=30s, 0x07=1m, ..., 0x0a=5m - MIC gain: 0x00=1, ..., 0x04=5 - Max speaker/headphone volume: 0x00=Indoors, 0x01=1, ..., 0x08=8 - Menu exit time: (n+1)*5s - Auto repeater A/B direction: 0x00=Off, 0x01=positive, 0x02=negative - Digital monitor slot: 0x00=Off, 0x01=single, 0x02=both´ - Display last caller: 0x00=Off, 0x01=Show ID, 0x02=Show name, 0x03=Both - Call display mode: 0x00=Name based, 0x01=Call-sign based - Call-sign display color: 0x00=orange, 0x01=red, 0x02=yellow, 0x03=green, 0x04=turquoise, 0x05=blue, 0x06=white, 0x07=black - Enable keep last caller: A channel switch keeps laster caller. - RX Backlight delay: 0x00=Always, 0x01=1s, ..., 0x1e=30s. - Display mode: 0x00=black background, 0x01=blue background - Key sound adjustable: 0x00=Adjustable, 0x01=1, ..., 0x0f=15 - GPS units: 0x00=metric, 0x01=imperial - KNL: Knob lock enable - KBL: Keyboard lock enable - SKL: Side key lock enable - PKL: Professional key lock enable - SMS format: 0x00=M-format, 0x01=H-format, 0x02=DMR standard qdmr-0.12.3/doc/code/dmr6x2uv_settingsextension.txt000066400000000000000000000105441501654372000223410ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Enable send talker alias | Talker alias display priority | Talker alias encoding | Font Color | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Enable custom ch. background | Roaming zone index 0-based | Enable auto roaming | Enable repeater check | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | Out of repeater range alert | Rep. out of range reminder | Repeater check interval | Repeater reconnections | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | Start roaming condition | Auto roaming interval | Roaming eff. waiting time | Roaming return condition | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Mute timer (n+1) min | Encryption type | Zone A name color | Zone B name color | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | Channel A name color | Channel B name color | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Field description: - Talker alias display priority: 0x00=None, 0x01=Contact alias, 0x02=Over-the-air alias - Talker alias encoding: 0x00=ISO-8, 0x01=ISO-7, 0x02=Unicode - Font Color: 0x00=White, 0x01=Black, 0x02=Orange, 0x03=Red, 0x04=Yellow, 0x05=Green, 0x06=Turquoise, 0x07=Blue - Encryption type: 0x00=Common(Basic), 0x01=AES - Zone/Channel A/B name color: 0x00=Orange, 0x01=Red, 0x02=Yellow, 0x03=Green, 0x04=Turquoise, 0x05=Blue, 0x06=White, 0x07=Black - Out of repeater range alert: 0x00=Off, 0x01=Bell, 0x02=Voice - Rep. out of range reminder: Number of times, the reminder is shown (n-1), 0x00=1, 0x01=2, ..., 0x09=10 - Repeater check interval (n+1)*5 seconds: 0x00=5s, ..., 0x09=50s - Repeater reconnections: 0x00=3, 0x01=4, 0x03=5 - Start roaming condition: 0x00=fixed time, 0x01=out of range - Auto roaming interval: 0x00=1min, ..., 0xff=256min - Roaming eff. waiting time: 0x00=off, 0x01=1s, ..., 0x1e=30s - Roaming return condition: 0x00=fixed time, 0x01=out of range qdmr-0.12.3/doc/code/dr1801uv_alarmsystembankelement.txt000066400000000000000000000045701501654372000231120ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Alarm system count [0-8] | Unused filled with 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 | Alarm system 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0018 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 001c | Alarm system 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0030 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00ac | Alarm system 7 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00c0 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_alarmsystemelement.txt000066400000000000000000000024571501654372000222600ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Index+1,0=invalid | Alarm enable | Unknown 0x0001 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Channel index, uint16 le, 0=None, 0xffff=selected | Unknown 0x0000 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | System name, 16 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_channelbankelement.txt000066400000000000000000000104241501654372000221540ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000000 | Number of channels | Unused filled with 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000004 | Channel 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000034 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000038 | Channel 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000068 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00cfd0 | Channel 1023 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00d000 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00d004 | Channel 0 name, 20 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00d014 ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00d018 | Channel 1 name, 20 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00d028 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 11ff00 | Channel 1023 name, 20 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 120000 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_channelelement.txt000066400000000000000000000075621501654372000213310ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Index, uint16, litte endian, 0xffff invalid | Type, 0x01=FM, 0x03=DMR | Power 0x00=low, 0x01=high | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | RX frequency in Hz, uint32, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | TX frequency in Hz, uint32, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | Contact index+1, uint16, little endian, 0=none | Admit criterion | 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Color code | Time-slot, 1=TS1, 2=TS2 | 0x00 | 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | Encryption key index+1, 0=off | 0 0 0 0 0 0 |DCD|PCC| FM signaling | 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | Alarm sys index+1, 0=none | Bandwidth 0x01=12.5, 0x02=25 | Auto-scan enable | Scan list index+1, 0=none | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | RX CTCSS Freq in 0.1Hz/DCS code | RX sub-tone type | RX DCS inverted | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | TX CTCSS Freq in 0.1Hz/DCS code | TX sub-tone type | TX DCS inverted | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 | 0x00 |TKA| 0 0 0 0 0 0 0 | Unused set to 0x0000 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | PTT ID index+1, 0=none | | Group List index+1, 0=None | Unused filled with 0x00 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c ... | Lone worker 0x00=off, 0x01=on | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 | Unused, filled with 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where: - Admit criterion: 0x00=Always, 0x01 = Same CC/CTCSS, 0x02 = Channel Free - DCD: DCDM enable - PCC: Private call confirm - Sub-tone type: 0=None, 1=CTCSS, 2=DCS - FM Signaling: 0=none, 1=DTMF - TKA: Enable talk around. qdmr-0.12.3/doc/code/dr1801uv_contactbankelement.txt000066400000000000000000000045701501654372000222040ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Number of elements, uint16, little endian | Index+1 of first element, uint16, little endian. | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 | Contact 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0018 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 001c | Contact 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0030 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 5fec | Contact 1023 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 6000 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_contactelement.txt000066400000000000000000000025601501654372000213450ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Next index+1, uint16, little endian | Name length | 0x02 or 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | DMR ID, uint24, little endian |GRP|PVC|ALC| 0 0 0 0 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | Name, 16 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where: - GRP: Group call - PVC: Private call - ALC: All call qdmr-0.12.3/doc/code/dr1801uv_dmrsettingselement.txt000066400000000000000000000027661501654372000222650ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Hold time in sec [1-90] | Remote listen in sec [10-120] | Active wait, 5ms [120-600] | Active resent count [1-10] | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Pre-send count [4-15] | Kill enc. enabled | Active enc. enabled | Check enc. enabled | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | Kill dec. enabled | Active dec. enabled | Check dec. enabled | SMS format | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | Voice head count [0-20] | 0 0 0 0 |RMD|RME|CAD|CAE| Unused set 0x0000 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where: - SMS format: 0 = Compressed IP, 1 = Defined Data, 2 = IP Data - CAD: Call alter decoding - CAE: Call alter encoding - RME: Remote monitor encoding - RME: Remote monitor decoding qdmr-0.12.3/doc/code/dr1801uv_dtmfidbankelement.txt000066400000000000000000000046061501654372000220200ustar00rootroot00000000000000DTMF ID Bank: 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Number of IDs (fixed 16) | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 | DTMF ID 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0014 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0018 | DTMF ID 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 002c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0130 | DTMF ID 15 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0140 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_dtmfidelement.txt000066400000000000000000000020461501654372000211600ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Length | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Code, 16 x one DTMF number per byte, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_dtmfsettingselement.txt000066400000000000000000000102001501654372000224130ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | DTMF radio ID, 5 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 ... | Radio ID Length | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0008 | DTMF kill code, 6 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | Kill code length | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | DTMF wake code, 6 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0014 ... | Wake code length | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0018 | Delimiter | Group code | Decode response | Auto reset time in sec. [5,60]| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 001c | Kill/wake enable | Kill type | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0020 | DTMF system bank ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0050 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0054 | DTMF ID bank ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0194 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0198 | PTT ID bank ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0298 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where: - Delimiter/Group code: 0h=None, ah=A, bh=B, ch=C, dh=D, eh=*, fh=# - Decode response: 0=None, 1=Reminder, 2=Reply, 3=Both - Kill type: 0=Disable TX, 1=Disable TX & RX qdmr-0.12.3/doc/code/dr1801uv_dtmfsystembankelement.txt000066400000000000000000000045461501654372000227530ustar00rootroot00000000000000DTMF System Bank: 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | DTMF system count (=4) | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | DTMF system 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | DTMF system 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | DTMF system 3 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_dtmfsystemelement.txt000066400000000000000000000020631501654372000221070ustar00rootroot00000000000000DTMF System: 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Side tone enable | Unused, set to 0x00 | Pre time in ms, uint16, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Code duration in ms, uint16, little endian | Code Intervall in ms, uint16, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | Reset time in 0.1s, uint16, little endian | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_encryptionkeybankelement.txt000066400000000000000000000041521501654372000234500ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Encryption key 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0008 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c | Encryption key 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0014 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 006c | Encryption key 9 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0074 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_encryptionkeyelement.txt000066400000000000000000000020651501654372000226150ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Index + 1 | Key length, uint16, little endian. Fixed to 0x08 | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 | Key, 8 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0008 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_grouplistbankelement.txt000066400000000000000000000045701501654372000226010ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Group list count | Unused, filled with 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 | Group list 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0044 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0048 | Group list 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0088 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10c0 | Group list 63 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1100 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_grouplistelement.txt000066400000000000000000000035021501654372000217370ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Number of members, uint16, little endian | Group list index+1, uint16, little endian, 0=invalid | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Member index0 +1, uint16, little endian | Member index1 +1, uint16, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | Member index8 +1, uint16, little endian | Member index9 +1, uint16, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | Unused, filled with 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 40 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_keysettingselement.txt000066400000000000000000000037431501654372000222670ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Key 0 Index | Key 0, Short press function | Key 0, Long press function | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Key 1 Index | Key 1, Short press function | Key 1, Long press function | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | Key 2 Index | Key 2, Short press function | Key 2, Long press function | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | Unused or further key settings. Filled with 0x00 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where: - Key index: SideKey1 = 0, SideKey2 = 1, TopKey = 2 - Key function: None = 0, ToggleAlertTones = 1, EmergencyOn = 2, EmergencyOff = 3, TogglePower = 4, Monitor = 5, DeleteNuisance = 6, OneTouch1 = 7, OneTouch2 = 8, OneTouch3 = 9, OneTouch4 = 10, OneTouch5 = 11, ToggleTalkaround = 13, ToggleScan = 14, ToggleEncryption = 15, ToggleVOX = 16, ZoneSelect = 17, ToggleLoneWorker = 19, PhoneExit = 20 qdmr-0.12.3/doc/code/dr1801uv_messagebankelement.txt000066400000000000000000000045661501654372000222020ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Number of messages, uint32, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 | Message 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0044 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0048 | Message 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0058 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0138 | Message 7 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0160 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_messageelement.txt000066400000000000000000000020451501654372000213340ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Index+1, 0=invalid | Length | 0x00 | 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Message, 64 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 40 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_onetouchsettingelement.txt000066400000000000000000000015501501654372000231320ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Contact index+1, uint16, little endian | Action | Message index + 1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Type | DTMF ID index+1 | Unused set to 0x0000 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where: - Action: 0=Call, 1=Message - Type: 0=Disabled, 1=DMR, 2=Analog qdmr-0.12.3/doc/code/dr1801uv_onetouchsettingselement.txt000066400000000000000000000041121501654372000233120ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | One-touch setting 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | One-touch setting 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | One-touch setting 4 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_pttidbankelement.txt000066400000000000000000000046051501654372000216740ustar00rootroot00000000000000PTT ID Bank: 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Number of PTT IDs (fixed 20h) | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 | PTT ID 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0008 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c | PTT ID 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00fc | PTT ID 31 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0100 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_pttidelement.txt000066400000000000000000000015771501654372000210450ustar00rootroot00000000000000PTT ID: 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | DTMF system index+1, 0=none | TX mode | ID mode | Start ID index+1, 0=none | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | End ID index+1, 0=none | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where: - TX mode: 0=None, 1=Pre, 2=Post, 3=Both - ID mode: 0=Forbidden, 1=Each, 2=Once qdmr-0.12.3/doc/code/dr1801uv_scanlistbankelement.txt000066400000000000000000000045701501654372000223710ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Number of elements | Unused, filled with 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 | Scan list 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0050 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0054 | Scan list 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00a0 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 02d4 | Scan list 9 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0320 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_scanlistelement.txt000066400000000000000000000053331501654372000215330ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Index+1, 0=invalid | Number of entries | Priority channel 1 | Priority channel 2 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Priority channel 1 index, uint16, little endian | Priority channel 2 index, uint16, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | Revert channel | 0x00 | Revert channel index, uint16, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | Unused, filled with 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Name, 32 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 | Entry channel index 0, uint16, little endian | Entry channel index 1, uint16, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 4c | Entry channel index 14, uint16, little endian | Entry channel index 15, uint16, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where: - Priority channel 1/2: 0=None, 1=Fixed, 2=Selected - Revert channel: 0 = Last active, 1 = Fixed, 2 = Selected qdmr-0.12.3/doc/code/dr1801uv_settingselement.txt000066400000000000000000000160261501654372000215540ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | DMR ID, uint24, litte endian | 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | 0x00 | 0x00 | 0x01 | 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | Power save enable | Power save mode | VOX level 1-3 | 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | VOX delay, in 500ms | Encryption enable | Key-lock delay in sec. [1,90] | 0 0 0 0 0 |LS2|LS1|LPT| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Language | Squelch mode | 0x00 | Roger tone enable | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | 0x00 | 0x01 | Ring tone [1,20], 0=off | Key lock enable | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | Radio name, 16 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | 0 |MST|DCE|AVE|DVE|ACO|DCO| 1 | 0x00 | 0x00 | 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c | Reverse burst frequency 0.1 Hz, 0=off | 0x01 | Back-light time | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 | VOX enable | 0x00 | Campanding (?) enable | 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 34 | 0x00 | 0x00 | Up channel mode | Down channel mode | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 38 | 0x00 | 0x00 | 0x00 | 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c | Channel display | Dual stand-by mode | Scan mode | Boot screen | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 40 | Boot line 1, 8 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 44 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 48 | Boot line 2, 8 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 4c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 50 | LED enable | Lone worker response time 1s | 0 0 0 0 0 0 |BPE|PPE| Prog. password length | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 54 | Programming password, 6 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 58 ... | 0x00 | 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 5c | Lone worker reminder time 1s | Boot password length | Boot password, 6 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 60 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where: - Power save mode: 0x01 = 50%, 0x02 = 25%, 0x03 = 12% - LS1: Lock side-key 1 - LS2: Lock side-key 2 - LPT: Lock PTT - Language: 0 = simplified Chinese, 1 = English - DCO: Digital call out roger tone - DVE: Digital voice end roger tone - DCE: Digital call end roger tone - ACO: Analog call out roger tone - AVE: Analog voice end roger tone - MST: Message tone - Back-light time: 0 = Infinite, 1 = Off, 2 = 5s, 3 = 10s - Squelch Mode: 0x00 = Normal, 0x01 = Silent - Up/down channel mode: 0=channel, 1=VFO - Channel display: 0 = Number, 1 = Name, 2 = Frequency - Dual stand-by mode: 0=off, 1 = double double, 2 = double single - Scan mode: 0=time, 1=carrier, 2=search - Boot screen: 0 = Picture, 1 = Text - BPE: Boot password enable - PPE: Programming password enable qdmr-0.12.3/doc/code/dr1801uv_vfobankelement.txt000066400000000000000000000046331501654372000213430ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000000 | VFO A ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000030 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000034 | VFO B ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000064 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000068 | VFO A name, 20 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000078 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00007c | VFO B name, 20 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00008c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_zonebankelement.txt000066400000000000000000000052061501654372000215210ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Zone count, uint16, little endian | Up current zone index, uint16, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 | Down current zone index, uint16_t, little endian | Unused, set to 0x0000 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0008 | Zone 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 006c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0070 | Zone 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00d4 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c90 | Zone 149 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3cf4 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/dr1801uv_zoneelement.txt000066400000000000000000000041141501654372000206620ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Zone name, 32 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Name length, uint16, little endian | Number of entries, uint16, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 | Zone index, uint16, little endian | 0x00 | 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | Member index 00, uint16, little endian, 0-based | Member index 01, uint16, little endian, 0-based | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 64 | Member index 30, uint16, little endian, 0-based | Member index 31, uint16, little endian, 0-based | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd73_channel_bank.txt000066400000000000000000000033521501654372000202150ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00000 | Channel count | Channel 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00044 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00048 | Channel 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 117bc | Channel 1023 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 11800 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd73_channel_element.txt000066400000000000000000000113041501654372000207270ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name, 16 x 16bit unicode, litte endian, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Bandwidth | Scan list index+1 | Channel type | Enable talkaround | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 | Enable RX only | Unknown 0x01 | Enable scan auto-start | RX frequency ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 ... | TX frequency ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c ... | DTMF PTT setting index+1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 | Power | Admit criterion | Unknown 0x00 | Unknown 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 34 | RX tone mode | RX CTCSS frequency | RX DCS code | TX tone mode | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 38 | TX CTCSS frequency | TX DCS code | Unknown 0x00 | Unknown 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c | Timeslot | Color code [0-15] | Group list index+2 | Unknown 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 40 | TX contact index+1, 16bit unsiged int, little endian | Emergency system index+1 | Unknown 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 44 | Encryption key index+1 | Unknown 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where: - Bandwidth: 0=12.5kHz, 1=25kHz - Channel type: 0=FM, 1=DMR - RX/TX frequency in Hz, 32bit unsigned int, little endian - Power: 0=Low, 1=High - Admit criterion: 0=Always, 1=Color code/CTCSS, 2=Free - RX/TX tone mode: 0=None, 1=CTCSS, 2=DCS, 3=DCS inverted - RX/TX CTCSS frequency: 62.5Hz, 67.0Hz, 69.3Hz, 71.9Hz, 74.4Hz, 77.0Hz, 79.7Hz, 82.5Hz, 85.4Hz, 88.5Hz, 91.5Hz, 94.8Hz, 97.4Hz, 100.0Hz, 103.5Hz, 107.2Hz, 110.9Hz, 114.8Hz, 118.8Hz, 123.0Hz, 127.3Hz, 131.8Hz, 136.5Hz, 141.3Hz, 146.2Hz, 151.4Hz, 156.7Hz, 159.8Hz, 162.2Hz, 165.5Hz, 167.9Hz, 171.3Hz, 173.8Hz, 177.3Hz, 179.9Hz, 183.5Hz, 186.2Hz, 189.9Hz, 192.8Hz, 196.6Hz, 199.5Hz, 203.5Hz, 206.5Hz, 210.7Hz, 218.1Hz, 225.7Hz, 229.1Hz, 233.6Hz, 241.8Hz, 250.3Hz, 254.1Hz - RX/TX DCS code: 23, 25, 26, 31, 32, 36, 43, 47, 51, 53, 54, 65, 71, 72, 73, 74, 114, 115, 116, 122, 125, 131, 132, 134, 143, 145, 152, 155, 156, 162, 165, 172, 174, 205, 212, 223, 225, 226, 243, 244, 245, 246, 251, 252, 255, 261, 263, 265, 266, 271, 274, 306, 311, 315, 325, 331, 332, 343, 346, 351, 356, 364, 365, 371, 411, 412, 413, 423, 431, 432, 445, 446, 452, 454, 455, 462, 464, 465, 466, 503, 506, 516, 523, 526, 532, 546, 565, 606, 612, 624, 627, 631, 632, 645, 654, 662, 703, 712, 723, 731, 732, 734, 743, 754 - Timeslot: 0=TS1, 1=TS2, 2=DCDM-TS1, 3=DCDM-TS2 - Group list index: 0=current, 1=no-match, index+2 otherwise. qdmr-0.12.3/doc/code/gd73_contact_bank.txt000066400000000000000000000033331501654372000202370ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Contact count, 16bit unsigned int, little endian | Unused, filled 0x00 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0800 ... | Contact 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0824 ... | Contact 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 9bbc ... | Contact 1023 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 9c00 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd73_contact_element.txt000066400000000000000000000022511501654372000207530ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name, 16 x 16bit unicode, litte endian, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Type | DMR ID, 32bit unsigned int, little endian ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 ... | +---+---+---+---+---+---+---+---+ Where: - Type: 0=Group Call, 1=Private Call, 2=All Call qdmr-0.12.3/doc/code/gd73_dmr_settings_element.txt000066400000000000000000000026771501654372000220360ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Call hangtime in sec-1 [1,90] | Active wait time | Active retries | TX preambles | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Enable dec. disable-radio | Enable dec. radio-check | Enable dec. enable-radio | Unknown, filled with 0x00 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where: - Call hang time in seconds, stored -1s, i.e., 0=1s, 1=2s, ..., 10=11s (default), ..., 89=90s - Active wait time (WTF): 0=120ms, 1=125ms, ..., 36=300ms (default), ..., 96=600ms - Active retries (WTF) [1,10], 2=default - TX preambles (WTF) [0,63], 0=default qdmr-0.12.3/doc/code/gd73_dtmf_code_element.txt000066400000000000000000000017261501654372000212520ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Length | Digit 0 | Digit 1 | Digit 2 | Digit 3 | Digit 4 | Digit 5 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Digit 6 | Digit 7 | Digit 8 | Digit 9 | Digit 10 | Digit 11 | Digit 12 | Digit 13 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | Digit 14 | Digit 15 | +---+---+---+---+---+---+---+---+ Where: - Length: Specifies the number of DTMF digits. - digits: 0=0, ..., 9=9, 10=a, ..., 13=d, 14=*, 15=# qdmr-0.12.3/doc/code/gd73_dtmf_ptt_settings.txt000066400000000000000000000014331501654372000213510ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | DTMF system index | PTT ID type | PTT ID mode | Connect ID index | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Disconnect ID index | +---+---+---+---+---+---+---+---+ Where: - DTFM system index: 0-based [0,3] - PTT ID type: 0=None, 1=Pre-only (default), 2=Post-only, 3=Both - PTT ID mode: 0=Never, 1=Always, 2=Once - (Dis-)Connect ID: 0-based [0,15] qdmr-0.12.3/doc/code/gd73_dtmf_system_element.txt000066400000000000000000000015461501654372000216640ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Side-tone enable | Preamble duration | Tone duration | Pause duration | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Dead time | +---+---+---+---+---+---+---+---+ Where: - Preamble duration: multiples of 10ms: 0=0ms, ..., 40=400ms (default), ..., 100=1000ms. - Tone/pause duration: multiples of 10ms: 0=30ms (default), 1=40ms, ..., 187=1900ms. - Dead time: multiples of 200ms: 0=0.2s, 1=0.4, ..., 10=2.2s, ..., 164=33.0s. qdmr-0.12.3/doc/code/gd73_encryption_key_bank.txt000066400000000000000000000027251501654372000216520ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Key 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 ... | Key 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0048 ... | Key 15 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 004c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd73_encryption_key_element.txt000066400000000000000000000010051501654372000223560ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Key size (in nibble) | Key data +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 ... | +---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd73_group_list_bank.txt000066400000000000000000000040511501654372000207710ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Group list count | Group list 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0050 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0054 | Group list 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00a4 ... | Group list 2 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 50bc | Group list 249 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 510c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd73_group_list_element.txt000066400000000000000000000027671501654372000215230ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name, 8 x 16 bit unicode, little endian, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Member count | Member 0 index+1, 16bit unsigned integer, little endian | Member 1 index+1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 50 ... | Member 32 index+1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd73_message_bank.txt000066400000000000000000000032331501654372000202270ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Message count | Message 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0050 ... | Message 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00a0 | Message 2 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04c0 | Message 15 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0510 ... | +---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd73_message_element.txt000066400000000000000000000012741501654372000207500ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Message size (chars) | Message text, 40 x 16bit unicode, little endian ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0050 ... | +---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd73_one_touch_element.txt000066400000000000000000000012141501654372000213010ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Unused/unknown 0x00 | DMR contact index+1, little endian, 0=None | One-touch action | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Message index+1, 0=None | +---+---+---+---+---+---+---+---+ Where: - One-touch action: 0=Call, 1=Message qdmr-0.12.3/doc/code/gd73_scan_list_bank.txt000066400000000000000000000032341501654372000205630ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Scan list count | Unknown 0x01 | Unknown, filled with 0x00 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 ... | Scan list 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 006c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 05a0 ... | Scan list 15 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0600 ... | +---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd73_scan_list_element.txt000066400000000000000000000045241501654372000213040ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name, 8 x 16bit unicode, little endian, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Channel count | Channel 0, index+1, little endian | Channel 1, index+1, ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 50 ... | Priority channel 1 mode | Priority channel 2 mode | Pri. ch. 1, zone index+1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 54 | Pri. ch. 2, zone index+1 | Pri. ch. 1, channel index+1, little endian | Pri. ch. 2, channel index+1... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 58 ... little endian | TX ch. mode | TX ch. zone index +1 | TX ch. channel index+1, ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 5c ... little endian | Hold time in 0.5s, 0..10s | TX Hold time in 0.5s, 0..5s | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where: - Priority channel 1/2 mode: 0=None, 1=Fixed, 2=Selected qdmr-0.12.3/doc/code/gd73_settings_element.txt000066400000000000000000000164701501654372000211700ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Radio name, 16 x Unicode, little-endian, filled/padded with 0x0000 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | DMR ID, 24bit unsigned int little endian | Pad byte 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 | Language | Unknown 0x00 | Vox level [0,4] | Squelch level [0,9] | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | TOT | TX Interrupt enable | Power-save enable | Power save timeout | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c | Read lock enable | Write lock enable | Unknown 0x00 | Display mode | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 | Read lock pin, 6xASCII numbers [0x30,0x39], 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 34 ... | Write lock pin, 6xASCII numbers [0x30,0x39], 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 38 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c | Unknown, 0x01 | DMR mic gain [0,5] | Unknown 0x01 | FM mic gain [0,5] | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 40 | Lone worker resp. timeout in minutes [1, 480], little endian | Lone worker reminder period | Boot display mode | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 44 | Boot text line 1, 16 x 16bit Unicode, little endian, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 60 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 64 | Boot text line 2, 16 x 16bit Unicode, little endian, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 80 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 84 | Enable key tones | Key tone volume [0,13] | Low battery tone enable | Low battery tone vol. [0,13] | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 88 | Long press duration | Unknown | P1 short press | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 8c | P1 long press | P2 short press | P2 long press | Unused 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 90 | Unused 0x00 | One-touch 1 settings ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 94 ... | One-touch 2 settings ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 98 ... | One-touch 3 settings ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 9c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ a0 | One-touch 4 settings ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ a4 ... | One-touch 5 settings ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ a8 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where: - Language: 0x00=Chinese, 0x01=English - TOT: Transmit timeout, 0=off, ..., 16=180, ..., 48=500s, N = (T-20)/10 - Display mode: 0=Channel Name, 1 = Channel Frequency - Power-save timeout: Timeout in seconds [10, 60] - Lone worker reminder period: in seconds [10, 200] - Boot display mode: 0=Off, 1=Text, 2=Image, 3=both - Long press duration: In multiple of 0.5s: 0=0s, 1=0.5s, ..., 31=15.5s - P1/2 short/long press: 0=None, 1=Radio enable, 2=Radio check, 3=Radio disable, 4=Power level, 5=Monitor, 6=Emergency on, 7=Emergency Off, 8=Zone switch, 9=Scan toggle, a=VOX toggle, b=One touch 1, ..., f=One Touch 5, 10=Talkaround toggle, 11=Lone worker, 12=TBST 1750Hz, 13=Call/Swell qdmr-0.12.3/doc/code/gd73_timestamp.txt000066400000000000000000000047631501654372000176240ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Frequency range | Year/100 | Year % 100 | Month | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Day | Hour | Minute | Second | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | Unknown 0x05 | Unused, filled with 0x00 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 ... | Serial number, up to 16 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 ... | Model name, up to 16 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 ... | Device ID, up to 16 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 40 ... | Model number, up to 16 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 50 ... | Software version, up to 16 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 60 ... | +---+---+---+---+---+---+---+---+ Where - Frequency range: 0 = 406.1-470.0MHz, 1=446.000-446.995MHz, 2=400.0-470.0MHz qdmr-0.12.3/doc/code/gd73_zone_bank.txt000066400000000000000000000032001501654372000175500ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Zone count | Zone 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0030 ... | Zone 1 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0060 ... | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c10 | Zone 63 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c40 ... | +---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd73_zone_element.txt000066400000000000000000000035561501654372000203040ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | 8 x 16bit Unicode, little endian, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Channel count | Channel index+1 00, 16bit unsigned int, little endian | Channel index+1 01 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 ... | Channel index+1 02 | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c ... | Channel index+1 14 | Channel index+1, 15 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 ... | +---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd77_callsign_db_entry.txt000066400000000000000000000002701501654372000212740ustar00rootroot000000000000000 8 +---+---+---+---+---+---+---+---+---+---+---+---+ | DMR ID, 8xBCD | Name, 8xASCII, 0-terminated | +---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd77_callsign_db_header.txt000066400000000000000000000003561501654372000213700ustar00rootroot000000000000000 8 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... + |'I' 'D' '-' 'V' '0' '0' '1' 0 | # entries, LE | Entries ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... + qdmr-0.12.3/doc/code/gd77_channel.txt000066400000000000000000000073051501654372000172300ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name, 16 x ASCII, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | RX Frequency, 8 x BCD digits, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | TX Frequency, 8 x BCD digits, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | Mode (Analog/Digital) | Unused, set to 0x00 | Transmit timeout | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | Transmit timeout rekey | Admit criterion | Unknown, set to 0x50 | Scan list index (+1) | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | CTCSS/DCS RX | CTCSS/DCS TX | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 | Unused, set to 0x00 | TX DTMF system index (+1) | Unused set to 0x00 | RX DTMF system index (+1) | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | Unused, set to 0x00 | Privacy group index | TX color-code | RX group list index (+1) | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c | RX color code | Emergency system index (+1) | Contact index (+1), little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 |DCC|EAA| 0 0 0 0 | ARTS | 0 |SLT| 0 |PRV| 0 0 0 |PCC| STE |NFS| 0 | PTTId | 0 |DCD|PWR|VOX|ASE|LWK|TLK|RXO|BW |SQT| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 34 | Unused set to 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ DCC = Data call confirm, EAA = Emergency Alarm ACK, SLT = Repeater slot, PRV = Privacy Enable, PCC = Private call confirm, NSF = Non-STE Frequency, DCD = Direct call dual capacity mode, PWR = Power, ASE = Autoscan enable, LWK = Lone worker enable, TLK = Talk around enable, RXO = RX only, BW = Band width, SQT = Squelch type qdmr-0.12.3/doc/code/gd77_contact.txt000066400000000000000000000024571501654372000172560ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name, 16 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Contact DMR ID 8 BCD digits, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | Type 0=Grp, 1=Priv, 2 = All | RX tone 0=off, 1=on | Ring style [0..10] | Valid if 0xff, 0x00 otherwise | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd77_grouplist.txt000066400000000000000000000030701501654372000176430ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name, 16 x ASCII, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Member 00 index (0=disabled), little-endian | Member 01 index, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 4c | Member 1e index, little-endian | Member 1f index, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd77_grouplistbank.txt000066400000000000000000000056231501654372000205050ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | # Members GL 0 (N+1) | # Members GL 1 | # Members GL 2 | # Members GL 3 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0048 | # Members GL 48 | # Members GL 49 | # Members GL 4a | # Members GL 4e | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 004c | Unused filled with 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 007c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0080 | RX Group List 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00cc ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 17f0 | RX Group List 7e ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 183c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/gd77_scanlist.txt000066400000000000000000000040501501654372000174320ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Name, 15 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... |TKB| PLT |ChM| 0 0 0 1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | Channel 00 index +1, 0=EOL, little-endian | Channel 01 index +1, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 004c | Channel 30 index +1, little-endian | Channel 31 index +1, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0050 | Priority Channel 1, index+2, 0=None, 1=Current, little-endian | Priority Channel 2, index+2, 0=None, 1=Current, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0054 | TX Channel, index+2, 0=last active, 1=Current, little-endian | Sig. hold time N x 25ms | Pri. sample time N x 250ms | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ where ChM = Channel mark; PLT = PL type; TKB = Talkback qdmr-0.12.3/doc/code/gd77_scanlistbank.txt000066400000000000000000000045701501654372000202750ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | List 00 enable (0=disabled) | List 01 enable | List 2 enable | List 3 enable | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 003c | List 3c enable | List 3d enable | List 3e enable | List 3f enable | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0040 | Scanlist 00 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0094 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 15e4 | Scanlist 3f ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 163c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/md390_channel.txt000066400000000000000000000107161501654372000173140ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 |LWK| 1 |SQT|ASC| BandW | ChMod | ColorCode |TimeSlt|RXO|ALT|DCC|PCC| PRIV | PrivIdx |DPD|CUH| 1 0 |EAA| 0 |RXFreqR| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 |AdimtCr|PWR|VOX| 0 |RVB|TXFreqR| Unknown set to 0xc3 | TX Contact name index + 1, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | 0 0 | Tx Timeout | Tx Timeout Rekey Delay | Emergency System index + 1 | Scan List index + 1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | RX Group List index + 1 | GPS System index +1 | DTMF decode bitmap | Unused, set to 0xff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | RX Frequency, 8 digits BCD, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | TX Frequency, 8 digits BCD, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | RX CTCSS/DCS | TX CTCSS/DCS | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | RX Signaling System index +1 | TX Signaling System index +1 | Unused set to 0xff | 1 1 1 1 1 1 |RXG|TXG| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Name 16 x 16bit unicode characters ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Field description LWK = Lone Worker, default=0; SQT = Squelch type, 0=tight, 1=normal, default=1; ASC = Auto scan, default=0; BandW = Bandwidth, 0=12.5kHz, 2=25kHz, default = 0; ChMod = Channel Mode, 1=Analog or 2=Digital, default=1; ColorCode = ColorCode [0..15], default=1; TimeSlt = Repeater/time-slot, 2=TS2, 1=TS1, default=1; RXO = RX Only, 0=off, 1=on, default=0; ALT = Allow Talkaround (inverted), default=1 (off); DCC = Data Call Confirmed, default=0; PCC = Private Call Confirmed, default=0; PRIV = Privacy type, 0=None, 1=Basic or 2=Enhanced, default=0 (off); PrivIdx = Privacy Index, [0,15], default=0. DPD = Display PTT ID (inverted), default = 1. CUH = Compressed UDP data header (inverted), default=1 (off). EAA = Emergency Alarm Ack, default = 0; RXFreqR = RX reference frequency, 0=Low, 1=Medium, 2=High, default=0 (low); AdimtCr = Admit Criterion, 0=Always, 1=Channel Free or 2=Correct CTS/DCS, 3=Colorcode, default=0. PWR = Specifies the power, 0=low, 1=high. VOX = VOX Enable, 0=Disable, 1=Enable, default = 0; RVB = Reverse burst 0=disable, 1=enable, default=1; TXFreqR = TX reference frequency: 0=Low, 1=Medium, 2=High, default=0; TOffFre = Non-QT/DQT Turn-off Freq., 3=none, 0=259.2Hz, 1=55.2Hz, default=3; InCallC = In Call Criteria, 0=Always, 1=Follow Admit Criteria, 2=TX Interrupt, default=0; Tx Timeout = Tx Timeout x 15sec, 0-Infinite, 1=15s, etc, 37=555s, default=0; Power = Power, 0=low, 1=middle, 2=high, default=high; ALI = Allow interrupt (inverted), 0=allow, 1=Disabled, default=1; RXG = Receive GSP info (inverted), default=1; TXG = Send GSP info (inverted), default=1; qdmr-0.12.3/doc/code/md390_menusettings.txt000066400000000000000000000040171501654372000204260ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Menu hang time (sec) |RDS|REN|RMO|RCK|MDL|CED|CAL|TXT|ToA|TlA|CLO|CLA|CLM|ESL|SCN| 1 |VOX| 0 |SQL|LED|KPL|INS|BKL|PWR| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | 1 | 1 | 1 |GPI| 1 |PRG|DSM|PWD| Unused, set to 0xff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ where: RDS = Show radio disable; REN = Show radio enable; RMO = Show remote monitor; RCK = Show radio check; MDL = Show manual dial; CED = Show contact edit; CAL = Show call alert; TXT = Show text message; ToA = Show tone or Alert; TlA = Show talkaround; CLO = Show call-log outgoing, CLA = Show call-log answered; CLM = Show call-log missed; ESL = Show edit scan list; SCN = Show scan menu; VOX = Show VOX settings; SQL = Show squelch settings; LED = Show LED indicator menu; KPL = Show keypad lock; INS = Show intro screen; BKL = Show backlight; PWR = Show power settings; RSW = Show record switch; GPI = Hide GPS information; GPS = Hide GPS settings; PRG = Hide radio program menu; DSM = Show Display mode settings; PWD = Show password settings; NWZ = Show new zone menu; ZNS = Show zone settings; TXM = Show TX mode settings; MHT = Show menu hang time; PCM = Show private-call match settings; GCM = Show group-call match settings; NSL = Show new scan list menu; EDZ = Show edit zone menu; qdmr-0.12.3/doc/code/opengd77_callsign_db_entry.txt000066400000000000000000000017451501654372000221660ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | DMR ID stored in BCD, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | String containing Name, Callsign, etc. Up to 15 bytes ASCII, 0x00 terminated and padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_callsign_db_header.txt000066400000000000000000000020461501654372000222500ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Magic 'ID-' string | Size, 0x5d = 19bytes | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Version string, fixed to '001' | Unused, set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | Number of contacts, 16bit uint, little endian | Unused set to 0x0000 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_channel.txt000066400000000000000000000075371501654372000201210ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name, 16 x ASCII, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | RX Frequency, 8 x BCD digits, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | TX Frequency, 8 x BCD digits, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | Mode (Analog/Digital) | Power [0-10] | Unused, set to 0. | Transmit timeout in 15s | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | TOT rekey delay in sec. | Admit criterion | Unknown, set to 0x50 | Scan list index (+1) 0=None. | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | CTCSS/DCS RX, 4-digit BCD little endian, 0xffff=off. | CTCSS/DCS TX, 4-digit BCD little endian, 0xffff=off. | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 | Unused, set to 0x00 | TX DTMF system index |RID| Unused set to 0 | Radio ID, 24bit big-endian ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 ... | TX color-code | RX group list index (+1) | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c | RX color code | Emergency system index (+1) | Contact index (+1), little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 |DCC|EAA| 0 0 0 0 | ARTS | 0 |SLT| 0 |PRV| 0 0 0 |PCC| STE |NFS| 0 | PTTId | 0 |DCD| 0 |VOX|SZS|SAS|TLK|RXO|BW |SQT| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 34 | Unused set to 0 | Squelch in 5%, 0=default | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ RID = Channel radio ID enable, default=0. DCC = Data call confirm, EAA = Emergency Alarm ACK, SLT = Repeater slot, PRV = Privacy Enable, PCC = Private call confirm, NSF = Non-STE Frequency, DCD = Direct call dual capacity mode, PWR = Power, SZS = Scan zone skip enable, default=off. Was "auto scan enable" in orig. GD77. SAS = Scan all skip enable, default=off. Was "lone worker enable" in orig. GD77. TLK = Talk around enable, RXO = RX only, BW = Band width, SQT = Squelch type qdmr-0.12.3/doc/code/opengd77_contact.txt000066400000000000000000000024501501654372000201310ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Name, 16 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | Contact DMR ID 8 BCD digits. | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0014 | Type 0=Grp, 1=Priv, 2 = All | RX tone 0=off, 1=on | Ring style [0..10] | Timeslot override | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_protocol_command_clear_screen_request.txt000066400000000000000000000004221501654372000263070ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Fixed prefix 'C' (43h) | 01h | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_protocol_command_control_request.txt000066400000000000000000000006221501654372000253440ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Fixed prefix 'C' (43h) | 06h | Action | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_protocol_command_display_text_request.txt000066400000000000000000000020451501654372000263760ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Fixed prefix 'C' (43h) | 03h | Column address (usually 0) | Row address (00h, 10h, ..) | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Size (bug? always 3) | Alignment | Inverted | Payload max. 16x ASCII ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_protocol_command_okay_response.txt000066400000000000000000000002221501654372000247710ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+ 00 | '-' (2dh) | +---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_protocol_command_ping_request.txt000066400000000000000000000004221501654372000246170ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Fixed prefix 'C' (43h) | feh | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_protocol_command_render_screen_request.txt000066400000000000000000000004221501654372000265000ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Fixed prefix 'C' (43h) | 03h | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_protocol_command_request.txt000066400000000000000000000004661501654372000236120ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+...+---+ 00 | Fixed prefix 'C' (43h) | Command flag | Payload | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+...+---+ qdmr-0.12.3/doc/code/opengd77_protocol_command_show_cps_screen_request.txt000066400000000000000000000004221501654372000270460ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Fixed prefix 'C' (43h) | 00h | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_protocol_command_start_gps_request.txt000066400000000000000000000004211501654372000256670ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Fixed prefix 'C' (43h) | 7h | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_protocol_read_request.txt000066400000000000000000000014331501654372000231020ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Fixed prefix 'R' (52h) | Memory | Address 32bit unsigned int, big endian ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 ... | Length, 16bit unsigned int, big endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_protocol_read_response.txt000066400000000000000000000014331501654372000232500ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Fixed prefix 'R' (52h) | Length, 16bit unsigned int, big endian | Data, length bytes ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ nn ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_radio_info.txt000066400000000000000000000053721501654372000206150ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Struct version number, 32bit unsigned int, little-endian, default 0x0003 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 | Radio type, 32bit unsigned int, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0008 | Git version string, 16x ASCII | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0014 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0018 | Build date-time string, 16x ASCII ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0024 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0028 | Flash chip part number, 32 bit unsigned int, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 002c | Features, 16bit unsigned int, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_zone.txt000066400000000000000000000027241501654372000174550ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Name, 16 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | Channel 00 index +1, little endian. | Channel 01 index +1, little endian. | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00ac | Channel 78 index +1, little endian. | Channel 79 index +1, little endian. | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/opengd77_zonebank.txt000066400000000000000000000041521501654372000203060ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Unknown ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | 68 bits encoding whether a particular zone within this bank is enabled. ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0018 ... | 0 0 0 0 | Unused set to 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 002c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0030 | 68 Zones, 176bytes each ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2ecc ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/openrtx_channel.txt000066400000000000000000000106051501654372000201540ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Channel Mode |Unused set to 0 |RXO| BandW | Power 10dBm + n*0.2 | RX Frequency in Hz, little ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 ... endian | TX Frequency in Hz, little ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 ... endian | Scan list index +1, 0=None | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | Group list index +1, 0=None | Name, 32 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c ... | Description, 32 x ASCII, 0-padded ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 4c ... | Ch. lat. int. part, signed | Ch. lat. decimal part, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 50 | Ch. lon. int. part, signed | Ch. lon. decimal part, little endian | Ch. alt. meters, little ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 54 ... endian | Mode specific settings 5 bytes see below ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 58 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Channel Mode: 0=FM, 1=DMR, 2=M17 BandW: Bandwidth, ?? RXO: RX only FM settings 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 55 | CTCSS RX tone idx |RTE| CTCSS TX tone idx |TTE| Unused, set to 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 59 ... | +---+---+---+---+---+---+---+---+ RTE: RX Tone enable TTE: TX Tone enable DMR settings 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 55 | TX color code | RX color code | Time slot 1,2 | Contact index, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 59 | Unused, set to 0 | +---+---+---+---+---+---+---+---+ M17 settings 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 55 | TX CAN | RX CAN | Encr. mode | Channel mode | GPS mode | Contact index, little endian... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 59 ... | +---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/openrtx_contact.txt000066400000000000000000000053051501654372000202000ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name, 32 x ASCII, 0-pad ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Mode, only DMR or M17 | Mode specific settings, 6 bytes, see below ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Mode: 0=None, 1=FM, 2=DMR, 3=M17, only DMR and M17 are valid. DMR Contact settings. 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 21 | DMR ID, 32bit uint, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 25 | Unused, set to 0 |RXT| CType | Unused set to 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ RXT: RX Tone enable (ring). CType: Contact type. M17 Contact settings: 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 21 | M17 encoded address ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 25 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/openrtx_header.txt000066400000000000000000000041121501654372000177700ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Magic number 0x43585452, little endian ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | Version number (MAJOR << 8) | MINOR, little endian | Author, 32 x ASCII, 0-pad ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 ... | Description, 32 x ASCII, 0-pad ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 48 ... | Timestamp, little endian ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 50 ... | Contact count, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 54 | Channel count | Zone count | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/openrtx_zone.txt000066400000000000000000000027741501654372000175270ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name, 32 x ASCII, 0-pad ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Number of channels, uint16_t, little endian | Channel Index 0, uint32_t, little endian ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 ... | Channel Index 1, uint32_t, little endian ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 ... | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/radioddity_bootsettings.txt000066400000000000000000000024571501654372000217330ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Boot text enable (0x01=text) | Boot passwd enable | Unused, set to 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Boot passwd, 6 x BCD numbers, big endian | Unused set to 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | Unused set to 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/radioddity_boottext.txt000066400000000000000000000024561501654372000210560ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Boot message 1, 16 x ASCII, 0xff terminated and padded. ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Boot message 2, 16 x ASCII, 0xff terminated and padded. ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/radioddity_buttonsettings.txt000066400000000000000000000030711501654372000222740ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Unknown, set to 0x01 | Long press duration in 250ms | Short SK1 event | Long SK1 event | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Short SK2 event | Long press SK2 event | Short press TK action (GD77) | Long press TK action (GD77) | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | One-touch 0 action type | One-touch 0, contact index | One-touch 0, message index | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | One-touch 5 action type | One-touch 5, contact index | One-touch 5, message index | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/radioddity_channel.txt000066400000000000000000000072751501654372000206220ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name, 16 x 1byte ASCII encoded, 0xff terminated text ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | RX Frequency in 10Hz encoded as 8-digits BCD, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | TX Frequency in 10Hz encoded as 8-digits BCD, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | Mode 0=analog, 1digital | Unused set to 0 | TX timeout (TOT) N x 15s | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | TOT rekey delay in s | Admit 0=always, 1=free, 2=CC | !!! Unknown set to 0x50 !!! | Scan-list index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | RX CTCSS/DCS | TX CTCSS/DCS | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 | Unused set to 0x00 | TX signaling index +1 | Unused set to 0x00 | RX signaling index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | !!! Unknown set to 0x16 !!! | Privacy Grp 0=None, 1=53474c39| TX Color code [0,15] | RX group-list index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c | RX Color code [0,15] | Emergency sys. index +1 | TX contact index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 |DCC|EAC| 0 0 0 0 0 0 | 0 |SLT| 0 |PRV| 0 0 0 |PCC| 0 0 |STE| 0 0 0 0 |DCM|PWR|VOX| 0 0 |TLK|RXO|BW | 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 34 | Unused set to 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ where EAC = Emergency alarm ACK; DCC = Data call confirmed; SLT = Repeater slot {1,2}; PRV = Privacy enabled; PCC = Private call confirmed; STE = Non STE is frequency(?!?); DCM = Dual capacity direct mode enable; PWR = Power; VOX = Voice operated switch wnable; TLK = Allow talkaround; RXO = RX only; BW = Bandwidth; qdmr-0.12.3/doc/code/radioddity_channelbank.txt000066400000000000000000000025021501654372000214420ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | 128 bits encoding whether a particular channel within this bank is enabled. ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | 128 Channels, 56bytes each ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/radioddity_contact.txt000066400000000000000000000025031501654372000206320ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Name, 16 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | Contact DMR ID 8 BCD digits. | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0014 | Type 0=Grp, 1=Priv, 2 = All | RX tone 0=off, 1=on | Ring style [0..10] | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/radioddity_dtmfcontact.txt000066400000000000000000000025021501654372000215040ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Name, 16 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | DTMF number, 16 digits, table = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, *, #}, 1 byte per digit ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 001c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/radioddity_generalsettings.txt000066400000000000000000000065661501654372000224120ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Radio name, 8 x 1byte ASCII encoded, 0xff terminated text ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0008 | 8-digit BCD DMR ID, big-endian. | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c | Unused set to 0x00000000 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | Unused set to 0x00 | Tx preamble N x 60ms | Monitor type 0=open, 1=silent | VOX sensitivity [1..10] | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0014 | Low battery interval N x 5s | Call alert dur N x 5s | Lone worker response in min | Lone worker reminder in sec | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0018 | Groupcall hang-time N x 500ms | Priv.call hang-time N x 500ms | ARTS tone |UNT|RST|UMV|DMV|SBP|SBR|DAT| 0 |FIT|STT|PAT|PDT| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 001c | 1 |IQO|DAL| 0 0 0 0 0 | ScanM |ANI|DBW|TXT| 0 0 0 | Rep. STE | Rep. delay | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0020 | Prog. passwd 8 x ASCII, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0024 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ where UNT = Unknown number tone RST = Reset tone UMV = Up-channel mode VFO DMV = Down-channel mode VFO SBP = Send wakeup preamble. SBR = Enable sleep mode during RX. DAT = Disables all tones; FIT = Channel free indication tone STT = Self-test pass tone PAT = Talk permit tone analog PDT = Talk permit tone digital IQO = Inhibit quick-key override DAL = Disables all LEDs ScanM = Scan mode ANI = Enable animation DBW = Always TX on active channel on double-wait TXT = TX exit tone qdmr-0.12.3/doc/code/radioddity_grouplist.txt000066400000000000000000000027241501654372000212340ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Name, 16 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | Contact 00 index +1, 0 end of list, little endian | Contact 01 index +1, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 002c | Contact 14 index +1, little endian | Contact 15 index +1, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/radioddity_grouplistbank.txt000066400000000000000000000035351501654372000220710ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | N Contacts Group 00, 0=EOL | N Contacts Group 01 | N Contacts Group 02 | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 003c ... | N Contacts Group 61 | N Contacts Group 62 | N Contacts Group 63 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0040 | Unused set to 0x00 | Unused set to 0x00 | Unused set to 0x00 | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 007c ... | Unused set to 0x00 | Unused set to 0x00 | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0080 | 64 Group lists, 32 bytes each ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 087c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/radioddity_menusettings.txt000066400000000000000000000032261501654372000217270ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Menu hang time in sec |RMO|RCK|MND|ECN|CAL|ESL|SCS|MSG|INS|BKL|PWR|TON|TLK|PRG|RDI|REN|ANC|MIC|PLK|VOX|PRV|SQL|LED|KPL| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | 1 1 1 0 0 |DBL|DSM|OTC| Unknown, set to 0xff |ChanDis| 0 0 |BKLTime|KeyLock|DblWait|KYT| 1 | 0 0 0 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ RMO = Remote monitor menu enable, RCK = Radio check menu enable, MND = Manual dial menu enable, ECN = Edit contact menu enable, CAL = Call alert menu enable, ESL = Edit scan list enable, SCS = Scan start enable, MSG = Message menu enable, INS = Intro screen menu enable, BKL = Backlight menu enable, PWR = Power setting enable, TON = Tone menu enable, TLK = Talkaround menu enable, PRG = Program-password menu enable, RDI = Radio-disable menu, REN = Radio-enable menu, ANC = Answered calls menu enable, MIC = Missed calls menu enable, PLK = Password and lock menu enable, VOX = VOX menu enable, PRV = Privacy menu enable, SQL = Squelch menu enable, LED = LED indicator menu enable, KPL = Keyboard lock menu enable, DBL = Double-wait menu enable, DSM = Display mode menu enable, OTC = Outgoing calls menu enable, BKLTime = Backlight time, DblWait = Double wait mode, KYT = Key tone enable, qdmr-0.12.3/doc/code/radioddity_messagebank.txt000066400000000000000000000066561501654372000214740ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | # Messages | Unused filled with 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0008 | Length message 0 | Length message 1 | Length message 2 | Length message 3 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0044 | Length message 3c | Length message 3d | Length message 3e | Length message 3f | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0048 | Unused filled with 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0084 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0088 | Message 0, max 144 x ASCII, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0114 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 23f8 | Message 3f, max 144 x ASCII, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2484 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/radioddity_privacy.txt000066400000000000000000000052111501654372000206530ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Privacy Type, 0=None, 1=basic | Unused, set to 0x00 | Key bitmap | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 | Unused set to 0x00000000 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0008 | Key 01, 32bit, 0xffff=off | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c | Key 01 copy, 32bit, 0xffff=off | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0008 | Key 02, 32bit, 0xffff=off | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c | Key 02 copy, 32bit, 0xffff=off | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0080 | Key 16, 32bit, 0xffff=off | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0084 | Key 16 copy, 32bit, 0xffff=off | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/radioddity_scanlist.txt000066400000000000000000000040501501654372000210160ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Name, 15 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... |TKB| PLT |ChM| 0 0 0 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | Channel 00 index +1, 0=EOL, little-endian | Channel 01 index +1, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 004c | Channel 30 index +1, little-endian | Channel 31 index +1, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0050 | Priority Channel 1, index+2, 0=None, 1=Current, little-endian | Priority Channel 2, index+2, 0=None, 1=Current, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0054 | TX Channel, index+2, 0=last active, 1=Current, little-endian | Sig. hold time N x 25ms | Pri. sample time N x 250ms | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ where ChM = Channel mark; PLT = PL type; TKB = Talkback qdmr-0.12.3/doc/code/radioddity_scanlistbank.txt000066400000000000000000000031201501654372000216470ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Valid scanlist 00 1=Yes, 0=No | Valid scanlist 01 | Valid scanlist 02 | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00f8 ... | Valid scanlist 249 | Unused set to 0x00 | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00fc | Unused set to 0x00 | Unused set to 0x00 | Unused set to 0x00 | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0100 | 250 scan lists, 88 bytes each ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 56ec ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/radioddity_vfochannel.txt000066400000000000000000000073631501654372000213330ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name, 16 x ASCII, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | RX Frequency, 8 x BCD digits, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | TX Frequency, 8 x BCD digits, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | Mode (Analog/Digital) | Unused, set to 0x00 | Transmit timeout | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | Transmit timeout rekey | Admit criterion | Unknown, set to 0x50 | Scan list index (+1) | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | CTCSS/DCS RX | CTCSS/DCS TX | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 | Unused, set to 0x00 | TX DTMF system index (+1) | Unused set to 0x00 | RX DTMF system index (+1) | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | Unused, set to 0x00 | Privacy group index | TX color-code | RX group list index (+1) | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c | RX color code | Emergency system index (+1) | Contact index (+1), little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 |DCC|EAA| 0 0 0 0 | ARTS | 0 |SLT| 0 |PRV| 0 0 0 |PCC| STE |NFS| 0 | PTTId | 0 |DCD|PWR|VOX|ASE|LWK|TLK|RXO|BW |SQT| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 34 | Offset Frequency in 1kHz, 4-digit BCD, little endian | StepSize |OffsDir| 0 0 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ DCC = Data call confirm, EAA = Emergency Alarm ACK, SLT = Repeater slot, PRV = Privacy Enable, PCC = Private call confirm, NSF = Non-STE Frequency, DCD = Direct call dual capacity mode, PWR = Power, ASE = Autoscan enable, LWK = Lone worker enable, TLK = Talk around enable, RXO = RX only, BW = Band width, SQT = Squelch type OffsDir = Offset direction, 0=none, 1=+, 2=-, qdmr-0.12.3/doc/code/radioddity_zone.txt000066400000000000000000000027241501654372000201570ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Name, 16 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | Channel 00 index +1 | Channel 01 index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 002c | Channel 14 index +1 | Channel 15 index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/radioddity_zonebank.txt000066400000000000000000000025021501654372000210050ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | 250 bits encoding whether a particular zone within this bank is enabled. ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 002c ... | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0020 | 250 Zones, 48bytes each ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2efc ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/rd5r_channel.txt000066400000000000000000000072751501654372000173420ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name, 16 x 1byte ASCII encoded, 0xff terminated text ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | RX Frequency in 10Hz encoded as 8-digits BCD, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | TX Frequency in 10Hz encoded as 8-digits BCD, little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | Mode 0=analog, 1digital | Unused set to 0 | TX timeout (TOT) N x 15s | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | TOT rekey delay in s | Admit 0=always, 1=free, 2=CC | !!! Unknown set to 0x50 !!! | Scan-list index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | RX CTCSS/DCS | TX CTCSS/DCS | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 | Unused set to 0x00 | TX signaling index +1 | Unused set to 0x00 | RX signaling index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | !!! Unknown set to 0x16 !!! | Privacy Grp 0=None, 1=53474c39| TX Color code [0,15] | RX group-list index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c | RX Color code [0,15] | Emergency sys. index +1 | TX contact index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 |DCC|EAC| 0 0 0 0 0 0 | 0 |SLT| 0 |PRV| 0 0 0 |PCC| 0 0 |STE| 0 0 0 0 |DCM|PWR|VOX| 0 0 |TLK|RXO|BW | 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 34 | Unused set to 0x00 | Unused set to 0x00 | Unused set to 0x00 | Squelch [0..9] | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ where EAC = Emergency alarm ACK; DCC = Data call confirmed; SLT = Repeater slot {1,2}; PRV = Privacy enabled; PCC = Private call confirmed; STE = Non STE is frequency(?!?); DCM = Dual capacity direct mode enable; PWR = Power; VOX = Voice operated switch wnable; TLK = Allow talkaround; RXO = RX only; BW = Bandwidth; qdmr-0.12.3/doc/code/rd5r_generalsettings.txt000066400000000000000000000066041501654372000211230ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Radio name, 8 x 1byte ASCII encoded, 0xff terminated text ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0008 | 8-digit BCD DMR ID | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c | Unused set to 0x00000000 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | Unused set to 0x00 | Tx preamble N x 60ms | Monitor type 0=open, 1=silent | VOX sensitivity [1..10] | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0014 | Low battery interval N x 5s | Call alert dur N x 5s | Lone worker response in min | Lone worker reminder in sec | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0018 | Groupcall hang-time N x 500ms | Priv.call hang-time N x 500ms | ARTS tone |UNT|RST|UMV|DMV|SBP|SBR|DAT| 0 |FIT|STT|PAT|PDT| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 001c | 1 |IQO|DAL| 0 0 0 0 0 | ScanM |ANI|DBW|TXT| 0 0 0 | Rep. STE | Rep. delay | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0020 | Prog. passwd 8 x ASCII, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0024 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ where UNT = Unknown number tone RST = Reset tone UMV = Up-channel mode VFO DMV = Down-channel mode VFO SBP = Save battery by disable preamble SBR = Save battery by disable receive DAT = Disables all tones; FIT = Channel free indication tone STT = Self-test pass tone PAT = Talk permit tone analog PDT = Talk permit tone digital IQO = Inhibit quick-key override DAL = Disables all LEDs ScanM = Scan mode ANI = Enable animation DBW = Always TX on active channel on double-wait TXT = TX exit tone qdmr-0.12.3/doc/code/rd5r_timestamp.txt000066400000000000000000000012341501654372000177220ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Year, 4xBCD, big-endian | Month 2xBCD | Day 2xBCD | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Hour, 2xBCD | Minute, 2xBCD | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/rd5rbootsettings.txt000066400000000000000000000024241501654372000203060ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Boot text enable (0x01=text) | Boot passwd enable | Unused, set to 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Boot passwd, 6 x BCD numbers, big endian | Unused set to 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | Unused set to 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/rd5rbuttonsettings.txt000066400000000000000000000030361501654372000206560ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Unknown, set to 0x01 | Long press duration in 250ms | Short SK1 event | Long SK1 event | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Short SK2 event | Long press SK2 event | Short press TK action (GD77) | Long press TK action (GD77) | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | One-touch 0 action type | One-touch 0, contact index | One-touch 0, message index | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | One-touch 5 action type | One-touch 5, contact index | One-touch 5, message index | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/rd5rcontact.txt000066400000000000000000000024501501654372000172140ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Name, 16 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | Contact DMR ID 8 BCD digits. | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0014 | Type 0=Grp, 1=Priv, 2 = All | RX tone 0=off, 1=on | Ring style [0..10] | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/rd5rdtmfcontact.txt000066400000000000000000000024471501654372000200750ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Name, 16 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | DTMF number, 16 digits, table = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, *, #}, 1 byte per digit ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 001c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/rd5rgrouplist.txt000066400000000000000000000026711501654372000176160ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Name, 16 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | Contact 00 index +1, 0 end of list | Contact 01 index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 002c | Contact 14 index +1 | Contact 15 index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/rd5rgrouplisttab.txt000066400000000000000000000035021501654372000202770ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | N Contacts Group 00, 0=EOL | N Contacts Group 01 | N Contacts Group 02 | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 003c ... | N Contacts Group 61 | N Contacts Group 62 | N Contacts Group 63 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0040 | Unused set to 0x00 | Unused set to 0x00 | Unused set to 0x00 | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 007c ... | Unused set to 0x00 | Unused set to 0x00 | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0080 | 64 Group lists, 32 bytes each ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 087c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/rd5rmenusettings.txt000066400000000000000000000031731501654372000203110ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Menu hang time in sec |RMO|RCK|MND|ECN|CAL|ESL|SCS|MSG|INS|BKL|PWR|TON|TLK|PRG|RDI|REN|ANC|MIC|PLK|VOX|PRV|SQL|LED|KPL| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | 1 1 1 0 0 |DBL|DSM|OTC| Unknown, set to 0xff |ChanDis| 0 0 |BKLTime|KeyLock|DblWait|KYT| 1 | 0 0 0 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ RMO = Remote monitor menu enable, RCK = Radio check menu enable, MND = Manual dial menu enable, ECN = Edit contact menu enable, CAL = Call alert menu enable, ESL = Edit scan list enable, SCS = Scan start enable, MSG = Message menu enable, INS = Intro screen menu enable, BKL = Backlight menu enable, PWR = Power setting enable, TON = Tone menu enable, TLK = Talkaround menu enable, PRG = Program-password menu enable, RDI = Radio-disable menu, REN = Radio-enable menu, ANC = Answered calls menu enable, MIC = Missed calls menu enable, PLK = Password and lock menu enable, VOX = VOX menu enable, PRV = Privacy menu enable, SQL = Squelch menu enable, LED = LED indicator menu enable, KPL = Keyboard lock menu enable, DBL = Double-wait menu enable, DSM = Display mode menu enable, OTC = Outgoing calls menu enable, BKLTime = Backlight time, DblWait = Double wait mode, KYT = Key tone enable, qdmr-0.12.3/doc/code/rd5rmessagetab.txt000066400000000000000000000066231501654372000177020ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | # Messages | Unused filled with 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0004 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0008 | Length message 0 | Length message 1 | Length message 2 | Length message 3 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0044 | Length message 3c | Length message 3d | Length message 3e | Length message 3f | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0048 | Unused filled with 0 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0084 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0088 | Message 0, max 144 x ASCII, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0114 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 23f8 | Message 3f, max 144 x ASCII, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2484 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/rd5rscanlist.txt000066400000000000000000000040151501654372000174000ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Name, 15 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | 0 0 0 0 |ChM| PLT |TKB| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | Channel 00 index +1, 0 end of list | Channel 01 index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 004c | Channel 30 index +1 | Channel 31 index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0050 | Priority Channel 1, index+2, 0=None, 1=Current | Priority Channel 2, index+2, 0=None, 1=Current | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0054 | TX Channel, index+1, 0=last active | Sig. hold time N x 25ms | Pri. sample time N x 250ms | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ where ChM = Channel mark; PLT = PL type; TKB = Talkback qdmr-0.12.3/doc/code/rd5rscanlisttab.txt000066400000000000000000000030651501654372000200730ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Valid scanlist 00 1=Yes, 0=No | Valid scanlist 01 | Valid scanlist 02 | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00f8 ... | Valid scanlist 249 | Unused set to 0x00 | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00fc | Unused set to 0x00 | Unused set to 0x00 | Unused set to 0x00 | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0100 | 250 scan lists, 88 bytes each ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 56ec ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/rd5rvfosettings.txt000066400000000000000000000021171501654372000201340ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | VFO A settings ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0034 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0038 | VFO B settings ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 006c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/rd5rzone.txt000066400000000000000000000026711501654372000165410ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Name, 16 ASCII encoded bytes, 0xff terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | Channel 00 index +1 | Channel 01 index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 002c | Channel 14 index +1 | Channel 15 index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/rd5rzonetab.txt000066400000000000000000000024471501654372000172310ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | 250 bits encoding whether a particular zone within this bank is enabled. ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 001c ... | 0 | 0 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0010 | 250 Zones, 48bytes each ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2edc ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/tyt_buttonsettings.txt000066400000000000000000000041141501654372000207570ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Unused, set to 0 | Side button 1 short press | Side button 1 long press | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Side button 2 short press | Side button 2 long press | Unused set to 0x00 ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | Unknown set to 0x01 | Long press dur x250ms | Unused set to 0xffff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | One-touch setting 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | One-touch setting 5 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/tyt_channel.txt000066400000000000000000000101771501654372000173010ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 |LWK| 1 1 |ASC| BandW | ChMod | ColorCode |TimeSlt|RXO|ALT|DCC|PCC| PRIV | PrivIdx |DPD| 1 1 0 |EAA| 0 |RXFreqR| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 |AdimtCr| 1 |VOX| 0 1 |TXFreqR| Device specific settings | TX Contact name index + 1, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | 0 0 | Tx Timeout | Tx Timeout Rekey Delay | Emergency System index + 1 | Scan List index + 1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | RX Group List index + 1 | GPS System index +1 | DTMF decode bitmap | Device specific settings | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | RX Frequency, 8 digits BCD, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | TX Frequency, 8 digits BCD, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | RX CTCSS/DCS | TX CTCSS/DCS | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | RX Signaling System index +1 | TX Signaling System index +1 | Unused set to 0xff | 1 1 1 1 1 1 |RXG|TXG| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Name 16 x 16bit unicode characters ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Field description: LWK = Lone Worker, default=0. ASC = Auto scan, default=0. BandW = Bandwidth, 0=12.5kHz, 1=20kHz or 2=25kHz, default = 0. ChMod = Channel Mode, 1=Analog or 2=Digital, default=1. ColorCode = ColorCode [0..15], default=1. TimeSlt = Repeater/time-slot, 2=TS2, 1=TS1, default=1. RXO = RX Only, 0=off, 1=on, default=0. ALT = Allow Talkaround (inverted), default=1 (off). DCC = Data Call Confirmed, default=0; PCC = Private Call Confirmed, default=0. PRIV = Privacy type, 0=None, 1=Basic or 2=Enhanced, default=0 (basic). PrivIdx = Privacy Index, [0,15], default=0. DPD = Display PTT ID (inverted), default = 1. EAA = Emergency Alarm Ack, default = 0. RXFreqR = RX reference frequency, 0=Low, 1=Medium, 2=High, default=0 (low). AdimtCr = Admit Criterion, 0=Always, 1=Channel Free or 2=Correct CTS/DCS, 3=Colorcode, default=0. VOX = VOX Enable, 0=Disable, 1=Enable, default = 0; TXFreqR = TX reference frequency: 0=Low, 1=Medium, 2=High, default=0. Tx Timeout = Tx Timeout x 15sec, 0-Infinite, 1=15s, etc, 37=555s, default=0. LMS = Leader/MS: Leader or MS, default=1. DCD = DCDM switch (inverted), default=1. ALI = Allow interrupt (inverted), 0=allow, 1=Disabled, default=1. RXG = Receive GSP info (inverted), default=1. TXG = Send GSP info (inverted), default=1. qdmr-0.12.3/doc/code/tyt_contact.txt000066400000000000000000000022131501654372000173140ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | DMR ID (binary coded), little endian | 1 1 |RXT| 0 0 0 | Type | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Contact Name 16 x 16bit Unicode ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ where - Type = Call type where 0=disabled, 1=group, 2=private, 3=all - RXT = Receive tone enable; qdmr-0.12.3/doc/code/tyt_emergencysettings.txt000066400000000000000000000022211501654372000214170ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | 1 1 1 1 1 |ERM|RMD|RDD| Remote mon durarion in 10s | TX time-out in 25ms | Message limit [1-3] | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Unused, set to 0xff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ where: ERM = Emergency remote monitor decode. RMD = Remote monitor decode. RDD = Radio disable decode. qdmr-0.12.3/doc/code/tyt_emergencysystem.txt000066400000000000000000000024571501654372000211160ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Name 16 x 16bit unicode, 0-terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | 0 1 | AMode | 1 1 | AType | Impolite retires | Polite retires | Hot MIC durarion in 10s | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 | Revert channel index +1, 0=Disabled, 0xffff=selected | Unused, set to 0xffff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/tyt_gpssystem.txt000066400000000000000000000020451501654372000177220ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Revert channel index + 1, little endian, 0 = current | Repeat intv. N x 30s, 0=off | Unused, set to 0xff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Destination contact index + 1, little endian, 0 = none | 10 x unused bytes set to 0xff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/tyt_grouplist.txt000066400000000000000000000024561501654372000177220ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Group List Name 16 x 16bit unicode characters ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Member Contact index+1 00, little endian | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 5c ... | Member Contact index+1 31, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/tyt_menusettings.txt000066400000000000000000000037311501654372000204140ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Menu hang time (sec) |RDS|REN|RMO|RCK|MDL|CED|CAL|TXT|ToA|TlA|CLO|CLA|CLM|ESL|SCN| 1 |VOX| 0 |SQL|LED|KPL|INS|BKL|PWR| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | 1 | 1 | 1 | 1 | 1 |PRG|DSM|PWD| Unused, set to 0xff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ where: RDS = Show radio disable; REN = Show radio enable; RMO = Show remote monitor; RCK = Show radio check; MDL = Show manual dial; CED = Show contact edit; CAL = Show call alert; TXT = Show text message; ToA = Show tone or Alert; TlA = Show talkaround; CLO = Show call-log outgoing, CLA = Show call-log answered; CLM = Show call-log missed; ESL = Show edit scan list; SCN = Show scan menu; VOX = Show VOX settings; SQL = Show squelch settings; LED = Show LED indicator menu; KPL = Show keypad lock; INS = Show intro screen; BKL = Show backlight; PWR = Show power settings; RSW = Show record switch; PRG = Hide radio program menu; DSM = Show Display mode settings; PWD = Show password settings; NWZ = Show new zone menu; ZNS = Show zone settings; TXM = Show TX mode settings; MHT = Show menu hang time; PCM = Show private-call match settings; GCM = Show group-call match settings; NSL = Show new scan list menu; EDZ = Show edit zone menu; qdmr-0.12.3/doc/code/tyt_onetouchsettings.txt000066400000000000000000000010221501654372000212630ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | 1 1 | Type | Action | Message index +1, 0=none. | Contact index +1, little endian, 0=none. | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/tyt_privacy.txt000066400000000000000000000055471501654372000173530ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Enhanced key 0, 16bytes raw ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 70 | Enhanced key 7, 16 bytes raw ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 7c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 80 | Unused, filled with 0xff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 8c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 90 | Basic key 0, 16bit uint little-endian | Basic Key 1, 16bit uint little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ac | Basic key 14, 16bit uint little-endian | Basic key 15, 16bit uint little-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/tyt_scanlist.txt000066400000000000000000000041751501654372000175120ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Scan List Name 16 x 16bit unicode characters, each unicode char is encoded in little-endian ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Priority Channel 1, index+1, 0x0000=current, 0xffff=none | Priority Channel 1, index+1, 0x0000=current, 0xffff=none | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 |Tx Designated Channel index+1, 0x0000=current, 0xffff=none | !!! Unknown set to 0xf1 !!! | Hold Time N x 25ms | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | Prio. Sample Time N x 250ms | Unused, set to 0xff | Member Channel index+1 00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c | Member Channel index+1 01 | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 64 ... | Member Channel index+1 30 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Note: - All indices are encoded little-endian. qdmr-0.12.3/doc/code/tyt_settings.txt000066400000000000000000000136321501654372000175300ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Intro Line 1, 10 x 16bit unicode characters ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | Intro Line 2, 10 x 16bit unicode characters ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 26 | Reserved 24 bytes, set to 0xff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 40 | 1 1 1 |MNT| 1 |DAL| 1 0 |TPA|TPD|PWE|CIT| 1 |DAT|SMR|SPR| 1 1 1 |INP| 1 0 1 | 0 | Unused set to 0xff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 44 | DMR ID 24bit, little endian | Unused 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 48 | TX Preamble Duration N x 60ms | Grp Call Hang Time N x 100ms | Prv. Call Hang Time N x 100ms | VOX sensitivity [1..10] | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 4c | Unused, set 0x00 | Unused, set 0x00 | RX Low Bat. Intv N x 5s | Call Alert Tone Dur N x 5s | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 50 | Lone Worker Resp. Time in min | Lone Worker Rem. Time in sec | Unused, set to 0x00 | Scan Dig. Hang Time N x 100ms | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 54 | Scan Anal. Hang Time N x 100ms| 0 0 0 0 0 0 |BcLTime| Keypad Lock Time N x 5s | Unused, set to 0xff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 58 | Power-on password 8 x BCD numbers, 0x00000000=default | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 5c | Radio prog. password 8 x BCD numbers, 0x00000000=disabled | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 60 | PC Programming password 8 x ASCII, 0x00 terminated, filled with 0xff=disabled ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 64 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 68 | Unused set to 0xffffffff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 6c | Unused set to 0xffffffff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 70 | Radio name 16 x 16bit unicode chars ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 8c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Field description: MNT = Monitor type: 1 = Open Squelch, 0 = Silent, default=1; DAL = Disable all LEDs (inverted); TPD = Talk permit tone digital; TPA = Talk permit tone analog; PWE = Password and lock enable (inverted); CIT = Ch. free indication tone (inverted); DAT = Disable all tones (inverted); SMR = Save Mode Receive; SPR = Save Preamble; INP = Intro Picture; BcLTime = Backlight time, 0=Always, t=n*5s; Keypad Lock Time = 0xff=manual otherwise qdmr-0.12.3/doc/code/tyt_timestamp.txt000066400000000000000000000020451501654372000176670ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Unused, set to 0x00 | Date YYYY-MM-DD hh:mm:ss encoded as 14 BCD numbers ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | CPS Version, VV.VV encoded using table {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?} | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/tyt_zone.txt000066400000000000000000000024561501654372000166450ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Zone name, 16 x 16bit unicode chars ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Member Channel index+1 00 VFO A | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c ... | Member Channel index+1 15 VFO A | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/tytcallsigndbentry.txt000066400000000000000000000030701501654372000207100ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | DMR ID in binary format | Unused set to 0xff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Callsign 16 x ASCII ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | Name, City, etc.; 100 x ASCII ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 74 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/tytcallsigndbindex.txt000066400000000000000000000021051501654372000206540ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000000 | Number of entries in DB | Pad set to 0xff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000004 | 4096 Index entries, see IndexElement::Entry ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 004000 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/tytcallsigndbindexentry.txt000066400000000000000000000010221501654372000217330ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | 12 high-bits of the DMR ID | 20-bit index within the callsign DB list | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/uv390_bootsettings.txt000066400000000000000000000024561501654372000204640ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Unused, set to 0xffffff | Boot zone index +1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Boot CH A index +1 (in zone) | Unused, set to 0xff | Boot CH B index +1 (in zone) | Unused set to 0xffff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 ... | Unknown index, set to 0x0001, little endian | Unused filled with 0xff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/uv390_channel.txt000066400000000000000000000105371501654372000173470ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 |LWK| 1 1 |ASC| BandW | ChMod | ColorCode |TimeSlt|RXO|ALT|DCC|PCC| PRIV | PrivIdx |DPD| 0 |EAA| 1 1 0 |RXFreqR| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 |AdimtCr| 1 |VOX| 0 1 |TXFreqR|TOffFre|InCallC| 0 0 0 0 | TX Contact name index + 1, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | 0 0 | Tx Timeout | Tx Timeout Rekey Delay | Emergency System index + 1 | Scan List index + 1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c | RX Group List index + 1 | GPS System index +1 | DTMF decode bitmap | Squelch [0..9] | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | RX Frequency, 8 digits BCD, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | TX Frequency, 8 digits BCD, little endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 18 | RX CTCSS/DCS | TX CTCSS/DCS | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c | RX Signaling System index +1 | TX Signaling System index +1 | 1 1 1 1 1 1 | Power | 1 1 1 |LMS|DCD|ALI|RXG|TXG| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Name 16 x 16bit unicode characters / Stepsize (byte 0x20) ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Field description: LWK = Lone Worker, default=0. ASC = Auto scan, default=0. BandW = Bandwidth, 0=12.5kHz, 1=20kHz or 2=25kHz, default = 0. ChMod = Channel Mode, 1=Analog or 2=Digital, default=1. ColorCode = ColorCode [0..15], default=1. TimeSlt = Repeater/time-slot, 2=TS2, 1=TS1, default=1. RXO = RX Only, 0=off, 1=on, default=0. ALT = Allow Talkaround (inverted), default=1 (off). DCC = Data Call Confirmed, default=0; PCC = Private Call Confirmed, default=0. PRIV = Privacy type, 0=None, 1=Basic or 2=Enhanced, default=0 (basic). PrivIdx = Privacy Index, [0,15], default=0. DPD = Display PTT ID (inverted), default = 1. EAA = Emergency Alarm Ack, default = 0. RXFreqR = RX reference frequency, 0=Low, 1=Medium, 2=High, default=0 (low). AdimtCr = Admit Criterion, 0=Always, 1=Channel Free or 2=Correct CTS/DCS, 3=Colorcode, default=0. VOX = VOX Enable, 0=Disable, 1=Enable, default = 0; TXFreqR = TX reference frequency: 0=Low, 1=Medium, 2=High, default=0. TOffFre = Non-QT/DQT Turn-off Freq., 3=none, 0=259.2Hz, 1=55.2Hz, default=3. InCallC = In Call Criteria, 0=Always, 1=Follow Admit Criteria, 2=TX Interrupt, default=0. Tx Timeout = Tx Timeout x 15sec, 0-Infinite, 1=15s, etc, 37=555s, default=0. Power = Power, 0=low, 1=middle, 2=high, default=high. LMS = Leader/MS: Leader or MS, default=1. DCD = DCDM switch (inverted), default=1. ALI = Allow interrupt (inverted), 0=allow, 1=Disabled, default=1. RXG = Receive GSP info (inverted), default=1. TXG = Send GSP info (inverted), default=1. qdmr-0.12.3/doc/code/uv390_menusettings.txt000066400000000000000000000040171501654372000204600ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Menu hang time (sec) |RDS|REN|RMO|RCK|MDL|CED|CAL|TXT|ToA|TlA|CLO|CLA|CLM|ESL|SCN| 1 |VOX| 0 |SQL|LED|KPL|INS|BKL|PWR| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | 1 | 1 |RSW|GPI|GPS|PRG|DSM|PWD|NWZ|ZNS|TXM|MHT|PCM|GCM| 1 1 | 1 1 1 1 1 1 |NSL|EDZ| Unused, set to 0xff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ where: RDS = Show radio disable; REN = Show radio enable; RMO = Show remote monitor; RCK = Show radio check; MDL = Show manual dial; CED = Show contact edit; CAL = Show call alert; TXT = Show text message; ToA = Show tone or Alert; TlA = Show talkaround; CLO = Show call-log outgoing, CLA = Show call-log answered; CLM = Show call-log missed; ESL = Show edit scan list; SCN = Show scan menu; VOX = Show VOX settings; SQL = Show squelch settings; LED = Show LED indicator menu; KPL = Show keypad lock; INS = Show intro screen; BKL = Show backlight; PWR = Show power settings; RSW = Show record switch; GPI = Hide GPS information; GPS = Hide GPS settings; PRG = Hide radio program menu; DSM = Show Display mode settings; PWD = Show password settings; NWZ = Show new zone menu; ZNS = Show zone settings; TXM = Show TX mode settings; MHT = Show menu hang time; PCM = Show private-call match settings; GCM = Show group-call match settings; NSL = Show new scan list menu; EDZ = Show edit zone menu; qdmr-0.12.3/doc/code/uv390_settings.txt000066400000000000000000000173651501654372000176050ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Intro Line 1, 10 x 16bit unicode characters ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | Intro Line 2, 10 x 16bit unicode characters ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 26 | Reserved 24 bytes, set to 0xff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 40 | TXM | 1 |MNT| 1 |DAL| 1 0 |TPA|TPD|PWE|CIT| 1 |DAT|SMR|SPR| 1 1 |KPT|INP| 1 0 |CVN| 0 |MSB| 1 1 1 1 |MSA| 1 1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 44 | DMR ID 24bit, little endian | Unused 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 48 | TX Preamble Duration N x 60ms | Grp Call Hang Time N x 100ms | Prv. Call Hang Time N x 100ms | VOX sensitivity [1..10] | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 4c | Unused, set 0x00 | Unused, set 0x00 | RX Low Bat. Intv N x 5s | Call Alert Tone Dur N x 5s | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 50 | Lone Worker Resp. Time in min | Lone Worker Rem. Time in sec | Unused, set to 0x00 | Scan Dig. Hang Time N x 100ms | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 54 | Scan Anal. Hang Time N x 100ms| 0 0 0 0 0 0 |BcLTime| Keypad Lock Time N x 5s | Channel Mode | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 58 | Power-on password 8 x BCD numbers, 0x00000000=default | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 5c | Radio prog. password 8 x BCD numbers, 0xffffffff=disabled | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 60 | PC Programming password 8 x ASCII, 0x00 terminated, filled with 0xff=disabled ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 64 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 68 | Unused set to 0xffffff | Timezone,0=UTC-12 | 1 |PCM|GCM| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 6c | Unused set to 0xffffffff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 70 | Radio name 16 x 16bit unicode chars ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 8c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 90 | Channel hang time N x 100ms | Unused, set to 0xff | 1 1 1 1 1 |PbZ| 1 1 | Unused, set to 0xff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 94 | Radio ID 1, binary encoded | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 98 | Radio ID 2, binary encoded | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 9c | Radio ID 3, binary encoded | Unused set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ a0 | 1 |ERI| MicLevel | 1 1 1 | 15 reserved bytes filled with 0xff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ac ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Field description: TXM = TX Mode, 0=LastCallCH, 1=LastCallCH+HandCH, 2 = DesignedCH, 3 = DesignedCH+HandCH, default=3; MNT = Monitor type: 1 = Open Squelch, 0 = Silent, default=1; DAL = Disable all LEDs (inverted); TPD = Talk permit tone digital; TPA = Talk permit tone analog; PWE = Password and lock enable (inverted); CIT = Ch. free indication tone (inverted); DAT = Disable all tones (inverted); SMR = Save Mode Receive; SPR = Save Preamble; KPT = Keypad tones; INP = Intro Picture; CVN = Channel voice announce; MSB = Mode select B (0=VFO, 1=MR); MSA = Mode select A (0=VFO, 1=MR); BcLTime = Backlight time, 0=Always, t=n*5s; PCM = Private call match; GCM = Group call match; PbZ = Public zone; ERI = Edit Radio ID (inverted); qdmr-0.12.3/doc/code/uv390contact.txt000066400000000000000000000020441501654372000172250ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | DMR ID (binary coded) | Contact Type |RXT| 1 1 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Contact Name 16 x 16bit Unicode ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ where RXT = Receive tone; qdmr-0.12.3/doc/code/uv390gpssystem.txt000066400000000000000000000020121501654372000176230ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Revert channel index + 1, 0 = current | Repeat intv. N x 30s | Unused, set to 0xff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Destination contact index + 1 | 10 x unused bytes set to 0xff ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/uv390message.txt000066400000000000000000000014141501654372000172160ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0000 | Message text, 144 x 16 bit unicode text, 0x00 terminated ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 011c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/uv390rxgrouplist.txt000066400000000000000000000024231501654372000201750ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Group List Name 16 x 16bit unicode characters ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Member Contact index+1 00 | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 5c ... | Member Contact index+1 31 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/uv390scanlist.txt000066400000000000000000000040611501654372000174130ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Scan List Name 16 x 16bit unicode characters ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Priority Channel 1, index+1, 0x0000=current, 0xffff=none | Priority Channel 1, index+1, 0x0000=current, 0xffff=none | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 24 |Tx Designated Channel index+1, 0x0000=current, 0xffff=none | !!! Unknown set to 0xf1 !!! | Hold Time N x 25ms | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 28 | Prio. Sample Time N x 250ms | Unused, set to 0xff | Member Channel index+1 00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2c | Member Channel index+1 01 | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 64 ... | Member Channel index+1 30 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/uv390timestamp.txt000066400000000000000000000020121501654372000175700ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Unused, set to 0x00 | Date YYYY-MM-DD hh:mm:ss encoded as 14 BCD numbers ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 08 | CPS Version, VV.VV encoded using table {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?} | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/uv390userdb.txt000066400000000000000000000031141501654372000170550ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000000 | Number of entries in DB | Pad set to 0x00 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 000004 | 4096 Index entries ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 004000 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 004004 | 122197 Callsign entries ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ dfffd8 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/uv390userdbcallsign.txt000066400000000000000000000030351501654372000205740ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | DMR ID in binary format | Unused set to 0xff | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 04 | Callsign 16 x ASCII ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 14 | Name, City, etc.; 100 x ASCII ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 74 ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/uv390userdbentry.txt000066400000000000000000000010221501654372000201330ustar00rootroot00000000000000 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | 12 high-bits of the DMR ID | 20-bit index within the callsign DB list | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/uv390zone.txt000066400000000000000000000024231501654372000165460ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Zone name, 16 x 16bit unicode chars ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 1c ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | Member Channel index+1 00 VFO A | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 3c ... | Member Channel index+1 15 VFO A | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/code/uv390zoneext.txt000066400000000000000000000024231501654372000172670ustar00rootroot00000000000000 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | Member Channel index+1 16 VFO A | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 5c ... | Member Channel index+1 63 VFO A | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 60 | Member Channel index+1 00 VFO B | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ dc ... | Member Channel index+1 63 VFO B | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ qdmr-0.12.3/doc/dmr-intro/000077500000000000000000000000001501654372000152235ustar00rootroot00000000000000qdmr-0.12.3/doc/dmr-intro/fig/000077500000000000000000000000001501654372000157705ustar00rootroot00000000000000qdmr-0.12.3/doc/dmr-intro/fig/Makefile000066400000000000000000000006411501654372000174310ustar00rootroot00000000000000FIGS = fm_duplex_a.tex fm_echolink_b.tex fm_simplex_b.tex simplex_privatecall.tex talkgroup_ex1c.tex trunk_net_ex2.tex trunk_net_ex4b.tex \ fm_duplex_b.tex fm_echolink_c.tex repeater_local.tex simplex_allcall.tex talkgroup_ex1a.tex timeslot.tex trunk_net_ex3.tex \ fm_echolink_a.tex fm_simplex_a.tex repeater_privatecall.tex simplex_groupcall.tex talkgroup_ex1b.tex trunk_net_ex1.tex trunk_net_ex4a.tex qdmr-0.12.3/doc/dmr-intro/fig/fm_duplex_a.tex000066400000000000000000000006541501654372000210020ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{U1}{0,0}{DM3MAT}; \activerepeater{R1}{3,1}{DL0LDS}; \user{U2}{6,0}{DL2XYZ}; \path[->] (U1) edge node[above,rotate=17] {$431.9625 MHz$} (R1) ; \path[->] (R1) edge node[above,rotate=-17] {$439.5625 MHz$} (U2); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/fm_duplex_b.tex000066400000000000000000000006541501654372000210030ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \user{U1}{0,0}{DM3MAT}; \activerepeater{R1}{3,1}{DL0LDS}; \activeuser{U2}{6,0}{DL2XYZ}; \path[->] (U2) edge node[above,rotate=-17] {$431.9625 MHz$} (R1) ; \path[->] (R1) edge node[above,rotate=17] {$439.5625 MHz$} (U1); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/fm_echolink_a.tex000066400000000000000000000006521501654372000212730ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{U1}{0,0}{DM3MAT}; \activerepeater{R1}{3,1}{DB0SP}; \repeater{R2}{6,1}{DB0LDS}; \user{U2}{9,0}{DL2XYZ}; \path[->] (U1) edge node[above,rotate=17] {$DTMF: 662699$} node[below,rotate=17]{$431.825 MHz$} (R1) ; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/fm_echolink_b.tex000066400000000000000000000010161501654372000212670ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{U1}{0,0}{DM3MAT}; \activerepeater{R1}{3,1}{DB0SP}; \activerepeater{R2}{6,1}{DB0LDS}; \user{U2}{9,0}{DL2XYZ}; \path[->] (U1) edge node[above,rotate=17] {$431.825 MHz$} (R1) ; \path[->,dashed] (R1) edge node[above] {via Echolink} (R2) ; \path[->] (R2) edge node[above,rotate=-17] {$439.150 MHz$} (U2) ; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/fm_echolink_c.tex000066400000000000000000000010161501654372000212700ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \user{U1}{0,0}{DM3MAT}; \activerepeater{R1}{3,1}{DB0SP}; \activerepeater{R2}{6,1}{DB0LDS}; \activeuser{U2}{9,0}{DL2XYZ}; \path[->] (R1) edge node[above,rotate=17] {$439.425 MHz$} (U1) ; \path[->,dashed] (R2) edge node[above] {via Echolink} (R1) ; \path[->] (U2) edge node[above,rotate=-17] {$431.875 MHz$} (R2) ; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/fm_simplex_a.tex000066400000000000000000000004711501654372000211570ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{U1}{0,0}{DM3MAT}; \user{U2}{6,0}{DL2XYZ}; \path[->] (U1) edge node[above] {$144.500 MHz$} (U2) ; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/fm_simplex_b.tex000066400000000000000000000004711501654372000211600ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \user{U1}{0,0}{DM3MAT}; \activeuser{U2}{6,0}{DL2XYZ}; \path[->] (U2) edge node[above] {$144.500 MHz$} (U1) ; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/qdmr-channels-edit-analog.png000066400000000000000000001252241501654372000234220ustar00rootroot00000000000000PNG  IHDRCON}sBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeDo 25 Mr 2021 13:16:30 CETx U IDATxutWF' ;]J[x)ZHCqP,+W$ t e٢ (jc3B!SU^ׯO? .CR.Xn#4zT5B!H1j111ɓEo F2чto*XG.!JLL  B!H)"{;;wsssS@pr^Iz2YZZuΜchX !"QUа0ɩeib8WrÐs{?n% !Bp3rÐ( !"SUQ69'7 zǏ/K!1 N罒!YnB!i`0X ŌP"B!DjQWcv+h7|HnIvB!HiNFZEQ"{op1L%3IB!DhfW(8𴉢u4!@LB!>ގ4Jּ&Jgⵤ^ilrQf#Z6r)SV3:vBx۴1eIM)mTPB6Q4׽|ʄHLvq;Lt_=2wlԖlM[C~gK/IΨ$]&:S).q B tv|A}If90&"5i?6_"m4ATrlкS*1~`NoY yšW nH)٦#M=JS =Du\â'x !^je!E툥DFā5k*  㜩69)]&aHOBEADL.W~le <v0XV|` Z4mGEس,z6pYPg&xEpW:1gYDV'HH*Yļj V#c",̣5Vp e s?7Issa/ĢbY}Gq2 呯#x5QQ}Y/DxMM$mѪiH2eh$ͻ.~m^gl8`i*GV7ySd7ȃFs+gaw3i2޶6B!g^<5s*/nj)׮UD7wΘ5׉ys-5 3[l1'˄sޒ DIifaJ Ό OJζI)Ӿ76BG-a.qhqrK7s16HW䁵PuXwGQ/QBF EXUVYBڔ C22OOˆ½pIcж-?o|@uE6c9sã 5vL;Lj?}sۼS=:7υQviBMh?{6APeHOB%&<c;9HpBQ][B6O(br='BPaw]RQ-nH{Mk-qB lYsͫ ˄yPDt|wwbY"9/@cCi FMO /[Q>hk}0͉GĶM2!_Q *(JQz8LedY^ '&K8MXև0$'Ҟȏƅ) K Wg%R7&O]EF U{1CY¯q.G(az񞔸&2<}3QJ;n2Z˿x_ondͣѠԨXszySU ->c, %d-TU$k{&} թ(vc̛;kei| 6sǟT4U# DXKaVֽ:iRl('fľ,ђ Y6t$cHz@:.NE[jވ{(^b`Y\ս>ETנFgW- ^^.{߇ߛ4űt.a|]r5^Əmxb˶DJ?Dcl c"nbka3uGnr61[ͣ>-Lsh逿9;$B wlw 5BAK3pkHv dɘķPB S[Gאj No]>q` ׽\.LZ$|m25VPNNy9F6EuΞ&$5Ņ8t%*]_QW1c)n= !Rg@ }dޏ+؋a:3MsVĶ o:? sH_OiOR%KO5 !k, |LH `VSѯvu⿦cd[M?罼*t !ŞCW}Xe".}- BB|ff4FC#ѪB3lX< CBADU9A<8{ D!!>GM9nL+(XƼuYNpc]c I7B!Ҝ8pKu>s՘t !?C%>D)B&B!D&aH!!!BdhBIB!D&aH!!!BdhɾP`PPj!B&.xyfB!).YI7B!24 CB!$ !"C0$B MB!24 CB!$ !"C0$B MB!24 CB!Вsۄ$/'Kvvvؐ7o޴.MXXi]sppƆܹs% }n߾V=K"ݹwR7PF.E|Ο?ϝ;wȓ'O/[g.44ZjuBK߿?lI)TR:t(U-c>s5&Ļg$HטHiOIːBTjZ {IːB!24 CB ]Z򩄟`z6?֕ @Bw^1E-e[@x;Nr?ӕ5_cך\2dԒJa9;PQ>w&{DE^WXhh4[㔧4u;ggqog^y0L58j#7_$\GFҴ+斸h=O^Ŕ#'zF6i4h&XػQJsNJe lN6;3M,Ȝӝ]r):7q6q.ɡ0~Χ8b͏7FHdJz60bZh#Ӱjs3[yI$P?_ aV8"̧oy9b#[FTVuDzZ"r2&OjM6sia#@Sh9fa m\d);:h^[XD<ƨ].mΠ#(ň^{h\ RIñ(l <0>AT4S\ORf'+,ΨUV6s#~(_aPǫ'< %LY7J_uY"CJn2)`]/Ϟž<(' N.*Q7"2kc^|dqv%oV[Ѯ&%sg5O)_¥pH{MUB՚hՏqsrn̗/,&@ Y%Ǯdg&8m=sΘ[/KzM7o>=eH 2heTNFm5f'F/VxAw G|mi1i)#iN3ƶ$tTRUTJʔaFB>V vQ¹ ۺc"^ItȜ,;eblx}׉ꅳ⒃bunSLk[Er$?%LU<еwqúWˉ7Yc[s`W*4'}y3Jf`\I89[a^ 3}Oj (@Le'g6 j `E 2U[ߛA tAƷ-GNL[9LWV?ؼysM,LssmX>LX;0Sa]Llۭelܲ9ssm>lCsYwv=-(vC|6ѧio7ål ]?~>BS W)6T2֙f^?X|4l5kR]@סi܌f]ǹ/"l~:'of/@^/[ɕP "=P;)5a4nƐ.% 7iҫu,Zv-A7 NҊγntV]ZfG}`3.b7Vmcsl"&_!w,Z-9K7&ɴĉ;*&)i!l<_-o,fѤNL)[*ONq~ipcעyz *gNqۼ ʙ6Ȓph\u>:P)C>/N㢦,5*)֑1^'mdp9sfn֔O);2㼨;RoΡ%gW*ֵ̙}P@*թC]M<0*\fSiU `=k9@)=9B918Heh(WĈnH O]'sA*d*܍kc /8:%HZ\Q}cH|Uɣ!GҸpMӯ:{T)nۅo~gr,v;jB#gwuNO ܚ-5PL*ҥcA,Y̱~Saeޒ9c P0Vk\ƒj,d&JӴc#JNowW)6zp+XBRn$ƹ0xI3W#t:ss6dN̢rm*l19kfŕH^87JT,Tl$)msuUsƥYn߇pT9ozR5˒J%&Mb6'Zrtδ&5ohV$r Ol 6xօ. ~ğh\p$K7~6\pqq% G!S~4mˋNodژmnlm|ȕlѣq>;ex^ \)Z(+P@MkX?N:S[=P\wz93M> KcBf<W4"pqwOAEFoa]lzǖ;sˑ94"8ȮyfPP0`zS16VP E<길ON?fZu+LLL011ũj|_e>@q^òD`/vШGv'.Ӱ~V4f٨1u{ճ=C?F1Bx^;ܸ:#e}r[j3Mdu5~ c=w\e^lw=PhA ,F6&3 .e˗`ʊMmqY6]}UKLXafjF\#?2<PEU ~ʝi[0?,jS\;WwTŽ<=߬L`c]ge#9j3.V%(j‰gG057GK(z ^qPM>s3/,lĕK9r0I *aq[7@ݻt Ԁ!k[Vd"΋mo<ũ ]YqdJKYдԫ8i2w[Cgc\|:q_+P@Zi[d*#K߮;NOK1YY +н+<7kRc_,ɨ} wg#]'(wږn9yw/xpkPJ ͓K9f%:w9eMێ& vS{GMF~(fػ[-,M3;p>/4d-ƴaH#&NZ1.K>ܫ}XX3=?fN(l2mV4-5k3dcU߯Ő /KsEOHbE~,osvX1f֎(\ZC6{# XƮ'ZkOyj@ Dd IDAT^7KI&>Eq4g~K6z>]S-[|ή2p}IBj>ŕ@F%EEzz4)*LIIBpqv#>!_foo*˕0ܹs.]:K"9{,666i]gƆK.Qxq v*>P_x1>r5YMHHAAAi]醝666͛7Ky{{J```Z"!O<:L${!DZ/*!"C0$B MB!24 CB!$ !"C2۷o* \Z/'a3wmZ-i]ν{@ # Kaؐ;wTY\hh(jJ2HٿZFPD.E3EsqT D>s5&Ļg$Qfʹ.CSuJȑ#R B*kL4TY !Ж$aH!\ba('s̟&BՅ'b?o 6ʂ#S.!UU_8q"'NSS*v/dvJUi{ ۽_|"пvvvO|5*eTD9#&J׺jm <3l wWҷv2g2ҥ /wZkPdނo&1Gqg~ӗų`mfM|1p57 Uuh]7;b~jD&TﱈK hHwIl B! og8@ B4i0iҤTT C.Е;v}2f itK$(-9䯝;rOULR4mgJGL"#0ҏYvmP*oˡC B߲+:ž9mQ~M>[ H"airʳ\p!v:= ?Zeh)̂-X9Z@wu>O<[ ,߸]43'x{B´a;q~#*= ͔v37aF+{аϖuG{My))53M&h8)kk֦Er蔅<ūѼ2F" ?ǧt\vk-(T6yE(D[RKRsQ\Y4@eT6A,|=uѴ.#{1D]J>;'̳|onQ|?m;n>"0غhgTFN^ϱq(Fc|d[A?{mdVP3,.ڀA5= `n^. fz_e9iD~# _?~3q"̹]F1i@uߨce6'|4h+Cٸ_RO8c6EC>[\'JF'ؾ fV{Pp/r -`_U~3n8 !s<18]!8 S֝y߆'%kܼ&j_ڨ`qk'hA~:q$. `"0H!KJ*i*w2eC8,cLS2Larő}` Tw̘y郶!ܢ7[lw2'Yc/#.?{:xmjZȘl#_zTHħnn޼Iʕ͛888z-t̐E%""դ =~B01Q5`OO`|ĂPNrTٝ|sֲu&V%pZt^BH!,ڲ]pvlG}P}ӯQGf\Ɨ㗰vJfǎq#8)4 )mi3EfӶ?=fu8Wװa&䄯' *3|̚fӍųSVU,᰷.8P*9~XB_t,$_'!&Q'P A6/7U.LY烿j>!~t_]^2jnع38I*|yy`_c"(07NZ5}s7Aw'^Rq/2+M<-qȐt7rf𗍩[ͻȯ#KQQ,'.N,Dmx$ߝI 8))r+;V4@eȗՅuI+:놷JA7\\S|܈|ĺKm2U?rBJ9Ü0k??^L_2w3ms"[{*VHƍ*VogEv͜ ҷ~oCwS{l_^fӳ]S_:nLW;Fjǿ+f L kOة۰6\1AU}>]ݢ+W^Ԑ3(S v`+>|TU=*y?VIOmS,^`֬vĶ6ſ Tϴ{3Pۜ;,5[XAIsoAoFK430h*Ë8cB լmd=E~H?,k[DCV|ۄ u\~mFp{&ũBW4ϋu ֍EV;62i<'SaT5ⷎ6Fb:F>O-UP!s/ (*טhZxgK\_a^gY417t5ra. ? PdY?LwWC5 YF?y+ne?!0s[*T+ZPlkӼ%]>}{wK1?Nw=CFߢkdI)68y/ѷFK&2CY>>Gx*;՜8% MjO0z*-jD9`o.{rUL k rZК[:Bdؾ lݾ LjJ LtDڑiomT/1;Щs{s93i54]i{KT0˕'w%y֑7; b/`~-z֯率rVoEcK*匽^FiۿyjGqG:N AO} CDϐq~g lT,/pó92SE{%KLS1CIamgbԁZ*ѼPAр.$Ѫ*bMF6ْ ,FFǞT w5mbq[s`]>ތMc[=@ğqth?3% ً!{2T<-"ǔHTU\6[= uזRN mo4YScLL!<qGAlٰ.Ȯ՗=ק*GFRImUsUS]1jU]}xke}g4Z'\aS]lz|8D=c7Ϯڻukӿd&Ʋ_b|"O4eSc{Ox# pR d̋r@iGz CzW2RhQlc귀^CvX˞̓qdKK Pg@|˗`n f,nLPR NQn;.Ja\[ד)y(wQ g K$vwji*WaSdIѫ`l]ᒗ^^^x]ψ &5S{Q0,(h759sbsw?Hמ4O'"<'o`>׮`ٴ[tknWJ@4bU b!1Cr9 0*L vυ2γX+Q@flNlԃsl^=V ` ;ѠEGLW'mhti/t$q##0Dh]qRxHg㙯3IT|f޼PrFy:3}!jGC{/K2PGgaZe0+jSCɧqYvl?ߪ Plz}NpR}7jO_>uȡ>+p9 ަ?.xeU|i_ȃ&Ty3Ǫo.r)_ 4;fޔiF?]hmog m6ʌ } R$5{sxXVbPq;KϮ_[۳?%tvT*Q*J+6GlʐєOi\si/butU}Sbz7 f8H f/GpoKf=Wckd1s١ EY쓏>Ķ83QE=ġ{j4u_+:dȝzȕd"5(Ľ;>8T)~'ξE35xzvη@NV\9/!6;)/eKJ?4n#֮!V3ܰ/ͭ[yf(Pz-|WCyC5DY9Cv `JczmƏE?;WlkM`rFOZŰΓ U-p)PU h:0w] #GbdDX\A׮2)#Kg3xTzl Folc,-ne~Z1n/t##vhAfc좑|=#(kMdA9#Ѯj*|=B S~ N+vrqkѦ|\)X:枤OwU5?*WnF`d5z0mdLܾ"BLS|Ojۧ9͵)33A&Uv0sE b@b^[2r\sL4;&toP5LK eh EXg1`Ȩto3V+C~W+8b*ntLеJaR)9 Jޟ1==<>Mb逽Z͹(]+}*exX AbmF+ңu ׅbY,-&kv7,2f TA?:RPPT$>: *Ak.fԯΦ{}d%}ѲeT/MSvD.f<{jfOUiw 0c,}-+!9M.r0#Y> 쳐PmϚ@37 ?{cUIۼt)"4==HgX#x1z:2-)C͊V]3_iEW]fZO<82;+ډ1u>$(֯_Oz^{,a QbAHѰs$? x|P$ \q{Jm$ n C%j ]rt'x0QG$eԭ[oݺhFa׮]dXhz~wICBB!Dz؀|%g1Ĥd=K*aއ H:BϔϜ]Z D&,~$e)~SJw}HKF`0`oo*ȭ>s666;w."]:{,666Q|9wQ%ٓ`ʕ+ھC)RK2P?7ބ֥naccC޼yӺڝ;w % Kaؐ'Oz /^V>K dB!$ !"C0$B MB!24 CB!j ۄʥB$ !Iݾ}V{Z"Ds=%"ooo>C_qppƆܹs% }BCCUVZ!DdooӺϖ7PF.E|Ο?ϝ;wRU>s5&Ļg$QzTqqsTY!!" @HQ*aH!D0$ z!Bdh2$"UIH ڲSehag 3h,?s9e9U5}j@%O1©nb"u SK ytg>yw0WԻtY]*3k7ܛ gD?ɞَoqrN_ς6Y܌LZCf4EQW֕{_G b~jD&TﱈK hHw TUIRKc7m6mœadV>u =BM*3dNv|5m)_Fx]3I[$2*aۖ bJWsEϬ5(99 XԱ!?8Wjͦuaj6k§k3 cah|?FTT_6hHm 3Z܃}&мʊR&ZdX{{{2;g%O4=/7EmK'vvعta˻-sbϞ=iT66XePf|7 ~#)OVXz/cXjqs\<QUWCrv|b.Ex>IcQdu.ѨJؚ%RyBUܟ違Mք# kE!N#N%U\V=ZfP*T1P ݏS}A׵GQ֣&=o_;S,*+W x<̒WV9j4#Fƍɶ7Q\(ݸtU\PtcnV8ONYLWEUVS 槢<ŴF!G ?zfdʨ_Yyz+w㏩2Y@Ř*X[}#&V#>Vk? ki҃ѯ62 fV{Pp/rPslYb;ex.waQ\]~g4 R ؍nQ[ {[b4b5jJݙT'ue{fv9l^x(d A|丐ӋcEj 4S{=7sfhIb$w,@I@aWʹfjflyA݅kzSOm"\9(*ڀ9a,KKO[@$>&$nKu<1αc\:;`1'A[wE'dl'~Qwgغ{\ IDAT+~q<Ì~0FL1O{qp׽y-Lm5{-0 @QЁB G{Q:lI?^DWp$Jb7lB ybHG̥yXT(ŝS6}vgubsgɇoGXfAc82dž˒rBHU*TiA@PH'h7M9#ѧIoVu^Fgz`ڈ [C4;M{<#>[G `sԔ}vsy:/duBw߂ӯGSA l܉3d\ xWrClۈ/NգpB􇞜c\7Žc,Xym4]uf|<3t)cFJ(Q@<.Eݨ\^?PzHf5͔iT5%Z;UçDJ ƫG?ѰgvvxFU#=#t\_>kܠ]1ҋhZrTcT0=ڨyJYo*S[bH&Q,:GQ`aeAD)14CzDއ, 02Hx I˕[{ ng]Sx^t8s$j=פ qNDbYy?0vJ'9,Ԝ157a넡^ #n;>PR8+W_FRto mI*@z@Qp.\E)c7p٣% *όȃMKI#5:bڛwX>|vL_MҫD3^ɯQI)+I l߫Czs̥5 ly:\9011A$vؑ?soPЗQzRԮmcMM $5/\#eZٮ{ZTS U9.>7£vcTńCD?N_F*9/7.M2{1%PT.U/Em&h ܈>;17gi.vucye(QFQ T4Gȣ Ѽ^Zn,unAWehD%z*`+~xw8'0MCT!gYM;Qf9gY>M22b bgϼyřz)NԱ;\Ib,Z(C=6swJ^8(UFfS/݋l*:Yӛ=1=7Mk)s ;d\5J@l?? >X\V1$\ze0!CRq u(碑k)i؏.smفN(K"H}ZBU;O/᧟ZJ8I82'u_kRzFHWҵ ]D2 h`咞8"4 "|L'p=4}#taW)^,e9m+!Q떶:Sh@g%((QDv@NC!nWؔә/9t j`S%vP72geZQ LΌ (h #]+`Z><*ќY+q|` yćggZ /L)ygb(bDVžhla@m5JhLO [Zv3{qu|xFBWy񦳗b[Mf[&8t5 A"9|JƸ-F1?f+CٔmKDug^KrXS*nҖ6 mB(x#0 *yBG 3>hij13T^8Lթ]?,ɰց<ۈ҅}p4*cF#Eb/,X GBh#?IEJ68|"럹0],⿬ }.də( %޹v畜m͊=O= z=`[%ӝ{39[i\H{2(m7PKqsRR=\u*MY[cosddVD:Чeˡ͢ݰta0hv @q4?0m1ʂePC~GгՕ WZ!Ɂ_ fUHrȾ(zlla ==uYJ(D?MKjT)\ה &:؛mΐVZ4-ʚ5Q3 t1 Pԯr:,kI: ,})[L pv1Gr/beH\z[ax#zC#ke0ou0P$!oxN;!d;:_J!ARqmH?o̒̏C4%w pE,ء$x\ Bd"h4ҟV УMą}yJXepZ!Yԁyz&Z 炊l=lj/;zcc&֣}Օ_]s`?%sFz\90iXVzhRJ@2''֣ѯ֥*Q~S `wC+p,C,癶5U[/aԆ yS1I\׌ 24ŬkGMKX4wcm:}"oW_`&qgv8Z'3+p7u& a] t`ANbuڪ$[6!kZhUF*9-&QW!o$ӽg$5d&?iF5޽ 0Q$y3D&Ω3!'x̟fT(=Ԡ'x:V'%p2@xq9.JG; re&I(z#VD_%sKZsS<] %s>[RRrY?eCSRaL5y&6o|gVWiYFZeoۛ __!!!h"-ټy3jʥ%ەwXNh=d@TlZݸ ;n̖+9޲R,k+ q 6w0:fՄ\B2qƧmէ63c=hˏݡR&P}]pwvJαzy14){ɬU+)򘐐ׯ(fpl=X^ Xؖƣ &Tͼ׻^=W`,sp1o`-ǫ2.St3L'.q7*I޴;~vH7<* FU``tJQlZtlHc"4B,S\ ԄR֖vd|>64Bݻrf9Td1bor" 3N'n~yy;oC2!!!ԫW/G1$#( ٓ'bHE ZuB#] !&FA9/]~L#\0͐Wq~ #FG93$3C2O|mQA7$P(E1ςY }s-8@\\\~#####۔###\K>-#"W^ͳECy&gΜ1^d1$####g888 227DC22222224Ő7<֭[ߦg055;;6EFF&Wέ[6EF?Ǚ3g}6%JoSZ`LS,r_/#e˖ȑ#mW|J2xWNll,ddނ;`LS,dG$##̧&Y 7gsH3N6e+up/04Ȁ, $IH1$3UMK٬rP~D222_rvHSKDV$$Rˏ}{9|dC/]Q?})՛˓:R&68:**M7m.PF1GZ$>&$nص]˨Vu[5U4+0΃Kik+xd=58Ŵ$%gM*.W,ͨ|3FYĻ %0ҧqa\uS9f eA(k$IHb$f,@/D)M/av3 eMk~e?I\}Kc%c&p v=uߨN8he.yαсgFX Mq$ȑG1y`M gS;,ՂR`qٯ&U@רR3뒤ZNJ9~0\ZFE ^T$ Ű=02}tZrrx +SQ5ը]׉+q&na9}ֱrpZf57Wȴ %j,F"bEY/kg5HJ4 LCo22y8::~*eTpk^~- "Q }pa. IDATOm[ ~"B5.\~ų^XvU|Hy%ͯIK)dMlO%@_Wӧ&ǯZ` U^ jHoi}Kgvڦ; l;wxwWĦDq 9wCEw9?߃mݩ9a^>ke"U i ⳧ˣRV"IHB1Zt(|m/a0nͺSIɩyYHb5]`^, ;-@<7Dהh8VR㋷k|y3yתBEyg6"^^SuEZ}Ӷ@߹Ko_J)s_TR.IN=/s}ZfG*Gݚf|lSn_sŒ~9<_jI ^>=37XQ˷70*3i:OHQRe;fq-긊J ߅Š}g^Yt*1mU"ƿ4O> dt2cj\Jv(AxbfөbjX{_P/jFңM JYzr'c}߿ZB/)]'+ _;tC:o)Wfh4ܡWOcDߊprviPkNs+҄[2Nʺ%,,B7OI ØjD>p2Mm6_[Zk[ i7m徍Hg=q/f9l4ǣ|SMGZ"S7e0GYs>wp0*0+>`CQN!ר3Q˸NF?ix$˟7fTc Œ t^(Xs Z2v 8>N jGf ʹ}0{˷07W>hV"Sic#3}]xZEp*WI$+1MƊ1Kcuioo=/<+~3h[3 1"oA)[7 <yznYZaAm+S2#...4O-BBB[.(~.mcM0V>;2>^AgVd( {VZmWIHHdGFPgϞ_=_j7'wO}\ Qs4ue!$##!ul|:g$`p Nɩd'!w222 cccDQD9U-#B()_5rfHS!gd>###\KėEWbd7|$r0&EC_9ܼy3gg066{{6Ed>yb!-r0&EC22222y| / Ő7,dddddddid1$######M#!oio[nOlll~"####۔| ~Wέ[P()S&MSb~V$ gϑoSddrbbb:u*2u/k~aّxV)m_ٗr0j^_O%$t.>!S#=ĺ_XwG'wQZLπ_8z/ 5B8p/4>WpWǔWZccӒ :5\ 26Zezf;P\!/蓑ĉm4qڤBg![(482666 yŁ V8W?dGχ5XxO|%=1⏏_Cז101s} 0봃W.$>b91ןnS!LaxYXWyyw&t٪ ƅqČQskC6@[[͢k͏!s+}i߅5s?Q]Delf[4?v'Cy2gm8= φ~U5A+WIs P_[NϡTn4}k[4-J1m_^Wߋ?&>c1);DF`qj:>#Y^~OO~:Y[6.o4;˃DJ `k#7$.L֖} zmϧA| WῬ%R/{X9}俘ڠr7Lbs;$@4︊gՃX,VqﳮKc7zL>7m6PlƑ*Z|F]MԮa:} j&rY?922_ϳ}nVħ|Jk|zc2Nf1h<|ѥX-W1S:Sdj -8nrƵFJHuNxW"I.ޠMzɹ+Μe0T$D^눭<6hH %%f ~u^0Ž iy/b5;c5O/,ރoyr.C7Mn [z$4C2}BoeFtd!!սcl?*W?Xܕ>>tͧrɡ7 ~’nܙۊpƧzaNq1)u1"^˩%q 뱽3KWnڱgLaj\hhAP=I-%)rC1Dj|OsNv>-cir=)TJL$1㒤BJJ lDD E#+ M*S29sIC&&&HXghS"v P S\Q@e+8. ظ@UlK*xŲu:bcf @yGOoKOZfԠꕌgxW ֣%sL?|] ](OՑqpX =5?_䕳L>͇$"(3vRAaI%[W2_7T/V0t7e5/{b{0uo8 ū`'nhxɞ@wj%"dTCx/)٩: 2}Vb*eX+<)Vv͸-..o:1eK-ZA tKD;ŏVowbRZz~bEY|wf^FOt WoN3.9uez攭S'^d%q Ă8(ޔnSa4c-=Ʊ;**9;z"oUE3}I$2{ Z$KYFaN]uoxrNz ˂SCt=QMJ}:dևS2M~IA nPT{Llq6l2=-X'Z)*XXST!J$Iر#}M=M&eTpkص1R9|%E,^Dǐ6 0Čt1{g r^BS xB%Zh ")YI طt擢m4JLrLYeJ +̅8E"=$F8J"DLWg V RZ\eMԈ$&- [:ULo3@uTӦCx?Ə;p4Cm].9u mܓ ?Li]꿒$! h0:L"MˑW:_; B n7;浼#eh3t[RJ JH4E=}B?Ɵ;`$qۺ\9\k:@bBB3D6[^]]J&#Rv2K)G)i2#NC05~]?T4@1*TXm?^խ%~{2N8 $]]߁8::KF(|C'XMM=&P$ A0h8VRɺx3r˔c2G#^aF1%( c$qv29Az8KYQ*UmYrbߺTkd*#_'͙޸qׯ縍97bUb)Xz0.2aW;ABMr|J0fhƲu/Y-f%)Qˌ,c}7[N^]Ӆ2?r[eq&EW>ki,]uKOaR I<{ۖj@9 9bzwȜٓ*rXL;vO{{(7|_J Cj5V,Yύ5+}-3l_š+o<Ċ`u1]C(a+{c_[bø껔bx!QJraCXz%l:ҳc;,wmN"L7coxN;!d;:Y# bF$UA~'w-6d|q?O'<7yaChn^2=Vz/Nҝvnyw1,ޘ%SV3hK&s%Pd_q:%},|K@Q˷70*3i:;Yz?3K:Bu7,)XȴS}4͒ `S,PRz~ї VjXz2޹ T|dtif rrr۹*0.betjInAK\kR4G AcXѧ _Jh0ܾ67D&-L1V`n]勾bPvM-ٲ4-!egs<2?e%#: Neq*y1GG fLy4~cѥ _{_psonn${XCjZgTj֦BQE[j fBI$d#qezjpsc1g^h0MBXۺФdWhpNK>t1;=Dvɐ _ ,،ޝlپ ]Z'cO:/c7=\;ZZzPk| u|Fr_D3ڲW6h4@.* &&6e–OU/ߋw VW |0DD3>,JY8C{`z6R٨7h$ bpwtxՋ/ۗد;ŗ «{'oBi]i3a *@ IDAT-`cr?6K';_}B!r!S|1/V* m}?# rIr̿IeZ-`eeeqULq1Ulô8-70|U*Ct:Ν;G͚5:! eF(ŋ4`DMף( x Hxi_:PׯBPPPV"DaeeN\rYJOTT...-r?UU|2888dz;@-/{!DVppp___:zt:K%B/C!!0O,wtB!Ȉ$CB!0j !¨I2$B&ɐB!&^JXXKmcaa"BBCC9gX[[鰷7H raaaԪU륶9{B4 7PD. :.CBPݍdPWѣG vn2<}9RwBd IeۭJqLY,Tq{Ac!+w6,^Ʀr/ z0`(<+25!'ɐ )<;cJ ]}[~l~_ӿ6נv$ذ-~=,hYD"!BdAk fumJDj{ bM)JY#❒. %ecY7z vcՃЩdVua4]\\Ǡ6Tu,mؖF88)OS98ޔre#JU$S4KKKlr1FMNN![{`m/{1d[}ΐVGQ @5/ϰœrqcW^#:/]V3cT\Ʀ31ER,[QB9r6ٺc>ye|rRlHΟ8KKCZB'5gDUSg8iYM:hgp|~sLEd}L2MV!%*~RQ0̜\7ǎb̛ݞ"i1ׯqTV$͠TI%(_EB:֚Mi֘6g3OioT =~f1~<L7ySGBc6ԚɈˢO3ӵ4E[YSĮ,a%ԹqLeJ )Wō/gϛRe\R±ͺ}ĮD4y)moO▘E(;SVYW(o[e)kk6 SLbd($*[.Z:rҞ173y8 bBM~26Va4Ȕeh,өPu+7+S;(>Kw=L> U Q`8]ÏٽeV#h뇴0SxޗrߏÛ/&u_K۪n)gD& φѴ yS,=[fСfFy'E[|;v(#JRvdɅr4M(dɝ?_L߼i9Gʲ **ۗ8;R9LLAUPZH!}>rP>U?"+~L?~˿TCUSj;,=7mV|_Z)]q4wݠ0?txm׷a߬W*q?&\CL(ӨVeh.'a$ay,+S^gF:~FcWs-?GcQjF2opj[&\RGqk<>-hʴ>\>%*j EPP'NHwy ~u~;+URL[3 5 b1S9]3~5Ǧ2Hl҅5|[yom?liF<+Ш/J]d1~OBOe]wGp/ά@̉$<"+0 E_J28ڛpyq9A;ZƟyv%N|؛=boM .66c\:gLn|u5UUˡQf]lՒؽ6GBTUUxDȦi|q80h,?D]|hӖ)km2 i-wGq yumQ:(&"Qgh_Tis u JݬMD}G|=}3ZΓ4RJ/cΣ{5w|fFّгC?c̙}ڠOqȦY>+&hu%ڋF^)Ç~r&-iFm0AK~I/Bw_*TzmqpϝXhԯsm`wP"2+^ͰIOZy K9)'-mqcRutf?ۿaW g4Iiv*4{9(Bsۤ&S %~[ԥIݼ|}[o9ZhSԉjժdݑЗJˉʡڦXshn)Z==]$oأ܎Ir}R֮._5‘ۉאjE(d_]<.sX" Ue(gr|2<12y&kʔLkƛ?fqG_90}yT( %Eidwt[H {5d_u4&[P,iE|Y"[i>A[H@"s鮯:"e_ʮX[=gG|o.[ogf ]8M$Ӗ׻11%i>JjϜ3X>GHeye\&OZ[禌wC;|e6j|ܠ!UcK(˜zEAѤ7$j(^7H BCC P6tgu(NҥKtXEt:/r94 5jPv|}}(W\G{BpppV"r0^NedBCCqww{ U^GfuH rAAA },dH2$BO3$B&ɐxDUEU9=֖XZZb61juv ,m{ӬI7 !m2]Ci:&wcLG8;U4laxA/~gDU{Q<%*PBAđɸ$Z^ != TR -SkD2.RrUi7 tEbXǪZ ꡽xmٓ_(+bMkJS%f?3d!U>tc}ۺb/P2@gI71 !A*C}v<9T3%__!U-JCFQVǑ]D!xfgB R+'u=hѼ5O`%e} ˗ֶu{J,OirS+F¶ڒ$.$CJ/Z"v0͗RQF`ۚ@%w #ZVmlKW~uI^"Õ)ףbQ%J޷ ل2lE_nILʫ$?+i|jBgUmFQ+Ӭ% Ҷ<}`%>#o=+QڒNn]TQ[&RNٳ')000m u ۹\'.Slԁ OSJ0Nm>)/{>gҔXW=ɧu!_W _dY[ߦQ59!S80{bGl8kf3&hW4&%lq,ڎSiao>/jv[|ӧ5.TeM4}3*ώa)|9t:KV֔0x"L9UK rx"=wSjl"R&Lp3liw{,eB UUٻwoeϗ !DZ ibk ?pԓ>}{к5@2&>mY(EgdʸuLO֍3g)_bWY/צP+N/ȃ@׮B>j4[Kyf22DbցV&7ڇ4v*ѧldz_zye迤 ZU ݛ^F bs.fM`8<3\MgBN8M3$/Ү]T Bu6?}2Y h}{rr(:UùliU{="#H,OYGJjxoEӈcpi;w׎<~&s$M_Ő<>{ƒmRnI~uiR7/s;{s*68X .d`Y[aeeu9F;<\W၇GB Z|k8 TW7f+ !KUnMWhCIyaC4QQU EVXk˂qM=;X5̢=O3qJSC- GTA$C"%BdAV2z}*5]A4arrGcKЧ= [xd NF 'Kb]P {XCّ/ _7"g|9z$8óۊ']-{לż v{a7TT{P@ xiD"El0y&kʔLkƛ?fqG_90}yT( %Eidwt[H {5d_u4v(}!iRF P]|=c-浢T%7F= ٰ|bX(v鵨=ͺ{ ^4vhօ&%ScwۇxnK2Kc?_M|3_G+Yw>Yͤ9->^̐SY9ԓyQuR:4-MW+0sfn:'q }ލɌ,ԍEOQV{)K<`|{}l>ׇ}ֹ) F!H%EDiժ^4֡~%" é_^769ٓV4on5ëę 򆓡hogFѶ*zM\ ç.$B Fz ^%)nzaj}qI̋=/;M?<'1z ifoaڹhV'5,6 5&|kĶβ+]ì(EyYlǬ#*cٚ۵.B!ҕ=Tl1F *onMj;ڍ7\$o5|Xjڧ<]}}QthNrTrŽ~µ/>^n}әOh'պ'{{2T(l(k{;ʹړ?2w7c/Km4|^X3#Ԙ_y~ ~V?zoaG}`£ÓI%AZkT"CTw6aﵫ܏mAqU ƵY >;ϲfL ش$CB!^]Q"I6c)6r VyFD>d^y1[ !)xpp=mߟ0,iGD:'HE tqoJp+r?Mg+1Di͹X";/#9YǷ sK?~轅 BtԋOmh+hJ|? UEQ}5>[Q@%<.yh`c7dH!D2`t,ly"pkL7vCzd,CUU4V[*7#{g 0w5Ѽ2I}zTLM@Qb?P C܉ݴ|5)Z8I5 &{!R ~Cƒi?S)}z(<8-5tC ڧ=kj@M|M^zb(oFLYu.ؚϕ!NҁZ!D2Ъ=_~Jݚ:+݆&K4&Z]ŵc^ώ%;g܏O`Wǥ|ej9ĝⓛdJyyP.ׇJRnpttLSHFƞ3&GXy&.ɧ7~4cШYc\;fuՑgXv*d 4aU/*dմfH) R 2̐ɤ2$"CʢщGމz|xRļ\vpb^\2:Xi,7Qθsv҄`*HZIŹڮ¤.#y<]*hy3vjK^hf(tAT5%{h4:ObJt`EF`uJٿۻ?[7o iwG1lp)y I2$~4;q&S[vd460xveADh PletjDi=&㏾d2Bboi}f m1OV|a'XDS]>kҚJŻ{-s/aLx%:L[h4Exwx zU[JMW:& 9/Pxm0y j2j/TZŚ^LX@3x iHB RI]Y)tEXtCreation TimeDo 25 Mr 2021 13:17:25 CETU IDATxuxWpV)P܊J)Z>ܽXkp VJ!BO$DV*݉B!)>n@1OFNB!>F.&*M`SZ8Ԕ5A&/$B!D>"1Sor2Qi`EmhX>8YCRL.!BK=#gfFLXv&xtv9)Yy|uޘ;P%iņ,Eyt>w!ҙ&QNj`E#6 "1+Չ ח^8Xy巭X?fudΛW4p"I:M_~[YI1:LS#8= 8w|E ߑW)&iҥ(ćHηyaT~l }1:"oދ Xίc6 U%wpBeɸi|[[sCd?|.r|:Vo9G@lY{+ORIEmiT3;bI\U,\|GiE!!1[:=^*kY<hqsE}sӮkQ.!>h"b$1&IdLdn ;Wg,tQO aiNޒ[,E\xǖr:?V25cN#F۶Qr1/Dڀm`bJĎXT ]F`ؿslbo)#^% 4t˒HG)HᆪUBrW (kA_Q!ډ̼t]y@ZX͓$ \]Б.KKwFjhv8Aye;3.Ă+[u}rpږYXuum#Y;M:qs$:.a9lQJ4i:OcћZᚧ8}*.D^iL_wp ]>~MYqq\#%,ɬ x2l݃oU ٷlsL(DLUܳ .hl&lLT\MUǤMw$\B|t.<>QMpbM^E 8 C27FiE+@elBxGӈu[1tΙ 2:ZoАz1*TlXd!-\B|4bƒx4n>{(6 I(M):]8(~B;?MԠ<K΅2]OY^2Y[R4JL'r``Ii<_.]E-;:Q0WP.OׄaeU)!>FzޥpL׫PWJI%å{DGQ%m*SsTT{=QMK~WYW/mJ!"f³4ӊfɏX}t wradrŋOR*ӡYu8n 97?BWct^eQDl=jIu't) 2ܺέX m9rWUp£5й+5 31I@!ɀ/i1ܶ˱vLJ:|Q-38Yu)pʒkm5عw#q*6TXu-JVGm\u*N:O0ܾX 'X6.eѪ 8*$g߅h᧯|čJXEYMܟ1/S8Ii⃦'ElmN-d,Ҍ 2Si 6u $SHz|NFͲQXAS6\\JvMg{|(M4MEi⃦7TAB8:m9cg?W8u҇ĪS6,?z`愅EahTAa7,.oC;5$?Ftc?{w1 5&6$bS穛0|%(Y"'z,&>%$0n_Ǿﭳ~du--㜭x ![hn^Iy\kV2]OEdE"VR- 6mSޖ+^4ݲ"Sq̈́>!1i6OZHR]܉v${* !Hn4H)lx{:st) !ҞHA+[4(2dc ټ$[Bwb%bttp|~),`CHv%>' ")VD=ft4:a-YlpSGQ&浧bu 7`sӸ1!RB!'A\MUg0M_F.E!B4JI yJQ!$B!02IB!L.!B#K!$B!02IB!,p3!Bt+ Yooc!BqppHV9RB!02IB!L.!B#K!$B!02IB!L.!B#K!$B!02IB!,?#>^3v^`N{ߡ(ٙQE|||| # }"#'''ȕ+QWY\PPaD j΋Di mJe':րAwHBw:Ԅ'btTAQ/Ci̙3Ν;888psW3}UM8Xp`Gҳ}C0*W&7醗7Jݒpsk}"Ƃ.R- @tXÒp} qOIG|韜DrSB!F& B|qa,&}"^iZ/aGMK6NXb~fF}߱r1&ICpb4쿇!Un/YZ*`-G#b 2Wo἟'!^bγrL6]x|IiN Jfv%[Dg"I9;t:L-mq]¢_s=33՚c=z: 2ݳ*-/5\E<.1`nWu\{d, ;66mؼ9>4# :^'F&PMXV#"fdp>.ҷ ]qppH|q*DgF/˵@΅K ߏ<ƔMcH|0;WlȕDF L CòDmcf:m _Cmsc}"xOtY_0ިд@NY9y/ |Uh(~i+^ #IEZ\9ë:KS~ǿzm b+ؐ][b\Ąq;CKX?2|MeYtz]acPX}=`iCF,I1#E1wcQvPxUvVZz}DGmbGSQEZ*L+>c~(A=T9R mʥ?s,"Ks1TEJ(8~>Iuq8 n[~j$-6`[QqWxK.'i?*y:OcY6g 8Oe_rjkċj71%GvfDg^ỏpJdɈ^뒊:/aQ'UqHK0Q>Lfպ oji ]ZNU}Yi ~M 3s)-0'`t,`Ja_O 浩t֌Fm9tttD46lڒtҥǪx_~:3f zrTlB,qϖ<tθuNPru;6aV/h߰@Y Ug5/ҨKYߚUs%@q<h\sf'wlVN|ލn ~xř:cfоF zM{jU Ò*98vdefOc3LMSSP76~sV$:L4/bSYNs9eJނ1 MpY 2T2cT4s?zBa;.6A TJwHp,QqoJ4I?SuU6[X݇b%~8U><^`t=|3 *)  SL15U-9ʫ-\c.r٫}0-/v]*V1\)Y7tU|q%4 jVWAUMӲm: nHё!;keW#_|ew6b[!3bv<hw]Pڣ!?  /Mn͛`Ɍ]&Yign:ԣC#7cˆ s0)D2sDa >+GT&xe0cPv$ޚ'SxrƃTy$>aFkw3A0szPX"'u-K&Al^,;|eyˀIx؄pd)^K1DO(2v?ym Y5B8!}bxfsD>] ~oԜxo )XGK4)MYwIv |iOMSp)쁫zKȓZ'7nVƛ,%o޼.k,tU韘m'^LOB0[ Х;Wy_@^YЍa{޻ ^&߿BP6,ᓿӂopt>np<{W/Hni<8]JDD^dz-Yxz@d0. x?ݗMR搩] Ƿj(=;d̾^հIW4U_.>nrzR/=Rk" N(Z3 fE áEeFJydxwZ (|c\3}5`Ȳ$wrgeKwZ/2++W_Y/|Kt;\U_< c?j1[d'N5늗|B|-TZ/ G38ш*75[:C) zjO֎8Zp| NiM "7%iTV]E^;vW&>q1-ӉeWs8<Pns)]IA_O,)o"4wt?1z7Dѭ UX:1ejz >kyL]4\t¥MhU}:3k2ūB+fE2/Z ask1|0 'w23+{\slՑj_hQȑkw-. RDJI5 IDAT̋bhzƗKɐ&}d@Q%fwQ7Vcޜ NY_ ]ϢG8_d|'ʭ'O,QjU{\7 c|r\+R~mUyab>ctm׺j4jM?Wp~F1z:'Jl^T+%)ӑ .AZᐻtWJƚLرSp8zM R5g5DjFVr0QQ*Geò:Ҹ!Q?9}w>_/ނ͘Lڝ%Xg$Gej䊟$]orpL~Q:x6!s>~䒻傂5a %({"nPg:mC]vQvmDɉNc۶mԨQ#888+!dq?2R~Z!Ȥ`ZJ8%-\KyJQ{%r8cnfB`x$62ž>ܔ9w(#("N888~Iҹ™ݛq?CJgS"Lv"Ysr韝/_`FN@r< qEF@w(#v BCC zߡ;;;ɓ)Eg !1͛} >2fT!$B!02IB!L.!B#K!)O<,RB$\܍7xyyPܾ}IDF@@EGNNNّ+W./ W:JwB|ٽ{C||||t-Z}"g6muN>ͭ[tI•I7o&ZaaaTZ}! ?F]xq8`XdмB ҍ>^iB!D.cK!Rb 3XKQ! fw#1;3iPi#"e4M{icܸq7??7&#%\lY\ptt$kfr{VqIly*}~'^^fCTچI7v&tN4*vOElkuaΩNO/.Wdƭ0u_ʥoԳU4ԡ(ʋŲ)+_]'u5Oet ~Ӌ:.'_YɵYm3y3s%r;Za嘇]r>1fо,ړfiVl``4S\cK% fW̵[ .WA썈+M:,w]sZ:=7́J.'ٳqIԶLPT/ɗ ~{ gG^Oqgf,K׭br{7hϗc}Bq3o.5ߗu]c9fewnϿ;{,MMfA,[E1i3&^Z%qDӬ9ܜqɔ[oB?=OT5t*?E"ǫ-[ tHX:KIҥȢWNyKʒh-_}~b=o Q'S|M&d Y`0Nk 6ť06,Hn}4Љk8t>1NEQgSOdž <հpD.:9z1Qvwsjc}XeaY0rxP,˹OWvc̘rgM?zO; ׂT0?TjAZ4s6rp̜Ro"K|l5_Qg8#M!%'\Ǯj O:̺]!V=ǹG*r)n5~0?O<>@޾sX?>]_E  WjXDr|!˻||s8=!6Ͼ03_' 0qb)D`B&r/f& 73im8,aD7jS,<{Nb|3g❥;9Pم{ǫ0}zͫU>encQ̈́fs!ksOΞE=%-vlW~۽W#!f)޾ 5?d>Җ(peF+~L^YT!3{(c^cTA=#--Sx65orRş+,H݃ff6B'!^^d]kjZM53 !{g3*åߘb#3_`^uR=f ?8WɍWy`I#Zc(!iE]ՅU\S̡O>5 ırMǯdQq0ckQL1EsnZ/Ft-ȿ߸^9 S#S}ǀ&yLyyK 9jaT</8ցesYr?hNr?f0M&Spe?[esQmWC=Oʖ-rN.!jt8wϱm P^m8_gNQs9e$Cruru'x쳑Ů UjU^iY1}g&8(@|]hEI 5lRIAnNfͽB'6 :dن6ߢ9K⫐C(Z6f<{-sI4:Tw`n78v"LU+KX@I1p VWAyh)-h u`S}Fʕ'wfO>HqUu˗-ȨX-R+f`9*eq|Gr$;㫒Y?Qg6w&GI cNLPI ݞ5v4F4̓9=!VL./*ӹᢋɣTKDM3#|DCoD>eW09$3/4Kdz vNgdeA3D{:x3Y8+fOC.4O{_`r YĽ 1dMW!FWfd?n#K CfȰP5>2"N&|t ?{~0!O"2ޜ|=9üyxglIKt{_VcC|(z_ƒ9boRh.I_0p}J4'?!* ZunSq7Kn]63oo<q bOtd 1j|-dYڬAU:+S98DG3c4:y)6:gA,Q:d2-֛Ӛe]2$OpGMɚd`b^Mr6+:Ndk\ O(u:IzУ)! 7˚dRdbj*JR[CۿfW:UY:%A[] \=U56XxBQ@gG6d(I*<ݔ)~DqL ﱂ A (?\N:k2jŸYUj kFҸ)ffLH,`!kV칅 tompf趵)LYN&㽽6֞f\Ŋ87<ᑯ ?W7'tz]a'h2ŝ$rI %/&p.ԞYRPֳ^Kg2򗴉πRdR#U.\qU&D).^F+w{xQ.CAE>` p?̉bر ?nc&,񅛐#/y&X9b?j]\ɧsqVh=jiS<Ǻ⦧@TӞ5ɮ=GN:u1c.|_EpqWߞ ֱ gt+JnlceFo;92Ԣ{ԟޑP)VQO,C ЙyѮC M")8vYg z˙3B?kKQ8-(_bK\ޱ_$4 ,8>`Y2؜'S&%lγ{X1a ݚ2R\2j[+r~^StZg mgؘø\GÌ ogYI 6d)f͆"xt@z ǰI^z6/wߝ}L G Cͩ٫'й{vF%}ߋ*24meKg ,$W1dءDU/9đPsNV3d) $>=Ξkٵ?It.KdRń+Uzrwm0n2̾N/b-y|]q\ф3kV)W}hB_dܙH9>gߣW>MqM]1Lr&5AX:p2I53PTytYQᧉ|}+&6o;Rtu8ͱJS^=Rg|W R6<9aԚ &8hˉM+9]%\߾λOW3?T~߷ ã^/d2Qflo7jrB btn!؏bO=+p+W[0Y]h֬CcfjԨ2ZyVOʢMO5.9<(ۤU y[90 eiw$4/:6a/dW([ ]M|ߡaY7wgX7L[{kYxtolh!,9s? Sk=Ck7Cʰ^.O(+I;fjժk [Kt:[n}9!!+VɃW pi0sNF2np fIH_?lk֬? ׫t:۶m3J%C?t7Yݻ>\ [>&ɖB)Kl|ݺu|/] U;x&a=~xq!"$JwB|;"R]oRbPUGGG"9svvv>}}!ԩSٽOQd/rEK2}RBBB zߡpppΎ]ҥ(Badp !B$\B!F& B!I%Bad'ƍʴB$ B!%Iҹ7nzߡ}6>>>tT!,,LJ#W\F_t.44ի0 9::{H( UTyߡ4rn޼i$JQ7H0*W6///o%B!R! @tXÒp !$ H.B!ȤK!ғ'l"ΣO9'AZEQVQZ4t!ũF6|k"uTe1So Lp鴝ʃy2Z`nFj]s*O'gN-gfr6 ^aDߟ<ɖсb0qtN/xf̞|g%"cfUsEyv0owDnG+PB·' ۆK܋6a!&KD=u۸&'\سSS-Yb,Fi;/ʃ2?-GYS@1#w]f*[xIJN}ZEiyzj Z.el^,2 c.C/rjfB+fW*nߏoڐzmϙz:,t@O(oWLmXSNd˝={i>}ǝpױ,-bߡY О/m pzXX #$LO73u; Ŕ5͗u]cO!~&JH'5Ih4i6fcX6!+w̠Ͱ8ES+$qs͜y2aEQ#U9FmK2sG͏]WlҟbT8tXzYG/q7201z 6ō{3zD;<3'Z0gf @>.QvB5NBSƝbNg |~z}|oN1|=Uor/K7ѪI&|e ՙ_'02L>Ǫ6(ތPՕqz\yRsjԞlsq˫1Ňs!̬(bus{R-`ʯ65md. JϚ@ aOi5 A}._:Nk+>3ڞ,#׊jA g;;KwEs Wa=WW=њW绋)hZtKtn< ٬"xtW2^oH]FBct^AkR!Tؗ%6ϱܴ;ᅝ}EqѥE(tE `7v[{5XIƎ]c,1QDݾPDE|?Mݽngg &g%0ӋgŴL^}dc\ruO%p09-zhB IDATgPpl϶Y[HJbdZȓ8ڭs">K.Ѡ;gf_,aڐT"bN LHPo<(w4NEW{|SSBV.X?y,|d$uAA߲͢sġAZP?ɉ4-T7Yڡ+'jYK+YS>!pudDz<2-1J…ۨꕄKKscZ=%tgԞTZfN0)c `ԱҧkOT V+2(g)ra_Gr2Q+k)F<~uyP9 u0spĿ4ٰ7[O)TfcڷX#z? #چMf` 7!w"p[ϬE#hi c,/H8F//Q8UY1 iA:V4fݷ`\}/C`S'3s1r-KJIG)a:sU 6Ux̮=X_kk, ..lWR \Q^ Q}tZjIZxމ],mIҹ%_-h2sX>tצaVK~[m"bJ U̝wmOjB}yr6xGTDV۲5^Jem)!lx3ieV3S"y=jgqQ˛orV[Kv UL ˵4oϖgj 7`xd|ȠBm_XN9Օ)\EIIIHh?WӮ(ڔ/~~DDnL̑czI F'Fz)'hW |7n6c;{PjիV6XSxp/b*S%R=-BytcK1j\;uZ8bl$-Wd2w2lp9$uַf,Myg2brs4_~$"Gadyt*^ÚWJz{,9UjxKENV98SDԀ g.!@F q?#S3HGO\~@i7ͱs?MEN1HŨ׌~ǡx[A.Q"mc5GmGoC(͂(i [~GdIE_F2oX~@I­c6'ZE~~zi썗YkAx?#}҆JnٽeAhd _pe 09ńօK=ؼ&]8'CQwk44fo>jP㧆^(?{.K՝S|lf9@ӤI Ll6l ((=kU iRB},ěC/ fW-(m T1D,Ä9xT̜}(Z)LZnp?Ay@)i\X=ݦ.D^&pL[ZV"Tz ZOж,71=S|%<<ڵkVguATMHX6~:kʝ8^ *^-1G v7.&kWG(s:Cr;vMxiDgpIqQF`3Oa%E%KNppp6%ܹS# {zl:;H [ f{[_,-OǼG㐅\0! $|, 5 _DKh 3gP\ |vN>Bx333j52Lr9jZc0psNNNDGGw^:033CPUP ˗/ CVsuD'AOɉ("##E6t'L\ 𑜝 BJ  KAAD% a"AA01h+Mbb"qqqy]AlXXXP((^x^WEhd2y]ADFFr *JthMwD%&&/~zBJthMvDʼn`KAd&%:&;a" @IS0p GS"JZ =ԊiljƘ&| [+ IG\7r[tٱsqQqt,V1MI>b _?4 ЃO_O) [Y`nnNAk;KҠ$6]ID|4sWzjJ䗦 eʿ&P؆^"I:Eqqq(r>d]1)o'Pgju'tƣRգk~ܩWL_NKMbC];|.o^ˏ#Q,z)k5c|Md(FmRZ9\5ʢXڏݧ-*YseC[Q ;kKlݨP[_':X۴eSV-3 '{3ޙ H5f沷O #1Tέu:ddZ7<><)σ 1}W**xS?d/cRboPҳ6)] aSș \=Eq]VZ '{b?+25ƞC nTtMܫfRm#iT"6XZdzN( HH:iW՘{K\ZI cljMR4'VX SEAkȰ?n|Ĕ?Xlْ{M3!.[VOQɔiXS/_ɂIiQVD9:Ujrl\üKS$n ˮ?2oX\orH~ru CmHzÚwG$:07ҹ?8q12mӜ},n!Cugm879izFr?mtwxpz:3d *)SS+Hܼ}b-%/_E[DQӈmڃ[ElcǸ,< uUޣkD)S?`lXF졷Wp\gnn$Ilݺe?Vz籰FmWa$InJ~lP b=pՁfR{ =[mVNY=YHBᎯ,{`K܌y v!&tkSGLak3#aQJH<n}u*P5~ʰtIJgLW&nHGB%GC6Tb.ͮ./[m6ǫ6+~V:ڙ4c}F?YH*SDDVft]NlOXS̩nLF _qqqDDDdJ*XXXb4pQ*$yx,gBULb>mZ`d%k4e#Z5E:FrKZw0 }[NEW(;ZF/btLN@x}E d GP 3 $IHl(Y6:IFÎaxYK}!ƸmMdd1KqW^` Êx9Ə\b?u(qs弆#IoP^$/O%T)*}MIL/~Lc.ԷWӵߐr("!3q ġF\愆uVBCC3^*<;n vImD]:3k.d6߯ˢ^nK -wANucZEԬl|R{Z0w,o9Tf5t _,WU\?!~X U}Q)SEvWC62-*PruWD \:ezzF#l 0mj߻Z8ժEi}>ǰNR3)O(D*IBP{6F]VD|BY$@fTRީT( ##ŝ~y1u J mmLkI m-P]M O֨&G Ez6XsF.%==S=FU2RoбD4NN,4E@۽>aBRcYi̋פB٬_x)U_]܃8{1`Vm;#Ly$e^7:.)"!i9$cGU{yƒLI2\Y rvv=X2 [.,yYvuOr4Z ߼[֙I]8\m,DRB,ڤWV\H!!}-)-䐮3V'0b+/`xyj? z:Hվ,SPHiQ}<݉>ɬ۞ q`ӾɪxozuK iѨqI&AfQUeŬ7!,Q0E[ޕ6WQKv|w"Owfesזfp$IH"48Z]#[75_+'mfW1)/Ϻ\:K~vbҋPbe0Nzg(z(+oUӣѐF%$.mtˇ8)shPopD\NE\B]nFᏲfŝ_Z$8!Y୻Տ96e"Ѹu >y=Nاl>T ha-Eʇ2a(g ±ikLĿQVZMa44]Ч l4Äc<*}K{wƣ]m#?V g>1 a#z%%|1{bdHE~kݵI؁ָا0j̚_ifKۃd2jMRݔXExOX V}iSGG9.^g9\w´f]Բ\ÊCWy! 1iY"> pdC.>y; z}5ܗGDAmP3}%$_EfEyq v etZ$qSG+p"^2*0 %|V _X<'*G5V Ac Fte=ShWc4/\.gΝe36v{hljxִJٰ՟A}R/38Xn9?,y6XĦM*GP :ZB675M_F^FΤiz86ٞ=j~Җxrf)Æg$JZťrgfWmש$OгcdG IDAT],xqdw#z,gfӲK665Ж\ԇvr呄cm~y1m^qxx8k~}}^xnLm(]&tB3oLT[׫˛h8@Acr9;vxG6333 gPW>NZ>Yf]-fZ \3ѳ.M˨Aur]v}P/,chC+wޖӀ+zߩyt8µqJtt42DFFr ?y" jBA+[,Pm.11[H"D[-666 |Di).A qr 9%.AN~ vuA2K|̀+66)S0hР.Lku/?4&pn--VAוy'y:*ϥ[qqq1eʔgV.ET=ǸdE $)M֘e=Y`d?,+ɗPəbV|dOoŷSב魨`oǕ&|!'O~mk\\'OÇ9n\?][ϸe2]C+fou\+.l>WPuwZb͕OI$I ]>a?c[7', PQ;J0iݔ3#4<2ĤK#ߌc5柍k!@e@NK¡~y.УTN%XdMJ>ք"`OxֱhSOM0''v>>>Ys5,,,rTާk+ϡ i5vuTo}#Q HbG>&!!IX31gRtwb öU25t h h45Byi]#I2Eqt(q?@Q_Mð [H#bQ0{e gY7n$6V!cx C/-pˏ7WѨ@Ϫߢ2;?$G ri]k,r˗r3r+`9=f>VyPpU\9GϧkR==8^o>x zoӐVr\F_P[$FA]@eL7JsGZ ̰baE#kVh9} y?g \w{ j !&}WEڗ>Ucՙvftd&X{}!z9:Y $8PReߪ#ߌK݉ x9Ս hfkGDDD˫Tק)5G3e5ZM ΐ6c\Ms9O,dlǰE`#2:fpJcY6OȰmymﰦc}/E+ 'b86tqqt뼦@nT({:D֡ҨrF50V9KJW ж,7pײ {d^X7 YVLך1k;:S!PoLXécҹ nGz[$M 2],:=TK2!;aQ8*"ofтs~9 R,a2-S "ˀKzmY|*NMYj/@ʮ 277G$nֲЌ)o23NC-[ ;뷃?ANn?Uʡ-7c{կPYq͵q/% |4g qs=f2 =1GZ8ij#MҔ nwg7w - x Q:[ak%#@\)OZҖZsKxQ&Na󌟸Yj uY J)nRi, ]z*P#]%'KgwM+L S{73(׈biʗ89N=7`@I+=ASy,3\i= 6D]KBBB BCC3 R(W;?\u8ZA\LܻVr|A/yPGly&e2I\8s ?bP?ֿB|ε5T`ps'gEMEGxD!EGFRCjB WE{fr-9BJmb?ZֶX%,CH[k. ˛,A2-;<Ie+Җe{hZXF"f]oZJ@DV8`frI-YW@BV2\"9uY 䙖K ...yz(H<2Ut\+ѥ8N8ڮ.Ś7X#ȪL 7sX"Mk|~C_bP"Z /hB)P6뻗 -yT.$1} 0ܓJ>׹vZ븺򩶘LZZU΍;ڬ=zpPsad%i^"N' J^ P}"ϋ5Wxi)O)bHZO0Gcboz " X,pIT=lzdGցJʴb 9;;1Λq&_n#C|)2mU;r=`RҰG 涜I$L<u`#Gǻ}cP؎Ne<ǰ*ޛK-5<!8q5>S_2%YɋJ񝇜cLlTܼM>,כ)t+&qXv5((/=z3ؖɰń1uuM-O yz*'#h8+|&SiGJ qǰj3ߑzE^ЃR^j8֙ԡ$5ó ˷0Jtd*^K ֽSW5^(UP;/phTepcKƯh2tK.I4'WDeTaj %#ӍWrsܪV~s&4XP%$FgAܮ gFO bH?6b]1#3%HP&lE{״oqcWM@5|>c: QԺ(嫖Z+u;M3T&LFFk\±Zxڋ~P'N~߲jDE)|2{A[ָ~D[U±ɜ2@J#k,B ggl'_/[AA W>?] yCzrZk$w"Õ|q?^-|> gΜ\ry]AиVtVdbW> 333HbWƍ$$$Uφ9 GGǼ&99www>N@$qtuuqrrĠy!8 |zNNNDEEI|||^WGP(l}p GJ VGܥ( a"AA0p  hAA4L\  &R]ӧO?=FFFjF Gtt49 ŋk|psO>|ӧOk6 Gtt42DFFr < N cYf}W*_BUAwxyyqAwS??8UA>T\\!M]РSVS~4>mSX6սkjwIAU$)M ؇$vbب9.C+m~x_ CogSϳsO|_sǗoaZ-"  Bnc!d&MEjmُW;**sc_gRROk5qo oMLՒzШP1HvħFKe`ci'_YE/ raJy5Ɇ6i"w& d I 'baj)&sIYSºؤԦ5}>Ir9\r ċ/t:k/M$]d^ߙ< ߪz֜RQԴzc5.v w=Brt ~aYi=F@}8mGCX8Jو(}b)rf hs@J#x g{C$Z݉2ݷ蓝$퓶esK8;yHd s)N By)꼯4iJ\DeՈ.h:.5̋Szүo/fޯ˔ "Q޸Mʲzᡕ̍k#=dBnAIjT N Lh[]D&~Cʮ=S߯x V֧: |YZʗ&J'7gBh)Xҥ5?lU; 3 '{3>YmkD O YaeSUB<$_^tUHٝez-^;StЭ n7ٮki0!tCԹPZQxqlg_)K19b,c\>?On:`?~Ml?X 4FAFtڶ;=zP47 W ަ _ǣڪv,MZwzfTG˔lVr❴[z5 _Dr@7ϯ}ܻr S |uRKgXc|U̾4͆9gzxUv0'9 ol2ڎ8ps= H+K*]}3zU6Hj,aBfu1;?n(?Y@+> GPşZM{21N (e~ڔʮ)LXu.!#9̌ʶ8{P96p6#Y,03"hV*m{/ mXضMNBVXZƵJ3FybR.fU!JVV{+٤Nnh"V.h)1Nv"=I㊵4$"}-Ol)Zmq==aE#k0+XNFғ_rblJfym(hU3g<5񣤽 6~9sxC}Y7&ZZ emofT2V'^d̀7Ga^2uz,qF,"V/kaKFFڸ|Nƫ3m3߃Xn^ڶ˖-[bccs=åAlqó.#RQbe0Nzg(z(+oJOޔq*{c8 IY9WZԥU-gd2P av4įkgq ç CmHzÚ+mxf`6,R /:ovwezzx̮բx "Kdщ|6?t+ =% el\3ůsGRNk˂%6~߸A2=ݷn9V1Qe3Ľ\A$jO>NwoY5]2y9l܂Ađ*zsշ ;iC'69i gѺcj)2RyX9:盇}%rr2˓vVːa0_&xѯ ͑$)`+ rڎ=12"?5$@k\Sg5 fMЯ-FcwrPͷ]QYAʽ\ ꧱5a%7+s7GK׋˱xz/ƾԧ ? ?ዣFTJͳl3 ѲVi83ij#MҔ n1 )KpЁҞږ ] _5odS,y6gDZSw."kPU,@@?t?ϗ,ݟ݇bي[k> Mr@9z>YxFxAִkS6z.^k`l//S^uI4ʈ2 V¢?ޥ1ʤiajI>9 =|6< xNڿaʙ}9Kr/͓:zX$69}^p~:8g!xEvI\pQuRlЀ~v rIyAJWǿR0z/LRMu~jbk&/˹Jkі22Q|&$$IغukƼЌ-RSFT{ư)g'8Z=溶vXVHOX7Aʬ;u1}{mj=ZokTĞ;ѥĪ'[:ha9tx7wPBCCw^Pː&?ߧɛv^V *1ƽd[:Ow@\g#ôůou?5Ɍ\p/b@x{_gg缮 |tdF@aiaZ&=e});.cŽT]{_?punnbKnb$!jMEmX?ZԊV[-բڪص! "1 q9w>y,O\4ecMAU#o}[v1ԗ70R IDAT^Ӳ˶mpgycq˩YlV5e _|׏[GSOi`Z3#dr{DjeMMMQT53IQSbz"{F/oA +j?:%kohMADt*fmE4 ᳂L(#a=)8t'''Rg+GÕvri.]<汽ߒ]}BħdOҥ)U܉B?8Cp AbB䈸W5`/ErFS:r:֢ޟgt_(dJ4(,+y'zswppt1vq:qn |uw`xlSZ|'gG4 ʒL̏^gD)=\Aq(h4m̀cWؿY3&Bm gWf1}Cvf\!tIOInT]2)^-* aقԨB~ =N60vX>E}++Mf%'9\iqSj27n|wX7/`+04-Õ`bCm5*P K(/ʳ#5.ܓfhhp2nFbR3օ2j wXHHv>5C.Q4H,\~4RrӨOy'>^w'&(E1V 5~Cѿb]h5>9c6L>N]X̼#Ӣ9m5ĭq6LƏf0/f=Br_0eK{`Q{;Z3nZwN 5ց6F9) %B!ҙH.LAu82iUL~rٳ룪9:__zb'N!\[MصkkNk CUЛIBўe㦛;"Oncl7olx:EIm*`+ƫBhދR# sIȐBCݺu )Lӱ{nRB! ("AWPU`%L@@@j7E4 sss)M锹9ϟ٨ǡ*.\ͅ߁ )fp~~~(BٲeS)B9'O>["ҫ+WD```j7E'9o:R{>@L2߿?!1y|H) 2 x jB| p}$B!RWz"B6IH4p.rʹ{%hc[gaRMdE!>O|HQ|.[c@םw—KfeY2ŷwI/.*/ҟ_y?%:tV&o8^ !oHFL=[\s9pэS8{MV؟ (ن#vs`kdYGG=BcQov7%-U4YdE"E(^^[{)[xz6*:8f<ˋ,>Ǝ"$aa|F3y7į)c'/'i*RQY}v;ҊpN :ԍ'YLCn@N<իW(s".9_L[:a6jMbX[hv, Sv}>䶦k=z-:ӘލпUJ%?on8P`]xNm / S4N.w΃u!*wX# r  y= ;H~ D=>|W] 5S%斶~ޟ'Fo?Q67ٳca] s4Pϗm"mK`keA^'7::ȣȄ[Y/l߾+_+Ļ L2-eʔ!(((7:P$S~Bv8HUfM l24W}3vL6yRq|6aU.)yh8}!3-KpZU:mϨ^3gcd.@EN{ݰK+{z|N;5݃&Ϲ~VŋG=C'Ew_5_T(Q %)2:y#E, d!Kf~Dz|Inb`Tj;ob)q-Mȓ {?eR9bkK_ ĉR.zf爣cPЀi6%GY^8::lk9WH~n;rt^<^<;(=\.^'(!{#mPY+uշ-9nAɖ8al;-DJ6dȽ"7O_`$wEu$ȶycV,<1`BR5?uh]3Lj?kq4MՔ=0ۦ/ߝ<1G. F&M#G xņf}0k&ޝU{W@P_gGjDnMf!>77Tڻ<Йfg9OJm I/ҒP$(W[Y>~ 7=GlIn_>ճt_,&2۞,yAVxZm|Xp}gBQ2ڙYɭM_bSUJvZq;>n$3fêf5wGG fg·ky4Yl9%nΎ˷a2~4sy1Y )&Хi +V=k7u-;L'Hˆuњ v@ oI%aElzs#y?Υc#R i&x/܅W̩7i;;{|?^+g5O>z;SҷJ``Q["bϞ=ԯ_U?D4D͗z)ki.s صkk~EgmO#oE91;`Bc7߿Ps0CT5ҾojbC61MAg|9@Hd&]ѽ 7oW %=33% ܜZͬNp*3P&|8D|}O&X˂eG\[e7rlr4_piγqXfozp6 Vl̗cоtM5%rOB!{~\(gs n#ܪ$uM`JJO{QFz92 Dpc3tٱGG]/f5K 9˺񣘹WCbW9'SsB \đK7Jv[J{e^wNoƈl9k 3U=/늝wsoe9СC^JW;v;!)Px΢%i}Lr`i5M}ЧQΊ5^HnU}}/ aunsPG6D%&W~{?0|p}~!)?JSpZ^kVDƐ/ݰC(i5,7?g) ,ma8y&BmS8qRq\8D-L΃g&g~Ax5hM~rsG*Yɝ'ڝxT/[E 28sssΟ?Q ިʅ 077cY% EKVWb'rPz?4$3KY{e7PwWx?7*{v@#†#GR0"od3'ڪ#+-u8u??_C1x@J%}QJ߫` Ƿ}K^"b̼;Qyx0 fѪ^9sX#CYـN|b:dfxktW63}axUAJx1mT-O֍q0!c4w ͻ5dNtgx Ld݉0Oc&ԝ1e8~` ཱྀ!;c39p+3!.'+eC/0,:v]e d ;X6쀇uZ2Ef옐M"ёd\cnƱy'ϥGh躜Ń`fx2OF"7K"G'4ౌq&]>&S1сbNcgv2DM(OD[00#2'$+5bqt.9j}֌gDW9H.!TiYִ4ms~~}ד<ϛs%%yB$vv KϘR }wόÖ4XQLݸtJf,YB!m. $2+y*x&Fԓ|hP$&CB!;سgu֕;Nc2(B0cy|B.!HÌmr Bw y3w͗Rp !HJBB!2 B!L.!B#K!$B!02 B!L.!B#K!$B!02 B!L.!B#K!$B!02 B!L.!B#K!$B!02 B!L.!B#K!!6??? H VVVSHזn9.ʱ>Rҷy)@F_Ia*FiBOPlnJrIݞs-|f{6TGZ.)~` zS9BjqwwGӴnJRL=eyy}ZUR\B!RM@@[uCǮ=@N9v8Cp !HUopǣ5 :p`fnݮY:鏦£ˬg{࿠6g>jN{h=WJY ByZx8NѪ@Ul |Wy|=;A(ҴC221~ÊSkAQд5`t*ێ5F`f#F\p !Huù%G__Ge rUWХ=Cr {(o 4MWXd87uQI2kZԬ'Vqeַ\.#<\B!RUT`o :x|XoZ"sjq j _&^&.z:ؽ?x;\B!R낊-jTMRGu+7_^`O/<ۓ< pڔ~DMAWHq77)].d h)8GZ[ T-X1 =1cL11ױt8,*41o,xV|}">ۗU(H%"U| P?rwblġ6so*y0AGyɩ=$/=6dx7iH&3G q{Z#yvrX&#~#,lDk9sQ賦vzmT!9l)^w2C=+SC>^j܅^ٗb IDATÅ?A"y1bw<{;bz^rbzw&%yٺ7Ŗރ)_78^\be <^;V[ŘidҼBT"$n}3|po*n\؝{QfsdWKPl*iB3GytGӥ7w† . XFY2)܇G'4_נۭ1qxǹ(׈3=DLՃߙ:x-|9cPɌC {Zh„ZؕBm+WiS^FŶNW_$K 3๨VcTFoTaw+F]tcΎ73ZO1SI%"U.0x0]z9-n\z3Wry:4hKY )s&#̮"ukV ^&#ٹ>_:lŜ?:~"‡&l^<Qoen|n|߂:lv^CzmƳ{|6"E JT[.s%@geȮ$Ӷle(l¶˗@{~^o.Q" B|LmzuG $_.[ -elO1]foJL, +8䁽P1cgJ-Or9K3ەqtf}Aj n N~=+%?IS6z?TMK:^PĘkdŒ)l(UsPp !HUV1AeQZl3d΄M<`W4 ŲE,T>KHG@q2Cw.A&Yן ͙? @3AoDZ%EPUÆe(?^P7w^:UƓ[5m:=M׶d^^ZT;,pȥ5CN_$gGtX*)꽌k^YylL>́%b& !0^71 KՓ?rѫG9[7GOWb3JhjKȮ;~g8uCG#i^b%)+"3sޚup)q࡚L8tkgGGǸ!?9uQT5b.-Pѳr?amP4h"Jxy9i ܩԎ)^ЩYKtge4]Nň!p !Hu  chیǫrq=뛤\uEכjC}L&wQ"Xq~Eqdoh' ?F%{'~oT:Epqw˓M(KǓ[)ܬ!.|qx&sX EiS!bAVv+AcK^ׇLҁ%[L\\1QLꅃCt;2Qz|jfuIJvd}~ctA&l{Msl؇b>6Yp !TYG*S\>{ X`wǮŸu*}[м]/Ư8JHJÖMsm;[£F]-G2NR?6zq)PE ~YDg۳8-8ziT̐ه Ky3P*ږOٸl+0ꐄrփTߧ3cK:oJc~y]o׆K`zM=s#rKJSU L/BCQ?TE PS&5'/QL3Nc׮]Ԯ];ow #wkϬND)(-ŰC0MgQ4)T#c5*pM2'R!D`jL<=F{![ X$C:32B^gZoŦ?s/mrV\`v%l\B! 3^caa2O)H]JU^'w) !HS R:S "I|*Badp !B\B!F&B!I%Badp !B\B!F&B!I%Badp !BY !B!B!B!B!B!B!B!B!B!B!B!drG\IENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-channels-empty.png000066400000000000000000001401721501654372000223730ustar00rootroot00000000000000PNG  IHDR^4sBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeDo 25 Mr 2021 13:14:34 CETϲ IDATx{|}dc69of 9$ E"*TNR19D!"dfc|߿?fkc{{sx?={{{.y@)plr-"y%@$p 6)Ҳzժo.X`n\DDDDDDDDDD6fĄS͛!GsliYJիV[`Ll,qqq$&&b6[?y lll.^cÀXJ9իVW5051L \DDDDDDDDDDn&xbbcq)Vcޡ?=7Ja·?a's+bo=hD{Nڙ1::cL<Ǫ}6t|wquhȷß}7ns&w扶`4fuVK/߼ܑmZ/9w  fNJ7%r{8DDDDD+Z-""""-GboMyO0O] $g~9Gs1I1.s%L +P H炐fMXGslqϱO_|UPDDDD$sN]c;,Iܷ ˤRr[DDDD^FcW0WlNo$^m޼'%o0`bqv0g_fή,>^DDDDr.֘Ew!Fw.tkl'=`R.$%EDDD!x J.¯ױK85غ,OъH~wK&3ch0Rq;jl'voOe`/!3ʎ""""2ĺz]&0,E ]bR^κ:`$fҷG<Vzl">iɢ zWi5*!Wc%EDDDe걯1\HxNHLko 7uv6SDW"""""y(4ȧa]&vF- -@XB--Wvm^XN Ɠ ߳8uMkt$XPG8x1nTDDDDDDDDDDD,2#9UnF6iAC6V-_`v.([o>ߞb:*%:*0H"q[^ijt/3lsj/Miw/?^KERR*סݰzEH̡lU\iW\KWVgyo_i"""""""""0bm3 $$$&bXk?I0Ek0~yCW`|/F' Z(,z o/M/2/h2~SK8ciQz+ , ɐYXx:#mB<8^s19a!O{t-| #fg'T>Y5_-s\;kIDDDDDDDDD%]ǀ HX^+a͗vRL1Pכ{k0̈́wE k.XlYy$&B 7Ϥ ['`Kլ>4wд~tWU&?n:[6ԼcT9l_GY*CMy; EyV|'6=X1M04-VÛ= ;,nX/MQ*`Vgl3sYңit(Nyk%o?(ċ26a6]#^_7viq qfݙ~]ˉ'%f&3E?^Caj֭{E󉅶 lta/ʥY\_'3F#l6ъ+&-g0d `[mXi a*s$>̪<3JVsBqsI7M[Yoy\էj;^ f`W@ʥ^BǛN|mOʴ(N۞# }ST-ĶXO'Eqq=ktF!t3JO_<ct.OY_yvwb4YujcQ7Z[#zr9ǫpof@8ԧw,0Jچ*~f/uF"b6V?޻jiIox)eK?Z'/ $]s>]q4wxbtꎋ䞼ȜfJW^% n5f%E[Lbxѷ Ov;;252RD /_tz9Jvaݬ} mx5LXN>\FJ6T?mbq<KeZօ9ͧO襞tҗssيk9ccu?WzҩSwLsגVj<%)>:}͢e֎z.F"fmČ*K92<+}vr[DDDDDDDDDD$ʒ=CeIDDDDDDDDDDekiË/Z3Yf"""""""""""8m2Ts[DDDDDDDDDD;Jn}$fٚqXL+EDDDDDDDDD侣䶈w,.K&:::{)Zhp/䣏>Hxx8`޽Ǐ=4nܘVZQ^=J(a8"""8y$wfƍ?~0J(7[///MF)%5',^!6l_5]ve/^NϹs5k_~%ݻw祗^=}^xsdBBB>q/X ۗ3fd;k3X͛r ޵͛7Sn1d sNw5ɉvnٷoK,a\~=ö }???s-oٺu+!!!LtFhѢ vBF)%5',^!CϞ=Snnn,]FY}1b'!![[[}:+W[nV+Q"맟~bƌi&~gfsʶ"""ڵ+@aڴi4hЀVZY-ݻ3wܔvvvjyYN1YƍSK};'2z|(=:G899$'LOFxbΜ9'O'ѤI&Oȑ#ٻw/gϞ?Fٺp߁rnذ!fƌx{{SNl֞iѢEHbmwLL ŊsN}A鼱d_"""""""r/kfō7Rn*TȢǤ.1xlPN{]dd$gSJܹ^*Woؾu2vX8{,֭[nn:K1DDD0|=hӦMFɆ 8|01'O~Vg4W6l?;wQ?M6^zUݙ4i&M~&3Qr{߾}4nܘ4-y򃜌IN:XǛDI7mDTT͛7AYz|F`ɒ%ܸqM6e9}Imۖm۶eرУGGHH[nɓVO^%K@^ؿV,Z(xzzb49y$;wիiӳg\l6sqvAdd$NNN4mڔ*U`0ru_Ys&"""""""rQr;~)QIUo%{={ر#Y&oߞ%K{lŐ 5jԈI&$ϝ;ҥKS/^dϞ=Trے0Pd;-ZyѠA\\\0 ~^x"""R \]]s- Κ5'NCR C'+d/,]6+;+\]$KbȉƟr„ ?z'/\w%Zܸ~:K.Q`,#<vݺu 80Mx׏֭[EbŲw}GPPP+VyOdԬqnXDDDDDDD$= n uŬ1';ٿʪԷ%{5pӓ|nN-::[[l%!iUyZ^ĉnoӦ 3f̸I۶myS'N^_~'Of)HZ$mrb;7K> sŒXӫJ޽:uj6euׯO&Ml ߧW^ >|wyӧOI+xsQ?In IDAT{;v)˚5kx"9)H~׻k4iժU㹝ss:k_Oߌ^Kׯ_Ϸ?ѣGs5&WWW\]]Yfٓ8 颍d޼yԫWŋI5݃8p`D/xbΝ;hu֙^RDDDDDDDD2ےk׮eI^(V}bŊfvQؿb$&&̨Qعsg2n8Ν;pss3j({FiQbL24j(Kd&ys?OsϚ僧APPÆ cܸq7C pvȑo4+V'x%JXe_""""""""N.Eb6KZMXK裏3g% DPPNNNzU^=FM~HLLdǎ 4oޜ'x PP!nܸٶmNJӯdb= &nQlt ܰa8i4 3ɉ>>ۖvZڧ~5i$Fɵlmmҥ &LӜ>} Iݻ8;.̚5kdĉT^wvv^z`}% ے$Eǎ裏޽; l6L4,XG};1OVZK8::8:th',?vvvԫWÇcggǜ9spttdڴi?3m43gK?LHH2=/q]>4ԥKRnׯ_&Nĉ2eJOڵ"""""""R$ߖKNl;;;kRjUz)nʞ={8v7oޤ@x{{ӨQ#Zl &Mb&}F:w_̙318::2l0:vxG=f=+VdԨQDGG[oqY6n?3<MO_P&My湲GK=puuew|[ɉ?˗/l2ۇ " WbJD\po]b &N#Gvnnn?+WҫW/ (EDDDDDDDQf"##wtgYM2fҺ` ēY +GawKXҥK3+Wޑ6\z5Kűj*f͚ő#G,l4yG:t(;v>K!B!FNc bرhтGb63|'WzuvޝiY7od˖-(Q__ߔo&}Q1 2$7 vMXX-[̕o weƌǧٌ=#GQɖɓ'3uTrCܚ""""""""Y>>L0SNw^6mɓ' >xyyѪU+5ju!{] ?&7kng&tڵk+O|L2˗tR\Bɒ%۷/ݺu#G+WI&_d.^BDDDDDDD~eIDDDDDDDDDDD2eITADDDDDDDDDD@DDDDDDDDDD$""""""""""rQmX޻w5퀀k!""""""""""b1qqq֌CDDDDDDDDDDbw""""""""""rQr[DDDDDDDDDD;Jn}Gm(-""""""""""%EDDDDDDDDDc܋N: PŋٙJ*w("""""""Pr[D6Nh4ߡMʕ;yd)}9n޼ITTP…)\0e˖%<ׯ_Wb ֭˶m9u8=yJ="""""9Ν#!!J*a00 ֌Kf9~8#91]H',,E>tsZ뼡q{ܕz<ϟ?{~$""""rO8}͔VL&5chݝǏkiHӧOw(4wwrNu74yOcPDDDDDy'ϊd2a4%<zNaz] sӘTi3f^*I#Gc?40^yC4K(""""b,%EDDDDDDDDDDJn䕸#|9fH2ucŽDDDDDDDD$CJng|3la;EƗS{by_++|p>7kΌ)?6SװΥ墹z[lOiQozZwYX@%oā%s~3lQG 8V ]G,hl~'bmfBN'unIZ+[f7୏q&7Ti2=:ܳ܎=Ƕ9L:T)NUiօA7On] !:V{Y<M;1h^0SK8W28i)=˺閛g7&D^JEćs?~>O&rr=<>Ͻ(blO l*cwFww<ޛ'֣S<5ZĖE&< &>I(9 f|`߸1;re Ƙ-l|Syi84!1(i^15 xCtm[*M0wx>[_!*M3n&;U z'dt8TMym2~h;x _ikz# kƾ[mx88?2uej՞mIh3^}۶C9ro<3`ۯP-7f߅"Α_g~Ǒ⯅XWVIsxr`|f>ێ_mӮV؃7+ʼng8|i_dKXۆ[PV`F(Y$斶M8]eRGf-A|$z_@\Ӂ\3cNE)GuK̟?1 Zº|ٹXȊkQ\o`ٻAtk8m;,|xs_M*L2eUa G?c`fԫ^e)W>^ZȁTَxr%>ݒ+^zYf&Bӣ,iwX΂BHwAB`\:jc1,j݊U*PjC: ?,J([JnbKg,{PmIb1W[T{YWï(VKB7&КVUiBp( _Njwwr ?:U(_֝2j%S_1ۋ(ɣ}YZ|%O'$-Ӑ qwHm-SyԯGfyf(>}z⋵MmFG­ kwֳl`i_ rWAiI77{PuK%1ꔩȩǣk0ϪҢ>+'jO|4wlO$.R0敺ʰtA|l_ x&1EٺMKGRLZ̅D3f5Ήϳh6GѻKHGnfXh\,>LDlO/jX# .޷: $ fڇ,{ĵܽގY2.G i0AHV[Gt>V20;fԶ<ӚS7s>ɷa<Ȓ|Gf kN%#ȿse uݘx<֢5OqK;jg#yʿ:<ӂa^tsG,eytte3UeV_L7) [_z h֦;WjEu{(cS@:Ľr|fKnϷ>Xf7֦acͨ0%,Us"g⬡)gَx"O?74Τ&c#NYwO|nj|.dj([g|>a|S<{0a[8^c<'p0,~qy%ˬ8}}GADDDD`5o,_~ 3ɪ8E!oz880W3s!~5֓? vD^w? ޒa:w p|zV/DL-U04ר9cEoÐ* X4/]mML0P۟ry3ESi~'T3O%cl;ǯ;~`,z8\=7S'0_aI;0csWFLMb f#WSj^˯~])P 3\(G^grSjS(V氍ҟz1O{HV{ &}ycTj܎~?FgO5S#kvf)bKL*CK^ؘγ|`F#f3nR \ .t`%ف\v4Cy̬^'#SQf.D` Oㅭ҆G܋tײ9[6rD3&4ǕkĸVt/ta5zs!%#سh"=}߯%Dѝ }Lc,.cԞt _8r l]j*ANJhd+?t?ߡa-[ .uh9m}ʟ]Úwv{(sPɏ6-a4ÞJ~kê e귡U` [yS>#4.A5 _n$sH8q6O?ŧ,nto3>q^ͩ5L,O]Y†I5XpHu=e>j[ y-oP\/TtXT-rNjcsz Âe1\$ƻ,;}7  ,L=8BNEQDrs2-0:Rg,gE#m]}D N  G3MtuçIuڍϗId-P 66E)d f41f#%+WikFupW &N|#`A+Q‰14j{>unL? FB̘ɯ\#GuvyCަƞyt(z{PGLr`'sf3γ01?gqD<86d:q?Ĩ[p#v3GRbn 3egR;Cq7 ĿRfLWukGDN-gX6%8&_NO[މ-ie`xTߓt y)ӂ@€? fs[rbu13:̗wVe0e%B׌51(u2;ܔxobSՇe3Z30 @=icfoxO&07 mʄ Mt[t(HG[ԩmRAo͖>q9Iv$^٣*U&g9u 1-MY E=*[/bl_)P 'Gq/MicJGb ^_Wc^,ǥm`*} hšYwW:c6@Sj7M=ifE[>Gl|گa͡k!' ^׋ͨ`_s6h,@]ޫ7]=o65[eG{T 4+!}zTeɒ2G o![&q_T>=ToRhxSQ/W-m͆_>ߑD[=եVihsk\ָ Р4yO :ams$ӟG~[̭~gӾ %b<ʒ$Ýlx>}XrDpz,{6xxaEh".ftaF[*zp5DǭXNߧ^50bg|CZO뉟3 L,qK}+&4~o^buP2pBX(iSmW/4٦4/^ 6ݖV`QB0#\-ƮǯT4v2݋aN %2ӫ{sat:P7^+s\ qf9cm4r0gC΃Zb;ÚquR&uJ^0&"/~k @?_#xFN/K_3q!kj '^B?ĹlSۖͨp&\bYݩHLN>,܈Q~ώ3wջ| i-L d}`l?p~~waQ-m. !)m * (bww5Ƹ굻&6Z`+`yQ93;9s\gV@ȳb IDATTcèϓK%>MO(6l*jL)oprي#{l8 Eu ״Z6BMȕ<ɔ:+Ufr7^W'klvLjJQޗ=0;s K4^Z|?YQ]kܮlP#͍&=ն.|'d&sg Ռ9ݼ sg qlom `Hܶ,mEYGyի~}̵zM)n+%)gmẘwq>.g^Kr{CP/{RHq,\N͏H/#y%-: *l'ױmbM(o ˋWO@ @jLdPu>%M ˗|Q$3ܨ='rx+'hc9)R#!WȑIWn+ˑ=!,lÊX=Y7y2Nzssk7&% Ma7k\q[W׸'{&4ko, >}*RK3"ޗYhIɢ~KNI=_zm9Ϗ=~aA_1U-ϡex'CARKnRqڈG 2,^&7tܶˬ+ItW:m˾9~*cy 8i" UU3;s6Hd.۷^ź\5Gerb~b8aggOO|˾x dz) q)MFE2cj}X ;zG>rppSzoWQ,oAȜl8eHj%ɢcKѴ#* dQ@ Aڐz4f,|;9&ơ쎫' % .)mRאBp潩kX $w9&y)k{\1NS`N0EǽuTu"Wބ7=mb\C>Y{\{mvZnۓw =b/in5+32qy^Ds 5`Z]DZ97\f('ً,UVްBuPiK;FF?xk8]ʗ53z=`bќNCl=u#ãaK,VR{r`\ܳQBP{`iS2* bxjt\ZӸL>2g&gg?iؙ2 T]+~cfp;`L$IB5s"کkp/ܒҽ{A GJfu1Fެ^gˬx9[ pѳDvi^Lqr]1baVȄlzO}N˩#r@U) L|8@`+:O6hGM(`YhAF@جfi߁dR\* M4~SS' G:*ӊ3Mκ (hN%2aAh掹 jFϒ3pDLJx2Җ\b&idXލuGNʈ[ioI)G ݇Uň> Uq},O?"Z- & kL_F%Ҵ'OrfS,Y0/:5aV3!9M*'Cp ^ǐ>Ͱ iCY7+dEj(QWcn`#NufqF4'ZRczZ)z4 B S%|~tcGC|LE/W6,宕4>EV۬Lql]<`R D+-&99Vk[r۾" Em&#a?Oxڥ, ߑ/oka>:L#{B -̛5y5+ Bg4};ӃT Q=0Iɏ(ȓیЃ È 1SbhI`;-yU@ ~REfĈ-9%[eXgPC^jˑ[6CQِ@K6-n{|5 c0 Cg ĕ]OX`T6O~Jh3;pQ3{!9r)h?Z=?3擩R"Yٛ1{i8G/cd|̘ G@:fׄ2b@hE},Ԛ+2cjFlHv9Pʟ%d6~&Lbxgg'R."$dݯ S'>4_H1fWڞA/x(LȢ7JLh02%ssz4\GV#r5cdNdq1~Jk_ 敏X'oi4 Bs8夊wќuG eLdDv rGobZ! 4Q,d]6 VCn x{t:'?KF' ךĚհAdz2=m'vRSxGzGd?8?ݎ~!}?ؘS. ^a ++ Lc|:BWruYSvD8Qk~ x~db Az!H\ l9Kc뜨 hhMWX:Uc6\× -^NХX~>Vb$c͌`v%MwCoo8$HEי Nob<}2;aJ;7iwfR13`e27Lqdn~ynO)`5,İ2v^Jq݆ܰۧ҂1]R~@fJ-sKW1n=^Wa`HfL՝*oI>̥w,Zq %6dGE QMiE^O+ dr@ 6FE'Fōe)&MLJP0 ɿB :2DɅũ;) :D͚5Sߘ7iÏ.3C[3;9tGyzqECC;Sȳ@X;@ 8R>ɛ_oxylfx}uS©<i UKY Dpv8פpĿg]7.d6&9֌[nu+@ !&qԯnsdT{ƛ22:tS"Cʧ @ ͖ՠ*yEk ڞe,/ޅg Y<ܶ@ @*|m$Ib5Oq'}8uI\ܿӻ& uɥW?w;P =7^Buo.U:QsCԀum;}MB}H(d8tiQQ'a߉o'ӖH> 杅"KqZu0kIyome\$&tR}r;)F%f3%C ړ3}D)ɤ1̇Erf'yzq"")>}.2e4cS+\ Qh:Z19!"+H. /IR~1 _"kY_ЂF~6)$ bjdHC#2Zؓû$-+U+S1 ,.vɌ<\R>ZIǚX|52<&ScCC L0u3."D_Wr43k&̬pt+F^9B׊ ]5d̟tkStCLlɑ;7ζF yO4n{SoAIX`fg U_$ ]#FzӢN6fZճZŵC&]uw A<8Xf$9䓼S^&xD$!IJͯi^:}:6-ș1;iwϛNlOB937!#n5< E7RNlK}eT<]V' c 146#SfWTiw_4;T;5&dCر:T=w/ҥ|-yRzG18/mҷu)}lWqWI4mK"D;p UjJە[}@GO_72RIi4˩RL C, *޿|I[{VLiPӎoE}6:tb lc꒑!/ IB|]qn<յu 1hk@dn b׺XHRC^|ơLqE9g7`BG͎C)bv4۸gϮ$b2rP?* o_^l/oqb&v+Ǿ[[9:&_]'S>x7?~F{1 u cv$-LՍLv3e/mg,JńH7_̮*USUSō)5Ō "_r9ffR?&ԇ2IbT-Tdܒnɫ;g9zj!won:7IDN ['0];5sslj%`Ń1L.E7'S?g2r@ncLtAY0#L@yysľ=ۇB[Y3fe5d KByp8gFFjIDS^a0=moT~6Ƒ|xu31N N:rJ9isCfG,dƬl+)9IP~.'WMahݱcHnњ/M3R :%4~*lWuB3K!/q6:o:ےPbQ?f}8wVzXVb3 4+]ܙ175ɇJfsu>XGEqs̄UfwvXB߈7A.h'#ՙq3џmYzŮUϗ{+32MS5_rY`aBI!Қ>Zڒ÷.w?7z+W3|)P*Mz3o|?`ƻĩV口mcDx ̲SHQIE(l :V- ػd+ 4*IVkK|r0ަtuR$=fX+&#CB"ty-,,.VU_9p8uˉ pj)n13 rfng*zg%|iW<^W?Ni {n)ed8' 0Όܐ^2CîGKAϓx5]q/"䶒xQO맱q {rc] y}9Z刵yTp f+VXgVQҒ5lqZadd}+vrӥPNHdۑzQzc:D†љTdn5Yw_"$I,/u* }84SNEUTͺf6 3 IDAT6UEdG//IhS"*֤ykJj6cglX+jȳ`dj*Wҥ6^[w66chݚV}{SVGw3W jVB&m5n)sD?f6/G!G̰MQV8y;C'bj{fSK\5dֻ| > WvB,Xc딏$:+00b*d/0r}$tdpp6zDpab;l_WTL8t6{!gwgf,q.\~o]}6;yQ=fc4d2i;mIBV.3Wrk\-@*Mp5}e< ?l"Ӻ7JTi'byu=' Re( -N|J\WX٫:2a)+ޕA)>>&O,LMCs7 AYdnE 4zz/t}ksSDm|u0~;boMV<ʍTW^cf-X[`0!ěq5w7ϊ^ 6#94K. YmK(%G|'㿇9x+50?C[Em@Aظu= Tu4|zUj6^-Sc+ûtoH?]sբ4 INxMCeEh>h.`ݙAyu(wb\7 -<:2e&V( =cmgL jgyl۵Ҥ)3n~"2I [`. j߾3d~WGub9SL[KJjIB~¯ʒxw/4ZȻYse!ui2uW=5.KEO8[6ff]/ĝ/e'GBDb9~a26mPm}^NEaXc$6gTgerV\~8%y!Cjn9?^Giu".2VvQVOJ$x?Mص}DyʶFb+Ǥ+h6~c^<8zoձ}l*o3QetŔM^?ѺWhUr/=h[d|} NhHǕϢH%*f'J_ ˘إC|q(%K&&d /a*/өB#= ڒgR|0-ZB<6]{|B9-ObC@ 7:N{KdoU>z.mEz6 Y%}~<H)V I$ 䙨9$gϞ6`aYg>+%dY2qLYBftd̩D[LAg:V9 Uxbvwu8/OxW[0wVNיޢ%3c}z; I2%ygãyBq(V 4*F9UԈ5;:yhlp \Mgݦe j>xfnN6Lkёt{-DzlZ&1 ?b jԅ %y,gϞar1!ҍ-,=DkR(giUX3o(m!#)ą;M}H$5/Vw̧CGvrlʺŎv&J^'T?l|zǾE׶;`y~͞Ȱ˕ 3Y-:ë%K39ϟ}՘0zвy+z VD;cB[oppI'fN6E=x^&c|$Fi63ͺ$RIi^Cwٴel=r틆& k+i=ͤp崱}3-[ǰ= %!,^zrW2¹26UƢDl\ʠw,oY?V?jN>$ ;)FPQ,ܼ[g4#c绨sc mI?dFȑ>&w%'{zĖ1._QRE:N"\c ɒW|KV%`㪱2E׊y3zduIɑضs ;n l~Nbjy(iDmKv}:]6b/ӹB#f=ul\9>-q֋X~3+FUDڮy~S,?)֟=oܛ>;Z~.$JezTRK Q)(&vC9Q(| Ad(Uj[Q$Nr(V 7 әjpc*oĺ])aQpZ-Ed|or9**e-vԱz6+Ѕ}X3g]:,L=([9\/2?ʶ1'=j 0Z.qq Fqvw;Tj %'&ݖEcXcwͣU(=PoX2?Ujl%'f1183NPq7WvWU?3Esrؗ1;^ b>=XɈf*R!tP }AG0JZ\h,kCK1i8ˀ@DPLҞ%5QCCаF!8r > yqWMG`eou$qdGn^#@8&qL@ⱡ"F;9{R=ِ%7YuJ Q࡟%w"-&FW:(bh؄MQ!w-?aπ Jѫ5K cL=4\y kWU2rw4q@T&豜pJ)R)|Av_J,YLo|2~ݫ=ލ:|6"B2 {#/Pr;QMA9a긁y;zZ-7_IR6STjv{U߂?%Oɇ8$c'm(XB3Ćқ8q=r$cj"E-N㐧{I0w2ZIK-_3Yw֔r1֍}.KJiLdx8^1cH )^%|J=| +DŤmx[tRɾ^9ɡ-6g[EV)A"ˮ0ʴf)B+U=,a?d"O})mVRRΒqy[I&Gۖ%QRjUra,nܺ{ vRb+QbJАI49\xA{䮗OGG m-45d RA΅K>yVt-''Y8~,)fJ=6LxccF5^^i*-:VJQClYcfnS^.SIlZۘ9f(^g߮߈lI$ dɕ`LP~9i C^t4ldTq]/!I|8}(SsR)"˖_ƭq#%$INF-)%:wk_,)o%\߶ɱUbކSXcg o߆ƓE9ZE$~n[G҆W>5x*u5\XN&kC|Q lСH ݏq6fR+%ԪdJIk %lµ`w)GWb?wZFGtReїe4$dvwb]:&!~w%jS;NY"EKɖgѿ1mu+q(N)pﴐ=Г$ 4d\67r<Jl 0:/ҵ<[iio Y\51Sy+S_#&5.L†pGdg!'O/_:TCf c |s@Jv}:643Wٖv8oN9bס1/qaZa|w qԬn+L(U26N{y :6L,QC/ZИd&٦϶_N&3HmOۨ K,`x*x+ %s9 PT6z$3] 8QE_Qݕ`~.m3׉#3BnTȔ ěO h@dn0=9,5N hdrd(L*OhCO_? PKQIk=8TGРBCe]8'~~ 2~Ԧ<( C.{Ǟ`(68f2K} 00Z+ FFݒ4$F%%ݒ%q~⌙:i= XZ=g1i-R0dL~[nҺK^ ]LA<9xwTsOhTܿpPyNrLʢ衯ju0uk9pFƻ'PBYij_/a/<\0^gUc j$zj >Tą@zɐn;~c#FШVPpj)z*!ޥT՘~0ͷ.cެ1ԛ3򓶳Qoj7LN (/\Z8h3гrLiޙ}SOO Ω~-w15/~=٨hCU }.wU3~8vsK~^"k^$9;ŗ2Yd rlo`ќY2ɍpnZ:17'̪tҗ2gQYˆL9pP5>Ļ5ګC}=FϧR+ж݁I1[>k  <Ҧۑ55F uNfDZtE?Ө_tYSKa͖WTa ѫuE(?CqnIi0iKMFǐkϗdJ׆> }VVy=VDTEXjQ{0SRLدȏw 37啉9Fv|Ku+xm،zNWY{wĸt4P&xyC p\ʀG.sr9b.+DSU[fGq#V=FR)Ҳ/qs@-ǜXc6iG+=p3ĕoɢ.nFDȌW%/A@<`a\ґ1Vd4\G>[5~O|M)3SxEn)x~6dwgkseD_a'|Jp2X`ˍ; ꂎ$Q? ?p qG2ͫr|eJTKy{,2>ػ'NGu=\CJj e[W\p$hbCF&[7=D넭&Wg"W<{c_~F_Cuim_J1'rIFOT<^݃>i2i\2lr3, ےZc{ jUDT˅('ݫF>ˣWmI,p"G^\f0%!QID^Ħ6T釡$t. dfdqݣ /۵c(~ҞOM|8HpLVdR6j rl&i..XF't2'+Mۥ.u:mdF23W"ے%){5 \Vdzշ4cIF6C>ߩh,VӅH 4m^İ0C-8p0 Κq!C+Xyw‚ iБ]jcy ڏNzjYsR[ypsPj8W#^$D`qRƲoFimil؋%rb|ŝ{@NO 0V92y!:bP̬dMtWQ15ל0jfVN`_3qE%FLk8$7*BV0^zUۜ?W0sKl\HD9,݇*TocI:~u7LmIS` IDATEoZf8!F~,clStx,⥗X:_FͿ+z377"wH&uEY^j5M.NhƯfm< 4} S28Io1)p+_S0+yb%ɥj$}[o5{6E{̈́}ExӦZ'i4-a@iG^>zʩH6Щtde ǥQ7j,hŀYaY6r*eB%syY+/GOq?zV7]''c96ƤbRFL5d/AR),a,65_*0Q?Ď/3MCp&tI]ɫݾ+{G(n+y,ߖfT3e*mRxu +,oYE&kHfN)+I+ mgP.uiUz"zb()VgJ!;Y ~CS!ު)KJnǪ5WhwT.EP"W05`Ml`,k4." lQ4K4bbbLDFWE;?@Y}rʜgϜ.GAvuHisjnt5Ͼ՘,92NfX3n8ߧͺg휟ι1+R>|,]һCOʿY䱎}ӧJX}y\u_>2,#]'W3*:9'Im߼>q߾~1< `ծyU]]:u~g9^[n}ң:L#r.Ow̎o|u,ߡ1ˮrOEb-_s_mpF]1ln:31yW:<ͺ{9swg}n[wJ*I~gɞg[ߚf^9'=.>_!}=[dNy;>~ZN:uo_]Q׿-V]^Gƴ͆2[fU yf7˒5S2;MMz}kfb_ʱW /55Y:Vj>M|v}&n2#'kG #b}u;>>~OsK|؃Ԉ#d;ʹvgw2>5mtK]cO+n5;.;^xNL8v`[6_0c֘ݺpܟ>Y⿿'ސΐWkO8mVs؏n=cTsܾf}˝}SdF}\ސlqڽo κ<לxw6뙁dYTդv2jGr~v yxƕ<芼&wU6.?0e]GɡWNHCYuI2Umyw0ؼreyYaǩ!3b{sYf_59r69qodIZcnQj8+;e\R%knZMc}yi8<O],R6,ݪәvhȝȜ9Ng#.[Nm/#^~vNsfuʟʄ*"Xd:|.u3/d5kþ\;Mn63Lƃswϩ_<1 m: g Wvz|sr%GƷ'M`Qw^ҔhF޴?\uY|ҁn>RԀڜ?{غG)c?l* /_88[6l$=M'd~bVS\6ѧ帯/{vf9򓁗w-=YmdwquqVY~re7w6fefFẻK.}ms;爋F䂇v螚p޹p2k ncn/l_S_'mvfvmyQs9Q93U,blmWCt]mgPŝ,ur3artf6}AY&nTe錸 u׬ͩ`烾~.3^٘w^z,7_pN㿩57$EO?0M8>k}O4l~VȠkɊ[^o5#(NiN=V+I&ܦm}3k4tKgԭ5mNsud*v/:ql7\ # ȵoP)[]8(@vzz%6{+.&?\I.Yjufn4uoV>3םn[wv:}s~/|{]~faycgY[W]a/j;:[kG}NWɛE>1Mߗһi3/ξ[gKυ?0_6:70~|TDZWe6-w=!{ΫWܗ3:ٜ'=6dDn8kJv蚥V8tzR.s|8k^[4Mvک*KrCsk%}Ə2fʟrW˳bɼE󞐩E';5k:V߿'Fˇ_<+l< xDZM63vpi<>x\:[N3d]s lrhNqܽskrIm}]r6O_ĬGNCuY_kj}M7;u]yl>W J=_}(?cs$GT3|ayſ'LVV&K~|R藍̯8SoN)r/4߶a)ZshSy{KϹEm9>-a\WsN}2zfeT".k.fO ^k |Yfn?߿ž{ɠAHnܞ=k[oi~ EA%yټ:u{zb9=?wfa"/&s_K>ZIyޟU׾Բj˯nyYfNZsE s{6_ ̨gkF[vg=_'6f<@O圪 広+ޣoBҞON MCX,_N^2=w8.7_G䍬/X6Qf|w'uج>}"<q{՞5}/W>ngUV2eJ3\jկ$ &g>WߕɓE=_MLܰg MCX{q{)]$i 2.e\6߼|3IؼX4eTm}lsz5IҸYuښfʔ)_|Y-zcjOix㍅= @3=a-_Kx-㢣,֧e\4e\Py֗e\44eرžq{=$\س@VG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqžcfĉ?~|U{˺[ B;6۷oUOKϞ=6 %h!&N޽{W}:na&LPi8r?~|*V59rT*t93zy_9rij|2dHFOljjj5#CK.I252$_|BO8riiC 6ې=t444d՚&Z:+^x6lX2dȐj^-g>;Fc1" :t&H2aÆMwg?\г4G6@ Ҕsn} q( @q]g#Zj]SS^-Ȃ8-ɂ8} Ђ,*q9(#Zy9r{„ oSQm6@ ѭ[466&UFmmmӭ[gy&NUѭ[եW^Uy-z뭷gapZ#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG8 ;6'NݻwO]]]]wݪSN9%5559rl?sc|yqiiCN:餴j*Ç'pB 6/Ӓ-HSω}qǥ&ÇORq72jc=6y7sYg-ٙ Ђ4uQ7/Ө6q֭[SSSƪL6֭[Ut4uuu7n]Kcccƍ.#Z^zg33&wl$3.f줕Jez[݁-i[ Gnϸnj1ct#n-guҏә1Zg HGo7N:p 0E7dwNR$1N:`鍵%[(q;$p;r`f;S=BI>Invq[7jǓ@*JlP U=n'IMM46_N:{Uۏ?47ƍ[س@t9j߿5va+qG86i] ;6'N=)Xdt=uuuYwu?vY|P=MgcǦ>d`OKϞ=g{qc-Tq{ĉ۷o*J5'޽{77c8;?T5n?N|h,|qZj֫ܶ#c2@UgUՏNEDË}~S34|?g#ON.5䕻OϙRF^OVYjx調eOtkꟽ!ǎz9;}gdv|瓖iۣqEJ&ݦ9{7eH6!0k cZTS?83#`Qy3wj;!W~O:~ $vX$Lk~tw3q9osea{YXw':dpc%9r/fǧ"/?3_\+JceӄIi׶:7mu:x9ouonS_~t&>.qGo~=beճ_͞}6[Ke5LO0kdm*Isg Esjxx4ִNݳڛd]1kvhzrnҘ6)~mTTO.M?}2Ͽ^*͚mN9;:|n)vj̿$=K~ts.w92hY~!9efYӔ< E3i#Kw_riw䮡KMRc~d?ts il @UU>wƿxྜqm>y妋̍]Wmf)/ }qw99i'KMgX!sqiNuUV<\y3v̷ix׹~Yk.*W/J1.G}W)u+*n9p~2֩C:v].m\n ۜ]ϬdtTV9oatʔ9ZYK,MqƯF̲];cz[fswg~KY͑{̎;?/_uh֩6rn=\cl+ ~cTml7뭒:C%Ff}/gҴO5ӹCt4*IyV|.Yz;93axriKї2663^RIj:'];1߾QeأM[G.t{;՟}ȚKwNuZlwJ* /m:g#6%i|3)ڵK%NNҐn=$#];tLݒ;<&3y=esc֥]RIjڤǺ_N>[f3.;|pG9T>_8O1;g՗N׺Sr 0'ڵKv/RIz! VN.ݳʦ2gȔ-C˹so;혁{}+_~SFlia啼٪KKOu yͳֲuءszuLhz\&MY'SfmTRmNKBcWn3'_yoͥ[/>=wm.-}{|6<|<42q#2J^?(t>iO~5şsNK&GȑvȚ?;MRb9l}tgdp9phzԩ2+MTR$?yf~]jv?3W [7^;3;yW~3LN_:Sv85ܾYz윃so_ʇq7(XjP.?wTfթJvA-WCO^W͟j:&YO<lԌ}9mX9v<L>뼴If~:tf4ye7gUZEg8J{ xy_K>L?U8Ks6?&f&I;eӓN?;oz4lK:~|ۏw~ nOwθjDڐo?-T^d6~^mvO2t6IMt[u*{fwgoəo-a;yN ;_clgCFc,4EǛveQWgs ʐuj>_3Rw {ku=٨sY{-Mj+IZeCṋGVIWJm<}6rڕr7Oʱk|qONf .5:m mbY\jQ^ܜ|9m>6a<:i=HUuXt۴9o Ӟ:ʲ}5m!VbC<vf6^uR# yڃnޝ8}aJvxo_]uThdۭ^r=߾6/uR&I&7Zzݍ~9XkrmүO?˟&}%[Me0fwu1mofʶ6I=F^ciX2=.[\2Khziyy{+ذ=ҵ&Fk'w>w?+4,DY2VtY&08e\Yi,6Yat4vJIYiY)VTXIjj񼩭+v=DxwJfʤe2EIΫez'YvXm͕gsݷj~Ifcq?mcnۻ(݄p$$9 W p8DA1b4,RQ"BA(rQV?P⁀ !B(LHd.4 ;f3wK>p XP߮VbTsJV˟t*ke셷) ~[͛v쐨酧#qUxT[hǃ͔0TKB= S3k_aSO9CZD=|VqTR2%0@U?O_1znWƣO-տNnȈ_swh5}z=nNӔw=>m7r%S^%:5f5i=}6_CY3J%2y*G,P!+z|먥zٴI[ 꺨G:vXr"$+k:Si3 Od,\N4=>γ )r~ ~'=B88]nY\+o?t'>mUe%O5!p.IV@vxw=Ast"͝VXk)rX@!!kq9%^}@EAfZqƯ[.Ptȳ]Դd16uM6wAE^)W{RWd6^m[3 f+Vքmʜ4]3+{>`v!icrv9%.mײ_*tZ>іg}Sլi37oǝq-bɲ*(y{_Y/BvVRe|MRMw끞)[:,SYTpEK׭WJvJk G6qr&QCϽzx?sb1^}AgRT!.}co'_9tۡ$K5R|iAa9S4ͥg7UnY:eL) {vrETnykZyJ~} Ӿ`4ʯ2Ʋ(5hTWwA~ ;@TtB?-e mh&k;$75"d/^oOƆ2 YMYN~#E:d&\ϾVRO!Y(U(?rT^:Y?]iOϏU6L^is5əzg:tufqI*N֞қY}SmҸ>ir\S+Oz'$>9U&ܷN7z>E/ :}wnhS} Um3Rӳ8;ɒ6R^.zL/T穀Xɗ*oԴl2j׈'G5%U,Ё{Y!#-^{=uNI.6 L$gq):<.mXV?)ۭZ-[w\-Fj}zZ-cYNen/SK^M]0te,_Ms1T*HN ҔbS+rUfJ;\ٷe(YGң5Yޫv[gk&=4MU33Y1Cڵ;R ZIˇ3]5=go7 qlzOԄΡi)\_VWPEv/8A0<vLr:eYzUrw 5g_^_Oi|nnT:*P+hղ74% ZLs'eTNЕtc$#潶\&METrJ5.~-K}\B>XLԢ"ذj! Z"uQvrcs4AY9lIE?pJ9W+OħstGn*idCr8?Logk5)oSw?pV.=Q?cHUMPU:wq^ɭIs:fUVF7hԵYCh }7ᜇLR.Z9筜uǡE^;/#b7Pӫ4lnX-|MYIjU+B8 #yck_tm LLS~5I}YIUP-5zaW5mZ9/rXgj6 2ȈSBICNPRjK]?rY +Iҙ?C@9^Ze/9 WftM(/]`+d-,g˒v1HNoӢjTǴ'58uYV"BOr(? zۨ-7BuRj凬 |~dXL^wmVRbëc)fS<7[ڧ' ELS Ԩx s$CWt{Lu{,gT~)&~^z>! Y @ؾv||LӔaQp82MSeCpZ'-a-nnm۶M7a02MS۷o.srG>lykq;==][lѦMtpn J||n˜⑵O(9[>vFFF79 @x\tPCq`;Cq`;Cq`;Cq`;㺐BrqM6l! v2mPmPmPmPmPmPmPmPmPmPmPmPmPmP؎+څ!@X{srIENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-channels-example.png000066400000000000000000003122141501654372000226660ustar00rootroot00000000000000PNG  IHDR^4sBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeDo 25 Mr 2021 13:21:35 CET IDATx{|}dc69of 9$ E"*TNR19D!"dfc|߿?fkc{{sx?={{{.y@)plr-"y%@$p 6)Ҳzժo.X`n\DDDDDDDDDD6fĄS͛!GsliYJիV[`Ll,qqq$&&b6[?y lll.^cÀXJ9իVW5051L \DDDDDDDDDDn&xbbcq)Vcޡ?=7Ja·?a's+bo=hD{Nڙ1::cL<Ǫ}6t|wquhȷß}7ns&w扶`4fuVK/߼ܑmZ/9w  fNJ7%r{8DDDDD+Z-""""-GboMyO0O] $g~9Gs1I1.s%L +P H炐fMXGslqϱO_|UPDDDD$sN]c;,Iܷ ˤRr[DDDD^FcW0WlNo$^m޼'%o0`bqv0g_fή,>^DDDDr.֘Ew!Fw.tkl'=`R.$%EDDD!x J.¯ױK85غ,OъH~wK&3ch0Rq;jl'voOe`/!3ʎ""""2ĺz]&0,E ]bR^κ:`$fҷG<Vzl">iɢ zWi5*!Wc%EDDDe걯1\HxNHLko 7uv6SDW"""""y(4ȧa]&vF- -@XB--Wvm^XN Ɠ ߳8uMkt$XPG8x1nTDDDDDDDDDDD,2#9UnF6iAC6V-_`v.([o>ߞb:*%:*0H"q[^ijt/3lsj/Miw/?^KERR*סݰzEH̡lU\iW\KWVgyo_i"""""""""0bm3 $$$&bXk?I0Ek0~yCW`|/F' Z(,z o/M/2/h2~SK8ciQz+ , ɐYXx:#mB<8^s19a!O{t-| #fg'T>Y5_-s\;kIDDDDDDDDD%]ǀ HX^+a͗vRL1Pכ{k0̈́wE k.XlYy$&B 7Ϥ ['`Kլ>4wд~tWU&?n:[6ԼcT9l_GY*CMy; EyV|'6=X1M04-VÛ= ;,nX/MQ*`Vgl3sYңit(Nyk%o?(ċ26a6]#^_7viq qfݙ~]ˉ'%f&3E?^Caj֭{E󉅶 lta/ʥY\_'3F#l6ъ+&-g0d `[mXi a*s$>̪<3JVsBqsI7M[Yoy\էj;^ f`W@ʥ^BǛN|mOʴ(N۞# }ST-ĶXO'Eqq=ktF!t3JO_<ct.OY_yvwb4YujcQ7Z[#zr9ǫpof@8ԧw,0Jچ*~f/uF"b6V?޻jiIox)eK?Z'/ $]s>]q4wxbtꎋ䞼ȜfJW^% n5f%E[Lbxѷ Ov;;252RD /_tz9Jvaݬ} mx5LXN>\FJ6T?mbq<KeZօ9ͧO襞tҗssيk9ccu?WzҩSwLsגVj<%)>:}͢e֎z.F"fmČ*K92<+}vr[DDDDDDDDDD$ʒ=CeIDDDDDDDDDDekiË/Z3Yf"""""""""""8m2Ts[DDDDDDDDDD;Jn}$fٚqXL+EDDDDDDDDD侣䶈w,.K&:::{)Zhp/䣏>Hxx8`޽Ǐ=4nܘVZQ^=J(a8"""8y$wfƍ?~0J(7[///MF)%5',^!6l_5]ve/^NϹs5k_~%ݻw祗^=}^xsdBBB>q/X ۗ3fd;k3X͛r ޵͛7Sn1d sNw5ɉvnٷoK,a\~=ö }???s-oٺu+!!!LtFhѢ vBF)%5',^!CϞ=Snnn,]FY}1b'!![[[}:+W[nV+Q"맟~bƌi&~gfsʶ"""ڵ+@aڴi4hЀVZY-ݻ3wܔvvvjyYN1YƍSK};'2z|(=:G899$'LOFxbΜ9'O'ѤI&Oȑ#ٻw/gϞ?Fٺp߁rnذ!fƌx{{SNl֞iѢEHbmwLL ŊsN}A鼱d_"""""""r/kfō7Rn*TȢǤ.1xlPN{]dd$gSJܹ^*Woؾu2vX8{,֭[nn:K1DDD0|=hӦMFɆ 8|01'O~Vg4W6l?;wQ?M6^zUݙ4i&M~&3Qr{߾}4nܘ4-y򃜌IN:XǛDI7mDTT͛7AYz|F`ɒ%ܸqM6e9}Imۖm۶eرУGGHH[nɓVO^%K@^ؿV,Z(xzzb49y$;wիiӳg\l6sqvAdd$NNN4mڔ*U`0ru_Ys&"""""""rQr;~)QIUo%{={ر#Y&oߞ%K{lŐ 5jԈI&$ϝ;ҥKS/^dϞ=Trے0Pd;-ZyѠA\\\0 ~^x"""R \]]s- Κ5'NCR C'+d/,]6+;+\]$KbȉƟr„ ?z'/\w%Zܸ~:K.Q`,#<vݺu 80Mx׏֭[EbŲw}GPPP+VyOdԬqnXDDDDDDD$= n uŬ1';ٿʪԷ%{5pӓ|nN-::[[l%!iUyZ^ĉnoӦ 3f̸I۶myS'N^_~'Of)HZ$mrb;7K> sŒXӫJ޽:uj6euׯO&Ml ߧW^ >|wyӧOI+xsQ?In IDAT{;v)˚5kx"9)H~׻k4iժU㹝ss:k_Oߌ^Kׯ_Ϸ?ѣGs5&WWW\]]Yfٓ8 颍d޼yԫWŋI5݃8p`D/xbΝ;hu֙^RDDDDDDDD2ےk׮eI^(V}bŊfvQؿb$&&̨Qعsg2n8Ν;pss3j({FiQbL24j(Kd&ys?OsϚ僧APPÆ cܸq7C pvȑo4+V'x%JXe_""""""""N.Eb6KZMXK裏3g% DPPNNNzU^=FM~HLLdǎ 4oޜ'x PP!nܸٶmNJӯdb= &nQlt ܰa8i4 3ɉ>>ۖvZڧ~5i$Fɵlmmҥ &LӜ>} Iݻ8;.̚5kdĉT^wvv^z`}% ے$Eǎ裏޽; l6L4,XG};1OVZK8::8:th',?vvvԫWÇcggǜ9spttdڴi?3m43gK?LHH2=/q]>4ԥKRnׯ_&Nĉ2eJOڵ"""""""R$ߖKNl;;;kRjUz)nʞ={8v7oޤ@x{{ӨQ#Zl &Mb&}F:w_̙318::2l0:vxG=f=+VdԨQDGG[oqY6n?3<MO_P&My湲GK=puuew|[ɉ?˗/l2ۇ " WbJD\po]b &N#Gvnnn?+WҫW/ (EDDDDDDDQf"##wtgYM2fҺ` ēY +GawKXҥK3+Wޑ6\z5Kűj*f͚ő#G,l4yG:t(;v>K!B!FNc bرhтGb63|'WzuvޝiY7od˖-(Q__ߔo&}Q1 2$7 vMXX-[̕o weƌǧٌ=#GQɖɓ'3uTrCܚ""""""""Y>>L0SNw^6mɓ' >xyyѪU+5ju!{] ?&7kng&tڵk+O|L2˗tR\Bɒ%۷/ݺu#G+WI&_d.^BDDDDDDD~eIDDDDDDDDDDD2eITADDDDDDDDDD@DDDDDDDDDD$""""""""""rQmX޻w5퀀k!""""""""""b1qqq֌CDDDDDDDDDDbw""""""""""rQr[DDDDDDDDDD;Jn}Gm(-""""""""""%EDDDDDDDDDc܋N: PŋٙJ*w("""""""Pr[D6Nh4ߡMʕ;yd)}9n޼ITTP…)\0e˖%<ׯ_Wb ֭˶m9u8=yJ="""""9Ν#!!J*a00 ֌Kf9~8#91]H',,E>tsZ뼡q{ܕz<ϟ?{~$""""rO8}͔VL&5chݝǏkiHӧOw(4wwrNu74yOcPDDDDDy'ϊd2a4%<zNaz] sӘTi3f^*I#Gc?40^yC4K(""""b,%EDDDDDDDDDDJn䕸#|9fH2ucŽDDDDDDDD$CJng|3la;EƗS{by_++|p>7kΌ)?6SװΥ墹z[lOiQozZwYX@%oā%s~3lQG 8V ]G,hl~'bmfBN'unIZ+[f7୏q&7Ti2=:ܳ܎=Ƕ9L:T)NUiօA7On] !:V{Y<M;1h^0SK8W28i)=˺閛g7&D^JEćs?~>O&rr=<>Ͻ(blO l*cwFww<ޛ'֣S<5ZĖE&< &>I(9 f|`߸1;re Ƙ-l|Syi84!1(i^15 xCtm[*M0wx>[_!*M3n&;U z'dt8TMym2~h;x _ikz# kƾ[mx88?2uej՞mIh3^}۶C9ro<3`ۯP-7f߅"Α_g~Ǒ⯅XWVIsxr`|f>ێ_mӮV؃7+ʼng8|i_dKXۆ[PV`F(Y$斶M8]eRGf-A|$z_@\Ӂ\3cNE)GuK̟?1 Zº|ٹXȊkQ\o`ٻAtk8m;,|xs_M*L2eUa G?c`fԫ^e)W>^ZȁTَxr%>ݒ+^zYf&Bӣ,iwX΂BHwAB`\:jc1,j݊U*PjC: ?,J([JnbKg,{PmIb1W[T{YWï(VKB7&КVUiBp( _Njwwr ?:U(_֝2j%S_1ۋ(ɣ}YZ|%O'$-Ӑ qwHm-SyԯGfyf(>}z⋵MmFG­ kwֳl`i_ rWAiI77{PuK%1ꔩȩǣk0ϪҢ>+'jO|4wlO$.R0敺ʰtA|l_ x&1EٺMKGRLZ̅D3f5Ήϳh6GѻKHGnfXh\,>LDlO/jX# .޷: $ fڇ,{ĵܽގY2.G i0AHV[Gt>V20;fԶ<ӚS7s>ɷa<Ȓ|Gf kN%#ȿse uݘx<֢5OqK;jg#yʿ:<ӂa^tsG,eytte3UeV_L7) [_z h֦;WjEu{(cS@:Ľr|fKnϷ>Xf7֦acͨ0%,Us"g⬡)gَx"O?74Τ&c#NYwO|nj|.dj([g|>a|S<{0a[8^c<'p0,~qy%ˬ8}}GADDDD`5o,_~ 3ɪ8E!oz880W3s!~5֓? vD^w? ޒa:w p|zV/DL-U04ר9cEoÐ* X4/]mML0P۟ry3ESi~'T3O%cl;ǯ;~`,z8\=7S'0_aI;0csWFLMb f#WSj^˯~])P 3\(G^grSjS(V氍ҟz1O{HV{ &}ycTj܎~?FgO5S#kvf)bKL*CK^ؘγ|`F#f3nR \ .t`%ف\v4Cy̬^'#SQf.D` Oㅭ҆G܋tײ9[6rD3&4ǕkĸVt/ta5zs!%#سh"=}߯%Dѝ }Lc,.cԞt _8r l]j*ANJhd+?t?ߡa-[ .uh9m}ʟ]Úwv{(sPɏ6-a4ÞJ~kê e귡U` [yS>#4.A5 _n$sH8q6O?ŧ,nto3>q^ͩ5L,O]Y†I5XpHu=e>j[ y-oP\/TtXT-rNjcsz Âe1\$ƻ,;}7  ,L=8BNEQDrs2-0:Rg,gE#m]}D N  G3MtuçIuڍϗId-P 66E)d f41f#%+WikFupW &N|#`A+Q‰14j{>unL? FB̘ɯ\#GuvyCަƞyt(z{PGLr`'sf3γ01?gqD<86d:q?Ĩ[p#v3GRbn 3egR;Cq7 ĿRfLWukGDN-gX6%8&_NO[މ-ie`xTߓt y)ӂ@€? fs[rbu13:̗wVe0e%B׌51(u2;ܔxobSՇe3Z30 @=icfoxO&07 mʄ Mt[t(HG[ԩmRAo͖>q9Iv$^٣*U&g9u 1-MY E=*[/bl_)P 'Gq/MicJGb ^_Wc^,ǥm`*} hšYwW:c6@Sj7M=ifE[>Gl|گa͡k!' ^׋ͨ`_s6h,@]ޫ7]=o65[eG{T 4+!}zTeɒ2G o![&q_T>=ToRhxSQ/W-m͆_>ߑD[=եVihsk\ָ Р4yO :ams$ӟG~[̭~gӾ %b<ʒ$Ýlx>}XrDpz,{6xxaEh".ftaF[*zp5DǭXNߧ^50bg|CZO뉟3 L,qK}+&4~o^buP2pBX(iSmW/4٦4/^ 6ݖV`QB0#\-ƮǯT4v2݋aN %2ӫ{sat:P7^+s\ qf9cm4r0gC΃Zb;ÚquR&uJ^0&"/~k @?_#xFN/K_3q!kj '^B?ĹlSۖͨp&\bYݩHLN>,܈Q~ώ3wջ| i-L d}`l?p~~wQmݥ!jB #Eޛ 4E"RA^AtҤwB@$H)g'ݛ^PQ IDATgKGڍos|ngOMcX ?sv&= ,nMwkoؐ¥ac7i0Bd2!oX{KkWJuX ȯ׿0µt\޹}q{ѭr.ĕ(4eb;gjȿ@͐ϩpZ6 p;2gyzwKE)Zjů>*Fiٞ;ÅT~܄zr4,{vqŶ%Ʀ7(׳')ZG}"3\r^*+ާQ{zV (4ʉl~YsW*prg} ?Us/GO('ݝAuaV*B8IxT}?S C4{M @ = lr[P2".1%)H􀢠HִqGn8~$;FȲGjE~2^d]h-/Ȟ=[>_VaU=Gc]|]$h yAc$^'F䘑;>Mci5֪٘. ..a5n%tb ӐVr~ SFz lwJ/wg>1rY$?h=m1;7O7M_D UG9 jVUz\2m<bfmAݜ 7~)bwm(cw#cPd%1)oG^R G@ @j(Jץkb>єE7;67J:2e(׹^hT*Q1R$P {-;ȑ1:%\_Ƶb%<*դ.w9fr6&}J~:¤c=Gxȵa4םޣrTZ:0Bnnp>*@ O>[b{c~ߙю>Cix# W.t4/)~iЯ !ٖ|kݹ#6*"G <;{wۃ?1Dp`jD=؛hyz7b^g0ՌոVOG7 6zң8Uޓy< uM7f@C$&}6)Юi'Q K9WC+ag*>$}Ǵ}':^OZTB;x]6T=οfgߡ %O[v ?E20IFuW Kٲ`e{U8 z:ZwSF}嘁pE\cLY1IS6vR-x?HBcg_ ҟ0{ ZԶuXe]֋ӦgGڡ> 8dc[Z1Vƍ!4v鬋Ť RQنNX;~2FJ,^,YPՋ ςgjUٗu z;K&?3{&lNdTo)]ÏǸC7n.Qzc, V%N/mF®|„kGK\*4gh`>3K>Īib^ $L\{1=ٻELtK{avw3ϟk񳯎=;{l_7lxmź $e?i@҅#=;˯(7N*7ws[fNYLF ;Ȗ#*p])!Ɩm7NjgZ/ˉ; C xÕ딧|ر/[ O7 ,EnyDHD{hZ=4~ۘ;j&Qxn^ArjmKe\~3¢YH!]OĂ98:6DUcG{4n|j+?`aa\^A (ޫ8UQ*wǁn>e}OGKLьo S&-ar=|>}!}NN9._oE5L83\kcږ|Z6ufLY5eũٰ2M21!"F $ueN@}@Lsi0xn6'|θ3QLgU'y2wMcJ̆69տ܌ϗiolN-P?$+meMsy#7Ϳϋ4G5.sw>}f?dł KخU{NgG,^iv'a폫 G1GCZ9 SֳF6ϿOe*J!K0"B s,=LK[,HNfNpcT@d0&y8o.S%{P6̲p% JU;FEϡ1L͜fjY^l} s3#R3rO#vzduM7;72k?O۸Pާ_:oJ#ZMƖv8ՠ+è WB~6[a֞1T^[@ @ xkÇVZz=紤F8=Z.H?j5>>>yyGϏa;@-V9v]v-پ};M6hׯ7]Z)4z3KJį7]篋eX%M5k, @ o.vvv '>yDy@l{JxZ (u,}޵Q$w '7M;h\d̈́WI˒\@ @ brGˉ- 8x "iw9G$ @ x$]`׾ǸHE {Їp:~t'QhlSp~4bn[ @ (5RE<*݋{82MG^BQSui']-tz~ @ WI'u:]AE |#t*x>-tz~ @ Wۺu@ !T*:uNrBBB^w5@ @ t޽p6v.]^B@ 4huWA @ ̎;|!@ @ A &@ @ @!&@ @ @!&@ @ @!&@ @ @!&@ @ @!&@ @ @!&@ @ @!&@ @ @!&@ @ @!&@ @ @֡)>\ @ @ ͛7U(p trYf};wP\] k@#tЁ@n d|72 K{:bcc_w<==_w^ bY@ @ @ uۊ%j$IBQ /Bw!㻁@n d|7/_BS xOn @ @ C< w7t d|3I=–ujxs I9_2*Q9׭ l?H2ꂹNnxcM9sȲۥ4/K#o@n d|=7\SQ7e<&/_Mg+<ݤ Yh_#UNob9CG HC<-wݝ|XE`؛}w| "r`mW_suC N?sd{+N7}-x8= n6vwlIjIMĿk-n6&Zg}ώy%V&&dP׷h ~a}iT mŠK{ޟyM L}u|%zxsSs+싸Qe_&~PP|n}Ń "^}ʴz8rMsKBh@3f4vÓ,Ӄ`m}<9^(&= 98 QAENQrʻ{z7HQ{+,qhL1,SL,?"'O?@\2To=I_ӬM*KW<vهyO=O掙 n_"vXYV}'Ǖ> j^jg70|׷UK8ama]nՇqxvʶ#Jr^j1S3`?N׆(`%6 _NA,mS 1dSPm2MǍ)[sSL,v,JzlE[&Z"XZPd5v3vWa#Z٧b9&&6zq2sK;KUGYrIkoT(Na;+S[Nk},2,9{4RG+ ](^g-9Z^r|㹕J3skQYo.?6vqW{yuny3wqK0KdעelJ|įȭo۵,*:}ET*T8%UJyyW-p޺ZWE/S 3"^^S۾ɰ8 3S#a&/o<ٶg$TF1HzwQ1(CebDB;se;gXz% EBRRتQI`q*+&u;yS4k*hB|9fѿy|>81")KWp?4ky gY4q f k>9ݢX:;jBPf;RDz['2H*5j)P_O3^E|+!El+GP"ӡOoAc,G  I!ziS|{[)Jr5Nl ǽxzeT$ Z:HYc2 eo ˨w]? A`rc\=]Ӛof/]qu !ٷfReիy;Q<,GϲQ!:/dtz%}[D;? ax< kkp_>̽Gy@KG Ш%G:O2EQ6yuآ(26ecP[2 ytOI=u kGW*5ܓ$~L)l-egf|ޜJ.v8rᬽw>r( ⽦Ps; (;$23O K3gb~eDf(㌽Edy}tVIGllq#1]{ѯ]ub@dtP+33l;$}Ib3-@(ℍ-k~ӷM%a]7muvAGMpI":=%bJp/lS)jvͺG'0m J:`emso;7u= I@>nh$Y #\LQ{V 2[`kne \Cܦ%Kd@o*`if}Uv?(Q 33s[.g9"viǸ{8bNAq_3d-ⰢryCPpwv6.4վ+2Xbc-9y]Ɛ;Lpj6^"F¾0U:>X|+-ZUH`âJt]ua͈j fm)+jM/BQBuY3!wuqGz4CⅰŃfwӨ36Nٙ;IYso5V-djuۭwHH'GL1\ȨU4b1D=Y;1"oR g;+)Q.-zB"vfYzy- 96ffX7ԕ2&Jvy+_06сn(GM/it4>lP}%7 / Ԛ娔}w.Mк~܋;`mKEtΚM?u[ D4w$bM ƞc{}c҆E7p7K`B~zS/n+SVVv8եN[*{G]J;`\!{>SR:N=F?EͰpϞX}ޜ}CǾ2;tgӬ6Ũb˼"; *ޮeI2oϯ5Mڼs%Γphޖ:\8x$hqs5;b_)O=]cc>~CKr{Ɍb !cLg& Ml0qf.YšhndRl QdNㄏ!I&ۛm3eچsc.+ӟYN1-K6K{{?z Ri>]4NUd=~0pDŒZ1xA$S OO3PZ|gVcd*6;G9t9$ճd[ͫ&JygK9W28F)˺ƆC2+j]-;HԳ_)G{Q(YI79۩Lٲ-F1ud9"G/+k)U5RcPԉ j;Gn3RGz%LHXP˥_0tnZ^"E(qq/Kٲe $ƟX|s=eզ7t=8@,ad6.Dʮ8:јv׭6`ccSPuuSVSKRtlrͰF'paz;~+GnG0!)"EA[ ?1kνYj@>9/õ$ v?1cW:bKz4TU_z)'l](Ww_м3HP nv`w|i f%)[,eJ`oHf$i@.%'\^AQ[j<>lOu G  牫C9Q˃6FugM=N¸_vp9\q"!72,3o}eɍ0Mq0Mɍ,'?^HBQt'ݛ'%2!Nr7 ڒb5ZPDZމ´j)=Fkᄃi2O_S&e2EYTNy+J1Iؘu{1\X9S)%QNUs릍SrǗrA}'c~}?\ms/& ?gѵXdES|`I }c7@9$?&Z%\MAQt E+Fh]cA|(M_ECp.z%g|HRܼpXE¢aKi.H~ Hc`A07y9/ N+M6W iҖzfraGš*{NNmd6t]y;~~xkK!IKYy# ]VM_֔Ss}VҭDX9ɡ/WP2ՌnN_?agFAӡAg 8ph=#OqbB:;(9h7}ou@qԀQՉ֖W+|rڧ ̶/٣-t*Xķu^DCPLmjLe6)԰2iASMײS9AէJI-#s5%z?ĕ\x8uyU\6ҪDn^8Ǭ(fw.w9MuLϚ:Y-SpF'gд*c;BdT6uףt:ӜJۜ3/`یM<~H$z4gQ>h 7>q?N}S }:!䀜sr^_NB*Ԏޭ SׇidBw.cs@ry>YrYC۸~k6}||{y}Em L2v*ip{P|S~ SsVO]έ Vp^?;Zst#{~~jiJSnˋ} 2 ]"|)*_r^"E[۫1|ӴM3fږxkwt(hpEW_i Qܼ0> l>S:sdl.|c6ScdG_w?c.'wl XH 2C!βa$ݽDԅi~uE?瑌 v f6A_vO;P?s=K*dWȽ+gؿs/G6 F!("SH!.kyrPS ؤ0j]6 r%+zL]}6]YGt8E]`ӡmv(T՝:+HaՊfcmB$^X' MQW +뫻.+STt ?YYd4]ncʯWdҔ9񿲜*>6'_6n/osAFzhPsD]<ġ0}3-O͢{c' .&[Ї8,?(wps`Jӵ>NGt>iy粷`U_|9( ~L~ԬR2EKdZ_ Iwr&g7;~,iJ&nEO|Y03ډ2Mp"FC޳S_re7ٵ0{&d|$ 9cn\t VMy?X 4_WUyS'TJ]&>ǼPi$<>:ɔ:-brNgoxzҵ=q.gSrr=rԥ@_ޭ' &HG"7s,Xʡg(&56*m.Ģ$pEI8Y[cSBdr/@Wr[UƮI  cwEc?F"([Ju>~lT]#@"F굨FY 4_6ȗ S\GTpr# >qfImYI[sscT;i]Ѯ@2P~sju*DŽBd>(Ƙ(F9EKsuuI#q``8EO\q3N^o \ ['\+f`q<4=.MюOiݲj%sv4l?-x+hʶUi9w_gyGp̠MS*dQլrD8z6 [`mmM9>#ӛpj|'U,KJe axr30PyP:u,;'*hޡ):$[ZLG*Q+<.} Ӹ֢ hBB SN[lؚӫ)#+h*ȇPOno+ U!tnh1I A!NmϞgǼt /!4oЇ5~:xo\NBQ9fP_*HZRg"( iO)Q79s%,{?JȜtw :ԸuviYz q=MjyPڥn+Qn@cIr&۶Jm{; AUUa _vN!?'O&MQ R]1q`ZB>jJOk9J,[{;aBW %.H$GX V39wr ]^(5jE&*Fv5UF LӈԷseST6GB&I(z#\}5nQ" ygOhfJؕ=,՞J\VT--2^S!{i9Lmkʞ*h#{GQIXRr|)v?/!},pY2jPa8ˉf\4J wn,EEX.4os RO.}I {\TA>|1 ni¨lv(3凐ҷ;ifFA#QPK7h\JRP8;Ƥh^ MOt~ 62^ҭjT;M'LUیry0KNLJVқE(^FNIOkT061FBI ʳ%%SW6t g`kR)1IJ@I"1@?M>o&{Vb/M^ǵ|S_r& S>u ~[.: +S!;_xH/d~r;E2iՏnE`hQC2NFCQd"=hS9U5$B4iFOѹ\| (a&t/1 *zASÆkTuPytG/;|Yк P3L29#' ɤCEAQԔjx8FͤIL4џ' pRz|Z"'a[΅PK}mf(nK] %S]}hYAQtE<[8-'əʕ F!:LO^8i&}>в8*37ʺj@3 .JJYNOyӃ A zMol]خؠTV%#$)d䠝l8,SV%iFwc/˸6m'm^{ ӸѡiXx͹Nu*xK eٜ!&1i$ƏK{RB[ة@{i~ IDAT}b]WUN`l`F`4["J(J"JHK4RRR= Xo(zޯysS9= Wr*`}T\s{ v,YJ̟{г%KMv*eXfٜ}=S#4A(g X,1ĭ Y 4E .=nL\<Ɣy&d^ Woٸj2M|w9J&啦fQ* ¶kĸ5X)xLԃ(}Z1d^튇 mK"9y,9grzd<4,1(!߁Bj_lu+2d6NcKy "/?-w-)߲17Y;:Sf&q:_̏#fLbQQxbEym(mjz']u*R2:SS\ilTpdIuH"%.KΣ ET3DyoDZ4N9ds O6GIʮ<=vR"iWgRC<4kKJ E<`yJR*t 6ޤK5BH-)1a(;齴?v9 ?BzRY8d8x}T,jH854u,8OO4+.$/_e3tE|;jL彲:M݉I/KLRYFOMwyczxWĝ%[ݱxS{twK$( ť(zSZ|sJT]fɂIO^PNg:'X|B&r4!mf72}GGEw?E gu:ubm(D,|j){}J*F!(5smٛX~!E_ UFxҨ 5Q(oE u6Zv';>OwO>K7ɿ=Ћ(mB+G xeQ]UE9Ȅ+HR TiG1gf#.B*.LBHz_'Ѿ q>6c[F eiz/1TSN!z>iS"ؒV'KJ¨4,]B18.j0]KZ#'2=&gZWMY$X%{8'Ԁ~CUFps PL_)Ǜ4IYNAA (F 2-yoJ .xL(jw6 F P*y{Ma w2">-B3|Mdj+F_!*Mb*SjӸO5ʺ(E]kS] 2^ JfyF#pu_tCi_/bNLĨUw@%pt{4\"^A˾+cLJ]~4i@E{:1j˾⠘1NU}qwr£|LWV߷$q-CẆRQkYVzѶDi3Wg2~OSRX3M> P{  [̝ͣF:ak ,ˆz^^UmB#BW3?:j2|$5OzBVԚʯUB1s{yʕ(EO0Cw J@cwj5nM۞s$Y(cIM\_olLE_]p)\AbVt4lH9O|)K EW{GΉUqYydٖRWlw+`1sL4fY:GI[^$O\z3)Szwb ]G ..nAeM[xG}TV &08-s &Mϑu ,^c,]Y93>n͹h41>So0CQx~qߔݱnz|[_ɱy4ϩi1~# ~`RTv8d̪Md+0|DtP^ޕxoM*[*umKޟ%-QD4 Mm0n'1ֳd6 u (o q'E~%Jpv4Mi'ňѨD֧Y5&U m^˔S#oi{:m;: ŻE}ztL۴-nޅQEE`I6TFҡ/8}%>ʶ.Ź|dJ_#}ݓD gΣD QJҹ3{goy3~B5 V ު8NNƌ Yo, eyZݘ2lwx`ūL Ti4ߥQ <{F5ULy$`mG8,\})W yaeSw~֍hcq//w+-}{ˆ9 nU*ݹO'%A+51x+yZbz7Z|NdV".xK/*5i$.:n hZAֵy=*E'U%u-WRZV{%0H7?'o 텫ܧ> )8֧,|;0 ;Cǝ=˹4e1{(e asB0O2cL$~S:rCk ngg&l c|ڳej1"N' !<2sdk ٠88ᦆla{%#Tn4mSДuh1s{b~OaᴭR ;="nǢwL͆T|qÝx{ld jV%[-$Z_5!?F(pѶj1TC1W|ӧt\cm([F2i(wL\o;5V.X^ƣޖz$ځQ?Mux累Xk}"IUx7|ϠۢרY:RTmZQe9-oР\Qth4zlب3 Ϛ7/_%3,^4Tz\nF,T_tF1[ѱhKtЁġY0+Ջ9@Tiq*ZjkHLSQTxuER:-6QiI:}5 z 5.>ߡe,ͨ w䇵sԴ4ѕ/V`D%e7*[X`0LӐ 8. X >b3ܫo=:65Za-Au/h=my g J~|Pw>g崄{hsQw^Un>c19_Ęc c^=cY*&Śm':0?cB=eyt:7R#H&?cj!ƂD%ܖK܎ٻHƓs1GxR]cB^mR$$$x:W/^CXP !!!i>yҔwȔ#cn?ĨCWy©ǵ?gK #ʰē*?cj!ƂDe0r: R'^.򅈈NCdٿx2O Fc cd11!ƂD竑W-圗>ݶmۗ'eNjikBQgE~Mk@ 3Ҥ'vtI޶W>>+|KE_sRw{d?͞P +WJ4²R|=b7,^U^$+8}Y}khuȱ/",lmTI;cmbZNFy;^؋lMh®CzŪ$oo ­al_5U0L>mvn6/G&ɐ'yo??|8L_wݗr%Qiiaɜ[y;ڝb~~z1Kb, D91@%6}՗pТr3 Cau1ӱ|i\3{'>j_7ZKQ(\6笹"ۇX0'+աC$EtҬv͍KSjO3a'A8y랼,lmUTj붚,I CG23M%C9n4%߶[dY >^kbxt@378=!Ʊ9qP xB~MLq,XK F0M[ {waim)1weIӯ378s'at"=Yfʐ>G bS.7vGH2t1kw ~3m5z l-)sw4FE([t~@ʇ7.a@K]Tx[$[6>n݅Ч(プoQԀ,ޟp3¾瓉=ho[C%v4"h5?XǬΞ{EoRgA8M?.e`騚Ggf.ЕGsS>k [ p;;ؗ/74>=l͢MVk;ۂrj]LҏfN2r{}zb_ҿ 5#4{k0SG`26Y3|->c;{P#ߢK7WGu!GɄ'O \ߓ.l@eCA gXeF ?`Gț8rwq^<6t[;qp,wFpQuݒx{@ _A=g%1>~CV =#S"qe]qSGؕAдl~<>6m=غ~(9I2S}ҥh4>ײqF]bu8wͫ"JZ gPwam:يۊϷDSAB_Sq cEޠ^Dĥ 3ۿ=j ' G@em"*H^TD BW{lJ;x~$Fp+$oKC~('$mO7 @&G}6GBIqӉ#ED~E)2L_/V צsF!'iD1!G eJsK".5xΛfq{^sa'!²aNwD%jb9Skb~nLoO]1N72>L .f)t/H^4C֮¶W"}bhVx\0/*mEYEZ_U|yU݅pGsE5h>H2>1\Ƞ\2>Ea<>FD5C܆^Y_3>M5tBY\p/L ]q>:".B ;bMsg:pT|RF+O9[4E?D\" 6łYW ^7>\nnY.]*ٙΑnܫԯ&MKSs+34/o!,C|&<*7ѧ?EFMȠ߰ݛf-]A Nr<=f^O7 {: bG/]ͫfO9GLnxbG>tV1mӘ;"uR\O'.uP^!1kZN|^e&^BN< .0W?7fxw0m^*iGaT;dyp $Τ_[:LQW(xL'gǨNvaGk*OFM֝oq\|}~m"h\|譟:{zO ."*~ս콤j&}GWz>1 ;B  ;@o}{rTF}~/7DOQIW* 0a2{sFbcM4`r6Aܵߞeos0SX6jإYᴲ;sMynǏst+XVL;|Ǐs,l _ Yr!ދuN8xK&᜵EHC&czM>ΖOoW޷ԭaX'>=Ȱ%ٿ{oCYWQp%+&!l`G#25_bn㗲n$NPM:;v17oqM1L6$Q[ߧQYܯ7[Ӏٚ>_;d2ƼcAʱ Nn-3חfͽM>r5|y`ꢯCǕqrXU:};;4gևk#c8M$>$W#ǓhN^eO*ԖK?yI/O?UJ”%ٽkSۨX;3c'LvbC6hR?;)~:u h%%{N$/{@Զ뷵HRNJY <*iݪT;ƒjK{Hidt.1GDhPrMɡazbل),F:;Www_MdUy*0i| , &P ˂!mHn7dfquwAܼAm(Z8uΈU/ftߦׄtI>oТsN='1qq;||+ALw-qZ{d#3­y4*<8vSZ)wp& "ظ."V|Y˗axS&FصSbxs4*ps;_JwyŞeFTS~T'w!nm+61uy*Yv47M) XPR>O+}Iځ%KQJR=NxƪfCjcm>pm48`u|aw=T@m[@d Q1-Gk7s(ڽx9(ThT2Z|9oc}OQBIKKz$N.݄/'~EB磂up{3u`|Sio˹n>1pT%xE_7r[/\^ڹQƘ{b}N(ǂ0OqJ.yn&Ν%KKIg|e&NۑE?vŐZ[\HbQ[q8'5zxZiZxRm .gpu.k^ʝQ\ I%mҟ̍m*,_co[ڭMh%݋>۟Ԟ.q^4oeBkL܌a3Eg8:8k]~^a##gFn{Qm7Qm`~Nxeςf4b狟܎8˼̮/8Aqn@j]< ?Ӈt<ﻢ!n?R*Y!TiձbJ6`<Ů4jD13-ԟPl3NZʼn{sBRh1'1L˸" D:?JIvuuuBYp!.{& /xZ0 ,<1Wx.c1erLBE/`9q#Y s5wy`hZfh*-OS?.XVXĭjoӯb:Ѻl{h<ȅ9Ҏ?G\?Ƨ,fWo cT |ɽiP8fbcŶ߲٘ՌV'ز=[G`@-lir|^ŴF}VhiJqih0c2DÿYH ''{MWa(KIA?:FӌD'?WN;:NE[W<`ӍdM&nFBqU~ĥD7mݩ=*K{P[ S] 2$VG\•yIy#ٳe5R*Uگq÷ cʣQl("ɍkw04\RYo*ubgR펥u+-:'l ܼm/l~1 .oN4Ԣ81+bBZߔT]] *;y>9h1g 2Vo*~=nD>Y̷)p(^:f(HW<ܴx6Er~0ߌY %R)Jpx%ۘz 6/jj$y52+Cq$̯?Wxx?N{=0_qBw^}ؽ5B|Gaw;ߤ0*ِBg|؟E#6+B%Ѡ%纰ftNDjߴe\fb[1? cWSݔÐ9jA1@5Eʦ*^ŝ2FcF^]y"ocu0q)6lP̿G9rD2Yd+>g͕1f+tt`GUr7Z,GImRs<G+Uj pץq w0]ޘCP\i1y}6tz/}hIBNfy/6wyAv-zϺ߹rT_[m nFR|KbØ7A[͡#G8t9ȼ.ny 8|Mu.UOfsz%XVŝ;t̵9*CZpa{!+'g,S=$^іhb`k@Dٵ#qI+fk=TlSу;B#Ο|ofZϿNpQ=#[ط3|MASĮ 1buJy=y#$S:Ä~RDu;Zo#guO _v2PQD5Y&5Ɠp ͨmYǯ*\jEؿeg.lcE *,-kVF6={LŪSCM嚔([vJn15*gr˔E rOt5x[qϗ{F뱬C7\M=7yqb(Ҫnd񔅬_ڬ>bR&/\͍hBqm[ml9o>;׬­'8sYZ?b7SKiM$m#'ip/_¦?ض;ًTc۾S4;DpY-Nϖ@o'^2Fc^jI.#c Av^D7ˍjՑZ>|E笹:LrdI79$"S'/ȃ؝PfyV!OiŮc缋ͯ#ēKڍ=tG^ -I(j/s(;[=;4D{ *-hV] [>y+OPM;ܶOҪp,;RTQB7^gE9%츻[&q1pGyN. [X3Ma_&5C>VՙŌn+śNZeQΥ]Ol߹ܹ|g1o*\ɸdS"UWNleޔY\M@w(d"}el"\eӯ3l`=]*xt[}vLϝЪ{p1oRN;vbSTLAzU(Q%asDzE~YP&."7+}|͟(i f_0CڔK"NL;~Xā :Uev,?zWXOHWh7ކk'^h۸}2k NKP&FۃktV& .Gүpq?u&N-8~p?cn?U|IWmgNG9 OsoUݬuJ% ѫDoǪ9݋Mzpm?)Qͱ==CQojk@ߝux>5 ńᛱi+]}rU 6=MIU@>X=Di|>Xͨ,DͲ=NጳE{W=GU|;fba)^kGSSVSkrhMY! y'>5ŻxuĹX03Ε)ݜVptN$){t;OVWeȮgj ࣐_Я&.Ƭ7.AU!:ߝZm'(jÍSWqn՛WvQƘ\cA1F}[yjzmBzLę =#*72vדjأ!=-1޽ğa5cUpkT?cN(nE7h8ӛ9 \ڃ}i#G3:jK?ghjX>54)٣18NTu̵Ct=sCPΞ;;! YUAxmwK-]e|=kc峆FifhIB7MԦ5ng1tRa4kٸqx|G,n(tcqH+f_D#;/"Z Ol<7QTջB"/W^v:Vk)RL9k!`BxφDJ^V6E둫di0Gó! [p)QWT }94 >y$)"4PE'X46B  4*z; KJ(Zh-DESL׎r:KVxZqD7 zgb#:Ɠ_:NzRJ+,=E;_eΣc!jBV QZyNļҖ}*1A(tETTqyR!ۮ= ǥ-ؤtaqǔlv}AbKxRYW:VX:Uێ? _`jFX}3iƋCE>'n??)Ro񢺕A4}SNZ18bg)]]8X$QƘb t91@2滇5܄N-z;Q(Fq-y8E7XXqiWoBhZaRLTlO3&fqsW^u8W]{gK)>1>y{ahS酭WUc>q7yCC7%" ΐN!zߐ"^D i+UrJFX3EDxCt¶*+o5es[/D$^oUtܼKܽNmd9M(%'Q$I &y;X6a עZ _ʡo4dl27 <(w 2XOn?fmߢԨT*Va Hq՟ɛx(۴bC@+`goÏ' {xh%{|֛M2*T* gK!z96Ū5) ։v3)~3ru'*ふ5eCtu`y\-T*j%Tiއn9 A_<":+igDzz$c4DbfmH)dk@oNzo1(!8[v/IA9(]=^L[ <[,k_#lևtW/gjL8X֦[H2 *Cr&y2_ỺPgH|KE_s2 (~q=_w-gk:ȖX{ŎUUOQUJjQT 55F$VBȺ~$!HbTy^:9{q}scNYu6c,gkp@U:Oݛp3.iwC۫6ζ:QzAG :}A3]7&^%\`4)_g[#6y)Ѝ/ם!y?3㗜(5}iZBwݥ+W#35;ߑw?`"8s vv+rfI8!x7|?tRrBJx#')[UQ(YU+Wdz͛QweIKk1Z.-DN-__OZ$O~bx"FH9R(n=6K$$~|e8ma%v(N5ˌ}rےh#oؒg?NI C5_Ezz䈃|c2X,WH(Y^tE?} CEIP#ыWr3˅i`Z~zty/(eFX ҹH׍Od'\[' 7 Dh'v9XƂbk/3/%\ 6Md^b>tjT.6w1_ŭp)=Hl\m+~+I_( JSH혦|YrZD\j<%`\D/6$G"b:9^b-h.dDȺ.yP|OhPI8d:[J>gnSDtl78I ^Ibm%yJ5kΥZf_WГ1a`SNGj%R("2 $6y/S%<\u&3drpTY1KHnH-XnH7=na1ډgeвr/)˟D)ߪ+;^/6.í!riN3.N>[B>.e{%&U3cLL3c~rfqw$/[^|5^jypHZXRݣMmݤX@{DYұVivN'N>Rt l+6~*7;k]5i'?1T5ƜDg m|Fڼysfʕ+,Y#Շ|ʛX>7JT0^k:`XԞкC;vcG~ePy=h)@^ݻ61bL+3bS>cF#2[1_t${n\!+ZW$Vnn Ž m EEKN6&|y{|u)>;f.dւ@ U[Ѱ""1Y9;zk(_|=rXqC #/qޓ=chC|8)J z0}εpa7bB|+&|wܔcP!5yi8P}0Z:c\W$H| O%رc C ~"Uq{a_X7Χ \Hؤ7h4pN]gv2>daJ*m3l{mk{kM 'ѩ1Ž)O%_?O(mf'']S^ףYINQ&485ȚIe߁/8?؀wWx}J67۰53X8]/[eٸ ͠_cKDX} #xD+z.NwG9OEhYgB T)cS\6oI&t!X2.b5_7ک'*eH̊BS(_U+ K| K T~Hg!/i2U(ggsJ8nhb1#D/H3X %^4Ag*VCo wg“}*L!C7z|\P!~\ח;fE}4a)%Ѫ@7߳`%@WeWI-<48>x#&6^L,k24Zo3 S BwI]!+:w&L9oI${>o#2}H}f=XI 6X!=1mH#SZnht^?,;Mf2M*{xzyB 'x$A뎧ۡa=]DGcea d/wm]]OXM,yvLƉ2)mmMxEN|Mۯy3.jMȍӜX 8tU%XuY| a+' ,#,@nT"˧> @LޏW?X:zBSy9^`XQ4c^!戲 Fcjz4K&16''Ę6E_T E~ q[tZ' oɍM8m'f$\i(Ӡ}PgRMהPOύ4=JqײtkU#ƜD姢dWbi(Ցri=hXιq-ԍշ,Dfؤ ;;@HHGmdѡV/_O8Z #>o-ӣz/4Bѫz-|6z{b34vql`v_-ߟO%4_5xxQ7\ S9Q\ Cj҇o `!5,iNs+R=5v,adhBlB|}7޾sۙP jݩ~)9NN899ބYtLmL>GiRIRK/*A=Yҋ!zW苟A$Qف MtOE:ߠ⃛gh\P8AzmG)d9 1>--迊 _IWT\La;~E  3CǢX/kB/s-1{ĘT u;ȾL_|VфɬA{jH_H'->9NinhgS3PX=#酜:}J<^u d_Y}-ں[BÒu` v'>ɁgMo`0bتJc` FO Yk1pgAR)|h{:RT% Da#tSƞ }X9^>7gTUQ)uHkG04*OeZc0!*H``??9O ЂƴɍO7f@\|Z![fA&ԪE̊uxJH!iO95j@]sаrkSq;<8 W @$DqC;V0B-xS~=y\^ʝH}AWFd;3چm:'>2FZ}h\R²!ܥ0_!*'8^4fuZW?|,:|{2F!i^dt3ʪh-b mNNhj1fPvO<:z  `%XˏlT>POT#ƜD姢dq͊i߭!O< +L"ó|X3CێFْTvOX-uߌsN3͟wkt%Y͉s86qClK}*3adܖxJvB{=j@{ cf_ ȉ8BHH!3V^'a'6Ӡq a;o>*%{OfH%= t.]!%e'/`̬NЁaNFmzw-ipLMQ9PL?֌iNS`<~0/v\9_츐TZ{~̅jPS}¶;9)G*i OO/f49;1ňZ?{vp{ƥly >K8Kr|QOQo2j3c1'Q(/SFnKsܼxߗew8b-R|zӱ_+&vE>:FU,xZͼ)#Xg)љcvSfդ^lM'h\ijw"wg]=q86MԺPVys?vFBLWNp}L^=@qQ7v|=+=Zа_ʬD>)? e _[f&%vOsܺDh2+VBsZ(eWx^4w!pX г_z῱rGd $ ; VSwc8WDdidq)xvg=K57b|/C+$,WXұj}_Q,$ԧE܌ܿOr"OCty?׽(qw ۃ{5᪁-b=KHHbWW?e}Li8au0a׮R:N\&oVxOwwLxx˦{gnpt&TЁpú2z öbnod}h{dyݔMqO?ެݽZ=kĠzʓYuGm>⬽ϵnaM8"tXޫ˿Lb+ߛOFQh֒bTr %>ç1på~ۓw453Xp8e&'.Ebsp妌e4TI?)f0OUC9Ak_dE$? wqrLęhtSn}9םOLشGD P\6MlLqњJ3SOwņ90-])#}ƋӗI6Ӳu\0<[`o塞:Q'i}&m\UOrqyuLІiDkrǷ,#b'f0CgGQ8բ|)BL\mEyL:]f)9!F%aeBXV1o0!ʖU[3uzx;3OgZ/YȀv_)*ސau%+=I1{ĘT,ʕ+d2=g͒n,Wdvkq^[2DPPPf'A$*_>uLd7'䳊1{P1f*AŘ=d@?\4gJY̆.dZ~J{=c%xs#'iݗU>+WƳtZ2YzJY_.XOVMQ%]WQEQ%k\v ,c^b"!v U-*bIT~*J{Eĵp| &CZTמah/Lo ӆ(UEQE*FP)'p3Rw`DO1ssM4hl5ݭgvBEQr*(d>='iRf#u~&ك1+1$*?%xEFn+(((((Cs[QEQEQEQEQEymEQEQEQEQEQ啣:EQEQEQEQEQWVEQEQEQEQEQ^9g\ʕ+jvm6?U(((((ժU$d! B֮]Kvj{. C1@Ř=cbTك1{ 1$*?3Fpppf'AyiIEQEQEQEQEQWVEQEQEQEQEQ^9s[QEQEQEQEQEymEQEQEQEQEQ啣:EQEQEQEQEQWVEQEQEQEQEQ^9s[QEQEQEQEQEyEe;o2vW|fDQ%H?OÔiQ$\(Y ?۳8a~%c:}f:( &/c\ʕ5sEU:?TY1'Q(YEWMA?  IDAT򌗳CO)@\(![7 6/ȑϷ(ʫ^I*j,hcۏ7EќZҟEݰ5SDzWב:+JV`v2E6šu+v*\QwPGaNӣr5O} XL]qUY1'Q(YEwngcjmpk]G0?#2.-V/{ͳZcO"E)Q~pU͖JV^Eڢ?t3|j&-c.W ۟t*w'[~VQ¿5ltTNSW{:bKZ7?%1xa"Y{tJl)x >pJ=yﷸRd¶xjXmY{n=/˹GҮj!\ l_o;ٲnJG=p,\>&5\y(^=={HZ+OI|ikE<شa]!p'U \9TBy4_'4)ㅃ5yt2N$roE:2Gӑ1RZb,gkp@U:Oݛp3.iwC۫6ζ:QzAG :}A3]7&^%\`4)_g[#6y)Ѝ/ם!y?3㗜(5}-`:>'d,.|:+o:gK]+* ܷN=:) &Ff]fmRTiϳԃ y+?:bf8'CZ -VYӻ}w`Ԕ]L؛f1.#|{^U2Vԟkք]iEW6U0l#[Fѝ)K0!|N26bԷhRv 7b~G#m.qXg>;9Ŧct2EM3;E/`o6`WKn/+"%cs~q7 ui9T1*UXrLl޼YuWC9bK^Yt{eMd'.6z[;g*l1)5cĊIRM@WܖVo[]C'-I +6(AAA-IvS㤲mu9ſ,n%,WM)Abmk>/_[ Mʸ@PV'1M,j.&ˉyJO [\rI;u]09fU7:Hp,ru^S|6)"b:6FˆbmA ^ŤD^6<5R-/s+cdRb)'wG#){W[]x_DR+WƳtZgq%G7]/|oMaatݙv+[-#l7ѩ=ӡuw8vǎʠzS|b+wmbz-Ř yX1k V#}Yboqv,#z{qz|ab,`٩ƽ w%hwBѸ{j k㐸_}Dx?<Ž m גHax|y{|u)>;f.dւ@ U[Ѱ""1Y9;zk(_|=ҖaK\55qS:ݧ`N + d͉)k7חI ]L(odnrcl2i?s8p? hP(i#V^B-)K@=wz"pZs<1CLO I㹸gm{ 1hj~=Ŵ;3tv n Ķt䑦A@hx?Ntr]o>p<#%?M:tcǎq4p 3Tat86p!/cޠ8uM< +-ϰM)كYv6-D25[ӒzZo6N}2MصH>bm Xg"߾4i_;s ߡ7dTHxOUcag ۗ~BɿKyW<pͭ(/Y3 ;Tɱ xwǯdxӸ [3#~ Wky 5cKDX} #xD+z.NwG9OEhYR̮UJX|oƪ 9]|  [u|s+p.IRdˬ(T=U9Mt? XX =݋&d솶,3"lKbɈlFKJcYɿMP2#vtYm0'Z >,0qGg۵xkF,`·=3+zӷv˯ixz|ZBsЮkwzۖj^٩̅y}y3kVIZr{ tc= VZ2{k?Jxը[f:CsÉ8bbSŔ-y2/Cl6#;e`!t뻟ѹR~gb4{ۚCz=D6)Ӈ'dHT,z{q,ڐ_|͘6~gp/֒0F}. Iqd&kVRާǵkhOZ)b8Q& `S o)i5W4oEM3 q7\NV*PN"Ҕ|[<σBp!lDxȍ Rr7)ԯt7#o~Nئ<Vܡ2䄲 Fҗ_3p-]\ֹNGf0"1;h<5@uާJiLZ?VtHGb0umgjceC\$2/ϟ]2&A^sgf {:AhX;miJƾp'7J7aⴝpuLFB:J6g\SCY>q>7OИ8k*ŹX^6sswҭW1fs^~qaTGV>3uaBx:ƵP7V߲Į2C9]INk@ֺp`VLqjb5&!$d/ -t僪qucdwH*>CIé|L f ,yuQJXUcnN_q#{U0ɥ9_A&}B/_b_64ǿBY*5٣^#n_NVhBlB|}7޾sۙP j{qP g''pro¬k:u}xz"ROOҢӚA=Yҋuļ`۫qnȠisv(@D&o:'"oP34. (x 6#՜uh&}rH~ !CJ7È %S+|x˿su1}GFD^6)ߍN^whb2"'99!gŴW=kUe${+W pA. Oxj6E ^6X[[cmE1^Zzvc1'Q(&-w}Uad֠={r5/$\[ܓ'`4Vb7_)CcIђȫ(CZX; WG) gGFB7WknF:?0 '; ,Eġe+ע\a^+]ቻ6a1@b,׹.8o#r&Œ6EAR&I?K|jܫIpEvs w$.0:·Fg(#JU@JԮAo?B1i'C蕛ssvL%^[QERvܑ 3OTV펡UK="4'7 aZp~ܘv? +/S3"dKc֬>ȄZHYT]o^i)TNLi9fVZVO;<8 W X!:,H;z<ݱ2něk\RDj Rj5 $ۙ96lSy7.ad[H+kQYXv0&+Dp׋bԬNꇯUqbO r3:.&]䌲s {Œm,Z$]DzjUJ(y}ӸӮ [5a|ު3s2u *޼13H9 1>C-,ʮ:[Gq^D`%XˏlT>𴅊1{ĘT5Yq3?5KC]aJp~u$6p>8s0{a j-IJ(heŒoqYyFki{htXC*}'ƚ]P,;Qֈ^6_ug߶J7 6T/:,̈́2q[)ٹ Rڑƙ +3SKբշ9GCB !vFV3jm_mױmM%şW)[؃Z-"١_@npZKJ*Yq$ +'99!gg]~0?-Ž+P|}K|0]qʗq5 X.dLg*cNSQ_\Isܼxߗew8b-RcVL4}t~4;Y/ Zͼ)#Xg)љcvSfդ^lM'h\ijw"wg]IY MdYԚ֖Vb$G".&΂rŻ]Y8z+br&x sG{䍨:' 4sUN3"k@[aހ^kxUQ[.<:-#@{ks3'Ҕq9a ӯ]t6L:*I?.x՚ݬ5Om;_6e;p3S,0T֕]Lvx'C@۫'#cnR}fMCY\/%gSD;j&hUdi]g}G`t&k53^\\eCXޔץt.6\GOD[>'] Yc gܾ ul Tz݁m`ޒB1~$N-ѐlޛsWsQ=FtA=TRy,3^(dŭs @넫W_eiSʅn܊ `]cH"nOŮC{:>5>i.L~cqAIEDdJM, HҼb:T,q!ͥg.k`,^~91f0H zl=qp0F#ZQrH2j1LLti,zh4VujHq)~dbhbKh)}'SwGk;k.1h51Tz(g'[.L i?Y~& :0\YZXFZ]#vWDLntzu-$U?3+d`Ӓagkg?gJʑR*<6$՛Rub;J%=&xO &hw#M=+ei/"O""ѧʇ Qr$' %|nMv4G{~'<~ܕ%-Pkh~/ccIlHHo{E$.H6Ji_gWY1?'ƿ+ KGk=x'n՞K1˅iM6>+r IDATZ[!oJ"`%VFG[tYE.#`'%GU2sӶX$-w2$$RFcXxag-?d<, bn6bqu{ʬ}Z\֭'XitVH\lvߔo?LĂ.d|cv1'Q1rB6o}YrJjdrEf7.%:%Cʛs1McEޜ*AŘ=c3cL-zY1gR1rBrJ+ nܑ>_^u+BU9lCEQ^EQEQAr5F{\\ԏy <"1W3ˊ1'Q(󁬚ПkQ^{mw0S:NQ%Rm((JV4Jy=0槯V' ?VŘ^V9OE|YRxV3;!(9j{EQEQ24)ӑ:N?AŘsU"#EQEQEQEQEQ!չ(((((rT綢(((((Qۊ(((((+Gun+(((((3Wcʕ{Zj\;߶mϟ*EQEQEQEQEQrjժev-[x!k׮]v5O[WމQ邃qʡT޿|cbTك1{P1f*AŘ=s#888Դ$ٻ(>;bvņXXb/1`-{oK,&j4QcQ^bFXw7RU29cwo~;3$I$I$I$I$Iَlܖ$I$I$I$I$Iٸ-I$I$I$I$I$e;q[$I$I$I$I$Ivd$I$I$I$I$ImI$I$I$I$I$)ۑے$I$I$I$I$IR revDv_OIMDpbgBR"e \.IY?̀ř/% ҹex6ZÒ~e%7(e ZN(G9#9rx%[2г p鱃hwMҳcV1&2?󄇇 پ֔(Q"CRmD7r!IC2AZ%rryslB>xTrJuy]w`D$IʎޣX1k2kOzBOy>Bt[F\^;~߯'f jOKeK 咔yxO)gm*=x3GnQHbKb3@j@0k4>0_ǠMw1fcn"SΟ?Vz;AAA\p%Kfod$]sJe$+M3EdHۦs/5 /bmf9YÚs7boQlM0[k"eugRR@u~Jxd߸@kc% 'CSVTrW ##,\N?6|7 ؏Lw=5o#c=)%?rLċAagjEJtuG2N< fiZ`bF>9c|;?/CkOk^)Ed>yc|ƄmWI|Ȋ'[~=n_-o%StfөD0{Me,ְ}{ӠlAhLp؄~&"9K*iN}|T;>Ű73ت[f39_f!F)'Qk+4{Λ{k5436q0Ka77\?AƘH617)eWxzzfxyyɓ LA1%q^/xp 7,e@% FyGg<{|ұi"t_@]$6d_8ݽӷ;~^ڤD#"Lh8wTP;Y1%˗ca7NW78i6zrYD%d?#>/S)]n#'t7+lhբ#ҫhsk! 텥|3%}&%ݴ.(Ʃ1 يgD<3kNvp?*غfnZHDCw71B~pL欟Rx~aMuonh ֔.ޣJ/}o B5qi}hmCfIuͤF~:mK/1w 9eS[U;=Kh$iFE^#z0=>F48~_1k;E,:n: /WO˶k܆f1{q?Wc7y#CK'R`h4?I>kFar3!z޷|UlٲyYCNlt_ĩ#?1ԫVby[UraLӆ1~1T+GO'x2%og{cK|/Gec/Z%S?&a[fmO]5vOiK89'f\G?> u0շ2o8Y^3%F8j_ocsԝ1ߏu|ޢ)-:`ʖ@v sܤ|4 c qWş 3(khfĂmw~2K t1ןҴU#kU@.Q 8+o=6PsaOS׽ n{`Y¨mA @Su *yCAq/Ѻ ˘#qoUޢ>WsjQ'Navy~CBQEpq4Y\lxFCω}) Ot b\PFKjԭyv<3 e jRZDI%&:ğX-)kZj g-r;1Jn9h3IVIa fU‚*C:kUʋ!ğ~SӜA8wgƑe޹{+cpձ!/sӚQ?V]|N>t]_2uK։_j2_UX0bєﵝAYr{u2E.Ep΁mIӧN[Tel]؛G7 q}UWOxDǵҩ=ǭස[7̡sߟE.rCRvT~Vp2n^-VCMZL-3ik~u[2/@$ Ojn Z #\YJ6˪96k{cΈ17)e/im:t(gf̘1#4lCVzKXs2wuo"Sr0NRjP6ʹ$DPgN_ru9vHg%JVU3Sshuq#8KtLz BˑWKhdBFfڙ=z@(4jQq! +J;$z]i'!$Si\7{.s h>Gtw(&mtWTvʩ :n.ǨeV$OVӢ`7c9 C[>DטZݕocMp9Lkis 9tKt1N `vs#v~kbQDCe)cP;P/ aY.=a\+7227QOyV017CjD-VMZSWKr6!|Gpp!Ѿ2qȋ{q< Ek'{eM^Gh#|HkJ;JשjjcaZT,Ӏ^Gca1gĘrÇ3sL 1|ذaz?qe~NER~#+L=[0y-܍-DGdg<탓 U¿Ž!K5z"3>q__J2qvxJ|ڝ3l\^KlJmfث46)]lcܾP5?ވ%>C;wy<;i~}HzBGo^V]U fz_ xȁ~4]b°\)lڲ-=yƓR?vhķ32'c98VVXYYaeǂۯӫ=%Gl@k=#HW=h ?Ghr 1Y*,]qs3i@dR'߻ E_{o"SU jz$g}ZnO;)675%}Q^G)CM(N}Ջ.{5agYue#ZUx;X9Qci⢣u]N179!4P8t*Dz2@Sנ5=eV*<:$SV0aO]$\v####m]gn1sR6#Gd;|pL%Fog%/Nq3bgNJ*H]HpM/lu<0[%>)3z/&Gpm\ymDUd[侲ŋ$ZOaAx$ -S:ecOStJ~3W R;&8@G%$!*4=j1ЅN}}8(= BxVLDә$ٖ׽TUgִ$)sΡ-JQ;=O"y{òId_)mpҳ9QtFФ>dQVs-7nCx˪.$RT%u%>޴}QgƯw[tbu?fW^C41:'7Ę6 [-j_` *0a@?cp7Fvd“2Ɯcn"SRk|;v,SNMFB1lذH^e3ıl|TϺ均UE(Ƒ#u$&3@kz*muS)Ie)d%^ 8:;cyFG^_R;{s-a.+R!FjUR({]wc?oSѻ4R}vQSg*'!5UV"Wo}~AeEx|z*kjcR,6FBlX>^ʩp)UӘ ^N:'%q+`h#VY0|gJ ۉ@MK ,]+PoooՌ6 Q!ũR?cBwE g͗ O9Uļ7 f 7̂8(K) HS~)sNFNx}2S>}gLp:vQjoSg;1$Q8z,CлzR&|&/Y'c咲KDORb.~u F.WD/t(Sثli)zNJo׆g9uCI/ҹNynǯHV넆L:э8;c /(tfPY|> c2|m'|W3n IDATHTQ2֯2Wm5L_"p/%ogt*O"dm=/b|*\%-bDap?Nt͸4b@J\:.>bL,'>8{'k[>[փЎhN ~5%b~sk$v-ܮ_GR4jX#UDZΩ ŭ x|=(Qf/C+2jZnQ-4j1mߪgj|Ce*LX# 6m&Po:3dZ VB&Q\mfڈycΈ17)eOFbԨQolwޮUݴijڵKWaRB! ̈́iXx_%Z2[̺B!~!|= #caW 0Bw]̨f(z1+}cEvXė[B>:(h&J1NnDn ]BB^dy&67ďmM1ظ 10w#]^.j Pq'QjsovqYޚLh Pe^QBCkq-2Ѣ0הv\>3ahd#OlBNrENkl  Pа>X<~Kuܧ(`e$ *ab"*4?Ȕh[zF˵տ(b#U*ah$<|{{}V>tw6EgBcBhTjab[XTn=BlFRn K S0v瞿QCn~yy Gc3ۊjE텩Z%4hNbEؑOZ8 :p+.& ߥ"c"a׉F,y~a QAGzTΖExZ"N?MIONԘ#nUWhύ+~x+ #OQLi,#{ K#04wk~.l$%Z일B m1oo8%qiX[QAi Wh7vO'Ѕó Hc*J4ל$婭Z452<͔sc;-Fy 7LQĵ/C%ףB+Ő&Fq;No7nu o6ƞjJZg2 M7{9%I2{%I$IchhiᄅG$yUX1&2?L|^F&yt#SЮAuzml/$Iʭd+I$IU|>G .3-]qvtPD|@{X1&2?Lȷ?sݏ-+u*@cn1S'D$)u$I$IҧʴNHyǭDu-17)IYE6iܖ$I$I$I$I$I>Dge$I$I$I$I$IR.sniI$I$I$I$I$I*4ngJJ$I$I$I$I$Ikr$I$I$I$I$IR.OOs>1 2ƜAƘ3sc crCqرO\ښ/Rxq}N^ϥKΐ$%$I$I$I$I$I%<<<8wkaa͹dE{ҙ1tX֌}?4 *{&>o"fy)NF N vsddseݬˆ)џ}g8a)j,a_6ʛ< >㓎M'7= ."|_5<->N*&%6ʱmi[跿Cm84>4!xE3l0&meLǥh:6`;a\97*i4I{q3%=b(wy͠CxwO/sDs熟:E_N1;]qcM7cߔZe,Ǒ1JٍOIʪnڴIht}v%Lmn$45ۺD_YT!D݅uZr`&lBmd)+Fc!]3 DB4QYt$VDUʹBh/M -D5^ḚzŶypq2.-e?7sㄧJt-{W#4ND|Lh$r=21;wc'{qY1pxhG# )HKe[̺_o{M73#3+#N.#4%Ā?k,!UF MċN\:"o1C-Q0VM1 1y7Fpl#l(0nGErUlvc>ף2Ɣb̉rg⨿0N|-[rP uEQ'b/|hjd(, {u.?;+~Ot0&vo_뉸3 Dꥄ0Te88q7w|'T)"LDBDiDXrae1ƘLvJw[nVlڴIUh’*þ6gI,<`8cw=ۖ {CTrt(mi$sq Tœqdn+%;u`\`&OEq384C l Mk'ble~iC5!xtF} ~XR -q/cRJ<;Qwp*pKi}'RW#o19E:!.ʊur`Y>[΅Q'ӫ,XR Th VI~^IzP ;WsjQ'Navy~CBQEpqtϨy9/œDZG9zKM5HX(&8N Ƶu <-Q"׏ϸMtOֺWS>lߖ3efމyW34YPVIa fU‚*C:k */: N˟"Osܹs GL~"YGY:,֥9kFF6i"FPabnezX5i O_8-B^Pb ߏ@C}e BP?|nͻPXQ:\*0.Gh25|!+\yX_VfEl|}C(LptU6PI8=f%.)qcmw3c źUʘ)NI9{ʾ:Pj34ϨsEY@T*ǘ#Ge 1LC/WdwJ ]~[M-$#K1uAz//J;JשjjcaZT,Ӏ^Gca1gĘ̔=Nq8KɎL3fn8Zp7<FIqn9۷ \֛b!]э7p#l?{B-q7* Pq~s:38ˆzqexN?G$ }͠X"7_+;#Sh& deAmfث46/lcܾP5?ވ%.?53hN*NT ;'}k|g|.Gp'p1-6ۯ/[ @OѪkcP,]k~7Q90ҏKLu =M[V\Ž'xr}Sp2=E, +++Xpuzg5 hc,DdY~Uf}  $0M=^+n0a&H9sFB"¯7AVx) |=QsY'c䞲*9zcnsrCipU36s]SkPߚ2ocv۩p+Z0ꀸ"8addwD 2Ɯcn"S2Mk1)Fw. hu T'WARBkzt|'f+摇z >(0/Z|MfSt>ץ?Jj=* t5RGdQV&;_U179!ƴQXTgܪo9Ts|f9zqZ-` €b2o4$(pΝ41s9ƙ_sdX6>*g"pWp:HcyAxD \= 6 )U˔T2}VJ]\8 /SAQ5XX})TBR!}+j #Vܣd/ɤ' R%0 "0(&;=/H[@ʂ;SjNDʌnZR`Zjx{{]N/@^`׈ )N͐zUR({zg~4wYԨ)@W*UW?ԽD hѵS1oԣOj}5܅]kC=+1F6ycNuoU6ဎ\*c?3߿ m_۰L|L}*B\ngHcL00&?EբOצ V2Gu/M &VNfu|= ,A_1LƉ mcF7^wv>O,1} f6f\0 8FS1*ˮ}(U !h.M263|gJUZSX-皵nPT}b9c4`!GrmOS;^ ?ѱ%]ޯE?s. PiWi޼GtAa鉏#M _P}l{}=˘;\qWauu̳uU#V'ޞ;f?j?BU܊:G' Ucנ6Uz5(ws1x.y3Ҿ v V:=\P#|cn~3 Եo..cL=c~dG:Y-[U>/*3C8gDo j5'-L?rxbk<+>0kNy̗59zw}4yNMA79~ƞ{jU׬ph̘>n\FI5Z'W*X_Ϛ6 h:;?/:qectCO?:s4<:г%I~7q!-ֲ۶'%ꡡS6oգ_S{G_[?Aez؋)tHWǩ2-Y׷תq1#0ZwGv_T̰-[L:=FU?-3TX͎k(L*ͦ=R]>tD1HwiWr5^QQzUz$ɦ]F՝v8Ә.6#u߯ج⢃?[ZfS3N:"4jlk%Ourm($!<"vEk5;^[Plݐ}6kޢT_ 4WP2[:՟Cg1BWt4m `[>W/~z^rK*iS\lչŽEziAVm qY)92K|TTiX%6"B4m|y^߬WL93}zUҜiuSڽhӰ+2ئc btJJ?>NӓNy pݕXV ]R`F$UYs>-%$sON?s:MeS]{fұ[*.])Hmp)ج{oӛōw.im#(B7^0DJnj>(U^i~cCcC-p+*n4Xo+Tښtyk}4U١Ywm -,Fw sgy{ڪ_ ߦovB?x|?=4C*.uN>\W| ו'svf/.nqy!{ѹfw Pʐ^1:qUn\YJIқ%^C5]}W1s#us/Kӓ֨L~JJ@C|b 6 M'jHggW5htxV]Z1P#z>nyʻ9^A'ٺdmNb{ԑ!e=-G }zaJ^/$C1}{k :1!@!򨴢Fiie2u챺8]g|Z$zzj]NG+>@JUtœgAv [v1R5[If27t!".kzp^*$J ')K^ #@?˩^RuL]i  ڗsܿgnN UT$I Dͣ¢*sӀ}9|MZzH;&F>-].QJ4$]:8W\j+ 攩RS{owކ'Hu$UoBFhiqt}:[YZ}1u^,H24jX{p~5[[b>dIR:}j锄LP!z0U"֞=EukKcb`땶ce65uѭjbjb.=CJ6^{F tn2٭riNN꧹3*j}nx> q lmKKnԬW7h֫izu)ɭ蚷mҴWg^NJe! j/z`0M䯆4,!H*zSE%4BuFJ"❻SJC!:}\9+*-PkuӴ nͪ'"Lc[ک+); J ӮS q:)&#P3;ZZ +weT,)W na r(0ePbu@<}&i 5f T`[lI%42 4s(!]> G7tYG{46]MxJ˴̦1I!#wJvD8LF:z m/:zuei^K[y0t ffv6u:gt*eś[ےa`t3sA 4\sOP杺mz!ۮG6{c!arŻ`pݸU羶M/m k5hӣU~U7/ڭA>Ji;s 6.YV)#|F|vP] ?5|6.}/tf DCkަ鮯Kd'Oj|/Y/ݻu-o]RH t."gRLxC9M?6AE\e4]1Jj-I˪arv5^i܈;@=2Q<ڐ֕Il+_' WNF^_ã)_FF%UB9ڞW SF1ZyM-֦CpRZ~ʪWo1Pw|XrRqz׃tÐX-\B9͚WX~ᐟa+ ?*PW^<^7=ړS~K4hq}"4S7{UNwjAϻ]Y%zluY!WK)'Ѣ,. {9̀Ϯ Tm[5tq ٹSr[W`.eT7[TA␿?l qn֫zOSŦP?emsI$2Z~DJɎ29%Ϧqiz#o6sa*9Hʯjyig6IӣgH{%ITk{!q)ZۻK Ҁ`iުֻ G.uU Ǐ U_MJ?l$SorkuZAzty>Tnv>rK*FOwkSA˛3NeU7Yjc/6o׃ )zd/S˯w/]ljznGzE^5BWM;gK٥Z]&iw"Hh_gLWdv]aʼ&G*b>P#5L'n=XmR#MI OFEhp\nC6V5w~]?|K3--Sm|f]{Fk>{Po2S\e\unICFcuR^i葟&M'xwfC̚"=J$C!a:dql_sx80[kwlnCkfK[t`^;ڜ6m>Z\ϊ mq1-?0f]vWK;93$aMD{T.٫˥wU3(Tp#)yJ|5WZY} 5vS_}]o]2BZ?; ԪͮrVhf$*(SOijwqC6f*6Ov[}'hJmhg3=ڗ_}ɗ$>n6%BmP]Q +:ȐMI=1ܩ٦6TUYl%iCBV?,SO5Ff[9MC^ b=ƨmI﩮Wf KNãJ<-ء(Tq먌~65c6gZ;J#:;ösajWtFBBTӑ͎*wԶèݝV9u`-.psk=Ǒc-~kX=F_8 IDATBc֗Or5a~O^+ޡq{LjW2dj ݲswS2j%Ost)x4>:=^'o=5,6'1L =Br+nO 7iTUm6=Xn(xgpM(5J./6踾*뢹#EZ=BlQ$]hɱ])tj@m.kQ] Zk]Zz?"{4_lUJoVPm>ZEqtGW+쨵zzp;~_FNM1ͺ}Uk դ>tiRoUhVxjT\\TZfrW85} .mjlpIu~_?mnوYu}tSsӪ y 6J6͜O8 PEJ+Q]1B˭Wfςteh8-PjUZ_5bݷyz ]9Bmv7G qu.ZuCCuƤhՔhMk  D@H2U @N SaShpF_hE2%OF>ݸGyxJ ߌzh`]ڣuFmqW]Z{^xGWeꥤ)>_KskUИ4{H2lK^3xZoh߫Ԫܴ)&:TԪRĭﶖ$<^O]_l_WN-"AO_OR s")}d*sm^׵'_Ӈ=OnjﯙznmoK?W]O/Е&sS+)6_jUƞQ/aᶩj{_vV[sțy8ϩ־h/Ju>zd+0~˗{*?Kj-D7gtC`L[U5q-RXC&uu&O]Hh*}{vOJRh>}|zb} חk7dsG*>P}mvr[pֹTnFڙtǣze-ׂOvmU 3M4Neޒǣ:v+gh^zmCWw\ GMت6zzT6nV{?|^!9.UklݰnV7Vj7UR'$MJ+rV{î7e8/ݭ7e[f7Z7EKSVzʚ Cͮѷ+*:\'$M:e]+rם\,̐>33^a:mغC7//Qdhazc|6{h(4$X'_ERR^v?M 9E'fک_2!C:u1\I~؟pjҭ ?yp]-6-_wi|^xY_.ؤ ItD0!S鵌n)cojӤ$]4@yTXZu 6Lm[]uznMZxOk ~җ; )Lң)4VnN2?ivB)\f)YmgZ]yp](/HO.ւCTS׽_[ӝQ*(.˻iUe'~jƞQ/a^s)_9cƌCZetwjCuѬM6IG/= +9"s{{}a?Sc@=5 3fƜ4N/ׅo)ݍZz]%m̉@=F_sO04ynFdM>qX.\Yf >?(G\fś,WǮumK_}Xc/@W3u:櫮nGu\ڑ#ї?+C mn,p`9F23f8/[L*>e]݄cɒ%>}zW7,\Pf:A7-mLKmn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,ǯ<*--UAAWh9+ߏp|Djj\.M]V7o֨Q %QZZ{};&LPIIWm2MӶMC%v8t6m1;z3 (s`9bGnwm!)6sn,Czmm!)6snnt{Gt{衇\n$Iw}v뮻t=tBZǴ$cYValwy6a=C:2mw!ݮxo6w}>3%!9̙#04M͙3نnZt-fi޽ӟ96鍊njzܡlG0 y<lf(&&+oڎW6e˖7ǣ-[(::ګ0r|ѣi&_^EEE^FLL5zh?m!)))]݄iIC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6rC mn,p`9!X6r%Kx{p{\=G1- rC mn,HMMUii )z8EGGkȑm.G? ᡯ{:)۩r\:zkj5jT=GWRM6Mizs3HǏ_g82xO{p@[@pkoO99{g.Z/[W7CtpgGM'FhaOR=u|:';rk;~];c=1wv,g{pmzEPʕR/?}re&j1Ys5=]ԶnNj((Slkz;t tYsWn(2Ѭg2o9NNfu=JWyn z+UZ>ߺiւ{(Ґl.3.u+_~ʬ)VAy|}1ѦQ";@mpid?u飕u+Ta͖9mW3O$9GV6[ǷzQx ||Mۑjөו'SBd⇜Kv5oK_=kOĨ0E&(9efq Ud 0:e-~yhTi*p7]&+nF?ҷvl7>@')'NݮR_Z}Vްl(2RWغKOЄEנok???\lvjl3raThLIHE}("Ie"4[ҷH.*\bs>9smܸ>>}kWiC%˵[3!srHyP;) @5LYK{RQtB*Rux`mRXc cq˽ܮ,SD|S5kB4h,EONfF/ QjTPHhuyX˷vߩL@bv9-K>1@7ԫrTqƿWYnܗغkսs'sFZqr[:*YMWp2k2eZf13qYEzMlpV Ʋ$"vZcnvGRuҧlR ln[s=4uЎi1.ր.׵h=l}aE}Jeåc[Sto2YKUVOq tЄ;Gur/ieɲ?ǪK1xSόNЧ3*n98R_k{Ҝ/vRyٝКjo$V_-K,In \VjҨ騮ogW$b39vE:}t +TEЏhg2:jq.K_brߠTKԳC\)aڑߪ^ܻkYIDAT/!gq)+M2ʳ_RF$-_&\϶={}] [ie{ǩnK_{TX=NKζwݳ hT?WXU,K C#]W;~ t&ɖ{[*c$coܜ8l;*'i}sKz~ȟiF9t%wi%T9},IvExYCdfeh3]m=',mZ>[a1yWvmMբL5yqi+w4'C+ՃU%?VOeTFNTdvGޡzLSYEz&|#S._qyo2_Tn׀w4e )3=Es Q%z,$Us;7],K%ER'*),{mo _ő̮զUIԴwZ}X5㐌5ƜmAf }=K;7))}#T.VcRǦ(uxDVڴy>[~tFG84hڣm?#~}f]oQ۪6e3F79N%U+¯Q/+rڔu"rB޿j%u{k9}v6jU&l>k%:q֎U!:֑^hYn3 .3Y\$o-8A-خ}NKnYS;L֫kҪ50&R3e{#zPp8ժ֯Frj?ּ{ܠVUfߍE"kqɟg*KÎ,YG=eZs4kp&7uִ]77< ,Y2^Mqjokmf/J6I< +w5|!۴ˌTzeV NuJuO)=nhndSՖU5) YJZW&=gNOۤQA?b>\q̟^ӢÕvH-R'.Ҋ_k`tp:dJ*u)xr ^)(z*$:QǏK7ODY?tTFF1U|*}[ZрMWJTkْ%ElϨJfi5˒iZa\ຩUYEcpBw^|K+ieފ$eU,蘪n}ߟ6(̔jAS FE"+aTܗkroCA*<#itZd{c cydyχyC6i6MiU^S[swr"CQgr)b[>[6.^[%EW+!ƍڒO- ֹksދ%O\2]̯5Z2HNU v[zu[),v4n)˲)z:yvzFpU!gqi*lWޥCkoutEܞ:N)K %>7ٺ4fNhJ'nzm-͜W9E^Brs.O] :hh{|L3-Skѫf馲O)0?7;?5-YF:O}_w#9j*jӖCr5, W]UO Iꠐw_ԅ(`s[%ؚ9j=3DԦY?՛$r`=} ШXIUgzD(:f=A}eS>S.ǡ][ҽWoho?h7%}];lܻ,ljUt)u3z=b:Q*s6t/@wamh/9M}J{Jf} %VU{s\Q.6^^gIRStykɯ6ydZ':{7i'UTk]mUs,ˮ:T1Ij]Zb\2T'2C/zBuV?v+#׈W-Dq}*v?S7O6e{~T4Y*,Ҋk4>&ݹ]fθWKQ:\W%R%%IF.+48i* $R;).ظp>B1V IOeɿLl$jyT>|X]?׍WZj0CVKknCc4i+JTihM R>R5s+6Ydz z^7# 'Q/(uR^.Cm'kejMJգUZ+~J(51ikyá\"[٫znM[_J5tu$Mz4*_F_q}d`M5'f+8 h< "{xAoWsۯSǩʲRrpSd,m4tL}?i&Q;2a E_d(9ӆhgZULVnRQFFŁ4= R[VhE8=ppLn4H wPܖT(\[GO3Cd WX;aæ3S~:ʐZJ*}}7IyݒCgkČ68 :P0oW]|]vOKKSd慏 ?گ7EaNG'bdٴj*,P\:Z;'7_ O9[4%%%5-kn$enIӂM~^Q!ھM"D7?~ou|^{VQ@KG6[*}j凬/eIH2u/֌#,5ohtࢻ K}t}79e(02^-&\\ @ {)1ff[>z>lQ(vhhnݪZj0Xom۶)446,xEX ڼy6nܨ#GgW\VBې\<Mb_sN:W,rEpBQCqs(n|mϡ 9>6PDž4NKK+q5ۍ7.q5AAA9ƚCqs(n|mϡ 9>6PCqs(n|mϡ 9>6PCqs(n|mϡ 9>6PCqs(n|mϡ 9o4/΁$ IDATxwteTB$BBlұaA_(Պ( 6D"]) J-=mR xٙ;tϿlhf P1BB 0{""""""""""rbZ:!>ᜥ~3 .TfDDDDDDDDDD$9N, WNS""""""""""pBViDZHf'؈#>>#1jpus;.t~;SHFN'4mڔ2eJaNd2QZؾc;~ETIF5 m!82g5x4V?V 䱈^ uG,"""" j*,YݣnTT ѣ+Gk_a^%! iArDDDDDn^N/q)xL_sq[b; h"c8 p*a8q8; vNLX\<+- v'NLX](aGB^vQ fa2 Dlll qccrc$$qqfo͚׌աCk+pDe"q$!fT<[P22}]Jza_"yN^e0 2ڗ{\.q&Č'ћNu)_Ok<֟URBbp*6oNu=,ӟOQU/DDDD xʕ+ngqks)V5u=˗/p\3]% w gB|vo}eJP |<aб?^ODm+DDDDf@ٞ/j 62K^/W_?n',̃FÞF^'2JR0NS}cjz=.x7 9b s}z-&hūӧ*xc*Zf ~z4rRݔEDDDDrfXl,p[(=(^xxhq5)#&mY7ŀX&&>&p&D^ lQ KO䈕<[#( T"1g=lXl؉mJz09αrқ|%)8'..Vv{NWNgŻR]׌K80$Z nΘ_L nN-[΄+Mf|MFxI<[aabˆ/RۅT+91Jb϶v%v)Tԋᩗ&)|Lxxb uPp!tȭ΄f0,z۷M4Jdܚ"e6m<-1(n0lFTLf3s'pX6qWŻ'""""*dNvذfLW^`b5i}ӻN=l rɄI""""l"&&//6ڵ65s)nF s;gRaҴ1.foneRPG .;RK扈ȭlt g]լ֔O68noNu﮿vi3yv[4.V+xx''qCC3O!aXWґ^FDQ)- etT `t,{?S<dbWUm+DDDDz~kAڥ4Hݧ0.\~ɂ[ؑ|;xo@F;IG.9dqzۊHj*U3T)IΝJ7x{0fu7Ù|J:8BḔjWvPs0p-䱈$&<0{zӥ?>9\.mjSx>Ng5匴-2E""""ld'NQLkH'1b1%'Yf׌~xfW3q| *#* GTXZꢅX ^)%1 Sm /"vb.ks``µlRFYITa78I'&ގa`Lfnb6Dg+EqQDӋ"f챗q`r |=""""NK)["E|R*Nl&,<3OUfsļw)Oglr9xqGc:Uy,""""8/O<#!K)3.;>loX*Z]=Nܭpaܒ75{24&xzsibc+F")Ν'44B<0Ҷ6ˋx|%@—pr9iU+=)T=xIUXDDDD t{++""""l6AdD.\Ow,._[sd«;&SvyӃUq~q{,]$&Cƻ)ރ+^\0 LڷT )0x/1,x`&"""""Yt8N Ja ŌՊْ|Om+DDDDqqHת:<`2puu5o]n[DDDDDDDDDDveXDDDDDDDDDDRDDDDDDDDDD$ %EDDDDDDDDD$ﱈMvʎEDDDDDDDDD$s~@DDDDDDDDDDn>JHJHy<6.f0}{DrLIDDDDDDDDDґ6Ғo9p?Ay"eC];TkN"""""""""SYH;9=,^WhV|X #m+QA5}&`\u+ZJyr1jV]~)yN:S{mVc,oڜD+Ldqcg9nߵʍ1ܼ^#l*JP&4kzi2x`q-)<]8<- NEgFgqhhؤ [{Lr%Kcayв^UJM^@ǽNdFG !z1C7tl8T:m_߆U,i*gwŰiY>>͎z8-cXgXroo܇몗ykL7f42 z|!K:M'm+b``9&\ff0ݬ/ ^AQ m d//{֦a5 _9iT1[0t iSaܹ{Ffpx.g՚xzV>IN]l98h&DeOSxd02/7a4*z7L>ԼKMJL:0[̘ 7%,0tbXP=ʠ]B2"6m*KЧok0Z%wڗM7 A/_/ˎR^JLSv .>I%q錯z@X^t(NkU)=F$;Y!y)q,""""""""r;ȻهbEMlꥸwH/f>2~ })<8w ź=HRxvUپy3:b =_.`(=;xq6 kbŽV!§^K0oD>8nsG0Qھ&OR.y~gڃCc^Y~I+'!R4.Tn8IsƏF>}(2xiZ',Y۰aH,l~+O>Cy_7"ÚL2SsyS<2g٨&$Z+g_vO6MQ \J}]Q2j[s{yo,-YO8ff}dQso~ {{vJ4NsY-75t:ٵ{~4s9NguN:ͩSٰWJ.Ev8 խ|c o9ҍ灃?om3YIO~<| L8>1cϜMBBBֿyg xw"""""""r}Ҷ$IDD FK_#Y4]oc؈gX76-AC`ї>}&M8%ɩS|B ⯿tRO/66_Va?S-iϜ;w>gϞǟVZg=^<⯿IN*|\cqKDDDDDDD 6k~8""'y[:{f-oyc[^d8y*k:6%/A8*UHV6i111lٺ?`_9_@^=sTfڌYN,_c؈9r(/]-3so=i3f@|cΧ0ROѦMkΟ?ϢEYn=nnJ(Hsw<6n`ڌY'^_VM0 N}m\\4lP u?]:w9w]:3yR^xq4+W_ќOl۱gϱvz> )3sFxl߱3̓)BT\̡ÇRC} lܴHiӪ%UVd2꾲"k&"""""""U<n+#ho=-,r{޳Y}\U9.|rq<or:lٚwbZ\]]~g7>b!lIcCȚ5m¤o^N8y%'*Ϝ=˖[i԰~cg͍+9w)QxeE3hҸŊd2;yjpïGh)gnnÏ?1/O>]:w"r EؗCGhѢ|Lk㧔+yy,"""""""9w-ku%%'sRll,V5[cH˜/..'<;veڻoӥS6JrC+41:@bK&bǹٖf+Ͽ2{ߙ*q вEs4ʸI i"ղL}}^x)Gcqfxz05k @XXs}""""""""7tW"UErL/Cr-DEEp8=1z&rِд6lڴ &$SgΞeiެ&ojt.^\IVf$',s嫯ʕ)V,a_bTf3| IDAT|qٶ}gwNj*s[`mَ-"""""""rḦ<*R<}:emtKqǐ(IɜoWV;vt:ٱc[rmw$QzøǠYϛÒ˸{([4`p }t+ x3QҪbTyΕu{bg7f =srNK[t:_(~޻&"""""""7 kz߾Zh=!>b\8nۦu!' YCDD}52h`77kU˖EX,Lǵie[jqT˶m/kBPPe3|c9p 1~st \;)IVJJI rFpwزu+/>x0!!a>HbH(i!b2yf;Is6mnόcG=oŶIcl-KWk߮- `ͺkWl+ŊCYCPPtycǍS'Ǫ_Vg;vf0dk0΋չRR >(0m!6oaΝl ;v[R-b৘?Mcݲe+'>Ã푫EDDDDDDDC=SVg8IW;vJ*̶Hd}Q_9׫K_s7ߤV}ysD9 @ :u_7j3~̙s|6c˖ܪ?q$lsY|||Eʄn^^_n܌~²V&xq?Zm<$$$4t̞AF -V Pvɠ#R%\?:uh e 8q$f.]:Ѥq\ߏȍn8'm+$EFFfUōPhQ}!mC&^*c^}c?vbe=s3=͚RT4+l߱FJEJ)]fMdzדpC&4,a# n>!0~XuO`ї_@5ki*BBӵkLnSFn汉z>Yb%_G)e5N^il+LwuXrKfΉoL%zv 3gRz-.^wo,eķ1GL&SL&m[bqԨ^-Sܵk7CG<_gjSKzus%"/?3;Wصv8i2ظn5uQժאOⅹҺ$ݩSx>DDDDDDDDғv.ni^nڵmM֭ne%q-ztY3ޥo¸\nN3՟===]&%M0&M}׭[Æpwgԭ['DZ$gϥig₋KX4ﻸp\߹s_7nx&NSxwr?uJ nE龾ݸ{жMb IJ8V+͛5e>&jˋ3dS|>fNN;`6NUf3ۃ#(#n+]6 8 9ܼYSy{2oo4{{{g2u$5mdՕ!ҺU\_o|ODD?_Ą]iLE B9utBcp-PqYQk^c_Նx%Ӌ'RL`4(g QiO^6~5 7Lf ZaޘTt+^yK D jͣc=p}^dCw.K*5KH؏xq"y_ ̻ϴ9ű'{m#g8 >5ylJ\8[6+z=Ǭģk'k5"Wʃϲ4uKZtrN{S4jȁLޗqa\Ø/BBElYdվYv;.{Y?(z(U^ďsF}]^8w>|Ik8toEʁTiҝo#$t'n;MFT"uh+,'yVԪXZ8prۧiװ&WB?fbF;~ {L}?ujG;{1q h薼Zy_7B`?RdA |ާRb%]`^ixywH(W"嫷஧GDʬ?d=-PJթ.~g3QЏ6_ ՛wg^lKRŬ zvs<8UfM}# V2WU B`~#\HN[2w V@PQ7Le)Y"]gA&|;W\nӟOӻN|NHF iҼ wΌOQFb1#g'(z+cMDILG?ͧ}gӗώcθ{(e&12} 4$RE+קۻ{ƥz#D F]Q* `irqó\l89t4wjA*Ճg썜N.7r:TPo.[zH~K͘gRZ(< {<[7nAs~~b5Ϥ߰/8È ,~#+{3~{Rbe4KNguۺ^>>:xb63nԯZ Z4ί1yY SST,乆.`rX+`U-{.ߧg+GfOp*6O?;W]86%;`"dїpkZ˸e_ka@kOq" \н}2}Q3*#ywrz^ m(bg_ xq;3z8ƒɯ1+VLݓy12R6JnQlf 4MWG;8L9Gq2BObT/zq3'#ä́L+7?@b =|Iﴟ$sK2b#8׹Gd=y8#1X7jyNo wS"j{~a= RӮLS}}rk@LUi1z)G[ =\~ݠ:Q2b^ J3#Է_BȽ-(J+=Ӗu|{:\OTv+,:yxPhنcgvzs8h(Fu a_Z$Ią ĹӤm30C<\4գ{+q˙|ٖIߒ&ryHL;+Ӽ]<홺z;16[ժט>cz =|NS!urp8?UhBŪ>`U)(=ۃXw8}y-ǿ3N#.ܠ>p hAVM%ߧ1c|UoIɻ2['^Vhy?f` =E7-[imoLԢA#at`2e9=V)_8WlD[(-t )T&$='e{@*V'ö?%{%7iOZ'9T[[}ޟ'7c8!:Vc5}8vU奣i9kd;ORh,ڊ'WgyVA?EVu f]|wɿe.Ӈb/ӱGz,?E Zq]1bp!sz GE]{6H3v43{_cE/?NpQ7*^ADDDDD Mi6v5eĊK`?i$+Pb*5ձ8Ν!gU )תsjt۞kTGB& Lzl۹cZ?Z|;#1b;_w[YDd cR`yL qGm JB巑;tYRZ-IstzxZK_Y%(%stڄ,ݱ^$O&pranNѪYL&^f:PZ`̎ d.Թ2 cʓ~lZ^cT 0S_󸟟a'TF'Ʌ{9iGP#Z7r"O˲Rm2WOao_?HƲҋOP;wWuֽ: V v aos5Oqrrb- +.p:ۇa`=# q0St~Y֭[OK2K NH:~ 98S>gпn%0:> I#!@Op(-E ZKq(X)̔ IDATŊww)n-nbw$\BiGnowv^wޙ[*C?vye\_8}ǒmI޹cvLS$n*#c%d @ZR2J_G &1? d궾ӲGթYm;̣CAsT9)U#mqw8t?̭-xgX(R:xe's?IO^prF$ *% ĥΗ J4"$f .O>[)c2"j[Ӝ$@W,J`@AJ9klWLȏ"w 7z:`s$<ԍY"ްqr.}cFZjCu0תʜ)uc,`Tٻw+Lރeg Ea-^+2d3dIFB8gi `ȃK~R&r!&͘Qlp^EaOضu{'{V'ʺzhZ9G @ >!2xB_e>U~|xxxz;xgj.:FݚR?~Ew7J Y6pj@XIW76ï@>};Vy;>lU9fDeYjZ3*y>v`Jzݘa+a뼵\+ *ԫ݃LYv- 0%sO&qp z3UdkI\;JOc4 7|(P0~HEtljr~fO&W)Zڬ,%_3٧S.g <3?÷'2 R6M̊Ǹ@~b‚\T;YPٍz5{{U"(¶mf8#@ޟ@ b:sS7fJތM,k{voayо@Qa儓y'-h]ջ8|4Gnh xIvC4:#5hcKc:ȝ>&A)(gHu9,^R+G󯔨p+[(n|+>Us#w;s~;I\1xz64/I|)_^L2j!,@ 'dᡈ gTtuζEB䲆NHuoogЦL׆48D4\Q#bħaN1x§^#j']&?gT@.>uy)7 fX`;XX2L|{-jV*{b4.֊.ec hG9?/w! *4񶒸qEE%m̜I"9Q1FJwtO rb%=ԹH؛ ūrd~<ˏc%}/bO+g|KI.SRk ,.Z笇(6mRz6Ml_%oE%2=֡ RqB{-\ %W)mbNd߬;WsΗJ;MmJ(Gpn0`qɖt(;Cq[9xqKqiS;G/!En]Ƀ^Y~ԛё6FhECVF\y{4HF"o9ǯ\w( ookv(҈_oB9C @ >=FUN]OR3|CO+wJEP`Wa8/pe j7W%ѭL#ʹf*6LS)-qr@ 7LN҅zcTZMg8KKnS"V7EcEx*zL[d¨YGQJlL#?dq2r)U\)kT*N>CT5GU6Y T*vW x^ S(w*TI +Zc7E @ {\ q71>yܰS9L>bOpՙ@#@ @ xI"Q6{Oyٻ85/oe$,'=_ @ @xQkh-vn+| :8:5Ӣ_>'=8$@ >5jI>~,BBY @ > W,,@TRn]EX^8G}@ @ ԫ_cC n ES @ /x +@X@ KA#7C @ @  bX @ @ @ @ @ HX<@ @ A*@ @ @ R!@ @ @ x,@ @ Tc@ @ @ B, @ @  bX @ @ 0A۷ov@ @ gGʕ?vL* ! __ߏOaO/_%%%Ȩ˗ Qv^>?_̟7_=vQJ8, 22$,#,#K2 kAٿn;cd9 "(xw||Z| dL/Q/A/AF]yK"u,dd`Ee"rb,(w&ˋgt H4@O>M]>-{| 2Ǘ( ._BFB%{||:2|I Yd>OaO/_%%%Ȩ˗ Qv^>?_̟7_ wG&|zvͺ ӓ72 >.ًSֱA)[0 zvS~2/$?5Y{ƺq7ڐ^b՘<:Kg;>Bg\>wDɞ'Q]>%#k2jṇW~/eHHD^?]{߿3S71iy!e7]b-o{|>EDd|w{1{37sH.BFQ /QĖ.gص7UgFcϬ}O[ǚY LK3'>mD;S>ͨ^Ӵrx9V+o? Fal΄.SӡcG:tdy8ÅpF˞ @젃3~mrW|LE)JK~M?hk-vn!x]yfyC=}Ȟ|=u)}v&i LZ&:3pkz8z*5s)ۗ\2X"'VZa嘛Ei:` Ǟh>~ c̞J߬#rԙ>q6tK 6(esl݄k~'%^_N!uwww\٘)BtSYI;b\"ѴVOWg,Gѫ-v+K2kk3<1Gςߍ;vB:>~$YE}yju{.QoGߞ,X᝟0IĤ1^trA[=z".NM:^du= y;ߘ8[ʁX5߾ϱ}/ȇe-6!>WOx ߺ!T5%uz8x7sj;8|Bd6>*?@}q*HϢ*"r &OjI8>3ZJ E.'//#_2b(~/}|VTreC(W ŽQ*)-4?aSpG⒎\g:̻/>?UK[;gF9yo ܅yg^eϥ*LԜ6_~Mtl)`s0rac{}H9x;;Gj ? :lz)?b]ˍTZD+)kR.EIfA ?/p]; sN.M88bEh4$jd47RчN;c;ҟ~.N-Gbו)煓#6y)X'^|#{&Ӧr!<]pH~xvk&ܨ >Ol)\*4h?SR3F4)K>7rRw"hCy]NXX\hz 52rIwGI\8xSn?~=^ÿUXQ<\vȁw\&6/ JgQj-4 +ɕoVJf"Zl g7pwv)_'N|؟rtNʃ;yJ5˼~o?A쁾bne]i"^`4'&tdt]U#qj7leg^.LĨ#Q+<;b"i<}YlS Q8 ΘڦD)kT,bL灭cn{z818vņr2vj5ǖs+'N"'ȸ/ec[ꎘŀ5m0ipm< cEV9īXcf]RQaX\B2JDw 脽Ś8!k;cnemaͲM -vô[߆CԨQ]1FkTۙb[!̕(PӄqSFyhW"mQkꃶWgN?jpb Oe>˙Qr.'4&}ײ!tlXu\:j68q%8MQrӉy1iB +_ߥG{׼<8{҉Zl1h1}wξW\ەsufB?0 Wd݄4A:4xzczyP}B ℵ^%a:㣅G{6wQUkx`:џcЮ\dm̕A.hvlU6Mvey 6Ij%.E*h3x1 {lV&mcCwMw]Ԇ̫Hj_Ϳ,.ع#0b,k̯PE_pIKRNK/K|Xd+dg Ce.To[\> ( ;|Փ"Iw0NU2n_ޗMWJ >›XЀ2zN](2CF(q(NɒR19Jh.b:Q5 c3+uJEfc;<0A\ULBN!=Ϥክ/gqVMrYc942\sѯ G IDATcg<;ݻ̛ gc9UWj^v^QS2@pqR4NX(f8m[P k(ޗ+Bq|7cƝ3`6"',#.l(Z֌]OxjoNLhCX0cM܋AKiGó;7 dА y G}{E6S$=?IA JodsNߵk%ݘ/` +N8~Q(Qo}o2f>-yjLjܝtL.^IaMtMPJXԢ&]y tDb;(rVI$)e?Ըň#f2WAk}_?5&-Q#^_AhéU&SQsB ei(E t޶2%Uع^=Ye1[WN~fjCҴ" { Nɉ۟豥M#_:{(P*U(csTͧ~.߼Ī1d'b97kGܡH[VcwQ˅1N toIVF%^94g< C)a Fy5z3wQ4oM\T ̔yլ6րtnZPf`!ut{$݆%-!=5ekKWAz/w.O V}Bs{8'K8* zEs(0R˨M>ӗ[ e`r.KM瓾|6y ^9dt+m=1﯊x𘫏pWh}QHiп1(Lp2ʢM /Z]L\K6dIWک;/6=^|NqLwiA21֘aBo$q6vVexݐQ,klT]b Wb8AVЫ'Q.YN̳қ+UQo: #ϖcܒ ;qkr?SGE)#;q |zEf=¢0:S;e8>*W\9k92PՏ8p8۶y_tͯ?i#9(Tv4Ta%]VˎzNB s,+2sӧĽ?׋|t}9KS &džo)-JKќ4p|yUGkAfN0'ryPR ZuBskcBɜC*mQy( rdjSw2tx-kL]-sAZWxs 03 )sSlQ ҝ1շ1e]ɫJ3 C [s/k\]FyKY|0Cɯ?N̷c?6qzus(V2uKL?t-aAgeKm53;ϡ1o"i,eYðe9n eݢc>cwл\0dTxc+GIhۦ mZFy$ϒ,}Y^j:1W 4Ӕ.?:=69XO}HδNl#WgFI %9KV*A.NȠOhfӸYê!XhT%MIr$IB~ o{ʄZ4>˶R?xΚ ԇ:3ZȨUMY#9*Afte=mG&:&SɜVrϾ"Պ)gϬzQIMLT0gqݲJL}W#0t=X`g|݇8܌{r2Ey{h.zi$ {ױď Ve~U H ug(FBұ]H&Aaazb-n3_Ռ{SƏXM|rky=JТ-M 8U&f.%6Z]-A [fcZhr)b%}ן0؜zPPܷ6i>w vq@P L뛘%C27a˴ٓUZlL Gcs]EåKfk &RƷijʇ`pP/6|@n9 w_|,uKj\(^#B(γ:^Ztr,MLcnūęPOՒ!=.s.bZr$! N˿A"7<0|j(NZ:H'q?̼J-/mju^[:/*0i<9$b%] J)T[ 'vmo\l ؟}ڔR+$K̡tֳ.wtw e]x\&NGQf?x.x>}_fJtOwp v5LOSw|4$e) ZsIKV$#K[ۃ$ie~-߫ 2QfQqktLS2.\(%bB)uxāg\啽FA_qQ}`>N|Rv9rx{aV@ŕb#ĂdW ؞)?+q#$$F儫#<~3/m)]`RžA(o־$*v7@l$.H'+G긓ObAqF%liQ)~̵Lu2ťW/v퐪#6xXix ]P_╤deկUs$[ϞfP荴pу|5W]rPXg O_et/aƓ3/SΠ!)pTzG}913xq<=(k.zeNԟvs`/W&*;Z5FrzPmY⢣TN/VR{}E~v_/FΥywWeNJ6JqN /\䑲 g`O}Bj{#n_&Y_BϲtK k*~}y;Z)t.a/ɱέ@Th/ $0 hMȥ,)mS YFà8PGzhk 2Wvѐ8彰:Lcb [u P1RhɧH~͸pgVL 76+ztdntNX؛8/6<'(,sXP/ #,pHx*eIr/: <2(J+y%I ]Ϡcݳ`lcY_dD#'^qBNWo@!}}#fT}* r}-7TW {[[*X")~%À>@BV(QA1,P(PFNO=q 0BBA Z.UML^\ر+ஐaAV [k}I~xBwn]IU7a-[OGva:<l܋l=a+I`o@M݋JݍAֹ@N!#>]۞j?chH#A4؇ ɪe5П3?>%֜>k9)wx}SW4؄{㪐ȑ"$e} 5 ,A^; ~=҅ѥol Ǐ:1U@W6c>6f$3kp6tZ׉2H=ayF %Ջ9źDݬ\8_Tc,_۟2 : G )3#SVE]|6xޗ*Jˎ_P::yװ<Z2&5-fv<\S:icNNi=c?QcxFUˆ;tHs\ յ-?FҎRq(KT2_wB'mHG_:9 ؼ|!f}G)%r:'8hH.o2\ۍf>ԧKc5SJ{/J;sײw {btۧ$Iݮ+cB>)=sljڣ4wiK/`QcUxDuTy'<~tZ~W L遝"Z2o XC:st+H*Z~GFsWy:YK4Iw ?ȓuL7JOf~(͢9?3ǧL!ΡtNo6T.UӑIa\(-k_w\6 _Ic\r#\ҒU#s'O+AP1B-}*?ʪWUA..w2;REf߲~,S!#9 9 W 037C~Igk4 I},+L/ wn}ȯ7 ^xIz|QV<ܸpNgdR+^X{W_ߙ\1O49f%ddQRAW`f?+J[7+6~8S|,@`̝\ymO,Ck2cl*я)j7A2hNˆUKaJ]lHN:ýגᔺN9\I2?cjv}hjv_~=Nl6Ɩ|'::WI-G񣩭^?m⑬.`әq~Y {:mJ(&fx:_N{][  ΋mºl9V\%9j֧%{)N/ # QPT~귺c}d<_sΠL'҇W)$< Gƌ TyrF2fFf!g@ ؟8GJ۔8*t=^%8{GDN2\I7ѯzze\ם)b2]q(}Eq_f&FH/sҭҨ|a)㔸q?205-E._/ sTE6.[ĺ#6 ѓ+q*t{'$kW&sd=ڿCq>pgsdohLnUzdLӊ|_’JHW7˕ϻҨH\;É*6nH"Mgi,mry+Mϖ2o5s;OcOELF{0Q.>3$%~Xw *!F/cHM|W'3G'g*W1<[^}!v%]9>؊4JA(jualo(P䯦p_Hʟ|`ң%v:~Kn'{ς4ـ6S1Q)>u/~]\R?(\$75&c;fldD<dJJwZ&lH'f+EdG-n \\w% ,(=d#[&0i$/z•sv=a6+ub|<gt&sl#}pm=|=aɗmcAnshR-WuFbQ:rK# WCoc,'MdѴ,ʣ lH͔3l5U,F}"AQ~smri3\~nq_LAeZ-\1o9vRr[`mi0bJ>F7sBuqw;xR|g,@gEڥ)L#5F/] a(g>kz;wg&B͙cXz7 [ ~&Gy]gSawTk9)iU뾈e1qOAeD?xY9q8?y-7{_aCUY=n΄sG/J7̂ `&n[RSl'>\Ji^c:GϢCT|kF_\i{Rz<wO7BTh } =*oWȷFn1oFOq?0lxd #n>%Ws\4Ƿ*YάEX2l=BQ MHOw q䵾!el ^I~5R'|*uwֺ{藯9 w(2n\5bBڱɎQVӍ攑,hRy WQ-&eވ}aIR e]})ߥZ(I_䉹nAm_RH Ͼ؂ eOIA{hH K7ry84%?Y#%eםSex^֋ynPcew긾}gTk#Ѷ2fZ7w܍Sv3CocFISWƘeB&].ϔ\g2iǙ*\J)* xqtԯ9/*^1|GMxϤI;oK2.Kד7xp~YZ/h7kKi+K?IΙLicvu˻>.mdybLN։?mM։8S1>SWƘ}k2rI\~Z?I{XId׮rVNL2K&kד:,~ L_qs䛿6mf8L&`oz#!S19Y)5Y)fcL!L!^cRˤ%|zkL&m8s]WիgJ1RRx㠠iL{ I$%s10cB̦c|Q(\Җgy,cL)I=y#GWiTN;҆',bL)o 1BB2F)rI[2?ӟ)汌9k3XˉcG  ~W$I$I$I$I$I$I$I$I$I$I<$I$I$I$I$Iǒ$I$I$I$I$IR"rX$I$I$I$I$IJDNK$I$I$I$I$IcI$I$I$I$I$)ә<8Μ.t$&S"I$ƩN$9^$ILHǩŰSKiJ(|c[\gҞ-/?t2)*}C<&(p![OCiY*5>|7slՙq!kI$IʌzKs%TlԹs]ćQƜc6XF/#7*J%jK;vOK_~_t_Lo=ejx陒C0ph4]=~03l Dfhάv s!C6v}x } o8=i̹&I}}yD ϼX͉֢,a80Aϋg4xA`a.jqt;jXdh4) a){>٣|q)7S-&w /w$LgcL^$)b+Mrpj5M-1gM)V)6oX'tH~{wb]S a^e8>6=?BS7oP'B~!:gKP[ ߲C"L!J"Zg(F@KW?Trj3p(:?)TX[DJ$/u1Ɯ. gE0Blic+1.~D$E|³BDEgDLi=N6ct7׊9T¦\Nmз"'2EKt7%'%tԉK|lHsbkGQ zQE(:NYjדX;E|DZoR"6Ĭz!A Z.C8>2^ !Yo7oh99oW:w{ӳ S!qb\SP0C ax$.skQRl/W׾(IX9ft1kK^Y@xh%n$DžnS`1v xl-}8SkOқ8@a-'wZS4[uu5F]XM=QJh\E^b7㤘 EEИ GoQx*ګ)KlG0~[? J^;QRK4[uLCQ{z˺d@uʃ:=peQ+D< CrU)q/QJ0G}H\(1cKS%m\t\}j̢B$*K=O6FuI(`d!p8o)_ vqQԠw$' :Etޚje=UjEu8!Oq^#ٮzE g :sK>QBGtTQ_Z"f*}y: Zve|֑W2/@dec\u CZɞ͠Ž :kWJJ2}t{(.\A㩒Y=ΊX$k0 u±|waX.3Ү3ї9f]5E5IMJ %9MiNN0H^$8֜ƶE ۖjflfYw̏3;/'TĆOA?ŵQj)je꓃#}s֎ٔbR*h]X{2w7PL]ĕC9nOjI?Inf8zpaxS y*WJ!'NP14V XTˊ%GV{nd@Ē>orU[6WăB?,Qt)VqʦX3 hە/wqq #:ŘJYRV|sPw\֛Qw;mS In%Yza@)ϊȫ[̢YG:2'ܸ%*:Y5.5U0^?cܩt)j! IDATG~Y:>sbWL,8^gEǾa7)."Q{8prHIrcW/&e| 2.9/ fRaek X5y--``%K#{xwf !T8R=wnx)xJxa(=qVٰp/4G!T9tSE6ܔ1<@z֓|$#E@ Ѿq@/ %{[%s&@<•GJ)]R݁MITeB ÷0_J# &A G@oƿ3N7֏۾(t󭿣{|*rUlBik2}ǐ)hLRcx0$h}+Ю/[1'g>5nfR5=xTǢ1ҕ5P[ իvm?Uߵa@5\N68)x~qn~]Ɯc6X.%/N»VR9_Px<pӕO DEFy't9ws` h6жrv=MQ꺷τ83x5C/A5(C>t'`>9A3jEhFI ` C]T6¾1OpxR:Cj-Y~#vUpΪ956CmIW-S<`&nqDw,f=bc{m)Ztib FF{hƻS!z4;V))[xAVE0hf/u-GGGuN_*09$OWo%&dR]?;ul]z>Cawx#ݽNΙVgEBH sĿf7P8S-Ndzę>I^KVd{d~f wYEo|w g U}#˦",yx >~yP>yߘ9{!g;iit]]b1gM)V3æF~rG:3'CARs)kx_Lրؾ<ʄAO$c4ɻ%S5cN˪sh wo2CσQy঄sW@_dTo(լ6wB/X&[;s!}9z=r_eMkO\î7vceܻp:;c'GS Pړpir.Mjmt_3-%{ =c\_ڛQҚe1u͖Q4k\  lsār?6>8h|] hocTLIm*:([vxxya{FޣPn F ">KW@ߗx' quaگc&SX>o*"~Š/t (Wߝ;~n!ݴqԨ)_͵C{уH%e)9~7Y$xp *Sl2KM":g*+ѥc~"vMaoM~sr$?CꤪY4h*{3TDSZ:!o {ūJj>dTᥴ&ۓ%33(119^l%KCCG ĹX rd̬% qUț߼ɨ'|ɘ_5c6X%tx%oɯI ɔ^ӌiFӲaV61[1d̑o۳kDH",̅+܋I \,c8} ;Sz(Ajڈܹxstn(շ7E7MGLꐓkn K/n'>fx߫jCzb/+nS;, ͣ(Gv(o8\tJ`֪ ?EJ&lg. ^$?T9Z!'.S ykb" 0E񸲈U_¥Us.?+xc]؊'gQf"Ļ4^9l-P%Kvu{ɻ6K pk&. ǔ Gpﰁ5ΝK҆}p1sUގ_ GV3䉇'njRK&꘴'FSR<&eWOw硢 =UeYoV ޙqG:|?6\A>}Q4QG3^yCJXEօ,/b;VZ6z_j+Wȋ%$(&3c`RTߑ wm*.I +$I1"ɥt`x~I>"s ~13țw04` }z!?a,}Rch32;mtbF)l'޵2" t_a+e̯d͘M)Vɘ4Pb5(ߎGYyw#Rj {fI_wy򩔠T%,Z]?jQfEl^Cu^Ě}=-B#QXQZcWFZff9~~lʞRb21*]A)0-W4!=JHSr86i3t,F`W[ghXzng-=dv/qpm*Xع`Yj/g{ aBn@X:UC6gXԮf4wpFu~ cVý0ǜܓuh)fC0 4Mbκ|9'( yJM6aj5g,;S vO_EV6xj9VVٔbؼahּyWr)_JڧHcqȾV5z0ىcG'>)K$s咹ky>#S0M!fS1>SW(eY.iKgK1*HS} ~#,|X+&c)Ś^N;^o O  B 6B AAAd\ߗBm>潃&Ov¸,&u䣚$Id!I$I$ehlqv xD3<"ii 2)* ` LKۺP whÏk $I>C$I$I81#)݃3<"vÃܝ6/9:fSUz|SAGym6/c!I$}d!I$I$e5nzF8;쐖{1gFi)*}LTI$I$I$I$I$)cI$I$I$I$I$)9y,I$I$I$I$I$%"'%I$I$I$I$ID$I$I$I$I$IbuY D?t1Y$I$I$I$I$I^o B `x B 6B AAAdtSO`m}>WVtN;҆',bL)o 1BB2F)rI[2?ӟ)汌9k3XˉcG2: F>B$I$I$I$I$IJDNK$I$I$I$I$IcI$I$I$I$I$)9y,I$I$I$I$I$%"'%I$I$I$I$ID$I$I$I$I$I<$I$I$I$I$I1ҚIGb2:%{f0a1wmk2n+7#m$I0ƩN$L5^$)!9ϜdHS#aoc W&C>ly{1gM)VC}c͟۵"SΧ^we]#v0 =gHݑ_pa>.tgY:h$k1N ٿϤ`0$IG!8cbBMekX8 R+ssUgƅ}[ SW$IJH3'Y.G!k_-ԟϞ/ɧ4%d`ΝژFu\Ɯc6XF/#7*J%jK;vOK_~_;E2qU7O߅`\FrC-緣_?2,ž=[<j̭])ۈK~'Lx?Y7н-Uׄg?1"}.x7#fANEQZXZPoC>VFzئ*As֎ٔbM{ HQPƾ̙J+0> `6ooƥ+Gmc( kL`TZnkgFMTg{nW84gQ4#9㜪\ƭ֥k`WzVe㴆8*p+i>{c}%FHFKٿ%ڙ؇Vz߾<:%{ u38uAU gz*[$0}`OD9s. )ßҬ4y2FC/\9;f  uUƾ+TwY٥>_X\p̹st*3;/NOi۵ܷA ̣"`"ƏoK3|/-%;U Gz^ݨf+'"9Jo>_>n*𲌷ͣCjց1u4]{+onTADtnڌ_D7>ݲ]^()4o9ȖY7='֍`xX!Eiqʇ납MgW #{y?|FPmr~Aek-!2sxKUZV}yD ϼX͉֢,a80Aϋg4xA`a.jqt;jXdh4) a){>٣|q)7S-&w /w$LgcDupcmWdsju-˓1gM){JIa!GlOW7:mEL罻Dpop02W{LQFXy !p7^m!}Z-셇oY`!&bf%pg-3]W# + ax*|SZx9[ F8zUO qI ]*h,E-Q"%'6[Ytq"޹4?.4BS{31P?% f6YJp@Eܢ_DC)Qti-x]n5(V=颂N4 I3{ [qh옯~eu5B]~Kj?F˻3;330Xh|Twq\{ Vs0}b4-D&(%|EBrVؾ7+Y8hqft1a)(H$Z ! DE|Rc-<66\zݎ&9^ygDb^jHN'aV K/!Ƭ-qzqgIUePX#|7:b>k咶L}X+ǯDww^KYyfnֈQwكcCb7D!w+rzߌb/*^Bcf&,Eĉ'+qghU!pˉӏoNصTc6XKpo{ŽS{u]w4/mؼaH {* Ck;l $6N9э2;46=tgpĠjPR@n#{Bɞ=减EYI$,Ǐ<iժ zU~Z^Ǝv3ƶڐTPV;Uת՜HBGĽlZPAFM'(j8KЩ䞥o-<o'3˃?b*47r@Mro[ON dY<ީq0 Wa^r)Aij0t*G^JK /Z Cy$ч-cƞ(/J'Q%lMJ]X֢&nCRTvQ1~ ?8Q{bR/ YD<4=[jת]gT>5ɉ?blQYrpwM۹Y QQDiud0hV~ڽ~~YG_$icKX5J$hvTi#? ٟ~K@VKyVc,`Uٌk?}Șv̦R@뒔-dګMe"uÈ{4OIr;5ѫ ǟSzU h}CNb^#q)ٍ%.Y(=]{w; wMݓȕΥhҦ̀>?Xq{xѩ6)݉‰Re|P̕*noڗ_0`0P0u9TG!T9t{sA 7e !1`XHP?SyȨ:)ׄNݺӽ}ujFUksCNֽa:%i=7fݎlTGEh[kh+^Phʢ}(lϊȫ[̢YG:2'ܸ%*:!Rb*e ʟ1jTE#r,?K9x+ g/%ÊRue `JCsGJppn\B䭺.9/  3lm0&y did! Gn 7OCBXks->hz@P8R4P`Y`T-_}Q0:gģ+\y$IxMITeB ÷0_J# pZK'%.?P[N}̜q Ͼ~D,jN:N^Q=>S*6NU 4BcL/ǘ%Y,esbW+Yz\B5s{&Saf&5^߃GE1maz, JW֬C o%T۵T^~֧:yp9yP.zlts֎ٔbR"c8u6 ZuK1C|Aypr%OW>1Fwx5C/A5(CbjKeN YΟԑ,R+kRb n[gl: ra|] MhH~L9Ѳ`VCsho-Bʃ~v[:*Mb2s >]KX }dy܏Kgefr]Gq'x b6ѣ^/v<1ؖE,F߰htmֱ0ط;9ql?D,mLj1Hr~qttѭ o)ݟ+mh8)1ϵ@]?;ul]|>Cawx#ݽNΙVgEBI;gY׷T (,qg҇ҧ*?m\Ҏl3;笢j軅{ 3gWUeSwqYU?πdPP{-^f*O͕Z^iZZY353ĉ9?BAA_/z||=s=ފ=r+N`S9ΣU K:{J̭-n$sVU$+Glj gJ=>АX_.4QY݌hы oDSnړ>:RO[&ӌlxR!qLFu:xS{Eȡ(F*~XGq׷qwEgí`xƦ|&3ܲg{X-Z|j|Ի֮OldW"w6Ԇ^Ӵ|<g37g+27WSL14`Jq='w2.RݸݜIsqx)[J]֍m0 uRd2@kMm;,[Nb61 BCW4ʅ gܱG5%\QB JNQ)\֮[w9#qu'> Œ}W]͆֞Ñߖp6G3WyyiH.unjrNijt ǂn$?\KZk֓+<+~m`d۫dz[xY4ξX|8'B8tڈ}C$?P:hbyB1(xDNr-qr>3 Qh0|@vU Gϟ/OT]of7C)ґïs9 C$1gRҶVQ3jKu$HG.CYZaIB⿴7snI/Qxa)u+):]Tojo#GOXPAAA!ql:˻S >/FBfv{yas]>FRoUH"=)Z#;;Ns \]5h4Z0FmT/D ߵm"[|GfR???*YOnIq:))6gE*zFs`nm,)W Ms-x:#]e}B׌a/t~>Qzv.k2m[ɺL:"zҔ{ȞJzmG0yx]`]MσE4wOfKXWe^t#>YDtΗn: [}NAZ!8ccvNQG9{$B ҷy2ۧOq9oZY==n,l['ZwԴƚ󸕧 ] d7c&)#ŽF!ҋ~fz [{Ҫr.otJ/04kƧۚ3NENfq>b ]*qV X/(O:Bsm,Bȼ)O|}wYŞ On17n5u TdtX@uLh<m:U*[mĹL9+*JZ\\0_75#%2mwVpKbGZ(>wC iAK|XY%¹Zoz|8 ;`? ӆwX^ڹc;mgxS ϰ MX+M9t3N0e6פ iE_qob1{O45wpIk2Rg6[N|KWۯѲ8>+NۦӲd+G|k"b-Zޟ3nb_^Ȇ'#9zWʇ)l6d KF]A{o3?u#+^7T0{kmp)#mMu<|_'NԵL֮+[Qy3Lo]Iw)P~G%qRQ No9!ŪxW4t;@6~!潆ѿlB fQ0F]n9NP<~S ri=|C֦P}s4獮4kTkRû"V~Nw߀gTKWRzeYQgt͇(=ew0s&oZo24+N~e4.uo2{J) ,1p4Oʈvbp"dB$]jg%r\DfViXQ~ś kP+<7cҦt+;S Oa*G#Q6x͠+Ɯb,[P5mWePjH|c)75"5=R/>u ?4.A:2T-'"qXxd: 1g JHFK rzKpfd䷥D{P]AWx 9kZٿga덥U^g21)ũ'OU+W%#RfmF!8C8;FF~)Իts KvY,ظBԒ !3&9."='ܽ}l٬ӿC%GD>HՏ?J̙;x}l,@PFkb4p$SL} ѺS&I!CV3Bd ҆Ϙ丈o0er"靚煮/ww<;,%$˓%1vSCjǜb/*hqo:Mo2Ҕ\=#!ﺬPg! 1qŌ3>ӑ4v.5(1gDsVU V!B!B!E:B!B!B$ B!B!B!c!B!B! HB!B!B4/TM5RҖ-[Od !B!B!DJXZYhyшI)N<. h4ZlB媩 g]:>:dLr\2pkԅ'ͣkh4rc{T8S/ቓ9fVN.Vҳ8Oϰo}`p wV x#Qr)[i4q[Vb5𛕜} M &ܱx3mɿ65"C$&b^E3)'0q-gC͊'ӻAIr9Z`fnWItcQo3j{r_C0%=]@bYX`z/[l(>O`@qؐM:.uMv =?#įleMa/%>oY:Q7՟j##Ǟ'Yؿ%}2XYCrA4ɔw8ߖ4O}ŗ%{؎rv}Y~!cfUՓ fuVN>T6cxFʢM ym<1R':ͷW3DqQ'NV,sPԿ#W#I=mGr m d^ɴ| vfӢ4˧OpR$2'tq4r _Hyi9] ے}d1Q\2kƀ'ee&13#?0}Opc4Vnx͊WT0koGS \"j߾qJPH~?F+?>uw/s9b-k/! hliA_6UG J̙;x}tf`" dԖńqQv,E@ՙCph9M5_aӺ|t^HKCIB6S 0qtS Q 3{ :hOO>gqA}a]ai+sq`Z/z5o]}UҼvBWVG[= ZwG DY񹸼+5oޝ/jlvXQogf̞نf h߉;ae<<D5{PJbٶD* z/}ֳZ@ŜEG(m s7p_H+Dj ;4&mp2p ~_×y~|ÿ"Qt [cΎ\7;S7Ďg7"["w"ch,WxY[64mt4!':"4ғe ӖOG1xr|pW홮/# uD<ĥTyh@k(n]aqu꼰`VvOm7u.Fli^G__!;SLWؿG5/A8{ A)^X2-5-5s& ɏtdFaws«^z(=Ϥj͞|;?0q(trxe;5#\nP] b|aB׫uo;ֺjQdӼt}q~h w/biQy/?\'yR(g {¦HT4tqvc%sVUز ULT|?ׯQw4H2ϡo`ZV}ѱ^M~=K' SJȍ*<i'v?UMuijSl(?WMu\Tn$.bjooJ8bo?ŨȨxd3W?[I7T91^:˭٫~moMLҖڼRʤm 5մkb13U]93ZƟ7&ZC"ωR_3W $7 j~SWeWe:2a~/r6ڥ9=F3ة ^w^E}T?S( C=54xU`̹w 1?SU]3= G5T᪤6^a= B9씅Fe/R_[O`Ę$?BTD[Pv?zN4_VVwSTqƫԤQ*ʜ~f֧| ck n}?tQeSk^rWYG|P],̔eMO -aFuʫdKOW%UM ;QQ> IDATkk`nB;ݑdSsΉ9*af{oa~S}Vye6oULu$}uDsU0Sfn·Uԋj }~tj[rU^Y8Q:NQx0j*+6jÔ;<յvIF C[ItWv2ήV]w_,jVnvLoPvJ#[ҷwqVP^C[Uƽqy-`8IcDKV-+S.`.zR-PUzse쩊UX=V*Cg76]SMj]TLiKDQê(Ws7S9KP#VϒekfHv}N 8J,ȍSPM]| 92XլCRvHXA9Z6SJs֌5 ܩ^syJ_^G~+fplB 4T5df$Hh ~L]Mlski9Ǐ?hl)ظ//]\˺0|_TIeQ{=9ij\qq:()]Q*IR\69U@i{Sڔ2-hQ*۷76W[F~Q=sY|ƈ./>!윿|bCanHsx{6/sn;;,} s1/ߔڹ"Q1:G9՝I N.MjW.nOMj g߱Ӝ-]9N . G =9QUq n5)f:BԜ™A"#"z"u{|9}M/3L=SϾ|Q{;4V 0uƎοoԗߟGc#>g9qh#9~8S5NtkXkeKy6;Ne|¼5hэ$ޏu"ϰe g歬7v nM -9⑥|8V靜4bfnsv=JgcL?b|p\%܊s 0x_+ҵOx "i8 E>rj~Ò[3'7˂o0tCa,!{pvbW\S\OF6>>oK5dƟ궏ocip5І2se14+a==j7cj=^׋oĜcJJ֥(WXςsgnOa-8C ;=seU"E#oWҊE ؿJޯαWLe^>_?L%qJEm,u)6&3j[`2QJU,ۡeí`psw]PFg2n1htyg36G4*ZOBR)jf;s\oʥ5qIt)p>MswbM ]&ԓ"z-*#3MUp<`UUWśQVxU!ߏbh0qkvNoh_&׳?qRc80Ɲ!RPi $MMڋ LX)fHg.3oםFʖIl!NdƑb:Ce|sZɧz~o/-sYw([ ĊM)ұn@c{"w)Qsijp3geK&2t#'#`'[7լG[}KX$:rWj{l*ĬY54oCf&lGߥVtX@ zW=OA0\ }u3DZJ@Y~#kj=4XN#<$\BZQ3jT&Q3nCK+XrЎ_:t?۾eգF<8ag(۽}j PZ}߮ $CK䇿a)[Py R'ܽt'mXŒqs o(WǁOiZ.ZŤ=RrMFCJԫ]sFy}_[BAfM6Z2XZq{Ш?y9C1:TB1>7n2F5}^ϝ{E2nmza9*-9sǜb)UJI ObN%sNx`""<[)R$㕫13Es\~ENM)bZ}rƗty ǖhmXL*]YsWťHp+g^&x ~J[Z·2]p6{l\F3<:xb3`9JI/oĔ}ٱf} &z ~; |>'ZVrU5X't:CC'F/k~_&n]ɶ4?j_y f CԶWOfM,55 WnR[;zGGG]21f3sZd8)7h YJط￿= =k{o|ޝ{o\V퀤Oژ q$Z{FuhG 3uIR3IS໵Lm}Al;AܵZԛph"8)L}' ,,,`}D X7檮,ȲEK9̕D'ȽpHH_$1gR"YiX40N`S9 !ƨAOy24$֗ dV{ct7#Z%>>x;XtP|?ĠNS 3>1fZ4oJ4o8)ߨ>*|ϼiw7xNN#quU ߻b_G|!;~A=鳬#?jK^2/۞ fSi _ޯ*7xa>kn_ȕqb!NGP=!w2T-LsKRonhe<'shQ-ceKI,A&fA#Bb\p"L;S5%\QB JNQ)\)(7efWIZ{z" ƅsb*:nLa"R\3 ]ȮjկT.sm3wY)Vri[G5sѺSmLP~ !,$G!ppa-E#*/L2%N0х-=yv< Mr1cko;9+R1ūձmFRoUH"=)Z#;젛s4ͼɕرĎ7FmTiZnҞ{TJExvWs\ƹN}ʾtJ|yYزw/nc%K %emQ wcޱcT.# Sg¼2/@1'8Ue-#3Tmb5w/H"zn_l%{8Ef׾}"Uƫ.ǠV(YṇLÑsFbQs϶c痴d/UClaٶ?lK♒Ndω!1?5sƜbIK.@D.ZÏ˙82~99x_O8lU9בr]ZQ|TLϞ'}7/ck t[7ZǽX5|Nv||s3w28姟?G1AS2k`;4O.8g[R _ ȘpkVڐoʣI熝Z!8ccvNQG9{$B ҷy< S\zÛ1}f"j}φbˇ V牖85fd7c&)eׁ&᠎ 苟Amש3Dz1Pv ]au+p{OZU΃ueδW)&3bMt[sFԩѬ7Nڧ>{ZLTKSex{; 6\0+u4%W_Gצnd$NŨO9_ "x9dG==8 s+g0eU&WCӹ/ftd@ ך2fP.z<9ʗ-]L+/O78H#~"to'y^H їfŲ\3AZ ɛ))~M+3IZchV1S<¹u:M>CEn|V|#!^r\2Bvbkjrt(\ȞLeJ+k $^?əlͬ[=&:IdV'^SլbOϧg:Y*2W ,$G9>$g2gY)VTbi5 5(זi[;'?GGT N Z]O*1`@zл) hq `6Tg"mسCM96?64aǮ>SCqP:G֚ly԰E5`^k-[TS=yxZƶqK$M)[uvr?c4-p]M'7=I7ʥQ b[>`@gF./ЖVN؅_k̰sP9j XɢOk(O1st. CYfëxM/>2}Eqjj3LWF|3=C:q`v%_z0=Ј9Utǝ}l]xW*? @=m0y<;XNq%ʦ:c/ļ0כM!7b8j 24fè ܍0)wt/_wJyͻ%i]i6kԤ,AwEޓS*믣һ,-Zޟ3nbSPZuH7 |SF1yh>|3g.Oi^4.uo2{J) ,1p4Oʈvbp"duE`P:?,h ۥ-Nm:-MF̱r.O%rc6gg!W1jFt'8g[b򢏰tr5Eƶ,݉3&$}GkGW|JWel bܸ̾ƅkOZђ$F-.ɧ}#x [Vy0pYݠ/ƌ ³|3&mKB)Ή"1ʬ1gXE4/TM5{lٲ{{{*T)R77_Xʍy IؿgW'2)'%MBV?+Ĝb/++1f<{R::Θrom=H{?svھk]0e!j ϟfGF#B~wm^a̖ 9kZٿga덥UFLJqI8i@aʕoyɈ:Q'NNoh_yB:C!Dɾ+Jeގ4e_r/G\y_^4Μ"}'ܽ}l٬ӿC%GD>H+]9Sǜb/˅Y>v%њ*bmֿ#."#:C!D&%n۩%2xckgÁ0% d՘c LCC)Y芏p5Rms9+*^_:7S7S7B!Rg!D,j2JDzfVk;!3*?ONGlNxܢĜ^Y)V2[MB!B!B<B!B!Bt !B!B!H@:B!B!B$ B!B!B!,[P5m JJ[l> %B!B!CXh(yzcieMF#&8u$i4ѰjJo Vr2{veDr1qX 1LV?+Ĝb/++1"%uI~s斕bM+J$ymB!B!BX!B!B!Dy,B!B!"<B!B!Bt !B!B!H@:B!B!B$ B!B!B!ЧwޚнL92YzF!DZP!=hD!!-|Ϊ̝V}h?bs3ת~E@5{4~7;tß#ԩ3{kȯ IDATH~U̞(e/ =Y҆<#s.՛lQb1gX3'O#]\]qrtPB['SE>۲S#  &dEЎDGyVOOu74AB=Qg91iDJL6_`;DZzia38=]̾K!h*3lfͼ^9kGT{W橸fƙkH9oе791&̖#YΎT{#gaTq`gYY˕+'Sn%|[<_gDgb;ڕeȄP90nVP= bVתx;YaCn9Zg,jj#(ep|{:Cd,8uKzde>E;1r9_v$ b@1L.KPͧnn6-M|82'tq4r _doVyi9] ے}D';$k[&qtL󡬿i.13#?0}Opc4VnxG`.ގVDt}IHa97] %p(&*KX3H2t{M`a=xwߓ2^%i$Jֱ܂o6&=/s9b-k/! hliA_6UO*1gRգ)Y,(JT3n0ztjKb¸{(;"LY!~8h4WL@:3]JZ|BMk ]=*f@fvq cA{j9`ukf0 tut ~X0'DTN0?y *&\ӯG8{>Ƶ C o1sqyW j;_4v*>!Vԛ~eD<Ź3gaٴ6C=ZwbC3NB=Ox͞l/GGވՍ5BUyx810U8^ !YdVW3R(57B1Zv#"[?D6w*@'!_IBl7hC -~G/0]}^x۾w c&zha=B޴A/NTQ^#ř´'q>G7VSjDoMMQ"q>d80QXhBij/ʎ=-W+ECF4Z=>8OTX8ٚ B!4VDzp0n-6LzuVn5D.[3RObmAfD&B(MBuugu^,ϤJZd^B4_J?;,TV8YjQSYU۸/͗x U o&uQ4qW?T㜛XYXYWυF3:]Pu: hOteu2#s _16]\S W.OgutkxXvLKjȧt8*5()G%K}s+v)*o8upY‘kWQ*e" Y0Ƀ\-eI%rLzBfroTeP# Agd{YػMwK7 CPB>Ao"%LqTfUWtz`6/dl4nT%jI 90^tГ 8Q'Cldc{0Nz_欩tߛ۝qޜ(G56D}5}shu|n5}*g-Kz?g΍8W\äTR%Y& Fc;Zz+3hŨ?UGdw???.y#!=mhlD8^ŠIo':G`[; OQ4/AN;{~4K:J,y8WV#y6-x1࿁_jtf9SdvvQ݄W%,ٹ1 ,Rmzݷuޏfwuu[Q2?–ác-{e!w@qy؛kS,:Xww&f{t zڎԦq̚6{P&|d vУzKgĪ]lߍlFRhN}:;+~iz$tNE5 ԭa[OAЖ̽[!UL@PŠ =0eӷGEڍfw 3']ni\. !3'#Vz7!2Oagϳ pemF &mktZU)sxb1G~VM7{ +*"weJGIۀE5q-Sn ًփ░^s_6/)k0!ä@7V2h{|b'j%KŰktwfk5 ֝g˜'!+ B2{;n{Kz1q{lpJo(r%Mrtb[F3_o )8@O?F@"K)mTP N~b\"7MbJAIynRuZ*S!(1`G/jI}taya_2&f bϊx]g.*'u5aEMnD*@&O(vȂ<H_g=iŴdV/B ж*;sv3~wMt27,'_dțT#6].d&8P3jPidE΂Aޏ\J%`iSOkb(?'.b˱KF7Q\>L;w6a]ſC}ȩA)l5CnPzΩK5 KUzT}$r)ܖ"kKժ`w{UzqF[mfLa:t=%֥ZҺ8 9lc\c;eR:%@UWb.ovh{eul\o^~ޓK'U-̡ ;J::mN3j~ .$V^ /xrק˯(9{:CsESwbNnlA< 2@x%<3+i Ca߉=6&sFӀNC{MlowS9HA4ȏFq{FraeFtܕmᔦVTo9ϖMGOS1qv72AzÔ6#jB)7>Za-Jwf$s9#ɚFx6q" 6gDG 2Aπ̫S;䏼Q+Ao$c{fjˍ%swb a_hM&Q|4l/8eoĢ0c(qe(l5(Uf8U/ef jY|}}x·J֕#HD3JĩL[x(lb,*i},Iɶ04>dߘ?&ڃWA߷s>{6G,nHAGxMJp|X[Yaee]=h]ZʢִIxu#8(>>?Ww5!E2 E!8 ^eѱ]i〄+·XQz2z!u *-9qR\ot<ޛm7}'sۦGQz.{їGwdpqn Nݩ~D߅h/FFcB>h>˔g ]s#u,znm9fNҬbiH #Yx&.x :43IiQ*+dx}L=H~t-/!/ ss2 螋ӇF71Yx&̍ 8K942aȔ:݆p,hMV no,1`J,'s5~Ublʌǥ?<Lb(GW_cOi^ 0"{5h,]4[$&11c23GޠgTEW0 ϳӪS lT䥀Ǐd=cy˫k:nm !pb4;0RiB]6-rf'3΄&^*[.r2a"*|6oZT(k <_bS|I@{o5fA]#=(b;$|y:nkG 2,,\ YVUy38deH'#L889aq//{JZB*$] IDAT(*\ӈ^ ,ʋ^I^jI}dX)EX"8Xw\>%>qʳ\HN? C݃gLl͋)/EOpIV O#9}w3e.cSNS&U.\/j2 i7/t/ž:<(7#[>g2".7ad\Q#sqѺ3Y'kB3YhUn?ɹ}k?«ߡţOV)K-|j:&1mJbݣ,_9js=萫&nxcz 2XϤwCfu8[> uSR&XRPb@8([ß9Q"+EUX7:_}$|:qҼv_4#c4(2y>[\_r RÖ0X])ˍiY!oys<J”`3,ǵ^i!*@zV,2ŝ\XMndMS<2@JgW7> a$: h?#=^W6FFۚN^1Jrb:[FFбMl\flGE Bfm<Yt ?Z2SL!էz6>-@u)^(܀ײhH^yhԇAM*Cz_&w*EfM$A3s=L+N4ڥ cGY\oxwVd3/1o54߁&Ѽ`XJ4 c27w$)sF;3G?ol'{J*BS9lmQKsLr̋[6=և'"-zfMs%F+3dHnnu碰/OӠ|VèI؂4jgRpi`ۘ]إOYZ:zҞYa.71Ԥ ԟiV`i{en3O(5I׭4 NYv q&rT܃IزAK!i*sC4鹿^S@sn kl3A5)d%gT5j5mdy x0;7c{Q&<݅S ruNSJΉQU׏7- yy,LG!L[.ïG?kU+Qv } ɏ(SD 䯙 Ca"U4.WfUU1 uʵ^הzX "4 Wam>l)OZݾ-siⲤc C}&g矉TI,KfOxW&u$)r.58|d yYFw'0sD \ūo/E imx̔I,4S %OQ*FAE;xCt,+fƷU$GB"D^Pf1~hU ڥ9sa}p-D0rĹhE^C.QFlXqЈ1C=uc&4gql/L^y6lMf)tǻOUQkg ԝN^%j%1tp1Ø23KEffKթr{F"/ͥ]͘gI07g4eՌ0-B[@ bKJu9;u l:_Ԣ?~0I)/B_"T~k(m6%b>*xexlEi?]S9 ,f=LQ3rdk3 >`,?ƌ;'~#^D(pGֳ3j'O\v? FL5!g7z ڳٿՑVЯxBk0/x80ID9*sF;5_QTd׭M6"l)"50<h@oGSG>tTN&R-R5?>#M2gcdL{D@]4;Ǥ_{xG˗'#XpЯHkDCPmlI2Cz9#ɚZ<~ʝc-9z Bp<<< uVoI;Q9-r2g$Y>sa^dxָ%$$$$$$$$$$$$$$$$$$$eʐ$H{8=(ffaY~W;My5g-}%ӷIV"my&]Lb;ϸҶ괍o﮲g..}Y< E2 eYv)Rk#a 4/ 3 ¸]m1Q0R~iРH Odq;&w7!F)ԯGwfz7{An?͓N9}osVJ㌋@}`?=˹ɛƪY(E*ɜeHJ|-)d<~Ǯn91W!Qg.O jv2g6Ns\G⊋5j ʷvr#ElmG;XQ[0h^ i(#Ʀv=M?tSP@q4Q 1R`B]ޗF6erT؊셫UW#e!-r`r*aVp$_  B.jpJXm}~w/:GHlD/{Ҵ'/p-O|ؙ1Ifzi6O)d&qu% _s@v;zkۆt{AH+>*Q {,iu0k< .c,BmFE8_G{ TdD&3NnJnY4鰑GU42Po/3Bngg8$s9#*|RhfuP zaN uo zpӧ"ɰp>/xQ^:s߯cmrbB ~ZoOc`#ۧy5bP)#^sJNim\>V$EsCL;_ʟX6)KfѿKiG qe F2Aw9q&xppm!E?`-q^s 73SdW9ƨ,2ڿ² 5\voNqU<30㯁1}}uӧksq7|;=t@1,;\ۧ;ݳ3uB2qaiCl♉7y#/k6;r*٤K#kе(~yrbFոNMjC,r@;ͧ|"=sp5kV"S#gݞFtNU>$;-'V1m8|; -=`Lq=nF}&}D! aޏ٣ra'7/`Jc*b!ݥx.JݩA0յX? *Mx{)k1ʌ.X{rM?>??~/$R3#2Gs _HW4 ymT8i wHvFn[Un-JalO]*Ș+^0m{1BVoPB66yDfB~GaQ (;~Zg_ 5hU'<ѦRadk. X;'_|Jùĺ2][!pl̈́JN?-w 4" $~xם={<>F(j){ |~]gŔ 'KP9bu* Xp0WW>#vwYEeE.KP9" wOEIv9*fRqbwHGh/4%K?bdacQtQK6q+.Ś?3.M"ߐ"203DER|C2?L%X+N p1WtW\jWGdŽsW+ECZ^\VG7j[ϣX|8)i;BhQ.5˜eD[ϣ"0wvVƟ%3IؿKܹy]<} I=^;f>0qju2 ~'H0jIٺ}ۗ"",!_xyfZ,:~w3M 2s7 8vԋזq>ːOϳanţy /}pˎi^[Ԇ?+M嬻ϵ C|/bw < Op⾒5*):;8}+[ʱ=\Y'#dqɋ >:5ꈸeoɔ(c@)y3˒=8UgK:FJU&!BYac^"9nnd]ҍ*!z7#W0u;Q95VY\y6>\>"m^hܨ$J5J jJQɒ`;]73˒.z?'tztߛ۝q[ޜ(G5h <(b8ϝ["tD!>*}*g-Kz?g΍S8W\äTu[rL IDATə s1o?EecϏK㨜V7:hOte4uGuklD8^ŠIo':G`[; OQ4/AN;{~4ʋNN*T)c0 ϕ s$07o٦lm3]ho7!wrɶKvEiBbj2<='TfĺݱFl]iVԴLzp(X˞{Y;md+e2v5=!Xww&f{t zڎԦq̚6{P.*'unlMb;GshU{ _&R!Akͩ8|-ukV^AЖ̽[!ULg=0eӷGEڍfoc- ޅ!2?Ȱ>-EӚ?&}/u4iW̉%^!Sb^f蝍#uշC9}˜dH*piqJTu!OD&F!]=X{+*"weJG3 Pr~a``dchx&, =B0)Ѝ#<>%KŰktP0z֦ A/29/dB(jd?Š"1p? <Ȭ)Qx OgtVEt9e3iUSi:<gd-ViD]ڶNs2ˍ`QSCϽ%=[6c% 7x&9:l7rW nJ^pn BKxD,.'SLZ*Y92 =5En:2% <Ĕރ^ݢ85U\'BQb6&W}``Ho][D?iUׅm!j*S`bn u2BB5#qfMBS2yB%UO:l8c<" vHc }=qYQ$I3dCۆ*n57g\@F|)"oVRt:dRB>TΨA9 "V׸(ǥ UнLz>GX3?O r,Œ #roD*\R Ye42w6a]ſϏ\_:F?rMJA_9uiqJCʼoD`^.HZC*G^=A{Y:ƞ/$i!dݺTBZA"ml{_p~"UjWJСB-Py/3twc=μsl\o^~#I}wЧ@Qv#!I\+>9;O -~["XQ(w}Zs|<4WY4u'Fij 'Dox%d(;ѽ'`td:5ϲs$fn|lо!yvzN  ? T_6?npr-G)3Zb^%6odB.\)3mGs[)7>Za-JwfQ9}˜dH wyUV~I.(kK2"R0oEaE_NɒŹ1:u}jY|}}x·J֕#(RɔӾ^lۃGQrk#{"!l@B?_{̲4}7]ɍ]sͤbxsMk3EGof "pCܤpliECf (x>Uݨuea^3q4U4v\-<dʪ<{1CCx䳐&V'۞ gKT,^chfFA߷s>{6G,nHAG>\| i#ZˇVVVXaރEwi)NXӪOs'::dtǶ>|_ ׄ 3dypoz2QyzD %\iup>ĊKaIfCU7rU\%'N {SdN}t|~ ߅h/FFcB>h>MڴsV hU ҝ0a760Y9gʑ/FxBYCdp `TJepYW\}t{% A$; vP[R[֨ڔ=k+Rgֈb+j BD"_rs999ωO n˗N>q޻3<~c;kt.ƓM-<ㄯKḰfdNu0{+LqDµa ~q~9 Z5YT^Ǡb1 SDIbVƎ#G-_8OdYjȽq|G0iY̅v}w>u,13RR~LAƲrŻqM^(PH/E΢wXܨ?* lȪ(DruQa">T>0$6M =W&7y]^&{]#wlE'gܾ/Es瞊]^'>AV!^Đ<^qfN7ʔFPco(=Po=?kJ D]jyڏpt?#a|dG3[To\Ⱥ;Wud^췆HA9!{d/PސszXQ+vgqCV\VjKQƂ%1zڿl-1o1|]yNKfgGܾg1i4ܻ}1NY2QUyq܊y106yx11(^xe6HM'C8|VMD_P8xng`屓<#OTi辥eRW4Wj]=?F `v/ )y7#cؑ_ed3}W#B:O-m߅P4 ;Soڻez=^\aT );%a)Q Pxg| ++ž;QfBwڀrZiP­F$ ] ;C/X֔mܘ^Nb\7g6%['W\,u@˺_lN-r]aZXcЍ.>h謹v|kUTUkZ^*qK$sFU]'1]mUu&¥={H=3sIp{ќ?D{c~W2)X09,C2ߺ04tRkz00%tBW5MzdU"x4UXA4sDq`s/mge3JP鹻q0#7Ee+|ZbO%p޲|]0\9ϥ(p1YVOK 639~' "6`?+MxkEADE194"o^# "(F~g<4ΉkBǖh@E^|m{l |LRe)&(ظ/)YUO+8TE^w&ygUҗBu"{;qB)Lْ);!eFx1QM<ŘO|Ɓ#Xčm*}4Ji,gL<Oew_Pq䒆r U_Ryߣ󾍬oۣO;0`xNĄĎ{. Fd}7 &s,=vo{ҩNjX? Vx ] 8r^~1'PKW +FByqK@J>oogtwm[v4ƴH_d|-J6a3Iv~#wr$(/E q+f폣َM^W#yl;[8&ynw`.qd"f/:R}WwcT-[W;cnfUdm.lL9o7Lj-~E)CX]?NES'<8z'&6qPdusxۗ 5<߇$8/=!øs %L*q@ј&TcV@e?n̬1v"g41ǛiVzKG=ď#t5.cԚqA5LyۚjTօBhՅqUl_"Ͼ F]4^?Zqu=ޙ(I{K'$g)VdjrhΜhVe \rSķvwS"Rpj< O|5!:7rk5&|wܸ nSXsFNi16uZ`ՔwPlpp.@1=jωa>v|Nj4ػa}:+LB՚`n炇/=/W--3}X`dfcǦ~!̞֡l[ 1֙ɑ$UfIxYXFA琝k^ԑ\,I=`C3jf^*3\*S6Uiti썝6 2ٹ\g.L\2%-ŲyaKJ5v&yJ(΢FyĈ4R Oy|0 O` .Qҽdl'ӿ,Ļ)V"9!T5柘xpv;S'0l])TK@}`Ycc(jN.;fØZ)rSg0uE`:|\Wt Gҵ4YWUzJ~`ų\;R?UYq%iym'Zթ6pDi// >Fa!OCPMxf2X㧏cF"VqZy)4-ۈYf|]ǑDF,-B?Ѧw?p3)8/ݦ dϏ-habNR y4%uЕٺƅӳ_$#Ɯb RV,]7h^lݺ|ʖOY՝.:VYϮO+rM_ҖRf 1sF1ĘDMi$J~ӮO_$?ErQ듏*q#X&!L1 9kJٿg!!rVD!B!B!Djc!B!B!HB!B!BxX!B!B!D5J;XXފ\QǓج>ʰqk'U 6As [o9! !s'0w-j91pwpˆS[qnq7xe,w2}B#'qdY'rkS;M5AAX DF9}ǜb+:cLh4ͬqt/Fփktr+%iVgN{t -曺n<&=F W]H`U̬fW?YP}q7ރ D`*:EIFi"1;KyL1PЀțW Ͻ{7dvk,EoV^|"~~=!Krޘ.Z IDATc/Vu ] aW )|{2C-435\MV eOz6ΙY-ROOee '~n6/IFN_aӸ|Q8;vfƘXdƣd:5W|^2B%Ƙ:|ȯsӖD&cJabs¨>K-3\-wMn(d:wW;L"71)VpFQ"ꙙR|_B.ܝuĜcHL-0O#"_WOiSt(gDzt++f`_l?{ZviJj7ni˖.*Oۂ偞cuZtٞSVQVޝ5?a BY X;rƀ#wflmg8U#.g+E}![s<qޘClfȉyt=hJ7.F<Du2e0cC.5c]GR"R0 CS)n<;f@am L^iJz قFc斞W ZϏQ+[(G&E//kŖCS(İobqiPCj׀tăs4h$Ǻre lӽ;WQ}|3XAV3Ԩ273ؿ9GUC s#{bnk p?ưOkX_ZC(g,Șe>1~΁ׁzdpPf##k`1ju7,_eiXgP_l>tԉZ/<[S, hFeT&iB0vuL=|tGb_Fߣ/>-9 {ccrlV_d'%>}('YiX$n?d9gt)(KR/Kf]H;Hd-GwJ\4hӗ_N kɀd6-Ǥ/:916r`n-.^?<0\biVkAQ>aC9X`bl[YZO=ȓd%=\^N3O 8SX^HL-`\Ȱ5bW%xx^1Ż<+Vb eFHcPܶ8+o8623(;=.E9` E1Kh-tjrϙٳQl1du!fN؎W0c/Sl&{" ?d~ij̖r,]ׁ9f T7H6Lp.X~k.!>lǏ\uoލѫ5? @ y~zŔrLa,Y̓ZvlL;2cf>']{'Múmؚe?#<07Ż.}{u]˄fJJʞ scKXJdKF1F1)Xx9*X?/ElbFPwM Q/'DV ϧ5W:M 95)S GVœ#@>1SccsQlWs9#**eo*6?&Y6w UBRf1ef6WٺakjhQN<ӧ1(Vכ_of Lna}ϯ/[ ĨA?I~se#v,G4$򀻱gijW;BX G <hK :C +YɎnyi"چO+ '6\S vHka.gHܘ&.%́s*ƉީrubRʟcNG;,=\S8/RtIպҖ Pyrpg)j8}؆wH G5Syv|^5,{=¾BUb3-_9[| g~f[ڏ/0ns4Q>=vYn2b0+T-u2ZTv c1XA"ܫP%׋ƹ 嫖D^6$u3FNxOd4[i%0Q>W_Zdʿ$+=̛_2Ht߁ԷE.NZf9OL?z'Or"pj@^~{ Ӛ?0J&Grrl=ގ])ٰ߲2mL$,q`TlƆci^Huc"N)G\ :p$v#{VKg۱a:KS×"(U걅֔lfUzE4uF`ê 4mKL71eu-#4b7ґ5ѯI>^Tz<#˶me~lOE}s9#**'(JF,:zp7.+G߼9x1OGhSmFOqגҗ_b*Kju ei%7b|tQ Wmw/@ L븿jEX}͆Ac5QܽIpc)ߴMTU됛JY:zjA@ڎYo(6miߡ u ;:M4A9]|%WN ޖu3csW fb߀1~xi@XÙk7Ƕ59zuB]4jѕA^Avߎ\At&* \?8Pq&NNL r89;98fɌz6wU=:)Ƅb%&/1\aE<1es ?Ҳ+aL׾ja z6wU; 9IG߾Kʝ;(N.89͝`^ݨd|"wӢW?(vJh$ fkU]?L E{$ze<%bЅ߲(%iYy 5Yn/OΘJ֎ P ޔ=i,i,Q$C61~A\d19M~,S-ԣz%ɐuFdFjLIdTchovi w' XpCG1?t-C^åiU/JN7zZLe&cZ:17֫:` LA~1uTMf UNe7 TTc"kR6A7Kd^fe;2 ĜcHH"%4VzpIZ88j˪G">d/!UMӡ/KO6M`ƿ>|߳6X󈇏czbFڽqV|IoW7[O4B?,aOl0W7#yn/cIw\ƥ֕Rﭤe,bSclz?BRH5{#ʴiV;EA&Ir{w+ۚXf(6x8>sFG(q4*xyIDE"h.Xsm}2)Ck!Dr2o c5+YϋF QQC<.bE={ rEb|r\ʼntk#G-_8OdYj ͣ{1J:ѓaވQ ~L0XY)(uɍ2MMn6'a-rrN$p 8eɔ^~=gv߮#|ŭ,n+Ivð)QCbQ̿+VW] ;>X*7.dݝ:2/[Ct$ ؐ@q(NNvoĹi=(Q IG)|ەD /+(rf,偖ow.p@At?#a|dG3ybHzsszdt䈳l}7N盞{w8:㘦G6SZv5!?92ڀ1tLib]aB7ndl ЪV X43npw)Vv)[O3b(e۪+rSҞ=$ufʓY9$P.eSxarXoX |"  s0ƻsO[t ,TcJR HTc'”-!q/͡k>/EADEF*N?Ս͆P%Hnaq}G_dg8'Z4JS:G~9ov_6oa1oIt3)D 5rH>Ho92 iu׭g?5OhަR/Ҭqvă! ȣc>b܋gY&eq-ҙwSD䨂g{6Lf梋m%n@EVh&7^ȏG/~9-1-җC<>h4;_~ICEɦ?ݯ^L~#wr$(/E q+;OWOϲ)a_U2MNugL}kЯ brcD$:ћ˜3~l"ZR`^t3W2~zYf< =It,krз5%?D}w*Ϟ>CU,c;A҄._Z4[ѳDZ lP=p3V- },3mUY<;vY”HZdɘi^g2}P4n]yZmWu" VMgk볎wXP1`7j9v^; 0 ֠wb֒v#cɍcZK:S(ʻB[)j$7mgd"_">-ԭqcժ xMPyPG.Q)P wm2,''aB·޻^}k\+ҵd?G2ُJ3G%LDK/{b\Q4j2m9rq /FK/Ϙ3R"1`93&Ѭ44&ع䦈o3F D6x/#jCtn>j@MxXq:ݦұ4 brm괞0)\-bFNW0N ЍY{>UB8a2hSuLB<~8fVq<ٝQj3 J4I cߖZWqO:}ذ<+A, ۴w\_>CQ7sޙRb2[׸0`|z6`UXk\R"$cט3R"AʊU k[bccO$ -fVu燬˹9Ϋ|bJD"I$%mI!eF1gJ"~I^)/~GO>ƹΎ&b}k0*8.s)֔.BCBp厙y&3T3O)LšիS6˙q̘|Y7 ߝ+}B%eB!H-j3߹NgELߡ{O‰x2{)VRނY9M(GJ_2/-~v=B!2)3B!D7Yq4VUmLV N$1'zCrǜb.o*hpnɩ !)3B!Dj0 DMt$Ϊ5O5JiQŜb+ j"B!B!"HB!B!BxX!B!B!D_ .( kVcS|r%'ٿgWO$9N&/iKF!ƷɈg3Bqex%FZd$/ϔXbN2R)e]T#V!B!B!G:B!B!B#B!B!B!c!B!B!HB!B!BxX!B!B!D/|Ϊh֜OP8wsBwQl1 !@wWfMLRq'`J27Y3Ņs|@9`z $VIx'cd:)yl둇L+mUi;=͈OMTωߓG8ijYk88F#{s9#*>V2u?g}Xhh4Y^j1wߝF2+'.VJVX Ϝ_[7u;Ox4dz$f?1 ¦qpM06'{*XwqcYZtDgjKMf"䒞Kyƒ9PEpl1ǁ8 ςݱ<{P'bLw1i"7;KyL1PЀ#F6. c具xqLYϟm1Yᔷ2~=BpFn7[Wa2^|!/䌥% idOS+ \z[k/&UBdws@})3 G~ћ_?/gטԬ'}*alQvfi4hM̱uTnLs/1^^r2K.-JSjg^o,qYmw/dFغz36iSD|SrzxjIf:f1Ž yR/)5~F+YၻUw)Vᒩf{D)qX6neլ십`Sg6W~UksnQ b*%MfOB?naV1m GS:P}w&:/cPt 3Aq6je h#f&y p㶞~ߜl+ -]=VwE;ee]ݙZ[sZ!ПA;)=~]  9qmw8=c._eY}![s<ߙEzb{c"T j/# ʗ"'@ct-+m{`jmdºsyXL vbd >@N=_51 Klꋍ'vԛ:Q{kvpJWeͨl$Mzݙ8{s.>4Ýß?sl1!crJΚdOGë 4"stahW?Bɗ|gekYSK95Ȥ|tzq8SˇYX̃-9@zcOdX Ң˒m+b!z9}ǜbIbb5:2Lx6ST_y.oEቺQN槪jT:ҋFP3Ʀ6gIj?u|YUU/U}Q!uW{TUcuw՜Uc#jj!6)wgufjjR_\Q%UfV VML]6W 8rM 8F|ZwHzުakԯ\y7;S*PAvH.RNHm$Ls%)\I} mdMu!TlZWuuV&6J×;"VqfFBmd!zdb7ZbM ulj\q2l5j 7'l}eN+>[^ LN O2V_&btQRgs5;(&sYܿ{+C76U5bU+D'PX=l saDqK1Ǯٸ],{\%&\Zp@ngm2mnd뀶L _wG[o8J*.*J`4 ?U%e陋"aq|; +jLm {2& "}"qV·} jRpodLh`ZuLar:]b9]bgӶZUpEppJ?Ih4)!$8s fd uuzYԱt [ ͨ?)d-Gx,e>@T wJd3R4)‚ ՛R1iv!ƂD&7GaS6^ڤk2V"L!rFm⫉($jELTkun7uҞG9zSCUHzFR1;C<;µU(YRvyT׎dx xKl_\52fyپ7]fV̹/h;’0i8a-_n xASk,m?wT{1dĶtțh' ;H`` S5.tHteL-t85}Xwm5^:fgٳF?q^-F4&y0ILZLIYH iUV0/kwOMKa7Ml'@} :hM]yS2d8jL\_jR}bFl\iϩcq7@};cFO**P:Ur+Rt;xВ ^;c;+kEAˑ.0yCFÏپa݋ixkV6X)1Oc"n{O^lFIgnm :. eg;-Ođ=NV@*"w~?wq/*oi,~; aMIvDulV_tN2dqZ|6Fײm Zkѻvs\ILȀM|Ο9IWIFM(_\.sWSOn|I\Gu/Uc]8)(Ts %UoeCRs-aĭBOW˟פ/Jem+N ,^+iƣPCA3}>,ZpTyqT^OHC d|oU|e!׳[+lk3u~MJ_K5^?E֙ƥsQ 5AkL踪/3빱7v`ÚX+BҼK<^F]m@)w#IMs.OӮ )u9#S6'eo[bk Gy"h)N}:ONk53m}AhY>7G}]}yM}FKi뜓tdmR/pt,ε;/7+j. ua#1>fB"ulDO ҠK3<@/]xC K-p6fV2mmw`o@5,^qk~l%3u%tFeΏ̤SOch(|~nῨ;A^ޅWsٳ ԇm>c seJgPch:[*²1q`B?E-zLCk.uccm5wEB&GmoKU@` ehѹ!>^(7]}+|7sAI|_e y#+^ ea\ ,$^^7S&7#UL [zz3]d0~RV.?]g8C[X4ԋ+#jQz x"qwFNC3Pq35.nP> M<=W׭u#>J˝>1W07EaeDIg!6=UTquJ@p` !*/TxC; T TZ rdn2FӁ1o숓qR;uSl0r$T7/uªER"4k@"y!ng&~(h t'oI $CAZAn4z߉X[󧇙в?'32 $ 7,SJAH#]2α| *_h^l%޸9xLf+P4bPg*b (b-0WXahJBP $m`ܶ]=z C~'=p ?-O㱬Q_67e}Cf}m{0Z-ַ˅n_kvr%s 7gIUCB 4HM)5$GY{ju `t85Σ)UA>|Ӣ+ZW]3XēcLlԈ TK5ΞMF X_G"'4 {G mP$%o?Vs%OsUyd## DӍˇ 3rp^񘠐PuQ Sr[ 'yD !Ba=ǃ¼*?bz={6#55 jۊו"nvV "ZKz,{@s?L @΀kBL ˇ#n=ߒR>:/]C0@]?VvMn$7F48 ֜Ů}%(O0Yf9xVqwYO}\j1iLj a¶8nGw!T;[zᬂ P=ky:5/Ɏz#fI1[ϡc}#¢ PwUP&>4RO[眤dn"92q,k.ISTGщ{`L4)Tх[1T*UeKS4`0dЅև6 e3J6s>0- :7΂[ocH1Dh(suBW+=&3cADx% `ρI ^)JS|f/h\.`hґMF~=TT˗h]99_،"~  3{Uf8i9 J >.D?5{Xwe0oFrx3T Yӽ%S";~k$ u1*W᡽ytó{mJ(,\>>SNIpmoNʽvI0}v ` %j O279= 1}JAOycߍ =P%?iޥ^Sr F͓z+V$pguA[쏶2mZ'r4fHPc?CrMxġ}1T.y&sSI C4O@C0ƣ\ +/~n@1.e~^uKr}xJ|=Q4ڿEO'yٱQTXR*ӔLF\:~?P|q+hJrcC;ԩۜ>cDjxzxUV/ :( G qe)|~^^՟dcGc߱lKyR #A7D uNi꜓t{ <5S: Y eFndoQiӧSۍe/5cۖl/E1,a$-`/:PDQ?yOqz,9qXU IDAT'|:Q}&T34g|J\Ӈ aMI@ujŜPYoX.oyg-5lD>D(̒ǠݚQO1݋&Y3SO̫td@щACL VhĪثŤ;p ?-My"+l3unG5ZmیqjfmVjVh 7[<=tZ|L.2Oz'*˼4<S*|:ˆ'gmQo߳2(ZFʾT gR_-[1LDaNJ6sϠfT߳QI[a},wSpuwDf^B1M a^g)SjM5ޫֲ̿5?Căc_USfW*ۋ;6 a6tⳞtt¾)cwEIGbMr)@Qh7t-%ΦhڷϤ΃ss3j?uޡ͐dZʍXĨ["WzuegLo~s7ܦ͌~Ì SK}-PǛKwalO rZLQAPe|:XOGE35aT4'7?#3"OJD8G.} !r||fQeVKT*]x~'5!SQCU(lȠC/j0w}z~Nuxġ9YțIk}g7D uN9'*IPbagֹ5)NEi}i_!)ph ۞ aȏi!ZQ)AJ{t\Sa FOߟ^.jݐFZ_FO!1(-AΞIoƹCWS~i|*rCG(?m?Ma*MurR:TQ9Fo^r8ƷLS5օd|ᐆLM[Aԓ([G)󒐉'ge+crLƉrfۘ<v7OK1l>Ȭ;hy0J8 ey\FRTMϱSQ< Gq)dy(˼7Osx'=Eg_cW>q@CU/6=c9t>H,W.c6M IH 2e n៮XBuW#s.s?̝Q"}׉|ɇx`Ú8 ˣ0wu?1;%iZARC7AϰtB{._bN_y3ɻ>{I|Gj*#Lp)C+5qVv,ݧbizAJtxM8Ͷ|KOfݿk/mɘindT?TcG0ujW~̎jQ%֌(f&Wyc8:Ρgٳщ2"NEk3:F^ ߸À,ۘIq(iVD߈bl՗1Q-n(trƻ;eؤa؏aruBSsGw/) ժݨF f0%ʄ3sza dY9OU眤$MkW͚7Jك%+j_Aڅ6-mDñ#2G?','/ًLfd r9A眠cJrRGA\ }@_Ꝡ٣ q4[9I.F}Kgvd7mc(=Xo>esű#¹PaM26j1 d 36|@5?Kl\k:D"H2l3$D 5Җ^n8S1cϯ[NA{SgɘyaO3p,ZD|$jͱ5J Dl8<{::$]%N n.y@ޔh/5K$$;# D"|BauiE[sn~:~DZ Yg8'K2x3M鱜:9٨EL5o::$]%N)JpY+D"H>d!H$O]Mފj)>n4e4ZI@$Lj9ǼݟĴ{o:gGޟ9IW%j"H$D"H$D"H$B%D"H$D"H$D e[a5ǡhMz,8EDW[[m6|c MK:bflcul6814J\z{q[85K?5\~"#?|~ M) 1uܱ35":<80tS"͌_)_OKkCm${{{Rϳ6&Z-P· o2\ʽ~ߴ^L_En-favD",S'Cd|V:?Lp)߈޿R+xvc݊Sd?֜>1_<ƨJTF&X9Rq_ }~ānnr0[.ʖUQvTODQrQŅ1E: w\˝W2b9MuP妐 ,AsNU'ByVk!!r`BV]Q4+˚U-^*B~tM3K%Mgc`,7PhSJѧڰ.#NXR~E\wpX4Z`o0j?,և iT6yM[@ =q~d.]( 9q{#['1E(z6b A-S"TB ?,OI5`))>_ÿbՊ4fqGӴ=EdxgV<@Q&52}Aڕ\O.jΠ(&V~w,UO1]?pE{{;?]ݛXED/l(674Oγjm\jq'߸%Ӷ_q}h7Q7`2^R$֣|^oSmE1͙߫jK̭i zBusmwR(☔FqibZNbŴ|\-#ZDw CK9?*~*kq>z՟*"lt2_n,hOoR_ΖCNS uwL]s:1,N(_kMqwh3$هԠA[[3S[gˈ냥I)ASp^[գPSd~9s!N!kZaLpuA@OUX 8'MYQDhZa]8xlH%d5Ŋ~)I'FfmĆ^L]Jw=t_&DՏ_1p46EDVlm-Ԣ0MlHq]ipt-",uoa&\&$ \W%E%Qبw0."F^]X=Jz^ܘUEʈ:텙0B07a?n,ׄ "!L~Զ֘t{"?݅WH7E拏j랗ߴZJU3%wʼnI|u_wXw֊ψkF:h{x@q".>3&9|Rză_ !X^H)vq?9bmNG"}M]2]\L͝D i?]dK4YZfÄADȒL[QLpcnaTt8v1:?ҩׄ ㅧ%[a$E<B5=!kc"ad\Z 9rhG-#LԮbS!DR>+SF>bĶו]Ķȷ I~;,]?@„OD"Xk*K6W]z^鯼ݶ(Fk4]|i&iZ_KQгn1C+\sW;iifIYAmN͹n6'!p(щFK_6&ضm8ZEN#4"U _&tD<<ѦJ Q\hjv:ZK5b˝pi^S8ۙ #VXl/5٢qQGaU inQx#)j.,,tB |eD {DPo+V:ad$J׫*븷͗4|db(m)kMaq08>pwBDS kbҩzM$\ߗъ|}6 ulj\q2l5j 7'l}eN+>[^ LN O2V_<7;S*PAvH-qsGZs9'Y{vl,߽[Ďm*m~q1b"s(,6ֹzn"Ӹ%oucWlŮMhks=G\xe.Z-8M`` g3S s6ďkvq6fPu@[{&(->p͕25Óp"0c"pfgV"aq|; +j6Lb ؂<0Be@T wJdUpqVq}V'*AE/{4)‚ ՛R1iv!ƂD&7GaS6^ڤk2V"L!rFm⫉($jELTkun7uҞG9zSCUHzFR1;C<;µU(YRvyT׎dx xKl_\52fyپ7]fV̹/h;’0i8a-_n xASk,m?wT{1dĶtțh' ;H`` S5.tHteL-t85}Xwm5^:fgٳF?q^-F4&y0IR-D~RV#56pդ"0]SӒcvmMۉl9,ڲ.`lSWޔL'},W׮T徑6WcZsXe\n̓l8E1cNѓJ Njbcʡ#{N1/$Wleu ZsyPr LoސmzoCbw_6ӳVk~[s~wEzq"Ֆ\[ڙ?݊u 2xCH$ ֵaZI~'9pmjM^˶3hF͙s%3Bw u[-$w޻}rld3-}H?mszQ\ aݔ\):+^ƺ`q<DV^@[X<.ML {MH4OVDm0Oډr3sF?m!(SAdZ=! PcbnQ",'2q:o0th?ԅwt(]~B:NoN B5%RdG{Bx& @C 2qȍjqj:0?֔L  ףmc_jO+Ѓm{[-s9v+aJyaVhEb*]L>y&8z8J5{j˞EZ3q+O +FP"wa/m='*WjBj晥b, eه'eRkp IDAT=S~ slTuf۲a2(/wQtjͩO#-kuV%JҎRY 5a.S3EY}‚}BY;P~}j80H/Hnɟr6Ktg(`ݱF~?6tk |5?׏o~u?. nO)_n65_$u";{Ю/۬ui 9[aKXxқ6mRu_CĤڭ_F| +Q}oBꩋ k+?F/t_b-sNUIӱ8׮l cx߬]6V*ԅ Ƽe@B (4fӗׄ `VI+RpQ=V`?eC K-p6fV2mmw`o@5,^qk~l%3u%tFeΏ̤SOch(|~nῨ;A^ޅWsٳ ԇm>c seJgPch:[*²1q`B?E-z1^ƣ\5ܦ< XNVun>-H.0%wؿt'_9IDbfHQS{''O9GwAEJfeRq`;6X[[cm_y^$d¹XpԆ1]u  `^U}u۷w3=d_G;;n,ޱPXB>\Nu3?E^m¿s:Zex>̤'ʫ'9:AO'e%#u&8EC2O,g.ww ltt:t:'.O;SV3HsuNY7fo!w\YðgU'!>6TQEG|X(R5mK߁$I5}І i9Yy&u|9[pW}׽2@SV÷ZYf6SJs+EEN#NIu]>%!.:ۥΟ9IW9ѧ8~>M5›@^Zq1K/F81z ->(sP@p2t>>Q\F@߻O c>hs .xJڊD`M#pҌaa/Y"sU1v!@<0hpmAys*ЃIO*;O{өx,MfPYw Vr [kW𚳇\ɜż-cs-YtՐG9=#GSJ (-QeZ݁-,{!NM{9pqtЫߴJ=fWE]2α| *_h^l%P1&6jqssBgO&#Ytc烯nk I|#zfr6q]@ҒBϷH9~A‰HF Ê넆P98bxLPHl `x@pZ)-b 8_FG2!pC_|=~Q´Weq~ъbBoDdY?g'tCX<ƕ}#pKJr{yW}9`[ʓ ~?Pʭ-Y×9ꧩsNU>5#¯OyxW/d'IiLGUMfLm7Ԍm[XzҾư臑,l}@EUOL.r?I \2?{pwG9:+whb-vb|ccO$W45؍&n?@EEAx~ûyfgwvgggcؿGWH9nQ>l6_jʨ9׳'+4k!bxv*xtg/^ÛXBil:A%C~C[k{{1oM.##,RYaBVl[,g2N1Yoj2Lw%(PdS]&U%F:ӯSܷ<١y/!Ʋ͐U1U7X:bgfl' K%K"ΒOL쳓a7q`%<1!gMcY {ٽp ?o17&9%~:f>LlAm|yM7Wu|aЯdэ .[?Ps ]w-pr+X;vӸ/U 7C2mlkc `M-[1n_bl\qsd!u 2tR C71{6{Snzz 5)K՘] E>]8vkiL]u؝EߟѕR}1{6Ce3do]F/{Ҳ'֏ZsB605#?,T:w4ct M+x}?QɁuP*a,pu$14M|ixkyRZR Ck0cU7' .}i8#}JxVq7SL vIOϾaMω+9ф&їmk Q$%sGG :pB˼?jeחŜ!ɓM=8Om>+` 9P ZiM7&O1 ĹB lVs|kDוg' bݩ4+ڗ <5a|fz2GzvoEcA̻Wqk`D'8lӁɍӼ"srYEpU_8ʘ1gXERRC hSsZsk5т$fK0u+Jiq?^gTg"i{gU>pbFlnL)#ٳr݊txH5NTlP Cc>ϊgfuoHۤrܲU? Ga-(m_dl#M4n^>lYOiciWwK_.OTX滓DD8d%śQN.ˀk 8x4'GRx)-hڦދe|ÔYڕe2vzaןe7d&v(O F+;R¼,C7@w$WSLh#7+RcZpNT=.^l= Q|fHQW*5+9p8-c}V}@gJ< O/΄MA BylX/%&xuޞwu҉~GWY5h-vfo˒&4_,c-YrئEq}f/+VMfyalKL4ݩ̱vpŻGɟj$]WvD 'vahl\Y&y3#1'fѶv$^?4e ͐gAzCԗ u[*݋.tzM?48OˆgVY3m>[&FL"WL:2 `ZZB@ф)< "yF0$1ʨ1gXE4+&M|%۶mJ)1YK5l+oI?ۓ: EI$%}I!mF 1g JO ٞ/oO>IyeÁ~y؟s;{Ύ&*#-Ù}{5`;2r̙)rp Kr4 rSF5k ^# aF!l8qvTuʙg5!"mB#+pZʸBd$*o@:;TQ )N!9ǜbo/-{1Uiq*hEF{ąBgf!@"|57oAt.HŔ)͝~S81i'|u'r RMs-7%t̙)V4E{m2#m! mB —Y":Bd1¤OJ|$Ͷ:"ڦ%(bLwg5B!B!BVX!B!B!Dy,B!B!"<B!B!Bt !B!B!H@rYjҴ)(D۶m>%B!B!p,)ϪC,,-$_:k Y;v>Wƿ=JsטRiw3S;G>\?z.Mo.`L};)lzx~L$EC_VD9cǜbMN>̓}V,G *V"_lX*~81N1 u"u)~]};_!mKȢ,*XQ~E 1rs8zoneA̩g¹qĈ~ot֡|Ewԯ4YY7|SqgXzO4Mw?cgp5je;yMJN{)Ws4CoށBVm`KdsT܊,(Пna,zƕ):grAkIK6J jM̱vNM񟲑?_;D+SI97h0͚c7 Wc|ix/[0qԺR:+~KxyT~&|^&ɉj) L8mQ:M)KY` !k9ZcԢJ䶊ک‵g1 wҢŰIiiWKc돏qܰ5GF˃S,5;; _K 6gN^|ii~Гv4dkzo}!0n^՛Zbut}koSn)G,_4&yebtaбz;C\%>hݛ&oV]_&Ԛ_'ei'K;( 'TOp1I;kpmz?]o~+)}tG=I߯6!sTPc e^ucpYem[uPOc IDAT)T/]vɪceodi,{rވ*L) ԔJ:ٯ*J)k1CAܟ  dLu!wEa!uG:KrUJ,u5^#U 3 `}R1GU!lQ҆UliPLuŦɪmzMMo_VsymSu.R5T|YEO}캃;)ԁGS})Wh)]Q%-Z]tT{ZcVi?ۨn>]ႚR\ٵ[^Uy.kwtO츲LWO-T**:F1\TS++]^(Ԑ2Xͩ.گe&&|E em^IMXUF硾3S7RU9j2*>]CVe^0P=U٩تkTUU3?TC[)3k73~y}jha-9>[RTvU:SSe*v{(aPg*+j[vVtQ:eQOP +;2Ϊl]+^B]ҪmzSv>*w enJԩwד>ƥv\Ts3iyFE9fSk(,:en㮊5^H%B2Vz+Q[TW73Yo塚,G=LxjhSϿl1>:~*ftV.ߗjޑIɫQaEBeU^}2i^Gn,%f9sƚZV~ݤ.?].^PnڐdeA*&&FD'xG@Ĩ˂ިWV. R;@cOh|~Q"?  vݡt̵[ٺ~>8g۾H]LqIN~%@cKF}fVČfZ>aԁ$򎔞kX1{ YW6џ,M5(`raWڷeb\⮶_M/qi|9y5/7mgwjQJSU8O%>1ɐiҼl͙r +aΟ)hbo s'ѡ&ˇ7Y."tPE$` .f/|j匍AE;޸ SB QrmoO.pƤ(>lb1,^3޹-5^q}6R R/fȦ,|6K &1pz>=5qȚj]fp n ZUKc틯gC 9LWMjy:Şara?yt(8~5\uVÏbc~0S:'*2z^u5my7{5l\Hޮ=3Rߛ},^rX饗*i?Xp'O?7-E_Ƕ~}D1ɓE}\~.:E ќԈLnZH0O9HumkaMK&Ѷ.ѥ7%{ \f~yx`]$?߆wfW8̯;TtҠgqW@῾×?p`ɝ*F`of!dx /84뽎f5IBfǎ)Z⃣DL*FkIbJ,jSOWi0~%VOn=j5e柱Sߊ}<gm,陝_7kIG9cǜbMOJ%/Ad{F<.E¦,=ɪ s@ "(-2WNaC)}hE/>^X+ӟ\@17a];u4^cN-+F4XF~:GK2j\ٿPXwѹf|2pj NWAyT>s@s51&fuAݺ7}w.;WmUz7#˾Q/ѐT~܁φU"j؅6# cƾ..3r6 ڞ5+|qLW4ٙ do̜=)7j2qNwOMiy5ܙ~$ HOIH#CZY8*sثR#N2 57u7x]LybA64`Hz#8{Ԙbek@dR֙EJI,=;9k}XĞ΂cun*HZW]5ܿ~(7иy[V_\Ý7ћdUoYqp]d!u_h(Xd, ᓆ~J>!$l~^u.Pw;Z -]b M)2aչs\7%; |E(l=4q W"I0eO--ʖLeL>n~\!JAڏ#)iJ`BΊZ.)!Sk6!?#77 +:,D:^MYk҉M?@Y^A3. t,U_UTr^uX͏Bjj?o-j^uuLݺC:A)KKX7r?N}}\2@Y!<wGl^H?k|9+oP^ `Z_MB-5Q]R7 ߏLy|NWb@OPl>zs[m1ꕌm{cfew6/U:'.N G P Crmٯm߅>G9cǜbM'MiSY6)19|,Z%9/ٌ J1 y:Ȉ۴\ [%x(ldRXO>euG l[1Veҍ %7F{ςi6>Љ,0c~5=33Xnb{ܛ:z66s!+FZyserAl[@-}8'"ܓpLauh-C_խU qƓS:i2AvmGGޜFEn]=g_:)FɝC$I<>MM(bZs*v&(Ӛ~̈{Y2 ^g)e+x }~>֙RfW@ppp/=/H|ߑVqO2#ׯh[fSTq|s&zG&\h=(2- `8o_P7'am o7s{{:Yiҍ;$caTYMJ[piQJe,Ӓ]Y>TsC>߰dtN ~ڼk (+,`^ Ӿm/)⊻l~3^[7nqu5+ Om?WN_@Wt헗6)ӛ:cӭ/;zW=෋^ZPcZ°_WP LqdÍK8S>= p<5 )'g2,RݸՌiqT)tmvedy '_5BSOfTivN7&|A iZKz|Ӏ#Cb۠Snh%5+}v'`[nF7GiŒ}׳m܍$=yM3[SfWyf0=7`#Yȭ;FLqȧ?mnR8tC-5n;@͛i\qw3M2B6)C:dyr ` $/of24IUu.s8'eGCyD^<1ʑO7^FVqGH3JaW4o&UHθ\Z>CX1qғWhӢYS^&[offq#_>0ɪqi8j/վڣpb+9&؃igR* WNY˗) ٓiT? %c^IQFRh 9>*Z2=Q׋޲mPʄ~ _:s4.xtCsƎ93Śޥs]:+u[}cGnZuE.SӼʧ}$wVXa 94^-UC*/L2%e7Sօ1a@G ];= "UH&1K M-aB~@@3u+C|g;D-8CTb#^/l|Xj&s"a/cv`=5TUݖO):47gNq"4PBs/ZAQDGsAyR r}*Q@{>NЉ`vΟhp੸trH5Vtk΄GXtq|O?HzV4{RT\li6/εQěYQ8piy,wrm Tám{͹gXRr 0Trvb>dTr)F(vݎΊ[sBSJRwmڌ4:YWSo_:n=Д}IN5HjmZQ$O'ޥ)aw};u=;a*Ix7I^gTGF(4W~Ƞljzޒ"%ÅPGAd\5JVvq*?&&C E-UAe(R %f8įSr| `)]I8])Q:{*r|NO,JgѰ|z~IKRn8Ž&~|#p*^\$3-H"\ =^^8VDZ%1?4cƜbMKjzv w/`y,_e_"miӳ (WwPM|Z)_Ծ&P{J-UL] ٰҟZ'ilqa9\#߮EU Ck0cU7'I ͵13!:.WNmg7/gt,*kw̄9pcoEpyv11>h քŎ<ƚZ=(bݾض9 \- dEmeh<*?cx_jLq+\Ή8oy1b%MJE`FGX6q5Sof=]oBCjm^x1N>xYk0ɒ|Θݳpܱ,=^8 ҷY L`_ɏf\WMn3&TGc6><ՂՇ⛫:y0sWTFHέ (9t8\9Ni,W;vi\*k6f󱋆G鑺}Ro SԛlYy)M7 =qpmFД{j.c}Oh.lipm?:˸NpZs3Ry;t;2¢/&nŶ2yi3DZQ6p>CeD'-+yb_ͬ5)oC_3bM|G3F׮_д'!5X2= W7Ob/NIW^Fn|4O8$,;Ki{^}oK_F޿ext;ӡ]#Ko|XsbJy4afń HC!d3d(~hAA]}wBrWgP VҴ=#0gttA{ף*]bʗ31KY0gMDeN^s{jV'~gVd=ļ{׻ͭ*lӁɍӼ"srYEpU_8ʘ1gXP:8.9ع`<65g5#/%+a^%" n-a3OEC^-hM;nVt; SQoMh,qv/Bֱ'{V3 'F `iqٴ6eb+;'}۲:+Ϗz v,H&2;!t1f ;r*Ǵky Šyi~>݋˳ŚYsIg,deF Y09#ŵ0=:ҝEAChA!~͙M)33^RL;)8s% +ye*۽nןe7d&v(O  Jެ0/ +ɨ數&f)UXIR"=иaf>OU#:zJ)>`-3hw,9bp"+M#ۊ9<>ڥF‚FKom꽀I9<۲d #Mb˸XKh6)_y`mً9ƊU<|^ikĖi^?O~iםJkWKLu.lG2qb^{ƅkwJ<3L*dT 7ŦFyZ2*7>u̗yoYAggϴyloc1!܊\2mJ<48|H=Id/=H2c!c|!^1ߗB4s .R"$,+Dwv7Qqo۫i3I̐n*B IDATcL8<<^XZ%F`gN/߬]zT{=L/hl ^O&MD]&g`fΆig7Oe^'y\B,f!88b W?eHyԧbq t:[ӾC%["IJDb1gX3m&o+-{1Uiq*hEFB!i3Bd XXzZPIy הN)}$_Nvw'WSs-7%t̙)LKƍnqI0:ӸYd{Hl!H!"e֥ȴE!)R&~RZ#im6%(1G)sf V˨c^8p@rOV!B!B!k^ׯo4 #GN}8HB!B!B|Xk:a#Fzf׃_u߿t !B!B!DHN1W_B2lR|˟aCNVZ<B!B!B+<~Y=j4ܼuÆ&.c<B!B!BKrG?/{u-eAIӦom6zB!B!Bxg/,,{Q)Μ>_ٳg%KB`4S2hZR1V k׬y>`dDʐz>vI_2 1Jf?3Ĝb/3+1"%eIyXsƖbM-I:::ri ,F4)Q9{,NNoB!B!B!޳"Ep)Bw:ptrHo^:B!B!" -Z$Jڴ΀B!B!BG:B!B!B$ B!B!B!c!B!B! HB!B!B2Oq~wj=1i!EqxA DymB9<866ؐxN:O/z5qqUOusƎ93*{埓$"z9i1}^%sfU<~Bp\ؚj1ի4;~͢7^sM$o[O܎4` xL~sh[St6*Vv wJ\ucci{zg7{-"C%9N}/sz7 6e-@6 :{kQ[Wo5+/tk)j--5h4IYdU,ig"]ԧkMqh}]k%:b.mfD2סX+-.&=4,6e/0y{]\~!1ddID븮T.&?B btv҄s@aߔi+?˪^Hy~9 bm]򴟶?E< e^x9ZaM 9K+hJ[nof%LNpai~_pz:.G5AliMY8/[&w9q4r 1w*%od17ΝN !73yVeJ|QFYpkl/w%(1g3SoU'+fMۗk<~!kX?2Y4}G m.KCߴJn_e5e'˚:-񻼎 㠁4m>ج9w3ksu{Z7Ӫt1vTq`V}74ƫ,T^8[q{8/fe'_L3|blѺR$3%t7 χ-#{|d[/eN/ *ŷ`F ƿ)>¨͙nZ@~u]# Cc4t`u~~y]>cM:H?CSf2 qOkM5]1k>yĿu?e4i˜Zt iM<>xO{1mH>?p~4ncӜ uR^Ə1Bb3}Bk Ob1gXE\J$nڠV=RA-yo%Cein}CRQtQκjŧ?ԫ;'2Wʩz#v02\PS*g(I q&^*`_R*2*23)U}c\VnS,UU*9^b9 gS_p2?@pg7e:4KT|fʣRJEmQ]Uᯏ3?UVf^ 7 /~qJML=:uwJEWLw*:Y?ZPGtx'cySU\Tɛբ.JW~S&+0$'7֫.ysokY< _BYWRS/nGC}y͝rRd1ԪεZr:*_u0AeS*Y5Ij$pUUU$U M=T.ԵrA?r1إҟTwQGM|3:1f. T(s*~aj4 yxmye5bgzeϵNզܐo aQ\WHvŘ3QuR:k^h+I"~W3>pTffҩjT1cKA][O.B _B)S}8OD$2$VĊ[QETmVզVQEQZ-jK?#E$f$d>DrޯW={=sv-z _ݛMN49Hn;d6Y/aGcÜasIFW8ԩ'ى+ /Wf⋥Ao !4l(_,9:)BѱFQTAT>O"Bh[26y WfAo; sVP9wƚY|}w[ׯG[biw6ab5"sG~+fT=,c IdP1o4㶽u/{-9 ,*Kv .p? wŔ͇1w^ż*vDL~,1j#sBľNJ+=ǢV=.hPyyf刾~x|=m>؍z|76l3ڑ:<ؽ2`TSj4GocкLfl^8p9K9: pzUZRP~T8h;|y!¸O}*%qȦz 72v)J|;N$ Pr1!5 vLbRf|Dj<qj_8vWz5'a9KQ9IS]:ƍ4f:_>xձ&f* >'_'NDNxhD2A+܀ 7ώ\*sterS/+V^(FU7S#?HX}.m?] 'J,i: SyF&7y^Öe2}!.FUYQn<TP7R]/M7= P$| wnu';˘sv̹)V)5I38khT>gNos"/=\KxX'=*kv Νc߆_G}$&G1><rd(^Хysh2;h~^N/A5:-f|G5㤗S̚18/aXxnò 0n⹥'=:V\Qyz5k|M6;߯P5;x1ga4{U"94昛cnݐd`d8Z}:,jxU5əK95Q=̙ a ;C 007D<ߊ˘sv̹)V)E?iN\iju>h5 ju곡X_>4y s難f07?*hj,\ލvs3Ne94kc י!k[U\/n8gѺL 8KIfI1PցҲ7|9.7~k3:Ϟ߫f֬UI4M83qU*3 HYۆ^~#NOtq 16 ?"P bHb.Vh BAy&5@1-[w Lc$fPy Heq[iYwڟB9uEYI~st\˦D˜I8opbdts:$5YgG:"d f MtsyĝI9r Z'7\wwu2Rkx)Iƒ噝`80v~~IE)9Ł-8Ս©$SĭG%J+^$Y5×9לsnUJZv7;|[7⤣͐>,eFLhεiWwۼ[2|;Sy1|u5?կʕ_Ӫ7%L1!+5!Tm8̜kq~~lS^IqIl*5 IDAT]#+o Xrz[XT-1lvo*Gz1ÙmsQJ+fF?A+1}[=q%CD0d=/&d1rj̹)V)QukDV4_ɾ}033ýz͌ϑxįl0q#z=dmF 1 "Kƒq~]{қ͎&bgwlۅf:a;3sS^脡Q&j4hKxyyer3u˖OD8B̕f|4䁜I$IO$I$IRVQ!6XmU$)\6+.yȊI/Y|?Mcl؊iuFdu^rOotYeM:Vؕ^ Jќ3LLL[m*S2QXIJ|l2snUXX 1\*3LlCϿx&ģuNJ7ٽn'|n\??sQ/$IRFIG!g>>+ZLMbיwa\]MVa3/^$)>1M,Rz='׉,ƦhFjig(IcSDZя}X40̎52l%cS~}cidIR4Ke cE_GJԒeaB oQ4sQI̲]~µOxS|gur<[SalOG)}[72Liy~FwA`\VC~/0.AȅU i↽zXq~e ȃ(t]CmJE߾Y|Yk} SxvOo]"=L~tKx"d$f(ՓIۮ/bHI\.=}8m)4}#Zc];VEYNe̾|Q憺[\)`yي>L):܏5&<}0 .CsC*:zF+Ff, q1"-bǺ(u;*KZ@3"8ٛ p궑><(WЀ˞|I~q5k2snUJ I.0-6ѯyr/om<ȫ(y^4$S^}ϬP/Մ)̝Az;īΚklxfiQmf&+ʚRuc=fp) wBxȭGJ. mwC zapތ|^PV)x}غt ,eڿ8?*@~ĭPoAFT!e׏bvx˚`h[~w?,cb83aR9jx7R &zSFŽXM-Yz` ]Ո;EO6mŰh&?>6!cLi=w/aQ"y1,__r9oqs sC??=mlZtXc;EA/< }oe#Ư.nwxQS< SLlֆEyˮec@4W(?wnSM;9=I>KzfKT1Iy+31L bwTZEX)a51u fdݴNVq?}nP56ײ3GգO@!7Ox|xh3#Liܐqg-iw=o+cNLLy_AC\o4hRi7ifx}9a 9c}u]&u\x)((s2'{M8`6XE?LcPӼ>y.7Jeے_L׺"`PoiC)6ߒ$WWy~+Osɘsv̹)V)6[##DtěT޵]5- ^͹&ڗwBz K}1v܂"LY3f¶Xex,[bVu}a{BDߘ!u$F }!v~SQg$t¼H5m B%h)\%lᡧ+L&-}_Xx`{'bYC}_x斘_D;uE%+x0 ˄FcF]/W@?oPP)ND !"tm1G}eG\Thߦ_?VVjrq;JS!"}|ΰ$# EW3=QKԤ)ޕbBmAlI$'1^E Vˏ_ VU?ߊ٧ї'v}ԯ&f$JZ%\Sn.VGZdYCaTl8h ;{Å_\Yt]uI$iU-E3Ȥy(VE{%E')Z0wӯ%Rl_#NEŜߪMCx05IT!/Lnzێ6COٕ.}tc1b˭$̉1wI893,'F})kwFUv=1v3|=Ůܑi0qzw>1O/}D-T _wy4a:e-0WB|M&Ę%W#[Xqk+~شA -/I5iZ}$A6'h^_S mZA8X |NVęปgbOEv@WOٗD3|u̽OԹc2W}8óF_),:/R++EznDY. 0O4+i+諅q~QNmZOxs6y Z_䱯 OĿy|HL.5z&v\ÚZ_x9.%k87ZIxt%?[8JU\-%ܢr:4)bR}a?gyLsVHXƑ0gaF}ltCzhλϗD3}ҠNj6㛆7%%]'|!^ZTXN3xQnܰ3T,cΝf?b]у{{pﶸؽk{]_dظnUG}O;X_BY$g m/omKdq}7Kj,ʒ… \8tA1daݰGv1C:1/2*yU7aGsnFhEjJGsi5|"gSxS˃Wwߕ~g-:lUk_HMzz^1 #q0*ImljY$iw+|ЫҒzb G ~W]l "p#cķT0.=-O !ᎌs,jգ|\6W<%;~%-Bĸ]y=?%D;ߠ8-0_Z;DFg}NTqʌ*s~ZBNpNU:* >'3&'<_d4"=m^ _vWӥ+}/~6"}Y FMF1n+y:b -n9S]ŴgkDۂg.pLfv}!/An$^WŒ4vٺk?z'=>Lb~Tq}[|Q7k :Ml |߼(FYL!>M3lXFxU1@//6uFo`vpުKwc_1E1R*&S{<:X7mCh=ٺF7=̖CTOvß?Z>]uzO\ΎC>_;R7ҭ|6F˳  *5;ٳ{s;燶[{_}0]n-뢛AU@2ylQL:wYޱ._nԍ3w1^+ǧ|JKxcے>?>d9;Z?qqy*RUh)@6M[ĵ8f41oG|_'"(]::8ic쿪Tm9R ɝ31bXӒ 3,7ݤP%TIf"]↶}*Jf.vժ+~>J5QsvQ"]閭L9(N\E4v1e}ĢzF,Z B(eX/Q s{Xe‹@"$DԥRĪ0Ѩb+t5fա'Y9tU\}Zm0:oʥz,]ٲs%0ѥċU,ԓl) ŢӗE&nES[l9d˴m?k۩fh-NlT=[A3yRyU) $ llO[!gW )jLM{o~SVUOj 8r+jG]+ctGиgv@Jdžx(6v;ȍlGQ`O)*^VE -9kȣC)f<4(MlT ҩz@-7]m sxzkOUT<5&æW 6 bmҔvxP+@ǜ¥JS:iVOp5HEJnM#tf0k;Ŝk#ұ:֜L3+YU_Cf_=mPfl bu"6Egt?z{*ɕA{8+يſ^SUi(Ushԉ_GΛ72ĩ<-ӐswW5jr O_& \W>n7Ϻ5Z2snUJmR.qL8$9/մOӛzȋcO6?yG: :y_“!ΌܲFo=8к]%RT (tždEn1 XUp_X:vʚߟsطa&ѡF_?ɠLKVsw})w=Ԧ̓mi%~&VβĨ i1;%3b֌y~9} Jƻvݎ)u疞XsEA ֬I6 Bg?T7 իǜ/ѸRn3f4%9#94昛cnݐd`d8Z.]ǢQvpBG "^=!YhbJrb>Cc+RCKФ- !n̅ %mH%YKEϩkBhu,/TE)Y4^,*Ѝݩ+ժΖ黱I6hmMG(?+K!/^4fEHiřPQtE H68fstXqz*=?XֽԦ\c^mmxsp-[0C$~Dz!lbϓg !k[2EѥҰm Ie?9zhɿ / &+ۓ=բc±c?#P 6 a+bek& -Y@'Z]lQ¸UqPo(;mF 5꘷)>\Xu (  fM'ύ's-^  c$@;ұN:kχblKRc߲T{z#jA]f0kV$Moj]ؑı?hi9DjwJ%ز+K,0{|~4_RԠѠ% #fYGk̆+YR,=h[2绺{Y/ eyBS+Gs7zB:w}?99 ,Q/c1Xen"^;yӾg=H.K1۾vFGs?2fX9 IDATTs-E(lrCGq\\]qЂΤNz|&ܗ [QM[ʦCڎe;SqHnwQIz\˸eބ-l ! 5jTRp'};O1GFZ(Ъ_^bk2B qf,Жχ+Eo@dy~N Fb;o?"ԜW~}FtKVZ#TsU"Ȫdq̘sSR2GCҼٽ\_QaVx%2dzfB2=8P4m])W0Ʋڬ9/oҕ eJ&f-G/(דK, 8u'=S[Tu>6_*fDhaEXۖJ'ʐE߱ pޝ]'&EM\޻K%\]>K) h ɣ.g%<,қu @boU2˼`S/ҋ=-<<{}J*ʇK[:=zS_~AY'|2)헞RA< ճ % A OM4WY=a6fr7\8yHޔ/u R,t"csDW^0s(e^X\aˡFUtG3e\ǿ?O簮;6[KIc8V㛐>Wh7M1ݙ`0 qicbv]RVѧҘ;ڈ ݹ ;`.~`}\og| #?/U2kZUuBNVc2lHjLJW|nƒg0jMMJHRǬkυxy>Xjɉ-wȀsP\G0-Ψ_:=mu =V5O-i>Es~Fڵd~H>$:B1wt +Od][JV̷RK7뭡&y8VeWO5:>a UjѴ_ԁam] ϰl bA B-B%נc; jT*s,)<9.ҺtݻIRpﷻ3Ȼ_ud*kOCyΑY*SL2XڍͧҦ`L6 #,[+F˘̘sSRR2CE++v.c1+GGMRi]F0bd:|;EuTIn5F6޿з6e2 M/b-oO@0C3,mKSqŞQ}Nl#w v~i/|(q6׹հ5y:6~aRe</ϓ~Wz/\+|d+* dv-%*Ԣb?w"cJ1ڈkFA(/6ep2{ijNt8?E h;܋m j'r:^Q3'˩wB֚gWYvb: vf&ZE^܊t'8fۤ1{Qgذ&ؖ;73ټ:F? R?՘#}6tO^XQȵ. 3 \?\:|[x~-9cU7,ǰS9Takĩu/ņ6cck9 ހ49oHYO$ ưw_t gƷ͙jD*w\R1/o'dH۟&ؕǘ_e9FN97*%Jٸnh٪U+ٷofffW9ÕMI8{$ls{nѡ.HW=2/KϹArc!c|!^cul.;i}S+ijmdw<3_2o uOzsّDmP>L41CN97ŚYuh(NVA+/]+s [| Ź`3&nε׳+2Z9ʎcIL I$)жs'ڕϹǒ<~)g}J<"%A/ÉxNF~DƜcMJ}Za4o:|Qu3fm~yM?w?xގeױ$IRAm$I$~#Cے$e(T,`,|3Tp g enFsSRee ۖ8r^f##([vYI+$Idɂ{Y IRE3n=#49#S1gGsnUXr$I$I$I$I$I<$I$I$I$I$Iǒ$I$I$I$I$IRX$I$I$I$I$IJ@vK$I$I$I$I$I (׭-[!RҾ}033lI$I$I$I$I$eCCqpt0Uk5Bp%29w@Qغe Iýz͌NsHOdOrd/a 1 "Kƒrc˘skf93.;l+{!GE{K4 QލA,(RnA ۽Oٙ=I2$I$I$I$I$IRrX$I$I$I$I$IJBNK$I$I$I$I$IIcI$I$I$I$I$) 9y,I$I$I$I$I$%!'%I$I$I$I$I$$I$I$I$I$ID<?ά;0HM}LdΞ_$IʽDd@YIY1^$epa'Y6g\φ:[& c0DV=wxӬ.>`s̮ =/Hi%`T,033ê.}HD/u"o|vacݣ9gǜbxD_פAY zc/`At@`nJc/EHߒOƯ<==-UML\&imXFhQK~{G@r엮T.d?ƛ3NOȑ_Ykc v /~5zSGLɕ-}vC˃]?Ҫff8iø=f'Fr!M~^̿-NkMɃT=%a/ϳ_m\lL0qN\ jnm׺'R^cCO|Kw!e/Qw=?De~JymI|H϶2O\~ܶ4}!QL, 'M{z?>+[kc5Vn΀_O&/d97Hv,E~kcTJ%&X9Q7 d8Fab;&!X{EN4R HRv0ʋ+.1C|F?6ʰ46? Y>]]qq0k2snUptK|׾ Zڿ|fBDxcEҏ̺RuAwNy48΢|Q$Qu]c ضږe( _^sD{fY7 @iGI5_+sdE<[r5 l_8j7,8ypBC"p춘}Oypُ-KFhM5D#鈹5Kzws*xu4 &TG\ ?Ĉ=æS5hAO3l@{)Lgz1~Ph_ʅ4k:MXy=J#/<ř/H* '㴵>85ɸɴ괒GƉ EaT$1o3/fQxSve>P[7Aݱ39;& { ?5Z9e6U~?͢qiIOP{W֍'ФLa"ڴvY5 wWbdv?Rww?M-U?؀w(!I`t,m@^c|B &mRas 8Pas7QCaڐ4e-) )?䦍}./MĜ1E0ljMڶ2;F{\M,xe/izɃ d!4bݘxei4i S5bd%^}eh7lxƝGxQkß1^j9?FO6m636:ë -qKwm+ 4&[핽hϖ?׍;'d9;f֮ژHH.xXH֞-}.$LUNB!wy55 _ԊEϚ"Z,[et&[a.-ޘ&g"BL|WQ81jFX;U=I ^,iEQ"-ގ/ugjQkB cEMa}?7M_#}uzfBUXOl ҋ̈́S'!:`06i,xѻE_{CQ3⭽FY vbuv[D p.* ;}?[H8FPB/7fb굄蟋m= #~P+N, :)AJyMtP !"& u_ r؍`pr<_zGDDeżFor׷5Č[:AesB sS ^;DBiL5PzИk6' pLu89R6v}|_ >}O(cbZ8%^SWJ"gk;aQkRE+VqE{oZ!1MTXf˂]/.i"̍j%:f~zz<BmQZW>0DHc&y46J fNKDHah\V ;\u҇*!/geݰy;FܙUKse^73 2-Ό)# 5%Pb7Ecp(\ ]{q4J*ۈSbNTAaV s jc'E"9wXX8`Π2ˉSATF[,>uGg6q\jp)6uP>JUȎtI'2׊/&}MQ.-aOʿ'P ܩ{ϜHRo//OqA] yQZ=%DGE/Fx}Pi3 7bMܱWխ;s}7~2b|L`D ˷oVXPmHZYb҃~rl]؏'GӸXOOj#˛QXןiOSx.:ק-ͧlw F1Ba IDAT:h|Р sR?^Q<˺XMNК+?>d9;V@T.b<ЁKly!׊=T{v*a pU [݌'A_%K>%eǴ$Cذ&,F̵KoBy5p.bጣ^ {Sn!Vќtejb9yZQ>}pnzPq [e~*w 1w>QMa!n0X4 sc-jҕ}ѽ; Q(3NqgF?X+x' b+=Zp7xCXD}{v8)xL4`^o,ˆ]srT]ߖ2O}kgꝌ88vvl!Ag6q'C &f&ˊ+yx>Cj=AGrEL>c^ F!0v3o?V"KhP0Z8)"vX'g=)`L'5 }㒍 CN G=Gֹ\@^Zbc֬Q0?>W.tOئޒFu3+Y3m؃6^^?Now@եSb9 @%~}pqdnkեWmI"$ ?tFxQB:C5igw6.C^_N= b&K=4oJsATXg}xգ=~wʆgWdgm=,at\_:5.u#\iӶuB?7稻d?3?}]˃ď\y|V qj9OWsT_@wfZbOcn5]7m`R k:Z1SgB1 M]FhP8/ o.tG;˘sv̹)V)-KD(4hj4?o4(댃y{驞 }w̑(l@cAb :›Sxm;T"a @2IÈ25 ZPWd'*ƎT}aK>VgOx&,0Xa^ʹy{ U`fiOy,~P@rgYݾK|ҳu`]fnR5]2wSldĸ봚6)Oaق9'8}1CJn;~_q\ ¸jW͇B X[/DY-(i1k pT«N;̪U͘gp/9^^l`$Ϸ8XeHoASݤd9;_y 'bYIjTiORVKxM&/#2@n'0틻࠿ß_vg~TajU+h8hW:K{\j8)vp10}o.cB ~tVܜPP-}s$- {Ff+P4gȗE>cŠzzQ 瑠0>C?>ё)p  U8pG1[L-S?v,TAKslc(H!ԮkOK[f.@ Q S<#(8 (D`] zn.Neݥ_hb|=sH|vGPV튦֌J\|v8Do:B=FanT#]/$)-qvLHkq-z04TCs´g6!!]0A!z(rǐG<֫ϗ'G:P\o5>LR{KGZzzl\뎻g$;qykҾe\5azq|`|9PXbŠ9RǛվ}{^Uxa €=gh&sFɂ2snUzUsM'ثInTǎօ{`+%֮riϰZPLIJU,KaӤ ]PD2Њ)kS/x>vu[R]{Jc~CUK9<{2-pprӘWT(kcϑp$A<ȁ]Q&.+&sz-0WЂpƲ8~%%ʗ4xW'8p"8ĊP4PڕlH+-[b&Q%STq46˂x̏2=IaƱ'5{R}m^*fX,oeKԑul %S};+à8ZUW][Mc\H4ѕܼ3{V`^˿a ^=Ca/W͡|0<Erg-W<>)fvm?5aC &9})UjbkԥB|rmBf15pLTUA܊Ő l*QҟXV!m7TX/:W囿۳ <*` c3yn az'0GX4v;PSc9wHTIZ(~.̤,|P=?'𿡢'6]OGӣHC{~xai3-*@Z9j{Q636'㌆PS{0onEŘ,}U4TG0qU} j]7-`1Rw|<Ж qeW悍2GNY#3dPjMNwI|YՌMcfޟr+o#IR#.ܼ95(zžhهayڋ,R\럤ͦ \ulFIQX#N`c{[Vܻʰ77XaڔONR<߇ V`\"zODžnQp:qķoe"NtZ{CtlS5y(x|t#[.C[<;wR_q{Uy7ow$Ax?YIq8[N]g^(lIЕm^Y\Ɯ gƜbRAJ,lm1OQbXr5;37ݪڧC a =.+)RJP$u\]<5ʾ4yυy˛1S:Q78%yvpçg2im92Ͻn;e=*c{43&aݔ/+A㦕&=WYб63(V]盎IM)kg¿JEJQ$xp$ E8ׅeD~s&_< Cgr ȠV [?mcN"Yv2"o`34޵"ʦ9ԙAgwO/OzS;Vj¤mHC As p鿚m2hVwa~W4#eTHׂQ8LՔ1|3b,Vcހ1̧{=P{ 7tvr6Or< '<Ԭ.?p87YJ:-wjSz9=٠oe3H|QA}eU4Lb֪|9+(u^70M w*SJŒ|.eձzC _~4k-:/Įp2T$=,́3 a`#ej2pjwp4,Е 4|WJ 6tq6.ɋ=7)v1UZےt'@&Y3ggda:CLp(3pCԩY 3[ O#׌g$|6L, 6ocLdt>#ɍ熘sCxeO!@T%S+NgKDZ-Aod3>=q΂Ǜ蔎'ɘ!'ǜb,'Ux8E]06I%GN^._W&0(lټ#0/1ֳQqt4-6S{TSu6rX$)>C$I_څsı$$"%=B1'OӬPIDD='yϢ׃Șsv̹)Ve'7>EzÀvŜf=xu;ځօԱ$IRA}$I$EcݖEے$eX߁^p%dM@ 7Aʘ3n!cMJ.)JZh9YúttH$X9ϐ$I$#OߋTH&jMvZV#e]5#(cΎ2._%$I$I$I$I$IRrX$I$I$I$I$IJBNK$I$I$I$I$IIcI$I$I$I$I$) 9y,I$I$I$I$I$%ذvhݦ ۇe&&K$I$I$I$I$){xNgM}N^._W&.(lټG3*9ΉcGrt|RƐ${咽 1On?7ĜbL,7+c,%3<1l)rؑNB$I$I$I$I$Iǒ$I$I$I$I$IRrX$I$I$I$I$IJBNK$I$I$I$I$IIcI$I$I$I$I$) 9y,I$I$I$I$I$%!'%I$I$I$I$I$rqqf}ށIGb:%$IRfa>fuZ$C$}Z2͊,3[g^Lw-1p"Ӓi/ΗG̩CK2XaffU)\eur]'Ƨ,o6F=ʘsv̹)Vȓǂ (l[)8 ҞeBe[q~'ٵև ӷ$IU>O[6e"=~]XjYo8Uw| 'T2Hҧ6iC8Տ0+NȚIsq52S?yJHU7&DSr=ϙu_R45S8 QED.d9;_eq> cnhRDmlKeZ{17i۵œn9٠Q*jї>ɣ_נP(3(1qߍ_Yؘ6Pt; Wq"nHSJ91PLo Ҟ2MќNnâV݇s2цy &6鿔Z.+:jʎǼM:R<9 (`FΕdžqqc+% S[\ktcl6Y$xz{*[뻛\imXFhQK~{ǫ˝&F+I+}?ʱ_RƦvlo&zBBXk0+׀\xћ:bLʞՓiSTeI]vOge am)}g#}KSt?9kMx >Ű3`l]m{?']dR6FTb`hUZxXHc&loB{.ŽQD3ۨsƌNei'^ߠ7TXt[,٤iR_>#\zlweJZm,ή8g*AsΎ97*} :q%ak_[wX>z3FJs"{<ӱk^Gf@IiK{)Lgz1~Ph_F 0Y#FXo9Pq(^wpczdiZ'd`9`wG5i[@A:bnf^ la, W"2@ D qiC4*痵 GK 10~Y7U38ދGjy,M+='gF emD[:Bl4RZ:LNOU<2NQhj~ǼΘFNەxx2Xv1w04I;u;>C>O1icFK/1gLL#qb"̎>WKh/EK^ r5X73FYM$xݰw!Y F 0TV IDAT/p?N[KsSNQ8-7N~3N ;qɽn?D8:{;<{jgQ(PC968v%IɊ^gb+zsΎ97*ن6&Rh#w;,x)V2g{D럋* SB蝽E^MM1v"tѳcj#KVY4w@ !F~KD !7jAb?>UNyLZNE'3}|RFcacHKL+N, :o*ZSF<vsT ,Ɯ =[!§P eyL vS ǾīTsJ@@NXԚ..TtmE[Cm2rXLiv\lO)NǦ=~JG 5!6p(gh-z숫hg CbD~ a1sxeFg?{uyܼ(bc":}gN&ۊZME"6OH\7>@tUJ8ٚ J%=O=JĦ,^Y!5('ؚ CFX:ukBB8G( ,4*6'< [ļ[ ϲ.HUaQh7qJ[GnU04seEU((c\z坱LQP-|MčEjE*\EtvD$?_4eͅP-,J#_ !bEw)80M혪>nD-TɌ%bq\PZۉdEm"5ӛSiێY!iLjC\q:"wGz4F|$u{ѿ((io"4&b7㤴/RoQ;zk6bMHsΎ97ŚY}|ŭWmq-w{]_Lذv KyaݲL+~#id 'z3wl[B ;vȔ+xa'C˩AaNC~G2A]N2Xq.M7 ZĀ{}| xeRT-oK&oEcBc̩9jFJm͓#&%"粽<QzviV`_ϋd-crՀVV ODWfţ`" )Z8fIl%=QXt;Ă~ViMBJ@< 5yr(kIo#?8j6Egx9bQ`]l>;D]N ¦r5bܯ;?+MϨ^?Fv|Kz?ѾV|1kZwi }"P=lNsD9]S9!-ݠ2Ϟ13 -QQD%"sTZ `;w,eUfukMc,_qf#Q>QaAa#ieyKə u!o? ĵU+ $00~]ot5ŏ0!pZK 9uϹl]x0Vt4l}}j>-{b]KkݛJ eQL:9DmNT_# ~HQ*S-ϲ~fnTū9iЛvLz] b3v s~.`Ɏ}ZZ6fMZ_{vvrtvηύٲ&ihtfp]vfFQtW^IABJ3twmdvW+ƶʠۑަ?M<~%DdJ+ȈHi~1=Dzn>V|S?wﱼK=zxN1׾Uc8(j%q,ۺ[o% 8;st30|^dnHّS<4~ޝt ,\>4M3e@6̍?Kx6QȘsv̹)V)2\RWy'BUw{4O};UaTD\#RA෺'NK9N" WCWe7){v 8W긳dtgzO~K<օzXR5 }㒍 CN G=Gֹ\@^Zbc\hVJ% x*A&P۔qJk .?JU*Zn~aVgU)Nɿrx~60P4cY!zPf207ZofNjWAo ~ B^/ҩl1?GP>8@82 5 «6 $WR~#<(^n_N43~G;X`Aӡ^/BSI%~̃MyMG% ǹco*,QTTuy|Ѓ>~CE;eC3zY:=1/y|,;M%Ҽ8*ĺm;:b*_!œ Ϥ~fpse@?cj|6jӤA= zwXBH6wZY_mǯjiQ7\y|V qj9OWsT_@wAyLޭ Ljwa~MBK5fC[7F}Bzh9gǜb"kKHF)K3cf}A8X׹ꉊ(B (I4r[ z@uF8ѴWk<@?\M:gOx&,0Xa^ʹy{ U`fiOyLnӢl8K|ҳu`]fnR5] pk' k3Ȉqi5mMS²sOphb'B{yrI~ɓɒ86>M8H(!.+f]ܽySzDX.  6Y^洯so6rU kSN) MH\UpAWB/Ι9k-G>wL5'يc ]] ʺddsnv_Y+ӸA\w|Ĕm]B%̫x3*ʁ/Zn9SiH>=?3{" jG#˽ f rm/\'RS3vJRͽ9{"t60Sa[[əzbaL' qkD xAnW҅\Kܲ7fDX_ .˿H9itor߼5^*xx;fϱvXE|Og< sg8ow&0_]0ZViUe]㋞ rm7'H+M.o\3ak%Ɏ=)X]KTV@Nhì~ۄr8뻂rElޜPF*Sb "qgC :yΜ3)H&XЋWv3h 2]T^4mG6w9 ֬WFk|qyA$MpO)Dŋ# ƾs,Gk׌dgqaDf\$. 7ybvQgrJPs^SᑇXlߐ%ΣόpN8;o{<Y)7Q^̓|@ez )Xg8Nh䑔Ϥ-WA[)7,VDp[ 5kz4.[͡C> q%q/lXgZL}m$c@oՅž}Me>PfNƌ0\~?C¼<Ԭ+{:ӴlxtW5|V/, O$oSK\A-x-vMv*xش}N+ ϚLX6ѷj-$z5uQS3v>Yc9JT1c8V]E qҩ ܱ^G,d&WF?o{p@;܂jSB9)t"S :Ϧ3qfBQN9R5_'&]2oC^&YpbΔ3F>f |[plrUG|w>bGw(CJ){T!u \8qKv0|փ9p_cVoCr͌O^"Eb۱5 ,Iq$(o^NkG/P7m%gU e7ۢγoLep&cq7g0"RÓr5JƟq^ڵ--"͞ TU|u3D>6o_jq_)&xH5Q5${Qqa>O5b;d1c׾`쑣\tK\D|ΔQL'?"5m[jT 1$%3RE<¥:v)Sy}ԯgd]Ggav2Uc\0Y?ľTR:Z+򙔥:D>b3 p+M^ Ld22SL0~?< SH{sᝈc^Á<վf}Pqt_Ph8 F|)E4rPtiJ+ޠy<5-~Eb|/ypB"E ]{6C٦f4}OצlP' ܆sZZv)̷#},d+Wv[oݷ+]D='u"uΑ͘u~*q{M>;~_,r#1A,}YVi4m_tb3%q;b%ki͒#%Y Xʗ,J0c~uA#I?Cٹn9?FY3f>/|ך K/ѶZ~|Bo"vyP(2]{Q5 _??B+G똆{D1ٞfԟ܂^E#\ w7z%`Z+[ϢnsL s+F|GtΗJgo}Z e~c,`^+FV'bѼs8^9S,GjiI%ꝋqϛϾ)w'բ nS&b͙G8o#/DH9l^.|ÎUxP;>3z36KsECye>;y㫧10)߰.e֢02ʁ{#VpaZ5^/ar\|8 -~Cu܉9{wwJM>l܇JE_1vvXKKԔ)ϾJ'eWܡܞsKZ\Ӓ$dLTQۚ2IOWχ?|bgtW/n=߶abr3v}фSsBLww8akύ!q`wVsa}ZZͯ[f5Ww/7vOS9l,[8K?;!\cxyדxLR~pqIn dMTޏ!ԕ7ZNmGk'mNz ׺}xLفlSP?&W~6D<MM<5ŋyq76 {ɿ돟}>FbI2*_ȘUoİyO{rLHKl}s8/UeʀwgKDP#e:?Nui!F]D=Ce?8UnY)T)7a2}-f HtZڗ7 a GRG|z?gwL,?ٞY†C>D[vJo|d߱%~;v#oX֮K7nȠ5o1t ;q㶬<y_cdu Цr5SnvX7xHfBQb;OIO=o$!3$U'ϝ{YB_>Z͠ERh›wP,\3o\Nپ.7.0<9^kL+|e%ψ$U!viTn|7\i9)X͕oӮlL Y|St |.~7`N,ʸәJNߴm&K<l,^>V9A6ږĕ;\3z,.E3js'+?Ƭ;<ƍןnbt}(RXqW׀17L2y\ONwdؔahaEm8߼|ReYc&#^e+-v# y*fS,0IhH-sZǩ q'O33{#QH1VlպUoˋ*k=vc5{֔d(ZN&}/iK~ʌ4qPǡ=UӞН)Vomdj>ADD$Q""qaתS.qS߼r!oV7B1˸Pv$OȘ ǒ?sL|fD͈Fd46T^.\<5r\H5.'}RZI""uܽzzb;/-=srN>y)h8ڎ%I#q.3?dZn|,y;Jk6^9cq$]*Xj5mn1DD$PfHZyv)7 \]y 8Suq¦v90Ǩ:EΏS]%t[DDDDDDDDD$.ZĞ{»LLVLn]RO[DDDDDDDDD$mݫv}\lT(QQ㱈3z4^^Q?{zy2Xc4s8;;_Oy,""""""""" 4#?h+?4[n PohedT7oܠ^M;&ibG;،n1Ma}v4hpkwqVqҜ;fá$m<P<81ǡ}/g{ꜱ=NuM)wlM"y,""""""""""籈HFa00&8Jr""""""""""ǮtYaI4y,""""""""""x,""""""""""x,""""""""""x,""""""""""x,""""""""""CsΟ;0#""!d͊Ŋw8婈H씥"""ɗLXXjKDEDD~ÇPx!<TDD$$Ԭ]4qBq<TDD$$(EDDrxSOY*""|i|q]H܌PGY*""| i\x fdJHlS8)KEDD/mh<6M+%8N{j o-ס lHFo+gIB{UyjT,H.ԓ4;f~DJTҹtpc7sɭnKV9I9WhvrTeHGW𿳜Yk^jϖFks9N0I6]L瀘nԓsO>ǫ3++ oW1A^͈]_"TҔTΟN>|8a8AyWVfx5 6gg<,7t>QOZ_ #PJz+D0o_4A B_XN[{EBeWsR<kppNʄaxh$MSH=s^zW/,/ϼֻ 96f>_ ?<RsIN0<;yM]Ή~)tˏ܈6LjX\Y}pFj,!rr=c:T#/>9JP\E"3[УAIyDҵi5{Rk[g y4m֚&F.&ץpvdm )j$yZRtAqJ m$wd~q?3uK3"u6{痶QzTtJL gxBժT]]0|POo{mm}hzԧ ]jQ8w*Og/ĴdN}_r^Ƒqkoj0l' \i潭7Y4kBxmR6 5k_JмWk3O_o3.1Ǟ'^In+$ݻebwkϒU;KaGա 7>f;ߴiwlbIʴɞ-bV4KX9yN=XrQ$*a31HfVf ״+gw&7gO@˖WrH*d;Ь"B aJd3.5q u6-]?Y1Y%41X#Q>8:F]lyvg8#9IWo;q"+,YywSAίx5wii=MdCuݬ|&(IsS'%]PJZ:;}]4yPip,m囙X}^M:Ϋ=#珦o'?I5?wIgbϿG}b907L;ٙ\=!wrl%"X˟5M,^4i#l.f/C~gnQѧIY*A2lsyvݖ}~:֓aE- nˍs;iɨM7{-qC;Qc|'NZȄ`;7]s/bۑ,JIizQ6 01y2_w0!q!n?ٟ7 k'7CW'VqI88Lh8ݷϑ_H:cВso؛/@ts^?\{}v#֮fE&)& 5{FԯԢJ_&uJ:-Ee=Iu5""zm_6UW3tfuYjcֆ,osu3p<3w/|8-k<uWiZsjRi?_<.ޥi2e>_B)|.*ӨnM\ZeCSr fǒ*q3sq,gyʟ5MOA^ Ԭoh+iDmxk}3Oi娤CRyNQ{ 1"&v ӱ%Sނ2=uZ9/"Z٣v{X8/;i%gǷYr0՟<0."B8h! a6튑'E`(9EȠVql el&6;L/Sf}϶s}~A豭<e c^#]9e+MnK:8^HEvOjXړJ )@]O cmw$p@1cN׌>'4*je/'06Ӑ2DDT҄Tˇ{4^Auqs W |w'/&c\f̮p5l&QWK9A[)F, r[;t25&.Gc*+G㛆rT%e<&bۆk5cå$b oc斅Kh_u3,|+A1 9W'Ked-ފEd|j:}cɑ,r+_;iB9' :$y41Ol``۱;CCXmg{DRWL0=;7tԘ kaTՋAuW38ݏ}OkC3J iU5;ExryBfyϿaF~‚[dsr7*Hίzua7gޟ4߂{3IoxѶW3fLN֮.mßB35׷XG4ָ \WDPw0El| jIao+7bN521du8ϙ \\YB8s~ͺR7Ȃ㋯} _+|%I婤Ԩmٳc'ma]ۮS[-FJ.=dCΠΗkFس)7Kz0әQF,ZvQȊPsi3k{>[OU:R>[hh5%Z= 娤GRy\`lurYN,|0g51Ix[[UzR6|ξ@b~phz.sء^#(JyUz+gNtM(`NyinRw.ǁ.9QO&g+˻D_9}d8.d p#[: ޙ +`T=9M``aETR[jf*~8/l(U 3E8a:ib, ab ;q㉑}j4 zekvSz:s<_"Ԛ/lhD&׺{eMY% JQoR9z4)K1ad_nW3~*R:ϻݟ'-w̭d8ge;83STq8nn* _ZLZ:_@=)ѭ1b½0>\f.fA,> 3UI[j3 w?2is4_͑9 ]"2ӚP/6-`._,:ydS3ǫƻpnz\<3#3(dRKNN Υ?w9sEnزpEZZkoGCڊ(|r6~ <FE\WW"TDD$RXgwE*XUX"IYYuXQމdR{Wa1 C}Kb`s婈Hܔ"""ɗ)tEXtCreation TimeDo 25 Mr 2021 13:26:57 CET% IDATx{\ë$ KHLZxCG!"""""""""""""V :EDDDDDDDDDDDD䚣SDDDDDDDDDDDDD9 :EDDDDDDDDDDDD䚣SDDDDDDDDDDDDD9 :EDDDDDDDDDDDD䚣SDDDDDDDDDDDDD9n]|׹sPRc̀U)""""""""""""", foDJw֕(IhtHO04=n3X_ wXˉNsP)"""""""""""""AgkqX'p"6ōZp8~Q@x#*U|sh&*IZ"""""Ҳ],Nk.m;O_!DDDDD:MQ SNI{ܴO%}5̣/ zql0utQ_}:G L^Ԏ]{ c㼴x<\i kVU 7ѾTh-ϖ\}pDDDDDD:q2P\USk/.#_2m/~U^Zˏ/$M3Ԝm(+C} ]tpOyMseCONe FuY!OY|`\{a~}՗'>}#ѿ?<>d-+hMmȕcMnOv, t1o`t#gmpLjGw! ],,/vn [""""""W[8Çuqr" u\bե^9SlFٰ;~+f_}fOuѭd|^zٓrf_}qq8vMCcّc>}c->8W7c[VfO"""""Ү 4 ?/ ika <9&F<(O_p67Ɵ=SDDDDDj_??ȕùl]?,N;x;3k_dmXOTf 'n֓|l98w3/!AxY(7~wEl?(=ȎH}#R;ڏ`̍ BgW '~Ζo=-^Oǻ1q{0.T^ !IylP".̵OQ_LJW_L*a_m.^x w.;{[̘;vV_=snf _e ̍7qbL&6{쯬H ?Cb:`L?(ti xIz#}ft?Jܧp/BF˾=TS߷7jߒW6&t ww.KIzb4Vu>f^%~/mC\FQ~nK'QYALKϳoPw\jvSg;7hc('1 gUָog1.$|}: K_̜Lp8lǩv[ dm!C.usĴU[Pvj+>O.<}|nvLN:ԫ>7I¯׏n?S ]Šx'[C}}~+pƫA*J?]Oshҍa{:VRf{x,5g(;o''e8 x[ ,%zt0->5i:#lzf% x>v,2u^œg}p3V{r웸w:Okln m=X+K);_h l 9l?I ؋J D1`?_g1ua#^eAuX8yj>VN3ڋ{G ӥ- s1B︓ \f?}S]G'yTsA[<dZf@<;тrpCl>PriI.pčzbQpx>gƧ$0v uퟢ|Q;ۮS+"""""ij6~\pEiSٞ/չ#DDDDD{.+<0^۩t{27a-!-%^Vz%yjvFcӓy|2r΀[Dq1G%vpXϒ!^38 > 5_e:>eOWEԔ"%\&Cx8JzaHYsOQȿ^?/oACհη0?F\qd8OKY^[30}@ ԶeO[5ԀDAdm?~.P9g⡉ؑwpSkȷ;yʗgm GwnLAWa㑑u A\pT}kO?ʴg㳦R&]ԘבN=u on~;{n gn51bg1cFrgam霶6gFm%ϋys_]Da a8_ug<AWjHwsg\ٻ.(竷7p}b`{_G2 ,kNSY17nt^[ ? wƭ ^lQ5ٽ8^tiZ7w{ͳY-o/bO^!uPɖܧ%werfo&CAvYl6篳l_<ٕቷ: Jv!G kn#;!zϭ'w}h'i$I-w2z=$&B07f:s7䑉2w֮ZmkisމgzwqVu,mWDDDDD%5UkrO]{h)007lYU^]Kapqo~ԀmOɱxW|i3ه26u L~Kaf \\06sx G]dUm ^K{:w\Edf$[!NbcھpԇV._3vPZmβwa'=5$%ߞ/HKN)<8:#g`mȷcwwƦN]"};UHܔ? aoγ_/♶=̱׹讼qQNDm}\eZr鯿m8Qm`N#lrM5;' :1m-(7~ wTut` Gݨ9oиg;{t5c8V-OM W\7v8vjj];eSm(>1Cz~V;Kc;^^{we0+(7_=ۘO_/^j;vN|qǀL끋->>\NY*:}xr77<M6\ҭoeP3o{-惟k6l^яGnmCJ*Х;7q=>w|tGs\x[EUxxf- 5@Dq]lS6>"i{62pst'/bVRfjKh8|u֜l^>DDDDD^WCYr٘ȪtgF^&gc8qZg*GcՇxTST8<⃔,fo8O[;'ssӼkJ74i>roxXFC0 xbr&^9_&onĒ֮摑pF~`F:v-a2XDɝ/#nwθSžVU :8ʳx'(#Wggϖu_,4?҅;_|:gs4;/?[|3;Xu ?q<PTNB2?-fԘhh߀F:q=5M}z:pkW .;۲W 4s/m_R' &6l^;\o?3}IG׸meb9&N=j+[י#i]GoNJѥ.t9ߏ42ٲ3`['MNs>EDDDDq%O1{|NRh3Bgb@(OvAڇE8:Ggr潲/ l0A}(;vrRR|<,Xm*8Byu%JN\;2Wz_/; O_|UPE)ߕЙ r>U#_Y<;]|6OnDڋ ۶鄟r FCk«}v?5|w *쀝א2*ԇ|_'qP.s+>'{w5\OS]#<>(X 5g(. fnu8Ŷԟ2=RonUGΎjsL[;)IwSTU۩sN~uV߿"""""r*6nl"lnߊNv>tn6("""""re;f$}Y] v;ts^_2S4NDDDDDD3 u»'M,/pϿ"^S׊|t&aZ_N~t-aBNBҊ<8j3h@չilau^܉ NC k0upj?-KULwN wbaLOT w:i ՞OS׊e p ;O(hyg4u\V1ōlN&"""""""""""""r(kN(kN(kNvt""""""""""""""NsۻqkN(kN(kN;%H_Kkf/sF:i.Wc߉޼jXtI>)/ ;25WCiMkٴXύkr,o;W"о}55ݵVt\MkWS-"""""""""""˭d̝Hj=^eLOB2l=_`x'poT瑓:a^T՜AZZ)t/̘HKj1ż8^Z5ƅd0wJ*HO:꽀;6bJnaDM$))nEzi ?*w38&NOlM3Y~zu-3#T5W3SeMfMheN')y3;6t;f&4/ 0`)Ճ_'<5WdT☒<0?,ˮBO.->mO(vT le%*aU.[_F\6`5&%f/b'<-O[%VWh,I3H55[OmMYYFW \sH\[`6FrK=IHY#IhpP[+۔Fl BK^殉&٘ xF0m:I=B*;’R*mF8mI$;cؚuz]/MdvnJ پc*IJ lmybVgK23;! l_/Rg?H#,gS1Ǘ6e&9 T?8E^4y ?{%$84)ә**"""""""""" :/s\n2""X6{!*%_[qG1%' y#n@9cLTwvF>qsIjynNJV'O ӉlY5DΈß*BZ17% 5WYKܴ)L M,OKeqh8'55Asȴ{0obyKZ T?'o(%/c^ɱҘߎϚUƹ3w)=pڔOD*f&=b68!BzdƚTFY)S^g#*JFPz^JF2TQH|Ϻ@վh^kǏ6f^OcqR]55kmt &յt d]¤kcf%gJ*ٹ%vqAJĨQ]ﻭcJ\3m' qDJNnyalӜފt󲒗m uFI>tm׋gg|c06GDaf%$$ 4;ԜL(֯3JeV^݋u|;]#6Óm[w}WN!uUSY:9Fى;½W_:F/f]T=9|n3Y;lڼV),OV³֎ˁJ?c9RFmxi쭘DϦ=`B Z3ёH۴cF7 8!R?H߱ kb,yyx%L!&gY$";JĔ[7n(^S5kSHJ[@bE3qLY0IlIϾM'-^\7qv!}G/a}nAgq,+ω&>k{9`^3,,""+V:A/m[ٜ66p$8,/$1%61i/wF"^b_k+3 Hk/;lR*WI[B+(禐pjlBĉĭznLjvqzF''FǒGLb4u#13#wWj(q1gG]~OAdR3:}&0t)+־ļJ~ƐСCoxtUs7OO/ΆٗD(F+ySy;ߊ18$'vt,""""""""""ryqgΜBDDGXkg7-""""""""""hׂZZ=pkw̙W_udz]>7i fiD0yxx+kGi+"""""""""""""rQ)"""""""""""""7CvtWjUUeb2Dd2uDDDDDDDDDDD8kN(kN(kN(kN(kN(lg ItɫRDDDDDDDDDDDDDt7#o ^<|Z]oDDDDDDDDDDDDDw:7#{N^تq!aWNDZvtr1g*^qEDDDDDDDDDD=9~ ½]Lk>v% #gcQtiy'^~[dW+tzk z{9y(_mvf98<|׀jj1u4ҐGۈ?z{3G۹Tqv>.RDDDDDDDDDD{×!H6nɤoݶS@g"&ṑ,ߞ #v̢F !i/O'd}H>;w ]Ӌ^C`l9 nm_q*fwKu. c'Wn#`a5ڠK{,!;k&FƿBԃuv5?sd%ϰzOGs gάd.Ic{YӇDOcNWda""""""""""16u3VS@n!ˤ_ܽ9cpǐ|G^+P;8?u4(&af`?l TxAg@oώx I bmuhiWpa0Ùҳ^A3j ݴq jzw@-Cdx[;4Cun~ؼ\)35W` b,]Naќ +N{[M> k+}xb NdVEPf ix.] ]ׇ ;!ї 5y\S4V~3pwsO5[$GGU+aѲ.ȼ'2z4{x#)SSk+0& 9'=×~osz=7aČ|aAܳomżtGp2)xΡ$KsmUηm:hqC {V\J\,q/Pa,jNʓ`9nF0TmL"50I>HA}gMd,YsbfH@L(Ѐ}יۮ=V>1r,7Կ-a5U4`8ѾL[º6<=2VS'L!c㍗NsT~p3jĄ!Ws*r0z_]uAw@n ʭx0kƶ+>K8}˸xׅ>3*\ʿ# ;-80aB܅z 7P^=Sra0;6!x> {#[H@Xb#{ >୬0?>Ȑ@}RV̾O:T0x1aq`.; ^CI@LQ\ÖCY1|Mgn>Wy8c=ؾO:r1x|L̀>Pf&s-ɤ/Z Vǒ= ik3'hSYvEhLvT>{2^a޼uY91a=K'8;u3B;7d<6k Jj`z۩,Yf9L>]MWdAÓ?kZ#eB[3Y1oȒKȬOLD聏o':U"Oi zg\>&Lt7\$S9zvph|r\1oW3_0 ?VJ?U0nW]Љu`*V#F:dN+6}/Rq_p :Ms661&&&#Gt7svb"ÑO- { ,g,v=~Nԯ`M2eˑXX^{Hc19[*>1#O '[*'ȡˎ-X{w3vD#kA,ޑ'A6L{C{} @ՙ BL{E)Pc-wr}NsVr+DfAy5äf/Lau"c+X2+0+&-a9#EAiyeRpϒ=@ >|gVg>eKʁ[d [ 9֭iψp(ز{Ow1/*=H!¾ R@N! @^G9|Y^w8C]Z?#@6|vP1ȡ}h¹'{Bԯ-/Np̦=_wP ۲O"ɓoxfc1XaN`i.e̯㕝Z6g\v3=g|t4A~ey;{Lv g ^=E'F`Mɨor3~H 1 id03 F ?Œ$tNvLhlt1ad#pV &n_f5a c 2/&1DC=ƀOt2+S|&M`9M]&_>`8îkקo.v~8îאFۇ0d@o<6}A7 IKlGX۽.#><%waq ŵ K]T1` #{S.N!bS;vfώ` w,}|(; ԭYV>>!sɺ`qڶ6UFZj &LX(?omQ,X=?!brw#!}-}p9 %ag~N1g0QۏFZˑh|_;|h_~z,{`??vqi{ P| Sn>xOFsBL#xx ɂSn}N9 6QVPw '#'Bhl߹kGs\IH 0y4bM&Q\p-ؗ8џ^Vθw :n}5:a'QZfVà 0&z{FtMH1{(" -$4;)h<5l&NmZY,X.]rB5;vVLΆ5M(&5:h5:-M&7բw(p!`盘NVv(ѴǗ$ȥRNd Mo`N4 ɶCSs杘O=(Z4 h3LZ0+u>#Ā=I)g}Pƥ2 ̀Uw֎(=uRqVƞ s3Dz*F'mj4ӲM3 H$k;p9 kCz "&[YHG^a,;[U mRFIs*ގW'\ tq-'Dq[0Ŗ;y e􉌡e'k.ǗVZZ OBPP^2r{1;awy._IHd<zI`y.MV<=Ąn !Tơ (8R%7[Pћo(rX6k MK9UQC'@:^8=,u4Xq'EDdӀYW!mR ,=)wz`0La |hUfi2S;"""zB"ٓFڶD^Ai~)265xR2.m#xek,l&̫7uL[0VʊL|3youMO[ۮ̙^r3h3Nĺ""""""""""Ү7~z'{brD #A&1*6*i :[a(hvq9 Fn;NutyXK>FY@ˆHbGcX(+/`!jGbGAFG{,P'|j'2v$& beY[r=?#bV#!}9P|t}Hݦ/?aF32քRFyA.'tSs@zx:vg8bz3bgGs`a©L=<9&hStؾ`iW0nE]f^ͼX?m>VuH Ϭ1x#TUq,ݙEuVY`4OŗS߹.dcSV4s,"9#g2̬xb*9s6f>ITu=ٳbckYf )b1J}NUߟɒ>O1,K—\ y%ٸp@*""""""""""'):ABpo7Kx[Йg~?_7q埳, ,<)Ns u-\7уV2h/&􆣟ыjא (뭙|]-Z/Ϝ pTugt'ݝ!$1A 'a8.츙WdֲJ-V[[֎|.^4#FYB[~ ;?$ݝIw$H ~Us>ӟEU^ > Cdx2YPP2~dp{Wbhh e+8|i~wc?pDDDDDDDDDDng#=$F65+|./<ͯ/9"~ב㿼1s^\?[[ ec˗S]}?=b?|| ~*:#۟p6É_?y~ v0ܗoٝN\0Agd/?;OA<6eq,˟vKct[|/ms/f_a]~/[x*?gO,w/<2#W9dM {$"׃!]xq!""""""""""r!wt?y~yy?-?5ct-i2W,>{SDDDDDDDDDDDDn]SB +8{ܿSe """""""""""""2[W~/2N٨DDDDDDDDDDDDDw];?0TV}&t1_p"""""""""""""2sS rBX_|q#"""""""""""""7F2fbλ_4u~u՛yȭKAeˡ| |9,r [Kgv|e19Wm\,G妟y5st}9^|acٕk*V_^JK wռs|m;.^]Φ5xp!x0njo: ߷wk'UNr>qjov׻ԏ_^*VQi``N{ndWngϑ;xyfHDDDDDDDDDDn* :v]͘8>՛Vl~?aWWVYr쯲>z)0_ƅoTV=N_KmGrC?~jx'VkwwczVby&fنfZLlPV#K w ǰ|T>QIuuw jjRa ~kh;i"y1t/}b ۩AS\I>n?v ]h֥u`8Bd`j!q$f˦w9esU5ԻS&].^YLqAm61^=g |m;uٕ[Zi_.oøJ7-8ꛒ |J<yfhLoN1֔iB%ه*4boJ'~?B[K\%",cۚb0 '}4ahۺU4O;`o}ζmT{h?^bi$ԜܷǶf=+<Ծˋ^735ct+UxlN{˄0Ӛmَ,ˉVu4԰6U㳗y]ybh8khV&C~1^DRjߞnQÕ_cK\/xy)Uu^g]:?Mv?Ms;͹  *)K A;N"eS C&dz\0'„qrRc`8xgUS6す>O)[gzaE2TW7V&./0KQQU7DDDDDDDDDD֠s^y1UfsR+`ͪ5ٴZ/9KIu5o#A=ʮgS6X,9A"34#9uw4bK_PZ5kZ^Iӻ;x< dhLfy!N8m[|Su&%z3}&dϋ41bV-pp2+f$`v;5\3,3e""""""""""rӳnfvB9',u@$N 801  NYv!yr14`@J{i>G5y탘kXs$@<q4ODzbo驢jjCJPmr Ty3~"mr ImA O>/tu h'`RL\M`RVB=Dt LѬJ6mek(φ~?A"jPoO54 ꛮ*.CVm|V݇?dbrX0`r \'~ U+obdC`c-XQIyRJ>v7 DҴjkl^[x\q @@!"!,8grMz.D}g^א *[FFߍiL(--w}*R]˾DLgH~!`=l]~j'x㾯XMs'|F;6=HDDDDDDDDDDn9 :| 'p5+`9P;/L P]aMZ'xQ ]O<\I &@2K+ek*)q4z]4{=[f6J)߰3]`ѱ7C 6Wtif}U%+Ymf0N>Rd;>/՛Ymiio?wRdOWylݦcuYRʆJfOqVUU'+@́+ :/]Li_WH4NʾoϪK(덦[hg=˞YCzאRY]ǀHڷ汵M|5Tj]'Z'V81LH$ONԋ&jv+YSM.9""""""""""rI,++?AF> udgsաctY|ռs ]EDDDDDDDDDDnP---Uy (I0c$"""""""""""X{""""""""""""""N|BDDDDDDDDDDdSDDDDDDDDDDDDDn8 :EDDDDDDDDDDDD䆣SDDDDDDDDDDDDDn8;:{x;wh_ _Fo6b b'w/vf9+Ӂ-/E1:9ݓJH$b&pJ?e3KA.K+ͱc%Lɩ#k07"eK*ksYp6ȑF:`1Rr{y  4l"3 Az&w |h+w/8;q V~\'.-2~gxkq.֡U wq!3;8ޑi f'lc %{Ksp6zD+H$0c#\9~ypA>/Ծno3v7kɖ 7 .W@C;{i='Ee vM #Y,$A?t30_DHtO\c'"D/ǾccmhDDDDDDDD䆗q+7.{Ǯhd.u]tuuH\/Toj& cāj%e|, +Xsm2({X{{L[kh/΂{ܤ; Caz &Rf g*.O Պ5e:kWaejv;b 9Z@7 ׋հ7'^@6iL;6"x{8վAnq.z0ͱ.Hõp!Y(]d;ik 1o1\6i&Io>--dܕɂLȹ}mSq-°ZѰLLb@dhAdu7)``&f2oNO.O`K7bjcIuLnbaHaٞΓcr9z.݁k 5D4i6 Dmi\qhf{djx0 )gLXՀ92}xfʴf?r4lKX2g2Ix!K04HM B<IGši7FfXȠ7ycÚIVVjb=q2)Z2j:8 vw*ZynNH2.l~Z \t\c>)p,oôH!}B<#tMףּ,$:Hw}GDDDDDDDDdN7u2aD%RAutI7 ?B:mΒx|{8u04r SI9bbcn&`sI^S"Á+Êcwd+‚茍obp܊ -&Jzfs%ܛMW7gq" = 04Bf]ld'Y@]nl]t#Tc:_QgeEgL%;;S>Bh>RqGM Dxd#6,EG,gw {)|94bD Y͙`[ `5Hua~rigaaǕaY&}%NZF*\O%`!yEg<q,P 5GGlrɛݍ+#[ɴe/J8:9ܵʈ' H3}{⊹Y?<g{Fottfq٢^:[H}!fYs (^`a/1{`2 ~o; Xx&F40Hu['sw %&.gXvk >_pt6˳g@,F,-lNp\VgQvS I7zgѶ(ǃ0zE餺P26߽KP2L>m. '(njfy'><ʗ%]a+ĩ?vPBNu1g- =9,ۀ`(BOS/\&?E,` %Bϔ)[S RRz7kogE98BLQz! }<2vTykJDDDDDDDD䜙`c(Vgf*4zZF9؝P|i4[% Y&uz ,oCi D-dT"!d'f(2HryϑxGɹ"X @`a%#I_gh_DbdwOS[ci t5%mc;nV)eee{ BGG|E?vOf;bJk;x IDATw恌|?>*QT?YLq?{-ߨw.`M#'?6*%+"""""""""2vzwtݽdqԉ/ΰd]!C ["&g;¤'a8v("1III."b(l8 Nuag8?7,~098̙oz'Ga?Ŗ 0;n!I<>GҲLII.*:{} `L|{8´M:bjaYL@R,Vl6녮 93Nˠ["""""""""2 %{ZOH2.^@84 ;K{ -pa_'0㕖lqi$0t6f SHbXt]6͂Śv *Ww7sc MZ>َywE[6}B_-U`_˵rݼBՂEPDDDDDDDDD3߯Utʭk*:YyxJwcĈ :MsGK0{ п:.[?PEХM,˗Kfe:d]ni9X4qN|qE pgbF,Xm,.}Ol.c[m:=fH-2pzcw)8߻pN;yeQgX7͍ ^`)eeyUT`f""""""""""""r{GgOK?f, KPoCf0=p0HhD<8 N@ND,G0a/%HO[ zv] `YY%;*xhy:h R0t\%Ol$whDDDDDDDDDDDn"7n_W9sO4Òϧx<~}<,iny 0pE)c,:Ty=yBYI. zZi;yDDDDDDDDDDDnAd<#kn< q?HG^yy>uW\)s,]F&@ k+r>Ȥ_.ndֱ4N)3y^ey1{!%el\/C5=gle9Ը^\OWK^eOp"""""""""""WW|W|epp:V-.cm9<r&?/ˬƌu#// ottϝe,326h_wTk͕䟷}_k6k={YµЉ9,ysK7ޏL-0t=4E.ȶW>),#~3a߽#/`?b=E6ArI,TyAg:Y@Ym\ʓCc#Qtt{“ΙqQh cSg-|yc\cOmfcE ni=lg޶Ѡ5~-&<͛(mFCS[yjrrsC{b[ AdSGK] )E|׊ȍoSn]ڻ;=ҡ~vPxK~/aN'zs!p]=7F㧍*XbRQVĉ)! K)[j֣4\Vp-癗_bkokyg.<2ϯmnqɛ/YWFn(~>6L> /=n|m4<< ޽ z^f S!;:`f:!6Ek4XAS>#UX^t=upet! '蜣m?m!lcMVpTT_UD݌FS<š>xl#־<UH;Wl\w't=rbwVp47trEcYp8 "&4q3i28I+!-Î98Pe80'8mK2q =؝ -%> Œn!"ŒBǑ J0Rm47txT0͂Ŗeuɳ+oXWs`(qӯ,t]WĢUl6o<䣔n`L>E'꨻ybq.amEV0 cO/ uc%mu4?qd-eE1NP];?ֆF:%ܿĸhsXdwZinJu7ѾR DI$`8=K))DbEiq3AOѾd>{z`|@0J|81ާ9,L9Cqfh_`X=#zspacdtc qvM*WtvYYda2+wV29q;OrHr70u{~`tc$qc*wr3iǝ p'7"}µeNn,xtQ 6MvyiemzA{Ўp8U>D$avZd%33ٳg `h?u^g;&>=3Hgv>;}޲v˴vsgWvO3mύ)x٥~?q񫈈>0X,8]]]H$UN9\wtv77[ue:-ki&nHJ's"|>̣4ޥeBە> nwF.]^ZdlpN8#Y hqflFyuu/UDDDDDDDDDDdnFˣ V빊D"AKK  fͧ螔QqĻ,;un@hR~/c7XAѺ El,:qם :_sGGuh}zXxg:^xIN.@.?$OY}<[AR_/PYvinh;k9˽Y[ĈÁ~n)79Jtwy1V"NӍE!hQF-?;zٯȸsceX)66 A$4DJJ 6_?40 EL ҳ#@Z}C؝)))LEfasXEXRp N|qhkp"r}Hhh>wu-??NiKK[QQ uAG,^ynӓg,Ow7O7sMҽ+YyoEܻќ :ёULNl6 5|9ݔ H;OѳCe8!H9=d1|-6h_iGz.2/N=+C<"-Ӽn3NzF+""""""?{wu[NV*"%R$MBY6xXݱtmN4$`o10HѾ1ѤN:$-Mb9-[mҢIZY$Uo?,qDz9"#B!؈7. й!ij4`A99_9wԷRw|At !B!B!sj!Y2EͪK񏇉XF[h V3P݀Ѫ#`+5bIDRZ$3C634DL17w-B熔%)W䲐SrXJ Zz5"B]\rJl&ޢEVa+5bIT23hQ7BgАMeWY^FJVRqS [n- {M;\W7*Ka>$ѹA|a21כ@djӄB!B!B!>!NOT5hHFb$~OX{3 3hH҅F@4$ *Ɨ1STp:;$}B!B!B![Ht '2MVl7V2,\LI QK6EQQft&Z707Gt.·(.7+b-LͱN#6&?#8ގѪT3n=ŕ5yr=bղ*Z&U% eTHq멱Ks;*uuʼnz߆B!B!B!ĺes A*!II2:4J6͑I&#iJ7'3qr dYs 4z5TNM2F(rPRmcv8V?KJE&RA24қfLt<}?3l6hD]kׁn';lжca<b_7a \!B!B!bVF?KG :tTZث&"l/#&ըjJjl$i,%F&?ýNcf0@]mzLEzr LEz,%F f]^ra*6R:13#u0/s ;{|HB!B!B!7s{tIdGg@gҢ7iQVfdSZkl7`^g>|´eߦط~?Zx|w͒T+B!B!B!↳Gg*&kޣ3lt"C6CLl=6caMS!LeA"ϐI)YR d%Ci,%4$i4: xuT*b$ZtF-dzL IDATr'_:#{t !B!B!Bϭ=:o`9?&K`+5K@.Љ5PTT*NF u'XKLof0Y7`ϟ=\39.dZ!B!B!BqYأsz3C)Wx(E<'3ST%uFfeDB!B!B!,$:+o*a6T*ȤB(./;MrY݀H,eU_P7J`"@ `:1@-dSY2,Zt"TAkJ B!B!B!↳95M&KѪM|R(DRFfLH6Hv1NLR ǣq4 P cLz޶XD⚰nK@Λg[ؼl([øp:.oO?zZkNh)+B!B!oJ0i[Ib&Й5*z5E.3MfCXJ:*o*T' LષDL~2i*9doHI5XW_U?ŃkߜdoRö'R=Ld oh3o^ыnrPslPvG94oS1u?cck[oVoR\L5Tך0!2#{/wi)--/ͩ~]| i_T:&4iZkpw7>͑D\vu;$q+ !B!ByL3<_R(YBqxl!g cdq`2\t!Lg ^ߛKH`oMۅì7,~?e}٫|EVԳS@"7-4N;!i4RTUD~*`rѰ7M.t&g,\p)qz VۿL5ԩq qԕ5?*F%mS}3v5Na *- ND9n^F&iѹU B!B!B [أSoʗt_{t-dY樸^Ffz0L򘸾yhI+t*]|^ʖ&1 :t=‡ʊ!EE'rT؈Nb-֣)bSX,Wߚ{WNs7#g3nZ?:PyG9ńy?AI|7s=U46z``y!ZV d"˵[SlMeQ !B!Bq':B\b$HdeR xct\87՗:8oH,Z NpuEiorBZg |$ ,&VwV,,b{3 SYF 1YzRKO6Xٴ-MVFH̆sԲgG-ͻ)!g{7,..qZD ̩1ޏ,km ;(Crdӯ⽬-ٶ")0coept~+7q7D~sN|-·x_[^=Gݥ8K(!Foz˄X]gneNҵՖbﯥր.gޏ]\l_[ޥ#i">^cfQ!ݰ&(*<_m\kˠu2ύEf*fEDp*>潛2D3W-i޳{w[|qWS8i-MQMx8IBW}hEתNYO-"4vmOw?|%0m9UF7jLZ!B!BkqSgҒ櫗%:5J nD8Ei]JFpJ:5*Lث&"l/#0ǽՎRb Mdp|i7BgԢdtF-X%֪1u wykw0NE1:1γ{cB:HE./llYo(Vkv O6s6 킉 ڱh^} aH?B=.yMlƙ85™rj C:~hlSYtN6mDZ#sţb،(8PЕSEfP]U.wƘ*uyW;_έ(^ig#;qp:(ҝ+yf TP=+5#L-ZF~^/cd4|ߋCdb e\ zvlO1th | 1\~j䄗7wSu_ͣzڬu5D[}S#*V[)/^ZLӀAW6ZkEGZH""8@tq>jX *LsUom\/L͐zZ7gx}Ǔk]4mDZW&6oWbیoaZj!K,G0N$ lgFkMpx4No[hJ1CKHeS;*af` ۛQڽNGE>ul .>nw100IPoa6 =t~Gdܽc/-\ Zt+~4$gSƣ3 ;ɉ]3 N*MV4/:VHv-kc7Ζ=e-Tۚae(m1͟f SY3Y0qRF[գ |p Z\-Fz竣kk+z}|}47W6 2ZR09 a}WICg|iK^ȑN(`0¹굚{ga7T/Œgx<]A. o|hUԼ%Ms齒A\`4`c|tg5 XfFWvyI9`B!B!k8i*6BF,:Fޔ#Ja0HSH'2xkwS`ёS `Fq7ؙ eHSD';+ XJKz\z;TT,B.Ҋn`9VrMSQZ.5Ph*hea9""(VYƇ3: 0cAf|R;r?K(ߵq}uUТO6d#LMdT))owϬ f+o0 E8ceE3!FzӋکi8QY%=8 Sejs&arɲ ܚ uZj990&KD afQOEI`U]H2}zҩ(Fm1u\,@Z2(ͫ'ZЖ{{Vi1j,X p$K z' b"M<["LZnzb Q3ӌ $glj>&Q٨'=83"粨 BiGsKk"ygW*ʈME{BxOM㹴8la,kKrJxPS/Z?,gH.?Em5`$7t`$j ߻Œf`VeaZ("߈c|0ޣB!B!bY贖 s51k&@gRpTZQkUhtBI%ZEt.AI VUHjlzJkm+\Hjn_6)933˖ NFWKp2xs '3D 7M -5f2@uNܷ/߷$?g .J4w`ݷRVzb[YHpj9 Ň#>>|e*/)zoc܁O9˹%x9Jpo*Yf67O7(Is=NܖiƝNSݡ딪[5uSR蠬FY&v)E ]dsxIW%P}':XT W3!]VؼAd*_TξeK`6ԕB!B!ZOt„XJ#̺f݀Z:{p.g6Mg:oj IHAж]1%j-+E2lr G$D"2jбhB X!Tu3*= 8ăUPU&$g 5W\*,*F"_;={ 4fFC7 ,jO-5-FnƈjW.a)#x]t<]E ]f}sv9$40YY 8-X5,ڗքݩH:_D$(do؄P4W7!2-sbV#}U0Y!=%B!B! bqmff0_0dڠAQUQTjbjh jK5LQl:8K JqFXF6p14"=:r3z%PVE`2ɦGkРR *$AMņBT\)tnx&[*&HeNFp1c|( -&RUI11_:~fE+i4v3ZN&9vp8Ns-l UӌM8j)Mdx^l{Yf|`pS٘brotT?F~nb.-J +]TH9 {XC.HGļ~ޛ[44WO9QCRHE14 ?i_AZDyo&2N*'F##q|8}l4X׋"\9ƮES7кŎ+t^jes鍲yƦ tZlߌpzqaP̶ǹtlg~Ng˙5ˆMRXe. $|UT益%^D {ZwhnB$z^^VjSK$yQT$<׿GEg}~m۶qIxG_<䓸\.FFF hZ/~qs{nB-]o|ATVV˾bΛ} a:y zhmN6_bbVYV3qQ">2ԃYdh&#cJ1ڋG945ć nK !B!Bqh4iZZZ`٘Jh4D"0W_2$Ld2RBn077G:&x<͝[̙3X,0gϞ%c0H&F昜dnnX,G}D8&L255H$B4]яl& 155T $lχ(s, ~)F#oƊsj5Hg^\VVVL&.vqUCOɮN>_62ǝ]k |L^NK6tQ{GLedŒYa@}{B!B!BJKKy*O 1ZC'/߆ŠGx _lsflj3d2ɦMxx'LLLOvۿ[l6mmm:tp,B;[PUU Kγl?~|qI" t:1 p8477WTTk-;\Ҝx;5g-xzλv VlV0eq&™o3)h q(c+m /^f2B!B!B!ĵ^8s8j Jh5j,*'Hg򬩿>Zӧq\tuua4rb0 m'''D"\.> &|ME'N,rˏ-~>66lfvvR E"U6ks~ӟ*:S$ЯR̴bӆ }DNo_QlDiF~+ !B!B!͛yw ղnZ輥 _$F^SsK<@!YVV.׾"ѹxw}7o?a IDATcccB%-?yMM DZd´ZKY`6Wgkk뒔\Pn.` L F]S]:ijJZN{kE¾>'O!B!B!Br'Όj %љ8Ź\k ^ߊ׮%ռXdc&Sh;;hooNj90vp|3drrp lF::֑B!B!B!\kmm6TMS_ -U|m&qK}MePoNcc#?ӴsNn7{eݻivɽ޻kFSHt.rˏ-~^SSbraX+8vW=`yB}.ܖOrY4:5[XE.7h{5]D':fpO(V wvq!iλh5Z;L:B!B!B!9˓˝83NJ~}D炋%:DJ/v1F ൭]D'I>=.~N>\FNz7 aҷRUB!B!B!_[i*aEuI5b Me3\Ϯ ˷_0YIt.ޗsq 炅Dڊ%`poh{5]D9/RJ"&IzN:F/n |'jg[Ojny`՚oy_RN7mѷyFSD4B!B!B!n<|}ֆjsv&/߆Šg[wZ|8g|t~:voN2dӦM <z&&&(++vc[[k_ڊ}/A>[o Ɗ BВ[D"Zd2QWWǓ/.Nt.~ܼb\Pjw yLEZZ} x(I.g5[Bgj&tNlu+i׷Za؛˛ZٳV* x0׃}3vwnm^KsM|l&6bm}<K=w45S ?} G.άi^~CG;!B!B!BEN':u tv٣sぁ},KItN|g͟1{U m<?9K[DN:w5nxY0_Z~u?rv)C92l˖}|s[s %D۽đe V|<Աxɱ)chJ Po'|w;&߸>`A^QƗ_ji sn9zon(w~S~Sq}cb;:v/f0hлG$Oyox{8r)[]^%U/-;3:64W~C/<1=8=wLq/H-љs<'ۙܓo=:B!B!B|6|^=/T1NE1:1E2 g{"J x^FfWGD8En~eUAMiR}-Ȗ{_}\Byē=HīǿAث"59 wr[T)R<}R9xo|+ڋsZƺr55LJc ~qM7FH N,K|uq렿PsSƽVc ~zz84^?X){z.wftTW;gΧǘW-,)/pB!B!BDX ZC>d%,fLJD J}u/g>jYؓSlU5փNvsC3|Opp&pj׼i02fKڅۚ,+wSC X;;ε׵拕NKQan[r-;ı1h>c+w,I1z4?0M,箝ǟ?x2ݝDc[ sqǾ6>:Wpq!B!B!Ⳬr[)E.3zcRus)zt"H{Q@.CQzUVjR#eq5ةn_ӽΦ.nP%pBjZNkRb$^Z+*H[Wc)燻Lud$02v#LMqxy ;>,ppPcb`paRuuNL 9?S}]t1ws;uԣˊt)h}<(-p?N~Bjq?y}/<3kikoO/q9<"LBss;7{it/I3n=XJ;Cw'+%n`ws 8 ̦C}t|~~w эBB!B!B!6Dg`2ZHtwϠ֪Q2 P* P|n/DJZb"ȤrY*hѩɭuv V*l@ᮅ"'@.lu0\qVM4059Ƿ>¿Ů/4ؓÞ }Gf񢚫w ׉&Zkl7g,q<=={l}\_8_DR(2 ecv"9V:]wNk  S8rltHE ;kV"ٲ-ɦn("%އpf8c!)QJ(`py~=<~M {OW^ZĀZ#ύ4?M|vqٹ_/pim!'y?~kA\7lD?maυ \^1m_o=Ww?{?eȹ&^3Sym3̍?})B!B!BMNB#*fu;5<;M]Uh=PKRY|tO9D\|dmבNouo{T_&xZ_z CT,49l|m_5K//.{F Y;?LƁ'vTf̓&xr;g/Ӫ7;X}S!B!B!TmM.'O`Z@zD1S2[dSy\MYq[W G\RUե0~!H2b3Isi,N3&@&#̗ؼf=H?AEp_T ~|8ݻy4M>;:Vtfeh>NP Y2:9 ИH.'8󋈙g\trV0OM۩A>zgB!B!B!)-nEk}fzN3LG!uf7j^CסU4Mc>$?عj\b+Sf'&(kpr8-,:}q \G!3=._n8SmN'M]0ʂG[W5fN56.;w9!hE'_H^?H@2ײ7:?~ B!B!B! kt5:Bb(Ut&#4 Sut"' Joj یpU٘$8KPWczgRm:2*(SF}iOq1`5ѶmwfCئ.dLʇ m_AoWOд][GO: %!B!B!BGbEgU`'⥊LX]u0"ɬXo3Γf0zd9eReSφFo6\7tB? ֶf,1{Qmc _O9 :~9~r!B!B!BύŊّD-REg*d5e5xz3N+.5 R,j^CS5ݯ!)u:{?:VZ3&S!B!B! h^fEIw}5pX] M]U39\ZScHO[L)Y L bhVl*O>˨dyM-`0$rX\fڡ9 &`d8C&H>BLHf%PX, ׮]֌-g;Y(1Z !B!B!BGbEg˯Ք\Pg\ аNG.GT}s'pY|~, 7yעvQ߈bl?|o'[l>L!B!B!B< 蜹Fą ,Z^+ 5%5qbYR,:nzl؃qWxEgXY1o)&TuE;C.u~ gxܠ)B!B!B!J?[D|Vc>Q Pza(-$#\V*[F$9Lx6Slyt}ix6,\ Kx)B!B!B!fs jHey-< ` ׂn$zN(m ᬲ4 ki}ّ(` [awl:_wR,&d$V(n| I*e IDATxo48\F-iؿxߖRmFT[`~&04b3IRz}k"kt>_QQUPl^ ['8PஶSCV Wm GPl&;q,lRs58%8BCE{E}5sYX&vV0yqF'z=9 b(IECI%KArLD)7nB!B!B!iyEg.sEgx*N>.Y+ɠi}RUuJF]'Q/{b-16zB!B!B!gVh"x5BA{B"AOT4013+V&lRr+s̍HF2wc8KW71*[=X]D .7R>=.NE3+Y Q#j7?nzY7w/ןoGn:B·U4:H}}=/xWO~RSSSAN!B!B!BȉjhNѠ'St|:ה9x.kћ>W9K'Χz ߝwjmow{^ŗ:|!꽟No8ZJ&{k 8U/=UN1 B!B!BqŊl*O6]Ҍ$WTfbYLfH 4@,ڨau$AXV~]G0m>㳾`:u@fFabZ3#_5;0 qh xhu4ﱑ<'pf90LԾuC!B!B!Cd^frf#m&\bGb` M{ȥ58hv7d=G6Us:QHI.doLxbk3d#smhP qMv/|G1TDƲ B!B!BbEg2)Ut_bT՛iŪNb$ȓ˨$yY|XYF.pa8<}* Zw7`rt!'aoÆ&\u&rSqRR@I' ):'B5buJazP7rޯB!B!B|1(Uil)695Tm^zhBoaqP OஶPz/m5,;~&Ki~~N:pA{Q[zE8fWW}uF#`tR,Fń Y2c^%?,MOаɁcDd MxB}U9;_j܈3u|K}Y: Fq!"xaѻ\Ѐf[ߥQfw'4:O \xڇˢ avם;M5zr#/^}>G|4 B+M>XCuu;DkevqDm> x*g>M.=U5"$|7lBc񾑥+>ֶ̀PiabϡGYpx7_;|>~vlO+J| Й$9T,~5+i ޝ4~}Wz TwISmsؚS׳wJj`􍕴~{?'ގ.yI^cJg3{vj>T@2ƓJg+ HTqдJҹZlIjMFwx|O8:~:u}NY3G> TUıA'n&RAߞj|0 D#g򤻟ތr_ ^= CV|;|4^uKG٨=PA *p L}4DQZ<ʶ xxk3Yv>®@O>]?m?d#~iKn襺$AB!B!b],$42la1j0l3M SmrjRY\U6 ZvUTl^ l2G2A0~gS2c[vmFXΆ'vQkQT`d]Ol˝!\:fKK?X,x;r: ޒCcD 2G3-/O<$dg5JZ:Xɜ_Jا(Zt޸{80q:.^ךk=C^(>oMPBe@z''8s<\_`k;پÃi_ޭ g~Z҉B!B!BbED-4(763!5PE{-Y lpY SM3q5%cBtJ~VЧDq+Y6}'YUQF4lu3̰L7Ŗ/ޠ+mO ̓9S!B!Bq>4Ϡl+>vNoؿV& ~#~7M(B]ul6>{`A'q?HKڇ̞$ pTqEaB&WaW8Ҕ7XVkprX"JʮVJ*F8u4x4.-uw[QИ$dcX= w 92q0yCo[?Er)F SKۭuW@^g~+-8<ݾ^/\YB ]m젵񑍞y[!B!B9p54VGnf= 4550>>$^p8 j% pb6dҸ"~}u]1+9P{1JIeR93>2"6؛ Q- Wct 1Bc1"qHT\z njC5G& ۺ?0i}OIuzkB}yB^hqř)YQ^jm+ucU h19V޸I,U{70O2[D_8vtXs~\꯿~5— V$}|i {zTB!B!BEQO?a"9Cnj??=|;[yj[#Om7Dkgjj'Oi&zzz>cz)zzzwn*V`+:6|5iCzN3O晆 }ùՃ3jۛ pHf|bT;U?G ޝu482\$q4hdϳ&CyH,M{ɺ&S+F79~8&fLp}]%;$_e]$׺hyfټy3ϟgyfΞ=kqAlhy&5pvދ?yyE S}v(ՙT. G򌁚2˩a~\`Wk"3vaњ9fթ z}ۑ>[c#Z$7++\lm_3F`G6p]9o^{=ljtT(ԱiuӍlSɶF'h΢2mh*vh$Gg}+ڹ;32=D 4ֱͩj{f# ;-]'[Uj8*9MA)}W~6[?μ`9͇ "7©@9OY07™tid ?ǧWS {h;\ 9oxoczZs}{'r)D|}X,҉)B!B!Xz*LK3TV wv>s|'6W޴tuu}rۖweu8 ~Ѻ.jYB= - m^]}gf_Jg9 s`N!Rξﶠ;GknB!B!B!nm߾}>}moi@]͵m2ߗe>5_n޽ϣiw_sر'x IDAT {н$C)p2ӳ+B!B!B9^AO]{,>hWCFzs87;_C?8K`2v'OOrUBN!B!B!B0\ 1>S5фgj|Ѓ_ff v6P} ãt;Mb&B!B!sg}s-O֑M尺 .3WOMӰnb5PsVcf^(6d幍, 輷T2ddf$⡔fْB!B!B!Ľ4[ 6$] rFAPDT4jDj$#q6VB*y#}:ݼonOG uu4YBkB!B!B!jw[ e3'4Ab.Ti6b4eӮJԜk`ғMHF2r>bC頦r0;5¨5aEW3sB!B!B!l*O6] OHŲ  hZX0% :m?Іϼ^NvrG9.j1 B!B!B!d^fh]p̈́j$8 ݈b73vnU8,z F=z|V|Dsh|EC5t-1 #!0h8p4ΦTq528Iwg^'gPx0/?`Yx^!nNp!B!B!x@+:LsBRT M+Vp#D\Fe~&ͣjR]M8qb1CÇ=!]tQS]AM*<tuu,gTG_+Lew[oaTorV0O.Xgtguwi>XGcr3}~I{8CDŽZl!B!B!kX骴务f}GA ZDV/եtsI*[=x(6# L 8-r&zg)ovSV ag)j4.3 r|4dl3RS㤦 ŐLc]t4-TqF*Gс~v :q]T$<`Z:`TkhMǩk.vӣXmr`sJ&ŀJA+uQA:,fXFGD^V\d1⨰bu&D&5:9ѬǴ4A3[Jm^K[(mLQ[b.^ p61pYA{ }A>3k!bKCV>֛Y1Դ尿2L熎|F#`tR,Fń Y2c^%dV͑ZSa>?á*$ό3z]Z4ʴV6Ӂ͘'|isΑ(]=n>+6)B&t"tI;W,PF>/nFr4ȥ7$ڮH_UƎgkjbA#9b({;lNAO| |i.{};ŗ$8ߦle^Nߟ(B!B!-VtΎ̓[NJs~v쾎#H _ջ*839>8je{(0JK5;[Ii\#=afF4Lu>񤻟ތHHϥקyB sdN_UWqE=66Oh;@KKKA]yFO\#ҼϔjkgOq0x&qѰ߮^4Ԋ>4MIrFN B!B!BYKl&Ij>רFt:6?Qb7q8M]U*DAP@*Fz(vecRPZCj>Kԟ P6M{ ޼\*RZ4PP5|\lUQ✼8!QvOAXKfA޷?7ll:‡IjIqiR :*Q\4yMBJ|r3#G%1?.huyә1G3%gDԝﬦZ\3U`jKe|Ӈ0G@]K;tdwBF)O{ο5A]K 5lXF!=;)RcK?X`H;mScP8A /|NL k<{MN(>>x;V }k !B!B!bEgp8ZZsJtjKJ`(b7;5śx%Q$Ӷɖ"Vi8I[H3)` >nt3@n4{ϞHg;u&$v,۲%[ Ex[/R%Y7ۺY\"e?=ǭ,V_C*ZQ1,n& T*՟ : ZB6JRY,H3r.h[LH<8`ӛvS )&ǙIӌ'aNk4pMޅɠ*ً3u}vr9 xX,d_9Up8 ʑKQs9]4CoOVh;v)[uky(^L P(9 *@RL&cD\,WxˣxZ/%(t,vLahoB!B!B*:}"CqRf=ĵwh=\b71?b7lmVqRY.U'd|Gǂ-:nb F.Ud:ƒq;=Dm\Hf6O>{{u!āpqIbuhsQȔP&t:znVԙWghhwMX{%wf쒭-];u?LQafffji>!8CR`|C Q1щR3DI )S٩sJ9@i] , /X7~q*0mkˆԉ50C|iK Wh,{u kXR ˖E-Pa׌ 3;F*)5HJB!B!BHۣnENWWW4YUtP97G2s=hnR&6Z2-5dym mVK{,>VV )Tou+?rУԴJ{;M5@QEs9ANC<wroڟ<8~5ňLiӻ+V"Ygx8hf`hDYCg+Olt>y P>**kz5J+5>Z$B!B!B'BEgx0mvbոşg[dfV֎Z`h:n, DaY'=3\0#w!B!B!&uuue,BEQp\{??rywt̷z+Wrܹs˶R)7| jkkq\;|r(fcf~[&OjP, /R)b=t4y`H:U" 45ؓr8ѐ3gzl/},Db+!ň<ʁF)dt`eof„ 7p@xƶ2C?db$}^}8*&{vV$&.ň8xPn^Q~z/K)J̑[UHp0ᄙ^7:ڲfOa'[硽Jpc ǝ/j8" IC2Ƀwb3B!B!Bn޼IPX,QUA6%bۉD"[:{|LNNb0Vm3Ο?OKK LNN,xǫZV(cs{mqZ`.>.{1?l퐏=3-ϟgǸˁQ">Yx& cBаd@pʠSq[*0}?/r-PRsm\w;|-*)k'4@># 1ԿF5irKgl\OiP$N+ؙa\h9֊'hPW2ˌa3j$\:3r3wyFxDx8-B!B!BtcB!BЦ]|y["@xg>h4Sq?9釙AƗW֑ 1pubtޞ}xrߞLիL<Pgx"~v-B!B!B!;ѣR)*ͯxܹX)6涢urnœBKΚG8r5: QHЉA1; #r !B!B!O#syfVB243i(v/~9ե2 b0)k Qd@嘺1ۧ&I+VTq)\v{B!B!B!3:HOg%,̎I^k@+V0 h لͭ7ъ.\R*t !B!B!Bǎȭthrr-Vnz \:\IkʙS> t !B!B!BBEg1Qk+7X|cAB!B!B!buk--ھ֞::q5FÉj3ɩ ssV4[ AB!B!B!x,TtfŊ(eB6Q 7_Y@.]٤TwC*Tq/xҵB!B!B!ⱳУ`RΒKQl&|OQftzẗ́Q#|sn0a17蘛#*bs)"¾c8}6kS!B!B!Bhlz+B<wxZ] dawNTܓ,]+_=,LQ|u B!B!B!,Y 8Ql&5 5u&gY`nZSE*!:w`1*, 0usg=_i੯5K_Gb[;y5W!<I;6bu$2&~>IbxYE_hw6-!En#[?̢o7[%WC߿ |EŒv{2b[ :w:Ԡ%>=O/pcqg~&L kpatr#!'h!C,e:Js-'fYf?B6|b g9VVf 5\ڜѨqZv7S7fmט6kՊk3rb5 :wu Nq30JAкN{9[.N74`l~4;PYprc(2k l7@dKL׋)Oj4Cf0yJT87*e46IzNeW HK](g(dJb!B!B!E bΉ(zZR#͑KUhf'TB:XГKV_ P*IGssmFYMzu>b$a6ńz[s/q`h54x!sQ1 X!6CA1`hdĆVUڜؼ:-TᔗFñ-87NjAf6a@~&E/bX3|՛$XpҬ#uiFĔsnZ uX;Z~ދNAH Af~Wx{p,<^<هsN3RI676}u|F TNvok=9υ,[ Tlʳ]JI;b|׷,z>6BH.f(Û6Z |qVq* Hu{ t>01\?rX?`{#E\7\ixc~9F~~c~iBk<?=s?FժJ&K,kR%jR`ZӁ!oVo<6H4Z\R=u6~w/Џiu˃;5RcR ]3*3''A &Iq+{hИ5\=Is.1ԅB!B![KBÛ A~_ӟ?E>w1~sfffp\{[\vH$in/~__100@<DێS+HЪs7$¸Tb1c q&F(4ɍW~sߙwec^ӾM%/5Ш$^|Š/ 6{oj/QB>S Z^P xZ/%(t^;$iÐ uJtIe%š6쐘bb1MLB%im9Hj > \r;>N/U[!vq_NĤU"1~֊4@я`wߥ5gRfxyvsdҥA]ʚWfB!B!b\vQZZZ}v"֛^xNG.#o~ܹCSSL`0x|L<G4ܹC4<$ms?"D_j#ebz[;~އN bRm׌ 3;%ŰSy0Mce[=80PL~r"M6 FJl \#7I->!QL@U!;)nII):nϾ +>y9a|[<W񑲾k]Yݵr:|*]4MX^Á#!:"#+h"HUW!B!B!Ďs|t)S|Wz킾wP-KG==$f,!co!>L?8;sYRA4r]S1[>ʧclҼk9׭+QFJ"jpYPnU,gV5jԀbY~ M,ܴL@܊%d׺\$&?PΏءwIBBf];b0au@+\5Vz//mp) %}ZNOvadu]XB!B!xBy˿S5S̕ VOôG}!ā8VPc9?AZ F=eb370 S7fQkAq(56ȧa|OLݘK2^è(Ɇg t ڪmFEOz:GttKEKg9h)N4$`\S)[J1&F5J^%ԟ&6&(~ϏmrOYOtܾr4+.(U1y3uXW>i0(.(ﻻŻ) {lw=4USe`efV/kT`ŶkWt+]X抯:G_ƾWMԯqHIkY4<+v>l7~to?$%BaiKf^K4""`sB!B!B<,3(kYr"TjT(3u+9ƹ1&'VЊ!\H6Q@+V˸JyxHev\:[w⸹*P*9$tyؖ*RkX]M HgSZ`|V Mv|^T},DbSu>=N3ZsN̄ 6{ܵd gh^n/=R;h=FQ+`0s ^8s?p2q=ENw/seC}u(>G_&&=5|'٦%~WH] ovC5: s i“q&0;  z9Lu@FLԪdF*vP}Dh'Nh0ZeO:d/_;kb}qj@`Ņ8KfGhd)VX$89 [i9xjN=f/  xp5b妫[>ɾc5\93BrjR+df_Fv@U χ&6Of&Cyf+[Z5 1puuvˁQ">Yx& cC pOÊjluЩh @\ d@ib/[V }='|tRB%=|sǹY3z7(#y Fgi|y:,3M=}B!B!BMXlQ)UC͚:,\EOZ^+H$ n< ,Vv;ֈgʙ]>C t ! j0fLh1U )B!B!B<*:SŊ@ t`T 2mDJ!VavBE zrjBN|I)x ݧ 4Xs$>'R,B!B!#PSCNKz&ZcTЪ4<~ɩ QG6Q@̈́ka~Mki:AQ&t:ZTFzz>"$B!B!B!Ι;)J4*2QHX?7TXlnJȹE@EOd~Ȝc?y*w{6;GO0Ǐy]tي_͠=awBݞB!B!Blw+:o%@WMm&r"H:l3-|rYLIwk:-Bx8v89m`/:Gtv*X,PRsB\?7KbxYE_hw6->~/I5LAT6'߷55(dOs ܘ8r=kZt:T%΀Lx$FBN#nM%6I㱧of#^jg5~/whǙk|_⾷Qa2+Gf#IQ͇*;*ڼxHq e ;4!B!B!,FscxZ՛pS+y,E|nJb/O5l3RjRqx}X7H;{;yᴗ3| Q^5r,޽|<(\&&J!4;PYJ2c+|57@dœ$Abl̦HR?؏h<^f_ŀBYBG̎3)n7~?B׃N%6B!B!BZl%~&3`C7ƌel3Q.4LtZa9llu}B 9tbB8\?~m|EşqOGtj~n2T-54x!sQ1 X!6CA1`h[ W3%b_2ANyiN1gRD#0d\ps%6 /SQsć<7CjqkG {)0?XCA^>\|\G~~^__ҊZ7{Fc@^﫥_ Clw;?ifߟ0G?깫|r&OE#98(y?7#5.x :eϮ1y}<هsN3RI67V q̃mDP1? #ooWVk0?gyS?@p&^a onnӎͨX6z= I&6ڻRvxqLN;hɹ3 .lbB!B!BZj,\Bl@\j?"tzxf/lquhjù]VM0j0SCxnj=HsYeh0î_K+=|Wuj>} 7gF zf/8AKDž)D2dm60Β53V;, k ^n+!_(cjD-79~lƮFZ006Yui'CNZ{Oޞ"pCAbxziW14t9- l쒐?L<&}~>+rgt .ds(i # <_@ =Pv 9~`6z zz3 75 Fkb?!}v7z (/V^uDbbHs[=$?¤ n<}t:-}IH=q.i+_j+O6,t@'kq+Re%q)A-z`wiw߯Fӊ &M_8pO[Gnef&K,( SL k4x Šk, D˭x}?B><ѯrGtp0殲0?5'UcGBk|>p}D.WZV^]oYS)AqHRb7Mb?\}BZT&H1Q*IMgT-fo "t9NOS=)EXzhU Y`z9<4SDjbL49H}%f?mi~=DhȪY^nu}q9~nI`@ A`z06`C"a[Dat/^K%FLah#$Ao#4p.z ѡ7oR'+4XQ~\;\p[*.g%4nS=QUKJ,CCTJ{n%WT-FLFũ:+TN# IDAT/_5Y :)MzSdK9Z|6?<@e瞭<=5asB!B! Pѹf,Z5nf}92ymfնY peS Nٖt YJMQ88~X)}cN0[ڋ+P|RNwmtk zŀqRFzT65j ed`гy{T~>C s?NCiׁ %ec-?9+Pv r5cLѺ%Ű ǘ{v 7`nEˆԉ5v0:yΰ ec%`Z zZ5ӌtQĉ w91%쫹* k-0$ݰ2*ܤ.̒5J,Ih`i5F :MOՒWS߇B!B!BP)\tM274e9{ƚX11ŝ0C!^|YMѻ^9·^YxPYo[ H!4ň1ޥI]J;݃WZTwèkdBMk*MDFZܨ,E+>Frhxwl^m(y$̒@dIk?Ã2Gv=4e=N{Y !B!B!~UZlR0>>/K~ӟr[:w]\.Xryfcff?zk׮D4 Mss3/?30bbi477sN?s333\.x< z=Ӹnt:gϞ孷/@xxwtK꫄Beǻۿ壏>bf>`00[#An1x7R3 8`(Kg[ wCm&BQOi'A9ݞ%%zrF+s4IZ^gW(KjQTIP3ˁ{TX2 UV7G?IZqL].Lj<9@D"׎vuE-ljmmޕ՜s~4=&K`pCAUɡB3MkJ]VئWܔˁaB!B!qw-~Ҳ?nD(njLNNxǫQZV(o6Ν;455dܼyɄ^Ν;/n-/^Dӑ8Nfffx<AN'L|>x쮮8-;?tUEmJtNa(DuOqg!iG8h)N4$ `4`0`6_lLŷ?'](yPM8eR^}b?HώmN~IFsh?@{i?f!ڗ_&L|/iʯxh󥡌2Z*|-uY~K &c&pXKa63%d'+tSacP 5*=wкu$EBra쟝1iaeׁ|Ūvzd'OZa2>|lxgbV:B!B!B<>BU+}}}z˗/P(xjpr,z-=v/64~@__k:1ǟ) P[43-ϟg9 O;8Ԫ@6MbC*; >P/Tw>z@}8j9Z1MX46+0a .N̄ ?7Gbر}08$Wynkfb8GbpQ(1׃?wC9frvg1 \?Sm<'Skl}8{RKreE&%2$Z!:V,'RbMPI1Ѓ=,@9E 8H Sqm5#R%˔DH%^`IiIQ//&wfg311~ X]6*̈́0[0ь088 U`hq0hŤ.` 8XWPJJ^;m>>Uju:`W#; uiaj8Nkgoכ\S$\w~=]ehՏ?AVoمy+W?U鬣"B{jňe@hhPKqH_c1˱4V/Pp=XB!B!⹳ 2p lu36|t?iA&}CiY"< S2%uEa͚݊%4Jp Gb{X{Bla4B6&|/N-*vj<_xZ^j 1+4uR +Y 4i=E + e7b_ ȑ'u]nVm;#nɢ,T_|.t>ՙ+fq7UCc=GmڛsĆ&u'E[_۾f7{PR̙^jc~6SSpfNV|;;npV(VR-B!B!ےa,eFj爇Ҙ%Lܞj.fgRﱡ7gU2,&R ttdDra(>yǸp+۠kO^/6vn>d:+G,D/K)\Nj!ьB!B!BrwÔXIf ܋*bʫ]>Fl[!Suک6:Z=OB!B!B8i2Tt[TTB pP9fILvETt2c>uK_9W>S0i!2xf3xV!B!B!*:-sP|6I\/80;D&XZ^V9:wt{>wHd~ +9zMN0{-Bl:l 0QYRRH)B!B!B!vNg])Q,&*&{ a-2sq45mSڊm%PA^j^%as,l%Lܞa])%ˠX(n ;TiDi/cVw$c.33h$$i&C[v*X39PRK9TunՑS\:+m}?>`F~x!= OQ o7짃!/KkB!B!x-TtV6UtL9ʈϤUcE:A>⬳WLpT[ 4g YD`( K_bvNWCmS<ڦFN|WCO⣌4\V4UZBugŋ1xX AuS+᳟݁ӣ! ѧtC 86ĝ<\s|jy*gor;7j *ܺYov^(ssuא)l >RFu5|i/B!B!bY { >zPyDD3D.b{strY}H+eii'g2iuع7w ت d}1RmEI%=z-Y=])^vƼ^V?Ð᯶eVL]e( LP<ŠrYSL%˂6'_C K\Vjo5$鍢8ЅAirG <>_!B!B|,Tt׆D=8N򵦕 UPJ c!~$_K?i]`v`l#۞2S3XQy5Qv7p,}&I3Ci.HCT:!~+^1 8MZѡɭ#N[)'UΉM &K!ZxOr)hsibC|z~ :9M% v{)D1)Zp{rݿ#eح* o3#%:/zĎ͡Tdp )l0 .h(7.kh:]CdL?=k7]ua+_Pk}w^ϑ d.\8Qc TCNѡIz?$rc^ZmX9"q'wi~uJ}PQuDŽx^:כ/g/YG`;# J]URQ` G?C9?Q9tGKڦFNUԯnrcxՊ⥷;H8qvIL'<7,컏8AtmK/  8kq{4TZ #־KԼGB!B!Oh0 4;w\~N*̙3??ajj ٌ`xd{wWG4 fy|l3Wѣ{(-)Y{JJ}~/c1]>8# 11NA),:cm4o^Sqv(_8uoZSOV~nVi0;3I4yi.:Xe)-=1n^ʂݎڀP©&V1T;l>~?( 04xhO7vstiCM=i&;E'I(QUmXzX(SF蚣鈇#(c$:uh>flHih(e4uXPrFh4'4Ps+g!cq/h2AQY>عaF z.-;j̯+e 'i(scn>#!ZA`>9I s宁MGKB/W*tncUF4샊@>wv7U8>(+B!B!޽{/7ZK=U$ Zf՝_WbdH$2kݽG'VOέTWOu;χަ8cռ˻?os/' CQm:W1t:@k-sZŢ/VM[aϔ2|r3`@8K>ZvTOrofpUgࢵE^>X y=<>!ڦV:$72s=.. ]`>^fק'|kDG :C6j=zbӛWSh[k班7z[9"v@͑2c ?am^^Y*wՏ={# ]sʨ:\o41FT !B!B_yطc9J:R[Dhx:C:a11sw26-Ttףj~8 :I7I233h0m/7Pڄ(:@l5a$In| >z:tVvFC!_CFaY&<zy֌m7Y:݈ wچrI˝jߴ Iݓ34Wbx[o,yHv4)l:y`K9ȁV]Tiw,:#*?,az|T.\hեR4xp4&vot`||7OܞTtn[JOYx DŽìG)?ƛؒe|5~&# QƆg %= ;:>K: s.-v3|1n2;KiJh> 3RZ ic4c}Bv<`}bY$t=JxaHm RQV`cVsί?T/Zy笤 ;tuoB!B!bV\~up FFYŞTbnnus$t%Լzj}[nN-yC㴔qPnbky ڠ:F5#4Pqn>p6[az5T?s9opw^@"*M ԼN#!|QbiP*o֒&iP\8J `:Eпx}1ixlWt(ϧBcx6z$0`u٨l6:s[@:`W#; 5dvV&xY͕82x?:0$bR׎2pϯ1lr|9ѮќgniƻD:먬ȅЊU+QG| xI'~`7g'f`os k>LC/yk?\&3RuEQzD,^045I5ةŵ+fq7UW+X+qdHg]4%dl{UF #݆[jit {nZu[uKmGpMo.'jpC?H/#՟Ew KS{=/iIϚ]5dahA_t钡i?ct !B!BlKnZq1J?oZ*:Uws4@/V7d3n#7-0=ȿXT+a?㘣#L/ڱ ]?dM)vm{3N+ ͝uuJZu_aHuv}8pܳB!B!B$o6cXCchљ(:3 >V+Lʹs,?'?Ɋknn28|0htU\pdkwf}4<,!x>9TC裙/o 뒐S!B!BJB3LFfi+]WN{=q{Rѹqt+խbiv͸`Olu`[!B!B!vsQnܸ/7pd NĀͬ&9V"1377jљH$0?o~e'stnBØk贐 G<3o[MP+B!B!c DP,rw"HJ*a3)TtVt_h[mEg*Ν;OlTtnBϹn/~9pB!B!BV1dY4:$lJhYZB(=W/s{*p&Nq-fwRm+IBy]8r>3~-Hxrـ 'z%?,Ɠ/G1GTR[&VJm|iR *`F3KώJ>gȍ,Z^%/_]?9-ھ@RFu5\/8P,%ܜba%FJ3:^C2y9w( :7Yf:&+*<Nnv ݌m~CWCCT:!~+^1 8MZѡɭFVѡS'?O?T9'6),hMh<ʥͥuYT4XZjhS4aB6$=NTJ_-Gʰ[UgF/Jnu8^C ,14R`a^]PnQn\t>ؙ~z vuX6qܢ}-_Pk}w^ϑ  u.bH66M% t{8ˢgd3u'<46[1!5Kow P) ?rC4aSS :q$L^pA&1P`ˆJb ^DI'4c9nObnjX%ȑˈ c;+>BrszHxZV4v̉ͨۥNB}΍k+uU:*=tyƩ?HYۣ?JlA͗xyB77-=zXQYc igǴ8K'?wOXiVC}|xnv[~t.|=O6<#{4Z:>7exUkכ/g/YU!B!K`3N?W UjpGU ՛EO&#J`v(*ΝGM1kCL̶SfuP ,Luqm|S wIsN^zg4&!;?f8 \8u 7YxKO{`6T8;pCU N;x~ߏ,mhЦ=YG9õt'z$ e2 Q25GGQ.yItx/gpx0,t_0` . cL0(l3cs48#ഄepv9%}1bTpi PVoa dS{NJwبp{>2DhI+Tydٺ=|.>.BP57ݬ s×[\,D?`W,&*;hû}a4z߻#opUKC<ٍc$]7~YU?vӛ@멤 e~%h;#7>3 !a7<ǡvq$i ^͕k5~q8Vu28==~蟺0L~&(s#.=W%x\VL.@c°k#V^JowaD+LxUkb|?wrFhؤ*B!B-Tt*ͨ|n95VE1 P?b1h0Z ͑fp5:H'D&$ߩj=9,n_ b`N^VRNaw/h\%p/0JPss͑gQ,4 ZbQ%6dgFŀL"kj˾&Av3SRβtT?{s:4J\{vV]JS INVvNx#*M%$0'M *j4U([$"KG3TH_Y Ž^os .h{7ooNSuU,YӭuiW~5Z#F䟴ѰF 3.|q'NqWC`dD*0|IUp;s,c5fQ 47җ@Q,wa Լ^904`3ǵ,7JjȺ*QVF_ } ʑ́Ͳlm׳ qʡy%%ZDƓС`Bqj).FMNÀ WS*X-TtD+<@l ,*MuwѢC0Gp8FJ >050g(%'NH)N;D# Arѓ"WW><:3ppMN ]a: B)e XZ)*Gbˇk2G/DH--9$Lg8 Z#'N5rԭL?C5K6Ľ^N־) g9. !'Yb>ܜ?AU,SA4rLKoh~}e=vld-(cխO맳RҒ(7Y;Br[*O K +2m#ۋ)۔*B!B-Tt^ ?䝇oN_|dbY,>S/5= ˖729A_3Qܮއc.mNέfN7.qi(z CE+7t?z-|\[A@oU KqgD:Q7e]\AY|OFI[a];`\t%.9T~Q6?09W 6xvqB)K5WI cos+d㋿\,frIKDŽ? (,@$Kt<ņcm_[y Y,1@q`?=0YE̫i˼Tp_fUk ; 8_eߑ2l=ZMQ%14o%ױ)K *>N]Cs3#ZyʟR=g7˵o :vs$"hUP_1t9h[ӥT:sd)1IڰMO^MD/ϐKMB!B!;BEx~IйJvNv'Ou-J{ IDATn*xdw?!ur>uh-#%:xv0`cHpV3{ >'Βρrڎ7sS!zk[I@_BR-f݋2ug~2H*П7j3ڠGx o )C*A4lJl顭~H!B!V+LJ$!N( vsׯcIR9sfCR)4 UUH$'|P<[EߤE♏[ս D%(:/PhH}OZAi1||   ?fFL!+6SeM]G$!=I{tfJl|vEq*á',2ς~fȐJTDYmGgODm4Bfɣ@*FxPi 7ӌCwzna`q Ԝ*#Om n-`R5!]Nx4ZN>Ci X``l,K)\W,kdcjk0 ,΀ɪ\dyK΁ծ\W Yovog0T4[ɍ [ye2?z׆g*"7B!B!ij_p}jf޽j%H099 X,~2?>::l&JFx:-c0!! F pIKJ*{v*'Xs#E7uFmTa0T*B{˰\IpZ!bj~PTg5-F]tfj`Gp!Ĉ8TLWh;|e-P) Խꢭ!Ǐ 6ty> oTg=|~TEj,tmj[;piA$c2{;B~2B emPyTLT;i[,0O9U3<2ʔKϙ(meX5yB'E͊ + 085*-Wo B!B!E>%gv*aQǷ;\Rdynl}̞5?|p;C=WLwx&o?˙ùbb5pW#ٛ>r;8 ^Vx\%Vu (`tVJyX5!0^ IYwpw_!'D -eX5ۍTׁޡ(4(5Ht\E(v#Fmc [BN  4]C;J: `pd{cKP :'Dnz-?yas͜L39 棕8KL_feBl:Di0cB=&t>g; |&&$b(oÔR AgXN'.=E[eQG& G[92o8^z_B)_ֵu)gw"yэYVpE $PזSg2|A_=V)bעT6-*Pa,?+84SOr3Z.EnG<'bT\~\9X?iA2ܿCHjXKgrsi26역__B!B!b$=2kuyRi J138R E=k6j%"8IǪ^&x&7H(FsΑ;f#&cd(ВĶx8&èɓ tU?j'؋ 7-2C GϗRa% <涯t`pX6JJ K9L6?kbJN|PY(e`mtUα 'n}nyW59i|K*[ؗ֙8ZCG'd1ܗ߻:$X 4bB0k*7=\Wey -Yqܓ؍}@ATZñ< _'_hõ^ uRA^,}gy]'Fճq6ŒE0=0-gt8$&_IpԠd =T99mM ]V+.H3/_;WGsk YB|9Vm*vw `=nsmgJi|dž:&2婵DRt8ӓYAvB^6B!B!b[*++7)W5\~ЍVjv^AStkwC&`;ޫo_ߘGNs:lzlzw{78v bAw7#):ųk9Ż|1ߍk9ԡw-UK9着{~%4Gqe\zC!B!B]?aS~l{y3QǏF'իߐ=?;_/> zNS2cFy*gj2Mřy%N#LO3R2EB!B!knSA)W5zfBQfңU %0@}aR,38 ۚS- :+w+?e[Zμ ,8qujݙ:/pyZ3/cf!B!B!ֻ=2V&.Lp*HHj>/äSa*@ohn7.vB!ᴉ ԯQ}*B!B!yܻwrU,J5YF|{R[$B-g[!^6}?w+B!B!7 r<&] s4Pi1xj[(v۞fJJKKj$I0D"MB!B!B!/MŘwSa`ςfZZZ,X,TVVrwSS !B!B!B!oOv\.D@Spycddd/$B!B!B!^b~E'#*tft-C1h饢ɊDJ]D^KG1 ͇(j(*yݮ\."ׯ_'.fr9z2nRݚ?C|g:JKz<@Y=cyq#9~[9x);"Z}X~o#|ޣrG hWh+H\; O]MwouhW_B!B!Xk$HF_Ϥs̎'9TGS"NCrn@6geh*ytfdw>{ 9WK$ܻw_ݻ;LE!^ .}ϸ T41UcIBܿZ{?cN7Vc!WS`D_Xdx;4#`B.:r\ Yg,&4rUPVPBT8I34>7.0MBx{ހ"L8sv$xkCz1|jXh~߁v?R!B!B1G[)aFs"03YN^B.ִrSw⏿}Lrrzk jDD8-!K`׃Nł'y]lM&n7Iӈ*0M(8a"Σ턜z>h "A9J F{ fJkQF[6ScL3?;E6qm{*G;G4m6IH1<;WWM :_D|6}51'b2`먢zyžy@i/p?`GZ\{}mZK$B!B! ZOfObM)R6] ׵j4,,/0O>ՠl6<7\)++&?]94c;tqnNWyS&A2ƿŸs#li>_ét|?- /nu[6ԞֈR, _l`׭)BXMۙBߨi_&,!B!BXKpzs׏F)Tgs;sKUZE|}z|r/wjЙH$d2˕lF^fb"rNx_~:,oNee;w[?#u66`+X?ڎ,×xy2Ӿ8 تԌn3s1PT{^%QNqFyfjNcpkuV`ӄhlu5 $&68iL+9v!~9͛ZT_x6Wpjz U(v1u4pFiI'Q|t, kt_/^Hm`+ժ#6d;At|P'dQa= 2 pk-W:9{DfpwDM?(Ln !B!B=Ti*ד ,䳨4*}aBŠ?Fes <^KnMd$M˂HjnMEE2Zw:g8;YȃƒT4YQ ZR3`:3u|smϛVE} (o-a`1EҴU,3}=ML9k+T֦&izS›'O`շJ9m'Y?/o<d@kxό [=5ČEFƟw!( xG+L3lP޹(}Mz{¤(-'lzVON}-Tx֣ͣYPlT<]WkŬ93Śg ] A3yӶpmMkg 鿩٩g6<8dyOXI:V:QvBnhtƜB!B!b-UtEE |D8@&cvPIσѦ#J8T#)gNP 65i[?;X|'/!^>ci<ٿXl_tfY?p:::ejjL&^pD"twwvs$]C;5J7u`n&:51^kSߍ}(jИWAzh8Q9[idv1WE LqhHm8NGB * E&:4{ : $& Z:p8 %Q=˧rkW6A`etm|[ddC4G;5Je IDATѢt2$[RJ{"GQհ7O, gIWUQ5K?Ngjf h|NX}.sl'{9 4*K{if'mT.XJDi3'y2鍮dEM>HWI z# OZl'jh>idѬ'͓=4L[[1ZyC'gKB!B!AT)v=D"7TVVbXּ>22B__^41f׹>sѨՠŧC^g9rs}1_W[K _6_5ʒr'ÌK 7%w*hZ !LgOOt?x,.OgeAT<|]+V3N tX! ;wphʂч|=ZVWbA8MlȬ¹SWW:r ޴\XXhzynK!߳rORx8GmjE9~%z+MkԿSFG\vgldB$/I@SL݅W.Lx 7!B!B4ݻwh4fFGGZs%/L*ŋ/yl& nzBzƍ'5;tff3zL&C$!/Wvjd2 '^p8L4%޼^q%g21o`p90wdufApRn};P:lH{GXFOG!Ol-dJ-oTQJ \_yb16thiYUPzJ0&q!ޕ庪'voi_/PZ]y׊Q`$\c^Um+Ҥ0o*< =rYRPdj1XȑdYDf2p4OުrmX v >ˆ Ѯ;aEsUv1TZ爏 0m,aatʫEٕ˗ %BoӲWcQВ%[_ 9H{B!B!8x\VErN:`1??=8XVB:ߏh|λnر[fbID{-% ծv{;ԃAmߦU1k>1dI<<UsZc]ئo;Lmz{a47=Iܓ4v3X䉡XՔ4tsQ, 8m!5t,,<{T7g@`E M.v`,* lpjKxdjloQg`Emi|);eV.u)! g{-4%wDMȎR-C:p,km2hWNT+<{~{b4Vξ@PjVH=vV A1;Lka(j4p:7X%Y>$oCWyۗ~C元sd׭]^/Ҥ_`;4 z_`& 67D4Fj(hؚf<]wn+AupƻCD8JfY 4%f,z FjNZPbT$n0&;ߝ<c3_=ӳ_ sCQh1M7 ]Y!B!Bv$DndNMMxxioo$.J"ׯ__dFFF8z(|,;QfෟƩ1l5MG(xL!3RwnjOa6b2fMfbl-iO*pǹ:sO}e GUT;!tNjLRJb<@髏Y.g +qt:@*gz%ԽsbI+ ݗt/}q?kb*9bE(Y puT;(Ԡ {ɍg3r(4}nJm6ĝr43LߕNNeՒbZo-3 b rvRsG*i[1d}ϭ;Kin*<` ",\*Ҍl| )]roIh ׀-;*NNŨY0cXPEz`q;L=$|sxGۖ㶡.;m 6tm^فtitr rwa!S̴}Ѐm6ph៑=ܸU؟hR:j6j &:ΕU׮UciI/C ϼ6,l/4p#ObIpX1 Nҡ& _ x!%4Bq3jg 8ϕ%K" ; Q!B!B!āQtkowkLMM-RΜ9C8ڵk@a|;7yfs0\rXlM ]7b`I/}suSwMF&1ֵZ#|:ځ;gP!B!B!Bq8yU|+:}z@OYYl6bAI^u,˶΃@oѡ6j/]:癸m,5\7qOBNF\' DoI)B!B!B5Ad heeeX,oPOΡC^/xOύ^.;xbnB!B!B!@QQL慶B!B!B!B|pЙH$jkX@X| @2nSB!B!B!_/t. \l6SYYI"Xsi[ӉFQtyB!B!B!B:GFFD"\.jkk7\G {' v;*HB!B!B!СCqH$]/ڠͲue2yiii5.BB!B!B!BÁ#m{Ϟs )--B<'[M988p(..&D";LU6-Vj[+ᯎҒ7?bUKݚWRq_}|nB!B!Bl͎K@`[M$ϬwCX9#\/PrlS_ϭ,㍳ػG&޹`yJY!?XԖj>Kt'lSO{ Y"L8sv$c"~SԽ]K]E%0xy._#4R\'تJQcb1dX_pg ںc0URinW>eoc_ٿ>iGlh|!1᥿RNۺ-yd4B!B!;tЕN;CPHM/V\S6 {M19ӄr&<ʩ%kle7i NkQ7#ftfYNMU`wPj1J<_(Onx JU9 ;utG!RZ\[9&_>͞eTB-3t(k,?MT噹38跘_n}𠟯bT4VA,>2+[iVOf>;yg+3sjɱg 1|moM^굵9~V>هKtNj2>YF)+Cq71xz?B!B!D{l~ O1<4ڏ7}yě-W!0"Sh!m?Fbs  dsd_$\Q4hȌoc6Gd1u*mG\ TT4Q>2G" 4vt荐% N=Dry_8w֓V< UU:j,'h;id@jdp6XWx7u`rE4^ƢI2qqJyxJqeɱ#WscKq:TKb9Sx DҺi/Y8eN*"=`Lh!~9w9t/BXM1Ny𚡏N|ҌɢX8"k*u; !Və%} ^:V'Ogӳm<v*|>6e4ˏ 6֤qUC?7;t4Nt'1募Ofl|]9&w,|[| ]T4,?Ky+N;i\{]*U8YwEhQkd} $i@>>@UGNQUB 5 u*Cu6K:/6B!B! :\.8ĺeLu`5Z8O 1mH)k-[|swYon[i*dow7BӾ8 تԌdPJ{|y=f3*-* [g6M_oH8F]W#0O"kn6kkKRJەw]{o# V*rй08 4tre⺛D盥MB@Т+O!`&4hy>Ƿe"[u+ܺ +x- O.tjM=֝"TU? #^SZN6r|:g>*i{P)hQ r5NNld47Ө帺9m#[ >FF5~/ s޿֎fP1}u5ԼۂuhwL4vgp}}L{.Ti.&h*:JxئE2τ2`p$cOy`Fھ NMU/ n0F,mE{鿙C[QΟ.幕kQNqFyfjNc[r+f!JP kJN@3Y4ZLN Mz=`;%/i|kb [ TP-Z*ݙ᨟Ge(Fрl3Y'b",眔Epg}8I}_l8QsL c+l7B!B!( P).X? r]V)vg[CjtN5$4MXF3OpGd"Oߣ0x|IT <w\Bb`d_ҋ|W*=~¹B0Ft6BB<7;lXԳrEX>$xnoа8צDX;LTLNB *JsD=@ eHzH,##|s9 [\O!B!B&AkhFI3>ᇺ ס,ޞHιqTL316Ձc7ʩ'm8U$xff*K& &?pb! ="_h"IT0auYT^4tjŠ!ʃ@]MɑNdȤ ϐ2,9} vAjН i>ʂY;mJQ܆Jٱ6`8v3<ЪC#!fJͣWY>2 cti fp$޻s{;4e,Ul>'Xl1,fɡXkB=uhJS}6@hyyr(J [x SCftJE^C& n7@\êqLz%؜Ÿ{1|C4ؖ]KZ {1ZS S+Y,~LMmT oI$E(v 5%¹UC-л<;}<|>-Ǝ s?ϮZ1{ 7>t!"+4PKJ IDAT曖D7/Z;G⌛jbߚ$N\|p\@\cA&V촓og&3ݺԣCKw?W=Ҍִ4IZ$dډQ,v`K (WW`oƀC:s>産zI '&0YD_z6z#+އ" ^5+4kiOsb`r|s3aTxY#Qd83rn+eO> 0Ć߻,#VyYSI榢qVZw֛H¶4nv 7:(ŠH"cYr :ÓiaGxxppe F7H!M q~7Ĥ%L| swu8W48bEn. jC +5 5~]swy~ҭsGMVroqlYZ+Ugf9op)fb9 y06ݳb8DwO*4yioRn/?K=uC]r+BV}ʥ ?:7[1nU{ X9o[%5~=4t{;eM.~"NUn;~Xy~{~7.jW9釘v^,/:V  wiXa³03룹Ӎ?MvǮXb¨bdrdN]-Wȑ̀ٶ8O9O6Q=;DDDDDDDD~tC/Lq>Ʌ7-}mij lFG#M)OI:9jG@!m5_U?3C~m+m)TE3Mrv{]_ DCDC`./ikn;`['3%QLX튉,*0lRJ;E֙vE,00~fz (( ^(tnx-zOJ$nږJۚ~ ĉnmWJx!rq>_V;i68Bz.S$xja`_k)LM߷1V'̆=*$QXml7:h&JpxD}54Hn  o Ͼ/³"F6 W͑C.2G,~ss4;G4HS>\3kpyNJr<x7 qT9RXqz+i-#QFT4hS] cb٘L Huy9X_Wdx&@Bk5'S\QHjj&}ߓ[nu5p5's# 8s9fq|AϜh繺i&GlvJ9~=LP lmfRߌ-N7q`R\z  a:{ݒ#8%)bwVP3IulFn82ȔmtBwH2 ,TxL`)^<@bx`c: 1Q0lxZPi2-K?Pӎv#2&/`m-ǖOr;SoOp+} ,8fVL[2^ SiY0{gpε| ]M9"dse9ަrlys[^mn d}O[d:䊩\-x-)ᆬ*T8sDr@)x⡧U}6zJ=)>MOVxj$DUc{yb`A.&rnM|jP6.߶gڻQgWV}bpX3EnUstd-y{5y ϊi5k_jcY"C>5}aZxJT,Eԝvs s>@g Z-H9?++T'*^ۏJh{kcF2YN{d'x*(>;- $"0"l S tVq2 # gB(pLͻ9ZNX!5{qc:楽H*0G&05:Kd pӕzK4u6PCD M6dbLWj 2Y׹+ 7x eT8z[`3pw13⤦݅R\7o˟`nٵNDxPZMD!en4o'""""""""+=O?;ݑ033]~Ľ+t1 ﺤsٟic hNy5tF}|f~y~z35"Sۓ\d|a /5>>*:Y?w5x?4f']΍)1URҦڷ: )0p 1SDDDDDDDDSmU-쓈ȶP)""""""""""""""""""""""""""Q)""""""""""""""""""""""""""Q)"""""""""""""Nw@dNA*7ӽ>F4a9-_|L/T~qܷ||EDDDDDDDDDDdctʖ04s~A}N7C77'3Ghr*Ŝ_ 1eʦW'xX=޾+G>\zgC>OjvFF(n~ײI١k`qq>;!Utn7{ -Oy9) |uK +:yfr1W75wS*NL!WNINm4O9\>`sR-;濽0M4wza>,B$*CNos-?.[LIf+9P)"""""""""""+hmfG(_P?rW1ihp={Y3 G^ϟ|C+uVrisbdRD}*:+fo\0x5:=CRtKq&xX`/կC~:Ɨ^e:_Jmw{}CNyp2\ """"""""""Tѹu&dt^&.~8`nhf\ ߉Z*pLi#!.ev-?Se`g 1QزGrTu4_~ #˯;_3NV{ڏ(0iƫqL=8Yo,/'{>LVe<?:?G8~.zm4$~"3<'y׎?1UtnBdrEȹm`q(_f.50lzt dr>K;G|Z ϩ#N:[-Q^x;-T]Ҋ[Ɂ ?}l6$OVWbwⲰ֌gt(@pJ\F/2UFR±7jH`QO{XHܭX0PV/F%>؍e!g-ݿ|,3Wxf9lr'3s6@oY؄D#7WgS7T:U%_m7"kBNY~ӍOMTѹ(-I-mZ2oR,|O^\.IB{vVsJ>#/n28iAKmЊ5>gz֊q3ܴtQG*f-[>ZBw*3oYi[cr4O߃gf)4yXK ԟl5"K&U -=N&?Jwrt5PK <>soi$ReI>v.7Xj͍:Ce>Aî*"""""""""""]t#;g􅇗zkmKSLDvEMg> 12MM096͇gNȶHRL$(yyMY/a}3rWGh4# U)qy(\r9R=x`|Ju{9X+*Q 9K-ꋑ3ܩœxq79`d#M"Cqr`fO[DԭSo"qn(%nM9tKSǮ3\6m$B0_8s^_ 9ȐTɼ3oX1}uT w5fZ 3vO) ZL,c"""""""""""W :w {|<ّR|jXd׆7j:S=x9n(z4,,x`las3& 7XqC)6T%ɱ}2H3l F]5͝N|nY`dYi:ZE63S 8@<)SR~E:猍O[0P]2Υ ;PsS=$V :̬zMTsY/eU#3^jgVS[U$]74͎LpadZϊ@6^XY>~z/(7;t/ϟaoj?"3tsf"|qmikxOM>atg;GÍBˋф 2k+rɇ37%nqfڇ=+?X'E6. ?:h1Qm@{p`ƶ@~{=7u]Q}%LS~^xm _1fA.~Y:^ &ޮMM)?;/f\>grUhYڌ'?fj <0 uFG#M)OI:9jG@!XUͼTwJv$~}S{PR σT=,$P,(`@&A,U=L{&UbMęZ$Xyi#܊*,agԳV\&d78J?W(ꏑ{͍; vH߷}ِWHlƨ\vtg2ExZ&֯^,М4x_}:K01쥋K-Zx 7arlhތI'2>OlՍ,J~|AϜh繺i&GlvJ9~=Lp}hk?{8v8I7@gb;s`rpV jZˈ~poT-h+X=N<C\$\r9< - VU.h/qmɾь gJK|iH$$7Yy]^15+G@S&,ёsI_l=^''K pkYVVn_g.Ei LQh݃9'X]} K32ҿA{~FI#`BD>A~Z|v(j̣fv2&'fr[/I O333;<]G??OL.=4{Zy)̒';ab]d7䜻6^8@oKtcȴ?'~&~F^ѹSGۥǗIssc 90SƖ2yikw: ) 9EDDDDDDDDDDNy| ; qV\mTyii8ڕB5w""""""""""" :EfvԲ@|pl"x+Dj{!"""""""""""̴("""""""""""""Q)""""""""""""""""""""""""""رtD6_o/{3;LcKDDDDDDDDDQR)[Jwqsf8 x謤eD$gn\\9cxnř g.EV' IDATt vWSqu,ӝy84vN8ӝ+8DsN1~xƾZf) PFo ~z,""""""""""(nZ /r RD'WjfW==O^7ed,ޡ]߉i6Dʩ թ%ToNv4sL1:Lh[Jz*T-Yp0zf^yَw_dW.'P׹84xv |kCc/fυ7=>mOk{a.ci nC.&SDJtlDǏ6tV8?؛xg?YNi8xS.\ydJ. uNLF+NL]Ys ~u4~ƙ @g1mc<ϙ9jcmK:^o 9w޿Hpt?c^كu6Hnq.{Jٽ?#[tnorĝ弧z6qWS4)|>4/< |rmsS X +"d 3|vI s,YMDH؇g)DVWm 'ZuF*6S05̘9KM%L~W4 L4sVgO'N 뤾1.j9|k,?^!GrTY7x?Z{>kaqiˍeϑDl`dy{zI ۃ4Ov(sjWJ(M[~,uFf~vTif',u)3I߆dFTo[ ZC zZ:+q{9!Hblx;A5`oy*S>hBVy&ڏo'ȧ& pZjlyBwB%U3&7rM}'4:)Afvpo>صaRй I&l|nr O \ Fy}Okk\/:;N'n=hw|;Ŀq~C-W}՟Ipz `p^vp4a0{9"Qb#z#fi{c]^Ip^[qp&CN+tud3\9 gi&8V tn+q0gS\y]t4w̛Lzs#/Hadj g3BN3ޟL{ o +`B2g?_N͘"!>Hvظ5JZOg-Kp,Xkq/}O㷼f7|1iKp79}n7y&Άmvyx%n{  ^ SKzk-"""""""""Cs70QZ ϓVWC `p;a2])q fm~jJ:DY͡W+p@H.n桢΁2bK$9+.}sL 9bp՜ŀ'#KL$ɃaYn3G'I+oMTFCnv7e9C9Ls,ex{UTznP6rܚJ\Sc5S>V7`eh.?6=xl -{X)G%u~iig*Rsb$'jjL4weD$B0 6 {7U`#в) )&PUrWm͸QENsxg9;rge顫D:xnb_ 0-wyZ,- u0w60iqBx9R5G\ogu و/_|_R$جSB$؃g @"KvyH$Ɋ6EnAYq녴wg)l1㬴et| kb,s5}l+>%ӘԌ2-80%e&xntB&Cd(SZR.y>g,,e؁CYaVٻ5Ͳ]ɺdfUrbE>a|ԪBT8b)[3EDDDDDDDDDotҽ<a>+Ogy_ 拋 ־*h?y/'/z>gaxX03 z8$If\=ٱe`@.vSI ̮Fƕϵ"lYɅ D.}q)0Od:Ay_iwϫFG#M)OI:9jG@!t_6 0iH,p҆" ~7NL kFV7Gdl,uƊbB+NYw׻V* ‘zVVn_g.Ŋ@+x^mw p\ Z< ,l|ӊf;_Rm_+̬8|˧'hy̓ 0ʿ7aީZ:8Z ґ3!6\0:>MOVxj$DUcOY2*PMekx/p6Yˁ/XɓOӮN8ǏX@UN@jh/OHfv`oΚ'kOT8BM"~Ǡm?-]ZU>}ozq/I?m ttBn6AUba駟r;ffftqd ?1 @)]?\LO չKbL/[7WR)ej{_sz #dG#ta&c)6sfߝ Ljev`ܘBNAr 1 9FuTA""""""""""?xmU-쓈ȶP)""""""""""""""""""""""""""Q)""""""""""""""""""""""""""Q)"""""""""""""Nw@3;9Nf%>N[Wi;٧aͣA׳hzpXY# |u#2;]lkRXM0ps.^ġ&TfCD \` ੢˃y6ܬOhAڽ9&wRٟ'* IhCF&O iBCb`xj2 @l|R+ܵ,~khOR?u0Gx:f.ƨ?c-c)#5'ؗ`بi񖓲rpjw' &ꭠI{Mo;ʂsSIR;LN-39"GIۭ'$ט"o)|qVBgU^(g篛i8Ƴ^?.+uVrisbdRDWWV>)5YwO:~2r"\‹y9q /ƙ @g35j)Wx,s {߇\aj"Z*՞r&c15xg ;]`97 ȃOM/5T;2M/8<8Ǐpckk Z|ccN~lvk u\W5:DGqn29a@6z |ʒY[~r}UTsLD6?4\=N06Tnɱ086n8nz""""""""""? :Zx#2t?5~?>).)$ :~ƫ-yp,/YbppaatpK3=HOlѴ͂a S`݃99(+U{'czq  {b[T p^{~ȹn5n*—B{O,LYgdqP h Fpr`=hf2ǢZ]b1>'YK3c/Mnxqa ؃]^X<`W^7{czTg'`5p*zs$09so[i~iM[UDDDDDDDDDGA6PT`AT |T8+fзAX@,q0ĵ,.Gj(-Rᙅ`i@bnInfÿPLq)& 4,曏&H>\gIr,Y,DﭛXjä@)ΟϰSFqy6S8Ra/k+a͘zɑIc`[w DDDDDDDDDD~ts47Pk2Tr>P+)aFʪ܎" ֔Y=֕{1ioHs!Ĭˈ}O.x'xkH"K=8;(Dzk{;ZXvPgxT>~ a-/>jbD6 """"""""""bn7DäRjMH]Ԝ 3r9ho%3rхu[*$+R,Uv.Vl56d-gsdC1zD Qc.Z϶`*@>lq=+M-jnvO_Gm{DnMy =Keg=^/&0RSEckoYV-wpol?chpBnpobrm .|˩wtG8|'irl(m{tM٩"΍+UqNrcDrvj:+1dZ1prl+?dt E^WN/K]֚2KI'glU>V%o$)iW#q{B+{&}T[L~2Σ7|2 6J)n7M/@p$SP: yxbOG|3Rй <$$љ$9ڃ K_3~o_絶3IGZ2W:v{9DƲ@npirplUr 7n݄W^K9 W?Y929g!f@!4w34ۏv&R{ /zJ8^f+$f w~q\m=8x+վFE"A*gq9={~GxN#G2Ë|gDaZ ^"Hϑ '~6ArC|Q*izM!:Ƿqm+q;l}Ag&GYs%UV t@G mDDDDDDDDDD>ݑ055]q8ϵ5u兩Ě0}btHg~\%/f|,йl9S,)"_Tko~&k`iy`2s:Fc.7_K("_s%Fϣ߬r]]ڥ?AcG)˃ 9eة#ʼn瀉 [res}4SDDDDDDDDDD*:EDDDDDDDDDDDDdlTEe$"""""""""""""%tȮSDDDDDDDDDDDDDv"""""""""""""(]Ǻt{|y%7+8}۱p;s޿m픈NFν .U#\{ﮭRuUTו6bzCK7,T5pyJ)CO6 9}Ow?:c[3w>~;BcK-\K%ty9%$ 6DZz*h<]㠬#=Wuz\NVP(’ ³;S6DρezHYr+hpcL6pW@c[U(y9 +ښi"F4!gf+evi ϐ 6\ꢾM$uARM1DD]:_hHC@oS5%| S EDDDDDDDDDDv]zsz8Xsf#\2S64p6<=UJ09~߁y``80I" ׷Ieԏx#ǹ߿&˃L):κBwXTYZvEmMɮOx~R]}\%78f;kG f#l˕420_DDDDDDDDDDd7QйrSD"tɡ$9^<=8D-'hJ2=eV+`Kfjp!Ptܸ+ӕd:+C}q=9$S]_KRXG{>;gb@2O>ԥk~B[s?o &D,]X|+pWܛB6ĭ˧=OKw7lq Qn~k8'g#˞Kxy/P)"""""""""" :\ M'jܿQşH//w\|(n>x{yW8[&%w@xK,3W]V;(ğr&{v?;'~w';˨mq=|wA~:ROq2(9n.v-@EP #&TwxhMw\XO5'^v2BIrڨ{6_uZ;y,u9Dd.|utx\yŵ&<8$e-nlJQi6 3S P :wT<PăZDqv~8ސ)G%Gϗx@@6n>K‚c[Я|Ui1['zL|^RH6x8c%AtzIϗFBiF ׶&jqr 7>d^q֛#vR7?~GérQra @t(Fxx;V6$o e 9s ]=KN+Lr ?}1t)p;EDDDDDDDDDDlbN餩!vv\8Ax0AaftpHΆa' -\tllse:K0s;v ÇT\) 2Xᕳw8䢺IUCVR{lKMݤ|+Gu8B|tg몊b :\tQAμ?y;ݕ{1ioHs!Ĭˈ}|]y, Pjn6JHgVvٱv 8M,bdY62\, TCtnFN07J_0Ij> 'Mc9 t` >'`zJ)!Exlgk5 0S"/a\ǼX2IL?5"J'ip }zaӭO0*`UR"+nᷛ#|ۄw_v/""""""""""Tѹ#V"=sRDWb >Mhv([ ͑ W%jl :j;NYr `bK{QVg`/ u6ٻo.sE9j?E7$Q\Bk|;::p)K!.`q;񵗑fjc11]G㋍aP>5D38]rv;f9|,orZހ{s8ulϮŕMRl5;=Vb#G2$3B`l]LL3QSAe[-VHg8A|WUU'-^:Co<# Qᕂ|[*hz͍;yS+itRY @,Yc \ O 4Hg ethҸzӻ}b`'t>_nwG6K?'II3Irfkq>]?ڽGqF&OPtl9.\SDDDDDDDDDDD{ _54<l """""""""""""(]GA: :EDDDDDDDDDDDDdQ)"""""""""""""u; GeJz{41dN`s?־0wPrrhKpOV}&~08S>(z/S |ǙGo;)80Nي.ȮS6s#~޻kkA]gueMl|ÛY=.|'J(qaeLp)A>AW3'wJuSNJ<ˑ"?GaMV2jҎF Է'ۆD-G:\9X) ^m׼ƆKwoD(cG0}|[Kٝtn5A:?JǍs5a&<.s}<`*޽$Lowsrne> "y2f[MiwMnYGlB#GDbx[})wZfzB6VD 'JYBu78._d퍔fbn<fl'񟠰!EN7Χɮc Z9j#Iuqr4?쒱l=k,tn#:<(S<έ"L%Y9x/HNfBAn1]ly7{/ɝtFYl0A 0k$ _&S?⥳+/# fd g^maf&hݔ^}ک!`h \OUPڳ;Y'P=~V!&G[Mݽ'3o`2~QDDDDDDDDd#)j_ss_7w4hq?soă]Sqx~meXu/Unvi+4˓[K0ϒZeϥwHb ˎNp_^uSg >=3qBP)a/qW׽ѥ*Zp8-)" n}6M<<9N{|:Ĺ\L~5[C&ziAݹZ5;p!='z9]oy9-X)&.m>L5 f3h|Kg'z '9QNS=#<ƃ:O-xRx#U7bpo}ʁ! [K)!GoL.6|}>\AZ8|^_1v d)"An|rgs7<@[Ss NW|+pWܛ:ĭ˧>OKw7lq Qn~kWxs$?DM-ͥXsDn;V>WyK]!km͜9_B5Vb ~W/b +8O5D؆Å=wf+O|_`PwGc+t%kdžUkX5?eky/?ٷ|u2QyK}2 #E/G(H s WG^< &91hhLw,""""""""률s'H%Cmq EGt(K6ˇipȬn=//OzzA=/gCE؀73Yxp 7,R ]x(-nk2!I2j}z{_8{%4#9"q9tPŔo 0'ag]Dž@t%U-eX7r> ɞ;A/pȑb;@BYs5Z1s=\X%~Cc] ;5h#( +Qr|56,W@xbR}ܺ⫢0Joq鏱uyV}#\oLWy~?('YHƂ)i^vɋrTse'-|ڙ F L|t{7m `SӅ+]vZp؄'g࢟Dڠō\YgOzlj9v_ޫ274=] ΕPHejt99u /qWp윓 ?rªƐ IDAT}!brv_i5 2pX #cCuo͗'=Bۏ::6XO`~mcB f6q cQӍ9&؛$hwbzZ2D!2N>NUMZ<՜M*m»}jn5ۥGt4@v7[ItsϠ=&H9*9zǃbꩁMInݏXIa1ᩎbSܯ35]so/O3:PϽ44Mܯ,n?HSuf Ӻ8U3:m7nkMKO3“$>N Uk\G}\[Fr/G$PeZеǦ\pz\ PE4kp㣉c^>7^Tpo}L%R ?{I \|x\xpȹ)P ޤo*VRs2͜9_MSkr&5 c߫L| 0r#N̫dk&z;6:V__5g jgx/U)W1c2ACI7Z5Ĩ>^+36X4,>{=q Ք2N[R//f S]~iMxύ>Tw?bDV\ [ڨl݋ ӿ|Lf]½1@b-eQcST Ik1rf +\ʰ=xPGUs$" =S^ ܳ8c=A~Ci@8&n_ѺPy,80@}[pP屐 L1(ܘ"sHs$O&+@ɽuȁ3cM|>$*pM6xXmDЃ,=kkǺ =2- 8˅MxWk367agE b>` *._[7cæ]%p X pvJn>&wu8E D$ř+Pu)IDDDDDDDDTѹMfn~%9r3aq7Gx K\2<544p79 4Bj`!Oa Bg?Ͻ憾Jr}kFIG݁˒Y^Mw6}cũT8JX՜+ Llup '^t_bR*y_lvX}٘B",YQo#&s0#dB,E f Rb,P76 OcMk%>!5Mt- _߰i- Ӿ(JgIeֱfĴB&y3ϑJ@ӆL;G"Iׯ@6D{Ud`:Vw/R%Kr X-ݥ߿*Ӕȣ ~PRj\5ޖaӮSܴ<+LlL[}.FC8i!NfsuG rڇq*=3>@.tHm ۼQkTYBᳫ.<-Tx;QuG:m;>-2¼zgQrqk58 ` B$S$Yt`Ɔ}[r 87/Ęʹ<勬\F _g7= :wQIZ5FX|sps'?6-v^̶z\12b0&WW}t߭br;H6v>19po˄1! 52\LBWZe'F}x#Zʱ%b-&C*6{T0'A9?6_ٺB{ i(v0$cR ]ȁ:_]tܴ:W3lFgI >EP[&a8a0[y5&൥VE]Q(H'M,aQl чv_5_zk2>:_Oia'8qӄ6 fjC%0G~8cö]gEIz~7hZ_#VHXv Um?Hk5&d DDDDDDDDDv=ѹ#VrҫtVEORyIv,>N!Fd0N~oV,;r[ct`K.%6kj9?XYJu{5\8zraZ(qS+@/ mX2ziƷ<d.u8Lؼq՟* L;9O٩\z7JhG٫j=Jy>ԚJ8l\mVxdm&gɮ:0pv0M@*rL\X4 pxg xixlwL8:ȅ'!loI/uPL`u1LVhlK!0D-mpgs Q7OF1I[pT;p*{""""""""+(RIm`pLYAZ\f९_ز'>2t,J>ku;(sde"^᧜ZuT-zű 0@7I sqizvf9|,Ħ,e6f5l5;=Vb#G2$3B`$]'u/," BյgrZ8Rs9?(3'߼##X&xf$vCMC6%zº+'{?>jO7M2qiZͻj3y]i۫8l,.?/ f/ S~f/})=~uyR/L%g) 0Pw$Jh?WIy0GNʲFN/.K*[-M3 S}Ke#7Gb,Lυ1Ǹu!ykN)_0x+i}W a>Ե(WV>A~&Ӻ"gi0ȢfB;{%:]?72'kijwQ ݟc:9? |4,] p8icc5^ 29Y7}-_\Y7J0zZC6 fo^\a 7U\m=8x+վ8WpE/՝>|vHGSA=O5b.|gl5ݎy{/%&\p]@mOVn$~ݲ^~'[8szKtʆ(oNК [9a*z?lkODd' """"""""Q^/P|~,?VEK~ßGyvڂ;J_TȹSQY1? n0S ԦLT*囮.)"""""""""""?4 :EdHRXګ9Z06,*"""""""""""GZSDDDDDDDDDDDDDFi>l """""""""""""(]GA:Ȧ3#u_+<_5ry/%v  ayJFν ?zp{Ǻԏxlo~}ƶ4/ r?P=˧yvχ/<꯿ӹ%"""""""S6s#~޻kkA]gueMl|ÛYi܇Sa!_g*|gNOhn[˩?WWJY&{ǹy7v<*) M04=-e!GtֽmPVG AƏ׫Twm]wE(RDEʖ*VbVvn Ni0mL3(p`^f>@@ifځ'vFHQ2)"%ѼWBuM,ʶd'`@ܗ^{ؿ[BKwy@+t# !B!B$йӪ챕ef"E^Wmʆ?KӅ{*15QEɤl!ϟvtG+'6F6~a.=ʉhm ffcLyJiAKP .;//ve'd?4u@>ʞ._n*ΝB!B!B|IsM_uݲOx6>evVN誨B:EeYE._ _CB6cib. S |'Y[7Vl5&G1Y7 Uc2k fvp UUN$TCgjH;o|2SC M>Gsa@>9Օ-0ɥR=5pfVv6oz.(4tޡRa\/Ãvk>yz <񤋖6#>yoWJ]7ު"[̴iBU!7֯?5QkeUf |ً \K9g'p3H0|R ο ZS%nC!3loPYYrxh..vsq-VJi[K56'v\Skwi5(vQAOlhEJއlU@oʋIoqޛ[j3eTv6b5RB.& ) 4VYvT7/*+ ^;7_[+?i!ss'VMe9*M'T'|hl(I F-1Js*yCHNۗh\Hm.6&Posѭf9s-]6pt޿GȍJH :Tz&~u=SЮ[im@qYSJ>VzRwz]d,XH-Α0E=${o=;wCgf9=1mX!-5 b[Gq vf.Q谝tӪeOjy7(=&/g4ji3#_J]Bp9w+$Ϗ2_lfUڱ"Ey6ںִ88Alő|˙WRB!B! tr#jb0 H m|pvAWh1v8ЖfLZGjROz\ok9m$%|/-95AJҁy@ec(~y } c4 "MKC0We;M4(>Hi+WfN>Ȑ-_3'quWghlkrĢւ\d(C}Ϲ%yܾCxJi|={1[`qpp4 onN}Nj/n1X} #x[w#Lm[땕owW9]lۻQKw`~ݯ2uv10r'[ilݘe^J?RzXV7rߠbI+zgr%z=hviȇ "E2afkfHYc IDATcWbkMR =7G0ɱSg+% s_!B!B<[$йKϟҧ<|4!ouݰ^O!dj|M%3`l!-2@&ZT2Ha)Mj̆"ЏtYk2* YS%fCj($bҌTܺ[u'1}lm& $ nRLiZ u&pm!.@As^EY¾U, WР6P#d^% l'ȑˀ^}?y y4i&%0. ؆@z񬯇 :W5&^qyVF".- *FKg=x]tlm%[h[eiR _ |iKmyܲʫB̟^UVfaB4=z)6|%ڿB!B!xHsߺ+(7Zӹ%ea[]o7-L!~1Fbu$Ӣ@>i_d=F>z K->~}@!wFU0'<j- 35ѠalmSQյ{V٬C>F")[^=$rl1{U!G4L8Ɂ>#n`ӟ;<_磋eTmZ<<>5m4_|rcEzu1A] na//B!B!vzϗV :DZ 9y"y-J&A`pohI=Ri+xu͐ rb4a#+4DHEuWrTPhFDzjL-֞j $n y*7yu-R[Cźɑ *q^C:eC|hWR%$]vH1SCC!7&4k)k?~R}H>E4Սe,*V|Vl+Ld {6+T&) !B!Bgdt0 06z 扦K[owURoLM8@QZ> &}`9do2O.cb$E#7p'OZuTa'uirX;T | N`%fV`0UFp #^OMc=]n3 ]PgC"%2YLf i?.lRd+n,`hB_j9K W9HA۫NrqyȅDcOfHȢ/Jω1z<3f!uU\jtruhR׎3>f-PXUp?/*nt&d2A|z2W<}puXހφ8WbO.~,7mL@n,=;ͼ@HFjWVs)@Kݫmyn$ @5G{g@iZRH,*y1_Ԗv}7I)<9IIf)o(s\y@x^askCʼn$8Ж!b6ϼ7B4߂Q!^;,sT\[E;o0| P>qOxB!B!ijF;JiLOWjqqbQ.~tIj#Xzģ D3Y;x=xGRI˄Mΰ8d3 ~BW!ub(ȕwO6ln\"Mxp𪓏_ՌA"I(CbP@N'8u@>̵*NGhd'_vˆ}s%gZb=Aw(9\W >']w:\ A=kE{^ 3^|h\M~:e7 q.Jc:لh .Bar. Z餧'Odxv]/=eL4M`tp]z"oyPg]~~RM]FWξ:̄=yVw8CV=-YPJ#c\J$}oϾ߽7G~S;/[$ږ߫Zu|;SJ!B!B!ė:cqs2~,v8 /mG*؛MX zҌ3H6*|߿GqׅcW;ڄm`c(v:SoSC1/,[]UHbL^3[Y-vE{ YP`ۃ%eTv::bR[&@6"<>؅G<5ޖ'p>O ^fWmSd{Ì"[g> Sv'|6W.A'~D!B!B!W:w8mVU |$C_fh:7_e;;1E KE3$RUH2/1 `gE;$>Zmltp`6{ ][(\{:x}4RrԶܯ:]WF# (U; WC/zwۡ! 31סj)`ɋ Fz—#$pƝXO{e t>McG(~eIϿ|l3 XRw; +hrp;H“i04ӮxHya@{B!B!BQ t*'{l$BwHUa۳a&Qm=̓cjԓ $HJImcW~?]Y0x4ZPQyecLyJg5VrU3?rz%wm&W({-b;M'Ggax|͎!.^XUѪ䘤~9йڀVT솯NsN9K9&>M3ةz \Y7!B!B!ė:w%_-/plQtN*htz+$o.SX]FϑUW 9%ivוa1/1; x͊:'\BVc-0y!![1Jm*@pH0g0Iyj0>?ˬG͜ę=x'͍΃5l2bH.&2`콥q܉sP~rKҚQX[8$g"$CbKeﻱBĭV,w [Py|31jluukm9|'sӄGݕ~uĊvps88Ա@fst8voltQI.OdˍHgǩv()6}s Xks2|6Ht!VZ8ޟ[iV;/BoK:5pf4Est*q2n*HE3FM>q>,  P q 6'v .^kkڅVH 2_^)CCq+fcg'4tڡ 3'pi# ;B!B!B<3$4H/XA9 tuwбKE܌T7/*+ ^;7_[+?i!ss'T-'(3U>dl(I F-v"dZ,mFG z߀rb@rh|۝soLlL.3j9\=U&TnGpyV;jPl&Tr,#LƁ 0Ih)?bvʈ 0|nm.P.#~}Nrg[WMr}tu<5W>vW:MeC Cق>+L:iZy@8c1P\Vꔲp+W46 `9 Ed6pZN "˰:9`5{39zDbt ElwLHF:3˱\qu0h;ĒqlAN0&Уs8pe# \ʹ2/s ޣ rFѣvMnK}RWl,03Q0p6gsj,3'hoZ?_ZUM2`z %?g!B!B!Ď@n)7k)f㮀к,<- 㜿{9WǸrGŁ4g:OWpoH.LRz\x/E/ ,eHA)vʥ`@"* Zb}r VM먓z]ȝZʼn(1(a+DLcuqG5̎F3I˔M15[Ds-65k-XEfΌ2 sϹ5&' T6q?oyS8z> !?yϾ.3ʵŀXurR#\Y HFRhn/X:ipg?:LcSq/7o8|&(V܍Ye[#6 c .)ڨ40*m7h=r@b>Y,K}cE1 "ߐ z?^ȭ:Wެu>s#'7凣?lg pZinS<ˁa=@>°B!B!B/% tyDz)Od2}<͚n0Q4B9bEO!dj|M%3`'|Tv d`o [NVzԶMq}t&v/ȱjA1 0飍uqB4ęqnJ>;̟N}VU{|qn\K<^gI7㻾xkC&U/ل2!e"Hv%C1}'Nk#0,Ppl G.zucɐ/ޞ JMC,Y"40qBۂ'v} Xl tjL,05uK6Δ7Oc~4.+V(9mfLjdlX*at9Y^W']o#[[I0̚.IoU}6K"R Uȧ8 ?aܚ Rb8A+j=c&@˄BD ݕ 0WA'IQeLN*B!B!XEd%~?]Aʞ=(, kHḞY:E :F_LQX*&h(P|BNS \}E y@_8>2x!B"LD(Azl]]KQo-+~4`qYh>stV 0leA>CV[]N36Hcfݜ'#x-9JZ] ?" vէpK~Ӳ4^ɵGQ(UCfr G Po֣ Yz?qErrWJ=Z$ִbkn=8t؜?+3|&Dls. CnI.dj27d<^@|"K"cH6Uլ*[7|r"9I@Y oTWMGo(iB!B!B t4wF=>8m:#1ЂOo䅕S,\1a/q&)&e:{\E(cSzߐ n}7|rGOcEry@'?@㋛47[,B!B!J@`a;m8Sx f'kh(Z@B5b\H^x؎+ۖbLZGi=PH>,UONzWN+"5>YWmHDh)A4T!nNt-kܮlD H9i m`HDn E"_rk \/%m@:W9ŮsmtUp5^{NdAVАYه 9on{s.l,G jA6a3͆sP dGhN_wv^ >aJ3N~mՏfM,6=VzJ ݸn~q_YȒNP$HHB!B!bKv6EJ|-Zt F!Y IDAT:QJ;7û?cgؗ`PLkQ2 }D7{ 6>X/@$()J*Ho\Ɂ"yz*+*Ul%6ZP)(jpcklX͐ >"L㢽.Vፈl8K u!Pz% G̴mRʥ mRͼs  l3TH0*wQ䔮:,"߳VLjSN]WʵSL4ֶ7lG-;QV_dm]7` NrgkzeE"7tպ6t&+8j7C̦ɣ¼Pih(ZI{b= oB!B!+@2:wjh`FS<4@~5W p!&Oø qu1D=Vn^d$!n\d*qH){Ln(Ex[H m~(9bUM.]Y|0WNܽ(pӯ8~+zkV,1gWߴ._~;YqCffN'==y"Ósh<\L.'uI4EdpRƵE~=Lv&>e Rޟ{bC?}Ê8CV=-YP Мv|8U]9j|ur4ƶRjx40s0B!B!B!x69B:|>ߋѹS7h6\֙^J{_%܄iNfbPB!B!B!xvHS| .S\fu k+B!B!B@_Iz>GHb73wHB!B!B!%@_I9|}o!B!B!BM.B!B!B!BJB!B!B!Bg:B!B!B! t !B!B!B!9_~Z?ċG ]fo֢a1ˁe[n,^{onCp!B\;wZ:sqgǸ[[jnisa,oaRۧJpm6 b%R?J']wT 3't`fUw~':,TERY#&\DV |' /y֐N=R`h+z^M2 Ɠ.Zڌ(i kvK ؊JI'*R; \pզ#xBCٵ}~ _/(q5Ӄϱ7t%nR Z[U~u6:M*gPcXk˗M^}J 8UTs9zBX&v^Q#+(Q}"qBA}8ġaxq/8voltQI.OdˍVՀրT {;U]cI; -JtUnE+KRm[ RߌQAO2{qk-׭)m[y4.XsSwAKYs RwׄQ'fal>&9/Sm,!B!B!)Zp8v R.rQހ3 .:V:+\s|z`g~Noʹ݊+hՠ1[hwTg|t{C1a}*j:hzm/9hrHyc$?AiASAA3m>p7:ꁬNjXI|W"=]9VEs5=*izށ:hvo%7%7pA%z{4Y 0¹{eϟQ:bL-sf{QpYFLmxЬv6hZ^>1Zˉ|!\EV;G<ދQeP]9HNǙҿm*e}^*k9ouӡ!>`z|m  .uVa)笠{j4ܐfd8cOG_:C(]Ef'Xt*oM oO MCmE|dkzNSSrKA|cI4u Oc#?vf!ğIW||]֪t(0;`|hՂ54K0<6Zfn -=\O,hSWLMnRC{mb⒟Q ̃y/Mj"Oӄ"Zj0w#Nf<L%3  6Q=K$ Tj`j =* e*i IdCqfo+423*Q*:O8t# Ԧ\/8tP!1gFFkӣ^{pܭ*ΧLDVO1_QE]MW~ӶZ:ڞ40mO<|u-m-hGWFnnUzz[LԵ)fej4I2tjMtو!a9&o͓U-4݆%6?_ۿ^ք*[ mfϸ י}a.*1 Nwٵ}fM՘ɔnIPvŇA[6HXB-J ҫwZz: o`&M4z=x~*U?׶cd(]6I_VfjWx }4C3DtA%E|y_M2\luOMO(<47k$q+ 0L7VpK:_FCyW˻_ qk(į ٍn|'s^)bώ~ ީ,6&ކ/ZxvF]Mff^Ȧ܁TFϾMLtuLQ._u ֜Lh?J޻^~/^Ft}{)~-݉e6/z''ױrգ\&E*lo%Zd<1h)%%@߽@T~x?_;R_»g_/(z7feߺ6v;,x/\O2Vq05沣Ĉ"8.$'M,5Dor}k|o&4_BaNS9V̋=taedm¾y?~1LӃ"l/.ưwעbua2$!oͳeE ck|Bwg] Mƹwwp/ᄋ=U*Q_=d]u@!B!BF90t{42˻*}&cGGAǢU<νcѽ~&v5DϔF+;ڷvx}G$57kQa"ز3$<cu $9=Dl4}vW?[%z+%4ӭ{ Ai|KO,Y#~pe\@޺] fĵzh!G>;NOl@c_=@n?X/Ο͘`!yެKƾGl<+/<6x.3FMƅ$xj\KNB|$Ir[y%8J|hhv=PC\l5eG3w:VF|lhd_ ';ƃqЭ`sνw}q ndp$EMIB!B!%ᷟ; ٹw'1̉`߶d<͵?}yf4vxБ GKD<% &)9/b`+#=afVvC3>{"?GaDgɡA1aX+(ڡeݬ3rdzv5Kka|! r /'c$c˺iyTOc|7t>VZ0<'g84kщYyNMjYZIͣA0_ug#7|]kL064|TW1y_ߘiAW7؂ېM1ƃ6=_k t]y^!uzvkwL zFfG!> N&Ɔf+fBa$M*Te f?;>Iӏ'!wQW,$ XΌ*rlǼ ͺv&{\A㬎!8cg^3\D]J{^f_)ܢAY>=V<-e;wlޙh`En6B!B!CDÍA{zy??" `N60Md;3{|o.~<$c.\B[{HHoX̼8`z㓋[#r9M@6.s:+9ܧ,=a< 1Jx ~߷%#uU/ވ%pQ9xyk߰`+]L˪.Wv2s~4Dbp @_Z¾}ic^㛮y'ƖB!B!X$й(z))ދ97Bc PF3_ 54]Y<( Vl70V?L$WvzZ"LN>u3I<,/oԚo1F@\5AlG$V[ئf3{%ED86,k6m)gt+N,Ssoq7"=fh@C,2AB!B! C\rͅ< ,/OoحyT5蜼j|)1tm<:4\'8[̆;|,d_2G&A??'/,­W>w=s 'An6ܽ=^4䢙o-W_B!B!bIF /,~FfUݬ gu >8ڴ}+b0BT`q-9 >K#wEoٰ.v-38ׯ 3Np IDAT?al 4lۡ2Br),}L`Y<.o1>[م#g >D8cp|;*_eqTl}s⼄ƨCP[P@+SscG.#;`y75g.FͬQ-=Qn)LzYVr.l|evlGw/G867U̟@ Bg܀ؿ~CI l+2:z+-4*ڹ~_ߛկcm)dGOss{wMS:+4{;]迾~v*6UWR%q$,D:o3AXD^"!Qm/dמ|OY&\ PE/پ;i}m7Ή;ܼ={[谶mGS87A~ n5[QEx?F'!1̽y~@r_! ۛ'ܝ/{soxogu5w?md[BUmhm:hޥV֒ͬ?J7ZT!B!B t\Gaz{|8̸f f1z;r'e'6tkxb \!>B|ByWBe>2o˩??~vw^˵|F_ŬThb:?D-P{߄87?7ŅYG# ;Fv`+*&sv<l>nFf_}˿YP^rӯKئf_gn7@ `;=~O2@z[Ǯ;(狶L;x';xxz b 3{k?+>fe0=W!c ;7_:žf^O26{ gIT#5ײ*O= _Nj7=I[n;`VQΫ?CƹO>JTcE"Q/-Iaj=ϟλk!>}!97}<ɞ|(dvQ7PMo |i`{s%'w6aOEl{g;n_޵B!B!{ xpnݻM@gzb ד_!o||_tSIc)c|!Ǎs>W˻ _^%^Of oC15ly}D]|b5lx?3gwN !B!B!BQQhOݺu$s#_O O@okyiHK$g&(ޖ rݻtKSUW4LJB!B!bM@xM ?_v+_y﷓}$h׳7 =X m_ѶڍO=1B\t"le G!B!B!0 t !lbm7<AMmwgS!Xac8{ Vt_'tN!B!B!2 t !bHDVBLCa>WB!B!B,vB!B!B!"[B!B!B!B_6 !B!B!B5:B!B!B! t !B!B!B!;B!B!B!Be×j9Z`#-xʌΥ;47}& nd3 ᠗GN GQiw*Vc٬3r3Cd*kSf`A<&ufUe@1lS/J4=qZ =CGv9uBkH . ,ӿ'Drlm#*bޜ:!F7k:`ŬSiii+Ό_YJhˁY K9^;>9gn4n#‹'B!ċA+Mol,dz yc<SwF73?ޭyNtVy_(+-tDV8k8pt~Fh;DK2d?rbvwspɾ.}J!)s~ ف˩ױXS+t@dq8FȉFnVIC'IDզ밖{#VBtXsX}:Anob:(uVrfB}4/XZUrAK5&{Y.o+5v#׼~<.M_N\N3ζhOCꉢsQ~gԧm o%Sij e1zLD=ANe!a &@7auQq]$Y:VhN*O0k9<"B!ğ& tfvģ7>xz;w_o; a\@ý 'BJ+݋*Ξf潯 >x\UO8I'S"}Fκ\vPnH>߃Ғtizd®il& [IKqҏijB~h_sYpBKY rsZ &sI&hk~r)wz[hns:@u]@CL#\ՔO܌oߔc܏>rg $|v3 ..P w*8WϜtG,]Ƴ.vaqa8|c5N˴WZݏ.ʩFǣ45ANΈ]4+,\6 ~0J+ Ky}-Hs 65RR]<91䨓 è(XjCfMִP9q;>[74Z(Ͻ)M˴x9q4e8ҙ\:tmi<*B!B: t;pb\]Ξ7N7 s: u7+ڬ%C4u⬫f-,Gx!<' ʧV91bU+*v뙮.xY7h'jbC]=8pÎŨgA:ݍ4-V^AW0֤J`Yujn:*]lpέJ,0WNssk-@S4҅g-'JH$*dCDd6z< P+.ܙq"m:RC3Uo g1SQJ 93fϿ:L 8zyvY4s=_ѐ:Vb5oFD <46_rrθ_GXjm<َ ;ZP0cT6sUWتj9*8@4E{S@2r樋; 'Ꙟ^w(/TfS|gDk-FTm =8]RZi:R{9U)'1yj9":p6NsԎy3ģ~NmF,F5D|dLP{u ٝtdd{n־re7 ^<ޙ%oEI q8-(tzuUߡmnPpu5[ͨa| Ac3R{[`C8 -AΔX$%.\V57:'M0ٷ|* #\u & -իs}/B=6,UoAw|$SrS!AW__km\8[NpB8cSYj%f2&" ؝~a'F6qcCR(s1ꁾE lƤbf|w^B~ey}!B!:ׂaN95𸛠 a l]](.'Vҿ<NЩQ/?A(A-0Zk9Gyt PoYdSP^86T3۪T^:|!15Nug0j9h 1q,\w}+J6_{;ZFyI̘Lb7(TWpDC;S,ѫxZZ +f\?cړ/' Dlf̦_KOq]*-z5f{).ζ9Q{>Odf$$ 4e5ت8u4OvPSij&3́Q r)Fh8n'oԃSC-uuT{:j,AgZ zkve=^[Q5D(8k=>áXz[iÁa6 ^7J%+Os v.h>,;/J):8ĥ `Bt F@"OO8tcv0X;cn Rբ ZqB0rG3:eNG^ DɁp C.%)uCHW0:D8B&h>hz=z@Qa{# -<=H2ֆӪ&#֧'iC9P٨?KÕ֋fQs!q=j#']m-vrjZ1 lVj"AX% v.G/b4\prt u筅)y8јjJoQCSI.Q_UT6}jxpn*5b)A[d ܈Ѡ%N"َ׋gĨJodmB!ByIsѩsleS7<"=k|1d,F  0+=R>8^r/R "@q>ncf-$3q>/pUͲKtF(sQ|ā8gjf&uWs1d@XbňF,c0KdTQLoT6DxB-9'VUg-{$gs.ts 5P_AI}O)=k4+fm尿s4ٮ y}g Ht^})\P]sLތAhtfi^5e^˯$ xlg_>l>T&#F Ge4DfO}OQlΌA{ӿk)F3[nӻ]wﷅ^T] IDAT_!B!%UR8Oqb?巟a1\~k< JjvDU?e9 f_jގ~HЗy'J(Pl&LIZvr\9HFQ BfnNOͦH@< cq9R Jj_(YbD`mN?Y:gΩG +\<{aܳ>~m/űY P%bS皅RBA5>oشdW-Sar*RìdL0:pWzeB^ v t7#(V;e7^|(f#O*`hzRx45G!I8th3"1V" %Y.Ofzܞc_W Ga%b}jN'ZMc*]\z.g\?,ND5.N5Nss m3 C`9RRjZ\\Jԏ|#MSj&<&P&;mB}}](vt9W3cBc|QB Z:#jM]duDjp耝TT2Ya0O 멩o f4m=1=֦(3믪4t~W\ٵ`좩JNu& tΊ3eiS)zS-oB35pv Syʩ0W[LY~{!!B!V:Wo?w PsN4#c(r{wR0~n<>sHO%Y~S9bQ,wS@5Ǫ˰2'j9!%失iZ9vl5E|T$5`(+qGEg+ h8]U8jь ,`d3q:#O6RR@|<:(BÕe8SbsF5wS1^Z K%>f7^+9{JQg<&hB U`׃5(ӣ/M4GAQH$C$̨M$@_`( $V XVTa,(!?!D#Ks+XYTz,:8GR CKk*X QIJJSc;QJU,6s)t0M?Micd{ ( 1}*sh0NNtE5zb1:ZؼyƆJkSוI$RfJhp *&+g9UU>uѓYɞz}1>/|sU(^>K9ssQjPz]-kurp1W>sMVSY*kLusp{7~%Uv,dN+&譥T-;/cB!GDÍA{zy??"Tkf;<9UBgG+P=4xSǬg->:>Rs9K9yG*gEbG m!Ng0]xXBJ̇#uG}GGOS[>m{Z~D;8ODQ-#|=۴8!3GQb6 lY:妮Ƌpp_VNiL+4!V-vLNBe'EIh54S(NDBNjGvNdKG$\f3AIËOgňmz]FU mʼnj1.4ϹĀ{k=6Ĉ(cE+N6TނIސ2ʜz._>m䰢0ڰUteX>O EgG1{ kf~,eTh9#j 4s&'B>:BQUABӯ렣h;υ:jG07}iyCB!X=\ ]ÔŜgNfz^̚1]৏ 5Q,'~PgaE,X܁1~O]s7c]#WSGEcrE:Mc\)Ҿ01@K@@B9rgam $׉(X1xX#N!Xµ"xÇ,;ާ>JPłX{-r^D__T44pԅ-ٟ,I,Kq7ɬY/`0UXʏtI7@ : ^c*1d9鋄jΙOk6G\"1Zک~KkdžvGu59Zo2̴˃GasY  Gϡbqѣap:0]Ly{ (FJ0ƯrRلZ#dGP)괡j$qō3O);Tvٜz,6 z5-F 6#|706hhrpFuމנm)Nڑֶp6 ȦP'jЙ/Ysww5;L-fM+9c% JRَc!H!8:ztΣ78;:yյZ4u-+BWSjjj}5x$BJG ܸgR75u*qڝ7hkL~}Wb\ v;(3{iL;o~ !B!n v ]p#laݚ{X2+|z,\Ѭt9]A{lx*PB!B!B!uFy?g9K_xg Fx޹H$B!B!B!$ t%U*}^$|'N˅oU&B!B!B!:/QA!9Џa։B!B!B!:ҼۻеB!B!B!ܲB!B!B!BL:B!B!B!K:B!B!B!K:B!B!B!K:B!ĬrHMήt 1Wrhsq״u$B!B|d.t3W\Ji\lt Z-y[`GKؼ?mprxf!N:˾ o ]O~??>u[6w%by7 VnߔqI!w=?~'\1k9^q^j2){[^Oi2l}1k{'vo?~F1ܧ<4sLفf*s-;=j/ق)[!tAOlUXPIME!6K6JrpCGrSƮDΪj#eTV8[M(B\-=fvh<*`!mb5RUSjBIF z;hnYv.5mqntEMu+m saZh>70rSo{Ntwl7qoиj#P3PQYNQ0_m^"w~u!~ VR2WF )b`2@vvһ}R6 ŀfo>z^\YjBVH x:ha&,cUեYXLdhȏ.v׋&G\yCG<O?b]=p8h?'{/2N'y^" Xw,lT8Ä"QTۭF>"h@>eTSD?/> T[=4YaCa $Q8laa_b_<󄙗FfpB|1omZA丛]͈[Q]!AΥBogGa Ow/b#ޮRXV)~ x4 ZSpf] QU>Fm][#{h?t8-0]Zi(d{W3.9k4Oڪ>H)Q|7ϻ+Z+:2BjȸVoY$3n6GЛPv4=7k]*\IĴt,ֲ*nڼS떈FI+6!߄ٖ@7 6{;ЫFWًqdGo:kU-G. }dLwbn7O#%6ÁΦFqs}K)Mܔz)[Qu}ۭѩ3SN.׋,U-ݍ<{O.:q]5P`">r8m m&H"ҕsۍV`SߟN첛)Kˋ.nm{yvntsڼBٲW ֢Ēb IDAT22ZΚz2Y/g<°—WF !B'..XVD@EpaI$arކU@RC[9'Dӽ;C>zثTMC<:D,dQ3{7߿V.)o^OnGEׇRUmrۤ"A}}:[v9?=JUoRvuwCOAeHU= R-v*sF&u;zSQQdê\K7ъU :^)?DnY젼@ aTU@6Fcoc;e%iHiT5n}[_X/?F`,Q@5ԎUUT 7Ua 4QۤHgެCNLNU@MK/Esp[- }T 1rSn%sNlToơi~@MKc1% t׳{?}QZPt>LGn8 ybL#";p@JxClk:Yg?9]MLbty@ԅ;Е>kEK2X$h6[Ϝg"(|/l|[~ E<ܗEք߃B!B? t.5vXaS> ڱ5|(x\:paqYٱr32t ζR賀c5^eA\[._^"2t30q#䮂#cexC Bȑsu<:~Wzx!2b1] *)ߎ=ׯᄟ@D:!/k`t-+>t( ٱ.~Ox|=]i;9\ӓW]]߱$=i1GQm8Zzr dL' ޻˯pΟ]U`A~ !B$s+QVaȺt?eBs<yɶ+ݏ]*[¸yndL wFLWozNm>Ow#W2 3őW~}}/Uś͕A=Ƒ_G7}V wI3Nf:VcHK㫢yuJw/Gi6PUR˾寠27>+aυ;XJ͎Ud2 CC|3xo:kO/LQ|+IHP_w}Ot|.:T:.p"i$nqP]BSmkwD\ ԅMh)clGXnmd4^Q-heFdB:/^:L==COL73%Ii!ۘNL(wtJ6q֬LEDQPYS4ʼnF]gwſ46ocgWs{kTO;ѩ1O.-ǩD mQ4T|eYX2jO\`^V '8IWY82e.2,EB!@I @?'ԷsO]Ekt8mk?na.fOsU\=yU{-=.KnrV z qӋSuJMZ8rDX,`hGg޿J\;Mbu7y+a9/ʯH_PAM[ߋhT1nIRZxL}tZYUFT YKC'Aѓm -$&u3gtբ @~l^EGH']W f!mN۫]ŵ֛.#i';{IM#UU #Г>NUQhn }NĭMz]h(ɾDxOPa:fLFMKB2=[964w C3r7x8=71t]r}J'+͔Zooi^@gb _$ӏ]6ģfàW(M !B t.}g/#`݄~ͼD XL[bNԗ$S9UF8'snU F2P2#~?:7D2:ZJ9#YSmԀ RuNT)&L8ڙ_v~{9PCFg<UBbVJMTg'$U z EbBdkW%=3H{I"LD-f&zϒڰ(FS7QC!AVS5lwѯ8նNBD)œcʙf DD*7^s4M# ߰Ie1FztE.xFhpq]W ֱ`(6r6t=^0i `umdDv5{L'>k/DUaQM:hl\t&,Epwk#q3lFr dģ__AOq 6>?x/W[zj93'?!B!: 222 3+)J.gxM+cnVۈvp)\k =G瑗x(os}&:ݤCDAD 'fVMj=,$NY|ӶY!8?*5n0s?#;t,Dި~6椦| ZںC*\=ᾉ"}0U vTEr{{Y.e`S;3کf3D"B5z{|Gi~d>yv4rF }[rJWՌ9!2>b* LumRtc"L# )F-b$;wԲ &r[|+)/(=Vs`0ir[/9Y^?!`/ Ȼa*š%B=i,h>7~'EkܬNьEPBc/@@俀Fg[,I~AgN}})` Kv_՚.ܗvq 0fd@1G" 8md$YkC}S.G|o ց2_Y䣷>axv+B! IsX/pi|0ǐC% .~%>`de'0+ذa Lj}gA} ~p{R#p %4v|ϲ5>p*W 6?0߻8i!x'Lԯ幯dүbF3e+Ă^WЋ?ˠ_Z3[- yyZxlʑsަR! <;l{i::6( 7:e5zBĢǵ@۸ub:Ki(IqM`;)AgCO˫B7H8Й;`1 k`[ tvrp' (%&|\4|d/^ߝ@ؒ5yJTۚ])Q aoi{`|Nzi)Лɳ3tlz@hcWD}`|Cgw{Jv$)..Izż߁f>Q0)KQC) &}{-|QbEDqK-.˯"J'M{ AO޳eMr:{9;qHta\@Y = VX)܁- QsΣ|CŠ{6PR@ȭh`OFo D$ˎ-X qz[Ͼ/zJFрeb |t5FjX*L.worEZl/: wY!B!nB*#ϱrgc ~zlf]ch/ln!i3XXɧ}oӿ&T5)FImҳQ:gWm8>=߱ o\K:BC{;5u[!'+{wƿ|=h6bg/䥛LͶ7nN@&?fm-L9=7+3IB:Ȇ^DAn::84>p:q  ݶ.h=ZXElQ(aֶ6z؛jģa|mMv]_lTć/hfs`ۢ@2N4MgGoC쫇r +*QQ&Z['iBw}SkkDOTRZ  B󡉑r[/s2J8"T7(]4$iMS vn9Tƞ҅JRQYFQa)[ ~k#R>QC-qF`GZGuq%;,o؜۔<HD(`3QV^@? tT&[%L:N~À p5]Ō`:*ze?ojOa.~2?g9ʎ\81IqB!GdEHB!B!b$)B!慹(pBҜ|{=,% B!B!Ē"N!B1/BeEilg)'B!B! t !By[o]j!B!Bψe ]!B!B!B!. t !B!B!B! t !B!B!B! t !B!B!B! t !BY+~ً]/+@7[bVvi;BI!B!\ J \̦l JpC}$zKr+?Q)6o6C;+>@E3FH q^ycz>5q \;{Mx?o?ܧ<4sLفf*s-;=j/ق)[!tAOlUXPIME!6K6JrpCGrSƮDΪj#eTV8[M(B\-=H^q)%vr&N'4`fǁVsnR v&V,U58&dٕKM~[<vQSJh{c[\X=z=($!N.j=': ;Tϸ7hD{ue`j(Й ,(ϊhV/;z?T j) #esZs0m;bw~ݘwP^ĞkÖc@׮JZsO_PG˞"Wÿ)bA8j⿽;> _~:ݘ;n0/r0gxB!mo <5i "s ^xw]P4cuOg/ l#> m+ sh~J<ԟz쏑ԯbF3D6gy'Q ط5G5[Y7^>?єYۈU_KIC$mkɖd/]n4ՁYK>}=L]3ΐ3g+%uu^`}f^:AqHt:j.v󌍷_xS_[V9fױqm BKΎ >%_RGKCeF]1@SMhq+ىfh.ڻn!}ں.B0j~>z;u- UTPth?t8-0]Zi(d{W3.9k4Oڪ>H)Q<߷֘qV=M}@xK3yHMzqKIS,jfJk*W5b4.`Hi*#K@n ms~ͦP;e)eh4сYM1>(1߹27hW*ͼD kწ_*E]N!B#ktηs/pvc%|6?չ1"}GC8^T>o YHQ- ,J\I)w"$;˼; IDATq}^ևע]ёR#@-ٜw:Ñzz*>^苣7t٫0r'L\"eUݴy>- )VlfB -Snzl_wW՛,ȎmItz & JH MS*F#>f2D`/ƞP#%6ÁpO90N/Cv>Eir[/sr+: Qx5:ufw5zgo)XES['. sVg_Ma פXU9։b { 0xةOWPGEvVEnζ<;gD:9f7kghpZHe@Gss dfe5gqe2×_ 3 w;kB! 2s1tOGb% $50q#.al\AsIǨQ *f{9vIhFH$>!2dI]"^e)W. <:Q\z9夆3W%o2̇} V\1ZMIϪ_ }ǧMqj Wy1 ɇ-l^_D+ؼi ȅ;CK YXfaʩE(YyۢzՈ^`pV8;:=Nbr`*Ŋ\9tV-=y?㢩|+x~umHn{u zHN:v8ނYx0HtBAI'63:"+ݸbBѹj[8}2YĔM9QPfUL@{=+VQ=FjK] EԘI^Vnkvښ {ޣT>8&5mw_'q7TV`tQՓ ubqm2gar^~g:%L6:ϵtXM@\ŠERhA^#IQkh FUksѼm4&09SVFUsևnOB;'Hl88*hڱ4jz}"*&j4lPћ5brɷZQU Ѵ$]4 ЧIS.7^2FVnZ00 t(f{'ҲWa:jvwQH΃w#b2YF4vH™s!AZSTd9 (nvն363.aa.\D0p?g3N[ek__p6ϭG{?Wft\Ń?|0ю8WgTN!B#4vw}hhO?ܷ"{aݚ,@eHd*ESEo"QHťGb.h)`ky/oډM޸2'g_9 ӯb3Ć~ f'H:賀kVW~ae.'ז6WWH ]hdt8W_>B*82՟q69Cy:G<,/ ˳ud1tT:Rxܹ_YHTU[8hٛ< i%QZ]9_ jw>]EReEt3ESO5~j; Kq,򮳲cW)9qgD8ذ{8Q@QYu}:c@2 kՆ[{SY9z[_˲jdFg3A_rlGXnmdI:ƥQIukϷcenOk8;&dȋZ7E("5rf=>=8y6;VC`w /r393_}ZsyT0#Ϸ+L A~+ؼ ٙ8^:7{޼!t*${zڇ]NoLgJ҈iw[H&Z;\G:U%8IkVQ(,)DK y3<^Ŏ| O"OS'c@%]ZzSڒeYX2jO\`^V '8IW; y/0GxB!bHs$qzgy๧."N y{۞g۷?N:>8b[ıp"ĉJئJ5]~K1{a,];kw♻ ;[Ldiͦv"'AI*##9pl,0c5u9s8<Zo[S4xgFK]:O(C|[˳̘~?7.æ[>R1X? Jpeibq %@_/ gXgl A0%un@|]saSv֊v&&Ƚ^U1hdf|7 DhWJKUp\K!Kb` X 7 q|2KP-dZ W48 *z|mC*1N-~uCTO,MF#@"mhzUEAɖt_17un*޼ gT#I9kL3OXpmskhTL&#F@AUM1 mܭwzU-)}bNRTYE~ȖΚml<ճ聕<"60oN$ylF[]םcB!> t]e!]s`?;ad/DgC!p/6߀bPP0b4ܼM8L [55i "8qPtIe>=G{ݗ}WcI3B3Ui[Ĺ/=!Y$9)f3F̸2~B._7A?3衪~BGxFZnƬ*NtcEU. @ؘȖ * i- KbAB16%i@J jiu¸d:&K]-gZ0&Mܤz,48V,*a=&\XsJHb<[,u'ӎ"'4캺]o6c&_QLr:]'XTcF)UsR^N~t7滑@g<<Ǧf㮮?wGbw_FgǾJO՟ʪ-0G ČMb3DF&[N!BisN /zj0J+#Fl];@o`o?W ͩ__I,<b'n\^6.S ǧ<]P Z# .rs7McUPPP3<É<%g&^guNV֫6G.eǞ-`.^[9]Gky(f 16@ng *^ s>"2 дvzИPQ+mC Efʺc&tzhu)w{Jw:A/Ղ%"4=\6!7LˉV7%r`&H6G|Z8qڛn2VV+0.l},pS.𱵒=GhOܤz,{ GoxJa}kRg׹.hu.gF_wqw()&i' :& "^<=ˀ#7#A| gca뻻L3O\{ш>Ok,Ya4 i"̧Zo2?y?@¢iܿruOB!3Gw-<~_^` ^LGMDW뻜Zv Cj:v}L4z\dm8NJh8@K}=5u7aX_>xE">_:wצl Qo3[Nn\_2FYq6N-Ruxla&L>#SK0Tuhۿrr؞@gn4+)*oB6 !ĜBF|)Ftՙ?]+ϜK3PB!7$)fi3U2i,+:0 /RR=~2S<|ex&=?} y,NmK?}g?H$9}Q/fnq>?xw\7>ϯ])yX9F g,]Y6oQtu.~4H,ǩSg8s:|C}%r>ǼXs U[Ma*3Q( %sB<&k`mgӦbʊ{_n lN5O/)64vQ@q ՌB-`%M֛'?ρb&Mp;m l{j 3mR E:G[O1 ob\)#mڑJ䤟:y!~}-/N97{Lf6w-W_ vw_:uCP/b2 <ڸ@ 2&Sk@p{::38ط;5]N`'U}F*JNs 0qi=jNA 4{0Zӕ[v|/ᄊU^PNyӸa4 Dǖ1岳r;itN\r* QkzF;,{ NI5`TNk[qEpd&sLOm/[$8W[[YJA4ٞ&̺VBSB&s+T,6 IDATpDBaT>aS̛I\ \NpPЀ=2_H` H8-;td6 Àn!K~a5zIB!]IIg#S}s d×|;#=s ][m bFKKn]Lg-./jC'yd!^yow蘛o= *A`[<5Oj/9hm*9^5&r)pm^mzJr0k(@z KN7^_d\58yp/9EElrmkj<,,b1H ;@#>ٛWֆ6jg5 _TwVus%innzFd zZصo h=XEaQ%Rn}=%$)4 ed)~jw0pdA[rH$2j~7T%@u|kSrH~;vKS`\i$h"/HpS!B1%vf5ObOf~` >ަ{Yxsf Wi.wnҖ""'1:Lsd!T_es͏p .5t#SW8~0* 1XVn&ubGŴڦO[8;.`r@o 0 ;rqi=8x LXR6z NKGf3ŠbRS63tzp_dLu{ a;SLdQ^>=69qf@O{(x @NH">rxLGOFn/Qyrw짦ƗxafKvW?<쯩H26n[_}6.BOK HDB\%jiti| {J6aB; Oi'ᰐ]/5v7R_ͷ ri̛11HqsF,ij5$X\g Y#Du,Hm{4 ܝOݝf[Ȫ9_x`)| ?YG_?wJ,!B1dt%'Cכoδa,[pǧxecsOt ͑1 ٥Xۺm3 }Ah`hh"qPณc0A0ݷ=[1y ձda &ªfg*3ªE<|A<,C,AT^}}"FPRPOKfJ\<ċoXԠ0v\E.҉iNӃ$kF"J 02inZ4.ԎgY1Ftۨ'ˬ@K5Zo.ՠ@\C7;ibo$oHǢBg̽5?,=5@j!׮ltOXL֡>b_l&( Q<>*W[AET^/ cQVIyBgK#֜", L0Qb/f5]dEyrZ_17vDx7c۶hﺩf>ueڦxtSnۋ#NSm%'晸omλg⺒KG0ykG⯖] 7;7O$Ye-s}% !BABDzI}^9}ed=cZ$UnKH_½@hZ-0}&br5O'sG?x}чoVGdV-S10hXbv 8Ā4z?Z.)7FA轔O=fKTwOY^@MKIYP{C?uBgs~%NCYͭI܊uC H\/[ =g ?x#f;V}F1YQS0%t(rVTTQKzL (Zklavn o{%Uֺu( sdZP#nt,^(2*i`ʥb V~kC٠FfOoXss H}to|_+(ǰiU5լIt_ϛcb@5Z3Luiʥ(/ v]{(w3Ѭ!H] uvfoU64MF8rȕ/sᡙ27 s qմ0YΧU ͎Z$~5Oau>7vLw&iNpћW5e oę? V Ɵ$ù3_SXd~TF\^/~(_b)ퟍ.B!j K2ya8^t,HBB\iC>58ps i/!Wa.$Usq"c(i+ɷ0,С0̈́I 0$lx>lv88G$ #:,-2m>0-P,W[pe^^ :3b_šx1?S{VD3As+kx+ <-O~q.ee~ `ea}n)@>|ChG*Y[lUk؞GDwm;Ɉzi.>nΪqx]3ŀ PXӌvT iFnN:t08'S'@Z1R&Yn}=oI#/ԍTg19So!ˁZGᘗ_ "@+rԡi,Q C񛭶`w`5񷴎:bx}DsdfhhBN4󻛮9Bԥ~$/MG˙/])>۵c=KWB9]tWv"|1.4[ְv3/v9u ri IwXB@B!$9 Ryusm֫б`xMAK]Br {c8x3:5tu{i9_=o|>o=o ^3U/$vi8:pjVy7xe˜]Y=ccy /o`XV?Vo[̰_BmnRk5&Z 6)^v}xhx9PIu/%y~oŶ茜F|Jf @V}hxAȲekiU.0=HJn+ o&׳~L&K:&oR)͙b ~g "O󙕈{zʻs`)ኄFDKoNuxl&^UI#wܚp0 :=Iӫ/J8t;)~E[<e5f#ڼ5H"2 \d,q  tP z@\1˰4/Y+0upڵyR^ڧ,7y$YS!B\%;Lge >d`xE.Ή5ra s2w7YTJ/LCQ &&-Fd*pU^VUVGԥ~bgj !dLpeibq %@_/ gXgl A0%35c}ݼu=P}+|0C96Q`hhGo2aU)Ѯ{n':sb:4Iy_)-VV!p/FxFP4hCMb@ظX4׼{2KP-dZ W48 *z|mC*1Lavꢢ awS=z\~Z7kQ}U &[n}=D\ߔ׹-d?{F6NPa:&=LO\G|(1<1;޵~~h {gUV>fۧ4O,z`%O?o&ꆇȳ<_A4[\ .r$چ VƮ8 $9}AwA@Ͷ HB!";j 3Н{K$TRA  Mh8*ԨrѢTHaW%/XOYi-2lL{-Z0ȩ ,.4a[`'nR41C%\qv_]YE%!NPmT8weG0$+:"lƈZƯVxv^(f}3w<=T\VH͘U#I0qE@UHcZAErO!Ŕ>]yI,HH;fF$vC7PS^#tTM#[Wl_'`ɰV F„B)t_1NJE7dV ^5u7UQI]s뼚csѷ]]䄦уН]Wfkמ7IN?QY<茇aՕGekyt.xnty;]ڲ O}KܞIlD/RI,\4%83, [L|VzCG\n2iV@f,xyQ K:Mh%Ϥ눝>Ç3WmG=t;O}"MZ.+'<>*B!$yGu~1k7|{jw$25zsFce %d}Mrw米R؊+=J p&LBw9qk,Ya4 i"̧^smvZ9;ⳬXYu89_ p3*}|yN񇵙d=meUcNuvڥdjH%$G/3j~Q{Id9sy NQ z !B|IBj&0%ӧ9weN~GlRѱkXv^w*OmPG.:tTDzx8/wBa9f/801/=.ٸa${ǎC^7|Hx^=MΖ n-phTs}$rM?GF:/㱬<Xz7;f$T̶-G0ر:r2*!ECz\\F; 46ڭ+ hZnj VdO|J 짺ad0U f'>noaРo"7c;W %h‘쫀BQaTl;ɳ+Dxn9ir} kϮe;ams*V2p7~8ZUطO3b8jXEfnDoߝllM\ǧ9nJ֭zx[O!+kݐ;(M[^^|c$ @* I2ԅp s2ŦB!le5 `/m%uJ!B!Bܒ˗igϞdtκ88t?LEBMB!#,8mfFB!B!S$N!BqGX * 䓟уgO32B!B!* t !B;"vRPQ`;͕l+ !B!BEB!мVv3B!B!WDl7@!B!B!B!JB!B!B!ByGB!B!B!ByGB$4wޢnB٢TA%2o;^rSsjoE6 !]2r#/!BY` 3 ^0g_(ba7i^JaKɀWO3^ ['FffbDwGGf)=JՏ+7~MOGIbs@O^~;ĩQE5TuXFmG4tWZ}]S :VNӑBc}lz9lC~~:M\cF9^?Pʶ7Ė~"}O!>]5+~8et\F\a*3QޮǶ;V4#D{ۚ=L 1=MŔecOOC7SWUG[$1>kQ IDATo°ʆd]DRjCcpX(D>V1o|,f P#-u`LIY!N%ZGfBx3FY\<l4ji_ǒ]:|>(jwml=&z6UԱ`.GAԖ8G^ z& ɴb6B4i_>"He#*%ݮ&6af`6Bg67Dn[dm(߉jǚnDhmwZxl~p_9,7oE2)\"g{8gb&cg˲ڲ/ B!_}..-6YkWS O;jSg8A e"V,H3Xbg(Px/eyڇxDf{W~z>'ny`Zfal[8][2 t:4],3Byk VHCðƳy׃Oxd 5m)z^-{Fշoo!r7ֈ0-36><K7؁[BADxӁ>-K&NW9廚^e۝k'l*d>#%Ux9~fb{>ەia@X+#teQ<^ Éq#rYv4T'.W9(1D4e4jT_MTAl&ѱW^xG\n}=$RUObB\޺d]O#YlrȬk%4`iMo'ȅ#$ گw3;Q wd1mvdG". ,5C_׳<1u<Ѩ KV`霡@HBggvX@J+.}s B!@N|?yn5 /tgt,pafلpǿO0ˇ=B1l|ou*cnv;*+ :3}ʖթ{'٧yYş7?ÙM81_8iS{| qع~1K"ߦ:&Ve`37l0c7 a l+8d]?i6=*5Q]9.wYhQ/W=ifqhjn6w$ LTDN7^_d<78yp/9EElrru8 ;*Hfo^1[ڨN6U\I>aۙrl}WQi=-Tڇ74xS)7dsb?5;iuq8h`Hx9XR@$[5\wl "h +ncrMqϣ]̋X"OGx ]l&`t,pHEy*+.9{ZB!KM:'dO*,[kҗ0'I5ZZ>m&.rTDؘUsߦ!p5<6vl2iv!b }85y&DBNQ G ւrnjZ' fʙhnQ ޣ`wdRA5aR'>\iaZ\75mvZ:`4c`_m8fྚɘ&2f<;Jgkghn,rS,7龞zdiYFkt-OM}#/Ždd5x ;_SOelk\ =-54" 1frK+)˵̂ ^)لi vR~ %{8Pk]bCAY"hxԿ_]7"_cA),]~u0:O_ǂMd-N/yOlQ#l~o>3;7ݝf7ᖬjM2@Xe`r|{B!sdt"'IG؆^wD"X縦\Cpww,ZiŦmH l"؉=ɘIY fͭg30WmL혫Su wO.~vf;Xvddb H$`ee@DmGmv?&RDVwVӟOCu="3nhZ'XNNΞq(,[hoffŅ]S!hs1=xnEB6RQ85jFȼ Wil>c]NJov1eipf2D=ƹI0GbdE{j`Zn ڻИ9Mæ  %F7K YQk5B}@_7h"3۝2 E3bTL\r =ԡ-^e|N4 z34M,zpTШh%NJ!YFyU9&=DU2Pd=DT: ^?8ɱL>kQ2o?Z3}o[K5M l=_m=ٌyԏb߁scz.l뽢02rJ7i;Q5&(ԫꧡ0 &[NJK]Df͆0 }R959kYwl UG(98 vOCɾ XusP9KCtO/f]_} 2Na2i/U:no @ݿw|.O&ƕswq30 .3S^g|ŏn֘ },Y7yW)AA6)L6>4'EDݏ}&K U"g>쥄RQ6)NQN|u>~S ^BWkvWXO~S TXy ܵY/wr͇TO&RޏeÁږ2Ste`TSY+G^G:z9 ֏ nT`dBo}+/Y΁ZFhK쬺4: [{eHyyCۛ9j `ajˍbb5a$(@u-|[їPFEl-Bܭarlp.FUWxZXR1cuV=h |oO~-50QiXϑکo0A&vUmRQŤZO>Q|ǼR}\$ݧԥ1Miוk+;UGp7y̮.=X^|ݎ\Zg>,R®:5npU[90dELp'bN j225YzɬUxI\_j\_ ϰ:~p2304KQzz?62 yy,M|jKr^w=K8Jf.''xVgF&|CK6EWJgGAAN"9#t,\wXP8wרj9QΊ1䄇G#ͶEn/Hg+ہ9wfJ"!BX0聞q˞f Z8Ya.4*ۥҢiL:`$hQ`^B^^?c;1gzA`pyi ƤdI:UITɻH,:,<߷g~_urt#{Md?A*~n) c#ߪj^ ))RQ&((Q9/Kղtt jSqga՞X^NwVx#Gʶ7 7P^9:'^FD"QPlX 6\ľ#E~RZ?((V+&CMs'~NuxT^Pț9vSW95u0yD:#z#Q|)͢0%z9AyXli4slI(IgA69Fhqhc5DՏ:|5\ysdkr oRY!@om-H\swґ̲^εsu |o>3+b!ߤOqm botlzy$pZȢy7o<->>~I׍X!1+GtT%L&oټd>fyx(-sE\iݜy=i XF&Gop=h?v|ƙ;0OY{ h`Ş3]DC?r1?s47,W-ŗ^/;fуyf v8Q+#!DSc(iS{-(ۃVC r*{پ}"VQ*8+<=Ax acjqE!aQPqc% w7rKjstg9>lpD#Ѧ<t#'DqrïhvJNrM\STkñ GoBuyZC |qxxJ;&?X(Q G(uD )/D՞'irl&FeN*.D-GR,K;U%W& KBa\ѣ(Yc p?xLt2$ejH%w1bDQ-< $Lt'Hҧҩcn/qzΧ9$R2RYnKVeO|1HmBʼ9lh\$2He zKYAAYL:dm|o~H͡W0[եu6δCVlݱݜoj'lZIs,)fv/LPv6RRn:W0ҁG%Xf$'-wTtb@NOGtq-V4!5φqf/yC627ш깗1Rن:|;vR]9v*ج }xܿ45<E4 6 uޔ޾<+Mei=wU5&YA"J.)zh$JMl} qIxj~MmQUP=DCxc /}"ۤ-g~nJpv?[vΞߜh?1%͚H,[x$b2$ǛTZ;J=E ,#SugodM$ OxMYWi:\tnw*V,KG9m@KYۘHק7Ɲ/a%cJ3d<3b#  f"Y^^-'UQ鑋tr`Iڹfe ~qnT$paz&rpr6[hyl[0RX0znrs!kxa \lŘL@H MwvqC5I0 ٴ_]z &{1~8[E8qs` PY/_(ح!xלSa¤!<jT1@mLdKºރ`*爻Ú k6L4dIZmX>`{rexF^=0ѯo[G}>aδLV3b==_m=dJ+fFѤ0F#6u5 zgq5mz b#aCj׸QTUMf_L2cd)LΏC__zw_̴0#HpfRSYw%{`+gnCgzM&Z{Yy ˟^8D,Z&CgJ]*"xfc:s;nqI )  <|摽>iM&9%Qt5$cs$@`tf1 &ؖ}Mgř[c{ϚYL?s&':nj3XΊQyǏ9;We#>sY ڕXe Kc{mHAAm]:?hF ;gu\5 `+SAw1: PŞ dZԺ ,EiII&ۙO&+lVZ}5 :(,+"3^!b6C 0&T# qfB4Հ/Cv54֦ܣ3b#;{AM488fJ؝vLs=B4'Ψnm|@Kƾ-VK :ԀWyLh?Iw3Ofvc-|;g#{:iF8c1mTCMb zTVd4BQ𻼴l"7JFȳ[ x]==g`fнׅ/\'N Vgc-/^8ƻk"Wϸ#$]^6yp|΋]BBݣtɘ&]J2b(Йn$33!✂  C*ys8??-Rrҿ3u%eoԂR$3vZ,vff?{r>F@B3mѷ{y`cw9;r/ϸpGyR`+TOܥl$~r#c\{黳%V~ PRYeܧryx紏_[wM]K1…pW$l*~xg.Μkѧr%r7N6Rx{/ gަ9, \hveBѐZoqM"f7MA L miR=2~ue%ڽpP1jm*cp;{lEED{C9fM D!7?}M}Ã2(sEVz|JHmMur=5> -8-aonx狿}L{&zV;v=@)rݛd8鉐_]GQ^Lqy x`!;]BUM{(7lw.ͧdg.(AܵXPSkp;طц JN>μ4ůغLkM7e!}+0h>jaȼ|2z&@oeS6l9@pOC1AT aq )h\1%%A[[H_>B5 n^k2Xk7dY2,)יHc  0@c'1bOr}{}x։h!&*?O^˶YI\oO7 u_#7Eis>&sٚ,h\SHeYO?j>W^e9^_HO7w ͑G7ZhYzV h\o'̍ˏ<L IDATa">zYCw޽5Sؔm%ۑà'{Ç14p:q QWujה h]vNx狻})c~cZԼYLEF]쏔RWyaQ+dwLluU4HQq!y5 7=g=2(AAno8!rr%dG~1o<QL+a3h>ZNŬ D)؜[ٚ9jR9@z,ɜG?KC'/iOtV-|w*Wo0w ^dBD\­އ  na7quk6F|ss1׷gt.gomydzKPܦnZ=_A>GOg?eٕMQ( ̘*SF$ _ic%/p/9[#  ̤E zׯŞ~v5s5Bj WESx)p©OESAIfY&"  |z:rB/  4  ca.,,;Hm &c+!ӰgEiJAAAA )  z ,z +-.W7&m_ավnBۜs{xb| |kK!T7ѾjʅO?׿?\cb?ZM#$(|:Q&&6_3GӺhi/gάYq 28U=aB8"}`ɋ@NoSz~]i?0SVřzQz,4b/d{a.6SH8SჇi >$))fICip$Fy#wphh/ȉjBOr5 [2_@&;Yfizhi>Zߨ3ުdkݛ~]'Աi}%[qXM#A8XK` _yzeQRuoR@œcx p=Hű1狿g>Pv wQ; #'wBAxyE[˶b2@8PMUe=̅lzTXPM,LLhա!YQTfaЇSGz.RBQ D~8tߌ'/ϧ%Y߾sO,gѵ˼/*1֚n>MLt:-RSYf.w$V2HyE/r>L\  0)"9w׷Feg&;!?3LnXػ rvҠl2cj N S542O Sd xK%qC-IƸȚ5Y\()lqW’%f^}šg `vr M,Ĝ/cֽ˛͙obO&t˯:Vy.#ithby~?[CI/\/4ߍ t Le璭h4yp(V쎭od{ voB7PsMP}ʶWɫ}ZbX#JNC zim@EF1[Y#'%ox\ Vgi9Vcg9ZcC;1o6TŁYJ},D#ǧAl;s?ր]y8Z`߂s^H N&=_m=DRE"*.YLnaors(50rt}d.!\!u]OdcS{؏/DgrPlo?Lc‡l,,S3,<#d ;n.&rߙz w.5Dbnr \rL’w3o4gh ‚  £%3IgľY&*JF^Z=/\Bo,r7|ߝ㢐Y? GI`б$P覥tF΄n) O&xpy'ӕ/comMgMZZ+2ewp`n_J~v:;WWW7yX` ~Mxj/,~gvo_`A4G5ycTK&`TEI!Vdw&]oNͣ0H}uP->]{UQҨܗOcS;jUߧ[Tm{ҝQ]T WTĆqY.:q8{kk ~YFeS8Jn*fKu#.9mT\7Uyֺ'LmgQiuڇK]Vx/d")jf J{*IAIV> 4*!Ԩkռa}k ql86fD :Fe؎ioub0pj/`J!uN7GItJ$ y}OAA"9 7E3M,^$tI trJ2=)e]yWZky=9} u_OЍ-,Aqo?3X p!ovjo7eUKyy%~y tAKǸ!U9kYo,Z0L& XkmnwgA>V;Y1"R'x0#-HX'$ˠiwT47uMa&LWE/+:a7Aj:j\79Ͷ#l胵L%6:pdB{MM,x fggO>=Z;NP1QY[L~I)E+>0Z T*ʼn1xzɎ&=4i~AP`ܩU!l,&qWQQTOfy҆ޝMC.\`IpMUnN<ӃFhjhdG UI!(}|gL/BV^U}uXL2?gЋknʲ|efVNDzj'>cþ:潰RI7z"t}T oBM0 &MjZȷ#޵]c%~lF,ND\N>;Y-y& lkRInnxdzx5+z[x,"1(n'ZAAx"9Sl|iZOO0;qo*/rP xh{LzOVf}Ơ!ʻ;{G: ź"wutLk4JIF% c d:PRA%,u1v'N.Xi4~CǼIu@bK>Ϸ ˪/LAFӭѷSQZð$e)pf&Jde VhQ4Y4)HF;G,3۝2 E;%= g a܃qѻFAobđY\Iঅg8QTKCGrLz㭫dEe4zFrp+P]jHV`V 9I͌,+7n#ߦfJ"޶k>,G{'zbscz.l˒SIOsQz5yE0ACo+fOCaL8xٱvD'fD$aV Qrqf}| f*cݶ{ͱC,yLV,U,3KM+Fv+tű;^#hIw Zzb%y 74ü5ϰK]wsiO{NGOڗ'XNzi0=C;u73&)D\>wy)XdS7ke8;Owbi$y;ȹ;i2snqĭ}vso0o~}2qR^nR2d] |*[G9u VŠP?mHϤd2I^OL糦pG!5 LTAA|"9#t,\wX:ɌȊB2rKԚGQAD(}Ohս,ʩS.=ǼEK*,1hfq|Ђ ]fìYe_dpJs-A_mczwi2^_zH`Q@ ,{[N?>R]Q@F{Ң]=tJZC4T(PiiZnr>MQy>݃/~LIo0d6a TQ#zCg>z=  &Vv(F+VV0L:#m9Ao]3ֲre%TzbϹг4 rɱMU^j[bYH 'nj!y\x* 3G^RBP]EAу`̧lvW"lP7mdHT~Iok` F7IEU){TT@1)|qOlG ǼR}\$ݧԥ1Miוk+v? f~,/]_>nG.R-^");(rf-v> +6ͣ2u0 y@"2}_OduHzø#t]:{^bҽMb^D3 =mS:I[#sYMSY~LQ)`GqKR=*o%1奣ӓ^$Mx$0$:3qu{EHR EAQ,8Mmyax󮄩 I=AÖTA+y'd]_I~PFC ͮDcj!'&H $Lt'Hҧҩcnxr'Cj"tуJ& !Kט1u"ti/Djb =nұġ>/p kXz[C u)ĵ=|y$%gpհ*#6g_)}Ð:椱MIc.t~]AAI:3y=ges# 4wG--8ntb@\LPF$)%f c{^9U|s!;z Mp5oz y:|K(Z}okQa|V/SWA"@2f.NiH&1hiaεq*_g8YBJ}h KE qLbfH7ܵvyp{O V+(-kS=?44<96e uKW)CnzDt0ݽ^ʣ͵j .4vWaF*&V0a"xsƂ/cI5;'uomܤ͚o' 0DGLt7?7d@7D1אLr t_y7y7xP($op2 IDAT\Q7 VEtv ʥ:P!\Gf3$Et= @ $&!$nla[_mСŞqZ]C7[4Y/,a4)̆HMx*(w.Fu\@[^rؐ=naTUiǀ)dtdDz1{T'̐,?pbr3'9LyyH3eTUlmLϰBkO2K6/p3<nziB.,<ΞQLzۢ=a:)9mA7۶,1d4IN _j5xЉ6>êܶQ~; )7wAA:*ys8??-Rrҿ雃 d~Fcg;4pCcUM*JJdec"<$R `"p1hu,A\Ќ2qjטg҂EL5 ct&嬜͙Oo>`y*@mE\ef^dJXthWqan$%f>ԭsY/wqfυQZ[=zkQޢ·/ `͒7 ) `p>L ۊXr=AweEdCc+PfƄjd{!L74Erη#4pX ܀{T1{vcz)f̑çh 5Chp)p:pf ;SzđhOQKّ]З}[)[9q`quWs1[7c [+~:$狻gxZv$`yE)G,I52u|愴hjSpl'գ:%E ^&rr181=?~W[9K'8'JF&$>@2tu`gR$B&r[a-"=6;sNމo.4/"ضUz,`$n5- KHZAAYD:6>y]iu=K,{w O7ۖ^.['^g-uRO·fqi!~#IӫҀ0[ /Lc)io3Woރfd}sm~}.4Ly!_SX4=!o=wx2-g^IIYoѣ^C=~wOs:~=S7u\Y݁ ~dfwͭüb9?$gQͯer~gVhĶ6}w̪ɅQ!l Ef{ }U_馹%qQ dtpmJ]J&ɔ]=U3oɋ̩=Ieʩq|&''#őGRe[H`@@Iha>Ur?g=T\efg] ';o}rO oЌz'u%x}@.*vb>tn= 駩!uw>|;oio(G<ƥ>Ϸ_c+|Lq5?Ϳ\7Et5]wO\κ]9ʛ̼=,@a _~J O`lt*(,Աb3ܒ5}x;W,=݅cMs.zm<H/5USXݺ+mSa{w+7OR"xޤxר>H\5q$owFtyPsB:Of ,rί|z] @'*HV`bűsxϑD[ *{7[C| 7O>֑׋0dIF+xǧŞd< ѱ{2c N3a-iP__WLIx$ 7^( KxLZ a$܎`p us.BGݧ$7R?DGg$IB밺8S};87gFcb+}l|&=g'7i-yb Apυs׳}3&éLUYo|8gqdFY\bv\\=6b/ &1יe$Two' |(o:fa>{:]o}4{?=M[wu#X xm~뜞[ |7/4<ͷ ÷n~CX_V̹KTk񊋊:*vY`2wsdmo}^2 m`xϿ^\} //1w?/B-x3Ys |;u"v}{O =H׺S1ru^8# Oa!p+MoVhK'xa+6 ,3o/W %r8UZ2PAZL6rP`f.|_}[E=]D{+t8z[aaz/Wś?+qɕΆI  !7t3뉐nA.da'T@/:s Wx@3ΩN~[`B;yTԻpۈ8!oL A,wӁy.][))f4{~my3onN~3#vp&r_.ֳIB O؃ʿfk[TmFl*yٯyl?$ R=[Ssg"""""""""""k>׾\=;obۥJہad Wr!y3t_V2d0/z"cݡJ~BDDDDDDDDDDD"deg.edykk$Q!\ ߅n&דYGI?eeH[$S=QЙBz[Nyƽ[uF#&` KfnFZ v (mLOo|?[D@NZZ'qֹ"30M4#8wSgv-'lA_Wv:X&6cs e˕S}Ӭncu\A/b>CpdAv6wSZqݴ4Ef?;ct`5o#_LR1}Ee6 rsn3XWznr T"3I¾ zO\g%~EDDDDDDDDDDgH,%s";e my]< /̀Bɬs6b>:NttbqoSF"Z~`ЎJpa#ۗZvޞ 26nڻY_AWf|!D[4Ka H7:.< `ÕP=LgFSyA[i6w ׌&z& aamlnh/ިmV\\Ƨ*m巃o(bGu)Nqr*M[({/i>rGM3n6Θe[ϫ2daH'-gGGh[Y]L #h"bSKی*( tdc#Շ?EXXhe.FtFbܵv:ϼXk+&H=M!x'WUf\DUf/LNb0G<ӗ ޱ٬9զCw'?԰w{EgƸ: @YՀ>qk<?bwf9s47M> JJȭLjgrw hqM$^fyd2lȥV=i cxE -%<>j/WO[3m_pkjOޠ/ឩ&> lW֑S84.eTȮ ?het9ڥH k܌52Ao]V 5aC$XY9I[&n-¹Ȣ̪q[cn 2q,>\Yd%5g |McDHgcRN&/@iYsz&gLcy&wg+$ }3½D0l5=2dxil8fyѹxd O7_ƽ!˜:e녴[+|pc?7^t$`uYXBNxڥғ/t*v <\ÜJyRBn`>/_O+^&2k$i.l4ɵ;ofacU=YM$>$bD#̲aKq[Dj7R'͘W1F#.6`m>׈&4tD1YZg]:9N~#i\m^hp FrK&>vb*jQCs>뗸xr"D珛 +2(v}rb,zggG6'ii mt@sȪZܹTd5 z 4d`.dj|j+^e,""""""""""" %X/{zzREgMrlDpn0SDDDDDDDDDDsIAyu:F bQOY^ϿVa)eI"EBNH "#tu+]*R)"k^s]"C """""""""""""X :EDDDDDDDDDDDDdQ)"""""""""""""k蔵m#|37FV皮M|j2וu'R=Y% :eYJ:_9z|G l%os&9yV̄Qy&t hC!xpYY/NǞ C\9'|Gt[+}&HSJMI#"""""""""""8ZVEہadpeI_*($4HM^|Jk,g.{_`xkO=8}&A3gѱ5Y)"""""""""""_pL1K|˥Cm)nQơ#3g:̻0Y`& %t^#[d1I2|,qienJVֻg""""""""""""t1[ߡIJ5Y''V|d c4b2 r4j鶁oՈ) D pIsNϘ˅@|/sHEYֆkvSK7]'xsH,6 b0~ZNgc]I~'|]OIBôop\z,FIB>ڏw]}Encu\A/b>C$o4>gw?QTe5y^/N'Q<Ǯ+JUXw75ϊɊ|fb3 t|dYKVVHNQ;i96djAlܘKdFY )䨬Nڼg&{s;>P͗jgzALבp 5J>waw<"FN[eRV_DI0o7;tJ4;Z7u_OrC7j[M 2uzi?"IQo'4(~o_դ[XDDDDDDDDDDd RЙ*wd9s ou9mhbaLLR 렱͈͎0MG ;6R}(]t%BX0.+6X᱅U JJȭLYtoNW.a5ޓdT4BzFmRϧ`͡j6v9יoSR68+8bmԯ'ní YwQVM{R4qsm5^?^xz=U}|]+&X#615+,%'sy]$9r}raF}IHΪg߻ca0Yq8f2sƒQ0/]zn!`g?=~z&g5ki5`$&@X̎9Qn8rJmp;qM2|~??b"$}|8*_YmLjˠh+4oޫ4]J`¶bCơWwF0e*A V#2x{71 a.H,)`$D`ݬo؟O;Y1̹N2Mn7.ˎ Ws~Z<{.8Y3Egęuv g0Z@hSV3y9e|dUs(AY$`Sow8NkgrW mgr3W͓/ƅ2؟QliĚ٠5:74{evJϡ-pʺVL:dህ ݧhƺmD87ص?{FDDDDDDDDDDDNF݄uC>0RxP;qf3L09IXO3\MU@f1<،i!3̈́ ]DE0N;ֹUFdw/׽M58[ՕC^I[}ŻƋq?s~XGiU 3I>̮p9ٚ2fSzh N?bt$KdpBnm6&v mȡ6dU:zn\:-'Fi8XCg2N3W&v%^jtöu#{6tٰ9 $GZi9=F81y"@ǹ6:߁7o|z'{L:??~zqSM9OE=K_VCؽ\튥z0"""""""""""T)"02?2$1!'C >e(hac}=ŕdfIK8nZ"7CōOds‚uheb<7u z؉sC} T?UH '1>VFw&}Б8tyS7Zl領܁6sm]️_{v:FwS'lD& &=q%΍,/)䨬NڼgyӸbG|vƂɃ7^D.ޯ':t3')ܨ7H#'Ľyq;D 8pFf vC{$m{5^m~ϱ2e~>D";Z7uX2pF|#3Nm~O6(~g^bxS?-!qOxsd޿{0wA,l>w^:!k&܇aZ:zFLo&*΅16>:׶˨;`'2D˙0qW`V)""""""""""rPЙ*wd9s ou9s6nd}wBNI%p6#J7;J4$H,Əv=$6kaC|#X0.+poKxtQREK IB%.v;0 n͡j6v:<|JоȽNLidG]xB4\O, `v}{s%xg/i hqFIXևlMCD9X\#܁q˥oU4.>DDDDDDDDDDDdeR=/&#h"e;F,;8__|mgyo`|B?3NL\0~:jYo/&#$aUkZzYx^434mez؅s̟a7f& uӂ E_31 m89dE}8@0¬.DK= 09燼1-v>ן2؏Ͷk) nޠ"""""""""""Z :Wd @/2b2pq:Լ!_i(f:St>[/<ȓnڟ3λ?/)nJLw LM ΍(ꧻ3ήNfeؼ. \>2Fp4Ai(v}~yd0ك%Vou;H?^Y,%.\m!"""""""""""XB#D1r3L5o C0;h|kM1%!x :6R}( \Z d/bNggYiHFr>wOӜa>o>#%H`HQϽ:b@VVUi`u3%>U5jdfըk9c`2cw0r2:1i_ T<_EiM.'1K *%ڰlXшafj9&g0]M f%ޟӈ54gisz>|pBK+;a|W3b#r֮LV-:7v/G)c8b!~oX'~c; n5;q:E J4Jϡ`NElsV:hS#.[L:X\s(MD޼?z lva<25NF݄uC>0RxP;qfQqId,v %:'Mtr]8 d1<.9otP8Ӣ y5޽46DgnFdз'DwS}ܫ>NI\m HwP'k ii91Jz>qBq2-.u-uX]21Hܜ1F#˴}?-pG{OgIr+:ਅ9!㡽I; po3xdό İ7IQyջ x|I +2} k3k(ŏm@s).7:?Ow|=i#YX@/"""""""""""FAq\5v>Fe J&=)*߂S-FvfLG  7 ,=1fXݓOgNknȕL*4xVEbҋwQub<'\%^B;yTԻpۈ8^ۛ¯T-oMtho~:2[is'W|IcP8a#d=pO9s詬5$=o]nJ+H#Wii/M9ցP(2qځwc3#PIa4ζr:9EDDDDDDDDDD;wꁬTaz6y3>,_.0J'"/..ke]ɣ eۢSʿmݟx^oK*:[ywC ,>=əfIglBN/)7#?|o%\;wkV{LkD"@?}QH R=RE|SDDDDDDDDDDDDD"""""""""""""(5GA9T@dɌ627~c$գY= Խ\%ܯE3n lM@w'T PV<_ٓш;, efͯ~v\ G>O:2tS=l4[=N01y"@ǹ6:߁7; u-F馽}һ2z0'>D #?ζ<\jį'n?Vc6xYy<fN#]I*2=h瘂>׾\=;ob`xRmr_rxnA ?L\_L `"*Gjc(:B+#07o-01d{޽qt\a6`&^;N룉ww4 IDATj"aHҮѬw|3+Gdgk{8 ڦͥ P}/ 6`p~HQOW}իߟ' U*Y@{B>mjB!B!B6 :w#{)[gQtsk-֊+. 9L6:J:I$dk޽un\w,Ę BWJ3$rǮY3*vخ 9L%e-u:x@ j%*mFB!B!B%A#VZ+V/oq8k f\ 0UPB"&Ō굂6OF1b=ee(-ͣpFŭ0,L"\j\، kFˁi=$Cciyf@AO r<+`Z`e2,Ѣ *p:st+n?Z#K _0Ate}OzpyX(2l5Z9:Kia3xZN{p>8'W;6S}rvliӕV}{a : O&A o:|1r_0v퍝G9 {xOVV( ~4͉߈pJ*& ,zPc!Ԍ{+a׬o10sz(G[ ˯2כUˉ7I~xٺZZ)3 :FoD&tvt>i5[y$o>g`| B!B!BBs6{ǗwҺ>Use4䕷t*^a{fEyu?&\aƌq;h13I\uFƴgYVHZ#sW*M>z,7у1vsf1:m_ǿ6Y;}?k';&П$%|+3O37jmq!q']MMC(,SxwBN7=?b%xa8V]5o0Ɋ3ш=Jb stW.g[g6q'[yWAb0x[Ǎֆ9VMGj3-y~1\QQ_mK!cNdEmWi -B>"uTcNf=Dpv}WK0+@+M?I35pfu>Ұ 8E۬Dd(;rIN@?̫kˁ<]Un-B!B!bHй#;F5vڎ|_K%pK(}#Fm^5|ƒ߰}A՗@1&Ҥ0`s)w+EA=d5ho7}8DJs79]OL,.֮fW9ϭf+OHO~YRgjyh>L$o>X:jL*)Chx?@۬S:/̇Gh+@:1'lJ!u >ၥ6|J|]ƾ{w@Ųs3eq&I.O̭e%s&?}'>*T#^>N&9p7)h,Y <.Yj_8ȭhw|zj (ih6PߚJ'4+;'l,djikBrŮB!B!B :w^>yk[ieOBvz mjM/x7@dɑtlG!aZo7*+)Źb0dj$N&f+0FÌ Y2_UQ-GKX`rlCȶm%mv̺Fhz:Я!B 8V\}F#7|]pIB-UoҦω3W*Ql%RXYV7bk*[ﭪF,L֫t:1"Ck%CsT[(j_=V[ : Mi;?vMM,نUSqZ- >\B!B!B5%A綳=D>'~Զ,7o?iLLv\uQ}6O713K4TC:zo[urN2)Aˆ{J=tս #l@Ll0)4wOX3?#>!wѢm?BuBEC͢ e4GhL[8(`}{pVRcYdv?#Ϭy=wRP~_&;dȓ[BvZ'؜-6l<:hY(։>{ ȾgscjhX%J?jC;hǺgHc4ăԵ՜ 7Myd, & 6'T'4_ӁՒ"84s|93cNor?lB!B!Bb 8L( y??1?sJ 3vZinŔ$p{7ŜM- i"9#JZ'46@twR0gw  $fKW9eЦSn=KoE#MNZ-]mt|TƀΉN0*6e9:DCo%FML34+|Sc)+^,Z:977Dm;m?d2|!3|'v$1cs٩i-#rnf썖'-ejCm\ QNN(i?vGSx~Ko*LkDln(\:ULM|O!ݍǶ6 [x^7]C0ZjZS s Ctw8n I5luQ6槯oFj v!Bbob2c %(u&{$uRhqЮ(͕/)AB!B!B񢒠s[-fR@~۷Y g)Ɉ]G8lmY"Yc&3d8FhgCmc@V~*z8 U#iY 7݀^$.̲5d쟇H@s zH TQ(p9f +՘ɑ& N\nVqflҟ |*,s'ߺc:F(< }aI^/^ )0F=ohﬧ5>6^]~<ΠcV) Y]Is)ym׭;S ]l#wP`þ{C} 7um)pX̀ rLs6> !B!B!xGdv (ٿs߽siMЩaߏuIγmE13S 90 7__KiAڍ/g B!B!Bx<0ïoIE[čui\ܢFKw9+rU"+!B!B!BtW^g>VT2\=8QOB!B!B5%ABzF㌞'\&B!B!B!$B|%G`p[9_ŝnB!B!Bb0tB!B!B!b$B!B!B!Bp$B!B!B!Bp$B!B!B!BpL;!ho`⓾Nf9~=On[]/ZZAi|QmmFW=D!B!Bu&Ax& NVϿ$D%/g{OU ?@|ivUql흭9|J|/_15Ҝ#t.gt<:<\Mr5pM7qu0Fn<F/Ty候i!:#~p]G*rS 04#B!B!BHйJ_y e~%f=FI(Ϲm]ٽ6 D; nM?M Jg-Ǐ LKg)OBxW0xkh+I/;N_[@1k)u5fѯ<g c4̍D&H =Utrn_T7`lE |fA!R>g@mcGY^I]|eoB!B!BuIй5Kٺjy9m2 8g4蚱יɆtRRgCI'Ğb l'r]~P}zSAR)tVXKJ ܹ:ɘ +x즘Jr zDb?(sfWo=$cT#T8Q' !B!B!B$A^Zc$JkeƆ6+r.H-y޽27y5 0UPB"&Ō굂6OF1bm!IdP᥃[36:Md)M?=L{/@in!A (_JJ z mpRIۆ'%]V cUɭ&ڻ؝&H/p . kd;az+%4:7rěV6\o~ m z]Ǜu~ih\9 7rJiYEZZeY46S}rvliӕV}{sr߿ 7>w/+ =ZP 98d* s~0dj|GܜxQ?^Cu/OMο[.|-z;&+2OyiiQf|p_C!ݕgԼC|rZu:.>O{ݨDvkPT==Fd"Tt6گ7|/@w5pMO~|x5B<0rΔp76S0]ҰƜڀ[Bx@u =p skAm]oCC !B!B!v :wJi3*}|pG+kK *ub28tRq0=D`asvTe&tnx3I\uFƴm<6Uta"58ŵYp8֙1L]nTSzNa[V3 =ʩT& [3u9Hˋd351c7hXK& gf\dIR6kuj?,s\{nMdw {Y;ٱ0$y./=?,_>'[yWAb0x[ǍŚ:MOأw ^%wW ~#LW5۪x9B7s^m5//WU$*m-q.g ؛h'35H]s5XQ(VBua"aKi|szg)֢<\=hF?7)ԟjΔ%Md*B!B!ׇ;Hc4,\_o'xx;!s 499@(i9¡Z|h\;D:J߈kC)ߟ%e㬃c&jugxkҤ0`s)fRCၕYHsDtVb+r1.^ {vxw9*ui>M&&iXɋLRTvqO2<JrF*J' GIHuK!{<4MkĚ()Edb?X+r o#zؚ'\ M2s{#2p<6)F0qArbD y<:!ڥwH|wuBQqQ䊟u|UD%UMC }ps*S냷tڰ|l-Կڈ-戭Bf+O1:CZq=Yz??㫆&}lw(ж?̈82C\9B!B!$VN:5cXƊ PH͊u1EjjZ[DdqbV\xT:t6՜d0 [5g>cB|b`.7cƊg89,X)wOzghmK՗,zo+y ggXY v%icŽ!2um|r`0VUkRBhhkCo.2B )Kh|-NVTTK$`Z=T2@ ,z[ ~ֆz ʴ(!#>/ys[TnjE>LBlC[tp?- !B!B!vʊ̄Rup|Ͳ-$㝏XKΎ).06*67JZ'4jɍʎOwVUzI7i2RwO-d,U}t6 *|fiD\zie]UZj̥btb~5QLY2 's90 ] sjFd9 WZ"tNWƙ[uL(2M<1t{N(3)Frd2Eņy~-YIVSfVrh,~Ch(Hz:%zON$ E w I+Cj*ޙNͯ6폓AV| _rL38I\vjZˈ80ڟ)rk6ȝn^`0sH);l͡pcUK\gб_`yϬ-g OzpzZ M n"GXi{+{"qFhS8 B9L{Nyiik :o1mm:C돏\jvmoKO:ބɰ 7gȵ_gG @௮1opٵTlŚӣ\St !B!B+Gdv (ٿs߽si7c[[I)GfAcWMpˬG8q|_/Cg+g*=-sJiAڍ/gu&>xUB!B!B!!o}oIE[čumo hq 9דR_?ꆜN ~ 9B!B!B< AxquӝnΠ:pוǑ_&=_Z;!B!B!Bv6[N6g8]5AB!B!B=$B|G`p[9_T8 !B!B!3tB!B!B!b$B!B!B!BpgRZRKo/..1ih34F!B!B!B{AGo/^IUݻvP}y6K!B!B!B{>Ao`J13"i,,䘥6*TUSK#'03Su?k!B!B!vgtY9y_̬ JQ{VaAB m|\p§K_Nc GzR2k3ޟ<#CW4{ʵ]יɆtRRgCI'Ķ\3mdL tu:CɟxX.cEUKS4su]u/y8(d(Av:N㯈q ^jcp3'jq'! f>N-xti5?"k!B!B!vgtrH-,2^irUB@݋o6y裯O٫qćܜ^Zc$JkeC4)IF{\Zz~c8td/C*mc4b2 jTH\_ĤQV(FL<-4ԠQL>Gጊ[٦`=sKˠ PVAjY=68ߩm{ha+ 1|nֈhNIi L^52՝0=|gqRLah|RW-'lĶzsUpoh=x[8wu>F^yӅ~aTs#:3t CX5RF)/-6, 2<(pL ÿsk`X|8qb(ꫧejfh蚡8f+ $C|6crDtZZg*g3w!8,xF6g)fr$Fd"WL5?Ram{kM37 C쇛8bL1o5Ȼ3XZn8F9a9vzzQTf>jN&PLYSnϏu_'PJ07Ϡ%ltm;1?`aϗ|3KB9=O7<&dbLhiAHSM!QSʜdj6W!B!B!AYJmT`|PONXԘ}*P=RQ#ɩ}9C :*|W(,mP/huB#jrܞ)zй8X8Gg/fjt4gV^4񝷚H_xHo- Oryx 'HB32m*0,8x&.7)BxpXNǷ7^los :hCgu.ew8V*QL过Kgrd*FgGnjߠa/$)X;9tpw' /g'F@q/}ӝ* NKi| Owi>:J-YdM N "۸? |4T!hAi5ҫQsQBj-mV$Ytom <|^>{!ax#Ȇ!(m-C>pW!װl2<մE8s$4~Ýv f3|d6|GC΅}:6moBË,z<>*|>_5Hc4,\_o'Xg_[ 'σS;[:J߈kC)ߟ%e㬃c6Nqt n6m[aUJ,M 6lrOJ!uz,sLsDtVbL\sKC {N.<@ 60{MMӰg㞞ex ؍,UO,,̓ -u9 O<|xӓ_,Sm.S}u<=b[ ?`nIqbi:iSB"O0ʰd<|JO9ɏe'~7@`mXjE 9K1;wFlW O&SԽ&z~ք+A[ġw|LǫƸ69m7΄A,YuP?,=Cfri؁꤫y-%h熹>t>'fNu੃JOT 2Tmf*1ncMFN40X͗8񵗒R'1~W03TSk|QyBZ+'κ񵇸:'Lqۃшb*q:!mްC_oNaLC{,U:,LY>g" dolGB!B!gx*WFxhV|3|xx9}˟U[ٻv+=@`feϿ G?uWȳI2.<b{T6i:ڸNgj|HΌ^`r0MVXF/pIn %40Я>f#>Ά|ڮGpkdʡe5%#ĦS`Rɭ(Ň*5ʩvа<4@l, ɑ71?yN|$^\CTޱ6۱`_LձZ*[FEW1r4a&&UQ-GK0,tHDes:LtC[&t#a#Ꮠ6 s~gBN`H'87vtýԙ2)%pMtaG=0r__tPAh 'IaBleR֘v2ZQ{7u`g3|xPaQ2 1W3sDr*n^i L^OpOl*7dΩxN#̮WM\<-AfB!B!/e[͵K^ ߅6*Rn g.Ƒ)ywip\\i>8Eyں~jVuېvu۔Yu(scdGub78UgD&?*|lod6Ka8|EؤZRttv)DoMpRx!,4\xa%;0PvS5b|ٴhݥw!Z`:wha 4'4X#%Řq{fk]7?_dI ?ߏ^ !B!BZA-isUjFarצ<'m1N^c8ef(FhnÖNHnVb͡Mv 6fuf8}앴](#0bPbFBN+6+04VPmOWYna.77bJS{eW? ^e@QXTTq(ɐ~b򽋲]f Mb>+1#J-j#NG#^Oe#8)B`4cA6C"rOHeP0(hf?3?7D'X]v*dGyr\p?z׆g*dfpSB!B!hKU,J!*[-F |͕Ԋ}KӀR6&f IDAT3W?'? ?78&~j%MN29ƎBL$mT'gULhsQ:9эωLcY hUSW\%@`g`j-YGO'AY66;Q8,Z6L8rV4jCRgz~:}8JZ}6C{DU8#!ԄBuW)l m~yL҂([YV/p\9;|JsU۱&ZL\ Ԟ;{%I\'Кȑ(,ؽJjoܷ}0ފ-l'"DPZZ)LygxB/Qә1{*\4JMyB .&UӴ@h(Nb$L@b"u_B!B!_{[蜿Р5>tZ}a{=z5'~X8U4 8Mޑrˌ0f ԖI [՜h4MAyG U5zi,En[:ead@,\34ѷnoN | AMyN9c2_m- !B!B!6gk~&-q/v onCІ`#yRZlm@n?S?7l)u)gMd LjT|''0)ه(}Ջp٥c B[WGt->L<Bmcu%Mo9M-w@fV} MĠق6\Kj$NJ0d- Wcr9[,pPMƮxnmh{x0'(JvV.xP/e:E龱 +a|;YGjph#~?Gw*bL sir/Q"I7`h~{,g-z eW!B!B!Ħ]Ǐfl|ʜS3^%o!31y>9RnRݗ׺fsf333{3er9O:-u9p*1=n{d /IwCKtsnwF,QR[^L}g6qGvI&]5w_3~7|{Y9?)/փB!B!B& m[LsQ)--gPjQԬh4/; ޕU|x.tyaYBڈ9Ze(7%ʚ혓1Pc)|u()ܙf r !B!B!1VV8@ꦆEو 5VLƲm)Nʗ_jNڔbO_yW^<{?yUA"QșF}ɗ>m 6CaMi}J~Oݥ׮n΋)FwbMWa_(#uourd:ܵB!B!Bb1> H$Ohn>8$h+;5Ԩ¶ImqgmE+s&_ nbzhlc B$DjqK0SI) hhSlB3vm@n;):w;ȸ J:hoWԘ%pe؄B!B!B@'@zOtOާی@ j ^ߧ NŲC>|X\˸Wb?s3xp1}ɽ7:e;)B9]J1Ml&YLs"FŊeE2}A4[^¸(}z{)ZESFK=G,0G 47ʘds%S+~M[t>B!B!Bg,Й}ɤς J*g:\j]Ә מ` -NALXRYH |{LokkZLYL@'(vdPd{{m?FIECGJIbϭޙZs͔ߌbߌ|HS!B!B!xg|$>OgQϑ znص5rؿra@0 G`j4b2RTJcR̨h#l캃5;`xP)ϡ J:&.:S!> s=%$qfzǸs]:-orF2zo!?m/h,a0(FnUK?,vjPq\n +y2zHK˥ 9)|Ϻ`d:9 'cJJ 6ӍaCf6}da>xcV\6-c9Z;Eq~ܵ l#^_ΪA˫= dkcYCKJq&a~Er?\t>f^PbPVB!B!B3v )VgbḁIWF,&jL)/Va&xH>㻜<Ʒ; {=z+65ȿ~n};4V>ߢH f 3=aT0FjamsTBHi*N@1&zǸcvPYEuLըY"}rfV7:f]x'3i xLQRW}؍/ jko4#e"ou3u|&G)e/Fbh;RJ0؀e/\O|I#e-*fe) kUMV.ݟN#84`pN'GSՄKF\?hdk[0Wi^#GǸ4 8*8rInG9B!B!B tSOO~Nv\*'&昞]tKIE)ctx8ژ {%mD?ɓğG Z:w/NcbF$0`w)>ohobz(Lp$"pjj=U(ͽz }2?e47fpj%_6S(}|]i|"oDGӀZɖ<}=4ß, 6 ОYRv74sH-Ebp0ߌ=^tf&>|asO[րsBnw8TX0ZkT67 s5i=F(W|x<$|.A S(CUv/]P^!B!B!Xa;͓c.xϯrݸGc߽*Vb 6R8iRۮcYMG2ʚQsE# KSjOq AV'Mߗ̒ɂļ#2qw(_toXXc"3<yIXfI_* FL$`/}k.fP%R`2Sݮfa '615B.Nkoq.!I BwqsgFNГ8H1f -t,ezxS`0=/1%n*F]88zE'쥵1E?3B!B!B$sE| woD#_~#L8+(JR_r[MdR`*U ѫ1 kQ9 d3؂EsZ҅UkHn|$ĝ߅C6Wn5t᏿$ `W >X* Zq{J.%0,哹%+ ao̒r,KCF92I0ZMaejlLlMI#~!L;g:+q&ǔrܻ4L8C. zNwm!tWdq88qw_|E`KS~[>bY0`o*StLrVTO!Zn %!B!B!(YP-Ҡ9HӀb k'14Q.l5xTT+Wbh@\%˷l)Pt~S.JXG]\1>~r`j-OGQps;2W<Rd1%$FOXRuюQ_nM4G  l27!4ڥ݁3[g;-4%g*y:bpWPlNB$MN:+ΨļhડL)nLrJG#mBAm!B!B!$ѹ,v'aҋ՝Rw3=-twlIi1O`OZJrS@:=%JX>D@kn[)1٩ĪkWySLmaGz} ͟$FBr>c dh}g,3(.no;0 g=!tTvTSn'Ih 4]8@7JDCDcE(.;v PJyNԧ1="PQ=edde01Љz2QRYhQ‘e(UaVjwBzcTv5p *Z-o, S(dfRY эQ?ે9&RU\.?:Ag[yv9rJO9b߯/#im?0sS\!B!B!7:wzǙ h)L.Ի(N0i#5[p3_Zo93&2m&586By7 xH'dU+*<d+KEpV_N fS{, .i| s5;<Ԛ ֙"Md}'y3l[DXLǹJZ/4߄(}ՋP~;"Q}Me c6>wjA`ޏں8f!U3ru'Wi7E ѓpC&ch=>?lwE%ʺ8ݱ8~Lj|2t4NƘvC.2?{oG-Lt($gXsl3izP!B!B!Bn K(;LG:xG?=ϴK/F$)B!B!B #NB!B!B!B!B!B!Bl:B!B!B! t !B!B!B!;B!B!B!Bcs}wcB!B!Bl'mE+s&_ >\Nռ3Ou 9=:/{`j}oo εep_m3LhjvACG1ImߞWDY{5nwoda~NmWXbʢgsig|0*h:YZk‚19y%4W|txv_BjZQ*n7K>\964K{ />CA{f6qM_A'RwQW! !B!B%$й,^.eV&Ugn>3fxF5*ʋM0v_|c/,1NRʈqobAN(ڲMZf3$hgijTn_߬4xͣJ·I8"/?_R[ESW6JiR (kOǿM΍zxYiaZ]{ǸS_~sWiB|*[pF%/xqjC3LrSݺS%\]G-wo 3=f]g#K4Zlኞ$Vύ IDATiXDV2oB!B!(@.Z#$g-UqfWPӝ\73ef2QqBIw/Wn&# r6a*%'lt ^+6t7*bb~t4{}3xBAz\ .O}#>;3'W3rF~5k!|Wfa^ *ij}n#hMTRxB]g#Gw;L N!B!BHsnssG _ r tL*bҘ3,)ň)#RN} 3'6k*&-41ZSrλv))ay(i\Ql@`jc)o[p>Z:q3 |0(Kdʜ&HIhs]ha2Fz&Ƹq: ?2UKhaOo8GmSue7+pSjN]m9r s8D{w <ap΢S \"d:9 'cJdI덓tDmTk'u zrw ㌇Wγ1mccXLl:$TS.GY!Ӌ2F;\X|]8'?y (Kw3Aǡsu=g,{ŒzáJ啵 s=%$qfzǸs]_v=pcאcΨY&fӷ1+i{pd #̪̂sV߷M3GuӍg2+Z)O&۵{> (N PCrZL_G51FL% 5/1L%f,Z/N!B!B:w ͆1&^XZ_<Ʒ; {=z+6\WsL͕TZzO ƈB52`ZIoHsj}c^N/Ff(S]k~އx;=tQƯwx\ݑBdV7:f]x 7dFI38Z&хQ2jw71vS%]MwO:NUVaA۵Ý^NLi>>[á) ڽPFf87NYF}o_.^>Lgw0xh:;\}dc cƄ뜗z/ׇ̟ăOy ;'bLuSaʇƹSn 7;l4фAb8Bu55_+iN'CZix=h?&`/ (2{?#rЭr ;\4p sTq1;ps*ʮׅ<\5Diokswg(XM۹5k W o)p()̔yT7c_? lmlc,+,k? 'AZ=M {w~Ԓ1&ާ~Cfndxo"g=TF&ԗA\.R8 Odg𽳅3apgdez !B!B tSOO~Nv\NK[ .NWvAZ}F$0`w)V>o*/݀2[U1C8wT+߈F` 2ǷUsE71p!0=Jb]b4)n0R2#\h|bRfD`PRrDsho.>TmM*y$'40X3 2MإivsFx:YsNrC>|fpyFS!4~ljyjo9ڸXlq`d3nLӶ~&l%@2M + oƫI%ಠawΓD ;NSL?0yR)k>]CE[;6Hbe|ngAu)4p-b< Ph,YuRסa g0pB!B!bq9?ϡ8pc߽?=,Wqw*d&FI1xrzx,k&j&dPVbnГ,R6!2E%ǘJAb;0h%`/e$\k@r2I2L*Kdlb@2]dycMfa =*5Toh6DPEyc tp9pZh}E(!٪RYk^6<.J倇tx4Ÿ zps;[RיY)b(G d0`_2zbiʲrQ f0oO EwU¨0 O_^j;Yh<|5QFkEv3L A2䰢z,O?w1 SDPץ M@.OިtY0Y!LE7fwY?`/‘wQ妲we겏k%-;WKOZ߱m42G&0qΫxǙ_#nSϙr|=(|N!B!B,!Νm18_q|߼#pBj bD!8Fd@nXtTPN2E$WXύSu7-a7/\Frb2=;4uf+g]ϯ%!ٰ@Q-PU˞fB`W%^zr PVLY:]^g:?o 6'k7:z )GזL6"@gvhI///'uBC"^:FIΓF\_2nJ2 >ܛߺT{ TV$ 0>Xϋ#GO_Cz _n<ؽcl%z̜-ю'~)nkqͶB!B!&@W v@b*qV7XPLO =K =(BT$WGW67&3bsug~ְw*?SI𯗞\a1|(flVMdVd/ea([_sN!B!kYc4F6*d&4Ge(I@Mg0v3`W $&L_]2(>eA\;v]6۴C0wf彼_c&iUm/fGD"#j+1fͳD$Jm)d!ʓOja<źh\DPJK?IN?9E *>kp]"QY,tLu}àC}vZ7^Ĺ-m7Gdny\E5Ox 6^1N,z3Sbd죩Epժ8*1^yӽ~FJ\tj'd,ۍ[xgVlqF. r;&P/p)mETsRDYՔ'Mk頛j#6N!B!B,%;bw^m{]fPpC)fZc-01T䩍Q0;32ڜ?D8ۀ/ crfUԽN!CQ(hh6 "Rط Pp#\xjH#\I8w3#is[c6>_Mm=g^iLO+%TwTcFYNNmiƇ6=*=>D #uo7v Yf4P\waPntp}~B~hPSve{aN{3|!BZQFnjծ%v*aM#loD9g[tN1Sױsx_C%&C84t`L5kP}h@3JD 9Ŋڼ5g՛P淐iF+D&#9j)R?[(14Kp,` l/£i14ՖQ\B29}k+8saE.:d heԝQZ!'ЮoB[8\x[ '\Sqx?74'c*.[|U"ܹ@9WuUT\SoKjVo#˧&nP JdO,^'PGMz|4#*?:Fe3GTu֥"Z~uo{'; @ nҿ2O9'9kuDB60a̦'4.]lpzj@2MB \ٻ6/ ,2 >b6i&K%6[ӵ[Yݽ߽ݺicti׭YOiN$viLlq c09<<.]tICo}>_&8tqT9WC,ChfWz^xc;u*'٩,IqsbL5 &g}!F5\<OO+)*$$a{AM ]- m9N|ȯ} tS~=Zl6%JH&x}:袭"A݈4qʼnYiRb$GXp[/= rА)OÞWs -leFr+-h:9YBuŇϾ Syj%XP{gB!B!Ln޼B_!`\YöE,z2jAV>6B MׯyZm4<S!|3R" 'ymc>Ǟh{Igbcl#G[Nw!B!Bqs8x^R^ZtMSrȑƢ wbGmWMߛTFJ K)1|3dSWUվt䫉4^!'i 9B!B!5jANzl64[ qRaN|bB!"Hأ:%ebB!B!BYPNW^MNNΤefŁ8pTL4b) ˛4B!B!B!BJwyt:b@ @0dٲeX,X,TUUa6IӼ[x<B!B!B!7Aի)++\RRBnn. L&l2l6999P(ATzn\S!B!B!BqֵVb͚5>}wDQضmJD"A8ιsՔP(|۷. 0B!B!B!ׁ9 :F#k׮SN100}DP9'>V($I9x p#GxP(l޼B!B!B!:0'k0]]]bZQ*i$o6dgg:yP7|q )+xعiL[GJ;,DuqC[zm|MN#gzfr~S5$\^NGHWYNq<ŷx;? JK&y6J3bGPH]~kYz(oPiHB!B!B!dVAd"??łbAӍS*j֎ϻ ~z_Nǟk?H4%.ψ%ÃOQ~e-l0utJre#k[J)S;L"g\Лʄ&孟vH Ue0̌vAU ^fu7xԶC!?WB^M6{-_,*9:N'7B!B!BYիW?!q\bd2aزe p@`qxx/_j%33^^g _AgQ]<;|>{׳nK ^la:U*j@E j9 UNH^qFq%ZNW!]6lfwJ B݀QCc`qʌɢp.N2{JcG!%e&%po@_V@6lZ86'xL‹i IDATvv2?N3GX{- Ǫyw;9W8/vi"=>Zμ@i[8yvؾy4BJ dO8|e 5ke;:J>5xWuFKmg%[u.>7B!B!B!csҺ6 qqdrlV+xݲe 0}wwxZbvZV\ wPףJ |tOYFzF Y\fzWr:nҞ_kw:.}r+L([Ǣ5p*4rQS,BɫeZxVT=<:ԑWmgCZTO̤VuG4tLMq!v jÉLGfő)!ᖵRk)B!B!BdNh4EgeesxNI& 044t]7'n;/Ss$#$r-Df 'K45.3q$3{Zi5Rc5]rEѮY]%ZCzV'N DbE!*|/@Ol-TPpcW,/g9A{RB7~-yWfR%E޼mI6l cĆM}\X^\>f(5ҽgfӆBq\v1ws#NJj̸^tpOLTZaebЇ +- maY!B!B!`NNZR$NO> , \p\:R*s2% Š ]")(bw+.1d8]K gS֒颻}eml!v"&¹NR?Ӆo,$A332PM7Wi*d&CnJT)Ɵm-r*P6^PJ6J nOCJ̶=A1;$p$QiƑ l&4asj&ߩ$|? 4y#z3ZzrOh{:t0N r;F8-̰k0'/QXf 6m@ #9}R7{Vޖr삦B!B!Bqfqw ~<hɾ>0 |tvvEII 怳mؘGW?xt !B!B!*<{,XVt:fʕ+7 Nsajjjll}4777XTrM7&+\oRn6A.-S$c97ey_>ul!ؖn{0:?g`Y&^bb^vPoBKljpSQCbhyIB~A,@n+x+V>=!(ӓhono cK*j竮m:;UQBq2iU!B!B!zu5]"H$F;66sر)!'@8m@OfL ό|`zMո|aR:-*0A|fZIBfӄN6lFy9ZL溊-Lc3"I?1jݔ7ҠО5AAR+R;G,Gm6 Ҡ^ʤ|bZr7qdkж`('q)P5̴Ut+ e:&ŶB!B!B!9:aMP(tuu{:uvʦMa. Ipfla]6[;87w0N/ro)4kp}4`qPܗt8Upxň*Xǜ|!B!B!t.2-8ୟcƕT߰B Ku2ΠoDota|LN#gzfr*0RNuxӇb(lUO ^|;J(]63a"5r ں :}c.'896ts_`ֆߵA\X{>M/pK֠bԃt5I,wB!B!BsDM4O,AAV!+Tga_ڻX~s^]J S;L"g^,T u?c4X-;)ziωvAU ^fy&b+ Xwm Yҵ^Ydȹ,-p3z -8Ys49c3Gp%p' !B!B!XR$\$ _|O.Xv]J٦maћTՀ:\ F`s7@\BL@)ۈYkVZ|wV :9 lzi0 Sn G:5IS2skJ8` )Wjht ;Lh~NXݴL ?uIn`mYTCc'}~Uw*, )bQ&]>tM8o2[E桓4aVB8{o3Ǜ@>_xq)p|l U%|=p3$^8Jc'TZvn䦪!AIQi"=n<3]ɵʪXmz2G5p򠟨WtAʛmز(qBmNSڗQy~|uiUPږzgvt|^?tOHO/@:c{.l}B!B!BsѨQ_ar0B}&AuT=`&uAx&08Lv-ӝu;D;|mYPZ3yJBCăj|Ne&MAG2R2Qz%3ɯ-L S<#CnZbT/Y]g4获@g-AWj#ev 9mwVR]%꧵>LBVi̋{I+Rs h6rZFh}  ե/#=M!Ǧ, og cBNTRRhlՖQ;=LiSto)J"$Z8w^ɵ5kN+!;7v+"* N0E<֯/c]&m^Z",& <Uy؉)p~!UA-̍8)1z!83ř =<:B!B!B!#AK1=CcGH e%k*t]vɥywqzأ$3x%1`S֒颻}eml 3d<˕W8lT41J):cII0D3b-B}o `nwlb#d'vXƆGX8ibcY2 L \@ +F8Uwa-6ςUؙ*d&CnJT)ƗGCDjsCg˅ϊ><>(UB mV֣᳜;0RE~>pktڰ4H7* ٱ% 3N%C}QTm I0'?k=!p{T:M N mO14صhmB!B!B1 :pm‚ڪ轛R/N{sܛIʊhq|SkU6aχ!Z2#xU(IXwb]c6߅G2=-3;3Ht0aa+ց7t^ C]N&}>LLIY [^B4_*f5QX4ӄu* s2:p8 &}JFQN/(К5„&޽2АAN>{3U;͆ AQ .CH 6-I$ӽRW{_ !B!B!K@wSM:ȥeB+Ph{?]#EǶp&܃9S-K̂ M?:J͈1C1 [A}x}hԐJ\LT"nBuuY-Gkw-sdGcMgilxpyɳC A* BjjgW5|E 9l\Qڟn;^(g隦lzhӴ;NůN/eB!B!Bi]qufH$ *BU7m}xݏЮ/$Bn/Ce"b>H E/ SYlŢNp]$j4Г&b+Ft;7j% '˭ RI^:i0U~QD P0ZsRwGh0 N0ו%]N/+. pٖ KVݪh!N\"Yܗ baKlԒ 7 yb<Lsqp03cLb*^Z!B!B!G=S'3K D.ŷ Jj- 5uI$z|tO¸u$[lb~.j2i)7@ я%8 JG6ۜB |(XQ0 b^bB!B!Bq=a@80lFeCez >c $xM` 0i4gU,+e | XX-Ivw2} [rrmJa'CD`-Q ӍlyDKwL1ψ،L޷z,p9{Ƴ~@j;VB x s_ !B!B!{ ̶.vFuG⨳,ws𭎱"2wq{a`6•Xqлوɘߓ294;ó yҾt h}EKEB7'{c*˰ӄ\ᩡEqq21!õmPk8w!I$؅vio/u?yN:\nZCi7b"M},H塳4<.; o+IGs.:js|_ !B!B!{ݼy=qe 6aTI [ytm,Ȗ%8lY3q}nZOv[xV}0 O^S뗲bcl#G[ m2±'_7!B!B!XTK*umwvv( ,QNju߮u[]TFJ K)ޓN#x_gnͮ!$ov [sx,ӫyelZt! ^mƢ{:+vs/Z /̦-YqZgʳ2B W6rbI!B!סbnTiL\d_A]@xriu}Qwӡ>^7CHҼTt=R~xxT`M*GWbs pgq_g޿Ol[ ~qbsZ+ϰ54i߾OޗþOq[86U '퍲-5k3ӷU*nS[Ѩ_$vo*G&[a_u o|z5O K]fG&O~<=8m'cۿ97ǭRw{gym/mた,?Tʚק(IS_tV*}BAb3#B!B%AW7GϹxK7,ڱ+N̦ov8Yj¢W;`ok/?\ӒWʻ!ۮ9T |F)G>|'/mܼ2h"́AN<J+A1֯NwVg8Jn~3:,>>|'ش0۟ny|*rutsw\N=v:S^ϫ9ks| J"4=ZCVn鉦ykc܌Ô*OMS|󠇯tpۗ|7 So*;'阧E;d{Q}7:WVź:9/XJ}έw: HV5?l'B!B1{lZ|Pr V_Yn-5Ig=6k;Tʻ?*4#؍jWh`p!|mg>p?E;voYj'gۢaG5pR$y\=5Zcg}uV D$fOK/J RhOZjnԐH{"іlʲut]Uv7>eZJ;Dlm_N9>\F5#_ mL>9^3|7}|>Zi%ߤ!s7J2_N2-##t yJB}ߓ.ͧJ>>[vrŬ>VdN~~w 1O7BP7&֙[>>uecT'o gF% Y6Z; *9pO~˭i &ikV3׆1yM 5cc=Y:)V-g}~cw[H!B!¬_wmHIlO4LZdNµǠpwzp4$gC~J<ԓXJDžc+6q۟!nIL`Dgra5`W73g@ ܳNJ\Os3$KVliL'[gdzpnE)9f8\$'@Ʌu6 R w` Gb|Uʺ<w`&3]E]ljsqᶬb݁Kw>;Q‚] w)Qװ+X8*j&kwjOk!oKL \x``y^cR.ǙuT~n濊ʁ¹7T xIL ;S+@V-di䢖K{TKuYvnohj noKѢ Ἵ?)|8QH=qȄ29OX;[ztgyfˣCֺ=Jنk?Tڸ0f53|ØvwSW2keT+j~ͭOxAi4~cLgi p}Dˉn*\jT9aLF''5q[@ɍΑ..J̊ci'~ o5!nuぁS9*kanJԓ},ձhuesÃߟogKo Jhf,4<~  u@VeiGϭjBl B!B+O@VP0Ȕ^JBf%#7n w]URm` vOekI Δ1ot屭5n,ؙd `864Sצ]~[RA5w_˪ۻZv"i:z%ZF37ݟϹK?I]OZI3y9]8;75[\#q_}]nNJR/>ڶܛƻb8pOޟφ%M*;GG 7:}TU )Ζ;Ѕc\BᬤRzO3HŽ{s]JJQ5FDh_; P7ؠ["C_g]VW@ώ[-=ynx0Of|{qɫus̷^ {?7WkIkw8;Q3?'9" yu$^4Wd]&7wDs4&pvW+ tT{5Rk-ez4 ZU,MP D[tz.k_iR>pߵ\iP蠠3b`V5{[s FжSs1M,9z܈`^i&A 涤ں8qk%BѡFmNyXxYg*?*ڙp9m23e o04X3ÂpwdGg0̱økc9Tk4q% 4zn]B@^*ْdž=&nINO+ɡAs^Ȍ5Sצo+Oi5Mχjn<7.΋Z''UJ^‹s.W՛Ǯ\MoUp7*(d+;jgjoWEɒz#,ޛ}|su|۴}Gׅ,O.FobT̅Ɂ6? GD{P7ZTuSyܖmK7qgX8!8_QM)]s}Tg}_ёBV׺07VBNk)b5<~`x& [B!B!푐!LNjqia6<-GjdIU2a|lQ utS3#zXbŖNm:^q8_W%_͌[KRA5&9Ѝݼћ,IٛQiSUkw/0٥c`@mJrBccc݉j-}\.YUZͫe91A&qmcYSAT=(kS!_M̯9,LG\Tt^Y &02ê=%W32*ɭߺՓU9)Ux4 W'^ix:XԼ4:ʮUhO9'wfsg-ChΆ;cfy6%jޛLԺVob28_/-|-j;~DIN}}؝^d1J:"-T_ $:?+~Å wgXy ܚ{X7#wgJKPϏ&M}m63֧l]5ۢxp;k?Y\ܝX21'OT//B=xlp 7>{^]g9zƹ" ywrēRTíkRHcͧKY?##/9:@FP1_YdRSk`ި8s"_IRMhtF Ց YDq4{ _hb5/x#Fo<{8RkؑKmZpw{ Y;6o#Ϗ4,MP3,ʃI=V"`Nc.oGG pU1>ҦDݭ5j}GObF_ɷ7|E-;ͲI|2+F'Ktn !B! v`Ǘvx([L&|\ưI4:%#T9Pѳ?SʁZ,Ob(6όXgNyv#KaYSa0|GG/|-10/W_ggr\Zlۤ2n]ù9n/yNKn`;]^VO$Ųϝ/ә?&\@#9嵼y^ck"TJ̒Nײ4A/96֓ÛI4T[]`;֝[Q˘ϰpB8w`jJ,H!#Y>9Z#.llmۣc IDATxu|U׍% `Gm.QBişb" 4PB@I8?6Xx?8;g9&.s4Brz׶X-E[S_}ȇ@" ã\M+Zbb1'$""""""""""<ÉfBhĮwu~[vNljtp\_Vmhn XQBb 0 ;ObZ&1!AZ3셝\'RUeÉwV4sYDDDDDDDDD$$$ڈ'!!#jpus;.7t I3 YDDDD#<"jժRT bcc(v;˔jp0Gxq|wi|,"]$\jCDDD@lm|Ng3sEY[DDDVb{J7,,~ɩY,jMQ<` $YQ.n@xݸ5bC0ݸKOT3juU Y\p=5ort{mG& g6vKtx>s@fcI| +""""77W+gΜt8YLIO8{ ™۽{OU㭋0(Δ/7#,U7ܰ㇒$ """"r33ņ3߇nًMYɁo1=ۮKGV\HJ#KY[DDD`11-[檭,Ҭgp L&XL)3׼yƊjh+\p@1_ *A+_]gq+Uda`ڹa6_@NlLq6&\aӍ1QcV'q &ر; ٌ oO+fNTx,_/lo1(xzQÌ=.X&W]0tDGP\YM;9#fHN:W"i?9G*G[ϸ"s- %tjdL<<8bRf\jw ww|3X00UK+]~7n)X=wOKDDDDn&xzs)/Ҳ"gA"L[G<Ow>>ܹTdN^Rx,|hOq0^vU`V=U|b6bd$x{{%+q81LxqdJz"?yxz2¯qk<~Bsžw6%xkvūUSj!""""7-#1;_,^EDDDD$ל'6É4lj1bbd&{=S RkҴF/H1M٭^.諃"""""""""""3 4YDDDDDDDDDDOQYDDDDDDDDDDr$p8 ;ϟe6sa' """"""""""7&EDDDDDDDDD$GT`ɿq5S1}m#k:O"""""""""CyX`e˭ l&g^Ñm^6yȍ-cMnҔ.4]15[MdgCm[6hEpJTjx;fo3峙OE5mJw(\5uolNYΈͼQ}i]e*ԡ #Sl]y׎J^9v9~aݩTҕjilOͪKynԯHj-cDeeոجS":`Piu., v;g\:>w_32O}||hs]Km$qO˫K/X` 3ѩHtiVx `"ѩ%^sw6}>Na#d-5$$$1Re Y~t)%xu-3-qB;Ǫ'a|:O"""""""""rC~TɿТ]'|7KBʗzQ4 &j[#^m=Ш*c斲 ]6jY&>ۗ̚t(ƶF;\#kb`ҍ鎖#Ly J&n}^OɗZ;p6?ZϴShlԉ.vԌ ԒxvB9"""""""""r AVtHlhMmٳ?2AӜs.߻35) l6+:15i=jHC+h=6HK.qEץS̛ܓ6K _߾=I<}, JdaCtTk 5}s_`6Rn=xfwEðPwYCe8k6L8 Ιx~w(}+Df:v6xrp.Osw?Ͻ%Y6)oUx곯y'fj\n[2GJb3JYg.{sр|01?QI#ӝ$8v}2{Wӛ """""""""r+2?]pN9cv϶LǵVAi """"""""""#j!""""""""""9""""""""""#gv""""""""""r2%ǨEdiv""""""""""rRf+CdP}YDDDDDDDDDDr@-2DDDDDDDDDD$GT`ɓůL\TX:dE}}sdC=uߛ5#OrqE^Ȭse&,,?vfڵl޼ UVUtҙƍoyDDDp6o/VsABCΗU֥3-[6'8J̮rFA\롡< |zwޞ_o;#Ǐ`o׷7#! \c>}9| rk.q͕?w8 s;}  o0 P_=###z}|?G6mC,~Qi^O`Ϟ?ٳOw#?Hquuͳ?&Os>&111K_'0LBiqe!S$22@ђ1t,ZUbbc6YM吘Ȣ_1hp,SN+.t:9y/XȠ#X7Y.Iųj3WٳR~?s,?"2vf'{& `SF3 5gDDDDDDDDHO (M FN'Z6qQ.cX20@JhۦfMKll,[nc?~ï@j$sbb"z3v0 ǰpQ^x~d.}=])+ $Dz{*g"11Ν:ү/ǟH5d0 >tڵܹs,Z5g߻CDDDDDDDd؃ykj-1tdÆ d{?!i$~rjqqL1}_˸ilظ3gtV^ï6aK-""}ޙ>MѵK<~}z3烏RF1@)smlא {0j^s#[1~m$۽2l &Lz+edhNQf11ZhiP^빸Ҧu+&O /fw}ڵjnr\\nִ ۶\ (^vL4\{-| +V<_߰׷C lא [dSVgsrLdht߬Rb:8F2ðxY3_s~d'|H漰k~NP`e^JJqZ5lk#1~Gn]d+>c+q[Lж7+Ww>>&Ow]<2/evfMкU4ˉvmиQ<*c1i8&M۹>g"""""""rȰR[ -1t~K%HJ.>~ߕtoHӻ7ztkc^!\9חm!!9sks|/b\+=dG-Z֭ZPJ&3bo[O+뛧9ٸi]TVɔʎΙH~Ȱs-.Mi&宽V Fȫ<)=}fA䌎I=t@|}}ue֔^wގjFLpuu=l޲5['琓5o֔I^*.?q3O9Ö[iܨAcgO]YVrSdt-gҴIc/d",,!;xjp"".?&9/C?EZ #r:-Z9gO?3"O>(ݺv!JŊqCGXb޻~[O-?+}?[9CnZn\侬I~=.jQqDċve8ݙ>muIrm/<^|.ơC $/i6-kw).nق3oҤQ#ڴjf^hh(SNG^Un>R\nѼ9όFgr_DDDDDDDFw[H;-7S?,o[ \ATTx<ݳ#3@Ol K0f͚2iBRujsBvN͙4al˯1WgX>|6rtpfrSr|VJŋg>ϯ8Ut:b IDAT9u4_}-~yZmߑ2=mdUТu{6oٖ""""""""7 {0mNgEtW5qÃnL=CR!l.s_z5tB6y5kdl1Kq cYtsʖ ?#>+e{>~q@F_n1*ۼRvȔeztȝf3I9f'M? tL*~s5wA˖-'&Ίlv+ȳ8Y'?d7H{ë:H-h9d\ofhތ!I?--Z`Xn+R~oۦMsSim^z^y* `!)rA^s5%K 4z9/S^+%3dPΊ̙Ý=`֭\nC B8AɄ-7t\|<qڵ.sK=oe% '7hԱC{Xn=ܙw6 @Ա}sq9qdIm1Ʊr*___ x՞q{.?̳|O0d@Ҽ-ؑɚ6oI3ˋbO>uJeV֯Ozp+O㋈\2zrVG.'.v[n^<5GľQ^8ԯG;_s7RVkw~Ǝȑ#GTΡKNluS |N>gs?"\Km1?]7t9\7\.yz29*<-lD JuoזyD Az3iܸ~ŋFH XyХS<344_,fuB&M|;""""""""ף ̹i!ŋ9nQ+#=ȶ!:tnѯ0敗hؠVkKpΝ1v~ۜ2=1v<ߝN,вy3ʔ.MF!!8,Rql24o4y\Kv@MXx8W>1""ÞU);t0mޒH찰|m)+=n/_%"""""""r^e`f z'+9z}{,:2h Ob5nԐ/p8&<q=hڸE!&&!;Y~#It~FF< E*UС ~i+~Yɶm!g)lCvT ̳ˏSu$[b ,+x5?XIhhRNZ)97.uxɔarnjٍ~^ 6J\\3MK^1+Qr߼np֨MTk.풥L/#k&)~d_&m0q8j֨/?pquK7}о-ڶra[f͜F>qia84{zzRv-,K8aM01ۮW.# =ÇR^\ǒΜ9u ...cZӽ™3gt% R]:ӼYSf؞A Vժ4oތeEW)*V[dzhW,X%ϟdɒ!rBL0/,3<^DDDDDDD$wEz䄹 """"""""""#*0Hu텝܀L15YDDDDDDDDDD͚_9 HQYDDDDDDDDDDrDfEDDDDDDDDD$GT`QYDDDDDDDDDDrDfEDDDDDDDDD$G\=JTTB ;>V.TDDDDDDDDD)iuQf 7+Tn lI""""""""r3?m?q ;)@^^^x܅tԵӹ[? H\\Yt?[::!161ad*씤 8_R[ cSB)UH}-8xWWÓOР ;Gv/Cͥ39e&5L;LWW+Jy7p{oEK91qGжprtp}",L-;Rڜ}:v U8I|h%fL~Zofʗ9nX+3|حX~/o7>6p |j;6D^ 1Q>ЋRhBǏĝ;<3vy ;il%maս%ԕy,_#YyKR}{.v[9{Vijz{8ݫz>hظM[>ÙHGmA,!;S,N1i&.۰==ϤcqW?Tď`ӌl׈ʕҀv'ǘzôLz2 D]G|,Md;(`4lN-ͽ]ZRje썜JFo^uX2Ar̝Ϥ?T̀ӵkO >BBhT 僮W<Ү!U*%vOPVnQ5*VN0)S0yU^4k40D\Ӗb&;-Z9*MngԵH9(N[viEjATJ<9c(c\/>H5TGe?-> w]:vGe⋷S&}mQR63KNtdmԨtK:TTL@`mv3QlV#8 },""""""'cȢX` 'dF^73'z(Gxu_ޥ0'{|x&v<7kFɹ?VTF͜Dv qֹgCbV"̈́e`:0EWզ4.W, 3ƿ㖊l/ fdh..O3F2|F ◌a'ggc 0V"Zj` ;GΨjX,g'Ũ׻$&?.tei/K&ư'\)%(R.nQlf 7Cu+߅vO9K/Q˲N`(3~W;w%7DzfPm&иx"gP/*nó`,(Y~Rc)L/0;NrH, w:qg/&5 ޔ_G}}B hE3}}rk@KCO5unEVmG jRF!`|4=^;=Iunݒ9,ݡGӿj2HKyF/ޑ+ I;Gh] ZZ]gʪYè$K+D8<xoNf]{ *xv؋QMڕk-;vOYS;SprqlFON7E H0Dƌir~Od,} xU oM豽|}6Q!]ެ  ;/wf;ġ &45w„[`Ki.ߧ3s{?]1m$Ch LM>Uhձm{MyKY2Ç-Ѭ&j0ˑs)~V>uݯmv^@K[d*+uz{(b?;(}Z1۶hU/M_bկy*\~X#i-D8vθxihZ'Kqߝ?f;ORmEmY|3 O$-jBAnۉ};1{[OԴ}uQԡ/sپ=C^z>]ӭVsz漿O|'/4ݯwj?sуĈKקCTr:u/f0Wjի|-0 NlJ<ָ#~AşN@W |<:d@JA7q!jF\;wwQk%b$Ɠ& h=7}:2?-[?1H௽^5.] - #s!*UqP`?=Tl۞ GY{07L<1v>]k0]썿r22e?¸J| N2x)œkظ35H.X7!Y?qgzQ#öu (S:ϘJ d.ֽ|!Ɣ'ٴGyQ3.tM+/v~]w.(X]pb8؝&,̿\l.{;Vʲ7uٻ?sf7zt4J)H*t0I AAJ@B~ 0JAJ$T{ cظO=v=9{q>{NߋdO O*4׿s6wًH IDATΕ<9a:6{ؚrȡ_ >F^߬8=Hu=);knxõWG&NޭX87$OJg&5MroǗ7r߹&H^ 7Y40윷sVb튩|םkjLh3.!ۿ'el~B2╞LN?Hߙwqn?1PB |✆sfN[wSr=V>Gzv07 &*d7w4v˖|Ǩz<V۟R\_׉c^İn3VqIiQ)ygu-`T"\׬aÆ ,>܊Vn\ Kv;`Dq}9 qg4L)W1܍:XYDDDDDDFB$ },f!O6' e&KC4kE@hn~Ԅ9Ȟ9R8=546^NJ_ u#{Lpg|$,Ri%kqlg%{O 3}~1MPt6#rJohFd7v݈m7YNvt wm%oV-+aInϘfβ?qa &,X]mSh^"y$'[Μ[>p~d=O+24w]L~D>ԩ'2ywXH'){v[; yHcwooڏs09v2+Kx M}rj7F0#!ej#љŌ瓯g`^9וyɞ-Y׋ꓼ5nppu[FU<:>wS6tԖ͜z37%'?OI2ONBGI/u+K 6'HWھueN^9po ݶ; w7~ę,""""""R4i4ҋ5vᳺےw+#MnΙm1xZ3ܠpxUnDDz Ϛ);YU7 z.DȘX$m?T˞a92+f}L[(d(>]iԖ~ݸپ9q\P~ $Iw=y$?$3>a|@Kj̸hL.mǏS/C",?wqJb5-gHF-~ʕ{NxBR疉SI+g205%ҋe?(UL?=jpO_ۯ;'GR盋ܳ#Q䫝*Ust 2vPK&^%^ܴPsR/tHpM/+>x]OHFEsh9ehq_V.DP$Bqg4PrxS5ՊT)I;Ż|Ѱ&G[z$v˙m`!ZЩB>!{@ɤW:m8]aٷmHݱOW1t*˩ L_Q+?n]LM|Ev;=7_v=Tu'ץ4 Kvn9]m=yߍeO5$peOiE=5Ո/P]N=CV6-䗃0XJz9[#4ꥱyȹ{|XjTqCҪFN3tN978 ٞ0E$>70pW|82.i}h1bm/Mp&sfؐNFid0F?qY0|qG>+֐\24x8Κ:瑖V7m#QZ-US$-Ӌ{{E-z;BZ鵤"[N "}R1,C&Ьt_ G^G L̥<w-$NBJiFKZLe`&|֒nƷ2潬ᗎe?30,nx/ 7" 7NK)nOGڣ׫: 9S0E@q}0whԬ;nil BHv9߹]')|k۝&CRxX~9g%rז3{L_'轻L@鑾v)ϢI̟IQhW6JʒySfZOL ·8au+Ēv跜p -1ist@93k >GNϸE}JEF3m/A_oKdpIjC&;NA1CSRh*%ty,""""""cY?/6۫jS<b{ǖCZ[:u=G\ E L:erT_YeIXVvމg1OVU|΀%2fs&k$^گ-1~kOԲ(%$;NƲpEnxty-%m sLyGV@DDDDDD$2c*, 5kSB0ش)s9:XPNG!r 2 zc/Z`T{U$FJڋț]iqp""""""""""RYDDDDDDDDDDE f%EDDDDDDDDD$Z`hQYDDDDDDDDDDE f%EDDDDDDDDD$Z`hQYDDDDDDDDDD):3Y&y+::hVko$Cd˖ёȫs>nQyI7]ut"""""ٱc'eWnn4Mnf4&&D;i`#LT."oqCDDDDDD05Φib1 GҢ`6@7E!""""""o`FqEms>nQy%j0dk܋[M?m'W2fw@9Л^"""""""QQ 7jωMsvd _OZ<Û_""""""=ki_9:X'vƩ;]߅ 9>X1hxƉqH"~#AO|qjCqo^׎.{[[(5 {/%_}6=W:0gY234cο_cCB$LY GBY~q.]JHhݺpt8h")o;6wM d d Xe"b ^~yu+O3y/ӨL)bp1`yaɅI aܺW(4k~.ɨ1r,J5s(}XL:v__ x?#1)"""""{ ȵW|v OOU{w83)OI2x|)Q"l\앜ߝü?_.>+݂ggguϗ$$NGR2`1D\,P/ɼSxc84[>dn͆=w<2I $IH蓕u佰ү<7"K|9+:'sd$Jmfux<!+_2~xV)Qc-z'vi:i?~䷝/J6U'9#Fд ~8 ~f1mgG @ѣl)}|y=ڥ۰6Ǽ:ipycaryG1(.mu',їI탩ҷ -һ5y=Rk"Gq{ӈCQH?uuN)ïA:3c7L,gS5iݪZn?۱&fgiwt1& P[ 6;v 3PRM[ը׷mf >K". Plq\tޝ\W1fA|?Ʃcu3,=F2Ȕ6=Db˱{bܕsmFl;NH ϛT'~bmۄNo_JŅ仳-O;T`j!6nn9Hq3nlο1J1i>'ҵbcb'Oipݱk|ո ~N@AMٴwf7);A: Pw8X2 ˺3DzҘ;Q ?c@Q Y?כP%J`bzzy&Cm6Bn]ȟ;EX~=FT6 =#,cjBw1>=]5?%%J̘#*'m""""""xXJ5TmI@'vo'ķP,}b3L/KG9ל4d+" |P3?2|$~q37-x!fǯs$e'.0;I%m&, JX9|K38,$O9.LL+`' \|[5P8=8F{s~6Y\8}َyb8'JutL"d^_6FiLM13qKF>+uT-ǯ3.j4svvưgyX?_5,X  yU6qqq- 3v#=2݆iraΔƤ၏7p3{B0M0dxw3Zgz~ʼnd)-._i\ 5~atWY눸-IfB ӊ'WM_Ǘձ#w#ygŠ9rgu9?eS=^I@L{5`dȕ;N`ʘ#J'|,mX6%V~֕nVbdO~:JTn<ךo5c>$֍S/,Am^fӆ=ӿKnO#{(󕓈l޸>3MVz[`o>m#$՚S{Ȼ h~Hچ69~2?y&f@>lԭ -Kg$aEGC)N0o8$9s!+s>nQy-::7^̵E(S[u8fOß{IG#"Mt!*e $N0DDDDDr8D;qByT<M!e`G=6bѮlf1ND&0*y^ %4EDDDDDDDDDf/DƓ>@5EDDDDDDDDD$Z`>'G6vmWB_EDDDDDDDDD^`ř{a;GXt /^Q ""zsvDmh IDATHlIpq!^2`EDDDDDDJ0*yOw+#oLdʚbyj}<\?M9ɜ1 AY=#lYJL=yR|?sjmmkO%LM}Xz[*HM[љ/E Z!"/~MhF">@jwQ{^0Dz ,vUL]TmӅ~w$[Nbd,뵜4 t_G>msekEkDY7ncb8'I# M,aFWk6EALP `^aWm[0f"S?j!0obр(,})ۤ/s]Oњ\%YaĚd)܇!v^҇F͙LS;|#h57Q9TI2Ñbbxlփ&(77;wsH ǍBp{ [OٱY[QnN׫Kf1O;EKOGw>q6{~>PR7]⓾gl4y2fܽAĈi"#+< "5^…ܶ=@X!Xa`#ҦOA }v/Ia[ۏ2wb{'6^(WFIlrǮ| Ŋ5;ϱog`v[ S. K'"e]ˎ^ɣ=u_W)Ŋ$W3-;vBn~?h3[6RpDDDDDD5s΅Y%7WOf/#,WnReӶŦwH=,MydL;~ #P.G3nyXGo㯽*m/{VE*w#B~ӎn>2fwhْY &wX"5/ N@J'3^z> W@ZwѲ@ $/şͲscpFn2}me 4i^w(""""""򲢝qX_h0Mal:{T)Dֻ=57j}ѷW{|>iM~1ѳUq\|+R6f}6*)G`i◢ϤMadY0L9IjLWg.OS܎5G]Ϫ$Ag6ʲ37m!3-܍P\$sޒ4Jzs_LhO w0,+%Rc`b}nuga,)vJA t;1*M$Oo= '׮EQ#ZUFaԢE4?5kf#㄰mH%j1I0ea|dO HP/h(cf͘q&o9>uAcf:ux֬YCѢEYz5.S(GX EgDy꭪•=_$]IG""Mt!*E!"""""ڦ R<$vic7Mv38aL``qE^|rNDDDDDDDDDD^V;{""""""""""扡qzhOG!"""""""""f~ 3̪,"""""""""։~qľ7?y`00_ ADDDDDDDDD^h'CCC(+=[nXL ӎL{ѡ[$ Msc,#{/uG""E!""""""oh'kթÏQԿdL#/a *E(/"""""jX=a& fyE;lfL!""""""""""`hyj0^ CDDDDDDDDDD^7,""""""""""Ѣ1$t44Ų;f4 ! LDDDDDDDDD$s 9K~߻a[br;60yM9vR[| 6fw\x/yF ut"""""""K% VnCpËyѴRbe1Υ3~q$ٍDsSI?|]7;aD,oSrŽ܈4݅ p-?lHŸqK:#)ٿ)֟uʽgT./W=S'Cf?ҥ݋̺%SiW$ch<̺A,_UN'mHD~B5FV^yRu'웙.]lpW#kMvN/z2MR|ؿ#ݹl]{VGrwfNP=|{k<מ$ӛV Df}ҁwȊݙ{.&& z!=8qzaU+w7Ut!_:օY0z-g< +ѡOf19WVd^Fusֈ<&@cVݜ7Uyo"+:üڡo?-~=o!71OOF]ϟ㑡;t>V~\Ym]qa! 8<8ʺ9>G`߱\c%Qz?*ЮIQo\&7h0j#~j?w (d)@J0I-`5@?L~c?5Pg; @%:Ȋjt ol֒uկ |pO=j-KV 1oֽD\~t+e{vazPe|mX'Q9O L~ о!nmYIlS΅k >&q7NgȀj[#%^8euPHٍ5R~ ӹKc}q)Nhh(̆גN$Y/u=F.&ΜKF{_Ոmb̃xT'^sыZ3k:W K,xHm:`$ń( Sb#by YV 4pZ% 2a+ipC= H<9W>Xc1vs|I=1/rZ!W!5Aɝ~yع`ML\N3 ǻ2Mx|"}|}I]I9c,WsvsNdX!پ/4rÂ~z^yǴ|Jw,dǓ6qo7Ç-SiJ$7N =R(.I_3^6H<#;gЬ\{~AwXo'uW """"""v1&2,ޙɔΟq#wr%n=-I'qՂA!!/a`1(VD,ǮO ̬s=aM[ЛI^$? y R`d.ႛy&7/&7Ď+.jHCa]ˎ^ɣ=u_W)Ŋ$ps` ;·oJ=pkFvV 9 lHzNDDDDDDD4z'חS%/mv㵇one$WtVpO惧(} 6sI3)jH L:1rdnnfzkP<_.OxjCG\I5# 9sĶc%kGۼaK\>zv W@ZwѲ@صϼnuGWTjz9ӼYvn,"rme 4i^w(""""""b?<(>*P+/;>:IwthۀTûs _}ʔKQ֢7L>M6D4FDבlfxg︒,?M'Mf`1H, aېJԘc6Waʶ5Ȟ;t)S,$m3LYF];~AC3{mƜ厳7}t]_kX0wYNh͚5-ZիWSAX /:BinJ,Cޡ|2[7mIG!"EpC;R.[R<\\ ::XOq+GRtxyEڼho)m`Ι|R8Y}FԹ)ӤNI[,  \X,,7[1=7\Sf=E+"/Ve,+Qn?~:x2va_F%AMo[γeRG*MIB77x:iC,o<&~׊׈np;Gd]{1w%~ynY빌} aY}z2bz ߡhŒ i'P9h=r+QU_v-O(<7~2A/lEOt>.OYUSQ뗌m_8'h Ly|LL";!=؉ډjP/Q~Jlt_Rk*EIHZZcA2s~6%IHy3ss9| ؝?ê:DI .n{T^pjOifu*]a!W6g_1ʒ+җl {Q1 5+ QΌi;crV3S#zKsh<g'O~AHH$T0_ű0F7߅/pdOX;1 G 4ާJzbơG7i%f:"ΖaМp~\5Ilݔ~ktOVN.E6K]L)b Ħ+̊0vKl)arן|u<"j2\˘ Mf) =ןqkSݿe /ҝ[|5.M2,TBb=qw/#::_;ٔ5Y;fOXjK.p IDATez"""""""rZlY3ŪT$C&HZToKL_f7j&t#&N@=r,&i\@@#]kQnx K|I;DKm*=:mJo'j9tõ_@Z9^Dy.7fк;1bq(LJɒ5ѭvyvm֝PcQVޜRrb?h*oiN2; !+ѿS߆gCu^Pk3q~r_ eɥiHJ܃9IpE AmP{Ӻ/78q Cn3UЀoݣ-y' 6ƧַNb Ms~}3%ŲX8wq-'19m+`>ד\7n.Йi˨ۯUlOB…cq<'Ѱt ?fUO$dܿᅧu +nIZy%߰FG*Oq[~?}WP%$I/{:֚-̞Ylb5f>[^Qk0t .""""""!ݷj֮Dٹ}+TO2E8y.dr xurZȈʅڦEsf1Y~$3j:+L&,(T-IHʄ)嘛kn=z1_d-~[gj5ˈşkߣsps9[d剄{^iQx/9``d<hypqt&O%Zxޖgȍ܃F|pq=/:1nab̸·?G~gY˾tFe?}FP%EDDDDD.^\J$*qbLAT0`bӆlu~20Yn]ldOR\(ՖPۙSZwvi\9w%{`Lݧ _X̲5Op vے#:ǿb{;a)s(dqy,ףCNʹEq< ]DE3mShzW~g2p> H د 3W'36š4b./tq~E1UeCVzt MgW4)rcpv:6仑uJӭܹs>l%P93#6NJ̨E[wqu9w {㺝iD%E3!L Jj/3`|(+cW1h `Єj^UGgQy2G?vd-PFG:>lqv}{|:ӉXғbwHp;/~=g?_]f0WeOɽ !A<>v`X=|X?и̩q)w=qX6L^?ÙsVۗR ;`ZKEG0.f`Fmf[B8ܽ>uk{u zyl {Q1 5+ QΌi;crV3S#zKsh<g'O~Ac 炑ŠAo؞ഹ-|^ cT2.pdOX;1 G 4ާJzbơ{<+1{q L d[˥{¶rrg_,J]$bL'6m\VfV[D:crן|>@f2|4noWѯ5ZI];{%o2"2\˘ Mf) =ןqk;Sq+E7-*JӭH}CZDS!w|;ۗgMίXlȬDGG'b,;VDDDDDyb`oȐ!L:A{?r)o/U+_(8Zs5͘MF2C[hv_pt_a*G~pzfbw܊qK.q nh?=.&2`ӅA^̙ɒ._eAtn)9m-ypp\w\p,|㔣eh3Sq鬼T7$3yXv+vNѵl?`X1rgˉ=ԅo=71A5Mjxd1,T7Cwߟ8c^JX:Ux4%ϰbȾ{[*XN,crX!V|t)㇯'K˹+xoN\3BQ_'H (Dž -yzOa8~=ͪNyqIɸ OVD^q9KakT}<-9|_;K H^6nu5%y[=)Yy6^Ⴋ]a.G:Z8fqW pa75|yήn{\]EOcN`jȴTG Fib 7̮x)Hf (ٗ.`2IZ0 >Uz҈ɋ`>!m?L[\ILN_;Gٗ|6JvxGJYb:f+ 8zDe!o>ߎarv+b"19> ʼ -ހc[gF dEi0A,ٳb&fbvp%["M}x?Viʤ96?%ǿ׮pŘZ?N W7W8|Va2y4˟KԎk\?5$-eid*3%,JPLr EkgٻC>n8ߏ]g`q)?#lP 3oz[Aq{jDDDDDD9 T&?*sAA^v-Ym_y"m,=TLz: yJO_t<y"7nZZX +N [wbk]ca0 &$%KФi fDZWmB%hٲ#B,Y8-2l]PDDDDDDDDDDq?.OZDDDDDDDDDyfZdb?|vaiH =`GZ65_T0ۼ<0ɟ)M0鋎Gd< <<<`%Jd6TjX9O;ʘݻvq'2'*e'EDDDDDDDDD*]/Cx 񳈈d|6'EDDDDDDDDD*EDDDDDDDDDg)haHF f*SIž6w̵L} 6g=\DDDDDDDDD*U0똷l+L|l\ϏW9kTsxOoԻHf̺qc&k@^88:X E qՓRY<\A<S ;g3M5̌c&'zQHg$uVt!f{G7 ~q,=ؓ5_Q.W+"A؄ҹptJJmoLC7zrgĸeIz&6..8_oVqŲo*~j:+~O߆T>6GDDDDDD$)PȈĦ N.o1eDa܊#$+'wNP-=G3q4W3lʕsgY7 >hGnUih]}3m K 9 V%")p5ڊRgx̾mP,Þ2kk҈hҽ?Gq}V Y5p7Až D\)CwӤȝcepv:6仑uJӭG*W0bΌ؜KeմtlՂyk,L3`\`ݸ(8m$Ƶh\8D^|g7ɹ)phBɺ[V{z|ɑʼnLd/Z&gз\39L`@0mt^6q}+QLNxNr)t 1rT݀CjQ>[? oql(T+J7gMG~ jNټYp[Ӆه-@ۆǩdcmWҫ~3=YG2 Ώ3)ؓ-om2 a+'}FŢtۅbY)3%Z dڂشq%[Ynxwd5]h\;~^DӒsN&&s:6/Uk`\\ɥ{o= #[,Y91ݡ.w:a.|t5~BjfGŮ}'1_ش3¨Yq*. ǚD\ںv/=nE4&Pup3y޾<hw~MJfS@f&::=c)o}jG#Ϛ)V" ÄSiP oKL_f7j&t#&N@=r,&i\@@#]kQnx K|I;DKm*=:mJoW IDAT'j9dusGw>i2(f%7N/i9[ƶT.t#;A֭3 ^v SZ{b,0oM~kyoVoW ?,A>h\@7R'+>Mby1P-(J辇U`+=>t`-PFZwS (OsC1fW*O䮉nTҎ䔵1&rEVeç~ɬGDDDDDD$ \`r0 L{W3kp`D@$^n̹;8nn؀}w%51W\X1 #+^Xtʍ.6 ;,*^x9Tp쌎Ғ섓NyxcuNi3;sܮ /x>xݖm켜*OP'/|d;WĔErB̙qy'ՄJEՃpr}k4?70'gܤMW """"""Hffۧq2g^7a3c"Njd6c6 ao_bu~ӕC~![,Zé-L޾\f|$k^w={ !!LdzԢa$NjWq@sz.dܷȲ \$mDH[ƜfRqzMۋщML^(ʡvm7Xp/V:H5~هᓘq'2|y-Y)I(EY>v#'#?8f+6GpT2%Β؎N{4t%Izo7ZRX$ S &VC'ٚSҙ\RLTd93^/A9?{N> S3#~>t,("wqΓ6ۉP9d}:h v:GiSr~~XI߱"+=Dsp1o4İJՂ8vTP;L|/ޮ{,T9BI)&;.! qwoZW'#ay-t {%BUIlHӜ_q kI,i\ˉeL+D>.edi9vͩ tfZ2D3ӱpE OI4,]ǏYS#/. 9>0{xi]Êhҫ>n^x9dzy7lёʹ7{e_+L]CN}ܲ:_טb< B:%S+ y8A2s׵^Xs5$s#@ͱ-njC{Q;~.8$o{4yvH"#1m;uj\6YSEԾisRgԘэ9foT/k1,'(9h9#ku"""""""6K嚼2mc${# g5k@i$;t[$_ 7WR=_FL^4?%Ԛ[\I299r~D6&xZ^՟!W;RrGc-ߞ捛D ,!/xREz'?JwDbr|k> ʼ?Ҁc[F4Bh8 Æk&4KP!_eg?^d*> hއ/h2c;ȁ2C*:>Nv_7=. 'gt3|FU*VV^,n y>y9Tb]pc[W\m/M v9.SfFj\]F[кX-K>#L+""""""~Οk z֮]?k֬uW`xH`0?j. "bO(mx"7_S>k7v_@DRx/:"""""En@V0 HlkFbWca0 &udb)hHf8rWĔ{\y^^߫՞ ~So` cT~YDDDDDDDD V0S) 7:gH _i+O~3""""""""""؞`VYDDDDDDDDDfsYe fK%5 sBBBj!"""""""""" FFF1Ղy3xs`PK/ƿzԌGR *rsDE#}Q_DDDDD鰹EVk*!"""""""""" fyٜ`6 #5 F""""""""""b$U,""""""""" 7zrw^*냇\V.6(33G# ?Q3s"n<|Щ:ٜqM譴v('Q9<\qvKN[v$/3..8_oVqŲo*~j:+~O߆T>6GDDDDDD$)PȈĦ N.o1eDa܊#$+'wNP-=G3q4W:>G`=6aБLZ0·37mm>m#I#FILYljZ[U&(6.oXg:s3fw am+V+(XlRpT,Nxʝc963XΠE<;e-7 a鱌wZ$Zh,zׯ@L/%%} $d[ږ~e餷yesZÄE[qha:3fsF\,oE1=& cMM8لd V(]DDDDDD҉LLffr/1*+C8{gky',_ף_Eq%1Уgg0hN8?Ϥ`OnJ@$\ml9g̔h1i ~bƕLoefEW%ӣ^>:Uw`ubkݯٯ8<~ϐT/ ԑiלTKzqRe)v}{n;=nE4&Pup3sʵ/#::_;ٔ5Y;fOXjez"""""""rш9fUH0TE3[:-$Yx)݈S7zɽuק=Z=y3_Ҽ#RJ~c÷N[|o?R ۦDEZG+pP]jȇ[f) bש\i*cẢ/5sQwŋ̔(1yY;묄,:GED*5}˦Br=(5r7ojͧ̈9ġ3+'KrDT*iro WuUl!!+,atju@|r欜ޱv˭U9sm.pͶ+ A߿z]_֗YĢ VneԤ vo(]s%R;OΞ7:>io%-`hȗP+l7⸑1wH֐o;uuMdϕ *r DDDDDDD"UB٬ow4~˜9{J?N܄Όxͯll2"F38=+͇BSY> S3kwFrUYo2|M̿ʘLU}?kS,^D !!'/b_Ur)Q,C;v'V!n^upDDDDDD$J[ZILvw*_ovmӱQóL9|9GN-Gq2VNm$eJܝ4?5iJ6ǜ-2nN2(],>IR&`MN:56#v z7t҉2M\K۟ef{s4ի_T)*I)qO)29ͻ :j17kSP;L|U~}/% :8Q""""""")es!IpE AmP{Ӻ/78q Cn3UЀoݣ-y' 6ƧַN4%ϰbȾ ܝƵXBR_OsiWޜ@g.nNT=>P: [D%p{ޛU>5⒰q5&j慗s<|~>yƥDM8H7v lݟ#&q[ Dv" EoXwڿCIVfZ0Ncav3OA5k@it:6eB1_ &\sJS~@'҈ɋ`>!m?L[\I(&'GίHУ\KW>guXr%;#,gk 3=QRz޲|{FoNjg09ۏ]1f e^o-3vWsx~̤}I@ k2j໌h3 y(d+& jr = ޢlQpq4~\p0ΒSykbC}BUz"OT<{ L^٣93GLf;2unݧF&/|ף߂ *[Z)""""""-k&kϚ5kh'-6̏˂JWQ^+DE#}-rըհa`Fb\0[[ +a`6 |&K/N$d8O""""""""""}Ԍ"US) 7:g[ *S1 plO0+,"""""""""\$""""""""""b̖Kj!"""""""""" 愄ԌCDDDDDDDDDD2{[ *rc#)!|g>} .^Z@TF>鋎Gdas T CDDDDDDDDDD2""""""""""\9lFj!""""""""""*EDDDDDDDDD&6O`)&?M&3 L .6'mI1βv=Z0^U0} HZ,X =ȞLad| {>{1636nO=?&|NHQc8}֜ӛKDDDDDDDD$#H$J&ecmq ~;gMݱ?v)"&M]¾kzoʙuh]wL8e-Lϕ׀8Cbqqp5W&G =Ԓ8y.dr xurZȈʅڦEsf1Y~$8yq[ٜ7sp!kTiއYgICa%|zL4'?<-\'#|j$+Oܛ`NTJ00-,Q,NN}sF2ӗ ?ߍ܃F|pq=/:1nab̸·?G~gY˾tFe?}FPtIE)>U;ش!![ݩ-(L[1ydbbq4%XڲrrqW sT{;wj?QNs5eio/hMkĜʙxbnbPɔmqjbGiz\bJ9Q772vrWMm ]u:(X"rSLISڝs3Ii8;FMhXγ{4ƌFk+6w׶֤#vyѤ{,D-*գkn=RtI;JK6ױ!ߍ SnwqU_AA p;57ijiȕ4WJ5W\)*"qfq*nD\~{=pyWDDDȎ ܔ l\/6mLtsǷ$eͰfT),NgIš[11L^.8dh|BJA+4ѩ)7Ҿ2ycM"Cޣfo:bgB6jcF5v;N\Dn'Z00.b<z邽#^i9xS. JՂhFo&~ۍ;ٰ]7Dj%NT~k[ݾ-Mëx՝ԟyTޭwLq7^w|Cܔj<vȋǹ#clP0K$ldgJ zMl;n-Ry3%mKeݟokL_+lBo5|6sg5l GMѤuWF~ MXp51>>  U{իXO,K|ʻ&"""""""c3718A:Xbveuys`c9-kqGiz KPγ#:+曖W q[Osl̰! :Ad1ܺy߱2Sqo5K=[鿗*٧a\'vx|:w]r|i8q6oҟ ד]=nle9ʂٛq7Yޟo_w:cfZ23 qH9(֫6ap!HŴChe~6@ GDsRϩ0zDA ff {}]ާHt%(t("- 4add{ЍsMlCDVwzR{JȏS`*zM4Rjkr=llr`5Cj۬k"""""""O\qcߗˑ)m}^QP5g6%|%ax*+QcaR4VjQKK5 ά>tʗ:q=HrNyݱ])\JDER4iHZەjbMvF| QfP:ZIYKٚXf94ٿR R~ԟ1գc`[Mb Ʉ?ʔI3 gDt-6Bbѧ# eILf6 (yl IDATEzt /Ok)R׻2S ? K=ߍsK6Y)Py8"0jbpgB)1ׄyNnQUO镩A8c;G=-')1ɰbIDmOrςw;杵gJmM~1CRZ7Ƴ8f4YҢF /U[U{vND&-BVK)b&4Ȗ|~Dl5"4:J~-2̹J!Ʊ4ƙ~ Ц"q DDDDDDD+1[ ga eWt o_?br~Aމ01DCF* F#F.]76Wby^rr;4Y˼Y;C=%+I x{,뛈2i٥d$,09Qsd|7X-XV Eđ9 |s9ƭ&CTۃ9.Xn.˛?'eXVM||1+O_|Y8=VͶ}f|}|\NDDDDDDDUhZ.-Ř<5oc͆7_bMT<P|&lĦHQUA~6lc6hz>7R(W.B.`IΆNݳ^O .ew`] dl=m,t>_Զ>RVޘs`̍SS4sbkZMqeytGFy"\y8#7].Y {><2v?ytZڕߥ *ynØp mW ѥc IPПoV${,`v@&9,qG1:-*xh!?\kugLX0qaюӵmmJvt0D#wQ)Q~ⳉZ%Φ=bɌ \V:_W=ZɗCr4,"r Ifޒ +{)/RЏYܾ'J;Q|-~ݒB6 t=9ZGdMd"oxCpCp.4˕l7/?3HexRo¨p3v#H0ۋJ kS>)FoGfј)rcVc \2z=?g@hu"k./ː3rGά;aٛʭgExsr޵{w} l49M8htJ4$++18ˏZ]0f1Q$Ԧ&ڗ!˂Y)|܈{>/Lc57KUj `s{>٤/5[#߽~Œ I{&gcv2WO|BmORH,lRX0L!gZ˜6y1[ ! *N͐9Qv|cgS$y_s+an khj*h&"wQq==~6XaxޟݓKx<[tyѤ; \-## jz>g""""""OFKd`d0DDDDDDDDDDy`Z9y(YDDDDDDDDDD`y=B92`H~h=G K1٠ :bgg2~{v%>\KI[!o?NyDYEȞ(tK4mgvZ֛Y{cʷ-̧UW̍#nyJRgRo|_ZP.+NY h5'xfOO[!VH2pwv-%1lџ\IBz^5㽗]0˛tj-4+y?5'dǗ>Kj;~B)ۖKG :׈=|ޠy@W= E~c]=Y 1;!Q1]٠0^oU)hriZ4"|X8^EO =kwnGk.-&bF<o_̷%lԃbZO |VeNOۓpzy_1iXŠ!Up3i:"/GR+ ֱۡ$28Ȏ ܔK3'ݮz5a4c3檍;e.f\aG}]rkFhY7 X97:c<^w0s`D%J/˲C phVڏ\Ns.,R7t aj.{lCS9Wc< :p`ZvyE{LyҰu@g2h=kYLףx iݧM^pQ>6^5 >Tzra Øݏ`B|I)+r 7]!ok¶d_{%7m!ZL7a[qLϮյ-2vOte6u vؤU['u+\M-}0fc9?N0zcԻ<0]2718[˷n|_+[[_~Ð`l,eMBh8|.K}Nst„i7i!n"cK,_lҀ9ǰ-ahT2SqoYɆKȒ!kM~y]NY`Sֲ^v$).뜉" uV|1dK>YNsD"Rr l?c\v>=ZXQ]}LShS nA/=ﯴ/G{G˘&]%pnq DDDDDDD+2Dѳ0Z+:˷pSgz  mH")k01wߡQXڹDRrR|>hޥӜH>]\K/<+,)lllFeđ9 |s9ƭ&CQn g=X.沼s™X\_feeȮӿ-l6d#9u?:ƙ0(]Oh/O_|Y8=:9 vٶό/ˉ2֡ -_+ţ󃇒ߟmp+QǣT ؀SD-AC1J*Sq7߆]c&͕BFT*UJS%U`,Щ{_Զ>RVޘs`̍SS4sbkZMqeyWA#NʓĊ6S#:zQ)y"\y8#7].Y {8=!b6>`TƌLCrLZ`9Z|EYsyXj8z$jfo*y(g~1_4k)L :g.?jw@rp|hM ǬF+R^Ckh_, fIh'>U;WM} V' ܡ̌c퓕6WgL\Ky헹zjGOeaؕǂyfFH8 9dƴɋ0ָDN܄TMWae$~T}{ YKPG,՞ȝ jeLJ1vF8=E'51g.BhSyF̰6 yV^M@@VYVqxabk?. !rW2<ڸ21I&7x_6dSY䙓yyd:y~] /B89lbw?$9yXljQOEDDDDDDDD._Y!2yc߅C*!"""""""""wI2rʖIxWt~g#d0g(DDDDDDDDDD乓"""""""""""/4M'""""""""""dqs&fɔmz7ܸ13l0tfe0f-:E1ɿ[eyZ2֌==ySk$V>5ͤ6чΟD jK<%\DDDDDD d2QzcgǶٳg/ŋ?|JdGaV~WK;gw~,1~+#3vdGփX|y [3 ʐ/#v.x,CwgWkb`C6 9wg0{95>+?xCPgkQiKM.|t\uQvZ??#439bQk3cp;^-ME1)l+uK<%3 6qlE&L*yfOO[!VH2pwv-%1lџ\IBz^5㽗]0˛tW?oкn(._lPA̪4Xu - oK>,'Ӟo5b;gz 7NG~ǣܵHNHψoΌQ8ս'>#S12qz&$uXϲ&flf\qllX1L/#"~K.xUz^-+4"WG{~.fDeYvh"Bˉq%ؐ`Y$W-esq*j'rA'|LW:2}/^0)oYøLg-c|\zT߶T>oO9?U"WY 8K/̰߼+5u>x_=ũ[P쳑̜ow#zf-K[p{"&}v)?#ٙԙ`=-8x[&lKeݟKtZ⬥iª_=5Z=;ly:u6u vؤU['u+\M-}0fc9?N0zcԻ5֛Ť{y8]"㮑MLa7u38ò'ֆׅ0$a~hYй&ya)k0_; q[/fF`X;mX|·Cۧ .Ɛ{3nJ6_Fli=HOM~_ ;^i3}^IkGԥ*q3';׷רڼ1y%8S>/_ ɔ,~d4VbT, IDAT<@k{6-l9oTd+7ˠG\ ^ cF3=|]/dU{ks3 `dd+T(ai7Žz-/T5 q7gZO/č8e ѯۭD+ Lmbbb17&g23qG 111lX/+Vk~2ۥ?K:Ck0[?ֈGWhTA] Ӗ/_aGV=jC"F]GƒLݺ[QjԫE͠5d.:v{=e3 ɹ wnw4 kFǼyxV+4S:g]'y4%si~L𮔶¦5[HW7|͢52{@ d. [/n|u |#WT̆5S`;8#Ezkb&㇟~Ž~YJˋCg3""""""?f4{NVa"[Sif`0`08Q}$Lak7XljQqf SLhAkWY={b0f53\8f ޭ.ЬXwĞS=[s:6OYI#G'E9/=`p gb9s}5j<93._L 7?6ɕbOJ-3'OcȞiI0x\ ٶό/=gwPQz{CO^6lxs(DQ l){{a"Nl䠡>˟Sq7߆]c&͕BFT*UJS%U8`,Щ{_Զ>RVޘs`̍SS4sbkZMqeytkŨ3egPvk9<^oO\[omvĖXpދoոsRUz9ݡw+OӺ֮.]hVwG[h]4.[gxOre|{"k$g |ݵ;7=g糖l>hQC Z\u;cƂ [D`vT>3_hW CyZ~`o`6/G[GL#Kԧe֣,WgP'͠E.[qqNYfS}m72b=uVn )ܖn fk0cd֓2%V*{S^0S}JO>+wͣg[^%l+zr 7D"wVΔ3 u>LϤce5='J́cz м.WyDb̠zDDDDDD>Fݘ1=gƭHQ|5gFƑ`t#֦} i 0GcF\v!Y9Rf:ryc0-Չ,CT8z$jfo*y(g~1_4k)L :g.?jw@rp|hM ǬF+R^Ckh_, fIh'^!ϫsD>uKwPWIvqwľm hh~w1] TkY/_N7R'nˀK&<,12x-i".i7Q/s'$ 4֙+Տt0qrɨ%iPtp [;!rR5a_1̟H~i>2}k/W ϐ9Qv|cgS$y_s+܌%Q207bIHCEW^M@@VYVqxabk?. !rW^ب \-[5s`D%Jc_S%t{dNw""""""""""|n]:c48ː0Ltw*"""""""""" :vGz;qc$Qקsx:"/}-:""""""<<ܟt1#&f7;wr̙҇/`ǪD =2wG),""""""""""ϿtEDDDDDDDDDfe0DFFCDDDDDDDDDD7`tQs1w[ `7&ƾ՜IOw`""""""""""235Z;^Kzu/#i<',Z3fe`ctZzg͆=.9k0:+""""""bZ9dM3̏ `)k^ qs$K^?j#k238P}毺Rdn\qSz=#8pgolCnp;RLܝprCv ['WRf=-o3dxe{;6ϒ6rf8!/>I-w{ȸ 0w7jƘqM㟃;wCz{|,5ZOltNğKԚ\yIQ1]٠0^oU)hriZ4"|X8^E/=kwnGk.rѸ孄ׯ'yrQbd ,CfӮuD^,EޥK/VNyo :D–Hv$[ϯw18ֹ#ֲ.U7S?ے}YۆF gM[r5l GMѤuWF~ MXp51>>  U{իXO,K|ʻ&""""""&K0q؍C 䞡yY̮lmnj~] C}懖5 k,9ҹVޤ]k-|YZzyHzK@y߱2SqoYɆKȒ9˷@BD-5ZN!,kkX>5K:Ӹ/\ʩh38U/fSZ~}:WKDlX^M)Xk/-3L钗XmJFlzp)]"3;{TxN% X86k4OJ6Z[ſ7N6ۥ4b9 "!mq>_e[W*@>y-חgc*oMd6d fbbb9jvOx_DDDDDDD!ClX#/# 8Bjا,I<v775"D]F]GI5<[QjԫE9[ ..(3d8)_긺{\~cF S~{ș KBiӐ>.|?y$ lb~t(P٬PHp*H:)e'a]:(deʦo=?{6mP3o1%k!5T}"6N-C9δJi[+lZ| xOG*ŭ=&lP֋?W1_ 5)ݮ)_̆焥y uʒ&+s]?_s)*i/;b9 RuKb:qwS7Ƴ=BWGR`wvF2ys gWѸ8K; |n9#BBj''[$E)Mۯ˘z55Ed.ils{Ӹ 1$zdC%Mi}Nݚ=_-jv?נh6w/eg>uN͢p_{GT}`}%Km[ɝ /[l?_lfL (Fo} ާ9:qn.oG:R3`"q$%ߧWY fr_8xB XinF4J*=NըT'f {T{CBb`%]_sƿÛSt.O~X6E߂|@r~̭f`M"Gx9' B: ǁhl;o{X=I$Uo!=vˆz͛ kP4F:̕8=y;2rt.3_ڌ;[/|#h5SWqɐBej%F2 */T偅m6wH_f4o;1Ϡ}̬9Z1i gtn%]iH??^1)X3?z%x&WqG^EqBZY5?O+rŤ"[=ْ{E1ve{87NB+ cA86z "|,%h<| # DmaQ:_j¹"#JoCې,pĨiC4,N^-Yn`AR=2g480f"Zj~~~\[>#m|0]ANzʵez+'w"9?RV/psO@v 1ݳ'`0޼ǘ/)Nf~gy$~"I9Rf~"$w""""""""""϶Ǩ`N(DDDDDDDDDD䩓2""""""""""5M'""""""""""-ږqS& 褌CDDDDDDDDDD2}ƍlٸ>ɂǧyOY""""""ItQ` ϿrR#aE(/"""""HIC?D+7D"yu_-l;>nGRyɔ7~͹qZXY2~YBXp% hqhMk IDAT';.=}Ws>AfLxO^7fu5a$ߝ'Gtvdpw-mv㳅^->cofZ۞Q8 d4K~C|16GDDDDDD$DC5#Ùr9#'2bvdؕaԼTi5EFlYskn9y^دg >>ˆ_[7^ ӛŠ[s5Vs*׬޴>nb.Ëz1z77e85)KX>"i-kWN}΅pn:vdTuM֭ϓ)_%w >Ւf&M̹.w҈oyeN{9yFHz,I}6?DvN۳i;~U?n{^a./w~m|-RTGl@Ŏi4YY "?<Ԡ/[ mx;9@Dzz+ظ'/9Au®J}YTu{Gݿ/pT/S 9wmCm9yw:VL{[N0= 7LunDmOG8Wd54TMَŧ~ν'"""""" c+Wѹ"~Bg\Ssc9Cd=,)԰7_^Kʒ^-F!򼰒"G _91cSO;x{'c^?Z|q k Q qpm|4R' `z,7B]|rt/cܷEϟ%8c7cGF&Nڇ{OgIC>i/;;ϲc2svKFJhh(S9}-"""""" ˜eعuIV EѩEf]DXfqhnQ;JsT\jx`˖yyX:FTβg< ׬fV҈vqo/rsN:ܚq}Z؏2iT@oǥxh@itp-yvyb}&J,2R>2MO*]q׵'692a8f`kSRLa.;F;Y oo<\fғN* """"""")DѶӄn-:w;1EX̚_? /[l?_lxyZ8'.F_Obۚ=_-jv?נh6w/eg>uN͢p_{GTdɴ }2SCf{z?Dߜ&>˓`#Ʒ ?'-B#"O ewHs~ 9RwI/? & zÛ!g^ L4o}1ޥ)хoa҄O+&t~diT9u߻](}[:W+ԩOMLޡrҔk]}) TfאƐd;"PHe~x vK&sb9?qȖ|yI?Hvl ťT%,%&n%dC54V@M tgo!T1[RYԿז+ iq0X;%ҧpEDDDDDDeiq`#Dj*Xr%M|ɣزq=~; OY)CDDDDDkw|'l D͆C@@ X?ocLS'Cd>%CM ITsbĽ8୵Q_c\'oz7\DXt$* 8f%;^^㘭\R\RǶӬjNipuK_+9M4? "|DgjMwfh@;>[x^f.-W۟髃Y;g;4͒_7b_pL-PM|cd\ΰz55-;&*븤LS:Rd.iNS2e9$w|c./Z)] G|Hl\;BitkfY3˕d=-ޘ ?`q!{1cZ];TGS SvY6w?u_HcX7W~A^uh㺃ozBֈ<фЛ6z1^}lOz4luzQ2ɅocH|u;b@o`b1E%_8;:̅L@Mmw0z1.]wW}} nR;qg^qn 7/j254ǴquY\}8jak'옴n;3f]b'i0a2>6;9.^4e}q*d{?8żutl 6Q]Ϥ{ØHqv+nl4qw,`z0s71sE3p92qVvfj^ȓjRԉc ZN;l,[r)f0sfN31_Qݸ^`.bvelwWz jTQ~lQs?tK~I3U=$tI;a|M]̲g2'#*.Ssc9>.4kIMj 6_&VjQ!9!~2W0CQNf߲hjoKuNwYKͫ Eftb KzRl{5ӳi_ 85eĂ5, %oѰ/\ '3 =mWY73YU>j|I\ w_Ku=bW._ICR`N>s9J~W!gH)_ 'wႆ{IZQ8i??4ӟFibkcI,Ihh(;&:JfKFJhh(S?x$&lY)WMZp-2 *̭̓܉VsTjV6Fͱ%m!ԩNiG 6~)1h(3TKyuK}7vViNޘGJc˖WȗK֜o0~>^څ_j;:tlK^xAʩ>j)TazC6z/Ƭ(k& UkPE ^0{ڷawh)}P'PY,{=:v#ӬtSyUzL:Hpt,љnqn.`y&wT.À|V'NW<8v*F%?w&얣iNĩCb0H <L&¬,S8/_JR?i{rXZ_;Sv8'w_|hGDDDDDD$%9vwf#'OΆ|{< پex)VW wKtϑ5kPVBפּ6o༊<:Px^fHWv̽h'2&)ω<>v߽ISxl~J9 u [lXΟ%N9f"kFt2[Z)?~?u瑄Q7+SD]?ɘc;Y3'"[}ǕS'f$LU!QjO?dt9y|ĚK2fɄ^=Z;2=ЛNNf,k?Ş! TM()ÎcKfZo2=rC:`mFn9S2h $t"""""""ϊ$"ꕟ|Js$C[:Xp[8$jj1q‘/z(/8Ùio=k7vz>%,j_ic'zKPbl6lw^lqɂǗ^,Vx&4X wޝ?j4a=m*άͺei~LZz?vqZ;|,OSpΜ_GVV̒"<8vQakqy A:QBi@vl]|~g#Y{%WZx%7g{y%KɒΚ W>}} kDY-SfУd!Kawp+J%L͉8s|T(V#VͳoWC^9U Eu$OjTl:B3z( }.@.>9Ipt!?nGt11?1iq!}or9NY+o1*3raZFNa$%yql+oMՀZz'O _4a5~"iն nJqWGc.5*[7VKHNN7` :7'^d1by+_#e{_c|8}5؎/dĐ|4/ 4)k|=Qz^܉xm85eO|1ƟSw5,oU&w*/niV|Y=ɉMQ)Vr(?ʖHhh?A%)| vГ4F/d$`l'A(#cp;|[2lq{L~DC;?gܼ!7|H{qlt]rtO0 r<~%Iz.nà~ڍfx\?cfq T;.""""""O$Uo!=vˆz͛ kP4ּt1gC>w'w@F΅c HSsGb%q#}Vo ݦ,.\X5AG:/-gfds.龈>#h5SWqɐBej%F2 */T偅m6wH_f4o;1Ϡ}|( xݘ÷pu'yU9W$ъ,XO69U1sTKOۏmO_80pFwV|&A.w}q0f5LN@>o>f}?͚#RZ4aU,Uߚ7P0^ɛ+aغiS! CRߣkH(k|㚌1?,pĨiC4,N^-Yn`AR%w"""""""2g480f"Zj~~~\[>#m|0]ANzʵez+Z֞2`߯:\D8%~ꏔE!""""";o>Viٰ={x= {I2y$*EDDDDDDDDDٓI2$H!q<k}BDDDDDDDDDcT0'a""""""""""yYٔayi?ID'mѶCDDDDDDDDDD2N0GGG'e""""""""""qL7n `I<>E|ʢM̍Y0o~2y [6WO'8!sR0ǣE${ZL\pKMH[v}܎Z)}!zovSlqu%mk&"c;>ыYI喊/wO{^R=,Gtvdpw-mv㳅K̥j莰 IDAT{}3}u0k~GruY&YVLB ɶ9"""""""%oV&za~D?vKb$95#UZMbt;%sqz/[֜CrbG@a2p#;ֳRs&߳ 9w>Xc\-=rj7U ^mЕo `m$͛~ɲmiωw{&;(y(S|7|4u)D %D4´99:m4uӯ_ sa!]kw`uA6O-oPJ)s Ɛ/vx8Ɩ֤,/a@u_-F:Q;jncv6E2`@W31mʼnk~hhY㒭Y}cl.jqo1qtr7^/T20ߊ'3+McbdSt7N.&} ׿K19IR͌= iS| &LL6WQfC<ƥ +:?\n:fv6>i>9568W0#clL/̩]sj7`1ѡMBYMGciY~MS@68:2Y|>27c̛/`<ݜ{¦j_ڹz7{gw2܍g>Ro͎+1[|c g `<)PAdnrn3\s_Y6q9fy}mta1/937''Yм,65+cr}LQTh21)D݌u24y1/|9LumnF>6_Vt6.Ǚvc 7Ke6N>wNN&[e溱KUwڱc{mݙx?zG87}6\5~lPʸ;0caY+̑Q3wz3K<%CDDDDDIZl~sı-'6Ǐe-]HDE93ǘ/ Mn\j/-R3 e12ūX[5*(?F9:sRު:ޤ]k>W.f3“̓s Ω߱RxP|5{/et+KzdE}E9e5``9|K{Zk>pf{񉴩ՍzE?uQ~z6Xec[-+QGٲj;crO@s`~}x"}ߋyXs5;~Vۈcr5K' Ջ.R wY'FԲmoOC)ֹ3Wz@;'f`ig_odGVrKN>q& 2G: /ꔼst-E@tmhC+D;3ݝvd.Q{ؕk3 j#'ý^_5ؒ}(_La-ww,Ie; %tdZ]lI׈1;B eW`*;"""""""$фc9+>JL ^A@ecy0C;j2^bVŠfبu9Zc-D:)ϣ2% eFt;nОr2@*\)Czl7 N(Svb(ѢII\۱5]YI绯zJRɝe}l >dyK ԩ^gJH6g铅=GވIsUQ)v~X9M|,җj3 }:_ס>d/SUSU²&˾O)W,&"7m@ d_a|#*ʁ\RwhxWf te >:3M$?lwNqGlZQ9ZKmc٣-(+lm]Uq9Ȝ5SAȔ%#iڭOqr/_̖>|[&țD&¬,S4q]SLa~ڿ:/wk]_8IF`?1cbوɓ!o<;goC^U镪8c;]H5(p+mV췃~p<:Px^fHWv̽h'2&)5]r?vفg=$;g)|R*V߆3l6)OI>"it/Bf;<E*!m$D\quu՝Bo&IN^{E|m ܅'ptH7ٳ䏭0/֢F'39[.^7yN±pDb%+4Os@7a6 uI}}˽N3Z\Ul8e|y7ɒ:⛮@DDDDDDYCdX/?CB7h"pEn<(IoV,֭Gbb8VHp=?Ԏ4xWҷܵY;gU=Sv#v\Yp͔"-I1`WJӶ !IL8[~!8Ƶ)yGn,7X wޝ?jxNN8bю1Ŋ V+D=;vptJDͱzZ7 qw^8g~Wwˑ+ \8?sx~H̀eW6C&fsrΜǒ)+%5%ߚA GϝfkqX 3OF;} }65eH%wJRָBR$9lfCAooa͖< eLZ!z4ӛ9d)LB&7j*\w3!h6'L8Fsp{BwPX.NnXK=|ĿlBs+d8#>?,`cJ;qkQo巹V*uDj-Rr2=AJђarVo?[ H,Xⓓt ߦ{EXκs/UGd̒ sx"7| ^Ϧ*e'/Oi~%\ a孙(R$3s6ط3Ns4Ǵ4_:^%Ʊ0~鹰~5܌yV_³\|b_bҐ5O~)Չkws1;ڔO ٹz%pڟ+f(؟I PA} ޝ޺8ֳvoҘwѴ77$Kf&* L$af {T{CBb`%]_sƿÛSt.O~X6E߂|@r~̭f`M"Gx9{P[8{8ovMPҏ'JЯ{U̾y WLḡDVB؅2 uTۏ]G? KD;%sRT/'CL_ɻ_ڇ9oے 0ve{87NB+ cA86z "|,%h<| # DmaQ:_j¹"#Jo+.%3ov~Lλ?bӼ2#f`h5~3ݚ sx[R)5:QӆiY"[=g$t QI9_`3\1w˜LGژUVʕ+iڼ /1ϗJ  DgOl\C)=k||%"s>eQ,^3Un$`u&5!88Y,̟71H`Da&N;BBdIc n %\yMLNՒc$>"WΜkz2D$:H>84i8M4|02Fd$:l%e""""""""""ϔ/#NN>,y"=stttR!""""""""";f رgsLD'7n `I<>E|ʢM̍Y0o~2y [6W1kSRTڪtХU-QUQU#9G!!S$>9;u:[>>*]# 3ImNuY:)~859rX*0_8^ԩ'cRJU<9rX*0GEEɈP '**ϑ)߉q6Llw= 98% ̒!U Lz}9 fc=栗57R {2GپH>Rک~(uer09hwI7CsFPn6-<]>FN^1dɣץkĶj]RBw>?fd8`L5n0Z]&O*\Ya-U"G˾]*׾9'fȬ|O+M-2= 0iB#wiT.=-C1*Ruj>纪uxc5o[#f.F9%.g[b_Ho4+BTRgU3wQUj69 qBW@q9edk*GZS+% #`02dˢVָzZ-_S+k4q<~4^*n?ǧoS+lfw]ܥO5W/͇j H!':OԠb1 PjbR9o=GOVosu^m %;rThAZ~rߗXb/@?_ Vauޤ<'`a-2@1\HKҬz5٧捝6qtX脶.oХs<ޗZj<ܥS暙΃d{?J-ɯZxSV_Z6% ?+_~}>KQ]~_FS"_o5^qޙN6ɖ#rȖau9GOy=z |H{@{No/Ŕ㴼v>Ϝ+4@6sJCz,֪%Me7iyK M+m4QB#hu֌YTpUKkJzM]K%]ڻ U^[7$LUVVLkfNzjNwtأ9ԲvAGoDʳ/ۯ/U9T۱jV.o d*?NyT;o>dFi(0` s8:#&zk[ղoAs~qE9"ӝqayRk__3j>*d :]n-0_?|HG]V!l)fcvO4lVЙ5mzF_!uNaHML l֮돪Nm*^*Xn~vɻy *p}mC{c:+o̧>}nҊUL16e/RLx_ݽ/)9@zgo@ pҏ?Ȕ<5SL2u=H*Хzr':Y~ Z^ccmϧ_RP7IUU~a}`މSȉmԌ]zs~%U 5q{pT)%deWHWkWVsrIr(⣪_;ڪu>68RC-fe?~"Rvv, +zs ~[Vz0xEmX &,w\7.Z,[C=*qU,wPUo%CkaOwȩB*_6j\5KF6ٔzHpTԈZ9{eZG'bm*VԙX#'$qzylq!oϫW\FGWEoUSVI #EYgwj cs.b}o⊍BNSOiR@Mbz*6sA/]O*kp8r#璷j) vXY?ܭ.T(R\m[u֙G IDATh8f~  iOu5 z3sUZc^!lF2.Dz1˸̭V 6i(9L)u?onM7u6<14}o-W漪\32r)gOy7 ~^lL%^:+RTCƸdCځel*d7s˻rMSs/+gP}k126/OPk%M$S=+Kƕ8Ɵ$;%ɸM.9iredSy89 =KENI[dX>/ Coi0o$X]ǵO7]E/ToJIS?U|q6aR*[>D$f:BZU,M3EI?bW\/v s=|;}5-7$}CE]tIoƜۧ1yCQ`qH"e킶WW*'j!)O4J2260R}l^[Y62*Gd Rqſ>n]yB(^gZj|Nt)/(_ʖ5t,9Wٜ͗{u^ee$s^[A"]U&s%f[-+ -ЪoU,I NnNHҐoHǀpMlY^Y>WJ?VsujFwh.iJ_tX mjL7n=Y]c5*yU]+ɩqj_znZ~~ݳ]Bjd^z[2WQc2 RUF_5Gk@Xwʕ[2L?+צ.к郵 ꊌ !WdlE;J3wдTrSMX?Z=JݾmVO%xLvqꃘ*TcJ=_ׅs4l]r+wn-_OmF2YUiq^T$o/LS4WMR3XXAew1Ro9[V缢Gfn5Rߕs8͝_K\o!U0Q -{9Z3V㗌S[ŪRX2׻feЁhڍ.f.Hd|jV뽬+hԫ4DVѰ*^I%@z|k[zi٪?djԸ\.nצ>T˖-} n98Y6֯[Kj-9MZ--2h(0s ; `f2.%??! IMKf___)tEXtCreation TimeDo 25 Mr 2021 13:39:41 CET*w IDATxyxLL6$ $Ek!BPjotA-RkmUBFQXUUڣJ[EmI$s4!I2ޖeIխ]g0Rb[DDDDDDDDDDld2a2N޳ogڕۮvvv%`vvv@ak[vX,KDDDDDDDDDD$s7Nִ6mgX-a"""""""""""e7vִS$L7; If%n69+yh"""""""""""-tq#@E-i O4_]rbCNmcxh,WY5G\$<+"*eJnU5Ϊ&`aEgǒ{kEDDDDSfr j(O""""\8vj]xpw;(.""""E~u0"""""w>Ecn4e^龧R9$"""""{a|^gF;HDDDD$lݾ=|]6)|K̆i:7S@" q ]y%EDDDDf~<3?D\:p2wut""""G2J`.&QWb`@̏z-|46%,`œI2Y䶈NDظIj*K"""""""""""bJ8X(jw=d|tgNeIDDDDDDDDDD$W3rovFEDDDDDDDDDDrHm(-""""""""""%EDDDDDDDDD䞣䶈s{""""""""""rϱaĥKլNnwon!""""""""""UTDDDDDDDDDDD9Jn=Gm}r۸g?HX$czDDDDDDDDD.klz>f[,`d+HƝ&\\џޭ>Iߗϓܫr!m៹m)M?p0] ;=s1ZE)R%׌Xz+ݜjeJRҳ"u:ͮ?^k7Q|)<*ԦJEH0֍{u+PIwKfyk bh""""""""" ŚI$&$%aXMӥgD+ӯŐeȕ|Ʌz/cEΰ|F]xcWI%~13G#a9;Nh_?q9>9} Jfr>Ƽg3\zDDDDDDDDDDnȽ$q_ƝɿkAmq|qof-"wq3u|Cn(eפN{?ʿIPv|fm/Šfү=PZ Vśmh%?T.?;G6ԸeT8x_EΏY"47~-w SF|G?wv=X:o(Mj34 ۦ<#T!7h2H^CF{kȃ"NMxc8kn(VN8b ͖x (IS sғ'S\-ܢ _˩:5=0@|o~SCA|:QڵE "%"t+(UDN4݊Kp LhuY""""""""""ɽ6)[Y}aQ9,}zR&D 4H);4 phk>MDRN@>>k/AVqcgb n]k*E:uLh>8c`X7E{P)~sB>=a3c \Ln[dł`_v,8L aؔw&<4O fVeeh"i 6{./qaqj<$|֨ofkJ:/7ThQ:1I7=O1vK\Pm=y6H߾F*<6;N/9GP]o50ںb8u__PӬ 1Rޓ"yķ(Kq0;WO^C flDK{dBn~*#+4.s0ae'J܉iLtzW0r0QE+>J>]xk,_QBŊpx>};u'a;9Q Slcի qM^pl_?_N2m}b繚z>K].SW+=0y.ؓǺaĜ5dZ\+s=0~6$mX[;ѥKj$rom2"lc^#"""""""""77۵"""""""""""Y䶈sTDDDDDDDDDDD*K"""""""""""-{k={67moo܌CDDDDDDDDDDjV'-Kn!""""""""""b5{""""""""""rϱ,aմr[DDDDDDDDDD9Jn=$|6111)z- D…,QTT'#ٳ~m۶qaqttLJƍӪU+֭Kbr-H=ʶmXv-&<<bŊC֭iԨ+Vh>jY3_sš8r*<qϟ??NNNӇ3fd;f]k׮m6׮]Ky摘=L<9}33goUƽ@s?DDDDDDDD7ݮ]]D2QQQwd|/_ΠAXdImW_2x`.]jYd  `ѢE&z"̙3,\tR~roaݺuiYϧ~94mO?DlllǹtҔ6@bbb*ZpM ;Z$;x뭷rurj9YzW}gg|N:Oɫ/_s=GRRʕ#00vѠA\]]v۷ooĉЭ['$$3fd9hg?~#Ft5p7| cfd)S1clْݻShQϟa)m `4mڔ .d֯_/2 C)kJOrޱc7&)))ܐ ǝHp߭ z;gΝL0$L&5b4h Mё6mТE vqرcɓ'EfͲb/Vb;ON ֭[(i&f̘A\\֭_~0mEFFҵkW˴iӨ_>ZʵXwΜ9sRVo;88УG\۟vU7nr_9S_DqԨQ9GnߎKJ{ĉ)dkDD ,QFL<?? @&Mjժe}{QbAD}LL 3fLJڵkgy ݘ.\$Nz斫a9 ƚ}i,G+oW^M]@ǠAB9~vQQQ@uqqII`߼ˊ+HLLB ;6%};ua̘1sIV^Mnhݺubd޼yz(7nѶm2f<==ѣ;wܹslذGzNf<1Ջ]vra… 7fGe.]#={iLapa6oLTT...4mڔJ*a2l9W(\۹^)QIUo%`dSnw {MGGG:t… ضm[b6lHHHHJbԩS,Z(%yYo~_%+B ŋe[…;w.קH"L&"""صk?<)mM&6'¬fȑ#ƒ/_>*UĐ!CxG|~JFϙȽetg mùŚr"'8qb5*I3ga\dϟ?L23/^۷o?|v''')BDDDʩK.]>}͛jժ,3qDlquueРA6Mމr7pѣGYv-'"""… 3g:tcW^̀$-J߾}iݺ5+V-۱?sΥ]v?xmc,X1pd׮])SߖIN&MOZLL JlUjy#GM6̘12'm۶eر)#Gꫯ{[8zhbⳛMNl۲0W! LnzWxꩧ:uj6@@@@euիG&Ml ߦW^ 6,G})mJÆ t|Iy(ݨQtoKXL}*̕+WHJJ"&&gg,zg\[8<J*q!rN}Ȭ^H2u3k+pUR]vaX %000}N2<X`˗/K.i۽{NwvN$YHkV vkbw1{l>CL&_~.\8qXl6EO>}6ŒrN#"""""""r_: ]vѨQ#na~l%DEE1{LK;08p`=k.VZEFDkb8sLm;wW_e̘1TXOOO^xᅔc9|0cǎmb=Gc7p@ -dY+%?f9Jp[Cٲe_>Vbڴi)zw(Z(3c ,Xo֭[ohjlݚZ dРA,X3f4۷\(5t٦ OOϔR$m5ʹj*5H^KߌΥ+WY 1116wwwh֬={$>>~0w\֭KѢE5CCC0`@D ,i\`Nl6ӺuL/v)""""""""]vaoom˗]Npsswޔ+W0ؼy3#Gd׮]#))PFɖ-[Rݻqqԩ,ШQ#<<<2׮]9#FX(U 6R,I^zǟӧ9s ...ܗ#""nIpp0Ceܸq7!C 􈈈\)hpss]v+V,W%""""""" QR$a}'m5acV8p x3nݺ5}͛yi޼9ڵ~(PWk.;6nȱckX5j'NӪؼ bѢE?sNf m6...Sve 'O… 4k֌*}M6Vlggg bu2l0={6L63~xM#gfYXfΝ;wKi$L_o!K4Sn׫WX&MĤI2eJO}uUmhvr)oK%']]]4{{{\2?86l`:tk׮/_>|||hذ!-[H"?r1H?hf3;wĉ̜9l3CSNc)W#G$&&7x'OvZ{9|I֭[G}įJٲe y6PPP#Gӿ[-| Xx1;whR+VgΜ믿k׮,]I&ň#ݿO`ٲeՋ|>>Y. c3zh-[vKl6sҥ,˙5kljժ 2N:蘥m!s#13fZh1 #<>___mۖiY׮]c+V :^1L <87 mFxx8-[ɷn6af̘ABBBq1 GGGF-'Ofԩ8""""""""Y6lܹs$Γf<<TXVZѰaC hclYs;3ɥsVZ?+V#NVLL’%KXh/^xӇnݺ啣z!BBB/Xp!gϞs!"""""""reIDDDDDDDDDDD2c$* """"""""""w s^ """"""""""UJn=Gm消sNnر#7܌CDDDDDDDDDDjV's3邒""""""""""rQr[DDDDDDDDDD9Jn=Gm(-""""""""""%EDDDDDDDDD䞣䶈s{}^ cǎq:{ZѢEquu|y]Imcǎa6 P +ǏB y]'KSNq5s+,X r%mN:lܸQR$ <<… 뼻RÇqN8ysJ=O3CYܾvZ+%7c{lӓǏu(%OOOwwH\>|^lLc1S4wܶĉy]vttXtY,f3p}őؖλ;'y.imLc1S4wܶS]DDDDDnϜzdDs#hlָ۞Vcph6vSAJVWa lx E껋zه?#0:{M զ%Kg9*iN!3XsZκe[1rtT?@ o/J{VZ<7up14(U%n)Ul{ D%{NjҞe\Ow~fX5OC~OMEXo&දu-}eJQdIJo}Zuƻ&{ƥ_dxWTgiO6u7Xu#6Xb+&Ѫ)||U{`37m{M}G岞x>_87+wƙq)]Jᢥ """""wM^{=17Q(!3'`gĵ{5wk`WGnGMiG6z=kߨK{T⎯`ˆ7D8xRC0BX X8x03D\3pF!z%SDLOVqԷ= (fNM7Ec_%8|Ɍ[ +8s|:UMZ +ǼpȔօr}<•0}IR")݇\P^ecv粍lЕ>s#O¤qI ^b_9>XіT??׿ǹm+<3'K;}%zz?;W&\^cY@^ǑV%\#"//} Ddy|=哮&Ѓ UD;ЫNlh:n7O:9﯅霍<;%Anniz!=~?2m1BCC`xr)L;nMyHΓrq-J!,L6o=_1"c5Pjx)?IYxStjF] x.CLMIG-[^T;I;^,W=d)+ QnĚθ~tnՂwcW)Y}*L<6sH*t(#-햓f^miWr,SF]FP vԹ⯒_?!b9ktiT2Tֈ3d{aYbSIF\\Xŀݘ{֟-QzhڶkDr:|-?=\2 |/侰ûRǾ}ٲ 3jWۼ&uDHOz4oYzˌab<>o7co1HYx*l[ϲw^,ls)Czuߨm¬O^M|/Ffڇgn| zh/\eVF$[fwb7emrVEKsOP/ږV;cޛ4R NmGJe*{9kκ OҺQM|zRڳ<՛OwV.$pjkoӆ;+' ^4^OrT߆qm:~1|РX1w51AdLDnIpۺ[ٯ~ޞxD/lq>Su7Y'\3TlC=&2eiْ6e!4sMt SPIklyO^oɵL:'gfԦAc̦\KF'o天)ӝ⛌Q;L$ !w:5jyOJ{zp_fr1tɟ%T)WҔ@ g`7>%xLdo"X:µ#,k^__J{(ro[B(K©LdMޖl̔=`l;ů'p _OȰuj;##:?j׮ 6jn8F3~T;1/ cyL>;GmI}z9g؈#LV:=L@ \aL]5ݮg3V&usM}s \O=~cE 'TV-wmEW4A%Y=(_b̘w.^;)<߅T#dǓx\n)m"nagXU^=qٱS{b+'p.sǘ\xg7.1zʄ4笹dfI߹/J:p~ZI C9` Iy1i^.3GÁqiL0|cLvwJ'R&~!ŊY>}X󼽿 o~8E9w,Œ3ķqü;ܒ3r,2x3톥Ǘve38_,g~nK3뛌duï/OghKƮ0n.&k)^<,Y7Z Mi۽؃PXq &a{y;x@TNn??LaTR:C$.HèCaʯ&V&:z.x곢w_EKyjKB4'gJ ls}>9i8|m4%isNH?{vOctx>Z`4(AD6zOib иESjͩ-bz\N:͑װGt3J)Ӥ Sw֥2{Mq,SM4:#XuL^|X[(l|ӏa+6pkBԅ~?z-[\AL2zh\dOlo;i&g MyPE\?*%wq wb\U{5:+Yoɻe.ݝa#hYwug'i]̙%䣑up|sھ\ /%\ϧf> F /\\ҏ?:@7}.xލs^ IL򹣄.Y; (Xd7*-нSt NSjV)ÃoIeXnVCz… 6x2VΥnϕ '۩|/Ql®x KSSo?W)H^H?NHmGq2^67zCBY[B% 3J&?d%9FVy4p=_@"? 2}cfns>n"58b"Տma߶E<zҨѓ ?8LEWkg9;Wjr*[/Sa ?~;ۜf?zBKeO7| qv^T,?c;>V(;؃%<H:.N`AYk0btn &Wz?J_ӱFuhZשQ Tk܅ѡxᓅTej l|5]3?X{.…}8mEUu>Ne{`)Ԍ gReQ>DzL9gdr)O9w;LovJ]DPl( bAXc kb.XQ(bQ%%*XK&DE `\܇e=gϜ{oΜX7͸Pr}kc"r{y IDAT]N$Iurl3}9`s߫~Gfsǟ=OվEV_=}5)* > SkT/%ˇr}=񥜹ayww ۿ4 o<">DtƎYr+goWM5FL[_l> +,T-; <4! fȴבA&=^]; kg6ulpy{s>+?f|[9{בGUGn V靥-;]7_/fҏ;K|:~תgfߟnSyoо Ƨ]Xr^{|:}/?رy]J%Rlpҝ3fXc&MJ9w?F&>w'W]1uunj%Uiݵ<:7=psXќzyr9i̞1_{O:fѤiSN]]SW[N49,=l?r zPjQj޺:#/yq&FZG̘Ϻ:/ti}sc2ϗ[fbBYe=&G{+-̛w%0}.,3RRLÅuKUU@!rY|{陼0a;9Ƥ&i$)If\R΂۞|=6{_rfFTu4gy$(-9-?Fv浹M3ц'W.<]{TfJWݴ:)MڥRICo3uk5k5E{=rew˸kɱ|Jrf|%{WCn{//Ji??g uor׹l9ϰ/>\_aԓK-edS}wya5ofk?ݓ1ܖs~xԶHUOk~鬳\pz&عgUY^G>ɘcva˺Go9W=(]ϩfi,kS7fiRlzKJd`/lrl|ssSӿLZ=}OPnնd8OǨ6O^Uv>,ۮ|/3}7mJy6_̻l97_-ҽkyNm.i[$-ҲEOmfϩYbE_mxi,2w厣^΍y̷U

9hOs[ {/w^TͿ\_9jx~w}_&/y׍ɣO<{<iRa6?=s#=zQ^#_?o3,;!isyƪ2r`<_߾HM^Y:i9_Ǵ)_twX^)-KM;/f-Mo̙W#soO1[c[zu;+Y4cӒ c_A91󏹒nGN+?$#N,o&KgM獯S=V-NT{,+/煷˒ӌKO+WGgyԵG<3x\m >hwt7,d*mR#WK3c調e]t+ˢr=I;,oؚߵ_}5IK;fOV>ve|֌wxns۟F]_ꤛKm=ӣ f9x3jߑy㙴g]VZOZվ{.#H3Ͼ6_-wOދOLK.Oǿs疴j%8ulҢi˼؛)Oegs.-kvM_6K73;nt_k;onm2q)OYm:/Gom/&=c2.w8<^#X$5'ȅWyUg~ ./#v:0yi>yŌ:8ۮ:&d333zrV罼YB8s$=kVrȆYuK$I&[Ҫ^)3[̥zʇSreɰGd&iRNJJm;Agrgyf ; )- $[?^V9,[^~&Tϛv/[v&Kgy3)Wy.l /<*/n4+۟ax\z9?2Ot_}]\}& sIF'T5p_.fs&g1g\>I״o%<ƺi|p8L??\ ^엎Uy[夕&-&Sy/JyS}^n^ϙNa ʝˉ-5uOvMM1լP=F=WɼTjc~s2k-*yd.|ssA{f{e? 3oUnUѨW.2?yg7!|/I΋d f6$-krMs'OmˎY|]dh/_x|><4/]{JIJɐssl8Uem?o^a2~Butꖾ[k)V%Yp}cuNqn.>즼YU,l69rlb*qzvϱ䗃sV{t"3}sT/S.3ʜ%Ւ`9$uc_^^}?_Z2kdsFm縨I댙rs[ +|>;iۚgKfSՋl>^۝S.?>{qҺK}~6gvgsf-~HoZkKm:qlzƹaYߖSO=Ojzllr}9t!n`?6o(U?]S]beJ{ X;j߾}$j?_y#W7vE2J֜yYtӫ^OW* ,ń<~V-Y}\i8恫:w͓-מzm>u`_lξ={?+|Y);^3Z)gpΒmeRn·_IEklT*fL۷=|O~QwIRÒNoJ[ksȿ1Je6V.t-M^WܕfpלImCCsnYG2]GjE;}8ܮ3kڽh[m0hOsSi6[{^kBUUUe7oj4OzGMx$W?[H-Ӫ],gpUyz\y>wrK"zyiyP{y4_(F,IUwl/+I&>_ >!o}^nT%j-JZ'Jy=UrljҮ4}?<4>V?%]vZW2eG:nlϷyϲrɞS읹=}>$5N;';m۬vY=6pl>d4eP_Ų$vKf5dz_]y[1k tjmnȠck'sL߶YdB1|X:oHnWw3w}fg w;sWfbVz=u3ۭ2>?ݻtH@gWEiݕBӮu,'[uQ.>fkth[I?Ӕًe֫e;Kϥy#I߳gn#3_/z =埲[vRNίZ3}\0m6-vR~}ٶ,P6-rp>2ۥmǮѹ/)[υ/y;o-6mRezo^).|VYy_{ \ʠo''NڣftkOwI>tZiTOY\W_n4|`lЫKj͗ΰ_gSRy/Y/x:whm;f̑ Se,h\Ų$n4pJ ͣ<'VG+d5OGy.ߜ?\5*/RIE8yly?kŹ_ə;s_޶zRJ'zvtm~ޚ1*SF>~'rM'^[o9x}`im47pAZmhAyc=758n; [~g>lvx$i-]viv _/%1xd%D2 1OV?ޖ!81CNy&zJ9c]r]Fu,G^r]?x|#r;s3GRiKNO{6k!m3xZof<~V6uG]v'b˜qƶY42y T̳ΦGܗvۏʵ7^0uq[L_{Kn9{r.:P\,Imlrm]I*NefAKe%m I>~<]/5Omm]ʕb+g݁kYk!uthz|~[FB[N~T% ۝庛_^vl*uuu?Ǡ<))We]ُ]w:<+3*4r_mjkkSJpd]t@LYSɠ .?B<*\I*۬.7?=7W֦6˔j)nݤ󠪶JiY}VIVHǯ˦wO~QVhdrvp|ZՕ&dCOJ20w,/1:zxm^չ0܅ U[}֖~6Yc٤nb&~Az\4|el|Tm=n'_xb6PY3zgx:u{.9_Z$}.ΓUËufI/Yq`nbNyX45U}qXe6"ˌ(>i_:OsU:wY {uTTϗN%ƍ阩ʘ%WMf̳m{\q?[ڼp9umYx=s 2pޯCz?֫ C!Tεg1/d'ʵgW;+3=Z4kT>Mm%I))U7KReLɲտyҼ40PE_+{Դ?]TU%lv{MM>KMҴI)Еr*TJiv΅OJTutI.sT{GiĴӞ+.RW IDAT띙GbD=gi>ENNo$M$r]S#=ߩgn')-mv^+mnflѢ,IR^lt73i|q\ڹsCCq:̠4zO;T&$JebYm2dA$ď<2Sff4'SVO,sdZ%v?0LKW<~FvRlzk3ꪔk&MUtr>,MM)w=W'S3?7x<8vDg\evfn'4#Mz,|)v@\qIkU>ȝȕmOiwܛAֹ-x{˧Wy+N +Nfk'S74F0ܮ|#,&fψ!grfU_ʮTd?J#OJ*/v[&W~:p}/|YuPh{>~ jӯT,~ۺP*-T]zsξdiYbyef02UW\eЯc7 2}:5'~!>Wj=˹̾{M R$i^;mp|E]ɟ`Y0ko'ǝ02^WꜦ=7'&US?Pl3mY~?7&.,2&r~qP۰M*\F,7ڼN?_'{H~5qR>sBlxI٢S)JrzmqC}fW5xu>3ԽWyV]g[ٻLƖ냓~Bƨ!w`~{ϼYutSBu?vlVRtb>΅_}~RiyoN<œ9C3b~[ɐd'QW9ռfYz:ŕ;;fso΅?2xVb-w}w7j@߹oV9`bN~l~r_yK.Ϟv5lK֭[CW yD3V* ЈT*99䐌=zWhdf7|:thFRtARTo 7 6,FJ2) :thN;hDfgِÇ06lXraWfpU}qSOÇO]]]Z՛mmFN8!#G1=eذawfp$fe>lr?]n4"=B Ј(6c 0hd{vTNǟmFdn,K27/n4"?pۚȜ7n7;e76@#ѡCJz)*r9:tUN<_<󩩩mFgϞygSO߯2:t萚ٳ^?pիWCW{aY G @(6#ppnP8m G @(6#ppnP8m G @(6#ppnP8m G @(6#ppnP8m G @(6#ppnP8m G @(6#ppnP8m G @(6#ppnP8m G @(6#ppnP8m G @(6#ppnP8m G @(6#ppnP8m G @(6#ppnP8m G @(6#ppnP8m G @(6#ppnP8m G @(6#ppnP8m G @(6#ppnP8m G @(6#p4t{}ٌ?>cǎw1555YveSg}6Yc5굜'x"=\zQoeX?~|S7ƍ2h$ƎJ2ʪOmFdNMN'~΄w=+JsšHRspnP8?pۚȏepnP8?pۚȔp{Fȑ#ӪUM}߉'Ǫom$CgaÆ͜ͅeIoY}衇T*㏟rH|˒]{Ȑ!)J9cRT2dȐ~2pSUUwy'w\CWgmFdvgnO>|9) :\.T*\.KUUU)СCr`*ஏ\.OMMM.fn4={3`IA 2k֬lf yZ7n\y{XZ|l84hܞ:uj (4Я_<# }| fšAI ,Ur#c& _mxxT/r;t ߟ dV;yA_,2svKF_%oZҧ./vR~Z{fe,u6w|%˒|7O9hiA7Yy19M Cl/2ocWe=|sz^#zۦ|=S]^9y_)R,0[ۥ޲}b6aքӳƧoޔW N/~o,釋y}kl8\sϣ)wNufر滛_ kN}fOʄrct.whpUռuk| еYE5i֝Vo:${nKt_uސz~ּqKsw3/59g,n}ɠa;b<&D[WSsy6Ss繹r mo+3rTikyѱ:'XL*y|\ސta&Tp/rVnέ'n_x/` =ti{rf&D~)jy2fٖ2Wd1W瀕.Sɔqms]_|P.Rf/~]ӶetMTv{)vL+/ aP.&l2)T>x:sZV]㦦283Z/ktY>7O=)JOl4m4v*+EfM8-[5O+eo^dUcckwIm6jtӫ+gGmMi3koils3l%iKT\9mQ6l Lg۹biڴi.+É7!Gڴ+u?G/InIcp0${yzoʏv ,*m:ʖ#o>N]^l׿Wo[je>z+Sc6kӢyt\c_22u~0!7~߭]Znͥf+,K/'"z $$cFɯO$߬t5xCky5O"EE%sdpWur.?gxn:ٯrգd;W/Z\9bg`2ґ9r]9{M$;6V?^;g Y%O^mZ~humcvO i"U6ʾ?y*U1'X-o7gdS&G$w_MOTo$iU:7#RfVJuQO `ib>c6l2wR\54\@Nr紨{1?kǜΎ9oe9`Xt}Yɜc}N{*]8=מ5{an9_r^mſΉ4NIn5{È< ui}9#J<~ɶi9~~h.8_[YL'oW򋟤یyYY]ޝ<:OF^ya6W(?Fޘ[TmϤE2=6πN3qeւ>ъYm&9yR-zT&ы͎*+tʊG)̘IT'ju3pӖ~m/JnL{{Fvx5>W騜qJe:/Ϲ~KUJ1{mWf;۪Z<5my,>w%4)ҨqRԥRW]93jrerƘӷC~wǭ+RIR罆E^,@,p&L/rwO"SJxtjӢ2)YςE%E(],|qIT]?V%̬_$5/;]M?M2kLw6[sq?yFk ޷o<,CO~6O9#\%-ÏKT'?c0 %M)>|u9<ʫ}rWz표sC/=' _װ^>d^sg]V}*ugm[xcc3R;pt'gnǚ6kV)eiʓTgcO7ORSv(h><_іv4͚%LHPzc<DbZt c,WTq>P=m^sO +Ug^5Rt57'ҤYL63E1g&OVV]>ggjvis#i򹯳Iz뙜pytP3}}IFłs)"wf^xL_yh{M#] !l\H w61'=,m>4zMՔ'}3bȚii& MmVͮ*9fYٌi>ة:Ud3r#sSԗ2Ǟntpءw_ǚ3e٣υڝ~~z¶g~YE%\D 9;z~9iu6㯹=9;Ny&7t_=\qg.lÌبS͚7goʉmhI&k ]=Su6w|%q`1r㭯IgsF_])<\~A%MtΪ J7N]uΏG"'~L8: X;w'EޙYt?zsկh}aՏEqsc=3gN]W/1߼(-e[*I,p[no}JN{I.Y++d^oK56휾^olR5hPiѮ]{Ye^fٴZhhνoDv_ܨP=4;ud~v!ji\-:3wMZW5{86Wlp3s s7䄽rƖ9f)I rϋG;ev.{0]7=7>qfͬIˎ{e珊Tw]Q'{Xy~.{}3j2m;֚3⺻SrOeN v֫.=;X Y{Ʒr]vK;vm7{(XUWW{f1K/;СC7IkmXLe54*fޅ< 'W16hn۶m*J+:J%m۶]>c2/ kkkϦgϞw|XrU*L81 | `QfšAv>}OgywTնm֦O> | `Qfš^s5PcX<_ @ @ @ @ @|㎆Xdߐlv˖-:`Ys(q(q(q(q(q(q(q(q(q(q(q(q(q(qҩY۴iIC^4Ӏ 9.iIENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-contacts-example.png000066400000000000000000001644071501654372000227220ustar00rootroot00000000000000PNG  IHDR)vUsBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeDo 25 Mr 2021 13:06:51 CETU׻ IDATxw|M׽Yb$V3=bAR5K*hi_kU(]lUvTQUM$B=?HF#͹~|{w<L|]^8 86Yx .[@z&lZVTi|yKښM+EDDDDDDDD$A|\%֭KO{ƍam*U*UzTt4111cVODDDDDDDDD0& @ŋ?~p8V&3\Jsq~X,Kb!66h *T1o^0k^o8y (ҥhQ7n#0 ߸A1 hZڡ֮adyL&L&6|iGM %EDDDDDDDD (h&,]͝5ZhX,YZDDDDDDDDDrwrV-(mkm6-""""""""`3v X7s)LF3{ ZhS1[ ] E/¶dυęuӎ9p5DC쉂 Ú0\L7%EDDDDDDDDRC\"mվ&FjgaU,%J\x7PDDDҥ`IƤ:9Gx~uRDDD?̾ޙ?^|ʛBXB"Q7H3E(z\""""lG]pv'%6ɤ]{|c,?fBd4)w'rd:6oG 䢃ɂ,κ!J4Hΰٱ-"cǞI,щdJj%!34w׿k Q;d#) 67>dL?L)("""0V<۹1UݜA9sAL+K/ѳi_+flį6t"3lG( p{XrkN/ХAUʗ*B~h/,pv5Zɿ+.M#7a; oG9~DDDD$%,IasU cREl-p)6kfWTQDDDQ`ʇйQ{y-HgJKwl.F7g_ALHBmqusLD5Q)AL5[qю5zD\h6-bwg¶6ehܹ9 5n:?>!^K<ۨ8},[~;$H:I}nŨ0a opcJTK4j1GXR{;Ct3f9_~y.bR*и7#c7j#}Ӻо]OavNkm/wSM!<¹9Qȇ~(!""""@=EDDD6Uho5/{25a 9_cqi/bD*l"o^GL+8yKwvRX 6a$'.kzccR7U7Ƨw0霌 Cx4J/%EDDDf*qĦ3nOmkg@#;D/q1 LfLqٯ+I*7V{~\5gg<͟JqNƄ9N6ęEæAFGBr!*_x[;;Ź6pjD֥eCP.E+"""5Dbv.uӎNn4Ӹ=s2&ۑ˓<3.V%EDDD ?7x+MYkƱHA쁄A̖6'ک~cVaP(([DDDDҖ /9){ӝTa!lRd[WdE& sҘȣĚq߹eG|JF8{f 7[Ml]p"9x܊]DDDD-8'!wʝB1>CH\֦VQZ4C$Ol|E<ǜM{<\"0;ZC﷟N<_ubgD;:1."HW8O?N_8o0g,#ܠ '7/+byeiSa<7aT/#qP &k]c뇩<۪.JPc xoOghNӠ!>MZб0}5JsSD~F|JW| ߜ:jˍ:ś0X<1[^ģD[7?72T+Sn|yO%qP? 6yY.K| |'Lz'ӟ[^\8]e;kHM%̇g-DQT98EDDDDDDD$d_ѡ o&;V㾓2ƞcQPupfK\ T+( , WR ftcv1T~ π6qa`OodY7:ή=h~F7u+kkWcxbImsCIo'-KS1f.bŁ7[Tq\""""""""ȾD#)[}aP|VT=yE(]I[8{xR YtH03Qnmp :N{Sg`X 0|MݩQ.nI: ` V `>N4/N~$6fLat""""""""hDL&X,QņeGB.x2d7K{wCKѣ?1c_*,k8Tu 2i|/Xŕ5gtXyR>ANڵ077*WmG+ZJax{]SJpwRi*WQDDDDDDD$. !{Tٕ'^>;FqTY̯_ceIc3|Z~θfo8! W@*E0]1إvxW%>g0ц}A1:Q")ץmPϋ(^$bX\+ߛ `}Yԏoи|^.mϤ4'y*EDDDDDDDQY\ʫŬab`|8Yund[lu(Z߹Ox\ظu59#'/|sz>O# {?M4Ns{]2oB_]1jFN^3|g.ٍ?c,|7O<ѓS&=ynɡES}ycM-K-*""""""""*kǷ6vڮlD.?]CfW\Ʊ9-xDDDDDDDD$[*T1~~Fy)(""""""""""""""""""* ck/^8DDDDDDDDD!fu#;ՉFŒqCLs4H)("""""""""fi03yGd"""""""""iV '222o͐!C(X` IMhh(dݜ|8_}ݺuc֬Y)R$ۏ .0w\ zK/[˼x"gҥ\tVy~1{ #""""""=&+kt֭]=X޼yϭ[2tPsf4 IƄDcVbccٷoK.e͚5ܸq#}Gǎ۷/5>X,,\[r%,Kf\]]iѢVZy7UmȚ ĐY{wމ 8WWW-[FÆ )9r$ .$..[[[6mZ7ns!&&ƪS|aI|o˛7oc`{NߝT#<< ث1AVϾ}4iL&5jرciРAd=m۶Eݻ &w^, ӦMݝf͚e(—_~$cR̚5 *н{lF-k̞=h6o?aے [n+**3gR~}Zjmٓ'jW^v<`u1io7N|.HI@3fL2{䔘lZjZtJ26h4dxyyQv W 'xQQQ*T([Äk8b0C vcͱԎEDDDDD$=2գ9͛gk2dHz%=.<<>H~NNNĻ{6fCfPǏOL2O:u7n? 6н{wZnXp!ǏOwm6qaQFsQ>CMMbhРܹ3S4mڔufQT)ssscԩL:5[s?ȣ%S}Ѹqc=n =?Iz|N}Ȑ!>͛yԯ_?]oذ!~~~,]7oyt'O>Ͷm2ԣ]v7777zž}t[nٞHIִB }a,R`A|}}l6sivɵkגgooO޽4&08y$;v <<'''6mJŊ1LYzH=ɨL%K8=,è3#![1M:.gϞǝ;w6}ޞ;tRv)Ɛ-6lԩS.\`ٲe /gϞG*hM[/PX{,X P~} .d"44 /d%IXTiܹ:u(CŊ6l?x{&""""""Qvi+˟?}II]!32B/ɓ'1c2P +a䮮ܸq˗3 whh(eʔ0 zj^ߡC~{;88PpaBCCǗttR믿ׯh޼9֭KW<'O>0uggg h+B k>}M6xbBCC+X ϧcǎ)vÆ 4(YH"ߟ֭[IB2 HV~B `ڷoʎ{[vߏEDDDDD?}{ej'IX2&!i0gZJy6CIF2iy}ԩmۖٳg3]v?>1x)^{58pOONW,phFf IƬ kbH^}U~if̘,zѤIdBBBxwӧ#FTl~ibLJÇӰaC]'|EDDDDDD$ 6j(ǒ1$.ސ]RqDFF2ʗ/_ g[ EרQ/SNM֓qܸq)&8ۻMKMfybQ#GRJY~VZs.\8)=- V?plK|~Fͷ~˨QJ2,Y2q߬г9ߧMPPPdF@@Çg„ L0aÆb̻W*ǎK[P!ڷoOѢEX"""""""!Ss4& 6 #!fM<>ͳz )K7֭˘1c߿?ر^x͛Ӿ}{ׯO|y&oe۶m9s&Y~1c0ydܬ-[j=:1d6#ɉ}ʨ܌\rf͚W_%{iӦ͚׬X{_v 6ѬXz*ƍWS^zbAMDDDDDD<ck\~=]3a5uTO 0'K֖]0ydΞ=ٳgYhp{#2 kΔ)SZٙp!=>\tsV tUTaԨQmۖUry>ԩȑ#L1[nԬY3 ׺uCRP,-?YfѧOj-""""""9*SC籤-=I۹sg}zI1 #b$=o޼T^= `L4c׬Y^z GGL#Æ Ύu2b7o̜9'2qDfΜ=EZ.]t vvviޗlmmy.] lk]|9qzbʔ)L2ӧg]nݔdZu:$$suikCJx'ٺu+{ĉܺum4f̘ALLL%Գٹ1dGcf"y!***[lr"!EDٖd(^8'N$,,͛7\j]=ݺuˋŋuV.]tO3lՕ͛ӿj֬ċHggg9ydbݥUapԩ7o^:vxm۲rJ\ ڵKOvݻ7f۶mɆӪU+w{Cl"""""""kҧO-[aqtt駟T I./oMX~=111ԨQz+CftZHEDDDDDDDD$5@DDDDDDDDD~J4H)("""""""""9EDDDDDDDD$ӬN4ݻ7;ՉF??CDDDDDDDDDbV'cbb3yi14%EDDDDDDDD$ӔhLSQDDDDDDDDD2MF4%EDDDDDDDD$ӔhLSQDDDDDDDDD26G˙3gq!!!CH"8;;S|EDDDDDDD*J4J9s f/pY*TۡW.\֭[DDDdW<ʟ??ƍJ2f:um6]w9(-*UJTzy󬕴>EDDD6V'/\@\\˗d2a23.yHa. BBB(X-3mrGg1bョ0gQ-cp|rL"ͼ׃fSl]Bd?u]=s{_L tl|םmr;IthL&ؓUEDDDD?}m^GT,F2n֕6WVe|rK%~~o(}Pέ5hyv0~ÅɯԶS bnf41_ wEy1^]ndڔwsDt+G:6l6\яU0ORÝRnָ#ϏS1)6p Jx{J. "~}ZN)Tӂ^Y>؟Q ߾;(>R}={pi(S%JP;ԧUO+~ᓑhRn(_ɺw̦505yVEL /f Uquo͜Swא5Oaig voύSߦʺV O0xA 7r$lmRP'[^DDDjٛƹu{wgN43@l(A现/C,xzx?2@L0g=sdpmkzzeGٹƖ yjcͺO|)K_5LxP;7t `7-9v.[h5p*s_oA7#cb? S~ck}Ƭ(X0gz_󲈣,:kf b`ć!M^~X;E'N[5. n<i)Kgu>r19I6O┺q%!vJb[y>\ӎ$I?7ñ]+*ao=ީ=ǿB( PjW&|_gonǑ Vb_WVAsX"BspB<Ӂ37I7+_)TpM &gnjA;ǟ6At6Ւ =~wsz>S{`NNE49q9T9t_&fR6EDDDZ?X~{eg߳|R;0b ~PUv'|kPtjx?%;):7n *Eb%>eğ^ūO^rdzքğXL_x­\u~N#|w' K<֡;ޜ€:E^[QbYTj#>Hnxq7ڼwN[γ;U^vQӧM+Rέne*ӨhVHs-fؚ:wzTnBq8 S_NF)Fj2`{p6-^_{0:Ke?˕u jٝ}7RҮx20xlcz%{9sS , ea<|Çase1&zd̂ؼc _M#Av }hn0sQ\xÇsoQˆK_hNb&;Ю.^ݚ3~JpQtmрjq+厇w++I?QL٪3 c&s}ŝɞ`':}W{ykOGoʺ˻ÿ ‚^߻"eJQr#^ȄӲsZ[_{'>vZb΀Vx{V"[̚ _n^`.Wzԭ߈fm{J0| 1Gع7ҽ&3}SmْOga4sN65; [\ 'oڅ|ř-wӠ1;<1e.5#{vqԔΏNz-&$Gsiy{?G@ܐ(c& %T.{RKItBY2T-|ۍq̡8>­S4kZrxg"""""b Za_4}Ȗ^c / G?pa gd0e}SLG3J؜Yˬե|?՟& ~ž*o jŽNFOOwgRpL_&Cu}y!ڗM)9`_saO*"ļyj%ˢƥ0^R 3fwzR-V2y +ͷ/ybcHb/&hu pLe&dܻޒ #z12x3~J~WeS^,A|y[.MFw|Bݤ OgȪiiKz|򤴏MA 9IaX`_i56}؂;~Qh10{ޣu|;TY3?d чQcSɜ㩶g |1 F0#?QG?cb{EDDDO%S6Qi}[NPkn&*S~^U[ }(O-FS?W!>])e <.N /of4ጉN봳a``2]`TDe_S.w׿e6񪐇f!ɐ3u%O1FmXK m2tUz/y곦oCK2Ay"[ynOye`=>__|4I`ZCFILܟbƴr4iPЦ=1qԲS'ogZ̘!Ͷ7¦75R>)3et}@ckSZ;ڗG˦׊@Ql /x馞}J0k_AH|73M7y3bVuEdZr?R 7}K۹>G25KxMY;^s|ŞͽzN߽ >._:Mw<_lFYJ|1wTnٕ}G,m`<6xX ۋvf05oJ,]ChmR=cq'P?&lNd<Щ{/H䆔ss o{:,px&wZ07wXP };6h|ubϱ8:5Nycl?孎0)d:'"""ȶӷ~{E}2u4BG8~#Rwww=28S[TŖre0] &$ q'3k\;|+UNsIVM h'w/yٛFz070h~?r؋u'&24~.r1,{fYw([wS( #ǨҜf)vbKU4i|7[\ryWyuWj,!@m=06vlZ:%gX Mdv_NӬD:ըSbAm⸺UZ㮌 ,Nj,vOSk ta:>)cSfmӿ~ua.dbl)ߌ qG9{|e)/[k6$Syʹ@Tu q(7ܛJWgH/_L>wB?1Y+)?q3W 6{ /Ĺ $ wCGb(KBfJCSYƍ L•Ǹ{9mݹ_S<^eK/po?x\mjZBܹw~$!-G*^xVcܮ۽Rs$Li;?Mo,ai_YE`Rr+G庭y?}&v@Dbf44W:~nZlEwAǨأo6xvO_ IDAT:>o-/+NxjJwޛr{]}:D0}2pDҌӺwtUBhBJG( MDK&("H^AHG$JB İD~s2;ܽsgf{s3{4R`fJ&9c #,ؒTܾ|g5'M/׹z#m?9?eKyi\I@9Izy8u0{ϸw˗s )1b_ظy3 ;;0lW{_Ϙ~ bY %hH_Gpb: fXqi`CnrmĖ 3dž#lXόióSW3-lݕ~:)eS E,O9Ir| :Bb늛+ܼv3F0&.GPHnr)LT̤ȕYS6 ~)9QQOm͔ʡ,3ï1蘧-֮giǕsj <"N}?&v%lX5fD@l&3l?2;c=/;tsI8:``cGצ ǏyZOvsj\%>(UOq3r݁ 0LQ=Y-`FbP/q~e۾pJF*oZ6]*oSh| G34gE,7y^vą RYQqV+^ =>gz~kSq/^͚u maFGO4PM U$:زgْVA3eπݟ]=e.l ƚK`{;u_oπC %cRLu3;g7zE|}$_DeV5_.ޕIܯSN8:az&-c)uj),"""򶊿`PzjBWcǿ?>l9uRgL懯LK>qچq6ش+K%[֜RL`b= NG-g&8u3)33 ܺqZ\Șy&7{߯ae<3_/|%0Nb6w.Mf2WV^f ;-dk֍WErHOR#h9'Ĝ?׳ql9)K.0}菬ٱ{wGPN%>SL&-bݦeY1'MwY5?Ț_gpB6MeoײL>Ib_▂Lω}(qy*za |s]FJS|s9O=HcWߤ$ϓ/iqqN~^mI񺰀ӏbuM+KfrepqH9Ƶ)Sh[kS ķ+3mVM^i enL[pJGlƙqaks'VRIdƟǗL1I~۵qR*ENǔ>7lG`d.vs- ?S:l+|:~U/{3y;sf's$WJtP<+'OrGHR* OѸڙKVK]Ǻu^q,vfĤɑ7xaDl4yQ|$<ױ3a<˵Cbʖ\y2$˔ ?M$"""_RPnmƈqT+䏷=.ۃa=*кi6jkK34}'=#pśV"Cl _q&,dKicO#X:=\?u ۃ̞$6GQ&qdGI#+4 n[BFRv hґmk7#Z/!-<t4(ߖ+^8)cOz6ǽ%kJqlg1}c8Rž}5=Zy6K囩;闅|eP)y-&A/һ:JOLQȃ*'2C]SS hCݩ-x%#OTQ]ո~M{\4XJ夞~C\4.\([rh#><Jw_Œ#:~;PjGlhЋ~Պ2rǂ<`sڗG-Iѳ5^'x_/HVţ@e*$O_"UaO l1騙/532fV I QvqO^8 IsJjм IvKǧrcJ0cr5+汧H}g4I~?{oo2ޱ7?l;M FptqkZޏMcXm ϣO&\jI>pDby`2D@dN) ;ff-MGFZZ߶3- S-Sv2V1wl4ʅ=N=tlhr ydV{q|duΩogd:Lqpw#;'pqdHmIdXH-^±UT(û(ܻRH]R2C2>Kn>%7Yշ0Wtg4S8 ;.O*H(g /XȚ%1ϸ宔Op.ф0SKjEdw8hglz""""b7-So-^ i)T4wl >6隔 >&G]?W܆ca:OǍ'Uܲ9KtM=<O!4Q&9r ˇY9i"}O^!̔K~T|]:o5F>s,u.C :ĝAї9SD)Ʋ惱ܷɫǮSVIZKzlL~oفZ辰_ K|Ʃ~8؛FBKi1`,CǞcw.?k44󠾌co&{ۋ&YyȠO~g(dўM\-Fn" ɜ5Hf{eKj4?a X2=xn  GĔԙNegbΏ^:\HR;gRxǶ2_p2c>秐{8yVݎ+ݥ4p*/p&m@ M%~m,fy dh9x"_ T%cF\4y}SP5>݇whL=0ciʙ xnZqNGjC:;z{Ӿqu/^ɽUL$M_͠k8Faɲ:N6-B,fgiΧ{~z瘹C>_»z1xzc98asNp'O?6aNTKbQcss>H>ѵGu˰ț?cnO0C\>8̘co_֮]5o޼QHfȈTSA}(,_baÆ Ԯ]٠`ʔ)H|jҍݣf'{ݿOu:x]zهPuFznv׹oNцia9m3~Y@x,Hy{{h= Wg/%-\9ѓĿróac2"/7by6\IF]6:R1M5("""P)g 9Ʀc|A]}[F׳Sq:oÈň ]篋CnZVOye䭭CI^*hZ5k>TGu:x]Tϯ\DDDҁ*<;PխuQ=~s[g7ox-$xf5jػ)[nҥK.ի:]VW.ŋۻ"""""""[pa5c9DDDDDDDDD-@ę"""""""""g 4H)("""""""""q@ę"""""""""g 4H)("""""""""q@ę"""""""""gkEDDDDDDDDۻ.^ʕ]K:z(Yfw1D4G>)44E;vػNH{Fa y & 0t\D@$dj"""""_("""""""""qƷ2YDN=ظrx3[w%${4-Z"_zFkzנ^|,V{GDDDDDDb@レ MWl4;.^ Ξpwv%Y~ln{޽ K犳sb*OeKWy!mM^ r,NE"W7y%#m@qC7*kDb?㓪DF7yݟ}vvℳXEDDDD$ޞ-,Sԟ66Lf3EXLfxAHU ظ`~u^ת}'{MLEuGEt]YRA$i[#ud$3ᠵԸ_Ԣ`:_e1SwZLȜ )P3ݼNf}AJɚO/Rd(L_oS}%@un )eƸis  LҋUW s|^%xlyfPBCC*9&Oޟ{%z-?$[ o<}_s*e̞TɼLJrQFۀć'1bSr1ʭ5ߩE;oaƷ| ;eުMl쑌w9i=L6Mx&kk:n)B |-?v F?;oc&MYcO7G$^䝚 79{j?~ےӉ+/\5;8luۗsDzcbxXzn/~n/%y *u ̉S1=ʂ`ƔO>G/.=5:6npp׋ԇ LXtKV1' XELa1%vASPg0?shQ /{ &JK.\p!XwV)޿iGFF/+6ejw5a3PJ"m<=GgǨ*$3Epz@G佣0`#\(g#)If'̮jbZZzQY2~„5?Gu_?Q:,^ L8mH"ۚS~v L('Вl7ؼvw"# |SFL;`m8Df\W^oy2m ̰ج1琷kjcsF9?&Qnjqwkm!'8q/N)BVGeǷX|h~;]w0;LѶ%u4U7e3kZZ g w$w1t66pE9lwہ,=o!kK>ͩ2'{;|""c\:±c˗#,Vm2H)h6ɪPtw֭8h-Y1'D͒n130 p&cOOf9:+~.2p_[3adr"NocȨ4m' eS0v! ٫7 ,4_7KcG%¹0 Xb*X@cbJB^/`6NDIVA¢pv462&WrnͻEvLNػߴĔ5/v׳={LЅ"HY3]k&J xt8=h&BlL$_i5*W~ҖEUOA7=KT3݋L.'g 09d lƳƸ$<"= XZ~RDK7Php8p.ǘ(26{zvCL`6}r5]&|9?[rhV.[v3d F3j"""""vf4FwLJMj1LIwFa8r}b6\CFgqp wf'57'gb3D\EHb:y;3^¨,ܴ+;ƫ<Dk҉ܹ+]1˪zpeXȐ'Dܸzܽp^$}K^}״~On_73XRwÜ_c u. A=\SFtʏ˃  Kfa2nm)*ԨCΖ>f?;cB\KLiR:& z;ϣXq|VX"Kq,1Cn IDATѲhS;G&2KMpڜNS`2 lّw|LD'ґ.KfS"k~l 7>g~Y_ 2;Btt0(ȚB4eRrч;yZ|exZHܨQ+yB&L$ODNfЂ3kcŬ4(_{\zǤY)>azy8`ZgszG?еz^Rβ{o|,-F⒇1ͻNq 7mI;;jx_sSOjLC"5.^k-U©_u Vh@Z|nsM̾Y)t0 GZ3T fn_NଋKs)Ԗ/b6wYraZeн8z$f{'c1^vs|{:TE ǻZ=X.X@B6 """"b i7oZj׮]K…_TvbZXړʵ'W-6IWS:O!i80+`7lѣdΜEItIBt&ӫ#""""b;v|.+Yp!+l{Ѻ 桻l_mk"~ޕCDDDDDDDDE`v[n'2q&7VDxrLFQĝ#I~N"""""qſďaE,}qJ8BDDD,țB$djOb"$xh ]+pႽ nθz"Wf \'f\[DF9;3a>-(>dF1,۴3S;>^]obXJt͓ -3W&=l )K*l[ eՀӎ]{/pbNű ;2[[WǺ)f@NW^{|yU'NϟȰ:Y}6t3m{n~ns%nDY~;e4{XszmKFE;X13MԠV-4 WY?.%r D)('3;.^3qipq)V-KOYQ ~J|HG'wf)GCpKs'CkӈjҨewF`\gyBuio\O}_DI$(t/>+6Ow;XF?<]qJ\}ɢS y\Jl'Z+m-ш;㯩LZ{{1}v,9ԪN:;+ebvm5& sIFΝخϠ ɄcV(WDDDDa`·8W>}yfq.e_ҏt-}GߚYN1Q9ϋڐy,?BmXP6B'R+f3QJkqn^B-͚<^ f匩cfY=>iк\}&/FX4%wr>v#8>)5^Ͽe~w~i*K7nG9uՖ$R-Y'-;;@V~vV3}ٔ]S83~˾C~O|:*U~k,"""""b7v89̑Kf) HpRKtI ^HT-ꁨ\̰Qi9\bN*(L{)¬ei&zʱGp{B9d!CdWA37bc]7?Ĭ땘},Rz…F1|qG6ZՈhvcI>|S΃m"SspRbIxw|5"t$W0w&:7r!fOݗP 9y[RH%'9?Wڸ`? '9O'3 0dzrw7']|YIl\ص3B'ZEb/ݻ8ʩCdT,{,B?;vQ-7)}ЌWmM0܏?N9]<̃Mf k#}\)J4I=D%,aVȓUb})p+ %u)|k&Hf癷D^YC*Ė]N}3_{a7x*w,|Ӑ Ō"^r@ٌd Tf#tmص;7Ӹi\TDyS'_X#mRX|8t c[m݀TC:Q>U6W2C\kR5T&WȚʱWy$a[xR'5m [&NYfqO֭%ø ״)W4А.u;٣&v}ўq5}0c:NJeٸ'wr2mRf#ǯ`؎fo|CIаOoq܏6dɤ-o 9XBn/ޔfc[)<|0ٰ$KOjh1{ _0c _6#Wu$7Gb4 &qp;ez#E~3WA'㑁ϪI;n eA#34݌' UMTM&0x;ziHw1z2'Һc"*Hmt܍YՃ薭"b_/黢&˚'{(b8Wmt6LZ3} W\u#{X6 E隗.ӆ@{Zc{HuI*#au; T"0%n,dUO51# P;KYi Sٌo]:E^#^k~Ay0F]$ї q<:F"{RDDDDDh}.6o޼jz]@֬YCݺu_t*_5["88@{C$Ay$ oLoV|.+Yp!+l{Ѻv FDDDDDDDDD[4wę&y8Phۻ""""""""" 2EDDDDDDDD$h8d0"""""""""ghZ x4FFF.DDDDDDDDD}[߻!w:$!S[k֬ɢE ]I,xđ#I~cRUDDDDߡYEDDDDDDDD$h8@a 3e4H)("""""""""q"""""""""gh8SFk{Z5+%tgLDDDDDDDD$YOg^ƝC?.[0Xqq@H#.^h<ˑQ.kRܜpOQoGڻ"""""7vUߴݼ9CNZZqu4/F8;:'ef,òx8Y08&&YTlғ/1rWSjMJ+f yl$K䌫?f',֐]ܧ@2tz?zQ9]\L*fs45y{d77g\=S|3.V3-Sٝذx qzp~2mꙃnGDDDDD^x̮ӷw1j%I+C%{6-2j@iGԮ=ew^/q/G[掭G\>L]*yLDwqgʶet,8Dfٿ3ҙ nICpSwGLiH=Ryp2&fr8ބO`-t'߫L=תcziOdX,U}Ki} =[^7?K 7rsz7]?؁[=9%͌KV0~PKz'/sC/W!,K_I_ yOL\V1d> Z,#Nm!lN-jBxï!c3 WodH χZ7fܜ~wos'o㇩p} 2R%)KײnwSH #<*f؎e#dz.KYb6%a{ߪ︖OƹSXz- -d|Ƀ50zjl^Θ:fuz`Wc'E}M}߿3u ]7 #].^9}fYl 3;Hěi4p 0[̘ "k21 3`=GU8 3>p7leüMw@$d#uFup%9Fev2prۍkܰ=Z:m r+Xݹ"5ʥ|U8|5>}н~؊g֬L4_y\p.AzZ:;5;|v0$O>-,9c:2Fq̔<Un&m͆t9As9Xc$yu WG L-*;2m#:[e44hVM#|w@k1b%?5B\Hmv^[>xe?c$o}|:O?l.°m7Ŏ b4nZW;QDDDDD|>FS'_X#mRX|8t c[m݀TC:Q>U6W2C TM>I:U-&rUY,iPE 鱁ĵ~a'ct=h1e;6ʹkR\~nD^=Ak;J<78J->/IlkX|u`V S>kg\?M9nÕ0g2?nI \>Z=Yk|IQ2~_VɡTt9ID-.J.ޝԈ tCl&yٔ7ţM$"uLj;7>^[зr G?mKq}3nr|%]Pye2o%FoJკ87͆fv'_zUH.t4g5X5ڌ\=9GW#vAi2=e"Z`3WA'㑁ϪI;n eA#34݌' UMTM&0x;ziHw1z2'Һc"*Hmt܍YՃ薭"b_/黢&˚'C7I3&i8o+шq[A`&/+<2JL>+-"+;Q1hoPc%51O7ηҡ j "/cG1v3$CckSplM8`Mtx "G N1`ZH/yt ؊6*Tl޼y[kժR_v-Yu|U$;Iŵ 7(jDpp0.HH2ߘT""""VZE]WpBիWu2ę&y8Phۻ""""""""" 2EDDDDDDDD$h8d0"""""""""ghZ x4FFF.DDDDDDDDD}[߻!w:$!S[k֬ɢE ]I,xđ#I~cRUDDDDߡYEDDDDDDDD$h8@a wq6_g911aaT$&i HJiR(d[ٷÌg9BAqusqsݗ2U4eSQDDDDDDDDD.NeSE\6U4^e5h!L>]җ0[^&""""""""rTxZ.coα&ss$w&""r\ "Wъ<6;bӔ4'ś2vp;;QnA'>Aќ{}vWF7^ jH"8<(Z*݇1s,b)j)ը IDATCiЮ/o/NĮpmzvQӆjBD;6g \r=5z6n4ٻNd^<xE+ҺF (Y;Mes9XϤ.͖kz)םֵۛ#qP=x-dAn!z}Z?i~]ȏc%{\]vX趭,dЊb,3b s/,:Z>?JJ'~ a_F3B])Tf˪=gƛ-Bȟz,æ,e$2mAL-}-/Nmik؅Fгn G>iGxoO>>4}\l~!5'o/k#6KCѿαeLW!//MaT〓yC;'/JFޘ 2sX@ƌ۴YS&sd=C8`R ?Z`Ǟd<)$054=^h_{ukN8'isc6Oț/YPnYĚ<;y&sʘn,}-'嬰>3T摗zK2R;W?LdeV{xiiEu 0N⫷aO s6wa-xRiS"d9IF,HZemn|1=nZ>nc˖TȁCZ?Tت f_KsJ)y#WJb>d#>m1Qn82ß:1^s`ڵ]):Vh)ڞWeڵYx\uBS(!sh#TipEtU6r'oKAiqϱ9T5#4X,Q47فBFf}‡wۅ1|>”c/E5n~?_pԾԲddn˽{ӨB.gnOR q٤'z5T/T+V %W9͇D֫~P7mbJX|Mt4[ բV"\DDDD_oWγ+ 1g}ۓ]X́+eG.{7IV`bTK`Ydǂ>&;E{ѢVyƒR̽)odWhv1DDܩGHuVLqHN$4ȏ["<7g&7DKHټ2XUXE@*_?i"""""R vјn]"H9q/59ex:r`£&gij >w0\[ٰR2n/YVKUjF٫JwkF|ŞslY@F. | kVM͗&X-WYuoGKK# ;^=QݲVL>}2=Zjp]˞IcP8:vwğ!ѯU_lăE(F/9zF~6)`S )Dxf蜃m{~K-O:7,O=}vm}}zw~Ć73͌| w>MTwuaLv}ؑZ?zg;Lrg`e{ƈoiLn>}hI>8E)g G5݈KSB{,BwKƲP?|C cxjTߌ,6o}~ǰ&>\ Ҋ7~[L1#0z>D՗hT:kyzMmȳyN^M!AG*ſ5cut}ƛr4M|cX$#Y<#9wPw,o}G8JU%(;|g_fa $<e|p,IK4r_[ĠȖk^//|ߞY=B7I+Am:c'k6)oqʖt㳱|`X(u涢)by_NiY|xqDft/GALFCXצØYVweÛ|l̸M9"rX0t N)#ytA=B(_9|GVDDDDDf-С%-|0o<:w|('Gێ,Z3بv"5Gr={:m)hs%66GON.]K.4o #"""""""""7VH\ 쑅O"""""""""rٔh˦`DDDDDDDDDxrt"""""""""R ŋ/h\>Gr=+"""""WZ'۷oό3xlxx spbv8"c 6?muM[4F- <(&gz*!% ; Rn~Qa\S rmXE)DDDDDDDRƨk5ȥӹ#""""""ruRQӹ#""""""r&˦f7""""""""rQDDDDDDDDD.*2[h17bO5 (Bx{޶*'4Z/S|ҙ);0s~U  $+OW5֭[GJJ IIIPZj, s~9M@pОś-]pBKӾl Wဲ7""׍2%|KqbBEB%|ג|'0{Iq/퐙Ͷ=ILZv/Hc\md49<+ Di랅<ڠם˟x=,ݽԱ-n]ƹ)LL7|/e,DV(Jhj2J#̲ظ~y:XJqmSQnE~[0zE6yEaIFʣw-Ϋ)IIL_7؇b˟$%X͢ے \XT2[~&^]]Z$b ܼcm͗3_,sA WJE=CZj:SH(3mJtew"\I^N\vdW6}KQ 膍Y\`ӂTJ~L% v䟱RAzӯ3aIܸ֯#;n,v+^"

Fa2Jm8r0]cFqeez KfǙ~;HqZ)Y6(Gj&%Z'GX - !?=7E+Xa.Y􍼸5Our]qV}mSOV3EjD2VLrpUq3k6l,Gf/?HB 4Ygrz>FqW{+U\ =ϋ,X(1`78qz yO /B-p7d-O/L)(""""r 2_84hnrdx<#cs%j+0LĴ5 ;w竺K}n:Q’aTv6.N qig p<追xPN@"vN~{g9{Ѵb0O/=w;KrO"aw1 |m6¾ˊ%}in>rVo_60㕹B}0cQ23D GSyy&Okɘn>fNE&lRd tr9$usơC2vس2 *V(]YɁb>uZf#ǎen v}դ=)ٸ<| kҹw~pTy7E3L"OYJu(xg;;uG"ߔfeu(oYJzެ^eRWS(<2|{9F3nEYrݱ^[qz7<7Xv,i B9N^s<(D-|v0wwxI%k_Rʹ\_LV6;U/dLa_CٜODDDDD.Å/G>Cr1hР]b qe|˗K ZW*w=P/Jjb dfBe,CKS,c[Yc`ιX ax&#?뙇gox.x<3wː(V֦!9Y$Mq<Ҵ -[/n98J1U%ǟQDCp4}8Q#`Irr؛na;—ϕH4ېO[ ͏rpPc2ne9>腁״3u} [3ؽT.M,9En`{sɶxo+HpK? ?IBJDMb]K3b<mgLvg0TRZ\~؟Ş"DұZ%E=_ORVJ"/GsyЪa8AnHKd_!aA6GdHÄ d!(=bI޸xEb'lR\`rP)Ћ!EzqZŬYH-77JFһSu"0oo6YjT.NJZ(-""""r :C|}YY΃ e0/ױtRq7)W [dd3}M"xi$zٿ?_GxXo+A߶ahFkvgp(}q)Qa xeO_|[z3)NϺ!t-PH%f!q_<_ׯ>VEJFiu;d2z>:gS) ?̓kW)v*gwq^B(crqQ}nr-;_jFj\.R3X em9{;rߥ/~شP=̆o)޹T ™~KZ7;dd׍]QBUvE:l -55;wV"UG.MckX$ݣJ|̭3xFTJF2ċ8冗c{ӺvA>JR=|5 u{ CE<NB~|$+ muDDDDD*tJFmŐe$V?fᡌk49f7Ӵ֕džN)oo ~l?a#VM߳z]4<6w0ՏK)iUuSR;;/V&/BhZ2;jև7UƱ^6{vҖkW[Azx*\ԙ\6w7wۂ-7a;FVLSScHx-V~_]Eޣ\=Eo1p$+nLĀwkq#3se6Nk5y!}9#۴f<ڏ ᛹iݩ>3b s/,:Z>?JJ'~ a_F3B]53N^s1YYY&..Μ.ǬVxzF'~L2nsd˝L&x&Rsߨs %nB[m6e/#fS7>0e91E;{Gzpi6w}}^yK.==}O?Ĵ6cie&;\q#ƘL;/ m"'Tnx퉍g bQ͕L)37xbce-7T/d=К0R ͽ5ZGlDMd.˓U.͡5 XX'Klv/W^hKOM{G*^Tqg"D97ڕ";ӆT{z^D\W%&w+ɠQذS6ި{'ӜGWx);3(WF&e!C`N4#%ԟӪ7D×"H[8g%lb7?!3=;@F]b-W67{T} $7 =_b[PA"<]w߀s#&9qy%IQ\-VԲ8- g"AmN]<<95聃dzRuY-WBb>d#>m0Qn8b?r;0𻏸,X/,@"<٢܀ IF6%Z&/KςWh{ 'VyK_b\Oc5oΡ=yEb -Ķ&j0/6,غ~3y>R/loڕ5kXy[wnX㺪hX?1~gm^ET贐gs6p ʸ7|U&[Pd>FزД=V2eW&܉M-jD_[TwN!c0,'eK{7,+WV[%{7IV$cbTK`Ydǂ>&;E{ѢVyƒRC/5r5Vs܉&6KYݛ&9_Q2e,HLa%V&5U ///D'm ~}Ynن˺#Xz4%c~~ίϋ|JTuotvdK^K \an7ۉl׍z9SWE艧'S>5T␜H;>}Gϲǒ]*#Y=(grmHw0\[ٰR2n/YVKUj5 2黎:&=L9ٲ2 ]T;֬FjbǪ#.8kDsb+x&NFR تlOiOZ0krgظ~'HY' ֜J/{۽=mB=O{UO> YOܠ!UNݝ{>gisvIπߝT\<}7KQ﯍`zJ9?,BDDDDDp6ґZ6dS>tnX {6u0(3JQ ;ooftcD1i}bg%\e tu F=3pwsֲ=ycķ4{7z wǢ3VգpLnD%qn`_֩m` !=YS[ !;%?cY^Hأi ;ɧgD ͞1?NJxU} jDM+{6|oo@ cx;;ۍ 6 JT Ϸ̜[-+S1}WcxFt/Ξ z7ºO%ֺSaQ*"!.NncCHZ@C cxjTߌ,6o}~ǰ&>]s1)))&..C{6 4fYϔ,cFntc2̪7Ŋ;?/tsN[6Dڭ&Mbj k&fyy;'=9ɹuq7cNlcG`< Y=o6e=+Tj9Ƭ38__anRzڍ#n5,1Ƹvz70ej7>ULG3ZΝ3M+@85 dq츏0hi3v/Qsqt,¼i1g`)o=+Д}y 7Σypx?6NuJ"_&<0!>߉w汕gvz'1a4~mNyh1`ll2Ƥ/:zYqZy~LfER&8[5çmoصVDDDDDBJ4f2xڰZ~D$|&,>aTRa>X.75߽0m']nAw5rX^-MØZO?<)V.-x[D7W}஍V"aiׇR?WK'u{yPwsiwR-zŒ-7g8%lMo>AӆgWn5aޡ3"_A}ߑsŶH~=ERρw`7u]gAC+2-O(,>^|&LRߘleH7UPshY ʄMWZ)״ֵۛ#qP=x-2#36Dx_\s_b:M`֯ td/n&%S_ݔpgrxj{U7Z% _ܡY;v(ؠ/}O[;~qW1#Fȑu{vKMKd9|آw;\ݶ>)O|J޼9v}OҽnNS6sÕuNnkԤivwIǥzeUK"~iݐ)7-fX*7ތIOys}|m3r7ƧN*O!/=b&/vVߦ/mظZʽk2z궮w.977GYMWakx<<9|mx1/.REU2`Ůŏ{[tl੬vN9eC9yn(wo\ ^`VM3'2˦}Rgl˰c)}1/[+OY j?+M/*sT-E>>!*SN,%zIYY]&'=,լIlW$>h~rŝ9gPپfF3xpz7?Irma"UI&{NNkIN ]>9)&~0#FIUZyi޼yZyn^VxkT"|||թycFuaf6] GČܤ5Ui& X/>t~~9n@$EI꽝t\{9G͛|>~@~]q쿕1yIyz|rvCʯ̑ٵQy5za&u~=$^;͏7͚ϊKNX?cxp+`M; ؼ_r.}r.گl4{s+᎛SUϋgeo|(9pnBZ6]#>ɳ#o0µYr%S}<1>5SyGモw>.f,ɟ[?[^:3G aG#2 drZ4I%cR?Ɍգ1Կ{fY4U^<{qwޓv^%s+iӤ2~lW>y|W6^9x:gʹm<͛LGdds4m$h\O ?ҺK{y]ӽc%/>5bP$3hD|CvE\ <]/?>nlrc-Ǝ_reߎޣGz|6u_6+ur l>*~c^M}R|鿿UŸ8ǜzɳ0c}.0z9wWfu-|>vze3iN{TF>'?:0?Sq͗6j3׏|.$S倞im˳Mӭo8< ,ds&]tϝ^jF~K|tǙ9{ysnf>8E#>F< ΢M%G[d>L^]۽ozTݑϼ2vN&L6vK䴛TvwUz5oovHd9p3]Eߞ~{Vpm]tԿ<eKUbrzMnYL^K(ƽ߼I3yrՃdqAl{̺y$x]e*c3c E?7gp ~9#F|fJK,ج]fȭU[vLۍe?mX;k?0lb:7>7惵'C:gL㿋UN -wHNI|O}ҵ]Ӭ񓽳}WKWMc;j;d||euY{t/hHCT5m.vI67v[?xHhޜxƣ-٢dYV:9iU~GktKˏnu;ruGՇ 2׏:th1nܸbȐ!3=[6/uQ<528q7o^liϖ.jVbt?.F]jf]bleٺK 6-Z~ŇEQT}m]4mRZ{χWf ſ={lQ4)Zv\Xq}=b/};+6|tQ¿9=Ls>.ܫKѲ*j*:tWpc+{&y>SIMӢe.EU6,򹢾(Ï/ԦHfY\jC1}MM:xé*㋑_Vb͊&[{O_Sl3a=Rޫifۋ }>WsbMŊMm_ǧWs3 ]hY􇽹k(xEGONa7oܻXeӢyźQ7/WܳaEMf)zsqN{(:4yj(uY݋&wl :ts4dȐ"s )2ѭjΣ$_ꪫ˶n;W ~H4W4(MiF{hoU1><#g_AA$ڵkUN @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @i٨Q2nܸ3QرcҫWFi3FGF 8Q3|<ݻwí07.k?cǎmu1cƤ(om]IM4ߓ'O `W5몪Vs@ihy(F hyH} F/W4 `4}_Fc4y??s~iUUUU0ɭ͇vXjjj_b?r뿍[0|p_"EQs&hA=:'tҼ 0Siu46A##۷ORIUUU*J:J%۷oQ[f.=ܴ1J{.uuu-hyO>9rdF{QѾ}եO>g0w^wi4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @i'xWc441#: &hJ4 @i$5jTƍ1c4(cǎK^t95cNk5h5jTZk5j ><>lz=˟?_U}5h7n\(s5|Gyd?W&4j8f(|nV̟{؛FQ `f: W4&xߦ = Rn?wQWU5gGF58򯛏 ,1\oiE(𭩌˰ɥfVL~&ןxp&7ʋ|3t{aFk۷r* J41hY)RFUcw~{%A#V[v[>;߾DN|stvrgefGy s%7ʻk>9ӳۜ5vm奬p 4eo-&fCܽ7q}fndWYh.|T:q[w^")Y3o6L_X=n tlq*fr}<)9fRĞ?/& 1=\\}>f:F&4Gy. +N tqoÌLO:idǴmPO}>)>?4~8V/.m:t?uO2~Iػue6ƛl]9=d.Rgggvmi5ө?S,햅ZUYbV9W3qںTFwIӼeؤ}Ѻn6 w_'{Ɍ| ޙvX=K/6m;tNOc)L-o͛B{Od2͏d2"j-+ZV_kPМwۣ%3v0mاrfʂu]fGdȋN^fbb})q>ܾ]Zv`v0򳪫"?c~jH]\W~2b|e0gu` ܑ_h,ٱm\WEydgujC^lr,ީ]ZjNK| oU,>*+u퐶:eg=0LϦi;s{.![]S;;E.jT~|lߩ/̎VQ)+.n/ϜRE\95rعe_~rߕ翝eQ돆Ͷ,UۜKnΙ'nGߔG.v簇bu<s{r5S]>[ϟr*Miߵ6EZ&܍Km+ ϙ6M]9hrGrOL֟;[i^N٠ERU.qij<`FLSo;6\58C?EZ7igӜtѭ 9dfY|YgmN ϳNgN5g/eyjsƍk{sdʢ7N9wʁ픻#'^qZNkN=)[l>.C8>+h{ʰ1}rҕ 'N1,6<7׮Sy-WQ|g=w9cڹ:;iӱ 暴Z'MWϞ̕W^0.{s/g-&I2g޽9nj8qeS f[qNp5Х2vVǧ??ImYl|eyu%g$뭝+Ysr槝M[+MoBypy̢SۮM{g)R6:uΊ+v6M*Kk cevYxKY{l,їг6׽bި/Ks~}&Y%+btKn~\V\532kF΃o uCv5 β ;jv-uHMs-ԁiK?;74]s{(ǣO皫G箻$IcoUCǥHRU/5K5a׬|+*n)LCgJ>_ImMRO9˝bJ8C?n >{YK'欝W.W|3w(m@R3 xx|>-^6V[T&ءg-WU֋6MQXGEM:xYn: _buSjYUJR{pY|lB3qlNu֔q>=m"5Mj4Wׁ$Uj%h{q[3vrYtk'l}5]s˧o_)U~EQ-En}mkfq|jڞ(IDAT 0 }cνri6sj{_~Y&I*y>١w33k"qƍynsO7re"E,[&w~\vSS 8 450b|?bauӲJ}P)׷$ǽ = Mݖ_^jYm>oM{rtu|H6|5;dPq}n[oOߞrU~|Ֆת~_OU՜Pz#ՆOG呛06'2EQɪ{ծ˱nG{OI+dmI`SY|;λWrΫK y7~ݲ"ճv̆;/''l_>:x[6|.svXB9|h|Q?btt>Osަb"MV;~}>٠{4y-Yn Z|=sZs7n6[jNp/ $[vP\e~rõgeCoʟN)9={zL&ֶBtIMt͏;8)]_yT}Π%[bIjG='6M[u7Je'/7KV5_:;tHe.Kunᥳjsd:}v{┓~LLy]UM}WdNϪΝ.Ϻ6GzN>(ZffE2j?U/o~vs*m-~YE8'u`Uscɩ^ g ~O=(+ϦYv;35dEzeȠffυ|kx!Cj[o5nq$TWWgС]|U7;zkFa_1nj<˷Q|W"\sE#3pE#ϷQ5jؾ}T*TUU`>P]]Jv5eNjoBuuuyҳgoSTϧn˨/sR}5hӧOF#FkU h߾}ҧO.IMh1ث[`f: &hJ4 @iF4A#P(M&hJo0qWn~9۴iӘchJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF4A#P(M&hJ4 @iF9]p\1;si#;FIENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-contacts.png000066400000000000000000002457501501654372000212720ustar00rootroot00000000000000PNG  IHDRv6})sBIT|dtEXtSoftwaregnome-screenshot> IDATxu|U׍%c6z"R*T@ŠQ@, DJtX166xݹ|=;~1q[aTSj)i#*,ʁS'~ב&۷ϷD#Ŝiఽ[=&& pճ2K62-NgAg+""""""""""l6bp:x1dϏ뷴 ^!B| 0 :OGa`w8X,`iҸ޼ŋU Ébb*Vpjıt8)^X}jrq,"""""""""q6cHZ-&\(鎫Mi8Z^V0p:5XDDDDCvbbm;-xyb)|DDDD`w8 B/_OOb9wpDR7@3[fE+s.!aξSݏoNIc^N"'S\=(Ўi'2<+1v^0DGk' ~ݰFopĄōbfl1 0[p/EIo)l6&jdz nLNv\\۸q cuJ^8"2Q8N}dHx < ]2k#zaGz"e^p%җv?Fs\ ;\~h@>xmIATx׬HR^x >MAaTmӆF! ֯QU/DDDD8*Ungqi*sV 'k֬p0e3>.iIo24`x$ߜKu'jt/%R ]b JQ^T_?n_"4ԃ棟^J2anƕI{lW(!Y-+D(]j#2AUh}g'Q'%M[DDDD$Wl6J*02 'J$`Ӌn~x ㈎#i{_xzEfSfB8\ lѰ RՈU<#VQTts,&<lp9Α"^"=0ODDDDn^f38uhUQ[?%夽 7GoysTKu7{ԳZDDDqZ =~, j&'6(! o_D,K,&4oF`0 &4oƪ?=كL&D8Z]նBDDDD wЭTkP^͢ux<>qLw<۵4-MWs {*.VΟHrep:3_K5qE<=ܯ{Ӊotc9)f8nZ_hGQ ȫm8yil&&H6k1EDDD(1ҟ^܅+WI%3өJCS{4V\^H"Sto-"""RXfL&8u *a{Y,W[P`47 ,x,ՅV8nIX=sOKDDDD <=9{,11Jj#X[M  X1Lԭ";po(ܹZ@NjX,|HOsO3^zU<"#͞*芈H.0xzzp%<0/;9lvcd2U)u;X ?G=Z Ab7gn]۫mF|aQv +>.X2^DDDDD$K'6É:Rlj1bbdZm=.(ZEDDD$&\]]pM[ cm+zWy,"""""""""")y""""""""""""""""""""JRpt.""""""""""RB/ӰcI\ HⱈⱈwcgfJ$tDDDDDDDDD$ XGsA\3w|ʲ>bS-qׄwCn,"""""""""Rxe}$-ZҺյV+'\iIwgRMoc肝\v7SB-iӾ3}bgkY5{g-IX<~ע|e>ıƔi*l4*vg!񛞤Fž,7b{ѸfujO.eD:M*`ODV_is(SM[DDDDDDDDD P6L8v?L55yx)⣯`#=lKX<eg&)OX~m&AɍsaĊeҭ?,o)^<6Pa#xfuAðPX1^R% b`0ArnX_.?JC b{ȯN|^V1׎SZ+zgRXDDDDDDDD fJ4[w)]#o1dZQ# GQ :ƳcͮCCj)˶EE VcT/y陇(ua-)2z6ぁ5h΋siV#j EEc0xLJp}Cyu5p{3\nCxw!~D$4NuXScׂGDDDDDDDDD D~N""""""""""VH*y,""""""""""Xϝ?_9H!cR I :)dXDDDDDDDDDDRVHJVCcVH**H*أx 牉Iz=e FN ;㲽JYQ+_ ׏QzBBB}>6lr"puuf͚kӚ=ӼY,0; >rK@zN۶ &)Ρ̜9zpp0? _~~͛fN˩S7m>KQ#Νge?!޾2l#"""""""8zZG'Ͼυtxx8~ofsW|U߬&"22qOxmWW\tg>}6mp:)9s,gΜe֟)_ݺtaЇiܨfs/Pr#߷#GӺT6v̘6%1>sZ@|||~cɧKys"""""""i[q}R c/h75fW|p|hkF}_|k9dz| 5?s 9NΜ9'K1|XV|ez}11߰H1?rŤϟ?M1}ذqqy/L*QezZ,)ڬpLoNKVWEvѩӧY>1acXj:@UءV-[Ctt4;~ɚoc9ߌ5x>9ra0 8豏s y\ |^fH(v>k6s'>>ݺ2p}*-H60 >h1:uŋ,_?m͕'O>[^oZL f_~]68l)^LߥN:zcyT@I)y-G>48QQܵSp80LmӚ/>O-R`]]}tڙ_w)sNϦ?;uvB/:[bbbys<ѿ߽¢(Ӑlٺ9po۶a$MK.,,~$8ޘ3-ѳG\F0h}1̺9$"""""""y!͞ڴ#wf9&k.l.~rv1 orzkw܆jBLpuumelkO!;!kݪ%ӧMZ868u4K/O*T;JfM;k\̹soO2SM+Q,G)U$&Pv汑c > JԺ2frj#GjdQyGւyiz,s=c *W ИqO9of8Of$쇜>L8ui?gr<ٳԬS4/_L&.=GBZ;Ο[J !..>[o&SBN:Ő=JRڥ3W~|N vFx,W \ n=|k(Q i.?0/>z F%KvkƦdɒ[v-َ\^\Wz,"""""""E9~ܼ}PӒ~n9N.&&՚1$eNGMsœ7gѫG6oW^z!p|Q}v{GR.R qn%(,>CfsRڷmC ;WF7kʄa#p8lyC.޴lތbŊŮ|y+ǎOtyV&0_?5ҥK}nk\ΝAf*lKNkۛ M)\Iŧʷ}qq,slx#?]GppBaAzI qJOaMDDDDDDD ӕ9"x*Jl/\VȖ?7gg8zے[2O||f0[7[ ;uz9yZ^pxd2ڎd2ѹcML:3={5q<3ҰA}}ƍJ"/?=W~߳.oqd2 4j GqWq3N<+J7gAqssu%;.n^tܑN;TJḠ[ s;qua8{zzҠ~=,K8amL:-nԨ!cG=θ1hԨacIj_HtZwqq߅ ^h ظ8͘ɴ3y}9w]*HIxܶM4_׸{йSB(I,hV6[1t"ƍEjE&M91>Y!o}ݻa6>mN'of3w՗qcG稀>o+KN 64ܦu+ޘ5Ow㏘=s:[d2ء}ΒO&?/_|Ͽ8a >eb-Ҝ^%y!3F9$8~s3_@:r)O_x/_~Ll&ҹ,ŋr7[6tkLzu7z}胫k`R¯\Ϫ˹36)7>Y9Wf|}iӺU'I:nWپWCB޵K|`̙7͖g.Ua:l|rtܙGyF f_$ʹIz~-X\o% >Yscs){J )Z\\ Hⱈⱈ""""""""""ם :)dLQWB5XDDDDDDDDDDRt""""""""""Ry""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""؉DDD|9SSܛիt*"""""""""9b t($`;qB :"ᷠ`8 PYDDDDDDDDnbKSEdddA# b^\|.zt:EOU>I<+VgKV |[DDDDDDnfS`ۨ &SA$0ZUhy L }As𑣸h[Rsm9}4HY8Glƿr8QЩIsOz@ \}}_~NܖǏt*""""""9bĄ4>ӉlFII*$qqqLV -7k DfJ}{Ӿ>/Ei?mtRHhై$\걈\c.qMoEt.Cќܵ{scYgnEvUGc :{g򉈈H3?~-ǮxZEl6jNQʕF$~C6bbz|ݎCq,x}zt2F}ET`L9 ?a}1GٴjԕF5chZ/2.pfOl:靖dxlDq7ӯ+jUb@jO-dB^_ۮժTRc37_Tqqk2en(MՀ>?ڏ̦z\~+՛g>)3,Úx7F~#bX?իT\JTR-{yp":;O e@VԪ^Ѥ[F\ܪ(~BZթX6;#H|A'ws~V)[!O/V?b⃽hRɖmFi~#ǵd%jԨNbɇN?Lܗ8]PRvSy>c{${=0MȐwb3)ctN{a{Cy`i ){a)'Ceoa׷A\zgƗ1꿴_lW;x8Y̸0,'!.iv&OKDzfɉc6k>KHGz:yu91/;u -gjpf·= %Ҧ̝Cz9ر$"2ԾuD|r?}G3r6p0SӠy퉪pAvDZx5#$Uu?c4 KA'xs~dז?ĵx҃:ƕ 0IȗQY Q7% ye»3|޾ȁLޗ\OB'bY7 ADDDDD0En G79mCtnjÈ;IFu.SJU7}/đNήu~̈́рM'gz.QrU=pL&7 FC#ݣ =n'3[l1G!iQ7*qe b\FUj˻O5V `ב\ӼAm*UJ:mEw{g{jW:4l;)NB5R:mȬk+g,۟O6X:9/axρs /, Rq5vƀ6RC~9J _zgR, 5-}{ٷo7K3~M0aߺ/'u$dHy/EQt}i1kV/cޓiUɒP6q;A۷}{V/!oUl۾gᖚ28wU6oF6}$# )ݩV$ki6Zo? ?,em}@>9&y@ƶөժ_ ܗ⋉бQ Ѿ ,qu >JՆ*ipc nR l89jhKݚ #봻 r6ic,G|{7Jj4_3gS{ ={aЈ?Aj%lK5p -NMQ5avz`*?J.~`zoF̀TR]0釓׶3aB7^/YaL-i2epV=1'hbGJy>#fhqfotNq繧G; ruj4ͣs .ƕ|`4Cƴ*OSpvv{ssyzړ;|iFĿF}8-5m!VyGNy-U6+^@Ԯw"l=1f5n> X +'2 =m1s#8<~8;fCkNqpdt> PyЕNijVeE&ZtՅVSM.ԘIBp+]z&;a <_ݫOxxn"EL/dm>څ8.UPkR_ ֣ zvy934n$}Jm31xə@>Wk w(~VlZ!a̙ܕ҄WG1]: cǢy1]Ѐ trv^#3;W0g];bxf%.Lgp 37皫3>ag;]fʴ|wsfNKvs̛()7>zu*/KR( xkj/י5z,~bD2f|MMy .u"lY4)r{o ՋnԮ xp ;ZXpŬډ&'ng:0 =b9~~bq2x &_ɄiVxяRi4:]p痥b"Jj՟ůkoZxVrNY̘߹ ϗdƩyޝJR?EUĊj,bGpÂgx_3ڽADDDDD$cR)nT0.͂A $]-yꛟwX(׸+]{hvMƣja]N8y5;ѷMycI >7WZ?6/I/ wc-{},{k<Q/_tռ}*բvk[HgㄍnNlvmg w^[W[/fbqI_D?-;q3otíUit2i,v^˵Ng:V޿E,Yً9Aɵ30s>K)fhC$bn4gbLr-|<£MZG6t0kxHj-X;l&#{&|X=td|~'63gh(FQ^6QiC|2nrKVIos=˄1Z!,xNwM5h׵cgCyC X9C.rMhi9x=ݘlJЖ?XHʊDzޖ8o#*^oA@]VnNmq5cwY% (wk 7 Z*E|HÞcϯ@0.[K86(5,@v:Lϗ:¬&>i@ް~{1 vgH]+МŏS5|< (\aeӽ{t>^J%|7={k>|)׏ku܍~/åKW0kLv ^}`շ jJ(K1v6S[WhWҍ7Goz ̦T ;/|bwQ`?b8TNթT)oq=SϭjL!\hO;g6e]]i֠. /gbɻ1̾ml7 m}87C#Yj#[~fIb8u˚ f gsdhԈ>>#V~HSWM11*c>\ G3Q?-ػ_f|Ka$gSyOӬ|r&~N.isc:.~^9yp?8eYֹӁKAf]},>y)]T?ٵ*&?z5$v3x X9LըVB.,_S:e;^gs v0{Sm{& O5Sm{gZu, -2N's{׼zNpvs Jޯ&hL _Tvm`{/Kв}TL0 o/KħX: ;p۸DR͒=j8tz%zHD (i, HQiRCw$~Hzذ }ȖyR IDATIr5~;"x,nF'hi!]^1uֱ~ѣ +vltl?ÁB}F6$KӴG,^v0dFEꖳ=_[dӏSϸ>ǟL; ('ZdM,Ң|E}}xyOɨҴ:cC| G}Gm1LͲ%(cNw v#:Sx: b+㴇=_v[sD0Mp+FEkְqJ>,m(P-X \].;i7q||2)-3i:NJe3 .?F j]ªCwzJ>g="""""".c#)ꇒń32`N 7N=[)iAKRp.r.Dn֚(7cOGy3OV8~;{t$>FprRӲf!(ΟQOݙl*]cy._a|0 {U|aL_Ugѳ߲whtf抍66->P9xcsLIwH/\_cAyia/Xc{ogVܙ!יա=.ǍY݆͗%u~ `f#ɲ_oqvLZm?a5l=~O0!(wnrs_ə;-~ԓ1Teys1!xA\]=OčB|HgK+wCF+x~|DΩv򧑃SSX'uL?fE`m v-SlwKn |i,ΕVҔ)CKǎ97|.=F>曛Fi4ԯv^pҕ}OsnfO|i{~FpP""""""/xLh[/ U)@$p=6!)% u;6bi&%}Gŷz3ޗ$<9k֜d7V1h?ϛy6VbINgrS+g# ڏ I);#^Q;OOYG~ٝ!ڛu(ڍB4.KMw9⛤Ut9^jCOhHՙe4mX]?'noA[3(%9ˇHqE5Ϳ'(|%}[n$KB_uÿSV~w3٧wKK}Y6mJ<銒Pc$.H80s?tџ5S@ѧП0lTVw巷&ӡjd=#3ɫ>ҞNF&O-Ks|oCxgT YRRvD 5k Z87{i]iMHvGbt/Қne޻H ʦȶ#unը7/ ]-?_/TIPP,'V6b IBLcֆdϝs vD~yk`CkMOlgO iع[ə׳Ƴȿٓ[xx\롞j$JW;Xb'nD/&{Iqd[ޖd-oWӾ'4ʅ句|4r;͠QF d^Xd1æ0%4].j+E`ۨsrhH(Uӆ=9WO4 h1bsn⫏25#[)tc17<=7`xCoƌ\cLhum>yZcuq0mrچTt_8~Oma!]]RipG %gZ8q_w9Ne[ MKܴ"MFU)KGL:y.CA>M<r:T`ưbz?#EB:g¦;AD}kڀ3Y1m㆑!t>58yF 4_M` Y6ˑn$$HcjE-rO2?/ò'8zӻuPƬvG'sM"gv8O{fj~̟fӘAm)vH'm7l5#G2-_G 4!?_F,a_ruLdLשy+$/hzbnb(oL IS sbE}h(CFbGXI8c × )(\k(; wGL҂0x} Ւ:{?~K """""X;`lkL_4)ͰS7.?VuҠa'Vah7TPQQV+(?W=ʞ=ԶjejR]q] ȳuw'G4$^ک۞jӋ?èSl,""""""""")9=hRzN~wm/z""""""ۢllQήE~u(WYWԶjejg}O@txl"*%McG~jU(¹ea .$ gn^:PHX9WM kܬd.Sm08{1/;ə) I&m*'.G2VUJSP3mPk*JJ_2nN޳뇿ȕ1 RbwvIW*dACz϶j-"RSm?1!b+_ PPf|S?%Z`ƧѸl>ҦJ_`y3mǘVrOM2 7IJ7rc7ԤX7S1zp~f(S~J(Npj4 dϷ÷&wtQҿg\6O^˄5{>/ 9vLFYM,%waÀ "Mj?#s[z[NDDDDDDD^c*J80EUW`d@0~C~r;[Pg8yԏBHuj|0 n&4kC&18ξ6$ݲ[1[3Ib$/?QgF-oa*˜T cj' ~_2^#em(3ah™mSxwpolŀAҋl èt0dWw>/V3^4姉I$#fd*c.ljo{8W#GҢU4Ix|Kcm9deqOvKi{N"ae3pj@f]> #pl[׌COSQ|],1 v9/bo[=[L($Q`{TVv ?"""""""]۶mأjo7.aG\lvie,YR-;g`ZT+۷ZŽY#s B˗([4)/֘/Q7y"V1nA a3` Ȯ-Y:tφ3cl6l=OvUM~Y=Jn30\1ggflf!n%%9KdےLUj(d?TƽS¡/ƳҜv7@AaT%{=Hc1h;ޓvsc fEӼ[Obcb?_yBJ&k1^qo捿qtlvrҸכ4 qeØ7Mqq ŪQT;[vK l6":;.o@nEwF~ymVPsjDl?vm c&˘ޣ^!ouPs| (Frx"ؖ?Nq /_g%Wݺ;eQ%ԍ;,܍k#5BaWLrǁ79ܯiYeg`N* ML>|"›CX;Fig9~:_mNqNM ҧ1v,`JM*.rӼŁ}uW^%$Vz f1fC:tCҷ_cab@Tdǃ`E06pwn{H?vib``m*-W>'Zm _Kl۴D6o܋=kMf،@;k_ǮOuj=@ZSk 龋 ;Q3%z;b^wyTϸLpVo)CD<&fP:I o«m4uLjdulFn~ P[B\l)(Fi:̿e_fwYFgЫ>+_}]QߝBf4-}}Gi#bĠQM|6] 2hH~lbip"yX3j5cs߰\=i>DT-)&Pk%tCjU zegZ&KT L}[CKaW<ؖ"]:tH֏׋&䚉 \njXcI]sq5)|SR:lbb E,"<<٥?gU$ 3)K6y;=ٸuVÔ?.᙮5cp,XM/5 aؗh?"6TmKޖpꖃ7FvgiQh>=ڇaޠ֙^IEĸG'fIۈZ9x"Z̩MkҤ/ =0HQՖ5X~2a贞45/Bn} Yſ bàYp9 w,>ZSȃ'nCHxvb5C?\NO22價 i~GAڊE zoч{Ã]gsɈ$R,F}rK5H vl&X0oYAFf(e%+ޢۑH1h?Τ!%8Z,+"{.EPk2LKy"~=kqtXloq}{8<,]R=9g.h{E!"""""""^9ֱ4}[$N֓>!:kQZ["YG ~|Hnt[;n}`ډzep-+wxɑuIg ?\õVDDDDDDDkЀo-Q掬G)\eH kQm7/yB]SX⽤2DDDDDDDDDDĕ?<Vc4Y<?8e=xVfT,U% rV8[Ֆ[R|?'֥~t0aHgڈIG4(Yk7M"&W7R$&wChx[vLBNꅻG"R(k#WC8|w <;5^z4{/<̋|׽Yo`ZME#_aZϰgwFgV0g~3O.{p#?E{9y4 F!RC1rJwr@M֐<%Ҹm3#=ηߢE:+#ne{?-n[x'a`xa(_~q6u ׆0\DDDDDDDEؑǷ0_އ~̋aRek۩ 5DQ~Lcxr>9c0ݷ={+f):oy@k,7#!ok$!W^M#=3xDF]1ߛ5qm|ߘP>=;<($ɣ/_5Ga $}ǰ<}>Y Ϋ­|A|j{=^I)N_%3qq¼b/g/i!#y=&>[:1y N[*Q2iҵ/#FZi7o5FT&_^eo;fVݤp,u~ojTֈ~)Yibp'֌}4grZcv ɼ LW >K޺#a`3Q*`Ӽӑ;Yzf߸eol(W?nt:H8Za7lbWii#"""""""(N8̇[qmjUdTn׍Y:3^ӈS/9 vpo"mk=o17w*'DcǙ٢&Ta0Bc{]h\&?M9V|؛y~dbiIh;ϋN׬N١aq-¨Yw9+̀} \.wfӋgUЌ^Nr[Ֆ[RqF>G츫/l嗣gnȻԿ֫߯$"KW9bm$uHу.i"G@X?:vBQ+I.ܰ(Q MWIWض/쥳>q]pkd|!=Ϋ[Jɒ/ԑ#9}(0y}:6m3'̳9|BbIUmC^ is%oa 5y$"""""""HEi&o-3eꉇ]vqZrF&.Mh)ox_͍lU#r;Gŭ{tяk?CvT.HZfy;7"\{^,۱;cn[<.qUD}( wdk/JiYM~$vZ/M.""""""""?h _6\8u<?61X-Dt |*~~>Lݞ:n Eq,\u Q13HQ"`xi`^Ƶ{ڕkC[8_,;rǏpYN[VJHsggq` MBK/qZ#29xfrf]{Guu?,(/Ʈ."""""""PD9ۼ̎d82m3ΖgX,ٿ3 $w$ȡCyRto%9ܹ 1.E'{)z8Sܰ$"e,]|Yi7o5־v]F^!DƮI1KZ1 b1}|eӛiPb!ixyrn(;$7I{RvM`YL'/G雑\Ū閍&3돮 є"06/3% :s^*\}eX6ӂȽд'>UY-EP˜}>nx:(S>öը3$ST|s.;!g۟޶#QXYH{V]ͱ$%' cܜtw~гAN7-"""""""4x+GY%Kzj5iG;̒Fs捔 .2$õ?\CDDDDDD^$7tHYL45ML3z*[3ݴc& <k䱈pd""""""""""J4XDDDDDDDDDDoeٹq7fH&"""""""""y ?2w}߼++-gs sjP@2OwgwWJTxx(Mrv""""""""Gqc:P`>)%Z XQ[5.M$x{%ly<ܩ>h}k]Hc8Ξp1 Xs$fvR39}  q&r)pnEլ4^Q}p(A1y޾"WJtt4ѻwR){3wE1ߋFA3SZer<42TyZ2aWg5O sKLLZL֦ۤq\^g?LK=(÷n;|o?V flE õ"U3!@?ٕ4߅W=@,­xypOd/V-l}߿y4azhb1bs8L*ɖ[EC8mm7Ʌ+|3Ě]|ncDl Sbw˾D܊]Yȉ]qe^R:ppK\wÿ""""""""JHı&!/p-b"?<+mMWy:v+Y>v+788鱕ӛ#9j*MRwO0S0=i|L1?N=yoF2\dXzNtd\&*l4/]h'nǻ鲆N׈ms.ʔɍ5z 8*z{.r"GfvY]ݞ{4xcI/DŽW k^/ëuqNEDDDDDDDғ3S\0̘58ԧmjaϩXdއЧg;@`l>O/V'_ݤM gX+JdptEh-'2)`>qד7/7԰ߙg7ҷSCs!q~iT}{3[3?.890{xi]9hڻ6>n^x9'y7lىyߓg\>ʖ7NewTz{1ɢc^ZSIZȧR8w's~U={nLB~@@] L[&2{^8"vՌ{w#;#?م%⥙*1bSF7}hPlY8NReޅ4]0Ո:1揤ͮx+L )?7uBbbO`&8o3id,'\5ɑ0*'#N[1nd%Yޖ<)ޑ2}Mfqj"(U!nYC/#ǵqGH ǼC"%2pL+A,+F ɖ3'gRfWr+F!ԗsߵEV i̊96ӓ?5#Z>΍а;~DX!+Y eB \=S ɯxc[LvPɇ~_ _3b*M cʜpzL9K|/|+֧ .zEDDDDDDʴ`UpcvZYf mڽ˳E"ۇQki0{èxƵ5j#ӻ M#cXt=DDDDDDYq5jjX00 #i\a`Ilۏ00LDFF?>ŋb,FDDDDDDDDDD21:EDDDDDDDDDbyiYEJ!ӻgW*:Ӱ PldZZ0ODDDDDDDDDDasxlIe""""""""""'&&e""""""""""ۺaTT$[6Y1zGƢ!"""""""lYhӲIQu=2]E#cQ/""""""<5 XDDDDDDDDDD2-c0Ҳ@y,""""""""""HExc*c+Ҳ Hy,""""""""""uCu[=7KsO\̔>SBkgIDDDDDDDDD$ٷ?/_sOd9s#GJ.m6<7똻,&'=6n`ռ7oa"F]N>d;t=|ԎCsyl9vG׬aPT|Ho~U=ήؿe<~ՓzBy<\Ae<#+wbhϝ,NdnDxV,CV;f3YH%vz/!1+wc^zMd28d}iGSuLekoTl⏵ѥ^Ir9ÇʭGd=dΨμPɗ\9J1(*Z4sH^8:ç GDp"iL$'q<\qvOΌ]zd/3.-a@XO_WsŲ }zď߆\LDZ;Ьi &(J[1qt7bbq4z'ʩEݨk.iGQ}Ll]wkv]\=0N_ݧ _X5™Nxcڑ+>'b{y 1۸M&sc+z}:8'དa5)d0}2ڌ篪e -@RI&G7}h7k2stBDžZ}] }]NxƌE607NuGLh]]w$@ryMP08x9g>c"nkBFlEwøȺ}s΃JC"4 vף]sp8..1apiV t૘wpx| Mc3`>tNq)ގ*Ѹ6;wlO_s4"㝟΋.>_W,yWL]Ն2j@M ܩ!#`LGUˇ >+>kb8.{;[Iel*=o}ƥ6w!ߋs-էE;7A!q흋p>ǔI<˪2c!)tn=5Fm/70ߟW^DЈzwF{x/#.2=j2:na<γ0{v&40xx{7|^ܨb*~k3_|u!9_᳈Fz/CeA"-`i븂 ~?Oh}k5s1 ϙz+~}FR&]Mm&wp 3W0.F!;+ΝqS { ?!cW q+7A-+eӭ(ufC+S6%7f;Wd_&ztN@6ebh9)"""""""0ߐ!C2e o5{Kuiyl93%U&CmNşE`%!|u:,\@X0~ͼĔm̄)aG^MPlkm*U yfn$ԡ߃;|j "m֭`Q0\+R=O {m8wf풽/e.^2Ȗ7XLLuC_C6åXm&Jϥ}h Jf9=yN}t Cru* kOK#Mli 1X!=J#F064t8Oxcۤ WQ=\W8{"7?0ۙ1@Bk21ao1_b}~ݍC~&G),\é /O_%V$ޏw7Meud.@:tϚ|]y+ 9+ٲ•Wu[ɔ91+:Ե^XsW.qss\H;*Fy@y~h+>73yDN+wIyt=8hD >ÿ"""""""q=,5j4&L#F$|ܸȱ&!/p-b"?<+mMRحg ؁sܸ[rVNo䨩4J=Lrǧa38#;[EقPry|\*L59ָLTh_(@ x^Wפ1O|=uEE䊛 ^-ɌvK.k(*T6LXqO@"͔oI y#'!W;qL|k^/ëuqNEDDDDDD$s{PΘ1}5cÞHxl7^ӰM _<9yC",04lGq}ӇԡD_q~ kElẃR&]Uǖ0nzCB]z;SFvjH|.$^<ίg"ʖyof}k%9f/gM{ /6/m5;Q5V{G~‰SZJo/&<0YtlXkJv[=)]6]Ⴋܮ(} o$qs8{8۰[fkѓ9\?*=`&{!? ew kw.{Vm?-y{=/|B;qSB9g1o'{h='͑*1bSF7}hPlY8NReޅHf#g?vvubj}9t<W#np>?b7♯057./].-&o.T fYvz1N2}B}UÙy _yufvŽq#//}I黌m%> l70SWq(@* uBNo}9=ϟW^?tDR=  -ِcZwF by\鰠l9s|!g`vp%GbTŒM}y?o"+4fEW?A0&uoh\rٰM̶b^>΍а;~DX!+Y eB \=S ɯ$7c w!`SVFAiaLN)go%q{nDDDDDDDDlt?*ӂysVL]֬YCv/=la~ZL0*y^fyhAEDDDDDDDDDR̹{}c8**Qm>=]E#cՊdj>cfV+6mosx̒Em.$mڨ둁zd,|*UVx1O( Ǟݻp9')c""""""""""beһ2ۺaj-Hfsx,""""""""""W*cudV[eHFc:H. OLy-aҷLs."""""""""iFi:._77jr~=oMžX~W2o1s.dY9n,m*yE:ǡ9}_"'.ˤ.VDDDDDDD$R?@qVO }pq #qD\5mkPގHZ907"v'5"t; Ev+?fSL,&[$Y9u;|25/;}p D\=0\,:6h[] TiLk6eO4׺-.;723{=o'icFiLY'غ#&JDrV n j9݃\+s7ϱߍ S4ƝǷ5 #6_X9u ZUH Y7 5K'ny>f;q=o5/+N(ՠ7r{HYqrnͼ[-_ cs_~_res"+9k>ƹ5N#2j6 !y!C:o/ẙ~U(C'< m\~w܋PFu4$PZTF[_15͙0i{o|l8fq-g!ʬ# JT:wαq3S.Nn+ׂƝY1OW8f%of Y;&a3.Fdqt@Y>p~eӔx]jQ*֣{omӻlQ 0p/,&/jA}p %: lƭ .ndX)q#ٴt6a2zrhg@*K!5/܇ca MeޜYZ~Z cg=Η! bu\AL?o%f:"ϕZ9!l݌kS{Z}LWJ8gj9dLkmfK=n6O\oY}v+>ZOKuyee +U]gRro=%#G,^1w0c=^}8#]rjV/>E0L?|{]!Md?igtϽQS;U6 IDATiˮE D\޶ve܊Yqi62ePc뉻}Ehw}MGdS f&::c)' Ms19tLj&?O8~&aWg5O sKLLZL֦ۤq\^g?LK=(÷n;|o?V flEduv1q(à楰7N݄/i9[ƴT-Ȃt _#/~m<1x|yo6oW ?Y(E;T} }kФA]R+~:C1Q#8N쾗P"->3e>t`:-RF4ήP ,Os1fJVH>*R:ikL.-^\ˁO?R؏3D˽a``2ݻ۸](T%+gv]$tA`ug ۳hO; IE߿Sz)]iPޗ‹Vn'W&>v&d=DeȢe]+:ˋ_!NNN897H<'gV jst[Jj֯#`M<XϬSb 1gcƕ]|FSˏgv }1KP &u1'?5w3trĸ{v`_ ~sd(f,GN6n}V|KD;Oٸn"(-'ٵ<9'm*Qr1'cs3*˩Dwh o N'~?ɣN4Kr}8&lz0_.LWN%'Ǧ`.<3!1 kpO>SDZ<$RCOv78}^NxI#hK>a(;\ʤ" _OVsh_޼\ SÖRgHN )υċ5 LQR8~= o$T?ᅧu liy߰f'J|O}n|2e U:ep O08ڄvKWPWqۓ^f3[{GDϋjt4tc0zʉ ]dG{R`w+@\a:oVגvsG~|^L&Nݩ3CK2Ww>mT 0?_Cۀ¸g™Xto-cd-.TDDDDDDD$]c1ucz#Zt`Dj4oHY|f_[Ç-Yп% Z뫁Lq J^.[c&-LKm ^h׫@dkv݂2r-n/}Oے';R2K-ЁM ,! (qyJe{N~-芉{+> ?ҐcZF"LX8z Mxak_b#̐~;*2lBU97}[آ/_Ld.;ȁrC2,ӟY6 Y@/⛎FIpO^ Z=7J9CF.ϛw2M4oxaJH%m3bڊpzؒ6ư\yuaOɴ`UpcvZYf mڽ˳E"ۇQki0{èlsogkFj?cЗ\=!둱zȳ$jjԬհa`FҸZ0ƪ[ +a`6$000|&-J؊,F26_<%)˕S"4+XDDDDDDDD$3/YLҀ {#>_~xsBa,7ʎEDDDDDDDD2LylOCćwHJ X]<5/qcI%c""""""""""ᱢcؒhI:DDDDDDDDDD$9WG.-Of1m Ίc'{QH"N<umci|VB&c\:lTh6ˎJ⏱zROWg إGM2_(z5W,бwMm8툈~s+bw2Y#BӠLQlbn6hOSQ\,ӎ^Rۙغ һ>'妼u!OfCf7iD\һ oژhcF . գknĽZ>oi; o`c#B=t}7""""""""i*;okBFlE%r@:nI.1L !0.nljOvgP}vz4k_W\sQAo>yKܚyZ8<N_g-H:++Y\Y6_ßKr|Ku5wz *Qf?G}kcIc7`_X2M^nՂV2~V -Jtxˍ[[\ɄɱS%rF˳ild2a+L."""""""@*K!5/܇ca MeޜYZ~Z cg=Η! bu\AL?3D]Gr5;U+11ē-m aS ?#RqJ ֔R-1ulڸi,c<)q~=5x{r|_Fи(~*Ac/a 9_]|IĆH~veN׿dz2Wpc)v{a'dVjA\ eh?L٨>8 ߘzuUMԁl]DGG7r <""""""""`N# Ι)Q29:„SiX$lC,tXIaNy)ۘ S6\ɽu4=T=ѫ@IpC%>vmG*D~ی[Z#וqX8Es/7-|T2K3}֤~b 9Li>sb K̯]ȧqb_&5rgOV''p1.`pM-*19cddKʭ"UJC6B~b]|~DDDDDDDDQ6w'Zm-5 -B-+Y9s''X'Y[ ;s~NNܚiOF )ww0X4iogh*9Q;|7 q?'rkDT?*9FǺK&rVvIfa{~pEn<(~hclgD 9Vd6c6)bF383-LSX> SYNFJydɋ &柟%lVlu}?+%Ӷ=S1U'1Ĝ8ѓ1\PZOɫ%rZ9sORݮaA %JWGDDDDDDD6qIȋ>\w/%OŊx[vnʺqx8ʍ(>NzlHJSѦA崏Ogq"Gv!ȋ ~5ʕl&]M&Ŝ֌:׉*u|g)'U'6?e+h"k4ԯ_T+Z)3#9 SaaqVASKr}8&lz=z_ ctx.THz9Jq<\xfkC0cP>዇}<"cyHz~C?n/q0>X~uqY/o?|D6 G_rr)‹ЮcK7|=Z͡}{rs.L [J~v#};5l>/ȃ3FeK735xSq5,k慗sö~+=y#D\g& Vkha{@ |w*3<0lGBt!'oS%j"s/Y|/R*۳94q~,W~ \c?m+jWlunϗ_ĤG~ƈMM؟ïm@a\ceLf,:I鷖1w%""""""" yɫS7FQub8I 1ě]WR;tзؿ%㯻S-?gۋek̤ xvsp&G׫@dɑ k'-7oKHV&Ì8u5GҐ^,!֗8LxAAWL$c^mВ 8{ a wOPth8#3v귁#xF zmgr>`ATO)/s.ϠYP oŽu9w2Nobq5G.VjD.3`M,67&4- 2Z5(X*zeF̚^w3b*M cʜpzL9K|/|+֧ .[z)"""""""NL 1Z?Qk׮ߟ5kЦKO<[$}7i[6P;ːt=2]ECDDDDDD%Q7Pfm 04ָjX1 Ddd$]3XhQ*KbDDDDDDDDDD$C}Tg eiʞ*ᇈO:DDDDDDDDDD]/\19brq,AYqDvo?vIĉ,ʒ?3f3v.dWj-23ir)sD[~{{⏵ѥ^Ir9ÇʭGA%f'sFuJQAQ;-Q_?lNN&q8> ?#:ՓzҸ.8l`g.=Bl0 d,BH/׫b?ݾh=?oo .@""""""""D*̻OwhN&4kD6wt}#V ͦXM)oZԍbv՗ĝ>ugtd‰q޷7 ٻ?{ؔƊ+vEP;B)ZQFZ-*1CvPFQE*fWjDlHn 6MG{9stb4=[ӝ2bEi p3<=b#=~ݙ ?1~h0wEԺn2X3-GO)GGմM.]ʅs_r.ƮaeD.'Ld0Vs$Qqѻ}4D,1-*| L{7~ ?zF/߼-D/>? =t3=&!reVMKh&C?g65 lْ%,2b'&r-Y {ۍi.ɰpqFZvkJ`GW>{lH0st{)INg{llUqIn^@.3X{[gI64 ML DF\bex;akcG5:|+ lTjMя)TvۛNmxtWO5>Y3''UZuC3y9Sfx{I:IG R eKfYWid>5 k'M/#=)-ЯM(öshzi@ϒ|uK`[f@{6lFЧ_oAKxK \sPtE&Sΐ}W`yySEY=Q!A|-Kub"e``W GMa4f Vo*!OMDDDDDDD$mLnv cC+%"Z}>D\./M.KQ }͟DݾEg֪.ç1Lg\I$Ԣi,E(jf Y)ެ'Cmq9ѯLb,#tOƺLmRBuaNkȭ'X2;N R///,t*kJ %m82kpZz~s_M8{2F)d`q>mK, .Eug `M99^)|뾶*9-}ј֙=86j.w~^0_b|Z!V>;gy[q0d `ʆ԰uv """"""""ϕt-i9ȡh#E+W #;z5&nA-ZHp0N<Ĕj c~Ek[Hζ ;H(И7wZ\9޽hnζ|`-IOܐI]1T3Xbs8H>8CVp `p$7޷[3WS7H""""""""#)E,   6,B->g̮]TTſvxHzx%F3ɝ~x0i>GN-Il{vd3q7++ fH"˙6Ãp\EzbM̭1H ؕ kgIjhuGx@Ց琉˺SJ}ɍs${s:u֢B:M$uV~/%5׎ \:ŃJS+[at/Bf_lb?21`2b 'L! F#F>eOG:gwh:9Eq=)EKz] 5y421c!3IIBa~X;9Sł`|Af8~ou.jư-`|ۯ)7,Ѭ[YprUr~AͿ^9}_䷃I-JQIWUk|,;ݕ>>O [~z"p+SV3.IG8pIc3sP2a䥀שaop?vErk)U˔TtJ 9ys#4:s 3?iBE@8<@5lvlyA~A(jfvƜ, svn=> Il)h;Ù+'=ǐd[,ƵG1fە*;ު{\DDDDDDD +O@FԫʡiYWxN#M E.7.y P^gh8w|գ5sG_62ƅu.eԇqn>=ˍ rv3=:ԣT^GL{AvKRűOFp4Tk`tuͼsШ[M fq!mKfkT=˥?uk8Iq\>';Oe&{jLhIQ+0Vn< 9u/oͨ?uӟPTolv4KeŦ@,JׇVǮ }~v$p`r &j¶:~}3deȺ(?) sBxwYmC X *°9恁Oreݺulv/-LMe ‹P1El݌o8Qo-6G13?y<E!""""""ϓ׬ó0WҜb?=K/~ҳ3HKZ(8yQoܟ`@2)|Y"""""""""5i_0/={r ޏ/gt/DDDDDDDDD{ T+=yai<G$SRzCDDDDDDDDDD24&)=!""""""""""uZwܺ5ӭ34#sx<4,]j5ӳ?"nxd"E㑹(\9!""""""""""Icya9YGq6,Rf }GdQ.¹/xlZ9cװXc"jl nƤ|S:]S{ӫY _R$Qqѻ}4D,1-*| L{7~ ?zF/Pd3h3lDDDDDDDDU:'I Đm9b3뺓:+y l9wV-C*bu- `t/y wT0A]0`⌦b1x ãRfRyVijwVd+TAC[9cKzuړ_ҕv7 M]bet}.'%y ebJd˔8odst6Z|=L 1)F)X|r5`L+>&оw}"Ɯ{w4!9dXڤ<xzL gOb7 ف ~6vv,]MX?bkr_-͕-m1NŬq}'Bߪ=Pn$WgհWO:3ΐ}nbTu[Wv `!A|R?]'6!^N a4f0ud4{DDDDDDDD3O`}f'lcwk0aww|ȟ7G{-_KfxVc|צA M4>J dǭ!j&f£^6nltO͜Z41cZo2xf1;靷hZ=e~NW:id;niֺQ*™K2|E8ۿFt<瓙o#zP:6 @c߅jreTz a;\f'=rC&  5|~D뢎ĝ:‰,x,ou[,19cXE/Ň%!NH/o=n~ `%lcyy)E,   6,B->g̮]TTſvxWHzx%F3ɝ~xf>GN-Il{vd3q72LZ N qOLӳ\:Dn lEʷ ~6eO{c?nL\֝mWRLn#3K4%(i"!܅M}ZbQ~3h_033g:̎=ي5 t ۜq ynt.[at/Bf_lb?21`2b 'hhx(~P,MB7'hV6.?$M>1{~ q1ʺ0V]L@,)m!)vvlNֹ8X̗/r|/ќ%}0=+^ΠJw|a c6٣IGShC٪py`p/JfL=|kv0 E<"""""""")}#y W%t񣻒LJIa˕/_`CDneQ r%[^Qbۘ%saέrC")uM1mg83x"<ȗȚF1KS}/^(xb\qc]/c|0K.y8dPEDDDDDDD2Rkw<y)tȈzU9Խ;-zjϩc$="ҝޥ5F?6O7ҫv MzuN fM:q!i] ƣKFu{3c &/ïwGL(Ӆc~RTqSѣz>M8w]q3cŜ-4VYqwHdےYZ%ީ_?'˅_{꣛_ӳaPEpXDFa$[!o eZΔ)T!K?_ яּU [0jOn7Ŋn G{l= >u.;OcȖ oӪN׏}X|-"""""""M`>7o\0cb7:׃MQ!ѓNac{7c5G V$thHYι3+ׯS5q~ >JlVO5!}QǏZsCgEbε+֣kB:Lez0tr6 y10d 3X=h*A9u\q]׃X(TƏ G0/+5vNbV>h]׈= |>I=fG]Vl dɢ$z}L` | B`gL&Rar&lzzy[5@6RT+u¿=r/CV_򓂙0'w'%O:^Ŵgͱ4 |֯_/֭eϰ{iab oj, $|o0=ϼ"nƷZir_=9?tyjE!""""""ϓ׬ó0WҜb?=K/~ҳ3O_ow"""""""""9id3yc查 O*Y!""""""""ҳʚ!~<ό3׻et?DDDDDDDDD$^EDDDDDD䅷o~L&5j=W?@ɒO ?Eي?ƚqjق:bK9L\=++v6dMvCY~y -\E([' ;߲7-MQ#"O\|60E* ]6+L:rMpz%`sk+&M}{x3 RPV0ݳeR. ȫ=ə8&q_A8㒯4 q8>&Ny␅[lKU<&-Xj# (`eN嗱S&Ȋ7/u6&w/@?C~$9`NlB`[C|i6f`tSixŒbcsgؐ-2e+'lcwk0aW@ IDAT?-goL[~/Mh2j!+}F+tiٻ2|0{9W1ۇ7ƿz.m3McE/:*8!+śc₵lټI-Ӗw}V/e~5}Wt&{W=դk̝r'ޠFf3c wO?|9rgRv33lP\op.  W4mBU B̊y-ÛoEks+8=^1-us99 Nt/Z.>7eFAVqX+3U&̞[DEE{&oOlQDEE7|/"""""""XX={nNךǖ6Rr?26` MkRÿ)J`9!klhBjHW`xطtr˜ T[w)Nu߄/d>y'_đIt,GE%>VxnMSjTR7?5DDMfl,6_FM]aq,:~ .2RMWWwaOzMs'mDלxc۸ǔD &H7"Gv $gUm.aSZNl$U7>5Bo#CzP:6 @%68@1{'\f'=rC>=@YNGC<9S=V̝Kif֛5Xbs8H>8*KXq!N߼nGxywIn~dXe`KGL"""""""uz5YtL|܍5 N qOLӳ\:D Ka)5ݶ>%7b{&7 /ql.8OZzrΟ^Wh2=J:.\H DlQ̞fb˫A,ɘ8;uۮg˙8GJS.єZT(_s7kaG h#Π}.YiKeDDDDDDDDqg?㧩e+E(̟Q\{˜@G$&>Y F#F>eOG:gwh:9Eq='I]n79ݻ[|itf&\   jMLvV˘v3ĥd͂Xvquy;A;cVKo. xG}Ǧ&Ϟ9ZѥCJ0L_ 7WSbz:kڂE.op>7_Do=gTuI\}lID9!gr>'^9mp-&R(ytDDDDDDDxaN ruXBw?+}|ȟ\6DV!g.\pȓfNo 爡e߽b^> ^&¯T-SRR)Ui0Y̩lLu[ǟVߑH80v̝ٳϮ%,vRB53,O]}Z7~+4_e-NNJ/9G~W|lYܮmGΝ)s~`}]^ۧ ZGV3w6d\s\PHJ]dLiG ^>>YJlVO5!}QǏZsCgEbε+֣kB:Lez0tr6 y10d 3X=h*A9 V.ASu,n*ǀ#Tu};Efޓk[Hq|-bYiԯ.Ω\UnVSknˀS'͐ C}m Ŏ?'ްhˊM,YD !w9]Ah0td;c809 S5a[CÈ`/Ha}EX_>ϐ՗!~`& gqӧ,o`1<4yO3qͱ4 |zn:Zn &vƲ@Sᅟz[Vέ&qxtULf)=xd.yfqp|$ ,YFa<^[w~ly'h޼cmK/~Һ<_Âqoښ EDDDDDDDDҍ K}Wk Ӣ%/d\4k]S# ^.$dY[F$Wμ鱈H>'#>چ!Cc""""""""""ifyDx зOoF>Ջ!}üe+DDDDDDDDDD2 ' b~|<7C$SR*"""""""""wٽF x2Έd2""""""""""u;nuߓ#o@i>?G\4"""""""Ya61 gr шl-M9< dV&$ElݬD4#sQ/""""""u8K`0C:b1( yy{{={839nnx,H*]x(cZw|B""""""""""9<Sǚy,"""""""""z L4XDDDDDDDDDDĴ[m=S.̵mLx3c""""""""""i$ [+H{a+9c;&" #iY>?.v6g+Bו׀8A9p)wmE2""""""""O)㇈?ƚqjق:bK9L\=++v6dMvCY~y 1v&qSH)>vtXq#cWYp^ -ĽAcR. ȫ=ə8`O:/{Рt^qW}8؆)(kuA:Rrq)}ڌ`exI';󱴘Ċ_vn/[g鈈4'խ13WvI"8'=l!G|̜ZBtĝ>@Ć3\ !6S9ם};z\DF!#;Cir4Tz~_axO~TMݔ2٥\8@+bV6~xʵ4j=4zz{E_2E%^q1i&¯G lb]}m}HJ Đm9t&Cf4oDϡpR6lI`mT`Ŭ^ 턽SNƴ]SdX8 -5%0ã+c=6$9:=$=6NI8$D/W\=kЦ`&Mm~#X.WE<ܝգ>K/P؃akZO7'݋Jos%lPcJɂ]r5W0Hp pэ>TF^@B5D@xt>իƍ{}^3McE/:DQ4kJf`-[6dR #+e %O|O/ ,iɸX=+ѬO\ٹ=i?G-g[F~}uӠ6 ,ʠ[رeSLN#`}7\3{~">j;Oa̧~c]DjO4$iYZ|W3r=K668Ξe Y99oOWpNO_ڌ\?n+lmR=h@…fx6|? 2˝WgL=}#q7_߉"jL̟<ِ-)boj"""""""" ߱[br(HI^MlS^I>56[&aNy)Հ16yj8֥5_>yZ ufđItEE%>Vxngo[ۘ1W- [7 ) @a[5::͓u,Vr\6,hފDa(W>}i߲}Dk\ @\ a9۸sPN}l'_^nȤ~$WLx6,19cXEyŇ%!NH/o=n~Nxy)E,   6,B+b̮]TTſvx۳Hzx%F3ɝ~Li9'tnYOfݳ#.Af/`SKmg?[t1=H89FcoHI\4rx#ZJWSY6ӬO ee컘2Pǥ%ҮC^^IVHQOɴ+>-'unەl9H8u:M E2>uHH>w!{Sߺ6~Ԁ_;2p Ly枤 AEDDDDDDD$V݋P8?#v9}؇Lr H$1k01,95/{bu==9 gfC ,ڸ ?-KF?#)W_?KVZnlRX;Ȕ8wˀ@K'4qpxGǰoL&\z,X,Vh?"owٻ5㟙'AVj-VPR]ԾNKm-AJi]kCM}33;ǝדg.翉O ::9cO OX@@e|%3;k8;.oo˕';ù[~y& KǀeW>Hvn EJd춑LJOƭ."""""""7%R|wd+]\?XWCYgRu,Y`Ϯ=srC-E)Q[Ӱ9U\w01x6DzoKW BէRb/[iUX*;Dd@-q0]! .|ᓛ.9}&o :,|:ΗW msi綎Ͽ cJ:r"l(q}y,?ꏏi~EG.ckWmkˊ=|#> w|"Dzol+ZKҫt Qcm]EC] 37V-BIIInyckkVbOn4hNSn B9Yϳ~L<_!=ݬkH3{kOzwBF )@y\kHhͽ͗[lGkZMp'!&G*^?XWΉ{.ND>V/<+X2mu߬J޴^x`)l'_j@{SkmО>(݉+Gvp`;TLǹ߾g鼔)6ӓ (6^)řc ^X9ivF4J=հ9rNsYFƮdKwD>O犤ep[KZP\ЏגLq{*\/D!q\(_WA۵MIsyG("VqO@EDDDDDDDRDekS~HaD[ÛJ/פxt8=i;y+1O\4n11w^#l{Plw$,.[9.?-edw.뾈M>_{*0kVځA3LN^14u4ݷo?^'"1Y)\3ߍ~D2fX\< Pɗ|߫8XVA?땏dҒH ؞)};ɵQGwJgX*N%2 %[) _¨6gsD w \Q{~cmt~ʌ f!14N^/]vPa>xeiQPCrJXbM|%E,Rea!ۃ)+tZtGh>R͇D՟ФL.28ᚱ ]FQ֝>qwv&Mj\vV ]̴_p>*| .Gr1kQK x&3_eoggf~&Y2]j6&ní13&|4Y>3K 9)؎OPͅj |3}Uk~C iM뎳0ǰu2}A9ለ$$g8}V9>c8S.f!s$FlX3윘בj&sX3|؝ҹ܈:է䁟~yK_4s; W6YO9^-{ŀ.rVu>`#pt:nߟcsf/ݚ19}35aNkr4q͡=j\՛gӭHEaH>;,r3C?F/ &͵ÄϰY U&bJt{ >w"7+;\j1k` \ShDDDDDDDD) !o >ђ'O̙< 2 >.^iHNt=km._6W^~{YIϒ>43{,wNFYx;n1 7]zݒ1?yԦ_H hL Ğڧ4nÓv3Nԡz, w'eX!lҗE//n{9.JQC]gpsnWVT|8= 37LUńҧ#}+3jj^~q* ܦ`lQy<‚y WGǪӣsejՉ"ekIܓsaBtD],(Ҡ7uk2%Z2xIg#"l 1'9:.ޞȜߏx|-t3/8>x#J+>ܕ"N1?cS期UܺVed>V}~/5=Mqv(gwbRFz&z}ϒ}0Ln Q;ϲmu2s]oX_Bnê kL֕_yl0>,V +M%miu=uZ^___|yypDb7+lu%Ǔgg_%mdsg8wGD,aڲTi٘>dץ+"""""""ϠMD0i~\Ė?*+'vlr)i+y6&XXm jWT*QeK7 `Y5{;cxh>,,+{F6Gm3Wؽ0+H;Z _^u7QV+cwCxլKnJzfWXer8 RQ?gڶ.L䢡 pk ޴j[~<ݒZKoq9Q7_Cig }f`o 6̟¦ʯQ>[&<-]# v'w~ښ=/$g;*^KjR<;-d79 V"ӽrNcwq"vV/<+X2mu߬J޴wŐ=ߞ\'3:Qӹ"i{a89҄gn;QZP!G1gCٸߑ2K~c?q FHޜ ,%,4I?Sg͐ȔzOY̜f=Q+WϏ+VФYRGI$'`<&g<Bׯ|"E(/""""""%mv{2!""""""""""Iғ""""""""""JrqH*c#$Uy,"""""""""z&<{8n7gmFl-O=cRq2X͌8~ssmf.aԛ1=rPC<(#BpU<^ M" ei$ŞiݩwggdƈؔVDDDDDDD=B>|T2/8{:pme?зi% eM+sR@N 3\ Bzȝ'4x+E͎߲=,Y)CXR:Ԥglu,K\&{qM e;gr5}.J5zEca IW8v")}N*es-~$yDd.w2x`;>Yw\X@˷?Wf7tѴ8 x K~[/ӇcHrIr$ԷիEM-F_1'"*vNHV9_]>N\nDES\qH\\[/njA>es8rp!G3F)fY3p ^vxקdX\Y)qפ0߽Zwd1TvԾhZ:6Oc,4o#ޫm\<KK Ϋc3yXCҧ.r3C?F/ &͵ÄϰY U&bJt{ >wÙå 5EFDDDDDDD$y͙9DXth̜f2qv.b嬱ϙU76Er N)MV&scL_mS,qql v57ݜTϸ5{7.9U׍1fLbJz\zi=sF᜞a^h2kD'eM>Owb2d|w[t.͸qhj?&4`ۿDŽ4=kK)i0֙z~H.8fJ6d&|4(drs6i0?]g"ƶE_3qvt6i1o|i6݊¬ϸd]3/1ͳyf%1&ҬqL:7_M"Pb9m 3__ǯbbs=MTsgҌn9f>DϑKc&ٜ:u2{ap{i-Mg'u`q'{!_|ob C&9qDDDDDDDDPI<ݻ1cbwبɗ_Ih9;lC9_ 0m%+qv'y0G ֪Ial`r@{NtuRoAFr0DST e;@"*es`9{[昅$4ne^,^&]Nw7s8Vs+׏?j$VMk2m-LD('/XȜ- VxLl o99ᚹ~ G6$Ad嗓keٕk3js,Yx|%Ժ]DDDDDDDD!y_ WA dWX8w?ϜX:XpJ~Iij G?7 vhYϚ9|By.(mq7=;"QIoggٰz '' ƞPXqZXT`c,FXXcvmYR1Xr i9l~HZ/פxH~ wTRV6q)?ȩҳ(6އC0Nd_8L9%+ny4/|2ٻ%f"c >>dBnu䔼)ʴx%/WgSu%[uoo(O(^$yQlUw~I9U$r?@6r v;voI; QpdtN :Ҁ7ͣt)7y y\ܕKqHޙ97?c |v܇7M2alkpv8MK$Bݚw)߱ w2zb[jsѸk3b(lFҿ.72e/nequat >ȵto-ˇ4%[wXElQSGnI9_ǔ3phs^<-^}' n&@!}Q?kFl.~jȒYb<67O9.%ӎ,imr"փݘ9Cpt[{MnՊV;0ptɫV2~Ap}0f5(N@>m6}?S?dhKԟaS<o)OͤSg)Du[DuD#j;Ty}5bX:\%,xiCxci_:=g¤M EDDDDDDDReiQPCrJXbM|%E,Rea!ۃ)+tZY֞ 1`o􈧪Y! |.yZ 枸F͆vI``c1X?o#,HRчT'KK(#!zHJlp~[Ft"""""""""8TV8SXDDDDDDDDDiDDDDDDDDDDINbm"IN&g""""""""""8&ׇ~m#Nh>R͇H%9y0(_59G~#|.E|/m+ۓ1 IM<VƘCDDDDDDDDDDRU=!yc#H0DDDDDDDDDD$5Q屈C$vѬ?ŝ n۔7R6q`;>%qGaT,9,""""""""Rq2X͌8~ssmf.aԞsbÏ 9'dJcZgfv XvN&er ׌4bϴTɌ3iUcDxlJ+"""""""!y|чX> ^*wg=|u^Dzd`۴ɕ|j DbNT2ܜpN_ r^<8`u5=Y ø IDATX~$? ѕ>0YҺ)/ed{-L/OLE>vSwu%Ck&{t,ǖ}@糓--K1y]1>x6[1jΫS,љڥ[l' q)1Ы'U!Y ]+s4;4Ò!=b#"""""""\0 ajSjtxQ$}LGɺXΉyj25ˇ)ˍ]}+)#c>ۿŨ n]˦ 1>h>%uLͿea0Ws|J&~7Df0*'Gʨ8Ѝ{|60MY W6YO9^-{ŀ.rV9?JCZ<gC4}a|XG &l(AM># ͡i߸ ~Ns ZJ _kb>:/*$r3C?F/ &͵ÄϰY U&bJt{ >wÙå @}vsb\qqdFe3̑z3YӹGt&Wݯ̾XcbǘE.)M6[0&n3fɻLB9LFw'dk\c_o:ts6Yj&s)38vWw0o\x{7慎_:\a8;'7/S(EX;\cn 4ͼ~gө@.軿8w5clKc"͒vY;fK/jyqj&bR=`:zk96.и6ߜJ};fS_VYw0qw,dz\3s׍qfԁsptVvfi'wIV.[j޷ǜ8v8Qc#6˖.zkyD93GX0/ |\j@/,`4 y13+Yk5,߷N9N)p '&w=^鸼y={4e#?\bGo1R vg k,w44w K̘3'9mR?b3n:^x Y-9p 7L ןEM^FX)\4?Җ\Q~ܳ:/޷ή+[_xyB%8v"ra'_|}$ڲ#|*٭[8)X&n9Jf̵%dtjOvxovZmQk(y6[ض/G5.3n5'ΦoɝrPuȟ܈":fe؉b綽P2~ C4"7pWWw9~!!%3Rb1X-FK)#&3ٲ<54SZ#$k[a*HLv 3v39V,ƍbbxvHp5?&Ԏ725Y3g;|~:2,f)H1__|RW0sZei$&>jԚ+oτ6P#6}`0wk4Ϛ@ Gؒ^FW2Sn挳3/]\y36g4.-_SYȞΜ<3bɒ,Vo 9CgN28d@B)Q2,^>dwKXGd춑LJV[JKޔKeZ+3j+R %[uoo( w|"Dzol+ZKҫt(~8v^v}+B.Y>w^Nɞ/|ɀOPc=K:Ӷua" e؆w ޴j[g鴈ܒ2xÝ?K_\{uIx8Fs"`*vounFΡz_c Qf_Ce {_#|ؿk˥(݉!q;P}'O3fwʑ/؎6'n0P}Ym;mZQ.Y'-Ƹݱ-)f Ů{\^1/2:ߡ%>+yo#/MhA r9C?^KV3 !ҒpAP_Ç0~@ ښTz&LfO+p{6HwVb/ҹh\2}mFH7eF"< VcAˏoY>)ݽ ź/bϗW~X\m?kuk~`Kzrzg̎TdNlc}hWtgŅGѽH.ٜI) "u-Y(umHvRG'ɪ?P%O` lek0qYB```J,ϛm+3'KoDL6`D?e""""""""""ؑrC=$yr%#p<Cٶۋ +TZp'm+=g:'o{ɝvm_K$':g>CϐO$'mCDDDDDDDDD1899ĈR:"=ccc3'ʸc1Ǝ<}<^>k-ytEh>DDDDDDDD/AA,7?.""""**SuNu}'݃OgϞŠ/"""";{}_^y;$ }'^y=;oB<s뛴ȓ)QߣWDDDD`I?B~gJ'Mxa-_HOR?QDD)qc}schd +""OG?J }gӍ3jL(N Ī92^mBxB¤M1qt:v;F G? 'ޝEU ,"&jV[>jejϯԲ 5++D5Eađ2GcƙssuVBA=g3dAgpD\Px*{gnWGǐ<~pԟӝh`ܗ}x,E寊cG:4F*G/Q߰D͛maEТ?MX Ǚ7ycU/DаwȖQmǵpPm3hws=ձuKQsjl oIlWjBT`o@- R!B-&jE#K7> ë =LIsթZi+$Rymƭ ڻpZV _a~N*Cuxr}* /_5~L/Ihw߈{i4ZlvB%Un]kL>ҶgÔ8F<-e=N5˅ȯpǮeϱqDumZITGlGOݬ83^֩* _(=0u}t|i8NGn5D׶j:F=O*b.[OW,J~fbuԢNB"(n.9WyxGOo0.ꝡ Ux}q\ Q^95;f"*j]4Hz}h4ZlwҔd^< ud=u/Ѱ\J>^6UM=vmUGFiKu8 "ZǨ}Qss턖=ZOA/.WӠd>'ʺ>{Cg-Sl)W]vXK_k.oWiV{V=d*XLd\[=GSE}e:|A} =bNYӪ?hV4*&sI٢d2NKt%Z_^*KmĽ%VgF.Zb&Nﵠk{>JfmTu~0uSh8twH,dy>lQ Z1%TW^sbxZ5ؾ{SM|o{J&ި6k_ᚴWS>N}Zsv7tHgy SIh`Kcԯb‹jx^5<:C%Ҹ稊gN|Uc&vӓ?׺yW6kdmW䮀p*mҴT#U߽ K:n턖l*(~"M*W^,l}r䱘oWvjj<6Ve&sO םPPesrZ^{`m=7_ۨ:9f*"k ow}o Q2 흧iﻪ˪G&IM)VGxezj*!\XqYjX_ߡ}Tc7{& 751 ɷZy*qbޞN5k>o50.#͐aV*DDd^Dȿ~n^yszURJ7TPL?xY]$R!qjԾYu!#~+ULt)!BOOcŐdVȬyfjm\xIRU9M٠=V#Ǯʥb5Up36ҷ˦ꍋ4{Tu6IjeQW5sTSe&+qd?|C+("f}bCaVxg|RM o<{vIY?_i(vMjp0dͱ?=5K:eZiA@{a"j|H5>U|uk3n= OiºR1zl6TiɐI&4t61Q'-4^Դ&& slQ2'tCruOq\v3Umw\gXyMiJJ %nJ:>߀k;˘I9zYU~u@o&!Mi7\}:]2,{JUJR.kR)*f.UF7NRLrQ7n%&y8$0ll6C2w9F o[j2]UzοʔrWs IDATsJ)%)a8gՇ:|2Yi^eJĵTUgܧjYy{QBܾ WT=\tYVk٧4d2aa5ǭ׌Llo7ee%BMsɦym+B2[[\\O>M%~}5eKdѩO˂s~9@>fÚĽVH-QNWFeUl(ٰIu4ݙjX7dwQ7Fzffi6ðx<9(āɣa8D>u)15PP`;9EU:]TCoys8ODžY|PǙHtY3cukjdnRR%hJ:ѯf{gd'Aw$e+pOYӊ/ z"a*]zX4cv^m ~]*JVڡc#*Um\i젘5ut0U>bVJUԺI칬{?>:E/S\*` &it=j6ɰWV|uu>,F/26=t1%]qDM<%;kp|K{*eqIaV(Y֮/s+6g'AelE. }nU}[ZA+EW cgq Aq]32fASbW;NSlt Jx^Ӂ#uyYD1ez~؟4cz: SE P"3&']ndvxkrQʑr]JEh~9ږWDU7BګE?My?"jvousyN+~ BU9[S7дVJIK F"@dx@^]^Hǭt!}qV~tYU&εN_=YgF=U~gW2%CiU?&J2ɯwiÎ }bivLumhH*ᕪ3ߜR@jb[ʪZ0qbt^TŏkL쓺28FU\t#J.jy;%ێ뉑|?=ڢJ/G>wu|)^ջ=E}4Hiԯ|+_JJ7xo8V(EMղ_ZV L9]Tb9Y//uC~T݊]4r`ǫy9_YNH]thv}ƩUDl;[wf}R?њ7TP6閥>,ضxY+7VL֚mJE;O5aROmK+zUx.!^%|U} 4kxMXzξ$W'!jL?5FsEU=4TO;g{I&yEvn^ .Ju;jƻԥf\n#Q9aMI]'C5Usp-D͛NPf F#zL%Pp&Rd)[3}AFZC~[hL֪Zo1kҸ5 :sլB"~L,&sq==6+Wk즩JI'4S6h4Mxt]UP1I gn320$zf}"=i,Rs? ęjDL> Pbǖ2b<'&ktyTTYmuP$A.bY11M6EVl6kC~O blLڴqcnjS۝ijĽrR^?ݽH+O?_y &D7KfY6M9nC NbsWc>rHe2$# |ŐͰ#@םż箒QQQ:p'&wu`^ @"#s܆ٝļkGGG@A + !y pB1 c'$NH<8q|g2aIڴq 17ƽ oqc1<01 c'$NH<8!y pB1 c'$NH<8!y pB1 c'$NH<8!y pB1 c'$NK_qIENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-edit-analog-channel.png000066400000000000000000001166101501654372000232360ustar00rootroot00000000000000PNG  IHDR-6sBIT|dtEXtSoftwaregnome-screenshot> IDATxw W-b[{D5JHBD !z{;3ئ`-9s99̹Y5g.6NNtz  bQb#"O^v뷳/$*ʬsws.7o  fb1[ ?kqqq7%nj<e[AAa,cPT{ݸyg9]<h/:L&3ev>AAxiEQtXYM `c6[2;  )(NCӹ *ZXAAx#]!^W51EAW#d&66suF++l1 O^Tz^4TU+Z7cRuev~2D%!&8;}f@o"Ӽe(EQ N$Ž\p $6X&ciiZiz, /j!bGΜGp`"Txh4YE2ѿ1$8.@9M&W9}xf78pyP^-̃ I>Xi4Pe$Yǒ37Yz:nN 鷺^41E2ByK2}-.<-Է? ~4:.WX2~G6H̺ \xݎ$V3]Dd?ͺ}ؾ"BLT,Q&L -Ύat'1O|ojUf dUUyXC/I|芯/ni"hqώ#/|=⸣c%))~z6)L_:SZEƲӻyՙv!['u]/ozH \!4-1L8G;Q^Sj-@#v 'na_dF B& Tq3DsLO57Z8iVI Z/AӒO1%`IvY %VRh",(Dcl(|I2:9),߹O=lxR/)*Ϻu)a+nRqTX?,D?!dAQ5T d{I2(XT$ G't04^7Tb1aR4d[ [{4$[a^6RoCoS7ye_*[N!DbelgeIYo';=r:e(oٌ*sأD=G)r:L& Cʋ!ZZ!c+*W gڻ~ˆZݨq7/!ۡ2G{PK j}?, ?m~Crf17w5J́lʱ.-X5ٵ>['=#(ӡ35s{hF̽`[ƒa881E&Ӡ?9fƥ٤ihB mU ݿāWt}\i a2~?W"fhҮcE%6uiOm_#]eYwakʋ.;rt&+5B][ЫZ; 7}VH> (D=?o船{}kHNioEK֊KTS£G qv8p<"514(:FM=CS^4MC~E,by%f~UM\d[kl$0=s|xڪrH1f"nݺíXtbX!6~Y{oj)qī`v!o \%8 G jwEK5Pغf0FH٧. (3GKnS6& {8a"6\! nU 7Dl,)˒@u jr@LY:9nb6qUuc7&+?IFLyz^ 4=c3=l➧s*O^qXg/HӥLh![f3E$hTSY F hH('<``OMПz--![aYZ_Z쐔l;'cܗ==HYiTov(7g+dMKL!;5vߪE32.i'UeYφqYpɅS!WJo`ph@6N%>%EOʫ@uYzƠ=`7ڽݡ0߫Oͳ8PUBpf%Go<%h+;d8e ш懛!53h ;S?s/\zR\kI,LmyzΎrŃƳ{;L\5KnbRޚ o#EQqÄ˧3byo 1by PM7d$ EM;Z!ôcZ,QD+IǩȚ_~g-8;Oew>NsN:Fa1ǰg |bKap?+9ն26.\|Zg$U!.HfT7lV kc- ympf$t~δ-Di[?x?N~Kh'}<G]M)?礤\XErR(j88ٿqW6Ngł+@$&{644(m%Zh9EТEDD 2QK2$b M"\c C):j%apӣVNsÊXBJI)N! @r,πyS/Llgn-J\dmIu 2Bz!i))$% IoRC }UCM+k:IC(T9@*|\) hSJ22cIvbSG\{,K܋iT31FCʖ Kw/P. -k$(IMr+#)J9&p;+Wf)O~ԮvO3wJ֓8b-XH̡pb 68S>=YUQ;zGsغe*'U*_w=^J`!GQuR f$ᚊ%(PeqoQ@/^mbnpCXjeܝ ?8o@#Z};vp'Epܽ' q=ʗN+WjqM)G>Ɛ&h"ǢXS M/&>)䪁%|05B1jdinڒ twW dB\=.];xkYHQBTL`LYW=qwqxJofYc yFiyP-tYaq_ߢnT*Ov[JwopDY됢??.=WF33NU-L=J^&DFؤS,YNQiM`z6zJTX<.6Գ,DG΀NJ7hԪ*~{H2D%6"'5;d%*b,X MlȚX5'Ax-**:G+ )jWh"MWCʮ'K"Pq^6K\ a 7xb HN+4,`1ނEՐt:Y[~TʆD+h0` [q Xs'eL$qFlm y %=>Ҧ,}:+[TQUQ6QD6 YN{K*RUD"+%_q`猋/lF E!..zq4waѱ,ڎ ѶY{BL"~0QWH#KΎ+zK$7OgUl (,ڰ)^bT17]b8`be#vF$)mC!լQI$Ě7j.#ւ TEl`QT$YB1s}A^- F ;$Yh4\|1  Y\NA,A" B AAGA`%/h~E"𺩑\29n.+ h^~ (γ\yQЋUP 7dQ3|Z>o[{~,%^uJLrF2 Έ寮p0e#%kNN9;F=ceN"UMO 76|C"])7z|5mrgA/:#vԔr.:wG#B7,Nk F;S^r1 9Ms;ų8_6؎0t-O}&{0$c$l/ QE_7O⮰iROɃUlJY W5O]I>xrkRfz&bJd iш;=;<&/5iɞg^-fLlֈaѮ'ʍ \9a X'F<Ix:FuI{>P!e]7sIɦ!(/\hDGHZrW:_"r|Uh՝e ǟ0rͧq`u)7|iǣ@r LJT^lǑm%*يF \Tלn3-wq̷'fq&e=ǩۈ}RdȈwTTZ [sBO%t< XNmg=g_7^F>i#:/̫`9*U@)YNP U(ˁ/(S>'-=K[{"Ǟ؄ˮY9W3I⦚r 6D"yGzXz9ٕ- 4l ||r(U:hS 9rSQ#wJZfE$SĖ#lx.Z ԃ'}QcRXJ⑷m玚-_V&o6] A%o>9Oj!_NOp^j]gp0\MʇBҾ+W^n88jfiD޸Q@Kv<w%l} SBy?\ʗ!ChZ9=]w$/\P44.tv@z_%#6^ǜl1g3y%dŷ?˱H)9y;e U wC&~wtT%;KYo{_b㰞L8z_v1S9Z{sz`rYh|Oc K^А VrBSz6"_@vܯtfYEMxm?1&rTN~=`9Þ}pZVI+ZVegB37bT2$gt9: s2RBfp=-e Ŗ0{᫿=^J+-oQh#J6GnLnXnCΓ>r&shqϒ}b LI h;6)c>Xᛶwv=JnͰ]&4udp5 ldtIm\7cz-ihؑam [6`bc;0z:փ&kW`D_rF"5 MS= or\Z87yҺ1{a*6ZQѹh'GX)fŢhr#/Zkcbj39M'\pߙ]AEҶ=[J7ZC,]ǚ)-0ݟ7t<2klt2wJd[/;N=Pt5^C_%Um9t&jW%W*VBNxn;ףU;fu9eI% Nׂ? @1&MְwlMꉉaӲ+Az~; Po1-3UU4 ]Yu-ܾɢZ^殚~RG&WxrIGfz{Dj@.iT]: ٌO?]  Kݭk9 ҡM>~]ݽR/ /z~[@<ظa)%Y*U%=[Ӥu}J2VcK( =!1ыvrzf[{0q\W9! !;a ڿ'm:ÄjL|7IOВjcZ d&s;yHZR]q;r22s_}cP ^7j99L7OI IDAT4iL[a*Xgr@"">z/2J굥4K Z8G!#XX|q(Oܙ`I6D1&|AԅE3[3yǖdGv1G8p҂}>:P!cQ5 4j'l}Ǎ* GC^Eʡܑ'ʋ|y;ʷUk0I.s{bM&8֛be=Xz1[#+>2;NÏɆJF'5(Iz z MK{}gFf}՟g"J˃Jߣ ^ES7;KoƲ?q{veML_C7>|]R\a+=%hyk ŽS\lZ8{FgYh>PxDV(cm}*sa⮭MMY+ íRgSaUZO^QG([ʥ 73nRxGiDG M8jmN%TVfkG){Y~}P>ێ[Nx.I͛pL=\!%Wio5…'ϺOZ1G=3o_{z2 ᩮ w7vM%6Hh(*htsu:B៳t4y` NnB1 Sx|qa36N9C⽻y0敦[Yol,_J^G=?߶Ot},a-iY᝿+/o.)^==_fLd`j&nnSt(OneYLրDޟRd69p3ngPN:ٺAGxJ 9g-k+fAnXHɷ)Z,A>HQW=o4[t㯯b2nBKByGzO>F%yul- {xAmJ}}z~U=`|k@-W9q_ucPa=,&G9Oyi$Jԇ ~O]mߤt2~qܽp}UyG>nuIy1]eVYniH!EWS>44 :ӫ 5.w{РReNѭi! H]ʭY'7)*i}%:|9~Fk/\#GsTT@Li0Z"IuЪ[I{JC׿3yetx$k\=S?9{SfO8e/@ߣ;8fˆ鴪9hK<Ԋ<}В~> A{6N5R*1UL[>-||4*jL•E=(KWgA^W9ư~@I@j~>12'ƠٸWdP ; gg gndCf}盁8xڬ|$.c4xy SrN㪶t]CGPoqY23=9QKkMnτoNaZXէKnLCrɄ (5e2s׌KvxVvm-4 4&2@FMHAH֎yQ)f-<=- tl'~ (z,7)$n9QwKޣf\ L>`FN?Xg#t G wsf)3fT~;2N>i5c՘a>%i)~a61/F b!Ly;2o\cBc̟h?l^'9_FG3@t_dr^`_ﶘch7$ov0aU/Ӿ .$BzN Bг~gμvk-HGm[:xZT$Q?/WP'';glm s͵aG30Wl#0CƎ ۊ^'h^3+3g8x{:(Z##ǵ1%\^A`'G\-Ǵ̙;WyիviИ1%KW*T~[5WhOgټ; pj ' jnedeN!)8zLF<υ ԙy LZS71vaQL׷0yi=lcbq ?0TTwJfLy @E揱3m^Pv˝Χbq,3G~i."t㿫g=?D!lZf~k{x{鈾~}7q#^yu3F̈́9 s$dAKء3{/fVvtI҈C3/(rͺtiOVݑi~X,XEldt?Wf\*4"w}K9sW-gF'o@wP6ȏg|i,-[g/gßLԖS.S)^=2swf^Tg?$?/dٌmľ]nR׋3?X?yK0kx'/KGK TȖJJ0;v@y$7WXj,:ܜ~B2ŋk9s7KQ"s?FIrj6|r$U|]qGՎ?/&q‰3Y}ŗ&K;4ᕰG}3mPf]τ! ~Տ=ϪV<^9]2u2hry}o~Lo)S1+fm *'~%ޣO\YkKBK}VyާH^rtA,9]/ʭ߿x\Dߠw'»"Y2?[hB`ô"MaގLj3|N3 [S*ҳ5+2r`m'$TSsaɴ 5EJ!@I΃>ɬ 5#jd6`C&Y(nFAJ8=_{Y$vmX^xckY?AE:, =% _`L <'7VQY3Zꛦ^eA|~8mH}.㡲sj=9z eD뾍-~,!ˀi|oEčXr@Q_}c9EN1R$)OHc 6#d7gӾ}|H}x'wq X[i8} ܟV o3_?"]n>D&ȷϣU A&psP`5|^1r"8?ٿybQ}t˝"T >ʱH'U)"`Ӵ=]oZwTQ8C- ڽ|5՚#N: Wh7GhȚ'#^闡DO5G~Z? M;i־!E1l,Sr~b}׈:v\Liɤi:` UKLZI/0ˤ2M~Y̤.)ߤ?o"]V=n613I|4qLDUi큊1gSml0R߀~-I7›;O,u~ދY /=EE9̞:ؿf+7 S+<+zL˓FkhWXKq@%eXx*&9PjU0VBׁ$ *Ozݑ5ԙlCUzN6*2Z@='2:̘J򬛪t^y:T'A^&{m2j'  7~eVaaR5j,5OP|OʹM=4l,'J`C9`q|ɛUB9y1׵4;BEv hV,Kǝ3x|R۲mRl)cP{*xsqX Ý8Ta5;zO]ӱ+%|]%;“X:uyOq4ɲ̆g|}{GY._3F)ш G>w" o2y{e5Jc8}3sD_~./! /CF;SOO%sؐy7}fQnbTdi9eUUqusP,#dv6rŬG뎐>IsPUN䜱4ޥoK$4$$o)7w7쬼;QdvV,''g2ZAAD  YZAAD" B AA %岰DEEGG GȲ<Axc\t4@27Kb?qsw!7%zͺ Axe֍`$IZZ-\ dQKH'΍W'*2kЯ BjKf-F- s EC,  -E. ex  =\?{gWE\B@BTIDTPT쮵۵ks tXuݵ^Zs TPA{J* G9;g~sd\" bM KȄf42ͼ4f555<̪L'X=#pibu1yr`*]ky-pڝS 7 s7Eǰ0dcp:xSxw~Z&D7E;Ğ~iIW*aӼ1sm]ܮԍ{zCh IDAT^\19 ۰4:]GqElׂ=&/c`ȅ1n" (3?RFҿ+ueoߕ-i,%`Y,?yA۱.,je2!:÷?br%#O!Tv"3C೨yJLN, l.}|:9}+io'~jߕ,]|0{SrjkZ;~D GBӔAaGeɟ Tl]o?dyjdệ$W`ZKN`k/vf.sO`@FTLT*2};f_Xw6EsgmϷѻvhcM1 {: 4z/E5.q;~bܑ+՝X7?eC7Я ϐa{Ը+A{J6,ʐ3K/gQ5S=wX4/*"fhoLZGڸ}񇡎xC8/Ii/C)\_\8?tͩF`P $LlB2;|۲W #(>@s9c]7fQݮ4ΤyPV3Kjq1^$L!-急uI0?|~ 7;SDLQ!tqͿU트Zcɍ2eJmn^#X8:(C<J1ˋNͨ2So*hU|OFZ@G@kϠ\G~[S2MѻVĊn +v%((Qj^*nΘl]7f<=N[99T327O!ܙ/);/g,Q>P<s oY9l eڮ1O|VRf1,u.᪦;-SgŽx܎?7VXzj)*[moo\$)JAxEL:{]KLX^ܸO2Ȩ 9ztIͫ'8D)?G;+ au s$=vnɘ55e5u~#pO)pv↖2;l=i1l#ړ|{(踣 r*41J|(/JN׋B7~("S%g%={. k2iXB:ô? IF(f(PqjJ4X/~ ѕFĦ=B߄(ʡ hǰx렎$Uv8ЧfAx'fۇߤA X.f==l{%plVWi)*>fWT2,wJzܗ){E}-g+]5 p6\[7;h'"1+U?G1c$[SY#VO]ODX%EENmqCe<;ک!Avҹs'ѣ$t!xN{nHh^Cte˥>˛vq[RN."R+CtʚH2,af* YṞ 4%6͹t.jZZhFJhC)ExdcDV6׸2k;W-!f,OU+cPRęQFXcNe_siʌe0L&~&>#~{<%\S)"-PZp%yIXY{UTL|As*)/V+3T_ݏ7o uL21g{`Ӫb XTNzAJZ߄dz"XH&6VkZ>M4Bt]5Y㽪|V^]34 eA\vEjⰫ s 2mPrEoo7_i`..9P#.F13GNO!D }nѽ ~{R +)R_㑾= r9SB "sƦhDrSlR tZd8'B`ESҼ/SGgq<.Aqwd \!\]1r.G4Xj߲$dbc2rzjF0O+NSJ oyNMy5σeKQ+ @ɭmh`|=Jb݌ƘaàEӽ`Brw40ԏ@SSTZA÷MavX~[dMih%Ʉ G%5%V%CCr۲>3=ӊ|)Gb663--bz)5%a̤&Da/G1o?.]KOaǢ|G -$= )pqhnZqS@4ncKz5ˀU6cnZ=qRP nKN, |)RD ۸g\YEp. {]9 ڠPQ`GZZ_i"]OtC'2gl L|YY(Z҈z (&="#f+}wMm_pW`T F֥zTy5+f¢A 0s.^\2iA<6%#:3'J%vhHg?ղ17+5pT@J/ 7(ԬDY9mkF1:L9>( L"/&3j`s9ZǼ pLQ%pehƳe;X3 !xN$H"3nX"~40k:̯c#b)}PcHT]I[$sm˼3?8Nc2~ReU~?u$n*c](r Zvļ=ӌ~F\:oJ3г[W>{ȧ$SdH&4|5IcI|7 "~4+(߸eOK}dYh@;ٴa-~ժMz$tpP?eM;?j̦-X[GtŮq ,#6㏲89+V*`lhhn,O%`_{\V?&Jŗ:𸅸İO)?x;+4;s74-ckZꑾ_vɄ:S F`7Cb1v/Scq=J7I\s2Kͣ)3ƄV, nʾ97[S'w+ PB`9_S"DK G>d/lekb { "E߰AjծR@P{ ۳sfҢJX-cE r̾3F HN{(עvcPw7rA@ H1A&C9m@M"΍EDZY020¹3(Ӧ ϝZ$Ib]_P(Pgc!w{xAXhXN"8L `dhmNr]b89WXu׃E")D@ ;w9m cllQ " 9* W D@ \-@ rB@ bܻ۷)AS#r@Mr)wC)[.M:)"K!%AVY'CDZr)o#SOwgk:~0N NJnF&z_Y|dȠhbJt^J.FfM^id %צaV[VT8"&7[һ ]l12o~)gHAYS%oGŃ9~kɆdy1GP̈NbM[Fc%CzSγ5dƚQ.r(u6XXPpV^@"væPMRŷ˙XTIZښ ĝߗznX.S&? ys<-kz`gib8^]U ;%/z_O 1*>EIYt+I0om)O01^5y5 $$gU obȃ'=>'Ά0s)Ga]>ӮaT< Hbx4`Z9ŷˣ}=c3ǯ=!3;bRROݨ:8@׼J`yB)리 ,Nڗ!M*#$8Їc/$>Ug2AI#-2JU$/_f@vu +3i|"FՌ;1}ʛV do|]Tкa'ŀ2yA7)38[Bt#w>G_$#G|kThiqPM*ZbNtD R>\>6dds6O_m_2g-h^`O"G%tC_9ԏ]B]x KܹBBxQtkkk:v@N"EۡH˜z', \\AV*oIO?-qt M;ĪgK[yz!#& ZX|.$sv7_iOޡgIaО@6.8hʩ8 i*b>$uPF<(YV`y%Eɛ7~("߇ '#52u95҅F!ib`IYˋWxp_V]#^8V寘Z:ʥ[l_"3_D{ @UEDskG'4(R156^RNfRY=fo%'E뜿Mj8h&%-EL0Բ-,g i(2rτJG@kϠ\G~[S2b/,7r]ejTT3?Cq/ʟsVc:{7X홲|W?+Y'"1+U?G1c$[SY#VO]OD|A+SנqI;-u`VSuQ;51$N)ѢړU62.F)d7!58hhPFumeoڹg\ gIW C T,P{fFyi'jGB3_!G&!I0eݓ=w=OC WYFcQ$455ڊGNYɟhjZLTO ZNwYZB)_eFMjξ6+W~,"ZeԼNUbc[+ >8uU,8=L,HrgCO_KTiMS+@II\U5O1ޫ #J1٦X 1+e Uue)0P.%WvCzlRJ=_ȔhĹ+S \KRPҼA\ =cXkc=Ϧ?vTA.y)L3F4)=oS'ɻ.秏1i%)'Ep(*iJaHZ㚩m',? 9geɏ=7O^$4!Rr2FK ԨUqmȲ~ÆIlΒZLpTr8ĪDxh(1\*ܶLϴ,{1Eʸ㡗(+63-Ӕ5^#O`^qV d+@;ѿ&~ˀ≬|SQݽ<M4vbY<.܉nZHh9_w~񾉁=?tCewiQɿ&Ek/KX4Sy2Šf2D %~z5cH-K}A#_Y]jFKE}:i{`ېb@h9sQ{/XH[߅BMmJph7ؽ֤h$Tw ? C? <}dV6ڈƄqّYt*[piPY+(M} T#Ax"WN7tL/ 5>)L'iiIIK\'IE¬ ~ͨߦi+xPDC Iʓ~P!Z IDATwg iru GxvTASrp\y2oI%49s͡C7c緞Nk|-rb%Y2EÁ+1o$z4c7d ofdtY+ZurHixa$N` MQ,ģNu2ddPي4!δ.(Ԟ͏'q 7J)^=g𿞩I E;RGO㿟 8|zZfs/S;kilixKI"#K6^q~jew) 9ȿcDɍ,GРxbRC+=bAm!Cf/!`촍i&} _L%$a~!ޏ$`AwjNkлDz{2m^Y>9 ѬV efqYU|d|^|&5RVmju^cn]~y3r =wWjN]Ajծe࿍B`:_CO5SՃlOˆTaV܃3s /xE7f;}@hd!a^g"LLq+eΒ?-QH$BqfZqN]#"-BDZC|NN%AnGF-|##6E8K"NX}.~:Mrccc(Z>Mns.18gIΒ E_G[[+C-@ H7Ydu2h@+/@+E A@@ !Z@  h@+ܹwo# iS F)5bd=_-Ϲ;Ph`k_,MvVsHdUmܻ$)(S\N"Y@HR޾J9m@d$eʖdm[|w e=8r(c-'1870d`daaa*/D@ fGW"AVwW "YRw… R!^ș )*ny,,' @ll, 0 Exߧ2jҾLU2y U+5zHv̿-'0pEial# MberoZY 7ZuNYd"aۼiSM7v pƍLf:c>)RG^tkiI !GV0wy^xA:_sz֜yq5Eݔwѣo} 6/ǼSgS|ߧ!Ѳ5Ʀo|<6-)6Ӵ2`T-7tsZ]ɡֆrֆ5é`6݉ȃ0-^&蕏ÇYhQ NEm11+Hvظxg ̈́z1X1{,9ӟ3#&-cCITYS!/o1q*]ت0<%!yA:λ}-c0NL;UrilYf@1M|ZCwa<%ʿ'Q2)6-`|2#9ђ5-uoPhOHX:9 ZNc;d@uc=Fܸt- H䵶E˼S.QsQ[|1K]0?+}AH:後׏f0Q#5.Wxw }4F°0vJD+NA:,صynz&*Q?ȂJ/nG 2D=3Rw_|8Ree)\}8zVj"./m fS$t_WXXZaPƯ`ل,BBvj!"w2zhǧ3Zl-< ).Ƽ#+UpaD4 CX://qNpˀs145FhYUo_?u9b"zfzaK).=3z=Tj/t+V/#/kB|Ζ OR\Acl9Itp':L'cPzl1R~ (ƭ?<u^l |},4WcQ^ ޝ9wFLCxSx$}鋨\(+  $ ㅬ; Iij)!ωҶ"uQ-GE i8hy툷CN)hz'K!w,JPE+U`y%E!o?c?P&N2(;SchSWŋpT<=+mYG@kϠ\G~[S2 >] /kގCsE><\o')iƟsVc:{7X홲|W?RfҢEi7#Rf-˧h^, H!䶛r,E|$ `_Mɍ?pcT. 3JרNe[vcRKs~P2%PU*c))W\ͥyr8S_w𯌹$'%=]'#jx[+}G\گ:4:(LQ;B W5^!Nn?*/Qnsx$Uzw1Q<EgG؇PaA,%[M!eؗ(JXӒdKS`v6sbVxgB|9KNM=ϒ.KOV!*nq0Q'N Xl*UN.QUWw z^5wGBP+orh$t!>W >EY~>'_?tpk5]o>(dN=s98b(A ѹ]7lzw+^jϠ e>uMMM P/jԟ3G"u>9ĝN-p%=ܵcjô?RF% 4A Q)|&%Υc=_'ՄCQ72z2k}Џ:I`KH1j uUy+Q6v+AVq!=>ZנF*]tu!$)#-/CQܯ֡FfT5gu'1bCI LR *_ײX%;^y;2$T}4INwhE@,Vy.W5{@A\5mC򘹖zGOyu8z>%#n܋ҩM:z:DdO4;]( {ix۪.Aץniz|J€wy<$2q*Q"L V yȣ~ah*{nNNKdHƾhƒu.Ӓ*f¢`֛kjJhaT TOntumxD>*5YN`P1s{pxh(dYWyoל=jSF}Ľd&#tMGJhq(KSD@͓cǸpU -dkRޛ Z[mbZ~u)c`8R cB`<Ìh3Ukj,(ȲH)N8Rt\<}Уc,vlN:T%D.8a@7txkHq|qu2`ٳfxV;KOۭc=zdzʏ ZUXi7d.zF:Pމ7Xt 'Od囲pMM ,> ~)j7M\Kr=AtqV!UkoGG'Xz|1dۉ=Lx@G=ggg޿{ȷI prrJzUKOp,TQD|`j:٣kSOjSƥ0+nX*JԥFr)-+^ɣ&̲UtH&4|5IcXID^sk+\#fczn tI>6^}phE 鱖}8u`S#+WNGk!d C?q'hɃG2E[NAZY`R ӻ=@ >kΤwoY2إ@mJ[L JׅDkcfF2>Q1BFљf5 -F4[lV3c غSV2,^+0\%qGӱ,њNCtC$rÓ1x*w6b2[M#P&Dt )`DB֍Gޣټڈgӵ T麌eȟ`~U5`Ӻ4e$h6=ΏSM{yfm*|\0+#˟{B2ѮdҬE6C 66mXKR=zʕS  wįZlUN־|ȯՋ^/pbsgld;3%'W0ۢ9_[7Kj*Uf^v"-Df;1O}qFY~a_V~̺Q|dsl@ |U21{|"V4vf0Q/Z2&nҴ}ؗ- |@Ɉ'P(P/?-"-m1AQoB4ȥ0203u/Ӧن$ׯQ\ F>|jcccj5$wֵ'8CB\{xМ6E qȏQ>)-w%FS $)Y"A]Eا-@ H7%""ׯ_)\1FFF, D@ \wZ -@ rB@ " W D@ \x\.[DP88&@ ^hɥDĽw?s" DDz9_OfvvwFFF9m@ _oM wmV@ Ƞhb@w ,D~*B2~43w+Cϰg;S V%*Rd~!ć@ ΞyJLN, l.}|:9}+ioM7! 4~EP\eX{o\@ dhCYw{Pe?GQVm}*&sǺnƕ6v9)^fҁٟ9gj4U&hǟxA:.ޢ ^AU)X K+,f%Z<&0+7sRD-X"b8@ dM0FLTT= 5x:nsx&54OѡƟ9BA0MǁNo㺪8EQqF.p9*ͭLE3nir(5-GjT4ܸ2 {0DDd}q=s/_^Fs؏9PnůpR6l\ MQvNǔv&jPU篑uL UE 6w'eIDATs<)t_A n=W^ ]ݥ}tOnO!SzZM6Ξxub|PWbQJ =g0)nJim^ W6"'=2MKsr\S@s?BoR ' !Qh1w{ws` 1!AWe ʭcüCoÿ aOwNt1n.A;ZN SuTl?VXe%{oV[ĺYyZTy >Yd}̋Ǝv>Y+_B ڂ>l4KbnfΞwd{!Pi"Bo$0m o_W,H!(;gmϒE}B"h/axG>9ŹkL[!,rB!!*>Bar1B!yMBB! !B -B!0ZBa-EŐ  +Qllm03͵"#r˳Ff! ǂw .BIb"h U)D*ZuؿowA7CG$pT$QxxxA7AG$N .B<"CLBASS$Q-φLUQX+k,m3))rC7SgԃM M2 UBEYiQ M>FXY✱/MZ_ic<˻lbCYZ~j5A?ǒ !(Ӣ};ƿ݌*+{ftyO`e$1%cĐr`o&l2.#fioBbK+ѧXA{jV¶<>mY|%ey©/Ѽ..X[`Y tOIWKWv})kgCEz=Hx+{B+[#:gJDqpfμԙJf޲+ Q ȵ|YhJ@>mUlŘIAuӞ ;ƙַ4 eb0̯RMrR\̪`6_WS:LԔe$E,{} -Gӊ?2iʲؠ\cU6 =Qa~MC|H`<$'{i3w݌YWE-1C%ױu+.C>b XȨhwovJ,~ CW :2?ݻW6HQZΝFq%4nߙ╪聄+Wϛ+WQ ZuThЎ )Q9;G@S!GoS6, 4) ϡq;l^kD:U1|+ئ+U4Wu2OBK"#gzbZ{a ):T@ʞu_ ܌}01ׂٿ?Y*~/tmˏc\"N&w w1Rz|̮P4Z`q3L]׏}TsW}؋2SoXQ)PVUH!]eG9XnMffEܠWEZBK mh%WCcPKZRÖbDCS~+6b2²:Umh ^'64MI3*lC璸h:}n4V拒fm]^h>acF;~Me逷kOٴ pTەOH׍ڭ>ޓF駤^AS76ycfN)jeS{EMҶit:80e71HJpԤ5Q[bH(47ڽg/^i@$?^Ƕl!Rq? «f_ZEGӂn(*Z-vY&!D0;|EIH"mKcmmZ~j}BB>tDQʋI"0DvevyH8 ?hIB48>`'%!$QIh1`O3aS+U~괊)\MzZ($(+++EAd"DjQ++n"Hh1Ps4*UF /ś* !D ؅$"""DwB$ -B!0IB!HhB!A"B E!ABB! !B -B!0ZBa$! HhB!A"B E!ABB! !B -B!0ZBa$! HhB!A"B E!ABB! !B -B!0ZBa$! HhB!A"B E!A_ B.MxXXA7 baf3-w8r'ZfSɂZOzSͣ6 !(D.hYnA7Š??%,wXEǴ7an?(WuE!(4CUjv֪}(V jq͎hW֑"Dxx8*rb͉.;z:z9{$!D1|nlh[BE-d0{v/ ~uކh\3"Ŋ\DVeܪZ8F kߛN jRڎ$< !Dqrr4nx\/2a,S&6>;OY y디4Ťd mǵ#Ȕ]Z=p~YFe1@r2%m3{C:u\sk:%qqAg][?٘KBB#;0o|Ѐ^^,[G9z׬ۓR(ʢK2~3u=q4uY*/qO)2'NO.ƿFoPBo'q.4*xeyS;Gwg㓩cʐ=q֍=.U\ߏ"ʼn&%KҸq#YCxXw7֬Y+wBIHHnx>Y߃`>YФ f{\9;"(ZctspL8?:NVXG{\y:<ކ2c{>ˆ}0cLyLcslWڈ#7iPR$wocTpfnYʛmEzZQQz5zExX8ܾ}G'{7XB2e$2GL=bO Pޚ6)X1%QIg1jcoeO`pB1/x`kIiK{ȸ!ĥRLGk,St%/{pv#;5Ve|rND*ifh/˽AcEԭXhdghVt:tec9>k /MÁѶ5m5{n;?Ȍ1SQEE}x5}_c5et0}K"HVO(*UdADG= !!M`0ʔqzrݸìJqVOqGn/X1Jw:KRQ(qez?uaawXt>1#C[wg [3w#\*I771@?e[A?q? yyHQrw0ϧ;pΙ\8ܺÃ$WhxX&-3jejij }o(*ѧk.y87_<$HաÍAEYi"##0Xߏ̴5,BxԬ9ӯXJo|)wm;ۦ83vG|c9Ry8;=.&3IN9lue7MԜZXM;̳d9-9i]l,}iO:#W'mJ89Z@xW39csJjz]#ڱ/[L'W\ۧLSƴ!xiIu1VG{"""PRG܋5kbSNǫWO ɔ/guX<$!֑X%]ҵUcZSU&.vb99oCՏf+k/Btt@p>?KZ@,kl92QDIp-LJTm''9)dJ k*q@n cksFˑlwd4ZB)$ nb[ GK7=9G"Izlyf}:tnq TKi~&?L)4!(V}FIكK/2|0ʕ+ FhZJ[gi&3Y;ތsպ>?9ۈxS)u+>cS;,uD\GS~-݆fƼ5{QV˽qК݇z@&u~: n]R kk;B& x;QN!DqnUeI3nh*W6Ý'bgoGӦfƦ)3wbI_onnoѱ`~DtB.c}Lke#;?]W­vcZȤ#kSl]JO`וv{HBԉ[һi J׻0h&O9Q8F;D|.t(UƬ}}A\#IIZB{@e,J9>mj1tnC/sշ;ŎOYv꣱9-ȇ yJnyD̻B! {ijEhZvAM2]^Iys\%N,8$rKGV1-!i x+Gƿu=B! ܾQd1JSyAV5p<ĕ'_;&Fyfgg$E!,!K疹gY<-OSõ 5\ۤmYܜ\oȍ[/Ԯ"H^DR I`Rҿ7\ڝ[%!D1aee(h4גEZEQzj_FC,i$**.6ZOBBs4*UFՐo*p3XX<}k27-iM\9?zY*w !Dq%"##(+++,,,pw{fy{q_ ϧ6_dPW֓"B B!A"B E!ABB! !B -B!0ZBa$! HhB!A"BR\fIENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-edit-aprs.png000066400000000000000000000772341501654372000213440ustar00rootroot00000000000000PNG  IHDR c\#ZsBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeSa 13 Mr 2021 14:14:52 CETmZ IDATxwtUnzOH.:z1 )JQ Dzґ&H MHRSHݙ$@,E=vg;{̀B!B!B!B!B!B!B!kEylO "!BdO[ ޾Bbٕ6B!E4L&SB;Ϝ;7)>>pвZ PhѢKNNd2iYO!/(z,--R]\HT,Y fȠ@ѢE?rw35\!/XEDF.W̑cl\dB׌i)ˑ:(x-\;w,B!x-<|V'_2 B!M4C>9+g5PRM&goB!^zz{'jWU5KcB!xjG/h0dPvB!^Pqѧ^mxۤf.7CxYBbyTZ$Q.WC[Duc(+Y xz(!ItuKR͠),fSt<5 BWL_+G*m-U9|@Z*,!3j᜔ AMh=΄&zո|[?z7MUʙ\>xZԦVd7A[b7t}-} =0]!-^{ӾW#?ZGsnJV@n1&>:YLLדTKIx`3M߶^#ȎA=SO"!2e"dA7z-cb4ߟv5a0wYCIDCh3{w B۾kىMEZ7+/'ݩP<-unv&/Nes.I/^9WO-cZvhD٠~)׋֢V %Vxoǀ}.J hx:huacDIBd^*n $]cqhd{#n(~\nO )ٓ BAÑ0 9M풵Dfw  pSH<⦪'(1,^3\u[ig/z=z@#N7 W(M{k:t]CgHt)OA=Ai|4#J[#J _g.s%,GwKXէz@-83A jD/bMvkۖNȴF*aG4.^J9kD<:2/f<yi%~xcK[O؇ Ң?ӭ)[5#Q\4l 6|Ԍ Z2Lhۍ h:GU*&U!2!HMxxx+; }>Ԩc= 0 jL"C⥻#Ҩ{du6I7g!H[4?$ 31]KF cU6ȅEђO@Ev(`Gh]uiFnͧXa&HxyG(a) Op^o[i7)yS^U {ڦYH%<*IیuT"p0FP뺡aמO}1]Z˂3FW΃6._QCpnV̛9sXz3o0(^CpCGEG$ܛ`U#R8oʈdȔ"C{~؆`V|$5bk$YbON4ԫL|ygbCj20~7P fwK޺)<3 *lMб`^iʦQ~ޙz M5aB^h*$$YXG;ȦoPM.m5P33g6:xԃvn2f]lgKƽꃗO v܄qwHem EG.Hػ>Kf3$ DVۧ{YY((Z 'eܝv;ND">DZdTΎcXy2&k\\KH84P̃FB t9kSsݢ}Ĩ 6ޡ]9twHtL {ʀɇ4%k$qr&ΧhF{L+KX, ag5g8wGet>Cfb0Z;hLJaڊ0XmH32la5$ɬ-SBt: >a񸺒;9"S}h{>ۉ'-'CB3%`ja-Qg%Hfp#EG-pK\ƍL)!Bdδ`i3u[8|~TR6uGӘGl~ô QjꀚiRӖE2m(g8 졕H+Y7O,f?&6zR,=-Y90#:%k`u|u9lϒIݨeg쑕~?!T0\n0Wu jGBg9 Mxgᅌӥ=Eپ}59_M@;?K!`26|} r!DˑsXݷts?eKZX~͢)cWlf׭1#v?Kg&a`j4ߓb0g'gioI$$.svYp7P">zX–O.?U ͟ñӿ~ Nʙl~x, {J`!omKBbɌe?b '_'4Qjf0^k*tnVUkҴh2`(֘.MkSV hO#|:2g AUS0/G ̭iX?k=j޴V0FY5k3Ū 1۷ACq͵AU)VMV>^J,}I>Z6vմ-Q->b!dg@.f-Nޝ)ב]tۘ/Yq{ ZX44;;QM_kT)X5 _Ƀ !^pՃ|1my4Yi^ E PlVIJL&cދ\^ u^H̩$__F^xyy啓J|7_d;JAE^A~슍W KlsQll-ioט z+GÃ> ۰OY`S+ ŽSiuoy Cq3hA#l<6Mx' 87W{O,\ʙtS߾Hr l*wSz@,uSls!0ŝF㿡Kݶ -C5 ݨϲ'fROiaXXw5@`ej*aD }X#w.pߚH`QWC%[V}aUa4:蛉4uZ"Ɣk߳=3:'4v PjNㆫh^C|67e8߻~ %[̝ >`hs?[I|Wo{S)?d-[YfLmS2A["f1[#ߦC6}z~hIzp\9 1Iand$@$!^|mCY^Vwy߇)Xu|(;t]204oގ~_?{CsD {oE_gDGvS UiM[6߬;_쀆–eO$`wjAGJ(٢wxG'5{*x֩HXއo; g{tF!^U%UiF~=^vSHi &CY^8d_!@A!@Ad ّ^vB,wwxBhw\~wB/M%r0c 5(_T9BAX[Y0}o,uU"[}ע8~%e7E!(Vn)Te(lѯB!^2GA!@A!@A!@A!@A!D %..ȗ!xDR ⹅( /)BF߸x" %P-..5k3%c߾}f)["##%HB\C(l!B7UB!K(<6 /%gʦ|q$~نZeJ Br(JV^jpufoGM*7GPLi&B8o|3VK>F)) _w1-%p0ϒ36XY`E20|1G#Lia[mo 4zo<,5fmD9ps-W1f%'/yK^/pڶdqG:b\毓8$╢r}dVƸ#qSDkDY,Un4aWc(K;؈pa6xrM3.3n[,{))DǏ2D]q8xB<+3 ɜ㎺|oФ]?һ .G1jwA5Z ?oΐ5b4 ᤀBzY s+:sQo\;+ʕu0'_NTX{>TΒ_0c\mEު6~$-|ͳy2hSN1o>g7qh % WR2;d@捦DE,9)kX*GTlRz8 ׇҩo|aE,Ҋz !ī.22CeyժUqss{-2W+Wêl| HȈma E5/=/2(㉢Sq?l wD.mGhzw~:BIhS@|6AgC~^{Tn97։,F?X" dTx#>z-~6'ɒ>YNfB|l^ P?]T_3%ފhFZ,m= ;;H&!єzR89nlq+AӂB\]]4-[i(i*RfŸiM'S9OƦP?meDָ)p?0III&n+RF-Ѵ{ix"Sb hTΒ s0:Lйۻ닣<!^# AAArwԗkrQ(% IDATbT@Ȭ4^"QL_6G$%&2P'?"LƧ:TOfy&͍^^^n4ܸizt˫YSZw=.'7b6\}dW5psr&v˦L\7VCPO ḭÅpZF6E;2>Vݹo[ Bj "(( f(\ PYSgiL&T4 ݨϲ'fRO51}ÿu:FnQON!0]CrZqo.xG3 FN._ʯwB9TǕ.Xde݆Ei]8+ N A/ *2Hx.Rf@o2 hh(X{LR(S3T)Y<^4ukH5~G8QP᧋m A!^3/}2zIt߉I-{ g]as$c?2(^E2*{cc<ύmeC:ևP.s6 =[9\ЮĆ[G3Cw~ZƛEѱoF]S?B@_ &%`> :ZS;5 qBO!pW)53αuc'3-ʍ߹-m:e_3yⰱv!S%v-6Q<㯤x_z-IM80/5G-hz|G,s2dZ;Wm#XE7u4w̕j?6-~C-u<[WdYSZ;1dopm.!x{ABdd$L8l2b@A#jFA^{P3r5a/+G#rH~>[҈OG۱Kٺ}:˗xw5_'Φ_P󌧙>l&"nczc|\N2sNbt4`gQ 6ucp{{2"U]M]N,('@UiL0!$H&L@xx9,CZNNA|0v~9ᫎ`Aq*DEj4E^Yzc;IS̪\S(XēNsiV@If]>8FXV XIVT376Wc$?ܺ~ S(f @hop ogw;}&*ʮ{io +?oH<,]ރ5z1S(-yג\JGԒ#8~YӫDEKp|~M[!$ߎ_󑟞[ZFf1jP*@GB,)\<˧dܟq1{Y*UJ*/$`@AۇV\sq4^ݛ& ?_e iމzXVu{6o\„~5ɭO7h1|/MGhwۓRHvEJ~0[3eftΜ 'E+KimK.q{$\!fOR-b&Bd{zwDNwKh?}I K)X܊1C*ae @#SfIQ j@.i <5zb>kԬIdLu.ꈯT5Mdƌ#c[v9~|qsʿ'vu`KhȘI愋%E{ n|̬|&Wp6Q J#Mσ+Eji80&s;*3.~*N΁nsMt rvSVC΢E')m!-ڋG0,g B2&q7IMV+l|Sd-b#f]k r5QU&3Z7h<;N])|k5딥 \IQ ;q;{D@k"O87^<Ήg*(>HNq&6C#ʓ ///geohr[d;#[ZL1ࠠ1W~1&C0С9O&&:!]Q\6% =mCT ;أ@^C ]?eƩT ;t88CLiŻ?gY]~J<1. r]v!^KW@ϟXUZ5mjc>j2N-L`oߦ>1/t1A^Rih#&lj>28rC<-F&2lU JӻrYU67cPq UPRH1iv(uq븎.tZl41*E}:n!xj; Fx"VvgHa7 )JIN(ǦK[uFͼ9W)AU3\x Zɩnd8ZwH]QK%ys$.?͟ԇ`IFsS$N?`(Xvw=$)Ӣ/pxO\4%SX}u84>-Pū # "= spz??!˄fkJ\\ks%777(XYʗ@AlߟҗغSǗ6dGY=(x' V ^S*V}Ųhܯ-fҚ뚵x"<=[3O9"Vfj3xdP&883ڐIi&8x7 R#p({C7~H+lЙ*ڳ8F!-_wb^Gl<(p0BWiL0!$H&L@xx9LC*q$0!?8a)C/aeR'3u:@h/Dǹ;h^?)'3-|v2z{LGV=N.UM44[G39WSvuK~SXyCza<5Ug5$EZasX%Q+#+~B`P`Չ_VBT.HЈ Zؒl;NM$e:3\&$$77!fzP PReTCPLX̦y9U/F?70]-̉w\©DBVi=h:;'q=K{{H"!1uA 5&Z B63bVJ`!o3c!VQfOދui,5 {0mǔ4)"1^J*eR (9 # 56&Pom_lhYuk,w y|0pذk~Jjyn4CqZxZt,#fZ~Vu:PBGҭh26ej )Aݛ& ?_e iމYlv֣6MKoCnqU6𾬚0SHι}4d\T 0t&;Ы#Muz[Es1ֵFhh>vvrרSjJe] Q !ԫ8tłjާ6 $p9M/Ҕ+f` \7ЖOg= @Y\OI84un"BuFT.B)ʧډq[b,#J%j֦F>Tr0ooa EмHڲKw-;>zsr|Sht15tDjV$jk:@S/d.\g6{/>5rf\ElƥOّrnD[OR~gѢ趐zuE#i>9!7܁NΜh z;!㌉qMRS6vX>) ni.M Q7nd[p_gN'X۪d75,ta@ PM:ҵ[ZTɅuf>EEpu4FO^/nd(@FZ$*a'NpUW;0JTf٩c 3rbBܹk~#łsD񴌐BݗOqllȑ;9i>Brb>NWv reOr=:'4v PjNㆫhm_\ :0-J≠_'߫ߛ(=sFSqZC>ߑvx+˲#:sg1 v֯9el0` {pLSֱ _-dg>ؖ^/@AY9c[FN~ݜKstD*`]r(/\m1O wH72;C ^)Aޛl8RZil_r uWloQ97J7n%r?ws3Lå'V4onx;t={O}!dvzؒ׿#fT3m7-6%&$~O#wN^?p3m,գãtir8R߾{q (E7O߳׋Ɣ- ħWx(*||9ZfnO˕n2uGufS Aq%qt/Ĉ&y٣6Pd!pR]*RXgڽ`m8 X{n? )R 6C5Pb.\6%p3q +|/-è?~⯞#`ީnMpSk2>f]b͊= [<,QgvTer#3ngOK8<Ꙇ ^5fLC11d4ru !{,*{`M8w(%'f9:^ p0[!G,v罦YUwTWRe4 {{bbKl5XhL4wc`I>+4Ehxsݹse;ޙŧ|sލVXԒ[5L;E6 Jֹ0F6uK.mwL \ Z %CпRl jJ]aws2t7tX89ZG6rJR2Ng?/GtZ7;stC&nç^٠URYj}[:߷X IDATǷI]|?{7QۏVڏqHl^k7*7%يkq?HS\*ʕx5;H9鉢(ϒ//g%3#3ZsCB|vI-: !D6@͚53, ڵkիWIPl߾΃N&B|`ҚBQt:]V 2;}vBFIZFcmmmXGA3Vɓ': !D6t Zm|T /zcee'dF) '444CBd:VG{u"""RVm'{BU޴!BK!BK!BK!BK!BK!BKnڵkDDDs=3$I ٵkh4(Q"CBl͛$YDAjԨa!Demmͮ] RQL"X!BK!BK̑sB!`GWnſz6[;WH BPNHxȑ}D9:P:D PMy$"=ʊv5|75ҭNk*GHKbXǟͧr]JR?9)%8SD1AQ8uXž{ؾq&npTSWYh8|ٳn:ʴ ƴ(% YRwl<51p Wy3(OC 7]'NҌ:zTĸxHu a _L9Eqzfvvδ|bwx9떤{xO"2Ѓ*+ʖlŪiٗYkfqV?KêS ;[\ա璓g:t:.=ze)ũk1' C`R$E49s\yw }sPH'*r+oTݘAfo?)s E*RRoq'[MEf3Ŋ٣?r8Nd᧿xr9NZ8֪"TTm5n|):FT(8+R+7*+^=:Ѩ~c>/ÕHwZOQiliΎ"xsTf4az4RcDWOq%fZsa9J&Qki3E~o>Nv}Hʶ4e?v'(m WkZ%{ICOO9s<@7؁Yz _YTXJBqOq4~0غc^ 3\e׷)o$J8QHB}ʝ Y2hE9Qh_tMr]ʖ)荷7 8bEv|Ԩ\1S~w T,%1 ͇es@%¡\yްm2)Kq8];]%rbllW*5鵓(< #,,p"ƿ~T*S"u sdCkĺCрBĩ\Vķٛ(j\JAȥkbWe4 p}ӏ?:9{~Qqɇ !7& S{'7Tn͸tqr> Ѵrq\]p/ޛ-azbc301%#򻻠 &DV?9(S ; #^h^-׏L@oQr xx#4Ǐ'-2Tbt:k<"#9՗q?xطzQu[Q+_]ZՊbD˩Clj+KiKf_ǟ둥s~OY fh؜?@'Q:K>Vm̔]b}Y5hmq˙C i;2FNbU5W,azaxu\Z@ѣHPaWaڳSFiI+ɬ,]䌺,S+ɸCؿq:Oow16JQk~ھ9'LC%/%g#8ʟPK9% WW7kKW{iIW,S4K .-2zFx*G:0 NMn !/& .^iD!W Vy͕ݿXCL=K~u[-Grd^n<( ޾Ec~5Ձ9ނ<=y!D6EǶ3Ytsgua4v/9*@Ɯc]s%Q(QiPЪzȠΝ;iѢEV!Z@@5kpyN ~]9B!D$QB!D$QB!D$QLeuBXδZ-'O0":qZ ugÃ@vEhhhV#يNCa%QPP!YKB.IB.IB.IB.IB.Ad@"## P"[ɝ;7Z777/xgT*Uա!Dt)_{-xgTZEQ:!ȖJ(} R$ ❅H YPC(L!B7]"K<|!Q& ];#ƍKs8\%_5*G(M*?%QBQ ' 668<01.!(QxB@wz& /pa#x̉AFi9$\o^weM%t! = ⽉ҳs#n".Z*ЄB]+X>&e=qrp$* P\dTrɓMLחy}_y鵘a2Cܣɣ݊X3K*`BnWȑS/@עoatrj9s8 !67ϫIg=V鄽'чrri?ϋ%Zۼ*ϐ-wIH>Ky :Zs*D^8O{&+VXG'{EY̖-[]PSB浥T;Q}{ (G@5P/ϗMt4B4{OQw Z8pƎ+ͭ\f|P'>&mOHW9 ͛YGڳ͡ cO VKFal=f(4YH\.,<S},U\ݜzcG &s*/w@ s0&mJ;2x-N7cS{"=(֭[_Zװagߧ(DfS8vG&GUqǗ0k-W~ϨPUR%sjZ*.j=4ZT1y/G!^!#1ݶ9S tj,4\N|l讇]%7k?LeCXMAGGe(TϬ}X"3s .Lac2bᣤd.f:g[qTer)l9`@QLh:iT3({)9'LM}SI%QBdKBÆ '9nE*>t %曘$ VFzGLL֨t9rftXeOV?K4 PTdä`U*;F]ę\8Zotx.|kML/{{kkJzwyoɓ'qAI;/B7%K~"s;!zWzB!6MI B|`J{ lllk,C6Lb3gu(W])ggg][2(U4uM*_5lB!RQ%CL”)SxK Lz};8q_oRŭ(GC59/J|4O8{MsҬF؊v!"V1!2L:BBB:u#06!'/GZ̻&>S$vX}Cm4hp"R>nX/\8bYڹ)!]؏F33ƃJ7(ei㈅Ś1% KUskXoB|$^IH9i<$$$͞C9 SYˎ>YG&RDOϦѻ_6|-b]U{f2ZiI[G0y rWEA bS]!6g4͙ICD!>z +WM̕+Wȝ;c=lU[;'*O{GA2/q#֢iRYيyGq5oHfc*t'G,1@B<ھQjАr3't1?;˺>+c I \v.4XhUJ2~M᩵ cu bՄԝ#S y7QɇږjQ%EO((AlExj*Gf?Ye似FB0r7J2>?'opƹrU qB\VBt,mUƱ~[5jV)I\wDOs}VJܟV"52q drB^1#GAe >9}+D=~)&*HWըUʳB}@!hCڎLXUGi7=ȗ*e26c3RǪ(,`}Oin\2|GC:JZ3aÊE,W9ʜxtĻ/#(Ru6 dO ;[G;4 }}ꍱwȍj'W 1ky/;'PU+3G@HXh*!uUi]yIDATbyp(U^-`M1Xsx jT9GTQ/qH 1ys,Q3r]_ wjvX1§CfxqG{Sk䟄ku Ӧ1F_w^2JM.|>[V,%޺wFEqqQ!|똱cjQ9ŮT/#0-[ ƳsCvGB]UgLٜzե|n|`TmgD!Zr믿믿Yb9 Ɣ =?"fKٱ]htߘ?˕%wbAU}S7n5<sAzZ&+0Yyw,2>_mSNѬ? c~mӡ/dL<ߌF<}P)cF/buik7Ο+ j_f8qwEZ s;Ɋ{єJfwӞƀ g4rabj n`Xꉉg'5Ec0L-U 1LJ<ۻ6!xgɉJ5]Y9'.bٰycG9Z͜^vaʣs?,S<6n 6nM`b4+ARyРƱLY]*G.ce/ bd[VSRaKl0e\ ȃ(5Z4ِ$ UUceDD(9qcPTs4M9q.[X߳tp-3t^^Oy|[fØ ӠCQKZ ` #GnWJB/4,`Zr86gZKZ|0Œ_ ԏeZ}˯3b%6'6N`[aRV3ڻ/Y)C PYd*[3ZsCxNHШakbv'Oq}%23u89,ߝ;wҢELT!j׮)joN͚53N ~]D&<( srX#SB# ÙO#FZ4h:CN~o'Jx kiWԋeWn7Bl-ӧ0?! j5pIJc2: = iZNGhk+DaI*[cnA98p9)aJzi14w-0 ((<`MƁ'/Ȣ1?p3^OHP0 Bo*-FLM1=] -Alр[Mi;o#Dsxu?l9ye--)Y&r<6Fe)3Xx*B1!C{_ y]C|vҫNq\pp)DN˹͕_Ҹv*C'PcXu;agk:\r`rw y!g⟕?&ix:;ߧ]!Xl_vj˔+0f @ w#IM:V1}=u _?UJQtĥ#WW~Y>$fY{B,Qycloo|;=ɗqY:s9ۍD!33zu]3&?E $3`lɎy|٦jH\BOcӟy|V)az4RcDWOlĔ|Ѩ ܵq%j8"}M?^e(]817Gri1!E/2w\˺c8{"o5Pޯ&>S'5۬|b/!D6g'3&(XR ,ʜͅXOZGi "B'KzJ3g̃`܍Rf@=z@>U*U|1-*|(, +7#JS8Sl>nOQ ߢޘMsWRwhN*@"߸G;`2YhcмTv۝o EMxMj;q<?Lz2W}3Lа84XXբ瞜XOz6Z M^ ,0=8˅(8oǚx}޴ ]'h?g%θxgRX׾uMd͑;D'Ǣ~4\/W܋fKXb8{J9s*.sB41).GoxJƒIBcA?g~1tg*d+ru8JB&az"9'j(I?-sB1z,9 rBD ((\ԛQLR͋19(5g7ÆXدsc#IUG;/`D_gԃ i;2FNbU5W,aza@OB_q"EsW4u9ܸh27t_R?{] ҃KbC1SdZ+K{BaxQGfU`ǁ[nЋj@YNs'BFnE( ]ը\5|lOKX7@eN>a/Ɩߵ03ߘ~=QHA /JޜɁ+?ʥM>4?w35&io%^l%|8νnUڻ`~ (#'Z!zX,Gل28 .`Pa3'D&& X֡g4׃f_ҥ; aB.p*[G8#X4vPH^,;F*Ɋ-_V1̽h.ʰΣTWuv|x*'Q;ҴO[浛E@^h\uNTs{ޔƳ͢Ό1J~ ˖|*rL!D>s gCjǺ|5nB*rZ'>D*#Q OBIa[n\Noc%,N6F-ah%O]`ۂy#hUjd!LѨr<]?ehc;-汅R[TX՘?:0vZFtN/?UH56=-J~Ld([~/q-&nMaBa`zȠ|04Uzlevڹs'-Z0"[ f͚.|ï+g#?J_l\Txtf,J)*IBl.' Wٳn{(JS!*}o%7B!Ӳ}: !$S;tYBd{.DA3Vɓ': !ȶN8V5H~A;ٵkYBd+:VADAd C}@Bd-zB!D$QB!D$QB!D$QB!D$QB!D$QB!D$QB!D9 B$$$$C(ΝV+y9~{VݞN:zV6H z !G(00JEɒ%:ʩSpwwOsNoZVxa}Fn8LNc5k6:8rң o!22U(JVQ)QKw{Y^ۢ.a%QB"I[zP伾7 3T#B7iО^ MD8Fq"Wrضo^J27;oë9=p;yr9Z>(hiJљoN!0 %.GOSzy:l;"/PgQEQ2=QIeI<T*{&CaUt.і1Նa!$ B^ՠ)qq\ЇC=A+}&}\ɂ"z z{m+_}&' q OŸF݊GVZkiJ ן QB|1_$qfkG޺]Þ5[2fϤIȿ1v?o$ B^u?*oR/rO-2ksbs۷)'L Tby OɌbUV.CIZ$`噵\zx-u}QsV_/@Zl(WS\qr'GS*?}w>5o㎭wG\ʹK^Rd:IBnFIxmU-eW^s-ªzg) zB'ѨMJbkMW [Ca>,WL[֞ %ܚ:7_9='ؗc׏)Vd`jY`vJ4w0}~'dm5VwmȐey g1&/"BW]5GH= w.ű[G\}Ep֑{W`%={̞?Ф \E!(;](d8[n&֡o#R+֖XTSrFضעC9ɥ |ռ9Ve ?!Hl3G zݲhCt.&brgQ@R=ќޝɧ{>~׋iЪC w.ɔQI=,xz?r{WpLfB/^wl}ūg䔗޹Xo_yu2c>~NZThH>MHW' ̬e-äO`{OРo_lݞߍewjpo#=tdaLPv0eZ8]r%zwy|/ЪH16>̽su.ɚVKr݉Iۻ=5W0~,_8N J:a>QJs,ܖї}BZ߀DA!ҫt_R4 ?9JeWp?6ΊJ8ۿhĚLػ9dZ!0cG{boz-qByֳϨM>)q1Dxxhy"l~9ebY_6nv;o"q2c| ȘǴX :cePJp Ww ,:6SIg.*IPiZ'cRMxlx~rmx,J6_I L) r HBxgOO狇3ԉT*29 y'@PˇOBh~lAiƠREe+O~c$G$'o.4k[%!bV3l]*Ax0]ң  ]f6遗IQDA! ax?b3T·,CzMeSiAٳX !Z{עTlVFbaÿepD$`ne[fXԂ8jA v^MW"έXIcpFG:膴)kJeG;̮A 0{̾BR CopΉ?xIe\nPr_n,Ҁ YckT$&$?GAq]Z1}EKi<;TL2:bhh˩BdS;wnݺ~$ǃ ]1RRiеoW}Pj5۷of͚i/7v3{F4%>sqL?:"CJօZ0 <iM̱M3Nw L! uatma+O'N;;RYZc =p)Za- 6gӕ_8t'?8X8\^q׹g8 =!Gh++ז {ې>Xfrt6N݇oۻɌB2ԃu1|LG]\iɪGA!>0:^Jz<HVt)bC|yM!x.6_!x Z/흡[z.]V棣(lͬf@]4L뙵,>y ׮]#"""CC"Frs8ϥ'=Eq+bO^kz(FeIBl()tEXtCreation TimeDo 25 Mr 2021 13:05:22 CETP0 IDATxy| n>D8 >꾵:JwJ)i*ժ3nu&r' I$zޯ׼Z3w癙      `,~pζ `Hd6Pֶ0Zm%IY=FEQ01g/\s(]FfR)ŋ+Y>.Ubb"FEAIP՘cia!w .Γg9]X.."$^,Y* &2z]B`eeevU\m7gNQѢ7HQ%WWζH:J ["Eƚe b׌̟pkMAQc BQFS l4dX dY]IJ Z A9,ېᚌ.T4!$LAteQ}*f8܂ dFCA6z5/T5c] m{k 2m(|u~fѦPRcJeanZD..N/J/suu۹E_0~Ç_͠].GZEq R1Ty2iY2n$B>7}~_0[<r,+7:$cK*GU^תG؏%U-$6\{>wP0&Oq0d|s fP)GP3{*j|6k{[3er20 bO";^AcF_!yzM zXPRAzZe2DKk-jXI~85jŝ*4l^?-}L(ۛO^gQ5_r7qJI۽ )bHĮD}42nf2ӂi*WHr<5xӢ4( D{@ 11TfP) oH#@!`\f}cɓ-Omo.^Nj(jNT-^i{v,%cu\G8(ؖ/\e! V1B&!&;r4tpTKvAB"O D/E~h(w5>[|JPգUe-*m6 cQ41A>aƪ=Xv3G/3 78~~ k,c{1pYfnOīO'߻}e\ZwGteS/_v@slqڞOR%R9eUT/-kfIT<Ȧ&9Ka9x2 **4r%~L#fI#9GA Xmcho ~m,ǖY?Pmn h߈p6D;nU5f踴v9c CGt5|eŵ<ϪaΟՖQ{ ^eSh5xƿ[lm ]Vd".ݫ{m|t|I-M5YIHJ$c9_, Z[8q<ȴ'?bԯYq:FK-N]xIY.h#+Ut{ɟ|>H,dKOAx'l`mտp=̠bk#,x, 0V, L).0c72nAA IgDp %-&J[L (nA0Q8<<'!B6pO>!24h %-&J[L (nA0Q"܂`DDp %BBB&,,,"圜j*T(G/BBB$5kvQwɓ'z* epghjԨ {ȲEQXXi9Յf"B-%E?>P9C%zޖ)'fy6{?ڻpoVo}@ Jymc#"v#5n888=9gĜX[!ݣt vK >bcNruVrI{2vr:TD>'@.1Qr"ȑg )!2FUy|0*f9ڷWIfl˜ZtǴ/ '1;!9L̽M}u9辔"y@:'Jݿ r*AY5؏THy亏)?7qOߏtQ4SF NO1@N%LW\<83z3+n:N%Tv>8uTqO焞S~ba񴑴82 Q{uxϒ^:1 ڹڙxcQ!j9&5Iο@L+Ļ2 x4/cF۳EBIг{8$_-GZΨ6UAƟ#—AckI-Efz#O\tl]p*sɁ5I۴舢(lݺ6m7)WjTkH -lC 6\ȱp֪N!urd,굩/Q|mql1PG~'?s.5uzKTV(BBr%,|ѨVU̓_f|W6CjZ3G95ygNI+J)jգu}*O!54_á[QORE)enͱ1V*ާ`4iB6m7-W*8ɾ.r?%(:RNI[Cs,gN_ș`www==・L%,('{%B9bR+"y1qgIٔ?[ӵj~K*zJ; I$E~&USpAqlZT~}ZcV!dJ\STy)5i҄"EPH\vɦzfՊ}6uCs(ZSR?{ bI^aH^9y'b{?3aLM$\K!${F={={p/E> v.\IqsHYi]1raBbU?W.|MPfh2uWڔ&g\£7)^ś(^=2\N+;(gUlqstJNv;IʹCxlN'ֺ}mߘ{ VGP#}l՝qSW2t܋bhtύ>ɩSE93Ya}fѽ ֦K~`UA,݌zhhǬX+7c^, EzLu2/1K Rz)R%K ̀7-H`8ni^ӿ]_~sOCD#y;faLٕFuHv8Q 0؋ՌXhCaOäMX}zdtV ?k7^enll}Hk;Sݹ}Ν4hY~`@є퇾"@PTl۶uf3U/'˅Y/(憶eAb&s{N&jB3>*k5eZIԵ3Ano>ܚ 6C^2yyc%Vogͭ&`1^v#ƙVmeI~QTȲC,_;iZΟ?_^L,\pV#F>>>\r'OrhZ|||rd"7 x ,nA0Q"܂`DDp g˳ٕ+W„W!W\AVEׯ_'$$$G.SNnCx8::k׮Ysg#"nA0Q"܂`DDp ʡpDzyFwsOo]7Y["K`FBmn}"Tj6>@!ƋǕx!t˙[Hؿs LIGA fO{ތ9th/-`dS;N#\<IȢmA6b;/_ԦtἸ], KѨ*|₇O%>wub[Ga/o`tZ)ei2gDH(Huii/HS?b&s< Jm5aYsdy,0}VmƬCp؟>KOk&ɖ"3j4S}ԧL;|?i8ߚ㖱a:+yc^-G/dudkr5'?Hݬ9oaoF$*'(bdbHJyҜMNxLi"^jQLf$dQ:e?lj?'JȲϡ`Vφ-pdm;wҋ*'jP *ypmnkD浱*kϲ3Т+wuV/L +̺J7phj=d{"# ];e1M1|YȲE.ޓ_M5p d8Yҽ8nm9# fʊ})B&_ztyTH|"w{yHTYp/B7BMől[.g}ǸuhcװIC\h6FUԠcxr哾]5=kH3E!LOrytӧ*<\3c/Rs$60;tf9kHs$ENZ"x؉:ָH6-Ꮷ ay?@wq' F0]i.& Ivmo4" >K\6 0i +} ^6$ܯb_I_,l S-/e K,^rGGK 9?gvh2|j-|8^X.r.)i al jE;/ԑvё%<1!0{,6 EON0SsX:gAZo`Rp`EaGs£L^>ɣa`:!Tmj¡yBYf G/owOQX;ŧt64ulXߏ -}xojqԄ,!|MΝ;iӦMnCxǬ[.G}: dnA0Q"܂`DQN *oDVb'N!|>>>k.Ds19C Bff (nA0Q"܂`DDp g˳W(q)"2K C %}Gb_A2E[L[?7nʯ[LɆnIp )1oIt] W[  P~exaNc?,ۍ ǰɣqxʋ%ZR4K4V̢N{'ҮBnq%Lk`W ;2tJC">r|'Ιy]pi"a^5Vw-^k@ە.|m=CfG k~<.S_@  *PU|տZ @¾d6θ+e:Q Ek14cjW՟?1eN%}Ȧy0+= 5VCh97fu· 0\9DkEy)aĬ{-BH +dWL%-&J;!|CBf!|b_!3s1b_m!`DDp %-&J-F.]"&&3`kkKѢEsDL;P|L}ĉ9T}'rn#A,Cgsug7O F~3=$^f;ލͦ=6 dZ5eCXS~wc47Ou\48]2x^ذ2@4;u{cO *7wU6%;oU@ia(/N=,t cE5OFr)y(s3G R? UL*AT">*T J~JQUŧJSݝ%ɔt"ҼzYwŝ:cog+X:5A[JmFJZ# UZB|żgTT,>,-=2fpާ7sWb\-E82pQ ,Zu?3Ó2dUtmctN ~</cr91ЛmpE3\3;3sm|@cffp-zz%*RN!|=P׮f~d9O5@EL̮HWmݑN<4N#ַĐ2OL>R/W[ddaJNSCu³Z 84QP"eY'^df|~:>Q}# -;r4ferN䛬ҋE:/*WL1ƿӺ^,3K8ИihL+ ohrDi/@R9{BڃiW?ڣ;!tu~=vvvڵ+N 1 cpB%-&J[L (nA0Q"܂`DDp %-&J[L (nA0Q"܂`DDp %-&J[L (nA0Q"܂`DDp z =BBB&,,,NprrBRP`.ޏod (ׁ>rdpB!!!HDٲes('OPp4ߟ3n[M7z!lTS<ßpB]M5P - {{dYlצ2oϑew ΢uc_{ k3ݘ.@vK!C23˥=EReY-Z///n߾ٸ]~}a4cݴjJ\I{턊^ϕN>x0y9 QgW0U%zu@J4뻔MLfyzVnD1>(r~XS(0~8=?bD>$5VmsФƞdF&9v~L"qz?'Xd{,Ȋ(.>HHH}#Aj>wGw-VOjyDZd9&~Arj nG>ݹ?c NC1~X4OD?N͸nZ{3өEcH iX 抂w)SW-y PHHLZ?F5JIo`kLr- )׶2}|ܴhQDދ+Sũw1#g5ZsgbY)ؘX4455J QK5,叵 qe4O{9HKfԫ׈|1-p9Q3xMI-(oi̇<|E1`첞HEA率k*Άzʹ{Yslg߽k& j,%~ddMP %tje>ЇA#3ͬF.ˤnr/QԑQߏx8>v[`ލ{O/N3e`y9=)S&jZg-Flj.g.AؘcggFyZsK|W<7lm<Jclؗa'(ͭlt3.kg^kW >}ݸl0g<>vOY>u'65?NYidH{gBVO㱲KKKUӕQX|Vgggt:qqI5cf,DS\D.hP)y'W?Y̑m9~ƊנɡԷV'G|UˑģSwؓ>_TԔKnMFQR֯dase{1p#!z9se(51SS:y݁';/_YįW*}t~pI[Wa2Z=~+>Hhg/U܍P POleV\\ylmBVj)jY!S!cQR2uDakkh$"2*y<Ofk!֯YS~?MkMu(Geʽ\*RL8FYMf-(:G8G:zc7wy /0;sZƐYz^e5LjR۾v`?iKxȿwVLE Q^fsD+hZ>O=p:er-LM'Ph Ey$ց,D?Vԙ)ϬHָY՚;!AJJ kDRͿ5V6DLJSX'y1qqqjtiJƽ)SzoM㞃[:|<տfV\O޾j,5S6Ԙ'* U[g.E%Y)YN~Ge3p]/"~Gq7GO!rTIjm$-<6-ll杨Ǩr6/NPQh_P(t39//k/ a򻥬,+ pæ0u=TA1*HȲ1$Rf7bNs*U H||<.R8[+ܼy3Չ`4 z!upV \vg+ؖ=RZg-q=̒ʛ2󛕬)&|E%p7r9Pf;?!pgmCV<^ZwC6.\+䓽ڬ6zviB8xpk2a`QiZ!5>r Wosx淭ZR&9cڎ=%0`n:7TiUM #~D[X;S(.@ۆIL3yW ע-P!7a܏zIrŰal=ϮCc ܛ7o l޼ OOO<==qss[[[,,NGzt bcs?vJMbn=CtīD5.I ,*`L ,pt󣺧UR@[m':t @uݖnr7,ӏ_C0w(@vi۬6d=aΝ4hY~A^σ|}ZR4j:mx$3>NT*mFݺu|Ҹ=%t++3033C`ii h$!x Qi?ek~ܣQ5a#Gjψ5D n_ơ]sl%q% 6Ao 4e%C0o {3jm/3{{W",/z uIkwey9uNƽ{02KrW{{o5w6rpp@e$Ize[HRet (!yvJA (#­j9<~~~9S"2.\@Mv= nP ZElۗdYfֶ3r̙Udp>9[p2|Z;Z?/nΎs\ΩodﶀΔro\FϖwYF^= DDp %-&J[L (nA0Q"܂`DDeGoyAAAAAAAAA?jΤIENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-edit-contact-private-call.png000066400000000000000000000360041501654372000244010ustar00rootroot00000000000000PNG  IHDRsBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeDo 25 Mr 2021 13:03:30 CET'r IDATxw\OcsŽ{QuoU[Z=ZuW=uVk]w݂LwPA_rɓ'|<       &IzV+uH#&666[[[jKI5B(hzlЅ ӢN7%id&*%'yݻũ1(J'BHZFban.߽woɅKf`g:7ٹ{xD,Yʸ '2 Iz})KKK@XFWep;V666\:GDFZ EQ\\\>)Cbke4] Yֳ(ddN% EQPk4^}FhpFsֳ%BveمϯV˲z 9Gek2hJ.'f2zu❥'FTYJf0-BƹhdZNAqAőfjcCF+ϙ'[Y 89Ƣ.e52MuqԷgi2Ԅ4܂;XeBod?\@HJMٚiݸ0-25>뽎8r\ONX_mLZHL"WۺᬑrJ]7X48BU?ȱc/O.cr)FX5*L޹K>k~"ɦ>˜!~qϦ?ȕH37ǥlcK*ev/-kYc~NBl.E;[(bIHvMŐ-̜nM-ǫlbBܯhmlq-G݄, JRkiaP:!LE&zO1-V[l{O$r8PRԁ6 , mDpg #wۤUli>hEG*4bYM-w=3ֳY}>@©뿖7 Sc(z[<̖9Y{!+\}JR#*'JCjwkpS 4-ꉋsA\9\i3|mų44(50nʹ y=&)v7I5(iA&1L[L1*u쾵(xzO"rFLAB`Dp4 `X5{$9'PA8?$XDrAjzTJӶKJ7 YSjf 1OWPHׇ~?]&AFe(l4hei6b L+Jhㆳ6GX:Rb>sfׅ\4XR,tpbݰ#IH7w:_2vZ$$l{O rhbIwyG"p߳Fnܓ/KJ:T03T |}:`٭Ou^o<$0i^0T."?fC̈́ݡ*ס3U~Ϛ{}6 kδeR(:z2 fir(Y*.'ԣG?,P 7UYam #;[($D󬼑 y j/4r M)+~VcN-VB?7 @E~9JG\ =jxܩ\ .E> VXZ@qXHaٰ `$cmgmB^VfF76Yi2 RJt;<0*k(]ʅQzgUR/S9lαazFbXJ< \*i-z"ĠzmQmmgmbP0 n8\7(*2 %.xTnFTM?šH3!J`iP s\WPWQ W4A=Jh$p+c-ܴ1*0^~V+UnTiRǃĘ72o+8=& *Mɂ8vZ"Ƽbu2˶r%w6QSa+w}ۖ1{~>_Ŷ>O3w 42\3ԭdO(58 pA]sPھX@_l#lmnG63*fri DA:k<p.3ko-޶/aίۆ RkZ* #Fe)y<[jN}?|v@̾5_8Eb]6?c.] bjs+lllRLfαi.}FNK 8~𴒓ۯXyaF DFơMa+'|.?' c}iBTO91g #&A,fjb!|~t"|I0zvK@_ excqÍH@y|݋+{lv+%s 6X4;[!VN%K- CL>_9̜ÑNU^d B6ؒ *O>W$[%\ɧ 6s(ʌ6q$ԋbPq(ʌua</kwKAł`K[j&[1vכxinAA `Dp -&J (܂`Dp -&*d>AfN|Az>Cˉj (܂`Dp -&J (܂`Dp -&J (܂`;ԲիW$$$$"t(P G^$IԨQ#"#N<ɵk(X`-;EFFRzuMV0J9l"[ȴj‰f"-%;("WMGQ{&O{4$,;xõ4Λob=_;u*9Oh9_SNɑF7R8Ld΀yr8[J&"1q}Y1-yh{mO[}C3ܚ1kO7ב/I6ӏ<_?$Rɑ-2V*U)/q)3 / yZ!h =VXA\](\q<$8+ץc/eJʬdM P aކ|`+O ?a7YW{[lQm Vޞ6"0ޜKmz7dnmctCocޣ/'i`JXX`aaKK9o lL9Wl).Ť/_d BOg߾%ٖ& Ly:H( Bߵ)88-[;FjT]U9pH95?GO`Aeg 9~?@)Ki[PCԵYuT®7_ J(NUNU^*V:g>,n͆9+?FD/e GI8Igiw"# "yt$fg[GkKxb`ՎRY95y̭й%uby|EAے6%eD}=27 Õi:OLd-<͗ +2Ub94%˴66վ*ut.h6^}M^^~5nܘB QP\ݟ\{ԺZWkqVuP* GZ@x5+^-}:ls+ Y{PdI\4yI\N6l |}}s}_rrm)>^bBT.5kŅ|Ib@vI :U2Cj9iI 4WHn4%ܚuW=^!OmTJLSSBvVM__8i₡D-S7McZ*.Gj=PiG 8" ($[#uS”qhhod T ؽUڊmznWHг;tVgaǂGzթQ9λ'i>;~7߳%>Oo)DۘŠҶi3:Ŏ3x4ǿ~_?zg=hת-GbxOx}^,k3pLleo(?erhhlߺsWgcEnށtON_w ={P~}d9o JΝ;SNU[.=(UAG3' >3=bZvp&QQ%_L\)-&(CwW,=X(0ۄzdYF$^KR!2z>Gt:ϟ?GoqAe.\N{Y ;pN>>9lY@fILnA0Q"D%[L-fsw½02)ꡧ9l4w91MKo0b Pfuy{.;]51רqy+xˀ|9hsgo>&`-dc5Dpg3VnA0Q"DC_:'sjS7+zJ\l-Ͷ^^jըT*,p)Xz]`^Z^ɟ] bnf/`_ao5>؂4(; ti8l5bަ%}".?M9% 'Эv:K̭_}gv!_X CΌ@st]on[!&h!uÙK&# `큃۱ch W S^,{oCO*UE ~eߍIuŒH!G>ra5ƙ]iͩEm<6h\[OMu,v%ëR,N4gլ[>vn@&Sf gЮT~sGPx3**(?62Wj*5Z(>\\~]?mەʃP%MeMRJ>KLc+vr3iLJ+*Q<sFp,2]1RbP}(֝WޟK*^zK[urLCBC()"-m-]NW8*Zq?OAO2#P]ܗߦL%PkTrI1()5QS/+vV Y('SYѺJ|OCYהU]Jl8OB\{$SP9#AmnUi(wZHP|t#;vaJSFdĺ q8㗱a/,_s?e,d s|ĢP~ݬVla6hw~J{;$F}I[Yt7[U/^9؆Y\ z{BLt \;1,Sfcɥ?s2YAњF#>)RxjV?aѤSՋ%yK_%>-Ǻ?~wL#թ쥂 \ߺҵY-́Jll# 49lWwd!T({0mGK~ĺ4jX~"wF]":eb3%#_ bAxC挪EZ$GIN>2?%.^FSZIrLpBd.73NL(ser7 4͞0x hQ$(X?[dbVF톇D`#\p&'2GЙ[F;o7psCqljuφU3 s|[ӓOX f h6Sp0^j n~vćE-Ici7-j!L{C߶c)$fnI4<౻y ø#ط̲Tӿ*[Z̴р!ܒ4Z$ENLSAS/+С1<-߂͟ #ZCr1rG+oR'OqF>)cEڋ<S{kqNŭ<7!?^rd=?Ax[1v^lO s:Ktf Ӏ~)Y ϴN /8m&<(a4!^[Tkсjga/5jeD +mREJD|j5~GyQ{[~2GOd.I&j1i lWf޹o o7WaVG0n:5E ]y+һ;OuH}q Q[RUCԝ DrYfvc~1[gؿG~A/62-*3e+5wңC Vbȡ9h7_ĵjꨮrT| y|pҾ}oO~r2E~jd~֟V'@z7+K^m8`]DT!<9+e_Yޟw-oO>-);|v4'Tw#4#?E͟yPQ%ϼ<_s1ەVÓ/,#07L~I_ 13o)|P[+WSfybysf|n[ PrwX^=}kg-%w6ˉO BV6 (܂`Dp -&J ([ͮ\BDD-tH"ѕ+WPՔ*U*"#nܸիWs$Epgj׮!ݻ7Gml$BVy#[LnA0Q&2,b´mS&wwN&{G _9&LZ~ؘcll{b\w }ZUIDAT)ޟGFz퐽< 6XW}4ĬHq$k'*S.׬ J S`.և٫7bbSL}L6J4)Ӑo\h8;!%P~^©ciY26~i ,7Oi^|j҉ei>Ʋhw"x7ﯔ.J hWۼ2,X;W1s(T8ZhK'_fѻP9fҼ\A\yUZC}_n5+^N֊Vck,0֭K1W@" M@Sʏ\/[ֳxGwfZdpT޾WY5ގz T}^~^u8t(?}Ri8a8= =ٍqKuV1m[4s[a4c\[-725oYې>:7)BjJ_GlsmtKP0xʙRuR\eV|z %5O9O=yD%|iN G?/Iw^f-4NO,5Typ?48~0F-8M[]Cf) *RPĹ p3Ao닿{P =e[/;Pvflenc\~̽h4)[4zfr;[N\Ǚ ٛ!!_gѰYi2<@-Cn ,]3l=`q;mf-6ov [VIBLu& 4i_)0iSdY$ PP5fPl)2pkm8H{_BӢՂJ 6yyG/U'iʙ/w?+" ,?sMKZܘ@|*'u~fFGpĞ< !(FoG {~ i:+N(^\'V% ؿр0ŋYqokĿ<+SbN|MwSd~~ZʅE]xT?<% A vy!{wg1_cVmB k jkoM7-bE;q 4d@Y~|^TآT!{_4*l#9֑aCz8p%fH.M>K 4sz" C+mgĽ_xԇ̍rȃw:g5T8Tf}Ԛ8|%3b5_N 1U:5؅nqWQDzJ͖?bS'[Jbz#CFOe`$REEF}Ɏ4+ȆK7G?F[I JC֭s;;_~N:^^ +9܂`Dp Q0m9uވF:;!CN8NˑOW^e޽ekcrrA0Q"D%[LnA0Q<]tLcccC…s(GLw6lٲZĉ9}';FY-&*;]C\=(X*-Og'nzt&l6!rZ.=Y:nHob9t'3yͪ0jV>"3{d/W9 }rΛ2QJ-uFR.wm1"s#_y\J$ z񆇤xCʉ• $Jy|%TLW0N*IϺ%Fbhn} S Y熋jm(Wa =O!*͆+錚'o7V$X}fxз ]x:{ar Lgh~:}0gj*ƍ;$ $0<.װMǾеQXҭcrul]#VүNjy#r|Ns\7\>Ev>peS\2FMg,zO94/jf5qcgvHMzy&.*Rv!}>\ow#}sZĜcyg6=4@yJNtsʖuݑtG%}Raې!'3nkx#T54^YxWnNIc5#_Gr2(zy:/>.(g "3ed5<0"wGYWkXO*H"8rUM l͝Vtc! ^1LFcb_Q"mI%l󘧝 rCʝlė&P"ڑ4.`ᯒmpana)֑zÇ"d*\EL]G||}}>f-޾/[X`WfOn@|#W%=3J dz$+ȑ+-tT_aO1\mVIql#¾#Gؿm9m8!OUi51t̷t(Y=psO䣵͘mvvvݻ7NZGrNH1b8!Axω%[LnA0Q"D%[LnA0Q"D%[LnA0Q"D%[LnA0Q"D%[LnA0Q"D%[LnA0Q"DW^%22;NG^ܼ=A\|kPm R4s$n8W"IKSN< @~ bUJo2[1kbm"^' w%wdd$իWGQ^1hRJqtt?}?@q͌+}I Yfq͢s70U@ġ?smޡ%w)Pxؾܛ^]0|cޫ X<Ѩy'[* sѺd~\t6:zQlP79UoN\*PW:7c=*>O[̩pEI@ܙH+4)ffN>MX[QxQbSL5`enM\|33jy}ѥ)u6GI ƮrEǻh-.LAoͣm~bﺞpEA4! w/*4Ɇ1P{ʹ{YrdMy' Ϡ)JOL7?Bɽ<:1|<ȇSN'8*Ƈx .F Π؉ߏm48Q0_OFgL^[LJ2|<)EjZ';-J}Ύ3U2S[\XkCxGpd*u6uFgXouá|U6`޾Dtn-<9́*t*4ǍbJY]g%11w魔X U*XOء^4hCiUbsN)84 Gz Y {{{,,,pP奪O7[F?// V9Tkՙ];ScI"_`K%mt ?ֵ;nBL]+G6BR/k:=A߽s8@TK쫿Mt%ʰ2_ݕ7RP ?,C{^m_G xvQ̕LrN\A{܉MtqBq9/z;;}he*-ȁg?̊KKKj5:5iT8פwm,,,HXxD65x4urz֯]ưZ2lNm:?cڪ\J-RLXBMSx"vNl;1k9:v2.qfi";}yyؾ\lߛ/(֘);bVo81]mEZ˲e{.X'g禊ʟCu@SbgR|)S/C5y `1_{7ubT$ecb'Rfb掋Ν;PxqF#1118dp֭QAAAh4 t¥j 6p\;Ct6Հ !V7,iީ 3^"nޘ;(T(.FqSڬ3^pk}wlɓe/u';F[0RG:mZ\1MA+'8S ndoXւBjBo4ez%C2wtW3z\AZ˄e5KoVv qNg9_:^ 0JX;zdYF׶D* Y.S Cq5!j ,S)G[qys7De.\N8x80 _ZEWdYf3tșMdp9-r > Nϫ7s\?Pm(@E3^F{wYF{nAx%[LnA0Q"D%[LnA0Q"D?IzMAAAAAAAAA2y׹dy5IENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-edit-contact.png000066400000000000000000000417321501654372000220240ustar00rootroot00000000000000PNG  IHDRwDsBIT|dtEXtSoftwaregnome-screenshot> IDATxwx+i!BE{HE HQT"J# \DCnwf;s)7_.2`]]]Ki'TTT:6-)6.ԍV=i1`שUe[YNF#;l Vc9h4-vM;^V5X6Ey֪ȲN؈{tOi )T,k0-!**/=B6;;9)_q5p".ͷ"G/| lJA\zuՄHM{$IwT:~  =*G% d% [ iTI9fKt㪷auc0I0C S )BaDSb(A`I'耛(EDgߚ& Ohvn(ۈ&ƍhxm c99;ODWBg{lɉҏ+y7(?ZYbśR ?Bgn=*ƇXv.>+!lQ圐l8} _J¡[|5\P< O-?bs8hx!ghj族|Z ? է5 nNeoaL]BٟhzƑX_r{ҷ*dmӣ1\⌃kEMz2~!wk-4/cf|lEB'V_$l6O()&|>Dy5Sʒb6O,xJK6Bd3Z7ݻ)da_=V\$uB w' ZF)Ȥe)؃_XAA$DIvJ[Oս '8r=c*}.E)右3(2f#9^Kv#4o;f`$D1~5 'o7%Yd/ՂLb 1d!~i$$kN]Ɋ]W7McܖpRр{hJY%LoRZ,p"Ɵ%\ {gIXmL>YjMqH}gaH1b.ً-K`HȎowqX'N!ɩ|DJ2@ȲDɊv"phfUNS)Ҟ,J! 6#B|gTr#E!$%kn^U 9qM7,Y˄HUI=I1DĦ`y<;:7lɹ KxD V>T(Z'3͖cdlLXB$c+Ud{)S!GZmyLduc-gF*>bIxRt.h򕐹ʩs&js`gԱtG!!e<hXe2vG\A6jBhS?j },b?rT=$Ƈj[ϵ+׉QY#MdG !35ޘNd.c3,N%,V 섒^*v7[RMq0Bi,7.&$L z'CB=cQ=#vZl[>ks6܄ m 1pMLYf3/|6;\]u$%Y@(@cSL_Nr%*͜Dss{-zG+dښJAk/c ?G؊YL|Gla^f/ùp+SD>#td윬z|D28p Mv_ee=GiPlņYl 1m 1M,QeRFuwS3H#cg\$@lL!9Ŋ&,>wv" + 1ɘ!uBhdUPh8ء{+bBb4I1۰)qtХZfɊUH z{{5isI!t֥.<6d"ɔVN@e4-{d@`N$bСa_E@w#uqd!,ف/c089!^Q bTQ!6 D3G#흓3mτN舓=GhM}Q\E%Ȳ;M~OSHi OM(mNس'ŁdYI$IZ"5!I`fU)Έ׵$YBkq{:**Iu,+g| ĪW**yQFJ!]6Z.yC ۷ ~3g?9qb&O^ZLiUl8$ǢQsTϰcvNDڲ_ 9%Gb\Ҷ Ycyr&/(Y`bx:;aiC֞{0p!X1yk>ޝxw5و>q]Q77|*иd^ˎk[!hꔵ*cZFNu /OAŃC񑑤Ī9ɧ{8[}Bwvf]oK>~Ԯ?a^ 7I m(㝳59;Xr3F5U: 9#kQ#X\T*8[߇N,#YF)R_/Q6rnF4k\0+J 鿆x_*k Uj|#cX]>B Ep;:ӎ`t.۵4t{y+WoS98?.n(K8+g脽>9_Ur˺,"P"ԙŮ]Kп-cQZ>N%D6lyƿ2;6av_Q9T9z(⽨-9ίi2 :H%LmI9;AoЮv 1 ڛvs><2 8id4(&ѧ(8V߀%3g{wv\Kpr=:N1o20ao]A!hp-l.ٜIn%Mz~)o2No[A^<#|-=g?|:իѻwr:tՄ[5F^W M#yQ~v]j1wcId6|MfVK@`V䭍ad3uY)X,R D\>O59ݚA+"B®hm׫q1JLDW1ٓ؊Wqa?WT ! iRIbZˠ *BH)93 ,Pny~jGQjF'V.š&Blq0d|zطon߹3/X z|DrPjG#էmC'Ja]^ҏn_>9qVyN1}_ i~9T8J`;R"z{>8GkcsRJnMh/lݛ "-j˫0sc z7Jh{)FR,eBq$1?xxzxwu@|E$,y/–G݋Ə}W=>]y+..4 =6R?Q <&ʌ$:!485CKf6ޓBkmNSX=jw=CzPv7Fb$`V3X/|NnI!s?J~x~߁<3]Ct^߆@qΧ, )&4[i^*늶]|-"Y|~љt{3ǖIzbxPk[Kql8otCv/FkLU.CV?;}dudOɲ( ?(xo{yAt^链cud~oW=FvJoѼ ;  2HuqD<%Zٔ,o uRhKPLηWR[YRi eK;3slfնQ1J^C}ǸKx"\ᴥۖzh !4. (5:'ty-U**yU**yU**yU**y9c6Iu @tT'Eu ,x2pP6Eʥ %&!! >o3T^ <<ˎQ@䆜U**yU**yU**yU**y1{}r<= lPgY9S"Mzg;X3FU2v&p$JQy7t!r0-H8ɪw;PvL [y|>_}' $X@|}rbR9teQn҉e#m;vC*ys63K{!'OߠB|d)%voھ'tx)C{n޶a>1wL6+4ɿa '*M 1,gÓ;gմ[Ei‡%>`$KVQ8m;3~Y8{G Ez~aagɧܟ #,gY縡\ SukS,zG2 9eC/D`x㫣4=\ʄϿ!u8eK?DӪȗ?52;&&tkJbATUH[\Pf-joJ^?[?t罟R#p+B"(ė33>C|6e= ADbZe,wnNc+aѳi=B;]"qોG# X!nI욷g$ Tuգ^z)lOruWեRao`::ۡs@|KxmR >.t8xQ|_6mcQ9$?Nztvn/cp{o4lA7|)l$kΙ9b( CqBK}s%Fٿs cXz)IN{ [-%v?GRc+w} VY0In֬C߀S<-i_?6] ,3ۢ]&">+M$WHGY $LqSh]͇_]H r4\ɘ/E:_j[UJ\!*"H/f,?/rqL݌']^CTxT&1,\kTݼ%'~a%d”17Ga7h٭:Oԟ;iͱmIw~ @$pnϯM;رi1Cnݙ.VdM/n'lLC϶8 7p56 q? >o8>x8py̷VөhT;~y%&ኻG&e53W_'vlƈu1]c2aZʎ́;Y3=ר?z7܍1quٽ9pp<$^`'p6BL4N-GY8%{@ChԉYWbr]lڷ6/dɛR98?nlU IDAT+6FӣhH݈Hn+;-iV5`oo=Z8g߷m*mJQ|vWgJ^lz6[.nFy9ꍛ̊1$dV \~dfzt: ي5 V%k1Іg@r;ؖOY14(ND\ qpv=z>MNB;cjiAQBvNI2.@_N5,)#|}V՟RK-ޟۏ=mFH.ܖ74$RQz=nanZγDIJw$FG8Iqq$ሓ(;h$Mfٕ |8ܲHH 6 %8ptrD$ּjN_gYD NX@İ[s2a$FhRV_;Ş v?F|c;Eqs+S?e-'ox3g֋Majӈg1?yJp[ZUq#%AC.ye|}$ZQWKͳ땝LEKd l͗|O$' $CAȄGyi _rqvn 7&Z^v&niqk $% z* 18Eu$ NK DXH}S>&B]iA Qߒ̘)N'$[ 6n|-5WrkJZq](ڬ|ѧvΞTSƴys/8ǼiYaBD^sݱ,N'l޹Qv&_r&mܞ _vѾSm kV%\xwQŨGPŨGPŘ << */ 9m7 >Py8r .Kg¥WOW7Ry"^X8GT1aJAJAJAJAJAu|MTp*Eu Q]©%3Bu STpuhTp**/(UT/"-e-TT^L^81ܵߜWE%Au c6(BTj>ۮ?>MA2joW[$?+ebok24:4_vĩ g]l'\^7Zv33_&vr`UG빠)3bKbwYk뽿jzo-H8<3G-`u /$zKONg5lj=8}'4h}>m+`9y8MQÇLWn+cZ䛄&L[gk9y[d?\_6uE{m8v.N8wF܉-/*}DuGl(b7DLm{hW-TpvZߜ.fo%y  n F~wTDE17/o:Rn iEE~ľk.~[$k ąQ^JBNB ĒYu߭1"W\h^LGĀ"E H1]k]>5 U=gmK s,3K3㘘XF/5m7Ŋ6>¥,q,0U33{ }Xqkdu߭ʁng.-039WįI[H볊Zou,ϑykvc?[&SE|;LIl2ll$Nr`05 7zn|*xUG?wzAPDw"[kJ ?ޞ]e&͍~Spt!R4aнm]Ӹ K[mKl#p-UЌaû_ kT믿#wD7Znw_!ykcw;v@|.Oְ·fZGz|7rZ$:@ D"^Ĉi#g2+]ıw|לycp߫D:.WY6m%,`TM&qNo>|3\q[ x^pK\e$pY![ E8ϩVqۇ:.,Cw&<]Q|Nɭf%^oۺT*[23WDɟ"l+ؑ1ѣPNǸh鉯YσNړK;'[<#p 2,X_/k.kԕTqwϚ%3ޓBOђkkѸoT3zM^J\ q c`{n"o7÷Lq=#+re#]ǥoLfop.[Urq I>?{. _'{I[xeʕ_㢦$C5-FP wN]E  W0}YICɒ('s81HנpѴy e c^yu=UJLQV.bו{[vp=a{1:jf~z]W> mw`~ ٥*LG.1LÙ2ÙZ13ǎq+\nzj@?dFUC?;ovFEga xQqx{F`-GUxF$n>jδ^~ \N,`Է)4ן yuK8@'_rk|?P5,QjgbS4(CW0qH]\|5MZx_ٙz~S#ۓiRarZʎazMm($򴙹9=녃2sf~[*ϐsfx$q] 6Ze2Ϲ!3~fDF^٫4-?̼/ګY/ꏋN%7.TT^PT1T1T1f%JnP]=Tp*9Eu Q]©d% :LUQ#bTQ#bTQ#bTQ#bTQ#?o8ȟ(M,^Y21y.;F7й7h6scK*lrF uY @?OB[_8u3_ًypo:!DNtO?ΎAQ;F\GK!*VDuyXlc߲[~ιSNϥq`yn ϼSIHԪYZ5*SMBv/BiǪKr{%J\-Ofh~N%H_R2ݻuHPPA}̨mPW~oeb,lѲL3 XLϪ鑓QXiG^P䋛9>ұ 䡯ڛ!Ug^xY a@Y3|% qU4֏Vq%*+Q$PTX3S<|m'F8ekz>@2賮=>Iȁ_R 5dza/d`g|Wo7#h2p:M㋹Yν :HV|*koT***{ਨ3T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1˗IHH$:*yB八3AAYyIɲA>wzEsHLU)/9y.(Q/-:%sR`2$I\v"E|+W#,\yys`ZT G5GU#ish(Ȳ((PӧOkȭ[$ NKrrrZ> V3]5BEj6Nή#F/לQ<~`oOݺur%QQHDA\ 0(ȈH, wcx{K:'^#49cN,WHL 细$2XNI6&²g?)S>>ܽd"2"&S8: &alMnΫ0䃅,U =EPwWfԎLJ+leRէ%McJ깤x}-=|ͱx%=mk#c'k.z$t:ǏY89RLISPL)hVjϟGa4&#r7EڔŌޒƍӦf ?I XvCB>{X!o|:B][@s/:.?d]cQSO @(V ͇O U[a0Dc6+*48C &1! Bttt>-8mY~0#$KGzi!}Z-@( ;dMo|cyk0ro6΢kexk6ǘu l3lѵfښml #iAJ:R SSGٻ#%''# h"..>|DYEPģGneyս)p}+[;~t4_=P%FCҊYi_/򣕳?MW6|9z{7>^ﳚ\/+0G63rd2p {{{<jVPx!8mBd"9guE4j ̧Nۮtٕf=LZm)v euتѸi03V4qD5&|p&㍟ѷBF6K*9c唗JO3#|l9~>ĠbbcaUZT9{%UHcc,`I`_[% zCXx4pSS8uOӶ+fYϭ|2VNNINNWWWB`q6tZ){{{L&Fl\|5o1-s_ g2,ٖB(~1Yٛ1X H F6XքY}4jha߰՚B'c>˧1~eb5k cE_㽹+kwwPKI5DFEʒKIHHAzi%@6?M5 X`Z,uNjfPWߍ{ =РՂX"u(F3'Yҍ'A{F);bT"㮔,+6;;; KIO1Ża4{.ɩ >Sk)ʼڗA|PӔtt:㧲n ~B(hMY|7+$9L&xpKU-iNd \~<%'tFk׮iE9#K2LNG\l,zk׮l!5}5ڷ`Ŋy?ҘqI^1j,1y}rw=%g=b}3# - NrP [jϭ( Jvvhr;d͛Fʔ)fh4-QpڵLӧOjXO7r Ǟ$p;oFY-`I T%0d߱_^eAS^%Q6qCZz<4`'/KXyVٳ\!**&1- `;dldYDϞ=sMŁZ΢oݙѺexF/~S>BKgֿ4\F/-^]21ƴQp޵-0ڣ;0F¿eDc eB6̨ٖ[ae a9D,1(7Fߘ( Z_4L`]{>/=/=f8k3l,+!40UWFIԀ ]$/ؗɮXT714''')** L||<]]]x^\ߟdlM kV𘩸?kihڙGu^#TIDAT:z;+rfɏԹx2}T ?t#\g ջ3o)bdS#O6S]2RSh:v [8ظoI"Nro:mm ic<ϔ*乽3 Ұ-n3F9 kDKkcعR,;JNc}eLv5h$]_gi+Zn:[ƌ6[}}vlnf3fɄ` ::F,NÁiWQS6ظb'#x [9 G;MoGy'3@kJA7o= |t>GA=]JѾl N(w]¾ L6??#2>f[Q,;z}:]:j5Z(Z vq:}sXgffp\LNN7|[VXz2x=]ݩ;_<'Ɂ;L{k+Ch8^9C7V5o75XkY/ AH⍎`jB~;S_anF^Zf&{V쾮 ./:"aFEQx&>g ~-a' N_ .R#J_5Y{?wl`h6a\sBIT|dtEXtSoftwaregnome-screenshot> IDATxuxorТ-]   cm6` :t eCq{%?B2Xٓ9ɛțsNΕxΦ@r_8  -a}ʵ#`ggj<=\iuZ49#AAŢ`6 ;sF$^Ǽ2e fEQ;ZAAEetZ ӧ!wWyM޼헽,k0Mq  kbXh4轍Mg999d5  [g(h44ѡbQDM  V8*# `4`b[ WWTVUUE=Pƙ0*h;!:XY)Έ_-6]-B"p54͗Y (Glps›ɩj|MyFJxu] X^Q(BQ4ž$܀dk &E~ɺ7)F)bd\d?#mɠqN OImn 1L"6UO)7[4-F^:(H>Xwc!)%?*ZUUEAx'̄=!h#IgVW_:7(bzȷ71u x(I?q4VK\88{{`gѢMBd`aq,*,h:pvСDhxf>Jί9V&M%p#4ڎjl=1اTݥ!2ƈѬ"鰵Y%,ԗN㾎_#\}:K B( !|[ڏYPh%fe[xzY 3fŸ:Pg$Zr3cn9BTTL.)넝bqAmV> .V !658%PEMCz8a1-8ĜLc{ ɮB|b rxOS~%Ї9|[]#YŔ>H˶ BIx*D%6mR"a!"0(;GG MpG'fB2(* F#H< qфG0Z@wtAlku!1L8<#T$=q!ft*Gލ­I_ {@H>eE(XFl bQMCU̗W{aK֏f'H6֜;lr7?B2ųfC9J&WU(3ف!G< =̫b;A'aUF&;4H];敛9kf)YBLڑ:{?fۙmɔ3tQ E$wjI={;ض凃qrb05xthb\( jɂ#6*O8m-Y"&u萱봧Obi~r#[ײT8.JEh]\4c.Nj. bç +lR;#ۃ h/ָ.Ax5_$@uNt|QZ8 lyF"C298d UU_z'<<ܷY?n/kP|_ YɽXܲ⓫ y/Yl2(xDaNbǐszd1c[ڥT#e8<-><t8!$F%"\Rc/'CXv_kERSLvzN^Oyi6eEk"0ĈOat#9~L&:b d|,1}-bx^lMH|IzNifμc"K|jQUU dHa8vŌ4|G7/VR7UI4XrS+F\9i}:ilb"i֟. `VGU_%=Y'cg(E|prqDR,Ć>7BPqruBJ6y,fԜɭ<2IJ(PU"_킐H(fjz!Q#C#2 'h_zAH2׻DfAxWp{xl} cfiqILN4ZRsSIKH eR<,ORx^(yd7$&F OvOCl!cj\u/WbsPc= ZY5HAOTT4.̦q:H*قQ&Q*`6'$d1b,yfe)y)~<#x;85Z VF{i9,dp,4Lf'ͥX#2\tĘ#FQ$'d '$GI͂$mS'!$+Pu*Kvvb1`AlzOE쭮˚" Qi ꊬ}0tdӋ*sߢ׹q.6Ir+5J),^K`uv%\ĿMB*Mz -I_؄5 oٓl;0~է)JS_믊j^"I.w-ɯuBtM{)nr  Rk]2V8U<%4KQ0&TK%$K8ES w^4M7gfΉ#l8Pjfl,lAEm.\rq'*D06q7x3.<6r4赚'LG{ Ѧף :'ЧUzMJO-XƝ[w Ur߂c(ƞJ?u|1D:ҳylٷteɏ=S?^;ȄK9%NWƓ\j.ף.0ɷ3Qh$FȁYtk]2^˾0f`*N!%b y%:3sUaH"]/(I)?:Y%bB-6.ĄOiV\q6w.'N+K]~6^ͩQ03..lBUQQ+%Lx`0ޞ8c"6?p҅KcC>&}HP/>nh-=ճ׈ Ej+R~c-Q2!;Yd7Y"숣%;RlvvA@4Lg}B7es,<+emXGd5֮  1QDWY脋(c ̘-*68kU31ĩ2v.3Qqę$4=N6ݭ"c6)HFFFzdƎ@,jpp:1 TLqqD%lgb,-MD09T3F F >В|c p!xT,w|=X5 6v %~!Pht} [%kcǂ0;/ թUU$> D%.JCo5Xpp^ qY>tFa'~OJwbG}X;EcОXHF93[4)5.AQ@9&}tuGksm{BmpЋWAxdYގqS6"8<4 PE9- vzHR&D"ժYY`!HIAQ4MO =Ed6c( m,Zd+ƞlGIl%D\3AI^x$0A! aq|AA0D# B,X^3(  )4A!C aGA C$>  d"A!  H|AA0D# B!AA2  aGA Conξk\yɉ 2~^vG<7"8((C>`89盦rⷺ>Ps]#NжLNf U|B%6:-+^UHLRe;?'*ӐԕǑLnWwGG]HT TQU< oARekߞ4DSf ޮ m!~sGx{4H|>`E\<>$$wL@|W H|A¿G.ߋ _qszT]LG٥ řn)Lˇ7= MsXx>7\2*NPK5q@&nJ%vι@KߐS=Y8t Ŝޱ|SZ1f}_| g_K+S0 CXv-BV7;FoO>}ܛ=}va\ypke-6,ZGnj%[.MF=(TSzOըǀ_;P[m)\3.=M qhj[p|jm.mYqGF]aBiA΍EjD$Gyl$W`~ȟS56:l]|(P9#w=FJpƉkSOslj(#k_}T&m$LMLvHD]=M,6Qټdvu=+} N&l &{ō=H >)ޠJ쥅tͫ]ڗ m +~ݜL(a܉|]Bܼ uc` arLjA8n/< ,#kǶx,ߊ6P{zR 3:_7]jsE9˦NِQ5sN 1i1d>1*xI`K%Km#O8{9:cEpxN?,D.X^8EW7nP(+:/C$<CC_;.Ƨ\=~+2 w ftљ9x4cz6C2sk}јlD \ޗ :@]ͺ=ZdNr/?pz''gn- eת&Lm1OPCO&7¸} 'C آ~3$ɖoy_y\W N|VL:ڂo_Qb9 ]J"R4€Kϻ@>-9?0f*keAk7')3>ŸQm(=H~._2@m>*d$rMIyʗP~QG6V@TvIWTm2v*pbtqnO\=h)V,\=挬¿ju$̢piLǰ`Z&lG:<[JήY.a6 'vt,5 nw*KdSNO VH{:M%Y I bWq m-5 pt_68gg55I>`ܪ\ T$ށL<8ӻf¤u@tkvrc}{]"~Mof\`e'v 97g׿Ԅ3rfzFv3>M9b\94܄\ *]Lo8qJJT9d(m[>Wrs袙e6MK؛jvř mZɰi_orUAQTxQ{B:)^Ɇ*#W3~;rMh߹+] c3DTOK!=H6OA>]$cB%hNHrtj˗s{yzn>* d,EQ#P 3ig i@)-PVo8r&J$$G/@Ak{$% K @<%j'z3f"K,,UKp4_L^ĹOP4I*SLАGԪ$Ʒ_]&y/ }%Cn↣~8+"Z,'_:J{OٷK^yt_֛A5ɚE&*(2ypBUs0>\E<9ɒ5'YRn)яy3 m>}3gpV~$WעkRd/Z?ə3g8{{NjZUƖut gΜl۝uj%V^_3S{.n;˚oTWΓB_W\we\1m9C# τa/j)ZQ!X?aS2ɵ*;kg&,D+H֭(АU{*meᨀE.źR\~RҐ%kf5dSK샓0J|2!I?K7Nq&•Պa80 IDATUkEgÔd}|ޤmgyn)_]gxսqȄG&1_4t \?8;3tWK7IP}FU@W>d4˫1S:# Ņ3f@ѡ$lW-ؕZ#i&eQ"9xBRtZ} x7.Qj~+?Vi 7N`S̍՘գl*^䴥n}~͛MxllyŮ7EF[[+<6:ЪvBdDlNBV 9M7 sXe+O,<>Lnמ$SNM\;q,J>(`֜w|T"M/,aw"#\\pvвٺw=vjݲ-czԲEF=Ӽi6vbhu _K΢Yz$Фj-wƯE9$V%U؟5.$O|M!24{,fϚyd}辗F3/':#IVO0F(K f_sMDXt J(m3Qϟv,gl3w'Lݯ|_|cV'LˑG =qњEf꓾f&{ [8p>P}V[/XLaUl;d%__[]vTn cR\څes.)Ue7ݫ&}IWat*?f1bs:QT;fm[S*$$X޽S\Zyq6\ׯ/ʯf7mp{҃Ȫ 5=#.N Y.H?/lZBpwCV 2~^ nnn($IȲ(H|>P] NP&GdFǬPGt8;sE ,$K|h/ /_KyVl\~ 2~^qjy;5߸IDDsssŅ  d/  H|AA0D# B!AA2  aq|>`7$8(8Ct運3~~ 1:F@!_En^U!H~kmQw('4?Tdd$5jK0^}J0WH|h+Vtz"gf-Z̩u?ߜ.#hĹ~FJ돎,UH|Ah^ ~m=(0mǔH|AAx/MGAJ/0oVJHi^G!OSnL;bJH &3|9 @U~a20L/XI^'O,&OVܽ|̚%kIl`˗οlJAuu{PZYmqB_ ];?8f)L+UŬ$I'f~|GEFWjyV~xboc[24;ҽDȐk.@(u ǒޱBJT4zhÉOnT=j I4&> Auf-l[Yth؍ew4OCVx6cPE./>9V.i>.}v.LUn>}s 6vd4赅jd/]VӱԷK2O^*Mr8l9J!'-,ރcZymHT1Ϲh^e+GaJmπgxr=wpk=] ;#g'&病 sNbls'mި䜓ReJM*VAE{Tͷi5f2cCwpq&-!,hjeOrE^ HfNN_OQW!lYޑwɈ9x&2=ᙰ=3_[72`yݻ|M_oŠ%`?ۋ>ȡycgD㘷2퇌>hQ 9;U\2O7T `DM hzmFP&! 3D3j:eI[ۥd _G ki&cdh8s>JwM*)YY>R'/䟿q6(lИ.ǢJf)AL%WHHLNܩk{o슋O(zi\dȜo7X͘& Ožy>w%[ĞUɦQ80fW^mXO]99}=FiҙY]:Ρ\ K3rfz;ZμW3ٸqaݙw(\>Ѡy9 ]82+=IhF2qyn{>X2B"(_ ˳>zsT̔ƫ%_:wX]-DϏK/ urȀJTX8fߐL%fȹ{(ORXr|a U%?:∕*$v66) ggg܉LtYu{ЦQ=4jA!XЧ 6j('qBxgMjZ2{3*/OWaw>&tZѼdɚ"֋~%߶rx(B#povRxz_ɼ)SQ܇'5SZcAwϳsO ?_`j l|Ry+>7+3eˎ.H.^2%UםAMpyҢ!+g!"6ϥSQډi,O.Jo2MVdt3vdt:~H9/jVRnVgĴlC~=w׍,T*^uf]ޡ_A?4=OJ鼷p뗯vMzbu.5t9ĖhMZmM敖Y ! )&H8g*d'W6s":!>.ke$Jg>&G϶nTj`P)5`yA?}AH Vz:[ ǙKs:@}3yG~ٜ,lJח 7 mI68~^-'ñqmPo&3+$%E]Z12'm6Fώ6;iL[�|nn<2"%K1o=.CbZtVO+i[߶17{1+>2XtsMl_?b1AG=w.zuKPс;K.3Q>ˎ]9MtӠ+=naXM|FȪ7=aj.xY߇S90h´~ sC(NUhֱEi^rv]ɖ_uRf6õCPqpR5E#eju)^}̙{,]7KmR8>B˸d EޠLQdG4{yzn>דJٵ 1X¬#>|{ ] k2wmao)]?wd#}ů?!r5b!JpV Ύ'?mIW 6R}|RzREU5dm15 Xփ+< ,ٜr:UEh0~#+_(u͚brjb(4Ҁ35?k[lij*r**ۗ,{$ᯊوƸ3CdžSL ./MTG1}Y:)VrWr<=ʍ10Z5^eO;OuF;H8eώ+< R Gc땗^y)^%KŶh풆 HRʷX3tBbV$7*W+,"dOjMmWE.źR\S .tius]dD^ '>iO߬s}.L60J!R?g׏oβf78^|$UQ[^5rVZn(ZP  y  s" [1^{`_sh} S!W֡gyhZjLVPsZl5rfS}a;\>壞 k-fxIMk[$dId|)9ˉ?0dC A +U[gm'.E֎ IbdQyZ^EEaD#I+^| +fTUteeq+Vzf_[f_fD2Ѝb 5.ʚhjԒIϘn6oSvq<mECV]` :&g-r9b?!jbBkB~ .r fqMNi<7ѹ`nҩS9զg4ݝt{ڕJbP3`GpHиd#_+DO͝Y2qh, 2Y Ǘ/q5N^ŏ L*_Y<`eFM{lGɗɆ{Gut.eiUmpߝuYл/fvP.F?W 4\]ÄQ,O8ȴ'pm:"}5ʕ??5T2٦-Jp&}XQ8yv;K?(/R|Z}Q#815˓YJƫP!-\|AR)'Vb…8qyʗ/Oǎ!C2>f1Ūja|{mvc5g4]?3"Zs,faԵ <՞,ѿRkg9[+11bό輐X\-`NRY6#ͤp,:'2e/JYjl)ݯ6q&0,zʖRWucHpJsiR\άsB*iI%'Y6u.8xLCmeWeU?@jw^-K$O$3!W# 93&si,Zޛc8ͱ:Dw5\DM*ZI&!)Ĝq7ß*]pHkŘ;aOw|ֈk;иL.\Xqӷ{twD<##i7=E 9xng*ׂR__濌ǚQzV7n5sl@<פ/׉ʭg?FBHd*JśBHH|tǏw gϞ?}Ga=g<8-OyNOKZը. *^lC5]+Q.vl~ B/BThڇ`.è8Ȉo9jOb9KzLC'2Ѧ7H3.o/>@ `;Goa8#%ۍaqbh5Əg޶ 2sA?bhuUk]3 /Q_ "@֯YE ^3&sYٱ}kDn <|(U?DT^mD#֥&ҏv )YfuO`>e;Ξ^7Yۯd-1Iu# 5s?0+҃"v^_z! &2"8<<=;Ax/s!ﱴȲ({iݙ3NK0r1\ȗ3''N.:IS9I(…puqM:2R77nApP~LOyxz삟oExkW$AAspŕܾTN$>  dKA C$>  d"A!  H|AA0DFFٝMPAx?P7e w(?: yPnDiEDL0Q;]w\݁Zk+va"aA *Q@̽瞙79gdܿ Wdk₶('aaiP"##\fV!d+pоVC#I +WjU_ϟ~P- mDFDWZު-KR>=rP6#442PΏ 3n!@ |aG 4{#ٚWX8473nVk9Ȳ#2heM0KؔZHxI#lҐe]y7Zn Fi# J^&-e=% IDATUz|xf5OD3 :pOagש.f6G# &uŘ8Ҍ PԢ.ܹ1/JA6v@!^st/.9!á-kGp) (dF2>u'lϖahǏkPN@IC /w")O>ڋi'%޾!o q!OuOAߦ:5*q<<IJLZy1FPGG,o[!wk0u|>4͡sS`WM9̨mX[nFq3(Ս@\ij~2ځƅ0HG 2eM{4-]~;N[{W aŞkÃ=37{ n=!RK*>f@- j"kmП℥u 4&E^z,,3E;q?4=|*]&ݻ' ugObmdF?2u|3 NV[ϲ#JK<ʗW@?P1Vcvu3$9פ5*Q4e Y}%dzM#G:.>dܼI]I6ۇIZS(d/s11p7g4ڱ%N`ybHR7(KǞKFA4ۿay{]Ƴ}zEG 8VIhd[/ޅ 7ig2.Uzl ˡlZ/jFZ¸U̫ 4 Wh%{Gd \M`_M,;n k=c he]vl, Kbm)naUcv !rql3>ǃ7\}W)\-.%Ư#')_,h0t5/77w[k6biW8O /K01RR\Ώ/SV1&bF゜QTU8KWhD{Pa>i;=ȨU*T1 E=u63a>^ESͳi:K1au,barrVU40&2'6_5ҵ'/Ae/oidw1#9twcEs~5 < єj&03jUC@gPvh 9=Yڹ'gj±.lzt+Wf0ɔƫD5?/ GbIN`IFrC |/nʨci@_pb& ʣW ɓ<*1k0Z5'H3/q~D-1qQ m~blJ g1GHC[ ˝s(VxR1'pK셍/4JT@c5Z>!٫V`Âg)3c;'Ts\6\S-؜ QswGSUZ\twSsSibY[qR^]85oZJ}OKgز fҳ4kakhЉ4(5b>IvHRNA_[}IIXPd4t\e,GqJ910ghD6Z=߭d׋ڟ=b$M~`ȜX1; 7u "3"Pcf3̵5ٵ'[ԧkW5AlY{Jbܜׁ&/<_Jܻ)տ8ɜF36HzX8e܌$(Nb\lݿ]b_4,+9cnHMXtgϞKs]reԩc?ɴ'7@(؟ *0QTN8I:X'̉;{nR)O^ʨ^\Z1y"%<+ 2 7=kCGx^ήSQUK'Sڇ" wF+7P>}y<g/%ĻDDZG7.nC^2J2@}!)(%wWi4E:A R?`Ljd͑0NsL؞5xu븴6QŠ>Grh xPěJ6٘s|RxNW},:ޘgi:u(½Yv,mc8PƷJ}~ؼSd/FƐ"<" 016fa>]]G&D}^4ǀQf\T͍GW ]jIa?{ʞ(̬TFqh:qO:I\aqNtW8Ѭ?SGgqw]>"axWFxʦnUe7kD~eIB!h,3&!8v ѾS\"Cg>ϩ4hxvNW*,hCU ? ANA==Ш5&WYC\B2䭌.~[?n%2|/.u/NI$7J7a`&VՆJ\S5u^~v%÷ou]:oVesACȿs+1foɊ=I`\'kwfwp>N>u-4 jS~¦z)qr|_ Cr2CEٓy^sv8:Kl^$ShyRGbF/(^>~%>KNƹE%GׄG{] 9uySe*Hq?UO؍N} }9st$2fq,Gx'Dz6c~ON *ylk&y `d]Z!a@*&.LP kR%/T@:eû03;V"CW,ت`ѹ*Pji<թ0NMRNL4=Q5y4ac9.+(4 Vw:'n }\_ȃ},DP e^<`S5-h˅/_(3(coCiڢgL șlްjU|s;ÉewPキ9Rm0'*GS0&]@'[ /8dV㗨s[R3s~w{g0%wN\ i=ӼxKҼo?~ 6D҄r:pqI &enF6u#rl&}-t |/Lgٻ[k:L:u3X)QswVMMufEp ㍵vgyn="%j #Zuu2UrSxL9B=1O w+O(eƐ躝= h~@6s|YCU{@; ݣ՘N͢EmDr煬讝#9cI x–.[%Ǩ\n3kCmϷEV dK o#c.;C)2Y@m8 &&(2h4H$> BFLv比 4$4U K LMpqvjU޽Ĩb(~EA%.kx-tqsuIwKr}""" jU9333LLLrz@8>@ !S@ |3G 7p|@ |3G 7xO^p0Qd*Acai..Y@ ƈr($%KjUlgWdkD& 2숌O%2"cd(#Z H+WjU_ϟ~PBCCN@.I 2; a<$;"hd6; ޏ@ D&<K[9o_D8l{r "hd9>99Ѿhx#yߕфrblVuE_9/}\DbvLygY9i*~~ɢöØ{% dǼ &_^{̭bik43-"E ffUcuu)clGoe]sG P tV袈"TטZÓb}翏Rw8 U ۸/Ȳw4'yi=U&uBE[t|4DS3+v`,{+^|:ĭxu`7INo]QDɪAJVqYDUoOcR1jm?I@%ׅ-im;WWLK 2E%\IO^C)l07@k'/|ȢQmIWeзƥ`AA[Yxɿ)u,|f?ȢB'ؑR>ޔ.WM{1sh/zuTF\alP0V_@"wѾ&3rfTlVSTPĂ~ԫP Q @˚pwvʾWr_J9߇%}f՘PDӠtE^ Sp*GIG Ol-oel15;&=?&e{rX P;k24cѩzS ëp/ d*CX"͉Sv~RNn@W/ &&ĿBξԼwn^u0Mܞ魺뗧5xtacͅ4-銵 FV.j<b ПZe`k9}ipd=?ƌ.qڍ-rE^Z%J"FŋW3/=h]\#4[52քVl'8E:v7jiTνu܄^ES9ttw\8 k ipvRӱrwN^,RC .oόM^F4>Wwt- ]}_UbܮqT Ҕ|Q7J5QOg',yzqg(7T> Ǝ jbyɕ{nهEj`M8oPRGN8]}W{Cgc;k-]tRĥ51JPS7ŠrU-(+PĐP<@R㨅k K7.;G9ϡ[mZ"n1wח~m4wp(ٶ .]ٳ\W\Y:u)dM95H:9ҫ(y$ܬzTzL\ŘZ=e+++LL Oq Tׯq#";˓wL@J͓ghp}sMťetNv<=L&/Gز*: MOϒVpY(N8>Lga '#"016faHokg51Q<;}k3;3kM̠[l}VſM1w;:OPϧ&UWsw~ gމJj@ޞ\NeQ"zXcuY?! !c*UlQc$ $oɐ|0HmA?n:d  J+*OO;7PaK@ J0矣 cjP'W\˭"uˣ E9K*,*V!jnDž +qsEh乲8mS}>wt|I?upPlmH1;g;΃P:160(7E yzCݤE`"GܸtKJёN r( @EC\;)1 wsʣT>hYkj->: Vȃ ”(W\ c6nF#ً &کM̓U,R&Y^@0RG71#ˀ[~eTЗd~Tx^`r0JQF?bIҥ$SZZ 7VN'4W?dq-> 8yHZ&Gdz>M<4X20mp.s[96 N=Mbr[Zb `-<9soqa&T`U; Cf[Ι9sUW[2*‘QNϜJ0i(:'SJ\۠%u̩/ҠMULin&1i-z:%.:S}Mz/~wG3)%obDIX7ɦ1^8οG.G\ P(QOʶf2 :.m=( gL1#R1FjE*fGI#Y[e5Mxl16K&D/6o>))ЦWdT15LY]V MM 6q3pw0ZʔTpiP ?-)>K/pxɮY))ì#0Ԅps4^y((aHpTӧм!X(?nJRY֋-5 %lta.mhU(>$?Css8WΗmE)d}$ɁNXI-۸c[s%kdsLSnhאJ!;Z%CquKwgpM '}io SkE6j骇4"FS_Gaߞ'oY@4JҊfWZt(98OG~TwF99@Qh0A'ӭ:Ir],o\;qPr}\CM,$c}ygj 3V*nI{M~G^輩)Sn-I)5mfl#KB@nSzq?[0L3]VR0> 0a8ԣ3"ɍI)py'W ƜezLOHPMiU/n{bFV2× GS:猦&3,[JSmf]F: zZ,ҷ+JY,ޡ`,O_+0z1{& rWܘ:방AQ,Cl}%*ӧ:|@4CM刅t>j] Lp)^+=RQ8qN L`>y)Tso# 6Ja/|{ԬU&c\{X~Uw;t"}`!f12/ ~KUZ/WbVUܘ韡y7_SvL]_س)` vg\oB{SoB2z3{xmwsPXaE qyd٭;AzdO&󌇄MI3)^ĹDc'Bm_l T;wR F$I9*e,|. 33VˣpݏrTGϗ,2f;DؘnQI!Ir<2Yí01' >;"hd v{ ,,,U333LLLpsujU޽Ĩb6Im5uzzZ{"6Af`G8>@  @ |3G 7p|@ |3G 7p|@ |3`EhHHV"d9ժ>] c^ΞCBɥ@V"|ҪͅgP |*$%KjU_ϟd\p~D'E5Z ೢ{xJ*M?6 2"rλٚ|8zD1/H 2pr y!Fv&44OAfU}l |dڿM8> ےVfG,^(⅗99C< vv@me9E8p 2pTE!{uZbW&C5/ϲrT݉Pzgqy% c>[;>ꇫiZ8~ MV+Ad"jU&ˌ7ppƏNF` ׎̳pt2Mdɰ| ;wtFkx+ODI'[՟[1w;7h,k’]Ŧ?M$7V}SqMV8dرёӎ‰m(`~kɦc .yz{l 'Uֳ,[%P(-(_>nYRbw ,CٻOrAbT׶|Q;+R.Q~H:m6GP [عWUؓemp)IqQ,LS pt©d]?CȞF\alP0V_?e^om.lyB9uJPh)(lGۖC98LֳY95uc dP1A/lXQA[!9b=צV~4fu@œsV4Nq(QSWC?Uʖ+{7+cGHY,>ce[bA`Zsw vC}L\":TM.q6ܵ/쏜/ԙ}(OV1W8q12EVi_46` 9R|5I4aS^Sccsl<1D!U9֮4Îo6%0>-k؉1 kÀ}({<b3}-8SsXH0/Ì:Ff4:!Q,Ӌ>-- _ތAW@_rr4Ǜ-!n~[@a/|}NIN>ԥJ/SU(VDlj}YOFڰH1w1ޗP}uf2ߵY'MD9' Hs^}<> 6H2ь N )(XSj*[wMcX g]} n܍i0< _$˯WqQ=7HJS~=.WQ:{྾}$^b"1ԝ.DQCit҂%g1*}?գcl zJ:1$:T u묛2 v|*F2ҥ8{\ʕ+KN3̽]2kAJ\8}m's<*TJ բC}?rLnfNPѨB([' k,@Rzrfѥي%i&bf/>Iǩӯ /=lFQI YK=yF4GOBޠS8r^ V5cQ@ CʎƾnW5+T,??l`މ'߆8.&sv;:L;66#1! a1t|b~t4VT0vn&#C$ˀO9)b,}\_GlW>ߠ~ ϯ\oEq'<>&Ug\B@5_>md9d)I7rZ 8'&V:o}Kd삫PN/Lc2p{;d  7=*kN ^K:qN:Aо ,Xcdub=)ڤ\ĵ焨u LkӴv~etQ:[JsDIO9|&Ɖ1#p;- 0|퓗c~~]KҟkVk*@CMe%粡gʍm^|BTľaT6vXKaT Lh><0=NP]wv҇W]h4S[~?' _aZQǯe9.WAN-Vȃ ”(W\ c6n.L& d:$dpdPaoױ3 rҹ!y\wkhd$)ng݃_7 B?0+e#y"Ktu/kc@( NAz(Y{=R EȫAHU0Ab煃$KKL4g38J}v2 'roOe;ﳡqfm7e.C~<:Uݙ6=\:/K1G8C]VX9l4kfR:-zSύܸ _r=tP:{+cp٩3Yf@Vӓׯ^bzE+&aQw$_ϨFԽ҅v5PCԓ;GIhү-[̤Ӡ\ kvN>u4ϯHVXz-LaQ-,#]) @o.G;Uwx6ASgE?˾xբFu86ϛG7G Oۅݻq2(rx0OB8c )e4fkwIjv/M*yIr>ewl1td}?u*Iфrs#*0K, 6НF!e]]* z7/'vWIVȶUg!`[ЏAZo (iّٯ_6{|deT +ơ]InLm腍2czn׍&18{/ĵ:GcЬv ͅcŎ^=/b%yLJ'Nw_^\x#}Gq0960~LcۺR̷ Vɖզ͟#[9b.Uכ3rorZU!ON,~8I{R[Fk̂ ˼x ٯn:猦&3,[_CR>f]F: zZ,ҷ+JYFӕQ-0fX6KTw21Wز f㣗< I(lSyiΔ3P"H|Q20楰OO9x0M[j5 QVAAGT/:B{PjϮ c͖]OpkO1E8>Y@&S(h4-?Fd||z"2>&FF\8ReZ!I;@Fp LML?^]eh$|$;fffZs0IhHHV"d9昘ժ>{QQIJьlj޺y ==]\]R8>@ ܽw²ZA @ o,z@ G8>@ #A8>@ #A<0r^Ei0!?F@>%A&$,,-112YvɡD»Lڜ?s3i#^p0rVEpY?U s|1+$LLLZ@ ЊȈH*WoKd %JpA|}~%Wt@ E2TjU_K U'V(t1qhtЍډKVY}gI Gii,"_ q2- !̊ت_2;/Cs8(}:}7#:eiy4LP=↓w^i5#_r#|p 2t;dH)oʔn,X>QAum/CE܎}c6՝Ke34 S|jƊ}ڧ jբI!,ٽ>X=x{*֯BBˏm2컌/DT84ׂO/fmQD;. (R"M*vĆ%{c]c챼b%%Ơ5^ }l<_>awvvvrjwzJG IDATdd$Q^|ߟwebc29ܩ>^a܅M:G.P.Z㼴Abw*?[êEQƔ\%>qY '$.g|f'}HB-%wz\F$2izG C,5}vu/ CTsS7~S>8#.28S]1.T^IRƖPڌfBK1/noVM/kύ.sCr-m^iEbد\˕Ӥ]܈%șdwvݧ {Le)*70yU x.ŪҰjΦYD=gV8;ѭ(j}ͤ^ O3h~}^l"ݿ8y151<(ר/> .)_ufWcQj4ǣRi4OmiL vŒl&Zfɳ$5:cݙ(/U'RbT. K ,])׌ BJ7+p 9 OKgu,K"Oh7۪2lu~XE]Ѽ\=\І)U%V1օq010LÎЩ_پN0>lע~8uC-y5K(֮73+c0gG)(@k'~':͎<uw %-5ٹSyu4oy8;k٣W0}C_|[8$k $:_VMoL%/m?w;}Nla:*f ˣraf3IN'@.KKohFak\ ;΁+:,#9e5lBT6p]:R賧8oD^Otꕑ! jMT&֭״:QM]~L8v63湋)r'g̩(OJ6+֟СYRwl~yRV}̑%@f{b\W(7Rw"SY:ŝf_֕_}n m0 ri6$h,ljAL3mG/s_@=09-4uәN gmN^GUL`zRؼÙjOJ+W*I-5Dpw*N[˞#ӾffqiNX %=u #Ŝi2)ZDޕeJ%]QՆ{=X{1O.oX*Zsb gnx$57zćfM[`FRR Ń0:˾Ѣؽ/φ1׈Ŵ?=)1Y6u=֠E54 i4(ӷY4}ෝG0Ro~"950ҋAmӬfDGFu86q>5 wGԋ;X=RJi+R5_<“eʽcfJ]]Bo6W{"vnP6 ޽rpPr+=W:]5h`P[?Eٰo~VYz)S?;FLZC +~Wb s rդ$J* cl(ŭeҒ+` 2ZGM0_:4,]P[;^\Ώ=y>R16LQQ ]kU"#"P0V&#…?ұcTKP[`jl(iNafx^98an%si#XKj`;UVCNVC`˨֊~kBe1{Bd:\YViHQ#&lpj;7ev8H~|9p<$9FϘWScWfdjի{7F$,z5(A2(XY֏2 RBꕴ1'g|x{##x._uM>'_)guazo`T҆Z Wo*7_DgC5T2͆}JzןVVXXQvRzZyhQUcm뚟?\E=Ϣ;hޟVV_(ټp |-P!+AJTU%CnyKD(< KY2dxEM/t YW(*c? cL:'K=I?\rѶml;Z֒FTbi쿽m$-ri# vM{}LT*i-~[ 8`([\ٌez{)BV9Blb4B]{gK Kޞdia h4/~DG"mY߳ C#q΅7Z2#g.glE3:cy8TR-\}x4z.+Z٘#ZQL3f Eu/"N|ko9,T=bfi!ħ:61WU8Ѹ}M v/cӭs`mlM^ƌ:H۹kg,YZbaW: f4^v)ksb9Z$Y߀כbܡ{EɆNtKIQi\|˹4߾}Xsi=eBj)i}d]!2+z-gw_ ѡ< !2Y oX++]^4hݞ[{y <6µ,yvLi\ }R>^u f B~8i#,ҡ-,] 3\~%\ xwdՂf<\Y)v%?##Ov'P1CǷ yfvˏc9`UsSeHBx)-\4e6Gjʳ?+Wp4*3+*!\ݷ%ǰHOVlD#9iI}`X7fz5:|V*Avbᘻr5dlNOڔɓѳ_9ɠ|}4/g lvy?/(BQ)I=J˟,p~Orkƈ%37Z̯*ƦЩa< Mdû;\?Ms7<==%3Q/#xFClTՐzP9pyоf*SLT? %>eyYSsi8f5G?ܙER@^rۙK3',H"xH۹8M P dxGfϚYҔ7> *6b¿Ze̊TMdBo'@{ .whOR.X뢹w \RܙM-Lk_?\^rȅqv2oMLKw@}64ؖNR u\",391W8qi!i0.D>pjr8׭CݨZ#g=| !MiZ"(ߠ>C*>񭋘1MN!(,UcibC}>㣚ͫMX1B>wOtlY"<[` <0:%5cHGޚ[)Z-lq70- 㗕-Jg| weft= nXe <$Zc "m!Aۈm,٬q)Pދ/ ovNHҾUT]j/gӪW91IAGsXæТB_̬M 4So'^h4%ee4{.MH]9B݇b_Xz=Կ5k(vސȚZ-;wljiBL@}xţϴ!q`ГM"DJo>G!2X4鴙0KO*ǒNJ"% |"=ggt5>!Y[[( FjQkktm'O&v% p2*Bd8[XUx{G@X/2+EU8][]]ԕ4-Ŋ8q(.G򕫄U5zwtm'=>TXh+Uqr!)^gt5kxzgtD&O& gt1 |2 ~xF!^MLM!s$B'쟸ga*[{Pۛt3} 0#!.Pu`|aB\L1{8ؒS~TmY8pt$QCMU?!tU)nJþ9~*R1Keu.<=p4M({]1lĿ?_ؖnn:1.Vk[){_, DPWfqsKAIB*QE_#Yݶ;`Y~gt=35e׾_5!JX˵5i쐝wP·&޵B,+]=>qp0Ȓ#g3E-UNönhJzr#ZQ9[?^5CkP$Nq-V?qRu-P7ιU |XpǼ zr jGw|-.x.Lv.Tu5S/ItP:q)&ϭaշ (}Ӈ'$=֟"zK=V9 iG)J4Tv8͌*G~ݏBJitq`&ΗiE?Vu'8`\y_s@cWͮLlQ2A#a[dJB-ƴlװGYDzw0ϡ0z;]FǷ\ 'd)ZcYOth0qpAP^W;nu$LFXE~mW3M\z*(7Y֦6NTr}?7Ӏ;h g,!hɹձ@%|b+.=DZb 0`tSNC\ɏC9"=:_2kVHIHWun94` ϧbnՄm0bO,a|t Y(#OzIDmy8TJysg~<Jn*, P l;_0B"I'YϯÉ*m;{QYA{t>S7ɪ匪:T݌JhU@P1ʗ%=T_Q.맭 (aX7"Y%/ӡC{޻͛7[B۶m^2'3ݞ lK1l Qyiqwro2cRJ$[9ws_ӎoӭXse& V AUPUUPU9gJLGЦ^/I^LLqg~,W /'s48f3}]1-ݞe60O[,H5/=A`Ch/YRΥ0ݨyg58/̶'0*'?C,2VViiR)3LpNyߟYnCvUz iA[K>m1;~!X0!%խƨ*9txnnH ii,?d{H m3Y6busasT0t3]3ĉ"Q fo\d)I;Kxyj?TDܴY ݘܜ*Q?C^y;>LdJF}^~/ےJU}VszD$݇BЄޕOL=Pf-%],GGRN։7EW'LF]jܱU*gtUtH!iBg% (a޿966ՋF?&q41!($CFJ|*Ty3 !^cjEz6_ &+=҃]v`\GĻ%5$^1#z.G5^ɗ|\!^GLEQh4)5iZE:"II--9w,h5ȣE֦ ϝGgte1+|*dtUpz<^YeI#B,#~ҋB!Ļ%B! |BeH#B,C!Bd!"ːG!Y>B!2$B!D!B! |BeH#B,C!Bd!"ːG!Y>B!2$B!D!B! |BeH#B,C!Bd!"ːG!Y>B!2$B!D!B! |BeH#B, + "s@XX8AA]L6{v2ߜp/c>P2np#]iBNB!>1Wh+^*8~PqI3{%c䆯;Jw?4o#=>B!,,4r͜>Kx7%j(r^ctc_ݙm$BfH&_صLc74(BtIhNksٛK^nMi4?)I#"ԤGUgU O?ׯ[yX9|z|Hr;BIlӒ _X~ r>g?8 IDAT|=iȟ@QQ,*`-ڥÑ3MJ/ |BKZ>0q$=t_`ߣj8hP1o9m{.B!G}ЍIlTX+WFÍ+WJ@bccy7}Xޓ|ߨ.#ܦ G\?CɆdA㛚C8ta3N٭2MG\{F>_'3QcK=<(78Gf8K (I/IqD_eMz 8Y,<^Ez|B… Ѯm[8:9`ooÇDEE Q#G`gobhNN#t^ϖ{Ѳ~-joNQsYҿ FĝGb[뱰sxalxwT ۇ"8dʞ%1dkuF( z1C \Xlu΂|}1Bۦl${UAQEFʗhy(Y(NLʤ9OVʗujQΌ\sL>?UTȋ/Ɣ͋haΏ->C!F;ݻw#< ? zBӧ79r8ma.?G25i;|v%}:Ws'^EU¸tp?Wrk7ulgN{).!(^Z1Faz]˦ճԮ*w7ҥNWg_2 F;x.?mt]u)ʻ흙!7W쀑A4qq =IT<||QX"&ҫju9{}C-k[EEQ# ?ˀӥy!܍eK!D$tJ;""s sBCC[ꉌTQ.p1PGX|$ɪ5%CmaNǶ a;#TT#OGe{pꊩ =3i~8.,V] 6-싟iBnSlFOF-#xJsVR- M'+sx:_iP016ȹTsW/sO":'m.r/sUWՑ>;ƮR=N#{$ǎg88xy({V\Jxx%hx~"/̄Q? aeIl#QJ% ;{?Eϱ3FP.~8Ebna+qh}ij|>]8sc/_@w3rdE=r{gݩ NOBꋡ[b4? Tq+WۙoyopI#B-i.ʫOm؀5AAA(Blt#A,Z0*W{7+9K>fXb5M?R$QuġJ3`'aA] }MZU}TXdITAB0&pW;""C09u>+LL bR[O&Lu}uSⱬ&acgTl9 0D>wԽJMD#"]:Q^"""q<~8":d0Æ !*"8?z7n ""YFeh\?';f1T<׵|a*%|l/ g@MQ~6 yB\7YpR)EQP^s>ANO҃9p.7!*&ć`\3r JWDΜ#?>s (ULgԉZAR>JBtz4LѦMk^FPP JJ߾ɕ+'Ӧj1כѦM맥&0׊)rc(m$/͡ oxV/:zxYKJY-y[. jқG}R(G7"qmXzRkQWF4Ax8yBZЬ66th3^~^էIP׻XV!<ސWMz;w|>({}5eZj\m5_r4%WU-YX‘*_0eM Хc.6Lw5`orG!D{CU3rv <=]ñcܘѩnw;w2C^nLVMhԲ;%֩!.IfhAmvv.ģuĵ h,^ft'6٩7T̀y]> ~bkU :ڑD@|;O4+NwI~o*ۃ q."YU\%EU:xd,=;Ɠw||ή [ !jԬtHV۩ZrKޡu?UQuj.6{::Xq32 Jrdhʹo#d4}epGAG=9.¯@+L ->5 z>B!'uߐk< |o/2ԥ_km~WqsvLsUԛ/ |B z:d&iR:Msތ/B4FQ4I#V(X[[4-qF!^kZtm'B4[ZrYjHW1dm*pyO$.Ê;Oӧz!t:mZi{4}׿iM.!qUBCC d z<^o枋7?P27[ 索[%tm'B! yB! |BeH#B,C!Bd!"ːG!Y>B!2$B!D!B! |Be IENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-edit-gps.png000066400000000000000000000546421501654372000211660ustar00rootroot00000000000000PNG  IHDRJ3 sBIT|dtEXtSoftwaregnome-screenshot> IDATxw|T_rGtЖU*"CT@A[F@AEDEe ^*(gw$-ЖھEOW='9=M$wY ѾFCAϳ- I\zs_.| $F jTEӢȷAT,LLlɽOOO!Virݹ,k0-(K+ b&=ÄQ`0<b!>! ;YCy:fk@LZ&'nų$ZgKUD;Ve9DPb3f;<(mHSIhqpA &Ӣ$u! D\\"^+Ռj1Yђ)k 6#:]EQЪ*?"2G1$=n>N\)$wgΰȗ7,Cq2O_t-劍1 'NLjriFf V5e $dXFFckF#I d1[@謬wà˿ShtbXPEQMdT@xڣb)༯$^ rYG/oKDUU@2eGˉV&N(iZi\A]LQ09Q%9*e8VJ2ޞ.#5D]xVRI_'ÎSD3x +bb&#%p`rވD9/q2`N'ٱ~Oc' 4bД +1(u!Lײn4raIØAW.\\I@.AI_c&;93Z̤$nƢ>J$-}XiC]k:nB#h쓮j&!"D+^6h’I1& Ei}&[v(fE V14;e5!Ju–Lbaz}4Z1A+{BW_cRr#^o)ѱfhv*1)$3=n֨'fiVP% g-<.2SILhY`m&%.EAABFƔAIYgzL&Pk\q1~lhF}*"U}EW0O\[5W@@t܊YٖѨV*4^zL;69Nr i P>(| FQo0bc&n_MdwHAt6ު6>ю"Yn|g zHr-vvjcN$LA4QybUc&CMlR˩U1 2J;L&Sܯ.Ło˗֢ R#6X"2 C|(bF6{;$Pn]9@ Nm7q#FBNi陴/e.#ŘyIg/ NLۛQ F(e`kL&Zm х%={r{"`d!5NK#xLxx!sg}FH196 d UU醴dfNHj&onj90cHܫ;`ľ7xB03)&;X)˧$qRvv:49 v<6j2_\CAdfٟ:]_|s KQp,O#De{k,i)X|޹B@S&1nf:6߼)%"Ős_P6o~Ȩoo!4UU;$K$?MĊ?Ri8ef_y"Hr~ķ[d$-XMe \lrwX9\o4Op\ʗf5~U0+ 1hiNs<>>xmt̩ݺP3SQKHRYz^9R*n*ɹ荗9wBCG +t.^E썝r>'i!M)WQ$&"2 kXyn*d,c,'1.ݦ7Oaw e5\$n޳)'t):H֓Ll /j`2H3ض=W8dL%?:׫KlH^ wz>= 6O8 +Xa̅lj[j}pm[5BhN֪ʗMDuJ22qǓD8ԋZHaƜeYIl$O-Y0SJo{p&NaFz;S.tY%&ӂdrB1]pgRRIB~溔B3gw=ut*XWZOt68;s’X'`)_͆]336NwbY 6eZ-6V8IlA縚aƢM%>)&U%NQQ2>0z=irGE5차6Y4]ƽOWqgӵ WjC}a6| T8iX|Hw KR:STtQrw dK|:cj ,-T15k*Y,$N$+hzх%L@Y)g&N.Â#io iN[:@O^gР=L| L-ю9Zֆ4/S%oNCvWt'ܻ_l 8e Rݟ(QQYTbKV9py$ 6ٶ?ݱQ8yuXfމ7:64-2DE&!f2|k3F5q'DVH&M`kd70DJ:] *JU cW%5kм0z%L(Id(;v:@qwo譱u+-V^31ӹq%ܝp0WNV]|b D(БQ<)bhZ,趒$QA" " P," RW/x<)WXA, tY gFow;a(,) 8ߎ\gЅ!6AZ!#ؗqϗ|{YZpB'$ԂҔJ˾\Uo˴hJ7wa¶bՕ 7~f9.tzHx\Yr7z~y kj mՉgC%,OeUb7c/X#䳙;n|>W;4rhY<| mDIY@ZծOeb/<Ϥ̸ޭWh]ڏXd1S"9RN5jA#Yt('hrVK7$CKlOʑt8GrK>iǶK#Dzuר9{dw3hcekO`Γ~gg 6Oݤ,<5N`9ѳCu~0,S"{g!|;W^_"Ɏ63unY˴bѬJ)v3dr6mZ'pf>@a^)}wY'%r ]{&g<~hzIR~eLl\lТ}lji0v2]p?*ћb}ޝ);R~Yys` O,ٛbI6AAIvrIVIOKG F= +wXbm cه5} fPiNt\nr荻 aRnCTҠ-ך;4aNݍC XgB~tc΢!tSzbjl iZZ=>5MꍛEP J\48؞wVJgtwؿ4xg=^{23cUfu>1G'#6>\\ϲ_j d e|y-wY鰲sůj;ͫ+cF C4Hh,׌Φ-WCT7:ˍgRkk؞rz V ﳰ$7|4[Ȱ tsD}9؉|ָ C7LN1Vg*h^cݒ }-ndEhٰg}KxIĀҁ7]M #e8`1XقVp0շfVX_[MG#Iw|lq*o)=;lY/ x4뛧"P)}7ψ7ز_A` ~Ѐ^eSY@3OAZڿմ)_[>A !>b~cAI%1 VXI`JL$[l%@G#xf~_V_·o(0بJHD!L}ZoaK93sjs_#S'dr/(|(_ ~r*|>3gXcD; y7b| @ =6|sd_= V36s:496+2 yj\tGA>5<͒NbymZ4ۅPJtR2d yveԣxwXvA` It/o~m^Qr}:ކYguZtY-f4?eKNͼ $A(ǹPs؅Uxuƒc>ϳi.bɨD8z{&C&Ex.F7e4uykRRPgYS|j\UvIOD;Qql]-q),w߅Kٸ'|ؿGsE5Hf1a*&_#.Sw9jwO $P4t@RBj#?~@G]GJ$T2-)w1*6S6qhlBW B"  }}%'X@Xĥi@"+YXӨ# dp\I X[q*ymŒ%pVG.F=zPۑ) B1AXDAEAXDAEAX} }xbcbuQAxH\p'00H4^@/^Ddר"Cva@%AD@%'%Ӹi ,(AV#=~.R@Ƣ@AVEJ/p_YDWw"\^CUJYKb;g32'N5;E,by*bIgۈ|Oeݑ(E|,761HedN@ ʸ+Zv;HvxQ ޝ,#kP36ػQ :Qo\쬖K|ڮvr;u V|yOA!amc^~C6RPZym*ߞK.Mv|f}qq#JMz0i{XRTERHҏS;aN#fow\VZ8>\ 3y8+IH[S󎁘r|19ͩ\;͏Kf1 R`D܈`+{u@@-Ѯ$,ĘUT>Ϙp*BRt4nNt⮟ųl77vmͰi,)D_>s ~eT׃R,l1sGup@.%ObuaIԨU СL'6|'Ns׻UC K8|@cxN-_scYb ~8{Y kz.Ca>W;4J`<| M!9p{ ׿J*+>Ͼ%eێr dg0uWZ]FTҝI.˓I'@ZnЄv=2oT0btnFKxph,v$X4| AA8dD䳝i.܋kXՎ V98PN-j7hLCYGb'Si،/dc.`Vm)Lh mIN~ IDAT=,ղ*|G{cX4zcVטԱ*>{&W^ǭ^s???y񎢏= s{.qxsZ  yǤ>>s&:mz3vQ Wl޲X!|o7`vxw%a[uKу᪤u[xvF!&9U6R|pτ6 >l$\=MD(nAZ3iW.5 GroMK̛&F^Z|NfQoH݊Z9E] Yg <9? KJd-NTӉ߽BךwV({?od}%6&]ﵣ[=d .ƒ! kiP7g {9NnI"Mk,s'{]<&GRTj t#&2>KF[&9o!=iH8g9/]cIP:t)C B*&ܱo7T}y*UV ;HϺNPV˽NK'Zi뱆Mv0IpI/O ׍tz5-777jp f|3e-ObJ nEyqA(Yǭhެyİp_}ā OQ&N1 D(i" "(gggEA$1"O1YQgg"D(gIXXy$] $QQTgXbp_@RR( <$8::R6(HDA| DA(@AbDA(@AbDA(q#p_/^$99ؘG]AW77 (R>qP/"?P;wD=CH:hJNNigu1A(>zZs"Ht[ O׍.1/ Sx  O!{ȀX|riۏ44~rמXw| | ~5Q_y(V\pv:p-#sepÇc20L >'<@${c)Lzc$шeGiۀ93I7O?<ՙE;~-#nJ( K (Av@׷/}`xH W6˳Mkw ekwb=mbζиr0>Tj= 9[0jǶJh ^TNUX^8{#Y63w+Z#q:9*dY1quLOTok }mKВRk ?we֦֞eJW f5cTNdPT}WsDj@f-xt&v$ .ZR1'Kb!<۪>]GN˅5:VS>cR6=u$q,>vG “%GazH-38SS&?g֌tk-;&7QJ^\[3_݉3[Y4;/!u:pui\4{oԂǵ%r DLZSר_9f4=OmC˪wmqK%ܗan;'k4MZ̋ dj),fRMsHa;hoa放t^/ӚTzy f |eGxsSB+^%}sZmO )g[.[R*;u Z4ts܊2qJ>$J5mAeef} TiC [4h܌ph8c_n@?|%'*_mg 4x2YE/r ۟jq %,,;` AӍ *sno909XV(6-gf}8K@``7#+(InTkٜ~D曹Xi8{` ФL>fFw*L<Cd,vlDU ?oD%\NDZDr+ JyyhjtGEbp+4wSx&"oŢ`'Ͽqȿxj h֘?8~ւIwfPteQb9-kRPjɖx }&eso[Y<} cS#PJ]VL8}&ĭC+U=K- boЀ;2> 3˃ZD-|Ϗd7*F}W~6&&Ҳ4Nf4odwߋqA(QE*6tXYIN vwz4TX$ O,29H((Z%ˑ7MNe0bfq\>;d_/glp/ܙ*wYUTUO׏0BJvſYO-AR#mqr>ގy`RߡdԒaս^fAITl<{^`ۭHU QH^xܽw#rU3B=|_c{-dƱX:PC$W<| #Dֵ2 Iyfu' *kɔt'`m i$&5a`\5ܧ8eEB0 e%8ޏ j Amڿ0עuyPC٦aSzuJvYM귷) ޔ Ж~gf-md}6Gj52h\ ݿpZʄZv%_>A݅| 9o:uЧY >P hKyis3QMm \W~bط[Hg?f y mN/H7 |ߣUpP>Y21V_Rv@% ,Fָ%]"3+zʡLxq)CZ)-)">;-Y=yn |Ǽ_eDdy.m{ijAJ*C{bt02cAdY>v V k= 6i?tͦ:!4l-ߺLc$L4nXry, }t=.6+ja=Vu/G-iy#3E,4Gt|  /Sؼړfmdl$x4fD4,|Ǧ5N]P]Cio)jKԔn'^#t%Zw{T%Ǻq+,~&]m`j֪6m42 t]}hKjVɾJ’13o».YQFA IJLс1͕F*FyH/2s&?zʚC)J*^R7Эd|>;ʜ&In=K ڰgZ`|'2?͛z;ORȗIbtQV8:b_ON|l2Ƅ$L=ToYs 8~E #jV!k|^xg*7At,E$މ.ĩ╶jzZhY,*ǎzժW ;zQCb$(89:m"υHJNG N)R>@Ab]X B"  B"  B" xp_/^$99E"O1W77 (R>qP/"I2kzE!;~0T "Z BiܴE)“ZpHyD [* 𤊍-Rz@j| "OTA0xƛ5c0ዳuYGYχWNUE͵8<&.89h:)^ɔo=a\ܱF:? %hkG_> U.⧽*?aݡH(â$p}>u2ݻ)iۃ8UoUΰmVNG[J@PH8w%*3+Ni6ϲdQ^,b)ɥ@6&}p'-xn<~V䍗,g=?z#ҁae)&t8=!_Nd` >L79U*5X Z =>j!ܮkW?vX7b˓U@cc0cgs$-Ym0{^{5 ߏ//ב~o shR4|5oV 6 hch SNs϶U<+ * Xg{5]1cꑄo/c3۶>e(S-S*.dmoT gw/<Nl\_C~ܺ1}]t~iS~HU8Uӧ,^dW0^;#>!ThFgwy)\mZ2xxW}JI=hVepo@͌݊!x֥uNZj?wSm)Lh mIN=,ղ>/yK\"-X$e(X6#!^8jw\zfnMru'n#^Zq( vXi51jdhY=wRm0* 'Fx:_S[#~ԟ*YX˸|0w5l/jw=~> R]票a5p3Gr&Y+U?銗G28> /GKpz{~~bhZu|a\crC_@вe9|Hխ[}vS;?-X^B*ɦ_l~g L fq]{PU3tPtC!J*%M~ 1yklf0; Lv= Ƴ2wy[:'Uux~;' qԆ M;r{ŬZq^%;n [259:U!'ۦ#0z8XVw{5aAX6!XI[X4i Woe_B~$^e8VP/s&?@؆_c]d=^!ӺC*f#L\t , ]qˢ]59XN2lsN&D p- vY= ذ)=o$q˙3(w¯I|, cO/CȁN%m z$3eץ1r<1qyt '㉱_`2uay\;wftK4kT)8U͒gn<5tX?_  h7%Leo^3= t%ܸ:n;ؼ;u3b;ض z}<6CdXC 'S n#=j_s2c"]X/Y@17sh&uh˳Cfhl=zK{V;R1 vlq͠C~:˅oeO''D *:[6?ڿ 7|YxTݱ,;ozrʫ K0SLfL+2U&ZTuESO8kmvI(7Ww$G|bMy}_6o Mly>wo*ㅓDREvwl1c9uɬ%[2c3<0 VV/ O7W9O\XVn\@ ?a)2ܬfoQu#-8o;NöEp5T XSǮEnn~ NP5[5){vJ)sNrsmm>H߼z3 FY"W {XOfN\Kcy]c 58єjurz̸ٕ3yI{ W{/;qsR^<|)Q?ZvjJpcZ&?Oy[,>!4ڭ2kz?}`ߟč=vp&MʰTt'$v[c_,?-OG!*|쌂98_|:HkjP+m+~4LU.Fm()#;c;a=˧弲a;l; eC=<2vuc%ekV}vWwn6 7I@ ym4/fҼ ?Zu+CG~@zx=BVeų;Γ%E^qk3 N<ܕ=H.4s_[tX3 E0~-zj:=ZLV$ !5[0Ć*V}0/h"|^4K7'3`qr*~6 ?BZT26CèOj1}8g@ejŴ|(/m?]t<} h@h@u-*wf2Së|đT 㨑|22߽r곕rs"ѧv̱#G\27Rfr2뤝?@c 2|27kycXa5튔ɺ'l9’- )i^xO8cfl'aF햿 ]H Ķm,:kDD\.l&00X R(??mJTT,џ,r7n߶b,,v!))Ү\"Vr 1Y""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bĽ+ w;.K*W*KXX{y.vNfCT+[`+3(6XcKT'qqX vU(?m\8""v㙮.oŮpOoʺ; ,r]dZ9\q4l5_C,~-4U*)% 8 g"!!6=P6ao "(8]X])j θy^:׳)@DJs*Eps ;l$%#:-kY>/k=.'M)My<ƎǾ}y~ª}?g|Q׶hW*^Oh~ݷ?yD ;rh&MiӰ\.dE,SR?}UP-ȟ샜8Q?ub#?p9ycA%us2=23ǹ֏&#W͖ʹϙ˧_/ SZT@PH8M?H)Οq`(Vg.׻GT>ގ(v@DJs.o//Z-"!>˲cѢ88r1+!F#9Վ=@GԘ>~l]"իK>}HO 33#GR"W;z O'(8l1f{kxp sXux␽m.]oP ;=yɎ46 F<[J Or $}jW "5?'e7 .ZWIcc;ϥe$f˛4}5)`R?nnM1 Y5D<+>i}jymtppv<S?Z@o[mD;*6C &%$YYY$$$GrR #GrJ܆m066A{+)}ʅ3~#`c']7X˗|=z1w=FY‡oⱾ7sh)>w2iɧ|<^~ݿ'vкa8Na|k|tG& ;bx_UJvn}rvs#0/8N߀UЂU+1z:?L R\YN_Wk> \ ضMVF6eƱc`Bi26U.el%ى|I`a`̝Jnxw3W`8{A^HٜܣdT/xR16(Uq>t'[,+놰BIMK{IyapVÉ(o$;R~]9N;6u5{cE wv:ʼ&:ĭDu6H):uR_#55Ή&=5qc牱cIO ;+ǏIMM;rgUR>'ϻ -lytoCT4{p8uEֵ;ꜵ~/FBDY# ~.lƾ([ T-_Mdg[g&P9)dX<@ u u+8?k b|LLEwRNbXO7rP+EӱN9.tm $ "Bѫt]7I{~Ǜ=zɵ#^bwݘ~ϭ۟;TLk,OjY̨^r[8W裸i$ҷR?fd D4tqx31ԩ];ȈpzIx[*3/Zż~8o'%&ŭ -H;{wؖm0a# pˀ*ufWC{ұSo9*uҷVI;3xRq"U;z,KvV[^!##qddBN'S=!QlC%hL^~0EV_Àd\3| qN؎FW^;e 8Y J/WEĥh\.>[-7:M'~ƚqι4ն9=*峈;6Q0]1\Ƶ-zKXj;(Gع3. 7Oq|eѽdۻ\DTuTP@.s<%%Q.,~h{ꇶޅU )zed)@DJѥI_ҮaUy˖VM=+@DJI`` mcYAra6.S<ٶeYhES|)@DJ۶o%*ˢHjovloێT`ƪ͌.nnܸf73-ԫP%Rz~۽$K*W@r3Wdl=2S<0ebSH(""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""b=NIENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-edit-roamingzone.png000066400000000000000000000754551501654372000227320ustar00rootroot00000000000000PNG  IHDRcr9sBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeSa 13 Mr 2021 14:29:18 CET IDATxw|MϹ#7{O{ĎbPEQU?-jV)RԪR5FkլSֽID%R%ysO~3!B!B!B!B!B!> `EB!@  9 I9:: (WnC^SGkB!aZn<ω-u$(@e˾_ϯWrJ.55Պe|B!B뱱dR?qT8 J9j+^l^^cٌp!OUU1$(nnULGykxzB4xۻcuNijCW8th=W|a"-E+R+.OcyQg;'eZ.]Ӯ@̸!5o"Н|\|pա$!тُR5ZcLMCE K0e,)3I0O`J_pzSZcۦ]pzSRl2w4?rRHI<o. v~Tn pތ轏^_l>m65R͉ z$Rc|t+KI?Γ)cCb1\6٫!.#|R_zKY?H%"`ܲ_רp[89͒㉟7M?SàcTa~<} H♦&]OP<ӺpP 9ơ{J:eLtX"v>D.ɬ*W:`SLj8Iv NcV\|Do%9,%(R:7 c$rs?[u^fdNR8~8 Q1A tzxt$p*{egnɫ/èNl<=&!NK=:lUv #ѵ|iL<$tcYt&ܩ:v{3/U6 j& M}DzT(g{J'-G93x4QE2獹5d@) HBHP.s3 kpU~hb)^mPmvVB(Md37:"-{lvr(cNi@3M͟V^436nr: ASYr#oǥ!Djg}QcLйҺ~ Ӝdz,,::jZ;+uwӀ]w[Utk6g)$o]NQ[s/c %=$ ShfvOE'v4Qj^a\wSa+V'|aIݘ{tr"]Iݡ1̩ٕjFmGM}h1f%-"g܊h-Soߋ|I[mϥh +v3$k*Q!\>+i 7jb@.]$\C\+D I2Ff zաk\ V @`H:ݝ5FPnf,n&¢H[tB~͏%1ݑժTX,} F0[ӖK߿AY'>&[^I~vkbL9ڡzONyrQ7QǵPN}HB! HB! HB! HB! HB! HB! HB!l߇Bv@:BrssV9B/H@B/H@B/H@B/H@B/H@B/H@B/H@B/H@B/H@B/d{gՙ3g#ZB҇"on %wNKmPE aZ4`0l%˔=JNXtJnJv !ģeI̦O/֬xJvwa%YK$q9u3&0ܷ_oD"_#]_ò?4ysg vyyV;1qV6ͯi7xZ rī'̖߮s۾4루!}O*M/c ,58tw¡UWZf֋~#ƏQCAWd˒p+z=!*Jm ſƕ|ݕc:i;;y嫟ذj:}I BN ޗ/+cdK)֠ҿQYcϊn V<(Wu꠶neFVjP(??],oXeSBS{*vќAL:p^y MEU5{4a}+'WYh8j2V}[ @tc5&^-,'yQs4-&>F?cj+W^ei=4l;~jjE>WㅎԤլx7-Q KYlF и ם~w_A4mmNc!Dfyuݿb_AXmwj{f̄%9~5#JJԬ{0z_ *$G /^f9rԐZ|qXHՙo b5s⭨$G_ٌVv )CIz9h |w2%Eqkʙ98{S;r kzW$H_G_CˆQUԣG WAmONϦ++~X4–KcNxg,j\Ǥs[ F(l0**V[,EуG TF؞(̫ Wpl0T fI8_wOX0^m?]nwE*du>>~gʂTsuQ?xX `B*CtBȳU|u?v\X8|bysx@@z[U,ێrٱ$ VMb,`ŤP ؀.jYѢ9x^GQc (X;:|Vu)DE/aB<3toZØﴙz<}DOț~YNr_/lyf:uidx2̻Pt~ktsO+Wnnn+'{=Rk*wK5 FB& HO],92:SIr !i`۔t+_=&!D[Ņ>f!Dq\\\rn{Ξ=˖-[ׄ6777\\\ϕ% =Dn]x!w!;!$!$!$!$!BۻwonT+ϼڵk?&\[sVBe6mz+!ihvߪ>sΝ;w}c܇$@tw4 ay_B HB!< HI6"0tmq-Pm0sWح֓3h΀ѓc*N%f]dxի^;0`ZN%dNRq^s֐Q0wfCQ4]S3vޔk9gSn=m98 &?=5,D{:Wn, &J\6sGӹv <lsp&Q1 EQ~ [*XB2' K{h2bR:1{>xE\39S)o9Hlr !r//Kg /џw˒@`͘rsף eh>ijIE.ȼ6ݕt]K$,֞V3޻&t`nY?%FrGWgS ?ϾS NYݻ:T. x<İv0^i >'65&Hk=>SLćEU9?LePRvui6qգ}!v_΋| \?}5PL䎴%j=9AqI Ծ\\ѡriH>ݕ^}>79nj{#hj~BFsFmڴyMsyAq-A@ h]%}Ƴ@FװU}ؿ>i!5k3Zv- /NYSS8u&Hp&A4{O߭Eʮ,=i͢92-ÆPUWw qN/oJIys_m:[~̈FY={:|KźeUsBqThmj0v0P#nfuL& 2]k>=9yC2o@Qzo&̣OLf5g6V4M|_/VEkTW]ȉ3`LqJ9ժ)64ޔ͡ ܨc*o̾kjٌVy:>Oi)0nӂ/|C(g_v]ż|gf$:ƥ+uQZ Zyk~e\K[v}҆eZX)Z26V &q !xty?T 5ϧ?Yk {% b@u̜)ye $lV.? ])ʕT)R!TوoMDmXk*z?h&>F FwEe)iuϿ9%{5#?Ϝsu87DZ,_W6-*^7BOv@IlLh' 0^Xȯc׋ne4]3akTCfnA|z l`GU0fw~4f+= ̪"ŝkl>K, c%Z/z4̀򾝘*? <$ !YGo:v0WUl}(]Ƅ(wfyt.֙tn4Mo[+g%ߥ4zKV6 bE`wr|q"}ЏƮR;wo~X )Fl(Z͆bɛm(qɳEiɃ¥5|05tzJ[š "Mm*3Xbϳ.scL1:Zz_çބG!ToLgпset*VWт3Z0B~|x4r:ȹelR (Kn8%t*WbN5_GSCv6"O$%%{[ liwѻ[޽{eo!]vQnG~ͭa3B^ B$"_$"_$"_$"_xKx@~LJe?}g-ՍѾvUM!imNYI$S.[KOW]TcV %/P=~ay&7/#c`߹#U$pfW*-.+jRN%#yJ׸nrip٘1U3*Ȏ#YjEF NNH,ţٱ 6V03SXEz;uTpd-v7^Ɏ/ҢX6Gѵ[jV;tAO^߲Q`g=II?N%X$$3'M~CEw/{Ź,TyJЧt3|=n9~ozS/,/˕P"Tpi_\mԯ7W6f 6<yw& IDAT4u7'S Go,Xe*{d(jeX/˻5 mYX Kl;-4qÌm\$>ñ1w[V"nGYFKVBL%FĻ3ɶ_|Jx(*`9poԜj|V'+t?Md8W̤C;/`'f>u_$Uc6o#K7"dIةI!rM}  Vꎞe>΃R>`L~}gNf4_D|8v#-"p+` zygPz}® Q ղs9%SAwW|/qTN|Teڸ IɺˮMl+>ޔle+JЧ⊋ /*&11A{Ol]\5Gmܻ_λZj-)/fvϢ?!2S$_bvjlc YT` !V!'KCD=QWؠTLoRZUiT"_pTvzx^w'ӈGg.gg8-Ee#Xq 1z]ETo؍po?`Ż ~[y؆7ǐ6# }uPZO?TRZoIhO 1{i;!]lt>(bGt t#rer͖xԮKl5G#tvi͉ť) i(>ݪ$N Nyi$';`NnzՊ#ĦpTZ%}.hVeĀ֡Ɵ}H?َO~ΑBey y%oF0/>'#&.mTm$1t!2y wjˑ vBOx'w5މW'p iƜlnOh9ށ3jS)'dPqKB I'y,}w;s|HO{>+"_$"_$"_$"_$"_$"_}Nlɴi߿LG1MU bqXUZ1#\<ךф[QjtuBI[r)AVxƪX3)`g>b׹Q s+gܯkٛ2q45 +.x=LyoWKcI[oޟb/BC//ekT:J}|hԸͻ&=̕s)[F% pc-(ru3u䐹?m"!UȻ}Hmcsex+$z#]R !x򴇔o h*ޛ L7PU+`_/9A´}t |g?𸚞oOy ]>^xoE֛xU<Ɛ\:*yB䥳gOddd။ %JȕPk$v[QȊ3;lR1Λ62͜@,c50|ﷴ}h2HWCA*}{b 水}N/AeXl=뗱x|_ͪ])qkN5ike'6^OBqYEQFzsQd^we$ Dm|ž+'#A_Ҭ`O+X%!gN|@д}:'g2O}Ke9 Q߽ɐii鮝"7dȮCRP!}\jQH\NЗRx&`ΈfAM`8F4uԿs0ӢǙaF!_vÙ:u*`X<7P)4n8>쳇1fV+#G|{( HB =z4G'%%u!I!ٙCzбO$!xJ3OƏ(炭!la*̰h&uVg)r+΢g98 &?v?RN3C!wcѹTHD__ )^u؛5;R@~ͩh۟Z/NJiW1j>~%=19x__Vr&ӭ[1]*]Ю}#];ӅK0kLR8Q<)_=|tVyߧ8G @Q򅝲oD憪(r+PU77\JЧ`,B5|ڢ!c0`rpj4h  lM&a BHEFLK¼%h|=>ͮ(d_X`':Q6+d[<=BY{LAt-'$7Xwz}=W>ǓK+(M...?~rZ=UU9q...R$B< Μ9C\\ѹn777\\\qO!%JzMW!B*'ָ8L ! YWkWŚWܪ^!R#J.`X!OkSK搄B ;yPNY6fU%)86P *sbtK?*;iX-V9v1pr)t|Blu(Fjɯ'b8ƭdQtN ;yeg  ӷ M7"Y+qw~qtce/ct3c"Yɉ䌲6kSR.h2g)VMm~i~(Ui]q3(h("gXw>,.ѯ0ߵ>Su+^N;=jy7$&&sj8swErAU.U<6at-E{n/A> !F$ иmp cܮbu:U~0|1 9j+5L6F zбR1>u8comsqwY~ 0vE(ŴٚHv6nd]T[Kxө?;mF|㣚&jU+4瞂W"|(DpqwyBL(_/%>*d|қ 8) ,^`[;JU2,usᮠl _0*Α-9{ϦjJ 6μ|ӞI԰s8UC38.pfML>|[ }9xG]NILM"g_BTԱ2ښVFՈд_%ֹ#(@,mj ё Kl<жx~'ׅr+7nrRH\}J)3bON9ϤXc ËEoY+عf=w._b(ndR 3mQުэe7qw~/c#p_( { ΜL))^]9+8%r$$VrVvόJ _u0sbJEpB%"RVioyv4BMìYt(E ~$DMn4rͺ-r=agpH"V4twT9y-rj\,a@ëlXw7j5!E1sG̕k,R;e&$q]QŌ^K 5<䑝\!iܞCJޯ _+(( Y{M15 0кUeZ)|q0ĬViڝLo;E5N^ʈUnAA#%KE`k if5l5s5Ot/$(.qb߾{~#sA gÒQ (lq,Z LjbXLMH6`Or.%l9Uxo~,kuFo!0l(Qȋ5K1gq4)פYؾ KApqvIU?U%F1kmA+/V/ʗߣ/3x/ߠTw{n{_^>uOvp߇WŒm•0Ety&==$fF%!%rz<. .B\4T㒸~lDGmMP9wׄ}{H >v\j!,2Vvam-jGkb*1wLWjG1k*ף 싺-[G5+1*hVV:l5UvESӞϸ^YAϕ仟εHg5sB(g.xQ&7- gvbRs!Ŀ +^C՗ 4U%E)QTZ54 N #F$<83*hTUÚ(]V:;Rʩ71~!f[抗rgm:^.oKrH44,\2x:RWE"(ɉ\J|+"~t)Dp)_*!|](hx0:3ɤ<ȗ|׽_/CJn=;e{ʱi(=j<ݜh^6_b&}́JV1(BrnNd0BnQ'@a1Pϝmt}puGiX`]ޔ`KZ#1Y;Q&vq\H7gZxPNfv .;h,Su.MbpboGw v& y=ڕ?AC{xn !\iE3P/UIaCHVMOj^cABۉ"FT z2oPURR-\g0~:vcLh"T]ccR8}*#8ff`)jifscɩ]xUza1[ ɑϳXlc(ěI#,4\譺4]dP\<]*{\;6?0"8cIĆ:W{ѺzؘlqEo-,X\ֲ4Tcq_'{V6T q7酯j%4",De=_#Ul;س[r-A;18{O>?Ť?,toŵ5)u$Uώ -^@mhy})dVY4qk6z$ @6oszISObC=ҖEo˟p,.뛎@7b/E1}Ņ"y MEU5{4a}dzTCܞـZ4}Y["{tQ_u!'X1)+`TTPlwCCQ6SUy+D_%Q ax-DP޼ƫ/R%t0,TߴB\ FP0ٷ}-~PS#j=7] 88oᆪfYD {X'5٣nl{IDMb=Kb5Ƃ *3AA>9;s{c86i[Sa(":L,gϞ~~/rA6q92.sr7۷9ﯦG/JM(և~"mA mIkH6n+X(RJUt^ S8JNy|(T(RU=9QVM㶉wȭxwTYb-]bg7ԗw?.J IDATRyPñ+&]1Rh~*OBYZb_"eVJ6Of}dH՚liB7vv(1w+;5B'<Kמ#CCy+Kg5$;6.,2We/O\s(.݃>ݴLbrvWaGu(L *?pY"fO~.F驯hW|ff#!;$][HZ5~?rgS]osʳ[,flp-{᰹a9op7w7:UJ;s3]KNhg/Pp0-" ǥC;OPӥ@$bد9 ?7u6,7e^oq_E}2!쿨s>>Ek@6{[^1=Ɯt^{wfq& ٳ1Kp]r߷"J)G-Uhfdrtǫh%Yy16Yɝ/_b[(l:?E5WÁbD'Zs쳐˧ 5oemφ䳍]F9'91NƢ.39m wֲ-fw~%*wfժT5}{?eW:N቙e2mW)E{1 M,r7Yg&w 4/k@1F)1>ޒV4!?7S!_%裏RX%!x9rD%BpBdBdBdBdBdBdP> t-2'2,濙vnz$"(=EܰŭhsrcY j̹بP k ̸`yn^+ LXQ֧Lv%&E+yRIs{]ԴL:ɟYvz{ U`ki[2 kFX[Z=>B%・܏2rw8zVclvsh61{0 85[iɬ[kF2EKdXasȃ m6īzۗ3N#x;uƙ=1gtlos|B(Î?xvZʞahodA>EmnytYh:W|T@κtYGes@$[QgnS,CC.~*7`tO8b<>o"kX!X>~Jʼ 鵥Yczӗ㝕;~d_lmQ~U]ssȤ=x{c+9ՔcG{iS8p?c[H99 cO`/! f:tT jM2b)~/b:+J*g{*` {tf.RjUX &dfcYJ.w1lKPnjK9,u, 'ܽ+G=˧ya` S zwx#WhA/[A@q(À{G&ԵW ɟ._ NI Y2Q'6lD*WAi<z )`]?/c~?OYQ*5pp~܇Q\pa?WE|d*uOս5uFEbos>eQYYB媇E#FpK :콪ytu:UmH)=h!ǙԨ3"z_Q5iJЧFL6X+.7mA-\Q%-6ZAUP#󘂟f 2@>=!A<,UknRgl3l,*ҹc~¶ԣ!/e ČI|.N%w[WSأ%SB;G FB% CdC}/:HrSbc [HPoolFs1}no-Η}k 0e&P3/L,.{LķVL%G,eԡW˟u<<ű- |)˛!m{} *̓U$1{Ml*%Du4Y ;8{'fX7[I!JNHh$J>.LX6IgYb[7FgXZ] iJ(AHHH-/B kUǺ҂$!xO$' '~,2229ӏ$!xdrv5$!=iZONp3`7+b3'5#3S&=6.;עjߛfI%S }A~Ծ15 s0Ve=pڑaQB4ϱ}W{++ يPz.'^!&6 2沩JЧMndԞI2N ~4i]kqI=fGh4*.cB`vLJzr:`ӾV<׀ȣLn>[mmc^T`?-&Ma!>z J=3˗NGUZ=Qf"$ gêV|7&1/ޛUzr3yPOuX~r zηEW`MI\ZZ,^=N1aaƁ+ñ0i-Bȡ|yZDBQ}P ^!pw12SVt+B(GoSvKf^5)#.`ſ5Ыe,-Co#k^o ƓlX;mf/@C@OOqIz׻>}1o KC.*Ld㯙":>~#nrչtߏ篬'[݂ڴ-?gGv;),db\S|5:a %Q1Gc|5I I+xH@J/E~gq|?9J_nRvVZuneMFw-eӝDJ?s96|Q?~Ai/gb+$/ćBR2&+T*7uLn8׈MWQ / /,XB StU~6U#h/;q/i?j8fur?ʌV1wpl῱l%q3>sA՜f =uh܎we{I9.uAdM/ĉtMЗd% CYbe^"91socMkT*kJa|q g⦓wU0y38Qvi#opvŕiҞ?maP4I^!xG >QFqaÆ1bĈtѫ>}1T.>aHƒq:Fiࢀ4Tet)g6kMۻM|ݥ>% % ;7{p,j''ʭ}q.7+"9Ob-;7zas_k]٘EudE2V'xg_( ǏO!C0rזC}@ ,s~Ta,z,2#o ư~ pk-1hvGZrgVL75:G\GҴ2-iu }w3vs &?CfέfU=1+bŚQ̢u'"09[Ԣ.d@$Δ.cffƘ1c>p@FӲN !D:K}{ᣏ>+M̙Ø1c43n8tԪUˤ}B|N`$ !{"w;n#H@B#(vɥPUGGǴ)?MJB .\ Ji1ʅ 0L%+9$///^ӧ Ju8::b0J% !{mW?}LH 'Ҥ»1'nX{7~?n`TyW * }NW&fŊgnv,Yqe2**mLNj풠O!@ A_uf-T!n d˴q>[Vm-2"B.ga< I>! y'#y;?(Ɏ}&Aߍ#ˢ5NI|?—z/!w8>}c_N7پV\?f"**?Eg.oC,x>ö;|k#{:Icaòi8,ίLdE_cU}Bd4ٳ3 BZQk@N4U̅ZS%oF4M!O֤8A}c1 @E5jhZ.KkYXRφ%q37('٩6?TeNI7Sb}B1XTVнkKʹ'#^9O ֖})MЧdEx7%v6O8b-LI'xϥ5}אPr%.MP1UNsmx9zr7;m{4,L3ˎ;A:&+/Absޒ*x^/ 6z962ꌢ^Ą$IMW L)_!{Fr>[Nh)9[l׽遡qL# ?IЗ b+6hZb재5!m{d4O%A' "ݻ7w%/#35AlB!H@B!H@B!H@B!H@B!H@B!H$KNsW)ndrY&=yzD7MsDʹN*bݚA <΂^)f%v.^i4M7~߮"3aiv!O! IЗ}q뜾/y؏+V2uGxn '4`r.ά[< 4M~F>$L*c*@b/+ ŷs}ӰyL, P N:= և`Ȣ4^bӤ|2YW!Ҟ$ W|uVۈ"E<)5m2OAޜW7a#tKexg县*8 I*ᤠ3BjƑ[(yzarq5[Hf/F1էEv7#GѦj9By;א(fcLZIwm߂}S Ϗ194"Co j)! fZ5i&1!"]s*c Og_uF?%}hzy츞Dg\0yG޹LK &U}zz 쳕{T5#}zufkcCgf8Aߵ}bfXrQf"5 bi("7А_r4Bt"[A|QKn_yJ/R -b뛘b|@h>y.8uk }GǣW|[{BIЗ}1c-wKy\m߯Nd˕%uJ^=v XWπ˘<Swg5(;{Jc Rz2B[ X@ :&WD& iSw(7f&d:!D:})M1dʛ`X耔kְ}9 0+ dF !(~dMu/kx!͇pJvK0B/%^aahlS}/:HrɊ-6.LX6 ~~3xիu'`bSU)ޒ:tɿK>B Lͅ!}B݋K>!"5$!ƍ7дׄM}'I!ҙ-*T@U kx4MCUոc8|pW6I@B ~@z9@iN_ayP!;AB A$AI bѡ9w1j:CJһÄB]7#"C$"C$"C$"C$"CH琮]Fhh(i !]̙1 xxxv>9?$ ]v EQVZZ/[ui_g/Zf~fIDATgo:4 HTZMKP`/|_>{_I@ /x+NK4$_H!_䓻Bdi4M '_=r ?5y^;|/"36']L$;:w&Ufmﴷ9E^o6syAȑYy INGٱQv]Rq;jSZIkHiY,v_L'MŪrn;yKV_YCfSez{TYK᷿'(kOViJAvdt隠O./>. z}ᯣGDI$.?|Fkxӊ>X6k>}]haHݛ&5C* ;[YU낓^B:pw)GqO:LhOd~%+ҢA}9[4 =.8U#yl9ǘX 9w|/ȍ}[Ƈ.p*_1v9 [D֡i5/ iBNS~2ޚKMM}2bnMiAoCJ{蔃b`姉np3! [ΨiT eqWb>7ΤR4aKMu։ǟiVѵ@v{6-GyۧDhPߣ7& ,rb7kaBx_fާ]Xp9*}]N~atq9WGq+Ab-l߶im8U;}PCr7e}qūiǁfnQDEqeQTc-ѫzqËL4tiUl*sFX'l٣RcF=WS<#wϡ!~tع[o9{%A3׊? ӊՏnG# 44baOG]/Γp?jFso6͠S"~עfpXFK eHpruh( {䖍'5:54rJ>wP<_:E33T_=迵x'kp7nZdFNՋ]V+ص-^F,,̠RNM[ [RaN'Z-$ߙ /M22tɋŲ=˻auqUu8:3UOnCɬzB^if&~2+DT>]{3y gTE.Bfovn[aZ/S\/h_ tЏK{2oAzHG>.cprT8;.D';pR3񟠥 q❘{0:q}G7 c?eLt&;#iw1/ם>1rP[>OfQ- 'Prtdi83j>f\;!3eٖ7c[gdp4kgr^gho-:7f ['\K:Sfя:& \M4m;ŅCήI N0E榓6ƍmĚ3yxNڍ:ӹsI,c03>9N)~*ë%znb[/ 4 l+Qn&TZ3o{BPPS ݳguEUVKKӱ{njժrٛ8::Vi>$D2r\/Wiy\K !H&i!;7R#(NCUUG2/$  .\@A!қ\x2Od{wZ-D'ǿ'w !$ !$ !$ !$ !do!׮]#44]wB̙1 xxxvy{҃ǜN5{t#(&sHBq5EDo*ӧOs3#oU>=(6:z1y"uRjU//|Ǭ#TUkrkYO9Mʖ$D((^y:y#m8% HΉkJȡD{W.KͱΗa_g&(]?7! @v{7B"n!)̅oEFϘi /2u]z|Қc'Q҇#-҄<7F0fdV],=,c5j_ba&C, HB#^w"#ڡG~}F+MV}ϓi ~V'/S~ɫ"| e,5MEV^åm Ji9~<$; |Rӝ 9|;g 5qf^ $o~M4vM8;߷ɜҊ$!;9_JwP+eZ<|-ayOohXW ،~KNa0NݻӅWX}nIHĜzXuv&,/Kryɑ@WW 7_c(퐛{"^,*m=R#kGNz S$!qG38K8ԨD5aXk& #E8ԨfT \EAON3qyNz3mz-58(0נi6; ̈́}ɉlbβh ;EYaѤ"&y~LY%PWф#덾1e]eMF =Si/unT)H@V@衄]usoRzt$ׄa5x5'7ЉrsfF/Ɲpl1MРi{z]Ȋ!4ϧeܸl˜MGVDt?;G6B<:akLٍ^ )XgǞEF(O~7'}2>V_xϾBJFYo>L^ͪᘋ_:o%<)Łۇby=tvzo9w>U4h݋1g OSǴ2umS%r{YQ+5 ;u'{^y퟉p Ws{ԔW拸{?܈c2#hvt~,r]yqvkg߸m0KfVljeciy~_lܞ cνGkoWQK&oJf2d-UwN-:ŝ?кPSq_Vp5:71l܏G1֬GWd/#WdDُǼN3REfn[H+kɤ[x. %y~x.3Ey宯pK\U(h/CQ59n`ى蓧EԩQ Jg(П]ە뫧!&KǬť3nցKS-Sts=@)84-^Imw2ڒJ]WЫ츞l=4iغ^ZEMsTq2uȦi=fƬ / i,) C-)Pn-O_L^ٰEϧ(T{)5.cprT8;.D'^0el!y9zЦ`K֝ߘYCqVx:zÂ2X+f.=hT&KYӿe]֘ kڏ^sem/Y$H$!YG#mŽ4i~ۊe:oLJ͞wN \B*1|dGgHy-Gd1Eޒ?*L=}|MXfя:& \M4m;ŅCήI NzIjZn0ׂnp7? AlRvP:ؔ}Z2g2t?͝Y*ݗң76DbCx\[ﴒ|?O)i$Aܞ={[.GHGК- Oћؾ߿Gu:wVZ_vٌ$UJEL?> 6<(jSP4hӒ!a{wƱЛ>{Nif2OI]Kz=c&m#'<6Ъ!^y|`l*v+zJ-zyGAXt]V[7*f@|Mt4t !Lf Ni2H$2YL.=:KV 鿸~7;--$!>`(o$t:TU1yr&*feL#JU)9Mʗ$Dg0p HUUx"CP(vc@")4TLPUYHY]vBEW^%$$Ĥ5ݪ4 xyyvo~9{AN5{LN|^`3.; HB!ҔIB!H@B!H@B!H@B!H@B!H@B!H@B!H@B!H@B!H@B! IDATxw|TOrg-meFFP"*@QA2PET Ced+KDe=G ("u_f'ޓ$ WJ(֫Pᰮ>>^eMfB!x.+)6.S'OuSt-졺տ[l1c2>Bq4'bnڲMJJ? `kެ@ jtiڝ.BX&\/Yƚ%X,`U5@;]N!w]q\L&l6kHzZ9Se&zyy9;]>!w9Kd2a2^5&-!94<ʚf9!xP;HNN%--+co6)Xm6K5_yc >dEE ByaIjb ?d|i8b!]S1~@5ĒO3eRM|H柱wm8R8KViV=bϝ-|l6RwPLlC\1llHOAcX$3@n=-!rK+=4gJLÉ:.'qħ8q`:Ōg>F-S̽$lq6OB0_SB@5fDJAX`/ sU8}{SKgi0-?8VpӸ@'{ppY8sAty .SKVԷG30tmU{z7Aߞ[ϿJ mP1%{S۷ ݺBKlE L_;6_/]2UX\B1V̚ʽI_e.r͆)viZ}pWĝZG#WrBQIW^jRR`E,NLbE^*21|QH|܏u3O<|TY! 75m&#\PNӏU")IqD"(>=FmN&$w{@9{q%'~2 y'0 ͠U㯌tPߥDD&Y듉Of~8l$AoH(s3M­+Lb~Sf% X>=N :JV_}w?(zy]ww]#~5QЈ49G׏7 WPB hg`S=0NZ.Gj &3oשۛ oשEYGZzꕑEǥ]\D&Wȝ ħ^.\!)`Rc_o+ɹ O?<5ˠ` "DL%?KFYRq L=Dfv7cQ|؎tAB\M|Q_&\:'Z1 (X#@+̡ne^u~9wݪ ׼q-m]2)OX͌x )hN*@\qq()\k:+*.ERJNJSfsdnM.^ɛ̘=D v)qnX/! ^7NE"JaڙNJ={4XE9G[ .֫&&s3liomN3F,[W42 c\7rEAUz5UU8$8Θc\UaO;Gy,:i.̐JAuM%MtGRNt(^hZǗG]őBS5B$r>6 GʠlaƢ#ؠ ;Op$3PHF;D2&Bws Tif{k*bcy)I$뗳2{&ū{F@6+)'㌂O\j%Kxvm, I'9. *(f=,GY ꄼb6/!\_Ƥ0j$_dBVae\UΞ ٜ >P;|H1wMM(cFx,ޘΦInE/ұl5hQ>vt N++fuAos1doMs-mOcĻlXHJr`ݨ xXOb.t[DsF)h 7+nY=35SXc7 $9urV|w0qL_cHs9I=DZ}w ,CycgFa;,&pxD;$_o8y]az^COvH<Ǿ86S< LJjhSƑSͅH?qWB] 18|wXm"ddRҬI} 4I9q>6HOI#9͉ӥ+ br7NbIU|<3NkNSIujLVmYZח[F=SIp-=T 3^37ݑJL|:. EJJ.QUL&nnM,C2ĩ(&VJ.RRIIp:LXv %ynԷGA| Plx[0Wl@DŽu2F1s}5 bef]Xmۮ)ovbww#Eȸ,eRl_׳T=~fza2l!nMQV7/"'yK: !]+9. !\JBk(1Qg!"פBC$@B"" !H!0DD!! B! BaBC$@B@=G9|6!]ZxeB|xA\ GIMtVt ܁X!PW;NnxA@M`l&=w8Bq׸K0{C{`΁<~tKC!i'&@\EEB`d@B#r;$n1j#O&mgYrԙ'S,7!;!, |W/JZr?5Ek{5StI\H܈Rxl,fׇ5V⵻2b٫Y]'Sd8!h';SfMj?ԄOi/S.}IOJbǧPU:Yi62[-a ң{B~D iÄ}_*lO+l_8Aq0d?>ZĪœUB=d'{W>r? KT!Lێ"Unj*D֩GB*T3T z>6A9ys'3 >K}7L8㗱mLcZ䲏c}_ [_]Ct00.KL=n4vg#r6Mj6{m9鋣RqsUa}}Oy߱32';i= 9.SU+2wK5 X|y*>ߢ1U@֗e-$uu>Oϕ Fi\w!_D1V^oa6Ep.)G2l8ƙDn)8RCIQ T8N'V2jxR !m^XJ>Z7ZtwHy| ELθC9~ÉYXd'NP@1[qC]ssq e=ЊGejǬhcܞj~f%E,G`ۑԿf}rLv3p`ݘ"D&݇voU.3\hyFG: ! nx폞eK1L38fؾ_I.Ҟ{5p$R]I܊Ȳ`o qm¢%Fي^*I:4->.q5_#q ݮ*wJ I0Jsgڋt}?$ !+yr7^jauPyEK:kϬF!9rJrD'^̗yk$mG`3&hTM=Ǝ[J!1/#&J)l otjrt})`ےsN/á2'vn^nlY`nh'"L\^9.V_j:z/sJ[.ݨhzt~̨&*p)LI mϧKT XʹdҴҚι E_ nJ u7g.?y%UZgb[c|$,OQvx#2l#uf7(a5m/w ux{b^@VsY xb 繅6>秓P<)T1͋ߓ@b[ E%Ƕn0N'WEAu q9~>}p#ºŜ*ޑ*[!xHmR`i":vH!ĽEiiow+!MF>5/T f5s19 !fZxP{`Z eB|x^^j^x[! aYz7Bsws sL|/jA w !y9""oI!0DD!! B! BaHl߱vC!]Fj96 z8!Y҉֞#kBdﶜQ}UuRSщ[:;M#ѻ,X#^*C幯Ng쬵|DCϊ۳Y*N}6y:ƶyu=5%gx8fukDdLSOȖ -,3aҩT >ٍIG2b SW6ytzĻ]`EX+BqJi DcDZ28~$ ~IB zc2VAK1pl_ݑq;kahTStNhצUӆXUS|,FnHDkRg? k}ͶNV`%^2}+6Wv \!oUBAvH!6;nO]GsC9i i;x`s$ʚ(L)/U 6M=n]U{Hk^~MvEc\'ٽ' V2XjQ@CunNB`1öˢ0:ޝBl\L' |(> eo:y9n0 OFaz1^^l͛}X4Li.1", IDATśٵw/{ !KFG+lDBnUX*)? ~-&㒁TgujNVO2pZ)[+֒EU-}Ҥm_>?- e:ڛ6:2p.32[.7mgXsLلU3ߐQb~ Kѽ0GɼSrqBdDW;vgwMЗ;xa6()υv[rmÏ3V"A8Nowf]imIx!Y/LL*f"TjKƼFi!+bk;]!9v]!ƟO8xKvoF?9y/[-"?d#,^5?At!˿ D>ꆭDNJ7 !x9!Ic)CW1Mn\IbgG~Dҡ-X=C(z Krb ! zԷiSfW+Xi/ lJ˖#ڡ1wsa`匛*uxvLoڹ)GtKN!3!nc?G?ż|$Q&4HzUvTwrjQo2E{*yåiV,}gNBqH*{w@/טWnn-݈)yi|+/~΅J# yC-" CHMA$wm>IYJtjY㟼|G1\-b[bJRwIy!xSJ8ąz6Sʕ PND42ϝ~ P K@i!oI ı2]5iK<\9_ǚѲD8FP= U14g^Ooϛ@[}1}[բZ4ūDJ0 qB^` y0B!r}BVn(%=хB"" !H!0? <3s,9k%9׈B[Ic޲fdloG-^Ë~KB_H k# bZmyJu,q !ȑŅLj E:L$^>/7EҎ (Z e,h(~B!r&Oa)TY5d˔K_޹Sxuc BA,=nؚB<ƶ3Yb~w5L; ?ß,b.e\C[qy,$KG4\mdDO3H6S& 3~d\#ѹE"~H#<]Bq_˓qKaˠkh ?79RqsUa}}Oy߱3h.boZhB}{SU+J0t/h\:C?`ҷɀlo=/zyr\}}{ٱi9VȠƴw=Lpԋ牾|&,dtn2 N.BK@"ʔ%L(Ozu*HY_krIA,E#|pmGR#w+(>"nO?-x'2`\/Rvay*V))O:B\ȣJc-sw v}ؑh~`Ŝʹ~!Bn9E'~GT۞ݴ?ž?c3y؇(?Ym;K/Fe,OTCtwPWK9syS}o~ymڭ&oQ4jr?Z5v`{9+Nv^9%&J9ttt1-}~B!JŜ*ޑ*Kx! ԭ,Xr(/gu퍘|"NB!-}-! !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0Ĝ֬\~;!Dt!B{BC$@B"" !H!0$ח;~D.DGߎqGˇEo:q?iV.=v8RJ\P_v:nI[@Mu:"?Tcoq?UN… #_.\gRf?;>K6"q?Rn9quVK[m?R}*a @t?# :4+u`t%My9d ?’ ql9&L.*$ک]Foum6 ~Rx49s3hZPI܆:~}vVCw^KrE o Zƹ@ޑڥ7asd:h">*R}3#!Dx|.r|8E===H9{ΑvwSwrNeV~6g aʔm$_5Fآ<~B1[Hг$-H*E \ >AOuΐe%<={,uOXA~>xS!|{"xEvL{ $.ttSOwQw/7A)J՚թYwϴqotkvK@ 鋟7>l XuzQL&Eke6.W~:qqg+C]c7[Z"^;Ʊn >׉[ᱞX:)w&<45]Sqq|+YB'd]=gsˮ899ho%g/7فjAp|z(c1a?ؽ.睯M9qg v~Ijֻ6 ʊAqS;=۾/}>9E9ŖYcId ߽JV`.?Υu\]65{vjS;ɬZ2Kǰu>J򆥼PŒ.Hn!&]LEIshuX*΢):J)/DmA+ٻ;!v͈3ye$ C>V.E?(L[p!TH}zׯk/>Ωd4]yvU{ "~9}<{p=muyihzqf xFTz[4i6_PT u^WYt2]|vwJX߮m՝?ͻ3FP$uG]XVBU7~ȶ |"R9n2o$/ɂ'}W؉7&=I q/ӻ@eֿI;ɗ=ZwW) b6N&S~N5/cP (6ﲺ{ }Y:~uv@gh9>Pqqpa*=07\'95{SXCX:[6@Ҏ-+Gױc ~yΗhP+&!(S1PXKװ+V܂)Q*ڛh*Q nwȇ"jZ {r]W+J9rw-~?]q%}b/+?F"yeILó&+JwJLg#_qh_xp۵W}6T5.M[Vut|cZUSj=}zK?C:Gw2e]N'í#ou,mG-4^dzh2V쎞ۄwI6Oe3P8ћj0z6OuјoT-C nʸt;-&f| "2z*\ G/û_Q~Z+z<(My v~9CN"kRe'rQFu”k>4tnT6ᔩ2zeupNv39K(VjyW }1"VfHM!Uq=qԪ_{v廯A6vHh6 )g`E ̢}@'tU%$,%<\?Ģ夎j!<ܡ&ANV|Q\a.\uT L}^YkD=ꅤoamjm8.DwiY[sj<ݬE R:omJGOK!Msqn.N*ӰWekO.5rnkY?w.B]i.~~k\Y@):eIz׮.sbRV@!٤u|} Fׯ|17=s?̖j}qb㽘,M3U:O\&MG#coֳ,}B}?o` 'u"~y/bjZjWŬdsY0I ep'WY,V \8Kunj٢2-B֝?d D6t)+*ӷ]QT=ej=(\0{I]ri:{:&\Ό,MǓI4F Ϫ!9]MӲw}є](Z.?^C K>l=[l4KImP eiWRT,cMạXwAfuaք+'dT˖5Ȯqg=.,_贋JgxJD*GՒASѲqeۆ\W67+e_d"ʖl |$N7VrN]Y?_)QW0+ӹvs~嬎؆l7yߠ 9/Sm@ԶqKۙݺQb$lWG6KbfwYwWyA*e,[2KLY]Y%ܰDlחQeV&tv/hEٲ~Vm3،^=ywJgXѕ`ڿ4;K'o>Un)8*k~|~7>3:RD wg dhxAiφEL5}.6t/OJk$K3%+FO)sTND;?/1W3^Iz z/{9λפCBT(eWEhNҥQaJV3Kf*֝Ƭ Ҧ:ST27<252]zYI^~eBpƜOG 5'|ӟapA8H>|o"G#z6vrelξw*QJROV -ߣWGg[k>כ}h^WI(י&R.4YF0߻qIxVB~ ?1n݆j֬_L~a"Oeghߣ5׺2,AJ-)y^īλ|MGdP5#Xv۪Q&ćxrwȱQUSv)v(j ] ΋ Jxiq$'PaV.U{wp$d2"ɸ :RWOQq*((EU( kIKuVUuW%#19'3D1zz^]$~yU?;,keÖ'V* "z7톏^}/AA/WnOAp!Jx3\1\(O噈=?MAp)"fG?'_M" >>1zT%q RqGg6} RSSyŋ?s69Ĺo0"8EvoOeJ7ʎC!ө.VY" NrС IDAT?,\IbilV=ǶɞYvqyuv4[Ԟ]7ySJ %%0_oE3X>ںWsUD@;4oތao>SOaa!jȭ[zL&73`,Jmؒ9ȨMغh<{gr'? q"@ZymiW3esf4O#~4lݏR0)tys=G&4>f۾34t9riZFp^PP`";;|$g{9(!k %l'snGN2q0r .[FCgY]l- tn}<13vRz2nqs—  BBXw*]F^ I3+H[&-j"+xKExT { aB]E%ĉl(b8;MS#k %'%? `0 \4d $!a=nnme[g%ITXN_ʎ#)SU@II.S2u|C zdEÉçQ⹀1qD2Wз/ U\3Xx:1ѝt^nfKPDV!xz [.eXCdri7Vڭ)V[0X8Jo@!v/q'K&.<]MI"£d"M_|LdYlA$FMU?ဩ=s5& T;F~j ԸURw_E1\6ĐjJ+ o"zbFޯ0 b`[$5kB-l?۔ȚT[s ˙a 鴘N1@wr+^\.#+(Ӄ1{Uʥa#xmȎ5+ƮgB;#2[KP/Fl0N~̖#K@(q,T"4)}=<,3g8ܴ(1s}| @cyE2yL[=v&JNaHe@(g`v_Vl[P1>yg}$jg嶕 w{ cm/ŝV-zOD,=@ 2νNV}lq2ňV)lM ]7hϿeMLr)Ç0rBvf>U8)|[QnȊ,a>فxJ |;УtA\J"qNCr3^b}8U->7S׿jFu_Cj0=2*:/؟!GpT]2mW= jє`;Mʫ*J. x1YVCaxepOZ,-"!uuvrU/G9rDz#C惿aQ0E7ծK^XWۑ˞EjȲ$IbN* Yj=&?YA$ERaW* "__Ξ;CDD$J®]Ssgϡ:~,I IDATxw|Orwݓ{"CQ@.D?@T\8Cp@\@%(Q@-.Z(҆b7h2O'O3ϿIԪ]}Xxxh |^_fJjښm[O_񏗁< 00]>qrEoI{ILJYݢ1^gw[4]x>,˪Ҋ14M{W~巧6Cz5/7Myyض]cmx}>\.~yy\m4\=?[cgrp\Uܡ!, nv]!>En^>YY9.?mvCܶmcYj}1򒝓O"$WeGHNI#tq~괍j?Yܑ̼!gVxElOp9i> &V-j旿!vsI-- 6)|MeHZ iRۋBUrkfFTp<[#em[rxIٹR2Z=>4S;`!w;Iu8oYOnrR!,>4]">2RIlltx ",Ѕx3Sّ*Ą{|YilOИ*DbA,ҳZ OϤYޤTnPnqIqw֯B 7O.U#KmضK`z>8r*W~ +"UC"?>h6u7_h-/_EG_̸Q^${=q7ZD^r2g:|9_l"/u'/Ϥ>t2`ʳaD"Έ]AДa٤դ㧖Tt.jP3<2KC@W[SR%Q )/O\r*7/`dfg_nBڴ@NŸq&.hl{IٹTqPCTEm rC14hӁ=KfFJݐ$Kq% Dbs[ <(rٓאKﻖ5=!M85x' >XAL2ry}Xhp\\qUΤW` .? $Ѽz}}X@X3w, '4fjY 7څ!ʶ!(kۼ)7Ew3""9xUk'k֦˰zMMAjѤ>077ٸ1Nx#L1؞xdDKM/_6{j`cysSƽաishp[`u |>(rs=]n|*|`pOSib$ʰYlܛNZN'l ዯI %DUG & (YXjDa߿8g)V>~uiV'x4auR\Go>cT_V>u[gFSGbe\<vj,M'"ڏ~bt C8ڴL-6}<6Zlʶw#'%˛m;=_j*ibݵ aRTS'= } í+(*{.7OqziQ8!}ly7w/i]_J(7,T-&V.Wnq3Wlس]>8Z4 ca؅e+ږE}x<o`xm00M}; 4 vxXǣ'3SGv|_|~+o[ڵ·ŵN&Ÿv-{A$_砜m[- GcD/bCn0}lxqrpF%yr?_{'֡P$otq/k@ 5NYs~#$p7ݖY\3l_4Zqg+ٴ+\]le :B* 6sLsqd·&2aN]yuvfGᗟ?ױT5Ǘw_z2y[$vypRmݣ%GEVj %,s؆i rc^ғȱMC :-/9x\~k\wP&9= HɱpD@`琜x򑝝KN.h&.@\C2e璕k.~#;3< mc˅' P?F\=&>OH:YMfF&##9#(弥2], 䕔@B1̓+%GA`X(2FA3Pظ oڦ`Aq2s@*꾙۟*U5  U(u(VR߶L v "]l '=À^J-y0(4*Hi\I d~FFu߶"r;Dvi9iE8= ~9D $ea۶1EDk>2SrȵMB r"[GDeq1= %SX"'Szj}K)K& F""R>+""N]DDQ#;M%"""0PCDDM7# qD"""(@DD8G ""DDDQ# qndtm]E9ftCp֠\ӝ4znFrl:&ç'`7ӗn!eqCeiߙûvQ%$#"r؛Í"o5Iln>I >bO[Q,N'UHQ# qɻեZ{Xe[ZNnŕ(kj4MAcӏ| HsJ9Sﺗnc/Q;уڎՇ~D1\ҟ:uhm{YUhtNeBB?]Msy93S`f0mxj":Mˏ9q`p;q=7\Q&.jSpGU%߶Gxl"70#٦FNprR=$#10c&6on$o:7:GώtcȞeTÓጾ.%wnc6Z%Lb<93?< ˋ 0~x(xmxi(|3\5^B7bf)"r":)a]癫X4Q^q(8ge/kȪ3E[7A _phڢ>|qVp Ϝ\:'P^ٯBvY.6kܴOw#;rXM.n᷐1?mcdC1AY7"i˯, /L0Ghg2m.3j#X\==c&pgx_xk'# *M8zPEjxzn]Y&՚q}\1 z~DVkH.CyQ\rJl c;{i{qU\Du|T? ^Lx0 l}16NY2wѳDD8rRHEN_ȉ bs7#""rLIL"Cz1嚮:5Wqc6lIL9@e]dt\7nGZ8^DDֱ^ -3krMwRTDDD*DDDQ# qD""">%Kr1SB2[ {9qyk|G ""DDD9Në4;q}쟬w4V, vvTa/;uE;^6Ŏ5?WmkPtj.Cײ-\{X}{|fY/Ngޮ߫V˯[xO'""ǎ# # ,,YA&6?}梺?';(lV-[OcDDqdg9-!KXXۙ"kìfu,9NPuӉ1إ#mi]/n̠h3h Ng&֎g M'""NJ# 3{І_nqfCЅم\ޜ+?-bY]Bp:9VMf(>̔|`Ꚁ1C/ğӧԧiՃn1O'""dž#2݂έO{1r[]NybK}WL'""DŽ M Χ%`ĞψsҜઐDDX`$q -YLO9A}>r=]7w# qD"""(@DD8G0@2yA a0 }\k+xL22M#+T wEf4&"v߽&>]DDN< FX=:vB3DDDU!^oQ&.("r⪐NP߃S8u?O(""ǭiwOL\Q Vu]%"r">j>ۏ;+LBӲL?9fE{=NK4yZ*""ǣ iةX7l.ޘ0Ҵs Nv g>{]Ɵ/eD:>̣q7pE@DDk <ːϖ|1<9K_s=y9'qg.\٬.PJDD}G ""DDDQ# qD"""ad5 sa&0bfx]12)""DŽ# =mv#sa4=QgRLDDxR1S O¾/ŀF2t|Oz>8+/ ň1z׽\Tu}IQYT׉܎n26oQ[T]%""\5^B5-EDD* ;9I5<'B9TJo_I[~eWx}a =Ň?+QlfNoK5OEDDD+G &a}(g=;.t KDDQ# qD"""(@DDđr_ƻif3HJL<QUJzuu<9\nڼ0i>9|)`S;꿜WKSHzZ:]m9mߑo~uUDv_rHRR6yĕˉiRK;Î/';@.ڋDN,eTUp }ҟg7zb IDATҷZiWB$8g cQ([~ˉA~89(<5'2>mv/ۿ~Iou.amcǏ,E3R!b۲Yב8t3Qy;廟m~ 'w^DW?B`j]{MsmMC.w":ӚT'24$IW!^ЇfWj^1;?؂UpL>QVF Qn8Wуo1n4MhH1dbqw,nx= lCt̲SlSqgonnxsNvJql>MopI|f%p<O6V/, Qyl#mr\ˌ`_?O1̟xwQDkc =b&{dҦz ;׳]9uX?G-o ǰqUL#Y%ưHY*7ӊjQTݞ]D]|kdnΝ׾.QQķ<;:Rͼ2v>50y< ۛ^øɯs9_@:фWM~7}_ LqX‰֜n׼ҔKE0$`DƻEOLd8h|%ot^2mkS%* O[ace & (.On[ɊwFx]:)wc锾c&s;9 MGY=oxUiu%Gx&m *N$ {W]ITS2GK?tP#2qa(yo|+=q먙$1 2 9O?.6&l{F*DVoAk&n_\|l=sNiA(BêӨ C[NJ>κ Ad֜s ~*6]o̺k ^%⺂s#  & wWzV~m*$Ӧsyi<xOƞƠ*g>mӹ[YZxuƢ7`){?yWBax^ۓVcSJ.շm^1NCjfUSCoƷ}_/ͣo0?w1U/"Vݭ7" 0B׉ǿM"0,ᥱrzR|>}!A1aŰw._96ƅA`l-LI;{h%Ai-+ ˶a\:Vt j'.+~zg+oc~UҤi}ĨeZW{m q*==oiލp^ֿ+cЖ|Kgӧ׺;]; \}hπ ~=oX΢OG- aD9,fWrNm OL˦ؙvMuO0',Y 73OnA!DX#J;7Хub¼z!x@иIr3v#/ 9 Ê&n"j5iS}SU?9Zt,|:Y}h ɕŰ͙`gv"Xw u>jwJq^cʕls{ЂۆL q*yiM~:ɻIVCYm\8{/dcx7-gejvmE@i;A)vZuevk~@?o0f4UKKdI|x$ _sU%*$`Z;>$ rI؟f˴o ZRY 97]9=z5>sezfu'?iEZ:m?Έ;ѴNM$sɵ|y[]8sheB)eYŦ;hF{ *C?xOn6o-/-Eu[ Iiֺ ۭ`Ц- fcSڲ]/`@S_|Iv)ĴjM5J!}vYUZY<Qs@`vj*>Y{W/ӮЮmiڤ%ET\u[<(H`'+eݎḬO-hѺ/g~wc@Ӗ aj i@Æ fρWϋϾU:Jt?.fm8ke/S}F߁濎'~Ҷ?4߾ NZi}xUbӣ}shGjѼY(@&)ʯ/GPQ&<~cmh_& ;42+wuv!c=/\p`6bp8!{ fbWaݜ'&2\y 4“ğ~dG;jn6nj@d~x^O}w}3)M> SZruK~nc9qQ?..ln)}KwpE:bw) R-Cyb$&sP"\ҽ6 FV K)>z7&LcOWN][k8![ܤWx[yeOv`%лyQ^{.橌 jvDŽ?{ .|' i~O&]S&e܋6y|"צ;ц ?z#ࡩr3iV׿uS^zɜf-_Q&1As{]} g⥏L&,SƼ,O?t Q/-c2m0jಖx8cWuy2~4f4x/屳ؙ/:۝UyxfL{"]6dwzΜn$!fCoLyf;wNzo%Dx8Zm#G$/Aב[MwM^݌!wciuPꫯpN>8LdgqVRWiN@D+KVe"r$SX"}Z r2;-H,0 iXEdd!QUYi aa]M60 }ȱFuk~ȱTT_r]?HKK#99\ 9EFFNu<9"""N(""DDDR# qD"""8xTM7ARbbeU*ᡡԫW_{nFLgQɎo<>5(tWlڼ0i>r\yR!Bمɱ\_"4'8[+]%Rr?Q\pjcjV :>^o$<uuis6D7s+|=@D*d NNRb"aPA=OMzuٳ{MJ֛o>x~+yryܳ<]w5 Zɹ`Wn?] H|H"M$׎] 4d2W x(ξrUm,*O;n{ƝJVџh=r`5ceGgoDvjՒ_NRbyyyڵXbbbػ7}CtLSI #|'ov)a]"'&2zn>ڒWx5i[5jw؏6[lI9~NA\cj ,-x/s;9 MGY=oxUi־i˾5Z$˶lkٹ,\& Mꐑ|:z ˯ظ9<:7  E#w޽>=yU0~:~ܺ8a *|݊/< ";wӦMH$??}ᑞ7Z\K@-F\'."yrLll+?~Ϧjh\[]q/l\|M}V~a6Ϝ]#ΠƷ=w{gO`>Aw~N꾃T),˪؎,ɣ `u^ftTϝkdhUgy,fƌ M ߳Chӭ 9o~GecY둱W/=;~Ĥ_ah}؉^SX" V>eeeacBZZadg琚V|lcҠ]+°}(5Җ[? ?kpgpF4g wrcXxk"0έޅ;%k7Ы-%o'Φ& ۶&CPk6v m[33-/|Au=N@D3)lr^MbbHNN*Lll ӧvбC[+_ ,>X+v݃j^1 0iti:.%fʜ sUzIުD}?Ԑ͖-[xܤ˸cś%5%???lBVVy kd]Y2YiWeh׶4mҒ EV}ЬUC[m0lWWH6lhP0}-$˲=`оQN5KQ|x}IVՖE4,}>.!Y=sKx?Mb^j- _l ]DfxM T~K@`]v)'{|6cƌfqm<9iie]orDn8߃ W_ɹ ubf,9G5igN|EH3 C\%u`M_2SI^ۜͪo2ªq~$~,)Yiԍ񐞟Tv ƕ=ezܼx#܄Zemc'1ӿit t "+Xl]\E7s5yoHWcl/@D*SY>n6{11ߝ4kd ㉎/uZ#,?)W6f5|0/\JFE+>Lcnt6AKS C~{,ݖZxЪJU@ĢbolК\yrsusylޞIZs;_莤4w\9oȼ\w*mr,Ƭ}!_Iܒ22+x, D*W_/}uK٘X8<]-^ϝS=*;]<5Ka:n]&g>K.4G<"r}dPGXW .i37etk>@OQ[. "EDEv=GmGEYNawۗ6u[޿W=#*SNDHraxka1^VV\$Xa)#4,CӶV^0 |ڮe2MMZQN"RIX76maNj6mn:aݢZO{2r(oWӪFD&UX"MFrrreIxx8 z붧rkT[ZQg6*t qD7# qD"""(@DD8G ""DDDQ# qǚ}IENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-edit-rxgroup-empty.png000066400000000000000000000364071501654372000232360ustar00rootroot00000000000000PNG  IHDRcr9sBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeDo 25 Mr 2021 13:09:55 CETc IDATxyM׹wf0\`̐eXe!,d-Zl?"I٥M)"YiQ_JfTTd3fs~̌a04c}4y$)Jtv'ӸH*%|r~'&㾼[iyXp $ܝœ'( ?#LI{W20?Ɵ4$tH""WNΕe %Ok|^"^pU-Ү6%t\NœFٵw%ӱw>S+_8V7@ )0.a G┍'VUĖJIpk%Q'o.;`#r9SeR|Y:Sq֦Ad;܈+h쳓{gs8Bi5{*cط'}o$n=7X?n|t'`mGaH2+Ӣg:reX'g7_ɢ}B3YN9fd8(oVrr4Nw7/XK .œyHuݩ$6aI>AbbbGiyF{W0sB68e8p:z3+Q6(B0- QQ=*,CMgWG1.έ%2?F&xemIJ% s,Tauh޲&%L\:ШHj'-oVPt$n}i(:p߯:F^F-$ ^Eȭ-cValZ~}ȁd-S;UX?~{3 nI9xR;^E[ .ќr.dyMEi^)Cs 9.JVAؔ8tjPA$.|=%clL`?Nʺes6-0eGؑ"tPwxK)W$i(i5Gn.a$!\8 J&A |8qם©c`ndx, -5]kX}1|~'g7N}ůAs3~S^S_⥗g@]\VHMMQH?3{> igK#0j.w6>f[8]GP]G=g3nj^Oܱg).e](&<^881$VuBnq70<{1dm}}$ p}~ 1s1t(<)G7'\I}XNR,sK1ML paf+5TI r7ALq$r3S:})&| 7~OpkDe˯DZɉ$X(CX"'O'|} 'pPp>cP0m]G_D|(wH uƑy+Vm8ePg nNqߌU2)7ҶY0>rM۩c`{=|MpZO'>>C&Mtur\,`鿷j8}QIZ(tc3MEt57j!M8|7'T~ҍ՟.<Ӿ,7]G\pI i;ְ8NxÓ?G>϶ܛ ~.qp>6b#-)09r6n/ØX5s6Gss5i=-20O$,;5 hd %]o/f}"V}sw%었\G0gB4tQHOVE c ѪXJw,1e%[o=B0p*L```G!_:wӆ2JIȾlͱ܊3jʓyq(DBB2~ynI8fA7֥ǾWK;oO.<5,~I#{˾b?q$ar_s~{܃?olk;Hqn Wlxn ]vm%qX|ذIF='xh`8`~i^{?1|v7S|ݲ\Zd gU:I ݇q. l |˩ND*:wȔbvğ""D_Oq`b|q8Sb&AwsO/e'"""{Ju; Yv""b $؂IDDlA$""@[P -{HGccY96ozHv󴜺DDH""b $؂IDDlA$""@[P -(DDH""b yudvq\{|ivލvڗ*""PR &>>-Z\jR%XfWiM'"rn޺N*DDH""b $2cɃ"=uUp [H؋ɫL?VoŬ~Mq\""W/qBh|sڥZS~${g( Pg^l^l!Y6{~_Nzo+""R0x7)>C1ql}N1>ԬJReT%_c˧m{mnFx9ʔ*EHxMxeBCߕ}ojRz_+y<֩S8(&ૃgkNg3M)Ԕg6u/0ێ?S""v@rg悇) ~}7^f@mO>YƳ|6~nHw3_nLلE;<4sF2aQsY+c<>tǾxNߪ5n5 jэ47?⃗P(ZϏg S=tC0Nct'g߰T=C_]"r yyOo1Zc9} 'tbִlfJO:Ҹ)*8A\ Upw~Y5M~9>vŲ43Eih{ןfI\K^nB V]wf1ÇYޣhV;^lټ5lU #DYf=*#ԙ2\[9k63LUrlO[_X1L%>Ye ! (8Jq=fle')ѽlI#Qq1r+%eïXOYpVy~U4t2oAZ*}Їr3aL>1N$5)$M̥Q֓pxe:ꎲ8|8=Yd5DO# 0|5L,3LY<7stJt^(٘u!k9n+NiP -rb jMm 5kp@:\_ʾʴ(a2jP ]˺CéJ_ Qz5] Sut ?ɸ mQ([t=4=$).ݾ ŬݬRk񛿂_7iAwv 6=͵Es.Vo|f5OhP. ?ԃ]!m\G’6ZTvV K`doEz.{[cJޘ;BRL2ƞnJ>ZՃаT';b>?F{iw8 ÿ%Tr/8829BJ'Nx\e>_D nޏC_N]#"ۋ v(>:sZxaǵa[x|LbɃmfpGwiohIi`e8޿æ䎅c""bwy67]՚իҥ宆m;zy^v|{t/;؂IDDlAt} "bk޺N*Nr駟.w5DDliƍ\.}}_xx8ѬY]pݸ\.ýR):""rv[P -(DDH""b $̲QUA ^i-[V""W>rWe'""@[P -(DDH""b $؂IDDlA$""@[P -(DDH""b $؂IDDlA$""@[P -(DDH""b $؂IDDlA$""@[P -(DDH""b $؂IDDlA$""@[P -(DDH""b $؂IDDlA$""s+ ""#::bbbR~PP.0@)1 Mzu;6mbϞ=T\9VH@ڵ:uB)bbb,mB) ,˺Lj#5k-j! y 1c0k, --Gy$O_wNj!\eƎˌ33f ӦM̵R IDKZTTT3vX<FVK$"R@/&L)SzTT1cxz@) H'Nd-cx<Ǝ;/U **Sɓ'/uΠ@) ˴KsQ  $•Hb؂ZH""[H޼uID]vוHC[P IDRlluޢ@)n7ibize4q)+%rؾ}P4Moߎ>$"R{n6mtFw\~qݸ\.ýRIDUOe'""@[P -(DDH""b $؂IDDlA$""@[P -(DDH""b $؂IDDlA$""@[P -(DDH""b $؂IDDlA$""@[P -(DDH""b $؂IDDlA$""@[P -(DDH""b $؂IDDlA$""@[P -(DDH""b $؂IDDlrW@DDGtt4 x \.aaa^)_$"RDGGcM6v6mĞ={\r.; !!ڵk{};u!>>+e$"R`Y%ۖ7(DD $__OMM\0HrgEC[P ID,-$oR  $+=4$""HqH""IDDlJ$!cz.k&M:gYޢ@ 3.7zhƎ{ jt&uى j90x's}}Ĉ7exA $"/]j>(N'x"?0QQQ]ߛ]v $"c<Ç0 x ,bmxID*4l0d„ :IDYpC=ޅl[H""4M 4Mlp`&n;{T\.۷o?Jxq\^DD pvͦM6n7.p@) """.wuى-(DDH""b $؂IDDlA$""@[P -(DDH""b $؂IDDlA$""@[P -(DDH""b $؂IDDlA$""@[P -(DDH""b $؂IDDlA$""@[P -(DDH""b $؂IDDlA$""@[P -(DDH""b >*>V""Ry%4hbEDS؂IDDlA$""@[P -xm󫷱cls[UZͫshD5񾠠 \.aaa^)rbcc9'6jWo)`haԩSL6m@ʕ |y1niZ(^䲚Х!^:hڴ)_ ڵkn:@A1JwE[]^C@R {u\8eRDDkd*0^͋s7SymEws\7"r:j?`KR ɋRWܜ﹯ô MLcWfcnӕ0=߲~_7I|)m~F<_ Wkv嶚As_m"Z+\}_[&dn*^Vrԓuk9=<ڕ(C!eRXlW<|:^[ۯ;G ޻|Xf@8'(0 -AHD=Ż%ڠ~ij(Z+涻'^Ol#W^+KZ>;m[`<~>6ӲNMXXKd,s{po|n֧瀉x~&=}ߣ_łڕQCU6/q^ ˟}>Nj W~3^^˘mCmjPtqBoHZ>ήGӥ^E f+}/uOԞߙ?lN])3[ӶF|n/ :ҰrIʔ.OpSxBY4e(sMUZ|fֶ#Uw”GLm/rW0ʗ,FJ4I^O^\X`Dq =WA^7{s|rFwnHdH ܡUmkl9qXϔN7SrA)T='1tmtePCXIDATcfmL#:P| \%Qp^i]ҍR3mWZKV8^t<>ئYum۽p:C ,"i րR.!Ui6_l=y}郴_S4E|f-z ilܐ@x.,޺z,&ێg[/a+KFI%q/MZ]yi[ym1/RQߥ\sKBʓT,xUg-ЈyOcn=ߋt#lc"vT|H,5)#ff`cRpN ǰ @k)p/\V-߆8._'N_ˠ1yyS~Ƴ#[R,O~Xov9FNbMx YsɌ|oŒ}[3lKMO\-e E0{G<غOb=5X6bՙ0<]ݚ ^cr?b9! Atz#6Ï>6;G"q />@'z0sZ>}? 8͗l Πjk~,,?|i$+fM.+yȔ{!aޡg7ЩeXnҚT%[o2M92ߑE-hS 57$~d UU]SFH hL*g{/ӧByf|E], ص4u|&YWɟ'NZ~%+y9قNOԭN3M$XQxL=,crb&CXxP|$Ue{^ P0ndqd>1\RXEoOts1;CIvlݍً:L 3e[wx(߻XfzZfsYL3Dzr,Mݼuٰ(ZiRϟE[67NM,I9fadZ,3g &)=M|U?N$- '=)7ԻGF],+}?VQ6mߵ8Q$-놓$[& ~b6.vIޡ{yjJo%9_s[?<ē<66a43O-𙟂¸m[ˀuzVTzO,>d9Ii}?O0_w(41k~^F]y9xw>Q%J54_+\4M6eR> g&Xdz'o!_xH33|| ,8q: ʴFq#ywaڗ}pX'_IqƖhS= 7lOޝi'c7{_^9u|:Ogv͛5|s)xmRCҕHݹ%K/ߗ_g=.澈|x-ոlgsWeߺN*?mێwrGs.ekjnⳞ?7&Q:2>1ZTYuYp~X3:hҤ^WpyՉ,_3O@$c1ssQ8WzvL47z#?iaYr} LDDD#,Yӂ8=lJ׬Eg_Z1kPaA;֟uK䭕7CFtaP]΂wZnǡjWm_Lٹozkqگs1[Vͨ63e{D IX~$c1v`c,ԌS-&}>DԨxz`b4mwׅxx{ @Dq'q`R$}p_̹QiR9p#n*OjE9boаvԥ-g.3nf?fGXܮ$f@1ᝩ]ƇĿw֛bhdKY2[c[o4[ˆ} Je,!M(dYXEΔ01>.:.RXXu:mOkh7f0-g]ͩR{] x޽Ӽ~=3 oޮ8Xig3zY(S?ƴn*r^kY2}kR/݇ӲǍxRN1iy]qXmeY-)&nxSgJ 4|Cyh$xz@Kk6U#fI.xCr mp˘8 ϧ;A\>ƕ~^lu&%Y_p Qe2xi/{s:-蟒Q=Wcer4˟F3 <@Z>;1?Z@LJ6 ,TۇO`+ 8CZ<7葯08KWǔV|U7yLx/I eq:eĝzjZj1V R8VZŭޚu.AA9S^kRP]cHN$:EC-$ \ͮR %I3M ~t)@iI %Ϻ85$4MnWJ U/[Cjt`] CbUPg]r}vT)"iر2`[ٱ?{\DDvԸCZT;rw&>>KT3sݸ\./t<ͲZ @"\H""b $؂IDDlkwjMBB111*W \.aaa\N^,EGGc͚5Ulڴ={Pr\_q8;Ihڴwj׮ͺu9q/H"6E:WWVI p!Γ9=W.bMPW'۾d cXrBhv3}kv'd׶}R Vj*EiwOmIۍkx,%I5Xy ^D={qcs}Ȥ@BӼs194)I |ќ++|h)0/ yދW^ﴞO*]}?)4-^Mv_Q;{H"W;F=99fgfBQk 1`Z?\;wswr,_(,\]9;'Lz v!Jdث3/0RC2~:K 6/aN{C~8eU"uZgH̶\OShyNZ77w,ޓ@sY>'kxD.KO=Wah"ΥxƧ<&1K?*&oHlj_@L%=ː8~kv~d|w7o~_}HȔvW5Όo쑴*=o9vLHȦ^9]B $+>&|.n7n!PahG=BȀ>xeqkɱl?d郘C0?Dmi۱Cǿ7gYm}.*RHѠ q,+LI/<'.Qb%pÝw7Lو`v|ݹ!!%pV!嘙5'0?WU8rlάEƺ)yr +?=Ҿ`Z%+Fm@&}w"heɝ, 6̏7k-UoѤ+.=}f:?^YިgX.|ǂo9\e4_aX (Ζ!x~+>ʫ3Ps^~R1Z'ֺ֑+z1T-zGKRs]O˛t3]P6ؒbuʼ0^&?/އn;dl9+n;fH'Tߟ>|7wz>#n,vPW{uK~^ oQ \uO 7N*y "=,ΡOKc3X{YK<6_O6zH}{5>KÇaG9z2F8>I*Nߵ۹Ʋc|< Oa7s{E(Dx!$G=LqEXIɧZJ{#BO=]~y>ڸ8IjrIgʄ/` [?Aҧo'?$埭t*0i3,(q-ÞHq,"Daqo⒎X0 B9 ϒB| O>bT]0&%\6i#7o|*2 B ñ/G F,(FXql% u#g{ΝF`',NXN|`iXifȱ; >Նl~_g .Ud.f{SHGO~]|g>|GO沦-ףD%M V3߇ğٴC6'<&?sۃ,x*C_ҕ"Ws 0pf)ǭ=3,;{ FӁkrn˝H\59sݎy6QqlZ*Sڒg~?T&"""^b Lc!U3i<|9ۅ/:qtQ/֟Ϲ{wrŌrhF w'1iuT ϰ̝\[IG1 QMFxbو$|6}{)<|=CO792R^xmߍg7pN^QllDV^MV0OaT>)tEXtCreation TimeDo 25 Mr 2021 13:10:39 CEThb IDATxuxE!!8D`[܊"-RHH)n /R܊;I I /{rٛۻdfvnB!B!B!B!Ҕ69O !>6 )- A91c|zRQ5;M0 aϞ>{G뀖2R0 g@7H  !>1133\sw*pdUJl9}||rurTq!ħEUUP,--x]2ʪc'OJ JN4Mӧs;++"ČwUr˫Gg^E!jpN *IiI !RiML>Ae N^-!HHUUgb7UrJjBf$'L[t)>lcQOzEJ2%A%494L(MNI ֱ)yZIP !6)~LTDP#S$-X!B$Sul"1 7SY-S.iVMNVC:~˻6"!_=ئqo#o-ı]*LT;'SyCT8$ 4H8&z!e~0AX+MRU%A%xAJeJxw~e6/ VXEbwt΁BMб~ifA\= Hʗ}в|͞pTA/N6d4 )aA8n:?,鱥+QPOFInO@\rbo|+ԮKqߠeJ6 Xׯu֤L@k4nX-CQVK&Ceo¿]Oc9d3KZTg^ԐrHm{DA \Hm nܓ 6u=f~n&:ۙK !R(MZTr FbpۺTRFdqݣr7L츹{dk/Hno3+9 T<ߛ{vUÛS' o ߩ vvv9f`8?V>dsu97d׷{Qש2b[9zٜ LjO _:ٛ hαbHSJzIYxɋKcN7vl~FNa|&Pf3k̾=֢]}C1vD쮸fH݉3V䒍:0iQ6pqvrWeIi&}r?a՜/_k%+ڡ1#IoУv%j<=7~Ѓ3]:gQ Wt-|^~Ǽ|˂[ȞE ⦠z\i7lmPaף6 7GVHme@nӌ k6@!]Y4*΄^ɒd~zX@ A /5tLKxf/?ǰ9S(h4 yk0~6;dZ?^#1! ѷPuAiބHiTm2"]|(ߨ5ڷf~LsvНs>8_dG?9_)P `%֘5Nx|]`sl.Svih VDϩl>%YoŮ 5T6yaD5 \]:UJ1vӏtʢ43}g'_xY%mBRP3=KAНyPq.]vhb&dZ2ѿuIZpQUY*[aU7ybxx7r|wgO߲2S-CӒ>/G[|T͟jx  D[6)=+ADZTm9)ccZ3__:6'80g4 MXXNŌ vB Dʳ2~)cb]{GwԚ׍- O=;FFeDПs1sX[{{//[Lf|9w,ge:mg)+xaW}Xc1䫑ՀiYQ1̰9e$kjm?oi,ΥiM='Vl<4b=uN96ÖĜ"+ xvGZaVWv5/OӐ#Aԇٵ| ] y(a.3gB !>vt!xIJa$FOJa$F/M':t(-B^fͣ^zZ-0R۶mKr˄ϣGPh4"=y/A9zh@ڲ˄=ȣOõ~;ӳL"Ϙ)f͊{ʣS]7P}̱ ^3Y3@( ZhAFծ]; .̬YhٲeMňIV3cՙ=磗:m%$C~¾}[GK tL:ϩۚ@pz'{ׂE^1?]`~fhShOUe][_KQ@UUG%{A e˖{ڵ?۱=,d!4ՂM=1\Ӈ Q_4-~YÍz8O]e61,uoWQpj֟)H|ȰiffΜˈ:5fĻGO/F} #~f_i@JfH B \0a8:",BЀ)5(Rgd0uJ թ>!&w8tP;FPPP*fggG…S!ćm۶-bTBF!x *!ѓF]8ϸEGPgwuH!>^gccۍ7PU;! dنI\B|b* =gSXt[ᘣU;~Rf&f+ 62S($s<'+j)eiRo󜩑;BQBwSlrSTIJFCmJ>.c GҬV(A0RαlP= e΄9VN9(Xs\4E֍3Pˑ VǗ+.œ{jP;3v05-Jd6Q]ksLmZ)@"qy:[{lua1M!HV΂^Y{xC:/ ~kUv "ؾÊ;'Xwb~:6/gtDwbԞ﾿sƶsqm2ߘD*_Ld6..K6=OO_Gp׎q$/bcń<3cc}y/]o%m'l`htS8'mL0wtXI,*/mFJQjÈE9;wܧwft:wJ5j[h ڜbeUMF¨"%^Ao WJൿ3D0׵ ,,9ns_U (7Ԩ␬/=F_>±;T.Rt/zZ1M18ړ UHT^8ϴȗl9vG1H)[2+hhr NN9u=Ctt4(&&So}x@}6o@V8*qJ )3`A ie?P1`n @L|g!>>/o90CQ`4xP :΋Ic<ۏhnrCjchGᢞؾ**&-,![8'Ֆ!6̜\֛/7x2dTQ*UǴeHS̪j(HEņ_-V>BTq@kB NBt *!G gpL4V^>dQJ fQ JUt!>|97Nk/ E*bJlW=]yn-HtT:299ai-+@g;wOʴd޴)i<&g^_nns.%PKPٱcǎ?&i3&s^I8]-0.yBtLB|U`s\@JfMyݤX~Ԯ":}1%,90ZTN:E֬YrO)21*@- 똛'U8Gchz y#k3;-¶ BT(1 a0=z4 4 YFB~P7~gt).^+]+`(Zte ET H!:* 2iҤŵz聋[JiQ< 8ͦEpݲe2$ Yy8R%0Iy)4^Q?7=4U@@!hSzEXMSlŏJc%gHEiyzA1cFƎKǎ1bʕ{16zh\]\ؽ)[A[Ų.>1)~ Ѣx(gH&Q02H%{#4ڵc̚5Yf%5El2aJp"Wb Ŀ4f+v=<z˘6OTOMvP{Pt:NJrBFIDAT96ʿX3BB0U^Qz0t//>_eҽ{wvڕTWZTZuN9M5s|b4ihj$pD#Y0w/cկ˸%MJ!݅0~̙3ãuظevB77ƅit+fg']+gP[XAYܽ %2AZ8Eǂ #Ȥ\m_w")!҅tpj!ޝͼys[(J|9rvn:#uJB|f=li2OL{oL(OoXѹ^W}1I3KӻLf2DnˡnVV/2ErJ.*RoU2,QWaH%k,bѕ|3P,mptGlQxy9 R YAi-СCT^ǎ#(((EKu^:3p"l۶ dooggW8yE;+v26RB ʝ+gV5'D:.Ǩޝ=w燮"%dӐ \e >xzڣԶ4 Yʂ }5?IL:Om2p WW{e_aP^ g8~&}u*_܍*gqڐ??dfIbZH Ԟ yd~ -bld1, ;?_ U&6^z%P K4`}]KxtXK."y$Jyle]a/v~+R WE.'(NE)KeơxF.xy5,b 7(qŬ!fIkl,Zsrfϴ6 7tYS":ԙfJ rA lΖ!-u2SVΤc$w/<%s=]/z2D+nTo0UeCaAg̦'~V :s;f7"N#d7]KY1%;ONtv>v".}~L:ʺf1 yPBtTTq&;騦e}nA稰l>I6^,t L P?[be~iOtŨ^8MHc̝r7_/+c`QgVڬ]Ϥ:6 'Оa|6=khX1a>+@q_F[ l 㐭T}~@)]vbeaz TQofAo4C&8 r%%Ԑy./)x9Ξ^d {*ߛ|'Q`mzO׻lIS7Ũ\&҈'K1yPJ]+NutӦD߻}9Q(ҡ٢Bb 1=~c f֔fXㄮ8ʼ`j *Ds$oPonƖ7K]6LQp2ZYL%~K2nfK)=MU%Meӯ >+kiz[ȦA\4UKE앩Ә25'{Ӳ?f}Bw?o-$x3!L< *Sϋ7]^N$5q,HÁyr -㼿’ $( %t(;$5_kql4{PF4|&=qrǯ9HbNN[TI>?L`u}6~#{TޅK5}S܊0ŷpgԴB,}M8,{UOlrS%?cq|xve/*;ڣpL|{VP)7%h[%b@ >FCpJMg4ЎꏣIK˒=CƱTqO',kM(԰Z4 ;ReIqi@Ǻ6D}Rv)ZOnٔj;0f!n+~kUF/@ ھ oY%c-&?1~Ls*ԠfM0jlΟQJMZ _PkiÁ?ucؾn0~wпiuj;,ӧp"yBOV: !>TB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0zTB'A%0z&iUmҪh!'&M?-B|'0zTB'A%0zTB'A%0zi6=A/_ӧ~誤 ؐ+W7n\‘+f[.u{%4)_IvI ;|2Pp]tرcΝ;O~pnZ}V+z2Y9OcggW8E=}ˣiڇJݻB߽:*5%F5)IYi3[* zGo* 6]e t.%/pZ=Deq'SYZ7#hئYzj%{dFŜh]>iߴ(IJ9 Oyh5j߅_B+PZTo.à`r/]cVZf}?fu/7urT(Z߷ĜJlMSрǖp%6*S2*݋˟R=w['\s|v\~0o]߿ QÌk܇?) tMo.hV,ZhJorwˍMF-ݐ>!XH}45s.dNH*2?sJo" N.Յ _"4vmzo|ذK"7NmYz%2՞_-^O6IP}C3|V*>f򟱭T}A%p=;Ƥz%i0fY6o<=*;EZ?UR*d# n,)eeR4~0~~2Gں u'Xo ~%L\ rs tM{vwuiK1uܺޜEܗ`ۧ{ŖIuȅaiP Zsl,}fM ki0߯GƔ>au)?'.Xer${ px Q-KbcV=;Kf{lje )Sz-_7:ydGQn-'c?teܱ,\czдN-j7ƈŻX?7g'ugc[/?ÀEWZ{Q6Jӹ7}Mܛ_m;bʍSȃ,Y/+ӊ"}Qk^[V.PR =-q47̦i.k3 tMAճ^YyDڣ PGdB{ul R~zc#SQq5PZr9ۗ-Ӆ+&"2?N[‚oڹ!iU^9I=Hn+\Nbr:oS-Wv&,*,J\]/r9PgX%:d(御5 4i&)N-A13us!eޟqo"-LHN3sw>o?vi cGĦW27“x*6p]Q3hFPs"Q/Vԙq7XLOO ׂo4"&;r6¿.SIClU֝8S͓GW|sua?PEϝe}is1cH=j_z/ṡ$oOVoG3%.NZQKUler8 -i+0љpi[%HYKu'NTz d;^Xj#,w!i+SU*UiZtMv_;qEcR7υ]a״NL9f%h0 [2hhQu>F~䧨c̛+6xk'W77'\=|yL&]vjHyTy&O#WvY V}h 4)Ǵ8My|d~:C}xf9kՈNj^Υ j=gצ fsĖkI鴻 U.Qx.u ta7n: t1*VCtxsUр:o>'giӇZsMm  -[(6)<:gQ?=oj3$gfzjD^A#ުtq\>ű*?N¯{Z JF\rTrr'D~"Uyeh=kVZ@~ _~1N.݇\BEjbbN^C+w?3UoiQD١*uJt]ew$:&^#ZU&KPc666;w__4]c*ϟ&ѩlϔ-'Fzd-})S4TUeSj6HvrH]t'O$&bZ666xxy}ww8~{YݑYQ9oܳ~TB&A%_B= *!ѓB= *!ѓB= *!ѓB= *!ѓB= *!Kc !B!B!B!B!B! 5¸kluIENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-edit-zone.png000066400000000000000000002566261501654372000213560ustar00rootroot00000000000000PNG  IHDR&+sBIT|dtEXtSoftwaregnome-screenshot> IDATxw|MwϹ#^ABGE=*GQ+Jd{yH$|߯'>r߹9w7̑5K;886!B&)"4n-8{4 @0 ˖.1NCqNl޽6(͚5K,=[h4ZbcPUCS!UU1LhZ,--d mg2Xs2NI֜*/#al4h;N9aoTpn6琅v4ɟIǦy9 EtWXp9 42"IqߕH#Ї4qSxH3b)bJfWV_ M%HDC1l*&#a<2bR"q"ۇ5@ l;VѠ$;Yƀ.UP̱DFFa(ZZaU@aFU50XbM*56z&#1U*h: v8x[=J'Xhtzmmpxj!Ļ f{bz A#4c`bc>VUDWegDIXnCp V̸#}_q &"Q D=qEzdL3:"\ O:Z^m7娠\*''Ԅ. '*"^݁8k sh6TI-6*.q`< &߷tNZ"g%,9,!{CMwcc~K|΍| g{ϙ6Jew[Š=]+M9B|䞻sDD.=ik pKk IPcyL#AqOk hѺdR >01+n`j 6,__t1G.ʂ0m Ds"[d6@\Fpȉ_.C/ң3}T=d.7]#p0?aV/wN;Oӣ( p>E}1>x1|vzVz~y L%o|3%<[GշQ+P . ̬StgF M3I|Ç.h-pqDl`p֔тn qW3vaζ1ZSej0{& eB2e^|NM38(Q5FaE'8[ě^u :Cg#YyÄk&/E*fddƅX1ѐh.Z*/ *AchpQTL'wDTcCD?zs1eF0yxAfY#Dh -N6${}(hgY4q ׭lp@93 ꃓl=4߳3P:YӪ8E 0_Ɯ;G;5cX((MFOI=̮ipQWUk";`6ơqO&Laa( ƻ1鞙U`R4t~cj+w]P{YhmqT~hu=(QPIPj^~6G<$UK.?~m{s5 ϕqQDUr+CprxjT4TQVv/ &`E.ŕ$]^*cB@~ɥSP#r6/PukdatUo. NHLz!NqVɚ(&g}PUfՙJ(}hF!>-[ 0Nqlf&ˊbۣ̆^%KANMv:\qXF0jx49:}DA6_3l(-7֎WZUR ComX.G烥g%E!$*ҫ|ĬTe爞̺!;б-T#X7lB05jo]?m!`aDO͓;WAkaSe=:XoHե^xqEȭ\:r"`taH<'wԱ$HKVVI20QT.'k2ND2}Y-lpT3Q1D1TTEAѠZF50$hU6HDx4F3fZ -?7EE: N'y6b4(Z-VVXikKȃ1*H;}%ϟVڀͳB3fJDx흢(k԰$il40=Ԁ lFTV!Bd"**jQԷHjB:GeI [ccc6CB!kh4 lֲ+҂2VQ䵈E7i9c lm,PpcD~!BS2X`2d;Ԅ)NAoà}4c1t~B![h,,$70Ub `M!L&BTC1B!D!B!RǁIQB!%$T!"U)_BjH`"BTC!B!"ՐD!&B!H5$0B!D!B!R LBjH`"BTC3>Mvwrҽ!V$=X'E}6kLvhUK"50ɊB!;,:LY_yϦ읇jVKg[!MN=E}6}L^G$AB8Zk4gFQr!"5{g!"uD!&B!H5RD7¶gڍW^6+&pw*%"$r-]xG>Xwq!5&*QgҪdND }*;Q44V? NVy)[í'g~eQ@~ H8vm>_BOk5sѴ쳉:ܪطg##ޅjqQ7KZɗ݇xd/Ks=U!]zo|J6a;%unV@xd/A͞ 9MFۨmM:Q;apQMzw|0' q^6w1w:$O.Tg#B L45mfWWGnPlV#g,fN:qX?:zszR~iO~X7Zg8ńN6$>0]C߱N_|T],6[yZotЄ20%FqY6v9ڬLq4>Bwൟ3'ҧOgВdJo~}˛qME~8TJzihZ.Ƥh^@ Ӭ=SZEXm])g(ovSljWZ N3IAطJTm}Q6pp0j4!~ZCk0TmCJF!B$rdlD?\7Fp sZDCM)60w_ƭ(Urenz.qgɻ%qp!bdDVkW5Erfˑ=ViG!xoR}XWkF{[G_.ݪ?M4bTzįٖS8Rq{@VAШwiUYܭ6rg&[e)Ti\ S9IW9G!x#o(UK6;:m7F.?EdtkW: .OkX3O/l3S)ۙEѥlpWVώJŰ`3vv_5US׶.3g&=F!o<ݭd\/b#UhΕ+L's܎~3Zr|Ӊ6ج']d:|tЫR)JVbG/w*I#>r MS2C9Z4JǮoSe$B!jaFGL혅w_U*5]9ȕޚ퉡IW)+'"ijWej|@q%LŒXx#~ť*XcfU1mތ|\Jo!B(!ϒX!wdvsxRU,V!"YQ} Z9B!H5$0B!DY&dG!xg솥gQB! +K=E2hfJLp.U p5&j!3#MA'ͨIs^"B|Ҋdv#o:ڗI~U`"Bc"BO!B!"ՐD!&B!H5$0B!D!B!R LBj̽38jWMo?ĕ5h{9W:/B|B<&7U'Nsʚs}WLS%ey\gˤ_02x敛BU凹xYݸfnHl|62%3>t~6!C8e=c.G9s'DՅ#O0F >bO&mCf}RyLb3m~l9e6GŇΔw|* ZNZeLunfx??AHaIЃih=o75Rf:EL|s9J'CxJvqQ}?{f<2]6g%, xz.mjU$$mbT:C^\Y_V(A̙pHv9Q@v}A9H[Es}t~M| %fn2r WX1?ZX⵵l2bʪ x < WYy)-S //~8F%3qwW9Fl<@dZؠ%S/ɔo~}˛qME~lyzit \+6s$e >+*?F,.Ȅcatt8u@ ]`kXuEɰ9vYɃ=9'rt|,-Z%M,8݌y7)+oY&TU){տR!>) LTP6jғ%;A>P ѱ4Bzj3fScdaܓ3 .NGfkŲ(MnEfx]Jo5i:X,niY e&41y ݰRX"eEsrQCA-1ˇX:n,kq ](0Ѹx`f3CJӂɄ{K(we6r?[ ׃jF}З'TEf$5mhmMnK8XcK`,Z3.8 ˃Оpgܻ3#eEbZ3gB䒷02pt9_پ·"#e!,Po#yH#"2S-JU 7oA[\-|яOf<:f3f~>hZg1kg3iz1[*{3;=ٱ{ob@+>oRVÉF4 ֔a-vf59ת9dQŽ G]=J$ -޹b1ןо9nGGEt.?n;rF |=]QdŶxU0c,˭]ggg"V,ݏUI4S#fGN?pAklIYǝا3X4m=иeKqTdZ|'c69hlpE:x,c<`ꕾ ߁(Vv8BzO .0lS`nP z'/ 4MGEOiϣʺ ];!C^MRO7[*t*s5%,=߂k : ^iMl؀F-Qw/,gH -B!HA!&B!H5$0B!D!B!R LBjxpdd$Ajh2Vύdd2⌵uIY!Uˊg80  EIZBf31'W򅍔B|RRV<+ŁNڀ^܊UBO(DFEp)+RV<+}LZ-ț7EyiBW)+Z_JV۫RVy{2@F!"ՐD!&B!H5$0B!D!B!R LBjx8tȻL^OoB: Bϡx @!HlXOEjU3?%31*YԄmTPUv*HY!ċmXCgx灉'5! y_5!@yF& BHO6#ṙ+0qii/麀KfPeҠ9.{d Ex `\ Tyxr&@}KIO&;KzvzLɛx[z%w/vFP=m:jϸ0?͖ջdLI~ XƊݗPmJ1'MI2B|0mEe }t54@KQ5w4ŧ=&ˈF`[Tiޏ{f:;>n 갴u%c 4"i?[-[}vi-I=3MR!FFksrK1k/Ũs{XWӍnx'._06FPמ~H+iaw Ğ1M(eƍ~`مG7DۻzKL0|ɂә)c騾M|,k֪(N9UQ/ ÿ~~ӹ1dY|0901 1dN_bGA?: IDATx l# `,>?]ԭ?p~#gWTs/̚cs&VM 6QBetPUkYi3zVğ".ҭضm6lge[6|`]2x V/&pst&#&v,Oc9Ouw:2~ {Vv'+P;1o {&;ϧRvuԉ|l|d^7x̹gdlG⮔/ _Pg.՚sw'u 6F pdܯ,3b?35V}d4UP)d&jeW½ ,]+\W??r"W;W:gg0j u:xz_8QG+oH|>HS␅JSBU L܌ޛ&<)^էE m͝ݜ7DiDe]jONtV"s#O)J.G嚍4x>P3O:{9E4 '3ߢ☕S|yʗ͋VwQʕ]ܹK2(Ԥzͯiom-߿t`X6/ ٺ"K֧G ¹s`ju/;6XٻS*߯ l_ YƶooY0lV,[:YklKĆ2ԯk D&=8#q8~GU _tﴀէ}&6vig:fR;nݺzJ:}t^N"@Фk̈~?7W.ϢW(i ݊7Tٖs&ߥ>* v^(닯/72fz;&USknWdN̙X1cS'gҢT*CZz+|zɠ$~Ǽ}2bo 7_Ymh}*R;}lzw)"wC&Cw>qh,$K{+DSAw3O&zЛrB6M=k:,*v/ȮPOo7]+n\KurGVұb=ƝKkټa)ܰk]NJ|{W'& )D:; d0b5Sل*Xj~o~]nwDn-niӠ޻75d-LZ\Μ7ӊsдSG yriۂ\_KTp3?~K<_7oE)V)J{˷w'hpS}FOZoX1weI= &)MTG(CV>}*LDPCU(my俛o-+4$s¶ ^T}c.ٸAOKZTڃ9n;T, ,oۧKd?7_S)l5dL"Iݗƍq1%ihʴcM`{?㻘BO}*{g#+o\zR;n,U(^ϖSOzT"jT&0Xp8ԁK`_#E:Ӹl&*:Z'k" x;iݚ3g0ctfLƌiS616[}}zRzk<)]-V&Щ?_vP7Sgҭ8wϾV/))Qۙ: Zv!"1`c2{qrl-S6d)3օ$_"qA5@vk_x8;⚹%˃DG$;sM[|ն%\@\5Es |ըL P<5DFK=Ia2b- pE-&qYnJ&oo=X&6r"VKFy_X۪&ķt/?|6 0B`k9SӠ䡐oV01'-UdJq6w蹲BC-f/S-[x} N9u4l/u+|`.8iM6K7&C Jdc]ș.meeEKIחXo$$[ ' 7\L!Rv(G> YJp;|,_cڔ̟h|`6 i "[w%p_ZseF=1F_x-Aٻc;Mܿzm3g [ɰʉ5['[%6>OmaԆɷd:edhO͔ݵb7ز9I EGrdt;خ6cunтڎMșvfm4senWτ_VK'Oru8Tv+Xw-*(W;Ҫ8j ,{2|RjWIW(V Mҥ6{'&8FQ{sZ )-Gů~bj Ws={18)-eC79/i/+m$,**];5 qxQG&I{x5,1b:3\ȵP3sQ8nK/*4x%RoS~(q?;ۊ,:=5}K2iy(xOy\޼â8_rc\{es\Ix[q~4Y8}G/7]ex#ߍB18?/u}[g_ŨdD5巍#+LY:hM4Fz.;E=-P45YŪ_|)*ܽ#:˟Cߧt[]?/=XFpU[y'݋AndԩմLqRٹ˾RFPҺ%oxb0Ŋϡq&VLvk4P&2v7YPt.^N'7GCv Z!ח !"x;̷1c^m*`<1 z/ǑuES>^}H|=\\9{P$0oʝ%=h;t=E <-=r;_5TO3`LOe.^}\ʟp30fߌsN)>mчު;s}ISP?AmgU&M&WDIFm:oKx,D_aT+gk xD_p$ز XgWXs9J$2bgEѠ7㖥0Uc;O-m:;>n 갴u%c 4ZȍP'[<׍fi֒<^tz,i4h8g Wt$*OΧg͂x&,% U33|e4$=S {J|鰷!}>j^8D0j\F@|Xj5h4:F?Lf pFl^y>ڝ,ٳ?Eqx]qð8?v KJ!:%sX WCxztˢC'_1,s,la Gu~դM 1fٙV~%!0#kVt>qQvE`jWaԒ 6c]hP3-GQ!uf#&W5gt-;9WiP#nqƏEDܮـVqbfM=6U Gyrea_ԫU~z[ը ƘXDCtm_u5ϢI=j?ݪdc]~t:^=<ҭ_q ;+ 3i&4)􌧦#0y=>HQ ]7y1sh5{k:9mFsnVC3h=} ~ {Oew|o {p`m&;O^^af.44'mť&M(懺&zHoӦ1 ҹ8SNbcئA8*ǂt˒Gu#O<64#Z?;[2s'(L?.xnHIĪ8dh SSWn4f.q)~ Q+x)NSUV׋4 ySM"4a"2tbԮʹq}y&ӪJ (x)Eǩ\m[WithSbmHiţk(R\2Z>AH\iYr$<W)xTBY/iھ9,-R8;eVe˳!ޗp!U'|-ʌ{jPFBc@qh `PL:zX+B]=jBCta5\G7iQU:A Şw㷑s}Tz݌+OrF FK\AURx=1W1 l>M>kubȈuG49Op3B{&rkDŽSXN=8;uBWIlf²zoVvM 0 8<)Z !mÊ%~@,[O+& ['3{W,*Cϰgo0(*L2ùX} ^=s Wv|1y >)id1?H)ħxbC gIwrjLm*u]Hl؄d&_?bYNrINFy @=F0v{G$JB{2/9νu{T%^XRӵ)[&3F%zt(Mnyf&Sw&jUІ٫6a4f\fh^j:TjKo?#T>QYKNJw>/=feepp~%VQ$HRU`= ً􂇜.[iJ di 69{g/sUhn>YY|)~t:tVm-ۯ2ܾn@-niӠ޻,=r2&gxbΤޑByy4+v!0 a5^#o)xz{R>_Q Xq1\9_T(G/ʑ=UO+y!w)&%tqCZ#t?gl2jۣH_)GM\tsLx%.Q|ru:PCA%5=SXf.I Tޘ>amRx=87k(VeԊP2UܿzFAą9CYXaӣ*T3h=/_{yx+0Q=S5{ultYvݾH8:UgMDE'*6XIxt:M= k 61_ZČ-4nS9~aMz,☕t73)9v$jJw\).YEu[E@ [1 v_; 0Tf|?@%~>09}^kAN"/D(^YEci)"9.NRL&66T0[ӅhBa6}"~g;'9Je쑢C!ۧ9&9Oj߆_,WU',W@SԬݾ}X#i"W@y{VY3BΟ "[wii!W7&!)6総`/ҡ F>: ƒX{r|DR̋a:7N&ܿ۸kVFt)OQSxsLQn6~ٖ{SOcf (! &Oqqd+Q{zSѐٵj^ƅ4r.(2lH=5^A94̩4LgJY8 5)ϛSڅtq01io ʣoN({㜕T;2FMśxVP#:rO纔vL?wlXKl#u%;mzFV<mTX22Rï6ثP)\_b鳔5[ifbm-ؠ&N" ^iY@rٌOφff'>U6E(U/|!΋U>GyG.Bim2#zR5ukhvnMȉ] l%Ҿ<؋6P;YgkD8 ~zy(P#wG}tR$1$~G-qh1V%m s]yĿ(͉ꕙw?7Ri dw?#@/7cu-̬PǛ#^035CY t'`Q ubq=]o|fc 0mW&tIGbdƘٕpWZLiFa #γ? n U[G?͙~BЈFز*^.X7ѸB' `!fT/Mkhhߔe62qTƵXP*jU=ٴ7!c3"bt(gH,N5+yo'uk-G T% +70 J\F r*~T3qqbﻖn; 'fn0V7iYV8ϞbeWAP*?bR+%  ĹvUPmyL3=+/M ,y~7_#8㼶:'̦U7fV}X9Do+#&¯`uvG.t d,{a"m N+b vBkeF:džO0(jM^=x/u'J%!,~ӈC\TJ b; &_E,~AV1Iŕ,X{Q$L)WtjK7"(qh5~Kw43f guʰ]7c_'B/#U{lf؊hҍK<.*~eT8?&NBtp|[ZL.2tϽ_}^EHl;bL~¾TP}P٭UPB  *DENp&kPMiGg Zds|tC*Tdfv6 u <*53,ΤTdy|&r'6CJ1Q3h̨7]~)`i79:O l[[ r@ E4,QڞmOΖ]xFOŏEqw:}\ɫsLeD d@y^OL \Xُ.snȐ$px 5HԵ0|~,>Lqׇ6KtL(Xm'^|%+K<)%hǡrSǽt@5GCXKUnӿtiCzhjRP?˯̜G3/%zb%E,#Qܖ1/7fFy{D[uMC,vg3|KW#OI q5EGׇ+QO#&_i9!>0[U|OrDpvq e8%]̕4c肋K%&7"\ Nn>SʕߊSs/7ӳ3e \pFƝf!ܙ[),{ ٜZ`H?^ݑ97kMXʮ'3ZnUPt_MS9]Fcn碟 :%\Z*UQb)USϭE8w:΃X1wԡ^ 7]41ޥL҄2fxjoLνqlʶm1s+k`'mܿl֐cEj]Y{?IG |N$2Qw5(n/jTofeGؔ&yAh2,_Ҵ<t/B@'(yKِW_u5 WLN1s]dڏ-2Y0yv"NeNq̚A-B-c^m4M)Z+FOҳ 5ZPhv?T~F}(]4[IV_'qLOz(HL񘱊iW,2RR -lQQb :!r-wȡX vZiK%J R2C_*RL#MQx:@F9BdD6[pm7bgJ4W9ė.V+/v)Vs2*Mf`nvO> y2N,*?3T~&( =0[h%ӌZ"=[*{,ؿ}hUU)t{p1>_~UF FNm֙g8K9p>mp-[F$ʋ3mvlû>"zes"0s~ݗx(b;b&Sz&NZ[5?O^.nbty˯9eUYd" {ƴ2V7Ikn-jn><\N:r#G3NiL4]}^vӖqdJ?ʜYۦVE j{(1KsT|?FszA7tz+Gԝ}%BlJW :-ҝ9ކ5y)\.8ז<ٳi 'UmhfȠ=fBU5ͤ76L֦8D[[w( z`Iyj{|{{[٬E-``h$'&iqī|~ӗc?wg/Id| C?,1k8-oc2x(޼3GeC܂Y>{2{;썓6 J\3IYNDT(G ^j>u*+HK-U o|Lͱ+戣cqԧfՓsa6nZ)F8::Tʅ|E ?c<|qI7բ@1G(Q֍ƽn o9Mt20üf\ʥ(cƋWj\:=t6k`n1z68~5O3:w+wLóE2Z$hm> A$M5+cEhF$0 KgcHOM-4UQo{C8,8ƖExudP(>*Ru5D(Qf$F QDHlIam3!rԢ+ 뢧 Q>$WDS6D\+" -4ŤEeŎWD肅8uWԑy淊Cљ'USKV{9Ŀ.MrHQfR!!2QkqDL#-11$ EC'2JhePI̻X 5P0|\U<>'\v8bړkE/1NqjV@>+vdFLGvO8Abi%y<;&&=FW9}>Q44mgq*&Ѿ3JRi-RL3yXZS@e9I2.Gq{ul{#廷D hktT[&6>m@/J"mt .*$-]+8Y$'.$`\3W M2ET/+J^l[aQ>׎OB>V ͑~nHjSzo9{:?P.E B"p,}.*uW'>&BC. D@tC.\ N Iy@5 I<}+xiՕ27 Iq5 ߽]X6i2 TO&+"4XO=Ss+sdz9eYupcO!OpLvM*%R?%G6beDfuTQtm?F߰F%G, Hxy-3weRjNTHQ=wD#{ ڕ,KL1|~)4̰7wFb]y¹r jie۶/Ѩ֓^%6խyyd 5.5LZX>%ļѧfS)Xl ,SP›G(Iubb,"vv.A3Gc$\M~r5Tr!7iwx JA#V1>%6tʃm2:Οr#E2c=}{$xQxSi¹q(lՉyxI .> 3v:!3)@=xte"RdXږڄ=_ 1=7'?緣nU:n4t\Xxu؟2Zxy0;^+S|Ӽb% c$73 -)V'k63% ޣc#y?{)oCzRHKvu.9QJ`w'Ǖad=@ʮ(% T~[RR+S}"8)++}E^3LQߑ;Ls\X2Ck/ͻ*؞w2!d}Sșu{,T˜r?rd)Gԩ/S۬ЙtLٌKGD>{LI~u k(5)7踷QJTh2bb-f_`+߻I6Z#BWǧ1D3fQ 8MkR"T!2 u@eC}#^MDjhE5}0?LaPhPJF. bYG #xqe' ih2Slg7qh5~Kwf*DE{0b7V{1(/ݝiTl}qneRU+C*PTPB  bl8KdJBN.dĤQ A+2o߱ާDb'sTL7e IDAT"';o8W5y[,Gr&(0{m:\|!oPr,G'ї[S}oNQO90\DŽ.MW!zo/ϔBDu5 +KD}.yqRσkUVJURj:'i_] 88!YMXʮ'3Znzzm]dPg*zаPSu\Iɴx9;b5m=SԡN[o6z osX_r*SZ-vW8:ŠS1X o9w:΃X1wԡ^ 7]> ;)S\;/d4[m1ehjFU]J벅̲ww_s6+n`m@#MMox^S&,UȲ<&,sy="矩eP)? <ЁFhǎ}Oh܌rQy*' U <ؖv9Sp$ۗ|* х7+Q X^WXħܱ8>%,tܡ1>( !i *`iL usJ5Ѹ5FZ2tK930_%B -2q4^,7?ETyylm*Xc~rttSsө&6t"o>#Ȋ,[p}XZWI Yv0VGMS}FziK>d"PcY9^C\f}h7c&Z6T8ӡiP0U`v?$)TSI;l49~e "]6ʂG8sz? ܚ);ԑr7 Ѳ>Ɯ؜^@iɌ?kq!ٷ '5.-<ʧ$ LS_"̪yP\yK9Oh"}*@ 9JH"vbɗnF‹Ck(㷴(<>6$@Qwƥd6ۖQiތrogvm_Y6} 6=tn]gsk$S5cxIvxfm:/Ҁؼ<.q\W6o<<8>-Oeơ$",Q\sgpC^xAE8 e;{Y;$5ctN-{-dVf6Qڴ\+2Y:{VNeȸýʚ݇9{9}me~v,Zg)^IwLl3wd[uEng8:u&r)=Yw<;hYIk;{?#̲whۻѪ]uaIԊH(T%R/_F pS}.\xbT-*Dg.×Шe\0n6/$@7'@e >r{o\S&GA1c$ס%0 Mb*N&u$JsՖth\1)兢)U&na֋veh߹ZWkJ~) e]8mCniWM#α(֦@N+kl we^ zv"ޒU"dy S1h:ԢCʼn>NC/sH,*ϟ~Wr2TwρI@Jćn-5<}҄˛IH 5v\\Ͷ#i_P>LqOIXWsS1ժcLDTR[[ Xޔ7 uH#,G \ɶ˵ȠEh=ɇ/"ČH[Nթj2Y$ppZ"5jQ4+Jh²i2;qcc}ެЭЍ#Nan>;N1⻣[8SMR5)(9vɕKQpͱ+戣cqԧfՓsa6n%17Euا)BWawړ?̀gSWp4a|Y~_H[1bAn^ytL]02/MmMH'G2lJ|$֞Ƕ/K"9gVH >Q@%U]P!Kɹ$ta-_%M UWz`'S]{\qDLtU8A O@RoX8=T#e9:ũY)oDVlDD<| !%`g;۶ibPiνLR-Mx6:B**78;DZD9}1*Ӿy,,ێ Q:́կKbH\Џ_x[WN*&oU\PhAK;eˮȤ1C$4Db@6»(Z1KT@@J^䞸8e2I e(Ś2#*#yK w\o3nrNg\aAnF)GGJ; ,Ju@e $wNr%#Vgyآ?4'ZH)^<%p7jȖngJ-Jԁ7;3͖ Ȓ #X2Z:L:M]zȥ(}ŝKXAI@R=ΣLJe43FAZkR8fXr&ySD:&h&snz.U@9s\zK ?T?}Jll?|ctt*C/҉qe?ׇ[gѽwAX~K^bT?}ReC6Bo5DJv3g˻b8طXKnmmxEP7̭35 =?=Y]|'41Tpet&Ԧ8f2i~Bbc+tKiu Sq=XRA71k `Ү6L0Vqž(Z.iP4P5V]46B$6䯾MfdwO+~3Ma84A6QhTYG?UCicbHڈؿc 752{ `$كW=w$2tcW*=Чy 8~YZctb1#H2DenĐ)Ȍy;?{g:m&Iۘ@+ŁɃUAEJU 3hb01t} {ʷZȡI=)KEBH84f6%*ǩġleU*8gm˨=ٴ7!c3"bt(gH,Kb( ff~^|Wd($I%KbU!Sps&h^EڬwSb n<~5]ēXRʉgwaM/hvnMȉ] l%R R;8̟Ԟr9yY.t>9k D)B 2dY`R >+>緱)_埅TV"`)YΘG_Lslڎ;Xw ?M#qcdd$srDFF|?y$ʒj8H͝s:cL핑DƊD1QrXS"{43f gu)2.GMDĉHY3"ٟȑX1ti(7' 61`\\E%U4I~AI./f *?XRLxB'-TdU9u529FBPt+UH˹<Y,j>drmYżT Z4@M]R춊?Nbv,&cքGb| IĈR4tM(X.xp 2c+m͙B#2̴!Z7X;Df6эs'5sj&Jޅޡ/[5LGx Vu9vg\e D"$=Z8ڧP-J r8&FJ"%B;{֣ /A%4^1AkxQ*%7|3*jHFyП}3HʁQ]&nv Qgи-槶&}jDptO[`I[+Āw݉M:{Rb|qR%L[QVu@~ֽvûO锭uB,aX]o7[77]+GSkJl:zy#B}*GslAlބO5xwq5o{M),D ɞn0AkFLdɾ6BvٗXPG!2gw>>9syy  ; 񾄭MvX7Ș7uYx ں"G:-bRyxyuG",rnw $8n9Qv/#o֍&)1ߗnQ޳Y` rٱ7˼ q$k2vd53ywXح:gk*ҲXIþ5q; ;@ۍݘeLw+45=U!~AsiCTP>HÓ8R>s5uA[LY {d斗4Ni95듲a&aI:uS6 Hqo)kc <<e9EN!nM[SLH 6>t$DgN,`QכD2gnuC63񻻷f5_ұ&;-JȪ\UjH(wȸeh2欫㷍9vt7%ґ95^z {ͿpcC!|GMI;}[!^ƲÏ,[dq! Qk+H;3\Py4RڊRf#(c]HU9?Yj/g0@bU3"CkGRot'4#[>y-^p^tŷaV/o#9(œ1f|9Љ !~ ȮNҽ9&b<1 @NXdR ޖļ(,b&)z~KYޫҚ0i}G=?mzuObDb».H &!Xۖ(zEW*ifqƝiYΟMkаu4GVprr|bdH)U|jRݽNEJL{{.+sYXWE$+58i;)־Mj_όihG \|iH%&&–:E1cy0C.t &T+$/n&΄Ƭ>ͩWۋ1tb_>[>K!*4kOt?72Ϟ x56% oxŽqxޮe{w?SI˺ƙ󯰭ߐr_]O쟨_&FΖg ^g :˴h!!RЙ̂ <|FƵI. 4PFdޙWRZ0reX5-{AFax(QP"Džmi=8YK Eqi9繴Ƈg0lc'AKm J+o\PlJejjW#`e6[@L?t4 E"S~|xҽqE.D]c9uVYt.L]~GUcA\OI%E0-h+ $b"J d2P/v|( [vaQ;]>KBL'핈yL*a]m&b2ǃ'1њGJI!:} n+Ng7]ERSPT"# 8e5]u t)0$eF3C *KZ.Ë9Yrħ#Ū7ܹݤ%~]a%1{ ՖZY?Σޠ6jb I)[QI-ݖ|\ U+QR7WdW}syZccۓ^YaRH"- ; 57{54Bqwԩ;ty[jֶX&'vˢ|籔P(,x,6bJ2S|vj3B%)8y|=mKQ) eű$6tВҦ:V@Q+Ng@YkV# .Mƺm+JW(̜W?bȮ$W Sv9F© ȝQ//yDtHl`N~ɥ3Dv瀁m1"f/n_)`~܄)?X .1l]^DxT+Ϲ͵ ^ǵDhf2QWhY1EWBn_~v^^):u0,S~'PKSJE4—1!%<1A+GY0Vm)g,ٝD3Sc~ՙNg9s_j'2ߋǑ"EFzaH4t1*Ke󣳛E|Q7WKP\r7l'eςm"i@a*VTkƮ-?'Sޟ o{/ ֬,`(]͑OIr)5,mm#7U0ϻSU}4{oewXTA3s #,l+8~=*_!q |#~ D4m(+D&Gش DL1VKvϪ;~ڞ:چY:Q.>':HmNuWzR?8%^ͧu.ϢӞVBm 1=S=lʆheah@1/$~=fZ8GYMAh/[i 7Z63g:;)hDM,Dߴ8.u2m1o~Qm[P *?_dQ)s,;dɞߖzn(c"i,"@؜2. 1mFlNoHHܹXUU|e]!$?^bb066BM7Ĺ4l/NE! M8W*5` &FhVajEA2f\AwocQǏFPxRU(^={iJDKm8BÆ)FE_$ A: tL.'))Gv9nqu^,#gw#쾒E Ukɟ\N~þ=;K]{GǓaw Ryi#/X&j)cnj ILEt߰Q5YPRB{o\\9Gg%{Yr* `)_؈ C~?ϐBCIGTߓP&*T|Ô*e˝8ERRWJ|UPt0QqgvŠEK?>&)ݤqאJw/@WEL>ʜGxl'cc06 %dv/62j˹* +ф*/wy JK$nSgJ|sAv "vNd>]ίX~{O8 @S9*Ͷ¡lume%C0-?DR>C7dՊg߱%#"N&xU3LWOAW*DGuJTGoދI۽&2(N喣^~4v.D]hm4NƼ[Nq<5Ѭ4s n?ӹN_\dQoOJcR}r%*} TM^ cQp7Ok1%5K x:S\_X!*TX u ]l*Ӡ.gd}p&=0D۸$U[eb2pb3@uQДJH7dź6C^s vXМ)fg g^,NL_!R<_YfsGA"ECk:t\ͥ,M늧9zhZP żYLX4e.yF 5". Feq3>Kl]]͋63uJ5}&͗dZ 4sGJ<:{h$c^ HlQ%{5hN>yvF:Xdѓb-nxm%Y<u0+aONSY;.x:2-j}_h -͐7_3`u+Bk ./ڵhcʲsoȰd%9[25sƣcQCqv%b'_ jBQ6g,4m1N1.ftG|YS>ؒP]/Al>.S9;c qG;h6,; @L'!S`2iDN!nM[SLH 6>Xxq:K3u_ X㷍9vt7%ґ@4^zߡd651FtR,AV׷cZ\š>+w1fV/oF hε+9wK=>Е9=W `+\r[a,Ī&?-cgD$֎o%lI,V9OOUx7e 7+N}(5;jgGh8@N_HD$###"][jxO"Ƙa@'.p% N;H昈+9 OcI-2sJ̋b."i'UV"NQ;cI#`RfVZF.ƦD{XH˨*|[(" _VX7'2e\PzǟIEM)I 16Zfeڇ$ aM7~R=C# xFb"d6wN~%tC~Vš?2%*ӌPǦl"<8<]Eo2TX=2@q"oSL#5 tt| a5Μml(≜+RLYAGx VFƵI. 4PFdޙWRZ0rec CAWHLDΝEQ앸e2jXLYgز b XȽ,`D1W"j::[$To0AӃmKb fdQ_ĸ-%4<~+QydO`c5?t>B*:9/"uAV`o$i$gPw"98e5] 1I~w1ƞpF0LbItx11+_tX4;#MM8nv[#)M Rd^c|_]N<* jM*)=2nE]$t[F sy*TDI\= ]E? ^j*d6- ; 57{[ЫH+o, -Ds?cɉ3)(ߥ+ny%up$+' ӦLJ.]$ꮄZ?Ok8Drz [# IDAT/<`mcڹP2Pg<~hEP5`mjDO"# [EUn}3YSYwNɷ9y$;4\E,?ۘ⭟ޛPc2ɗrtӅ爀lWEQݘ5 (FGz:SW6(JhΫܫ=Ee*Wj)]rU* ,Q! Z9TLΊAjK9c)$ G$Ƙ δv2E=˙Tsw^(*OƑ`֐ߎulK bjeKf `(Yu"%ʻa}}q7!"EFzaH4t1*Ke󣳛E!KD t2Z坋Vrϓg_R w4d S0+ c̖yC)vO@w7=ϗRlGpkV~1L39R)5,mm#7 '9iP%w1@-c{om VMZR}d~A3}03ھx1¶iv |#~ D4m(+D^1K&aYTsͺg?NmOtmC,_BPw@POgo$6'ٺ+ |U[q*R0a,:i-4_qgT6G,C C+ })9g 7Z63g:;)hDM,Dߴ8.u2m1ί*lۂbV2%ӌ M$H#`i&sOd4vCnICg$qi,h3*mf3pzClFέoĪۂBE~RVLlFI?Eaf}q*ܩ<=cp@ 8*xOXP46xEc]jEA2f\AwD!*@E1("KDbg@E$@dd$U]|Zr)ja1*]qd]U2"o|~N0+Y֯P6ۚɥ^U۳y<&*SZIhɌX߂ }v@S楍̿cbpcO)B1acPrQ:(QsP0IvtENxޟP<$s\'Y>n GSk26g(5'|(QQh _ 5, M`ύVx7Ӷr=:r]:ԠM&v|8UT WC0E!r3c34j6TnCpjː2ֵ?mŇP _uM&ڐ`2c-C ث'æx}o"z3r63P~c>UPUPB  *D!&%tZjI9w7 j*D^\YİSH+c# =."wM?0 2LT|>b {ɟR949@3%`% 1v^ɉlKb֏c }*%>Wd r-m$輼22x8W2"/dblW&* 0Ig 3FM"A}w;ݭALmE7FzԵ 0/BnYv2b橉f1PҔ n?ӯO:FhJ%):&X;աCj.g!ȞphZWTǼ]RdEtԐH$Hu0O/&mX)fg ;+s!C8 f̎RQQhrM|lӆ"$C?0#IIO~ʭcuz;nĥdFT*)NfٴV, oOt*Y293OWΝK1c+GKLȼ8Vmff kfXsM ZAF}x^̒azuJ)[쭖SN0QmIohˡƒC{FP_"k:3W3 gSى14mJf_ZVt\=5#ct][i)w*+Ƹhg@JJJ rٱ7˼ >h2w-0~ן `L=u[@L?׷ p9m1[1#詜;1rqw/k%}  7%AN_UEBj"/a:c8bU΍~@~{a3gQ34BF1-cE*M$_k KY2 39DyMf&/ U'|Lvm@E@Z#QSeh1 MŢ?ʿ+G0*EU77IiZ)3U9vr 7lPQcXD$>*rAKxxdӞ^=ѹV7TRF ' ؕܠҙ9r&.MlQcIP4sXG 6'tB_xC= &pD@"F:EWĭ&v?ltf@\s zzOA3"]Զm*զGpWM`~4iЧf*$?9*QEs iJo~7dfOq^=gI[H-; 1Veha쏞kqGlUA@9j7񤫙a_+AahaPsjEKz`mjai\kzPBАVaL}u!2<'q-89}* K eb0"o] %+SýJsy ݗ:j(ƌĉȪĢAM[w{sH^۹CM!&pvoNܫf!5<qn2՝]P%ӑvhQ4Y7ޟik#%p!e! %ZuDIǧi|PQ'8o+u𮮼 "9e2[Ox{t` W2/g_zyB ){L38Qڏ"d78~&SuR -kp8>y;d˘Mh'Xnlcοg˃ve1eÒ3ٳ5Jdq$@ҥgy;3\h.e]2:ӗz~Qo5keJ? Z%ի\0@bU3"CkGRot'jӧy\xUU|-C, SI#0vLZ|;gYX$~?5ńb?tM$T_1LQRC 55 ) 5̤Gi 렦)eя dU* diP{=a-ċ^Ì54𣎥 bB,r@8]eSy"qOȳ2!ޡk|FZ kgsqd'NiQVƸGϠ5zlAS8>q R0Kiwy=buAD><`鳂MѴnm^ËNQ;cI#`RfVZF\WcamS**W孿x$ųx1Ƭ#:q!4/u݉~@W6D'Fa"Sk_ZV[*OZ3~"sQFG~>1o_!4r͸V&JӦ} &T+$/n&og #x/Y}S+b _(2$ƦD{X*T|0Qs x"gd*U&^yC9.^ȥ+?h}E %#aV#hL^Kl,ed 3 zFbNЦ(Y?=ۡ?-&Bv-X&_ eIH,/axuw ]~Xxwtэ  T>DĴT~SK}dC<?H͍vurWՁTT /Q$%$blCXˌIn26zFĤ܆k\P$htl3y+2x EP#-OSO_D1U#&<ƆMCpRV”!Qx N#o/Ά2$蠫- UQ(+kcWj-zL^ǔ2N7V&899Q!|QJGo`šO&sHR z 1X0v AKi4gAo>)$ 5Ƣn\L7"*-uVe 5We9P\ bJ)衯/ 4cX[HۏA}i&)ɹҽqE.D]c9uVIH0(S.%QF?؟fiDPl7O!1sgFQL&A흥S2$JDMG|l0*>o#"2ַ/HG -͂d[è΢Y"0~Il2? 2 +>')Ejt3&!.^8ϡ$L@#uCga0n(A0"x@2j! &Ԩ_ z dܚ׉+ { C|8^jm5܏gRk&rصhh#$S|vj@VJ xq;qXqʎ>袧-kr!ea3&↾Zz`Kzia-f'l݆i/~dˮI6\Ô}Yҕ7I(IVGO|seH@Pɣ 67S +9s<3y:)sz|B#5 RɓljT2談X"D3yrR2?֩,_L:pϔ|QGTÙ>DnӴk Oō3ݯ$y**EA );l/SE3S~n,:S|fA9W1 k:amNsd3VP{:(-sNQܡ fyBR[W{ڽUHz"2[IH7`zpz)+_uUF2&VG14hS19+֪-匥<~y ELdþ4PqR(嚳KLŷ|G"#O#eoXq)ȋ/7K"Bvz$_zp.z<`j BY_,O60𢖳-fZY_Э U5!ln|A[/q1:k- ,Wq\;Af/5VEh+\z%gL՞SS2+wrOgʐ3Sc~ՙNg9s_j]m* xI0y=Qe=f-Ce \(6%Qٗ:J E R3u Pѕz[Y{EB.5{:N_n{+*غ1i%y.s^.e_'kRקxִlQr#SV1nm4|,[36EdNs6!sZQuNS5<ykkѲ9KI?EY9a?dU'4$eւe=ٓ S[O eY$ _RSƥar1`@ YH3qwNUTYL%$)Ʈ0: CUY*{t ,r/5,}ʹpKr I3{货|m۷˭bzz0 G#BT("" +P"bg@E$@dd$U]=Cr)ja1*]q eDa .`WZdyuO.MwLoRWEyS٨U;ϰ*.{ APQT(Ŋ%kbԨ5{`Ec{W({|?l 51~sD;;{f9gz!>deI?r8 +Tw88gȪ32ȊcRE?hD'CG]p5"IPT.g SJt|2tdAq:>z>$ȴ%K'6?W@cDT-wqtd|1 `fœdȦqplJCeE@ݖ6b㣐j0hǶbL8SQN0+ݍ-O2^,>CZb?zM!СC:tW[F2@#=t1vhΘi~ Yw?q:t|DaXɕvn b 50O0{MٛR0u»aϠ9Sr^egdqz|̉f2" 2TV]xΎLa̖b&l4FdӻV3kK;ʰ06r*tZBWT[O^v`Sr>W  1h~G*n%<ИETYO42籤GQh_9uC̻5\!(3.Frif3݁)gV Z&23HȶMRǧU4+oHnh3}&a{2>4݈#OVߠnءߎ5'w34oHYr 0ﻁL@Y~ANә;գQ>32f^ UTJe_\-%d.Vywܱdߞ毹Ϋ>O6AUբYM\w1哆猵zu-Gxvv 'UHDci b޷,ScYgl-YQY58]Ouwr4Why)Q5Ǐct g iR$eJ¨`)|WfˤpzC3Tm1;p2?2o|۠.4mOf:2V6mTZ _qb#i'<45^5nXuuaHB7upڎ{el%\aOklg.,ԜtCgΤ37\z y*@si bD؞ bݬX(Ш)ޱx(G=uȖ/#D2_hn5_gH*UW9zkRuv}/jQgUP_Zj* 1X-(;V*O=y@>ɟmES|Ir!?S0NwbƦ2o-ϳ dŋ\)Q[HYZ"B.qe >ŒqHD" `hd).sl2j֦VF2 5KLKrRJJ#}HLH)&:EM0Ti +k2scc3cҏ-5GydPķ:|$jfb̽*mcd}kK$//H'8#3ڲuenTEb=@DQǗpHMSsT{$tbd IrPN^RcF7 x)#x^/D>Hdkl((BFdzO9 jE 5Jmɠ;pz*eM=dzdŧjO2sʹ_FP(r-:(_Ah; r`]լy1$S(Y:>2Ȯ!cLL$$ G&둷DB2$#SLŦrt]ř0EU l0uJa 9x0??°.}cMPbYUkՈ`Ք5d 1$ Y͙S7ڹjP )Pxf1N1q[E'pBBv0ۇwI$ CCrVk]e(&"c6ځkG%i/^}T >ν&r}!1X5AhMr2LΌSr9M!X:rƿR+G$}cx >"̓KX QO/J@ځ{}6Tp>KgP`_2'F_GR՜g V\ 4 $B0>9L >cgS|J!=k752GZnhSo KNsWzR2' aYClcoEQ]ϤS1+֏N+xB2~3tcVՈ06Ye7ܛE51 l܋=,~Q0u*H'AAʸ0-+f3jxMgNASܤTu˙4R퉬 *_;=<3kK}Wo2}^%EK@/xçfjG> K~ʼV4VQGֳ'M="Ns e˻늯$r$mhU}2=#,:Sb+fC=E,ɧ,SMs~|yTٟ#֯rȈ5!Yh ՍXmDz~-s)qwʇ"Nf+W|>KK>X\¥wPͨ 0!#AG]p5"IPH~֐>:D'C|e(z3oS[ f-ѷCXF<;7_Ɉ1 Ue2w|T/ ɂ:1t}4ܥ:H& [yObQX8RV %5r ۋb%WrCDsd[: CM26fH]f/^1YŰ3 KoVFss.zн\xPƗqYp[skw_*O۽YU(˪YRNu@ı/g]q;3gmCy U8 y>u+DڟTDu+LBecIjv-4X3Is34gmwUJR/+.KKSkC}Qr.gbI;%Qed M)X*-f߽w5 'WϨ@e6nZ<)G'ƻ)F37SouhT|'Z.%nv&cT&9 xqqB֎u24zYlik/{'ѹ* , R0 b6r,J2mdo#n.5ə:#案+"+ߕ~]T)O#Zo#3ޝ+V!,:n&U]UR*93)4Z(]qdL3 s>+ A}yŭB3~57DcꝬ^9-VfBZ/iF^C2 s{ʨ5afL⊋%s\c&H%dt#F*6kLeb[ ,+') ׏,cԸ c!i܆A7FzIJu@C3"4<\ׅjVtzC<QvxVl+h[Xu:޼ &L^/ o_Y/"cm w[cNGdO09ܯ֛i\Pach14jfmTpiG&-aVm\eyqOYo՗{?qפ+fG@Խ\3>YbCzԢA!sjZRc#T`خP"6{r/Ķ5)aȝ>/gub}˞B8v#%0es [[}Dj~蹜uqlM N@֕iۑ,]>Hs/o^EefOhLʯrΧ`\BT杤 +[UzW.ZxC8 ·o䨪+j庿Swv&_]=DB2@C :mx. .JX9*qihi~$[J '`yʤ l&_Q^4?i1L< |o:rdV _ԦIh5&5G%4M8#% qnLW g H"oM FK[ըOp u\xOYp9ʌdp~5 i>'saRktj~.K;?]c\oY[Fը۬RW#fr%wy_Oª_ʃ\LAinOwڻNz෥ !PԘz @Uww 4Q$)q܎F1|2 4jlTQEm-1 ӗWs~:ׄ9Kݣ!xI@t.A|ljV_FRȯ̅k0xsdgbhL)ϸ6P*n ><ƱJ|jV)wFBI9G޶ wyBQ{zw!khpI?CVrsҞRDDxB8fO!Cє(}rw|Ԫ7i7[=z_fM@Y.^;$69rx;ӛ[F`K/ Q~/4~E{x(P1h +5ƫjAƱ̺]cOzJDԇFH"!H&8E\Ģp[ ه},;'8nw"a[gu\^M{,*Re2ܻAYY&.rEi2r[o ݘFK* Kf_9yj u-bM}AĵCi")FEߚpαnM 4 \ki^ m> |d@>L>Bx gܾIH%9%woe oJҸqA}̜( V8WLRS@a}teJ< 37[4ܼzq)ʺe|q*S u\W#sKz`J^l {'ܵ%ρ XkH}(j{/΁4f߯m׊f݊@ZծBZ u0±xs@N-Vrf/X? xS/j@,h,.\A{pnd+O܊I :RDb"HKx88u5jF3 E|%h{qa=VQxtsξڕ〣C2IxuBbޟNb)5zjԭQlƔdhk1<<

CXXCwDZVү|o]Qd*!q|~7ܞ,eɢTjO-v&oeF}oY$FQ62&Sfm~labm!SPda-ͱ.ܑ ϴ$'F~ahcfXu SV2a}bwMf5~Wc3sLyFtLzDQ^(Q$ IDAT%z6,1ݖ̽*mcd}kEʹIYn;m?0zHL 7U?==y9԰qtm- y4iI$g%!Fi XE1Qz d0l!iwHp|F87y( ;(= ZA("_I$JZňhݼ'lhcܦ. I=s)JI;/C~EkX/y{lDa"Ԝc ,jI˷d`!HE4gNDk熫VAṞgbD̋(O`n;#$ CCrh֡+3dwOO<1nDF̢[xL>Mqk@\ (дO{GGn$hcuAn%^dܲOF\xA U dS`'ūw X2 |M:P2Xɽ U sjh^8+'o'pƣO`z}3hywˇ-  Ixpٽ[ጻGyS!$ܣzh E?(^$]QuXhZt G,JM2ܰL gi304@Nų0Bo1vQ$ھ$jo5}Б3Z9 OCَ}ظِ|x #<P,wߜ vN#{Ӛ*EmQFtց$¯^^y{H>?K@$=áqˣ+!lZ stZ#Ry|(!J9<ȱ ꃲ,Vf~T4c8;Hѹҽ&~>k7U;tj}:y]x<#dNשOVL^%dj еS D8ˇOJJJXGʒ|J!=ft.M8ҪwcƷF [Yr23'%'156&m*^L]>at2Kk{ضDAӑ "&{8f3oiY_;=<3kK}Wo2}^%EK@/xçfjG> K~ʼV4Z^dX[IDYK4B8ͩ&-Pt||r$#)Y]Cq$}i{۸0(1Y3;fD&v=MY_nQ)yUݢTցb1ֶxAuٞ^Tw]Z.dj< \X-ܩd*RM[b܎H@XYAFX-ۄ)m{9O*="ݙ4\ndטrG 7UgxQ 2p]r䙛 >3Vo a٭> ג*IHfX{P,ߛ1x`V0QU"mhU}2=#,:Sb+fC<&/Ax^fxuW^)&c^ZL2Y$KjB?0$p W`Z)[sk8֌yfy(r:;w'Sdvи- 7mVp -*LUKg+WMβ>Mx)+T yHطkRM(Ti03lbOOi0ɫ62@Cu*ߏM ޺&uXV9mu:t )&IlXXg4 =Kz rwG/S nȑN_~f1=()_xܚEʷGKdqɲ&.+ڗBվnZEL Wrg FCLL,vyl<_ #)糘3ڧNruO1|&'K)TfS_^i3ϭ_\/g _xӼI/t|$+  \裶㦞_˴śr{XKgF¿Y6i3kFQ7ZkY<`<?>K;o.<ȉ39LzRH~֐}tJOIaך$qAZ­7?e@՘Dao #|i'#[|v,Z3[NL,3nC'GO#Ewфsak03OIL2 GJ dӸ~T5r ۋLe\9:tG9zΕ\CǧEѡC:tݺT_jnǘڡ9c{'fĝ b3}J<ɿ`ȶtdnmĝl:>)_bagf߬lܜK39t/5tA6e\;2>Ho uLR{'DՕs6GiAJ)豐:-Yǧ@ı/{FΖuêON<}Wjqv:@"VFtOUjCq$m b'G&PcXGzf 7֯ጥ?-|w@B4gwmwUJR/+.: |¥ኵ>vŨw9Z2d2&,Qf^j2 UɡɬskttkW #L c/'#fWWeڽ:}@/O0{MٛR0u» z3,b6r,J2mdAi`m/j¢SY9BLUedB>W\-QI]Yx# yO8=/?q].\́>¥ܽ{{s[c.ݘdr -qBJCP?fobf^qЌ_w V;IгY\Cb5m&kf5T ɐ+ 1w\ZƳp.LJFnBgt3ZЩ:9__\<*ެ{ a 4p%} 5֯;h*y=18)>_ RL i7ТZ O6Gܺpe(us8V~ىq[UƁ.yH6XWB4gjϛO2Z&O4ޚƠ+gA桌,^ nОL/f`ڛGg{Mރ{`U/:-ǰ-CS|ŋy2fw!;cN4^`*_l3_&b^6ojfmTpiGaFy D;5S)C7ehE% j]-&w:\!n][oع,=;FG~Ѹ~P?'^:FǜbLkeI 8AQb~P" Ŗªv!I=$/e\y5iĝ~BOX@pUDg^AB_+4yMBOY%ti`W*(Nm,1@DiP_. SB(GbӝW"F{Coƺ QEFtܖ|sE!3\&HJLJDu }sGsy9 BR;YqX"#ORW_µ\ <93N1XT*z%xZDmˬ<)DDi4΅"Hu~ҌJ5:'R00Nr\[e~>_:}2ESgj Nش[yARVn5gx"RJL(pLo*c[oy |d@>L>Bx gܾIH%9%wA\ޔTq-ԀYX`]>WvAt,e"8: dWY'dq-vjcv@ML!+WnYzݬ<9r@,MS?LJ2\cxpcW2W+I((LsFN CNiװN!6/{UBi䉃ΫAYgOCj5R4K>/l.D#̰0í@d̜k>د2fWd \Xx!Of,@v~`%"D042J̩Y9,F2 5KLKrRJPn>$&$#ryRMo9\RglCNl0$ {wU]:U7:;E㈏.nL3$1xS#z3ܓN _I2iy:`nJcϑDrV""bd :E1Qz d0l!iwHp|F87y* ;(= ]A("_L(k#ytw# rr.8IΥ(q.ZeL]\ qcb"!Y6`w82y0?_ !b"O%.6 $#ؙ""8Ad}&R%f驔6!B9yIQk$+=A:E۩l8p남fMkHLCOd!r~d\7)ug8kU-4`烟..ԩ*-úL 4桴t?)A20Đdr4$D4gNDk熫RVARBrN1q[E'pBBv0ۇwI$ CCrhJ'OF[5"^?oQnz[JBOPx6Eq-et{GGn$hcuAII! eQ쑀80| Rٽ"װ cA\lZ~y={=ȾSh R@U&U$޼-2 >O{5"c6ځkUጻGy00Āb;\ #H׳˔VB{Ju2kGqH3Ȼ۳gMfw:JRg`L&XIx}Syr+FbF'aTKnKbU2n4ʷ2cL"n`UW께"}Jrs~]摌Q"큚z93-' 4g(&afU^)c]J1=!WqY-;A{iJ˴7~&,Am!٬5l%ONhafΒ3A2=, x;f;#{z;d^ưkj2 M*ib 6eQZxYf2A,gǫ~1fKѴ\21S/M@nϢc٨YE5[e]oBDmC̬]ɫ~ IDATSޣryTFCH[ l܏X|~Ll΍(s.)Pm4!ob1 =S=ڱya.'J6 d׈䗉D<K7e9ukZhV41oeLʌkoGt^6n ]1L!h1`FFܿ(RMA\9ͧ0D;oHd}ܨ7CWOeiWJB]c WB48j\V8 _ޛ'."U~o+]@JC;!Fyaڅn}/W|<{^ӥ5MgDq{*ݦR`54EՅ:LZu4u?p }*U߇ˣ(鵄R!Uf'K:z8TloN3Rd-sՅe]19c!{ײ'C嗌 :CXr am;Nq.XnʘP6NPT7a~ OQ+ҝѰDZrP>eDvBgX2l)Qg]\ٛ9/jK2JrYk!kuIfSw`*F_+ʆ 8 ]J h'#1 Uٴ٦UgoN?E0w|0C&?BCIހ foP3xl lm*g^7ZJ''芜e4ZF?و:0ڐa]7uSz^j c ߔ(圷7SoF&gcƎ| dHH.B2L$ċ 1 *2c<3F=1Lk=?(?  {$Xc8r׳!kTKξH*O3 9Ʒ_UCqwXւ֠xt´p_Hu1p]kBogdlTӛCgzz޽4p&IAe p R36[y˓HGپYۋ緮g_U~g~] STD?؟SN__$cyMt1)@΋Pu ǬW}9}(lm&7 b-lV{S4u)Pʡ>150>%bF689P\fV ik*u70d_GV217# ܿ{dAYj#g2rEДdr4tLqHA+ZQ=Mu sTw}(3O OEkfL 73s$QZOɳ ȥL&C%:0z- vp, GD2cpޯ_S~g~]_eԙJ@h<7C: hU1ѯO/ )Ar\hT|*E ީʤuվ͊4KbId˝W$9wğX_>%C6uHY@Ɠ Π6q۲H!0(i#(Fs p̍0Ogj1Zط^ q\Zi4j\euuWLM{oX_3Mh{ѽsfπ}BVA!d >Q?Æҹ90n#*"\̓YڥB|4.d6;p ŪSSkCJtD:}5Cw0nsСPeG^|dN"t|.;wd$9&hEf2#qnr.IЊ)NQ@^+BiH<. kyr)dr \W ɜ֎!-r>f],ܧ9t1)2_`ᔩ!yC{<˔% Zw|Fd@E";Zrbx K Qq"h (.c8[ΨVR,2ݧ|XMzyP6 h˴]g̟Zܟˤm<.Sko0ޢkTH:UnoǮҸ§]75K~ KVDyZz!?%{.4^-Jv$pMR93r[zy,j tEjΝ80TANjEa9!dS~#t8_S'ӿwC&GDeU8$WҥaصYȊsq-Aė?xQOAtKFQ>cUsǹXin)6m{K|ZfS鲔 ұn7C83 =wf|< >:0a!nD$nd7ϧ XV<RDZ~ yk)fR Q *,̧JĜ9 }!!qt\wsvd_3O(/PNNx W\`u;;0wkƠzc*bi'tȬKNPY ʥ6,G&˚eŵ[.>%N2II"OA\7Sk (1ʑ m9IINY 5dXd:ז.*!KU*]}f'q>,{VWr XY2Bٿ?̠Ue<ˤ#OYw9*u2Kz;R|[P3nF)DܼO4"III_!ga5Vܾ8$c\/<r\4y;vЦE֋E* *C.Ÿ܅qy/Liۡ#-9b3V? EUcVJǏƵGmd,+#3P0]S\vq`j;Ws/%{m5tS 6/<nM"#ط䪋|BֻcllRD*Od2Jccli2 +{=z 6M[RyIOZ2K֦UthV [%ϏZ~ZNK"2{?j|,Vy mM0Dd{nrHy?N9=@UPDvXX%#5na\JmG.U]&y:~']<&i§J[=R^5ƭ0B]CJ8RnJ2!8uGݤULZS{ˋ{U@^Ro$^H'?4&E(X>zzsk -tb*ebA N_F,K<:Sxhw# zLBLJ$)@J|w þ@ .Bptrl\q D -G@)*~:KNlK =sKen;V3E#Ozn[!h?}<%ϟw~us÷sV%J=A!&ē0;CyI|MY$ZG@R18] M"o~K<:o1vE6,ty#=R;kKD"Pឪq>T* y}ׇ>9Y@N[day%?7*CqtΏ8_O h/[k縔ZQ;k+x[هgTB;l e逫+jae vC7+Jղ96R-nYNOaYM@IDR@rv |EA!۝BR'˰rr$e7GN'k:G[G16 EݓQnZUDr -i_L~g~] t8l]cTޝcgPMFܸ[wdNj7ad1w8qw D?,9 &_F޹WF 뗵X(qA=]y WiؚZ}(+a=7\C /I8H)SV)_pk\< 2w0"cy"i7i~m?z0(C N]NA _׏QXnTӃ 46;J*Tߏn0TЍ܋fEplb۸aC=3̴S8y9˶ZeN?ЧD)&]2)22'{;3l ^ʔsL+ȫ[>eC |;ʮ*=q׻>M6Q[-y$gGQC]#8B\]YA3&nJVtq]Mߓ*\Y (~)VŌ,F7WO Z Ū\ )d,ݴ8a2T \u)$Oߙ7j8r I㘷n(m&FFi.;J׬JCH_j[ؓV3s-3{a`nFlVLC\)^)2} D^!jXndpLSro% _xrQlB'Śo03 gɌ D<NE($]#:_UdŽhAꋈK&XCu0ukZhV41oeLʌkoGt^6n ]1=GÍ75lFh 2Tb X7¬:!U+s ]=_}* t-(^  X` 6w_QsYM%y/#&PiwyU;'Pp/Etu ե(!ya\ĬSwy jƶ:{m'mJ?ڍ* dxH>؍<̏׬2JW˯86Zkbf[r9 ЮzhãPv<c5E0e^d)XM L -I9arzMS[ȕi> (K,k u7wg]/R1WB t̗dk`aNփY|"*(f!UfϼF;3kA&GC4K1̛gЊie 2 ;]"7ҫ-Iiy[\X|6o-UӀ3+9Τa0> {ˣc#-uyҟMs6T H8ɴrwfr@?L:&m,'NO0{S*Iˢ#*"\-Y1Oy@/۪La ʣS+ mW6,Ӵ'b;4 %Q*2$x"^`d&)(x\A_Іgx2cQ My0KB̝ІrpPdTpcj}j ~Hv09[Ϡf>5[̤#Ԣ9ˇ̼>s{Jo/'T1OEbvf 斶W>yYe}Q)rxxy[Mbvѳl~.^xyiJ`vך&CSU ]Pl9~MΗZӹ} JrP?#\!fZG-J7t0rQvxNL <]92/MOU$p\"bLd JWH]tY8Ti$f\F'*:c9aDe vm-mD<@MC=}I*yT`(RaI8Y>K( -N%֦ͫĘ]m񺲎ɻڑm8LO; t#C^:uĭxV S;3VWK/eAU5  g>t_q S4ftL{0}v9x6292eU^N4SG IDATϨ (u>Y@~GKNLϞA-~rY6B< "NͷF@7_qJJ~Zaf!+5c?%^ cS{P0}NըDl‡}-'N B1f]tjx[HŲi6"DjZP&{1v: ,`Kdoo6TO) dk2jʎ3Jkf_xċd-\y,TT킗Q+1&b*qγn&x]RU]P&sX|4Jp sJݶ)czxtl kn+"Ԏ3ol98yԡ Bd8J NnoZx3"1noBבZ^Tᨿ3J EfW[!'Ρ(Zˉ]:^xsWqZ}('2sl]@3K;v^df4 :ϕ+WrWAB0cA`}!1Df֚rC~8gp!1k5 FOAci {D"7c2On!/ I"OPAof*%Rz$*TV )qN<ɜ֟ ٷ$*-*'$P_s]W8*jr[{Nb?9{-O0y?B^e9I%1I9O>@TT {B:fxZ*1GR@t;r7 <ܱR.ƝT(,Ud)ҋ0q#k铕LZH`*&_B 'R<KU6XX#F<'b52_z]f7&mhOj3S* ُ>thƟbye$ƣ/fY0YՋzRL3S7J?0a. pv$b^|U5m я.'OH*?N|V,8*PzEuó+n^D1.h S9q.Z V;hS"H"|IX&-q(M _ä'-Ŕ%kӪm:4 ` cixgJɰ s&XɏL.o?El]pXXEzu2JTjteK(u@\K9}d; qk`OBʻY8,/^ҹ[7ƚkhR#"eS1FY?1193{Gl;ӿZh :=pVӬ?;ۡ:!'tr:zZd<.'[lNS3\u@GW,ϿΧ5ż2)k#a %o2] tt!)wG2!8uGݤULZS{ˋ{U@^2[D*F.3Ǿt[6c?SQ<Oo0WcpreyIOyPVOU%i$(@['gɣ{ՠDܘNoH|I(5DеƱ ..E(G1W#Ksݲ&vθPU"(ײt<8s0YA TDb_ƂhAR7/F-ķo\Ao>Ħyt-!y8 FE"XXUhZ^S7J?^,DuQTkO喻p5 e]AGtKNB*<4*ԲPEVtVƃ8~M??bbhGN9N]ME o%gKLK1$7Eܽxv4 y.v|-9yu/$\a5MO՘ZA;Ɠ.uiM<1=>wxQҮ)Q|( )66c86vmh} %6xtzvEՈ,=@tr!e逫+Ū`eP#ĵ~vj\(^:ne==GF42*f *UBuT]:yyɗ 򨩣ˌ98!3QQbs%y6~#UB1S$/6%7QQMLcI,S;̳K9.'sn4i]ңE^!x7^jM z7DǮ܆0IqWVHHq\B ^? tX)ҡ|0V=ϔad낭vŽ#vs ]n]Y$Y71< $?]WXն"-Meb4ƈfExrYR,O" fam,bsK܋Bs)^Yh=ʽ kDA۸`Ո!b ?]t!6U&22'{;3Ѝ)jV W2}}tEsLu~\MIT[MpER S/m: ȣf7! kiGlV!/ɤM:ׇ{yaP%yV~a"shXa:\\p-Kϕ(mB fky QdvLE{Dm ٯ*;u IM䇣sx:%mi6qއ+bғh dӺd ъ8}a8&%'h#jbZG1.$8 gjQ{E[%d^ưkj郠OWu{vE(Xr|0 U?KhyQP.\N4AC?gfY~l=w:  1v'.^;Z'`\k`{M!PKښ"42.ƹ RlSfe{a;m{@36nz1Q9-F<`#W5|o Iqwx֎}̋$5 s9QI Ft& Я¤%Ȼ W9$昭v0Dߗɷ&!!!!!!c lF9BU\qLN={eYy}e#g'<0NXjˀ tŽzI_>6XӯÏJU$$$$$l$1rd2XػS`#c^*`~zf+Z7q+!c<5:Eڵ#<{)1y4qrUEקR\5 \]<8܊hM^~ű1B]C3T˙Uܘˠ%4zfCSKZ? C5W貄/6L ɹ;/J< b3>)9~06c`MNMlw^|q<L@VnYA-&o+)w#bb4:T#udWzbݴvĘmC-[W3+.۔D\vH!+n.`62ĜɴZxNB0z϶؝GЙsY,*Uޟym97%p.^xyo:YZ: +]=,3Y>.e([~M:j9O//a5_^;ȑ#O\ưvG-tʬ1 P1=ǥ^]?ؔρ9̦Չ_;U3ProU/6Z=LaGؙ颡]Z:"1 {VJ-] tcs'?axcOl=B&6)`]Mj.-T{$$$$$$c"R*`v^^8Dadl*UI H/0MZx3"1nSݶ)czxtl kn*uV,j"@Or* ϒ#63gƳQZe+J$4(}W{f47"txC:xfH݇pՌQ'2YSR>`q*[Jٱq*4wͧ3o]YN~=lg L8.ݨNiV IHHHHH|9'Xq&OuϘ|@T :Y9E8#"%1ǿ<ܱR.ƝTdu2 Zt킛}?UL *6Էh(w7gUrtj[7 C'*VC HAFߪ!z_1ӏ-.ʏ',mI_RNbnuo\ؚ@Dqn%/L;L2@Tdk2mG ȗ3Z ߈hY8,/^ҹ[7ƚkhR#"Ӽi?[y ձ7Ї9#NϗѳrOEȅO*rŋ/QM<4_gT=yq(EJT1J@Iˏf۰7I*Pv%`~dJ]JxxÅӗR5禄$ē mVIVIHHHH&&8u5='s2ab猋 E{Qa/ax-KǁkKx{爂H k,<8s0YA Ѵ½nn:ϥ~X܉>>1mtxCB%Z[ ( rNyM*KpS}" ף] Sģt3X (Q" ͍<8aOILԵ88:Gz/>gĤ#T\BBBB")1&bN>]E/spm0K}fyg0Kd]>Fh_ϒ0W U`e|WfOM,s0 CPG|ΞT ĪN~8]'YnYz^x<)~*~z^ԭi[ۻ7Z/4S?qS FhGK91h2Z_ܭ?C7bx"!f.EKugT `uP~^2L!_zL3iJY 1L^;i CU@)*"R (yDLj KHHHHQZBK̪r ciޘL)XXW9[߽>QQ~bL -+JBBR]8l Q+ҝѰDZΗY1Y>F27k3wl ViNQ"!!!!mHIv";>!ɞmeM.ǾO0BNY<4ZjoW6rc0fSoٽ2r,;6O :2 Tq)V1Cl%iݺODD[!;> o/vPMA ]A* EE"JGiJ&Uz/ IHH ׷/%K}%ճGH!&b)usv֜=-d?s&|ǿf< kqՅƌ !l4n2'olǚgl;)+-DykE3`?Bg$ }KhNá ^krXv_3sh=<(U57ƯlTm&p}ǴS'|?n>5~Nñ`aJTnD18٥Zʼ8۹bu2%^ h\bqp,oM3|>>xbPs? i˔ ox~#=ɼs7ΆBX3&*%Ovlc;S X&!9VUh:c'bo ODw7^hqj5U,:td7iߍhڻ[ ZLX11=8yg9J\ώQ i&2jO nAj>o5d,]ҹf|_Bʓ[9ki_Of_m<֑qxbw>jEG|*ץnjޑ~Y޷ -> ѦY[|Ewe8JQH8)ܰ)5!פI7cG{**'p0iV<0Ѵ?0AB~Me`A&?9|ۅ=B=&d> z*}~;%9 &߾~: !0UEK` oPմ]抢$Z$2eV4M+ 63gk{B!gDž<@||BeH'<=̛;7%,_LjyZJuAzL)eÑIK[gSoB<5&jdsf A/ #2IϧeZoB<=Q5Hrtd^Ƙ/"(Yi{B!fr23C|42E2BɺnkբO_'=!1í5z莚n%w?>O+8"[=3Uc'xf*ǨsNHC2sBb$[s<$_Nд$9!D0y`g";N<9B۲q}^gAaLX뙅yC !D2yQ!C!BX LBa1$0B!ŐD! _|ȇzW=+P6vn{r)g265CA g¤IHH k2)gUcRlmǘ$Rۓ#lTRlm_⇼%UZ%HV>3fyO|ȱ3)%U#jSpHj#< l,>>uHڊgm9n<P c,^eN ~:P hb3Q¤:$mųQH`/X3=tؚgI=uHڊgS1˭r|Y1Xgi= uHڊgS1I855Jwӏ>bݕGk_]_Weg4k(㎋?q,Zȓ<]ajNѻ/Qhh<<+#;֧|1O+t}&ǴAT/SwIJ6E?v[g#D{?5_~|5գZ #h%'mEMN^fv龊04,ܚe}{ *@zv}P3l2nn<;. uZR}/ 6uvXp Ս]Ԙ_ER:jiPW򏚴ފZ@H_|os3 "v`4,nfOd^)s3{L *L_Ә7~U@~MWjqȃ\V1Ѭ~b<~_vn&FHpz-Cq>>Oq׮:/*j [ Vw> )/9u9!%IڗѺjq:GnP(}:>|PbBP5w] wFIW@ݑ\KhDŶm0}FJxv߅DZQNy#f<qTʔIi+OY·V`eQat}ΛF"vyITPPQTǕ!INT kZj+H1q^76iXFbL}R)DOSpptAA314g!{]Vm:WIdܮxzs<иP1f:碎yw(cS"y&#m3C[a[9KeFKC'@çg` Q)=zL^KR* ӫĎ ӊX ,C~*T);NpɟeNe.ÏUїëj5Vtj朸 PQU@Ï׺gJQlߊů^*SD] oS8Ch^ü&m&'m3~2%f Q%ŧ eJ#qzA|ro^wI&epI{*x&c`_ C:|kV* q]ˊ߼.g-]t>3wڞ"D^?M_7:':]rj-Ֆ~6`Оd Bٻ`߅fCCC͛4؏14YOBqۇw̘ӟQB/q2:W&[B:MVd mţ來9cC O"l:`\~-1|:`)f܎ZmƵNKY]m',RJUMǗveVdAJjŔ]>e}e~(k%oΟw~\CO?e|CRt7V-ƠZBB S陡{w׻aodi.UUsAt:7m$QLK9['qz axz:V<{}h+ғ)egna,Y?i+lMcbuݳ"#KONB`"ѱ_ANY}^GUUE{VJӡ*zH9[7cԤY?i+l &.Μ:} *,akri\\\53MMꐵ0S9.^"""eP-^ ~e2Sr@ꐵHcLJzH9%u ,$ܻB!yD!BX LBa1$0B!ŐD!C!BX LBa1r{xH[!ij-'m@ oYimABV_A4Ew4 MU!!>>uٕ"l&'*:;#߁!GI[!ijض"lO!T!BX LBa1$0B!ŐD!C!BXl?.,²xHB:+VWgg|\8s;)g֭|\?[qa!!/^DQtԨY*:h~Lp2{9bT;&y3fB1:! xAà&l Y];=v[,Ut|^s7g+&BaCBBBАgNIB[t53z._رFo>6OIyG7b}c9U1 l8(?u^7^m<'$0Bc{LH~ϞL1EcQydcUl?jd:6&$a(^_Ӽ?d&Bact/\\)[,]9x?,[Fre(T 1w̷}?f U`9T8^hS%ɭ if%IJ<UDl y4ԅNi׏/rpӻ4uZesD?S[ˠƜ.xV|cs8r!5Zַ& OÆ/ru2BQ~,[#~ܻ{xС3:f!W{8K~w.Iؚ3kVHG\B,!aa\uuڧЎIy *EkZ/Ȧ+Y9y\P$8b/S6|p6]@v|Y!1VB= !..;wS///!&&{w1qxzyKˑYo/3FlەdD]H855Jwӏ>bݕ[qlU󧈇Nn^Ձ.jO16I{իOalⲇg~ed/找xeԔƟ?rvs7v?M}sLn Lgglb^kՂ64'gg_Q'}e_C5f1sR3;$0BcEB 8ȈD7mFf I#a'&B!Bdeo/'88UUM@Q|W+,!""AR*}sSY}$Z\3Aat}ΛF"vyIGCT5MSH ~N2JY J*(♼1Q LT4-&2\11\%U9s_DEbs;t}qJR.>z7c9D9.J$AGٮ?x2)4˕J/'q1B OTT4W\0ehhbbIO <, GGG\BTTm8֥cDnهf \Qh4 B**xI^/,OŪe.vm??'O2e}#GGUU'#;ruEq!{ObB([ԉ8C$ 7B(Qo2o>:!j,4#8L6qL5ٙBo1oZ@ߎcZkc"6'!Oor%4 B̘9Nk!zx3e Pt;6۽y59 뢹aǼȤAc`iӆnv^ AcIB0†+oAh?^ȡ (bOӄuK3΍…!x֟DɪO@1VNnE{4aQr ">$\MbTC/sW^ ,Ik-K~Nف?2NG=HGi{i?En(]Q._Ó%=&Bak4&|</O/F8˧|_ƿ4'N$ӴK|y3{ک ޙo iȨœhgDǗiԜnKnWO%2HoO{[@#~m9j@ӑЫIF]0e%'] =h{4]_'.LTrw1Upw.[MQƁ͇T,BYO`V&ƷCj >ݝOgcpe^c9U5XUЕzD}~||jVB2p\!fm"e 0Nc4yqL1I0漪j[sm;52c5tMZc^1> #"-gQSҫO$ͫ⟋8FSd3͈6'BcGs搛 g1r{\qo˔eů۽xS+rr$0BcF?բ[FW! (2H:UU]'; (N#A(鞭t! quqTPE`T6 US9}4hZ6gh*錙(٦$2k w^Ry*G!l/BCC:+VAi,rYN 3̺tjq7V: LBa1d5!BX LBa1$0B!ŐD!C!BX LBa1$0B!ŐD!C!BX LBa1UYIIENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-general-config.png000066400000000000000000001054331501654372000223250ustar00rootroot00000000000000PNG  IHDR v6sBIT|dtEXtSoftwaregnome-screenshot> IDATxwxotJhi{S"lPTd *EADADV%{ɦn:Zz e!B! d2jj:GfYZ>B!9l2h_^i̪B!"/!66)\U++sϬt:UU1C!f#1Zuzd4 GSϗ… akg@tTwK8%<6kW*\pAT,[OcB_kj4O^BHўEƩ7^*!B )[ʕAU͘LJhjh4N{3g^o!gѵLQ.jD5! VGF˪X}ni3TUB!RQەêQ_`GnY !D0SV-<< u(*퇃/UKC cduorD})x:E-W` ƁZ TS,Lػvoᛋf\l!CWW ؤ饪f\]դⅆd2>1^EGLQlJZre`zAQt&cjV(@EG=oR3"/S,=,ĬȲa["T#nhYtSݩHlc }.Iy:vDsߙ~zFL5:EkhRQFG;HDH1[g{qAv`$$ʄbe<K!f#(A|O}F!$4;o`oFH"F%v)k9]Q|jk1^|L!KCb`-E75; zm,,hqpq!aGӭNZTEkcMXBrSDG%*<޽Gpp((Jꮬ9Άšp-::Fbf@B eC;m,K\T@%dqT "i4l  #44GGGlm㛘M#QQD<|(8۠(6d<[;[h+ʊ6TM_ 2i9kþX[4V8[=1(Mԓ.XBRb 4jpvң}"B!rd`4b4Q -V^C5O` !LoE| m!sFJUgY'/"B!ByB!" t!B!D B!"$U@E!B! %iB!B yB!"HD!Bk"B!59WQc`{cDd'!BŞ] PNݗxqF0 1 rB'!B@_`¸<)xgL:S\͖(k{[ʀ{zg __o 9(5"Y۫8_]Ms_LgаHil˛ !B /aU~6&)Za;{ L18wY:QLl!BDg)ш1c2aNƻ+ >3zȤws 9rM8QctpͺU{y`wBW |{&G⬩RbS ̳!M*h w773xrV/ȷ)˅UMƦw0~Z~:V[Gӝ[ Gw\#?/;%>B!xid Vo5\uMҿI˯ ~",nT%P5Epnİ T+e6Ae;3SNԠ%v#ߒvٺ#x5gnޒrUYֈNGd,7T/yg% ٤/2o֬?)c[E5KІoΏk7aB!BK&ƀ/8q [S}.pL aAC(94id2wg5s) ‡9IU Qj4&s;7VIUR?gPˌ^j X6|'٘'/w,]Jz4j"NN ӱ"M7qN>YKB!xye8Sb%etAUR|V.r6n˩GI5a_-*k2z4^qJ6`M7ZF~ą2UUAԍ9'U3YG:_&%FEUAW+whw4&4Z JZZ!B!# P0Q@'[3u!t4X-KNvcx5Fz-6\bNS6()"/BʔݧM"OB!Bd"B!5:XB!BܡS!B!%K!Bk"B!52DG'}8y*'sםha^s% ͇gQXx8 .}`;;ٿ _ad$VVzS͛QZs,\tev_@PPrwwח͛Qnm|}KxޤU fEn ?Sי=c*nnn9nKnܸɼ_ӚtډY$1 doXG>u kk+zٓ9g:=B!DV뙿+Ts.\>Mny܄˯XW9 Y">L{LL,'Oɓ|JڽҖA:ʶtf?⯗snb6Ssmnݺ͞Qp!6nL>RR%4mH|Pr.I~.\Lm׼̝%Yh`W߀ S'OtEaťk'{LL,XRBw ِOrem c5*_~eٖ8V^CACX'n߾l֭۬X# ek}a(2/::mwp)y wK`]ssy8;tymRh4&d֊?fkɶXB!DfjaaaL<ٻ gObn/Q}+3͛)OtDh }c2(Q8 ӶMkjլ3QQQ8xobzWХS,1e ̛SUU9u4+`D.OKOY i3f2ghִ ݺvՕ%-CMv_UU| p=VZ͎]ba|0XetB!'ǀ u0R|/*!Itm<@R+Hza&LdBQ֩OFSfVVδnՂMqPOØfN'vB|KϿGr1̞;t1cHe={aμmw׾}*IӒ SnI6&&YsRF5Z4o[N,ZMR+^{>KϺ^2$ybq :%N=+Z>Fem%X(ə0iʴ;˙Gi588?k[6S'OJJi.[Q?S'OdGc8x0׮_+)_l=v9dQf  ::9sG*3/#^2 Oߞ|\RU4,kAitڅA?s^2$b 1Yk1-aAIwaϠ9 y<ɷYEyTx%$;?qmh4]~TxjU٧3`P]oK7h٢yop ښ-[0uJQam:}|I+dJnUl ԧzٔ*<aʤ L4!G4yτB'X9hK=%efJ8I>

    +++zt뚭iRU/w>qrraz*凢(ٺHk ! c@I>iuQ;Vv5xq,,\ \Ժa)ϝƄo}qvv"IۿN`e]6G8Ӑkd 7orժ;8xժd8v^YLOYɺoOSM˗/_-GquuAQC8p~hp@nZWz ֭_rEbbcT)? kC˛g|RzqgB!?Y>9{>jhȰY>Γ%=Y;LFe۷+S0OX*UEfx)p?f޳7tkk+\]\  &66.֭Z1uxxp zGIIFlXǮN2g3JPVx2[ncw~4_|XpڶI*!nnn۴lߒ>d:7ACSOW_ΧmV\NWrx,BDž 9T1.^@|^'V>ͳRV>p4oz椨|ԫ[WZ5M1-((3gwxGYJ۲'U>Ԯ𡃩]!!!,]} !/2}&uJ]>7۷[p?[␖T^򬔕5?o%Kv +%Kn6}k~I4I>{TJqyk2[!xYb4XC]9˟{ƺ$^4j؀6[&"x/mַeJpf34_/ÑH^l4ו!G˳˗-a k( \q~bc2zgg'|4 ]- ]:Ѳ4oV2vFC|3cβg7nE3I߭?=iBӨ H9p:>ɥ5=>KG ҕΜHONh_P>+Mrσ~M`M 7lssRg8n+ÇOp E<2h ) IDAT=PU8w>䩕3w }jFv짬[L&44,[ʫ^!~w-\.^"(($esBw8EQȟߝW۽myW_/IZO -vNm|#'c@.XL+x}꧟xQXmҸصǎeh#e޿puuiFNoI7oʸ yVݮ&uLN/ggJHZrb?e$˯ζ̙9Ke`8888r$%! ?bco3sz`;[[^>[ !//"==]R?.37n3$wI>ÑO9TDW_?se0S\٧.{OW(^(vvvNCfM׾]fbcmܹs~g" ݮzѧW4 -7pz윴/W rr?pŸi=GddFNK ;I |pիW`)kk+/Md{:YJnܼFeԬQ##B(,V@9Jݺu0Ŧ.}Iw#yw[={pp/^b_h'd4UTc7~Ra?qq'p<=ӝkQP!nݾm|4ůU(\ڵj;OO:ӳ&8${b2`0Ip~%bKO.ϜIjsqG6qwwˑu !/'ԭSTUUS|Myғ4,+twؿ/)[IWʘF^L&~ФqCڶmCհ'22GǟڽK/k6ٽg/c>ˤ Lg%dI7nJ3lС{vZ4 5w壓I)r$ZfIxqi ;4>ɟ_P\R+˳,M!ċO 3k2lrܼ={"::g>u%'O "qAvw_D;Ov`lu錃jBKBrhTqTUe_0ir]RE<ƆaCQR,M5Oף\Fӥ]{nիәɓҐѨ3Y)޽_S޻Ͼ`_R=JrNX7-ӧϤF\ <*s/3*AO9 *i)I?p`5i#&Maμ ?UU32̘58CUr|!a)XY2nαY*XЯO6ȲB X`|1n,al۾3ؑe8N_ϯ$K]{ LUIh(\M5oSbL]'2RVh׶M[dܻ';mZJ1O:'ѭ+'Nˮ{0 Iӭh֌.:f)~Ν8~w$.Bg\W@^f={vgUDdk\[#K1+y*;,Pƍ3XǍT'!DjAu2D^ zD>gg}|QPPNIUAEkp++i.|9ErM<==:IB6 K[/̸ XzƘ]A|J]Q:C2iǩ=9z/}_`g^9$e[dʃCZ=<1Y\,5Sqiˇ `ELbm̑.E~3rvv'M,yk@89R|53vf%ϻ[ec,5f1cc*HҔ߁_B\æ`8˷{dPL~eTxYzJ4F»XQ £k畨EҵY-Jyw94[,eA^ʍ1Z^H) yܠ/}%UJ{S/k8oJ`ڿXbXxQ7r,Fҩ J%wt}WMCy(׊G6;bw1|Ł׸IvÙ9s \&c~:Em+?m}>O`p, 63E򽑃PMFWhٓ!Q')U;xqz@|3=M ~MK#Lc=f2$ c4._8`n]9͡(INrѕ~mELcp|0v3S%x `sk~6JD<͡Nv o]ܧ/g˷?q='>}iΧs$|KHm zrplG PrpQ:g?ذl45-o3wod ][V3wDSܫte̢ٶw;k7 x@z?(,UzEyLw~w>׿_7 6_Nǯ8q8'm'NOфٴwVMlitp3 D -_a/ebdKD*km'g1V~vptK>OVhю>a̷(prɽ[Qϛbeyux̵uy?KYԛ}Kq9)IAZxfn2Qhqocr>:be\/W)W2Th؛[=Z"?1'ae?SL')Z;?G-iZ7 QHC{n_5oxB'"7iP/J1+OF$5oUiL\Z ^Uư`1Ѽ.eH r;6l ~*RX |*5yȭ-` `f{-OEvN-孆U)Y<>9?o$lĶ=hY~>)RbmgazSXq>S~51`!qeZq(J5U3S; xm@#wZvu+Mm;s'72%}5$+>Ae(Sz؄_|@|2yQ[ '^ɪ~4TxUku0?~]P|}=>c3_*wyՖJ _Kh٦=dh|n=Tzm܆7?/-Jߧ0V{z4oۉLO5\!^|ARt3#UEU',ayk WQ}z A=&?ZߴXnʏqz|WX7ii5S;qƭ/s&4!?aĸ{5`.:YcӠ@(N.}%}|s L9,Lm,f^t?&eo:X]㖍's8}e95fk>8Ey=;PUB9j6 9w&1xt ^ލBDr.{ b:8qi<0G q7gIޜw- ,̚l*K?)ߌ+Cᾝ >gL.tb+w[?9TՑY ̞ڛyRC)8FcYLxЂ1WP*~VxmMyWE|(a8W +4rn;t~>曺(7QDcSKVRŎI~ʟ'Q5KwK5>&sۼ;v{)*5٠b1:Suf~߁ BXFpUlVAIiNqyhBᅢû1AIdG1e;̤=F3e &G썱գM;nJU?0Sw_^ɿ,Xs+$ c&Ҥ7{<0JlAG>-ާNJ޼W6M>=hV!wnqrҕhL\i>7bt+/C߰pKi8NzTu|߄镄 B+b*]*EkH{T-C 8C|;=vk;5wL/ZJQtbt>16lT4PRW&  ~_~>7~m0bFv1wrW ~ P/rBɪq~jPKN0o7,fLB֯Y&=YޚSI4S \ȺA6V'S5ݑ(]e9b;жP{;.jYJ h8o%nnYpԍB WaVEӬA]XͶϧPNKMh\iv2Ap5AgǨWɧU|8\EHǮHgLGsl&+cXSVW~;gGI-\TO|m0=/{hTUs)9ф'9Ji UC P1Nzv߁,Zgp5۔]|oy|'>Ǡb6PZwYq+L6sInj* yWA5[/72=UGF 1YJTeͻMxe ~:tا*˖YS Ap|) Jj-]WMũ%C4Z+q <CèPxјy"<eZ%4.CqSO?GѺ(t\GzNslOcJq0si />O{SW\6<._j34%mi1r `8'Q^T',-Ey ŕޥ hco\ؓcݟxz-nL6}~KbTOa;e ޢf7'}χ Ӌz0HxxQe)X3-*|wشuRNۏҥ}JsWOQN+޴YSC Sf5=P1_U+ϡdt+B> 6IhX\8iMO<ѕ•i~B^XV1|/%#}*F Dw1kLNsOmIk`2ϣX,4]}r WϣwGՄJK'UhmVMFMxo\}ҋ@VmKP֓=Rv0\[:qoFqq-N|*WO#p?%I>l(h2tZEQАPAҗ;nN i:j l^3&3`8w~5`zGt?ѡׂ|sٶ}+;>vbwoǷ <&˴7?P-<%?F Qܾ{E#-_Suov34#d|1լbai8R> IDATPp1]2tlgNLф@X; m'-[ٳeW_0y X.]Gdݮ_qi/ij$n!x{IMhή]}SIc4t_ONYꯖeTyJ+Y1{>Ǹp?QAセ}頔'[5 {2dlY=o8#]žnCYk)h簛ѯcٻXw?4*RH4[f{ٶ;619=4:=G㯿پjJ`2y5]mDM+/a͚o7Pbt""R\icyAe(] e ƌ(Su0BH[l&<¾*HYq+sFe:.f Įʖ\V|S?{_quoGH.EW-J,gE[{*ժUE,BmZ5!$V!7Gp{s{3~ ΋O^&ad듿R4uә(F| "w~ّ_\+(l6ホGO>\ܵߞD2?k-kų>=g,1~H6hR0lg̱ P>[.ys'O6{Bٜ;6>l;fuxŰG ;ܾ-tf.lqcE?7ĭuq&OklJ|= 6^ rQ)|qaDX~2{Hd q>IN<-^)R(3-_'9"LM6l>a̗sd1f!Tk9fl$2JJ9EƗ3ǨA4}g<2QA?KXɼ;:ufNMK..X<ţr-Q8 ~F ks欳aqLWxd-DaiY ]wӹ l3ר/˳<+4TV]OIier"FzCʝeLn ʘ)_6!4 OGOYDަqɨwUve y.uz;IQAFTDי7'476eJ\㎰ l==m%dПsCkdҫ$1S.9MQ-`yIzwsH^#i#㚽α[";7,fTx2ï_ TZȫTYWq1sH7&d/?0i6\kM~v#z b h4XGe7x?E_Ody:z99aџ׻4fZ۩tx»M_!u"ǫVs*f4bp+C;_Rq2,>7McWg lcXA@Lo1N8лq';m1`W ]K-eHxEvF~/ 19XIWRur!/v,fsfZ[tiSߦmZˁӍD8#R?c˖Q[2(M{ξp«(y|N/ɍN0?GCx}{Z(B6W? = 7YY;|]Q:(^\N%;QчG@RƗi̿l`Y?kOy{sV\il{ w*9|? &G[N`] r^ЗՇ޳ ?UޟN9^eo|ו{_}\3z[/j5Rd~l?fj`ķW94l0ub@\-sS|\`SDFuK?`ޠΔx|.T3?n-!EڎnL{FvA4F'|2?t|!NG3sܻ̾~R*J'rbzOB-/^GҊC9&~7C'Ь\NƉW2ͰR'^ç*tJ [AiSXޝzF'y,Ft+ gFKv Do{KJ5ˢu3@baΚG.qLyg@j?Sz#ٕ״!~4~-?'wr32Qc~a[bq&c`EzmFŌc%yC3vCu3mMKAh}+1n7(;_3d f|Tl7=6\̚_]g4JD^׾_q&,<T}(kb5-\P)}qbh<" ϤPJFɹ+&=sRg8;CɯqO,2eB>vqIxe"gZW͒3WNeŤs$1!VЃ@D Ò, 5~bM 2cA㏵? U<]3eK\ &s\ h]S7*uŪ}obFtӎR~tML=X:J_ `2%?gtMӇ{ѵ5]g6ò ,ׯY^Zn.DDDDD^w`KBruEDDD|9fͧBK̢_DDDDҖ_LO.@É&*22Kt닷AAA_D`x8&Ew)""""8u~"Mh]<\\Ld_W;|Ĝ_/|S&UPvHP̬O2?Nfp#39դŇ cI=C(욋0D,^8dp7<܌w'H|ǐGI௟>C8虃>dIliͼ!xxn< [+">; g-Cܳ{ 9r=] gp. GMw[`^7('Gڀ%.X\pu5`MN9e z}M^i솫M1ɧB!\~۹ݫiR8kϣO.3;RZ&· $ḵĚ.˚.Ep2K?A/]vJ?_ EKWIY,_#3&:Z,Xđ, yg2ax.^$GZDDD$<;-K,Fg4iSW 168h(;T\W;Щ{e\θ8ZM6@ .DjFXt<ȍ 8y7eZ4F?kѸ:ƭ6yqHGppF32ynI9׍^3ʀ`S"dM I4>5^=)_ p)-Ô,e2)j-?stKgiln#"Wq,\bp߸ 1p2żF 'Bˍd)V̩eǒ[lgÍ ݱ_.g>ϡ=QwkaOoQaRKc>?0jdlj= Y1,bTX<p7r'Pn[%W!eQOȶaO72!jI3293(Z 7t+~l!J<{?)!?%$ θ8Õ- )'5~›7Á+UC|o艏?|߯.k vdp!GT`!tE>buw<JVdo"X"} [fǀ4, |Y1Iv3*CRbrCؗg2)>k3eq"㕽 :Ixt0L~`/תn6{SW2S%"""3,Y8ҠaC򠰰07}3EiР`0|+"""""֣""""""V""""""V"%s( P^^޹#Ȯ;NվzoP&Nx(IA8xw"_a7C#uypKg&C)F2Zbg0`HǎH=E Ly$33q?af jJEtXC!v*0]y '9K+1t},ȋD 7rnR&fǨ-ğ;G)10,N$զ?>g̷;@66ndƍ4 m3h:6nƍ?ylyi=U,ʄN^ҟ,U<}pt7oMuF5,ZV-I@?pBak*gù{n(c #0 R"Y2P\y*TIöfdǭӕ.p.@|(~Kvߙ2۵"oo7ȋl4å|Tm.̀%Jhh(eј,E+Jhh%JtƐSDDDeTĘ{igy/F 63uJ\~Jb6D]'X"˜Dm(]ZnaԵJDDDʞzs>|3~Ў]Ma rWjL2*]ٻ7Fo+UB45Em K>jEm| 0ǜqiӠ|I*4^%X-מI8P*ؖpˑ\uNANwT!> hW/K$ I]YsĥK]i!= WeAuyp#"wLj|[й tn̑3٤""""V f!WiLoxO;~ȆGu,DV2h3ׯHq16ɖH|D$1?p) \?H{u=qv͛wpǨ}h;nԛ73f0GJJ{e.E' 鹒_Ȣ1̪Gޛx.G'`gm;;&Nct-:-}˷ٳ'];JPmg*F/)EDDD$M>|;v:+4s<4s?i0U?Ò*qg+Û}=`D0;1f-؍Z=y28nvL¥J~ڀ%6X\pu5`sQv\_zf\MŴep Wbcw/~-]bC:+5Xp[;. {8cb=$M8۷}m%kY4!U<C6m6T$ ı=)t{@r8@őEa~NR#ӝ%#>blnWWjl,WqQ{ԘԎB6Fg\-ŌG q #I4YKL,89;a >aF-ûR3N.Xb5h|冲qB] F?ZМ%ٟDl$eUo T2mDn.5۸ڲ!g,e*+l &dQ~%{Q݃3\͂eCDE\Ʃ'ή} JFU %wvH|d#o@"1?3/n8y^yG.ӳ?K bg f xҖ )e.O̦b iݓ`-FxfrprMtH(^؎)S6f3GhUOϖB7/Y_z%"""""^)X gڐ9jk'c֨RO3(!-1Yʰ![b-rK1KzeFo +k_3=Χ;M؍:Or !no"#M޷2b?]< ^|ZqkxswvǼ~5K4kΚ1JZ=i{f̊W.BZmBO9 ɰdDDDDDRbR(δ!s"NƒQ &gBQ4{;CZcx{{T@WG JDDDD?B]DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDjlӻy '&&Ȉ.EDDDDҙ^jyL&  IRDDDD$ lXFM?>w <OB@bbbTZz!""""ibќڻ1 deX9= ~y/03HZ08xLc_~O18!G6;]EDDD$۵}vi2x"7fwb[!BL>"bf@iH_P-~3žVguOFpmz?~?ƟWH&f'TWyQL,H:0=f.&b畛үw!(jrB)T*qݷZroMm """B,X6s)2-nLuqV%`pi5EmDs~eCdx7*{=8D~ߢ[tZERpx*+֬۩yF O,XMa{n~O; kV^_սYo{*,8\>1!"""|R*KQÏ2oi)c9 WOeyWy;s#""űKG#H`"xâ눹x]g3{iyww Z v2s`>q/s& d@H9Qܓӻ4y)=Q{V9Xprr S˜x9ֹGg,q1ĦtPf1gx&.)lt/͗V_.݂J7aNV,!kᚪcvсB)%wH$11 'u_|%;wLqҥKӶm0X;X≻bq{=1?Zx 8;;ÕXbSLxkSs.oѠtnpN^ >Uq}-]7= AmȌi_ҫ4K@C,5:EȔpo#[>/q”fp=J6^|Lu_zY\>?ܰ4y#U Ϯs.ɓ|]?vU{%>/9H\֛}@Mw=DDDDE|A~}ݷq7'brswlcxbXpAÆOb ~JGͩ~Fow""""F.O>w{0iF~ < –/OEDDDD"I&Nf&ݓ g_c:w """"ƞEh4b6Nչ4Aӓݻvw"""" c0 ?WZ@䡂??刈HX//oSu.y.ADDDD#KDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDD6 ={}(R<*p=,1{սqA:}X| L<U=#K5壵gIދƩ i^\~8{g'lqP6('AT8ߢSdaI,ޝcGjPAKzOW2`>=J>ԝy *A[HQ B)wW7=T__9NX<=ɸm|1,_L=S3jtDnۯ΃-27lF]#r\̛ o"3fJ$~][eI'U*}vl؋c,Y-+Co|%?CFm{vCd8u>:\e6|{^0G} Zme-W:={."""",Ô1kkK|ڤڷnd!kZԬ TF\\iΘ[i?"3tkz2J2(?V^ƒ=(UO=LAm6+E,Yۄߞ8TeSh cf5j@͜FT먴{v%Ԥ}(?+F݁RuhզU"`H2llZc+۾Brw^o5 R.~~hkL6771@OPG\^^\1sqj :=W\c֨@F(guurx9G:=ϧK`J)aD2\ػf3\aFg7O+OH`"쏽Ζ zks7ą0s}R2g-s`$>ʲ.(kj !dVf/93਩XcRV4%ZMMc09 PKk 0Qu͜=<~{?mgNy`O6/]r+L[F~xF:?$sns?ٛͣw ǹyh ʸSOEg{䔯_%+8fGO[_O~WO$,}2n@nu3}͹cցY1L_Em#F#I3$f3w-unǭYL{lߒ,8sK3ͤ KMВͱ8yMxSΙtaX8"*W̽>O<WnyF}h_H) }IDAT7/⹯WsȠKfq;fc3|wq[v%.e!o^r Zþ,_},cu:֕[Z]wWC?u/?v46;]]__]3tI{ascot倽O2$~yٙ]9x7럶檬kyxusi37sr\[bT3n%|2W|Gkwe⌇3j^M[Y8\N!i$7Zȁrs󭇻2wg${bNIAiX#'ٝ~̩ߖ:!G?#-<,$c?(Z[qθoM='feYq3z3|}NlΓ3=IsvmY)9tG޵{vyfߜgN{mo\<<\[o-=O62pp?4 3ѝ3zhgZV-ɯXA!yM\ ~z~f,{4w-f͓I{Sfgl}@>yxNl6mZf?1qPҚ0>]ݒsϜ '϶g=`3@VgY:oz`n˖1㿒Ξ!/ 7ԕg>r?~Zw=>L`r~3Z桱<i^Λ?\AC2 s)#Ӗ^qy j\{U~/Ҙ7{;,iƴrNzv>1x?`U}ڝF#ϟV@66ɝG͗>}e Xod<wܬ'ʜF8x ޘ-+zC!t23^ʴFc%oqKnnA =`#a:PFePFePFePFePFePFePFePFePFePFePFePFePFePFePoUH(IENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-general-settings.png000066400000000000000000001123341501654372000227160ustar00rootroot00000000000000PNG  IHDRv6})sBIT|dtEXtSoftwaregnome-screenshot> IDATxwteTRjޫؐbAE *z/ DAĆ4T!m˼$9lfgyfg׸U T||}kYBmaw\ 3uػ@< ãLVM/ZŊbN|JDDDDDDDDDDnkΥ[+&& pڥvN3=7wEЧz9T+apjw%pڹq;\`D\rx'&,.svv'NLX](aKB^mfa2 HLLL'8 oŚn70VǎnN(#*Dҏ S2(ⅇ9{Nͩ[Nor>4~j!"""rlٲlv{&6QlgNj0^=Ҍ_p8np3>.ig`e:sWWJZ0T‹.%K""""r 3cx2 &2+^+[V$,̃FßF^#2J0 o~{lqnr,B5[t$^ڒ>Upq9A!4{p3cy?wS(F"~lqI7(pROR 6xq87WGep:fu `5tc4y-FoVx,"""".GQMڹ4.D$fmP?(aa'hS+qSWh4wuqŜq'eQ;JSDzL]FNk;{Ť\$V ~ *>'..Vv{NWNgҶŻ687Wxq`\+FWXײ9~01esj٢p^ l2SdHce6FJTr1aDgϡq!6JNRe 0/;}\f ymrk?> 7\?Ni yœƛnՙl%ޮ]oE&4H^MaKҋ0 [#"ٌaIfN8L]^7[aa2'vذfL׿`b5^8vӛf0ۯ10齵ml6gRpȈۚj7xŻk;ϔSaԴ1.f~)fKqy""""r21;Y'TNjVqwkDԿ7w]:ٛ/]w|zݸXT M?qr aZ'6)! ϥo_T,,ޚ6jH0 6j?R=ٍL&D9Z]նBDDDDn#\/dT]/ΠYD<޸pfx& nrK'^P&&AH\|$=*>\\;w%tfj2% xz_+o߾r87cp&i}Aq\IA^5K jc;y$1Ab9 \ x,""""^Q`{ӵ^1=I@iߤ\Z2)X0>ΜrF""""ٌ'OtR7l/b;'Na2bJzVf͚0VDD ]6~~3!J#*,YVV\cq+YDa`ҩa6_=[N8bl L6ag5ƭ% +Nc∎cw&fޞV̆hb 3^x%vH 62(xzQÌ= aLv!""""4| ˔pab6 3x*ٜ]bnĻr O3&~@ ?F{^Gb:5XDDDDnq& ^xF| W?g\mA ww|00Ս=NM ,'%"""" <=9s 11Ij#X[M P!Lԭr#; xx \- '{5^,VDx' /q*m#͞*芈H0xzzAxx8xzcqIښf'::˗1LxrdJ"7yxz.Ѯvk,E- Eql W!3 SԶBDDDDnF|WW|}\|Lq:v'La ŌՊْzkA""""rqqpaׄǚu,""""d6k:gZ]m[DDDDDDDDDD0cII7TT<T#svH N@DDDDDDDDDD EDDDDDDDDD$EDDDDDDDDD$+8s8?8=v"٦$"""""""""iq4?ԊJ`[ g_h .F=s6)ޖIDDDDDDDDD+c''޽JwO8[&Ry3~xPxiԨ 9:ͯ,AA(eu+P9L9-S!'5-X3ks^Eں_[d*@E+C?⇡W:r&qr*OhJۣt[W-eݟssMvׄOuN,"""""""""Rpe~ɏMhOZoZQd;Ɖ3x@w2pN.9[?a&4oՎ}1Q3O3"xTZիJri|y4cӳ(uvٮōyԃ7=K2=yĵIOtV`yJVE{f+>7b敇RJ%JUm].eT:M*OGu)Pbըe M[DDDDDDDDDQL8v? 55?VᗟR8O~\|hs[ m28+J'|t//gc3F-:3?M:m珟ЖU,*w 9KXh:mIk ...͎|8ϯa[d%_Luc&"ӌyh~|hW>'c\:O"""""""""RdmE ,ˠExx^py!]fnLOL@ v6U?`_sgmV8Bbm/Ͱu3X 4Q6޹F1eWz~ͺlJXX:wNrplٛhPd`cq.ohTO|y5#.[Ko5}f+Mg}Ŷ)vl MҾ5@-(ޖz_ kS^JsܩQ;/\)qiWV l> Jt,Fku)-F$;ռT8/Ƚٗ"~&B÷%wh/|M2x_SzrYяv%pk6vz ?^ʧ["^mkt%~"ѭFLapVA!‡s^KK=P' [ow,؇F>.j5~2t9 ȳ\ѓ%\>aЧ7SUHLYEJh9O}EDDDDDDDDv{LS1zį;aph8ϭ'rOa 2](.}poxn_ofͨ&&NV橏>gFOO}Q \IJBվie,l[s{yk|-2u 3rx3\>^5p@OQ 3S~_O"""""""""r1]Gxx8Gc۶~&$$|JP];wEfUIzʩk(#kvŵ3ύf}̞1Er)Vo^62Yy9^/stӧ(nna̚|DDDDDDD#z'ϛ 0`AqfsA1a[9lǟ|urccؿw%/]w#Ѣy3\]]s,ɞXlڼst:SsN>O[~TtlߞA^ݺy; _CEe/ے~69F"S<[,|}v;o0u,|{{}sZFǒO2{"""""""rsҶ" $"""OѪ0l,["UzW/>,|eϲ+}~a#Y $bbbYa#bk I;w_ba&bcr=埯L*YYOsͱX"""""""r{׎#""8y*[3-)rO[DGmˋNQz-b]f5 8@ hӺ%wѝMKtt4woeO?Ͽr2l${=L69ta0 G>ñc9::/t\+P6c&s'>>N;зσ-R?\labڶmÅ Xl9܌+>4??rrTFZU4{-ŧx=i:qfOR @DY;Gs.&Ld2Ѣy3ƽ~";vt2uLЮmL'$ /T8N.&&sX޽ȣ5 7;ĜyK8O7l[1 %N}mll̥It)o^... y{2ܐf͛;3Hoc_db>_ )Ӓftfkhh(K>=@͘:y"Mw;WZjXvſ'NKUFn۞}̙7?˅MsW011̙*AUhP^etM͏paTE⛝ϯHaClӛaC5$"""""""#Ͷd활q|%"$q!ߌG6$f dyt^EDD@1I*_?9'ݷ_}n'RE^rRf6ϫĐa# [z]:g*p>޵ S$m cYn=8I M sRzJ^iڤ1ZHqchۦ56ȡP S&M`ʤ s&"""""""4;S^;kaҬj6d>d:ؾ}Gw߉jDLpuuǝw'mLmCVnB֬iLplp).[T<{wQ9yw-fZɾOR-+\0,Gƍ(RDhhw_Ŋо2fjÇUVøXs?YJ뙈Kٮ"x:/ew774͹oNF$Cvdw g$N2-Ǿ8:۳JO9C굸V$** 3'>BCC)X0pwS-RįaŧX޽[7N@ҥ8q$ݿѡ};֬Z|&MƂfE/Æ<݂pr1~X?\Lh . ;guEѵKg*ܿ; "~"???yM-ˍו~=ۃ9ܺ}PӒ|N9N.&&՚1$eNIsynݘ3{];lqGKINj/R8rhrY-&s-AAVa (jќ&oyҸaCZlbYHHgG/f+-I*7o֌gFYhGي/"""""""RI*R>[WF}om ** ALl,3gˆaӦM2)%Eڵ8{yfL4>ŌW_Ϻ ,Z_t1Gs ;7=)<\++>"*WHg-ZʕS-w:p IDAT9{_v'==s*UTChު۶rl"͞v[l3rWbgdڶ]-zg>"߻.Jfx޾z ލ$886[e:]IkԨ201(P%g5=)] 08q$>d3 0Q{UŨ,zV"""M艝vs7$|fSt:f+~+ȹHAaMvFͱǥx cq ڶɱOnlλ7l?\bArA>oެ)fm[Z4obp\η߯MM֙տ?ϜIl`^2AA (aCüě <q9'S^+%n3tp =b\j#G KFzBB9F ɄQ7k1Io۶@f &"""""""G=oŶc,ѽ%3ڗ6 -ס};f~۳'S7~3Ecv!(r'L)';|}}:x {y쵒\b y73s:K}J/۶{w:[Bpn~ٶ=2/// y%>`9;ؼ9&wo/"""""""y|qFg'K{&`nޭWHE3YǼ0+gSzuy]|s=&3yxjլqm8p'sq*/gsܩ#[~ޚ@\\<_gѢ(Sj͋f.;e:&[ܟo I(L׮U3ivsA8O)ȹHa *OI#C/dy2'7|~3111̝=w,9%#c[D CussRQanV3i4GL&SHL&ڴf Ԩ^-Cyٳa#3ԩ]sgS^WPgF}n.xL&ujg+jkȉ'Xi]\s0{t77݇HZҼa^Mvmhۦ-uBw1l~///3 tӓڵjbXR1 ~ʄI3u0r0<ݳ}<=buv,Iܹڙ6V5...;w.;|ƍә>>XVhP>C<Nj?͹ܩ#fstn l6s߽=yzl=yJh߮- L*7o֔Y3I|!3OY&L&\]]:xmZ'=bWDDDI>$[K*x/Syn[[ĵ,7S6"| 22&7!q񋣟ɴR8w3sDGG-JfM<5Ny3L : oζ; S- 0g|l6[?0puua1e931k6"YoJok^!p<䩁9QJ(kcŋgM)z%תU3\]]}TR?X̦?qܹTDfRJҡ];uRAw(3׊'=#ݺ|J.\LSߟ;wKNͲ <ԷΏf%-wuuKN@y{cæMnHceh\wwylH,wPxq&ooo[m6Ԯe)l~zL:Gg]߰#GrbT2]:wY&Tf[UѬYS| ͛7#(rP|9MȲ+X3.^Hed+~re2i~ٳ9Hrm^\Ϝ Hⱈⱈ""""""""""u]0+az,"""""""""")Xb;)`t<IEcIEcIEcIEcIEcIEcIEcI pqH*__o*UߩHc a6[hШi~"`8 TYDDDDDDDDD1mڰh߱K~!Ǎ/Ot._Ҽ*E29y4hsVcWnqN~,""""""2kȥ0; O!B(K L0;[ar\]]4y@{4r;sc9uHY;)ʖ8Nf6 (WCR`eiѸ Nx,ǏTDDDDDD 3d̈́OzAt:1-iL=hl5yvcUn$2wR0j_ .Rc4Gc1+8 """""жBEDDDDDDDDD$38 2୽;[GDDDDDDDDD$۳m3֬d_S?߲3CrGBDnìLnn}f0sw; dxl\Uyc Ct8M x~mRx{Wu2Tu=bA{jM8oVʹI̅c\wѫu^l[+eR25Ev&y~^4=[֥b ڀwbdyQ_e#|i?kw>}?MI)lucTf@/LK[ S35 NN~5aS"ĹR$6gOrKpe(\0w#$eMc3^l _#$,GSr^3/9SI;YӞ)a9Zw,xvL-5VpO=ê~_Hп @0sP%1fB'Р$ۿ|H^'o@Mi]\dub_/]fĆq>İM'Q!z&2!'o3Ln13чǛUZ%j݈MnnͿ=׾3-aʩ<_L͊VG6$;.FLn@Y.:oFiĆq)ʃ>F#/ql̟Џuc݌\zw L=E#٦8go۴חH̐yg(֋WkWoEaC1.=0aMV~kɅ% =`쮯Yw#͆3َ߇o-X9 K2Ϳs&o*W-jm>M\8~΀nYr?ӿ\w y~-2O^=H#kT @Uݡ??q8cw1ݴnT+P)> أ55*RItT5m*GMh$IV/vY4 9|hNNĩAk_|S>W>ח6psyow:w=/I*o6uP2UvUq5.CK~| 뼸OA T]F:%UZ8UYw{yx(f-|>vDF4j؄'qf26Fdks{:ً/O``C<:#ܯ rgY1#..ދ"&qU" ?cBFhGKk-kzuB פӝh5:ӫ_"bg<}7QTpiJqKv1u'Mzxo;*x8k V[囏F!eOHI+yn7cV*TVMפ}9YNޓc.Fi_ ZSz<_L[ȡjhjʂPp~<i٥5J^dȌL=ǥ8ߓiP(uzp0?x1_|,1Ejww6v^KR#[ģUG*~5;3YaGrX0S<37eDIӥ ,S,ڛQx⹹^ѝSє+cKwOɕ"-\\5ɋ[ѧ葅9ftL,4~v:O؋7^u/ٗ/SiͲaZILi=b΄#XJX'Y67d؅)ExQYLtzv^S˙3?, {1 SX~wE\#{y313cr3ś<ƨ{9'ܠBmK0lãg3_I S_$W8k;JI_`'Ә1|$`HOx>2?bd0}GSe=^$o`Qםc\Ӷc,_=7P%a]SLwb֨X}xbNjKaS^#x`?&\SVM}\)n uOe)HE(\_M{SB;8N?O!/^ҘBOaT/fq7^y4' c-ps*Mx~Mw\n W7K݋ٶ{պ$J[\8l8ͩ<^-3s:o~q=j✓+UiB??'Ngl;tp)dhC\jyz<~t -I0k|+^] ??;w[ě8RJ%̡8Ȯ=T| _x~3V?:ASe!4EcV:ıvLz<}KִK3TgLg37"]Wy&\j).X*mY/ y5#s <|ļxC^b\,JjŋĺФ]37C93^Y]&{Rݣ>̏O18eG8rDua­R nT(:yB^MaGd;&ʴ&nOC8s \rЪE[D-d8r6eZNd|{ЂFY?G6MVLkuRZ+})yZڴhY7Mư<Xꫝ:оВn}T ۹D QZmw:M_jG.P2Oo.>Z_ V<VYϿP+(d,צoԯ?QɫPLxMv{HMYUy=5_gۥ*jѩ_K pBy|n޿ Dү)%jSQ2dl6yOEl첝n-eKtleKg,tͨ{A_EzC8z8B%cʥwVGj)Hz3iosjPQj"C[syAבGPHBÞS)Rlu}{E_I?F]G㾝֧~/Ը~N%i{n,bŔ~ڞj ٥v]OHr;H.~~cs *4jXcrUg]0hMM=`wu-ߙ_OUjV)L T_Fnb,8&)7^r{͖it/.Ӑ(ۭa)!cql٢!*^nW?픯  tzC̬ˎ+WVN?#SqMhX|BTZngٮORmɒ2s\K^5~ѻ[BT8y9aTBT\6<c2!^rWX#gVClNUyQ5 :I2ɻ^]}~͇)pW5էj;̑t̩xY%9+ 7yOMjһ;OۣUY tU$w_w]4Rs-ʚ5U^S~\-4{ ]rS! ժLϨԾ:un0UlErr9$2ŝoaB2J#Z{Ȕ/Mjt^Y('Ir1BƼYRXX->譏գlYY"/H.~*tk\9I r4+yubd}׹?]:W[`OU _'jWslvw`n]EEKO2k.UWW ^uxTd8]uR,ʚ-JW*TfrvRS?t=5}($%GOנgul+魉UӞt9U?hTmr|aU3%3i'ktpS*VdRUozNըj-iNnڨ#I<̼eSUZVE3jȩ+Q2| Ji֭v,qBg/;7 $L^>sGFo=RQ]?}OoWr$gs GN|mu{\>j,"WS7]ꏟWh؊a:u*'<*Uu.,LiޓqN5rhWcbOv-_WIrqӉ|)%93[j8X m< 4x.= !oK~7N}GWͅܟ.LZtW!QgrppО=TD)T)QM5V[¸?<-c{)]N/s|\y W)+4$~fCdRGkӋX Op#c*I*w+!0o'~L<ϳXedvW?K#Ƹ?<-cƙ__$Y)JIyQ0qx[Qa`ZhbÆAxd?)*** ѶQ*V$2edlWC0?+LWf3 c`````````ǒ?"BR%?($$$C#]#"dZVtf?zDz21j52dڸ~M1E ɖь````'?Brwc7eɚM WsiHvRkzɵ`r9CNhZ@e)_?_1y,YCyKQkofE)*#X$I6^5s9]3Uo?YCiw?׎V,<*}Gݖ֘_4w`;=W:WWDkԮCY(k0{g]Jt"4|r+[o,6^/~ruvwjJLv}\I&\iٮlvBS\j=vri2۔E^ׂkܷ G-5١[t=FV%훤޳vq jlH5&J1B#Q1Q$mظVkGm ϙUڛt Ƶ_+|0YMF5W5XMcO^׸;9oHMӉ?XCwQqD;IKW/VIRJtKZחj̫;U5Ɨֳڳ5.[Ao[iyUK4or}9P[cǎ=UCVSvčU>_[.׌S̓\~v6eͯ*)$R|Q+&:mR6jּ2Iu< y&}\ * ?hHΦkGS%LWQWu#U;EN*Vt$լ:{VmwқKwok7YuT[OVόIΒm1VYnюvAr^x~o\~,>[+(@d=8UV]hz=▃8'nj.&>啓\=_[o[_~4[K ж"CQKG볓%ʱXA_^YK?W}}2'߃ylT%߿hzd$(&]{K)4t2Prr*2&9&xYuR{ 8i_h!wMǒtQged_GNs-b}}ÍXRt&F ;яǶhvuk| #)FNS\jbuC7^ћ*(IϺTS5_ <[NGhiyn%T6Ģ#UYy/_]%D{&DI)5}*y쩶%m(2:P:}s=(ˬGmhQ+mj-TT&%2)=eL}G/wm쩓eCO\'=n"B  q<Vr=(kˌ(Q[ gzw+էgB,*Q}&Zw_C9ʮ7ܔ-SF)d|12ReпS{6%3eP->K^!I $9*쵷l}:u.I2P||ndy&9+,zO_Ln"cqWu}Bө8i {uq5F!csfmVH8"NJy \틜Ó[9RSTedJzo=!ϫMNUR99^_㺟e_Ad'<kY/}ڸxlJP> 5$Cezen)[KWW[]JQxfeo8ZSVV5Ru6FNQt9oNsf.!f2 aQt`Ϟ+[TyTk*1sz-/֑EMk Ik2pWwjxx %1vȫl=ucM[XTR&4鮶CiXA}yw_VT @FcOUNsYJ=i8t=bxȯgNJ+CWh||80-^0hԸd=mj+] Zp5jt;L _I{;;Hf>d4# ?w%dYNu_ FxZ5F=W+~=I IDAT2ƔUf~j5:/ɈVL*HRiȌ#W^CˆiұQ4(2fwH#)FNS\jb9Jl:+SP[}N~3F(&}BmGGIDQᚼȪ_Ufo>Qqs+K_|Zk?_Hc T =r嫫}omWUrjc:@R/ߠ*E?KdcܡzW*U&}5GYEX/;BRP_Vv.Zg-뭢=~̕6)騕hOJLj֙m"Rkx}3:}VΦ oKx1dQv}YuH_nO\S&>TfMFZiXrPW'kJsz1_uQ?n*%sPyZ6˽>0Oޟz.I2+gs jD]S~!-!9gsZĮuY^/Cd5'&*Y&UgzAJLHMnׯ,*xN}qBlllQڿj&~/9穢i>B͒R+!Qruw36lS>d+U.4~v&:zYw;G[Tegʴx\Qƒc`/<<\M_~%AKSF~I˖fGx Cx Cxtgv CF3>cG؞eȀ;7CZp- QDm\F1љ]/___) qG!]G;;;;;;;;;;;;;;.ULttf.G!!!ڟ!ժ3#8{ XUlf ||k2/=.ZUOf|;;;,81,YQpy˷`-6$#պJeOj1{L{b%~C4MMgUNy'^AgRͤ6vizT2(PyT~/-:|)\YjrXXx03:GCUgJ!m;SjF\C.K$ɟ5G틺YB|MJ<@Mt@//NmWK}¦s樢2gJ\uÖz3.-m(wMm@}kȄ*zT v͜w`5RIvl?Oqh UfU9IV꒶|[G[W60y(w'I*\U{5d64:Z~ 9HYMuLa}Fi54mZHUkWWi*TFCVmхVuFoΩ?M,ʫԘZ]}z_q?!SGHɫg꥖{j59\[hVTvK~JCn ʢhlܻW~ӰJrVD3~x<Yhl6{kHkʺW?d^ Crkǫa5噃bx|3gWYX&ds?R v$uQYŊ*жTWr>хEKj2.W&T,Mj^{}`E[|h ܬl-et0MZgr"uY*SϷ7{LJKkL,d4#+yV,ήr 訯/KCsFg+@n-A I){HRD~YSZ ^'ӞX|GIPg-;adӱorEG.n oЙT3] wK_JfH6^Pz-wZq:[OȚ aӹ?hsTQ 3A\.争aKk h5dru=Sfʻ}9rHSl 䩄8ms[$U+uI U#խ`+4GTvuHMe4d]hXN[i$/"J9՗jUb&tC{kZCo=.<& EMmܓ|m)BrWBOs_ԭSCn ʢSъdݫSIiX%MI*v*)8[^i!૷`G!>0"GdY{{kHkʺW?d&I!9VЀ0k̑vpm$ahlR-z2!7svuňUl!!Tu Q0G}:n-`IM?谹ZMeaV` -իϝoi-~a%yTUsXZ?Ro|}N5bZzs-M;B`K!u]\ Є5=|TQj۴o3;wD_:ZM}i/< 7ExxJf ,F&—-m?vv.?.}hGxt3 svfh_2!!!8(&::p뫐"E2?1(44$KѶ```````````````ǒ?"BqqqR%x{+$$$C#]#"dZUx.O[;5ievEDPhȄHW\\Ved=m_t9i]ExtѪxcx,IQ:1>^WbCV;iYl*aW[~Z#QӤ<:|3F@:۵Sv`xlSH?XA s5hVUt5 6|&SS11@qv~]ghl J'jr'7M^Ԕ[r-J_>$G_׫/VYHu)TIJ7]mMӖNL|}5h`'UDUUgnحcf1C BổTԦQdJ.]֚iʈzpPLY if9,}=y]Ϲ$7sA1d2$E'!$jLE3EM}̤՚BQCQ3Иg5*C<~QUB^ks~/C=w>뮙=ꟷMnxg6Ռ\w㍹O;;˯2gl"wsgWù} %au3dԆݥoyQ9ֺ,?t@ dz&mv?8[̺:'ԧwGo^Y#N:wʹwqgjҽߊi;/L3\zm,ۨ&-ܜs"utZyP^kͬQ9f.iԦ_I88;ln\nt̊ ΠfnIɣ1HߴkmZ%W3u92?{(X;ܾa^ޝ0=ڴMvCrОsΘ)/䢑7e.ӦMd;ywK?ONӟUZ9qdV9s :nJ}G_[d巿+Rme.o2ׇxwʸqw9w*4<h<X\9 f;ږ-R5)Ti*3rr~sqw~6u5)Ivezo.txӬVL8=Q׾lU%Լl=sGd v5_d:.|\|2aFLV^\͐650/fbIuImZ1/+W6BzA0ֽesfѵ^-v~zW_qYYx$9g5553lذJ%^sMWai#O)5_\gg\"J⚚ק[6?j߮]zpHR嫒<ԓоac=z䩧Ο5~n햹>Ҿ}ٳA%z1Gb P(c 1x @A< P(c 1B=_oIDATW%M|cIuuZy΃c|I_6zg'g=g&٦M=[`< 9y%I2gr cOe9|g6 L~dYzvYYY_oΓw]ҦUv욞1/|jn<2<00ܪݗMڥsQ35ՠm+o?4pX-8_},c^kȍ0fӶO=9yҸuoFNݑ7Ņ{j͎??Gi6a~̰,QrkӪevNVvH.ybrf=:᜷&Ybin\0k|[YkڵY4]V:?̾ôG۬ei]zibۥb=֮g sؿ:1Xf7瞴wpp6pܩwRʙjl<?Rt=Lޜ0l`t["mZNc>;SڷHf-d]󧹝sC?mgmӼڏrI9uv홞$3^GntLVrmO|K4(7l-T/"O!N%62W\s;eg}p̬r6:e3/N[%ɴ<|f/dͷ\#Vz)nǜ稾5:{.gb8př9b䣫k~xLF]{]8~TtH<2ݗ3gԟ_ϧwި<(Cz_)W_3?`4jH=-Wp}x!YemN:9ãyd@ӹ{ǖ!ݞ*g?4}:~O[͛?(z#k{ur͹򤽳-o|4h%w-;_-wzm}vylbN(K~v$kge2umrYcIssʘ%r_}'Y=ܺrGe^ԳI);&j+V[_̏-/^rR.8(g}f/M}'I Y&F~ Mypteͣ.Ή%#6߭QqYEFݳцޔ{ j)cƫe_dVIB疭oU4Jt^GzÜ>>>oK}3x`tkʇ'fLmTV.ZԷ!~[6CO/<1߸,{Utu^1oT 4y$ӟkvLmvm.mg?㓦V~^̌{m\35d΀EipLR3᭼=k~H egMr}RM|5$=c3 0^kt>9އJl^Y߰wڟԄ9\`vn}gCm/N֍|g5[4lwN<f}ps~}:fScr%;oǦYrXλu&ic}/eNvbckw^s|yxZ%kjX^YoyYnGeg4;Z'.ܲH${ɓw-{eRmw/5wʰI<]ӸZ s#?q ^ޟ9.o)\t؜+7y<_g3ƥf%Z2ՌuR95dcrz͠lWsʡd=oozvHeKyb*gizG ծ٢asni3sG琁ɤ$\ryJ=8Gry{Mi1bD8q4mLV ߢwUgգύΈGd'dfEUL]6L%5o o1S<6ˢIn0xZZvh:9WZޝ8m:-U:%7k~>rQυF3{<{tp^ZMkn8s*{WLffNͅL2ywcoOޘjE\]s);vm[o ;7[_ZfIä3ީj٦_u7{G.Ȱ J%^sMVT~.1+5oޑݞc`&υc~M2u˺{, /&NK9zZ{ << o x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A< P(c ?W2q\{5{F*lu~K֭[< 1x @A< P(c 1x @A< P(c 1x @A< P(c 1x @A<a1 %DIENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-positioning.png000066400000000000000000001450751501654372000220150ustar00rootroot00000000000000PNG  IHDR*8lsBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeSa 13 Mr 2021 14:13:33 CET IDATxw\׽,QQQp"[DGiB--̕[SrYijjhhiܘYj.@p(e\0}?{~~?sH61duy€ d[D"""""""""""_DNF+h̟?Opʛ`lCDDDDDDDDDDDf3qq[w\>~ĸ7on̖֑ X|wzz6&&&xf'""""""""""" qpK[?qb8p ThqLL."""""""""""Dll,QѸv̛7$$G ܒ-O7(Xɍ75BDDDDDDDDDDDR0ܸyBߘҩJժQcl.$`0`0:tpc֫H#*\lll(I!"""""""""""1x,)oid2e>,y\)8XRDdt(y)XRJ5퓈ȣxڙp1orf2S8Q!"""""""""""B&:F/7[SBmgMDc<!&:D9B>ܢ%'2L HڹD[T`f`H`NHN8<] E!7"""""xn\37_\t,)x#s;:I+$Ff|d6{# @ϐn~8!)I()!w)Q!""""80S؀J^.$ wbS|E7H!\jHJbC1/Сriz6+]!4kIm7aC\7FoH(]'hqKGvUѡIg<Eog2~6\wuH3ʴMQ$$+s\ r ImM3q96{VPBDDDDQgȇV{}=HG. wb,D7r` HlrHDUkS>[׹arjUs|$7nő×$L~M tl_>sێ8",>e,~hp/!K&|VBDDDD$٤DԶ50n.~sɾDy N7_gk۞.wX`?Ϻ}t*4_OҾXÁhۦ;oltf )2/uCf~k< 8ZA-(!"""""yٔD9{=t ϽeJPր);M)k3nu`ʉW0-36bfbSM>Drb|U-%: 5k[ O{B5)~JfŽBө+y CbM9a!<[;˿$c0$|II`0c.b] {=|PuZ \]5go""""" r>ʹ$z~~ ]2Nq.Uز?}}1)Iv2{n%ߍ#ytoM5+e%MneGv}VBDDDDgs _4w=MBqt/=8 M,iw(FU|=(h7tˍrp7`6>ODDmܾ6sfk5&--""""3]dWYwEّ?_,\ #sf׋u6P"r+vV!qF> gպTi=4$qٛZtXիkXGDSf?.l:KDDDDDr ( lw`Q9rIDDDDDDDDDD1#b<4Pi10$#wʧDJT<և;-;&)w۔CCxˎetOi'ǜ(lg&!YoJZOS?EX#c7r"-9G\D%*DDDDDDDDDDD$(Q!"""""""""""F 5JTH`֌CDDDDDDDDDDDC'*~>rĚq#բrIDDDDDDDDDDDr"""""""""""k\ U~5K~#.c}+ȅDvVƱ7 0˷q*sc%f@;\$'mVHT{Q zOX&H^BQě~4Wu[qʖ!kͨ|ޫ.)3ϦQR};VپWQ7ySǡnݍV &n\#\>$nr(\ƨ6rrVKs e`v,""""""""""O7֩GzԭR G8<EN40yE-+BɪO}*МKQn=7lFØv;ȬLq+9g>~3KYԗOΗ( /Oop0z \8O/S5 7?71٦T.Q"^e~<%otoBE,Sv?כiLdaħiZŋxQ՚=˔Qר0G\O|<&70scX:,ݰrhJ/Uҫ/]Ɖk ycD$Qa`;(E ~=EDh<'m6@jVnoq9s{1/[*_P98?t0ާ7IQ_4t s7'kWu~BvmsCOد']KHV(aiDU c-蒵EDDDDDDDDDgD(Y}نb*)tpwp85,KQ7}=v)._C.p點G9U)Km pM՚J6%>1c6m V,E^RJ{hcfR;Z䬘& 3m9Vaogd'ʦ1P2Gp +FMpˆ}J2EwrKSt.$k?[gڻSm!77*T) o24+\O1G)$S@P0|81݆b4w$:> {@n _a[n]{ҭv~ O aL_zryB&o^!""""""""""r >_~~{?(l:?NVzvB'l °\`sX8QF,j1}̻/}p'x3¯Yc,~'O=՝ӿ O lDrWLƮٔY6""""""""""")Y:?X5o1{m1 ܎GDDDDDDDDDD* *#*DDDDDDDDDDD䱥DM$"""""""""""NS?C҂.]f"""""""""""8Qc8DDDDDDDDDDD1dqd2Y3y i 5JTHx'l8DDDDDDDDDDD1"""""""""""k\cOe"##O2!CP@\a 퓖0>֭[ٻw/'O֭[K hѢjբ`V#<<ӧOw^;N ݻ/:/]… Y|9/_~`=o޼888зo_͛xDDDDDDDDD,e;wdugy}`;w$=dС :>S1֭cȐ!Z*$۷Yv-CeCLL VbРAX'$$$5.^4hWEFFe~7mĕ+W~|2~m2Ga֭DEEY=իW'%)FWd˳?#DDDDDDDDёS?eFDDSLȁryNL?__t/DƲn:y(Umۖu;wطo_};v?ܹsеk,a̛7/f3GeС={QFe(0,鯐pa}̛̙77oNqwwgɒ%椲f%KШQ#^ʪUppp_W^!O<>letO""""""""",%*O OOuɊСCFU$9p'O&>>?ƍnݺ).Ӻuk5k8q"d21c iܸqb0L]6SI"##;w.eʔk׮VJrΝ;7olٲٜ-ptԿ3guԡEV{,\0iT=zDDDDDDDDDf'*ߍAI%i>cƌ>) IɊS&/3Vaaa,[?f̘_aÆ̘1QF~/>*WE>$EݺuKdd$ח5jdv?x=M (p_BA}'磢puu{N!CrX/c YQqq5ɧ92dHF$.""{HyIɈ{GVd#G~z(S &LHJR TC l­[hڴ)uիG`` ˗/lٲ%ÉӧO}LݦMƏ=z\|m۶qi_I!^zq.P`49}4w)ӳgll6sIvEDD4jԈra0u_{&""""""""ӲH_*+/ &HxOwax߾}I;v숭mƺ=۷gݻ7S1dfqz1}$YbEK.o߾G*QaI_yb(T} (ESnnn 8x $<<<`#I\~:u(Cr6lO>d?_%LDDDDDDDD$Yz;[dugNNN,x'XCVd5ĻNcƌrBŋ9 '7o`0p͛}QD f3\v-Co׮;vonnnX tOŊo߾iӦlܸ1CL:{/l0dȐlp}aA5,,ӧOw߱d’+P .}믿fРA)׏-[RlY\]]3W_}EPPPh"ڶmƹcy9995x'1+;9x`K$^M3>5gEFF`kk$$H^_Fp:uT[nͼyJM6L0!)Iq)^}U:t_OP,0Z%9W,!q-kzWݻ7gNHwڵkӰaBCC5kzbĈY?NJRԯ_ÇS^=_G}EDDDDDDDDY,X2ǒ ִ:ܼyx"##qttpΗ/_ o[ݺuuZ*.]"883}#)Əj$;d聴d%ɑ<(U2rHʗ/Nn͚5i>ホ[ϻsv?Ú5kXtic;p@sR\9N8AM&""""""""R"]D;SdtGajM*T1L:_Xbv7˖-cݺut9EO>* TȍR+X6%I>}c| O)P@㰄h/\p6ɔ!'y(YHO\\\Ǚ/3x"""X`A$x{Y>^_~}<ƍzo~ p ŋ)8pW_}SlYxɓL0I ,>%h_I.5RJ,I:uظq#sIJ([sexicطo!!!9u. 4c R~l=DFFhѢߛ4iНÒ{cGG֨OYxu/~[sM?~~>:t;wFf2CٲeSݾyf&Nxߝɧ{lݖrqqa9a)}%Q ,ȑ\]]`)F=ݺu+KS)f޼y,[7"kϞ=-\pp0{Iɉ!Cl2͛۷/iyҼ> IDATSl_DDDDDDDD(צ~JJ3)|׬ѱcG>3Μ9äI6m+W~k=ʛoٳg;3 O<]RL͆ t| ^^^I=Oh4ҢE s/"{kOZYyfITT}e?1yxxA@@7gϞ V4EQV-݁5P4hP˖-'l2Ο?he˖.-""""""""҈bkk{cIߍ72=SNpuuO>*U ̮]=z4L3!Opp0GfIۏ9ĉ9|b3<ѣo5jEI ER^ Œđ...9>=.\ŋqvv9kOvߚ1|p&Nĉ6lAAA JlǎKiJ۶m)XU%""""""""_5*{2)礇muKywY`S < xӫVZ3~Ϯ]8p M6m۶ԩS|qm<7|9sLzM&;v`̘1L:///bI&X"87ó,l4iҤ >>>֎%g)r3WҸqc>5j(SYUV}ׯ_?ѬZgy&=ȦM J*I696ytdiDlll-qƍ Oӧ[t0q-^tSpYΞ=|$,8|qv\f 6iӨXB9rGMLtt4/_*WL/%qgggGŊ5j[RJ\t)i5k2rHI#b۶m/TV-ƍ>C5[Onܹ CDDDDDDDD$di`{,H"ر#.ݻw ٜc2R7o^T}fvɓ-wjxqttq8::2lذl,VZ1;;;,X#saҤIL49s`ooς 8tP|}٥{^y;; %,uʕǵk **iӦ1m4fΜt$gdiDEtO>KLRjԯ_Om6lj'syחzѼysܘ>}:} aL&{m4ԩΝ&222Sر}H)UG&227xnݺe>C~'J,iڴiI&I gR~}z聇 o3>W^eʕ8p;;;L```Ĕ\x"_|]taL6 oooF===pk֬W^ɓ'끋XWm#""dug/gqZ,]!xҋ!q$ѣ3._o߲ԕ+W7nk֬/Ya4~zꋉaݺu̟?cǎYc\t)CHnѩDDDDDDDDDDDS?in@DDDDDDDDDDD_JTHQBDDDDDDDDDDDr֨\cqb֌CDDDDDDDDDDDC'*<,NTX3y i1m5JTHQBDDDDDDDDDDDr"""""""""""k\D%*DDDDDDDDDDD$(Q!"""""""""""63gpMBCCs;G;....]:C(%*DDDsF#M4P ?gϞL2e(Qqyܹí[999DbG4wݭGm6)|M%)rP͚5پ}v6H~y\X8Ejܡv $ogMBCC)Pv$.\+C'*ܹd2Y3&1xyyqIl6jwQ۪ rJb;={6Cy,yyyogQb>w\n""""""c,NTܺuK_<%U& HttH6Qm9%!tYY؇5}}ԦCn=j[ANQ}P;&C DH.2G3Xw*.CJT|ms0|Qt@D,$맿܎$}y/6#y 8f!Mn#""""""+H)6odXg9H.ێ^4ʜ5l׵>hY{;p  EƗ >awymXN4fB6F?>nsrvf_I9`ϝ՚{F0'](lq.C6Y"VcςT.B<8.K/\|u!|ޣuv?2E|~0sMLR]ќ]?gnڔsr{cr~ۀr '/:cyY?Q} ^[+Ekܙ3{{]wұ~EJyۧ* =ϬWuq>,>O% N)C?Jk,os }*>*Cy~—y-VfA/eV|:r$[FԠ'EW)lJatN֪~?%'c&6x?זak#7>[%zqqKgZ~K¹q0{l56Ƶ dຫhu?ʤ_~bNˑ3L\^Tʀsk 82+ó1no!}O${=(O|hѾ!Tb/2luRfz}ɓąkЊ[my׊Aa (ӄVj`godBv9MYGۮKZ1bo_Win\;/2V=oI>{-\ٹcNJ/1= }?Atm׊6Oi,E _ੀ)^j͞e֋IwkASbxڴ{q Ó]z@3ͩ]4^ż)[kCğXBxëT|M-A߈6&Sf<Ѯ+ޘFI"OgB.WԈOf99 .L^zݻKSnE v64+G)bxѬ>[5%5މ[ί!_85:We*9vҢse-6hّg)w"\̟\sDtxq%[1חܬ.}U#WS mIM\D1/JT5L'b|/T-wqei( U?pwѺlҋFUJU uZ3q-7fr%QuӺ0 nӍ'7Dxne\@It~<Q^*fY;K]"]F)̄w>sVxW{ݱ&^*զb~\~Gnoe%xv[j+w?鞓̡l܍-E1Tiԋ7-߶>KǷfk'4`ا NũV.u&)E_d*o K&M-|{S%foBRXv0Ga4+K6 g|ɽwr7Bi7]^і'ɾ#6=GxOTw |1ˋtbDwBÚ-«D9j4=srl'zK?:CьkM=e#Ny"2dž ?3Ӯ'/֭GK6ӕu,XO}zz%6#7QXW#i2P`Vxkqh4~2oԪۀz3 (u +$Mu 6:֣Ri/yD?x-ik5+ƻX1JTdկHIT.?,#S0#q`wNnboV+W1/JUI^bهj쐈Czj @ty-=ƽX_G?Ȅp~=|l0_?r<]Ҏ[g7Ϧ$6ɂ:L4fKyWF:6A.Bt› } {>AD2E0-@Ϥ;&l`xa1 vu+sǎ;6#,}ckӮ_=rbTFs԰s?v9cǟ_LdhOKy/?Jc'x37d_r)fU'_̡1s+ܛDԯ5#X1q= gS5Ow-fʬ~,)|֦Fܫvbs%j{aEŲؘ.jPgF#cb)"ߡx1FԲ=n%l~pN3>sL>9Ңi1:hbdfR{n{INk({ZY6Gp8^0yS كPp|l,8/xAM/n_Ӟ;"懧OǞ!YVۃwOL[1t͜Wcsfs?זyz ,8Gğ~]Ҋ13W립uϽeṠR7ٻ NN}H97̸LA1 a1W,A8 d~2YG33(Y01r9Z;CNp⪑rukM=M?'طMf^m}e^d!z[Ti݃Wr$EǗ9LW 3գ[p`ߐUbɒ9=p6p1yhn\>́'#DO=i]5ѵC5JDDDDDbD9CԨKz_ome'>r/zԬl~' P Sys[<[9U]!$>?[Uag :{~T͓f!ήǗ73۩t/liI˖|eߤ!m2-LSiS o:FEnnpy)G0 p]s5xSI}2B^`E_{ M9M8vC~_4k6fގ4=uCDFJ4LN@jʖϬ-IwqٗMF TPQl*}<KP1 "<ݑϗ~׻-E&">s0 ;_fۼ44_hD?#샗 WjSu_FQ_bݙ):TwzFn ;;Xgkr%q. {HHM0Gr EhGI;Rsi={0* GnGBx/_ƨvs7*ĈUS;b_><ԅnjYyF"F _'vmw؟_jP˒o'i0DMҔFt$$i6~3c+Y(ΜDkY: )"9ͫ|KLhBa#Gq0vwkI+n3QAC7B"v;*Ou];GK/{Wj<鉿7G`&;Iw y;)P ކ0Bh~8Mi[mW/47F<_$:ՒV`6c2`HgM4ǎB <-eH~t bERy !"ݕc\:R/e+2~O (b&4l64.w0q 1;uK xzrΌ . 8;zAEEػƒcO-{/F{{M, (wT4-338mhn`ϡ%`(ߗH4ˉ|,'͋XԄaZy9{Yv#{$ѓ$ۺx/G1+J IDAT[ mrWO]d:(NShW+Er&{@ K3"A'%D_erUZ3[0Ĺp6>-p^UKPӜ xw)o-s#֩^P|sƕoguVwq" 76S"xqo\ĩ\y.qDʌ\[8{|"ENݠ]]eͪsjL`2K덑FFХ;=fӿguOF/( ~%0M#y61d}s3هgڊLGl8fwï,EKM*#JyoNPI_zpGD=d#G65/=Nhj{ʓTȣsg.L@~!B!] TF틗y;%2GBy#fdA#gϞc/`]l= [}GE:e6-clcbB?MzMb݁ tw:Ƙ9Uvy9w2uUvIhУi``C^p#?r+SD:+Ӂ&;n.^rTr't$+u%h 5ŜDnΥS)\uk%ٴjm}L+*W%&uZLsëczt_|ݩ }lXHg_S9)Y4{{!oS+smȓs3rRRQ֨QM{nDRQ;YDĽg?M2d !^(7=oۦoaۉ+~Q^uc;'%*.P0({82~fo ]3*tyoo|WГx3bxc v0Yv.ґy12\F泱?MqL:wlq+\~ɗ~y0S*BnVR6|)Oԩǡ1ܿe7JjWY㇯_Ij }Qv\[\ _z0*Àͻٳ{ Ӛa5Y޼%gP ~]G3Yi~Fd2]l*-S)n;@& !B!x?2mԯ;3lͷ \g?7;SCPB/ro5%[@2~[Yg }*jr%߽Zpc'NH4 켩;&&e){eX>Qtr4ikfͬ; ̽Lj0 'X4Vz)zbQ[VEȱMlM|og*O6-P;vޖL4I6 ᮦ%y h;nUɭ W,wN,!9ެ&K]Vաv|]ه _clOf]#{5\"/\lj6RmD^wCSٴ Y";ʰx:X-ݞ6hC@FTp@~9VyQj?r+'[[y'a5'[?V/ΔjY^SPue!pL ~5J1Z`X-]WpЉ@PТ UMjT_΢٭::n] }SVfLMB9Qʔ>ԟE]NiۤvNh15)INW_]:QagĖU)k2NGJ_Ѣ7׏rz/U6):}&hۘyah! $x sU %ve*6}i[M/9%x8Yz~&_oJb`NmQoZjzѢ fV m܇zy }& ȖnΊEܴ+{VC^>έ`kguf} U ǍfABڜ,])]2Ubz?J Y<)=x;Wj !hF0Vsٔ4swͯ.N NlШ_/]~q3?]M=~dAsn ν0-ƶxtd4ϠNa\5)yJMy=3M)bO%ZmUVGk:b`N0=㺰sG/Ibtߓ}ѫ h8"Otn~zLq/ْÚKMzC鷮"Oo=t:7KĜ1z3& 7SkR|yqDG&iw3]-hۊ"EcB!"GR: Hvk`  Ǘ61y5\'~Z!%#!ì\V )QȌIHKwfD?<Ŋ1+xRUr~B!B!d S8p>O^pʇLY \!iɤ8AB!IB!B[.1B!B!B!ɒ !B!B!BdB!B!B!D !B!B!BdB!B!B!D !B!B!BdB!B!B!D !B!B!BdB!B!B!D !B!B!BdB!B!B!D1Hwڕ !B!B!_J*et]TTX1ի͛7!bIyd.RG"<< B!"?~"MF/2w}t2:%B!B!'*^Ͼ G侮cla4u/Igg+^]mBڊZ tfCǽMp1SsryP#WqZߨ hՈ>Fs,G.[H"9<MIk遧GGXKSMLqͯ-D6s'mg4I|7؛%9]qs͉ Ҥn|CkѺ/.V[fŹ`9YƟ|ה\Yf#rggrɲ 53ocY[4T5?N-1ٗfdBةY/y(,-.)V /T;cxu R#;Y-ˉO,mZ1Rr[ci Ryo0qw:OsSLMM+-bS'Nq'1[ &+oU^D a Kk{rO;0G%/_Dzjj,\Ȣ1)KNFfF yd ~|Q;:Ӧ"nI-U|8ڐ3GlNɕ?P Ym^ 7']Ϙqf]voզF>y3;uOf4(@ά69yQ NnswY;;agi]Nu?_WTY>gl,-}'g@V–0|8mBȆv%T< y[_Sȏj2m_ ^\fe߆psҚ9R<3KNuoNrqjc@ZM:L{!B!?Sb&rLk9b?;[f.㇡>s#Gk~YK':3Wê{EzsiooZ06\w&8PaSnFHo}EsߜgAdve|]5[dԱȘDSa26mܮn\ԃ{i4d>k̢Y&O?N5:ɠVneÔ&mN> |Iu%voZy +%JE]>ϭǶ s`Yi^ 'UL!O%?y'kKԯ=c>6dȚ}(5iS|_]g8[cKxl:q'Ordrߙ w6'b"~*ƒ6Uiߘ{Oε#bٸr.3ufdldhn1QХ\'#gQ4DZ-; SZ p[fZÜ>ܚ{^AQ<;-Qf aFsl|dvbrȪJz3˹Ј] a=P^o#އ&}{e-gm,%]8ri1Ye.fݦ,nS8RlyS)2A޾ϡ#$ *Ջ3DZ+}P3߱z4|5YL]MKṣfQ . LyۼE}vtgP=a{P|g픃?7׿'!B!B| }'6^VsJ5OF́8)Nh: .Are0+&˓t;~&͸L~<'jpaޔUѱGnYVtW.Z}LG^[L=Y6-Pd*NVE89G 9~oAmG>2tQC14괨Uc9VWY3Tsex63eݽ?W͎/=:}LWN%YS +\ i uH!Kݲ))P@*x}ĤYգP%X7<ˎR 4)ZZl1?ϚJ8r9":tzPړÓCVsVv&N:cǭU˜-Js8S?ӥcwnSM#""/r϶"AEϔdl,^r^'J33\Ӣa՘_UuC [b[@: )9:K Pl7V~ql>'_ƽ'ՠ0\)f*"e #9ԼxWφ=OyCQt*Q.(ikRq yn%6|7SFtY&ëQFl՚ojw&@r{KA ?ÙC^aleH΄WSo#zs}m(U7?cK@9[Ns.Tm"y`ct)n0.#ڌ3zpG.:i{"?9u>fV;⩛© Q(WpYcW@첲8n~zm1{E+Z]68˻想/+^d߹N[<.XFI c%(¯+X%HP7=8RdچnO)MEԐF*T5حpPn'x0 T?}Od]i22~X<?r+ 12@1@&Ocټq;b2'g>Uy™S;n{)EStȩ{X163ȤX'>"/ 0@N]sFͥF^;gӱ7iIݵD'rJ?oI b6s8Y.)P쪘bGUANfv冲Lc6Y3RfR1[wM>" AWcgg$yK004E^ݍYf`Ұd7 cFJ>f8RlL}3%pnZw>7/yVP2P*?fI}؜sGՁ*쒸ECYٵ@_T&YqA !B!"QL0WF3%l47b̕PN>w Ƶ^/8s)9[p'Ώ̰и`W&DAI%gҵ:U g3]&,[ʛߞcWwsy#ږZQ xۇ3a|ݛP@Ie]Ta^v#+Ҽ0*fn Sە%,3㞂hr\6GϺ1[`&&(arwP╥Տ;p E1Ppx9(`J׹鱢Tg׷m0bA^ 0`G\W{4A^ Youo .4vn"5ZT{@3f.:A1*orXQauXGϘH:5g\0wK*Rq2D GHTGQ~^.0053tv(&nE4SssgR۾%SWm. {lws 7mIh<됻 g'}!B!B|;Ҿf kn'yӅ귫%5/73jNAbQm5#_|Ory҆peT@8qrɠKK̿ a}Tbםajr{yb4S疠cI'LWJISt)q 3Qm^g7n)̀\[sL4ۙ9h͋ۜݻ_WAUa zb.rMsObㅓi4_ ٨Q4)` <|U.LʛLV]˛rPg'_^+͔TmLy]nxxc7F*Hj[Q~7a+b^.RmLG:ZU1 M ꁊ+L!TDe^VLk8V݌U~2vQZB#gU9[38CKM^qJ(wފvάZ "x#xN4gz9uNХu7A~8mLѝ%nYĖ=?-,fGr6 DjfRq٢Nm+P}i?feT"ҭ< `jfl6į`z¥Cc=S33#LHj1Ѷ0q@kXqܙ^mRr^Lm6mz6/i]sB!BK~Jy9:+Հ 4&8;na5FfXY&5Cpxb(в&Гz85~`6| ;G/4uFܤ!Ss _M큵**>uVl[߸65FX763Ѯnl_lϯه?#*&@fwl$Jr3T6?fAz`da{Q\mTlF0DFnE( \nWMnXe2!QX h#CҪNZ fo6 9 QQB]l Cذډa2xj vM>8&OZ¿:\)Zg ӿ>L44\d)C׏%,Z;ͨ*NyMN̘|O֘aՉ'Bäl1,֞M`Gyγ`fmW;UτqNRanapž)`B~hLR2玑/=,FgVДQYcJFNYđ!cѿ8sg7sԙ+B!B|RVzժUի^ڵŋ2gۻP{4̢ml6o3F)&y0$~ZիW<2)Ki=K3\*Z}JB!B}nݺ_Usϻ;B$ Jsƥ,Y2#b;vL#\<2)رcdt2B!B^};+SE'p5dt"B!B!BXOycfbB!B!B EQ#cB!B!B!m3J!#O@!B!B!Dɶ~B!B!B!Ho$B!B!B񖬨B!B!B!DI+*92Bϛ?bma6GsIA>?¤Vy :MRww0h~7]]\n3AG{ Ycg_ftB!B'^Qr!MA(l9}S+:_ɶsQZ+8Ȧݗyiol`ĕ}j9{M*:i_i͢$8{?&Ve$̑qB!B! T/>JnO|c+O=Dx}a~Cw܃q>2a8.K OQ,c=wn\\\CJ_ݘu\S2~ wb{5@FcdMvOJy39VɁ dd"bV 6xxy 0ɡw.XiP[ŗJ-bx%$rNn?*k/[逅!&ߐ)&#'J#SQslVWjɍ1fY f,X?Q S |3d=N,WeN@ؙafNN9=Q*vw'-F^EqhD] ɓ4>7y0mN#jMbw[BS{ZTRB!B!2OS? !ң3Vdٯ3ٵ&yodP*4J`눾2QF]c|8G<~ˁ ǰvkˆI),hZk&x3Vԫ1C8{'y,#WeVnCO|a[ݽj4 Y{vɱh^VN!B!o|I7S͋[׹SЇ+rUskO.- h7Bco;l='9ST _ԧ<;\ 000*Gr&ơQO[դO22n-Ӻv#$1ȘIxEmO *DΕm9(Y1f!5'|X3ʗv_R5˷jB!B!g ݻ?ڏfI %Lk72*t_}ΰ&^|y2Hhd)0pd?Ulc?߉~tW\8'Q̥ &&&(Q/w^ٳ܋`0Q/rT$f4jo|Xzzbmk}ҹ@I/rQ{ү|kv߭^fEσS)Foo T*eʣӧ;= pIpwWkAvؼ5N_d$P!B!R}?-egZ 0,TE#=mFd%-]ը"D 0N su0>^[|󿙑lX7kg5!6ٜqfN5,4I;~ʔrN0_{yZTV=eh@~j7JGP4'呹̝*Wa* @I<Ѓp?2BGGpPkwr@{`=ČHoH6,4T^aiCec6<(`g|5V9=zEUb I 2( ʚS3Q msa>&8E=TVek_oKuH504E^hEsEovIeb&PB!BY|HIԉZ `@tX8/ŠUռ=!X}ʍm0W)f ž ,nx"o2&~Yq7#/ ׂ * fC|;J ~=Igcav6X:{lԙ3ys`al\X0fX* g|ts"ulNU ,%J,Yv{{ *yeWWL)n/KOMNl^qQ w|O[ ?oڷoj<̪gfA@\rE 0M\BsM3Ms\PTZ.iIVf&kf- "̜"" dK[0IY~fwߌlp9f )Rdƌ7P.mV.QòGwV.&O# y<6 w IDAT_._:1C;mL|av_w?t<\?3t962nWSd7RTs`ukѤ~i?Df~i?;|Gg|7W?a-z~+珻5\0q hfvL>.|6+ ?|Ac<E߻+56̚+d姟gW3gjUzH65sʞkߗs2풤9ro6#3;C:reZ۾)^ |]do~#_f[62ogr^o޴cگν^?F6|H|YF2<_<"or^g5 QZVݏ'?al5+4NmIz0ο('Qu{ql⌽S19xXϬ8{JtlqYS^-GoO^g62y:3S,Zafɨ;N;2Cr\~ڦ9-&@jsWN{nf^x⹬<| ]տ1hV:U֦=.IuvYZrd~+Ż)#oΘekc[es$mB"3^N!Z13&;y`8 ;{t^.x;U5NdǥivVM__vtjҐgh-+}մɀ΀yg5~|yψ ѯȶsvX 9}VmY}rʕrȓϖǥ}oNoNo^{EV\u|n3ăN=verwLr?,u䎚x9+›Yi Sv7혪v[娓w#'o[?]{{}}9Ӫ:ϏSUU:m=.]J=hQNȀ?^{0?o]լY{Q| P{\YmlxYTu1۬;93zzC֮ޝJ^9gsz孩}t>]N9-?vVTN_:;]j;z>_/CWKRc||~wqEmݖ[oM{糽Y5dzg53pW9&Md<Ѵ|t4|)wxs7}9g5}[nСC^rf]vޅݷW~#S^|1[wH+,K3kL}라|~dMyz;^o鐞Gߗ {ūzniIN}.jϜH*+=ɧ: ]K#s,γTYūòÀҩm[Gq]pBo=۶Ovʉx>󾚋7/,ֶK Ȍ1˘&ZNy>x|{}-yXќԯ|C(4|tK?5W˦uM]3z1w1#VV?3W~}P6>;~  mH|9=G4\ó]bmhQLg4] gܐo9GYϚ r۝nm?5薯\uloC=l_|?_|}7襪ufِ ;#Ww:Yvat~}ݯ2j'3kۃ!7?&לKj;(~`Q5j ;lΜ~yc q's òbo<s=&t|!ݗxǏ_-4a„b̙`1fFwwqmT<2{߹VVEoܸOѭK)6qSYV-ϝdQ^[=FW̚,Wǣ_wz.>yШkvEQuOۤh~o_l]nmvq1.ZwlEQ۶X.ث*ۺ1ޏʎGQŌ}Ť-<&Yj?λK~{]ؤO,{c?=qZIŎzhXc+ccw^h9?inE.{2sbqԧ[~kBfQuOQlںcE/ٶ \th38뙏keGǣǏ(:ݸ8w9uϜS [ib˳*x?W\cע3= ݄ |SƏ_$|cDgT̸\sAaywyj>Nf׃^6+U-z=j܄Kr?>Uya[m3$\~Nnh?~JuMMKW&=Bj74=k:oܚs/~>[w>]S588W.?~cs1k施eஇdmSgg퐕ܩ:U B,ӗ[ꘪCr7ɋ]<7_iӦeڴ3}f]_z!/ҽ[W5ݻVe / REf~tkBVAS/rҭ{yv5J/ /[V=ճ ~5̂5F9'#OʷZl*I\r2}3rPۥ!@Y**g.=IRZv;pHcl-[JϖV;e܍?n?4:ófMUjZw*o#'ȱ!*IҶ>;eUOv = /:ڔ4(uȵ ;d*IJ_΁_I^>S ^oz5>B3_*j7vM._QxSsWA'swmr [%gѯ++om>۪JQ3n4Cﯯo/2[Vwk>9fAx/A_Ȁ}kyss_km9:sYe݇gܮ3}rO.Mn=:,oʀa[gS;|:_!3;<[1cIG of~}JkcΥz$#N귀Ags#7{LcOk}3 _i]i_sJ̀ J5sكZ&ia :#|{͜eC|=Α;F#'9}a+h5S1eR{E/KKZng уmVy1\g}=_~t&t'xZ6obw̰/n=F^6:&W:.[nRit~{Yc6夿&6s[v9dA,7c4*5OYwM+Vc?~wqf~me[;/Q&M^ 0MhZGb<[2tвWc\{e]%wa1m~L ΨJ 4BP~JSPQ___ETCE]]]4S-*'&I&MTEGb<ѴxKZCСC$+R43*@i 4BP(PFJ#T*@i 4BP(PFJ#T*@i 4BP(PFJ#T*@i 4BP(PFJ#T*@i 4BPe&OiӦeʔ).]R[[޽{Wd*}Tl.̴iү_/:ujE hfL(ڲ* EHȑ#3nܸE~\9ƆQFeܸqJ]]]f̘gԨQXF*ZP8sꩧ.tcǎM}}}FWф Xƌ3&cƌm۶3g\ګ h zlS!T@3$T*,+_ΨfgTTUU-PҸҸDPв*FPgT@3(gTL:#m2*MfsihhHUUU*444sTtW[[|nА<gT@3ӷo{,}2\ iӦ_~_N3uԊ.L2%EQ,eUPТ-[~gϞWg L-Y}\UUbQ 4Ψf(Q4 @iP7*8e !(Ͳ*F4CMzjڷo?wyoyUP˘QFe̘1 ߱ѣG/5x.Ԃx8SUUN:i92QUUqa hsi:*5559>#Fd̘1 }ҸPPcCOUUUN8E?|n2*MeaK9^*j9C>nQQiB43;wNCCCPeTWW!;w.sgXx>,  ӦMː!CRE%,e˟gXx>,  SLQ l˧J_ֱQC ,|`qTgT$QKLS !rf+j>wixǷGfUH2(`5=P3ه4,{m~$ʥ|H%d#fj!oY?v?2_^H_}"wjfp^*h,`4|y{oY/ow?w|)to,Yo=[9+n[hI-?'ͷX?+y|msդ2[6ΰ:EʺrwH1g (Uս!d2gz~st&lcZ}t'漣vΠWMmұ˚p=refQ(ۦM6iס6{l/}\54E!uI1+=7ɗ(3~>woen9/~Æ~|eo74gb/~\Ծ絆>뾌5ڶI6m~Ug̞/} mjy5z~~6̧VN:j2hǑWE>Nf?&2Wu>&ӂJc(MCG.Yr5Ge=rNK/^[dVN1<6WHBI(1@B/@t" HP:R *MbA_ч{IyHH$<\\ݳ;;{Μ/7QZҵ~WDcڽ=FyrS^Ll;SqNڮ5 F{ c S3U~Jmć4. 6XSv~UB~9 8頇I#8dˆ K- 4RIω$F;WSrUHagho2\a]sջn=䕯U Wo$ރAE2~fw+!Զ2$[!UJ5 fY5Vׂ=}pwW14cկPӔgʲ^ezӌ&7m~^JmjysWy$҈rn*65nT?8<]?Xϩ;+Ar֥y# mQE%:kԫ?omӄROn.r.c4o`ZBy<] L1⟸"߼`6$?fO]/5vj?{حY3V6I˪RX%UQO-{L1r=@޽MdbMՊ *~W]ڼ N]czjԗU=˱ !|VA΁zyG #|^ VB*gWュ>T]Eux XX,Yds\"j5RL7lfugG>23&H:o:!Ba4jxyJ:HŜRMِaF] x{(wuU2"~|a|}D{+@e]Wn-߮O*G'eZ2JU W=7%%HwBwS3u럗+?̫$ͪn-n8xǖU.(窼]fkhcr4V?'ueLuQRy]呻ʷCIG?i^F -[^=DImC.,OsTrc,->QC#VQ݋/DBpJitHS4רS4q"ёMs4~tO.V藴TOZ\_.|K; ״(Z=AGP3G|7 V5Eҝ۷e ("m:P5VnlYEXF)/M*|jvsR!Y-1p̗^T1&dP+գ)U~_;gԓ[q}nt_r)8doPǫ{alY+j@ds7ާH+ cp͎u+5f){{[gg+A$=t[7oeXRZ}ڵvzy9N*J&;yQ ۿѯhjrV ~[`0M 5b u+XI[1,x?#:inZZvq}M]㚩 [Ŏ% [^}>Vn[P)k-?D+gI2䕻̺PE_Ԓ uctW~T%z-YT=6$Ѯh'Gk`~drU^4Kt m~s! u~ȻG* aKM$kmUR:oiR [*]>*QFNVhL$[h2A/ęxoWɽKYu*D*C6 lB6jjX"M-YZ/m'tzG?= \.ͯ=SGxjp,AϟMP܇4+6Tuk#ӟHEKK *%]!)gǕ{;JYJ}MZz>IgNyx_3kw뼋[J:sRՏwݻ_ >_K%EVrЅ=_}\4m^I~rĜ:+_zZ*rZd@e;nIrBMg)Qҍj]5 w쫫*:v}F[wH݆st!U7w%Su$NyK.I{.7Z^)/R&3 4T)AlNcjݳ)XDMi\?CҬ/ix6$نWԖwo|S0UHt/Df\ R8B_P56F߿[M |ȭM/)cᗶ)GEn[3ШiֿHcJ ͒MpAg,ɚG2nfElmd%sؚd$ﻪ@lIϗX^Z`y3?g}WRoP\׵]7\m$:KziSYJ-R{khںfP}N##e_e'9 +VWvSieck+9YərY!nd%Q{ڪHB6rW.el052" e 0MYavsb@px&\*`uW]gcF66YI?X{TFN:jEnPBajݲZKꦭJꮒa8K봧ї`,ۻ&O{I{CUKfɶoVr#"*۝ZJH3P,H/~~-SV~+q%L ) CJJ6髓=9 2ߢ!w:Z 3&jKL2$0$s^ IzȓVS=RWد'dx=TZѸ9G[rպsMDo) 'J])Ny?7c;Ѫ4r>y5Jmdm\fN^I_q"uXD*C"˺)TϴǏɇ>В>j7gzhiZ+V\RGZ:.2l~QPPlQcѮ`6iѣ\K堛r5QZlIN`|/q#>Ȑ'd?ڀ#)K0/HyװF%ÜfO䔹 )v/˜3k\brլ 9uDTXxWK6Tu1ɵhmuW[[ũVYU }P.ao 6ҁX뾋2%2w I x>SR:Q]bhZu*V{Q"?-zK{I~ RN,V*`I[ڨB]~Ts 2XSLڣ".[%Jۑo8UUh҉ڒU :ٵ}.T3.ʽU.e>w.F2F-J瓫v9dDzt˪j*S@_U@*ۡ潨1t c'rj*S+92* 5MVd-hk RIDATim,~x]> 5Y9g\s)oRLf-u%9#krGOk͈ZR#u٦ +rB|YG6;{UrH%2^ rShѴ8}P>WNvmN`dBP}@Rը:Tn jFlN?8M}%s.UWŽ$o_Ҽmw,O RI@+_iYZ5f-9x7+/\h)۫`\?wKWK_Y1YVNʉ'u9Fn~UEӹzu-C "sw c"R54hhEKn4y~|`ȳhFTV"5{ƎY/ԙ[& R7Ryx?qZr7lPkQ`tc4UKלV@?2]K+GKVy+h?Bch>8lVyԘJvU "&i&[y3V&@Ձ*٬BjO'y4r|^}K g)QS#b?lUAu\Fqk+1rQ*65ԥd>;Vhʑ:y-Iv^tf--eEFLӋkfௐnifV@oT%#JUM>jܧEf0q)^$=_9OFIG:9ӬI=]zTԡ?l4/|m͚5{7_zׯ/Yb ?Q㿛94J6lrr9!'1.UI*lƆ;Zhzjj*\2G3.3ɲ:YW~!,'Orx)tEXtCreation TimeSa 13 Mr 2021 14:28:05 CET` s IDATxyXT ("* inY{L3[L5s\%KS4w4\+CsEPtX\>y;w3g.sH0euR+ZD"""""""""""r7I(>$ ㉈=d2agg#8s?ʄEVo[ܒAQ/bV"""""""""""rX,$$$Gb*9, iV`"EZ(tt +two]H\_bz~6 """""""""""r+& tj޽-~-Y;ή"""""""""""r;a`ggfkN!aF^ɚ}MVY,k܍5[[w ,D$9`&فNVȽ(@q{KlחhdMYd}Ѣq<)Qnv!x> /H2%+DDDDDDDDDDDCcgվ&%bp0sb1"""""rO1VAPGX&!""""rWR4DEjHѬvCDDDDDذ?LxKFDDDDDB?شMHܓnim4P"""""""""""dێ|_[kT}}>q[#f4:S@'" ౢq ˽頔G%$n_(!FBU Ĭ-Ȁ\͔DE s&eN ٍ:ݞMy+?͟ۿdʜ-YT:<<=R2/Ryys<\e])d?Ö0[wq!c=\f/Z@Pt0 +""""" pݸz0q0+HB) rg %+DDDDD7B2iOT‘D^$ɕҕj-Kx1(&K xxb&U\Z V >-xj3Hrt"E}h-Nugviڹ%u AµKD^upR4IX"""""v5q)&L0+^M7-Tjnv"""""sɇxKEKlܓ:mVRl0{>H?WI,:ujG緷QY ܅{ WO{/߼Й۵A_k󉈈]F#+DDDDD3vU K6V %(p>)ooͿ]n=O}'Ѧ1Y.py,+P; }  uj"""""rIaHFE4PװY-*%+DDDDD7f!։ $dq}<{4 $FiIO`”pEDDDDlvWg\Dsu+ |<5*װ9ah|.MJVwNMhT-V:E5 i&>D-:-˯S"""""q'%#n8Md@ӇӭQS@'b/h2)X^zӰ] (<g82Y]ce #s*EDDDDrO~$M]r!xM7 ȷIF$ٸM<gX锫1im +Ƽ̼ p1ց"LiQ:?\WX=!'ʯEDDDD$|~u$*2{Ӱ'nzicթ-W,"""""w' ?O~[|O{C{L頬2҉aVw߾iDDDDDDDDDDDCs.:s6SnM x'aX?%SBDDDDDDDDDD>"҉_V4 ٧fhhE:S?iDDDDDDDDDDDPEx fn=4 X%9`#d+%+DDDDDDDDDDD$_)Y!"""""""""""J WJVHRBDDDDDDDDDDD򕽵;F\d8DDDDDDDDDDD>eu}lc+f~JDDDDDDDDDDD"""""""""""| ?3! 1c}ɇd56҈JM`{,`d+XFc#WTHq 6+ٱ)Fm2򢢝ClA,\ H||6J&^%, [_hhk &\_܈YDDDDDDDDDDfiLũҨ MӸfYIdFn<2?&TT)[ {;dCsHMk֊N=2y&V -Ǿw0Ů(G?*b8%ʍ81P?=wv9_qa“-Q4=P;쎳Z+ط#:Q}W2ch٠*J^"[=QΆkV$%&Ū .o~ *p\ɜe37C&&2?򅛈H:ɧOvݥ2j./bP+@ց>'$rh6ZҪj5 $S!dtF<ٌ ypF"qq_ΐGCgZo|u3,3c;rL_f/e =Ց}{[ͳy6wVɏvBzuv Õ5"-(XYz8&A3kGoI@#L>OVbh hG+Yo^^#[GvJWI\4#xwtzJu9>xe^K(܈ޮ'K 3@=Sey!㲝*6M8-Z sH||M.YԌח%(y %*;pE*Xƒ1(e%1|Jؼ8i=j{B=5%ajԊ.  2kXsғN3ħ)/=:7e|j P2ա-/dȠ){ŅvJ$Hܭ8A^~_ל-""""""""""%+(Ju[030 ;>1%Lm=$RYAaC^Y=i:M) C[꿟!h*U|f4`$l^ԪTZ sJ0}YqoʥR7'X2?f;3&0 2:[0Ya= , }UjڱIЅ*L?(v;eyW175(a{}Z} +NRȰF [ |ma)zC~oUY6•iU6nz2bD}VX N'+E)^w?jpu҃zN3/9PC_ZFIvo_I1D_+yKƴ( c nG`ut/.5ppjHt˜phƔ+@;JO2aD>05K']|oώj'9vn){OJxaL7OUbV*ȹsx{eM'`EOKƯ`̝$`hIr5]̫Hg3 EezO)QLyr,ٕk&&€/W2s!NLGY9p)aQ\*fn}KѮ9g G/6<_tcw!؋GIzHtuvby/%mDDDDDDDDDDDҳvK.mi$rw0Blc~#""""""""""wbŊ5nY!"""""""""""5%+DDDDDDDDDDD$_i( M%"""""""""""w{kw<{- ooo[!""""""""""")Ŗq}JkVHRBDDDDDDDDDDD@a8DDDDDDDDDDD>"""""""""""|e4PĤ<?~<hѢÝ(** '3ݻ 6}v=Jtt4дiSڴiC (Q∌l߾G@%m۶SJMf)ڐ55'N!6l_5ݺuc鸹 e֬Y|WуOOlyY̙ 8wmϬ ,cƌَGDDDDDDDD$+LVڵm9=XoϵkR{xx0d kN4ɉdEnصk ,`Ŋ\r%} *DN۷/8::Z~yqFΝ;bp_ٌZgN:ͶDY{ʭ6dM{ kbȩ;vЫWN|.\H&Ml~쌌1y摘=L<9e33go{B0 ggDHv,X)v@1/GTTT>ŋ3p@.\ȿi'6Μ9 8p K,Wn111_=5kpϝ;?f}abccm%KR),kf""""""""roi#**tAj9>I&1cƌ,w"2d'Odȑ:#/ |;k+\\2e 3f >>֭[ӣG1 #e_0i޼9.\`O899/K/Q@[Zf';U;wiӦ$%%yl =ʋŝjȐ!)+vڵ~$L&4n8M#ڵUVܹcDzsN, 'Oˋ-Zd+²e˲H-&&ӧSrewn)$m޼3f_0 #e[jt }2m45jD6mlk=3gN zi㉈NV+9YӦMSK8'2{}EGcRNv\\\R&LH9^v'X#""?>7LSB%>x{DGGвeK5j7i҄@,XիWY~}ǏgӦMٺ}3OOOzɮ]8w7n6Kִ!޽{{t M-Z1?~[rҥ49::ҫW\0 =ʖ-[Ņ͛SjUL&S++2{DDDDDDDDDCS?)r"1yDFzJ(Ƚ;v<ܹ3Yk&tԉ }lŐ4i¤IR,\0SٳرJVXVJ,n[ѢE;w.5xL&"""ؽ{7 222e_Ʉ{œPY8v(PU2tPy,rKfH~^m9=X…oObM 9v0aBϏ=:I3gX\r)X`A1 ggg.^wؑ9v'''/NDDD ROTlYGHHH2ZlɪUτ =喫+h+wB k?~?-ʜ9sԩS]z5Lpssgm۶TRbŊe;4'+VsΥC.?5[\l}={_…ݜ޽;ԏ%{;~HNEj111g+QG}./9;v,ڵcƌ馕j߾=ofJرcٳ']ǏR,p}Jv;ݓ9ΝV!y-[z饗ӧSNM4f 6Yfi;лwo>DÆ I&\t>,G勈+r]d5˩ĵ;\r$bbbpvvr.TPOq)S<ժUg&MJ3b̘1[.$IVGy  QI#3wB[] jbĈTV-׏ҥK3}ۛŋg[,_.]ʧ~vڕxTZ#Gdo4{Qک;SO_թLiJn7N^^:wbB```HI뛛Y|9]vM۷o#*r5ߚ 1SF'[ kbo߾ԭ[ٳgGa2߿?hѢ9f9_>}tm%屓S^#""""""""rDz:Ywߟ4[^vV9%DEE1{[NZrAr|Ϗݻwj*Dk%8sLmv_f̘1TROOO{s9z(om9ANX,m% 4(G kcP5bժUL6-%SO=cgV?0;v ,,,>ǎ#<<<Å2rd2QD rܹsS~ aɱȽ%GkVhKv]p[.ܲeK)O?޽{={yf/NVC*U2ܾvZƎS?elk2hР[Aa)m%gΓ+; ,H3):::G*edƌ̟?Am۶t ¶ml+\0f̘1#WcݱcG K.Z*ߦJ _%ߡnt>w+6 ֭KΝꫯ8qƍcĉԨQ㶯=x o'OaE|Alْf8V\ٳg/L_mf6md9;m>+gʕ+CLL w IDAT8p ݾ՘qww' -ZЫW/X8sҠA܀k0p4'''ϟσ>1&sb6i۶-hdݻOXnٞ)/+V}RbE `˖-5ݻwgJJJ"$$QFu֔cرf)<<<2}~݌5#GZ(S M4R,<5ߧ;ӧ9s ...鞳z֨$88aÆ1vXƎСC pHDDMb;tPʈbŊѡCJ(acmrfEOay9խgVO3h qqqUY 4`<3$%%e @˖-С5PB\zݻw߳i&N8\?ѣ0aVMPP .4εkײk.;f3AAAx{{Z=R-ާN:Ņ hѢ_u7oجyŋKҥK^c5jп\oK988ȑ#i׮<gϞMY~1&޻woȅظq#vڹVZ2dŊS>}:{fM9*$$tֲ):wO=(\0aX,4,X5kbgg0ؼy3oǮ]6?<9>ggg4hqpp`8;;3m4ƍǸq6m̞=={d9y+ΝK7-KwppRZϟOyܰaCbcc8q"'Ndʔ)9*?[nJT]%G#+~\r5_㰷ϏjժcqFv#Gv LJ&Mкuk/ΤIXn]ű- ֭f.]2sLbbbu 6Ν;[@rOŊ5j111:u~'x'믿RB&MD˖-sAAA)i'$$GϞ=qwwgM7Ņ? .h"vڅ "000WbJD9sonݺd&N#Gv>}Kһwo (EDDDDDDDD=QQQrzY4l4>d-XϭbHQ1jԨ +s婸uyx .].aa6tRʋg̚5CY=l`СtGG,ז AsCf)̘1chժ0L?}[z5~'J(_^{?L& f uֹ2fo63f !!!M##GY'OfԩZR.ڨܿ\]]$#+r"n-P6);ɉ\(cKYTR7H֯_ORRRs5jryt >c6nȹs%Bf<<}䨌DE~OJ*)R}ԪUz+[f֭˄ 8q;wd?~04;UTM64ioo{ja;]rB(11e|bdE\(f-Q'eWn #$趕x_Bg?_*z˻M;>;.`Uua>$o%ɚ”Me8KXW9z*C e=+Si'~[VOEfuIadYtNj1^J.]2V%݆`k9+^,疼h14.SR*,ޭEY Tߊ_\>^ÓIg8бU*SmHkn{pn*_ҥKS|Ѧp[77~hV2e=ۘ_gթ05{6n݀jc7א5`ѶHn9&L♇R'|Q Je#/kaXHrB>&?|%[Jm| BDDDDDboү^ݙ {g܋)?+]Ϥ} D0N;Wd*swEňd7o<ԇ&6Ka'e ͘E4M|RIф?;Ώ U.,e4hOl˸?>~}ZϫO ; zEj:SbwT2 K;Zp95Z:'~+1Ɏ#u3乞RMdt7CR458h>ntK>J=s6)5=~w?&1} B&ѫG7Ȩ*?B8Ryy]7SBA9 ErIl8rfع |+U,672UmH:oMCJxJ7H:1}jUϊ5i-քZqEȶ$cӥM+؝O}b>mhX5_r I eKT)OzčL3n9N^]ٻ=VgYb9W'ke^f_&?2~M4?I^ħZS6b IV]ϲ^xm33~B!.յ3_?GPݪ/It1^vߌMPJxf@?>MMa\`.Ә1! i^"ިc7E۾2Ss)O h߂vO eg/f\I\#KܨPѿp9mV~HxNRϊ5h?mq֓K@Q,ܟFn"g$}|y]-NJaDcխB <wk_G#̅oK=;`{5V3|8ou|M4nJˎ}lxsHԎm25gyzaz>zV!ad 6sQIzh38q)~n3M;p5i5t 5ѿe&;h5֮_/TwSķ\$lb녚w]=]:q~/H{4}>߯ţm{&enDDDDDD7'5!ŋj&# zћx/qYvN*8}r 3*uzk [Q%toJиy-Dl=Պ05{b çoŐ+˞ Sri~m]ͳXίdX77 Lm}-<5.w.<&Kǎ3MUʯ[~`!7toN`359]jn6\+g,qS^x[OaÕp3w3=Xepm 9SU -3(/ueTfjua;=([ܞCKK -S4ve>7Vt"*ET]`rxy{.~;!/[kg'5KQ\gbb1(HQƽ57z\nZY68ӏ.̌ -q2x@WFAkX2NǏs%Y8]arE̘ڋng@ y(x#|34Ys[]9k'=㽉M)x= &0u`JlXI˿39p Iy>I^,3=^ظst >݇'ɠ7iYL%1gߑOfd܉h*HOMG9L)Tc7ǻ6.a]?_:Tb9+P\Z^dg+-tz;u|zx>2Ǯ(\L$]!0Sr%XroX|㿑s8Zwa0i=^GRlAX3s3g vN),k5ۏ$ҩэ3FV4CS/_:ӿ8YͿ'X[ɵm[hşkm\Ng3q-<uu$m9U'"""""rOY?03zoθe1k+E缯gr÷Yԏ#7/5q֖N֭hzh1:xaͻ!Jti2CƜ̤~,zOx9$qb`~Ed2VΥ3)+4z3Rfv1o^ґx{OzDҎ9Ь]NOi.c*+5`L Ѻ3+Zxyk\LLl&#a&Eb'\ʃ\"<֤S ^ b{8e׈W[e=%""""""*e)׵ص/Pn__c& *}Y-TmOE.D6˸ˉԭNZ<<q1Y1Zg? yeB fDapv!=}nUo!YFfxSx"dq0oKZxdV;s >ٜ&3e &-qhi^>o3\;O*W,@BxQ\<3?M's }}Șm.Ps5qJ]ӭ kц~]BvF=RxxVFӮR>[ V -Pg(Y&\*Q".FdzKs]H<r,wT&3ʪ`{g.DDA".bנ$5vFXGMk,=*FEP L~` {f-sg}e{U}8iϣMGO̾缟ՏݺNՑZJF>?)Kzo,mV_3^ȗfxQ/Q_Yvz.6=/oL(~T]lJ3r+go;7s5Jk,b׸m,W˚ IDAT<t{lorwqYs|cixt[v=t_rR'I˙̓7oYecsz֛\ yocM~>W_bl7:;dn7n6ǟٽCn.[+۟VV|y.ڥÔYk󗽖τ?L=.%36]x%|mMW53nb)ͪMsI*V77C#ͱ-}}5`{=32OSS]27k܌L^~RyTn}ݳ^mr _{zk]ݾ9{Ȉ^z8QO=( mW"|U,9Uo[^"7j l>+rMViժU}PrluU=ǯFw?sSQY?W].-_E.Yo#rTGޞ$].[%=ㅜ5}OLHe{}3#ǤFkwϞ|4+PYZw擟CO߂yg\ݕf˻ͅ#gG[Eh|^,޹s:)K|*?i5^se6k,]zOERRS[u+N{oڳS yRSs:wj&IR? '_|ti3{|NRKO^~w]tc^iEңGz(G^0$$yg['sϩ0gyIvC=mgyʲˤMs?4Lڕ' iߚ:=D,uIY٫5*eH<͵SfZS_ k_Se6'lVWFE,ףN}Ѽ]y3y{zuz4Һ7E(O5LԡG5,Oxf}?*Ȁ?97lWʧ3yVsE^JM~^Ygtn" fsC%J|[[TzKnye6b)KԌ=Wޚ+ܛ{#WƷӮ{|xaJN<̆j5͊kB/4蓃zLVy2IUʡ}+.G_yOڜOWAң[|z9uyzȌ|4k=z%u澌x\tmyXÁ A'gGvlv5>*Ψ'KK?6^?{5[wv`vZCIxn?k3Gtt]:ztnRqoMuIZ4+o?_M6ޚC`5J%Ҽ|ʝwpݳ\}rdޛgE_eTD8gzV\:58/k5=Hwɪ#w=6_az3To&sųPy?Sݰg՟]Y4ͳ~߮l.T/?|5O=[/[ -7[\{zT(~>Wtn^[zQӔf<ԴfR[uΥr"uΛ߫Pg]gȠAeMSIρY]l31Wb_x,[iL4e_T4k,1_TtmN=;` ۨE*_  Ϲ<ul5O!o;8njm4\۵,Dv;k^CZGl9._||.Bܽik(뷼:8|po@:5ʛA'fwaKfދ;2l#iX㔾|7O\uVn߭!t͖!hGgtÕlq?W>wb"l51#oGh;~"o~4kt[wz-W9i׺iLx4gpC&7:.FcC+eYe^z@8e򮚏ʵNJG:,4!eʚ;^Rv?Ǐ;f̓!nY)^sDNx]Zk6snOZ}Fه_U }$]+:$"|0W2yRv^'Il2d]7Ȑ/kRqRj6|a7\n&-ezHRY(uq{ڠq_sea샹{sԨIʎ9Ӷ1| N:1 /m>;>m# l_˛[.;cJMfYòS}U 9csHM=ϦTv7LPilu6՟'ěWn%gӓrR 6)s\koׯ^7/ 4KҲC{9d3r[iYnT[CW̱hRZɿ86msNlx\0lݤY_.\f>*uIUvYʦ-G'YL%MHiwggV39R磯~A^;>zfݿ}š/嫡V1IR/6&SN so~}mzQi/G[nTI)TU^9}>bHVs<#G62өUf ke;6έa̪u旟{=O~jH_rdE\j.JqϽ--eOCk)?xp٢hӯ_ySVYf}<:|QSwd%B5[sEM_e~#|{U6~D9瀢?kjj1*}ǯgܽv=tjuĈ{D2n}sNwMM-VX0UUU?˹F>uZ >[u0gXpqXQYY9/62byGݪE=TV%`UVV7߼bb<#=zt7ɣnFVl?@@۷"dÇe% % % % % % % % % % U1/w~=/^~.<7OÊ$߿>"u]Ea0 P_VX9sm2sew5=K?9[9W7WU7Ï;$,53ݨ2o\u\NTcјuz yg&U\7gļ1zYKiŠ^ʝWߖLMzlԺM6̲u{Ƿ gMvQ䪡r+fI<'3)y^|ùޗ圼Rt|Rwi<4Ǖ:}^κZQof,Yʗ/ݚcw;07Y32Ê keҶYTn4mtVl~a,եK:j85sv٤Zf\n;<9wK̇7zRFE|:'Pq.%K/|:.@ lO*zt+72;2{rt?p>z2? =[7JF{⧿_u7ҾRk2~3^?vה'ȉKBnÖ^ -I:kw?"}eKͲ\ۅSN,rZg0.f\8jު9!YtϗZdn:){j@6vP.8e,2<ޔ|x)aҢa4jdV>|[}]z,gPq}aF[~wy2jjԮj;ٳz-4 J Wg6VM>{ԷcׯMOϭUIR%:+=7>8i5GVL2_~zލy*yIͮV>*޴N^ټmx@Ԍ;nZ߆mV Ny?wlz5Ruoʉ Kwd`hbXJ/ԭ6̡gݐW:/'=3:<~ieaɧ) lϝ]}UK_'=%{wYN8 {rOUnIiUfTu &(_g<gMKG?AnY$al}ԾYMy>2uKmrfUy!oYK9mxojQGUs2fņ#ώ[˒ϜMwBO =]nğeU[{9)B҇8}ҾIMukEf.INreeIb.}v5yٴ^R鵳Y5muZe]PV}uyjҀ͎ߺ!_u0`@0ՠ}bڿsSn:|[wSL}uǭScĻ77\6I)z/(@p~)CpjQwӥG,1Y [`yڮ8 mdݬ^dY׌u{ #x)%jh=9%)j?SWdwf%g0X\y}egiyHgm7R:eԽ=r7_(ζ+V^&#$?^W[jʃyof=7~x zݫ"jY~+kksfզ;5& V흆IL|y,D[* ` b#r#3y[s}egr&g_ʤ]OS~t;L~M?$ԥc> *]mni]gg]9lUӭ]4m&kl&O3;%t茫ΨG}ʍjwZ5_eWIF ª'f^i:~:~k5~!VKUd3O >K?KuII->zy|lدŜ/>_4[o`'އQ^;=-Cw>CPjF J<~E*RҥGoY6+z.;!N*?7_KڗM´n5w+f7T}.-7;>kM ̺ˤnj0rQrƥgO=+WERUT6)5LɄZ/fʾfT}5?.fF:HufL[믺2)U\n_ nxH{i;Ӆ̫S]S3ʟ_Q}1-_=^k׳}xտsßsҢWdhаAj>Ϧ*O?,vMO~ZwO꠼s,$ݲ\yk)*ߧGfRs~e^= ˥}ڕ˦uy2gnY%B4a:д7Y{yݒR?VȊqGFd čgƙJv9(c||yȶYa:iê t*Bd;f3p*k+m٫.w2~Rٶ*\f`5+ϧo|󞭺׿"&Yղ-O{ՙCޫNяLFI83뜲]~UqTv_cM0;,{mڞ4(Z}><}jNOj1Otl3lի,$lٳ9abշ歪dǪvQkg.2$)Nޮmm;xِlI9bUx/EXYjnw9giXR>07CKꮾW~Yk6]f>}#OZ>l+uR-{r]gU'Rwϴm8)|YKJGOɷgU[܏X:}eZgn6kNVW\Yz]؋&< |8,M-n"H rq[?cEno3n닲J9/ o7G]uFxFpz>FMg/(ݮ} 9"Kj%zf4,ml9!7E6ݤqYi|!ϒ&KdWWʏ88>`eô_yq٣{Z~sZyl5d`68<,ݷ9OUO~C,'NZ$nz2L3ןNȠ5n?O7Q){Ρ^}/+Gg-gV7Q'm?R&YdYFso wix2ĝSE,Yy^iV*Ow ]#6̿o8!9팝T{gWduT[o7ekqb?T۾ծGr9=ܓy 2у}۲C礕jI3UIM^gI{4,mj9|2|}yUSof⮻J~.Ə2| 8oGgZuƼilg*"[uh|Bz\}۟%~?|ܶk\Q&D.=d)A5)/}`tU@+5\3ty`JE׽sѰs}T粚zF?%;1͖L b.Lyr{e`8K 閟 ꯛsߙXt): S(Z3g2r0];>.@{= % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % UQt^ȸq2f̘y-[y޽<zRYY5Xcgɋ/=z̳c @ƍK޽q铱ccY 1cƤfk^2P555ssA3ϜkFVaa3<3R)4hP^W*~Jj ;#r'nva9S .Tmi?xTUUū5a,fVwq9g!C*Gq.^ +h費3a|KtfT{}s6a| 544ZRZ9jjjRV*O;O=zZ`[ZѣS__ߪ ׯ_~<7n\!ׯ_?_vJ%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%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%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%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%J%J%JU'[XkVk6,JXJXJXJXO3wرc[T@+gyR__>}|q=ojX3Ϥ1Jk=cygӷoߙWeV5WUÊwy'C IQyk4`_Y_V +Ǝ@-Ok/Pw:2j3+WUZ:?g9C2jcU*hۏkY/|!_e(E 4w.Y9kE鼷MTd;d>nޕV|fv5pT"-yl\{h\YdFZ?2GL VYycW 6J/ L{,%iznT~ֈ-{,m/}-6m!]]<ˬN~z`2-8w"2<6罶R?} ռ~ ƌPMjfrke˽k,itZg:|^hʋL̾JW}Z#e.Prni͙uO1o;sQNM!Ъ=T[c3/6K3O徫m瓇sܚ}ܝҡS,6'9OcTxGﵳ߯. ; ͝N^8Iyy#"Euln9lìtB$ox%ޜc^;CXl@V<6鳯}ygੵ -ȨG^37JQ̺F*i!Luͥ|0ݹ>|w,5o}'?ɳm ՆVq_&z[ y9MDIDATaۥar⠶izWdEk2_grѿɈNssNd.ʦ]+t"EQ͸gCc ȲsL?UŜn.X 7;/͎YGIKģ~\O_+]*MW9w9 rGjFƲS8,]E8bݔ~铉yMmq嘥j&[#>tmyOò>ғ2\9tLP9];դ飏qH훳;'<:0nyX3,sOx2zdvg i,P3CߪŇ=y IǦ1Gv<ݙ}{զxֹOrYGeMyC5?,ګ&{mˌ\2i_)҂W?=;]+֟;L<1Cz,6qm~nytCsVMØsҁe|yug +jܕs/wV=z,^SI9?$MNךGw3Q9q>9#j)(̵??/,}H|֞Y6,wsV κ_m>'xgW٭]YrZrL"J}1rK/"yc囵Z)>-|.;I|?57-{lgtL%{{V7]~xf>by%wdOӭiHqbuek{1/]|r~Zu$ge!/n+n%J h).ε__~^]2;dtI%C7c~skgE+w]x`^cMʀ K}sod%KOΕonUvY&I%^kf^'M3饜˾~t^r-$|_'k!ɪܱ#\J^XM;6g|/=IWSzwUREtans76ydwƗ\v=4}^5emMhl13Sl5kM a^I_y4O%+>` ZiRSzFY|槚|x.YaOI&slv}_k.-G^v[,0O>l~#2ۧ#K!{>[ J{O䒑#sOc?Nm~SLP$3W:|›RK*IfA΋enoK5#ϤwDR<=jxO jv3H3ϱh,XSƾ]M2)<|>yl:%Tm'5mw7&fV| ̻c&dOTT!_Wf&?!~] l>1; {(>NtS3ʔ6y=Q'巫/=WWڨK6IѤL)*IjۤRMSu4"m꒦4EI.u_63\)޻'#<7nFd4;Oja_)I9_fN^IhF.>0utcZW)2c"ES?5m'MSWdqUSr~bn,T3cPL[-#o9>?h?!ϜK}z< Og7L7?ԇ-}rIf8t&m&MME*Ij֥jf6IRԦ6VS46Vں6=?%{B?gqkf7K]ijX3W]mlTLy& 5=we.}dl}e٣o?rٮ嚋-eew1/mϬ#[Ԭ2Pԥ~~߯m̜ZsRY;y'3a.IOש1/=T>ZdzB~WOcjo&uR,iLy )T:e~oΤ)0'22:.+wlz'ݓ= v|Iߛ^o~Jm?ēo{MVJ$MML 8>󷚱j<8~H^(e%NqۻeJ#3+/;=||q!Gݳ4}&w%ʫs-5q,O4_2PY.zayҩJ۬zY6ig,r1eǯ78lC5nbh?!oZ6.;/ʧ4u;2s#W,C4Ad.99u.IMmZSyvN}~p䃙'3 2YUS|Zg|Xm.YaxYoi̐IMyrd?/l"޲f/O57mpj_Ye\ur&|[~`;mT蔆s}}/K1\yЏs' LCUjRޔW3/LI ϙaw5sݐ.9ӳ˦ohw$v[z姗!9:g{.;1m΀'gM.*LM:=wv^vԴ3]z,on-?^ۧ_r-?:ܴ95srïu]qN9ewM'KF>}qH~|e#W0:97]#Gtyy+=\#6۲W֦.9rVw吥7ʨ ݖX.~EvWM模KdmRgJb\|81WqaweCw8%jznsP6o|TI EZ^W_&lZm VsL||Gwfgv@.555y:OSFjV|fӝZŬjsg-X)C:=+橾{.53o<y-}PY @Z>5%WeɅҭcqOߐS~1 =%|c{@4&zQ}vY0K=IUR3J:'+pA IDATxwxn*!B'ХޫP^E&rEFA@(U+EA*lG )<;gΜL&/g1£nUVϯxZ )ҜgThXN# //J:67V%-)LNAlukLLݫβeJ6X8N\.WaVDDDDDDDDDD bfrrbZVcXcfaSDDDDDDDDDD i8NV+/X6iN*n"Ӆjj)VtiƱr(^Xfqq,"""""""""qvcLȭڬ톎2]l6i\u,""""".1v]V||ܱv{DDDDp: bqNZTP/oob9wtFR/VPXv%/Iio.GDZ\=Cӕi߫xؽt Ɉ"88֭[Sb3̩Aa]/fo c\t)_32glC1c_.]2Ȧi*y,"""".in}̈́#FPNmʕ+Ctt4\pPBYjתʼn'(U7S4pFs{X{w|u24M ^%UBDDDrNG66\9g`Y3ׯ_t#Yәi[-6 W|\v8AeiH:{իiң^KDe+DDDDfO祛+ᆓ}(S&#$qyĜy&c&6Ofk³=c}`6I mCFհ.7>):Nnn6G. 7w7.WŻ28683OnhXI[턆^7G8g偛-'p5HŰs-""""7:ݎiZvړZH0KSdZ#齌vLӞ*ylF ðX0Mp\8viÂӰ\/q{=0ODDDDnT aI=c7-W&ڰf{3?vtOoX0p8gEDDD ::2]vtm-DG`ILf68xcswBxbr:[b0~.XFR<>br9Ι*^=0ODDDDn\ ssA zkQxR>%ڀpіӳ׼Rڥ=rUto-"""RԸl噜 θ>qJV+`ْӋ\2?"LHouH /hN 0M^hќUg{#\l*[!""""E ;Y/so&,O⸑0<ÊGv҃gЉ|xo8qn؀wt|]rz)_,.Ws$ .y%ٛAdtfoc]%t%ߴج 8WtbOMj .ܔ<'nW .P/y-7Nc;sC)#ibm\]^L["Kto-"""RX- N:CŊ2-/j;'Oc`kӦMB3iqge\ T1!8#BR4痭XI/izt6-?'""""rp9#<߫Z"{`E|LqN0X,=(mb:&ִOR4Mb#%,8bvb{_ P;CDDDDeEJ)Q/UX,BB8w,>ŊaK̏xQQ<^2N1 Z^KcV|Rg&?t7ML,OFWn.(^MMޔLKDDDDooOΞ=KLL,KJ.#[M  X1/ #miɻO<]< OI&Ƌa^X1t%Wc)2ҭIłaaRxq<%|liwCDd$aSH["?yy{.Ot􌡡{,ք$rʁ8Oymqǧ{LޭVHaڛdpp0]3-67 5{1VHN 5XDDDDa1pww=o%H}""""""""""oeh汈扈HJHcYmr霦H*n\<4<4/yl^bQ |ۉΓ#l~5:ζ8ְXE|&Va~:XDRE<*>c͢xZuoٟ|,i괃NX@g׶={?ՀQ{=lp_vfẰQwone|1N=}@x1#9gU?]'H؝XOIDDDDDDDDD;/[C+34{K)X]&y/.%L=ߟЬ;ʍ3QFػ!ZQ:^`M6%Mc-Y֭8̺OSgo-'ǗŊ.n:KqyxU^MM;xXeO oJ_mJwۣZkzu;Х{;ʄv濳g6."""""""""ELj_I5 kظfc%, N^rڡ9.t'%]\w3W 6\pa|ݏ1G݉h;1tqfPLp[Y0eSRF=&}![sɕL'BM(Yҵ{CWq """""""""r~Z4&i=olMwOJG`NuEE+&Ӹ7z G?o8R6j]4x 5oN@#b?[!,zwUR)1){u;=aZ0L7%l0 p\4ge'ZjeP.!O5!8>Xp_<Ϙ%Mt*e+@t{ʓ aV~URN.M}¦AJ锸v^uc{!\"""""""""џlDز8$<|A^>Vi?a`:uEDDDDDDDD(ʿ QC<7w'v JtȪOF n8W>2Evcnq\͉ƚM `æM?˕j3grY6o ӽkWy&cy T<|[ޖ֟qqڔ*?p0'8wg[󈏏{ll ?[ě3<k+W')tN j5#x%>O8ZTt4˿Q˲+ ,Y9F>%K9{\qJ.3g 9eWd9'9?R-n\p1/wkSo~~\ظ|o+#yrN}hq<%"""""""EvS:ﭼ)IW}s^* :`K^:}:v_fa8NWF澴n???پc'kM_1|hpwf 3e̚Vi5z Ǐղ2V !9̚3xztƠ_|3T[4t܉/d2o܄;Oy/}hy*+*DDDDDDDDC5wǧz;:ifSﱌ8@$9;Gu]L8Ӊakۆ_z֭ZJѷO/wŽymDv܅bTK>NHA/s8N)&&7g!0:JX1 :[63k\?秭[1Mʀ{$8fΚMճGA`'>vss㾁P,+*jcHDDDDDDDC5۷m fB̔s##3I6{#ccԈaL2-yFgNQFm fgkۆ'ҴI ssscLMN_=9/U_pX:RrZ7k+{#Gɓ|7;.zꙭ6pn}{bꔄ= ]<{|I@楌JAiݪ%ڷK`ܩ#-7ˣV/R%LIu?RLDDDDDDDt;KU8^緤٭Jv䯤Rʇ]I3z?@duJ-}֭ԩ~h~q}Ggͩjf}z/P ݻ96l,HY+k4+Qڷf͚X Ge I;m2MÇeVsԩS0t_ّ9ɮtko6ud޵Ry9ya&*N}}'@N8ÇKn;_l6k6bnO?[ ;}Rr6[1eıɩӧYdIrl߱͛f;vXXɽkOٲe,+Q̛C-(U$a®{x|^ya@2YWVvVZ͜osQbN<9z$pG9K\DDDDDDD$sE)QD^n2ēMw[߼:Y2NF>L8iʴtgr=ٳԮ׀t߯P:'>X4wi4=<)U!ŧZ޷ON@8y?*F]XrE3i47%B9%ʤx^U^^^t܉N(<~|e;)Mzuk\.wSَsp_bqL*TbXj5wy;*VLN:Cy,W|yqY{TŸj+aaa&i;mX,$AkNvJ~ឫzn\/l}מ_i׮-T",NRKNyֆܴ'?d aaa{H))!9b\oF۷mӚ]fm^ ڶjf9&ߩcliΜ=j]yxU&xbc9|>2F  d@$c%mF Y9c߭l߱˗>rAA!>HrP0G)!bKӶM<>Hk5##sDDDDDDDnlER+utd`V&%$պuBXqݛ+@RޭKPVt~M3gR-O(U1u?YLjaC3a)c%o^`֌,CF O~޶{d%޽mOLJg2k~JKU _X,#_HҍsDHdW2)S4ڷKN# 6r4̛C/U `vðS%=$9^~*deŧY|{Ys13߭]GPPBbag7_)#sDDDDDDDa_uTsmJٍ[=DLL ߜq3:>/PRmݼ.pyjk@DkUL<ә=0GFe]:ud ԯW7Kܻw#G4j؀ߤIyz?Y+K}r4 -qsMs)V,['KRJyޜ1{CDDDDDDD$=>0o/M.ܩ $ B~2wΛ |||0gJ7 ܄jM4M6 &gyߍ7bxyz8<=y4n(ױ$/)g憛[l4ﻹq]!iO>b)i 0pwwgİt!Wڿt/V~EXX ?]E0af͙nOu3Mww7yz,7N3$>ޞg@WCDDDDDDD6۵Wɞ<=$Z[\ٲ˄ÏRJnlswwcwRvM>p6miI, *[.<6?4n(G Iooors}f+x)!NҥoTk&olܴݞݍ^=zps=طo??n@| _.QOc9:>((ESӧ (&-::*4p)@*U9!jI}zĉnʿV@yH AA)Jt,'-즈-22MrraX#I} 3Ϥi,-]'(sұܨe7DDDDDDX#wν@Q_.uB}PPR?cvD,M<"FcqY ¼Ć7G38 -"raմWhOla$sQ{xo0fЕODDDDDDDrh&]l>?s?}zο?ePf<Wm!Ùp0$eFPE5tjsˏ25I:бmV/}PIO|e ݸϣ*X1O|Nʚ1GٰG]Oc[&LO>D.D)@Ts~ Ǵv9ejۆ*SOn_7=LJˏȸ|=y"0]?鶒*ϯӇ2 [9=JNjĞye~R4-/;<ӖmXD1&O`p3? LBA||sprxZGm7_KG3dv!VJTkFG^dS=pg=i9 V*-F-`pYgr}ه?q,է'yXחq9+Q/%qe>Pȅ,ޗ!\˟d +9s9s'Ǻ\.{Y:w:c/؝}}<0t3}Ik9LHnՏw]~6:h@шvȲ?WpqvD# j'l'p86UQA'zacx3?f:;{v-4H^-Wʹ_7q-"Eļħ'̤ܞW82^%U/#pawEúTR*q똏5,em2ԭQhV)NzMjP6M=90SX z/1wCILɃФv omcc⟕sW4H@lq3JNKX>$ c 0_0]֡rz4˓ͨZd:z`yekYu,fv;}˫ <*(` wJU _w6j]F=5VenQrc$?jN9#uo<"lKʗ}/\^?98JgڰF 3h/R571Ar4t6v^Njs+S[>`Wq# M)kZX1*[+3,q/ѱy'aR I'C,zk 5/Q2c:XͩNL # _,r.ɟoVN}Qݩy^Y),:ap3 yZ)l[?[KWQ{_y]EӘa5%7-fL=ߝDR?E5wk2'>,~}IF,x{{b8cux{'g8r<kf4,a^y>s% i-"vbW { X)ߤ];iq mdghV?![=$ekkwiw Zezym^97):io hGe/'ǖ[=,~k$Sϴ=~P?+,:ypPЙU,о-+׳ލKcFQsKƇ..]"3V]Ф4TۄFoexϠrБ5c}}?' ߚ! $h[7f&& RnT31lrZ⩒jː<99V}(Tw3+f\3uZ<ۣ/WұxL40~ fPn}緬`H@z[8˯N~Wϧ-=x᯿est6jIfu3}d]M8x# :{;KYITxt? NV0J4-I6|7:Q>1^=: h8=營.x`ďdr=tkjT-D~ {8K|Bj$f>FM+Щ= bdGK]#,` vۀ4*~Ձq4"thי }1h@:rKŁ1(#ߣpR ײMg/ɇ4y]~e3uA.] R ]74lһ&O^?g$a7 ;mKOz;~|`ͯf.q?5/|؛kj}+A $s{.k?(E /=exBޗ$E5lԑ[\FDq,խ׺Gw~ZU=p9ȟW3tz2aq ӴP:6$~mܸM'aYzlXq$q9<2rhǠ fHm{M [î,Ɇ17jBUWr9qXfɹ?sԨGzքe7:=e;19:.rԇْ屜*>f<:O/5gXէL;&# IDATٷ;̊.HQ(wc%=1B bQ*jEQ&QD銰CA)YX}s̼3g'?p}~8ZO$)Nz"L~Z߱wGq_WЗs{DXL*̱g_?oR71{\p/y_0fܗG.Cϩw~bu1ՙmnϹSH OOޓ˫W7-ޓk<%3g?kz6^lf+~FX#2qi6-ܖޜmG$IYSE>~dɎY77zVż\Rv9jl YjYmŷBR,o:gڤNe,2;/U˴"I7H|I[>ʗNg2n\|my}aBٴimps K.%='}K7>t˞)Ӡ~2yoeI?| Ӻzt _:Qq\m3}Q9dsyWsһ&9c~f_~9gnqvչȈ{rssh|z{0<9kσruȍS?Iݵ{e6o#势>G}8R;s<^iݡyTsyGsal03Z℗;eYgc]|gճ|j횞36=j:y'T'esswF^e:~~!5y 䤫}w]{?5Yl by?TpBR*ݹ>U]]?+?sONR{yn.ZEZ,%9whfupV3B 1M i㤜ۧ e-WI&ɔ7Fgl1i$e;}.skW ecb]QI0ʗY>ȰfWNz_q!eҲy!㞾;9.oﬓ3Ys_O_?W^y`V[73闩ʪ+2e;e5enZ]q^ni=&/1;|mڧϾ[ߜO,ϱ}O1OesӓϞ?UVYY&+ϼe*7OӅ,|u ͳ9}G.Yqu|z.gɩ#ʒ_A]ח/l,7b X:5WO9\u'}kNo}@XvT{/Yil}vȚZtv]ak>7Ocٹע)$i9g[Isf,"y4k\qiٯV(-.햕ʖINH~) Z{ʯWU6OU9y2 .u6]%.+ԯOc:|d鬷X!>qƸ|Q$V4'\NkV't6ZSBe|۪ ommV O|<{=ШyZԯ?5ϭC|Z}iN,og=fYfscVHٛ}:pMvh_|oCoYk-9-Rv^%ɍ#̲՟?Iu|!D昫>Ί믖e6Ia{yzEY.{T*t->+^/_~ J9}/|,4j(oqIna&L/O6vgH=dY}re;24mB=qFt7ǟSjpfl{^8, rVN2l|.z::x_pNxjҠU:g.<}ѥ?W;~ŹdȰyLQ&m:;e/),u8va_XVvT:jnǟǜGoU韞'X&\=$ =7W\pd2[>#-=fYz|Myi(2!~Leg?>3?1m 7B.̿d3oۑٌQh%'>ll̓sȃg37_9ӵ'u<:ptx>ŵ)U;I璹07?#FwqUWOUU-̝ǹ~rv…[J󧼼4Sfю]7n,&񿘗?Wzzf\T$Iq~, icg^~;k8˵\'t$3ʪTUUv-ԆۺjPq:>,,? |WU'K = ǹuǡpA-aeeevI_'cv!? Iҧoڮ~~ Y1믿A5`,x?KiU `}<2IYwΈ,ӗL WK:5Y4]tԽ%)(-65<~xXͷ'*~:wZ{s@B͔|aLr.&\㬼<㬚b?01%POy2)g?6F+N|:םynnweW8.GNLk~~qWrߐ'I7/֋KNq=;G7*trsk_v%/X _V,uYvscSU3|аM)4La^w.͝CIa]j?53LU>}|zYrJƽ;:w_rnL9sͭ-k'3_ܑ#˳[ea SQm+ ::drUGdg&I^]ӡe4l1tU4o<|٠i֠n6^,t&n j.nuҭ~:iТC<=]C3Vy_ZSUݐ_oz:nzu{f'TtQzwnEIm6g|=[ub4H:]YYzmT<2nn ,\?V/K ߜ:.IGeɲogSQ̕3_Wn|uf6NVug7>{O(/Vo<8/S]IUU=,U=5'U&^AVZSr~6.}[oWaP͹6ٮi0U7)~6:|,|ZڼIb_<<,mV[-W }}|?VqqҪL<= |~6C:_l{L9ntm['})u7{{BVi`F}Vy2/ 9듋֫?u'UмsVZ*_qF1+4/go~o7Gu=<'n1(m~/}L>}:U';SCqYZ_:-4;n< iؼ]:A48\ʓWsډg!C2fjY= `)-zSÆ罎;dsO{"Co0v1]~6~qjr;j cُ0<[wϙW=O |ڭO¨ W>]n|"M sڸ:=pi.Ϲ[53s}SQZ-Z<0}Fa?vog `eT[[MOvzo߆ޚ#;u,u$V'ɤ{ŽSz޶i^HR:+Iޮ[j$<|<6Ya3_]AqB_^Wk1ʅfdaVǬ\wєoI:'Iq\jQHYrYoM}*ve{OY땲E'n~|M9~j$^-_S:֟7Kf|$+ =;/]G3vO/?^);9R@)W{o%{2& ̟Yy\~\qCL]^9Ay:ls:~55xozef&SпU8e-HI׊$wG{5q05ءN; y'O]}PscZc44oHRIF 8 [!m[4KϪ3/Y< s|ةo:[vmel1nl&TGKT]-5 ?mϮu $cr`z?2[y"|r6k'=8$L^;;].NERU1nnR>4on^?:S-3&gF͋szgrPj$<=0U6[;+,4oguggD9p[+䏗7X< +_ɇ_|8ge_fs{WcZW#i͹KSSl2KYY9]_`jRXQ )fC2jwj ' *7)L#FH=kGveۊK{eQ13p=w[n]Ƽ+r5P_3tdyMZ ~j9<ͨݳ-,{(S*Zo[Ϗ:<9AymW#wIz] /K L~ji߼~mVK-&/L/땧^g|p9>]we+ϓWL&dUxz?k[/|:v:Ydlr~i[}~Вf٩8վ?s7d֋eŷ6=/-dڭ_=yw*}0&SY%cWg3|IURoS|>bp;)$+푽֬Γo[_Ō*sR&gF՗^)=ǥr֍&?a6;x,W^ew|kepR̸dD&ٹb)$IiҢt$_䉁Tre|+_&-u!lQ;? k~jyuT;yꃲsZykF3r[uЧM I z׻߮!/86w}$vIօ^Ӵ"iΑ9fwre;a<Ь\i-0}Hf//7<I3,71#9bYϑ`y~ж_߰q)V!Wߗ ݐދR(R(4i/^kq;2tdy#-gfbB&|YH&S(kݏ= 8/wAlw@ݮU eMҴI2q|cs^K?7]GV\/S*ۼ[O͛%=:{*Wwzw2=<1C?]3|_0䭴7Yo߬0S֭&f-tS&er|f.>,4ׯ5j(8!g}_U;jtyZrTv` 7TӷoR,yF=zp)U׹e+eפG9cu=w[n]Ƽ+r |z- {>zMGW|s&w( j_ͳ*秝Zec~<P)ܧAm<0VZd﻾X<Dǟ:$zuMҰUlrUynR7g;N6+mgeɱONTHީk/R/u̒!w}iҺq4_j|2.nuҭ~:iТC<=O\EV̗Ӧe,tgg [}J߾iZ}tO<^"{ 0]zg8S}#U&{O5<vim5=69Z%)fCǤóg蟖OlB.>6==/==wپbzu4VǏr)}<]$Uyr[>y$Y3WpV8sh{LkTq.VWc?MLY5}Ld5f_]q^9- IZ!F-ܗO/$I٢yltYi=Yݚ<?m5ڶjG'SO}ĠzZ^|LlӢ~ׯfeYvcGSRLB>69;J}Lmk0s]rzvVc)llUYѕDž&Kfh<" bruQkSHYrЭ OKϋI}Inwϝ,/5nιӹb0oʳ~벽sB3帝ӵmL~|qn|: xɬѱE&ޙ-H<3ZYuL}{r!-ZHl|^k1'alEƼ~Zmw@[9OB_.i' ɘei־K?o\Q>{rSRl&oxp_g\6jH~7ļ72Z.w8r*RHfmrb[ana%O[!}MŹiĈޣ, {LnjyW(vc~ PBx @Qx\n>6U]۵-˳b귳r|->L-.I~j(<.f7mWKSnZjly5yazRh&tmP3U w刭-_ߧkL~gtFHZV$em}jb*Yy\#ylll }ֽveI&%4v&=M-Ӹ^m6xznͥӢa4njv8oT>ق˲F+gVSN4^s8<5a|5Z6JVAWI$S ]ҰvcL;7JD/_ ˗蔎'f䍃ҤH]gteC}rYϧd]]桓ȩ#%I?|&mz6=khx6][N4ce-uvH}ힺwGߛiN*m@;/yjR=\Q}@.?k, V#m++5{zHV_˾9}kV?0Ayڮe6ۨIu޵~}VE y|p<>ߩڤ̉Zg-N˒ͶV] Yc9#3ddž*]{^n+'wxB6o$k gcHφkwW}'T936l5:Yz kˏl%v nd~NJn\369YF~ Hx\9z`0&lvVXi ΨΈ%rFSkb >ZZ-mO?7/-O2T-R|kD޿[\Vܯ_z]wNsヤ:m/)Azpg[vt׹or-1KU#=HGN['fk5)Rh{̶M?7MtKIٯv%-B'[l+:$O;1wsq_!g*(n)9w 9w(i;Y14iRƏK׮ܩSzѢnjICCC =smƌ=ٲI'fI&}2q?[G3>eǏOQڹZBx M\֭iӦ6,Ks,Eg7wJ˳c1"1sm ,E<^cE@x @2kQfP2}'WZtDx H3ܖ-UmXbilY_suǨ,$9RWsq3N?m|`hNx$st*j:Ec>*gyz,iG}V*ywܸs֙_Xx di㙎8Y'h)1>Z-J%Z9GZMVK}}}t9c_ZQKZIZ*;6[y )իW^xaL09G}}}:קWϞ-_x lAE݅l%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @V-'yɟyu/KռӢx)`7~>`I7w~Z idPd[p;/-bf% Kf^`-ώ"<. k_˃ on94G89mXl5/;V=^)GsvyNvW|,hV(r^]^]: K4u/ߤϏef~bryMlQg%>cgr;ں.m~7nm7^:Г̗Y&Icrlq}Ogv(ȋ|k 7"b?ܙܐ>bޘ85m:}65ƗFf/9%J%u4w?SmohV43bHVl|?\~x|usիYW咟!G~qMWR;*'wo*_-#jص;CrwVyfoI Kbx{E`Bƿ.; 'lRɇN^ݏs%{zJfg3Ƶ mͩspsf|0͹k?KevîG60/9<+lv..&3vɞW9rPNOVkA+o&fJۊqZfٖmsOen{-Ǯ1/6٦qӹYotv~ya9mfoU:M7_uv\׷ٿ~G) )RTWW\ we\[@sՅ||sQgB?4$sб9sfmRH+]7uMBs?j)R??7hnlҥ Y {gO̴![f*752vi2irWӐ"{6?K =fg{w}:[nɓNotSrI_ E9׳^mz?'|-Oy(O9,kvmp涳}"Eçep ~Zc\{dpt53J= ~S7=u|[pҘ"^>o'5:tJϠͯ'4lOwk)V ?~]nyk@< ~Y-/ʈKOoRFG}˜pmmJ^{\^9m0sx.ft\.Eޜ<9[_%#IoN]/xd|srqk7Ik3uHSg o{ϔ ҽSR5h9snMʏvz6m7sFGɯ6:Kܵsd<|E9tyQ:u SX<{EmTdjCr桳n=S7dC2Vkszq^D5^Q9tlSrSOrIgHmҡHurǡ.[aT^u39&K)34&=sN,՜kh:-ə0]*wMN69촟-CIbnǿMlsm$y?mݜvټSC&>US∽yfo?[Z)Y+ͨ_HױHߟ~;5;]zs^U\7|hv=!?}l%S -X Lm.-.Y(J2{vn6o̓*gi(4Ny?xyy2sN릮Iqyssў?޷"ݾ~Oز}>LkEh5߱(TfKիsϤmr/~*IJu&3[U7 gtv^71hd@z|kR,nNXf0Q9+YuYvG^~mZU~+y)?\*&A~Пr ?syynߌltlo=_xd jU(*iY{Y!”g͈gVQO\C6hoz_jnT}Z6ݬ6-Lz' E&"ZFQ_<+e|Fu&KNN^}һݼ'U̜Q#[?y!?/6}4?8o&IM̛栋ow?-52;<:6lQ 6W&ζ=-v:#9,7;"YW=Ѐ/b|s{9xf\]li&!#I،6s d/ONX59><'n=>ŸStm<ՅjER86#v#.|`a<'ղQM?0ѳ۴mw&OUi|%n4֚Vh:`Eئϵ}Lrt+ȝG+MY%]iΏ=''!cnM߻>䏯7dP& j y?2)M\|3Ϝ]WIZMeZe+fG+J6,* ԽdGƣƼ[]+Ybj!;_H޲JZ/jZE̱C-ZV=7|-5eХiVdɆrڣ#򥦓j+,RҸY$I>7Cο~uQ8WkXvOxL~zGrQt9ow6o_~q+e՗J/d̔"|hڇ&}ujQˬknjIôi\k]IZIfԼVs?|+7KhC^r}yah ba:iFܤKgɈ'C i^IFSөc` 0S߸7g} Y.u{2s/Olr"$Chf.z+ڥ]_Kcui5]{ 2o:>] ]$J5ze^mW?!<zH}'6Ï2sN8tì9mrQe'_)?m1'|PάӚ\ _=|-&O>HӵKZI:W?`nnxWͻmҽ皩zd,X;|yr׶F=`wǜ;Sio儁cʲo]{vNuSvƳuk+??)7eoߟSz,sg8}F'C ݑV\40͚:l9oa?ʠ9szޖ'~.|wmy_%9 lJ"RKx\p%97dbnprϰ#ӷ>z9rкCQnJZ:^%K7;N-/Mkm3s܆mGKU^`,)/;9?=sp~Rig7Vݒ!,3#_KeS˨NrMk&mYsP79벳sNuڇ+;rv.ٻOTLv8}Dy|!J}|y`zKM֝?}g+MEw1&9.˷orꑜvͫYruխ ?l}ifGsbDՕ3{e/ޖ0P+g-vɀZcy3wn #=;g_{\:J: 9sf\:\' y3-wohk,[C.; e VM;e+u{AxӠj%{ʕ=ZǏ>w}ϫ{h?;:s5X,杫J%0kqgڬ5K_^dj{3 3.Yם[$0sY hQf`|-;$w5$`Q2C/oM{gקVRX %RZMVK}}<ۨ{X5束ǝ;wؗƤ{T+I>zUX4tO!`ռw~Z+/b0zt&LТT__ճ<ۨ{X5束yZ+,1Խ|VUuX(P"<Dx @1%cJ]U<0jԢzXԝ`ҪC,f01%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(JӺIENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-rxgroup-empty.png000066400000000000000000000772211501654372000223120ustar00rootroot00000000000000PNG  IHDR^4sBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeDo 25 Mr 2021 13:41:38 CET5 IDATxw\U׽,q(8q܊g jjf*G4se4-L,3KܘY#58C!P q9ss@DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD?ŐrW ZD""""""""""_e 2 d4mJ˗_c]h0d6G.""""""""""LB|VdGsMliJ??(Cll, ODDDDDDDDDDs;;;ąȑa,LpgHI q8L&SǛd"..J9]644W%-O|5,P ͛-""""""""""Va6q&==˗w@%)Y'1Ŕk3  {kĝt涫]a%EDDDDDDDDDf3vvv^@~Kַ4d22H桝,Y䶝dHCY$$gLٙO0r)HfC[(ho[ yp7x.,|u͉8%EDDDDDDDDDbh ϲ75\bY-*aJnEzF5Ƣu fܫVvsEDDDD%؆gO""""?vr\cx:Ʒ{} }xox9r:lioGtʅt_Nﳓ[L#^B̶oڴ"ź΍;Ж󝄶̳cj$Jn<y&\"bpǑ֯MsxDDDD$~P;3 @cܭOL~t+F쁤v *+ycVouk YS 0x@?:zRE=kõ_NyMhφQD%0לgDD"""""VUG*%E7͏$%7Qĥ8TVr[DDDD_<ŴKH<]'ɕ¥*ܭ xA~ (튑dmca*7Aϲ4.qg;3v vũ׾1y .o;7 )Cc!{ыCyn! Y8c~""""-Үi% 7w_<쬗 %EDDDe,/v(&wMt ޖѻbL8ǪWmۖ4_aOӶ[l 79ݓ;кUBZR01ׯ]&<5 C\ n}y!SkVqĭS0.!""""HmylٕyrLY@{)Kq{lf >.9oȝ G^Ty]]Tni&.ՄuG,kYT; o[RHҞ#vbY5N[䶈<;2f|7x67{m=>6qCÌ9eƛl""""bYcZ7y|]3ovϥZc; x#TX3KrnhP5!:q*)*ԥ}BE+""""9aJ^?(nMf`mڹ`$WvNЖ߄[XC%EDDDe:uwlbt=$1%?Kp4uÉqrHHm[ɉ$לx%G=S$EmZA/S<c6u'ƤJtգ`Oq5ځ|yB8)>#9u o|?/WhB!'ȩEDDDDQhaܡ&vZ- EXuіdժ*'""""yNd/q2""""" ϨI,B-^ZOeIDDDDDDDDDD"s:s1ȋ(ICǡXar$į?qxا1,ȯV.EʒHB&<$\3,ؔgRfh%(""""""""""EJn#Gmy(-""""""""""%EDDDDDDDDD䑣䶈$Ԕ ͥ$jզN&:_nfbc9w>y#U,4<|=qi( gb~-{ߠcάg X0P{쉱Z*7%UI ?oQƱҸz9ijpI4y NQȆ5$ɢbfnly>%HY>E;;CV^&^ 5^}g/jfn$d y{Oaz%~1&7>g@Eͦ {a [N$N=D4I뭘C3 ,2f}Ί%B}J8d㉉&:ߟx^U zz 9ekN80yc;qHO|_.‹'b~cIDDDDDDDDD.ە%YGMA|3kaO}뀛V*2ܝ(c *سqI{2kg4>큚T|[fx7% sǍgxBisx5oIz5O끼tJvK|bՋu=a3bf350m9L&frTcفPe(aUv?UEy[ y14VwO5MQ:y/Xɕ'y*ڣ\>K>:'7*T*w4)J阄{^Ԙ#qSϫĶN'qwk3Amnuq IY9${otBOכT}3F7 c> wJOy`vñUۡ"N5ѵ< LyO\!?T(١͏(Z~ĘpkH s_\+ՍkpTs>nRTn.m5qԛ=""""""""""֓[y6m ]~fen'aGn/`Ki_(Fv>= xc/F]kfE:1ۏx_3taPׄqg}vؿ z<דVGdW,z)$uXZ;k׶4ya(C3񈈈[=`ǃ"""""""""""䶈4Iۻv^z$$$xl pdGa5h ۉ?wf$$$`0[.jJ vtte˖4i҄]v1vXvڅdbШQL`2/3N.**ӧSti:wl%lŒ3`ƍ믘e]~N:$hMF͚5i޼bҥ sM@׮]m?"g=&W^sgEZ'Q[?KR{ĉILkxx8K,̙3ԭ[ɓ'vԯ_ɓ'3bvٳgOXbnܷo߾,%kժIGEE1c ʖ-˓O>2q>͟?}IԞͦa{8 %ƒ}i,K3oNz'OI^c|>C 񺸸$%m gStiƌ~jժ1zh8{,֭sδh"C1\~s D˖-n7b~)^8fggg^۴i/rr''' OQ_9y)EӫW/BBBRѸqc֮]x&Nȇ~0 8Ъ+C ?~ 6pBÓ˟??sΥm۶nn:"A޽iѢeʔ-ӱh?ͣu֙n?9[l}>IM޼y1kNٓ40cɜDab $>o:EEE`oo6ܙU9vX[lɌ3+sҪU+ƌ>vo{Ǐg(3+>IĶ5K>< cŒ믿N=:uj6@@@@eujԨAS, ޣ{ 6,K}'I:u0tPj׮ kXxqye)]nTKXL~8[|pvv͛$$$sH>3O<-v߲Zj%\2/^$$$:ubѣSMgt9Ҝ$0PreN孾V\s-7L?\Ee:ݻw'=>}:ʕȑ#ԩS\#K7$'?_NK ?CvP{d2B 2F򤞟5LJ%Kj*:v"ݳgTglgkRؒ9Q$׵Vݒٳ'̙3 `0ӧ}!YF1GoxLNNNfٳuqf kc-'""9s[!Ą|im_Nڵk[.vvvw}{ 2kpBew7`є)Sooo^y啤c9z(cƌy`b3K}7` 5_D+%n3`,%-DԬYk2mڴ/}<<ɓ'9y$ ,tHf3k֬!""I&ҧOS]'&&K.Y|L+VO>VKK<cĈlْ'x/&mVÇɾۗ43:+6mA*Uտ֮]4hnnnVm?ӧӽ{wl$!!!888XҗvNh߾=gϦK.ٜ͛c2R;wn*U}fl-w*Uggg bի3l03gL6q1n8M#sa޽R,=.]4C%{{wppЗ&|r5jͤI4iSLRw:uRb[DDDDDDD4s;ɽ%ۮ9=uԡ|+W石{n0` 4JL(P ._өS'VXIaĈnˋrJwN\CL_݈YYDmFivnuj1؂%C#GZ3.]Dٲe3\R/_waʕ%F#׮]P{ZYfq!Fx Bqtt~myCsCkHky٬Zjl޼YctPdsS[WY>w*U `e\0͘fΟ?ѣGqttɢ}R$9#,,k,[~~sJޟϟ;CyY܎Lgd22&yF9zƈ$ɓ's:ǖƲ<>.>܇;r:ǂ>OOӆHF(u-'n`[Eqt=͂_3k|~a&n\B Vl/fNxNYx:ۿ1U ڌ CWd%+NaX \i {UoP|}?v.]|KW[S6枡S=xeRuzr?;Ilbp-Eͺu(f6]ܜyGeuisBnnaX|d g65{3}f5+@'>0ib?FiNkv n೷G2-u;ڤ \f}ڞ"A,HzTߝƎ&N/>p-ڎ%򥷘0Q^:V KPOaT-5m¾|m1}o}sT_9b#{~ߺfרRȞB>qe%MVtY`疸'o9[agٷf>3_hiXWt~eʻ3r)?!/v4'7@df^ޔ|ɶy;{\'W,7bs ? L> m؟3k;p%?3%qEG-e]P\DDDDD,dù(q9UC ||8qw[*Mg6ߙ[Q߿%]u;dőȔĹ׷+_6//xh}&V7+֥}6[֯:_CNhޢ=]f(w7q{f=^lJ>/Wvj,dgA&[ >7_brj}mW_E)^.GhTn \ bT Ţ+,}L"qaBwV*wI*lͷ2Gf9C?'_fpuh&*297uޚPycgwoz=mlg8=5wx@Z6]cg3} Yܥ)ͤ}:9~ǠHܝm1m9&]KײLyޝ՟f2Cg~6|60fMWt,C!S[S\q|JMdciQ eKxSԻvǹѧuJlEXZ<3Uj֢V;?5KGz:'xtûx.nIP--;''ues~Lxa͉z9w?.yg6wmM&-x0&jWQu}2g(SML:YwRq^O;>Nǚngfz]O6};=srNMekԠzͺ4jم>\kUg8{mnQD zMތؗ6u(] ]Ż?>ֈ<ƪ=h\Ό̫8] """"`9oq!,_~&3=Y71;xeLv/h1#-9~4/ٕ`䵠 ӹ|6/w3`I3 rEceYW w|,] ouP5 4Oç_o0[7#%{bű4sOם㷭,z;/e6œ`^bk'OEEx|=ォO.׿V+c.Φjoo S'aޱ 9PݙO1j;fդ>?*r\iWԗN7݅}#qdN|aAa+A8 d^$?O{Tw҉[(!97-i0:'nDk,oI#nZh]"߯Khzg9{1vvg|_tDڒ R[.?n."6)XL[YwJqs,ב*BExD~>`C^ y.e;4 ;?E>~\ #{>oR;#FMl63yoPGL3 ˋ#?do^~q[9pDKw,m;P#OV=nļG:|Th揟{zi{g=~ (fzu(p^ciTmڄzŒM_mϹ?C ]Sк# \/|N]{УC=|8C7>^Fik5) 8z\qi,OuVM P+A63?ɓO8 û煢FyVm22 ۈZ]1P';lkJ8ϱؕr$p|sSHB|h#> !_֧=P4aD>n1>r }ٻ<08d?(C1k=.\R$;S3@ r\> CՂFT:[Raߌ+ߎ_Ogd5Ǥukd5ԟ5^1ATۺ3 Qk{+7Nmkv[8И:J{|7r0Mz37 PC^Otz,ˉߑ֍n'޳;'͋8AӆpUfol |^y/5|ߏlO~_z:u=E&"1 Q>} /ϛk& *kf}銯iF_|퀆T4WLw@ *O8kX*sJc.LAsiLu"a){ =ۧwOMH>&zS*U\N\?[-H'>.}f24cy>|A[{*4H=蛢~uٗf-;镋R#_7ʇ=g%4.%_3!WN۷SYG.j р]q:Wƒ:ܕ]R#9!8!nŁ2k"a>h=4X[mUfMo 9O #NzS5 s̫=6zmbxK}{}37A۹n[7 @{n}<ǔʒ$&0ARͿ'K/?}7c56}|LOV&pbZ7foq B K m܍ƿԊ )[vE xXs2u>Ϭׁ]\Fײwc)Nwyms&1z pØ+ڡjw^ijf i3`2_@Lkڀٌd!r1 Ze[(OfySd.BHq\y:APͯ,e0z{,hb&sƪԯb,>;JuH0Qs^ Mܗ+j~>ת&ή8k nܮyq%[BSXn&N?Ƚ(\r~01G\kFK5 kGk{hfEc9u%J9R~5eqN_ȚKWjw5*/}>q\!KtV_4dbz#WVwG@hq],p]^tmamΧ[Y0s=|ݛx _mWݬYR_L~Dyu{szG1ϲKϗڿ??ɜ^I]d>wi}}r֯[o(fm]} ^6U-MιyiǡQ>37g۬Y"+왞oӮ>+vQ:ld^lM?/o_=zShym4OcK ͹yұW,Y;??}vIE;dfܳsRʳҧd ʐGX#/^y~FI}|,?_,͛%uu8¦;/ԧ:|asӽ?{/t;#.;Y;wy2Y$;G'Q#u䁇FZsZ,[$'_~whLȜ8է.3>7#/J=>=绽w/L< nT4k}ycA[di=WZe5Oϸ6yJV?1n,vYԻչgҡC,ބuͭ!o'6*=p42]]5p׬?kwkۏ^sr gj1ȍȕo%fy9"[$uSR+-ҬɵeM{M*Sx(byf/M\ݲR3d.Y}&˨8&OTuVպ,2N$u |Ӧk6\c^y>/Mi/8&/Ynm./eZL}/oT,ӏ/)^iN0.+wj^~\юgޜaW=7u#ӌ,yֳK֙>;Gv9j]Rղb>{n]h[ug.?G}c<o0*#/ nH~й*u/ݑᗎ~<=zoF=Զ^$Zͅs :gAUuX:y.Yt;uӞ1^x\9uȶŝlŬڭy^~䑼?VuMϕrgyߋd˥cOyut7ݪ>ȋϿJnYCO}iqsGnr.-[&un^^| gUzf+Kvi5ߌXf\ɛ/ uɔggי:}R}ͭѹQ5N8670]C_Y0vOgӆهGޝ,575_fUYtY<軟?惿塿}n=)ScB?m^zRz\v)`_U]~Dׯkı%ޑgdχ;og'۬t6}:=WZ=7'^h{ne2+. Yi\ٹPU&8!u:sJ譹nz;N=:/'t[*5MΛܑ׼kU[TӮe+~S..~^?<4_NET>$ l08|c~p̙ɦg>Ȧ93Yo䣪6ix笷&1 j>ޙcdZgvw~RZ wN^uf҇5˳;x礯Cbseoud?^MoǪTԤՔ;gng{%]v?2mcyv+ g5f82`S鏦NW2,Nʁkݐ˦+פ2<=gٮ[rʘKrůb ӵrlr+e >Gd>KGkeٜ㾪^~<>0Jtn^eMW湚^Y~?5 {?kkR[GrN2yiS7!| *+_+d܈yٚS'fGY]NŦVO߳on՞^Ursd-rIkeN`ѫs;5w~1о7dҷ91KkXzynpo97^fI9ᴫ2dK˧ͧUvӹc/s orz r9^WSdwr5ԑgYrY.To7^N<'Lh1=6> Yb3s~[ɛt֨,a٠MY+>]Ff3;j7kԦBguȰ_mͻYsDRrsirog'iN8=%wsIۦehJW;ahn86&]{ԤE*Usu94 _~xUW: 3goM[:1ktB9S]]ѣG1νޛwq^o7e*`y`oFmX9Os7}?Oˉ]z}9B<}#.e{o!5S]>ݟz#n|sm۶wGfw?y-W_rM/gʢͱf%һߎzbNoiA}}\#n˿;mԽC%iɯ-ןy}?.3gW4-WƳ7_[T2b+353ͲƐySbҟ3f]MWWa.gܝW^kof -+; +h(mU}}QZvל/q؏c[>9SLim[1·cطb?}907UxnᆇuK/vm77yhviIh;7{כ$r- oU#n4 qG86( @qm#nPqYc.n0oބFרq;I6l^;&%86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPfz{ƌ'fۧ&]vmJh"ƌ)SO>{,cǎMnm%h"&N^z5zV[mL0Qa6@1~ϵu5&#9:#sMLCsRdʔ)9Jl^ 4(g}v1|:t<ު Є4dڐ0d4(0`@cm^MI'3r{nq|[FJIDAT9(Mȷe䶸 Є͹ Є|guie닦mȐ!\VcH 80> 4h.l̙꣎:*J%'| o?#srJLrG:'pt߿<ύiIm&sb/J%'pBӯ_ܐu46q:CSUU^{-'tҼޜMHCGnqs&n4ڵK]]]*JeUUUKveQ7FMMMƍYn7.555\h"wz*GΛo(h׮]jjjҽ{FYm&Gz%86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqYc;lU4jxs4Q%86( @qmӬW0f̘L81ǏoU<Ӿ}Ԥk׮czmjԸ=f̘L2%}m7c=cǦ[n31M쮍'N>}1Wzʃ>8]TШq{.hRf5݈cƞvs} _y0}vbt >*.WETv6w} |MyzU;WMUCseZ@3\wо_W:o)ߛo}-=֧a_-&=@ 9|-23^,΁)j: u\.nclۥMڼp|驪r&m?y#P#ɺ?7\HyAU61yiiѶSV^k>T47Qò'&,jVZ3m{wˬ0ï?m`ܞT.sܱ}h*ՙoEJkgQ{I;d@mre!ywd\}vLk%l41gܑQwߞ/;=n:޹zgp;+A뼗[oM}0%,7'^/x|$(=(g>= ?j&aw&yyV9jؕ sgw'fRKy`^0r`v&?K.?Kpku2fc}3hf2'jm۬{|0%Yjs`C>,9kV:U%YjyLJwIue2{zԖY5ra疖{3"h7C~7=9 +7͍~r;zgZbǜeiJ&=kN;fHLhDzmNrH_*ɤ~cԝO7;,Z_\;ƻ:' AN j%6Q[pSۨY(ԬQ#%?!WOr^{x\wKMj7PKN迗otϩ_Vz:'1Ǒ%K2\deU&IհڿVOtR ]E/i3<) \]!ShV?j*m$V"ϱ䪀o1ٮG_U7JaF*X[)͡T%U0 UFi5_@ۇUWRAz;&TjoQ |R#7?Ј*1c} d?k#薩ׄ wuS:yUW=g+|:}}殲 Vc4$HR^ ._O[Baešձ@46\u=W_AoQX^쪡û/:6<ĭ/h` TEtP 4ogOM Β0 FF;:\Tr$#cRtnY)ҙ_eZVª4iFhKbtp|8Zu=eEצEfxsKVѸwjݎzaB嗢W\TG2/ hcsPGPk p{~o sb}z@Y:sHWhÌq DS?1Qe)5}[ǻj`8vU1ioҠW{V,fOӖļFj ]$ߋ[4߿bѾmY*qv]s-iKM4""ROkՈԩU&=g3edn)#&ZqI5i&={|VK:w:Sm>-&z]7:=DzUHR.ݨOSؼ+vWM Q<`VT-S5CKGR:|>SU:QW*։"b]Һd6KxUMհ>RX5=i~\!<$IA=x ȘTղ,߽ /Pu=%N킺&[&P4gOc{j/\;++E>a4]~$- FJu|y}>ݦWizjbɒ!WĽw5uw^>S]KH듺k&j*?3V^?㻗hS?Lܓ-pSr֫^ Y_=-@uOgLZR|DnnҴi;t̒(i<}<\e턯I yjIAuTTLM] zO[`~B eWkSlyұ\.ڳ$S;K^20bv%?/yR4"lsK1κC;.+.m^?ђiYaGMmPLv$|;:,`yn~A:5lA_MPUCXF99Ԓq-C5ϗ8ޑ]ÿRL9^Rl7|QM ʙC9Fdf~SyxfU|~vn7g(Wߠ#>4On["G!wJSuI7oT:zuzN35` >R vW4h(M+4yAv)z^-M{>Ieu} Hֺ],^ޒedW?_ rJ֩J{I^ǝܣ.D²nx =p[zw{RE^S/LɔqWjԚZkjvUZ+ԧGTR:k*a3%I)^5E\iHJ$5pVyY%K>QȈ j? jMj<\ޣnmj3Yd^wC>~k+@ŽDҢt¯&7&YRF U(bzЭ֥0.ES*Џվ#5V[~njQR۩2QpRFOF} qŦuZ`Nh̎ =={jYdN;gjvl"v\ح]ɯmO5UC[[^V.und &IUgUz2i%oQI_׾ᘲri>h%\gUslXzTۯ[:ݒj?9L Qʹmt4HMhWPuhXA[')y^NDfY՟I S@Oaj0[Guc}]+k>UBhT iTFch):ll3w/ X״eF]+]M̕QZ^_IcG(bZ|>ZGvR  >|Cٔ7d^a&xb &Q [a|k=bm*'n*y=7Wf,;xfͩդr>%PF~ƆMvt%?@+GTĩ Y$u]s]qlYժ?,)-XG6T= XzQ5#{Ekkx3m1\+UPp~*TP+`Z1GkϜSEp۵bWJ&ӪF[էXKMcGhN5m(k^5T*zɐN暒9vRҏ>jLQPLP_EL/ku uB~>S+S{=qfު~RE| WikȚq%EP& z.uԞJ^5>Z+U m/QWijv\<}ׯ^*qպ!O5]Q?n2'LFVK/GFFA2M H .SƌZ} Il6m޼Y?*7)z( <~V'22R-Z&is{PmxdF} #=N⯸V>a6RKϷC)tEXtCreation TimeDo 25 Mr 2021 13:12:40 CETX7 IDATxw|M׽Yb$U+v-U+(Z֪֨YJKU֨VRjGGQ+ʾI@#͹~|{w< |]N 86d8 wZ@Zx?^FDDDD,fkGȤ x|`.Gki贈d)[!fnb; +ص+f1ۻD%`${='ױaZAw0K.:̼77ZF>~#~̙śp>{#;:L|T2= @\l"rZ[;"~ _"OqlTB1_;2iD˜*{J3-[I?4zsq4q53#5Vqr;;a,b&&a[[T;dԬ@"""" q)L<'cs6>($JFxoJl 1)&&CLMhlt4}iGl˴wؑ׋$=? 5>4Oc/%;'c&Cb\͠aӠDS!9.AjWAKϙ}JNuhӴMъd)K cGk79ltkEƯ<]8 J4<L6.QSWcMg/2uG,c]P\R)[DDDDR ϕ7x){TDI"6/'b$E sИt#fަiG\1s)$pv}ԟ?`솳m$žSgW""""fF<0+w2IƔ[،M Z:NUh 'HYAƓy兜NSDDDDLnIP[`Ms-uOCEDDDDDDDDPs;r9+"Fhςs6o~cZ:$oX@hNLCEDDDDDDDD|6!qFZ]ZCEDDDDDDDDc#Wb~ h:-"""""""""VSQDDDDDDDDDDXMF"""""""""b5%EDDDDDDDDjJ4l-1ƍ̌CDDDDDDDDD`'8p 3ǐEi贈XMF"""""""""bO4ˇ}L"wNMJ~)?ᘳ>LbTj´OIZ|DDDDDDDD$dBsS~ Oh EJU'?z"0"[!O}6p,KehO=עx{{Q(!KM;|ӵ[)3q}D%=S6JR?ǿ <)aelpc\E ]&?O;_83|5t _ չANF,""""""""˼Wk֦vjU,#]BG]牼Ҥ:E QҳdOchN%V6Ѫ &AE9aFz;a?s|f;*ýpij?; DypY T}1y0e cґ8yB͘sߠέg Po02m;vN~/ۡDm4=Рna1u1 |)""""""""%xohh4ؑ1$<$r6b?\3ALO`~W1m•8 5ѶQnz yr=O{jZ5$EG0?YpvGS ѡM2yСD;mtr,8 V~ 坭[DDDDDDDDh$/+33c6P4@|ˬ$+\)laf3uSi$af Oa ùj֝Iڵ($όd<91אۓJժ㑨3i\_qLƌY+Et:I) `6IlEDDDDDDD锉F `20ؖRy:  egǓXZ]ȍgi5G.cpudSft]Y >Mnl|%o})kavo2 [wR4*ާ ySnͭ$I'yqu]k . :Ii.x:Fr%\[tA!ׯ1f휍 ~[_v*o}݋.^ 08*n[;t\S]=&T; P ֗65 ;me> eNunjOA[n_%бF Oe=uK9]C ]RDDDDDDDDfYJ6ojOM 6/SeG;oߛq6ܢeSa$O_@6y+ xg ;Yvm;z5|&ۧ/m ʝ0l(Ulpjm ܋ގϣ{?9 /99~Ük9cS^8¼7 0[.ܼ׃6^λ#i4RUDDDDDDDDVosƍrƍ͛7bx3sL ~6mΝ;9~8oޞҥKSn]4iBɟ?ɓ'ٹs'6l㄄?~J.MӦMSޙ^7)jCWkXBBBjzI5!>ó~VZEYlYIF;wrJ 3,h-[F߾}Yt)OIFtK,o߾,_<"""ظq#gׯի _r1>`ӦMDFFfz˗/OH2&nL%Kdh;{v'KNGxx8ƍԞ{YӃ+{C~.\@ҥSM$İj*^{5(QhтZjݻwٵk=+Ν̙3СCz6FGG3qDMl 0ӧO3lذ e޵%%&O̴iӈqtԉ|`fs¾f P~}]Ʋe_ppp7䭷"G}jʒ"""""""ɪDݻ[.qqqIgC"0`@Bx]?{aرa0S>jJYf4jԈݻw3zhvލdbҤIxzzҠAt`2XreEDD0uTJ*E2md[2m4ظq#f9a[baaao }EFF2ej֬I&M2-N:1gΜ^vvvt9ӎ'"""""",N4& nݺ %~l^x#FXud?SBq KOkhh(/ܹsԩSI&W&Mbذa޽_PBt-~j%M#""6mKjժ*/-2=NPBQm'###qqq03x1giXj""""""Vh|psN\rYÄ{ g֬Y@urrJH&>س1#8pիWKR5jTBQUȑ# [:дi4Ƽy8zhwppYf k 6~Ç駟2iҤWlJmCZcVS~}WAQ%Ã'2qL=Σd{&""""""O{nݺ&y{='/&F{>ƍ}6 6f͚iz}ڵgɒ%ܹs79xIlْU͛7gȑxxxйsgÕ+Wؼy3'ODRVbڵ+{}hQy燗F'O}vnܸd?{{{t钡1f?ζmɉSL C+-RzDDDDDDD˪Dcpg'e5;è!;vJxܦMlmLiժK,`G~l YܢvL81!x.]P|2vzB  xh[޼y;w.5k@hh({O>%k0pss˰xU1c'N 229rPL ?WFI=I/KԹ}k;wGߓ.X5?}~ĈV'/]DҥSFέ[0 \z5]pRX1f3\~=Moٲ%Cpuu%444||K)R=z v43~xf͚a?CoYV^CCC9y$6l`&<7o^̙CV}u۷odc|ٳ'M6tx...̝;-Z2ږcy2Ν.ȞdV-wބ8K'mLKN=/cbڦ+z[&./CO8f͚1mڴb7oޜQF%$O8o;}*ɓiMo,>ɘRbI C3[oEn裏$R_F իd[HH~!]veȐ!V'$}}}/_>n2Xb .Lwl{IxKk[I,5*hi ŋf͚])S$C?&_|+i>vJغukvEppC8qdNv` f5&""s&]{cǏUs4j6Σ>6lHn +5Js n駟=zC=Noٖrvv_~ιSZJbgϞ%1ƒ%K:}UC3m4/^L~cǎJ,((;s;wnŋ6mZƺk׮ErI۶m3|mCYU=,CwLJ6m_s)ƌÄ P#_{a>N> ٖe}Ymۖd)@TTk֬|xxx$ YhI&iA E[fONYuVAdd$zhߣGfhLnnnG ҥ W-ia IDATܹs^:D߾}$MXx1>lŋsF#M6Mu'V7oLpB)Qfm۶1|pݛbB9.. 8pѣGs…4PNS|~޽ >~aÆYd(\pª!gsO/rҥ$CCC1,,@ѣ=z4 "000ٞ\Q9Ņ-Z?L9Hvjf9!fI<3gdׯ899eVV1b={$..m۶ѧO6lH-Y&rΝ;ݻ~-[pԩ$L&~WF(6///XtiqOٳĐh$ // G'''ˌ)3s54h7|+6K^l2~ ֭[GEEl2^z4(ׯOXMbŊ &96yX1&&&ǖyfOgԓjĉ?`V ~-ڵ`={ӧOsiϟ[0Hf3k֬!<< &X 3z>|8}rTBzm)0:;;;ʗ/ϰah֬< /_NZj :4Sؼy3rտ֮]0`...Z~bSNk׮ZZDDDDDDUC{豤.-IۦMfΜINȝ;7f9d2%=gΜTX1f3[neرr 8GGGёAexHeggG2dvvv̞=GGGL˜1c3f SLޞٳgo߾4"5W\yh;;TK=ogg^FDFF2a&Lɓ*?}J2HGV8%J`DDD{qy6lkFǎظq#};ŋgĉ4l0CKLL t777zP/j'''>S]W_}Ş={_~gHLϟK.Ҿ}{/_΄ dذa.=a5+VеkWra}"""""""4R'<<|K4Kk'^s\ xR!'32$\Bҥ<|RW^gŊ%F#7nHSyѬZ3fp3<3 46m`oof%poR{Jg9r$5ѣ?ϕ/_;wZvZݻw_ȟ?? }]fΜv  HvΝиq Ec2m4bbbԋlޞaÆY5|ҤI|GDGGgXB=ڨ<ٜ=] ȑL)D|1000"J^B2-P`AƌCXX7n$... *<{{{ڷoOҥY`7oʕ+%1Fiذ!={rJh!qvvёǏ']juh69q#MK{͙3'Zzh{fX|9׮]͍͛''{ew҅e˖$CiҤ :t;r6mDttኈY%K׮]YtiёnݺYUF|1Kg 2n8Ott4*U>HWyFƏϩSؽ{77nɓz{{ӤIj׮S.>kk2r_v-x{{gɱ+Vɓ'l2.])P=zCxzzZU~ѢE8q"_5K,iz/DDDDDDDCEDDDDDDDDɒC5TDDDDDDDDDRbDDDDDDDDDɧDXMFhYhܽ{wf!"""""""""O0<,N4FGGgf"""""""""b0"""""""""b5%EDDDDDDDDjJ4Քh)("""""""""VSQDDDDDDDDDDXMFmv ";un"$$$Cy˗gggJ,ݡEDDD4'/\@ll,%K`0`023.yBff3/^۫X)qjt !o޼jD AVQ=g=yJ\/^#CIwMq4L o~ғW-O "xzUnp5,J=~/3j,nMR^̋4G'}ĀIRTۊF}lj :FJuj2ۛ|uqj&n^FT,ߺ_7|q?1:l:w~gr CיxLfHrDijȊ4Z R?Nfn6§'E%?`9\t=3bcsu )C[yWa;uxvQIf}KO#ʐ-Losf4?; x6?}.]W [˶j`s_7_oԭ\Ӟ7'j0s6go Rjpf M]:>CF yW٫2nȳxDpt0[ή7u-N׿ˊJW[^͉LszG7\~=_A|Ԃ~ޤrA[\<̞Kypyڕ'wXqX1!ٿf_nɾ)X;tx}#ʺB9)o;՟XӆSrS_2k{lթɓ5<~3v;o?3y~b&ҥ|ex_AsK,G$]rfEDDDX?㋟jl Z>G:k{BG9_%J-EF/3yӥG?oT/_ "E(Z-.`X<QFRcj/GOguc W /"xH.C7^m҈g[v{U!aխ 5XZ0K%&R`A ONݑvy[?rӻ6OJxX9cw6aTO=Z6Xꔣ*ԡY֋oycʾOiҴ dԗ)ssJcXZ8]{805Ex˧ =Ƶ$ky2+Aruh7|#1ٕJ%,EE,hƒ<*\A1su~xx\fr;ud6?16+F3wb2y v~߱ly?yטANHئ{t?q+ ~gͩ1w=]N|&oun|!L Qs?Ƌ~)Y>%LY=boPγ5}bMw0W|<(V`JOIio0tTY:EY9_\DžCl}3yK4kܘf/dA4H_佟P8Y ۘkݥd>>Ұe7:%櫍0q~3d^n,mM07YN5{w;_8~8K&&&晒ٞLz{jJYe|17-}Sܣ:3b90.~9 k=hX^O,?$&eˎh:C]Sot4u|[: 6qьWl=>v>cGMqOmLn 0];0f]"2Ԗ.65:|:p x;Lϐ aGі2]]8Pu&;Bl'>^+  Æ?SUw߷IX\>qo?ϐ.U3ھ: 2 .v4V%S/*^o}],o]s\E`b˗q&V!W {C60]/6;ߧX,p":j0q .|D\6qdhm|R/:5R[؉"YbPa~荍"cҼ:tK8~.EuJW0q-fF0`NsqYL^YR2Ec CZY†G}eϺh'|c׌U-M n@Fk,7+а;18AT=G\ 6f"_ \s&U͋jY\,kP֫ } c|;ylϬPjפۜ޳Ʉ䉽ƾ%_oZC37I?w$?y7e+^*S,?o_fΔѽg<~p]Sk:e^&`5" 1+|ϲj{ x_G w)E [\-jwl?1ݍ&]v_ k#w_`$Fsm=|MN%FGr4H*aGyZObďk\8dEkLzy|_tL7moc'qw#4)P$yLXC6Icء aZ8_:l<= ;[gχc(d,} )OzJ]6b68c{L0 :AYp1]/0d hDcq蚉  Q+a6 Ng z߄y0uz. Jp\4V%g Tjj.r]lP)ٿ84ـ?SAO]6Y?a07Ul:p?)tmJjynmA79XȌ- F?ϸ8{l8u:9<KFX%c+ S5þj 8k7[{cd˺ ?k{wH&m3Kڎ|kHt-^M{:KF=;đЫwO:LfQ[gXvhKu?٘Ő5ߨjи~]쀺U"WmF/S.fe4Əې̍qMǠMCL?b1_g͌9[M6O덗 Pߏ ޘ|z}zo]#2kwa1=c ԠҸSli5QVtONa4>:ѥkGVpw-+sfAklx5x}j׸{ԾGM0GpM|T|bNg_=[|+%6&WNcfPܾ,>|Hzrѩk7x%:óElߎo`W˲dbvǷ+ֵ3G,:(h4`l -ؗ:ӵs+';$'nF4]\VABwb֧;>c!jhvjTs5Ϳ'T,M}0+÷j̏[[9j=Pk[~î#|#H:BoVs>V.8 \ 3DDDD6t:>9ȟޠOw\2{GoE{dmyz鉧wwqrJR«đ2rj c_mO9KWCFEeI?KyatN L:Nt.}?&b`1w/s9,sfYw(^ OC(7@8|C'VUͬx>-MݗȲfL&3 ȁ0ۿ{ٕNq2 TĄ'1\e*mV4#st$fcvw %}_ܠ _UU }ٿ;ެbԫ홴m~79sse '1\L:p˚#ùzwSȓ7`1b 44 5e!8kҸ<%K-'ϾxI<>i&JR BxKr]=ErT,zT"7Fw Ѧ93/7f&JhQ<~xV?Nr0qgs%_=)j6cr){b,Suu%CE!k8gs0ܙ*=dLђ~XJ=)wd4M0bO K}hvֳ- ~wx\6,}oTQ#6`wciSi@c|ߡg_MhS~?C|e:v /7%=>% R'UxKZ^/QC>_Go}z/3dT4qA.p IDAT8W σ\?]hZ2eP6EB;)HF\I8W[Fhm`JClWRDDD$eZј8w/<9&ׯ;̊`sv."KJAł]K[{Xc7j{`(؍ X@AqwARNyp=wf2k3|`zܲO^ġ"NM_F&>l{t:qǵ9uNxVY//wfnyy's>_jR]Tt%W {q=p}lئ4fLQ N7WƬTTTLjGu߄{r&N#6ԂءQB^+N?S? 7(O&?f:۴Kێgw̩Wݚ;n8/;bJZuM4^Θ_?g,zU}O偧ƥӡtCOY8.!y9g]jg]S̸cyޫL?mZI=}K>:_wtl۽[VȣO|6E6I|>Fk.}67wy_y\xrRgf N?5;c%n4qnv82ǭb8t<82Ʀ֘yfhv}j\U8Ie7̺7O.}XzhXy3޾=9a½ae f{s%{.>`=AN&u0d36S;g|8dS:-r_TO﫷y┃srdnųdyw ˺L]5kNSMf߼k_#i^U|鞮>S_쿟ՍݟN^?y&\}m8I%ze]yVi2R6\x[o1ɤߞ_iu6\n)jf5wdL߃ra6]ߘ̬4w#{,A'^%qb|:۳4OYwV^{i8vi_ 3Tv0Xcy ٥Kz8a굯,m;uJIyS5߹6 >on^aǬLz'ߞ9m.=}*d o83 2J,.Ugvs}sL3)kV-K;r+ݳ^Ϥ{;(7_)u_(&f+kNӾfɆYp?e{mɗ{ ̒m1lMnRBY{yY9sST:Wտr,9|c2aﵳpWR~IwʇN dil\?R9vȁKdj5ʆ'[ /5GxDN:Lx!Wtn^]l眴<)MMg[oܩk:g_Ŷ 'AeoQn9kv'>~;1(/==ڏ矘^'?}#eFߘ[G-^̓_?ߝ-7ԙ,9fkxrݠMJI28'mzc6;֩Z3z4}O;)pdv8/kV 뭙?uBR~؁9m sMӡF9ޓkokNOg/g5MrEj3~KeSˉo])rYgWGe\Yzl׆{/\s3'?n9;;V7_. 4~**++KYe*Lwߐ4^FN:oZDu;KdѥeouMo_Nz2m]/Xv* n?!Gm*?%kuLiyyb8{q_9i}><_x;O]{V?X~T4_4%uX_YC(5gz<'*2oqGψkөu|Sye>/6w>{WawEъf7}wIf_[ru;zS"KLo?9{L(k2VM׹KIρ庖+O/IMڤR;62s1e4.<&ya|^ M["VKRR>{ s_Wˀ=Ehf-έg_gy;FM$797ޒ*e~AK7O)s!ɎKyn2_zmwf_ui߾wP 9+s䋹:wmj˹ r34WR:,)_ wO{u94w,Y﫚ڬnzKfsϙ3Os#rO4{@~ဟ|~ h(eOȍq;keVɟm%ijxakzOW孇̫_OKv&V+ ΐ'7(5d ?WJFEtir҉sҰelA4}wG854n=/98{8\k9>'_~Lv9Y~sӁ)n*͕/{9gOyOSێ{Ua˅grn]S4kI3efBTVu|fiH~U9pi0~3]y!yd29fy4bլJ!>dt>'}{>ep.t{6i2/ސS?Jr,SףCptMIn~p]ApE9c?>|4ethDTyZn89kbeLRYtv%@O_+ :%\rgUʚCV-Ï=)//ψ#=)//σ>7޸2]"ܾQ1K曳jy/'l2oO6?P }#g6/_?~hl]cљ? s̼-[Ktտr5>oz;-[(mY'8)=?9f7Slo 0G:ooĿ.kOu΂fd)CI}³vA9+Wy瓯Jk.ͅ9kehODE>|9ö/A:mARQ^7Sb\u\~nYLg~#f#nMMUs[{~tƾ$쬰o#۱~y/'l2[or3oO8aubzuw6nC`7KywI#Zx/6+sݳ~f94N4¯˵Ƕ \ls߶ 7X 8c壏>alaРAU7h= 9E~{0[[fe87Bh „F0(Lh „F0(Lh *jsC=ZW1I P۫}WC Bcu޸vϬyyg<{ù#2vqgW5?nW<:_䚍*g]3P{h ^5o' 8M[uJ۝G>/I*9/$U#nӢq4k\ĺ{4[ƚQg56﯐ï3KxjpY߸>zb^vsPfIӱ厇g5ge?] r9s|$_2g7КIy]eK匵ۤRJ}&.stxzפӾ_F3g/Yuw6zVi?.=r&}s[gmJW:LUK`Ӳ}V>L?.]fjldr ,MJW̠u)MrmN6Wi6 Rݓ}'`}qz-}0,5]64n>(??>IRɈt 0`Gw__Yn 4_gJh „F0(Lh UܥC7,OYYY4;m]*km{x.{L⑓9,͗]n̲:lGm\#yr!s%13nGWQ+-X4}ʒd@6vl1xͬyr5ǰi8GcE?l |^fY^/Jޭb k _>FN\u=`Vfl;\?zlq)Ukƾ'~(U}o=\,K~KN?Ef:{ -M1̲VRoPCcYn& <7[qn6K{-r{fz@5䮃]Sh@.~ԶTCCcӬw?bkH@a99&4 @asFh\7xI1)G9#4{.מvActeKZu? O|Gsǰ/6ݓ{GN͔7Oz6mE5ko|F_lT0;Xl6Y;|4"||Ym(vf*2ςb Ey|oAܟ_M|>z[R̿Yl4*B9LtXW*'s#'~sv>Uyo(8iyj=L QsNӇtInS]#{Cy)R)wi>IRa˶Y[d{y;i>6]: hM[Cr[ϋ-RJYb|튿[/7O&-2_ sS̓nvv7g*ΛwMWX,mj /N{4]札V Jӆ Ӵu9*TJmNߎ9ge:yj]77˦ɍwwnRe#g-݊O^G5OeN{} >$w^e+%eH҄-{ueҩqƾeYo>i d͏bKs˦O3~(EuS=+mS?& gM^ʪޒwv= U*\.˿}ry:U6^yղ|gr䞗˲RvY{,m'~YWr ,rK{6̐[̇|d}hR:'oY.ht@to 9|ıo|6|rݽ 3`?jC_uqYq4xy|"wÇlBNrhGM|)Ͼ0.co&|3&Ý_e`6V3SJgGo]zm:fP6zRsxF)+Ĭ RTMN55)<Vi8\( RJW"O^>'wW1sNRJiH HKs/IwIFIR,^SŔ"YxFcRa_lB\poͲHYų6捫.w %gն3I&tl>Nc/KcP.Yt&?uly_7e&欿=iM|ã|uiiWt=Iò{ewÚ$]ӻGEd)%/*9 _ IDAT%xFc`Esis&}S;akjuϞ ],]^2*˻Z}#M6n28\sZLaV\>.uti0_hy)+SX$}z͝o<1'ZU}ʗO+V*40sw2>z3YY*Kύ~}2H6o6~Jt\ݴpo_MfZ=ԫ,цT3)6>V~?k4~8K~¥̷_mG ZncTޭne7FzX]sΝ/etUycu9|5g?ݜ53iu({v{=gov\xihߤQ?zlyyW?x`Ymxv.Sn\ ߳s%ؚf+3{l 7pcm,-|С0` ~% r-4hP$豎 „F0(lUyrŏe /\<;}Sިo˜'=8^,زq4l Z;-=kKd7URJ|547NWLVQ+{gZ{8Y}E>kD"fi$䐛b[gӥWO̟sQg^ZMɛ$u'`hlr9rܺA65cs>kf^>\ ,;#g7Lyx3uAٰMW^5ݒ^YJYbYaU3wϙ7Tsu~YykLZsY#`P|өyk2i,<]G}䮜IYwU{tHnG Y@.Kr=m[屣eޏ6@1u:lr_c,|v` <W,yKd5'I ~SECCn%̘VKeff$tú;me_uoM4 o|fVdtyҷSeH^zTS\c櫜';n>qu1YjӍi:L玍32+߲9;avYjs_WO.5_?*_s68Ù eVSݟ/M7^+T$UU:=t~Y|H^{2fD\toy:4+>fLroZl+n:=|:)}f_vBV-W^,5.19_\q4RZj>7C1!+<0 0ۨuyeZuC.:似ᗩi* Z=plYyt~|mqYq)5iȚ 5K)52j1prOش9s` < }d̘135*555udF#ԣYsgjPf6|93S*2iҤ3RTdx3ŌFz9ӓL|pN;zhz43667ӄFG?=؜x3\QGz衿fgI&S?nܸt Pf?مHh ~-hzV3KZšuq(̌FG2q̘1?z̮ POZjJTWW:R]]VZ[O.ʼKK/Z}-f4@=ѣG^xᅌ1"Guj*ѣG,[B#ԣ={~ „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh „F0(Lh oȑ#3v،5VߦMTVV[no POFI&eVT* h 3QMMMh B##((̌FGB##(ѷqZ_'xbk~h?\Vm`6ug3|AC=F4}zS38Js1Ӽa(J8#B#ԣ9yKyyy<ȩng}2x>.9{Jy䑩{:js+eeesGp"4@=իB=oVQۄF'ZJuuuJRkeeeeNVje߭VLWeee^zbcm|UWW祗^Jeee3#/BFѣG:ZjѣV-QϞ={N @aB#P&4 @aB#P&4 @aB#P&4 @aB#P&4 @aB#P&4 @aB#P&4 @aB#P&4 @aB#P&4 @aB#P&4 @aB#P&4 @aB#P&4 @aB#P&4 @aB#P&4 @aB#P&4ޝYU~af5E P\T4ʲ; ni{b&fZ(nZ ý?6=F~>>30=ܙys(Lh S4)@aF0E#P(Lh S4)@aF0E#P(Lh S4)@aF0E#P(Lh S4)@aF0E#P(Lh S4)@aF0E#P(Lh S4)@aF0E#P(Lh S4)@aF0E#P(Lh S4)@aF0E#PX {Q-ܲ17p4P(Lh S45N'c=~;SNm]PPǎS[[uYcxKfh|RWW4nX&N<ݺu[e<'e%QƷ~;ORi+w}b.,}>)- Z4N:UX }%2ҩo{hBx,ȧN5wP^s6{{nYRԠy2PTݣ<7lih+stZ sd#wZ~n-;fNj_=Å3ͩ{bl6˶h-[rǀsEkus߹+['g{rj<0m1vپϚY]jB:uwϺ/mC3 cqc :5+u7=?<{rba~R`θW䐝dgoK3i揷ߒٲ7oasCD+ocJ=C5q+IJン- w90Q6]Kro<ߜ3ócUҾ]mP[g2H%)5 6H߾eC2ԫs5yKs9sd.W3;iz1>ܽ"=#\0G_=+*gvmҦ*2RIF2{Դm#Gޟgʀg|ߙ(QfR^ %+ulmjb-2lyq6 ci2a2:ᆛd6e.yvMqawFݫؽUұ]Ǭw sμm-?tZ'[7ӻk3M%-m=gRYqwoرdrɏ6H$ٴOZ+qQtp =JRW3`Yi[ﲃOFC6;+ldRuMdA9pE㜯T1=6*WJ6"[vK6ܜul>2/{ҫ2^f$I ʞ]ͯ.7ӓ<:yvV/+.njȜE)wIma.o;oF&6Iy?M튻ʩuҔ,>c/]]V;C;o92P9=H^99glf-SًyؒρTR.W^l_-̘e++9loUos^X?l6Ygwפ|E,$7Nyҽ:)5].|)Z-UYX9Wz_)uɷ "ע$3*ǟ`;%C֪JiݳN3oeadElR*%)\.RY&]{Ulm^*ԵH˖ɻoٕJ\Srݺgy9wmEc#TgݶI-Wg̥~ke{[85,VJ\J^~S^?]]$uOߛ<]*E#HhL^$u/-yٻ|g\!}ʱW^]v=#Cobz\'Yhݜu:??e~[8搜yܤJ^co7=&S}էu^m\I7,_kRIu0U{PF; WY63z6Ϲ9χu 7ѫg+_7QNeUJf`J}Zuc3ʣɟVe滯晿ߚ+/!O =}vͷZ}vNuy$)MmOd6No ^4< = kz489o|ou7MfgV&}2tчMͫduڧ?̞k4[sݒ́Ir.g[gC#2lruSdJ9^ޖsjCr`:eN΁g4^}WHiYS ˕usRYaigUȨmkaf">`Q&_nnG])7\>9Nu9;V]]alٺY~m0Fm?&G]k~3}SrZ-M:˩?*qTm9~~{ܙ'godFղ]alYqչy^c r+͹Oi|cyc};gOʲ_m.5UܻZ,7+v韝;lRTtcNLڛ%4 4F^{=;3xˋC]& ^V>zY\ͼgro X`Ox3~n'G#?%iHv{U}jXFIM3$qF#8`ls̔9[y>Qۧ\.T*R*r9۷_`Ґ$4jX[['x"]v\>Bbr&OΑ. xKBݻwϣ>~8oFc %}M;GX4$- ~=z4.x,ȧN)@aF0E#P(Lh S4)š~Ǐob .ۘ,\4VWW7qK1h S4)@aF0E#P(Lh S4)@aF0E#P(Lh S4)@aF0E#P(Lh S4)@aF XSSqc_r ,$IENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-scan-lists.png000066400000000000000000000743451501654372000215340ustar00rootroot00000000000000PNG  IHDRvDsBIT|dtEXtSoftwaregnome-screenshot> IDATxu|U׍% `Gm% DZ$$lQP@@JPRkݻq~,~>q9s;^>|瘸ȭzpР =]bADDDDDDDDDDny#6"2jc>$G6WŊbN}KDDDDDDDDDDny΅Ј]6n#>> pڥ֒%3-Ngag+""""""""""ٌՂܹ?Z4,(#f$av""""""""""r1 Áb͵LRbYKfy{)g ;?9N, ۫HMé""""""""""#No"Vi粈H~IL@bbGrj1FOw\]]nxNÉjө""""rqډO劥 <" مjU)S4qi<ሡX1,s+bpWr4(] O7.%t8?:Iyxܓt] ;8ۋPB"ٻQYDDDDNFXX5k֢VÁә=P3R8v>tDRxmDm#T{aN"NlRԫ'ٽj1|bZm $VlD5M$as-DƷv""""7Dʗ/nrkpIVe#˗/p\63)1mqgْ`Od ߞ:5q5 : iRE (w Խ.8&Ջm~=hZ?eqq&aa;jSưT=FNNk;>.tz~"zQn%l%݌dgB">]zqob[{ ^[iGr:a9N\\\ZN'..8δmwqrV98.Ϗ$ `\,XWXyes^7+-S3)b`?!^*EDDDfl00a%'FXL>.E3Eymqkҿ<頵 7\=nYxoeE]$[Qj^f`(""""7<6 ð}ܶmehڴq&SJu˶H}/xN ðe(08"#1adɌd$^zȟ  9yÆ0cCo )sA7Fl6Ʉݞ2d”k+IX^\|<`{wݒa򴈈ܘfqqxyyh߾eLL\l<"ov g3w.N)W?>'y1 .f6oeQHGb .$:2Kȍlt4SVݚ&8ͩ]-gƺYys99*lg囒WRq=j%,, BoXXӳX 4,՚92xʼn YH}/x]܀t gLFԈ `LF,k?u=dbgU-2DDDD&cqV~딦o,2 w_o\Hyɂ[fЉ: |'D@v`I|' Ym+""""W+gΜt89LO8{ ۽{Oe㭍0(δ/6#,U7ذR$ """"r1߇(7|ni[%c'qw .mgg7>_r,f3&?~e\E,;)sdV5kvXcve'%wp Ke.f_8&os``µPje~enXqH\00X]b6DǑ`+F QDӋf90z킩PO &&RoY1̈́GDr)l) DxxƧhamDxd/9S3EDDD&``$=#&\ub{W1oR69r7o$Ƕ{>jL&tԩS'_\b )V,KWԝr™_3i[:CV&Nj"""""""""r˰z &m=#r7GUjP^É@"kO&cf7qVS;HZ4UcNg&>֍A)]6?ޜ?J֜;^Ȟlb?BQ!<%S@&6m塙nǞpQ^ qc l  (Î23HCQЫ)~Q>З|+tYG#ɍ[uWNmgﯛ hKǪG3k3|ϛ[Rz pfHw=gcO[ލ5De3pyE|5o"V9{etDDDDDDDDD䆔?1(`w6ZEjU\TO=Ix~Vl(a>N3_c0^Wvl<^zL%ntS|S'Z'_fOO6$CӟMKB~ieI,]s^tŜ'뾁;} q+y͕=S󜏈Hz׬s̞.Ç0Z{^GFF2w~l6~BB"{ɞ=Ӆv~Z4okt:ٹk7!k̙֭8 ](_X||B֎###0i PdΟ'm ⢮m{\'NPFm#]f0p0ʕ*ѦuKn~!MK\\le?n  @^L椤$&&3gu0 OrQ:ZR9+\}i̜5$:u@W8~<#ݔ]0mۆse:\yQpww+CW9i!"""""""RXel-)뫓SgDo~9R2|iSem?aDxi$C՗ۺucvlٺ&e6N'SޘFڵm_}M'0YUO{QiLÕg̜5j/q#Aڲ"""ݧ@ڵMHHd̷hڤ]:w/ק7A,f{d_7’e͛3뫑<$2|`&N~#mfh^QvE1Z4oƔIhP^۹ҺUKLϏamw|ڵjnYs\\i۶̷R5* Sܸ4\x-| +V@aC˯׷ÆlcHDDDDDDD O-2f+s9s9624uߜҷb6f2Oy9f^q~d;ϹOZҙa=,v9vʼKi+i԰"Ccv霫"""xٿ\m]0erC۞}n +Vb}GL8xpe~n\+!MЪe ˋmZӸQ|*k1yx&O_¾f"""""""rȲ5-ݖuAK%HJ->ߥ>>m݊jժb2u_5)Y`n٢y+#n+o27w.s~/mvrV;'sq2__vl޼%u;oj"&|"6mޒSs˃Ӛ4eq)e'NӊϜa-4n ױsڍŜw)YDeEݹhڤ1ŋd2ζ;xb"".>!w\H P3nC2?1$)'&[6Ϝaiެ'0sձXzuh.lg gjٹ^ʗ_}{UQx3SJPNSOW_ЯdigLBUj[[DDDDDDDFef-!9qUD׹w89Am~[tKo̹!~ WYo_z5m߁di*בn۞f+W #%Ar,!K}=wEecǏ#g9s`޽c b>/۱nrsl&jى3ςw:i\*~s5vAͱ'%fxVoqRڶɷ&"##y/ Ԣx۾ymOhѼ9%qv;?"mZu~BhhWb2yf;m^\Ϲȍ%7bGތR 9퓛Zv оvܙw7 @ء]s?d 8y2Yjuc甯/ClO傸N+饶x-Ӧ`G <// i3wfKؾ}mڜaÆ}O}@@r)m1W.ft|.v-} :,f6U?%JӪe ڵm?JRR FY4n c 62C͕?sghh >['0tڙM~DDDDDDDDGYE<Ÿ+>m9t0~c^╗^aX;;n"۔|=7gR> 9hBҥ3Hmvǧ ?Qql24 i<$} ^MXx8>1""!GѲE:t6mr&vXXXoYŊW X/m Тy30 Z!0wys`A yb >>>zոQC<,`ïyb:owiF)RXm?u8|HNu70W8~,9,2WDmYl\JnNXxxbfڷkK|;>>> yb`ZNyuJϿXLbbR|ÏW>11Ͽ'O9^O+V\SV,:ezMDDDDDDDn,sW]i)^2uTع>>XVhؠC]V`Ϯ*~2e^_DDDDDDDгW"ׯŀ,e762l˯gCA9\3VgΞjD/gϝW_%_t:3g6 ?:Wl,]-;}s܆l6SVMF F;{lڬ/s+Bnt% R.;,)Ul Vժ4kw˿yfW9TX7&M_/8<%KCҧ/pU+T(˯t"N>nϧEDDDDDDDN[U_0v""""""""""rcRYDDDDDDDDDDDf`V.TDDDDDD$LOѣ6)TnJo EDDDDDn(V'NKLLLa#א^E(W@>9-:G9}.] 4jҔ,4DDDDDD$On &Sa$ׂacq!\]]4iy/8:APՖ Zxqq)p:\Cf 9~h ¡^ptn/#Gv*"""""""kLL &L\@tb6[HLL':C^<lm/zܞηfK-mCZ4LZ-5zF[qOVNW%g<Dob:ѸNuWD-yomzwz`%*נn;xp&lY1?mW%bUj4u1y<ӋY ]!/1gTK)%IQj k'g俥/pOԮD@@4om$$xfF*[ns c _O(_uYY]B>TaF4mޖ`GO8z}/>`WōDe*ݟgѸ's{WՏOwQLrAhۈʕҀ3vcؿꕇhS/AhENOD|+dY$kAchprl܂UP= SiOU]*VLP6=k'jkN2>'jV|Х*p=6Jst<ʪ еU#U\jПq?xW&p{ qL*PISB&4 d{?n^iC1ӿaHTH&3hN#=|o\AiuX=n5F{$??,ֱ^tJ˗U &7˘wbՆ,׆C0ͤx͒~/=x⃼awS9e] U}w{>V ; fӦq iẆe[EvRN!v=gЏ<2-Gxy)<3sdݩ*5v qœo\1+bk1l=~e'Tc ԋѭKg]<'<îOڼ?agiO@Sqgɕp&|8:h%ϼ5a,Y*qZmSy:bǍ(e c#h?$IX ?:ݓV"ĝgG/g1.Y̕K>Еo0?zHń?ͩ{ N 5xqz$p|ÇLˮe~)-ET~h.=%+`ƀ<[N8*N9͚ٷ(>f8'Mk{K;y8\:ٶϿ8[n]<,F{׬b f@ "IO=Y|ge\#{Mr23%>軂?S'Wtc|vCJ` Nd mA?Jˑm9Rv'6~GkY+S3^cӶ# _=} LnB߇`s4GWrd93 Y1-EM9G8a c&}JY:Upw WJyw_<<|Lc};冯˱!gfgzLcD6 jBhGK]{+KHK~iy2frL$2Ҿ1A.Рa{60V"Zj}:9vUGH.U2}BDDDDDF$aѐzSvbB;ɏIQ mMy۵?,߁ckг4Lɧm;~lgՔ kn#!`|/{4ۼzz$]~Ղ?ZcaGӿrwlЁ)yFӎl_Z`N>(?jjK fhs_e@+y]a3.A¿QIpif/i.{*u TO]lW&o,GR&5-$@>\3TyV׷y:0m6zߑR3Wjիe;?a: ~1c;{g([8.@paK8m1ʷe#U98=Y{x~a Ǿ1giŃC`Jú^nc-ZZgcL9lݴ$adyxrɧ -;Iƭ:Na-y >jі:>MGJ9˩,Tl݋z-hlURUVLEuQ ~;(RtИNmZȎ/<T:$=x| >&o`jl R%8ϷOQ8U,@_ZÒay< y#\wg2 Դ}=ԡ.T5 GO_5wz$QLߕmVۺTvh-tM9N ̖Vtqp_O D&&M0̔-V^ɚkײGgeŊ'G?b||ųP/xWjAߧt̀۰ .fKVZ>Vk$KRiZ[o>撩0s0AF|k\ߜ9;5850J* IDAT\}7{WRɬc Z5j4ZI}~Mӳ7W>7OxbVwgoj->i#ԷOS_S1PV g~N tNnսr_[|Q\c/>;`i\<{1vؿ]f1u=jy홞Mz3^w[js=ץLM 횕J2-[>;u¤?语a ժXUSΏw/[#\we9u0 CC'j ?·~N]t;sMͩ_[bQy79V% r;_tSn*)ʜnU3OrdHևߙ^4<|gnd!gʀvX[ߜz<;7y񽙒1ysμJ,wr䞑k5TqI3-lqzIYeņ2d3p_GT'!oeá+gAg 3FJReA<(_\_/<:d%ύ]aI:tL7ߜyULV\ /-KFmvny(Gn`_==c` ZuwW?S3!W]~.g鐎xk1P+Jùf~N jӢYqР ^uqޑYg/7d<~A9MreE~Î~^̶[,7 91I]JTNy0wӾ>KG6􀜰y{3}2'3~ktM] Snf _& -mv,gsjva}f\d$-Ֆ鑺7=?Aoc<._}tYK5_ghl}nY}}%{7R˯N9?O 5#7ʬ'ɘQYup׌߳3լռP?4hsW瘃wIwϗvOӫ/$Cˆ}~ܧ֗fN;l#sfr榍iDž9엳陿f4f;o\ܴq;;fNwUul%ӳaR^ߩavvkֽ:3^3!ld.FH 4_}U.=,YK5<9C!V_K=kl{9tld]5mRZ<]ƥqHz~,aj9[10+oΕ"{esSmcP\S+{Wyh3_`͈K; ='w=5#˭r[9[g仇,C_9uwm~|ϲul'@5=m9|{RT&<'ݾ:( \3߹:qAy:ҫnC}Lo0u5.yD>xI%2lK}r/怑Gf۟ӣobS+Ҝg/7]>C7/OWh"fmGg IC*sϿY"'w\,kl\sƨyk /{O~l}Ylsrα$o65KYzͲβTcۋQQ;#o:r;d~僮_ayq|LneWM$?|QF~69st9^sNp2ar}[>C>.>T] &c߭K+gn5v~;9h_gTgˑsEgeϫƧCl]%K]ysOnQt??92UolAg__74ke:_RYNzK2~ѬՑg[tƇ^:>S?|Z>ca~ѯsO_oJEWʎl i,3~ˀlCG=ȅL]>f|Ȭ9S3Z?'Yܳsɵes^ϤNrKn|+R7{9Ybf9 N]711GeުuH~KdutHxyRYsr[o%2 s^+ouGdnCgm.9j7,{L8pjt;ˬ~zZǜsjaf'[ nsk<)S.']34<̳+kGϣg|#6]0Ǿe70 45}JI}`}(0o~+ sPWW:es;رc@ IͶ.q@ۧ-G| '|rw# Xj">'EeBu x|B ("0PD` @"3Efi(Y馛njq|*m zŊs wkSgʯ.!|$ @"V}E+<}rr[CZϕe!k}%sVR}7f;yդO5]nx.|t]m급yy~bK.$I-|ib&/9CzN[K6Z>wΩJuIȻ9:ZS;o>fMC; /x;Дe`b'idN3t@CƽFf6`U'ο^w7rFK57=_M}\74ZFR;Ͽ{'Ͽ5fs5M5x `Sx},R}|[gHWff3ExϷ8>unu z;("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @" zGg„ 7v|~}ӫg 4h} z|455e7e1䐌:c:<|G~=~#ԜC=d~ O`h=rʩ}ȣJs9;l~ O`hZ ǟpBN9r8cԜakO`hZ#r{]fxݷ'~x[L`hf֝f6j ;0WmVmRܾ F1LQ>-|\ۚ>"0,|jC`.~"0Y3e @yyc?ܾG) jllLZMRIZ/QWWjƲ[y<^zeSUSRW_TkՌ3& ;ڠ!C=^{mGcccz56fE m* YCh)2("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @" zGg„ 7v|~}ӫg 4h} z|455e7e1e|ʼ i3@U+yn׫ezou3 F (cfJ"So`߁RD`hjӖu`43ttM܉?:i) ؉;ڮ>oa.ǟpb8CrfafÇrAT*9fmTfOm9I:7?8yĈ9_! fM`N/}̱j}sQGպ3¨9g>{R+cأf=J mPcccj*J|yT466+cjJ]%VZf̘1U  2$=x~衼k=ӫ1C.Z_`hVYeȂBL@"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3EfSIDAT("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD`HC70Ey7nq c ("0PD`HC銏 &dر9X ^={fРA-u0 ijjzoX=}=zLŵ`FsnIQ`0~|]jMZ}aoy,tܒ_Z۬_u9{~Svn=x|s'e-ty3iY,\ ;crUJ͛SM6+w<ߘ7X)]Z]k>߿o! ߞ'˗V@[%0nܖ?\PWnRHM>#-{|Iy溓s)_Ǽwղ޷ngCd/ڿ{2l~gm[]? g3Ofآ3o`:hܓ:Ƽ)=}66l.t?)nnwrȯe뭘Ww=WީOgQ;gRsMkg׿澣?3S<]Š %-)]?ok6XGHSd{d?W'II V*x9p _3c 1|>Kuz7/7cH>ό.{i&/ɜ.)|EՉyu9hS{g|^]Ge<{9u~u,Wkʁ_~~cp1!o=hw4,A%- ln#b|—)sn˒{^s97}qo5coOv +oLgoG? V3ᡋf@cZj|uy63eSuW9c6͗7&9xdN {uGeز}ӽעYj7rM&Ozeѕ7nߟ My|Kkf%{1`pr߄l@Kg)yϤУi/΢={Ǣ3t D-U_϶:C!9 kX,ŋ`I]J:/jZ{W>';na/ߌ&mNVZOz.>$_<uM{Uwlҫg|Ӝ<w3lb٭Gz.Z6=Wδ޽+lAV_a4螮Kfec&?g֟tY9Zt9::?gbX,^J"ԚxOsʗ_%+wi.cߜ~/hz"]:9_fE'/7)2/2~<9ML^kfq˥[/f~63 |.++eϿK-崯,|.i\큌\}||e%av#gйLoT6iCm9ye\tFqyj\L[1۞}M\Hnkr Iisg+{G~;n|~.l[^]b}tzi|农|ȁپ?IwIy yGs7p zWJ=̓-7p\7sWe;ePיlᔌlqK';:{S !қD "tX *FP@U"eVA`\>( R RD HryH]3BV[YWun ZK[iˑis?3y(V yWаBуouzoSb`Q]ЬQ+|FՖ~:xHM6ϣ>)7u(Z)o:FeuA)kK0W&j0]޷W4myu_ůh]:t:U2Rݪ}VfZO]!ʖzA3L+M(^NλLhY=bO'm*^%ʚ;FdYifmwK=gSډ۸3t歳nɑXǿttrSU{PWKΜ۬vڽT9=5}yߚL̟47no?-Hcɷ8[kʫ\moWYW_uK2=ZXRY{S+/܋>R eA]=7Nhw*Zʤ62W\nٸٳeaJ[nS;f]8i ׮z Բf=vg(_i꧵~Dg5)n>dN<lY!y}3`.i6@ZbLg%,|N6OS39^p03&߬cQv:pK! ɞU:"ULF9U,=T7>{Z0T[L9{K*i"DxWQcԤ(oN#ԇkeCSnea%:8AcgkT>{)Vs<>$Cn\ϬʭWRe {֗ZB^Ńjz4C)l}vG5RuWHc}*fyQxP)o~viҭ4;6'&3u5r_% +jg˸jZա_~<Ӡe+7C_mN7yzzdz-}'jd_j?B5h!Vk(O 2RQ>Z kq픾XZzm=ˇ'sp vL.Rj`|U X|~ձ3*/X#_V~gv;)pZD[xlgY+:jU^olLЛ\jQfʝ33U[>UjXn`+0|D=4C }o^Q29U+LSF^Z7s+LV6=ٗY䒫B)h:w% k=LMT_$=щ7{~m_ʐp G]Ӳ_Ыo W)dY-Lkek.0"tB]dݏsO'-:(owgҕt*n3jJ[/h 㓖eUpI}K T*'g%?[?^3WAUW.3v٪;l"-y#aSztzo l ÷_xYs!=C%WWߛo[Y!-F) Ibgkޞx͞;T6ڷUyJyc Nϯ$Sj>yfjXKO-U dY0S/+ ))q?\͔$N~<]uL}ܵokW_dÆ ޣ9Bf鯟}~:B6$'Mf^n˝ԷM}K -2[t/ffn~ f`v:r2 s/l6n9"NX uw a3$Nܦ[Vp15u0/wVR:.]d@it:t*2"mp:8{0GEEZ(\{}` 3f%K0,!`XB đa5Iڐ|P9qԼ({e/XB ` 3f%K0,!`XB ` 3f%K0,!`XB ` 3f%K0,!`XB ` 3f%ԢsSIENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-zone-edit-select.png000066400000000000000000000425361501654372000226240ustar00rootroot00000000000000PNG  IHDR*CCsBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeDo 25 Mr 2021 13:25:08 CETy  IDATxw\ldƅp{Js(m\in~jiGA(r((*(%?{|ϽpߞB!B!B!B!D!AٹUxDq *3fr?<\]ܽ+#(!sQJsݽ}统)AQ, {E!HkY3T:GFRB젔B2}fd4="=NglPL&S%"3L&GUTB4@5;C;#;_aӰۃv$;_w=#.vLi](ۡZUd~Gtwƅ݋?c;&At]wiLu~>x9bO v~_FT 9T&,8H nwhT|.< 'wWd?O .o^;cPI83s?#Em7aa!{BT"Ǝr~ϤξXĽ(- ׺x$ޯG=^ђ&UbvHbLxnۥA㈉M"[r'>zH`o_|?^QyuO[߼ wg nswe_Pɡ@ޘ^m c"9u7kAovv@߀U{iѢ EbF ښmGDkK]?uBPR^+Z6G˸*D Q t%)j\%~tB~k0Edۑdx=ʼnKF`kkƩӡ|wt@<ނ`Hʮ&c9s?{p"C?UT"VFusRi)~rUƏk+gDFTӥX{+ vjD$`%zf ~o7-QkRCU^,H>$߰"ҊR)Q_xl @`ⶬHXҰ"HfO?bƓ܎ OSEg>cwtQO{St-^߉9ǧ1^[bɤs! ˑ/ U~sl1PKO@^S!DȈ*v;|Խ 7mvT+R0v,wAGlSZMʟЫaEyS!<ܽىYA]sʘ)m=pvvٵ<#ۃ,^*FAO7ZXwR%\o%'['7#]yЬ֑ҝGӣ+K0z=}Z=Ct%'ň!<KG# B߬7=S*Z4x<:{n?OB&vL=>gEMy4#{Öu 7RnB *mnLq>ÆRnە=_I)@9|ǛwLqgd5bGT~1父8k _6rS7a&.FFc(^8qԟS:֍ωx{K&2LWAJV+U{nMWlpbjYuz)%ټK"^R=P/6A_օRjVLG_Ly`lh 䯮}>"쯌3u.zOuxOd$pߨᖙ)Rmd8&6~~aٛDۤD(G4V);O:sσ}$]81 QQ(#7B1ᓉ~ arvjʝRyo]ݏcTU ,4wL yYR:uAR;mB!LMI=g;}P qɸu~w,BtNg(CqQwjya׀ɄI.c >e%KnZ˶1oAOtL񡟊<εWfM0m;R=kcS!0fİ+eS})aw#߫:6ma {,ˑJ:>3nX"^y^aIl3G5_lf@`F}Еah\MEI,KX%3VVmnOEhn>o+Z6񄞹Nf=xSe޼[u9#N0 +P R;?|rF_ X{xb2t޷lEȋwPg u6s2+qn۔ $@˦,epJl>Ӗ*N}noVp]M({=.̭h=YC:a>rѠs%n5?Qy)XG^-[w͗_2s~O"e_mq{#zoy#ӨΗՋ<|RF@óhޯѝ<?}1/s$aUn[RHeLsfp9Jq\cs8b 9c9;L#d-EZvL_Oà`,q hŗu?3tE2|< -AgZz fMOVKzOho>lGq~LeZdd\Z?!xHJa$fOJa$fOJa$fOJarde}rY!D.m]5gɫa~@8{GR<=x Yz#+V|.St";~&UtttɌ٤р f0WFT*H! !]gJgGU(OB/NM޷,?F҃s?Vr)ƯX6IpJӦ[&һabLv c||{<]sf[>>+Uzm{3z_\5Ľ0T oQVmLq2v&\sA:zP+;`p5εYbL Loq麢+#>: V1~t1 ?oM?bp/}r ӧ=# 浑[FCBLlfŌ>/i7'к=CcDiM{t+MARni~)D Ԧqؕ a\?]s,(O=~*\LGY}v4=r\q#ojc!4Iُ{@E*ULO-yk n>?(C%zhdM-׀"uǣiMcO~i|5+s/mL\ڽV?<SZlkHJ9|ISn-$\ΚIh cU9kzv&73B88lfS2ZV?ͤF([,|=ŀdD) 6ݻձW Wy nΨۡ6Xq7$:ij  MͤFˎtҞ%_ȹe#q7k?Xn5Kxf<4z7_a ]-j5\oR"CqEFTr* EҦ>)ERuDY DrO) E-6J.*5 !G ,\)հi3c O0!\垠"'S165QP[Io]biEPށvrybA>u$ ͬXN-z\7|0W+g48vR ^"Dn-y]ܴ]ÇZ1f)ilidWQWo?4ɢ~ݨ|e8Y f2]ZfV4wih53ɾNϖجceB>T?.y?qX2nWPN(^~Ӈ2Kymr{ۗzÇRǜ)_*RBKXE_R~KQ y k5OЯjIIN av4D"}-G5AǎUKB rM.s3!S]J #XmZNg3~XJ( j܋y{V>nC#.攉/Zddm*7d2=$UxqeccnݺwW-nm,k}.6v>1J) ϟ Oi/ERyHEګ%LiͧoNnZ)H3$(I.54By^=FuqJMeiV-M}UQ +UzAJջk:!ׯtNDFF*Zf襎BCCiӦ 'N|p_hxxx4TS:^%x_}>`H1oyofԔps3oG"ԡH:ɮ=w[S_Ӊ=8e]7O[ k]YGmNXRޞ}1fn߾h/Ɔ> {vbW%Ğ957,ҕ)keL7^z &1a I|&/* =Hy|@pĕ/xAeggرcO?ԿANY =l\>NV\ ߣsby>meC Sُ}0!KLѣ˗gƌt%{GSDȯ{晕xtG,/^$͚=y 00RP̚="WyS,򀰛iHحhܽpw_Epp9L(&g$Q|@=b6:է^'x5k֤_~lٲFS*,{m>%x6Pa|]?MLDFMuDrx?!Wܻtm[!>׹sYs͗&]7bfsmzֱ448˿OBZ ?aƵr|2.VPSfwOJ9* F*K@`mq4R|MN~UZ蔅Wj={ZbkGVyjFqU%eIF4譕m5fYfiƽc հ5Tqoe)K{wPY% ˒SrMq8,Wwfuv/TY kHTfۥBvu$V]U ,W.~W J)W}`zQѩ^zP z[<܁KLѣSNeƌ̘1 i(Z2|ZTYxRRvO*Wھ?oBΙZeҁS?ۦq`@STG(|\/5 R`ͭ0sq2sϫxޥ$MK><& j )b'yȲV<=cJW./.fwޓŞV:ݘX k UZ;|لf6_rdX.S2QL\y ÏРz -~e!ʵ9uJ=$ []?WVJ(JٻVewWW)g3_'.VJAi[Z ?un&ucQWj,Z(kGO_Zu~Maj۸ΪB>{ee<\Ӑ= Q[Ri֥zZp4Zɂ2HIFnO_ХfU󯑜!\VEȹto9OG=,mֲtꇴ>̞AmR2N.-\;1f*~[1Ϻ]WyΫl|1 \ZUӮ>hTfW6MԜ[~T /4ElT4I:g\R_rȟ7Ng\)'yՅ2ŻZu$mbƤJx 0D2q#0pޓXT΢IRo;=6T+ OlxR,OOу3c tͣ<\?wKa7: deE3Lڴ7ëpnBX^byU,X=oY:SS*( ||p#ݠd~ԱӥX!;Mq0`S", =箜ПK>/}O-Ac/X^HƉ-٘v** >x\ yfV8/yyC|PjT)[e^DUl|a2^~eQ+OQ\I !̞B!x *!ٓyB'0radܬJ &nmwd; ?mi"ۼC?GGǧ._dzKtdY=<j.Pb9,樲^8OqBo^XXSr,1k6Xʋk}64'l:2f^"sr?-N]a-0O-6ıGUT>k"VE*аHEI74ܱћbن[9eWV?*CfpZˠƽXԖaFQ[!K^Q{j@7:V-7lH8k;%-R_x_Q*a8Bf b֢ ~ҶF".~X¯n85:Ľ;T6c|8|;/]&bnP]OG=n_> 5Vۨ8iԎ#/Sf11wt2xYKջk2|\lJ9TS=S.Վ۬Re??=JY53UUOeoe TPܨnf=zP 4u-M[IǿPe-U#>j堚ϻܤniXT/Ӌ '?nRGTRvzL՜Vʢxu&)vxKCW(~l[xa|Ovl_}uI\#աtP,͋9c5z]tO[=Z/MT7|*V~PQUuS.+Q>Mٹ?u3qǟ*&EQʼ-*Wx? ~/HXf~EoN[6S:^cޫiV5DDӺTUfԫӘnOJشv'YtFa@ Y-qWnPmD, %IwZZwOܵBoFܑ]y*{׌Vl6`jT{spI\A.-gO)|=sPBTb`.mvNye}넫o/~c">.fBǶ+#dÈʆf}y$!߾9z4p4w/sX6 һVcJ&NMqVԝ@q2rRQؿk'8Cϱo jM))o;n{0j'y:,[amvTTPYly}".cKlo_~PrK2q6poL"vߣ>}s?7Q+t.CٸS.'u`]s d7y-4#^4(hEel4fr+yɍY]Y%?E+FС EKɻy:jOA,uƯL ,^Ўg&5t;o*uډmpKb Ǧѵ4=3d+g& ˵RoZK;)W v [? S.WѻAg}T%z [ܟ8GHlb??RB lD&ysO=ZGK7"+pIʕ°{:~!NVⳮeqwr.Smgۦ,!-0ge[۹ǂ}({{f?)[d݆|9}=qKzt9s Xm6[oa/s͊Qj ^Ryto4eЊhIiT Ǽf+"GU<Ӎ6Jf iƶ}#}0kF&_WRVԼ|yv3͕U5tj%([OeY|:hHΓ%lP=,Uɑ!) \έZrˤTo^ ۣ3J.R8/ [E*8PJ (Eغ.ډrmj8BUGg?Os=o;Gv'H=&pVfcH1oyoqmϬ9/D+ /Iʎ?tߖH|ЮUtT_֓X:,'?oz5f1{od蝺ǭ[h{mk4Gq:w.kt| _';8ɇtY auyBfd9bzzwu7#QjUW7(>?έ- 8[+Vl] JG F4譕m5fYn=J4[){ªlj*:MqU^k+e2a $fϐ*1qj˫ VNS.?؇)bnK-Jg堼KU[ Dʩ ^ްaCN5-xHPDBW !̞IP !̞IP !^-O/ǹs{.vWr}v?lS9p>%,wTă[>uU_frQwcge@~su`U.v]j׮RJR\9mOߌf^FLfkF,"""$\93T\V>Pqq{*bv# ,{P3m)SuY{o3,9-vp-&S]Kw7i4I: W2^Ltş9Oܞ )P4RO͇R*ۃ*ȧ[fARhPܝC Y :=xJ .wwzFa"~ 殰Rl+nj~eGMTF]VޫOaeжR&0bNeI*rn<%rtلgTZ1;qIl׬n;Y NE=g^}-_? g+|7{[NGsYLSꏵ} w\.~4TG;읽)^ f @?"RvGe*,ۻ&M 8KP>YblxS*90pcƝe~"ΒRoidX_QƇ=NukPlL }|De3q[}h"bow-z1o-cqFtKLJemLGyG0 &IRXzböb2Uu:|:7w1'ALZcveJo RŽ[|l:XdkL \i;Emɍէ;|DQէ;7g-$jtݱ/L]~S=|qYVs?2F9T2=gq|5AopsB.vo& ƺ *?GWU=Aގhnc'xk!ι|C=&y_z18@ _Ǡ&]KWFLys+ _U%t ɛڧ#w:zyۄ<@|Yq6g#u;$퍘H%i<:0}ʃمOOUgo?2F>9L*{ZP=ѳ^cgRSůBPnSJ<.a{9f艧>CeDújg_{p~GSIyuc՜f׶R~ vLhHSGޏ',+8z{e=洚¹;鲼7qtkRqsZ˗>߃mձinO)PѬڒ%i_"G^SlZ˃sWp{x|./H>~_=x[:l0$x?^caKX&9z*Sj?xN=EͿݦ$܈R#+'ӳeŨM7X} MGhQ鞧hɤPy'0&KrM&PX[5֯c{):l&(HPrO?O͠tATƃJ=X[է;[vU:5w?Ϯ|m9a q}`:L3'v @dJzKyA˜ӗ@L9f~9Y>Iů{0z6Ks'.2gjYzr?s1k1*G 9cġ"5A  h˖dz.MN-6TwI]5SIR͓۱B6]?c3܏1\Rt]JQ15+%xcYQ?aGڷx)pKvExˇ6y;L&c3a틵E.oߋ\:b2FAbm۱Lc?nw98m"GmXWۋMɷf6ҧEr_Nd IDAT>x~q~-w>\Kj=ܭ=E|RڵGxs[7Ʒ>oѢ/y ={=qՠ2oXن'``H;yx݆lN>0!)%} mFnh:FwOv{$R9#y5^͚iU"/O3dfezv}T=KO,OZ̍t@gߔ~XRfTN8fZT.+Z3yR Nw}_vփۻ ,KQ'yJ?s _&vLmo_2LwƵ'L};< ZZ񼯉>a h+1ֻ=?0vdK܋:Mבk|w/ܺ1_ >mAyhi=lkN)h26Y}# \\?v& D6*F4Z"P(5/xy XIa#*Ku!(ʊߩ(MUU!zkzgb^xe?U4 hn!b4-o*aȳ{`2)0tz H<)tEXtCreation TimeDo 25 Mr 2021 13:25:44 CET' IDATxwTTG]:A{%Fcc5{w^b"!&|s8 ;;3}; B!B!B!B!B!B!B!3,̀-`ӬFB!/X)Jm tںo޼|jk$BﳹUš0.O!(V155L?.x"F;rgJ||\7v^7[}ʥ%RkBJ3r 4ޢ o@ʉ qN͋cj ܋BI(*- Fw/O֢Rj>%^fFvLvCXY1&:>Q@ȕ7ƊJظ)jb\ɺX"##'sg<}KՑ!>- P*> zbnИ}敾"S<3 xf\Lq/XM~onñ=Lyhu^)@{z_1a¤vp S'4*'4DlJԃ[>.7ugk3/dlȗ ]iW/og5e)Lr(y,iDc2N1Wv6#x>g?Ob=Mŧ t/\>h,١ --*H6(JKG<ӂ69oQkX[+f!(V>qM<1AKtdrDyWWRl1 E,~+X4`I.,'p`^kiv(xArK{ : e%N@G&# Yѐr>̡Ç9t K;E UA)[T_=j׮Aa3&[ jF&M?yH/FqW1xvҫ\m}L S#WA1GxfY X'5Qf?ALr1T.hMyfKFui"֭ #l-pwv~Wk{,u\[GLAQ|x%׻f% va' y9txΫBߐWjI#gEb8GcM#$x5_BX80~(+o:q؂%;T( CUv/?ĽDzCB"TzmyrΫNVţ4 t t7pC[6[-+'7{ D•V(h4վ>$MMd̟A];e nվFyK[yj0NNLܰ "D!Ҝ8iB TTڜsvjcIW#Lt:B|Vx˟-s췯OliĨ:Y)):=!Hyr'FQU~^CGmW\&NX#-[` SYD븽og&j*?qX[bB \̡G2,͝a*0pZ !VQ&gkrˉ޳2kiLⳢ6:ɗbx]q6ޏDc댭&w.q5&>’7 יc`籘_/7 ʋk,_ ؝>3vp!( 5QOyBOs oЯ]a-6q\̣x3{"kU }19)csBx'scE >% !"CtRDN.- ] !"MM a֎1IHFAacs6 7oڵkHgϞQr] !]%o B!>@G!:B!Ȱ$B!D%B!2t t"Y,jHSg&G?ML|xi/wOmznM(ECEZ>s7#IuB!HV:./7}amZefHҋ@7OtX-4:Xe|!ⓖCW&>)|{n#ĐB"SD\YpqvųP5::ӄ Юf ze7|ږj~ɒ !DF_Yàf)3 nR|>OS2yRL/ cDUȚUqTby<9W伄gq<051 &#<tFS܍;yK|x3ZSLNMˆoi\zyt7e†EӖhߧB!'&]U:?vWQSe˖ZGŶp}) Y~ӻy- `)~1En٤.Co`Ƙnv]&o~ӆfY-M5#11$CgH)[7VlLlbS =ri1^t=G9,IV@,'עȫ=W2u4iĿuoY!Hˢ3{iہ mWS( 8)ǏG_U (OiwtL[Vu+a=+8t^K*.fusIy {[U(86}2(*)|>YOK' ZDY 9+Y%8r.ҹ^.NPT,N-xC.3MTa'u7;B!fqzWm)Sҗ92~]ZXGMjW*D<&0T.8Γz g/M<'*GKʌpMMbپ.YjOL+˶TMpiO1qNSNvs-3<ѓ#:6*Jt@A=Ҍv;ҶM@{gpW,{{xnG!ÇN8Sc$ZUiBhr> WِT4<>bfZ@EcǠA|Y<9 Y✺ h?@AR6>u+T.UjnΏ2b<"S5ɵ%T'>I5Vq'΢铘ޟa10(ԙM}kTN!|uCM9OCт'kш:ՠ otƜrÞsܱE3 9~ʬ,neÖK\\QTZRe&1m@W-u'.cyҬB!'.,чwPkE^#̗ӹ<ǟnXh/+iѝs:3yw"z4Hf .kτ}:1t K{)ы*K@};'8q:HFLy1*3 bȃm*R5m@T"5"^&]b͔%ev#;4`%K[b6>+LعeJ!Dc_tRypk670b\jĠ,H^ҘiDfZͣ(YSgPm踱}+bcԜʓ^y٬IY5'0dv/F9j wgTxba/UsXF+׽z4dt +Ǭ*[* qbpfEX;"!^%V~ 9=Ak(A!D鰰CZOΝ;iԨǮBYYjUT1:} md+!BdX!"Ò@G!:UB!>;++lmm9yǮB8q^^ڵkڵ]!"C//'~$#NB!>,B!D%B!2, tBaI#B K!BdX!"Ò@G!:B!Ȱ>b' ۺWqk`Y-Ǯ#i+:va<1|ZctQt=ȨB tbkRO,^ܔsgG}9͞ 4+l.8889k. MQk'[uu M2 Ȉ'g943%JĜ`Xذ,N5bRB|"Ⰽ(&i3yBk[̡Iհ ΃!zt83;#;v.VڝujPN3: Ĭ%1KHNJߊ|;f/< OޯreB<$O]2ʝWgg\= SLNskU/+wW\)0<=ż^ u-KJ61)gͦKxꞏR-Js n’ _E.bp>T! m?2ϯ$e+ץTӂcGIMӖ>=:qXfUcyx#b:}: -zEM3(ٍQ5lÍ ;=0)S=OsSCĵ͌y-5Xk'WXWтؾUe_(ɣ}ua 5e֯#+r}%<2p$JsWA28×W\U% )6)S2x|4%arl6g %(vXǺ3+Juy؀c/^ /;`ֶ ·+ I[>?ga0,eȔs*H*] (J*UYa61O1G}Gt'WlЏO/Ta MiƯ:^/rAq[%d0(brOZۃkNEr`rI.5%[:&o!>V$>&gÁ8wjɟe[w.] *OGG]T>+\ViYQ.㢿…xMpz)ף7>ܕi7b6 Y=/fR_wgϷhSLJ7i+|dĠTxWtXg4 r][jNMՓ)^eߒ|y0s2꿜A?|CpgsD Ÿth *1˚ݍQIϾ9'0-7xlqW'Xw5c^i&M FoOdwv LJ-i+a'Q|X2}s|CtB+n*u~3wo|79 _J5 OSl,3naĸ6 A15/YyuFjhrqz, cخ)jH]?2w<26a;ǫ^g7 qW%U)Uj-KYy-{  *39t\-Zy&hٚBثtXXءtB!oK'&B!Ȱ$B!D%B!2, tBaI#B +ݖGEEcj5j,st899aiib:i+Vt tBBBȔ)(lY/2CHH)BWjڊwnF*BS7BaL[mtA !mBH ]'#K7ci+g BaI#B K!BdX!"Ò@G!:B!Ȱ큁qĉ]!>v>*i+0V$I:ժUU⓶m۶]OBIЕB!2, t2ӳyq &F0.{|dV ğer^};UVRf5V4$Fvg"i A%r. +l h@1;"qtkt1`uBY,3jW݇`S~0?gY\/TYg}g!Ɗ}ٽe%5'x9/n(Ş{غz? z0 ~C`>N6+tPPQ4h4Ex"6bxL g߮M,ԇY0UI םل覆fܴu2o}ܔrENٍc^4rURUʭ>a|m#3}Ck{PĨ7Ÿn]MHmŇ ѡ?]߷ FӭG~m@b-Y|E>ׁUl<7Ƴw<(m IDATbO͞m<6Ǎ;BO4yAE2XaуC+V9P&,KAƔÔY(Q돿*VoDKUǒ\DcdNg¬ݩ[=f΢sL*>(W1pNm|3EoԶ_֨CN<Y-.cE74,Y*"ZXPoqsy?ԡpVL̰vPv̿yW˅a0QŔ=wPv,MLtr\HDQ!%7f_gS|n+>5[TՔ.Cxlҩ4RM5۠>/D{T.dͨD.ǒXҙEp~4ܳ8NVYVq=kj yveɧInmbHx$X9dOF8Nh|P?m֭IwLI:Ql)'r;xP5tM]0y%'\=^">@{CP*E_FŨR֎LJq坯\._u._xP).[g?~K~~[eFy+r~8/d?Y6O䯠f긂q)D 0c a<ߑ6/8ĥr+3$ 0mŇģ{ <]֥# rD\$s kBGW=h|s%^x۶bvҘN;%yfŠؿ Ul՜SCbnʴ]l^=6ӵj&dCF:Wj9{#;btuێrK69zM">ͪ(%kXt2t;5)Xft-Y^ 0%3^`V@%QĨCC]+8P;@m1-t4mMRS2DwoEZf^yO)/Y˸F(1ձ7es.&Ej|/ᛒym O%:;9;(MrmE\N_1\tU^jiݽ0{0GǵS5A1-K65k{L)[WM_~tȖFMΊMșd1ȑKz.G'3r _^Ɉڶ(@7oaZEr;9T+cQ-K` u-#MT.4yӧOsavMe4-ہ + /n^yq3[R9x6gwB:¯rn c *2 ,,xyt%^=,;rxc{b1F bB|dLH?{M!*(,LTS̝{[g &)ٛR^A/s!ߎs`S֬~'&:6<<uqXJ) B:m] P:_d{nj%DE%+E::'9z^uߞXqgr*քys%___ r^̜Օ\w3W]p %Z#8( 9PNoD +`욿8Ovǯ4F VE FHZ &v((LyQ\Ӎqp"YnDXZbjdB)Fǭ'R&oDQ4(XCTjQ{g(͋FQPulʦ9+y(t:ZԞ:?s=+P kx[TGՃ-bLC8F̊4i]Lio9&uZ{ωF4(oO]z|ؿu0 ǒ~q bxd٨R Ց\z9.jydGto}RCI6 eXZIãp3"|;KķP d5Irz.Bva\+2T暆?8feZ'7]ӠIvrn+0'e~a_8u߯_bw.KZDjLu(](? nT@`T-J6 vH+DXj p~?Oi{d$\"|S W9o/uQ>˞峘edDM!vSIt8*G-B> D:O!&v6IiYI8h&."w1o,{UEs@j\;3^؏iЯ{%,ńW\ؿfD\EZthV+)==RRIfAy~sGS^E*8$O"QD'^e^ -bxIeH-MMh!Ɉ z[s[Os(X0j.Z,+fwqrT9u qDR>?VQxk:!#"oc'~YΙ}5Q!Wlfެ\vhƌٝRg ;pMt -є dMrjp*CZ`YK=5 l*L-CXj/ E~ˮp/ՍUҭSEO?bdϊpڃۿ.tWٴKn4cʁϜ'l̬I;w%8XrI',V0#gJm,\:PZ .9Rh؉ v/5+ڟ5+w_- wU_5 )o35qPg6#c6Uӂ)tm\?NSJfʍG,(TW_%Π:_]ɲ'^SȲ^cZ~kW3 ad8bP'L|9l&fbꌲF.sh4fݧLj~ԟJ ;,jx%3`m|[5j/M5T-+H|o+Fǭŝ8яL4_‚<8DlVL0q=_ *]ÒNxU*iL_ۅ?֮cj>;K3k]]5W9<`4o9Y[Mc Ք Q,lqrj+T7 Ⱦ,s>9il?k%%0ntJK6k8發)EKXw9u&"1cGEKJMAAAۣѼ=:qHܞ^?328xx8F3ɓN}1W5wsR,#۶m~~~j 54VO{v(%Qj&W~>):o~e\M)SPs$;'IFm+^eoo_|[OGG?uj% ShB|*8cyڰh^U-ځE?hgJ !"Ò@'r}P z%"~ň߇B!>Adpke2nǥj4_w}:5\Pg-GCK8z(CS~ }2 QٺlgBr}8:w[Bw:lYhT*ԦeC:ݙz( )r!J+bJ>gűTt~JmC64#@Vu\2mv4S'ICcD,\[7Hb7svk`~#XjPTM,sCZ4O|r} ̉)d/PcIvcC8X~&*=|0%W1h5Xf&793[ )ȚǛ<)@Y&Oշ߽'Ick0FHB|F>@GGkꃁعup2V_|t"pT}l]5m8WT鶉d> ѡ?]߷ FӭG~m@b-Y|nzbor#wkfWѶ -Kssf j8B.sX|8M0rDŽ`Buem?vmb>rJRd&m9Ԅsװ~L|];{F1e=[~Qg!Ɗ}ٽe%5'x9/ TnoksfAT|Ƨ=kF|jd?!:/(v9t*iћ@)Htw돿*VoDKUǒ\M+A1ͷ R)].$rbh@o_/D%hj!+hju )f0e=&rE!#bw=Ŋ{~à\,Вv?!4nՆm%Cu;Kww gR"EW.sOyڼ,)ǸC2ղ7fι"_5ʗMɂ<ݶց'xN^lEAvpn @OiB!>5tD:39#,+Ss{JT.4yӧOsavMe4-ہ !i0a;g8ul/kt"Ǿk2ïReʐm\{?$zTS&3ȏ!(H Sb(X/Ld{mV`+ݕ4o4f{i?e_2My飉K˴ڄB|N>#yL f/0 s`nf|ws1'ǬL+q9=ϟE`PanVRy-}ӂ11h Fn'7-O.#ŁUc+I_S9ÒȻwx^C8F̊4i]CC p~?OS=M텓ہ^ JP k^AgKw{ tB'ݣcƱCqE5Ľo5/1 q<8{&j"B{~&G;ZU4O!8^5rfZef̘9a)v}iy{QlZFVILC6])?=!]r鮲iyTLk<)ZȆF2VVܻ>KEq1JAb턓E;zf FR>e Oc 6'Y5fLM;ge% b` ]^GgB~wOg'N]qd@ke2nǥb-pw.ͨ3ڷ'x^G{8 u{c.u&΄AqtLJbC!D:,oYI.Wk蹈 ɧ]KEg 4**)VЀc6wD'Zڊnk^N$Ishהa yfյ؄ܚP3,KNzoDK * %vny(Y=ÖIcA4.'+SL-^"-,4t$ c79PŦd2UR0%W1h5HҘ9z퓇, LdMYL:ゑRr9i;cQB!:of]OXk-saVF /z^MJSKj};X`4*Zq-֒_nj &&7Š1pg w_h~mr޽ ܜق-N9|SgR^L g߮M,ԇY0UI םل]:cWsҩ Caʙ 0.vX%/c-u{Nx³g!Ɗ}ٽe%5'x9/8J~`-sI6rX=4>Y3W$x !x@MYRvyb:SdTLq-C@@yjJ <6ZQKnO Q)|Qٻ(> n l I[#M E" UDA) UPTTzUPJ   ٝ$$H2Nyff=4N@ظnߍm|>n%а|eJMo#Rm?TZ:<NYŎoⱼ/~9v5az;k8mM|׵w{߹Pk7so1/;>ۿ9dc_ʐvc˄kf|sj8W-Y_\#8N3s~ +! IDAT.O^̸d7oU:;Gu`"'.ˠu"qN6|n6J`2K Ukfj{O6͘~ԮWgF6wRx锿uưy.bRX}no_dsoc[9عi)̠o*L[?;HdWb=._ȘF-ŸIO);ٷoțe\I'YvZYYinD|enc_""">:VUJl:jNcs6ܪv}{]i( \,XsTrXֹ-}*s Wǡ ojNR(Z܉\2pn'7rtDD䁥st XW+*] ?x#bs4.k=kXĵK8y3~㟥q?̙#23Y}%x˥0l鏷ONK֤ÜAr6s"۔fp`i,8ɋqrRz7DuX)*5ū.Ӭ'`+ ץPw"VزLL\׃8;Sx'\,DXĻ] O9'4YϘu~؞Z1Xܽ.N\ox4Owh?KثsDDWJddӧl8;'oNÆ g#A;yI;e]VCTlG?7[-_݌Gdd$AAAIZ!hVf lLn4ݣ#Gw10'b62{1|% p]&jG2͑.3+䈈Q1!¯1u98x3g~痙#?b+Gqj<)'^&_ D!""SrPaSq*ؕ[>K. ࿝V\DDD2@бt Lݐ {oǒ}T8ґ#C=? ^n7# ML|GED0Љ723^ɞ௟')Eyq}? #ប=t~sFiqX?tV2oVĝbz"Uyw0]0v<ɝ+2eXv"NlHw}Қ(eGҳl]ǛE8nM農(o}ZFF~$~)蕺t ׊ƂOw'zTzԘŒAY2>;ǵ+3u>$` &2TMw/{y^FDTfX#<X7p4ałwN׫5)>]ԭy?VrtM!͙80,.14٣*֞ը ~%MhNnd.O^̸d7oU: =ߜH6p hZԪUZ5Pׂշ UjſW(k: 3S&sf NV WFux tcy_pI,>!VDhޱ/]~Y3(#f1'xp cuNxBjTݐϽńK<,ozH6ElL׋=AV3SzVJ(V.*ikc!s2TVZ c,ڶdb̏9dc7cyz=\qu![4}.IwwZ̹|{VW!ˍ8O'nt͛0lEx=Riԏ]yX""@ :N ?E̚K[<)d_>^3.cB++KmA-W)zfkX|/nb3oRRsl8ȶH92˃t|K]xh^?NnGH۔͛Q%_|zQuZVr<ٮ @^QJ6dIvx_ʕϏ#ҠFoǮAnמ`8p8 pJ_е`ܩpr- jDVk,"p_reP̙ه~o{K?ˠUI:u}MDƽ'NZș7WBK veđp?glķ1Q9xeJT"{/ *h^!w\ȱୃW""f&{t<-dz 6M{§fR4Xq_~}}¢\{py%_}I-|2@H&bϝBʏ%ɹ~޵{vuLW?ԥ}w=,`@珥 m 4pF|]0F͉;~C<$;gȎ/ZL*1WxdƃhuB٥Ѡ-rObӔ_"xߡߝr闯rEڶ v{}$ƈ`#8 Sv;X2SXq/Iht6Ms" 8+NpN=7ǙӜupre[Ǒ`ar Vwkc0ےw+\IѬ®k.(/n8=1~Ih+46_ׯeLf%1Nf=IKDvйʼnluQ G;vs%_՜*Q"43'2"iODWiS" a#RNTW /Ł[&,Y%ͯYTJq cADNugbd)Ijp?t,!\ 6${o>>GmM"\-m)Ln8';wuI)Z;Ε:YɦV""f&щ=ǟTqOί\X ÊBNJj;gbר6Ir9q֮f[,QgL K1,x 5.ac~-?cLTߚBNT}Z~Dy/S_k9g<|EǼ9V+*#ijڣ8U2c% <`IU)0\uB.ũ-9o | Κ9o9hH5^{QϼKWy/\be)&FgvmP7ы=h a' Y}s9hҽ3Dzk._%4Ѵ~EDDDL///ɓ*FFi2\w?k=+~)U~_:dS+<7ũ+3 -_5Dz1W|0I9闃BjmD)V63/+'Ѧx 'W \@މ<`b8j}T\NC݉X]3cV2:0q]:V ◅?d8l~?׍'u[, ,`ɂVۍzs,z=N,̲KoyVNv3:z#wi>`3U.c⽞ܵ <,T| ]*N??󚯧M,^z/6sҘ0V+5kLܹcǎ7oTX+RDFFn >}͖0oNÆ gI3Nxy"GtG8%ٍlRfx'̄SZRW/Ktr6 M{zi͚5,Y2EIPPP}R4Ţ3H\"0n8F߿?cƌy̭,BA'#qb|nI M10 ?&a$8p`j92E-  ޼8.+]bojư'|q=+#,.QpprBĝbłpsq3k~*4{ÒoՍ}Dbv:7E#*r'gA ̑_Z<W!X0MڏgCX[0bHM‚(-Wy1bD2dÆ KKDLFi08t)|k׵,e:d')S&؍hv4;1tb|=!ϔ&̉tb\p{U]5+2h;W7v"NlHwȺ_c樗(}+z6l"j5uf-Ӻs:DʞeDqL4:NeU u %4,Y^Nߘof<ט__27׋9Ml\X6jҳ+\] Kfڙ<%eubћލro|džozQ*YY5,#JigN1rAso ?0`chfi0kw: ٟ`7{[{޽pג+nՄYҶ)O4m?b\"$apjTQ'Z)MW<wqBhJTQ2ijOJ\ź3ʼn2Gд!m.2v R|B\eZ:k 7orkðIBnǮqLI.)9fԞÌ+"KFi2XB`\̲l,Ԏy7p^ư{~(VV ۾_lZ}no_dn `ur' &-G%Ubٵ~+եnXMp:Mwr/ΫC'R{ 9B/htgӶhW*M]t`X \0~Ol%^g XNYjc}۷}FPpCޣ-K[,D([,N6-*h2ZjE :8Jowla_,8ɒ֓l /wm; X' >-1\Buy~6|C,+ky{cŮAnWg]^gʠt-N%0 ֪G`^F8'".%+P-T7f}N+Bٖop1Y\c'-̛+鞭[-G' uƎB_iR&ԤPbxhq/N"lTiق*QVօʍܟG]:ъj1c\M#3Dvxȃh" QU= 8T maDn^nkVclgbMgTٛs[ʳ$V,EAl^b.M0zBn/-fYs<dK~x,.d w /7P yRg2fÉ/xAjjMʩЫLGNY1SteG`Y' =7ז>i`,UX %^lh2ZjEJOt~&|il6g۷Ӱaܯ,јundːwT=K iǝ`iT9 NjI^q޿.QT|rBCClժU4l#e R4jeԮ];EIPPPèV발^Rx9笽9p>.RRyrZ0EFF5nJ!"[F :|¤S0vHW|^_ -++4e<#h1xled6 ÁbIZ8l6[L_DR_F :5BWa'sYkwDv鈗H"X,ӯp88x ^^^2}I}V(舘@HHGeΝwu5?*6 ///BBBRe"2bP1|=&H:jRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRr~ G#,,("""Re~~~xyy'OT72ZP10V+5kLܹcǎ7oTX+tJ(SLϧlٲ\|9#"## 舘@DDag)# r߱9"FeZ#b",M_DҖT+tztDL0TK/ hKDLF :"&ъ\=SHJ)M0V+jzMsN;F޼y\9Kn+raW=ˣ1hF>[;. E͚51 q7EB2eXfMk;mv%Cߡ֕Y7~THwH(M}M^8%l w$%}Qa4t}3mgyXw%aQI3AGD$% ЏX :/^W3w,6僮e_,U?kx~Oڑ4ټRS#C+7W^kwO~tjE 3v6P+nJ3A'C9.qh&g-.epJlؽ|5S"zju.ClgIU;cPA%U+\T+D_|75ѥ0f!*_ p80įG{Ik Z!J :"")0 f#tcY : v)<fzOOi]:Y闛r-뷇~eﵣbHށ2sƁq{ykץ}KǍB_iz;`b-[)a\9~*S8[ v_t/zZ֠t`nl 1mR_T)D h?vs\ǜOR6/޾9(ոya`? u}i1̭sN q>M3`8"w0GS曓?b6Nmw$ !YR;,=qg, Jq`YkB-J%0(5:|ƾY;Ώ}V(<\ޝ+a92{08O;}bx\WnG{ڵdOǷ|a |k%pjGGWr}H>ڐk>7fPzRo1v$cXiCz ,e6?~fꌣﱊkE"D8n֊MӘӗI_RUn?l}J :QE )Kt>1^ qa-F*EHk |oq*MmWٿw9f~u+'/pq"8{0sgysAp!_Nxs'TvLؽ)ڲ%ELahǪWN 3EuJCߪɷŌ:v~ L1] "np>(_0NqƍZsvobYHH_XUkoRHLIw{i^b1tyh7f u7qewp nt>RFps̟މ\ Z< pc \\n'ֶsławܱ7V-iSz,^KT'o.y 4N&+\MHW+v0K̲GS2'ߏDkEJ)ijV<,dˮVjf 80p )E,YVԫGg`J"ؾ_Vm3ۍ4UzZ)? n)QVx|%;J;.Ѷ獏fU|k$ /mƇC2X4+25ƾ`@b t,淿{)ޮ2"縕}ަu"@VnkRrM+UJ|ax Y&@BZDDD4tDDDĴtDDDĴtDDDĴtDDDĴtDDDĴtDDDĴR5褥g]/5@B$cKjAn֤E$IV^-H;#aX,=D$阘9jHu?AZ ܹsDGGZ4nc p"[JkŃJA\Rk"b"tՕV]u%""iۑ#G|2)邟ބs8tDD2#G`XUnJsN=J|\$^Htej֬ݧ2eʰf͚$?z}0ɭׇ#"EDD됔KC} :"" qssi}^gZ^(j53I͠s!fc)rxS'JTwz#""I2bcshZ]=trvWtjKo`qqyL-5k \1łaGh mEO3\nR`rk\ްӰ;83+>FYc|x?gJGblW=6 ϭ`a80/vsY*t>GGD$2 㞯I6$tiz\MS/yv}_qvkCNr,}RHusn;]is3Ǒ;sǽBL[6T}Sq`o>Gٶg?\Z-0c<=puYx^`wJ0\w)홋Wv{ܫ6/;2G :""򟋘 y >uQ{WS{AWKnON6LZwFOy_պ\%S09H:N󩚫"KT3iZ _k^1/?~F^׾ŤƵ#~)}vVt/HAGD$uwr=''L#g=!g95eVcOv-zMMh}+jHyOܔk1_1 Y~xUJ%ד,^.q-A{}{?ok0MWo|vL`_lيR/}q'ڣ:xrsvlEq^9Z NMh>ۨ):˸ 13sczn+GKөk,"A&sx|SSfqbȻϩӽ3#Si eÙȼ,y)&<8"H@dpȂK@L8E%r *(PkIDAT# ֮!KHTr.%ª J0BD!B @;Gń3L~1oݩ}ǹwB(߬Ic4E")</`pǡuLyt @vSZ{:(Z}n037W|Y5ٶ6ԩd=,|ð->o yW [D$9J?@q?l& _Bj&IsQo,=nëKط,LcZF.dx H.:""TMrY1Qi܇2:Gbe:?CWZ[Ú.=ek^x;l}u+I,_d,j$V-~>W_}HYᓹP)L] t{7s#u$G=˞>|Fq?-3F[3*GKR xW|6^e tDD ^AӱkD{zU'N{bS g^:?m{= D5Bh3aL|پu'ӉlV:'ع=Sw2EZa4,q@3R do=GK^NZ vCI8;0 ,\b])D6<]興S5ၧiY.v&xvxKFpMqڸGW}ZE<|;^O`] <-D/K&;^/ ( [S7QGH~W4`#(nC KZIiBy.LDDYMָAeCD?)'gȈC(:wb[Yo˫:fBQ2w®_6-@ұ}'߀N6q?7 f6ai}t:`T3dyTY ǫ;Im9}[>9ҊH(w\qlxi5xJF:;5ք!dZD9ggUsB;‚ YӤFO٘44});f]D.ԡə8NM-rC>iKK'y&>/VCfwjOMs{g!$_?ۗ;[.#:R\o>1<ݗcq-ՋyqKYV*jQ<8y1r5~?a ""rN@\.pw:f4sF]G۳KL)tEXtCreation TimeDo 25 Mr 2021 13:22:30 CET;j IDATxw\U׽,q(8\DQs*piR˝)Yljfj24͍] ^{|~9 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""b%W ˵DDDDDDDDDD. 6 (WPU-?I;{{ɔޑeI׭]g0(-""""""""""d2a2Nڽ)7ogڙۮvvv%[0 ;;;5[vX,KDDDDDDDDDD$s7N֬kmrbX[DDDDDDDDDDD$nYFUDDDDDDDDDDDR3YL2s.LvYC[(nos8 $foI\D--uJn+SEcq4?˺qv|v)-""""""""""VC;8u4 (9s= 2np;o燛>QS8۩upYݝ裸H9[sOQ$ -)v)-=U4b)% g{pv8'n'!lMx7oE=o7'aeڂ]\f/ @QtQ qW>`.{SP9;$$sw,ܹHDDDD$WtP4"HpFb<.&rñ)Ԋ[(`\BTVr[DDDD\ '+H".EJ qOu&it^ *CT=I6FP=0zWy SÌ?٣7%d"""";tƌnx;`&"|[|͞Hvr [fRIPr[DDDD`ITT.RZ5 i9q҈J3y{)y}D{n2-B|n\)=1>߄Jn:Vze4cq8-#\ |8;&hbV$  )EDDDDl%/.9%Ga!j|4d1aY ˢr5&PRDDDD\32~*?g"I܈f۴Z~?s {Ww\c v#:b|qjIlgGHtծUKDDDD?/_y3Gb^"$"""""""""m }EP䖟AS9dkT.]o=s1VEqssý)d<.d'2TMɂ\l5eϒh;Ӗ$1&%6.oB{%$߿L(oŬrsa"H+܏>Jk0O|B>MAveILnTߐ iP4${\}J6x2*?LuT$j%:h\ɜ~5'C&^ ^xΰ|$磧:Βy{|/Π^S-(xf#%O=O-D{5yk)]!3?gɂx1O\|"Fr~9C̷ys*z ߏ~rm^ȶ+՛ٟɲSynm>d$q_֝ɿsAm~|qg."N1u|CȮ=)f2kR=K$(fn+ߡ_5{>6+Xw4oI/^ZC-P^M2*Av8'6mRԫ?a*C7;LΒyh Pl:aZ;߶SlGi@ܣyau].""""""""" %q'69Pw,pBUA)-,PIS \!$4x{KT9wk95d^Gװ'Cħi/=d:'xM[ZĐA+R;!9@RHىQ[!s ~|]sHFlܦ0j?30 ;J?9%WOSQ*}}H@J(z$iM*#Kk\`_} ^#]8Lp$lﯩ53դ?a{}Г2i& Ό 0 VDDDDDDDDD$70m= , }ejڱhq(׉JUF4;ޞ<#q|![VpLQuK7M[i/:ze_,Š$|֨6%| eRO΋M'+}]"KS:&)=F4a[`)ۋ*䶹0nnm%QU=xlH7>>} ^Ꮧs,珜mo=0ܲ?b'|3w ;ޙUz>GC9_ {KzdBn}*ÍcGWs0~ezSw\DDDDDDDDD$܍iLt|%W0$`pɬlYt.`Fg8gJf +۱=4^Ή❙f+^nkcJ l%:`t{1z ɚ}繖{`/=_yuE,'|Gd֎htҥ-6D/HCB+/;qxDDDDDDDDD?H";3EDDDDDDDDDDHm,3TDDDDDDDDDDD]ٳCDDDDDDDDDDjV'mլNn[,[!""""""""""b5""""""""""r߱,aCDDDDDDDDDDj-""""""""""%EDDDDDDDDDcuY|ubbbRO0Sp<^{Hv͏?֭[9|0W^hٲ%u֥Xb6#**GuV֮]ÇXbЪU+5jDJl7C֌ל&{!`ذa|t҅3fPhQo;=Nb̙|t֍OOlyY̙… 9wϨϏ}!444񈈈ؚ]~}KN7?;s 2!Cg+{Mrb;9sv… YbW\p о}{zMFptt̵8, c;wŒf7oNUfm.Rh<f5q 1ԶmѣGJÃEѰaCo;=#F`޼y$&&`ooOHHSLvcƌw!>>ު3 `)_Mi{,ɭ\!?J,_xL׮]cٲe 2%KZ ,^h" p#~.\Yd ?İn:~4׬YS~?w}]uÏ?Hll\dIJb 111ewv-\0W~]-KL03SLջ=;;s)|||2M^%$$|r}Y(_ HY0 >4i… Xx1?NNN⋼K˗ֻ)E""""""""_  )))c[H]n$UC H,gǎ?$L&5_AiĎiӆ͛}vƍ۱X,L2///6m, ˖-Vb;f̘AŊڵJHڸq#n:~ HYZTT]tH_L>ӲeKڭ[7̙2{ݻl{""""""""EV'SzL\9SDq9چ-t%%=qĔe2522 _ШQ#L_sppqL2#G}vN<ɧ~Jjղuݻw(ݠAF>&&P|||]v [ѝp·%4v{>66"E=L>SPrwuX-ch5cڵk) (`kR|M6 M4rʘL\VVd/rN.Erc[_JDr)yVvzD ^2q۶m);v숽}ֆ#۷g…lݺ5[1dy 6d)SNhѢ$ٳgٶm*mXyb(^m .ܹs_>nnnL&"##ٹs' ***e]Ʉ{œ|c֙3grbccɗ/+WfСrKFi^%+XI1l+Đ9?y6ĉ}~9Nb9s KxxxpL&ϟ'YFdd$e˖0 xb^߮];~ۖ;99FdddʩK.]>}f͚jժ,3qDf͚0 <8Wwc 1iFFFrQ֮]|@ddds fΜ9o>׮^Ip-Z}ҪU+*UD"E7|CHHH)RsҶml-m>`g̚s;w:LX'9Q\7=vbbbVbn*O^Vˋ9r$mڴ!442'<cǎMIl9r_~]vѣG ܘ$mrb;7K> cŚK/D^6mZ6@```euիGƍ,୷ޢgϞ >&~ѯ_? .8a6OmbIyt7O: ؝;wҨQ#%XI-5 Qʕ+GYjӧOOߦhѢS<==SJwll6Ӳe,s+WWה"uז+nKW\ɳbbbe߾}{\wwwiڴ)=z >>qƁ2w\֭KѢE58p`D ,`N:lUVRDDDDDDDD2;wǒ˗/gPHzM1 M61j(vܙIII1j(6oޜ|Ϟ=7SNe)F;wdԨQ|9Ҫ6@RhذabL nWW׻>N>͙3gpqq9[ێvTT!!! 6q1n8JHHH3###mہRh(Rm۶Xb6ٖȃ G5KtՄ&{ٳg[]^bРA⒫Y[ueۗ$6mĀh֬m۶~(Pk׮sN[6lcҴkX=z4'NӪؼ fѢE߳cf`s]\\ IiSve 'O… 4mڔ/2sM4VlּfݶҥK^c<YN֬YCDDիWO~/˱huBBBq, ɓJ%vHneooOΝ8q"'Ǐ̟?qӽ[#2 +WͤI]]]ׯ{aǹsާjժѯ_\KK<eȑiӆzgϦmS#Fɶwޝ23:'֯_޽{Yf.DUVC H"~j3f̠gϞ899l""""""""y-GeIpppd.+.qѱcG{=uF1 #b=T^;;1 72~x]fMys :4/֭qpp`8;;3}tx x O#gf׮]YR,3Ν4C%{{۞wppҗ&:|zˤI4iSNQ.](-"""""""y9\ǒsɉmWW<T?ٶm˗6lH-pssc b?XmLN8q111ggg Fǎo,|5^{5N<ڵkygy'Xn~!+ʕc4k,WrȄ޽;E\\\xp9;vA+1VX1Μ9W_}E.]Xd &Mˋ#Gf}N> ҥKٳ'y""""""""(k3}tcYM2f4ں` ēY 3Gaq9|||\Zϟ_gҥ%f3.]R{,_3grfz!JǎqttvmyCqܐx걑BBB3f ͛7q/[nʹׯO?QX1S꫼{)cd21dȐ4WlݺZh+W j񄆆_ ё#Gd˔)S6m%NnQpuu 2OÙ9W7*˗/6i; vHHH.E%K, PD x XnIII)UV-9::ҥK|||X~=Ν-ql6Af۷/5kVO7WWW9|pJeևapȑ;?~ڷo6mڰd.\;TT-[ҰaCS7"%hkrvfKZ *UtWlٲL:ŋh".^Hӧ]v+G)Sɓ'_pBΞ=BDDDDDDD~sW˒d&˒D+yHV)-""""""""""%EDDDDDDDDD侣""""""""""r߱:}v[!""""""""""b5AAACDDDDDDDDDDjV'mtCI(-""""""""""%EDDDDDDDDD侣䶈w""""""""""rQr[DDDDDDDDDD;Jn}>˱cǸr y]UhQ\]]PB^"""""""䶈5ǎl6ס_ǏSbżEDDDDDD侗S~:W^Uaapi>HOvRV$=.\Xt}ܕ?O>g^$""""rO:}V,-clӓÇkiPNr?~¶t}ܥ~N"""""""""""%EDDDDDDDDD侣vf>a'1c_aVL~b:] ζD`\q} %3cbx2Y g~/~gzK[e9p?c98Kgƿs@DޟhD%3f?冲Q{AAԮږ4mpNoD vp.*7ǶEDDDDD2bv)6~'զrYOJ_ luJ5yAsørWu6μ`K TX Mg6-p}/\hҳoXB 9mG>Cɧ{@p;W|k`W-+FwϜ(պMKYaĖED,'=JDovav!=dct\lBP>w7 x+~5o=̍\`"g~|_w{b)^Oa+P‰χ:8Cugj?4Cˆѯ2p\:1\8',a۟w+G> pqߓCƥ-ƾH\;g QW&\_ai`^ǑV.TJ3FdyT;vM_]L,y; +G&1DްkN{6ʭv4ќ"7O:BdтB^swHNx._<ί0|y ao>M2xar)Lt^&μH͐ɓl8%{a|>>Zc7|Lx,%@EykPLEj6?IxvlJ]ߊx.MhJ5m$]KOokqԲ9&ѯSj1GW0WKU.G* xlgK3*3t(IwݜŖrI 7f3د2=KY*:bɡ3tNss_eƴ{v>{b9:7JYOO*VkDwvߵRW sOPe|-[u17)*LIOVE/Vn@bx 'I ]^xo/\H3>pLҞڈΣr8&ˮ1= Q2TGɳ ,|“%JPTƄ%@)Г&Y0nUwfj.e].5͓CKԽVI ߔaətu+_|l=+e#͡>&xYLC"`\ev'Z9e&1Z2ϰ"e&qx| ^2÷q\տ#C?)G MkKe*w19Ɉ`'iը&><)YMzw'O Z[ >u2{;]rc0bvgY4>yo˯26&_ T㡝xxZ"-D lP>,#+=bGӥ5#zwj~Z5?kxx~NfV~6&e0gc?*fx{[nފG gx$5HWY|33Xtֿ@UdwϲVMh靓8sQ|գnF4mӍg}̋5՜W)}"Sk4Ou|woFJM2*xcCIiOoK/SK,YjJl@zO'/jޘ_j{%kDDDDDl7s;!ŋFe2&=9ߞ˫>6 za_h5}/kxg+-(hESv5ߘGFS ]}zzWLh y"[>NTKb|_lĈ fYίdX=vGf:ޖl̔=b,疮;ů~'Y|Ժ(\:W&2oA*mx`DNϰڵ #ZDj#WK?^~SYҡq&] >3152XˈXˈX[~t/¥?#ܗ͙F|4 oeDM|D/>5SF'ՍnDX_bh)?}v,ؙ{|xf,ƕw"uʔɍo/ex]09V֞_fst?x;ߕ͈٥0.f31 XoK?l)#Y`d+zZ{,i-:\&ֽvS,ЙbGx\v m"nfGC2{58sBnBOc;7xzsOs̚KfvN[8N Pv-ȴA)JB8/\e ]~1ӓڤx|D[c97=ě'\8|yM˱Zá7DhLMQ,ق!eojrBPfԵpz z=L gpM{uBC2:ڜB+M&vaL.]io;]a벯8԰U vrhv/r^g~@iL0|c9Ί#w& yssUK~N;ܒ3r,x7%Ǘ28^,gn.KSxuů/OʧAe- ȗ:v)b"z +PȲ~ڸ+W0w?ŊS0 {BޠwiU%i/}kU*?qIv$]ի U$g8v"Of^+w+, W8;?̚f*3ύ}w͋q GDGp[, 2&*4uʗ= 3g:[ J٤J祉S!75N46qϏdf6LۦκcSf礛yDzh$ V,d/qrL |o1-CnTG ?c\4Z1mpyk<)goj^CSO0x5mN ۗeI(ٕb>@łw/` SRV@sI86_|4kc\= GҢFOҪۍ̙%QupnS3; F /\\ҏ@?9@7\;v0HJL$1!mfnJ5cBfɐ1oU[t[^t NSjV.ÃnIϥXgjZCzw… :d AVnΥ^3)+N7xo> ل]77l/LmGtڝSc;4ҏv}x#A8KRm-աf|Ž%3Q51eP$'ܪݾ)@j%$! Ag >~'پ& -"ݶ<ʒܸ0A/Pǯ7 XHsı}LCzyW, OٌnhOyﲘ.D6۸˱Rɧf#1.8+7ݺ#lz5zaǾ$]Dw1y%]$\?(ܩɩ\EL_28v÷M=jKjBASblY: )rqF ]'F%btoj'#!"Lf1:>T b̖xX )s-7tr: Kִ%ώ]/W* ^G <<+R-3繏Ba5v~mc9}.is-SazG{9+)ܔG}0X)lI& wȋҜ\n0<7ΰu1$?{K)ƴW!C(sze~,e=Xpc ٽ;g- }$v%[Rg*Yurxٽog2n@+9'v7ڕkܖmq s(*>/,cPҔ,Oպxv֟T>P ϓ_c#;k>83H$D&"/" KRT[ERUj'UU[-/ վ.hEUk_K#HDs_z=y{Ι#A IDAT3{{N[;_:,37ʟJ޿x.,rnL5_>m癬<7f{"d?Ն2QC}yrV;=6[NsvtlH#wFk{-^2a YGCkC`ZyZ~,l/u.mOvC<7ɼj_رNKZ-[sw7ݕ70p)US~edңn%cW~g\n,?~Y oAQΟ'R&M+|'qw׈lս2IfhjTҒ$մL&MM3E]|fěs̶L'pA"Yfy仟4+]cRm2 id=ߖ#?sUdZKR+3ئE7O]?atiڃeNn}sw^1.¤wKw__lkyFf*ihlH%2i֭44!"5IMiהTCyZ1lvns{3w7rϰ}sdӵUWlg'~AJ֌ѐg3^BYio o]lqa׿Zj9nz>t/*ѹfNOGO\| vݹO1C6~1Urg?gJLmy:,іYk)rEzH=srۥ7|)Y9;r7嬝IQS.Cr沝rQonZ3};%%\}è\|Y/ڧ]V_il{ז4FKKKRrNA_6[,7{vF>яS5-7;1zJeTO YcC{~kJRSN>Y=S3y~rl lSZvtiL!y7>mF_UL|(;+fvʐݖ[^9/\qAn;MKf,k3⒧2&8O;yYXYdty=3psMwfoSsn3훾+m/0?Lg q˅٭593{+_אVE1o)iBIgRy{E3},\I^߻1/~:YׇM.CN{K?F>[o'w=u/䅳ҏ:;7}On<eZi0hbw{r/Os&%>KF^0*<]eӭܸ?1=^;}s'fN]Ο|3f}~kyvpλOIssu<Yrŕbkn8ߌ_j ZW}6]=oƔmjs7dwLn][nj7׌[zl|4-Η7YkޗUݺ3ڳ3hJvϚ8.jjy&kZ2(IRwӫb\}9T헮Dyӣsf[fc3p,ڹ7yI΀Va{l%'>_rvZ9eR$6}Ͳ+W\yYnY<V=v|a2~ˋME&W?=9WϿq|L\~ų ፿]3爦eN'?$mzظ[&zճ~閵O%w}}h|͜qVgݪ{d~'=;kw.8!_5G5h0 l _$גg6cѫsKeᅛsIdMVK4q,lfw9=eb󾓗4osِJ4w?tUYk1ݒgn5ONIg |%=wSX|mrie`߬ϕՑwmwQ9sKҾ2YemӋgԈЧ杆H/KNVs^J/ LKe#rOu|sͥ4YeKf /yW39}ZohwJSzY3|h y욋t,`yͤk3|v٧r\{oyk2iAYiҩ:.KC?`to1S=7IML YF?Kb_?7W^=&{}{VF{69>qn=vzŐYgYtvrYf sm4 Y~3Ο;f%;'+o1Ү٭ɁBCΓ5/T5@QO.r¨d9g0j&6̛=j$鐁^˻S/9)=w|&weV=}v(#S.9hqy협9bsF]`^i,XIR? 742G^6:ԵSuF~=2ߣWn}b.?t,ِdms浍9턳s奷2"}ّdUK%?7,CpVmx6_w}ggиԮ99Krf"!idnyn}LtNg&]fyUNbٗ^?=Oj|=ޮ}Ӝv9Ѱo_qM=cɪg)f|'g9vLj7/3}7IҴlU2}s᫓ө eV~?>0OӅiBvYz=wte5#);ȦՐE>.C[? qvqg5_;ypnuC<|saY# u~<4tlK|cirhv][?~~\G}qިuHKd/BC~ Cf6_,Q|fxSV;iL>iگ͓M~>6_YY`y,bCr¶>ro֡k|óg1r'F2ߢO39}*-5fkRsɜ1WQ'^oIKed+)鞓e[wy1M'csCs?_̈́Jt_z|_нݤGso͢U'I5wwj9n.fu:ek8.~]oWOs>v'ʩW<_oI.d5v͈[yS7vhy9|-5ݗ%G߅?Or̈Iӿ9+ ik+nV^y) Rd+e G y}iMc'o47=$+r{466殻6l37ekz9:amsԀF<}C[wݘ}\eUuյkAI|\?k^HBky2gOIi0+>SGݘ/MFROyg,༙rũW+Kڟ+ZGTەt^w22#\-4u[ϗ2KGˋMHS%rQ{g9mb>Vj5was;NZf12اjJC C}~^?TLzaj }>4.-{MY'mv,bE>cdOzr|gs,ɓ'k[p~aSO9~s`Nr%|544d-ۛ1W=y饗fgvm7(Ƀ3oz?ɠA&gu]yP6( @qm#nPqG86(NS=~sLl07d6*xϭ:7a0, ( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPq4 `=ztƏceݻwOsssSOh#Fɓ'gu]#<'x"}: KF?> zquh#ƎZ6UOhCj,M|p>ϫ7wn1χ~xJɓ'l*ym>fȐ!9ӓL~xN;USs iͰ!Cn2dHZZZr衇kZMhCf=؜z3||Сiii^k5q }O2  2dvo^$rWСC?;~wygNo mHk~Vm @qmy s{`VsەJS-VmȜdN _"n!mmm6dV7nǞuԛ Ft-j5J%j.hhhHZMnSץܜ1c|1UՌ3&u^ܹ F/?x{챼+uYGnܜ~eڐM- K@qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqG86( @qm#nPqinosѣ3~;.޽{ӧO,}6@1zL<9zy<۷oaX6b0`@3p7p6@1vj9zڐYڵO4ivoΧ&n1>R| cnPwn!Zwn 6@"nPK66q6@ys[hCmyhCޏӛN>t飦}Oe՛ @СCg:a!Ć-1Ò1tg!J;n|9#?qJƙڐ rA1ṡ~dС3}DhCZ;&Jc9&Z-ݚuԛ t~ihhȋ/c=vno·mHk~7+7q֭[j*Jj]АjnݺeK39cƌ pcV3fLsׯ_}||kq{љy<۷tw} @[1k١q{N=]#͟v>>گ#n3{NarG灥K F7Kog ۯO>Q~~aŨh?[f4=}Fe-6M_3-ߙb2LmsQsMʪ/\3o|n<2ӥKW.}zrRˬYv5kNV[8\xfwǜwaYӔ&<}}Y{t]Ob˙eUbӥs,гw~\u&{ W^*螥W.Ld2d2)+e57#BrՋS!zps.'=蒅z 'ܜ'tY<_:Ͽ`\e L&f1LTo%3۲7o̖JMO^#/ %j6?j׼6zTѻ'_c{Mti#rۋ _.?Qe?W#YYq]Vt:f!O#-[,ۖ?~O>3j}wfǎyh3i.63]ڍ(Iv^/7-/??.֒%*<'6%Tvru๴Q?[nw3]) ~gw<-.>CTw61{V(ޑL ?l4uy#~o~{e}= m1E넜vG;\c \|_f/8)\vڥyyॎiܹoEUHVE۾1;lN_yUzgEZm\1śd[֠R.gU,եGwL}Z:<.z4S'_w[ J]vSNQNץ퐽;hya)>S);5'g,~#/0O;_._}~FlE9F)]uburE]~a^n?:G$Sߩ9v'=ns )-|.shJԥNsrBc9\5n9矮=6vfͯY>3+#˘q2a)7uc>ui _zznnO} [5N997OSߒ;'Wjm}wzyqc]ťyz%#ox.W?4Sn*N \u[d|#7ORwn*;gЄ|I9S i`CКЦvccc<#yUWА466q AkjBnjjjW@PGמ+65G @nPs65G @nPs65n]Ϙ1p{m9hV]vmy@ @nPs65G @nPs65G @nPs65G @nPs65G @nPs65G @nPs65G @nPs65G @ͩkݻՖ6pAIENDB`qdmr-0.12.3/doc/dmr-intro/fig/qdmr-zones.png000066400000000000000000001051261501654372000206020ustar00rootroot00000000000000PNG  IHDRv6})sBIT|dtEXtSoftwaregnome-screenshot> IDATxwxdڲ,e eOA.\!P[D *@p,EVQeս{F?JKK*WI)Fłb*pjıt8)]T#jrq,"""""""""I6HLLHέZ-&\(鎫5i8Z^V0p:5XDDDDCvl$9-xyb)5pB:ATTOO8s pP7X2[NW,L8_>EÙmpt&5X,f3~GMHׯzUp3;\ٌb%la埧X |}t:z""""a4y&| N!""""Yq:EѶm[*WD\\\9UDzubW.d[$B#ت]+ƈ őE<}xZof:0 %EDDD%íA`P $;..,mذ1X=zt6^)Q Gt.)ZiK/a""""r-sfPFvoJP/,X/Ğoywa /#o] X΄2I| W@Ul#L ѳN*zaN"ol F4 ѧ*e]UBDDD:HժUNWw >Y'ymk\&nҤTG .&:K扈ȵlt gP˻>mYG,ִo69noOM%6_R wùGθEDDD7.V+aaaxx&q²OaX%d"峬Ni grԏ `rӼdh'VVul8З+穗iOi\2<|cO^6ç(]Y$ʻ̒ř+""""W+gϞb8ϥLo8w<Yۻw_G6E^ gM3-;#6y 'ϴhZEcfxDfxs\}Rrrru6nҘm'  ,/gd,o+XfL&8q+WʶD,?)>1xڵ6VddDb̮|z]egr+] Gt84 E Ka=f\#vb900n4i5L-ѥݰ$)>D;va2a6QӊٰGa $DEmO/x_iLz0DDDD(811TR2e|*Όl&<"3OUfsĢˈtOM g¦8Fxd/9S#EDDDg`$3R LJ-uU'icdOOwN>M||e$Rri~y"(U)cioQ:w.%S/ Gzm'Jg/eJu#ӚJ芈H!0xzzIDDKKckxcb0LxrdX(yxz>Oqtrk~$r¾Dw&zgvūk ԣ{G놑HD⊏ WÉnpb8/6Z̸X-y˷x*[!""""WʔqM֨c)& WW\<֒Rي*@#EDDDDDDDDD$=0ODDDDDDDDDD2PXDDDDDDDDDD2HM;n g4XDDDDDDDDDD1wDDDDDDDDDDQXDDDDDDDDDD2PXDDDDDDDDDD2(q s2؋l#R`:O"""""""""BLDZ噎"g{Y#\?o3v g$z]')<$*D+~w{+>U U/w"r|W\@AzsY,9_#jV%(L5bth *'?q9.U1j}R4MDDDDDDDDD <ڞVw*6\y#l*KP6k{mx`i<߲k/@`נFx'Y-ǦyWe6ؕ0oY3*{-1{Y6q0եRƴ9>?$fol&nzUz2Qcƃ7(:k4-ؕT[{ЬN-*mM-g_tE*Pv6Z@UԣiaLonEDDDDDDDDnǞpU\qyw>5EtDDDDDDDDD{يoX)7r==9>__a(kZ%D؝C/2žiYGs5Bp{eƬa @k>†ײfD:ob?NRWd8Z|O#29]!gYVyZU{>&^j2U@3JW`۬ylmݭ@(хG c]DDDDDDDDD3y>Rjײcc!H 2tƧv:lP _II'; l6+91mݽ=ͱm+5tFXLU_#X6?lG.}" B$g" ( ӭGcLa_ 4)XEDDDDDDDD{C&Mi B;_e؆7GJcJu " \ڗ;r&3+nt7>ۗ_qͼ#KfJr]nkcouY; >)f'< &0A;q1~fX;d(-x4mkt+^}hPns^_p7̕ocEp7&>w eK_r*:yqϞw! xq,ߙucw{DDDDDDDDDX\ """"""""""rQXDDDDDDDDDD2P @#EDDDDDDDDD$뙳g """"""""""R˜bUBDDDDDDDDDDұ:n0y,""""""""""XAU+DDDDDDDDDD$=ܱ\Ae+DDDDDDDDDD$%EDDDDDDDDD$ByQ7eRϜ͘#)Sm=|ڢ҆(2*E9JXX6nd۶ZW2I|jq^_o;3'NdO2x nj& Jc9s%o,+8{\gu=psse09nw\i1 ;z$>W!9Y$טdkl6v W拵DĤfq,_Oɐ8Ntri>XcƳOsԓO6ow;w>gϞoM̞{aF>45q `SG!WjO(X"""""""r}׆###>s6P[Z8%) wKE]ݒ9G'NNFGGdfckyhH5kԠs`nחmZC\\wdݗ_yOoFwhrRR^z _sb0 c?cOcLzB }5\tHNv\/\DRR={tg;wajko.]:syV\ņMqss cVԻVrSBDDDDDDD(dZxG/whÖn`2:ed#(|<+E/cGH}"(ܵigp80Lthߎ)>M6%`]]}{Coztʎ!0u:;vt2T kNyOHAg+qV||,$0&qJX\O}rڟ-?naE$&&O[bK+""ۄD^6[һWBneC dKSGp;,7ۺLkoFr̴)&?5`vEE/>=^JљsU[xq:oiެi빸ҩc0gNgSٱs?{,Qz9گeENmBO` C//> 9O2e3$s:o}HٲEzƎG3zd~!)elR>K-0fTQ@'{E L}/"##Y 1NM_90ٻ5_nX&Oy65q-aԘ{8_~N޽ԆXѷOofJ~ڤ''c~|mfϘV"^Y+WK6!݋KNj٢Z*U5cfL+䤸ϙ\2MTnKJ7]RF*QRKi_w1O|dHƌQma#1^׽[7ڴn۵mC<>|(6oIW+7ЇIUzioDldXWZhL&N# z`] ݝ gOi-?foY_HLLJ7 70{4T 'Ä%]ݺvyjόY/x.ØQ# 5[JXX; \2eXx!o_~ #H ӧw/jRl|}W_3jtS-[_{ސiu"""""""r}0g6sWȯX]2L˵+%R53)vZJCr]x y.?qL{>җ׷/Cyg˯b>|$OmMĦ$ ,AI+O<4}_.q бC{ڴʺI-!ݼP̝==㓞*Pۖ~j}vL?vλ+P| ;H?-׮$Ҿdp\Att4<#f2@)O<ח böm0kFrI&qYBvBv5cjϽ0?i yܛ ` ˑY)H9+%|gY~Vv Yv N'ϜO IDAT>㝥Y'ssysgSN:xm|))2yl%d:1g^eʥۣi䖇]t_>i^O5ކ$ߛyNJ|3_~ ٍ$$$Ν:9]! r[ybT b~-T\ 08~p#x3{T|oJy+&o;m7$Ak䘝'uә:ZE$MDDDDDDDJ kfO+:Ǟn:7XA$v\hm(H{B^kmRG/f~vmBhhx/ơLJL&h߮]~xͷRޭKqJIn<V$=YGz m]ڔda@Rn])]46m~-?/=wsjg:´>>9<EqWJ)aoZ{ϝJ۶{wkBHn~޶=ȑ8m&3OQ9L>GPz5<==܆^={O[ӕHLL⋵_r9{w)U\*Usl6ӻW>>"mB(ON4nV𼗕ɫr)WΟڥ3wIII@KFҪUK|} #$d7#njOwsseoӫGBoghh'Ob6ӧmZ.툈\m& RBWTTTKU\ e˖{f>?m2٧iѼ9Vk.p8xa -uxat:nCvmTb)vg2N>X2Wcʕ*Ѯm\#'iKXW<4a\^ĈF}S_w~޶=aaaEҶ/[ 7닿_lKDDDDDDDj^YKCv`njIIhOnڰy7s]~`1 ooBbjقOMp8VFGn]~iՒRJˮ|7l#GMtyˏL~f 3@@.ȵkҭ[(v~zv !,<'M7bтy4kִPEm_'wwvIN _OR>>>Jvms|<2n mZՊ-7g|m^] zlmN'ߐmfnu[;ǀc(n]0rdpvmy|m3,͇9h׶ & WWWFNN }~ӟH`yj>,PJ*N$$\s.[Wr<&в)z.%YPrsLkCʈ&=QJsԩ߈ ELyE>3Ngf3)^Rk`ؿ@K#f6l#c0f= 63e+iX8#lOK_cæѾ]NyEzL&cGdڋϧ.mBٽ[P@:Cǽ=C?JP{v_NX""rc%ED$=/1wq3؋-"rbKnIbw愑,ء+`񒝔5q-m6 W+fKb+e׍E,V3ׇ.lӇ;XɁZ*kעb~K5qMnmH*UPjZСC}V{r?;O{SD+'qnGHb=|#,$E]OX4._q*#dwP9<1NXoMnO[gD^y)zx,i0'NZ'v`tjՈkP |Utrsc'I 8pr tkِjPQgn)sx#tszu׍4tK],\ P5v۳9 d1|UzTVj;pӄw52mfmo0ԫUi&ye|;c(]!z cz*-MV[0ݻ?wzEsH?2hVs7~!8w+Fu VڭT@5aQҭy]֬O.NhS1SkТUKڴBAXcqLI&)]V2$l{ f3/Y1unC[0i5ߥ%A5kP9ɔ{4M8C4~ƚ,'ޫ $|;c Yn؄kش3-503}l@x }]ZPF1r 9r]wӧcK֠J4>9^ې5Jܧ=fv׆c>\]G,}:Y =ޔ c 3?6B z*3r8’A/Zdž/f;}8`?a>~B3}0Q]a~6zsӳ xbH_zu-=̧nRo_#_{;:7Fiه O{MOPmy<39/lc~4[Zh{"kΦt̮%{*oEmЭ>&=XkV!2"W""""""yͦ9w3F5+ Ƞ_0,} 1;;椓><]; O>{e6/EІɴpqrl>] gѶ0GU,foeKRJOe6é _Oxtj\Ūk/dfʷLssV3i*zc w^اkCH,Gwmh1:=KGN׷%'{v)GiK,؃ՓeK>he¦fLgb*G pM.=ؼ &6_a*tfۧ ꕾ2b'pܙ҉OaTpp%|m@Sk1;=.ۧj]ջJGL' :vc53-ܲz{lhs$h /K} Yy;I $Цk;7CuJ=˅8ѫ|ՕMeHЉ 黔 cTɻ6uL> ֝?"nMy&\S^t=>{,lᎷ^ӥ}!yrp8B^g+u+(y=#t4{z#?73IgHJvMn^&juNmS%_pzɗa6 ~nY]qnszL0wÛж7&"ב (W}9n.^@ZYRUVLeƔeR|/w kV i"vLE@ףQ;RqoˣM.wfc?[Em1] 0SƯywOв|p*¾}47scX[zp9eYٵ ӁKӻsؗhi:r՗3ZiU G1ز AZr'&E'2yפf9K]|M->:F&:7’{cIܖLkC$פjÁHKϯGs{K]d/z. _xFOYօ⫹wSgcB]ړj7_n i+oD扦 `4af'־x;qO<_Sc/?3U#Y~Ex 8˘2?9RF0ճ:OzL/qi2׳۷%v*lvNQړ[ֱ-Dٷ5_^ŅgsFNifvlO@6kLv=oodۊ'NvǤB뾜*>8.dY̹#0GX$:+s- IDAT<9{m*((kvIƾ32m F! wm0KJQ{~┒[5~;wsj5]J'ysA9Ofo<^kVY+~U+e=Z|bn=uKҥsVsi8kB9G詫ͫKzN}󧐦=g&/]v`s.\&P>Y Mo]wޕ|,/tYDsawDžxP4ywd]WdH]*7vb>:mzelUvώMzT|=bq>ڠk6ZUFSY7+ug\jk^c,Un|7:UytPx2~)nރy}?̿͝E,4ƃ{#s55P']{vN^~(_gP}n4kMIn5P+wMg3s[5Bҵk4\xl/9;'?=/\08go@ιYgd<\6/)w_f>,ݣGT?~t|zju햶I-ҸyRTOBt[qV)O>;f?陷sIհz-[qi)q 3rۥfJ].^f!<7%g]|f$ϝ[r{y/U[yW}GyZ~M͛~/f؃/ܥ;_f]f=fǺkΌ|MN?/UYǞYG׬ԡMԞU54gXf )[>_nlgmdkf|1i$e=>W!;Mfwv[nk6_Ww''M;sk2.O۝πI{O2Ȕ 5JYfZ񀴘f> 0|,kA{ y>#U#>랙Ob>~rY}}J|T4mZi t9~l^ٵ_,۰:S{5O4My̚ۥ#k*o}W^|Zf{%o>;4GSl5}[i+䠿#;.\:~Zj7ovSE` >ԲFim lݤϹT+2m^YqF)Lo~nnZ!:dWʠ/Tm>@g3fį'*jkϛgG57[5Zgo>7ҴY5yێy<ϟK<FXó`SlvK]9?ʊ4lLVX{`P;I!M6}~Et 9ns2 V c6h.O^vjۄ(-Z[vZd_/0 -OOf ad4n)>1-ʒ,3(P3O(ysO-K6+g5J9C1]+gg])'OH{CY!)+KYcrqkNksN1mjVoQ\=oT^{^~}Nuv>QF:zg3?/gpNNՕP:=kUV7j~G}}.Vwb<^eSrɟ?L+e7&IOX״DYl}|< Gxvv\wyy}cr這aحn;'_6:6k~mFޔ^+o93q>/fm"-].[N=)ni=0٣/6._S~L.Rfl׭Pv&0 {+S˚uCHB2プK/ ( jryd=||t¥9jK2V/"{n5kj sųNΙk5=7QgQz^E8*|_;ymٞWh+}ak۟?l.p(kfSoU\|YufK!O|uԩ9ʓ$MkGG̶0\|;r;RiR&oxq%a~~FXFy[qU{jQ|gse.Û+-<9*)}ŝ3!Yuu|qw߽fc-쟨B!#n{6xyŹ{^BL9|3^[I'׽ǚfw,%4K;ϸ:G[jaWw ৠ"I~CQHˁ''ᱚb4ή< gk\S< *RUU0^55]<\8.=k!)}/+Ox\U_^jkx zh? _d橬,[muR\ReCf{"T$ɠmYnIIb]wxR#G,w(.%FaSey(!PB<x @ 1%cJXF]ׯ[x$6ۢ&rVPB<x @Srz᰼UŶb>Yq9vL&|6*{4y039ތ<|xnFO0ҝ||1|KR#riݭ,<|Wn?Ͼ򞫧g3Cn!b> ;q-UVi_vyWϗS/T}UӬP3p<;&?tij2*w2+'I*ګħU37x*#FMgҨFy<3QV_k*jv̇Ǖ/;?Sv>n4]Å4^O^O}b'^쑾FdCӒOYwz59=S㲖kf-敇k^ɴo<^~ݬ<sO'mw#kי@ m2̳X~r ej‰IRh}lB.k\v7ͶOeY[|2En陪4M6^h3`jhUy+d^o+n7}0JyףkfJ,w1?Z1%cJ(!P\;^l8,oUNJ:Ͳǭ̡Xy @?{ixrth [tF]g>)|pƛc]avsrrRuR~o[ێtNˆt˙c+0;o3[6NZRYylBq_XϧM˴ْtg^}':.g޴qZw{N:lUl^60Ӯ=8{';_0*0ZOi8jlߜubzyyr:,KR̤{g/Fag亳ec-ψۤ'sӳe׎2՛!eۊyҨF/[csg*/_qzFۮd\Y3 YwYuYkc?MJY52`5ӱVkYu9n~yiVHRIRtzMڗ%oݕ v[Y6]WǭF#y䑯>ft2QOg^/ϛMݺuSn|# eYq#k˻Oճצ$갃#*?A9/L9ة8r7kzmݷ^f[N]:&_y\h.Z;ϾlEѩ_wTW'=vBnUHYvuߍsN瞚-.71GKF ;g:ON ZLX_[Xk'UtڗkJթc\Xr-7̫vI^6n9+W]ڦNgsfS2kҬ+ZA_{1{s.43*VNyW+}m.+4)}üy*Oϖ![)!/S+z!w+is{]V,e^#ͳTajbͬԲv>}}L^\HR([6; =gnuj۩{vݗNM6-x-K<'uF0<~Z&m{fSn}W'ޞޙbVYis{m>̣1NLvχHE i鰌&G20mV~mYn~Mq6$|4z l8-;o~-i|wBF<@<x @ ?x<᜽9{&KE<.~b1}|LZ^3JR̠we+ M:d:˖ .=8=?1ON94X8]lԫ [H74٪^l}q+0;o3[6NZRYylB&O`QCwC2S}>._;{(IR٧ybrM{CVfee<{Ǩ,j4,.ݺgٲ$_/u_j=!/TW'=ve_1[jr*Uk5:,ayO3ʤN)KU$w?7m^}J0"Q8<ЃiQ55-0Y7럽]=2d}2;deje[/N{e>3{ [Eyxy|=f54lfmۧkno:#~jfLF9Iʖ%Wve>~xykR6|e=G\ ï)f8Iv qZ?nw>*[ 1<D<x @ ?x\̇]cW\~~PscF%0g^pޫ,Yj([kFSVV: Ӣ*OR1#̬X053Lu>`|>\ގiSY>3Ӳ˺n5t(]TǬNAlxXH.3LR26WYn Zf沧&}i#SB BʚQ'ߗS_=m]}3_IY^aحu֩+nW+VT筿U6KZS.{U٬!f.˦iک8˯gθiT]jyӚZ?\ vBFiXL:-7fWSoHɁ_gܙܩN> ʛfڟ掣v Y_>-睗'C˲$|tˁt'䪳ӟ-He;J2+3_ek!+89<ϼ\sFi4_.6]:\ ;hu*++3ړcse{%?Y+xCNb]o|c`N?¾3(4Nۮӽ!&ɚX+_v]>XAY,I6r_jV]gft'39+ɬuVX+?xY2|g]w˒2óe|gjM:֩: gUi}3{O=H g$IuүO|SO\-VWyya¬'WU:dɌuIeYe4i=<ϧ󹼓_eۦe>~Ej8ʼneB)x\yƻk3li$Ź/vB;=\wۼpNgܧPL]ݩ׹hy}TjNTq UV ю%A;a^öjsop51Һw,WdW;|h}tj+ʓ:륢8)&dn^͌ZݲZ;ʌ9ϩrSyW{Y>SߘyryŚYe|:f-PX&; Uxr޾2rôY7;+J[rթN׽zeiyu|:mES!;C*Mvkm:-w$)9g~-.Y-rksuG忓*SyǬya=RFg/_gN6N^i]w=$9䔣22AlU]6un~c IDATMq6ǚ_=ztlB۝J~4B!#G5i(!PB<ď;O17pD.|t O+O7uk{zO}:=E7G%@io3˦.jvi-rOdRq޻WuMb\|m`ᨨ>}*o> C_*98g92!~<>azjH,d5t<}>9VϞn-#|yn$Iw9,+e3>_:VXj$?|)//K5zܖrgxis>$F]9<9kJMWkk\όǵ/Oʡ;7J߰m>?^5W{ҷOO_X,XL /l\yzRӬw8I ͳQ^)דyf|F_v>;6ou6ɯ(2 2Ϙn?e5ZtJǦy홱<'9ߜܶ@bB!BR(+KyfdFܞ k9:{|'?b6Pͬ[g33rSNe}/6?>ꎽJBR~L[}LO޽\YRּu.W_~;sݫ,Ku4u[ȴ{\8z|<OOͼa^dϯ6'O^:lӧc}^y,N3ᇭ[vkzJ~vü}2<_zU$e-WɪMe<&]jZu#[v咤*{ܽ\Mx!7\ن9 R'IoAfЦ e7W?ڽgcQE4?B5VH K̹>N?530;w8Skg#o~U6?,i{r.mg]k ؼzyoSѪGvy6ڧO޹_e'^무ι{Y[&^|C>跹msgei|t\`5-KqyƓsɐ73azEpMߨ.OZmMf.<Fmvү_=P#jhc~RcJ(!P^([ɖ=ڧЕҼy`Z L` TCxJT/u6J.]ҡU\*Ҥmy,UX"U, $I2rjh:Ym-yhp ZdV|мA蔍4|Rिfz5g~C9u>Yyf_V.\V\ٹQY leYj[y*Q+nסyr%][Aک۸U:6 ozY$OsSrMڷ#瑻Jmψ;.="}>:lm.|*/?>6cǎرҠNs.,/q=\ݹ??MLq="X*/_qzFۮd\Ye9WnVL}{YON!_ӻ3ҿ+7Q$ɌÅy?b Og^ϛ]L/M(ffvdujڧcYRt伱y^7*_{HX\dZ<X IG9;};|}L>装r͛Yޭ/,=zt_SVx @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJ(!PB<x @ 1%cJT, ,^7.'N/n2͚6M׮]h`{aܸTVVf 7Yy3n܋dV,b'LHk$B0;> bqkAAqKZ3Q eJ_#,x~+,x;/&^}2`1(߇x (ecɝyŽDž?;"QKQrB(IjR Zj pHSCXZ6@dL@ӴF%OQS^ ǃ _Yk=z=|gc۳->+R,ܺbט1:/4N<X9x`gjQ/ngyF=e?@oF y`JF^租vj{NZb9qv)V3‹{OΈ/>/ 䤓NLZ .LQ92uib%xqV*yi\8e,<h]x|5ZJx OVKRIV{_QVSR__߲]vgVRVRVW%Sk c+>`}OO|2rtO޽[t`%۷Bl[@1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<MK|q iQx`]VO72ejz^z1[^۔ǯj3EQpX%lNGny5oSZϘ1#E~3fh3u/]S5oSZ iZr{{hݖ_.KXPI@Ԭ:Z @+x㢰2+Oye582&ԼX $y\sy[sv+{44 d+m"`jemn}sO]Kҩ{)ޚy0hNPd 20ptxG{Mx öo ~s9Qq\ٲ><#6\#nsz>^Ukx,|ei(ivt۬O5$G| ܤ}Oh*lN~y2O3yv%74ҽkl'{~Lhl oʡ9[m=:vJ_Z~Zmѵ澔_\|g~Css4M`WӴս%6n?hpq\/qԆ]n֫Oa_efky6|i eͲgeso/vHkZkӧoEjЬiuׅqf5[ LJ" Gt5ݶ=e3۶[ӡslgr/wu?im+qnwnG t>Ijtc-}mF93~ ^VdWfYeԟn2_s¾!|}T6Tn=*Zzf2ܖi7~Ǝ8;C6˓uNr]z U-弜s8^U_gKn־g44|G7~vNѹMΙͱ=  |̋s_ȍ3ͅ_z!.<+G mM&]֘w@ۏ{lPI*6L-A G{o>4)RɛFfޜʐڈ^{n|.pXLjP:oIu演_Œ9⨬7ԣirV <"707<{vH#:+3p$ou۴M虗#-(қg/#+W?Ӷ]s;766W~tlYdVvvm=` جX}=<'2`ùgC6J5N}D>vEΙәvu6nȜfYb92anaYy))z-:V+fgvisv]6YNsSm3b 1kF=Z).`)q2nʬ|l1 NzOKsEqQ$Lc3bi&}1')*hՠEޘxQjZ|׳ݢ=5?ՐM?{>\]pJ6? [ν&OlX9T:~$[WR+7~Z\+cۊ{km3ƟTmnE%R5xuќ>K^x)<=8,OS,'(._Xk6)th7N&X T籺hX-x6wf-?E]6v}sR‚JuiuAY,5kE-i/xUo4J5|]!Ek/,_} Z<\=tT)j_g')5x>E]ڴIj9ͼT3jeYm+Z|]U_Zjsय़LF5 yGyᐜmӷLovZIwޫ}RLD:93ce9wޮgr3a1٤55g-SW$ KYh׺I'}-~cxy`ug1[O5SsEOι'ulӳ.)6Kh.n{Lۺ9/EE~)fZ}3.>=nt,ѧپO1qb8z*߼<ѹSιhw)D;g_stjFstlx9|]v? oWYhՀ0qf2kfcyi娛/ːnI3azU9"k6^kVi|hnPM:tMso J;SyOҰ]'m&[n;mo#c//u̞yw5'ʙk=pu]idw$#{9ԭnvp>r,2gOis_/A|U:sz>_\336g=_FQn}59.[G^e󝾘>ͫX5Uک_wcܩJڭ)6핝w>86^cy1y[sػrΡ# \Δd̏͸C1w|7k8.Nv4titt$nt =C6l~??H;iwƞv[r%Cu~rtmlzyE-~Ze+8pȐwu/|rj_Vj~`<~[^ͻTJ?~E<ZԳ^Zem[/V[vVWקVR VZV~}ԽfͩyҢk׮̔+j%GК<3tmVz5mJ>}'wO>W^yE>]ӧweQК5mJ<۷OKVC 1%cJ(P"<Dx @6 Wa*i$*PAjiӱcǕ=V1@1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ0e%=IENDB`qdmr-0.12.3/doc/dmr-intro/fig/repeater.tex000066400000000000000000000025021501654372000203200ustar00rootroot00000000000000\newcommand{\repeater}[3]{% \node ({#1}) at ({#2}) {% \begin{tikzpicture}% \draw [black,thick] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);% \draw [black,thick,domain=-45:225] plot ({0.2*cos(\x)}, {0.5+0.2*sin(\x)});% \draw [black,thick,domain=-45:225] plot ({0.4*cos(\x)}, {0.5+0.4*sin(\x)});% \node (xxx) at (0,-.2) {{#3}};% \end{tikzpicture}% } % } \newcommand{\activerepeater}[3]{% \node ({#1}) at ({#2}) {% \begin{tikzpicture}% \draw [black,thick] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);% \draw [red,thick,domain=-45:225] plot ({0.2*cos(\x)}, {0.5+0.2*sin(\x)});% \draw [red,thick,domain=-45:225] plot ({0.4*cos(\x)}, {0.5+0.4*sin(\x)});% \node (xxx) at (0,-.2) {{#3}};% \end{tikzpicture}% } % } \newcommand{\user}[3]{% \node ({#1}) at ({#2}) {% \begin{tikzpicture}% \draw [black,fill=black] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);% \draw [black,fill=black] (0,.5) circle (.2); % \node (xxx) [text width=0.6cm, align=center] at (-.35cm,-.4) {{#3}};% \end{tikzpicture}% } % } \newcommand{\activeuser}[3]{% \node ({#1}) at ({#2}) {% \begin{tikzpicture}% \draw [red,fill=red] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);% \draw [red,fill=red] (0,.5) circle (.2); % \node (xxx) [text width=0.6cm, align=center] at (-.35cm,-.4) {{#3}};% \end{tikzpicture}% } % } qdmr-0.12.3/doc/dmr-intro/fig/repeater_local.tex000066400000000000000000000016111501654372000214720ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT}; \user{u2}{ 2,0}{DL1XYZ}; \user{u3}{ 4,0}{DL2XYZ}; \draw[dotted] (5,4) -- (5,-1); \activeuser{u4}{ 6,0}{DL3XYZ}; \user{u5}{ 8,0}{DL4XYZ}; \user{u6}{10,0}{DL5XYZ}; \activerepeater{R1}{1,3}{DB0ABC}; \repeater{R2}{3,3}{DB0DEF}; \activerepeater{R3}{7,3}{DB0GHI}; \activerepeater{R4}{9,3}{DB0JKL}; \draw[->] (u1) -- node[above,rotate=70]{GC: TG9} (R1); \draw[->] (R1) -- node[above,rotate=-70]{GC: TG9} (u2); \draw[->] (u4) -- node[above,rotate=70]{GC: TG8} (R3); \draw[->] (R3) -- node[above,rotate=-70]{GC: TG8} (u5); \draw[->] (R4) -- node[above,rotate=-70]{GC: TG8} (u6); \path[->] (R3) edge[dashed,bend left] node[above]{via Netzwerk} (R4); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/repeater_privatecall.tex000066400000000000000000000011011501654372000227000ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT 2621370}; \activerepeater{R1}{1,3}{DB0ABC}; \draw[dotted] (2,4) -- (2,-1); \user{u2}{ 4,0}{I/DL2XYZ\\2621234}; \activerepeater{R2}{3,3}{I0ABC}; \draw[->] (u1) -- node[above,rotate=70]{PC: 2621234} (R1); \draw[->] (R2) -- node[above,rotate=-70]{PC: 2621234} (u2); \path[->] (R1) edge[dashed,bend left] node[above]{via Netzwerk} (R2); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/simplex_allcall.tex000066400000000000000000000011721501654372000216600ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT}; \user{u2}{ 6,1}{DL1XYZ, TG99}; \user{u3}{ 6,0}{DL2XYZ, TG99}; \user{u4}{ 6,-1}{DL3XYZ}; \path[->] (u1) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{All Call} (u2); \path[->] (u1) edge node[above]{$433.450 MHz$} node[below]{All Call} (u3); \path[->] (u1) edge[bend right] node[above, rotate=-10]{$433.450 MHz$} node[below, rotate=-10]{All Call} (u4); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/simplex_groupcall.tex000066400000000000000000000010101501654372000222330ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT}; \user{u2}{ 6,1}{DL1XYZ, TG99}; \user{u3}{ 6,0}{DL2XYZ, TG99}; \user{u4}{ 6,-1}{DL3XYZ}; \path[->] (u1) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{GC: TG99} (u2); \path[->] (u1) edge node[above]{$433.450 MHz$} node[below]{GC: TG99} (u3); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/simplex_privatecall.tex000066400000000000000000000010531501654372000225600ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT}; \activeuser{u2}{ 6,1}{DL1XYZ, TG99}; \user{u3}{ 6,0}{DL2XYZ, TG99}; \user{u4}{ 6,-1}{DL3XYZ}; \path[->] (u1) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{PC: DL1XYZ} (u2); \path[->] (u2) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{PC: DM3MAT} (u1); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/talkgroup_ex1a.tex000066400000000000000000000013331501654372000214400ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT}; \user{u2}{ 2,0}{DL1XYZ}; \user{u3}{ 6,0}{DL2XYZ}; \draw[dotted] (7,4) -- (7,-1); \user{u4}{10,0}{I/DL3XYZ}; \activerepeater{R1}{1,3}{DB0ABC, TG2621}; \activerepeater{R2}{5,3}{DB0DEF, TG2621}; \repeater{R3}{9,3}{I0ABC}; \path[->] (u1) edge node[above,rotate=70]{GC: TG2621} (R1); \path[->] (R1) edge node[above,rotate=-70]{GC: TG2621} (u2); \path[->] (R2) edge node[above,rotate=-70]{GC: TG2621} (u3); \path[->] (R1) edge[bend left] node[above]{GC: TG2621} (R2); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/talkgroup_ex1b.tex000066400000000000000000000015551501654372000214470ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \user{u1}{ 0,0}{DM3MAT}; \user{u2}{ 2,0}{DL1XYZ}; \user{u3}{ 6,0}{DL2XYZ}; \draw[dotted] (7,4) -- (7,-1); \activeuser{u4}{10,0}{I/DL3XYZ}; \activerepeater{R1}{1,3}{DB0ABC, TG2621}; \activerepeater{R2}{5,3}{DB0DEF, TG2621}; \activerepeater{R3}{9,3}{I0ABC, (TG2621)}; \path[->] (u4) edge node[above,rotate=-70]{GC: TG2621} (R3); \path[->] (R1) edge node[above,rotate=70]{GC: TG2621} (u1); \path[->] (R1) edge node[above,rotate=-70]{GC: TG2621} (u2); \path[->] (R2) edge node[above,rotate=-70]{GC: TG2621} (u3); \path[->] (R3) edge[bend right] node[below]{GC: TG2621} (R2); \path[->] (R3) edge[bend right] node[above]{GC: TG2621} (R1); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/talkgroup_ex1c.tex000066400000000000000000000015531501654372000214460ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT}; \user{u2}{ 2,0}{DL1XYZ}; \user{u3}{ 6,0}{DL2XYZ}; \draw[dotted] (7,4) -- (7,-1); \user{u4}{10,0}{I/DL3XYZ}; \activerepeater{R1}{1,3}{DB0ABC, TG2621}; \activerepeater{R2}{5,3}{DB0DEF, TG2621}; \activerepeater{R3}{9,3}{I0ABC, (TG2621)}; \path[->] (u1) edge node[above,rotate=70]{GC: TG2621} (R1); \path[->] (R1) edge node[above,rotate=-70]{GC: TG2621} (u2); \path[->] (R2) edge node[above,rotate=-70]{GC: TG2621} (u3); \path[->] (R3) edge node[above,rotate=-70]{GC: TG2621} (u4); \path[->] (R1) edge[bend left] node[below]{GC: TG2621} (R2); \path[->] (R1) edge[bend left] node[above]{GC: TG2621} (R3); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/timeslot.tex000066400000000000000000000024761501654372000203630ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usepackage{pgfplots} \usetikzlibrary{shapes.geometric} \usetikzlibrary{decorations, decorations.text} %\input{repeater} \begin{document} \begin{tikzpicture} \draw[|-,dotted, semithick] (-1,-0.2) -- (0,-0.2); \draw[|-,semithick] (0,-0.2) -- (1,-0.2); \draw[|-,semithick] (1,-0.2) -- (2,-0.2); \draw[|-,semithick] (2,-0.2) -- (3,-0.2); \draw[|-,semithick] (3,-0.2) -- (4,-0.2); \draw[|-,semithick] (4,-0.2) -- (5,-0.2); \draw[|-,semithick] (5,-0.2) -- (6,-0.2); \draw[|->,dotted,semithick] (6,-0.2) -- (7,-0.2); \node at (7, -.5) {$t$}; \draw [thick,decoration={brace,mirror},decorate] (0,-0.4) -- (1,-0.4) node [pos=0.5, anchor=north,yshift=-0.55] {$30\ ms$}; \fill[red!30] (0.1,0) -- (0.1,1) -- (0.9,1) -- (0.9,0) -- cycle; \node at (0.5,0.5) {TS 1}; \fill[blue!30] (1.1,0) -- (1.1,1) -- (1.9,1) -- (1.9,0) -- cycle; \node at (1.5,0.5) {TS 2}; \fill[red!30] (2.1,0) -- (2.1,1) -- (2.9,1) -- (2.9,0) -- cycle; \node at (2.5,0.5) {TS 1}; \fill[blue!30] (3.1,0) -- (3.1,1) -- (3.9,1) -- (3.9,0) -- cycle; \node at (3.5,0.5) {TS 2}; \fill[red!30] (4.1,0) -- (4.1,1) -- (4.9,1) -- (4.9,0) -- cycle; \node at (4.5,0.5) {TS 1}; \fill[blue!30] (5.1,0) -- (5.1,1) -- (5.9,1) -- (5.9,0) -- cycle; \node at (5.5,0.5) {TS 1}; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/trunk_net_ex1.tex000066400000000000000000000011041501654372000212740ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \user{r1}{ 0,0}{Reinigung 1}; \user{r2}{ 2,0}{Reinigung 2}; \draw[dotted] (3,4) -- (3,-1); \user{s1}{ 4,0}{Sicherheit 1}; \user{z} { 6,0}{Zentrale}; \draw[dotted] (7,4) -- (7,-1); \user{s2}{ 8,0}{Sicherheit 2}; \user{r3}{10,0}{Reinigung 3}; \repeater{R1}{1,3}{Terminal 1, TG: R,S}; \repeater{R2}{5,3}{Terminal 2, TG: R,S}; \repeater{R3}{9,3}{Vorfeld, TG: S}; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/trunk_net_ex2.tex000066400000000000000000000017111501654372000213010ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{r1}{ 0,0}{Reinigung 1}; \user{r2}{ 2,0}{Reinigung 2}; \draw[dotted] (3,4) -- (3,-1); \activeuser{s1}{ 4,0}{Sicherheit 1}; \activeuser{z} { 6,0}{Zentrale}; \draw[dotted] (7,4) -- (7,-1); \user{s2}{ 8,0}{Sicherheit 2}; \activeuser{r3}{10,0}{Reinigung 3}; \activerepeater{R1}{1,3.5}{Terminal 1, TG: R,S}; \activerepeater{R2}{5,3.5}{Terminal 2, TG: R,S}; \activerepeater{R3}{9,3.5}{Vorfeld, TG: S}; \draw[->] (r1) -- node[above,rotate=74] {PC: Reinigung 3} (R1); \path[->,dashed] (R1) edge [bend left] node[above] {via Netzwerk} (R3); \draw[->] (R3) -- node[above,rotate=-74] {PC: Reinigung 3} (r3); \draw[->] (z) -- node[above,rotate=-74] {PC: Sicherheit 1} (R2); \draw[->] (R2) -- node[above,rotate=74] {PC: Sicherheit 1} (s1); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/trunk_net_ex3.tex000066400000000000000000000015471501654372000213110ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{r1}{ 0,0}{Reinigung 1}; \activeuser{r2}{ 2,0}{Reinigung 2}; \draw[dotted] (3,4) -- (3,-1); \user{s1}{ 4,0}{Sicherheit 1}; \activeuser{z} { 6,0}{Zentrale}; \draw[dotted] (7,4) -- (7,-1); \user{s2}{ 8,0}{Sicherheit 2}; \user{r3}{10,0}{Reinigung 3}; \activerepeater{R1}{1,3}{Terminal 1, TG: R,S}; \activerepeater{R2}{5,3}{Terminal 2, TG: R,S}; \repeater{R3}{9,3}{Vorfeld, TG: S}; \draw[->] (z) -- node[above,rotate=-74] {TG: R} (R2); \path[->,dashed] (R2) edge [bend right] node[above] {via Netzwerk} (R1); \draw[->] (R1) -- node[above,rotate=74] {TG: R} (r1); \draw[->] (R1) -- node[above,rotate=-74] {TG: R} (r2); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/trunk_net_ex4a.tex000066400000000000000000000015461501654372000214520ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{r1}{ 0,0}{Reinigung 1}; \activeuser{r2}{ 2,0}{Reinigung 2}; \draw[dotted] (3,4) -- (3,-1); \user{s1}{ 4,0}{Sicherheit 1}; \user{z} { 6,0}{Zentrale}; \draw[dotted] (7,4) -- (7,-1); \user{s2}{ 8,0}{Sicherheit 2}; \user{r3}{10,0}{Reinigung 3}; \activerepeater{R1}{1,3}{Terminal 1, TG: R,S}; \repeater{R2}{5,3}{Terminal 2, TG: R,S}; \activerepeater{R3}{9,3}{Vorfeld, TG: S,(R)}; \draw[->] (r3) -- node[above,rotate=-74] {TG: R} (R3); \path[->,dashed] (R3) edge [bend right] node[above] {via Netzwerk} (R1); \draw[->] (R1) -- node[above,rotate=74] {TG: R} (r1); \draw[->] (R1) -- node[above,rotate=-74] {TG: R} (r2); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/fig/trunk_net_ex4b.tex000066400000000000000000000017741501654372000214560ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{r1}{ 0,0}{Reinigung 1}; \activeuser{r2}{ 2,0}{Reinigung 2}; \draw[dotted] (3,4) -- (3,-1); \user{s1}{ 4,0}{Sicherheit 1}; \activeuser{z} { 6,0}{Zentrale}; \draw[dotted] (7,4) -- (7,-1); \user{s2}{ 8,0}{Sicherheit 2}; \activeuser{r3}{10,0}{Reinigung 3}; \activerepeater{R1}{1,3}{Terminal 1, TG: R,S}; \activerepeater{R2}{5,3}{Terminal 2, TG: R,S}; \activerepeater{R3}{9,3}{Vorfeld, TG: S,(R)}; \draw[->] (z) -- node[above,rotate=-74] {TG: R} (R2); \path[->,dashed] (R2) edge [bend right] node[above] {via Netzwerk} (R1); \path[->,dashed] (R2) edge [bend left] node[above] {via Netzwerk} (R3); \draw[->] (R1) -- node[above,rotate=74] {TG: R} (r1); \draw[->] (R1) -- node[above,rotate=-74] {TG: R} (r2); \draw[->] (R3) -- node[above,rotate=-74] {TG: R} (r3); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/dmr-intro/script/000077500000000000000000000000001501654372000165275ustar00rootroot00000000000000qdmr-0.12.3/doc/dmr-intro/script/Makefile000066400000000000000000000001111501654372000201600ustar00rootroot00000000000000 clean: rm *.aux *.idx *.ilg *.ind *.log *.out *.pdf *.synctex.gz *.toc qdmr-0.12.3/doc/dmr-intro/script/script_de.tex000066400000000000000000000047761501654372000212430ustar00rootroot00000000000000\documentclass[11pt, a4paper,parskip=half]{scrartcl} \usepackage{standalone} \usepackage[utf8]{inputenc} \usepackage{geometry} \usepackage{graphics} \usepackage{subcaption} \usepackage{eurosym} \usepackage[ngerman]{babel} \usepackage{rotating} \usepackage{makeidx} \usepackage[tikz]{bclogo} \usepackage{tikz} \usetikzlibrary{shapes.geometric,patterns,snakes} \usepackage{hyperref} \hypersetup{colorlinks=true, linkcolor=blue, filecolor=blue, urlcolor=blue} \urlstyle{same} \title{DMR -- Digital Mobile Radio} \subtitle{Ein Mobilfunknetz für Funkamateure} \author{Hannes Matuschek, DM3MAT,\\\texttt{dm3mat [at] darc [dot] de}} \date{\today} \input{../fig/repeater} \newenvironment{merke}{\begin{bclogo}[couleur=blue!30,arrondi=.1,logo=\bccrayon,ombre=true]{Merke}}{\end{bclogo}} \newenvironment{hinweis}{\begin{bclogo}[couleur=blue!30,arrondi=.1,logo=\bcinfo,ombre=true]{Hinweis}}{\end{bclogo}} \newenvironment{achtung}{\begin{bclogo}[couleur=red!30,arrondi=.1,logo=\bcattention,ombre=true]{Achtung}}{\end{bclogo}} \newcommand{\adef}[1]{\emph{#1}\index{#1}} \newcommand{\aref}[1]{#1\index{#1}} \newcommand{\altdef}[2]{\emph{#1}\index{#1|seealso{#2}}} \makeindex \begin{document} \begin{titlepage} \maketitle \vfill \begin{abstract} Dieses Script soll eine Einführung in DMR (digital mobile radio) für den unbedarften Funkamateur oder jeden Interessierten sein. Ich versuche dem Leser Details solange zu verheimlichen, bis es absolut notwendig wird diese zu erklären. Die meisten Einführungen in DMR, die ich bisher gesehen habe, sind eher eine lange Liste von Begriffserklärungen, die ohne Erfahrung mit DMR schwer zu verstehen sind. Viel der empfundenen Komplexität von DMR, rührt aus dem Ursprung dieser Technik. DMR wurde für den kommerziellen Funk auf Großveranstaltungen oder großen industriellen Anlagen entwickelt, auch \href{https://de.wikipedia.org/wiki/B\%C3\%BCndelfunk}{Bündelfunk} genannt. Ich werde daher mit einem Beispiel erläutern wofür DMR entwickelt wurde und fange erst dann an zu erklären, wie DMR für den Amateurfunk eingesetzt wird. \end{abstract} \end{titlepage} \pagebreak \tableofcontents \pagebreak \input{script_de_01_vorwissen} \input{script_de_02_ursprung} \input{script_de_03_simplex} \input{script_de_05_privatecall} \input{script_de_04_lokal} \input{script_de_07_talkgroup} \input{script_de_06_textmsg} \input{script_de_11_technik} \input{script_de_09_roaming} \input{script_de_12_codeplug} \input{script_de_10_netze} \appendix \printindex \end{document}qdmr-0.12.3/doc/dmr-intro/script/script_de_01_vorwissen.tex000066400000000000000000000151201501654372000236430ustar00rootroot00000000000000\section{Vorwissen: Relaisbetrieb} \label{sec:vorwissen} \index{Relaisbetrieb} In diesem Abschnitt werde ich kurz den \emph{klassischen} FM\footnote{FM steht für \emph{Frequenzmodulation} und beschreibt ein analoges Modulationsverfahren, bei dem eine Trägerfrequenz im \emph{Rhythmus} der zu übertragenen Sprache in der Frequenz verschoben wird.}-Relaisbetrieb auf VHF\footnote{Als VHF (\emph{very high frequency}) werden die Frequenzen zwischen $30Mhz$ und $300MHz$ bezeichnet.} und UHF\footnote{Als UHF (\emph{ultra high frequency}) werden die Frequenzen zwischen $300Mhz$ und $3000MHz$ bezeichnet.} im Amateurfunk beschreiben. Die allermeisten lizenzierten Funkamateure werden dies noch aus der Prüfung zur Betriebstechnik oder aus eigener Erfahrung wissen. Wenn Sie sich aber für Amateurfunk interessieren oder selbst noch keine Erfahrung mit dem Relaisbetrieb haben, empfehle ich Ihnen diesen Abschnitt zu lesen. \begin{figure}[!ht] \centering \input{../fig/fm_simplex_a} \input{../fig/fm_simplex_b} \caption{Einfacher Simplexbetrieb, DM3MAT sendet auf der Frequenz $144.500 MHz$ direkt zu DL2XYZ. Dieser antwortet dann auf der selben Frequenz.} \label{fig:basicsimlpex} \end{figure} Die meisten Verbindungen zwischen zwei Funkamateuren finden im so genannten \adef{Simplexbetrieb} statt. Das heißt, die zwei Funkamateure senden und empfangen abwechselnd auf der selben Frequenz und die Verbindung zwischen ihnen ist direkt (siehe Abb. \ref{fig:basicsimlpex}). Dies funktioniert auf Kurzwelle\footnote{Als Kurzwelle oder einfach HF (\emph{high frequency}) werden Frequenzen zwischen $3MHz$ und $30MHz$ bezeichnet.} sehr gut und man kann damit weltweite Verbindungen aufbauen. Auf höheren Frequenzen verhält sich die Radiowelle zunehmend wie Licht und es wird auf VHF und UHF schwierig ohne viel Aufwand\footnote{Auch auf VHF und UHF können sehr große Entfernungen überbrückt werden, nur sind dann große Richtantennen oder ein sehr hoher Standort von Nöten.} wesentlich weiter als bis zum Horizont zu gelangen. Diese Tatsache schränkt die Reichweite gerade von Handfunkgeräten stark ein. Um dennoch einen größeren Bereich überbrücken zu können, wenn man nicht gerade über einen hohen Berg mit einer großen Antenne verfügt, können sogenannte \emph{Repeater} oder \emph{Relais} verwendet werden. \adef{Repeater}\index{Relais|seealso{Repeater}} sind automatisch arbeitende Amateurfunkstationen, die meist in exponierten Lagen (hoher Berg oder hoher Turm) installiert werden, um einen möglichst großen Bereich abdecken zu können. Ihre Aufgabe ist es, Aussendungen von Funkamateuren zu empfangen und gleichzeitig wieder auszusenden. Da diese Repeater gleichzeitig empfangen und senden müssen, können sie das nicht auf der selben Frequenz tun. Daher werden diese Repeater im sogenannten \adef{Duplexbetrieb} gefahren. Das heißt, der Repeater empfängt auf einer Frequenz (der sog. \adef{Eingabefrequenz}) und sendet eben dieses empfangende Signal gleichzeitig auf einer anderen Frequenz (der sog. \adef{Ausgabefrequenz}) wieder aus. \begin{figure}[!ht] \centering \input{../fig/fm_duplex_a} \input{../fig/fm_duplex_b} \caption{Einfacher Repeaterbetrieb, DM3MAT sendet auf der Eingabefrequenz $431.9625 MHz$ zum Repeater (DB0LDS) und dieser setzt das empfangende Signal direct auf der Ausgabefrequenz $439.5625 MHz$ wieder ab. Auf dieser Frequenz kann DL2XYZ das umgesetzte Signal wieder empfangen.} \label{fig:basicrepeater} \end{figure} Für das konkrete Beispiel in Abbildung \ref{fig:basicrepeater} bedeutet das, dass DM3MAT auf der Repeatereingabefrequenz (hier $431.9625 MHz$) sendet. Dieses Signal wird vom Repeater (hier DB0LDS) empfangen und gleichzeitig wieder auf der Ausgabefrequenz (hier $439.5625 MHz$) ausgesandt. Diese Aussendung kann nun von DL2XYZ auf der Repeaterausgabefrequenz empfangen werden. Die Antwort von DL2XYZ an DM3MAT folgt den gleichen Weg, hier sendet DL2XYZ auf der Repeatereingabefrequenz und DM3MAT kann diese Aussendung auf der Repeaterausgabefrequenz empfangenen. Auf diese Wiese können zwei Funkamateure miteinander kommunizieren, auch wenn sie sich nicht direkt erreichen können. \subsection{Echolink} \label{sec:echolink} \index{Echolink} Wenn zwei Funkamateure miteinander kommunizieren wollen, die sehr weit voneinander entfernt sind und somit nicht beide einen gemeinsamen Repeater erreichen können, gibt es die Möglichkeit zwei Repeater \emph{zusammenzuschalten}. \begin{figure}[!ht] \centering \input{../fig/fm_echolink_a} \input{../fig/fm_echolink_b} \input{../fig/fm_echolink_c} \caption{Repeaterbetrieb mit Echolink. DM3MAT verbindet die Repeater DB0SP (bei Berlin) und DB0LEI (bei Leipzig) per Echolink. Daraufhin können DM3MAT und DL2XYZ wie über einen gemeinsamen Repeater kommunizieren.} \label{fig:echolink} \end{figure} Diese Möglichkeit nennt sich \href{http://www.echolink.org/}{Echolink}. Dieses Netzwerk erlaubt es FM Repeater per Internet miteinander zu verbinden oder sich per Internet als einzelner Teilnehmer direkt mit einem Repeater zu verbinden. Viele FM Repeater sind in diesem Netzwerk zusammengeschlossen. Es ist auch häufig möglich\footnote{Dies hängt von der Konfiguration des Repeaters ab.} per Funk einen Repeater zu steuern und ihn mit einem anderen Repeater via Echolink zu verbinden. Dazu wird die sogenannte Echolink Nummer des Ziel Repeaters per DTMF Tonwahl an den Quellrepeater gesandt. Dies ist in Abbildung \ref{fig:echolink} (Oben) dargestellt. Hier sendet DM3MAT die Echolink Nummer 662699 des Relais DB0LEI bei Leipzig per DTMF an den Repeater DB0SP nahe Berlin. Dieser (DB0SP) verbindet sich dann mit dem Zielrepeater bei Leipzig (DB0LEI) über das Echolink Netzwerk. Alle weiteren Aussendungen die der Quellrepeater (DB0SP) nun empfängt werden nicht nur lokal auf der Ausgabefrequenz ausgesandt, sonder werden auch am Zielrepeater bei Leipzig (DB0LEI) ausgesandt (Abb. \ref{fig:echolink} Mitte). Somit kann DL2XYZ in Leipzig DM3MAT hören. Ebenso werden alle Aussendungen die der Zielrepeater (DB0LEI) empfängt via Echolink zum Quellrepeater bei Berlin übertragen und auch dort ausgesandt (Abb. \ref{fig:echolink} Unten). Auf diese weise können zwei Funkamateure (in diesem Beispiel DM3MAT \& DL2XYZ), die sich nicht in der Nähe des selben Repeaters befinden, dennoch miteinander kommunizieren. \begin{merke} Sobald zwei Repeater per Echolink miteinander verbunden sind, verhalten sich beide wie ein einziger Repeater. \end{merke} Es gibt überall auf der Welt FM Repeater die per Echolink erreichbar sind. Dadurch ist es möglich jederzeit weltweite Kontakte mit einfachsten Mitteln (FM Handfunkgeräte mit DTMF Funktion sind ab ca. \EUR{40} erhältlich) herzustellen.qdmr-0.12.3/doc/dmr-intro/script/script_de_02_ursprung.tex000066400000000000000000000240001501654372000234670ustar00rootroot00000000000000\section{DMR Einführung \& Ursprung} \label{sec:ursprung} DMR kurz für \emph{digital mobile radio} ist ein digitaler Funkstandard für Sprech- und Datenfunk. Das heißt, die Sprache wird nicht direkt per FM auf einem Kanal übertragen, sondern zuerst digitalisiert, mit einem verlustbehafteten Codec kodiert und erst dann als Datenpaket übertragen. Dies ermöglicht es, bei jedem Ruf\footnote{PTT Taste drücken, ins Funkgerät sprechen und dann die PTT Taste wieder loslassen.} zusätzliche Informationen wie Quelle und Ziel des Rufs mitzuübertragen. DMR wurde als Ersatz für den analogen Bündelfunk in der kommerziellen Anwendung entwickelt. Ein klassisches Beispiel für den kommerziellen Einsatz von DMR wäre ein Verkehrsflughafen. Damit ist nicht der Flugfunk auf dem Feld und in der Luft gemeint, sondern der Funkbetrieb zwischen dem ganzen Bodenpersonal. Auf so einem Flughafen arbeiten sehr viele Leute mit sehr unterschiedlichen Aufgaben. Da hätten wir (ohne Anspruch auf Vollständigkeit) \begin{itemize} \item Die Reinigungskolonne, \item die Sicherheitsleute wie Gepäckkontrolle oder Wachschutz, \item das Vorfeld, also die Betankung, die Gepäckverladung \& das Catering, \item die Betriebsfeuerwehr und \item die Zentrale. \end{itemize} All diese Mitarbeiter bekommen ein Funkgerät und sollen die folgenden Möglichkeiten haben: \begin{itemize} \item Direkte Kommunikation zur Zentrale, alle Personen sollen die Zentrale erreichen können. \item Direkte Kommunikation zwischen zwei Personen innerhalb ihrer Gruppe ohne das andere Gruppen gestört werden. Das heißt, die Reinigungskolonne sollte sich untereinander absprechen können, ohne die Betriebsfeuerwehr zu stören. \item Sogenannte Gruppenrufe einer Person an eine ganze Gruppe. Zum Beispiel ruft die Zentrale die gesamte Betriebsfeuerwehr an. Aber auch ein Anruf eines Wachschützers an alle anderen Wachschützer, um zum Beispiel Hilfe anzufordern. \end{itemize} Gleichzeitig ist so ein Flughafen ein riesiges Gelände. Das heißt, nicht alle Mitarbeiter können alle anderen Mitarbeiter direkt erreichen. Es müssen also Repeater aufgestellt werden, damit das gesamte Gelände und alle Innenräume per Funk abgedeckt sind. Daher wird häufig in jedem Gebäude mindestens ein Repeater aufgestellt. Vergleicht man nun die Ansprüche dieses Kommunikationsnetzes mit dem klassischen FM-Repeaterbetrieb (Abs. \ref{sec:vorwissen}), wird schnell deutlich, dass es sehr schwierig wird dieses Konzept per analog FM-Repeater umzusetzen. Vor allem wenn mehrere Repeater in einem Netz (ähnlich Echolink) verbunden sind. Jede Kommunikation zwischen zwei Personen würde dann das gesamte Kommunikationsnetz belegen. Besser wäre es, wenn nur jene Repeater aktiv würden, die für die Kommunikation zwischen zwei Teilnehmern nötig sind. Dann stünden alle anderen Repeater für weitere Verbindungen bereit. Dieses Routing von Verbindungen sollte aber automatisch geschehen, da die zwei Teilnehmer nicht immer wissen werden, wo sich die jeweils andere Person befindet und somit mit welchem Repeater sie sich verbinden müssen. Um solche komplexen Kommunikationsnetze realisieren zu können, ohne dass die Teilnehmer detailliertes Wissen über dessen physische Struktur\footnote{Wissen darüber wo sich welcher Repeater befindet und wo sich welche Teilnehemer aufhalten.} benötigen, wurde DMR entwickelt. \begin{merke} DMR hat mehr Ähnlichkeit mit einem Telefonnetz mit zusätzlichen Gruppenruf als mit klassischem FM-Repeaterbetrieb. \end{merke} Das heißt, jeder Teilnehmer und damit dessen Funkgerät besitzt eine eindeutige Nummer\index{DMR-ID}. Diese Nummer liegt im Bereich $1$--$16777215$. Und wie bei einem gewöhnlichen Telefonnetz, kann ein Teilnehmer einen Anderen mit seiner Nummer direkt anrufen. Dies wird \adef{Direktruf} oder auch \altdef{Private Call}{Direktruf} genannt. Außerdem werden Gruppen definiert, die wieder ihre eigene Nummer erhalten. Die sogenannte \adef{Sprechgruppe} oder auch \altdef{Talk Group}{Sprechgruppe} (\altdef{TG}{Sprechgruppe}). Diese Sprechgruppen dienen dazu, alle Mitarbeiter einer bestimmten Gruppe (z.B., den Wachschutz, die Betriebsfeuerwehr, etc.) gleichzeitig erreichen zu können. Das heißt, das Funkgerät einer Reinigungskraft muss wissen, dass es auf die Gruppenrufe der Sprachgruppe \emph{Reinigung} reagieren muss, aber alle anderen Sprechgruppen ignorieren soll. \begin{merke} Dieser Punkt ist sehr wichtig: Das DMR Netz selbst weiß nicht, welcher Teilnehmer zu welcher Gruppe gehört. Das Funkgerät des Teilnehmers wird so konfiguriert, dass es nur auf bestimmte Gruppenrufe reagiert. \end{merke} \begin{figure}[!ht] \centering \input{../fig/trunk_net_ex1} \caption{Ein Beispielnetzwerk für den hypothetischen Flughafen. Es gibt drei Reinigungskräfte, zwei Sicherheitsleute und eine Zentrale. Um das gesammte Gelände abdecken zu können, werden drei Repeater benötigt einer in Terminal 1, einer in Terminal 2 und einer im Vorfeld.} \label{fig:exnet1} \end{figure} In Abbildung \ref{fig:exnet1} sei ein Beispielnetzwerk für den Flughafen dargestellt (in Wirklichkeit viel größer und komplexer). Nun stellen wir uns die Situation vor, dass die Reinigungskräfte 1 \& 3 miteinander Sprechen wollen und gleichzeitig die \emph{Zentrale} mit \emph{Sicherheit 1}. In einem einfachen analog Netz, bei dem alle Repeater einfach zusammengeschaltet wären, würde das Gespräch zwischen \emph{Reinigung 1} \& \emph{3} das gesamte Netz blockieren und die Verbindung zwischen \emph{Zentrale} und \emph{Sicherheit 1} wäre nicht möglich. \begin{figure}[!ht] \centering \input{../fig/trunk_net_ex2} \caption{Zwei gleichzeitige Direktrufe (Private Calls, PC) in dem Beispielnetzwerk zwischen \emph{Reinigung 1 \& 3} sowie zwischen \emph{Zentrale} und \emph{Sicherheit 1}} \label{fig:exnet2} \end{figure} In einem DMR Netz hingegen, werden für einen Direktruf (Privat Call) nur jene Repeater verwendet, die dafür nötig sind. Dies ist in Abbildung \ref{fig:exnet2} zu sehen: \emph{Reinigung 1} startet einen Direktruf (Private Call) über ihren lokalen Repeater in \emph{Terminal 1}. Da das DMR Netzwerk weiß, über welchen Repeater \emph{Reinigung 3} zuletzt aktiv war, wird der Direktruf vom DMR Netz über eben diesen Repeater auf dem Vorfeld etabliert. Der Repeater im Terminal 2 hingegen wird für diesen Direktruf nicht aktiv. Daher steht dieser Repeater weiterhin zur Verfügung. Dies nutzt die Zentrale um \emph{Sicherheit 1} per Direktruf zu erreichen. \begin{merke} Das DMR-Netz weiß lediglich über welchen Repeater ein Teilnehmer zuletzt aktiv war. Das Netz wird daher versuchen, einen Direktruf für diesen Teilnehmer über eben diesen Repeater zu vermitteln. \end{merke} Solange das Gespräch zwischen \emph{Reinigung 1 \& 3} anhält sind aber die Repeater im Terminal 1 und auf dem Vorfeld belegt. Das heißt, die Zentrale kann \emph{Reinigung 2} und \emph{Sicherheit 2} nicht erreichen. Dies klingt schlimmer als es ist. Im Gegensatz zu klassischen Telefonaten gilt im DMR Netz ein Direktruf als unterbrochen sobald ein Teilnehmer die PTT Taste loslässt. Daher kann die Zentrale in den Umschaltpausen des Gespräches \emph{dazwischenrufen} und so zum Beispiel \emph{Sicherheit 2} erreichen. Im nächsten Beispiel (Abbildung \ref{fig:exnet3}) will die Zentrale alle Reinigungskräfte erreichen. Dazu macht sie einen Gruppenruf zur Sprechgruppe/Talk Group \emph{Reinigung} (R für Reinigung, S für Sicherheit). Damit erreicht sie die \emph{Reinigung 1 \& 2} problemlos, aber \emph{Reinigung 3} empfängt diesen Gruppenruf nicht. Dies liegt daran, dass das DMR Netz nicht weiß, welche Personen zu welcher Gruppe gehören. Da sich Reinigungskräfte üblicherweise nicht auf dem Vorfeld herumtreiben, hat der Repeater auf dem Vorfeld die Sprechgruppe \emph{Reinigung (R)} nicht \emph{abonniert} und leitet daher keine Gruppenrufe für diese Sprechgruppe weiter. \begin{figure}[p] \begin{subfigure}{\linewidth} \centering \input{../fig/trunk_net_ex3} \caption{Ein Gruppenruf zur Sprechgruppe \emph{Reinigung} von der Zentrale aus. Der Teilnehmer \emph{Reinigung 3} wird aber nicht erreicht, da der Vorfeldrepeater diese Sprechgruppe nicht abonniert hat.} \label{fig:exnet3} \end{subfigure}\vspace{0.5cm} \begin{subfigure}{\linewidth} \centering \input{../fig/trunk_net_ex4a} \caption{Teilnehmer \emph{Reinigung 3} abonniert die Sprechgruppe \emph{Reinigung} temporär auf dem Vorfeldrepeater, indem er einen Gruppenruf zu dieser Sprechgruppe startet.} \label{fig:exnet4a} \end{subfigure}\vspace{.5cm} \begin{subfigure}{\linewidth} \centering \input{../fig/trunk_net_ex4b} \caption{Nach der temporären Abonnierung, ist nun der Teilnehmer \emph{Reinigung 3} auch auf dem Vorfeld erreichbar.} \label{fig:exnet4b} \end{subfigure} \caption{Temporäres Abonnement einer Sprechgruppe auf einem Repeater.} \label{fig:exnet4} \end{figure} Damit die Reinigungskraft 3 jedoch für Gruppenrufe erreichbar bleibt, muss sie die Sprechgruppe \emph{Reinigung} auf dem Vorfeldrepeater temporär abonnieren. Dazu startet sie einen Gruppenruf zur Sprechgruppe \emph{Reinigung} vom Vorfeldrepeater aus (siehe Abb. \ref{fig:exnet4a}). Damit abonniert der Vorfeldrepeater diese Sprechgruppe für eine begrenzte Zeit\footnote{Diese Zeit wird auf jedem einzelnen Repeater konfiguriert. Üblich sind Zeiten zwischen $10$ und $30$ Minuten.} und wird während dieser Zeit Gruppenrufe dieser Sprechgruppe aussenden. Dieses temporäre Abonnement wird jedes mal erneuert oder wiederhergestellt, wenn ein Gruppenruf zu dieser Sprechgruppe von diesem Repeater aus initiiert wird. Das heißt, das Abonnement verlängert sich jedes mal, wenn \emph{Reinigung 3} einen Gruppenruf zur Sprechgruppe \emph{Reinigung} startet oder darauf antwortet\footnote{Das Antworten auf einen Gruppenruf ist technisch identisch zum Start eines neuen Gruppenrufs.}. Mit diesen Beispielen sind die wichtigsten Grundbegriffe von DMR (DMR-ID, Talk Groups, Private sowie Group Call \& Talk Group Abonnement) eingeführt und deren Verwendung in einem Beispiel DMR-Netz erläutert worden. In den nächsten Absätzen wird die Verwendung von DMR im Amateurfunk beschrieben. qdmr-0.12.3/doc/dmr-intro/script/script_de_03_simplex.tex000066400000000000000000000121421501654372000232700ustar00rootroot00000000000000\section{DMR Simplex Betrieb} \label{sec:simplex} Die einfachste Form eines DMR QSOs\footnote{Für alle nicht-Funkamateure: QSO ist eine Abkürzung die eine Verbindung zwischen zwei Amateurfunkstationen beschreibt, gelesen als \emph{Verbindung} oder \emph{Gespräch}.} ist der \aref{Simplexbetrieb}. Dabei wird eine direkte Verbindung zwischen zwei DMR Funkgeräten aufgebaut. Wie beim DMR Repeaterbetrieb, kann so eine Verbindung ein Direktruf, Gruppenruf oder auch ein sogenannter \adef{Rundumruf} (auch \altdef{All Call}{Rundumruf} genannt) sein. \begin{figure}[!ht] \centering \input{../fig/simplex_privatecall} \caption{Beispiel eines DMR Simplex Direktrufs von DM3MAT an DL1XYZ.} \label{fig:splxpc} \end{figure} In Abbildung \ref{fig:splxpc} ist ein einfacher Simplex Direktruf von DM3MAT an DL1XYZ dargestellt sowie dessen Antwort. Beide senden und empfangen auf der selben Frequenz (hier der DMR Anruffrequenz von $433.450 MHz$). Auch wenn die beiden anderen Teilnehmer in der Nähe (DL2XYZ \& DL3XYZ) diesen Ruf physikalisch empfangen, bleiben deren Funkgeräte stumm. Wie dem auch sei, der Kanal ist jedoch während dieses Direktrufes belegt. An dieser Stelle ist es sinnvoll zu erwähnen, dass wenn DL1XYZ direkt auf den Direktruf von DM3MAT antwortet, indem er die PTT Taste drückt, er mit einem Direktruf an DM3MAT antwortet, ohne dafür die Nummer von DM3MAT aus seinen Kontakten heraussuchen zu müssen. Dieser direkte Rückruf funktioniert nur wenige Sekunden nach dem Ende des initialen Direktrufs durch DM3MAT. Nach dieser Zeitspanne wird beim drücken auf die PTT der \aref{Standardkontakt} für diesen Kanal angerufen, der für jeden Kanal im Funkgerät festgelegt werden kann (siehe Abs. \ref{sec:cp:channel}). Diese Zeitspanne (genannt \aref{Hangtime}) lässt sich ebenfalls im Funkgerät einstellen. \begin{figure}[!ht] \centering \input{../fig/simplex_groupcall} \caption{Beispiel eines DMR Simplex Gruppenrufs von DM3MAT an die Sprechgruppe TG99.} \label{fig:splxgc} \end{figure} Um im Simplexbetrieb nicht nur einzelne Teilnehmer anrufen zu können, sind auch Gruppenrufe im Simplexbetrieb möglich. Eine beliebte Sprechgruppe (Talk Group) für den Simplexbetrieb ist die Gruppe mit der Nummer 99 (TG99 abgekürzt, für \emph{Talk Group 99}). Solche Gruppenrufe werden dann von allen Funkgeräten empfangen, die entsprechend konfiguriert wurden. Wie beim Repeaterbetrieb muss auch beim Simplexbetrieb dem Funkgerät mitgeteilt werden, welche Sprechgruppen es auf welchen Kanälen empfangen soll (siehe Abs. \ref{sec:cp:grouplist}). In Abbildung \ref{fig:splxgc} ist solch ein Simplex Gruppenruf von DM3MAT an die Sprechgruppe TG99 dargestellt. Da DL1XYZ und DL2XYZ ihre Funkgeräte so konfiguriert haben, dass sie die TG99 empfangen, hören sie den Ruf von DM3MAT. Da DL3XYZ dies nicht gemacht hat, empfängt er diesen Ruf nicht. DL1XYZ und DL2XYZ können nun auf diesen Gruppenruf antworten, wenn sie innerhalb der sogenannten \adef{Haltezeit} (\altdef{Hangtime}{Haltezeit}) auf ihre PTT Taste drücken. Sie würden dann ebenfalls mit einem Gruppenruf zur TG99 antworten (der direkte Rückruf funktioniert auch für Gruppenrufe), auch wenn sie einen anderen Standardkontakt für diesen Simplexkanal eingestellt haben. \begin{figure}[!ht] \centering \input{../fig/simplex_allcall} \caption{Beispiel eines DMR All Calls von DM3MAT alle die ihn hören können.} \label{fig:splxac} \end{figure} Um wirklich sicher zu gehen, dass ein Ruf auf einem Simplexkanal von allen empfangen werden kann, sollte ein sogenannter \adef{All Call} verwendet werden. Dies ist ein spezieller Ruf an eine ganz bestimmte Nummer ($16777215$), die von allen Geräten empfangen werden, unabhängig von der Konfiguration dieser Geräte. In diesem Beispiel wird somit der Ruf von DM3MAT auch von DL3XYZ empfangen. Durch den direkten Rückruf ist es allen Teilnehmern wieder möglich auf den All Call von DM3MAT zu antworten, auch wenn diese Teilnehmer den All Call nicht als den Standardkontakt für diesen Kanal konfiguriert haben. \begin{merke} Zusammengefasst: Ein DMR-Kanal besitzt eine Sende- \& Empfangsfrequenz (bei Simplex identisch), einen Standardkontakt der angerufen wird, wenn die PTT Taste auf diesem Kanal gedrückt wird und eine Liste von Gruppenrufen, die auf diesem Kanal empfangen werden sollen. \end{merke} \subsection{DMR Simplex Frequenzen} \begin{table}[!ht] \centering \begin{tabular}{|l|c||l|c|} \hline Name & Frequenz & Name & Frequenz \\ \hline \hline S0 (Anruf) & $433.4500 MHz$ & S4 & $433.6500 MHz$ \\ S1 & $433.6125 MHz$ & S5 & $433.6625 MHz$ \\ S2 & $433.6250 MHz$ & S6 & $433.6750 MHz$ \\ S3 & $433.6375 MHz$ & S7 & $433.6875 MHz$ \\ \hline \end{tabular} \caption{Liste der acht üblichen DMR Simplexkanäle. Der Kanal \emph{S0} ist der Anrufkanal.} \label{tab:simplex} \end{table} Im Tabelle \ref{tab:simplex} sind die acht üblichen Simplexkanäle aufgelistet. Der Simplexkanal \emph{S0} ist dabei der Anrufkanal. Gerade in Ballungsgebieten sollte für das eigentliche QSO der Kanal vom Anrufkanal auf einen der sieben weiteren Simplexkanäle \emph{S1-7} gewechselt werden, um den Anrufkanal nicht zu blockieren. qdmr-0.12.3/doc/dmr-intro/script/script_de_04_lokal.tex000066400000000000000000000041511501654372000227130ustar00rootroot00000000000000\section{Lokaler Repeater Betrieb} \label{sec:lokal} Eigentlich ist es das Ziel von DMR, transparent gegenüber Repeatern zu sein. Das heißt, es spielt keine Rolle für den Teilnehmer welchen Repeater er benutzt. Er wird immer die selben Teilnehmer erreichen können. Dieses Konzept wird aber durch die Sprechgruppen mit den Nummern $8$ und $9$ durchbrochen. \begin{figure}[!ht] \centering \input{../fig/repeater_local} \caption{Beispiel mit zwei Regionen (links \& rechts) mit je zwei Repeatern.} \label{fig:tg9tg8} \end{figure} Die Sprechgruppe 9 (kurz TG9) ist die sogenannte \emph{lokale} Sprechgruppe. Gruppenrufen zu dieser Sprechgruppe werden nicht über das Netzwerk weitergeleitet, sonder nur lokal vom jeweiligen Repeater ausgesandt. Dieser Fall ist in Abbildung \ref{fig:tg9tg8} links dargestellt. Hier sendet DM3MAT einen Gruppenruf zur TG9 über den Repeater DB0ABC. Dieser Ruf wird nicht an weitere Repeater übertragen und ist somit nur in der Umgebung des Repeaters zu hören. DL1XYZ befindet sich in der Nähe des Repeaters und kann den Ruf empfangen, wenn er sein Funkgerät so konfiguriert hat, dass es Gruppenrufe an die TG9 empfängt. Die Sprechgruppe 8 (TG8) ist die sogenannte \emph{regionale} Sprechgruppe. Ein Gruppenruf zu dieser Sprechgruppe wird meist durch alle Repeater innerhalb einer Region ausgesandt. Welche Repeater zu einer Region gehören und wie groß diese Region letztendlich ist, entscheiden die Administratoren der jeweiligen Repeater. Sie entscheiden ob ihre Repeater zu einer Region gehören sollen oder nicht. Im Beispiel in Abbildung \ref{fig:tg9tg8} rechts, sendet DL3XYZ einen Gruppenruf zur Sprechgruppe 8 an den Repeater DB0GHI, dieser sendet diesen Gruppenruf selbst aus und leitet ihn an alle Repeater im regionalen Verbund (auch \adef{Cluster}) weiter. In diesem Fall auch an den Repeater DB0JKL. Somit können alle Teilnehmer in der Region diesen Gruppenruf empfangen, solange sie ihre Funkgeräte entsprechend konfiguriert haben. In diesem Beispiel empfängt somit nicht nur DL4XYZ den Gruppenruf sondern auch DL5XYZ, auch wenn er sich nicht in der Nähe des Repeaters DB0GHI befindet. qdmr-0.12.3/doc/dmr-intro/script/script_de_05_privatecall.tex000066400000000000000000000051571501654372000241270ustar00rootroot00000000000000\section{Direkte Anrufe} \label{sec:privatecall} Direktrufe (\aref{Private Call}) ermöglichen es mit einem anderen Teilnehmer direkt zu kommunizieren, ohne dabei weitere Teilnehmer zu stören (bis auf das Belegen eines Repeaters). Im Rahmen der DMR Einführung wurde der Direktruf auch über mehrere Repeater hinweg beschrieben. Eben dieser Aspekt von DMR ist meiner Meinung nach besonders interessant. Mit Ausnahme der Sprechgruppen 8 \& 9 (siehe Abs. \ref{sec:lokal}), sind Direkt- und Gruppenrufe in DMR transparent gegenüber den verwendeten Repeatern. Es spielt keine Rolle über welchen Repeater sich Teilnehmer an einem Direktruf beteiligen und somit auch nicht wo sie sich befinden. \begin{figure}[!ht] \centering \input{../fig/repeater_privatecall} \caption{Beispiel eines Direktrufs über Ländergrenzen hinweg.} \label{fig:pc} \end{figure} Das heißt, YLs \& OMs\footnote{Für nicht-Funkamateure: Zwei weitere typische Abkürzungen im Amateurfunk die \emph{young lady} und \emph{old man} bedeuten und alle weiblichen b.z.w. männlichen Funkamateure bezeichnet.}, die sich im Urlaub aufhalten, können wie gewohnt an ihren lokalen Nachmittagsrunden teilnehmen, indem sie am Urlaubsort einen DMR Repeater auswählen und von dort aus einen Gruppenruf zu ihrer Sprechgruppe in der Heimat starten. Damit abonnieren sie ihre Sprechgruppe an ihrem Urlaubsrepeater temporär und dieser verhält sich danach wie ein Repeater der in der Heimat steht. Ebenso können sie Direktrufe vom Urlaubsort an Bekannte absetzen und am Urlaubsort empfangen. Vorausgesetzt, sie haben sich durch kurzes drücken auf die PTT Taste beim Repeater am Urlaubsort angemeldet, damit das DMR Netz weiß, wo der Teilnehmer zu finden ist. Damit müssen die Teilnehmer in der Heimat aber nicht mehr wissen wie und wo sie den Urlauber erreichen können. Sie starten einfach einen Direktruf zum Urlauber und das DMR Netz kümmert sich um alles. In Abbildung \ref{fig:pc} ist eben solch ein Direktruf über Ländergrenzen hinweg dargestellt. DM3MAT ruft via seinem lokalen Repeater (DB0ABC) den Urlauber DL2XYZ per Direktruf an. Da sich dieser bei einem DMR Repeater (I0ABC) an seinem Urlaubsort in Italien angemeldet hat\footnote{Um sich an einem Repeater anzumelden, damit das Netzwerk weiß, dass man über diesen Repeater erreichbar ist, drückt man kurz die PTT Taste auf einem Kanal des Repeaters.}, kann der Direktruf an DL2XYZ vermittelt werden. Um diesen Direktruf durchzuführen, muss DM3MAT nicht wissen über welchen Repeater der Urlauber DL2XYZ erreichbar ist. Diese Eigenschaft des DMR Netzes stellt eine deutliche Vereinfachung gegenüber dem \aref{Echolink} Netzwerk dar. qdmr-0.12.3/doc/dmr-intro/script/script_de_06_textmsg.tex000066400000000000000000000063741501654372000233170ustar00rootroot00000000000000\section{Datendienste} \label{sec:data} Da DMR von sich aus schon eine digitale Betriebsart ist, bei der meist Sprache in digitalisierter Form übertragen wird, ist es natürlich auch möglich reine Datendienste über DMR anzubieten. Zum einen gibt es einen Textnachrichtendienst, der dem SMS-Dienst der Mobiltelefone nachempfunden ist. Zum anderen gibt es auch die Möglichkeit, die eigene Position per DMR an das APRS\footnote{APRS steht für \emph{Automatic Packet Reporting System} und ermöglicht das Übertragen von kleinen Datensätzen über Packet-Radio wie zum Beispiel die Position, Wetter oder Textnachrichten. Mehr dazu erfahren sie in der \href{https://de.wikipedia.org/wiki/Automatic_Packet_Reporting_System}{Wikipedia}.} Netz zu übertragen. \subsection{Textnachrichten (SMS)} \label{sec:textmsg} Mit diesem Dienst können sie kurze Textnachrichten\footnote{Bis zu 144 Zeichen.} direkt an andere Teilnehmer verschicken\footnote{Sie können auch Textnachrichten an ganze Sprechgruppen versenden. Dies ist aber eher unüblich und nicht wünschenswert.}. Im Prinzip funktioniert eine Textnachricht wie ein Direktruf. Ist der andere Teilnehmer erreichbar, wird die Textnachricht übermittelt. Es gibt aber auch \emph{Servicenummern} (gebührenfrei). Wenn sie nun eine Nachricht an eine solche Nummer senden, können Sie bestimmte Informationen abrufen oder versenden. In Deutschland wären das: \begin{enumerate} \item 262993 -- GPS und Wetter \begin{itemize} \item Wenn Sie \texttt{help} senden, erhalten daraufhin eine Auflistung aller Kommandos. \item Wenn Sie \texttt{wx} senden, erhalten Sie das aktuelle Wetter am Standort des Repeaters, den Sie verwenden. \item Wenn Sie \texttt{wx STADTNAME} senden, erhalten Sie das aktuelle Wetter für die angegebene Stadt. \item Wenn Sie \texttt{gps} senden, erhalten Sie die letzte Positionsinformation, die Sie zuletzt an das DMR Netz gesendet hatten. \item Mit \texttt{gps CALL} können Sie auch die letzte Position des angegebenen Teilnehmers abfragen. \item Mit \texttt{rssi} erhalten Sie vom Repeater einen Signalrapport. \end{itemize} \item 262994 -- Repeater Informationen \& Pagernachrichten \begin{itemize} \item Wenn Sie \texttt{rpt} senden, erhalten Sie eine Liste der statisch und dynamisch abonnierten Sprechgruppen des Repeaters. \item Wenn Sie \texttt{CALL NACHRICHT}, wird die angegebene Nachricht an das angegebene Call per Pager (DAPNET) geschickt. \end{itemize} \end{enumerate} \subsection{Positionsübermittlung (APRS via DMR)} \label{sec:aprs} Wie im vorherigen Abschnitt schon erwähnt, ist es möglich seine Position ins DMR Netz zu senden. Diese wird dann üblicherweise direkt an das APRS-Netz weitergereicht und Ihre Position kann dann unter anderem bei \url{https://aprs.fi} abgefragt werden. Dazu ist jedoch ein DMR Funkgerät mit GPS Empfänger nötig. Aber auch diese Geräte sind in der Zwischenzeit nicht mehr teuer. Einfache DMR Handfunkgeräte mit GPS sind ab circa \EUR{120} zu haben. Neben dem SMS Service ist auch die Positionsübermitellung per DMR möglich. Dazu muss das GPS fähige Funkgerät so konfiguriert werden, dass die Positionsdaten auf den geeigneten Kanälen an die Nummer 262999 gesendet werden. Wie dies einzustellen ist, hängt sehr vom Hersteller des Funkgerätes ab. qdmr-0.12.3/doc/dmr-intro/script/script_de_07_talkgroup.tex000066400000000000000000000070271501654372000236310ustar00rootroot00000000000000\section{Talkgroup Betrieb} \label{sec:talkgroup} Ein klassisches und auch schönes Beispiel für den Repeater-transparenten Sprechgruppenbetrieb (Talkgroup) ist das Szenario einer Nachmittagsrunde in einer Sprechgruppe. Zum Beispiel, die Sprechgruppe 2621 \emph{Berlin/Brandenburg} kurz BB. Diese Sprechgruppe ist bei fast allen Repeatern in Berlin und Brandenburg fest Abonniert. Das heißt, diese Runde kann ohne weiteres Zutun in ganz Berlin und Brandenburg empfangen werden (siehe Abb. \ref{fig:tgex1}). \begin{figure}[p] \centering \begin{subfigure}{\linewidth} \centering \input{../fig/talkgroup_ex1a} \caption{Beispiel für eine typische Nachmittagsrunde auf einer Sprechgruppe.} \label{fig:tgex1} \end{subfigure} \begin{subfigure}{\linewidth} \centering \input{../fig/talkgroup_ex1b} \caption{Der OM im Ausland abonniert diese Sprechgruppe an dem lokalen Repeater temporär durch einen Gruppenruf zu dieser Sprechgruppe.} \label{fig:tgex2} \end{subfigure} \begin{subfigure}{\linewidth} \centering \input{../fig/talkgroup_ex1c} \caption{Danach kann auch der OM im Urlaub wie gewohnt an dieser Nachmittagsrunde teilnehmen.} \label{fig:tgex3} \end{subfigure} \end{figure} Für einen OM im Urlaub, gilt das natürlich nicht. Ein italienischer Repeater wird sicher nicht standardmäßig die Sprechgruppe \emph{Berlin/Brandenburg} abonniert haben. Daher wird dieser OM die Sprechgruppe im Ausland auch nicht hören. Da er aber weiß, wann diese Runde beginnt, kann er vorher per Gruppenruf zu dieser Sprechgruppe von seinem Urlaubsrepeater (I0ABC) aus, diese Sprechgruppe temporär abonnieren (Abb. \ref{fig:tgex2}). Nachdem er diese Sprechgruppe beim Urlaubsrepeater abonniert hat, kann er wie gewohnt an der Nachmittagsrunde teilnehmen (Abb. \ref{fig:tgex3}). Für die anderen Teilnehmer dieser Runde ist dann nicht einmal ersichtlich, dass der Urlauber nicht über ein Relais in Berlin oder Brandenburg sondern aus dem Ausland an der Runde teilnimmt. \begin{table}[!ht] \centering \begin{tabular}{|l|c|} \hline Name & Sprechgruppe \\ \hline Global & 91 \\ Europa & 92 \\ Deutschland & 262 \\ Mecklenburg-Vorpommern \& Sachsen-Anhalt & 2620 \\ Berlin \& Brandenburg & 2621 \\ Hamburg \& Schleswig-Holstein & 2622 \\ Niedersachsen \& Bremen & 2623 \\ Nordrhein-Westfalen & 2624 \\ Rheinland-Pfalz \& Saarland & 2625 \\ Hessen & 2626 \\ Baden-Württemberg & 2627 \\ Bayern & 2628 \\ Sachsen \& Thüringen & 2629 \\ \hline \end{tabular} \caption{} \label{tab:talkgroups} \end{table} \subsection{Cluster} Im Gegensatz zur dediziert Regionalen Gruppe TG8, sind gewöhnliche Sprechgruppen von überall aus dem DMR Netz erreichbar. Das heißt, ein OM der sich gerade im Urlaub befindet und an einer Runde in dieser Sprechgruppe teilnehmen möchte, kann dies wie oben beschrieben tun. Würde diese Nachmittagsrunde aber auf der regionalen Sprechgruppe TG8 stattfinden, könnte der OM im Urlaub nicht daran teilnehmen. Er würde an seinem Urlaubsrepeater mit einem Gruppenruf zur Sprechgruppe TG8 nur Funkamateure in seiner Urlaubsregion erreichen aber nicht den regionalen Verbund von Repeatern zu Hause. Aus diesem Grund werden häufig regionale Verbünde von Repeatern mit sogenannten \emph{Clustern} verbunden. Diese \adef{Cluster} stellen dann eine weitere Sprechgruppennummer für den regionalen Verbund zur Verfügung, sodass die Sprechgruppe TG8 einer bestimmten Region auch von außen erreichbar ist. Eine Liste der Regionalcluster und der dazugehörigen Sprechgruppennummer kann unter \url{http://bm262.de/cluster/} abgerufen werden. qdmr-0.12.3/doc/dmr-intro/script/script_de_08_aprs.tex000066400000000000000000000000001501654372000225470ustar00rootroot00000000000000qdmr-0.12.3/doc/dmr-intro/script/script_de_09_roaming.tex000066400000000000000000000044541501654372000232600ustar00rootroot00000000000000\section{Roaming} \label{sec:roaming} Viele Relais in einer Region haben die selben Sprechgruppen abonniert, damit eine repeatertransparente Nutzung dieser Sprechgruppen möglich ist. Es ist also egal welchen Repeater Sie auf Ihrem Funkgerät ausgewählt haben, Sie können immer die gleichen Sprechgruppen verwenden. In der Region Berlin \& Brandenburg wäre dies die Sprechgruppe TG2621. Es wäre also sinnvoll eine Liste zu erstellen, in der alle Repeater eingetragen werden, die eine bestimmte Sprechgruppe abonniert haben. Wenn dann noch das Funkgerät automatisch einen erreichbaren Repeater aus dieser Liste auswählen könnte, dann könnte man mit dem Auto in dieser Region unterwegs sein und wäre immer automatisch mit dieser Sprechgruppe verbunden. Dieses Feature nennt sich \adef{Roaming} und wird von einigen meist etwas teureren Funkgeräten unterstützt (z.B., die AnyTone Geräte). Die günstigsten DMR Funkgeräte chinesischer Produktion unterstützen dieses Feature meist nicht. Um das Roaming nutzen zu können, werden zunächst alle Kanäle mit einer bestimmten Sprechgruppe in einer Liste zusammengefasst. Dies könnte eigentlich automatisch geschehen, aber die Konfigurationssoftware für diese Funkgeräte ist wirklich nicht sehr benutzerfreundlich. Wenn nun die Signalstärke eines bestimmten Repeaters dieser Liste unter einen Schwellwert (meist $-105dBm$) fällt, fängt das Funkgerät an, alle Kanäle der Roamingliste abzuklappern, bis es einen Repeater findet, dessen Signalstärke größer ist als der Schwellwert. Dies geschieht aber nur, wenn das Funkgerät auf \emph{stand-by} ist. Das heißt, wenn weder etwas auf dem aktuellen Kanal empfangen wird noch gesendet wird. Hat es einen stärkeren Repeater in der Liste gefunden, wechselt das Funkgerät automatisch den Kanal des neuen Repeater. Dies muss nicht unbedingt der stärkste Repeater der Liste am aktuellen Standort sein. Lediglich der Schwellwert ist entscheidend. Wird kein genügend starker Repeater gefunden, verbleibt das Funkgerät auf dem aktuellen Kanal. Dieses Roaming kann auch auf \emph{manuelles Roaming} eingestellt werden. Das heißt, das Roaming startet erst, wenn die Signalstärke des aktuellen Repeaters unter den Schwellwert sinkt und die PTT-Taste gedrückt wird oder ein manueller Suchlauf aus dem Menü heraus gestartet wird. qdmr-0.12.3/doc/dmr-intro/script/script_de_10_netze.tex000066400000000000000000000060541501654372000227370ustar00rootroot00000000000000\section{DMR-Netze} \label{sec:netze} Sie kennen nun alle wichtigen Konzepte des DMR-Betriebs und auch einige der technischen Details dazu, wie das Erstellen von Codeplugs. Diese Konzepte gelten jedoch nur uneingeschränkt im sogenannten Brandmeisternetz. Dies ist jenes Netz im Hintergrund, dass ihre Direkt- oder Gruppenrufe vermittelt und Repeater miteinander verbindet. In Deutschland ist dies das dominierende Netz. Auch Weltweit sind die meisten DMR Repeater (c.a., 1500) im Brandmeisternetz miteinander verbunden. Es gibt aber auch andere DMR Netze. Zum Einen DMR-MARC (c.a., 500 Repeater) und zum Anderen DMR+ (c.a. 150 Repeater). Welches Netz wo häufiger verwendet wird, hängt stark vom Land ab. So sind in Frankreich, Spanien, den BeNeLux Staaten, Polen, Tschechien und der Slowakei fasst ausschließlich Brandmeister Repeater im Betrieb. Während in Dänemark DMR+ deutlich mehr Repeater vernetzt. In Großbritannien, den USA und Österreich sind DMR-MARC Repeater nicht selten. All diese Netze unterscheiden sich aber nicht technisch voneinander. Das heißt, die Ihnen zugewiesene DMR-ID ist in allen Netzen gültig und sie können jedes Tier-II DMR Funkgerät in allen Netzen verwenden. Lediglich die Konzepte der einzelnen Netze, vor allem wie Gruppenrufe realisiert werden, ist von Netz zu Netz verschieden. Das heißt, Sie müssen die Kanäle zu einem DMR+ Repeater leicht anders konfigurieren als Kanäle zu einem Brandmeister Repeater. \subsection{Reflektoren} \label{sec:reflector} \index{Reflektor} Im DMR+ Netz spielen sogenannte Reflektoren eine zentrale Rolle. Sie entsprechen in etwa den Sprechgruppen, wie sie im Brandmeisternetz verwendet werden. Der wesentliche Unterschied zu Sprechgruppen im Brandmeister Netz ist, dass diese Reflektoren nicht einfach per Gruppenruf angerufen werden können, sondern zunächst per Direktruf an einem Repeater temporär abonniert werden müssen. Danach verhalten sich alle Repeater, die diesen Reflektor abonniert haben, wie eine Gruppe zusammen geschalteter FM Repeater. Das heißt, ein Gruppenruf zur lokalen Sprechgruppe TG9 wird dann nicht nur lokal ausgesandt, sondern auch über alle Repeater die diesen Reflektor abonniert haben. Dies hat den Vorteil, dass die Konfiguration des Funkgerätes viel einfacher ist: Es müssen lediglich zwei Kanäle für jeden Repeater angelegt werden. Je einen für jeden Zeitschlitz und jeweils mit dem Standardkontakt zur TG9. Um einen Reflektor am aktuellen Repeater zu abonnieren, wird einfach ein Direktruf zu dem Reflektor aus der Kontaktliste heraus gestartet. Dieses Konzept ist auch näher an den \emph{alten} Konzepten aus dem FM Repeaterbetrieb mit Echolink. Jedoch gehen dadurch modernere Fähigkeiten des Netzes wie Roaming verloren. Dieses Konzept hat aber auch den Nachteil, dass die Repeatertransparenz verloren geht. Anstatt einfach einen Gruppenruf zu der gewünschten Sprechgruppe zu starten, muss zunächst der lokale Repeater \emph{konfiguriert} werden. Erst danach erfolgt alle Kommunikation über die lokale Sprechgruppe TG9, auch wenn diese Kommunikation alles andere als lokal ist. qdmr-0.12.3/doc/dmr-intro/script/script_de_11_technik.tex000066400000000000000000000070231501654372000232350ustar00rootroot00000000000000\section{Technischer Hintergrund} \label{sec:technik} Nachdem ich in den vorherigen Abschnitten versucht habe Ihnen die Konzepte von DMR (Repeater-unabhängige Direkt und Gruppenrufe) näherzubringen, geht es in diesem Abschnitt an das Eingemachte. Da heißt, die technischen Details und Besonderheiten von DMR. Im speziellen um die Begriffe \emph{Time Slot} und \emph{Color Code}. \subsection{Zeitschlitze (Time Slots)} \label{sec:timeslot} Wie zu Beginn erwähnt, ist DMR eine digitale Übertragungstechnik, bei der Sprache zunächst digitalisiert, mit einem sogenannten Codec komprimiert und als Datenpakete übertragen werden. Modere Sprachcodecs sind in der Zwischenzeit so effizient geworden, dass es möglich ist auf einem $12.5 kHz$ breiten Kanal, zwei Sprachsignale in guter Qualität gleichzeitig zu übertragen. Dies wird auch bei DMR ausgenutzt. DMR verwendet dazu ein Verfahren das sich \adef{TDMA} nennt. \begin{figure}[!ht] \centering \input{../fig/timeslot} \caption{Graphische Darstellung der \emph{time-division media access} (TDMA) Technik.} \end{figure} Das steht für \emph{time-division media access} und beschreibt, wie zwei Teilnehmer (quasi) gleichzeitig einen physischen Kanal (also eine Frequenz) benutzen können. Dazu wird jedem der beiden ein Zeitschlitz zu geordnet (Zeitschlitz 1 und 2) und beide senden oder empfangen nur in ihrem eigenen Zeitschlitz. Diese Zeitschlitze sind sehr kurz, bei DMR nur $30ms$ lang. Diese kurze Zeit reicht jedoch aus um $60ms$ lange Sprachfetzen komprimiert zu übertragen. DMR erhält dadurch zwei völlig unabhängige Kanäle pro Frequenz. Das Bedeutet auch, dass zwei völlig unabhängige Gespräche über einen Repeater gleichzeitig laufen können. Was oder besser wann nun Zeitschlitz 1 oder 2 dran sind, legt der Repeater fest. Er gibt den Takt vor. Das bedeutet auch, dass Zeitschlitze für den Simplexbetrieb völlig unbedeutend sind. Wenn Sie später einen Simplexkanal für Ihr Funkgerät konfigurieren, ist die Zeitschlitzeinstellung egal. Was auf welchem Zeitschlitz passieren soll, hängt stark von der Konfiguration des einzelnen Repeaters ab. Grundsätzlich gilt aber: \begin{merke} Überregionale Kommunikation sollte auf Zeitschlitz 1 und lokale sowie regionale Kommunikation auf dem Zeitschlitz 2 stattfinden. \end{merke} \subsection{Farbcodes (Color Codes)} \label{sec:colorcode} \index{Color Code} Farbcodes sind ein technisches Hilfsmittel, um Störungen zwischen Repeatern zu vermeiden, die auf der selben Frequenz arbeiten. Dieses Problem tritt vor allem im kommerziellen Einsatz von DMR auf. Einem Unternehmen werden üblicherweise nur wenige Frequenzen zugewiesen, es werden mit unter aber viele Repeater benötigt um ein großes Firmengelände vollständig abdecken zu können (denken Sie an das Flughafenbeispiel). Da bleibt es nicht aus, dass verschiedenen Repeatern die selbe Frequenz zugewiesen werden muss. Wenn sich dann die Reichweiten dieser Repeater überlappen, kann es sein, dass die Aussendungen eines Teilnehmers von zwei Repeatern gleichzeitig aufgenommen werden. Um dies zu verhindern, werden den Repeatern verschiedene sogenannte Farbcodes zugewiesen. Diese kleine zusätzliche Information einer Aussendung erlaubt es einem Repeater oder jedem anderen Teilnehmer zu erkennen, ob eine Aussendung für sie bestimmt ist oder nicht. Nur wenn der Farbcode übereinstimmt, reagiert der Repeater oder das Funkgerät auf diese Aussendung. \begin{merke} Um einen Repeater nutzen zu können muss nicht nur dessen Ein- und Ausgabefrequenz sondern auch dessen Farbcode bekannt sein! \end{merke}qdmr-0.12.3/doc/dmr-intro/script/script_de_12_codeplug.tex000066400000000000000000000612041501654372000234140ustar00rootroot00000000000000\section{Codeplug Programmierung} \label{sec:codeplug} Nachdem Sie sich mit den Konzepten und dem technischen Hintergrund von DMR auseinandergesetzt haben, geht es nun an die Konfiguration Ihres Funkgerätes. Dies geschieht üblicherweise nicht über das Bedienfeld des Funkgerätes, sonder mit Hilfe einer separaten Software, der sogenannten \adef{CPS} oder \emph{codeplug programming software}. Doch bevor Sie loslegen können benötigen Sie wie alle DMR Teilnehmer eine eindeutige Nummer, die DMR ID. \begin{hinweis} Ihre persönliche und eindeutige DMR ID erhalten Sie unter \url{https://register.ham-digital.org/}. Da Sie nachweisen müssen, dass Sie lizenzierter Funkamateur sind, müssen Sie bei der Anmeldung ihre eingescannte \emph{Zulassung zum Amateurfunkdienst} hochladen. \end{hinweis} Ihre DMR ID erhalten Sie in der Regel innerhalb von 24 Stunden per Mail. Sobald Sie eine DMR ID erhalten haben kann es los gehen. Da dieses Script für Einsteiger gedacht ist, ist es wahrscheinlich, dass Sie kein top-shelf Motorola Gerät sondern eher ein günstiges Gerät der einschlägig bekannten chinesischen Hersteller besitzen. \begin{achtung} Falls Sie noch kein DMR fähiges Funkgerät besitzen und mit dem Gedanken spielen eines zu kaufen, achten Sie unbedingt darauf, dass es DMR \textbf{Tier I \& II}\footnote{Wie so häufig ist DMR nicht ein Standard sondern eine ganze Familie von aufeinander aufbauenden Standards. DMR Tier I beschreibt im wesentlichen den DMR Simplexbetrieb und Tier II dann den Repeaterbetrieb mit zwei Zeitschlitzen. Sie benötigen also unbedingt Tier II für den Repeaterbetrieb.} unterstützt. Ignorieren Sie etwaiges Marketing-Bla-Bla der Hersteller und schauen Sie in den technischen Details nach, ob dort DMR \textbf{Tier I \& II} erwähnt wird. Falls nicht oder nicht eindeutig, lassen Sie die Finger von diesem Gerät! Dies gilt vor allem für das Baofeng MD-5R aber nicht für das Baofeng/Radioddity RD-5R\footnote{Manchmal sind es die kleinen Unterschiede die entscheidend sind.}. \end{achtung} Der Hersteller Ihres Gerätes wird auf seiner Webseite die Software die Sie zur Konfiguration benötigen, zum Download bereitstellen. Diese Software wird \emph{CPS} oder \emph{codeplug programming software} genannt. Gegebenenfalls finden Sie dort auch Firmwareupdates für Ihr Gerät. Viele Hersteller bieten für jedes einzelne Modell eine separate CPS an oder gar für jede Variation eines Modells. Achten Sie also genau darauf welche CPS Sie herunterladen. Die Konfiguration dieser Geräte unterscheidet sich von Gerät zu Gerät und mehr noch von Hersteller zu Hersteller. Jedoch sind die wesentlichen Einstellungen für Geräte dieser Klasse sehr ähnlich. Wenn Sie die CPS zum ersten mal starten, werden Sie wahrscheinlich zwei Dinge feststellen. Erstens, das Bedienkonzept dieser Software ist aus dem letzten Jahrtausend (Windows 3.11) und Zweitens, es gibt eine Unmenge an obskuren Optionen deren Funktion nicht ersichtlich ist und die größtenteils nicht Dokumentiert sind. Wenn Sie des Englischen nicht mächtig sind, werden Sie auch eine deutsche Übersetzung des Programms vermissen. Aber keine sorge, die englische Übersetzung ist meist auch so schlecht, dass es keinen Unterschied macht ob sie Englisch lesen können oder nicht. Die Konfiguration Ihres Funkgerätes erfolgt in 5-6 Schritten: \begin{enumerate} \item Allgemeine Einstellungen, \item Kontakte anlegen, \item Empfangsgruppen festlegen, \item alle Kanäle anlegen, \item Kanäle in Zonen einteilen und \item optional Scanlisten anlegen. \end{enumerate} In den folgenden Abschnitten möchte ich die einzelnen Konfigurationsschritte im Detail beschreiben. \subsection{Allgemeine Konfiguration} \label{sec:cp:basic} Die wichtigsten allgemeinen Einstellungen die Sie vornehmen müssen, ist das setzen der DMR ID und ihres Rufzeichens. Diese Optionen finden Sie meist unter der Rubrik (linke Seite) \emph{Radio Settings} oder \emph{General Settings}\footnote{Die exakten Namen der Rubriken und Felder kann sich von Hersteller zu Hersteller unterscheiden. Üblicherweise sind sie aber den hier erwähnten Namen sehr ähnlich.}. Ihre DMR ID tragen Sie dann in das Feld \emph{Radio ID} ein. Es ist durchaus möglich, dass Ihr Funkgerät mehrere DMR IDs unterstützt. Dieses Feature wird aber üblicherweise nicht verwendet. Im Gegenteil: Es stehen nur eine begrenzte Anzahl von DMR IDs sehr vielen Funkamateuren gegenüber. Beantragen sie deshalb niemals eine DMR ID für jedes Funkgerät oder jeden Accesspoint. Eine \textbf{einzige} persönliche DMR Nummer reicht völlig! Ihr Rufzeichen tragen Sie bitte in das Feld \emph{Radio Name} ebenfalls in der Rubrik \emph{Radio Settings} ein. \subsection{Kontakte Anlegen} \label{sec:cp:contact} Nachdem Sie die grundlegenden Einstellungen vorgenommen haben, können Sie Ihre Kontaktliste zusammenstellen. Diese sollte alle Sprechgruppen enthalten die Sie interessieren könnten, ihre persönlichen Kontakte wie OMs aus dem OV und einige Servicenummern wie Echo, die SMS Dienste und den All Call. Eine Beispiel für Deutschland ist in Tabelle \ref{tab:contacts} angegeben. \begin{table}[!ht] \centering \begin{tabular}{|l|c|c||l|c|c|}\hline Name & Typ & Nummer & Name & Typ & Nummer \\ \hline Lokal & Gruppenruf & 9 & Ham/SlHo & Gruppenruf & 2622 \\ Regional & Gruppenruf & 8 & NiSa/Bre & Gruppenruf & 2623 \\ TG99 & Gruppenruf & 99 & NRW & Gruppenruf & 2624 \\ Rundumruf & All Call & 16777215 & RhPf/Saar & Gruppenruf & 2625 \\ Weltweit & Gruppenruf & 91 & Hessen & Gruppenruf & 2626 \\ Europa & Gruppenruf & 92 & BaWü & Gruppenruf & 2627 \\ D-A-CH & Gruppenruf & 920 & Bay & Gruppenruf & 2628 \\ Deutschland & Gruppenruf & 262 & Sa/Th & Gruppenruf & 2629 \\ Österreich & Gruppenruf & 232 & Echo Test & Direktruf & 262997 \\ Schweiz & Gruppenruf & 228 & SMS Serv. & Direktruf & 262993 \\ EMCOM\footnote{Ausschließlich für Notfunk.} EU & Gruppenruf & 9112 & DAPNET & Direktruf & 262994 \\ EMCOM WW & Gruppenruf & 9911 & APRS GW & Direktruf & 262999 \\ MeVo/SaAn & Gruppenruf & 2620 & DM3MAT & Direktruf & 2621370 \\ Ber/Bra & Gruppenruf & 2621 & ... & ... & ... \\ \hline \end{tabular} \caption{Beispielkontakte für Deutschland.} \label{tab:contacts} \end{table} Natürlich gibt es noch viele weitere Sprechgruppen auch zu spezifischen Themen, die nicht unbedingt regional beschränkt sind. Eine recht vollständige Liste finden Sie unter \url{https://www.pistar.uk/dmr_bm_talkgroups.php}. \subsection{Empfangsgruppen Zusammenstellen} \label{sec:cp:grouplist} Im nächsten Schritt stellen Sie sogenannte \adef{Empfangsgruppen} zusammen. Dies sind Listen von Gruppenrufen, die Sie auf bestimmten Kanälen empfangen wollen. Wie schon bei der Einführung in Abschnitt \ref{sec:ursprung} erwähnt, weiß das DMR Netz nicht, für welche Sprechgruppen Sie sich interessieren. Dies kann nur Ihr Funkgerät wissen. Mit den Empfangsgruppen definieren Sie genau das. Sie werden mindestens drei Empfangsgruppen benötigen. Eine für den Simplexbetrieb, eine für die überregionale Kommunikation und je eine für regionale Kommunikation in all jenen Regionen, in denen Sie unterwegs sind. Die Simplex Empfangsgruppe ist eigentlich nicht notwendig, da Simplexrufe eigentlich immer den sog. \aref{All Call} (Rundumruf) verwenden sollten. Häufig wird aber auch die Sprechgruppe TG99, TG9 oder auch TG8 verwendet. Daher ist es ratsam eine Empfangsgruppe mit diesen Gruppenrufen anzulegen. Für die überregionale Kommunikation sollte eine Empfangsgruppe erstellt werden, die die Sprechgruppen für weltweite, innereuropäische und deutschlandweite Kommunikation enthalten. Dieser Gruppe können Sie dann noch die Sprechgruppe \emph{EMCOM EU} für europäischen Notfunk hinzufügen, damit Sie ggf. Notrufe hören und darauf reagieren können. Zuletzt sollte die Sprechgruppen für lokale/regionale Kommunikation angelegt werde. Diese sollte jeweils die Sprechgruppen TG8 und TG9 sowie die Sprechgruppe der jeweiligen Region enthalten. Für mich, der in der Berlin/Brandenburg Region lebt, aber häufig auch in Sachsen unterwegs ist, habe ich insgesamt 4 Empfangsgruppen zusammengestellt (siehe Tab. \ref{tab:grouplist}). \begin{table} \centering \begin{tabular}{|l|l|} \hline Name & Gruppenrufe \\ \hline Simplex & Lokal, Regional, TG99 \\ WW/EU/DL & Weltweit, Europa, D-A-CH, Deutschland, EMCOM EU \\ Ber/Bra & Lokal, Regional, Ber/Bra \\ Sa/Th & Lokal, Regional, Sa/Th \\ \hline \end{tabular} \caption{Ein paar Beispielempfangsgruppen. Die ersten beiden sind recht universell für Deutschland, die letzten Beiden sind für die Regionen Berlin/Brandenburg und Sachsen/Thüringen wichtig.} \label{tab:grouplist} \end{table} \subsection{Kanäle Anlegen} \label{sec:cp:channel} Bevor es los geht, sollte ich erwähnen, dass die meisten DMR Funkgeräte auch analoges FM unterstützen. Das heißt, Sie können mit ihrem DMR Funkgerät auch normalen analogen FM Simplex und Repeaterbetrieb durchführen. In diesem Abschnitt beschreibe ich aber nur die Konfiguration von DMR Kanälen (meist \emph{Digital Channel} genannt), die Konfiguration von sogenannten \emph{analogen} Kanälen wird hier nicht beschrieben. Um einen DMR Kanal anzulegen, müssen Sie im Feld \emph{Channel Type} den Wert \emph{digital} auswählen, für einen FM Kanal dann \emph{analog}. Wenn Sie schon Erfahrung mit dem \emph{klassischen} FM-Relaisbetrieb haben, wird Ihnen das Anlegen der Kanäle recht seltsam vorkommen. Im analogen FM-Relaisbetrieb haben Sie für jeden Repeater und Simplex-Kanal genau einen Kanal im Funkgerät konfiguriert. Für den DMR Betrieb werden Sie für jeden Repeater mindestens zwei (für Zeitschlitz 1 \& 2), meist aber deutlich mehr Kanäle programmieren. Lange Rede kurzer Sinn. Lassen Sie mich das an konkreten Beispielen erläutern. \subsubsection{Simplexkanäle Anlegen} \begin{table}[!ht] \begin{tabular}{|l|p{2.5cm}|p{2.5cm}|c|c|c|c|} \hline Name & RX Freq. (Ausgabe) & TX Freq. (Eingabe) & TS\footnote{Seteht für \emph{Time Slot} also Zeitschlitz.} & CC\footnote{Steht für \emph{Color Code} also Farbcode.} & TX Kontakt & Empf.gr. \\ \hline DMR S0 & $433.4500 MHz$ & $433.4500 MHz$ & 1 & 1 & Rundumruf & Simplex \\ DMR S1 & $433.6125 MHz$ & $433.6125 MHz$ & 1 & 1 & Rundumruf & Simplex \\ ... & ... & ... & ... & ... & ... & ... \\ \hline \end{tabular} \caption{Beispieltabelle für die DMR Simplexkanäle.} \label{tab:ch:simplex} \end{table} In Tabelle \ref{tab:ch:simplex} sind exemplarisch die Einstellungen der ersten 2 DMR Simplexkanäle aufgeführt. Sie sollten diese natürlich auf alle 8 DMR Simplexkanäle erweitern. Die erste Spalte gibt einfach den Namen des Kanals an. Die zweite und dritte Spalte geben die Sende- (TX) und Empfangsfrequenz (RX) des Kanals an. Da es sich hier um Simplexkanäle handelt werden natürlich jeweils die gleichen Frequenzen für RX und TX eingetragen. Im \aref{Simplexbetrieb} gibt es keinen Repeater, der den Takt angeben könnte. Daher ist die Wahl des Zeitschlitzes für Simplexkanäle egal. Üblicherweise wird hier einfach der Zeitschlitz 1 ausgewählt. Der Farbcode (Spalte 5) ist aber nicht egal. Repeater sowie auch Ihr Funkgerät akzeptieren nur dann eine Aussendung, wenn der Farbcode der Aussendung mit der Einstellung für den Kanal übereinstimmt. Bei Simplexkanälen hat man sich daher auf den Farbcode 1 geeinigt. Die sechste Spalte gibt den Standardkontakt für diesen Kanal an. Bei Simplexkanälen sollte hier immer der sogenannte. \aref{Rundumruf} (All Call) eingetragen werden. Das bedeutet, dieser \emph{Kontakt} wird immer angerufen, wenn sie diesen Kanal auf dem Funkgerät eingestellt haben und auf die PTT Taste drücken. Eine Ausnahme bildet das Antworten auf einen Ruf. Wenn Sie zum Beispiel einen Gruppenruf zur Sprechgruppe TG99 auf dem Simplexkanal empfangen und innerhalb der kurzen \aref{Hangtime} darauf antworten, werden sie nicht mit dem voreingestellten Rundumruf antworten, sondern mit dem Gruppenruf zur Sprechgruppe TG99. Dieses Verhalten ist sehr erwünscht, da es Ihnen ermöglicht auf auf Direktrufe an Sie mit einem Direktruf zu Antworten. Die letzte Spalte gibt die Empfangsgruppe des Kanals an. Damit wird festgelegt welche Sprechgruppen auf diesem Kanal empfangen werden sollen. Wie oben schon erwähnt, wäre hier eigentlich keine Eintragung nötig wenn alle Teilnehmer auf den Simplexkanälen den Rundumruf verwenden würden. Es werden aber durchaus sehr unterschiedliche Sprechgruppen auf den Simplexkanälen verwendet. Für diese Fälle hatten wir ja die Empfangsgruppe \emph{Simplex} zusammengestellt. In Ihrer CPS finden sie noch sehr viel mehr Optionen zu den Kanälen. Die Meisten können auf den Standardwerten belassen werden. Am Ende dieses Abschnittes beschreibe ich noch eine Reihe weiterer Optionen. Viele dieser Optionen betreffen Funktionen, die im Amateurfunk aber keine Verwendung finden. Die Option \adef{Admit Criterion} definiert unter welchen Umständen das senden auf dem Kanal vom Funkgerät erlaubt wird. Hier stellen Sie bitte \emph{Channel Free} ein. Dies bedeutet, dass sie nur senden dürfen, wenn der Simplexkanal frei ist. \subsubsection{Repeaterkanäle Anlegen} Das Anlegen von Repeater Kanälen ist etwas aufwendiger als das Anlegen von Simplexkanälen, da für jeden Repeater gleich mehrere Kanäle definiert werden. Bevor Sie anfangen können Repeaterkanäle anzulegen, müssen Sie natürlich erst herausfinden welche Repeater sich in Ihrer Nähe befinden. Eine gute Übersicht bietet Ihnen die Seite \url{https://repeatermap.de/}. Sie können dort unter Filter die Anzeige auf DMR Repeater beschränken. Dort finden Sie auch alle wichtigen Information zu den jeweiligen Repeatern. Das heißt deren Eingabe- und Ausgabefrequenzen und Farbcodes. Diese Informationen benötigen Sie unbedingt um Kanäle für diesen Repeater anlegen zu können. \begin{sidewaystable}[p] \centering \begin{tabular}{|l|c|c|c|c|c|c|} \hline Name & RX Freq. (Ausgabe) & TX Freq. (Eingabe) & TS\footnote{Seteht für \emph{Time Slot} also Zeitschlitz.} & CC\footnote{Steht für \emph{Color Code} also Farbcode.} & TX Kontakt & Empf.gr. \\ \hline DB0LDS TS1 & $439.5625 MHz$ & $431.9625 MHz$ & 1 & 1 & --- & WW/EU/DL \\ DB0LDS DL TS1 & $439.5625 MHz$ & $431.9625 MHz$ & 1 & 1 & Deutschland & WW/EU/DL \\ DB0LDS Sa/Th TS1 & $439.5625 MHz$ & $431.9625 MHz$ & 1 & 1 & Sa/Th & Sa/Th \\ DB0LDS TG9 TS2 & $439.5625 MHz$ & $431.9625 MHz$ & 2 & 1 & L9 & Ber/Bra \\ DB0LDS TG8 TS2 & $439.5625 MHz$ & $431.9625 MHz$ & 2 & 1 & L8 & Ber/Bra \\ DB0LDS BB TS2 & $439.5625 MHz$ & $431.9625 MHz$ & 2 & 1 & Ber/Bra & Ber/Bra \\ \hline \end{tabular} \caption{Beispielkonfiguration der Kanäle für den Repeater DB0LDS in Wildau bei Berlin.} \label{tab:ch:repeater} \end{sidewaystable} Ich denke, es ist am einfachsten Ihnen am Beispiel meiner eigenen Kanalliste (Tab. \ref{tab:ch:repeater}) für \textbf{einen} Repeater in meiner Nähe das Anlegen von Repeaterkanälen zu beschreiben. Der Repeater heißt DB0LDS und hat die Eingabe Frequenz $431.9625 MHz$ und die Ausgabe Frequenz $439.5625 MHz$. Des Weiteren erwartet er den Farbcode 1. Dies sind die elementaren Informationen zu diesem Repeater, die Sie von diversen Repeaterlisten und Karten erhalten. Diese Informationen müssen sie natürlich für alle Kanäle die diesen Repeater betreffen, eintragen. Am Ende des Abschnitts \ref{sec:timeslot} hatte ich erwähnt, dass überregionaler Funkverkehr auf Zeitschlitz 1 und Regionaler auf Zeitschlitz 2 stattfinden. Dies wurde in dieser Konfiguration umgesetzt. Der Erste Kanal \emph{DL0LDS TS1} ist ein generischer Kanal für den Zeitschlitz eins. Er besitzt keinen Standardkontakt aber eine Empfangsgruppe für die Sprechgruppen Welt, Europa und Deutschland. Dieser Kanal dient dazu, beliebige (überregionale) Gruppen- und Direktrufe aus der Kontaktliste heraus zu führen. Das heißt, um auf diesem Kanal ein QSO zu starten, kann nicht einfach die PTT Taste gedrückt werden. Denn dazu fehlt dem Kanal der Standardkontakt. Es muss erst ein Kontakt aus der Kontaktliste ausgewählt werden, der angerufen werden soll. Der zweite Kanal (\emph{DL0LDS DL TS1}) ist identisch zum Ersten bis auf den Standardkontakt\index{Kanal!Standardkontakt}. Hier ist die Sprechgruppe \emph{Deutschland} (TG262) eingetragen. Das bedeutet, wenn dieser Kanal im Funkgerät ausgewählt ist und die PTT Taste gedrückt wird, wird direkt ein Gruppenruf an diese Sprechgruppe gestartet. Einen extra Kanal für diese Sprechgruppe anzulegen, erlaubt es diesen Gruppenruf zu starten ohne ihn erst in der Kontaktliste auswählen zu müssen. Auch ist es so möglich, diese Sprechgruppe schnell temporär auf diesem Repeater zu abonnieren\footnote{Die Sprechgruppe TG262 (Deutschland) ist für diesen Repeater nicht permanent auf Zeitschlitz 1 abonniert.} indem kurz die PTT Taste gedrückt wird (siehe Abschnitt \ref{sec:talkgroup}). \begin{merke} Auf jedem Kanal kann auf einen eingehenden Ruf innerhalb der sogenannten \aref{Hangtime} geantwortet werden, egal welcher Standardkontakt für diesen Kanal festgelegt wurde. \end{merke} Ähnlich verhält es sich mit dem dritten Kanal (\emph{DB0LDS Sa/Th TS1}). Hier ist als Standardkontakt die Sprechgruppe \emph{Sachsen/Thüringen} (TG2629) eingestellt um diese schnell und leicht über diesen Repeater erreichen und temporär abonnieren zu können. Bitte beachten Sie, das für diesen Kanal der Zeitschlitz 1 verwendet wird. Der Repeater befindet sich in Brandenburg. Somit sollte die Kommunikation mit Sachsen oder Thüringen im Zeitschlitz für überregionale QSOs geführt werden. Des weiteren ist als Empfangsgruppe die regionale Empfangsgruppe für Sachsen \& Thüringen angegeben. Das bedeutet, dass Gruppenrufe an die überregionalen Sprechgruppen wie \emph{Deutschland} (TG262) auf diesem Kanal nicht empfangen werden, auch wenn er auf Zeitschlitz TS1 liegt. Kanäle vier, fünf und sechs sind für die lokale (TG9, nur auf diesem Repeater), regionale (TG8, im regionalen Repeaterverbund) und Berlin-Brandenburg-weite Kommunikation (TG2621). All diese Kanäle sind auf Zeitschlitz 2, da es sich um regionale Kommunikation handelt und haben als Empfangsgruppe \emph{Ber/Bra} gesetzt. Das heißt, auf diesen Kanälen werden die Sprechgruppen TG8, TG9 und TG2621 empfangen. Als Standardkontakt wurde die entsprechenden Sprechgruppen gesetzt. Wird auf dem Funkgerät nun der Kanal \emph{DB0LDS TG9 TS2} ausgewählt, so wird beim drücken der PTT Taste ein Gruppenruf an die lokale Sprechgruppe (TG9) von nur diesem Repeater ausgesandt. Wird jedoch der Kanal \emph{DB0LDS BB TS2} ausgewählt, wird beim Drücken der PTT Taste ein Gruppenruf an die Sprachgruppe \emph{Berlin/Brandenburg} (TG2621) gestartet und somit fast überall in Berlin und Brandenburg gehört. \begin{merke} Auf jedem Kanal kann ein beliebiger Ruf (Gruppen, Direkt, Rundum) gestartet werden indem entweder der entsprechende Kontakt in der Kontaktliste ausgewählt wird oder die DMR Nummer eingegeben wird. Dies ist unabhängig vom Standardkontakt des Kanals. Letztendlich dient der Standardkontakt eines Kanals der Bequemlichkeit. So können dedizierte Kanäle für häufig getätigte Rufe definiert werden. \end{merke} Das sogenannte \emph{Admit Criterion} sollte für alle Repeaterkanäle auf \emph{Color Code} gesetzt werden. Dies bedeutet, dass Ihr Funkgerät nur dann sendet, wenn der Kanal frei ist und der Farbcode des Repeaters mit dem Farbcode des Kanals übereinstimmt. \subsubsection{Weitere Kanaloptionen} Die Maske, mit der Sie Kanäle konfigurieren ist recht umfangreich. Es gibt eine Vielzahl an Optionen die das Verhalten dieses Kanals beeinflussen. Die meisten dieser Optionen werden im Amateurfunk aber nicht verwendet. Dennoch möchte ich diese hier kurz erklären. Die \aref{Admit Criterion} Option hatte ich zuvor schon erwähnt. Sie liegt fest, unter welchen Umständen das Funkgerät ihnen erlaubt auf dem Kanal zu senden. Meist stehen hier drei Möglichkeiten zu Verfügung. \emph{Always} bedeutet, dass Sie immer senden dürfen. \emph{Channel Free} bedeutet, dass der Kanal frei sein muss, damit Sie senden dürfen. Und \emph{Color Code} bedeutet, dass nicht nur der Kanal frei sein muss, sonder auch der Farbcode des Repeaters stimmen muss. Daher macht es Sinn \emph{Channel Free} für Simplexkanäle und \emph{Color Code} für Repeaterkanäle zu wählen. Die Option \adef{TOT} oder auch \adef{TX Timeout} legt die maximale Dauer einer Aussendung fest. Das heißt, wenn Sie die PTT Taste länger als diese Zeitspanne drücken, wird das Funkgerät Ihre Aussendung unterbrechen. Dies ist eine Funktion für den kommerziellen Einsatz, die verhindert, dass eine Fehlbedienung das DMR Netz oder auch einen Repeater blockiert. Im Amateurfunk macht dies wenig Sinn. Daher können Sie diese Option auf \emph{unendlich} stellen. Die Option \adef{Emergency System} legt das Alarm- oder Notrufsystem für diesen Kanal fest. Auch dies ist eine Funktion für den kommerziellen Einsatz und wird im Amateurfunk nicht verwendet. Die Option \adef{Privacy Group} legt die Verschlüsselung der Aussendungen für diesen Kanal fest. Diese Funktion darf im Amateurfunk gar nicht verwendet werden. Die Optionen \adef{Emergency Alarm Confirmed}, \adef{Private Call Confirmed} und \adef{Data Call Confirmed}, legen fest, wie das Funkgerät Alarme, Direktrufe und Datenübermittlung durchführt. Sind diese Optionen angewählt, versucht das Funkgerät erst eine Verbindung zum Ziel herzustellen, bevor Sie sprechen dürfen. Das heißt, das Funkgerät sendet zunächst eine Anfrage an das Ziel. Erst wenn diese Anfrage vom Ziel positiv beantwortet wurde, ertönt ein Ton und Sie können sprechen. Wenn diese Optionen nicht angewählt sind, fangen Sie sofort an Sprachdaten an das Ziel zu senden. Ich empfehle Ihnen diese Option nicht anzuwählen. Die Option \adef{Talkaround} erlaubt es Ihnen auf einem Repeaterkanal Simplexbetrieb zu fahren. Das heißt, Sie werden auf der Repeaterausgabefrequenz senden und empfangen. Dabei umgehen sie natürlich den Repeater selbst. Daher wird dieses Feature im Amateurfunk nicht verwendet. Wenn die Option \adef{RX Only} angewählt ist, können Sie auf diesen Kanal nicht senden. Die Option \adef{VOX} bedeutet \altdef{Voice Operated Switch}{VOX} und erlaubt es Ihnen automatisch von Empfang auf Senden umzuschalten sobald sie in das Mikrofon sprechen. Dies lässt sich bei einigen Funkgeräten pro Kanal oder aber auch in den allgemeinen Einstellungen aktivieren. Die Option \adef{Power} legt die Sendeleistung auf diesen Kanal fest. Wenn Sie sich direkter Nähe des Repeaters befinden, können Sie die Sendeleistung reduzieren um die Batterielaufzeit bei Handfunkgeräten zu erhöhen. Diese optionale Kanaleinstellung \aref{Scanlist} definiert welche Liste von Kanälen gescannt werden, wenn der Scan auf diesem Kanal gestartet wird. Es ist nicht zwingend notwendig, dass der Kanal selbst in dieser Scanliste enthalten ist. \subsection{Zonen Zusammenstellen} \label{sec:zone} \index{Zone} Wenn Sie nun alle für Sie interessanten Kanäle erstellt haben, werden Sie feststellen, dass die Liste doch schon recht lang und unübersichtlich ist. Alle DMR Funkgeräte organisieren daher die Kanäle in sogenannten Zonen. Diese Zonen sind einfach nur Listen von Kanälen. Wie Sie diese Listen zusammenstellen, ist allein Ihnen überlassen. Sie können die Kanäle nach Region zusammenfassen wie \emph{Zu Hause}, \emph{Arbeit}, \emph{Urlaub} etc.. Sie können sie auch nach Sprechgruppen sortieren. So können Sie ein quasi händisches Roaming für eine bestimmte Sprechgruppe realisieren. Wenn Sie dann im Auto unterwegs sind, können Sie im Funkgerät immer jenen Repeater aus der Zone auswählen, den Sie gerade erreichen können. Somit bleiben Sie immer mit dieser Sprechgruppe verbunden. Einige (eher teurere) Funkgeräte unterstützen dies mit einer automatischen Roamingfunktion, bei der der jeweils stärkste Repeater an Ihrem Standort ausgewählt wird. \begin{hinweis} Kanäle, die keiner Zone zugeordnet wurden, können nicht im Funkgerät ausgewählt werden. Es ist aber problemlos möglich, einen Kanal mehreren Zonen zuzuordnen. \end{hinweis} \subsection{Scanlisten Zusammenstellen} Scanlisten sind einfach Listen von Kanälen, die beim Starten der Scanfunktion sequenziell beobachtet werden. Wird ein Signal auf einem der Kanäle empfangen, wird der Scan unterbrochen und es kann dann auf diesen empfangenen Ruf geantwortet werden. Diese Funktion erlaubt es mehrere Kanäle zu beobachten. Zusätzlich können Sie bei vielen Geräten ein oder zwei Prioritätskanäle definieren werden, die während des Scans häufiger \emph{besucht} und somit \emph{intensiver} beobachtet werden. qdmr-0.12.3/doc/dmr-intro/talk-barcamp2021/000077500000000000000000000000001501654372000200665ustar00rootroot00000000000000qdmr-0.12.3/doc/dmr-intro/talk-barcamp2021/slides.tex000066400000000000000000000274431501654372000221050ustar00rootroot00000000000000\documentclass[aspectratio=169]{beamer} \usetheme{Boadilla} \usepackage{hyperref} \usepackage{graphicx} \usepackage{subcaption} \usepackage{standalone} \usepackage[ngerman]{babel} \usepackage{tikz} \usepackage{listings} \title[QDMR]{QDMR ein universeller Code-Plug Editor (CPS) für Linux \& Mac\\ (und vielleicht Windows)} \subtitle{} \author{Hannes, DM3MAT} \institute{\texttt{dm3mat [at] darc [dot] de}} \date{25. März 2021} \lstset{ % basicstyle=\tiny % the size of the fonts that are used for the line-numbers } \input{../fig/repeater} \begin{document} \begin{frame} \titlepage \end{frame} \begin{frame} \frametitle{Übersicht} \tableofcontents \end{frame} \section{Ganz kurze Einführung in DMR} \begin{frame} \frametitle{Einführung -- DMR ein Mobilfunknetz für den Amateurfunk} \begin{block}{Was ist DMR?} \begin{itemize} \item Ein digitaler Daten und Sprachübertragungsmodi für UKW. \item Gedacht als Ersatz für den analogen Bündelfunk. \item Endpunkte werde über \emph{Telefonnummern} identifiziert. \item Verwendet 12.5kHz Raster (wie NBFM). \item Erlaubt vernetzten Repeater betrieb (über sog. Sprechgruppen). \item Erlaubt durch Kompression das \emph{Bedienen} zweier Endgeräte auf einem physischen Kanal (zwei unabhängige \emph{Zeitschlitze}). \end{itemize} \end{block} \end{frame} \begin{frame} \frametitle{Repeaterbetrieb und Direktrufe} \begin{block}{Merke} Alle DMR Repeater sind untereinander vernetzt und vermitteln Gespräche. \end{block} \begin{center} \input{../fig/repeater_privatecall} \end{center} \end{frame} \begin{frame} \frametitle{Repeaterbetrieb und Sprechgruppen} \begin{block}{Sprechgruppe} Sprechgruppen sind reservierte Telefonnummern für Telefonkonferenzen: Ein \emph{Gruppenruf} zu einer Sprechgruppe wird von allen Repeatern ausgesandt, die diese Sprechgruppe abonniert haben. Es gibt Sprechgruppen für Regionen aber auch für bestimmte Themen. \end{block} \begin{center} \begin{tabular}{|l|c|} \hline Name & Sprechgruppe \\ \hline Lokal & 9 \\ Global & 91 \\ Europa & 92 \\ Deutschland & 262 \\ Berlin \& Brandenburg & 2621 \\ ... & ... \\ Whitesticker & 264022\\ \hline \end{tabular} \end{center} \end{frame} \begin{frame}\frametitle{Lokale und regionale Verbindungen} \begin{center} \input{../fig/repeater_local} \end{center} \end{frame} \begin{frame}\frametitle{Nachmittagsrundenbeispiel} \begin{center} \input{../fig/talkgroup_ex1a} \end{center} \end{frame} \begin{frame}\frametitle{Nachmittagsrundenbeispiel} \begin{center} \input{../fig/talkgroup_ex1b} \end{center} \end{frame} \begin{frame}\frametitle{Nachmittagsrundenbeispiel} \begin{center} \input{../fig/talkgroup_ex1c} \end{center} \end{frame} \section{Code Plugs und CPS} \begin{frame}\frametitle{Kanäle einrichten: analog} Kanäle für analog Repeater einzurichten ist leicht: Ein- und Ausgabe Frequenz und ggf. Subton. \begin{center} \begin{tabular}{l|l|l|l} Name & Ausgabe & Eingabe & Subton \\ \hline DB0LDS & $439.5625$ & $431.9625$ & $67.0Hz$ \\ \end{tabular} \end{center} \end{frame} \begin{frame}\frametitle{Kanäle einrichten: DMR} Aus Bequemlichkeit richtet man einfach für alle Sprechgruppen, an denen man teilnehmen möchte einen separaten Kanal ein. Und das für jeden Repeater, den man verwendet. \vspace{0.5cm} \begin{center} \begin{tabular}{l|l|l|l|l|l|l} Name & Ausgabe & Eingabe & CC & TS & Kontakt & Empfangsgruppe \\ \hline DL DB0LDS & $439.5625$ & $431.9625$ & 1 & 1 & 262 & 91, 92, 262 \\ Sa/Th DB0LDS & $439.5625$ & $431.9625$ & 1 & 1 & 2629 & 2629 \\ WS DB0LDS & $439.5625$ & $431.9625$ & 1 & 1 & 264022 & 264022 \\ L9 DB0LDS & $439.5625$ & $431.9625$ & 1 & 2 & L9 & 8, 9, 2621 \\ BB DB0LDS & $439.5625$ & $431.9625$ & 1 & 2 & 2621 & 8, 9, 2621 \\ \end{tabular} \end{center} \vspace{.5cm} Hier habe ich 5 logische Kanäle für einen einzigen physischen Kanal eingerichtet. Wirklich notwendig sind nur zwei, einen für Timeslot 1 und einen für Timeslot 2. \end{frame} \begin{frame} \frametitle{Code-plug programming software (CPS)} Weil eine Menge Kanäle, Sprechgruppen und Empfangsgruppen eingerichtet werden müssen, wird eine Software verwendet anstatt das Gerät über das Bedienfeld zu programmieren. Die sog. \emph{CPS}. Das Ergebnis wird \emph{code plug} genannt. \vspace{0.5cm} Probleme: \begin{itemize} \item Die Software ist Mist. Bedienkonzept aus den 90er Jahren. \item Sehr auf kommerziellen Einsatz ausgelegt. \item Sehr viele unnütze/verbotene Optionen (Verschlüsselung) \item Schlechte Übersetzung aus dem Chinesischen. \item Hardware- oder gar versionsspezifische, binäre code plugs \item Jeder muss quasi seinen eigenen code plug bauen. Es gibt keinen Austausch. \end{itemize} \end{frame} \section{QDMR -- Ein Versuch einer universellen CPS} \begin{frame} \frametitle{QDMR -- Ein Versuch einer universellen CPS} \begin{center} \includegraphics[width=0.75\linewidth]{../fig/qdmr-general-settings.png} \end{center} QDMR ist ein Versuch eine \emph{universelle} CPS zu bauen für verschiedene Geräte und Hersteller mit einem lesbaren code-plug und einer halbwegs modernen Oberfläche. \end{frame} \begin{frame} Unterstütze Geräte (bisher): \begin{itemize} \item Open GD-77 Firmware (RD-5R, GD-77, ...) \item Radioddity RD-5R \item Retevis RT3S \item TyT MD-UV390 \item AnyTone AT-D878UV \end{itemize} \pause Quasi fertig: \begin{itemize} \item Radioddity GD-77 \item TYT MD-UV380, MD-2017, MD-9600 \item Retevis RT84 \item Anytone AT-D868UVE \item BTECH DMR-6X2 (läuft wahrscheinlich schon, identisch zum AT-D878UV) \end{itemize} \pause Nahe Zukunft: \begin{itemize} \item Hytera Geräte (vielen Dank an Matt)! \end{itemize} \end{frame} \begin{frame} \begin{center} {\Huge Demo!} \end{center} \end{frame} \begin{frame} \frametitle{Grundeinstellungen (1/5)} \begin{center} \includegraphics[width=0.75\linewidth]{../fig/qdmr-general-settings.png} \end{center} \end{frame} \begin{frame} \frametitle{Kontakte/Sprechgruppen (2/5)} \begin{center} \includegraphics[width=0.75\linewidth]{../fig/qdmr-contacts-empty.png} \end{center} \end{frame} \begin{frame} \frametitle{Kontakte/Sprechgruppen: Direktruf} \begin{center} \includegraphics[width=0.25\linewidth]{../fig/qdmr-edit-contact-private-call.png} \end{center} \end{frame} \begin{frame} \frametitle{Kontakte/Sprechgruppen: Gruppenruf} \begin{center} \includegraphics[width=0.25\linewidth]{../fig/qdmr-edit-contact-group-call.png} \end{center} \end{frame} \begin{frame} \frametitle{Kontakte/Sprechgruppen (2/5)} \begin{center} \includegraphics[width=0.75\linewidth]{../fig/qdmr-contacts-example.png} \end{center} \end{frame} \begin{frame} \frametitle{Empfangsgruppen (3/5)} \begin{center} \includegraphics[width=0.75\linewidth]{../fig/qdmr-rxgroup-empty.png} \end{center} \end{frame} \begin{frame} \frametitle{Empfangsgruppen: Hinzufügen} \begin{center} \includegraphics[width=0.25\linewidth]{../fig/qdmr-edit-rxgroup-empty.png} \end{center} \end{frame} \begin{frame} \frametitle{Empfangsgruppen: Hinzufügen} \begin{center} \includegraphics[width=0.25\linewidth]{../fig/qdmr-edit-rxgroup-select.png} \end{center} \end{frame} \begin{frame} \frametitle{Empfangsgruppen (3/5)} \begin{center} \includegraphics[width=0.75\linewidth]{../fig/qdmr-rxgroup-list.png} \end{center} \end{frame} \begin{frame} \frametitle{Kanäle (4/5)} \begin{center} \includegraphics[width=0.75\linewidth]{../fig/qdmr-channels-empty.png} \end{center} \end{frame} \begin{frame} \frametitle{Kanäle: Analog} \begin{center} \includegraphics[width=0.5\linewidth]{../fig/qdmr-channels-edit-analog.png} \end{center} \end{frame} \begin{frame} \frametitle{Kanäle: Digital} \begin{center} \includegraphics[width=0.5\linewidth]{../fig/qdmr-channels-edit-digital.png} \end{center} \end{frame} \begin{frame} \frametitle{Kanäle (4/5)} \begin{center} \includegraphics[width=0.75\linewidth]{../fig/qdmr-channels-example.png} \end{center} \end{frame} \begin{frame} \frametitle{Zonen (5/5)} \begin{center} \includegraphics[width=0.75\linewidth]{../fig/qdmr-zone-empty.png} \end{center} \end{frame} \begin{frame} \frametitle{Zone hinzufügen} \begin{center} \includegraphics[width=0.5\linewidth]{../fig/qdmr-zone-edit.png} \end{center} \end{frame} \begin{frame} \frametitle{Kanäle zur Zone hinzufügen} \begin{center} \includegraphics[width=0.25\linewidth]{../fig/qdmr-zone-edit-select.png} \end{center} \end{frame} \begin{frame} \frametitle{Zonen (5/5)} \begin{center} \includegraphics[width=0.75\linewidth]{../fig/qdmr-zone-empty.png} \end{center} \end{frame} \begin{frame} \frametitle{Code-plug Format} \begin{center} \includegraphics[width=0.9\linewidth]{../fig/qdmr-codeplug-format.png} \end{center} \end{frame} \section{Down the rabbit hole} \begin{frame} \frametitle{Howto reverse engineer a code-plug?} Hilfsmittel: \begin{itemize} \item Eine Windows Instanz in einer virtuellen Maschine (VirtualBox, o.ä.) mit der original CPS \item Wireshark mit \emph{usbmon} Kernelmodul (bei allen Linux Distributionen dabei) \item Viel Geduld \item Viel Frustrationstoleranz \end{itemize} Vorgehen: \begin{itemize} \item Die Kommunikation zwischen CPS und Gerät belauschen und mitschneiden. \item Die Rohdaten aus den Mitschnitten extrahieren und Gemeinsamkeiten/Unterschiede darin finden. \item Daraus das Kommunikationsprotokoll sowie das Paketformat ableiten (sehr aufwendig). \item Danach differentielle Analyse des Code-plugs. D.h., \begin{itemize} \item \textbf{Eine} kleine Änderung im code-plug auf das Gerät spielen. \item Kommunikation mitschneiden \item Code-plug daraus extrahieren. \item Vergleichen vor und nach der Änderung. \end{itemize} \end{itemize} \end{frame} \begin{frame}[fragile]\frametitle{Howto reverse engineer a code-plug?} Beispiel Hytera (Rohdaten): \begin{verbatim} 7e 00 00 fe 20 10 00 00 00 0c 60 e5 7e 01 00 00 20 10 00 00 00 14 31 d3 02 03 02 01 00 00 2c 03 7e 01 00 00 20 10 00 00 00 24 02 f1 02 c5 01 11 00 [...] 00 5b 03 7e 01 00 00 20 10 00 00 00 14 41 c3 02 01 02 01 00 12 1c 03 \end{verbatim} \end{frame} \begin{frame}[fragile]\frametitle{Howto reverse engineer a code-plug?} Beispiel Hytera (Code-plug extrahieren): \begin{lstlisting} < RES: flags=00, src=10, dest=20, seq=0004 | RSP: type=81C7 seq=EA crc=170F (1887) | RD: addr=00000000 | | 00000000 16 01 52 43 44 42 01 db 07 02 18 50 44 37 38 30 | ..RCDB.....PD780 | | 00000010 30 30 47 30 30 4d 30 30 30 30 30 00 00 85 85 00 | 00G00M00000..... | | 00000020 00 00 00 00 00 00 56 6a 19 00 9a 00 00 00 00 00 | ......Vj........ ... \end{lstlisting} \end{frame} \begin{frame}[fragile]\frametitle{Howto reverse engineer a code-plug?} Beispiel Hytera (Code-plug vergleichen, einfach mit \emph{diff}): Hier wurde gar nichts geändert, dennoch gibt es einen unterschied im übertragenen Code-plug \begin{lstlisting} < 00000090 80 a1 03 1c e5 07 03 16 02 17 db 07 03 0a 0a 21 | ...............! --- > 00000090 80 a1 03 1c e5 07 03 16 02 1a db 07 03 0a 0a 21 | ...............! \end{lstlisting} \begin{itemize} \item \texttt{0x07e3} = 2021, \texttt{0x03} = 3, \texttt{0x16} = 22, \texttt{0x02} = 2, \texttt{0x17}=23 \item \texttt{0x07e3} = 2021, \texttt{0x03} = 3, \texttt{0x16} = 22, \texttt{0x02} = 2, \texttt{0x1a}=26 \item $\Rightarrow$ Unterschied ist Zeitstempel der letzten Programmierung. \end{itemize} \end{frame} \begin{frame}[fragile]\frametitle{Howto reverse engineer a code-plug?} Beispiel Hytera (Code-plug vergleichen, einfach mit \emph{diff}): Hier wurde die DMR ID von 1 auf 12345678 geändert: \begin{lstlisting} < 00065AA0 04 00 4c 00 00 00 62 00 00 00 01 00 00 00 10 06 | ..L...b......... --- > 00065AA0 04 00 4c 00 00 00 62 00 00 00 4e 61 bc 00 10 06 | ..L...b...Na.... \end{lstlisting} \begin{itemize} \item \texttt{0x01} = 1, \texttt{0xbc614e} = 12345678 \item $\Rightarrow$ Die DMR ID wird an der Adresse \texttt{0x00065AAA} als 24bit little-endian Zahl abgelegt. \end{itemize} \end{frame} \end{document}qdmr-0.12.3/doc/dmr-intro/talk-ov2023/000077500000000000000000000000001501654372000171075ustar00rootroot00000000000000qdmr-0.12.3/doc/dmr-intro/talk-ov2023/slides.tex000066400000000000000000000274561501654372000211320ustar00rootroot00000000000000\documentclass[aspectratio=169]{beamer} \usetheme{Boadilla} \usepackage{hyperref} \usepackage{graphicx} \usepackage{subcaption} \usepackage{standalone} \usepackage[ngerman]{babel} \usepackage{tikz} \usepackage{pgfplots} \usetikzlibrary{shapes.geometric} \usepackage{listings} \usepackage[utf8]{inputenc} \usepackage{tikzsymbols} \usepackage{standalone} \title[DMR]{Kurze Einführung in Digital Mobile Radio (DMR)} \subtitle{Ein Mobilfunknetz für den Amateurfunk} \author{Hannes, DM3MAT} \institute{\texttt{dm3mat [at] darc [dot] de}} \date{5. April 2023} \lstset{ % basicstyle=\tiny % the size of the fonts that are used for the line-numbers } \input{../fig/repeater} \begin{document} \begin{frame} \titlepage \end{frame} \begin{frame} \frametitle{Übersicht} \tableofcontents \end{frame} \section{Motivation} \begin{frame}{Motivation} Schnatterfunk: \begin{itemize} \item Ziel: Ich möchte mit bestimmten Leuten oder auch mit Irgendjemanden reden. \pause\item Auf UKW: Reichweite begrenzt. Gerade mit Handfunke. \pause\item Lösung: Repeater. \pause\item Nächstes Problem: Reichweite immer noch begrenzt. \pause\item Lösung: Repeater vernetzen! (Echolink) \pause\item Immer noch Probleme: \begin{itemize} \pause\item Wo sitzen die Leute, mit denen ich reden Will? \pause\item Welche Repeater sind dort in der Nähe? \pause\item Welche EL-Nummer haben die? \end{itemize} \pause\item Eigentliches Problem: Was interessieren mich Repeater? Mich interessieren die Leute! \end{itemize} \end{frame} \begin{frame}{Repeatertransparenz} Eigentlich sollten FM-Relais 4 verschiedene Anwendungsfälle abdecken: \begin{enumerate} \pause\item Direktes QSO mit einer bestimmten Person, egal wo diese sitzt. \pause\Sadey \pause\item Teilnahme an themenspezifischer Runde, egal wo die Teilnehmer sitzen. \pause\Neutrey \pause\item Teilnahme an regionaler Runde. \pause\Neutrey \pause\item QSO zum nächsten Dorf. \pause\Smiley \end{enumerate} \end{frame} \begin{frame}{Repeatertransparenz} Es wäre also schön, wenn der einzelne Repeater nicht mehr so im Zentrum stehen würde. \pause Wir vernetzen also die Repeater und packen wir was anderes in die Mitte: \begin{block}{Sprechgruppe/Talkgroup} Eine Sprechgruppe/Talkgroup ist ein virtueller Raum/Repeater. Er existiert nicht physisch durch einen Zusammenschluss bestimmter Repeater, sondern im Netz aller Repeater. Habe ich eine Sprechgruppe (TG) abonniert, höre ich alles, was in dieser TG gesagt wird. Sende ich dort hin, hören alle Teilnehmer meine Aussendung, egal über welchen Repeater. Sprechgruppen sind also repeatertransparent. \end{block} \end{frame} \section{Ursprung} \begin{frame}{Ursprung} Digital Mobile Radio (DMR) hat seinen Ursprung als digitalisierter Bündelfunk/Betriebsfunk. Daher sind einige Techniken und Begriffe an diesen angelehnt. Einige dieser Techniken werden im AFu nicht verwendet (Alarm, Verschlüsselung) oder zweckentfremdet (all call).\\[0.2cm] \pause Beispiel Flughafen (Gebäude): Es gibt eine Vielzahl an Gruppen: \begin{itemize} \item Die Reinigungskolonne, \item die Sicherheitsleute wie Gepäckkontrolle oder Wachschutz, \item die Techniker, \item die Betriebsfeuerwehr und \item die Zentrale. \end{itemize} \pause Gleichzeitig ist so ein Flughafen ein riesiges Gelände. Das heißt, nicht alle Mitarbeiter können alle anderen Mitarbeiter direkt erreichen. Es müssen also Repeater aufgestellt werden, damit das gesamte Gelände und alle Innenräume per Funk abgedeckt sind. Daher wird häufig in jedem Gebäude mindestens ein Repeater aufgestellt. \end{frame} \begin{frame}{Beispiel: Flughafen} \centering \includegraphics[width=\linewidth]{../fig/trunk_net_ex1.tex} \end{frame} \begin{frame} \centering \includegraphics[width=\linewidth]{../fig/trunk_net_ex2.tex} \end{frame} \begin{frame} \centering \includegraphics[width=\linewidth]{../fig/trunk_net_ex3.tex} \end{frame} \begin{frame} \centering \includegraphics[width=\linewidth]{../fig/trunk_net_ex4a.tex} \end{frame} \begin{frame} \centering \includegraphics[width=\linewidth]{../fig/trunk_net_ex4b.tex} \end{frame} \section{Sprechgruppen} \begin{frame}{Nachmittagsschnatterrunde} \begin{block}{Anwendungsbeispiel im Amateurfunk: die Nachmittagsrunde} \begin{itemize} \item Eine Nachmittagsrunde findet in der regionalen Sprechgruppe statt. Z.B., in der TG 2621 \emph{Berlin/Brandenburg} kurz BB. \item Alle Teilnehmer befinden sich in der Region BB, außer DL3XYZ, der ist im Urlaub. \end{itemize} \end{block} \end{frame} \begin{frame} \centering \includegraphics[width=\linewidth]{../fig/talkgroup_ex1a.tex} \end{frame} \begin{frame} \centering \includegraphics[width=\linewidth]{../fig/talkgroup_ex1b.tex} \end{frame} \begin{frame} \centering \includegraphics[width=\linewidth]{../fig/talkgroup_ex1c.tex} \end{frame} \section{Technischer Hintergrund} \begin{frame}{Technischer Hintergrund} DMR ist ein digitaler Funkstandard. D.h., die Sprache wird abgetastet, verlustbehaftet komprimiert (Codec) und digital übertragen (4-MFSK).\\[0.5cm] Da digitaler Mode, können Metadaten (Quelle, Ziel, weitere Daten) parallel mit der Sprache übertragen werden.\\[0.5cm] Moderne Codecs erlauben Sprachübertragung mit geringer Bandbreite (Datenübertragungsrate). DMR verwendet AMBE+2 (ebenso wie System Fusion).\\[0.5cm] \end{frame} \begin{frame}{Technischer Hintergrund} DMR verwendet lediglich 2.45kbit/s (3.6kbit/s mit Fehlerkorrektur)\footnote{Quelle: Vortrag DJ3EI, \url{https://www.delta25.de/dmr/dmr-einf\%C3\%BChrung.pdf}}, bei 12.5kHz Kanalbreite, ist reichlich Platz.\\[0.5cm] Wir könnten Kanalbreite halbieren (6.25kHz) oder aber zwei QSOs gleichzeitig auf einem Kanal per TDMA (time-division multiple access) ermöglichen.\\[0.5cm] \end{frame} \begin{frame}{TDMA - 2 QSOs auf einer Frequenz} \begin{center} \includegraphics[width=0.7\linewidth]{../fig/timeslot.tex} \end{center} Mit AMBE+2, können 60ms Audio in weniger als 30ms übertragen werden. D.h., wir können die Zeit in zwei Zeitschlitze aufteilen. Jeder 30ms lang.\\[0.5cm] Was/Wann Zeitschlitz 1/2 ist, bestimmt der Repeater. Er gibt den Takt vor. \\[0.5cm] Somit sind auf einem 12.5kHz Kanal zwei parallele unabhängige QSOs möglich. \end{frame} \begin{frame}{Qualität und Reichweite} Technisches Limit 150km, für Relaisbetrieb ausreichend.\\[0.5cm] Hörbare Kompressionsartefakte durch AMBE+2. Auch bei idealer Verbindung. Dennoch sehr gute Verständlichkeit, vglb. 12.5kHz FM. \\[0.5cm] Qualität beleibt auch bei schlechtem SNR erhalten. Dann aber schnelle Degradierung. \end{frame} \section{Konfiguration} \begin{frame}{Konfiguration (Brandmeister)} Die Konfiguration (sog. Codeplug erstellen) von DMR Funkgeräten ist recht komplex. \\[0.5cm] \pause In FM ist ein Kanal ein Relais. In DMR sind Relais nicht wichtig, sondern Sprechgruppen. Wir programmieren daher logische Kanäle: \begin{center} \begin{tabular}{|c|c|} \hline FM & DMR \\ \hline \hline RX Frequenz & RX Frequenz \\ TX Ablage & TX Ablage \\ --- & Zeitschlitz \\ (CTCSS/DCS) & Color Code \\ --- & TX Kontakt \\ --- & Empfangsgruppen \\\hline \end{tabular} \end{center} \end{frame} \begin{frame}{Konfiguration (Brandmeister)} \begin{description} \item[RX/TX Frequenz] Klar. \item[Zeitschlitz] Hatten wir schon. Konvention: \begin{itemize} \item TS1 überregionaler Funkverkehr \item TS2 regionaler/lokaler Funkverkehr \end{itemize} \item[Color Code] Meist fix 1. Vergleichbar mit CTCSS/DSC Tönen in FM. Vermeidet Probleme bei Überreichweiten. \item[TX Kontakt] Standard Sprechgruppe für den Kanal. Wird angerufen, wenn PTT gedrückt wird. Man kann aber auf einen empfangenden Ruf direkt antworten. \item[Empfangsgruppen] Sprechgruppen, die auf einem Kanal empfangen werden sollen. \end{description} \end{frame} \begin{frame}{Konfiguration -- DMR-ID} \begin{itemize} \item Jeder Teilnehmer, Sprechgruppe und Dienst benötigt eine eindeutige DMR ID. \item Eine ID pro Teilnehmer, nicht Gerät! Zu bekommen bei \url{https://radioid.net}. \item Funktioniert wie eine Telefonnummer, incl. Prefix. Z.B., 262/263 ... Deutschland. \item Datenbank ID$\leftrightarrow$Call frei verfügbar. Kann auch aufs Gerät gespielt werden um ID aufzulösen. \end{itemize} \end{frame} \begin{frame}{Konfiguration -- Sprechgruppen} \begin{description} \item[TG8] Regionaler Verbund von Repeatern. Nicht festgelegt. \item[TG9] Nur dieser Repeater. Verhält sich wie FM Repeater. \item[TG91] Welt weit, ist viel los dort. \item[TG92] Europa \item[TG2621] Berlin/Brandenburg \item[TG2629] Sachsen/Thüringen \end{description} \end{frame} \begin{frame}{Konfiguration -- Kanäle} In DMR werden pro Repeater mind. zwei Kanäle programmiert. Einen für jeden Zeitschlitz. Meist aber eher mehr. \\[0.5cm] Ziel: Kontakt zu Sprechgruppen. \begin{enumerate} \item Liste der Sprechgruppen zusammenstellen, die uns interessieren. \begin{itemize} \item TG9, TG2621, TG2629, TG26223 (Chaosrunde CCC), ... \end{itemize} \item Liste der Repeater raus suchen. \begin{itemize} \item DB0LDS, DB0PDM, DM0LEI, ... \end{itemize} \end{enumerate} \end{frame} \begin{frame}{Konfiguration -- Empfangsgruppen} Überbleibsel des kommerziellen Ursprungs. Wir gruppieren alle Sprechgruppen, die uns gleichzeitig interessieren. Es macht wenig Sinn, Sprechgruppen zusammenzufassen, die nie gleichzeitig empfangen werden können. Am besten, per Region:\\[0.5cm] \begin{description} \item[B/B] TG8, TG9, TG2621 \item[Sa/Th] TG8, TG9, TG2629 \item[CCC] TG26223 \end{description} \end{frame} \begin{frame}{Konfiguration -- Kanäle} Jetzt sind wir so weit, die Kanäle anlegen zu können: \begin{center} \begin{tabular}{|l|llllll|}\hline Name & RX Freq & Ablage & CC & TS & Kontakt & RX Gr. \\ \hline \hline DB0LDS BB & 439.5625 & -7.6 & 1 & 2 & Berlin/Brandenburg & B/B \\ DB0LDS ST & 439.5625 & -7.6 & 1 & 1 & Sachsen/Thüringen & Sa/Th \\ DB0LDS CCC & 439.5625 & -7.6 & 1 & 1 & Chaosrunde & CCC \\ DB0PDM BB & 438.4000 & -7.6 & 1 & 2 & Berlin/Brandenburg & B/B \\ DM0LEI BB & 439.1500 & -7.6 & 1 & 1 & Berlin/Brandenburg & B/B \\ DM0LEI ST & 439.1500 & -7.6 & 1 & 2 & Sachsen/Thüringen & Sa/Th \\ \hline \end{tabular} \end{center} \end{frame} \begin{frame}{Konfiguration -- Zonen} Da so sehr viele Kanäle entstehen, werden diese in Zonen zusammengefasst (meist nach Regionen):\\[0.5cm] \begin{description} \item[Berlin/Brandenburg] DB0LDS BB, DB0PDM BB, DB0LDS ST, DB0LDS CCC \item[Sachsen/Thüringen] DM0LEI BB, DM0LEI ST \end{description}\vspace{0.5cm} \pause Einige Geräte unterstützen auch sog. Roaming. Dabei wird einem Kanal eine Liste von alternativen Frequenzen, CCs, und TSs zugewiesen. Diese ermöglichen es, mit einer Sprechgruppe in Verbindung zu bleiben, wenn der aktuell eingestellte Kanal den Kontakt zum Repeater verliert. \end{frame} \section{Demo} \begin{frame} \begin{block}{} Demo... \end{block} \end{frame} \section*{QDMR} \begin{frame}{QDMR} Da Konfiguration recht kompliziert sein kann, wird eine Software vom Hersteller verwendet (CPS, Codeplug Programming Software). \begin{itemize} \item CPS vom Hersteller nur für Windows \item Bedienkonzept aus den 90ern. \item Miese Übersetzung aus dem Chinesischen. \item Sehr auf kommerzielle Anwender ausgerichtet. \item Keine Anbindung an User und Repeater Datenbanken. \item Kein Austausch von Konfigurationen zwischen Geräten oder gar Herstellern. \end{itemize} \end{frame} \begin{frame}{QDMR} Da ich kein Windows habe, brauchte ich eine Lösung um meine DMR Geräte zu programmieren: Also eigene Software schreiben. \begin{itemize} \item basiert auf Tool \texttt{dmrconfig} von Serge, KK6ABQ \item läuft auf Linux und Mac OS X \item verwendet gemeinsame Konfiguration für 20 verschiedene Geräte von verschiedenen Herstellern \item Einbindung der User DB (DMR ID $\leftrightarrow$ Calls) \item Einbindung der Sprechgruppen DB von Brandmeister \item Einbindung von Repeater Book \item nur 100k Zeilen C++ Code. \end{itemize} \end{frame} \end{document}qdmr-0.12.3/doc/dmrconf.in.xml000066400000000000000000000347541501654372000161040ustar00rootroot00000000000000 dmrconf 1 dmrconf ${PROJECT_VERSION} User Commands Hannes Matuschek dm3mat@darc.de Main author dmrconf Command-line tool for programming DMR radios. dmrconf file Description dmrconf is a command-line tool to program DMR radios. That is, generating and uploading codeplugs to these radios. To this end, dmrconf uses a common human-readable text format to describe the codeplug for all supported radios (see below). This allows one to share codeplugs between different radios. Additionally, dmrconf also allows one to download codeplugs from the radio and to store it in the human-readable text format. Commands detect Detects a connected radios. You may specify a specific device using the or option. read Reads a codeplug from the radio and stores it into the given file. This command may need the or options if the file type cannot be inferred from the filename. write Writes the specified codeplug to the radio. This command may need the , or options if the file type cannot be inferred from the filename. write-db Writes the call-sign database to the device. This command may need the option to select call-signs if the complete database does not fit into the device. If specified, all call-signs closest to the specified ID are used. verify Verifies the codeplug with the connected radio or the specified radio passed with the option. This command may also need the or options if the file type cannot be inferred from the filename. encode Encodes a YAML codeplug as a binary one for the connected or specified radio using the option. encode-db Encodes the call-sign database as a binary one for the connected or specified radio using the option. This command may need the option to select call-signs if the complete database does not fit into the device. If specified, all call-signs closest to the specified ID are used. decode Decodes a binary codeplug and stores the result in human-readable form. The radio must be specified using the option. info Prints some information about the given file. Options or Specifies the file format for the input file for the verify, encode and write commands. This option is not needed if the filetype can be inferred from the filename. That is, if the file ends on .conf or .csv. or Specifies the file format for the input or output file for the verify, read and write commands. This option is not needed if the filetype can be inferred from the filename. That is, if the file ends on .yaml. or Specifies the file format for the input or output file for the verify, read and write commands. This option is not needed if the filetype can be inferred from the filename. That is, if the file ends on .bin or .dfu. or Specifies the file format for the input file for the decode command to be the manufacturer binary codeplug format. Not all manufacturer formats are implemented. or Specifies the device to use. Either a USB BUS:DEVICE number combination or the name of a serial interface. The device must be specified if the automatic radio detection fails or if more than one radio is connected to the host. or NAME Specifies the radio for the verify, encode or decode commands. This option can also be used to override the automatic radio detection for the read and write commands. Be careful using this option when writing to the device. An incompatible code-plug might be written. or DMR_ID Specifies the DMR ID or a comma separated list of DMR ID prefixes for the write-db or encode-db commands. More than one ID may be specified using a comma-separator. or N Limits several amounts, depending on the context. When encoding or writing the call-sign db, this option specifies the maximum number of call-signs to encode. or JSON_FILE Specifies the call-sign database to use for writing a user-db to the device. Initializes the code-plug from scratch. If omitted (default) the codeplug on the device gets updated. This maintains all settings made earlier via the manufacturer CPS or on the radio itself. Automatically enables GPS/APRS if at least one GPS/APRS system is defined and used by any channel. Automatically enables roaming if at least one roaming zone is defined and used by any channel. Disables the enforcement of limits. Warnings are still shown. or Displays a short help message. Lists all supported radios. or Displays the version number. or Enables debug messages. Supported Radios The following list contains all supported radios and their names for the option. All radios running the Open GD77 firmware. Anytone AT-D868UVE or Baofeng DMR-6X2. Anytone AT-D878UV. Anytone AT-D878UVII. Anytone AT-D578UV. TYT MD-390 or Retevis RT8. TYT MD-UV390 or Retevis RT3S. TYT MD-2017 or Retevis RT82. Retevis GD-77. Baofeng/Radioddity RD-5R. Baofeng DM1701 or Retevis RT84. Bugs This program is still under development and may contain bugs that may cause harm to the radios and may even destroy them. Hence you may use this software on your own risk. If you want to have guaranties, consider using the CPS (code-plug programming software) supplied with your radio. qdmr-0.12.3/doc/docbook_man.debian.xsl000066400000000000000000000004011501654372000175270ustar00rootroot00000000000000 qdmr-0.12.3/doc/docbook_man.macports.xsl000066400000000000000000000003711501654372000201430ustar00rootroot00000000000000 qdmr-0.12.3/doc/fig/000077500000000000000000000000001501654372000140555ustar00rootroot00000000000000qdmr-0.12.3/doc/fig/autodetect.dot000066400000000000000000000020101501654372000167170ustar00rootroot00000000000000digraph detect { graph [nodesep="0.5", ranksep="0.25"]; rankdir = LR; enum [ label="Detect all devices\nmatching VID:PID"; shape=rect; ]; single [ label="Only one USB\n device found?"; shape=diamond; ]; save [ label="Save to assume\n device is radio?"; shape=diamond; ]; userif [ label="User selects\n USB device."; shape=parallelogram; ]; ident [ label="All matching radios\n identifiable?"; shape=diamond; ]; userdev [ label="User selects\n radio."; shape=parallelogram; ]; id [ label="Connect to radio\n and request identifier."; shape=rect; ]; end [ label="Radio detected\nselected."; shape=oval; ]; {rank = same; enum; single; save; ident; id; end; } {rank = same; userif; userdev; } enum:s -> single:n; single:s -> save:n [label="yes"]; save:e -> userif:w [label="no"]; save:s -> ident:n [label="yes"]; single:e -> userif:n [label="no"]; userif:s -> ident:n; ident:e -> userdev:w [label="no"]; ident:s -> id:n [label="yes"]; userdev:s -> end:e; id:s -> end:n; } qdmr-0.12.3/doc/fig/d578uv.jpg000066400000000000000000002546431501654372000156370ustar00rootroot00000000000000JFIFC   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" VerSL`'J/Mg;rޝ#Ii_+}_wk|wrqɇ$sFƺXM} G֓WuUlydȗ-,'[I/fk\ -4•,7I״6íƏ\hl i N 1isW$MR%+",.@cIhRctNmn<^2QƉӹN,[w-T:<(6_90+|X칶Yȳ2e5B#T^YUw9|㖳UeuI18;e?SFe~RD ^T$hӼ\ҽ;w> C8+RO}4^{EgWa±5M%֕ ]2>sEw n^2ӰCkO+3~˯!g$mJR2_D*`Cͥ:1U{I|P^󅜭ڏcĠrz3Ws+:-RM1_sdaxA@}% Gzg>?gajNVed8esA4':;>ꦫn:e^VK}Q;#6 7QjÐ$/izSKҦ6qh?MXL~Fݡ}a]9alAg2qtYF\Jכ:Dc4W5 \aQ}M%w9sGc/- AeeAsI?A;n|+^۝ל?M53)iK:JXEoCGю}'\يy#N)U\NsOu77i|bQ `kU!]7ğяʶywCmpr9^cc 3awY 3l+FU8]]]6wtunA3NYŝ jFٳ^ 3UVK;:ͼ̤עF؆tM!}ůz,4[gs_tou+>N;Ϧ>u(6qo9kyiAĥxr@#^2w)g~Nj𻏄,y:JkkT=Ʋ*(nY|"Op𑏚KH,9yI7%,bg7~U%VYܗJHh GxCx>&O6`|=z]=:}ke..-K[ iQ33^5'Q鱯͚֑ȉ ,"wI̹S}~}MG̭{ϔ~{2Fϫs-o]L}gΥ d9q~{ckYQNg&/E'W\87\)ȯ=_^}q<<ɈF^2yy<яy~:$=9k<<=x2=uŽic]֑Yy?(!_'ނ-G*iOgOTAHkmS[R%hYU$Z&`Gmy#'&#&>1<yɏ^2Ld'Os~{5JZa$ZMN)T?-:=i@L74>G߫zdg>{fq2x=x=x=x{meԙ\vtګ0[oH:qk-Üoľڠ_h~w?ˋIPV|URjN`5gzJNjɳ!4]zޓ4Muu+GVɻҋDyE Da|#2} dJB[8GiZHFDc @tUW4G,"zlj𑎑-#nQ&^,r6BMB${I8׆Rbz]]$aψg+͞l|7No+Y$gc|y=<$c~:3.&}ֹ{Gc ^Fߔo HؒDQ%JED# ޒ7B?4 m-~6n>DI9̟$λy;vf&zCҷ|bX'}CANbd=շ9][8GyݶÿD]a׎+ӂǻ]}7-}炸bv0}iOG>Ru>Eϔ |*J/6rW4:}|'U>|fõÑ^C_ H}%X:jt:΋]*R: |*5"QoZ*-n5Ւqo/t8~#;cl-WZT%egG"7D+-(EТa"﹊6ŕooj/^˒'ʏt{[Ej|_ťōjp}->O>7YM7'$1=A|7{ ݸL%Ummy_\c r/NhZyo^+fjW>utūYT=YC}-<;@~k:m5jyN]izZ%}NWw6$\l"Vd~ojY05a׈a]Mƶ4K֔Z%1vpYѴ[$0»v3L[7LβyTmek$f6臸ʿy%}鱯|$evږl}UVtZ*۪Dqn de:lmD[@In3ys„\7rg\!et\bO9+SlkcLi1\A>TDѻ`"ɂc9mjv4y&83ȫgaFFA<6ŕ۱Gh_A؍5{G簯:`>q&X0=VO%eq#$#f~cz{Ɣi3Kw\c#F~w$zZDyWN,<ޑr#^H{ um#uYGA >X#<W֟'|q&d9F]Ϡ&U"y}G59SocIާjվPz14H܍yJQzt%nV^-oYn85lָFͺvPzk奉*7=IޡuǑPP H&h!aRj~[nܕ'mijP)R5uTr̼1z72Og?~gN%*RuyVSGI>#Dizʖck$ \Ir ZU$W,toɌMںy&k݂hٌ6O'C__bfcQoPԺlf4S~u ׬:_t\UaʎUsSau jsyW+wfדYs8ytYUvUC<%&5ySդl4+};9oV9:d`cUqZg1;ZN۪u  ![ݏG>JIGsHӏfӢdKªY/Ȟ-0\#4T%qk0vZ*=8X4̇0ν>!6}/7SZ|V)w"°}(' ^|>p^wz8HEzOx|,~/><~>CGȽ+_~O>_{a>>O>L?Y'C䯬|q~~>~>>]o c6"8]Yh=+*N^~Psz'u`9Wk:rneW׶|^]b}>]ODI-Ÿ-.~F_<uṏi>/ytrqUWg&n:WgT0 k#mB|a.'c \35\^_t]}7tZD%U9}gBѼQSv4!1"2 03#@A`$PB45C6"r*'m:}V).$'t&U+ċRڍ[6[gtmt0T'C$6cըuޣ-)0iVf-P#BI[<{܎<.<% 0[ɵ;MZd2zBAU42_[Y~Z@%$=ZWJ}GnD"R;e,"xs) D6g;[h?v"w=yL=mQl]٣ Y_3oz:d ! NUG08`%<r b d%+-yg3 SF&vh/ڶ\OSA+ /Jh̲׌"]iSgB'voqXɄaѥ?N:YTtU9Q.{Fc!~R!:we C~lGy_,:Tw10z-d6'2i#'u&5AGdfL\QE`d9{f"yl0M*&AM̴ݖb2}?Tћ/GqGqyx,R`_[qE؇gĖx7[8L[g)8>lR6@Fb1u<8kF a?z'C\ \. gEJ4eC؟o `LC.6@Jx8ʘ9ZE.763RޛYjV!a9rdQ3=mpI{OFעlua5Zp;eEnUZ7s#bw8#0 z"'##l&“W~]\s@@ܰ#f%Bʕ5fA+>9|PqT{ [MB'+[NOսpZR Ӊϸhe*":1N"zrZ`(J/Tw[Y:rA~{V**IHJKm6+A^ƴ3>شn+ź9ѫepfqFՆqG4֖byZI)^ s;elNܑL yUqJNd%z]@G7sFSM99.QMH>)M:<&6˫,=wو7+8ψq/􀼥+mجHلy!/A\Zd\Pҍԟs#?㉛)0}\N`unYS>_źb  E'X'9]mdߩfq҅PfFj~}f|iUm}Q[h$.;7joXk❄QY(|\+X͔pᴍc0K@ٚWpml4}d+ߑ7m6Rzy%KZT|#/'*R8X}n:Ud86Eo-jLvi-E`eɞYY_.&UX`b卝7AH9$ji,?;eǻ'ڝ Jj_о""&L X}<3%͋R]ݖ\./D7?NsQG3{K0`1ݐd% Ԃ43TyEE6s`\aYW9厓b8*l1`zcnӡgJ^z5*.忖O9>+[ewH2HY"a32H]VjNw⫧>2iTtZVɡ^G$vI4ȧwm4:&-P7F-xm?M+I(3PTCl6zM'z\t? SOCCz蛸~tu%jRS1m}qɿ}LNͱ[tA A-jqw#AHjtR8T^7HLB/f̞nSkBBSFy$"d覯ċڙ_臠(pDNŢ4IG|JlZ5vM=zrمR6O:,-8#N!}jp:ȥoˌ4gN؆P( I0/246CDpQ-6 зW]|?~k[ M-wWO7]odŐg}}/xWl>Xc(ՎyJf~}:7v__hTbVTKRl-ԩebՎ^k lߜs-ŽRO ՝083w㞹]_ (ӎO!)g/8$ gUuir ̂9c奄tնڍ]lRd¹AI0>=Ǵ7ZE~blA'[K˨֩U 7#`m⛵Pq]ߪG9ːb"Q39QlΣJ2& GH.2yyQG (as6`{s2 psjK#jޡ-7U"SK%E@=Tqޕ/xa fyigsܕGNCV``j8Z R ݠuuDRrܲSXWiWqv`oS3|cwBNkҪHʄ'3ymH]Re! ]V#6Jy[לUBqVl2Eڍ]>[EMRܒR 92),{R*ω҅2$[-#m҃6r$M9z✅)!+BGHA .2 ^oRHIe b]V۩iM̾BiA5 R(ui[Y<u:P,D ܄b%r5$-^ѯGe믛jMC$%^ܕ93K6=tO5y(u[LN4׫!vՓbb-8ei*,zzR 1ŮK>`AE?[=b&^benIW)vmY~sֆvY(&5M^X⡚3hL}!av6z&lKeP47iʼLJFkPG!7=?H Ű;:p0zޑngA+|nةZ݊GCYf!mZL2Y§Խ#>gqumٞdDҋ>"3F,YUgz_u_Z+7nmS ]NU T5Y*W?0;rB?X'ov\3  x 'пfwvXxvvv~~t;ܣ^&uGܴ 㲌@ky,Q^>UNV\gM2+p)eIq%گݓ:>mB͆ڝKӗ/Xs88+S=?PQ EJ[cޫs40QG+c{s62uhbwvѓ:Rqe2ruv ,%Ի~R!VXPRB;#?Hm\Hi.\EJyr|awN.a/ǵz:p8ݣwS[)G <d9bj8I Mɀ/+>|.S\- o5O!<2sJ\.yW4\/12/3233:332/12yW-ChAfVV!-j:QaO$4fG ܎?]! TOYFok[OA< KB$F+3Wvik K-fh_)iX_)tImm0t_-%O5i 3Yb3>RG8aZvqrg&52buCk$xP{qaũ ý? V/(_9pnp4KuwzI dnO#3u;iOՓ޷췭D&dz ̲k+u+.j˖宷[_+WXYb_/ץmd]eegܙQy%;N).y&(G#)Sӵ8hoMvXQBrz_k48zk/}1&Mˮ53f *4u,.]kϊENu6'UkęngN)2YOͤ7(FR'bGoPF8q^KG8[5;8zv"\˙sWǘ^as<ͽ̉ķDJvcLϟ>QGatՙya^YeyvRUi^Yj!Tӫ*e Pmvt^5.còʎMgSJ@{TҹUOaZ (b,[_?3coӇ"L#`d|e 7AMnIb$ &ښ4GOi 4s)ME#nSv_UM]) >ZLr!k_Sx֤SUN05T~7dr'6!ݝ.)0)F>fG[+b/igD&at`kx;Lx:ЧJğkOzr^cyE'a._kx7f#y9'^Ref^ZU^^DbEW)m7c}ĦQT3nyY dFx wo(+ 򂼠(+ vw 򂼠9(@Uī?Mx[]VfF 46eqIw'S6vL'OY:~Ȑ{įԫI;VP݉˫n L K!\Lq2Mtq;'j'r''Bd-'\<&h*[kԭl"hZ v : WEnδerxFg]V qufOх?mFYe]E[3 Əaf:x+|q6 rCT Z[RE. mv}+<Gr)xD>&Tك1 HGF$@w.(Uщ"v[ X C8 0WlVW5*kO dL c7L.6^ ~?SEp¸WGx?>ŀ_ ҟ V‡ڧh}^,vDw'5wP.} W\K8v~F>Xڰ8 舶!s*U8 #rO gg/#fb7tYӮKL15( Gxl ze(Im, 8zugXOOnfsvd$M%aE{"DJǔ[pE)?G衣 Z-HtXҴj G(ԟLWhԋH)۵ijbK2io JbuzQB!EV{Au%uI%b*_h_S>>ŵ;x; | %kPw9}"!j!!JVGB۞,F3HbF>o0|C8]RU= cLRYo!c7eCT=~ֵ%9)dW{S(}ƏN/2Թ%z^bƓfMN-Y(ұ#A+´O$_+N -䖉$)>eO/~{3ͿLM \1ؘ*kA{Wѩ 'PE&Dw27o?H]w*zenes:>œЖՖYeYe[ŗOh˚~H,Cvαn4ub膡8PT{U GRկqQ+4]Tu +ԬR &/vQ-O)i$K*c px5Dl$sK!IV8x2?BD]Qdc*Xm}6*"/)e'`wxoND>ߧ+>  .itJ˚To""& .5ɣd9?h{ghx@l!r/w׼7'?aZR#bmkֽKֽKnCK"9Mot3'7wq2/F aH #tb|ZcyO4P]cZ _{?7d,LoG݅G'̋q2[fE r #%[0pkӏ[^5ZSPET ܢ +p&:'OY:gWZȼvB$QOm)m4EoQk9'N,7D\˝s2d2 xh7:,.*7GiQV Xaq # .Vآ|.E9jaMڿOgb sIjlMM Ri,4٣Mf)8%حvpQV Ryv/a_;Idu2 A}=΍3&DަnS2Rf-hS>덚ޥB/ > ?7~bV#tjul~f^ipc XL>Lxa0 jܒZFҺӡy5ioiu"mH^m+'o3eZQ~5ݜ; u'QKٿ d]l݇[4#9BR;9B?82wfe7mMOdVEzA\ADl4߉n Z%:n*#etH:ڲٯ;zx7ecǬ>vYeYeF6N|@ζF[#[#[Am V> k`3I $!]~ΗAkPE VCQ\/j0#\[fXbdq1XЯP2">ZӾ2h Ѩ8Bb$anh-mȡZ]]0tmj{zxagr$`W`iNuvnR6Z7#bWdd4-=bȸ p%K.'\n:rxebUf1h@'-Z=-/ư,:MP,tFn@n`W3|<Ϻ8郶]Ѐ\lGwL-Hٍ3v첿dXXN~vg{-̷2;Yur2C+sffngn r) zӯ/3//2pJ\R.3[ l%[Im%XuXuEAM-raN?JO4d)PSnܥ, ) 2$,9XgB9{=/uv_׆vm&?Xo Ŕ_S,Sǔ]p{[u 3; g$*8v(iiDm>Tv7jVMyS/!gÐFN*scTCYd KL(:i: K i 2eXt]WzzWl#yuח^]yuvc3\8,3(PZ@4i扗(mg,y 6N;7Dosg"ErlnY좳#&Z5I95[n% *qsgq[>Q%"ot,YNR}6ȴ*]?pƿ1ȯ/ |F_%|={+,/X_(Qa|=*ޛ3 kkqu7\nquq5Ƹ\Kq&\kbض-[V:aI>]3}4eV#3˖5%6I5J*-gng8 -E+/'Yy*^NUWU*!Uy U^BWꯗT_.[Q|mEڋj/U_-YQ|eUʋW*_-YQ|]Euʪ_(SU|5UZWj,)ΓZuN`3ÜH1a UXLޗZ|: J G*USTk4Ɗ$ :jXdbHS8тp]Y &e U{XtY$NBP0hcIXA~u_Xr-cNUgO$q@% JBh ZJZTݧ Βb9쀫NLY4vj:jʅ؛cIV(K(dS1Q_D2Mg^^gU[6`XORHFcbc4^U&t.QkSZj+ٝӫʧэ%WWo}F#Qy74qO'4KqՀAv# WqV7oB|biGMqZ+I͎hm; ϋkUyh5;ʎG)-P*^7Vv- $w.bYH[jVEIRt]Nt  ? s>qZn|O aUkcNO5*_Rit~ `Gr@ݽKky3h8rBK+h~Iܕ6f̦5j^״6 Q;!O Kx(Bڊ &@T$UcL w&:nѡh"DfGTj[# Z>*zvԄ [WI^ |(p愇siP2^WMJ;%ѨM%>r͂2aB-Pl`q;iV.=eoA*[V))ޜY!'=ؕhԞhͩ3eFrL7ٵ*Ch҉;- ^&2ȝ iQYvӖGZuh&%) oM $mKBb7arҫc+L(8oF-W+_g8WHVMIUa}HMI5`B*(n;Ӌ֏Bl%L/GQѸ9E x4 o=5JYW{+4ɹߚpxtZ qp5J3!(e]= {N E{m BoJ i+XJ6+G(g}ܴ@HXjMhMJ4h \܈P`ٜM{}J`~-֩%;C{Wފ3d;TLaT1܂K}I<+ӪjRт N~jmu%Y1n DYNs` '-#1UȨi9fcMޢ+-BSX{ղ7Q6Lk}x+hXb@cT~'SyiS73'8l 772Nbw^NbsJJԚUE2Q{5S?yڍom>"ԏi .cI%\/BqѼyXn?uOho].}hM.~6AxN#{(pܟYBɐ/{'WUɟCEU %(GN!;^P荩ZmQyMd_Nݼ|kf"/ ueV{.,p#bNV%Rzb&K'b]p;v(m\3E9IW'%S XZqPzSlTNMfm̀s8QeQq^lܭ;X !Úbf IUZP q(khNMߊO B&`܂oP b]L qo7"\bbyhF&Ĺ oʨjOx {zh_ :&iRdLE Q^/:-[He<'e` 34(,vJd:VcK%]#ӂlf"PDkt,֏37=٨w?@h%wrʁ huL#8b.$Fj `,<3#qM|\ OI\Bs* #>Lxv8mYEBf]eCYiWUʝ:S_j/j լ=+:tpkbӴc#Q&`6^Dw*neI)Ţ YEH?Cb1rWb? j{WK(1}0cV? 6\ufH6ɝ7PlH1+F"q)7w3j;3 u{ X5 Y?QTe1܄!£jIT-Mw Fsd*XR'/o{ xk{[տp*MyEKn7ӼȄ)զjc]!UI%S00*Et^hǣfVfCa }m%R Q_6gܛMu=y#Ȉ2iiT)L7rNF/$$)tA \3qP; ~(z^ȭy洘°I;w!ѺU^DSAR- b͟UƱ_϶C?@VTn; Է ǽrjRqNc}p(R ኇaCSqd#4 RX4C(sgx^-bN;%t#g^&wu*mx/X6lZ0㉽ t*˴0tDjC:'Wh wc;;~ͤ@} H?;})SAB]Y4jqeε5s]#=kVI!h*X\J*EA|BOk o kժyjO;Ԓ-Zt){u~WW ԏ 76\%` "/*H#zd2^됪Tv֨5X݈%'7!b( sh7a$[ɸ|ɻ6߇Syq;CpFv=}=^ \ ؉˚Z6SOK-tlMdVez:hJui˰.&Wslg\xQQZ7mEX8s7nr%Ƙ=Xp(YpڎW=WjFФN8H+J3`! |&NO;.VLvn`h)D@h9y[,h\GSihJ ` h:'{|8_kҼ- \{v7x  NcxAkЭ@vѽAu⑲VHaiP`Mk+Gz^y|`-J~Uٰ]Uִ&gGjL0H4oY5ۈqQP+Rqj ʇFx`0`;Z94R"BoKP&3^ k'n),y渨uqRc؃j*pB)@P3•j>Uw%9SZ$bҡ4#DԧqE9電jA r+'NWvqsN+P+9XW9] U`ZjsP5yG^'ڋcҳw**)bڃ2Ǿ杣5[ux" جB-UbVذZaGiTf1ERy*itvt_kjT|,V*2 G=@U١2zЊU~X)jBWs;.(ԅ"sW=17+͢ g<_u?}8409-F~?5虪1YYVTu/،+:/ڴhU¹ `Gb*90R~|oYKpw`ԔEXNbw6* :5UEάVo' BҦpxW|ȹ^v3YA;6)SQr^>hGos=|첣Cq4ѵ2t[5A !vUԏ +]ww+S~KR~KR-ZES-Jo\wīUuEmkT%nZ.k5jGWkS?4tUwkʻGWl'WGZU]WVy?c]U#jѫk'\w~+VX?6S?-o\?5|.[Tێ*jA{ sWuBZzIQVVjVt7QAc~K<0J9>J.\f֦޿A*(1h(R"NE031˥8- {=3}+WƈWCѺ. U|) 2Z .h{QsW-k(Bj{zGW|q*J d+"Cоy8tGxԨPx (YqFmb.z1 B).w΄lE(:VKYk-&ڋP^V<#Sl+]Pw< :BY;Ğ BVrg K\ ŬИ ~ 8lCN\DFI-$݈q65O bCP?p7.پBP*{eO 9X?U-|(q%.W\B%~ߪ2е)Ic/3h܃n*ы[慀Gv~ ,nfڟp$qU &N.tWYب}Gm*2zfpQSE_Fh8+Dܜx- WǂZZZj> XBj-Ec6R@k]`O%ZysPuR'UٮhuC/l7iV7`wRzԹ B3 JOis*q fqkQ6[ P4eoF؂/;nԴ  Җ/ VZM#NDx `ZlZUjb7'!CAV2eȽ@a%$ cfq+0Z\-eT$J%߂zArvm]u*3 8WaR4eRs毪9Y;wh5]XZf>`[5)m@1ZU!ۂ{dPtG{a3UJeDaadvF/mOSzå no'Y՛ ]GhRkcZ$D|V\-:N֤ON^mYD]٤;I.Q}-ˏI6mr]3Z qnI,W]b9Ȑާ>~bVx+Xm`8nk%z*~!W֊u7Kr*UʯvU<]O)Z ymLQFº*4zPzZ /ɭY4 2 &vNb8#iZ@{y7%ߒ^M|&wWx_,ɼ].̛ߒ^M|&wWx]г.m]/8+u%ߒɺ0"#2plXۆi#BgFq݂-k]fGb/Q*77jM;;u¥61(q=CԽC. TfqW#^q]mLmea!m -oZYku,AHR'49瀬zTXX5?g%N?G]ի/ٷOJMLylV:,ܕ SI#L"Rٛb87W<7rjZUfжbXV+&˿~Z=@]MoTT4Bs:AghA}6PjaZ(!F.qSYqҜO-.O%6.އ0 a9<F͙fk{ܰ \FfG}2]L$V߳q XϬs#khچL 5(wRY`dNO4̄dԍ)kd̪ǖF'u"&ůWKQXV U@3k;7uTQM\/2T4\]vl]ilӤ nQ7zSx  7P n$}eFCVL0Uu cpU:{]:fj䍭MԽ+e2Z9Cj.7gkvQNu9]UƥX.6KmO kķ2 ?,LD Pڻ roycXvoT26 84ȸY(+Z72U\3Zr3\f3lAܵǕzV5Z7\< 5X}[~gS;3HX MGF}-OQ,oؤo.Q 7Yuˤ^4Eҏ#O=j_]J֗%>T΀0oYC2J_h!U2`k&DAYNXֱL[))Vc"oFH{qvo33^w# sYVZֱ` s\wWh>oh=[LVAM%;:Ⱦi. hck5_V7\@3pQD+i(fagV^g!Jjs@) cao*#z,V+\ +Y=Pto↭PG^Q\Qִa$Yv Mmg #cD쑥 0K7>ӌ]4,Dгn"v 2` ̢[` V @{ qQS6fT0#˺!9#9W;pNSY5Pl=$=|c5>V3=Ñ]O9]Wj-"I=hq$qX5 axxMnݡ5vm b7¢28" *|Zk,zr*ϊpY ]ijhp3>~iGcԒivk]XX @ZO$KU'c6b\@hL<1NJlD޿B۲KFi}mZmXV+XFI YcXqWa8_gZj,Vz_,Qx֨Q"%d;jeZ͑ %9C:îrU 3dz֩[jZ]ԙQ7,}M>Nm1ȱ]Q\=[z*V k0ɵ੶i&y ? y>2"PI8*b1ۍA2)o(Z$mSeԉL2%7}ZmP DmaaetY6A{Ju:j+ kki=c(իi͘hndN[N^!-F]6JiM~~99vZθA5H Wiswܜi"fϨqAQe5f`'>\cF0Sɲ욀a@zhRs`a)(87m kG g,'ʲj\}R^_]XE̗oܿBkK l;Leq ލå.Y/ \Q{qNTvѳz<=b'$s(Ӫ!4<hIҞPhhxzz^H娵 Uj,#b՞(#T-SO> T(5hTF;{nC}ZU6Bp4+T FEINܬթYJ eZhVQVg$о9!C+TSO> \ />͊bX܊Zmy ԮCYs}:L ֕-lfj*X7aF)|Z>ѽZc5~exZ-V]QOnZ{aQ>ZszuWU&7ܲ~lSCPhsQ:LF%R650njjؿvO]vO Q Q Q U U T-SO ]JdnM u*?gd*o5X&ϼV nhqIq Cu,%j@t8Tf74|Sk?˷ ]0񂎧ISZRUS ljD~kS&ZvPгdX!*vaWVw.EIWPWǀPb*vcr> NnGVi\!` tՓV4\ Nd4tL0C5ۺw*lF,xBOJqn۵C'WS'ZԼO䵩xkR?ץV/}/9v^:, `X, X, VZU`X,3EbE_F-*ɝrYжx,VB`z=wZ<6fvta|/z^POKyB=/*)W/]?3v`3v ]]]Įx|J%v_/]Į쿸Ϙ쿸Wg+3+?+PWxWxWx^ߊsSdoIM7sTAX4of֕~֕RwpQWѻ}2+2vGWa|xw+oD8lQ60V8pqh\UzZcOlon+ѼNӈ"TD&Z%]zV+L48}7h-3ܡbV+4T^u o{` iis\Ph/D7w9KLQ< e{ZKbSIo" ҵϊUXײ3\+J-j>7צriڧ/EU~AZOSI3ZJh*o}0չz*e}2G zפc]-g%siRwuksKy,!1AQaq 0`@P?!"2o=-9 ogԠmd`N+6 'E9Q{3ɇ/>mAS-P O|E7U@a>F'DMq.#qǼn){1L5ݝb5omVPf L@3Jo" xo LnY)ezΣIz_jvbu)q \8&5pP/ӡMyvLD]Ex@Û,@:7>p970#*Aƈ%% ld'u6&4}e]KIJW d哪 WC84$?1hB*PyecӉXlB0CaRZ;m&V} eV(.MݺT/6!z@ tL f":6sk -tVV.nym`IUm<(VhPq-5wTWɊO~W\Q˓)O:Ϻ{ҬؖNΐL2H}].ΰԡn]'c}A@!e >0:=xKZC%.`Cj8 )vv#n9pS3hfPb9 ѹW36vNαS C.RB0=:3uJi._)_A V.oɅk{Dh^qn|chmhҏ9)NƞCYgEP\u2(Ŀu39=TQҊ[*:aPVX5 b:^Bjej_/#c Bw3(+pHܮRq~EQ ,j*^%XüEW[[H ˸@ef:KG)]يmHSo.&RvQ,vN5E5bnUaa`/8h%X0J+%%u+,5U`r}#xϲi?" HND)o:f.%_P3-fu2c L=n_0$NU"'!\`hK;"J"Ӊdz`-7-0{EW{΃jKf^e&3b\EYX;o ?a46B6XlMu2̛K:~I2 2N_?h%>sP.֯ur,gqQzp/CZANA G 9bϮ(Tg.f7 N0T. cξqs{1 Xy}pxJSm*8%Yhf@G&_Xι|}RUsWp? "v  w@S%e@F/Ip2Lov`}ԓ5B8&DoP !ͥ6Jb0trD(Z>R+jm: <k31JO8۝*hP'xmX S?$v+ysوlkh x}% y7{gK֑Ȩl#'{JMHGt'n %1Q"a\0vf=Y1t.]9=X1^tNjR 5W< =))^ˀsQny Kzi9UpcwcRzNRx/n6fN+ˈR/vJ?Ǻdǜ&~3 q!Fm4|'yVT oX[DPyE(.otL\)L|0-Z%ǵi9:3*a9[Ҏ+KUfe:SV(Wytn:&3jϘ9o,TN-ekUy~޳_9lRS ( %Û䧡~*hyLj& 't*TpCc=atL/G#uՔW-DغDP$jt}cƈz7(c"`2;ں}_P1ᬘܶt0>V6_K'K&>^_ #}fh4ny#Rz>jctfuV11x^<'O)|rv_>n\zs2J;~}t:W*;}e)e|h] ~kg ݝR-5wCf&mج:EU9}]]nWWKdx+x\=[C'BAZU؛00荘[qu[RY :@yx|ȜzK}_Q*ZG/XJVSVdhxDbǚLUhȧoe%iS%%/&6.۫jcU gS:uYkum~9<ɅL躇' Ĥ6gGٷ7p+}` ]ahW_ǖkf:kG_* BtXx]2~ڶ+571xa~/=0,FC&&fArbtҪgΥxP讹JBv\}Жr2&aӈeR qP>64Wx*h?iSY{]e Ϭkxۆ]9Ϟ6v4YϬmK5^62yyJxjCfí 9' !Qn;e93qaelzJZK:N#77ןa3w`toŴ>ZK+*zWĠ%d2'bm(#gGhG9{m!C>zdՃL2z=X{.ou<{0ކw5ZR;u2 K' md/)#3z8񫽓R<+Ay,ŏi倝>0p(vhK+'>.186-TU-zܰ2/3QS khVN{19Cgn&# ¬Yrƒݏ?X`:Sw`uX-ӈWwֹ+mg{1wMvZ8oZ>Tgtg+¾Ih[}~i6^`]yƦ[Hr;r-L7E`hpmfjWFj5OMQWB,7Oo '`%ϞK7VFJe.JgͶ)Cqr [ATnˀMeGx٥xoӋ҇+VP=7S-<$p<93Z 9*+7+AsdFpJWԺ,zƵJ-b9&Ҋ3uL7qąi+;'*}qnjQw2ޯT+ze@sn1 5 ZjglhS5N7tnUKTƋ]|@ե9_0abiu0\{N)E.^wrԹmc-%Qeq@*w;1étpfG[[/G}&8F|zNҗ}4n{NƋcB2ү-D<*kLʓ Դ*iÌx{TJV+K To8{ sm_ (3} .]B|W^-k!,|c1ź~b9,OOx-N^8El*)'nYxu1V4oUR cWӌbcsR}W7Gh s[[\k5gB8U#L@+RgWĦ6 "U576vo53@ Av~jMawzk["Ȱ{< A%Xc;%ާ~@.rǟOG2R@ÒxW `<+QʾZp^?9ʞ2FcޠӍssZ?=!P\O=C':v_#*u@Rq˧9\yyXEz>.ݔ{&^s;p:2RÊ5yw:2tE[hB nx+(`{{/شri>U+[>34?yPkΨ$j,R0XX+prT|y}җo(GU/x'80VcrvZK_HŚU>a([(QӡH_Տhg^=2yW"qVɊc͘pk->3>jYϤHڹE En&zR.K^^ZZ)ǁry3?rjV~&}jU`5LiAJK&}ܿ 9NZAH"(+eEs= \pcΗdOPhL`}.-xL8 >nЪ.CYh9f\gƥJ8GW/6,Y_i Q=w qn"b(vBRJ%/;{fe z5114KJ,|X'/dJht&S(T3[y{qYܽ*-Uۿ w6Y]5*%$X7;]g]SLhP:+%ܖWǾ/YJG2_oFfeF6u K8<._ڿėr_cYX#u#\[#gx%dɑ$}eIz#(`H0SYGj-Ӥܗ( 2Ԋd \q}ɕp3zzrz~.\ڿr[..<@FVt#6 ^hLU#jOEWe d0O2i '˫1wxK?Hi"\riN95:vHA:-c/j[G^=b?r˗.\r_Rt< G=`8AY+,k(eDx*0RsqR4r LUgӯk!wwrYR Xgr_Tt3ٷA+7%\rnq zURjQ Y3 fs58xB2ynbq,K譞By"3c! 4j^>df~9uw'O4#+Q șGM=D7G# ~ TK1f51'ǗIkjGR_T2њuG* pSW;apJ []"ʅ |qvwW1叇2w {)|3\;}'r|'Ac“_]h~t]c8|5o@#05L>}r5v%A3A3QiZ委,* (5| :n SPA)csl;`ޑ{ve<~?ihO~s;+|62 M~d[@WWTCSd;<CQ;8QlwY{Y)z?̿8efzR3zeܝ̲rm?)l7^f%˩MNzŝ9\ZoOT^%K%x*WJ20JΛ/t)X. Ī՗=4U2cOW*j=>w$ BM#&%m`:j_L}Diq-c$N^~MA}/K5 Xsg ZT pr>3 1V[}#J yq7@6&^ékKB w ՟([4F1vj 5a]+DeW/lVz33 Ut^e,#)istj<"xTRxB,-U\gE+o1R o;8}w3*qJDR`-u]iGZYĉg#B,0+\ztcF"dlUߤvJoFZE^~Ͽˇ$}&/3y. t~UdyV{76zsn-$"+=%N0p/Z9;lZx^SH`f1 ,i14&EXBl4:g脁EcE]ݲDAMyg3=1)ؾ9{nR*=t?WÿO=*+Cg!F\$9K_PкXv`F8)~c8z̲/YKv,K3s:ZGj e_` aǏ 4`(XgkK3TCi79vppdRCX^0ʘL9nSfd삼ӊ%ҺmW}R\r(f@*8_B+{aWrbWx=}JΝ]ljȼ\UW<7<ؙw҄ `+QUWPrۖ \kaS MxO~5]b<@y'6[rqN V6fN|z*]om2څ ^Q3{^v'K,@ v\Tq]B*OO((]5*gl&fji8!9W%&_x]`sZVj4<\.;et1mg;샬Qt.A){\N=?c1? 'y2rkptJuG1JYԖ^yq#ԴyR"z'QILi;TW0NGA.p²cq۬=35k0WPNcW{`:x״+"1T `Uu,)pKVDV <%/FT2~W)?ܯq)?ԯoU򇿕&<$J֋eF,[<۔6K!n,L~{v^&CBPOhHail|QYV6N͌J \ʔ|V!}&fKNX&VʅSٵQBu0J2l+vRΓ5}&zM񨺇" TI kz gX/&HBb*6@D8αEgíYcfJݷ5g{Bú~P*zAO?2'-՛ǫ0UYk3/P<<,1sS{W>Bt},Sۣ^ -.w 8a37As*DRqiǐXz( v\ k0O#(0#mSrBaCe:qk;^0O>Ĥbewb0q;"w1kĦ5+P]cbg>}5NʻO|hik͓e~7^L3>fyI|2)1x0]DfڕXlNJ_Ĺe\sxb3dh[ɔ][x6`<5X׷xb&ǔxeK+FmZmq?1= '2vl/X54];FC>9̦(wC7ѩ<G.ca66xrQ'7tr6p5J!73 3-u̯a^jqiO8ג{أ1 Jmu7C!嚂 F<~t#! H~H^Ǻ̹BY2c-k|DwOh0T0\2*<Pes4x$ ."ZD92QU*-ķrc LЁv< P &LՈp\Bŕsy^u-Jf;{Y h^C^]QQ,&g:v&F(hq.{UEVZ4=b(}ED9[%콗-U^{gn馯Qx,{qO:yΌt&LeN < NIjbWu h-dW޲ ̭XhtS/o1xa80N '#:'`?;)L@e o2&?9ҥJ5"kMi3W~,]:ށzŋ<~ti\/eU`Z}JsZW>a @~`5uoց]4-r9"(љa:MUGn;x|=SnzW !i`YnPhHc^ff`ҠvJ8F[,.W.*flI넄u*ø%]N g+r}>" yާQS CW/92Èo%ģxL >VVC@)95a?ܝi;Oxz\)g6'+.=tS6/izmUS:ԹaFm՝J-BALQG2Un-;qxҪ7yR*-vD/\<' Jx1.?EQE|&פ{P!Nۜ/(|KHmo(f8_OPd1^q^;>tuPk?;oHQ PP 3yN#!ƋTD(3Y~EҔQ)89gܴO {,STa,_a3j~'/s5[BԶx _z ϤP[,(/vR:sP4l#/2h?gw sp rtGۊ?b-̸ChU |SClKWc<'^Ap# bxuƙFf Ad_)4qrF C4&-'a2#t6YgC&B?h)(o󛜟Spf7 \I%)慔 joR1ǩ{K 72`+L͑Kc0lkjg}!@G>3P`5@Z~6E)A^TI^6yvK&rJD&&b42T[{v0x nN߼ϙv_GK6yL]9ZԷRVeB`^(᫝'w@]>K+i癆>-YE)6|j# }@wQSך&v.&3awy~&^1̚PA0@>h*{N"JfҞ*>;V|?U,OQ5_iysif5$!}9~ߒC2h 6 [-uY33|bU|B;= ZPv&qfw%XWMA0`JZR)k WSJUz@GA]&A( J=&r/cFA3X 6B0\YGԋInvӲbt'06w%mSUT%> "|[C0Kde*,vwDlWdD!1Lsᨒɤkiyi$6[n@׬51=>Q' l<f &x9Beٯ/2h1΁NJ0Q<.H$r13MRhDمA*ySsJ7E?6;o?6;o?6Ί \I 4oT,tC/yaq- 1';oh H$LP:yԹcr#35y_}7}x^T,^U:^re38`KiLcqla((p^d;p[-IgVcIKFwrΐN 8w)WYާ9*\n[g5mu{Vҽ2 b[9Sك^$)ٍU:O`vYhPguFJ(| ĸUіhE 5e5l2eǧ49=ez6n(3KGWR?Kj9}:nU3<0E]VaL\n{p&S'QAn&E~f @Kʙ K#Hy&:ӴFu$|Fs. ؖIwEp:Gc ϷY&vEK:tOhB bV;Nw}ӽǓ<cY Jgf*(p85hm9EP*0:#*a "{/HUy|^jϵ DIA.M/Q 4=c=L9Sk˓(%!hC"|OM|km{7lb /y1Ӑ}Lka}!LB<=XHx3;;) MGS{!ꞗ>eJbz+umgDޢՖTLx.x.Sǹ7b[\5Jwg9#?ܼ$Wtzi{Kte=ܯ@ʧIn-0eY X<] %6sE^Wf̌"}_԰Q#TU5Q4 lʰ=hsbYd(6gܖJ,o,25T[g(+7w{J*yɧii׻7KZg_0ͼ}bwL r@8K"qa_~bp``WLlL(f+XC2ˮu]'>ۡ6i\]eϧA]DVtM9zc}PaSvZNcAz~b&@`zE#[^ebv'b .Y[jʩq#6(/e>ok(8k11j?ËseJ޿+p-SNF(w}%C k"ڲvl x fׂK;d#p6N7pmf=t?&Rc"Z%lVP*w_crnMh~̨ ٕLw>з f1%T~nIkM'7.QQ9#!3ۃ 8t}eH=ӷx؏(YG)#-4qܔu2LҽR4Ki5z S5'<_\0ȋ>nTlc8%dNj +'{JzJ%6F'\{1ǣGN&tNK͸̿N;χG=z}F͹™(5M \ض9o,0%ҥ*L]J+ D`_IXT55& Ak0 j4!&sNJ^ y?{Mg ז;Kسmovyv?y]cw~o柋'q~7ww~O䟓o?  /z{wkAd_1X]p\=Fit[Ɉ(JV2+sT}/A'mи[yuǬO?U;iŌu 9sZHt4iS#Yhgf=-cckPhHTxoPxnS,3&.bRQQQ_S*Τ|Ar V1 d%x%[.]3J| n*|C='ᖏ]g !5RFԜTi ),m^E&H'fpQMf3I?ϙd$)*i)| ٟ帼Oj <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]Em<<<<<<<<<<<<<3M6D %ŕYӍ<<<<<<<<<<($~x41vOYQU0s<<<<<<<<<0$Y=Aq m PC2s<<<<<<<<<5Oq8S|Dp0K&<<<<<<<<<-VaU-~ZuqTC0!A<<<<<<<<<uAqPAuR$2$& <<<<<<<<{DAC(q0p 08<<<<<<<jm*&*8qYa<<<<<+SpM  0sOUg<<,!<<@#pt 0Q@t(<Պ(]a+l GLq=`P@}2$K<`NUu_ 6|\pDVT0*BYɿ Essrگ}.N},QxgNTq -pA J?jgUʔѦjVjTb x[hð!@ AKEBaR܌r奥˗j;GT~&J*1!P 0AQa`pqѱ?,V D"%dEnw3.i]4ڕzA-㕥z%m+Vݵ\5ږ+U󂍑WwQj7o]|~‱rsvBʬLZ0안aDZ3HSMjQ{E|J㨙p`Xjpaeʽ .x } -{@~mtiZI7[ޢY#X?"Ⓖt=v@6-\1GF 5Z}T/3s 䶫+,!1AQaq 0`@P?qD3 4y_7 `#Nq'@%Į1QgǬf;]#e@x((O8`=2@n@wѐxoup24 Xx .I*y[ϣ5{hWK/' q+h-zL?+{9"@/L&9m\@Xh:SzSk ^ۃ?v(Y Su|cdm؇N wH): ۋ:߼a$|j ?@iC|DFYš񌋀V-u &C9;h sP,;mk(='e_f O?ǀ 6Cv-'gIM\JfDPvLNPmָRh>w~ 1DIˀЁR<"m-J=~!->^0, KL܃Ўo2K \-*w^}8OgYрiwߩp[*Q@Kv~nEzaMH ;i&rAgh(pM ѩne6ڑ8rz _Eoz`;i+qbM:<1"H˥y.u>U7挊:?Npo+8=RL8)Cq%*9qbj`MWK0@r xFzbE==޲Cb OX׾4|9*?3 w.ijHC2-]2"xVW/A?_50<{q9^pCG'O:~Wcm{rAJC~uScZY$ш{PdXDZ+H0xeN;a\I|QYUy&}4/?YQa3Cֻ<PɭkW?`"|'?Y]iW' xLK2a#Z&6|eCX|fT+րD$:'ҽ5pTH@qB=1,Tغ/x9s߆@]c,ʩdZa5ߍ佷bȪ|Q dlR_я>0-A\gͅ /aG@b)]b::eB5E٬reWdxIXfn2f D! $etaT*wAbG}>~㡤bBl;A&/}Eim>LGokMxޣL(E*Up[є,f} t Ba>抹qvS6 ;ݩ0vxO=R#>NK||IѕW(=5 :#4󼳣d:s?FڐOǗx@JMwb5b5N5*-ͨ 5ki0Q5Y&2UkgX @_m[M~v j}!aqlZZl ?#tCnjem0#W^ 09qD|Ry9$ T H^Ie UK> XdPqTsb"cCv%n~Gbo}Xrw嫙Y]&Ӂƿ xlApX4*v뙇@B濇rpQ徱"xC2Fԝ߆9|P?'$%q0U Sf\7%r $~VN$L |z/%wж0~wn@v ([XA?E5B)mGYQD)߬?E{>qėg49h I-/CΠ\ob [9{xEiTY㔃w/?y #>*`A~!4t 9pGyQw1/x,`Et.1ۣZ2-ux{16uNNY_܂}w #^240^=\e,)&ۀCY93XD; 4TŶs+77&Pro&/ų׃\LºhI_jD&P_,.U KiWuu&5(Tg6όӳ"8bC *rnQf/ gZ,s\P5I5s7{7R$tvM z\bd'Ã]?]5av<70ϻSzl/غ ]rV4 ֟o84zˋ[2x79Nnh_|t Ex5*MCIba}3+Uvp4:I;H [=' >mT-i P:ǎT 4NG_Dm=*q{MM. Y,;ަ< G~Z ?Yttx:]@v@X7@N0+hf+y?pP_? PRhs 5A _Z`#GO* oPqu8oqMe0w% p֦x֌_}gHt~ٵ $`B0#Ͻ,B fGu$J]XX%(꯳ ` 4;={u\R<(mw7() e8KSd`&EX gB8MqDAږyf7Zڨ=USߟ;GW׼B&i!`ھ=WWt~uwƁFnb'M;'C{Tᓭe<+M4 DLE"2Ht8d9 #R8'8/4ts机;s{A|8cAl5׿Aܡ=A鑂)Mg89v`]eod }bw0x Q DkuΨR nVUխsަ\0&mb8k7%q՛Cr=͊Xǥ'uPZƨEcĎ"lhm:!p'v騢j3f,6`$ XNζr q:QwMw-0X5 m*gg%H5jtӤͩCx 2bp78 :do)&Z.Pvƥq2צ]BcJ5_ظe!Ev&ޜC kYn~0(7ID"9aسYb n!G9"0:rxЎ͝w/un."( TaaA F'ʳZʄ9M P˳M.ǜji>,E9BvXvSb{ӔZ8[ZZagƚ6S\#c@'c]v\^SG.@j'yԥPsO qޛr}󅹦VrXmsS +786Czֿ,g )a;C79K;z/tpyZUn<6 7#xİQrSBzcDX)'IaJtkټ/b0؉v<0Y T4 5!s Ţ}+1ۗ"Ȥå$inL-|y(C̎r6 N&&OX=ov &‚&L ΊyxM3_Tos O Jk9bc`]E<`@oW*"?%?ei#wp.t,G_FMZ%SS_4$*hq?&(_@E(CUB`Gv+^ZGs\EG$]y)ιɚjYx2a[bwo Eͩ*l;Q 4߬bCGg[z I]0 "g8y[!J0b>&dƂm_$mS çNO;rP" } 5L ]>тQQ(~pȑA-e>9x2F)kԏ"L`R%ʣӤ0J@ఇcJd![Kαࠖ Zq+5 _$8>+ XE2MpKP*E Dݢȯw=]9JPI8!w4|q\qWQ ׂUc.&*vNq_6EP 8wD=/syT&bd G[qș4Ӿ?bGM٣t=ZkaY(;6OyEPV0~ \+cF G]E-̤Gr8^S;)g PA )oݯ!v*p%E`syDX XC5zK(4|Rr;/;g"quJm)Aklxz1MxyD1 EsgbN,H{E?ؐc8*):R.(D6uVNkko FX€GhL!@&1h.AM׬F/iJ= s Ԕ$օN։5h×h5_K[g SD b}yP /mb|䥽CC&N  1}$QGwO!fF>)IAS8"AU/>x"W # Iظ@T;ix)_q))85@B5~2BV!ouu  <݈tr8lvMyg$< 9Dn$q2T#e9]'H|k~L0H-P3UއaA&Am&ҽ:X RL-^b3#ګ~s֚~ 5 JnP5tXr '@4uk04p _/?vs0tqo8$v8Ƹ__&Q8 a6kVmXeإ\2FkZH]ɳƓ$j CTdΠPDtظpJU(f {΄, X֕CH | 19@@9\x`>,ԅXl8CÉaNA5]#mx>seECn!׼M P?F n,88 7fRW-[[9ptwfoÔyW\P7@wžvȷEf+H~A`|-(l*)YQDHSj-y`$/l*u F(vS2ʸ#ٕ,KJ@&jJY!ۈ$N4N@(BxVAýᡄ4HS0@ZLxPLmJ? ~sb滛(Ka,-ĔQDjN|p耟mm GRPQc:5e6P\ B2q9(k9F׊iV )@nn4R$Kqȶ٣| B|%! +t1JI00BE !!*Jd<#Gu^0EvMu<~pok#p(sXZR>t9#,n [ M$?czCT&~WJ |"#cZ"Sx*vChVH040D,kqy\ S,D9C׺ ΀N#WQT ҂Q%;ջt6`:20H)Tp$':@ZGm7)/,ۣo7QFyg;kJvF!'1y;۲l֔ˠXPiGByчD X¡tzY֗cRؖHԏ 9=՟>qu"|fL-<#@~%AO\au Ǡ%>tڧn7' L^++DPQ"m95,f}r &v0@yt'XlQK-g`ۡo#xFB @ hiZnLJXz?g(pu_noKm*hFW|P KJsqI^m-؅xm!U%j!Ƌ鉦DߐxsĶ=x'5&ٖS\ 9`?<1'{1Y 6?.F>BޚG 9(T4!Ǽo^ŝ~+QCְKT2yK-!yi;V=nD*Ԥ8nX&/B^1Jy?'nbd$ k:+T#\.\Ƿ\O".B0h@p6_E ͧboTjXHeU^q": H[Q+gN7*EC'X-SnjƑ9YtTDG@GMFB^Qڍ%z Ev/~qw>ŏz˙Unɩ&-Pԉ$/'8]e&D~񃡵bECRRGg .ڶ~F#< +GJqf_)]_dMźSɯn'px1PwM]M#g*B򀫾AζP*Ar\k_1`MxMD+'FaN ("wPTF1SA%4ha5K[}bqDWظj#H>vIJ!yy'Zb ο1 )ozDnTOֶ̠BO?x""A*U3 )7?ˏ5x nN$qo4.MWDA^1Y%]zXZN8Fj|(%+[x{y}}(k)3Tq b&žP{ӌU)QZ:ذMuFB0a6CIdUMjoR0"XmΐXx.M8; h6n+N65):s\KG]ܘCbՈYtTwHz9 na,SXnX̥|bpk{q2,9ǬwCY)~ S|Iș].Fi])<HSlnGbLP"^ܸ"$By,5Vw.#%AW>&ZATw#y!W Zob_:L]YnbQH:u΢QJr*i*(]Z$6wzVoq /R郜kgnIy'n$BA(mE^jŁhNg_ ]2vs:}[Sh@j }z*^Uٝ8z, SQ̌NDf@a*6t|a))@y0FiiO(/b]|m?Y(C.~d{/O8 # x%5#0fi87J'(cG b0jnNJ5o,GuhHx >&k1T|qs5q](u58k_Nnp7Ku\NY&La5~  Ox#9W-*&0lQ5]k6RD]um9*.]q@8fv0 ,YXCzNVM)"scDPan"T]!.!tC8h \(ܷxq8 b@9< YXE4CY(g.1BNDs?#X8O"!g&niĀ1N@ ].<y{7D&ٿ8Xrޫ^=et@9'yQ c3X:.b>8F T=e1Q9oG#81ܿKQdAΰ"Dr9=V*s}`9j@='xu/hݽn1:h vhnGJ? .1k#)7uaIgx:6{֦ܻFm킲q?`=\`<9`.[1M׌:+FA'Zg:0h1zpymJ䂡 :7׺7',@`<?P?W?coi>GXfzC>rMͤ1FNOF;_YqAp|wصJʼn|E4HK90g3fPŬhMǁ-oG$ ۗ-s312r87YIrXዼbg_9.peove7U|VZJp's#1!8) ckR Ȝ9T;oX4{=bUt",Vp{ͨ.A1pV]B4ʡ=djœoS$y:CńQG=*=!/Od(͜0psє˗y8V],ˌhpYg.qLvcr\>3 H+BoXboX!]`kfG#׀.Lq(~8kaN1!apH|bMRuv]u< ˏa}Ia9 = jL)b-ΧY]}7`ߐB_^ZMg~V4 b0;OD+e9pwve8.\«\w/,ndc%~Ǩ6v8\AgfXi)'#DbLHwOM1fdy# |=S,=o%@)wwqn#suXDwn._s.JL '*q<PodG ?yJovgasbb#Cw.\^r. WWWX(y {aDO\&f} 4pD\„;s^vu5?:r0ޜ߿j7n;3e?Lrc h#TS7&OWkgcjќ?" wz ӟr5I:+ $Tl#pfn?_xppw=axp}7?޹L\yzaݸ:=dǹh;3^\7XAypm;v) A%yeӷLcXsB[%הm{7 ~Ç~N|cڜx|`k'˛8nC_X8/ϋ`$'wۏ T a3%h_4.\3[, X`rO1L}˗._rƥj9j +t']҃SZ󉵊 IGˈQ7b`xc"#. } eisxOfMs6{gN󎮎UF> @pȕsόs|ݲ or9"hӜ)0Oܯ&~qDs._3/W;vT1#מ0=]qS93M|UW唫ÈذŤxxZ0TO:rV}f?C*Sx?!Xs wN-QJe 3{q|$bB>@y:.(H@ ƽ0ӕv͙009˂G{E|^p4s^a9 ."a(na-[S76 tfa瓿?\W1@Nl%]}cx1.<;!q6'77ÎŤ/MObZ+gjǾ$&f(M6=#E1VW'qdWX"yLt'vQ?> Oi:́},A"^.03ioޕBD;<_8Ec͸*Gt}`QKwz`p7<B^oTcFv߬S#6k^4lu/E(S]Z~``"L]⢷8{0Jq_7GEͮ?9\,w%uGd ONY~fn?;&lFz#*9OG&L o7Ɂ'rd;#: `}y}W8|JüE}WNfϬh|^nMhl^8!MKz1TEN .8bxxp8D?ɧW# (f*pȻAӏ 9›Ŕm^1Np澱v(*\\\#.GL8d9LO?ɉ8f\0XO+}brFl] .%℄_M^F-z;eƈAۗCFriǜlK4Yγ.p>v*.O&FD_z y( 0 )Hb^O"7g[@PnEWā: σ.Xu|Fo @a|k^}MQppPkZnkj ָǼt [sWq|~ bhQx~<vpbby#0 4x. <8+5[Ȱ,ѓtlC}{Zg!DMӇ9l])9x@f_+9h0;,s9`_X E CbI皏+0 f98Q9hEE +`Oډ8օ9CX[{xƒM\H0ONWbo4揌'Ow bx^p Khb.&w{?0D|E ͛-)}^6d(/Gw8mpp7e$>7#Ék}D˷w1q)UrixQCbcCm(SH,WmAOO\Giu0GogrǏ> L@6TjMcP.&܀hK= jn 4:#vpPJp%MnL{D@LzlSW.$NpgNcvX<6ӯб>j xss@dܼe5]$8KM ׭dowkOG6 _y`U`yh %czـ+U 3 1mNJssbuQ$})xpm0V'2!WF>s::i9Ż<{:6 3_ÑFtxͥ hS 7_K׼&@s@%W2 Á2?R]8rw7CHQCW#am_kk(!64Xr9Fp6O0HFH5 zT 9Tb,V$ i[:p-m jsxjy,-AN A6 RѾkkmNL1He-/8ҧ%9`a G*mOYppA'f;O})LVs<'O?,0jd!/x$?fLys  I=K9z|US~9 r[#T޽/6iLAqr/F60GMS,ZN/_f"r8m>yb7_wl?3-*1u\"Nn(T}FLh|~8dGt6e/AA S!^"PlT8Dʠk{}\.1QУʞN}_׌UZ{cNwmA&8|0|ݓ˿ DLto}bƬ1907 .$ZdZlW/Q;6N 3R@*@hP 1Gpćox C`mL*| ~85`pm/'@SfpCO0Lk#Dq' Mjtz"B% <8;`.|;L?>GS9t]q9`f4SS\KA:"Vc?SA)YsnhPhyz&S0 @΅miº1Ìw~S Ÿ|MTlj#HqE`teV^+Xh74h68ŧ~1EQ4DC<4=gll޵B/ 3LjÉebhۂml20P{\&3Y(!م_wocpk6#EnƏih_X^^#L+N^^o͎^'= ý 4iC|{QeP%d'1 1 m?fiU/. F<ռ\7j~M 9e^._CZUxtip>A#b ,گ@mBS.K 5hWk"E6g Ì )0pKÆt< _k*NwћمO}Ν`/胿3ُ-):״0͏ x9 Cuuξq:T5uh$SL%B=Y㗌,<9@i .73\&_DL2]?`\_ =o._95F.a8>#0l51>\Q5: W6ON6~0@uy |GG;~dt[g]Ud1/QQH}u9d7v42̀~l!3nҹ 7WkyM^-EjPmY8F]uSVl",{wWt>:˾K0zpÇ4-8pzrF/;t>#nj`N@hIہmv)'E#*5SW\ ,?񎜺M~N?vK8{`@B$^]y[ʹ.]|5@q10G¬p1m&mG ΌsN(18`Cc(=_퉺+2IÁmP]@zš=(\DP Lx#F`'ڊ^WxPK+횄#FH( i|١)J[{r&҈"pq1l8x¤i! C6J~Gyo_&ELtDSn o.(JhM82 ׮g`6I@ww)M2fMqe4pF5*r!BejK hPD+im =&%UjTKh$u + -4-GBTvrK哦lՃsin<~1^vpK@D7ǣT;2OnsLX9C rψmz e"4ÅsBڛ}p1r/ v3bpMcX!p-v_i|f^ea#qfu8a\%ۑ5cx{lWʯO1::6O?" LB X)nZ>pE;Pf~ơ,zsmrN_8إ &B)٦pA{N{g"'(rV,%xyjmr|>|#q|f?\|U> pVvydXfs9~fme @b?Owpjgx%(߬B悯t<Lcp.MriǙ\\hws4u_ɓ!J[1HZ`ģC]=Y53MJZ>U#]3`Y"e>S]ӻ aMcE~XۍVS]' a%%$mYf\AXD\/M@~0M#)b9rIzy;4<~8&ZܼpYW쫿G_ mFH~ $0'p+\EUyaQ{і帳kS6ўfm"iw 6JIΦLnygFB$q+&ȟ5qU =گ?~̙ٞ7kBr~mn!/aj\ xĩPppǥ~s)/b}e !'T|gU7h'W@r<_'B~j :q"ab[9x RfcG:g(AW^f8(^S;L;,FZ3}dM:'/C#0W8ˇm@!~,h[Q]aS5Nu휰/*#fNRӚ, >p.)#9_/o4XW Qz {=mkٿ?=iVh7+DpYtO7"9'fH2Tt4i`H@"NrLk+tupaǼbN9QyQЫ&r-? AN R~4 "믥ϯ@&= ~m ֏XrPX{K8Ʌ@X{W)Ik|`Yp@/;Q8 'DхP#cL.?>Ko5T,bړ/|2v{B͂{ί>I=8&?P k$8N]> 3qJM 9울S BggM[Æ)URBώD'hc Y1~Ld#0~'(.]pۆ5EQ QLoaܖ2KF L=ԲՔ|ف^^#',~0\K@=|p`qkp5DZX !Ό7Bv}a) R8qơAroFj9V.V׬aӥ]b;pVyœl*.8.A v|<n-1O+F05vNMR#t\)0wD' K`VA$Blu @ҚL&\%,s xP }%sL~X(!ƀ c~LRI{ۼ *h8ˮ|dऽ䃸7yE 7^?b';Bש,x+8/xߒs2Dȍi1]0 ۷XP6D.=P L3ZY14]WqʵXmqk+%8ƥ~sXNx^.3h7f.[Z^Mr`_^&c/;XzgzY[BM˃x>MT$))3XeNYh ҾAg/"Sve؜bj7w :NُwnQ#l{sWDqzu4S|qhcVf?\4 by3(|/Y8uZ."-o,w/|/V c8!f:ۉ穴6P׀ą!$ huEeYkלgs x@<`O[/XjzL@cb'&'h c!XXMgaT:;rCgC Cr('ȏKu-$n7l3qo3*v̸Uzq{'1dr822" =C#619.ο8;uߜ*pe/!xS,ɭrk+3LU?IdY &D? {*(HHo+mӇ]! k67nÊݸHdO>Ó.z8jM`!eO-q"k3~Wo2֞*aymQ igCU4=ŗW y]6̾}mƒ<b87 x_9Jrш9:Ri)R8c\y[ȸ.'?!Tc-U{AL"bwXߌzz dpw`<~WXg% (nWQ *#tRǤ,f'}/Կ8͹}.1 PPc|ßf:VrlOr-_!^NMIqc(ekeZn/ۃWOtKxr`Pbɼ.( 5_ 5 8۟2LȸT7#ŗ"\LMä&rA򌛷l:{DfSa)Hx~ U?R#d}9%,wE6D9Oj:fYd5bRpc7`QqCȍ7+HUZ& Hʰ[3YV/ADbwA|y--Q\/'Ǭ)7<Vp|syg۔tj㴻bd~_]yR?; W3^A[&h>r5rqK:p lE}8<8~S-0x _K"=4_OGYV~ŸX]js1ǖ93X!Y%7fjdq WC0/$ZZS{4j8ޠ~o>3x~0X{6ųH-5wCoN#~p /9-ӌ 4t*o Jy.O8L#w[%m@?$r2A8{b0 8N(:ߟi_'X:@G# ωi NI0"-q4 J2jc+,NUN\ya,ʞp`HWRlR n^ y]xAG;0pXx΍^՜8wFnE`8\e:scJ(S. {}nY ` ;[m1־3YK|g0sk˼gxK)6'X7}bDnxėf|G|8B|e};^S8kBA&J+L;qo%Ɇc33X9W_?OW|L4GϼXP&:l.al&\Ǖdo0I ᢹxu,A X;p[~p/Üpe;K:@A#?sxn ?Ǭ9ױ_8ϴCl)0_MQ sIg?0đ"Y&9lN` fMN&T M t&Tusc8aJ`O$w"yj/Sbz_ǃdLrs r~8&5 DwmPkٌI R לqz-ӳ-]e!~rE\eɾWgX"~򶋚!{~N٬󑶜F[Ṁg#m8C* : :/Y#$q}8#Gzb‰ u+Ry\|x'$6ϟx0. eIPrj\c}e&UADp8e5bCdDfpan5@:|(lWoU?+4G6G6p9znr8q+j]@$$M*bɇQP]emGWf)8cYT!w\J]9q1'J(PB ?Keڗ9q5'&ۧ_C )8#{KSUgLE58)g,֟lCs6i9D7x{xo `5\R6< fZ<%4޲ zǼ0g.-͆4hj-ouB{Ck(=5__ɉx}/O, tqkw8 34pOn3mo(.J I3Bׯ1:I ܺR1Umyˍ&rCzS 4F2f"1c; g24/gzk_Y^?zPJ0fM*J]8/W/x!xa-u^0bWffۉSG@ su{C(u Tz'b҂^kmz/(T:^0^[bΗdg?yg8-GӚ|>JqΌrJ@7~Eo|Mb9 x2^R!8eLB7rȖ8Rs̝9tGI+?_)CQ+S5-z}~s5`$R/ qxNWk|K U@6;:O lo|~xr G]b0RR7Cټj}w]]̆1 }:*(!D{[{(ֿ;3p(8wNtQ{FCO0,y_p/s0JθjUt@4SARRf܉}:*H0RFآ9f@¦7Jab."Wch *nEH>h;"6,~1G ؠrC6i5xqSiZ⊢x26^X9\dwc ?0 wHy΃066c]mnsfX~eU0?*e0t)\A⤃9<(' obE5]}3JGnsPoxTjkI9npx> @")rsM;}d`VKdG<$|<0n 8B4[$7ҭ'{a(qO1 A)~ i-]Spfm]ܥ :'M$yP1HTV;q)z샊)hD8iwJg)|,1އA|<>F7AnᧉNZ(lڳPA ށipmG?+҄'Ϲ*]Bhwu:p$^Ϛ9q;)/Eƽ+g q֮!# 0SIzG~SNZt a޲!Gٳ$3v׽SUS@ 9[>#Gz~rXb \P7?| W"6er 1mՁcbc^p>4yٝW]&(rS@?{3Oߌy~1ǍLɋ}_Xe2-Axs[`RtgjO.eFuI&+q6=aN˒0~z0-B;A}+Ww6,$JL," _PAy~3J}s߀} wYΈ" 3g't6*[7?pphk =bzO7 (E|&f=\ q6ŝ~S ǪGs|O_eJ/q_jT,W>}ɴ'8?_?x5>1o?HM?Sw9,X 4_ã[02 MYj,D]g„3:+%ǜ|?G#‚r`jЏ؁PhxWLT'qx`#r`C6qfE?f(1lxsXTRcOOɚȟrz_O@>㑎5/f`T|W7דP+>ۉO7.o%>-noE ?_I|2!?f4ng99bdoɗgEe2/ja9y 8K_|}%h},R+".4zcPgUկSqvQ믬E4r(܀pNG7vWQyS?A?w_1n?OЎT2HD'8|_a{{/mK?1>y2zi(9Zas+vp/F.{mj;º_'+9_ *~?$Jaj`}xbM]84c0)6<>*}b-PU?5 8_ß6BCG~n@LqFK8e槼$X:\h$ '8Ρ6p$J8a~ ?fIR~ [D\<~q+nɫas:/yˇ2Nx`@PXSD<=`1P?qdmr-0.12.3/doc/fig/d878uv.jpg000066400000000000000000005345661501654372000156470ustar00rootroot00000000000000JFIFCC  V!1AQ "aq2 #B3R$Cbr4s%S'Dc&(56EV J !1AQaq"23B#RbrC$c4DSs ?@@@@@@@@@@@@@@{ݏ t@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #}x@d                               :w8'?-SĎfF1A`[fydlPl(++xs% ֛6fU0XmM=[+'RMv}}tK =]lh1T{HfvӷJVn:xP7\2}KU(~k?GY~?ͧ#(% 99ŸǞP:Ѹ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@㩻dC@a? F(8٪:zw˿Qƪmr %қ8կfL{ ^CR?-Џn>P9!aow.bg5zi[NuM/;/WWfrd;㏈9T5M/+} xES9f_7B'dg܀\@?_o@u;a@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Yprwa?||n#H [D ϗWzJe4CcgjˮxDហeu)(xlgecw?4,d3[//myu7c%8'?yg/ ĸ8uWJjӝVC=wx W>02SjUZ]+^2dgl?NR:87C;h#S}_o@ph;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@D#OpO({Ohַ} cE㵱{z;|AG++*Oٞ;g. Tڛ&ka[2;ygYAaܓM$|- u>;q[R.Z__Ļ:Õu~Z?/d_8jC˃uNIχ&ݝMOfUPZ~9l;Q?@Ϙ4p@qhBO/ 8(@@@@@@@@@@@@@@@@@@@@@@@@@@@@@]=xqɯ42qY: 8;sY sjphQ.ukA]) |u?O+zO׏y_]uKYu? Qt92r+$q.n991ˌܒ?w4>/3Ͷ?M'?=͟hK Tyw\HұxR;d 1V)G}uX׌]R_h,Ӓ7ú.uhy=HTMo/PT:ѭ`;vr7.TZ=eu*Sg>ؿqR.l=v[l 8uz <Ϭ>Ƞ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@nQrk{QBd n>_ ۪p{Y飝~}mS^pFEr_QkK!#wI;~JʢTx?ۙw %ueĢ]_,:ze?U=G}#iޝWT7+Zdgw_Tw^v9Mu6~A֜J#;} ?$>"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@pw֏yr >JE-?ÏOCuA~-s #?8M1zזHdONK*-CJ(wӝsZXowyRݣŋ8q^P?i5V ,PX:o\D;l}N+y_y;ES|NxKNjv; ŧv9q#ls<>"7@'Yr                             Z/aRn~s i5zrzI/vW6mԶtz.̇Î߼x[Ry4s 1#> sU "/Ӷ/Kn^JA=S0  y`.?k <}ߟ,m+`:.(JO,}Вꪅm *\#oN=;ϒNuuF ^[|o<Ϭ>Ƞ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@oz f1h>v?<˵\"e什8qTQe=cii^ 'x@-6_CJTpןJT&ݯm-~A}!tNju/qUeᛖKJU[ϧMMc}r Ţ1djs^lkڧ'\/#p<֜wgȠ;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@oz\l3gTP84ۑy+{2RGjyνȻ?g(=+$nߡK.iw<1jyXBS?k%cw?P]}`0u @so(2y}şX}@w                              :_~`{\6/Wo lmNxW6iUҞp5zc~]W"Ed]$G ,$m7ܯ•C|ݘ؟/n>))yfkPևϨug .kzsX?uy˿{t~De.lQSy_5WWj(~Ѹt~jd=F^w?@so(2y}şX}@w                              :@|yh6R{_-c\_]27öO߹nk]WۮyA*sd)ȕ-ɚj g1x}/VóKh~#WB1#l=vs{vYu?!/F Y<Ʃ|˄JwOyY03 Ȃ>@rg67{?0=                             ~$K>^E{HSшi\ʏnry\ۯ6!iò[x|| _;jf3m$~rsӓn?$01%                              rNmF2߭^mg`*}N8>Ej=/e Zu>'M`ݽ;@؏_SRx{J񿙍_~ȶhF\p|ńkOr:9wnD8杝,D [, _y#j<>&\>oN@sgj                              %Cz|9p_]*e7yng|3KMJͺ_׵!.GtC׻GNr-Y B5{^7Ϳ9^b s9-f^}ܟ_UNqJo>Pq?듣u57=(g#lh                             ?# !gk;"}BΒl4DI?X_ PlOȇ7|:%rY%Vñ!g}js|ی~g~p-h ޗ$yg KzǧF{TҳM_#lo|s)Ƽ7#h''lYںIKG 񀾛> i7> |9C^ XZᖜ? zrm5ݺgƳ9{YŢX߲_zV0tz i)'O<@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@t絹gꀇ9@yG fLۓ*Gj>k@Sc9ngO~ vSnuz}sB9*xg$7#?gaS>>\L6[-T!uB`ῡ\VoYkקZf0KIn};dx  7, 0w|+WW3w :NO fy䩠t;c*$e]\w&Mg k t;pNQ_vsK#i \vi/WO(DDx.ˑڗVZA(p2226⼵0+J˗|U McgC[#݂\ӝr&PןKf{M*Z;iӟ3/0{K8COl΢GI땏Kn&u%*Qܥ7dʬ>-ސ@;EU3uO%_o;dVz|$3DTl{<@                              <2s@@kpz|WP!41c'iـ;n=-WCֺvYᣤtX[! Z,mf7/<~icmrw^ڨMV%53zhi sJ PJ9]79MZr#95=Dw`5l%'hER[=ol;E g?Sh_C]Ŋ\7e;Ozg|Ý*[<|:Mֲs]*t)4K=݈0t?wwUQ:$ Ӷ:m                             $';!2}hպÿ@Kfla c|g[mnwvwkܐku3su\Qs ⊹R<^o>@^ga: 2Ol~Omd;zwt#K%WyyxCXme_Ry mAq2& ➝᝾|ԹU.ynjZN#y52>>.jY8xl)e7q^;6M<7x3J6"z8Pb <+\@$NTyix,u/fB͠ۍ3]2K1UqrUfh ng?|LsVlzV=s!ƶ|z\K55S$7˃v8Ǩݥ% (n'][v_gϏD9@GƸe|.*Z]rlro̴ qZylinrIGuNզa8ãn*$5A=>UfA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@盨7oTO}?[ƕJ5oO"ZagN3?"jސ-*o'V6Y6ΞO)㦧.9jOkg8%բӝ൰x(tcnB&S:j_"╺G榀lGm>[l;-qvYxD%%8_%]ԓSZ_n\~L͏e)O3rJ6 owQ𬬞$ kܚ6E7>< t%)2_D,gS:RSV{8Fz|TؐqN<^Kw                             3dDys?K%B@v[BK>NiJw9}es<)&k<~/ZOty_Hܜzo߁6Wnu<46 {y}WcR_nKĸȗriG.5xE{nJ,ȑ m^zc=|d [y9Sg}qU7?.qxLwp1Sg8,'=?ݭ뗏3ZԸ20˖wOSuxL9'? ;ݛ~9~-32[K 7N^;}uno__C q5>e mgm3X;w*ʮۢ=pOJz6fC*8Nb1188<,6kˡM38wB-;vkwt;2v#TqM9j̺V_ip݈v>⪞#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@I~ tw~4!Q{)MuH>}S}`p6]QBM%D|mUOwp==ϟ9O1]ip7.xKwdoJSSFGTOS3<бY"-qa|N.M D(rDmܟ*"+,Of>)p6r09ap$8z{~]~<*^IUN-kXiJTҷxG\ X v.-9M?E̠Mgc~Gw<˰@rgo4@@@@@@@@@@@@@@@@@@@@@@@@@@@@@z~z].iǽ~4<$>"p2GF28ŚIܭ<_zNkkDR[.<_y;;samlORuv+s>L[9M-R6Č;ﶞPi\9߹C Kݼ|tm`O՞gpGW\/̇VM'HV@#ߥ,5@y|nؖ\/c\m՛-QLe1<2OX oO4s}e9Y o^#ѐZwgŏ kd1Tk ύ 6/gogNUU;'em kn7RߥggC} )a`s~cBj^raJw W:8{e2ns|P.ޱޝ ~3;;!/ ӌ߻F?ꈰv=O`7@~                             Iz6UIoA{faGJɦ7ﯩz>?wg;Ǧ82>F>r?h                             #~? !pܗ>#@Z7?8壅 O;y)ug8N=duTWLwoCRiO_\51veҹkW&ܯ>=- L1=z\vf5OS˚ha=Q{,@vc62Iv|BoȾMG?z.Z}Czo|                             u4s?ހ8Iݱh}-5ɿk%3-%@hw[{[/y3}c̳A#8quN$]IxIφ]q{ /_*EghcW[Zr;͎ezF4fx$~#%4 K]NhS #RʨG!4Iþ-kvSIns\4^HyѼ*t*xsxv_>c!LZ˾ѾF>RWJ8=8Y\1;6Y~|"jMIM #/ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@$m ٍOۺD4gڈhK *RƴGK=uoeKywoDX5M>LZ^x} $^ߏ=u%\hTJ,tV1q)=/k"ٻ]9_+|;LJ/XkknydTU-`܆z?kӓ,ٖd&P/P2#|++kqX/u]̥~|Jв+ZܖB>Gȫ<ǧy)ʓiA OGmz/H                              :]rՁI2=[fƾ#4TZ@ǖ͑|sT:QkK~,MGJ#&V]0Y$n{T禚/׺뛞M}iu,Mv ,ol8ϖޠ|[q՞KN.-p5aUN^чfLzey.7>[nm~^I_YN4B8ga0V~7=;lS>NӦQ 0Dz]Ʃ|ϼmEDhhs`oxzv                              {g$3q>H |i3P["{YW_(֜zުjDŽ6VSt[Ig̛rynS}>i& 3j_HoNfl5jm%f写" n?/ؼNN}r -} Tɏ1,~`'=CW+\<%*3/"zۤoivdug=8{F5y4Lt>odXncϷdU};t^,?{dpǣs="(                             p=]@4g矒X?ڏ}$~5`[}aqlas~9n\ SݡC{>r0[sIv= UF.v|힚#^"E'\4 6czNKwާnj_8ȶj\IX_ ;?{. oD1#gT'8)ߴ FEղQJzL,M;زI†OE.g/\FgիfC-tϠ稏w5W}t^wtt|vخON|@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ e㕞dc?,Pڲִ\ѺHJ|zA>\rv ew嫷_,Sۿׯzt4%)+ K"gS*t0Iow-:w$,>f*&껶c4`Ge;qIcO榭94;dRni%{<F{hv}zz}zu5K mk˅{~G`~Q2U @;*;E)~՟]{>妿O߻b-psM.kϚƫ>?NˆQ:ڦ3Su9x`hV>'JLuL6-3R%sqkvh㷪Tw7B3o#lMm _K<(JeoԴTVp٦G^KN].nf濌N̂RWQ' 3Ŝ;-\KIӥG37j?k)hgexH908utycFUMJ͹?MR;nVڹE' >{26nl_v0J#w)-?y[M8yAMK2T L?fD1|y)W||~Kt_Vs7ZrF6*g@=uc6ߞQ{ĭƞVTG1kmݓw)K~w1]/'on.y+Yk[ω7SMO%v,D6݃_=~ ͗yIf㉍`kX{*+z3ظ@v                             >+ogW뚚Kg8XR}}*MYxwHZp<?쪗~?4kF}@@@@@@@@@@@@@@@@@@@@@@@@@@@@Hic؀r65ãv;# Wis'] /.{gONCMQdcg USYYQnH][)gJ}OG;Bxʚ'KP_ZݒڃW6;k8=SMܻ3]  Rӻ7-e!J?h9-sZ@n~jbO'%sQJ5N{3C!ao.!~Ԓ['pFVYl+)p2 ǃ:?9\UJI5BݥO9erYDSeL{R_⟕zx?Qs: H$;wEMS|򿎞W׭ܩʘaLD=ǽAv6uF]}8˾qr6 Sw\xd``{[ޥjEٴ PTSRGO޾ ojwؿkScef6C;^YWyg/R`>C@~                            8z?uӯ~YU?X֦N1_rbکrr{E}#HnKl±}|SPy/N~^Tͩe~*M|җ\Xr;y-2m˖徙JB ߍpe)n ؠmU޿ǘǰӷ jm)WQg<⚂Y`mSOqsJ۹7χ]e==jf5t 6~$-UK˟~#vTA$FjO";;zK#lIMCTq,ݞ.Q,Fs0dĴr~wW@XNG^Ic9@v                             $gpp}~@78zy]2ӜFAަmbɫ[^.]?#[|fNݯ3cs%z=]VQ_HDYOi(m>6^/`lq.cHh38di:!L~Lg9o],Lʖ<Ty$l۾W~iەE==4r[`O˘l﷿؂=m|\%hu<$ϗ<n:>O.F'.^1c3<۽S&M;k8av@l[<ӷaZN_gnl~f:g9@8gWޢ>^HHtQT}%0n' zz~?$@@@@@@@@@@@@@@@@@@@@@@@@@@@@ =?_z⪕l/sos}so,'J\ά #Ād! [[ Ɖ_$]"^ڪN~kũgV1DS#[Yw25 bQܗif"os<ە-x/֙ N:&z^>vCڔIZJFHZd 'bG)xԔODdaS >v{oQѫ6V/Q:G.ts / ;l{_fU_NgN6y<9]PpDžvC@wv\>xkLA|C?B?@y3XVb#d=/teۑ=w=(Į}.IMJeZk}. }V۷}Skd`jj %!-7VvN[ՖaTЈP0_6W)C6hZljrSKN~vag햇6v%UJg'vNfʏ[&'a@q>K!D#T7X>u?r@uZrvdsǓpxǖ>                            &D>AK~Kim\~XkxgK2u:2ܳ;EJWhDtTogJ9~>eO,Zzu>8>odM4FwSTSSqs4dԴ390q%lOurhbMOE{[9ȇ,!XfsPN{zﺱyztLsT[2c?ovk'53ϼ-{S>x/g}é ꦾk)& a$/18}ߍ!Q%.OV3>-o9=^,֒U,͉%K:}y}OeF8A#m+k/]$"Hqǩs5kz#|PIguN 68g@zic ./ =!;|rN7(=m@8@@@@@@@@@@@@@@@@@@@@@@@@@@CO[@i'q忒&씑2pf%zʒ}ycv)Dǯ~0fm8:{ǦvˑVc~ǷsOٟZS:o˴hEwʛ4OFjM(85ødCo$<8xVIDg<'5Ѿ&1H\rH=O8ۿ[2Pޑe7(BώrH$dFI g9gӶTS)%*~i _[2m4-Ę={zgoSSYYwM&-ƺW` H@sIk_?JT|{0>7M?9'aZR:(_3n I`uݰ(q&)3c/f"[ÌKu߾ٟI[[<8hNǼ{I1cSm赧μc`G~\~g@p4z@ycԹ}!N1\l~P}{ #@;;@ra                           ?3xm>RN#'ETTғOrA^cJw򸜍G6bfCypʫ{y$\bDyKl&u=lsE/[,j5/5`lQ9ؘ|PFs1ݡ* 5on+XZ#]nCKO~Cykm},dSɼHGXc=?$Y.ܼ} =!ҹNސ}?g~wwBxdmxѾR} wþYA_F#s˝g^~2UPeiz6yM ͑@`0ދ?%L%[7S㯧tg1t954;$ c7sجGgRY>͒.޳}oN\H|ϯVޡv@xex"S:!7WKrO,d9#`w#s\6|㞱oelgr;@@@@@@@@@@@@@@@@@@@@@@@@@@R>)v\cz="ޝB*{9RW[5[T*ij*ju].Ws'y\diW(K#l1韵ia~?SWm7\ޞ}g֕438fKCr}!P%- l}q;~\QDJۮ"R?9#HS,MNgb>ϳ১(< E3v^ZȩsӒߏy翬u6ܵ&w޹$ag??y+kIc{g;!R۾3#q*XO3׵{fiZg>xSc[_yxBHsid/c lg1xzv=ۜc-1 <6y~!PwF,2R$~??/TcoWO.oDcv@rdc؀mldTF9r޸9S>RX{y ?P@@@@@@@@@@@@@@@@@@@@@@@@@{7 5yS̳H祐svp M2^3ZSo~~gwY=,x|}:NPvm*UDdN#YuQJS`>wsV緗ޤ({Z܊i6o9Wuv9=ϗ~+ߛy;?*;<~$Cm# iϙ}Xmӄ}*J jxbB3O.+NYing'✁q ;'qD@^TUm9H+ -8y#K[;~roԃU^!Z|^׆ ga\=>"TdF0Ǵ8"3خۅ'{~! 'pMzP{8Mtog8U {Mcyԕp_s.-aúZr{yzio%ßARu qr~ z๮L|~IQWG?ÿiUlחwCRe |?Y’])SZ׻w|Tjѽ5{BKӾq;~x~⺝1 gFq`@@@@@@@@@@@@@@@@@@@@@@@@@*tؔ%dc~-l$ o]{ϩm)Gi@8lvzOhRuoCb(C?CHᤣ3M5zһE{5v$vf}>*S~R.XUO'8 ŨԗYȐZ~+uItN*SSжPTKyY'lmvI^oNb/ rd+EUO {CI v7 t'4~̘[cu- $F9cht^Q>gt:-uT[*- cCeMJmJPE[Bɸyռ^gT3TgM|p[Cg x "}5"spFr{ٿQXi}t *NOe8.URp=tŧ doGeF*ĥESfQRq9pך249m;v/n|߾ %{ ]5*Ou:q3E4_WKm|c#m܀F{b`Go :'zv폂@@@@@@@@@@@@@@@@@@@@@@@@85үbDw4'i6>`?OKyss>.H;g';~RDB搏.߯?D9EbW.p:k%A{:~P]>E*weν߻oW8WhZV%Ʋ~ 9g{XMn[eGY,x껴NKL!̅w_?,(8nSr\>[]MYq1eCc[N!5sݵ]^#\I!:ǎ2YNt zYz泻zX*&"/|52Gڇwn p'5] 06,Ljjz>{UQqoz*c䆼т}zÆ0׿DM]=]ci:NJ}]jMUT_$s31n% IkӶ=g0Na>Msy9۱fYy ] 8oz< s̷M]~ݫuSdl1M .r̗*z*TW=1vۅYZ[FE-,;yMqB2J%w:c5~#ګx;q}9v]#GOpq 5:9bETZR3ZkmubW)9cEDڸ--3xqu4Lt} KzZnԯ6Ѷ'D*7ڇ򫼧%Z6Z[ͭ/.<ףc*q_Bn'%Oj *eڢHU|UvlAcc9n=Y[KI̹QG&|U_|3Ֆۥ5 U(u,9dk߇ٕTUW%á2UF&:ͼK->A_%YqVMD4Ĵ{8;{}>=׿3nmឧVS$m ƌ0;X\}U}&c @p+KB卵SO[(9I~G\|D󋃴Ok^8wkNH]SZŸw|2g8{AȪNLeq!97} Òd~]ȏ@@@@@@@@@@@@@@@@@@@@@@@*tؐ:ok5:}Y/fp|)YfrS./>={M$Kyw=GuQp˒-Gkmq~b2؜I07'* G0?5oBp>lLIi|?KFRI_[1K)d{kH q݃wUe כ"M'+|,L wƖ?Oީ9MT*c̰t2$dXW{wIOUUM)7ikz[% {|] ;q*Zh[>qg|׶z[n'.syb'|g2*Y$/0{{c*;)/.ՠS~6\yG{sA]b˛El/sn1@u]T [9e q+g۟ԜNXekIF~;?b]J[.t$53ȹJv=N0LpkZjZKvQiwǟ4wTfoy1~5iu7 ] 'Ӯigڙx^'C\:}FqV6Me~3| <6swSq:i}VHMTf $EQF3n@DrF Fv9S- ^6Nnv6XCoLY?,qYs)>fAݘ6?]IWt_?Dz-hTRK^3|y8㿳*nqOk{DMC?-M =?/6&vnjvk`l|8h5m0f:c6]5ULފ߃~+^v}&.a1jʪJȂW֚:KC=>@;,_0?nűcTI4]>7.(hz.6{+;g?_" a#v @Xpe x* 1xsߪWҗMM+OO{KN+ߤV|Qf\KIvA=ڎSIͮybl[RuNUEvqIH@ i%Cs\s;U |on_HȨkUxN} gDZ>oSjg69/6]dcoz@@@@@@@@@@@@@@@@@@@@@@@C]*$3N=%x?z>wFFJVY܅5ag\VNk{ܤN}qV,YhkZ#U+ \ZHpRү..7.X0ۼ-w\2K;6hZV[],s ":P={>BS%>/cއ2~߱$\o,R\.T8s N;5U:Jz,QF_;hd[ϘX* 􎞤6G^.[z,Π䧣UJQgQ<,:[MYp߹|3y,7#wqCNt帔=eW˷>7K~_Rйs[«<]qM=Ine?jX9#{K[}o=r{_踚Do(n5k軓GiCga8 gNV5WŌU5fn4@kۀHp/#haR=ZH-ϦMţq6gwIKzXO$@lc@`FNGlTa5Kӷ>wnozS>FC8{v:1P[N!cCd9$w;c ][9p˂mws?amsYpN3~v5׺Fk~n6y2mu7]Wo8YkT/YMxTFB1A'o|zOYדY.yϻR}g=KqԖn9oHS.%TLϧd[ʝ5eq{6K"I-|7㤜)qjwCk5x>:$\*}o3WsWmsÎij}gwJ[/d,M dccj15=inW4ýq4 Nkƫ)y5O=H۷en&Ǽρ{vau^} Q yZG$&j:\aa%m?g-;UUaͫ[7Q硓 wۛgʁ3ϫ}>0jڽ^ls^hsww}&N8ߦmHo򮾗##s [S^%[<26?\WZ\0Ip{nNNUt(5tCmo|G6Ι+2_:pE/W(RΎ v>hc2I,5$Vϴ`bDBr~'Uc+urYO~'}cin5PbC[Či/M;Uu;R7m9.P!Ϯ{'CXIDr1"G]ư<ۆF\v|ЦӇ}u>~++:tz#̻m4-oR;6C]U*[sr=~Gm! /{s?BĩU^ˇ3N2ᴝOKD.Ho K.nQE񗾆*?Uǧ~=UWWÎ ˜?BR~n|ƮJS^5^.u['Z4nAy?.Gde韭2m=}/WFە=UytͩatZrNF2Ovz{Θz5.7q#Хsb>$r                       UWOs w£wyO z$q'}~jFY,g+^TǮswO?ę0q?RQS^eGKeȱh8~\n.3k*cNf\і|T让*jV||ԗ4%]xGZ6kx[㨝Ml'ssOW gݼ#қo5ᖼ=| [ͧ]=ξ $4AG`qTTB$IBĦ*D<5k9+澧ySh;ƌ}U+ k:gc#M)¦rK9|JN޴5n!B(d]Kwxџr`%~JWJXr=ߌx|Ge|r-eJ\"W-4K-v{s:q&N|%;;=iYGkßOC Rޟv))m5p hwK$s]U+[I`2IbQMiVh]<|cV9K2Z勘N!r ƮKO4֝Ufwve=}/MR,m[#Xr:o?Okữo%_ԧz&Y]yU^-4^nk ;  6Ie+EWRοI*\O3 |MWqkunc۱ũwz%Ev>R v41eg=8e zTunǿ iӞ.ٿW:PxuS3uXRKZ7iM=;qNsn3Pt &4}Фߵ'nk1JX[QzA|' ^xK죁TrӞ} l;As7;qG C *iϯ~PUĭm:Drp*5-zroꧦdru>>ss=>˕=J;J`hk8~;OޒVQ6;N4ܛ &c Ӟ𪪎)jO'2E.2z? \(TQOXYc^ZK}28_-ٿU>u*)m|,Gʛ8wF7Vr5U5MqE17}?gJTpNW4^$qUG$ġ-;-f|s^z{_sf>"^glmVM)vk -O"$^dgཙЀ ;X6Ϫ                       t#>U{3Lnn}̢tR֖?qWNꏙ<<*Rz ?=956}M؞Zኀ9ǣv'cUSptqǣ-qÃ|?&|!1[V(&3LOk3.C`-˪pi7=N%Zp~E{Υc֓41:vf1=b=^|ckӒkW^>>̯$\/Zfpмb=Q#I3ָn 9(OrS\.851RiqG3 |DvS~ϐ$.9IKZzfOI8UU9Dje iӉ0244:ׇzK.T*bgK]adU> I>|q{sWw[va`mL4 Mffk3n3:t4ng|<|G1DޑIE4'/ħׯ̟2t:dgOYsw^tDa]qS:zj?o^'BƠ[dMs#79k}zObfhTi9W୬-8>wmw~o<ㆷw+W5zqeO4;@C }-k><3:j«C G̑1wccWR̨֩vZG}OiS\z,[Zg.PǺ=v[<$T-7/gloKn B 5ttחfՒ2sXTԩī\Rh8-w䶂 U1L@.o\'>vz[L]o>ŜQ}=GWߞMN#|Sxߞ t>xTC| խC2:pC[)^㡝_f -S;)[U~;yM:)d㼹3 & =-4>:8m~P ֞^jfdN>=T kzF}ŃRSsxjMңE1KAo?p$7ՖM<MU1gLJNLb>^RTygjs>2                       ?#/Vk"¯b=깆:d';m~忳\/ϿFov"`u%u4\&H)MԢ3-(aN߯O, (0ۘU}3}䴰׿s>kOt sz&!`s>_`ERBqh5Ft]v~˷N5u.珹C.]cfCs Z8g|`yz˜U4 r=֢HWo[ak''eJqS{EnF_, n6K\j inRUBzݴ8u-mE\Q^\5}cjfji0AKk{3GsF~o[kiRj~[=؊5OuY49ּ|w`RҮVvo% F;;m; ^x[8O]k,UkѝP@?ZW7Qwa7+N~\O_O)[MMS%-?p*oW:^uЕ;\q|Ǝ+I5FIK51lͮ|;{! _e_?oU^pyx[wI땕؝5*_V(q/0~~4k<޾B1̏?̓s*#ׇ;*oݹ[ϊ=>˨Y[qISDZGV2~#_Mv- '70r-9ҔdRG3] d-}hjr/Sityk̀1c"t2btruCI981uԕSͽm+7>5TKvgj; F6.y ̀sd׋j9ْϤǰrMU!}3 `{m;_<[+.2ԭ3"2n2gHWulS?fH[VO:{Gl~lyq9oI=                       vs`폂fr-5US_~| Gu^]AE{gӚk\e"xUsş'5cHZtr7Jvn\2֒MMTˌg=qVrÆhSy(bweq}__َ[Tn%>[I(j=%cwP~~y\E(UӜ5MOqalZLÖZzñӱPDV5n]b^Pzkt>4|Jd7":?ɊZ }{%ir}|9~[Hq!T~uEj>5{{KoP7w[X-Hݽ2Ky"V;:z:y9ֵH`\w$oб70p7hn)^-2E;%WzIWۃ: D~IIoUCC畸ߗU4Cm?~x硯{93ms|%;r>ԒY~33W+zq^!_ Vv#{ŒەsM=j}aQ[P)OD}Ls 9fjJk՛1SkTw'=]۝wFxT k0ǵb~*IķT|z4{?'G-Ak8˷+nм@ӳNWb q`#~i'jT*UQîv+CY]kKT!P-; s9.Ѝ(Ԧ#8˵jUWT9zI_.q{Kw킽i:.yȡ4a&;D'oi =i6osD! |;FƇgu?5ٙTTh8ӗeFsígYijX+kϖvC'QCAn3۲]>F'5~S\mPwDUmBn,q'ogXyg~q*iټV}x7?jO\X_6\6SniI9h?XؕhBs~,Ԯ0_K7(`njH2o뵃ڥ 1ϾOUL:H`vTq#!8%`4g.>CVm߆S]_ϱS:uN:Zf-'R688u÷4Ua7KFW_>zOoA߭_{tB} |g0B1K~^J]Q~َ3iKnՕVydI3˅C6"B62`grUJq])'Et KnQGnꦞ 6#Y;UqvyطsEqOz/LjZk;lR[#xjiq-~;UTm8τ#&೉MM|-%L{^iu/kDq#ϷAY3qkrOF]/rDF]26{>|Hc :{_3:b=9KD2x yhϞv7ckyUҮYO~baOv/[5  z 4 ]n5MJ=aػ=:I#)[߶Wh[bq~z,}kڢXZkv (euFEӺw?>8. lH^b~CZQOYlچޫjx+Z9nI'? ,] T҇Ln -{⾩G -vv#oGU#{`eMRPSp4,2]#+U_ǭ?8a疝 FE_ "S޻[^zyru^}Ai[\{ͣ.ZS院?K:[]ٍxs([3Cזv ʶM{|inP%4_YFk|-u{pq<F& Ktc;vklt:slQ1ʗ|YOk<|߁]^)(m \cg:l>^]_53'VMfǽ4仓N7^[ iiԖΓW GbkN0mܥ^^9Olyu|ew\sH6iIJMD579 lVݦDk. nS׶9:$⡯4jZr۝2+t;q2YG\o}G%_c5UFj$-ilgI;n. m+tSI^.HVB}cp,WDT2HdwUUj5'=9drN,hys @s/S&EP%oԳ]mq%Y?叵xDwຕLB醎8^'.7goy}g<MYOYoˋ=pn3,@@@@@@@@@@@@@@@@@@@@@@@~%u^XxUSXuM^:l?otF~>h]5':/ {߷Et:iyl8vSÉiiʟĕ mL\^86OQv6|a)m<o~]Ku3x|D⎩Էke3n7lWMf7:V1(n/=U|r(Kr˷Qk n58u2{b[TqtUç r-aujwAߤo_De%$*y'M܂1~K] ek?_qûZ >t2Vpx{DrSB؃K@92{~=ty-K FCrϯ5SC[0i-Û %_0!)%y{s(>/9(k{G,\̲]c޺=1ICjWҐ,[dʓWVNZwKlDuSDM42K뻒凙,i$ՑZ},T4i7$y(pƪo~5ח^Nzq)RY!s)FږB5%=抚ZcfӃ dc}3kMwNm꾶3UkdǪuEPmc^e.P>FDmgx` ۈӲD%,sO=#n/ `ٰۣ]4殢?UCݢsT%+dc &8 H:NsUꪕ$dyO_FW}SǧGd=kqv78wbtwJ}.㣼xIo)}'8o> d$d$\_ q'%xXKTiYq(bU.Ww̬@VpCeLM ?[mQ<a]oy+n`xYͷ_tO:8JSjpU.w^o._MrCۭbs@DIϗ޼[s# P^}u} +j,v BeeKJTϟᓙަ*rFx5ZڇŇ=[7hle5e01#'$~w6n'џQ6SV줟2Ug95569׹1b6=+6q[ݩn8w:jGVsM䲞YS]oU=J/τ{r$csNeJ'P@@@@@@@@@@@@@@@@@@@@@@@;{wP~G5-x*QOH|u5L#I?%{bTS\Ÿ _`#t h.;꘸*O);po(_ٚ{e-jQji6CؖއЯZVluM5C!mV E qk?k46,A:1D]CFǵ%яE5U/Q >(kIW 9d3z^]%e[iz;-/.q.8>K6WE$ jICxLTi-'nUVܟZ #|ZUc*ǮdY6 c% c=ha;yn3袩J&'R}z^Ӝ;!EꕞhzTe 5TM Otϩ?5=+rJN!u:+Ih =w8̗ Q[oTCRp 55XCN4('Mt?\ct5/g j56X"s-G,g42) a;c J4%Jvf3O'bUon/|/ЙOMڍmˏW:Ȯ0Y||MmE$bRY c?+gϻQi`ҷW 4Y?Gng5 EM}|um94݄{;zJkwo_[q| W,~z[8WN*~Vzwp|ŽG*i"%B_#CabiѺǥ5ǖee=eHyguAW'1y3n_}LK867euQ%/\Z &Je ik|8|moa\S<^w;ƪuz[gr^6\}ب5zwKo}Y̲A.r|ǡ>*L>>f.0^O#~e@P0;㣶| [^]NjJwN}3CJsn~E#+C=S\׻2i=}ܜa~SN !zy{y;꩷dsb`>xnN?M*uy.:w]:y>O!n.3>C+-8GJJ)ŮvVpiEQCQ_W,ZXˇy ֟\}`Ve3]5f֓+ǏWog0:ʖNiKAVLl35^óRNJxjG? rp񫪙o+xeRLW?fw6zKJmy)m2n:ZZ(ceิ4uw8m gTG7[ˉ8S}lӟ\'?0=sHijEG,gﮉ卭/~#nw Us*pj soX ƭV+קoØM Bpb}NܦUR,hGNf\ԩzKɃTq<ߤE˺_KrZjawun~MlniQM9:qlnGp<Q_:[m׷L\9t<[ZqftVSJN%s:d`KORմIfzy{;\5mYyG[POPacHȨӈ)7i]a֮ z Yj[Y &3kpv v*jv~jwsG 5 K~ W ǯQu5 ;{u/x[!F6I8$? Okn_WkIefgKCoj^pcWk:sxe9R1wKNۉ}v {Fky)VUK/-֖4u&(aczJX"kX]nWz'aZK~Lk[++\V䡹Sqk_Gq2p6R=n׼êcusyM>0zw=7=%-jvӬrԏ~+W8϶ZzFs>I5?${wz {1䣯s ZV^;N{Y%j+c^c˩W!v|Rg|eOGہ|q{[m?_A[uUY ڀMd' vZj!|&χpy7dB>M-q3Niw;\QS()j(kYk BRݪ5n| 4n:_z[EX9} +j_Y&tKTK,rW.~Y-s-mkA0|וl-ϛ; ć{֙I_=L.3ig-du2Qi#H#^CN:g 8TN)ȹnqOZ[Wkk%`QW=U0&6usN [vZ8(7ӜW^M)>Qgϙ8Yp0'is}vn$?,LZo>s:X7hg' Z-*'VriV&ۂ%-˕rY|Ib,Ms Ou̫l{`=899Humzm_+ث:ӯw%n_W6SiKUKb̐79n:w |zs.wՌ:驥68KU nL1.g^ϟCs=ܸAҶmdy8-:v EH>4llx4Uӄ"1zw9NJ|g/;GhpnpM>'ͣQ]35Is˚GӴWҞ :Q;5 fE4;^mghTYxstoAIPH 9]QNzF.׍KjH< f6n׷nByY.e¡ڣ#C<-$8 ~3PRpP^ˮD.p7rHܢkp{ïRK|k$ܯ |IJz۟x͕\xw j*_5wxnVHBV#.n1{a|ч59ʫ{ŪSVixۧ^pQjmp{ v>@{z/,<ߟ_=Q[!EF]$Oy߭hnzψ~L}A9!L۪eut= tP$N|>)wCtTkKڍ?go8spIw5W=eG킹/vi%2dt3=4Ld7~5;+F/sC̬vz)omiIM\'$v 'N:y>?bEW7rr_7u1>Oħ5 EE|mEº@YJڊ f).p`P:װfo9w==Oy,NXu5fwvSimtQxiW<0 'y%"T̻[e]g.^QRV[& h; ۯ Y!3/P`la˺WT9i¦x~2^/ >s9B[~&7;+ u$ KX$t^ Cb9y];EztrIL&w+y7C 7kN5m ]Ylt\(CsH: EN;8׾c/g'oKyKnQa\WU>]jZXp%HJ;8$j/}.ÆcqsfIK}Rs=eNAiéo21}2JK}4DܗJ%#Cs񪳋De'pN\Χjg9K_ ﶺ:ZI%-3]&Gd7feyKyZ3Ҭg%Y(Q~H}u>>:~qnգ[{\ = դLƉ_.b%{ɥ /6mX[{V^$rOŎʻetNjWcN#oTBܥ'Qa SmLNik[|y'Ymɪ.ӹ:fE!pY*-vS~4v_U%O~QUF@S1^Qb %jrw\OFpQl5ŧ.Bo2vurpZ?M\*#2265vbǯh1\{dVĦ*t r0<obqkKr3Pm8O+ujV~Y_M DZM3)sLû?Kų~^w[UTO]|uu8`4%6zN3Selˬ*pۿ7}}rUiU׺8t0I@;|vȾ%4UR燏%ǩ:Om nxr|V2Hեy0T U4i@@@@@@@@@@@@@@@@@@@@@@C]*)s&N{q% :6bKzw~ ]i\"n u|}t֜6Gwjh>v^ 5zJJV<CD'U6җ|M?/[zra0=G.kK=+(Uڗwvcqs >Aa*6Jkqȗ(ڵ1C~izoj[u kJ [p;Z"{yQǯ-WÛ-n)=kJ#q2gs4vgKP昲̄9s)s Z qQb*8KNjU9a3ݙ5m4+R3,⊒9{Φn}lTQ4e?M-33bS e9{ێEyռrik[lV۝'T٧Z =d0«RUy(mXTO;^/Z9K.!hK%]N:g׺])& I7nskcqvw䟔d{Ҕ_,논sΗ6:v_A[KHAơY|Mlqd<$_OOX򹫯;w_iy᣿WRA# K -#~4Ъnn?k2r]_byjr?[w92[j׺)¾J l}oIUKSV\gU E6ľSf \7|sN#p]x2۽h}kK||],pT{wLUudz6SR= 'tn]I7t/jMs4?w:(L; J,/|ΩkeݽmqG:[u ^~ZiT[\Q9VL@6S\5 b/nh~s1ˏD.\i?Mihy/4UPUi(i>4 h!S9ak\QQêsRT?w[=y2\gӜrL.Z(+`m@gr Y=^I^胥W7IS&|JIg#%Ē|Т~9 u7'˙??JKq及I()EZUR52B쎣w_K}qK.=0in6oS-GĻni& jiggsSLց4LǾNo7IZ/Çݓf2yͲe:إ 5vL_4u¨T:2$klT.`{CUذ+:[Í헁:,ズ/28w*7?K햝R^Z;=&j(acb]+h[4.ь]~N񻅖 #?UGCHv[.|/9;_GLíej|ne-K.qҜ58lCq샏MV:jit$JjzࣩOv ZZ<-gbaoWY9@`VIEZjj`F㟖}f PG\*YƺGy} -$-?/uK]u2rib.NOnʃ{6Z{mSM).} lF?wRr.\ß6u'u٦ +6 eҟ>w'ꕽju8\t,f}VM8Y$e6գO^sx8m^b G~k:¢Z+DroN:.x?,]BF#3#1&Uh| sKA cOkHvb[VsRu%t~N,S5]X#ØI os;-^Eޖxl:u*_zʏ ?7Yōs"Rh=|C{PTdtpp- `YZңhORXѺBڶZ@~ j^yљU M._f|rMŎ[m!y\2КnMx_wGNa19 }Q潙VܸozX[joy>*7`h m* bq%G_I^Toxk= DJa:gϜ I8Cz74Z.0GÇs.i>S{kt-Zt9sˏkzmTlus~<<<{cNJgvMwSyGPiݣkz7Ah NS.a/~>KeK[*iHk?VIO AcZ1$iܩe$NZ8ߴcӜY)8 [!}-;l3:[eϟaҟy/^'4&"ߦV3Iuu;"gxbJ嘚Iqk@I;g?ꝅSm{eӚFٸ} &RӇo)?cN٫zBS]4lLMhoek+撇KBi|`ԍ w+^FdG?[>Y '1~ki;_[|5%:'[1MzA2ء 0`egˁQe g[$t\?)·r" &ЮeھW*-{.toOZb[9P[+%9:KpI\]VVSN%u_k̅]}8ѵmkTYjIc{"U5##s闏MM's/fc+6[/e| k_.:z^UUf`5Q[l,$$'06n[sS x3LG'5vԺ՚eQWo P_8:X"EB;xuGds2x5o⮱IMJxi(`L-nuUԕ_:xN]]ZLn79k_lU65c\XFcg$몤=2SKyZkDmgQ=]3A߿ݽlw8eyZnuJmѦyZM=Zà )4SP-4kI/tppOs}¯w<~Yźs/4K9.E5\Me4ŲSZop&=q? 98q*0W?Zr?                       叿ji'<Os4DGb'۲Ю^/N9vM_²ϝϕ6z]E}Ҏ[|}]-s;FoLvwN"^>XIӥ)Zn |eܼvAQssϗѤڼ[Kr3v.5]S[yM޽\t_qzS{fX8lLmG#i"\ߟ| s eߦMis-zmK..}ꬠh閪De!߲_Iᗚ"4% W^:[`mOQ N x9!'/+ĪW\00Ze>se?_Acښg?ŸMጒ_|۰Xڇ _[ΦJ+Ey8'ǭ2X0BCYFam.?ח^}Bchk2>[x5yi:[iTRPłd#gㅕwRj_/nE7>'VmvzJ 0 Yic:+U~5!ƍrxut@?RZܰmSx{{ʐ                      (t7[vH~G],UE^){_-^zdv ߷ o^u$^:x, zΰm|w0Zᵚe^jXb?Ṟ7;\n[J{EQ *WOj_);UI5~83&4 G}K%S!d,k_#$؞AZ*hI(ĥUUV^>~F<%'aln*\;~W#yEQÜތ'mOML!ķ^\Ek i623 u,=뮕M10Pjg&u\,kMp*85P:Ũ-o1fPSV=e$j{/)>/FxE>/,axZUU8*)2H߹{[|,:UXB}=5UiaFv]r~.^qqIG4eEC -{Ok5M*!ߍC8ASM{攖Y~'=L׍n:xe},U a#ƒ6#g8K;79$xFtӌwEJ.ì\d6ms39q 3 ~St:y4?DJ6 5=\W{ǂ馭Ҝ@3PGSpK;Rصnsg{.XTRzgm8~ z:(ewVRheJ1Ǯg俨vщ;Mxs7U^ɒ|>&%\[%iK/?/3*:bϏ]5vϲ p+iq~.-udf?O-z}vM7YzMWWʿ-i\wEAj^@،m쟢|JfgyO J"j7)].|Юe9~O>uӲ2R)/>th&yHg?>M:&Lq_m3?W*'J9)-^4Wu:g#|y,LjiJc',ŽEQ筸ǩ5,vw};u5879AG &jy9WGշf48ˑ1va;hCYUaq]o~>(:|q^zg^:.N6H!ǖs+&p%4FjN5v⡿NՆU=I˥3W[͖swW 8_zqSWmLJR3;\R<6(o=f;;>xU*+][WɻsS~~c?/E"Ui(9O                      zY܋/ Gpq.tzgo%E)l/3Pܿ9p4kqqne8d#>KJťT6~9dnnқӆwԷw *8sC40d:@'8X6W__yv,w>{vUCqye~Niۂ]#F=ђ??'SaBWW˅_CJtJgV9ϩv\X蜺$3D Xfs-6/B)}WOemރ+5U ~|p˳+m \-.Kq~սBl0j7PĩӊW}} j Q?md ݶwV~Ƀ^wei&Nf)]g-.fxWtO[eQlwK[g|Tlga،/niT 7RIGN&+sh}I=]7sSiNjy +ktkfGzPQ9$oI'6ѶBtzNDŽ'뿫^K?~՚q9ixG# 5`UNYӁ6?/KتhUJJ/xW?T~:)~-jN}bR%E %R܏wa!jCޗrCrm9':)'声.tz٪uZ)^~duӺ<Ɔpp;n~-;jJ_SZG7F?o t֒r}z'Qܪ5MX_IG)XUQ35o=qNPu]V+橷 RuFrۤl}k;%zz"=0;O^l\x^i3qLT"E VLmZtpUHV^ġԓi~/e7jۯoS|^W^&o夢,+{j%4>&q偵Z6tf6M50󙡁7p+Gm-Y,utKd$ Dl1'GV54&U5U4z}OSrFI ĖiD{CneG%@@@@@@@@@@@@@@@@@@@@@@@;{wP~%u^XxUQb}Ujf4Gcݢٛ;/Gz^} iklZ[K-׆։0Šw_5EU b7r\3vC+0ŚqiFSl8aq ݧ;թYGwˏ.m~rw--5wX,OL8Ի~&2Zϑ5+u]8^޾=P8 "|~XWE[6Qe):@3 ]%;C wb|SF2^*AS5hoO6QKV[yEKݣOHgO kzq+NՕq}fsߵFj-OxC6}xUD { ns#a+6qǟuRUSgSFJXmU|9c>FVrpJ购+j+3ۼ84wL'>.=gkn\K}NٷUiᮆJuր⎣ӌqCS^)yjfC kg'榇%͒ctuKx9fM;̗RԬyIx[Ğidpj}Qns#,ya{[IW6rͩ?xaU={SKUx5ϳ@XY˥[K_X12(#A2Fǧ+|*tғ|S[}QU/\9^Z8|qA];f k+d:٨h$_\elPTJ烗 c9kk |\[TSjm^n⶝iƫwd/N-u%ۢ=:ͤղ5MImsRJ$RG[)H?P  JէQr8qnZhr_8iÚ-uwoiGEQDG>)"㣩!@uwTUm;ЯΪcy=gwv?>f,0%hn^p"qCL%5Ǿ8HnTey֓iRj0iohQl\4q1G3G񇎜3$OX:ii4fS+Y]%lTF;Iۤ;|EQblVxQK74s_PSF̾њxph/j#~6$S\mk fZHkc `dW&/6:J EnU{+x_ނ5ڎ+\`nzBǽ`%P*{s[RwZiI7^rYY0hMVnKŅrϿy }1:JzF]1Kcq $ uWMT(oݼ]\%uհjw[Y~p/=>֚Qϣ UIJ5VLH:~{i7UU8$u6Iq gқeˁOQj;.܈+-6lGoA52S}.~ 8q>%u:\44p0R엤Kx~j}kƜuSbeԎ^/u UIv}E.ww2oi)j3$pXsOIaQEU%Lz\."d62M4~H-O:ìMtdp{V S~X?mkyIRp2WcI:RM=v[,uw[rAJy!Sr4W#Кg5RaYJjNIUeh/y)mxy^Q %|HєZ}2A(U5"CI!C+s陲&RP\=cπYR9#OV;VMÚoNSᗶw+;﯆F9ż[x % C;#dNCǖzġV^*]UQM9eoBnqSsLz(q9wYy-\|whĈY$PtbolM]Jr",ye%3K~qTv>|W_t 8upӷ'PV[-sQ j`!.-`{|4ĥ&IOĥ%8kK[Moed |ܖ*n?-?eb6 ~>V'&t^2=lʌ                       ս?c?fr]Qky׼_(IQVjƖSKSI$r>>XZ@+ջ5CTԒԼЭ\cwz_lmMh (y\>>\A2ݐOI&EsճP0>G%|7[SWåqfhv vvJLM;ruoh>"p~_6 Uk*kIns햨ScGg jU'9'WJm'οO{QNJ?T)I 1WK#C>!h!\Pnd⭚j9K;[rUOR7ی۟?cʙ wT{R9&p露&T[FqvWjXDuQmr ak;nvUj>W+gҶkU7-zWiC- ̧fEX.BEu<1GxH4psv몦*NUսsFKUYڐ{=&>h}ųӌ'r?Cp˔eKvdz7C_j5LE [jZflŸ4y(qM&N]F]|;=TU[IC<~-Ye :IS VRX%-d(4u0%UW-{kk%z͏o?jâ*P檾',Qtg xo,OX[G-\RMnnfIQLփ#=FcVDOeViZ8,djGn[5g%+c^]*v+C. $4푕{SHA{VѴ7kkOhM [g}| Ӛ+<70Ļc.qkW>Rhh&뎜F kC0 tD:Ϛ|]jy߻oc~̖ ץ8ki[㧑ݾMFpca1Vv|Wr}-ur@S9solޅ]ۨm^ǜptsˆ{GNTp^+"EJY/VU6ɯ䑥 w-_?Uά[kZ3>yxicu:^O-e{h\fCU%9hc}[mu:_c7jS[NYaW -%Y.Msit,lHA\OtջrlV@',.|jYTo={E 8\G09f`I[Ғ[qi]Ztyfe[`/_"uU.@:ݻS;;%-^xffEo{]- +WEűB4K|Sý/zf˟=8;MZzJedϼ|灏ת.8ЕRY'޾ǥT}\@@@@@@@@@@@@@@@@@@@@@@#w@~%u^XxURbΡ'9oA9c}kMUxpfߴnvuWJ*kN-P6I=Qꖯ%۬V\jypvo/{z 4f@1AF:pCw?גJCrw/شY v|YǘUVW(|ċ_|cÛ;m{+$CϊuYY3! ='` 5/8SۇpQtݨmwJd7Δ} c9=P2Nţ_EWKۨ_94iw25JLjjH9ixxsǖ\N̟WQR{M5)h60lVs-ƄQCMUIKAT~#pvh޿5aӉ)Y?>1ݣ-ޝ0mzEedzK*Z+ff.U-m8ō&=uT@~ ge{>;punwsk96TXԴoҖ.io;qFz_i5拳٨ kƘwzY+b?˰q_ӏ*JSlOvq*jM;u괎m? ʺji~We#X`y+8%[G~:Ȟk\$g/t" opޖi$5N[xaX?]gdKRWϕ˵S2+q>7\ n@MNeux*N_^yGJ%y>Q% -`l2=d?6kvއmIg8;:Oů|q m{5쩵s[9̌] ] HSjewIM2Hp95?OįӳӪqV.x3|E4QVn3*Xt7K4'CuOʒˌŶ\@۵uUE E YK-c ~$E@USUWURp՜;<+B¡ջq7 o)I ̲`~5c|zPnr{erz)nAʖKKeMmhu-,<3%h}>kMSOb6ys甛F.2WSt*3SnSv]3dzKy녖id142/NRtIi47Š.^MTunMIv>+gyӵ:mEW4TֻW=<"r:wjj975?$?k{cu*2slLUYҷ &<3R]-u5GW;zda.icIuh*UJZgjgŮCUˉmԓKfnbL7LmR:즤l|Lg!._TRQM.$uB]g}nғS)CŪ9sn.g[toDAH3a#?+pxj'(MZrQiӋR^]㗑5Gm~+VTҷR+/!SMnmOi99X m+Sm7G(ONQ'!|q*uS/L8}[rJv*%ץnܾS53﫜?J?|ufǹlSM}Kpм΍5r?K齕/GhSw,ʺ*2XګQCP?͐{]KJ"'ϻޅ SUS{͸A^(9uTp~K:iJ"svMnBز]";N"fjcUSN>q+ʝ>{,\UmBs6ֶN18=[i%ö񿙷rR#Ae|'C`tosAq^zeITngr[㮌zdiġ5ykgeOG.fv;(K$@@@@@@@@@@@@@@@@@@@@@@Ǥg=K՚2_ҙP4Ӏ]F8A1cjO%|#|=K\EJnWѶib$6cphUV]׵6ݦx|Pz/~Yu:j޼{w 㳉?qM_3嗭&Mׁ\x8Wo%#깁<֕G8sˇ-n}3Lh]nVk֧\hU%\0VFpw=iT[7^\{Wzk6}G]|{%#w1Վk6p|+YdqhtV ]|[]_i.č4ۧ ǟ[5TuMÿoJhjl̪\&ճURL<9ǷW4Iy[g^ 1)jjo4k"yIL꺀?|?2pu?vjN)K48i L5owi*b9 #`o/ōW~U~U<9fLfTqsVn:Q5bIeTAkZ%$`7cIK˲L:!(Kͭ/WmOa߲ہR1Rx>Җ^52Km{Ý#.P0GHtd,Mjop+vl:(xX{tSF+jS*^ʉuf\={xsGZk :͢4:N=jtE5d"#ǎItcr6*UU]CPXvNiфġK]JW)Z׾^pgzZۂ|8T1g۵%,3jUkf4\*o vHqTģR9ϯs3_Q]^ʏy+8È,Hi{Mh{ſttD<D7ڪަTߕPO; bxEN/Fy{">0φ|0hӠ-G]̏ŠV H$s݀[6&hn=2S{ϩ>u ҝVV\><& of#XqcFkh:^4jভ[s)zHYp;Zob<:NJRα,xbZSUf˺n|s{g 8wnsJ'T`ho4298R`6׿ETi+>.W_ejM[^-[5y N\k8 xh=u'OOFszC@«qvURN{x;ÆZ+\biO5ٛw'R E|DO06_,ïps~\S$.;y=Nyw& v?yWzg+w!^5}69ϗjg74^fξ[ʆ[k7껠V8 s}NJLze86SSjZϡhVj<2̻xR;!=D?XmSDZ8c(}]H90>=;첪wm"WJTUpGÇ*JWRJ1i37{ m.yǬFN;}qI.ќ;[x1þb>~ײ[qw׾GƲoFiCx |x}=ŝ`$ϗ՜7wy[e4~XZ'WP Ln0馚a_vxgYpK(lFJ|>1߹5ӕ1b@@@@@@@@@@@@@@@@@@@@@@{5{bWK)yqI E >DúȦI&nHy|My^<3KZ﷽|Gu?oۅ⺖Kzyfdg TTOW>|< `:[곜۲[;Hq nD5n(-s}" OH=>W ;u_[S̉E5[,˶q(nmwkuY]8 bf: e8 3CDoT+!Cӏ}oӜj+K"eL{<[S9n^\_dbŶs~xuշ}方J9vXn1^^F+pg=;R[{tN6+6:g(bCu-gxckIĴ g9vujD{w/"T(: }^*KSV |17'nߟ*Jqa[\{ɝA2TqHq^Z8tJ{֟w*3[[3C^cׇE[U]ZQCTuEjR:XI)6&NR9Y8UZVGXj-eGvH解:hO@*ىoLEI6]6Z>; g« Sn\x= y{٧u}җS"v垶&FP<1s#F1cM2W3a[ }rP χyĪT>Nܢ`x's^5˹!l\5z3qX썪?jg5@@@@@@@@@@@@@@@@@@@@@@@ zxۯy}"uDxBWjkڙPX2Լ$SNw_%gk{/=[O_|ϕ[<:׵-8:D1ND16߲3#iC#d{qǏ0O:yH%"wl{ou+S*''#-z&`􎖝WwnԮD"lj }@#5}F% RHMu*SZl;n yqNM8t-VN_VSmFZٮ|(e0i{:%aH.9lqeT=p]\uӽnc9E70D.X0%#4 Xt:NOH<\MUK 5xP{pȶS[1 p;Җkq#4m~?,yll:%o5dNnP.'e.l{]%.T>ܼk0xFH7.jSrNMs?F 蹥OG9꙼U]txFCtqL9}Ϛ]VQViƫ'3Mn}TJ4I܌:;V Kt_X_FGK$UݥRAEt|kFrî*USuaMKKrfJN]q?ۧ]+Zf89=>NZ/#}1E ^&ݫNHn170bCe?xŸ,?p8M:U5S.-K=Nd7T)Vjd;N6$ЖV[ytbEOf~ixè)LqJR oG - m75jVW4j')U4SN\7+zra*WMfK?wR^_X[][rif<80F6 Z h~e +||I"ĜJeڊ*ji?6M㨊x\×?UM(s O+"LZڙvo.yOc|0>,z(7wy1qN$Ԧgp#$ tEMETToIzjp~4o םK#XOUn^:^RZ9$V3׶:                      fUSR^Ả`[|ݝFvsteL90D?=߶k\jbguf y#whЯW ~FJҒ۬|LOJ@PV qF ?C\byKwKov2c6RR8e}jTRqoc'ad!ױW*lRji=y8 $+W%7h_i7SI*vtҿ.k~'ilTV2DΓkflVk-8n#]r=v7*ͽs3^Jw]^W㮺[QۡvgǟZknN~W]j&w\,JmSJoSǎ>>sWn=]#0σOGN5کü%R-n?EGŦwݾjm[p8) Zʐ\);| O_]ռȑo- DM_ot1Ü=HcKF1O4S[PJ^|_ԮJZ˞׹ERH{J|ei #'cra,9ąVu(.&5.PuNωU{M1RMw+m쮭[NC4|O:hMwUTER6J9]hn@_Eis4&I8Ji޹Lkrn.9\2Wy϶ 1Kj,:>WO{# c7 cT+N5;6_*J(Ou[zu7TM^U0)$6J|s!Bo<^&k]mGiKTrjQ+Ǽ66\sGk PO{зݦw/uSLT✢j6xUbU j\3D֎\WKY˦:E=j*VI+%+!h 뮊 )[t ê>"%׻pr!Qt|+VMMO˕ ac{H n.tYti5YӋNyT5Nk/N8P~^j%jizWx}Gݷ/)iMw堦*J\8G3 -q{gb}uo|0]RI{a[?=ޒ{?Pk:ت!IKGQ.p|uM-VRku6-MU: lgsu:p-Qdw8Zԩөq߹h'qV21l/S0w#m6^UKjn04oUzZ]N:fx.ݠx$;-~7&o<יU,Y"\)sOdnJFMϧqssJ\u}Lp%W IcGS0"ퟂ3ӯԎeK[F}3maUψ*hk %55J'd]$4 nT uC{{c-wݘ k! ێTm ZxN3ipϝz੩>|t(ӑ~e`6ߟ3rt@@@@@@@@@@@@@@@@@@@@@@*tاIs . pJ> 赜SRoŏ)I_y>7#7l|=XxκYc|NឤJjhˣ?wj_ƶ6q==M$b:@9%3Ħ,SRr}{Iݠ|OuKޙQk>賂;_ˎ-t%p;.lqҭ]+,᧢\!:>0k+ ãa uof?zpD-TUM~~{Qk!ӧ&t,騩2@nۑg8_GU*ynegodeOx~+᮲kǻ['tDꋴ8#'˨cT$Sg;]J] LJ>-9ۛdX1r~{ؾtCw.JZP3!˝8ͥOqhZqfk5u 1bFM I`xU; )>OEsxԧzmŚ}vw*|Si&jJaGm|uCLd;_al58t9RK't?)ŜLTw(?5ŧdu~[t2\h#=Bz)D?[Ql{ ŊhɸQ=V+įM7y7~(MopQUذq#3A&Eds?ܯQ£iŧT V_~ó7U)N4ki)g qF^au8w4YSEA4U<V7 Bk;͝lm)[;ˋh>1ɏOw7l۴G?qQ\5?B}K)qf{d˃I9XxSĮ+p%U$*T%JT4ic׹C[iDԛV5Vɭc~} d Ioj!n^׶*f42#Tˁ_Ʀ b5ӈbhchŢ=Fꦊg{kyTI,ht&8 lmqGUˤslva>,*hJɶ\~Ewc|ΛQ-.Gk.eC?f~Ai38bW_*uF [K'nbӺۄLJ2 u:|=LUS=%In*y ><_i+cǮEuҝm(u|,Oݮnb9XCTT&Φs1&1rnLWpKWFjJ |g|tw_UFljܥoSÏ,SR%^9?Uixk9Ԗ^X --AC`/u%%lsX6+xXPG x,_-cZ8(\)ޝ` 鑡]b~[9SǏvO[(8U?o\n5J- )y[HY~N?O˧n scNBw‹9k]d[u"ڲۡm^>P/׾MI&WS_E`qTkn Hp>6{tϺbz)VI 2-z&mmHqNOn|F?Ͻr*ewC $tS3= e`z駁N7RYK)H3NvoUv[=]-os+ꗏLC㩷.pJ=_Y_rRz?jg%@@@@@@@@@@@@@@@@@@@@@@@85үbDTU4sKP#&8COӂFs7S<׉ox<{zsWhJiDnMR{QwS7vz~+q?YFNg$<7/&S_7:܎o?g_OUJMw|.OyHW_?7X᧦/%3`c25Ďo煺TV2ߏFuO׉0qF=;#l(7\A3!4[n._Ovy{Ͻ y!czHq[XJN^nK31juU)*/.tW9)9͚8N2wrӁAcQn-o2rʘ.ҝ5 [t1xRm:jWws~-gSGOowݏ.qQuWWԠB&VE f5u7/[ 1Ovu7a@qq7ʏAC4T͔=Y1x2gMYt~qԐWs%_i; *\OiUQ4w3FȌ4=-8m8TsKz*RMe+M,% /~gKR60D#ik(sN|S-3ކI6 Zfu֖6H$-8=-{f[-U:kĦtw]7eljTͤ)?!.6+^ Ԗ:2*ykms$5N52}-lYz=o8+}%nxܧ9hL\GJݦhVtK-撖I7?gZr mi.| Y]\,V}AY]KM5ADd2^ތճ׽7^j\J)š^*6KSf(oJէ< SvxWz.h_+õ~u o{iž#zζ8h a5sE+EVuFnUF5TUBqa˫ySG̝뢖pÛ$WI¹:jd?&:'RPخ}UkepuuSj`¥XaiKߴpO jڽp;E[e8ּ VگVUa`ebui)ju0dsZ?ѩ%5]OJNf˖jѰVJV4ҥeJU5&b졯C7$qs[-QxH{.4Ksto OiMX%9fcK_[U-]=\kqGy3Ws~-Vl֧ZHjz*cT @i|uU]PK/luаp0T꫗U.3~<>넼_?9tA,۪WdYjkks#.M__\Կk8ک½7,5xU*v[ikk(\ѹ.sq#U+۽U.2*t&jUBCsSJwYmp#;?_iCwx<2'4sXV&އWo.r#|W5Ry&iIRC וNOK@xg|آM}o55/p^6TlWPkSds7u%et7HG[o=m~-Uǵ=1٦Wo߲c[|9yvo'˦Z9ɫ0%x17H!={cOWc;yOv.58pZxc݈N y'~d{*+%{sj4"X^`E1tӌ׿c}5T)$6Vd\u6P _ao|)驼&+$^9/OI u_,2v3G5op4ڳQ&/%tWi*fyiձtITGs3|z؝Z0>>k1rl KЀ                       qVk^;S@G >GWY~'IʶyeН=6ɩঅ׺+O#tI$&i:~Hk1!e7^}μ¡nJ7'kE.s4qWlAtM8ý5:3SM%ZǗ榭ռ2^؎F)p#}zævZǞs7*w˾_YCY6&tN>! |X?gujsQS\x9)j W*ԺYKv-wk:zGHqi94n /O1m'szxIn\TLMTx@voF@۷uUJx٫wßKUE}|{e6S_OÇ.1iKyӕއcxz/1)Ѣ~CB_}N #=vkg4or|;#wFny}.ӻDSǾlUq{Q8QRM0\)WE-]+#YQL1#'lNk%.qߙM8y8g*&M3eY@mԖGS3!6e ъ=U˳[֦Y-|]r^oMk`㞋^am-Ej@_iWcx͎M,m$7\;V#KҲT`XxuUR\Rifgzjsr_雮 ࣄ7c#^GZה&;i-U5;z/UO|YDEEDl 4x5{ ONKrmiVZWV )Kvzka s%yR{qC^^"oJ[3U?LHkd$dSA W13U>W¯g*sS<~zyB5Kpȕu넺Z(MpGJz*.aKDLyl *_]W=X-0K4ݭj$vX~vD tƚe,0S(,E1rwUN%[#7)[e{L$!UL\JUTt]2TʩBZUO&̜O(UQ~pdA^ʕE\?ka=O-5ME kk3Ҥc]bWC݄eQj݅g$x.T5\t3T*kkkditU޺omŮ.|fOǒZ|!9{ݾ$}O?>#ɹ;MM,#}`mŪUQ-O~ӡ?pgG_`Ik^> wmW%\̩l`=f=CٝCqp;m}ƶ;]Y cEIs` sqhO[Og&n R_L/Zu?-PԺ;iG\g̟qS_L8ni1ˑk($1wm_~Ȓ Oz{} -0 >(=?(+r>嗅:ՓWϚE#6j޶η_gk))42<ޖ5 =$gme m{VUͿN~zvE]("oC"Jhh[wP 㯁۵Jaleltg#._w^ݯGݺUTVۓUބ$+4VIp9                       fUSy45SJV+2\K0>'lhJ|$MV~x-z\~з?*xeuUK k5%D{;"7cش;䂾chǦ}W~Qg\LdiH-( td- 07hs-L۞mkO&WuaLldC}||m=/tҴܜIA#GOlleJbif3^?NKsv~x֦#VHڦo/{{rd ~ j&wsJmӎ~9cK];;|c~݁n3=f|iʏ%9xkUU\%uޔY k SNa\ªhub[uKnXJc%'x8nUWVgmQ-+g櫛{>w# rAJ&wlI_Ȃ72깡ShOI~,z]fAÎy*O^SVL ߆2xP0#0#m( . atM2\~ϛc+y!UW4&1m͍"w7NYPke%Z&]ݏ~CeT6u3G6ӺO{irF!NS4@@@@@@@@@@@@@@@@@@@@@@NP(G[|(ϧtTMNYknNvJsZ|JSSR.(Ec:+ۦO_QqjeQi[03:Z*%c)i!=6o b:q{D,JiTT̾F xaӵWQ5\Ona(Qzy)ݦ4Lcstm=qR;5=T.:꾲F 4,7 mzp~_g=8:a&[' *f#dq4z?g֥RbyϚRQ rt3?OgûSs<>'F?0{Znt*(kw,UzMXmSSMs4V#eC >5KGKKSǻES BO~KۑB̿~zFQ=AQZ,{GrZR3Y]Y  kʘ \I_KS~&,Iʈb.gb?pZJT?8ao8N"VniPfwՋ.oH>kvlZr. O>[탌L/+sN8˝,uklU F~v6;p-KR=/ LKU>ҵ.nܻЍW3\k듞qþ!НMpv=)C{6&όtSV{ӺǎPEц`WRV"zFjcVoi_r0 \)[gSfϛ;bM[F-WN9KV6U+5*a*H?G|ʿg0g#tqVo8| &G_>XIp~f{>'e .mi=? Q+ 1=xr u >x= Oh[hG]C-sRcV'EV+N.?$Vu_/xۙM#QI`{<ǥЯ<åí2%Q!*.oMS㯭pS{KJ46Xϓ~I1].ik_ˆq˯ _/i^ne|j?^it?f |ObҪL41S~ l,<*ZyaټpPp[|FiӔmJErL @Sz|ړvUJ?jiq/S[D]xfIrrߪasvMXK-C6Mgl~*Gk= ^|w9Zi4"!,GfL;ܪlHgߓbJLȷkh{*eiL٣;pWT:{? E b4#_4!8Ͼ6l~~*Jџ$rBxG ¿RcNK:d8/US6F\}u65$Y$0n$%.k˿F^\T\=4͖6JWw}8 ǟo_9                      ?#/U|jN(ntKM.qIq#ݨc$_^ _svUUSqt< Aܬ[O떟Z_X4 I#d'bXUT$,}'3Q/LYzTH8:&e,d -=۶?bJcU7Exru4ǖ0Nv­>z5Tn8_N3U:sJqoKnAH7Š܍=__?Kp46^K~U5uժ[+'OO6MJ睴X8eu߭|Fu-W*+{SҚ~]o gjeMU*^T\楻\*e:8ukؗ>'~4?j]tN}- 4st<;P#:Z=\OoޘnKu7˅2ݭxgx{V~>yp+p/:z\n.LӣkzOZiHjI7[qîm}PY_s2XK))rNZ|y1{aSTEX׉~gB9};y6Wp3Za;\B;x)[姯 Ψ4$sGR;l%6jSDOXS%qiUDBܦ4)Z.Jqu_y&ɯt_ (riuJ=+%`3i#䊞O BىF&jOŪjU:)u/弨Ji.\ R)oQdmNo$ {6%Jv k:sJeMr}9fOX2zʫkCæL:ψbEqA.sNk%pr~rB^ Q;oy:ONYCK WKNō85'eN55ҩ[wWޜkoϚІ*aX^.=]'%M-g9oVk}Ri%mz*'o34<Wlx:pk[vjҩ-6)7 4ʳISJj^MNlm9HRw6 o}=PTٵ8}YHuuQaaՅ^=X?ުn%4m^"J+ەM%U m哲i_VD]} VՄ11m*mn(ieӓ iUWNJOqdp>L\2Cq*UV[mC[Nd>/zp -K@a- lXUU;5NKw TCt)jK(\5k:htEj]ru-WІ"*Z\|sTWFkT{SԚM|dX:ʧkwv]g1N8%# -*͖uZ&wEGvm#imf΍zIna- ӊ*u=fQ4 jr݅}!i~U.YkukŞUWpѢ8ͩGEPͬζ(}nN3Q6:)M\nGr{!WĶ5_|jW[wT?I{e"*pvTS\L5u=#s9ʯ3v[ {ߺ⹋q]%5;$iyv̍k΀s203E*:Lώ~ݤ|= #)蟩?T'.^h[' qSžݭqCVRU`9-#XZUսO9wO=4JھhT{4]α>{Q71|Cu@3f}C@#c>k͛z-_=8tW˚ދnh2[O`_ksY`tugsNǶgX㖙Nr~JP0"q=7V jo[̂dnݙ*Z[o-NrlnӚvC=]Vji/?KsAe/܀tq Q$ss%c|lk7j|**zcz}gKj7] &Mχ|]j׿-#Ů-T(Bݬ;u0jO^F$Ns~ž<ݜ;Ӗ˅A2I$V7TݟC"4Z)"9-+̿ /x­'U(gE*ikzp"^k0ijTWL(W=U^4'8ԏg7꧎ڻWAk p^2\QZ:s1CF̽$Vu~NQ|(-ӂӕT. to'Ċl Jie+>Ꮍm߻ =)6|'=tm`u6Z1JuU4Bw{vn[JSjO)pI[jV6ayWn <Sz}WA.rH5Diț0Y=Zr?͏.4T~eTH%)#s{X湯UTj[WKƚU[o41Zw4p\/^_fW(^G9Xuu+ﯬ#~HuՌ˫ Uт(Kuږ_:$mznjݬI͹J@9|w()8ߦ `K38R]%)v)]:sɽU9{W˿6pgZ7:^>OAtK[ouĵtt< "71Gnֿ\e6L1ɹ$l>].jgWsne,:^z%2b/7CK̰-p=s}ӿR(Gv60/ fFÜiX56jQD_Z3;ZC`0Z{eak82\NYHZicLǘ{m)&҇=xu+7):duŎx v2;v~qu /(c.eecp\a2^:Xc npߟoE[nk?G%T֏7 =.0{SS  ymۜ=*3'­(IO =ocޘ04o7)K=Zpp?|UMCVRM<,KǕlO\2{sYr)Yqw<[zt} on\};yGeqѾ|(Je;~;s̊he|op 퓷ܼizfsק3ׇ]*Sjy.z`MUNV4}[;U ewhOê/:M=@%snjHbpwknދKX|,vy_e][˄C)6ߤ`yd!~?|+ŔwP ?s 'I{1v˧w%XU74?|+]=pWp"HZqvo[U.Ry}s9XX NN^ut[&C}F|\SMM:rme'n>r*j>gXӚNzAM R"K+O`3I^vO^֖x) 㬻o)Թri4~W*+UX'^秐QR>F\#txÈfWV%N2SijN݌rYEm|O_bFmUu$GtgS_-ToY'$4z}M.lbs\Lw.xe?˱j)/[__%UM:6S+/њ6l9{$Y͞]2^%YV?nDZ_Wą|}cpcdbauC=;bvAsNN~½z={Y\]-.8 ׯw > SrmDtj5]޽Tbfh:Ai. |jTݴI]6ӏ3 8{pxN$CP ll:| ,i,U(PrCЀ                       ?78뿒UW%D(Q08GoiN]Rϯ3&єԮ=_M~K Z6h-6; o/4nmEK_Us0y/\hX[OdyIOuiM5*lsFB(tw~?tDSOPCWAސ$DՌ{VUKnW ӧe.cŻצLu~.5SMZ CDSu=;zŋ}{tCڡIIp~E 1=ʢK@M!8l#0ds^׼]8[5B;W MKuOi.e͂@HqRVQRLS];(;$5җc ᓀ=ps{#_ߎONчE 8g+y6xl 4uSr4 uS\6-aǺ.7R_b)[㜟uD,,~'׎Q5x))17T~ӜWa$ҳZ=<:M=]Wѻ7@XAv>x\*"{e77 ۜdO2Rpχ)xv'ȟlU7^|W]mgx׎t2pW+aPL7L5//éi_e66mL-8CAxSTxj !w|V|ԏfgnGtTi(S}od^ F\>+8Wm4m=mϩgXz \0m.Mc)eV|sv]N|8S% ZVQ)@`\۽8pN땕k߉*xS.J޷s,]u*Rs۷;}Wfyƅ@#]Ay8pIpM9Nݯ馐akb#3+~_nS)D^3 LBG8dhjdt#29e~_CkˉVۮ^_hxl>w{r߷kþ.ʖ+t87/SK|t&44;;gԷnt]5CRj~>Ndv!*@@@@@@@@@@@@@@@@@@@@@@@85үbC toxݻt.uo3hoSfg9i&Yz35 t̤nG[U< h|3wsFN Z2\=<%iJjyǩ3^73R;>\ˌ U'~^|Q: za𺣋';\qo>˺mi;Ч.vk.^JGVOc3SroYχո$MNdj:z9<0CA$;)2Moww jb/y_﨩I8iLwkIX+{g{ Oo*̪6:&-tDSxaxJfJ q ŕY?moKv];熵MtEq- ܀*:OGDREoPM.;ī}iz!k8YOLYnܙc=r>X^|uNGE_/zI✬5WkOMQgh#s'~݌4|_R+V}3̭V.(صc ,o.qeϕ/n ť)޼d۵֛>9~ "QBfGK]<&/|xU;洏]r7nOpfUst6tT OϯuPNiuuNPS/ϙP45~*)zj"p1PދSJ9F}HྖˏkđݨmخZ;OSji%mKsl؇FZqޫǖ~ x؎TY7"CͭVާ[#RRR> APIgIIr :)ɮ],%VJV廚Ʃ׺ {N;M4wyֻ!~&ZqQX5]^#UzqgŦK)=u|p5{=hJ[Ǫ[) /ᝒV~Ձ\B_?icopsAOv"KeޫoSMF8r>Q>9W~VQYUEb[w)gKAǩ~eC^׃.i/O~u6ں^;;.Ցf)lԖ1\Y}eWM 46ZA2Cʆzqqߙ'fn%^_(QTQPd\'{ ѱrՖIi|&y~7`RזQnvv9i.9_jk4t^\b6e#6vό?ṄKG~uUbpt_@#&'fG]Te u+5=um|̉즾K7$NJLI3Ub_G#Ekk*l?ZOAR^-֝ff|';; -r/:^;:+; wKoIet/=?"UJmgb]#c/-QUW #'-=Gǩ[ir-\'ߏQ[TFh!3INzs%/ ꚾizخzf7&6+ Q_:sڹήA[Y~~Ut5 :p%iį(MnY o{ZWٯ2rzee¨`l|=)tづ1KͺW^zՑad.=o] tqЯ3{(\Q- ǟ 2Đ@@@@@@@@@@@@@@@@@@@@@@@J&sn>{ VN&219ʭ:k]>F?jsI5y]O>}9#k[S@ wN>U *sq4u”I lهÖ7lm^b'U~:5E ,wXc>{ĭ.D2zÞ[/&IK/w^T{?Բ4B 9VDq&{T7SJfīGer;L󮨹LPjKLzoHwYnU mDA܌na_rBifITB[JJ*hdZ竝R̲v1 p 0UŭJGz)*̩zh~Ŭ6 t-e,KJ˥lm,!rç\zy_']>ey|cH9b_ ).FRV2g A1R6M릘e{yu6+W;ctci+YsgqwF"MT{[ϗUn[i=DU:PiZ:I.wTGp[A|N֑;.j߽9q?gfvZK WI{Xʡcأ:Sٸu.u[}-¦/g{Gz\Aռ<*!,g5!a礒.yT{Cl5S]WY>>:]㌌åg_㕳:[[z[gr^7 tI8LqlΓ}©SI/5)rJY_-Xe.*{=wq9Tۆլ۲.6%4TV@VYxd1SW RPKܺ,o+s7O)cnYy|Ot$w$ /qrtNϷCwZ.fQ.5a&N\cI $ \~LRW?B9ߚ*? 4*m۴9_O6Yi:I>7e&4Vo wߺ S Vs˂ACeBJ"CJ׽5͉7'# T_%TRJL̢E/WzC<7ij %c\׆7'{PQ;9M>,U;xs5/od- >K]K l8]9qvuJt_ۤx^%IdkPx☱TKRղY'WH?rqA9x(T lӆ.ݮinuWwX'EfYKŇԧ|9[M(LKXO&Ԑ*!2{U|Jq6k#RTO'}4AKꊍ#w6MMU $v%/`nIqI4+/H葉_{t)ܧug|./H- ηs\ߘqo-ˤRu׿zK#pGf;(tQTjŝGRU4e; ##p7 J2zU$^^1ߑXFWJ|y3<(:K}LyZI[*>O3意Yp%Mج2HKpkL}#g8uyd탅.>OJMnxec%pSkTW&{-(gGm$81'98*RWY^냷QR\u8v]GS:#ϩygqSӹU-)?|>2WI,                       ԵDY9Է{~9ZѿMV;tdbFY涾x[UoiW+%Rz=0K m0q̀*NYMi{iWLQNK@2F?OħLʈVoWׇ'}}{|`ꦍDƟ:.{Bι1G$s?PCL:^P. 9WL6G_ʊ=Ad$2ILjclβ1ĘbX`ThPnlj%湻+w7.˛ +eӜu Ӻm+DQᅕM9bQ[2yO)=|V27.r+*4n/ֺ\WS'Kaqlaѹ5ii61vՄO-5t.> pu:lp Qoe~)1\ғkЄў Qhqj*SvsË۱_,jD$OM$Im5=9gL< :[_-2kV+MUM,S6E sXu`w\cSJn̛ nuWU_5n(*p]tG  C:&v-8ٞZDٌ5cUE/sWT˕g6]Z2\i:SUW-F,k'}Aiq.ݤ{5PI/KmJq,7y+2aq :N'&ϲ翮WtPN&b2~'x6لa(unp}% J=L[ cgRW2޷Mˌg{{/|i&g-q@[XvspNn>\^Ǖam=sCԚϩZO|PВP +k(szV<9ق2:b/@©VM”9^ї1zg1-㖶(8Zhh)4|tBKTX4>~5Uh 'rVZrve@5()WUHB6io[hl|*ҩNr<{˸ fVokGF]ZAc!c~ؿ(>ڞb-swBT\+'Fq|1kRqNuM;6xpM+ꏩG[Ӽ_=(({m+ݷ[{6+hB2'(s[Y'*6`0QZktBa7W +.m9ǝJNپ;'dҷ}.A5E]pd&z!kK~rSV㖩<>ݪE[MTxe6v)8%=e<gM xcs>/_4TR7OGVm&_u8rKYRGE|wUKi*j,x$K^\s[24F]TnTumZCKNďNnՌjSYÁ:~&9gc4Eter<:Hj%;c;ZxW[TF˲3VKޮZv=wieF3㟐N6ﲧ3NwJT2E2_Rۭ͒Fm̱Ki&}^G)$ gUN\puRDk>*Nئ{Z$9a. qӯwEإhܮ(J{zu\uwmQfKcVeop\ hq+Ír |~O٩*kd:ZAI,xdwH⍮Ç_54ҧ1b4f߇.(̽$3;#u>"-6I .XXJv]CdjwEJ5T`G 2gH(%gEz^WxFbfz[kjXm |v;ԫqGLVQM|5B4w+~V[-UGG N% {>h<ϛ>ƮjQ4u*m2w?A5v^ۻ~cStT)MU~‹k;zt̛U}y`t;n>?۲⍗Q ^?S945 9 udn}W6aNRqz-l*jeO(,yeN`_n7jݚC⥧1mTs,l Gh*3}5J:+68,?OVifӽڏP-9pdҐآ=l=XmURzKEJkŸ>I3 #,6] fe i#9#Z>*ɨ3i^=yu=!D8.'mrQXt^^=&\ h:vvC/6-5n3xKxtpH]ݣt|ԭe]-'^e(b_맄xeu[C"I|`Oowb7u!x?[x{] !_|5,yLy^xh{:TSQncQ[UFcptcz긏2xg W;# zA͢8ĞiԮjl1IKoaAotmZ>i.0> V+>ƋU.~™V@*jcf {p2q a*)jT[^zԎ]/'|/iU̍`pRSxi ZA#9sF:5RJ"ћvm1(p*~c$i5qI"Gl1aIJJRv74?{v"X}Bwv=-Ž,țFٮ1Zh<+˪jNa.KsˈyC w,L$)FA|mÿݎʖ- 5Ǐ_Y-g/+ds@@@@@@@@@@@@@@@@@@@@@@@֦*"K`ԒieJ|1 ҩ0[YV]QU *%i'YoCSjhw)66> tT! %"6psW[xG5juo恵e_5EGA2OY'{tS49ܪ)5O5]}K8n!xJ/O$E?L~qU#+z!z%HC x8>Y߿/w/;4R-Z )C#яgO%>][EKZz }ΌH5lT슶G})2L|#Rv+Yuȭ)߬ɕƍN ᖭQ:GY$OShXii;F8n 6ֳǿ"&ĭ5.-<,yX5g*\tWYvuIem;KvgJ0ؼ}\H0)6i۷ |*Ô< yH=E<sbdiq]ѫ59;FV'եV[ V&MuMm~benWP;UTUO9;~361I^z<7rh4EUCE5淪8#YSMdl<܏,.UT$Ժ_2;.qI\uMIߛn'Q]NƸ%1G0>*& [N+85ʛSNSu%F}uyhBY$t2A&nc骤)}UE5$PuoߚT6#\ alfs ]ճuKMsOWNU*Y{tw2ɇ1 =ޮйW}@u6w+</\x#Muz(2=QpK"C\]&Ժ(bZyN!clԖz_~.g[ٹ9w+aw||ն)%9/9cGKc~5aukW.,JRQK8:%,! 8gUw1bf)OiKQӘ-ƠKVCF-H#~ *jr%x\s9M:;/KuщNll,p~;=W55/H}G.:t #)?]b|eo~'t)ՕUF}/?R7[~\Gu}:Qco{@@@@@@@@@@@@@@@@@@@@@@@yN?1NOod<үbG[1˞?Z5mRTΰSӲs~YG#ͅʙr9]O/ƩnGOFjKDОk "뺖]A hcY)!vލFhFQ9ȓf{C wr~)I8ƹd gB[ùS3lƢ9ﹰxlk/?Хޕd+y__L~UxK.]ST[)SBo5,gyϫfgl)~!-Fգ5(Y4ⅆ8~ZM,eN_i<516u gﮋ̰F~Ӽ(wM9Gp|zUt[Vr?3Rj12exЬ[=8t<&vBn|Df}KYK=00Q6Κu9V8^>ߴն]sg/|Of^c$ݧ;()9$?-}m9d{;6km-c!I,>RIm:ȷs+b&#bvM<,p-ռVgӖnZV^eo>Gn [宒 kP]1LϠuQKzfs1h^amK%%E҂^sG3};auMT⮷^"ĥ$ՔkڟΆfjEچfD`J4\Ӄsm`Z>Ϛ&fZeq|Dw'JjIkܭY5@S>yuwۏjk6ҽ-hIh25w1܁>>Kn:pSx}1aDZMCan}/] >YNvȜD1!z?𹪖o$Tz&7N[OKO?ڥҝM^yC⚪}tƗ\KGhȝO`3]YiiN+-r}V6,>OυJOz Wk{[bUs.1N!;v eRá7Z}=Ս/SsÉh<9_mECu oo >;yGeQ⪟z9NZi$nRqݳ6Ta}j河[5šIadp+@/ꊦ~q159vfnφ| J~(srd~P"> AmGT/q:w'b?8zr -G-f ?,IoTH~XVvXfuӸ|*jNrO> yG{=S[en [-E<dVR^ߤ{ICmU9lyρr۾ˬU?4Rr[m+n73B.iIF0ўʝtUm 8O-[%+{(MO 9^g.w9Ig7֪~eEBkJ]bHnH 9`ymzZ饵yxUTMV[Ww$VOI߷TUqn~c]SVwp1'p`$:cd<^b$no//\wikPMbd7C}S-/'8t__oM~7'UDZg4FݏqP                      ?19<:G|?c?fr,\*$BMh]װ (;~ ܸzS9+9h/:wL@y: O_^us.M-PОM|TYVj98`>15_ gzJ۝yxP4_Yy&K8GŞij'-6Mwt$Lq y|͟iT˞NW\meM{fcp6auo#?r*,<|[ec3Zic\tam-U߹Zuͺ QVd,!68Į!z946V|~_꣱C > X0ޖ Jri9a[o /N+c{!qn_w9ǹ깯Zty[#W^mUpߧfWF/,Z 8.+q~z+Mx>!P[n\+)ty:)#b)Om,].靗/SzMe(J*}5-'K=vu¹3c^g73@FLLZ]|I? w:|'b𣃺ae=Ri/`J }N#wϒlu`:K^~ }kN#lwc/MKD"P`|o[LQx9s;mڬQST5Mѥ| UMN8Gj mjl{cl)"hF[0Gov:[_,FpcvZ!,n2&5uUM]R-ɴqANmt%،dBo1w߹&Smu㦝 8G^$hluIc!selzT4ӿ^4~k׽:swf,8\cx}RA]Mh%LȎ8;BM|5pJ4)޻!lWA]+)me_ :mo[mM=[5;Tvεm*5{ߩ{\ *8ba$\͇pջC=h>;*ͺ4([gSwyyWj*~*O5C!٣;o/ /G~ gljA?u9K|ON                      fUHM:{сC{m؝pJ%:om9osF^=5Ǎ5b|'T2۵&|n#9srAPbRNU5)2pҝ&_Gs {\iJ >9Gr50vvvUNt3ϟRª7TqyVMW~J?(=N|}+w6r3#:E G-:IþvVpThǢZs+֔n/-clR.[, ovw]o<>Wv|5S ˷F)UTr5 '`NSOx3-2ӗeb@]*¶_-Ӟ)\Zkڋ/ 4͑տn $d7SaƥF;tMؕ*W6WFy})qjoE≑bi,1d>~_ANSro*{ʛkq-͍rx-CDJm Z[~};.cʉ , V]8MWyLѝZ=rpRh_6O_XmrM%(caHk s qcW\[W%Z>jjK5k 򵺪ѥᠦ.ЕbEm^P>FꞠv4t=xFQw||3Î(z}¢nZឪ;WCYSL#/u4?7æUe욥VJχF-Maܵ5udR'Ď=$3 g7yCMNyd|l\ӔK])d)dqO#:.aiwIkGlzj`4չw= 8TS=սKC ;stT'ǸLOךgƂ U^&zrK7ÛuN{e+WdՖyEWu8x25J~NfOOM\}g"T^gfZY[hgAF/t7+5Mp]he}Dc뺘L֙Ƀ`c48ԃ5K7U_V 7 z U,/j.I8%\iIxgՅV-iR7pb]_9zpχHtǶvfmFY]Gvml:¦-W:F-.Ti*hݦ^&H1Hꕽ@Se)Lp&Z;>^j5\Xg -MCXjYEDrwKCMYI5>i,ЩW啟gYk~6pEPINf]54rJ:9wik }g|x)>*egO=c.fS.< uM]et=nU_L:k ev[NPyf|PPxӽLݼρѣtLڪkGo6ʣ6J\zZ 'k~Rb6.7hr/=m|} 4ݿ@h i4چ- KRsFq=%eBIm*8]Lv,,J|!#_.cŋuk:,1ӽ.~}kn^>_[z3CIs/Z\g ng1t5]"k+_N87o*NURY[㝳M;]ӻOu-FQ,af3Y.t݆D[̧zddۨd۶߂5wyE˝F:wq8T9MfOTŪr#D+Wo`u[UxHwoў7'\4Cu)m;hSn5%t VourD>r^8{.:&eh,XIU(|B)ca,j[I+7zx}Yp= ]=ՊJwyMG H_폖IG5:2:v[(VbVtc_v{vd­RMKU\Q~㙐IMol-m~{:sX3 2rzVJ#<5N\'V^kjh.|ӷEpUj(SK $p๬i']cNk5~ TZ"tN;^i5#+|VGI)=mhp.8hs[7Uv|zuB^cЫu_.W|lVe?xouQ:&Zt$e e0 ǖI[v?yʸ{Oy)^O&]pEU+6ֽSw-kuyca-MNeeߒ8{vS)i9"#ֺW5]ڂ6IA}N0HˏQ*[Znj~dZNҲZz.4S+*mk/lLd;JMoj<}ŀqm}2y&.U8Qn<ɥ,:+&}U,afM.v0 GgMC˥L~׿C` МuњYkh XlX j9@c$aao Uz|ץ}iWRg\O⺳|tmq7ڮ.Ilix`nVW$eC+Z()q+]GPndk >?{fM*V}{wt+E.i;~CK dysk{C`z-z^KM_؏[84΢k 3p~;X1<e|?_7q)s~=t4C^KÄ9y^`5nTjMUU ^ޚ)] 8'%d{T%'\OduJmD^K1jAekOh[}HkZ0mc [+܆-^m玝Q}ks5 \̑ӎ%nյWRIO#=,)s̻NP:S#]TTRjǯNoM6i}]KM NO{8uRx@ǖ r|Rӻ5kelؘ-^9r_% SuOiq"X䝡͡op/_~Ϡ8oShZ7jFMҔJ&叉Z88TaU{~2ghj|ԜF}`ITʺbM ­MMZsКJI)m'}ݼzeQ-E%}hDGQ_=©^L77^mJ*K=2KO"RG>P 횹-0\Go9X?+J_ߵiηĮeYk!?促;fkOXePֱUOys|퀔ԶZd-5+k)j׉iびTv߮:NH;E)VDY^W}ܑWLfkMsRh_zḩp5HںS3*!t"7ꑌw  [ 8χTעrhps!lWkoU%Ò^ ָK.ү\55{S=hl7*JI;gV~.Krk{UTæe?|ĺ#X(j*t픗& :KwH?QHbڡΙ7wWn"}t'*}ĦǞ_YuEڶvtU1Tӗt oEaMWY|\V\Oz>_榞j hj-K^ۅI4CǎF1};-Fi+lYqw>ǖ.V9_6}4%E5[6v7=8'k,(o.ˬƏ^_nXWZfM=eKw"a5T??Z|yb5RW8ȫjOu{LvᅏWG['OM߽%֪8edutov\L5Vk/*o0?u_˖WtӉ#!7 N'JxiǘQп.N^7稟(H˺ESp|ӿeVM89:hZ/Ͽf}=O9<:Ќߩ^̟!O/sL&CO:ͦePtʆIprȩ˷UD<3j5 tjynw8zć4@@@@@@@@@@@@@@@@@@@@@@@JY+[ M 65-:'d>DOpwf8;g|]Q;ͼ7|'55*:(nrEM9.8{_)j#Ŧ>|ke{ KAvl@?/L)0^˗Uu k[~Wiq'u)Ob#}lI<2AYoUbqlx9meO.cYy{ECzdko|%)p;3qSIיCoϩi*͝GG(1L|&KWf߇zG9h[_e0SRp'COSOMwx}AAvۥ/\͋[=M͞yxztF|۬k-kj3'9H>JgE3:NKRSVkv~$EUVRUԉ, 'g@C\-ߌ—t/Ni< !مDoPavQ\ߕjpi7VY3[򾇤pÞW@ U#$p|-a92tcTISTiӇ*e Ǫ6=Nt=OTuO咸yVzpȂXۍ=y7K4>|rOޖvTQIJ]-G^ ]-=SL6yχx]뜵~>k{e[[6ixZe~ƚuo`я]-CV篯K"Rn,[Kg Ek\i*MECzGm}pϷ_/c%x5̠NQt_k%`rE)ݐcy賱kg_?"J^vyr؀pJIQ[s*2LG6! h"uG3F8K}6jA#d>!.i?v~bn|e Crx>~6Gsd9_ xkNKkUxJoǖzq .u.UUN4,%@z_j_| C~fB@hIJH?C鿖ϏM-*[˶~q=^DrhUK/tu&3<*\jusce>5vw˂bwy,2.G n=[evj k{c"4VN^%+[*s򵚋2 \*ӆIM?_Z:J}]!*k8UMxᥔ sH :p\8~q3ϘTDu})9QUP+!U'fY|G .'g#XRĪrwyhЛ oUS q>1¥̚du]3%<8~%Zx^:N͢2i䑘kzH3)[=ƅ-]2Ͼ|2ɫ)t4uK|i<6;?T^qd9lpEi{6 )63ïlZoKU G˷벤SR~T.ބn>Qm =U>4dQ%@@@@@@@@@@@@@@@@@@@@@@@85үbGR+GmpϿlН49OF7Ĺw<9ἀ76*I(s+O̔;꾜Wj.prL6{w6:c6ǑM߿p}C|6Cc'~ހy~j<}gb/2~Kx%S*Դ_#M=??r-}nY a71H߬7մD{i^N-2_V ;&7+iS'poQǟkesO2iEq2-%^pijZwSyyl`%;>[,%*e%m-{};Nתm)l_秊{22̚*hD. 8vI 1)W9\c+{CN:G'iiEoWI퓍E%3Mi4Z;]_PYE>E1SK$  f|GL5M{Tb.GR\)DU*I-s<\„YLJ|9:oU:TZx}-Ӄ p'nۧ7)D[EQ*UVmJۙjT%nZ[Sk־M sDᑰwUZ9s+^:'Y;E K MP4*b#kGLNF .m+wK=tſ/V;JȮ-IDeCN_ wm'VgU_6no~>2C4..Ȋ2s+5Wuv51n?x.#Ki V7xq31t/p\48I/&4Q)C#R`l*z*Le78E-puc¶x: H4a`˼ܸƴ䕧OI6t{*q o-XUWTMx!k{f3=jLƧb@@@@@@@@@@@@@@@@@@@@@@C]*$9gv~ka~<=̌]<d}^󺱈xbeֽzijQ0I/ xQIs(n,9;m,zq*MW~-YQqųHw>G1>j[%ÙJ]=Ǝ 'xWn< *ޏ_Jj"Wz[:( dZF=7>]W-O>*r QWH}ƇfR@0 w P?{%T59.,NUZꪪʛw4ttl6/759 g=baT)gh3Ϧg5S9O$-OxTk$ϧwC\ `Y']aӼitKe˦]<=J;exBiIZ%8?:\B͈ޅ.>r\DY*-׉8`WG#f@dzk u;I-ϞWo>yONRrZ[2ʭ|9q#zzUl:#.ȜE)m 2Pu'¥ ļno.?4ZhW+źcGAvdQ[~]%K3N[YǞ=}O*]2X9=)wgmi{Nm[#e[ۖxH##mtR#sSGqmחye4W*Z-u08550HƐฏ$X4UUkU嗶dTwJ[::RqzOrs|z:DOpqS WQzFݺU? q O5z./%9ѴF]ӷ˗Jx"~7Y? |qwGAMjቍ1r3߰ ie.;ЪI>t^>(~5]Sx㢩JZ"J$ oGTs~ Ǭ؇ܮXCYmpZ(mC _9ikOHVs+ ݺVaF#RQ,vnEtA(Eit*%՘qq$``TfsbJ\8d~L^cUq+kGOPRt-ÃDv1oÅEp.l1;KLj;RT]guf6fGgq岯[nm3{0O4ww |gDMkq=;N(7Y:z#-"?)'ϫ;Uʥy+v{ܭ}O / FVqiU)Z6ϟ 굼V^} VN!2Fޥ|5n?ma?t0ϔ>vj*88`a[b ).Ri&ťoG[>fl;h<y>dys)niM"wPFiA;IMe|2Zi-/;you|kcF4RT3=e`#;-!ГYGȩMr.ˊ:}!%営Vi5k8#@͚j=׵mk=gxhA𧄾_\KyE2=?a56k"s).'N.#?ĪS]tK_1ɏ.n*u>k22Inv#ESN*tקj]XURm!蛜 GqjM[˛*RJd|d>6WRwL.mrzgɫO;dD仞)ڏI`sӀp S|xhϙ%;N=KTO+N僞.۪5j}Sl8\(챗%CZaszer1UG.(ӈVVϖ-kCϐ]o[~r:4ԫQ7Hyb,1uklT4tTS90Br|`Fw)IdVŢ7,JI/Ӧn55[SY 7TyksGY>9]|p\| MKͥ* 9&gz.k^M3)g2TP^mKװGaXKqeM8.0)D5ޙtsunКψ^GfҖۭ_?4qӊ` FZ`/^ZÇb .O-g<O9iJ ٦}n>:)w0^=USwSERu0zSWSO?2ј~F7s7''PбU:uD#n9͓}>iSH4f@cFaV)x5=8%ZRciXh5e aq]8YeDJf'1:>}悪ܥzρJ,юgVR;lRT\bOWWOK暨!crclw\QV3nURݕ|Ueӌ.U.^84\NZ*;=II@|ȦьaæAo?.WKH!}?idtdG˷o?}Qe@@@@@@@@@@@@@@@@@@@@@@@94|tqVk^3TJsGهwZULzr<^ ϋ^O3_~y\J|\N(ѲAݧ=;?v_=Vq{gʛGt7n+~j+MU[!hc/w]oR󧟕?)埗{5טJ^'Wj7dmCDd(G 09l`+iݧഛ~eRnrfM0pjV=5OYMO.!Í3r;ž[5D:ѳ'?z0M,>Jkwi'-e7㇞s&vpYlR&Z ]ϫpiܑ]aBU~_L!w^%Kt͖= ΀nd2@\#9 \&wdivlZq>.8S NIYۘYj5M]OKĩRQH֘çwE8a>DXXJL%W_zߤ烋>O^c+_ ;AZLچ DRָ<=jca,Ϲ4Ք{ϼNVeHSh vΌ֕_&m4=N'l l23ݻTaRÏ);}4<$ꪴ|'>+= וPUzh+1+=gmqa2K X\0쑶IX i bE_O5ΕKT&m<۸U\u7*8EDbuN15/vv]gN]YLT߮gmZn޼NT|Ehm{Uf /YdHC}i ᧆ^VlCrzumpյV:Av4cG+XCӐ@\׃ˇ| i{9C1FNlpOKW+gU.%<7 lXQ8t%~:O<(U$v=K)+ ='UKX^qk)aR8?T׌EBk8Zd;Zypw ѣ..|GKf5DqyprCI֕HZxwhչ|;;U_zy'όw?5.-p]p-N*n[_V3tEArqs~綇Ԣ>9ALxzegyŏU!hr 3q6 ng(B;/Ÿy`!Ҝ׎睾>ۢ.XϞ+aN棇8]1IщsŮlm*7-2"Ou2KfmxHu@Cz`эqQݗ=}J8 b/?ƷMu.*m+k%ӷYjoԶcU1551gR.wXYN!s+N#-f3%-=D;t@reo۸z\^*(Y6?6߲B碴樦 #'qEhN8Sjedm-__NL6I3i9A}B+-v?ln/Krui-m]IjxGUywѾ8H͚vϗװ-z_^F];:-S\tӑEŨnOrmZ]+j(nu2JI 92=<ۍkhζ˲*ZmCW\]Ɩ[#iV-zi-PMR# Ąf3 26&uxk71[z&q ^(iM[-àpd}`JziK#sHZ w\L vrq{hEM EUi3Tg #0\qfNqWp3pa6e;+ 8c&|0{|Wx;.+tDۋ>cM;/hͻ壟nZýYoI5.ժ5Ve/ qk}&M4ioy9*mfǯzU+t~^俇 Ѻ;EڻJK+nֶ9H p<(s󕾼mŭӏ^a֭kc.a6k[:lҖTT #tc.MK6ӷ+wgٱV<_8噃~ ku%6IqWMMt c8$v+>u:*K;ӧ.gakپJjX޿JTME[F|g=!KFU󖲏(o[=nd{tӚASw)A`2PLJۍ׋B?}<ҫpZn/n=i u=Ù_ ogdN27.vISu`bVTѽUUNN-̿ K]c˥OY6~\-<9tVcƻ&EouTZ̲UTb{HrGuXXt*'|ӑ;n]r|碽M=Wk%ήSk?UV_ֶ̢vӞox2(v7O OLkGkIڡ:-vzaT1yՍTͧϻٴ4=S48?L0ʕk?vlVڣs'K5{  oQ;%t4o߾mW+vVࡨ\\ꨚԷ+E`t>+<`FX_SN0|=5Jg;k$uWS97SH%,}P|?;>-ޓϜXŖ@c~ l:V2,̿ 832 31チ>:Jm4ΩwaƶSQZd@ph ;#˲jZ>V?:H$m,ÏVN׿B#̝oye;G$@@@@@@@@@@@@@@@@@@@@@@@Jy#Tzv??.o Ҽ0.Y.7Yx;F*Xvn"9Dꚩ:{~ U .kpv߶3mͤ NrVM}RE8_XβsgSl4K&9}'UǔG_I~:ኝa'!~JI], 窦;q60asg; P:]kMI]IRӋߴ\A4|iy"-VQ>Ų lP%wg[x1kqj[}uYF=O81}I#-zCG.v-n)D!>v^85G78Z=C ˵6պۭ4,e Ψedm9?DSRjυ>|}t&rsᗹ\Il36J!M 4<œ406ߺ\o= P#']yU]_*$NP0;B`=As{?}Hlȩ OgwpZ*JwɆ3IS<3m sq*u<>L݇eߏ23mqo]WP-owp[hc+^^֗x]씷o7lz5iow(V-sV3KlWN뽦z-N뭼]H8Qrvf Ҽ|A1mU)u%*]zVUygoh9jgEObUyM1E#X27[Èǒb˫Kۘ*J=Wery&Ļ-tzYr4/svC7Rn*=xݬOۡZ=Tkn.M1=.p413N;Uw\Nʹo+)JlT>fѯ:4xrqk[>*Sn:>{ޗ/ogQKpz6ql|IԟG)55ķ\-3;l~%Wθl$.27ELʊ( <ii'qae5`xO.>H*.oNS>],K';m EkS9Y:0Iz]1>Ȫw<؞t:PDO=?jۈiO/ֲ^7ߨ*:[5oOH,Kӌl>l(.<f~'-]m|h|T$:+dlNapYn+z7Z񫝾˙2jJfz Hݧ{Ժ/ki,ͩ:(ȏH/s02rzp`TmS.2AV&Åe|H}k)nM} hD #fIQ[>p+~r'⦖Y%iy?w\{<<>Lt$}tk->pCp.vuKJ99kg9@>gEk斥,&ၮcd(\!tFjz.OWn`Z|1P, ǐ#l/å'C\aM__a={K -S΅*EUx.Q [9^Q֩JМ5|%xUC|Ek񺶪6EttpsOS1q8j (oZζ#Px_Xy˳U۝Q)Kiim쫺=_[x߾TUWUz{[rQwU \a>CǾ8[򚎧3Opd/co[aHC|6U&ε_8UhV]Ҽpk[r(p߾J/W+n^{ؕZ9zqn>48m|t<=:]5Sc\/{QFM]&}5Çip8Ͼ6)޶+M`Ǭ4-r2(OKVZ4g'+"i\gtlꪝx>%g;MD\$t.s8L囁,.U(ߎ}Ia r1qT6cSWS7)$3^GeU/χK8 aUJ#FN!o8ӟ} .i?rd lW$.r=M#^%Z\={}zht1oO"5:@OyMT5*Ը:                      fUQ@ڷgdv?%[/>TyI0ùsNW*TO.@]WonK.V%tle>/17 +='\MݙJss~t>eKySI;IsKv< ͒sW~5T.jӌ@5<c<1JM?2,jZ坴z]ѼN褀ӓׁ>)%N;rW^KEOE [op&oڇv785V)#ԫf"/]33UpJ8mOkQiʋL+#p9 kO V'[GzZ_(㫥=GC?[s*W 7Lc8ܸ}ܴDzٮqx)ʗ4͓M0".gSJqygv})"#nSX{q]޿{5u2|YiJ<|O;3]'sf&2Ӄ|Stt 駳$"*x5jї˗ u67򶒰Gx2@9kq{=tˇJ~7hr޿/pQV֗Ph{ڱPCR}7 |QWxk=5-as{VYJ;T fֵirm?d\HbfwlrqӉVyN?RJb~_xoMĮ.ACzOe#h"ᖃ0F7쎚ֹ^Ϸ 2ezꆿ˾_G< IǾhj|0;<)RsF/W:=tT7.Xg6JsB<J,_.o)M*P6_^YeyarDw 5lKRBHԏlUWp|'Y$81%WsԔUW$̣Ntu%. ,;Ry?]c3=ӕ'=y%4N k(-}ʗ2IeXHwGCĭ;pY%xYqILxgǩY>]i՜\Tn E JUOS8bêCSQ1|:CN/3 %Z-K8$=Ckze˸Ӵ^~Y[#H]/ڒ>)"KB!d0>i<Ǫ]>/{i}-G dm1TLY#6t/nnsV+yNKtyNP*a3ͨd1h?ϺOi}!NJ_4'N9Hz]k-ښCUu)("29;#mĦQDJa< m'+>Tj E}{jkccOu&NZħ⾺]1!ݮ,BW Y%]u<:9" 7媺+_&£uJp+>(|i Np~-p6Xٲl1鏱VTBŵu^0:Vٞ.~zA-޻YrV|<~&dͧ7 tݾuY>9n;GCI%'^XH~w;3opsܻW\4Li[=2~j?w'߁JΚS4;~~X|5S.m*c-W/K}|qPFNzz{'YBKNZ>SL5Uk\xKs7#@@@@@@@@@@@@@@@@@@@@@@@wooEu^xRUv;۶6e45ST0yg}O{Ii_/>i].$s {t塒446XOD06!^9}'nM.m1_gC.%G @|7g(*ZW-ӧW kNpHmyoZǹ=._Mo^y-m'WNNv;'g0\:H_Ft+p"%]M)A]1B 5eΰHԽv=$Gskl-/ls'/ Όb,-+nr# '40mxW\+Mu"{Uo!\5պ;K]-&&st-'3|gP$w^c=UPS-–ޭŶ!5yqKKrٗDin_eOf~aպf :fO$M>32 ȪGQ)~rhb`чN_1w>jqKTަgsi&|3 7q_-%Ǖ>SktRQ馼Ԟ,|_Xl:HqTh54 h/N0LEmo<7*S:p\b;VQ1蚮 VΡ2ZCyHp8qWSOvڸK3?jʼn85bOk/ztwMM\Q[$krX -hyJ~Ώ\ϡ-aF~z)lj$hHtt5 e΂{-5LEzJ2YG  )oƎZU!h*M!m?gs# lU68w 6Ӛt]kuoOɟHe;[ fLtc^|MGªMPnOl\Dz_j-mP6آz+1gD}\c/*j^t:;.5Wwr㎗_۔mWbW~ E_ mon}w`4T4T4űi W9)Yy~FMXLWW.Nhe,jI\j)jiAkw`ٕ4on.EԿ/TKSҭnF/9# QU&Vs0S41d}/etjV>?^3_ԖWv̵̇F8Mj)+_Sެ~6 qqO|m{Ңϕ2U%~c}^m[jf˅Ҷ -ktO0Z'6]͝WG[cXRnz}lF曌{ISPh.44|;ljkKꉇ& 'mQlY8ST+1uvg-T=~/ֆ+xoQZ+É[\ؘNJ}6اsj8t8vWlt]1ivק6F2n9>-2z+.kdK$Ol 0dՂq3t*,y˖sA",lpk|Oވx78t>dxz$ysr6ϵaS WKjd^5Nj]}GoS mmArW.%t|m^!i ^v׍.r*S^%5$6,{#ڊBE35@kpZNnU5t__uɥU|׆W]7$UPj#;^{-v;nmTT/ˏ16 ]~ҟm /khJ WDER˵ƗRL|)Hz@/nGNAק|1L _uWU.f%xեn4>~RO 6ce;(DS[|m]5iYlAO y,^Űsv_nA;roq{;ߡNʦU;g5֔:53i$qS?Nwmj߄ӁT)YOy~O=Ru6⮉e\zSm/lMka{\ZFGV,EZix^GSj9NkMóR8j#"X6ܤGȦK݀ޜ߲[.T⨇J!Ųj|wq{so\q͎LmU%[s-poyNo ,,/*R۾Wbc~͡ߏtsn=|ҷ'32rJ?U97U56R]m!]ׇ[*dQYU Z zma**TЮ[-cǬUZie=3\Ujgad/մt 3$mno^>'EsVwQDi_j.ܪ㫯eC]1ꙬP'eWeǟ-xYraեCL*u$[kٮ!'IW:W'~zpIiX#5ŃopS{bD=yt>l=T2m74dwۨ?ܩm65Z6IB_j`7/ =p?~}Z"nq?JB&4=K.oϸ)[g8#vRIbU!C Q@@@@@@@@@@@@@@@@@@@@@@C]*)I5~O#쾇f;#q=ǿwiYv:>^1ihiY뼳1cO ny4nLYyϔ,}zK}zu 1sM,w$_,DxNn=ۏ1 F]g뗁^6Z/el?eu$mA)ď/ϖI+ß{OV*gJN1 c] =V&F&o_|GVȩT7gMPNZkZNL_yfYI6P?xϼdnpJ٩;[?Kլ_ݙG+H غoǮygm8aqI;4ƶlxiVַHo=E CߩL{Ӣ|qq /,\aMoƊIp}([ڈ]QH%5$5ʽWCg9s_o}*">#DvKEu*&Q2zձV\hv2cpեkTtNvݺB#Pru%S?wSZYA%>wT0S;ua# qk ku^zSQikN_9嗆^#,^jf=-(ǏR\UW~<ƥ)9S URT?p?W@ѐiÜ2\7O=j_ZvdNmҭ3 |V8i m>d?Z~9N?ضjޫ~i(V>/V;kfRq߅B[|enc#ĕcJ<^^>jk/==ÿgGo׫|T3FeTpdُa{)^ϏU0q]||6im3|7C'T86RxG@k}iI.cՃ LO׽$mi(箯.K=ixn`Q>QO/] 8`e5[I{yHKzy~9} OXD~#e|9*h\'YtZ^w+SMRe)d|NfCq-w:Zl=d3;䤯eڗcq_NJT%UcvyqOO39߻[o>'_IUMo7:e?ˎ?CjBek䧢qrCAy/Djyw#eǫZ9*߯w"zg/ޓW'>6~Zjʮ?3EMfHэ;1*\NŖCYƽ]h2iGmqVk*$pnr0|aQPS/y09V{A#]sk]➇EFE˾-/q_}y? JކQl] {d.4Str>x&֓(ӧ7ˊ9jy&xnms#VwwY_<˔^=Y?0`wOý Od(̈d>{}6)OGS3{;-yy< 7>w` (TQ-xP@@@@@@@@@@@@@@@@@@@@@@C]*)TyO;쾃e>&&:o][rޜ￟ae/O_LkT!^Wuo\ 2=OyVO?KcݗYF|u0:Tlwˎ=-)3-ltJu"l_ߥ߁MGٛ#v9[ϧS"u%M;Y=t#ղ]D88v6ɋuQG yk<&֊t.XT- K&P>ibi iw/< ˫9O"2)ύR$[n[,tMp1۪ZfҼޢ~}[m-)ng ˢ+V>?~3<}M/廉:?xZcJiZCd1y(vM.sNwO(9=0×KjkjMcSn K}yGto*~5rLL:U)N3 SK}5 toWknwIŭ^KCnZM<;Υ]=GvZHd)#u?v{gOKۨMK^ƾd_[Rq犧{(K+tyNL{べT?D*ٴ򤒍]8z^.ttGjx8JܸJEUxxf+գ/9eU]P,nU6[7'l.7b4vN ~Ӽ{$өDYeyt)F-c^sjf6۩l0OQ ަtc89xu5]/%54s>-19Z-S_Kq o(⤎uK%trTX{}4S紺yU߁6sU,ZX߅yM; 8)ncCgyx<)wQswtKUQAÝUDsGm]X1 C]IЭ)t\ שoގSܸZt4mS[`'J! n8h߱Gc&ԡ:S\{K~=T^\i^"mGm+ZzKZEMD<Zܫ[y[lDOӧMI~8jۗ nPIT}Yc*tԓ^svsճJV{f@TKuM5)^uΌNowVKޙ\y9Q<},E^Uk%z;5:Ξzkl0ڪ :KzCL10<|u[}U5K/{7pŭiC츣wRqBb~ӧvϩ?e=1#2f ^MiCogi;s#7n?UCC]"?VXje3Et_ת^~VX%[?Hy>[gk_TmDۆHZsLTKF^?DD'a@@@@@@@@@@@@@@@@@@@@@@@?bAbzt]?:bРU|x˂wA'IYΙDOُikHE;gdu᝸xRQݹʇsh0_QMޠ'!8|טղ&29@cv3}+T*5]ENcF] m7?-֕;'/= җKp\<[ S]ItxqP~*{/)5S e{^;uMh%iUVMCiFYTҙF1t"9tʴy]W;/Gg io-:\։?6lLYOz@[S1U#+u?5Z&ncʷub֦tGF\Ca%+L Sӧ~Jq-SN^]N:oI]qWWy)Zkf{*:LQdaCUTxs3NZ,JJv>>izqJh8#Ët﩮ٮ˅ⅱku<9#ncU)g˿p[{.ӹw{^%-,5[)6ڧSܨv8 yc ]ZX;>"J ˣ閼UV.6sSy  hw𣌓ŧ 1;ST՟,yje_rI>q{VmW?7UҺI`sh+Ju~_J1-4i]xna6 AFAlbذ(UG^><51q*W=pFhT uQ?۾_zW?R:߸+=[ޗ0kGIz7׺Rn>TE=%%\t ́c= :hua.Ӌ[^Ӝi&9u-Z\k|MuZ#hASߖ;,|7U.r/?kN[GY:JQE,^[O3&> i>65TVݯ1^8c9NsZp|?EkIeN%:SlIˇ,.}w24m 6&Ü{v E͢jPʮ;xqwҺ SQCnf(2rg):x; =q_h^]_n[}(皒 9fN}ƶ> 9Œ8ҴUTӅSvT*WSjfRWz񫴾sټٴ"|!לMԺק8>Z7,=dBRckAR.3fjްk7{6>tpccŇMeRW: 4RUښevྊ[iZr{>Ss3^9|H!i`ᔗ:ijsE} !la&BFJUPw^Kw-fz_ ]߂\iUbM%(=ut!ߙ=og:Ik]Ub6yF_:e 9ԡe30KՏ|ѴWB)W>e§u*ҭnJZsJCQKp_SOQqC{^#To~ipp{ԚZ{\+ӱUj:]–jK$ ٦0H8$.kS4Bꈽ3-:Q]ZsPs 5f[Y]u HiOKܖ: L<|.T&[exu4ҍ8,y&qKk:;kN&ik yіa6Y,'Π=/vLBxzk.ߌU:q%Z ДuFRۭ)gO+d7fW)تR<_i:yBǦ-G%ХiNpF\'[9Ӣ=5\2ˡFLՕQ z|+aQuMUU-;Kt\}u~ mC{g+ki˾䵭|;ZFo70w(O9{zk֚f,t75=$A- 190g樼JϣY5KaD98^6*qQpWhMO-W*jbt2D5c$k];{(jN:/6h԰ڶE dM k7oѺCYVP]O--|s[MgOp6P`cNMUN*v[57l&U? 7$yҼ .牚K^ٟ]OM-5i l2=k @4\jҦm^k/DvJo>1xLJZCi/AQG]itwb@ɺc<32د`jS_56cr>|h:n ]WIiË=ƒj%\-uTS5tj,m-)\iܻ᪼J]n^\sgR5R*]5=Uo[0-RRs@Bpî߾{ )+S*Hw%Ĝ|}+j[y}|OoiJ? }x䁌l2V{vZؽ p9,9F^v?.y׾g&KԿÆF1{=S$n?j ZkjgEs~%P_f&@@@@@@@@@@@@@@@@@@@@@@@? kU}ĝ`':8_/tr}5nv8oM"F*~EU42݌$lq?vSRjh{ۿuYs1BcǶ?5vܥg5饵yjU-Pst`z۳FIl|7Mm*-+>6Ǩ;6} Nީ\D'ns~mo~=M9yEvS2Ӧv^mCj}T1j{u3c4SFZ\:Om|8,5-z{STTQ=ri)Ǎ5ת[UW ZU2 *zP׸NF;8[E8rrlN2MUM痕[jv{uk_tUZ颿L0ƾ{mVu-~9"JíNώjTPEދRj.'j qT[E.ri&{$u2wSWiY«6,sЅC,ys 白W,-gE![uqc#7yqܬJRz-f~(5wYn,Q]kR[ 1K>'4seQT%F kw'gˋ՗3#ʚltA ,&VGyeN=کwY)mEIV^m̫<Ğ].4ZoUSbg0 sƌn:\صp6/U<<;+b*S<|ퟷм%^9=g6[)" U p e} #%Xx;ŸԲ+:V/~ 'Ѩ(mڪXZz㧨..6<}Jl9jF}ÏQ}W2]~jlFԷ/?S<:[v~K.9Nx}pkz7:+ݳPVFKC%k7x'9WMq4LmtOU4*]Tk/W\7.=[eo#Sd+'pͭkrwUS8u'W+}cϪ'i=ܷ O0%֛.kLS:VZU4Mxo7>y[?u$YXӟi[5.gxHZ/3 mW>3ꚪ0|Xhkr;:(8ܭIqzu姉^i~>+3dҗ2ꋽ<9_v0a@B3tcc[~e/2mf1Û𷯂Ϯ5ekNQMS-ϑfRu[mw]3פmeJu( wn U qlF]şQؗ9UXP5:o%׏WRxG^msUN)^h[=I3#X&q/w˷2=iZ[;~6 ;ulgRa'yfi>\K_鮶Yb.:9/Jmih N˿ٰ񳶿H(8-U^{]?Ii-vj Ll7NIt--9jѳZ'q*)ʥ1xK\Oj"-'~%R>Y=g9I&n]zp=N."M)V 'g;]iolAo2۫0ؚ r5ہ;«Iz^5*մQjim5xYZyksYf9qXxĨw@ qG}UzMUJɅEizrϚ,G~".GV)+#O@/Ӱj7—=/=1>Nڡ ?.KŲߨ^(Dl |?[(y,LGLJcFy[^y3O͏ui3Ss`0QS)7:gHoQʚvxub}nŏ&滄mֆPug,g88Afl_l5lgszD y%|J#(k+Ƃ}ad~$AkK#r|3EsyKE3Ee*Kp|bВ.x2ϩmcY=qՎ3Y_Pjf֖u\n>b>ql42-qp`9c`gWJ!;ˉxy=%K'k=//heHÆPVuMWQGL[{0'+;fn7)|N6-IY˿/ൾ!x eSpKصMS$U ci*wVK;oU vE|Y[Kۧ([Էt˪%t*W oZ|KAIOĞmzc9%?ĽsGdzFeepg-5IEGfdC3Evh1);ME<".Ygk4s5uTq=ſ+OM,IZu#ũrug4TUZcSQVv6$E4E/ilQNNb!O~xtURQ\. wxմx"զ3k 6R1 :RYxi+Ukh߯(f}=:KO 7x8߷)ޭd>䒊5YeꊡFZha7l]_Q7ngfs(=̬\6a9ld `g=C`0wx\sgnBj|3w{dvd1!8 1,|:~*/m;bUSSZVD0;R=W+:$>``=GӲ J, "Sj_Ă|lv,)D;?(LTO/c 7%RZ}Ob@@@@@@@@@@@@@@@@@@@@@@@xJ!ϡo߷t׻d<;qn;Rvn.kG*u¨#on6Z&/GU[޳YKOҦ@N|aiiKJn]ǷLZ\rRD-UE ˔/#G*~گ}3b(iɐsmoRrE-oUX8:*Jkm,SF#?'c]mʝ߆-N;psk\R^(v顤Tsfp7>8hl3PQ\'MJfrZxMK.Ry)E[u&bQP|y^ u|O$ 줢yMQ'n,Yq8-덼w7]k[t_[nY>jکl&6Ʀ=[ZI u(Iťf=rhNzVP[PbTASVahk$nZG}ˏjT&%5QR9t|矖y#h-\ꗞ:PŦۮ"5y(n'e7׿ hSk)\MWņ8@hycq3яx9.rӗ+SM.ʅ )KtWuVFgH lwe.я 9;O quQzK3n3;Yv~>{+X9[egxKSKKOp!x[k,)On{ۻzOn%ji]x[?R`۝Qk6:Q||8~#cT*SN5L+u噺M uGGuKL6vU++^#Bt55߯~ |5Fo#TkWx./_E.J}3xgЙo/wdzoQWEUJe}~2"$                      x&HC_>W>ci{)C[Re:#-vH9o7;.y4ǯEwx`\q"N]fw.4;BQUNN9ʜ&OG4\EsV+v:*WKuRTR0t2=3qZ/+$ҟ0m{4n=5G9XT8_5OZ20 y3ekuۮ' C63bݫiwE%                       x}"rxP$s6=֮ixƞ>>v31qZ'_NsycQ?GTbs˞}@8huW`nÊsc}vwYZ:ͨ948tTԺx+_hYM4Ad/il9|~>r>Cj.)k}3pNF./`G 'MWToF󄩧uoYP1mV)WaJ,+Lᣌl -K[tu1 $?շ\RNp)Qi9i6:tx]S^<É~D? .ukzBqjKQqsc k6_NvM4M19OOۅRen?^'G5"\6-~՗q]p;aWQr69 汹n; ;NVLny{ȏdn姞mf^=ꋕVjm|q-%c[!};v'i||t⸪軲h}&5V褆=Irw8 YmnqtwP8)q6黲X/h.=Spֆa6zF|}5KL 5בT6\mN(rM#䫃u YmRҰw.61Uaюyh_XkҾx3_r˵+ YobMo!09b3{;-[+ԪO/M=ikrxvWh-v6OPC}$5_ԧUFYxtSSg-El\<@>TOu Tk[VR:W O.1o%鮍j'?e %.\${]-4y+ͧi&egSqw*jZ[5F::'xOK,,K`}V&QJNVylgUbU|yu~єԹB!NNދviĩpvof9JXtÂWxV.1>F[e z坴JQi=5pi:Z H\ w9 H6ǥ׎_m#uN\;t'X*Z%qD)Xëz˒HnwE8|b"oIgK76V4։.O=r>aprNf6,@@@@@@@@@@@@@@@@@@@@@@C]*)oP}!q[Xۧ,sy8䧗#iۤN;>?g{]Kgn:$_xeݩJ+G5yvn\Cm,0J5[ q˳[lnru}ؒZ]3._CaQ@npU1y鬕RA[+K]僒nQK7>^,'NG~þk˴&W=Sp fHƴwL9ϟuj[u/2EmWClER"et?r_}Qfn֜Si|z>dj{E%+t  ւg/'NN6PUcSd+umTԬ[n6%5sS5k+ {:d9#[o"ӥᦙZKqum/×M,B|_'ў>V?!sbS\7gR 4kK(SY[u$t4nqSDoY˺gZߗwfJtsקX} غ8IWA\#V.,h'ǚXu连6:)uit]agAW;}#ұQU0]릐7B ㆻbF~8}^ig1YfgW+-VaN,]('nǖU6v銭wǍߕ7?dE޵8+]hn(LTsbZ|YeGSi߾I $=_}$x5RU<'+M9o5O%T%un|iz9v,;_˅$ġ˗|ghJsvPT.Sm^m"0d݁pjhm.FN0ES1v{Qt9ZZ 5qq.Rme>':M_k}O_G>zR&V;i^?JZvwgJ|'|ӼSV׸=:~k,v{%^͛X K 6H~zp>Y-yyhu^,ܨthvE2xuSSW#wG9;TL7UtȳEP$5:(&6>Һm{Sd>pIϖWt2Zr|{ӡ+Jzu-> B?B^1=Gsup+yĬ;^kgF\9?Vh;8{Qm{{_;D< 8 j%9Ǘhu;!R{Bb|B<{.m4McLOc֌s-;>%-D*m~OWH6T޼ʇ<l_ttVVLf `h/fMiZxxnsoן;tU9SWrYsW}PG)_-[Nz[$~6oVZ2טieejzdeexGz\AU٩I$|]祎S9hrhaռ:eFR8>kUDHX2[~ayW?~5ۄۍS_I]9}% e^ѷ)2iC.#puةXnt_7| o7˾ք*ˬI[QPKSwrXlUݥrfjsg{[^TjS-__TuHK;[k!Da8J6jvOLU3/-zԔo}J"] . C@ސƹ22; qjiς,a<;NrմjyMשKswqjmm{#[|6z8dՏOnТ=8x>.&bYddYf#;۴SwWP5cvL:VJ%(KWGKz}z-C_&NI2wS5^c G>t~b4 k!ERuֶ*Wj 3a;.ŒaU Yry:$uO57WΞѵ61>3vU[;_>۩ꃅY\մԟdKf7wypM<{W*}'gw!{!09FюtQ ɮ,]MMQYn}E֦)=L,w_P=Xꮨ㦞Js8ݻ%|iw6 Y)5e TGM(;O珆U)v2jg!/'r;<&݉\9|-ciծĨ8𛰏q+5nթxӏB2TV>=WMIW%ECi0#;~ӞSe=TYuSk|.]l)2`dg?xU)Su?7㎙4p[M啜kP!h-cWzuo쿱Y                      Ɣ9yh>0Z~c8?C\F@?|ϢÉpGGWLYYk-)5-l|r2&:ě g8[(k>+'ݞG4-]?9gQ\shy:I`Ϣ|?-gw/=Sϑ0JWYMW`g@#}q8V˦n 8eSrM-;Q,zwԔ2NΕ*I 6r+RzN_ov~76帶V$2;}_{|U y_W8%ku1uwlb-/4|hiv.Tx k(qwޝmX䟖JqST.q*~V urb>>]]8$Q!'6)yh./wHC^:1{w$G)ȃwwis˝N}sN&>X{|CwnĘuP\:Or'F7{R}SR^y7:\/?Yʗ0RFj ]kaZ|ǺσWi~[o6QuE_]7{9~|bkk=5xw=mk"o~'Q?c|^Wn;6n.mKx&ztltj:H 1z~^a]È|#ך8tSZt\cGwm#$}:8`|kj>랳 M!hq^Oadymo R|ߧ2U3M'0斷E83m&{dcy8O=YFmAxݦ]-嗩_zu~NnjvG7C۰gI82[鞟1OoCqn]oWT26}i.昌9o}մԿm>QuVSzjt7]knY浔兤;9zh\4Pԧ9ן.{4n-#F[L/O5Zm;bVmwLJ.䯅NMԚx4Pxya ъ_!1g I >6S/ӽcU~.X˂ViYt:Fq;o{+?;Z>r‹'M*~˥a]6ަ~;Mq8RS7*^er?U3NijOpO $#"i8?6폟ux_YMS>y\^Y!oOeN\s`姖s?!k!b 6|q[ +e~3z񢬭t|;iJ Q|Qhaḍ蛇8Ǹ;~c[2yxq+>?g@k܃1,{|]*io7nݭ݈Új#f-asX<6tϗo+U%6~_{+ճoe^Nv/Jvhtx>u-))IeeYsg^k+66’iє,5:TT+W\yKdS`v&.OK>0-?d>񷽜1L,aq>^*N~,䓿 Ék8bf{5=2jpV쪸}R\ǾV [+,stn$L >lktͦ~}zin;#OrE4eC\k6̕43ܑPbL.IiLTݬCCÇum(naඖi8 o3GKZ/hq1nWβOJ*eTOpq5.?:PxUڦ^4rQ/a;Y>x73R)TՖZ'9\'AC~|v[j[Tc>kTB,2kJJR7ww3Sx.?Up=_f|tBJۉ"OMm.7OOp:ˣvaywK@tIL?r:8+})5R']< =Ϣª\BQ(bEn9 LM$%uw$ Ol5R—G tsn|ɅW-@@@@@@@@@@@@@@@@@@@@@@@@>qMO\z\=H]nEztk9#=F0|jʨ7i۴F&~^## lv>n7)+YrKe4G&ml2r;yc__ivo2qU[n!Nzm H0?ro^6ڋGW ;߸Wnep,M%ump%ww,~XiMJ]υwKftrׁ/G)Kٮi//W.2ӧ"QYeY h>?%Tg7q^5& ϋo8:9'A'=g*k?O9.<zPzEuSBֹw:OAyn]4K>p= ;-%\?.嗯J[>cKAݸoy#y:9qI6O?WH^*SS}xw;U:uE辋(n0;슉S1SϽQ3֖c#$N; jaEwG;9>}5S`u=ܻ^=u7 ;==:9K|U[։WtFZFCT9ku} vKN{~Gw,sY};s:RUv{-B{2UM7y zqO-n]3o8RaMuUo_Ӛ!0CUq鄂d9ĥI6pԯN#XOmǘSoH_q翪{5TTS{4-[K[\9AUPi)ci鐴07,Y$ܥMII)Z~Åk-/& W&EX           qdmr-0.12.3/doc/fig/dm1701.png000066400000000000000000021250561501654372000155070ustar00rootroot00000000000000PNG  IHDRgAMA a cHRMz&u0`:pQ<bKGDIDATxyd]ߍϾ֩nݭ۳سmbcv(!Y EQ#NԹhQx> (^EUUu]Oz}0q88p]7I,'I$E^("p1AAyC A,0 ,KT4܏'"l-˲UUa7RUn/ Qk$IY-7n10d-Z˗͛7ϟ?zŢ(_|jNsƍ-UU/\xTU][[tG`jUU5xك vvvcY4M];NUUO?iCJAJ8,EI$q1UU }^u]E=σLsPyvxXݎXu]-i` ^fb (A m!DA((B.EtU@eY&I *B0bp$I|~>|q$I:uʶ8mۖeڵkoy[ lll lu}2irLD4unEQ8;8=z/YgYld2j:8^X, )A2q"X !_xumn@YL&(il6yQF81Lt È󼝝?^fypp0N<ϲb@UU^+r5UU[V;;;I*IaEQy!T' h( U@_U;%IQV`Ƙ,|WMx&Pi1EQP/"((:}4L,3M^C&t5۶_yqL ð,K$Ye۶yKj#$IqOS1&_6*7  '^ /|QQt7 ,KYu]\a9v`9uc00X>}16N=ê/7 :(APϜ9#r[[[qٳ8`08< t:5 nApo" x@ ,LuχRi~Xe,YU(Y!PU4M:h(,˂(( ܷ,^z۾n߇y$I|{{{mmm8 l6(ʲn{( ,,!>LA&*i ُ$I22ৢ(:SՐ-@!ʲEQE|cl2n[X(t:q,_vyYan1LE(8aXIZ(4LA< E}UU%IIl۞L&qCd"s]ױ3XUUb{ iTU~ P\}_ӧO+eY5ZN$anw6Ij&t:F1NAAZ|1/qu(r]WUd7p(IR^7 V1nܸ yGQ뺮E, _ A N|0ˇT=#FTe$I8~AqWU8,8nBf31Ɩh4jZmfW^yEu44ڪ̲W,`aeyeao^G경=z!,*[KA q"(Jq(i"EQQ/(i(JEpL (fXh4˥(;;;~cvÐoGQ8͛7 $h`23ǚTbnYdJAtvwwooo_|ʲlvFnE ($q  h@klN&l}1ic \#dl6>MS e)IR$˲eYxv4=<<4 q]W4Mfi׮],4͝|3(>S ضr<~TUV$I(qehDA uxMdY::c۪#%4Ͳpxxxh4fYe,}4 FQo6׮]S+8B^^^F Ağ7q/4.s۶Q量'[A?s\!DQ8x<ڊ~@p+zeYykfvZ"~  {;UUAer<β }yz)a z`YUUuqXA( \׍HQ{ !0^|E]pƍA0UUMbnϟ?$̙3Y h  h W<:.EQD90P盶`ى{PU6dY&iyw:]קi].ai}S >C/$D7I$IEA(,C, (rAUUieYiʲ޶ FeaUUty$. ؒ0 ]qaky7nZu\AqA'e(X`62$  D"]111<u]ul[!lK (Jn4eYX,ʲ|衇]iZ^ܹsfܹs( w  @ĉ`M/v~1P3m?{;$@UU<]@ z^eN &wݲ,/^vO>vO:e뺌170 766N>-`0HҥKJ!]׃ AAP )"I(-ˊZNQ' CEQ,;`c,aaYP۶(:s(7o40|^UzeY68EǸ$h4 N ^Z-,cD  X( D$Y$IwA1h"EQ<~ CVzp:>BO&H ׯ_WUu658L@ #0 'If34h4*,k6ٶeᡪDAq"UUIT뺆aec cKWEq?: pecx\SZ50HUյ5۶- <}Xof6Ms4!Aqߠ ׄiܟalEΞX!aHTQF1DQ 0 ƘzV?SN)2DZm;I7n9U$sY5MQ[AѠPUUōt*8QfVrNDA<<\.mKdaX#L&r" &DP0#GY1Q &1-417EQVúT1?aTU5ϻneH\E€Ͳs\߇ܿaX[[[[[[qÃ~"DQ|Gb"}S >C ^7<EgEQemc101\EޮhTUt:`mnn x\.C=iZm6 jКr r-۶0t͛nWӴ<' 6GAJ8AdYVE$PEC賕__V(ңP5Mc%IR֖a:ujsss>q_kkkys˲.5M˲l<q|ppl6y,c4y\ JI_SEEp cLUղ,}߇,ˎ`t80 7(A ((|kQ4M\.ł1+f zoy[v׻xbmZYqyDA< A,KHaVc Oc3c Ξi]gf$IpAl6S^뺞繮۶l6gYFznv$[[[{{{vE1 ^\NXA%RLAă u8?x?_,f}gA&;0x<Ƒh Ȳcy><* ekkk1I=β*z2cQA4 DPG(J  a\ Yv{>C$iq+(yYyfs\BP˲vvvFcٳf3˲fy$Iah4,IqdYTmgg'˲^h4f x A_el҅OE5 /r51.AHyq,˲(UUYFs5M`meYbݯ(L0oiZ՚fxߢ(-= fFUĆa`;l6 x  @eGQ|dža!{G:뺖eEQeYa˪Te8>c<67o;w0, pN: @0΋i+˲r)Iŋ/^xttyax J8d,Χ,z$Im۲,EQ$ɳ<v'>i,gYE, È84Ma"E~_,(~UUz&c,r2766N:/ZiZ^OxV={oͿ9ʲd]|y49s(EIau  #YXVeUUG~~4MI30 ?򑪪0fpo6AAyC An3y] #"HQ(dY$)c˲0'-1fmۊAeYeq#HuQK~EQ0M,K 4LqY gRU5M|~u%ð?88X. z`  8! 01&`<827kbiP!!Xl6˥aW\82B%04 &BF#I^z]uEQ7-( ~6Ağ+q"(B"(KӽEQ`MP#h6űLe˲8j$Ia`+|\!REQ`3iZE E1' ZEQ$A0 ÀYA׻߷  q"A/rܲ,?Mӵ5XeN'2|2LӌX4}_UhC$I'b< 8,"(~i1,bEQ4Nwvv&"Ē` Q;( < |n=)G՟<?Fp04 MӾA?A`B AcWO?}K.AQ3ƸVbG$h@U}Ձg5OX}`' R!~,+8ݿwn"?AB Aw zO>}8?Ox7R@Ӵ;G A nc(CRvg6yyeDA(q$$Ia ,K˲t]GPG?!^hFUU37]tu]Aq˲(oy[t:m6p^sEi6iz,aSL&`"ӀeYVe%.X_!E^ TyLD AzHKC/\a :iB,˪"$I8 ~,õS,˰im{K,z>NkyiZ-۶,˂Njm?qeYbd2jo<ϻMoBlgw*׶ayEQVE1c$Eƣ(˲V1ooo#^ڂʨ/KuǑ?o |;A8::Z, ܹsfs8u40ư/DqUW8U`!IE|AeV+qn޼yEYEQXwmmmk\A dz2}Xjo&,t_Ӵ84m4A׎~UUpLer, (²,u p6z= $]vMQx#loo'I2N8nXF&IR|/2yc L&EQT\.A2cl>7MQ%I%l4BU0 q8F҅`u'lZy_vnoVCA ۀR5dny&IEE;`P`'|> 9:c gT.c SyCs~W^}ߨ(J{ǟ|V8ŋ? m[UN5cQ=SBU'xj:)"4Ȳi0QUEenY˲N+'IbfZڵk=ޞIa{{??F %ATzwݏ<իW? a/KAS²,Ed=9]VπؓG $=P7n}[yr1_ieoŋ_|EZVٜNeYnnne/}ܹsP"UUl67jzq27G)(4<y:u… ԧ>c=|WA1=Aܝ4M'IYя~TӴh::4*pA/2.""3m)jNHVs}7|d2yǁ*}|p4Õ+W;MozS$?xXvmYVEQUEM FegX*IRX~R4ǒ$][[c  u;7jccgj}}= (` q,;s{zVY=bYI[ǚ9L藾__6 c:VUc/|aww4۷}۷-daXrh4TUY!t<sQeYuIDAVg PE$ȝ'߿pႦivhpz~l0AAJrXXe7}B gq ^]nVBv> DQD0.r0v̯[ծ_}(I @b7]zOO77D… At:#؆nnnb?yqEq6={z'(.䪪~~VJUY]_Zy֮M&8MӜN뺮}ph4ʲ<:: .~7A W N*\GT+c0GZUUʲĚ^UU`AH= CёWi{ua4eiea(9aVV`Q!FF^n/|=h݇?S???;}| $)^xl†_җ_%8y$gdY-I uWW;xJ.{<;NQeYɲ:B _(M?>/xxxhb(ggw?D~} S( 5x,K iП`v*v۶5M;{ld^h4:{yW^}[޲X,y衇0 4r}O:yp8lZ|.IR\,ë16n޼p8_y啛7o:86oFe,}XOg}vgggmm-?/8 |oے$ٶ]PZbQFSb>ʯڲf+,sf3u[J[>i.u}mm-|?Ǘ.]Bg&|s=|_~_ _h uUUeQJ|h4uEQ4F-naG]z1>׮], IBA>}z}}W^Itsss2lnnyƆy/ҷ|˷C_*iNL&NveYg?K/v$I}F1NE],14حYqeY =$IB&^re>뺎V0B1 ʲ4 M*_~AB7#UUc1eXa`B7qz=).&z0`t…[;EA|!O @}!<%cV)½w_J=L,<_Ub@lʲEF~MTU1$  ƻ(JA WU1Ƣ(ƚWs`! Ձu_iyC߯i{.!*SpG8`򪪪AC`\J_ qXBe~T0=c/xĉB8AS!^fQq$IeYf3EQr^j5MN8 4Miߏl"8cJҥK777x6!:G~c+(8F$$IȋPMIU4M}ǽr]7, _3x0^-K0p1L33q4MNVʕ+UY[FA\8wGUeo ,|%cHbPF͸؇x]Ur$IQaN>$0 1d2ceY^v 41c,}M1t;I8xʕ+fSUUu$i644͛h;4b?֪$ILdYW.1R=nM UUuic`Qcύ E@jQ16 :eYp2AAB A'4ILB.t:嫸]Dؐa7N0 4׉` ,:}K/wi[VV{-1Н1g~`Pm(YZ"b=pX.$If%AĝP@ĉ@!4\"icL40 A 24?p̄mp q 形(Om’]hʲ,fiFr <@ZM,&P4A)硴okCb X%@y 4Vu]I DJ=&/qxlxxY[,4 Pu];߬:(4MP2u1Be~ +B! F Awnk}۷x}h\rģ1(뺵Z ey> cЩ#2x#Eh^P &/حw=~5$nXL8.rɹGrR%mc"]$Ijyc1^7k4HUUY]vipZLA 60~q w( 8<8J0 يS~YtݗM~VxTyd9`~ nUneq046<ϲ Q;y%q6bhl6]yӀרY,y\Z4M"@C ˲z1Bq2h4!bAy_<-48L&,AP@]w&2TQGtU뺦iFm˲ *Y:w޸{mmm>[V^o6 $Ij67nt:za>իW;Ѫn޼X,<ϓ$ =_ͩl7nԓ$ Kd2I kkk(9 Vagt$:b!u(Z  F AZ19T4 ^ja4Ml ж,bmll4V(ٳgQtmo|86Mڵkfq,bzx,? - j=L|W`C4/\pppa-^= ) :!s:v oY,l`D[a LYح2?c0 ./"cI4mZp0={~ /A<8L˗/K/T?"$IP}'=LlxY*wN]]u^rf5QٶX%FaeVKb3g2 b ]םL&a]~S.\0 ,;8" |h:rx 0bk|ױbhzQ?E C4@0 Ǒey>[U4'u,BNh4:t:窪ɟ1FA_OP ?c1;hiE߇e>*E!1:Qcf8k$Ii*\~qEay{X,jڅ 4M[,?q O /3gz _}xxl6p]c0T11fa٭XL[raoim3ק)?8_zղVa,͢(7 fA8}eY z=M-Ӓe9^__Wǹ6!c\_h~wZM!\c-G#n7MSn,ߨ,y&c bY3Pi{Ϣʪ*=vSQݨbH^oZL.u \d2ֶpj1M|n~; K%A܆(E^0}_[[ckc2b_xq( UU-Z,s8Q)}d2qbUVAEW^}ZC7nܨ/Rl6aQUx1ha"AE1M!;`u;NA@1ZƷBϹó7gw 0Zp /Nq8y뺎3Ì4M؆L } Q`ڲ,\ۿ_^hiWvqNt4riFR.(Ѝ4Oy]MSo1h{{}̜>%n޼я~==, _ k %A(/!pGu_T1xgĩ(GQ?Ή.FZhz%зȲ j\rpNu}6A_L"M2!`q*(e|4E9pH\Ӵ$I%}aͲ}۶q%H(Ȉ=/RYW^V 7lv:O(bB<۶Cqs(ea\4EQ$Yʓ ,[)NH#(CY-˂+2xd2쳟}'H NH^A_( Di4e~ Kԃ8^.<Ō,: yG:LjDy,CLl &Ak_kZDcʕ+fvʥ@{W`(bqt;'Z^Ox+wlu CcH0Q!cl.K mos]nH!eEQaV²,4AC_ (B8$KEQy!z^1IԱ@4MsfiA7qWN7mllPO5 nGf=yOu]wwwgq]gHnn%Y'S1Cu^s|vb[#UOO6k\ ={F1G%N˃]=hAwXSqDP9 _xm{8sqV~ۿVQ_]$E)ɒ(d_g>|teYM7x'}g}矇 kwun5KA|eP@mLO'QYm7 (]E`fv+=_ݳlkU+D)nۻ}(0<::, |];p<(B/T0E,DuVcQqur)TY㘦\.p8LTu*^_rFQZ/|___[>?Dhܸql^t^?cY>>EQض~q<Ѓ+c=1}W&2LuQs]{-gl1s]3[{vxx /+Iŏ?#?Ï%A ?̙3Eы/~,/A+zySIv_ȗg?WzU8kX}rUqm1y܌W-H$OӪl^}-WA4MerngӮ+q\.k]Ȳ$IPnK@UU4=}Yo޼9666,g?3ϴ(l9 NEћfyއ?Vtmi&0=~~u4V5ͺ|>vZYF;;\$˗ t:IUUM???x׻8G>򑪪08ŋ?oY-A T_rG Rr;?O؏t:L&/ny( zx<-c]Ys\}-o~GW&X80c냓l@1+""$IstG#{pS~Up1ښya}&/䪪;ȐheYv7}SA#x @z< 4Hk&&AUUV(t:Ŕ3/~`XuxL&fF^ ~?s?F 2( . ,]('?@%x2`B=UU hZ3fY^my1qZp8l479tjYTv,vUjx<B|>;,qUUabDC(1>wZ B#nf<}ßdW ~UUCOA x`h61/&GL&8yǍFna7+؊ju#2NSaSǶ#sP%8#`%v[r)2ƚmyhַWW{A qBF=B^MӠ:mF[!EQ&SO=5N;UU_~8ۿ]Q_~z)q$ۃ58zٸE C6[rL&AbwegN4Qƾ*`}Ӹ*.uz @'IbYVV ~H?6-ʲl>3p+pQAc0bX,,=dih8ޖuBŸ0DnZj4L|!0MSQ/ /$q\Şs=wppڵ7MUUy3rX, 'au]M"f'Vzn (0mGt*b՚爀 .R 2`9AbuZ!cߩZ}[;_1 %OiUuW$ x BB~:%A_$"6Pԧ>;;ׄ?QUUMrMleajqɽ,@0su4_y啪z^^P,&>5M뇇HnQ!(gZ0 MTUիp  inmm-N>%mw{1V~~ŋXj4144M0<&9)j$!al2Ƙa(bE,˽^74M[,t]] FH=}UUdzg0~ x#Ϋw&Qcx: q{ yࠚe?G~AD} A|=QW_ C c"_QFp9N<q c rT9M,e9kکS8@l6ǙL&aʲ IynYqayv>u^_rv/_~饗! [@ _{|L2gp^ߏG^e|y7 T5M LC܏Eld,KlfAm"}u;{W湠bwԻFwFBDb\Yw}w-A!SO}S?s?q)\?<¾8eY}?"Ƙ\Hڲ,腂 `cQwvv øq,˧O6_h4Z__GiIY5`$mQEQTo޼{^UU7n@u]>>;Pe?v+'Ŏ3I0):;՜to89d?Z h~4 EQ8l C׏ PǪwrk^kduw]Y $NS- >6A nW^1 'ߒ@ ^ϯ|bGח&2 u%&hgY݆Qgoo1viƾg4Mr@msi6h\.m1bruc\ײ,tV9wWIJ"EQZ$I `sjcQy'˲aKh4 @Z 惣( ~҅j:1~1RHzf>N}ttk C A(JUU!b^neYV΃K"VCZy(c8,jh}}*v\_y9eQf}˲$IJzITUJ]$ x_8N"?9i#$7{OV14Eqo7!۶FH\`r,JEi,afb@oaD+M{yӮ%{ɗ]MKJ ^qBs[Vc\@,MSYݎnD(T{NY AI8~ٜf_v~nc!1\Aa?f߈zi4UU32 0y [|JOZ]YAGiBK]0r zO>͂ eX 1,{>Ax}nZ|l?s;c7}tzyzG,A DAF( 6x- l^ aߎ~պqX v=σ':۶mvQCܿ\.Ǚׯ__[[;{,qcW^eԩS](#Y!`QסV1Wj<_unEw\eA9ZP|0q qyr$d#ya Vuu<*j58FJn@$$ tD?xBUr,*IA<@P@mx̧ }PgaX1x*Ȼo`)BVc,n3ưzLUz(&c [ X,eFS 1ma">6 L{ѩmې?u˼9&DQVU5P̮ $IQ!/$f3DHQp΢(WX?WtWci:L$c_A9D#rVR} ]Q.$ p  $E _?V,˰Lt4|ntg9Y;<_.H8^___[[C(t]c.yZ<oܣ_vGp}@"N̙3 "c,".^X@ t:iZ|3pGqMjpDEQvuDYq [c1iϟ饗w]pAQ۶'I^O~d2ݽ|ya:inoo!LB z/9H-KrpԱ2[n <3>4`QeY?#A o-`{zg%zjx\1r)<3(B\JӴl"( )a{{{i#qP1UUmmm- M%(˲qz<#83BpAhR|~ܹjz}<_|9 C۶qTUjXmvԩk׮ɲGn]4Ej2eYuI\׍??y hZGGG;;;z]Ӵ][[4g7n@AZ6 }oVY?- ԯ;2<}vߙ{ܽyRn_8}.EՓA %A"0=2tA.wFc v+'tA,˂U mXՂ }jz, vzUUGGGG=>SNa)R(𾌱N3Lpw%ͅ =wo~K/O|b8ews:^~^?SGGG.\pʥ$Ic ay=BAodcc㓟c=˲̙3V+Ih$dssNMO_Dv4 t$p<{{޳wʕZ_]ճ:OjEb'ཽC xP@mp7L1xQ+@ ݉2?4<@`WR i*e.:b֖1U\z="ʲۃd2q]̙3f}I Myv۶y|$iccc<˲{{?&0Ο?$ v$#ϲOOqC ^h~t:?SO=X,~k^@k8hSē* TU5 %Ac!z.'vfa@eCCVn> gO$Iq8d%|5bk`{|%IF ϝ;>777'I( 8.s&Y.]|w|v^GW,Ԥ%I ~x@142hضQb˼`mGGG[[[ϟrJDva.t Hk4Mlh4(xzO0wwGy'˲#'I`0$i}}=A+9=8ktmY(׮] UG[1XiOO.˝|UOH^՗c| Wtzw J8u+dZ9n$I [;EafEQeX0jZQ1x}v]I&ɍ7,RlZ-UU'bu]>.c Qz/ElpQ0$8|>/N5<H$aK$Iz˥{{{(m\CJc[4Mc~g~gpr׮9v=b 束c?Ec c1A %A 0Ymr#(ʲ78!/{ Xb. @ ,8,z81|ǙL&| g i2\UA9ʢ죭eI1 McL4E>#Hi X6 ya!Z[[3 c\FQh4p Y+Auv]iqeDZiBelOeY4$uue_k _*7Nm&xP@mOú,[q8"fp;P㜐!(Qiq<6Mj0 q(ql6:sM0"q/KIX+DZypFnEeU[Nle]bh~1g<+c/s8#E7<24(2{ ,.h:;>q >'pY ,0tIDATͯaHه!e'5 J k6gyDIE?81݄զiΉBaj8m 0É<ǻv{XeAՒeQ`iixlaa#I 0nD2M.xs`3ac$AJظGE _;:<.L#9f8vȅgYAJ ^F7($CuC-Ust]}0 Dpv"lba8Mz(ip* \u,KQ<l6SUf3MS8]W-vmYVu=LxLjj0 0 _68F۫k ;p3H 7ŇV 4Eի3Gx5&= AXxt_$fhAA_W 3 ^/Ad./Aq6"HNEE1\QEv(q9iH4˲t]t:Xl6cecSrGmu}8FQ Γ{gz1pWc^ϝ$( */Lp(TLΕ+Wk  I8ɠ:{Y Iyɍ-([Xc?iǾE)HUUe!4vqd# Gp?mۮjqlp8<::iz8aj,\ׅ}y7V(|c /~q&9l~̝וp~( #/r?{ﲚ7deUU9FƾcثF9歒$- F A.:pe9^ǣ )\;VV`/j h>!BD(bŚFEiڶm͛7l<c׹s˥y^c b-ixX}62W. @( 6#⫬c|,é0nEQO1ȋi]tea-b}0TU h8@(E}xC^z4MEQi<(l< ˥p{eYNu]UUrda4 {{;CX |Y-'u k! .1۶eY]_'\__G~upht\nkð_ޝnq{?XV[[ pڵ}c.\T7di89ZFXVAv( 6ʲ}_UUY$'\Yحh+ס؎ ,BЏ!T. }!Č1 uah2Vʲ4 cX(b`0v^OQ(.c, Fn$J@!g0dY&2+WZWF_җ]/~իgΜ>s{{{rcr!pW%|t]߽y޹Z__ey\.˗_~/A[[[1Qy6 %A4(‚ |NRsWH11_̕"^'6m&x]PՄ u0 (+VMWA="SU0 $I0B{x8iW4M\(qe8Nl $F,(dY_IsQMӴmʕ+XX,<_,ZͶc5pIǜ@ ^,Ο? O>3Zx [ *4  ^/nll0 m۞L&ut ØL&(;w~a&IZ-UU-F0&B$ZT<={1{'<\~-;&mJ^$u=?C?tԩ}s,=t:0?~\ʲ +Vg `zf\jb,>gymo{ۣ>w…hwCϝg1 | x-P@m}[`1] Nn i`<#!&2bo[D¶mD FzܹsaN&loo_tɶ퍍ryttia=3eyttTU͛7d2 1qEת z+{.VbD՟?O4M-Q0vF(( {٭S|'>s=}Ca`sM+, C8NC A^}Qͅ :ǗvIhnaJ` 㙢(0ŋ365/M`CEQ2eڪnooce>ynfzA n*DȞw `X$|/s9b$IASPuC?cl[$yki^j|pL[[[aNSEQ l\jq|aI{B;σۂ l+nYQ! .y>r}J]0y&IUR;'6wn3X!g=Dz9Mӂ k7Mmo{ކOE/|Mt4M;wC J8l(p5 rCeAc z!:`*{ u!BbL s^g)u;{[uI'^M  a(0MTӴ'xVApԩ?A9*" ؿp(Q&/Q{{N'"I X,Q5 AP㡌(Pc@'Ώjz .\pYe0`yB(.˲8Nj![`a8a.~?Nw]6~n}L D** *@DiCRIHdz{dz7/ow]=~-s-Oj~5N:tFuvvN733zl,0bG*P(tHJՈ/===6m:pɬyg9ΪU|~6x*J*|>Ӊzn{^yfPHL&s8"br9h#GVkcc#Frdz^h'& JO)ƿu`_10>\Q\.w՗^zV5 :[$H  ?e |D`0Nb1<#E@Lu[Pʃ!fLW DQHo^<ħB4x}) DpqYzT*b10wp 7`)a].W,c2fL&RJDt:]"l ٬nXYp8Њ|>@fZj4M$e2|d'VT\TH}ARL&rN HTSSdB jYZ333v}jj* e,j*b|NWMWۃ|`dl6DMMM]wNDw^ $H[ $T*MNN={vnn. bxArklll6Xx&''/_njD"pxɒ%x< q?.T*jBN],ry0Dk{Vtd.Or#H]]@ Buuu'EP(L&'&&Μ9L&ǥ9tk.U.S*5(+ ZmZi4Fꪫx $H[ $OK/1] c`Z )ETfW d2 `cPOןT +NRyd2+rP B+L& |~\V*ljUՉDb``vCD@>rz^$m۶ d2"APP$it R>Z &  x<l6X(&SI>KX,H_ v5 A3Tj&FJfQ(\><<ڪRfs hll BA@;r'''QCހ rLT*u:Ryaaavvv~~VHI*3/_s= GU.\[R?:}==#ÿ/FFFn|\. GPFc2,JR$Ni.KQ B p8|>.fY@bp\H$\pp˗yFb0@ZkL&z:}0ʃr%KJRmmm<O&~? trlDsss0rD,]b Js*šP1Lfy<^$gQ. ø 8?FDD"&i۵Z-Nc^{@ طTnjJ6 $H olH;R 1D"l6d0DVNV:K ?5#H">.?D dx<.e24 ?X,;gXXKG.X,vi4T*E|@{[{4 555xbw JUyD"d2cxy<^0r, fp|ZJ~x(%I&Yc2.`HRR9r+`0(P=Cv `(LFP96]outuuA  .-KA,]it:rX,NWEdG$q8qfGFFj5t<o~~P(Ft:lZ_njj*Jf3t:.d2K<oqqC|RD g$H Q T~?-#OEpW gs fDGcRf].T . DB  "ʥ2AR\.;N>/tnR4ry\ĉKbrɤR,Kbqqq1Jy<ݟ`x^٬T(%R cXv}߾}*jqqD".ARi4BŢD"=bdB0L ހeNRt~~v7559rD"եRt:hLH$|sss: J `p؜>f EX\rq8JW߿nݺ7괺BrJ x<^,7nBL*`0Ba$Hk )FL&}h4Z&f_+t=Ty?4 YDYAZ =6L} ЏfCqbqƍ,`0V. B$4HB&P(JrlldjZPxӧO_tEL(+ D" t:}qG t:=99 hr &l6z\N BbH$ry<?qp8T* Y]]bd2 HZX,BP"9 j&z=H&> 555. {{T*H$bBaX8}L&h3333 Zd2JDPTUU!Y"ڨ)Q]]P(z{{WXzﯪ P6P( .kʕΝc2 "ɜ;w̗B!CaX|aTI A A~N:5>>%k&rUl3_ HSß|0NTaгL&K\|>łg?A@TbߏP0#Je$ 9Ί+.EJT* h4d25jM r\PȆ̙3|n   E$ wyF \.ͥJvvv8q"|_NCCCA*bH$> x<>9919t8Q 6lP(Pn5444?~dBǗ-[fXV^m6qܙw}`0X,FGG B BA...Z,R$H0N:H$r%;v555R)hl*P(l6TlhhΝCK019iӦ7xC.^:FQH#TJe0>Fn\zUKKK<'Zmkk@ ^nmM"Á۠7 Uj A H xJfJ+xTS" 'J P(J`Pj*FS*L&-fv\.tB@ FZlP/}Kh`0׿|b1 X._~)XVGGG-[*]8>>+$'O._\.,, !;veuww{K҅^( M&믿P(nݪT*Ϝ9nŌF#ZZZl6ۯ~{gdddnnkJaaZ]phСCaݺu. !n4^r_kRqllb@ ;?u\wJngϞmiiihh3t:f]$`08??b 588x`^DR:fN  F)pġ ky.n&b͚5S"0Lt:}ٲex,>|xʕf`0oKJ A6$H@X[ ux` v+l6b1̚.&Hǯꪓ'Ou] Dĉhr+z}^:s|>a[[[tFgٹ\ /P)R9|>X.YL(7p%\bZ<800lN$4pbhXbO&s\.WRyޟ'===\GGG ^o[[W\k}_Z֭[LK/$HɤjH$`v?~/^lX}>u]'H}\l1-[X,Ci۷˙Lɓ ۷dXB|333[nбc:::<OX|nW\Q__d2C px _|ţGp80$ Żᄏcǎ.żk׮+V@jnn&d2d >`0:::vܹtRO>jmme2 ⪫NK.tɓr RtffFT9{,J={=rȒ%Kb\.߼ylnmmI Ao$CTqT޳gbL*PX0հ'pp΅ J Vmhhhnnm۶H$:22bŊX,fZpryjjJ,kݻw7E E$|uuu|b \.W*rF)͂фdBVlssJz<bxjjJrllH/oذAV [lQ(sss333tzllh4_җz-[l۶mqq񪫮Zr% ;w&֯ehh^K׭[NwEPT*JjDVRMMMw}Sp<L&;sdhhxf˵sJ*BOOx<555{=|e(exdu>  ={d2-[L"LMMMNNVWW777/_|jj*:αoO?tOO͛BD,ٸqիCЮ]vA%IKK^yP(FϞ=t:ܹO>iii[>製{NNNJ˗Wvsͺu7|s޽Xtuuɽ{X,@r;v\dɑ#GVZ x<Jp8~;سgWgyHRrK>w/җ_~' *D $A(tzXܻwo4eX: Kح[`#b1x>:S*&''[[[@mmr'''gς[d|M<88۫hxz'&&Zj={RTTPR):P(d2Y$!"Lp8<88qF b֯_L.<>OT⋿կ|>ߖ-[Ov]wmٲ駟~7fffFGGϜ9#7S,ߨz;wOfF믿~w(Gr[o_ 7O< /~,KVoذ믏bJW^#-֭[m6:o̽`z饗z<z}lll۶mv{R###=3X5_Wt?Sx dX{j;v eo~S&\~ĉX,l۶mJgf_[Bxp AH H~Bb1*D{Aס]DU+q3@P|>+Q]] t:;\n977}ԩSH xwBUTj'''Y,gcc#N0T*y$mQ!>c0$$p8D" /LBPVgYt:~6=vثzM7JC_|\._r%C= gaHCC괺7xctt4No߾x5oY.uuuwE"v/===\pA`W^ѳ>G]6_WUU꫏?x8~7e˖9xwLD"ARcأ>Jďɓ[nݺu/N߷BcQ(H$](=zwމF7|G[[[xvd2GGG|>:$  tFSSSZ|>ŢJZbϿ[gggJe}}}&X,|BX,JɤeXX▖z#^ltT*OOSg,{ RnRvb1@@^~eYAXb۶m>J5 P(K.ry,~v̙oyɒ%:>hTniiF\}߾}fyÆ ?p2t'OJL .ù۳,h5 =Rd{|>?CV?Lӯ暙'Ot[oUV/=v]RB!(F,-2333333=5}_bŊ+x<~駳ڵk B y?? dؼy;lٲnݺjZ,_h^(J.kjjX,+/VաP(_qXL|*jbFR-: $H3L$H|!`ZkXrnf---jz߾}F-.. E,ǃ d*JtXf0a2L&.hT"x<S(֬Yht:ݣ>699ymrիW+iP(HpUhN e0>/bǂ5L&d2F"RT>Tj6BaӦMh4`0 P(lhhd2fy~~^ӉDRpD"1\NVcNX,RB0 m߾W\__Z[[vO~T*裏~_x}ѵ^{٣G^{O+WtL&{) w}wCCr-<LFp t^FzNꪫ(~?b1JEQ'lgg ~O>䦛nWiӦo钥./|~,j===>`__?yH$9RHNF2\.= $^A6$H|p8?.]d2kךL`04hd0``T\xP'D"JRA(O T*e0`ɣe0H`x/<Z (ʹsFXl6pKHu\UUU]]]t:斉ɉb 4M6e2uuuC&L&#puv{[[ɤbɓ>\.^B!4333k׮u8{DB(aDӣP(`0fi4$pX,{o[[ۏ~#$y<6-JM&Yx\ #y~~+_huu`xB!njj o=dÆ B𪫮:x`WWW\NR 㥗^RTVmmm ]]]T*uƍ6-466K.FOąB!Y Ad@.:{I333 #z^B}̙M6Y,\[[-K]]į\.wzz`08Xi4!@d:n||^z׮]rkqqP(455̘fHTUUf"CP`2X =rf d*n:VK/}K_Zn/KL&^H$۶m[bo],⊚8p7oڴ)>3&.ٳuuu [WW777wYu7f?k֬x!z] %h$X(wG  $F<|>?99G H$ǓdWWP(T*A8T*R \XXFH\.={vժU~_,lr KXc d2Oj|>_"H$rNχYH$D"@RT\jzowݏ?p\ve.kfff||\  jf%Kx;w~__z??x<+W +W.Yק.NvS"rl>_~ܑ#G٬@ Zpxffdg2, d2piddp8 )Je2'H)2"$(1bO$PZ#-A% *@bX6c3 DrxBs: SSSljrP>_oF(ڳgOSSӟ'X~x< ILTCDBRqˀ $/lHL'ɾ>Tz-:tNg2:z= @ v;p8ǏfZP122R*4djL&v|ؘL&K$>aL&1^zu\VpxhhHA\N)??ٳgٲe;wD"]v֬Y~[oMLL8qnSճD"bMMMk׮{D"jժcǎit5x^yO= ^{mT>p~+ kd2d2l*2H$ 7:N(\. C9 {SʩT j j@|ZW{A+5 A JBK|QQT:1M|dr9!FhB" >m3pH8l6.L&b@yBn><BBhd2xV0A0bPA68Dr9X}BkvrrgN& CѠsxBЪUNbD"At:D^we@ 7A6$HZ˵k׮޾p8 ~XD"(`6?;;R\.rP(B!H$J0វdXZTx>Fkjjt$ 'J@RZ6Hb16 X,v@"Ɖ JFQbB^Ofnn^kkk{zz 7pæM.~+V[/vmwիy̟~?P$Ojllh4]t*Q߿7~W,^>{饗w}:IbBh4Z$6dI$ݻwwww#x!lڴI.D"aX\nMM #H A 2]G}O>E~t6x ©Sz{{FL__BzON&eP0dqqh4Ս$i8xX,\p8@jiid2``D2hJt:u:]&$b0YF`2^/A2naaaffF*`b۷p* ^w߾}ޕW^d2񱱱[t3g8 /p7tҥKwxVZU,.>|x^X655EtJ&X,b遮 ~AW4 S 5Ky<\dr84{bQ $9`.G?P"x\.U*ʞ :4 L'0O[.&`D I(2LJwaX|>G&f3D"nT*dr\>ǩzDX,b J%,m`$ln#G|ᇑHDR]p`g]VT x"~ɺ3gΨTɖ&$H|A6$HT,ͽ;@vsss@.x|D\.Cq]DBfffb1EJ$BAY,# Dpt:˖-+ׯJ:NR\RRm۶gYk׮ݾ}ʕ+W^hPPbzQEI'&&&mVUUL&=zRTCJܼFxFNۗ777?s`?P㚢 }JO o*rofRxܤbN\.h3 N`xºA_Q9~TxEL&֯VUh/9 è rjAx$d28kDRt|jP*u\.R;T*={n{gƺo?x|֭l6`0T6 $H|A& 7r Db2P(H&Wu82F贺|׭[gق`kkkTRGP555tۭP( 3(!H@STZvlV,HAOp8z|>%tM<onnk Nsbb" j4ϗL&be %J[ntttff|>/ o^xnz~矧RpXվ⋩Tw]Hfff6mڴ}~*vOOO?۶m_yW_}NP(</ϻd2 uiXf0X L&vC%,Ɇ+ `/v1h`}{MMMhJhߏtHLMM@@qGGG.l\ I(J Per࣡R(+yf 2_?! @O^L}2JA=IR$h{r9d2)RT$Q*t/'0j2IR ~fY(vvvo'Nr-mmmXzP+V|_ $H 9 E BR_=  l6 y<^mmm6 B2LKRxg(wMPuZz ps:aH$Pxɉfժh=lZ1.h+@*,q 0LtbyL&bA.|/..Xa< hK.?^xSO=裏߿h4ZL&xWW ~]׿lhnn>u-[vСӧO\L&@JRVcؕh0:>;;[[[[.3L&8bJId2P(r!(JB! pr7޸(JX,f2gb˱(@I@q8l6{ʕ@ H$pu8r9ˁ 4$J~b1 *op-B[l6]Shp8%_gVm!hE5uE|(Ilvh4B`0^VUUx_8P(XR)RDx<fv/NdYĄA &g~?XGñrq\8fb[.%fW*nX,NNN{a2 ph4TP[[RJs JX,JRP,.. r544l߾pp87n9SOZn!DH${nVFqsss?|<{e2Y}}=ܓ EKKK___.H$L&l6piF|l ER{UH2hXLTk?b1hT׿R4(bo>fُ<ȪUPvc";H /<!O X \..D(bΞ=f;::ܴL&FfR)00:`=7s8X,D Drt:(fG`i@ڃd2I8HPc?fA4d2h4X,r333T+d"=ȑ#[lQ(|>ԓO>y $H  DrX2M."x@Z,f3@rlD" D" PB괅BA$! %X,` RiZ\.h,~LtP(*\EV3Loo#Gn_Jۛ|ѣGduu5^ RK8G"։ .[__+ ;::Ξ=7mzOM6vmSSS,CE 'T/`^hnnFE]7>:ik r@ oVrUUUӠMd5 >/χH$ACh8N T*U!T(Z|z}@zbqٲe0ASL&/RNTBՅ,\)BΝ;.] ?@[[ڵkCOJR87\. ao^0<xBa, 0W(/ˉDha |~,K&"(H`J <1eZtcLd2ODccc"8tжm$ ^/mhhH$-:FtkkpWW̌[b|>?88X(PoݺtpORe2YP8sLss3NjD"2, JpPjbRH&t:s=ys88Riɒ% S\.+.A w}2t:}7\rqqt9AAF _o C(+_tJP󵷷CDZP(R -[z6lw}K,g2@ h?dd2`8`0RT>%$p|FnL&֢V+|x\.l&>e6!x.l6{-XB,߿ɒ%w}_?888??yO Agώnh4Pb\͛M&S0X6(J1h(JRX,̃E";-,,],l6TL&d2/(H`0(FYP0'-..VlV{wUW!R&G.ommm<w\l6[Pd2P8<<VP(Zv||矿;R)ڙLտL&}rlzzZPA[[[Bjd"X,:رc[,UV `zm6P(L&*X,d2sKӥR hXן ^{ [PP=믿.'Br9UW744aҥK>;<\&\L&KR555NSxh4*JϏ,Yt:}jj* 8 |p2' B8* ^r^袋=1"<O$A<33Cӗ/_.H(y('DB$ٳL] `2rr9TZ*Bo *x˥Ri}}F~6??sN׻/744yg Ad@* l~'a Fc: 8П8q"ɨTBV#w`N\ՖeTYX\,Q2r* AY# &<XDHW4J:u n&L(hƾF011!JA xr*Jju"XXXP(XL" 7D!+ r4DC>}bd2e˖9\*^?^SSCPJ%˱X hH$[l)J}$DjߏNjD" .VmmeK ߅J p+6T烣8Ĵl^WӁN|jspl+ 744@WL&YT*d2 BX\j~#,P*!LX~{``@Vbh w)J!*@sd@Rh<\. {au <|k׮mmmlmmFAܢ }\\*..[FcNE"ҥK>̄h4zE=R<(Fl6&JBc.{lɒ%}$H _ Lbqѳgb1*D2 ;.^:VX,"BPe%  &?u\NCR*Pr[X,J@N-KX<ORmذT*9P(as0?p82P(_|o4p e0b8x U*  BHz d216A^`;wnrrgzK"@>}]Ptuu+.h4|~bb`d2i6{zzD"~#B!T*}뭷l{{{078QӃ*JR"L&.X `$Jjjj6nt:R0a B0Aq@OBpٳr P(ą +WDHx衇&&&0q P(tiP9$$J%Bsܹz'FQզd2r6nFC&jHuAP(*Ba:5^Xh3gΘL&,ͪ.]::: t '{(ZA@f]]]A $x<' {z<7b|>/jP,kƍvd,^&YX,&Dp/N[b0 :tJsY<իWWUUʏH /W^A Bp  F8~뭷nhrH{@]d2>#N2r|>VLTÀ7 XujvfY\UU+㎱X,VSS3>>w`0qƖ7V,/7 $H|@6$H|>7 Btt:x">Brv1u .X,<Էs8RH$lOO2l߾}<*H&P(UfPe=7iF M)J0 fX, Qwy'PV7mtwr{lk׮-Kh*6 _ Xf˖- ˪RPb61L[t("WT~mmmhd2Ad\bjukklnkkZNS&ZSh=OGGp E`P(\reTR*}I5\s/JjoK/nkVTn _PGNvL&n RtxAdikkkAsx<'|vQB M>cgΜk&'' V*K,X,,kӦM͢h`p`x:N d|g9Y_|>o0S`0d] fEfujjjpwp8|bihhhUUUecXUUUh3T*E.ARFNjjjt:@<駟~7AB~0yRzp8~?2z7|3Fۺu3g h4h "?5,} QaRpFvitsV*AB"aO h4@`bD"YdO?6+R26 n`s|رcvo< A 7 Pďl\KI-:-GE+^өhN'Bd\ŤRi8)l6;Htvvxl6v=p|>ZfbpP(FQ SNiZݎ30Y7L'Nغu[oL&-..*O:U]]=??D"`Q*oWX,xeXfff@gl6|>h3F 04L`466Bc B}}@ p:NsL&svvpTvR(믿~aaattےT*x(H$p9s jlvkk+NZ`P$Bx6 pQ*(Z. ,tEb.Ag7*h&iٲeryŊ ٜd$I.#D|*j.2|>bjA477h:\wX|Ϫ0ǟx 4v>WZ,/{ JJbQ(8r}]P yfшlD"hZV½%X~_6xT*VT*U*b&O:54455T*U:x< ▖L&c EB E Pnc4 @:4ns\* XRYVXB![\\_a6D&&&!,.kʕBpjjJ&iX,y<x_. BJrRT*}{ e޽ _uUU*Jf e{H$"Á p8 BBYd N)Pr[KP/b-,,-aǤh6lؐN :qAhj0??___O4F #o!T*t *JA foNmIg@Y\Jt:I[T*\.WUU}'W򏧧T'P(lhh@B|\nwwwh|>p8(;KRuu5>Vz,6nJ>;DAt:= Mn|| Ь3L &tؖE\RЃ>sMLL\.O$H  pX֚b(A9@c\.dҥ:ɓ/۸qL&2 x\&UWWBŚR?#ȋDfgg)Tp"M1HσcS__"BÓA$UWWCh4Z[[[,wڅyvUU|9Α#GyDx&~jJ$*ZSS3??/ n݊3g0ÆN:Q$:4p|>@kaX`w` FZcX,S @ #áP^D>əL`+^s%,..ٳbृHl6[__o0?^[[bvD"I$APTJ> kPD7޸e˖bT*DBa8B_]bxAk{{n7A|X,GdbUW]:8Lu4www ByUVQT$KS l6 s}߿azX,U( as᫯:N_s5`0X,Ù[[[r:bEQD255iӦ{mlqqR$?A0$,, b1@7 Aih^/b1YgΜa2ΝCB"ƃ $( $> ONN^zL&s||`\..+0rN3ÊdJRbŊQzĉDh4ڹsҥK].!pB,JꦦRlX,v8F1 FT*R(bAz7#ǏRL: 栶D+z8gP;2d2ID"Q 7aň\.RJRd"|*+A#{z竪ZZZWQ*f IDAT FCDTlb oa?77L&J%RL)#/#d2Di$1GQ,d֭[Ѩ`@d23/l~h4z^vиD"v?/$H/lH`fgg{zzΞ=lݎBǑrgff&''%KˣΝ;Z--Hɭ!bttT*R)z\.BA(Jӹl2XdZmmmuuulT*eۑ ARIR\իWOMMy^D"ۋOfrn:D"txǙLx*R*Pvk4ȋ.]j٠frfσG%F*J$JKK hzW^y%F3L,: ¡!tP~[,<;Pƹ\.Bdf(vǁ ᮳|rx"@ ~TrPh```ǎ\.7I$#G@,fcǎvZr|\mmmhT3{a H, iern9vB i!`0@`nnn׮]XB!A{ -` Al{dvVQ l'&?@p)ؤ  V kjjnw,c2JT"p TPT*y}^rO/(& E*R54A`|Bf͚p(H$z($b>/t:]]]Z6L\.9|>ND"R}vtD"H$BP7(jŖ799J^yX|cL{1PHRJ@Gdb'{^d(o` 9::]AUUxH[P% 80|>?BP`0⊱1Ʉl"i:^WWa6J|~ BbUVMOOg2ł~"ZɄ3X?MN(\.N,kΝHdÆ k~|d21DPk_c=zjz(5 LD"QՐ{z^^b}:rD"N  uXJE3'`V =|@)8 ;|133dF#bz~T+JFFFZP(4;; j~0L\nnn..kbbرc@.R`0`0|>L&fٳ|nffbed"Y,ptP |}{ BY,@ h#m*^"+xP(t}}[j|X<ͮ\bԯ< ,R*la`y!_ \M@`SZo)+V@u"8 ;k_ZPH&p</ "!\SS_*htuB i M `0N%R+WV*VN yS(wyxxP(;wnaa&F1LF"X,&щvVp8b,Yfgg,YGTJ$[l 4 |E"Q"P*.r! -[b)1-H0 ' x!BMpz`0F1jV;33cSdD0{ɤL&Ht@ٳ " ӧO ZиTjGGbm6^/NG`00Qilld0&f&QPLNNbyNCG!12B!i d2L&q$Iri4~Tnq_=NforyOt:`s=WUU3LT U{jHa/K L&vMDMM ORsssPcǎ^H$ jRTF0ęhbӐĠ臂C|#H֬Ysԩ@ <ˍD"8'jCh['_ZX-/p8 %J%SI@@.H bH@zZ+S^a0\.DBT* [ Sq, LX,"Q5$I[ZZlv<9ci ~\.|x|@U/---N`|E*swU[[BRV^.ɒ$Js6 /HL-2ˁL& &<0w:\bans΍B`>;;k4D9NUV߿l۶mvXel6΂bW_jfNt:+T*B0T*h ZjM[~|b!P~_*OW_ (J6#c_yA+0/ ',pħ&NH$Bk>l0Ԩ_|b׿?~ӟ~ BRmM6~q<6Aɀ]2 L)J"@7866V[[dxo`@ q@В%K`Zׇ K/ $ $[Q.?mmm]]]fٳ0) Ft&IL^@  Nb0j(&&& d&IPhl6BZ`0aXRR]AFT>ooog0E&d2PL&C^/¡`w cTdL&s~~8񠦠~B0G-Z0*HR777{<(-ҥKSᨯTADc 7|3g JDZtb~ݚH$B0mmmK!Ϝ9АH$N':+x +ԩ 87#+ @ RWn+sA†FH$L&?SŞ߱`# I!jK)+10*T*5JD B;;;q.,,d`vp8FNUz3%mq8'*QT'=Q.TPr ]]o L>A@B#&AP(\dIss /PWWgZKP(\~}SSD"y!v\r\.#xX,f2t8N&^x_UUU߿k4$HblHo/T:tXrbDRphfb6 . w(4Ĕ"ƚ4 " 2\\rE 8L& }H$ d0'"A jp`uVF\ (LþT*!+Eцt:- |^L$Q*(t}}{{{Zm \.wttnz衇`oZ"bhMPШTF3;; qnollLRlvlld RiŊb>NHtܹx<.JtzWWWXrSP(xL".ѳ'^XXt"d2Yp83u) b, &njs}S/.%~HP`.b✹\nu.EtC> h66؇T,@$K"@\"w}:$J!X,n-2#FRѶ6,"T*i4oV[[B i x'499T*yQcr0p8Bpڵ]\\Fq?;;Q2ddbZdI,+T"`pʕPဠ_(+GP1LQSSQ2|b1  ܺHgXDV4B+$(l,S:B[y0iE4F.X==WUUᅅ'xoܽǏ7 RjK.dڵO=T**xn+>^\P(T*LL&ڦoVggB i Maw9??H$Q\.7HRpd2Y*´"KL~, Fjb]*$I*B|>Á&t_(+ 9*|}LFP &A{<rʯRX,V<#@ @PT`EdI$h4Zᖀ7H$jjj: cZi4Z[[ӧPPT)~?V8Pihiiڽ{JMR. {\.rR)2 l6xkkkTd* 6mZXX@q6mllL$HFtca\!dT*Ǔ֯_yf:44tGQT $ǣP(X,˽^o__T 􄍍&\.#LH$D"q8p82 x<^ssH$, A O' " CF v:L&S׏ V;??2T0mnnb6 ˁxj.V5H׃fa J AŤX,N(`NfLi4yE"DBV#2DtL@RBf⋕Kh;D"\.yhhp\zSSSAXf }&. bq 6yaJ~k MkX, -,x3g>!JloJn8nm4 $H|@6$Hannet2TAb^ =Zd`0` fdRQTHQz&jMħ#m\}^v>77!7r cp+tt:&x X1D"Lb16 Sr+5A/>O"`r`0ddd/ G&#`0@RBaooP(ϝ;D䱱1˥P(`F'&&T/HтFH$wtL&ĉb#m4KT*b]P(VZU]]}qNz -Pl%ND"L6>>.H|~$RnS(h4r,YM,wG x<`0cFl6ƍ?CH~51ss\^jZHX=Y8wqӧ^ogg'"\.r-бRǏX\.w}=l+pS(ĽA@њdR$H )~`0Dt:"rr{R`s ,a_jPUB1?_NU*@ׁ'+28~6X, c6䳸0&8QAF\1b 1s(+(@a q•Ng$2=H\.'0lhWl6yΜ9kFk׮D"^WP444m۶D"02̒%K@rdE"|>xȑ>> ,BOS|&_* 1.kzzz͚52 XD=rk2LX `8qUl6br^tE~ٲe  b\)>?55f2 ( @+P(=.p8 dR73 nٲE4HKu\.Ro(^/_k4RZd6X,FQ./))"T+#O(%I8F}0j(+}m+Ns8B8ḏ  ހdB`0bq8?NP|&IXb:g]]_\\|ۡP(sssp/E^A65 ßG\79sF(BЀ7X,zsff\uUbXPdY533[ѩSPj4@ ]t~L&fؓNmJxCCJ]nlOOO[[["X\\1P\.^L&snn/V*&:ri2 V242}nnCJ$Hr|@ m+AP .nwtt(Y&ɸ\.e0`!_%Zm}}}( |x<&J5 (7P(`ǩT*_UUU*gl6ÁA˖.Bc~XVXIgժUAr<>t:p@^(Ξ=;00P[[uH $H|A6$H?RT.vٱT*UR f8fh48KtH$VDBTJt:NL&Ht7eW P(d \. и ( {%Y߉yns4JL2 دsk{m g{zYX6B(03=9WU7yZ KAϙ3ꮩn7|<^|a|08WTg 5z}4OR8G>9CCClxQ@ڏ8)馛t]_YYjha)»׹B,//z뭹?ilvmm ;?~\U~֌馛4MF% U*cǎٳg{{[Ӵo{: 70>>><э]ZZ:z\ӱmŋw}3g@\,9[[[F%H8q"J44CanOO:u̙3a[ַB@~br~\=x]wu~ϟ~',ۿ}||h988n,Z\\\YY;9rM7]pA2.(wK/QJWV777 X[[V̠^H7tӾ}pX, Y__G۫W#Qm޽D"Bf-c\,t"H20 ͪy\__/Vkaa!N{WEٿf # Hu~ffŋĂvZ&}:2i3¼0yOOJ-cVXmg2o4M* DvX\p%<0귷ĤK.@ooիWTB (Z AwQQj,d2] p^VK\.j<۳gqZN'NKty!Doo;]׿/unpAT===JEP(l6L&;_*^OOOZ4Uaa*p2LRp>66 /=zpNS(^zP(.^=yoy[.\Ӄ'N|u]رcN<۶wy~;>O>3=ϻx},zs>76>{iL}'|ղm~0ltt駟. 'NH$L桇;cyyG/^xԩ͇~ 1fP8^__@]r2%V,SUR/^DAZ#o;v-..veYfҵ5Foo}x F:P(>Ou`P@ 8eG˲Z.aOREu)B!UU1Lprr!BA;EQZOe%f1^ LbnI$IRA89T*ЂvDcL4$ M$5MXUUeY‡e\( d2NLLzyH___^"H4ͣGNLLc6} ~~`(. Z-GGG,rcΜ9sa4/_H$D4N,u\st]G)lZE+t:ņ±[{;ށsmۨ.(J===4R~XJH$ǧ/#?#lV1557Ɍx㍥Rĉhtll#U;vl޽wqy{/s=G?M=wyg__w=;;;55u]w}{o>vX"__rΝ{{ǿo{Ͻ!/(8qgAŅ =?###`;NB>+Ww(_k7pc=vt EiJggg=7ƾ}N~l6{̙x}ܮeGM|CCCvD"FxL4ANgTK# `&y4x" aϝ;߿244Zl6j|>o6###B3o61._<==vW^EGW]M7422dbɓ'Fq 7 _ǃr˗/ LMM10wϞ=cccsssOj~/\O|?TJ=q4Ʊa]5M;pߏc277W.w>|zz'DatMӵZ-EtZ\\LRqAl6?~뭷ٿc #h>#KKKX7#}Kjwp].u]F\vu]` c>-jHj6XaAF3227hd2ib81ڏ;Z 95J}^ %@*4xK4bEJ7ttR`QTEAD@ 0`#4_LR/_.J/:GnU2bKKKiZ-J LBN>=::?<66( [cBQnȈ$ICCCtzyynD"W^=xi7p1-x߾}mW՛n=yi|;G~dxxo7M3r˻Z  `߸877ur~eV###\XXuC*2LTBz.4_3hW~W}X, gdd̙3HhANL&Ѩi8/臃r,z.8$ v(8UUuyy9i\28JRTX1̧R)41) +L&.4LZbeYVKp8yzwr\^GP~녊}: oTUt:l6Í}BI9ȶm| y睵Z_a8GF(o{#o1lauuch*IR&SSSBׇ!4T*bYͰmRl6XءCS2PhpppaaniY7ߌYf=+uҥK@C.T*j4MٳgOXlPJ.HRZ-"IT*bmnn 7MlBÎeG zF4qFUԁ # cvmmٳx}>qow^ŷ1$+W ūnmoٿc #h@ GGo:tH0P6uD{7M6#_CX IAD"eN^w=F&0#/H卍d2y۶3L2,ˎbhu`Tjz{{ŶrdB}ڒeY$0lp*]F<Hc1eRJ#vPZ ]SRu;NRA#j駟馛ܹsb;|pFE]׉DnV+++V {3|0DMWb4UeqlF!q p@ BjzY)(g3L UCH$Bb1˲B{Y{mZrwp lTz`vvM9xR뺍Fs[nFx|aaP(;88_x؞={fffTU_\\5 @oA u\𭭭}C8圣ÕОu]t)1HGFVYYYq]wll1~FхH@V=<9ZL&24`ņ:KfYd X7M̱BN?^У`ه!e8 豈rۘA;66^ݻWQ,cRipp0UBh4&9ccc XٳT*mmm)@X,HCCCi .'{WF$I]VX׋)aLpM~yW=jBW <ô,Q DxjyݜfEQ@:6ziX:KD lj>̲0 #J/--% !DZ*߿J9|pVK$W\fýtu܍͍\.FZM:r|s Bh500ۻ}[^cyۧi'F"~ѣGϝ;^XPW  ?  @=躞L&;s=DTxjj󼭭-, !X#sL&J朇Bd2Y(:,țDzCx<  BHDQe9MgD"8ECX,9,t:CMA!M0^лSeYLfeesgnnnvvVd2M AUNpӨZu`&nFBv&X@j5lmmVe,]_0G}qDPnt:h4H$Jp hj&( @VשKnnR~jLƟ{91` <;!VH޹֋-<;jEcB۶klz j| 7\zСC8m)Riii//fggVTޝZ8rᾂrʕo/IRI$h٭_qe08-˪V?S?0676ә?3 ߾}.@H___իWhoo/LMMEhBJٳTׇ\XX$ fD,j6/{] @܀ݧށRڭ),#_&$PJ1ҝN k.EZݏ? uRk&|nu{<, ?ނ|}Kbu$Cf;`+L&j.ؐ.Ux Fq3lpC;%.yx5vm9о6Hc3dEQԸ 6qfԯcrh)?~EmbqiiijjeOQ#h@*r]rX< 6vRtPPFu]_^^T*|իX,mLbo. Oo&z}}}jN8166l6l24P(\'(?H&{VĥsP(1OO1-M BSSS}S>h{zر[nBuiy^:^__Gkt:^@iV*|cW\A&KTN>=00pgy+Uoؽo=kYjV*We{ݯn]v4F{~ײCWH$hT(L&iZ6]XX@5ڿRJ&`pLjzU9z"k~8Hr68F8sQC/NBH$D"84 ݻwo"8rH&dl V*l6i%Iz\>|p\#Β15ySSSfZݻ\.OOO_zJrȑf#Gb'OZQط-]Fm(ja`Ē$i|q@"2|D /,,ܹs%ZT0-f ߏ9u=nB`Kz23}7g5ˌSkA̓aX =VWWkvzÆamP @hs 05BM|ccqލ L\ Ò$a׾<011gϞH$ry2ÇQ(9<44T.ы=|ttt}}=R*={8OFWvaD"}s=b(;~衇>=D:>wmBbyXL^cv@Hƴ2˲Pnqj@<7 v&KVB!I6랼d(*NMMmooollSJáp @9s_z,Cv;R{zz0Bu||\QT*躎a8qbttsiSNZmhtffXї0IޮT*8;_[[CCU<`.KR؍9c( 뺩T W^+_WD"R:ֻ^xf nrҒicccv{~~1uM0^OC7<cZ4MtDgz<196M] !O<ıc8omm---+h__ߞ={N>mYH  @;_x>Lj5tB,TTT}߻w/:2%FGmd2XY,9ŋL Hsb1J˗eYB۶mJDN$HzFܜf"eR,Jӎ\| *䤪d6 緷gff677|˗/۶l6GFFnB!Xҡ!][LNN:d ðm 8t:cccY˗]X`L&z8F@R)J$rv]T0 |ͦidj:*0vTUUUB`foo@\vghhu]_BHVH˗/W*B1l󼡡!^z5 ejh48mw:N8{ r1 cee=]wcgffp~O|ǎ{#GƷl6#xz)a;r0Q7771̶mu騪{T#8}}}Nsp`i& /ɲ<7?H$Rɓ'F @  @tiy̙3k뺮' (#޽{t&F{666pP$ 8皦۶mƆ"JBrMӜŘF뺮$IrlNNN@,'|29r gΜ)0<<ӳxԩwO>}t__#<3b)Y?^zz9qx`ttѣKKKO>t]wU(|xmmMӴÇۻ~˲,ڻw/ .^ {755@hth4pdYZ933s'NMTG>cz}hhhff;j14`nٳgByYj*B9uR1 #6{7v:H$x<~+WE4|GirNcXa@eYV 7])ˑHqz<( r6S===F#H`y??JFFFdYzm݆}"h3~I˲ЫlBL&#`)vFcjjjϞ=8uT*a-:cڶ8VV+J'x0uqհo>0p8N2t]nzI&ajR|>\*(6*XLL&#= [[[e]|T*U*~3ˎ?OG‘Oxʕw}{VWWMӼx +IZ !4clmmqD"Z^P|d>|c(v{0?J; Wϋw#: tSGWnnnT*e={:i BDBJTfY]]MөTl)H<˲TUZpyJѣGe>ݻٳ\|yvvgtOO0__[ooƱc#w}ҥ+W|_F,˄l6DyaLMMIH$$b1p8\V[VTPioc`ib0›]?'"BW\[֗$^'_ 39pc ?r-s0Z+Wrg>Ã===C?C  @L @SJo4xtUUhXDKBޞlOl~ddĶt:t\׍Ftnm0 ]+ c1_T0G nH$jd2VWW\R(\Hʌ@dQ"1ƨs%RFQ k:I{o|тW\P 댔a@KLJ\}Ĕ|<@! X8a׳sGݗ=n{RJQOr뭷z7;;kf.fhG{\֍(r'|]ݛK(@^A uӏ?x:q\' SMuȷyvZgϞBc!EQo剉P(ّRtJWWW8p .X,~w:t<ܹsr97T*ivPb$-,,躮L65MseeEO|Fciiw~w7~7cPX,=z'~_[[k6Mn777{1UUk6R} @vu6,4t:k˗/Z}7h&*-_zX4D2QvX4s_' (& @ {\D="  (s|NLLs*)@(ʰNM ((N{J)TOQ*IL|_- b1tw3N'9Sċ^6+i?ݝ&@"v" CUD^zxQ$ UU[,86~}}?\PHQ'NT*Փ'O|KKK?OJ[ A  e?\uQ`YVh !JhVbh\.ooo?sQ__2 fnooE(ӧO/,,ݻ>}ҥKR!L&K@OOORYYY4h4 0RfUU_/kkk_}?z)rQlZ̤R)$Lzg~gfff~ᥥ%!jooG?ѷm?x$i۫$) !LܔeuݞZ5pj4.,,RJg`!縯2qP?C'NZtm(DjiiFCbj}:H0 Y s{|B}Z (qӀx0 P!rIqjr9ɬ(Ο??;;m@8>yJV3MSe]21r"Fc%YΝ;'}^Z"oOTBP4 Bǐ8.xo۶,˔REacv:?83o=wrq^xN}_|O~}߯Z}oFXTGy߾$ @?8#Jdz{{Œɤ$IjUUՑ08ar-> /088:ݮ)ik#[:SS*e طoRBmf#HRI$ӓH$Bx< d2)2fɲ\(0ʲ''O޷o?w_ٳ}nnn0K.}#?O~~fgg?OyN:t4*ˮb3aP,eY$R PcɭBcݲ0"q\HZeɮu|!H<'46`Còh,wy7]\Gd"݇ɷ2q @UU2-a.ϹcZ砒UaYVX% ( 2:˲\=\ãmB{g[/;Vjs:Ւ𡮃O`B3!( ꃁ @ H| ` #۾Vek7~ lYQjbfBmONN>b itzyy^گZ^>|o?sh4zҥ/}KPA uPnj6<m …õ\`߽ 4xӱ,kbbbee}{Q-;| TU馛>Oi<L&:y$6:lh`"333h4ic*xݲ,]M$<ȃ ER#sL4۶]TVѮWGyJD9uT*2 ܹs߿?s?f  @WCp8$Ihn.?{===(m6sYǑ333C_rC-//cd`qq0f,gΜQgkkk||~WDB89B. | [.UUj>Bccc?reanb_`2;t )-]Q!,,F?E˲%]6*o.*Q>^;#̲v:еU渎m$7|q" 9khZ*5|ZCa9ܛhܹٵP(ᘪi(2W*?p sW믯oRʲp$%ģU"z"8j8rӧ766t$4 YWobGXVU̙3 =4\G0ʰlv{{R`U4ŝcB8bׇD"A)- jubb"~ 󫫫A۶1 P(12 ,XǷZ-///i9 OpxhhhqqKL___?kkk"[ ØH%SKKy{|sٳ^+1r<-d2o4X,.// x< ڊD"\Z!$K_ATnvw~c+{Ls;jZܵ ʩ|]Z9\Ir,u,"IԶLM>'TQRڱ*B\ ڹn}A眮:հ } Lv% A} `]V".9Y^.I $UfӏC򶑠c"ޗ\JGdYAQA]Qr$y\!m v[\n%сpܹ>v+,|h\j6H\hۮ~ ';&xUړ|>H1Hdxxxkk4͛oyqqQ4h6}}}tfrj_"~ 1?ݿ'`ݛ;)@ @ Lb8Lf~~i<ﯮ2Һxb*r]nj?mooc+@/h4[Vu 8f= 1B}˲rX0"滃4BV+0dYNRiq4!mtzbbbkk>ݯ.,,x9Vԧg?777$If3N`ʕ+sgyL&~TJed(Yr!Y18s~<,< dCaUUgqPuq_KJ (d zH еH,:t?b:i 6VN@ 8e .r HV(%ba&_sż#KB7p<qcO;Ba;[ITT]ejTq;h8BԴJM>̿7?977y\Ѩz޷XT6:`9@X6(2\4 b' WIRJoKcO>.q . =o}}q4D"ȳB^yϟod$ @p!If2L&*yXh6a`/`Mp5D20>E{:bP{^؅ =\˲Pŋ!YPJ=ÇnZEڟ@<m۲,˲٬aGf766\B)?Gy$~#>bƻK.mmmqF… W*LBi (/jhϱ,+L JebbBQC&[oX,;wuݝCquϯhnIu]ףTQUs#݊_W5)iJ˲-F|Ao:kʊDbgT;k -aW6t;0~-L ̺e4'R])҆ĹRk>h Jkf9Y\S!gOoY~KR8v]k_ڏbOr_x#Lڙb1EQ [zr\$UU}[okqMJeb1 +JJ9gb1UUF.b[[[ayÇt:]TP:m!(htL)mBM2Vpx~~%8qbaaF}>&خlV'08icc{$I0 èj@Y뺪&IZaبLOOB$I¸֯~{uRxe=)Tq= ᵴ1 Tʌ)2S F\r ʎi.DB0yQuU>p{W;J"N5۫n[4}R[dlfrtS\ sB8im-Nhe@TiqqJR_.cy_$v9{cǎiDC,,,dBxjZu=:8+T*{o7d>p@V/ϟB#9?;}o^'#h4MQ˲TjuBiymyu(B6771]YО|jjjmmMUUo۔\.ޗBa{{[Q%dyٳg8vi9iRJ5M.//J˲dY򺷷Z*l6Sr@#G/*xqqGGq*BRD$cRϟ?!UU.//{0 4A HRw9;;OK$2>it: GL!OeW% J1 W}_V]O)f]_\xhZKnTP!$sz  @{W;kn3svud}v 8@4*Uiz"h\S7LYQr|.j4IhCɊaS=(f2';/D,?wK3QB9"+1n$I$E)e[}sc<>'0\,Q8r:gtfj̙3Uo߾Υw'J>}:b$ISSS333_W>= @D @6rϟ??99Y,WWWv*rǙhZ*FbL&lP(&cFj5IZ MSp̉?*_2n2 SMxh4{0~G_H$bg?__x7//rc>HYqe||ȑ#=.1b1Fi6<KH$r7^xqmmmhhRN:/KihT2I:&ؖ]Bp#P$F-qB@8.ղ[o#0p{Ta!Y8빔RC'xYQe`&ywaUc k.iDYYx0(%Ψk\k B)8wo89qo/M_]Y[;oH6 9}pw c*8!|<}ù9 w)Ɵ߿ߓz=EUVs]wrr(NuFcRܹs|+??=|Ym4mnn !r\4]]]u]hPJqB{/Y;{l___Xl6h-bv^ǜԗw늃,B` -0ƅۑJfϝ;G)M&h裏w}`˲l?P(U〿ZzCJt]j>œ,L@ey~~g BXIDx< mt>Bj߹u]\F@6N޳ϻfIdG$>MⅵuY"1Yfkj.ƒ²F7MCVo!/ kQ_,ڗ ^8Ȼ=(j֔<_!dBUEƿ͟L{B%T018Iq#] ɦ"$E$IaLfJ05AbR8~0LZerQOh4wZl6;<<4͋/:u w_xX,RK.>77wǏD|4||utdaD8qrQU[J8lln<`ff;$mEQ0 x|EQČ16Nw:MRTʊm똳k&n Cq:00l6\h4ͦ" yjڳ>~G Joy}}$IssstzxxlWӴP(i8hU;wN#IRZ...RJO:s綷83>4͵= pB{=F09'h$I|z|hxmeqvF '"TbM_A85l PkFx9_P b (m;p,'m JdŰo{O*'L(=}ϗeEӬOzL T ѐN;톘 * B2Z;I(c;wuN'aj`E?h4pY 0Lcxx\.9s&6 Bpxss3﷾  @7H$H$8mGQ!kkk}}}dn/..H~xxxccv>FlVqܹP(Du$ID<ȟueY4MNy^:<^Cwsٶɻ333a n617hD"J%j<fc||_*ڌ?~S,oVtptTUFz}߾}RIl6(BN#r-333ݟB"n<k|v~?3*@d*K@WsEZFHecrDdGIDATu']s"sНρBI |Z8@7mF[ Põ0x9 ]68}޵5+jb@@ >LM$LJqE<&I a?9*-Vdp\𸫪lϞQEQdMeFyۨ JBt%8vF[xnbk/_bhPl6k[cKťNL&QL&A uk\l^陟jHqX,f8b0jccm;L }{7NS,CСVVW5M-JHĕJKmk\VÔVqVVV0[ wXyEQGGG|r"`$ 'TU@!tv`=94Qy1FDiIcT ùdZ!\%Ba .o! pt'VDv&KEA0\; @2H׸C[]~`w0eo߼tE~7?жQp}{U(8 |ף@F<lg.}:NCjh{ܛ1F6[Y]̍B||"ި8r444v]Wq?U(7M3Jbfy~&@^/ @%T*4?v ggg6,ru4mϞ=iiZ2.ˡP;cE3{a-qx)mJsu]SJ#ȣ>z-vm7Q*0 CX,ŶDn7 ]q>z…l6#X[[fR1={ֶm}}}Ȉ,KuFAeZIR7yJqM]<3 PN}d}<>nA |B f O~"j`G @8c !;FQѭ_"V& e*!ďSwA,cRƨs2 v)Ff4 8% q1FHp3><]t3=2X-?,ɒ,dF?qj^G,0Ξ=D|.zj? zb=pLC~,N @cl``嵵5,VVVt"0 cmmmddgkk+߿nnqzAIaX# kD^3;`裏:tСCgϞŽiWÇZ.{zzJڶ-˲P(tw~,z}eeojj }iI]j(q`/I( \z5Np JRZzl6`KKK'NxQ֌$eVfP*5X${y4,˲GN ?5$i;Uv.,> qA(Ra iXsdM\. IBQu-u_k/G'YBID\W$8nB"}}M|ϏvfFs×{ \ޑK2=>DA!pNTr*Q\pp.Ȃ>(#;ʄ dvqLﻞ @F?ȩ$IdyG?.IRպ;]1?F̜z"h>oddlmm#8H)NRۨv$iaa!ɼVnq.xwYJk9`Ve! zEtq8|(jZCCC,lx666!i !|P(w^߿}0lZ-$&!a:uRJm6 ]><|677ǯ\W뺮v{ppV]|9͖J%{w}B!"NE AxȱM1=W\^[6*ksKBQ$xKUf;(阶$IfizV;[皚ryS mX#cQ#!p=`L- |MV^@ (ʮ#۾(` @t d$!Y#1@@= Bj}E@A*(v/Hp F= s . IykBP&y B.PJ2}`O(P )(j/֮ 18vn,[XXD|q )|ON㼿\.SJͦ뫫|$@O(J\ja8. sGGG+JTrZJ{VRBl0!NhmZǑߌ1.%_*3H$l"zj6 4MY{{}/J(cBaP$IZYYqg޽眯RP(aƭV+LfW&Ih?Q\pڮQ Tқu(s lшmT˒&!\0sAu }|^x0-uU3z67&KpHmTs=w/hg ԝ"Iܕ451Pvjڋ,}MF@W  rP GZKĬ5Jv1@|U2j 0 D*t$l3x8 `H2](wdZ(>7Y#;0^nmjj[,|oo/gSJc677WVVp'9=4 ?/#h>4M@8ッ 4MDZΕ+W{'NcZ0从7zG0$V !DUյ54 h)r(‰;tuuuzz:jF}YyoooDwQڶڭU@bf>|xkkn?3Xl``R ;"F~GeYFnwl8.( :MR*x*pAU}Rf4phcU-ɹ㙠*z҉Fî&w|,^(Yxe TZM@:RbmGias⑈)FCU]BvBx< !ۄk6:!LY& `칾ʊmpDP(6@X,B7x4"˒防T]N90 YD=#+Bvx 6k+C 1I3uŊB$yM)Q5?p0@]ESc(VU @($%JT&d\YE$˅ h?ef(mu|>K$J/| e Kli!s 'LUp|'ϧ/>}}IpFIdIV$Qm\ms AtAmm(m]Cyܫ5یA8VUQYB"^5#jX%gڞe x]mw<&&#s2:Zc#w*3Y"]]9ux`VC[%ƆeYl\X٧Rn u:aUU;0RJML&xܲ!TUt%Bf/ @ a-}'H2&#f;M@1MOp9!@8'(Q]lTdEeBsIfM jZvX԰L:Ir(Ĩ[e{\Y,҈ Kʄs6 YU@MeY@%BgQjɒtcsE2z|BIm}]<3"$0ӵL績KŕP ׵5DD!BP9@Xv[*Bs]U!@Xs-8k:%ǡBYd \xm;Bx2,.\ΝBBl4T*Up826]ѣG_K4\O`U@"m۾QLcǎmllJ:m Z펍\s8h& ]NN7.3j^9;~{رH$R.1fU;szz^y$c p7=rY@N'ɬU*]QLY}qV`Z& x-gqIຆ(BJ:dIQ( ִbnqrͤR)c+jhD]om)ݶ|RN<ʲj>¤Y5l^3gDDPHhDdex0fX6.KR8mR*qm{TJ 1l{E=@$IrcPaU'`*Ta2#0Yc`TwYD @pP;^"Qs8P bnBa 0Tg=]T$u]X^e1vWGѵ54xcǪjVKRlP(麎񍍍Zv] |#h=SEQ,DǛ|>̠&[[[ȴ閰5NnՎW$%VxwOm[2,!qlp}8|iiI4mF頤KJbZ|>?77w…t:d08a/رD"UU92>>n>*|hsY[C>}( +ӦQwl3k44MO22Da1 <:W€Q_.J N ʮQTU\[PA T0 ,#zGIT`Y$1pe,Z6LWtrEFmǐ5 Է ss{O]dEx\ȜJ#lnDS2) 8鹶MT.S{F%)>١]q4 $J%J !TN1N @|1|S8(\N`0a !@!s`h> $0;o3.#UPVU?^v{kk h.U E A(eYpb @o: @<@cJ"Ғ3L("8i?ue>ruMF2˱UUe8RT.ss.xJ\pʱQap B}!v\9+ AP\VesU8߷ !TmwN PA/#o<6-wǑ$ixxX,>::|`iiܹs1.UX {`۶鴪IWח'''1ne|EQdʌ!ɲ0F)N 'Q&S(뺖*DLWDAb = 5=/)U=0\W0QP*O%(`(cDBUC0@ |\$IIWCJ鿳;oVT*-,,|[ߪT*~{XpRԩSa.--y1-..FQdt:~$ .vff&IVZ둑o}[t:iE=](-دlmv?kp;@?Yi+B!J^+o~'KSSw7ͰXvۉ0Q@,тNqުK:f71HuE'z]PB"9=sKF(b7?w,`$2 $FHP "ʝh( PEE@RZvHƑB JuRʸ4t$T%J -#rdF)*d ,E'4zL@H9fhD@`ZK$c3%ްuP& B}wDzNMM/իWO8Qכͦ?Kt<9cccZW*'|ҳz衇uzxADB Ӳn6nZYyq\^YY `gg\.z b\LJyۀnNߵ/fSt<401װGqAP,wfAizNsBͫWcO}ٳB#GΞ;wz8 ;ٳAVJR*VWW8N$^{-(־Qi6~133S.~!`-XYHQjǎ=082,4˳q4MDP( )y.( 3&' #|ӟ:|Dܘg22!" "&BDgmX@0*V+#k(Ԡc)Rcu&$3T %TR)hg& uQ #mHP("]d(D6JY -PhN+儯ם$7'e7Į˭`9 L I$3(#1X @J%}0Pۍ:rطoꝜ,Jϟxbmccc̼ )e__1f||<2<99jz衇5=EQ AqT*<ϛfZu:tP({c^_[[+ z Ow5RJ5g0{s."Y*vs W̻BcNRKVW^p\vĉǟz)D~q!&7=1X,8rֵk׈ly~ر~7߼xbTQ۷_O#v5 zWcţ}iKs<_/KJDB9 A'!V ӬjeFd%sk,f@Hdnx_-~`~_'&ifht7WVWڍz\ajRYGI  d@ L!1a,+b)NB2pP*Ȍqv89:w.XnjP d8֨?rabll{{`K[*SO?3?o4 d*g&d9!\07V@rTRVZ),c&*E}k-8΍KV}}~vϝrOuKtvvV*RO~@ya ۧ}Kbssss?XC=cE衇w(fxxx~~~yy_>00)Ra?WJmmm[>?n{ڵׯ{r|ދ{~Pl}HtF߉Wn~>r$`+Wx]/SNݻZ8qBwvv|tʕ;SO=uܹRn0\^^nׯ_/޾s7`aaa||̙3˗1'Nh4^ɰ-H3ٳ 7\xy;ln9,uC}W_N[JDG99"9D y#LIQjr˯4 P:, )_f٧~OWӯ8LAP(tXٹPkG- Ug2й&5E qp&ϒ$Ё 53[$7Tj@%رA Ls%1dc6o5Hu۶TP}(NR(>sw_~Z[Zkfέq${`4pJHD, hB DD,(0 -PB*0To=>t0(TBJP $@;4kmRc>lmmkoА'9677ggg| _B$gC=kznd׉bbbaal"ςtcJ `׮D)mIvt|Z)d J-1f}S U*I2iב Mu֑%vD8`J+H(Z$q[XYYz͗ƆgèP+;]>!Knd@6w60,YW(Sk;Yk* cC!]fq!,Z $H"R!F)pΒv̔fR@Jp ,`*"&Y&gS,gL.JaAfV'&:L@̤[]A= `V0H wKzm12 $֖c~Fc޽:n(/^h42۷oss\~}nn.˲{_̻C=;ꡇi\vP(LOO{~Oe敕֖{w(6V˛[%/=dzw L3{2^7Z]DE{CĵORr|-LMM]tƠRʓ'O<裟'8kmzk<{ yItY^^~7fgg766.\777GFF~'Ο?.1"aeL#˲0*1k4d0v穰cf$KEʁ?.M[BlRaRv\H/K:|On7 0IrO||x|B7>>iou1uɥZ ^EZ1ł"hlNM,dBgp&`` 1`=Qd &MA RJiBIZsT#fb'pu-o-Go[:O%% 51{{~7"C@ w\ev`sJ+L\nZ)AJi-PDR1Fg0#<)Ȳk_OON xd_j{Lc_U{{͛6gydY|fR|ҥRt)x|n3gVVV1~xtݧsV@EŹ?)v1psS }OL[,k%oV}e y.X@Ƞ-% P0 8c)a4`$5p`QJVcdH  D  `=>A,KA C=Q߶ 'Xk2!b\]]j w=,///--j(.]pر;S)EYwԩw3zÏ cgdddqqX, *jZ*~Vv<vddZZžn37Аr111 q!wc7+]xrW0wݛ_K~8:00/|9|eeezz^yw^X]]ܞk׮MMMMMMj5cґ#G< /lmmR&Ŀ]ҡ[nٳZmkkk{{{xxڵk'IRT‚1Z&I ?2ͺ3!rog5i4a DqEjġ[NPsˈrFPRJ)9GDRZiDvi)bݐRI)4cJA0 )r'I*R<=>6{/~l79MłzŧXTKoyw.^}'Oac?ӟz+lO}_/:Y]\OvKٟ噃⁁Rsc 5@(V Lv5e'X";,X!,y[OR 2"PC8P, [ я@tzFvcŋVWWN_v;˲/NMM9j=C?)LzxkzU*4M=l6MPJe޽{[ښv]OTz~СI_|R\]c~ӗ&C}Z-E#7ͨxg}sU?G?0;7::::??LNN^LG)gϞm>,&&&$Z[[Èx94622✻;cǪ3??2?Oͷ${w>"q]!ԁ.+lun gmΆ63@ȦۍNs V,A2 dT@|g~)*[-fl6Rg9An7~uTPQ(scgQDdCT*K <ӷRFӬt{|XPã2n#S+ϟ{v;}_;'GV|ó~BBDBbsD]F"#tҎ [<Z[2,J bbCl1J2XJmmXH%Ȱ@v;Pߤk@lsq.v. uݾ>/gӧO6WWW[[[j7|\.TP˅=C?zx0 ׏ŢJ}n\^ZZvOvj(+z}{{{Ϟ=p3 o}=vNd`tpc]_&>@J633o|7^|ܹsfѣg]*yĉ}EQT.@Jy!f=û^C vi9Ƙ(RMO_ZZ(Z=m677 nJy=A.!?̻5WJ)paޡ'&eY~DlZVkggnONN\;Jtyy9 K.]zP(KzZ\\=wJÇ_zӟ|{5څ̠B-"g`IZg,i- aٍ DAվpNގ-徲P2IAj YpNb Fg @AT00X6 )(@@pH)XG'B!:hD J젨QFhRbɔPַ߮+WjNgϞ}JCmllʼn v5::z$I岔rgg̙3OpgyF)us\w=û^C v>n{ Zmyy9Invֺݮٽ_p܌`7pk?{[`Ia2j5d{Qmmm 6$If^߻woѨV8}tIdaaʕ+r˯{[^{'''###AEQd0 @yiddd{{W_ZzчYq-o[s6惙֚< bdI0VJ}Eg\BKEtX Y/|yџ(d-+BAuXkAbf,㾾G?ߘ3=9QF'P( ;+L 5:wpZ?ח~pY1$q;~`ᬻ/J{4~Z'H0҈t.H!%@40,*ƺ,ݰv;W,x77YgT"Ks !ZO~cOgs/t~w\?n;w~5|+_IG}?K__br\\R ',n{llo,O=Z770ZnF;ۉƊEX 5nr_/ɤf;_hZ?GŢ BB« NYkN{: v +dd ,;1B%j$AH@$D$Y $Q ;GDDȖ8,ō0?Bgy߃EQ^r2"mkaͦP(e7;T*5(ޡz衇^C?&{JqRYYYbq||l&I"(J?2U 7Ow3^i,JιrK'N8qbkk+FY[uT򕽵vrr20 sD|'IT*~pǻ }uC1&RuK k)39#}}5ZjzxCms;qHD5ff3t}Z}=fӍMLqP(U}F:Rw?f7=zmW^y;B6+B\dCc(՟|鋍VO~:2h?GGN;oݿۜo{ܹ/}qx=ÿ~/--/Zh-]0|Nyn?z硋xiku~]h:@qxbR(ȑ`TR8H  Cc2bR0CL " 9f4gfGN0K\>T* ohjl:/y||Vԧ^x~SOͅeHlvIHU&Sni]^/on*Yȯ]\*Q!lM_~kY@KƒЁH"k-2@BFq=GThkR8kQN'=O<HiravV 29/eYb!bXlaٿv=*6Ł o?i!=(IΦ;n+7-{:^6ZͿ;ZJXdADb iGZI)qNGJg&Z FT9ΐP`愂u@0RR-z4MGGG8yꡇzxkzDŽ8$4:2?wZ]]VJi'ZMx/|E`}, +fRZ/- juggg``~@\ggg [@𿋈zV=^FlzҎ6 z~wׯښP"B0>>~پÇ/--}K_i.x~'GiUt!5sYb4I}B( PF Ãq_n'&nk6CJ¥ٹG,$JDbDP[J! Bh\ZΝqsR(#G:,Pj y CX82! BT? $` X@YP@.0"N Y\IJw2 ?0,RO?^ 2 yZCnꬹǏ < *AL$[$!x^<2, QHh-!ˍu?Eyn㉒ر Go92۷徔r}}RfT*sܹswyʊw4k׮uf>pȲڵkccc^355顇z5=gT*V%h6Y O[vq;`z ]F7 )Jell677;8JREwnTg / vmrg}V]pZ{(ocKxǾZ I NOO{8:VR9wfDo$IvTz'^}ɍ 6H=*Īpݶ:tJa挒c6d, cLs{g-KZ%#0i8'@Hec2A$;ʌ VR:$Q\*X˜d@XD‚܀Ddzg@@2 r㐜ӁJJ[yjR)ԅ &RfY!R1rɎLRcJ@L0BDT‘Re8-Ej-[: G GBHX1Y[AOiq~3H qM{' zP<+l9tБ#G;n$)_ٳo߾ͅr|uOٟz]w9 / j52OGaOOO(NS<c|"kssmo9tn>`O?oqk}}ne"Dq<11>33^7 Bɓ'0TJu,>\FѸrtey,Ga敕$Mox] Pnb99$ B,ɥ @hP4uRCG2R @@Fq Y rd1*En$U PHa%v8@CTJB !P %9b8K!Br@@ yE-ɴR0  ȨP$ #:$reI r(%+p slDRk!%100!@J2PFvB`9G5LFTqW _'@&BZ5/y;WDEYe֥Riiihh8R^pRdYvYE###^!sڵ={LLL,--1}{zǃAl:N<77700077W*^BE{ {廗WX Z˝NgWa 27,Ji~ )z{cǎi|9|+R;)loo zG?ѱ{Nk=>>tFSSS>3g.\P<7ϫD P(,uȨX): ͼ P`@̀]n%BdT$Il @α XK)u;-b$rY14-Bmc*3;G\pf\f Ji\gmS+@JYG:#  9Fs#;#9$8=P(KP ֘Xd,K$ou.#ԀĎ5X jPiΌN i!eR G0R`RTA0`-9)Ee?&! dF@ IBk*ٳgQ,˘Rx"PyJ%cC=K/}00v(ąWC=CߍӱzpsN9WTؘ>Z띝TP: QvMإ5ڛyw:k[V$ ,fff1&MgN> ի$\ZZ>cahϞ=[s+++[[[j=W x˗ϟ?hh_"q<77q:tːW; 34M8099ys-,,t]kmPh6Νk6CCCJœZR0 FcFV2”+Ne  P Y"C`,` ZH@h&J !#"4H $`y@Zp. "% IX` N l;f d%7d@a qT#v' g22p)'8k-8' b`$Nh`Q!IaD-MM bJJk[:hi+Jh&1CI]N@ n(cg  l6?q9c߿'߱/?? XYYyg}ݝwh4|;AC=áGꡇ  !t:BX,/1kkkALOOoll>|XiX#]ϛ}~|"nEV*;NeιE/$RJ%Oi6.]vZ-嫔B UUcT9$i4~O>|s|0ej-`BJY b䘁?-eI\$j @BJ!4L$C8&ysUR  q4[ "Lj,DȑX8Pkr7U$ fL>xm1"0# : o%u&;ĉK#wwP0! T $J$DRc`@,`{geg@im~[Kɓ'YV$v׷GGG~ixqqngYvС>Qz衇~tzx+~?>GđDrR4::^`{󃃃û Ţ[z:L!ρ%y%xRVUյׯ(<=;j}Qu"NR$IjWzhzzZJY.o%?I=88o۷;v(Fᣑ@Jy>)ѥ7xcttGja'՛O|] 0Y*8tR\)2 !(#bfZF@,9~Y!2 2vGcR&DB I0t `[c,q@ adYȅBѲg J@DDdm !0"RFIDP@0X k Q*RJdme\XQ$eN"3sf *:I#," PJ ,1DACRR9r@K$@H!GҷZ!2bV<]_&hm4Gv/<r9Il6k'O̲nooo쉀{衇w AxgO!D###sss}>ϹOӴ yUNVz8yaQ2sQyGN1}Oo! 0  xccn}S@BXVW^,ˬ}}}jucc Rt]w-//lݻ׳O:$ə3g&''~t^VFjZrˣ>y;g _|j9|jFFF׷>u:k׮%w% & bG Ad!tQJq⒉V@BrcT u6o3tY'"msX!$ Xi-ogg'I0 Ɓf333+w{ndd;;fӇGU=û^C ־յFg~/T*̼wAH) Oe{+++RѣcccRJc4Nn$~?)zʲl7ZKD-;=UE^h4dݯ 1ËVJ8q ou݃v:?rR9t>9f޻w>,Μ9Ǯ@ǷzO^>C(y)mEM]LLL@}{O?sooɓ'ɓ'^:11˩f 8Z7ccc>߿{Opggg$ yܘ4 P ²r"dLB ZDD1",E:E&:FIP`tв@ vq*?'s+ \+e-R%DAHƅfuiy0 , Tց5TS2 ! Q?xCvu (#p'MB! I:#h) I!#$BŖᆯ# !Ga@7#3'*(De@B sؽ]^ z6yy$VP(bX,|_vG[~Vuq8<=5==ہ{ɬ'aXTq{{{bbbbbbyyh8777,,J)km\smmmMLL0R__jۛA 2ppA%*(e+Lre @̨04C(,%  ܀aɑT.4RfQ&}}Yn rkL"J% `< DZDrڼv$)hB  @8td0tϘ`d1'[U&Z*$:IicF"A P(t:P  Aۙ`Y%)4 K D(9ǹ dpqM Hg2eebfGaeY&t:~={8;N:}(o|ǎuBW^Bq<ѣGosέWC5ÏDN#;/]]ֺl^pW!JeeeehhhccciiEҒs\.[k;M<Ͻ!etZxf911託>y^Tׯ?sUdY622 !Zm߾};;;KKK>*MLLxR?ZP>*/;& Hl!#`:!9@ P!rc%:bа˒PəC/_$ɾ}Μ9300h4VVVsϟ'V卼+ѣG$Ig +W@e###P=@衇,T*ׯ_ιOo߾,˼vccçڽT*kѣGO8QVRsssyA IvMҠ6~)Vc>B|ɡ<ϧ0LӴhx o'r|6s|ӯ fffժ/&''/9keefEjСC^g\*Pj5H{,|}zj*LZ{…ׯC4Kr@X!в$X2dd#c( s3:TCw@8$7eQ\ lb$J&rPEv*7:̐FI  !G>"K @4BX'&b DFpB8fAH f,|R@7H!2 ="d$$dŅ@ `cߠX,!B $29E  $Q $#'d$!jdl禛tB131h矱VZͲlxxxuuȑ#E7Oԛ MOO///8pܹsJ]tɯV^}U8t{󞕕SN_ fbb$vJ1`D``a<3 AJgfZ%8a3Cֲ!3R0as끣,t?qՅYv=`TCDN^LBlEd%Q2kQ +bEdIs|WώQ8YFfF6yH(P (a$ 06$a!|DdD@Dji D@@fo17FR+) g3J9ع@P@" NP(ZbIDAT(?C/o_\\h߾}N*J} /^,v[s5=û^C ȑ~pp[ ?~˅Bnw:A?khϞ=v\.k 6 o tťI/)ɲ^8<<B)Ғnnt``@)-t]611͕Z敾*]__^.\8v} _'>Q,WVV,[>c̉'{^-:t(˲K.y%tRʱ/zET*U kڞ={Kƹsn|ee'yi߅PtQK!Xf!( N*L (%2RX#@juV0(qn Z(lVJ|'qN+[XqJ/\8_, u{L;!"P Pt7H22!@ZkɲA1YFB(0w O(&C Ebvl@2t \]̒cH'/|S_{ wq￟$t#q q|kR$0<}H"A4 X1@f4(t2kҜBQH$lx<˿.i)AIPQ ); ;N}~tN< Ymmm ,//x0 }8qBJ#9sG r-/_~?z衇z @=J"!3c۷zjP(~fAD333}}}G8֚ͦWSF^xRx/Y|4R뀕R}}}W^-˵Zw~933R,\RT$yoR/.K5=zteeŋccc.\𻎅FQDDΝ˲lppsssBTJ;9 4VRʇkRJҒW g}ٳt#AH:r"F <##P|J7TV ä[H"-U9N7/^>?Ti_4*D@/hւD &:)g20Z&G46dѱ@IZ!I&bk##AjUY^)ƁJZP-K.,.Uԣ)E;ٯ>xx<"4N(ic \nQ֒'@"TQ) R ` "RBgyR/Ï>GC( Nyooo;vlnn;ݻ׳}N>j|\j\. loo_xCx]|ܜNC?~͐z6|饗^z%_J)8XXX!AaZk`mmmaaA)W嵵W;wnvve6Çonn=zkZݮT*K4MO֖sq<22b!F1;;tH [T^N>Bf.{1H)?ec=v}]p{;T*"z駟}٥7|s}}}zzڧa|Kl[cÇ}rYG뮻|M0:kmHZi-eB!{B%#%#bI9i :+Gc e1X\qXëyĉwsxl~y%_Ye69ˋq@6OYrؒs5Q$"D$DH2XK%&jČ(23`r&y,ɳVRX* %HŨ BBIH lO>lm73CJÇc,޿%V4XAF`V0B0JBB*I@#  rb%ZyR`ͷ3s'(Ξ=n9tW} !97W^ +++i>v{kk+Izx!~~=ZNNN>cju|||fffjjjyyYJ[VT* a oАg  bBM/7بjVkeeZoq7lZ.]Z__ʲ,Mbm@wY,F>gy؝w޹v}I)==K/}\\\ܻwfZq]N;|l6>@c111@D@ `E>G,;GdACdZk@*ӧ_yO?GzQPf3s˱;T K W}.'Lv8is@<@@P p\q! ,r' &&kLnQT(o P" e###˗xѭwlZQqRn&~w,7hyyO"86\xѷׯ_ɺ/ٳJ4M=5(c_v NѻI)}֞={ӯRk/r? <22vٳZkKKKJ)'v}a-,p_reccclll}} RƘC _ލH@ -$$1!`!uVJi vD GYI@NH!6,Q@BDڝro-l!7Ta #c%n4ҺJ 1(SNXq7͕*H%t_MbbASG)D APJ7MVcfr0Db-$tM*y-*8X2VJEul52 ԡjcc̭ FkAZA)F\PR ٰsAhLE(skA %%в@ DdӸ\pAP !P A!:?*Ͼ;N:u\T*MNNAfy뭷cU̼}}}}S=uzxkA?βo/WWW;tݾ7 $眷FQ,Ϳ3h8]|ڵsΕJ<_y$Inֳgv]/}7je<<55566644h4|YRJuuRDZAl6bP҅˗/Ň~̙3þ aonv=hw4gYRA x??B2,,MoP(FjuUvw6;HYJ(UOtYFq52*FR|ɓFR ѣy?z衇z @=DLW_]XXػws{7$njy1k$ccciz$I1~eilcJ眧fR<}_zcO^kO ?}F擹|~QxbR_Ns{^mpRXB$Is㋋J)?Bx+ |9CժV###0T*٣GZ-Pkkkfs߾}G}嗿qy,u Q$[xArHX7-o.=g$J`Y{KtRDQI0*i 8abs(C8u`߼pDAFC#{ptj7Ǵ"p+}S?OuLQg{?rÕtcIf9O;` (‰ɏ~# Yo^(+nV2:R M|x0q)T +Xk0)K- d  3- dVbFVGX H2)=,k߿jٓiӑR~/JW^Vy.Ţ_|ykkСCW^~ꩧ~~Kzbaz=;^C,ȑT' I)Vy/ f_YY냃Ƙy)eT(JV~vۉ$Iv }!g I&,pHchzM_| Yf7wn8R} jurrgb/_fZnnn@ؿ&ivpСk׮[oucc;g_nKvZVJ axmnl<!%!ͳCl X"5u.,Kn[(dk [&FZgӢ?N}˟_C=y_=4MNO>4:y774{@x>{l<A 2gz{aGJ~Fއ~j(Q,ˋOgW}2 s@ bց2hdրTgr!礅(A:+@b!qNQDC`13+ @fmlR:K ߪ`622"|c3gά۷ϯ<.˲+Wu]wqNj/xԩ;/&''_~xhwz:z @=( ~?JF… IJ277v>,xC_:0|f{V^d"yO`𤠉 ߄(YWcccj̙3f708ɓ|xggʕ+y{fccc^Th4cW^{5$QR\d&v$Ys>(d9{9ƁuQ0z<!Ll" rDLPLWflR(R7GY޹٥R!G%yq]*mvŨPc"ӀFl=wɟ-شP \γ̙́Q_w'Oć#v0* YIֿ?myP" n5oqAinmna(0BNNKI2?$ZRR7$ rr@ֹ5Z'?3{e罍uH  =wnקZ-wsss^jKl___^BMOOgYt*ӧ8 =zD衇wҧi:44a)BADZ7syN^'"_+`ۛcz|E>47"_{BOTh='Z-_jksZm{{[Jwލb82<4X^z󆤾^aj/ K><|ߗw[{UWWW /"K(Zmɶd9&1=ь/ƞa[dˢ--A$(P@A ؗk73T"Rc/ Ш7og-C?ŕQ?+>?c0>>>22rU%DUե^t^W׻vR9ukv-$ a7xcffF%zT|0#XfF$ @($ CjdDEF $fB! !F8BBɽX,A$Cjql8Opff sQwgÑS2D>4ڭ8׮\ lmeNS?pZ} ׿f\.{^ZV'''WVVj/YS ?``~ȲL}_v-I/ꁣ1333ZMZ351ͶU<==vݮs9Ԙ\>| _JeЪ}dd?OO=/^{g/^x4Mϟ?l6ѣG8>qښ ɉNs߭jwu֦z}haa/ҡCyii)MSwA(-"h!]1&d,|r" D!eƢ:ڳC@D$ɲqX -=Ͼ?$*f0d=a >d@ 5d\R(!dD( O34y+=>yOg"Al^[nYB`w,rHcWC~R7~Ψh})Pne4+Ŷ KTwIl%KbGv/`{ܢlm|[v@R."@g-4$d^R!lg1<9}Y B@ `QU i 9vƚ<$l!4jQ )$G4nGĵZ)j<˗u}GzPJ\s=+++VkffFݫٳ^O#.@ 0{A0? y.~_i3Jв4XwsssxxxKKK׮]S(s3knժjH7KZmtttiiIOw"eYX"Y8$` !ȇ#R>0#27 XA ``$ 80!Hadx ׇ v;+KKӓs;FJ9 W\EMJ_G~'=ZHwl^*̗rq"Ph˕J+H9NofbBϊs&)1@2:lvۇMs#_K`^?m3`Erk6wVګ^p&)rM@H$Cڼr-h3<'B"A&KBa>6pM\Ѹwvv>>|Xߨv"tjzڵG}4ɧzjvvVh>d .CgZU~P(̬,!ԩS^o{{{jjJsy'N:u###SSSZk>u޴[hNQi:uހ$9tPEJ(\Ts=(ӟ؋=VeǺ@җtܹ?^|ST/_. <̹s^z5cNRUv;NRloos=###8yѣGkgffN8nvǷ~5c$ !r@y (Z $HB bR1r&! R NB 7vg{ߺrq#'۝7$tΥ}''ϟ-gD 0 ++1 0={_HeS.~5{wj^z:W6{A8锈U0ƨjq$1fhhNLL._<==!!uxkkg}ǷΟ?̣zHCjt:j5044땕˗/OMM}RS5^wСzNGzbeBA[$I8.ΝӸo#Ё^'Q~3Re@D@H $`3> !A " "@DtD}.Z/ҍkW>m\g~ar`@zw?K]^ڼzW^|7߼C<@8?=3OOp;YځGN}Ϸ[v sgY玜j4c;+ׯU~wo\:|rd {_g/_;Kk׮,/k~NgіլOznmm;NԆC6v>0_ x!xfW h7:(2@֐@&DkB@ոRg>?y˛q{'˲r\4Zxǧ766^(<˲Nse)ȑ#.\Hdnnnrrr,,H8 0x$I !ZjenVzjXzRT*ŭZ622oonnF*]*kȼGߋhvr94M:Vz\cLNӴX,Zk5l{{{hhhOr=0ƴZr<666???::&/",-/;0lllt]LMM!sTvF@\N$˲W_}5I&IR*ݤXt)+BE"F@!A yf|;0@HB@DD̆ O_~&!;#v6Gf_n7\(7{ﻗ7lG9d RHv'?v:x4DHpq263{XiBtQG~µI^ZjD;M ջq_Ƴ}k3?wzvQohdF?׿ͯ=vCotmtB8!DE`s2 X 3e>8kC!IH BNb7rBd6~݈7ht:B~pbbbgg]699Y*$Y^^β o|VW_j?#?t߾ȉ'D›`8B0 yS]Ȉ(eccS+rʕ8ljH=VMP;Qm-T!ઑ?x5tRsy܀bZ(4ԙT C222b/_YO{ϥbƘv](ͦ,߿… ׮];uTX[["f wŋz_\\TmFD:55u?CgƋ W? gU @HH)2o"!4&3DDL| !DduG!D l (2 xfK "D l՚AH J)GǡB H|RD@;G zs6D!PC |!QT @_-xɌA@A@!6T@[-@?6[-Tl7F1Y+(`,:`593̌ :9c<d! SғSCo/o2{2 7-/G$IԴW㺫j>|ŋ8077/}jjMʕ7nco/ [[[Fji6v$GͲ>::@<Wl^fl7xc||ZZvo >|h:+_lw[=JehW9"z:ODG5tLuԁtllLZ/|yzvHifffh QRٷo_gffPv?55W_~/۷ѣׯ_V%IrjzܹmkD,| H$@`  8dZR@@ C *D6=.ry?'1L܋G:8gS@,$$i"f 2H5BY]Tm%df7DBz ƾy@B0z2 k"6" ւ+ e@I3246WKӂ4Aـ~@Rn :93 br@»2` ԸW3Nh4y~ss~.\\R܉vE%Ir;wntto}666~||{& 0  iu^+ աqv@GʫQ]l&N21~_8! AH!PDdddT*ݸqcSUuqFQKgϪyO쨏4$}EFC3m3{bbᵵ5m3Ne<̡C8P._\,7::Z,ϟ?EQZUv 8sOO?^w9x3,90&OXZdaAb@c1B.]DItzӄp"C۝~ :Xfm3}v+1 BDPR B!\|I| 4q"#zV@/b!@DnP$I{}A@$KĐ`'fNCؚͮ iRv3i1!@WH93!1&zE@XD$FnBoͭ ǎ?cZYYy&i4R>^gΜi4'GGG|Iǩf0 8``~HPCOƍqVKTK^7>>nv].,,(: eBA]J ck*RI:_^^qF.J{\Jh4 fgY644Bn//mnn>{$}XPw }l60QB;l*k._emH;h4>Nի:ځW,12 #g>%2 |@ЀYX o<(Sƺh"HY^ @̃ 1q?͓$bIٚdiZTƒYٔC "YfDP<:cp{hB߳CHT;g5[X,d"^pIl"3s Ih( hI )%HEAB@.D,ANC VDh"wv$RDiF$ J2 Bd00q|(o~`,[YY8y䗾R4<<|`WWW4=x Ymnnw};;;gϞ?rihݿF}xuM7 Ǡ`jZ^W k"R욭4MuEQAɓbgz꬏RihhT*MOf!"@rYIGp Hwz<7}>:Z(bej,Dرcׯ_뭷zjH_/ſWR(4t:mЮ@HBj5˲,.^h4jZY\\,)RBH>|j]pao_\\DԴc5]7/)$/RX1kٳgڵkQ};s֖Xo5Ob rAAAF!m)0cg,I瑉"~Ǝ "KJF8SHY?MKBh]?Kpk% ,Ad) Xpq V@A&тsG9qHBǎ}b pgD/hnrIbHb F H*4L (`& ɳA ʳH}. C新mphiq91F⢶ܴ֪͋`l^tiqqQ~Μ9o>jƘn< b 0 tR}IJ>221_^^֩WVV>OՇ666_e+{ow'@DDB`2Nnv6Č"ޘH,3X (tR>Ͻ3YBTDzFe>#KV 1"콱h&0g9 c$`!ge^D*kq!آz>}o$Dbu8o @l]j 5k2 aFFmA"$IDAl{CF/^$~UNGѓ'OJ#G|k_3LMMmmmu"RV5>>^,'''x <O:<;;k/3h~ [k$q]pRTQsL(=7QJ/JWǞD0gY$j-~)S?sD|?'˿|ҥ'|R@ah6Zm߾}/_n4q;vlrr^[[^XX8{,"*G;ӓS!C=t…,W(][[+ 9XN8!Y?~||^[k}iErzoUw9i!"0A 7  N ]1$,VgyA90" p.<+6r4c/L81 iw2k}`ed, hM- P*)T^H1X4PlY$."!;$\g&;g48H,0L9~ M.(F 蓑I HԌU U <k:in@4 .ry&y>0q!x#mB~ kܸqCUgϞ}׾}"Ribb"RibR{R4:::>>>22""DZ' ) > r^*+W\~STqku.]~j m68XnX{"z'-,,諞~C̨Nl68Vgo|ǎ{|<8T*ڥ,--}uSbkoOmͥ%`!3$`6 HQ8 ` (j[dk v{=[BϒqSFgJq1MSKN HL(̢ٛ'wŒB`D=3և`4@ xVk#c\D!7jÞK$@ B!!5HhQ A`ѸQNA0ED6zeg% A@Ƈ,"ePDGɛ {9w +^zy?~+WF>~}FGGejZ+++ƘqRi s=Uu=Ġ`wPͅ fggT][[jfX "&dxaLӾs^\2 2 &@Ng1 q&(l 3,4f @6d(,-_4joaFklv?tiEI8p@?N8uTT׿~+[[[bg-NG޾PVggg(R {4A0?$T>I(E h@UDܷovTJ2>>>11122 FD:k{8WFrX,j:22vB/^XVVV< !h@Dznj5.^xƍGsZy&17övV'vKKKJgnDWI4GzׯJjTnk׮}ӟR;Vsv4K"E6 2 C0@ [4 5֧uyziH8ZDD4#KssK3q9HE # 6Hn9 ,Q!wr1.ZH}](2AGE.&G>,0K }gMZk|`CI%kHwH""="e^/s~N̹5 0 R2"qk+?h}jiNgsssbb'4ƨcճ>ϟOD3(۰n066|}loo@b `+ !allZݮ|>sX,j %^XXE\ng;3;;[.qy9rdmmmvvvrrW_v>`ѸxؘlmmN hǏwk/s#ș3gYHDh4b/Wսe |C9S0LD̂(˲@`@d`!Dk,Y9 6v5|gd%l` e5e%!Z D$c43#$I4sFPյ`1-"iX, 9 yJ1hsUx8hl2!QEEPA16C DT٠`0#^)Bs%D ""߿_I>Oooo?W~vb1BpqơCfպ뮻:2``~HP-"y^(zY|ǽ^oeeEi:۞ܿRgϞ~zҊ\3}1_'qk4̌J[jZuΩ<@ԭ_CTT=:4WxqqQ uAr?MSqoԴnrQ3vvv&''ۧJB777 {߯K'N,,,Ϊ?$Iu_'^݈XK1 @H(@"Z%"YLҨcw1Ci*}c(D^s jܼ‚VEl#8A$!"";d^w[d!Y]~BN}f1֡E >2D<#p\80K1  a@~2@^Y@@T !2fu! E$0Ɛ1?szEp||??soLZ-j6hͽcccz.]z?`̯ʯ0~(ꪹU(4XWeqko0<<5999;;;77Eѕ+W^=~=;2w@3GuD%+>Mu'j5}@WD*A83DR"> hx1nk6'{"`=!;;;~Z{5 (˚p\tO2>>=;#pܹj*"Jz'O\]]}'k˗(jۍFCn"R{Ա;zOk'5k@0GD@t`L}$tea$C Y3B3 bE*V(Ɛsq h1KB.^MGIrR BȀzT%DȽGkH"}-ɠ2L E{AC>ϭ@3,񷖈и#Hm.#A}/Ei2\ks-!cATļk"Jn X jR+:7o'F[ƘZ_[[zvW^9w\Xԧ>%".]ހj -4 h۳f?_L 0 6 .90#nwOJazOOO#CCCCCC׮]) zbd=~>2 ȃ#9yNG#{-n# brQ_M@(HHB& DY0b*P 'KIR"n6 `L0Ȁ A!)B,dY'-%M}d 12#x`qe1xV ;^|V{ui+2Q# 5Nk?F'8z>Tu?t耱<;J2"Z-}PX__٩+=[n{ȑ^zillnc_}ccĉ7n@RV533E:u9W(FGG;Nj6+++xii&Eb}| ƀBlq.\H!@ ̼;~'dəxvGI< u8H}n rp ņ-/\:qvX?,QnE 5]k Krm4ICHC,b@<4B2TMv;g^) MNYɼ#BYcsgl/%QI(H7/&Qās 2@s& HH48M%Cc!v1@?l.ki4CoD$Icǎ={lNNNj8j$o=7|ȑ#~aaAo=;i`E!|9 0<<<>>LbuHcǎ^xaii\.+~OiQbl``m&:ksЈ_x綶>ϔJW^yZƽK4MӑzHZ+4`eeEDx .//bkkkggGG'~ڵkKKKZ/..?lB?dMF%k(^[L eC"5Mo  "Ffg0 #y $f6ߢ؃sYg^85JO??=tP9|/<33]t$^;zy⢵>?~eeEU74l`x/0h T*)_ իWmsy;;vƍ^{Msp{%-?ryaZdT?e|)Hcqmr(ZXX?W_Uc8tc Ʌ kcL][[+ l6TEpkJhYWzR9sh8pX,nVob~~СCƘsζv9222>>C=T*ÇoDdPH@EpyGEA"1Q9K$>Ѣadb9Ԋ]}"A|K4XY_#SGf{׮xy/BD$ hXB2βPv AcAL$) ~FڴV*'q<<40146Fd#0tH1> Q`Мc(<"1"HQTY{EDƚh"1Z\\<} kZE}gYVTO8Νz3<c7``~Hr9fID5vm?22rw9rd~~_y4|"c ߿BVXCfVDSM~h4>K/tŷzKO^^^~Ziʁ*nժuk[y;q?+Wttzʕn-0w` uO_^^Fq4ͷ~X,^zU "233ꫯNOO>}^ p]DT; 66V*Te&[^aaG=rHEN'˲^n~W*ֱRixxX/Cģ>WWWULDJED\h4ZmNT(}Uo=sssccc/_~G~j)g{{[]Z֞θl߿aaa{{{ff{?ԇ>/~z=wDĐU F2FDԠBTxˌyFg D`aW(l-@P^"fB`AHA6dYECEvϰPVnn8qB_rJefff~~766|Iiѯbm釆4;NX{]9u`xO0h]3nsssrƍʶϲn&CAAEp_Uyc_>O}f.d텯>3O t]w~/t7 /?rB~z)'>c?ȍEr)o.{DᑱezVV5@d!c(g922p'$"y’Kz9F| " #  I8"b{ ݵ gXn=}bzιuc̝wީ/ҕ+WB/_>Xx$IF0w}/JtT$K/АFdh@0W4 .20JEgv)"NLLhŬ菵:+$^K6=5أ='?O|`wW8dryam<џ>v>oyu.1_xk_?3?}G_?fAbi5>\S9g, Hb+KR>b(d#a YȻق }/$|cgUgaHX)` D`R<g!B$_,J*=44v{rr{ƍ7n][[[=v~~ƍy9r0h@<l])R:uJ h%$Iooo+&c}V۪ժ777766mb\֬BZ_>Ύƍg6Y%Ir(494MRe{z{ġ]St] ޿ɓ'oܸM⢈<~fBazzP(<JZONN>KKK >hܷo߹s DDD% gf Y7{O1ZCȀȹ T0!g1vQ`NeYȽ!kLdaiyӓSS^f q yڻއF'zPO??r򮉩)A'}#Jl/FsZw OcsOty`$(xo%c4,bPՍG4OA$ { TsCD`Kq/އd w.I4YAng1! 4.˲ׯJNh4&&&:Ή']vܹs}fYj,W\9rHGt)x8|0˲`x1h݅obtX>66?\;ѹbvwvv&3 T*Ҏ* \ei!inu~֟jeꪖ/뺋!J%e6#huIZ[ /t c=[oXˊbzӧOkBÇGQ$ɑ#G^Z&"[[[Bacc]` y?uD`$M"< `M<1dXB@a#nҀ8N(ivz{Q1Rgy0Dr!@RHQq /?o52",-T ²5}0YF\uιaG<Պl퐑R -moN C#0@6>42z7~g>36! ! `"qȼ}CNdm4"a@0lQ{>D.\9Qd@a`6D{ ^wuyl680>> /T*JrekzZvzmmMuuUwAYXX_cǎ}qㆆ3*X.@ 0{gPK+W8p?pʕSNmooky9pPhۺ+H/#2d?99y 8MS"R:dY6<<\*z^ޣiyD4<tzO߿O?Ύ&!1;z "krjy 'c\rĉFP.VyK(*=) wD$ s.!i7 nXF) ! {rFX |Kl^8m9ؗťV']kyyX 9 b)MsR;K m͝r9jJlk6R qSɩ4i/+GBb*NWʥBR)VC u{=s.X*BF`f@(6),03B,s\!27,A`k_17/hzEQT*FFF}SN9sX,:t(I^xA,k4z^F+9ݛhC>0! `fa1q<==qcfիWWGJ,?b RT:,--ݺ倚k-jvvvvECCC*&t:"q 9t"$իZ^Bqw;'jf ?m/EUI...a嵵{Vn1;;[*֘yss3Mӥ$I>[yueL$, 1 2#0{f V Bl ~ "QCy<,Ӕq7̡닏D=/<?r}m5JBхe^>u3Z)bgʶT.T fVjC ' dQittV KnW wyz;㭥[o]-LE4-J9]^*`Nb 2 !H"OۍNQHアƲ9rD[n 2!Q Ƙ$I:]wݥ`*A?4 -JPj yZ(v{h44Wqq$ԔvggիZk],cԑS"-V4g@c,S}cLVs)_En~ɓccc:-׻nZ|[SGGG477~uAvϟWPekkjqi^ѮZQ=Q333<Ƣ;CIJpIQD$aЉ>̇n?e2ɨS25PL!(S!d26LqNZ'UmYS 2T.tnhkmʅ^~gS|Ƴ64z,*??[,W`Z֊HHJh֗Y_akG[! o6Zxt 1sKeD@RɃxL' r) Y}… y7 =$IKRX%f/lHMx(ԫ#? [ٍ]+yh Q'@_H"@֠@`B@@ɲlMnR7[fh}YR!/c~T..ܸ'O?}ojui}Fry1ɗξK/Ƴ|?=Ƈ_}|Zg\VW׮X>}׽Ltwno'~o=~(O>u<ړO?O#YΚ:s: %KddIDDQXĹ( 0CKBdfh Y2HHDZc !  3}k21Q(n瞥/u]~^w:NE{ov\Gq:IDATyN81;;;ߧs`  0>>qTjjΝ;wiDW[8yhhHDVWWשs(%w|w0f `l j;w_:tHz_~@}ۡ2߇~\.\tI}ll6ͩy]bgvZǷv[̲Rlooka}P|rәքo=cD(IHrN>H<30dA,zyR #o^.BJԒƩ "Dy?t{Cyv)s'|I"gɒRB>rةBTaR {ݦ!Km(y9qNߍuڦPR9,vIXl5;ݼHCkȉnk Bcgk * v#{B ܧ$f"H0ƀ0"XDvdHn@sjnŋ/8;;;>>~bx .\ti}}}zzZe&{7 B)I %"2({tX.Lgi?KNǑ@|yi:4\+v&g@iwf<4kG~LCR*`; W2T$x@A:}ȼ'X@n#F5IӴd ~7Q n-cm`@! D͑nvB ރGF!X1c  !EQgpرWy睟gz衻̙3z/z~_0s`` 0=2qߝMynQ@ZUvz]gbQ{\Ekkkjԣb%t:kmX,CCCqBTdNctfcމiZ'OjIUտ,z^^t:̜h$"qra=I_ܬO=$,JI r|᧞zjxxz`fseeezz>4'|R(zjJ_kVrz=1l 8rV} tbB ,C, dc5sL{C ch>G&֣ (ALދpHAz@.·"0F9< xH;^  `v$B FX:9ƈm %m1(DE+((hm8b1yak""s",Dso$\jjot:k_T(IvVSK^WtUN:7`^ icީm1g !T/˓ڌ/j8p~ꩧtsr̙n?wq/ĉƘߎhvvƍ'O|7P2vj» 0̪6 {q>9+! /ˀB({c齣2D @u^)ˌ@L &FPbK>D(!0" b0-h/2Frf@C p!" K0LHlEV1U oQ*X1j%_C 0Ff֪ sg*ֿt痖~z7$9zh\NtuuUۆ<χ/\KK;;;++i^timm`?4 CBA}p9QccciZ-u B0== *+{eqGQf 1&nzjꏈ>Hj佴Z}",aVސVp 2!CDk-"fy2 4}` 3#02#M}9l@oE*˓ 'O?|̙'O^|9㙙W*4M7669zkaaafffs 0`w{J|;U) b4jteejܹs<8;?VϲLͦF3ꓪr`qgY~ =zW*ej5%:^Iq7DTVͦʂuܮŽ)mIVse痗GGG?񏏍 ׯ3Afggsns.5lI]P2"a t;jOxfsrrv|{3dȐ#@d1$O.Zjeqٳ>yq\,92::EQY[[[XXX\\l6q3l۶,k?M{JyPu*Cc(_2ڇEZ\)U(G"1hwHB[nEQv8~[眄Z> Vwܹ:99K.^(\ZZlJ%d*/_^XXg?;33{ * Ɓ1R%LD^ Q':Si|q}eX k @ mnBAGN.?3ju-Yͳҳalw?|ȑb /ڵknT*K~򼐣c 休iUd,Nu~xx<4<2hiU'#:uf={F/i7cNfR9wc͗'vCF2hpr۷WV^T+:0+^:n=4˕}alZma~h{o8iȋ2g\J r`L$2[ `e1T33R 9ch (o\Y_>wl% /|nڹ{dk&lA@FZ:K|?^_n<|b3=?], j%PIY6܍JW_]Gݠ N._-}cB Ԣ;uFkQyF˿ƹg ٪P*6biBǮFQtJq RʶbHbl6777ݮryzzzjjȑ#22dq![ȐK㬭 뺝N*\.G,˲fffΝ;+++APMO %h6nP 1M1I% iO@j$cYHOFQ[o ""i҄`SdS[܄z9sx *n޼944Or;jFy^__#GŴzGΞ={ݻwݻnO?=77s9h۶l`vvnt :#>n+ⷞyqE-MN8z,Lx/\~sVWW߼gޅ0 3?n4{f`*  ;'מS#Co?o嫶%o_'Y۷osƅ U(W~p?7k^*{~_A[H\!F-j-xυ]mÙS]Gە ۖǐ Dlˠ` -k׷GQq.k4bhP#zK.ݹsg׮] J)rvyyV8p`||\*_q{iۮ...aHȪ 2| >Dи0R41RV*^u %t:z}yynSJ)8|jiA@٣l|\ 8su2DDrѡm{hhE6e>4F pppP)uzk1XV)3i[JkVkmmB |ߧQ/<44T.~%E#rΌ(IG''T []eJ8+ i1BfF4җàY r/xN4c hLnL8t8`r4B"er\xn:h;,vЩH'TA GHL|\H ;b/43820\dҀdq__[x__ :uJ] (-nr=p 0wV7"_O q0hQ=w}~+2qCahZ67?g7í ?7 3G`#h8ۢ )8q~666{Jk?~ppJlaOek^οgJ!ď~#:rBx7>>~ƍVy^X'{Sv'ÇW_^xqccV ?<f xn."%TzS,[A؎Vxߞ/ߙ Ađ-l6r]UqČ183J5j86c0:<7B%cT1ݭr`N?xyWoa׃qFpTkttTP 3Z$2H Wr paIJc+ip8a%sNQeKc]~###ϟW_}>Ϝ8qSSS7n puV.۱cٳn3dȐG6ȐC5!P\TH n>elQ@_$m6+++=7Fyd1 -˲mRs΅Rfy+W,-- !&&&j>'ZmC=o2VuVcǎJrmb4=b^?ycXYY 0ˍ:t(I^zippP(DQܹH"LǧRw ǣ(qőt.,0QA,vO{O?Y\\Xq}}XssKJaY};+U#c}#Q-8cxdbH$Qq+_rW*U<+͠ۊlOS` G CxGϞ7}륜caƔ8\Xd",۠Ԉ]#2=O"N> Ƅ Im)U$\|58cK akdzuUQ|07ϞlݨZ >Ȑ &u,7~S^}#i WG`i eZq|ȅT,&1jT2J҄! |Ν;A,㩩)D듓oC ¥Kfgg}uInZ͛7s YܒF{w 2|V2d@| b>y}}}@BP*v^힧> yqGJ911O 2QRَ aej5VW..Ocjzٽ[:`!Ж)ۋ^&v>uIT#6v}=!H:ICr\T B$CCCfSJO~2'OaX_~r<5593޽;{.I_|gmYzmOBk 3.-,GTi~Aeoft|$v[+}1]Z}xjuGwv~?~Sz}>cmKyZȄaj*MejI4hf’ʉ8?]8wW,mk4Ia;Zi9GЀ3 6$erg`q }?v Vx'7gD)d:xa6 7k) VX(x_@dA.C!1Dy )<[jGFF:j>wCJ}j/+W lj9?~>۷_>44={= 2d(52|D nTk۶m,<Dz,2 ^OIܹsgcc҂Lrz1###q/,,lllDQTT2RuNz;f( (f8MSQP<dFDf Q$Gޱc2j0vǓi.//ϣ(ڵkyanll@V۽{oo8qV=Sz.S [&9$ Ǵ[P~rɣ'N}E2ZY*5H%346 ( 5 0}76 B0 D Ѱjb 欒(ln4[B޲x)y[_'>C q0䩷ٻP0DaV&f`{ow߽0 Eǵf;e5ph.ЯV9ܷ$I666t9}8v?ڵ_^߸q2+&Ƙ}Yjj}[={СC!C (CQٶ=33SVɆvvv}}A8AZ4Z֫i?00@oܸv{J^XXnIDZk-)!L]PYOP)JSe ]mv[8;m{}}/~\.oll<<Ȟ={vUhoVvpȑGڶ?)NNǸ|o'z[XVQk?pgbtpl P0q m & N>smgs}#cFtŽ`ٶeql ~R}LNTend'pq&'Pk8B΁Bsm6P8( Wޘ|$h@cL*Wp,CB10 e* P`A F e !֨(D42ֶ B <ыo\<7> qج7B ~rvZ1_{GUmybrP0lMT._rܼ4J  3Q ,?Tѕw!0pJ) ҦmRq'O+yBnj>=vB2d"[Ȑ#KKK+++4ŧ1y^ߵkr<ϫT*ĺji׶m=44488jzEdjAP,{_}~~^_(0$/nE M(iO8666.uzh@AGB+?9]Rjmmmyyyxxxbb"ׯ !hUnONN...WUn߾8ݻ1:::009󛛛5c0JFs3>p09ެ7DQ<7{J}˱,wX>c͍7<-|XM9}nݴEOܗ4eryΠ .^[yLy|@X۱q띮p=`v}3s8`ssT7oz`xkv@I $ G"NS!ҩPFj.8u0 `9AH[g|U]rǎk67nPJmnnNOzvv}ݻ1t}!bE{RX~*wV 2|4 >"̷hX=A^^^& /uۥze?IRRBV?pHJk׮J'x_rJr9q{*=ZBnduu}iHUl61jUq=,--5 j~19{VWW SO=F^#b.JQ-//O~0JՖc`Jubj!gfn_߬7\Ǚ޹sZ{qyӟ650ãn[o c#`5Xºrmnprj|߾kkݻw; lFc}iea }`++ }Pĉ:N522t ;T)޾}{vZo4u 9Ӗ~N8ǡ8 Pcp[ 2϶*I+A(bWCT+9k z{µWKIb_xn e#_oFvTe IvҴ |{<ȾV+رcxu}{_yʜ`ᨌgCcXoaskV /K:uϜ9gϞ|>_,3>>WWZic(R_Rlv:;vr9ʼ|;_kK!CSϐ!:yW?gRJRAr@ZShE뤑%NX$ cގOE={jmDPlCo#RʾU*H<>>NJGԵk* 9SB| UJYջMIh###0 }?~8w(,ʕ+Dv{iHgϞH~ӟ}W:NV[__rdpW,j4&s=r ČV3.^| FxN#3J(CcqHs+K?$1I.o Q{yGi2Sr=Q4n뺶T\DC Db7#(j4')Vc q4Sႋv+ȗ8N6e L1D+m42넁Iv]v<+M%Ð ۆ$5(#M̢nK~#m۶CUlECݎs#%Bc#%S*, LFҥMM 8bh['sE?;+G _W,Ik׮BgϞgyfϞ=Q]|{jw|s\}0tرc_+ZR8eȐ\d+2|D"~I)9ԪQ큁Rn PR&=ZSAi/?/w?9<˵mz#uo޼v-ˢG"ꅑQB7SH)(߹s\>EQE/"|?w !?|Zvԩ$I"-Dj9q'[2QB&%5$Z4q>7ql>@+J-5#DzZ 3VqrR~7?[ooXt0 PaVyMy2L4t Tʅc1dhC tˍ9Qr1ƸۍR,1Wji3HJ(B RMKkf`sd 2uXdUGvfTȶTRm !S,* DQ@L9/ZC%cҰ5Fs!*ETVQŅ3C$ev E?cpfk@%a̶-@*1ƶ_?8J8ǡ7n_|1۶}…n{…znY1HoYDݦ BYJȐ!CY!G2$av ͛7srDz!1.b[p (\я~#LOOolliJl"SA! ttxF1FaaCCCd[DTiŔi.Ѐe=QFB7vmڵj=s|8NEdC]RZ}WWWo߾}y@8{뺻w^YY7 ۷oHprJQpE^1fhnNt=/ǹA#~<+Sqf0_V1۶Ad.Hp#4Ɯ 42`‘R р8On\9J FcY&ڂqVbc8MmItD9Q*EGDh6L> u]˲8ͿiCw{sRk6%~ۻDR_|'z*YE$I/˖ea;=y^"SpǶfINS) z:xkVTO?yC(Ymnn_5vTj4yW<8<Ymժ3@G) &@ "Qs6X030fV?F0AJ`0fkBT` hUD a$`s0aJ<-PJeZo40ZhGt-gȐ!Nj/Q "c:c"r9QJr9ql۶¶mzrdnc"KOAy)sŢԖKOjNH@ VŽPJnllXEGNTn[=C8RuoLNNݸqu݇~8 C^tiii)M(ƞx ؅ N8qX߿ꫯVձ1K.رƍK'<@ pXFm@@h5TkAhPAqplH D! @@0@[ժ V`$[1sʼSj ;JgK@g e`s0HJӀ8l l%1 vA`4cZ`q쭝nU @HSwF d @APlˢVen U40\PKYi4T+c8rB-o+ ܕ:61"0z衩7o*<#K"9c< 7}ӻwqRS61pƺ۷VuI0\ZZzj.۳gŋFTkcꄵՌP#cB@D1j@q@ ҀAPAK@#Za6@ 4΂jcym]0ث; \p%_;KxZkԧ>5??ҥzիRo~Ƙ;wjl._WO<E H)"}$2dȐ k2d>00.˛4}\. jcsE̬T*(&y";wE}nwAPj}iKx` Z|_wt--֝:uS.Ξ=KBzNz#<.n޼i9{޽{K޽{/^hYV}ssjvqMʐ! >"P(IsmەJ%9rڵkdOr``2z c?Tg |,nUeYA\|yˀR rb:yub !&R ?۶=??cu::t賚>NiP3ȍQl {ȲrkH)`0BeuRmD+ӗ(;\G.Rnh;ZkRZ+8_ R+2cec01LfsJq!<D*T"pۖe "28*C%Q r+`DRaV;(tTZ+ZIU$:G3*YXPbm5 @T*n767 K˱Z I$Xہb#]mn^AEX($,ƥщB:Z&AS-0bA,ѵh0LV04s 3o枒4=t>;w_>00# v횞^^^xbZmZi\.l6)###J' ɜ@3d!k2d`YVP`mllŞoll,--̜={<7)o hfd˴媇^@iN'@EB !=sa:uʶ .\P((̶mz?P0lj}/Jǎ[YYv\naaɓvJӔ1SSSZNرco߾z@Z%q<>>>66=s…fI 2ctQ*%)iC7RL=)lj 0_rj7*#y %>@yav;1"jqn$Da9 *3i >1ú;W8,h@HTJT ldjۈ[omu- "6u bJazNX+ptبXBd Uu.SWuA U(8L0F%c.4Ȥ Ti&4Ec fqR*H4K$঱ ` A#O "2 " $` 0!D^U '|嗩K`mmmnnz$8Zn8NѸrʗɩ>|8Iɐ!CȐYѓNϕP(] Ǐ){֧Հv>'ll3'^rݦ-@OWC+:I̐  cF֥GsZ^^^ZZ"KeYnwqqѲbx .~:"~򓟜o6SSCCC jϟ?u{g62MD ǵ>D A ™Skad:8P߀uF=%!eftG*뢥nH1p @RZqR)2VF0['&I*]֩ X)1+>W`w{RkBhGaJ%6a@Ԡ|Kp8Q)B`ƶlRZ3 ŝT* <6L8"R:N4Aqb,MT(U&WRk ' PkԍVlII7IG[KxHC^Ria'N8uTXjrl| yvٶ=11qQj_ƣ><Cΐ!CY!Ç 1nW8ROzVj/_y93ztd"P =s@|c@A8a}f2Z;9'rR\.l6)lضm6I2EjRʇzhtt>{l>&Q]T*W"w{n_ps~5u(C?Z|>I]OII IiP j2hAڅ k%jqʒTIFH}},N,`4r6&c h0ƄqjY@ #0h#A0`D* @Ƒ3^*vL?kc+8/Lk B"/Kq ةPJiy*Je,;ICxms"YQ:Lb˲Q$xv9'IbJZõsQ7`\lr E ±S8'9Daf5XLF! 2-M*(0V︜n>~q3@uW_}hp;IV^~幹\.722rڵcǎI)-Y[[r ".--}ӟ^XXsssq[#ː!C?d @ "zE9<㽨V庮8ƳxDz=u6YӍDt|P`02" |>iYerٶ8NNӔ^ X؈'uܱcNJK/O}J)l6[Ql^[[kZ\~qaa|KO>MƠnzGx ÒD]˲m8n.g)h`.Di8gF(N,A-qkbК$h(0K L1 Ȑm2PLȳ IbLfqG=|ߕ{[9WtR( Dj`h cST(0$ڼD~R5hr<;N i\|SSG` tie11qҥ^I\}Loܸ333Q%`---a[oR>ΕX7F'25Sb̂Ni14.{y*:RgI<3Ƙ85j >>W(Ш4¶| P (VF۞Ҙ1fs1D)$2I,(B۶-(vm#O»s##Z RH%*-b[rWK$p#7\e9()e0!iDyI4hッ{3Ӝs"fa7оXB@Ž#GD\1Zs[ `5aYʠ:I,`?XMVj1hFu暛V׮]#[nݻwccc׮]rСCa^~|u?C # CwyaۭVƘbl6)krrjٶh4jeY$$RJ)da䡄Q|z= þ!Z:sn[1{CzJfDOV޵k׹s(G,2'%Tl_=\^/]Ǐ/ϟC###Zmvvvxxx׮]/^^e/ qԩ7|K_s=744O|{'~ڙƧR vDY4&xI@-MV7(aQ ,hkF0-qt|OQ`5< hAضT FC"|l!U"5J.p0CY9q O;7T^ROY]̀, qFd~(@A $m[C20F#T@Dre2\K9&0^#c. pJn;VEV2I ĝaVVGE`3f@k r@ OR Nrn>L$L%1(A c>:%u#۷RxkkkW^-ٹso1000>>XVk۔ⷶARv-ִ2d"k2dSRb8==Mނmk~$N ~ٓ_P>Ja$KKKA~Ϟ=JFAvj8ЮɥU˲fgg=<+{t04)6N#ӧOw:B@K圓nmRnL aF;v}3gG? @kMQĝN'cZxGo}ĉG9ro_zR髯~Ѡص~zЀARHRE#X P@?|xqf;P,Buy1:\ҍ?\ݬo+f;n(äG=aTK.N;6äTH$UFƕR9<U*Xi&A't PkrE DIu"p᳽N@$J*˲8XłmiDΌvmۜYFic@pPe ѠBf[21m`Jy,@j/o!Dtj|`L3Hc(61Ppcqh`TZiJj8 r4G0d?tw:!뺅BP(rbXV_xۿۗ_~y߾}n]v7o}ݸq^OOOrׯ?~K2dq!k2d@&9DI(Ο??55EV)yiv$qONN6yB)XRY]]o4/_&gqj0lX, SNQiRnbiI[nbȄ4JΒRM4ݱcclmmLQ (!F~ݻ_}JB2$I^{N7QbRJ0 )8EbqbbbeejK@I)U 'M1ƀ1`E,QL*v^- ޾s}r|k]~ ³?_,< 'o(-_ D <|ZW{vcu7n;c"5SF+cT10Nɳ ,P+A˟=r5bЯ%@m J:`•.Hp%ׁ4Bc3!jmPx- Rr4ѩaC %-Ah@-`9{-^{mYv2Qr0B! A䊣A.V(, c gL3kÁs` X`Kc~zTb1њ۷կjazez!ƙ3g:488XՈtD|>cǎ]vc|'|ܹsHYDQivTBl̓ i ˊ1dN"@*J*׳-\8̔zzP3'j7_{c/@Ɖ뫫7\Cykny]zๅ[n'FAl&_~aLL2?b4jq1I6|Ef %4Cw}7'%ݽ1n% ib6Sh1э:Bi ̶m!f1cR R9B78m1dvN$CY ,C9 T ÅF# 5 0nhmk% XFi@)DCQ~Q/nfffeeh4('x̙3zw܉j 53'Nz۶i݀ w0 ðhJ՚(˵Zիd_V2z#b (~l\P(t:E"KDQD]bH !zIP ͛7~'ѣG|gVUy.{{챁U۶޽{}g}vuuurrW\H~ZYF 0TN'K˲L1@a#Ur!8w~um>!74jc;prh=},[CGF-.:o7Oo$UIeBZkD$HM*]UZFTssօ+6܁H_b>78w44hi1BD21Hc  Gl('lDzSclגq Ƙ'61M04T]]`|ΜxcLӍ<hSAY΄Q4PFJSԘv~oRʲ9c }m(vŋJիdJM&y>^{5D|oוRgΜ1Ƙ.C >d @ :bA _T Ӭ=$I 뺶mcEQ/<bV]v}GQDcxJ* vl9ER@))q666EDBZ)e$yGn^]]=w\\NBj?яzjR9&}MNNp@V23dȐBd;AضMVs%/K*Iś$ %ldݓi\.a᜗J%cڌVeYm4V(HsLO#Rټ{k]m/yOBJ%jCjummmyyyvvP(|͛bQ177l6Uܼggg)%m???/_h4R}}}˗/+R˗/OOO[ T$Ix@! Mз qj6۬T 2_! AQf ʍۉ%h`P(nl 4 8dXiʘB0fnlonv2d#8 %q(Z"7ѠmYgw݁~${` BRvW+;,f@i`4[A@Ƙat8(- 2]~W=7xI-c]U.:jD[H3Ùa)́k)dsØ$`JI ]%J)%+"_R`xx8fO|qZvY l:tT* E)C  >"e/xҳV,i@RBСft:d8΁h#@ QݒݶmrΡ{}*S=rWyrj9sfttX,nll[*+v !9Rjeeرc''o߾((iX)5<<\*cwDַ8.J+++0>>__w}j\PO:E g@(8NJQ VpgO_. vs k]/? 02KHRi R#8N-X%@ 3 "j rQwPZ% cm!:DPAX6  h+ߥfnV.fAFfqf%{˵LPj&c"\KıD'RFaFa8GH`lD9ЃhI ́@P W  :Fm8 N$ "̳~Apuʷ&+ÇS g>ӧO:u ?OܞnK ʭjԵ !|Mg2dx52|D nR!5ydpINseWPM_VveR ]@!jB4 #oP*0$jH{@"VtX\[[ <*}.\ЋҟT䀦jw:>T&W\yw&q~~_UJݼysiiڵk}}} n777777|ַħxꩧ|ӧO]MA5ćqv:-*$݄&CQ<P拘j0 $Q "Mud}84vuRehD2W &(I0tF1rm;gq%C8|wP4y{0˙':[q}KeTKPJ) cD<"mϵlsP0 'ʷNK)FH9An}_ 7*i`daSJK5l hh8J[xI(?P&Ν;I355eY[oO|bΝ'N ={7Hu366>55UT;sNؕ+WR;v 2| >"8SʒNLLj5u.#/TǞm-qr =-/HKm"AB9&Vj+Wb___$CCC$w^ZZB7$}ѣ\\._p"T[VѸsui={@}վ?صkmh-..o}[dDܧS""0`fjabKtDI+8hAFJc N ?#O~jXx~S :8] xBA׆ s2jv5pELQL"gs1 L۴,m'ڰ$ {,}2zE~ h#ho۲:Mis[)H4Rpl#XNRL',5b4,ÍBCjAH sseUI €F = eR4 Cd4p.dTщCD R8 c@a34-G Thy-MZfGGG>{}޽{ۗFZ\.WwEj3gP>@&w/c 2d8> 3"޹sg){4GwZUJY,O<)gQq\.700o߾ZFb߳wޒ'{%F5R0 4"Z'A9mn7 C)< + !i+++Z\.[T$f鉉zzyDt:fu;wܹs<"YQwF(A''4i}{Ŝ?6Z06216ZUصťzos]b~{:dx7N?a^/Ir;*Gr9...߼;=wl3\fvOܞ֛Mev2JR $˃6]}V}_Ɛ~L@-e:mu\ LnTƄ+mb4]Tc"u0eL)(II(.$RER'Aõh D4$%~L3hAbgZ0͸pi jk@&e (enܚ9? bѴoaaȑ#oJ);<YYYAPoR$}``͛|+_E}2d_(>2|D V dhN9_^^B viO:Zс"v:\RR=M Gf,Z/MS v]=ahhpE[,GGG/^j0hO.@tP(?^k=00@O޳gRK.qݻWWW;w]ziiixxѣn =Swia||F{20i~v1ZgǑA5ВI`;_}m#`R݉#u.r '69DR`8952=c7q&_u,f-/m= @j4gV+b^ Tʠp ƓC3Qs6= }:ӚCa @Z#h @lv!Z4J bsیY,E`(@JgN 2FצFp_} [J6hCjtlpD&%:]drCCCq߸qcǎRo~nw߾}m>}l~s1ǯl(xlllddW_t:RijjJI9C >d @ |'Q{'u/y$!뒧gEZ~&1Qp%v x12֬j|rݎp93ӵnj( P(e_(]VWk\uV2dȐAdmavjnC DmP(i)tj;{eUs~?Cbb*?!?=k׮ /--[5??O%o`"{Gcccju~~\.$IϞ=A8z8oF\.JqS8 ]vA@ O!(, ΕR1ZF3hiA"D (cf&UI-`jDDH=D Cah#3ʄJI#p5[f q`c1%2097i k̖gSFXkͅV:T&䖭DDZ{]T B#hнSȕLzCT`hGd [ ֦:A!7[\ JujsGkƀa/\%+*]600xѐR 9է>??/ r .\`(\&$yB!C8CZi{onn˵Z cVD ,B)1>DQn۫@/[(z 8 <ϣ IǶJB@{*ӻ }@Z%W8ץS81SAOUL ݂1fhh1J+Wc>|ڵji###s޽{kZPx>OOO߸qc~~j766&''GFF]O6VލqFPx333?0-f @ >.d+2| ?|?,n+ QthO|}9۷/˭={h@E۷&aWj JVܹ3ycLRYXXvb\ݮ҇16;;RDkffh={7nܸ~: _vEсVWW}$I{Fm|~~1kcfgg:Z> n1zb{Tl?u?./z/KB85h[dpZNW.&-jXEQE'}߿y)Mo||o߾]V]=y$_3dȐǤgȐCX$EIDAT ^(TFK!L|^J9117o4\JSjfgg6c4d+2|PR1Unnn6M I*wر@ChXXV JǴ @|FQz931(,ˢ4_ .uhE]GC`fffzz̙3TV A8C\b4mWFQD^۶:ti6ƘGy/Gj|__']v]r(cVav:)k(lRK>$C j_ MNÖeÎ\z\.߼ynq|itǎRI)UV=۹s8OZi G!Gn{0 2Ʈ^*9edC*a6l/iTICj%FHjm˦KﯯKQ ;r]{uguu=/wDvoܜRK_ғO>Eʑ#GYp7੧J~ǯQtc]ٳgR4::JkۛSx=?g_ǐn-ˢ:vݳgh.B@VCCCICQw}ܹizΝR3ʐ!CY!ÇnYV\rbP5Zk2'R 5;~Zeqi}Dߎ1ӪBvv-Iܹs;w\]]|>};R\.GAVc=#>veYN΄=#Ԗܺu롇z'~iTXR1ܺuknnnmm(rJX]]!CY!ÇS%AZ(n&J1HO+LĶޤ3@Ae7EH+[ e]*!c, :s{<:ǑRAO>y|Myh6#[:tB;vܺu+Iu_n9p@TnBcǎ-..c8^%3^r眒7_|ũG~rcccW^ml S-}wɫW3<vfȐ!LjȐq[C5:؞{HI_(z8[;58Ύ;)Xk](Z3g$ٱc'GDh;իW믿&Ez ]0::zo.\ k׮c\rwqjjG=}RZ ~!cRihh(ˑ^۷ozzʕ+ccc."s=iv;wΒ"vA~ӗ"eȐcDdQjcŢ1qqRt{ L4>AƘRu](2xt*khs>66V(0X@W\#)Rә#GNz,j6oѣGb^;wysssgq||F#MSJggQ=#޹s^W*{^|1FU3_$/ԫw:4Mgff9sԾ.^x ZVdi533'h,,,ٳgbbbyyytdbT͐!C_ P r\+Ng\z 9眦'`fDb].ɫҮJeVSYd뇫\*KNvr n 0&`rwOOߏ<ڟ]%5v9} O`$IZ<^ p8Dj5Qň`r%E)OP0 (&Osf[,D"aHy<fJ17 J2<>L&(;wJ%L6?? {=L&J^wRC`DVVV666~UyIh*:BVj4L&hrX,h46 RR0 HRf?Ad2y9sxxxB$A4 *F$I˿^c^VPlBV f0R)rbq6PͦRbFl6,A IV=cp8^/۔\L:S(rܹsFd1lP(P(N6myyhDbB!6(LV~_XlVӡe4hx^4v3jF,aё"`0@3j*t? $t#†H `j?v)n띝BP(JdJHO- b6JRlFX,.b^J% L&zj}飣P(RID"Y]]/~[oEQ~5M&"GX>%6@/rP(D?.o}k}}T*;w.|frW.hj:1l6v;ǻr pҠAs-A!z*cJe*PVd2'j@B2Cp dz5r.O}[ .ffWU@T*rejC)MLLF+A珎I$FNNN*P(, v[*dn7I+i&Ե}w.bj{CܜX,*j0T*rd'Ot:Tzvv633#y<^Z!I?\ZZJ\.?߂ 4hРN~f O6)P(B@D"A#jj~5:Llv:% r\\?G#FjP73L\l6ݮ\.Y8HA05Q m۹\@ hZ W^ybjD"!H lzbWV\nXxn;ϳlHh4jZ jƍ[[[fI\.w0j p=_C|Ў|\n8>>_rEVז.,?VW^BMh0$6X,l MܟVd}iРAnh[?yVK,t:, 2Y<T*xy&X,Tϰ'O$$IBfaB9 H(u?3@_#J%Je2YT n z,T*G}411144f3L>c"rp""Iraaavvvkkf/{<~o4e2Y0dX0Z6>>>99d2d\ v:O (.\P՘LfZI\.7Jl6k۵Zٟxz@ H$XL$e2d~r*H$ǓH$&IվJyi4h|AsijJ%J@Nt& xnGcPדɤAFyTNb 2 ^W,`Xz=ulXT"؛mY,Vv8z~ooO,b2L,8. 20EdXDkkk.^t`p8Bpttʕ+~Wo߾jD@,;w|>`0 nPj`hhZ;u*b1Lj=B|{_o`4O^/?| Q|>r\j'bA|OЍ p8L&'BׄW>f):>JNh?҆{ Nl6Rimw)\v;0 2N-%23K'Nc?)).n6V'Q><33LJb£GX,Ll6x>T*H$sssF׳nW*-//3D"[[w܁$T*-,,|>d2Qb RXh"HPX.E">Hp )wTB!HәtT*}&`_TZqs\xM\4(񩋉[ ?jߠ6E/N|2G'OC /B|'N^.߿ ~o8;\;A4̯^:>>hfHH>^'/~ Z=66P(fǏ"4hР4zi2P3Lr$IFST60l6hD\.nVP(0T6C,kZŢ*J.Kw2,0yg2DN;,f+j ˗M&999 CCCJrggg}}]VD"T?6 Z6x<==}WrFy7y>@*"yzzznnn6GTewH)x<^0<==e2b1N @ `XV`NMM5b8::H$4 *Jvh4PPn|n<@]VVk*###tzZ&Jt:FT*UUYT\B~rlvVT*:q `0fx<`0(lv.H$J2 JT*I333d$ɗ^z闿xZ*hF"z8rT*\d0hZɓ_,rf%ōi^_%׾j.//Z6::\^*եRN~ii kn7 VfFU(H BB:=As-AᗿZ$I}6% "dtН&h c~h4F1|>qtX,nrrc0[b<bb5+Z DFH$M@S&^nyׅBa4D&%&S(RivvveePNOO{^ptttUR/]0d2c&L&U*˗ Aj曡PRt:Fa͉^_\\, njD"LT*{ޕ+Wtp8s+Jt !Qx@]x1Y,TL& NKnZba2p?433H$0h4`(q\F… lVPd2|Ff)* ŷ\.B 7[\\<>>jk  r_N ʫGAP(@ Bb77jpEDD".p}^uD ѠA3 A>ŘT*|>9NXD%DyzTnjBJT*FhZ6pD/H/l6Vt:MiLv1JZFSK]`(ͮ@ BT JBF%i/}Kr9*JX,JdHdggdiZ#89zG"JJ=z}>XClB^/LOO}w~Wkj|88T*up8lٴZ-ˍb%cr|ҥ1|Fc~~~ff&JV`0HyWT$ I@ _QRG7>|H7j"W*T*+GeYñ~XYYT*t<(ak H$mrBb`NC:Zd3^HP <dj4\.g2~$Nl6WX,f0,K T*lj56}…mjZH5hu:A@wxxIFP(T*>|(2# P1Lh4'j7ojZh%4h|FA74h<#rfEL@ 4 4 g%Ɂrp LP(TʕNffl&|>e$ P!Q*l6jD Q+|>`J%.54,KחJ%H6xJ=%(@Νzo.vj5 -5CCC>pRc󗖖V+ @  ,\v M>/z6Ū*?::"bhhHV*Rp fH2(8z=L&\4Hlpjb(J K/;N,j6- _~Pt#lr9U@D0:X|rrf3"$Zm>W###^/L"1pXVccrTʕ+r nV5 ;55\$Ǖe7<<,H`F"gff:RT*b|Ph6n\.7 $i4 C:.AD"HlLvj TT(tzjj' 8rZJDHD[[["(Gѩ)\^(2dP  ,K&=|b(Ux W%GDX,* B)xWl6t%@d2ƭT*F 4w:~P(trY LNN޽{wbbbaa!DQ$^/I|$FAhd2g3F;qhsstpL_O0vΝspx}}R8r||`0Ν;b uM&@`bb"u]XLD"gzzZ&靝Hd4l4UT J2c`0*&dfx< r :==V(V+I`$I6-ˇfff:d2rXlV*t:fqq89$jH$Z.//263GGGB |>/J`a$IJrLT*% WDb&bnKJ2 :.N D2 2fS*&HRѸ)y*JFJ###R)|r<`3nhtT*7nJ%,KX,JaTVF#|>~}? $I 41g?J9DžB!5 NRB! wRbҿX,$ cO% .NǣF^\X,χvŃd21 ~h,//G"v=66Q2j/˥RX,jBjuVTp ,WB`OOOͦnd2Vs\.rB^kD"B ,+r%$y<allj \._]]X,of0L&wI$&^Z֍7@jVVt:O .r>š!BF,xӛx\.BJ^,!3`0jLT*eXL&P. F\.C"ɓ^zh4trrH$BA+A"I j:d IKD"JZPt|>  Խ{j(~*B(/"u:p8FwvvPq8R5ϷX,J%|FdzoN+biZX ̓\.{~~d2A?CZf3fCCCX ik42 /du.o|C*РA㹁nhxFxe2*ŲX,`~lP{Q@ LQ[ҥXAI !EK@d**J l ^q`Rj`T*S(2zǚB(idJ*@͍Q.jZՠv[(Lu|3 2?~d2/MMMT_~YP^p!H6 {m4QZm49p8u:]("Bӝ~p8BV:Eӹ\jT*b1(`0 y_|^4L&3j5.RhR1ud.N&͚L&Pq`0 ~霞`0`CR,www٬jJ󇇇tZPDZR .KPsSTW^MR[[[^f1H$ԔL&j "j5H$HB}Nw@XL&S:b1$IB`2"Bx+ ͏Jl6WVVNOO\."R˗P(BPv:h4bBJbM6 NQ _Q:u<NSTJ녯~|>`4hxn4۷o%r9HJHDVfT*?<<E$N?|P(,--Gr9Nt:<&jx< 3t:ѴzVTvww'&&JG>\._^^ro6^HJF.H$zATb0஠QSSS_x\.;w.ͶZ\. 2ͮ\fh f2Rp8|>_\V('k4w&Dث1vvvV,!V뭯(JLBneww@Hl6a^ VB*y(ec z8V*je7Zm\ebb"=~/ko$jpV+~d2 fvlv<+++;;;ZۡwH$׮] rQ|^l6kj.KӁ,ѠA3 A{{{xbty *|%5pr9MR=`S`X+&(|EB!(˰Ԥ,!h^. 4BPզijw\. BT'O{}BDl6x"V :KϷP,1l js-~~~Çv]&ah4P Ftah4`V /b*F˜bπ0QM&Z|?LMMjw楗^$nx=^R +IpTNNNqa*d0ف`ttD;Z`0 y$Ba^?99xddd ^LFhVVValjeFAP( hBnfpJBL6Lf:xX7`6 0O$v{k{KLMMmllW \.J%FrN#Ç*h@r;mZ-`0H6Vju|EFd`Y*Bp8 c_D.^j B6zl6'|Y%4h|FA74h<# %/zX">@6Hd2*J*C>caWrp0~D"Lv ZNx<###_.8(@'I2_@{ :1Qud.$I.,,b >T*ƍz=\. =O$ҒZd2CCCL&֭[rY,R)ñ055hZFʕD"qrr":N8wġP(JU*W^ybVUq(ϿvZ /XLMMb16$N'EHc15X,v||l6-v\dy[p}^}'ҋv{8p80d^?9 `uo"tBED(h45 ᝝B3S\^Tf@ #0ϫ\.JxBŋdsssXX,:Αd29 h4wܱZjHT*}1ԁ@rU*RL kkl6D,BQ*j5JVEn~D"9w\>/~8Nx q8FaۏaBK/4 fggE.**j/FӠAƳРud @jGm.!fS {@ }ѿ F0d2A0m~5 #.QiFFFrՂRT@ pp 9j0Ah9‚P( Bj4=6RLfZUB? /pf.JLVARDT*uppP(*h =zV}2D/ɴf~eebd2Znll @s rÇ,믿Y yL'&&d2Y(b0333| f Bzc~|`Xft:rz.ÁQ\~饗LfP+b2qsYXd2/_t:>|\~Q8@`nn… Ā89=1\.ácvfu>zRۅ3L& r޸DVFTdZN#Irhhhdd$qжlD)#i:ǃ=*^D=[ nw"JP'u1fIVz=ɜ;wƽ=\k!yMAgt@3ݻw?~ l6L&mBLc =L&ŰBGjσ?tX&T* hB0rrg0T**?f᧎xv &4 mbq:7C Q\~P(pz}ZD \.CR 6HSAAd Z-RY, :| _rXlnnVJuHjݮf;99t:0ZLfvvnà& k4=x|Nw…U"ȍ7_~>x`xx5NS*h1;; XL) rSSSl !w~~f~c4,f.0{}}i_'#!HzNgnD"vZt:Nr&)J'&&@͒H${O&&&ɴZ-HMS4Vq8hwwwfjAeR4*K b(@ {뭷n7F3 |T^vܹa| 4>4677777|>AHp8"hu'ϗH$`Z-_rT&ˤRT*H$2LP@180)Jj<o{tdT2F3 V"L&& 'uh4Z-J& QI(0oz.KKKśf- R;Ib&)pQTGGG~_,gp8|yӹ9>>^?Fr >7 d2AS[(+++DP |>dz{_$AtA /;ė\.rD8uG(*,*xQ`h4@,D"!gx霒#X,XlZaςj MLLu@%IՊ*蕩!Tgb0&''JBP^llR "P(h4$I|ǽ{ N(.ﯯGZ2Z۷EjX,@ 9==}pp k2l6fآj&"IpdYǃaLj&Hr|XcccJhffymr9Ll6 Bp\hX,*X,h4bq2DDN K$q8vm2`ni\&rȵ a###% J$Bad2ӍF2, m `fZD"v BD[n?;;W 8999;;\.rVKd$Ѹ ͷ\.d2zaPj5:ιsRNр-ɄWBr^GWU@{ҠA9nhxFܼwP(D `R><$ \$^] u:XZ QAZ塨E(P+p8@JR1L&dczX4!jZ4ͧmKX@ja_Ώ?`0V lP(泳ő|R \)Jj5Dng;wt:]T:<<D!iccT*aN^-XB0L:RM&A,Jl6k4IL|~\&bwlhLR`9NsppVc^x!PܒH$p82ؘP(<88ݢja7)ONN8,*5La` H*#[V NNNf3&A.ikkK,KX,f|RGb$ I@@.#)`v{0P f TfT*%FFFKޠ BT)P(T,vZgd2 :`0q`MLL !uBL&J`b|.]_h!ܜP(dXKKK 4h<7 h@yr B6 pQ V*6 P(,B$IeYE>u$& 2Ǩ Q^P.d2$I6 6% hB~jgXFQ.|X􀭄V"2R(|>v_}UQB 5b!}QVST`{^D^/\ZZB˄==ZpEjL&Av@J¥F"b o"rbl\.d2KL? hPUL&C0i4E..;66N$$IRn6T*|:99A-Ūj ~DLX,JUUDT*A](Z4(%unv. iFj,x<qM~2%t:Zf R!=_l6Rt:DlFvTVr > C*" \.bfknÕ|>6mj(ESh$h6Jebb?omme|>v>LAѣG>Jv{T9B 2 E>P(JԂrq;rH?~ +FO7~_.a Wt:UG$D"IX, bm4IEP#/(P?Cz~3P8>>F\VAs bdd`04MN oll,J%IΝT*n޽{E?pxmm <|>r3X,ŰhS#(J\n2`1p8V+ ቉ @p@ 41ǓdV gxèq6;;[.lK?vAG @*:NY,ϝ;d2JP(4lFBzDjW LbA0%.5VEwT`rr NT*4N+Vv t2 ׭Vvjj4&?.KP=Ft: Yx<qL&H 53\\.Hd2H/T }6H$;%I2L>|>`@`Zz=N`tzz:J^x;gX m.vt:|>/3̋/x,As A*oɟ <œ$rZ-^$l+lBM ZN߇WY;0,z^d2Nb{\.f)~eR!KL&8\. էNA h^'JKܜ<99hx 8<DRB!ӨꉉNOavKéT*^},A0Bj e` dpWVVOdYDZp87e0I&-:\|ԋd!FG ʰ">5 LY,Z l6=8 ؑH$v<_d2>~\5A^B^,j%J P?Sp_@f ~!S!tԿMh<|<?<==n[(x<7Lw Rhk yhРРl6Wt: vzAO&.KR!` a9%g@H090^T@nN*fS:] IX*`0"xMX/\l6K,x*i/1 ,R~@+d2nᰄA Z \mN@ `0v{P@{0 _~e233ѾL&s\.fNJ r `=xHF{~ xD"N kJ7Xlv)*EbŐxO z0K$v[{`c^2AN\.xxbPT*Zv777`tNS,rY XV\@ h4'r X67&@gX`P)3o|@0E-RfTW~ 'SsSŖio(8z^3s}x.hqBK<}ApJ ̓g>aIe+fv{}{R*_.^?~ P(l>n[,D"P,D!YTΦpݝN~ IShР@74h<#( h4 N#X,H&r25J{4P4 HD|2mE,/N^w:j*ސ$d4r14)0gzd.GpF8^ ^m~car(m6H(*J RVmlѹ3xkkkrxw¡ښR3]Tjl6L&ÅNRR((7% edTYLt*UۦdДHSħzt:29444<\P(D`0ީyC}'<)V+g=b~zi 5ԧHX:7dO4!qP?VrTPuj{HZ G9ZGxFNJC >ip,ex'ׯjRtv= j dj5@ h47͹UD"`0͇v:|;_t:_,af/$4h|vA74h<# YKQ`ȝDyer*JN'yBvDjJ(C0mc ( Ϋv-ub z< ()u)U{ F0E!&%#v2ƍoߖH$/bV|f3H(D" RjDj_W=zzJd2<|0P(nw$l c}}?H$p8?O*ҷ+Wnll L**Rve%(>XLQxZ_ #v: AȢqcdEH$H$vAAU?Of󟢩bJGr D*Aj  .@OE_ZH$q[10~h|Lv ^-1;;+(&fnA:88~H$GN@$ ??fv=66&jP(PF:pr…=6m2fѠAƳРlD\crbdP2!z}:+9SmmcMq#X}rsbx<"(\Ƒ@ sy<hUY pZ-S(y5` "?KKK/effFRd2 q2 .KVz+;w|;YXX?rVjl ɤBX\\x spp7 :. "b ~D*J.gY̖nT*+ dfN* r' j`Rp1$ArۍF }$ff댬_Ţ_B*Y:V(H4\bv% i4(app6 \w)x"z D XLIe2d"X,m0$ qg&IRssssssVa!6_TZ(D"`ràmmmEQٌR)0z}R Zy ggg As@3~O)bEjd0<ŰDz^(EЗQr9`(Կ~u8vb<`EgXrfC<*F!l6h4 @ t:|>l6$|eT1;T D h6>*Ђh&i2 uz=j lV=?_=c_(ժl.j9pqlVR~°˱XL" OSEl6l6tJ2t:Fspp>bP(jPiT*5Ad2Pz=EzHeh4d2ZFa*]˫T+rd& Bl6+ cVRXatSL$JX,J$}pNNNPJ2 rE^kBRD"_PU<O.;m\ F\d2z}<p8&)bՄ^O&&Z9h4D |\.d25MQ7o~'IvR)XM 8NtP+o}[k РAР@>GMLY@wl6juX PA>\. FAO,)Ta 6aHL&STf\RkV3L(w(P#牉F^WQV!j7|$ɵ5(Ba8.˳@@TvχީL&H$*J"8;;wERdY\y$ E8gggG.# @ LNNf2tttHLR$Im' f3ZBZ JVh$R04 RfV{ʕl6 Y3f: jxxZd2aۡp8nW,0LĐzTZVJF)H $IxRTP4ͩbX!I2 U*~/HX,d/»{vv;$4h|vAѠpxxC&( (J'n- 6@+ y<,h=$R:* 6IIY4 AER}4B`G!Aq!6ݮBz}ll;TCbd2fT*ݸqg2~fymmv_$IryyR A]ggg WjTCCCJ$Ijbhڝx::R,Kݖ堣Z-ٌd2Lp7zQ؄\.DRdؘ`@wh^BVK&x<ԻUF@(p\TE%2"b~~4- F#b0{&vJ&N3H$r&Z&DzRb1^t:;;;i6T*kZ`Wt:?v}ƍjRrLFєJ%Tz#ϋ \./, :,AsMA'??G5w6A&|>rRlD"ZZ* L衲dJ l \\.’P&<2RRXOrBpddD\N)>uł1F `H$&)HѽPgiQ(v:I^l||\.|>N3"(l6N/^HD6 *f~xX]]ǨT*: d2Ch|E"Q^Jh4ZX,f2I&?|FK.Cjp8^\)3t:T! mIQ!Յ NT*i[,Pl60* GPZ"`٨vT*|pz~j4nznt:=volmmY,NsrrGGGA Y$I鎏GGGe2٣G\VKhM#xzC!|rfN&rjf~o6U*X>h?DVVV/ŋ C?ǍJ  t@3|w>NCe+Ju:ʋZj%\.U( `@ Q&|PF%?)s!jVjwE[*NNNT*Kb~(b0iJ ~P$TPb_LL&.g. t'&&Pb}Fd2b:FƪX,&+J B6D/gV]]]h4ZD |:Bd^3 |l0Lϟ?<ddDsp:L&x< '''SSSHdcc(jv#lV"RG"fi0?7of2á|J:>>f0f6ԫ'''jW_E7]½^f;cD:) pz뭷Ǐ$A\xQR=xV|udBth-``ErM&SVxtZ$6q\,v`$^Ǟ$Ij5 @Kχ ٝNz@`xx8j5v3H DNHR~_>6>b^tttT.G/~_&v8J曋 4>Ӡ4}O FVTjZpd2LЋ7۷1z`B f/! JXnsjHP">GQ)JtlPh6$D"5>>.,¨VMNNb82GD7dNMM1{1W^ybe١z~ppg2FFFvvvpH$?NNN2/D"񆇇 B ?`0tJ T*UX (pA'y\>/\ш;YnjObsssp1\ !I2Zޕ*Rxt:nh4$Y,L&ɭZl{U}sRikkK*T*L_Tz.ɴZZޖH$Vkxx8 akd2Cb&FtjW n;LJ%RH$]h4~ 8{@ Zh4b?& Z-,hfVM~:7  4#h OI"RKN98El6JF"(!SFχZ~T*$Ia~SEMrCY|>Lzxxw+z=J!8T*k|>_XX, xh4H$o޼*nL&BSh9?m6[oupp J5M5_ r/_?LR.v\zUр1jjx<$Il6/^r$xd2ǙLJr\"D_WݭCCC0/| w-FT*LL&, g 4Hμ^^>ڡ7nQ2)JBg5H*$d2p8vOOO䞜\[[kP6/~_D7ylO~"z@ ER)&iZ"d \.֖@ :88D"nwww7TAQ.^oEE:q* 2hܿbT˗////rDV,X,pRR\קe4hx^4[J%+JpG].a.[XX]#FYDb.l6e2\.L&nT"Pc]@D<w\6B IDR*&fd2)Jry׫Vbܹsfsssr'Fj"؆\.Q(?°MzLE"69>>ƼVaf"W_}W_oeY=J=Vz-c63LTjJ%Jz=l6p,--m=zFCVkZ0jT*`0 o/~ sbRX,DT%΢vZTJ$oV*r"Tj0Bx<^*\fMf){{{0q}>p\''' vR*fϟO$!X,۷oZ- JL"|/B]W*SSSo}8͛7S`@$ 2woT &&&`AKX,nf9 §c`/~^Ђr9-'.,,I 4B%oo@0ّH$?aV]]] c X, Bw1;;d? 4g`0xmBKtj]z}˵BGv~S`"`pss5AEBl$哩d2rq؜P(TLؘ2 SdHT*L\ Dȥ"d2x$7#BdJR*nVe'''xX,fْΤ{f#"˝?d2Ex<7˥h>clQ2F$F#RW*gZ_~e{?0NvpooOT Z8ơP'v0<O(裏eJӟ:J2L"y_*zr|vvf/\;11]tTׯ_ܜjT׾!F^(H$/ͦt:/2A{ST&G'%H/`T*x`ST JxCq\"> FE)\QσzƑ8AhĠ] l6'J\n<`rrRT>_Bzf3 Z611VBNxaјa0X o[ӓL&/g2vGFF,KTR$I&`0xrrh4 ~doFXz}aaK_R p\HŋO<9==mZdrGFF4 Vkhhhfff{{[ӾPV_r%NZ,Ő666vxxH<<>>hDԚB0L # .//T}{ccc,鴺`(nnwݎD"A  !dw}X,aHtyLvttl6766`kpEbQ$>P('O.^\.'/_t~_ T*}677$LZ6ɈD"Fd("IfJ&l6{||厏[֣j|bv:VuK.`03s3ҠAƳР~P'dbh4n008\.t:GGG E.#I`Px֓:K B:<=z*~tt^|>h|k_߇r Lv]4#JRpFk~0Xj =f2J"@t:Gd0?<<{׫G}$IF C&AH\.g2z뭻w2̅d2[owyG_vmllg?Y,á"ڭR411͛7=zdXFFF(|>tAsZ*T*\ntthd2R u9.--)J2::8>>p8GGG{{{n}777O$HDx<6Ã|>tme2J0j6bXc:w󝙙\.D"! -f= HFGG+ʫNH$vZ"O/$x@  J$I<99ϟ??7?GiРA74h<#{ Be-Fls\8p8gvvd!xai=rh{<|%ۍ\.C-al6D vvvдj׋QhZ2)\_ i2%AbX(iχr|hhhEp8RF2l~~ڊb.+l 7ep[oajCH&Z1 SSSՓ{{{w܁Ujnoo߼ys||<X|>e]XXF\ZGGGXlllH$/tG.]Dw ^YÇ/J d2n݂8 %NKKKPn T* H(O~JV67,--qdSt:W \.>8BN' noo3|>y<^>]. Td2Z-dR?~Lv;D2._fggg399z P^z˭V ]$7nE'x@(nR{)JDry_ }]XLb1X\jM&` F{ >XbׯONN 4#gh4 nPV@uDRhǵX,bUL&svn(q6P9LfL&e?drݳC^OәLpM&'''(z=,Kgh4t:y(_a߿l6{tt7h4nJJ$PhaaT*AZ,7$ITBjo\ZZV^P bx0333$IbKCDZvb=^YYY]]-J2 JvDs@rf.EuD"XP(4VjV+ BӉ#@ѱh4FFF677-jŷi_TtR)$wvv#[qB>11qAsРF{=fQ}V*t@J C`-0(ns@OJB0 <@Gn6vtBT*d$zc{pEBEXl0RpQϡ+JL&S*q555ft*J<4H#[χ\ح-'2b8LdzO<_ggg5JR}N`v511}<@!p @R|^|jD F׫j^ $ +W0p8 眅t:5::r>WnooAOOO+ѕ+WbZ@KD[!_xfѣZx4M8^]]}RÇh4SSSFc IDATV2 ZC4L$h띜bh(<~V=~8Lc0oZhVE" .t:'˳J2###0z:9=)|>? 6 ;wb$I^1h4J[.^xMnb"PT<bb;Z=??v^__/jZLLL|"H.W*Db̏ʾP(RL&NphĿZբ(ƒaz/]4<< g Q az O6s%*!FjAl6aOD't:a)hnT`5t:D"C dbЄ{i6^wlll}} \!jRhU$ 1RJRp8L& O9e J/^dXx"rmnn6x<.i4dz,gff*4łhMWf~j_VWWY<|zb^P(JV dx<.46@f/..Fjx~qbrh{M j\ݾl6ܜD"wv"j<߆f+ Nc4ݻwҥyvD(%I*o$-`0঵F AaB ;/jRzzZCCCbΝ;Wqܹw:^ϥBvywn7`/} x͆RRRd;;;'''fyyy;w,-- DgۉFprJE.dxb8p\xfL&vgffFFFom0/}D(gV* PII4`QN5: t: Nru:L&d`cuTfd2D"r? A"x7nD"9xjnZJu<"Wlv>v xbX.;^f# o ƑW*b8 X,av8|VPf_#ꫯ^zN2PGl`KKK׮];==t:&ɓ'/GE;sn6^b Db '$jŲP(P,~2,hZ r"H$b7l0D`Td2^7TǏ ł,^T*Zd2gggVvv:hj`0X*RL&rn݂d=~l6"7 *J\f(ΪёVrǏ9d2iZ#(҈bhZV'O͛7l6GGG!@ prfbrV+NOOo߾ hP0bh4w:}{GGG" |PrHvl*_q{r1Rd4Ʋ쫯Z*B<88h_޽ԶnnU(VkZqcdyeӠAƳРL&oݺ# ?Bad23V:|1|:`4=== BH4@ dbBpb0',)0teyy9=zsPXrB!z{ܜf |r\LFaR$>JK f\.ȇ*Hr*wB0>>h4Bp8@YY^z>D"X_tVEO|:(ME"T* @ $PG& 3L&I&um^*t###Ν SSSRh4Bl69NX,d\.t8ooDQo4n7`V!ŕeX"B2qR B|f߸qf?d0/r,h4&irj2$xR]R9??vkZV2 NZW>>>`04 XEӁ(_|QRw}ҥK[[[lgggCCCpx08NPjz@4zIq~b۷ݮl.%NX,&HBa8FRl` $Y,nxP(8d 5fpKbW^y[V_z%b@ 4g~jZ*`iZ!:Dqh4@ NiMv&$(A?WB j5V lR w]F3=====m6<끭u)/dc]paee%ETL&C𜡔p,nH1wݨ'&& !KVs\kOOO߿rrR9bZvbb d! MK|>jK.qvvp8 ~1`0VRv&Iq{@pF?w>3$n]Ը|$.+t:ɔz=>|>tLLL8ǏT< ,˛od" b٭V x2w,X,¥'H / :99/,,P(ǫ*^{5dYVOd0D"2 |6F`0xW677D*:88sݑHDPs3B0 MLLqd2YT AShР@74h<#d2F8*R FVS(|Zj&&&JzrrHT* n[Vr;22222z!p\ h:F"Aw_A{xxt@ûr #LTFE.G]|?A*zW&ЅZ-JUVr:jaxx$ID:VV AsL\4hm+_JT(b12L())o\3`R=-m%JEDZZSj 塀+JVrE! X"[5ׯ`p\$P*T*PYt:*\.g6!mVv{dd傰^nxvvu!#JJN$I9>>>Ft6bi4t:rwvv2\.Bx1bǑ+ xH$D0txwޅ7͙>ze)AXtt… `r]pZt:=>>ɓv <O_Wr{{rEёHӹ,񈄢D"҈כ߾}?6j;V;;;X\TJ( $q\>$vz3zhNCpT*URVxd2al6K)'? L&0͛, D?o߾v(m^4n뷶 o@F̾h B#D9qppP.`988`XrrE\t:}ݥǏD"p8XZZUI.kZM>W.<>>>|y5Ag4gh4o#4 z# Xp hbXZ-Hd0v{ZfBP(@?L'1i B(z`#/ˡnĘ_,#6U,JR< xrP(`t:'''YlVݖd*DBrjE ۫Q.f1FEBJltNsN7 8 " 􏪽[Cr5p { Cu\/N;99b\N&]v-J\|y{{j|>hhHRaXw:i~GB!l]\7v;w 1 >c@VzppR?Wc}&3hs==4(J(ʔ,{P[u֖^Ye*PC3p2'wNFhsw?)o*aٍ9hrXT(Alll~X,Q;zd2ymVk0?~#B" fff;wdZ1#w^ݻç| \.0DrVݻFGG-K\߿{P(<}4$d 4]\.,A߿y&Bb9y(FfsZ*\.,K8 Jrooo}}}nn`0ܺu RL&p;d2B!r4ǫr\, qH$m``رcnn R)&i4WVV /&BH&3L{ppo~FZjb1޹\ZqM#l6lBDH A6$H_jb1ÑH$G!L&dжbLp\.V(| \.t:=NRdA'O齽*J(Œ(bX,KpTj:Gh@)N3b1 Lbrޣo\ dR:6rp5:: $ ەxR}k']) Ƚ|Bd2%7o2̡R_}\.'rL3p8="hkkKPT*R(`fL$v=H(ʥh4 uرl6K?#Xӓfpј~(B B>dXNvl6Nt.X!A>66 XOe t:](JsssJr8|c٬\.f^bK*nnnfY_Ο?DQ OH䗿eՂ&T* rPJF.D:Dł1<b22fAG3!h4w(JVc2 i4=h6j:ϟ?~zz\zi4Z'lVU' BB7 #H?Avnrd2߯ J<`$IRIRJ%DrLqߏym~r2 ZP(9B$vmWWWTOhZׯ_7L`UVv{ZP(N3K$\4x㍕O?4J=zZ27|lؒFQ'8N9,{ee>NwP(T*R d/mmmjr׿iV{ڵe[ FjHP(D"t:=88HÍFj^z֭[fsjj* dFrTj$I&0ޕlb1.^866V'`vvV h4 ,l @ 0LPONNV*t9'''Ϟ=K A)lHxBHW^^wRb`tvTɠLѺ* 杘cFՂ'JE-,; d b ʰ|JpH$XAPוJ%D" @H$LpbZ,_~u R(rJDp)(nq6Pnrd&666|>BdoRL$VVԩSr/]T/~ ʩTjjj_988( 6mrrw6 /BPTy`X ̙3g0F677[VLRR魷ޚd٥RBZ {zzz=i ܥR Y`ԍ]4;;>X"Va2PM@+bXՖ@pQn0a  CO:wөxp8,ͦi 8Ϸltq]P677].@( ^D"P s700pm8b}l bL&CӋb4mZ}}}.^Bq.s8|>X,FWTP5  ~2LB,,,b]:j=R Aj r@ fggK B nJ  8z1<~߰Z $<'L&77WN;Jp8 2YCh4`/Z~2lrрI(d2FCT"g@x NūT Rypp $3v뻻~ddԩS\.1J套^:rjOL&b@HkZwV5LJ$D?44,jFK/d2ouzzP(^x>sݠT*/mxJL&dz{{T*jnшI>d&h4X /Ž{Ā̙3l6;55566v+`1VhRu4-JܹGACn4|B=sҟ%$HlHxB( nwVe2T"(NsX, dfyaL&v],+x<FFTzBZf P6HXIE: 0 EX, X @l6E"̙3wܹ|(w $9,GVk\hppjr 98$²X,Q([osΝy^a@Q*r9@p̙jjXΞ=[,?~<11#,:h4#GollT*P!V(vٳh{ 9>fD"׻h4N> vφB!" :tdbD}>L&hq %I49T*U{vJ%NJ>ϟ?88@=44455deNgxgn7®@ ŗ_~2Zцe D"j|͉5Áp8 f Y,{Wվn5>>F'O*ʵSNl6X[oabzzz:88H$r|JRz饗>T*Eіd2|TĉgΜw gX,6M@ ?"dD5V;66V.;::rNP&''O8A A) A v_'ɀjV+86 ^wqq1bN)BAb,r hZH{Z"\zjHd2b^#VпRVr@ T*Zj )PCk4xn@d\0UJRz[[[H ( FcwwfXnkZ P(c͛7!DgR! Q( .4M(et:^D"f brrp`AӭV霙ف]=ywGFF+(J\.bV9bSA+P !WNݥZ...BF4ǑHDP4P($ !<}嗑PNF#+(^L&.ˈ0C^‚@ p8ӷovJ}+_ ^P(p*d"|wy Ǿ\.(/Jd2[STlH mϾo8q;wx|cccnnN(M~xfͩ\.lׯ_HS>88d2hwww,|2JW%\.H56r9dzlX菶VVVNߍ7z{{ӟB\RnD b7{fb^?==J GT*BA644T.9|>*>А@ waX/j:gΜH$w KKK\ڵk&RoY,R%\.7l4O>jccc vGQ QxF ###X,NkZ::;Z077pYh莂`(~IVt:}=V{ћ7oꞞlz<(H@R&)d2xCvDlX e2BA^ڃbraáL-4DGPtz8L{h4iRʕ+~jj* !hbxx8J@E1{Ɗ"\.W.H$b6T A)lHxB888ַlvZFHUը/rW^yu\._&jJr$ "nc~_(:M o4BA`&]դR)xvk~DnV*w fBa  % gnn.a %RB?# I7{{{㥗^H$׮]jْdt:=͚fv!\yǟL&M&`Hsss}Q4jd}}}@`wwwooB' aɔnݺ+| CR(j1 FS*BT*- q&`x<:Ɨdļ JN<1 ֖j;56y<l&ZRȐ.$Hp8> r:|!pXVDh4 &U/_Ӂ6u#b2 E׋D"Tj*jJfG"AyDIfA'.P`DH$"ZtlpNrJl6$EpZV"`:}B%|  AhbR)R+˧qՅ2^7l4x H$zzz_|>H @6$H? BW*F%+1Q_#&9t:T*,}QFp\nPx<zAJH$r9vww*snܸf-g}fX6{56::N/& Dl6nlntuu-,,L$Atuub5AR?rr9Of h4"p@( .=APM pZb`_T*|xz.HB_Zl'\@ tF9 L`:A%;" =n@H X  rVk$i' L&t|>P* BfxxX$ݼy/J}A6UgϞT*_"qppK.QTkZwwwѰ*;y& ̌jL&3 ub d+ ׋n*'Ol47oބR6 ښd(>`*mzVFvB4\>??/ s\Fw]@GI$'OMH$?1Fy* 8솰f )P=caɿyWy~/s:_NGo>B{gP(|u Ѣ=ܵB[αF\.q!fR Ғ\.۹<~?v4- =34mccJd2wwwjEА= MLL-{I A⩀ fi4nvT,R)e؛Őa3xL&(;$ Da \/A@N8!Am~[H? R(Jn xVX,FQ   ʕ+`p8#Gl6?88L%Q R)FS(^T*-K^L&===*b=zʕ+d\.GQ lk nܹs'NFwww`` Lr8x<1t:]RR>r\nZV:) NLLbӃc{Wu:~w Fph4x`ƍ7D6d2zٳկ&1RZ|&U2Թ N3)jLwjT|Ֆ\uq-n J ܮ?^.߿hyᛤT*'''i/ ,ht:H$266 m1[[[R`0ݿ_TJ$B$ $H< Olk_Z0x8 RӁQn.bS||`bŢX,j{{{,  N$e"; Opbt: CI׀`R)LOQWUd@ A?~x,k D`pJ\nTBmooƆT*(Ç. q>YٔH$WVVjZ>/V`04ep86kkkP瞓J|P(H&%\.@"r:Vvuu;W^5L?4@P*NݏsعΟ?z777<'B7QkeҗaƎ]q; >5Ll6;N/--=z( j5Xhz=g h5[f;.$P(( 0wΎ$ÁNFy}vv>}}}Ax<ńBVU(M&AT ɣL&Cd`V0d(+B-KR]]]`0xQ \fs:V6 T޵Z IJJ'NX,R)Pj+ (zJӧBfST2L~Z Sz}ȼNKR<*P(t60 aHߧ| >7BUcn3.; =jH`[rvp0h::t\:0 PT#bP(d2?~p8`Z  4 Xr2L8zT*rY,ֽ{TjPH$h4z(3L&{|>_Xx" AS A !͞={6j 2 5'H$z^a `0tG880( <Q(t:}mmVMLLd2FM$"7fad2cX,QiJhiw9N*T,`x:.HlTn\.JzzzɤJxv@ ZBF!Z X,f|>h4> IRX( JVwvvL&ʲr Lf ^N#)G"b;'>qxfƚ/RBښV> J/OWhPVaTėf /ojޡ v ២t~i:oU5NGکa츎(:8i堋: ũT*,!'k`[:- . pAq\.wAxUp8&&&׿|>SVE"Q$AJ8l6Bŏ=V4-t-X\@OOO\>88ZΝT*/|>F\R=I$H)&A jlBBL&B!B'@aOD6d>o6ZV166 wvv 'd2A-^WTV*DJvX<$ARRD"pDTJ& "El6٬@ h4t;vljj 9+++Ritt4˥鞞H$hX,>BV(RT( t:7M`0$ ŲZ`{ NP&(d2966Ft:%e2JN2o)D,+#6ɠ0b{{{{?v؅ B'|BD"@g|twXXLc|8\w_Ɨ0㨃/:ʝ?#NQCRM}'YHFhbZyNP Dm6P(42J3:ެAT*H[r >jmoo|x2?`f& $XeRJ%Flt:] $ ^Vju2d F*xux:0k#5%$H nAa?F{zzMD Z ;771AwwR,J% H$ұ.zPyj>| ljxBL&b\*@PSTxr|ll`0,,,X`0؁}ZpP(8 Il6 -9yppJfVMOOK$MGP(Tjuu5`ZѣǏv0;9tLZf2JIt:Knဪr].߯jѰAB ^jG 0Ss\k4r ;ӌi4\.WLܱ*tX,axFC$Z"4*@P(x<$ )Rd2%T*- io6rYp8X VϜvm6 靼!HUVzjd2Bh4x<^6MR a^?88?99ż^܍7zI333/^\YYb]]]hxb\NRAףr]]]zP* T*<>{ttT$U*gς+, Awd@B\я~fAJFQj  7b40h4JRPTB?Hfn䋡hT*hl6Rd22nsxL&ha(~$Iٌ.xcAӑE x<<O"d'O>~^ X־aS,8?XP(>}<ojj2Z^v SaPB >OR EVy&p8"l6frF ZM&+eVo|qww͛7Bd `r9X<33S*Џa槥R z]P3|>d PƙLFÇ`@ c"L&˥Ri(BWPB !NEhb1^U>z!h,K*b0/bل+'JrcǎX,ߏ6688RVVVp34L&+iڋ/Tz~nvtr9QdzJbEt:mZtR|#Mrb,..2]*fxP(bZV,KV\.'vww) ??r-_גd(Bp_|E)gy<Z 0=d2rA.STKӉD_H 0$H<5 OJ'? 榍FB "(L;H1aENtZ6mnnRԅJRV`e9XF\.b"~"(pć^:d2Th ٔ |_e|>?La  @@ 0<<߿bΝ;rRT>jmj^7n D#:^*=zŎ=j6agƩñV(f 6@*6G%D".]y睇~AAJ_o_WWW"l]]]<*4?ťR]]]`0h|>/H@qz]$i4bN;d|>b1U[#쏐rZxT*uݥRiaaԩSFR ^{- f) 9h40ikk\.Fr2^8b2Тf$") t:͊b} E(sN gNaX`@.nll<3jP(l6L&ot  *rP|hj===4M"lnnBb4GGGlڳ> /#GB_|ÇXf;cǎ}ᇫl6[rt}hb2!;"L>zBE \bRr2 |>V!; %AS P>Cz[V@h4 C2x\.P 57=z+ ÑpZL&_ǐb`0 D%E&'p}鐿a K+1r4jƹ9FCǻy^W\nVNn|4 F^fX*Ov^Gf].J\.Z:eJ .]4???6:v-d#d2cǎ_regglr9 hyf2t+++loW*tF>i4333>`xxDj.,,quQp8@%ǢorrTjuaa!ݽ{W ={ȑ#X,h4kt:=99911xކVp533d]xV+FC$` èX,r-KZb D"Dh-..fh4Ο?V L\. > ^HJD"ё#G<g}h49NVK|>h/^,rx}zssUr>O$AH$P(r'&&][[bA4`0dN>f}LŖ^o*:}z||awt:A@R(.ktttkk+J J$ш>gA>44499T*U\.MZ={ȇ~hXX,֍7PKKK׮]s:a(9Xt%U*Z2 XGh#Af޽{N#aqqqoookkZLsݹs'STXy<^Vrя~tt:߯P(VVV'p8<00tg,pX_eBBT(r8Bl6 zO4`~~.XB!Z:ΤGGGX,v:===}3̓?я\`@Z\իWժ`P(j5Nb@NCh׾5hl ADFպz*V*Pd2ԔFlb.UBattj4$$HlHxB(˯+ X,Xaܮ(fVZ2 \.7 d2T0hs A a / } }g$ t @âX,J$fE  C<&ttrl6`2Ӟ.\rwaaܼy *:99b6FS(eX3g\z---!EV?AH$HՊ4(T>՞;wܻw… A{㍏a/b2]WWWld^⅋2-KTJRJT*}ߦP(| Y H$2Ll6R=zT,v(o6rԩSBaffF=xzuu& j:55n/]tԩ|>mxǏ3+Wd2TT*;|U@ T*:BܺuKRh4\;}RiwwulV{{{A*|2łQ.fXٳ>>Uի=2X,. e2`pd$p\Nr BG7\.ݻ'˹\jVbfKd2)J,>P(jfs}}N @ flvTbXR̙34Ç?gX& Aw$' S(\R([N õZ p8r9h&)L p4 `0F"@ /`DD<ORx<bd2ND>p8^?}@ DgΜQ*oh4ǏΎbTl6B %ry___6jUєJ%PT\n:FLٳg'OnllDё\FV|fgR[[[lرcZ6HX Zz766@ݞT*}\ppRH$/MT\|{{ . +hj:00R>|D$ +JZ6 xĉ޿e=O0b^Z(FFFazWk"E.{7jΎL&cG"^hZ'mssȑ#Z6Nnll@&Vl[LT~nn.Nv9jv duuu?HxQķ\.;}4u\|~kk;w!Ν;rt:3xF(RfB!ST:33gC>p"׾pvvvh4^.r6  T{>Cᠵt*Ve|E"U(ExǯF A A T*} =n@7@  9H:>va0Vrs KGzQA TJ(kpXP J$8j5pAvXݶX,@?VgΜ/ Zhd27n3>lr9k`(H$x<R|~Zcǎ]vMח坝1|~R(4"x7xB<~BH$s'J566r\.B.qҎh4JD2B u\bG?s%$H PT}v.C^/jHBPVG"l5FC 05?y^3 lZ kf0Zvx>r^ >OEWy"Jp8 Br^~et .HΞ=i4Z$1Ll{ee r .bP(gΜX,NFYV¯F,kz.!SNRvǨ555>,߻w p8٬B`2Vwqq1cVd2yj=fbxxxxmmmgg f%Ɔ@ p8ׯ_{Q:6͑Hd "plXD@ R @  SG&^xn&DzzzlBP9r|Eӹ\G$d2Y,wwwa X,~\(N|ۧOrG rl4Bݻw?n6#a^H$FFFj})Fz'''z{ssT*|oH$߿nR*JJy5͞~ߟd`9b?^,H.JLtNMMeYX'!QDy:oD"˥V%bE_777GRɇa& 5D"㋋pLޠh XlH @6$H>Nnx4d2V su߇v=33D jZ&-//G"v+8b1\`0Fݻwn7|~{{; JJ}٩)U:'Z^E"\ H4R.y *BZYY_wBx饗͜N'&&d2lfx}&fooos\>zAV{!yH ;  A Bh.@hZf]]]rZ4MRa@n7"x¢3ljbH5EA(( n  3ӧ3L(rtܹsǏ B8T.P(x\R n)JFRgyIR6&+ !x"JOVV|^=B^.޽x0gee%AKRDPx1J=}tXD,tDvX,uzt:NmmmX, =L&+Jfff{р/S:d2r|aa\.JX,&fgf(̦BP^$ -,,LOO{<|@`0޽ jPVB0LMMB!'?s\.i.}BX\[nCV"P(Rpxmmɓ,͛GQ&rd@ A V?ft.Ը= |tLRi4Z\xw:૎R~abXՄBF BJE"XFq޽d2iZ2ElXD1ʠD"b`wwwMfޞj9vXRhb1STP( ]]]XJm$eRp8"Bm4GV]H?~N#W.;Bj`B522"}S(.\VxѐJN޽{)˃L&bNH.{. |>nP=3=r:r9N166&ɜN'gsssxxxff&JMOO# !kf3͢Z['SN [D"l~j ]:l6X,>z(\;'&&666d2bG pŋt:LB7LRs\>h?H$TjkkKjZ6=44xH$xFaXBmZLl64 XT*KR;6bm|>Nd2 C b1Ӊ5P(jt:]V |fy<Ľ{$I>r"Vmll\pF|4 2 J7|L9$Hx* $˗AT*EpB!Ҡl6ͮjl;;;T*5 ߷#:T~ZTQZ(0R20kp8zl6Aߏ*Jk j5Z g#G Ǐ#6|~>G~V&JFJ~AӥR)Cu:VT*zfX>XN"Gt:l6Cb6TTP(<6j5|$i eZF$$샃 hB\.`0n% \Y5M$a0v]Pj*H ;$H A6$H77bn߾r;tqpt:N&f2Dhi4V5::JDXPX,x;<eZ''' `ٙL&h4d2 2ry4jb j) ZVwwL& BHۅ&TPV%  EXbP(l"VX,?VaGJfx<@'RbX,Ah*f2tݐDQ8JrRT(`%rWXd2Ba6hL&zr'˫j^CԔA\Jp9L>NA^iZA$Dh4. MJ\.n{zz`d =f[ZZ"r xph4dx_ ll6Jj5p;x8@LNA7@.iq Jh4t:- ry\^__f/D"^(R6gϞt0cǎ|X<44~VK$ts=W(Z@\f+Da9Yl6b!\iZx2d2eNTBY!H@l6A7Vpg,QTTEus1ECe0ӏ{UC9Ƴuh>~y|Gw$1u ZqWt׹:=RqvnEs[)y ^jAڡhYVb?ƒ|~Hׯ_yfX<;#766Y]]/X,{zzE;DTR~_׏.//t@F@$Hx* 7$H<9pVx<Z !5D"A\+~_RbXmjN^ I(P3Ϥ/;{@r*X,v DX(vcXB^svd2ދI e6Y k|JA JpFs̙\.wm\fI6h-r˹s:</ Az9 e 1Fr\fXr\QBR' ]QԿNW_H~wr7?-l3:nZH$R`T*AIB_@oORԟٟrzkZv; PZKP(j\͖H$ Va|J6$HxZ $w~;Z&IR C>x|>bP Xj5ʝrS2v?\.Wb "D'zr9<\6t@E|xEN#ANe2YVp8'Oۿ[/^v-|vbbJ"NkZq||rq8bfyOs\R1LDw1<R*RT$#0;zkJc0B.ZT*0xiiihh?DM_V| D<&qF#9r_T*5l*$H422B ### X,r=A~tRtƍe\._!77gfyppfx>x.łbU(&X,vsO;_>,Y;4|e|1# BYsW,9b p8p\& Z @;F@@Ra~](Ҧt͛7C#Gj V=rȥKj /pZ6:::77a AS A ^ϯ^ ffYЃI$ g\.|` ?rCFP(4L^Vj_t-|l@YT:v8([}>X,sN6Bphh&'NY[[{wz=0dco}nB3ܾ}[{P(p8Da\!Åh2|1t/-֭[?L&haT_ ޴Z-4ZvjyalL^ zN Tu. Gs:~Lq<{t$h#t:Bd:CN]aS a9 "H* $7]]H$J$`^6C,@o6!JsD8x&v}yys )%V ԡB z*Z^^f2bbt4i{lH T@6$H<9|OAIөTVYVDRVřL d2)J!|kX$r\,"^7q@_T#BP(`Lr3NS###vѣG\.7hǏWUÁ'/]]]_reppX2uA~sK$y?H$rϜ>S,ӧm6&> žH$RՐ;??fwwwc(B`XA,..+RB B!zhEA2/ F, 2H1830Rg |B O}h9::o9\.u6 k.T<SC24P#Jds8ǘL&"L&Hn7l2yـ^e=܄2|i+J\F.5v;X,OܜzA"hZ|>]H$F\"*hĄ@ P(D"r:F1::v0AD'?IOOI"A)H fcjAd2t:VÙCޱLH$"h?Gb~l&t:]$0El6gffy>7n w988x-"^-A@*#S"t^@6Bd2! \.ZxuH{3 dR aTfq @BXCCCDl63 `*H2D|>?N# <ϯT**JP 'Z}ܹ_}mm^SPPTepLNNy<6LRtooB 3u>/σ2 Aw'/ YFQTdh4&IT6#T*|>? a "WY\! nPqf2\.rhhdB#j4MZ B(,K"?N$XDqdDj5!$Z~̌F@ᘘrv=vz] M&S4MgϞjwg0P>Kofٗ/_+2r0+]ֹ\.:| GUXD_w]}2tSA"x `NRŒf4fX,& ߋyU*F!jud2^5=B7ł%KR l6Fj %l)JCrYPsEH$"F2lB y0"+TfF0&m6|N7<<'P( L2lbbj޼yF}Nx`3~ X,tRa̿| ڏɴZ,|WWsJ3ϬJRD"lSSScccn>X,(,X(2bhr\x饗FGGhtaade0@G3A #FT*d% H/ l6X,BrͦJ ø"L&3 ~OfKR(j6:._ȣE"FQ(h4zRFf{{{9fenn`0|kl6l6y晵7ojׯbE(c~hr* 壏>p8R Q.+8(}}}. qKpcd2J- Tj``Kp:`911L&m6["Vt 'zzzVk&I$BP,J%d E8h###?ARD"Q(F# TV BP鶶T*p:zv;Lz<M~Z](h4ZgL&{{{VnlT6L"h~2666r4ͦP(;;;C? MLLL `h`0`0h0vvvt:ۣ}Q t\N&TK$}ʕa*>pR$LǏfVt0F|Rd2b1SpssDd@–+ˍruPded2_1r<`S\.P(@Z;vlss3STV{ܹh4 P (l6;:2GVI0 $H ObnTfF " y6pRJrf`0us)$3߲ } QmFBJܹj|bl6D  BfE[oz͇A&Y]]Z|b⁁*q.  1 hoǏ[][[b \7|Z{=ztttrc{*j4LbF#\0bBk' 8IE(q>=FPH&wy+++333b1 < wT|vD"dJ@#Vu8='?ѣGe2HPL&Ӎ7b6zFt:_\ܹs,dR"'?_ݻwfZ-z6 0 J\ tt>_$I$H$ *]x<Dr޽Rh4bX.h4AHDT*vlmmeYNw]VP%`&''n7ho kX,V,+W^ZțŰXaPn` p$HxZ 7$H<9{=R  HM&S'T+Z6-uuuN}r(2Aܗ+lJ}W  .a5y<6RiY[[{[*V lFKKKxƍ B.' 8~t7np8fDoݺP(N8@ m6$jd2pTj$M$Fj 5\$[(JMCCCAjT*N9fn^ŠH$JP/?6 "Hj5 VU˕L&e_:.|>rep8b1>oss3 q\\H$$bT*N2YJv;B|>yt:98|>z2BѣG xL&sxxj޺uh,--moo`Od2zʕ+f WT*&dL&SׇѨN{br:.x^FJ@::rH:5LAAD|޸>y|0JrYVg2ݑVr!gggzzzmmT*)JVɤn'ZvJU(bA/͖R dVU*`>888{˗h4ZT.E"x#pBPV[[[G}455T*\Ų500033?~hp8lbKr`H$666ճg:ٵ5{v=?? :>l}}?X,IJ|>[ `0#X,R):VP(DlޞjݳZ</ mnn4v[TGNV` e\UtZ*Bd21%Ve0t:fgٽ=\^,fffZVPjl6;HushiPT,p8x<vG}R`+'''sA<Ad2T*͚L&өR d2e2L&bZʤ=f6!%W*.kccC&e@ T*bTz&…Y__ ~t:btٔd ԑ? $$`$(Y-zXy=[V4j P.ٽ^cTwPL_ gjj^PT2 ϭVK"|.R\.fJeoo/vNVr5 LBh||>d25 Cs8Zf2X,V\V(n L~,LRZT*Ad(9v=88Fejjb;Wht:}QTjXdSSSP^sMPxppL&[H$ >bBM`X^W CJfX,v\|?d2dSTz}*j4hF9N̑H[V>oۘwL&-.T`S8^[[M"~D2??رT*F᣺醆z{{ O d@ē{キ, ?_w6h0ȇ #\;xaAxG^#8h4Zccc`In/ ?|<Z>B.nx>OPlb1mnnF"n@οr@ r^7mP(nfj.d2BT*u݋G:\^^.JGD d2qB T*U*www|ŲHR4444p8k^߾}AP._|Ezzz`gH$Ѩ+"_R"Hof =t__\__?}^jf|L&swwR(u,^[oT*rZ:GVh4aVoa@TV/\C،:p8NGFFn߾RO?c|jzyL&KRwj(h```@T(~?Ar5,X,bbJ*D"p8,K$ӍFcnnnG")۽|^*IxXC Od@B\^^(3 D C6MF.CXVr"V zXLӡa2|>_ p\5Zˁ@E4;$L&Pl6 þ~^Z\\WhzVR...t:DݭVɓ'O8jr~?9/*J Z \.ǃ:L=zX,F!1R*꥗^j4=l6.-GGFF8N0s:/ҘtOkccc"ˍD"bAz,sCP8J(Fp=z+‘0jcccb8kZ(F~Fx|>Je2Ryd5Fv~?7t:]ߥX-E/P(j Xz}"HR###tʕ+d曉Dw*>dll,ꞞDܹ#ϟ??00F+Fq]J\.w:|rIR^_(, F{KX,, i\n)`PM<f?~. 4X$Ƣsd2p\x< eln݂olV2`ccD"a2z},óEFĝ;w?riM"A.'*駟>~G uT ;JL&a"Nz}0mJb svRxbxjAA<ijΒz?q8gmmM ( >OP=z b=J&x|yyѣG=== BT9rDmnn^v- ^(z{{S{w秦z^e A)˘?Rt`0h4[(`Y,}>N;~kH$G8Oz]P4K.y^OPJ& 6Voݺ5?/WWW\.˭jpDnqRgbb^7bxD"E"FBi_ܹs6Zb ?~ /*j@ J2޻wott4p8zx^7LD7l6(mnr&yWy:88}4*Xd VH$ǎsn{GX,aLL&-L&ĉt:}eeF.ksskkk:FVl6o4h X^^|_נo&A Aayy޽{smZ CDX&0P}ra ~?vbL+ 7'P4h PCM[E"Llv4)7 XB;44422vT׿WG^P(&''^o %pbbfB2j?~F% 9b=ZiXy1988PT`cnpL&AL&ӑ#Gzzzt:,K"ǧx8.JD)PXq%FT*HDPeYxw Xv6zzf`ZH$333  h4*#ȍ7i40ɼpL&tRwcpV;88yh(ZpJl6;sP(?88²رclҥKx\.ڵkݽ׮]~qb`&mH\.wooh4mr8@ &?22;Hҿ(CCC</H3#G8NT*\WOOZHF^7 ?~\TR(cxݻDVq*J&WT:.C<ht>6DѨjU(d4Z֩SR) A pΝ;w)TTaUHb"Ab Ś/`k ,c.C.j* ܞBP*L{wFu(;J:vŪ;w>c/i$@<O&JrL'| cǎt:E&] 2>>>33  Ʉ%7777J%F ܹsz}ccrQ*n[ono޼I fsbXTBH9N\YR@ cXR]ZZoZ pl6[*N'f=OߧFQ"ܺuv/..9rd{{[$mooǓ<F9fkN62Tnݺ>ݻD__VEصkxWWX,Fv= a!boogjj [n9rD$%/\.\8P(488 6PX(677www=T38<OssL䐜!EAeʒ%{kkckwW{vU[[eJdڔ(ÐL0 4:s9|?e_d`5oxB(Id`p Jݍ7\АVM$O&p/˻حJNҥK"ooa "hBL&366Nao|kkk/.." d2e22fgg{{{O<)hh4P6D&)ʶZPC 5|U55P*`*?# X lfd2o*p8p3LoM\.Rr=ri4Z<->IiF PY5L$vݑH$Lr\@xCH$666B?J>C544<\.|500Ʀid .$RJh4ڵk, *wwwQpooo߸q#wuuQ.t:фz^\d, Jt۷5X,6SSSvdnnn A eهfYb1JŢgrr<(;::www_^Ra(~%Nh>C2VT^vm6H{{64ǛH$yZWWW$r9N:UT~_s8aj5JvzgffJH$* 훛^A$Y,M&FW*oBXXX8uT\q3g׋b> \.W*V*ΑMX"brfv+^͆ޞ&l9Ζ.3^ Hәf 0h2I$T+P(]@H:<<$R& T*500UMP3L'T*.Ɯ\.2PDBbͦP(dR$IbO  FhNL&Wr%b& R)y:dJ"- +++:>?㶶H$H$o L.^عDQTJlJ544X,(L&r d*===0ů*&hCCdD""p 2б1p/]pvww1u\ݳl2 6\.;Ň‚P( D"aR1vTjVl6|6=22 VkWW"~\n:~(vN'x/^X.o߾}xxu:\.a2l6;H(J&uzzzkkkoo>A \rE,ûS [nB!јH$.^9\.H$$IF"/.--qx_,Bf뛛 cdz |rD"\.ζ={6?z( # $"dխឞ_|b-,,@`Z766l6[8h4@,755Lbtuua b1Bhte{{;H&r!9X(&IFd|>omme2"HPB!χT*mjj&PC rQkjannT* l0ǍFi x6J! f&{T_}\*0D`]cD>`}X,(@t,Z\f?36}žU///{<ɤT*d2kX@gϞr zR''o͛7-K&1LjZ$q8X,x<0 `d2VT*ON7<<ɓrP(>s\T^xHl۷ f0 ---pB>\. /8Nf4E"A6mjjJR=ҒiB_ׂ`26LN3x^Dl6n`1 c LRx@ X[[;~R<88hlll|>wx<捍L&rN\.|bd2ՅBb]hlw<g٨E"s=2L\~ByE",tW_T^x!Hr\6Jr$bDH$KKK,K& \p8oX`'F NRRj}zggBaX͝x0AڥRϏD"r9}uu5 R 0mjjg0R #h4O- 2* J,Wχ vRQwWw[{ɴZb~mm ^7=yT*Q(e bJz._zKK ͶgΜdPhjjjhhW*\.f0<2 N8aAJ|`uaqjEJϗ充 b2(K/bڬV+˅4-ɬ;88@0Lϟ?uԣG ].JZPx<=SӧOE"IL&p8r3 ^ΝcǎnHiիW#H <Ç$f)`(VJ54483gRhr\0p8/X,?Tr\$=xwń.R ,&|@r!b(OfX333xR 5/dݻw?~ @2dBR|>_:H.U5?I~? yu61p4<`"+\ Rd>@] D h04DR466f+ ҎU*1_|H$ A H'O ÉD|RT,#̩Sr9N"¹n7FFR,\pQVNʰ.?.FFF.\L&z`\.û&Hr~Pw^<puPA|f`Ylooz\ÇVk\g{{>hy٬|Jw+ &SIDj].ӧOvr_kñr`ԩS###h:::$bd<|PTH?bo,J=~nRQF ._l6mXrsNoo/T*^{.B04M<p8/jZmmm;;;ZǏFFF"H H7n' RTgg'"PTXtiZ,T@v|`0L&%=z{eee`` NrY(l6{mmM"bY 677RS#`B& [ ARf3rjgg*D0b'N@O;;;{zzZZZ?H5Pÿ\jxv_ZZ*b1lZ:'Q(JFC2`;hLOMytt'96H3`0 B&y^A~ǏS@HpRjַRi,+ CCCϟ l6n\Ғdr浣A uyyhu5t5 H X,P(@Y^^D"/b}}=wD"3f;88X"p8^ҥ\.Gn7btkk+fmllpr*JL&}6N GT;vlgg' ) XtIJNՕX,L&,}_n T*e2W,d~NSpxxx8H$^xP(x]hɤhmmd2B!:ҒfnF3s1N788P(t鹹`08==4N___o}koooii)]pA,C &KKKNB Z(y饗"~#$|-//?~V #Jϟ?o٨TRLweemmmS(4f9NpfffhhŋOǏcXGG \.NR)Hd`ʾO|@ pI&`٦z=T*Be'[TBk0, DǏŧ6}U^D`0666|>FP(x,h48...FW_} '''u:&tsssׯ_NXtP(@޽tz ?_>M~`HRV;77aغB.r9*j4au 6LFY&yvS<y [[[)ݛDƦFպ_(F#BY__gXHdNwtt` f^H$\ZZrݽ|~rr瞻uҒqP(޽{ 6GѤp8V>}ǻ\333S,d2&Joh4p~?x^"˥Ri6jgϞmnnx?88~a 4A">öT*V(rh4޹s`fvvN[/y<^kkk>Bַ@gX8, dX֡h4Z.{{{qpvjdebz4ws1ŒL&Z٬T*}7lL&}ee*eZooobpP#fקR|>t:AWWW6㠛>}ZVBVv{KK l AFdr8#\.hR#Dr8Tb=)Hhe2d2@8wH$S8LHR)APT |@ ZR\.1o+QW,jJ|>U1`M \[nep$ZӧO_x&g>F[[[[ju<T*A'dV\.Wl6{`jjj||FQ#ݞH$jښlnhhX\\{&bQ({nO#ٳgB'OVk4 a?裱'O޻wowwq z;;;4sll (>`hh^@ "NSN=sO$Bc>oL&,S(PcǐM{=??؈\hPT9Nof2JRL&syyBt`0 ST͛7KI$x\վ \. u~~Rd X.\888X[[oX,{nSSS\v:|JR&y]`0xɓ'n~8113d2Y0$" j4T*>&rIһwrqc_p$3- ---H$577l6+Rt:}dd󭭭Hn˕*DRTT"N᭭g655jBgV?~ Kba?FCÁ6J%PH?hSRAxYR4 .6-˱Brj4BP(RfpxfffjjJ.aSկr\[[ۭ[xcX\Y,ٳg'''>T*{=dW*mDחJ%ĸr\RT,lFVDikk[YYFD"l6۝L&ɴjɓ}}}gϞ|N>Jh:ufutt$ rt:ӧֺ:=Tp2L˱X,J!JU*,Ar!BN ݻwsܥKhloorTS* :# ի+++?>{Bh4Xmy&HRaBݺ:2 0LocUk@׆P(k?2 JIPi߿=}pi4ٌip܅*{n7y뭷Z[[?sX|mP`(NQݼy911JVO(NO&L&br9xcRNh4Z[[^Ct^RAeQT .̙3C0-dr.BXqTz{DsΑH z0E lV, 1Zd6V,jjjreyyR!M*}GIL&BqjNGGG{zz BDbmm Iv]  'N۳T*+?0t:j}ɫR*^|E.xr`Ie^woo/%'N|Ąlr|ddN/--H90jjjBs( Bjz|||iiT*Juppl)JDbjjX,BNXT(10 ѨP(ľB&IR*N@8~-ɓ'5VC 5P3BXZZ^Bl|Pp8L&S.a?J B*oP-QߣҋP(U"`"˅ $T*\.W(jRtoo/2D"sϝ=BN}|>KJe6p5@ xU"AſX,t:^`p0z̾AK~>Ckxl{TE*JdNNr9(/166=Ju8>tB @ RT4DBpppxpxDڵk|>Fa---"P(t:0ܹӾK.\R444E`GG".^xbL<ldba4].@84ȕq—v;MMMj?>~xCCÇ~L&e2x#X,{R433Vdh$W& T* >mnnD\.g~#2n?~'OqRh4L~eeeii L}Bsd2f3qطdپ>|kdz f7X,vxxxK.97odZ,vsN @ӧy<^:Z2leeET& &FZ^XX\N&T*x"~}Y,xPVڊbXHP(4777::H\K*jxvp8=B\.fR)BpSNa> DhT/@RŌOeUk*8'EzJTOt:GL)T*} "źtR>C9 I$P(]nC\.={F%IZZZL&S\D,+dxVJ$3gX\.տZ]]EDk6޾2>>xppt:}uuED}R)!(LLLd27o:v%x\.].W\~y<|HŢVܹs/B0l~;w|>BH$l6{eeRD")ReqqQe2pl6#HKќ?^" K"D" A9NojjJӫpMPss3DzQ*D\.cMA1DvH$BZ0ARppAanL&R H$0Slfff^嚟vڅ GG|bb3FӿﭬX`0(+֖T*mnnƒɓ~6d2Q(gQ(cǎKEd2pfΟ?T*U:y<7x# P(H$Ο?T*wwwI LNN"zddDӥR+WA[fƍ`W^ŵ @ 5UP3w}W f~?A dQ,2Bd=CPYX,Br 6⋡~TBA -/L~> PTFT*.f1PB`0D"( {n&я~t%(TJ ^/Lv}}}P(433H$בVVVN:1;;{̙zP(h4vvvkH$|~z=zC*/bl6v-K}}j@>.9X ]Ǎ7l=rxxr鄅J>vH$ׯ_D"fyll,L"b8iB% D"9<<D|\.0Lkkk\.WlJkjx<xޞӧO455]|\*_=Jݿ? A1 bEK/yq̙GBZ %H2#7nxqLLLx .&l6 gϊDչ-<  F‚;}t0`STG}}=FP(uuu4矇72466v%KRCCD"Y[[K$O>괹\irr\. d2yq!a_o&IRĂ X 8%B6H$ L.3gΜa0j@lkww;v 9`0PXGD"?G7ď@y< @}ODXDz @Tp8%FlT`0\.ju8C,  %  YJr-G_pƍB!ɄBd2I&b 8{lRVӭd2 :xkkFYYYW\r>‚F9D"!X,P(LP(p8pL&p.bqssT*g% B`0`zhZ*:55u֭SNh4:;;p E8Fm2x|>?NwttEJѨ5??Ry{h4ǃv|qqqxxXT!aSp8rU*555 W?U*bt:f f-//OOOdE\>00'(Jp}^m1zBpܹ>H$1 Z_Tld1TCCp8*v͑䎎@pjuTb2Dt*JdSeP`0"X,nooQ"h4</~1L@fc2Ph~~b`0 onnnF\.ooozd``#FGIk1j @ 5<;|?яju>|p {_x |>m2P(\.7Cq>: H$z / HeXd2ARd'q0-d}Pi`SXŸ hp/\a <3͈:~x"f/R}}/`(>}PS -@s綷GGG/2Ltۏ=hb:W(V;o>ydooٳL,&BvX}>_XlmmE0ɜƌyqqӧ@@բ.  d2_޹sܹ3;; 9%b``Jחnܸof2_Jedddww~6=99zI$*Byד7rQfY6MPO<Hu8ŋ`x2lff<22b6b1z%Lh===2,|>>T*i4T\.!e|>D"iDJ{]  )?a f `IRh4tA|>t@Zd2+P($H_ZZR(rԩS7o޴X,/@ ( wܡP(v}yyR CPh_|FXP)_ߤj_(j @ 5<;|oB H|>T*~:Frm\d2<pEDEd䁘> 2 @凌u _VTl.#B(braX(|~R  766а:::Z[[o߾p8X,f'BO~x<L&D;;;TJ,wvvH$J`0c ~t:>o٢h:>bϒY[[s~dfZD"FP0?L?)M CssND"nTч~x* ByH$h4bxdd$ z<FFC~?'OZ6HǏSԧO]p>H$B@p 1 P(b20!`n2L&544PTXd2$!" r8* B#LA&A<5 r9s- t: Aqz]ݫ|ĕJ+x<. CFnquU[\q۠AKLX,&H>"|>`08N<x$ t:w»rn^7˝9sh4zxFrB: $z<Ǐ]'td2zill4\.W0L$LR8??QBH$ E sYԔ{zzpBvvvBP[[[ HR|㝝pW٬P(rX|jZ+ r)aF+FRdٹ\.A?&׻nd֖J%ۭP(X,V,wg&P(>vL&@"5NL&?KlT*bRZ>TA@d0pL&T|֨3 W*ܟ\X,f"J$ `0 yEL"냵U2$Id2T,qp>9N ؘe2X,666P(vww;F>öT* ut:T*ML&rsZjgW}5/ D"2 4lt(:M] (:E5b EdB!;T ]48q PHUGOΘB?A/APFF\.g4U*kx>eE].W}}믿>;;kXA¨777^АBhjjO~Z-KK/_R՞9sfxxx_.t:0, ]]]J%%l6T*777߿RSoo<ׯ_A$JEA.;q]k}}}&I.K(NMMQ(H$"0 h4) h4`u*4-h9ťRxC"n2, NTJקiv-J Bз@IӸPR汦RJC`X0I$`Fm=X\)Ÿ/T[>/ i\pV,gYxgH$ЊT*TfnRLRt.e2988e"H"f*]]]~O>J\.Ѐ:@L${zzf4ܹs\nccN}dZ_C 5|mj޽{i4 zU,e20͘ s8 Shd0RI,CB F|> x<2˅L\Rr + ձX,kAX`` y1L6 RPdX/cP(T(juwwZ]]UT*J.!Fs;K/r>d^xqeeeccC"tttm*###D2q{{;_vmcc ǣj].x?Ϣ(ZrOOBA:f{ML!\vFK %cdit& ])"r&(㢣hq3 U%@NQǃºND6J2`0 -T*5ª0b󱲀;ͦR).#qUUzDA`k:nZ-8]18OU~4QXd2l6T*555l6h- N}>FCBB&Aoh;f4LO>@B\.+PrfXT*$#t5ɌTC 5Egr矿krD"! `NET~t: ] 0lnn&B(/RQP1;_"8&";rx<&! r:N&".L1iz- J1Jc{hhWx#}kʩl6sY;wr>Cɔ'&& H$/iZy<^04 `kc0NM\&vvv޻w?oii 6 ZRꫯ:k׮彽C@T.?~j^*2oj1ҥR Y ;N |>^QaA Gۮ@A)P(fA@|Ae21bGqm .|҉/VUl6AP(qxM Z_`s8ܙu-nWL-( _X,B"O|cX,`rJ2'EŲE#h4 Ǐk4%W*۝Nٳg[Fww/`0H&{{{!?Tkj>|>J& [(T*j&I=&lP;*9&d2cb0-~ CrRKSPJbѨ\. ľG-1*ɻX,n W_}:r\uOd:Q4f(:BCCCT4=yʊfs/,@ hii <f$i``@(nlnttt,.. Br|ppv[[[ p\azzZ9>/~m0hG)_X&G{4*՝ծ / B p8;" j\1,TUs+@[-1ASF(QCR`yZ]82ƋQW%G3T*Um$TD"xL&:*3]T? D|,UjGӗ\.T(3^tyumM&l$ r|~~_gg2LFH$0qxxz}Gr3c?Bj[ow"p\PLT*DAlUc.u9!aBОB͉:&GQ!JF(JT* l6AA7JXӫl6rsGL&#˫_~,..R(FZVAۉDdJߏ?aCC@ 裏.\PWWg2>`0|gÅBΧT '$Lc @`\.d---jb=жB!VϠH O&HstdWmX1zGO.Tp!× Z3 *jՋ# =. t͐n2Aɠ%dq\.7HTWG9[[<pۣGs//A_j_}jŏwq> ܴǏ@#ەJjZ%kmm|`F \RL&`惃7|3777>>>55eZhGj5p 5<#\%{ +͂VرiEr@Ɏ|XT*X\.#GP "&iQ7T:!Fb1D b<^$`ppHD1H$(H$'O4LիW߿Tz鍍[nD"155r~0ۃ喖۷oT+Wp8O>$LP(RTd !|>d!Bd2i4GR)!Hvvvp: Ǐ'E}}@ jW} 5/ƍbQ&h4 O{,<ǑKA:oR&'ZO PC:8 CA`0<2T |>FL&>X,bx<$1LTkk+|r^Fn7\.h4:z'''u?"HP( ~Q{н=NCk >}*Jv;F`0lАH$Ba2xUMMMhX,V__؀O|6-}D"<4d&GP:p~AlU}習82ҁ'T*):~BUkC(PKCFK&\.f]p){ϟrQDJvvv7EfihhFb<^k4 QPC _)jjxvw g( èrކ9`,JvD5=*JV|r9c_T"BHӠaJ%@@5G J"H@7^3 c1EQ'IZ*ht=r---6 VAbx},GZÇE"DO~p8(JGG*rؘdnw]]ȈM$D>rH$ώ?ݽ+X,|H ENYYYqW(dbb 5::& w}(\l9/_rZGDqD%r`1%poTJrkkJ&%@ '''%ɝ;wJ%Angbb'O|ɥKdL& tSԙ&\ɓLfeeetttdd$ܻwQ,t4j_5PWl6oyyPdr8X,V(b1r. jTD===vh40rj4x<^(`Se|Q`0P #ɕ 4(0@ BSTc @өT* LixDG;88!jZzM&ngX ZmmmL&+JP?cݕH$ccc_|6فHmxx?$էn={ӉDBՊb>ߟRT,l뻻f9?Gs8FT h4Ѐ/ W蘼ZU!~!F}Kf5C4 yVHRV❕J% ӋSORypxzzPC 5(@5PTVVV^|EPX̲s̿2\;QRtA'1x Y,fxl6;:p88AƉJ&a}}}_V+D"RRf3 ȑZE]^u۬>zc8ZT㴪88Ϊ.bUZ#s;`u}Z](J%HH=s8͆HP4"YT`zp1/h%I(tr|vvk4]+@ FNF$& %z<lZh4& Ϸd2Zdy(moo"XJZ__P(|w)J E,onnbL&R)Ҭa :66f6444422婩˗/onnRԦZ_C 5g&:D"t>CA "͆BL& ð]=['Wt0\5GU(+ŢNV}*\.v$9NYVeZZX4bNj&b\|ܹspIEDR__svK&X,V0فNJvwCAHL&sssAuI$Nb"XRIR&)HH$E8."8@ j###RtQT[[[4 BTkk! x5I p`>Ym<T8_L&Cpx<DKpf5Mccc2fGN $Iss3rvSD"iooO$X ^z{{}>P(FJRk׮577H$5Xl}}=H8qbggghhbMLLAR% 6`IIDAT}xxd2O>JL&Or9dpxNOO'Bd^p8J}+;v {dl6Gbcǎb.;88hXG<|2BB+ HBEXX jxpxxX.{]NwYK5@ 5{ZPC $p|p8g&㡒#6+DbbX, %I@|I9P0c8`r>>h:F{P-m1o7X,bP(b14b8v<|a0 l6Bٳ^wffrVcɄ;*{>Sj4Lf8fX`0 D"ԔNZH$|AqLVx\#@{֭[ml L6V Hkk* ȽKrYV766\.a86н$ `0$rCC&&& Vmnn]VPC 'Qj\.oooKZmT*%8 #JE"%AY6D<O0&A58zxCA-H$BP(bXR`0 )d2oh>%7MZ}݅N9;6555;;{-\, .X,%H$~f?W^կ~u֭v40RncRPdT*500|g?'|211Q,2Y,ܜFϔJ%Vϯ@c~e2Y8TT*)5@p陙f+vTH|~ww͆ݻwAgX.]lfz]N:%?@ O&jZ!_x1Lʕ+d2ymmFU*t:gVBӅ&9<< q r$J, $yJCoi! L`<a!H tQrT*U{^d ^3??…굫j65@"~O~92k"r~fŨȫhJ$Lf9P(T* BP,+JD#B!xF*@ j*ÏKR|b7\. Bx>! SN-..?/ñXbfZWW788x1*jZm6BJJd°gϞ ß|n?|4u\>LRHjpV}נ rwtt_ommbjjĉ}}}bqnnf?}T.A>rր;N#l6ꎎ!E&\l\.b s\sI:n hkk{WNg2|L{fɓ'bd4T*BvG"-#H&'&766b:zt:_:<88z>駟U* Źs>Cꫯbx<^.fjWWw󝽽r,fff\.?[[[:noooss#Lmm6P(hooǙ|&饗^d2?ugg'a H$F V*\.''@³@B,+x< PE"Q( L&pԁD Z9| E*XT]Y=Jb,nPPHRr.+Hd0\.i{{;*ʦ&ñT__L&U*D"~WWW?hBT*\.<}d2;v͛|>篬L& z _F㓓}}}>lnnfX/^3UXlhhlvuuh4z<:>22'0h4`0rǏ!Z7iۻ._|@~PP3BXcJF0:|>XI$tqľV5 Q*4R 1IEm&ZXVo05^T*6fN Nhh4 e. gΜa?BX]]à5LMMrݵׯ;wnssf\.F3>>z? dd2$l6uO[[ZP(Fa:[^^><< F[]]fT*5Z^{f?~x~~>N766X˗/>}bi4;`0xZ;~x4F#v [[[ՍJnjF2L~7B/P(|8^]]njjt7nܰlJE ܴLxL&ASSL&h4>}e2Y(|\nzz:n˵}K*~q4UTbzP(lh4o>~b2㋋Zɓ'i4F!Hr6A?}>T*ڢDT*EPvwwy?B$̙3/^rZvppҥK---x=>>>:: n_ZZfHd~~06pV@Jܹt:tݫ|>?~T*ݼyS׋D"|IRŢ\A\rwHݑL&BH$j$O><88T*<o~~~{{ bff\.z<+W  P( R \J\.N&Smooo(:880 ~ii)Nwuu=~ݎᘛ$IPL&/F?88@*E*C W7RIV' 266P(TjWW55Pjjxvۻ|28 /K#djmpd0&U @Mq0^PKԣt:`0xFc<Ǯh4CјNS+rﷵY,D"''*?q8rD"a0of|>d2566r8fΞ= ??3L~T* P(X,&˹\ L|2l6{~~Br9Dr D{nR޶X -RiZu:l6P(P(AњΝ;7??2C@p۷o;;;e2ٓ'Okd2|pŅGXJp?H$|>liix< fannLH$rU9???::8;;ƌJ?~|oooaaAЌTuA}}=\jr8XւS455Gi^?33] ?+W(J͗vwwO>xf3nrT w)HdN>}^___?77wΝt:(74 7bwwRĮ`{{_7oCCClMH2, 'O|A8Y,l6366DnK@B@P|D<Bc>& ^xAӍ="j @ 5~1#,9"Ќ|>pl6; W߰ZУ@??zxhu4SjKP}?E&(\.DJ$  ]|YV_UXo6$&&&h4ڙ3g D!"LRp8Ŷ`ꊣ!FkmmR`@(p2@ W(>fJe4ܬhH$RooP<w H$Th4B!(Aƅ lvkkPԐx񶷷؋3g񛿃5PC _-j @ 5<#^G!BKH;b0cŤRi6|>Ռ^ QUcV'?G/:'(J0gXg#JL&XF2`0(!J|~_P?^*:`0hZ}>_KK.LFl&>B ܻw`|{Cĕf[[[d\.Wxrh4o¿ Bقse,[\\l$7SՏ=Z]]=}X,v\6pfeX4c@& ~xjU(JR$ N&%I8F[:FvRlRn흘hmm54mwwl6hRW"^?;;[(Ξ=d^o$a0~P*:;;(Fd644X,֖8lt:.t:)J6E.!Lw8]]Fqqq 7<~A|獍ARd2L&fh4s2 ˅@ߺMRfl6DdL&cs #L&æ"L½iiiN744郃nZV+<(?cDBP8Ngg{8R@f>kbcX,FA>okk3 t•5 'O  Z ot:}xxx4rH$lJ&$#Y,eTzĉP(D"JR{[R|cM&Znll|R=<M;\;Z-@bxnn. `0ѨFimm!qad2Y.aHD"JN裏4 BAv" *J 8A+b =p8$IDȹ\.QWWW(Kdw܉b---`N+^$ _x_}jjjxD"կ( N'4ƷF C^OD(dR AЯV-?n/4! b\lx<^RI$l62V=zhggJ7n={իrzt:WVVƆwzV+theeeevv6H"a |T*KRxFsرD"D`DP,N;{x}M.I$@ xvh4 (Dh4l8[ bP(:;;3㞜?vX{{`P(ovP3m,n+JJ% b"t8L&3SNt!Ni2z}CCL&Ce2.,8lrMMMrE"\^Z4 P(D /, $^ȿT*tz|Z- A ~KvvvD"].71k @ 5JŒ{\.~?jͺ:XH$ łJtxd2?#\n40>Jf|>J ±1BR,ZmGGDs@] " %z*HH$bQ 455Isss4%HzbMM\jp8Xlۣ(L̗/_~P(tݛ:PWWr0wݍXHRz~kk+_xm!X^^brf!"BחX,J2 fa\l6佽=D['d'\.qhSQ)*Hx:HAZY '766V*d2i0bբѨX,r^f-˻ヒNk?65@&{gA,PIBJ OR9DBQS(u/x;UϪo+PKdP( b!x~.K(h4p!F T*X⵵?x l^TzI$R2lmm Z ׯ655}{߫T*2 T ߿t:Y,0j׿5@ NJtZ*yLą NܜH$:y$khh(͞Ꞟ.jJRlkkSKR8o޺uX,"ݖ466arkk+h4\. CYLP0fd2Qgٵ`0].W&q\h4l6՚Jl&tɈH$;;;Op8B$ 4 yL!P(  |>{Id2k+T*U֥P(|>J7 h  F2 0zhAf,&79j}@mPC BKi42kT*- Dd>dZD"hB6@CfNR-1Xx*Gz fY: 8E{Vm@i449"&b ϯT*>!N{^coܸul8>uT"x+rĉ_9l6[TNOO[ֹ9Hd0N\.W(z!Bn#o8Rԍ X*x L].`0K&y]\ @|~Xh4|j zyդiH455=55d֘LL&YVgٽ=L& $$bd2ydddggp455r9[*d2ZHT(vwwpP(t:y<0 ͦi69}~c& 9 LNRN/ZJS5orzTAѪ\.L_(RдeuuummmMYVݮRz'|rxxh04ܜRp:.+q\L_*j @ 5<+Tx"gC&*.&(\h4䆥RjrT*j /xf# t<@B>;t:e2attaH4d=x,d2e2}JR(~RFfLD"q}HN'fKP(loon///+bd:;;\b`b2^o4:sƵkrLF( `0R o%ZRJ6mooN_p O>E[(R3 <͖dlNJ%lx<:NUX gR|T*D,, ppb1=A@ d2h4ZT0'"ɈD"aS`rUJ& FCA -/ɗ[¤1DŽ>UT_7P(J%F2>VFcX %L"fEBH$+h4ː%Rd2988/]ZPC 5grxo#b@ PTZZZ\Ũ{P`u`692nlvu2J\P$I\rlUT`Xr1jL& f2j% *4X,|R`DAXlnnjT*׿5vl6w}}nIR&\.7==xEѮ^`0A\nzz~1>>..:6 B{iiXp8=I>fPAnmmsF1p\ae4ajN(^(AA^Aw1Ai + ,T*a1d2a ׎JV/7Hh,y@XhYeGb?BIo&[hBK0l82j6T7YlX,;'Otvv«*Hd2x¦R)tן~h|`jPkjT,#Wy!]N5'Ĉ#NO"` "h4h4"b =vs8P('=bM b%X Qx

    ?220GtB`Dvvv X[[t^7͞={6 MLL,..̜x`6dq I$RU9h4jVr(aU4E qRcs K 2kx8xt,A[;xC% zg j*ywXXǏ?xGU&~aP(666VQTX\.c~f#Nen7LERr^ZT󃃃[PC 55P*իW|l6k2ŢH$RՕJ%+JH$2#u-^PBD|}c@d2F\.O&T D"164NS8+ [*  v:HB !+333 B_[[d.l6XGL&w ۈz d2Y&uvvd}|'d2Yvvv>}TP`F6w~~~aa>EZn4ԣJjBp?z6]`~=5R[w>_GޭJ];a񛛁~V}'*9 *h) L&nTd2U vT r}Xd2 p\|]]]v:;W ??GKPj7jxvD"ׯ_wݰPٗJ%.РVAHrK4_*Ja8u 7wԾ(]Q>fr xR߯Ca> tbaŁদ&xSہ<:m&anc*U[N`q4*OuG\;P &죣9(U3:,=@|1Hnt:*|>FNgU*lD"Avwwwvv^dH$=~x2LO~===UB] 5P_Pÿ iV¨*QI>! !OX,v:PdŠ:h<H/ptbz=- dܪSW$F_dAɪV777766d2YWW=͛7dR,NRA_rrz>}pۻL&SPJ:|>y^ׯ_/Jt:!bBіzDb! ՝?~ww7JMNN\ T*VWW\fsݹ\TbAߊg~m@B!},&WU U߱dlfP`0Lf:FT c$K_}LRAeÁp@ L&A\.Wx;`2L&'|\ΓL&% BPC 5>gIz}TN, JR*FQ>Jbٸ\P(DWWAP,..p8Us"C՚ E| l6۩Sdݻw}KKKCCC{{{dR*:6d6(D"U.Up8b;wN&mooABxq(Dt}\~654oo+N#M`͠QU*.aTX. l6t_*៰0NĪ*uGT @ bb1([, }V)3 4+X,o 7`0 #' ^frU}> _g"j=jjxF@A׋4_ H$R@ с)>x&XBł8B PSTb1/h 8l6q;BAǘb#Q@f2x!GQ\ؔH$l+;::Jn@FXRilllooG6B}}}GGJO\.7o"= !Y #Ì`h4sxx{t:"ׯCu]*.\  =zxUpe/F Wy)p-ګΥR JWaD& σ`p୉Eh(!y*wr, r9N2ĸtFU9UZ 6PA0d2IXJ%dgY&fb\.ǑDQ%Jioo/b r؜RpbQӡD"|>7O>ǧqV[*" T*٬D"?NZ6EXp6N&CCC"\(FFF{vX Q^kj7jxvH&|aI"Ac#0eJf#ALAbcXжrRz& at4 X1WpQbrIB y~`9p8'O88qz`m{6EHʒi[vʞM6ɞsv7ɮg6ؑ,efI$3{3M sv9020p;s[on E\k: ;I: 0mh46 %i=NHbl6 QZ 7VWWEAP(chwJPfxaPpX 6xa:Z a[+ , >*XbF]C$! ,@ U*h>F0#KJE>b_G,B 0sC0I!x6h0T***d26 \p8T|>O X,nmmlϟOR>O(iXߟNzh8vݝvybhO&Hvy*ޞH$F1 p/&c ߟX,DB*z:m 7B,T*xr6#?/c:[a1e+t4AHHág=Z-J91^/͆/A E,w#r366& ԩ<xxH ^xa0&''U*Ç^cc# hŋb>hll7x<ޣGH$Rss3B^6Imfn*zppx<|>-O>v O0p xJl>L;!yxSx6 QX,A)u288'j5N%Nh4AjM& xD"`0X Bqh4 +eRiiiK&Z$ M&<`>/F(HlLб\.$IF^~v+$\@> rr9HTTbRޥ6jD;7BY,No2 E! nb4 Vkѣ~t:gff`$XJNK8?AXzIBQX,@A|>FCxBl6rGFF> .J0\S$x#7 :-L0XM`{<#;uXtSSS<p\p!HdC v`kqwϟ' <hrr\\.H$R8v݅B! >x`ll ,?O=BQTbbXL&d2[[[Jhb%d~~`>Dp8\*X, Bh4(4-p8dlA`^/JЦrɁrl8R@nj!VlCъb>Jh4* ˥j)r.]zx|xxP(d2|>_Tm:<< o\.$T*`0VVVJ*NCE__`x<Hx\PJ@:~|nN8GQZMp,ϟ.\xQTFH$IZNLL|],+HS F$V+r\oooCC7nԧsz^J 18U*B Y,lmm[Eȱ Y`(JpxPSc1lpPVhG>h4 o>OR$0ht* u6T*JR$ݻwOCFܼѩT)oݺ522 }>~\(MMMhT& ?&HZ6fF,8|8^][uny^}iiѣGl6L&&_nooB!H_X,#xl6VpZL&{T* o:744h48ERrD"Q/ `)JJRTzh4 l6 (wvvBZX t:bbX,|>_XaX"aF 1:|GuwwwwwJ%DD"MRT*5h4=|hw ޻w/ju%^ :Ѡ#)D"JgxH$hBX\\rT*H$t:<)hf0prɤjf%?xPZDѱyxxP(Ţ@ HRAxW+խ-p~JR:H dYaIɓ}{h >Uxl0 H$ x_>F3f Tr9rwzM  'P(w===x|ccP(9N/Y$P f\&?88?CS =ytE*[oTwºz]e׮]T+/^f, "\.wwwwqqL&{^v`0d2Y d2hE"<eRyL&CPl6T*0txJ%@ P(p8P(D"N$Fbz>NMM l:{uwwjpRJ;wX־d2rC@ lfy``t& 3XD\¸\*pxxx֭[= BͰD"8u1 U3V?T*`ٺ{.JRtkkB633sر czd i6rH$FGG?{ïP(/^JB0xH$?::KԽ{kW D"H$Rt:Bx<'jϧR^x766 >%HJl61W^y`0$ xftRE>oUSZy/`d2`n>/JXj)Vx<傶t: 7`)p n#^ a=B`2BlpT - KJ2 f3 ׿G544굵5NVhJ.?!ɱXZf2$r\Ry}YXjݛ ɷoVTd29HB`00e@ FU*LOR%閖KR777!\DؘP(<<_$AљLJd t:! ,^1bSSSX,6z 8??/FFFn߾]T4 ;;;u:X,򁁁5mvk{l6A"vwwt:j% D HlnnfXdR.S(hh`0 .A$^r +_zx~ggF˗//..b/^}kMMM?O4 ;o~3MPFe2_@ T*v\4d2DL&f2|>, Ph^$TYP(&hd2믿>::Z_ꪫϧ @]u66ҒH$lvOOO,p\mmmqb\,:wp7n'|fh{{/;H$IY " CH$Wr?H%ht_Rϟf@1<ȏ TZ.JfS(b1F8 R*P<>>rL&^yFd2+χRBt:{366 <>Rr[,bp HRz`zA~' 3'!i`0 FZr0@WÉ>^ t:rO* &m6('''X~ŋ^{t:E Bccc8`0HdccN#n9skC^PL8XZZꂝ!lϟ*p{{R"Bӧ JmiiD"G|cGV:uJ T*@abwwuk5FIRV.^ ;R Zf&Jz=岀/`0V5NS((4ojjjh4j>VbЇ@&V+D!'ᡶmoox}:Eћ7oa6O:U*P" H8|\™L&d2CPkk+tR\.W& RUK8o߾F(ڀ3EKHU~*; Gz_&`0qtl HD~ JR@ '!\=xQt>*x7vvv>|XVv{:6pljD"p#<~&H$&&&VzbD""M@f˗/aF$O:Fc4d2CCC~`0g||D"aVk6J%|<O&mjDZrf$m Ơ"\.7 "H$ioor~Vx‘p__Jjll݅.*ҙ Bx<"r<'`0FGGGVkVL&z F >d2]řL`0*JTD r\0$S{{{jAR) ZW\o.J2 rr|fy\N"< PBSSS[[[2U{<HcccZ]]W@9t\|(Zh`09]H$WVVJL&ʯJ\ DhliibrʣP(Pp`BB2 xx80QU6&1 (%l'`dgO.h4Sl6ɌF8dB>Jj51::;;;766T*@N5*~7"qSSS6u\, \.W۪=<?33sڵGϟ?~xxd2)Jͺ#HZ[[/IPhi՚H$h4WܸqO?^ BJR544lv/\ŋD"quuTx/;vLꪫ>W/uFoݺfJ`>CS@C~P(pmm @?86Z gbd@o"D"@ B4Gݴ\N,xr  B"t?C<E>@*t@ 0sj5^<}tmmh4G(apccyWj5v:ZCCCgϞr"hvv6+wx<,KVJ%&)%T\eY^|>L&%P(4J6(WJrdddkk+K$t:$Wsαlp8Zp8 1 t&Lr2pKL&ǏcGd9]NT*\.ffft\.TH `0J` `2MMM>OPb-Z###wb5644Ӽ p ZmKKnD"/^kZ(ap'x<T*Á,L|ŋ`0866r\\d^y啇2__r]]]@ L7n|DP@z|||ss3bY*^|>fV充 ߎ96J===>O.G"ꫯloob, {J?<< NkjjRj\CfT*gϞpҥKj_ꪫ>/u`<|> TBP(+Ng\_dFZ[[9l6LRX,Ft:8s U 痵Z-wuudH. 4Xb9ǃ @P$L3G" 499yIByJe2bxO&<( >OdYdnoob1}ĉB +2??1==ק   Hxp DuKÇi4DN.jhZ#FGGbNH$6󍎎de%wd$7nttt0̧OTL&ӼfK$.+HpL&C&B!knnlЕ&t`|nIX V ~7.k~~t:Jh@h4]]]GuUW]+zI. mVUkxX,~ {AHaQ> b1, ' p@^.e2@x; _JD"ZB!LH$0pCaɄ[pK?> y(<Fj Joo/F8D"`0*ٳB罽===TR(µB$A \j`+ZsssPvb1:(@p8b1j-*Z.zNZ~zggFiii|vS,_Ap8`0 m?H$ d A l68btFQ$}'#NX,5??X Cq2dпvpp@&\B"޽N |> eh4bǏ_|d2\.`E|+t:=O4- eV(l6p8"ATz\`fX2o`0t:͞x!*?~ ѡPZ^|H$bLtppDT*fd$@ 4]pa{{xM buu+_ d2MMMf@ p87xcaaf;NWZ,zi4vDLtEEpSN% 8'FyJrNH@ N>R$I"( `ٟlFXpLLL T*x_7??Sr9Պ`V^|2L6DZd2y*rN`j<ؔQr @,y4ښL&M&tp6}Q2TJ.% $hi4NV419<>`|P(L&S*ܿ444ē'Ozt:}Ealv:>\h40H$\.mnnJ} #;w}6TZKKKr߆7I4mjj vBQ(D"xjgg" ~UW]uKzIBPrᣇ:@`LJQtH$Rt _ X,J% 1# ?>zXlV^l6 HB0G߄H$j X,V"PE$8tƍ kT*AJŖn߾b NxN?~X,ҾH$>{#JeY4 oSS`0d`r0ϹnͶDѷzK"ܽ{WT޼y3BP(b- ,WZ!d2xy6 rL&sT*=<< Ld2VZb)JL&3A &__;}f;wxT:;;nWj- B̸nNL&!0:2꫹\nccC$d2\.Íȉ'H$RPK'H7oބ&H$j2L.ˑHP96-///P(T*t/^looSTŶ6F #  l ʕ+pϓH$ס BmmmH$AJҳgWd2y 21XH$d2h|X,fCC>4X,NnhNAE"0jd2 =|ixx/uUR^J>8}NbwP8*D"X,RH$. QPVu(m#?8pr=}X "h4 A~?4M*q R,r9  򗿌H$ٳgX 1jzzԩSvvyTJ 0Ύj>_[[~pu c, :sL0z|L&R%lvkk :|>Ut:HER$lvV ñX,ARc~fCUVwwoxtLf2lvww7LCCC/:^\\ < ) ۽g2`t80n8aL&"χ@`:;;LT*=88Fj@bTU bx|800pƍr 9P(N3d)]YYI&@Z>}[,G?> ŋ---\^c2ww1Bhڡhqy^&3 P;lt_Fɓ'oO]uU׿T ^* ַ@ u6y(l6'|`q"ˑd0URt: ^ _T`qAV@xBR#FwttBK l8ϟJD"bXL&R//q(qppF Vv{KKK2bD"ɀDSSB!x* L*Ai}}FBJh>dZZZvwwڔJ۷V%z^.l! P( \3AJRSS͎D"9<ٳg`xh>O$=d "d2l6L vfϜ9]WL&u0.ST,f|hG~ @v@__FD"gΜ>L~`q Bϟw\^VP({ppb`f7Uǃz n{nn2 B@t$@ ˹\Ç\.8={066b2Rr9Z](  )hh(`MX,VT n:BJh4nkڿw_/_ꪫwo%T*aO> V TcXTUB?GvHiG@OdO{J tj5*RhFa"k \.GRFo111aX,֗eq޽B?~vh4|p8 '::V;::B$BaTبT*T*dWNLL,---..b0WfYdƆX,`|~0FdZ 477Wx{,bHpBLjٕH&~6;"nDb6{jJ ڠVAPX,xBdbbۄB!tx^f2fuwwEñ?;; ǥRvX`Nth4)~zϞ=+P3 777m6[ccs禦3JQ6M$;f6!=@<rhlmmmooh4a~~7ހ:mZm0x<^WWlggg(ńB! j5xRؘNE~ESW]u?UKLO<H"_U 2T*zinnnVg s|g&09>>+}sa0D$FFd"%.\.sŎ^R]vpp`s@t0S |Jju, `jr;;;YED"L&2NOMMq8@ nZpb`KK ͆f2b.]4xfaaÉ❝,Jt YIYFƣZXxX|ԩrٳgώ:|UKRREsssG'j,O"pI"D"Dd2xS?ߟ0WU ؘ33MәLfZfB0x˥8MB,e`0?;q2xVVVx<DRկ:n7 O?ud|>_b1,K$᫔Je2a\C %I$h4 l6[.KӍ:xH x<@C9ƆRR2@ ZMhuf6j"~[*BW @ۛoe4jMAOO@J 𽽽SSSeA0Ut:乙L&"`.fD"1JUUΦR)RMMMoϟ+JA2l6 '2l0h4L}>L&knn. XfBd:lkWWԙ.-FnR kkk|>_RC+^BI$Hl+@ZZZ[e4dX,f2p@&I$R( Іr;`{6 ,+2bezFy}H$p87 Azp]u9T}$`,--l^T*6|>/$D"Ip8܂ {_u @>Z2̆l6`/B)L&Œd e2>LF$t^mnnX,a˗/+ gXb0LTդRi0 333ǏOJ_ L W% `O$ *je2Y2sh?q:c C%I.D"rʒժV=sFgggTzj0D"hAszD^jU&E"H$JBa&IT*UV[VNg|>Ba20cǎO>$tQVTjRIR" #Nx<.J;::rHdXf2 h49sF.@ 4x<V*t: 4-Nh<\.ooop8 +xlrG2r? H$bۙLfssp8]]]Z ]Peۡp-Lr9++Qxjx R̙3Zmuuj!' O5ORmmmxUW]OzIϟ3 A/h4x'BDP(0$A-P"X,,CZ644DNFa@P H$RT8Ctxx---<bwww|BQ*?.I$L&{u:N&{%f٥%tih8vXZbvC1@JS HRS(jDbXi:jaX6 V/~QR}Gl/.+ dx,+rh4 X {q:J2 * HTR\p@ yh4NT*SNEQd. bٳg5 fl$= , ѨD")˩TD"B`nP(x+QN3L.C\.wbڵkL |>ԩS{{{X,{<*d2NL&o&1.^ pPp:VDJn'd楥T*U* \.W$q:L&1 4 #, !kBQ)WjZTW]u}T_% N֟? Nw C p~?gX~J!D"|^PҷMW?C= ~P(llJ2 D"xA `cqqpHcǎE"gϞQ(ӧOwuu|%+wwJvwwe:.fj`0)xbp$$ @7t|||kkpx<^(RhCCƧ~:00kիWT/| ###'2==v?S聦h2ɓ'^(M$Jb2T*\ Iҷ~} ESS Ѝ"gii vBPTdr.9s̵k׎?l}>WUK@WWW?c=X/(J(Y vt:B! 0 pV<f$&'l-hTBX,2 \i6p^T\N&d2F=]$?<== upɀ`hNkZ8C$ABggG.C9@,hMMMP(σP(hX,N!8NzX\.oiilpTTL&z&)ˡPxxZBd2@ X,Dt:]\&Ha?$Ih]\\DJET\ZANzj  ZmV^O"* O %A?F<xhGGG:>s$RT(VWWFc___"_嶶ZbH(KӧOs8DH& \(RTr\,L&kJER|Ύ@ dBy[ pj jZP: |UK?{luu>X,b(J J$! & &F}>L&kL&;R ͖H$#q8P(Je\T*ZG@۫^z`.`5HG H  \+ ?s*T*GFF^{gώ=ٳg2JnllD"FF ~ŒɤN:!Dy`p:DD$ &f0dS(xLDessv`0(Xx\9{4-x< @:F1hZr:nxx`zH+J<L&Ϟ=F ō7BŢD"\.?}K$BAj5>/4jM6 Ş8qF566xoXh4om0f֖djjj"ɥR`>FIO>DVoll2 6 XF[V/~F/,,|'OZUW];-@u_˟Xr!XVRAx#r 8X J?T*ZBeY0ONNy(Dvjh4j0ry<v]211+L&sjjj~~^]x*ǎ AbHP 𽲲/Olff )O>w\SSS2t:r:jt6:ST޽嚚(Oh4ڏ~J{iX,dZ[[bfK #Htuvutv|ᇱX P3bQ h4d\.י3g JD"ijj@ cǎAJe6+BH$gϞ}Lf*XYYihhd2p^J$y}òhsGP`2oP(w)(|>iincX:Y,HT*ZI +nD x<Н;wBS~B6OPNĉJr2x<̙3RZϓH$Rl\fjP(<|Rh6H$6fescǎAzA|>x(h* B rX,lx{r9o ZH$8 p{yz@o@K" PP(NTm\a"T*裏y??+Zꪫ @]u$}vhO&``/IR890X,9e\N v{{{;3L0͚H$BzxZb0"0f BT:bp]Mdp^( FO[lGj(P^FT2T*n~J...BUKK 0>xjP(HRfggissS444X,ӧO[n!"kllL,?yd``s8JbrX޹sX,h4И桡!(AH$*J KB!Hh3܇P(2>>H$)Ja2rPR!H$r}qꃃ2 h[Jx<>+ Z|>xӢP(f0LEPf@Nf$ rfy< PT 0R@ACRw>XݲlTLRPװ [qKKˉ'4۷wvv; +p8\RZW\M e\ ~uPh @x aU(HA2 gBD"'Zp8FCL&ZZ-e2J%& H2hO&B@рN\FxX,f_ kH$F"\. WA``0pGj5:'O^tkii7ёJ??9^{M ll/<_7aXV맟~zĉX4p:~?nY,b9vةSn߾](Ba e^8m-JmmmG.]:<8T+~aaA"h.x|2ȯJzp^sJ- ׮]Zkjjlr655T'O'曀`Or. lvooT*.N8 >|HR_,߽{ǎJ'G.|(%ߩbH&aE@j-, ZdrRf& y>____ZZD"|>̘L& ^W,H$ 8*ϰXZF`T.aJ{ꪫϗ7uk7p8`.j QN777gX,$PDh4 {T P"hەJ%2 B\./@`a 0H$89/B

    2AT*s nz{{x<}:P(W^AzA5,{ D~ W\O>W\d2O>%>_* ###l6nnnZ,sxP(oիW#ɓ'}>_6=88hl6;B~bx<6 JP(/=|pkk̙3p߻wR cbbbꅅh$񣮮rC$la{{;͆X,FRǃ< q8\.7<;#vPXV(J,1 "Z A` B\nCm8 io|>AV`jVt:)z fCPV|>F>pprFGG~wuu)O>ȞdZ h4 0jd2 \RTWVsssX  l6ÌGFF߿o4?J LDF]t)t:H$_mpLѲl"֪D"@`I&pnry+X lJ `؅WR@Ʉfe:zZ%Zf* <Ȁn aHcX ;E "r^pd\.dQt:{ X,XT{º\.L&ܢ3tV R:j (BRp8PD"`qAbLC&S<@;MRlVx<܀ (}eZDׁ仿j! bq8ʣRNqfB5uUW]zIɟA <O*VX,`jXX,h4I$qCCa=av9 H0cib YD" prXRp8`0I_ ժBA" PՍPEP!iT*#ZNRPTrq@h4NtzRr齽=P8===??vtАD"sLjsssbțRQ(ODBp8|~RF070D- ZF"vwwS4Vcc#Fs8KKKSSSvBeD")ccc6޽{Bư$ { C.ܪꮮ~" G dR(NLLXVO<777r˗/?V]W]u W=\W]/Up"gT*pDQ<^|>.s. z^. $wM  rl6h4Bt0mww@ 9N^r8N&T*`0lp)X,Exh)f0`xT  6=66xbkP8>>ѣLo:ul6߽{wzzzdd?d2ҥK$֭[\_jn{hhJ͛7)><m~~L&o`Hr9$ >>>R>C:vxELlV(pǿLP(|`0v;noo `0+7 j:x lF8?LRЕ H.  d‚ k9X\``Z½ kllOF#Dñl n$K$)ծ{)t"B1H\._YYh|>x^BA777766|d2/GPV}]0 X'O|E f3gl6* åRiaa$IߏBlnng?t:955h UUxɂ`8GFHŖĉ+P.vzp]u'^7Nh4DfRJ ' x zg6h4X>CeZ677CTFl6Jy睫W[n?x|~cǎit:~_,ZOR+ػ( yV ZD*|뭷azz'? lh+###~ýfmmጎQ. reCөTښj*N采X?@R޽[ZZ\ ! V{ĉcǎŝp8 -A$X,vppp8f A4O]uUPTW]/IrF'??C2kZ8d@ `Xj ݽ\@G_?B*L"IݎBJ%M Z&H^4cXDB D~H$BX,F+Q H$wY_x X,ٳeJTX,o߾h4ַ\.ת^[[ۏ~#3gܽ{,:__JRh4 MF'x<.B`hpb W^y%ܺuKPj5J&_{L&sq9j`ܻw0frA06%˩T*D"A0OaBUGM<`@QŊFPW Al6\*666`j+ pBσ&) ۍL.S*>ϟ_|Ν;,A&[[yxxxyj5mmmFP x~:x<B744p8AT*wzw!v_>oUKRZ`0zL&- B&X-dC$zKR<d'4^gr`p@r?-x$BdNeέST*[A8f0h4 `BuxxH$‡~ZvUHD!sss׮]khh`00޻w _B*bMMMd2fQ(3g`0zĢT*Gd2Y:NRHj DꯖeZ Hoo/h4xlcmCP,kjj:sLss3Pb1 BGRf+===`6-~g28Bt孭d2x?~LR=d6.A<{?X,rd P,ɖn7nUkkkXuVss 3@ıcjZCCH)h4'&&X׿uX`ؐJǏ*TZbFX,z<)a4+ʗRp8ob0[nlL&s9&yY8??_(Zm>裏VTx<ޱc*\.x  uyL^*|>BubJe{{{ccwwwm6V}qWWFCѿ{F޽DT*եKN< |$S(fN"A _BVK$ lD"jlN@ lllܻwvl<&i}}`Iү~0双 rpp088t:>} `0T*;::> Iy&j(jccc}}]?ۃGMxP(VD칹9 x_755\`0P(r:u\xT*vvvr9R K =|P&y^IO:. h4j&h4KjeerI$>nw[[[OOJ5pVe6VL. *Z-|>`I*R)Rye"h2%j^/.]ڄ_?(l6Nfs4jŋd2 d2FFF ŏ㵵5 r~-ͮtH$R,_}UZ|J~k_?S@R#Hoo/mii<Ço`n/J_xQV]Ff3FsΕ+W֜N'H!` %ff_xg?AGGHTf2Jz 222399i~?Fb#d yccҥKsss$j5"K~]u9T^ Ar>|r&]h4 UD8w8OR&8L3l`GB.ϷlF X REBX'_WUJ$X,b0 B `0zd>x< 2d2U*@^E&"tLLLLLLHb ǹ\`X]]=;wRzӭ馦&>rM$P(@P(0B`l6Fva,fXDVBd2 O/lllljjr8|Qr]tibbbnnbl6S^.%ISSS&ioo߿ZP8WD" Bt"u# dY;wW_ZP퓓V7~7,2p8rGРP(^}Y}\.r?f_zurr2iiTpFY֡ݝzcǦXիRh(L&7779h4 ؅Hr޽\.wYuppa2 ŋbJD"H$6 H|>CQ@CC<|4Vntd27nzH&ϟ?@Bٕ+Wn7  F lmmApX A,r+ɓ'a_UW]7zY!h \.z F=J"T*b~}}}>ؤ\.J%M&bX,&IDB8NV#0߳X, x< xB nb04 r\SSONND"U"FjSSFsOJzzzzzz>c2, !7fYB\*GcccGGB#---ohh`fyuuJFQ:Z-ͮV@C{@*|v= h4bcrbZBdY)ʱv8a,od>@  Аd_ZZ,khhHRy< ɓmmm(jww7aA=d"&d~ _HV'bxΝD"1??h&''_x- ].6ТͿ7pҟd@J"ܹŋT*t:Je,^YVɤb! ߄\.tҳgp8\&|gϞ577fyۉǏrzלNr83337n܀6N]֭[|buvvBweeE.[޵|>`0t:^YY~:& \hr eat&j,r ??V)<vP(LOOd }aaf_|D"U+ @]uS}$879:wGr T<_G!b 8vNR* RV^/q8  iH$ɀǺ6 XbxgX`dJ`0J%L\>_V S*x<^(vtt\O)i:xxD"ݹ\w`` ?{ B;xFT*/^,tARkZr ET:qĩSH$|%Jx>M6F~ 5UnpO`08NPDT*D"1 < `E"FIcccHf577wvtN7dRVkwwkf3R~ECDd2Y.{=2|ɛ7oDw}WWUF|%?~\?noo'HvJccc{{{PV`0XTo.//Ç"H.C8_tB}Ν;8T*MNNzX,3|. q҂ Ǐ|>k׶Ξ={ϟk4Lf4TH$T*5Ncwtt>bl6_g2pG!ˏ?G5 >O2 T{H4DkkkaِÉb2V9JbFGG%IPhhhDCCC$"T.Ջꪫϛ @]uTYjZ, N'KTZ,l6rJe0͡P9ioo+ % Db}ppFڗd3gIR!axh4ࠡ BA CGGX,ٶ|>_*0h cccT őHdssd2577R鵵5p+W444BRv^Յ X5bd0fffv^\7|~CCd`I`0**{߃V8p =YFOO-..  z+d18\j?nZ# .^?(uJ$_x@P 뼿駟ZV s%(mhhhnn^^^5Llv{{;J %/T*phC "H:;;rn__p<I0rr8P(`0 \._XXP,r\( fh4XH$B D"孭p8, ?.4Moo{q>T*U*U>qH\]]zk׮i2<99WWLAg=n>ҢP(fffNjmkk X,޽{0|>ddB c06-<Ossɓ'ww8^\ȵ̭[oo.--L&`j4D"i d ^FU*U4mii:bV]uUG^`8v\<@PlBbBpD"!"x|2d20ك "HD$=nO$6mccC RT*eLJN'!NLDԔhYڈH$dh~`0lAR)8Lfooc_* 0Edii 2ɓh4znn.FtU`u &`0DhS"n/JLd2JAG>J%htkk+Pϟ;۽U.+H@y{{[" ' ~d\ «W޿:cf,kii 8BvB \.RDvvv B@ ׯ_x(J Dљp*ʋd2vC/ۧ~LRDbOO!DZ{}6֌Fc<w88AxzRAbf"[,K2fйf].W(z)>흞B6mgg ȈD"D"2!qF^T*O< }=PW]u}T_% ?H$ P( 5d2JL&ST+K_X,h8/X2t:bgϞH$j: P(Ec444(l6 TM2B¡0bpasɄV*CCC:V].PCCC6u:fR坝BhFX ďc@pՉGݽ{㍏{{{ {{{jufffpp0LF6<<֦hf3+b1@011qppf~\nooX,̙357SŋAWWԱۿ}6yΞ9sk_|ldOZFӧ򾳳"HǏכ\!LT*ʕI$Rzihh˿v0z͎B}bB!,#Hvwwf766:N633`&&&AC(vvv _^կ~ufffm}d2Aϟrʽ{x6`0DH$288K7{֝¿srα@tlfD*PfF`8Cko\w^{=cOFy$jD1&$;nts|LkGUb@4|X,V]]=447nܐH$FGܜCCC@gΜr\LvCo4}g`WDX,_@`חJկ\.Po|#LT*[SSv ņx<~ΝɕEˋb<p|߀bJD.+}D;}d iXzT>YAr8T*`0:22lZFO `r^{ y4-LkL!\s޽:VvWWWѨR( V,j՚N:e2b2ϿqlnlldX{{{'OH$7oT*$Il1g683fknn`YYY ,pߟNST8LM&0fb8E:~e{{{a~R?nXl6[]]]:vRO?z 敕\Rp:::Z6-D4=s h*<O}}=xKgϞU(0 r׮]UUUhn}k. lVWWo|h|i4Z}}=a^OR,Kד$y]$ƌFcMM.kjjjjj$In`2j58Jҍ LK/}ǩTh4>xðǏC7RWWt:]T@dp8o|ckkkkkСCt:]///x<‹D"L&ST~ᇰ1h* HJ$cn~mNWWW655d@ PSS3??o4ޞZ&uwwRd @e%DyPFO P;K.Xd2p1 1AHTx|2 ;;;zE"l6;;;897nv00 L׷/\٩!IrR%pݳִ v.*BZ b /l6}Ŋ ; Ώ?8B!|$6=77MMMk2^ZSS#~!"u{{[*Y,H؅=ƆD"׿~ĉ۷o8NK/?~|ccի޽{G٩J&sssDQԫ`00ammmZvqqn^~=͂sAMMMP( z7AYX hf766BRwD"333n[V@Wss3%"UmnnnD* J$[,HD"Q( z>ãVPR`?44b_:Oxor BH$ t:Xt:lmm555߿wgdy6338o- Ţ^XxɌb?h4ަp8Dvvv 3Pe6{{{L۷a S___[[]MM N0ZjJ?~h2Fy|lwcX\bׯxC /i0=j0~򓟜;wnvvV*j4P(r(aLF"h^{m{{;~[[\.޽{SSSd2 l0+0LhZǎc0422t: B- -tV+QeQƗ @e<%n߸q|:Y&Kxf3=rz^(*J_05=t:>t% ܹdsss&  [[[I|v(H$`fOdss3p8l=ϧhs/ jzqq`RTJtE p8nE---L& ߼^O$8%kkk/_H$:;;A In(sgg766f2p8 !+mccðl6;??uܹ>vww4z O^z__Ç`{{;EQ7n܀N.,, 455陙#LsΫisss"$I(.H&`:55ѣfP"cVNϟNZ__6$>tUU(\H$?z[o{{W\77cr(|P|3LH tbL@Dh4Bhb6H$tFAD"q:'@i0,v.Qn6=vDPb_|qjj HT__oX@bVkMMʊRFf0?Ŕr980zLD"x3gz{{'&&D"P(qի|=Nv˕L& bbN*|G ׯgςe4H$P"t:ݑ#G})o~Z6X֝bK&J2H>|Noll466vttEQ~!I`G?r\'_pA.wtt杝Tz@ A VVVs6](jkk?~LdCCl OOBH$_7T*5LkT*x߽{ɓ?яyo?~|ff&H|k_3 DzcXSS׾x~:tpommhn(P(4??l6b1-//k4` %AϷ)bP(4L޽{ "ѣGp2Lp~_dY2<<;w>~ȑwbj>|T*q?uݻw{zzBexDp+_իW[[[677CP<?83 YHmwpL$"0@p)RYYYQQz?^QQa4S͆^&Y,ÇLdZ__rsss 400vp|w8pBa߾}FD"ZeppP(: 0yX/]4>>|>)ł(*;9!bd2Z,hfB:!… =e2VVV|>_:o߾rH.m^;wLOOOMMRE$Iһw:d>W_}ٳ###tڵk:.ɀ8 gώݾ}rݻw]^^B cff֭[uuu_ׄB۷oo3}l7 ICCåKz{{As}kkkB=z422x@)ȈVvZggн{WVV[P-B$Ɋ+Wx^Xp8\.K/|޽p8 :fPo;jmm^rbD`~faag?믿p?`08000p!Lž>D$``2x|S_Wr4׿P(T[[zL&555)J ܾ}|޽{@v/_&I_t ^/hmm=v`J8)7***ӧ-k(/ @e<%@V(d2|6 >O$Fr\.P(D"S]]],E"2 666bH$7nd2,y!٩)V[[T*!(HEvBF{{{\NVs8,a(dL& L&miiikk{`niirEq L߼yhhH$544H$N'___b B"Hg2kZvT*miiccc*j~~~eexp $ggg/_ ׯ#G ѣLʕ+'N`XKKKtz/^uX,׾BFGG!0^C8vZݻwo޼ 9n?OH<|04unb ~~~^T ??]^^H$@ Uo@եKJ%}wkkk~?N|gp ,X,">Cٙ嶶pkoojP2UIooogg˗!?K8|׮]j.\ I?4WyRuB4o;w`W_}uoo(Co]_dwQ*l6{||b ܸqp>}*$əF388P(~~M_JظywLVPT*uIBMTjvvÇPl6$H$}HZhoo/݋CCCgww655Ri۽ qYp+++"2(KrPFO b/h4EQt:=r9 0P(lBƋD$IP bIјf.[UU`2"Hooo$ B`8ICCCBrI`0HTWWs\Ntw]\\6A@}nllI(<%& U/B(Lg2  b,S*?Rlnn;HӹbH$r 6-Jn̍D"d 8`6_P}3g΀ FP^/N>J^~e>ÖX>@V755-///.. A$bccp\pСCN/J=\eeZlzs=g4/]tҥ?~h4޾};J?}]ooŹ}{h_JH$7xb}ߍF7oDD"ÇA0L:ꕕjݿ?Ùh |> bWhPP(WWW޸qcuuU*>}ѣE֖T*LQݻ' &4mxx 400p'NsSNB,'JQd*J?CSSǻu֫U,WVV^{۷o;{XP(DQT}}`}},K6x<9^3 dr96=55`0(<$e2\.OR|ȑ#K)2" @e<% P(z7l$I`s\.+ 0w6hZm8~LA888888uaxo4ZPh4Z2zl0߿?BH.X*>p8ѨP(zd""J';˵TWWt\.Px<{{{8gYɵh4Nl;qlDMMMhtss'rooZЀ666޽{wkkP(@ߟH$VVV\.lN|>Fٿ\.r4`\tO>H$*^]]d2kkk"_>s F[]]5 @sflW^l[[[@d o߻wOW^yjşN?g0@ܹs\.+**Z[[z`0`0vwwaF ~!B( `0(h0q|||d||NOOSL&1 ZmXL/o!';L޽{Ç^wff&e&9::]TTTh4SNq8t:m2궷=idddgg(ظkL W_}(`yD"~}: @e%DY\FO X,g}X,vA+HT*&BT\.FzBP*ˁ@@&1@ PQQ`0B~Xh0u-@ v;^/xr$tSSdjr͖dd2N#V+EX tCiZp#hPHՂ iӔJL&ɬhii=|b 8'NxJrcc2B"L&A5o'gyF B(σ=Nn(me2YPmmmm{СbXQQv LphږnRx뙝ZUUU" =B  DRQQ199 [[[e2gjZ;΋_Dd::$D"*$ D"Y^^p8P(LFQCYYYH$rٳgj_׋[NZ#PuuJZYY) ǏonnN/^Zmmm>رcsss iXXX*BRT*__mmmk===>nll@tDD"g2́O?Ł5?B!i6[ZZ޽+9/SSSLܹs JqBdӧO߼ysbb bwȑ9XLOO:ud_ޞT*鐓t:oq7%dlV.s8^zbvDBdoo^:O400022277VA=:::99y޽cǎUTTtttdYR ZyX,699Iwww;"遟JR _\bH䧟~rE"F0fNq'|r!LT*Cj5LfUBN~,D6>i6+++0 Sfkjjb(2t(7e@QY$˗YdX r\U(`f!6X$yȻ]^YG :nٺ)z뭷tzXL&vbtT*f"8>}ZP쵵5+X,0akkkXB[gruuuƍfdd0 oVPqP0OMMJ!1t:H$R7!e۫zf:fbq8H>J0A"fn޼a4ILR|>?K$h4ZYYs-//߼ysll_uŋw~GѼ;~d[WW *++NSQ[[[UUr?~<77aX{{; zN gB ׏7o&恁Ry۷o X YRT*BƙY]]˝'bii{{{'zŋ?~,gggoݺ544Ϟ={s;w~PՉDLnx<=3of4ӛ/Z,4M ߿?jx㍕8/s8SNUWW?xvWVVVUU}Gѣj:JA(J8 #Htuu) ^o0~$ \Gl:{ߘL&_xH(ǏA/B( rU*ν{tD>~tt,fCB 8pZ0Da|6r'OjZXY@eQƗ 2"IЈX, :x HKBNXl6 T*"immMSSSj*SNN<@ `bRUWWL&#yPHӃDFX:nooO($977v3:v^YY 555eccѣlμc>yd Nd^Nc6O"8 lv(*ns1TfN&B6u!|`ww>|ȑd2955z{{8pKu /_<33c6B555Vb2޳l/2GGGAzfoݺt:ڞ{D"100 `K3??OА HLi4ZeeeX,h`0/?vttx/W{{caaazz… `gaw߁֙F9r$Q"ܹs G޽{7LyBz._J2loo3d2)H"=jhhD".H766VWWJePn݊D"kkk[[[###@ #?zδv/^V]]]Fj͖H$l6FX,l6~LWWwp8|񮮮,//755 D"133366\}{{ѣ?DuuugllL.I5ZBᥗ^kkkO aZ],1+7eQƗ 2O>rd4m\.D $I& !TBMk4*)x[[[drtt[[[ `r##Huuu,[[[[\\ BXT*H4==hss36LMMM/`a2>*'ʝUSS#˪vԔFk<g0A\tiggȑ#o FQ.r rÁjJBÐe0***B!a8.m6N_[[mMMP|C{^VD?~Ri p8㗏?X)ʱ1ϧT*!PJVCu\.wQط,--Rt~~ꊊsq8?prrÍ\.ѣGA>ީ7z{{/_ <>n@p ?SA`Y,'''ggg_}խ'O>zhcc77aSt:vǟy晊#GT&t: y`0@( qpKR}gE"$<V=qh_XXxQP5 /JRfyuuU HRG1BKd2?;;fyΖ6=z(~hgggY,ֱc(2\bx<0 z7ZZZ0 fcc#WVV )r\"(NWTTQUn(/"2xJ<}4d' H$L&fSx@\@Xdx\TjՊRT<OѰX,0OEmnnc&Jo޼):ZG"g2^h4L&6-vvv***(ŝ#8X `kE\~~{ssspp̙37o޴Z\[[KD'rjkknL&p8! 骪C9[n*X,կ~U,G")044c/bXѬy%ꙙPxᅅT*fAr`0p?v؛o:t rq î\R]]=::`Zp8666@"6Vӟj4H$H$"Vuu/b>裑$9d#GdYطTWW $I裏zzz:;;oܸAdnko}[oT*q/\]]khhv5QB5LC+C"0mD" A|t:5ѣGyhTG}Vl|U*bI& t:}```kk9˗a[hp8(*FRfF+S\.tL&svv6 tVPw!:cRIz}$sN}}}[[@(J$;;;Gݷo _~_G>~xccbtl6+_jPĉrXeByPFO \G]zR B,r!.+@l yyϐL&Dպnl6]]]ׯ__]]u:4mwwչ\gbb"fYHP(DnwGGǾ}VbH$"r\45ldFjaqqqoob!jkkbs^"{~~>UTTq\0NSs\.h"(@I=;;i2\p$p8 B0N=zHT666 Y>vhmm]\\L$'NsO1<:jZy<zt:=<<655U(hzzP(@s}[x<7o޼uܿFn׾a燇ŋ ޞ^__X,RرcL&''Y,֑#Gf㩭u\l+JLfVVVV+\zU.Ξ9s`0\|\vM, *Xeݾ 0F RB0~?ɜ )Z ap8bhX._, F#Cf.6U$!i "rD"FRiOOJ>[:55 B!dz, j:x R!tnNrPFe|Pn(X, K.ArNs-Aݐ`H$xd2ѱ 7駕+++0Zld2$l?*_V Bpzr8ߥba0XL•h'< J9o[,Juuw!Cd}}=Id+H@:lJe&z`0N pT*۽N `0A@\h~N솆ill\^^@<TtͭRV{ʕT*e0ܹ|F355b"cJ}ʓ'ORleel6߾}ٳ>l6@dvwwy01A[oe6rVW(޽n0Ο?}݆'O&|>f?~L7~2z;;;ĄL&cǎ8qbii q&b2nFjmm ?6D0h?qだ N,If-8x PjBjrGٷo_2Ż7o$"677WWWC\(V\.?|}}u`$I>|8 ªӧO###~3kkkrʕ+3###J<a`P&uww8. +֭[t:}{{.͂͆f:^QQQQQ!t:`'[444]hh9H$r\f9H-s\NS*RݝX,݈H$R(D"phok'g}}]$1Lc"qo|oo/χY T*RV{zz QF_*2xJ0"'D"Q03a<JbD5E%JIoMJUQQa[> QթTJVG"I+\~H$r5ǓN ?JP׷ZvxxX&7n(A\xUUmݻwݻw/655Au~ĉgN/--AJ\.gٟ~i2h\$T 4 h4$I:ÑfA a6VcWSTww"I===OSښ^|~||ƍ]]]@g2|Btf28\PHRb1*YYv69T*t띜d2=㳳4ѣTa033\{zzݻ799 6PrP(ept@X,...QSS!xjj 0ncѣGy<aN3Rh}}_W 8yDFO cvvpEG2gff&sH$BQz*2 ZYY!BD"؀rnG"FCAD"LRnhD"fs*r\%'+Jt: |&G|>&L&A0|>o4Aq=ϡC*++F6d2 555ëJ2@?VUU%#G*QF_*2xJt.裏BLq $y<%BdX,B.c1\.W(l6[.,Bfn B@6"hhhb1k׀gJy`EGѺ: \ǵZ-!Id2ݽ{w߾}"D5JեRy^_[[ fgg|^jWWW9z=88ꪫY^(:[}ll}}}Bhyy(+?0Tm[]]QTgϞP4h4:UDb*CX^^H$tBX,ꈝp;rA,kmmnttV(ܹc4 2 AX,&k,Keeǁx)BfN7͠KW08\l6xVwSBO) [fG˙^[[fggB!tDVU"y0T՝GeٰGTպ wD"Dat8p( :h4  8T*M$9t:],Ţ@ X R >2b1 V2V+A?ߟ<y)]z`X,B&|>ڲX,Bb<OSSrPFe|Pn()!Cpe՚J V,{H$8O($I |\EAJZ[[kkk޽rr~tZ,Ʊ1e0,LCCC F\__OD}}==z$ICCJe4p8<fX,.f[[[&&&A5?==fe2a555~_V=񖗗~& կ~x|kkj:t:'$ - [b1r}_x \ʓR|@( n{ooO.Fhl6 ER\{!r</{DD:(J"@G$bquuP(|? *jkk+H5 o|>\طoNZ'|90浵bl.]zmmmggx<Fz{{;;; ?Νj`eSSS嚛\.L X,KKK@8\#H8h4 4sg2TJdXPs8& pX=4L9P$ LSEQ0Ň }2B0T ZJa o L,#dL&Ţh6e>}>_&/u:=tPmm-x<\.ӽr46epeeC2H.\X[[dt:za* e2Y>xX rqhJRD"aX~{{TfӁ@@/ |>>NAHjgGg,J*jvvp`npWtRssH$ZkkkgϞb?f@ `Xr@ C-,,uuu>}zrrGE]tIP$ySWW'==-l/(Rp_1͙Lj644@IJX,njjZYYRqԩT*BBOOOGG8gggu:_0 kmmr2o{Lfju]]l)>_YY t,^ BRT{qq!\l6va2sBd2xd2%IIMKQD"Ő`4R4M2FDX,adNTl6<onn矏F;;;< 4l6{uu5LGQ&y…BT*x 6 >L$/Bڵk(fi 3 (!,҆$I8\.vPy(a@Q,X lSv_]f %p'ph` Er~_ $ﷵs8@bprF"pRT@m:pQF_*2xJrxv;d2|>h4f2 ò,BrbVE.@ cĆ=EX.bxdBMMx,ND"XH$͋~6:: ,sss}!RiZ^X,nnnx6m߾}w܁bx RN[,KbhЂYP>|ضg3$Cuww߼yt:t(@ fRFr9˵$t:jOOF{\.___ً"4ck0וJ@ 8.JaLr$777rH$xH|>?T* ,ƒ Nh4"I֑H$z l6P(V3LCCښh48qLQw]v??r:׮]Y6{UU^|t9]bazz]p֭[p<3$lnnDb8Ǡ䊋"(F @L@ @c4p8C/ fYp h4'p:>I$I~: HD$IЙ\F yX,h###}֭P(tԩׯ_z NΜ9s…gBQn(/ @e<%@ fϞ=k>|>(MZ- 5q\,ccH c2  lv8%"8#$ Fz"B(d$^/gX CY. +;::t:l6[CCç~b4Ng>v:t:BQ__/Nqe-l[V7>JAY$tN3555  xł***,8Ӄ 1 7PƁ+(AZ ~D"f$!h4Px|xԩTX.9rŒP(˓^pҥK{{{ALMMuvvf٭-PiO$X,X@c3~8p Ae=P?yBRwa|(_#)! )!) C 0e1 %nnnB7,l6[]]=66f6Zv8@$IDR]]=88~}UQFe @e<=+_ vJ]];˅aBFaNj"pa ,F)J$nK$rt /[bN!"7'I>o)ZUUUVU iRY x<D Ccc#2Lf Jj`Z+**0\ 5X,dfwooO$ P(DQԉ'X,'|wvv$4NgΜY\\F,tr\5<}KK_OeQƿ~e0F!FlvP,Pa ދ "Œb!dBa2h4l6pHl6`0Z bxbPBQ %p8bt7`@m9joV2dٻl6Jj:񶶶AN'`Pd@ h6-LMMΝ;|NP('''*++/_l09p666D")Dl6Kӡy(88&32'w @ nx$ ~B!(L{{{0ڴTXRI %>ԯ'$Y@} Wi#`F}[!4nl3fgΜ~lv.;vX.?  EGGNj/x斖v p8 npz8`r/Af! Tq8hA&>ol544%1 60 8a") !(N00T(A2zH$|>@୷ު &dJ$TZWWWjE~_9eQF @e<%P(/JI`IFb` `~P(v;"8ɰd2JBPs8D"Aъb,uAXD,mBa4!D|>?C(ut:rD% ÄB!̭cT:xuuJe.[XXrl֭[6 !T]]H$V+t:}YR !7::o/e{{vx8@r eԗ+N ?V ppJ9Lna0 $Ir98,"L&<PK 6І qX@ eiTUU]revvjddwީ??޿?FZ\\lnn޿Gl6ADY]+ \neee:< _#p$=hB~?Ͷ!`LRJo(2(7eP, Q,?{l6\ǫ+CB#H,CIH$, K_8 A9Ql 0Mº:t: Fr9D(B`RDٚn2JPX,~!P(FcKKntLfMM|cc#D"i:>77p84ojj|&i`` ZDLrTQQP(R~3o|㠠$,//? hI.5N9$Ku?ԠrJci([acP:7Lfs 4&ܰ (J>c@mqN~l`h5L,6m||? h񁁁T*DFFF E0zj(lDq Dh)tR,q S_Ɠǜ""YП}-|)BaY y ð^z?9bȑ#еb-r^~宮??}0",L l6 Z^PA /Pr9@H$p\n0DԸ, ldb*3]2 b1ɴX,L&S(F"(5 BX."|> bL&IT{FjΒR4;NRz!ʀ •HP*N,wdA\~.@p L455!ۭP(Ξ= l`0f=JQy<ΎZnllz*ʔ!l6͖dP#~A^C>;7+_SKc҉|s[/aJSp'p% K7+=t 5 on?p8BdL&(Aֶd0nݺ E"Q8R  4/t/OQ$ }ړG BQJB S&+Linn~wXO?dBI?~\.PFF??AGWn(/A`eP2/7=P(LR2X&lJdBPXT(7E?EQT H&D"< KdRm@,J!sn: :4n(dH:$xAkB!N P+ ^o444 @ @QH$Rj*nx<!Lzl6rd67 . e2Bhuu5 9ZZZfSSS۷- *-I$BH|X(!o+˥i!7|a-]'"O7瀮@kAxk@rjkke2wb"&^~epЉd2ph4Ţ\.W L&g'p cbIID!D*|2#𿩼aN"D!#QiYVH$Tq\:fwvvӧo߾ӟ!R ɓ [[[ϟh4---yQn(/ @e_6F"@@d&)@_/8l JyJ,0BxnP@*q|--- E;;;$IF"('n,+UUUr<L-\~i 96T*SgqHcjEQ~-:cǎxݵ5PKi9[E0#`j0a8N#p"1a/ RPģ'l# w_ߛRKU2$p&:A#pp`@ ҅R.NGD">>|x}}}{{OOOR~V6Nw:w BHh4CBp;+Bl6p9PfyJB!JU,^R6j\R%I$}>y \I~A+ l `X0P^]c0x(H q }&ɆLJr``L5kkkv;8EnllBUZB':Ω)͖dN'0.]) ;Lhr ۝H$zȵk&''y<dh4(00}~rH$E" ! !G_xKwU1-siB8pc (_v4Z88VH2@vp"IR$cP,{7ܽ{G?Q(h4C4G}h666N:e26779<1YxB7 zJ}"t!X#J6A`X,P( Bb? B0S;N/X.CKyRǏ[[[w8ȑ#x|rr@l`X"|>zU*jP;aZ㍍'Nx^+**`&ٷo_2,kooxggD"yH$d4dY/Qw4$'fa8F$Q%>OV/yh7+ѐ?8cOnlf;~d?# 顡! :;;].T*}g7771 [\\t8|fhj5f2XdY8lL&,c\.$7 54N&͖J4H%Db%轈!!Cd ! C$q8'.P(yJ~36{{X,ih>8p:d QFe|Pn()l@BVHyT*6>t:p4J|KU>.f&<>T 0BdN\H$11dh'|>d2 lGܹٽ=Fng=h4Z2rR?PwuuA Y,r:ѣGT;B!( `0b/N߼y3k@ J *ºt IDD$FQIaB6I/ )0UJ,UON%/CWMt8@O x|yyy||ܹsJrccx<{I^_b~~UUU:npp0L^tI,+ ٜNn78u&\.!J!`D+J}X,cl2)Z 6=Dx rOC©bN+)IB]\Z0ER}CN@ءCZu X,4|8#` 0T(F,5X,JD38P __ DQb$ E3@OR r9\ س!$ 󋋋 O~](._bzPXH$bB(L'i&:.J)T*vt ,˾}'NP(p 5Ѯ$H!t%F#LQPRl @“b=\.* bAFQЍF!C޸R3nfOl5tjvVk6u+++|>7|}A{pbŋCP}}}8.lmmmNdFFFnݺUUUpoooַu;w>}:݋/2 \Jz0lF8_\ 0FI¥n>%p4(-3Ӆb18/?{&ammmeiiѣlVVWSFeE]()Dn7 Cjoee%jHK}) Xx0a1x-J~?=ρ:::\n"xK$MMM/_fX6P*Q7A! ! +O bfB@Gq9`px\^sSJ"XYY~ѳ>Kvuu9ÇUWWap*2RQSaP/--mmm1L( 0bB!6],`EQ#4Je2T*0 x,d2r# Y,=C |kv` T"Xf aN&rlFfC<:ԯlv߾}jll42,onn t:t:5 hX,D"oww7Y$#FI"(3@>66V__o677E"j%rFCPd)=zt``b8,`YЗR6sIB#=?mҊ TiDw[Sls/~\.W[[KјL3<կ~p!mAUUUW;;; TrFuvvBl6LfccP(>/B|mL&njjyfEEhz%4@9@QB_#pDQ,IH:Y`D:Y?npRĦ3 aKˡ`bs^SS0p驩)d9 e2ojjUQFe|2qʕ  =b͓dFQނOR8#5 1٬ݕuj%Ww J!c0NLAbt:8PɬX,f̙3@p/ >Fwu:`f\.NGGlv4up\pSSL&vX B7 x\^5$֋bq@, F J0"Igk!1RŢH(Pkٖ_|qggR:.Hnf{J+2((7eq֭e0L&)D"XŁ@|CPUUU>J0~p.&IQT,x e2(0pl6& ÁDx<$H@ d2h4DfKR)$hðo~E9vł0d2Ud^R9"rt:]]]mXB[YY f[ZZFFFpGGᨯ,Mp~MH)9$E)]s}NDsu g1 H$B0 pa000!D80 /]“*p\t޽{W*"(x&)iZgX$@ 7}}}!qDh4loo=zۚp(Z]]$INLL$ÇIROR{@ P(p*X,PB:|>WԋK8t:^? ѣGb>HPT*,ǫDuuuRڵkL$ꖖG$988g(桧 zŽ(Q6^;6zDLjJVA!FOL|β?DŽ4A1YEnͱHT$I${e2p8\(Ο?÷~{hhC<| E _WNeQQn(i֭[kkk0ofX`Tl6x`p !Q[b2$IB % B`Ql`Z ~}4LyX JheY6r4Ą(B Bƍ{{{$I+W& 57EQ  {m@[]X]]=88HD4miirmmm[[[BH$|>)BhX]]d2H$$`0/ ~@yY'cLRdk/R̢{mK$V$I]_K9(bqbbvfXuu޵kjkypjnn6W^ L&N P(ǣhCCsx<Lt:rGUUDuu5knn>|e2h ~?po"pb& !X,T* -b[[A^W*|>NGQT( ^jww7|>ۍ8^%Bpڵk qzzp\.QrbAÓkx܈V#/\`I 4Dix'0YSD?gsE<O2lx\$x<|OLLmooonnX,Je0r\CChUQFe|2*Ǐ?~ L,S x~`( ȵN0 VA6 |UW@ 3HX ,p'xa0 ˜$ ti":P$~TAZcss =U*USS˅. m{0gfG1 pxppѣGhl6߽{!$@fajKD<_YYrhF&C[[[`~ZV,eW-A)˜X>SUHTlW0"80(Aa88ϦԿAOIdgffbX24 <ݻw!{DX}޿,3B\.W__D@ڻz|N㰁H$rCCC.]含@D0-<,jR)Pf::f- A;E.yBF |R 4t8GQ# ݾ}h4H쵒n  V z$9tF^-dLf6J\:5N.ehJщ"0ǰ?P__ظZS]sH|V5 $\.XyPFe| Qn(㩂" p t:]$?W"d2Y4O$2 t`H$C$XZ @DZF+S /8 !p)F#r\((/>ӧOvxJ0bmmm\X,$i.L&U^_,M&DLh4RE@I$'Or}=h4~јfә4^(@AAA5zCVⲜçD8*INF8I`I3 YHKqm\xr-K4]]]E͛7yр'pi{-n4j'lU,zHK4ZIDZN0! vrhhŋ;;;ra|||uu6td2 l6+ןGaeQFO2>3%Ça BY|>PB{ a 6FQJX,O  `^"H@60ܿ\.D"`ܞa `c3rCʒ%%$rcX,}lh4&ݻd2 X D"qСL&֖dVVV CMMZXX(FTJNt:\nEEEmm-`0 BeXp8Tjxxxff,--Y,X@#0Iõ]p:)S+W|.wO3\&KB6^&D>,t$/ 2NyTp8 0,Lf={NONMB .^ ?Rg{{;6͉D"Hz(Vf٬Jd2`A ~f:ntBj;;;fe-8N$bv vDH$%d2 78$r9P@H$Ѩf<@(@,s8tرc4SNݿ󵴴lyQRC@>}$Ibt"Nq % ZFK҉,bt!4B8FϪ|)D0*_it'BP6  &cxx8FH$Rd2 [D"!H8IИmoo' :аy<^0j0 źD"qPs8H$J,_~ V|>tdp8 |}`A&a`d.loJ łk8`_ _ZW C3fm6[cc/^첮..{T x`mxC1gr$'aVU,R4h8Bg!Ag@]WW>~$v+r7nx`0866p8nݺߟH$v3g WSFeEOJ/2B0\x<źL&r)~uuu&q\bxXh4 >!XȒbt:]QQ~T* D",V& H$aP,IFIR#q`BrDBզR;;;ݽ[(F#8~>b &NXBp`0T*Z[[ ]nWuuF|\.FF1Htޞ嶶=zb800{)J6}6 Rl? ͥwoD8aLQ!DRd$Bl!Da$!S/R,(RX9qXlnnr׮]p8annNT8qѣG+++mmm=;& 2$?h4@7a>,*1=i5L3EtSr}),K x<)<\:1l,|>/N'P8aC% x, p`0N#FßtKhdD¡pX(l(L{Tr#nF$AI)eYȾx=ߝ,[3lKN[,(R9ݍα+p(fƿHuYZPP]}7 pl c`!DÄ!8#NN/0G+W455m߾v_tҥK>}Ο?/oڴi͋NSӵ~@*>in?=OTi%j3~( gl6 ^(k O`D`l q X,^?`E cP(`!$YBv!fñ$IÇgzzP(tvv^v-e\f^sA۳sN{k׮ Ο۳gOTN&gN>#H ٳ6l?oOLL455?-J(r+J \_A {:#C%K]ycyA qD%(a!D0G2ad0(Yሮq0!jC2nڰa~(GGG+JJu%>T%ZA Zgxk1L1C&Pb h\Ժ+Kdۡw8T*15-*bE2-= zRjP/A)BHp<'$IMMM|I2;;Rx?y2}cal]ۀ" B޽ݻ`0NNNof__W͛7 /DVWW~*>i6UT)Y#5=E=D2jkkWWWu]3ipl6eXRx8n%o{3gN'UUmmm 'Nl9͛7oݺ%IR4bP755|f3i_WzYVoofffz{{}ׯ;ή.J烒QE(+tZkwx=#qLbIU8QEk}:_L1BE!M׈f%*W /iP uvv(~RH>\J<Vdd2AJ.P+X$Yz@ F6- )R,a 0ULfGUQ# "NwB2}N 4]‹jn/;vΝ;.<;oKw" cH>|0MMMuuu^v3Ϭ{Hgя~T(dY޶m^}1_y@ Wyc/W$1RL$Hf)T A)X Bҁ˲F?]PW @ႂ_y cr!|竫X,?O޽R555ϟ饗8ԧ>ׯ[{੢*96UTL)u\(Z,Rl0A辡~ssdJb!N5#B0wOOUU+n)B.H/۝A qc\pUb`cp8 <m6[4!4M[XX(ˍ BoްaO~T*&DQFX `6:::zϷo`T{nArrrrnnqzzZӴC=ǎopsg ðX,72Yr>h &RksufTUf C4 2!0/!̐͂J:1!2f{gffommܽ{_"Cx=O}TV5!>te A,AL 7U6ɺl[JQ6UTAN%I$iaaL& lx<<<]Ƃ xt|B.X郴S=q0F weZy?@)0pu:tuqxf% Y׏;ooddac;#wfp8SSS(?>o޼7o޼qF,۸qcOOj8n~~c<66VWWsNKӷnڵko+_֭[O~]]~ ScCbda1xbb0 O0b!0E0FlQv8]]]###p@ wjB@bCE|vJA+|$@@,E#oVlv't7a)cp}A}2-l2~˕N@qĉT*s]~}yy8łjxsnUTQE4T*mVjЊ\@$ B .xBi0$QcH$x(J\.Hhz!El63`P1jryuuNB@BPI$󵶶&3g,,,;d102F}}=W\ڳgAihP(BrD/4dzWVV ø~ V=wܹx\KKK]]۷?H1BiÌBTb0(! (|*c1f>??gg{}gggM&䤦i My^Jiv?z}vχG" PBЖ2 FT~FyeA9X.#!$r,ݴi^x̙3QJv{]]]MMjsm4/ "\.Z* RBNU<3??L&߿sΓ'O'/~S'Nطojȩ* @U|<0LRtAq\8Zn[$K477nZaEu XȲNR LA@Nj!`PP@b9 4 Ad` p8p8on۶mqq f)H$|8b@Ν;V2^z%Cz`8155 |[[lzW|>ߖ-[bݻwhSStaa_NZ h20Yg}!#Q!cBhiyabbF8~vAP!dRZ*lBP Ԡ@p s%Pè~!d =0nZ!9ȕCC$I`8Q Ce :}8!(cb́- -ZQo۳ad2Y$4 cG) +5E%RA4,ic֭T1lۺmbr"\.q.kӦMq"سg$I 333O_j_EU|Qm!$9Na555bQr Od4X,n" K777 ~6o4L---\cr!EQ8f$544B6d 9Ġ{P'bX__Ŧ;::VXooaP655P0cfȖ|(H󅽄ひRQȟz(===|,җrp8gϞAI.\k-..>{w#H$2@ e.>X +L[/T'QR J1B5]655m޼ڲeoo=z7nܨ`l.I, T6myyV5z1M `[0tpQ! fP(/V.laB ~PE3Ix@SU$U׭6k"S5/r|X4b1&a:$X(3YWŢ(ssslv/իWm6(裐Zmmٳg?7n?~zzZ|/GEUU@UTQ'*>:00*@v= fF-8XhNA'Jb8;; M7=ЊA$2Ԕ~ŒN$0| TX,fc)BA1rQiiiq\۶mzok׮@ pQ4fA 200冇[ZZdww7p!#g`JxEQ__Y~WWם;w!رcPݻw/߾}{۶m۶m|ŋf._<55TU86?h$4zp!D,jEQJe, t]S@&ihh8|ߥR)TB V~:\.}}~d2r@!d5Qy1~n(px#d2ˢ(ʲ$$aL5AXƿ !à@T3LP߿qrrҥKB> A&fknn'"IҦM?* (t: 믙 ʼn z8ef |;Do޼yeeooBO=Իz:;;?O*>cX'N>d^oXZ\ !5L&x vX466Be ^EX@<=;N]׳٬nST*:~(0wt 2X\.ecccss D"JAf3 ceedd2oZGz<0񬫫{dz,(]~;|vO?}ƍ˗/gSN>|8U(S*ϊ$K >(3L !ݻ\ZZxG$I:uJ>BOTn0 YcN!#de0n .~`06I&j,fWM=h_9s&)J7h֖֦:xdN?Y,' i 8 .hYST>$'4L c X[[[ZZnnJ#G@ȑT* kkk=zse300x:;;!hb\ رcdbh73VUTQDžjPE0 ѣG|\;@7 #NC(SUU ^tA/ϗNPyjZaPGGl6 .i(;BlPpD"_TaD!xhh̙3===;vvƍ.\ [ZZ灁 -555RbÆ p8<555??L&=;w2L\N&TV l6777===CCCmmmPΝ;ː@(nx|f/Y3:N?33Դӓ'O꺮j6 }QB44N333\n7A TqA!l2j#0ij2I|Vw l]o@)/\.C!Ky1~챭[j& n ʨa蚡A1=<޻9RUUuMiVZPzk׮}k_f|IY{m۶\a#L>䓰EXC8p`rrmϞ=ַ힞Vȑ#w~ Boo۷)cccT*PJ w¯j0EfeYtС`0qTQEU|U PU|Do DD"z${B@[^00R>@mCnk()i%%bK̛m6[X@` r9^c x~fffrNs###^(Ba||RWW2TC8ckrr2nڴ r._/~c|ĉa2| 6;e2x<#U ?E%IX,IШf> ʊM + ~XZu}yy9>o^zj*kjjB(B$Z͛{nww޽{7ךWQEU|46UTP!ĕBaQjE&*lB^oPH$nbPJ^R0T/A2dY]]uݥR)H?`P3;`@p8n8H:;w>uT,Lڵk! 2bx-PZΝ;t㸕x<!---6lccc/^lkk|i[[H$۷MMMׯ_O&n%JFEv6Y?TC pp<֫L*JlpDu`QpGG,.\x??TjW 5(52_"$ B6}w~#oE0(e9H*<^9u~jB|ccSYxY_=?2rcw#l6nFBwׯ_/B 0 xyy^;ub}}}WWW,4-MNNl-[@q\4zr֭[~߾q(?ƍ].WCCÅ fgg9xb}}}OO۷ B Zp!uf Ih>~nU"ՠPPzP ]Yss#G=gs: T͆a# /}ZGGlώ/\2C2̐B<&!S0&թAApG>=c0t셋زnHJG#zMQB%UA.ݧ0!(mٳg7nƯojey˖-hƍ;R֭[f೹\'Nlټd2NzkkkSSSf+Lb1299Zv %T*vƶn*bd2׿u377, ?u*~ @U|ta0l\.>1555\reY`݀$l6[VUU!(σ1(bɤv>B3iBHCC B2TXI硂,CecX!fX,B`0([<O{{[rggg$YZZjnnF"&0._jmm]XX( mmm7od2?x}}k@ ޾{7WVV6m&0x cccc~p8|ARD"1::z#G ~Tb ~nQ)ߡyR_3NgT2BQUsns=7;;[lE1H| _xߩSBzAxÇwѱyfEQfffBc555?0x~~~ӦMocbXCCwM$uww\.4 iep<___d 텐VPGڳ|+n; YSŞ~#GXօCm۶RjZvF#ȃ,+joă RF !f%]쳟۽cWPIZ\ZtX, <b::e(( GeQf:Pl6 4'A8EQ,fk"hy qF3S3V QJ0&SEUTaT*H1ïH2bp8H$i8l@4bc(@{r@UE1N+fܯ(&u]/  !@܇HW0pTU|>e4Y  G$[QSP&ijjڶm[*fCCC&) le  MӀuS*veo߾D].׏~YŖnϹ{n.;r䈪=XTw8> gΜimm$iVE_PO)M$N]]]vbUX,_zݻwCj({챍7bGGG ޽{M&/k׮JRJJx8JDg+`1Ƙ $ ~wxIjaaϝ3&0B6:fcl@jf$h@*p`2٬ܴITsC7̜03?}OdTQea 3 >;#wTEe1D/;v...)\Xl.$յՆzkZKR$yd2s{(h~U2L}}=8ގM288bȈl_H$xjPEU|Qm#BE=FO8\jr0Ρ,VʅBdJ Sx>J0Bva-ӊ(MdXTUvh6,ePJ%A2hsKZ@d23xAZVD0 vl?ڦ vJ`Ioذaxxxdd1644qݻw7l===}ƍ۷ P(466vvvܹsxx.]ZZZb/R>lmk5ɦ\宮B:;;!b~;v !l3LT:yd&zv:NPGGG8^\\, ,RӧOCZ6}!v߼y3޹sgaaȑ#wygyyyaaaΝ?|ookCI}b:_P֟@BHe'_SU@8~z*N 2pt0 !! ++ AV o]8JP 1gV%1HE QQ.†c"j6 DQՕJ%FYY);VMMM,D"d&BPSScly%(j8yUD_,(hFggg(XHxߴiӅ `SS˗^tgg'(6mܲej|2p_tgq8gOUTQŇQmToPB(L@PBB-N5`sP@2kH=0*0~4Q2(G"߿UU]]]=v @BT*f2_vMh4f3Tت677D"aٺwz@8]]]-JEٳn޽{oF]]ݖ-[AXZZZ&#IDAT\\L&O\r޽{T L!;;;EXT*ӽ{D"qƍ[nMMM-,,ȲO,//~ӟ޷oiCCC6m`׮]{j^zudd CihL2 Q+fAOe &IJg2mo:CG(# Ǔ7e8BȺ~0mBX.9BtMj> A#C #QF6Aʨ/&ⱿfQ an:˜2F0``6mۖNv۞={? 7n866&vp$¸\*1VllE)3++kc/}KMMMO?4$aI۽wl6 |>pz{{~ةSdY޷oߣ>v7nv?*TQGh"ȫ ~ [*d25tWWWy $IAN =C 2D"p8*x(9u ˜aTR)tA J1t }9+(t]= =ƺsK%dڹ}0 x㍛׮[-Vxg!8~FdDA(]EQԒl6i:1q/bQJC=d6c`'SUX,ŊR6b>c\.J&R*g3BP*5]8^X4YWWLӴX,f6?ϥR .B naaƍ60#G\.SEUTaT*PiN> LsrOO쪪&r 62(^͡PR*IR:6ͪ 0񄥁(r9\.á*pf3Ї bd2UU+m w< ȃV8R411/}K.6m C:\t(۷od2X \vCΜ91Z###kdN1{01cY.JPFe@W+R@b1)`>1FX,vC(Ȝf. "c` CQ>sݼyT<Ѻ{ob(a߿<*>QڀVQǀD"U]E _xQ҂'!iS+ŋV8PdB\.W>~J%qтf@|>37 2:TUu: S!566FٵkW__fD"mmmPh˖-vfǏ߿6]]]miiAxMMcz…zgyW@l6/xOJUR.>UJas# }*Baw;( caVyM867!X@p3>Ul6Z$rՇ>2[^^ CP@8"  XD!Wr zӦM.]:~8 |>_mm `L&f LIyhW Je`B)aU(Sٳg!ݻ>4[n}].Um@O*Pk00l6K1Ld$I윛f~3G:f=z4544ȲH$` I dy,]]g288(A 7 <2fp8t]|aرc\^-pg6d3ɲ ̥P(H$Yb C@q_W~۷u]Ym$t:??l6(kjj$Izc=cQJ\.,+++wq555lujjjqq1rb8111;;m۶d2yD"ϝ;Iv#\h%dT"X]]! T*ݻ7$߿nnNOӵSSS===O>f|r29mf,˰ T64- gXĮݻ>x= o 9a=uꔡ TкqlvЩA9( T?@T>pۃ( yYS*ifW5B<Pgwx>>>>;;r o&za(3c0J"4]l6JөL:L&cx,ORT21{ؘf!kqqW_:@Jz-ЁAz!IRP(˲,jAhvؑd//ʲ MMM~iij֭[GGGEQlkk0GIi\. `0yׯCB8Jx:EQٳg7mt!`%ElڴixxjՁg(h8f"TE)ˁ@駟x<--Eqfffyy4333ugyX,&K.;vlff\.OOO///777ܹpݻ Cm6clppjZ,8ɉD"C.l(2.d2ݾ{gjf:L>3(olt{=_zulh4/+ʙgv,W^M$333PHU{ CF+ Qy H/Ð0 D09Qu]gˉ1ƄC 90D3tݽg59LOBNj*1JBɺRY0I !Bgcf!˜a0n$/r v`B #*_0(t\. ݾ};JJ&I4Da ЌB/r\6I҆ar9MѕZVKrIEDu("Rx]$Qݰc|e\.W8&͛٬ٿ `e2@._OW5UTQ'*>:@_z% l6'I_l6[¬=lݺrݹsgddDuכf}B>l޼ O)jk !MlBPq* &hll盛!W#Ga۝dVVVEY^^ްa㙙8nn2 8t֭6m ###.]2Lgyy9X,蠶lR(jkk?/B*O iNR:66r|5UU!MU7nPJx&&'d&y~nn.;`0p8$I sssaLLLr6kMM-KfGFF.bnim- nX\Xx?Bf+ NR T닢 xePix㾨{t!P3'xc^aB8 !̩3RFFa~G8Ɛn8#|+W>CYmL&QxQRa`$JeEeQ(f#^cuX*28,*BYQBbQ2:::fff"6Ց;vDbͥR̙3MMMg@PEUTBVt"x7avis)0TN&>/ P$IӠ兆rvN\( $(,#(b٬fErByiB3AMMMBT*eٹ9ͦ*Dr98NP ,>offfrrƍ\[[ RX,6;;x>?<;w?c|ڵ{D">1`.7B<766N)rln%ӊ:N](n457mݺuppnSNÇbT*!VIvv5@J)&XRL)_5WVxB& =c!Bp.;s,!3(a2Zs8L&(yAS1h`GA =A !@ \.pÿ/8t:v;Ngwww(ڷ[!l&vI-x^!#3FYIUF SG"WTvSB) ܶ A%fMN>M)ݹsb ,B!jkk7o a3LKKKsmmm{xJ*>ic0K+zH뫫tn4 (` L&c2FGGKb%>ݻ۷ow\ܼq3^;vFNseeg?Bme˖W^ye``l6ްa$I iE1*qj g8l'Ȗuv$ 1&b28xp( ]Os?~ӟ .3WrE4?p:^\\L$;v8w\cc#=C+++gΜy=pjjj6lؐdvFWQEU|6UT1"f3yCTz6iRJm6X:JlL&K)ފlIbN>y\.CUP7"o;vÑdVWW^R>\.W*&&& BڵkH`f aB**|<>aVMMMQJݻW(:Bn{vvvppP]vݼyѣG_}۷/K/ڎ9冇KȈa@/KYEQ$ z !s< \12F DQ$x,sd2[]]u:[lu*Ţ-J+#ۇ(ā'nC#`Lj[o DV/ R ]ii!X%C{UUEI)U{5M!(3_%"n;FQFUU5-bݯ|+o+W&'& fkcnclk޾m{woÏKXWG.j$P͛`Ê  X,ݻwn---h1J|꿊*TQ)> SBp8aaZaxf2$UV`&fP)B`,;vԤb yb0;w`&(: ^vŐlj\.CsNoaQ~:>VWW֬Vkkkk$D"hjBO|>m6C暚VWW۷޽ܜ~~zxxxaaa``G?ӶmۀwNG,5A9`"dYX,S 5͐~:0b\ss)aje.1Q(޾}{yy~?裥R//+^+p3 A  oxƘn<kjC1.<2JE0DA\C7tp"zAؓ9SB8k:(_W5B }5t !%8Hng=m^7Lis?i-A^OxItx-Y}{vVwg|~$ppA)ݵk/܌UeӦM333a\.qSS!ҥK ccc |_~P\EUT A`$"fK&!`[D"155vaЛs\%4@fMMM9s n]s̃NUUU xl6W-2 @{ _^իN.Y[[{ꩧ$IVZMM(+++҈5u]3LݠuX,Fl6 K/dFssjݻw,Gmhh4 lCn7X mذ7 Bnllx< o(<ۏ= ܕUUԸa`fEE!(  Tu^TME P,-vZ:. IR$ O:Z)/%ЀUts1cPqu>]5(03>@Kq`0l6h?ϛ*TQGL05(0x1lv||zW*꘮Ţ$57rG;yf櫫jz͗l6\~{cO=T*bbի$ܺu !t:!`G}.*Qm#IPA0m6[SSS2-d\x<(}0_Ţ`NxB CL&s`0 әtPH&VVHq c'mӧEQE1LL&Ak"0|ݻryeecq\.kllAeEQ r=8d$b1쬫q|i̙3< pbwm۶---Adb.r o׃W!5!ߟ>81jxD=zTQ}sgJlwޝP@ R6~ ^hNZ_ePfP3L)%xnT~kh&ق E89lz.<!Cױi= q!D dbcLV X%"T]X$(nuF~; !T !TSS (}a e4ZBR!uҒ,OR:H$`(U(6ɔdr(iHǕeX;>|̙36lؼy۷O8}q&)X,v̙i-q<@&9~lT v'ɉ J)l ׾Fo~mp̿!kMAg!$"clg 6x<]v]rn>\PR)$a @xʘR1F@TDx,~/Ƙ6pBD\,6 .(Й$$"kDQ ! ʘ착$r(UKT"l^-JTU]Y I*t6\.,3TM ⇮ $I  F)'OGzѧ:oh;## 6;5!ɓ'};wj}v4zw͛kkk۾}{2|WL&c=@*cArrHZ(===q+Wٳ'e2۷+iZSSS__믿^gA+aU? !$ h7J "q_nL&388 fd2vPlBT*F=, |I(cVzb1˲,*(k*b!() p@fqR$_J9ϥ3HӑN}'Њ,vl6ۭB6T`X$Z* !iB](2B?;vƍ5uu+涖_D^zG!hʊ( p|3FwN$/^lhhi2ԩ**J<?+V$IPm+4@lb1C#t4`Xxp tJ0UVL{{;Pf,PCNa3@0e9Y,|>4z0ܸqJ0ׯ(ad`,IR(ڶm[WWכoe˖B bww7(%B\4UUu˖-%f27ocǎ'OF"sQJ-@"fV>yd8v:45֘1eGBX,6f`QiʕxMDA-&435mXT =uMӬVk<O&8}3e pQM .}MMMCCBhaahBBBVP*[m\./%fTLAT*A5>4A|T*e4tvvgYɔ}>!CCC.--ekOK"");FRqwz.BIOt%ۿ nYQ'NT,b~|;?={on\.w -A $ڠ0t=dYBlP`)v凜RƊBP$\*( SJemRBl2LԠْNJReU7/\.b&#mK{K+el56(f.8~rUr1>X7 eTJ'Zgj&7}?*Jp 5ϗ+Wuww˲KdۗNGFF, q_[[+2Y:VBSԧ? .LOO;כ~}}>{by'csjn8.ܹsgΜ)J}p}Gb"10F{mN?x}G|_iw,.-FmidZ^^޸qc x7!꯾o9r?a2EVz֕ Qi`S4r!C̀BBeDkcA B1d8)@Eaj- .qnn+Nt9Ϝ940`1ɷnʕs]]],߽{5"[,~xu5q܁ B*s칶Ύ{%3K.&i˳b?;~T*9V~laW=pe \EMc0D"G9PYMf1aoێ;4Mtn'8r|ر;v/B$iii9}L{{f>*ra555frYP(0u]EM*D%QUbD" ml6 :U,R E$Ё`oX@˛EQ\YY]2(S4B}0 ɤf+J(fr L&c8&ЍH$R[[cis`D{d2I)OOOSJvxƍYAJ̌(qƦM1w_ObX,}b f(cccC42Bkkk6.N766jۡػwo.+J^WESO?~<CP=dD"|ccV0N& pBpC?T_۰x/VQ^8s=jW+'O$<Fs{۷+Wf% MA%b6A oB@0lEyd*lR#@0 );Ȥ&I*K&I𩧟޶uiҁM=Gss۷/|sm޼ymmM;vܾ5ZZv\nl6KǦfUUۺmj}]7j5Mdrbt|~n.Nf] tu`jjLR6}~n$INn\.^! 0.瞍FcG;!"p<|T_گxv˹cGo[]Ľ{ARRSS m۶ݾ}{rr'eYUT*e2:;;2iP(r*> 6UT*_R07O{hlY>PG*&R*gaarAS|~;ၟp8@ Ξ/U.AN-D]]*RPd2$AaZ҇z(ɀ͝;wrᨭroEhq=P> ̷n*ˣ"b8(1Ji FgΜh4z9 ܸqctt5///AFAto*mz~0:pny/Db#ѥCL'Ow ʅDyqq(b^  a Ø:~89sfǎ=PCCC]]]$z7eeYN3l`0DGWWWww7<󭭭mذ0Nd2ɀ+dеT&][[{oZWgjݼyscC^wzf&Z ùlNuIv78{?dTmO @U|D0TU}! |pݫ1ID|˕N(` v:<@"wX,Ed<A6Blǭ"Vi@A].e9pb:ā!mۖJ\.A ~|Śv VWW7mڴ}t: f<#@~z.SUs``͛CCCW^mkk 2̆ VU$@_E`U20GBy!ozC6)N'0IW.ݼ~S'\sKMu0q![nܰaC{{͛!M\pAUd}t8ɲ lM6}s07 c۶m7oEqrrP(lܸ15P4EonL ¥K|>޽{a/R(nܸd իW}>߾}Ο??77 W^yEQC9s߮W`kjkBry~nNy===-˲ZaܺurM7jPPY[+TJSU߷Z eT$IRE˱^dc0F608pɹY٬k:BX\.7==}ڕǏ}キzoNgVicH$xv ԲX, Ƿl٢[[[NC @UTI@6G]YYvjj ~VWWn&ׁ^,fR @X, Jrl6MXww7 v+"2BHue2 6 #NCIÊ$Ig2Ӊrt}֭SSS(677+200|qI@ 2!Dܹv1,˗/ôƍ`ED`WӒ$iΝ,JeQVΝ;ݻIgQc7n\XXhΆt߆a#≠/֛oj.m_}"r|-_|W_GgfgN8)J: |p)OLL@xN8qɆ񆆆l6yÇ山1Xe}.>}ٳAM6}RJ\.L0foFXfmmm<{8,y8 6ݻzK. mذӟLSSӦM~FѶ6I èMR> Wy6 lN$l߾---YQL.;JL:(lhlmjljnjZR)!~(B1Ir\_Z,1ƨ{.vl1??lZ~(͕J%255p8>̧>9PmO @U|HD\.g6$qnd e ^ [p>jh d*`&0t_UUȲ `oZ9r(r).+ CVHHX4"y~ݻweHREQ H%Iu}uud2 766Jg7n<ӧOfA~/ƍN!P.xdMM8ѐo2cxBBcMeR83:]ķt{v} _ܸI?\G15]4ˇjotdN늁W<O$~\.<XXxΝ;۷oE͛@޽b\xp444;w8 }1v-2D>;N922rk׮P( |;vDT*ġC< az3̮]:tСrnq;wr:d B*J%pKZVQVM r*ZR)0Z$JQ__WVQNK& !F!& J1C!aĐ VlL$x^ #0(5t]7f]n߾vVM^o(r9L `eYXjA544[YJ*Qm#B7x#W&d|9$I c <3ƜN'XAJ|!\.ypEHL&S.m6[sss*X,t:! cBPnMӠU DYN>][[+I-[|6-J5550p84M[ZZڴiӾ}fff V֭[\eyY M&Scc(R544444@SL&Vop͛7 ݻwoOOO X^^b=d8BHEF <ǟ|mCCCC[}0jxEIM+;XCMĽ˗.[,f]+BZ xrر# Bóo߾NHOammRFFFo ;v:VgggSSӱcFGG<ݻA`p8L&SwwwOO޽Aw߿[oBӧO/..vvv###=Ћ/uV,b:NNsff299R*+\. *ɔJT*+Jhtyyyy9L2l2岹\]xd2---A3iiip<evv6ٳZWQE@TC+_ʅ u=C hcX*jhhb1ϗJl{H]XXx<@܇"UUu]7L(\B?P%+ϛL& |`[[[&Z^/d~tb1755ܹk׮-//cCkoo_ZZEtcP(HzϞ=L&R͆1d2hg9 Bhַ!{왞niid_|@ `jjjIRR,2+(KOkJZQ)yRYù9V.AUU"o޴ǯ/m[.*S> uv3ʮ]Nn7RU__]n|04m֭~HMa2\TUmhhuP(hN CEQrZ[[4{jjҥK7~7Vk8O~F{'|wd2,JSN8pv֭tznnN۷o'Ih#(BArE卋,BHɄy dxndj3L^pZLӴT*L&sٜ0b P !ZOnF1mB]7tLYPرcDž Ax~f2Of-~@Fp7!pUTQ'*>Roܯʯ=zROK__L! |*%I*J|p/(6p,-- tʂ T u:`$iaaCJRǙL&F)`#2tFx<|b2 RP(G.\|rWWr4`ĩ:xƍP(H455*4)cBVupppccc~hBkkkƀ:٬b)˱XV.`2;7 ۃcǎ|>HAlx\i1f 2!ow~k4/Yffd[[EUTsPM/旅BR]c Ҹ{Ţj(*^dP?Ii\.JAYi֠|b(d bTynq, x<Q\.Hr`>qF"f7nj\XӴD"sr|֭7b^L&S,DkkkN<yMM D|>-?}}}.]FO=ԯB֮]c(6\qSX꺄jhmavi1 _W[ghSJ@(&$3yCPI#Ì Jbzq+J]xu8ALhb؆  X\Z, h4j6#A>222;; P;P%6<{ر|+MUTQGe-b0@i6Ak2<O4} j*4 *lxbqnsRx n-\eDQLR{D"!b I Lƥ@{al mL}}}oo,sss333lc Chakkko߾m@kk _͛7/,,wX,Wo/t]=x๳EO~f{a;<pWB H0ׇ|@Ǘe9N;;w.//~~/Bp8|Phjj"x(Y0XC"<C\.gGkk,ˡPH4B& j}x#$ 4445+=333lŚ8͛7axf$)EԩS6+_i^7?~rrvL&EQv177wΝr|)x 6.,,խVVVV{{{Ϝ9 Lxňb'"0eQBn`834B 1aLړ`Q^EiT(BcF1& A>ٓfISut+*\\µZ\$IUUpPB +D";E ͛7_yկ~uΝf7saa־oo۶m׮]ccchHep3L&7#rk8B8 B0f<&T/r)~x-*+kb oQb2F?6s*jPEr\Xt:\.0ommm&,߽gs~_(r8eT*$h㸍7Z .땕 d2;wd2`9 P۷o^paϞ=NLM?csssb:::1& " !J)c J)8D)]1B+[F'q]8>L z"e%S8)盦Ĺ77qvĎe[rlɲE@`z/K([-Sk,k8L9 -"!'۷ZSJ À @MC<-!,E(U^eZTreٞJtՋ/鎎ٗ_~YUջRqƭ[.--/-- l`}rP($usyh1p 8obLBef}&C4Gϕl@OsAZ(+2,vAO91y9-J'@֭[Aܵkvu…ӧOCwd2fD6ݸqc5Vʩjxj @ 5% 0Ng0\]].PBZ\\DAxT*uoܸqmmmzzW#E,ַ {{{WWWD-t`bhEQ@P,TWWdnRQ0r\a,,,___t: hbbbaat:N;8wT*uԩrDN'? <ښ(,9r[VgggOOω'2̖-[^yIUU#it?S!due!A-J— 00B 1A- 9$`!D$\*W[Z[_fcEa|= ƀ1$!df<OS0A @RBu\*/ǩ л3PCo d9P(OOwsϭ᧞z_~ꩧ=܃1QJ:m۶o~1T* He3|0 Iȍ`Yx ("J- !B$)2Aw 1! O0r!З9Js6MvQr81Ӳl*ΎaR4=5})a¯ʯ?x^x=x er ۷9s}cMEk 5>Cn>Ͻ!pzX"xn"d?CS*r:cعs&&&( %I(!tK.]|X,Bc`@%#G~vvv6449nA$@b]w-իWSԵkdY֠aCEU8?~\4Y:y'&&Ο?t:eĉ0 omm{n/oj֟yxѣ+++[lY^^B\.g!Je^y'uaJtytTEE bC,TQo2LqG8<{o]0ЀA)_n>J^{޳@ B:鿙~xEQիW;;;;:vu=Hdvv > kPذaDmPC R)Nn t:Sh4 _MMMbquu5-ivvrveYfǭG?rdRQ0T !7l]Ji&immj8ԂPk>0@J|T*1 6iE'\x $xb\H`҅D"1NRbѲya444?IcYmjj'Ot8  @" ԔeYD-Ji0emuuqxxVWW[ZZvڵ|%EQ7 6I= $Ie!e#<J%c!]X%s BBҍཽO!8^}\P^D@v|nwL&{\.>}mfff֭?pkko`(eP!ԔJw )$IjooonkE2Rl9EImJ-ӴBPaheR NI-u0t6By zղ/ K We$Ijjj*>/˙y-ڸqӉo?x 7|s||!榦D"= _//,j!jj$ m64]TL BD]lK8N˲ w B) o!Hh4j  <^4\.W**~\.W"TJ ^p xz{{1~'r@7ڼysTJ$e)Ycl!Ϗŋ!(" ybfff"ݵkW]]ٳgAx7WWWMϞ=dM6]z5Xe^x0|!( L~2 /_:>̭ BdҲ,|R(@rT*N!qVu`BGF)][['Y[[6 ҂5M|2\YYQU65\xB5MY}E0#6mڴ [nd26mnnRzRk C}駟޶mۣ>zܹIO~ѣ>$Ip8Bl4HMBw(/;^wv#L*Ee1Mys`taI0*sc;sP}H$777W( sOŋ;vs=.]xb.|G_{վE0jllfC6-Pr Z&XGu24r9UU~69f2x<0ܳfe?>22)HSzDʕ+Pwt:e˖L&OyTwwŋx8ꂈnPɧxN B,ӪU5PPC 3gΜ;wNUU4znAf>*|~LRrA MP|>(j<ć`;GUUB AAN𢄐x<^*VVV4MSP?AE3Lɾj"{3>>\PR%B *gY;vyn |}sBabbbyy˯@kZWW*tVnT!799YWW۫iڦM0\7z@kkŋMSڹsg,.ٳgώ;CtOOO*ZXXH>M6bɎ͛7ccc݂4Ӕ|>}~W\t(O>x-[?|;w(@5Pn^ysv ud2(l6[$, 8*|N46@і-[qB`Cs\ s v;nàRJY`.Laj <\T E;wam699 ߶m~ƍ>g4?я...{.pU:;;e˖{رcb ۏ5M><11aYV:T*L&WVVZڿ'h4J_RfJ }^z#GGѝ;wرcddW*PJ'''ql 6@Z0 ۭ}SC 5^(@5p uEQt]XVtC# Q.*e9755 ˲r\(D"Xlaa}q5MD". }dEP}}㌱\.p84MOr MV|>oBNR‰a0??ҥ{NOO3g477wuuca@mmmaaadt$JqՃK@ .]NEQ,ˁ@8s;w>}g HL`!r^߁e;U\UUDMӼN߿!n pI&yU).1Mx_=5j&0}JyL&* ,Aq\>w g z N$]]]@,p__7nյx]wG?:~8!s8dȑ#sssO?D;vx}ffҥKwܶmB(_tp,..VjP-??B:hKK A 7"46 Lf߾}t̙3`Ȩj"lQEQѨT\.ONNܹs֭ӹ\3M;1@I1@gg t40$IL:F)vggg5M>pU,`X___,GA~eǣ(J,۷o_.;uݻu]ORsssD"m޼9:N866z{{88tf7>KKK+++L륗^2 trg=烱}U rJ"<1fUJU* 30x[jwBg`A5JE$MӀeHF B2 1h&Ng{{{*ZYY BH$o>jnnzZd_0 {p,===JellE"Q [.\(ʯ>rO<9;;aÆ_~m۶)r 900t:鴮htMMM.\رcB>_ZZx<]]]RX_z?x" իWAkkkv뮻z7m !,h711 odpppzzwwa---/_vZoonmmE1?n{aaatt90jx_P-/| p8\.7??IPyW'Lh@*u˚,ːat:MquV*06m4;7`& ~grr-FNL]|>_^^>֭[d?677_ti iqq7B8??qRU̙3Ktcǎ 6xɶ 6/}BKxM@cs> EX,fYVTjii +++sssp, r۶m/2LKK tgϞ-Jp8777 }Ą JefffΝ G?^އij`ddٳ0 |@DA<OR.x𯬬R\.W(\.`3 m6[}}=P;;~$Ia@ :ԣcp`an@,+++i snPgΜ8h4j4c\.k4Ahqqq~~dY޹s'(,4z{{8p…`0 hPի?͛O>]*c`}'|reeenneX,N!A9-..k]rիSSSP(D"d2lX,V{؜tdٝTm699ym߿P( >|755ڵ+Hl޼b1H?^͛7\.7;;kYrھ}vV__i&EQ:Zᡡ+W8::Յ1a`vL6#roo盛۳l[[ڏJb3 ?߿D mlT*IΙ3gfggA w~@ elnnumjj㸫WR)A<(vBp\]]]6񬭭J PxL&zpxmmmbb4MBHXxzh6 ryddرc[ly صkӧ_[[(Jehh(CJJ3330>siMMMpѱXl˖-/^ oE1LvvvJh4 ɩeYnB %Id2nL&n鄠_ N-r5fU$`R)0 ȝN'|>C,iuuucpoٲSfp.b߽qƍkkkAJ¥KFFF`N)#<ёL&}^0 #Wʕ{g/^L$<ŋovF|ѩ)PBN¨T*mذH*έ[ʲnYzWWF~lW\섮 &h4M03IDATI$kѱm۶_tˏ<=o$əSN~[4`<#.]bMOMO<6???<<|-.+qծ.~ix H&pˆalٲ%͂(< %'~0xۂ H\.fffl6[<_YY4MH^/T\.?O[[l6N:p8\WW-J>/KD6t0t‘b`fE\&^FәVѨeYY( B.d2Y`7'IX÷vY6cqqF"UCFFF;{|۷ok[+j]ij_a!p\.0p2ۜBP__:DU*YTJiBCY3N]13*J`0l62`a`jJ\.'b"L& "BP,5}>_.r鄚ryd2)B}}2Bn1nw݌R=p9;|'BP.s:n4;F)v׿>c/`::: xg!`||\Q~H:pKr(a<}i<@Pkkkn4?c'OA۽{ocǎ|Ν[rW^yLo;u?яB뭭+++>(mذ|Nr$Y]]lu"t:Dv{ss3ř__޶mۗP(C}ߘ?Eʕ+`DCA,.!WWW" .p8jrqvjHǥ閖-[@hÕ+WB|rAi!MLӲ s$ICU@ If  ](X ] \.+_)J[lzoH>cW"޽{>o~RDTU=rooFVʩjxj @ 5ɟaH\B!]UUM&ǁ|R*ٓJbbBB!b}$IZ__kkk]^^rq'Dt\J+JNX@vW4t: Lƺ3r9\uuu@6a㸃ax<ٳi#Bn3~{zz__vD8vؘi۶mkhh8qDT4MRK/m޼9ȲсY\\n͝?P(9rdmmBGFF@q̙ޱ+c3466_rpccKzzz~7^{?g2'O.//?]]]O?4k6==裏lmk2/%8/ i&OLL/qd!J575%',P(v`7Yu{R+@C:vrR - B&ieeI5;; zh0,[$ȢFp"J$T*mܸ{z*cԩSlp\&6LnaLl6\.js{6U;EQ$I A[qЍ.rM0o׼lEQn~CC֭[Ϝ9cikoo驩߿kI5Pn/?SPBB. h+++yR5Pq*˲NiPQr]cluuV"xMB J14M͆1 P0 XUBr`S dTlkkɹƙmvʕT*E)Y]]4 6,b͛7:thxxG?݇?oii\.r8sϝ={6Nwwwoܸf|l6lJcc(PXXwwlGGg>MN<F"dE"7R)|_f`Y(|>(łx<===.\8tŋ{|3_җ뮮' 6l˗/y<qkk+_!CCC8JI!2v;@)0p@&IfـE ޶mۚo!0Bh~~8nŋȂ,jfu0 !CA*$d|Wa PC_ &<9B᜜jM4ra |ԲLˢBQӂw՛ktU2$.--Y- hZ>J$Jyr\(yꩧ`PC 5B[? AsfkmmLinqUU~:g}(|+++$V⽲,pxۋ"hTUf`|}}}$Xu܇v{P$\.SPBC2 gΜM4s% UU+J 'HRGy-z666b1pAUUu~~r hAx'|T,[mjjÖ 6F= 9OR!jY$)L-c5S!uzLЍj[mK  }>|\.pa!jvv6N DB+_JMPC 5QK[! 43L>zPi T2{G09)rvRiPV@B 0ŋ"üd2BNsnn<.m۶ kZ]Ait0 SSS7oMgggl ?|0mmllB333^{54<`[MLL)Ԇ ,qЌE"P(D)c]]]xndY ƆaDQ4 #N3I48>BqQ5MKm6Hcc#qHcEQ^(Jj(fm b\X!FA~u0JZ3-1#!D t sss$ RTQffXLҊ 8B0M57'INQ~xcחcЍA>{}1 f|{YYY|-p! jx[? ^'n@ P\.Cl-ReeeeBu4455eYh*` PB@oiA C-"BWR@A!$x<Ԥ*H-5M+JoVP0P <@aQ,ovQM)UU{}}tnٲ?q, $IpԄ1>|,_:::8ζ6NR`r)X)9͛7+|yCQ R@Vz^Y`ZWWdVHm(ˁ@`޽\.p{.]1 w_2~WH1 #R 0*|\(pI8@RU5 z\.jQaWb ckffX,_hbb1aXCV!= 7D1F0آaP|}Ucc1e$={'PU GO p:Dae!Y1Ԣi(|1qbp]~UP΂?U\v:KKK ijj<|;cX]]]<O$ Lź:P:NpJ,3 }{<ر_nP$C}}}[[9=qF[[[!@U\.jo\__ I4Bf@j"2|09y8Nwvv"`C---={VRdӲDAmm>q#9qi <1yFaP]%hV ^@ tBuN-z@nPJ%HxRikks8.]ڴil7?`L&=Occ#xNOO///J1dnn}H$bH$t:ir` pRO<144E8hRR {D7X4!]o.ovۺCY W;O kMӲG}իXlhhHeY1TQT{ ijV*4uR n&y2! J ˈL0!fS<&((V#$N'SJO<`st5"!pi!L'B30dIT!8K"/L|- r X<3,R^eQ5u |TBh ijQקjTft 8X] 81!9 b!O=x/d"ZTnI1BU1t' |N%l!NByR!ɓX,fه~?cW\ikkkkktPC 5n:>+]fS%|>ñ\Blkkk L&l`A?av3LR1&A~UU2Lss3 l6+2B1NCZR d2 !P(tڵ\.h<~]]d'yy \f(-ˢRfVth/ 7 +}Sx<daI-$Ir #S2LeyM0tDa$F8s̲eiިhԢc ؙd2F'it"4p BԤbؤÃ6>lr>zC"$ lmqy[Y(U*z=^Bb1Ps3eb_$I!J)G8]v{xZ:q}\4(BI|ǎ }}}'N\PN]Rillܺu+jj @ 5B! V-@$X,677BRT*~?y/ @q8`YTAH,8{^˲4Mۼy$x€Y; @GIuԍx<|>`@,'E`pT*e@ nހ3=rxb7;;'>1;;r˲xc wqGggg"iBaqqy`Lfyyyuu(Mr@[ެ߽9zB1ňYR#yxALd!9tsӓ[!B ?(Q8(p0s#a łeQ4JZExA1Ge@j,h!DY2e]RLtVu*EZnE _,u5lb"S[[;B ,R&`̇aAL)Lʟ|>qG8Գ>ew>qخ-/kn0 I_ue… %I۽{i~;vX\\\__зĉ੿k׮gyɓ!~qqm|48 q0  ̂ Gh kaCME=!GGMS YQ,‰†s\"ɕKPG)JDG Y2@C4 #$u]WB191L>X'rBQ+_{MU8q5-"ʎՎ#Gp=].K7>d\UUEQmVVV`mـc|>Hx00P޽{M hP(v|UU{R ! jy+nbZ[[3Fa z .ȲF#ȡCJҥ˗v֭[ ߿{ xEbu-cLprS$ټ}{GWgI+^uFMZ(d]δ,F%`0GE^XZ?}[DtKd8ʥ7w핣l){{G>~>u^yMӢh.83]Y6uʕ?^8nnm;985L%I00$p( |NzW4 wu6z+ir0?C޳}^̡Byܹ1D:SJd 0M3nݺUd2y}U*%HMu??ڵA녕 n̪ ._,BPضm?qoqwEQh(jii!\vڵk?w遁h4 iΝBfu 6膇=' :%C۰pU#/z-H-,FMs׍oChfu#OʯfblWQs\WwMU'geU)/nNg޾w{bl2-${lY*@ڵeAmsȥ?__ظckGK[ƱDvmiDr6KTeJiuI`LZ6 o URY-44KKK"XˏezzZ円P(ب}_xQ)a۷dWE)D"'NT*6m_^z… +++UO,G's2 ٮ~FsS4 )^#B0fb&yPcݼm>J&󦭛ܶCzw\.gF (xfrA[_oI:m '!*&un_cckväX,]c[3̖-[Oׅ&&'˥PR* /+ k\6iȲic2-A^GvZLJΖ򫑈(9 `Լo둈9X8gX9A䅲sn:c6-@'ԢԢՌdR??E$*y}'sJd#!JeAT f 7ί,d2XTrܥK4v}}СCp!kj}ZPC r9HKORv09Qemm ,z(JRĄa `z<g T*Rpb6,//#@( z"X\Ji0\XXz(K.VXEۍ1N^@<\.י3g … NAD|L&L&eYnҥK...bz!IΝ;Р⢦iTE9r?رciitf2HZ!gB.UQ8d 8LadANaqP[Ȳ,A"1?c"O_o}_fOK{&7 ]׳rtqx<ݾsλKU+W?~8q"͆P($|!𢈉Dx!sZ"G͖001 d:!0sq\&ˇbq||yt]V`?|0|\T*555544H$bYn`ի$I5;;KFgΜq:0fްaCT 5 #ρy}}i\.}}}>7M<<$Bh0D5]R*;;zj#n8F` Cwv#Ԣ]gyw?kZ?]$1F`D &sя,ϖyr[Ne04T*eШ##D/W,J zwjqsE^4//{6n?~yt$E.ŸI4 con>YR\.uS/G#cLdY^t2G0̿yG_z!ĠiRUIɸaTfjgϞ=rrP(8EQ^傸鶶;ciiRCj}ZPC ??@z:unC0frTE]\.U(*0!t!r9ۋ( ๛ƥ!0?544C(l6LUfApXR{)X,t:aMp8:t̙'O*uh4 D{{J.x<>H$JbGydhh\.! Vsd2v JO|G=w_YYinn~rxA1Doۚw7cB͌"rYpUSA_5ſ\ ꫉!eZRJDRW4+0zܞW:tt"yڽ[wmtD- `5 \.uw6Z[?効gyq2/~O) ,S˂MTRٶHR*=#J"劬'_?,i(5MD)GH^0$IJӄyEI9EO a)@Ekd$I/_Rq]^D'?~qK%0d:B9f<ɨiªnfo~Fvu];B[ou{f\.X a5@ 5~C|n42ٌi .vRIQBeYPȲep$d2 *) ]Krt:x񫫫 B H@A!rTnXt: !=z4x@ deB!A, I{{{^o2D"DO_~C566^tR^,^o"*ԃ>xwC`1ƉDRB`!B4 fL-M˒!Vb(B 0F`/3ͦR8NE08O=Dc0~s?n +e!b!capoZRaa11!D gǘ#Z yR/!t|J_}9۳8'ǺQƘFEz<ϟ;soʂ J"Qqݳg||"sd{+Z08";;b}}}/^ B`y熩LL޳"nϏ=0J%M\l( l~DtE _? R({L? X߿? ?~|߾}.\bdrg>|8ˍ v]=ϙ3gZPC 5Qkj+E:N[p8|Ͷ0MG|bi<2NB!M~m(cxZZZ"H0y>*3 #˅GydmmP(fYF`:z׃s纻ya YfYHF^\.OLL͝8qBQC=^ײb>07ݻw e2rFKKKsw^A]v)UU-bKD"V(RW}mڴibbtb6d!81E cf1GOhS#G>XkS ޾kwWCMnh즂R1F0)˦e!" ƺoٲ`/.EWb؅ Mj i:Ƅ9D 0J-x15L^(bI劮)p@DaST2YWb @b!pD7Oz$A o(1jY F?[~d Kas18X)1|&#< v3ƪS1D017MCC>nf1TjJ!9Z !crF0Gxj!(aa\*8#麎GY\_iYBR*קif2L&=q7`s%G)#$ NH0q$))?EAhnnY]]l6njM\${}}=wwwc2xA -//Cxh8rͩzw_z5 ڵkyy4nذ!A ~ݞJ{9~G}t޽?яZZZ\B)p4 {ȌQJ)y'R?`D1Mx } o) /N\"xwJ$I"+˕J\*Qj]/o3(BD#1nG0"'z<<Ǜn!(~l9@Q͆mlll GAR˲(BC|8L4MEƘiH2 $P0577'5655;::JR4mkk۳gϫP__Niii.--qgsCWC 5sCDt: YK![D"f9s\q:}G.l *p8bdKiPJ].T6(iR5 P(X,?4Bzh Jg20 w"Xb1dzw^>x\.g<d2t:a_eQ_a˖-hW^YYYE1Q1p0dȑ#gΜ1 cvv[֡Cy4"TcQe >)12b`BAf !5Lc˫/V(6ԇEBir'Ȓ l&++rr!tA a8Q=c!ъ^EG܅gkql3Ezu7}~&Ig#,h**koPNfQ 1s|-ӢE7;Oa`H===[l9'l6¢TXe02tY˕(#`cB0XUj#֣>O~!x^y啑>ضh6VVPC Cj91F8ZXXS[.//_4AÑ硚G clUUAx!"NPxRFf1t*(Mx#|ʦӉDb-ۉVUUnoo,//#oaX\Z\r\WW{ZC 5~C)`RIUUJi2` ZZZ4Md2]]]r9Jy?qP(,//X,666eptI&D) pa@)* $3毚R)UUyZPEBW~WFGG8J[[[GGDZcBt:MeYMMMa,--y<^{SUUq tx PU5H(rE˲)---?яZ[[x 0(;22Gс;#,..755% Q|B<ϛv YqÕ^5"D! t&R fY1F_@fx,`FlJ, A830E"tR)plyv{XZ$>L&p8 :)/_ lPJgff%I=R F1MS4bW J,c*f[GX7|>`\>zh]]$IL& :NEVz-`$%Q*(2NMM7=?VX4-=+XU*Ae,.,̧RiA9h )OUV=ݲ,=99</ ! o) F Q0c|"B(ZTPcE !5 ?BH}Ccccַv[z[ZZہ]W*JaL,;t4T*|6]D Xy |>@6(7mteEQ:t5ROC{Ç?S(֩j٨55Jիxvvv\T**aL0/$ <JOrԢ̲,D>7q%1xpZ 8*vh8uvx>cǎe4v; Vd2B!mMJ4~bt:ٔiinx///)һ:zzM=}d\x^z)477/,,BR4== yyr^* $|_}}} Ţ绥uj~j @ 51H$?33JBO* 0:i}}|Kv;4T*r:r<O>GA뺢( Jqaa日RP(*FQY*M**@`ee%4b|rcc㣏> $(v;p'''ԟd2---D0D"p8dYnjj׾~add$ɀjGGʖ-[}eeett>(s~P(ؘL&UU$=CYvϤ2TXv,3Q|SR/&iTsg'( ch`Nӏ7LCB1Xf<<6acAi?MMM{왝D"xg4Ms\i1:uǓ21qDyB' #!Qf=!eW!xY<\lcn|d--- G)~Ϟ=֡ÇyL: RRiookii=yw۷_r!PY2GRX @ZXNjZPC ?'08F MP(0Ƃ"pLt:zAh >TrTY"[]cXss3!$NwvvBp>< gPp:x<ˁ7ó 5 >OD"!IRSSS2:Nn_vͲ,h3<$I[lxDXَ8N{bFggMz뭳gφ__FW\u=L_|qrr2 _xgC;v'X]]ںu=3::zҥ7!Jht@ 0#B(N}+_ ׯMzN\a!0"7.OXqƄ1ZwG"L[ 6rL&F( c,4MUsE-u]QPRx!DxJTی$Uumm#H4M&Cpz`0uB8YEz4 P P J%] BT*pQa|.;8yeYw>\D"<χ;v..-΂7.8qgϞs:>YEgffJe~ŋ>|ئڲ 4MtT.@SSpKKˎ;:::{90 O[(D"kkkp8LNNN\Dq^C 5p 権j9,"^Wa=JXӃv`03Jz\.\L&t:dJtBo@ ~Hp:dvCba,8Hxҿ w\}&pR\9hD$`HJ$%hYdɾxͼyw;ό<ϖmS,V E%rSO{?~&Hi{oQz%aU9]/K?Im622iKR  i e14rsxO)JB]%q!DW/ key% wn.Kjﶴ4ɲ|ȑo6v{{{a:D)!T 1 ~LIE0EhDoQ^C'h(ᘚڼys x饗~C=VuvvB/p3*ipL˪V*jX,Z!^C`"0Hd/ʲ \\z ~ƍ@4ݳgߧR'N,//뺾o߾p8|…R&MMM===$ܔ(-eY1njny}###6M,,,HTwׁT* ַl eY۷oVt{1 #NMMmܸ?X4@A46 4KJ4l{^Z;<| ###j O ؞CB^,a !HQH9T*v8RjT˲ r>$ȅ%BD|r\,(zp\kbb"J)|.N]vr=CGxs]vjP(47?Ҳm۶+WZ.X,fYV___.;vwߟJN:1^]F!XŐ %`.Nq(z# 9FsbN0AdgX+}c! cn>ŋ!m*ȲRoƹBQdk [' @+8Ȳ Њ@(D#}R)Hu$vBo$f+ cccpK(1&+Jy%:*!~r knnu=@Z\wwn|lv֭8wB۷̙3;t׿d2yw~ӟ~gcF|>1ppwݵy/K%uЬرŋ??ŢiZbԔ޾};2Lܻwn?zeY7o|^7L6D 4G~I2fs8bP(?|4Zj5ƷuJiس(O 鶪677/,,~ x/OZ%x<(АeY0!{:Gssm7mfb1HT\@ нpaʋڼy3S8m۶رcjj_122ts\X|駷nOرc>(BhjjP(ٟC=:11!IMq pݠy`l60AzoL/LK!"z0ƘJz~Mm1P " j;*R, ~9#&B RIiB e!lvZl5F@ -F2\͌d2 F7 b:-[k^3棏>zСhmmNRYv| $K!l\lٲ/lx`,O~Rv 2~CCCPUߌF@L B?z瞃'`=áPg [lt oߖd%*s˝dZ[[7ګ'?cJ`۶m65޵kOrz/\UÇv{GG/v~?h 4QC@$~ZVV=L&r91x\(D$NV+JHc@^XZZZ5H6v߲,0~?$ @apC> ժiBA$`455IL&cI6ma9s^Yjj (=^oOO@4MvZ>w\@#i*J&@˪>\.gFssl6 :\.d<O<_\\vtt8x_p:j"SY"dE@oR[:'ro *QX cYo`*UU^o 4mrr>S$I4tz\6 !(r^7M˴LxPkRV|>|Zjn|J)L#]F Mȑ#}}}'޽{iiiaaaÓ|>?00}ܹ`0QB~R C}Bx7n\z5vvv>/^;qqq6HZW^ݻwc=sϝbhhc|u7?Ϟh4/j_}…\.z[[[ƀAU BP\.WX?D~ ?DM@[nwR1 Cl`AEj50J&p1V.IRnzzAPVa@RP'''+ʁ\.oQ(c k1q^okk-[TT*u755a,--jrB`0K/ "$|ӟnmmM$?Os3g v- 9Nt:C+FcdV/% M!y xrBFT*U*B?o~ʕ+J)!0/ 3!t%nYH/,(nQ#[ {1aht.]B822<COS>F s>X*fyH;x-[|MY8ܞ!D4 i_W.]t:s܅ /G? _秦&''<N8{{grrŋNޜNjjjRU5NDgffmfJ%4~ootBPB[ZMuǎ+;v'I흝O=/Jz衉{޽ix? @ N?ςZNFJXV^/@]*vn_-R!cX,zǧR)ι?tѣG !FFFyu]RJt!!8izkŴ B#ozzzvvvӦMׯW駟]6bzfpv!=ܓdbXSSә3gD"jPMW&ٸqa?O{{{xW奥P(I<^rS8\, 3fعsK/500 /PJeYv\d2JA|nH}@ aÆ_~9 Y5??n̙31bZvۯ_/saiZX,nR[[ހ,kՙ~5rɲO>Py{퓓B9w~w~:΍7={R\ziqq djggO?-xꩧ_~ƍ-[477_p.%t׮]/,= k۶m׮][^^4m޽RUxرSNuuu%RܼvayRFwqǩSRԎ;fgg-˂$HKDL&m6Ld9}>B4M`>B(I{vv7ظqiSSS v\P[nxxɓk׮:|Xt:֭g'?{h14?O!|PBPm],߳3JzViMӺ/ h_Tƀs+IR86 L0a TFC~4@H$lz`0dt]vZ#ĺut]?{a۷ox&X,&˲fwbng˗/{f7|3 ۷oiiڵkr;lkk[^^z{M&Xwu[lْMr'OBqp---h\.CJq{{{R#Enn{H6 /ivZ9zMMMH2441Ê>}c|wH455uE!Ď;:::`Mq+%IyWWg.3-4MYQ$IbB㚪 m-,7^R@Fׯ_?Ҿ}\.O~RT.+>??@|rP9D">,sǏ_XXhnn zfׯ_,ܹsP(Jk׮A[Xwݒ$8qxJ'>wD" hNLCHl9fff*J8ڵk ð, UQUf5dr7 X*xC7z~yMxL&D CdfQ7o޼m۶iJ@4-Jz4/'OvD\.1<QVj$I^7# BXaT1L& $nCfׂG"ٳgOwwwGGKKKNsii _v8R)LT7nhSB0ܻwo__eYX7R^HB8reY~s=x My֭W^eSק~Çk4@gf,$P(\.˅1v;vp:R Tum۶y|8 9s& ^vmttTԄ1޲eK2loo;رc c3g,//39͂l0& 0!t6lXZZ CDTl===O|.۶m|m޸qCK m6jzI؜sr9+D"J*JZ-R @f#r$ijnTuiiw~w7v{&lBpuMD"8kU*7߿r~< 4@G@$!OaX! U VKKKҖ4$IR t]N8bxz  rP(400p)PyZc&TP755 !,!c )`3 F.P.Bh֭SN ʕ+pTh 0ccc---sXGpרs>?? {1~l/O@.lٲe~~~rr2 e2C뺾wnRoNNNnڴiΝ'O;3ק{GRr hF<{l&-[XS@W"nwP(onn'N|>Mn6˗w.\055eY---`lfggNbhƳgD"Mpȑ7"J2fi apBh=!-T# }cǎݻw\paaaAJf]~R `,K$MMM‡d_ysss2#[RpׁIJ,߿\$ٽ{#Ga###O<~\.WTN'eB@eK7(` J &˲"N Bt::.GdUU-fzT*ccc1"pKKBNњ5k^u4'O677|@ 4Bh_L߾k@j 4J---05Fb"Enbce*Pm߾}aa̙304myy9|>BY5B… 񭭭P/F"d2922R*@ _v… hwddviGGG>---7nܐe| ֬YxFGG89?rHww(.]ͭ|c|I `/--E";VMOO:t(NM6bӧOW? B~;LrgfgBѣGD^x裏˗//,,6 cu۶1oM|>Vr$I0n/IsbEfx<<߸q'qΝN+W:;;wOs7o I4ח@")Ժ>R r  nTи.BŋW~333X xVRJ.m̲NW(1j$B!a@k60,]u]뒴9?q!vC]woOMMw}7n`pp0tuulڴix? @ P(|EXR$%BDB\nddDUՎk%I蠔NMMf۴ij>}Ztcw>z={eYu= y<Hkj\.#:`n .\B\;w?>(~i u7oNGx<333.xiNu ֪UH-"EVRIVI$J$ˊh6Mz-[_;m۷]tkk-[Tզi6!8RT"s.(%z,fibAvTFqpO\usYa !L((jqhrr3z{{B hѣmWO~mmmgO 4hh/~dҲ,bXsss.\Lfiiv0h333pfggGu`znФTRfX7pM6jW(X@i ('No޼j/^r~R 0yM$` 1,;Αk߿T*J%~ԩd2if<BB@r6m477WTvyYK F^{P(޽{lllffp۷/9s0JFGG=O0<+IR.{7,ڷoC,x],2txcMMMMMMO;H:;;Bnŋ]]]ǎf͛7vmgϞ=zh$^~}۶m?>;;k׮BN8j-[@^n+A2=1`c577#cf3M^뺾:BA9fF"B(ttt@ZX#-іj?# p^sc !xr0m0ƈ`9HTfYTTU%;].Y5&K$˫6>T0 Θ<^DR c"WceHdfvZ l6{)~j:ƤT*1D !S Z)YoF!BH1 C0F0B`P;R @@T*:~0RH&Ţtva4@ ~ItBep8$IP7pN>O$@ J:PiD\.sC+`&3Pn⢦iR +B`Pvxc dXKs=wp8r^xwǎ>GӴ'N0:::"d- !{14˲|ԩ~J#G~bB$9|0O^o$mmmD" T ((mmmNȑ#jJ%EQ Hann0˗/oҵZٳX@0m3?vȚ5M̭ipe])ğ ^Ν;AA[* $;r d0V@ }@72 S(\tEQq!8L CBH"` B8ccfCKKuM# M1K4SN 0U$Is.n"73fӦ$IWU'Lvuuwtt\r5Lرg=gO 4hhgyOO9/`Oj*˲a.kbbg=`MN'!cBtvvSg"%(PG(N3"BrdYN${<ȢpaL:OA)M7n,˭ǎ[^^~GGGGgff80gӦMB] (J{yw\qnI 6͂a8r>_~Z^^BRԴ~SN577^^=<\~=`:vZ.[^^v\8|2ݳeY|^Ca 0 H>pggޛfNg<vO<|>r \ZʥRTlvfem<m c ]͉c/_q\@RevvvU>88xU"ʲ  TsJ)!,a1f !,Ӵ8gcڱB L,k$===~o9;}75557^IUYEA,Y (D 1_"`f&B,ɲE8C11TUx<~ҥ{wzzv\]]]ccc=== ʿ;8088ذm>jh4 44MEW՞8DPDx~bb"H=LCB Gknݺu|||ff hb4!ptww///< clYA- f n;N(;Ӊ$iqqQQkNNNBR9Ca٠+T*||>_6lJ/_v:C HR,--r5ky^'?|X`0Hb1a۫j Hڶ6PY$Iz%I|>_,oܸC}h0rrZvuu9YDFGGc]]]P(;77g mkkF`DgMtB@ ]֠5!$S'ɲ v @YzMJ7 P(TՉt:LӴpxxxZB9P( +@ )1!Dnv Y"J%J1"xLS$u붅 .\|\.UU\.Dd*QL)(İL"Q1Y6{R&QeŬ+zwSo.'B"q7xի@xƍDmٲEUU>x`P8vXKK~f67@ | }@U$B( ~NH=;; )^wnnnSJnp8NJiZ̰S$Xv \r_eڵ]]]/_c~YQR9GbQeBrZLTnw&|OOOA.KxwMӼqF\2Qe@ Kσ&u߾}p lP(dٮ^ vyɩH$d\.W?/^f[[[СC$`c<44411qرp8 L*ɤ7x^L, BxOO\ֱP($P6rٲEQ~WWWggbZ%\r%RX,bJ0&Bhkk+1+;dYr:cow6 P" :c|0 A0ƩT 3`zX1?D;u=^dS\\.6 }V'x[lT*lڵk^9J9sP(<êwuuJ蔀ښfm6[0J&Lr 0꺮E`l67M6<< "lBīYEm<,&C---=044Ia}}}㑑BJ%1Ƃ`&isML%0BZ̲d:%@\0~`ڦ ]@ذaOv\tHG 79ES۞P,q˲L1YXc$ `BV3!PB$ 0 YSUfEθfcs93ڀ %$y$exhD1frBD +Biq2-ls΁Yx@#Iҫ Qԧ, ÙL&J8p5@ |dР5 ({1(|gg'<[EQ 6 A]&"JhXW2MӌFRFΝSUjMJiSSS"jpFMӊŢ($\.ٹnݺBP, Ol6+I$I?pgg7B}EpX\Z gH,kzzz߾}w}KN>M)6 *k;htii) BP(zZEi޲,܃*Zzp8:[ bZeZ H5McAFd :h0^Ϲr;Bawbbb?ɌB]vÆ @An(,\5fcW8KpB_ZuSSS6lظq[o Q---<88a5@  @ |\ @2Kesv;(h9`cۡBe'^\.͂(DV$Eph岢(iixt:uVߟH$nܸQVc0Vv޸qcllT*ʲJ Uf4MCEQΞ=;ﴷ/..^p9H59L&Bjzm Ν5M3 =#/_>z(?8ˁ;>kT*t:/^O}*gY,RUJR(B$*02P 3ER% fq"%`sզJv'}F,k)$%pȒDݎT{ݖ`9wL-!6CuI %Be`+Qe=Pz@ Dy`ھiݎ(l,rK`bw/ʲ P.?3gLMMaUU10B0@ h4@>߳go}}~ϟiJT՚9LF4rvB, P_yP0nݺl6z&Hd;v\rehhT*%IMӀ}X,z_w||:ϻ\.8Q";|^/`1=l:X48S),KcBpθB yo躩YK#4M1ȬAD1u=7-S(g3DBa!ĖbSr&\QB Z!N( 1u A `L%"'E7} {X,J/JD۷v7BBG#޿|T=#o969' C¥Ko͆_&k?{'Qe2%I.˒,%l6gۉ@ˮsdUc(kV]/ռ> ɁphjjJLT>7?W ]mO􄛭r=y\|a7n4ի.j|k׮ʲ|رv{JgϞF@ |hhD7ne&]ׁ1 u8RH&ɲ'''c!0~_u irBz!ͼf3 r >!0[B c B$ >$1_gӦM?JHL&޾ %G"T*5>>^*nwocW]=tЙ3g.]ՕJoΏ㯼J6Dgggw /|(d2i"*4- wvu* 持3&+pt]Y+eV[7 bgHV34-K$KԲ,0,f:8csƹ ,9 B88_W'~s9a!U$P-rgu*Wk!K2-AdB!L(ƈ"hqoz`$In3(I)x=0U^g6ul[u*izo]N}O:{较O}W&Ǘ?tܮkΎ;yw:}*~ĉ#V-!* !DQiR]*8kZ(ddmv1j^R,O8aO$jqEQri +P!ՕL&!Ӡƍ@{7 øv׮]~}h4Ual>x]`.>!s8 95M3JMvgYdjZ@_[i޲, B!Dy<Z\cl<`"<8a(ĐR)R˗].ז-[hTUu8SJ#H d2BBP*:::|>K/AŕJsn~31;;+r{{#GΜ9䩩K.1>b۷ow *vUUd"YUu]_uxP`ι,Jcfq,nH|^%Q#0T,FItE! s$_yIk`{39FƈL$ɪ"+ֻ~EQ$.}JN Ř J $aTg%$"8BD E+c e^oWsJ(u/R5vE# blud4Ox l!U)m]=4ARem}k6oԪύkαkUUUU1BBQȹ.*ǏC&b!J%^J#YDDuĄD dYV4j[]^v%۷o|0A'O}S$MMM577V*hh>jh4 4bU:99Y֯_366}T*K/!l6xb ]!$Ip~nRPJv&AH$R(mfss3΃P2fP@}HQZn횦h,ιT*P!D ۷/,,ܸq;󳳳P+CDlA7H$5k,ˊbk֬Aυ j{{{ jjj:w\Rimm?> 7nXXXr{yP> n֪U4)!EQLM< )LN$DeJ B`WY&X(ni$9A4pN@H5g c*I`tnC3V:hun(Dyoa"!i ,L˵j^s{R)y .d*9l!G#jl0BHX,atdc1*I^_#lSd|anX.Y6BCD $KB Bd ڻkDH%ze*KjP(!,ˬR9T*(x%wE:F)%K6Pf1f*)lrӦ 2KnTQVaYir! Yf2vCYzXjii}}}\_t\onnq8'τh>@4NuMӺGGF[orہ _ՊŢS ( H&t^`0 ͂)'inw>wQpx< BR1bvbf!xSSbr9@`{fc vH|֬Yt:)haabT*uiIlחf/]ݽpܹ{NSSSv;޽{|mmm_7x`z.7s1B)1 .B`nSUq02-"B $0粢t:Ţ(Vufv\V3KuƘ,6l`3Kb.yDr8F`&8"8܄*`lH&s1BЛ^:^<}_z<;;MV!)8Rss˲O)6ʐQ2ɩL6OՑiMR0)HP D0jV޺qHivnYMÔ( <(ͦz<fYlV9]81H"#JF$JāH-K0!-HS Ӄ"7uf8QB◾ҥKEQoٶyG2<ņ7nܸ@ 4BhB7nr90N8uJ% LNNl&[nddR n[Rdl6|٬wnA띝jn;r |LJa]A B PAw`o*2B? BGG"LJ;3455eY%iߟd hnn. .kǎ gϞGFF٬ k_h4!Ⱦ}/nܸ 'Ovtttttrk׮)BsnYeu& J:gJQ۰syKeL9 lY#kfYE LkqqZ.Kp1F 4W9nW4S$QUX6B L0 BF$BX̊ݻw>c}?нyw/rlZjZE@|> mܸq|||aaaUJ0&~α3(]SBsbK_4`Fvqؕ.mM@@:[K6o7̺aN3I=>Fq>d* Kp  ܌лQJ ˜sd2AL% 68U0Beَ:b`LF#yz䚵kFGGO:FSd2 U] 4G >@/gX Ð  B^׳,8 U*INgRi]np*ixrJH$a6l_fM&FMMMTRzuJi bH-oݲ!8K$W\Z^F`p~~~aa!|{"\rڵ{z<0c4-If,cc_WzmǕ뗿o" %Ykn[sSSSo:n|{v&{`oOO{_sbe D@\)j5YQt]7(!-S(=Z^1B$"!T$6LSl084;;337߰, FcFRU8gt钦i^wii _|& 1 IHkvYUU¢Ptq~iPd$~\)jڹg;:|.wlii}ځޫ)SVU BY!0 3S+BjJ(B n1Ƙ-ƹDeMNF,*r$ DA0!ÇYs/<|wwwV}k>Svŋdѣg͚5CG@ 4QChpa_l6FsBR)matJQ0ojjr\fi{N0333ĬjPmu !>Px?!C!r^T* ۷l fڴiDSN---!`*.V6A˗a$?LϜ>s=vÇ^ƍn+bwŠB9s#rv9??_.1pM0+!B9F50_yG>ER~+"!/|K8\GosO}K3|q?0 oQ+" !8F!IU,24뺩D "*xθ@ cȖ"ĐБڥ4YU+('c.+ƅ%),˦e_u _ E\J1!\`N49hv{Xd2fOrP}~rIhv$_)KE+MU63g?O>wpww\X, Mn=Mni@BmMLTD$L$BE"d!aEQ a""Lc, k뛟[nRG[+JooaJ6==uÇwww   4QChPRr9 ]C ^˲ic٠PrX2=d:b}x@ Bl614T*Ac~~^u CQn\D!?\.#jZZ|t:kc, 9rƍ#vaCPZrJ"V555zhkkŋO:(J__$ 6T뽽 BfD"pt]T*wߍ7|MP/BQB(9=l`SO=E0RHbHLsm[?gr }ɸ8%ITnUC2ؼEeIuRT4izzZ q9G\ ̅XI\L\*84e)USuĸ$ILWV+vc"NKqX7-zaS5S zMjkb$!YKKK_ꫯu]W\X\\\ZZZn݆ FGGu]_\\O$===\~=@ 4QCh`xdY^\\mQpXr|ll,jfu߄*t,5z_2 JrUBt:!>q^˲fA `f ~8?RL)$IZ/ SMsӦMX7H)*JZ~ z+^~}qqR:xԥKnܸz?ϖl6{J鞞n0^:>>n_lkk۽ksg^/dp!zA|ncB$$ETdc p.PsSmL\?ukѾݏ_xb.MNO)$7uCbE_ ^^Ao1&+ "$5Ɉ֐xO0s[ Jё^7 éo|_woReu]o R)x<X6%0ƅBٰLPZ˯U&. zk!u, N;i66M0WMTu8 dE0RR5U9gbiPIN {B6}]l#Mê *H&dkv !1" ©+R_׻:X?RW(J4M>;ο˿rshh4 4&˅B "TJjY V+x >؁0d(9mYV*o!dq?@馔@Qa~?B%  v^V  ---~[BvvO< a_A4/n>22Z[[\t:.FB%UU2;vLQCmݺپ}kcY$X>" \cB(&XX8mIڻ~|RFBLe"0GH1`dI֫>/l:2_xHd1;2bx#\GB$"S"I4B,+/KRS(Rr$#0 "–B&}ws''_YLrt)//}^E[[oM+'|gs  hAŽ"Ag颊$0B:F~K-鱉n[{BF]eIQ=/)xaF]5M$*Q3$B,$D)^nes9fYv\uҍX,Jxmeɒ4:3775v8naSB BH I  D` A)uk֬y5Mr@ooR&Y\\p.[^^t޽{#˗O<}l 4GhGJ!do-PQ` U>P.ONNI,˫JR u`]veY$jj*˲aBX,Vׁ/˲e9J 0<!tju]ׯlep\\\,,e4S!_lnnu֕eIѨp8\.)O?ɟ۷ᘙYfMgg?M.tNNN~߷7nd2kZi1f1Yje}+_vIvne.% E/+#+U[ۿO<䗿]NkVuN@H`Ļ/w휳T*U, w S{ȒaaPRY2lEͲ UYQ%IFmzB=.^PKuVzN(✻nhKVUԫ{Ņ"qٱ{z>l* e:eB .8 |5,$"0u]utL0Tneo:AHF#DrHRͲylqf!b̲Lq7M!is@[[[kkkZd2g߾}n{``@ }(6ghE6֙L4)*TZ(J$I/IҪ \;ᩀ鶺Cƭ( bN3Jl6IŢTU@QםN-˫=<, z\.hf* RZ* @ 411k|>h6<Ғl[l BXԩS'l*:{+W`B9!h֯_חJr\*jvuu-,,ٳg:d9_8竜 m%{7z١:yEIzo4 (bHpNG"<;|fӜ鿝_uC!~k(P8ǘLMM*>URiR# +,׮Ѷc &$u#?vcZ;(Un=ߎ45-ė{{r> P-1޿nzSSSp1LWjl'BUz\v5u-ʫ.wϾ;ʵPX:;Ν[?yw'R#o:" qyi*JGGGZy_{+II^- AY§B].Z/i[v/D 1s9g\`s ޾p閶VfYRB! SB B>KDjPo1Ӯk*ƘRAlxe Br6 Ó'ϝ;3τBH$j.\Fҙ{[Ӵm۶9Nh4 kVV$V1PC477WUI֮][,r4VXT*QJcP<u}ڵz*~HgO0g;L1@BN!!p3ƚc0~c1T*9xE}q,n1)BHC7aJpߕ)L0B""aΛo8pGR ą D=511!0!9⦐e t5ix4‚=f>.R|x<^("HV]ׁ}ojjR^6U*4W61 x0JϙNM4T*5M? _kR bX lX ʢ(t/`p\l6[,4h+ ptV[v-!n$ifzfjzbO>r~3O$ijjjnn`ڀZ,,,]vAqՙjRg¨ ㏮[3Rٷy3fBJ0J1\pR0ݳ;o3"];KeRu= )'9a68lvL0D[=p!$Ѷ6pW|BS(||/ET׸eY59Bp"IF*Y[+gjθ(2EzƹŘ>z=fY'_~״L.V`:1 [Jʠq:,B!MreYDW}  0cB(e1]M=s3\29$рߏ1xN:os=`pڵ?OBp۷kvL&@@ !$IE ;vAM |psSwwAxcmoGwlxR bHVJ.'5ȑ#=|_ߩoМRx#`sɔ2dKR+BhOOOV3 YV>3M #EAJKr"W}BdPXl9![uD))X $,] d!|wz)"Mc*IBcB% _xW^~Jt]g X0)BHTbIT7?@=zWU!!g9a$݂6cE)Bs,KlBvR*2rIsgϤRAYWѣ9RGGԡCEy9皦MMOA?p뺯h#F@ 𕟗2Ƃ ci&ɵkB*着wK҆  D"൸z@(ˠ-f4Msii { h"@Au>?^wqqqU (R9Y%EyV(L11tV߷{ّb S`"3$1q;]#o Tjh8]N,LL{.JilaQUUfMw:f߁ +㚦ժ5Ӵt]'o"aŖn!s"E( CLHp&Qe lq,,$x Lӄ`9BzTGx,AMMO9NǓfo} s%29<Ri늢8Y5MV*!*IPS S nwd5?{۷mG466n&+q{3%rB-EQN(^R xz`qPXB0;;1Ǐw8W\U^\޹s'_Q+J#¹?ϡh4.u!eY $ H,]*cXL$YUU6`ݺu1}ʲ a477'NB֬Y3??N{zzbЊ2ȁ M,>V}! T*uD"F{zz$I1tv7nhoovTU}ׂm݆ڸqիW].˗=drff&<;v  ˱Xkbb"χ7 5\j-$Y.K,AB45E1GXl͊(Lt8v[LiU*4}niY!q1!ݤ!QBL\^x^4ʹ,!ԪvYaܩӟ} S0CHBTƄ>93yƄ3  Da}/J29Ƅ SRdv==B> !$E~M'T,#$** #L˄B@92kBȊ| !ٳg^ie:Xb3JY8\m_IJYd2BX,.//R|>_.+zr1~'|2ɼ;KKKw}wV~7nܸp8&''m6H.3MW_}'?h_F@,Z W7O8^0v{>.I<OP.Y!rMOO˲vZi0'TQr岮>ܜeYdRaZZZD.slK[ZZLr8b13| p:`@Q0۷oX\\t\TjaaڵkR4MUU! <A\.-|7oJ`gL&NDoo(8?kxx8N?ڲeAuBȾ}:::&&&bؑ#G ?C$.MgB!(F 0UT c"!d &cZ7tYWSLtG}_cW29;7 |t(S1FQJ~G?}a 숚^[0dLaL%W_7i6UU`*_UJ϶"i9p<c B L,fu]\.=ztttӠ0.dI4MRB4 qZ+ Z̙3l.NU*T*Nr^Հ1޹sK՚~rrxw~?Hd2UU(jj>d27(@ 4G>X_Rf%k^/ MMM֭+J~h-lJpDMӄ|31@t$ D v…v4+Jss3B0Yp8 afV:q~ڵr JCR)T ⠯THX,鎎[9rT ,+d^.]rT*e2۽fUUGGGgffZZZjggSUo^xG_xEQsRsB*BHĘ aq5!aIp:1ͱb<4&BV_K98[Q-Z㛲WXeӴdIjmm4\*Uk5YQny@"@!ͺ tiI벴 xZρ߿ҪF"ȊwzܫM!J ![^ ܲ. v 6 _ 5H[FRUU!ĺ5Hk͚5 SYp4TF馡lXd2J9LV*zfYLiǝNgTl_WyBޞfm۶ky{ĉ333vl.]R%L硇D"nڵki~h4@A4$vy-=` dYeftB/HfPT*%K,S J˵ʗBp8j333 dY !B^43?l=11QV7nXV,HMӄpSǓL&p΁s%Cqhh(,,,;wNd2  !t%4~_x~֭QŦΝ?GGG_Խ{RJocǎR<;;[.wRtڵr\u$G^V,1ӬjuaoB@q0oy/n}%WJN-ؐe Yyv!BdBf]deddbgUUnOPNW^sT,u!yn ny=NA!ą@ɒL(` JTi՟uݔR0J$'ODD^幹9eY,//Θ,\j5f&vvv*Yo_OWooӧt0 Bvӌh4לd2944;wg?{̙oېj,OOO !6o<11!I҆ ~: 4@h4 4lgrZ„V(q+ Իb1z5M[^^2"٬~?-J|>T`0ժeYR 텼a04Mf^iYV6BѣG À?r|ssaPi;tҵk8BaqqquvAZ ( ή1P*V>GaBHOOϖ-[?J$IڻwD04Ms~~v7?~4˗/+t:1b4Y$}YS9UP< +H!C[YB2V,%Yn\4n,. Ea =p u1sl 'XV+8+^B,UV@z.,c+BH0V+[mFbR4<<<=;ęvUL`I#θi1[cHnᰃvaҊerkPuZmiF*:yPZ} ^c!+^ $EŦ "Ǐ0j*ݹsw]b'NxꩧNÇz!ҥK ۷o|z}vo~V=sss/R4UU^reڵP@ 44 1fYT>FQ,!7Je2fY|V@4 ʲ` O$IN3X,r90wZd QWI^BsH!(J|r|>1!|>xw\X,bO:o_1<}KU斖l| B/$S cr8b^=bq^t]V*bRr+J\FnZeJXʦ7݊c.X,Ay98BHh4i;SEٳgO:^-;SΔʥGiZ\fjjJUՙ~̙3lvqqQ۷RL&x㍖Ջ/XV&''av= j+\W՝;wfYC7z}i 4F@ (J^, Cof ~GGv[[[eYǏD"߅ihZ,\.d\cǎ Z[[Bpa]L!IZ[[B\\.C?^eU*IdYT*}}};vqF>""\.jy\/Jѣ+ӧO{ƍn{Pl׭[wڵηi6mnnӈLDD(A B X 94p+ǃ1]1H RUU%Y&S*)L%IdQh gv?SN]=^nk:+ٴJz,K MVky] B#L`BmqI.eYcqq\.oٲeӦM?я'> EQ~ n x?O].}ltlg4kaBkqq1IWïz믷{'g2{… mmmwui_׻Jڵk! ޺ST<ߴiS{{eYNf-//g?koow\ýP 4hh [V׽^oRIBt:D`6u<MEQO*Vl6};P=O4p±cǖF|> Bz=E"M`'P*L 4|Z38x9|(r1p8r99Ji8,B;vhiizjRdPWWKKKKKK㞞˲Ο?(ʆ p8ԧ>;Hkkz]uߟ0ƐŴn !@Bb$oZ&TUUT4M8q'vq9ORӧ}SCpc˜q.(T"q(a1o{UU"3,S4ͮa11θbvv3rL˔e J266v}z[Gԑn"DByEl DeXA_w}& oŮι' ` D"`&%JD[l,׶}t>km^ie[D$D`LN=s0Qw zzXq@fTEQ\n諫7E tvuV+U(& kgff|>wzϜ93<|xtt744$n0Vkǎx|ll5gCjP'ێs8WHijjr8r9K$0BB|>٬,+{^ J%[[V(bZcXWWמ={ fbB0#p,--b1t:+ Ȳ\T@+ POB3p8._m۶B099Р:8`0bY^^jhh(jaaX,,╕D"QTxMӲ,744;wnyysssBرckkkCB bSSS<ointR*:~5 ~FS,c躦麡<.›1U[2`gm~ps!UiAQaB!KB!#c֢<χ?hu+ORvZ^^ \)[VM! e~?BhzzD$9s̱JRWGBp8LQO&RgϞ=/#+ MMMnn剉?C_W>Jpww\tBx{{dt:r?/>|СC/ hiiyAsx:::^@ H$t].=G?jޏZPC `{{###T*J[fkj___0\[[T*El|>=dZS<al4!әB ɲ\(N'ƘeYMӠ8c $FcXRi2! i8v~Hknn+++PZBl6F-$I>QHzbb4M.2WVVm9Je2q 5 T*MMM=#Jed;Vhӭ6hK2,y5]j{O! 2ulb4MrSusk{o{{+.˄u,òjmm 60JXNWl"1B!H^, ٜ!Y%cXjESi0Bg9e jh馦&s;3akjj:~jxMӵc1N+|Tjbr@nf;;;:WVWtKsˍNGL&|aپt*5~F\VѣG=c=V__/ˏ?7Ms?s_v_0V:Br6녮/[,}y<^{MQƝ;w^reuu۷Oz+ B'NT*_|tB$I.kcct:1EN˗{zzSnjx?j @ 5F{w:ɲx@ #獍r$516 TrEa*EQ`fV+c(.+AnZL%Irx<M E1C@Q&eB$*Ι={˗/3 aQ.!ڕd2 4Ϟ=rٖ۽ 4 ht`` ɔ۷Fe憆7x-,, ,,,?Ħi׃$F\z bLp9@67[ϳI4R5ZGa<\+"K$EvX,J:+#r4S7LH30M}PÍ avYUtðB2 !flBeسi?gD>MW.\p>[VC6bεU͉){Ɏ90!^Z=++/^\\\ B%}cذX,V:7S/===Ӡ48rxOOP8VG-ׯ_rZ& V5d2 Ե`0t: ӧO' ݾ}{*:<AGgϞ$k׮eY8۳g_kjCj)@O0d&σgMtĚL&v8p8X"P>lH@A0$XUUeeY9Lt*-&Mmmmlvuu㸺:P)haπP%Ji0LNN;0>/JI8&O]UlSSS]]d#yMӎ?r)nnn^^^ftzxx<Bi@ 0444;; :;;5Mf n$I$ ,A,83o6[EJC7?TM#>' /,~|,"DSQUFTY8@,(0$4 +*8MonnnnlQ76 w111 9bZ?}qqqdddOxwjJs"MJ<4A-gWUgـJZZZ8+~jj vCLb.//nݣR :ye|3<kEjnj @ 5Fj//;whIJ,+} \.z0L}[[[- !tRMJ:8AFd4p8 tp8`+T*tZQFZE1A4 ,Iah4r^L&cZ8{<99Y*XݳgO(7QWWw=\re~~;vH&+++bG4o*i1@Unajjq xҦ(,g !Mwtt][Z^m6(D"0t#468??@AR 4P<_\\ewaa&R0@ 000H$FFFիLZu]:uJӴ^I@8z=OsssKKKPX[[s:',//{<\.8x;\.J9vX6={Zj@YZZ(HMMM'x 0x +333bȑ#gϞ !r\l6AcUEx۷o Sccc2 00BEY,^UU\t:MlmiYX\ qǓD*e0M cMB M#XMRJEQ!t,fYɫT*ʇYe|FGGx<`t{ױnܸGS4Lmۆ~D2t 5PÇn#!r~+J4%@Rq x˲ !^V%;NJkjjJ} RF2 ,bL&sY֚:::UU]\\z}sέ755`\ ٳgs;;;_X,p8σ9Nܹ ÊC[OOC=J^UߟdVVVoܸj& ۶m =\CCfp@QslW\r@6.b466yz}z`i\aZ[[w191׾/IАn_ /MOϸ.M`r,(0FSod6VVb444? ((.aׯz+J,Upr10 lp\˿OP4Ukj+jȟٟ⋆a,//}|q*˲ <}Š_z x8ԋw}ŋMt:^T*e2.]d(e.G8VU@̙3~'}饗xL&Jh3 CCCDbaa+)rSSSx:T(FGG= h\XX ;v|###B DE斖>ϗJO<#<\СCXt^xy`p={v߾}k׮ ;α1c}l4y:|d*nbC tmXX,0s 2lt]P(lb,Øiin,K`FE- "$IZ1ư6?@Ճjݸq#sGQ<O2ig *`=zӟtGGmj3jPs `Bh4JӴfKR >He~~, ny(P"?JA $ Dr91GAg+0W@me&gfǎ ,--zr>}pX,nJ}>_8ua2޽{wb̀IT"l!iNHwW?fovkkkWWH4r9M`EWMMM'O$5ɲ<88833j{{>kf{{;|T*500`۟yj|HZB Ü9sǎ(gMkk+!̙3lСC@… v}hhv9sffffǎpAoB'xbttܹs!u]{{.>kX X__x~~eׯ|>D٣}ݧh{{tTڹs'\L&s14gff|nH)D[8dckWn9,ݻT*?^UUUU F1y^֖֦` r$(r,"ƚi j Oj 7<%j5>c.\8{#<+++>C87nidY8~(L- ;Ձ;n>}4Ͷ ;sbbŋpxǎCCCSSS:$)Hx# x^4N(A&a_[09x={_5ASq(?ɕ۷8N(Dž+XPTBe[n(I0Ƣ(4 riK9NJZ}KIDATi J1ՙI5M_4EQL:(iڒm{4MhKhda4Ia.+H7)ˁn{k7jHR{u @q+6mhhA-MtTg!YìP(@=MЧt]V7}tv7a@_QBHSSid20"Xpvpxem޽Pʿ@(8NR)qܹ+W @ &]]]Hdee%tvvR)0}@- ϿNA21>>>55U(. sss|P(lllJxxxҥKt ǎhXV>(@nF,O/ "vN ՝;X,ܹ7߄0T,;q0Nuƍa^h5444==X.[ZZx.V,5!ˠ Wx)O>#---RW^Y\\yggbD" an'Dl6Z[[r`$"hA:8/k|`zqw[^ԧhP?:;; nlv甦ifcPqZE[mƈ)&A PM((eY<ȇVUUtEUn&&&3٬i]nw(lES4eXwD̛Q4u DLCM# }d@q,BHU50ˠhfuaf?/Z,ޱcj>OjPkj"L~ _i:@(AZ[[!tm[).CA /NSVPE}PX?7UpiRT \L$2L[[["(|>___( ,+@fdH|୎hro߾r,`)B>g !477NyZW\q8w>w$]zjB!e3 @pluuu M}}}ov:L&;vxZpp8zoTrFArv=???::JQTggSU5,//Kt:Vk[[ۍ7l6[\|r @#GBZ. Tp8ι9EQLL`鱼rrUT*|U ]BE_cal(vjz`B7޸g EEQ"VU%__W#Y0"1 9SuMUՊ"*MUK T{bNnU!uKn~wLa'~3?1H\t hWn6@W=e9H}BH*rk7?L+Uba9n3{`\|ippPʩt :Z۠Gz'<%T.x≏&~Pkj6믃x[0|j҅ b Jvl6X 2!Z, Ai~nd2 @/(1+0Y$I-"BT*e($mllexGJeݱX, 뮻־&Ize&/--E"N'7H(۾}( X({z`0(IR,uuu`'!|>O~g}ngنձ1ۍ 8!TV!b xᛠhue)EQ"Deq(I7;[+?p.c|w޽dTC 5O!+L ><[s`0FN'l6 ܁I6X,O`X.ax+` )Q^7l6Ya# @*a P>3 XBxN'BHSOyO}S׮]~(W0 i P뺞wQWWD^/ @MSSSsss~40GF1B}BhK`o"S׭8?3D@#hI7 P"lGo,՟x8y"bae Kdgk @SrkY3L}}T.c*^Wn* n6e~ Qkj"n瀛 DB kq\6>$I,C\rmn lx]f/>P(@+n;JA>+8q\RL&tp8`a!cZ}> x|uu!dXy\__`Zwuv'OB /R)[XX( 6mee… Vuǎwq/*M ڷo_ss$I0_YYikk;sL__۳, ! ب|>I[06M3NRrȹs* d8N0G0lhh<ΰМ@Ų,pPPUaRiaj5U!dFR!&! p)Ȼu]V۷o>ydxiaYEQt Ce6CSi4nQ,!t4  ]Wu0 ˠ[4VoBk* )(Line:iAOO!4M C[ zbæqڭ71ryGffUUŦILbn5MJ(+,G"Jҹs@.\dP 5PPmTPBDi0|aU.롘L\ TiB8bc xT"@= >Pp,\.#`QzVÆAt:mLӄi: ! @`gxNB~PT*]pP( ;˲h'eWG9{Fztmmm <8rBʕ+7nxݶmJ( ={oiYVBԜۀl`!AM-4[82BjZV٬=իWp:|a)tuu544KdrYS7@) 'h09h ˜B0qiqpQ?A|MT8+aTUuN4h(bhdCc4Ejua064EKT: hpx<ښb d$D$f- 4Dip_1 EQŜW5f1nhSY! \!W ]Z\E j*BxCو8frp\r{!. @%@ 5ZPC  ;07 *Ǩ*q@GwNH'j5+@ a#N 0#AYT)UU ðX,L(`L!qrkfH7OXhFE8'BhB90vSU*Qjk0q4VufYD2rU}8t?ߘ҉Yx] 2(666S,18Cĩx<7ޥK`X}B5PC ZPC  r TolE* u\P5}C!0Y/Mv;EL3L(E0 de󼪪vX,B IUU(Äk,(k-  ,(BUUUG?хUvollXCh0d2UV!JIQH$277J=~{3wjj!ݻaW_}uii)Y_Ӵ64A hJ4MQ5=>v&3 r<n3ZEq4CQNLt<\T|>/2!µFё?( F{=sca(aS!&B1dbs\[\ :~&t(4U5 4P6i!^h^*~w~Н DLbXh)-}]HT9vSѯ5dJujVpP+wn'D0> f,"if.$IVdNNN*B["(vttΈ+ AT*w\.ѣGmzzz2LTC 5Picee0:nb!JiL&6sB(2M\0la:].8|>`Pe JQ\.MMMb1Lf:7EP(,z}}=M`& a?l"0]M~a4U1qZv3 {괻[Qt:Nݻ{ׯwww%?೵5DqrVWW9r^tiqqt677Cf|e^.lt$:1f r󪪽w)CIoR1A&B7As(li≩;w\.@jưVDa s cbXD6m"9`W0YQ41ubb 1ƘBA4͔+b$96R*2YűA4 12 X,Nдefݼo1A440,ﺋBi 2tM@W3 E s%1as2q8:Оޞ}잟韽<~ʕkԧBW.\rJVbU Q,{^YxaD021B4ebD1u#`s "EļLВ+S 1)wϵt?яgʿ~)bEWT4}_gsxgY1pJz%w:th||wu/Cjn/8j><~ccCjZT2L0dYvyyc 9 j… ---]>?i0X,Ԅ1um،<7 덍 U YQRIE]'|2͎BׯC\qXDlb,677ռJOswȑD"|!?d2r@LzO8At<!BFFF kbbɓpA YQ d2\nS&1鑉Bw'9aBӦaKp` &懮,ƘSX"Si64}n7D"At 4NsM$ e$ԴG abǓ $Q2YTmı>лJKEUUXP-J7yh6D0^/FX3WϼOg\_wMdZhs!E GS +Z,~? /,bddcojۂ QT>Et@ T+Uew w?0==G_S*N*C?ꚎLDaeMnCi%+2LggEQHp@*rSP(44445PÏ'j @ 5vUG-0Oe?ZZ|Z?x^A@QT:V2pJ~]Ąjn!X,$yIx'OӢ(?DiBꪦiKKK;ڵkBAT*f8|̅,K$HU={ >}zqqtLNNrN8BZM__MӋjUQ@ fv; /--EHRrɛaDS MS46("d"bhLaLaL"e"(L0yp'@&S#,ê(2I|,MC_ cL!D``"BŰE =&sA3uaDaOg./6x"qxnl?/..֯{~~볟\<P``" xlnw;+ׯ?Ng6=<X*b&`I6[Z^(K^ޚe8a 8 A?,C!|Puc&X x *1 l!NzOk^_~ IIff{nOkGBP=$4td ~vIv;񸪪,˾7o(fXF5O} jݹ#̢/B!J8nhh(UwuT(x٩l =?z3go6pn -uMk"Jy(Pa.&ͱ1F¤PneyJ0 A vY(!zΝfΝ;An~@ ?,Z_C 5P텢(R lchr>!])r8DrA ^@\I; =dBT\ޱcG.JDDQ0˲T lM\][]Y]Tϰ% nO/8nyyׯ;@ 0<<\V! 2 *JZlXR8˗/R)I!,744pt]ԓ M Ty,k Ly|1ˊy611(02)JB#NFD/<'mJL!`#$0Y0BWN/ c:B TU˺4S*As?it*`iJ3(4!C0LӠhZNޘ:rɲlW/_D"DVB}QU HlZ-| X!<F1a2;zDWb6924m&!&-W_6A*#Mıc.\z.IL]]],f>ַ|R={b؇ TC 555p{:1%ITu00D|qqnU*AQf/B<54(bi "e2 Ӡ1iB0 i[d|#vJ2vI N6 g2l6 Q? (Es?obi^B1WVSUU.h6tK_0'_{FtPz>{Ő?[[EQ&q9nh&BZ|>@Ea-((lZ^/A8L>-/B^_90 B<aVQ[Mթ-c:)s!4M[^^PUuaapnY'&&&''\.w)M8쏊-jpQkj6:D'OyF0sVUJ10Ar'IRPXYYl1D@D0Z3ǻkyy9ؘL&aVOa4D,ujlr Mf[XD"!IRKKbЋ/ `ۭV,ˉDeY]+ qpX @^mnܸ699 g1M9sy0+Wb@<|DQ7x~oR\((lbDapj UUiPY 9yTٵ`FHjƃwƖ{}A^]wqk7_^n,!PUO] d E2nug>!Q*4C/(gxJ>s9H d*i (9RUElɕUS[[_]X{ld1p8i@1B>MQTCC[hA7ooRzn*MB/r+ yb&X\Fu?D !dL#D`q4¢/11zND2MBQDWt `Php$ C l7iº|> Tc9bX*+!t! Q oOLa񊢘cǎ;#)nvvp>33RTΝ;'嚟okkZjͨ55p{~tп1 PB:](Y`@vN|(J$Pj (VWW#Hss3m i8CxJ`! ǏOMMnKĠKy&m۶fA]1Zp#˲ DRl۶p8\.w]V/]l6{G"^/ɤ(X^;vرc6 @WWWAxw|__oe$c_JbhfUW+JEGsG6Z꯶#ע`a-<0Eо=wsw~菆w_⯦i Lacx>ppRil]D1,0BQ s`"Esò*QUUU+'XR6յU$@1EQT nKQ1. Uzt˜"D2_}E @*Ķ~b)M]Lj6t4Mr+k,V)D2Mf* "D麞dM1a4Lc`PAZrAUMf(8L L{ؗO~?EWגg91L Jp Q*afeezkQ7sBMEQ4Q5SFB&1 ݊Qy-Lv9?10UVL&c֋ .!aOO$ȹ &!_tf75@f5fffx m4ME"Cggg\aPC 5|0j @ 5FL&(hMӄ+, !ȍ[YDZP(J(,'ŒJhpxaaeّ՚J^|EB$I. խJ%^Ipn94maadR .l=2S$1a*r5X_я~!DQ; x?S6 So;}Gޕd~'>.f-~9&hiiiڦL6U\ȡ&B4bB5*m=~rn'a,БIx#HSL.j +kPQ0 c4p 0-X7㛶n"C5dgA3 eQ DDUX4L7ЦaMHa!L48: [>V i(a‹P幧xlLbO10eX&&&bGGǁ~_Jĕ+Wz(r˗aPC 5|j @ 5^d!dZ!v+Ei8L8LT$)r,Iit:R e%If(a]C_UD"EؐeP(׻}5Msaa )vcL&RT*OXk#eN'l6[2㋋@իnܸalapppcci&B!|>855/> Ν$\.éfYpuM Ʀ+UD6O}1}P5, LADTBLIr;ї0LMaTNt S# ))h?`|>d:'465fol6uiXiLdzzzaafJCMm a["az=t&L&!˜aoj$X%TyÁ1FE  $&&BRc,=̛ !ES)@(0AUUO:u4 pԵӧ٬fknnx<ַt]­xQC 5ORC P(0GJ.r@сo4]*TUE(`1$!)*˲,o&4 9\ky$nrǀHxoyg&˲#x.--eYӹ .!H[pYE*P}>h"#|YM4˜aj"^߈Dw74qƓ㗯kk gv,qBniXBEynL#D :FxfaµyPҫsKj,M 2  MS&IP](?4~=0itr9mc o0 Bȕ+WVVVvo+WʮU۷Ã?j @ 5oRBiŽn/` ^uuubtrg٠^6Bk/Eh@k٠^_$>I^Rg‰ÄirU*T*Px߽{a`0477;w., ; 1M$`<0HdjjJuY0q۷oGT"SApZ>@bJ:?33c- hkA3Sdو90gXiUM뚎YvejU{zx_{*R+/OMLzxocq.g$D?pSrP8n}}!Py\.hZ-\.z@Av{SSK ðX,4ǁ3UBHggs=W__677?XF[[[5M[YYDq㺻4MpB*jnn^YY)_\ZZyl0A M^,[;ڎ="#+n\iM"+<;mg޹ydeE-?gXV\.kܒm}yU"G k(k)ln(\*N0· 6r k-4,K#FSLVWTYך9Ve*Q0*nn' [BUYX,}ߥ}&&!EAp9]r%b+eP{{;Xŷ~MI(sӬILh(b(9aJzƘh(Ri}}=E6"y睆aDP(t^{T*cg2Qo٦Ν;꿆j1DPt^`cT &\cdY6MS08?PeMM0 7eY1M4\.!fnHڂ 5M bCS8QۦiMnw*x呑@ PWW711$@å|cDaaaatdd dC*pѕo?IFn }G~4q\Tŭ*A[]S H_<9paLHMCOOO_rwIRp@!NQȻ-Qa8DiCӁp8f+J:. о½MMM=P";GFFFGG[ZZ p:;w<}t>?px$ٶm%ɷza߾}۶mknn9SC 5E@~81Zܮ|>P( e$IP?y<HԂ9=Xs "DB^-fBjBl2[FKr_ԧDd1j ;z1к}VcS]ARDbdYUUFg9$t:]TVJJYbIjjrNgYb$?6^bo""A|w;6{aBBnKoh.kii4Q^o}}1 SUJF<[EhVnf qH N>{'N $ɷ~rY,՝;wn߾gϞM&mmm^wmm-m۶GH 5PmAR=G=fv=ydY h4rFe`(Baa>TAx<ᧀ( ,K{{;(\ &m&ŒdXeY AX38xZVZZZ!e 룣U4ܹ3|SN1 TU0 ҥK:w\*:z(8bAV&F\:Q(L]taʨj ~N0[>BAD1a'P (0 )v\*}k_X50$vTY1Mi0O߷@\a|>?J>A!2 B7 V+di eyNxrf4V\oo%I x l6L&rR ȟnmmٺ:T*Ivލ1~w ؘ(W_]\\ `0sιk׮577f2\.oQ۷߸q5PC 5|55p{!2xov8e9N;N^u 6,X,<CSۂjJrt]mmmt:<*2TJEUUjfPb$hKW ea,rys 6FIhXΞ=vzJrڵ|>oZ t299iZyH/0FGG+WܜNyO$d2@|rR|72̑#GdY  ###t^؜DgYiZ1u>9++bD0| A˙a: 1H| F%0o^qM({ eYvCј& ǰ" ,ŲyHTYN1E3 ފ&i<(0tB`ww4!("haYЃ0 8CS%Zm |'hhxd2uwwǏZD"˕eRmRBMwww_p! > 4M|#`r t=z 4FGG (R{^wwg?sd+WJI-$ QҏBIn7j @ 5^@?1Ke( @٬Q/˲(꺞Xlj;g2IX"L&%q $20e4Mfa JB`}1Nݦi^z5BL&sɕ`0hf*8B*˲wyJP>+]wuw@jcc- G>|xlllbbӠw`Yڵkz^[o™/t]7M`d_ /!ݱ+nKQa˸GjJq+r<n`([vὗc2!!t(I0݇5mSNMLLD"}gJ`0C=z^޾8<<(l6+K/ 믿q//?L&iڹsn>j_ZPC i︙TN#SYVQ+ U. BCCj- 0 cmmtF=OP(P6h?C&(<EÑJ"|>aL&a($I0cM]=O$Nv$l޽ l6²,JPPz^Mզg^,B0TW^illT*E TՇz(?\.Ǐ`sw,.. ǎ8nqq\.r9D)`oͱ_}aa{T*pavvv>`;<<<44LNN>s^}E"l6ޞΤc//NMM\??lhTeaop8D[[ !_~'xZ}GO:Ozyy^FDP 5PÏj @ 5FB4Ms:x|VHD`q\8rX `Yvmm(i&B.e9 |>a5w RѲ, $b1 A* !TUEf\92Pz6b;d2rEUx<~E=1b061EQΝ;($E"I}Q:zu4;eF6{7xCcc1^YY9w(lR| ߤM;bHq8deY<DiV* y O69@$)H,0dYߟNI߻wׁ\{۶m|hooooo[[[|`0X, {4Mw}O=Mǎ{뭷fffvzWWWa455D!@뮻yP:)p&D"=Bhaaixx0zcCPCCڍ7&&&l6з9xC[/ύj?n/*ɓ' 0 [eu:HԽT*E0޶a6 !q\*rzaYH`TLOZa$lT*<x~f"h !(7C:$0~dßRdZ`sNI\RTv;0MMM4MU*B{A,KӐd !BR,..b1!t[['&&V]mZI m^M[Qo- C _0" ,'ɔe~L&r_WW*bq:-p\P|>v].Eb t*Ng"kkׯ_xŋϝ;722222RZ-==={#G,//_pap;566y82%@0d;>l6߿ʕ+.\p+tvvJ[X,;R鮻Z\\|g {n^{~766^rA$B~X,V*92888;;!" ggg6໿A7#nZ.QA,CPCq\.`cR@ ,NiTUS*< 677ۓ~m||H/ J(*͖ކѕ͕A.~GUU-Hpקrގinss3.~/ ׏]ӵ^9V4MIL yϝ&~ c[hbhfhd`=Yx ˲ܵk P c^nooBH8BH*rd2qefffjj/]<6v}Չh4!2M@-4fN3 }_]]}q3 0v X]]u8kkkp8aC0L<?~8˲~zz:|UYCg>=x=s=Bh4sNe_}նh4Lyj/˲ݠ('?oB^HRaLMMMOOu]^n:ԩSCCCF"{\.766Ν;yD"!K/iZOOϡCfffE J(čD"u8M?~Ri׻o|;vx'elii]w77ͅBĉ۷oҗx^oXdU%o;wR]vl/-- ՕN롱m9??Ͳ[oL&8 Rԫ|OOl@ r>|+_rO>dooK]xeYFdt>cOnii9qđ#GjvBH\qF6EJ%|BN"ZBus߁`p`3puVfR(Bz_(aTo PMr My iN ! jb x@[[[cc#(ݻb6 ;gIAVkkkիW!\.\vb477\>766fm67'0{rXLRbxxnllR9BpܹX, BH:V$ݗ$IU-IN˱tvQmv;q,2  MS̝hodE.Jdt&S(Rt!GST"Peuu5'hmmx"pN,ܗeB@PNӴ$IvQk`988 ժv B,%J KKKKGG:B444,,,Z֡grBtRISgyor0q 666J? ٳg۶m.\9`LMM% Yw;;;}'x̙37|I|K_p?;;0//k׮۷G}[ZZ+xJp8o߾\WWwA.\cXcc#vرN `"Iҍ7t]?y$m@}}},kjjZ۷ox<ypxee~СCTnWW׎;[(ѨipС|؟[5Pmjbqq&''1Pso/ U4PZ:N B LMM(IL~+s쪮i{fFP $1`c},mhYI#F=s9\pz=^-m=Tx}j4XZ`0HéT*tZRr93cKRP(r989  PǢA l0l6;Lr8t: Y--f766JȘs8L&ROrd2aARe2ǝL&X >o22P(vܹs`pii ۃ\.P()~?g|ꩧ]. k c04l)jZ)drXJFI&j:FT* W.eT"ȤrL&I$rV%bJ%6rr9XL|>vvvjj*Ja_׻nP(mmmAr|>/4M,;hB춧zj}}֭=^RH{SSSΝX[[܌D"d_d?=:;;~X,vtttvv}xGrbh6l8;;;bX,F"ɔH$VWW4MRaXog =>?::h4VWW&D".(Jd릁$TxL&F' }}}FB(}{`pppP.ߺu n7og?[T^{cG]zER4HlnnD"P z DB}_N7M&ӣ>{uww 2|ƍh4QTVVVl6[*Z\\jjjЪ[h_ZZQ~j5L>U#+ HDR3 B9ks8j EJzKJ"0lagY .L ck0Fl@ (K$Jo(Bq>r&E M~8N"xh4:db1WiեVN'8]d2ٳg?cccpXVLk׮"J%'P(O$D2\.J |P(\NJt:M&S C3xVU&i4L-JcL*El6涶b7~>)F355E&VkggH$p8~?H ###\.wrrP(vjgfzh%qRIP`0o].z!+%hP(tΝa@߿pʕz}r9PڬV}wڵD"qٮ|;SՅBaiii~~~ww_$1@ }lJ_|FJ̜:u㭬0 DRVFc&qt:ݻ/_h4Z2P(HظpBoo/J]][h4\QzR(nꫯr8U|g_xzR)Z̙3R7M2;I4"/B ,Z:{aa!J5\.WqV.RH$ B$f  C&AORjj$JRp\n4jzL&l"|>mF^G+PTC$@&a*h4PSTX 墬G}677Ϟ=K&"0Va' .{  p"}\(B B$A/hT 3g䲹@0zv{.e:jBb1S ^P(D"F#&-dRtR9}h2嶶62,Fh[H$lG2J(K/]-T*]^^=X  ]]] =wÇRwD(>Nd+++Vq83;;91zP(޹sI }hSԧ?ASO|)vZ‚|E"rmooƊS"t:I.\ ^|EVo>}Zӹn / hVhjǏ7K. 3g Y6=77Jy L:`0X,! áCL&H$<N R5X[n|ׯ'X,f2yx,.wrTjbb"HX,HdZv?B -oVB !H$R,RT*q9L8qX {&p N$bJ9Va^[T D|^(J$.P@N.Z(V+9"H$L&zjU*#O#Eas‘p(2XJöX,BۛL&=MRlT*wttz73gv|:P(J#B{7f8&ɝ vU* E2p83<FwvvSN:E&xoO/ˍEt]"w89hF.z{EBa8zrrccc]]]:l6K%pʥs{Zyl&c1>D"p~\":"VKP{eN'H666]iooO$nkqqQRmmmE"T*,#cczr9c0}}},+~s%.lv``mދ/xhr\ѸqF0@Ν;CCCHd{饗L&P(\[[j͛bxkkI[n;Rp?ܹs;`0=[o\.^SZuaZ;::w i t @ Ȉt:-HH$h~_ $Ʌ.[TNP(?ȑ#?8l}l'Nlnnb1a<#[[[D^OLLlmm=t:ԩS _K$Z @ -ChZ=_p"`؀'Ti[.2jB x~"%T*axn7v̭G 4HGv=t:ݎcL& lYP,+ bqmm- a L&X"2 a2 /H$Lf{{{VRHR(G^]]:}{kMRiyyJ sȑ1͖dJٳg C\c p8J\k\.G677;223ݵlشLLL|>6 B6$333$i{{{}}=wvvJRXܽ{7N_?vZ& Νzׯ_r`jjccnG"ӹ%J9b6b{b֦q8p84p8v^o$zbooo4d2[[[l6Ν'p8(U*y'-?zs666N8d.\@"b1JM$Z޽{wvv*>ÇBH$RYT"ȹst:ݍ7NBHj:>>aOi6֕H$ Hd2^/B v/$ȦR)J%3L,k4< BJkD"R) 8N\^L&MBr8\M8@&1a†C\.7 X ʄRMb0̙3jBtscc#߹sgjz {p<VV) p2̽{z=NxsssnR,--R'N4;wg;vlmmM";wN"LNNJ#H$'?i6AP*/r\.trU*ӧOEVP($ jڱc ̂*JXG^p8` ӧO_x1jwttܺuV=Cp8\^(^zISק&ˍD"h+L&jo޼)L&ܹs7o$R/^ /T}}}cccַfff FD":\.B!H!Pi6r\*& Tj6- <^'IDR*z{z%JrT*AZvh4%LF(8q6[6 ` }{f:f+ a4Zk~ٿZhMjZh#DV Bwޅd>DR(|%I .r7`+ #f H@P. r$a@6P{l B*ۋF !bx"b &)i4EL9DH$eb7{{{\fSF.,,徾>:.X,P( @"n߾}-DrI x*۷o߻w/H(1)|>?ϟ?J.--AvpwwP$y'~BH$åRݻ wttx>p*Éb(dI$/@ F@={vpp0Lnmm1ǏF6S]]]T*>R@?7nܘQ(\.waa}S|A8HBmmmfHgggoo. F>oxx^D'p;wnmmm```ee޽{S7xmooJ%Ѹd6rsjp:Jv_d h z|l6rÂkllX,b]G&(˅w\XD" OR?zGyڵkl1W*Q&JbqWWןɟݼywwwxBP(P(tr9 ৉j)J(XaƒG"; #ZH'F"D/ftXhH Y.t B4J60* L~$r p 9(C,x1IDATd666R2 ekk YGRdtFN=:R>33cfsWWǹ\.^8ڞp8|Rt:n# rԩ) %IOOݻwWVVVWW~k׮}zرc,&z<>?<2 fffBH$" h4wyG b)dfi4.Ke2{GѺCh| PՙLf}}b쌍IRd2b.H5ǏRCCChZ:s [mddŋ'\'wvw.^Ͽ[+++> Dqmkkr`0x<BA&@&a2ht||R@pSM].`(J`P.o6].W\V(rJ #G///b]sʕl6h`hH$@ t~aTVwvv` REhAdD"!pz'N$ HFb]]]W^esgx띝CCCҗ$Jܼysrr~v{!NG[h_=ZhN{=DAVd2 >p\_| ӏp(X6_S* O`M6pTؠUJT*Jx<~d25JEPtl6i4Z0r9Pjh4&IؒJR#`CP(VrD`VlF?!tHZ|2\VŢRJwwwJ^ꩧx3< % V dbuuٳ buun9rX(*~OOO?vCБ#G|>spS}_ Bhivww766h4Z6u\n{ffP($ɥ%x4Pd2 1 |>NDzt:}!ZY,X,N&UTX@kXJb BxRhAd t:]$-//|>#X,yS(=== '?\. Ax|bbh4P XbtR9::i[h~ujZh# /]T*H$FFtȑdrY">r9ɰlS*,wP%l<6Av?@@/ ST$AQQa~_Tz}L$FbHJeR .!VJ0A0L???|~8^YYQTZ6dDx<X,Z-$_ݽ{wuuT*f8Ft ^CX1:~ܹP(dp8Jz}>moo=흝6O|IǃhXU;vԩSbP(UjHzL ;to~;;;4̙3fjjzٽ;w\v{{\O~`0]`0B^X!"b۷o' jUN7ͣ.X,XV P(, zbCBP*(JH~'OROb###;;;[[[\lrٳ6bk.6NkkV50P0&h4M³5o~jZh#DٌD"7n܀!?g 7A%m4"B\jBV\.WFQՠ'4MrtRP(@ `02 ORt|>d2%T*JfYgY&٩T*Qz\[BFg2d2jLZVTd9(|'~]$ˑH$r9H,r8m&s LNN"7`cccyyyuuG޽T*777SV]][z;;;wwwWWW}>Fǽ ֓o6=tP\~uYÑf}DP(]F!djFQT&Id2?LOOPhggG*JJ% RDI D"y衇RCH`;::H$f,J%bHɡ`2sssAuϧRRTʄJeِs d2!ͺT*Aq!LE\ X<7L [{{;_^^xh5hd]]]e6N'+ʕ+Wb1t:]mlllr}:thwwW4{l6OLLxϟ={VR]z5NqPdAH$f]|FZ9sp G"BpYFzoy!.k49B -GL&'''cAP( C}T*5v4 \  dXA{f|ZrvRJ2buJ014 p8/+J`Cn<g0*P(@P(\.h`ggg H7T*B^WRT(`RRI dsYX h4Z- L&h4 C>CgϞFY,0bd٬Vϟg0'b1O|";oooh4fxETkzzze<.\f/%I)FGGqt`0x'NrH$tdD^F1͝ Bӭ-..l6+kU2/JȞ BZvxYZŵ(Lh4Z.Q 9 fj:jZ&xWWxn`X, \T*qBTf;vX> ʖZvԩYWVVD"rWW[Z> ݹs;Y]]*z^{\.{T*1q8o6Vla|K/TTRi.kooz_sjZeGH$oJ% 1  0ԧRgYp@ ҅d2Tzq 2,AaCbI&2P(@P($ (=H&ɠLkf 94 Xl)n \lT*z B#D,HPJbo4akhf=OPX]]sr\J,/e2@ `FݻwlD"(JWVVN2رc:.vttR)``2O5??QX!J,k4Px0a$|2}uu5yPt777lR\}vZ[P(ʉp8 :~P*tRFϰgca=h4DT'>1==](pvЙ@){^  >ͽf٩VV@^ٱlO^\\LәLh4T*>_.doo|WW}w֭K.!w_?-Z2lbbj~^brrr<;;{q=22wwwQ2,pqu:]ggP(fwR)aSd2MLLx<.{)#H,ˡCccc6?~ FsŮ.>ގ//P -ϣGT*5119:`H$d2Őp}kͿ7\. DK.t:L/--}h4|d2 Ƒ#G&&&~#G|ѣN&]]]3!``P(0=j\.#! *dH1\l`0@1*ˠkGSxl6FeL)˧|{{{XT*` $jG.smZhzOO`pݫgJtD*tf?~|ee% Bx {{{nooGy /RjtVe2N0fZ-Z4xzOP@CBFA\F|>i\`0J! j,\)WWWAJRL&+x< 7bdj4 .J:æp\&=EPD__JXtpf* sOn6~?HiDt0dڒH$ VUR`|`x{=hd2O~ё[nq8BT(cX^hׯ_/ 7~7N9{4s84f{ɤL&{gLk67nܠR`pp0[V1ܹCD:jSSSW\dOz>CxZ,a6nbYXXt P -+VB !j{D\.c Kݡ L : .;E1GNa E,NGW٤X)T*|2d2R%AF&hr=X `<%5RB纻bh4@PV 46 [[[.+!FT'N0Ů.\@Rwwwqdroo/~w~g}}|> TjJ?Rd }>mt:1O ql6`qbJ%A'p;ũ(JmmmHN g}Bb1 裏 _~B `2T E-Aش eYa Gl6Z :p[[\.r`|Ե e&`~V&'' "^FY[[rJrcch4=ztggh4fffD"L&;HjpMd2宬/,,\.\{n^#˝8~;w::;}H$re:ݝfy<@ ^TmllT*۷o#ǭZK nx`uu7\]]E3oCD"ѳ>`0Ϟ=;77/,,T}sl`0166^ jjf2L&300-R)Dj[h_AZhDNg&]&τ"Ë  dF s0 Ey1Cy9`+tEn4Bt#9JFB!m nkkK$bvT*`0$,MNB!h*wmm zz>;;;:: 㝝T*LFD"qСd2jd2=sss_WgffCl6&t:V<]+ h Z `0s\===L0B;Mq\)\.R ɓ'ɤhl6;77wʕ'Nl6CJPp5MOOO HR F! aQ/<rj*d\.|fn#T*% R }Zez ۝5M0\__G$}ffgT*lR@ɐd/ի0_ؐd0z:~ZWgR\XXDHdss4mlllvvvccL&p_W~:thnnnrrR,[ߚ%Gooo4|;͛X|brB$1W_}5Zֶ6ɴH$rLOOիWd L&g>??*/r8Yğ1'Njhhj:B rh5-lFхl6d0 r|I$,V$xR xP9xX,&@ ¥9) T&x0g0`C@O %&# RyVA dٽ=0yǓ$>ABNg2znT*moo7M͖NU*ѣGE"Q.7CrQ>bYo,s݉D@ @H$ Bg~~SNl6ݣs\2L,i3$ ˖b##z E"t===`7Anh4zJT-}kZ"8y$X@>oeeessmk].*˕J]]];r{{{!jd2Ͷb`,qZB!D2::С^{D"i?-t__ԔFH$t:}RbT*5g}vhh'? N _W^yOЕa5l6?Ͽ;Hd?',|N:u3gΈbZp7߄zkkj޾} ߿!HVWW'^]]U:nuuOO?ɵ_~Z>󽽽333`4MHVzn4SŢFd2[[[ԖJ%(ʙ3gv;<#yh[y晥%Hjm6[[[&ipppqq>C* #JEQ RB2T*l`0h4nFԔxl6j@-h4z{{/UN$rL&T*0{{{bR5 T*JD"/6\.w̙w~ _HRH|>h& z]ӗʥׯwuu{em b4cR}HPzbhٞ~R4??_,ׯ__^^N$<@C^;N*痖@ 7n|- .\ cǹ'Nx_/_h:E>R~iiioon7v5D RhQZh_MZA"f@=l=fSd2wP` &i0G$ աhV9T*h@Ӊ/d j,+N#IP(0Æ@  IROAJF԰|)˽"%X Jt t: 6=44Dvvv\'V ׯ_h}}}2rmooT*$DL&CwG}DQM&ǻtR\\\$uZ<Ǐ_XXh4gΜO~Rz{{r"JdZ=zfwttlnnXFrEQ\J|vPFTJ" _ʕ+~_* C=W^Ŵ>;Nt2z'p8 zGY^^nooAE;hWZ,~h4[FC|ᇟ;;;'&& l6?~?XXX8z(L79000<< -۷tz$d7o߾^.v|dkZ*LfffF*\T*uxx\.?s__dg'''םNCB&vxxxvv͛"ʕ+ɤNz*d1 }Bן8qV9 cib0]f6M&{ZhjZhE&d2ܣǀHgVC/A  H`X,3A\.f;i+u\#sF$ B3B$ ]FL&k4l6xjb2 BRtbp ZD"riLJexJ&ND" T*ngd۹M,ˑ#G kkk.J Fc2@jB=BL{sݳn{^zܹ zB=J~Ho7ǏCHB|>7FjnݢNZZZfp'wIRRTTR`0yY&911 /gyG?QZبVccc$LF"-(U*͖dW\|\._[[Rh&Fϝ;asa9ݭj衇\nلZbL&\2PhzA,婩)Z}aXB -GzoZ:h~N@"jX,ZNui\fXP B ,pua`JIp8ޚ, 1J%)f2P up8FLDER166r'&&|>?tP> @ 8c~Oі\bioofSdl6#L ; 붵MMMt::OKQH_~r =o߶%fyuN' /]$HN8|;V*^7Vկ}k<b?… kkklvsssggP(ttt⋉D4*H$[oݻw?\.ӟիWWWWFikkKRO>$2|>_"P(ߤR)J$P`X\.d> #h4wp8TJ$-,,@ Mȉ' ֹs~w~gcc_tFE"<O>ԔnrΝxbOw^{gルya2ܹ3==}I8IRL>==M:::@aBXA&AC.De||b @*"_X, d2&ɇŕJe{{ Dy. y.h5rh4F j54#Ji#GPT٬RL&tf]XX@.nx&QǏW(L&Zh_EhJΝ;hV_n຃4jvI&zd0Ly=y` 7 Cs"51ABa0aR#U T*]Dl,LJ L&ӧO? F"|~^[|bP(MFc20dkcc\.TELu:2,\.^8;;Vi4dT*7oެ?~Ųs-Jub8??/ݻ9rddddaa۳d29Nu޼yS*t:C?=55e2٣>:>>/|A@ XT~~R?{5T/ŋ@̙3+++>>쳕J%Lx^z饋/"]D"a}-Rr766l3L=͙gٜNg6}qh4Z(:HQ?~7B0::{9999I:r sۻtRRz}}}mmmw3Ll6; v@~0tS[[[bDcQDBANl6>}zuuU&e2U" *l6C,vvvZmWWZ`0Bժwwwb|^k6xtJG}3g0 >Vc؉'6M2YZ_-¯2Z @ -|fwޅ|XT*^O-@``Bg `*)/m4p#1 ]?EXbl"[X,X, ND"lvfEhU, " XYYf\.X iӉ :ni4Z (PFQid2DZ]] *ݼyR8H$zX>(J833s޽g}vvvVe2ž>`0 ?~fOMMn>Њx|H??RT=͛7ΎZF[ O8qƍ7xqi< N?NLL('|26Z677Sh4K/8qbnnnwwl6wvvF{x<׾ 0f[ CV5/˻djvh4޺u+|>|`0ۙLP+++X\r %htvvT*fsPb`fZbĮ뱣 h`ae0N:555=22rEXj% hd2YT*agM&766Je^Es\cccX֛7oT*N{uuu|nooGPTt4U(/2B9zhTtsssdX,šB -?Ch~;;;ͣ%jU#4Ԛjp|>FiM@cѢG ,F(Fh{txMl6J0_x>r  "b.QvX&܌ZMPloo`0SOJ'O * <c=oóA:~ԩӧO?x駵Z.n2333==x/.{QJ$u~6L-V7n܀^ywwwfffa=`07H>\hz~ BkT*ra``@P$jݍw. _}sb?d0̙3BdB|ꓟ"I7ryww 809zoMѶ?Q(۫hkZEhfF}ᇛbO$p8J Zf `0L&BAA\.7"fN+ |x@hPTtBA>j2(!W_(N# eхBaٴ-zJre@ H$t*a&hL&͆!x衇nH*2@ʕ+|`$v,|>pզT*O< RϴZ-Z̓T*4M,#2=nkk7o޼xT*=u;===bXmmmrСCjURuwwcrjL` zTU.W(LW_}lTɓ4M&Y,ss_ZhZ @ -| Hd2?Dц} l6H$§RH`C4q>`@{`:?|!:X R~ٔH$A$ *l6`PT`f)P] X,&|>z>Zb8J%E&3R"H&a\ gFGG`R Rp8|zX,f0:T*q\Dzzz].WGG믿d B7^z^z 5߾}G&~gݿwT*[V0# Çܽ{瞛B|//;;;d2_R;w޽k0N>*SNmnn|򓟬jVEbŻld2Pr4Jd2988t:VN|gϞ-J;;;bQL&|>,4|>XYY+ bBZ[[3͐|T* JL&sP(X,sp?cl6Lv|Ju:JjF$L&ǃ @ V,tٔdjT___јCod LX,l6|pOOϹs z~tzssstt޽{:D"ݕL&s8$7/,, V宬 mllb1Z n*ZhW>Z4͉m*Z.QsxJZ@" THJ&3r U !0LDdL&5VFK2 !.Cn.fd2QîG 6fY$V*FDDY0x<- Rɓ$ ,t:=88p8f3ѣ?Zo6)[^^X,_,^'G?K_͛7xx+++Ǐ>4~wjjjtt_K~퍍}}}FW_H$.wff?JV;~{キk1K.}K_7W( J޾}F}ӟFÇ?u|>_LRj#{ァRdrVTjhh'я~422aP(D~&H B"QӐ(]O|rqzo2,j6<;qQ(a{{n'dJZFh7o>4 FyK^L&sooO JD"QV~9X@^8*e>,a<>%2 d!P(ACbnlI$R\NRP'H$:8NpN dzp8`3h8̋4"(/(kZ<dL&#zX,v:F .8'NnTrL&QD"q]2R|+_(_bww7RIףO~뭷^vy6FFFrɓ'bޞny&z.\088X*VWW)J[[> |U(B[{/h˗/ $wttp8 &:KV>`uT*%HBFXoohJp>`0l6nJdr0dl\.tիHD$D"1??f)d* ~?u˅z}}NcAz=n3FN *b H$@ 0;;+ɰdx&&&D4ŭUTL&D"wAFq8`dE3==&6 s[nAٳXetvvŸZhW>B667l`0Je\ }T*tt:3 Lva{.C ^TJ9[.%IfbA Z `X@ l@3ej?11QbX,b QNPhcsfV $Hn坝===333Pɓ:noovOMMH$"|L& ~3g| _7tp _.[WW?~Fqرv6|駟b1dٵbj~ի]]]Pk4;tuuݽ{֫?я>AoFZ5 Rijjjmm <[b,K$vS(p89SN`& bX8ZjZJR>_V~+-ph4.tPh4 bkkK(|3Ph4ѝB f>D"Dj |ZgΜ:W޻wossu| `0p tZV@/._NxDBVs\P888pbB8q.FcwwX,fU*A03=sLXΟ?9,֔Je&tRټq4B9tе~+ٳg].׃>t:H$(@-¯2Z @ -| ɍz#r\.c Pd2}L&&șLf.C?(qhv|hGSTTd2~:@Ύb\X,Q X,1Db0rir !2A gxtnllXN؉bZ9L0x<>Jf$Qakbxҥl6`08r?H|D 4- $'ݸqŋjztt… ׯ_zsuww?쳑H$GQꫯ2ohtjj*˩K.ٟ|i;5<< /g}fK'Np\iX|۷o{<6sD"I&zV mZ. &PbX mJr<`0XV*z￟Y,L&cjT*JFR *Z*aH$vP(؀JşBY]]]h= f;{Y@ollf+Wvȴ>rHp84  "j;vl9l6ST$pnW(׮]C+HPVdrggܹsl6{{{; Ň~xnn7|塡!~D".d2O:uY.hbRDwoh5-¯Z @ -|$7LNNRqA`l6,j.T . yE%~ \.dU* t:A 7@׋.@Ph4AUBJ1h!BAX B rHQaT*^/χL{{fܔH$JAiCCCL&scc#xJ^]]m4B!lnn۷o/--E"/d;wf1b'wUՈo^Vt:N'$ 1O&.KRKŒ\!7͹\N"E>ND*rݡP_ۃ@ XXXH&:BX|>론 G&{<,H~ޞbq{rnooo#)byy9§th4f{zzBPT{{{\.wmmwtt̘L&DD5FAV{U,ofPx衇Ng:J~_T#(t8}4O\.D"!HVVV0WT2wۿ[pp02׿Zh_ZGѫW_~D"a"b~Qj ZPCш<@ F:\/h44z.JPdRf3L2d2vxO":::Bl4,Ks8ׯ[,T*e/D16*oH$VkZBf<GtPVTW^ML&L& x<~uD"1jKKLswwvÙl6OOO{<Hݻ3ze#ˣ(8Kh4_=LZ, FI$\.G&2n޼bmv BF/^ tBRJ˗|FZhf{<&˷~;w </_~ygg3 <lv[[i5-¯Z @ -|Rwń_| TpNPX(c#T*_*'<ԉr^2\T* rPCc&~(GT*|~!LFLKqXp_CCCRivvF1:X,V*bZɤBAPh4Z bF3guh4 L&3v`tuuVy^'ERdP(*NCXNtVFQ!IכLzn4 4"d b0j5]BA, H$P(r9A"HӁ_1W(:l68+):l|`P*Jf#;vի` q{|C1`0xڵJf0եR8ν{f0oj|`8ysN& BFf #GLLLn0|ZV:U(,ҥKkk8-JfJU*tvR) H$:v\.Riؒ Jr]ۍXeD200FFFj\.֩SvvvyXrNg>d2jZ&!P}bb^CeiCT*@ VcsejZ?G+ʵkbX^UӚN5 #HKVg2<mLfE P( YG `ceF ZF!HBD H$t`_Vt: Va޼VAr#\.D"04 z 4MѨP(_.i4Z(ZZZ2 h.,,,--t:Vx0L&HNN' HRʽTBzݎOh`NoX]]]\dp^\PյZ { }dzt:Jz*l֖N#*BzzzU* E.#`zK$0̷z r}9rˇT*P(<۷oB!V {uvv;w!M EL`)Jq)^o<Ɲ#W.D g2D%bF3222;;fǹ\nggsYmT?==ёH$\^:ƂԩS臃l6{qqVIRȎGGGE"L&C+ xlfC9 KRQ(4M$B!x~lT*˗/_N$f l6X,d2KR<X,r~QY`۷5L&c6Y,O"`P(ѨBpgΜD>g)H$t:gfft:$Phgg㡓q\LfbbBժj`0H"=?B1 `lnntvyhXb xZb`0,//DvFF`2\ZZ½fi4n__۷JFn{~~ȑ#t:ݽ>z(\.N5\|>X,if}}}uuU.OMM%SIr\H$m4. |>4r^v]2ߺu Lv5ժD"   rVp$GyVWpl`0( KROOOww??,--SSS},NW0D"d2Нx-¯ Z @ -|@qO&gf].5_ 0 W~B`̏ .h``~Q FP#h nx5@Hh*#|X\TPuEQGRb1N_ZZZ\\bʥRCvl2,|>lj**J;;;1p8If\.8JB+Vj|H$b6m6L&S*"BD"x>maaaaa!:T.aO&=N&GP8NPwRN$;;;mU* k4\.rt:][[`8x_6 x" |>^ocǎh\.V^3%Г<O<ooo+ &C1CřxV;;;cٳgi4̌h<|ҒvX,ZRhF399Y.m6[Z ?嚜{N ~nJs$ s\\nkk?VQT"X]]e2d=ztx.{JR7vk4LvMT*t:fsssL&fLi>b" A}} ?~@@,Cnd9tP${ggfA>%P(`||\.tB>22B"lbA~p1+++FCvӹN8ju2,JdR"8T*N:>r!'Nj}ϗb(ZV*TJ>|xqqfT* B.Ů_ /zNkB -kVB 瀚/_VF` (}!ޅ\.yuL<+ (:4 }A\.bHad2p@%aTVBaGGLJZvqqtx`tvvB3%AŢT*;Α.8󴷷F^z{{Ri(B]ov:9==-JO:h6@E hBP2d r9 ހ`s=hƃ\nVM&ZB=&=77'H FZj`oo/Lb1٤vvv<JEMl4F#Q9NNM^u5vᮮ.HiN|pJŎdX,P(d0P#M&S&DU$!L&szz:Hbh4!4D"$i``@J=4 xbv!XZZ2LD:BL&77r|>_(B$ * %۝^o> 絵5 OFH&alݣP(\nhh}l [h_ZhNRsN$U +C+-B9 `ճlt:` 79ʗeO# &\pX@Z(%1X, rz &˗|>V5ܼyL&l6d`Z'zzME&fbiooH$n?Cr֭[#2hqqhoZ,*H.KP:;;13FQxՍ\.vm6?ގ"R"D"eKnllvjp8=zzzf|, ~5NP(XVR)^o(BR4 ZVR8w}T|4mmm |LD.VnX\*Ѩ@ X\\x/T*UOOO<w:Z-3 xX,===8W 'OeَiJU{{;GIZV"UTf3FGG{' ۣRdl6l$E^.Jf|> ^`0,Jرc===?gjZh_Z}>Xmt:(wu$O<`1\gR%~6 Y xL E,U i0FVCl6aAP`A&e2YP, `yј#J&l~?G@ x<0U#lyNWgff,hKL&gD"&)˭V+Ld2L&tvwwX,VVz=3w|8GB'''J[XX`ٰju||fCϪR6Tj\.dX,DD>4x\ B~X D"Q($@ v\tJD"ݝd^'H|Jp8 A@5`0pzzz>V!Tim6Ѹzv*Lrr+++8===^[[_T:J9 qTp8ryT"H@q`4ŔJV4U*׮] 333r< X,Fr{|>ov;rvqۍo . 9ql6tb1& Fhtww__+B#LVHzN"-¯Z @ -|) Qiׯ(fZ؟eأdXC`u6Bŏ2}U1;v8!<K A-@r hdr.t T\V4MRYXw:rV`e!.d( HTƚd2Pn޽+N:h-'H$fc2Jh4uߏ۷ z>H$8 \Z߾>՚Jd2Y jzo٤Re24 ǓH$ZmR+ ̋D"0j >TT*rKǣbb4J|zjūoƱc&''cry:h<UZ^a`Uw;:r|yycǎe2@ 5A f  BAr:.H}~D27:n4 C*JӋL&Ba@{~V(>67756$BD"bxxx8JaIzbq,bf!jfiDfIJT>w:ldBQ RQJuwwmZ?-¯#Z @ -_?B0^vf7 d"D{Pz揘\fhb @] h0< i=h`GJıb|>,>ZL&z}"XXX}}}&fER2D 'BH$RހX,FX[[郃Va_T?3 [Z n60$Ukdz}N83P(,,,@{@Roܸp8 Oɴb2r!H~;X,\.gX* p8 ºL&DBܹsv,yx< J`Z,ZMfggͦhkkfP&Y^^T*z޽{k`LMMYBQ(|ӓJ~@&xRPǢ[CrZm<A "8Fjy<^ggT$!AXRb‚T*U*z^$EXj6f2oD"R4 r9K3F zfs:Fbk>yb1~!Az`PZhM -=x/}K\)(#Pc"MQWaxo6H"e:^ ֨B|P!+p8\.B; PX,V\Re@"fY z6=,\v#(B! DfeYXt4333RixxX*Z611d;hhw: l6Î=Hh4{q8Z~\D"H(G'Lx0x[$xYTjZ^/|~> MmZ.kqqQՂTST Oݻw$"H,߾};HQ`0LTHBQav% cX"Y& t:d$NudK)rFA2׷xɓ'Lښn|2F;r rL-XK绺& I&hYѣ6[n~P(@`43X,JA8N 8h4бˢPD"Tt:]áp'[[[Z .bJ%.[\.n6i2, lưn?T* ZuAkB d2޽HbdSTpw6ܑHkQA(ʑ >B`H$0  4 u `_:ehjE"XYT*> `6d6` O&Sh` ; {x%R lNg2h*J!Wի[&Ns] @fkkk2uBp vw\0ORX,FCVVVhGGx< Gh4&Lf4͙LfaaVvttBhdpل> ̽=xܻwo``hloozuD`0`@%rrBY\\RX dBdH$ùvJ-Enoo d2n6(L2 2qE"8xz<\. ~Z"x<\hqZUUB؀:U4ri4d2 )3 "ܿo5-¯ Z @ -/Jx<7n@^ x<r^ Bf\.~vΊ +NِjD" BX pkǫDB*R)D6L?bHi}>ry.j!NW+U ǭVk:FƬVk0\^^Dt:\.Qh\n$qrl`0vQE"rlZb1,bx.+ mmm=rJfs"h6NvLVTNVì?zRtnncr5>>hl6j~.%;;; +ʨ44kkkA%H(JJ`俳+T*JpV|>lgXx<^>- jBP(tbz`2JL,;rC̰\.W(pBO?lz=d$B$ {]d&_[[Ւxz%Nr9([,*^l6T*>AH$By|&T*[A܇[.Nt:0< ("@FK$D"aquL&l6@ aaJEyX> `.IRY,XXw g0T*h8X,P(SSS b}}p8N&''a!r>/J|>fsnn.͎!( ݭj0_]]U*$)9sr!T*aGgggB!\.F! r9t>|bD"Ud2iZD4흟*LΝ[\\d2'Ox"hr9 ;;;\LOOO,cXT*UTNLL0Z6<tZz0NjD"R 4@NEſٟP^t: <"Z{mm~_d_VB -hE@#("6B@rT piF h4 v!dael6/f >= !` avT*sR|m j5VUR3}EZ`IFPGT*ED^R2,| ;ގDx<6mmmP(Bp5&JrL&c2L&߹s'˙涶6X!e'IݝP8d&'' y<H$2,|>XV5M<j4޽{͡FϷlryeel6'l6hdp<d2* (CAJVVVz=ɜQ2L(nll#":n޷q]ߟ~ն,ذ:ME[@^ڗiB? ߠ}wcEn|e˖LHxѐs?,q g4u! j4s̅ju||k2e!0BH$nwiiCco۷nݺwÇJ/y' ,jA18䂬Ϧ>z8p}6 K"XFڳg>cg8`0^`8}\4??ϛKKKH !T]KQ1== H96 !@qR8,B#z!3< OH,jX[d2B|%D"1558N^, :gΜ,{llljj BN!JUՏ>X].\bvvށ^o4ZmvvTd&&&Pn[(rϧRU]Xl6>~\ɓZ077,-..V*O|wyy?DjՂ+3Rinn0 J+W,//I1>>>99yƍNZX,9:;w!SyʕBP/ "eYxj5jBg$XAs!! w*x(0MOxa@)O} %kY$o$)OZW Nl6^??y_C8`0#zom78/KRy˗/onl=q<8wX}LQ 8BT)C騼o3 }!} \߲,$As,/.|~ss˗8#\M K>wdX|wJ@^|r1 iٶ7h?֐t:ϟ?z'Oh|45z*K__^?}Զm.o󳳳?kxi}73gj5\MЌ U?y~IR|yϞ=~:A"*:bt&)O^ZZ >jU*Tz niYEw KjqA{8!XVޟ:jEnx</C1aԔ'~sN[ADyځy? 5 a ػ/_~ы/.]+4]=8Töm80Bry 8vf|hqR6PO^epG [5'_B RLxӁX>'iTBc& piXFٳgct:j ø~:GO:U(` ?66699yׯJYHy睕7n@~[n]v F&Aӧݻ711qZvڵǏ۶=??&eXUO:5??SNRt\XDjzyʬW*j~V+ɸNQ[MӜ̠C۷Pַ7o޼qF2|/yw}3x˗/! VXtBn85)'sssh (@@FؿaJ!Jp`00zbo.h?rܠo)}_|1==mP ]k; ajg8c2:üw2a* #E0uN]zxgg'@vzPH {{drss^_|^W*H\&_[[+ +++p[|v.CqY]ϝ;777)mr]r>?s˗/ݻ777{HnA #LԩSO<9seYF8{2&l6q.]z /_/"H,--!]ը%0ڲ" }h6zP(b1am嶶&&&(=l6IAh`(0#KF@02:WA@?55 h mcccfsbbҥK;;;æ+ȻCt&&&`d B|>ӧOWU+gϞjzMلH&vd „ (ƄɀR ?^{GYA?}P& :f&R!Āŋ_~X%B>uMυ`0Q2ї?_!Xo\N:Ǐ߸qcee.Vkll H[ 25GB6Cz`z |,G In@[170T n+q xMiBUmA aT$#1h4h`oK(b䉷ill 4Gr5' jAx }֖,H=6np\+BEm{;<@P(t߿\)@Db|>r97{9,+@_4]ӅzLHt|>O9 `0Gaq% e~B5cw޽Ν;Ϟ=ܤ JxCRd+CBaI1 8yz{˜&!ou,_Z|>AX(YvHt XҠ4X XrGv<?#b '|AE%fD?b݂4MPL&L&3L*fT*3aXj'˱^w#3>-##q13=U-A\Bޅ>qPR&arӅ{-..F|"q28#2<,B t@1@# l6e9  H%Oa` Qhit^H|>l6$)3զ}5 ݓZ$ m^8eCpqN 0uP>Ąix0|>ODuL&O3iPD"JD+ ԉ)պT(L[P ;r (c K{(#"hY i8ʃeYjɓG,..nlllooJ)?!A5ʐΧQPb1 щ=^BXZXsIRB/,p!p€(_\]]C?,Dg@!lO{d ӥ@V(HGk0 }}r\wTc!dBIC5%F"ϓۈߔ}`58`Q QV2%ʘD8|ӆ`@Iz2|U,+++Z,J`TZx}HN L&Ѻ rgDi|\Ur@%@=܊ jlH\)܈~lQ^ƗjhXc>!<_24_.q x3D"A%B:{o*LesL&}*!-?M<\?.RҨ+=+ `00`%x;fON's W?(ȕZrD16(4A vhzjqqoבkG&AdI ?x|+S'x? uhHc&0Zw0웉<')" B _tX kM#& BA !O>Cdd4(>'ή\' vIx4wR39i1C=8`0'{pR_=J[%6 (e%'4^bnqgii &㌏]xZ޾}hlnnVU"ܔŲ~HSw?& >J@݋AZYU J&2:z_Cw_Hg%f5<=qS6Et: r$5cB`X6ݘ&(o )_I@Mr{1+`̀=S=Eٿ;folD푔BJH$zZjB6mWUpb6KVBlooF\.733cNnZhu$K %@?j@nFtg@vʚC3::ȬPA);P/!K}OC{Pv8D؎%H޳,1>WC3 =qh-P1ȳ_H0{^{PO(6@:77`0Nc%==%Z3({jCCϪQojOHomm/^ض!t:;;; v]t-v(mk:v4MCqV @J؇VL:@IV ŝb/"Rob\^>O8Wk9 %U/R Е53(MD9S{C >|Tfo'5MCQ5)D~)zRo+_YϳӇ* ?ep#f(=8`0pzDͣ~wߌQ3F]m:j_zT*J%-ˢ#t$|GZn+3u 'hi(ekYJ.|rb~Br!5Ψ껱_Q䟛U>˲?E+#+Vae=Z\_u4 P l2KwFHxџ>`nlPkET_"(!_TB/r,` 8x1*@F] @tۅBAqݧO#X[_ACÂl Gt%=SnA`!=4Ѿ'PT3  #:Y sZΤu!=>H`"Z\q!6S"kփ6A{'Q2ܿOQ [xgj- ct ѩCDJW_}%$F )T$D)1rn13t8xF a ʋDespV*6 ~/Z JBSd2D.UkARizh2/=D4X4t:|&A?\.GRl6KRT*IhVԔкj5wUeA I YIX=n4K# ԑ1 d0aۀ2X(~&y[C݃ò,m!H=`9 X;J9suzθ *Ej\&X:<4"m/3{ȐVqD9 ُ(H/tA*%EJB0P, +LmLrt: mL$AY6T&TPdi}Tӳi]B+1^ﰒW~~(빺{ (#~۠>"ٙEu˚lȻv5<SHi: "F~U|d.JQgHHm ͜(>nJVl=-pJ<r̤>Xyov껌^ ]HmХɓXAHnTs@H ZAE;-Bڞfpm (%@#C(H?@ޓۉdNBqPŋP醤BJBϺXh"~Y[G('F[nDOp +!X0~I&h@Fƚ@*.'pmzPAz-~}|Ld PRnVw>A jU7A"(]emX7]J Zb lP p0b rUvOlZ H$VVV* R`g'J r!V B0"i$)6OU]E#}"~pnXQUwqWd޳55FhD} gȜ'_CQoHO`0N`G?tGFB %+8r;h|ϹtUur: j C;-HhY&=U jBRXhx ivB>6>a_8ޙ(7#^M\}k;vu<^' c5?Sȴ" j xZ_C업B.OlѐoZ L;r P{._%TpLG([h]`Ktx]}qd)A"Ή8~ Vw=cYʫFKjٺc`0F0b8@s߹D(%YBӉa(sD]ulzCQ NHm1CHȣ! hRzž(O*ؠ/m(!=J{GYyQaﬦ u cd+DO#H~4/ rW>H/`!OP^:dZ*UP")J_mZt.g!K-3#f!\u~0  kz`0#}ѐk&!-b,W^YVꇈ.z'g_4Mf~ړWxɇCydf+Ix٨=}~?A= kF !뤞cP @V\`VOQVCuV=0ihE:?}pyɎ@Cp0(y}jvl=qjtW[/` -x`dڑ"D}X-m4vik\h!EHmDÂUco0a\ӆZ`0Q.R908 ҕL> 6WpDE-h3qX_%[apDyGX`0F{p=TV!d*xaD|EWRE&h"Z#`6yR8Ί kШ<~k Lc={f> dGA>lQdy=s*diH4]GtMӖ`00Z?: iq׵E׹H3AK `0+aİI㒣峩Uvrs+oOddža_8Ck֨?pBV4?QNZtveq礧x 0Ṟ'3p*迩$y c81bQ?# @ Ђ7{b.[X28~=y+[c^Xu](Ǒێ-0b;)w4v1 1` 0Oُ"ib&izvl#fg{X0 q pq۵MtGn!|`0 Ga`Rj=|{n5M1=Ɩ c$`0Aĺ_MLBk(Gdv4͈纞tcB1Q Fnq,8 0y74Gq.48=$:ax͈⺦kzkbNb! ,` 77"B잉y۶Q4 )<۵}^~{nۻ'}; #*8`0 9Q ~ў!cA@$=ʺT=Bu<Nj㻅m`0# qP<=/alͩ@t۵1=x^9$4ͶwB "D]Յޗ`ye"o81˲68z߶{^сޏJuq9mm۶m0 Ƅ'Fvl7u<P_]0 ~W ưW1}Eת_d2iYXV;{W\ύN#@zӣfq#6q $1=@"h+ c`,G0sv[݋uݐ]sc"&@;$=j%uu]GT \a;6xMט3! 1\P~? J<",J&v[=-'!O۶`6St۱hx<./.Kv7j`0\pmkԓg`*v[xSq83vtyeѴm%;C}:ʿzn"h >@h b %!s\< }4d0Kowg0Y]fJY!DFye`uzb]Y]v<,+J˰s8hU(zx`v4qwc1dxO+oy{䜽pBje9(f\!]%ꌘa;6&fc|>؆tM\Q.'dp= !9TL0DW`0= %tEXtdate:create2021-04-18T14:45:04+02:00Sib%tEXtdate:modify2021-04-18T14:45:04+02:00"4<IENDB`qdmr-0.12.3/doc/fig/dmr6x2uv.jpg000066400000000000000000003302331501654372000162600ustar00rootroot00000000000000JFIFHHC    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?j( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( *P,yQli98'cX|@,ULL%?-ttW- /?/W?5#||ͯ/ GR=$5PEsCK$uDXΦW=P?=\JWA!ic׆md @]Skz}SS'$8MW?/@5nXX[e@ (((((((((((((((((((((((((((((((((((((((((((((((((((((((('{"3o =1@+J{& RO}Nnںɀ`0U\,c[a?TE.P#'̊qVlU*#,vp?!wurp[i˃l"N8S D6@cwʯamo,@ޡRճ.8~ڨG [ܴb&`wov4 m"!y Cq܎=6v7Z)!X0@U<TsLL`IZ?S] (((((((((((((((((((((((((((((((((((((((((((((((((((((((('3bCs85 $7͞zgħs-֯09$4 3bאs]r :cs6F"Ox]ʴv4[{eO QYs5xڭׅnxn2،Z-&"VՃ ^NIsYoi%K3DS9E\h5[8[D!Sl[p~@z{&&v嘎Ӟ5CvV8S>SWbYo\$^39ᜎgIwz#ˬf5s^^yd73_kЪF-Q@&ikh, #s#8PGC=*2Χ3y|# T߾kiyMye#>u:~xV<ÜtZ`@.H0s];^B\+ų98qh+G# 2<&d%[bMÎHc`m.ݮ4Kמۚva%ȑA@?CYLRIs"8"@~9V<}~_͜VϦh_¬6UX%GSbS1Xp95 g"Xp0FNqkIʤ&c] pγav9m7c@ֱՀn$f=A%n-sףך|&ls5T-Q@p51fqui"a4(#9϶=uZt}&E4`8 gx'5F<ļyc@7qam!V|,=&!9K<|ya $ry cۃRU#"'3Zdd#P,F|/nqL 4áǧ4lMg]oOAgݟpT`*ɓJ M< D'ĸ5Oձ*h|7O4z|`e։6ۛF\( 7eK@?DO!X^ʊPK#޼ٞ7~W}#HϠÍrCOsDH,#L }pxQESX$?lo\>hR&4 /?k_Rvy*hFԋ?KlZ`^庶cvǚ+^z0QTmu6gLM]4QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEy?+"9kϮ1z5x핼K>Z/I.Fq{R@7G RV6aebMZ]D* a`#Dd>6]T a]' 8AxCK} gӊ . 4Q)b@6-"wұ7?.r6}:WoC4 dڱpI351 pcL8lڐb5N2BH Fz|5ԆQEr_P~Կz?`%vH 3?|孮/d-9z]@nqu܌P<өi8V" vZmv2sRz7 #>Zc*)BdgȠ#\U6vqM25,emG(Ǩyr{֯雄XE2' y'.{u[>b(.N GVY2)#b6~tӵ"}o8t3YwffxBX隧hQ؄j&vo^6E=ר|9VdIT±yp{b-THee`AzyX|)s82]cUj+54137`gc}KTynU; 1_FDg @/.O/vџS8LO֬3~MAUxPrFzPHpBK9Eղ6>Ȫqg ڧ.'TƯusm< d=vi+IWT >K{ng i76thYZ٤`jUT=Ѐ>S\tϭA6$XIcqvF*[hd⌖ #`x'#ӥ=S-;DU&2Aܯ^H(/֟mm-do>d)~\+'P񲑐W˺ \9J]@Ƅ0:qZw ZτO_תNW3 ҄o# Ve 0=kNUz blKDUw)~ji@@n.CxcMcۆX^Hm^=.6ȪY\ַl2̱1qFZd $κ%eV {ch'Z|8NZ@gi[k_Ɲ?mQ5k`FN|9GZ˺HYv=kBx5E1Z}g\", L q]-QHvߦٱ_PnˡkiiZd}% : '5~26|Pr%>Ӗ}OFzcO2IȘ `cdtd`N2Üt5;)ܳR8J.ڌY 2ȨG?[ln5]n7\O TV{z 3"^c0]QѬO-, o]JHm ]F$bO&~O?3n?ξ>+T dc(>r 6zHzU>4|xqR*l'Jx<==*Wils$!=*aGLv |t4&ta2;^Um xPhWPLIPӵ ⺕]*LG9ǎ)9ϽX38Zx}@Z:be;xcGx+ͫwQ|N}kQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE2?\ ?ҨxŲiwMien>iX+s~wH&,39`Aⲭi0 F1&-auS''0 A1=Y(s9\0jش('i=k:u"/kS5ۅA[J8S7\a^zd*xY#rv UFE"Sm{\C(fD!FP~{T:OyF8V2KfHC)Of 18Q|\p*(z-Jc׵kX}@@`)'SBfT*ŕ*v8P9V/d|TЎ-٣0d9 z_Qt=O(jEhBn9 Ք m)MPq׷S]VKxcV :޸$=_P ?h FZ6}b_9ϥD@=Lzzd~uOŭ:M/Ti!1`y;`R0 7p; \E!yŏڌzc=ܗd%hwaaA@e<w2D2ѐp38853@[o3E4mJЎԐs ^ PAu2B A@ %P4MVW2:dnӎO\@Vs\UVGxZeY<*` =M6Ae-Z}ܶ6$8ّ #qLXD>k["xLocOZ=1Krai1S} rm$M\Jɐ9=[YXʹfMvt[}7P++7cV#0s 3:Tmy4Kn+w$Z斀 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( uw8H gʾ`Yn1cgM^Ww$}+ ޷ /O<_b36iL +㎸tϦZE) M'zw>Eg<~Yqm8H ] /ry[hD%BH+^=!d`gJ;Ӡn,2vR럺r;cdћ2gϒ D#sOkp궐L|`i 201;ָ8Jq"I { _IYŐ[`w`vxp >'8r-q4PLb9 Ʃ-uw}}'60H!vlѸ =u Zf9>~5f9nmNF /u6PI!6y5IARU ֟jed-nGV,;qgrg c=x#8>߭n@I=I>mX*OyOU>*t̫=B$8$ƗN##i>՗{{-kk"Onm~fQ꽃.8)Y#ӣKA"@sU;29=d OO֠&[e'T4 @7cjÏ009>jo7J _@:9sY )s4 @iV"G%{ otd6܀Nް.G| >a6 A@-"QЀ?;ӮG ns]9lC΋Iv܅rxՔ`:T$ximz'0=1ԃJDh  Rp}q( =;~tlaK5v~g-QcʣT{Jg=3 뽄0P085Ry(Aa{P47 =G[ l څNvD@}N}ۿ ?Q9!+6o'Un$f܋˜g8[֠v 98קZ-juF5x~HW}&,ϺL 1ydjhUա|ST!?($CB)bF$I7pI sMa4 Qs]izszž6O#V-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@zQ/bO[88@?Z.`[hGR{ ZY$3H 2?J=2$V?{&#dv¯Ԏj[2;ZnsGy\bX81ZsqPK3Q7-0!ּi)Tr7s:`2i/\`t?gPpnj{>Xn#O_ΪK27^_׭1*=3*<֌DD`VhvsBzӔHIAҦlҞ/|CD#^vF2#/ n-~BQ{EW'!! }(*9Brqv1SLs1~iZ_E%bLrsqʘet"ő_Jmd?cK@ #bU =Wk<9Ll$F]p`jL3mBrd/\sE__㔥=EYFe)ʓFp{5OO%F[m!7ʨ8Sҏe]>;ŧZvU}è+w!t;:Mv6|b$r)K'p?^jy}vx%6 FRI #fj2Rjf{heH`v(;.>`:u-5 8̲6ܓ(lrYT@W[P]1DbE T;p).^8QDm$W # #i'c'9}wTpa%2_7rob 8<S; `g'jݸX!+\W p7u78`.G: ?Am񃁌1S%5Wi_C9f"SpG($)%2H#_M" nSTmE|ӒzSf'IዕO"faI'q*:5Νur,!Cp=>b9Ft7Vڬx_S 99Tݐp$qXV0;@l/lYmASE/H}'% Ke9'/HU(&8D$X`P=8=*9WK,, Qc؂3jLŐadᐮAl_j0'Ar8}~E1mЖ9= Ch4CаeX[fEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPHN)iBc IzUŸqFwa 73F;&?k^Mq4˜߾+Gj$Ҿdl3zZX"`N#G> $sR{f66H#:b姕 KPWJA#^7d 9jK/L}hdݺ`Ԟ' Cci$<d$l?ԁQW浼?MCZp--աlr-? Y/B ƇІ9)QTX7 +_\,(diڨ?!GC{{9M(so0̡kxthuF!Ypq$&^jwr⸕("cTvq^Zhb5͞;ubڑeR\n0v h\ok:rbD`b4 =Ics=jbHAfu? @ ~P>8 !,T o c1+NE4;gx Ānz$05 uᶳ |1cBqhw}WQAgpWȐa]qFI&9 O=s&|I3. (Ay֩7ڲ(f= uRީ7>nlZ:F}0iw*18}+6 6&}rsut]!6QϯAOs^McJ{`<l(A$}=Pdqӧzg}um'2M``z~f'ʬC` 6 ܄{u(d=AE ʑ8V)LdqjxG_k,kH>׻[̳G:FD78ҽ:b 1nO H((((((((((((((((((((((((((((((((((((M\:]kBz{~JLk3\Jn<"yb0qR0+ߠ2oGQT#S w@;})&p=GzCzz F68k'޻_ xR8-]Z_0Z7?yÓ+úlwCۣߝA^<sZ21i[ppsSqskX{ ,'f4<|#HJR̹)GBA_*|aM[MJO/,04ӥCw.һ,!!к*gEm9nGt#擿ۉacU$ ?z-fAH$ܷ@N0=O=mi $]B HWi~R=qQ%d?"XɅ}'8j#W-f Wr]ۆX!m >j8Vø|s,ǯZu[kKKld/*yUGeQ8"[xiXpF<ބt"6շGQ\ׂLFTc8Qp ~ZTc)Rs/g OTᴿ9-;y@`W͞ k]s̱7v~ʊ5FFr6ȼqʬf60+R2zb!T yM2 vr-lz՜糂{p'Ơ ڣ't5z3̯z*ΣG}!1pSÁCT'JN ڐ KO=qծlrLl~ҳL&Իՙ1R_\M&`˕ޓM p҅85U_Q5A\ MVIS0K-2sתw+ #nQ*86_;geޖ;I`c)$dsa )drHc85g-$|Nw7NO]ew{3\+I]9# 瞙YWb9ROb~1PkFS#n 8?ozrݭ1=sR"޸7{~.14xwd޹9$cqd{mVHsH(Bͅ`0z;׬4?.;q1(FNrk|\V@G<ӏ&3hQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE\/KrxY*Ms/a`EaY40?J@62%jk$o['GC&q\;(Q9?lPNY08<5~e\jc"t(Xz|q+0 'qq;Tsɡ@|a-rILh<S[sR#6b%B+o E{F#$#)VR0N2Ŷ$^aJ$I;bI cB1It12Hs 0 gFz 6&pxԚՐ2F[b0#;s;{}Zeӥ$=N~DR}+,l-(˞H&@AXC`O|c$J~bJ^gcTiii$HR}*V'k1,z6〲js sHż CQіBW=}hEiƛL@:VI r xLeb˽m-@?ՎxFnRWrOE6Hx=z1I$e+zr{Q{vџ'8L>cY6F0:}94qPFt} r!Xv@$ =zS/cc{p3.὾Pz˵8]\,>GNH❬cS}:ܓUI%~l Ƕj Kxy'<Y.H 9i1WڇlJƤ0$apHq0q/Φ[pRn+Fp@ҽWH#\9$^XzKz%,lgڨG&3覣nyS(((((((((((((((((((((((((((((((((((7_E&euQ g+[UM.I׊)d2"^ИOspFGXtkI*޴{gjkpLx1Vuéqiܓ #o_aE0 V3ž 7׎![ &ΙC@H_ҝBhlz҅ow q&YV0WN“KR!g-v}܄pɩ kg#,DLGRȠ\gJ k[I[<)8fEI6P2Lc$ 3Is-\[3Rйmjb?LW'C AFpq׌zuȑ*6{TAM'P_4|sZzFi:\+fyhi s뉩kZάnpv, :@鎔6eB̧H`zݫ]g bّg8ۄ`q>Bgqn%kZ\D.- :ʺv9q"K}Gtj|\ pWT?,ob)s"18_#Zq9SnW=p{y$siGmۃ9-GzoH7-JPPp6.9q银qL u)1]g ` -ɔHnrr#nGCE*n~o%-.:` JW\xwBīnj`s|$]!?oz-2)ekF& #>QǠұFp᰹+LmriOe /#pi<Cib9a'Q!{2-2\@1.ݿtqLI'mg'8-NQ`UOOԥ9m_2<8ܑP܍O׎k:n<)5\g^1 gJQz]|l㓎zv7@ #)< PRG*o P;FN}N2>+GI 'ںBLy.}ix67Bw7/Ose~4_@AiR"䞹s).=5&mxwcS29O^j$Qs:h $gyZMOJ=9x)1 *t&$ zNp?ɨD[?<QsÚz1F+FӃN8sM-P>cR;?h c*Z5U|eqlpN^ϸ\f'OӠq}XqW8'26rb)Ӂ[ [8sٍYă/2hsbIܸ^ֵJM#.FyI @$v?-z@A#wq>u]p9u? Fѓ^s{\]@LEۘ \~]+ /Q6l״QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE_?da k+~" #FҪ` rxzա5*AVi4ͨpNQt?.Ngp1 ӨP?4C1(r 3%=rßGn[!K4ERFT:\4C8dSInG #G81M&bpyI3\d{Z>ey$'.y9qP:D7 R;n`$G22Q?Nj9s&ѳηt%.Hham${cψ*ر,e3 h:. ]O`xfDR߾7>)$ׅU~Tҡb%NtPʽOgU\+d(}MQkR [;TW9v@B|̀AZR5FT'-,V '9:21Tr; SHVR s?Mbڒ7lHZ=9˨b1+wfܨ#R{P΍r6sֆ[c-$|V7s6BNGCڷ.f_ svQ5 Z$$9"=?KHx~Rxel= Y1nSF]aĒNӥeW4^',Ӧ!_b$#M/,!A~{U@v;xҘJq򬥎;G)80& $+֪g H zKF|ۊHѮ~' F,7G^ƞåb©}3V,B&H.AіwrrŇ[ۯZybg d-)~;?ᣎ>qY](nIY\h[:IbeFHS9!xe8`~9͋Yc Ss&K;{ߏ5*dzyo nS ,x@Y{~4M \C$ӱ O.̇LOzn0/<~?UrtUnCќy"/2c~)ֻ:(߽2sG#R6AH2ZQR3(? kQH%%߂0}Jq_KbLgt[yQcT|!ʸ.>1-\24s#p>oºRdi9+"+܀92(λy$E !Ag86xq.󤸹RK"6@ڠrzNuhƲO F\`H3C F߁ОZP u)ЌLp݆ p%OjzPS@#ܓ5!9=맕QNHOӭ }!cOss_A_%Ѧ.0pb=AAHbEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWz.4vK'fX]8cx¾^"HZPãUFH9g  ~߻rŲ}}?*@M=)T# GqMc򏗃#ZE0V# t=)>qsB Fݶ!cp2:h oOJqq,n*v)$0^w?E493 I@EF?= r-Нat1}AT˵K@jiNgN{--;cJjO[f4J\r'x.yA&2 $8 q0; COJQo,QLU'ڞ>JP͑F-*I#4Nv?V&x>졟#una#ؔE*9a5_ie Djh *;p$W9e_[,% |uQKy "K!<7(GYA99O`b߈vs9Sit˴M pS=O9jIqqU4{}N lgӯH 77N1{wbMF@֢B@ҦP1 =EtRAmyky:pOcGOƘ$-?az{׻iU`cʍF'{Wxm-IB5#FQ@Š((((((((((((((((((((((((((((((((((fH݂!IwvJ9q1_I*@PXpr '۪^Iby!M: ݌dOʙc:8Zbʸ?xsTI3C}9Ҝ`JAey918fۃjNzu98#z֤2ۮ)U: ;5;D`y8#S!I֮%+ :ə@9ZۙV#y"yKu#'UD[L3\{@c"XmB= 4lE2,Q@$> usV$+.ğ5n7j8m\c*A$c0"{%WjwU89ӽ%&1p:5ER6nO,ZF}JKpB]~D T>r`9 P*8V2dqO#;B/-C'q^DbԚH*XV+{Rlv+1EiO <9'דSzwߑT7Rr@wn{e\Egz7tƽvV8$$cqxAjP[,Wv{cg]&'U$ 6 74;K!WtsӡJ;.'S.i[ɯe|%s‚Iƒrp+݈J@T6A˃7q/qڬOx_[ir%/7ddts[P9;荻2X[}*}2u"f!T7˶UHAP cO!3^fyz |=mίRH7%\)5ئ\Cv^!#*@^-,/ͥ.VeJb,7i7d௷\m?mʓ.],O@c\n~#,В ]}bIO۲7/]td E4}Fk`(cuNپR9$-N$F&v*ɵ=qTve ?z~!OEkDq HܘƒR1S؃q;!U*Ue~;S5( e:8[ܜv"5 Grj7OݎAӞy* N33戵%  }* 9wPWAչW2dg9=*]EBqx ۄ+s@ubR,Dg59- P2O<ֶ^'DI՗M4dv:drpqXI_@X2 R;KXXKc i-Qf'%>܎ntWBIۭ*v;Ԟ& {^֣U 0Tp ׯCW*#yАޜ>G@QIR:Td,2[ U+񏼤=G'%& Nw  t2rXNA\^/A}kb頨~xG0$!@cZd5KqF}+|8s@35qne[ŏǯ^ߥ c0zɤlEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEլ1jkoA~uwZMup! c8dׁ_ZRG2,N9֠3^?#=|˄!ː3Ԟ cm5wjd#B1@c#Zxj1Ќ!GL`qҗ6GZR$:y#bEa˻Z;&(S BVqm.g՞>dhpjic8> Փ|ŃlanGAlx(2Om) qTr}a *;yk/0Hg^IQ:j+Q(`r~ajhV\o^ }?~f@1h|ÑqXb |]9%S`lF*+Avlz/pyLs .NvxFp#ڠ%y=vcTaCZHogѧ<9#o=+'2q';vǥjLcX3h-$-T z@Xb!WzlR+\ʌ-[r59u+dRrF?U}|7r#)z9VFA(P8ϯ?aVov/-;]:~*=J4/[؟ `긋<8^1Q6X2&ְ/uԮ[8*|>- o$eљ6ԂzqϨ8QgLjW - 'r93@gD%TG\cֺm UP#@]iTOih7Aev2n!cV4#/>YGgϹVfi#͌8o`Uc pz|,` XA8T^r$7*y NzzWm(F@ U5fF_+fa}8'E7w-[iX7ky?2=0?OJ|jm$yq$);zں,XvN29Ozނ~R}M)(hDUަdĥʠqԏqҝl>Wr rv?z TTíBۑwdc?Z솼8A )ώ5EY[{?x%O% #1Ut18ȽzJ1(秧i.YpzHfH~o?4NV)<`ac>DR3XKdn> (I7Ugap?#Ek%0O$Qb"1n'ա^X<籨NÖzdr[L*s95b~2l^H5 I"$eeș63sU^7VE#hP)?%PO v< qtG.6pI9'8#Gj`b [ `ɦ#Qۧݔv|@={vQx Cxīy#OMΒhv(Ms 84>3ppڽ`'Hktd(S?@ݟr@I\@n^ fs,^$Cipy+|}Uэ#9=vŌe[]VPFD4F <)Ul9Ƚn/f˦"PRpLVp"p9G5$(cᩔ#/-<}es{VY^Pfh]4v>Xni֖D @4CN1o ?hP*khZĪO9^E9miWr%NOަSn( sϽ$ʢ$!![ ;GtdF U0@G < ulN7w8s% ˅e9 vP0zg<}ʐl&*$eEv3X}ZaKqv(W,wʹbr6׳g\j`y< =j ?;>:ckʹym7).mgq >F8d7}qsZQ[zL]apz1"tN֭yU%!c0 (|Z2$78z}y[yP/F?v)I]|>[*܊y}GlB EC_:*nwd\F>գ<1'VvcF۴\Є!ȖLUJ}ܲ`KkI\JH#ߍʌ;Z;X@P\D>';H 秃J\ZƵx(K`x̧=xèP*3V1a['px1SA$qҪTa=S3tU'S}nބ{xW Rh dJ"**FӞQbn r'F7\rp ʓP3QVֆ{!F{?RY궉Wnb(تidBA5G$zmOR6tuk9aG A\]hS$f9݆TAfgd\li(,f-PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPWϗ|a+UJ8̏ lOx}􉋈o,r}z]@_ Uˇ+ SW HY`bxxgF;zU5F?*3Xp㰦`'8 ^֕0IȠi?ɦÌ?4Ӓ8Fc'\J+K$c~=LU *y>Ṧdmn@\ɮyW)\9𮢄,`a~a(he[- >cRgMфl ?\fj}F`|v]:u9&'kf{af6'֚<(mŔ蟮=}tUQIwIF.O$hI[H_HszuIN-*:b2BׁeTeIba/Y]+"I7 IX*\\]WSɗ했<8* =sT-4ۜ|/ɞy5*ӣ4)][5G_ xB]SoV8#f8*wnѻX8)ZB8tJb;v}>ARqsҢlh67tPWԜ q8}+)@FH=Mi_pvo$sbOA[wO4xK'XKH7Ry\aa~P d]u5Yt>c#'55OqDc@،Oz[gE'x.6*j &ѷcL 9g4 3R% .wTHKxV5Ge71zi.JxFBÕ8t+}KUki2U[k"O4q 1.|9e( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9/vXA>OfE={5 _01"3 C}\~5Ẍmڛr84eLb?jȠսH!WAT, _P3~|L$O-ߞ0W7Z%Y!Nyb@w `AN^"U; > XBۂϸVp@n=q5:8\ҴcpǹR6!_\<)0h,Q&;y%ppq9$; ]rфO#?ϽIu06Rma嗢s`}jV1ڈK̸GnzP3+o,B@%ɬ=$3@ǘ /=Sq-xufh^*W遟7Q;w я>bڻlY& I Ľ}͏.QL}E>xvA\2]FzZhlc9/< }w OrP'/Ҽ g1}ke]e-A ck]đޜ}ϥm|:+cZT9 z{ιظ;j;-o:kNĄ27nUT_bORO1rO8 t^ff"dq13xr&p2jӏS_%I_?3\rEy2Ieib4 ŗ8k&ـ YtčH֩\"ҮRL%=γ<+]F(2g/o\S5+2/Ox#ˏv |+cra>ٮg*Ua+{¬LI fRIcsZU_/cy.2pX &0H=BH]t"o3 sB|ÐpW?7ܩ776 +5T$RSOEa,bٕ~hh $|,WmNV3 V8 =vfo$o>گxmaӴcS,v-'ȩWHZOS>_ 6 qrg*`WvFs8="0W ёJmЃƺ7wz7SOɌp=?gxIגE w|@9N+JSO;nCU,h6vUk|I,Z|{`ڍ=w*i,}QrHuJk}j ZҔ̻UUs’JCH՞K.6e,rʼN8 G%c0%2~wWC34kḥ[׊[(֧V-Ӣ+!xS˔/c03)pfL9*3eFOFۥM#awrQ*OY#eaзc26nqjcp!߭NTgܚy'ڥ'wc ۞*:w^"rt w93€+\F%q]wnI#?^ApM`t׮|?20ک \TQ@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@RP~:hW;mTӃ^ f;6t{ÿ%E,ݰj/Xcq搁tfm/qۭQԎCD[V2탐;?ߙDRJ`ӓ\H%76I8B0Iؽs^_3)%!Aߎs랾-IHʎYzf,d!RXg\uU<ۂ ?gvDNt"*ܣCm*r9 J4Я$ax)md:xnED2 'zGE.8#h4y`玭4m 19tTta8ZR,Qqd^t~dyKD?%2i|XId |{hbBZ"FQp~ۥfJPҶup7vJ2z5cNҤL7tЊxG:y{m:Kߖ;rzQi%ɵ{Fy}g Ҷt|R2\d8$V tRXtpN1ɞV(tHQΌzqU8PyvԳ+nl0Di!6?_Vi>RYG"-+ol_|hdip) [>IƯ:p%K+K`Nџ1>IFH;؆-OZ8<0PvL;}k_^ }"IK@0Fr[⴫Er{Q)7 D*+#tO$OtH)w#.qV4!V3Ls$`dмQr5ųOy L%F#8zV_rs`A!N 2&>$o`AzֺióÐ <W\)ggV3ɿh'+$[ZjOtSV|ECJfC?TLc g kbn|)vN皐OīY?z<t: i:\ʺљYr9k:D-(cqoJGj֒b.$ c1\s;2uWJm7##PĔ,iDv݇jL`/5H8 @"G}[f3haڬ@ZgD w2?oA"K(2ű=%Uaڍcv}ks}yRɊ& 1g9]Jp嬲s4ĴP(#!%R 6<OOmjK [[syIzdq/Yodނ@2nR9 1$`ȧk1x^.F>&3zPYdo+SEY9ǷJp''U3O>_1P0jGk2Xd'VȷQ3}eNK&~LXM#R[ &xPcLe~ʀdqs*IDi Tmǯ zð$$`(QH((((((((((((((((((((((((((((((((()J+;w2o3LT#INԌ8Ϩ=ao@#֯6ֵquDImFu- zW3zq&GGRAYq%'znĘbJ<֙0IC]z @y4ه'~)F匮e,KB39ӊrp<ҀwӞ\/>ልpI?N0}6@70OҴ4Rfجgq9<cM [4$‹'sGfh{ηo,O_+ tfIE{J&N Y.N NBQePbxu|񺁆<~}Ol;f.[JH{X~zO|带حj,?i\0# Oh֚ua}3 Q1;zkFkU3rN~i89ح?"%6?68mD,b$cGt_˜1:*Hbdr{楊u!?q=}-sNTI+9„r=?j\hڞGqy`! 2 y{Q̓ՈǸL|Sոm1`pIxTwR<# p27^*S2۲y.A9FRc3<<і@r=~fHpCpC)!F'IV!_)ʷϕ.w-=U{8݂ yڮzք. /1VHNw@a.\'c_jab0$AkpRf((j`6G!,CcETH{XA`@4$j%PLE9 vzVwmrʫ= tհ1Hadʿ4Ip ^jI'"iҖ9hIq(!R0\L)J,́Ps9+Z=ZG۴g{kzҥ 1uQQmǙW!Wgp@)$3 >`ƅF-,c|cکK JU'^MiyFL)7 98D̨" a'4Is6G4Y>Y'@rN}ߐS=ĊH>PZv9$_ZbQsJ\ H#Cspg?J['ZjOP1zS 78# kP"ѫ#V#(z܁jcm tӊE#i[a8V|u~EX%!8Ulzq: I")2Oo/-+˖60"$.27B zIH}߼nD4 r6^iԟ<^g/^XJ+$0>QN>oҳ`8?0 }ٮ;n rӎ*ծ5KIm'=~ZUA[s&%GO4 |-@OE蚮kؙal&ѳd;UFm{V_G4ݟц!@jg GXD#:UN÷o<~.ӳC9 TxVڮѡ{ҡ{ A>qyV Y; W\jv^Jٹ YK:HKzd 1E$Rs=j~+g 2'==개 O˯Qʍ>nȣFYˌVXFEg8ļ@!sƜd,|BĦӌ}k6Z2L9.4bFAV,sӠZd슧8 4=Ka`{`W5rx-gU 31^f'.;H{ K+oKPy dgZND#L^$ǽgi-3m+<whnHp@(}1o! rlXǠQ.V*J /n$ruau쭴`aj-}}jx+v>[q,\:%Ha@` nV jQz$tk)ˆ߸|9+sװ=CO"s7~OBr^7'c D3ۣRc;( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (Wӣ]dX$c"B;Z.|c%tTk}(~B>\rF9+]Jƺ{+snj?rי߆uS1fHcM9$Zz`8C9 ˓=Z1##4 8NrsPv'Z͜MKh%ߖqrDFҁ@9٬yYwm^b"CrѻR+Jc`6(I3tL.Z&JWD!*ygQYVثi)V=WZ]2VV<;Lc' '縬Y2d2 1 zKGOa^t1> 9Y"B ޕӪ?9!-f;kևi.T:V^sThʿ(<956m1.4;ȧX%G.` r(hP@I9D<2KgV~я wH?um:JƧrNq(2G[yblK7r>\ʢV}? V[+zp-i6!>XFTddQ:FIx)*ayA? pXv1z*''m&z^;DFK zTe!y~FKbFw8 ֺ6Auxj6k*C9`Fyc88Ke-Qecj>ԮaKx1q-r~(YfxVHcp*#k/yqwsm@jR+y1m?Q+j^o#әSL9#> }#DIukiK'e YxC^j| `v1}IY^?ZI;J}<>W'񢌸.x>jkQ*ڤF P\\&(0kQż`q_Қ yzwJXus<-=_pH!P)rI 鎞5cPGЭ+{=FMU8H%xEyGڶqL'Y>qnq巭m][}4_u 3ި4(Tc8'AgK=qW3iQj1Ky׍Y2>V7QZXI /:9#d*#[e̋lK+mLpN:OQڱ,ŀ\[qx8})X k$2Y[s 3LE^wWmbal[]y$ts{4+aW\"3=o=34׺n%ywa-ߞru6$>7JO'$# 4BzӜQ. qvJOάz Iyϭ/٤'N,(ٔgKFI99F3J`g9,8]N0 _PĆe>HҬeR1G31v0cƚбl`?g!g5<ϸ;r'rA?1;(xtq;6'{+c;+apvڐҵ ˩"2(=k<_jv}Xe1(&|c4:))hQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE ,+M XP!Ok敮ma޻̃ھ\1T2T#pkł{xmP-]ݏKN~I$qoL#51OQ+rh~od;tC}?Gz;y*N d#jx)9# AxCi3Z`mx.qX sZX~I0 [ˆhʌo{'I #J mL`IMw?Y׬tB"#jr89MI'<3q:4;#E 7 n,>Ӟ;tՋFֺhnm$vxYY,1dV3i$y& zD_,?d&2;cL(AץvvzLr·D`@;q!|MÁMtci`&ˁ$s`sV' bio(E c#'ۋ)VF6 m&X`yc9%GM řFQu[wqac1E G >ZVN7ۿmq9#)rq5 bnK. vgwN3q$ӆqmPG|嵲beٲ;w݉՘I?hp|Ē9튭FqTYϡ5ylfukѕ l wsu2Cmcm߄D\rg14 O>Zbh,aFF?qTx"G xIH#,QZ1NG~3O}q]S +qKK3N,N޸ jAb%^Q9%r9%s^T'zF ]1o ŲFmvvGs㎙RffE ןj|;|F#yAV nTtG8c4Cg܅XLV. 8K0: 6ZI;E'@N'uH,\M k ,@RMtfyVOKu;ZD򋵰nTA#DX q$Af!m\(aE J8S3郎kjgY1py n:pBcnH$426PE6$ΒiI)+W1;uRӾ2\n? w;v;s‰[x?@pi9SWҚ7A뷯@qAl}ʠSEROOjѳRKp1^Ֆе_2Օ@FY7B}Oր$Oi"in',Ί=j3Z\'"pDp X}}NT>wn"2b@+N q4@d>\y$v0-Z^-Ks# G=׵'Hdb:(<>;!#qRIXLLTi-YL|];=xQ:kzdQS&x!,)Rqc+`cYHܸ'?/$|=j2\\pŞi/3 In팒ŀǨNo5Kge~6ik0;P8}z QQ%48n&bHv y6[#lVɭRkwK,}qpNwך+ "H1"Bvn1"׈4~XIS"w-'=H'\ ܀zY@88^+2vyM OU4IZ(Tu /9 9LD;VIXӂ@PG混C"|Y =rN;jcy*7e9֠{+W<2g ,7;(i]zUʂ y'vmx֨<̑\l ۀ݃[͕ďHϸd;2sC,3+TR쬠unVed$S*P<Ԕ!e9c*d,*jIӞ"͟.@QK!}+L`ۺH8#XJ3 n鑻5BT0or[f>WWvWs`'ޯ40[lڹU>euҮ4!z ʋ2g4QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE'-|O[[a"Pkګ5/1ǧNO4rArg&L 6kcR?0~+X1 <3ҚH#Gn[ ;N$$7Xph`7p '<9_z2 q"d r$9`X^2ǟӌzsڳabx5#ӳ hW9Qe#9~:pbځ\1z~uƄBDAP^=?GoqZw: D&`49ےCcqsM ȽRyWHd!Xv:PS7oFGPqyH-Z?):ZAtgn28Q-NzjHPCZ+;*^rg?jn: ߰2}^"_^ $ L3vF1mZv?zkۯLr]\5m3}9n x6pQ߯Ֆb2t' qztQë6=Ŀew4ATpq={OX5 P(%W(8߀{UhVG> k &݉ 0<*Xdsab{[I)J.4Kb'o ł4yF,3''!v8-Ǡ'V$[S淞R8L[('s8< g=Mgyib/ 0U$qoֆ"Bѐv]ϯZLdg< r>ɍt`rܜtZ]I_€1*s=ioڻn- Y.XҰKF0{b"i!$6=3ނl\:r2 4߼hdo[?.A'B F"*`#R}sWtJ{;mc0r~4bIɤkc#a#8ڡv(rje_1 rۏ iVw7,YlcgN~Y?yB^zޤMXf ,0T1ړ͸Tabz)N0;MV+ɍ+(7rNzғwDQjBA5 *z7SSDFsrb[qNU;E ZhlxVKcIҵM#|PvzqןYr d>brq+խV3aTO <=$qɈ,؏^8[4J&5 ;>Cپ+[$(~b\sԁNv rb]۸NO dR]J'ީMX8V5[YTvdUO;T6@Nϭ=#U|o8${g)`pz⤈;{sH, }#bۗ Q1ҹ?] 9b8=08Q^iY5`,d,ǁ{Wz:V_m>ͥ!.f>fO`@Z(((((((((((((((((((((((((((((((((((C^&W&ysrYGc^կ#JnNs|"J2j:{ N?QKk*m~`Y9~>zU96=~6G'="r)Fx=)gޗRc+ڀzF{,JmµlZHtoI$gwe s ?|.pczZ#ҮbIw.bڭ'NGj[2Qbp/B35wT-֟%0Q$n6,K6Ռ9nݠ ωeoL1#,v==+W>6n( ;trqv^sցѲ#֯ =?G'u0'>y%T2H2sAZ8 F:b=1zuR @OYu.sY8Cc28'5P:}2sڛOMRv7Iq)i|C4c\ XW[wxS:<,X<N+ruK;+q$Hwbԓ?SI)r~杍y6kxRzlnKǁ'ҍh7dU\JrZ#1H'w3>lqiїv=2)*9|jōȂBKK GAק$KHcI|fLW =y=Hm s#dP pA\:tǮ'>ؤ(A@8'OcF$EsN:GT0$J67,HcPfxAy5]T8Du{Rc ֥@w9!z{n"~cD-z?4{hxTr<Ҽ|?: YmM;O[G_?t:((((((((((((((((((((((((((((((((((((v6b׏iiӉ;,Hy^lЯ0:d~9Rȸ>p{ $;R+9 y>Jen~:ֆIǮ;sY\ 0P*m ryM0 pMTB#߭1 \R(?.pܞ:$`G8=ɠd[v!}:dU5T {xzfIA9?A</At ̭` 3Nv; Y!I2MfiLJ@d䞴 g}@$YIXnӏj ׌C¤bxw0G`zКO$o:C;A;xx¹įcʩld`P5SZxZHA)P =pPo&&|1֣ r:.o i%(ƍlfOs{swMHG|ӭ/^(ڲ}p$ҭi^_\źK(Pduf=JֹFAJbQX$\)JdZ0rUeyַƹh**gxZBb m^3 $412)N-y16`=r)@ 8<~BI/ʕrce= nKT dr;zR9@`jUܹlކM &3cӰm>GSP `$Rm1Q ݃NFFZmڝ0es=,4v;]N*WqT8JaC,Egv2ty'΁\ےqwuu%ͭ1cGTgs+ai|v6@$+# Qv]'6 (<ѹMh:q=sCX2n\uz0zb)9 {QRVgE(K5NtNjm qT,3)KORmS45ZIG^cE=8Y$@ ?%DiB.aIKBuS[yCm /?# `pN:RH+ڵchRF12hFrc+=kC|O<KFYL",r Sc˶}IR <0yI%ߒ_bW%[$G' "߼W')T?s<N@?SUĬ' ?ʬ۫G܄}{^NAUN0 8#4/>r#n9b䪜ԷOj4q@5K $=k舣XT@T_=ʗ݇})0(0(((((((((((((((((((((((((((((((((((gCv%.X:a605mx.{s6 ~rA4 23!ΪH>~?Jx]J3 %O$wGBI0sH[]ʠ)m$)  9;q>c@9QH]>w>r96YZO#j 8G.+%}Aq5zI"I8;8ք2o⤎lEpFb#cOlKkc;V6 d |Ƶ!4XZE1!8#K58vHo4`vAV8#s \q\I[DNBz#4Ve+uvYomnu)|+l!B @5$>}]Z[MɌH!Be rsEImx&CǠKjqÜ=VW5KK%xcT@` ?%ug̅-3@'=7v2O09[u2\ѷS]t:[Kzb~Jϕwr^T{ՋJ;m V0mّ۔p˴۸n:>H5I5ȖE]*@ rK|wץtj;R1nے88/eԵk{;3C#䓁,y G-4OC*[*B HsIGC\Zk+Ie䶇Y Fۻ,@?w+0MtZ2X#'2,=qt5x)1:@쒯ycc򬔞9ϱӜn#}n\0s^s{ ,Uydxbǣ|s;f@=x恡8. Pyj9 ynq9 &1SpFߦhe9`6@9V0ƫғfX^=ăpw ho BrG bi0A aAhTG-wǯ۲] >m>Pyӿ+O" ]XZ;sl'ǡXX?̽/%kx||gn&}95LR)dM2u#JqĞ8R9Zܷ{%R~腗' FG>\Uh[q'E(m7oX<Nvr-ԂkCM{}F[oJeOZS v<KlewjJmăίG>Ͳ!<5BoJ:ryϾ) P%xOsr1@i/%y=ϭJ #ۥ4t unE=: à(m^w 2)> WЋEHŢ((((((((((((((((((((((((((((((((((((Im5C?z``Sךisy* b|^ 8n B~`lאi?fvgFsI99ة[xnzc?Ϋ1j̧FBy5w#M z2sCh-@ #įArA;IH$F5[G@-Z 3z.cBىۆ."6IuA'sp 1ڴpIL'.fg2?O\:-OZZ/4JT N1\=p^G5z4ds4LL!&=6~jS\ۥ0L\쑅!qpu+5B9ޠUhEuD#uݖrX=I9n#%As=s>fb(pLqBg t?e{4#nbKP `;cܷc7hy"C{L<C+m,`uKiee9"AJeh[tpۖK2$p+Y^}YDV1{{c)zҤх]U0`\곶d[,XrrB(IB8NP -܁݂E}@)#( u~cfr׽$t鞞Z+V$ڗ +IPHZ9Ido-C>* pqҟg4Q0*mfc99 =G_ƺKXoo`Y+NG18d jxvHI<fP8ޝsANOjkq5!I'A+U>6BfBF-ǟvpsF*dYn^l,p13<3SKom5YwThƛUflr$cʋ'cbpp'FSpd>qyn) 622GOzD&0*TfI9=T$ǽOUNs CAH?ڶx!]'ox-5OM[Jdv ;Xt $tZr+pY(IG+?L~݅W\+dqW9Ol#yV̼ :r;UJK( F34rGC}Vm|Ht ',F@c#֫xX+y U:ɩmC\k UB6-hE{RPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQExGQ0Ef'=+fue# <[S g+ԡرGS=k5%Ќm9,ؓ~u! KhD9RKNx7 s iCq=< Iߛ=>T|0:Rn1M`R:s}NƖ[UIXqY-`rxXƟTTăLuh1XpďpcMibA&7p|D=(.8v;SbǮ*IJol .p߶>g^dXdF1r쑩[d71@Wݱ0T4@/[ynzsJv5ZvbX(B~8kMSSXIjNFO^P;<OlK =+e7 ]!"H'T2qx\\bA C1O=Ff-߆E*9+sapC7ZçKB_aj扩A*fd0H~Yzk|}.pmi*1pG'#Җ[H" 8BpNP;/_b*k BG2&;ǵ XA$ct)WpzX1G|kCtRE6 5T: fpidHI# ?\zAJ:2H (yp=Hi 䠰{(ox&]Yo7er#Ղb ZO5-"O3Jض<6aM+qQ⨤"sjKo^#hdy;N} >R/&;$? }WFP!kX}NH>/ .M ?R=u`D 28UQWmkUw oDXm7"Xe,+py߄%"=CY.xch~Y"rl3[?|# Mt5~t.vRNWkK`3j77m)7/`sijl1Ee{;(x"ImSRHv|QEKi죊 ,"b8䌐7#.qX|HF9UݖLW%Y@ig99W20A͞aaWV xmbH듊d&kESUFIb q _# 'pn{뛐bu'IN Þ SEp f9+<`pq^d;Q0:gW+"OR89tdT/i(\ tΩrV,mw2͞@_Fʦp\zE 6 z54O qQuqy8隚([9^XcSHPB$*~NO9tcr2W mxtޢ# w,7OF)9<nA,n!>\MAW瓁]N5X#htcdd 0<54irJ"B}T0Hu Q`[Rڗ#p?My0j+y@Ry''ȵ dR_:d6*̞~P~1Y8XXA/fRqsxPڲr"ˌmf!=THҳ%`[Ip9gq"\fR9O0EӸ~iZ:-YN2 cyķ֗X4L731cG`d LMs3GpXu'_t_CPY5խ؏,YલvyjZUF^K18,'=M+4MHZ,6 2qxeLG}fJ=25 p G=Jy畢L3HC8#;i.1p",p'ױlLW9fH,!GbÚO9-VX&[@,NhnL"" ]; Ĺ9'qQf;`.1 ~UXZWs:D~S}+xyLp09 zTVRMmn" 1~G21O~.r,Uf-6xE;a32NV>k//%Y!D F^29UxNgYc;_2 }g(U!9<R 9]:]?Pnm' 0EhĂ:I}P@wrCo 63J R3 ѭ7<,RO<^HH“Esc}1H}P@ VqVG2#{ Lz_ ŷ0zՅI@x!Hu :W^OG?{uMQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE`xw;n+|gj :~& /*urW`o`P0XS9$ƙ9 T dz ܂Ori#PsqM,1!PA'ִ,!˕@ Kvʳn*_qִȲ8~Q2@.*۩Vhet…fٻA==>lZڗ4ʲ(YIB# C(8z룗EV;y|?cq1PF䑉FI`!O ˜R7ץys)hWO@zW[5ǧ0eV>_.Wљ4C$Fm'OQqd\>vHNZ=bxzJ456#מOb9'/3wqI繕rIl]A>dUzDK4n7áYO%8ލ@>x4s2y}g0Miќ11s*W!#I|@ }ȢoB ԭHrһeoH,_vֈ`3Q^ E>C1;0ets"`u?\qZOZj624J<{sl2x`w~y5_X{ 4鶐JB1rzP%W #7EFsH8 ;rqF:xmш.Aya$k)CY,^i2+}1ziz}w3FC9=n0=sք%C2O'VtwϿN՞nX}+{Dy,o6B+8jō{xV-?JH1 ?KQWR2JEw+4rkDo,bA5$W:vo lX*sbG$L;j^^yT!idⶌ˦ꨬ Cۧ?[x~h*2ۭ9TFp{~ss1lrs|>ը9G8}sB1ɣL[M8+Att2PB)npGry43'c(N 8umA9]ٜ/;\}kBC`X͵o+k9b&FBrNTcb46Vq@B>ޘ3WO,SE&$i r9bA׮ν9*!%yhJ Fe8]gi=pqiSA%I%:@ .<13HƳ9c#o ;{T;^HUTXxl`~j}2(tl׊U28 .0xQ#ސlՁ>JFB9,2TPl^9ܤ5<\.=*lms̠CV< ^^%(}7@|{`R1h((((((((((((((((((((((((((((((((((((8>Lz?ʼo~*#+ټLcRWύZ``q@@m 0!dZ@B@L33q1 3A6=9#֔/NqM 3gDHbR T@!WۊLk|Sv y:SJķZ6ev2I; \ fnYe  (:s泭refU8븮H3-ŶY_(7) >'4vs*}]gjJ^gP:˵,,XZ:[y\φz`|Zn,>k[@,ne;FXΑ kq  '4 à6]#ޠRԬnaBW>9sRK[tCAAh]:8!K7{VkY 7l'N8㊝u;Hi[H(1I $AFė޾7Wc=viYӁ+,WI=dQ`RZRҠɍ!=+&- yo;;<[kH ަf7g$m^a_iX7vIq1qp~ަ%Yң:͜mFl*7yvFG=v>wB ~8VOPKNBÅ>3yȥI<p,݂)ˬYixc<}yJrXtۈLW8a >AL:Ҫ7$;,6#t Ju$ϵ_K->X~ b:0Fz:fEJizN1$9}i*y錱'sکA,31Q錀zn%9E~fQ^4<%A gu=ORPk{H+tI8''8)HXȑ19ֵRV4ş*OIP9cKqdV/HYH<38$֓*iC=I׽Sln[mpgeJ@)IOl>y8sҦᗑPaomP^F^:W9a m,VN7kهJEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP/1cèj.:qf7:k֨x, ˋS&&8t}q6Nne͒Lt|ʧ) ~pFNI."eAs]^:I2|3]"hJVT*:ԳD#c>Xڍt+ksvZHfxbq헿a+Hwds\1rw y =}iWQ%{Iϗ'}aul4F6ܑĮ(a C4Coc'Բ>bE%zrrW H>͖!փuI^-K@;y$O2i,;"e`[\H8w-"χ8lr)"A+$6 }OL#ѠN1q(DY]T>naY43|Rˁ(`k_DXYz>9hWtz qFdo$83!<6pqϭhCrO0Fʏ- ň8 a$1qš <*=;LO2U}X{L&g&YrIHwcOF{TK}.[9$ dfFUmcs.0%@8- GUm[yќL4eF#LHL-V($̹;Nrq8\\m@m:(@;ԦTX- A^}}O5`:HU `ȱ$dsӎy#G s 0 >r><Uվ3,۞V> -KJHv5N>B y'=imc/MiE*T$>Do$#isI9t*;wlz斀v3oB}9ǹ?XbkgC:#TMA!S޹(n)?7$gڶP")|eLdcӧ4ivB`GhVPѫG}t=)a K# YFq(Ў8 8Ed)LFety`FY6βd3$l98=84%)lAh혼W?.sI)&7FJ4mIIG}-Bg2t$}I؜Ƙmi;]'<HRXE(r$1Q!#~Oioԗ,A3-液[]B}vvA~$噮. 7{mp;9޹?*YN Pڄ2Ɏ "GPIJE7^$8PX8I,0۠6Y31$\IBFF0bVO 0r6yI 6K,Qu) b ְlՠ;DR6F2קؚIN>b[nmr?0{`l)jAʲl篧 C,p[qDF ?Z8}9.Ք<^dl'O#j`C&{& !zO WA)$ʩ1F,TnH#ҫ}ɶ qVEʞPduv-%>FJ*>I#=v=A EP0((((((((((((((((((((((((((((((((((((ϋ=<ƼgRQe:p?½u5x׍c (rPh%GPzQ!\gH#u(bp@8Q"nz6۞Gn\?.x搶z`P2}Ŝ+ pjibQ a+/-}j ă^*7TB"Xt2n}hKBc-\c;Z3rMZ@$Fm8<`=zRh"\3a@K{>);ۘpK{4tO9ZywsW=8=8隞1H#PR=9=jKP"`p u=)FF#^xچզBOsm{&& |/Q\b[[$cT%sB7;AbIzc9J?ӨZf{]-\ܕf(,@⡞[KmdRppW pAMd$WS,{BF>Q .9u1>iht9pcB%UCӃیX{s[0ү)[@ r1?8֬lߖ=29-!cTa{!3yi|m;k1̜$)!^A!Ssej<<Ŋ(pgSH ͻgM$ FpK=`8g=/qǂ}jp708_ʣ,זt^tF[wK]FõYcrzWfbE<-Ҕ`c#23ʽ.?o k4V$in,p8\Ҩ|TtJLRXPBB8'w=;` rG/ഖK[>f ,|gHϸ|qRi:|׉lePQHKϦ\4OK[mGO&)#U˟ M:΋sRT\g,LI c*D`sP]v/&h/# 2`@T{0}zE,w?dkP20w8tjK4±aَXg ׵8 U;MjCPvE>xMGM026\\`:;⋉N w`thtz 3m=} *eͲqu—>ЙV-&OV^0>B1ǡ*,pMSېլX1='=ꋂp Hbr(3Uj 8NI>U>VmރH 3֦:߇+ZA xFf+ƾ` 3kGJC(EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP/^1BL#7fI+a~-^9 '  z})2;Ӝ|=qUFFUA<}xpm;m4qֆr~fzՔ 8;y"Q#hulv b.FG$.@G,=x{`2ˎq[V%ǛBu1.JFG<=r9]КhDh1RG܎7u*?dUbm܁/nhvC܋O؁cTIc=zfYm >5溘r͓&29fֶ M-:a06xTy,ەU(݌]c[#癰@C'jtG7X="qYK8+G^k:Uۿ^DJ!;H#+7 $sG4ď@tM.L:Vt]Z9Rrr?Nr@5mH~{afwd 2)z C gD잀Gc'ocSx~iU9/CR1%A;798RZ1pE()sҡn ֚ybxmϙ928,Xi1Y8?$mŇQǃ܌jYe #eǖ2(q=:C f@ :}j?O]Į}}kxZKu[x@vcӀ=ˬ*sGZt:߇4(,Zxݟb$8=z(uɏrF@N2? r#u_kV[6 -mUY6n?Bxp0ղ|M}J#:uz֬īm{k=q9;ۜk(}cl4"NRI)⹌I>f};|$8=l5b=kU)$XPI9$$O5 wB7@>"xV_0[Na96]sz!uo R[`& wbA8Fpp3\WP*1lR1ڠv' ǁŝssҕ7}ᎄ,H>ի/aSIJ.FJb#)M9$q^{PR0$r0քzLЬM2,{}q[<86DdAmHl''olwN+fDXc~Q:ҝ;$cxF@Of}{qq6ѝU9JVHXd7K|łdZ3Ќj ϴ:K{8o.P1#rBG$9f3kB`l`6?ȫ|/Ar M5Hj/ ~a$v`cpfO x" K's,̠}':9&D`Zca]Fyme 1,B/ #ױ;鷌rc縥i]>O#jl6i ,rH֧úBNG -Ou2Iu2FeSDZgM-H+̖灓zߴiS)3ǞHrCT_i6c4y2I#xفy2JSjI: 6wTHY g޺{9 p,$;w)c cck)*B5e$`*wd+$S==M 1ŴfEq]"Mn0$qzI\?<㠥{+Dem%6d8%$b<܏Py5@dsߴCZk}-ͬr]"L-Dx^F1NE͛X@ٹAE9= <_UlgZ)=<}kV iSOw$KpfH[z /v̲}'(Nq|\f g 5MIbY^і~'`nۘd[vɃ T`OJZQɫ\jN ԚFM ƀ Ovp]p@tVc\\H8ة`RH#tW OAg8vK9mTbkg:1ʜ瞟;fI(BU98=j N!9# .OL7\{u皿"b͎\C@HKX.=QVqu`wpW6 8Ϯ Cs>WE;qՕH珥RL0Ot0(=h~7U ~;W h$~T,:W#BEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEfc=xߌKzhp㠯dFoQ(Fsm%p9N(rj` ''>Pڞ(;t 'R<Lw7tH9OOJ`,Ϲ@nO75.,;vd}j Ӿj [r{ts d:<>mdGN+!R^>S2]yȆG\2  f G\0jWR6&6spy5s o$ vU=BОA9 Nq5H ֊ȑ/g+NA#5-p3[G$@ijm8V$c$ubmRC@Np$zNI5ǚC)‡H;u9M;w<;O4 IY22ۘë 9=zk2e|8MњeHa$_ NYtEk5GpCYsGSeqɤ[)8zZ\:R-ծ$0o(Xۿ)]I ژ 8;I8X ze` \;N%1>a NgH,rgQ:i.pȻNc<Os[iK!zVZ|1 udnp2pLx.!]g.>W죹Eu<Ѳb7T;`g# ,L\#R2@ꤚ[ɋJ2[4Ȇ8F3l"Gn0\b U+^Guu. J dAn^զackvq\cZGӣF& ,uߐ>4h+= s׏"\a@Vb@IQwcM?gW@jSLM;bH84CmG+o& ޘ$\KO\@>s>c1sQta$O*\BI[=N1ݜRX%%[!H~{N?ZLFo9Dǎ4:5ghbRJFr*9^~&i΢iƤs7w sJu GLph6ӣ?alyWHڙ߼n uL~_so7n$0(HQ]a:ZC%q%;V5o*Ux!UG*slUŹ1&B%qsni Wwr[["vMv䀧 ֪xT-a[bU`> =~]ųFw;b@%bNϔ0-yI<E#FPztC4V= XB*<}r2>uyFgt5#6 M<$pwn2vRY_؅nug;18=Ezׇ3ZYοJ)9!Gc''@97lm76qAF<$>nk5{x?O,1q؃NjZՍՕ՚ / #"O$0錃jb2`gp>AU4FuCB?eQ m<|&\ uc*{\Zm'ⴼ/k +B0Ff=Bgkk}Zimon䪰FprF+ْBw']Pec 629F{VZRHl72%3gaGSް|a+M|#8=M7W24u8bxWi,I,pąݏޫZmcqۥv_ dݕr;Z`K4Md?]%j;3QIy[Esk$JOo]|wk\E}e)N9<]AF1$0jkr6@4tViGCA(2-O֭/$Ӯ-fOo6 ӂ>p¯=`ԶIq7Ң( uk>Uk&t,0T-7g yr/p y`81K+/qxIO 1K<ՙA;FTqg#i ㌎ QŌbv6BцoCaH`CqI.%yIIyeTpn 7ekEgV?c4G_Mqwz^w"gb~\c>k̈́8$wM9A'KHolIc3/a+մ EΧ^;2]e# ?Zy9^qS.Kh NcC. Ж1c'֪oTRxGgOfТۘ9ٓFV5;XlO!)8f'=7]*KܬV7VPI%\ώ|Oq[Ϭ!&}Akff$u>߭m(j xJ-3w) ̲v(졳?(-/vnh<%4ڜ8Ɏ z㞕t {t-oC})R蘹OLOhfoV KG9,?^4u;=CTq\--*b9cQrEcAsqsӷP9==:Ti}PJIE o3ʜpyq0 kh=gX*Ŏ%\G~MrX3+[_/TnӤ@( [!W=q=3U_k!t>c3~*C.SD2.({@]8P# 2x"n lG\bTp;dTa2HY6<5v" \*8S8>v8鼀Hg(u٩ X>=z%yV-IJGr;H EP0((((((((((((((((((((((((((((((((((((/ă:wB>2@'H WKuЅxnj7]Q5s%EJF܊n913V X\0nNzXrAiNtl|۞í$bL x ɫozD.$!"mqO0~R8(MEB7P&LryXpe)Oh9 riڎ.<4 5g9ֲ,cY '2# *ʹ'F>oc,1 }]l9 m/;98;_alqѭŖ' =})N ,qEt\5i|ϴ6٣_ՙ,|=hyGᕏ#igYG)[E7O]4Z_8Żv6HfzÜPt4e(Ҳ4ݤ.R˒H<('-7WJ,ϤB4@rJ̮;u#H F"Cr@6@'8>fF6>hM)2 y_|AkDЍä7kO< _Zí#YI0p~cdѯa3X& pĽjǺ& MJ.AhR3 Z{KVKѓ˜lVI%Nͼ>֒ e#ʓ0*1o-;_wF93j1{v $Irkt5V&`p1M ;_iO1,,#Xٲώ.r2njM{ Q[x8з 3=:O}iM<=ض4m&Y (E(Bu#gwWj+Rqcy⪢ܽs 3))!8SGScڟ+ E5; J9+xOjLԜ,pơvD27oF\Fq߭H$^bFQ3HV&(8T[ FFGL5zo֨AZC%aTT?ZFyەb :nݣ9פa5C>IG߽z%y81.3i (((((((((((((((((((((((((((((((((((((:-+@ }i3=iRQI"n$zU/}g=5Mل# yǭ^Y.!)I$'4"-WR:mYrqZwol=(͈tۙLu(/"}z^jK˛#hk`)EB;;=R+xd$Vބ'9eY|U9Ff@mX)=yNl|ƬO &{cvHvrO_iI@K4YU$ {c0ISY"t˖iYZ6 H1@'B(GN˟0E"!)볽6Ci(Z}# o.@$}}*;[鶗"}z?;K?`A\rar2Ea-BD pH`t6t5s(~%Qq5͗ ;`@Jw]"qH$rOSS^c:'4";Jì5x\~SLep˻$ ׎9+$wBf [&r=J'yQ6[@AM4D hZJB*֙k&3QrEz?M*S+8P1$sg{1\l>s(C2:2*AEzϋՅNUFJ07c&OӜ#WѦۙPх>F?5N-XL[b@8v޻ os߯4oUvǶ|Z4 wݵ#9qCdϰ iǏ_j)ҿ`{hp,>'~hs+|$fhK5:,3Y ex=2{ޓCiyeYc|dzn Wb=:VQNOjrDQwYAs,1 ۺ15t:Ŝ$W'ʏ!;GR{sS1D`h"cllkSq`J0.}oTgpq'Ma- t0+ ꠜ AG5? 9g$+= S,aT 'CgA>cg"qUXC!?1^Ţxڗ y[kwrHa ~)iGul"|yr~SM; _JV9uڟe` <&a(3r̪XLoc0|rΚnQ*'zVd\9x_Nk5 2ytK ;N2)_<7Wڴz֢eQQI+bzge.q Fu#W9l 3[3*͉P}աK K8k:+9p n9ǧ~ H;Ɛd3Ԯx{ډWe7RG!GKQ 5`v6zSܞjvig4+e^}pّdӭf Z9'%=³ԩ1t9昉WbT!yOnIk(8lc( 'Sa2k~ѐ?<gٶM-vQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEEs˞*"sG׷cWxqAd1 g_es6K cQGS1Q9l < I Sڐ4¹nrA0:,YP]FȱE>U=&dUAF(=:/ ²U4au0|,yq*^Т7lI tVpz:z*?Ώ|M C,Aݸp~a{Kb>ʘ5xڮ5Q=Lv\'9^ c9$2B:gjɭjSiKۇ26 `c8v)h!; srLg( Z΅ㇸMSe$K( %-Hzr}Mjixg@ᶷ"\0f1vgiq?x^j҉8;m)ZtWrilUsf 4xFHZ^22J #ek~ֹ1#֓y*rz JNq$fcԖ6qƲ_W@N y$'ߵC4 l20OiCb[ynVT9o09?`=аi[UŅXʏ|bx%KhY8[#ӎkmIcܨZux(I> u58x<=b67G(K%IU.NA16g$`Cx:UVVb9䲓'񫦛}>c=ckĖvڋ1#ݞ}=ʾ#OڀF-"'rY= }/xTkMGT9ڠJ2d>c'MK#1m6;\D"iIS10HZѼAZm܏.jwD2ȸc$ pFO85$e <{R㌐;U)ɱCeE./BgFH מR]HU6Er?4}qЃ1Y7$}BU5A3ސ B5ۃ+FC.9s+>&ésW-ғ׾#/]%ZኟQҺxN;H?uQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE5r2WxOz~5>3CE 8w?1sҘ@ǧ0sr^1V"< ݾ)td̓nU$ v X ezmrmX&Rs=Y;@{<% Zh14 BeGgY|.0qAsW6׶rA:K-ggn=G'jg1ʻ!ө]=8`8msCM= K nc_í1/ݟX$f<~%tiWʄ;?8zRV"DV[<~|($I\nfXeF[hb}3ґuqV ?m'pnm#%AT)*v%SۨYڔ[~WʁNs٧f/\F72u'ژu cϒA_$;K%qKuN@#p8xEfiFTps dIsiw8-Έ,afh#ֻyn'OM6X$YR\Gc}k ζƬ7`"(;U[C@μ$gnT>.r8*.<{r[*cThgX)#lV:P*+i'Y&/R08cwzӮdݒXAm$",\ctrPw; I*%TuL].dF:kk8&dh!'ag,H*UԯL`Ż⣸ؖ)YU-U \=FOAX'{U y~US<~< gY5ASS2XE|/9qG,'hVEbPo#88\t[r!e8;y]jᷤ@7c[Sŋ"ayW$n'+0Ld`6O_˿˱CfGU}N>Z4 %H򀑤C9'<hqk* `BͼZ2d0L'46hYAXR;~52įz8GnUiL0:@qdTg)zZʴZFpt4ԧ;#foH$~UAyI4d [1EQp 88Vӵ᷑"KtgOg'?=@xx[jXTn< g܊HI";,UϻmN;챲o7 ŏ3=*p!wppsz9lגQWV%dsbEW7|kL 5;p<^V'ؐGdq~12Mgc0P`<jn5GFH;3 ыE(a ٬IfR@*Ќc ti1l{'zs]=s~RmqQIH(((((((((((((((((((((((((((((((((((((g ;{]xM~f,I\q*6#Ol Tmq&8uON}T^IML} `&4Q \I.#7x'{jls[5Py `76HV'M,F * pN:zڏD..[f*f{\oI- 9w}($]O&ZBo+fdt t>L&$I#*I#$H3ګϭ:K$-lybs-z{H6>2ct'qǭcYz}g 㞔e3Ocb֢D-0G^jpcSdk/ry$ugۭ(ҠzRfI{{x3Sçƃ%;t>Ҝ}ϝNߗ'<jY t=N:TDR@ȫA= mwHDUH늵KoURUʐ[NUr !#eI=qw>iKgʱ*Џ>ʒGc^խCönyxb7w+ a1#`g'N.cn Օs?3BP)=:G˸.H ܊cjGeln/:P0y'$gASEd'MmY ΛQrry8>(Oz`tbTdHҔm ǰ<}*,ohy% hpv2"ϖ33Q1]1#d1F~zzRE3;$i_>o1]v<# L F~*m?Ʃh@xM^OA!Ԣ֛jN.0$=n¹瑽M/ֻ [Eskj7Yt1-\eUT1c9zoaKUTIG$2}ߝsIgogpQ+I*U\oO$p~ p :g4C=uxfcco$92ڛzp1MyNlo,):9pFF5LG,e>t7 Ja3Agp"ȡ(h|jA(_l }b;h\LC8Q1$TgÚxmBA9cl1p/7[v&Ffm,q՛A|7zZgEXb`V āw);!["6G^Oq⚢CV)>_+׼9Yw$0=ń#4c,>pJni/cy܎iptq^JI=  zU;ӶCS5gf1QtmGczWT F[n0ry<e鈧OZCo]Nǧ"eX#Gi]h@bޮvD%3Lrjk+Gyǀz5)?{ @zy~U|?ZCzvwUyb]ݹ }*\;y@"x/HִGc^LWhqw#= GXTl}7o<%`<@kvQE0 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (}45ةGB{6fwFZ$2_4>?w@Xm2* ;)\a}ϭP2)Ϲ-aAn'Nt#(ڐ `XWT`.1zҳ-˸c}:S@0J$VH䉡1ye6mB8o܎9CKki?nLX ה`iOzO /XVZUA$Jϔ2퓁>'4ؼU"lopBsԀxWdFx"#/uku$igMe9 vT9L7umqQM$qB6;==k/QA!*p4sF=R{>KV|Cto^ZOylk˻!gk}(Vr@,:6չP[C&ļ"7Gȅ=V?LVv"acVx^I OJW÷w+m:ڭ\,j8GjEnE5g[䀸g"EeI͂Gy>gcLaPСE:IZ F鵔n=KM+Khc?xnQ9$r>䜞(QwwOjϢ^Zn@;P"ncjwSk5haX >Upz VFR60qLx%*@.{*K2 6!?1ujm]~޵L99e>wPrȣ;HT!?4"ed<)_AޯDn$֪݃C$@hv {dEnzrw;/ѷ hj)QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQETWG.tƠ3nI'4JR~u!Bpq۠Q!b?Mҥe@N*ݟ6Q#MK/w1f9$d=I7aX+{-GtSp9>IFz~""2؟ i7~i!}c?= tR )ۍ3VWA 1~ePdF[Ҕ,up)F6PINxJ(dgs1$`w0?(M3[eqP[w&,o,9-u$VLj͒7V`njv3\<%h}Ӵx,(\䞹:NԟbӜN矧*kvOe^#`0Kwc֘ں3H1 '8,RK /'8>CӞ4>[yXipࣣ*sERMzh6[ }Bi[]֡e-B‡3ZkI.>gyP.={#Iï=|V6*,b6mnqR r$kyf(NĕAPq'k/32=SkZY0L\rq^*I-6YWhՔ g {Tn#Ռf"7IB^: >" eތ1\g#֥Ok҉+Enn]V!}zr= =۷ 4kS&lI!,sҋ1jPMZvPJ:޺=?H.updw TYkזnFyO'Pxgx`PW8ZpѢew`;"zVtRobINw)H~S$Ҫ yZmW9qԱcqo^Kڋ)19 y?L?G޹M"IdH@YQAI'^˦iڪe\G9:KEC ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ($i$NH^E ~谔v6A9^`P5&69&WFqYf72 g=kIl>Mҟcg+ |3M:Pښy`K.&!KΤ(pdi~`QCHu- %_5`[1MRYJGChSx1a$K# 01JcOkq$p#|IH1dO;#+-gb`W&'2$a<(6N1޵$2\5_ X^o) ؎_ǯ5HܽG*OG3[mrsP6u>]7cU6 DŽ;Ȫo& g}1SB ),=JOJ9SnnhP~c x9΀>^#ʀj ?_xH{S7yqކAg1/n%9XmsSh%ힿ*NU ֜y1qG6[;H#J@dRzsG:rs&IFˑ^A.wNU8xm׷K _8)ڤqu_9lmTÁAGGAcv4 c}+o\<1#9?ۺc댊=`qĦ@*OyuK?.rd~&FvqҎt$U(Umc:cq5>O`ㅍCn<?1G:gxc,1V zc?R `thgOGnmS<]8`O.B`FA<ҏhqc{1WAOK9F]xg|,A?ݏ#TUqKgYhb3+.I NN1}@Ӽmބ]]oƊŒg}Yj?xB5k]!JӞWLKKV(`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEP2:yrƒ)AYv![4QʀO h鍶@c?΁HŌkQE1bhSoz('?w#O9ԓZQʀ$idjDѴ'6W #FӃn6b}K01E@PD#EE0H4=FKn,bhZ(1E-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@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@qdmr-0.12.3/doc/fig/gd73.webp000066400000000000000000001600421501654372000155030ustar00rootroot00000000000000RIFFWEBPVP8X (ICCPlcmsmntrRGB XYZ )9acspAPPL-lcms desc_cprtL wtptXrXYZlgXYZbXYZrTRC @gTRC @bTRC @descc2cicurvck ?Q4!)2;FQw]kpz|i}0textCC0XYZ -XYZ o8XYZ bXYZ $VP8 *>I$E#8p i?xq{o6s|{? ѯ_s?% SˠǾyy_ދ]Mcg׻_O/GOw#ڿE鑦:ͷ~t|>yT/O;?O_?~| g7?yQ7֩_ퟦ*.E󿺞Oryga?Ֆݱy֯=An__9H +,wԾ$6wC32Z'wȝiα~[mB>Fm]>BJI/J&_v7r `"k3dSj-7z( j?Nx&پwq&FtGz3% c|񚮐33333333333333333333332u!D^6~ .Wf%o݇3333333333333333333332v E04fay5M6\5!I7>0tW|E|G {}BR ٜfffffffffffffffffeV|:@7h6c\VpB(jg{O0P!Ã\! TG-'^\lW{8E ESnkAD)U!mo9MlΆ,Ԟ4z8_8&p43O=vP .RЩL7f<>mAb&tR7wwwwwwwwwwwwg#Lj%&1Nl^$L%+_hQ@;\ZٴZ+b 9Uga5;ՖN~ r,ŰSXA錥2{,&u9*i/tT}+M>ڿڝT3S7aVB:*UUUUUUUUUUURd?"NAB{ %+n;Y堿+ g/ߌÂȥ|{s6쟉P!@xK,,ܕUUUUUUUUUUUU8T}J/lZ߂JHq*O&.iF}ڽ[jaOjo]\p{cp Dhs6 uE@DPs O$ۊqlm_a>FrbȦX焁l , mF=|hZw^?cqRؽǏz7XoԈivP|r)a4p?AqpKwwwwwwwwwvH^]Jd'X'N9:R̸2LRw8)ִ~-OnwirF*Ln>98sR" Iid`_cffffffffffVꈇUSlz10M4|k@b~NlR@VA3fYyRo<`[Tak+C!pu^hbu5\WKor)4z|슯bqjUj*BeJY4 wwwwwwwwwgXm(x ND*Qdm$ېû/MĐ<R|[C*)1o5/H cOܾw킢 ab-!l!!,݆[4&1#W;^'nv,~*x,Ԕs-@BXcZBT#]W4k>Gf7>ƼLU ,'1wP(GHqu!AA]K=%轷–OkAt0-‚byS$@ъ6p^b鎥.ؼ=p*]GIDxJz^~'|q1_2Bd9Td mf'|ԅ-_ !A0CJvםz.T Of;fffffffffecsxS->)Ps:^4BܥE }XW:B#wfCT4b[ZpP2؝<⤔cOnQdFy;,::!o#МldUUUUUUUUUD%bf0;{+8"vF,ش]Od+}lUÁ~+QapTJ &ΰ7EwD" RE ?uzD,6TUUUUUUUUTCfw.1pFS'֪IlHŤmrS/ucf8,',cì0dA6SC'Pw᰸zy/_21)Tnt͈!/ס~\.v"oWb;Y}Mqώ JEG,{}XXrׄ;ifպ|y[9gHK,9eVSGgek tVuGw`}Lvؾv^%z[7|겎oIH-]wwwwwwwwwh.v}(eyU=lX~1:Bis8M Q߭Ġ!3Hs ./Qa<ܦ^5Y4X<Β`,YxOT+?xn΂ffffffffdV3ཛྷ2QMO NgmitTgV1-8&t#*ILEV)_?5.HV? 2h 0Z?gPY6PsM4χFS`rզ7>dh~'qNR`?4EN|+[5aVeh <,zV~Xw/DB9uP׷FIjx :55M nEDT+Oy[Ӊ;jlUkH'?QҦ٭R7^&I_e*`ܾGnK".5,ٟSZ g6?UUUUUUUUUS.7<88~ ,4LײŦwY'6UQ{~_8X4)ϮPlZgj?zoZIƥN@ٹ>ѡ,0$p^Z_ޅƹaRUF?Ϫ}V-נIjy; vS%GGGGGBn@8p?\_*SZīkVz宦-8jOA\+ H`z>:M,{ E1M^Ū@ۓd/1Ks %M}&nwKhot"Š!}5mf<"͔KY wwwwwwwwwwv7[U us^զH@) <&N:h@ws)%wLڜ,d)$$ _zpoRԲUT |JyJF`oDTc5$[?_j !H%z Vt}!چ11\&& ƭ "M*]p泔?݈;Vaf]tC"7 $kO8 LżkMj7^#E T'PTOblۮKZB/A_㘈%<$MP}Y^_e KbRoӫ@3:80b=9V.â >ffffffffffVk|>6~D?+un|DpA7-aY$H=gFp{Yka&D~ sUdC@LmmܯH*$K2 -W GGr%ҞSZHXBUUUUUUUUU3;lacaΠ/QPtsB\1S "v{-%uN5vUGͫmUrxdϋoZUUUUUUUUUU*AwomGܵ%0Ё̠0 Yٛ#YymĩQ\ ?uD&5.sRh6|2UUUUUUUUUT*=2lXE!1ưQ(vZOO<^j=yOm%O|^*l :1S?b.GX@=wCڒ5UUUUUUUUUKW?Rc!sT>wl0?ne=y^Is)8<Фh^3ەr~8\*Ls ɗ xq@HRcY|iuM7"9YSO܅:>vO8bB-똲཈ꪪ cA)#mp@#;h]<@Hž fHWp!i uir^KW_@6FmQy 3,444R)D\{hc2;u,Rײ97=Tr :[y+d333333333332e"4 x۝㎒hVuϽif[\|| ,Ϣ~PǂL7͘H;+S8!Ya$tDJ8d+3ϧỎFu2|v_0dFn d4}F3gJ~ڦ}q|9$2̂jdyȥ\ %>##m@Xۄ_͎R 1,|>4 \z^j5WF<t:(;ko##Gۈ*F_2G$)E@qP ! ,řg UUUUUUUUUTIA#eCYذgtJ{mأ5IȈ숈w̓&h`uxAѥ>)gC3rQqUUUUUUUUUSPAugPHBij}ƵoI -| <  5r{(l+a4Bfʌy4*=>E@!j9sǸ, LWWV7wwwwwwwwwv(6bG6zYC|57*퀈2Os-0` xϑO>9?q(rA's\d)<ů1+ŲV+Uh:A%\ *vX+ZX+37L|߂:[F/ `W]\46B$ w;K*ĐeCN++|pq!V\mpOC ZiMGЎrȂf3IcpFpgɾREZNp4?Ϩw$) żcݭGTD8ۿ %6Z3v*v<ġ k_*y%u|`vݛxYfg~dzK/33333333333/۰\t5N$~(&gc,6sRN}VPqdR$cV Z>}mz'N&1$nDn 朕Q/Ǚ~lkt]š,ztIU_'vP4= VDoUy Z6.l=;tG{~  1W,rEa=KO[>w2ЙԨ6k_*wUUUUUUUUUUTI_w)G-.n 5<"cꩢӥd}Kf TB !ȿ >n|4CG9r vdkzLsn@o,i)V)7Nwwwwd@OnR).^dTփC|i'ZYКGg|\DW&2^]n5:_LƁׅ?y(2%MΗ2kmrsS3 c6{ @ bP0鋢f/d=*x[ 1H [觀T S>[Y:gIFzi"@pSkZj J v]ը?7k۱FK j+9}arl ~?k| Y4X+ȁ#%n R(^2gjg6s櫓>d.R#'٣XZ‰߈9U[ OC!#zx^vPVM1>e,8Mk M)"$/ 7gS5;d/e;/TwEt! l՗ݿw,W5Ԭ~r 7J6[6} ws+m<BRqBPT%D]]ҏ6y߲8I+hN2mE}m:+Zvh" _҇hEjeQ! T*LOBm d< Ox2 sk<>,a2|rcڡɝ9=nkӻqzPq@zTDtȅA .>#} UKme-,MĨ0h{#HU*#ĢTP8@])~:hƨ #: }ѧ95Xonp` $PW{8-'a.~ <&eؠPъ_I)5M.}[TT{/^F]}vY":fJ-CL4G}Pm.s<e+lSz| {C01wwʺaј"F\qVN/#rxc'ۓ5tSV{v7L@(f@$qTJks}_̬ rRk_+HwkY9)JW6C'EN&K-2QƟNm{CW#U#vEc:W5S##pxHeC cg}f 2S0]m._ᄵU&iХa{L{ CW6Q $3J)FYo ;~ն:TG+bwT='\fvvo#O͋R;wi>c82*ͮ(=G]H+f,:ɘB4PA!مnk.,0]o^pzi;9[0/z@E-m5Īy|t:.єGy$": beWwjF]qjΟCղeMf08)!"@!Z6=(VAl?_Ƕmk pVjsvfmc:W}M$y,<_lUg7a ֵf&eIz,<2o0%)#GAJ{;<`:W`uy3 `OXs6&lYE!S5S'>)&t 0mh^-ir kLcg,atT?#t\z& ⎒IĚP;zĈYDT=^ ^tZ(3;z xF z4ɷ#Gz 3VݑvX/l:G^̎8BJ aZn@ ~.R"09f3I`QƬՄS¢NpњFYjX` .pE<L[!={%\<}!g7 YvA,1xjFF섰idB)i4tp$ p5-?kqNHM2ұw-`UbD)d%zRceYFJYcQ !qotjP:RW9+0 fuM +2ݨ|ƜEAO f )Vx?%Eu=S ev%߳Wo~N)3ABQ"|XIX R" (k:-|N3JXG`%=@~N:Tfj^XGC3fġkL(Fv*VI Ԝg%6KЍZdFLmJX$\ `ԲZ0k % bfJ3 }^"&b@>0!#a؁ |B1|Q XEM8(zEȂ\;uz ݇Pl:n%,+9S.giBNiVҚ#$,\%ŹmL#y'Vg.RVP6 pq$?ǥz {n9Կq m@Ɂ\^F2%~:m paa!_k>+q62xnrk RO%I1TQwǫ+ APrt-]orY ,yV_ 3`49[r3mے*YwG1⒯ :+V {gɢ=ʒ[twmtO  U;S7@I삜*IZ\@,xoQ7ףaZ˘&SHB?[ UmHP--iZ|mqw"P6ef%\`[d[D&VowT!eQSnɢ14(X'S hRfwȵM9j(i/daϬoǐEu"`*W 4rN^:jf+!d8o ** :rL78Ee.@׍ܟj9opN-m1\sXN;LCڊS\_/) Lr9a`QU>8 UP,|pɪ&Pd?Cl ΀#Ļ$?koF0O +S!J qj?~W?Ρ +\rga`h=L~W{(#PJ0bi؊)FX8[z)}=lնMʖ)DowGG#}k$ȯ<p'H&j:{q@䡗X'{tYu1VsH9au0hEl g*/ȳߦ^HϠe<ɞA9v|ǎzSNZ~JUN+J N ~l9Dah\z:Hvn2("aIY]# 3#SRn õ/>ydg^\Z;x'+ɻ5g]MJlU73h#~mSku'7ŶtVC?CiMS¾*qKЩ"NL`n߭P$%7DBTLP+2Î+Vm^cSqHD4|{ c}``3,SͷY,d떄͚;-5]=fEOw{{`#r,i `qpsOr6:h˛a$9| TxL+!S=+F9 2a]i!m?(ZkxQzAOALIAy~}k PFF؈M6ETLbF6ϩ$*T﯎5Ja}UD|XY_.U B,E394@ķHE /{"%ku`"e-U?,Ն?Ben 1c͑e{:u 83ҤīHŃgXCoaS$p!v, gf8ݒT30'àȪd4L(Gyìis3n Z؆nq!P[0Tm_uoZuN;(4 }5ޗG,M!}aܡx_Hm]J Di[>yW]VB:-6+P۹ʟ,⥤ȇ+0eϏL= /5O`&]#SłχM&dH]8>$ۚʾ+w.'Ese΃ TF—__4w5 -AeGgv{QٸMs=f{uI˿r sI,J?n]Nfj 9 f\%VoYixv*Qy6kSլ/ /E< " )5)!2c3 ;qF}ůr ׄUӰG2nY  W[9O!}ZΙGڤک*NG*㒛zSH‚t: #QKZl^rnpGvkW290 鍶$mm [Gop<ޝjh$B/""(<G(hѲ%ff:T yA x%v8M7@N=k؎9"dIYPeX%%}eBawsGfQ߅z*uϏݵa̐o\(kWFm8WԞjL`79֫×.E-n2O0W? 5 RA.Y"6R@y,DAE *һDeF6q{h㵱<k+os޲|~&BBQޚ Ki).+Y7ۼqjď:hQI W ?*DJ^,rĈ7w֛.)?%̼t UQ G-Xߚ~hc9t6=Bny:ZI0K$d̘l?8BpY>=^ʼn9dARA\((JH Ǎxʂ+pCƓf9 ns?p!r=QEh3C\nb?܁ Yëg[|^t^=4FW&TgdSTz 7)^&޽Mq(?ZɎ}#7ʍ UR!ǕZߎDݨw(6|IMO}pQ$s0N(BLZc `p:, B6nL4-sUG]A&寰۞13䚛4z'R>Ǩ1͉N"-E F(!m:k(Խͼ}Xv4w1tNoIArkl<2'/'3\e-hGIrOKQ w;uNI~<-G[@wX##moƷ?DmP{JOaO+aǿRU7l 6=y}GWJC[۵k{>CtPʱ[1{rp?SIݰ".Ώ{/RDdzzd?c^׌C!`f ;^z=9}??JA~z7'PqfR"܆m ~PQ|uK!9sRI8|̑:k!L4o_BOzZi;P{cgyOxPw%iKX!O?.ۖ }&`¯'Ůq& E02X{=e@`p-$LO9CHFté'BLd F_BnƳnigVS}Y AI*Ow*,Lf1DK_CFNiz;@=ܦh畅"2V! R{ GM&f7z_N&iˬgrYR+$qR˪3 ,%-#ݗf:1BԢTj~n}d;'4(Q#D\@Qe=ĩr$je}xtsv$e) x3SYu0bb!mLZGSjT)i{,5Lfn&M(gިŌ6,2 GxX!XEҔ%LXo<RP0TğVG-ex1?aS*6DNCůλܶ1Q#J 6Lġ!LǜD()dL^x8r)⥧t&%3*,?6З;8_AB\R@B |j7I u .v~!H7 `̾ݵӱ{]rbTI00o0!GuêcR =$$ŏ5\|v(cɬN!0by:/Ɖl=\Va ;^k(ÀF]n$x|-Nua"hEx)m tc(e@~=  jԣxez}4F)䥁Еt7:Wsh|Yn.Ā2=q Q (Z r%8l `X ԄWafX/ Nwt=ZMcY͍#SZ$)()Kc<cY1KIC&&pd%pS WƢ4~>$vZ_ ewYU e=)/U@`L#E0rVVudelĬ+sA՜ S[%haP U DiF`UKnJ*+N1s# —A6U\C;ǘDJ*{f[uި? h74W2Mlfcn)bcЃۗ|ޢ9ސfV,LU)$^0TNdztyTB6a>^>`tZ ǒk N:P#ZO UyVr=F>gBLF~+oyGyI ƍg,&CR0h=nOڰlk=bT\>շZ*Vr@*iw 7r_T<0K%uN6!%LYl,IHNPxnvxO72.x"^l l\<SkC5ۅ%%(Cd&>nW'bn>k`Gyj5K<(֝|l$iK'Ղsk[-`.8UƂcƃSLmEj^cZGi*JىaL$D-f$"h٥Vh7ju Ldd^cUmO M>93qK6bK$|LzmBZq9)ɟF{Q1#ŶEmc!||d7/>p0N=[QIMhQHVmHiC+`6+a5i^ g4f۱_kK9Ha5 Ybs ⽯t)erutr,]KzĐA(wSG٩2a @8[sX/o /6>l Ǚ!`NFgTrz(#MV׳mA#B/@;rLYL ڡOCߨC\ʼaxȻ8%(5d[XxY=~p#D+8dQhKl$%Tb(N`|?͟ނcFlF~\[|0j'8SPļRQi0NXD,I$k/ ],F#D:#;Cr QyY+tBo} Wy^sR~rţZZ! CX4q>KyBPxT~u)tι!hVQdGZz < :,#2sйa wBWLnҤWIriT؊roW IfhnՖ4 ( y꘷13$!Wpn-`j5ƺ NTlGMRthY[ R%ΒR 5Kِچhc:;AӅe!|/J|#OK/lʠkGrS9sɫq$Aģ&br(B"N):g=8'w cbҰ^_|bUHE]j-5ZekkN #T{w|6rbw&#FKh%ԬkiٽmP"mt*IH ^u% $L(dXd~:;jD ͝v7Jyho84DMA⢒qC$yk.4_ p1ՉζHy~ʊ<7Vԓbc#1S2g=6c*?#Y] |ڋ[7l8Ow!aBdҤ{ᛟD&6 % WSfMAk,h3BgĴȪz ./t`$aZ[ZlU?Rnod?Zy"'pp<1&]Q` S7Gd MnC,eY ֞Tnnsa~9{3x_Y.kTjD7ѱ#Μú;7G@A` ;?aU92K-{c ]):t˄̱% RsňwT:"Y7b"K\@>*Qiԁ-5Faj W [q;$,߰mn/JWB*j׳ܷAͶ#")32_:Z>5"]!+/]51ۧU(u*K.^mP?Ag-Yu] <w9.scK1h^Wb˭-OJ0j6)uN@cJV*; vF?ٛu^Hཐ}甑ڲ?#,c~}SUC+i k#H{vUw.Gd ʉB$4x ZO!pt *WC 'G1Ё,-?کs}=QW`A@\]^y$fAX{j ublg ЈK^1x5n]c &%mEϾ=YS-ڂ{%w80$Z׌; H/yœ$;AC {c+`"[ȶW[/KyJ4ú3`[O/.ڇhSay"̞zH }kP\LnniTVWYR'Eb3ޥ.4 9M}b/8F8{^dPY}o8_P, =M3^D?$"T㑾nеEQgz ݛWq}Gя_" é'r?Dž2;D&oC~#';j"b[(qWljz@ԏ{)7Û;ଛN˰4Pv!頯1Z|i1뿝nc11iBTD⸢US&k KE@2YI(/T͉FF&&GCjN5;Zx#2PMVb&:d(OvBFO)]V* GAؠNj850BCYQ/5Z)4q-iQp*Bb'QO)漐VqqX(n`y ;˯uL]%Dw-HFLiG=آYtө}L He暕 `!R}Ps%~ %C\QxK/o᭿Q1C8x@#J6޻]CxjE;E֙էmhA\,ɓck߁w|Lԝm Bmo]bS##CKrYĴ^?.{To6Uvˍ,=2Ou=!͗&;\/z_! rT{RQ4W~DʨFgU I+hD9CwewUw=}2HYrE2S+R f9ٚp9D41Ap/jQQ 0N]J2?%d 9ߦߝN'yDfbo:IhrinSzI0_}fht[-HRUFA8eB3k`,n@Ig|uk3zUU&0I7ֺn%0!c'p_qYbgPAB?c(p#Q܇qCsa^^acw`ҽ3;دkFbvt<1 fظLM.ߛmzVH<:OAYHԡ,W{N@OlyM^mVF›杭E" W8?X| rYduEni9Mgb!QUu "YG2cFT>^T ,ӗ7%5)E@dt UG -x/lWx*cX jz٬0,7WmWR}{,*rx뙀BUT#*Ά-SvƲ!vF20@v$wbTFgWx=3+x<A5K"Le]p}<4l~ p ^g{"(N5PC4`'BnQMWmMZwâ/刱tмf(?o}l1'޲i=@!̙nƦKͫpIQCusa@ohv^N2U|LnTTLҼ¿|\^Ҡg^rt+gJZy/l,~W8Cœ@\Tw SKT#;lb?ٚ(J!.d MXFNz||] kW`Jf2s)KAX~gngyn!|rs }6ji4m=쵽DOQIOFY(tSIћR>HY՟@E> "'6Ut6C'{t°&C,jԉRCmYN):Ϣʦk0&I>1*:ogZEw}evȴ8i/G%P\M!#n fNFf^̏ٱɷۦ?9rJD[T"%#Ӵ! j[ 8mڜ]px%:> 'DFN!n 5WqHfC<^W(Jv\"Ooo#9_fZac4XO8X,hN6u7J_H4FY&F?[B]lTa;yu7zXޘQAӚt" >F8˰bC3%el"o3!82G5 NtAq" 1}Cv0㛯n"45TqȈ8U[ ֝c?tBtv}X ?RB(GZѹNyJ2}-tfwwTs;+`l8B rjU؀~|u]嚸#u3;b!IgF'%!GBάl/=J\޴'`ͶtˆjBa,*a:=Ɍ[jǶ˝9 9sZaMYW1aY:wO֘ю.> \uyrc>}ziVvl&JH2}oۛ_fnZ<j)>ۺٻˤ^̴\׿:\CwdNE)8& w7`r͉q>mTi-MK>|w rߙBBpf-%=Ҵ'AnF%lFTN)ev@"VuÞ~h#_Uc G_ਮzpux]Ѥ+>:+x+G 7,saxhuX6l]"QbKoXQib`Ftwn}?3`p|]'\SvBʵl#m,ʀF%oSֿKXsn O pbJ=ǭml}p`Sz4+տVdSh7;lzwFƍ^:[J(rD6m⧛(^U^4zDvx!.!u:hI-rtj bӗj!}._RC{8F؉ )eԯbLEx` b:|i*ۗ'm8alxG/HY5uE| zQIhA!" ?¥$P%V5+i +@Ԙ04AD%F&^2HY/z7ٔrn-T@;@SB<{GM$\N dFm.cTX5(ݦb VyEJfn|ms^ttmⵍU/?n'BqNR74ʖzS`C9Ġ{Qodz)U1wQ31--#U}2$i`?'ӬS{@*?btu<8['\v}}^ͪ 3wq4E_grjxa~R2 C_:F1FUwrmV@IK1o9s!-_9*X@8o] f Bn=pxa_p8fCB=w9%zKm\K_.n1I)]XI/l]%YZ􋓋<+;XGC-YC^?Ѡ}<" RCvpQo|[Pи JN(G.穟y4k"4@8Sos K";A{+q 8%ix5 ?YlMg0; G{] m5k)e3޴rQP ~Fڱu~11!O8f@꺢;eC-L;|kԲ fw$xV==?iԤw:,#XXWp:A[[rhe[.I2:G["  xD ۔[AP)¡"5EGҤ{a^#'>%S4[+J?4H>`rXg/Bc ʱslTT X17MG1By~,J+Ot~C:iA)SV5VVXBk^R}8byro([*0a2y0 Cpm  Yo]c)Ae }T}0_}P^@qBrhyi‡%E{~C\8VVixilVKG.L@(Tұ≗O(Hr'QTWg3ɄMF˔] =]ba[ <\Ac k=n[Gˏ( yA -_GMy.MFnn 2ŏWZˆdæKuIse-1V9P)SE hz;>+Dw}5eQk)Ŀlθ奅AL0FdCia Rbl=n]is%.2S?!#៿R[]OOUAHr[Q#.X߳ޢ"9Zvy>Ui AĄtGSAMmnlőu89## _p~P@Ai@р8 a8gi!{[㘀cI_j }}XM!/ӥ1x*URm q; uHwӶFae^cGNfM0SFtL΋5 L]X .`­vb#B=KxYFen/%wfH {L C[t1P>=j \zMz=ZpnN8f0™YbQ쎯Awh|x9{-k/wC0,Y2ʧGD3RakQ9eY|AYҘ}ytXS.[BKbl/Gw G 9TMWM݉9C"%Ӱ5ο⸠^-w!~1S 8:yCb3" GE9r-``vgeG{ )sҵg:4 A=(wHl{1 俘 @Ψ3|݄8X6f+Jc3Pj?G3x=j WNLStYWE Cw^r@Gs[hp'1?Q̛/,8Gt^NL`K\Vuyr1`U(F:]uAط_M(58x̴ﺌ*n%.e>j=ylu`Lj(q,*IpNuxJ(՚|mIL@tXf!ᇰT \sN>麍Ù5y~Sژ({aAoPaT<0t=L֎ җ;NX 3$ #ON`,pBI{s2܂PRzs5u"A cݩyb٬8OD_Vf@eZt#5Ed= B&3d~ PE[ $uڒ-k}䙄Xh:SNp0KGF;xy!Ii΅.͋oAA? &۷SmI:EV2SA|OtZVp 9Xr%(Be""4; 6}P]e\3vx5C1o?ભ~|}Ykn-cgr8~FD0R Y~9HBn֌YM/B@yYu^X°T 1 MQaF\HjB| jr%cÑLe邕qG埩ފK@y!g·2;?| =O2*8~8>)?V?$阐F0r>@HL|JMxY|XH`9& |a kp(&aC&;([Xֲ>Hb?B"dHPc1X$gCz1 zO1.[d 8(C)`)b-^D|֕#ކ>B[繖V}yI.WZoN;p9E{pSRÝ4m|UCC,c q.G. t gQxmf IkW(VǢ4x4i߿KR*B,3MEf@ޓtZFS]>5Rmb$o0M ֖C hJM>Ԧ? PL.HѱLhn%{vZnYߟ ` 5k@j!` PxNfͫL_̦MpmSsmLOB+ `WY FPyӉDڰ%u,`^tII:vIWA`,oLkG`4L8~6QV?rec 6h) VBRԕQ(m,f0,OG,~xq j1M&経9R]JLe GaN'4g]) _ ʌ[!@']rϜ t9PVwwVѣԬhZc`L=11| l0>0CTiء-bESrxQN@1 G!&7Յr9&A;L0 :?CpcVV~d&;8EOSaZve d^lsW>s=m0Un.7ӧJa'vgVsm8ey32xzFrш"9RSU,8ڮ%ѢLZbl{·N!@l|8s(*+oH-W ̓?t;ʁG,3 MJ-!f/,szqۛ{ySE{!7eW ]Y=W=Iǡz[MQ)fG|}ZHbYNP+| aN(̏fyy F6Ty+S2gC{ޠB]}ʘkU)UWSsu-݌D@&֣g%Ɲ&* zL>)$f~2G \$ I̤ǼsͲ{͢Pa9g ŴyIGzO"vB}`q㙺#Z@w_eycJXnP~# hTwgS/y!|ߊ]i[5[c=/K s'73ĕˎ#4 LvĖYcaJ-hFIk oOD]cמRyZ.pkBN4+7S:+7 R%JVLD.?QMY+xw)bN({3]]FK|Kq) f˯Z}S-;J GT2BV,Y0 ;JKi 8dhl,M$QyɑSz1S#Å t$2fZh~^!p>˃!ڣHvSB[HwIx p؍'\c-m|ćS~oz_MCRH\ NptVҒ`Das/Hf^\׎I램#h:B;ȷsw1\T@˖~S%\>`4/񌢱S5e{ì D 4u.x;`D#FA`逓aPx\cFzH6|.D}MLuH%Wz7/aN]>Li.UF&) N"1VaT &,ׇ!0xf5Pna + m r%/SgWT^JDL-[ i} P]?,$ DmJG=5U!4~Z7ʑM4IXW(#kx}rL:^/.[0B2;ة>*Yy=0QZ_gDQd(%QdMw ~brUi'4|_LDgn{/v166VZHuc<SoU0oep +.6ln>12Ŷ/C@Sk[Z^.]= 7 W;cu)0pqԔ++nel*_|4]b/:RqyVDrCi0Κ$<0"YeGI8{Mb'yъU/(ċv ʃb }g BP10 2LXA)phvΓE@6<14/)$m#%Ϛλ1K(񡩌r4P# 7oecMsNw=]bT]nű5E{N/w_,TynMh\D7.O=uy3` X"2Q]iH}PTAt탙P(șaYiV-iBuN?J{r5`˔ l_c6!gDc3.㩌K6G!+lc"+"Y:%]MLMЛ nNXMoVi}ۣ0c֭pw|DjD<'! PHF6@$;? nِ~W7]F{ p3FFdvIUqdHl2v-"ipسҢ3B.4uU*= z+]1yN(|''XMsEQ7#YR7VЦiȱ\M`yMeKvUj; ^F^'gQR?dZ7b$]*.cLM$'~@p~ I^a[vwGD{[7adq ݅㵎X D#|2WpmdoDJFӽh2 6H+oO),`/2=7,o~HUpP^h{cԛ5m/kpҔ #awW?knm,vQ`2*enkfvݎ6@$0 ]ǜ^V ),m䲁5:s2U:yľP}Y=nMR4OfA nj?CkjNh9y“<+y2ݭA ]4/PoQ`e(Dm\Y{H 栱Bpw/8[-*\wgG3ҋ67Tʳ*G\ۭLά|+aEĴT a,ާy`JP0E"׏ #]J|4}V ip}ݖQ^\Mnzw2B_U~\ C^]ePbm{l`u)=g/.P&pH`^T^u]v^l1`3uy]^k]55c>3iT=Xv耖7d} UolÞ7(Ra]DG4?%?lXW㗰~esw(`wtڑTLOOt P>gԮ P:uYƐJ,gʭNko[4z.$OHO PDK Qrm+u_H)]1=;r4lޘߏlohCe*0._?opB WdwX GHs]hcd)w Z0e64]hRɑ3*Xq!Gu 7um+W"5z6`_&Uh׺QMy|($j6۽qqP@]+1 B-kN+ء.#7b߸a͜h:&w }&hÝ8&O՞:p'$ *yO4 wњ-8k{>e5 s$_%ߛɄ`rQkNxqx+BM? D cG()iS{ x^6SBukFAO HJ.JgzZ skduCPC:* /2?obR[F1ԉ1vYf5Qx.J' YƛT8w}cs-yTS[0m+^ټNh]$e?2Pyec"ꪯm׹ppO'HZNE/RêaET վl~fS3DΣ%)I {BuGc>1?0'XHuK@3uJ #`%4ݖɅ!oU5rA~: }GpǞ[ڨ34.BoR%n(!'- ([/bRL\E2PCلE$6Bzk;J9 Modvq(釢yȞad6Ṣ3]-?]7:^w15? ՘37(;ڭDo˹EIu|Jv(I7ҍOJ6 v&&~"qa0u]i>/LIoNJ>2bjѨ6C>H?f[TC9.DO u,?^RێПr F((tJ Bp}akI_͒ߑ.A/ѵŠnq?珱;!\-H04\0! B1i x<.{}^Füo:!8^:tS¢iW -SOɭ@?;`%!<'QPnr #}{ (=ˁv[62O/M78fS^*g 2CF\rj gV ki@,t-nmc=H-s;v%ZU^E""°'[;kD2˳W4uk)ݨy=g+wJ9#q,a"bǑe8 y=dcbGVPm;WB8 LIkR.6qͳ /U4k@3k ~W֨<܋{W.TE P!ʙ4D~(?\AVXrQ DyNv0@JF&T.nL5<x9- cθ9S* \Q6Q5`oBtK$y[o^,MnbeϚKf ƅ?0tp8'X޹=j?ihk3ye;zyNěrv}+ +ʖ:rV`"V6@S35(©oweM8޸^vTl&.ʕyiΥʸrU o(׀T[=Gʭ^R c3]xQ? /jpjJsL1HIO]u1[V@YYo \a\Xڢ.xkxlgew}Ι=unyl@ކ[]0{*fȀ@r{Fl?_d=e-"cWJ&WZV`hM鮗CB8#,ċI2[-Md3`]N ,Nl,[4N@И\:5!83q}ЀmHŅQݒr=Κ*W(t+g?I-a =ܘu$0B{ę"_N}^G% zW77.鿶xUDQ! E^k;whwT:FT .ק ^"WVE[OKL'D'nD ȁcp c@Z,Tb^JWpoU`}늱o*:"[QPQdka]3ҸAeFuˁt= Ui/NzN8 2|W b{ ;+QMz.* I1a^>u C̥fL1wz`H"R]]G 'LK2IGypJ9Cxe޿{~Bwv(bw2@%{ ytd9hlZ5f֌-DI.ҿmw͗gS)ѽhbV1Y~#fҩ}rwB0w a j'4Hcw~7d (V(e͘nkh)zWqͼ Q% \S^-1hoOR*);8p0L ޻)14BkBz6eAp$XR3ᩒ8NKp}M1%>^ׅ3FvFlW:;f;S>i ZN@{@K&VoDjL|W Su31 ~ΗZ$Ϭ򚆑Ikr?3uc8@>KuSack'j]ʞn,ᬖL Fi*9G2|)"Z,l Ÿ_PYzЖm 2L@ϴb<[mNtV':_a2g~d+IuIQkAM8^ )/]zZ-#0nQX OyOJWBϤYv|_ʴt^iZH+2|0L &aDIwo ǏDerzqK4"{Vue3ؒgUTXa {IZFʾcQOؑF;5aCqr#u iY} 8K$>[~7)M;CaW ;5!~H{ wzmvRHR@C rk̮<<2q hLE>QJ"VOmK7„;v=2?+'0wЦ`?n\C$RCN`A '/=q^zZ#>A*קj;OHvq#2isg$0W w)3"=z<-[L+Z߁u6tAְ5,! E!pIXL\X?AoרG=tSK Y6 R^ݝӃC[sR,ش˯5⌭[[Z~7V݁[AwuRY*M9Ka7}ӈ}Җ׮!CX󁾝[R~ő)A6J̔27epgB#bUETIKto = ~6T6ŇrA<]M. ]~nTF^DcW6L,(y%KFF_ķ1D뿮'0tP黏+z8".%XF;& 6@{x;IuҟUv;ay^w$'- 6Kd-kKSͭ_DarvM|"Z*u1.K0Z_PD/Մ J@|d+<6a!/|.;{Ix-i c(b0m+UHmvO v90dL1r@D'q$yrw†R ͆'NwhH,i1@9xh+`!6hmP$Y̟:şvv (;a@?V0r,<6F#S;-scN"CV;9vHk'1I - %,\ˢkJ1Z#riFo 3(E>O z AUKY «QҤ4h1 >>]~ GOe YiWA>R>FCb;/67q5{^nGV߲֮"\"OuK6'MW L9Td{38I1Ľe1Y$nVhר>j"LzQgU.8ݯ6!xmV)8snI#QĴd:a %M]~nPD.]ޜ Pr p*RM R7j&?bLI|mSp@AT@z xe}Ƨ1h~ALFqUsCwi~q1k+e,MObm"ܬj+E!('bߝE4vĐǬ#Eg?0|&Wd]мPVQ u~;hkA#Ő<φK i `qХ|7%@pfl݄HE=׵ɹ'=k;H"+u1aYMZ"Xƒ{SXeҏnk*W=Ip'IbrHM= ԛ7KF)Vyr*ǽ1[XD P Xs&eZ GY&)3wxjb讓l6i$[ ; O$%s\ط`,pv! gtoKOo`G6W_BO6~Vh5Ɲwt'+ +Yx[6睼4?8}ȮGû4TS#AkQr[,Ovx">d$N3"W9@X ep;+GH0`f͟-"sDo˅yk5H yiBeRU\npkƅfH;mhE:χ҈!SM 'D: )9H:wceEԸO$4 Q>:Z>F9Wѡi{wO~o4g`w2:VًAS (^.r+Dhm|ܠg ;af=,sD M,#_Fuil;jVE3%$'<<dz%iApK/I>(b EZfM>4GF <|p^ SލD.Uc݋eoFQ.~,s1ڍևBG׋TzVuIpM,H{+a2)0gB7X­,%N 2 %GڍWi@>G~l89R[) #쩷8{\#H 11a l?50%AJ4HGPqxJLDɊ'eHGS:H$qR\RCړ4ёhiG%L.; Z}H Dʡ0`ERȠTLuKu2RfrvQxZ"-'$%mFp[, htC|I0`pm207gW)(~ :<;yx@CeOL* FQIu2"$2۟Es >_|'hf] hNYcS)gЏ_`k Urhvf / BZOqxa'bLjW`{9q];mT; ڒ@LJ{Eo#-n$K`Xwa=M\ZXc{PẾyS,@v2qC:_쇶3</tAI/!<+G5|w/Cb8N\]ntv$wo[Q=[X60ɎPCqSBm#A> -}ttug?]u@ce E{EI~̩7a[{ўU{GFh2;cRo9xD5-R_LD1%/ɱ»#21BU]y@ 3\PH >-.)q-a)0 =L1ڛh C_EC 'p}]̪N?Z- D~exDcrt]WZJQ&PU%Ɉ' Ject9k/JٜGmPǘ^]Sh١@YG,m9A k⌇}+]أ20l Ga9kVf 8*c 7acfR-knBpӠQi왲xھ0Tgw7uGm/{&R7 K>A3ei36@givXZDe}z.Ka[Hfپ0%t 0)re.6C/#n& .1X̠b`tΞ]7BfʬiĈ_؃Αm17NXf ~u!Иm޺g>S:F?"F [zR׊”>V=bC\ \a@ǂyL[Ċ[]5LD 抿n*@҄HI7\5,PxT6} WbN&kN; 4 N?\ZMG"ŗGo{ⷋOeo(~07A?E7.cQIѥ mE3?{Y ^7Ҙ?2(H4f$E++Q=Wt^@]T0sSg9ohzAjOHM_}4) WBMzخE0eBe1kR>s:$\+l^6"߄oiI598`j<ϑ7;[S]ZI̔_;M>Jb~NCҢt<1v95.IGiG]2׹iY*2MY(ڕ)׍S.F!7!悩-_S 7rt$ wUYZa:G{M8zres d;35 @0D-4(ۍ"1truŢ;*5xxRu?y)dœ. A]veN"c& YeCE~I-]UX8lAn Ň5 9!*y2ҧ-7l}VS8,Nk:2zZkߐe`3Y`5@ R‹XYćߧ؁f/ز(CFEkB:: iMRY;/}3mx3@א/!JhDfZҩ鑙p Z4 U`yޣUɿy-{RbG]Q=N#=XcrK-mtPgg}wB!33oE>+b"$@{o!+vU|,͞,w0"lVd[t_x/nDWV-l6).B+T lkv %>^%nT. G{r!t?}܁Ux`# pLwdO5>:df8\6.bp>p 99pא}ZuSnҔ1a)L]^" -->Tvygᖌa$<Ӈ"@/w9bU@G^?mFl~@6`)_Le:\Vl#|5)Ӡ.+wB7fVNqIO]wv5*{I=^9Fm&#=uKւp¦q2Չ,ݘѵmLmHE[<8ob mdދ?z*86T|F=T= , Je 1a S8ȣx`CK >t UW<Þ5'; ֨f9SUV==F߽2;hyo/rFeLӡ1#[C3IE4z?,1 bATfF Q^[k ^ Lc[7NHրtY ໐.^1[)?gR[wsǬSC)%7U#s2Yq0?`Em¨טr& hk$Mbhqa|(*%2A3봃K\%z;yewѐx {4y1pRO%^m(ڏ'b.H7 J2g A_E]UaWԬ9W+xIJ$W+;Nkc,P ED pT?I[Xywj*{Si`+dٰ2dGD @(rN \L=kд?B_uE܇.;?uTDmzB zǒ38fː+VBx}hhV# \Ljp:V ֻ#aZ o7\xw?T=[>Otf>o L{)E)&/t6_-ہ'׫JjIx˖$1eK"G51UOZ ]}#sRKmb\\n;~CBO)TarS b/_ցG8s+&. u>'=HEL!Od 6fbXH[)XF@!̋ L{8CmpJ& 'ߙ@mCŇqqMrN$/pC+`ZpAC%n:IAI䋎Rntᓙ)I9J#k #Xnu .fK &*tf͗-WFn'6ńssq ((ݯ'5-s/_T/ `,Fƒe@>aA //?# 01QNZU@ד,φV0RDK苀 BFWXYǓ[hzfw|Z5\kLYo}2փ;n {Y6hբA*mbi.-;c؉bkooK^qo,&Gs W}E\$ Dy4P\ti*5KtˏV[+rn`B`$$ _ IHZ$ h*FHEr ,3:x%M2i3#a ƝB]<фɁ߭ (]ϐ8=X!l{t{-Z4u nfKkx1HH}~4_,kaV) ؏]+":͸.{b9%6:PfŴGKsAXnSyk7ck ˅E7[< cI% e7XUwEFUJyRT\E)^s?uS<-l)gBG:8yp6v}_caGl3}rS -!^z +,vf@JJF.;/_ +Spc! j0,Az(~=(g0 O*d 1 KWGr@e.nw푙W;ڬzxuـRaC)D>M/qh~ȋvh('=#>ʧjls7ޅ& >>*ˉ+3̢1d=YG lUw*RJq[L _G;Z^SwG>)+a%1[b]{s°ClcJgT<1 v\mv Q첔0~{p"@Y5iw(u9~3qMh hCA(;>8K"sSƾLZdxW[%sU$;!C;Xs_LVY&1c{7r[>$ )C_`HeOSZe:|Pޔ|dKn|T &j]yh=ʺcl'J^UJ0ΚKZlc KN2IcF7e_[VG |V !X>aU씻K_g ) cj΍g }31u1s|89!qv(;!j&IɆ4f|lx/Z_bz/]D@Gƽ+ҥGo;Sm4~P1LwB&1Uxص^GY31L^!`d.tHmmN\7yC؃`epOIXvMyWheђO3$b:>)i4+Z} oS-OD6,wQ)4!Y?(C/5"AD, R;XJ mt~zc} f=:fXDB^ =Y5*;[g}E\M5x 2P/ VޣuJQ\RP8U_mEMAƼ LU Wꑝ'@!t TLt'7A$5hY&RU<:Q>@&H&Oߋh>\.aTa$@g1*i-`ztuQcGMhupkrd٥qR Ό_YJ9oٺs/(8ɹuRZ|XLum\OɂU'UBsr }C똠*1rk6.nҴp[48 ~N Xqy|@Jr@|>.:r"F:]UbyD (q6DQw\l3X+„#y5,vK +*="˾?,ӷ!v/ݬϰE*vJ߮w)ћ61_'A>ŗW*V90b26"HmnGD} W 1Tȴt=,mFnwcƹܨ *3) |twhDDNR*Q]+g&>;o= +1+Kxvl=>J{v&/E>WԘ6kSu_dѓ2N\ĺ>g^PbHo{JR,?!nv׳hJ{h/"טg {( ,K/;h{b1j輔;Ix.pqqyU Op~@ 8/7C?|=BW$Svu [+@vf5uNdx9 /)WV{^=T bj<? c1T*[ݸ,%Qw֜(DA(b@ǔjx=]9 C HSWB3qkyG6ފeRz2/.텢0ᡆ k\GIxX&xWGvS]F=uQ/rNρ/CW03\[8VGv-tRp[i`@@A%$9J$̓5F":Q~O+m!r!qC{'4-*4[ hkFWtfpNPZzh` 04>kr&-ѠZ*P JR. _GH&+] HeR(.02?:1ITE FU44, V/]APЌa(Gv D4La1 nL`3c|A+ɼ1xdQNGS9{2ox[_oIdeDuoAn֨81|ɳ+7~h$*%].ʁPސ\4hax7<3/BH$ND*FeXgA D<+#n;֮[ R+Ӕ&]#*Kv ^z\G(ö s]jHػ^l] ȎcG"}1Ns`skGԋ*Ğ^M /b:IKOd9HR''fLJ }Dъ>[$)KEXIFExifII*V^(ifHH02100100qdmr-0.12.3/doc/fig/gd77.jpg000066400000000000000000000675511501654372000153450ustar00rootroot00000000000000JFIFHHPhotoshop 3.08BIM%8BIM: printOutputPstSboolInteenumInteImg printSixteenBitbool printerNameTEXTprintProofSetupObjch!h7n proofSetupBltnenum builtinProof proofCMYK8BIM;-printOutputOptionsCptnboolClbrboolRgsMboolCrnCboolCntCboolLblsboolNgtvboolEmlDboolIntrboolBckgObjcRGBCRd doub@oGrn doub@oBl doub@oBrdTUntF#RltBld UntF#RltRsltUntF#Pxl@R vectorDataboolPgPsenumPgPsPgPCLeftUntF#RltTop UntF#RltScl UntF#Prc@YcropWhenPrintingboolcropRectBottomlong cropRectLeftlong cropRectRightlong cropRectToplong8BIMHH8BIM&?8BIM Z8BIM8BIM 8BIM' 8BIMH/fflff/ff2Z5-8BIMp8BIM8BIM8BIM08BIM-8BIM@@8BIM8BIM?XXg*h-2XXnullboundsObjcRct1Top longLeftlongBtomlongXRghtlongXslicesVlLsObjcslicesliceIDlonggroupIDlongoriginenum ESliceOrigin autoGeneratedTypeenum ESliceTypeImg boundsObjcRct1Top longLeftlongBtomlongXRghtlongXurlTEXTnullTEXTMsgeTEXTaltTagTEXTcellTextIsHTMLboolcellTextTEXT horzAlignenumESliceHorzAligndefault vertAlignenumESliceVertAligndefault bgColorTypeenumESliceBGColorTypeNone topOutsetlong leftOutsetlong bottomOutsetlong rightOutsetlong8BIM( ?8BIM8BIM ,  Adobe_CMAdobed            " ?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?TI%)$IJI$RI$I%)ywɸYF?Oh[eЮ+~?2*m5U;Ǥ׺ ]U:^ezsʆq+kW)I$JRI$I$TI%)$IJI$R$S$*)IVx֒_GkkuTa^7ԒRCwh2e{/#%RI$I%)$IOTI%)$IJI$Rx,?hӷVʭ}[>^uKki{FuGe;/"Q7}cuTsj+/RKd>q,7s zՃ^ׂcoדef1u/H$hoOnU]zlkp=A֚4N@_uX,oN;eHkD{ [Y1j{rJPh{.Zޠl9cw̷uN> ~#.GHe:B1;@pJ̜6wƀ9h|`kc\pgs\}H˾t,{y總=˝CI$$I)I$JTI%)$IJI$SGJ2#Gon&ݧKpK,h`>d˞{GԪlLe:١,&бx2qy<k~KĻ$JRI$I$TI%)$IJI$S'}Xh=˿v]gu^*e'6h-p}W* k$cnzJ }+%Pk cg6۴\]5슚&6/ۨ: Z1$CoS -/Dnhݞw dd`?[.\x.WdًexQÚ ӳڟ,#K\:mxf@chZ3k n3vzgOt~>-mѸ7 īmLr?7ʲ$I)I$JRI$TI%)$IJI$S[Cƛ?\  ccGO^vtFݿRKr182AiYMfVslcK[Ջ}f=Mp+*.ݷ}U>o/Q\Zv<4liF72$L i(C___s=u_+4TA>Oݟ=$J/V۩}茀X=M6Y褤fN-.2ϳ;~͵]}Of]nGJG=;+YcSNޟ{:tv[j 3ж~OnV)?mQm`.-Cwlml ]_ #.{.z4>H'^I$$I)I$JTI%)$IJI$R`Ա3wIx.$Շ; ~g4BJ|`.9aEvݮ&^ޗpTɑҭQs :Uuk;c{ %%<_繁cUz(8m5mֹѽ6-𞆩U-xߢI$$I)I$J8BIM!]Adobe PhotoshopAdobe Photoshop CC 20158BIMC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222XX"S !1AQa"q2#BR$345Dcrs%CSTbt&6dU,1!2AQa"q#34C ?<Ϥ T7ni}='7&wL7]`SOz #. "-Wy{3t6iY[JhM|gٔS)$*g]缴&69/;ZJm{>^'upTn<yyHGhQy_>cs^x,=M-_MEUS5 ׆p:H/\o. Up?y(Е۫[z̢qe<۠ ЏCd6u_ntN3kaS&; L* d &NOɁgA{_ey&mVƆ~5z87p罖yEglx]K`A<l)eLEv@9z\KP* ڠFAJ%BPPPˋ*/sK~OEg r<_zK4՞h{I"?෼jwB48JPa8}2n;gRQ|OP9ʭ*^ԣ}lN}/_7_>F B"% (JEYWC@גLGCqԾmIg4yfOOi/o_>&OMG>f9?GT?L=K9~Nw/Lw_r(` eJ0 DPQ TaWJEۇ\u?KnwE7;R|c/?+ӠN#xeoNKՍu7݌zi5ӆVUK/DOt@_>  2h Gh^%^;N4p5(jkF_FJL2[7rYyI[pmSM~[>{P߉KzYZYR\^l\:+qVo4]y$nկJRMђZ'IiPX[Ͻհjoisk^эj=F<5lՖFZJ#/E%N/>'#Xӌ&OXpC7т(g?%rbR+2e7."IW#Ugg #Qi~uE>ں^P -do-1Kl.aZ>#V[o'Um=#?ė΅lo ҳ~Ek.-av"q8rg̜rۊvw]ZԄ#*75%;7y¸} :ң;-sĴlNg )δ,nRdb֢i.f: rV ]M7x5bNtܥIa6jr:eMe%ߑ4vYNO%*qV2 ՝-ISR(|2[KA?c<*<F}KuxwSA4橶׭8ګoJUUkw9;Î*ўLurKhayy{updY=|< 90YZޜNux(.\(vcJVO*}τ[՜%8waN]ӆ6ŬSO7̣Q*d!%sRSYg8K+kgR8i{IŭiIhui}V;o/YiOX:r96"|n뱼jJʕ:pq4}|N4*ˆsZˈu:#%A+Q.*W4qjr[~?pumh֨U۟>D5y_]O˖Q+GUEr1^olgAF<:yj'C[Ps^%Ai_[Εwt#O8jXV4{UiN~XG:&';J{.=*1,WpIgmOVfǛ^|~څju!84X="6Ύ,xri:RjN/scOק~kg-Ynu [=Whoi+JiӜNm,.kijש h}?>F:^4:M$p`^TJS ok/indgcta^L_{g1[˥Ʈ޻eԾ ZǴ~Ӭ}"a8Ӵ9Jsp[;iopFt3GioBYTBnIez.ǫcm>Mf+U!7 -wByn[bY< ^3wR[Qo㳝nG6ؗ', nq[?>;NvJj6$G0>f\oJΣWt(<,Uo:bVWxN;Gó䜜95z ]OM-_Kȶ~Һ9Z6ϵE{ܷ'W}m+XL%ko#O+uNmݑ{q&ZU:nbڔo=wDnN2zXx~jgKԛku@{Wd=g+O?,erdb[Ipg*S=wC 9[cMZl%)9O)|wQM9$Uw՗ceu^7-!QRihU&Iϑ[}b]qmU{c"i,vz2*>'gsqI';xys<-GR2qmDŽ]q~ҭw}J3X_,m#vZN w{,ոsiS9e&JM햗)VCsK8K w̃Sv5%koRTq暚z~װ*ljvRZr96Ii8MtݝãR^դmKɗ'/-OqIq͝OnR[Eu{ь$T[=T~àP'=\.ѓ3bόM_mo է VЗkM1ۢG2V[>ªփzսR};Ԇe-)6skoAiN/($gVRffE;xMih-b9rRQz[i|}BTNmJoߟ4t(*|^-B)8{+O}BIT([>iF4niVw9%KR:/3øԜU%oIqU5=߶B˷eBv0jԓpVIsXa8Zvv.ڪٛ^Y[듟nD5s&^Kf2^uNֿ^QQVtbUWKjsʢtx6i-uԲ(9M툽xqzN+կg~jW,}a:UQ%,[r6<o 7hө4[O)L:N:{|JZyqۏ:Kߣ%8te)ӓJЌ(e'^'=R|Biמ?\$TxM\\ƔeQ_^'rmW-6t3' xE$׎_3O5.m=EX޽g )9fՓe6y}>"U&iok^sieW&Wp3(eS4Xryg|g+?^7Yie4}.) //]6}ZZv޵ZaasN51IR(/}<3kmwk9~5qKI,8o=^+OV0,cI'~k7*֙%yg8_Ztn/K9fYRR|9շQIQ4Ia -F{c -ziQ^K<!).o!._(SiFSyxW͇kVvxsi;MԷJ-'̓E?/.y\p-9dRߏRsiIi5sR(y{_҃xX]ٔXw:0c~Yx@<=442wO l:6\VwKN6n1KnaNe.x/9t YKM)l.Z.Xo+Vz=ô=QR[7Pn=ۍ=[68徽sC Jsf󩽼wU{[w[[mms{#M^ke)rq*-MFQ^;<=iVa)<4FT_<lz\GroVvXXWeOL<IȈR-:[[y~ldII=8^>k8<⹟-*e=Oߐ= ,JRRxϼJQxu$jxƦܷ݁Ju4O)E&\!ofmyZԲ=no9;E4m0]$(I{3λ|YGwEiԊzmu9BKzya*Q[_FhTXsO9O o+M8ܗFJ:RowEԤX=gm.^g[!/rk&s_i}œX}vFԳFKޙIӂEszN:] 'Dq줱`jJVuk/-CM\êԓiglUBQii-;6D/g+R~#81J>Ό&[eIດtBN6pTs8,^^c]N0RnXO? N cK,^W,8eTM5O>)C95SvjCL^E&sa4:44vzq<:-vیroFX/u-$5QI|4mB.I2dF*KuHoN^8xXzĦxvv9t#sګF)gtxdQ'($" KucmݕoѳХSxOr2_<%2ʢˇMETyZ($Eğ\ ԌUi'%S-nY$)&ܵ~S.Q|x1֣FI:rڒY[-׮Lsk;>kN6ӏx(˧]cZufIK)yxn+4:ŷy'~4wkirIMRK |X&ǸG',)ɤ<_ЫOM1ҝix@y?Hc[7ڞ4?4s/,}{;bg5ԥ-88[w-9G/Rc+`on'gQ)c+ي>K?g^Y hrzzu.k.ue5Mc++>Y㹳KKO^;|4hcړwV8o~~җrur</cdoT$2oRN{8aҒ{r E9cVeFJ)̰eF e&0bE,rP>`RRNIwo|F$C-Q+l2'&ݦ5GK<kL$pma>7U.ws-<5?e'M%^vdޥ%oLd&Yh]ϴTsm{Srym=}y%4:\6FӮ:cce.|K/$J?SXFIieŶol律Tw2om%=|:6UWU$e/j]0%Uͷ<'k,NNiߘW &zI++A(\e5Q'HYrÑo~S\6N,-@.FPધYϟ.gm .OV0Zl.Sb".\P׭7$|qb,!kOV RkQq__o9KSmelJQYrТ8ʴ]$p|Bdw0!+S/;j[YRMZ'Xr%1ٮmSUb𳜬<ZbZZ2ZAE'VOnlIG+.zZZ~;#IT_-Ɲ8]K< 0o`tai:k/'o V%OO|\~˹ՋצI% aOՕX=qv~(\,G$l|g*M&3ߖ1S{=U QR-v-ΥMiG.(o ;<F{_U1Cp+!q,B>j~"7'ݽ)!.bAcs,XG$],J[sK+_HxU QOu9$VqZÖv45g<^ *iZ1%ewےqm9>B[|m,U]M4=nO@]E% #_ʩJQRU5a/l)hҜzW,lģ)Iѕ-[g sR4nN94e6 eNsZ2ҳ|m<*Qb&&KV~ךּvN=Z<u,-\W-1UҜӏ>:4QrfŪIǟ^@@QUr1><jԪ4<=IG-IxcY xA'RKmt cs:qjoB*U"-9mcrskGK?YB-1YՌ`!N71*%4U5VKtN Xzs*<4O ۜ[ܪymٺpʆ":Sn9ɷ:{,5/iuOVQ^NU1dcTRPN}NF(Jr.Fp]T1(T6>|ovr}.Zc)u:ס'Ƶsq͜kJm:swk^Gq '8\vm4]3j-cGǙMˣĴ,b-? OqI4̱<} \=yy{M+/8lͯ~EGRYk$eQ2}*_O+հS2NRcnkK[G)IJy)qǮ B,>ul$:wmovd[tr8-J;e} ҅PN+Vr.4LHJ[.~Gm+jp6;z7cSy_PT Z*P\ehңB:TJRX^8G J<"ϛ8oŮhKyAo׻ܾv}|mQYϳ-}miԞVy.^YO8yMB9I-&K;i8iģS~+oKѫBw:\s1%o~H81ެ6 %ҎRX(~"V_(R0 =c-xe, lzB2(m-2Qy;cݎͺqPE?QѡM~^[Z_RVcJT&s$[FFZ](7O N2#^;ge(U;$5kk9f/d"J-!NC9Vo_q_[Pz_[4}#unjTAӃ~T0>t:zcͽ5gJQKyXY^KXo;}+UK?>w:=2^SԾX-?ѫƿQGJ7O/YN,9ŵ$|=Ffv[jq{_;^ Q,?r2ҪV{6.OgBWӴ p=-=qߧ#p۪Իlo߬J*N8yym0^Im N QEMjkFZx5m:rǎLMry~-q'^IYO ; Sik8mҥ+$)#i7ZaIoz1^LɟyrJ^>x[Km[3\OM$He<[[Ś]*[[zR/ěti$/wh,eirifpK=^笶)҅5R2m'WTo|¡J5 eJ}iI$9ݖӧoJNN*:r9uEԫ:VdTeRgMnUw9'>Zp3,*SJ0Uu<0;h¤h-~̣jDi75DqIH<׵.l;J.NQޥc^F9)B\qKNFU銗Q&ծJsn5l$1ymQVSiRTTKq,bߑbSXERqѥR`%RQj6X.x[4ε|}dh^Թ*U7vSNZu%Oi &IWJi)N['4v5TīNXv|{Ks9r,**R[2kv\?w4˹<2rƮxKmx(:F I,Ԗxml|&>[>ytJoBO U\'\K3KОs/4v 8v|͑,kDE^Sl'.h*/-gKrE_TOU- P2z s2<6/5>M'ԗj*q4||+PuVP<>f_5>gsayd𸸧87WOkB̧T-&J7x>~]4.3Aң}US`|c(Ƶ=k4s̓o[M&~![QOüS~,7rqY~➤*z%վk5ğ;5>o~X\o-O=ÿ/x[ߏ? q*%VW=_T_+}_w4}_~!WFT5+g=ZFHƝUdR[|~_sLUk]INYTXNOlO/uK~ *//5^:k>-VO->Zۿ+xN eRߪO= b9 ɩZqo-؎J %Sma5IbsOgexOdic. Fjigw_}Ď oj}'(l mU"*9OO-LWJdf=[1N.qR^ʖRWINQ?UFcmHw~ԒςoB^գ.%Z8uWurg}ʅ[Q^A:I`|{hF껍Xʞ-+泯&Ոm}g_vBJsH걥%\ϩ4cgucjӔ&$$Xi]An Jcem&kՕOWj y,!/W^諥xn:)$WSU%-;xd6ڣcO$p/:rqeϣ_ޭH4\3[r8x ^-4*/#cqǝ/2rŨeo,1$2ʵc+b?O?Z랄7?yżSdva -\*xџdkriK`|q뵱Q6|GkDxfXwkQiC? (c&jeI>r٘mӟjiKO6"FN2RM56b;N?(p/O/qsYy1_xq.9}aJ5K.ʎ3ϗ>5Ԅlh^ӡNI58Y5džxa5Ǐ&-QKSqG|UQ$Kı[,f{ 76FqO9Gӥp␄!I%/dvW^m ח5#:RJ*9DmQ!:Vcld]%䓍4/yuӒM`Ni)NY-I:w扱q0SwR4ݚmc>Yy64(Fښ9>f/u<8va+;mov@?J9DC::ֹ5$y2nY{oWFTT^os0G)#LiSzJ˭ˊƅ7qoB19imFgi9KdR|&O>xǹYh%x_gk k'Xo{X1Z8]퍹_,+UIUl#o=xϫY֟5c'|M\&t~zA Oj`V-}~֞3p)./_:U*”yIy4i+ Sj9M.G⽬vU[zZ'RJ)}ǔ?+dA}2Jl*Dӽ3<ɗSo|]g5?g|~N=#Pغ/k)+(P){Uyn7^WX~~D*:s|SF4e,lpFNN)W!vMY9]#mlEVsSK?Y'bg')-sKڦj8-;>5csU+sK8JI҅N#Ni4RHu"O2Ѳ9ۜ\^S=|VsV1~_#s')XReZ**R9GR^do)oܬ|znϒ.ݤkVyr]ؐ]K@Cjpe8y-̴)SK򥜶f _!]эZ3s--.l%RMj>Hd-7|߮3FĚ%X~qx*KrAj~tx@hɄ>+{|E2,ee 7xl)5y r{yw)&rYOOb:wRT/e:TF`a<$GW7-kލY\U*(VfJ-)=/)'vy{Iv̷rAھ+ox͵(() %xX|/>JV Wg2QŹcf{So[<-}읕n%%^MqYSRQZc9m̵q*]=y5\crY1ƍW^UjG JIO/QW;k3Nx: O4T%p组d%g;K})SAwt3og̥tiG;/8-%*u!WܪCB){ԶIK cf_X\;hKxqd(JrJӄ0'Ie>,*9z%O=Hp^?q*#~3_dXP!q_',A|A]Or]}Ļʯ?Ȳg a%\Jw5 ),Ge0R.+f^,_^cN%(._&E_O\qQ|Nǻ3gO63v5emV֧RqqQA^*W*WPT#9ӪY^VR7hRZwR֩qi(U='k8;kk\V[]7F_x ͔|2LgV*ѣaF|2WU*^[V}5屣e- [(MP8 EEfY|ғKmrq+H;z;ON˙E h[R^o!$N:I-J1MCV~ߝ@6Vi:mG(sOz6:?axmחvjН)NJw\2^K~FҵPqp[ur_S`Jvם˖8eg;hӜqi_TQԜ[,jwq4mE^XIԅ:O Vj5Tf"d*~fDZq[pj5cS82ӚgHճm˧hB(cJM09'e_25j1*Mq=tm6Y֩`8;J2XM7録CmTzզלpcNS*SG ;Ԫ[TRZQ\ZPaN*3UWZ̉&),-3sڞ>K9O{Ilo^p?~#ZƤcVtI<>Eۜ>T^s5B44x= 4 ][J֔iʅ#'n}!v+WVZXt a(e#5\{Tbb+^HNYqv߁Yp;:έHU]^ zvz߁[µ. iTųQKSSxı={Y7>uMlF*vtf5Z^@un x4;v"$_-RoZV4oBIR&;'q\货A׳ٮvg+k8frޭi})?ޝ;z1QN*葘-b5wmoZz4l.\@xn'y)N[E_.Gk8kQGYiq^p%>/Bڤ+* o/qnԭ o7) /6} EE|v+B͉;~j@p~wx~j@p.+˰Qkw?WC×$c_E+GģcӉRTG\;A*mgjo㺏4u&ٹNKY-p+w+RPRR\RK[*YW{l `` p0W[.LL ` ` `` p0WSgm)T.0Z38H5( `0``&Zqdmr-0.12.3/doc/fig/md2017.jpg000066400000000000000000000666521501654372000155100ustar00rootroot00000000000000JFIFC   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" J47M±(42g @ѥzGW4LNmj4dJ7=0湘Y˭l(Laf`zuҷ^niy;=J!=ٕX~A$dĦ Z]n 3rUY91l[L92$yvemeѷ ?qWtMs@ӂ|ϳ/de-WJ;ϬҀMܵC^ŔԀ1m+Hd3j;>+2Lz,ڷl6|@:HlzezbFf"dv?i鿇~3Ϣ̾[; Ҥ<:bYɰx]Iݳ@xkWpҬkr}-J6{p %py+½R*߅,]P3@ӭdQj|d|ؽ2Z*Rz)w,y1q%"){*:5Mk 6Α{ߏŒA46|=.M3%4rqd]5S\{vCfNlh5_竟-EQubxdӏBӏ5&W7sJbboخZ'F 9ޥ9 E%ԕZH%(#Ϩ@G$-xEةHZ 4|ǯ[:8h7"Xֽټ5AM};A Ͳ+JL 좘8|Wb㽈ڄ}SxktGer1u+M4l^ϥ^x{Jt4TlqO{+qmbr|{5j:K޶L'=e &(𓳃ᑓ4X)ems~z=tq^i l'^~R2v|}/G}HhQVQRR4ӳœZ;8 }dǍ#r'WQn yw *k-^ēv8؄ֶ]4|LHhz窟)sԭ-~[߅ZתtaIqtܿb^"d{еT0kҨH[ƋuGz~'P_䧑w+ba;8duU^WRx$l{yF+Rukr#gĮ6/\bb;U㝏uQn%ʴH͋$")2֍V20.jbb;U}~=vyz)NmF%$GU_])2q"y5n5ٸU *=f~ ,^Ů̍D͐5TV>n`Rf`y518{~ci] J vK#f6R6k:*n>@:ѸϵNI]|q^~uڎAbq}8=}ci4̀31!235`"#4 BCAD$%@մ7z"u/<}鞨V: ~{R΁"~7'9%) P]w;Oh;>wz!.rhvޟήvޟήvޟήw;ޯwE |}uwE |%5{E t|ДqqN-^Ak&do83[3w& ,Yۜ:9L t+a ;QE)VPF u:{cFvSKz,#ڱcq([cxlg3331o+:l.kuJ0`JHJss7Anu݁4h(JstkM]o Ơ # Q[O0zxѭ6k-WWF0o%QUщWE\pUW%(ЄZݮ]+5@J1#b1STL4g@@]m oѮu oI  QcV-nk[UD[>ь+B9 nt7AnؠqHB^ Zݞ䶢ۭьUn * 6pzxэAv{_J5;]wG(G>A| ZRgv{__agB1|4ۈT#1S=敤}v~cy-о9&o C}C|H&^r^4]`dҶ]5AC1C1C1CC1C="&ssX%HBYlӐLvKM4vTv3eP-Ģm>KyZJ7 %=oSS}wzqSZCN?k9iy- 蹬OeRU `H˳U䷡m 9OzFe{[]K *0]WA8$ RmiP}T6BHs,k%;#}<Έ*sTs u-fAegC#JQ:s--aACmIQ0 TCOD*1(b08^N Rg3킡bff7J446@BFbFjH$3315,9{ #) n~"P'1ӄ y A!^> 7$ydݞGBr3l=ʤ=ӈdlaQXGd*ADXhz5Oی+fclblb@F l$I 61C43I2b j,傅rخ..+嫀EM>#3gRFTY=/m 47:#B"R 0vQq_fD1@8^Δ!!iThJ#t=8֚tF0~^J,cI:a0 ޅ:#3Břrě5LG$@o,r0A9,FjFrAaDrNYhVD\J'4{݁h9vH$4gY?/m "4`HhoaMRZZRH)HkeQ85fTdFbidV=҈ ´8f^J%;°NK9}CM;X̬3Wvovo5&δC@h|Sg $0u)5a_7ƯWjpux$״=cUI,nX%zApVS񽭧Im2|ZUs6XU7L]R䬍UWszWȭ}|1ܫ0~Yj] 2SiRtltђ­m.]'k*˨[^c:1T(ӤՌ,ac V0c zhX?!²$ܴG8l{ptŤtZ%VgUd5JJJZS+ ڢr{ZMT'R+9PDxN| !ԭu6P3Q9P6# yP~Ix[Ds xϬ7vqp68պwofkGvxd+pghʟ}b#qhGT^:Q2T[$7=%:#21m|W';;"?y rBg(!@Jm(iq>/Wa(9&{fejGyE豎豎}/:)a[wУq?0dp:9r ;?栢g+S Q8vs^^^^^VPknADRC !Eɉʒw3Q9}~a?*4zzJJonZf.'PI"WK揊EEtXEtXE/歼.j'*Jon$/(?0œjK}%77lڶC ? lfsW=\^^^W9[gA๧#ݫ/?ڇ^+]~ݫ/)?5aaXV V`+XJ%a)ܼio3d}ӹ}*qWqWuqWq< 3nERw/\6! <9@ ^XNv[$TmymX5XU{G6Ʉl|MsZ dGJnj#1#ASCsMDPKg;kM%˶W^:+E+:)hR`kFbVzz/OE3AS1$on;ڈA BtYD:lWmd)ݙ$_jJ@sS|-wmhqMJV؞]y2M; QsCoSD .xC{brT= .0ORTHY IAhnH7IMA{VREl( HRK\֓3TKj`{j6jʈdql7Uqa=VzUXOUXOUi<"^Sa3Ui~rW!DD2PvtC=~ #I@]](} UfY0/n qve ^lL$:QqsUY[4K32>0LRxh7F*^&拽MVSVQH D'=eiښaRZT6jek<ވBsH8a*O 8G^8[7Jį4O9Jį8|Jį<|Jį8O8|J󿰫(<4@mC*;Qu->5{IUv~N"»~Ey0VOÍ;-JBwQ/ .9l$7țxL1\:c?EzqLW eq3t|\M+1 Y#43~~f4\Hl[/+47q 0Nb0 fJ W.ж${xn>FE1kQlE`i$,Ziw] ՂKzl?Ƙ 3`R~bJne?RsJo+<b_fF˓agVQLЕ ~↯bz| ,2}zJmF$9fhl،ơ,Řr2[xRh>+$<'kpbDLb!sYC2t'LI!V3*i~LEʑ$Vpj!̍Д,jn;~fYCn|,{']Ѹ6 ,?\`ij U>2(Cq4#H4$f $`:@݂SGr0.kvsn#[}dZ2?řkzz*g|*i^"B*;2&ڐB5O Fœ 6?sQtU.*STa>EHS*]x䋬e$b$ef,OTğ{hVG>AV5]^=&T$p@l3I)vVdYiqGxOYHqm&6VsFWcn}˖PXin 9/Z +SgdMfz#[0+25.X9f;O-o>M<>؏_=bF, MQ!E*BZIͨPIbpNnlES Q]TaeU9ƤDa"&⒞4ܲI8p7$1Alg`kx@Jiֻ,:Ozx;M hoQj!XZo7|'nYj[[G؎(%T`kFTȚdV'|el TkPcU5IJVBCƖ((F;vVc:6ET/6x{9jP]|+̞"zq,j18eAP80IE$Ed]Q2r q:Rg!81%͎e'(ple s=q㲔kA#Dn]AZLEwB̸z,!qڪo3Ԫ5(cGhzY++Y3ɇ7NC]Oѵ(ƪI:f*Q4v*r-K<#}s[˔r.ܣxE>Q`Tp0(ojCR2KR_jjTCB^~MWn#pHrBX#y=(Z= yl5Зͻ6YrQƶ&WK3U4lPWHKՠТujH[ `eec;w=e]JuSBbg+=6:l2FݤZD@Ec-~fqr-Bi"eޛ#<(goO`ł}B\“SjD5j HʱCb=clY>38 aӊǒdZLU>IJnI'3$҈tj +.a.V[إd+#j@Y5uz/f{,5,NͪP^LZDPU% AVUNc_-wd-S*֢Z>>vzg6(%;% 47a[>N-YxsޠXH&ccʌ+5{,f1V̙h6bsHT((I>-*S cLR2R/\[4* 8'i"[c' Za={Ǵc+ofPԓH| Vqav󋢭U *<2&@8pQ]vUޟWGԿ!#C |JZ"J[sUeEP 9eǵ$;=kz{o{ѣ}ZR殤? +ꂽ DH&MVP"<ìH%ЦHj"ɍ!h@3fv?[=6{~G\j>"$j)sتxH{\)BdiSiNusqHp0yh:|) 30r]g=wo)HiܯVșYLva@plˑa\J͢H~ [-5={/[<&mP;{QQTjyqLwMdޘYMN(I،N<;E]ԉ)]+^*q> Շ#9,RB*hZyl)<<<<<<<<<<<<<7F<<<<<<<<<<<<򺙢X̡J<<<<<<<<<<<<_p<<<<<<<<<<<<<)>JSA<<<<<<<<<<<<<}vѳ<<<<<<<<<<<<<<<<<<<<<<<<<  :?<<<<<<<<<<<<<L0/<<<<<<<<<<<<<<<<<<<<<<<<<<K Ԉ- \ )}x;FRAq0C5!"l"W!tqfru  $euA6z3.C~:m8pSԽ*gp+SEm>>pW EQ}McEfV4%A{{F\#iΞ{iEy{n[^}m(!AӋun0o~hyXn0qv܂;C^ǼmZI]G#KoAK U C,JNr/L΍ⓞ6X|KY,M-QxcLzÎ=u0V)L,c@CYמ\h)h4‹ܡ Yx6zwgbn٪)谏/Hc.YHSJy2Ƃ:>y ˚rp70@O^Щ>]%al\Uq{vq;m;SqEh4<{˥L@ #.EiRͮ2 d"mIsp0<EٝleM)ZEXmj*иupw+4\p7P uXb_e6LDQͬE@;1ߚPjCg-5Q)-`.JB FшJzʠ C!>f"V'CtΠ^'FxK<.Ӽf4fd/6"rwK%AL_Qmv i5> h8@Qͮe)};l@ȷ5`[W 6)Lqn1tJ< ~{nXC0P1W)2˟ԸBWxe(<'v ? ]0 nz/JWWYs1(ظ@t ٗuw JϺ'%)Ų)בfGrQf *-~!.sA$*T,6 $Ѐ0vEouI` 9ܪmf ޹?, ē62y_o~Io\$M"C1+9w5H}'sA}Q薚qٸoHaTQKOK@Y53J.7_/ 4lnj8qU8(/ ?&`u u3İh+˳ @m`j>%& !q[:nyEe J;cZv@)@u@z2Ss,$ S9MVB48Mf\xӷΖo@uWUƓYtc#3f6vbχ4Uk1 v 1bZq6 E3\Z ^K>(27c+:crM\Gٞe*)Zln^vUq)Ĵԍ+]. x!dDEJ>:~LBЯSGxR?5eTY/DSh v@,zv#;eaعATcjn&w;/HnZi[%x7P5Uj@x87_GU{~MDoaVՊ uY`Uy"8Q([\7ER E`2s#$+؄<&kxvbi_$cd#X^df>]?&bbGo8oԋ*x^re-Hw+B1Ryp?qu 1hۻKn4s)J̢SG !u6 $5]/W}_Oɰ A|i|kKA~/ 1\Ye,#H.=}/2R@ioHU@r9 M!.hwʨV/X&TPLcl-04kIjA[>4jHm+~BcGH=jZ 1/w:.^XFBp@]}?]F@]_~D<-UxOroVg b [ AOg#x]MG2[6ҕ%$@Pv2KYDl*0K@C zXr.rەimUHй\GoVe{݀S*Q^VG2߆n_oL4{~eӥ0 K׋($^k^X2t((1=NڤR.k ZSUV @OuKQx41iczA3n)kgs:z.9e MO'tO L?kTCv '}13NߓbvSu,TƔlbX[n馕y:yJܙ9v ('He-R YlHp:|^ jJv];K6D[jW 0x<\7%fz rb]wBpy/ NKsD-DZx>/yT%.M46euT(s6M5܄b*phE/]DŽ #+ ٓo^mǜ4ӱwFKnh$ErP4QoadX&5*@JSƐpq2N7΃XzH(µ,:ֳQmQ+K눖@H(/ 5^s M 5]ԶShmq~КjՆ XQM{NSLmOC"W\Ԁ&"wc kMCZ6S/ֳi-ijU5 d+EeZxbP ZNaJIaY+@Fi| ot Vj\eM0jbFA_FK5S=xQɁl+BuϬEgZؽ^ea]4Ƒҩ;mRj)x0aرXɱ/.f_-\~h,yD`zf`~\ʷ~ć6~lmqIj3C-gp^: VՊpH[je$-16.yZy& Af\!T$Yo!mT2SҞUffLi>Ç<.t{#DzBS0ʍFm*gZn[wqŶnu@ӈ 6zY/X,5V:] ѡ)׽U0ښ>>RgCY2@ }8FdBp5ư^95)2jnz~wFa1fAk( RGй=Sgb*^]2/lRVJȡx{EAVbZJ-(ڥȀ-Pê5L <ʲDڨcHK9=PXKmCaE#*QVfBa=ѧpltY>qӱ7 h c3S{֟ޘ|W暵J-垊 s]|/a[`_D@lmonI_'&]M)6o܄֖<|p蹚)?OwlGTTuѫ6_8QiНHc /u7ŒkHE7Mٔ)v:̨#ɚ9bU.[(Ul_8k\ `t،C-* j]#\Ӵe M1.mқ_gRѭp@| L_$ Hkg 栖q|B1uQfO7>3s-ذ5 Ro'0;L?W $&SVx)-HIbZNt~P] l0ʹ%H%UT Mi7?@2^R k m4XǼ!p&I1,CSUh]#X:I(7ˉt|ׂ__$U콠fyo!5)N7^mQf;h+v*`Ť(X EZVQ(g%g*[`ɖsd7W^!P.su\Z*DĽku)4jRa"[ٚeQfGuXMУ:Gi^Q&lKyTA0ô8䉄"F˽`y7_e%iJ+ƥeA1.1Ouw'X+8pӰ~Se=sp. W(ZjQ.BȈ'r..boK12dxmnur@| .dx_f<Րt3FD\+eBK*&tk^ip#3HRGm"0eV-oxɖX/QdqZG "XӚeO&ԡ׈4_U#M#L 2iՀf7`eL 3{K  䶥n=*ڎi4htU]\@Z l )fJê2e|8ҟb?:YWXMpCkP9vG;gQnevuJ:O-'j_h20$uo+DjfP0EyWS ..g'_kj}&MUϖfm,SOxcs0}87h=Iߦf{ecW'}~cND0Yg i]+ZxlB {ZEo8݇i@|^nw?s^ D-xư8MV@HFV48aðJmqdmr-0.12.3/doc/fig/md390.jpg000066400000000000000000002656651501654372000154370ustar00rootroot00000000000000JFIF,, ExifMM*bj(1r2i-'-'Adobe Photoshop CS4 Windows2016:06:25 09:40:06XX&(. HHJFIFHH Adobe_CMAdobed            " ?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?TI%)$IJI$RI$Y~˯i{s [}?1icr XpWvV5OUM$IJI$RI$I%)$IOTI%)$IJI$RI$?HGJ?rUa(x-n]#?U9X)I$RI$I%)$IJI$STI%)$IJI$RI$^@ޗXrJҠ}յp^_?[Jߗo Cl$q$IJI$RI$I%)$IOTI%)$IJI$RI$?gOH⻯~,. 뛧WE._ҿH𒞅$IJI$RI$I%)$IOTI%)$IJI$RI$~uL82Z;[+y.̛Ǽ6~>ϠLN}js+vg蟵NCt\?S#QԝF a,.e93c?FS=?럭ײzNu5bՋ3n>[Xֻ+W۫{"M mswֻou?u+Ɨk%_}U`Vv#mx6Zׇƻ/ݱ=OVI$I$$I)I$JTI%)$IJI$R/230>ݎ=V4[Ѯyεx}6UnEIqmN.i)z~> cۑx}-Mp5\:ncXژlѿVr+Zʹ8ݻi=CL^ʄU\lns}徑lo=?TU׏cѐی.le]u3{?_)_eﱅx?y?Fc6{Ugk}YƶqU[]e ,U7vHc\^Qr.2KZSw{?~E]uXy.@k٧-%נ$I)I$JRI$TI%)$IJI$R;ϴ-yRƻyie'%vJxsjRMn}M~ڨ@LZ];u2Ev>0W1,Jz5UWNHlj:ܔT ?7~FㅔAzpAvI.'ǵgMpkx 䪹9Q)$T"v>\='oĔ'?"*:4I%)$IJI$STI%)$IJI$R6zmt^K ֪H2ml?r8v_όIO_ۃqťD\1nLyn]Y ?onkTbRَki\}3[][=mɯX̃"&6+KHq~O;{'QMOҋohɽ7}[ .# }vf9W✊r7~.sdՕ"5$-ݸ{\sR8}@0xI\3;nzE~ 쫄7>u"AY] ~G{v+ 0]/ ՚)$IJI$RI$TI%)$IJI$R-3jk\vgϵ=[ ')QK6/VS7{mˣ7enklcksW&}{O$_jXR)VHRI$I$$I)TI%)$IJI$R-3}MsFL-s+UW`-{kF5wkSZ[M%\mc_*3k.8GlʙQeuB?ad_SvUuL߃XkZR;:=7{ -fGmVXLmBn^g0s|r/~-mnsckms`#]ޖC򶤦Ke,٫qinn?-IJI$RI$I%?Photoshop 3.08BIMZ%G8BIM%Wy$n k{8BIM,,8BIM&?8BIM x8BIM8BIM 8BIM' 8BIMH/fflff/ff2Z5-8BIMp8BIM@@8BIM8BIMEXXMD-390N;VXXnullboundsObjcRct1Top longLeftlongBtomlongXRghtlongXslicesVlLsObjcslicesliceIDlonggroupIDlongoriginenum ESliceOrigin autoGeneratedTypeenum ESliceTypeImg boundsObjcRct1Top longLeftlongBtomlongXRghtlongXurlTEXTnullTEXTMsgeTEXTaltTagTEXTcellTextIsHTMLboolcellTextTEXT horzAlignenumESliceHorzAligndefault vertAlignenumESliceVertAligndefault bgColorTypeenumESliceBGColorTypeNone topOutsetlong leftOutsetlong bottomOutsetlong rightOutsetlong8BIM( ?8BIM8BIM , JFIFHH Adobe_CMAdobed            " ?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?TI%)$IJI$RI$Y~˯i{s [}?1icr XpWvV5OUM$IJI$RI$I%)$IOTI%)$IJI$RI$?HGJ?rUa(x-n]#?U9X)I$RI$I%)$IJI$STI%)$IJI$RI$^@ޗXrJҠ}յp^_?[Jߗo Cl$q$IJI$RI$I%)$IOTI%)$IJI$RI$?gOH⻯~,. 뛧WE._ҿH𒞅$IJI$RI$I%)$IOTI%)$IJI$RI$~uL82Z;[+y.̛Ǽ6~>ϠLN}js+vg蟵NCt\?S#QԝF a,.e93c?FS=?럭ײzNu5bՋ3n>[Xֻ+W۫{"M mswֻou?u+Ɨk%_}U`Vv#mx6Zׇƻ/ݱ=OVI$I$$I)I$JTI%)$IJI$R/230>ݎ=V4[Ѯyεx}6UnEIqmN.i)z~> cۑx}-Mp5\:ncXژlѿVr+Zʹ8ݻi=CL^ʄU\lns}徑lo=?TU׏cѐی.le]u3{?_)_eﱅx?y?Fc6{Ugk}YƶqU[]e ,U7vHc\^Qr.2KZSw{?~E]uXy.@k٧-%נ$I)I$JRI$TI%)$IJI$R;ϴ-yRƻyie'%vJxsjRMn}M~ڨ@LZ];u2Ev>0W1,Jz5UWNHlj:ܔT ?7~FㅔAzpAvI.'ǵgMpkx 䪹9Q)$T"v>\='oĔ'?"*:4I%)$IJI$STI%)$IJI$R6zmt^K ֪H2ml?r8v_όIO_ۃqťD\1nLyn]Y ?onkTbRَki\}3[][=mɯX̃"&6+KHq~O;{'QMOҋohɽ7}[ .# }vf9W✊r7~.sdՕ"5$-ݸ{\sR8}@0xI\3;nzE~ 쫄7>u"AY] ~G{v+ 0]/ ՚)$IJI$RI$TI%)$IJI$R-3jk\vgϵ=[ ')QK6/VS7{mˣ7enklcksW&}{O$_jXR)VHRI$I$$I)TI%)$IJI$R-3}MsFL-s+UW`-{kF5wkSZ[M%\mc_*3k.8GlʙQeuB?ad_SvUuL߃XkZR;:=7{ -fGmVXLmBn^g0s|r/~-mnsckms`#]ޖC򶤦Ke,٫qinn?-IJI$RI$I%?8BIM!UAdobe PhotoshopAdobe Photoshop CS48BIMmaniIRFR8BIMAnDsnullAFStlongFrInVlLsObjcnullFrIDlongp2V|FStsVlLsObjcnullFsIDlongAFrmlongFsFrVlLslongp2V|LCntlong8BIMRoll8BIMmfri8BIM-chttp://ns.adobe.com/xap/1.0/ XICC_PROFILE HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmAdobed@XXK     u!"1A2# QBa$3Rqb%C&4r 5'S6DTsEF7Gc(UVWdte)8fu*9:HIJXYZghijvwxyzm!1"AQ2aqB#Rb3 $Cr4%ScD&5T6Ed' sFtUeuV7)(GWf8vgwHXhx9IYiy*:JZjz ?ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u#FFT*"(,UB [_ STZSvVخԀũꨱ4&dU*EczuԿ->0XteeYQ;ne(Լ9qH.e[{VAz[׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u)Z/p;+w6JMsUo!YD՜Uu~&IY'Te=ULM;WQTUX+(i *~5,BBV:G7)3U7.R>eɷC4VPTbz^Z2ȪW'ϯ]?>*v'`n ׽7GNzǹrLJ(%d@!TYܖb}p8t~S^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u*JP}\oCro޸#ۑ=Uw6Yd?54qH?7$>2I..ڮ>է]CPoʺ X*TYFkӁq9WOsBon8|g :^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽ug+{Vwj֐..SM]?B~}Tku m%heHU/(RUdMYHV"V:I7:.?+}huxF#WԺ?ԭ[uxIL2Ç&#7yG#G+ugu{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u+:H8ǂ?jSL -no܏Ϫ#|~ۂ:1|lx!rDљ"b}N$ɻXMeT$frK3zX~i'z"$ l?<]3G ëMN׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u'+NyY=ZR`͇05$nGOT! *g`ѥ"$m#73,QVK8totX[6Ȗc7ss7}Ami.O=[u~{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u+J^k ޵~=BĚΫG|U mR5QRL #Kov>ꧏGL?m3ZB~Ԏ[1`.@_j$tR~JrTͼWְbOWߛp}N=;:?^t["ӼkM#=`~ޮ}_{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uw+ w XVlw ba|U!tp?oW׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u'*ڻ_ߎTC/4,wӯCqkOUv`wgSq*kieRBm`(7*>K% [4']%[LIK[r89Gӱ|#8sFC}d?y{O't~/W׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uQךA ϴ4!>Ƙlu {y>z ׹ƓC4tFu,;&},a~mJ|=$W#Ҡc"wcX[9zvu&- 꿏;)-Ǧm ?2__S}_^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uB(ڔ]z j->j_r?z%?ɬbuh~ 2N@#An zG/tSJwcJ:#eGU[1QȱDJoǧG[i-#ըcFm5~_S}l ׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u2WI?JRΥeq]*o':|r_+ҒK*ʍF|#8T:G/tQ~KʲnY%,hn1EԩqmDI8?[f.:R?*ݭH?Amxө޶W׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u$V=G^qUֵIkJ}=lMa7?Ou_^W + :)@03 DcJX#O$)Ot_| .S@QIbEKJ9GHOC] wŀiuļW?~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u\N_Ϳ}'J(ݳ1o>_u_^B('#KӴ"DJR@\:G/tS>RwLB0 e,YGmI{BI8?z݋>Um7'Hc6_Z=':u8^{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u9>~Pћ9oL=G ~o½WצGsTYe2!afԱL"H!"ڈGH&(7Ǒ_8pGP?^)'[[j|b{E6.|>ؓ:_fomu~{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u{oʒ̚iHVƄ!7#u_^|qZT+$,`BZM1D)kb,>9>37Rn+5 UFP -cn=N==:ܣR$TAYԊU?1?'}u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uu5ߘi=Cv}1K(TO} _^fu䨑-GS"Pjj W#=?U1yFR(EUC>l G7'vh{eg̍ĜW~W׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽upb b\7),Xqa }4{P8/}z[t!om*$o9uum !%dI)tFm($~Ot_Y٤*QCCP[@+[MI8_pJ]@ɸu_ٻKqaܐ|?OlIzu~&k_mu~{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽ukbܕ\0JފKMooy΄_bc$D#U"Ћ:0&@=@X KtP>K ~_*F)v uƢN==3)I试4za{_"x/ӫ7[e{k~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽ueU{>j18ъ7Z-n< [GpgT= #UlLtfCHR72R}$E/d˲B矢m.ѶZͤyUuF\jUFYR5}U}#{ IoylepSƫkVUTp?m?aG[OK+_x޷мrU @='¿oNni힯׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uC5ŝ;J<Cpu_Sˣ &i@6R5k^B#|g-FWPeKh$z@,@~҈U)_.)ɳ£A&ݑӏ?p}'ϫn힯׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׉'9$=u}}T=j$Ore.XX/WMO'CBcS g-4.H t_?X( dSUċm 77^~=?:كYSTQj_ 8ɷulgE>ړoNn힯׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^,OEU+PKʀ^wv$x\dM\^ڏ>zqM $ӫ$,E@"*Y$  :G'z)?"ef\(d2i '̬8S}+ܜz~*il /Ox|v(G)c&A[/~}8>?˭=u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^sx5] Mʩ}*A<ïuv99Oldܡ>j_SװϪuiUʹeM7ƜTYVO-by#rWYE#|Z2EoeYT[2D8$#&^l4Ƙ{oԦoN7[g~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^cz9ٻn*ٱ|iy0ªRkunn~{cKwl\n KsS3j,_lAo-y"8"b׭cNxb[ޔV))V?,16*d> kg]noU:I Rz+?!7DG/iTo*y>bD*>V Q]+!_I(@zs.V7Z=}]ӴS7;n,=-D2JP 8#c:pwֻ?ruup8lW8jz񛊲+lRGB@Ɓ[t;?yj ww.'/ҚZ&Rj*>nD(53%ЉTzBtW>wj7;O;4 PҚvlA- X#8Bͯfj(Ѻ0Ī O("Jj)Ra]:/#/eq74L~%?Ǡ2:^`/bXZ_g$׹q7/|t11"GLʘJɎb=\ٵIIiO1S:s*RC5bmƋ>@1O UhBH1勹e$Y?ON II Z{xuSëI)^: rYH`6-uBOT$iu(CwT6R=>1'Ⱥ>߭by)bmk^UC1OLS:vn;yc ́&u@aƔtS"c"i)]#OQzyF_ShgWl2]!2²I.Ud*1鲩_[q(2=[>={(vU]uTc O_SY]EKiZHeC@+hfqç>=YϺn{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ֽ≮oFo:*+2G_̘ FVjP~G˯_K)C[3U![xۃ=)ën\vnETjvoP'I&ڕ:C :- rUs}ǜ7U:PS5 rZt@S'~L2O9!)'P,Ͼp8$mJ϶Jo_[i_H+~Ro bz}їw  1ԂH6q~:ۯ>U`| ƔzeZ碚8xAPcY0Բ!*?gHנUʿ&>h2O HMIԑHv<'#˧[ozuOts+S~UtT2F<j˦B$q+ ,/`G?J3ӿMkE$r*RNajRak~}|'/:GGX؅j)'Ȥ&Rrž/-d30b8KL_5z۱p%>/Vb -T!t*V1ƴeI۷Z:ҿKAߙ,Cc243yjfi"wF˪n=.c"8vۥW/lI}:<޶)N׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ּ71sՕ۳*vN'{u/j&)7=Qk,Kt~δ|޾r}]|6++)z)WR'UOJ& =|'Iu푰w 5~_xm\'Jq%8u5 *+)Xj`AS ä*Ċ)` ^0b wl_=цeb(67gvtmj걻J%i%* JXNAi׭Y@7|r9aRN0:|V)R6Jk!o~r5Vռ2)Г{ref(÷Sym̕v/Ck|DRѻBYij'>άou~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^֊?_3yE-ݣ߁Blrbe"Q 1c KM"pсft {Z]LƟQ%gg2%jY#V&m}MzcWd0uUP4[hK$_@6f-T@:@K=ϗi؝{<ۻ客O0Yx!Vt|>JI E`?['*pD;7/ݍIsU{G=ES%^W5=sшՋ b@$XZ+ iZuw_V5_5l ѓ;AW_hᩢUC,5Pږ;M2 zyޙ7Z_Q|Zs{tO;Y[|6FO!¬JLoW d.I%;_ϟwq~#%>|fX`zز}cW+-`h y%Oˠk CwlfdjnؔU5m v|^3 EY "ϽxnGu" \3jejTuw^ν{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺU~zc>W 6gwR,eragMY2X(#a0aEC%Թk9R c}߻z83[vMI&ow^mun>)Ӝ}$1Iiht[1c`#OœT)5!#"XGiTkrBkx_SW=_;6&:Y`,S?qYdRI1$W$t\ĝ(gEwNx0v=OX杙V;׵Rc%0ŨDe̚r: OA8>OyTņ<}]\M"Q$D4KTiՀ:YᄅٮݝIZ'^neETM)7Xr9NP$ "U!ҋExt¹S|QtN]+3;b9]QW C\|*39 Vn>Yuu&+Kv\]5m;F$U>Z:b t:F }+a"Џ>qN_;н}כkV[=(*h)1{{mBHh]^ˊew*JTT3N3TPuMqdwN1 C Ʋ,b$HD'$)U =8=1m'V΢N+8m4AOSݕc3u"%$Ӫ75ƀ uw=3}xo:,ϪHXf46ۛx eYfh*ڷ"z)붳TAf/vs!vFrK07&%OPUpOyR:4V+e(|DEwr_pwfw9+嶵NҪ2I6#[USORIJ<@Gub)Ӱюuhʯ/K6F[omdund9Hi熃ldpb)޾UV=j:U, hϤIG%8:;Um~ع I3xt4Uչjqt9*ZvKƥ|gKfX$e_JVRABu :}׫u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~"{/~:wgnVt۽uFδED=!LI@R^86{y3m*ih8&j-Y Fs*L}EN*Qj,I!Ut @0'Ggl:*<2T'Y'5)D2bp0ŀ`M-P> Q?VP Y4P!j~2)* Hߴ^ztiX- `YaTѼl1h}vѮ=ּAnlO[H1 8t2'4 nIeZ8lJԲPEqA bx uk\צar2|HX)+Q"> ֚5SL%Bu$RN+tz@V?Tݖ<,FaUғ L޴p[u&nI<mtMYsDY6׼_ljl#g/ sđw+80LDv),}(To. \>[dr-lU̽d(UYZ7[A u\UEO61Nd֮48óGUYo:-pO5?/ti5%e/'UTT]aIU,UUG^ϱw?ُ"a($0~c4iOBWyzns]UY.;uVU-5_|l,-G .;+ RJ)LDs]ӣݝׄaeV:(7LvԧZj ԓ|Xz"ibBG)\S6NtIӪh؈b:L6@D}2:ȦZ%8Y5`ӠM$U]QA01iƥDԦI%eTYJ4F o'#n٦-?zqjb)2tX#T$dSVcFu&< >s:'&&\fW%wo/q2O*g-t5ԩUU,+5_OĊ==nzTSUK6ln49UqR-,RM}K]U>UjPk-",z>QmpaIԇaqf9 |uT R`$˦(ǯZif^31|nB*|%N. N3&FbWEq :z^y=Vt8zudCJm}}Mr`輑FESK4(G^-2xK9I!M6}"d:I)i ]UcمCxҬ 'Gt'˧Z]PUA=N裛hᡈ>j:FzlκdtK"@Y4lPyzS+Rv>EAMV f)I562H#NJz/Br))\tcqW2{V2SC,VX먨'#]LˎX0ZF@q*V={lfoQ.Ch(Z*zZtZq#O8p'ye QPפ-]?ݸL|բ)MoۑD~W<mVsӌ(>]}kν{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~JտkM*'Hq3%9b eZ&2kih7FVMe^&hAIBj3|mhORJ? q9=@^W.6(M/r-#LBIBKJ Jj߃;/uf2n>* {ELk!r]:8` $tT\t8꼢.@U}*4(mk[O׏ZO[6܁k\x\wC 9lEeF7'BVZIQ{4RFC $?_v=Tyue=75Q4 =RFU0kf-X3OhRW[Ab+9'D0ےjz* _F6%G%"RQLXH,[Q4?N(J'݁s)%;OO3YTEH {}Qq:cu~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~IMw/樢%÷q zyk*Vb*Z}uo5D$h grAbƽU^@]| HmeW%2SHޢ:3O|"<ޮnE1=KjM6fjfhfqA{OPTUAT$%0Ja8gGNnc*=wvSum@00NMOB}Ϝ9yYwNYimOs!y(##[eN*te4>E$=|xcq.띧)YKCA4m&z vUL$s)TmzO#P6XӏDj)aUc i2G*Q42hi#Y*G$RԊ%Ffϡ+v?͏{'Ⲧ) .6sXElӁ,2cjėi }ZcC_XxhfD)bu9cCA ?V럿u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~h 1KPLT4q97*siqSSFC6ǚHᨗLB-e(Ԫ'Ϧʣ1fjּ%]Q_EPǭG(z*QҼi,,]´mh:g5*ؘUaW h䩩4Y:l]4qőH"$(MI0gwufڇyn' 6 .::Z}THZJZEQj>E ef5$lyIhrxݏc*xz cS E\uIi,WVK4rf FjO1Ӏyj =]5iMy꺶ڻRjVzF\5unH bqf[U?g^=@L[] l?l*~vef fBcx],U3>LQE7[PVV f%ih(2S_[LtѺM5;t֮u7u|E~i8*Pk,(Cvd5- ) qşXԭfSvF)eeZ*)XX Do"q dF˭Q}vwOҮMYjJecȖL խMH )2?cӯQqnޭTUԮݦz}NWU βTJXSr.kJWӯQx}| Zi!hkGO%g1q)c3PT>̩jo<~EE Nfi']d *?ֻzrm;S>ɤzQ=Y.B f6x ӣ8vSӪМqbj͝CPF1J8h'SHy޹|u Y$C$1o^=WʊoMKEGIMR* _OO h㆚0@ P:ju~ٛmSU&zIx& xd7 I#CM,5ΣTL=jHפí?:*ɟGkTO{KmQ*dڴ qj˄d <"ӳ: ZKCQN~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~|;pU/$7VV#9 ydn>y FҡxjF:l9,QVNZY~,pM<0*.2!,ѹF!Ç W©zzj>)f򘍙O=N/j dj*l1 J-ŠK2GH8RGsTv7ߕH%a(fӽLsǐTM6KCU,> iSߎ^c"GM:; sVQo UKGQ̘|jEN͹'3RTȔ#jV$@ǀQK識جd-Q]]Kq%;iʦFE֑յ )+Ԓ](+wF|027Zi0đA$3#Qk[i>:c޴I_vbj\0\d1EP.+G!QTBSyfWҢHា:toTG>䂍ZTKW5UK$(b26ҼsֵOӝTRc (yj)p`LPAKEO*+hFͨ}H޶]~Κ_@pUHsySMQ.IPQQ2+J Ϣl;z }rNu GqTcHY9妖"9cF1G YE={Yi::k*ԌfCwNn켔,3Sixx MFRC1z֦LSc7zRZݛ*!NM$u|<0ӼD~UGϭk5gu^Ҭn撍)"(󛣗()+$[%/=:H7Ԗ)֋ S42"lt7!cajp:i T1zAC<a]j+dμ­9ڴƺ<|t%-1_Z O{&zGFHD$" I o'߁_0j03ID">ڷQ#K,R/_ bܣy/Շ`/[_gu~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~|患/f*/h)1w`F(C,,HڑgݺzMQO edzhzZ! y.Αv7½:LսWU|Gjj֞JUd_#1y䨂*pK8MJ*Vn|ٝ֔8ofb[o-f>*|5cf:rQM=8J׹2-NͶNPzf5u={zVAW>;᪤n 654R Y'A ϫ-'#ַav[|: I''1##IB*ή+7YyP"'J^JgR 6 $2dEu/-#:ޑzenʒoW)GS *b)FAJY(#YPGuXS*L?Ph C$VBJ5 ك14|ym]x%O,A6^PQR5 Jsjfec}яN=:gx6_C{KlSQZrVB#*ߔvN}?&|gZ?'|T,2M;0UGv&8/ߗ[`{ku~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~|޿@9+:mW썉U{GM!ZIk3RksSAMSXF)BM i½PxGQV!GdJjaI` p gZ9HM?:v?\?&Oz:6gìܕc=B9↭)M + PQx֞&Ktԟ#8N8L|_3E{ k 5 JZ()'F㕤 4m4BtPOln:̂u~=5u>ڦG]O)rNNM8zw$U檘WW=\ 1fʙ gtE˭`wU:1`i<a***墨ZINѡBP yQu ӏX*'N,VxFUKE_QZdTn"THYV2W>g]5uY+\c%#RPSUASi*a9aW״NqwT2oTüjJAֳɊ PL~2=^-€ux;$]r";ziҦEzL<M3G'.HxD=?o^8Μ`x^0aqYmT'Y&4S"ͿGhqV16ֿXq=mw׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~|^Ì_~--vpej^zҦ'[$O/h"UT{S>Κ?oD'N7AQOMaohHb R1YYkFl(4zz?>c-]\VJ2ux9QAC դMI$u-쒮LE=:f_v>peud2Lon*<{C!OW/Ԁ|JN3 ף!3}zݏvԕk W׾MLJIA&~IaIe5&HW]:k.$β0@e/O%jӆ#r z/H@(I I,$xNսOLYLwTSPɱ6;SQa4ՔtŐ˒ GOa^w: >)u鞣i*٠%L\m<ɓjJCL2 !`JX)p5]Q}^ ާN51%9ZfJcYPԭV&zw,t4ૠStFI'vUN1Ne۽k]N N[eShbP-\39E N4qn쨩 5$=hsBw|2&-ZK,J1PzuLzMty1=^V 1MMKZ:zZFc-a'BɊ@q4bKX nOu^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~|dDWXȑ(V>=" xQ>C3EO:h>vzǧ7>. Wr(t/(Uh3Q7|TL+l1U USe<Mi2V`qY!rкz0~"<>Mto6}CW=[D,4cUPfdj^U#v.f ŕr}5YY6I4祒fye"BX-1ƜpVcNw54bcc,2BV(e sS'ʋD\('+ڇtubioRV*@i檑O8)Ue>Dmri Kz?o'ֽylTOhI))sr=Ve֐>.;Lͳ;KSO٬zSLeYJU ,j$K[#Kˬcv:Fɱ?oVMgfH ȍق&xb@ KW5o_vu`eG(쇪xar8!)76'^ G0┴T/]GhofJk+e >mZ,_XC$q2++s]KB2(C'tWx=VˮTr<[iVҴmNifKҼuXj5 7C=n?Tl=`XTf)˸XWei1X-&N,I~Z֣5$zv[ d4rq mrx%зbI:ȥ>g޽BG5_pQ%[*d"F)% f#uTpgعZ삞I*xzڦ5Lo䧣AQ^R=l(=g[OcUr=E }4Q@jEDJ6H&>ν/aojXN[Mt_"ަJIT!%?1 Qη~_T]qo, FH-5M Λz7IL*aky$,dRQi1_S.Bn;!- &h-=F2JHgxX0 6 H)ǭi1ztE&5$GYY^(՘Z+G-*pXr z~ޢü SSo<3g%:HR>^q7{iLh}:䤅⠦Q.o$) n$$Mǫg_f맬ϊܘ/y)EZBEd$x)YluOzqO]g5<)δ})Jo`>MZ$*(O \jO׉hmn{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~|bT#|Iu]K#o$Yl mnyO7M{c^!MOKX2OV%gcx$(Zx&C3hôtth&dv^S4rf]TM+[URT2ĐGGOY'BҚkrdt5C[[IG%vDnr_xRĉ=LB5\Bc*K=YzџG>HZ%6>JtYjW橲*Aߴ 7 @4u}JQ:zTOv砭bZ$Aڞ* ݰ'O6%fn?zޓuIؓ$tRlZJy`ieLHh'/gLTZ~5Qݚ:7 ds%:2ZY3¹ SΒ,l+ 0hD7CjNO7Tk|sƳ6XZzSSjtIZ >ӮAp[[^֟oQ:{JR!4k4mDxk!vH7}*Cz?g[֟oRhz|Ev3l餏#+"3K ir5p~Ϋ拭7CoFZ*PԤj51"M Fya;2O"֋%a+STV/ngJJy4*E,A'z&Lc&K_˯kO5z7Me5uq-n嶺Rmԩ?s|5 ݝ$@{ExG^ʥ:; A۸6(-?8zdc1jj ж/'L")!W1q.SL$^!f14S,4֦X .rO^xfߥ ?P5ehM.,ZvMN$r*d29>δ})GPc5I;^+$dʂЎy'R~8[Cou~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~|η;3bm^>nu˷7F[ m8%%-A}jTi#xvXR$26i6G5Di8UGFU+?½^?ލGY`l 􂚧'Kf"4.4dAPd9*>!':4xܹsЍ+W |5Yhj)%79))eU2AuؓJuEr?޲뭱M9rT_xoiduY|\5OXieNANz0M iQ>%Fә̳i*XMh dߴ~.'/RXd H$GctGYŬ\\{է[c.6Urz_`@Re99#PS?=KzGBIgX:َV@B-gɮլSdiqtU_i[:yzhzn6?oHFT+1VccϻmVn;x /"PhH4,굦Yq"?GU蝨|zw$j?ܥ~ ^46E74" pC0#&ݨ$ xtS-jhҎ4Sׂ[4oT( <,~Μ2G]`*.1LySoC?@ n$R . 'yA>ޝ{ďl?b/,y[܍p`LhzO˭x:%W^ًde:|e0ʇ%W,bM DTG4)%MF:H*Ė`dhS2nW5z ]kFt|a;S꣎٦:&U oDk05V޽?{^iS EuM\uO(âdEܬ5OK {WM:aN昦%7sSȱu]IǩR~G=lqnxV׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~|>a폗?$;#t]J5yv>cqbiZhǀt T }>]7L|J;hHJ_*d2ꖨ 4@''5zv?ތ8xzǒj}G[+f)dUV}4~XɚtԿ%?OP3GEQ]' Ё__YS+d4}$+,%g1^5b9?9Qe_dn=28rΫ{T-uzzVQ=REbZ?T)?"zO ʧ @ox׼7?ΨavnnM&: ungqQ״g1L>jPbĚ(h2)KH7,{ʻuaw=0H\HȜt]efirh k`lﬡxi/ 0 pi"@ YH`uqLcBC\} WK4մVK U=lԔ3V@hᅤ[1E$lj4&lTI@$JT@i!nQ펜׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~|Bb|1_'{H $^>ѼhQ]Z=6~^.tkT6e/sRRC<2"LqFTYU|kJz(k/GcY2δV)<ڕI.驾3ю[[E15B㦏tfݘ㨷Fᡦn(#di!ѝ S hU͓?_}Joh"Ili TROIđ^ xR1,T Vk`VKsyQm۲f2'RMSHY@"V /G+4w]k{M F^158 (?{\-:XM(NH(u25:a41y=ڝ|nY>岘zsJza+#;:]ZmX ;%FFX<_\s-pY9R4B;UQFQUw&a{+wdY:%KwLtQMO d [=ZQanAGic\\I$jLqH4T|>qm/o-cޮ8e $ H t;}7noڿm8zKx _Gۻp߿t?kQ=oY?.Iw!zӴѪqG@{+]w{[C /~ѴѪqG@ߛ,]M?[^hM7<]Y?.6m#ew39_>rA:&_&1.Ga~ʫikJkUj|fOӮ}ʯ.y+ZYYwX.NAVPZzv+BMUUjzz ZGGgS_DqLZMWVj:x@Y⮠(4Nh쯩b" Q*j:jzk$0NS$&(RAP8f D!tw{GZ ko,4O=06[f2ʹ^ 15\n>W[Oӽ{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~|@z0<6_z-Gbս?ghԘ)&N*)5uSY'4}hd+:.vB/!u5Ro5Ѧ1$[4m=tWjza~1.բIK[Lj+y,榫nWGJ )$6'L}'H~5z//^h%-?Fͱp,bϟ8\šX8$A +mN`/c[uCsyqD<+~8P<~j[ co+xjJhHSA:D 鰚&>Ndrk0un#=.Fkh66D6@gWj)O+jxM@~ t#N[{&幁fdýiZF0 +c'_ml{sdn;+bmS-6OEavRI(ܕ#T›jXʫ{<ٷMmv͂8oCpi BU`8VS=IJqr;ff1&MI'$=Ee`=k}`|G~SO_l/7wM߿7?{M=&Q3uQ6a}s(? ߙ:Dax߆qˢs6]dGES>Wߒ:1DoqSC-KG\/C0z/ۈ|gܖ tg־%?H}tƦZh‚z>:তZɦRvx>[`>Ur,P%V\Yx:rFFdxYLVOHz}8tE0#og(%\>ۓuIj/mn{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~?GwRm.uTv6ۍƣvgrpd4t QT@f*I=>U0:Fq^`);$ċ('2"C8G643`_:|GUMh) Q粕MYQViZkA+}gEiN/QS I)̆$RA>>Zz5>1g$\d:o@~_6Z=˔V.M>5'\x {/F;z&{d=hFլڝ͈l jnG595x{kK X̪L+F(pr3wx.].:3, 0 RGT v!X ت0j_b]2?@n>2Qyf$H7)wHcX*)gbd C;y]-Ӿ[:(k`UWGOE-DbXiXFaH@$xTDȨ͢$ ca$yxٚIIgJ<@tua_Oi~?vuVF-炋=.ؖ;:Ǭ\tux牤i36e}MͩzSul.x|F8 40*wwifvbH׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~?jF~9/H, ϗR _o}S5.2JiADOK2EM!b#Tıuh#Ǫv*Z\@iaYWgL!)&6-tӬ9A#KMGLMEuLEY!ZY)ꪉQ,TUUU XjG.pqMX6r5٪ֵ9o}[w'uǯ= z!h:ˮVoeW{TټNk~⬦i(b`jh3*{ow9v^Q']AA ,q=t m]{am$Z[6n#nD)hK4UIKtifa\nͦpO/#q>uU*sL~VV#ezi:ky6kx<89dXgeYk`а QUu\i̻]x)-/8DacFZ%™p_s1:/v{W=fpUw+CLBJǻd2Ta^G45;]'ve$.M坦Ѡ VVYhNpw^sw+ ԹEh_Ě.H"R [M/\>ïg[ߏ{׿{Ϯ[zt_ߺUc-QLGua#E&_Ii"적 r?uҿA5'Uj=H |mQUbGiKE,1Ld|k2:(5'U$ dݡK w(Ki [jtus_ȖI7ϙuC/`jHSvg*Q.zm?=׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~_`)Cj ~=b8S3%jrvO~ު }|{ `Hh*DȌX3++:ԱzzpCo#%`ȬEG^ziG_SPUuUb8['$CSUQOQ3Ө22RTS$q/*Q L];MzӫoC|^Q۾p?y{+M}Ww(?q.?9{5XzR|ՑXv}ܕ[4˒A6>.#C1rYxe:G"}0n;o!5{}&!ܔ8i*(5UNȔM#Fܛ{m{K2*WP*ԡRC\Ͻm{t3 ?+]JAH:G:=+_^ׯs#u@o=p:O^}ϗIWca<}tSbDTEQ,D AfT|,ˤѼ?r?\O;U\iWG55dlOM-%,/ID# _ v4d6:5fh7cJJcIN/܌QtxBE/8Czy<T0oXĘnªSWoaӼhēi* ]"|c7QOӽ{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~#Bp\u*$Ԓ.Û ~ݓ_g_#Z7ٛxd45z|vTN)YbhT(JP~Et-tmEL>UlDx(Z qղмAIUES漒F'VOe׼RTVi&w)<4ANdVx'0)3;\t|鍃[_i?ln Vcy%}NܤSa3D G.W=MoؕCcSϲ[W1]{[SX 1Iyi+PW O۹a}9w-.xqB?+ !3vC *F#>_3-FVpǏʆUB٘>"n|5lg,LfUf#JOAG}oB q#9<`NNϥGȹ7f֛t$zDjc<)W"9'g~ a ɨ|4]CYoM=}m&>n5Tb*zyJTLdeԷ5޶ ݲs@XP T%*I- HXlvc&] IdTt5-O_.2\OSuJyHPIuut׺킊N<:b+ Fwc/)ZZаP?S4֠㠢/Gc*)+u4ԳKYG3SLrtMOXs(s}-ě34PS5xtCf58|+#N$z||PAcO$vFk#||>*ժ0K"9DSjkT,e R#U}zO-T0߉k<׽=T)gi>H%m >@J{! 7N~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~*}dN__7:-od7ȗMv]tQG,- JgfOOCG'!f_#t"t[ʄ/T%<fAY@׭]Fs!r"|ۋXF@9gRF`8bwp9o˔b-IRH8! 5z硷>^Tvf ;7Sw&;qA!O4kjc-M%zRLT$zitʃZ))o{9l۞'=E\TqU$z@aU8'o@gY44ѴRX%Mdw9~(G);sIB/w͎C6%Q5AkTѫh) <Sa̾v[Kxe[i_*( VF S^}ǔ}Kl@d 0UPZѭY*4xQI oPbVlvopQPbu |J^ x!RBʶKɭ `=GŷM֍"VHΕ]M::~x*O`}]ߓeHP# ͥtFjh:2_Sƞ*ߵ;jmg-Qe2Rd0CUQR#O${ ʿ~k[Ϫvo퇱3w9EȝMJcX:z?o+7Y$.}?o!\LSk>dx_=؝kr;eC\^+A[)୦t%ky9Ͻ~lm{5—љ5(I 5J2u߽^k5="FSS/#I71"|6n, 91bYU]C+c*cϴ϶%o3է T8j8\Oe}^FoM ]B!1JFj6JZv1_Hhc)eQSQ}SSdh8S%׏i&FXV5 51 Y{}{y9r :,e@1]aV @7?qnR&RY4˘dVDQ+@*tjE/orZYZqQ"W›:T F+JӨgp}}yx9L(VjxQ&iБ<-&?cLԛ"wf[2dC_*vE6!-y8)GQ;+!b_Y{vA$*H7_8H婏EMAAf6~?}޽{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~+>H`nޱ#P ڷ|KWMBm椊E.fQP>BgOFԞHx e;!]>tӶml^ŭ-P5%5SЬrh熚/S-xT["4]e=UD+v\`xZ/Y %fv=PLdpMm$dJ,HG yY@H´ɑ/:]qxuyJ7)>0M%5GHjR? ~KU1WZөiN8rvZjC8F2 }zޗ wYbTEaM:3E#>&Sִ(ԈހҤաLEJZ9 &R:Dy=ׯiN߲yS=X/ˍgYO=TK=P̎Q״]˻;y;k1 Q#Ӣ,00G#QM'oު=zUvn]oO ŪTW)2K\0*R"EC;/鞣}x;B}l|M)%Dq@ʨyC sׯhoNn̵Q!DԴ>D))j==DvLJQbGhu#ov1 $5a))UD#Pg$KH}?Qs%?@csYꍿTeqj!+PU RMPYc$@'+*tl_q퍲mV?5Ccq)&Rh%y &Α׼L>g(~6w.J<^/_Iˎ Jl{esʼnqQ;EdKuȎ/th1ĐXJ^;A[QMI BVE:x,/krmjSl:HmS[^÷i#ٱ͹Reŋ^hi*HY-d/JO۟n9|ѭgg֊J̴S Ӛb9"Wcxz:&f}㙌QHrSI<71+LfEYTzKySk_H:8h6>ՐJjvK|X4K5,u1d.5R`zJG= ]ոUt1+"LgHRHp* WjK⌐*TTF;NѼoZl[=Fdd'5.V5bTP*s;֊k.25SmCq%׶GX i u$Z8ӣP+i/o9Kr-{i:Vrv'׬;_cTA2{/rA|>S S^qyj mm)j*jF%䤣ƾ;:4GY9}Oha@43$ode>\c_=Xɺodl͑i4t¯Mߑh5tw5ܗRCEI*+USu:@֛ٻ{OmT&܎`2Rm+)dr9$453VMCO3W>Vj `Q̀~^;9Jq[ѶWUJ42ZC."ƀS?Ә>0VW%麷:ϱ'~9vF)o/fGZTn۴/^9fsxWϦ^]'{]_rϷ4֝Iqd身v]@}TU5uUݼ ͚9Y2yYMnE`wqa_hf#O`j)V@z6:J;UO#ziӫ^]w2;y3l}$x=ܻzRT3SZjV B{& ]+hncPxQFxOql9pw= ^öƷibd1CqQ>^sfn}b[nW1 \n6*i᦬ȍEɵ96rc!ˡ`X; %NI^n<[w;uU#HF),;y.?mmH+G>Uu)֢IhhԝV_cMDb9@HwtR؏V3C/z$yE<.Z0n>Z>'#df';DcC B-2"7kkS:}z%{Oӽ{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~&Hz>Iݓ_aY( cTzNП+Eזhb $TQHdS] ;?l%rIP6*05=lQɏ*Zȟk,kv:|G6 ёA]__?c˸wc%J7,Et"bSBIsH@>Rk€=[)GZh竕 U>š(𱊕Ɩ`P{IoZ]>O:[#9pY#k2ULf>d^L*8+3RH˭)=xtߘÿ|ezjl O_yF$VɰxVb+?UZAVXêTIA^QG!k#T0rr*(3LUL%Ykek\Ahۦln޻=-9C9eVV*y V2<<<‰OMo1H΁׵+vГwH흣ۘbX*i|1[QZŁ AZ@,I^{gk3)ոj}ՆLޟpEh1Tu3H(iZZp8as>:o-OSt*=33#5VgT/eXł&=НUHS}QەoU{rIeÙSmv#)LId.uvI1O(89ˤCub֖:UTVTNCW%=O;Δ!X7h@t[ۧ$ÏOT3q'.uUteQ-UBjZ7e"Kg Ok߷ӫܔYlT{jOgIITFE[x%O˫0 8'VIQTGM@ADsTI+RC307SG~ί'(HV&І@U#xvLa~iwBB?n׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~ϖK$ǡlagz|kWUͫmaW^xxTXH4YIG_[C.%&X6 G A2Q\MRҴs*Ƭ\B6¤`?~m_)|S0yZ پuvfrb*W DWO풤pSwXڽI[#6*%fW#6'1fޮ2_+Z*l9eZ[$ogӢz?)2+V FڽwYda$QfmzZR3[kͦD'054ԐE4#r_5N}ýܻ|v_qv.7Sl>yϊn-;s}7^ܬ*B\tzFq[4zDvv~\/%OU寯SHm(ZWccS}Uq[S-Gׇ^KE'źE&2mdvTռT%-Es̋  NJ)N$~z?vo͇n3;s;ne1U/Lcv\n{%kTbp_鰣ę´_{+TcZܽ^:xkqOO'Y61e;#`(75%MAUEO5u+1td&Wq$c d(M-(缪fqOZGc-_Q.||kw:wiv]]*~3G&G1G}/_Ÿg۪䞪oÚP}7v =)JZMM:fc1-6o韰LħebTWAHjl:)&ϖ ~<TK3K*JF]uL95F&Q[I[?O-.)Rid& #Y#A+ei$ Mz&Y*0]5d Ǫ9\iX@ꟍ~^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~oɆ4DtoZU~ֿOR}G|kW=|f.k,*ξzHYVuX/[fb{yDşѵdڢ|4QEKOWUt3PV_ r7,5xJ|fw]fJ1;}e3UuV,,0SbY#Ge•WJqk_oG;h6珫z--^ᚗe~H&VGIe,mOh>ih{_ߴiWk˥̝eݹ `_eG)5A]>4 F2Ķ"rս]T9?Kѝ5O _>{93tj#4 #[tY䕅8w H& mkmz~@mЈ(29[*y\4bs7j^}[Ӯ]w67mͶ)0رJm& :kMV^!M]3rHbC.<T`> MI(ptR"}=T3L$(̟oP:*ڽDMuz 2ޣWs6nl_SgpE̓6j=WNt^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~ߙ #o{t7*U~R@U:!ɷd]mKML`c 8I$W)0Wpd%>] jm`#,ŊhJH` w|jUt 0mnVzzb]I v7o2Yɜ2Y8ZdT%,/+ >Gʀ8txjP73xI;w>Nۥ<;QšdgJ9ayRO{L=GZc\'Y}GNcLvK~lX6):;s#.;^^IB tT}H7~Έoђua-qv>K7_2y*09:JO9豦*[oهldϢkAۙ_B6v 9/66:XdAH5SaA 'W&jz7׏'M['md׬&v-AQÛTۃg]Uܨ<]mDymһ~!뜅Qd!l"Zzu$B zguTS/~ޔ;N9=(lPck~W̽eDOA9F.fV4ʾ@o}OT`0ϦZ6lDu)u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~_J[ /ۆO:v&q_P:-uXL|^be'C⁖X+5y2bW6]__o?^B:Em$KFT <b-[Ma`\? 8#+!2{:Z';{tVm퉊'׽%I6{yqzɳz j֕h*290xh:z'"q_vXJ=Ѻw$ 4yJvbB~5nOokf]ߓcx5mtǁ[_iUܟruI)_ U{R5Yp7*)ד}`_NZ'`[[%NEqr"n齷: YٝuQRv.:=\(fUG_8=79oD::7}faҏ3f]A[Jx;([uUxՁ18(C%D]S~[M2. n}GQTG"ŧ,ԩ Gu0eY/W omO]4]]%.\~[rl6ģ6&T2;Kom؈v^<~㬑#xytO~8b1G'~>3dоgs[|b%a$x}0!T\:H㖳we_mKf%mn"-S-=4_oWxĀy=l y%`E0M%VuGMҤQhwY(X[N$'&uDIGK-.}SVB KͿ/Oϴ?׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~oHZޅzh]`1`U:wb))q֌|Hb>\GCJX^u>}Z!>]aN/R2hiR**Wy "X2atJZ ubMyD5#+S[$ĩ?:R9BBKpz;dd뽤2CYoWRM,T2W%)[H$~Q]㔣5{SzzSVoif(7z/ޝsټ8eln}UGPn5J=*msi= >M`q`YaA3UL5Fc )134y* zG=${3t!gFv}[k}ɳ7d;SwWVm"arObx|.ļSG7nWn]X٫*n[|equTP$UT‡D,i*vqb'W\P q}Hü5rgBI HUNHkki*}PJ[rdTu 6VTmj\}Ihr[6z <SKC;+a?5V }J\}z9߿)hg,iϳz \di`=5S66#&S0ӹ!$Q:zn|8=&Qfrj~j3[[owSUS ߱l~9sʑ3wq]CEd4zէ[{g~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺZ;/[royCSa)]mfm3@ivJ;KETq;]\iC[D&Muz`TH/Xm{|_N-cE>?ՙUJ=^/ymZ\Еg6kT#Ji8Ֆa2O * 5uH!F3MҌb@d5t4⚝R)0NV7hFNr:Nf#OU:gum<2bs$s<b,A9)fUO}:ר鸻i//:T)RZ?*h:^DSר:,WhGW y%hhq.zYC?f u͏SVgcS^a<(Dιרkv{,tjGOg$x&FxZ$Gޝz&~ç)0Beh)Fr#MSYDTV/'RHB,orުz3n,dq3v)ⲴRuyeYBta(ٹ_^NݯsOM/HvKpfMS$t9RIIb@;z#z_+˼&/^vzm0/%)͟ 驚$! jN֨I6u/n؟Ko hM/mǒdI2ۓqgBcR4dMOV]{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^DX .<A=u^4pl/S%k3u{pÚjl9<ϫTgi&\lVC$zYl:H>Jӫv\[𳥷- jd呝#xwisYd? @P~^Sno)+]i}ev&V7ߵ7^AV_<|f% nx1"=f@7Z7&WulйDEz>hI,H wȖBmGI<5&]8UM"`wD k}BOԁoko^A'+kٝK1b~y556W[~8&zR*QTccXSei:x<8M׿.xNW7~Yil|w+XYR1M)V%tHM׺_+HbJ^I`zIY\aTC'5 _ڛׯt)`? |}`(f}UgkI$ =?~Pml~${FLf!Uҵ5+ Un z\89%E)tEXtCreation TimeMo 29 Nov 2021 11:04:05 CET\O%_ IDATx{|}d3l9#9yC/PH*0*Ӊ;8$䬤B9!sl[vz>{<]^{}sI؄!I*!S$EWI1Ifm& ʹ׫QK j04W-٬ثW?$$}X3HVJ'$"`0A*b:qpC0fMUTU˗ III2L9 O&IIIIOH0x-Zbd-hq(V+WXl+W ŋ^G%Yd+Q%(hPիN9(bccT4XRqK[dtpt, 6܍fJ򲤽I6SJo 6T\%m-M9L&Ow"TX%m-R(H L*`$EdTNZ,NAqGMPIv4ex/2٨W%]HHW􉗳!Ug>&uLC8VF[5daoLgɤ\m,b,!뱇k˛@x%X`Kg~7g퉯U4Ѥ~>7n'}Ug"`;w6?f f0J6-ֵe47J5Yy%hEە$dH.I 'eh6)~jy<;QrtRI "NЕHKj➤]emd[2n΍USWyު&^U^|DQ&ɜݓCzwOFiP54/_NZAKuʶJ6JU Sv\{J ܦl:dI?rYfIIhSNAnn2djPNrtkf%%% Ddf,3REF"oӗH˅$'))'i?d|#ft r.;;o~<$oE$llZvlh9l9Hͤ>S)Ru"y9:9eljz4R綥3-;MAJe%W3῍ 5t3RҤ/%:)$FL'k/TL[xYRjFӿk֩^5-'I);X*?}_rCr6HM~o$ZM % <Ŧ1nMLgr˚]wRIL2 3W+Ox]b+v(.&qMO${],dۦ,-n#=PZfG?Hoo@e,iU.so,iGv( ; :dS>B٠/ۖLE w.1Iݼ=I>KD&c>vQKDGHܑ J8p=bԹ$n( YRDQI[?"p#X$`%lHV"X$`%GKF]d8;I_g@+ZE( X$`%l?f>o?!Wr~3z5:iX >w|H]Wc#^)Z~J[Ȝ؉YV>(ݑ>I:ݩVI7)$ۭʹI6^%Uћ~7C),]QKG8v[%Tv u }Kj"2T)P% 6Z:_}v pU_)MÔ?Q.!Byn=;7֨ey6 \,Ѻ!76-Lt%VUMj_ 5xU}u4^R6 =Ii*hS9lFpb&>R5ʖTIJmI( 直GSQ (-Ի?WӥJ.;P%XF\O~%TR}uyyN/U[\~3.mTuxu(%P:MIw+3DW(%a+f &Z?1[YeJʷdyj&Ky3ȞfVJrSRRdSf]5ubGViRiB ]!sþPobkzI9H2[! 7NnrnzA7JHW|? 2wk:sY cLXgߍ>oM^|N_H_IuteW rzQ1{7t:UQ"3_,k@ GIu#nV:Zo6in^OɯM;ռeTSt|T}w%ҝ9B r(;x8mViϑd*"*UU-]?w}{=7j;JRs=pB^/OvRh9 UnG+vQ]7}AQuNMj֤A5UƘ=}KSsWIz@ݪ|ST,ͽĝnn}7;M|9e޷KjӲ%hDzays @fdsiWSH3 N*VI>-Ov0^17(m)9QΛbvO4tWxI"CV]]SC)Z=u,)1C 2W{]M|oʘ̗g& x9{8JJ!%~,;hT U*a$m,jpP:u埮[6TH2:ed6gy͎I6 dY+f5-T*eQRϦ1d*{]F9W⦕:xGzgpo񄺖zD/]+F#ZHjVfnU&p)oj6 4,squ.)Yr*Yoެc5T5G%_Rj՗Us"bpS}4qoFQ*I?ժtYLmJ;YUHU/妹s8(S'TŝE'ٌ^"w7P~ztHOk7kx`ܑ3W-Jr $sqپM)AS-3m.Irn8@.#z|#åu(~=ӥ$ɥUEM}YǜjJ |ᙃ*LJ)NQĩ|vҪRC_U9w)Ŧùz-UQ>@e{U܉~eImufB=u,cr Z- _ʽj%A.$k:AnJPP/h߯erUsjabvQ=.U9}"Ym|~eI2s5ঈfUIjօ5x\JV _e%=.]ݮ: lPVrx-Zٵ}-BC "( ؉ Ϟ=k8;I@{1,NL&{1x&`%l,.i6p`%`%l,.i ٶK{=a qwssw;]vm{nmk׮Ӑ!C4dyzz9IM&l=>7KJJݻpB\RW\ɲm…ձcGW50L_4w\mڴI2L5SV3hEY'[!KknXgA!vܩ޽{%h"5l̈#4w\%''Kɓ'ϱcjJLL}V^paf%^XҎr7'񈉉ɓYb%K6&IW^!Chٲe6!11QK,h"Y&ؤ 3gh…8p-[fqw8_^[k׮չs~з~a=e˖%$)999mU[N-\Ц󼠯"s"&&F&L$J/7+wz\NԩS M$XBO?RRR$I˗WfԡC5h@vvܩZ[lщ'_)44TԣG\hKLLTXXíN~f8p@C 5rH aUe~`|'Lp%&&uٳ|||4|洶fYWuy-YD7n^zI ٔ%%"$ۮ]Ըqcdxm#E2d$fKeٽ{z-`0QF3f4h!QvکUVڵkƍ]vd2i P-rd[zqqq6m*V=zحt$?p%$$H֯_Qf9m[z֭$ͯxM:UW6mkϞ=5{lNNNիݎ=lWjܸq{_FVFѣGώGZmĉidkTT,X'NH5jɓ'vY䤦Mj9rvڥ'OO?U5cyJҾ}r`kР ø8+((H<@(1%x-ZԮ03Z"gϞ|X[nULL<<<ԼysU\YDzFV `\%RKDґz;uZf盾tdΝ;^wYMggguQ .$ر#G1DDDX_Æ `;u-ZL9{vyW%,+B Tx[yyyiΜ9_e0={gUtttZ[ ___œ+Vӑ#GB r:ty?_5Xebcc`ٶI]Ae/Đ?uuĉ3}ѹN9sFAAAYӕ+Wd0t9Y}(-[VfYpU?ڲe-]\\ J_"tҒG޽{3ѲeK^ڪx&N3g4lӤS^̕Cv5**JGպu4|EEEٳgcǎf 80CGW۶mUR%-Z4DZ EjΜ9СCOm>ݽlWss={H9H}FRfR߷sҋ$9::(&]_e?k>9r$ڵSxx-'۷o7x#-v駟nѣV"]_%dQj͖ \$K/';3!&IM4mziӦEFFV>}/*O>$- IDATaÆaÆK.?UXF2}$)ؐWUW\QJJjuW.\gOEFF޲Ai k֬gj޽ VXXXlcǎirUu2IYJntY)s%j֬#FJ*6?vz˗/@y{{gon2j裏rݻ^O6M+W֟`I9+ (rdKNN%'aҗ9P,{yjժڳgL&ݫf͚YGBjl$) @ ,Њ+Եk }f-<==ӒUˏOmkD%1o߾]f͚>@A Ѐ8,a4Nӧof2^e88ɖي={QFJNN:儭k㉉ѬYn[r/͠Ar}Y={HV^F~7ߤެY3c-VΜ9aݻ/kرTsϥÇod` 4HmN+3hР\%,\r_V^S%}]詧YcNsN]x6GQdd//^̴4`Pbl8͙3'^A [FKMXXܬč-Kެe˖*R$iƍڷoUO$Ijժ1TT)}ƍw %"sڷ<==5hР>cڹ^YfI E.\ailll/f&<<\ ,РAnY }vݻ7}ݫ۷gk i;wԖ-[$InnnҥM|+ĹW_ʨW^yΑIkVΝtR;vLǏפITFl=p|M?~\MnnnVj֭JIRBBVZgO?Zȟ9~Fڴicu<7LNNJfYrJxﷴ=tMc4i-ZwJLL$]tIԜ9sTn]H̿{j...Z`|AƘz ԩS2j۶ׯoFVٳG]|9%"BѢEշo_/_^fY[nըQgϞ,)))ڻwFm۶m߿ƍSNYCF{ѨQ7hȑ%$TRjذUN6OO-[C_ .WjϞ=oyf;v,C&I[lѣ5qD[[``BBBhѢ,{n"FQ!!! 8zxx(444?{\N<ϫE/2׼yf>K,QBB-/]5kdBB,Yݻ[_v֮]HI}ݗZ ̕ jYRRR-qeg"NfgEț9::k׮'uq}$pydv^fYVRLL&Mjժe{|OOO 0@ׁ2mϩF0`Rґ:'''UVM#GTvTzu={6~uш#r}ˍM6_UZlV^2d-jӛ6m#u*w^999gM-KvY3fPϞ=.ٜc22Oc6?護޲صj?/WW\j 䤺u_f͚%WWWM:UǏ5uT9;;k֬Y駟NNDD-%knxNNNV%o-uܹՓ$kҤI4iLuF ,W+RKD`8*UӦMsN矺v * 5lP[d2ed2￷FQ]t_ӧ+...GaÆsη< S|y5JqqqzuI[NO?qIVr-[!!!8p̙$W^3}Z|rG }LYqh=K06ّ/KkY `IZhQKfUOyάuz}n]t`~|*7|]lK?/ m׵dER#ŇS,npU*못: yjEhQO$I'ƚSd/T $Y/rY.[Y[tՠO WI[b 5 M~wP`*̷u s6B hr13%JD[J=v-͊sJTڿiP^9,n5)bӪYJ,TZ}_B7Zr MUC hGn!KIնHzFMDXSr/ՠ9{u%OƙQ*U,%߹r~Mjsr$-EЯ{>w\%kv;ߘn7J=􄞛TWe=t~8zYf{h/jg/"IQ:uMw\ &hy#lSƘZz]]2Q;7V_Xev+կFaIRʁյGuAqTy_)$/}5n:vb]T,V$mӤ##:yT>81Df^X[~;b~MT}%ЪOp_CO^SXXi[^n'^z[o@^>>T_{tР7V G]=}@Qy-nM|QQ)4|kj隤ȓڷj?~Nw3h0Nv8V*zci:A}2{7ڲ=~m %򅿴 A})ioz@j pӹ?O/џ#ę?9VB ͗ߵmW)C]jXwрnզmg4V3=7n'XIOTruz~v=ʤ_TV T#K(vQ3TrLit*Q_L)2)ve-U:n˱}R&Q̮Si~SOB^$͟Jٙ`ըx}`>IKhp (!OhJg}}z$-4FsR^UFu~YiͤM/j@'$ɬ?StM[Бuב_QFUU_k4Rrl6[uk:*e;,lѧTο6饉X]+DO,{ȵ#Z1 u}cu ZvdϕleXި7-C9כuDZW쮹z}ғ^l>^z3:\/u .$bMꇟv<=VIj[c?r6ܕ?gcO1Y\.i2+ _֐"eg^[^hg_z gN#кOYcT67-y;L={hŚUIخfx:W%nܜ4|9 9 e |jvѰ{b.1/ihJr0֒]5b7bƕwQ̩k*SALci^-Y\ZM׈f޷&]طN[ ԈoWLwH3ǔj:|>7]u*ev(ԹyzbAOn]/K.1֊#Eu nuK_i-TA1't(N9CGʱz=v8|^z"Zw7ՠ .mUZ?mRڵn!gI-kgAfSU.}8TvjRDRگu8FZ>]0_wAR]N}R q^eոM3-ij׳Y'>(M}Ҵ'؅.,D^ ?R'{譋iŪVLz]CrQWUG6JKٰS?g=#p}и QdV\O筿ɭL}t鐾|g^J_N6NxVoh7?މ8rR3Uslwk*f.+јn0ѷ~[9jy}xIgP;=?{D'R8xA)o6x *bڪU~QȻ$)-yF^CW$}L%5{f+.3a:^UwERJCo区gL~E{kѳGz]Ӈ;稓AY=b:Ջ?Q35.]>ը[yzT,( ;&٪(t::V4VU[wU>O[HsiՌ%loGuAR ]][/ܤKzFUTw+S~sMdQ!jR(5V?$moHn$s II1/(PF2\J9#ǯɡJm$p}u|;Q=K$5S8m;O3{V{ǟj܃n\nN):cj< |~yZpQUlr}$=RsՕ$j8ےCBB BUz]A`" MA4A={o) $Hݙ $Axsywݹ[dBw-ƅJѰ7iE ;fږ~hTr;ܡ! ^ 膟gC-n_1*;O_QUE&7-JfO&\Zgt8Z%`H,tiiKS v޺Bt,Yb)0q\Q})b(dMt%LJUkχߛmx NNQ'ԿMYJ_S+416")o,Ǵ xשK:c&ڗp&q͠=ջ;*9ݩ:ۙ忔q-՚Ѥ-PZv\Fug8Ū DǠuF(nUR_3v4hhH&?ƔP7ϢJ/NⓆATVTz IDAT|>{\ZYErp8Zes:dbÃ83-SܴtmF0A ߞ֡=ݳ5͟=O!rw.SvekrnPˍF=6 }7BڤU.4T׀:EPӝY5}:|iyַ_ɡF:KdGNcN0c#;8LpҜ !B!B<\9z˲})/3w@cjwp9HO.ꉧ'E|[[aMc)Qc㕓NR؞dO겛䃟=r>iSCOVhH=ׇ2+[n =1P%;4nGo4FŲؐ$&'*:I* jgs czA4O6qjTâKer31lPxuz :٘*Mrsӯ2$|4מ""̪*mDDZ 2L K~'9ӍGd G٥sk3lwgM=fzPL"K TTU;q N.ΜLߚֳ s_dNXAt+L%OASW>"&S~}?( )c:ϩ GB4\br)ӓhE-%fxωlԇҷV0u-Z]GA'8@`<5Dr3f53m~H?ӲI/QsskF>.SEJPV|0 >͂!U0#&|qڱ%oK}pKS{w(vDmQg~{60ڈ]%RԺe9ٟ atӈ H;r˴ Os][uҜm^k>s1gX}/ZծB2wהD3΍nZEyZׂEdt6cy$)Bi)~GN!B!"ru _ħ㖰{Hbȸ=$ެ6m;w3uٵgOih -=;tz= Уg6mLybuh6åW΃,0]:_zO=rC+VL)=0aST~{->+A4)*IS~T[cI1DDDYAԌbpO5Z-: soC3ve1#L>/Je/;~uZ&DWΥ+PFSR7.I~xЬ|Tk%?}/Ѣ#נRWzWO]y|uǻcbv^/>GBP)f|<2'~'ѧ ;1jѶ Ce&=~_tU䋬~_Zu høv][Z0Y ~&*~*|;XOxOǫ~uCw#K&G);uJ+|JŰN 'ܺ&O+[1Z@jv1# y?cnM+ȅuwoSַ|G"5AmB|H- ee> {ڎ msM"}/=iT<H ;Ś܂zO66}+u7Q3EQ%R=Q;2mMBux ܢ~_Zɤ/Spo)P;o"B!BmvHM9gfH$\KTJMGr;&.'VICwueh]+/-hY.p l?L3? Xp\ sj4=,m?ܼVݗ#{R8+Bg /etعrWѸc$];Ϳ?5nȖ?1L*Dww2&OŻbZcW{p ǎfքȀ4b^ӴdzԌw7&Z/զOlj$IZk܋zT솴^.b~%%c_6oN^ŷ] ոք}7{ Z Qv39I5ڰ 4F'#]F6kJ3tǔ4?&ƅrz,BA}o?fsk?6ϚWŭBOO䧡wހI0)7b^EWz(>c͏ >_!{Ofw1q6m cMOq_5W޽ $ sec/7a%;e y4>y;@y7Y_4)c32|`%VcXx; sbT>ȿe9_jDQ|:gOgWKX X+L3Yd8Fڑ5Y7Nũa,V@<[ɲ`8PDj(,cuFp{}Cc:4U2Ofecgi=~83+,[1&<(W/g|Abs#C+׬hG}1rpM9/i6_?> )昇aV8߾d31hSuB!B!ٗݻ'uo~jժ)2A(ܦt:uW"yS!TȐk_{صk;wV^M&Msy/%/z ~Am_U3</zKRٟH~^}|H}>_ϼ%Θ>;a,wwr4mEQPUUU[Q}a_0:&B!B!B!^,itSUV~ y~h44T!򂴻{b*zb$r}|9 *|$yK1'1}v!q9ϗ 5 !B!B!BC$ DA 򃴻{b*/(!`7!@ɧSPsuh_MѦ.wU1^z4q}R|\"|fIWD^}=Fx-w$6Ӝwajy.Tz 1|I\sY^\dj q5]VOׯGUUڷoEyaɓlB!+xO*Wµ ؑ~!+7fЗ?3ii? -<[Ɏau)R fOj=&| GH8q-ڌ_:TowŸ!' GƪTuw\N4ts!^CJb$Os%Zܫ.iZ[`Y+K䄗kyxO>ܛ-Tq!r3^fGzȨi8oߢ?ߘ׭_ya [{?bgaEFM F6<|獍`]|Z {y%,m#V:W=:ͪ 1O_QiUSv%~gS7inw/Kk; F>|(ϸ@UL΁vq[ Gƽx%}h%gϕ\$/Qba \,u: b@b}?W 3*E֣9M{x'sݔ>% k0}MrүѲsُZLj\S?KpڞJSYO}->Srrޕb x1lJ:L=`m=omQobŒ6PKC:zW=΅JRQ_͹גʽս)`iuE躔۬㉕u=hwG/nM/2Ȼ(VXZXbe[Q/uLjzsWoЫ.6(B:yeKi秴l!'--ޥ0kパ9 wOWP#,^ gu}KbmZ9eı?߱\(vVXZZbm{*4~{(w_')x8'5+ٚw\%e4\;=:OjyeNۉAq'#g}G:hk?%l;y1Mu+k[݊Q~g>(_fGmdՏ|uX~@[^"Ut((&UAMCi7 ̚JJR>:aǒw)e+ݝEVo.9_uVF^O2ԟ5iژEE)4՚Ybe{z+4ƴNMkDZ`!!:,޵^ Mqj|>M90eE14:m7ith5`q/qv|o*&NoV\;5;~$sbӟزUC8e=΅ :u?Tʊ3yfժqUc_QGR@%;cFCGD6lʷqƒxhÄ, ,FF#ԂşB߱3=9F4έ5=Jh5j/?vo)kӜ":ȲMWTU!V07"QtV[Grv͗ٲ;\N_;ZNCM?iѺR(ZgZwk#*p}$<pt}F?g[$?Sl%ph>wq%6,nahu]B IDAT\=5>ذZGtAՠ#FSN_+]_GWoeSAx>x>>k̍6kʜ[Z}ϝ{}y#Ø]-fvN}f3ΜYwNxb#W}S?>vAvu^h/tZ~ȸ8⸳'3Bb#jo;ԵʮM}xUڑV3qgH6A9ς8QzݾeGsz׊N:o@c_ 6~A{ O)זq#o9ag{ZtzSUBu([; xR~]j#n}v : _~7 >JbF;??%'9aТr'2v k}ә}"^8;S0ǥ-YE:ޔpw xgF؜JN8(w,<{xvMO?s=+`pFE] nkP{'*6|Kj)lCAWkˠ?O3~ xRy<|ў\w(G*4fi٢VVY%ghηt Bei1E*7}~>s!!\:'Q\*s<ϖ]6eE?ʝBOrUV.d[t%q\*w_7!3.q͘-?l}Q9y5wot|3::$0wʻ;R?cvYmټ>P_id~`;ѻA9 9VfƹS_l9fȖKn\-m$'S`$ƾ?J7ƮcbQ]:[0d5\K\{ z(3g2z|Vh_.No@>ݿPnGKu$f'S#^@B bJ{-;}>w]`=4$ˏfq9R*q{`2Ql4J krRT-]%8ZSXg"|׏53{G݈`\PU0oȔTNdξ7,`ơ%DݐSl^9}c'jr6VlO)4izҢ7fJ0!KqMz>j PN_fUn 1w9RКu`Ȏ]0*+{{ňƬm["o[_ $ʄk,B8;ZɤI)21V K Δ(bw{ܖT dUW:עxZ%rn ^y#Qcg$R",j!,ׄTSIo;…h\#;f零/ [qX;|VJ]I}>܏9;)5 M<4DnCsz`&``83?zNSmpMU"MV8A3!rPZwGnbVIWطK9KFUёi 6ƕܹzvl`חa^_(Ҷ 0enV`E"5t6#f luӃ}n)u=sxN|/pdM'sodv)1}c {65^ 95M)uԤ0B#DnVębS/~<4L| 3dTiC5px(&Z4Řuʨn)>&:G6z'\l:/]A$XC5,Fs~<+pCz?u_Lւ'p+N%g]Ɛ6ߨںSlYʖ-M]s/UWwf,pǵ\sϝ2z0]cDg3g;3֣PBxqh^Mj~e?"8ٚf^&Pv%(^\!3.9t۩G3wp1Ξ2+RFl̠Q,e˖}c;%~7ݵ(2D&ipm7I<%r.1㦔+hҭWc<4)Lf}`̪$[`mF?Vy[!:;S٩L^Wn^?x(xx]ʙx h V.)[,eK94u1*V$qnbODZx1xwԶI3N.ˑ{*ZtUh=ZӒrmGg]6$g՘迂Pd*UTȓkZcgz˔t)uz#Y49vx%dDUDAS!w M^ME)kѸ]( 7=~T5CvH:>zj>D| G)w競ƕҵߠo2*SM(1‘=gKs4hZa-UtxtJ딿GoKFzo1fEc='hțMEt7OWcGg,SjIEG ~v/1lK[˸-dUB g[c:JjŖg k}/ad[D/laoLM'ٶolLdnt痣)4^{3n/̋Sjq,cqf$ H.v_W|&~eڇXdЃs7vOFMfMUuh^D3SvTF4uBmQ|62?/ŵ9_F26T`.*|O'1& j"1&$³nl,6ӰU-,5 Ǐrɠ| TLG8r˄}/E>~#[׿Y#T86$gB_NՒlF/#*EVEѩNndavp̻֨ - +Ư:.s-Pcۄ 5F)҃Eq9?7p6LL܉ ܏T?fL3/&>Lvak?u]M,H&`^ż }/pd 4q˂Ka\+4J4{!><H>MP\Q<&\APwUSOM$nN1GIpl&֍˲N]i==k?AWnz~47/ i7Nܩ9t)!\>!tc8=lN.qaJTӣmUͶHLQԱ` ȺK hvcɫ\!$hmmA 1c޻(Z'Z.NgXrCG~S5R,S7ˉMĵkQ Sql5s7Aȟ+tyDr<΃0<#}=IY8ey.\`;EЩBp5ռ"V'J!ȝcb˜|;{5w1*h, LR?.ĔדY0b~ \[+]ἦ1k4 !ú)}.hNx-o~Db0f' >a rYՀiIu-c6vSO>YV'=-sAbJS&=?]__|{էϬg4Q)|^3*|2/kXdh. UiL}|q ̅SZg"thff^?ZcVy2~-LyP[9 kף0^R@"FnF$D\HC֩=́39hIQZmnJ=7?aeȺ\)뷦fʘ[5J>j\ɷp`/d6${"[Xau:n,ԔrŽi +gT]:tшʙSGW*@Ϧvty'.Mݥ!!c}aOԇ5Mjޔ/QRo%Ф{ŨǿJt^2u~f$$$7ct/CǮ OM@N~g-h,0dAgYpncHͥoa3$XV18'k,I}:Z|A}=k7潼۳W>q ~?@|G5^aӿI|cl櫜߻y|,cGra?b}-]T3ƽgpA_'𖻒eܞl.rjz; Q;׳+*{0r[Qw @q˷b|qK^50C!/ Q{s *Waʍy1u ,j7?~<jUU?ã$*U٨#$< UUyTzaF' QG ;S>[RϭBnoT̝TA =ĄHnLHli,R۞: t 8z΀&pZP]V+`ƕ`#p"^EcِwWM}rW9޳zר%TSaDO>换KT(ؖۢ1uqogW mVO)RHĭh fVB[i˭b&Ǐ`BATtً&cL8sɨZgZؓҖgR}>QO|;׬P"na2p"0ޛOWUTu6&MoEGW<[3lC(&^}uyxU;ҽZ5GNHЩq;t{sSuACϱx}ִ*0J˻>_nBA%.6.٨'cF5vpөzʭ <x]@IٗITM~9p$lzOj#O̾SܟWӷ37%Dw9]owe#"[${hmOզZEUUըT$޳ "zx|3~ zcWVUnҷc9,Eqy]dYFQ}[9z;Vכ4vێؖdBBuYsQ&fQF 9WhO?_)e;Gf b XFmZ$#u1.ʤKX-tCH6+\w wo ɂ&L;ि鮳t(Vǩ'F.^{xlNt*I0uBҨHʍfRJe+e$xe)9P* mo| q騩$%קc3!Wm'O ){GѲ٫cΰcHyRL Re(H+Yi!?ɪm!葹&ڵ/]L ˆ iP'NA9Jf3}&~fڍtKP+ 2yɏ72vlsTʪ|ORIYI;L/dMe[̖O^e}QP30a(6uR?,NJr@ wG9Ԋ.=JO#nW~/ӪؽT7m:Z#%bq&?.ERWH\ 4ԲoS?c̶]͛;8s۵eYRɻZ39eLJcs:N\_:4#Jn=}鐎݌kROc2ޔFǭsI vW>=H*,k؟FwJ41挅bYr=N(xaZ˾QF;IBcg2Toޓh@V4fQ>u:l8n]n&FVݶ=e]22kͨ>i>,%j#4FXڻ]kE;$_Spom{D} ūmlUG d &i}{*Gtr/z(-4Djei #&j&e[A߳`b^_NB8} n% 4IYI 9k'|sN3 [$'ur久:6p/,3Υ/Πoti3_=cG+.Oh*%/CmߊC̨k$;} 3ĉHlS2dYgZ+ V2=֍<}_ƦF:z8l .RE:&o⋿ 8p8q" gR,酻JB9Ė2ڸ8r8)@~‰THNKrįJt\ۼ]VƆmQƽi_D,:O(;H%#7rOI~EˁpEfeMM#ݲT??D +ZP:Ѻ[=ө{l,?ىԗ[ְ;BF2ī#hm`mp4eTHP\ ע*IC+.Y0u @o⋟ |NĽ܌AOԮ kR zw4v˲!aXȬ 9c(>*;}CT 1W8w=.y ݬц e>|WdSѴ?FLtЗ|0r.? ,"ǷϱoҪdGNdkBF6(r _qϵȲW,9P %:o[C`92& 2VYIO?|RԿHsSBb=0~):ʜ&VH~X B $t9p#N7&v(BU-ќ;xGEV-(`2ѳ䮵l9U5ceeem\(W6٨\j}3G{?;_(x:YFy]dY&>`e Z=i@և,\3s;qDXݏG1fCl_őqR뵖w^2ƿ{Ww0W< <^Fֿ:.iS߻}Z쏛fbxF([6sI4:YFi怓U`bd8zMl<mRo WѢAQ+5ɞ_ n-K'yg`7t3(ЇɛrlA-)X[gfP|7YB珚`LF3ĜaA'?Jz^0E}<3".5oG`,,Fde|MH6E.nx,Ky6 U-2'%KzxIb$ ~x*'&AE]-vJY3Ϥ;TFE[f\1?D|־2Ŋ^5c7]'S|arC䝣tm7J]N9u;b`bdq$^1K{lu?|I;d1*gc,͐B):xI+lNIYFMˡRx8)<2%JQX)gLhPV:1~[H ތBҼpvC%<6΄qw&j-[(]pPzעFa%ğz>. L % _LQ?sBî|"F+ cY#8|8-*ySXqӺt'׎;ԣť`[3Bk 6>H]Ff,:ߢvؗaܡ3{GA"bږ*IIlG#Z`CV3O'iTs6r򐗻/v.Jݿ"}%KE!SNL$=O~GYxcL(L^_'c3`JOf?%UwExt*}A~ר.p,Kݟٵa] kZMFae#N[ʃbLZUY԰B!`JxlKΒwr%Bz{PTuD#ddٜzCFөA}I2ƘO*MYNȸ~'/r-#EK7`#Fo9C|^M"= bom~yMz/i ӄ nNc_#x^ڨ^J-W~Sq)ӎ6 dYŁlCC#? LuvCJNJJΝqyL$=N㻠1Y9i:etA :4fiʟn 7 lhnҊս2U"r(בkz'2?lԯƙN_/d*1eOX LFb۞ޤsGf:.ؖ~.Ys8jkC;,Y*Z_Z-z4fAf)Т*pnޖ*FIHg؏LUIK*S=(8X[[SZ5VJժUR˫rTT+RbE*TI7d݄PJnajWX¢$l1pzյ09ǟӵ ֓]LO8_&ҹvX>}ދJȀ™.1ke[|޵(t˜VGW#"$+T~RX*5]PȪ2bD>D22U@2buc;&q~ыXK!(ꏟ٦. Bgo@1bd@߆Φh#Pq&yS?O]b S,cV0`ƭאN&?{ #\MYl[;vcIiq*mhhg tGJdž4+ohY dd-ȯwվHԞg#pFIX&a˄ڋAk}xHDEIyէ[Y;pTʗa˗2NF{cʞ8Yu2jsGH;ٽl]RR3U[ҭv1Rq>KZA]؏f#~bΤqrhڤaDjЯli3s)cZhKhHfH#승`]1&6Eޚq`Z58arse&@Ӈ6fB}CQ2IMOdDdGeATVu-FBCɋ0y^]ZRΖ)AVcA瞕c (ѥQQ:P[\6&0֞/ih7%@${^BS=MvԷT ǞdK9 h݋+7Xz[SmjHBWbj".dZG*e> r0"DS (J;E|ڵ 5֠ATUj:G]eTPn^H7gۧ3+ꙸY捌d2DDcYw4آv!hY=n63%9ԶgOS͍"VGܻ{F8xT,Hd礬MntZcIPf2M@KUʍ]`ZqA?D;նچ modݻwSR\ /cttJ!cv{t#^ؒJパO!Ϗ7Bxxxو5iOLjlkdSd@ӡT*?8rŪ-?-Tp!oʚgt_ws՗ܣ5x+2z'k n S9Eg"fy?>jM_QVzhLbP1ҡbK2 >7-ڷ^ BG,)+;<ժU{9yt"-]z+ iР[Kw^i&:vXd5k@Ӧ -G$<EL~d^ sh#i(Q:sMsۂ B @ X&"w tlZ wƜo1P!y$Fv坐ѻb'^wߟ)sO?8r~HxP*ǿ1]qO#kt0]ƑS}-89C_wCF9""/o?SDy.Bo7.oEUq9 ]|k$ IDAT@m/:a߸Lez7f!8 =$[``ی>_÷{л(>}>H@}(E">B6#d7f!]F }$[6mؼy3:tx7:t*Ud3D9AvC_AF""C'Dy.Bo!}L_!üA5m'C_@ @ @ d@ @ @ [demF/@ @ @ M @ @ @ &bM @ @ @ &H@ @ @  l@ @ @ A6;r ݉ƿM"sYq[M#`in9Vվ&(k!9'}JW~[hhcȗF^"@ ee-zBN,  B qDXxAs?3vƘ՜ `H^&5;q\2n)ngM {La4w"4+cÏuǡtb`ũ,_Ӥ]Gj|ˍӓS&f4kN̕ul䍽 + B "O-lL1qey[9kۚ`TnܗټnG%q+f&>V)(* ځ.VL%E'>l`n_T84Tqs!w@s"5ô-WHj^͌: dzvO lYfO~صvoC0gr`|݉7sXKw+uMtUL`|g:ѡ< 7음 X w.v>n҇V$*8u;ئ:ul`ヘ3뼄=J֞XDN?L]^?y+k)L_qqx:66]V$.ƨp~$fuuPyqL@,ۘ2/M-8i8rd W>\> Š&IyIm,!LTW~<gz›SIxOwa-s(&fICn` P4S:K&2蹹-'\ΔZۦg+p< 5̀۲?uͱ831c[8[?tA!?Fu_fH[N-ofYrЎg#0t\,h>5f3~I|ݢ1XÏn'6qĿ ø>;LYrLfo;EϻǬB=1!9QS|, BGL Ȍ|̀dF5Y1z懝{L Oɸݑ!5fͦwML5hLpL/!@ok'ّQ;Ń#*jgFmkuzxgoi5xVdaxPS*TRM:0`Zjeà@9Ut0K u6 _Aa^^T T:5jԦA. #+h~k:ОYv4oڊ.'IuUkסN:ԩ凣Rk%j1ܫUƑ{ㆢWMgP4mё~Ƿ}} 3kdBtQx6©ǰVZᗋ \:̓(a< p.]8RԼ tLp*U *'Ke?cLz%ŸVHr,7Pdd6ncEc:9]={1'3{Nڽ)M{Lgʹͪl#ȄAɯc6 G0+^5jRQkzoG]6 c]ȫ⯌jUfڹSN&n &ZÎ>($ I2͚LG-1NU噕1.2($h6nU}-b9:/S㓝@vA'f& 1-DqFRwn.K9ciFc_1lߙ9o.>V]rHLGvc8p g p9>OpP s="$N!(k^0or>BfAfIwdIձMZ]il22w.vmYJ7>xRq Ξ=i95Hi=9wq^ݘOFղ }Sq[onHQƒiPӊ'fKteJ\t({nUk9񺞞žXꎲ#+cי={ aqY"-2eĒUGuV/SCFyڐ?ӳA%6?aHߙ/ 9X&Y}tVKqvVkڋul+_4gez^{/;wb/Za{رr M ʍo;9ytȂ.(xȏ2~'>ټ?EOi)I +{fZ(ldL |4u9g|?υ{~ !tQY͜q\~,cKi_7Oz<Oq$,Bʵ~~\ Uڰ뜹U JlDOra })X;<bKՁ[6\e[3ɇ<]ǧº/KkSRngJ&WL:t}5?,<YДE^C%LKgt]CI(%= ݅YBK֯U-6}Y"="u6 ~iU.J'a[~}VjӱW_}؁*ί$)_;K0nO~__k)$ͧU-އQzE۰`PJ8Wv! =@15qr#8bR^L)Q̤!$<.J)P G4ả)tfU0?G oXNV@^ 9LmN~FG7T2GrWU==l y@lc389H=!Vp$GgԱ0UAZeQI(")&|'O,#&M֪v `ǞϫHWWQ6ܰ#G^ y){o"xi%w*_ǍU8`_\suV&zY?R%G̚@5,[C!m]( Ӂn֝+gMf*&T;e{ ;mv4 fs]Bs'xmӿ`6ʀw/T[P#  gF]ֻIf'3)2vJ>pydۉ~X$a0=tkθM7kkvh[=A6tto6d2i[iڔ|2.7AWz39lG$K7Iȓ;2'/ww)m1661E^X-]ka "2"F/ RA9Wq2sNސ-C EOWWS9y"\%Ր,lam_bXY;SkIcyۮ-h;9C7Erq#"#Y`mB*-P~KS@&&&HˀEd퓋88p ˡ:X !#d&[ɖ"szMOV'Re=Ki<\ݏVc`B/e\^j4eƵԫ]yPd(ai'GAH`>FrpA9!cIf8@>t=/B䀓{'oG P ;+qs2V*dzP`ھ$-$ɌŽ`biYŤSAǀ ? wMÄ>Phۓ%6Cn\ +j 罁G%mgn"t1PZR»8N]E\ RRHaY}(],tjL D0or>BfAoK~音>4$D*JyJ8xnxԓr):2zx+ůBY\Rb-LI$F*PzkB؄bO'0 ݑnaUEF8o2WCђ V"Wn ˶ 9}gNԩS:IU4Xϡ KBK+P[T7~dDN7L'=T,'WOȅK^L h2;VZ0~GFlIE~HXUzԨQxy2<;FͩuLQ*U]9oneM*(55*atm/{o&9d5:)(Tb J2T2 I;Rxq'ui㄄ zS,䉷˫Vg>88bʅHL415AFxnHTwOoo"RLErxt6=s{d,'ӵcq"#ir/OY0sDK@V2IΙLkNޒ- cٽuS˕aypuȷgb 5}+Pڷ,}uS ߒk3(̋]„y17H$YQ"]"1Ί)bn&bj2V~2\@|M~v{A3n +MhFTQCutn~hct?|p(C٢,=ՖqxvR=)̞_a5`Wi3'h*E%Z, 9gV<;b^uÆ~0{1nǂ7^D<žOy'J*kS+xC;Xdlh ܕ=@z)b˧[W~ܳxz[9KаziYw_gv- (-iҪvh`ٜ c˔8U/1vh?zOkƌޟb?g1wx; -!*]JnDQ"H) E)"ғ@z w%H@\vgߙ۝=REޡOtԙ}ݠ8[@G3xv(M{ IDAT!,gV\(G&QK_ZL̀'T} 7Ωt\u>,M(lMֺzb,GXY X\B7ḵ0ReL7ahjc%( IcFus!龡 Vpi]=w]NȽzԘ}*Q{X':{5G.À%:܃ 6)k"e_ܳud'փ+[:%/u!a`W2ջ 䍩oҷmL_pn~<#%?arKw4$pѫ|K[IJ33g0'e ^z|N~R|Ed䣠UhΏ틧lr?+Z9#^pᇡ Fq=}p*?=a֐fLGYe-y`X9MFkpͷj^tJ5xƪ~a$oϹѿEtZR9G5w:NlFSd/zWCc炧OiLjߐφb ]ݒ{eѝB|f~^& 6vx,AVc^Iun8%x״S|GK^K93C^-o/ίE|J9JVހ%ތb׹y7p~?hftv}\4at+@=5o0m8le֙:n2#8xQJF'Un80:CJ/aZQdVQ*q]kʾҋ#':wa=M~!Xo`覸it|;F>5'%KlE;bM4xzsq~`wۿ dm[!AЁc.Z,mFSK}tn?c=ޘFǟju+Whi; v'0ghl94NN*MɄy}UbXOYcΫUi̤P^=iiPoJ@ڮ'uِ8\k֬ P \ЯI;BGd7ɪY=c#fr]J? ï&oʹupM_*41} 3M>/sҫ }<>O&i6E`FK^j:IEX )GcO"L7m)YͩS?qsfΞ-o?ƽeK9} 'bBލByì!q8IoɬڪULuVm۶ʹ>D]|o46kimFjr:#:H><,#caގ^Cϝ{^i eZA"D3sI%{KM/Tu_Ej|XꤖG 7SF)rpz0kH\3NburkJ51>^c|H~f.gxNb$Nb5$'1:۶m$lE !B!B!BM6!B!B!B!2(Go_S!B!B!BDf !B!B!BArM!B!B!B ɝrB!B!B!EѤћlB!B!B!"DDDn:t:]>ITIrt\ 1[n{FnIdS+2|F(7 +'6gGڄ"Pyg 1r8:kQ ʚa2yo񳜝LrtٚR<~ES-zHŶ}z'!!!wRz8[0-9GI 5$f/:GGG(QD^ŋXb9lɦԑByj0j_:/uv3j['2{׮dn8kd_n:Mv4grZ8v ?,ܕB[=E;n2{f^"g[9.z^v-N 4J7oZ'z;O*oXn{y&yQDf$}D#Cpli[@>\ȼ^+{2@bl_#LO"Ŭe2*Q/Vz=*Q1 ق5f kĪx:Vx.+ɠCբs+ ;ɬ+1zQxQ|ykWE1j|n/'>}U-z~hggpPR¿X|0mZڳ8.piN[|mjf]p/7fw{!6Z-:=EyE/^# 2n{ /{0Ɨ'r.k/G.wS G[_l_y"zbڧ#v6h@)gz#N^Tm)A"w3Pm4Ǭv榁Tu$; ׏k2s蘗uHmwJ1o)ˡ;O݅>6,x; џ떥5;Xn lkK4鳀ɾk9Ϗއ·|Sb3SO6ZT:P3v^x/O<\ [H`s4~C~|T4ye1f2[O (!a+ʺbijmuPWy֟M{cYsw).ALX* ֘'dDK}m  ʜ}iۃ no%eLjH^[Kb6.G/9`>4N]Ld`El3=/C6Jt-D3ႜz~1EH~fk!e1 hagAnq,,Gh_t[;;l |G5I 5$fBdX^%JyH QׯWcMjc3ك|p-\5\gm PW.sCOD^'b;ʾ6DKy<2sql[v}G(Ў^, 7 d0q\!1kص7u~sDZrǔ0s ڒCĹ4ŝ|^RKz4熒5AXztM_92s6,U>;:.0&|5dDiOy][qqOښ{~o~`ű3ltg練|s.W\!Z uþW[L];P7QX_n(65&ϥ|QG:)柊"ZqR늟]Dl`pЀ a (0gTQ-|53흋򉕝&2ZGW1m gU3.hrzr!^LE|R2񤘁; +}ѩx;@&4̯)@ :; }s5'=E;bT qEݽɅD395M蚸lOI_ӹ;VO/6m=4އCq{m6UY1h%L7UNCזn⠱ E]=fÅEqwoaǭxlsmX,QGX5u$_Y &9cn귋I~fk!e1Xz= \Kb5$'1" R&)^k֬QSAAAaj^K[e:kNE`zRJ)WiƟAq\+PyEݠ"R|RiTy>XRcT5= ^'4^)-_ Τ%))WgFF;~qLaaa;>SUCu^y//mP= U_W?76*ciCIP;SzǂDѼhT*שf:=2ضV<=T1QS)oBm|mU9}|ntVf]VIK3('IJ9y r]|ͨFcb\utQ}T|*_5j:]fuQƗSG3LhreRj=k&%1)CMIRʢ"f7Sƺj7 S+;jG h"ӑoUj3T)E5*ebL*.>wͧ 'u.CTYR9&Cos*<–Vⷩ~EuRtxjtVF()Gc55A!WJ R;sfcz`:0LU0wV$O׎*YUMU%'S=?־.1zT/w{@Mhtyڪο;ΎϚWR#@uۿGlshw,fP[`RGFUUF8)\dT>ޘTGƪ+ guݢ,.6}}+{gN#1!hd|0iZ.Xz{JI9pq}aZbb-+Ͱ]RNWlm>R8t^B]-Mv%dZ2e-crgBnGIizМ_ٖae"nMYqj !SW/ ޯ֧e7!;`-G'E/eƋ4+=8:q[L$: dUW" O@o =6p36lg*W>%RV,j ]`]c[H`OQÅ: ;˖;W32KlWkv1jSл&tĤV:?ڮ4 {[+,\^ Lߖ6fj +{z|E#uGmKlbb3,kv8G?Pf9} Hb3 I{߷H5ytʟX5-;1}CQZ&oYm_Het<ƼNp<MHt˻/R;qGX$?3eYw5-ɩ݋"tr.A/jEX)ƭ0<~sѩ}/ERC\K\4%)S % Í`*ӭՏeRHԙC  ì!q8Ofx:Tj);3$rvH=@7g'm7R.n¯ATI2۶R# ۦk taڜtC*AwiQ3)㞜 wشOnE3^֣є6ˁ\Y_+CN;ӻ;]{uN؋BO@YX輨t)D\I_nhMXб3:|4nM nG4ؕj-c^pBY g;E_* l?|e4KuԳ´ (1|![&59tizSKR),xz!QEhU*RX*%u,>r˖&OL>EKSɖ8GFR)me#Ta\RJ<*oƍ݁ozb}XNKik ;Q4z-d'օ_R9?.Y/b%30mg6h>S"?PQ9wSw'b a_OF;+{{WM<_!$qMb-c2Nb5$'1Ire2<$bX4JuVGY2/+㏛besE_ =KnݪRBN%3Xڷ&Y -@¶$D\v&L:{w^vlY9:M fţ+-ÛQkzۮ'7LLz Ejc͕1oJ][̀h9 j89K &mcӟ3[j^iϧǝ+bqcB]|lRΩi'#s#JFIg:/#ݨU҃hɧ": IDAT±[=(>:X3n~\ps&-iC@1E&,8`8zÌ#>Yb5$'1"59:-CE&'c?TLllHR2+- Q? oۛKm1RQ|ԙowulN;Q,`uK nwTm6`6g"'Cdkh|(V4e\ /t\f$kWnَ%r9U*7 01?"qiFi)XMx޽ЪxH=OodK$)n?‹IZLh/ >r6օ+Ste6*bqb9m/ʉai%jM#ZUy&e],UK=bni  S\@A-k5Aٿ6]'޿_cvm+ JlKBar4_k~riJ+f:o4|5r+n%1jvWw*/|si Qj4;>oG٠JmgO"%AaM,߅B3I@^o ĤG Z $azUZ*GeiX˙c#U1j8qVXK~{w^YjZMa(FHrGuR]?V7L%=l/S݄KzX@86oqfЪ'`KFZk% 5kբVZԪQY3x6lJ'.]v̴SP?xΥ6jSVU'6HQ߁p h\ Oӎw?sҭA{9Tŋ*VTGۧ>yyb>N+O&yfɻ(FozQ4Ǎdm:*&o+H1ߑUl+Qp&@dˆX*P.:5NzB](mvdEdi;3Lu,Hq?;n>Ь\#Fͭ,eAmz{4;{퓽lV{H 5$fBCȬ3k=tkX6Z߽Nmgơ-dL Ivˌi4E+~}!xyۗLe&[jOhK1芼C83CmRKmAkqjd9Fux=e5LY ?u]O\΄_NPS*g4knbI r2~_|VJ璟.PWvРs-@QO Dy8W3>_F0?}۔OTqp8'cL~`ryoUcԵ?ϟpٿ2E{~Ox6 Xk=xǿ!3z(7_כKxS13j;,{&_Hx}$Ɩl[f]-*[,'S׷v܆J}+=mCt>#G`1TGt)enQ-=^m>M3'8NJEЦ\>;gvyS$bxK ѼtҗS;3}D;xs*Wij-i=izSh`+99~#nxzheѦ;%*0}C'g9-x=s,T4MڸG?5qs?0y9J~'CҘjxedoh9CZW]{+Grc5oJ։Nn#^͑ 0`f?MJ#xX뷋'.oBb`(~9]|tqӛ}MwEKz3g+7#0v7nV\'1׌ (+L9O +'Vh B܋ռSIvXՏ~?970WN Z]S50}0zH&.Cljhy*ŐQo{h\)MITٰV֟[},snN;(;ե0u¶ҩXuO7]M2Bg#?*B_'ӳ]9doB't| ,WY4޴[}>aTf—*'r\+$;pr뽉c+YppQ,yq67'fh gG*DpOw`e^o57rŭ3;udGbqH&\=O&ݕ4auՇ^ô0>~ȬT471v} F|AORu|V{(B ߜMqӀv'|Rk*OJ^C-LoxdŴv& Wh#[3c18q- ;=v^ki3c>c>τN4aPwoyD@zY9]ϯr eh>b9&-}s5wxvĚhԟ`7^S;0g0\ŵ@YڌlMT/hɗVܘ{1(e?V xv,:OF7a?ϐ0 :(>s.h1T8 =)\. IǜWӘI!4zCױ87xrd]?NN!qYޡ@bN_wMv6l a-,f=3޴ŧdY4NqAΙu:{P0"-m+ ? {7 sTK 5$fB<$%FPPPhV2m֭Zj?m6ӶuEE\۬KٶmժU˜+ҬLIJwrn]2܉&4vVҎ<~ }^z;zqd'<+Qi eZA g~Kܧ%e11UD}QZ>ּ:Q Tzì!q8Yɭm.}, pׯũ~K.]v5i}j|1upAŵb KpӛB+W0pgQ݊$>&jb&n3Rzt!Dn=4&ӑ Bl"gʤ"̧pU΁]Ϋau,!q8O9\Z!B!B!B%7لB!B!B!ȠɖE !B!B!B=r&l !B!B!BTr&d !B!B!BT4\VZeڎ׭[Gtt4ϴ !B!B!Bjժt2lcgg[,, ԯ_?S.]Jvj[M6'U-[dٲeem۶I,Drg$ދՋ#9E8gxf/w$6Ob$YCqm۶Nf9\B!B!B!dB!B!B!"r&R*'w/B!B!B!SlB!B!B!BddB!B!B!"dH!B!B!B!2Hf !B!B!BA2ile»%!S"";Hv<`фE9\qttĵ(s4A飮fX'Lޛj,g'=dc|&$!_&*r;c9ۚ|1qs$ע3o bō슰[#NBBB !t9XYm֤l=uKNQì!q8O+dS\ H<5/?MV5-/wJ_fJə,S#!gV%rP_weeLgӿii| <88gkA;r;©"D0+&&#|^nBe_ W7mغG^< 8}3~|'3O)"I!DfrL}DV)wX߻8>gKl|j/Ÿڣ[SR%*^}N{ +Ubk/1׌ t&]Vv+AVEoW@%vYaWcX:8h1cԀ<>lѕ_YaOr)h4_?2) k no@oN8ONu 6#F[W Viϰ/2 *y`>2^II%|&.e}psħ\kăAckHSL9{^cVVg=?%k׸I^*Ff(f_v ؓT`74gNOh\g[[\IYKqs@oCh1tmX|ζRnw~h;Wbx☷{Н'Bi6,x; џ떥5;Xғ#·|SrY=t-BujAg(Z_xK책iX<v8|N㷒|ೊˌnup={rP [a|y"p*23܎X'~pÐAe]1h6:%o,;QoD &u,MAz tf"+7v.x&g{l7^+_7;=<VmNi)?:rӋZʉ"g9uxnqعr!:g}v{![bUgշ(/ŔkdD 7ŊQ۞ W+lDSMt1:W`ٱ'uh3E)^4?.Vx3IEX )Y@; ucal?rG l akVb5$'1"r*QEZ~hR;Mw~vΠWkRw8kf`"tBZFmZ~%oY I1d#.F7kĠ.49>q >k;+K4{ӻM{wmbVLPw.q4~Utܻ}c1vt{[ᦁhv~˼183 i/O3 EdjU{2ǶbweL_ +XښcS׆{ 9#]_7A%6 [WycS;nEZI'ltp}R=F1 C&՘δw.ή o]^C=Ǐ! }A6= xRiq^g͊aKm fwWn^S?wЪk,єJoT[ӷMc=;zLݳl'FʊA+{h-\4`ڿr|ew>ҬtX8-Ѥ,Y?0~pQܹ[q+\y/KVMWcp¯IN(OrЧ(s1nH|ϛ egOppOԖ<"e.w1@ӊ7zYYGdosd~|s4Odch[s+?XQpTOB?˄M6oBb{( zvFr0kH\3Nb&DfL&S֬YݻâռPu֜mm*OXR*.XK?u&uyXչV+)ZU5AE*WӨ|F)LǨjz$tOhR[j*+?{1*7I-KRRPvXRIUURKe52Jk+KjV̐=*{涶ůSB a UҐO}!.c7Mj{BW>Wa/T}VT|QwR*o۠J}4&ڸޏ?=WϯSTPWK-)Sur1)S=O$)}-26I]N5CKjreRj=k&%1)CMIRʢ"f7Sƺj1 S+;j敔c:b_]9nN|QmtY.R@:">D)l͹ʑT/Zy)cUx|jI#fTzފ:R)e:b^FhQ7g7SF{IlqJ)SQɨ |Q%X]w}[ܘ=^?FjǠ`W^ piT[m>!wWMv̆ZjxuzBmeH>{jUVf<~IcI럈cWJegtP>eNlU*4&*S]ΣڻYpϟǏѬOk*S_/^>+mT}Ԫ |OVFԿ+7=u' ӁoTyjrCF,\lJ୪}C?zMMR jQ~yFٺ'm7Mt+658*QuT_(ӁE lQzt~j:9[>@y;*Q9_.']٤Fw\mWo\G1?]N^dKOW zwG~3ĩ+RQylkck1U9_'e1(B+GMJc@1e,1P{N\S+7R*W{1UCk*/G18۪뮨Ɋ]r2SS/fMߒ^%+j IDATiIY|Ԣ"OnW[\PtMutfm>cVuʽ+-jd=Sr|weKڱ_nXb.=mjjK6PYԽ*4x ٶ[PqXl9x=!H 5$fY'֭S'OT.]J… ܹsj͚5zZ^AAA g9:nTedT |E{,)|$&d aO&-]3y}Bs/]i=i7}74Nx/?,ZKULleefeKoY16=S%+@SaPc<Ć) R0&4&/71郶_ەPyząf3dogâ8һ`A`cF$ؒ&5DD+{"n?E r<>wݝ]G>GVSդtEۚ*pYM'^f- @[#K*Aht*;lScĹZ5pBp/HE}{_g?3[<=F+E> |:oa6!~_z51盭|퍥޷ٶ{;O=J|<~k*^FkCQ/na5LnKw]$rl" {Xcl sy !X>ϧs uΖG-T[$#W+MEK/fLFw;Ʌ{xWQKp'0H!ֽ*W|i{WԪfփGD8k~ͅ#9t3&`dR5vn֦LE(p- cU,{\?on`*_JD-clzl@Uw [ ĵ^} 7GHg%[ -yZq'W1c|)ʵjsW̥?fC.I3s̹߯dV@U x{[=¼(^)&xھ*"/p!RI>XWFZP5jhERSUvsU#Z([Z;b#Dz&燾uUpqT2PQ^{jlLyBi4g [eNZFg00`0P9Rzg(0-ۄv~HvDD66truWB8]A[9 ߔekZMN]35Zii}h9?5)uc#\S)G㷛QC 4om qϮh~緣{,md`rfH޵ 6On*2my"?/ UG%b{L&]gJz+)7)]V͛ֈ'gu+:uHYij؜bU<]-zK^zc@JecNpI+ppOyLJc!ܺ2vu$ ;{lM[[qQG'FcHޭ0qwLn@#\c#s2C5d$)3ZeIg8{2Ցm{LUKboKҽY@|\QUX ˇ1nGhܛ@{"ןѺwG-A-,Zñ<##88ٹj2LVn~l'R |I:M% \bF\{8>ɞ[n9#7,g{#"0rE:LzNaӎ遇ػi*gp.q5߉h6jFcJ))~l~|6c8Ir @0 VaT۟3{L w/e4Oj39 wXp:wba$/B;vvaԒY7ԟ=hO]p2A2#HRM(kHZ5Ն` ,WFmK,G6x:Cdxd!Oȉ$ޞOk{SLMM155@NT&B!)Sd|aE|h;4&~P^:Ɖ/)7B0q{ rnfFGc)2hv&d0Gof.nF!$JU"si*۵[g<R#v4x83UIzCU>4I%R**keOFյt8Y4sϖ'Bof0~7Ty/m-C4ufb8S\7ޒ7dٙ乘;Zx28ĘX٦IS`gL.)033CDcbE#p_HL Y0ȸ朌$e@hH9|Zo94KN@.)H-X/FoTy`ğ rRE<3&8XI󝈏&<O=N OT|gS ֖cc*e9S5Vƥs4J.N84 z"prI os1”e(_&ͱ7)JS X݋°X=i53D;xvZd N#{1no”O Dt>kC5w JzנQgB=:3eIzAgrB!}V\_jSlר҂b5㧙Ys kAV؇D [%*(l 5 ;WͺaC# 氍rӥkBAWrX'f5|RbrvIEhx|p]ygL8FK$!.EU^ܟ3Jz *6hgӼq1\c'=_uKB(/|Yk#*Ӂ"-EbE;>ǛSѾ3k{6QzKޑeg^bQV$/sɺXRT.a|t Y%c?d\sNLUxj[!&etݜ&&RBYW 403njLSK]w7x¬AI11ġD ԯc8 vDP^ƣJ!RFO;I^[.G*\QY+M/OYy5l l:ɳ9Lpp0'vM-vgqh̀콴B]77lW R4hv ғL{ X$z?{7 jary7^=u}zuQbU f{)B8JZ+=C)G '!\kC.(0û\ UYʕ+ϫȺ|4srF2n!P(FD3s3DCb`sG9b7h_&;?lJ%cbR$f$INU(9AjNxA3 @]jj_rO/7()ZIIIЉ{صiL5oo|j |<#<#<,I973aLki!a#>24;N9 b9iL5Fx;2C5d$eF;Mۉ-q~ +:ѧ>J579MQ1`I AF>"Q{[wp<}LRy$ (ń%xoWc`_n}~,x9O{CeS Bʶe݋fITt,.ϰUSbGع\y;1k޿7ߣ|7WAb/uAYG:IS8[37J,toC2b8Esn6WGWDaPF4-s!/>k7괞lu5?1~NI|Z)H[wH|C+&~%c`}j&Ö';/>jPNS`t/ƚOw`u~@#x|FCC `vi,N7gba݋?"vx7jhyY: ,Gmq^C(5#^z.ylҎ" a*tOes0vzUVQr;̆6caBƿc[€AS \;nRMbDqɘ|3߷˹ҩ']8L@\3&nɰi8=&IKn=T?k^FrDrV„O87BV^dB<̑~f3}) vKB74YSVs޵#3Mv.gќ,sJy;$5u3RkatsuM]/sKރޱ> ;=G'ݻ!=w">Ui|Ze ?}?cBVllY=gUb3Iy\AL[o} e?6JC!-bS`^,nA\NM1JڅsYLűxx*:k܋ۢ$;33O9(l@4E\+2129'c&I/2f%֎hf3Qj-s-C10q a 6ĽeTJPQȹpqok5s%>ކf`.MPPś%S95~c I[LZ&19xkۂJ6 d̦U(c|>QcW&nwI{¢(nҏ:j3kJflџ&)-M|FnU"F~;Kbjk~cdK?iwЊ㯲8Z?-ت`jH h6r>ievUyfq51*X=Q9Ut's423:QcF r~C C0<"1e&goEcpLVL{,Jy;ϬnB>'Aݪa" b<ߦNbDjTn2?|E7Z6᧭KˈVmRcϭSRV"Ga<7_}@ZVc]>%J[t9ً迌1tjZ7T믉4NmٲjƤ- Q fXm™s53~ ?Jwf嗎8.F(G’"NY 8Wn uQ΢*ƎOWXmtЉ6()Uhݒk+0ŝ:(tN峟ߢ_q ~Ę" F#N4(ͿǨ>}SN䓩kPڧ[/:3FܜR8dFj#s֌HBnM5h2b gԌOh3&φ`w/|^SrIʉ:˓q S/`C?1^JKc{]OE۹YC.w&w"Ui&/)Uk.S<y1 KGJVi[V?SR)LXQOC4}{:;׻2j"vhakn~ևQb:Ń119`PJ7FȳdM+X^H;=2rH `d33Yo<_9ʔމi>4ťBeWrinUZri\3%j2T6ڍ0Zs;Jm1͘aq93IzNvzW>бc<Ν;S/].1]~oWVqgQ[^i\:D:u :FwAI9>&e==޹ `!miuw7 񿡰caTB!X}^L;Gp í@[^o7–N\s ]=GŨUQOmzԫSf2C5dOa-wڅ;fff00 ={??|N~zvZ8ovH<'4T,:s[3I$8rD$p/, +,qxz#I,$$I2n Z-yG>`{ wf"ɂY""}M#lg" y.'11BF2&t$ x[0ȸ朌$.igo#FoAr2oƵ3 A$I2BI žFSu,Ǐpc.D@Y~ I.+ūwW*St IDAT }6WGmFXP*윒Xs1G ҍ>(W=Cy"q,Rɐaq93IʹH6%.qzS$IzsrDCIM*t=t2$I;L}y#SfTg t: HByOuZ4*]&SEd kɘIRng$I$I$I$I$I$I%I$I$I$I$I$I$IRC.R$I$I$I$I$I$I^}Ȧ r$I$I$I$I$I$I+M%I$I$I$I$I$I$)W]իWرcmxΝPx<[$I$I$I$I$I$IRASNA'!vڅ;fff``0pY9uڵk}`VUdClذPtСC2k'; C {U 1 M7Mm\SNїټ)?QG 7IOHxmw@ű1`jɧ[c8/L3/G̵Z,6er3H{:_E]i7z.#A 9h<.<'LgN+0y_vIn^*/ ;Yo<yq*UOWo١ħzu|

    0.N_ZF= g)jL,PrS>n]۾Ce,,q҉;X5t{fJSG3J%J ThЉlBlUmQւ"nhN{R\A| L!`ZVvbi5tu eƩvChήImQb֦X'~ƏO`?/bY"-,L6d%]̔άxq;)YĎ?O>s, וʅO-=N!M\m+W3ka(f"tVIk,(0\Nxn+ڢfXM);*"&yao'ɩX P,:ax:7_kK>12 "yzQmAFWDDTcj;ln0S2~==җkk53>: :uܿFTdwhsZ7k*[YS'.s-|vovbo&F0lpxlT] L-׻cn(]Y:@^F_yekObv>ΦJߚ+0|ZL֟/h6(G%2 ca߲'}6Gּ \7SZ^#9Hѫ}pEQ&n7'LœZ;?w[>6 НK* Z?,X&or{ޤ/M=ϘMYZ~D%bZh^F}MNsQXϖq dSDP,$O:=oFm=m8|UT6<[gOI+y(pkU)cO1;iCbr~3w{;@܌_2Jd.8b֙?[y|ğ/+]cҥ#똫eEFVS|~K7ݒHB@|Nk5T)m[F aq93I+V^-t:]m۶MʼnիWňL4qCcCqB&x$">I1ju"tdѫp0p)[K[D !pxHB.Mu4HSy0D-nB6vnDE!%)[&f5"/fNZ'=q|LՖw _$jm;+1du=6ƦDAұѢ;!cŚnVB5BNJBLMEYpM<BVTHJ'E)*HxkK;U²4qI'DbDi%.hd ~a>ߟr}:&HZaw12wOӂOĮ*,ůWB\FRcg 3b~Xѝ(j.^%\E>]]z?ƱH CK6[AπXIX7,&f,bxH /.7'BݹE-kqI0_FX?_H]CgQ-?ZKJGX6 BS ;2m&o_/.mŖ텂g2igpherŋ#֬2-~U]ˊOOQDf ĔԆB&}Jbk~L) b!RW4) L,K_A&eDQ1&R {a3rOa^qoQV[D>DgP v#{Y2BJv}o;kDQGӭo/^ܚZU*[J|m"<eAzeZbBbs4IUN-}n[xdxӔ=qTMS45,w6pu/^v)y",^JJ]5]zbΠ7g sw[wn׼pwZq@,0]tѮ6Q EQjX-鶮,/|zgkSQEwдU7[StU[6e&Ќo$&O.$3Ke"-W# \{/ OOtq>q(88,?O !Sir7]tJ=_@9yQ}hb'C}C"m3kLaq8$|\<''>2C5dOa-w)\"޽۷o7om۶s#c[zeY98UaCݑN[\/1$6`4-ZKfy$;\".M)un!!!/i@aEØj2-Cg|+x?Ƥ(+a/)ãDcó!k>T̩їc`HxݳXHM)t81IiuxWyߺ=~ UI/0I} Pn?/wj]V[>0*f9x]C捰NYF鋯{ND'ьfS{Шy-TWj p'E:A QD} x%2;制[|9k-l!^Af\0f Q[X!BWQ>ltal햁O4CcqȷQSc;NBNExaf$#<_F*;|4l=<}ڇN&s_(|+l;0+(9}XF@L[}ŷ2$2=o ۍ]ߺq~,ˇdfc\wuiw[M{etז78gB4#eTS9r1:L:κѴ|9ћr8e\cF FSI񾛸oۗ #4ZMs4hep:bj:.W<$͜r;ٵa.c;%0C4wÊoJ/ynXF [}Rl۽k}>{صl/M瀿H~嵁!{̗K}u%໮_\;NmߞϺ{^#qoz6GвwyUrI\Y!~OkcE`ojE2>\|ejT$9ndv$ +]G.:Ý !G[ T\^y=KuSgCٺQreʹ.Y;^ל1OBvXP%s[ii6ʏQ$@j~{JعU[3vtjW,káC00!"#xծieJ]b$"_>LcPp͙ zP`^KKnׇ.Nip*ꈈ%n$QEC١s ~Үj\9X=Gbek[勝"<_t,ٛM w`Ax֫wfO qV<D"/M✲x%E/sCF%VB{8.MGFAi)A[=?TB&qJc՜Ξgpn8|}*Ԙ[YMGuM\£3V,5dbᡄ ;*;9[N8) 'A<&,,+ik\kqIDSIfEqR&zV֛# Z| ;*@f[~;?@jO( [w?bS4kQ$箠ÙKzՇl]^*@_eίَƕTnJh;h|+G[ᛲ"õMbێOuEf>|Φ7ޒ2rDJecNp xt8W{;>l$ ֝K7,5lmEqa"{Hr1ݣC\x@sQ\ל1hUK×'HPʏ$ npQ.t1jyRJ=\d-i7לѳTὋk9"&}E=iC4a3IDSc4K~}S}qP1suuJ?6Z_z CǤUk9 ` vt |Pϙ=v' ;,8wQ1D]O0֌CZ{;;sjɬQwO.dx f{~m$-ŚjCp0g+\# JP UC@j(/×eҩo*EC/|rObI 8FOطtO~%OXx'1$t(IeSH27u338KgԵFD0'93Gpat3ZM!UߜO(DtKkVqܮ-ݚZ R#v4x83UIzCU>4I|4QM 4vsnΣO%.anRuqXoN%od~! y.掖"( {8%1&x5virH v 333II$ @<&]9?  }e kɘIRF hx|çuXKt ڂaKn&LeVO.,NY;kC 4߉h#DHlQw,p;A|ot{ FVC9\gvM]'\a{hxh=aP8ऄܷL~a2/؛zϩq_E?aXf񴚙l}"_M|m-C},^DMa7зsbaʧ]":b5PPһ%kШ3$VX3p9!z>z+[.AUW6kTiA 95tA@Cuܭ[UrHTBk+f԰PsFwORP5J|%ubV'*f*gQ8b IDATǧ ؕwDasQkġXJÉV8 ><4DqϠCPXZ:[z4g,ϔNlՁ4%߬*Osy~M-П@E~X)i,dKxq.kf*tX1?ޏό,UF ^Shs%pjNuMȴX$ ϴ2Ow%*#؃yp>EU^ܟ3Jz *Ck0INpzG,(4YWv0yʽH.cx-y')乘GT6+\.'T(@r1:ǻQj0+dJaq93IJWm0UKЭws^H] e\=p30:mMFguE֌Z(;'ϞTp07uuš5߳b AR wk\ܰ_-Kž4G3t^wj+U"!ԓ@s׸YB9P-oЀ 8;"XQĆ`W4*{7KL,11Ɩhb75Xb^ް^+TTyyv33TZ=lQKץ3U*T)ϑ/ע7MX,4xWnv|qD(U²budN%㽻8Z4*#\m]P0ųl1MK/S,?6).Qzh䌵*C( NHS3SDxj\q cF(#F.̍kL;m5>mSt4veB9/JDwPe]m'ȕwmh1Pv.'=iJպ50de:J`U--3wRv]OT>>_?-YgG@kVh*Vwy[˽ҝ@NPP:)gT*U2ٛWv&xU/ e|ɪ]ovmx&anZbf}P-RȼT(^ȔЛwKR[X%Ga@caIZ Q 3$Mì!~2f;M]о\?3Y1{"yy*ħ߶`l!ƈ^M|qX28lhNp FL.LQ^bWiU,p0Eމ.j5_fޖ WK~K명3A-/owq.‚ 8lڎz4ǃ3ШzsX0e }EZ&B^=Z9ndT^41Wm^jF<-,Kv2k׸A8Rv%JBEQk tgT/jcFǹrT LPeA8:M \Am7` @)W+ƾy#Y^&8G/pNl_ʌ9;3K|{ kL]6NXCx8jy~0MBVXUttǜ^)3%#8[K3R=z..ƘҔsEq}F3?(߶)k:\Ea]5§7ofCh.#=)ѻ&s.%t(~C`.lk0کtِѝqHcLXjO튷vOI|>CMR]A۱lr8 CH#xv8CL/?t0,J[gPSK_Āf_կ-]4D9GtbBUMTGide- Y <( ZeT{dMv8Xh0h.2Ae.-Ts-w(F2#4g3!龻#UbϯiYv<|:Ԁ}|ށ^ق{[1`QrԷ6c]d=n 3A’~ͨeҙic58/g55<X1tz39^UM./A]ф]ڙLkSQ2T&s;8r\[0o~wIvb[ϼzR6Ur=}ie\.nߗ^w +ډ`Z39E)WS)$Z,DߔQ|)ZU@N~D*fJp:FΥ|jctɢ2EZITޟ[Kl{8|̔8n4c2ՍMQZ92>M H˙ѣ ,SJuρbBylя :#S+ Bl߃GkveOGaY.?ezn-K% /;_=(L)X"ueiHO竘=p7!L)T6W g`ϪItpI$Wgҥ}mA5a:c0wh:*&h.1m &Pܧ&g۶KBf<'hlQ/&/ޤjmYqo8pmÖU@w5sGģsl;oob[z 0r<ݵ\;Ѽ[xƛ"=!;L@çZ v毿FSEϖem^/i>84]Uř66;to\t6OhA{QꑜDɌK{DgTi0vWBTڳp1<|&FC*V?Z5`ILdPYDSl]ܨ\yԀLA?d1MjK ~Kچzq@O2@:KF}r1 {eщo( pJc0k0BbA"em[(1eodi?NN!X]h?!_'ӮIul=h|dJtՎ|*eJӱW:5i7ZR*?Q<8z҄f}\*a{mjNF1uŽLuz/Yx$<$Pit&/$+W_#9@#ɮa\!&$z5Hs['}>&WM]+b9tcvaރg((Sݳ}f:%]QM{2%v.׾# B\)h0Jr@Mw|W?5Z]2eG#vSȼ(f-ZS^(7np9՘X9Pī0)#xyjy/Fc3[ \wc2ì!~2f̪V:ТELΝ;\2;vu%xuSޕ_؄l,rٝ #] CeirH=1sǒ9ڋK{{s"d $߆̪p !cV2bnvf/od^vL7q:"/A^=E)f 1:n޵knnnmx``0pRsFpppf%5QdLuѦMj6.qW2󔆒y3 ։9`$IrVH=B<'NOO?sB# '.cB$)gʵuO6)vSȼ8EFYM2mì!~2fR&:w:__,ɓ'9"2/f>qnXǻ 9I2G.>Ixǝ!"ì!~2fRf lg~Rɦ¥T[L$I>PeUt;Oٝ ILݩ>h"2/f>Q8vFjRf 12_pppf}e Ԓ$I$I$I$I$I$IRvBӷo_&O\0D$I$I$I$I$I$Iti}6`&O(t:铦%{5֙lQ$I$I$I$I$I$I> bĉ@s0~lNUdL6^$I$I$I$I$I$I@Zs,c!w2 ~Iʐl}&I$I$I$I$I$I$l67$I$I$I$I$I$IL%e$I$I$I$I$I$I$)C6I$I$I$I$I$I$IJ!* I$I$I$I$I$I$I\.R$I$I$I$I$I$ItY=MQ }fEJ$I$I$I$I$I$I},ݓu> :Ο0j|vD$IzD8GDgekBt XXX`Su ٚA3]?ԵK8. "|'&$5eu*0oYWX7Sug{AlzNL:[[;`";sjdE)d^̂y8p尢H| 8x`CvM2YC5d̤![Z>&xs :Ve4ޔN2#n_}3G/͜W$IR.zD<%`Dx@}sJa@NNQuAw{ybi]NnXKaklu?Q 7 ω}ovO>LlzKͥ%S3ccb)/v$|o^ +M@}$xqnɕGfНa٨l)Inm8،lyAi &|T {I[BŬ a>>xt,{Wzoآ 2YC5d$)crClVKc5* NE|gzڨ}!2WbI^bxCnڕysjK8mK# 6Tjڋbt;%jkT,`%eWw?QJBɍ2`ak\͌PT5fظxPqWF$ރXLJq07ԖkăD$M>3hQ%:=ā~H ',L])^5n{Yܿ `ĹDm>AQ BN]p 5JEJ_m_+ yJѠb?{Ǯsdڊonw9lۥkA̡?z+zѰr.$pk')Fa匓b^1WVЧ^qM*Pt dN3Ν=q&lhkNqџ֎N1H8Pݝ"m1? /j*& uA3u;&/Kp?u'f/V1RYt#f؎W弑5yS)s176 Lu^J} WHZt$<tW{6JOCVSټϟKGӲh[m;w~ؚj06wĽbz:O-U|5}Nxuص;s`{s5]KYRLQ⸻s_.Sۂo5-wrj<#n,3E.*c6-߃Rcksp>? -KSjhіnG=4s$^+bG:}i-p(T_dW(4ڸRJs/*TJ,`bjEr?G.f[T9>z-1&%If 12Yzf-3ٲ(7߲ؒrZu}+Ыl ^ˆձQk`{tP].ߜkɚiqEN [wQ~,F<{;PwUP+?̟}%8܌ү\9dv:E4 Mn0x(4DɌҏKӬ"* ܜ9M~Gp06 G|l<m\c1dHqD>틘թO`o2؃iT~(d̓s9ocyT9|Jwˢ 3@ђ[T'fy`Gadm W b:2a85ÿiE6ͼӝ=;>4i`cVXs,Zqq:MA<7 bW.4Լ'DrJ-XYl($nhϼtbQHAԀ~$zaqik˸N|bUM(D$jœ\F(h*GN%hUi^gj.) xWHuM:]7ƮhՙДNV'eAT1k{4jb?Q=%{s #9~>]=Cw!us\ImtMQwگtgܜ"5`)u!n?50UE6 ̶ձV@wf/e4z9Yx^i?fŐOmGg'c4;Byr?GC0ɵ'/%}?ӸlT߬c0_l-KX~ ɛ㏌~]Ԙ_'n̙{㕃~C%/s6LPav(=?̤grLL2v_8svGO a闶v{fQ}-2xԝ{;|A[Pꉻ>s`GI-x~0v≞wlœpY1O󐤏v# ySѺA8T(*FĆq!ck86yA2/f1A9gLa|̵d kɘIgoo`@Q Y=GRa0ϒLUWZ%t:]}-VZ%^)475$?6?UFDB!b"Ť/P'/:Uw1.EEB诋 մDBwuA@G]D9'!Tl_yQLh¶PUqjHLJ IDATT|:FdC%Ž$Н&jLDB!%ov&6Jnk)GW6yDhB9Wn^ѫbBâF|)!B1JXDޱ/R,zA[{b|\ 8hO )2:qiLEalX8 bn}T+R'0thf冟)mm]-5yy$'jean\MLBē~EՋ{3Y1QMwi`VEOB_,n*vY/$xKXXh!e\SL4NvrCĢNwߤT[ZXXh+_|A10.1HO(GhDEB ܆7IٜR,;߬GE9c[qSBAzX%Z H[{=#k1N.Lc6Xx~VLk/Ԏ$߳ M|0/"jv-N'>lhj~D4[Jh!  kxB٫uZB.>Z="!ƈ͝«}i) B MS3qt06-+JC803}'.A䘍I7$S!d~1VRFkK^;kEq⣢Tk.\_^:vM*ۈcbg턩F#L틋mǘz6]EEQinlj=F]W?;ԷPn_]15c"{1YDrmįW<½ARў$W%ɣȼ\$v!R) OŮDD#4΢"4lhjX6 !O-Hغ!o zqgzmafN M{unu 7G alօ>TѴ yٰXw3微/!j-"LH+#vIlWlL(۠(j~:bfyp0+*ի,lEx5(]Ϟ L[ޯ GgVFpqwaZ;Ś*~d$*`K s4;\%:]h3$gϞKJ4˔߿TleBBGԃfdbC{_1@SSF"Rrsw8`6S/aAPn R^n8/|C"}^4A K$ۆ̬k,`#Yn,eyڨ(Xއ<\R=ign0B1`Yj)>!A/$kipqR}D,;|*~ˡPDԑ~FY5iө ]lMe׌tiu9u`,Ԍ]ZJ+,SKU~fvzn{X6JP%BѢl|f@93 ת(g%7/ykQac}00ΌB3xotD{hTN_J1y&D8} :6/rEbÑǺX]1ʮEG,֐péafic4bD4Nj)]Y,oj׭xƣG(ή8'sUa:u>\TI"Nx>ŀe=bWrQ0-ՀϚa ,ٲ˛#k*RVϑycY̖EcPYvSb OkH,Ĕdbb<)$xU<'*W*› %լtKH.x:Eն,kM@<ȥ*<*{5MQۘ0(* aX-Aѳ80f`r)PL93΍y'X}*XU'B^ZΚкS,?0Z"Nʛrrϊ?ڦHҌ{Q6wݍv=R+ &ԯ/8 ;W9UcjVMm-kH/2\ÊV4{4o)߰!ܒv4i?(x{=K\Xf:7>*FOmY6u%֗]#Y%MqyS68R/T@=ꔌXmZ4#g-Y!d )xnWyR~Dtf߄e-|Q.U"G(LACErQ0r51hF~d2!.'4n<)E&%0^ zxAt]Ǡg1:Z&IϽ{&yRaZ S\xrE{U&\$n [kc⏟0\&_s[;lLC |HaGG1]a<!99e\f 12tٝ,}:]&<cvE &АNX'ufx5^w)Mz0{7Ρo5Hw)?trm\͍ИRZG8%_M =Fs,Zį[oXT{҇VSݻ C{ %Ʉ {~n@f X_Oo 4}f~ֈ9Y)}cgk-N q1cA;jK=&ٻHWŏf)2iaC:s`I7ԡzD.3< zKfGpͥ &&&Q!~;V?&{EvTr=Cu8RFj*|\с[ {gebڭUs,8vKfK-?B*2x~]ʸw6+}XX`EOCon,e.>KU^vosYumj֊ jš v'!1T,OșEZ1ii: HĻ2vO[pmxy꭬DUTQ9y+|o/$VТcc D#1)XϏ 7xT Y/E ;1͗'>kל7_4FC>KeMaZe+%3|=d3SnsX!? @bAA+N8kptqBc$>4<~DAslɡX\ʯ[,7H#4F sG8h"!0to6,B'I1'Q}KjJB!4f]Y8'(oz%'N?L;El >uH}"}Fb*: 1vYZleweyYke94hKŌzGeqS\kաlTʯ~da'7<3/7i43Xꕧql:ox#2{fy1S, P)7$3[X% ,€} Q 3$Mì!~2f;M]о\?3Y1{"y*ħ߶`l!ƈ^CT;g[7)1%;S?V3aF5zUϋ_Up4qz'*dm78 Ƒ3ɭ)Nm.xp5qQO{n ~T$/}ըxȕ[X0g>eܯqOH4s! x>2M?CE4rR@Uk2R"K1¶ AS٩$z*{JIc^mv aU:f)dUm|˸`O LwjS^ &3dfi&l8Q%vΦ@m$?A8+m1>v(ja7o$KKзUB%G/pNl_ʌ9;3K|{ kL]6NXCx8jy~0MBV[=M)(_GZ9(1#~9~k&_eA8빸lcJSuu|ۦ|p9t޼-*7H6pO=fJ׭Szs|ȫN- Y <( ZeT{dMv8Xh0h.2Ae.-Ts-w(F2#4g3!龻#UbϯiYv<|:Ԁ}|ށ^ق{[1`QrԷ6c]d=n 3A’~ͨeҙic58/g55<X1tz39^UM./v5JdtgcgVk U} j+%̫g,umC [5!ǚVUz1}ieOpi.R{6/Q^2szyym;ӇN 6Mvs!-YIP,9KhSH P̱07,AԮX$=4'L;7XW*};mFE2{* IDAT|ы~-c({y1)Г\OS h:+٠"-ϙ{p"pF6< W}f 17Am*1ژ&fz/iGA(8xR@4Uz0ggl)962d)?|ЛP^=cN %b|DӳMlU6O/WyCkR=d0WaŨaZJV{(rK%'Ԩ@KRxBo ӹp7E+4d֡|W1Fwmk0)ȅ>5i?߶-]2Gn~<+1ɏGX>'xGe ƽҿ<µn[6V#{Dži-)?-Iz3>zf!ěV7oTw"WN4/lV#^-fin-0n=O9 LP*δ>1{cJOyB r+2P$Nf_% f68JQl% yPkjԯ ^Ւ)vțZhՀk&3Amg!LuqrrQ3ĺn4.'/kYVc%?ɜŲ2wg(&/MAh(]W⇖sD'86`Z*ì: ъym- Ɣ鿑8;b˧w1}|L&mm+T;PӪ)M^Iڋwפ!kIlL*OD0yK -~i(sN_޾N[3y'Y[y:Nq5,Dp"zcmp^6ua܋AۅyX8RL>vY⢮-;d,k@Ծ6+r-OJGտ%K1vpr l Sod% %?B;c4T|~UCЄl/;, 8: P[%Xbbc/Qc%{oQc/ {(h)~G۝ywvfvwfn3O&;ˬbsϚ%Sډ)]UnFѡt*%dSyə?NHK3xvWpKgpIz0Ϗg1S|s7s;,wc4aƐLIiɬv~~~v;wÎ;hGNC9gg:_B֎(1L}ɦ2B!B!B!BId !B!B!B&nWaڵ;w$<<WWtۯB!B!B!Df$ڮ]pssCզh{^^ܹsԬY3Smܸ-ZT'IM4aӦMYeJ,'0yHO!vBק#Aq)$;i'1}Ì!qM=Y9x`f'A|`\B!B!B!YdB!B!B!"2%(yx!B!B!B!Df !B!B!BJM!B!B!BT"B!B!B!H%&B!B!B!D*Lx-?&S"CRB9p0&6S!^XZbiimejRF 438oΤ}\,'&$!D$x_p,@V3ex#KE )8¯% K{ԧ{tч%1ԓ VYx&õmɓO6/8fǭ&(@}¡{"sj 'JB,, ?=2PEG]/ێslu7bonf@b8[:=,~ky9q}u+ؓ\=!м+6l=z VF옛`N\_#Hs]7sÅF ndr(R*f&OYC3xɮ>V{Gf" Ō(zsW*wU)adwRxۡ1Ì!qM=ic/"5V&Fj4ZkKQP=רly)X.V)UV,Py1U nԅEg '~ZLL[w3?mjO-Qq?3ղk1VQiںPBS lʣD*[;Eo%烘CC6 Fj#4f8W~+(X5&Xj-qjȿfYtaJa:`aJߦ_$̜B_۰$ИX$,[| *OKQ+Lt e;qswD.NNk3L-sPz7 %a_YFϚq072GQYٗ8TjkL]uuOr;Q qeG9=邵69=w>4fF5șn;SiF4I|̵w=J*fcLnCh-)\D㑗Vӷv![h]vS*Ex0Vs{t_ĩdםnG ́$&Q1r g5ZŮ|z®_2quƭKLo[tA4cb>W6t1}:>;JMNȘ͗HX(61+6سy=Y;;v]V4Ɩ}rM$K OJj0ȎGfBMGb׳“=)mmGa<4/ mn <0qN?C>

    %J|H=aU[LoSKr?,dt1?ӑIT^egYfߕ `}j61~ "~&Nl/n6ihG #pi;%] D<ι6.L f64ȑ5QM?о}}xc t˴nXD9LhV?oaH_lT{j/a^|; iVxq/gLzT7$1&46܋) `3gјeٓҰ<=6eT%xU)}_cHn<A,0OujH=w;/?ُG??䉮ö6АX1aqj9ClJ~wഐ~0R3Ȑ16s>fYÌ!qM=1lqT6Kjuhڡ?ӶeǀݎA;3*1;:LJq07DkKr405~5K0#?`^-|s)&3*~aqgP\WzfF~iŋ O~JcĮSp>2w ||SFZCzz%; صq&7]2mnޞGμoj6lEѿxx #~Wس{Жf1sSkDÙ1povgURjUV.KT^.ݟ{ku$箠w=B ^}x&lBUdo0ÿ¯Vuj7!0- X ೲ%i2ëx6=LB?3(T$%J̋snXS`,cdԮXB9{`%Fǹ|!xLK x>WQ\-"(]t܏3-B,6$3b0ݘ%[Pa~Y:k Of͒ Z:֡eJÎ8 ˘u6v,+"td~aG@!t[Jڠ5¹xCl#߇ %[ӶD `r[ؓ!\WѯQie]s",ѯx֎PTTZO}Tx35Uşa?جőqH.vM{֯~mz q K"ҿq\S 4ƥȡ dЙaAN|?CF4j֞cb`EL'ٕj07ūW_xwg"2M5 gK 35$fB$'S_%>- *AK۬^D6y0sй7ߛ$;\URR;bqN>}I (ܸ͖IOՔX"#LugMDA2zWM-=‹vJA)8.0cq WR.5Ğ@S =RԬd\z}㳛b/?dyd`E$VYS`\e=y5dMN2ڹƕ{mfKߩT6Pс,Z|CQ#[9=1uƮt6n]Ũ,mW+&Q3QعƟغt"m<.t?jG[-LYc|Ja/_1G(Z"_Mw('YQڧI/%mQ^j?}]Mč =<*5,}z*.CmfoC=^ S^ݚ!s7+`Tntv>~RQyl޶LUN-sCoa쮸Fz?p8gC:FvۨED$w\Z—ue!cWmQ>jE).E- |[IUp<8XQwrY^%*ga nE(YU]}fgW9u>y)I!SnRKaƐLw3 )aM٢,t;:pO7%u?b]1UxؘOߵScXk_R +rm?h(`:*;<˫g8ᅣ^þH**wZUW|5>WB;S9QXNl4(z=zEAm[G *1^#sdGyx&OR/XM:1wyolՑtQ 4|uOq}7 ӎ kc It+ٯ/6aR@KaTP 'S]{HMq_WPɝAԖaDO+T蹿Ѿ+ {m5֮M8d@ ^ ϴy›" -x_̃|4x!27ntrͤ_DA b3>!N S;#T9(X}*/rBeNbL#]}}}\sbs{s "1#ObKғ<)P? b;'ƥ>V)WVvaڍɖ(ϔS{Hwg&Фc#|ʯ 2)cOQ<j1qlC = IDATXu`vt)TݙI>C2><¶GBMCiS) 4*u|kV1}u1ۅVPQ|76z5-ޕ"O}z3DhV̴EвHf< ݾG 0a2}e ~w_īZ,qiX~{^AjR`eLe]+p>]a.spHf3:˄*.v}L+W:ƢA)[;#DHY|_j.)~bU2VPr Ep;b8o;T,5lm1yOWUEb-{k brSbr|@f kĪxLjƦ1g9|<ZuՑbGLZ%loC'UTtX4%)0~J^R<[JË&sk䈫pnmO|Qb5,gB-pM`Dk8ɓG9ׯ ~۷q˭!oөܼIЂ+]zPjiqz0)1p%LBw~Eѳjs^w)=?XѺPn)xA39kwx"AsYCz=DܿU)uhfD^wUgh=3gথ|㩲d{γ]fn /TDo!찳αnSh=䥓B_%z=םQٸCgّ>q; h`offD˻}f|V( *U]#÷F7Pej͒/D"ka3MX:e}W\$UB%@Dݖ]"N‰#HRQNJ%8 {5Ld'wy\E*2kKeJN+B׵jr8yƳXX Jev+tbEg9ZV1GT.F^G;lݞ(WD){$g")[42" >rx :s}NW8rL[=d*fغ+Tx`q_qE(ꋏᬍFN%}>B)icG!rpʣ,H5v *V#qK4@*Z-b@yIK?A\|C2ɒf kĪHJdKr/qL, IWTI7NwOYretij)G\j rf^dg)į xNh,`*W.lzȭ!Weg_mAvD?.J h426MMچ|.+X92һJ.fFN8q?$ă v0UY=B.W!!qvҐ#]!58Ȗ#9R\wv$1c tH !NDi_cz0.އ+;'IRYI/Kr 1ĤxA՚*̮gձ`ɏy6~]Vl#ÇJfq.Mubs&V,-7r2d=z>Mk0~$j#95KRJiSr\*d?~Ky}H-)Ȇr< s.Uj7,~j fN)Qkfp%Jb1$'1"QY<ؑy;? H 4К%Cdbt,-߻Rt X*֧3㩌E Te=/x2*Fڕҏ0k:f/:Oݨ/H|$U`jfʸ}x'^'+8\g@ ӪPbK_R&F/z.8^<΁P эl k_gH2h0Vo\3=0M.n:wJ2gu7}U1ΞU(Ds>j ϥ[Y2-`0itz(U e.7]*{ +lB Bm. 9`BfN}X+[4׬D|^9ܺıW2k"k¶AqwYߞiC?S6O^}nJ6q r4qYΗn߳^tHv~wVh젂~ڬ W[Z)fQU-|ۢty<:HecJwl`>cG1S{*,ۛNc360༝$nfcaL}ub}j&~.x:m:3Ҭ~~7V-V-Ԏ7oo(D>6.\wc4>wRFԏF3qg^oGG,oDdG&4tZܞ}3ȏ:.}eT<_Ǧ3^4+fOaoOL-f|j{”20̿>#pǷ ( OfFat݌k7_Ӵ;\g*thBIS9ϹrQ O gn䪏BsiTFeq?X8Tlbt4K,hU~ߕ4ьg݂^#8DO: ٭׼/C/8 MB^lbH|`򨁗WEvjW&PSMţ3G6ddkDCh5so؁m(!, L£OeY6T(<Љr0ءB!dm{|QJ-PHYx*s \ M[ s\5Zj̆6Չ;gnq̔g BA|yI~!nfLȥ0ͅg՞3^eU~ں>#P)j>['4Nqq475eʅ}q&;מc y *fy,WsYH|VwkR30½J6EߩĔ<:l8hU[`6ECq>i]^'Ng}{1[CB32ُGihG GsN*Lmqr+Nzcggg@ڵ Kw1rD}/ئKɵ~ ?3)Z^Fʉ惇TzMgf23:3Ԏ­@L*+8Jicbݦq w f-5-D 4UzL8@ 㙻n]&=ʒ^T/&x m ލãwc\q:~MLRi-G׎ݩxt-m]ըեY5s~8_K8HC2k5R:.40SMFP0ʆSre`é)֔xeo̿Kﱃ[h# l]j”+0MA4&G2.&l2w ~#Z⩱ % c~ǻ oƇ;B0cZʍqUN~ۻcݣ h]܈"vq2?~0R?99щ ~isOT׮qn0v3.I,fz&abnG`nL&1ԓ /)-֮]{/sN|||رc͛7Or9s w~Y%ӗ[>0{)Ie1Uva9)[@ ärs:W(c{fGNC9gg:_B֎(1YP~eb'fv:ҟڭQ}2;B!H7oBbSa,e¡Sȱ( $Bz3!#\Z!B!B!B%/لB!B!B!HL}ɖE !B!B!BadK6NB!B!B!"M2%[lllf^!B!B!B4Qp k׮=n޹s'~B!B!B!2Of'!vڅZ6Ezz=Νf͚oƍhѢ"ܶ =IjҤ 6mʒ.#8)wAC} >I?ǏçO%$Iؤ?f kI23; "B!B!B!Ȋ%B!B!B!/E !B!B!B&2M!B!B!BTlB!B!B!B,)B!B!B!D*L6!B!B!B!RIfŋ@vh1!ᅃ4XKKKl+.S2JnGIOs&eiN=4$!PoBb$G  'n#0>}yMb1$'1"L6kے'{ƟL.x { T"ת-+Y!4#@AAtѾl;̱oG leF )7eBǮD-Ko8eb"`o3CRؘj0/W\XѓcnbEjL:XӛkBd,C!e1#(ܕJ]sՇ;vJ{]vtz q0cH\SOb&DKt̓jGRn?A5j4[^ *U x9 @^{LUuaYC <oKeSLK hT"_gNm[4p(`w9T龈S/]f0ER%c7ﱨYۘQk2fvs[ȠBˁz{(cT֘]B_۰$ИX$,[KQ+Oӽ(kccąU]R890Aݘs4m[mlQ\6Z]LPXݯŝ-15%w~M9WBO|sۢ5©pu;FhFʍiiz8;3ZK2{dN3I׃\{׳@vOɳP65Z G Ǻ+D"/oBdb TxL~ڷ}ݙ6<10*3zx@{bjF6ʁ^9?%'uni5Xdǣg|3ȿHa>Ǜǔc 3vȌä҄rhax1 T~ iܭLHE )@LEkjJyƥ ]xDalg9L0j13I7Mb1$'1"2)QE {"߲fz b_IOX2lbRaSG:4w눾 td7AUxiv ᱎ3~g=Ҙl! !S^a.m糤K93߬Ɣ9R6&0{4.r^a\z ;@n}6 \? -l鋍 bO% ̋ov! 7iVfĽ<Ƅu|؆{1eXy9,,{>-Bc*8q&w\ֹ>t}ٺ-. jYIW"9gC" '2"gL4x܇Y0cH\SOb&Dr ?qlE_*UC(;v @_nV_`d:Tʏ &Z[\ =Y9j2PO,0јaV3Or~ܵ?/b(>D֌kIdnImON?}rxzW~g<]6q Ft~ö Y8& \!3by]%Jxz8%; صq&7]2mn"28OsR.]:ۙK˖pFGX٭k ؁e֜KQ(G1GnZ’-Ӱ ,L'fI-W r2~iayM1q٩nGPև6hͬp.ސaTX.IEJTݘ}&HbfǞ8~J/T:WdklU*T*-MV'>*<[ffz3ELşa? լ"K{őqH.vM{֯~mz q K"ҿq\S 4ƥȡ dЙaAN|?CF4j֞cb`EL'ٕj07ūW_xwg"2M5+U|1Og֝2J,߹t &/9gRpy)Pw|n3W&Fx:d-Me\Ay1d-׋]]/,_<_ܩ(;p#BqJXWz3Ώ6ݩo%D"Cj=?=N^&&y_$/B`Ylއl;Nxx8a_~3tI,:g|S$ņc'<+~WC:h9LY^Ь JscOKc#>nh gv;;[~Ӏߪ,QVmO}h:~veLެaޤy+/90QJգg/-,e_m`J%P1|;)"ރjV$=ɕ:҈:y1GjV6gz*.I% ܫRFux8D}w*]yEDH5q3% DI?1,2'c&IVfxZ֡A5 +ΜVN|U/у'Xƻ/siӜUS6yUVu_UͺS4lVBp싯ݛ-C\mOj>%bOrꦊk]Vf:=1fZӿ+R M:r!~dY_"W^skKτM23=i_ȘZo $ޡtFdGz^vKQzag@ŧ͛IG8y<ֵ+@c@$^mjUq{[2sz<p+Z,30I#-vJ1Wt{ Kt!yLyQ 2CƵd$QkSK+©#'8{Q`zֵI?zMtN{J=bf5FlNSOw2Z*v˗ ^< jEA ?1qs㧜̏hbN a]0[ ND*qɟ~7^QP`ʠ|^f ҝ_+Pq]q(\"3r/׎魡G9w&Qe;FӾb.g3MG"ȸfaffAqd\z#JV?f|I$@Po:ݹ MǯU]NWiPCc-'٦x<,$I*>fj0!W .cE7=WVx\sz hNf;|t;)/NhȺȼdLp*3bZb?#)4{\ij[mTY3@ (d @G{@ -="#d!Zx2f:PEqV>}hğ^A))),Z1NN0+~ N ¥x= GVX[+(=؝&sԯ %ku \Q룉i ݌"Ơťc9`ȸ=j}7;wTl?Unuݢd,}% l3Kje#w`255ʖ([-]%G;|wӆf0>=ykb)ۘbjὡ?ѭ #&WG uA3Z8}3gjt1o3WEQ1(ή8N-Go`]*[<.#՜.ӍI& anP0uS`_S6DCH&AqE[DHP+PaFvꭶcoPz$ #].8z#Z*UD`MlX5 eQ,)@r^{y\RP$V.ea$Q ?ơzl;zjT7H8I<|"͏lS,UJR g8)z)Z|[|y9GjɒJjԢ'TWc@ T q!Ir{-EG֝EI"Oyq֦LRQ)@ր1TQi1/aʓaq-<3ISimxMš[xkzTje Q [`N [2:#ų'cDը^, 4",Xd&$ 搑e!<" +^*K ~ c;7wR 1) ׸W7eroy=i>~Z3?Ը :gcL8α0;ILﺐuEA232AT2_FMQEqRC˻,9}Oi.| : Ims/ryhҴ=HzCc&nz.l=???T-'lNmSKip^PCsN@tIni+UNlcWiW -Z5Nv^̹ qG1ORwf⏜@.([·qѺ2>>>~aoGsglg8d՟9倹9"!fҔ~}H3'>P$q`4ǻf(`e|Lz$m܃yK-8:BNIޝϣBY uGwl!i\?d/l7>maj~=I[d>_N (2Gπ*S= |AXl5Թ[_7:P=m9bL1>Kس;d5]9W]@M鴨/;EW%qn{Oc|j<S?4B%|7|>슷'nLQ!&pIw_ԩK2CkeKךԱ0L lǔ80^ie%[Ȃ߯PuF&?fFq ٝ4wN (&eȺ4ڛȊkdj:8ښ5FFdpN+n(#UMh4tY3޺3?ww+8.^H" ET |Bږ}4gF`/VG Dlp/k|Ϝ)e]v '-jw:bod߫d kɘI҃DMRl^2ޭ^gApz5r' .=cKX~=7Խ*P󞲧m'qf7 jKuz@ǾX~~8xѩ(8Һ|4U6ڕcS1.l~˹ޤ4d wcFA{_^%edS+.n3q#7꿲ͳ>{ a˶XtnTSqnm WǞB$ Nw 0wbMƭʇ%lMIbH$!~ ,T{ ^8+ 錙4-ǒ jf1K YfBdU&q џ2GS"hO6k.(:xKW93!+r%GSCMNm.YOq# ݪҴb~җri &a?X{fԺo_Һs'9wCY-Z喕¸v?`ZƯWxYņUo2B~tZXeԺ37/E=cFvYHSu:~yݳէ-G#Dق뫆R4n;m¿½!ol)d;®\ OF|;Ty\hӖYE?r^Da;kSZ (.~{3^ϝq}s6Oz&Jt(֍!ΛW+3hҴNxik2VߍQSJe~>}Y*VIW_"`Bq|,Lz5%;rY:vc$|2-[re~oMFBC]9ip(.]q [֔sgE F[$c!ⳣXPRb\G3\@psE C4kQ5&T+ &zUưxȸ$ݧ9ڵkuڵ}v5j?C=l7'Ye1+5j!`d3:yW.]?Jd+!!!23'qɽ{i1 E8EKْΟMѓ1}z2CƵd̊OHHN="%I$I$I$I$I$I$$/$I$I$I$I$I$I$K6!<$I$I$I$I$I$I$=9M$I$I$I$I$I$I Id$I$I$I$I$I$IBEJ$I$I$I$I$I$IR!əl$I$I$I$I$I$ITHr&ۓHW^aI$Y q㖆{ qZYaee]/8 *S{3`AX3\^@k]4/$ٮ$WH8Ͼd[IS2oO%iW'ֳlHBbHIDPP\! o}L$1睢“1'Ug nERMhuLmP pKḿ#ŝu3G,IT=A="ndK}20L%uAwyjm܉:y]mB7eSA¹@68IrGTez0L~ҟY蝟ӣnYlMyΖd S+ʻ&&X~YF)dF߃-Fŗ>nƈA$qQte<6J})yR1jc؛쎪_6z:lHB aQuRʳ;vAS.u??/ɬjϢ?uL9 ]̉^^x$s?jaVRi0vBxmBv\.dϝ*?÷Lm7A;2W54E~}ޅkEӠOYCh[ۖz@>p\q0ωAڰwZ?Ft=l[5n>O%[g coIJ^ :2+#%~qYSiLtR(?lQ2hT*&عUa,w xXPRdӯPZFRi4%CS+*֢uL7/0 v*S,Fvd,d^,*-f昛jPDg9p*ۢh [:j131Vưxȸ$s%{H17I:5{K!QvͿg0;E3J XĀ_QU.sj0p X^&L (2; X=B`l @jK/#,eSAZ_3nqͭtGzhbd8q߇gΪM^fB4G–t³bHF3bg`X `U&^I\"??|gѵuZxivm3nsXԫ"*@e_ _ucIsӐt98j;!`Wʘ^ٓZJE#|C sLR1{ox`x_~*\9qz=#?F eþɃGz\UzK%#K>exd9"9n,k/ʪ,1n=fc:+gʏнf0sb`ݹWnexEڶ{;M~l7՝Ⱥ \?:KO~doGP~߃RdbeaR2ju\͞O=jZ/nl]rz5\I\<_ϤG|{poE5U*idE22/iѧ9n|,d kɘIRQivZbڵ~Ibe3akqYφxRSQ ٖ("O̽A5KZ5Sv!ПRo-҅3E#-Ԟb ! qbDEG ՘ MDD!;)?M+DQ ?:qu}L'&&Um1ߎ3wSQ}es| rp\Iؚ?fPwvhlj#:,NAXNX5u-wo4 ۓ !RűY-e 1j}ů MKgaU{JȉP88[ ۴ ˝QݜGC|gw b@ےfw$),M9糮.bg&~o? '&by;(H5d\-͵ b~l+,*!?w/bs#sC>1V [snJ>¦,_>_+J7ty?#D7[Ww Ft]+ vVČ9;&U&Cñ˧ B§&ϦLd0qU3S+Qڷ|9 0}[@Qmaq-p'\|QKL)Q w a)Z ^攓E'\#fԏq{iajMs#]5Y6Ml ˴" μ`}8Fi_ab^K 4$bdН5@8r{!#.ɕ>c8/#<|Ӫb? "e%Ganj)\ktc~=y˳]mDb-Er\>mw{>G]{QvOɺ5b_."sK[HT3&VnV梒T|V ږ_+Lᗧ۟EƕD)K3aWxc^q3w#X5f-L4&ҩ-N)15#*p[޿GǣRQ"|^q,2WKG,+'=#n$޻"ȿ"d_ػg"BO^ ]0H(NPq%>{\Dl “1+>%n޾}8qF~]&\""}od,?k׮@<{FEaNUli[|6{ ,ДӺSF%:;$qT$%4+@ C(A ?cxebcKAS+W?mo Y"uyoس|XZmtʽK@`KXmKO,\Iî.U\.;Eoj{ݠx؞gO?:h9LY^Ь Jsc0kŗAμ4f?;زl&o0so(;p#BqJX<6*,#F(kYF4 PxıR}Xi;;)]*0n⟐ ^?ůۡ0cpl*m n=;&;˜!4qTP5Kش;Qyg]ݬ:~ӎOz2dEV}u`b~ϠZ1YR9`IE H1{@'ޱe2+Wdw2Kx>i9y9n_?j@¾FUI͙o䏭l{yEQW$`LV'oes3 Փ{Q-yi=iEٓ\Iʯ3&G9#pto5OJns TI% ܫRFux8D}w*]yEDH5q3% DY?^!“1G+Y3<-РgNqM'Y"NWϡ[:hvb5|}s_Wa]AЪ\hؓ»aG7TxuNZ{ڎua4\Q ٳ5|;[с޼9 >[_;"@ld4:uYܜlQ9YIt- Xگ2{ҁȫSql͌_?&;:WpU|$}: x?oGZNyP5u8JL֛cD)1ŀlmo)ݘn:@~wOkp%On9pR ZJWYѸQyƎ 裉 .ιҥ7#.EIHJ!J`amIN X6}m0y>{0w'5}z+]622ёD {j(*g\"I)DE%릴p)8ˍ"ZF(*[bO6(p=`^-ot hQAeGx1/;E[L39p<:\8 ֫exZYk;joGGV}_?zxW|=m=X>`WiUUլ;uHC;f%ʾ]̐9B5_|ZiT_;k $oe êcU=;)`)ߤ3/~pEe2e+m5 &0`0P; VzN=5P0ܒN)v\U^]/6Z3៽#X}Іcy|sBvx)2u=T|p>bChM._>DJ}7:05|` sW7CF1C4s;UЈHVXBl * Р A= \=C: Rfԣ Z R4+s-rI.;mD=cittj`7݋n)J/L ȼTyS9'OǺv2x+\U1|5!P8WZ){ ;38n"I8`ds5LGo긗ꗱD™t)g_Q cXjbٜ؏N{:+W2yT$fn1v_V[CvsM8 w}\>fy(JG`o=mYx^t~:&@( uޯ~ujRhr2vdU{XTs1 o [zzDbcdl?AƵŴw0 33 &#:QB1Ga&̗Dts`_}8268pdP*Jfۨ04 &HaG8wVNӔO ^n^d]yWxtzz 7JRٻ|2RITeFfM ÉVסYG4:SA6?#y1Lkn܍F@C\V76ߓ]%*jP~|=+΁]>|/1{q-EC֝G'ceTQ9ÃP2ISlF<`nnID I)zt'  $00`N#2(A1,2'c&Iy \$@aaCه&4B^}4ӾOY+\j O{I\i D߼= +FyЯ;rhr.As@$đ CGFvɜ|kkBIZAB|*`G)Wgh"oBB71hq)X7z};q{+2n'=v~͎0=G0-MݮS0FK73E+~{ ׼MRgvLXDKr6F]Fə52Љ;0@eKyzG.DzvgZq MiC3xww@Ci| ̣"1i;2;clywW(fP$q`4ǻf(`-b|Lz$m܃y4^ueǾ]G) ٻ3gmjZg2]Sm#B)mdH!!Qf*OZS<Ne }#v/hoZ4\L.kSFw6e֬Iͺ]Y~o Z*p# xw=+:b>>8dfopH_B`&҂ھԩW ^(1N~Q-EF֝EF"*OEs.^!>WUfbeH".>׀E}"VV٩X`e|-c-i$%cX+3ޜLw5Ly:&v@:\**YasUMU0v6e_,tHV 'L_A4p}^V Z~wvwcj&6M6^2l4~b/_N؆u_^rC$m+K d\T0/[^yOmS1cAvv\ǂay * R%AK]_ͽЈ(VLKDL{-UUqVۖ}"qTPە&$gl:ԧ ğcO* Bmj?dЯqe3=F|gb1xO~;0bzգ]JbBeWwjXWL3XYJ)%ƚ z,Zm>ϣBY uGyٻ?{oro!1#wjXՖ**?UF(EVhڴGj{KHq=?$$ ?y}sι<~{6Vtn[{w](CGodzk@Snj2T4KF|?-?パ A_<4TKɝ ϖ$Q;-#!WBYO\OMm/ x>4ށ>$۔&nwN)4\u=~6lfEAv2q&5h]qϡ!lb)Mn (~lM.CZ._w~xp, T42A '>6U}w?1uE]<j5d*]79cZTM{'v5OrTў5DG㟺Q*v"MudQZ;ٽ=w"ofגLlCI(ӡIW}ȡ8Ĕ*O ָD.sf`Ok}&}܃I8]CR9..TV;3s:*tA<o>㛍;A׎]ƽGbF7>.=ےsL/x+9g1Yuy(^̙Ma;*g/䫷͝uFk$;v3\3I꜋78u*)7F\95]В# =y1ݏW {rI 35$fB<I8yx_5wLEէ em=hB5x(ޝs o uZ꒟Qi]3sti8+ܘ&ۙ ӖZ7uݻ&>jWGqhDiΪ=l$]&mNqa: 'FkO_4OIg +0dB}3UUݷµ@MZ7mI{j"n4JbC.)X"F$<ɦxz*"zdLƌ m۠gW^1z}x:ɉURnobRi%JF 9pgoC _4!.{m8'҆fsow_=JgZxL4/Խ1Lf<ѿDR포W nYݓޓ|?_GSaϖcƭ]|q%i2r9;&/z?[t#D〻Ww< z;|KR(s66p?k)EQ-hb\ϫ0ωzO`3lq%vYr}Γ9M 251bm2@u{(7y捦XS -\-X\'nZi<0%x;Ǝat9JS76)kl1pJWٿ:Iɴkin-/nѺ$Nk|>oS(it<}Jcpi..ʐ'Dz4@I'L2ܺt^a;?_𦿥=g%;GYu[Yԇ#лD*|cPO̽[sd4Zt(es9t ٟU•Khb]SF{`I2[hU0a' J9{KpѤRIyXT .\L۹%'ӔI 35$fB<&9ʢEe؁ׯ_O`` ֭U~KL%uE\ӄlZ;wwACej,y [:{p|S;Pq^~?:Cr9WCv;}(V{-fLENy3vs 7'wЯP nny[y2I^tLwX&E/muP3 _a搸,d׺yÆ bknfLhh(uԽ|K.uU.q2}5sfɱ5-MWA!en"u Gr䰷|aw$.cB!,[,! p]KeV=퉿_Cx/ig=BL-RH^D\\kke7l$hF-!qM;iJ%u6,!RQ&{rBkղ !(#!_R>חX=C( י/̤Y9 B.f; ^p֔ze Z9wZD V)[ K+Pg[s 9!?ezL)'NI0sH\Nb&DڽBOi̶O!TXSy):Oۋm:B갩 1Yדu9zGNGv3Q J:TI 35$fB+B!B!B!KلB!B!B!H,d"B!B!B!x9td2eB!B!B!H,d3Yyx!B!B!B!EUYh ;뉊 w_!B!B!BIH 6닭m>o61̈́RnLN˷tRZn]Zy,[,[f̰sNx$Y:u8:H jxu"3cI<_.w$6Ob$Cv̳sάNxɲtH!B!B!B!#dB!B!B!"tM)B!B!B!"]I6!B!B!B!HلB!B!B!H#Y.R!B!B!B4'لB!B!B!H#y-=w06)B2Hv4Ƙ 18;8K15eiRG4@3_J=rp禸גk||],Y,я["ɦ3~AvMO2{v3!qDCpp0l{VxBّ3gu$1״ ^I6EQ1Sø=[ Ɓu5mwBQ=ۯ5wY{t.c`J֝!p׮6/b;Xщj/!TOK]4scHZˍ>^ <)쁝^}7|H5BX'SәhӇΧ>REDdp}dV)yfcOOSev'a)$/f(ܔ,Wb>i^ޱSCO@r1!qM;clY%zZk['W4/千ޥ[0lUEL^V|6aByv)R gQ1}O?~3ǜ\@E)o%Oܑ0Q.RJ4l LG&FU zhl_W;TNVk;JA>p^r̗^n&x"+UA#z:n?VV3c1g|7͇[\ۑOr/T2z3!MgI%{LG'ѾTɬڲGӢ`GĜ]xL^\m{_ gX),_t.H!gQ^QJOrJEŧ`yW0HsQnMh괥GKMwr[B[ bkB"8]È}-jm8|%ttzllm[Ē$Cv3!,K{_.ݰSZeG؅l^#=jKX>. εfm4PWNsEiz|:E3~Nr1dT4r{{MvG=8p.϶{4?kDSp2lO2wiˆQɃ\f"wՑu $UZ6"19 DBS7Yҭ7ߔ?2O7u(tt:Boi)+@Cim|U7X3#ƞ $ۣug;_5oEt~nحb GFovZB]E:إ}Q|əVLl:kw aEێ^^ם8{{FӼ\&mkK^|$4Gَ̜&_ПQ+G}Sw< hҁ_ݺ0vA#Mtpˊ}܌ِzĒhoǧݏҹm.t qab]M;>e(껥RDF ߝF]{3y//۲"+apրf/єfͥ9bxCޡMM~ؓ FmewD6^wz((7eWm2.&@w c4z7|s/voo ztm-gf5 [G",Lb& )]%k;-a搸LReѢEh4fQ]4^urZ+kg]j4lTJ)mUo2QS)ePW^a 5]*œ?7 BBBRQSQQK;d;닪kÔ9MƩzfA{<<\PG'EꯁzTd Wy;"|>nZFy*mRh<5NU68Ƴ%٬jm'[[R8;z\K7NܧIT]mʩq,CJ|}T0j1{ͣ&9 [+{}U5L3R >mr7TQߞ1=mA_?[F5NbT\|m"~SlUGY@v&T:z)}U`¹,))0]Vy*UhJtEm vZNկR=uƨ'47\Yr0'Z~N/:HOY] eRxx*wULGJSDl *gFߒ3)sqjGecQ{cjRJJͿ_t/wyrNS5;Tw~SMmԏبfs>~4ۿ4VGUKBeXmUJCrM*!ƨU) 'fV|kjhSuY>{P -mxiT;( W'Mrjjٸ$I9:ՙysLlSl(s U05+RMT?=*mwK)P^ח6_RybFx u\:Ln˽&-3:I:pz$Z߂.MgթfNq K0JA˛Ph(`ΖۉP8rm[OF|*f/aN,ne{{WuY￈|cv&ԏ2}Տa>̾-p͈iqe1`@U Go y߯ϝ5.͙/!}GPR|Ml4HqxF\;La?^79k+))gX56z8+eHvoyU<+"۷JӖp.TZ!r=dXBUrhTY9 Swɯ$̭ÛYVߋ=܄QƷh̗hݟd ]Y=f/ [A:mZeӻ{`4^%ϙ9|-ޛωkum#yYq1*-r"L÷8%᡽ͩCG2"ŷ%Kh7u3g#p(F+U">?֏F!qM;ϖbq+x" ]̛`"IOAp-4g1{"Z*j/[ŰXwNo+ӗnru }h~l޳ȫEpsuWL_~f7 oT (M!8S9H6D{Qs&.ۛe;B g.NC7/SA&CHc`ټlO_NThՍO0ѸA's?G3-}BgHItQMm\55s$5T/ue3z/柰o/ڍ$!&n^C鍧EBl}/K֙4Ղi՟^o w4BTx yYnyy㩉ڍso*\za *7̐+1 n R㉷uϙC㘏irX]=iGXamf. Xk  V%{|~-ƞ^Nn/ϫ5huZ4d^ƽ0=QUѢ &?&"O#JiF3bm2ųBd׶Qh)\DbYիas,Yđ^_f +k'`:h55zvKR<fxP{~t_Sߺle&f* s0IHprW,hܫ: ߦ}u2^uld;3 s&9pSN2dHph 6^E)hfJ0sH\Nb&DOkKaa,;1+ lw"d{{~T)]ːlaj:Q+1d[spK^F ]BlOT6 \HatԿ9; ѵW݄뤵Vy'3X8a_B77o U+n,>/ryOzr5^'NtԩK)=Μ xkZ}blՉ9Ё8p708Pk4v."eF(HAãV:g{t/;GrWً WYإD}R`K8U]uc? T%姛g TVjժQJI|xǽ~#*v!Ǹe<:+S9_bػ%_̜]+)sb)W( ;c(WV;XuwVZ4>܂pOiUnV1\ >gEԥ#;{W_iۖ?w_ QwPA6l1{hJ;`w:9uߧwԁa6}͠8vZn}Gs$` M!c&Jd:OFڕ( Mygb)/kc3wDBKbT8%bc:zxkй䡐;zet@q's-?d~dQ,֥@FwG]`݄ljhWN i͟&P;_-j̔!M*{p|HtN'1df?A\JHƋBE'ţR1q`)Gnű,ѝR |z/~eأ6~&r\c9F?=r+V7Ϗ=zQ.v 5@^5t*%#X>t w 3=[~#2x iӡ=ړ;`-I:ˣw~[6GBXy̛^@GN}h:}Ht)mM#Rh{NlNcы|(dMjкCCض,;R%(P \\L|X0h5dX@N|)mbO"Aj@>=]G5Ёk1`?uUro#D,mPwN{{0D8ͮ%әنZQXC74EC q')U~A =q-F\&|uqLXSK_.q҇r]\27tI7х`<; mӪ7/S f? b= I^`?Ʌ7qx^잪/sjM-RH^|r">D&s.ԩP ܸZs,׌NvAKj3g3\$rƠ3w?kwo\-'1״ $ &~Tީ3WS]W zwxw- iAK~FuӥTJnrctogz0dL[jxw˛]}>ġ9>̲t|u;Ǎ懅C86=9||Ҥ>%5@oQ":q_ƣSSif,N<8ӯ4~hWƶtX |^|LG^ZqkNBع_6IU}*ɋŧGt' *C~Hp&={3p yf bN|ÛYqW?k`y 5L㶕e fո>TNnԶ }as|ECLyٱKhd0iK!yؑ`>OGBs,NI;~ :Or+H]Ι ѡs%KNg/)fkĪxLjsfEm ʰ_@֭[GViJ*?\ 2 Oٴ,;w$000!^3,\YH=9ҷuvwUnBɽ3= u_ rv;P*Zn=̘Yݮv˝'7e3>/>Sݳrn.*eAo'$D*ǃ%Cv̓] 6닭mwff3ԭ[7S-]֭[Wv<\dz\υU$L`4]\͢ !!#*>1ɑOO;}n%6"l !l,\%wi l"HEX ɋ@Cam&(x[b9$i'1"^H IDATD3%^ #dO"Uh5s ZV ez$>K2ػg%-pWZ/̤Y9 B.f; ^p֔zG-RH^x9C㐟r/O:;{V''E!qM;i =ɦ;h2ۂ&g>B>S=bMq0tdkғ͛lٲl2Ν;%⥓|g:_#9Wu8׉\ό%|$)d<鋓fkI2Ν;: %"B!B!B!ȎI6!B!B!B9rw!,,,SၛŊ˔F)B!B!B!"9rH38ѣ/^q{f.JA]xa9Q;Cx]m-_w;Nr1?Վ_=-CؗkƑ*g56.|:xy=S;o0vJiaOMg~AN:HbGF0QYyAVLg7\MwLdܤG",̠eP}):7ʕ#Ɍ%fkIDfPJ'#},p>QCbm넧_9w!cȑE ;^HB) Lq|R[WIrbբSJM{0mMN2JSĘYapp'_7h7t ''gSHدV-7ŪǷ+9䗳Ym-WwpﴆJW4/千ޥ[0lՄ4.&6,]_GL@4/ŸH,pnǿ94 )[cMa0˙ei]z)&LaЃ|p9W)1iقTt³?̕ \$l08h0uy808PIzFgAFv`B mϕG|k6aByv)R gQ1}O?~Ĝ\@E)o%OA *?vvn3B}'0V%c q'?~y\`P) :-Z6 uA>p!?uyk3oXĔv3a8G6u~x珟g4cn4(7;ι(Y##^e~g$%1C0JN}Yek珦EA [m;<[ejkM>dmab-sqG ̀9ǥa"x:uGwqQ}{GM6ް+Ӭ1ј1hԨF]5{Wb7V&y**( ȡ|xwnfvnlmX=(fN,I1'32]T]'Nӝw^n̋9@e)(|9z /E5%8tFbbKR12'c&ea-W{>yQ"H3;⤉ލX5udz6` |2q@uqz,Gʯ++ևu3;OAl+$/<Ԉػ\ gp5qC7S&}DÛ9/5D""h6uʥ[Yk;y-Eڰ;jX2i_ FXEA;&ҮT:L`B|8w C<|bЎLeդt4O<#"ҟ_k[}Z ٻm0S5nn ОC>uߖ5,Af{.IQ8眍- h_=ee7QU)N\ya da3Q|ƵA0:ʱ!1O0U㏭ ))אoZ`D^>R4hJ-FĢF|$ǺRح uy!ctiNǧP9ЖˮV5l mE7l*σ3QSb4'hqk! >1܏xK,Y!a0_@ۘ<f$c"gLv1Ǚм)#N9Y'Wa[9 h/͋?c"6qV\=ŞdR& ;fM/1mD xC{Ix]Ϩ+ ~,(,S͞as-(L9O>w"xwᴿX=YVnbA54^y;r>.uI_ȼCtć_䬑!ټ7}$aΐq<3 Xnek֬&~v!Ě5kċ6&¨6՟u*ܠ+J!Dv5"tѭ75&¹DU|>)ګbjMc"AXX7+t"}IǏX7`$гhXTU1I*% ܌~SU#B&T%J#S]5[-#a6 vL}o07)~񢲱z-}.kW Ww5eFϲ룥h%yߢ0HΪ:qoaSaVb8FT]tio: S>sG,k"(j1p2R ([ Љ%-tM] ĉrh8T$g}4lY閝/#BMXQVt79d1F.G+abl!{5_M3( 瘰St2?/|:_"UA{CJLڻ=Ȕ?[2IU?b"Z/zC&?1O&V._ By1(~mŖvB.tB,L_2xql02-/>/ͺH?03(!=IA꜍ji鵅Q?[sbƫ N,'=DEՏW^ -Š!ʽ45oF3>'Ծnד8ϫ6E+t?]-~2EzE{^uLLHLGK OmN3Ym W@YEX Cs'=H|{o$lB$->]Fg) m8A+nfv52nMRѫQB 뢝?: !ZvV݄040V*aĤޢ0p E?C/ײYVgτq1NrJs:$l! j0pe[(6\͝/l[BݙU2/fWcWCn?{=#AűClX<)L:$΄jhbD3!qaq%q7OGCC~GD`mӉŹ!w$P~;tVuo ̓19yn޳gz{n~n߾-n޼)vؑК5kD||OF=~fYT>t9bDa4n6#=a6-39|r@ >A Atz2->cl@.XhlMji~L+f7BS*S ~ʸ朗px +/$ޏ, qib?e2n}ܳ8ʽNjB8hQJ4mdO8>ueĚٴsy1۪ t`8A9J*(8~KJ& a zh,Z tIK̍^Q4*s<q "P5Q - ༺ -?b*/59 q/T}33BFlP:Exs cS%c)z?C*e>q;EI;F[{:{ښ:O@~hX1eSw,DKBCB|<5}fwF"M4{]D޹ 3/hS/xc(ЅF=h؁)cN'D>Ghacau&`{eu$fZ ehMg+VT<6WYh?cP;N's_t)6LNї,Z|f>҉OnM6 [W2a$K}ŝt唁m.ga ZΆ{ٺt27MT칙kպ8o\GʣqEr-Nlq3Xm3=8mJ0`D_Ŷ]Y9Mh?hMlXyiltk;1;zS^AR/.~]1˿H~嵎(ˠ[عc-w^\9ǔv-׎.c0I&n߉އuH ڂmyf6/@k3yjE?2/5bxYy9 \'wӍ|\>s;O+hE l\=(S 4r|(x-K)c;ǥ\{K29C5d̤!"yEznk-D8kC9Gu;5'^^_X)[J-8rn|1BN'@H_ңdm]42c.iḄjQT#toft]ٗyn^nrM1IzjXŎU(۠ >*_+V'6'eGXů^Gʉ͚Q5՗Ю"-? EnldNk%嘊u) z%ߋ1[#?=fLCs|?ˏG{NP\^ !N? hLxB*NbQD ![Sw2/-.%)}XV(˯2͇ N'7 lH:̭;q.n\~)ac`D ;lD #ɾ$]%wtZ:¥$9G_eaΐq<3)f,2*WZM6.I:DZx\5IwUG7K/ >gCq `1:]:ıC`HE.k@)Wmx 8}2=J>݋&:=|ڠ ֩=Y8T7C8`bmIC$+9|+[;_. ӹZ5xո~>0}WJDßwriV FܖA+QgOMi5όҵH޿{k:m&yS[1#6gIk)NDWVVQP?~ f b\'Eiy[,@-/]ˉ>ɴvcnZ3 Uʭ8O/lyH9fv˜ȁ[Ǧ̾M''}>=p1Lo}t͸Q ߟa~}Ѯ7%'FA #oْظڧ)GʶwPlZv^ٔ/ C ġN)7]*k={̦Vq@;f~CŌ !1H `Y6\#3#B~tؖvX1)]uAN∳!Qk A ."{:CLŲy0Skt˒# 04H^EOT!Ka@2ٴ/ P@LZ{ySRPH")=9׽8d>+OMq^yWL'BX,^}3Nԫ қgeP{4X%L[27g)]բi"iɛ⑁}uH!@Ƀ+e?vKugvy1)Z0p٪@RQ)@򄳔18ƫQb•&Ü!y2fRnlĉ7.m-, 6,ӗNkK<Ѭ~P?o+CF(pa2@S3LqdꖌK'uZQ'e*b.fbҀ Ư)\w'^_ΰߏc\RA63LU.?͋aݕ흒+,0Q<ԁ5myph/Rv^GW*dFK;|љb\9?fMŎ`tK74K;R+m}_g9DPPA2۶9(ARb >{•Rō&Ta\/[33ýdg VƵɳouwYݫFwgݖR{Z~+M]QvBZUjٓJ5غ?7k]gk |z:χ(Ԩ Vz$}}nJZ1Đb|e?@w1jPYeZ%=\WAāQS~JOWVꑼI0-3 x/ WnYRJ=)Yt ~4utZ{!Pi9`jf|Ldvwդ{Rl$ʝhNφǮt^kiDo̴ci`^V8OKC DVrקl^SJyN܉_n<~_[^hgMМ[ÚsN˽ oAףڠ2^V17**T(;>9^:tvKugy1[, S)qcͣǩ&,jx} 1O 3T?&ymH2]29C5d̤4tPF 2Do@WǗ 8x'eqWbcfVX5ĤxFʮs ̉L^Ɇ9;?2v~ޤ$-r#"PYiok^c$so,voi_.J^5$ VNZc/c1ۘ!: AA),(X={oOf R:D8N jBpu(Κi*xX+\4ؾ<(XlH5wo@!qݿM⠡7ֲFa|{#Oz̑QӤsa8fIǧ3rvin%'O1qD)gLiWg$ƒ60m!,/#_q 4"/g##n>qlzյ1zZ;(Tw,=J'uGly"AE{ {kƄ7Vgf1|fwe@it֍&%]Xv\Ig׼3R=H̝~lXo3IxH9 bLegCotnNUG#6pZܾ)c[JZi|գ*[ertŲ[SN0kRǷ1U-`o3F77QMWc#`lfǠ͌|ÊPfNj_{~i\>_Ӯv']<_\ k= ]lMfz5-AZ7o#I"X5zg|'p¦EmX}oQT=tѥ5\pi~#ڊ^,;.)mfQh9j՝+krK0Mq9`͖4S713{޻%Ys0JsI_ȼ @Fu ۆsy55B5VAEF3gcA'n(dZG 3/12'c&eƁ (3&8Çv{ƻ$*h,>m0 9.ũPSfS:Vp8Qx>[cWoUR=ҰCR?lNe<-}9ǡZE)Tض;ڰ IWaW}Xw:Bm j ~j@kԐMS+ J)қzi8WLg˶٪bb"Ui0t=+jAIZeFXi94D m}~y)|ۤgwSeh/ӡG}oF-H ƶݛG!QOvTjSZLpymp!3tQ<M[)^㷍_aMuܔ3Ro0XQ|FmBw5RV \Uaըal>1XZO_?qBAGرcxK=,Ur JHT|ȵc 2wŃ0nJo/J)fJ#>ibQ_.Ǩh4[Dqa2cq뽂M10 SvwkulY6`%b@$:M\*NtG1Lą*bԶo~īB=O ͸7 +ZMdzy>@UKdf|X6~|;AGLY7 \)ήxT P0l8zNߤ&5ΝG#-q3@fT1'p (V߯^JE'P5eaMܵc9>qQ(7d3L̯h12k|x/t*h׼qIʌ9Ó14Ʉ")2+cvTC&W(~>/BFZ4imZ#mq$VXƤI=Xt7"5{n3X\տelWN?uT.iT({cydFxʮqP9N[$}!⻣Qx/Mp(rVnes_a`W^\7Р̖"6(ݘO0gȸf yg?ZfԨQ/gĈo>ɖќYc͚5۶mm޳g޽2{A.6rcxY}xȑ#x{{v2wA^I=9&0ȃra;Тt<!|999&g 3an'GK\nyZy.ɼy՘ tT/׏=@y-kx☁G4Ü!y2f9'{SӌMvtt:Ο?OÆ ̆ hҤI{?c FѣGgh;JΝ; 6бcǚ>̊ӫsD7jm-.$I$ͽ0-7陧{{Q$<$1cB$I%z=?㣊A[$}!bZQ5jzoNCFe skɘI_~T*3<ދD2uh9Kՠümia_$I^= aќ<12z8+t7Ѻ%I$uY.%?H/ϑA[$!bQ bQJ`G7O9r?w*NNd skɘI-#LO>϶1r{Ms$I>y1h&v:ʵ? s;$If݈THuI_ȼUN\8t=/12'c&e?{{{t:dRt3\;$I$I$I$I$I$Iޱr{r'cd skɘ#Gvw,W)I$I$I$I$I$I$Iyd$I$I$I$I$I$ILA6!Dn^$I$I$I$I$I$IDd$I$I$I$I$I$ILl$I$I$I$I$I$IIq$I$I$I$I$I$IIr%$I$I$I$I$I$I$e\ɖ1L%vJ$IwIDrl0, F plx9-,Dis5A#1 =f?ݍY4K>/ +7O|I v$IR6quX=;JCL0gȸf$eU^&Xә"j0to=47@ѻF'ٱr 7Y$)B="7Km=-0:{!ͽp4ԁ#x!C7aυ$W|wo1)Q̒AI'$EѡRA 1q1@A|a>Sߏ.ϧdF$)f7{o$+SԗylHBŜ aV2*Q3;#vTTn(Z8_0gȸf$e=aK"XQTZV~bq@ vRT \,3W,)P$%arnՓE%Xh/̢cg 0pHty=ާ;FѦ38k]wy6y+q5"0RP_oCU|X`?/GݻVcNՖ߳KS򑩂ck2kiIAИ 8I\8wNg22"?}/\ݱ21tl!c&qoa Ye➯qQ3 _iS(q߸;g>e)`K_/L#zF1q;nl1JgXY*T*L,Of|Rbay߇ w 3;{bg߸6tO5qsu̞}uvNM E33Ժe~θMM1h#>yaM 9ss e׊ +x+sJ/W0ýjK=NS>6{okNؙv/+u" sSSL P#.r=^nj9Kp;l015(GTaΐq<3Iʴ\%Jq:E-gvI˽9j>}cɈlQ EvdXG_c={7 &q(|fʤhxs#gVĠ0X>-ΰ0?Ȑ2oLQ…y|[vԡG )iK@,M9jZ8n{3mrW9Kcuy~D}hGijRrQ9~tQ:g<&$hl h2LS0 *yYVN)x:0Ǧl]HIq|K &mcPivK|>t=d}ѦTH"ֶzJvVY9%˽mUjwz.Yкku}hۉEIHu[#:LᗩBz=߱ O9s-]/&jՙ ڊnV'ET"gg8䁽ioOugC}&bYҳCncÂa4>+1yͮI8~Djn:1Ǚм)#N9Y'Wa[9 h/͟MiDl8Y&)>&z6i=ɤMv̚_cڈ끇8(<{>,B?nj%IB[$!b~F+fyaΐq<3Iz=\ɖLvjZԮ߄v3c v)ɱ]qO4; Im_:uGK؁t]3#Lmp)YcoLRtakl"@ʁ[ODf}6|W3l*T!_1K6q~ӜM.jfE[:ǵQgLkN:iY,*WrT\#X SJyć #4TPʺ;<|?יQ)+S3Z…X4"imˊğ<@㚕(?I}|;KvIt(eg릲F~\:/[4a':S^bh2ovjJY>?;mPSK˴`ȆkzY6sɬ;j7y_V?Ȟd`7zs**qi=*EAQLi< Җg%bZ.Z ߰5J<ERV4[lo8>?/ێqwtnۊv u'x@$K$ k3'O=^Fjlӏ $8 F8ͦcl1[Ѫ}7Ckb pdV/pr˽7mS=fFF9>ԇІ1YRpu#?HA+ݩ7%pb]210ԞIkM 1pL!lZf" ;!\ o_ D/?skE]BNpC=|Scr)BnEU29C5d$Mru-lP>t9bݤu7qK IDATFrĹ37bצ|fw=/ޡTtHpp03!(n=WVfWeg Г^ B+f7BSЄp8!vuQ8&hXۆfj.gqZߏKYēGvP:ny0(FIw5w,߫0|(s3W3uq65ѦYеNsaT- ' s<q "P5Sd ӄ~|?!L7È~}oˆ6|9JO{nSiQSfj*8oHY] ~b_MԻ$4$'Qgvg1*҄AױE/y靻0󢞮6⛱_h 7"]jhԃq;thKZN x$~ >Ɍ9V?V]g &pNUo+0K`b(CkZ8XaʲEy64dP;N's_t)6LNї,Z|f>҉OnM6 [W2a$K}ŝt-m.ga ZΆ{ٺt27M>6̇p@]{Ľne[8iӟ9|$.Nq|-C*rɗc?_du"ߚm1<, xBNs/O7r9<}KB46)[U_} UGW9s>MQ<ʖ1QReaΐq<3IzӼ"U= Xv"b顜X#O~Kbx`[/ϯSSKZ%ߊ9r ] 7XR,2sN fXStm8jtUc|Pҝx"rW(DJi?*BU"u*\4=7PY'zE&%ЮSTqm<-a._탭ģi[s6PlÜ]R(v<{vti |ZƎL9gl::s\,m)<1pJAL핷&.] !3K ' ˈj:5\=btgT.I~&};Mڋᡄ [:(*G b Dqr!S~e'q?4 .Tȏ*̵?PWR6 2\(z6V 1Vx_غ7K%eR8!WEؠֿ so/84-P,聗 J~,FV)f)ET{ۉE.WQJEҪ,*RC 5`rBp.ᅗh-EPF6 \Ş![Sw2/-.%)}XV(Ku<=pO6"D0<ƹmrbnk `cQ#"-Qw+$Tt͗G`e&"}#y29C5d$urdґs ǵQtWud~ӿQb8Ps=g x 8}2=J>݋S.ty(Gw|<3?)Cڃьޟ6/fpj9ź3p`|zo,2D~ |iUl,_`VF @. }hdT~+" ?;]p#oߕD (-:6eӜY<;>t#v:JނZQ1gdc-gkA!z4c'!50<9%ӛ^zDv OgU'yo/ &&&QzHw55]3`>/B (J_:͕FRA% +kXJYY&g,$IFi@=寑u]8ull]3IqĿuW}8!Μ#b^0.]#cWIUBR%/)$%$Kt|}7|e skɘIRzru%[ {g5X+4h ?%azj9κnX>4caUy[Gg O%"3*x]aR 2Bp8g4xvY3 !5>ٴ_lyMޟm9V,IN#)g)U^Sωw5^?%C++ڭQ{RTUjRAkhP1CP#VljHr=?䦉}^u\ l˔6jwqu;Iv}Q5dm XUʦ[i)r$s/le&e"[ =Ubs甛.+E/idO9߆ummC\ nU3:_57u+ R>4wAmHSp'*X7>z|љt `rhru$ƹ0<4tGX^$Ao+MÜ!qL4֖a1,V=D6.֭dvs-#vGp+=CmA)[\Q,RI6l&Z!\ބ/$!4LvG 2^]˖)c3|jEi؈zA8l]voOhpm5"?""2*3پoF( d1zԢ2H%Ld(ޔ&2_9?]\Rvmj׮MeJ!/QKP6DŽ=wf"sM_:jPp;mө?mOУRjNg{n%t'5ŹZm)Uqec(5U-iV&+H^p_Ih{T('#8\%K>+Qt/G{o\Mlj<~F\+Q1f4ZtZ_';qb+bܽ-j x}!VOd4{u Llݰ{%:X֙׮79 eyvWQri8MW.I7+?LXދB"3y]vR1ޠ eJWz)n/k%Hݙm$/f+S!JĤײur j&17蝜LS〓`C?;}^{$.aΐZNb&v&9N4ڙlX<nRy 7LT[WiGӮ c^+|#!hS~1][s]\Z폃1wU䉧}Vݨ曳LB%rqV:q`( Å"oҭQ~[2} 3sA0Y+2T3N$tT3l-"HhÁ~RI,݌s㹼3Nq*ģ&|?¯}py4fN7qxwZNKO hg.=K%vhEzWmߣMF7e @idb3 VlZL[bXÅ0V̛ow4:h{Ŭn| tԃkpAhҊ> ㇮?=u3-Xyu{ǏGb0]o$).](̼g΀w?\/˧.Lm^^#3-fa4_Ȃ/ͅxn(bOJ׊/Twł/cWu' /`K*{{?0ƿDMMTK~+֟W%t;8\(X%jt`Neess$j9W9sb8S\f~ԍ)@u38q7:?V{k{?S]>[ہc[b ik>o@G`E]iz@:蔲QL\]1L=ʧ+ \~jn 3?v/Ze&'< jup',5cm7_m|^ǘa\ŭ`9ڎkCt;gT?4a/S0 q ~E톏`E %^%7Q{':‡wȞsEi㘺p=&z←>7S8w gM:\< ,WG1ʕ1peNF[w g^ $fB<$9ҥKn:vn:BBBi׮]m!"_7 d.| v9l}vBBBr;9#:yȺ#c`gl{FUg(5$lc|6<<󙽤tIMϋTq?O XkG4Qfisqz0gH\-'19yn % { 2͘f"##iذa[b۷lV\dV$[¬ztK>Z,mBG h-g$܈%1cB!vs2FCY{Ff_-1WĪ3іHfک0}sę_&[W  z$)|$UG0ݻFS GpY|Z|D@޹ !Bd ik!y1~S*W*<ezvr%1WĪ34Moili=-)=5&81ӑْ00!Bl#a-$/f? .EB\u&KSǺ_V'1WĪȪgpj!B!B!B!rHXRw)MA"as޹q?0i=_Фba< ػSa>]u՘A~Jd, cêTZj;՛vxH'wIyB!:zTXSN/Wig} rV; IDAT ,=6Z-:[SEfnm %JPJu~ހA'{};oKvsV=4·H4 EQܲbKGg=vV8$5aΐZNb&r("܌BB;,-c9ZΪQqhp k[C]1zFϔZMcX| _Sѧwˉwƛl>Z| )nLJbϛޔCyt6kT`/ԍ ܿ7um}Ih1Qߞ4ua-hNsmaA9Ɗyi\-)ԭ< :\ؖy]yqט9M^qcl~;-Iq(&/}/"RO9 ײ`F.et-jỲ{̭ ' qg";JG}Ł,]>I6ߎsեZ@^ϛΔfq;|98qtÁQ$5V~}ꓬ x ?Av6?<@.ƶh,|f%!]Zj(1[KiCV[yIGMiBuTMnaOLTߔ8&FLM/n< '߅#޶v61>[bctAZNTáYhԽRW3ugMjD>6BIndڊȇt{|.g2tsͯoFO3f_63qpGV% 5PN6lK\k5a^O5ip}|m560H3-Ҭb# /xԷգIۭ|%t28켑]=y#5M1"Ms;E;ĵ3'.ڏ_ճ^ͭ[ 8:jS,V]ZM:mB>MIEX ɋ9ݨS[v>YÜ!qLRsҥh4fߟܹ.]S/|ΘR}lQFTU6ET7RJ%Syj)}Ѩ.mRJo|WSƬWJ)e:eP^oRU)@5hkRjUT|JocPEjקP7)I^2ثXNMQmgG1շ>R U 51(C9禓jz='eSUtReVW4Wjƹ{0*{&jCLcJܭjj|JN-J]Ү*_]jYGgeSbڑJܮ+%GUZxUo'6vRҏz:%jgܚUgMJ%SVI'j*}rs%&$QLR +l;ٛxKfJ,VHM}u%핣m-5dr1FSU ~u~qPS}qҔV,g:2R}w?_U&ʡ=VQ{U_\zn#.yCkoRgLL;ԏmT+EceVQ +'C ĨUW]Ih j>RJ>6s*g:朕ٲS)ƪߒ3A[eWR(;_dGc68H٧>F ^z*A T/FMT T#v73`Fo/FudB5e{[&(U *9U{( eh2OE2_ڧ]g.Fwe}5dKz2Ǫm#*+jPSyjSmm5T:=M>mjMN'x啭!XJFNf¿˧ ʷ+ꃟ/ldu5lAe+|%ջko1>{?F:7*Rh~ MWindt3TuǪNݍ=5_SUJ)W;;wGӘT Y)ozNYUYll{m~ђΨ<:72nTFU1("]OWx`jQWl1*tZq:N.WT>.vJocP.+vSR\ި>)['?U A5RZ6-q+ԈA{ݾP.ra e(5LRj jJ{[t=ugVI^~ԬbOPWtuut&=bڹy:x΃<:y)UfWWl۬6oުv;.|p6T[Mj*fVwVP[lVaȳ1U**{Hb3$圼Z7[N7K*ffQe©Wj|?:xcK+ait4}{2}Z֮kX8wە֓TԃƙR-咵lټimcí%Z7ܴ;ZȾK9߆5 ڎЏ K/8E|L,F;e->+RxG-XǾ }ǾAЍ_y) sbLoI`zmlWOHpI.>=lo)9<d%¯2oyn% .M?,Vfէv˱hQ$7 N1gv4׋Ow3 iT(9h})xMm V_:N *j)#{Z{3WĝLܣxx]"viXǍ[9V Qkxs\_J3 th.VF{p,/= ^934(z/CBjNn#aSiX;%kאr=LoM٧DI{O\;m`/۷ ,q]2ΏN5_ZzRߎI+خGA{O؞\?ʹ9V? c8p=$ϸ84k>F+L@9U qܸp l*Ny7N?F"+GI?7=ı(*aΐZNb&ēVچ Sbg8ZCv-Fo \oWןQ܋L(Z#ESU"lQso?f`M7'}zMzJ3RN`^.;A6sRK U!Ob/9jll6, )Qʝr>zƥ(thPdZ]GPVÿIOfw.}ߦR4ԃ7{uE,],ڂlLCāO\:MۏQ绲|I}5Qi~Kօz-3ʤYцs6Ҳgsn+'YїV}L4[>73~{o^^qߵ.96~TmS&F> t||SwPW.ee;7`Ndk\ڼ6EGtR+!4bǞ5R|9> \J(ykSm"?$.]ÌsH>?4KG4ؗnk-b X6abMe+o/;}Bם5?|H>R,f)жүU(]B_WգdJUq;UjeGpܔ xn f6qLv?żĬ [6ժNeX)\TFKݹLyyRՇϙ<[sSw>+-~%(ÇO\4x t0)pw%i]{rD\FMH$+A>G7su|c-c,'1WĪx\mj8u$;%K4s4aSߤQzY\7sNB4X7Ka%3hkCzY̲P5R(4hIb3}FWΏ_9ڠFU^Ά,7`p-DGtӛ_)STΜ!|noJ^Ղ{1nU5ӛ{Zg|4-apێyRƝ;DץTBL\ۗx^}T͝Sn̦=?-fybz!Hʥ`A8ŕft>\[G|򻠶_$ )IE_=^LQ\b_0_LYO|:C\CirZCH263qelYӖLH~ ħG Z I$ezPBuk~OgDџ)_~DtwQneɊlמr ۔@Dzf6}93qgwH\O}ň)m꥿Nn૑8ޜ|,L6lAIX;%Lֳ=jW`6y?^ęG&O-a̬}8Ni]:4'_w?;>Rıq szOXrjjBĭȔi`T .7غa/Ju6߃9"S3y]corBXW{)2" v4Y-Sm?-e!ZuqPSZ=TL T/m f{M;3zaGʥz6]yrюKq,KEoˌN홼.=Z_>7vK3H^VZB,bύgIU:9aPq܈IbIM3z''28!!6;NIKb3$ 8ɦbi#'Mvf?aT xӚ;]_ƼVnGK{ZT(p_GזxǜnpW*-e|1k`i8yiĶ?v7>VM^|3oϐֵXʋ2 o/KXy/Uߙ1$FT7tw|8g{"f aH_q]l wcƊy|Q.F#uU"o%L#19\(&ف*}AҳTo&Q'J5L%raziRekm>tfڌ&5)!ZO6MK`F(_:]Kq3 A_0nO]1S,7qoq/)C\Z⩗aTQ8jйx'dӌ/Q~.sM~ʢR n[4jHu0PPɟIg]ј/9؞_vL~La:ζѸV #'Cl)CQ+fe/ H(d"f\炜aWdXA܇3@ٛ฿hV;-4_[mߣMF7e @idb3 VlZL3R=b}2S(s0-fuÖo?#U|ĝ=^% GېV4)<5%\L?tQ;4DoYʃi[ƃRa|L@Z_vq: IDATxX:|.4eL}p/d+3j;zoo'kp۴6 N& oA3+]Vȇu4ׇ6ik2QYle흜bNsk;F`YLhG(ӡRujqedk &́f`nX'ǚY53Bn˅)2yS1 ]PjFYn(zwFMHf@C}"t g^#u"/d|nBb_"Wp26էrH \8% В.g< |\1$N@鋻W9$9Cj9n-.^^خ fuݯkʌ-N5͚C8^鴠ե=R_ű_ѻ l|eZs^6NgNԍO24*2Mա1mC{+Vk)\LWHz9U1y4OxWn; Ihfv|)Q"gNNJxqہ p[7|R8<8槈4dQ ޅJQ$ڇnɍ>X?ASuy1ݷΚMX:-s=;gs7d!gc8STEɹ*Q|3l&nQ|}.˰p{lKӸǩ~iGs;)Mn￘AX̎ڔqa@s+֟W%t;8\(X%jt`Neess$j F{S;1i.Ɓ_0eG(W |I"|~ǹD#_;aiUrG[ٚЦk(DNsXy35k˹W/@ j5Dϲ* H.W5>0Lfx!FYGe@g[c̰L{g.VmǵkZ3]0)MfxTR"uƇvGT||_U= xC;dO9QqL]8FqWIJ xoRoS*:q WmczMSc=Ͷ+ah>:נ::?O:Lik2H:;Q03iTw?fEߡxC,<~0yI{X!Kw9bOS7LGwo*euEd#c=-4N^*߀rqtnz GEnؔ6^¨3nGfX=q05zj_M`c9){rҨ/s?L-ZH^|z4+LS}hop94NqAΚtعxX(~b+c˜<vy4}Ü!qLd6g\t֭[gێ׭[GHH7YzI"_7 d.| ٴ.۷o'$$IJJr-IǗXvr.Gcg%ǘD4ٟ#<޹gQ`f7Ap 1> y &jn>jO^lyΧv'7}?/>SS?*[rkG4Qfisqz0gH\-'19yn % { 2͘f"##iذa[b۷lV\dV$[¬ztK>Z,mbUcTM!V8/_`p&_>Gkn9mG$F,yBa_sAѡ<`G-ZH^Dbb"^g]M2I4QF+0gH\-'1bD["kXr"Egr$ɚw>nn]BvQ7k KM!W$Hư{hZ.K|C˒h;$yݭ,^q=Sy[ ?Ʃ+ъnLWwi=s;9 $fBXɦŷ4۴rw:,NNB<R='v:6` [v2aWgv*sM-ZH^~\VㅢLyuNb#$ U2I!B!B!B C6!B!B!B!,ٲwH!B!B!B!\}f2rsB!B!B!BdI>d3{!B!B!B!D\t֭[gێ׭[G\\ ȶ !B!B!B䆐NBCC >S7͘f"##iذa[b۷l6O!=jժ+W̓.'l߾]b!:wAC={zb$lc|6<<$O;}'1$9Cj9Yپ}{n'A$|r)B=62^ SmK=^tٻU mV_g[T5_gϪ;q=BAĦTMٻϸ(>;;z"h{-hl5Xcb-Dh4${WP]EnA)9|?{q;ٙ7ֻ'ltVk O>sez;`Vc^ ӂ]i ]#IRS]o*BsQ ·LfDqY9y[tJ=}sPȼ#̊SZ5sL涝FvTV(tz qaq93ImJa6BT2ѣ-|Fh] l KyyjF}]4Ԗ0X`Z}`ӵ^u9iמnuwTBǴ+S҆3hjz8`QabB]" k~z i捽 Ej2.f䣌hHRi=*6w'o3MqL`][(;L V3EM*Y^38?`^SMmTWe;6$A1mJؚRcZKoyKJȃvD8Eʮyo*[T o'_m< 9Kjܑ gݍBϣÿЧ;l"Ŧl5q}7̔s>ڙza=`ĩ.直3̃ӹ/EwߊOWx>`3˸|K[EY hfv4q8Vt4Ehw_Tg[>10 {\==(a:'eoVs@o~ e[1=Htniؚbx9M> URؙi0.Ff}a5I"r>K>cXϟi-stWՈNvȏ+^鼏Ŀp]NؚP;Y?A(T W!wϮ(fkR /j¼#%fNxx{dFJ񈭫h>mh Rvd(d^J&jQHqc_njøGPl115DmKғ129'c&I9VDO'#}ƚ]p>s\! `ɸ lWk훃 2ib1}uQl4^4HtlS AM,Ņl:#?/VrbjO6uԃ b5,*=T/'yy _ːQjs&C&D~u;'Qң)͑J 0x9F^O=q&g6$G YNެ$$̞@v95q'Ҧf0fwϝju6vMSϾHC6䂦%IMƞ-sܚf}Όq^ɭӇ9I=xoFȩ>xr~vgq=?yS<=LjrytȜCsCF4_؊"7nIzN[$C!b>ѓv jm В129'c&IyڵkVͳΝ;E||XvxQXDwtGckscW1hwBq3Zr`SS1U p)L/B,~B #1HHµPkk]{1OMJRc*Ky) [ݜ!UOW6ݯ&JRbȁҥ,lP}ubv a\^r&ҋ%mW̽?05k%TD'()Z_FŊ7V½0-_N3bBeUZXB[VXhj}ܻP#NAcM@lPed$/qg[|& Ľ*]ĄukW>9H&۫.>tWxoQ1&#qksu=1=X'rgH/pUeEM,\^-h)4fĪű^B=J >;6FT4um"o1_ s7mFf#2oVf!S⯎ª4q){ "J$j3v>UXCR& ҕ+eBh&*j[{kJzD-.eDbVDc!#7gz~>&=i}I+xzi]5fg&LxFSho~D,&Q/"iJ9qDKBh/iD bۇvB?ҴS< X6-*^d_Q8>GM+~Q⅒G-&̌0g)O+q3IPKH_=?kJB)' Vcp0h\0՘ ĈUӴgԮbBXT(a'LU*aZb|Wpvcj;WOWhU&]k_N^Yam*s'Q"4/$lB$'>hPA:X 0uu/ :qw0{_~ͫ7 6kמb#: +P>Go+x [C8YFX&:W =(~Y[ژܪ(m~ ɼ\c9iKIQXES;Q/PhڏBSkq"Y!ĩ) ]C1TJ%ؽR_.f ugnɼ^^D[q!x8~Oy*Cҕn8z8q8|8~x=ΜGCa@q}Ӌ[caxZ\B ,_a]A2C5dOa+Ç{عsg72ڵkP7;7 tHS-]zb3IZOs?fo nwG٦t4AAA**PXR\ۙIɶaݙ`S)mQ ŧO˹kR=dk=?C'ѥxfqQhl)b]8UBqE_T>|:#<#xSHJK01I;|z0f=FOD1E8a@2gєijiؼ&FG )\Jzi͡Vp!&̦x-bsNTY8+pjҌoВ@BOsvgQjɈyٱuCk^̾jGgPCQ~lRufڦ;lc`JtYm~TU Rdeڎft=G+3r Yc\;ϥѫxJWt`DCCy5(_XmZ$IM}cg6f,ˇ+dRfc+pv3ٸgۗNG?6B %iu4e dQݱYu/6^d,I&dj޽USZٷbϤ#zQEF,Ʈ ],>B{i?;z\ώ PNA5o!uf66@chj_y\2K?O|dn ̋;=,~n ^>ǂ"n)2/ 2H%2F`V +Ps)8 U`_ AkJ)!EF?^nל1+\OxWfyc]}xcOZv4'M@"cZupo ԿjUhK`=u= }R,ofi<ە&a|oԯ[?^亾k|V%.uh4Nn|DH" y_OU&5y->msϚ΁ifr?mŲvV9Zz~Jhh4 '%̩(V:P;VK/O ?}eBYHqk`ņ5*2%3|!7yFBSؔ8;* #l2#A*^ӉJ؞%f>;0%t A%rبڻ9?I<1am| O)P c,-P6K,#\3d zBಱ;}>@%0yt?I&>bY]du%֨8.t(2',I~_eիPR1](0-ߊ훡Uylʗ e/N\_3>d_ijP9<5P`Y>>/+ѝRŻ%ӟ^[WLXeTZTF9#]B5S>ϧb֧tNR>٬_~+xըJ9\.~v!X1&W^S~\靖e`DɺҲk3- K M_xH>k-Y IDAT0RA9?m踒UP`՘VGظҁM[름&pF+s"5+ZVӝ~'nMU]?jP&6}wsڏpIݦº iJuc767朰kY'߆z'2qt\[4Q-0 QƯL%kO-{34^8CnKoMȼo)1u+ /cY4v/5DF(p*_wmZI>ĽQ8M)7*m1llPGrb6$ %7UR`rQNE11r9'c?d\sNL^@ZZmJENyL6cq޽/#$'H%2R`h_dԆ-|S޾YwˍN]kd+=D9U1,O~ ulpB'D klԔ?+.Q3jm kIdH lǍ ~L_ IģuxSR<)3+ [0)4̟7SƝKIz­!AE1R{n=K慒D!I*A129'c&I)'r4]$S· _ݧES`l]RQ_ tYڏHɿPn,yFUm$[tYbnB))fZ"k6ptVKGBGOR.T :KnLN5땳,tgU.1I4XZ*Pصc@7N͗N1z }ܔ(4J":*8B>$LRFg)J_Pg|udxao( /Aޑ%Ihio8rl<5%}Sҧ: ;щgRUsgqTDic }$-=7Ƭ OzCwV9u#;i0 #^d[ϝ]a)~jFè1|3{^t)qwI-Wjn*"ZKjXX( 7y:Yo*1҅HR~c E t$²Uj~!,bɘz$1*c tH^.{z=W[ 9EZ((H&9ۃ dT{gթRWfE;:y\HGw+=ճLyuDf#_,/^WJaR;]'άH1\БYyXJ}2- U֭9Ci=ק3@q؋vd6UqA;7tNRE/t&) 舥}}T_>elLkzMؽ-vKޑug^y1YZ$QgopݪXRT)Rĩ,>%_Ri! Wd kɘIR OkKDqdD<)Ayeb"ҔRpf--NGF~ўSzeJzofD¼*URrM KK8š)ӹ s7l\+*94s #qes!Bfȳ |'mS07S** FGYR SAtT4zTT_͍쿕֡߾R`zJpiڌFO z뎿qΟ=ٳg9{fj8uR&]>nN)bcb ԛl(ڕq?9=ʦ sAӗ|Ck|8m:?+՟gg*֢l1+_ ?rֵ:أ@PyVbƶBz&l! |]r~2g^lI@7[b9h JYǁ\7WϿn2wۭ ń)Gp|+@U|SͰfsL& 8Ocvce= w61<){(o8a7՝U޽@g_zױʤjGv:-!C)uGt\4J[(x췎M*ǎU+c1gUfp%%Ci~u,4q~ت6W?0z.Fla|O6sE.x`|Fc7eژV Vzv2k[p社`D?Q8vMH1uGvc3 ~팷\9._+.]>d8`La[b:>.](kkDDpXK]g_7_Χb`8U,OqOY/D5Q`Z:u}M30c;p?MJϙEU`=wZ6PTfW"9+4dE22/?fżp ?MptoQ6/V%MBVAIv3/e(dHOҳDT{sO0ȸ朌$IX986E6֥4U\!I8u1#9s2ݗ>AVFJPeQZcC󒩏I9?it%?UU8;>l#^i(s~eQk<{+2},V8ُ-EKЪM 5dF3e5|֭ v+,w4A4[@mf[괚![* .&vA5ڀYT{a})7b"FMm FeXy$W߭L2Zz7Þ"LZɗQk'o!{'+"oҽh掔њO6yFR `Ǡla |7F v_ȶQ|=Z|&z8!\WN$bB/9LAIܪ`^<Ǣq:G:[R9dј/G=t"Z/"ZS:/`mU@q:Nt}>ǁmjt˶_:P1$.{AYK1d!OؔH+%ŲZ1m tæ3Slݨ[F‰cƲ|sK]{œ? -,qk8r]@aYq{NRmd~]>ǽjS\+c¡P}wN‚hЭ qz5Fmoӑ|={ m'bـ%Ru,$%JJa]0Rcfg ]z$U˫i&/4kݠD~a|`ҝ z_9ȯf0tujm֒lȝkb9q ?܏Epd,Yh~G#\ڌ}r& q]aB66u8u2jZK`xlkNneP_|4lHB7GaFҥx|v/Mp.Wܻ͛q:#LKخ4}4ܺJ;h1BmfK).t129'c&I/nάv#;v̳ ݻڵkg:wUb6܃`i;^iX]vBnWKʱ\;){;G^dF!\9k?Xx%-57YW{! r >&N9mt:ď5 oNa;N[0{3'bWc*ys1ŨY<;.(]<xaq9SX}憩iwzz=.]Yf7oƍtҥp4ttn Ϩ(Y ;h.=~]}.=[ql$IRaGDr,CCh,)RO93"ǐMR!yDŽ$IaK$`yvkUyM*8" БBaJedXMkIZaq93IʱD["'7Y.{LΜeayEDkkOc)yM$)|Hr7T/ 9ur< pWNe? Pe@A'H$K8ۅ?PȼD-NBaJ07']U]OyǝҾ)!s2fs'ٔ8w_YyNt:$I S=ht:m(~C :$I&̽PЩj" +4t/Mgcyd kɘIRnǟ$I$I$I$I$I$I$)M6I$I$I$I$I$I$IʡɖEJ$I$I$I$I$I$IқQ7t:]An^$I$I$I$I$I$Ir@oiڂܼ$I$I$I$I$I$I$"v#;v̳ ݻX/g$I$I$I$I$I$I*k.$ؾ}pss4[z.]Df9uoƍҥK=hV5~TشiSt!00PBzd3 8a!Fr>7 63oxY2ޙ{2aq9XIް.R$I$I$I$I$I$I #yM$I$I$I$I$I$Ir@o ! r$I$I$I$I$I$I+I6I$I$I$I$I$I$I!yM$I$I$I$I$I$IrHN)I$I$I$I$I$I$I9$d$I$I$I$I$I$IOFQ~+'tJ$I7IDs1Z@JX[X`aaM+ec^{6̥]~YXy7ķlHTDc?Zf]c㤑:M!Z4x2>ySPȼ#Iș<5[GS2ל1*O I)x.L[M<@RgwoÜ%v9y&K$bGOgl %=#0>}SR(oQm|pT2u׮>ӛײJTjǩ []&.'b=Yr,gF3X?йZq5*Ll߰8 Q;"x2g’b^xڡQr>iߟE % E Np]NRؙQQbSz­QYϮzx*8[1dL QP?o3Mq"MZؠűt س,;3ãsl^ȧ]^W#:b!?v{>JmvM;KbkBmgv PI-)=": JGep`j[Ƴs-]C+Pc#6.^j?yGJ(̜Ɍ[W@}:$t/^k,wL?9eM /xw5n ̋@S1 ɵ?]yLmQm }-#5& 129'c&I9VDO'#}ƚ]p>s\! `ɸ lWk훃 2iJ:LCH=7CP -sܚΌqX%sŸQIJVV M;bv0_y\0ePH ɨ}3=ݝ[Mn_4:0u8 0x9F^O=q&gǣB6C=u~YgqbXVcI|~ԓ>=Dsh,vJOV=RRw'udJ^^E7ӓS2BMuB<_0g;1X5i,C:uy. lVDlíL8=E^ [4'|:i$ڃӉi40ݍ9tk7307hX [ct 'F^<Ԟ lDJE%:ֈ#KJ^UP cI~O!lLL؃Ew<NrG)RNAF6XǨcu-B !E=bDᏞsK{!Ep?傅 ^738D."Sڴb{ /P؅ IDATƬn?CSMƮVAc*7i|Hƃ\дd5)нXv@>g8/L¤> O۩.gr>w}?c0/4wwɼ׉=q5a9ɝѓv jm В129'c&IyڵkVͳΝ;E||XvxQXDwtGckscW1hwBq3Zr`SS1U p)L/B,~B #1HHµPkk]{1OMJRc*Ky) hŕk b LWjuY1xclI~bFTF$}T31?"~ne,x ӒŎ=#&T6^I+(<%SEh8$5zpakai ݱL]őaC]J|~()L&O,zZ "-cd\R 9BfNY+]Ry5]BEL+K;:q~3ajJ, &^*wHB?+qY [Rh:Uqi %c#{8*#*?HDۥو̛ }WGGa`Y=sm?;q_VXhj Jl]Tˎ'sv>WcI&MUԶ֔ wZ]ʈrŬBi#Fnȟ}LG{ KwRߜ/ςį͋#bE饕vՄ0-!CN_I,GXVhL+)e/Q$~%>;Z&m lKOX/bڴ;{}5F 6,FEXGѱՄ,%|; {e}V3n&[6tS+~W1)' ϼsƥSpNXuYOMRf!Dy [7Q,rɠw 3[{yмpsjcv)V> m,Ѿ yQzx+=PkUj{EHmB{6&Bm"*j(JkDB2/Xv3D}JHi%[۾]&¦T-}a(}#X1b)jan_JTlXrU+HG{ Mٯʼn;WHlRTr6f*aΠ-tDLl)-Xm%W,*Ғ4Oezܒy1S>&/Wq!x8~Oy*Cҵtq"yqaqq50柣{ .9!Ç"?t7.?|Aetmל1?n޻w>ݻbΝyzP>k׮@<+q"'*3[c=f81h=@~޸ݛ{\'>M)i:UTÙf7~3mú3)K:ʢд 3%eTO7#nFSA.UvUo/c dI] >!(xa socNSch15 44r0vӈ;5YƿQy'ALh.ݫ~8j1 {4s鈻syKQHJ@Ȗ"6iE*U(N Un<}u' (r18vfW`l6˷A' G$>c}ϸ6|ٸ}5fi/_>\ r0$^a#=ؾt*=*jo_Ӕᅤ1+Z.ҭ4fBKӬfiVF+8c+zp^m*L'Dɫ{VMin0:u.W`޺-lnCl8GoWs.VND" .xbgߊQ>ޟW^ytG|vcf'taвEזo5s=;6f@9rp&)mQ #D];~ӑRݪÒM4;X5&Gr:48寈Aśvk|yŶ+͆NeَCZ&ɶ7{[-y_3u/s76B/cAAyV$#q+G(nO|*md0/q\Ŋqs" /d kɘI'<ͫR1ˮ]k:̧%[,kcB 5*?xEqT&=o_7{ [Th݃~Ы7$F{{9Coq[ 3~/6VD_t,ٗ-Oݟc ĥn=<1F5I͛HI$23BTtJ7gG!a$M&xy>Yk:Q)bۓdՌ)St`^HwxD\"*gJA:3C4Q0uھot2bZex=L֠'. ^Gi(USBCQ8.fNEyls$2`%-3Z ޾jQg}|]_vbM*k9v9-N\_3>d_ijP9<5P`Y>>/+ѝRŻ%ӟ^[WLXeTGZTF9#]B5S>ϧӧtNR/4 @Z;FWv;8,fa -kȾMRRD)Ie,EeIDIeI~eMDX̽ 3^f;-s|=о?3gH DOI3I;NE,ٚ&u}oCm[x/1]29eA/R!{Fҕ/<În%ޚ%,k=LxGFp1N:psn5lBp3ԯKSw2~oF-M~4 l/PY3'&1-_*G7kBinӆTqԪ2f E5'cd4xΪ)kq3=meqHi,q/ߞ3X?#&ZIί)H/ǙG צ),C؂hޔi߳HM=X~8}XJvmfE̫1u[iUw%#7?mn-A ػO|+F$rҥJl=Hؼ#_D@1ɝrJ OH.S<9NBP *N< q, !yqk䜧:WDn'K |yn6m#&i0)L|+!_`.tgY;qWaLWp|x\<].Lm)fa' {wkd9COOL۫s;0羛x]|sYؓ |*,yz'1&w*KDMWţ F3Y2N -'0p;9ӞߊI7Ӓ (:0`u'Uل؀Мn{RW6r76;g8Q]y^^,&7.O~-q%T<]%e03ſ1s%o:]3huw/fx~I`@y;>utہ|i;v9ݷ2oAJy֘?g3Hu_HQݑqܶ#< ///rP_:јąlqq@;l v\Oר"vJd۷Y9Y첌 K" Vm0o߶闥|J% nHq;IPDgO&>cK@K:Oy{rʾy l_r+ l#! qi ŋyp$v K%TAgy YLX<ـkWKx$a+[ԍP;'ز-i(Sה+d"FVf<לa `9dJ?+[2bgѶxg<.I88_Hɏ\:ỉZA&1%$`%.\a=WzjV^Ͼ36gJVfիTg"i؜.6 ܀ej3*H\Ϟ+i4z" j^ЏΕ6әQ4΂;H }frY.ۀK3$,/a7=1*w[yXt!|}M[2W85' 䅐eX:p38?|-Ğ"wb8v"]m?%C΁{†z|{+ȑY.H"7nw"6;%ↁnex~D 1^|4a1@ȦyM%([snv6߲׏#ͷ;N][(ُ=ɃOB+.wyF0Gebf9ԩ<|v{97'ݙm FVS]ҩ- a#>gxgd*pdӴ^fεV'L+̒]g?^2%ujH:f40 }7/Oѱ6y&Z`J "m"&?*.OqԍUXPMBz(&VVԮ]5zLu7˾ͩzۡ/[W~դس3)V@Sv5<ȏyV܄HMj3g!wG;yЂma›2J9[ȕfۖ~|m?{}dJN"zƈ=]X}+Z^]uy] ߌML֮Ƶ,{ъO6gnJset&O[었=oO/|ofk43o?ʿۓRcM`J!Oxfy%rkߎ) {]qSwG\6Ow{ְh )B'1X(Z[ϝ wqXwK=P4Ն!3ַF@pS/HJp&#&Ufq:#b_gyV89/SaJarRoz؞`3iW(߷5W2nC _:р BOS-#8_x=1elߞ\P8W6a;k]!6~>~X0Q~teeǼږ5n6LObNh4%,3yїA~N-;:vFrCE<Ňjܶڄnk&: ^B9pl9gx3-_Q^叾ѳBȤ|}fٗaovމ+ W &h IDAT1/jˎQ0by',&ZęsN͖)o [2󀑌Pc(RVq70x"G9(^T9) d;x'BoojR)|3-"8/N0i})Vo,ed ssYfr?#ӛf>?=bϙ|UKJ8[gjxD>"+ץ<۱"l$'1Z(_|O yO?C]y|70hRo<5g(e?ǗRZ8N`*\·\IX:=F7#!ehۃn\bߕO~0bX&c*Ph'̄bw8ox/kL!t4ZGո/+L{]4 "Du&^YFu[1j #`ж1 ,M/*A96 ^~-Wд[`xroX@\y#uLӦ:WDRptˬ9m۶ͰX(~:tp _>x(CqzͦkٰaQQQY] hs \֝đ[1/ᦾ<02΢EA_7L2a:춎 P7e'oV.wO})&ߓŁ㖌ڣŌoSaTA=6"j&O6̡zu,dؼrJvb7ݎngϞ=4j(K[hѵuMEމ6wJ #-' Z- G :~OO_rγ_\%>6=;spV'M̡zuLyНlf궓2p""rNqĝccx~/!"rj㲺'_Y],q3 "xH,A<Sf իTg"wJ2ܧ.^SȿEI65knO|aדia`ۯ}m5ku1p""""""""""""r'nNl⹌,͖#YdZYx;brp_۶m [+pϰd.V\I ,^ŋ[lIͯf5jE-"::.ilmڴ믿Ζ]fذaBs\~;w?w~#Axo~3c>[ﴩn23y̳aÆ.]kݺ5axlْ-[b۳dIO 1֪U_KɭN6q-גlaТEFJJYdӏ""""""""""""zZja"64\dqR&4\dGYL6q-&)'~hMԜ?ڶmf؂WXaVFnѢEDGG֥7mv/Vl"""""""""""""ّl"""""""""""""NRMDDDDDDDDDDDDIJ8II6')&""""""""""""$%DDDDDDDDDDDDD$dql"""""""""""""NRMDDDDDDDDDDDDIJ8II6')&""""""""""""$̚]8wNʬEVܹ TRN1KDD\█dY""]8T$vڅjȌً8l֭޽ҥKb-.DD$H/fݍLI;w:u`Ff^DDa+Vdi~%""DqKDD ,.ҋYw#SlNRqIDD(nHv%""Ef ]idSB1KDD-.DD^gd7v'j/^Sܒ4mxƵ'*e;3I\~SqϪR˯Ʊґd]}8w_aA ߻S\drh:,9ƕ8pVc.]WrQܒ{}eOS:RemU %n{m-cSy@>;gܘ4zGP<" ?#B/n!U֎!z974x%-qE~z"otPY/DIW[L=qKf ΃fd 7"mUs,,YާMZ̋Uĝ;_qdF4SH"'-~ل,wokfPɶjs'$4:8 ?;Kݸvi+۶`f< cE9ھ1 x0{Oqdo>=!K:_ ?AX0&\NV>i^X/~9x"4/^L^8%~*$/J7eǰ'fوT/"bΟV0n}lKj('>)VoqۍiAբy % ,Mݗn,HqN>КJOfs)؞j#(8U?? ӟ2s{f)/w}fԋ!>exSKH oՋ!0*e\6{ ԤDh >-QS%ƞyŽitkXsC{r bI?!-Y6@_|RYa`G}[PX%rmSR+_& L-EP4=}̪5y<>=VH`0- IߵǰhV.@_lz߀ɇlחE"rxYyai 㗇QyخcoFūtʠxWv{)n^Ԑ2 tnEo43fqK2EH:<NR+UC5 6iަ+ 0pX`;]^b|F٦3{O/i`XOЈ֏gkYb!ЕOqqK{8}Mľw|٥x} "Z`{=up,=LmW R]SP0y} *D6YW|2$6_ Wfpz|?U"po>^Ӿp^4]֏VO.!P>z$^'7ń:oXZLr{@L~31*0N.ƙ]?TF̜L8zawٰ|5/&L=J@wbw~ưWWX%V |\>bO=sϩ,kAfwaf?քg6k3S܇>!?Rm{0k]Wm~EޛO˽K,֑|&0Ꮑa.HgGS-w#2?>;rԇ̨xu2x%-za'IU$gq}tl ίz]S~ N;W/n|zɆMQKfPp qg?nû 6]|ptQTp/ymoTw1Q;fbУ[=}\qEY5n\{ߏj/h7|ϛPm޳ox&`2"Bnދ-U1f^]N|[MKZs.wI-&a\?S6h(=+'2"67~/(&.'~^ǦV MHV'x؏`NGɽJ]Ѓ Ӥmw"^,;@^&ɖk+NF)韫2۵\+!"fZ ~r(ߍ3A¦s>cx3?L@ֆ Bwc~f~[m1 7|=aJDa``«dcnVvˬ˷lD(f~=Ju_gU/\s06na3s`oϫΉUU`?Z u|wJYK^NK!x[@ղo,X'}&ώA1KmZ 7sEʶמ^z4ɗ%)ylV~dK,G/R/@%&Jog4NVNgb9mШA?༤*C}^1ؑy ۟r 6FdwU\E~ S1 ^5ӭ f}?u;(-4<+D,`˨e5K$)bcN'0l|6szDض,R' OuWbmcKm[9l+ qh&'ǖ6S4:y;Y7u?=%!/;8yF1ǜⴍT[Q]_F^N%!ޝmέHȘ؅#dlVjsoӑ#:uOw.ؼsAtDDa``29?ԧ-߻wB(ܭP('lI^Ԥ E 6-|D^ES#Ii7%]7<y~Gv-)~ KuYqw]OJك*;nGJ7~='/Z}H/2;NH;ĥg87E}z|dC+/d`77]ī$Mc)$^aвR"66*wϥ7x(¼vN6XRSSzi0 V0f1IέpQ cIlQ?|-7ðf݊a9>~|/G^aXwXݨ.#wĽC7Vg2{~K2b܍]N|)H7o儹%ZRa`$Xᆻ%])y7L^|?0/Koͤyd =TIqgLNޣ%YFpO۷+x>ٝ}mv B.YЯl&` SS`䲳h..$o“4ycvl֔!evGq1G5=W~nWj$P̒z_XY8ft7!jnnI&pMTfft|=yk8*X8:y:NK edlV{lBj6+L1! :ǧKNТK$ʸ٪1"%NHyIJf&.5E0Hew Lx㽄=i-ЗӨ| $v O ھ⧵yIu78# HXɚ Gz^_[5K+.cǶi5/7Lohn8I 0q0!^z0tB_Hza30n%> 5kYr2.vnLsK-M݂G:D1 ke.\lZI*,^k7fo^l[ Y3)sv;6qS-C52(^ɽGqK |fLe*>ٖ+1 #Knn=6?gOdR9{Jx/~^ʋ-5R7eXTKl='j'cSZm㪈+R̒Z lx{ _ #ipO8Z~u {{a1bc6GOJ;+gQ濹h}ܴ|i'^ϖf_(cbU c1E*SlY8w=^ ӹJٔС; +wE`JtkS ;$2 ۷"MG?A'j/vCTl/Ǎwg)Ve?Ȋe IDATGq7:[9 J`㒹lB=_o.F舾}ӆ3+0Nzq>F}2./AX-exvBo6GhjG~4X mi$[)\83f(zȇu/n}=&2 ܣsҿ'ЬTC쎯@eq7`Y K|m9:4bܑ]?I6#c; '8lЁI |S0 oF.h7 zFj~qMW&ϏvكՊh̉Yo sc;%'n}v0|ShStz'z=xG3ZL"1,q]Ip6+gq>7EӍC>5B qXmCGG֩oOżn\<"й- >Gbm뫜zĿ%٘ۖ,:Bͦ\/Ҥg- PC;Aicqֽb7 ZOyJ06~Η3% R|?چmf+Or͸Mv-hy?+Ѱr[Nz9X"GX3΍RaK+NwhK|~Ƙc جżƢhE^ϘstGy^/V>/uUgdƒ^Sm0KWͦ+ESC`i87e"ӟ Ӭn]4՞%)] A߆[q `<ܪ5]^'d<0WotED\@.&9W~hkZlG^C&?g֓I}?CGymp7a6XL}x4en~>̿v_1Q'٧֧3Q"LY_f =TJr*2d+,xx|36^ϏϞ+Ɲ}e"WY:;AҪÓ gŦ5L2H}}@&.Z\;27UWqЊvdڷsU0ye Z?/]āh&|oZW![%)&lئ/'lf7Ѕ֭;g6zǑX(C3c :4^tE*F;2׉{^ \_cH6虇_۶m[&^hM6n#YyWLʋl6bbH ^Gb#w18EqKDD ,.ҋY7[hѵuMgdc4[+N9pK4)l־&rYrqۏY_bax\53^g.<(nHv%""EfƢL}&d v;ĹXr\|>"u8--99|*G0ᕛ2at h""""""""Нl"ف[y>|LtuHjrPgb J#&DD$P"nd2ie2fNPPP(fPB1KDD GbȔ$[`` {dɒLXHv;{%000iDDU(nHv%""م#1ndJL2ܹ۷s̙XH L2iN%""BqKDD ,.Yw#ӞVl̚HRDqKDD ,י"""""""""""""ٍl"""""""""""""NRMDDDDDDDDDDDDIJ8II6')&""""""""""""$%DDDDDDDDDDDDD$ܜxѢEUl$[j2"""""""""""""نI6??,Hg8II6')&""""""""""""$%DDDDDDDDDDDDD$dql"""""""""""""NRMDDDDDDDDDDDDIJ8II6')&""""""""""""$%DDDDDDDDDDDDD$dql"""""""""""""NRMDDDDDDDDDDDDIJ8II6')&""""""""""""$%DDDDDDDDDDDDD$dql"""""""""""""NRMDDDDDDDDDDDDIJ8II6')&""""""""""""$%DDDDDDDDDDDDD$0 VfDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD2\oCIENDB`qdmr-0.12.3/doc/fig/qdmr-contact.png000066400000000000000000002452421501654372000171700ustar00rootroot00000000000000PNG  IHDRvDsBIT|dtEXtSoftwaregnome-screenshot> IDATxu|U׍% `Gm.QBişb" 4PB@I8?6Xx?8;g9&.s4Brz׶X-E[S_}ȇ@" ã\M+Zbb1'$""""""""""<ÉfBhĮwu~[vNljtp\_Vmhn XQBb 0 ;ObZ&1!AZ3셝\'RUeÉwV4sYDDDDDDDDD$$$ڈ'!!#jpus;.7t I3 YDDDD#<"jժRT bcc(v;˔jp0Gxq|wi|,"]$\jCDDD@lm|Ng3sEY[DDDVb{J7,,~ɩY,jMQ<` $YQ.n@xݸ5bC0ݸKOT3juU Y\p=5ort{mG& g6vKtx>s@fcI| +""""77W+gΜt8YLIO8{ ™۽{OU㭋0(Δ/7#,U7ܰ㇒$ """"r33ņ3߇nًMYɁo1=ۮKGV\HJ#KY[DDD`11-[檭,Ҭgp L&XL)3׼yƊjh+\p@1_ *A+_]gq+Uda`ڹa6_@NlLq6&\aӍ1QcV'q &ر; ٌ oO+fNTx,_/lo1(xzQÌ=.X&W]0tDGP\YM;9#fHN:W"i?9G*G[ϸ"s- %tjdL<<8bRf\jw ww|3X00UK+]~7n)X=wOKDDDDn&xzs)/Ҳ"gA"L[G<Ow>>ܹTdN^Rx,|hOq0^vU`V=U|b6bd$x{{%+q81LxqdJz"?yxz2¯qk<~Bsžw6%xkvūUSj!""""7-#1;_,^EDDDD$ל'6É4lj1bbd&{=S RkҴF/H1M٭^.諃"""""""""""3 4YDDDDDDDDDDOQYDDDDDDDDDDr$p8 ;ϟe6sa' """"""""""7&EDDDDDDDDD$GT`ɿq5S1}m#k:O"""""""""CyX`e˭ l&g^Ñm^6yȍ-cMnҔ.4]15[MdgCm[6hEpJTjx;fo3峙OE5mJw(\5uolNYΈͼQ}i]e*ԡ #Sl]y׎J^9v9~aݩTҕjilOͪKynԯHj-cDeeոجS":`Piu., v;g\:>w_32O}||hs]Km$qO˫K/X` 3ѩHtiVx `"ѩ%^sw6}>Na#d-5$$$1Re Y~t)%xu-3-qB;Ǫ'a|:O"""""""""rC~TɿТ]'|7KBʗzQ4 &j[#^m=Ш*c斲 ]6jY&>ۗ̚t(ƶF;\#kb`ҍ鎖#Ly J&n}^OɗZ;p6?ZϴShlԉ.vԌ ԒxvB9"""""""""r AVtHlhMmٳ?2AӜs.߻35) l6+:15i=jHC+h=6HK.qEץS̛ܓ6K _߾=I<}, JdaCtTk 5}s_`6Rn=xfwEðPwYCe8k6L8 Ιx~w(}+Df:v6xrp.Osw?Ͻ%Y6)oUx곯y'fj\n[2GJb3JYg.{sр|01?QI#ӝ$8v}2{Wӛ """""""""r+2?]pN9cv϶LǵVAi """"""""""#j!""""""""""9""""""""""#gv""""""""""r2%ǨEdiv""""""""""rRf+CdP}YDDDDDDDDDDr@-2DDDDDDDDDD$GT`ɓůL\TX:dE}}sdC=uߛ5#OrqE^Ȭse&,,?vfڵl޼ UVUtҙƍoyDDDp6o/VsABCΗU֥3-[6'8J̮rFA\롡< |zwޞ_o;#Ǐ`o׷7#! \c>}9| rk.q͕?w8 s;}  o0 P_=###z}|?G6mC,~Qi^O`Ϟ?ٳOw#?Hquuͳ?&Os>&111K_'0LBiqe!S$22@ђ1t,ZUbbc6YM吘Ȣ_1hp,SN+.t:9y/XȠ#X7Y.Iųj3WٳR~?s,?"2vf'{& `SF3 5gDDDDDDDDHO (M FN'Z6qQ.cX20@JhۦfMKll,[nc?~ï@j$sbb"z3v0 ǰpQ^x~d.}=])+ $Dz{*g"11Ν:ү/ǟH5d0 >tڵܹs,Z5g߻CDDDDDDDd؃ykj-1tdÆ d{?!i$~rjqqL1}_˸ilظ3gtV^ï6aK-""}ޙ>MѵK<~}z3烏RF1@)smlא {0j^s#[1~m$۽2l &Lz+edhNQf11ZhiP^빸Ҧu+&O /fw}ڵjnr\\nִ ۶\ (^vL4\{-| +V<_߰׷C lא [dSVgsrLdht߬Rb:8F2ðxY3_s~d'|H漰k~NP`e^JJqZ5lk#1~Gn]d+>c+q[Lж7+Ww>>&Ow]<2/evfMкU4ˉvmиQ<*c1i8&M۹>g"""""""rȰR[ -1t~K%HJ.>~ߕtoHӻ7ztkc^!\9חm!!9sks|/b\+=dG-Z֭ZPJ&3bo[O+뛧9ٸi]TVɔʎΙH~Ȱs-.Mi&宽V Fȫ<)=}fA䌎I=t@|}}ue֔^wގjFLpuu=l޲5['琓5o֔I^*.?q3O9Ö[iܨAcgO]YVrSdt-gҴIc/d",,!;xjp"".?&9/C?EZ #r:-Z9gO?3"O>(ݺv!JŊqCGXb޻~[O-?+}?[9CnZn\侬I~=.jQqDċve8ݙ>muIrm/<^|.ơC $/i6-kw).nق3oҤQ#ڴjf^hh(SNG^Un>R\nѼ9όFgr_DDDDDDDFw[H;-7S?,o[ \ATTx<ݳ#3@Ol K0f͚2iBRujsBvN͙4al˯1WgX>|6rtpfrSr|VJŋg>ϯ8Ut:b IDAT9u4_}-~yZmߑ2=mdUТu{6oٖ""""""""7 {0mNgEtW5qÃnL=CR!l.s_z5tB6y5kdl1Kq cYtsʖ ?#>+e{>~q@F_n1*ۼRvȔeztȝf3I9f'M? tL*~s5wA˖-'&Ίlv+ȳ8Y'?d7H{ë:H-h9d\ofhތ!I?--Z`Xn+R~oۦMsSim^z^y* `!)rA^s5%K 4z9/S^+%3dPΊ̙Ý=`֭\nC B8AɄ-7t\|<qڵ.sK=oe% '7hԱC{Xn=ܙw6 @Ա}sq9qdIm1Ʊr*___ x՞q{.?̳|O0d@Ҽ-ؑɚ6oI3ˋbO>uJeV֯Ozp+O㋈\2zrVG.'.v[n^<5GľQ^8ԯG;_s7RVkw~Ǝȑ#GTΡKNluS |N>gs?"\Km1?]7t9\7\.yz29*<-lD JuoזyD Az3iܸ~ŋFH XyХS<344_,fuB&M|;""""""""ף ̹i!ŋ9nQ+#=ȶ!:tnѯ0敗hؠVkKpΝ1v~ۜ2=1v<ߝN,вy3ʔ.MF!!8,Rql24o4y\Kv@MXx8W>1""ÞU);t0mޒH찰|m)+=n/_%"""""""r^e`f z'+9z}{,:2h Ob5nԐ/p8&<q=hڸE!&&!;Y~#It~FF< E*UС ~i+~Yɶm!g)lCvT ̳ˏSu$[b ,+x5?XIhhRNZ)97.uxɔarnjٍ~^ 6J\\3MK^1+Qr߼np֨MTk.풥L/#k&)~d_&m0q8j֨/?pquK7}о-ڶra[f͜F>qia84{zzRv-,K8aM01ۮW.# =ÇR^\ǒΜ9u ...cZӽ™3gt% R]:ӼYSf؞A Vժ4oތeEW)*V[dzhW,X%ϟdɒ!rBL0/,3<^DDDDDDD$wEz䄹 """"""""""#*0Hu텝܀L15YDDDDDDDDDD͚_9 HQYDDDDDDDDDDrDfEDDDDDDDDD$GT`QYDDDDDDDDDDrDfEDDDDDDDDD$G\=JTTB ;>V.TDDDDDDDDD)iuQf 7+Tn lI""""""""r3?m?q ;)@^^^x܅tԵӹ[? H\\Yt?[::!161ad*씤 8_R[ cSB)UH}-8xWWÓOР ;Gv/Cͥ39e&5L;LWW+Jy7p{oEK91qGжprtp}",L-;Rڜ}:v U8I|h%fL~Zofʗ9nX+3|حX~/o7>6p |j;6D^ 1Q>ЋRhBǏĝ;<3vy ;il%maս%ԕy,_#YyKR}{.v[9{Vijz{8ݫz>hظM[>ÙHGmA,!;S,N1i&.۰==ϤcqW?Tď`ӌl׈ʕҀv'ǘzôLz2 D]G|,Md;(`4lN-ͽ]ZRje썜JFo^uX2Ar̝Ϥ?T̀ӵkO >BBhT 僮W<Ү!U*%vOPVnQ5*VN0)S0yU^4k40D\Ӗb&;-Z9*MngԵH9(N[viEjATJ<9c(c\/>H5TGe?-> w]:vGe⋷S&}mQR63KNtdmԨtK:TTL@`mv3QlV#8 },""""""'cȢX` 'dF^73'z(Gxu_ޥ0'{|x&v<7kFɹ?VTF͜Dv qֹgCbV"̈́e`:0EWզ4.W, 3ƿ㖊l/ fdh..O3F2|F ◌a'ggc 0V"Zj` ;GΨjX,g'Ũ׻$&?.tei/K&ư'\)%(R.nQlf 7Cu+߅vO9K/Q˲N`(3~W;w%7DzfPm&иx"gP/*nó`,(Y~Rc)L/0;NrH, w:qg/&5 ޔ_G}}B hE3}}rk@KCO5unEVmG jRF!`|4=^;=Iunݒ9,ݡGӿj2HKyF/ޑ+ I;Gh] ZZ]gʪYè$K+D8<xoNf]{ *xv؋QMڕk-;vOYS;SprqlFON7E H0Dƌir~Od,} xU oM豽|}6Q!]ެ  ;/wf;ġ &45w„[`Ki.ߧ3s{?]1m$Ch LM>Uhձm{MyKY2Ç-Ѭ&j0ˑs)~V>uݯmv^@K[d*+uz{(b?;(}Z1۶hU/M_bկy*\~X#i-D8vθxihZ'Kqߝ?f;ORmEmY|3 O$-jBAnۉ};1{[OԴ}uQԡ/sپ=C^z>]ӭVsz漿O|'/4ݯwj?sуĈKקCTr:u/f0Wjի|-0 NlJ<ָ#~AşN@W |<:d@JA7q!jF\;wwQk%b$Ɠ& h=7}:2?-[?1H௽^5.] - #s!*UqP`?=Tl۞ GY{07L<1v>]k0]썿r22e?¸J| N2x)œkظ35H.X7!Y?qgzQ#öu (S:ϘJ d.ֽ|!Ɣ'ٴGyQ3.tM+/v~]w.(X]pb8؝&,̿\l.{;Vʲ7uٻ?sf7zt4J)H*t0I AAJ@B~ 0JAJ$T{ cظO=v=9{q>{NߋdO O*4׿s6wًH IDATΕ<9a:6{ؚrȡ_ >F^߬8=Hu=);knxõWG&NޭX87$OJg&5MroǗ7r߹&H^ 7Y40윷sVb튩|םkjLh3.!ۿ'el~B2╞LN?Hߙwqn?1PB |✆sfN[wSr=V>Gzv07 &*d7w4v˖|Ǩz<V۟R\_׉c^İn3VqIiQ)ygu-`T"\׬aÆ ,>܊Vn\ Kv;`Dq}9 qg4L)W1܍:XYDDDDDDFB$ },f!O6' e&KC4kE@hn~Ԅ9Ȟ9R8=546^NJ_ u#{Lpg|$,Ri%kqlg%{O 3}~1MPt6#rJohFd7v݈m7YNvt wm%oV-+aInϘfβ?qa &,X]mSh^"y$'[Μ[>p~d=O+24w]L~D>ԩ'2ywXH'){v[; yHcwooڏs09v2+Kx M}rj7F0#!ej#љŌ瓯g`^9וyɞ-Y׋ꓼ5nppu[FU<:>wS6tԖ͜z37%'?OI2ONBGI/u+K 6'HWھueN^9po ݶ; w7~ę,""""""R4i4ҋ5vᳺےw+#MnΙm1xZ3ܠpxUnDDz Ϛ);YU7 z.DȘX$m?T˞a92+f}L[(d(>]iԖ~ݸپ9q\P~ $Iw=y$?$3>a|@Kj̸hL.mǏS/C",?wqJb5-gHF-~ʕ{NxBR疉SI+g205%ҋe?(UL?=jpO_ۯ;'GR盋ܳ#Q䫝*Ust 2vPK&^%^ܴPsR/tHpM/+>x]OHFEsh9ehq_V.DP$Bqg4PrxS5ՊT)I;Ż|Ѱ&G[z$v˙m`!ZЩB>!{@ɤW:m8]aٷmHݱOW1t*˩ L_Q+?n]LM|Ev;=7_v=Tu'ץ4 Kvn9]m=yߍeO5$peOiE=5Ո/P]N=CV6-䗃0XJz9[#4ꥱyȹ{|XjTqCҪFN3tN978 ٞ0E$>70pW|82.i}h1bm/Mp&sfؐNFid0F?qY0|qG>+֐\24x8Κ:瑖V7m#QZ-US$-Ӌ{{E-z;BZ鵤"[N "}R1,C&Ьt_ G^G L̥<w-$NBJiFKZLe`&|֒nƷ2潬ᗎe?30,nx/ 7" 7NK)nOGڣ׫: 9S0E@q}0whԬ;nil BHv9߹]')|k۝&CRxX~9g%rז3{L_'轻L@鑾v)ϢI̟IQhW6JʒySfZOL ·8au+Ēv跜p -1ist@93k >GNϸE}JEF3m/A_oKdpIjC&;NA1CSRh*%ty,""""""cY?/6۫jS<b{ǖCZ[:u=G\ E L:erT_YeIXVvމg1OVU|΀%2fs&k$^گ-1~kOԲ(%$;NƲpEnxty-%m sLyGV@DDDDDD$2c*, 5kSB0ش)s9:XPNG!r 2 zc/Z`T{U$FJڋț]iqp""""""""""RYDDDDDDDDDDE f%EDDDDDDDDD$Z`hQYDDDDDDDDDDE f%EDDDDDDDDD$Z`hQYDDDDDDDDDD):3Y&y+::hVko$Cd˖ёȫs>nQyI7]ut"""""ٱc'eWnn4Mnf4&&D;i`#LT."oqCDDDDDD05Φib1 GҢ`6@7E!""""""o`FqEms>nQy%j0dk܋[M?m'W2fw@9Л^"""""""QQ 7jωMsvd _OZ<Û_""""""=ki_9:X'vƩ;]߅ 9>X1hxƉqH"~#AO|qjCqo^׎.{[[(5 {/%_}6=W:0gY234cο_cCB$LY GBY~q.]JHhݺpt8h")o;6wM d d Xe"b ^~yu+O3y/ӨL)bp1`yaɅI aܺW(4k~.ɨ1r,J5s(}XL:v__ x?#1)"""""{ ȵW|v OOU{w83)OI2x|)Q"l\앜ߝü?_.>+݂ggguϗ$$NGR2`1D\,P/ɼSxc84[>dn͆=w<2I $IH蓕u佰ү<7"K|9+:'sd$Jmfux<!+_2~xV)Qc-z'vi:i?~䷝/J6U'9#Fд ~8 ~f1mgG @ѣl)}|y=ڥ۰6Ǽ:ipycaryG1(.mu',їI탩ҷ -һ5y=Rk"Gq{ӈCQH?uuN)ïA:3c7L,gS5iݪZn?۱&fgiwt1& P[ 6;v 3PRM[ը׷mf >K". Plq\tޝ\W1fA|?Ʃcu3,=F2Ȕ6=Db˱{bܕsmFl;NH ϛT'~bmۄNo_JŅ仳-O;T`j!6nn9Hq3nlο1J1i>'ҵbcb'Oipݱk|ո ~N@AMٴwf7);A: Pw8X2 ˺3DzҘ;Q ?c@Q Y?כP%J`bzzy&Cm6Bn]ȟ;EX~=FT6 =#,cjBw1>=]5?%%J̘#*'m""""""xXJ5TmI@'vo'ķP,}b3L/KG9ל4d+" |P3?2|$~q37-x!fǯs$e'.0;I%m&, JX9|K38,$O9.LL+`' \|[5P8=8F{s~6Y\8}َyb8'JutL"d^_6FiLM13qKF>+uT-ǯ3.j4svvưgyX?_5,X  yU6qqq- 3v#=2݆iraΔƤ၏7p3{B0M0dxw3Zgz~ʼnd)-._i\ 5~atWY눸-IfB ӊ'WM_Ǘձ#w#ygŠ9rgu9?eS=^I@L{5`dȕ;N`ʘ#J'|,mX6%V~֕nVbdO~:JTn<ךo5c>$֍S/,Am^fӆ=ӿKnO#{(󕓈l޸>3MVz[`o>m#$՚S{Ȼ h~Hچ69~2?y&f@>lԭ -Kg$aEGC)N0o8$9s!+s>nQy-::7^̵E(S[u8fOß{IG#"Mt!*e $N0DDDDDr8D;qByT<M!e`G=6bѮlf1ND&0*y^ %4EDDDDDDDDDf/DƓ>@5EDDDDDDDDD$Z`>'G6vmWB_EDDDDDDDDD^`ř{a;GXt /^Q ""zsvDmh IDATHlIpq!^2`EDDDDDDJ0*yOw+#oLdʚbyj}<\?M9ɜ1 AY=#lYJL=yR|?sjmmkO%LM}Xz[*HM[љ/E Z!"/~MhF">@jwQ{^0Dz ,vUL]TmӅ~w$[Nbd,뵜4 t_G>msekEkDY7ncb8'I# M,aFWk6EALP `^aWm[0f"S?j!0obр(,})ۤ/s]Oњ\%YaĚd)܇!v^҇F͙LS;|#h57Q9TI2Ñbbxlփ&(77;wsH ǍBp{ [OٱY[QnN׫Kf1O;EKOGw>q6{~>PR7]⓾gl4y2fܽAĈi"#+< "5^…ܶ=@X!Xa`#ҦOA }v/Ia[ۏ2wb{'6^(WFIlrǮ| Ŋ5;ϱog`v[ S. K'"e]ˎ^ɣ=u_W)Ŋ$W3-;vBn~?h3[6RpDDDDDD5s΅Y%7WOf/#,WnReӶŦwH=,MydL;~ #P.G3nyXGo㯽*m/{VE*w#B~ӎn>2fwhْY &wX"5/ N@J'3^z> W@ZwѲ@ $/şͲscpFn2}me 4i^w(""""""򲢝qX_h0Mal:{T)Dֻ=57j}ѷW{|>iM~1ѳUq\|+R6f}6*)G`i◢ϤMadY0L9IjLWg.OS܎5G]Ϫ$Ag6ʲ37m!3-܍P\$sޒ4Jzs_LhO w0,+%Rc`b}nuga,)vJA t;1*M$Oo= '׮EQ#ZUFaԢE4?5kf#㄰mH%j1I0ea|dO HP/h(cf͘q&o9>uAcf:ux֬YCѢEYz5.S(GX EgDy꭪•=_$]IG""Mt!*E!"""""ڦ R<$vic7Mv38aL``qE^|rNDDDDDDDDDD^V;{""""""""""扡qzhOG!"""""""""f~ 3̪,"""""""""։~qľ7?y`00_ ADDDDDDDDD^h'CCC(+=[nXL ӎL{ѡ[$ Msc,#{/uG""E!""""""oh'kթÏQԿdL#/a *E(/"""""jX=a& fyE;lfL!""""""""""`hyj0^ CDDDDDDDDDD^7,""""""""""Ѣ1$t44Ų;f4 ! LDDDDDDDDD$s 9K~߻a[br;60yM9vR[| 6fw\x/yF ut"""""""K% VnCpËyѴRbe1Υ3~q$ٍDsSI?|]7;aD,oSrŽ܈4݅ p-?lHŸqK:#)ٿ)֟uʽgT./W=S'Cf?ҥ݋̺%SiW$ch<̺A,_UN'mHD~B5FV^yRu'웙.]lpW#kMvN/z2MR|ؿ#ݹl]{VGrwfNP=|{k<מ$ӛV Df}ҁwȊݙ{.&& z!=8qzaU+w7Ut!_:օY0z-g< +ѡOf19WVd^Fusֈ<&@cVݜ7Uyo"+:üڡo?-~=o!71OOF]ϟ㑡;t>V~\Ym]qa! 8<8ʺ9>G`߱\c%Qz?*ЮIQo\&7h0j#~j?w (d)@J0I-`5@?L~c?5Pg; @%:Ȋjt ol֒uկ |pO=j-KV 1oֽD\~t+e{vazPe|mX'Q9O L~ о!nmYIlS΅k >&q7NgȀj[#%^8euPHٍ5R~ ӹKc}q)Nhh(̆גN$Y/u=F.&ΜKF{_Ոmb̃xT'^sыZ3k:W K,xHm:`$ń( Sb#by YV 4pZ% 2a+ipC= H<9W>Xc1vs|I=1/rZ!W!5Aɝ~yع`ML\N3 ǻ2Mx|"}|}I]I9c,WsvsNdX!پ/4rÂ~z^yǴ|Jw,dǓ6qo7Ç-SiJ$7N =R(.I_3^6H<#;gЬ\{~AwXo'uW """"""v1&2,ޙɔΟq#wr%n=-I'qՂA!!/a`1(VD,ǮO ̬s=aM[ЛI^$? y R`d.ႛy&7/&7Ď+.jHCa]ˎ^ɣ=u_W)Ŋ$ps` ;·oJ=pkFvV 9 lHzNDDDDDDD4z'חS%/mv㵇one$WtVpO惧(} 6sI3)jH L:1rdnnfzkP<_.OxjCG\I5# 9sĶc%kGۼaK\>zv W@ZwѲ@صϼnuGWTjz9ӼYvn,"rme 4i^w(""""""b?<(>*P+/;>:IwthۀTûs _}ʔKQ֢7L>M6D4FDבlfxg︒,?M'Mf`1H, aېJԘc6Waʶ5Ȟ;t)S,$m3LYF];~AC3{mƜ厳7}t]_kX0wYNh͚5-ZիWSAX /:BinJ,Cޡ|2[7mIG!"EpC;R.[R<\\ ::XOq+GRtxyEڼho)m`Ι|R8Y}FԹ)ӤNI[,  \X,,7[1=7\Sf=E+"/Ve,+Qn?~:x2va_F%AMo[γeRG*MIB77x:iC,o<&~׊׈np;Gd]{1w%~ynY빌} aY}z2bz ߡhŒ i'P9h=r+QU_v-O(<7~2A/lEOt>.OYUSQ뗌m_8'h Ly|LL";!=؉ډjP/Q~Jlt_Rk*EIHZZcA2s~6%IHy3ss9| ؝?ê:DI .n{T^pjOifu*]a!W6g_1ʒ+җl {Q1 5+ QΌi;crV3S#zKsh<g'O~AHH$T0_ű0F7߅/pdOX;1 G 4ާJzbơG7i%f:"ΖaМp~\5Ilݔ~ktOVN.E6K]L)b Ħ+̊0vKl)arן|u<"j2\˘ Mf) =ןqkSݿe /ҝ[|5.M2,TBb=qw/#::_;ٔ5Y;fOXjK.p IDATez"""""""rZlY3ŪT$C&HZToKL_f7j&t#&N@=r,&i\@@#]kQnx K|I;DKm*=:mJo'j9tõ_@Z9^Dy.7fк;1bq(LJɒ5ѭvyvm֝PcQVޜRrb?h*oiN2; !+ѿS߆gCu^Pk3q~r_ eɥiHJ܃9IpE AmP{Ӻ/78q Cn3UЀoݣ-y' 6ƧַNb Ms~}3%ŲX8wq-'19m+`>ד\7n.Йi˨ۯUlOB…cq<'Ѱt ?fUO$dܿᅧu +nIZy%߰FG*Oq[~?}WP%$I/{:֚-̞Ylb5f>[^Qk0t .""""""!ݷj֮Dٹ}+TO2E8y.dr xurZȈʅڦEsf1Y~$3j:+L&,(T-IHʄ)嘛kn=z1_d-~[gj5ˈşkߣsps9[d剄{^iQx/9``d<hypqt&O%Zxޖgȍ܃F|pq=/:1nab̸·?G~gY˾tFe?}FP%EDDDDD.^\J$*qbLAT0`bӆlu~20Yn]ldOR\(ՖPۙSZwvi\9w%{`Lݧ _X̲5Op vے#:ǿb{;a)s(dqy,ףCNʹEq< ]DE3mShzW~g2p> H د 3W'36š4b./tq~E1UeCVzt MgW4)rcpv:6仑uJӭܹs>l%P93#6NJ̨E[wqu9w {㺝iD%E3!L Jj/3`|(+cW1h `Єj^UGgQy2G?vd-PFG:>lqv}{|:ӉXғbwHp;/~=g?_]f0WeOɽ !A<>v`X=|X?и̩q)w=qX6L^?ÙsVۗR ;`ZKEG0.f`Fmf[B8ܽ>uk{u zyl {Q1 5+ QΌi;crV3S#zKsh<g'O~Ac 炑ŠAo؞ഹ-|^ cT2.pdOX;1 G 4ާJzbơ{<+1{q L d[˥{¶rrg_,J]$bL'6m\VfV[D:crן|>@f2|4noWѯ5ZI];{%o2"2\˘ Mf) =ןqk;Sq+E7-*JӭH}CZDS!w|;ۗgMίXlȬDGG'b,;VDDDDDyb`oȐ!L:A{?r)o/U+_(8Zs5͘MF2C[hv_pt_a*G~pzfbw܊qK.q nh?=.&2`ӅA^̙ɒ._eAtn)9m-ypp\w\p,|㔣eh3Sq鬼T7$3yXv+vNѵl?`X1rgˉ=ԅo=71A5Mjxd1,T7Cwߟ8c^JX:Ux4%ϰbȾ{[*XN,crX!V|t)㇯'K˹+xoN\3BQ_'H (Dž -yzOa8~=ͪNyqIɸ OVD^q9KakT}<-9|_;K H^6nu5%y[=)Yy6^Ⴋ]a.G:Z8fqW pa75|yήn{\]EOcN`jȴTG Fib 7̮x)Hf (ٗ.`2IZ0 >Uz҈ɋ`>!m?L[\ILN_;Gٗ|6JvxGJYb:f+ 8zDe!o>ߎarv+b"19> ʼ -ހc[gF dEi0A,ٳb&fbvp%["M}x?Viʤ96?%ǿ׮pŘZ?N W7W8|Va2y4˟KԎk\?5$-eid*3%,JPLr EkgٻC>n8ߏ]g`q)?#lP 3oz[Aq{jDDDDDD9 T&?*sAA^v-Ym_y"m,=TLz: yJO_t<y"7nZZX +N [wbk]ca0 &$%KФi fDZWmB%hٲ#B,Y8-2l]PDDDDDDDDDDq?.OZDDDDDDDDDyfZdb?|vaiH =`GZ65_T0ۼ<0ɟ)M0鋎Gd< <<<`%Jd6TjX9O;ʘݻvq'2'*e'EDDDDDDDDD*]/Cx 񳈈d|6'EDDDDDDDDD*EDDDDDDDDDg)haHF f*SIž6w̵L} 6g=\DDDDDDDDD*U0똷l+L|l\ϏW9kTsxOoԻHf̺qc&k@^88:X E qՓRY<\A<S ;g3M5̌c&'zQHg$uVt!f{G7 ~q,=ؓ5_Q.W+"A؄ҹptJJmoLC7zrgĸeIz&6..8_oVqŲo*~j:+~O߆T>6GDDDDDD$)PȈĦ N.o1eDa܊#$+'wNP-=G3q4W3lʕsgY7 >hGnUih]}3m K 9 V%")p5ڊRgx̾mP,Þ2kk҈hҽ?Gq}V Y5p7Až D\)CwӤȝcepv:6仑uJӭG*W0bΌ؜KeմtlՂyk,L3`\`ݸ(8m$Ƶh\8D^|g7ɹ)phBɺ[V{z|ɑʼnLd/Z&gз\39L`@0mt^6q}+QLNxNr)t 1rT݀CjQ>[? oql(T+J7gMG~ jNټYp[Ӆه-@ۆǩdcmWҫ~3=YG2 Ώ3)ؓ-om2 a+'}FŢtۅbY)3%Z dڂشq%[Ynxwd5]h\;~^DӒsN&&s:6/Uk`\\ɥ{o= #[,Y91ݡ.w:a.|t5~BjfGŮ}'1_ش3¨Yq*. ǚD\ںv/=nE4&Pup3y޾<hw~MJfS@f&::=c)o}jG#Ϛ)V" ÄSiP oKL_f7j&t#&N@=r,&i\@@#]kQnx K|I;DKm*=:mJoW IDAT'j9dusGw>i2(f%7N/i9[ƶT.t#;A֭3 ^v SZ{b,0oM~kyoVoW ?,A>h\@7R'+>Mby1P-(J辇U`+=>t`-PFZwS (OsC1fW*O䮉nTҎ䔵1&rEVeç~ɬGDDDDDD$ \`r0 L{W3kp`D@$^n̹;8nn؀}w%51W\X1 #+^Xtʍ.6 ;,*^x9Tp쌎Ғ섓NyxcuNi3;sܮ /x>xݖm켜*OP'/|d;WĔErB̙qy'ՄJEՃpr}k4?70'gܤMW """"""Hffۧq2g^7a3c"Njd6c6 ao_bu~ӕC~![,Zé-L޾\f|$k^w={ !!LdzԢa$NjWq@sz.dܷȲ \$mDH[ƜfRqzMۋщML^(ʡvm7Xp/V:H5~هᓘq'2|y-Y)I(EY>v#'#?8f+6GpT2%Β؎N{4t%Izo7ZRX$ S &VC'ٚSҙ\RLTd93^/A9?{N> S3#~>t,("wqΓ6ۉP9d}:h v:GiSr~~XI߱"+=Dsp1o4İJՂ8vTP;L|/ޮ{,T9BI)&;.! qwoZW'#ay-t {%BUIlHӜ_q kI,i\ˉeL+D>.edi9vͩ tfZ2D3ӱpE OI4,]ǏYS#/. 9>0{xi]Êhҫ>n^x9dzy7lёʹ7{e_+L]CN}ܲ:_טb< B:%S+ y8A2s׵^Xs5$s#@ͱ-njC{Q;~.8$o{4yvH"#1m;uj\6YSEԾisRgԘэ9foT/k1,'(9h9#ku"""""""6K嚼2mc${# g5k@i$;t[$_ 7WR=_FL^4?%Ԛ[\I299r~D6&xZ^՟!W;RrGc-ߞ捛D ,!/xREz'?JwDbr|k> ʼ?Ҁc[F4Bh8 Æk&4KP!_eg?^d*> hއ/h2c;ȁ2C*:>Nv_7=. 'gt3|FU*VV^,n y>y9Tb]pc[W\m/M v9.SfFj\]F[кX-K>#L+""""""~Οk z֮]?k֬uW`xH`0?j. "bO(mx"7_S>k7v_@DRx/:"""""En@V0 HlkFbWca0 &udb)hHf8rWĔ{\y^^߫՞ ~So` cT~YDDDDDDDD V0S) 7:gH _i+O~3""""""""""؞`VYDDDDDDDDDfsYe fK%5 sBBBj!"""""""""" FFF1Ղy3xs`PK/ƿzԌGR *rsDE#}Q_DDDDD鰹EVk*!"""""""""" fyٜ`6 #5 F""""""""""b$U,""""""""" 7zrw^*냇\V.6(33G# ?Q3s"n<|Щ:ٜqM譴v('Q9<\qvKN[v$/3..8_oVqŲo*~j:+~O߆T>6GDDDDDD$)PȈĦ N.o1eDa܊#$+'wNP-=G3q4W:>G`=6aБLZ0·37mm>m#I#FILYljZ[U&(6.oXg:s3fw am+V+(XlRpT,Nxʝc963XΠE<;e-7 a鱌wZ$Zh,zׯ@L/%%} $d[ږ~e餷yesZÄE[qha:3fsF\,oE1=& cMM8لd V(]DDDDDD҉LLffr/1*+C8{gky',_ף_Eq%1Уgg0hN8?Ϥ`OnJ@$\ml9g̔h1i ~bƕLoefEW%ӣ^>:Uw`ubkݯٯ8<~ϐT/ ԑiלTKzqRe)v}{n;=nE4&Pup3sʵ/#::_;ٔ5Y;fOXjez"""""""rш9fUH0TE3[:-$Yx)݈S7zɽuק=Z=y3_Ҽ#RJ~c÷N[|o?R ۦDEZG+pP]jȇ[f) bש\i*cẢ/5sQwŋ̔(1yY;묄,:GED*5}˦Br=(5r7ojͧ̈9ġ3+'KrDT*iro WuUl!!+,atju@|r欜ޱv˭U9sm.pͶ+ A߿z]_֗YĢ VneԤ vo(]s%R;OΞ7:>io%-`hȗP+l7⸑1wH֐o;uuMdϕ *r DDDDDDD"UB٬ow4~˜9{J?N܄Όxͯll2"F38=+͇BSY> S3kwFrUYo2|M̿ʘLU}?kS,^D !!'/b_Ur)Q,C;v'V!n^upDDDDDD$J[ZILvw*_ovmӱQóL9|9GN-Gq2VNm$eJܝ4?5iJ6ǜ-2nN2(],>IR&`MN:56#v z7t҉2M\K۟ef{s4ի_T)*I)qO)29ͻ :j17kSP;L|U~}/% :8Q""""""")es!IpE AmP{Ӻ/78q Cn3UЀoݣ-y' 6ƧַN4%ϰbȾ ܝƵXBR_OsiWޜ@g.nNT=>P: [D%p{ޛU>5⒰q5&j慗s<|~>yƥDM8H7v lݟ#&q[ Dv" EoXwڿCIVfZ0Ncav3OA5k@it:6eB1_ &\sJS~@'҈ɋ`>!m?L[\I(&'GίHУ\KW>guXr%;#,gk 3=QRz޲|{FoNjg09ۏ]1f e^o-3vWsx~̤}I@ k2j໌h3 y(d+& jr = ޢlQpq4~\p0ΒSykbC}BUz"OT<{ L^٣93GLf;2unݧF&/|ף߂ *[Z)""""""-k&kϚ5kh'-6̏˂JWQ^+DE#}-rըհa`Fb\0[[ +a`6 |&K/N$d8O""""""""""}Ԍ"US) 7:g[ *S1 plO0+,"""""""""\$""""""""""b̖Kj!"""""""""" 愄ԌCDDDDDDDDDD2{[ *rc#)!|g>} .^Z@TF>鋎Gdas T CDDDDDDDDDD2""""""""""\9lFj!""""""""""*EDDDDDDDDD&6O`)&?M&3 L .6'mI1βv=Z0^U0} HZ,X =ȞLad| {>{1636nO=?&|NHQc8}֜ӛKDDDDDDDD$#H$J&ecmq ~;gMݱ?v)"&M]¾kzoʙuh]wL8e-Lϕ׀8Cbqqp5W&G =Ԓ8y.dr xurZȈʅڦEsf1Y~$8yq[ٜ7sp!kTiއYgICa%|zL4'?<-\'#|j$+Oܛ`NTJ00-,Q,NN}sF2ӗ ?ߍ܃F|pq=/:1nab̸·?G~gY˾tFe?}FPtIE)>U;ش!![ݩ-(L[1ydbbq4%XڲrrqW sT{;wj?QNs5eio/hMkĜʙxbnbPɔmqjbGiz\bJ9Q772vrWMm ]u:(X"rSLISڝs3Ii8;FMhXγ{4ƌFk+6w׶֤#vyѤ{,D-*գkn=RtI;JK6ױ!ߍ SnwqU_AA p;57ijiȕ4WJ5W\)*"qfq*nD\~{=pyWDDDȎ ܔ l\/6mLtsǷ$eͰfT),NgIš[11L^.8dh|BJA+4ѩ)7Ҿ2ycM"Cޣfo:bgB6jcF5v;N\Dn'Z00.b<z邽#^i9xS. JՂhFo&~ۍ;ٰ]7Dj%NT~k[ݾ-Mëx՝ԟyTޭwLq7^w|Cܔj<vȋǹ#clP0K$ldgJ zMl;n-Ry3%mKeݟokL_+lBo5|6sg5l GMѤuWF~ MXp51>>  U{իXO,K|ʻ&"""""""c3718A:Xbveuys`c9-kqGiz KPγ#:+曖W q[Osl̰! :Ad1ܺy߱2Sqo5K=[鿗*٧a\'vx|:w]r|i8q6oҟ ד]=nle9ʂٛq7Yޟo_w:cfZ23 qH9(֫6ap!HŴChe~6@ GDsRϩ0zDA ff {}]ާHt%(t("- 4add{ЍsMlCDVwzR{JȏS`*zM4Rjkr=llr`5Cj۬k"""""""O\qcߗˑ)m}^QP5g6%|%ax*+QcaR4VjQKK5 ά>tʗ:q=HrNyݱ])\JDER4iHZەjbMvF| QfP:ZIYKٚXf94ٿR R~ԟ1գc`[Mb Ʉ?ʔI3 gDt-6Bbѧ# eILf6 (yl IDATEzt /Ok)R׻2S ? K=ߍsK6Y)Py8"0jbpgB)1ׄyNnQUO镩A8c;G=-')1ɰbIDmOrςw;杵gJmM~1CRZ7Ƴ8f4YҢF /U[U{vND&-BVK)b&4Ȗ|~Dl5"4:J~-2̹J!Ʊ4ƙ~ Ц"q DDDDDDD+1[ ga eWt o_?br~Aމ01DCF* F#F.]76Wby^rr;4Y˼Y;C=%+I x{,뛈2i٥d$,09Qsd|7X-XV Eđ9 |s9ƭ&CTۃ9.Xn.˛?'eXVM||1+O_|Y8=VͶ}f|}|\NDDDDDDDUhZ.-Ř<5oc͆7_bMT<P|&lĦHQUA~6lc6hz>7R(W.B.`IΆNݳ^O .ew`] dl=m,t>_Զ>RVޘs`̍SS4sbkZMqeytGFy"\y8#7].Y {><2v?ytZڕߥ *ynØp mW ѥc IPПoV${,`v@&9,qG1:-*xh!?\kugLX0qaюӵmmJvt0D#wQ)Q~ⳉZ%Φ=bɌ \V:_W=ZɗCr4,"r Ifޒ +{)/RЏYܾ'J;Q|-~ݒB6 t=9ZGdMd"oxCpCp.4˕l7/?3HexRo¨p3v#H0ۋJ kS>)FoGfј)rcVc \2z=?g@hu"k./ː3rGά;aٛʭgExsr޵{w} l49M8htJ4$++18ˏZ]0f1Q$Ԧ&ڗ!˂Y)|܈{>/Lc57KUj `s{>٤/5[#߽~Œ I{&gcv2WO|BmORH,lRX0L!gZ˜6y1[ ! *N͐9Qv|cgS$y_s+an khj*h&"wQq==~6XaxޟݓKx<[tyѤ; \-## jz>g""""""OFKd`d0DDDDDDDDDDy`Z9y(YDDDDDDDDDD`y=B92`H~h=G K1٠ :bgg2~{v%>\KI[!o?NyDYEȞ(tK4mgvZ֛Y{cʷ-̧UW̍#nyJRgRo|_ZP.+NY h5'xfOO[!VH2pwv-%1lџ\IBz^5㽗]0˛tj-4+y?5'dǗ>Kj;~B)ۖKG :׈=|ޠy@W= E~c]=Y 1;!Q1]٠0^oU)hriZ4"|X8^EO =kwnGk.-&bF<o_̷%lԃbZO |VeNOۓpzy_1iXŠ!Up3i:"/GR+ ֱۡ$28Ȏ ܔK3'ݮz5a4c3檍;e.f\aG}]rkFhY7 X97:c<^w0s`D%J/˲C phVڏ\Ns.,R7t aj.{lCS9Wc< :p`ZvyE{LyҰu@g2h=kYLףx iݧM^pQ>6^5 >Tzra Øݏ`B|I)+r 7]!ok¶d_{%7m!ZL7a[qLϮյ-2vOte6u vؤU['u+\M-}0fc9?N0zcԻ<0]2718[˷n|_+[[_~Ð`l,eMBh8|.K}Nst„i7i!n"cK,_lҀ9ǰ-ahT2SqoYɆKȒ!kM~y]NY`Sֲ^v$).뜉" uV|1dK>YNsD"Rr l?c\v>=ZXQ]}LShS nA/=ﯴ/G{G˘&]%pnq DDDDDDD+2Dѳ0Z+:˷pSgz  mH")k01wߡQXڹDRrR|>hޥӜH>]\K/<+,)lllFeđ9 |s9ƭ&CQn g=X.沼s™X\_feeȮӿ-l6d#9u?:ƙ0(]Oh/O_|Y8=:9 vٶό/ˉ2֡ -_+ţ󃇒ߟmp+QǣT ؀SD-AC1J*Sq7߆]c&͕BFT*UJS%U`,Щ{_Զ>RVޘs`̍SS4sbkZMqeyWA#NʓĊ6S#:zQ)y"\y8#7].Y {8=!b6>`TƌLCrLZ`9Z|EYsyXj8z$jfo*y(g~1_4k)L :g.?jw@rp|hM ǬF+R^Ckh_, fIh'>U;WM} V' ܡ̌c퓕6WgL\Ky헹zjGOeaؕǂyfFH8 9dƴɋ0ָDN܄TMWae$~T}{ YKPG,՞ȝ jeLJ1vF8=E'51g.BhSyF̰6 yV^M@@VYVqxabk?. !rW2<ڸ21I&7x_6dSY䙓yyd:y~] /B89lbw?$9yXljQOEDDDDDDDD._Y!2yc߅C*!"""""""""wI2rʖIxWt~g#d0g(DDDDDDDDDD乓"""""""""""/4M'""""""""""dqs&fɔmz7ܸ13l0tfe0f-:E1ɿ[eyZ2֌==ySk$V>5ͤ6чΟD jK<%\DDDDDD d2QzcgǶٳg/ŋ?|JdGaV~WK;gw~,1~+#3vdGփX|y [3 ʐ/#v.x,CwgWkb`C6 9wg0{95>+?xCPgkQiKM.|t\uQvZ??#439bQk3cp;^-ME1)l+uK<%3 6qlE&L*yfOO[!VH2pwv-%1lџ\IBz^5㽗]0˛tW?oкn(._lPA̪4Xu - oK>,'Ӟo5b;gz 7NG~ǣܵHNHψoΌQ8ս'>#S12qz&$uXϲ&flf\qllX1L/#"~K.xUz^-+4"WG{~.fDeYvh"Bˉq%ؐ`Y$W-esq*j'rA'|LW:2}/^0)oYøLg-c|\zT߶T>oO9?U"WY 8K/̰߼+5u>x_=ũ[P쳑̜ow#zf-K[p{"&}v)?#ٙԙ`=-8x[&lKeݟKtZ⬥iª_=5Z=;ly:u6u vؤU['u+\M-}0fc9?N0zcԻ5֛Ť{y8]"㮑MLa7u38ò'ֆׅ0$a~hYй&ya)k0_; q[/fF`X;mX|·Cۧ .Ɛ{3nJ6_Fli=HOM~_ ;^i3}^IkGԥ*q3';׷רڼ1y%8S>/_ ɔ,~d4VbT, IDAT<@k{6-l9oTd+7ˠG\ ^ cF3=|]/dU{ks3 `dd+T(ai7Žz-/T5 q7gZO/č8e ѯۭD+ Lmbbb17&g23qG 111lX/+Vk~2ۥ?K:Ck0[?ֈGWhTA] Ӗ/_aGV=jC"F]GƒLݺ[QjԫE͠5d.:v{=e3 ɹ wnw4 kFǼyxV+4S:g]'y4%si~L𮔶¦5[HW7|͢52{@ d. [/n|u |#WT̆5S`;8#Ezkb&㇟~Ž~YJˋCg3""""""?f4{NVa"[Sif`0`08Q}$Lak7XljQqf SLhAkWY={b0f53\8f ޭ.ЬXwĞS=[s:6OYI#G'E9/=`p gb9s}5j<93._L 7?6ɕbOJ-3'OcȞiI0x\ ٶό/=gwPQz{CO^6lxs(DQ l){{a"Nl䠡>˟Sq7߆]c&͕BFT*UJS%U8`,Щ{_Զ>RVޘs`̍SS4sbkZMqeytkŨ3egPvk9<^oO\[omvĖXpދoոsRUz9ݡw+OӺ֮.]hVwG[h]4.[gxOre|{"k$g |ݵ;7=g糖l>hQC Z\u;cƂ [D`vT>3_hW CyZ~`o`6/G[GL#Kԧe֣,WgP'͠E.[qqNYfS}m72b=uVn )ܖn fk0cd֓2%V*{S^0S}JO>+wͣg[^%l+zr 7D"wVΔ3 u>LϤce5='J́cz м.WyDb̠zDDDDDD>Fݘ1=gƭHQ|5gFƑ`t#֦} i 0GcF\v!Y9Rf:ryc0-Չ,CT8z$jfo*y(g~1_4k)L :g.?jw@rp|hM ǬF+R^Ckh_, fIh'^!ϫsD>uKwPWIvqwľm hh~w1] TkY/_N7R'nˀK&<,12x-i".i7Q/s'$ 4֙+Տt0qrɨ%iPtp [;!rR5a_1̟H~i>2}k/W ϐ9Qv|cgS$y_s+܌%Q207bIHCEW^M@@VYVqxabk?. !rW^ب \-[5s`D%Jc_S%t{dNw""""""""""|n]:c48ː0Ltw*"""""""""" :vGz;qc$Qקsx:"/}-:""""""<<ܟt1#&f7;wr̙҇/`ǪD =2wG),""""""""""ϿtEDDDDDDDDDfe0DFFCDDDDDDDDDD7`tQs1w[ `7&ƾ՜IOw`""""""""""235Z;^Kzu/#i<',Z3fe`ctZzg͆=.9k0:+""""""bZ9dM3̏ `)k^ qs$K^?j#k238P}毺Rdn\qSz=#8pgolCnp;RLܝprCv ['WRf=-o3dxe{;6ϒ6rf8!/>I-w{ȸ 0w7jƘqM㟃;wCz{|,5ZOltNğKԚ\yIQ1]٠0^oU)hriZ4"|X8^E/=kwnGk.rѸ孄ׯ'yrQbd ,CfӮuD^,EޥK/VNyo :D–Hv$[ϯw18ֹ#ֲ.U7S?ے}YۆF gM[r5l GMѤuWF~ MXp51>>  U{իXO,K|ʻ&""""""&K0q؍C 䞡yY̮lmnj~] C}懖5 k,9ҹVޤ]k-|YZzyHzK@y߱2SqoYɆKȒ9˷@BD-5ZN!,kkX>5K:Ӹ/\ʩh38U/fSZ~}:WKDlX^M)Xk/-3L钗XmJFlzp)]"3;{TxN% X86k4OJ6Z[ſ7N6ۥ4b9 "!mq>_e[W*@>y-חgc*oMd6d fbbb9jvOx_DDDDDDD!ClX#/# 8Bjا,I<v775"D]F]GI5<[QjԫE9[ ..(3d8)_긺{\~cF S~{ș KBiӐ>.|?y$ lb~t(P٬PHp*H:)e'a]:(deʦo=?{6mP3o1%k!5T}"6N-C9δJi[+lZ| xOG*ŭ=&lP֋?W1_ 5)ݮ)_̆焥y uʒ&+s]?_s)*i/;b9 RuKb:qwS7Ƴ=BWGR`wvF2ys gWѸ8K; |n9#BBj''[$E)Mۯ˘z55Ed.ils{Ӹ 1$zdC%Mi}Nݚ=_-jv?נh6w/eg>uN͢p_{GT}`}%Km[ɝ /[l?_lfL (Fo} ާ9:qn.oG:R3`"q$%ߧWY fr_8xB XinF4J*=NըT'f {T{CBb`%]_sƿÛSt.O~X6E߂|@r~̭f`M"Gx9' B: ǁhl;o{X=I$Uo!=vˆz͛ kP4F:̕8=y;2rt.3_ڌ;[/|#h5SWqɐBej%F2 */T偅m6wH_f4o;1Ϡ}̬9Z1i gtn%]iH??^1)X3?z%x&WqG^EqBZY5?O+rŤ"[=ْ{E1ve{87NB+ cA86z "|,%h<| # DmaQ:_j¹"#JoCې,pĨiC4,N^-Yn`AR=2g480f"Zj~~~\[>#m|0]ANzʵez+'w"9?RV/psO@v 1ݳ'`0޼ǘ/)Nf~gy$~"I9Rf~"$w""""""""""϶Ǩ`N(DDDDDDDDDD䩓2""""""""""5M'""""""""""-ږqS& 褌CDDDDDDDDDD2}ƍlٸ>ɂǧyOY""""""ItQ` ϿrR#aE(/"""""HIC?D+7D"yu_-l;>nGRyɔ7~͹qZXY2~YBXp% hqhMk IDAT';.=}Ws>AfLxO^7fu5a$ߝ'Gtvdpw-mv㳅^->cofZ۞Q8 d4K~C|16GDDDDDD$DC5#Ùr9#'2bvdؕaԼTi5EFlYskn9y^دg >>ˆ_[7^ ӛŠ[s5Vs*׬޴>nb.Ëz1z77e85)KX>"i-kWN}΅pn:vdTuM֭ϓ)_%w >Ւf&M̹.w҈oyeN{9yFHz,I}6?DvN۳i;~U?n{^a./w~m|-RTGl@Ŏi4YY "?<Ԡ/[ mx;9@Dzz+ظ'/9Au®J}YTu{Gݿ/pT/S 9wmCm9yw:VL{[N0= 7LunDmOG8Wd54TMَŧ~ν'"""""" c+Wѹ"~Bg\Ssc9Cd=,)԰7_^Kʒ^-F!򼰒"G _91cSO;x{'c^?Z|q k Q qpm|4R' `z,7B]|rt/cܷEϟ%8c7cGF&Nڇ{OgIC>i/;;ϲc2svKFJhh(S9}-"""""" ˜eعuIV EѩEf]DXfqhnQ;JsT\jx`˖yyX:FTβg< ׬fV҈vqo/rsN:ܚq}Z؏2iT@oǥxh@itp-yvyb}&J,2R>2MO*]q׵'692a8f`kSRLa.;F;Y oo<\fғN* """"""")DѶӄn-:w;1EX̚_? /[l?_lxyZ8'.F_Obۚ=_-jv?נh6w/eg>uN͢p_{GTdɴ }2SCf{z?Dߜ&>˓`#Ʒ ?'-B#"O ewHs~ 9RwI/? & zÛ!g^ L4o}1ޥ)хoa҄O+&t~diT9u߻](}[:W+ԩOMLޡrҔk]}) TfאƐd;"PHe~x vK&sb9?qȖ|yI?Hvl ťT%,%&n%dC54V@M tgo!T1[RYԿז+ iq0X;%ҧpEDDDDDDeiq`#Dj*Xr%M|ɣزq=~; OY)CDDDDDkw|'l D͆C@@ X?ocLS'Cd>%CM ITsbĽ8୵Q_c\'oz7\DXt$* 8f%;^^㘭\R\RǶӬjNipuK_+9M4? "|DgjMwfh@;>[x^f.-W۟髃Y;g;4͒_7b_pL-PM|cd\ΰz55-;&*븤LS:Rd.iNS2e9$w|c./Z)] G|Hl\;BitkfY3˕d=-ޘ ?`q!{1cZ];TGS SvY6w?u_HcX7W~A^uh㺃ozBֈ<фЛ6z1^}lOz4luzQ2ɅocH|u;b@o`b1E%_8;:̅L@Mmw0z1.]wW}} nR;qg^qn 7/j254ǴquY\}8jak'옴n;3f]b'i0a2>6;9.^4e}q*d{?8żutl 6Q]Ϥ{ØHqv+nl4qw,`z0s71sE3p92qVvfj^ȓjRԉc ZN;l,[r)f0sfN31_Qݸ^`.bvelwWz jTQ~lQs?tK~I3U=$tI;a|M]̲g2'#*.Ssc9>.4kIMj 6_&VjQ!9!~2W0CQNf߲hjoKuNwYKͫ Eftb KzRl{5ӳi_ 85eĂ5, %oѰ/\ '3 =mWY73YU>j|I\ w_Ku=bW._ICR`N>s9J~W!gH)_ 'wႆ{IZQ8i??4ӟFibkcI,Ihh(;&:JfKFJhh(S?x$&lY)WMZp-2 *̭̓܉VsTjV6Fͱ%m!ԩNiG 6~)1h(3TKyuK}7vViNޘGJc˖WȗK֜o0~>^څ_j;:tlK^xAʩ>j)TazC6z/Ƭ(k& UkPE ^0{ڷawh)}P'PY,{=:v#ӬtSyUzL:Hpt,љnqn.`y&wT.À|V'NW<8v*F%?w&얣iNĩCb0H <L&¬,S8/_JR?i{rXZ_;Sv8'w_|hGDDDDDD$%9vwf#'OΆ|{< پex)VW wKtϑ5kPVBפּ6o༊<:Px^fHWv̽h'2&)ω<>v߽ISxl~J9 u [lXΟ%N9f"kFt2[Z)?~?u瑄Q7+SD]?ɘc;Y3'"[}ǕS'f$LU!QjO?dt9y|ĚK2fɄ^=Z;2=ЛNNf,k?Ş! TM()ÎcKfZo2=rC:`mFn9S2h $t"""""""ϊ$"ꕟ|Js$C[:Xp[8$jj1q‘/z(/8Ùio=k7vz>%,j_ic'zKPbl6lw^lqɂǗ^,Vx&4X wޝ?j4a=m*άͺei~LZz?vqZ;|,OSpΜ_GVV̒"<8vQakqy A:QBi@vl]|~g#Y{%WZx%7g{y%KɒΚ W>}} kDY-SfУd!Kawp+J%L͉8s|T(V#VͳoWC^9U Eu$OjTl:B3z( }.@.>9Ipt!?nGt11?1iq!}or9NY+o1*3raZFNa$%yql+oMՀZz'O _4a5~"iն nJqWGc.5*[7VKHNN7` :7'^d1by+_#e{_c|8}5؎/dĐ|4/ 4)k|=Qz^܉xm85eO|1ƟSw5,oU&w*/niV|Y=ɉMQ)Vr(?ʖHhh?A%)| vГ4F/d$`l'A(#cp;|[2lq{L~DC;?gܼ!7|H{qlt]rtO0 r<~%Iz.nà~ڍfx\?cfq T;.""""""O$Uo!=vˆz͛ kP4ּt1gC>w'w@F΅c HSsGb%q#}Vo ݦ,.\X5AG:/-gfds.龈>#h5SWqɐBej%F2 */T偅m6wH_f4o;1Ϡ}|( xݘ÷pu'yU9W$ъ,XO69U1sTKOۏmO_80pFwV|&A.w}q0f5LN@>o>f}?͚#RZ4aU,Uߚ7P0^ɛ+aغiS! CRߣkH(k|㚌1?,pĨiC4,N^-Yn`AR%w"""""""2g480f"Zj~~~\[>#m|0]ANzʵez+Z֞2`߯:\D8%~ꏔE!""""";o>Viٰ={x= {I2y$*EDDDDDDDDDٓI2$H!q<k}BDDDDDDDDDcT0'a""""""""""yYٔayi?ID'mѶCDDDDDDDDDD2N0GGG'e""""""""""qL7n `I<>E|ʢM̍Y0o~2y [6WO'8!sR0ǣE${ZL\pKMH[v}܎Z)}!zovSlqu%mk&"c;>ыYI喊/wO{^R=,Gtvdpw-mv㳅K̥j莰 IDAT{}3}u0k~GruY&YVLB ɶ9"""""""%oV&za~D?vKb$95#UZMbt;%sqz/[֜CrbG@a2p#;ֳRs&߳ 9w>Xc\-=rj7U ^mЕo `m$͛~ɲmiωw{&;(y(S|7|4u)D %D4´99:m4uӯ_ sa!]kw`uA6O-oPJ)s Ɛ/vx8Ɩ֤,/a@u_-F:Q;jncv6E2`@W31mʼnk~hhY㒭Y}cl.jqo1qtr7^/T20ߊ'3+McbdSt7N.&} ׿K19IR͌= iS| &LL6WQfC<ƥ +:?\n:fv6>i>9568W0#clL/̩]sj7`1ѡMBYMGciY~MS@68:2Y|>27c̛/`<ݜ{¦j_ڹz7{gw2܍g>Ro͎+1[|c g `<)PAdnrn3\s_Y6q9fy}mta1/937''Yм,65+cr}LQTh21)D݌u24y1/|9LumnF>6_Vt6.Ǚvc 7Ke6N>wNN&[e溱KUwڱc{mݙx?zG87}6\5~lPʸ;0caY+̑Q3wz3K<%CDDDDDIZl~sı-'6Ǐe-]HDE93ǘ/ Mn\j/-R3 e12ūX[5*(?F9:sRު:ޤ]k>W.f3“̓s Ω߱RxP|5{/et+KzdE}E9e5``9|K{Zk>pf{񉴩ՍzE?uQ~z6Xec[-+QGٲj;crO@s`~}x"}ߋyXs5;~Vۈcr5K' Ջ.R wY'FԲmoOC)ֹ3Wz@;'f`ig_odGVrKN>q& 2G: /ꔼst-E@tmhC+D;3ݝvd.Q{ؕk3 j#'ý^_5ؒ}(_La-ww,Ie; %tdZ]lI׈1;B eW`*;"""""""$фc9+>JL ^A@ecy0C;j2^bVŠfبu9Zc-D:)ϣ2% eFt;nОr2@*\)Czl7 N(Svb(ѢII\۱5]YI绯zJRɝe}l >dyK ԩ^gJH6g铅=GވIsUQ)v~X9M|,җj3 }:_ס>d/SUSU²&˾O)W,&"7m@ d_a|#*ʁ\RwhxWf te >:3M$?lwNqGlZQ9ZKmc٣-(+lm]Uq9Ȝ5SAȔ%#iڭOqr/_̖>|[&țD&¬,S4q]SLa~ڿ:/wk]_8IF`?1cbوɓ!o<;goC^U镪8c;]H5(p+mV췃~p<:Px^fHWv̽h'2&)5]r?vفg=$;g)|R*V߆3l6)OI>"it/Bf;<E*!m$D\quu՝Bo&IN^{E|m ܅'ptH7ٳ䏭0/֢F'39[.^7yN±pDb%+4Os@7a6 uI}}˽N3Z\Ul8e|y7ɒ:⛮@DDDDDDYCdX/?CB7h"pEn<(IoV,֭Gbb8VHp=?Ԏ4xWҷܵY;gU=Sv#v\Yp͔"-I1`WJӶ !IL8[~!8Ƶ)yGn,7X wޝ?jxNN8bю1Ŋ V+D=;vptJDͱzZ7 qw^8g~Wwˑ+ \8?sx~H̀eW6C&fsrΜǒ)+%5%ߚA GϝfkqX 3OF;} }65eH%wJRָBR$9lfCAooa͖< eLZ!z4ӛ9d)LB&7j*\w3!h6'L8Fsp{BwPX.NnXK=|ĿlBs+d8#>?,`cJ;qkQo巹V*uDj-Rr2=AJђarVo?[ H,Xⓓt ߦ{EXκs/UGd̒ sx"7| ^Ϧ*e'/Oi~%\ a孙(R$3s6ط3Ns4Ǵ4_:^%Ʊ0~鹰~5܌yV_³\|b_bҐ5O~)Չkws1;ڔO ٹz%pڟ+f(؟I PA} ޝ޺8ֳvoҘwѴ77$Kf&* L$af {T{CBb`%]_sƿÛSt.O~X6E߂|@r~̭f`M"Gx9{P[8{8ovMPҏ'JЯ{U̾y WLḡDVB؅2 uTۏ]G? KD;%sRT/'CL_ɻ_ڇ9oے 0ve{87NB+ cA86z "|,%h<| # DmaQ:_j¹"#Jo+.%3ov~Lλ?bӼ2#f`h5~3ݚ sx[R)5:QӆiY"[=g$t QI9_`3\1w˜LGژUVʕ+iڼ /1ϗJ  DgOl\C)=k||%"s>eQ,^3Un$`u&5!88Y,̟71H`Da&N;BBdIc n %\yMLNՒc$>"WΜkz2D$:H>84i8M4|02Fd$:l%e""""""""""ϔ/#NN>,y"=stttR!""""""""";f رgsLD'7n `I<>E|ʢM̍Y0o~2y [6W1kSRTڪtХU-QUQU#9G!!S$>9;u:[>>*]# 3ImNuY:)~859rX*0_8^ԩ'cRJU<9rX*0GEEɈP '**ϑ)߉q6Llw= 98% ̒!U Lz}9 fc=栗57R {2GپH>Rک~(uer09hwI7CsFPn6-<]>FN^1dɣץkĶj]RBw>?fd8`L5n0Z]&O*\Ya-U"G˾]*׾9'fȬ|O+M-2= 0iB#wiT.=-C1*Ruj>纪uxc5o[#f.F9%.g[b_Ho4+BTRgU3wQUj69 qBW@q9edk*GZS+% #`02dˢVָzZ-_S+k4q<~4^*n?ǧoS+lfw]ܥO5W/͇j H!':OԠb1 PjbR9o=GOVosu^m %;rThAZ~rߗXb/@?_ Vauޤ<'`a-2@1\HKҬz5٧捝6qtX脶.oХs<ޗZj<ܥS暙΃d{?J-ɯZxSV_Z6% ?+_~}>KQ]~_FS"_o5^qޙN6ɖ#rȖau9GOy=z |H{@{No/Ŕ㴼v>Ϝ+4@6sJCz,֪%Me7iyK M+m4QB#hu֌YTpUKkJzM]K%]ڻ U^[7$LUVVLkfNzjNwtأ9ԲvAGoDʳ/ۯ/U9T۱jV.o d*?NyT;o>dFi(0` s8:#&zk[ղoAs~qE9"ӝqayRk__3j>*d :]n-0_?|HG]V!l)fcvO4lVЙ5mzF_!uNaHML l֮돪Nm*^*Xn~vɻy *p}mC{c:+o̧>}nҊUL16e/RLx_ݽ/)9@zgo@ pҏ?Ȕ<5SL2u=H*Хzr':Y~ Z^ccmϧ_RP7IUU~a}`މSȉmԌ]zs~%U 5q{pT)%deWHWkWVsrIr(⣪_;ڪu>68RC-fe?~"Rvv, +zs ~[Vz0xEmX &,w\7.Z,[C=*qU,wPUo%CkaOwȩB*_6j\5KF6ٔzHpTԈZ9{eZG'bm*VԙX#'$qzylq!oϫW\FGWEoUSVI #EYgwj cs.b}o⊍BNSOiR@Mbz*6sA/]O*kp8r#璷j) vXY?ܭ.T(R\m[u֙G IDATh8f~  iOu5 z3sUZc^!lF2.Dz1˸̭V 6i(9L)u?onM7u6<14}o-W漪\32r)gOy7 ~^lL%^:+RTCƸdCځel*d7s˻rMSs/+gP}k126/OPk%M$S=+Kƕ8Ɵ$;%ɸM.9iredSy89 =KENI[dX>/ Coi0o$X]ǵO7]E/ToJIS?U|q6aR*[>D$f:BZU,M3EI?bW\/v s=|;}5-7$}CE]tIoƜۧ1yCQ`qH"e킶WW*'j!)O4J2260R}l^[Y62*Gd Rqſ>n]yB(^gZj|Nt)/(_ʖ5t,9Wٜ͗{u^ee$s^[A"]U&s%f[-+ -ЪoU,I NnNHҐoHǀpMlY^Y>WJ?VsujFwh.iJ_tX mjL7n=Y]c5*yU]+ɩqj_znZ~~ݳ]Bjd^z[2WQc2 RUF_5Gk@Xwʕ[2L?+צ.к郵 ꊌ !WdlE;J3wдTrSMX?Z=JݾmVO%xLvqꃘ*TcJ=_ׅs4l]r+wn-_OmF2YUiq^T$o/LS4WMR3XXAew1Ro9[V缢Gfn5Rߕs8͝_K\o!U0Q -{9Z3V㗌S[ŪRX2׻feЁhڍ.f.Hd|jV뽬+hԫ4DVѰ*^I%@z|k[zi٪?djԸ\.nצ>T˖-} n98Y6֯[Kj-9MZ--2h(0s ; `f2.%??! IMKf___ IDATxwx+i!BE{HE HQT"J# \DCnwf;s)7_.2`]]]Ki'TTT:6-)6.ԍV=i1`שUe[YNF#;l Vc9h4-vM;^V5X6Ey֪ȲN؈{tOi )T,k0-!**/=B6;;9)_q5p".ͷ"G/| lJA\zuՄHM{$IwT:~  =*G% d% [ iTI9fKt㪷auc0I0C S )BaDSb(A`I'耛(EDgߚ& Ohvn(ۈ&ƍhxm c99;ODWBg{lɉҏ+y7(?ZYbśR ?Bgn=*ƇXv.>+!lQ圐l8} _J¡[|5\P< O-?bs8hx!ghj族|Z ? է5 nNeoaL]BٟhzƑX_r{ҷ*dmӣ1\⌃kEMz2~!wk-4/cf|lEB'V_$l6O()&|>Dy5Sʒb6O,xJK6Bd3Z7ݻ)da_=V\$uB w' ZF)Ȥe)؃_XAA$DIvJ[Oս '8r=c*}.E)右3(2f#9^Kv#4o;f`$D1~5 'o7%Yd/ՂLb 1d!~i$$kN]Ɋ]W7McܖpRр{hJY%LoRZ,p"Ɵ%\ {gIXmL>YjMqH}gaH1b.ً-K`HȎowqX'N!ɩ|DJ2@ȲDɊv"phfUNS)Ҟ,J! 6#B|gTr#E!$%kn^U 9qM7,Y˄HUI=I1DĦ`y<;:7lɹ KxD V>T(Z'3͖cdlLXB$c+Ud{)S!GZmyLduc-gF*>bIxRt.h򕐹ʩs&js`gԱtG!!e<hXe2vG\A6jBhS?j },b?rT=$Ƈj[ϵ+׉QY#MdG !35ޘNd.c3,N%,V 섒^*v7[RMq0Bi,7.&$L z'CB=cQ=#vZl[>ks6܄ m 1pMLYf3/|6;\]u$%Y@(@cSL_Nr%*͜Dss{-zG+dښJAk/c ?G؊YL|Gla^f/ùp+SD>#td윬z|D28p Mv_ee=GiPlņYl 1m 1M,QeRFuwS3H#cg\$@lL!9Ŋ&,>wv" + 1ɘ!uBhdUPh8ء{+bBb4I1۰)qtХZfɊUH z{{5isI!t֥.<6d"ɔVN@e4-{d@`N$bСa_E@w#uqd!,ف/c089!^Q bTQ!6 D3G#흓3mτN舓=GhM}Q\E%Ȳ;M~OSHi OM(mNس'ŁdYI$IZ"5!I`fU)Έ׵$YBkq{:**Iu,+g| ĪW**yQFJ!]6Z.yC ۷ ~3g?9qb&O^ZLiUl8$ǢQsTϰcvNDڲ_ 9%Gb\Ҷ Ycyr&/(Y`bx:;aiC֞{0p!X1yk>ޝxw5و>q]Q77|*иd^ˎk[!hꔵ*cZFNu /OAŃC񑑤Ī9ɧ{8[}Bwvf]oK>~Ԯ?a^ 7I m(㝳59;Xr3F5U: 9#kQ#X\T*8[߇N,#YF)R_/Q6rnF4k\0+J 鿆x_*k Uj|#cX]>B Ep;:ӎ`t.۵4t{y+WoS98?.n(K8+g脽>9_Ur˺,"P"ԙŮ]Kп-cQZ>N%D6lyƿ2;6av_Q9T9z(⽨-9ίi2 :H%LmI9;AoЮv 1 ڛvs><2 8id4(&ѧ(8V߀%3g{wv\Kpr=:N1o20ao]A!hp-l.ٜIn%Mz~)o2No[A^<#|-=g?|:իѻwr:tՄ[5F^W M#yQ~v]j1wcId6|MfVK@`V䭍ad3uY)X,R D\>O59ݚA+"B®hm׫q1JLDW1ٓ؊Wqa?WT ! iRIbZˠ *BH)93 ,Pny~jGQjF'V.š&Blq0d|zطon߹3/X z|DrPjG#էmC'Ja]^ҏn_>9qVyN1}_ i~9T8J`;R"z{>8GkcsRJnMh/lݛ "-j˫0sc z7Jh{)FR,eBq$1?xxzxwu@|E$,y/–G݋Ə}W=>]y+..4 =6R?Q <&ʌ$:!485CKf6ޓBkmNSX=jw=CzPv7Fb$`V3X/|NnI!s?J~x~߁<3]Ct^߆@qΧ, )&4[i^*늶]|-"Y|~љt{3ǖIzbxPk[Kql8otCv/FkLU.CV?;}dudOɲ( ?(xo{yAt^链cud~oW=FvJoѼ ;  2HuqD<%Zٔ,o uRhKPLηWR[YRi eK;3slfնQ1J^C}ǸKx"\ᴥۖzh !4. (5:'ty-U**yU**yU**yU**y9c6Iu @tT'Eu ,x2pP6Eʥ %&!! >o3T^ <<ˎQ@䆜U**yU**yU**yU**y1{}r<= lPgY9S"Mzg;X3FU2v&p$JQy7t!r0-H8ɪw;PvL [y|>_}' $X@|}rbR9teQn҉e#m;vC*ys63K{!'OߠB|d)%voھ'tx)C{n޶a>1wL6+4ɿa '*M 1,gÓ;gմ[Ei‡%>`$KVQ8m;3~Y8{G Ez~aagɧܟ #,gY縡\ SukS,zG2 9eC/D`x㫣4=\ʄϿ!u8eK?DӪȗ?52;&&tkJbATUH[\Pf-joJ^?[?t罟R#p+B"(ė33>C|6e= ADbZe,wnNc+aѳi=B;]"qોG# X!nI욷g$ Tuգ^z)lOruWեRao`::ۡs@|KxmR >.t8xQ|_6mcQ9$?Nztvn/cp{o4lA7|)l$kΙ9b( CqBK}s%Fٿs cXz)IN{ [-%v?GRc+w} VY0In֬C߀S<-i_?6] ,3ۢ]&">+M$WHGY $LqSh]͇_]H r4\ɘ/E:_j[UJ\!*"H/f,?/rqL݌']^CTxT&1,\kTݼ%'~a%d”17Ga7h٭:Oԟ;iͱmIw~ @$pnϯM;رi1Cnݙ.VdM/n'lLC϶8 7p56 q? >o8>x8py̷VөhT;~y%&ኻG&e53W_'vlƈu1]c2aZʎ́;Y3=ר?z7܍1quٽ9pp<$^`'p6BL4N-GY8%{@ChԉYWbr]lڷ6/dɛR98?nlU IDAT+6FӣhH݈Hn+;-iV5`oo=Z8g߷m*mJQ|vWgJ^lz6[.nFy9ꍛ̊1$dV \~dfzt: ي5 V%k1Іg@r;ؖOY14(ND\ qpv=z>MNB;cjiAQBvNI2.@_N5,)#|}V՟RK-ޟۏ=mFH.ܖ74$RQz=nanZγDIJw$FG8Iqq$ሓ(;h$Mfٕ |8ܲHH 6 %8ptrD$ּjN_gYD NX@İ[s2a$FhRV_;Ş v?F|c;Eqs+S?e-'ox3g֋Majӈg1?yJp[ZUq#%AC.ye|}$ZQWKͳ땝LEKd l͗|O$' $CAȄGyi _rqvn 7&Z^v&niqk $% z* 18Eu$ NK DXH}S>&B]iA Qߒ̘)N'$[ 6n|-5WrkJZq](ڬ|ѧvΞTSƴys/8ǼiYaBD^sݱ,N'l޹Qv&_r&mܞ _vѾSm kV%\xwQŨGPŨGPŘ << */ 9m7 >Py8r .Kg¥WOW7Ry"^X8GT1aJAJAJAJAJAu|MTp*Eu Q]©%3Bu STpuhTp**/(UT/"-e-TT^L^81ܵߜWE%Au c6(BTj>ۮ?>MA2joW[$?+ebok24:4_vĩ g]l'\^7Zv33_&vr`UG빠)3bKbwYk뽿jzo-H8<3G-`u /$zKONg5lj=8}'4h}>m+`9y8MQÇLWn+cZ䛄&L[gk9y[d?\_6uE{m8v.N8wF܉-/*}DuGl(b7DLm{hW-TpvZߜ.fo%y  n F~wTDE17/o:Rn iEE~ľk.~[$k ąQ^JBNB ĒYu߭1"W\h^LGĀ"E H1]k]>5 U=gmK s,3K3㘘XF/5m7Ŋ6>¥,q,0U33{ }Xqkdu߭ʁng.-039WįI[H볊Zou,ϑykvc?[&SE|;LIl2ll$Nr`05 7zn|*xUG?wzAPDw"[kJ ?ޞ]e&͍~Spt!R4aнm]Ӹ K[mKl#p-UЌaû_ kT믿#wD7Znw_!ykcw;v@|.Oְ·fZGz|7rZ$:@ D"^Ĉi#g2+]ıw|לycp߫D:.WY6m%,`TM&qNo>|3\q[ x^pK\e$pY![ E8ϩVqۇ:.,Cw&<]Q|Nɭf%^oۺT*[23WDɟ"l+ؑ1ѣPNǸh鉯YσNړK;'[<#p 2,X_/k.kԕTqwϚ%3ޓBOђkkѸoT3zM^J\ q c`{n"o7÷Lq=#+re#]ǥoLfop.[Urq I>?{. _'{I[xeʕ_㢦$C5-FP wN]E  W0}YICɒ('s81HנpѴy e c^yu=UJLQV.bו{[vp=a{1:jf~z]W> mw`~ ٥*LG.1LÙ2ÙZ13ǎq+\nzj@?dFUC?;ovFEga xQqx{F`-GUxF$n>jδ^~ \N,`Է)4ן yuK8@'_rk|?P5,QjgbS4(CW0qH]\|5MZx_ٙz~S#ۓiRarZʎazMm($򴙹9=녃2sf~[*ϐsfx$q] 6Ze2Ϲ!3~fDF^٫4-?̼/ګY/ꏋN%7.TT^PT1T1T1f%JnP]=Tp*9Eu Q]©d% :LUQ#bTQ#bTQ#bTQ#bTQ#?o8ȟ(M,^Y21y.;F7й7h6scK*lrF uY @?OB[_8u3_ًypo:!DNtO?ΎAQ;F\GK!*VDuyXlc߲[~ιSNϥq`yn ϼSIHԪYZ5*SMBv/BiǪKr{%J\-Ofh~N%H_R2ݻuHPPA}̨mPW~oeb,lѲL3 XLϪ鑓QXiG^P䋛9>ұ 䡯ڛ!Ug^xY a@Y3|% qU4֏Vq%*+Q$PTX3S<|m'F8ekz>@2賮=>Iȁ_R 5dza/d`g|Wo7#h2p:M㋹Yν :HV|*koT***{ਨ3T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1˗IHH$:*yB八3AAYyIɲA>wzEsHLU)/9y.(Q/-:%sR`2$I\v"E|+W#,\yys`ZT G5GU#ish(Ȳ((PӧOkȭ[$ NKrrrZ> V3]5BEj6Nή#F/לQ<~`oOݺur%QQHDA\ 0(ȈH, wcx{K:'^#49cN,WHL 细$2XNI6&²g?)S>>ܽd"2"&S8: &alMnΫ0䃅,U =EPwWfԎLJ+leRէ%McJ깤x}-=|ͱx%=mk#c'k.z$t:ǏY89RLISPL)hVjϟGa4&#r7EڔŌޒƍӦf ?I XvCB>{X!o|:B][@s/:.?d]cQSO @(V ͇O U[a0Dc6+*48C &1! Bttt>-8mY~0#$KGzi!}Z-@( ;dMo|cyk0ro6΢kexk6ǘu l3lѵfښml #iAJ:R SSGٻ#%''# h"..>|DYEPģGneyս)p}+[;~t4_=P%FCҊYi_/򣕳?MW6|9z{7>^ﳚ\/+0G63rd2p {{{<jVPx!8mBd"9guE4j ̧Nۮtٕf=LZm)v euتѸi03V4qD5&|p&㍟ѷBF6K*9c唗JO3#|l9~>ĠbbcaUZT9{%UHcc,`I`_[% zCXx4pSS8uOӶ+fYϭ|2VNNINNWWWB`q6tZ){{{L&Fl\|5o1-s_ g2,ٖB(~1Yٛ1X H F6XքY}4jha߰՚B'c>˧1~eb5k cE_㽹+kwwPKI5DFEʒKIHHAzi%@6?M5 X`Z,uNjfPWߍ{ =РՂX"u(F3'Yҍ'A{F);bT"㮔,+6;;; KIO1Ża4{.ɩ >Sk)ʼڗA|PӔtt:㧲n ~B(hMY|7+$9L&xpKU-iNd \~<%'tFk׮iE9#K2LNG\l,zk׮l!5}5ڷ`Ŋy?ҘqI^1j,1y}rw=%g=b}3# - NrP [jϭ( Jvvhr;d͛Fʔ)fh4-QpڵLӧOjXO7r Ǟ$p;oFY-`I T%0d߱_^eAS^%Q6qCZz<4`'/KXyVٳ\!**&1- `;dldYDϞ=sMŁZ΢oݙѺexF/~S>BKgֿ4\F/-^]21ƴQp޵-0ڣ;0F¿eDc eB6̨ٖ[ae a9D,1(7Fߘ( Z_4L`]{>/=/=f8k3l,+!40UWFIԀ ]$/ؗɮXT714''')** L||<]]]x^\ߟdlM kV𘩸?kihڙGu^#TIDAT:z;+rfɏԹx2}T ?t#\g ջ3o)bdS#O6S]2RSh:v [8ظoI"Nro:mm ic<ϔ*乽3 Ұ-n3F9 kDKkcعR,;JNc}eLv5h$]_gi+Zn:[ƌ6[}}vlnf3fɄ` ::F,NÁiWQS6ظb'#x [9 G;MoGy'3@kJA7o= |t>GA=]JѾl N(w]¾ L6??#2>f[Q,;z}:]:j5Z(Z vq:}sXgffp\LNN7|[VXz2x=]ݩ;_<'Ɂ;L{k+Ch8^9C7V5o75XkY/ AH⍎`jB~;S_anF^Zf&{V쾮 ./:"aFEQx&>g ~-a' N_ .R#J_5Y{?wl`h IDATxw|TOrg-meFFP"*@QA2PET Ced+KDe=G ("u_f'ޓ$ WJ(֫Pᰮ>>^eMfB!x.+)6.S'OuSt-졺տ[l1c2>Bq4'bnڲMJJ? `kެ@ jtiڝ.BX&\/Yƚ%X,`U5@;]N!w]q\L&l6kHzZ9Se&zyy9;]>!w9Kd2a2^5&-!94<ʚf9!xP;HNN%--+co6)Xm6K5_yc >dEE ByaIjb ?d|i8b!]S1~@5ĒO3eRM|H柱wm8R8KViV=bϝ-|l6RwPLlC\1llHOAcX$3@n=-!rK+=4gJLÉ:.'qħ8q`:Ōg>F-S̽$lq6OB0_SB@5fDJAX`/ sU8}{SKgi0-?8VpӸ@'{ppY8sAty .SKVԷG30tmU{z7Aߞ[ϿJ mP1%{S۷ ݺBKlE L_;6_/]2UX\B1V̚ʽI_e.r͆)viZ}pWĝZG#WrBQIW^jRR`E,NLbE^*21|QH|܏u3O<|TY! 75m&#\PNӏU")IqD"(>=FmN&$w{@9{q%'~2 y'0 ͠U㯌tPߥDD&Y듉Of~8l$AoH(s3M­+Lb~Sf% X>=N :JV_}w?(zy]ww]#~5QЈ49G׏7 WPB hg`S=0NZ.Gj &3oשۛ oשEYGZzꕑEǥ]\D&Wȝ ħ^.\!)`Rc_o+ɹ O?<5ˠ` "DL%?KFYRq L=Dfv7cQ|؎tAB\M|Q_&\:'Z1 (X#@+̡ne^u~9wݪ ׼q-m]2)OX͌x )hN*@\qq()\k:+*.ERJNJSfsdnM.^ɛ̘=D v)qnX/! ^7NE"JaڙNJ={4XE9G[ .֫&&s3liomN3F,[W42 c\7rEAUz5UU8$8Θc\UaO;Gy,:i.̐JAuM%MtGRNt(^hZǗG]őBS5B$r>6 GʠlaƢ#ؠ ;Op$3PHF;D2&Bws Tif{k*bcy)I$뗳2{&ū{F@6+)'㌂O\j%Kxvm, I'9. *(f=,GY ꄼb6/!\_Ƥ0j$_dBVae\UΞ ٜ >P;|H1wMM(cFx,ޘΦInE/ұl5hQ>vt N++fuAos1doMs-mOcĻlXHJr`ݨ xXOb.t[DsF)h 7+nY=35SXc7 $9urV|w0qL_cHs9I=DZ}w ,CycgFa;,&pxD;$_o8y]az^COvH<Ǿ86S< LJjhSƑSͅH?qWB] 18|wXm"ddRҬI} 4I9q>6HOI#9͉ӥ+ br7NbIU|<3NkNSIujLVmYZח[F=SIp-=T 3^37ݑJL|:. EJJ.QUL&nnM,C2ĩ(&VJ.RRIIp:LXv %ynԷGA| Plx[0Wl@DŽu2F1s}5 bef]Xmۮ)ovbww#Eȸ,eRl_׳T=~fza2l!nMQV7/"'yK: !]+9. !\JBk(1Qg!"פBC$@B"" !H!0DD!! B! BaBC$@B@=G9|6!]ZxeB|xA\ GIMtVt ܁X!PW;NnxA@M`l&=w8Bq׸K0{C{`΁<~tKC!i'&@\EEB`d@B#r;$n1j#O&mgYrԙ'S,7!;!, |W/JZr?5Ek{5StI\H܈Rxl,fׇ5V⵻2b٫Y]'Sd8!h';SfMj?ԄOi/S.}IOJbǧPU:Yi62[-a ң{B~D iÄ}_*lO+l_8Aq0d?>ZĪœUB=d'{W>r? KT!Lێ"Unj*D֩GB*T3T z>6A9ys'3 >K}7L8㗱mLcZ䲏c}_ [_]Ct00.KL=n4vg#r6Mj6{m9鋣RqsUa}}Oy߱32';i= 9.SU+2wK5 X|y*>ߢ1U@֗e-$uu>Oϕ Fi\w!_D1V^oa6Ep.)G2l8ƙDn)8RCIQ T8N'V2jxR !m^XJ>Z7ZtwHy| ELθC9~ÉYXd'NP@1[qC]ssq e=ЊGejǬhcܞj~f%E,G`ۑԿf}rLv3p`ݘ"D&݇voU.3\hyFG: ! nx폞eK1L38fؾ_I.Ҟ{5p$R]I܊Ȳ`o qm¢%Fي^*I:4->.q5_#q ݮ*wJ I0Jsgڋt}?$ !+yr7^jauPyEK:kϬF!9rJrD'^̗yk$mG`3&hTM=Ǝ[J!1/#&J)l otjrt})`ےsN/á2'vn^nlY`nh'"L\^9.V_j:z/sJ[.ݨhzt~̨&*p)LI mϧKT XʹdҴҚι E_ nJ u7g.?y%UZgb[c|$,OQvx#2l#uf7(a5m/w ux{b^@VsY xb 繅6>秓P<)T1͋ߓ@b[ E%Ƕn0N'WEAu q9~>}p#ºŜ*ޑ*[!xHmR`i":vH!ĽEiiow+!MF>5/T f5s19 !fZxP{`Z eB|x^^j^x[! aYz7Bsws sL|/jA w !y9""oI!0DD!! B! BaHl߱vC!]Fj96 z8!Y҉֞#kBdﶜQ}UuRSщ[:;M#ѻ,X#^*C幯Ng쬵|DCϊ۳Y*N}6y:ƶyu=5%gx8fukDdLSOȖ -,3aҩT >ٍIG2b SW6ytzĻ]`EX+BqJi DcDZ28~$ ~IB zc2VAK1pl_ݑq;kahTStNhצUӆXUS|,FnHDkRg? k}ͶNV`%^2}+6Wv \!oUBAvH!6;nO]GsC9i i;x`s$ʚ(L)/U 6M=n]U{Hk^~MvEc\'ٽ' V2XjQ@CunNB`1öˢ0:ޝBl\L' |(> eo:y9n0 OFaz1^^l͛}X4Li.1", IDATśٵw/{ !KFG+lDBnUX*)? ~-&㒁TgujNVO2pZ)[+֒EU-}Ҥm_>?- e:ڛ6:2p.32[.7mgXsLلU3ߐQb~ Kѽ0GɼSrqBdDW;vgwMЗ;xa6()υv[rmÏ3V"A8Nowf]imIx!Y/LL*f"TjKƼFi!+bk;]!9v]!ƟO8xKvoF?9y/[-"?d#,^5?At!˿ D>ꆭDNJ7 !x9!Ic)CW1Mn\IbgG~Dҡ-X=C(z Krb ! zԷiSfW+Xi/ lJ˖#ڡ1wsa`匛*uxvLoڹ)GtKN!3!nc?G?ż|$Q&4HzUvTwrjQo2E{*yåiV,}gNBqH*{w@/טWnn-݈)yi|+/~΅J# yC-" CHMA$wm>IYJtjY㟼|G1\-b[bJRwIy!xSJ8ąz6Sʕ PND42ϝ~ P K@i!oI ı2]5iK<\9_ǚѲD8FP= U14g^Ooϛ@[}1}[բZ4ūDJ0 qB^` y0B!r}BVn(%=хB"" !H!0? <3s,9k%9׈B[Ic޲fdloG-^Ë~KB_H k# bZmyJu,q !ȑŅLj E:L$^>/7EҎ (Z e,h(~B!r&Oa)TY5d˔K_޹Sxuc BA,=nؚB<ƶ3Yb~w5L; ?ß,b.e\C[qy,$KG4\mdDO3H6S& 3~d\#ѹE"~H#<]Bq_˓qKaˠkh ?79RqsUa}}Oy߱3h.boZhB}{SU+J0t/h\:C?`ҷɀlo=/zyr\}}{ٱi9VȠƴw=Lpԋ牾|&,dtn2 N.BK@"ʔ%L(Ozu*HY_krIA,E#|pmGR#w+(>"nO?-x'2`\/Rvay*V))O:B\ȣJc-sw v}ؑh~`Ŝʹ~!Bn9E'~GT۞ݴ?ž?c3y؇(?Ym;K/Fe,OTCtwPWK9syS}o~ymڭ&oQ4jr?Z5v`{9+Nv^9%&J9ttt1-}~B!JŜ*ޑ*Kx! ԭ,Xr(/gu퍘|"NB!-}-! !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0Ĝ֬\~;!Dt!B{BC$@B"" !H!0$ח;~D.DGߎqGˇEo:q?iV.=v8RJ\P_v:nI[@Mu:"?Tcoq?UN… #_.\gRf?;>K6"q?Rn9quVK[m?R}*a @t?# :4+u`t%My9d ?’ ql9&L.*$ک]Foum6 ~Rx49s3hZPI܆:~}vVCw^KrE o Zƹ@ޑڥ7asd:h">*R}3#!Dx|.r|8E===H9{ΑvwSwrNeV~6g aʔm$_5Fآ<~B1[Hг$-H*E \ >AOuΐe%<={,uOXA~>xS!|{"xEvL{ $.ttSOwQw/7A)J՚թYwϴqotkvK@ 鋟7>l XuzQL&Eke6.W~:qqg+C]c7[Z"^;Ʊn >׉[ᱞX:)w&<45]Sqq|+YB'd]=gsˮ899ho%g/7فjAp|z(c1a?ؽ.睯M9qg v~Ijֻ6 ʊAqS;=۾/}>9E9ŖYcId ߽JV`.?Υu\]65{vjS;ɬZ2Kǰu>J򆥼PŒ.Hn!&]LEIshuX*΢):J)/DmA+ٻ;!v͈3ye$ C>V.E?(L[p!TH}zׯk/>Ωd4]yvU{ "~9}<{p=muyihzqf xFTz[4i6_PT u^WYt2]|vwJX߮m՝?ͻ3FP$uG]XVBU7~ȶ |"R9n2o$/ɂ'}W؉7&=I q/ӻ@eֿI;ɗ=ZwW) b6N&S~N5/cP (6ﲺ{ }Y:~uv@gh9>Pqqpa*=07\'95{SXCX:[6@Ҏ-+Gױc ~yΗhP+&!(S1PXKװ+V܂)Q*ڛh*Q nwȇ"jZ {r]W+J9rw-~?]q%}b/+?F"yeILó&+JwJLg#_qh_xp۵W}6T5.M[Vut|cZUSj=}zK?C:Gw2e]N'í#ou,mG-4^dzh2V쎞ۄwI6Oe3P8ћj0z6OuјoT-C nʸt;-&f| "2z*\ G/û_Q~Z+z<(My v~9CN"kRe'rQFu”k>4tnT6ᔩ2zeupNv39K(VjyW }1"VfHM!Uq=qԪ_{v廯A6vHh6 )g`E ̢}@'tU%$,%<\?Ģ夎j!<ܡ&ANV|Q\a.\uT L}^YkD=ꅤoamjm8.DwiY[sj<ݬE R:omJGOK!Msqn.N*ӰWekO.5rnkY?w.B]i.~~k\Y@):eIz׮.sbRV@!٤u|} Fׯ|17=s?̖j}qb㽘,M3U:O\&MG#coֳ,}B}?o` 'u"~y/bjZjWŬdsY0I ep'WY,V \8Kunj٢2-B֝?d D6t)+*ӷ]QT=ej=(\0{I]ri:{:&\Ό,MǓI4F Ϫ!9]MӲw}є](Z.?^C K>l=[l4KImP eiWRT,cMạXwAfuaք+'dT˖5Ȯqg=.,_贋JgxJD*GՒASѲqeۆ\W67+e_d"ʖl |$N7VrN]Y?_)QW0+ӹvs~嬎؆l7yߠ 9/Sm@ԶqKۙݺQb$lWG6KbfwYwWyA*e,[2KLY]Y%ܰDlחQeV&tv/hEٲ~Vm3،^=ywJgXѕ`ڿ4;K'o>Un)8*k~|~7>3:RD wg dhxAiφEL5}.6t/OJk$K3%+FO)sTND;?/1W3^Iz z/{9λפCBT(eWEhNҥQaJV3Kf*֝Ƭ Ҧ:ST27<252]zYI^~eBpƜOG 5'|ӟapA8H>|o"G#z6vrelξw*QJROV -ߣWGg[k>כ}h^WI(י&R.4YF0߻qIxVB~ ?1n݆j֬_L~a"Oeghߣ5׺2,AJ-)y^īλ|MGdP5#Xv۪Q&ćxrwȱQUSv)v(j ] ΋ Jxiq$'PaV.U{wp$d2"ɸ :RWOQq*((EU( kIKuVUuW%#19'3D1zz^]$~yU?;,keÖ'V* "z7톏^}/AA/WnOAp!Jx3\1\(O噈=?MAp)"fG?'_M" >>1zT%q RqGg6} RSSyŋ?s69Ĺo0"8EvoOeJ7ʎC!ө.VY" NrС IDAT?,\IbilV=ǶɞYvqyuv4[Ԟ]7ySJ %%0_oE3X>ںWsUD@;4oތao>SOaa!jȭ[zL&73`,Jmؒ9ȨMغh<{gr'? q"@ZymiW3esf4O#~4lݏR0)tys=G&4>f۾34t9riZFp^PP`";;|$g{9(!k %l'snGN2q0r .[FCgY]l- tn}<13vRz2nqs—  BBXw*]F^ I3+H[&-j"+xKExT { aB]E%ĉl(b8;MS#k %'%? `0 \4d $!a=nnme[g%ITXN_ʎ#)SU@II.S2u|C zdEÉçQ⹀1qD2Wз/ U\3Xx:1ѝt^nfKPDV!xz [.eXCdri7Vڭ)V[0X8Jo@!v/q'K&.<]MI"£d"M_|LdYlA$FMU?ဩ=s5& T;F~j ԸURw_E1\6ĐjJ+ o"zbFޯ0 b`[$5kB-l?۔ȚT[s ˙a 鴘N1@wr+^\.#+(Ӄ1{Uʥa#xmȎ5+ƮgB;#2[KP/Fl0N~̖#K@(q,T"4)}=<,3g8ܴ(1s}| @cyE2yL[=v&JNaHe@(g`v_Vl[P1>yg}$jg嶕 w{ cm/ŝV-zOD,=@ 2νNV}lq2ňV)lM ]7hϿeMLr)Ç0rBvf>U8)|[QnȊ,a>فxJ |;УtA\J"qNCr3^b}8U->7S׿jFu_Cj0=2*:/؟!GpT]2mW= jє`;Mʫ*J. x1YVCaxepOZ,-"!uuvrU/G9rDz#C惿aQ0E7ծK^XWۑ˞EjȲ$IbN* Yj=&?YA$ERaW* "__Ξ;CDD$J®]Ssgϡ:~,I IDATxwte!zN *"݆ DQi""]) JU-@ږy I =pٙ;wsU _B|jZ/DDDDDDDDDD;.FE{@" ãl捖+Rbb1'$""""""""""<Éf\xwuzKEvNljtpLĎW<$8ܣfDav""""""""""r1 Áb͵tbBiK5fx셝\'ot85sYDDDDDDDDDDpUjZz.䕄D$$$p$_nnxyrCsNV0 N^[N\DooW,ܰQ1]Bҥ .6'OoA b1JoY9\p\i]̃HTxR`v, H {iBqzEpp1"$81aq ȋ\Hcw8qb↗[\<6ܽ)6""""͆d&8qqqY 8$Kbk֬Y{XڵjJR^NSmCjx=> nx9tӶau={ foW w0 \uUMz~b4s8)n*a|g/͖2 '?i3i~x}q^#e{kTx.,V1Q/ۨtudEDDDH ƣ1;l{pɯeia4f#&Gt4&8W6qWĻ'""""7 dNa3̘|_bj2}q柗$7`2a_Ub2agm[l"66ooϔmڴ>G_L8̗Ń`,ޙx;%]*޸D& q1i+ϖ)JmT6KpLٌIN:!ȷZEܭtmcqޔ>u֝M t9&-"""r+pZ=y,j|v(py2C_,ޮxj*޲8F7Op0 F7ҿR=L&v8Z]"CDDDDnb\la.TUNOIt"~>p~& nћz}{k[F_^Ip d8W~jΨѾ""""rssqrJ*әzɔӧ~ I]vgp\5OQ7ÙAj8bCN^$ "Ef93| ,㗲}Ob{U-*/=Go1&F賶-b6c2ѣ)SU[YbȑcL`R>;fI&Wux̮,>3)N#&2UV8g!wx Ա}+lK"""""7(؋ pm2O6RFYAV$%`00L&f3VW7|< ;1f """"rʰG""""Of3&** <=ܱ$}%aG̅ L&1ҷxK+-O1KR9aaw;<5zxa`j׶ZdMHL ⊟ k"""""kNݎp^ql6aqZ1[WE\\)R5iYEDDD&\]]pdxZddA,""""""""""٧H,""""""""""9R`v8`LgOjd """"""""""#*0H_8˚Cv5]'ɡ<,0 jg~uy"hpvjU=7I9N"""""""""RQ`vr佻(Y4E)M: &$4x#n@`(CBt䞡YLu+Z Jy0jv]d#)ys}tZʠ2õ){C>{~ m<_ȋEDDDDDDDDDnlٟkl*JHF4i|q2x`q)6NqdFHTv9gfg35 oԈ[ӭ|Kcԡ]ћuP|-|& }.%V ?=KycĵOSlw>r9~d㝩\RkRilMͪKyN tvdgոi۸Q<*u:Piu., v;g_ >_vlO~y||i3% sq業xdҧ|` .,bSٞFvax&O/lEPK gܣ5:c͛̈èh-5$$$1R]e ,w/rƼMrg|9o̻OtDDDDDDDDD䆔 ??o;'|=s;Q)jn !fK}\=m=kQ/*nMer jX9ȚۗtƆu緬\OW b(ݡӍC g85S%Sq|_AWҿ`ƥ|6^,zk<2 'KƱ*6c5xT+734EDDDDDDDDaKIldMmٽ7aT AqB@.ONf`7٬<԰59:?ÁtZW ^jh^jqK%R mJ~{v'_FD' (KЦ]-L/dEDDDDDDDDɏP3;=ghhv/F*.f_ZPQ.lwdƧ+F{;|cn9+ɬzM50 L~2r;щuC'%,g~$3&lEDDDDDDDD֓s6Lt:1ԮfabwB2i͐l>%eݧ%ޘq^e9QͰT~-ݓ/-̲~53)w 7'(z*qaoU p;ve3h:e8϶{qV|*.H+0/j"|w,wV+݃{0Y<S#O~Z`D֍Gıtmx7cHż4QOsuLVw> /r%IU" ?3D ZLD]|Bep^yi. NV.Jӕש $=}&΋!,ȃS2nq3C-"""""""""rO2qނ EھҏRx=?3;Ef|e<>ϼ%bʷX6~oїLɆO^<4b?q)iX[|֮ݼd6JExYg.so/ͣ{ywc}# ێ$XNwX< <ԧ//~O>2+ȭydU;9Z2=#Lkka#"""""""""׭ """"""""""7!EDDDDDDDDD$G"CDDDDDDDDDDrD3EDDDDDDDDD$G'O*DDDDDDDDDDdJ""""""""""mVV9 H=EDDDDDDDDD$G""""""""""}VCeQYDDDDDDDDDDr$Oz0{_s')c'<yCvz}< (3c׮e۷ /Bhh(aMбC{ԯG@@@Łظq3?Z; O^ S4kք>6Oyue~͍yꙑ,j =iSޠXb~=z/ЧwL``{,..>bX Ѽ> gΙ7/oĞ/9FG#ZMb2fe7@i"ۻvq˦[? Wr59110}l ?0tS:t> BAOWʽIIS2}li߮-}z?8#Ք]0xjՒ3gΰp"77W~I}婬),`޼7š5aKL;o;y|Ȓ=bNky[nep8L&5m˜GѸQ4EZWW?tHټe}[t:ycTҺUl'$DsT\N-..i3f\^=+v7= :IHHNV 0 R֥E^}Rm||oNAء}Y^=xݹ)]\\wO eX7=$"""""""%aM3rndr+ϏAqQ~C`I)3Csr25""?;@Mxcԫ['\\\i<7&GyV;r>Y@s@wb9..7n-[s zus/;n{}>EMWHu:d%}zb]“a {l\嗵_V CLf:;{knܯwy] ɜvonTWƼR\^`Аw}^=S!**w޽e;777t6ѬX??㮋WҸQC5K0hղ ߖGYe,lY&8RLDDDDDDDn\ja%YoɳdU_ŧr̓g2_n1!ث֬¥mӆF dk&Ѳes>3.Ʋjl>ۭ.?.;3}:uHrk. )/8p`r9-&r<(zq9=Mqy4jy{"YuL:~gF>qJqi&<5|(M4 22y?U|En#ܸRL`mp1118pwv^wa7bkSlM4aif.XV^aa칳X1*WN+ IDATtrI+>yZlݞ>K.{lҀGy'2|ElϗϏm1FWmRl& 91;oYN3e5WHuغw5k=1!rVd[AI..n2rM>!9DGG;]AjE|3ۿilݖ4sٷiִ)%qv;X-Zd;׀b?q"ͺ-[ܨy L`@ r.^fqx@\:%Z>Ȝ96m̹sf?p p.<+1Li&y;;⦅6Z]sK=oe% '7hԶMk|||X:~߱#[ow~^ k:9TpW9vxIm1Ʊr*???՞q{.w˳|O>`x{{y׍ض}{&{¶mu4뼽2hS'i6mfݺGzzxp4*̩g+gur2(o]:wF٨Ymu<#vs#q.ի[w狾C6n^Kկ{2 :t:o_6ii7˾|4o.e^jї~v!l6ӱCls%??k蛟is#2sLd[dW/@fnՒ~D Az 93iР>`۶ 2tGl6өS5leX`M )\ϟq[PhQ~lƁY"c^ma%cdz׍)wͫc_gά r͚4tRd$ۺmϏ~>zɂY*.)]&e9kI.(tg9FEE1h蓄5k@MĎȗݛ2h"޵ X""""""""땽M5mai RAZ|wr3h@?|}}AH?_60`0ڶiwQxyyqEnu9xPNu?g c?UXd\96mZ3sŏ+ٲeYi6iӺl}}}4_JN9UPJ/"!!1H[5OHHd_{sb%IZ5k̒SfDDDDDDDb:q&ו_)#xԲ'?G~^ qqq̘6gv.y%+cWdGɽ~SVs%K?a/#k&)ydv^&-[0a8WdAJO>UҥK,k_DDDDDDDӹnW,e66:tg!?deLCG>iqiB||Ƽ_, ә5Lٓيhco9-ٛ6 f5!tϦHR>Lν˚PM4NYW;)d21t@ƽJ67m&<"m˷ƍli Յ>ÈgoLʔ7h˳?b!"""""""b&s+?<-f涕CrqyЀ~yQJ,oe%Jګcbiz7׬Y#\]]q=VvϜ:u*]:ltRmݚ'>JڵsTOe^]ӭҹX̙IqеK44k$^}zkgl6[zWW:oO*~=عs׮%1vDDDDDDDD Poe>x? ,$.>>OzЃ*Frqb䅒%JWohQV-ƾ:&Gf3eW9x0le58xsg@P<ʕء=M7rpP`ФIc}M6!$rPBy&Mx`9{,%JW(W.0W˗//dg p: l6XKPpeiѸƠ $C ;&+$[ &ytb6[HHHИ {j Bȍ\ A_;c1-Xȍ)E&0H6]l\?)V{o^ع?""""""""""wspFŬ;*ʧ'ٹj9[N8q])G=DF`I.n罧2c3[D Z2G&nH~SX{:/OfңZwScKˇ^״ti Σx%,`JRsg\;~Zʹ;c*vy"øȁo26ԮD Un9/>ڗWÑG!ǿf##l_^E qј )Ue*RQGzo&?]脣snߘ*A)Tzz1xJ\OCP ^ضFr,L×h+ bÝW5Bڨ=]ނ>'ȍ-zd̚75^_gX䫼8&oȿ_WX5JƃS6pq_pgsjV,nޥ~yN.};Tto\=)؝qV('^%~'H;#Nl)ø~<=a&lw#7&^%f6R9{0[\0VٍG-./^wT7<BhDo88nJqoOܼ([^[yHoFwv"yDH#)ņ*Ҟaż}}it΁,8@W ڬw =X\5^r>V}eK/|9.*ҹլa3ί#x9̟7C;K2|$_,F Ub""""""r0Cv9R/=L3Z;ν> >_#y}[p`$5y,/eG:ɭ߲XU?.b(g1P7UWxlK">LYcs k)'!m'o~,u_9|w={gvd׎KaiĬA,^oO}Yfƴ 𐆍o=m<2;>;^w]}8w$Xg[TH`zt+w|9!Z \pzc.7Q_y4F4U9p»6)ܗ;^}СmGzLxvJ'72Qײ+V&~'ZrtdgV޻,ٍЕzU*T:`iJޓ XDDDDDD O9dY,5:cヒXk56ϫcqU8k0HR etc5a; V*k'2A[ %[wGKmT蕟P"/J1 tQtΧc}yOk*z8{Z滏F>D_ˌtX5\Sd=)_O8vM%3nAChlOL|5]UOFR VDS@X4(eS#;r25,x@w& W&[+s ?|Q_B,2>gɲg$l9WfAmxק`DߣĥTzh<%cLZwԤ^-j%z9,SCC8pp/F gfj%wR&w+,]2 Q݋ _|p<.O>=?N;y>vaa^y)mY>ھɈ_i<]b>-ʼn&> ( b .-KDi$u/8+3D-3jDj{$p|"O}'>q-g>Ytg%\{ 32kr3%=ʈW'L/B֌60j#1/ U_?C).rh&¬Ma2t8+bPx>4;7R{Wra\^z Xͼ%sO1o3|0 f&%iŭo197GTr[їgV1e=ϸs=y˒7^fh_WʭEcwYS-w^To?Fɧi26+)Fw%7Dzf&w?S/RXrU<{2?(c)Lat`6{B|zw.ݱ#cz?~y{"<)' iR3N2#+&3zlw(|'`4#! US<?I4q<2=3erh3sex*=ؑj ,y>+{C(Kպvt#}>ϷfҷcS rIX8pDjwÄ[P3:hL~uV}]F_JAg|L k2hC}{i7uK@9|iެkb&e9r.e^NfB7Ќ?X'mխԢܱ֡oqԥ*ko  Nb/g\һRuҦ7Fc=}1͜Ew#龜 BF9[J3InP ?uovwfʌ 5ߣ]=ytA_pbW_ٳ1KTtZ4%9{p!H~fs !AŞ+Kc<}8 ðPylm;_ٹk7D3IvqÞyyd kX#He758;2^& Nm}N}z0aҼ+bf>|HjuÉvV5}́㸳Nz~tGA0?'gUVV.!a4+;d),e -Wm;l -kU'V;^5#1D;9jK&>Y,[{=T1"9e<)]*5[d̶ `}/ u26,XN=/3w 0S?,?'3d'gw昹.-SWZp4ORu+{:3厡S| $od^Θk MHK}˸'yڄTϝ`O^&JT*"*U \1w<xZ(W1rnq8N \A̦I s毾I_c矉o֜)\͔i֜v;N)~ft MATlg8y/*nPW<5# dWt5|~ ?]?{cq}߻acr>KD99O3SR9" S(lN V^>v=}ۡu G0;eS\It NNqKڱzdV.> IDATEԮ%0ӯp kg4nͷlr6NVWEd:G59>YZ뿚"ܔCeKeM ٓ7ԖۄIVc_t}ĞzqƝ NVYd=bUWp|jzmY].WQ_>w}/&'9dwHJޭ^~]Ŕ?it;Ir8MH} 2 55kWk՟ 6h6WV"i#awj9o#OK0 Cok7_kPUq T:4ïaRM-ꩁ+pr(ܝqi~V/ͳSJ/hj>3<zP^VuUk?Bs>] /vwY雭Dh5m5TڤMҢi3@aG#ju0&UeUE1-׊7)i}ֻ _u_iфzkQyN o{nTFޢ}$ۖ C5(BŲ_hof,ܟ|̚K;='iZfdnP7s4emqf$]Bx,ުְ|NFj_v.窰/%FRfm;I?K&eş)oثj޵(aU(zBa rַΥ12xo\Dîrޯq^_όԡG0@X#VU1fZ1_|M1/gkze_K5]kj[#;*Hesﶧ ݮ WN'- @ގ#:x^fNܡÖ 9Zܕ\s֗i;3FcV}VP*`{{N*RSQ\O7}pzƦ=c꣋u5dp}eu2lT,Yoݢ߯-[5LE ~>|$4$=>'WpE=rRa*~Vۿվ{4l~a-_7C3m v)6dIh&N9lwYiFϚKlM^R9U""cziV5xS P*mU# u֬ij4<UEfgƖ 5K'1Tov}]Z+#Imh-q\g.۔9[i:VVI9Zh' ;V;/_QD4P޲8Si}tXlY gT/6۳+(KZyzNk2 uJV'YP̡u5+u;(=H+vЇc(Mn__>.1VjF(C'jǏj07s_uy揚C/k>$fO*?JQHAZVkE߬\/p̹L2 VoWJC*XspK?}zV@Q]D,ӧ19T4 e|}y- 3~) O+1餞0GuV &<K݆9zzB:Ր%Jԫk *նG裳%կC+k%VŚortyZ<:|߂'ǟ5eb.5(C߿ք[Ug{#+ɇj*ݶy e^kej`vi r~NPg䔎WJ,uqJ+4,h‰Ȧ-W^rA5Il]պB._>h2g9.[~{jrMY Uޛ?o~Ŕ/EnD||d3qoZnt5`6*)P;Vdѷ9uhA>iBkx)6'v~T{taW)Z\WK_u6I5XջOU{aW(F.{^#o?Z}+7Nt*z[/ׅ֩jxWKӟ._e*+jAyWutd?ͨ}%L,?W[2c_ TŧLgt.*TeZNDC4e˚xM9KOW^4p9а뜑ITɊj,.vޚ.zRcO}Y䘟Ռzi]x(Kz}R?~ȹMwgwt,QE,_ȧ`-EC_w^sVVU9$*z?nƬ:i%]Vj<.U4vjYʬ/D+mkzySY6rfLxKN_o.m:HKx%[}ws6Ob/'*o덊>'GhܡzeJ]_gC,Smxч{4!uhG~~rͥj|ǂɒ3it=˒FEU<ʎAƹy wW7*WXՠ.X,q8VWtpz=ēzqங JBj5Z|MDUk]ρ_v%희^q8N6HC6$kb6l\5gjXcVGUy=Is3wSQq犜5\xhۘ*]N 6{s~//u05o;]GOs;ƪ~ɺ'k%Z&IJn#w)Gx"})X{&#v{;jT:E[k˹l<}jƷ-GNaVر*u*URF{J}^YndK*]JRM5||FS߲kr /~J'b,ʛ?Hƌњ&jO|#Zщj6ܟ(re])z<|j7Q. tQrPETj߶ZWNKW~f!p?,SzZ~LrJٯս8eE;چ!l=`/8_%|[PlWQL4ZvYը[qP*9t2/JQnMYJ*?9mQBn2=}xY9N;I*ӁzKCU>`x}q-^$ MT^:W]Yl(њzz n:OZ-.%=s5.tyGZt~ҁpY.xY*$9t1/1U`޲j1t F,I):+N-ӭrܚPHey4ֹ+mj-DƌgU&%ܲuRM^dx"-r UDD>Xƒ.8srх7kf27]qq5iFacҼ5Nkóȱwv$^fQǤiN)-\ob*bJd%S-aN_USϨMKUb^\{e[^0v8}/ڸdnJPދ4$CeX<~e%KcKY>wyKg)*k4M%5XvwH҉=j.'_i:TSwP/'qUJ^ݻ'waqQج!j9yv/+MOOo'I_kĐoBմCx*G| 4"2Q(wȧڈ٦l*ٽn֢oкmSO{&'U}BȨ51UށrY5]-kOCYCTBSM\Z>tu`Ce2DT"pyzH⮬`N3A—NOF 7_x_ЗkYP)U{>n_G<3h'}C.5HOjG&=q>m%碽̮}QXY5uY%ڮI?JML5KEes8}jҰ_sE~OVsaJ8ןuyނëF>GNLQR@B:yͿ\WPaS1 i$j:pA{W-pN4~E20Kd!;iclKJRڣyϨMR9C|'n*\4TZGGBT$4jwtNn[K^n䬢&#%4djMyICn:/|XnbJ#^g%wwY$Y[0b[-K!VI{zH8x$#Nf-ՙ I__u{ւ_Nb<",`$r5ת>:ZY'-\@3"5vz9u.M)?iर6]Uzi5Q`_gB^ԋe_Xl=67>GМ */RkzI2t. ՀY7U;5YRjk񐧇6A$EBCzI;SwjX${fE:Tӥ|-dpVvɈLV]TQ/r7F9}Go*`o8}z꼲fH=jeqV@pj0[7No&uY[哣 ~NUށr|ZԘ,|xm*f(1!QKy\3+T2pW愸x9$9oTMb.ªa>BYR)!Qrt>5`ZԃC{Ξ돥s6zZΟ}GX,o4lH2&^H2<.Z  "/O%/w` 3 00K3Q0g~~a.ch7u_ׯQlLLz1(*d| "<<,K{B )S0L!`bKpž()6&&K LO싊nWD] ̑)*j=d&`Tz.cIm\Թ`h =( 0L!`f韵~fNyTWLP7{5ܓ.ɀ9Q<'Sn7귚]NRv TW.)@yYNgPsZl]aQUՇy$2B7 N5 =FV}VOQofe R{ݴ s]Ae~`^[N9+(Ɛ?׏֋ ) UR!_X (ik G:u*SX+ j6bNZmݥ]U`yRz=ॴ/p{][Us_ h5C>NjTUQVD1߽:?q- ST&rb6ҐU[tU#l6gכM"lʪ؃Z]m2>!w H'BӺL-9J;w')O qѪU'ܛ`$Iɱ1:pԞ=:W <}:tR?C?mat+{ im\ٗgsv+aH5`5Ȝab|3Ww7ٌ8%g0pּo{ :ICnFEbῷx .RXews/*Jv]Jw)3G~; 9Sһ !?'qS҃9-d6$`B 0`# u8Q]15C^V4rO9&D}kN9Pª~jv\6w;I5P^yں4Rey(7j~f*,O efx\ҝ- x4,\uw%h:~;\C_o![5ú0"Èժ! ͬL^AQz8_Z~9>,ku)4G=|埿Rzr!d_Jj1kK{%~M55CCZe +{<|RAFlT+4˳*,/\*RVjX7;mU-gzovK[\=9e+;唶!m)pjZ]#֏R7䣜.w漞3W/ԥ6[y6!Kw09\vRm_S¡32wJ]ұuwpKuʱKZbUZWz2SvMM]{ qeHSͦ)E7)Oz-2\UU"jKږ ZJ]grK9 )9Id RɧkjQ٣f跢UIR*Ê=FWW'?d.*OUSIʡTJCVmVuۜ]o~7YI*cjueZʔs4{:P7 `Sh.zo''Gi$i]U!N6ZJU{'n]B|r*H&%YCƞ=:W 9"'s?j M4$~]lQ"W.{tsb*2p̥㕳qCI`Kޯ?B;eX!7+KEj6${YnlP z]wT ?Ye;I%"ksI{|~:~d Rh` {dSW4i:7ZW+>v,ɫZKN/޵uFVyNRos鿭ЋZ,RhȎsI.~yTiRD7ɝ;u4RYSSmm+_$!%  5s"22RMexL-]@ 6,E._0L!`Bw cf3J O?؞ex ܱ]ε=Z`BXXk5Ir<&ﯰBLOAw pOh0` 3f)S0L!`B 0` 3f)S0L!`B 0` 3f)S0L!`B 0` 3f)S0L!`B 0` 3f)S0L!`B 0` 3f)S0L!`B 0` 3f)S0L!`B 0` 3f)S0L!`B 0` 3f)S0L!`B 0` 3f)S0L!`B 0` 3f)S0L!`B 0` 3f)S{wh@ DQЖ*.H% KXfY HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` HfD` לgpk1^{=\k8'$3 @"0$3 @"0$3 @"0$3 @"0$3 @"0$3 @"0$3 @"0$? mW)+IENDB`qdmr-0.12.3/doc/fig/qdmr-rx-groups.png000066400000000000000000001100001501654372000174620ustar00rootroot00000000000000PNG  IHDRvDsBIT|dtEXtSoftwaregnome-screenshot> IDATxwxn*!B'{U ֫Dk ]DAQ "]+EA*lG )<;y̙rK<R~~[mb pFGqO:t] Ա}JhdsaZawp18lϦ;n=ezv^LFÉ*֊Ubft99>d?mlm X,+ vL,vU4MN'V M4xbvGQODDDDDDDDDDrN Պj-e+SeEl6Te`'&&jA1oOݮx.Ӆf󱙦˥""""rs9㎵#"""",IhXkQBy̙3$SYDDDDr.n'$$zn⦛b.N'.W ałjb8Klႉ΀UP逤xX,V+y%_\.64UYDDDDnXflaAxXYθ_avCS0DDDD$'\.!!jՊ+i0 jש;vt3 $^p8cנsQ83g3  ,Etd6MS f(l&'dɉаHj׮Ere+|t8PZAA;vR| <=5K gTh/Gg/Qo) +v&<:x į yzQ#ATx&_4DG ą͋eZq"imQnќ `%yN/ Rok=~'3*Q^f0iC*y?-OU]6DDDDn1T\݉+p)l6ka+Wt:wkbEJOoW, |75Rq7M)D\GL#tsxs%pIe*z|S<\D([ #24ڂIDŜJѵE=AŇ216xb+Y qy;좤Q"""""nSTIei/U'%OYL8,5,3& \Δ}.m35*aL:r[6Ǖq)Nn,?mOGVYDDDDx"Z1GE܈`~9bnĥȅY2V3rPם{V_cPŭvҮwOCt3ƱcVXbX2.""""#ݎiZS>ر+=Zla$HMȲF{s9옦=Mٌ a`8w؆a,^zȟ\/LLÒ8 inZ0.&Ն0}q柗$ƷX,`8IJ CEDDDnALL,>>)%-t>IZŒ,dosf9(T&0d(/j`<]$ z b3Mdzȟ\?,g8uiU?~ҵ'帣 u7GoycÅtK8o""""777x7_l@fcI|+""""77wgϞ|\ϷF:Ν;祄p&ݗi,әea&wӕA qP:6~prsPp/\ |"8oKyr\Rrqqp4\:5z:b =}$f4EEDDDnVÀ'NQb,KYjXFgnjn:XaYƋJ%WPh㋋Vbn4M;_ FDkwbbz8 l}i="!6xiX,la1DgZ'F&qDۇ^ф81ܽ/Q!""""2b(ᗦvrF, a9}bŰX~r,xQ<^2ޱ)IZY{a^K fV|̄XSg&? 7ML,ON>._Q8)X=): =9}4q.]*dEr5Ν'$$bż0 "' pwhܳg&FU"CDDDD[fB(IPnuժ~Epۄo7ݙNsyvUF0xv.2>MJ-iӾ3}`XDeɫR3`7M* hu|}$gS)Uv/ō_4+ug' 랢f~?~ȤG{S?*էo#7b-qT݆[ZľL b4>jDʔ CnEDDDDDDDD*b&NGĕ&^G~ ~%U`פGx˳Hœci}(1>4(>e増Zvkx$y ?~Jx@'ղ?s`;Q}lp/3p[Ȼwn2~R~'#"ØQ G*[,ˏ&Hc؝ _^@IDDDDDDDDDI9/C+3$gK)lovB _3Y\?J@ "7e g wձÜvB "#Vl@ ?[V|XƷE'^/OխqkNRG/-'G?{᝙t/̳,(M ͫ Ïwx<˒@c8V3a˔nv8GVws g-毾OQ/}]DDDDDDDDDn@܎}?$`6laEw '0*eig8tq?8k?̞JYobۨ΅Ѣ3wvak81|cw ̺ѾeB8_`j_ʦ:ʔwro.md:I0<8fBlR.`}QP/oC̆6wNw41M+y%#oJ{`OTr9ftRCf֒VLVTWpBoPxIFS}MLOF.5|hج 8WJcLLlu蝁TI]Ȉj0I< VDDDDDDDDDn<9O0aբa]+8Ii|eF} Vd`^=ʺV?0U>p_.YhxgSk7Ǩz*qQo[?6O Uʠ3v?u|\D`QAoϛnyޟ7|"G1dS>] /nz +촛0 :"""""""""r*,Q~MDŽ<;w;v JtG@n0?3;Mv#nq\ 䉷<ϛͳG3{yp<~)ͰR{ô{+]K;xk\*{3 5 |RN/G9vL:%zŲxp ^&n-G2Kȍprs˼Ucn\ N\`\Q `\9{ """""""""" #!.Z%2DDDDDDDDDD$l. """""""""" `\*dHLEDDDDDDDDD$T"CDDDDDDDDDDrE fɕ|U IY8y*#_l's[sgKrWΛ(3!!!{/k֭c˖m~Ý|Y3=""""""""Z ysۘ&>BH`^Ia֡k^3~v;wODFEy?..}~g߾Y"z =x?m۴=rؽg/>6ls\4ۜ:uSNqTPn]0d4npp5 a/[R~>JRE`[80u\ǜ{~s >~g᧋xc"""""""?DL:$_BF˾Y'%_K._.:&/˒/[l >ӧϤK.r8u4,ZXl'$bciZ=VȹsS~>{*6{eux;|4% p8Rf3'˷X"""""""":pxx8'O v7E~}Y-ktIjխOldXv}At:^;㖛Ъe a;6lcͰyɜi3kΛ9N>g=zqO? Caeg@bBt35g. t֕T)pf)i ԩ#ϟϖv<OHW_*Zllo̞C``uw!˸4\|6nȬ9sON?Yϛ7cK-,,Hqq̜5-ѳG|n%wK}pYvu!)*`nצ ϶;q>do{/urUl߱8f͞GZ4m8Wrzp)QdD[FSd#_0p>~!):pb~K.0bXAdFz߹sf]q~ gw}뛒D|&s~سw˿Y Fu^yŔ4kڄF︷]#Gm ?s~)mZϻIWJaiղ۵M0Ա͛5ͧVe,R%LI 8WRLDDDDDDDn&'p,YJS]3/ʈa?]GTvBsV-ر=|~Z6 G~4{޽x T `Ν={u6pOb쌕}ٱsWݕ(QPfM,Gݝ6شy3ҩC{j׮az욉 k0k&iL=mw/b0\F~}No3wRf'g羾œj2'aC%`m)n͚F[nOe܆<8uL<>)lrI}YJ2ٳlݶ͚8vXXɻ+Oٲeҭ+QoϛC)U$aʎx|]zxa@2:Vvv-_oqavZ<9jh5KH2.9S d'7gqm#ud"}sV2@mh<*-5Ci3}<#lڶ)6[3zHZn@hh(-8OEDDDDDDD~mJYrNB0%^^^DFFt:31k8qclժ%S&%hؠ>gΞe_iӺ5S&O3sWdzz͚ .^\JL^әZ_~{5kRT3KQf`.V} IDATgŗ_{'Яd])o̘JZAڴ s[DDDDDDDZa f=. `)Q&rξ:NvyyyѹSGn+sې|7lj˼o׭S;wrعs';q;v,׫W7ci&1P guTX09~ g._,LJgdkfcs}tlX;Ib >;OY.+e=O 62.comGB|a|\ܩc!/)9mCxx8onRKNZ:$mZbٟ+V~G6mZَp8U)?w!m-_SOY}NyE^~jP)'F O9k\.SLn!@INr:VRKg%3;m۸x18lC.p(r%aP?mZظ8~ttU32s5MDDDDDDD-`Kd$'rYz0ne~[\.)^8koݻs_c㦟(Uݺvqjf~ՏyuDN:f}bY iMcgÇɲrA\ԒeY3>t>>>ieVvڕɞs.~ٲ5:F {}[ncÆGz{yq5*̩g+gwr%3QXunɳQ[ yFsόyԤq#v+/#GڄL87ջ?9zժV;mѽ~ޜ@||߬3gGPRRYAە==Ky{flq3} y *S4eʔ}tԑz AzCGysh޼JΝ:bTd; |@n]!,t'NbЫWZhe`K )Z.QJ,C;9|~̳ϿK/>G&MIowIleK={{s݆[Q|te2عg'>Vrb n22BNWP.cXXF>ImRw~ٲ5Ù!!!Ҷ̪/Y܇ҥ X""""""""WMڶi ii SaJd sz~7ۥ°뛯U͚xl0N'~ÞkNrʹlތbŊ͎XaGMra&%&Mxl&k KN,Zy\joIHhhbZ,tD֏ {|pJNUXc%g/!>>!P+g=wߕ6fU NL^7S,:ejn\[y4*r>8U?q2W>s/?rJ%r gΞVD^_-[Τp:i3 #ydv^aйc&O@uݻ0bh [_I; 7n/ݱt;IQ0ش~ 6So܌Ot|)Zkƌ>>4m҄ %u\immX~<9jd^~ "/t!SmZbSdחO?ӧкUK ݝCбC|o_i)_-p~(%')~ RX5q/"""""""W#<\Kd0If܆p,u?IVmH츧Yi=wZur@;^~/~Jb!™KHl7̙vM7ɑ#w[_3Mh ?3u3O(O֭Rֿ+}f#ekle6CB޵K|a¤)̚3ݞg&n{z c7N3 !o_CDDDDDDDvMrF~xG.k)S2V\K.+[^}0~Z.Moqpb$',F~(W,ǿJY;v6hW_UK>Y3gp8""""""""y/%2DDDDDDDDDDcWEDDDDDDDDD$7,E6),""""""""""j0HضmQmk)""""""""""cn\?%EDDDDDDDDD$W`\QYDDDDDDDDDDrE f%EDDDDDDDDD$W`\QYDDDDDDDDDDrV ^9vH/uS{+KՋ)""""""""7#!r9r[uSn@%EDDDDDDD zy)bb*H!*UDc $ҭgQ7熳~jJXGO C>kS8vja``E$) &&'Cqwwȣ}|1݀B|1~~D AaH'O$ $""""""9`NepE)D*U9j}zرn +rU|0$񱣇)""""""C( raX'} 3i,/'!UzDDDDDDc5X?ˢO VDDDDDd+\4YDDDDDDDDsJ0eEDDDDDDDf)(궈ȵ~^]qEݒECMw>)g?~#3~?6slw 5'lXN^Ϻw-3~E*o;Vn^~єNb輅g OxS{+[*˞?z'l<~u'nì[WؖŕOд~?/{\r`69<ѿ+jנR À1|*On8'׮իVrm7\Vyk=<ZoxZ` Uxq]=Ȍw3yw֮AuC t |0r|wʅfv򟙃 牯7%6a՗r*Ixog%eͭ5G2s,y*cC|XF.b^կ\*|zV(W1b^-, &Q~KI-{qӟq߾1q~ǽdej֬AbW+UdyAܔʞ\8y~ 'jypI<<kP<"GwZܷ@t[f׽ rJ5*z'&6GHxE eT{p[oY>(cA %W@&ƴw]}VWm`^Z+3{T]!ũFӥMTR;rK9ƛ3A٣ =n'0GfᕇoE Ԏ;.T 8#r;^|}Lu>%m^|ы8`+y(w׸,fV>\Rҙ gϪoWG~z>/ߓǖ:u|t{^y#v_Z|O/\.bG1k 5qh>}9~d<:W>̉esF?իIU#ԔժP ' 4~zoFjTZ]fIzab^OhI5YvG/usַ נf[2c'S:%{3b<{?]ץj`c*Oz7CK_ Z::rb-l g>ړ/ }0~9؈jjRY/F/;Jl쳯뺰ُL Q=DzlrgtO.~9d_S1eXc|2$bk;{Z񿩏T5|ԿiÞ'_͜ʘ!Zg<؊YпlҌ[ alXj N{7GꞄJ%+\$dBivӞf7q%Dr|;c&ǘ[kcSY[xWX?뼅-QȪ&`Y#}=i}Dqߗ1V? k\lܗs60x^4mN~uܧm@^J >y/]:u}4U.Yn do,'P{{ jT*o`Xy__I|:,Hy9c+G+Im(8ϮOQ^pi{,1 |xEoFM7zT<㶱[ѕ1uw_\I(adcŞ@[~A*F%yFT>f~@'eܫ4{Ƕųs,u,uxO-E|HO0awm[<}Fyo\dǨi:>a\cIc3{^OcP=МŏJ7ӗm;Ѫ/iZHgn8gfsѽջtzT*^o`\S<.?{\߹~˅ X7KTs ޳CFdg)Z9/a\H<Ҽ!GOҠz *WAM&3d@jT6BEf]i֠A / q$dcңۘ!m`y;g=L3gW&!gs_ݤvWIb9˙~?+VVHpb8vLPЬԸcKobt4~!#r&y Yg0봧]K[m8~s8MaD`5O! Yᄥ1Z\wpk4oۗTSo얪th['l`fH`IGAe;Jұ33X 2z?aX /rW cקЕs s*[c9cUk^wt%TG|N2r1$ӇZ L"NcԼ3u&\GUڶZ4W ۶'ȹ=fxb͵5msFD7/ʭ#_8rQWYs͸b9ve˹;`Rwzv³~_'QuyqEg,a.1M OkV.~z6|jqaŅ3 <5a_r5|G ūeS3X+FW1HL8 fK^v-? 2NFe>;670] 78\V[_.T7~k<й+-S$3)#dN&ݬt%ncdܣWfR03Nߧ9ώ*Ra5ES|~>|U' _ѝg1g ?` Amg㭞?ρ׵ IDAT}Ѧɒ}ODTʒ}g,c2c,4&`B;YaרT:s[|<9stz~4dY7_6޺?/ft*ҵJYYuŷa!)o :fӵ[7veݬrۣNv˵bIK|O?NaUЮLK*z07=.rThl&ޞ nzuұ ɿ'SfYS\nM&O\cZVq)&>z5niNd4诟eG֛d?\$7mΑֺͧCw=%gq`n9bMR{^٦cr_c=_ܪwϹ.w+=?d,H%,XH'^7>_7{nZ6J6*cI$)["!}I;#_՛Iw߫ s|| xHVjZ"o5I󦅌vŦ̜+Ng6ȺۦY|rZdiA~Ñ1zh޲"SO&,SvZQʚw*m+ͭŤ ;{x<ֹ7eslS{Sd$)擧qj6I٧3}Mxԇ/gll8uzaޭSlX)7|N_lw9RuVYsӹ[5Iad|[EtZQ}qWϻwZk׹-OX~T7Yet~N;dOn[tv̡kޛOcY)$i9{9`swE/"},k\qiwǙV [/Uŕ-n7;!>N.wf~̜ٸ{: U ɻ^OҴ2Ywj骅úg-w"C($Ǿ;/^ʛvMjܽ紖lYuUxA67)ķ'Gn:3Рiխ?-ϭC~bԥ9MW'_4rkއv ['R^kxY|kC۲FkߚSO/-WlķʌNٯWǔhnE48Ͻ8.VI0*G_(+oFo()eDu2h.b\rH82{ntQ[;=\IQy≉XHMom7-%7[=[go=7ҴY~3y^ϠtXJ P¿fu/7i=?VVaYfݭaI iV?r\lVo#9*}+ |/<;{=wn}lٟWG9\|N T$UU\caayTz<p ?I_TV /0/ kwn|5,.)򷿍رc@~1*O߾ {-/`lqLaþ I̴~>Pr_B?Q3%( @IfJ"0PD`$3%(eÇ8~u붰Pstѳ&w-!O,@IfJ"0P ̟N)++Kzr5^uO}YYN5sj t|7:oG\??in#^eq5ҦVMsY/xj}IvnmK8,'*~{:XB sl{;r\p33zƙ5sZ?~fJ"0P4'3pr3jpIOVjTsR\c?\?%[sg(Wo;n]"Ф]n߸[z~L.:<䯷9Wk [Ϧ^>p4JT7\ c?^2/^#'f}wŏʵnV[<~ь)gtE:rBiv7;UWfzY?,Qc#IR،N}Uy֡WӞf aܫY>Мvҟr?^˘)iR̘&Ӿ2Wʗ fc3:/;guȫXj40NWɒeI)]60u_i%/y\U̝e1K*QKSQ3On|?|Wʳ;>{fO(({7ޗi\IUUk@SCA`6`JL2S7u FF+{_}9l#*Gߒ[Fv6J9mq~'7ʲɧe%)fɕZ]j5d'٭77)\ZiQy&}ZvY.|n6C:1u^9~r\ti]+y)'{ka7#O~c^Y2/ %n'_wǝT戅ʒU32͎Yiy>yTsK9a9qrȁۦǤoOwXj(0YeK&S,MۤjțVHyj\HyN;\xZ&m;ef ̩e>O^8vv{yތg3vnysjoSnS&LM[rn1CKNʹk?Bųj{9γX اoߤ8 ><{êY3<&ޝ1g]ka Iw[n {PÆÒ?Y_䩡nSgqe;!/?KޞG |檼qox·^O-oV-E/0W>]n|" sڸ:>xi.򯹈Ԥ L~i۴njnˬ-&/L#fl[٭^!|)~Q3`M[[1~.m*2Qbi>Og"r[]aԥkj?hNOs ;OUeȹۦi!IYˬ\+y&^FsG&m|Wvj-򛿼'ULϣ ];Mf]cړrYijS$yY!eU+dM7M[I߽){|]}{BZ-}N.:q,-Q# |i_wRF8(/}:Ő RcrrQ挧~O wy@)W%{5N1eL, 0 沶_1v'wzb n~i7su,fmY!ǫ6;0Oy9eL~ۤKEMÇWiw=wLÚx|O -"Q+o>0+1>]7f$KfMҢ>LofO~$5w֗moW73Ə0{5Jq%f#rgM~c˘^ǏH=GGo99]ԟ>48N^';]TE*2S#Kd|_yl-ʇWɿ'vqGrtB^j?|~Yʝ~ j$0W0 պl22j YፕR[K7a./Ͽ_3/9+}'Ffi-KGSS<ˬ5rFN(u7,7~XN9{l)[,-Z-jB $:]7szÇ{53⇹vۊK{fqx޻ҭ[=yW(aj&%2;9'+\,+apy^m*n;/6Ӈ_eR|TNy"g֌N4~9fZո|8*nv٩}>>(w~i_jqxH5+G?6ސ?7l+;tJ.>s,9ߺ!=W.xd>]۲8)c>< _>u?<,Oc睓ZveѸ$y~8}D {7ճz}IYnY%\T;)#n>u/f٫3÷8fti/~r_?E6کz|ƎO?'o~.?sh\s SSҵ7T{\:O%o|]yղkVMx_ em%;8]%=aƧr֍&?AgV(/K]Nϲ{r58)f=3jD IRq~4kjv?iߴx`@.5#ۅTCs+dŌh+_.-\8&wz8U;KŪ2}fo穫̊cńkOIR;սV)$)4_*u)5- _>V=1L(qR#r&ow1C3OCjiX2a'3u!M^_nx&3T9471WKUY9سymu1CǧXV_}&~xCz/VHPHP/\Fx}}5ܙ!#ӭ_4ٗLP6[yn>we9wMviNnTouS7fqqRg>V>?R'{N6H4zӽrxϳVfx@yp52yzyld}&✷iޣg *9g6p {h0=~tmac }ذs [>e3q`ѷdVo[؃`Ut:5O;ua`E`$3%<%KM.;~\Y93(B ̟28v~ܧ/V09IRѣ9k.iXԮT|>c,$euٲdM{k횼?5DF5O>o7_giy3M[n֭*ӧ'e]-~dc/˃MI1I -òǦjK<쌗싟g{Mǭ&Y*Ǽg7ڊ.e 5:h鬱κYj%2OWS]Tzdo5odVk[?vM. {4vnzurdsܓ;S,7cW֋*]<˖U+)VzoW/!y}TlBV\qo:MBp6Yg熧FSrY-VKc.#ߖ}= {fT5V,ⷎa@ͫٛQyVl{L9_4O=kG\!-57K٤RѼy+|gn"ivVlY;S_"\HR([23zv)}6?ϘWKO6 ,j\HyN;\xZ&m;efd5ߛ>bVYqr}6!'I|ŹUSBnua&'q}ڱ&J[MK6 Un|SO߾I8; ><{{޻ҭ[=yW(a\D`$3%檼q˱=^<[/&~+V'oͰodjqj(03sԶkmӺUAZ,F<0=)o:tHVSUA[[1~.m*2Qbi^GS̕cdsom:%C,K2/]^i;|>2stl< J톭ƎʋZ6I֫gsG]TpYvx,ݢaԪKt]28v~ܧ$S Rvݙǘ<"Gul=IQ5˗u'ečҧsQfWteùrșϧgw䒝wϩ#%I?x&}z6;sHyx6ݿ[Ͽ4cf-vȀ;wGݗIiO*m`;/yk?ren(z̐l~i>Y"<1wTmiZf-Ne˒[fNwc*^wl^?I=HwW7=6ro޼,?5V椆nW+o>0+1>]7fiӶM >+^TLœ L-Qoͭi)y{NˋϽ]mMݺuSntS>?˙:1*\ukv~NݸQ)Ff0YYor-{e]c6_a>|phez0`Nj60+O+o{!oy umPRA?3}^_7|߀l8Ia?7G]*_K\\xplE{<Τ ǝl˘hSrN ><{%oN~eD\M=p]֭Ƽ+ra53_M[]q(D`$3%( @IfJ"0PD`$3%( @IfJ"0PT,0{/'fرlfMcǎ%/0,^=:h<>rV^y#%2A'LJy͵2a'% fEиqR,عJa3"89 `<$l3YŹ| 'd) QGv+J j98餓sK1'pBrG bq3)?~N:9UU9cfE՜is=)*' |FϞ1N=TUV夓N Y'ߟ|k[>uUfEܬco3" ;0J:~"0,3nY*`UX" 0Mjߚn6fE_A`).\`%0Pk0PyoGRdg:2'pQQ0,xIrԑGESO>G~xN>Ĺ_C3@Ք9I;T՜z)"zHN=&`Xa zAV*yws4}me33:\,3@3TWWZJZ\ZV_ԩSƾrjE-j%j+UIeرT`6ӧO^|S`dM`>c JQ0PR`e|3qČ{ݯ~ѹKt1+X45N_N}}}بE9o'/<6{:\,斃T/ [RqRD`3nܸ9>' Sk0O!\ u9s.bjCr |<sQX{Z?g=6er0*:6ww:4/=<,!X&&;ι<:+ 9X 5ϘGu~ZѴ%O=~r,`|T\k\/Rmhw[.-&ICxsɦȸͺ=^ܼ~⵼y練 _u%ӫ&zig-3Jr0|̘3[,6<;vYk_֘/T0C'ε[/˴j>+'<ο(7??SYϺrߓrN20gDO-6sk9ƺ9{sܧ!W?%>ʸ?6]>2;u.{L5~/VuGܕʎ2rn~ܵٞ^Cgn-]&/%FyvZI > K.N 3|Mg6YY}~eݏεwu>|<7~#)RTg/s݆#ν el+_>sm=9[3߹yr>C2hжes7uZHWoA۬v:ks{)ٛԝr];c>Gw\<_6{qN=$EOvվ=۷On˧fKMm6&lgk3]7 8\py訥3}rNߕclwN}A7j3SM3;uͲ)Ljggh6Yx6Y>ϋ<zdDvjztKו6=q| _qV.joCʶɇr=2x~}3}WH5EϞ{nV葎;c5rcܲr-F IN;Ώ^>^0 {'(RSz..ڧmY߮977ffʲDo'o ){73+9_ȝo~>k^s/ ŔsҾZ䯟|ZQL_KvI^_F6Y{TbIDATc|BbMt{lz΍٣O|>ezuLj![ Ciןe>{!yFy䱓"Sg4b?׏ɢ{];/;7o>6=1:?Z<~9h>f3;[˙}/m>.gYvdԖolzr;'>svjW)۟x#׫,+Jk0 ޒxWьi,>Pue?7jKdϵ2u%:ovʃ y¹eyڅ˭7InA:O\'cm{\z4 ?̍OemQ$%2d-sƾ7e_˾K&46X=믵H"uhh>(gJ펙X5mW:Gzv^ >{E.w)m}VjER8&I،fܺ5y?> Ͻ)9ob[/%yv.SVW2y@hf'7e&?jr;[~r=^oeΣ=]2[CfaC̗f6=NO_^V+7ڡVƿq|螋oS{SgERVIxo|Jh9|k,9h>fDF֧Mj-/Ԋ}@~rnYq[ig|Z1 -*y Z5> :,d-E"=v*wgҡY:s6rrYH^|i\iE-8>(_66-+WZTRɤLHsL=JJjEY_&ٙ%2J_gΨ;Jݑ+w(\Kw͡ߖVY5K4ΣOL8 "3L樦\$6Kd>}dON~c1Wn.$oc^4CrFX]ֻx<é9{e2cnǠ!YcηZL`q6nt 꽒OwR]'I>g^(}V_!-jtNWg24ZMhl<FES8&/Mcu?WnGdu{ġ'譳g/+ uHYsa+壝VI긼N-YNon:3WTf77oQ{ZJF,9h.&bgΛ GߔQO5iIdVSv:8>8ktmJ&,kvX*)yW7A֗]}6voNLIWJ9;׌)Kt>?ce!;GfۺYronחӲwyT|Nv/gߊ?|2jgYHo Î˒G+RKi*qYy+s}ٻfՖ^Qi9x֬ , gJd /] ^rsFhoM~xi{s۟Ҧ.=&.4sgȴM[#r=rIf'fpe\wt6clx^9sCAtޣߑ+w>UYrN(4r]yP.u9s3e9]x=^sXnso.]Z-쑖Wɢ 經FtI}DX `Ҕ1KJ[<.{ms]Mn=Ÿ(;9-Cr՜u{'մ;?ePvZm*Y<uT<sF򪭲wgtq iXszoEzV+v "{nS:kl|V5w{z~>Ht[anm άYsXsd>M.KʦΕFczg-#p9}ap:t]:k_5]bOko|A#ʈM '@s1Q;n-<_:ɽޛ-*ZX@|pgv^T.Cnۡhƪjnf \z⨼rFiMl-R{G5} `ДqL-Cyɷ`-%KdUjzhξ $ mo I2o?h`+UY*>LMl`KVKR jZ#iJRsN2ӫʩT+IfX2tCX4-IO>yŗ1?~| @sRWWNuuӻlX4%I5WYOC`%to?)(E @) fJQ0PR`3J 7-ѣ`>SM0iٮ]o?JQ0PR`3((E @) fJQ0PR`3((E @) fJQ0PR`3((E @) fJQ0PR` r IENDB`qdmr-0.12.3/doc/fig/qdmr-scan-lists.png000066400000000000000000000743451501654372000176210ustar00rootroot00000000000000PNG  IHDRvDsBIT|dtEXtSoftwaregnome-screenshot> IDATxu|U׍% `Gm% DZ$$lQP@@JPRkݻq~,~>q9s;^>|瘸ȭzpР =]bADDDDDDDDDDny#6"2jc>$G6WŊbN}KDDDDDDDDDDny΅Ј]6n#>> pڥ֒%3-Ngag+""""""""""ٌՂܹ?Z4,(#f$av""""""""""r1 Áb͵LRbYKfy{)g ;?9N, ۫HMé""""""""""#No"Vi粈H~IL@bbGrj1FOw\]]nxNÉjө""""rqډO劥 <" مjU)S4qi<ሡX1,s+bpWr4(] O7.%t8?:Iyxܓt] ;8ۋPB"ٻQYDDDDNFXX5k֢VÁә=P3R8v>tDRxmDm#T{aN"NlRԫ'ٽj1|bZm $VlD5M$as-DƷv""""7Dʗ/nrkpIVe#˗/p\63)1mqgْ`Od ߞ:5q5 : iRE (w Խ.8&Ջm~=hZ?eqq&aa;jSưT=FNNk;>.tz~"zQn%l%݌dgB">]zqob[{ ^[iGr:a9N\\\ZN'..8δmwqrV98.Ϗ$ `\,XWXyes^7+-S3)b`?!^*EDDDfl00a%'FXL>.E3Eymqkҿ<頵 7\=nYxoeE]$[Qj^f`(""""7<6 ð}ܶmehڴq&SJu˶H}/xN ðe(08"#1adɌd$^zȟ  9yÆ0cCo )sA7Fl6Ʉݞ2d”k+IX^\|<`{wݒa򴈈ܘfqqxyyh߾eLL\l<"ov g3w.N)W?>'y1 .f6oeQHGb .$:2Kȍlt4SVݚ&8ͩ]-gƺYys99*lg囒WRq=j%,, BoXXӳX 4,՚92xʼn YH}/x]܀t gLFԈ `LF,k?u=dbgU-2DDDD&cqV~딦o,2 w_o\Hyɂ[fЉ: |'D@v`I|' Ym+""""W+gΜt89LO8{ ۽{Oe㭍0(δ/6#,U7ذR$ """"r1߇(7|ni[%c'qw .mgg7>_r,f3&?~e\E,;)sdV5kvXcve'%wp Ke.f_8&os``µPje~enXqH\00X]b6DǑ`+F QDӋf90z킩PO &&RoY1̈́GDr)l) DxxƧhamDxd/9S3EDDD&``$=#&\ub{W1oR69r7o$Ƕ{>jL&tԩS'_\b )V,KWԝr™_3i[:CV&Nj"""""""""r˰z &m=#r7GUjP^É@"kO&cf7qVS;HZ4UcNg&>֍A)]6?ޜ?J֜;^Ȟlb?BQ!<%S@&6m塙nǞpQ^ qc l  (Î23HCQЫ)~Q>З|+tYG#ɍ[uWNmgﯛ hKǪG3k3|ϛ[Rz pfHw=gcO[ލ5De3pyE|5o"V9{etDDDDDDDDD䆔?1(`w6ZEjU\TO=Ix~Vl(a>N3_c0^Wvl<^zL%ntS|S'Z'_fOO6$CӟMKB~ieI,]s^tŜ'뾁;} q+y͕=S󜏈Hz׬s̞.Ç0Z{^GFF2w~l6~BB"{ɞ=Ӆv~Z4okt:ٹk7!k̙֭8 ](_X||B֎###0i PdΟ'm ⢮m{\'NPFm#]f0p0ʕ*ѦuKn~!MK\\le?n  @^L椤$&&3gu0 OrQ:ZR9+\}i̜5$:u@W8~<#ݔ]0mۆse:\yQpww+CW9i!"""""""RXel-)뫓SgDo~9R2|iSem?aDxi$C՗ۺucvlٺ&e6N'SޘFڵm_}M'0YUO{QiLÕg̜5j/q#Aڲ"""ݧ@ڵMHHd̷hڤ]:w/ק7A,f{d_7’e͛3뫑<$2|`&N~#mfh^QvE1Z4oƔIhP^۹ҺUKLϏamw|ڵjnYs\\i۶̷R5* Sܸ4\x-| +V@aC˯׷ÆlcHDDDDDDD O-2f+s9s9624uߜҷb6f2Oy9f^q~d;ϹOZҙa=,v9vʼKi+i԰"Ccv霫"""xٿ\m]0erC۞}n +Vb}GL8xpe~n\+!MЪe ˋmZӸQ|*k1yx&O_¾f"""""""rȲ5-ݖuAK%HJ->ߥ>>m݊jժb2u_5)Y`n٢y+#n+o27w.s~/mvrV;'sq2__vl޼%u;oj"&|"6mޒSs˃Ӛ4eq)e'NӊϜa-4n ױsڍŜw)YDeEݹhڤ1ŋd2ζ;xb"".>!w\H P3nC2?1$)'&[6Ϝaiެ'0sձXzuh.lg gjٹ^ʗ_}{UQx3SJPNSOW_ЯdigLBUj[[DDDDDDDFef-!9qUD׹w89Am~[tKo̹!~ WYo_z5m߁di*בn۞f+W #%Ar,!K}=wEecǏ#g9s`޽c b>/۱nrsl&jى3ςw:i\*~s5vAͱ'%fxVoqRڶɷ&"##y/ Ԣx۾ymOhѼ9%qv;?"mZu~BhhWb2yf;m^\Ϲȍ%7bGތR 9퓛Zv оvܙw7 @ء]s?d 8y2Yjuc甯/ClO傸N+饶x-Ӧ`G <// i3wfKؾ}mڜaÆ}O}@@r)m1W.ft|.v-} :,f6U?%JӪe ڵm?JRR FY4n c 62C͕?sghh >['0tڙM~DDDDDDDDGYE<Ÿ+>m9t0~c^╗^aX;;n"۔|=7gR> 9hBҥ3Hmvǧ ?Qql24 i<$} ^MXx8>1""!GѲE:t6mr&vXXXoYŊW X/m Тy30 Z!0wys`A yb >>>zոQC<,`ïyb:owiF)RXm?u8|HNu70W8~,9,2WDmYl\JnNXxxbfڷkK|;>>> yb`ZNyuJϿXLbbR|ÏW>11Ͽ'O9^O+V\SV,:ezMDDDDDDDn,sW]i)^2uTع>>XVhؠC]V`Ϯ*~2e^_DDDDDDDгW"ׯŀ,e762l˯gCA9\3VgΞjD/gϝW_%_t:3g6 ?:Wl,]-;}s܆l6SVMF F;{lڬ/s+Bnt% R.;,)Ul Vժ4kw˿yfW9TX7&M_/8<%KCҧ/pU+T(˯t"N>nϧEDDDDDDDN[U_0v""""""""""rcRYDDDDDDDDDDDf`V.TDDDDDD$LOѣ6)TnJo EDDDDDn(V'NKLLLa#א^E(W@>9-:G9}.] 4jҔ,4DDDDDD$On &Sa$ׂacq!\]]4iy/8:APՖ Zxqq)p:\Cf 9~h ¡^ptn/#Gv*"""""""kLL &L\@tb6[HLL':C^<lm/zܞηfK-mCZ4LZ-5zF[qOVNW%g<Dob:ѸNuWD-yomzwz`%*נn;xp&lY1?mW%bUj4u1y<ӋY ]!/1gTK)%IQj k'g俥/pOԮD@@4om$$xfF*[ns c _O(_uYY]B>TaF4mޖ`GO8z}/>`WōDe*ݟgѸ's{WՏOwQLrAhۈʕҀ3vcؿꕇhS/AhENOD|+dY$kAchprl܂UP= SiOU]*VLP6=k'jkN2>'jV|Х*p=6Jst<ʪ еU#U\jПq?xW&p{ qL*PISB&4 d{?n^iC1ӿaHTH&3hN#=|o\AiuX=n5F{$??,ֱ^tJ˗U &7˘wbՆ,׆C0ͤx͒~/=x⃼awS9e] U}w{>V ; fӦq iẆe[EvRN!v=gЏ<2-Gxy)<3sdݩ*5v qœo\1+bk1l=~e'Tc ԋѭKg]<'<îOڼ?agiO@Sqgɕp&|8:h%ϼ5a,Y*qZmSy:bǍ(e c#h?$IX ?:ݓV"ĝgG/g1.Y̕K>Еo0?zHń?ͩ{ N 5xqz$p|ÇLˮe~)-ET~h.=%+`ƀ<[N8*N9͚ٷ(>f8'Mk{K;y8\:ٶϿ8[n]<,F{׬b f@ "IO=Y|ge\#{Mr23%>軂?S'Wtc|vCJ` Nd mA?Jˑm9Rv'6~GkY+S3^cӶ# _=} LnB߇`s4GWrd93 Y1-EM9G8a c&}JY:Upw WJyw_<<|Lc};冯˱!gfgzLcD6 jBhGK]{+KHK~iy2frL$2Ҿ1A.Рa{60V"Zj}:9vUGH.U2}BDDDDDF$aѐzSvbB;ɏIQ mMy۵?,߁ckг4Lɧm;~lgՔ kn#!`|/{4ۼzz$]~Ղ?ZcaGӿrwlЁ)yFӎl_Z`N>(?jjK fhs_e@+y]a3.A¿QIpif/i.{*u TO]lW&o,GR&5-$@>\3TyV׷y:0m6zߑR3Wjիe;?a: ~1c;{g([8.@paK8m1ʷe#U98=Y{x~a Ǿ1giŃC`Jú^nc-ZZgcL9lݴ$adyxrɧ -;Iƭ:Na-y >jі:>MGJ9˩,Tl݋z-hlURUVLEuQ ~;(RtИNmZȎ/<T:$=x| >&o`jl R%8ϷOQ8U,@_ZÒay< y#\wg2 Դ}=ԡ.T5 GO_5wz$QLߕmVۺTvh-tM9N ̖Vtqp_O D&&M0̔-V^ɚkײGgeŊ'G?b||ųP/xWjAߧt̀۰ .fKVZ>Vk$KRiZ[o>撩0s0AF|k\ߜ9;5850J* IDAT\}7{WRɬc Z5j4ZI}~Mӳ7W>7OxbVwgoj->i#ԷOS_S1PV g~N tNnսr_[|Q\c/>;`i\<{1vؿ]f1u=jy홞Mz3^w[js=ץLM 횕J2-[>;u¤?语a ժXUSΏw/[#\we9u0 CC'j ?·~N]t;sMͩ_[bQy79V% r;_tSn*)ʜnU3OrdHևߙ^4<|gnd!gʀvX[ߜz<;7y񽙒1ysμJ,wr䞑k5TqI3-lqzIYeņ2d3p_GT'!oeá+gAg 3FJReA<(_\_/<:d%ύ]aI:tL7ߜyULV\ /-KFmvny(Gn`_==c` ZuwW?S3!W]~.g鐎xk1P+Jùf~N jӢYqР ^uqޑYg/7d<~A9MreE~Î~^̶[,7 91I]JTNy0wӾ>KG6􀜰y{3}2'3~ktM] Snf _& -mv,gsjva}f\d$-Ֆ鑺7=?Aoc<._}tYK5_ghl}nY}}%{7R˯N9?O 5#7ʬ'ɘQYup׌߳3լռP?4hsW瘃wIwϗvOӫ/$Cˆ}~ܧ֗fN;l#sfr榍iDž9엳陿f4f;o\ܴq;;fNwUul%ӳaR^ߩavvkֽ:3^3!ld.FH 4_}U.=,YK5<9C!V_K=kl{9tld]5mRZ<]ƥqHz~,aj9[10+oΕ"{esSmcP\S+{Wyh3_`͈K; ='w=5#˭r[9[g仇,C_9uwm~|ϲul'@5=m9|{RT&<'ݾ:( \3߹:qAy:ҫnC}Lo0u5.yD>xI%2lK}r/怑Gf۟ӣobS+Ҝg/7]>C7/OWh"fmGg IC*sϿY"'w\,kl\sƨyk /{O~l}Ylsrα$o65KYzͲβTcۋQQ;#o:r;d~僮_ayq|LneWM$?|QF~69st9^sNp2ar}[>C>.>T] &c߭K+gn5v~;9h_gTgˑsEgeϫƧCl]%K]ysOnQt??92UolAg__74ke:_RYNzK2~ѬՑg[tƇ^:>S?|Z>ca~ѯsO_oJEWʎl i,3~ˀlCG=ȅL]>f|Ȭ9S3Z?'Yܳsɵes^ϤNrKn|+R7{9Ybf9 N]711GeުuH~KdutHxyRYsr[o%2 s^+ouGdnCgm.9j7,{L8pjt;ˬ~zZǜsjaf'[ nsk<)S.']34<̳+kGϣg|#6]0Ǿe70 45}JI}`}(0o~+ sPWW:es;رc@ IͶ.q@ۧ-G| '|rw# Xj">'EeBu x|B ("0PD` @"3Efi(Y馛njq|*m zŊs wkSgʯ.!|$ @"V}E+<}rr[CZϕe!k}%sVR}7f;yդO5]nx.|t]m급yy~bK.$I-|ib&/9CzN[K6Z>wΩJuIȻ9:ZS;o>fMC; /x;Дe`b'idN3t@CƽFf6`U'ο^w7rFK57=_M}\74ZFR;Ͽ{'Ͽ5fs5M5x `Sx},R}|[gHWff3ExϷ8>unu z;("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @" zGg„ 7v|~}ӫg 4h} z|455e7e1䐌:c:<|G~=~#ԜC=d~ O`h=rʩ}ȣJs9;l~ O`hZ ǟpBN9r8cԜakO`hZ#r{]fxݷ'~x[L`hf֝f6j ;0WmVmRܾ F1LQ>-|\ۚ>"0,|jC`.~"0Y3e @yyc?ܾG) jllLZMRIZ/QWWjƲ[y<^zeSUSRW_TkՌ3& ;ڠ!C=^{mGcccz56fE m* YCh)2("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @" zGg„ 7v|~}ӫg 4h} z|455e7e1e|ʼ i3@U+yn׫ezou3 F (cfJ"So`߁RD`hjӖu`43ttM܉?:i) ؉;ڮ>oa.ǟpb8CrfafÇrAT*9fmTfOm9I:7?8yĈ9_! fM`N/}̱j}sQGպ3¨9g>{R+cأf=J mPcccj*J|yT466+cjJ]%VZf̘1U  2$=x~衼k=ӫ1C.Z_`hVYeȂBL@"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3EfSIDAT("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD`HC70Ey7nq c ("0PD`HC銏 &dر9X ^={fРA-u0 ijjzoX=}=zLŵ`FsnIQ`0~|]jMZ}aoy,tܒ_Z۬_u9{~Svn=x|s'e-ty3iY,\ ;crUJ͛SM6+w<ߘ7X)]Z]k>߿o! ߞ'˗V@[%0nܖ?\PWnRHM>#-{|Iy溓s)_Ǽwղ޷ngCd/ڿ{2l~gm[]? g3Ofآ3o`:hܓ:Ƽ)=}66l.t?)nnwrȯe뭘Ww=WީOgQ;gRsMkg׿澣?3S<]Š %-)]?ok6XGHSd{d?W'II V*x9p _3c 1|>Kuz7/7cH>ό.{i&/ɜ.)|EՉyu9hS{g|^]Ge<{9u~u,Wkʁ_~~cp1!o=hw4,A%- ln#b|—)sn˒{^s97}qo5coOv +oLgoG? V3ᡋf@cZj|uy63eSuW9c6͗7&9xdN {uGeز}ӽעYj7rM&Ozeѕ7nߟ My|Kkf%{1`pr߄l@Kg)yϤУi/΢={Ǣ3t D-U_϶:C!9 kX,ŋ`I]J:/jZ{W>';na/ߌ&mNVZOz.>$_<uM{Uwlҫg|Ӝ<w3lb٭Gz.Z6=Wδ޽+lAV_a4螮Kfec&?g֟tY9Zt9::?gbX,^J"ԚxOsʗ_%+wi.cߜ~/hz"]:9_fE'/7)2/2~<9ML^kfq˥[/f~63 |.++eϿK-崯,|.i\큌\}||e%av#gйLoT6iCm9ye\tFqyj\L[1۞}M\Hnkr Iisg+{G~;n|~.l[^]b}tzi|农|ȁپ?IwIy yGs7p zWJ=̓-7p\7sWe;ePיlᔌlqK';:{S !қD "tX *FP@U"eVA`\>( R RD HryH]3BV[YWun ZK[iˑis?3y(V yWаBуouzoSb`Q]ЬQ+|FՖ~:xHM6ϣ>)7u(Z)o:FeuA)kK0W&j0]޷W4myu_ůh]:t:U2Rݪ}VfZO]!ʖzA3L+M(^NλLhY=bO'm*^%ʚ;FdYifmwK=gSډ۸3t歳nɑXǿttrSU{PWKΜ۬vڽT9=5}yߚL̟47no?-Hcɷ8[kʫ\moWYW_uK2=ZXRY{S+/܋>R eA]=7Nhw*Zʤ62W\nٸٳeaJ[nS;f]8i ׮z Բf=vg(_i꧵~Dg5)n>dN<lY!y}3`.i6@ZbLg%,|N6OS39^p03&߬cQv:pK! ɞU:"ULF9U,=T7>{Z0T[L9{K*i"DxWQcԤ(oN#ԇkeCSnea%:8AcgkT>{)Vs<>$Cn\ϬʭWRe {֗ZB^Ńjz4C)l}vG5RuWHc}*fyQxP)o~viҭ4;6'&3u5r_% +jg˸jZա_~<Ӡe+7C_mN7yzzdz-}'jd_j?B5h!Vk(O 2RQ>Z kq픾XZzm=ˇ'sp vL.Rj`|U X|~ձ3*/X#_V~gv;)pZD[xlgY+:jU^olLЛ\jQfʝ33U[>UjXn`+0|D=4C }o^Q29U+LSF^Z7s+LV6=ٗY䒫B)h:w% k=LMT_$=щ7{~m_ʐp G]Ӳ_Ыo W)dY-Lkek.0"tB]dݏsO'-:(owgҕt*n3jJ[/h 㓖eUpI}K T*'g%?[?^3WAUW.3v٪;l"-y#aSztzo l ÷_xYs!=C%WWߛo[Y!-F) Ibgkޞx͞;T6ڷUyJyc Nϯ$Sj>yfjXKO-U dY0S/+ ))q?\͔$N~<]uL}ܵokW_dÆ ޣ9Bf鯟}~:B6$'Mf^n˝ԷM}K -2[t/ffn~ f`v:r2 s/l6n9"NX uw a3$Nܦ[Vp15u0/wVR:.]d@it:t*2"mp:8{0GEEZ(\{}` 3f%K0,!`XB đa5Iڐ|P9qԼ({e/XB ` 3f%K0,!`XB ` 3f%K0,!`XB ` 3f%K0,!`XB ` 3f%ԢsSIENDB`qdmr-0.12.3/doc/fig/qdmr-zones.png000066400000000000000000001046761501654372000167000ustar00rootroot00000000000000PNG  IHDRvDsBIT|dtEXtSoftwaregnome-screenshot> IDATxu|U׭cm1AjtXXXt*h A lP0%Ej*]ލczlx?;޳ Zz}}K7X-kጋ[@2 ӳJ /jbkawp.,rMoJHH8n{겭|Mf Uҭˈlft99s&~4*Vf v (ve0 N'wwJIɧ-͛5Wt*v'""""""""""9ӅbbYK{jr4sYDDDDDDDDDDtQtFV2TsYDDDDDDDDD$%ۉO$))3%jpswnn+:paZari\\$,x{a)tln`*U tr6X,f3-/1^ 01Y$-,8:o\.V0TYDDDDYFB,H+~ń_c)""""R.hڴiCʕ1j2[lN,3%^X[բKa8sg5/ȏ@/+vC@@lg2\۲|6RDDd u֡BrC(+Np0ϧY;N6|@r"]=V_yrH#*.$ L) <N6:aϒGVYDDDD$b<0gelDh*=HULbAz7+yU'qxv|E#66Η3|xŨ|Ռ|x)cg3uOMeho5pPYDDDt٬8Z].67N+}ۜ❟]'WR"u-V is?x.167QYDDDDݎQl&s8|l$fZɅQ*F^nc4S7-v"#3/wKǝt5aTTR.""""W#vð۾}G[n*eK) FiSʞ?f|ҵ G;nJϐolevYWwmkj%<<OODoxx3X ,<՚1x H{/xbig@x+"ت%0ت%_g2{ M%2DDDD*fvfkf~F%[3Yp—{dr:wDd 8~k.ݶ""""ruY9u +dJyZg8!ޮ]st:s.2nҿ>Ӣ 8RWpL؏Hk .XnlJ0̄w kz7 3 웾ž_f۸scj+#Go1&F軶5b6c2ѣǩ\R,2mgp1L&XL׶m\cEEE/\Up/3pD@+`!^000/)PDDDD rD݉ #yVϭ";V\$'$40L&f3V7wJ{Y1b"I4xzVe#rb`򦌧GBNLna*K2rʔ)㛩vrvf3Q>XL&ޥ<0(x^Ns=h얈%%洰;ɃMIě.k<00uA%2DDDDe$'cÒ&"""""taw8p8]fVՊRO%2DDDDgsLך,""""l͆[4'{9K-ݭ"""""""""""93 4YDDDDDDDDDD NBQYDDDDDDDDDD %=t:K-""""""""""r1E=""""""""""R`n\`BQYDDDDDDDDDD Yw(EyB*s<@/9 pE?A+FѸn_JұWyK fG޺ *Q]_eӊo *s o$c\)Lp^6z:C\U EEoSF-8>\ 7/+;οйJ=FN.5Z-Ԓq~T 5-nxF.tMEL9<{RϹc{ش|1n%˒[{9a>ˎEf+[|]kڶ9ӦQeF*Xx|^OTшf{ro~%ӛfujQn;nzl1cr(a֦)R.M{ aʷti-"""""""""B0p:8~N\*knp1Q/?cC1Œs5p#s /H,0>4m?fbG;}g6Lϟؙu,Y*uom9콙/Kp?vF:w<ʷ|16V?][Kt1cٿV?ZgMڇyU1'"DF д~ܳt'uˣq/BXWYt?ʚlk#~ WcZֳpB2VV:>ȚWծL Mg^կű?R4[N-}ϣ0]!x7y B7L4Lm߁% 2e'@3fDWf\o>{67Ḳswh5."""""""""נB`_i5MVjYɳ}?&6cv3.(ĥ)>^ĜvF]mݼO9V@jyIu/FPl%8^[a?/3'2}{v'_ Jd;'# (ӵ{cLdw,4vա f/54WA740 U|efޱ?Ջ+9)14 /sOLfJWrz{/|\7=@oe͛yyvS} d\kI˖f(t,310 7Q-cA/L'3&hEDDDDDDDDSsLr0uhR?C~PHl38ޘqkЀ9ϸeMzw+g>̊4?)4T8$7opĻݫdSryyʎMֽjM=o%EDDDDDDDD$E%;5!7ׯȭ#ڽ1xcwg"R7ogWY)fpk|&܏\SĿ7a- Auxwγ5~±w2D&"&P~yPﮑyo,/cѼcQs +TRȸR˓Sf ;!fSZDDDDDDDDDUŗ4ߣ蔼oÎ2ݦi|Syp ?Q9Ν'3o[㯮p[xezVoo[km}bq }_%.LK>7 _g+s_ŝ7[„7V/(?sX|>pׄInp9fE~ro⧵kټy+'6.77u!][zA (vDFFr!6o?ľ K9_ woߖ79CR0=>ϿX@;nc+3/}gc̛:|9g쨑V)t̓'OͷhN:9?n :`θa ̼\:ԗs먨(_ϑngP>x _}L'&&{~7}Mvmqss+v\.~߹7Z9u4.+:Ǐl3*U{׮  ͚6l7\c(K}oe3_$:o7fLR |- 999_I|bfRYDDDDDDDFȸ0)suIW0j#,]i/=Q}y!99>e1,^ 'N̒\rq >ZƲKHHǟְ{N>SUٹk?YKbbRsg'Glh"c EKDDDDDDDZR;bʴ@Β]Zє(*^>/K}ik=v::v_!q:ԬQNC>i}dze6V~-7̿aĨ EdNNNf˯0gkN>aq:t ONH_*QBB"#(&qe\Mc: 70g|Rӏ?M0 җeImbbΙKZҫg"nygobl5N.9Ͼ1$"""""""%%!ځR3닑i&>5vR^X|||=r8S>30(K#oזӦмY!Ӧ0ᩉlݶ჏ӨaB=w1gB'۴mCIHHd C  1 yO2e$:oٽDٲ~GO?`9~!)9ٖpd(帨ץ|XaԈ ǎ+s~b7}㓞Dp&sQعk7_}APܳ弴lќ #F#GovzY6DFFۋػݝ>{1czC&<9U={xk;̘:xpeQi\*mZ_GW;uUEԪV>ur'/%}DDDDDDDڑmy[jY =96KV ╖| ZS󁃇X~Cڽuyg R d[h(Nf8xؓR~ʥp]#ʔ)CvԮ]́"""2݃i `߾lܴh|||ܱud2 "s&"""""""RҾ]+#i?_(rEu/sOo07g'g>>Sk2Cb-[_FVKb7G/`> ypZ6>mRjrc,^4=y)lJ `.XXxyO,+S o,GZWDxxCw0l"#?_όiR2GeL1uŠ=Ŝo/F +Ҥ0V9p?EOxʔ){ nw3$??x?{$ve˖-tW~#F?Mٲey׸Qq\Wz,"""""""sv \iuY~Q](!!Z2ԉNXRvygW3sɍ=\޿O> ;~-K-RڴrQ@\O>ͽWdJ.thߎ֭r.r]˖t iiYXX3g O]Trm7v4m۶ ""⋈\)}zY̯ʕ`m <==tGcp(UzaYئMkOM)Ѥq#N:E_i׶-ӧN4s'Ml=wGs>I 8c8'2V>߫];?g QvP. IDAT'Og_9'm׳g͠n:o:t`m-"""""""r%ɶÞ<`.S.1N~yzzҥs'n;ҫsې|ˋ?췯_.Cwr Sm{h 縯18UX nT9z8ۙ+}}}x| >_c|yuSjt6]fR]5/ώuJvQ6X.}ooGrRQjE'-ܥs"kŴ8 QQQ[:(-i9rЋ>ޜo׶ CSfX-۵b;Wީc5?'NdZm{(O>,? ȘQ#ӏeҔ<]Tߍ>=\)MAJFiی^$sNpsߛزu+΅e-A"}pgSd2Om3o-JkΗ5#'sDDDDDDDʒm +DFZrٳu}Fi MKZe uڅҥKfz~mذgޭK\;~X͋pLSbLf?:v~2r\k*y*X(\oUd3k&#;{lB9l ;eL˼5bsf,ҶnٲSm+""""""""lk0gߙiJ~Faqs}WFFoO?9>/RfMwM|3jld=w+vNj9zf޽{|?""""""""lS"CJVtttb\ e˖{fP8Ɵ7gy٧iѼ9Vk!t:yqT69}]yq6nCmTb2ig4N>Z4_eʕ*ѶM|#/e<]n#"Y!ۥ?yK3Ëm{MU_lnŲ/ˍڦ۵0L/K\OVK>ÆSǘ[V-[0 <;n/TsՄ$7j>Kr8O9&"""""""WSt4O~fT8U?a$$$0w_Hʤd̞5{[CDDDDDDD}_诿assZӵK':wxE=8 .ЯM̟7A#ua\L{yyѸQC,K8aML:-nڴ cG㢏ÃGƌi&K:ut)6 ͖6V56SNyN>V-HLJbڌL1_ygoUed`n߮]5t霒d%}}}KVvm0xd(Z_ V+޴hޜ#W٣;fsarX|l6sۭxdJ2{zx01/ Kڥ3ÇIOkۆW_GᅢO}||w5s:m۴d2CԱC/? ˿&**?Z`࣏/*~J_$&\>LQa/D dimTl_2CqOֆOMx(ɩӧSg%3gx/p\3D=Yxv5Ξ={]l6Ӱa=~7-ٴ)P@T乢x*gAJ||ԪU"""""""r1E=it#DDvaf -Z)馔_C"HIfLLgOG'>>ؒn\BxJh  iP=s,]*攨ukVU 2~EDDDDDb=z8ZA0a2tR0 008v??Mc"OKd۰sapv.2_E,8;v%$)k||\,,%d6 V}VPm">-ַ.\V*i%) kll,&Lkl1PDԧ%C^|Rf^4G8S+mװs1Ptԧ%C^|ԧ/VDDDDDd-BEDDDDDDJ0H>),"""""""K8c%/ĒnIvָݪ+\Wg?|Æ3~?7o]Z#5mKr{"\tDr'_. G.ĭp_Ğ vbd{IP38r -c޼x1\EDDDDDVǁf37֭EkӇ/dәOnq?ؿzӼ^ֽ voZN/=neRv-*T1\EDDDDDVF$ bRւ =v~"r[Cw1ˬN(|CRڧXt+\ϴ//%(LiIu;bh KI7J"&~Tpvd7.ZC7`wswB̿n^w=K֠=-<_< Ic9 c5yנe7= œϽwN*ߘOK0 <+5אi{S?F䯼xsFA<7{2z IN-DcN؊cgD3w=J>|~M+X;m'|({Y+>&in\ O4G:LqKnDzzp+ ϫo>Ew5UHAiݗ ~vvQz k5ϰ.N=7vQYNǿGז ^u/!7OM0녇gWz؟NfHK| oTo­GLq֤B@*T G}5e,>d=hոUՠZ4=~ʘqrf󛌽j f}t{^L (s:VCv}yq}lAOe2V-z!w\O^}kij̟uuS'bCӬN-7qolp򧹽gX[s7y}sPr *p1/^LuZ>;?Ļi;O%ܧԠEnי>0h'NA&DjnfYvvC7,47[1,|KJΈdܡܒ5ݜfqsҩi0Ua3,g4~W铬\pUOdő&r{42~^(ޡ ի$i'n;jg~2 Տ jS5k8~:v>|T?v])wӻCKՠJ46I9y^Sڐ5BE< =׾a0>~jR,}M:cyy{=[M.MgG_e̹ ^'q ͕xxJ|=^rIlM  [_IR s!6%̇г[/nQ=u#ѩ)5jԦNތ[~W^׸|{5n#)uv3s@5 Ոթ5d?""""""Rr 19usfhwDozC#px*n{x~0'׍wȿ<>k&oY6MMΪZ̸ʸP1'yrSLL ll+f:qggGyY&r"fuɏg.60W rX~-ƩlNi ?0wʋs"?< ~: %u MR#Fj9*z,}e&f'h"l;WgϠg"G7.bʬaO-)b x`,,_ fsc,9yXӃcTbsds,->͞ٷ >f8=6aҳ_;)>t bS!\8پý۫sy#?^̙܍rDP (KG`&G彗yrAY̐Ltk3ۖ1g`&wytrlMg5qLr 3[[I3eVb+0}0ʻ/Ne5AT$C۷p(^ڎҎ3e^=_0"y^f>F1邦Z>>mȤ SZ&5qϦmјD~.4z3zʾLuԻ+@:]׌IBܽu^TG&V--cuiI8}ڹDS8 >R%1l|9@psiڎxW~ ~y]/nn:0ͩK8jeų^9N ~EDDDDD$<5&ТAJ(O]:YL:['fW M,zn}u?9:YtڞN~,wQthJ2g[t{j}֥^a 攃Atfv[agYCoK w"\D=KG Yy34jQ1PMo!/%gMRo 6wcOۉSjτu֫iz2~y-ܱ]^Y:Sjy''KO/vOVȞ)ٜ^κ'k9O $K 8gv&3^=wl._0o>غC3u&6<=Lஜ 3Uvs6ءz6IKU~ .]H˴ve {'nz }ds :.yeF>4=ڼE[wCsz;w&sƥg*``$pl4Ҍ6h$=;dwM.ϱ0 \.lʲ'<Ъ c~vHиf-֨E՚-S'95j \xn_;ve7$c$'Ii&f }{=6}] 3fގh O.Q)b?ɨ( 5a53Y,_*oˑp‡Ϸ{5Un 0a"dTNbp8pLX9\l|#/]ӥ/B cfgK=wroӕ)k Y6;X7˞䅕._ w.?Dَ32-EwpORH*xCwVω۾e IDAT/*EQfdla;c[Ye0vf}0a }'`1,Խ(Ĕrܖ{s>/M׾9$g𥷒|o:k_UK=!;g+fnߎXϿ6 hU ;%z)4y/ߩm'[/u35}>?TytXȿ|*[:Gߝs~.,ZhMFkߘgN쒼p03㍇؇uӽgܟnM&OZ[ZU?MÐ봨WInҽ&9cGʁޕ~Cr;/uFze2型rsMRVZ)mCv x&=35ͻuO^˖i\ϼ)'VK/?]2첿o;71wMհ,[qKy_eYk!nݞYk^eiwQ=zo3=rȥd6MWk츼_&w:<{Jv~~!zósz?3==9Q3?^Ȍ$+\qkx}ѲYCӔ}ZzT4k7z[ f~~grd.zfՙy:ꤽkU_7I/3.?+m49omҳ{s9Ou֮q];[3Pk=7LMRdemVExM;씳N|0=6'\=7iW/M);wmNl ߤwS=>[{W[.*yB[k=sz7ef&+N5W1Zg{oyC>YMҹauzmܝ kz-wIeR/|eӰ<;Tf %gy^{.ל7M2_R=A{jA4[')Ϯmڿ5j}=w/eyN\)rn0ŖwaαkRre㧥vYu?d_& ƿ[='Gq5JeUe5[KWfe:ҍR<>HO%882 ڬ4wӯ0a_R[a '/ota\\6`fLnm f4B rҨsխg >͗eӬMƲEIi'>vw9:wF9ߦrʯźiҺ]V]GZv ujg؋2x;ڰ}tVyZ]vYN'Vh+?&6 :gYZgȅg ].Zߤe: -kzd#7gVgz~(KɴpTF z~ ُΦW/f{^i3|t>>8w0x.eQ4ͧޖO:6rɁ׭$5i9%I}{\IGe*4Wo]=!*2f6Z@=jy7,N`ǘi\t&M??4<,\ƏXP%4-C`^,U=ܙ$WRWfg^#5Y7yn,)*8BZ <1 <}\~:z慚!?^ 檼wǡRѹYq9IRZ5u&>'^yh9zR?Fߔǿ5ʛO ZRQh`|l ٷVx>Qx=Oθ7v=kՙ@ m3sx~b djԌ4 \~ꙇ[lm6ʲmמfZYm]3Uiomf@ ]fQSw,{aXev:Qʻ]3S`Zp+I( @IfJ"0PP`K o?RI]yv˚y) V0PExeDbtl [vΆ^g?+|p۹Ͳ SN,i~$IVK&uR~OF%gUziYcѬ+_(;3Z5NZRyǬyy|Bq_X/M˴ٲugkߐ'<.gݼQZ~pGN:lYL]>0Ӯ;( /;]8:0FOyvH4g*^yFFۯd\}Yy3ϲ΀ Z3(~?gkSת/c><ӼdCe'&IRTz;%mVoܝ r{i6]W৯FȨkpn~<7__o~eܳ/gnݺ[~Vɫd̅Fk͵[d4~VLuuR!庽~]h6 )+6W2kqE˕3谋s7ezcseFŊգ"=ziog-b W7tlqޙrYmZ΋٭O|sj)b״Th&yKZ5Bͱy}r![6Oll?divb^ן_y7-'.Xȁ9)[v\u_yN8 D,M6l]4Gߓ+N#ߛbYar9{]>c1OL珃WJE i6G ITl+ϰ N ]nqmq['|4f̘ t8-~-ipBF`B`$3%(F`HcE=f/^Fܖ?NTyqi-.;ՋxnK Ss!+efRQVӴmŁ񩪙Z~t5[O`T0U_o䣎RS; NAOk楒-=(?0-2 M;f /;(+M6csxeDbtl [vΆ^g?+|i;y۬m4W;I=Ф?gzյ3ǭ|~th8ukJ Ś|{̦V0G2S!/_+{(IR ٻ^yrrw䤃Ȗd囦Iqbst^Xjp>031S[wIyޗ~l~٥SL~:tpW.4țXh`qi_XbR*N"{u-ORW<# MIz.yxyMy$)n ES{ָeKgٸCYi/f>Fs˶OBuMx'/킜0o6->{qϾミ_4򛓪+3N3s׎)t|>; sڽG)KYmuSv!G:Xr^i_Ӧu&Zfae3ʤN)KU&<6pyj`Q8|#(^7띳ma3t˽3ҵ2i=[ƙBÖiYoFuMXwQ&g @)氌ao\_n~6yv<*9ڱu@')[v\uߟr/dҿyI)~!sX( 78h뭓g1cd/iuoQO+2jȚZ F`$3%(O00×F䵪E=*0W}relުQ1嬫553miT:rس3^R(oj;1'<7gpa^ʒɭdoR鞺>kx~h]z/3Zʰ^V#CWLyqr^{y}a h?rf6.\aʞz.؋?[iu*++3|Scsfz%S歹o1l$$}]:40g{QߙC]`֐ŏRv xtZOn~}>Z7]ͬuɚ=3#OerVLYl|S;g/ψ w5w*KĿ><Şi[٫FȘTV4hVE~mrtW?3O_Us2[>0=?<[YO|%ϿTkeNf;OZ5KV=2/Srj,.q>6u)N$|FsއeO_[O'o]{ARs6ewFSrvykMyNiR{WS];Sʪ-VZ)TWG_foװ]zjz\1Z7m;V=~ ?+JNz(NʄI+ZA]){sx,3juϪ=kck3<ʗ󕽲1f5{kdej5g0jM&o,ski<\izFjsî2i_$^ZZo i޲y _省'g*sܯ6Hz_乗&|ҸJYpk>gﴪ3-\Xo'ku>#Is[)~ͭsݧgn$]/\~}bw<'z?4"\T=2uZtʚ۞?^)IoxFܹL> tF4m1k6eIyO9*$iYyeRgy#-z#cƌɀM7_ׯߢW(dȑ53KD`$ہy˹scL8W`~<^L| o$BX+\OOb>/[>9ҽ~,xN ة\:,]Vs c]YO亣nlV{z%y~s֔{zUs9>30ʚ:)(IvtKּ&_5>|TL_K>R?I~ 85NX,XL o\ƓyfRwՙq9I -a^)Syv|p~_=V;wg:7Cgn}SF<g4%]沖өYuxv\&Id 7D=s{b1IB!)<32rno55Cz?jt]{XTC'fvNvr|ۙ9)s|JEUJ ygWIu^Yi!W?y&NsӺw,[h'WM\*K䀳osϟL7dA6Bôr~L5w;"Glvܗiy{sf.v^zl^?hRvY6>;O޹_g &^N[Y[:\rc=\t69닲4hL:70ݛ88ٲ^lu?pN۲OV\y׮M*/LN1IP(U&ޚ/S'Z!u S^8!O]߭uԮ[2j`K%B ߐkmn/hԾl9L G~: #[n6vĸ7.=J:]N}Rˋ"}=>#7uOnt&=OB"*^yFFۯd\}Yyf9We,moi+m0ˮ=&I7q.B*=g>~}wnv>b )++q55)8⠭N>i̘1 nV-*5;-ܶRs ]wN~4~B!F\{0/jUy֋ҮE|crkY\п P% 0OͻO̹W?uZN"Tg`nKnK 0xoƝXC$D`$3%( @IfJ"0PD`$3%( @IfJ"0PE=ŗ^ĉ dZJfҭ[C/R*++/y'K/{m8aBz^=)WUWτ 4G+C}QB{RX *&)~x9tΏIV0,yС9sR(RYY g'0,+y ;:ٳN)fСaC, bq+=aÎNUeU85=`q5| '3Μw\2#fߝtRN=yq'*ÆU[߳Gc>k[O|Uf}.h3bj %;, *f!lQ~z&unM>ofB_A`).\ (=(Y_C-3bX*=Pz_[_\ * C`XL D`4`V0P t`.4~"0,~z>Te% ULm2uꩧa&_}쏝|ʩsԀX =򈔕w'Cy6ȹG,0,qBw?l= 0dCS^Vߞpⷎz!>(կ26bJ 09i.6l䚹.'`frF3W;1bÜm)N1,=t+үsw}?O/3|R/D`jJ`NO?Tk\tYst1m3М4u(Eyə={vveעOd&O~>={.~6-m^<}ڴˮj%'R_L4GK(0O:5h~N4GKx 97mhN*K @96oIs9f//C`լ=T/3Ʌoݦ?C9`a =3sSu|{-C_^%2 KU'\'-J ˱j-Tɳ<z~k++2Q=O[9RrkbKq9|/sd-r/Y=%i&s0{_<.N6z)?\rcT\?Rln{7?9rϼwg'#ʶwN]Wf/|l>Jlܯ~9WoV&o?-۬>gޗsoOw픎ʮ_*NYpn&}'~_65k_#_<9#W~-jt5?8nt\W>{w;Ŝ}tYy>iksqܔmjι;)v@ڰKA|wۙM~l6mEJY"jSu4 6A<)_{Ar}5?ܦC~\}I-ImM磼.9,|9Veo{՜wl8zhxY5iSyəc2ӭo眙-6kL|A״̡]V뛆C -09hjVj5λ˩#Cw{-c+~9g9cuWrgoFab5|_an[}[9!kϹgз~mTZ~Öi=9zߡyuΩ\r)9v~O2m°|vrk93}r3đd`iyڑ9icl¬ci`e 9k`%ҐoN&tdՓU|&7[m^oU'_zL/[M~ٱglU.γ?o͛{};??XI&y9ce:tLzxŗ3;'g@$铫^E?|!Sͬ-ϳZIVKfg&o:3獆Z:6Ͷ})3_AߚFff*Ӵjf1ᵌ944#翟jޘ81hUnzme`6y3`)rjK? oy, V?:&woy}DߴJVsoWfJV] s i>x퍌=Ԍm}XWa79k9ukiѲUR[p.J3z\۱BUU9X-ESz55wH5ja?"5^Rv묒ZmX=gVk6wkPi9 ,8f9i˞Cs[u'*k}!pb?-'?3ۭ+^~qzY]n"NCul8rUͦG};$ =;4ZjȱG>W!mNˑn˯\6?`%Qi|kWԼyk8Όsb*̘olV%qao ޛէ2w{84#wO>][?irn]}#w(%y;O>h+Yuٺrʀ;sI_:kN]y֩eB'ZX5m^Xkmκ;c;^n0G\3- md_KFff`eє 4 Zs:֍/-VM-w׎׿+.e~U/0^vln~A PK!Ͼ){0w5獾#gzijYk9u3pJҲoNԼ| 9Ңf{s?0WQ_kO'y.k37p`dOx+iE_tz&þ;6-.++m7=/Gl÷-/vVN:,aj w=ɽޛ>wj 'M]]]t\8>n9ZXJ;vY`&4/Mo472Z"D+ݹ@sfVF;SVST?@PWWj @sӔ9xIsN ӣfUTVdTkռ 鴄 @Ҵ9xIs^sĉy뭷^>Iө>z}47Mx ޽{ +,s0,PO @"3Ef("0PD`H?U*4XѴL{ǎ] 򿯼T['e9%("0PD` @"3Ef("0PD` @"3Ef("0PD` @"3Ef("0PD` @;HIENDB`qdmr-0.12.3/doc/fig/qdmr_channels_large.png000066400000000000000000006653571501654372000206010ustar00rootroot00000000000000PNG  IHDR9<jsBIT|dtEXtSoftwaregnome-screenshot> IDATxu|U׍% `Gm- `"HEI DE Q)EZ %EcuX`c>.9={w&.s4R z׵X-N8KQ8~yH0xxxThצJ .V,s["""""""""" Éfbh7m-.. pޭöҥJ40-NgQVDDDDDDDDD$ٌՂ]?4"(CfDau9EDDDDDDDDD2evłkĄsF .VfuDDDDDDDDDDrpbXX,%^j;NۻX]jrSDDDDDDDDfh#66Db͍be5 S8EDDD$vm$:-xyb)򈈈#0]Q=r @\l,gΜJb1Ibf wT@%tbl΄SD:39:Mw0?Hv(]^$SSDDD($lEQvԩa8q88I=%f3̟g_+郋K& '^$kU v a6cXI0[X)uYs:X |""""/F\ xSǖGy."""rm9N¢hѢ˗#666ܟdFxyy}vg1Y(B#4e1bBqdj2`PI.^ @=9EDDDJ wN .~9{/?Q1~r3f,}'qQݏTOk?4.+PDBbݎjEJ>}=kя(a""""9@Ŋ\mbN:j6^^2b Gn+S K8R{._^썌_}\ .ebBj.""""W0HJBxg\p%W/JU#AӑOhS/p&itbZ' hԩu<MTq;KҩYm<EًR5B_ G1L{a?=9)n*jl6J,͖(PN%8ϱ\aV""²pسWGl 8]nsML:|͛Ln N22 D{`xŎyxsy-3I=* ; ~-ް&9VMyGǙ7arKtM~< nXWM˂ =}$m}7I]$n* Jsdpbn:t:qqutU˽53+#x~$LrҴʸVWĸVԵELddBINIǰ0 t1aDe/aq!>NNr 0"o;}\e7$L^x[lDDx5ᆋ5GCq/O1b3y̘l%iѼyӤ#M!isOy/xN ðKr8"#1ϝa'Ɍd"^ZxHDDDD2 0Lޘ6Ìʇ+Vy2ˬ9<'f0ۓ{L*"""#f8ظ>s8.%-4TpbzNJ) N\1ƹ؏ ̹(יr.n_c# wx 8=G*"""# N8EV8Ob2bJ},^˖-m+_]tro RWXOt18-Y\J/auioW.4K pMY]2ƌ[EV$%`00L&f3VW7= ;f<|JaA|T46p{%c\=""""9t\|/n.̘f#"9s4^Ŋa6*xb.X8yƥ&:ha}Dxd/)S=9EDDD$& ^xFbҾg$=DŽ;oX*WA93nw-P'icHNLӧ߿d`;wd8MPat7x_DgʾEb<)V=x)USDDDD霙J<\f3DEF7X\8lvbc㈎d2U)0Œx ĺ=zY {ٔAb3 SNm4\]DDDDe$&qɎaqfcw81=/&3.V+fKc< Wqqxqפ)"""r0M ^񒇫g6IDDDDDDDDDg'ܸ𐈈ДZjpuYDDDDDDDDDD~ጺq \%9EDDDDDDDD䆦$ /i\`pB; 0U6' #yV62|jb澛$%xS'/ELrt7[!r\AqTSWd &i79;hE %X23;fҾBMN,\c\ڊ=O{˛Ljqc'8ҋX.Ǧ yXG,z9Ki<ߴ-2HA}KS 5e?-;OhCِʲ뤍k!WBƷ2x6.:?ELiզ`X6"dƘ=ӟ6 kPR=Z2NXTCJ7v 럡F.L qSTЛw_.t¿?2 L*uir 'Te4Hbpʚ ҩE*TT@Mtĕpm-""""""""#yXn۱88s4Aϯp# l4}% *Ig)ɇf?͝%Ot8Ѡo{Y+s?4n4_x8+?Bd@{:Wdy|p.#g.``Uz  lv4yn9#~z ȐKrݼ*cs|3sBzS4&z[_{_\z֣IM _9iT1[3b c0ko]β=cx-;=WzUɼ㥅ǣ{"FF=?'zGBޫPh^y)bFQ+1'&j7t}IDcфzpFȄK*""""""""R /f*E']r &wȲOFR??"s<{6xrcwOX>9)x/y'f=.BicI綁.uEs;y{,9O {컫9n朹]t1sg|[vN깙>%xıy` ^6ί.⣇JS3.&?Վ?aߌuyDDDDDDDDDZ)4Jr MEDDDDDDDD䆦"""""""""rC9{ """""""""gK."""""""""7,n+24Z]DDDDDDDDDn\VC9NiДZ]E8y*#oϝb|sfHr[ףȨ(}QV}~Z͛rAb.]ՅիҪ%ݺviFZ9"""8t7o5?qABCՃ޵ [$8ZMVPNk~\O?W_/oT ܙ9q$_|}=b8̙|pgϞYջnn |~f9-]99Ӟ筹b0rP|Aj弤y].@#ƶ;E,v911ޏO`?ػ|^ʃGV-quu-r8Nv{c݆ ={әnSNsi6 ʕsǎ  4l6ֵ(灃ou3eɢIrzm`vw :ybczk.9?zO`g 1]INp+ir}H'-v9#F=%_dHp^Rl,_~ #G?ɒ/*2$&&x 1?3i9NN:ͧ 1th|UOwqqi-{n~ܹ>{*>᧵눏O(r.'nOՙW.\Tm,..b\/EuH&N nooP!±Qv{^щ'^.qYcXr j*k­=oEf˖XJ6lca؈'_=:̜V`a9 9ʳth6 I=2<%8ӊgƬU_>hԦj׳̜=槵iA궴"""o@꽍O͙hެ ݺv)_6__yޜ...=ɹn6$""""""uҪIs}Y2,`$8ǗÇ0i=r*kXX >[ıкUKNH <ŕmB:y">?۶|B֩Ef~ߵ9٢y3mA\\<3gͥzpu7jxq3i/^"C2j-(Qd/п#ڐHizmsكRdhb9K>=bXa脑sfϼ>izw׹Oj"a,vv24i܈"F{7ݻuU"""xyW-ݻ1uJB2>7U}?CNp],Uj+Jh:CyѾ][6i\@\@ L4)&yȵis[u-1–[PHEeİ}5kPiެio٢9ڵq)65?u#߰1\9ճGw^*pl۱gϱn>R艴k)'mZl߱3<ŋMH+UdClu suu LapA~޴(|||h߶ 5jTd2r#{&""""""r3tNΐ֭_^LV]â^P2osy9oK3:N3G m[lM}[Z- .'~[2e1-3ek N'OpԄڙgٲu+M4uܹvm1'm%~=Kʰx;w6͛5dL&پc' EDL&(U/G e˙=ml{:}r)2UJtt4&ssQ9a΅'sumbƟ3lwssdn-=z0u*/'[:v`үrUIS^g;yj___F {@C[ #z >OX/;sg֞?0D>ݻu%Z%JsWa#FD[zKI⋈Hd:uƕTJ c{q8)szajѢ9S&% E_.gΞehղ%S&_cO?e\pB.rȲdV V,߫V-%WjՂ2lw:>s/>:{5۶L}=cTjTЪM6oٖ"""""""{bk IDATig:'b".'yJ:wC.):oG[z3ə2$%u,כj`8NvAmryk׮r0T Xl9wuʗ 8=i+>}~ @zH^>&RnJdddIs5њfry8?uvXN3kl"""""""glӺu+ ^D.8) XS2DFFg;8C}Yߪe H|JZjbq\V]۶.?NNm|W'Lfר[Us~^8#GPr%<==s]]:/ /HHHq9>*TH>~z2l6ӭk\J"m0 ύ4nVO'[JS?mBZӡ};{aVgѼ;w6M6dIBرc'CGNusseڹS344-ɓfwJf <"""""""L.E+***Cԯ%Jm:Ͽl/1hܨVk&p8}.ƿ6MnNݾ{^ƿ6oͤb@@кe ʕ-az;vؗ>]8G NѲEj]}ބsR 8![.2ta~ݼ%W^PߟڻD \"""""""9kf+Dn 0ҽz(ON0wys>>zjڤ1cG p/xl(:ulϭy&+VK.}NV~7#Gu:l3c_Ǥ aZ :vlEgYU?f۶(lcT ,zacS}ʫkVR,| z|BB"?{ܝ2fU MJ֫['pr=MDDDDDD$/LQa׮Oҩشr0G6 qqq̚1gu-%'uW&G  zg^.K1is#{&)udu]&2yjתrڵ}sԯW9fаaw(gNvѱK|'L&?+Ε6l'jɢ `iEDDDDDD$E w\\2ء۵3M>usf`@{H1lN3ݿ===WN+_61aA9w|_;AK2:{\i \\\pqq՚}Ξ=[;w\fMӘ[1AfMjEF>1>!oz]:c6glNkfG+0]9i3tԤe-x|:C|||2g|iSh٢9& WWWBm |~^-,tajO?WrʥoO"""""""W2Epu_22ѓUsn:ɮ )=8,V:gϝzDΟg˯W_gXl6~L%&X[f}}xHlN<>rokֽ 3Sܿlϭc1TܴX֮߀ZHҸWd21rP&J>l%4,.:J 0sl6[?0puugfO9iy$& ,^PCDDDDDDz_^ͭzYwXuJsc D+[L%8ʔ.֥ͫ˳n:B>wQz5}8u6r frҩC S9II[{f~K,+_HSߟHOV-_l7?{ l]]]֥ We=n """"""TIᅲ _q=y#%YC BҥxYb%6l6ӨaC&M#Gٺm;k~ZˡÇx!iQRWF]h٢9Ղ3*-Vlق+UWe\O%_p\pҥK3׷++~J2i%.\ę3gTzC W)*V Y("""""""""rCӜ"""""""""rCnݶ """""""""gKQ)"""""""""7,kbB|QADDDDDDDDD$ϴ𐈈Д"""""""""rCSSDDDDDDDDDnhJr MIN))"""""""""74%9EDDDDDDDDf-GMТ.M_ouQDDDDDDDd p(BGb6[hܴEQێm`8 RSDDDDDDS g'O{.\C^^^xB  iH/бs.Mi'۴>TiEDDDDD 뉓mc„TEk09p.j:-i=b(ozJq_\ڸ9T)rn({6PpTEC]uq3p """""7kQ@D@# "RIb‰_P?1p?&XEDDDDDD_7gӸy}؋,"rc7dEgQEDDDDDD䚺9;6*G'~?vDZOР1,:Rǩ]Y/eVOUvGp(A;f1p"Q4ۛ/d$qC`TN4Hlq ~M}! Z k4&!Lp!GIHǩoxvphɣU\ʃjV>_# kR zu p ^YyZg7TX3+QBEWNl~8 'Xs҂UXF1|j_OUP`r9 e iso}_A-İ} IѐŒ3Ңbeg)l: 毯`wL=ټ;UC˜c.o>Yo[c[X,7U=Y.qk_f~| ^t1j[>~Ũܢ/̜:phw?*wTq+_| nǃgUqcC{.DKqLHSxnb6q}zʌ'PʜS߀:<4gwS憵CM܏̸UxJwFOxS/?A2.Ƕ>#My/u9!%F|8=<3oj!1"G}ɜ z~#3<=lƓ~X{C{'eO'~zRZ՛b;M YomӺT\EsUֶԭSуpOбI*WB{WAHv' ڑeK-uC4L:!9fDkݸ&L;Yo1-`]hZ&+UR6;}GjV!j-극L4& }TNV!&2ϒ?|j >=֭{9FV1yPVrn}\HiN]ww n *RiOy P|z=Kz@:6AŪ~>qSMмU{n7>Ec8e.T?%+(f+MDeH3Lckc wPLRr`zoBp*TkD{ƥrkLŠ" F' "xU,sobl89l,wwmMI=jȹ?s:;Y<zԧr5h]w׎nŏ~2Te!Ùp9}?Z_!&51}S ?omdgc}-SN0l 7߳wQm?3[Iux__ian =}Hxq|G`> ^xG]:MȤel_1=ɲC&J4h͓֜~FkG 3aXVTHf2d:N}GQ{ش|{$em_a?->,p ];udR.巯@v R՛t牥p^;?sǶjU'Uff=֓F5XưlJg\t y.pL4mҜ{#8l1k##r """""ן_m𿩏O|ܷ ē^<G /~s*O)Fڱ4vqrl8]1Т0GE,fQ_2x|V &2&/X7ɘeqbnM1n,:˲qz'JrE<LlYlbqv/};l%̚8G,4^Su}5)oo"~8F~x&gVbĞgGog$޼>>Ų]͘{cya΋ToLc}Q,] ܝmk✩81Ɣ_\>|8 &~& IW4'aeڼ0g =8m 3|?-^g2h Knk俜rs|U9L9eKX4ՎPS o|gOVL%r])6?DykRlҥ=@&l ugyFd|t t }7?M>G |1.>KIЬQؒ !utq ֖eаI'U>ڎA՟/KۧnÛcj`9S|VMjOqS#0L؍?1N}\,3 ATufWKE?jz_Yv~av^1ĨUվ3?%eL3ޛDӒ=|*~)ٌ9%Y1O݋<#)IN)R1 vBNq(y6D6^@-=,k'2Ԡ}-mq-7,coJg32-_6N5 pWBf.gNtN.oԬyGۯLr&]۴M%3FXݶN^`9#$s+F8pxwhIf ۫.P)mfCgG>o:P%ZJ5 .ksjD[ H5ם󙻭CA@S{f}|w (e.ߟ7fb /Х)y;o\O0︤s|F.Da.ې! 'g̾mGs4  >46BJQ%g^~atfS>-x~xzUX%UOq8Ν!ܪQŰz9nDz יMH'1L> l:c@Z;0H?b;{k%J5B&-,g"2pj,T )"~]{ܮv!x0{2l8v)OCJ$ ߕclCH{кL<>X*T[E"]!6ݜ07mK&tNٶq@*פn{ >'_n G2Q6-\I]/3e 0SƏyФ#|z*Jvra^N.mkJ&\Ny+U;'Ⱦ9pip/:|H'no;M4ls͟W..X1p؁vC;ג?\W 7XpaXxlm''޳=_F&srbގlϟ'p{QbzrAgh3MtLR6TIg|6;,VN¾}dVׁT.Gl9sWXs<+vlgvkB1v'8ː﹜SInyj@.2;2|sNKRgSr ArKn=sj"""""r}Rn8+'rq" 3fŚV.gl衤^;qb# <3퓯Yd6d٥0 fSYk(ן~=?5`r[_־I=2Ke=',V0N;v 5~2c/,[gpNkjmHfvxVɒ IDATH4K,^u16K-O?\v-O+؜EB\ Uo.]Ņewq7a1a?{g IaްFآk5ƘXbQc{oQ^Ei{,.,-'̝?sob= Ji`~OpfF~V $}2rpV:Y*gk4+ 26JP2~zм2_yxa5wO^J y`ipNg Vm12 6Z)gb<=NeaT^s4BPfz6˜*o 2;Etd̂])B'aӚS>9ՁP#ғOjʜvӱW䲉{iAWf*b+^o ܬb`%J Wgo sNHyPv;S. 7.\oa[KY+j/͸vyuCeWu">(]òcm"oA4n_ݧqw' (EeC"nǢvv~3o[8F}JSi]9uDݿV4ב{hqWF}9mPcOl\.t`Rh6ƿunv`Ɔ(cnJ$(ӎeW3{vǷ S2槦~Ԉ|-6f gL uEsag't"sҤl912+5 MNLo ;A<8c6:Ҿ+̢d{zV\ushg*Gph$Fdؗ _-JMX2l$zR^ǵ,~lew4* [5uQ%;Y;\p#t68ۼ%ër>+OaK"8x4y)ҿE۲×M#|Lz6I;gw {S]*8מ߽2~)O$Rؓï:+z}GBC>StY8n3F!l% #w4as6>|.F|u+~h5/X1?ʫfaܛ.in%LlQ QC:\PD||:6 @Ksv!=ppq|PDحAyQ;~4սimڔAz\7{#DrOq\ aUbt\R iڱsHw&uH$D"H$!sO&PѬu,=`<Өw"o:d:z0n*FtsaO%zWhN%WZۑY3t$-'^iyb p~Ϟ>CTqعqc9.q*= RVojwL0y0Zcg%PX^\~v*dbF9Q`ˢ3fwt-<%ȯTRO;ظQlfiNx*^O_g~8xHԏ(0L=I]$Fo B)`OAxo,fEM҉u:_8??`G)uϘ/h4!kk3` ōqsZvj',hڗO6iTNT ʌ['fi)J[=sSV%ZUQNǜ Ka|OU5$Q k#cp>e|>ɉһ,[gb:6ﶌnp1=o:-7wr\, To[Vf_hUa(-tbԹ|!¥ȏlژ fd˒'1X:{S14ovY_̆KLX/ Ob @~KY4ɋy3pIγi3)tc00إɂS2QP]8Yĵ\7%gf?(Pwggq3FH$D"H$%m[6bJվJ2xf5S3kyHEPTG@!mj i/oJΝ;hiS3~OT! X;UVhcZΉZJxkϙH$D"H>ju?E-I63. _5HMD"H$D"H>>"{7M\Z[ ԥ VLB}AA:cX[gS:M QQt.Y~|1-dD"H$ɇ@Ѣ՚kL@"mnʵ7GӲ]KvyH"H$D"y$,rj5h4dBZzMrM!cZm!,H$D"H2bՊer5@"'P*|Ҩ|z`}<|]WD"H$D"H$7i!H$Y\|CO=H$D"H$Ț?0xI"H$&rY"H$D"H$;rD"H$D"H$DZEND"H$D"H$DA#9%D"H$D"H$\H$D"H$D"H$4rS"H$D"H$D"|END"H$D"H$DA#9%D"H$D"H$\H$D"H$D"H$4rS"H$D"H$D"|END"H$D"H$DAI۶mzH$D"H$D"H$wH5u2M9E *T]1h~y1hLQǠchǠWj+_i7hcǤ5 ٽs'j: @:!:1`߾}E9" "%ƴ ?| zFɻB%k||6|LZB/v&.z !P*ﺢM98*ƴ ?| zFɻB%k||6|LZ%O`Qn~y1hLQǠchǠWj+_i7hcǤU:ƝYY]i>"/?>iriɧ47Yw{;:IzS7G*~ͼӛHwK"ym9^QwH깤XML%[ǾϚMjv"n [EGLFy~FU9kxCEㇸI_i^O>4Yt;א֦#~6@ZZßBygd\ȷ]&x%*91xɭc[z =J1-'m=77QnoQ_Oϳs~k=3g76^ p'֌c/g܍Jo<~k_Zc]M*ŷzkNr\6\zX0$+g,` Jnڭ?t2șCfl;̭㆕ޏc3V08-7fUǢR%q́@O4d:e lxUN穬"׋byFL{{׶x6c~.&ڏ#V.mY$ĮM= 1t.읰ˆk%}}τ}go:㮕;} ub?-&6#>~yPE`^4(N(Q+_&*eeO6f2ee#O<ՙW>FMćR N3CKkir4 &;vNxSbNžXLT8OJփȨfo(ίHڥp.[rEƹ [k ؙ[m](= "ҢR^nػ&yHsCVh4הK+8ݳ/N9\4{ƜfR/'pe~sBːC6+R vtFay3(ssW^;1=(;/ \_D\ntN=__sz,;s$ک}nZq.BhyrWInz#i?~:N4翣f9am/UZg^$~F̴Ǜ،-47aQ\)ZoWg휰vpջfƎ[fo~H~\&ugRt]ڍ[o>bG>Ɇ2 Z 4?%}'>^C&fv9|]]FI!Wr/H$GE|aFCm%[E~L1]/?&֯3˦67N眙~v}̭[ʒi=~kVDZ5|\:k7Y-#;m~J6t#@8! [=vyȧ|?c]nսhb~B(#wq' CͿq(۽fE_@aͦ%Vw//@_~ F}i|ʃ/3ɧ>JEڅLoY~c8`zg"M3r9;X< ̪(c;{苉O՜jLOƩ' Os.7R+4- 0ukpvg]'W^_9ΞKЩ@8&Uk9B^ t50al'Ө\ucrxpa9eÖIOuf+?MF=wٶEC7W^XC7=r.RH~]g"M4ıX+߿1A}G(a^n1sAz06ƕ5n.l]lɋ07m̈+ Dp#2vNfx\m4\H{ro]OjwXSO2 <<sGNOy̆ PFEG\>+Ӈ7K?;#77c7~{,c͉Q#T[kGQ&ȇW9;F7Ż=\H9Kg^|scE-6qJ|+Թ+o> d( v&o~8s1zEF&~q'0l xYbE MK׉計LnO1%]SIb]{Oa|up7 "T[ DSŮOSWb"q'*) Pc^<aǏqSqaJyai]LԨ.;X#,v;L y G+k}p+⏾EB,E-ĄD"r)JV~`PKbb!O`m/CV_K $.=KDQB{LDE;bo= .k教E+:V+" Kpn&QŅ'- 쬅SRMa%}bB8y RŰQޏʺXd(6=5ώ  셕Z-yr&= bugFQMZY Az$ٖg'`+'ٲWZVª q3Ϊ62h52*BD?,V4)KOxl*v b=);yG57-6V9(csf`i#B`K ("3ꏧ-E)ݣU-Evh&AgebP""q'*/v/i$,$#*:DԗQtm)ϣ";;NDP.Gac.jtKOO=ERDv{+9Cӑ?]OV؈C"pE}kk`>˩(hMWѠDRyFc7;!x&vOBT*"llg`x;ҍBAw7jMbai$orEEĮńyuaUd8.k5O.lDޯ6) ~+:V, \l,R_8X#<QOŹD"Dvka򅈯#[UvI ,LcBjU!} |Ԙ)cҌ~ȧvoZEvo>o?.6 ˊVlOsy_F4FeE6+ a%~#NDG|GD^(;lxd4_["FVO26RFpqg~=/z'}qhx)aoWZ9x_<5@u(3dxh~i+kOVVsETa^\[/F7.&r:X ghSrYg'Ĭ.ªP7#qrj%dSD yDyOt[-e_agwùP[ ڢa~l?*ߟD-bxR-DbQ.M%\jNaOL莊)訇bwo+~vvߐJlbS S CԝrMCj6MIٛ<3k^i8d~/ ܭE`j Wb&2?̚?05!x.悻bB[Hg:\/뾸8MT5g_J̈́u1J;bySGaUa&&nV']ŵbBZXy"*j+QmE2*E}kp+/8[ +doۼQZċsm۴mZmZ[[Z3.G>"ȧbuZ|ohj2wrfv^ oõla% eT@,#޿PrK IDATA{ۏSg4/&.~Tűk☑: s4*@#iЊ/zs~cr(ei7|6=s8xtcjϹt`Wr!\qCubfϜ(bA|4W5Ҍ&?i;nc 8ލIN4&lx)NJ eͤ %ǵYkqD$!L'PR)ǟ{_Ps'gAΡ4h<DztRnaVtuϊ(u7XԶ>= tEqJ4}:D]Ciljk@.gQ f.x~%iQ6̈=εG3U5'eS|m2揄5x@TĎuQ&x hN/;>8ie:z (Z0 oOy;g{M æ\u𳬘8n=8Җup4G%p>䞵/rS3`$3wy89xݖ Ëba`ӌ# XaE,1:tGvlQ F7$KǏl{%Wj uEw|u*^?‘nTJsZ7a:v%mj`:$Ͼr&aTpD\q`F>]z L4^>t<[so%29;3Z`C :pS%{V<%|iQ|9p2F<w%.a:e{KYx#DnIJs8xN,ZylDZ0M lf-ͫ"ZǛYZ0a8S7P:QڗpC{c#ҜC0N8;*Ѿ|IrfhePv ݉6_4 zYLq\tQ1υLjN~\͘>a=0VFdmt̯2f!3lךr.ņsR'W|TX1+Uu!T1q^~Q[9q4,N! "P\3^MYBE.o_nl,dXӂ8F_`QtkmϦY5lթcG:vNKs@_Ψ?D2ȩf:`juh {CbM[ӧP~T?N!EF'@F`jTVBHCz?0}cg~ndDž3Ylɪ{R3nR:H&P*떞FZ};9UfӏN?hVCU?b伱S w`aI9eU|)b Oރ׋<%w -sJ?aպt]SyuFh:bx|$f}.U huhJofwN[!VѦN eBa!ɻCwiuQ`o ?ǵgmHc@D(Bp:Sv(h`%3$| 6%;xǠ 2};ju I%bB\+;DWf[/R򛾾h]N~NCytMWT B(cqmf/ 4s{P 4 ay^ڶNJӼu [}jE|¿wӰuF .,"ITP%UeXܨ"UuLu-`/ؒ8E5]}[<A)P "hE]˪G BpJ'Ф~ )_`H'AU)g54VF-RR 5\"ڄvy=NhW!%UrOڄXWXgo!rӋ´σ%.=Q[6W}u Xs"w⫓#cC#)h]5q@~>F6#@ʼ7i%J]#tud M]RY0o?HGTj(U64 scЋvxRBy)T(ibJI^-1"|T%_|тAMEKj$KȭSt+ae5yHLJKugEEB? Mٷ?OuB {Ayɽ(=S58,pMsyc}e"92mJXBu j#}2xt(EcQ~n]ꕃFs(?vܧk~τ1/BАRwV\ˁq.Kf=x26SioNV㉉q{aO*3` t4"C$rs )筄\ZmP+i~='hX1'L8=z;*.a N/鬌ʷ[яJGNҋr P[ evqW%Ҝ8WOo|\,^S1̊rc&thDFPeYӈ?S|BfQϙ>{/mWF~s!Eyct"7T׽y쾝Fwv9y(ݱ¬5SZ5:g^,t:-B(+aɜu2;뒏ʕs3qԤ*?Q]'Q=I>__δYUqH9-~:tƂ1 6[SU)<6ɋoCl`i {NoM@0PzPO ϱ.Z/VAv|~*̋2O+ t\ R__E&7c">`jx{؄gT?~fkNLeZeX0/lAeC1U;M_ 3Gk&UI_7] W܊'<|Eĝ8CV֭O_ D,%Dum 翩Av7O<LAeQI˧=S!*\oE=c8y"Ԩˋ};'Vl.Nw-w'Vɾ`Y&!6Z: L[/Br}&= "쯛S k%9=Q<~W h͉'8̉$+ ZaēZ3gy$i4Ƶ(ܨ2hagwGInXxoS#OK67\sRVڶbު1T3<6 c6UU)UG MiWsRۆ0#DcS ᱝ66D/5ԛD{e#oMG5hcuc@PBFD=yԨѡ ڄ6-U>tfMTl6URa?Z%G.$^&0y} ҕԪEikmpҕ6Aט1i#"زBVs 07(8xY0Vdih4fkc_9N`iiZm'Nk^~82@X3Tσ Xv(D%-P#:RQ3s}_V/NXZY= ~9ܓnaS>$ɎBCm$HhtXבkS~ DP0S}7G]q,CLb(LR% %{zX<~Y{|*Ԅ&-@yМ<͹XAEc7-sLV&4Rv.DvCE7 i@)Sy-eWoJcb_v.:zr#{)JqNitz1I1zc{2f%}3ÀD:XA5/ ]H֥ARTO{EsjT6IC28ΥHkʦ?3wTYQ/ i Mz3|'W&=ۿ}9/t=y(Qվ̳{R B :FPcbQlڊ7GrFs~>]+>F"o~oT8sUC]4xGU`//m}6rA6+E5d*34iW]&3s~S$B8~޸M-ͫbS]vqaiҬߪ׍u g~kmP~sPe~)lJФ0g.dJsZK~/~NG:˺uE8HP2;5SZ8!O $O9V'~= .e|&U4gkY65]u5ڻo_5f|joWzKTe, $|>~t:#'yLӰqJ +I %(x㬷D|X9,YV,Mـ`[_=ᇖ·k 3ܰ/OV0'ׯYZ44Q"6d"vSAh^~uKr)u=U@^pb $kRzQ(0eT|oAb^%Sf:ٶV;dnvL})'O q% =Ł^,G5.\7Zsh?eFrhiisGu6otκ/jai?}1OGe[0='Ȕ?^ocDp%L\KWeW.CkLS~7.j_UܷZ#*Uۼ{7vaMxjӗł(OCZPed_]ǁVKY Cb.[j\&enײ#"j:>.q+SvL7s1;ɡI<_ItytRO5Y$"L_zEit^"u)}\ S4]D+C10?cQAh6u" 5F #eYiDm_bӨSfikg?3+jov jvo SX~ceU~*Xos7UٯċBk-Z9qR]~ڭap SǗq{d#3kKaۛW)$ӘH*Ew]'/i9)q/Z0v||c9K`Tfhz9=~-cKsqJfpY桴z5c㣢F: aZQYR &yrV96,[Î662WV$/?lb~^ DGGs`n{9/>x#r!>̱#G8v(ǎCvԿPxf|z&m;۲mcxp..ZSSEٶ|Yl9zu}p{*xpe@!}Y%P}9?J>(ptKɕAN~4KڪӾ71xh[&9PbK-3o3+8Ĥѵ]*w*E1)?LUԗ{%1j|&?lӲg6gL*vه1N]ǖy!wcKJ2toвK" DSA \^Ƿe\/ҕzy<˫G*iՋ ;3CS,0KdZl |i8铿vN-u >)?3c_j"aQ=_ .("np.(ƂT1f8T|4 aFpjrdWMA [0P.wH2y{y#2z,e`9~3wgWgčݬބ5 H?טq|\OBlPI CE_2yw"4燮#H.RBR{M sS`*Ey)5Je"`6'mŦl[ŰmpؙZNh߂O u|4]yaȽhۙ$R2/x>SpɦDZ1,=E^'P3Ydǥ&4MȺ7;F%7\ grctҀza_.8!qT;0'OE:=n2=+iWǹJ_zy8v' π $Г`waQ}ǿ{#E cWl{7X{-ԨhhEQƖD#`Aiw7R H<[73;;;;o;6p+'E yu7[ v6`zfY;|]spHyfLj?gR|s)5Ϧ^mӘyBب"ytgj34.4#(Kqzvt%nZ֥x1KY͒Mjz;)*xM-?=q ;ץ9"7Œ @${|J߮=VL)SCs]a;ΏiYuvDuPќ2*BĭucJ)$5._xé|97)]P (mqVW!u3rg8O946<51bJ˵P1>3N'?}^bsЇ VL|1-FiI.ָ 6 s~-6T xia* FTm+YhTѓ R)4mQiӖBn64_/;c4%myP.籭]jxՎR!W^?▎[&.mIW?Kz3:2KЩ3T0Ykl)RSNI۱b.c=RaRұR9Ok[{3; 0򛩄N\+y3rpPN2iy Fmc4flAhM(\SJwk<ݴĘh.m2q7_NGgܙ5OĿqՇ1~zMYJz=%_lwSmc\-Z5P 0(M(fTwhl)Z0fJZgh' TU'U1%0ȭ촞t)^V%-k Kd@5 ڕ0Qkdֽha0@&LUc'S'#~Qm6O`/TT%͔qÂ5<kfi3nSZ" TMi乛}./6A&fw)vC[S?Gb}S^ ۟Pe c۔T~s;v1o [S `Gu1=r]|y"۸ 3X 6<֕ӥe)lƒ< %wre~fQo;0ep:Ίڭmf||؛z}rj7ahckkY G%t?T+2bOf+v.6õl-]ˠfE:9I;x|2>G?~#NaښyYoӸD˪)ѬE-cބEqu#35\`bL)fwRw[p>Ī~YdLދhY`%O~Ͻǯ&,]F3 ΘΊ9YLsIoFsYug 3(+JP/ycFnyMx[6DD_Ȇ) 5:LXFTfȂסv(A i\14P)Xs K6o} 5*;j}BWӰa{`ˬ{֡vf~wZl\(QVe%ҔRIqSiS/uF/J Oo3pLԟ?ghIVͲ_} 7mayi [v7J|rZiYczgb`aGb^,dI~~hgHbA 7X33ô#BTj>e@-ۖԽD BLc9{zLESj2cct5<+y3A*-]y|QVꭼxMClދ t.uojM2ƌ_;fYቛjhJTӈ7pZΪ1[ ah@~4A䶓6sC|%yK~%k3{!O8U>e\_h*`H6,߫b֔%|=x;q(Ҝiu rJ^%Go~Ƴ~ZI5TR^GqL̒qXҔ1cz5Lc. B)ŜB",fzG)Ctj[|2y[[v͗B%IN2*tqlR;~(M,_ϦEaLO#;I57oE+iPeo<ЉjSJ{3K9@w3[FL_9<~_BBR2ϖŔ[7m$8[ȑ#TZ5sRҦ>i3 sKJu{5} ʀ7oPHLOV1|O/θit1M?qbkwK'W.m0~%nJf-ڗ XFs$ދcJrg)53!Ƅ!^c1rt~&sdhgrt@>Ś395u]`3 N~) {vΊ/JJ4^I[&qVzKYNwfIf'+TPWR%wQj^_ͨFҎ~͛sax즁x7B`!׆Ӗ]cJrMhkrM)Ї҇xe\+$ r\~:5L+['@$މvn=ruo_@ tX/`#+'\Y."aH$91.䓾{ *B^+X#]KI,+cJrOiokrOӇ҇xe秗>Kfq&C8[]W%/ի O1S11" Rھl}@Gl&c^rwX-bA|o1$l,Ę>+c,%3c˘s7}5:q<%J$I$I$I$I$$I$I$I$I$)G$I$I$I$I$hrS$I$I$I$IMrJ$I$I$I$IANI$I$I$I$Ir49)I$I$I$I$IR?YԳ3NfuJ$I&8v,_Ei!ϵ$I i87XXX`Sck:MoYwfEsYb#r 芫C f]N%"S8k/Oĥ8P='Ia>O _Ȗ*'rl:*>?Drc9`fdyz̻V$I#61K\֏"Y,.ܥK[h4nQƜc֧XȠAW능* CS++ҰxH2Sbŋbmӊ%h!;[aB11jo,cj-q-[ws3轆?NUYg,L-p.׆)=n@Ck5}ΥO22"/%| ח6S {3,w&~®^m EQeKQt.KA{[jξW÷ V&&X+K[`]}gx)&Cr^ Fc1ZCSP掔h<66,)Vm鸛7~}knguJDFg@V`KkS,wZ{mv8on%Ƿ+vfƘZ磌O/͓xGl909ѭ6D%$̤Mu&Qw94/#s<4g_ +NȈ@rc (eC6$}=<

    DiH˜֩kexxq{&okA$zD Un|e;J~QP42O1u6OFAwW 6"bSk,ct*΅*',Y%Q w| HWog݃u_ʷ.ɴ)!MW2t(OI_ԗ ӴPLt=ZɜU~MZ͕(Ǡhў\3nĶ6h4ks| 'Ŏܹxsc0ѻc8ӽ34w+WCdu2^1OI^uՋDƜc֧Xd耲bN^Ԫ׈6=G4vc̑pb~Z,z YfFR*;,2I0MYyj!q7;aiPP"Bؒ ȯ+7J8'm}|y^6[:9i Te#ۙ '<5DăgQ}G1 @d&9}אj jc7n Xb-_E5T?eQzeS=.p.PBCtTQ b4n3rmĈRvc" scl4kZT>r,fӺy3d䏗K7-qشbū\z+S]'ǜf5LcR<$punK ?m9|͛[M]M:78QnKInMJLjYNNЇ$IRlaܲ}ܶſS}>ݡܵؠ Ktә֙MJoaGѨjB'6 ϙc^Ɯc֧Xd=RJͦ?o@ &B9{7K1=35_?Kijl tiP^G V `K3Μ{2lN f%yO 6G`ǼGAdΚLxЍ۽UB\J<{Xqx*\j] NRgT-gܾXCTtң?"JU[̩Θ3 ʄųYA'~溺0=>LJ0y3siW.KC"S_}ʰ&,'DED46k-ٴLmAvDe.7\a&^.cx$pB0_H!–N U9<(h0'䷙S^_< IP8l"/XB#w$0{-eZ!흂i)_:S8x4/(ybMn>QQ'VI<,<\Ҁ7nkB~ 碥)Z4NWِxrT\!^#19\~a˸qw5,U\?` PFKձ̬^+dcrJ^'HgV3Y}::jr3J{kf2ˮhw3O2gq룪([n*W[A~iaZvNE@AnztsYbޕ9w1 f, lg}^z#*2X6#9ϥK|!#x+q)nϺ@<)j8j'b݂%g85K]n;ގefXi,U2 IDAT4"o\Po7:&]݌|sFq?pcw{ @GЃ`ti۳^x0+&6$f?H0,x_Z|cvJ_<(kˊMs2+_ 1Z;[[lmmue1kvtksU'yAGI\ܨVӁ9/Œub펇< y\EJ3rs#!B$jn0[W|s %u}*<%~Y'sz-IRFIƒ-(NX&o' Ǡ2>upƣXaTD ū鎋 &&&+4A.c1SR"?^`]*IFV= I7Ũ񙱈]>X:ɴvCFIƨέ٦QSV[̆hg'8`Bˣ($3жpd}:M cwqdL!뵔 ǖ2lO2B c8QǏտH|s A辑#%PLp˂vLƜc֧X2wSrj?-@^ _.Bɢ N3c15Ôf0 ^KΌ܂Jj 6WsAN.ӓ1iƆc5KR-OT5/r49yj$?,(X^jo~}k~ӊ"IIQP)ؘğZ\Ȓ_1!|N䞦N&=qԸ)yy/Fu£]?4T9a{ej'']6r¦*Tweˑwj%}cwL2<UM8vGg'C['՝UXV,8r1~=P<:~+Jy&WIaj.KPQMzv+ΚųskX%h+9;{B۳;ʸ&\eJQGE[=Ͽݨj=ıRFW/>צi^Y\&O)$cNL*aI~v|Aoycg"S״ KPK{GgY&2>*С4o+޻VT>IgKUNZ3D P3sgd_,r,G?M-q ETnT,S;&~^0Xco=rΫU?MiQ) xkGYx5L~)}.}2K};*0;8> m+ʂS;^r|xZo+ɒmt˯"8e,3OQd``Sn_]eJXA-~Y7-a%޶||e$O.?U1?Nt=.>o ȭEyu ]y2vÍVݭ!Pnq_JbS8~ Dbִ8uي.ŧ_eAi G-/u3) ,ΔgKMូ;sf"՚yIH=R5CӻD,yξɳT7Z٣m&9ͰE㽤jP6 :SL%N0{a,o[x>1Ǯa&LP w ܵeLLeubN݉w-ViS;+o% >2|hEjMF7f2z:smH?G*%kU%vx$I(*&o?5>>2G3N,gՙ9&᦯*ug^Y$۫f]}ɲ2rg6ȩT*#sl]PNѵS2;Rpj/F2r :ox[5@e㭆5Mv*C%hcYP7${4(h7Ǭ!æQL>uL,ǨP>-GO6±` *ǡQƞcٽ=A'aS0l׌.x݉B I}t/x~</a)R 3~U57Xޱ6 C)^]V2cfp\?׋H6([׏.,2fLaTu'98) h:˯Obci*-M^#/!n1fgĚBC&웞—d[KH–eQ%my¾/MZ'7Τ ]{`='1rb|>q;V'گ:_0_3fB80|2A흔ejL<`[B== kS4Q_Y}=!R {9juGΈrH,GX{ԢQu)W8צ$I)Hg[<(_&̸9ʓ$Ii`XecUmTlO/[2; תmXxx6Km%˜~a [S|\2#41ɭ1SʎE6mN#G`mmM3>E!+3.vgNȜ}1'咽djy.[!f}1!}W(eY.Kg{?c Q)Ds?'x㕌rskF9u85jIfBĽRJСNBRx (žݻ?"vVfHI|ةøC^|$Iٓl$I$I"by%yg`@"*GQD?"#ECƜc֧XnOos4Ez%lsH$l$I$I8J)3ڬNMbzR wɘ3n!c֧XnέpM$IR$I$I >/4s:ɳ첗.Esvq1SRzd7H$I$I$I$I$NrJ$I$I$I$IANI$I$I$I$Ir49)I$I$I$I$IR&9%I$I$I$I$Iє[7mڀHw9rkkLL$I$I$I$I$IUЁ!^[ND:CJQ'~žݻQmTY;9u"WcN$%{чЇSCcBQ*\2̧y,c)֌rNBK$I$I$I$IANI$I$I$I$Ir49)I$I$I$I$IR&9%I$I$I$I$I $I$I$I$I$I9$I$I$I$I$)G$I$I$I$I$h3qE=;0DlVr<>Si$)a[;Q믢H҇VZIKOSGK5ew#mL1h87XXX`Scg:gaߙe̹;f}UJ<)x+5u9L[q' .>هoL/5VJV'FxJO=$TR96vcml"ُ032+-R»+χx}ym#~>Ű73"o)|l)6ڙމ-[]'_VwY Rs ÷ V&&X+K[`]}gx)hU$댱n4\'߅*05wD켕F `nUt[x:Q'[_buڸۙafA":K睼^=[;){áy1(Ӌ$a$ehQz,[5 jEt v ~9-3iSDeI]K55*Wd,NJ>,)2":6ƨoϜB3)&-F1; M45wc>_Kaܝ-3t&9wǬOJiAW[:^<~DtAl_'K߻m 'f/lkiNǦ {Sͪ"_4N6 7J.j]"5yҕќٶEҡ7F~fݞZ?i)e)d g"1# c[ïחm >~rS?<7aVy/W *EeC!<9Ƅ]Xۀ>&s]14f싔?MeyIs{8&"p!byDI߈<&4\D?k}qU"u2ɻ1IJ>ѻHAɛOJܨXVO7fbVS3_&47ffܿI]hڸ"Јjo^F¸Jz1bha3ld">+dio[; Z1-JƜ# ^wBYh YLXWsn@=0*1V;6A".u&ҤV=yg8F!L_>N­^QEpm= V01mX{xPo6~8?lvPڎ_8 0_Md=†A935}6}˘sw9'UJ h=D;gfќ<2g}zҢ}|&!߶忤]8ĩΚLxЍ۽UB\J<{Xqx*\j] NRgT-gܾXCTt]/*-rgLjue,k?s]]N[<9FҴ P¡zƩ`eX""CgΎĵb lb _Jk"_2ZqUԘYZ`:f]6 e iR|!A[:%xV刳KP\pJfNy0$'A!h J8b z.(j>?(Mj4w |#NW`,auN'7DEʞI 'K tM\4q[\K&*Os㑊+9C̟w_2z\] %6s;|EXђFu,3+D׊?\cV?Uy*ҲcS*:-Ŕ 3咱}V(zVNèFĞ k3̓2Yt+|ʁzupu:ט&xm?O%pjGJ ^yڭ7A1)V]Y{spk(hi{LhP0v6Ůg:"3讦!,"=xзA۸cϩء pS<)j8j'b݂%g85K]n;ގefXi,4"o\Po7:&]݌|sFq?pcw{ @GЃ`ti۳^x0+&6$f?HpD<ƌ{7<~yQf2d2wv--:ۘ5Wi;:94yI]I(ns! C{:?6Xr>Sp'!O>뜾HiFnn$@PZͭ_fWon 䡮OT3C^K9U6 P O,#lAqŢ 4g~;yt_? T7 K#5 z'Z .^$tOw\LM011ԅ^ z@.vsYb//p AUPӞ MbX.w,_dZ!OOq^*Lփ;cxoiSCxU&qˆ3Fm, IDATpd}:M cwqNt)h6nkZ)?Z89<'(${L#m 'sZ!<$DB∳!ΎhCzfݣ` qʛ'FtBjLfɎP :z\=:bsб%)ͻ D\u-͋%E_9w[;J8ߏ@Mf|:Bk)&OZk\2l ŪS7D?V"q}jCC:t:@ or"ܲ`9wǬOJoe Ԍ)lZ` $z%*l:u:ď' S^˛4y/;3rpG7bq5W2oK}lT/yUc fu\Oѧ>9(TmQT 6&6V8$iH!S/IO5neJb}Ѵ2tq(`hsy:obO) p& UNh`^II;wM⣰ʦ:]cr$᝚b hoS+!xzUx1YxɣIugU(4_'RUR/>)9}aT݊f,nV ڊpΟP7)r*tj_usYxΗ)UoG)oS?Ftq8q":Q ϵiZ磼Wk)mBˇsvbRa$S}lD @E^OOks 43;I e0G] _;R<21)V)Nrп8k_~޶?=I=[BtԚ9]&n)`8FCr,G?M-q ETnT,S;&~^0Xco=rΫU&Fͥ%YD9杖g~Oųv8-ɟZy)^{L o:?E| Bp2W0)@Q7{x/sٮ2\ Ζ o[>2E|n'oaꟈx Iesj ֞]gmv7VƼ.䏼mV;. ֐~C(/%1y)?MK{"1k lGuKӯ q썴\nP˺aPJxg3pO93jMW<${JeÚ]"il[bq[C*҂*j 7}fѽl=K&44aĄ֙l^%h>f]Ci1K 6w͞Y|F+7b9\ ClWWah=Glj-e^ɟ=(r?{eyN׳93}v Vlĸ_FiAcE[^)_eqw0E'& !fMXT>,sFP[;~ ؐgˤƸjt9?fKS/o~#OΦf1#m_!7|IL^9;- 8p%n&iyV\ʆO7[C~i<&t9g%Z$Mnˬ4n3}ʄM7ߙ[.U)iFX;OP~8-OiDQ\}r[ɐ?2f |z4ݦq Oij[P҃7 AQ8>wLN}6$*MJqQ25]Qƚ*#yO[j@&9C[SwLgN vfEk;`qh銟wjD*5/1BLQI~m۲QTa-sۈ[Ǎ:ߐ$)|A-jPOqI_Rxd.ʊgBexUɔN:.)++i<6b[k0I,rSGqm%BY$3ǜbM)l)ZLF6FITdDEKdxHXH=}"!铇Hm٘t2FȪK$Ϭdo:: |Wi[P~Yld~+5&](YVVEQA?'u\$!rzk.:5 ]!q% R1~SBJǜbU&x36JK^ P%meNQEIT3}REI+:NirZ#q>%bNR.((((dhjSQEQEQEQEQ M r*((((ANEQEQEQEQEQ245ȩ(((((JY|l $ik.S1Y(((($lZZX"K[ A_Z'?4֭]GR*9C2u|5b̈qI_ 1LV?+Ĝb/+ībTҊ:.)KgˊybܲR)Ё}i4+((((ANEQEQEQEQEQ245ȩ(((((J9EQEQEQEQE (((((TEQEQEQEQ%CS((((dhYg30>|q:%( `??ÔiQסZEXRc,;ua+f̺1?I봤salzr*0q8;8K aN4=/d{dMCv{T1gRJrANQ g1bL%w^Cw9c3Xٶ|3g$o;EQ<$`7, U̙;)4yq[j:ݧ< ;}#dpG"v$mGr*L|n4`{ 7 Em7h~Y^cg#b]&/nvztvn)UFlzܝИ^l\Slfn 5ٻm'&: wO[Òtz} ﻘ?^U+hc}>Om[o00"O6WO#7N668,E~˹3hɬ:V^iLz]V`Y4k/*I,$C_R'lSmkX{[TĿ/!߽>nvعR M Hk,mi W^K96[;%})=/7;9)י6\&~$8_bKww>liti=cxZNp$/OejCooWU,Ên)8kk"dw0`뚗 Gգo"޷$9]mj]0}RzҊ…)iG[.Ϧ{&o4o>+C_:eKB) tvdLl'+z*n ^Ls[f>krP/b1gXI- O'XoVl)7ΰo|ԚǏײax \4eGdڪ ]%i)z⛝{l,\֋.?dĆ/4rAI&5yaǝiѧL1W~`P @;qsqF~ƹ86/}[/>z0a'ܳՠP3f ,Y>sЅԳyO?bwQ,ǝM NC31KSBt%Ưu!max݀nGɋ`c>'x ,.̃|9821\B-oShtdѥV+_-9Bg˭U8A_`h1%i :4_Pǐɭ;V4iF'lgmSA3P]0y@OGsza}~ -{cƈ؇oԺmf8ktvOK4~v Ynbňg$M6̯L^7s?6b"[9D(S2s^o+|=bu^Wfzffw;Y5_58<&%pewVGJR<_|&8ޗ*JN(-z}QX۱o#~/66'IJ[JI,F˽ ҥ`ij`,7W"b̾w|rk˂{ xhWU&_1'w1ߖŭ`rǔ$o[5^ q_i ~ɢljD~$^a%?.N5ycbyEʠB:)Ӆ R$-E,cq0TIvD6t!Dt̹Y$Mލ-8Ԍwg}{gْ2^9 3>;o,ArdV7S]l U \~AP[m$WѮ&:غvBۋw%_ٶ2rJ_ KbDF15Fhuh_}#6l1-et64^&2ߴw[wm ?c[KlT}\>g,ll~{9~͒Q b_u!G0߳뉝{qmH7(+=Dom|Ȳ1!͊zZt9DAw]]SO'Y)wyi3v܍O&}PE؈[גnu\r˿kS^''^R\ZPvb"~V_Q\m[ɲP9kƚR,&SmP  ` {DB'%4l۲1YeU˗H8Suȗ&+$$ au;رaΰ"GFy8wle`Yh)|W-hesv9#1e_ėX7vn\[J97֗1_cA_Ȃ%c-iG O1YWx-K.1˃@D9&^kcS6} rᚣ0ȳuVu>[|:.Ef-.|4&21UyhuҠNX֣^F4p(/7™Z *au0GZ9)__g]?JYNp<%nb"*2/ ywo1pn/2̼eCvM0Ɖ*˳'Ycж31ҩ+<Ѹ`Ωs;wc^'Ƕa6>aFMn[i&,g_0:åWMEv=u۹-&Ӿ!b2궑kVeإurRNW^#6AeI/#&Bb圵\_eG,wWҵA0em~s/J0bl_?޾KXïdo yi插ip?K=:/XbD ز1lWV̌{u}sP1gRJBR`Tܚŗ/r >&8KEpbPJxКY^Sk\D }(P] ry!`iO+Lt\Sy:k&P4pTӺGn^ Ձ+ LSgZ0 VZ0wGoJXhVӷ Sk-F"YJ2CvçORhp^.XSבt{{?Ԗ/G`o1cH=VJh:/ .Gޞf 7ࣱڗ~)mxJX%Ӯ}\̖X4~1Og3m"{xu KR3ޚZmvkx|7( ޽`4xo;8Ft7U.k"ٵ},O`ƕR mNmq5C.N@l}Fe. K\ Rb9Q/^䮥4vx*Ag-1+W/sU:׆XCǻ~~^5~WǛE0;μ32MYS9ovLfhS~XeVqyʤ&W> bbʎR 0sI-?@EcWp`f#4C3ZUIr? _T_ uk^_#58D~n:qBFԍۭM["yogÜ1u;;5Ev}*Xu `o Fo c[2W7~ԫea'#ߓb Sl<=g@ӢX]VLZHP92]4@< Cxء .g3n+]mbn0=%NFCj4jP=PB$G}ݺx IDATO_BMV-bwZ gYYu/hV͟6u_qMcnׯEPI&MG~1}ԗWoR1gRʿmOE[@K2}D2rs![άyd!2"zBP`2UlFnfOIkWNR~g7иSǯ,+'ɋ:ۤU376A-f3xdǺ0vvgNs& .ǥai4Dro- 666Qta8%3k| @>Az%']nW%<݉!R4.ёD&U t' Ν#Ъ5+C>#+9ԣ!_}S"x#޶֛.1ݽ='aUA׭S5.)/!T,w7us$fpjRsٴE5½'۪"~pq_U[:ɍx919sǜbUNrw녉&Lf IOM{l?2M_OO:m6_|\->}P ?uD3YŊ* 7Ĉ\::ǿ{707ΏZLFL4:6ր֙<%*DjOk.v^L[RV̩򅈯C+O/ks702`ykQO {'4s9+Ū#u[y¡qX07vn Ь RkdaKO5qi|2fT+]ʐbItC oMkh39˹vl0:WUn\Z&hpn-gӧ9}4*z\[O(PLAs+_>w(c&:İ&bGȩ,C]>|s[&.iOEN xf}*RF jԨAj%z3ac*t)P*UFslg2_ÞTQ:ը=7c.P^Z+ՠٵAl\o7g5e^)ugRdSߥB_;!t&~ }=uOú$ZK ymB6Of0kqz̡W 8=M6K*XtIoS&L%&0`RLaz뎅(R?¹qIEXŪ?whQn'9p$i.O:7~{k1ڔ-Qr `y#Tؔ/ 狉Zr+G. ְ9{M`+ωb{7sƜbUbLNyr#qgٻb>?}J/1/li~Lj?6X3]IXL;KS&W O"u|ɢ]sӧFNL<4fu?sF'*ytiG/ViX{ӦZܬ#nU:+8#>MwzDrqW9L~8RAtAx pq6vEUg%s. =75O{ V.)?zלqHqo'x'EкLlaޅeNXʜ;?_r%WmYsd׋< <ʲS95ck,qz'7d~ޝb!9[Kl΄l?ퟜ>~Mo²ql,IX=Ƴ0 VHdz8_Nk(B1A {:5ٷ6 }F|\1FNt.loCZ[j1 Ngg3`Yft5h v_|0lPC0q.wJZ1Pql(*\7TϏ}_Y3kx Ş:#fcOk6ʅ^Ӫnp]:F@D} g@ӁQŁ[LfZ+|ߓ FQ^cŨ/՗q9O|%yy'Ap[;l9 t@Ҽ+ޥog:VuJO9jٕݒl^ QиDN3Ru՘g{'j6X'ݛt{Jcܴ@j̧un̛0eu~lwwdJ y_ֹ&y"{Z|=b9&Woj&%IS_5^mŜyu&vGvxZl[vyA%Src]] =%~y>̡T}b~>'҆9N9+Ū@ rjq@y6ןVowAxADHgtݢWV ߱Ucȼ\3fcYf4i|#&G4d*A<1;Ӓc3l=gi4y<߮AIADjqO )}EFl\vhƴpk\ TML|34<{^ K1<?}=7g`FeV46]dλEiz[xS$6BЅ=̚6#vM:}X4u :Lܲ}Q n[&5U+503^z5zq?}yzw6}71mN ᫗XOgWiQr#q4IC§R6~Jih@ޚOw5XɛĪחk1c4j39W5n3}ʄM7ߙ[.U)iFX;OP~8-OiDQ\}r[I 崢gf!*OwGmJru )=x#b4oM_sǔWn(+w %h64_|S-ngmkOCعz[},;=X1jdf|aZ\rVtx ld&/+WI;3bTJFlco_dUn+8j&u;ahoVOK2nyYz ͇~3 vfEk;`qh銟7/uk}1ޘe1hX%kWrٔt0uPgzjot<̐CX,!EԚ,EW>;6kP;VnY,SF4H+*5/1BLQI~m۲QTa-sۈ[Ǎ:ߐ$_:b̈qI_Rx.]ȊgBexUP5njiT&KޒצLdzcerF9n^Y|B1㙕MG1ߜYso;V:UR%KP(yDqdZnr?ir]o"(ge'1~rAe=<{ ۿ_!N:|'D=|S_s9+Ū$M zZ|@ٞEf/ngJ*_SEQ2CXwR*=]Uf${4YǮrq$֮(p(&TRA/ teFqJrC}HV)_s9+Ū$MwIx36(Nw(J&bS7":<6_ZCtT|NGۯ'}JQŜ\Y)V%93EQEQEQEQE (((((TEQEQEQEQ%CS((((dhjSQEQEQEQEQ Mji٪$y]v윊REQEQEQEQ%f j"ADX߂EE,Z:ɧѰnZ>TRIw:レcFKGVebY!c|Y!^VqIY*?S_Vcs斕bM)K$ \]QEQEQEQEQ ?TEQEQEQEQ%<@Ј$1TEQEQEQEQL"}vmߊUpS r*((((J@>&#S39EQEQEQEQE (((((TEQEQEQEQ%C:;;)QEIsa N*Jƒc؉[1e֍Č,\_և&56@@٧UAI"?/TSRQȊ>lX_b1gXxÃUף$I6bL%w^Cw9c3Xٶ|3g$o;EQ<$`7,n֍x̩o@$EƘF)=xL^S>; :W-6N( 6^7xDxo(m;1p{d5,IN' 9(Zߞxx?ǯ0lpQ#%_6Զ56Ӯ^)dsċ8mKydcsR4규KdVC+/zLos.O,j|õ]$ġS))V摗WЯa9tħpa xڑKi^4ߣGMx_@yI,疿^ˀpND%um肮`ǧ>XG@ |@4?I3РW`U(;6ކ mM6qsq7}R/ ?xc1Egb3wY)V%iRj*֛3i 3[1>l^zcQ/*Htgtf'[ 79 juvIMbqg|)Sd 8AXnx!WΠe;MdۥE/ ;1-m.}d1k`1ӧ5.̦- |Kb>-Ctuc'/Vx='3fNT#r !8Ċr0wL%ёGZYz|ZEs+ey_S C3u 5} iډܺ3iEcaftr:Ʈy}lL=4s q;6OgnQ)G诌o߲ѸG?fȏ} I+ifLg4Ag-Vxz6iMk~p1Oc#6&dGU\q-Ϝ徥9cccynjΗ?[X@tڔX^dq|9y9uRXQo.~0GΞً|@/l)OeD>}$a2XM]zdV4Xͦ[X^u׌t/^ g`΁yɕ&xDȔ1Gx׶ΣqDWí{_xCxpkOM|{|.|Ӛf_ܠ'3ϯ\:+.d#k;<'5ӵ Q1gRJV-_"&c“ڶe /%-lD_k0{D^QIOED$jkf!S}$wNN5|V':g*TI#""uux|MDteT!~"Dz NtqWM:<&-I /olՑI.N$zes#J^_Tzx D6_,U.Ίh)wBX{K3GޯYRy8Zl\KN3f{v=s{OV=NzMIe%d IDAT Yv"s3YQ/q2X>h4H]7>KzJ2>d#:k8./mɤH>;xKZR ~MKqI/'uy˲ n>Rײ^܇b(:T~ k[-246^Uy7Cӯ]#I;'k)PWltvaMFB_~zQ6%m\bSv}"OZ/&RdqN3kS^''^,x *r0<6b?QۥNm ,O~f6y{g%D"JoRV-(6Ⓑ|@Mն,K楱9sǜbM)m/ʝ[7uUٶec!/Խq/y'MV,IH FF#8Յvcڹaס+D$6XNq9Ν:8R[Z~s !3懅cHl.<}B03eӪP9J;ٿ~ Bt,.;fBocΏ+A- > d劻K\Y,LpRF, =<{1,`pV.7\svyN .^Q6^x~KuXG֣LÐcŅ}BDf*-s/U)v+zр߈&Sf^8SA%] !8#=kG))y$2hB^!>[ ~oL3/yu 5'F ,a/Ϟf]AĴK~CD҂9q9V:׆4/'҆979o ؃k۲~,PN^M6QٵMnn,LRԋ3w~ [G&o5$/Ǟs'Y)v ޴cK$WOcG*T)RbR\6rJl^tP$x5{h$_f$(&Bb圵\_eG,wWҵA0em~s/J0DkI\>ՉD ߯OU&NXŖ5a bf5ޫ !xwj;'Fb./Zm|9sǜbUrrT*ne@VO˥C8d(%uQ{V.bU GR'J& Z W)օjPc"f̣[ٞs'KS/wm)] :K77O-cq^#qϔ̜\@'Bc޴onqʒλ1'6hX~~޿ 7M`uXMΡP-ewk?ֵuB̓iUgF-mVJIY4Z}Ѿ +P.}Qztt;z-8'(y6ZNS; m'WeؼmnGFSZ&Z[;orӱ+Yj_r,}OQwA.^#kfyܕmɟftqi N{кΥ'64LܣL;\ӎk3eAQS'&p\K,% mn^7dIVOakeN¡zd3Ҍ6]^vXƗZ"MP IUGi_`\+~y(eQ7C%sd 60d QIf x0zWiwbQUbz-^̬=7q_X&it}t!/"y81Vg'5qizxt_7+0x΁i!C,}ϓr`{<}MLym2H곶K4;cia61KZn]3t-`cDڟ- ;N IfqϿl`u.RFnVC|U:p <@BAW7Os.,Ҿ䚯¦,5Zqoi_ΧߛqH\1 8;rh&jAhQ-5ќ=zL[{QNogN=,(c qvvjԙzx RR}}} qHG< Mvѩb<@ 8cQƟQ䉼!M=pc=&#&*DKaG}b?#LŋU.EЩըb&JeHΝ aʞ+eO>[Y6 S]UO؞̻^~+clf)/yhE.Zv򟅬5чQPOQ@{,ڎqs~!)9#7mc{l4^2/K Wyt}jst|=j1XX'|dKK驿.7|13)O+\+a@3䂣OE^$ls{ l=wE!O)˜eN|ן~sWu&w45jRbH$7<]ѕ/3a&} bUr A 'Kjb%<71 s {Q9< tr=MժQS˳#8zb%ŒJXi*UIX|PJ&\nysn+MuP80"WLξ23p$ƍT "p5ĦHɃ9wx\ y w@-fC:xo:A{ji&{y%<(3p+f%jή^\Mtpj:ԱF IpT 3-z9M8" lUamg&'ϴ`>{JVmY*@2ɇkM xɎ?ŢD[-WOe$+'9k heDj0쇾7a.2Nk2 jw6Mgo_bmR5Tnx[G.b-`"Ru-4#87׏޻.Y,cCPaci™~Ɨ TX\[^_CǥwZ-(eS|t!9mӷ)ULlXwIL1rhR9X`ٓKS{/r)AAKh3,4H'ݮ| 㶼B|Z_C  ɲ>;PaD::8(L1щ&<,Y5AKqM*3{5 /~@6ي\z!q}N{/<-:zq7dlJυc33 p~d/tB萧2vZ#]J8d@,s֖9;*;<a0u/вSu-BSHa;Gd2 1 a 8ěK*,JҥgصjR8]ϓz/3NE~EnfMήI5W.q!( gdy],qNxBD22.~ŏ)'?kE_SPFx6n#4_ U~jnmK ۙR3P|fPOOO<+>_S_f?Q2).M]xľ#e=KBgYn7>#O)P;&e=)A9gsA*EŲI{!P Ιmr'Aל6aM&^yUuˌIaJGwJr9- %Ϣ-JϹ[")=u{8qb@-ZͿ`4*T}7L;v"ɴFeUaJeL̀l9}Jz1Wp>%ݝ%t[ k՜bsӢI}o*0ĄC %@El-K{L# aWDdMƙs8w%mml l)硄;%\pH" HɒtoĤS/[ N={7R:s:ſdׅnn֜ȫ'9Hrt%_Z9J\)yt :d ve?5eNĒQ5vCyˋ.p`xLIb-E~Zj̔h1m KOBI?CaSR#Y8m+bv"p/ޟ6"=sa_򐌭2@Yv \~:؄q5*pW/U(2мw\\?%~K8nЊŋzdClPmqρ dSg@'W AH>(5[# <vBWy27[Z;]شT\e+ܺ3{W1wDUŮn\Rs6NUl')lY;+92RFZ=hPRrV7GiVDaض]AAqW+] WWO`[Tq\,yuC&8n"87tKd@|~m=SסT}+/tvl=(6i?:^zT=L/1p;ӿ ~ց1 Ȝ)*\|`P:LѯRV!)kJLp˕<="'`$<'6cA}f6IG@?1%ThaO~vVsaz7fNDM$MɢriX:b۱{{] @/N@ i O3"a^yKy(-b`7@Y˵"}kdDqjF͈#(sԗJ@aU#r$MH#R)Aȫ8. RttsӀd}:I qzv[5ܱ>#֮fɡєeW_3L j\% lғMU+B.me梻UGL9+K>m[ypTы!>գ%&% eK&FR"-gjI4k-ɋ|<F]ɰCdM2nANu5?FXճ5~GzCyd$a|>;_ bЬ Y=QB'[U5[?av=w۽5U# ̰+FFa k̅q;~I,>ɪ&D\7NS0"נpZ4X-[,U{ƯGMRZ'XSK7we  a®1)K1e%f^/S<3},yJT͚߇S2N'Us%}0"j;Bz͕9M(='Ayu=q}y;/ρk Fn=d~ԷrScC&겈_#*MCч3Q:lD*ǏLc :=D[[!J4}ԽL ?vLk,o |0$4'0sD &JeEj[Muk3'(LbtHɘKқ.%oc NQ0so''צzM m׸u̾\nzPz~ȭ@Qކl]J$aTЎ3Fqǧ LS'DXjM}xƖ_CV_yK3:ZXғ+W1Esec25L|F܈E"V t zיNF# k)@u13)S:$ckFMb.ѶF5:{30JFsyǵHv#@A).A>t^sSցdKau&}Ԥʂ-"7w{[9_freZE%qHXb3\%\ˑ &qt:^;3{gʵ1=(`2\7G8(@53W|5dr!5a(֔ Zٷٞҿx…1.VG|쿆0iD9*svUiq&'}033|J_"?dxu>uCEv7hOƻkܐd!+˜dM/مCmLѠ+/K$6o >< 1"Tsut~?<ϾJ222dn-8ed"5!OgBF H YXBW9K˜dIN&U-/<[2lzLEw22222Yf)F2228ҹ3IqDlH~;;_Gx:&˜~M[$L`D]i<;c!###Ց흌LB*~Eet)ddd҄ ߛ}3cv m̙9;*9d ddddddddddddddddddddRGr|AN9)############*` IDAT######]#9eddddddddddddddddddkkVMH7ۇW,&"104HZ\|\$y&=W^t?z8KCd.2Dv?;ȜdLHvWQ&K"u,˜N"dru9)##################]#9eddddddddddddddddddk w䔑22222222222222222225rSFFFFFFFFFFFFFFFFFF&9#1c &$".̗ 22fE `A>FT_-ŗ=1)Coȷ\&cllyI\dt>VXŕت we2g'Ye>o<[+0|;kYƕơ^ A{wp |]'###e|/Xt reG|P(PbSНF8GIz9\{)mLU؉-ѓ@s%?5ʢzɞeaR@Gs”kЛG?iGG8](3"_ o~D5Wm{_.F)لӶs=Ü˃C)kĦh/]k'>z9qڝ?O<#2z8aeq΢oRx۵43P I҇AS|zM# M䵲kzׄgojT_\% ף\}z?CǎF)uXt4*IN9Gl^S lp5 >ʣn g 0[v309AbAJ4ґ=q!"Dzgs]k.Mϒ;bN&su=S{P5z墸O'~zIn߹Rlp`B~.gLu]h:wDRf8i"{vVX5Ʊl}z?Ew%v/ #v;rp@ym1舮}e t-jB_ D4MǪa`MG~VLH^Htr!sgl_sZ+YG}r{{X>=fUCHN'8樜s"53 } *QO@T?~4Uc$>FҫqO/ENF/AS z*q،{fȄoE~GG @7z/rSz|ZW@|g8')hgXY[$LHіW!xW4VspzW%#7u'撄Y)Dtou^w=8bd<ÛGcn߇0aOqd9S.]OFgԙAeD=Ɖ,܄E]h[PM}sz"ZAJFE ZWX3n8))|Տ ^l]_r vFܙᦜ[ԀIx+^+q:zM(olܖE1i9`>t |=&!~ہΜ]*ׄם,hckd$VX邎#]WR}硨]d,hJ:-woReߌ+qhlOM8ȐXfٍ)kbza.4xƦu_As6RQw`F hq$} L!f9+vQdJӲl=c<1@} JkDw꾟B<|Kz5Y;5U3T SdI3|~Ì$gs24,׆/^Pcp+` *:+,>&^I0ҭ(!zP{$x8G jDQC$wy$V4:oԃZNp掘+)?2:@/`(ŽLr%Y46k90TX%:mmwBIh ^ROS LlmS cbۦUV<^XG{%hڂOB}qpյڙ/wBYe*EbuŠMm_GEbańA)1HriűPYXx+{ϊq'Zܝ%tp\5_gXw̨BWsBh?*r=#aWuU&I\#"Omy,J% r8_ 2&lZ(kn.  -#֝Ů:G>ŨݢJ =%ڿ9Ո Ābzq1\7=~ƈSCҨؔ6w/BZ ., Ar׿xG?b/UBed+ʏ=)?maE}}pEs~hU\6zJзp:_~j%4_UahJM{Wn]U ?ϴB}qhb'LBeS3HlYWUKByEēӃbE~s}kl/Jծ$ )ϟOq^6&zļG : +e.QFy`'tDCPGnmL0UR®p!a_[,z[>+F7%GۺcŅ yҾbQH)1ɜ3CxDׅZ"Sč0K:: }GtU ^4^zW/fr@_OZ}{Ѿ R[5zg?)m7ܣ6ʉ6ų:^X4#dež;;bm4H~Ju'$3SJˢsO>yQop [oJhp2 SwQQD%E)HW_nt?c1wCQ,a! hMAhdv~g7v.]v"7bً\x 㨔I3>ƢŗЯ3ߪHfj4}Rs?y]U:z]M5ﮱoQg\Kz_OV4<Տe;LNidywpF\9ænS9DRs4BM(]Hz-Ig;g[Bftq*]UIp='D2:~ޙ-fLymѓ!@v.Xg}c&55ixOXˮfb4[>ټ6:-arkb*{X=rH(+eb `9?/#vK~-/. Y ~]=709cœeLmRG,i7k#lC"*~5id>cy&wcF/ƶSZW骉!DxϣM>U_x3n dDTdȄ䒞a5vn\S"d`"]0\!|L(d9w66M%݊O'=ƏԟBo'=7aN/-?Vu4kciј+~Y[$LR|}ȍEq(o" qeW /+eJ7Ub(Ŋ&8C_9wAz?mP2M>> htl7̫n|@ an>6+NNRTCѯk'#cӺXHI%B8o'Ѣ2*RP'`/lܹMԻ4-xdh!~`t,a'pUY?]gwvx9>^CBTDCegΉn̩:]b\SGP:=PG;m3%)141F7^f=N\~ hRa' J&XSF"I0> G6 msb*n88OQF9QZ^$GPh@?HH4; i][ugsMtb׹\s7L<(SD ׮D[!vQ}u~t,WuW*מ)(\ЧDaԓ>ĪjyLn|ηK/VhHI)4j.y%LxPL3/%\N`WU=^=5]$g{3"̏]ΐkzS*sFO MaNGv.B5C2Sz&IWkvL邏kEOC{lm4Pv{%_hVX}-jh+ofHMz|:g~Mݳl?EX%x^vYҾsW&y%,8A6(8И0q3~p5O;͔6'GM~vbq-. K5yѮ3߬"2gm2֘˜<Cfm3PT,ͱ*Љ/DEK׍ee\A8yx; q}X5La3 -:MPPoTZꭡW7d/7g2|S($Օ| 3,Luq:V>a{3zy9EhҾ*uLhٽ>DGA=zF9[.h;> |::ztJ ޴kɼ,}7\pvŤca'iB̥_FⲦr.0 Kr'; g/F=q;ޢ~ݔe2g'Ye>s9ÉKj:;dFBL{1$J0>1y{r*|7éh'Wqz.-NqX:iZ^p,IViTpi* տ+ݵmOC%qGhEI%0#o-VJUlyR)387 n=Ws|xk#\|Ų-Z-D2FZh:/aGpn{w5\Xuzø3 /!esZ-(eS|t!9mӷ)ULlXwIL15hR9X`ٓKS{/r)AAKhr)5_]!!2Qw1&&e}vw!݉NutpP EbMxX$$j,TgGWkt^6*l 1C[Dg -h.˻/*^y>[(0u(o: f"g$S/-ʽ/~!OeGqȀmFdIV|1{(,Dw%dbWq&73T(YK(qզs8]WjG/!k"Q,J:^JjE"aO_ą :uh<~Nz?QIB! b??Vb??fw`ޟ/^JYm+Wyʏc@͍miԔa;S p&@!P¬`*zzgkV5R&ťP|1wP;5g)Tp,-^qdI4*a'%b\JQͤ.{!P Ιmr'Aל6aM&^yUuˌItti(G#ޒ ^r,ڂL;k%ҞSg'Ԣ J3BJǚˤ9c'Lkt^ZTqēdكieY4$+kRgg?<clK^4eg(i / IDATXRsAK~4'ؽ͖T>Ih?c0Lȳ{8̈R3$/&<7bd(*JOgkYz0gfal  O&*%+nR6Μ;ǹS,ikeK8`L8%q-CN(EJ{#&wJprܸ5<޻Mbo1Cu 5?`7?IKPUk1%n@AlSd>GIVXҵOa7 9c]wL}Gr.ooL+1 RO5B2 r#'.\"F$%D_Eٹn>`8wψYrh4Y)ө7WdICۆ_ӪxtJ?K[.FQ:SR|~V?UybrzhII}9CY҃}ч?EpZ u"a$O>ĪQ|2l,s3Oe,ҼL.8U hJQNҰW%uL܃*IdHXaנTZHY Īp2O;l!bI4aԱ+Bf4?<GkdC2uk }kmYv8W@zt25ϯL_nQb(4}MP&3}2ncA+'m'۲_1C}Q`M'4] ADmg/,6"O&>It+2j B̠wkt1ѽ&lt]w1Ø23A2&ojtL/mxb\c 8 m>}i;MX#Nc?S:oKZ*k{{ٸ'U\Uhl&ػ(>=,`.D=kI4Q5^bo(($B p73<ݛ웦L񌽿YѪ] jEl\348 tϴ3lGcbe­ 30^bnJկľ`23pTw"| :c3-nC?0%ReDEfɫn a S'L*ɘԘsSʦkg֟4'C077Zi"816FnNfwϜ:>e!!H%]ﲄn97ĘXnWƘĜF ݴ9ό*ule~)8%3G3|VzwWڇ4d _}ŒW2f1X{q,℡Qi5BpU<<<9u@QضukO}i#.Q9?a\;'h-̫$IR ;I$)xƭOlWƜcMJZb́|E-1th^5/$IJ-YI$I9H)6l{KvJwٕO)xR~; zzf-(lgCk2nZHsSRd)T"Nz.dH$;YI$I9;KEev*rRңHiDvJ-ʘ97*},m=Ixy<YSe'ZLm0̷{HnH}k1W99Zk%I$)+h_]r{}Eu2;M2i??|7Dkbݢ9gǜb>E5rbwoLuPTaT]DZ;Ij¸TIݦSsXaK hjF(>FwYX[xοNbS;0z.#9hEҏkdi3ut+YgFS\^@ˣóհ4 P䣸{~:F5^&DAs5jS{4W70)LPPLMKIBy0Tb[ܝ>\ ,яF{EyQY7ٱ<{Zx1YaOhT^X6)-D?kH:P^ jμ6NDBfPal4tBUl7Eakhih@_CȨӿb"8ٙiO,sΎ97*N嵖!.1Opd&~%X 'y*ݧp*Ie3NQzb1I~hy!BF^,t8 MHO>־MKM[ز:tw2Jj g"Q}Ǐ[k?[c8Hy&hˏٽsx[$_rphclPaΝ27Ѧ= oj-7aA#-[1CVkØ7? "Ys>~]| AsUa kaTٛjӖ9VHǙ^k9wܛm#bjJu>Wr+v׮Z5Ǟ@_پa*Jc9К~QͻeCoΊՂ݋+Y gϨ郎#Uc{ aM[:&f>jS4^>׋tׇnU^.Ǹn p^(bQ,W 0 ϣ1mَ vBYu3XΏ?%I>Y tc<5u9gMJIIe܉*5ݨU`Y ;3KꉵA-Ihxl.j%>ywJIGy<7D m 4T^uf\Ǽ`X?_jbD`".xϚ6s'o碘E@ͭߙ%F`r4zk6 l\DNuY&q#q7x6paA,u%.h8Ⱦ0/RVGo퓜~D1ػ?Sޢ*e*k0k|9p赝>k'M}_'cOߎ(x9VT(_r>e@_~,y -_y->BTRܧ0Ljcb?qLӖ5+/~s4jT;5գ(s{9?E 2_g|Ӭ¯88.f2U|r} l`06_NFz͹=\A=g <; doL:EDF* #ƝԵXp'W"цa\Ok=؛ewd4z2ۖ~;|طhڴE|7W֎@<̒+#˶`Ć)ף['<;գL!+1[#Sڢ*ZƠ Qy-ω[rCqRu)Itp[WK:)r1 c`lCYq툾X 6fahYsh5acbEjtsFƜcMJII^9G}Oذ I]{a m8c-/q?D&@K_|>Ŕ-pNâ6*vdtL"F“hSN}:Mc\,]Qڐ_yBP|y֋K,l,`[σ2Z_|"͊}9x%+A/ٞ;c*/zҠP\QR:Q wro? V}M Ύ:>A7tJSB|]EDm'kqr/ZBBy~~eTPgPZyx}y/Oa<|3^FsusvBue2 Xbυ:=R4QAmqv13^!^>vO7ͩݠ :N:e8X^GEHJj;}F _}~c |ݙ7 S)VQQi;ՠbFche~5MSnFu); 'JV,ߟ^1O+4HvK ;El۳IaNuG6뼾mmgl:Q'5S(k'}7}ˢevrEn1Ɩ$I8: ^bd[jﲲC}znM2=oК7nsL4m`}͎!/Jv u z@Όך^ƜcMJII+P.kC 8%7˸Y|׎kg)mT%)UJ?w:%EZQ൮g<@[)4^Ş Ji7JQi<^5~ .{^ޏM"-“ bu `o!PeU AOђ8h?vfFw,$PwvFh)ЊeP*$)(z44wcv1 ibZNm~T]%Ϣ~3Q}߯1OW:WTn7QJM`kg(ug (>/x{bdj~B ڻ0f}8-'r]]{@J1>ʬ#h{z?( aAD;Ie ^PlMTla&qPL)'P $B;Ò}NWx9a@?C02 '7Dk %u0@:lqm (F+E)7gŏ%K* Os#XEɓ 3|?s`?8Vy\j?rNyt(T%꘦W\+ rCyhf'ɕI:qvWCǡT^JZ bztڲiwIZpoSdBHz凰iI;lŨ6%gwζp8 Z232[#7'B (Jm ~E<QQ\ 4rfһUkIҊOҖ,AłwosvIxu*Q'xT7diEtBP^Ld9;kMBk IDATF }<3{TXǏ|ֺ{6O e|0M(%)>uI$C# 4,ċ}v5ʖtr8xզ$b ##@}0x`|Pcb( C Vq ~:C(>vJ9jV1N4'I'.]e??x\ʙpMmFQP)@%n G\l7o}7⃛]R:@@u; ݾO,1{enlٝ|gb ǜ[,xz0_/ڗ^ϱZ N憛n5JcWٳ.y6rCauq,]׾x&dkyW: TC g|31[9У[ȝƶ<샦H ۩@>)B&:sNxzٹHn~WvIvx{6}“\D׹"̞sEj3NETƹ'k?e%˵|[JG>I[2?#X}17PB hsd7FșHʖ!59)_J~+o?.Ȭ}d ̘sS!$$$~XBBB>+iڼ#B1ni^enXΪ/8n=ROfu@~LXX}u lR@$+cyKlB%Dg*.]Zn1DoPL0ǭ+U>/_D(&񍜠k{ނ oT11иh]z>_DasvQBZ ?7BAU{zp˚Z ԕji1/@1ak  /%ʔ79TօgXr`^Ӄz!6OAqfq\3QXۡcy;ŽxOnōSYzɜC*Ĝ[m*Z{s (?οu䷍SO`p_X+1 Nh=,E" RqX*3t9vSO3f_cCLم)r??L?W[iW2}aŐQ."GpHItp(QQ01gX<Ϫ3}p= W/vaN ӽF\HVi=xe rf̹)֌!?5A>W>#D^0F:_J wm2?`g)jjQJ'nz5x#,A׶4_/jF@&A(+EBqcԐ=~1%f,׈w̡h{Y&O2vnD5l$.E*r.f~e@F?UY(9G}yžqW,`j__`])_ưuc4F@5ᘺ4`U6>_:hlhU,ƈ&̯gR{֢?x"#FտZΞYMPtp껎3MqՍNcZٻW{ZJt4EԔ\3;)(<McYD^^T6nbhkWL>lEnbST57Oל'D*&s*K/էȝ5}h65ε0S:+Ik?$I)kl=#'wF,olXT+%cR퇷ߩdfU[3LH/qh='fH) 7`LݞxاG0$cScMf65j ,@Qbc5 Tk>kg֟4СCSf7husz!wF8sDė#_trc!cb!^Y~I[2?߇yȍޙS>y+vY>a+3sSi8q(uli5BpU<<<\f۶mj6Ucƌeܹ|7mT uEaxzz*+mL~:}i#.Q9?a\;'h 9$I9$I$I"b^Zmq7$"B (rzsΎ97ŚU$̙&| q X #F OeH1-Oo{y@}ɘ]:e/~.IIOw$I$IRGvuǗH[Q. Kydiݴ1X0&2kd?agv>W&7t\IE It';I$I$)3Q}?&L;m'SZnQƜ]̹)֬%FS0}ƌn&0fN^f7rJ$I$I$I$I ;}ƏcmhZ##?V$I$I$I$I$ '/SK6rJ$I$I$I$Io$I$I$I$I$Hv%9%I$I$I$I$IJ +ȩlZVxnI~:tssQI$I$I$I$I^Dx8E042LZ\z$ٶm6Chڦ; {}{v陪VRضuYfzvԉ#_ܰ?rC)ɍ熘sCxeRf%mL1e9[n5޿7}6/R puI$I$I$I$IRoW/oa$I$I$I$I$H6rJ$I$I$I$I I`ݴ\. S$I$I$I$I}JOO?X7d#$I$I$I$I$IɲBբ( TaST*Z-VVV~G$I$I$I$IҒkׯZRGA+\v l=9%I$I$I$I$IJVRŋ<}4]~ K++J,YF,W=2^fF$C" fESEcs$e/i|̊Z|5ZK!3o$Iһ>OZ9#kDKБXsyvO8ɍb>1?e!ZI^?f5~q>{Z!<ˋbI3!^'c1XOFS}T*zf8Uaq$-ڪ0.Ű7MSAŔ\(VkE/~7G,FQ?:4:n٨Ӈ/9`i% ;ޫl֔v& Pr{&Dֺ<;6*O\tZMof$012]% QSH6•GC]bHO*ʃa~4з뼻_uqz,Ve014lk&xlHϤIEiXE?3HeMy)\ hygz ;QKP/լc|i w, 'ژ.KχiZ K3M{.`Kָ8?V_!0%~T$jqx_\a lKKp73E[ď{~E\BT> 7a.P5&'GىbЫMvq%N˝_ïL.ʮ#l͡1~Qͻeofmh奌Ղffkߦ?RPS`4oeKrhϨ郎#ӛ6fż43E1~u3[~bV*0W 1^e>4}YWԍ0M$:yŰ9w|O<\w g7سl^pNQ{u[},R }̝0?s%H,nul8OgL5@IHkG'?uE6סX)ǯӼ$y.Mۚg8ETSqNN MMIRb;-Ysxf],rŹ^ƜcMJIڴ~_מxX@^(i} VƺE/BCUG爮nPO bUDӉGDBhn5k!D?E5=tĐSBhUZX!,>+k\4RDj_bƌ*BߤΦ^  }Mn'`G(=ᢈIj@=4UiK+Ũ !z-$Z<-~l`&0֯)݊۟צJj{kD.k4n 51yhi'O"b!6FF3{Js.Y)1H&̒|KF՚#E'#(cxĿG# ;!DLxy"B+V5&jb̈́ ] ccL4[_&ԠV=p$S !b'ˮyзw% WV`Ę|SJ#N[kn11"~?΂ZB߫=#?1ǟ%O?i{EXؕi.M\uD9c]Q0V!Ɯx5ME7yii";HjFNT^cĹѮBѲd9KDXIh")6(-p!TF,n/ 􄞱6GD̆!Zkc.-jMZWWX8]3ޯ%4wad<鎽ZnP^8Z}]0/3X+b턙ZWD[k rN@骅YC" oiY_W}{QqmQTW-<~ H޵|᦯'μZhW66yOWźaEY{S/:2u{U7c1gP-%\~N#JE#=ĵD)11f@bʡ@mXSs*ur[>hqz0L\f_lc^x#&!Z+/ڗ5%mZzϿNJ]}c"jWaaZ.c1Xʡ[K[=;?}0|#ŦkE>V̩>{}y"^chdgzxXAGK:#V/ibJX'OaQtdwtzFVi~[yu7!>:qvGB}SjL2&)fEm]l c*/zҠP\QR:Q w䊷R>▚PKX$`1Nvݾ /x9 : Π"gQm/Oa<|3œ锪9_2,Bk}xw֠6f8E/^;է~nP[DY<ėz哖C(c)Hm}ЈKoo v;fܗmDb5Ũ|B112Śߎff7 Ryw:;N=7|&lG{7hq7S/RqLdsΎ97*%%'3@5Dg.fQ_;QïyRQ], Tcu(RKUk]3ΜySAQ<΍`.U+%Z@r uʝm7@:lqm (F+E7q?:8,EZa*'Or̝[j>(N?s$}:СP4c^!rrFtY?rY|Ost @hj1Wzn6m:(TG(>e'7@X]Zx&Jq5e7Q`sMpߐ6tǛOwmԤ ﶬ@6 TvO:ͥ؞bJ/SIBRLEDv{ď,w#<p/B6%!w|5{G(ɰl'~{X*@*Ds,a>hn=`XRNFu4iP}^( ZBeO֭7ZJX}oҢJ<~RqEg IDATvWCǡT^JA_N[61ynv<)ݘY {G5jJǏ ;iɘsv̹)V}{o %-մ!x-`gE噖2!@Q>=)\%GsUav x՛9ou5oZK S[ v.gX ۖ_o?ߺųtP EAY֮J11!^^`~/+6 IhKA0/^rXF+qޯaCͱ!XZX`aaMc{Ӗ|9-vɆ%a Zӊ ew,А#k0$,$8;dg+?~97.|f};l Od\nRܜ9,6sYV"o(V(\ 0>g!uhqvu;VÁ~].V1i2ǟx+hjiFMt#=OLg" l|YQI);y772Mu&=_$&*B)D1gbONjUM6 D\rʙ[w2>\?m]7/؀j]8=|\w}n)JD"̗_5rX層P_"2 NO~[Z,XMf^ ^}xI4Ť $W_|H'c1Xw +>KFAW7PHD-F^cx8a$SSuBf?q.h5\=2;(y]pֲ/)#/8]ysd_ݼ9]% Ha 柳N:oh *,>ȉ(v,-CZn\/H՛2xZ-̎u=N +6m7]=_hny%[L{m)4%&yvfeZL|:MWmie׉;E3?.VQۉt_R%Q'ܴ-_uYRSlYVlŌ?P/h ݾAtS.'wKa+Q˘ NiRj{1&cL:/U qТ͐a ~J ʏ`82n&߼BE (9ϑ&n8Ñ3X-Cjs>zLds·93'MwD?x?-+޻ YumcGl a':Mm?]&v,c>&*+ɊXc*љi3sd[~b8ћ<5?qJ>{Q|{5ƀQK({E٧PY<;ju7vLe%sā ֓j=NR3 ATE5>,kj hSWћ>y5[c<)}^p|\.YVߦ.8ڔCT _} {7z՘f}p[=snhS;,\A\[az_źj b'0m/Sflzs`Zc4g@l2rt8&5]ԓ}ؐSvaoJ?{wEptHH&؁uƩxSlE=S ;Dl ~;;9i17#j2`h}V kvONHl -6L9co ]Yoq8V䥻3c]`If=nt-S 9EcW0T &5w@;>AhCʏ؃7zP-4^Dcm=7h:G`L#fmװF7Yޡeu2ݤl4땇2z4CBR!GTEJs/ObCD_۱ԝYo/\`["Gv*^u/5glDo妹;~u>|冴( Shkɼ ;ynRdL B\^1*lajdkZvNNc{եV a7ch4U60oGLJ#(B7^MKtQ~+_(Nz:ǬZ4ԅx*)740OrP4;,e;N`F;g> gWWL "g)V2-)1jhr5m KSͱNy _$ôRS%vVAHVBXi%Ъ4AYENjpwb1r2,8yZ7f VMas jB%={?ru ,Z˚ˌiI>̗{}2-gݜE\Rۄ5[0u$JN+J{yw#ѭHQU2alY+@}9=< ~#n͆TQ)7+Sxɓwk^+X<On; ="ѵim ]%ﶯ$u;I&lޏgz?NfۈIjw\ξ-Sj&vGanxNɺ=Yn 6bV&0b*jn|Z$2S- !Lt̩~sńwwB1yNeiNV{Ͳ"3n_ijϜm;12XUnD*ը2j5G2Ư?.qYץҥx>ƹJ DMTm3vuɺYe9-0 g(@L_"{ew |egr1y8iC>S> YH?N x%bsA5= K+4׸LJB)I\v gg,.]عcG6E]]…s>gob{Y2^s *OJ^ dی\wI)[3%sJ ^Al^*󒤧\ٻ&u,*d ;q Y !h5C$.R."ThxdzE;>9|I"ݗS9[ A,';A!pbу؜.EަZa{.2,!ϺC⬜.ǗvEL\97ʼ RBF AAAAAM$9AAAAADSAAAAfse;o³Xmv. JK\Oťp~FE!zAHD:::יεл-1J)QgE5w)V!#9)lkWJat/S^x)a eƾ9:o-'}^z.O?ATycSh[zh[H =idcE2;4ߎs -IJ"渮!{2tf .xߦfLNPa\|Ld\{+Gn [!oH&0_ –eN< *q$[Ĝc.H IIhDWM\.GU&Vic~¨^6e`+Kߪe-cCR F~t˪kxōԑdit<>98?ͪPK U-CJTnDW=KiY^ca/, 4(Tjs/>IW'PKOI.΢oJ-N9'<Ϥv5(9[b۝/UMx|olNhbZΉ.KE/?\' lfU?V27 ğǵ-KpJlk5n2LNozPKҲX ehh]8p>=&fiVOg`ToI)-Q,>0{5uaRćLǣbNc3c۴E[3)1} >)1ƻwaUBc򦧏%J޲ &ħ+9@ F:~ҒBW3j+= ]`OȥgВA3o;Ify elaBVQr?ֲ9/Ѳ0|-Ў~2M- $Gdxo0x\Z~hBxW՛ŻN9M:9¼e05.$u<oooT&LR-rD,,NY0 0{*{zxE/sAlkqb$/o 4@T)Fϼ+r3w]Ja+SX{%pwC/܊stF Ĺ^Ĝc.H i?۰)=G2pw0|6=iJ6v@@zEhX- ̫2rͯףZ&<~]RDa2*ùa8i^f/lmF~*JZq K <cAĞ R겊LߒvvԩkW//BK:gol˵2A\zr!,ؔ8/j:E#6঩IM*caR UU4 -qǹן^`+yk C/o;:7DuU -DU?ʛZbuwӏ4fM=MT5+QvS42fu@uݢTs#$;q+Vak/]Eqqz} goA, ,!zQERd(뇶bm4M(x+.;/}E/NψwGa>}~:r_蔚tm_^1C P/A)c͕p*vQ(eN1 D \ &Ϊ,(Qq-!(Lɻs\$eaЙʋ~%6+>+ +J6eػ67ߵ~rv(8Of \F3A\Vci7VC*NŖ_N>/]\GbljL5/ʕ@֎)0?3Q/ FSccLv`m7'zn cqV y(΢Ke4&db}p?JKd:U5Tk O#[T> %"wpGں(Ӹ7T2{1~_A ;@{bܝ{pt2&.÷6y-?tZ{mF^[nс&/w?q":~cm!7w=Mpg#yJ~%/C2#6pm,Ov |sx.;eH9Ï~Uo =ÀF56kI?O 'tٸQYd4ɎY Ȕ_hTguFٚH[ٷ}.1~o]_HDNvy4-Gvp1Ύs윏^Ĝc.H imUA o8 RO gی%*7ǐcSWDq v.o'=^"Ó'qo ?' di~JzDQ5K bʞ$3`B '̘{2i הB?<_$msq[0B1Mq,xS@"J5M95ۍ`!AR8a$UV_Icga|Q=HyKT2yߐ@Y;mo/!>ǹbI>V8ώ3sHZv~ #\2iInaIQ<}Tj-"$<a$*an"O"{5I?dTNktw24+43j@jϾOMlن_ -nS-ҪulY^7 SL 2hLQPBE*~6UGVf5t<Ȝ{pCQ9M[T@A:n4U!Z&CIxk}'UspܓjC@RTJ0yJz($nNEOM#-,}|rl_ Z(Wzgr15B c!?["l .DK]7F4Hu@Ҫ?jR.vM>>|L,5 +29 P {?N_ lUSMSL8&Z53kRI<ޟ./`Q53׶A>K9WQ5,?oOéԡ 2*]oejFfԜOos5 0\ tP*;/8NZ"sAUTΞ>|1M-ݔ Zbfe/R=CI$>ߤ1P*A)6N14@UMR 'p "&9H϶1zmZ:_Lߠس lЖw?U)g+#'zvL96n B2Q"܏.E^T98v?yʅ<1Ul*iBOףg}K-Aw& 5N<' L2*1g[̾OQVMOmPO,nBIkH޿6 ;VƮ3A:6_Ng)M_ ͏=}MO.Fب:Cdi\ \Jc~Y&k:K4sbN?*rR!C>l5-f@,x- 5@CCr>ZJ%WxaAwsd_s˟m)W}sMDBl _ EQ2$x+l[+Y&?C*5{Zy'p P>\NPeJ~$ s 3uZimV*j\?:8k9":Ccx;)u9I {tBĜc.H v SY}Hb CFZy:S Sv(§+%BBԥ +#0?w'D LN]$g$&]eb<8N ףDXz L jü՗cta:=Ӊ LSl;).mĨCW0?2d*<dЂVޞejV̉ǮP OiPm+9m| `f S%x923LruK*5vsKJ^a\m z"B"! .^(Qa"{MXx9OI3E]iBHg)E 'Bf*f&( {) <{s*E ^2ݒTmGT%AwJev W>W0Ȍl1VB(g#/#C8# V6_|W^v)wQXB0$3ru/ύ1qʔW߹ &Q?BuY;=d?lPHH0G19CYĜc.H 3S'8z5Ah*)_FӧIog24$7}!(vɕ:U*PFUJj~iTfQ:]ݑNJn,1(fA L8ǁcdWBHH!2N Ŝ6iO&C.HO$]ߒeKXeO{?곛ݤ'W}MBB݇-'kˌw=gj,Jy xy(R>w)\Η{ 2j舣#u*a~&^c߁5iIͯURy.tP~/UKa]\=%8wԇ9=Mj9VEUlk1{l{˩$YLn-G*‘S~gg'rEVZ9ѓ".G|e+(٭,{3Ow3o,ҳaʋԮf1/8suuzx_ 䱋(Q%8ڌ,~_Ŝ}S~˧ 4)x4Xnʖ-˦8/lj׶);vQ* wqՠb.NrLUh9~?*Ҡr z? 7sUp#,ӮDMGY{%H6ɒ߱SϹFPcfLJG[)bkK;2:˱1RI樔ZEU6 S3߿?c.H 25#C';)p|rKq;<[Rt.޴¤ΕP{|t!7Bb13 znݨ^ncb<x'!1H33;؃Zf}Ԣx&u9x0mXb˓$}x*7>V>,ZY͎^54$;`֝eiȐx7dC0KZ C"|k{+Fc-C_2F?aM*7Ǫ)l(W Cڪc6h&kDqU;f8!SNv ڎTN7),Ga5PN9neB%OaJfh9y0]3Cv2oIt Ц *ϢLRǰq5x:ه1HtG^Ugk)3Bݢ =ReA?ʿ;i17#j2`h}V kvONHlܫ9zۅBW[N}Uy Gױ=ᅣtcn3]-DwBNQ?ՂI5p0,ЎO,#y6 X~ibύA?ao<ӈc5:u}`*txw(pY9j*ik |7)[&zL/UR#Ӂ%v,uF@ %=ևȑaʻWye݋xM1j-+[i_'_zI2T,ڥ>l2oG9N;=x#P/<*xo/nқ=ɇ ,CD {ÒxFϭY~Z"K\{Rt\M28jEomFeDJGC2a^'}W~03x@N+Na+{ mʋaxwfMm'RR+1j9փW^ *@%926F-`-Byi9vb)Oڙ~d[ʾ#`*׾DŽ 9iتa\HP1DW_ZX=ޑ1402HvZMQLߚ>&>XNfԔ'OSk4~ʵ)lnAVM'87IP Ah+eu6O0_g}4ʷusqQJmJlR+9i(qG#yG!WmʄݳdeJ/T^i@X 5qMip0qwm6*J1]m+dM]8t]=(ێe WѥI1 J =y,=^0b^SxfyaV"Y*^ks~~}Li;gvϐMi-,rj9<eߓ[(Ǝ`ܘE;4;ټ fi k 3Aͳ"t1'˯1Xd[7o=<24'#GΡ^Hz¯?mEZgO̚rc^$~%KQ 1sA11I8Eyڜ ez+.؞Ym@Ј4 !)}$ocf1C~ ŚY܏ZkS&%$_s. dܱ#< YrIE1TL9Umg^%A Q GA;xdݙ7)OK9 HE}ó7Ľ%31 RBp[˻lIfuo؁{]_)){NAGbOelڶrnQŝp#"rN23dw"2q5s73dv)V!}rD/}s YNw B>Ģ9] AEYwH2.钙k1FsAUȈ\fse;o³Xmvl++a,{_~7fLryvE.>\=tttЯ3kI9]uf[]9\b2"϶vq_N:">赋Fȡ=Qf웣.?r ;;%DM( ?6Ջ5?bѓF6h]!Cs&ZA[>=fEq]q;C/Le@"]. Me͘lʤmw0}ҙ=nng\I΂K")> 1 [S::-B&DǑwWs *dD&%9 W]5rUBXUIqzQ$¥)[s]YV-ӥhʔ2D]I7k_V]DQMTrjZ蛗'O?}AEZl u(Y!]'2I8yrچ(眽ʱW'PKO@?GgѷI%@][gRSG]לʭFN엿*&[ZRLu }Jl>7Re6rF }4u1-DeHG~\!C&^ߗyZW6CGS*L:\ؓ>ILQbvݯݕCQtvL #fwH/8= JBSۄrͼW屷0IY5)T6!u]P -CkXŕ9-M_ɒ!=[;!wh^$Zhs/MTIw1W˦߽I6n} %S4 *9N~4ZMUԴՊAK{[A#_}lytd}bAIjfԘ"2KS-2|撞wqd:b~)d*nZ!V}ElMS+g[gN,2ԞX"sAUHLR3߯Q<p[Yok`xGe2~SV(M N3NI|-ݯ-Ɖx¾E3QQ^B9YSA̛0nZq~++]0Qyɳ-ZȚrb߄q>V`ղ,ۿ.Vmi +C-6uǟX8W3mU(scN@y|\۲D FƺQ&Xt>wޜ(-EqnPiօ pXcj&Fqid|A4"eOTrb&!h;8876m8*eDgxv_y1Jzq]XИ IDATcA $7x)+~C+># Φ6yѭzΰ3ЕŌJBӧ$r;dбێtY^h[n.py#OlKF? _o pvlSc88=$^9A* ן%QҔz8¼e05.$u<oooT&LR-rD,,NY0 0{*{zxE?3̝ײRneo_WsnE9:g9\bҒ2=+j98RaSJ8u]aV;;6kK)3RVYEґ>9!b<ݟQSef7vI!\{?0n{2PngoSyFÄ?M_ZʶNE 컋[ǝ.]y7ԞtnJ[ݫ$ƶZUVM~U6"9}a߅򥞸5mO˖y|Ĭ|ujԨ55ܬ:U#mLk6_pzCuItŸ:ft;eTy^"!lV>Ƙ)ǮcAGUYƥDH{sGtz|4yשlҘ-sX`]p}DT+.f\ sZo&YϱkOtuwŽ'3hkBa!RLgYF%^Afn=Fٟkx. S怯=۸ڦ?MYH/9 yWFnz[ĞǯKC*(ZFz8_JlbȖXܖnr0UnphP:c<+M@../XFȚsmʅcSX(j؀&7&7:׫I!4TU4ġ^z-7C3@UK]U5зIWp+o*j:fTj9]>|M?Ҩ5E4QS@D M9T? MuRݏL䛮Trj{&8`i9UƲw  }j")2C[P6&k<ї;&ҾNLt41(eG٧YnD;ma?zea:GI oΚo!^Ag9}/%(F[vPB/`D5UR͋8urm OPQJD>ѲùnSƽcY}* R6}gUKubO;7)˙tpjVtD-ID>85vq]Inj {c?A!DM-4v7Eq-=@hě +*ag*S%icem+yISç&^aX1)iTǹ>/NψwGa>}~:r_蔚tm_^1C P/A)c͕p*vQ(eN1 D \ &Ϊ,(Qq-!(Lɻs\$b\iCg*+/y:)XbSҰNdSF,΁js]wK F6YZ.MY!GlXu8[F~9tq݊%'2,Jhh(W}[;ǟah aj[7S޿%"iqȖGX;gǹwCvG/b1Xd`ڶԪ†7yV'mncH5ǫ*,|`P+a,S]]"p g>BY* }AqOJ IR)+m2Q%X (IH 8C)}䔬Q"ʵܸ={+1q7v;a OSŞ(%bYrh0S$3uo\8aƌݓqLk$}I'iXB$iܴ2-QJm_lU̩n {f& L#= #<>1oS&_TA} fN@IPGtQgǙ9x$-jpq_?.P4Տ$7Dp(>@fji\i Iwxp07ZEL DI$ph2*@;5~hKgѧϦhlïsgᆪfM)bÎ Qcc=KlQcoQ;* [bTPQ3s߻w6Y*Wh(\Εlq]&]pu2sj:RG,} }ŧzʅ|Mb?V.eƿx!gm1nj]q Jp| ,lj3̗l2"5ߙS:]~ٿ bhhL> Bh=ɻ Q\߸iY4 _ iA)Q/23NZ6@0?@%Nd!u]0scA2b ?Ղn~l[%Mߛ^H˜7q9D?Wo."N2q{[f9ޯ=i8s҅(n6Q>K"U(i>[BV+r=~ع:*C+1 5ïĜcJR Ѭ_=Tu(L1FEPHSs}ۀ %Rl)|zj9]``A5`B W3l_xvU(7s_|@w},^q'(h@%9n.`nV<֝WF?υ8n](x8ܽOtϲ>7cRp'6툿3e낧Oΰ99W]`TO//PYyf]%8V. e9xľ#քup*zzapnml{ɑ'r37/J>1߳#WJ閲#)2/)D\VI >^~Tnsbxpmq?+#2uŒ!q~D׹ ={TΠu*MX^kMʒ: =ͺe̤ qiDRf RpByȞ*S? liԲDԛg7P,Ŋ|$ܴh-]\Q'sdYǧ(mn;?zf7L(U`*zIaA,U'qݣ-4tu%w)8Jɗ[U".kTP>w3gY)V-Yifk=uY~%eoaw5iݧ1ێY/]F)=_VqCcSRCY2m8bo\Sx ~ȴx8ozbjQǶI}S}qG %('6/`!g6N̈5⧧CxS_t”1ۜ;a/pN/,\ :F 2F:Fu._G ߹lcX}yn NS & :P }^rh4Y}6xqCKDZ&BsOo eBY|W<]N!=w&yhcukw\lΦө `o4쎇lsT_} ⯙ܢͱ|,lዃ̝q .GcVs _Y+ 5|zSbyP+Pja _ץO #qqGәl_CM GiV (ٯ2KK"|S9~H@nd4N0@G#y -j dudLki =_Rމb@aKqksWZx:`3o? όXL2r=4姚\ M<ԼVg _cb<=Mk C6P֍ZmbC֍%1k?{$\A< :2aB\tHO~I."9~W3§u՛㚣XP,W+e}sXfމս@9 Ղ7Oŧ"v/;VnJ}vɟhsn^'捞X α"ǥBFmwgyaْ\msUŐ_E;0DVuE=u;FJoeΘRSC6kkẉuyhM/@i;C;wx`|!;_bЬYw h@w^,5t3]ⴛ]MFNlEP#sQ#Itθ&ߏm;$qj<XO3Qu1ǹVO2I\QͶH]l-BS='UYbda6kQ}fG!/XU8ڗ9LҾ1  xC7$s9+*'ۻ"U[2w-zi?!HNR !Deݖj֜2 8esّ{\5lD~;;uXOP2&1'~CrǜbƗP vgsMB8)Bd"՘wMZ")J)׉鈟YۭM=JQŜbIW.B!B!BϓFN!B!B!D&B!B!B M9B!B!4r !B!B!24ej&M@M3ۇy &K!B!BTBBppt(QFEUUt ooN] PlތwJɕtÙ:>1fD/Y 1&$+Ɵb 1ƖEZ%yI~s斕bM.wJ$ .B!B!ФS!B!BI#B!B!"CFN!B!B!D&B!B!B M9B!B!4r !B!B!2rZ0pDZD!qa ^~ȴN_B IDATB!D:Jbnj)+LRTZCZa>S6l|Qb1gXERr#ʣ g]y yn8|*=;8q_#?m'_ ;)~˦}HG21703 Bڇꅬ1$gU^rB!#ׄϮ3^׃:i֝ɀNΨ0"ws9+*"9_{>uh4eƩ 5;gF$O7SkŌ\ Q0% DotcԾQL|QW&泺ZDUGrQ4*a)v%0z}G3ߡbm7ho,NlH|*ؿ׈ݼ,y 00Dl O^tQCVEou>A>;=uE]`LiOCQ0έo`T+A^+ <']yNk[lPloX-oy8v,^ 'Kc-s)C;Zk}t~EtoI.D6v|X`d_3n5^|+[bXߏۖЫ QfҾ:Ԧq{LI4 'Qa|U*/FzXV "H*fVt+jFd17?#O@Q;z O !7Њ8@?|sa~|SF1uWLs쌓YĒ3wY)V8tGaFs[OE,m#Ȯ(W=Ւkt^G]Tbc}n5dP9B_<ڑ_:l/P <`"MM#VOÝ?0i3 bpq4թ 4jFg߄b-s:b"kO3] ^G5# c4cA K܇v rL]?ԥ0]ٜ6MRC^ʢ GLiLscSN2gų|:,@Sʻ?>9mYQn_X:#KEu chlJE ė!;Ҳ;% ҀNK>4#6K. O0S,kH,H?BbBڌ8kE=w9g&7ua;= s"$}.dG[|hPu#[S0MIW\Nݥ/3FĒ>s1rO ;gԔ\xNZ(e_B֪iӚ`%9sǜbqIe 7O/*VEN{  @]6o@Ew*X}*O1y t/X#C4"@}~p2A_KO:=ɋdnOLǮ;T=ƭabcz+ΞK@^,^CZvb?9Hm\"v;uݑٕ5Qi_P8 ?Z|ZzT6L[G8v/! ,o ʔ-Kٲe)[fY--3$]%s"s8ZJEJ8[|~aR&&?RՎk+s(r(MYӣ= +ݣ5=P8ģ#0l3CS^7fB[)**6M㗻b]բŰji8 Q7W2mS&ضn< h4~5&@%hW?J6 2.'5ׄWuS]4näM9ę 0X(j+^gPAAEfގP/V(zUgv/230ߖ^NtjڀM;_X=)ujA+ M/ـA$\~n7Ӷ*%X_c"gZ@uohp5IgsEV1ZB$:8Tym!_4Ć"5z}ЦR la#pyp bcjE~wN=Br#1gR".);jV1c(ZX˺e{ 7uFrܮ3sa϶%<Ǿ RcEEgpzQfÑ;TÎmwQH8yV.yϰ\WbvZZ-u%{r2tpD/ޞ(B2S#ou ~?Jo?)}<[ jO8E&iyH%nYlٻ+жA{I!w:Q `IY!x$)`Q}2ϧNmò!7stZw&̹}3é:~ u֟)TOyQGNJ>9>1Vzs9+*򋃙VTU׮p/ bM d\-2S=k8gt,b(ŊŞcՖ8|58~_aK&X@{5YhQKwVnD #ҬMNkGL_DfĽl^_ %0OqޯJNF,Ӈb1gbلɋҨs=JnB}lcҶ1x9]3qnjɹݰ [)Uy5rk]{5Ȍ}ȡӼNЗ F()}+&ey s '\uci_62 3 |@jA X_; S_a=ڪmsM<$R'76Xȉ&Oђ" ?=M% 坂Qڴi>Pk`|W(Wa\>' s%[\SWʹh@1Ʈ`19| б _bK`@}r+4,W:i!6$>X1',\1mJC U,B̚絢IOzbO\O*dy$?Ӛ>YSn=SVs[60~tpu/;䙾t_]GXSz-9hjelno;+n2-T"K}gS@3wY)V񱴽|D\78ԨD>ofWjr2;V_陖7a46\tΝg4XK8:E#>>W>Ȑh4*9ż}1ƧJ3~3L ͛粊FfF(h#oř ]w*ޥ\?8ևmRB"IL[\AoÕt-OrxW5ŴF<>sqN8Ź#gxD͙ {#C 14D>%sVU| Gr:͉V+LOJ$2JAW7PDn4| 3#Ԭ7'R1b~iiw*;>,VIf6.x16|ׯlx6,yWN$<3'FRRИXY+K*R(bbml\1I $HVk;tyH ?}Z=lsT8|.NXE86wtӍ::Ū=@0B8u9 }WFrBƳ1i#_MZdB ֱEʑ$yI~J]1Õ}/UV 訨y:d)8A9&1gR⽔mT_pth?Ck`>mQPy仟J)~t0,Kʻ49U{ҩ}a̚cW![r+Ƴ;U2C ^0䡘Zae-rҫ#nv_PDǨsQtyʿ|bߕU >)~ {c|{Q9v1)Y8.q[10f5$"3qRf+q{؍;d'jا%gu%x7?N8M4R@)G~Y[:Dwfw#+wtW b=coaT W]@&3h7# fҰ=5_K;tۢziŀrÖ0HFvj>t7gpAPe/)`ǥɁK͗q黐Qpz}+}z$x>,~~U1$\SNYka_W$q(.êQWlV9+* 95dFs@  OO1pp4:qOoիE#3uk[vQ~ȉbd]1j5c\6Ո]l8Y.1*&9ɟ+=k E(x*JS \y}8}HU!2MVV>`{IwNr<7 aun4)ma 1UL6ŨmSҟ\ y9q] zƭ0u 0ǎ9I\'vL0ijfpţbvNS5l KԀ|;r*eV);{'ܪ8mcv7ߪbK;x޿/{c"k1˂w"(f{23sz 䍞noRO:flum,8nSө$[V%DOe4szPodil??}q6B$>cA-]mb,H"&߿qڽ Ȕ2)Ę|0c$z$Ebб+'TS`X.xۧD0$1Gˬ1gXʆM$iM}anngOzk8C>q>;?z8eKGB|/K~Rޥ Y1sV1(Ҋ|/K3}tIM؎صaMJŠW3d昳Re]88:adlkЪ*_w .( [6oN}F,8s.,uk:F/lBBOR !BFC ȑ$bQ߼ы7=Cr>ECb1gXEq{8ɇV_UģjKCE]"՟*)G;!Bv"S6yq9*SMogG J5$orHR"qҸ[]i<;m!)N;!Br)iJhܣĜ%_Y)Vx!B!B!iA|n>G}j mOyR_Jbnj)+Lҗ{)(x]+bgjՆ=J̙;HTnTy+0\"/ɑgf[yسl?kn% " & !}Ay>ot/#hV&7zfw曝!@(WVP5 v4-B/o6iO%w:dxM#(Nr'y+i@˓{wU36ʋt`+2ׄϮ3^׃gjZw&'89&ÈOn9sǜbILmo=f:h4aT=9SpA͔Z1#WBoQXz)o?Q8^{磸q,вŸ0RPx^F-UC?k5zSbsěTWPu鶛p j'W6C LsRZ~>ax]n^<S`6^OL:f>yэ3*(z}` *w5H9F%052ŮdFO:I )d5[ ݕVVxN>'02Wx#\[G61"[q,.XܽN[:SR·$w46u'\ ,#:ߒ\މmvOI46<Žfܶk.[zƚ~ܶ^M4֡6͎CGسfM Y8vRy0Cg^AR1˰no[Q3 's }R06Ylӑ'3u&_qrYѠoLvoT$ʥ>QTMwꋿ. DzZ<{NC:lߏAhhdHn IDAT_ZCbGgg̒uĜcJI--/?"̥46wb|*-blEvEO쩖]*?:7*Eb `>YShyONWXnU6n8uqT2/IxC3#ʇuaA_hYvt=.֨#\A/}ujZQ*P e@GL^^diKfblf,ޗ;zЮB'FWYܹ+צi[jK^Y4a#(inb4i6fX=%wV09mYQn_X:#KEu chlJE ė!;Ҳ;% ҀNK>4#6K. O0S,kH,H?BbBڌ8kE=w9g&7ua;= s"$}.dG[|hPu#[S0MIW\Nݥ/3FĒ>s1rO ;gԔ\xNZ(>ΌO>_`ږ {{?SQD ;fӜfXGvΛDZyu]oƮ1s5mXwHKjCIt !TV}MÜT%4ӓ3wY)Vd)NyzQj-t];8?Ƕg`삵aEf~CitX+c}c_<%ӽ cYT z3;}=C,<8$/^1w­kҨ0o˽,pبKp1'[VHcK*TR*+PRAcY Ubޫ5Xأ+XyvyÆԋc7EHmb=q=KnW;#ҸF5j6˜jc洗?Z|ZzT6L[G8v/ieʖlٲ-[ Xw99q-]R_%b_w\rbBjķ)f9@[;21Qv\[+Ft+FqmϚY_gN]ͨY9c>?O?aY,ҜZ1kJŽOQQyi-3Z-dV+=N[HiB5WƶMvY=A띬9!*AQ:9Ff-ޗ"ׄWuS]4näM9ę 0XG5Ï3nuɠ"3oGu(h3_Ǫ3Ι]oKm'c:5m@v/SƆؗluW.G?͛Si[ys`UMI3:-74\od%JL:}=Ѻ^xsk*JvGʺS7 `?ˑS>Z4(c}1g_Tg /\d7G4'ct=ǠU442F_@J%pɆFxvÉWۺ?hQKwԕ銣zdwh*QsC=M(^w0[D;֭5P39͍3XO٪׌[炶M 9o݁%cEnT{<ױkPmi:ز:-=鎺s;!To{R!ޓc Yt赒%`5wp*⺚ Gr =ČCAz?$وN 3W!D<}dny T<m<88e,v3E,!/P"96?dD6Hsy'].yTuzGU/vx/Hz|Xyg Ry9SAߍQ@hIJZ|y 4ªDIJ^Y׹hy'|ٳNcuu8u [M7lBS 1w^ג"?YCިC ^ߦW!/4BAi?f⹱|[]صz8:Y 'rtA=jHhF#*$Irh0GmjX+zU]Gox,zoNz|'cg6OOyncۄ\ cJŨZǃrj@5{*":D#w1LLh4QE!^#߿Mث>u_fgN26XĦ+_ЅecRBi@b5t܎a[v0'3ݷyi_GSP|#0G VBZ_P?EIx;]^ZQfr$wҤ]ں`$I(bڀzL"duvGMPܨ77ora}OxS2&3atݧJ*FFFewi90W ,GOQ闥y;;R,[kّ2I1˒>J1C#uAl;H`KXIؙ?70k.HmDnN$2 mܝ33ݣ,CTtBq,RX[>uh{jE9;Y4~\FBA8}t'ˆ9ssf]&(._F̣e7BKK -- $&1?ZR <? ~9gԐґ/(^Д#qJz5Q^8%Ҟ MdAaW2͞;Ƥ9Cٱ(+*L֘M-[=M K;t9U䣵d<#Ew~#̫ݫ!+SU\ q4h0Pr*Y* m<4}LPA (ߢ7d~5o;&:b@R_p%-3J7ͲLJǞg s(Чϐ,0= ClOz_noƢʩODPׄA =BP[`&"8$* zPhJ3@>OLBdHaZ!8TV _P>J|ys(NI J1|:]5@QSr{S"|62<1lh}e7 ;lT][}3: S[c.uspcwjv?\؁k}ҔtY+RZl_-!3823miITM1tDJ=̈́Yj܏-9G{kRxT$02LkL8pJ9gAe&Ѫ^sq7~T^(edYINeu&P1sS"l|Q]k$4"8w8iLD:,y_aے?7c J+U 5KqptılEHq6n}B-) 5V.oS923jdM+\ "(( 㪁QEK$LlL, E+~&(xʽ<1˫[ER%9PՈGp!: 9*~y] ah[J^IYw]{IOu*֥Ѝ>O Pr:/ cWPnV[r6.ePGgw4ƶ7;pxY(wq4A?,<˄>% \2c$E-]H-4*ѥc1wLcƉ7[o95{ [_[ӾK_iVmB)~Eͮ,e ^qJ=x'kӳ%*$Z9*6pHA؛8BSҦd{}+V?IC]Ħ4i]eGN+ SPx:yd2T(nNN?^f茮Ѣtp GB?=!=±l’n)z;FaiɜKQ@@[)Di(+~-9 9;S ,}xU&'9x2NX1G8N%xt l}TVKf#bN4gƜbdh6Gs7/^R:􆲣23uiۿ)3ڏE_5Ʒ+ƣì< ER>̑2"Y6{4k cQ)[81{Q%w'!`ˉ9~T+҃?|W/6_ML:˶iY"}oCWsdQ]guan^5$ +yRA}S/o 2Im]%O͡W(6'N>ð+?jׇX8*Љ%$h+s'E2J.5QzLzbJ<\=ŏU#۾A~vjv99K|0ӛNֵX5zvǬH ,3݊ҧ+FPni0/t\]`:|^M=pVEN j6#;3^w(GE:Xo)̢IsroZUF7`WFPg/TC͙Tˍc;eëzëgY5ۇk5HzL=. "^l͐1 Xm(o1`}씠J%Pga/wOf-o@X43Ly$[68Z G߾&21ݹx3v-IsDӫ]k8aY6K5>F>A:R&VjT&= {0/~ePEĘa[iğW0WaIV>n*\2ʘ}g); N׼MqܬX>(gi]Œi,@YωgH Wi8l2bA/Ю2mW`R p گn#* 'υ.x'$ׄ{Տ5Ѳ\I၇5Ke$ƴZK5K7n)5$k{4@Q8zy b(ǀiqΙY d>xd)laʑCKc`UsNCZ03[-1q%ZN"LòxMFV OLԘsSҦ k͚kMchhk*" ״eT<^ِ_<~4Agzc7(|wd-|d.ɍ熘sCxEYO oje׷f]du"?8!xơ)DwƢU8mM:V1CN97ŚQٍ-:iSǣe]wNw Ilݲx+b.nducs|,/ܳݺJa|օ1l| ?^N*A.evhs8!'cS45ɛW7^:WQd"snUHLnS^~ӽhS2n[3rf~D~ڊVSONA |٦ '6p4[Ps6S\/"oBa 6m^H IDAT:m$,ߨ%bθf97*M&WIX4ϱ37A2KZev:AȢrPy' Z5y٩!Mqy/oGsVq1XCAAAAAD#     ٚhAAAA![     dkSAAAAlMڴaܴY3Nchh%    BzEcmcvG%\zOO@غe jedTrǏ wĘbLMn?7ĜbL.7+b28/KXĜX3ʁ=3; BLWAAAA![     dkSAAAAlM4r    FNAAAAA5)    B&9AAAAArO#g uiŔ?`.qG!8HuN |̸ 27+bŤlb:q(!gvZPq :,Z7l.Ďt !8=C==SqW$1}z&{_(b1X2:`eZixK;DSn ןSOl(57gdA|Cy' `V":F!'Ӣl 5cG]@$7zao3r~͌{ lߩϒF|}H) @XeF')y#2Sx~i?ݷ^r~&xlA&>1%||{Q$LtPB]4q1sSBzdP;{ZDPmmYju͊0j-}XKi۵O-lO,?QMSeu2nExnrָ+M+m I?VE'l4bk:ZWʼ<<C5z!̤GR7BS/kG\Y=&:Z+A݁kR?snK!u(^;ɾ"l:WH[]bx[ŷ GE 4姿cw/ k~w z\!/3I#kI}R{*yLaQB&FT~^_=u˗AaA t{"!ioXڳ :Qr.gtdp5F|$LYӘ NZgVo:Yaa~Jyve[$/Wv2Jd݁m_F _G{MYL 'MgVO):ع4ߒ3Y}ߏ%)2$:6ɞoBRCv8\';׷u y?1GMq2kvNJF`~fu!BZ7OXز,k4eJ!ޗVف'R1V],mgPH\ J;z ^գMxuZn5,P2i{֤GױthM![+:~?Yeܓ1a;~B!w}t`T2ӻ7q >N:0~]\0~Ak;3s?{ƻc(Aܥq^߅,0W4k;-5258njyϟ0wLQyG9*\Tonk~˯e ecGc qȸ+ ,<{DIeu+t b+r`zgOĜcM )eJT^f]2Y +̹-1VeOt\ 4`i_@u9ZFQ+qTOĂ]cm֕43^2ť֢IWc٦;5_Ħ+hں2 STZjTZ{c qQ*VKcIZAѝӪK_FY>QUA9d-tFN.aZ, vij>|*@2UfhR&slUom)3> Z~-ݠN{K22/&0fuSi\bb'kfHylp.[rPjt\rZ7h zudc9w|{V˗4ͳ#VΥ{ڴڨo\9SW3}]v5 sC;dvĩ!ZXjȆchLƸ3fLhދ{Ą SŜdhQfeݏݻHT8q$Ҥ쉌hN|o4oDy^ M'}zR :ZzX:6b R]bkDeA. tL3p_VNݩxQ4^c) \]XœFYtb.pyw}ލaW;\ʅ7u/%'!?y5ˇ34Y߸KiW4fhmlC. 8:G| ЬC:G~-7Vѫ36fhkǺ#_Y@cK гTal{XɎ|hkaX-'i*3;am~~. N͜)`N^;*wѐ/Շ"8?*y2|\@2jyӞV?Hu@~Q2.Zfُe>K[yÃ]hU(fzvcUnD9;;jV2m2xWFRXNZtcֽݾv6_jT\|'u)؛6]oP;&~'JEc(d"E&M*ji?Wg@ .'Ҳ@&NgDzn9Uw:٠6xf_hThJB @&uqJ#I;9Q *n~^T=:֬AgJ԰db0,4 N(bpL0.na (~MVFEeTZIٕSR S.c;W&R1Fm>sͿ~bph[Qlݵ aX.\M67ؿ8j cZ9vR@;_A']?!<mᶎ+Po䧚ئނYh &;dډe;s`oR'gw2S|ٽlZh>U7z'p-éz>szS1²'`Fۿѩ*W!BK3d?{yגkqWլoz:@~="mqO Cݺ8YN,wWv_0g] d #lY6L>nKVMq4]Jsj6cA3 r)k~V/MGG/b1X|4]g\J Ńm!M_9vyB/S۩FKPFϔToM* X׀'EU6 }*-gY  lCfK^>y۪p)5sY?<Ȗ50BRj5M ueG e&,۟j ,+V6w9Fa1^r^(r Qџ,C (ml. 3W!DDZb%vpucs_+6mDONCsXG;R0YFѨ[0gzch_~cbs;t\"U vI }=4bS=`5wp*⺚ Gr =ČCAzkA0$sK̓嫎y> < ypqX)"fXB_"gEsl0EuɈ)mNBD]5D^:_k8p]-$jp)?acIJNͽhՖ`n`S˃6h9WY}bY7ڶLE BQMLinRu10={&QS+/ԃ񝱂 $pbk%8o s߼ Vsv̹)VS{QX׬hizF,cqLl ]X6X)%+VAe~x=}k*u4e g?X s`)Jc[49_d؈'9c)%UKe^n$kżXꪡmMg[ F۵=C)fV{4+;~_3>.qӥ+?ɫ{]}FKueA062#,zO(r8aLkX|U)*4wgGcksp-;I$>J(HiN=R7w-v< L-Rɲ,$}Gw Ìp()/

    ˅ևmq] ٛH^"bd0gnެ ׈yZhiiCaD\ߏ ,+)iC?jL΄"snUA2/ ҺkM>V'A^%oֹ "l[2FTt,ArejRtI7?8m´C:ƭO(֢%і؊֥Tm*_fFL°\vKAAtc\50j~})R% \2c$E-]H-4*ѥc1wLcƉ74ҽ)l}mM.S~WvY- &sO51xCH*I_ryTN8&tM6ߗFA؛8BSҦd{}+V?IC]Ħ4i]eGN+ SPx:yd2T(nǟ/3tFWhQl |#^{ǞhXaI=`E0|ڴdΥ( my I -sV4yj )>*<I^G'ҘVj< >* %V1'}3cM 2t$6Gs7/^R:􆲣23uiۿ)3ڏE_5Ʒ+ƣì< ER>̑2"Y6{4k cQ)[81{Q%w'!`ˉ9~T+҃?|W/6_ML:˶iY"}oCWsdQ]gƎvM'Z,o=qxqScf M$v/t\]`:|^M=pVEN j6#;3^w(GE:Xo)̢IsroZUF7`WFPg/TC͙Tˍc;;ڲ<J%R7o-?>5,y^u;u?ͦXMܰlʂZ5t[o%#Jg咉lIq#%/&Vn^=`؛f‹Sof[؏ .Tn#h -7 6QsLn8 1R ff֮)|&U #LJ<;Ƕ+4/7}E8 X¯;\>Su \Mޭ?m/9`K_1u`uWփ;14Zٵ2V:_Ixf^ssnU kT``jm=|PhbdY'󢃫$arp? IDAT eoSh֮Q*@LV"-烚y):o@΋Y}׋P-$I,PөM4qw7' ŧ@۹LD/ tF;}zƲdiL^,}F*I?z˧L⺞!zxUox,fp0Tf)_)3%8ZWanV@v8y?J>A:R&VjTHk a߇Z^kI5I-Kn A*lGϲ>7OsB&{IV^j1fT^֓ݹx3v-IsDӫ]k8aY6K5>F|< e_d1&h8y;{X'V%sZ.ՠLՀiXRo*L#2fYΟ¼S5/(ul=e&7+YdZǩ0cK&cs"%=:RuuU8nXЋc1LԾ`5½ 1>U!+51ѝk1U gA%Gd~ G(S4ͧ4c*f[3yw͗;ŅX4*1v\^ W9kkd]ڤyyw =6FHf'Hzr]b/оV9J <̭X-'1"^YT 3qK!Y +qSɜ#G<̨MUs!g$]}LaC P_3=\ ͘w::dD¨|odĕ j90Y5Y'<-G0i$bNScM H66k5 ߏ!d|'^ӖQ7xeC>1N?]k ̱vYb;q^z>sysC̹!rC"Ƭ'75i~2\ܘn%}DZq7 H/ڿ3Yl3mұ╈rr̹)֌r`nmlI[7*>,sU<==sIb-?C_sq#/`c0cyiNv[%l/޺0MoI %lӮsn $r[=}>yf~c@2rkB_G*\^ĜcM imq*^ omTƭzkFTo` ^ =#ҵMD A  DwMA2Olh巠l^D ܅XuHXQKĜqsnUHL(h:cMgn22dD+ߗtENAj0*S!B66q33;_~3r"(bM !    FNAAAAA5)    B&9AAAAAD#     ٚiZif ;&KAAAAm4}_Jv*9u߁$u>\+Uɨd9'A1;%k #7Ęn97Ę\nW(dq^2/7汈9gMf{vgv2.    B&9AAAAAD#     ٚhAAAA![     dkSAAAAlM4r    FҊ)Gc3;% )8HuN | q%r)VL[Y.2Hrf%'Zd S9GG9by*Nj|fco}`>=v{1sSBzFNM2ȴiĝg)]Bb ϩ'5gN|Cy' `V(!'Ӣl 5cG]@$7zao3r|@^+˧S7%=S8#<ʌNwSFdf$"80yK hԏ;j~?&}>s(~zq:(sk..b1XȠFwB_CB@]3۲<O?0Ŋb/mג>S1>z|PT=f&$OY~b=zQ:S~R»zקZy(T=. 5xo$U=:Εl0D׬smQ7FGy1姿ϋ_V*-/&Cs퇈'EM9K$1KѮFi Qi wx}<@-_*6*q woXڳ :Qr.gtdp5F|$LYӘ NZgVo:Yaa~Jyve[$/Wv2Jd݁m_F _G{MYL 'MgVO):ع4ߒ3Y}ߏ%)2$560DȊrDz러Uo"3QS(PjDz(yx(=hE1s}CٹGFtx?iaR 5Nfۭ*FE)VY]Ȟ$- a[̧mM2l 6Yˇ:KĜcM iAO[*< %ڡ?<.g/rw)^U1[>ƝϪGEjX|dL,Ic2њBV$\q_?fxά2 \?{.w Xņ{FcFc%Xbn.vM1"VP;@QDa^pνs9sffקuʋJ}'1T3':|6EƢiHKe/F¼q%*g+;1I|XZWǿ DlmC*NGMosQwIP\ăEa#q~$1 O}+v{rHNET#nl'z}bw.T0ָqJ.鷢>ol:@b0¢'ɷa'"`NgQ020kW?'֦FZVEs[ #==adSP:d$W+LڰN0䚉/4*2*Ayo}9$91Ljz^~ͫ+Ez%0q;"Άw㚈 3qƜQِ墾h"}~hQgg& 4ah,ʷ%?{_KhŽysuRS8ۙ }Z~1gFͷ|ʮ·p07zah:nJ-CbRr"7u%jU4oAP:C彾tᣯ'N.b{Kwagb(,-'.UNfB_/LlbU:c`waPh8MMjRnNXOhKD+ {S}7qhEln) ~hJ즟?8J*ݢpZ];Fn7Ä墾jѣ7Ew-QXۉ~ߋҦ/R +bu Y[$b;Ϳwcf+*\_Rdb|mkeFw{سu-ϳ߄'G/gx"O=P(Ԡ?3jbGߖ Dˑx% j amK|ԹquVsk. (L?M\SB{Er͏^h {yK/ۈyT ,$6EK'b.szc8vGr+awOꋯs8y~OXO;?unnplU_Yꡉ,ӒoOdS1ekTR)5V9ܩqqkSχh$ҥ'A}A:^(_UtWpG1YO~̥[CdD D Ҫd}1}ܯ1uf_}1De鲫 sCihqfg78.6bbِyg/rE.Rfuk kj6ɴaQ\܀`n6wa_(~տ4ƾMGԛٹl2d:oulP BO_/Ay 'a(*SB,mor5@N;E)Sѝ}'@v.صe} Z2f(_ˮ=fBm[duؼ6ͩaxkɦCa?fQP9U۱K~ȟQ7bAcc3[Y?]W.C̔uvĚ637t) ^R%@5-$wkcY\ֈ+ei˪]BٵwPУ80)*^r%[v{/pn_B>Yٽ.j5/26}$j4=ɜÒ>z?gi|m3;nHC$HIVIRd`&^)a_׸`6LB9rE"yDÜg)TU"xx'&lI?."\#eKK9oГ%Ӵ%g7Rv=r-ug6lZ狕SrW:Թ2O<4*W%f8(ϸu"=KmpŠb rqWxL$`VuK^EkOʭkBg;?L{i46N`a9Zrbyjpηf2+}(WNxtwX6a/jͥdޠ#AjӮ(ڔʍᣃL1: 9 9SCQpH90 8'AĨsdJث zIԑ~$~LXRҬԢe}?ʞ;?9?IXu?QQIsS/JV5tǛ剺ΏڊENqT, RjUy|Ѓ1;yն sʚ1J:{ZEl<BhDk'-I_ k(SyɹLZcXN&Ըd4_իT-A@no*'7nr:C2dKǽRE~ GA9LحG_/v#ObҌCtGMI_^'>Y%e2g'Y%y˜<szcg|Kq66>I6B (J$ ډ|= /G0ppNSģ  '@yS,3+8-3Ҕ_o ʊi_t/N&{Ž,nQO}=rhS%jEYλ5l[Z]ӥb5K<e~: 9N!C} bmeVwP̅r̚=☬t!:4i_7G JOFb=̂ye˱^GWv=A:bႫ< ~fS.sم\$.BEԱ6CCc տc3 Q={ē7 agѼ껫Rw__&m&#:"DQՐ'xx"j/*MC:ܛj99 +6mcXI}ًlnQ\9qkbP#i_r^X@=˯P@Nl,nk#O633g,o|sCt<:9=ui*'XbZ?o#=$/R,HIV;dTݫ3oAr%rT!FѤ= IMWtF?^a\>OO;*[W\tlHX$]D(Z P+/q>GMuP xk9E[ߞqwu1QP8zb5ʂ<QJUri%i7 bW\3A>Ekfɍ,{9AW{GB.x^Jms[ooj5ME';XG$5!OT (&80G@rE!<8PG=Qk z=N6Yʋ@1ˋgr5>|EtO\=2=PlqӱL /)@aŢ{}}ii]% Hf0yzd\g]b[{X-ӯH#EɘFmXhݝKgC]i07xi@EG Jd–8n6̍KEqJjr3R pVÃPb4n/?է m4v13 yE.=t캆 IDAT/ԩ׉O9k˜d%c[Y47;T'"4M ):z:ad M8_gyҌSx*A^ NAtZ4퓙~*,Ǵ.p135vܽuĻV~*,-K\ $00s^NF8{ &ST :x=s~X"Q5Ԫ\xľ#ov:F)\DT.w ws|cvۏ~^ɑԩY/P¥4|||QGo1lkԡtKa58-IYF|*O[|˧ 7pv|hQYȾC:GsA)A2IE}YHe+оMA¶Ĥc/PlqU$IAZ4~26LZ9z΢sxum퇄XdΪGN{)<~ˢ$ѽ"E bQnr̍ scA\^4l^1uN~D< -mRx)AB0h>+.^%)T K[ .|6"GXGp@.(:U{cU 2x*;qС$~LzdH7 ȟoPˋ\8`W9p<M1':y2͚2g'Y%|TON75O^څ,=63/}ThѣZF,C,; ʾ8%rh0VX4>Ph! l` ɉq$8C1(csҧe?viLv1ckZ4-kMVpfM.xEpm;oAǛ;Qv٠ x#e;ScIױBAPͽCm w\Pq0QP[}^ܩ.M'sdUh!7)Wyx 7ř=p/fkWlY٢=.5cik,ۛV9[GD`nhdE .P GY0j}s@4'ׂf\]=mEtB&.hV*{qx|5DA: qۺbUW:`a>م/w3jQD[?fB؍_xi@U-}Ov6쏏8ġ{0krF>=N0E ?Rѵq|]Ww8Qx6FAeaBs@jn4vZO^)CkѿoI*mC|WΔ&3xvST[z$݈=+0k2,N#Bnp?pBopb|f2⌦(S~;7DrKɛ*ܺL-6Xy:+%8"ɪSόV`ڜ?ga3o&!qAO֔9;*fTang6 VNi#=i]!)84/2pxZ.{s9 UR'=cW 篞sR{.UzfOF UQ,uz؎q mą!;:t/>Ŷ&3 ,X72əjPBҌضU1mZL&D!엖^Gh^i~:+3@ڌ5^e1ט׼Ӄ)X2~GsO$֋3Py+&P@!GEĐYݨ11&NxT))oz0J>$NTKlyco=#g c d~bn\o= %*7"~=SzUs%}F3bB BvNwPVjUc򎥨oyDޏ[9*4yNz.;6.wφbV{OSrxfw3г%W5n=̰Bl?oC)OWLRͫӧm2ȐQwD$id?Z-IHK% ujxI! VRXIV 5+Eƍ?hO}aaaA >~TwᇜwY= =1e"_"2_2Re A cBRGA̗6&u>eD{|ݲqK+_"8v'JOv8~Ƽv2CV9;ؿ{]02NNE'W._/.]LHH=XQ};{{(\T6o JN®9քru4~g_%D-N"H$YHmז|u _'9 ބ'X:rO6'#yۮ##&ex|l*</yY$*HY7DH}'H$,/sF|X|蕤ﶳd71?w<Ǭ[}7J3#O$kLj~+=yWD"H$D"H$D""ѩYG9J#D"H$D"H$D"InoδS^n)H$D"H$D"HR&~`S"H$D"H$D"_FND"H$D"H$D"ȩ_R4jFۇEFK"H$D"H$D"|(pQi%͛נQF&U)iԨQ M'g KL񣇳|=ddL(v9;Ș Qqd42gmwe̋垜D"H$D"H$K&/WFND"H$D"H$D"mT~FND"H$D"H$D "_zæJwHD"H$D"H$D-7K߿&L S37$}Oz =9%D"H$D"H$IjpAQTc4pCJ w4rJ$D"H$D"H$I( cƎK!)CFND"H$D"H$DQt~}Zb1_> C)gOΰch5GH$I҈PN:AK/"J$_Ί,7eVLd֟a۫{R˯ǚa$$}C߉/ƒKBGiZ2zZmgε=n>&92bʶV"xjo.V'/vHdxL'R߉~-s^t8u=d@zMT:șQ US:385м$?n)s֖9;ɚɈߓ3 W;z={tg6|$#kvtɋJ9.%nK3h`n8)i{bFN7wYcJ˟py P%Kb)UXglMbh=bfPVF³0ݎt CC(cnbc\(9 10A!XpW~;Ɔ(B>+*O=Vc=φ TUȏ&뾘/c9ƪŎNĥT}/Վ|7{^d~egRDA-F[+*Lƛ T"ZK31v|nXʳ^Zk/v;b$sqSR3/Y䤨_~ Ux-~PVE|_d;<žUh\ qSUؾ JokII/ dfC[ =taK~ ]A#CjwuzbIIBZ=1wc̨.|іYf7!څ Kobp, qW q?," s<'#:ä5z҂bpẈ8 uw &_܃Fd*~F f΀Ě?гs5t+GP-lgjιIcqo!l;6*Mh˙lS;}WFژUԃWܚ>+Sx\/ }kNtU2.zڕ+`_w==GU hPbqtu:wAqKW@!ko]1hp}ڙa[#6uC8{#a&J4H":wC)&ԩs/F8c.7.`R2lݣ}P !_ǰf Yh!.d~ViF6̯S/wÚ>{ϣ0vuG;g5\ cy}=ߙ:bTZ2kiǙm%Qtΰ"3n'Ҿbl7ɽ,u| xR#+OJc B٧4ζ&b\vsNQ8D*UR2eRQJN ?fO-'54QN1ZݭOKqQ$%|Z0qA XRrwtmj3v?cPvp:#3[UF^A9v,b ]0zeJP:2O-`ˣ$2# LnFͺl0n-dO" tg15my1Xǫd)J`6g8{γ N*Q%JPxr~Z%[ˑ~[ʵ#Y< 6s[1ԨP9NC><8_z2dt[o;ŀoLF ۪Zg ];w;#C3e[|>_0БQro LMFiԪ'7`W.NĘcKE+^Gg&]2Ȍ1~.d b=նٕi32* p'}V7O&㯬\sPcCSgkPja"N3UUX_cBٛ~x?F. Jƕx*{fE4~:3 ?粖*e~J+GJ~oݟy {Q4GN|k_mL0uJ{8cS| H}x=x?402\OJp7PO#Th?鸿e-_+7^[{7E~{3 tnÚ4(섅3-w|֪nTt%zX)E0AwC'xleYN<&:F°^27ƕq88+Tkt6va=wgљ+Ҧ/s_w|h5v)s֖9;鱶ETR W\:FΌ]7X=x$_ڀK2zARRcfo'ٚ9G&\TTI"]:S-|[Pzk?EqWKw~ +8exZ|RD D$(HڨI[9'~ˆm?{~YH:eW1,9p׬X|qjl-MN(&8RP,2X*ꁢ"ofuk kj6ɴaQ\܀#Rml63]/wX2gRƾMGt\i{ GE۸EȈQ;>~JNPTȗUե!,mort(۶ѩzGN-U}q@jc!B};xV-! 5Si޼v_[0/M4.\]T7-`R{5,o?t(WөO4QP+%i({7脓JӋT+(f0dT3涎(1Ҹ.Ġqˊѵlw¸q%#SV;3\֞?> 2@B|1$jXtUz~Ï0TK uwXҢ]ϖOsѽ5^즗& B5m"oɬGalMm>LʜeN~z<<W<|Ct5vȁ< &F '{UW^MpStdHͤ_+% NH-ZC߱sCae. \K{a>7ta5]#HW\1nx8P[`߆LWSNOV!o9)D ?s1HGO)߀3n2$[kẼ]ΘkzR"oDI^^ ttMj@˵œجn僩f Pи]z nUWx$*;U V*ɩƽHw{#kOSg; );] *Qwe1t)EA*UR;z0f?6CsNYq: }+88ןC_dz:&fQ2g0yXM1;Z8GE[/ؔ ~Tm) O{Ӣe?̣{:4&֣;\Ǒ'Ek1i!/X;i OJX+@BC5MG>Y%e2g'Y?=Ez|(m>/slk&끖ft-KLGD1n 8I÷v"_rO5r4%/!z:r 60x_4MNϬڶEгJS~Wj(+}8h3r»& ;rاSܾQV,{eV)w7zt|͒wx%ogb߲C2š +Z̻=b~9fMMqLVYIWqJ}՛#ju`%ї^@R[J?fA¸}"vTXx0ܱHP=6VCw?P%F=(es|F#l{xm}{DC=* x"G)*Uq ȥ ӗX/4^Ws%7ƖƺwKjGG@_1 9`UX{y*́ni;݇R4[%`Tք<&st;_i&VHf]A zí* ; k1*|bC\2+΍%xDz>(&80G@rE!<8PG=Qk z=N6Yʋ@1ˋgr5>YEtOuZJe.{غnc͙@^RD+/9}UE=dxӆK &@̤a dκpwݷT0^[_G܋xĵ(Uëil= DM>l]1 H[4zzqq7ٰh5IܵȨ0E>R^1*3yO3~&"}\[`h\B` rX5 S0) *E|9aqk?,y@ݨjjU.oFsb:F)\DT.w ws|cvۏ~^ɑԩY/P¥4|||QGo1lkԡtKa58-IYF|*O[|˧ 7pv|hQYȾC:GsA)A2Ó"}YHe+оMA¶Ĥc/3ҽlqUI%bPrdL Ӹs2X"p&8t3A<0xɂoŠIQϒ.DiKEͷ/߱S̄@tD9ȥ=́#oqǦ'ybCÃ;= kg)sݬ)svUG 7yz.d ~I쳥GjM0eQbىH(7T)+ES¢!7(܆E a`SUONc mbkͱ9ӎ24yt;Ԙ5sGϏ&wо ^sqN]qoX+IV$͝(~;lP<ԑ$X ^ߎr]އȅ;.8(-sl>/sT[9?*+L0Z;;€C1HmF*{ugL^!`,{^IjD&8\3ӛLe;T#aAW`մ6Wb~'kƜbbjL]@;u/@Xp̋mِ VKjCLg9TZ TfQO ж-Nǹhem +bk[3qiPiowŧ˜)?m}Fԧa@u(n85-[ ,I!SϗgR/e͌w# k=c_>8 TgVԠ)e)q@ >ڟ;ue͚I0Pr|C2|h#{QyB<`Txj$]fג(8èxYO}*7clhhɱ{Fz5{2JBR2vOmĝi'6]HUqOܞʖVK|y1?{7b2kͲ_+;nT&,eNdILg?4/7ʺ>SadlGO Sө$"TKɡ;c0zhtfh ?w{ 'p2B|˜<۠qq }}"2Ҫl-] -q.YC낱[;cOc z|ؚ%ݩruK P'3Zp: Q\۞ G;mjH6(ŠՓ9R?=F6ZIʖ#FlOK5MΓ"kTgjGzl܏ֈL_BsfB"2¢\lg l3!1k3fw1 ǏN|Hv13.CȎgCex%ƌ' h ggc>/헌N3}Q.i/ _oZ~W3d嘳S)=88:a`n0ML E+WHԥm[>#.P9!\3 r๹jWI-Hy'"g+;km"+Q^#Lȑ(Q_e8/I%svU$O:wkxvۏSh[ja.-Z.F_>Bd\R !B!.-[Q"#U !#esّcW/"nJ~;;u!$_Sn)!cNI*fs9ln&C!RwB!}7 OT!EJn=-ӑ8 됒k39;*D@!B!B!>O9B!B!4r !B!B!25iB!B!Bdj)B!B!T6S5oJiR1YB!B!B/&4G' {ML E+WHԥm[_QJJNs,d3#/Kv!ƤdC!CH/_Rgˎy,1gm)֔rpNBՅB!B!4r !B!B!25iB!B!Bdj)B!B!ԤS!B!BI#B!B!"SFN!B!B!D}9CO0kk&J!D”N_N5Z+DrU?Oaۍ xCGJm2 zg_ŔϘ{:l&U"Ȓclly\I4}w[gl՞o%svU|4nTx#+3b2/_oL"˩/?}/|rBuSj&?DJDC ,3=͝vw(Ƶu^XcQZL̀7)HBd.1-~N{<ӝ0ՏsN} ZXm2!68?_tg̵-L+ș})D%@wo dԸlNpzVFW9kǜb_"9+&Zjt Lq*C.?#rǥPAMT[ʄ\](=_Ƿ=Yy%o;8, V5ܾ Ԟq!{čc2}jk1k#Gޤ/]lLܚOc i?`pw?}ׇF9&;3 gud4(w+ y,ko[ȨtJ|MZ\OϚu%R*O1YVFզ7:ށN~ wae/z7B9vvχZy> {Cmri0<$ﳺ(DzI 3 {dhLmQ]#|;*f*tW%coBVx=nM9Fuq 5 ~~;3?ͫk^4m$B面$n3 zc艴Ɩ}ɟ%(WL)jfW--j/OUڦ;~|+?EohӚSj~r5IY;HHVTfNRjҼ997 XWcwCbxtd]P]s]*ha^hn3~фKčvQdr8XC?nW;RbE*W .O ;:ޏt+VP^KzOXdOCfb*]Ujx4jw_hv}z,V*;~ ֓}Z"~; 놣?jQE捬^E.i#qRTl:kg1'3ΰx!aۨ'sVe gG]ӿVTl6xwѱĵLYʖ+]tZ4Z4/xB}ʸ*ER(U(3cg]2-jǍ5pOABhw'| .5ݒ:UP(gܯo{z=ե5iK 1aƖPNSDMM ~=]=( }7bd1ǙXg&uc̙<ۧ}Of4Ŕ->̙Iw"G% o#O2vuIDmkib;Z˪{2쎌m ijEuê<^*h1%=}$2gס%PWC ؟vf]nDp,Y`auʾ9hרͿŜ^?Dž5d5YϷW72ȳ,90rfe\xWRshBKɅ.94̧?MŮ7"W/)::X:RnDk^.~#`4'1o>XN{ˋ8~(VZz@/:2pADZBO;J%ЇY`4Ϲuh!EV- }Y}y"kisp*wvD'6Ba/~VhFj@!eZ0wh-]WtӚ Nաb}솛C'. SG'lN[Pl!ug,_y74l<O*hL <R>3vcwNd`j̕L}x" 5?Y(.&wwnFA^RZ6q¶%4;|MDx8"QXIny.n]+r;12Ґ45+)Nە!_._Cܱʼ) %TϨ#Ore'<I&t.ض{#OM `I%::0v(@,ئ $Zнv7dC}i27BK֝Y/elӂ|.4ϲd$&zgolԀi1lǨA'?1w`P*e>TΡBm_W9tn Skxx%ΗofNsƴڠ؛+LoވYi~:^EtxZ(& 3vnƘ;ٹb oϣMzE6}*[ϫ-&m˪vtMɛؽe&vӷvs}V9u[-`uܜ=צ|s֎9;*R6ko\A 8N fE\/hhвc ī&j!K&EQaX~x߀nXWo4hnʢ}(^Up^̢Ǩ1ܾ(%Ok1A+,;/LjaՐKX>-(ܗQc,O?ڱodDxMJh:vJ>1Ԧs™~+7JJCctŦIkyU$8t0W} AaBÎ݋\= Aekm|5͉rQ< &Z+76xȉ:ghZsDF?|ʂ -NA@Q-~z#+M0c.P^S5ʹbؕrE|APbWޟ- - Hyzk,Wrн̘~{?X?n10&-bqeJEM[#ᆁ-k:?=CcH^~1\[9mZ]0 ʱ! y hB`M<=HR'cjkJA j`sk56LdVӦ4a]7Yf-e4Cҕ-mg\}6UfQϝ@M.8_?tnb柱yV]~|dtHtq@܋su*vDunlY>g(| V]࿐Q3ilbb`v<-^kGl5['恵xh`_^zmgafĜcN# χPn#ВM=];Ksas a):v@N\NN毶ҳp?|Fo#mbBǛ6MQSy2$Gv.cPsxlɊ۱tʎ`z<톽6:T}Ś6_yE> *vS͜hy}q 1+;oIoSæKU֬z/^⯃L7d$"92XXX`aS',iD P A1)CZg:DZcR bG02 )='^$vc g[x4z>沋FfE(R%~F: }w*5=Jy 5ˤ9hG)4sYgOp/I4G__}}C '?ɣ)%88~0fY4M-/Hw®0E% ž.Fxbh奠>QORQ` ZTD6|m|֐&]R6+n~:mA9CP9{5}ksܨw4<:wZ xT2sK?9m7+ L7&$svU| ߜh NhcThk'?*j7Qx'C1q6[<0y{[J|7åh1rJPZUM\PhcPߑ+R*axMH9#u y^i/qj#1єf-VR-AN-"]ƻ2{-!gĘsk|YTrOcвFpx7@G, ۢ[z52yHA {Gy˺#6ymOԡ=$`o=n9ǔjȨOhzojjI?/ &.UɃ9yT26)ɇk;!,8 v:X٠Lc #ht͙#KPtk10L˻Xhǎ OIsكk |`hg`%}e6j+g,4lk }3B"I4'}2B+{>=(K H9`n.$mʞo::q# >fX{L?Wo.N3Y'Ye/pͩŃ_dҥ 11hxW(^J:>_d#31EQPo)3 i5Ư^RP-t^4DqHiF$svU+u KOϳ<64iHA'00Ā7 _͍sqjŐ=\(˖"ѧl&5oy z%QCz0K~Gus*]h[(fs\  UQf 9UP"??1.~ng䪇-xZEqs>ġ;Z p.xWSZ$w߆Wws, ~0PS9TJժUZ8ޢXiH$ơx"38J~jynНwycOXJvjLW8$QC\RJI)|My.8 *tTНzGt95c"^:Сk_U]Ƭӡ D۹ L_p޿ByC8d]kd%uVvgZvO&6p&J_1)HBsɃy2~}eҴMϗɨ7+8w[Moٽ,ŊBa/i>W)~?<-4`lTh硠3hY jQ*` ?ugapؓ\UVl957rP[(-c&{ZBLZˀIN,ޣ,0q{f(N>ꆉ /Ni ^+'*Z8<9Mm r<9 a7;8cZk%뽌t[ibش]'4`RXn ܞj@g6E]?Q؇Itt)Dzѣ܈:ր+r~ћoYȂ)2x'?3:s1>ԂTjoi^ K[]?_;Gg2T߉N]ј{1lVvB/#WS=v2o3 5<\{j6E]֤;CPN/:W2MSsh.l}3UzsB(,P,W g}+ [hعKk^0gKg[Ɵ_<>Ҋ;FþձS>?[{N=j{(ʼkTsz0 Q5W.k|~N~JaԎ I'㋾u164u4XV=#n~=Lٽc`%!zyv6B;|AO lH4=bU-b`&nd)z3ɄWw"ݨL*2jY̝u=;p+]&~h^(Eo3u},<ӟ2&SIEFCw`̜nA~'NZeZQ9y.A:.4YREVi'yOrw`!-o7Fy؜NI4r~rζEg-XF$ޜ:u0z,^0 3 2uIZ9Lڍߠ2&oIw:D/yiU'6i<զF~jԮpC.$ ]+g*6 Q6")ׂl8Ԏظ&L 5$_7>E;dDEfgBc fDxاF0$1ʪ1gXM)͚79 8TO)Y\ۉ|guc{ԉ/1fF_2TRe1sv1ĘDD7_Z drl~)<pO{qp$_"|`:O f!+ǜbM) uibb( ^G*.Tlۺ5`"/Esa=3p,fyقwB!}s{Zf#)R'gBF/ÉxNJN_/1g혳S"yҹ[ó~lEzըT #vir6/BN!D~mqiي2i G# &9k ҕvvCH ι&1zSBJǜbɓΗP5vrMB:)Bd!n,ީ:$?EҡҴDOKt$Τv:%(bN/g.B!B!BϓFN!B!B!D&B!B!BLM9B!B!4r !B!B!25զf͛w8p33TLB!B!K Cd^FQ<<4erc3¸ wk uu1YYvTBd.1-~N{<ӝ0ՏsN} ZXz~ۯiqaˑB7Q%o*rt`~KTszd4QBw=T*UZ{82w;7JPCKp܉YP^b7Up0@47;iO(z9)֛g?sy ^.Xc(ʛ$6֛Ԡ㖉kc;դpNcU!_]5bK k_}eG-ߓ!&qu<vRw%l(Jʂ*SKy(# [,~Ûu amdi tu"*^Wu, 1tF\ M Hoof^(c.%]|c;a>/KC= rQ&A"My&g?1Wfө7Jq}'Ѽv,w;J@]#k73H,ӊEM(0؟:6eaZ!IA+*C[\\p5䋯\cvm<ʽ-4i/Jߊ8@7rry|R>W/XV[?ӥRy4/~?RMP鑫9Fuqڌ=3k3O=} cedž Yi>sjpѺQLaz7g'ѴZį Cѫl?7P=8Xr,.|׍!\XIjWV;} uo:ZoykYF[ Wb70-z1%uߙhޗVo83#N֠AIw!w}l!I'U/Ui9s? ukl«"kbH <=ĨfXUߌx:#65Yڑ#oRs {ɮC gS=°]Xcً 14 A& +;qaR`E;8e1S[g_ /UI؛ﻐo;exn~FzA.gjX̙Iw"v<'RP;Wwy6 ˵41ieՍ=vGF^mni~;-LYuE90Ъ0gI3; 7w8MYA);KeHي\ώX?#/py{u#<C+gjZƅWqu/9&j\kJ|:cZt]z/-}8ؘ#UԋtlOmYt\[C:8ژC'6>Q<&E1A؎ nm^-if>fyjAūnh`ZJ8XgETT:Շp~r ,s`my DJ¡fe.@_&9H  (}# s>ї"кrAl _ӏHY;HHꎚUQiOUyOBFPhNuc޶}۱9p&a5Ph<^u@eB&㳏cGw3߁Fw'X2z3*G|4^9UЕƯr)0Mfy>I 0{GXQ;P}B9޵PƶtirG_k'@.zF<w o0s$r $pZ:Eyl۽q!F$R&ckzsض WOC ԋ5PNn& GhBgVnVģBl]HCخӒP^..YwfܾM faó?I'5 f@Z z>ۼ1jІ:Ow?z$?mC{0sPWU:C)zs[Cq e16(4 ӛ7ct߶ΣW+(Ovǽ5*Z_/ES_ ?É]i:mfg@}*[ϫ-&m˪vtMɛؽe&vӷvs}P9u[-`uܜ=צ|s֎9;*R6ko\A 8N fE\/==j~_>Jhhвc h1\[9mZ]0 ʱ! y hB`M<=HR'cjkJA j`sk56LdVӦ4~~[6h@-x'+[G9q'm Q;e.<9\q~ ]?cI=>m4t?^|AnȆuY?oT5]Rl>m:%C \~YTdAٲ|PtqD?P!u?j%:5I{uevWiL}^ul'ARYc [kKCEQPP>Ұɟ Kk.֍̕ᔳGGט~ 9mu(73l NIe2dI\ƠْcGڙx6 {#mt 䵕59mNc,4,ʇ^c{7h뚓ʼ6vA[;oIoSæKU֬z/^⯃L7d$"92XXX`aS',iD P A1)CZg:DZcR ;wŎ`T%eSz OupI+;ζ4i&}eI͊%~F: }w*5=Jy 5ˤ9E]~.p1Gw|+G`z QxO",>Rx?oO-O)<}A 77p΢1mjAv݇a-*Q85Tt0 E+/z  rZ$*&3(ݩio[ča\8s4e-}ײMF|qdZR>g*[pQy'x3qe=|&҄ĜcNH78u9cB1OM+xL'L|j+g,4l$w|MϽ["CbFbHH̠؛RjMĞBlGQE;{F%_Us}9yγND(!Vu%#/~@:FA=)ra#‹/4mY6&au'J ]@mAbeS +Щ1SH7tUfX.x34fٸ9 5Q'~g 9˶d-;G"mntV;^ ~ZŻ=)ɓhFbMS 7a7u)QkW[]p1]91;Izz,wnqOczÐw=Vt@e]sBθ$N{[t4!<|ݏ}'Z=rdT8Tfy)UcMcDۈ>.h_kzR\֠.D!-k|42,W5X+胾2װsyMcPH0ތ/&Uq-26=R ëtzGٲ#LtQ.e7]=q/h><6GQ_mI8EۖV=d&n9t f*]Ѡ] {( |$hvs3n&U[ c9P{3r Wg;׿BGAQtn!BehcP3wY)VԽ(/9:fr|4 ME }(L1Fehn SSP%RLI|\jS9MzD UXr$i HWw:J6$/r |nC<)PT**c/g1xv*ͦN$=IVHtYԴ3@CNe兗^njevvu(TX]$,Ag#^r*[u.O G˞Lh=&uѪ8D$H;Զ%(+bEl_$H[JwLS?\A'yL6ئj}J_=E. ~05;:rS}^qhd[9ɡEYB%p]ͻ78{s!tvr=ɵ=/5eÉ)T5KyX6wr%GcFU4<1ROk~sR=Ex|-)Q﯆ǰ58-yuCƲ8=Iǣ8<9NQu|(uc?[9cM79`_oJLB׻>(-6Zt]}~K4ב] cG6Ffl~ ]Iquvi46a/0wct mGK;V (뿀!Gj3׻YGL!hfoK~[ Φƌ +[]?A`x5&עoxlK[1t0Ή ܨC݅(е!>MyyGքr2!ӚPHGUǾԟ۞ :ֿ9ex}ϝyu.bk,oѻI,B*XVɥǠ+ޥ.kvxv{Z0ɦA྽ؾ`Fyq綠o> nH,2kEz1zh1լOщTT-ήɑlTŲ!RB$>EzcVU=Ϟ YNn9>v9W?cRq4i7yyx> 'FJdΘRRS gӲl&X:WKf񣍇]"_®Ɩ8U,.5K0bDf|odJ1hD#GlL^Y$wx|0~ yNp! ]=?NůB5X\&,1t8&LȢoPڐD5j:\/lZ׈?\|rcj#]lu(Ug'3d]:{ڜY%L2I̱2kY)VjݚoF&޽{BŔO_;18g/}_S't$+Ę!] 1g JOƇ/2=΍w<&_ځ^I< Eno}0<@XJbsV5۵N'LѠU\O*.Tlڸ+g5~V/![{B tR:]Vk_+5zIS.?560q&.I#g֥zKT> IDAT<̙])NS:8ɩ5}I>\%sVU|j|.y1AVgdSijA>d= L]YYaݕ|_3.pv.NNK#tjtPrs1: 4fS6./t10&k DZ5WP&vj}LrQī~q=*# Bλ@&ҹFqrb`]=o.-ϧƆ(J+&z&FjT*?FYM-h[ZDʻ*jUnRұ)G{YU =]Dtƥ>E~.q*^S]I@䱶k]<M_Vqc-}j؝ vڳ̪  O{3lc@0vNDv7HܾG)֩ŐsڇC1y7„lή^X A_O] Y\2ڡ-j$ϛ3L[t!=Qfs8ӾƎc6q _*䫝)Jao3s['oL:qqOe;?SޗY|h[ҡZdW}t}_Ov[aݧfNs9+*?RF3ci Ǚm5QwЛit.>Fp(T?6S &Fܨ<]g~< Kv. zX9VLfzU|E˜(u#_><@ou*7ѱ@HV} xyU§n \αKv5ݾͯغlWy، `kvp6u2kwVJƽ9sPx}x깗ģ`Ӏ_8C/pR,Y25Nu>vQڛ\رTlq+]2ebjw6]ΝAw-~<¡Xy}G2͈EJ=7-Za,QBhk[ ™ѵ 5*pOG)1l5%Rng/L &R Z:֤2c_9[˘!c3u-j ];w.PٛR,024þx]lMsErjlG͏;Oyok?o\ۉŎ'W68 ^ g o߆}F#u;3QЉ!AvtrfCoܞF/d (8> U ZclhC_{$6ٺ yc |餙kkm Lb\g{zoZ֭MSޥ'9&/C&[;S|ν{)? ^ErH}x<ϡ+h`dDÕ0V]q5PO#T0/]rZ 7VG~*ݚkZÍfͱ-k(h.ͤA, 7xlnjaa!yt>ūnhC1'F夔LSxo I}Ot@~KC (ͷhTtp\[i$)aY޻6r`hbK,8όQ1f bkje>ZO:]Fb1gXEBR[ea4{HrOC9nߑv{ZYg?ORmM_rE.~@eKL͑;X>JwJ a0g#{S,i#ss"w*zZ2w".a͵ʩ͂8Qk1 G/6@ adΒ@R=P{J%ߵPƮT)r\k'!,m[N\M6ػ(i>v,HkWԋ5PVn `XurR^5G /?T#0i~~=h7B 6Xm/6ʈT6if(Yݿflm4[иowٴ6ͩ-9ƃabnϮBPF.f@@?Ӯ2bovv魳8)V<9̤Fumo෍3RD5G1iۼE˗KuaߞIoUY j8ѕ@>H[VӺ[ǎ Shf13ǷBt>/g,홋?y\^b1gXEBRq07܋5k){J@NDq=H/eve+5WiN+.ǏC[JD?EV->ұPjs3j5( "]XQLirhr(5v .mPKUHR^,7Ϸl}E'WCI1:S/̝rJS=D+QDD&\T"Ԙo'Eϻ C?&S*h8A!]y?n{` Srp[r{[ D>'BntF6v&JDŽM6Үwit ^՚M <<|ii8m&O6х! Q,q7VmCTQ(*;嫱]̕?xӇ!Vo9UGZ2Z$QG~ɏ5 ]ީ0*ZV}*zK`ޮU0c. P^S5˺aԍEtX2ƾ`19?9c+?:-Z.W*)X`b8b`O z`vυcqϔmJC Y)ᆁ%k:?韖`L5~L~-&X>jew?.rxfm0,X>yW$)j\դTn*Tۑ =iN~uUoVQvm8;!ܨ?ʒlc:*Tetʅxr?oM¬SVt>&T@^|FJ4(ݢ/@ fnF6un}Y|NW)e)4*COb.=ZoXǘzռyZ\:jRǷCNXӺ +«X0ը=?mii9&hn]$;zAÉ)oTt[ӷ*7aQfzeY1M.zFxmbCۄ7}߾ϳP7:.9mtaqodfg[D!3'ӭ̇MX|^ŭ}7s[aБ$'YQS+KK,--Ŝ_Yܯ Z֬4;˭eCk%['Ij^JLHT6 "(蟿+P(/,TN8Ӑ_70c.HjDnf( *U'mS īZiwS٧?Ns.ad!u-=jlcf]LwIsW:Vbhhˀ MO)P~ e=W1t'.z{ۆlT{p" E' ~Z-.^$D rIThSo_%?B^xEr/[UI.?m)= Qhyt wuS) xJpѿ+Mp0+-1W!1gR '.`Z0>Ԍ!FBW7PP£?2>c Gդ77S~{ -FN5+e0KqwqcFRg}Ө"#U49uGy^\Go˒W1QP?qPJj +CbeXَNG>~v9gQ**1݋qw֟_EO7vTב15 QF˚3 U  %Fm팓. HaI_M:ᯀafei~ƗHtf˪|0EeB=tFƽE)~\4/>ϾmoՃKfC˻`ҥ  Zޕ0JROW$HLEQTi>[Beȥ8\ɏ>\oE!welW⃆ ïĜcJn˙cF,7| )bѣ׺%)F񆗡Ѽo!gyԊ?ԏ&9nN׾!U bD !k YJ ZN3D/Ws5Є$ΪRV)DGX|f>5PTIl6u"I= z_貌iʚ3@CNe[1kԡlSauq,^%U9lxf#nY3@s{?4}pI:+;&<՘{(|_{;T(<> wOݑ—wi"c.Я@Y0}dtyxekNLætP#2@˦yXh"SObGu.MI=pȚDϢuBl*[zݵ6Y|%{άOI=ze WH9?(V O̝R?uh8;~E7+σ9s[f(tsjzǧRZJ"a8؄ ~Q/I9=1,՟vx(nn+Z׉[Dq'O)&VUׅRtY|S823wY)V+Ei?:t[<gɁWic}G˞Lh=&uѪ8D$H;Զ%(+bEl_$H[JwLS?\A'yL6ئj+/orh ֚pBCnrb\f4{Z čXQxp(z:D=ޥ},>}G*8qPOWq|KUK-gNrt=7"ZK\j }s^4\LTdMAGkyšٓ9oU&fU };yt 7U̙j9-C7+[zRS6BU{le(hc{Y=q*Wr4fTEC@˃SXx//e a+0w]<'ՋPMp GֈrD--lZ&L8ARG7uQLJQg>3tc .ycbb# U*KޕX:D}l>|`t]a3.fwu$UgtMѧTga_ÌߍY5qL-z_w"PCfD-wI CІ9x"2#3*MUÓ=V<~jLE>ٖcaQ QJ7k)X;b*W\{3[ǽ&[b˖y=I?uPuKؠaS^w/[y'ڼ9Z q?~ޜe԰tHV94RbN.}O?U_I.u1/\ó]8_SiɮGӣ"c{'߷Cy}s7GL/WOpȲ)köz?ydrzvkNq}{ᙃܓ`H2:}9ic;&5G z8Wc;|lN9+* 9՘ؠ}6-FoCJyd?x%E*cǫ>VKaA5u+_po]jF׮8mfԥ^,q-kbTc[3#}0r#v6d{'UcshZi>Ɩ8U, ˩aw?Ĩ526 e}'k@7*'D6oٿ;Tzې;ndU׺2pV|4: +^9Ͳɳr/-frk4ƜbOҕh=/Ka!@ӫ̚6͖~=zV߯МL7*3\xuTP97UxbDfDZT lzҿWCcKfʻ93+=j1*bKX/LkKgecn/A04CuqŒΎʎO/u8(l6'ʂw"ͨ<4gaʱtB5ݪuبp(*Z %leQJݡX8{r>F{ 'p6B|K?ۇ?Sxo`c6NY}=ɫ5K0bDf|odJ1hD#GlL^Y$wx|0~ _ǩ՞BFK[K$?1τ Y *SFM用%/>Ϟlwc~ }>O@PNo@#VFoGݝ2Y ~1nt12 IDAT fLr{<݊$wȈ ofwF.OQ*8Tǿr'|R#dce֘Rպ5+F>kM{baaG)"Twbp^Z?z8uKGB.KRޥ Y1sV1ĘDKi|"3ܘOZH)q4APJi/r{{웅1Z|ƊW3d昳R)e߮8w8y{Zpe|||R9u@bƍi u>(+P#٩+S./ȺByD{έhyOG 5re{V32֩45GH2ȞB `JޮUf,X0PJߙEbc\M}g۸b`jM^*pa4tBA[byQh{D:(NNsC LsR+%NF4-Cn  p7D|Blb\zxҮ[# =ϊɓ #3\3Ҩ㠣BϠ Ny4k j[hUՕ<֖T-Gs?r`3Asym;Ei<퇎{XMp%&uᛒy4Cgzw2Wd,sQ3 "ͼ1&sf0E '*R+HTsv)%Qv402'|˿4q7KёXs9+*'Z˫',ғCٿs iNkJy:G.2u qE U4e#(op.T|c_gΉ 鹝q G&+X:7aŴ9=-V(~ p ~`ֆl[U5@xGN3~f=aҎ9 gi`8sGrmq++ A%zLYﻆSQ@ݵ cLaٰa.giRP^ƲsNܹؿS [awl:t^ĩSw33jU~)OS24[͇`^K왨_Poqru_浣b!mȻ֜ מ%k0vftA_WC1ա6ZM܍:=|u|Ay'ҏSY:?Dߧ3Vc՜6?ɄT/A_۟Rǫ*S;%ڒPՃiUޜaB2*b _6 궼|g8W6e[:3cB[ :53HH6(,pw4П;Og@>Όlˠ6L( Nw7ʆCreo4!}į5 bG:hP^wYM3ʱgL_rb><@ou*7ѱ@aόYy~4ِ~1s?i\^a3.G3YC÷zUj40xxcOͭ=vݒCW Jzb쐆>± 189iYvLKۺ)W2jv+ɗu0xP@бq^?ALvgJd?Wgk_")SL쟛=7-Za,QBhk[ ™ѵ 5*pOG)1l5%Rng/L &R Z:֤2c_9[˘!c3u-j ];w.PٛR,024þx]l"95#Oǝ'<Ƿ7@g.b;Z7:N%o۷h_QHkL?dg'tbHݷ}и> Y93BP>^K6ٺ yc 'G,ZK4\2cgKV1VBYtp\[i$g)aY޻6r`hbK,8ό siUG[s 0O39AKSwv Yؚaσ֓uW3wY)V5s0Yc=$T FMٽeγ'@gx"FRz2åA_hf{V J;dĨ|sX|$UPXѠXUA K㝍AGV˶inc7NẐvKt䎆2gI |'PR9)9 RP0aᦼd\{6.XgNR &'JЋ֪}`-OB)^:7!;ݘ%LTeeBT8!8vG1Fǹ*U9xh9xHjT{?*тѹuZ^R*6*=K7'*Sb "_T J+-֤ߜ m ]cZ̼!WȘdUd` >(Ux-V,:8]7~>tvmsŞ'l s"/^BȸP1-a (.Nl@;l?M;O(KVêYG6D^cƣ֛Φ=رt"] ܛjru kPNN 1&"O!4&q+~-}:nKFu1`=7b؛)9E1kٹ7֌fK-R#5eݷ=sq'_:K̙;HH/f{Q]Vܸ} 8O a\w镃(źzNuWb㽦C*rW{h;%rÖBcV7fjPE'F$Mmflsؠ<~HH\Ѓ!۲1*?XooFNݓbqGʪaǺ8ӈzxО[~+igw|MϹىȐH+VTU͖UR*EPڄFPEmX!$2# Av~5mssν9<Ϲ<|+ސ[>>|?3y+Kf̅jX!f*qVŧLFvC9?LZ W4=V6b҉yh.k;0pg 壯T){w(M>֞ CKsYܽK\Knr;DY-r㬋 } dH 7S&iXl}0joOغ.V0}{xOG_aSʞ&p;Ьp-ZR/{ ,+U&B˓33d;ӧŭ>~uQN{(u`B-hP6B4ZӥOf` 1&$OVe{>3>]c ͣkY#1q)׭aB ]WSf|sT].k@]wԩ?V oL];{Mo|{4J ߹ߩ;Z.1g)V>"rD$8- A BX@X6+tδ_p Nci7_5{)(D*fWeQ2|ߴխ^Nklݴ%ӯv[~3Lx2ҵC5 {GX7dbn=(X իBrt8WY+'Ǚٺ!cta՚qz*}K7y.'4鴈k1:;8c܈y^Vd5p ܹymEtmwTjGKlJ`7(⭲ؗPũdZ~ =)ХTw]a<\^t50}X$6"7;RJhZm?9lw9Rۧ<q2aRNZV=I[RBYKc[W6Ǐsݤ2>bSj8εxo8Q܋"qL(6t ,,wnrmov9{lLWHΧ9W$6u"_ &{|Y|&bOal Lƨh^*t#^߲t,u "n+o=xm^#3Kb^5DI3Nє:;DRx w aSt_u|]Gߢ㌫jYTnۋo^dvnAԛaS? @L$h< G`LJЬ{q™2-F^ \:%L3 -GM/ZSSyWoj ʀ(2!_2+"ЎI;fcU=z cY}?獛E,qA; K+,yʣ/FqnϚSlIJW*G7D iNO*8"}_SCs],v! ~L:F~}(z*={`B|\[673_ݘwHZRҵxUhʓ~W*][2k)gbvq Z$g>;ac͝)ʔt.sp ) >T5>y=k1}^LC3bE/ՎuGfPצ$zaV߯197caet),Fov8i.-ţ=|7,zjAf~w0X'tT\4h,lA. =|&p@LX43$ vg{K'4t1!8=<TЃHgɇ#0b0j?Gm|.WSމbN呋@c6bᄐWW[79dAXS{\>݆߭>ҺZatϸ{6VNt'^66Mhc/k^G:ZKge !23u|ܯ.ufZI٭&Bs&N-?'FJ̱g4Krȑ+f[ҡ\tfػwLJ^. |Kx(CKcELfjZ ~ѳ\.pvSuϲ=}jYZGYˠ8=~cdG)Y.1jֹ)'?1RCo%jۇv #Y:ÿ!Ϡ1)G?*7FEZZ2yԤ]|1_>O_:BD[=`Fly^3B$i :^++w`2rS?o+ Tm?&3WP4f7F_v_lp+Q?-=I&9Zv٘bhkVPZNќ;w}m_/f@_&b 1fE]2te 1l 1e J"vI[R:o'ž)<BS0 9fc5ھ BJ[( JqY|||ҹt@ذ~[xPOfIS<=|skXىwB!"'ݽ- C="QK;fcU$O ܿxaMi&]MW"H{'B!2N(*q%tG%+]])y5i8Ĝv:fcU$O'úV3BB!jS!rjF#a%cZĜ]HLQ]W=Y>N1q;J;fcU[Nr*|H\ՙt*'?'_%~6쓟[8"uRީ.⃷*+1V̋)9=tkʅW,VffXô3Vc64FEWS)m)4Iwel6_蒤Sv (c!{{ue7l<ʭo3EzSD?#@O(S&8љcwH4Jr>cKؚ0́sኼe2"ܱ ŊVK[k)Z0 >mރ}_ WM1K=ˆ]eOhXVfZ9L=>c,4|秸]k3Hn>e\lkqLv'y0Y $$ A2w /@TɟKkgJ4K݆'%9WCĽȯ;vZ鐴ϭ,_ۿE5%Rމ%_OEp2.}ƿyg1WvsF._l\DDEPmfey"3,d i=iX.?Y£J3+mZ%m)2ď56;Q8ۘci_JmFz:oB>4y-tYӭ(Uc4?Ѭ\(\\HqϥټrZi+쭖{~iҫ'Rh^2ٗw5iNp>{gn w^Í崰яAomfBvMӑXs٘bɓF߶ < g_ViKSb徭{gK\/ɩi?M}/c~͐"].Z؁C`/},7))<=Δ yԎgǍNrS5'Ԉ[Jh)G`@:A2cirZ=0 3RrkԵ1y[w[:Oa 2ӞsaqWZ ==3cE)F2zGNY8\<۠8ܺF_:uo7_"ӣhw'Dq]!;_R<~(˟Өi ex.Г)< oNQt*g'C笣h8]/ [O`ԕy(8Kƞf^/,ߘ>zws̎>(AnN[rEp?[N<=z!iwsmwZj]ug#PQ#[/w5 AcSi[iSq&J"^\Yލskrm썢wך+Tdx|,?oݤ|--;ߪkQq^6)CBo~@ p7WGT=\YX?=JVJB1nT݋WRշxQ YNYP3.GosT%s7Ηkm릜̫闣}#_.!!\G2T^@HYG!jK7eZlfՠʭieDTZ95s -sK-otUݎТʵN)y~bCZ~OA,il̽ԔoHm[EDsu{QcekQ3=hRgƫf-u흙2s+eij,x1foonD,N}vT\y >&i8ksDq~s#>\u3Nyf*gj-}ⵧ~QUTVұc:{"|_QMY*Sk5 }L5P-GG:, UzwWջVJDs*+՚/wg=2ӛ+_ *lռaWֹUCՆ_|TrK e7W9UTmީ{ՔTMk*7W>IKvy\ LmfNs\ejWemr>_=9~LuUfz3eT@Zr1R)/KPƞ&զՖa Tn*қs.~P顜m̔,[z][ ([zjT[ެpuh>߮z*:U1+7jׄGTs spQF2ʥV njijHHm<îݙظN?&4XD'8s gNƐR`]oev[~xCo_7W>N cw™GG8gR 6o¢8h1:4@yK|1ڊw%Ka YcM%pH-/nL13p.Qpm ( exr.O^/<P>c^O͜;Hf?%/ )ԨN^sVknyCWM9b1K]P$Nyz+;ŽwW!xrėS,>uc :>1hZ޺IH'<m 4`ȼu (vYvbŬs_~%Kct8kؽڞk96t$-.{x1C?/Ny-gK8Ù3g8;w2>̢@Ι-h0x7]aJS]|+ \}oٰc[NcxM=6qjƴ(UF'_C ν ĨHBn`cOU[ wVQjږi1҇#@=͜ՙEwmYp;Sg*+'6&j@ zu6úq{͙rG]!T /3zQQCV/f^_v<|K?=%gX!+|1'_;ѷ!FeZ릌:@o94Y:ߣ@N"Y( EA4hGubծ,훗ãZcyۆk,>el+B_6U=^1_ܫ&0Et㫎C8}GdF~j|7x2*;54fVo͜_n/qLs٘bI+P_f7Xb8V iX`*K'.q a;Wt=L;&ZpbkO!@S%F9NS Fsq%N%79?ޝ"M欋qEp>\$Q:4{ķB; ˒f@- [wbyJ&u"(^9km*Pӄ.pИEKQ퉆{ ,Ex,d;ӧŭ>~uQN{(u`B-hP6B4ZӥOa `P~ߦTĞ~0ʀ?ҽ ŅMaIW.N=+ps7%^9ifaQ6}!IRp].k@]wԩ?V oLݘ72UGsdϋI;]Ը1uPfmbLkCSa=*Sq[|yڹfݟt߶q>}g*ft U7GcvS:[Äfyrt#K>稉IZ%1g)V>"rD$8- A BX@X2R @ެЍQ0_ \7[sYAtkE)q=Kr,2|ߴխ^Nklݴ%ӯv[~seXx2ҵC5 {GX7dbn=(X իBrt8WY q'Ǚٺ!cta՚qz*}K7y.'4鴈k1=&+8X{{wnļk/+2Xpȁ`8ܼ"m6ŻBY*5%| 6LfWožog*N%~~/I s">,6ҟǻ^E^\S?8JK:C-R9MseG_e$?GofѰ \YAgN#"8X`aa%qǣX!t(Yh_ջ/wB/fxjȗn3$a%I~zXq/GwϜ!5&rBF .^=~*A9N цθYlK7oyF䝛58~k&UiWMrHs |7r<8t Fh}4ZJژQӱ =~&T$q{kED::9{lLW9W$6uDFi/F|y=h>,>fΧ0u\OO> ?CŰ[`, x#, ҿh]7}mƱѥu(}(n[Ʀ׈j̒W D 8xd4eΎ*T%ީm]W@Dѷ8Z@|֦;⛦][f1u7C<a&4kkj%™2-F^ \茫 l'IE 4gW3bS*1z^ɉ_Vq1OkLFD33SyDp$m| =N@X`n¾ 5.s&yb@挫)\1 N L$x ).(NͶz?N`Q6;J[JۻhzL`0猃Tb <Ɲ!;]b >+yч<3"|5O:B+k?0g'K%;NvYRd jzcq>]C1@_zǮHn=1#3c^էkjSLܥ)De\N,t%sۄzbnRCTxq!svie'VaJOlR/]E.Mvd$hWeJTԸO}Ps>#gL OG"C.ۓY&sf#_}z*^$}Lms٘b/oqpXVGny&4}Ys-IJ(`fo0S:ܛ dTfbQOs& by_$3yQ]qu2nzH Vs_ V87;4t""<P<;ŋx_'էotQ|(A|~ {tp=2IWmI8Wl=e8;}c 4 W7xW/[lFeoWqz V{iODl8wg.o5/؊ZRŻR vdc:JTTsa[ś2soPvVi5H,!5]Hd3f5ک8!&1ڭ'> ص MנC<2G)!|9gz'0.O8)Y'}~]sM2<%q$[`YR\1ϿE)^˟bș/Y8֜ueI;E_QӱtWR%Qs p)_v-p~t0NT,]2}^ y*Y= 0c>-ѯn} :?z*~~x4^M6 G^tvP۞x>WzZ8iU׀Z,KDilje􃴜|No3rnJssA=tf`|8 c-3yض[ɇrE>5(T/4fl#/.{5ܱ~B` IDATzu:C61:Y5Gmj)AZi#1x`EjD' |emSބ?>6uH5uUcgr&T|9?YsfG=у8Ep#3{5g Z4߅->"dh{*_n:,Ӳ4zO9r3FA>6mjRj|osފl5gp4`ˊ4NGDvegɋ_jZ/}ѷW{L՘8@%}Ld 9V٘bfIN9rl\:ԛ{"%pi[e`}ݽ(bIWlM3|;z6ޥ4nJ.Yv'OP4K;\KQ?uջ ߊ1v[K|7iN vALGm=t8Sٻ27F7x3=*bf&z1_蛯c^=OrF0kgTn̄mc_%:ilR+7>gȗsT{(OAr q}\ļ/v #Y:ÿ!Ϡ1lֽy?}D!ƬHK撮CڻL7!Ƹ!^184Ϻ[h קf^os<ۏb!͞ŬgEoq~C {D0? KrօĜc6XEd>`_N^ϹA<&w]qMS>Bd^ !FjmukG2+ e< Co_`äxJSQT3 rc9~l43~+?HE4{_9{lLSfs-B;iBd#u{-,KVd r#`J95ˑ0ێ1-QbΌ.fc5uBBBrrg>EbR4ef%]B!B!&lll^exTCC BbQ]DB!B!BdK*&*&TJӲ$jTB!B!B4Ir !B!B!4]]!B!Bl 8rH|BQjUt%L;ښ+T֭S;wbggB!B!BӐoB_˵׀I|2j q'K ƆHNUq2g8b̊dd.ư=!c11+1"%mI}?cc9{3Xʮ^} | aQ_LрǬ@B!B!"1}zLL:%.dNN!B!B!桔C6KFr !B!B!4Ir !B!B!4Ir !B!B!4S!B!BlIG^h:MlumU) fΝMLRa;8x=!B!B} *iLt`ccuMRxOPƆS?n)^Ё}T~')B!B!=;SdɌ.Fre׬AreSޑx$IN )_' !B!BaL$2! ,=_{UKr=!B!Bal1R#Q"ko]>I7׮ӳGWŮ=&3B!B!ixrIx3v!n޼ɘѣ_ň2OW7P%+Lpp0pZMӢ'B!B!'WēGSJq5>| 3O{ccpz?ݛW:7B!B!B*e@2W31GrB!B!0f;0Q^ͽ%3Ri6s9 2/Z2S!B!B$œG_;L:Erlq>iPodNLoɉ0dzB!B!0R.K)şGSϗQ_|3F]e9K7M%B!B!0BzrJ|QskF\qNGdvͿ%B!B!02ƛK›ywM1M1&f>)vjye6Rvu⭓MdOQ[ϯljPdt>Qγ 6{:%I!IN9>?F[$hU.NM݃8m?m(k7VY-eQG{Qʬُm"e{%5nE)R 'f(TukmP}O.7.l)ױ_P5dV3 fceVf?>1IAߔ@2(~A~f,Y%"OQ T}wk[Yb+)^&5Bqqqi)6 Uq|^78wGTL7vnz8vFMҷta(Nϗ1sTG!L*2Q< y^ g]?G~ kGT':fk]ao']bONY+Bǝzw6Սf0cVfc1flٗ$ED.$GR=PKֹw̸wq>9߳|s{gG)xwLFD$ɶC1,vy/ ^ypLJ?~6Oնy^֢O"l߬fhpG;gdIbc&Dcycc"v28PW.Fp@01tlm] GS$,? E"nVNo9UK$?hr],;c1 GDl[^| \:m-@ GQ,=l0̭XFfЕM<7#f.bL#?R*zT.NH`u1αqHmJ ?G1Σ˿b)]$?yPzsmz{dT '0$͇FÝmɐf}A~K.7\^s۟_2G}J㟯$O{^NC2SbTQ6Eg|4(&dg^XA`ڮ6_7J:D'o(a*S, BOoZVOI\@f' [wO/VhҴ];o$ʒ2~jC o$ٴyZ-GclՂ+$HyQJk;V7׹g`p_>O1 &z<5㿜;Tm3(4xΐl)r%47>r֓ ג]q5CԢLP E/`"O4!?|fB:mEx7FWqYo h{m ϟ;Y9m.e8w `&}rY߉aU0LؖD oYaS!.9Xqs}l) ªX?~d/h¯m,iWK6;Jg|&D0hVvѱ5 ,JHѴ(`Lxv#·L;uXL}xG %]o5ϝe;-u cgS9VJ9(0^͚lsYr.{T米(]g  X0}k^,v5oǤ- w8I׎#iIV/¸Gg+{fwbm'h*Y9j$cVd0o %z,ݾ`7FVE6/zeJ$ s^*tC!~᎙jt50_3%ٽ \P—T`z 2{OB>R7r/Ҭ: IE;M^۞~Y!fL>E6d!\/tIo%Y'L yN~: |& y6p}!oV>n1׿];[ȁF=|tcd؏[295)pd ,M Ie2e].;%>Mz.yJrjLؘq-n@)3RsߣhD(ap?̘X8$:U[zN^-GPLZ-|IO$-h;ooC8b[0Lv &TKKlOKhGzԜKs?bD6z| KbFOmsGbJ;Nt NC(/V>qAi+?-5 z6yYrIM)Fad-~x} >x%gRj<тG#P:A6R3 @ST :<ŪXTʎش$Kp{'X=m)gbʑ\H͜m9#~x(ex RO!^``(iYgHAPǷФ5|91:IW;9>pmVqp7aS)D-dOoAQs,Mi~wܴ˺l:sAJI҉Ӝ1'ۧ3=u 9mQ Jqci'/p Gԩyw|JȮ8fz)ҼQ ?&nulfPSJD8ԅ3$˫˷Drh y"0'ޮ =~™z$] ;ƍ}I&wX50NybkIpj~Tf_H͍2troFkc2*#z H{WS9in7č HTdQL+Yly[B0f& b7]#]>{ P8l~ӫ;J&l]w}* nvqpQ>3fNΌdq#|x S¿8KtT-&awp8Jٝ2Y]; o\2xy͊0CNӸvF4-:pK;aJ[N & ^;fnse"JP1?9Zx&/uU;'W DޭW7-3z+kS#&g8;ϷŒux2T,Ͳ[8b%Sʤ5r>;fÀcaԐo00|(Y:cAC`Ӽ1u]YJ|;%GmW>_̲//Ri2˝Am|Ԛ?]Fsmpَq~Ծ700P?Ɣ6j:o*r6:&5&4ݛUp;;s87L"4Q6] 2Bg;㺤ۆn9Vx=n:<ߩs]7Bp3 [^ {?L\:;C&lry@_fKkc ڍlK0Ob8]lL9Mf,挏ٽ0ͧ{QYFS+s!LZ3x}ٻY;IJRN-.]gw,Ϲy,.ؼJjʨ.٭2sL`L 5;Q?̄w՞jauM<Kwr(ģd!\/)zg _H-δ?̌< D#FhJmx{ c`Π5l&or8+PO"wmdiFV4kҨqv{,"YwTn=gK*Dl6񦍴l26?VaÎM36yиʶ Lwr"w/( ɝ,1ia'E360ͪ,B>?{30^bMDD4Yz\]~e=ž3_8uyK"='|r9& DQ멹|U|t|_m"""(ry-<({"Y]Dc.O28E76ywrc1Lzf3vL(DDē(DDANJ:9`QQe1Mp' 솝tTPȃƽllurFGGg^vٳg`Jpp0AD+iexe5{{Ĩt+ű͡!t;{jsƆZ)ip{Ev=Fk}jJ} y'^}rc,}.[]wm~=F/' u]kdYew[SwzW?O_]_Va߇rmi55:ͻmv_%!y}?ۗC2C\V\~W6z틱ux׶p7wvGI>n9gӠ1 T'T3zK]KXz [{+.kX J}$IJI$RI$TI%)$IJI$jRSO3*woQ3߲>aMtmvSU^ӳX&;c2km~o4uc51;sHMWףl"d2s^ѹW4VV+\ *Cu6ZÞְ6 ;@[n[nvF//}̬ Wq6-}ֲ'e֓[u,3le?ͺnkʹ$y~eǽ_5iv}MPO h۽6iKcVِ]S,-̡[uEN=_\x`ˣm[Q}d= 93 U<S{\Yf7UpUt׺l.Zvmw1mzoоIv].oX/vM49;Ϳle8?Z薽h{s]p2RY_VZ􆵀68d_-TI%)$IOTI%)$IJ\~溻Qpc548~%?`l6{Xj;9;7~{IWOO ?㜽Ǩ|!QSdYs,kK+YPuO{Yߧw͍+T\cQk_W _YF_<Ș>#_rUX s[OX{>>}j/=Q9cvo/Zk-=k݊@=۶ZNӹypƈk={65%>GVGӺ8h ە,gg?3#}?EX_}Z>eFMh us{(zO_T+hXl3w/k[Ib-¾I;1NmoAS5+;<丟GkQx {w@ ncFMt-ycE 0=6W67}Ѥ:n}; 8dns]c鶶{EKS2i{ʨjc95;{]i%)$IJI$STI%)$IJ\tx]?&xerJ|궺鵭-a'Rٶp.Mnksfy I"TCt, sݬ|nm;:{[a[ECsuž~,r=Pf2Ikf~3}L{A;Lnc/G'JgN#+~U"9s]fk}?mӍ}~TZvVCv@WC}oet}[Qs]]?GwY~ڥ |$be#Q;GW)Ƨ(q2\}s;^=_׏cӜc]=-ߥާM5m{6KɐsowԮK_/P662d6h+.џ>Ьm7PeVW!v79ǵ[o{~jlL|v mk08Zh>>?ᖙI}5n$~jvNll~w7dQX۝[!du,==J}SzF{9em?p_C@we$$I)I$JTI%)$IJ\ϫΊOڰEU4{ߥtRUpV6ĖWCF$ 3/wƹvۿ{Vb7H^q ^mg[vE`mKld)w r:Af6Vec_Lo]ck#.%)$IJI$STI%)$IJUOxV3Ǽm{xkjJ~|ņ)λn#"A#ݶ=&ի~ͨuJ z`W^=73g?O.u,\qs9 }3ݎ2v3Z"v=LY2HV+V,}؈Jx-|؎87Ԯʭ5~,ժζ Zi?bkInڪk*w蝑U.*]+shs;vvG{o'K܊cr5;ۻbl#*B[?:9e ~=s+pmQcSw?*Y 2XuS=ey9>\ ܃^NM%L0KPYqh]Cd6iLFtrOSt Yˍ̗Zmsn}gt#`im6\WogK]v>MT%]H X8e]3wIOI)I$JRI$TI%)$IJI$S&ɝtԿ jj~s1EMx#Ѵ}lk+c1qnGo=B[S#}&Ok7zlɲ#t✳Uǽb9~rVQ=e95ѫ{YfѩQ~mwmwuwnUsu:K\ MvKt hI]L) 93!>1ZCMkC}/{?(WYSa/uaq?{G~f[Ki9/s AgvskvUmNwe-s\o&*oA_KOn֥Ӫ_Vyv<>zu,cK\ѷk}E#B,nx},sr(ͻ9xˮ_;nik1H֊yI$JI$I%?TI%)$IJI$SIې"=7VQMRN kt2k0]s齏݌z{ \sfC2 z~ݻҽZ &YY&=vU/ a'u;QfkΠW |aT#+#c2\{wߥht*lXTV},tl4:Э z~ܒI$I%)$IOTI%)$IJI$S}c}d[.̰=Ԗ=zQh[ko6c,6~7}7זi}~;o^’J|lϬnn)nzLϨ?Zaien3 ;Z;^"֞@SN:dѷ {}?Rݥ~cmڽc?t} \}dufa[>@m^wی]տ=3Wmͬ{nֵsMv>ۑI%)$IJI$RI$8BIM!]Adobe PhotoshopAdobe Photoshop CC 20158BIMC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222XX"X !1A"Qaq#2BRS$35Tbrt46CDs%&UcdEu0!1AQq23Ra#r ?舀 gSTL̯dnp`%VNZ)Z9' A\~*+5CKq'xǫӳVV9Nc]"@n*!Q@G%d̬|--| -*:Y!w0ϳA!SrNڛC^qx:C``ɴ+ G6Eo^=\ p0{~Pie|S׵_/Lj6F-}_.QRܢ\5S_Qp4kD ّ`޿g,y?T.xհC~hwE:y|}eHL|J]oZbʙHh!&AǢ\[=6ͳpRëVh]^8ir^gѓ?1|g};{##  VrXݤRZc~9>r,(@ю^P[ Q=0"2<!H pݲy%T<yw2_b?̯0G" """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ """ , Vz1\? 7OدDj]$9srOZ>tYƑ< X~$fZfy1'\gW -?9Q>@z( :F9dz dd:4.iP2H$qwu}SZ&AlH.z  pZxCnu5Npg 6w}w8~:<)#oM'oQFC3x45z|~ ;;,vR{978<D@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@X ~Yp'$7g$+c?iW]V ʇ uQ;*;" jlaZ${*wڬmPBTJ4"ܾ^v_Pz!3GADDD@DD#W=-p#8Aʽ*VGU,:@`4.q#.K,ǓXq{[d3 ˇ"dዕeDa(_.q3mm] c彋rW8puU4Q u;/H;at 5_^V_,fiMnt߸5p9 O/MD/h_:?>`E3,sˣC!ƂA9<[yo#k`mbU6!K4wr29]ھ9}.""" """ """ """ """ """ """ """ """ """ """ """ , Vz0?Idq?zhg^@?Vl}"|ُͻsG5R#V3, <˨A'L;AcVsqW٭yXxF@AP3+oE- xZ1?q_.{}9MV( H].[ Qut5w|G0PW;G \ R49zPٵΕݫ\Dc io5bl5W7AdMnͣSA>xyx.2V>zC^0v~(&W6Wڟ3eǝ[[5?*F-÷:+UיEU\r5 F>vnEOY+CԎejM}F a=,n ƗEmܩI*mNvMxhA]bdlї堇alV-̦j- r:-jHjgSԳ|zIEUM;eэ8?qڃ{m|W;|5糕o SV_-LuCAW9>8_j)A0..eSZ!#Q#6m_ʩYJ٣2I@˺g~YpZ[U3ˌP8âI ]WUWq6̎i|d 61;g/i TIaps= ?]ot=MMn4 @<~=Uu u8vҥ}\y,QQ-LMUF 9ﶯ LbH%dNcMULttJN\pazw"Lpվygkx5>+a """ """ """ """ """ """ """ """ """ """ """ ""_7jr7sѭOy'pN=(O4β[m;9>׻/zCN*Hm,.g32tT+taÃp@>з0踊Adecb9uvΓЦx^/_'73dnpF6-qc*ZS"Arg ߔ?.OA )j?3'zij?3'L ߔ?.OA )j?3'蠥̟2.t'/~S?A7~fOzGd)s?Az.t' jOU̟5I !E-HC'{~S?A>S?A7~bOP5U.HZ6ow+/V!I2f׷Ke(* P'-^m^l s* _Ѥl ճ(5P//E^vVկ(/6K ׽J<ߢWWҞ_+cjj{^jA1|v[Vi.-tq+⦶8("W&,p2ixlЌ|Ŗ-Bh;>'~KB֚{KY,Ǯt!4pą}`6jS4G+OsO]|B}fNj3kR*Xs .E%%\ 9{C55k8<!Sõ[JE6sJ+UadnH$e W]S]1WJ}+չz?^>Ѵǡdw  һ@Fؿ7?]hؿ7?X7?cX7?& ?*'*?0 Co~x.GۄݭIaGgǺ?ß|ρzp_A/9-p~> p~ ;l?h[7O |tEז*Ͳ9{C\pϧ5]g:|t=k@ 9<.ðCAot4U]tIRf2IٻI +W ȭ;b8q/:;>atZj#7wLૃ#pc&hSN \dQֻcϽuuv> }Y 4J>.Mgq R,gWQg뽌 ipxQÚn{E-7| әKxgl푐V裝{DB4JkYYGnWT12X"D MF+idTU6)/taŌ<=]]m;TÙJ qН[c{@ٝđY38ib^Uf?7*x@'m\M#Dxl8 [yWTvmcc8n֘VuDd;FJiK>'7ǦLMbɝ+Vz-#HziogUzSNC"v{%7c)+*_K<tNy@;WUė^"u9Ē9GoB]j5dm75tX\D֐;gs545WXE<J2$h@Nf#lma`R])k.6FIt{5F=HUY6+ HF$ ؎X;[(ܪ}0,.9yG:KN<}7؞}sZWQ߸fKgh:s!A2e\.76+yFt%cdn<43\DCKxhㅢW[Z5,9 yvj{NۍU ^gLy㗆7.GK|8fmD6+^i?H XޫE;*eYGF4 |!GcQ-=,͞I"67}GSGgѵFH$y?KGT<0Cq7 4w7k[K-,Dzo?g{O C ILWj`azy(O;Uu-vYdpw~ پvstEOnQsHۤ:xr0A[lEem6h!܈z*"}lPSS`,m?nN\m"5So|B[}#C7'˼xFTS[y٣6M{0'm+yާ_%[ͻ]aǺ~ G2ڻ4ͥ|-3C~q<E1d 9P[p_P].GƎo+ٽ]2fTwjk_?̺A7pݗji4~pg{[Kc|<+9kM+4s~=;Ucj矪oȈ4J#>Loø/JJF3ŎsCn G{o\CoY9obtE?ܷ꘦'0HHā>#o(ygG+p?,KWg-z#rAuGfw\H-8Q\Z'H>̒,x#%7I<tR+P+6 Li?y28ͦN'躰&H-wടy $q$XK2摟`N#*x%qL%<#T vn,5^\n-٧N"t]M[~K˘@c?Si.ƒ~aN2X+ZRC{m׳p #'}$0^Ke@; &EšuGLH8uT Jon25!K4t ͬRLYAa>A  M YLjਪ}\dI4I"6IiXfCB>x uՏqu下ْ>aH%oZ۬-*@)h.ӡ`'YzK& I\ߗ#>rģ`sF5<=枭Y- G~ٜH?ϊ8b i++UM'!xY: {h1iI k ~];NS(- G8E;52H>ZqR)kdkqhKaӿbը.cLTgܱt.$_P.DUF}[n#IՃ |A>yTi+qmv 䴁{gkX⊘.FXmi HdgPHxGϪ\uns?/V3}ѴwY#[`IߦAX\C=mY=0.ѥӐ&lJ{x6EQT)ycCdru1F!`nؾeoo}}?uco7~} PtQ?}2?Q >r-1R?Ur_F]r~F8KA"" """ """ """ """ """ """ """ """ """ ""SUOHuGNd=}^\ҵjjC,y=ߓ>}-qcbLhlvQ;83ZK>(+kfdurNISus/8sR5ڻFX VcGuK+mޥ;],/4t.v{[%XRh4ӑyE;pqeM& gI$<KMqRܭ[&#0~cbmg7sCxۮTKtF4ˉ,- F9Ljc e5M%zVNƐI܅Cd> ]}c_F4im}Lt2AI-\yϹ@PquEeT 9a^ӡ=x'ʛ ]/C{u44 v pۑR%rIN%juw tփY݃,nWKB۽ړG"+Uʎw}03fv㜸+nV*dN{ @`a3iz PpFؗ F\T[j(_W tkpS<7yc`zN-5V3[~UVK;9sXH-s<"mK-3B5 ikxq:[<󺾢d~vw #w6Zs3ٸ0K@tW5w*}4햢t;n:"]o+SW -)Ue̩%/mOxS[ow.w9 CClALS'?nKARIO® UT:VdKpaA""" """ """ """ """ """ """ """ """ """ ""ޘN'p; >ۮypCS|/9DO/I'x]gTi1<T)]j"Y9. i䃣'yg9tGZ@WFwwdWSY8SqCSj8> v bCME/14풽 S3Zr5Z% ކ m%;c1[Pۮ*ݾĜ$-6SGBԘpHfH)AME.1\|M\?DçMw׾uOkjacf5c0|>USPW6M@h%:]I fl]su=aF(SwKZú2\M|ߢ`~%B`oo}JWLڹ.{Pl;z`Sߵ] ¿s|K˺""" """ """ """ """ """ """ """ """ """ ""Ä$(^wVЊc2gǗvU?յ,tlkKdo.`RҼcٓ}1)Y<&#b\nTX⭅ma`^eDM=&=v0s 狜Ȥ&- Ġ^zxWGVtfs˞aׇ ^g*/4"$qhkA-:˶ϑ!LAĴԑX{|Nf[g޳Sԑn {}]}\\YYcc,"I;mWY&I8d;c[ф\*jX:NsӘXAz-[y:[?fz՟`AVحTŧ3:24秽.h^|V+FǗeh_xꏔmn@n{b{ jNst1/Fdgb칓7$]w&Jr @C|bȥ*#sƬ.A.Y VD.̱FI ӨA*q|ˀn'ށ7=4Ίk[dooh6U5? jq>;x^~)TgU>'9Q"2^>mZZ<̫O71vKW :bg~W:z#}:Lv=#f;K=pދ+sSWbդ.KU e4u "eP^DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDGcIX84eǒVA cK_zVnhZڢcqg44eƗTU=ީنv ZX0kKFV=S*^˜HV0:CU8(dk:3/>0<kϨYjL 9x樂W>9FTM%G;LMi9 %8rA?~?6SZh<}@bZ2oAA}ZՍ=WwVb f|֐w&lء}=C=?صحqXsUw[[0cA Bv=h`V=T pLw(ۜ͹q6T%%tF[qA.?6.`ch?S^4iEi'>\MSQg mA(A+W% N=\;+c#ϱ[)qk>[ԁX5 _Oy#vz~1G_/xgoBj(ep*4:G/5Th ;tş-j4N?:YȌeNns;c1*.OGo[Qp2#H`-zFmw:'M B-68$ 䮏G = Q@j uV㰏\Ҹ'f2J6[jgln̶KOiJ#i#}6 o;m v|;t]M_Ѥ ,(87`gP_?#gfuXpᭌ)/EOx_ҵ:vh/}PA>+bTַ)ãߏq00摰4CsI ue#|r-CpHS+:k* 4IdX2B1 Y|5uZQCMS>(n3PAv-;Fy86# vY`TՀ{'9a8晠A?Ռm; E>3[Pw3[ p \Q8I))zsn[,Ϲp4G9cGc1nl094uˁOxp8'wtv% cQo&Z6kvb uc7%ds6+eJzjG5y[b1d?y]$+Ac#]j; ͤGG`5$y1[5]ˇ=̲h|A'2Auu]u]Mb é8q rb[ß·7{}zr%~XA+e34li:N2rKW=v5|/Kn۩%fa },E%C2@O*ƺx@p|i™>AtђrNNϗ({6[Qҝ˚Ǔ/d5ϲG(.C `u3驙vs)mD!vOؽ^⤱%KHa-7BwS]nTJ1?Eyvw+n87g]=x6<7peES`#%׆6W WRG+䚕΋Q1R F7#+e3eB_ QXn'TWOMQQ8A#`> ln[gnzOq^WmĦB̎-zIwkyJe"_I/h${83-U<|stۉ.iϬ0u9v7|w欟#2K-JFgj-&t"6j8FAYOEE]+9"'bf<'%Ĵ1dvQJ]TkS6h sfplkz$a'oҔ51[.T6{S%c(D+kx: qlgFǂ6?|\C+k q2;Y' k~FfxcXS"WL}c˺IgZii#%MP-'p]Umψ/m5TXck@@''Ĩ\o'@)vRIvr<vyuTMUS9NI#Ө\֨J5-L'yfalIq8vMW`CgSYC'cIםq~@V*`@#Fny}0{A  7}WZɦ+stN_rP}A=[*.]J%t3gqnttY+itW9UrRINw=.#iɩzWLcsF zӝn$]~blW:t5Nn$l]@n9tΨg{ֱ Hn^xZ:䶸]|6gQLi^ܑ˧C翚ƺպ~!>MMp-:yrL+;;L9> vGk\;}ݶ[vzgVa8h\>^ Žܜ1496||o/VBXVJY g/ Vn~_Wqc |`[6:q9$\ytS [Jv_坉? X/LXdc4JH{Fsldw|{l-xv(%(zD,VAO:uUnz@:>W@Ʊ۱8+&C+aw\ПV,֖+S)}6h89kRVl+WOt>gY$j?5 w:fyR=p_㑟wൃgp9$o N >\tT?ܝEOSUwjji.2 &䱼<I赉V*|`٤8ǓuMsYrle50ޮo0B,a?Av{Q7.8a9伙n%l(uV:sŲQR8B9喭G40/b_/s^*Dggl@]ٳ%$KĞ}4wK+ے$ac^< K@ Z1JP<8o -:$5pO?µpu}F{K ݘ'0Fx[&Mx[)292y'rvEΙNy{C7}AήZ_GPT\%dBj89n4yрw.'"a_|!m| ΚWbpM,aՎ da N\å}r]zL\:#s 6ѵ5w]~c k6B洵dhJ$#/tTrjs 8{I9؍nx c& JC6$h՜c\3!QYKQ݌|4ZǧVÈ  •NeP<2)hs,!su{'ѥ$IOnm8Jw1Ӗg|ʊEdzFulδޣ&Г|H%yl]ZxJH,#1u2:Pi4ލrEM<2jkcPʢ,>Gzˣknsnj~#F\fmˇJ-Cug8%}j;^x "ze3)驥˨s Ѷ܎ M|ڇP]9)jxil7x?E'9H*Y8}3d&eA9->o|i#䌩IJi4R:hnG yev)dt[ SlϤ/T#.\bidIBeGLȸef\4 v=0]Sk7>*8奊gDv#~xð|ǒC|1RKEbkdavZs'5̸4n`}W[:8 ;Kˑp1,.MjՕ{;5\I2ws^'sCv fT)SL]ֿ 0p>b(鸮61{bwzfi$x9{: g 䱃M92vǐ cbL?oTp.ߨUr30$g8>40tVAէbĵ3wB@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDDAăSlg$3U&_%Cii`y<^1@H2ѐvs"*>4E IY#;Vv@k$da7>,5]KY[QDyicf4i\+=%]m|KQt4nL΢gBƖ2[H1t6hYIptg"_ab75ymi5n;ÞqUpc衢mkCBIn|S!ݥQ(N=\w\K|n6' g/:i{ @Iqy ;^I UH.;9Σ v'!dZgE[MGW҂kHcZ2I2ssNhqmmZO?dp9{2$])ڣI80O laF;w/Ľ .sϪ^ӺZuHߖϚ N;iE$L7t`4jkgy 5UOA4o~I ^5=-kruc\p]O$m$ݮZܑ!y]bO<I5   +{VH3FE4`;G jJ52#8Rڎw'#صp̦USKXwˢ,cN zsA(xp M_DfG;KeG }79TQMis| h<}%[K35҂]zCqIn᥸0b%C^A99?XrOS )$2LrɣsKCu`oI; 'epZ칧χ? sw?Ai=r0<ɍls,IcBܵ )fGvA:}UR]0<῱]cJ,w׶e BViF8mbӃ,/`>i +s촇2Py+uɽ#p]wV4HI$d dnH*1%>;,Rs\܌㝆+zwRG  1[n+lsZ{\j5>5$#] .;c9pPQ1޷/$ϱNxs:?UZotzF%,xF<6u usYpK㿑WlGCƗĂj:Δ4` gP^[y<9_K=mdv\頵RO{gk-|7S -,C洝x v<,}Tt/8٩p稴` ~`Xh9%IsnAC| 뫸2^9#:C@satC}m)J MeK-2ƞ9񑾖yҥI|靇xhyeiqW9MxvN INA;G,xlQEb:B>WIZ>\5՗Qy16H:Zi#}=C}ތ$}vi-I/< :]P$Wy=cIb-ѭ ׿\, Qޟ\"qZI-ςQM,l*L ,< 8[XV#6WVѩNnbGuAbM?qF6|py):E4`q@OSBf :i˞sw矴v\V dxFAAt;KA:{Tqx*Hu,1у۟؄AFsq0F<خ/!^""" """ """ """ """ """ """ """ """ """ ,Ko> F3GD;xT۟ lu;mix4ѱ7>o}֑6dpiΌC c<*\{q#٩:soS^ijL6a`GI协z!#1-hglЁҡ5ܔCoOG*Yc bK^00v#T":ָrAS{9Ƙb|ꙪgsfiyZ[;f\ulxҿF5 ybHw8';g/bct 9wv  tz0NwM7qxi6ŭ?k*3Ki=췿G4 M3=Tx$l4+x:q'lU3Q0@{<SE=+#90 ]9HkV' ]$+3Nz]ʾnEiMdG*ƷAs5+ʪ;;oqtm8[Ҽ奿K9: <3q9nxm,o UQ-XS4k>.NY.ҋVM, p\sXcHi EC $9]:ЮT!l4Q0NZ0Za  r woݟenmx ̕)с,phXFǎ -'*n6eѸa7u|T;A=m;t49ωc2GT(8z V5NJ]P}c[Q 'qwNGܮSCu> ζ t 旊k}=<q9e[e5;k@kFuן.wQCK Mه';< Xj$Y%efd0@Fqik ⧇89 45iQTіC9vcF.' [/G5}9~~ըq}ƚY,POQg;iη9IA=3iX@էr:O&֝$YsUIQ1tow}gy XAo: NAc=Qp56銒33Z,sZˀ->ˈmo ()vkUkCdr2 4dyP8D6FnC1m{#xn'|9pU5O\48Ɛ4έ܌n0@mxp0r[Mp@ݑa=B=8#vDc$)mgWTߌ>DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD((冕UN"k^Ԝ-roMH,l̒u<}#m]E[_Q#n@rg / F|09gYK'i=a7mfǶVn7ohprYYhΩ:3VU^)ph@1ᷞ3T\$I-# ΚV\3{@5pG1{Y#rrsmmi(>+='(5vY2=v7IknH>Ug%t54 Z[9ξ3k%l2@xwh$!|vuE-Ow;$lHvpuJ>'|v\24NmJv3>zV[dj)+.B\bxssX:nw$8.gZz!3$xDѶ|5C #v2Xles g q8"?hNYo k^w#:6G;ݶW0IHƍ!oswHkzd1 q|Ɲ'[a˘h{\@1P,~+ڷD\%}χ?P7\H;۞{ob"1;$` =A<,x9,isl @:ZڈN<(QJd`okB~.AΠv `xr A""" """ """ """ """ """ """ """ """ """ ""-Wce-ҝHdzJ͢1ך""<9]z[1!w?-QH kAiHƆ1%k<5-9nGނ-n2]zg~ @ueJg dT#xDFpHX0ܩk5\t:n8s' n|Ҷ2)2M!+^Nk9Cp;%!Z;jdywAh1hxzEK['Tl>@֑B}Fj s!`3>٤_'סrڵ7|UGLN&JYlu^;O8;mCN12JQ΢MF"摿]501UquTQ$W`tGcqM[e]-qkƒݶkG.w0]TAˮko=++{ЀsWEt{aJd - }OMJZk#+/pV<6Wk]+v+)Pɦֱ|3jt;a=qgS"e4UTҹϔ湀c8*~yHʫuΙZ)F[ȴ7x*3:[LRӏj ).Pp$` `PhpIfzZw6gC|A SzU;/=vDW㞍5ݼ3Ը#wh1}q[Ck.Cf|MDncނ{yk$v4'-|S7kרWyxhp|7<}G!ۮNP};pMYEp CJ""" """ """ """ """ """ """ """ """ """ """ VX)̿ɈݫٍX $sIӽ0 ?b1kr<@LEuƖ7H^2AIfZe؇oϕ0șڂ3L-wiATg}Ŏ\wK/EOQ8F`~^;F-![/^Ȑ7R0[(dpּo|j'wG7 {L6!o©X9L?_U'$e(p3B[Qڜ"w_?q61g1a4s-x{ #`&cAx¬Rц9f?Ƈ_g& 7GD Y溥ka9X 祎= "o2@k;H{] ܅qUOm8K6Wk!g;0>5` tkvp?=ʌu4$~s\|OҌki͕]8\ 6(*ZKtG 1l!8ߚ84<[FRg 4Ti2@is>#Aȳ8#{@3ϧ7 w{#Pgu,t+RXKpze3;WN3Pl-&`6焆nA@pI*? g,.H9$n,3[`u>{&cOtYj % mQ0ôqa u"`>9|~~ oIosmš|FecAk9o<~r sN! ;S9_$ ɮ-q89xM+&Z5Zݶ<_581n5yoAdQR` B >'V$o~3ok;Ie,8qnJN9`Y|iwbeptNg}I d5_|Nq䭘X-/GA2:HY ݳLc mE,3G͜ki?Fuya+ȥ {}])H ױ<<1)\پFgXv--1э̵Iqh69ԕ? ܪmoAlJ&vG#gP8@tNhvCuO#W!@0~Ŏl5IuǷq;} q*w W)UWk5Kqdmcn1v}X_D򏞈8<|~QPI~C΍:gtϥFsG,m/ouHӾdMjz/rOi{[y-#ֲwN7}Rv8wQ'.C|մ-7ѼuZG-iĕ """ """ """ """ """ """ """ """ """ """ """ dk"4Iy# LZ7r\%-aϞq`b{@ vǞ<f p8o珂H5!ܚ\ Ra,4`x|Ȋ͍ӆ.9U<,Pkӈ?'n[.m<ež=z?75\25y3~Dcc j.1 n\؃z#I\[D9 |UB{KMg0nzx~LNOx~ q bGϗe@U(Avv>;y@XZ[ mA۰)X1ϦbOCWG;N,nq 7}v>>`VFsKO<y}S[[*#- |qzJ)ydc㟱cG76qi؀*mXS\jtC4䐹Ē7@Ȁc#޶ TsPA%[* [N3smX rߏW9mbEWL !/HѤ&vy&Yi+孫C$d'0]F OpqV@8<3.=g2U\~iOz̏Afnn ;((瑮!Lp'Q/XfG,xpp ;ŹoKkxDUv \;b>ptZ~Dd3}R]6oVCE]H !Bgemj\XI>Rv=3uZUYb>G8 g~q]͌1=@l|yiksHs+[m[!gc4h(<|>toমz` vzgx5;7vS5zDq dL@}8Ar"7D`c%=/*uHZ0q^Q|=~};ף/˨kp[~ 6'9?r]4pxwzӻFe;}.9y\><\w8BpHdc9x@>>j]+bisd#NOC|U$:`!9ryJ"G;Șn\I[9gljo 0:lhc̎xsץ ; -8Z+\WjFd_p@|A$/wr㯊Zw ;` /1Whf6XSl;\Z s¿14Nt=K\׵2427 U2225~jJ!.##*.0a<us汭tή$8Hh8ܞC+yz:>_]G}n{Y] b$i9ùq4ߕ@]yoʭX;]Ʈχ*Ij$,)p >*\Y-$m?jf.i@lz\('ê21_dce T"o]1Kvq?5 xꦀ506vz6<\ r' ņ=}UD8h$$#n&K_L&4bLrr/Y7xnsqmJ %-$X.=hySt=sU]ۆٮ `=_54A<FgSͭ 岙'0D-kNL(JUE {0Z<[JQ6^ߢ䨺2'2%<_be%h1J0lr5 ZH2Jѯ;p>gD#kLjm-Rީn@{LCkrjf~ql;n.ǡ]a>S~1.\a|<>29J`UyX8obpq_c=<i Ϗ? iCvېU~)8sM>fvfquk;xd>WHCnyx`yz/GIV٪{i1t]s_i3LZN3stXZ ߡgԎ?f\YMfeWGp1[8O;)?l닞UT杏s]in< ݼ1B$4ؑ8Ѷ f@8{ǚɧ6},^ uT%c,$ׯ,%LYN\[Ii N{kctrT vK88靆$#.ץ-翏O51nBgFefN2zgu-3|;w:+4UV?P6lq7Yqwnެr-ayJLMpdΔ5r$)~!eIt^Fu Ɛ:GG- ]KpPH%їa×=r<981r#l)qcLgϒtO,v#y.$:~9r6f xKV#{\0N~Rny54jHk)h+"Yt:y[ݏ$N:ő2䫤VQm `#Q'ןd1zh:F?B[CKo5Uv̦cHq{\r z;Z4{d5-*^\KCas] isFF%v<6ZX'~Cܽ k-o]V81,W@͔ɯ'8\ςCpIyM5ɪX쵹$oXSfPGf痵'C2wnf"Jjii␘lnq#>{+ŶŦ4iOoccqkAgl=Mh^]E93|Wz@x BuIvInCR8"hsK;iӓⲙwc& LlDrP#nR#~>!yA霷|srִ)OC\r睿|T|r6zrX0[xppCp<sdnr~c\54=ќ2ޔϬ "L ,p2cpUV]-$KDW7W9ii.2ywX?bh9Ǧ 7_)0 ]fO9TcO=0%`#z$sӜ}0I3y冸a83NKvkS(DmXUQ1`ssbURek4cʡs:&NƸJXoWŕ45 ׹w{A;edч-+$1xJNvgdPVSqY#$ze_=_'f%sZƽysoxu#'8e\qk :̄/Ɛد  ;pQ(E6hcdB a8;c_l,8'- T֗_1bjLqi\Nh=ϵXr4;|WV*YO bS䠢lur7S8{9l73m'Iv>~JniIK쓷 QS>w91avnr=-9p,udOw$w_2dS.Leլa\ǖ#uOQ$qG 2sCs;RnVp@T_iՍRH񓖴UsK#Z RT5M@I;- ÖgM%xf}nLU9.`c> ,be8ߪR)dΠ7TA4̧ e>/Q2kd:8;˄ڨ f'K_z0ӁDԐɩ5iG(LVbȖlc< {Nܜס]Cq#H<|8Ϭ(1j[Ƞ) | s# qd@ʵ(BFqQ忱S 쉅$rZ>T5 -.lۇ`qV_bzۍ\,!E̕8 ;i2$.{d FKH'=V+p3\vϷ Kmj6.5 h >)m,(L`㖒wp9뮲Qo{1 $"2T8lْy3WǓ,fe^j4r6.]gaC=V 4cZenH91EGKl1y''P5ز<JI-g m<}}MDU:jLG;\!cΗt>} W\!xۮu)[_\nI{Fǩ 献wQn&M.ǿs(ʂ~W(8*eddG0AtVQ-;]1ȗXZ3wU3#N=LyrjV9paJj$0Fӗjn]E13$oFPI;fcA-.}12vg8qm>MM-e;pKPrAǞkqwXjxvKEvvd̎H8Ǐǔ>K7|f) ;i%|c3ے?f>*j p.lNge>!JMŰKTLmV#@pճvqT Nzb%s cK ~ %MmϗMǕD1 $xw[ ],̦<'c((\״L 5A9I8voQo&^p~YM+cΜ;Gwu5+8ZVkGI$RXX :g~ݡhW>.kۨi p|>kى.>Ï(KeM40Ie!t"3氖ά;VleQEꬣ--3ZriF8aq'n^rkk!oy#{(t 䁇l݆>[,TJ!vPq>$ ;5r=sl3T\ekZd9r992Ӵ㷸^o6*L067 D|h/UtVYr& WH n]Lc"IZi~:p@Yʑ*-8ys@7ql/oj\iG#!="qef^le0ky0NϽddr7cT\SO|cTYlti#fi\\ X#TNZj` vrO E5oUT fN\^v'}êxˆ6{E-]L qhZF !Z!TFO^zN婟]A+**jZS9q߅=O@*G9'a9h'-'Zޙe,lIK ; ﺨex{y;]pr;x:+Aa~-yگRΨ!sH؟(0Acݱ+5a\ug<Ⲭd6Ir?sn庭7SC}Z$D@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD6WEϦ\,Vuc~CV㿹e:*jra:XW<\8X]j3Fy`\[N.s|yOˏ ‘f7w;a Tk%&(pX2F{a.]=pO q,#~ekb/|nx{ u0ðq9E;sk5o} 5=ȞC|y|sbt}}}@01ڃ2Rdp'n 懊Lq$2y ڳrfi$82 o͞6gF ZN\ޘ<Ix9)h^iqM-'bqz *8&7'oHX4D.7Hq=m=!,( }DM׺)#Cxҍõ6U=d,s];ꌺ0tF2p6OId>Iwcg%Qgzx0w'6PSOvVL#{F]ե,%EKFC9%mXͷRi---:F w4o}ÚMmQkuo&I)˅4/Lb= ˯G66H殾)$5f8o<3{nq~FvJn_X/8ʆkg)a qA֟ dh<];o>v6ug#Vn]̷Q:ۄ\;@p3́<2ps37rqߕLP8đǚ \/:r6<>[gި` 3;}# o$8i߿554cs9ALbA pkF7دyPes^NA+ <-/'#=}#;ijy}A.#G ÝOC$ ym/rSWXq4jqǏA4`F;I?QOԍ] z^Qem(Jd ?YgcZ9W,MOMSG5Q w-=GTM qU*O#7;ǚwG#N6yi#k@J.nJ=o&$G-O.+_uUQ$Vi\ 8=P[w*Q VvHËxW·ڍ;+JDxpl:9h5cW $P ϕ}Y0<ʖejzh*) ;0׺|?T =6Q3#ySaa49Iۑ=c@zy}+\CF DQW=q%\-y6$UecwH;7FM.L-.sƟ1Vg`Ho>]?} 0DlΝ}9*CfğrpA8-=m:A?W?%Wog 'I22wNre96(+2jkـNNʡ-h؝˿rA4G~^ݜ$8&{Ç&A<-m]xƢ74;T!?8n-鑅ZG[\`$#/kH4덂ɓ Fql /$mx-PxghkT=8+;8Mf apxCܲ щf9 Ϗ%utpqtk2P@7PǴlYWP-ۮ2~O ]H&#Gn8(#7ٟߚJ&p2ed^=s#qowc3˳oͲ9ye3G`cOcw@[Ά~ 0'784١ xey4Wf߻ 3ԫ?^ ?E6 =#aS$1h< H_誾N`˗{]˺gyH\Hڜ܂pN`9~Y^<R_֞@ON?b9pcG>a*:Pu5,E$k~lgeaU8mO;#@ ՠUa{4猅иwվ8vB]|Nt;\eZ0P6zfT4=Ǚ9䃀p xYE -2cK$zow~n k&(TylȘƎMh D@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDDC:uhk892M\bV5mK48azT4g䅳:7&caQ4ߛ]GШ!`OɐT ֽB/ͷN7_!>#^|I cSRf?kwVIPk%Rb?Cj?>IS-4&~ISE WBZF݄P%(5ߓ> A[mq >#A(6l?&AR|Ge!&763s cm, "n;N[ #aF0d# ѐ}6 HLyn:,@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDqdmr-0.12.3/doc/fig/uv390.jpg000066400000000000000000001631161501654372000154550ustar00rootroot00000000000000JFIFC  !"$"$C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?"((((((((((((((((((((((()PHҙs(Yʒ#B5O+x:3\ -5[p?n޽)q\sUtQyh*9L-.&-_ s'M%x͜ f><q_S57ҙGmsr{RՇchB ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ZOƖ (t(((3@-dw(((((((((((QE@QEQEQEQEQEQEQEQA3QEQEQEQEQEQEQEQEQEQEQEQEQEQE[U]p6Gč}+mWAW_۶ HqZ.cŒsVygڷ|SZTqѿj2@t]YO[e2_3rrZ;crDs͟g̤{QR ((((((((((((((((1@(u(((QK@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ *(((((4PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPJ(u\e݁M| m# NXd7p\Ҿ%Ie[8WM}?1&F\HbdCt2PW;WMvlxdc>3%g#AReDf(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE/QGsI(((E-QEQEQEQEQEQEQEQEQEQEQEQEQE2(1@Q@Q@Q@bf )1ih((((((((((((((((E-%-H<fcxk$E~ ۯ 0M'wsZGSCvX'*F1\vvCc#ĠȮ/O\nFodqe½p+?f6xɗk&8$ { ?җʚTw>EH((((((((((((((()h(4QEQEQE@ E((((((((((((QE@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE)i)i_RuqD=xWߺ#_O%7Yi]M/`XH cP$λsWkO sB]ғ$=;Wx\cy756?*r+H.F/sd Jl }wWs5tn<\-L 99:y3ӊ?؂Ab$;v3HW]; Ï֦{ϴT((((((((((((((((Q@ E%QA()(((((((((((((((QE@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQKHRR( C<.?#_u+C<.?#__5>a洏./\-{vW]9>YCKbmYrхr #57jѐF;b{9ZPҤ$((((((((((((((((Fh))hJZJZJ((( ( (PEPEPEPEPEPEPEPEPEPEP( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( -%- ( ?75q⫂29_Wq\- ]rsϭi]MMm\i>Ҹ r>ؠ1*[=uK?pA9ָ u-w^Gŀq@`sG\t$*eKs 5i U-zGPQE1Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@{Gj)(qEQE{QK@A(((((((((((((QEPQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ @((/75|qۜtz%yO\W楁 VEֱGs+m }מkM^J ]rNh@tȤo!; *auHqTa 6 k+F<gOZ aŠ( (((((((((((( ) 1@ KIEQ@ EPEP@(Z((((((((((((((QE@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEP)iwEPEP7?rokR u͑k<M~}j]dWՉ^y9\?&?.e0psLyǠcܱJ{C +A52FPw'/a퟇:I?oW7ɃJ#y*{QLAEPEPEPEPEPEPEPEPEPEPEPEPKH)hO€(EPEPE-%-袀 ( ( ( ( ( ( ( ( ( ( ( ( ( (EUQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQERR@QEQEC}7MU>jʿ>lr.s1#"9H79]ީtݻql4 :X\}AF1ۨ@0V<(?/J?“hO;5Wq¡l9nQE1Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ (EQEv((Z; (hOƖh(((((((((((((eQTEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPJ(Rh@QEQEAōM~|*ۂpF}LWޱϋno5qEHNd]ř@Zɽ)qBzf$>ի/bYvyAyGzҘ-ڜw'g68~?v }l7QEQEQEQEQEQEQEQEQEQEQEQEQE-Q@h@Q@Q@(RR@ KEQEQEQEQEQEQEQEQEQEQEQEQEQEQE(((((((((((((((((((( (EPEP#_z ˜{+RuqkY]_\~i^DdNkv@5^ٰ\dquܳ_~M \zFsqߩ RcLdq+kgg;?קT--Š( ((((((((((((hb ( (JZ(((((((((((((((((eQTEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE- (Q@Q@/\R o'ŗ9s_eq⫟;"_Qoݫ;Cw);sɮ"d 9 ?g U">Цێh16G8n}i8>|9|J5[_z?^PES$((((((((((((RE'Z(((@ E%.((((((((((((((((((QEPQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ KIKREPEPEPmSA__GSmq⬸>~u4.⸛dR1q\Efn8i 6t|UR|1T2H?fg c2޽Ka+c`kjR ((((((((((((E-PQEQEQGE((((]A#4((((((((((((((eQTEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPKIKH)QEQEQE[U]p:?+9Ϟun4LA5ϋ. SFfHݼ\=n w}_Z5" dЀA0~[q:j ջ~&_IkI Z?y퓪$^PaESQEQEQEQEQEQEQEQEQEQEQEQERRQKE%Ph-h(( 9 REQEQEQEQEQEQEQEQEQEQEQEQEQEQE2((((((((((((((((((( )QEQEQEWWE{khYpkc@'k t⫂7֑EӹsW|mWk0&̮I=kԁQ[֒GcW!9jp*} /M\d rd̓ }P}( (((((((((((Z(((ZQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ *(((((((((((((((((()hRh@QEQEQETxO; \%|S>r~szZ5oy?_dA?R)j#m8|n}qzxTzB54Nf_j$0sj)zy\_٥ۄ }5YQLAEPEPEPEPEPEPEPEPEPEPEP@Z( JZJ(((ƀ :RfJZ(((((((((((((((eQTEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE- EPEPEPEP=o@'k=pUqG }^&׸EVu4mǣ@+R(.Yn>M` :WR7fCWXYI"<}Igu;M}C_,~nV@ߊE=( ((((((((((((h( ( ( ZJ(h((1EQEQEQEQEQEQEQEQEQEQEQEQE2((((QEQEQEQEQEQEQEQEQEQEQEQEQEQE/z:RQHZ@QEQEQEQESp4KN&Usk{Zz}OZ&G"hJ85j57tI;+T8/A(BIn^kMFVVT&Fp{fܟց?glWE OXG_]ϩO`)(((((((((((()i(-Q@QG@ E-%QEQE%QEQEQEQI@ EPEJ1@Q@ ((((((((QEPQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@QH EPEPEPEPqeM|r '爿__ω$x=j~R>ϜgZ5Ls;kmӎꡍ98ǐ?ȫ1T4N?ɭ '!AxcΏ \m0?ξ=j:(b ( ( ( ( ( ( ( ( ( ( ( ( ( (((@b((Q@ E%QEP(((((((((((QEPQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ )i-HQ@Q@Q@Q@CYzQ!>`3ߊ\ c{Aw`"ڕ'[lqqƧ tESQEQEQEQEQEQEQEQEQEQEQEQEQEQERQ@ ((((((PEPA (Z((((((((((QE@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE-Z(Q@Q@Q@%#?_?)8A[2FC}ӌWsx'm eDw^/SJvA!,'F(((((((((((((((hK@%-%-Q@QE%i(֊(()i(PRQ@ EPEPEPEPEP( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ZJ)PE((-us ^l$` ZEĞ,76,h6 n-|5znw~ɼ[Bm7`g_|f^>}JR G8,[q=ܮZ{-`0ӯ^O$R$`IQj4I63 8'?ּG9u5 AMXe >z}j.69<)͕r7IW0!Ꮘ6ٷ8~fknn}E͐$RwwȮ1m}O5ܟߞOZHB@Gyx0.4m665U}7^_ 2T- d 1rOʾ=fK5 nHC>vV # 79('+__F _W>9py}y0K69ȹ9">zA0{#=!Ӭ>hz× ,#|@I8bPsY|8I`g6Z)q$BTg<(v }7ƭ>2ؠjuƝ.@[kku]W^{swG:9smE9d!sZ,I>& I Gc6?ͯk(} B@=}kO Ú$H;~ir$:kN̼>ҟ?V22oDs~Zkeq?\pW?mf[]F<=lMXh*(((((((((((((((49EPEPIKI@QEQEQEQERE%-Q@(QE@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE- ER((j^<O_(7' kOŀ,3I־VgkQ+s[1Jyש~GT\HN܎Te7P<|۱+cрsv^A9涢?9NvNy9m6ߴg yu{Q\uρI?zDECzϴ41^ XWU_hep_mީQBTw7mդQ sZ/h`;A7 UrJ* 7Xc"Ä'pIDgPA=p+*sש~/TZ,Z_2IJ̈́`pn9>|>1ʱwQ ?[魢PF* V ZB 﨎k@ch 9k+D$jdⴵog3Bgì>MvD[z\'Rs&Rõ43Q*+ vS%Nݫ(2aPFNq_D^j) TbuQR (((((((((((((((hJ-ތ(E%RJ;QF QE (QEQ((E@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE E%- ( (<6 L+ 4bcNO_aѺdž }Zn$]~~NJgL5V -Ssi.UgjVk1?ݾ5jz-RuDǠg8Ci?BPއPłl+:>1O^nX/>M<0c^N`I9 1[*k6Mg5~^'mB:瓤Ur.$RA,9NKq z'|j`1ۥ}IdK{ECk$yzW-%|MSD꡸2&~H}~r0}7|WMU[iZPQvKGJ=2*ٗ]z2iyq\m*fpL#==kf; 1#Ƅt(KB$` l}VϥvV9.}?^_gsUmT/.w6W~`ƙ![]metaWcJ:ư5xI*3=H~xbzٞ;kVݒ2W֥)w:(((((((((((((((((E-f QIE-f()(yRIKE%J)h(QEPQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@-%(KIE )(j B;+)ϗluk񶫧.sgsqqc7>ǮZi]>b+h>#Gݥ2>yO-N䇎ȩ;w\x,{{1)(Դ-B+rwk\ɸ J9\LשL&_[ ~nJlhKGj:h]u?ٶ?Ķ&NhR \ds]8 "H[#btuovYꗚĖSY]v rpqYsA=  sM ~=k֫rt-!pnlWŚE\=I=sN5mvKy~˹F\p>P}}3Xj[tGUDRX#zG47%TaR}szLm*QpN[$qoNDOBOTd&S$M77r,_N0qCҴf4UhAT<1![o8;`eàiei$c+V^y|z5/j:jb 2=GJ P#ڨ$kie 9Sfrh~kk[xx.Dс}Xä-1G6~`_Wm뙢+:3XVEHLFc YHx[ٱҦG9{y><+4ndreFژ`hiW$WvyK; nqھWz47RW)(X_*[0-#(CW rzUæj-> Lexz"):)Yp#!h(=*͘t:)WZ ]3w.zwwLO&nr0~\g' j${Iܦu'75H}tsl?RU{:.]]CdZ#km 1\9T*42i1ᘕl7)= ,9 '⥹(t洼67XZ[Z0I>d9\T |;XY'I/=x+άo'aX^L1:l${c.5x#tO)$c8+ѳbgՑH`zN47AtH6FmAk/EŻe #EHXEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPh ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (W_պn7hMހٯE|*S "IXa[qS@@#c1ƒO\u'<qk1rRxSd뜞l 4?_>v$IA/ 9G^%%H#zy.` 8Rd IcwA~^;JwI+I$T|'FZ\kDR1tqJtLps̙@B]GRYNuO;w '9Vi 'F)]ܡ6ۣ;]\vax{QĶs ï;6澦kÚWEcs@I8<¾J%0(جFQӼKvW-lbQE!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־\p7RO %ȋzWC|@<~IT n$z *&ywdiKR!3"_E6ę4w6XCcuyRbwHvn>aW<zLWz߸.@ʤQNz6G<6w[J&Ӷqk3',p@BpUzOQkp5mXV(p|(G5xME1pXr8ba]G&p0중yrj!R4rƨ#Vw [8?UfeѷpC\ `siD0wD mD8Sܸ>=:ôLd6 <&^X6DNF#5ɯ,d-8##`7aI^vF', do KfԺAp2XU%3M3DbO\dW @V.8ES{T8wᴊ70x, W8@#y㜁R<#MLd cSc9~w1rc#>n0}(c>ּWEx~ͯ5 U#.B1ccrz~CAu(+/xn[ wV1(ݕz:w}>$'O R?_7"Y E\,t4u/D/4jK=[š˯ Ez.66  0N+~ |H<;mW6st^6ތ{ۊioWzmJzYACǯƴUvz9nzW,m.)ŷOl-ww!@$rU@;3xOJ>\xZ}?kip%!HŠ?ڗ&x[Bկ-,h9ѥp Amsݚ n*]7{މ4sp-i 'P0$*֣ZVu͙nzq8*{$x4zw4L W+3UTQXy' pxϡӭRY, ZYUy'9ʀ:[}2[ #sҾ^%.$` _.X.f t 6ZmLc(RjǯGOg0N% <4(2\ggg>)8YT?nNGZsio)..) H#c8z,=w/_ +O3K/NH(Ԯ+FC-PQs_&J#,S3aR #N?xuc# ,ՈScE%.y[%*CH! 3dp*5ѭfHmvsܞ*eBI.`'jӌ[1 9::m;e~XeRܟBdv9 ?8V1 s1-^_#/Te2}v2~REQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE6((((((((((((((((((((((sIW?4x\gxŕ,T{G.FI&wPȱ'c׵|/{cӼ F [SOjRá4@`IRH װ̅~RHסOeӆLѺܮ' gvki`n:Z2[*8ٻ9>F9nB']%!^ l 1%:1S g q$qu%V3 nY]aHa/t BBq>ԭdD njce$7^3P7"H@H IFn cRx#$bU1wqU9" HL9I xn#0@, Iux/%`w;1z>i,$[Y.ss;RF|(][$\c4xV_XI<0crqIw9⁕fEә|\(sOM]^eWeWi۶[{\"?2K9눅_vRe`6ΥM'E9lO?w϶) 5ا珵VѴ#`N9Q3; Nަ&==x`$i2jOR8* 3c(A=:s5ƥN6acxDt@˕VxJTJ,FNzw>ջe9BFtsg w9૷-攗A$_ӻ!5e II eTVppHgݾ֡׼9o}eԧnSE pي3נԍQE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (EUQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|pD/A~\9'2{W>xәЅ|)N|a3kTyYxܮI 3ȪWkZ$o !X0?s:gYlU#ٛh-n1P[[圂>I[j ԫ7@ '$qDhc |1gz4YƩJd*l*F7Ƒ&@T" f{c9ずş\eȇJ<О=bO%UQ–$I?t0ʱ-j *}K -3YmUi!YV |O)P,J3EiDۈQ߂N~]℉H>3p1NT;^WE7RG̎.q#Cb$hb*0ĒNOLz;+7I"sS?/J"Q Iֱ'uຂY-S ҭy.yVc~6`,cNx>$pO541gw?J<[Yzs#P3[C9$;:)b[eI0w`님YO\\gZ&(ChF.m<$t9B#.Z7껈1zfVdF"bR AiP(i"o<%B֑rp=?fR gcgsZuB~}j_tSYoz>q\Gde23 FvyN{vN`!*IBUpH;I+hN5)k9\ڥv'rv,☲&EBQߗ\w'}g!5"8Ȟƀ:9@Z&=oObxf*,Nzqj ŖXaA"HW4ԦKVMVKX@'\ixt+mHRPWibN;0}kk 3UEYebvW 0=+F`E9 !'ns?_\v a|V_Ԭ=ʧɌa}~UelK[#Š(AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPh ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9#? D8gW>%A5Ɓ'*s-m|9 H(le&R~O8[Տb&rm[8,1oz3D9p?s 70|u${ LV.)dpQO?;GB.v*( 99!u G97Khש%U@W|~ Hd^/%wFrǜ=ַo!4RI ('B=kʼn(BYzО5*ȪD rrwd"VmD9B?~c+#s*p<}U6$ۨ_G%$S0>W< 9 b|x<#wҷ SXIfY H})n#ggy_Lr9¶9=żWIsHCg 0:t1[E ݗ"FFPLۑA< ]]I+kzz~t+l.SUV,G'tn2]^aڬjIm3n9*kZ`]6ąz㞴o9Cu0eo$,cw;g28I7((r?ɬFepON~u gZKk47{Z]Z[ey#*l'9\muҵ.4mQ[[H<#?+ XtK{6x% X2֐ܥ٣RF0;b}*] |+X'8 )Rhq,8$Rxx5-UL+/=1`rZ  dR:`ޱI,F3De$c c8b+_h F@## n0[*d9f!Gyօ֡&y&R*9 2q֫,qCGM<$pAϵOEF-*$s$<)Ƕ*;1gtY N A'<P: /O"\bٱ)a8sXWkmNj(OZ!|tV3KmsϨf)&C8v H`GOƀ;1~Uga(u@X-U{s$5mm&`gb 3jjD7:oݎX'}OW#tتDpORcܖyLn%T yy!Wi,C{ ۚhz<jy,]my՝<<`Ʊit۞FN0:i\eDr dĄgd }y(Ǡ _YVT1?!cL\z|Y?ޔ0]W#%[=1ҋ8.iIH„$'>:^jW faVRc>alO`4C0.:`Q`+>X;c4znK!$cO qҕd"A#V B#7xֈnb@V6g^1 '>e9Kb"4 @B9"W7%!1+4 c0AVΉ)ivB=>l; 9>`/Ɠ|[2@F>ۤ2$qIG /dx8?M]N2v#ie;3v2@?/?gx[[0\@àFG^ f* U/_Ft,+\4kGS2F$>ciФjζUԞq?X\49ΑG C|!_kZlg$snGzzXi+8٥Z$28co99F)6YYJ=ޓW#I=oه ]`  N:I#Bc@rl 8>X<ƈ1\c1=C#kԓ1Pr>dyl6rz#OL`8SOG 1pF 1#ӵ\78:pmu۽u\Erdlsq?9pjgPzc$zzqJs=ˀWItS*wLGz=帑|ʮ7 x,H8鑀O; zQ TUެ~uK j~Ymz=el9>tCqbZvo)#hۨ ŀ|pxbʝ7QFH\Y"g sddV=t>GYc Âz+ZѤ6p7YȅyܧFو22)|=-W׋~EefÌ״ Q@((((((((((((((((((((((((E@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEr[_A 1,Z[>d2ٔ T}W_ bHМr+6w6$Z;q$`39*ZHKw,9Q!n>szˉrf)s#NVK8d)-ԥ(crTk# p!up= -#*GG ӿL [RUbb2aSHu#jmV 8Bq?C z' ڠT(lь};x_3ȑ.舟pXEiQۿ 8Jcf6Ry$Wd1;h $͏Y$QtT3SL2FA%Lj`C``g5ޖ+\ܸU^$4'q#srT{Qyz̉jCiE-DJ's:gi$8B8'$t8G`xj3 j6&!ҡNNp9]7AKh4wG%Ԯ9BH,Oڬ;CeF@1#>;Wq$}Mn֮>b -y>k;foYd$`XNrZT#oزPc#qu@aM8aWwǨ&j&VP֫8y6B{f^?*4$h 9<`==jr#Kd~=~,v۔.xKcq׿j郳N!|,Ha .z| +s+i$`gR5jvdçҺb[{eTm>Umh#hSh_jQ"7+!r}@N~dIg-Z0^^) ,ӭ  l(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE6((((((((((((((((((((((0\6wC|A$BsB@U a@Lq_m|`aucۥ|G_G)8 }-rP3̧^|åq}Yf[w ã%M3G-Ԍ P+קֹG`fL ҆ [*lʛ(mb𴈱-o3}apyS>P?1l-f5S_'d!]ԷQ}FA'ޭkXC3$QT±==a#@X<9mpN3V7#.R7K`NNf2?z }J[\)M*ev`&y݀Or86%#0 u[g7 R*qW9- V6 76)!*G!y phLW+tWn<!_,#=8[Vi,o74jr X`Q#tZ(IE=obxPjPڻl)&]x_|ye;+ʒجLx>U tjGوVU܇ XqAkm+Dp`~RMRp Bp}qoʝB<žqu9Q\jpoWyfTwGLŦ6Jr2Ǒ?)I 5=>xqLQ,,O@9٘*['|%l(lϻtQnzTrبAVX1׏sh;+R!lZfʹ6l ~xt?6n`bx~60.2 qXeѢ_Gf4aws2}xImG%2 uמhږi,_ZS<?2Md!i۟\(}}sƥaӣ, Xq;\.uXxܱ隆_d-o[2GLک61f c*xT]Xe]nZ.ѩdam$c%5Swi%u稩WLжn7v[v[Ӵ{ks,Da"b4l>w::uѦ4[u:hwVkn ]eA$2珡NiufiY$Ėv9RܓZ"souvֲ9q{}i GMh΁2Ó3r σ"cqoZZaEP ((((((((((((((((((((((((- 4(((((((((((((((((((((1o67t+]{^T%`$tjgf]%Y0SN1zBXS zJ*h,[|I%qS/t ֣C,A>ZuTMT,3ws2:c+˯aĖH>f 61$T<qT ڒHWlzTZrA(KmczB:;ƪ>{U (W c%FYB vxwUmm4 RM<9 V0bIVw8=I'{c4um2#G/X$d.w` fl䵋iZH5~#n~#I#ךֻY^鍐FoyóxPp#1S`kX,39L+TI#9wǰダGPI?4`|q“<+HK zrH2@qZCt29QK %q}pq7pZ^ʋ`x߷VT 8秽 3vm-c0Wu~UC,*$Žx( N( F#ȣ Hzաup""f\9 {cQ`ě =NOSg4Q.j]˒~rO=LBU-;SRM$$l{iI"ulnb3} <L._6~tQxrGGH8fU\Ċ!⨿7!V,2cO8B@{~ Jm ˖5qºp<,Ѹ]`h $q=Zdgu}Ƴ%Mޣ+Vc{Un 4^krQ-ю $gO[<2#}WGC[P\%\1 sG@zZ_hK"܀ "+l?_Ҫco46iܶ:{jE6]66{}iia",rm ys YU <3L`k.#eHO }`AjΞ6ۦ3Rbi$Gœ"]' ypmNy})l3anphͪz7 hw>🅓%?;4ʲ22U߸Z|?>~4V(#Э i mY@]/J@:cHIڂ:һ "H1odHVwO`/|{Qzi-61_lL{5g#.E.7S*q҂=)bsdIQG;&xb:eMg_?dX0i(O׷xzb>^ 0ʛ.6FQ>c3r##]V\HȎMY$s@18pI?u8fEm(A֘H~Ȧd0v5.7/=K=ݗV!vHl7I gEnG?ȫ24Zz}&̍XrSԌv$d.@ TZ #PcE >a?–VGHzdc"о7<%n˜NӸ~ۼ><F ÷穫>XLVBa[w>q7*v%\G#ڤk :42|>Lb:I kivD_'.&2q@F\‚p~Q 9mDi*BPIb|*xe9<,Vo Uw;U'#wLzSb-]~ʆ,Ȣ8u,rOdiV0.\r~=sҭn`O`JАw峧Yp$H:]$+^(p]K?1qD#$!]8F\ x7| gC`IP $K|;v[}^k]pr9n1rrkt;1$g8fVnz)=pܧ(((((((((((((((((((((((((:((((4PQZ(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEL|-ƢSw Iei=z σ?]?8j_\ҥZDgэʟ\v , A׬m2즺Jdb`I"-O99NhhH1?_z]8An$;T8\;~u5D 3ۖ~,dty˴ȉ6yzLNƞ̈ĒBՏ*0WWٕLwO|'Ed1͑ 1ǸcKyU4&>>]rԃxk@-Q^Ĝų|VV]+ۇzf+ۤ W(  l %%ha_4` iqe $o6ݾwm}WHh۴mbK8HhAv̾j!Ҭjo?ޥ>RuB H{IE:S'4}iQx4 ;&?o?d>~+Vٙ;@\JЈKIȸ(ܧ<`O ]B&d|0gePI%~u_®bN~Ru $O NHGQsȯ,$s2 s:ǭc}㍓{*bXwpN# ֒+sb68OALg?l/:/ʜW׌;WaKcWV`Š(AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPtQEPQ@Q@Q@Q@Q@RRZ))EQE (Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@|xZe`NC9Ǯ9毌zד)}(\1%@U/I{-͉9{u᯼=fdaqr{HaJg+&ߝvc7?6s  C)ggn$;XPsE]kv# `F`T$<ӟZ7٤ ˴0=~LtjnXFı{ƺUI#ff,TwzH]9\a,qx>nMs D rzzvNETL۶~F=ȝ++c'sWV` (Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@QTKIEQKJ(((((Pih@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@|4>0S vu=1_GW1+1 FBO}jx,s<;^Mp`=ҹ}U"vL=7FAǶF:[F_89ʩ+ߦ?>C|)fr G _S"G2VƒF%c4!R~|~\wۚ.{ ?=Jҧc]4إ{ъ\Qa U$gRA֜0G"8(HtxC@5]gU@c{˰gnVGFcmcSf8A$IGh9Ϧ1]w>Ru0IbF7otJ50*Xn`ӓi2T9RFelBT,׵Bogiho\~,"'q"`yaЎN[,)ƩAʳ;sӞ 0^?;">vsQy^ide&@9iHM$`X7t=8vCbF@_a~$F17 >r:'޲! 2Ŗ?5> F`00#Ltϥf'Nv.8;>E0.!}J)Xs۟VZPԧQx,+dijvuPO0|00OC8>>i HDlaŸ'2+lkVrIȐ[,w7.K_ֳ^5Xɘp<y yEt!%J WcϡWQ8Y+ȅ0@ FuE"1-&U$g>4~G˞=>8X<~J"d $›/45V[Y'\n$W?lubN@ u %fX C ipShRA|g4.ᔑ&[ 8-D 7ݛ zXj[T3瞸 GEd(9V!W^-.9-S08Et;mp Bqa}Pkua鈟'^{Tۼw X+kb~Y׽RQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQTEPKIE-%PEPEPEPEPEPEPEPE%)/ғPGQ@Q@Q@Q@Q@Q@Q@x7oŷ ky0l k<7ɤa= H眷zq$.",UwH!sxqw) *9Ǯk${jmf FIsW++I\H:n9Qy$ V+`Tԥ#4**rK ;__]+MJ)3b7bu,sGP+"x*-;sW-aO[@8 .T9һ[}%֣Vdރo}{T k.Fv#$W?֩^=9u\UĶ*麅ۣC L32oR=ykKÌc(>K#XY>T dn[ 1+>l3,t T K^S?.+ [_^x3-%ZZJ)RfZ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (#*()i()hSI@PQEQKI@RRQ@RQERQ@ ERQ@ GZJ>Z;qI.hPE&iiQEQEW|y5ĞcXdn<+1`:cO\V 岤/@8>X^~lgsJLFʀ+ 3=:=G v48Vb9 I9KFv.w|;es:1g9 EqX,UF;WVB˾(66A:u\98oΫ' uZ?%]MĝBF[F3hI&:>}>"xci⻹dP.C8麗?y%^/.9y6'\g?v6Z%4K3E";C TQ׵y'D_/sϣ]y>%-c+;F%@%%T Mq 5*:T} [i&_DtZ|c?cAO9̴.9rd^jsꗳ]Hd1X ;yZe~1WtyB`ma\$402Wx*džkOXᾱ{vʜe=ؠ>f)-i2vٵ >kvI8d:ˮirA GeC!H%۟ιGڟ@2t>mekokVT1\qkw[SlF7f6WQJ~n׈Sb[qgҽ .kE9?!{׾` (@-PIKE'jZ(@-PIE-%uZ))h (((( ( ( ( ( ( ( ( (#*((((ZJ()h)M%QE-%Q@Q@-%QERQ@Q@ ))hPGIE(4fZ%-- ((|Iƌ#nIlxًmT9N x.W3mQ$~xuC"V.7#\6lpe[Vf`!ϮJQj$P:rh ɒڧr8S+ʏOỎ"%ۦT;VGɊV7ćgAnGȷE"9ooKweaY!yo<UWx|< (]7ɦ_J3{ x'_m+NԼiwjڭ鳳ҍf`N@^ǒ9/ D̂i􋙠YQi ʝ98@g0-=jyw/䲴AIy ޳4P]3=/`,:WAM> *m[NU~am{'RG7Qiz-ͯ-܂<[ʾ{e kKRmℷ@Ϲua͝DS2Ecnvำ<𜓁]i/gRMm9ne̱o5AR*:b`>GӢ=AT&|v+s@R' 7+JmiiBsζHhhΩϯzhoSЃMг$qT׳Iiz֞4JI.B۝jA+$yV-A8v|Ufݝp?1Ih#ش&ҴKm[LѮ I%y\Sc_5 OWz6vc$Ziko Q#(?0s{WM`L ~9OZ֋cqkvt?I0IxBc%񅬒b:C1^--l vV݂bnNI.Յ:dkY֞in˵7UKS77{ ύc2}VOZ[B%Uv@=8Bj+oH]|P6=.P:k51[aÕnq>,ɷ'bH]G HW5,c&P>?֘Ϥb?HvߩX}_>~kH_A` (J(4PE-'z(b\QJ)qAR1@ E.()hRъ.QK1ENRQp\Q.Rъ1@ KF(b ( J\Q.IK1@ E.(Qp𢖀FiqE((Q@Q@Q@Q@RRRQKEREQ Q@gҊ1EGn (Z@(RPbR(1F)hb(GQ@Q@y-YuF6w~ҽ~{\jV.Sx&PTI8_˜}Yv0 ){uzq ́@w6QJfd| 7FZAt' e CCp=ǿQM lqөCG:h /XP@v*9$`T( 2.ޔTp* [m̯m$q؎Lغ*חr*qO5?%Fn楁p/; c?X2 @a=u\FG%Y3VCnwS9u猁Fu(pkcF|Ԛ(Ftr*e_k[4(\޼{}\0zK\G@xig-*s_.|'?Ġ[vyw$q[Asq4W *HJtv[]8( F3#9 92Rk$ }QYYev+Kc[iwo2u*\+ؐGkb(&E?Aڇ o>eYu C_B|$k N߇64urG"A۞@w-<9-4-BF%T(҅`8hݙoY9´H#ԗJ?TV+l|m->cʀvIǚX n9$p~Xr.t.Jj6x~(4YXghHotq.;(6Z~6(ZFK,bvʤeHfǖz>qKkU% ?L$@IpҾƑoHy\Z֋fo%ZRʳy*+4/"𖹤x_ZΒM Y]ΥmxHU8 )-;fC1 Hgꮗ'Cv)۴wyjrY܅ R)W<\q YGOt2 8nFrJNT!v2ъ:"mpG>7_8mz}@\vΖE'LϷOҭ祌Bd8sצ)Ѱ$ʁoO|9cd(qXgqǧOJa'8s\NFRU/!}+U0Fx1翵lE."S(=5j Cv'OjXͷKaw$>moRc5!,䴍pOL{Z`sxQۜ {{W_"Rn&&"{ Iq,A~wuږ?.-|Y_C J3P^>Ўjo4>cAsվxú-Ƽ WÖͣeю[7JL~dv#.4?~|@́`2vοǞ )ƥT6QID8Lj0H5 c4bK!\6&u ɬMA<"~F#Ѽo xgOiiu)?3vcy`+0@&/kQZ[܏%Զ?<^r骠>;)=H頪mBVZOOB^pSA sjegtfnXa،ӏJau3Cw&9 ~һ [{xe ,f-) ɶm/:o5Ձ X8d wָHPRL7C۩ǽw>$̆X.I?wu5wTIN;3Ҭf#w c$ϸ"MҗeΪ.1s?O:F`IRI'w=ꤤ]˔(: 8#&f tB] {NXZQUԮOo=OȭK#mt8c&Xw >qI+V rx0봞(Ku_欰Tl$Jcwrfٷ)s.|BN:~&kWr::ڰ-F~Vo.4xKH#rK1x<,h\nd`sJ4;C]'$^ھF-úNpT4 F# I~![!5-CKnuibb𷔙3`)`>!-HY7[5ʰ\M-C؂+/XﵯM+I[BP Hɴ7?}2f3o]{Z6,e7w)qOǖ%-Ս\Caz0e,@8>>Oz=FVi/*X J:'?3q-,>)#i6'IeF݈d\u=k|C6涗X֡'eUi_;G%ى< o$O ]"FHf`7lyGtIZ2-'Yv˶ %w2zV`g(Z}=|;,p> ǡ1׿׀ŀ1 E^k0{Q@4QK@%-!(Q@((zRQK@%(E%-%QKI@RQKI@P)QEQKIHZ((⒀ )qI@PQK %QL()3IEUvx4ޔQ`E6,EuZVhN:mE6`:mXQM@(S3K@(ShE74@&4)4)3I@&\ILњvh&hvC˸{M1IV~QTw]$%w}qqzܡOé\һ_?V˱}\V~nNzcsNf$, ('PnUUCC${1&bOHF)&SwE_avr< co'8.<2qۯlsYߩܗ7[oNj%M_;6#94BNjmB7ҧqj.2yrdc$q]xR#:/}$ *NqP*`:pw_\qXxv!`ԡ7|zj$5Pbll;7Z⏍&Կ({yegHkU`d3K9Spg 01Ț\k! W>860Gnp?F@fxm>M׵k+{uēG @oƨlYjZ]ɌXҺf`R`#RqFs\qi7dvdַEn7'qJV;Jی>nt[[eC1c*qk_9dk|FXsU, X|iz9[F]l$C\ dn_~ը^/f?wVm4.l.{y#젳\K9h&XPhhnF*zTдmg=kVֺ>{z+#cL$THM\_l|3w-B++;[dq:pӫtXOsv,2([˜QV#])RH~e!=N3@E>l 2qjzpfAZMɝȰYJhx3Z~BQɯw#4e.^2{{, MzfqE(BE%~PQEP(`QEQE 3EQE(h(QH撊()QEQEQEQEQEQEQEQEP( E%QTA΀ŒE&hR8&Z(3G4b))qF(֊0h( )(Ȣ3FEP0&1F(>bP 1s@4Q@hJu&i(C3Fhnis@ţ41ٯ9>Hp|虯>]6'RMdS[|Uѡe%=GsqZ焐|+2JY^D3|^;Zem9 8?xʗGL_\U )1zw; pWp;Y)3򓻎{sIĸ# .orD. s=?:ܘ͏r;qXy6tc-' =ʌzt5Q68դ zc%QA88lMas=dpKv?(M[hs?i \=zR `7{PhC5_FZ,VO/j.2ǀ)m<7)ou-@Z]ȁQe~a#+di7ÏkBtkk}Rr]k0zx3[|f{Nܭ G{1oT!3sI~{Muu'wWʀMq3Ij x_GӼ53V%&2JʬYۓ8c~6jKgu>X1Y{NyuDM3ԁ^kq۝GoL䘴03sH l|$ ’Z}WK?|uwwɐ'"|mÿzdwA7١>Z ^"umOVb} Dq b8U1Jͻ_.t۝{Sv{1K+v^%F ;I-4k~ޑ闓 " ql/)1͗.ќUx/ [jkƈoaApaUXX$\hWaYX*{ R8QZ,BMiM_ GZj6es:6LWhGebK8Q;˖(2*đ/Fg+@94 >5XL4[ \`b;x`֔Z5q/{h}2N?L %NTsy?ίL=G= jA_ݏ@?1m,ѴB:_TpFcd x fG4gda\\ 辵-~g~t6KDI01ljfBI)RfP}(4f@-;QME:m=SyE%ҒR:SM⃊ hS~=1@7b1@R❊LP1SIQKJ(+^^K,.Rr`1'2)?0g%9m«X+{}#zָQAᲿ6~__־4Z%K=Ia0$o|uWڌZ{=T=vN+x?wYw`ܑ$1k2x?ZϜm77N= rr%Csyeہ:fSꆥl]yFIWdT|#9+mPcβR#e 8k-n]:~=CNA/Joc&7.A4qҰVl4h?p7NT8`}Um8IQ%G0֕i9Ry@>W>j;1:1OtF3ڔIeܛx {BIGɁ<3EQluBu+D ;X_dӬ  0|#~t\ CF8RwMQT`]?_ךc%)B5U&8#*quF#6QYp(KOjhp<|rO{(Go ,V >5b >fu?lY_@xsU|OհUs}7nP {ǀ~|<'nuPF?AE|S^-94_3gjy$|=O h_4[}EKK+uk{ܚǨ K`旜Rɢj(sFh1@ G|hLQ\QL4@&)hQ@Q@QH(EPEPEPEPEPE (&)QK@QpZ( E-\RQL(QEQ@jZ(1F(`QFhqFh4f Q3Fh1F)sbR(&iQEhQ@Q@QE)9-):uaQMJWڐB[Rwji Q}\$ `r?*`xǎ> xV]Wd /5'_)HQI!0pE}eh 5 ."=j;! (ѷAkMJOk+_zà(iQ&tҤWGG<`?ƒUk ~^V߳'ٱ:t!Zd:ޝTfùᥣ g[im>l3`]쑩1XvkjF>=B_]Eq XZnqmOTDR?ٛᵾ<G`>7VjI!^0~ES@Ƿ?~`}?#D᷄>G[Sɨ\[{yp9B xP x_Vp{rοHm1@1 Wc-Sƒ5? manual_combined.xml manual.epub: images-epub manual_combined.xml cp manual_combined.xml epub/manual.xml make -C epub images-epub: images epub/fig install intro/fig/*.png epub/fig install gui/fig/*.png epub/fig epub/fig: mkdir epub/fig html: images-html cp manual_combined.xml html/manual.xml make -C html images-html: images html/fig manual_combined.xml install intro/fig/*.png html/fig install gui/fig/*.png html/fig html/fig: mkdir html/fig clean: make -C intro/fig clean make -C epub clean make -C html clean rm -f manual.pdf manual.fo manual_combined.xml qdmr-0.12.3/doc/manual/cli/000077500000000000000000000000001501654372000153345ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/cli/callsign.xml000066400000000000000000000116601501654372000176560ustar00rootroot00000000000000

    Writing the call-sign DB The command line interface also allows to write a call-sign DB (also known as Talker Alias) to the radio if the radio supports it. This can be done with the write-db command. This command behaves similar to the call-sign DB upload in qdmr. That is, it tries to select the call-signs being written automatically. Although many radios provide a huge amount of memory for the call-sign DB, they cannot hold the entire list of assigned DMR IDs. Therefore, a selection of relevant call-signs must be done. qdmr and dmrconf do that based on the DMR ID of the radio. DMR IDs are not random. They follow a pattern similar to land-line phone numbers. DMR IDs within a certain continent, country and region share a common prefix. This way, it is possible to select DMR IDs that are close by selecting IDs that share a common prefix. qdmr and dmrconf use the default DMR ID of the radio to select the call-signs to be written. This ID is obtained by qdmr from the codeplug. dmrconf does not require a codeplug to be present for the upload. Consequently, it requires the explicit specification of the ID to base the selection on. The ID can be specified using the or options. It is also possible to just specify the prefix instead of an ID. This example writes a call-sign DB to the connected radio using the DMR prefix 2621 (for the region Berlin/Brandenburg in Germany). The radio will fill the available space with as many call-signs as possible. So for the previous example, dmrconf will not only program all IDs with the prefix 2621 but as many as possible starting with those close to that prefix. It is possible to limit the number of call-signs encoded using the or option. Unfortunately, there is always an exception to a rule: Some countries have several prefixes or you may program the call-signs of several countries that are not necessarily close in terms of their prefixes. For these cases, it is possible to specify a list of prefixes to the option. This example will select and write up to 10000 (given the radio can hold that amount) call-signs staring with those closest to the prefixes 262 and 263 (both prefixes for Germany).
    Specifying own databases With version 0.11.3, it is possible, to specify a user-crafted JSON database. Then, this file will be used for selecting the call signs for writing the call-sign DB. Here, up to 10000 call signs are written from the my_db.json JSON file starting with those IDs closest to the prefix 262. Specifying a user-curated call-sign DB, simply replaces the public one and thus all methods described above still work.
    Encoding a call-sign DB Like for the binary codeplug, it is also possible to generate and store the binary representation of the call-sign DB using the encode-db command. This command is only useful for debugging purposes as the binary representation of the call-sign DB cannot be written to the device. The encode-db takes the same arguments as the write-db command but additionally needs a filename to store the encoded DB into as well as the radio to encode for using the option. Like the previous example, this one encodes up to 10000 call-signs starting with those closest to the prefixes 262 and 263 for a AnyTone AT-D878UV radio and stores the result into callsigns.dfu.
    qdmr-0.12.3/doc/manual/cli/codeplug.xml000066400000000000000000000244131501654372000176640ustar00rootroot00000000000000
    Reading and writing codeplugs The major feature of the command line tool is certainly the ability to read and write codeplugs from and to the device. The majority of the action happens automatically. Like the detection of the radio. If something goes wrong, an error message will be written to stderr. A more detailed logging can be enabled by passing the flag.
    Reading a codeplug To read a codeplug, the read command is used. The codeplug can be stored in several formats. The human readable extensible codeplug format (YAML) and as a binary memory dump of the codeplug memory on the device. dmrconf detects the format based on the file extension or by means of an additional flag. The latter is particularly important if the read codeplug should be written to stdout for piping it to another program for further processing. Will simply read the codeplug from the detected device and stores it in the extensible codeplug format in the file codeplug.yaml. The format was detected by the file extension yaml which refers to the extensible codeplug format using YAML. To store the memory dump of the codeplug memory of the radio, the file extension should be dfu. As mentioned above, it is also possible to dump the decoded codeplug to stdout allowing to pipe the codeplug into another program for processing. This can be done by omitting the output filename. Then, however, the output format is not specified anymore. In this case, one the explicit format flags or must be used to specify in which format the codeplug should be written. These flags can also be used to store a codeplug in a particular format in arbitrarily named files. This example reads the codeplug from the connected device and decodes it. The decoded codeplug is then piped to the python script my_script.py.
    Decoding binary codeplugs It is also possible decode binary codeplugs that has been read from the device earlier and stored as a memory dump (i.e., in a dfu file). This step is actually the second step automatically performed during reading. When reading a codeplug, in a first step the memory dump of the codeplug is read from the device. In a second step, the read binary codeplug is then decoded and dumped in a human readable format. The decode command performs that second step. To do that, it needs two additional information: The radio type, from which this codeplug was read and the format to write the decoded format to. Like for the read command, the latter can be passed by the output filename extension or via an additional flag. This example performs the same actions like a simple read command (assuming a TyT MD-UV390 is connected). It first downloads the binary codeplug. This time, the memory dump is stored in a binary form (dfu file). The second command then decodes the binary codeplug into the extensible codeplug format (yaml file), assuming that the binary codeplug stems from a TyT MD-UV390. Since version 0.8.1, it is now also possible to decode some manufacturer binary codeplug files as they are produced by the manufacturer CPS. To signal the decode command to treat the file as a manufacturer CPS file, you need to pass the or and the option. The latter tells the decode command the format of the file. That is, the call Will decode the manufacturer CPS file manufacturer_cps_file.rdt assuming it is a file generated by the CPS for the TyT MD-UV390. Like for the normal decoding the output format must be specified either by file extension or flag.
    Debugging the codplug decoding Under normal circumstances, it makes no sense to first read the binary codeplug from the device and then decoding it in a separate step as the read command will do that for you. However, if there is a bug in dmrconf that gets triggered by your codeplug on the device, the binary codeplug is an invaluable resource for debugging the application. Consider filing an issue at the bug tracker and include the binary codeplug as an attachment. If you like, you can also send me your codeplug directly. I'll keep it confidentially.
    Writing a codeplug To write a codeplug into the device, the write command is used. The codeplug can be read from several formats. The extensible codeplug format (yaml file) as well as the old table based format (conf file). It is not possible to write binary codeplugs without decoding them first. Like for the read command, dmrconf will detect the format based on the file extension or by passed flags. This example will write the codeplug stored in the extensible codeplug format in codeplug.yaml to the connected device. Before writing the codeplug to the device, the connected device gets detected and the codeplug gets verified. If the verification step fails, one or more error messages are written to stderr describing the issue with the codeplug. One verification step is the check whether all channel frequencies are within the frequency limits specified by the manufacturer. The latter check can be disabled using the flag. There are also some flags controlling the assembly of the binary codeplug. When the flag is set, the codeplug will be generated from scratch using default values for all options not explicitly specified in the codeplug file. This might be used to initialize a brand new radio. However, any changes made to the radio are lost. When this option is not set, the codeplug gets encoded and written in a two-step process. First the current binary codeplug is downloaded from the radio. Then the codeplug file is used to update the binary codeplug. The result is then written back to the device. This ensures that all settings made in the radio are kept, unless they are explicitly set in the codeplug file. The and flags will tell dmrconf to enable the GPS or roaming feature whenever any of the programmed channels use the GPS or a roaming zone. (This depends also on the ability of the radio.)
    Verify a codeplug The aforementioned verification of the codeplug file can also be performed separately using the verify. This command also needs to know against which radio the codeplug should be verified. The radio must be specified using the option. This command will verify the codeplug stored in codeplug.yaml in the extensible codeplug format against an AnyTone AT-D878UV. Like for the write, any issues are written to stderr. Like for the write command, the verification can be altered using the flag.
    Encoding codeplugs Is is also possible to perform the encoding step of the codeplug separately. This can be done with the encode command. Like for the verify command, the encode command also needs the radio for which the codeplug should be encoded. The input format of the codeplug is again specified by either the file extension of the codeplug file or by flags. This call will encode the codeplug codeplug.yaml specified in the extensible codeplug format for a radio running the OpenGD77 firmware and stores the resulting binary codeplug in codeplug.dfu. Like for the write command, the encoding can be controlled using the and flags.
    qdmr-0.12.3/doc/manual/cli/commandline.xml000066400000000000000000000030401501654372000203410ustar00rootroot00000000000000 The <command>dmrconf</command> command line tool Beside the graphical user interface provided by qdmr (see ), there is also a command line tool allowing to read/write codeplugs from and to the radios. It is based on the same library called libdmrconf and thus provides the same features like qdmr. This chapter will briefly describe the command line tool and how it can be used to handle codeplugs from the command line. The command line tool might be helpful in cases, where the codeplug file (see ) gets assembled by a script. Then the same script may upload the codeplug to the radio using the command line tool. Additionally to the the features of the GUI (see ), the command line tool provides some features to analyze the memory representation of the binary codeplugs as well as debugging their implementation. qdmr-0.12.3/doc/manual/cli/dangerzone.xml000066400000000000000000000057501501654372000202210ustar00rootroot00000000000000
    Danger zone You are about to enter the land of pain. Continue on your own risk. Some radios are actually identical to others. They also identify themselves as a different radio. An example for such a radio is the Retevis RT3S, this radio is simply a relabeled TyT MD-UV390. The RT3S actually identifies itself as a MD-UV390. From the perspective of the CPS, these two radios are indistinguishable. Consequently, qdmr and dmrconf will always identify the RT3S as a MD-UV390. There are, however, virtually identical radios. These are radios that actually identify themselves as different models but the firmware, communication protocol and codeplug is basically identical to another model. An example for such virtually identical models are the AnyTone AT-D868UV and the BTech DMR-6X2. Each model identifies itself correspondingly and thus is distinguished by the CPS. Some of these relationships between virtually identical models are known to qdmr and dmrconf. In these cases, the CPS will treat these radios as identical. Some of these close relationships between models are not known to dmrconf. In these cases, dmrconf will stop with an error that a radio is unknown although it actually supported as a different model. In these rare cases, it is possible to override the radio detection using the option. This option is usually used to specify the type whenever the radio model is not detected. This option also overrides the model detection and thus allows to handle virtually identical radios. For example, if the relation ship between the AT-D868UV and the DMR-6X2 would have not been known to dmrconf, a codeplug could read anyway from the device by calling Here the radio detection (resulting the detection of a DMR-6X2) gets overridden and the radio is handled as a AT-D868UV. If you know of such virtually identical radios that dmrconf does not recognize, consider filing an issue at the bug tracker. Of cause, handling a radio differently as it identifies itself may cause permanent damage to the radio. So you should be very sure that the radios are actually identical when overriding the radio detection routines.
    qdmr-0.12.3/doc/manual/cli/dmrconf.xml000066400000000000000000000347401501654372000175160ustar00rootroot00000000000000 dmrconf 1 dmrconf 0.11.3 User Commands Hannes Matuschek dm3mat@darc.de Main author dmrconf Command-line tool for programming DMR radios. dmrconf file Description dmrconf is a command-line tool to program DMR radios. That is, generating and uploading codeplugs to these radios. To this end, dmrconf uses a common human-readable text format to describe the codeplug for all supported radios (see below). This allows one to share codeplugs between different radios. Additionally, dmrconf also allows one to download codeplugs from the radio and to store it in the human-readable text format. Commands detect Detects a connected radios. You may specify a specific device using the or option. read Reads a codeplug from the radio and stores it into the given file. This command may need the or options if the file type cannot be inferred from the filename. write Writes the specified codeplug to the radio. This command may need the , or options if the file type cannot be inferred from the filename. write-db Writes the call-sign database to the device. This command may need the option to select call-signs if the complete database does not fit into the device. If specified, all call-signs closest to the specified ID are used. verify Verifies the codeplug with the connected radio or the specified radio passed with the option. This command may also need the or options if the file type cannot be inferred from the filename. encode Encodes a YAML codeplug as a binary one for the connected or specified radio using the option. encode-db Encodes the call-sign database as a binary one for the connected or specified radio using the option. This command may need the option to select call-signs if the complete database does not fit into the device. If specified, all call-signs closest to the specified ID are used. decode Decodes a binary codeplug and stores the result in human-readable form. The radio must be specified using the option. info Prints some information about the given file. Options or Specifies the file format for the input file for the verify, encode and write commands. This option is not needed if the filetype can be inferred from the filename. That is, if the file ends on .conf or .csv. or Specifies the file format for the input or output file for the verify, read and write commands. This option is not needed if the filetype can be inferred from the filename. That is, if the file ends on .yaml. or Specifies the file format for the input or output file for the verify, read and write commands. This option is not needed if the filetype can be inferred from the filename. That is, if the file ends on .bin or .dfu. or Specifies the file format for the input file for the decode command to be the manufacturer binary codeplug format. Not all manufacturer formats are implemented. or Specifies the device to use. Either a USB BUS:DEVICE number combination or the name of a serial interface. The device must be specified if the automatic radio detection fails or if more than one radio is connected to the host. or NAME Specifies the radio for the verify, encode or decode commands. This option can also be used to override the automatic radio detection for the read and write commands. Be careful using this option when writing to the device. An incompatible code-plug might be written. or DMR_ID Specifies the DMR ID or a comma separated list of DMR ID prefixes for the write-db or encode-db commands. More than one ID may be specified using a comma-separator. or N Limits several amounts, depending on the context. When encoding or writing the call-sign db, this option specifies the maximum number of call-signs to encode. or JSON_FILE Specifies the call-sign database to use for writing a user-db to the device. Initializes the code-plug from scratch. If omitted (default) the codeplug on the device gets updated. This maintains all settings made earlier via the manufacturer CPS or on the radio itself. Automatically enables GPS/APRS if at least one GPS/APRS system is defined and used by any channel. Automatically enables roaming if at least one roaming zone is defined and used by any channel. Disables the enforcement of limits. Warnings are still shown. or Displays a short help message. Lists all supported radios. or Displays the version number. or Enables debug messages. Supported Radios The following list contains all supported radios and their names for the option. All radios running the Open GD77 firmware. Anytone AT-D868UVE or Baofeng DMR-6X2. Anytone AT-D878UV. Anytone AT-D878UVII. Anytone AT-D578UV. TYT MD-390 or Retevis RT8. TYT MD-UV390 or Retevis RT3S. TYT MD-2017 or Retevis RT82. Retevis GD-77. Baofeng/Radioddity RD-5R. Baofeng DM1701 or Retevis RT84. Bugs This program is still under development and may contain bugs that may cause harm to the radios and may even destroy them. Hence you may use this software on your own risk. If you want to have guaranties, consider using the CPS (code-plug programming software) supplied with your radio. qdmr-0.12.3/doc/manual/cli/qdmr.xml000066400000000000000000000107251501654372000170260ustar00rootroot00000000000000 qdmr 1 qdmr 0.10.2 User Commands Hannes Matuschek dm3mat@darc.de Main author qdmr Graphical tool for programming DMR radios. qdmr Description qdmr is a graphical tool to program DMR radios. That is, reading, editing, generating and uploading codeplugs to these radios. The configuration of these radios is usually stored in a highly vendor and device specific binary codeplug. qdmr stores this configuration in a human-readable and device independent format, thus allowing for sharing codeplugs across devices. Options If the optional codeplug file is passed to qdmr, it gets loaded on startup. Otherwise, the application starts with an empty codeplug. This option allows to set a Qt style for the application. This can be used to alter the style of the widgets. So call qdmr -style=windows if you fancy. However, this option is of real use if you want to use a dark theme. By installing a dark Qt theme like kvantum-dark and starting qdmr by passing this theme to the style option. That is qdmr -style=kvantum-dark You may set the Qt style for all Qt applications using the QT_STYLE_OVERRIDE environment variable. This option allows for specifying a style sheet to alter the appearance of some or all widgets of qdmr. Logging Qdmr writes a lot of debug and error messages to stderr. This can be used to inspect some issues during reading/writing the a codeplug to the device. There is also a log file containing these messages usually at ~/.local/share/DM3MAT/qdmr/qdmr.log. Bugs This program is still under development and may contain bugs that may cause harm to the radios and may even destroy them. Hence you may use this software on your own risk. If you want to have guaranties, consider using the CPS (code-plug programming software) supplied with your radio. There are some issues with the so-called dark mode under Linux. The Qt library is not able to detect that the desktop has a dark mode theme. To this end, qdmr cannot react to it too. To enable a dark theme for qdmr, see the option above. Writing a single application supporting several radios of different manufacturers is a hard task. To this end, there are plenty of bugs to be expected. If you stumble across one of them, consider opening an issue at . qdmr-0.12.3/doc/manual/cli/various.xml000066400000000000000000000072671501654372000175620ustar00rootroot00000000000000
    Various features of <command>dmrconf</command> Beside reading and writing codeplugs or writing the call-sign DB, there are some more commands and features that mainly concern the debugging of the codeplug and call-sign DB encoding and decoding. If you are interested in the codeplug internals, you may use these commands to study them.
    Getting help As usual for command line tools, a brief help text about the commands and options gets written to stdout when the or option is passed. No other commands passed are executed then. Similar to the option, it is possible to print the version number of dmrconf using the or option. Like for the , no other commands passed get executed. To get a list of keys identifying radio models when specified using the option, can be passed. This will print a small table to stdout that lists the keys for each known radio as well as the model and manufacturer name.
    Detecting the radio type The command detect solely detects the radio. No data is written or read from the device (except of the radio model information). This command can be used to check whether a radio is detected correctly. This command will try to detect the connected radio. If a known radio is found, the model and manufacturer name is written to stdout. If no radio is detected or if the model is unknown or unsupported, an error message is written to stderr.
    Inspecting binary codeplugs The encode, encode-db and read commands can store the codeplug and call-sign DB in binary form in a DFU file. The generated file is a valid DFU (device firmware update) file, that can be handled with other DFU tools. A DFU file may contains several so-called images. Each image may contains several so-called elements. The latter represents a segment of memory with an associated memory address. The info command produces a hex-dump of the DFU file that is written to stdout. It can then be inspected using more or less. This example will generate a hex dump of the encoded codeplug in the specified DFU file codeplug.dfu. The result is piped to less for easy reading. The hex dump also prints some information about the file structure as well as memory addresses. It also collapses repetitive memory sections (similar to hexdump -C). To this end, this command is a helpful tool for debugging the encoding of codeplugs and call-sign DBs.
    qdmr-0.12.3/doc/manual/codeplug/000077500000000000000000000000001501654372000163675ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/codeplug/anytone/000077500000000000000000000000001501654372000200445ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/codeplug/anytone/aprs.xml000066400000000000000000000062631501654372000215420ustar00rootroot00000000000000
    FM APRS settings extension This extensions allows to specify some additional settings for the FM APRS System. As AnyTone devices allow only for one FM APRS system, these settings can be considered radio wide. The AnyTone FM APRS extension is a mapping named anytone. It contains the device specific settings for that FM APRS system. AnyTone devices only allow for a single FM APRS system.
    FM APRS system attributes FM APRS system extension fields txDelay A delay before the start of the transmission. Specified with a resolution of 20ms. The default value is 60ms. preWaveDelay A delay between the start of the carrier and the actual APRS transmission. Default 0ms. passAll Basically disables the CRC check on received APRS messages. reportPositionreportMicEreportObject reportItemreportMessagereportWeather reportNMEAreportStatusreportOther A bunch of flags indicating what is being reported (?!?). frequencies While AnyTone only allows for a single set of parameters for the FM APRS transmission and reception, it allows for several frequencies. The default frequency is specified by the revert channel of the FM APRS system. Seven additional frequencies might be specified through this list. Each entry is a mapping, specifying a unique ID for the frequency, a name and the actual frequency. The id can then be used to reference this FM APRS frequency in the AnyTone channel extension.
    qdmr-0.12.3/doc/manual/codeplug/anytone/channel.xml000066400000000000000000000161731501654372000222060ustar00rootroot00000000000000
    Channel extensions This extensions allows to set some device specific channel settings for AnyTone devices. There are some general settings, valid for FM and DMR channels as well some FM and DMR specific settings. Examples of the <trademark>AnyTone</trademark> extensions for FM and DMR channels.
    Common channel attributes This section describes the common channel attributes. These attributes are shared between FM and DMR channels. talkaround If true, talkaround is enabled for this channel. This will bypass the repeater on repeater channels and allows to talk to other participants directly. frequencyCorrection Specifies some sort of correction to the frequency in some unknown units. Consider contacting me, if you know more details about it. handsFree If true, the hands-free feature is enabled for this channel. The actual configuration of the hands-free feature happens in the general settings extension. This setting is only applicable to the AT-D578UV device. fmAPRSFrequency Optional alternative FM APRS frequency specified in the AnyTone extension to the APRS settings.
    FM channel attributes These attributes are only valid for FM channels. reverseBurst If true, a CTCSS phase-reverse burst is sent at the end of transmission. This phase reversal of the CTCSS tone at the end of the transmission can be used to prevent a noise tail at the end of the transmission on the receiver side. See Reverse CTCSS for details. rxCustomCTCSS If true, the custom CTCSS frequency (see customCTCSS) is used to control the squelch. txCustomCTCSS If true, the custom CTCSS frequency (see customCTCSS) is transmitted. customCTCSS Specifies a custom CTCSS frequency in Hz. The frequency can be specified with a resolution of 0.1Hz. squelchMode Specifies the squelch mode. Must be one of Carrier, SubTone, OptSig, SubToneAndOptSig or SubToneOrOptSig. scrambler If true, the analog scrambler is enabled. It is not legal to use scramblers in HAM radio.
    DMR channel attributes This section describes the attributes, applicable to DMR channels on AnyTone devices. callConfirm If true, the call confirmation is enabled. sms If true, the SMS reception is enabled. smsConfirm If true, the SMS confirmation is enabled. dataACK If true, the transmission of data acknowledgement is enabled. simplexTDMA If true, TDMA mode for simplex channels is enabled. This mode is also known as DCDM (dual-capacity direct mode, see DCDM) adaptiveTDMA If true, the adaptive TDMA mode is enabled. This makes only sense, if simplexTDMA is enabled too. In this case, the radio is able to receive both simplex TDMA as well as normal simplex DMR on the channel. loneWorker If true, the lone-worker feature (see Lone Worker) is enabled for this channel. Usually not used in HAM radio. throughMode If true, the through mode is enabled. What ever that means. If you know more about it, consider extending this section.
    qdmr-0.12.3/doc/manual/codeplug/anytone/contact.xml000066400000000000000000000031271501654372000222240ustar00rootroot00000000000000
    DMR contact extension This extensions allows to specify some DMR contact attributes for AnyTone devices. This extension is only applicable to DMR (digital) contacts. The AnyTone contact extension is a mapping named anytone. It contains the device specific settings for that DMR contact.
    Contact attributes For now, there is only one attribute allowing to override the alert type for a DMR contact. Channel extension fields alertType The alert type specifies the notification type when a call from that contact is received. To this end, this field overrides the ring setting of the common contact settings. This attribute is either None, Ring or Online. If None is set, the notification is disabled.
    qdmr-0.12.3/doc/manual/codeplug/anytone/extensions.xml000066400000000000000000000012321501654372000227630ustar00rootroot00000000000000
    <trademark>AnyTone</trademark> Codeplug Extensions This chapter documents the extensions and settings specific to AnyTone radios. Please note, that not all attributes are applicable to all devices.
    qdmr-0.12.3/doc/manual/codeplug/anytone/fig/000077500000000000000000000000001501654372000206115ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/codeplug/anytone/fig/d578_front.svg000066400000000000000000002651041501654372000232410ustar00rootroot00000000000000 qdmr-0.12.3/doc/manual/codeplug/anytone/fig/d578_mic.svg000066400000000000000000003714201501654372000226600ustar00rootroot00000000000000 qdmr-0.12.3/doc/manual/codeplug/anytone/settings.xml000066400000000000000000001610661501654372000224400ustar00rootroot00000000000000
    Settings extension This extensions allows to specify some device specific settings for AnyTone devices. As there are a myriad of settings for these devices, the settings extension is split into several sub-extensions, grouping the settings by domain. The AnyTone settings extension is a mapping named anytone. It contains the device specific settings for AnyTone devices.
    General settings attributes General settings extension fields subChannel If true, the sub-channel is enabled. selectedVFO Specifies the currently selected VFO. Must be one of A or B, selecting VFO A and B, respectively. modeA, modeB Specifies the VFO mode for VFOs A and B. Must be one of Memory or VFO. The former selects the memory/channel mode while the latter selects the VFO mode. zoneA, zoneB Specifies the current zones for VFO A and B. Must be a reference to a zone. vfoScanType Specifies scan-type for the VFO mode. Must be one of Time, Carrier or Stop. Default is Time. If Time is selected, the scan is stops for a specified period. If Carrier is selected, the scan stops as long as a carrier is detected. If Stop is selected, the scan stops entirely. minVFOScanFrequencyVHF, maxVFOScanFrequencyVHF, minVFOScanFrequencyUHF, maxVFOScanFrequencyUHF Specifies the frequency ranges for the VFO scan in the VHF and UHF bands. These frequency can be specified arbitrarily. If a unit-less floating point value is given, it is interpreted as in MHz. If a unit-less integer value is given, it is interpreted as in Hz. keepLastCaller If true, the last caller ID is kept even, when the channel is changed. Seriously, who needs that option? Just keep it or don't. Why is it so important? AnyTone codeplugs are full of these unnecessary settings. vfoStep Specifies the VFO tuning step-size. steType, steFrequency, steDuration Specifies the squelch-tail elimination type, frequency and duration. The type must be one of Off, Silent, Deg120, Deg180 or Deg240. The frequency is specified as a floating point number in Hz. The duration is specified in ms. This option is applicable to AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV devices. tbstFrequency Specifies TBST/pilot-tone frequency used to enable/open a repeater. This should be one of the common frequencies like 1000, 1450, 1750 and 2100 Hz. This option is applicable to AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV devices. proMode If true, enables the professional mode. If enabled, some menu options are hidden. This option is applicable to AT-D878UV, AT-D878UV II and DMR-6X2UV devices. maintainCallChannel If true, maintains the call-channel. Whatever that now means. If you find it out, let it me know. This option is applicable to AT-D878UV, AT-D878UV II and DMR-6X2UV devices. bootSettings, powerSaveSettings, keySettings, toneSettings, displaySettings, audioSettings, menuSettings, autoRepeaterSettings, dmrSettings, roamingSettings, gpsSettings, simplexRepeaterSettings Many more funny settings, grouped by domain. For details, see the descriptions below.
    Boot settings Collects some settings, that control the startup of the radio. These settings are collected under the bootSettings key within the anytoneSettings entry. Boot settings extension fields bootDisplay Specifies what is seen on the display during boot. Must be one of Default, CustomText or CustomImage. bootPasswordEnabled, bootPassword Enables/disables and specifies the boot password. The boot password is a string of up to 8 number chars. defaultChannel, zoneA, channelA, zoneB, channelB If true, defaultChannel enables the specified zones and channels to be set on startup. zoneA and zoneB specify the startup zone for VFOs A and B, respectively, and must reference a defined zone. While channelA and channelB specify the startup channels for each VFO. If no channels are defined, the VFO is chosen. priorityZoneA, priorityZoneB Specifies the priority zones for each VFO. These must reference a defined zone. defaultRoamingZone Specifies the startup roaming zone. Must refer to a defined roaming zone. This option is applicable to AT-D878UV, AT-D878UV II and AT-D578UV devices. gpsCheck Enables the GPS check on boot. This option is applicable to AT-D878UV and AT-D878UV II devices. reset Enables a MCU reset on boot. This option is applicable to AT-D878UV and AT-D878UV II devices.
    Power-save settings Collects some settings, that configure the power-save features. These settings are collected under the powerSaveSettings key within the anytoneSettings entry. Power-save setting extension fields autoShutdown Specifies the time-out for the automatic shut down. If 0, the automatic shut-down is disabled. resetAutoShutdownOnCall If true, the automatic showdown timer is reset on every call. powerSave Specifies the power-save mode. Must be one of Off, Save50 or Save66. The latter two will save 50% or 66%, respectively. atpc Enables the ATPC (Adaptive Transmission Power Control). It reduces the transmission power, if a strong signal is received.
    Keypad settings Collects some settings, that configure the keypad, lock etc. These settings are collected under the keySettings key within the anytoneSettings entry. This extension also allows to assign specific functions to the programmable function keys on the radio. These are the side-keys, top-key and the front buttons labeled P1 and P2. The following list shows all functions, that can be assigned to each of these buttons. Some function are not available on all devices. Key functions Off No function at all. Voltage Shows the battery voltage. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. Power Toggles though the power settings. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. Repeater Toggles the talk-around feature. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. Reverse Swaps the RX and TX frequencies. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. Encryption Shows the encryption key selection. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. Call Performs an FM call with the configured DMTF/two-tone/five-tone ID. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. VOX Shows the VOX level selection. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II and DMR-6X2UV. ToggleVFO Toggles between VFO and memory/channel mode. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV. SubPTT PTT for the sub channel. Only applicable for PF1, PF2 and PF3 keys. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II and DMR-6X2UV. Scan Starts/stops a scan. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. WFM Toggles the WFM (or Air band) receiver. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. Alarm Starts/stops an alarm call. Applicable to AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. RecordSwitch Enables/disables recoding function. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. Record Start/stops a recoding. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. SMS Start writing an SMS. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. Dial Start manual dial. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. GPSInformation Applicable to AT-D868UV, AT-D578UV and DMR-6X2UV. Monitor Enables the (FM) monitor. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. ToggleMainChannel Toggles between VFOs A and B for the main channel. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. HotKey1, HotKey2, HotKey3, HotKey4, HotKey5, HotKey6 Triggers one of the programmed hot-key function. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. WorkAlone Toggles the lone-worker mode. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. SkipChannel During a scan, use this function to skip the current channel. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. DigitalMonitor Toggles the digital monitor. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. SubChannel Enables/disables the sub-channel. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. PriorityZone Change to the priority zone. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. VFOScan Toggles the VFO scan. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. MICSoundQuality Toggle enhanced sound mode in DMR mode. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. LastCallReply Allows to answer the last call, after the hang-time has passed. Applicable to AT-D868UVE, AT-D878UV, AT-D578UV and DMR-6X2UV. ChannelType Change the channel type. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. SimplexRepeater Enables/disables the simplex repeater feature. Applicable to DMR-6X2UV. Ranging Applicable to AT-D868UV, AT-D578UV and DMR-6X2UV Roaming Manually starts roaming. Applicable to AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. ChannelRanging Applicable to AT-D868UV, AT-D578UV and DMR-6X2UV MaxVolume Shows the maximum volume setting. Applicable to AT-D868UV, AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. Slot Changes the time-slot, AT-D868UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. Squelch Shows squelch settings. Applicable to AT-D578UV and DMR-6X2UV. APRSTypeSwitch Applicable to AT-D578UV. Zone Shows the zone selection dialog. Applicable to AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. RoamingSet Shows the roaming settings(?). Applicable to AT-D878UV, AT-D878UV II and DMR-6X2UV. APRSSet ??? Mute Mutes the radio for a specified interval. Applicable to AT-D878UV, AT-D878UV II and DMR-6X2UV. MuteA, MuteB Mutes the VFO A/B for a specified interval. Applicable to AT-D578UV. XBandRepeater Enables the cross-band repeater function. Applicable to AT-D578UV. Speaker Applicable to AT-D578UV. CtcssDcsSet Shows the CTCSS/DCS settings for FM channels. Applicable to AT-D878UV, AT-D878UV II and DMR-6X2UV. TBSTSend Sends the TBST tone. Applicable to AT-D878UV, AT-D878UV II, AT-D578UV. Bluetooth Enables/disables bluetooth function. Applicable to AT-D878UV, AT-D878UV II, AT-D578UV. GPS Enables/disables GPS. Applicable to AT-D878UV, AT-D878UV II, AT-D578UV. ChannelName Toggle channel name and frequency display. Applicable to AT-D878UV, AT-D878UV II, AT-D578UV. CDTScan Starts CTCSS/DCS scan for FM channel. Applicable to AT-D878UV, AT-D878UV II, AT-D578UV. APRSSend Transmits the positioning information. Applicable to AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. APRSInfo Shows received APRS information. Applicable to AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. GPSRoaming Applicable to AT-D578UV. NoiseReductionTX Applicable to AT-D578UV. The following list contains all keypad setting fields. Keypad settings extension fields funcKey1Short, funcKey1Long, funcKey2Short, funcKey2Long, funcKey3Short, funcKey3Long, funcKey4Short, funcKey4Long, funcKey5Short, funcKey5Long, funcKey6Short, funcKey6Long Specifies the functions associated with the function keys P1-P6 for a short and long press. Must be one of the items in the key function list above. The function keys 1-6 are labeled P1-P6 and are located either below or next to the display. funcKeyAShort, funcKeyALong Specifies the function associated with the programmable function key PF1 or A. This button is located right below the PTT button (on handheld radios) or labeled A on the hand microphone (AT-D578UV). funcKeyBShort, funcKeyBLong Specifies the function associated with the programmable function key PF2 or B. This button is the second one below the PTT button (on handheld radios) or labeled B on the hand microphone (AT-D578UV). funcKeyCShort, funcKeyCLong Specifies the function associated with the programmable function key PF3 or C. THis button is the colorful one on the top (on handheld radios) or labeled C on the hand microphone (AT-D578UV). funcKeyDShort, funcKeyDLong Specifies the function associated with the programmable function key D. This button is located on the hand microphone (AT-D578UV only). longPressDuration Specifies the minimum duration of a long press on one of the programmable function keys. autoKeyLock Enables/disables the automatic key lock. knobLock, keypadLock, sideKeysLock Specifies, whether the knob, keypad and side-keys are locked, when the key-lock is enabled. forcedKeyLock Don't use. Prevents the manual key-lock release. Only applicable to commercial applications.
    Tone settings Collects some settings, that configure signal tones. These settings are collected under the toneSettings key within the anytoneSettings entry. Tone settings extension fields keyTone Enables the key-pad tones. Should be disabled. keyToneLevel Specifies the key-tone level, 0 means user adjustable. smsAlert Enables the SMS alert tone. callAlert Enables the call alert tone. See also callMelody. dmrTalkPermit, fmTalkPermit Enables the DMR talk permit tone. dmrReset Enables the DMR reset tone. See also resetMelody. dmrIdle, fmIdle Enables the idle tone (for DMR and FM channels). See also idleMelody. startup Enables a startup tone. tot Transmit time-out warning tone. callMelody, idleMelody, resetMelody, callEndMelody Specifies the melody played on each of these occasions. These melodies can contain up to 5 notes (and pauses). For the sake of simplicity, qdmr supports the LilyPond musical notation for these melodies. E.g., a8 b e2 des4 d.
    Display settings Collects all display settings. These settings are collected under the displaySettings key within the anytoneSettings entry. Display settings extension fields displayFrequency If true, the channel frequency is shown instead of the channel name. brightness Specifies the brightness of the backlight in a range 1-10. backlightDuration, backlightDurationRX, backlightDurationTX Specifies the backlight duration. While setting the TX backlight duration to 0 disables it during TX, setting the RX backlight duration to that value, enables it continously. customChannelBackground If true, the custom channel background is enabled. volumeChangePrompt, callEndPrompt If true the volume change and call-end prompts are shown. showClock, showCall, showContact, showChannelNumber, showColorCode, showTimeSlot, showChannelType, showLastHeard Enables/disables various display elements. lastCallerDisplay Specifies how the last caller is shown. Must be one of Off, ID, Call or Both callColor Specifies the call color. Must be one of White, Black, Orange, Red, Yellow, Green, Turquoise, Blue. Please note, that not all devices allow for all colors. Especially the AT-D868UV has only limited colors. standbyTextColor, standbyBackgroundColor Specifies the text or background color in stand-by. Must be one of White, Black, Orange, Red, Yellow, Green, Turquoise, Blue. Please note, that not all devices allow for all colors. Applicable to AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. channelNameColor, channelBNameColor Specifies the color of the channel name. Must be one of White, Black, Orange, Red, Yellow, Green, Turquoise, Blue. Please note, that not all devices allow for all colors. Applicable to AT-D878UV, AT-D878UV II and AT-D578UV. zoneNameColor, zoneBNameColor Specifies the color of the channel name. Must be one of White, Black, Orange, Red, Yellow, Green, Turquoise, Blue. Please note, that not all devices allow for all colors. Applicable to AT-D878UV, AT-D878UV II and AT-D578UV. language Specifies the UI language. Must be one of English or German. Applicable to AT-D878UV, AT-D878UV II, AT-D578UV and DMR-6X2UV. dateFormat Specifies the date format. Must be one of YearFirst (i.e., yyyy/mm/dd) or DayFirst (i.e., dd/mm/yyyy). Obviously, they got everything covered.
    Audio settings This entry collects all audio-related settings. These settings are collected under the audioSettings key within the anytoneSettings entry. Audio settings extension fields voxDelay An interval specifying the delay to activate the VOX. Stored in ms resolution. voxSource Specifies the source for the VOX. Must be one of Internal, External or Both. recording If true, recording is enabled. enhance If true, enabled some TX audio enhancement. I don't know what it does nor if it is reasonable to enable. muteDelay Specifies some muting delay in minutes. maxVolume, maxHeadPhoneVolume Specifies the maximum speaker and headphone volume. enableFMMicGain, fmMicGain Enables and sets a separate microphone gain for FM. If disabled, the global mic gain is used for both, DMR and FM.
    Menu settings This entry collects all menu-related settings (there aren't many). These settings are collected under the menuSettings key within the anytoneSettings entry. Menu settings extension fields duration Specifies the duration, the menu is shown. separator If true, the menu items are separated by a line. Again WTF AnyTone? Why the hack is an option needed for this? For me, this is just another useless option to check on every firmware update.
    Auto-Repeater settings The auto-repeater settings of anytone-devices is quiet complex. It allows to specify rules, in which frequency ranges some auto-repeater settings are applied referencing a particular offset for that rule. These offsets are specified within a separate list of frequency offsets. These settings are needlessly complicated. It would be sufficient to specify a range and offset. AnyTone, however, decided that this would be way to simple and user friendly. However, fitting their concept into qdmr's concept of references to objects, makes it even worse. You will see. The auto-repeater settings are specified within the autoRepeaterSettings item within the anytoneSettings item. Auto-repeater extension fields directionA, directionB Specifies the direction of the repeater offset for VFO A and B. I have absolutely no clue, why this depends on the VFO and not on the BAND (VHF/UHF) or even per offset. Only the wise people at AnyTone will know the answer. However, must be one of Off, Positive or Negative. vhfMin, vhfMax, uhfMin, uhfMax, vhf2Min, vhf2Max, uhf2Min, uhf2Max Specifies the VHF and UHF frequency range for which the auto-repeater feature is defined. These can be any frequencies by the way. The vhf2 and uhf2 variants are only present for AT-D878UV, AT-D878UV II and AT-D578UV radios and allow for a second pair of frequency ranges with a different offset. vhf, uhf, vhf2, uhf2 References the offset for the VHF and UHF frequency range specified above. This must be a reference to one of the auto-repeater offsets defined below. The vhf2 and uhf2 references are only present for AT-D878UV, AT-D878UV II and AT-D578UV radios and allow for a different offset. offset A list of auto-repeater offsets. As qdmr only allows references to named objects, they must be relatively complex. Each entry has an ID (to reference the offset), name (because qdmr objects have names) and the actual offset frequency. See below for a description of these elements.
    Auto-repeater offsets As mentioned above, qdmr requires any object referenced within the codeplug to be a proper object. That is with an ID and name. This makes sense, as almost all referenced elements within the codeplug are complex objects and have a display name. However, AnyTone decided to reference a mere offset frequency. Consequently, you have to give names to them in qdmr. Sorry. The below you find a list with the properties of the offset frequencies. Offset frequency fields id Specifies the unique ID of the offset frequency. Used to reference it. name Specifies the name of the offset frequency. Any string is valid and not needed. However, references are shown in qdmr with their name. So don't keep that field empty. offset Specifies the actual frequency offset.
    An example for two auto-repeater frequency ranges active only on VFO A.
    DMR related settings Some DMR related settings. Some of these should not be touched. groupCallHangTime, privateCallHangTime, manualGroupCallHangTime, manualPrivateCallHangTime Specifies the group and private call hang time. That is, the time, within you could answer a call by pressing the PTT, even if the group or private call is not the default transmit contact of the channel. preWaveDelay, wakeHeadPeriod Don't touch. Should both be 100ms. filterOwnID If true, your own ID is not shown in the call lists. smsFormat Specifies the SMS format. Must be one of Motorola, Hytera, DMR. monitorSlotMatch Specifies if and how the DMR monitor will match the time slot of the current channel. Must be one of Off, Single, Both. monitorColorCodeMatch If true, the DMR monitor will match the color code of the current channel. monitorIDMatch If true, the DMR monitor will match the DMR IDs to the group list of the current channel. monitorTimeSlotHold If true, the DMR monitor will hold the time slot first received on. sendTalkerAlias If enabled, sends the radio name as talker alias over the air. takerAliasSource Specifies the source for the talker alias display. Must be one of Off, UserDB or Air. talkerAliasEncoding Specifies the encoding of the taker alias. Must be one of ISO8, ISO7 or Unicode.
    GPS settings This section collects settings concerning GPS position reporting. These settings are collected under the gpsSettings key within the anytoneSettings entry. units Specifies the unit system to use for distance reporting (ranging). This must be one of Metric or Archaic. This software was developed by a European, so don't mail me to tell me, that I am silly. timeZone Specifies the time-zone for the device. Any IANA time-zone ID is valid here. E.g., Europe/Paris or UTC+5. reportPosition If true, GPS position reporting is enabled. updatePeriod Specifies the update/reporting interval in seconds. Any interval specification is valid here. E.g., 5 min or 360 s. mode Specifies the GPS mode. Must be one of GPS, Beidou, GPS_Beidou, Glonass, GPS_Glonass, Beidou_Glonass or All.
    Roaming settings This section collects settings concerning the roaming. These settings are collected under the roamingSettings key within the anytoneSettings entry. autoRoam If true, the auto-roaming is enabled. If false, the roaming must be started manually. autoRoamPeriod Specifies the auto-roaming period in minutes. Any interval specification is valid here. E.g., 30 min or 1 h. autoRoamDelay Specifies some additional delay in seconds before the auto-roaming is started. Any interval specification is valid here like 60 s or 1 min. roamStart,roamReturn Specifies the roaming start/end condition. Must be one of Periodic or OutOfRange. rangeCheck If true, the periodic range check is enabled. checkInterval Specifies the range check period in seconds. Any interval specification is valid here. E.g., 3 min or 180 s. retryCount Number of retries to connect to a repeater before giving up and starting the roaming. outOfRangeAlert Specifies the alert type for the out-of-range notification. Must be one of Off, Bell or Voice notification If true, the repeater check notification is enabled. notificationCount The number of repeater check notifications. defaultZone Specifies the default roaming zone. Must be a roaming zone ID or null. gpsRoaming If true, GPS based roaming is enabled.
    Bluetooth settings This section collects settings concerning bluetooth feature. These settings are collected under the bluetoothSettings key within the anytoneSettings entry. pttLatch If true, the bluetooth PTT button latches. pttSleepTimer Specifies the timeout for the sleep mode of the bluetooth PTT button.
    Simplex repeater settings Collects all settings related to the DMR-6X2UV simplex-repeater feature. These settings are collected under the simplexRepeaterSettings key within the anytoneSettings entry. This extension is only relevant for the DMR-6X2UV series. enabled If true, the simplex repeater function is enabled. monitor If true, the traffic can be monitored on the speaker. timeSlot Specifies the time-slot of the repeater. This must be one of TS1, TS2 or Channel. The latter implies, that the repeater uses the time-slot of the channel.
    qdmr-0.12.3/doc/manual/codeplug/anytone/zone.xml000066400000000000000000000023461501654372000215460ustar00rootroot00000000000000
    Zone extension This extensions allows to specify some zone attributes for AnyTone devices. The AnyTone zone extension is a mapping named anytone. It contains the device specific settings for that zone.
    Zone attributes For now, there is only one attribute allowing to hide the zone from the zone list in the radio. Zone extension fields hidden If true, the zone is hidden in the zone list on the radio.
    qdmr-0.12.3/doc/manual/codeplug/aprs.xml000066400000000000000000000124311501654372000200570ustar00rootroot00000000000000
    Positioning Systems Some radios allow to send the current position using DMR or analog APRS. Consequently, there are two types of positioning systems dmr and aprs. As an example, consider these two position reporting systems: The first specifies a digital (DMR) positioning system while the latter defines a APRS system.
    Common attributes The following attributes apply to both, analog APRS and digital DMR position reporting systems. Common fields id Specifies the ID of the system. This can be used reference this system in . Any unique string is valid here. name Specifies the name of the position reporting system. Any string is valid here. period Specifies the update period in seconds. If omitted or set to 0, the system will not send any updates periodically.
    DMR position reporting system attributes The following attributes apply only to digital (DMR) position reporting systems. DMR position reporting system fields contact Specifies the digital contact, the GPS information is sent to. This must be a reference to a digital contact. revert Specifies the revert channel. That is, the channel the data is sent on. If set, it must be a reference to a digital channel. If omitted or set to !selected, the currently active channel will be used to send the GPS information.
    APRS attributes The following attributes apply only for APRS position reporting systems. Analog position reporting system (APRS) fields revert Specifies the revert channel. That is, the channel the APRS information is sent on. This must be a reference to an analog channel. Usually a dedicated analog APRS channel is defined and referenced here. If omitted or set to !selected, the currently active channel will be used to send the position information. This, however, is not supported by all radios. Usually, an explicit APRS revert channel must be specified. destination Specifies the destination call and SSID, the information is sent to. This must be a string in the form CALL-SSID. Please note that the call must match the format defined by the AX.25 protocol. That is, it may only contain letters and numbers and cannot exceed the length of 6. source Specifies the source call and SSID. See destination for details. path Specifies the optional packet path. If set, this must be a list of CALL-SSID strings. icon Specifies the icon name to use. The icon name will be fuzzy matched. See for a complete list of icon names. message Specifies an optional message sent along with the position information.
    qdmr-0.12.3/doc/manual/codeplug/channels.xml000066400000000000000000000240211501654372000207030ustar00rootroot00000000000000
    Channels The channels list contains all channels defined within the codeplug. Usually, DMR radios support at least two different channel types. Digital channels (DMR) and analog channels (FM). To distinguish these two types, each entry of the channel list contains a map with a single entry. The key specifies the type (either digital or analog) while the value contains the actual channel definition. As an example consider the following two channel definitions: Channel definition The first channel is a digital (DMR) channel. Its ID is ch5 and its name BB DB0LDS TS2. The second channel is an analog channel (FM) with ID ch76 and name DB0LDS. Please note, that there are some common attributes like rxFrequency, txFrequency, power, timeout, rxOnly, vox and scanList but also type-specific settings like timeSlot or rxTone which apply only to digital and analog channels respectively.
    Common attributes The following attributes are common for all channel types. Common channel fields id Specifies the ID of the channel, this can be used later to reference this channel in and . Any unique string is valid here. name Specifies the name of the channel. Any string is valid here. rxFrequency, txFrequency Specifies the RX or TX frequency in MHz. Any floating point number is valid here. power Specifies the transmit power of the channel. Must be one of Min, Low, Mid, High or Max. timeout Specifies the transmit timeout in seconds. Any integer is valid here. Omitting this field or setting it to 0 will disable the timeout. Setting it to !default, the global transmit timeout will be used. See . rxOnly If set to true, the channel can only receive. Omitting it or setting it to false will allow transmission on the channel. vox Specifies the VOX sensitivity for this channel. If set to !default, the global VOX sensitivity will be used. See . Omitting this value or setting it to 0, will disable the VOX for this channel. scanList Specifies the optional scan list for this channel. If set, this must be a reference to a .
    Digital channel attributes The following attributes apply only to digital (DMR) channels. Digital channel fields admit Specifies the admit criterion for the channel. This must be one of Always, Free or ColorCode. colorCode Specifies the color code of the channel, any number between 0-16 is valid here. timeSlot Specifies the time slot of the channel. Must be one of TS1 or TS2. groupList Specifies the RX group list for this channel. This must be a reference to a group list. See above. contact Specifies the default transmit contact. This must be a reference to a digital contact. See above. If omitted, no default transmit contact is associated with the channel. Some radios require all channels to have a transmit contact. aprs Specifies the positioning system for this channel. If set, this must be a reference to a positioning system (DMR or APRS). See below. roaming Specifies the roaming zone for this channel. If set it must be a reference to a roaming zone. See below. To use the default roaming zone here, consider using the !default tag instead of a direct reference to a specific zone. If omitted, no roaming zone (also not the default one) is associated with the channel. radioID Specifies the radio ID for this channel. If set, it must be a reference to one of the radio DMR IDs. See . To use the default radio ID here, consider using the !default tag instead of a direct reference to a specific ID. If omitted the default radio ID is assumed.
    Analog channel attributes The following attributes apply only to analog (FM) channels. Analog channel fields admit Specifies the admit criterion for the channel. Must be one of Always, Free or Tone. squelch Specifies the squelch level for the channel. Must be an integer in the range of [1-10]. If set to 0, the squelch gets disabled. If set to !default, the global squelch setting is used. See above. bandwidth Specifies the bandwidth of the channel. Must either be Narrow or Wide for 12.5kHz or 25kHz respectively. rxTone Specifies the receive sub-tone setting for this channel. That is, the squelch will only open when a certain subtone is received along with the signal. As there are two common subtone standards, this attribute is a map with a single entry. The key specifies the type (either ctcss or dcs) while the value specifies the actual subtone. For CTCSS tones, the value is the subtone frequency in Hz. For DCS it is the code number as an integer. For inverted DCS codes, use negative numbers. txTone Specifies the transmit sub-tone setting for this channel. For details see rxTone above. aprs Specifies the APRS positioning system of this channel. If set, it must be a reference to an analog APRS system.
    qdmr-0.12.3/doc/manual/codeplug/commercial/000077500000000000000000000000001501654372000205025ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/codeplug/commercial/channel.xml000066400000000000000000000026151501654372000226400ustar00rootroot00000000000000
    Channel extension This extension allows to specify some channel settings specific for commercial applications of DMR. For now, it only allow for specifying the encryption key for a digital channel. The commercial channel extension is a mapping named commercial. It contains the settings for commercial features for that channel.
    Channel attributes For now, there is only one attribute specifying the encryption key associated with the channel. Channel extension fields encryptionKey References a encryption key defined within the commercial codeplug extension. See .
    qdmr-0.12.3/doc/manual/codeplug/commercial/encryption.xml000066400000000000000000000071461501654372000234260ustar00rootroot00000000000000
    Encryption extension This extension allows to configure the commercial encryption features of DMR. Almost all DMR radios implement means for encrypting the traffic. This feature however, is usually illegal when used within amateur radio. This extension is a simple list of keys held in the global commercial extension. Each key must either be a DMR (basic), RC4 (enhanced) or AES (advanced) key. To differentiate these key types, each list entry must be a mapping with a single entry. The name specifies the type (i.e., either dmr, rc4 or aes). The value then specifies the properties of the key.
    Common key attributes As only the key size differs between the different key types, there are common attributes. Specifically, the ID and name of the key. Common key fields id Specifies the ID of the key. This ID can then be used to reference the key within the commercial digital channel extension. See . name Specifies the name of the key. This property is usually not encoded in the binary codeplug.
    DMR (basic) key attributes DMR key fields key Specifies the key as a HEX string. It must be at least 8bit but can be of variable size. The actual size depends on the device. Usually, a fixed size of 16 or 32bit is supported. Some devices, however, support variable sized keys.
    RC4 (enhanced) key attributes RC4 key fields key Specifies the key as a HEX string. This key is fixed to a size of 40bit. That is, the hex string must be of length 10.
    AES (advanced) key attributes AES key fields key Specifies the key as a HEX string. Also this key can be of variable size. Usually, these keys are 128 or 256bit. The actual size depends on the device.
    qdmr-0.12.3/doc/manual/codeplug/commercial/extensions.xml000066400000000000000000000031761501654372000234320ustar00rootroot00000000000000 Commercial Codeplug Extensions This section collects all extensions that configure commercial features present in all radios. DMR was developed as a digital replacement of the analog trunked radio systems used in commercial context. To this end, there are many features of that are irrelevant or outright forbidden in an amateur radio context. However, some HAM operators use their hand-held radios also for their day job and thus may need to configure some features that are only applicable in a commercial context. To this end, qdmr allows to set some of the commercial features through extensions. Some commercial features defined within the DMR standard are illegal to use in an amateur radio context. Check your local regulations! Example commercial extension defining an encryption key. All commercial extensions are held under the commercial key within the codeplug. For now, there is only one extension, the encryption extension listing the encryption keys described below. qdmr-0.12.3/doc/manual/codeplug/contacts.xml000066400000000000000000000102311501654372000207240ustar00rootroot00000000000000
    Contacts The contacts element specifies a list of all contacts. Each contact is a map that contains a single entry to specify the contact type. The key specifies the type name and the value specifies the actual contact definition. Currently there are two possible contact types. As an example consider this contact list, containing 4 contact definitions. One for each type. Contact definitions
    DMR Contacts (<token>dmr</token>) A DMR contact is a simple object and is usually defined in one line. Each contact contains an optional id that will be used to reference this contact throughout the codeplug (e.g., in channels, group lists, etc.). DMR Contact Fields id Specifies the identifier of the contact. This identifier can later be used to reference the contact. Any unique string is valid. name Specifies the name of the contact. Any string is valid. type Specifies the type of the contact. Must be one of PrivateCall, GroupCall or AllCall. number Specifies the DMR ID for this contact. That is, any integer between 0 and 16777215. This element is mandatory for all types except for the all-call. For the all-call, the default number 16777215 will be used. ring If true, the radio will ring whenever a call from this contact is received (if supported by the radio). Optional, if omitted set to false.
    Analog DMTF Contacts (<token>dtmf</token>) An analog DTMF contact can be used to store commonly used DTMF sequences. For example, it may be used to control the EchoLink feature of a repeater. id Specifies the identifier of the contact. This identifier can later be used to reference the contact. Any unique string is valid. name Specifies the name of the contact. Any string is valid. number Specifies the DTMF ID for this contact. That is any combination of numbers 0-9 and symbols A-D, *, #. used. ring If true, the radio will ring whenever a call from this contact is received (if supported by the radio). Optional, if omitted set to false.
    qdmr-0.12.3/doc/manual/codeplug/extensions.xml000066400000000000000000000021011501654372000213020ustar00rootroot00000000000000 Device specific extensions The sole reason for introducing a new YAML based codeplug format was the ability to extend the file format with device specific settings without breaking the format. This was simply impossible with the old table based text files. The present YAML based codeplug file format is extensible at almost any level. That is, device specific elements can be added to single codeplug elements like channels, zones or contacts but also to the codeplug itself. The latter allows to extend the codeplug with new elements. qdmr-0.12.3/doc/manual/codeplug/format.xml000066400000000000000000000036401501654372000204040ustar00rootroot00000000000000 Extensible Codeplug File Format This document describes the extensible codeplug file format using YAML. If you are unfamiliar with YAML, consider reading a YAML introduction first. The documentation for the old table based conf-file format, can be found in section . The introduction of device specific settings (with version 0.9.0) required an extensible codeplug file format. The old table based format did not allow for any extension without braking backward compatibility. The new YAML based format allows for exactly that: Some means to extend the format for device specific settings without breaking the format while maintaining some degree of readability. (Yes, some users use the command line tool and edit their codeplug in a text editor.) There are several levels, at which device specific extensions may appear within the codeplug. There are global extensions that apply to the entire codeplug. These extensions are located at the top level. There might also be extensions to single channels, contacts, zones, etc. These extensions are then located under the specific element that gets extended. qdmr-0.12.3/doc/manual/codeplug/grouplists.xml000066400000000000000000000035571501654372000213360ustar00rootroot00000000000000
    Group lists A group list collects several digital (DMR) contacts that should be received on a channel associated with this group list. Consequently, a group list consists of a name and a list of contact IDs. The groupLists element is then a list of several group list definitions. As an example, consider the two group lists below: Group list definition The first group list has the internal ID grp1. This ID can then be used later to reference this group list. The name is set to DL. The list of contacts of the group list is then defined as a list containing the IDs of the referenced contacts. Group list fields id Specifies the ID of the group list. This ID can then be used to refer to the group list within digital channels. Any unique string is possible here. name Specifies the name of the group list. Any string is possible here. contacts A list of digital contact IDs. See above.
    qdmr-0.12.3/doc/manual/codeplug/opengd77/000077500000000000000000000000001501654372000200215ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/codeplug/opengd77/channel.xml000066400000000000000000000057731501654372000221670ustar00rootroot00000000000000
    Channel extension This extension allows to specify some channel settings specific for devices running the OpenGD77 firmware. This extension can be added to any channel, analog and digital. For now, this extension only allows to specify the power for the channel in more detail. The OpenGD77 channel extension is a mapping named openGD77. It contains the device specific settings for that channel.
    Channel attributes For now, there are only few attributes specifying scan behavior for the channel. Channel extension fields scanZoneSkip scanAllSkip In contrast to the original GD-77 firmware, OpenGD77 does not implement scanning by scan lists. Instead, either all channels defined in the radio or all channels within the current zone can be scanned. To exclude some channels from these scans, the scanZoneSkip and scanAllSkip flags can be used. When enabled, the channel will be excluded from the respective scan. beep If enabled (default), the channel beeps are played. powerSave Enables the power save featrue for this channel (default). location Allows to specify a fixed location to be used for this channel and the associated APRS system. talkerAliasTS1 talkerAliasTS2 Specifies what to be send as the talker alias, depending on the current timeslot. This must be one of None, APRS, Text or Both.
    qdmr-0.12.3/doc/manual/codeplug/opengd77/contact.xml000066400000000000000000000033131501654372000221760ustar00rootroot00000000000000
    DMR contact extension This extensions allows to specify some DMR contact attributes for devices running the OpenGD77 firmware. This extension is only applicable to DMR (digital) contacts. The OpenGD77 contact extension is a mapping named openGD77. It contains the device specific settings for that DMR contact.
    Contact attributes For now, there is only one attribute allowing to override the time slot of a channel whenever this contact is selected as the destination contact for that channel. Channel extension fields timeSlotOverride OpenGD77 allows to override the time slot settings for each channel on the bases of the selected transmit contact. If the contact has a time slot override set, this time slot is used instead of the channel time slot. This attribute is either None, TS1 or TS2. If None is set, the channel time slot will not be overridden.
    qdmr-0.12.3/doc/manual/codeplug/opengd77/extensions.xml000066400000000000000000000007441501654372000227470ustar00rootroot00000000000000
    OpenGD77 Codeplug Extensions This chapter documents the extensions and settings specific to radios running the OpenGD77 firmware.
    qdmr-0.12.3/doc/manual/codeplug/radioddity/000077500000000000000000000000001501654372000205235ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/codeplug/radioddity/extensions.xml000066400000000000000000000013711501654372000234460ustar00rootroot00000000000000
    <trademark>Radioddity</trademark> Codeplug Extensions This section collects all device specific extensions to the codeplug for Radioddity and some Baofeng devices. These extensions are applicable to Radioddity GD77, GD73 and Baofeng/Radioddity RD-5R radios.
    qdmr-0.12.3/doc/manual/codeplug/radioddity/generalradiosettings.xml000066400000000000000000000154231501654372000254670ustar00rootroot00000000000000
    General radio settings This section describes the top-level generic radio settings for Radioddity devices. Radio settings extension example showing default values. Radio settings fields monitorType Specifies the monitor type. Must be either Open or Silent. Default Open. loneWorkerResponseTime When lone worker is enabled, specifies the time in minutes before the radio will start to remind the user. Default 1min. See also lone worker. loneWorkerReminderPeriod Specifies the period in seconds for the lone worker reminder. Default 10s. See also lone worker. groupCallHangTime Specifies the group-call hang time in milliseconds. This is the time-period the user can answer a received group call by pressing PTT. After this time has passed, a press on the PTT button will call the default contact on the selected channel. Default 3000ms. See also hang time. privateCallHangTime Specifies the private-call hang time in milliseconds. This is the time-period the user can answer a received private call by pressing PTT. After this time has passed, a press on the PTT button will call the default contact on the selected channel. Default 3000ms. See also hang time. downChannelModeVFO If true, the channel-up button will tune the VFO. If false, it will step through the channels. Default false. upChannelModeVFO If true, the channel-donw button will tune the VFO. If false, it will step through the channels. Default false. powerSaveMode Puts the radio is a sleep mode when in idle state (no traffic on the channels). This allows for some power saving. However, the radio will need some time to wake up. Consequently, all other radios in the network need to transmit a wake-up preamble. Default true. wakeupPreamble Enables the transmission of a short wake-up preamble allowing receiving radios to wake-up in time for the actual transmission. Default true. preambleDuration This sets the preamble duration in milliseconds. Default 360ms. powerSaveDelay Specifies the delay, before an idle radio enters power save mode. (GD-73 only) allLEDsDisabled If true, all LEDs are disabled. Default false. quickKeyOverrideInhibited If true, allows the user to transmit on a busy channel irrespective of the channels admit criterion by double pressing the PTT. Default false. txInterrupt WTF?!? txOnActiveChannel If true, the radio will transmit on the currently active channel (if double-wait) is enabled. Default false. scanMode Specifies the scan mode. Must be one of Time, Carrier or Search. Default Time. repeaterEndDelay Specifies the delay after the end of a repeater transmission in seconds. Default 0s, off. repeaterSTE Specifies the repeater STE (what ever that means) in seconds. Default 0s, off. language Specifies the UI language. Must be one of Chinese or English.
    qdmr-0.12.3/doc/manual/codeplug/radioddity/radiobootsettings.xml000066400000000000000000000025341501654372000250140ustar00rootroot00000000000000
    Boot settings This section collects some settings related to booting the radio. Boot settings extension example showing default values. Boot settings fields display Specifies what to display during boot. Must be one of None, Text or Image. bootPassword, progPassword Specifies the boot and programming passwords. The former (usually only numbers) must be entered, when the radio boots. The latter must be entered in the CPS to program or read the codeplug.
    qdmr-0.12.3/doc/manual/codeplug/radioddity/radiobuttonsettings.xml000066400000000000000000000152671501654372000253730ustar00rootroot00000000000000
    Button settings This section describes how the buttons are configured for Radioddity devices. Button settings extension example showing default values. Button settings fields longPressDuration Sets the duration, a button must be pressed, to be considered as a long press. This interval is usually expressed in ms. E.g., 1000ms. funcKey1Short, funcKey1Long Short and long-press functions for the programmable function key 1. This is the side key 1 on the GD77 and RD-5R and the P1 button on the GD73. funcKey2Short, funcKey2Long Short and long-press functions for the programmable function key 2. This is the side key 2 on the GD77 and RD5R and the P2 button on the GD73. funcKey3Short, funcKey3Long Short and long-press functions for the programmable function key 3. This is the top key on the GD77 and RD5R This button is not present on the GD73. Button functions None Disables the button. No function is associated with it. ToggleAllAlertTones Enables or disables all alert tones. Only present in GD77 and RD5R radios. EmergencyOn, EmergencyOff Why not toggle? Either enables or disables an emergency. ToggleMonitor Enables/toggles the monitor. This is device specific, on some radios the monitor function latches, on most not. Then, the monitor is enabled, as long as the button is pressed. OneTouch1, OneTouch2, OneTouch3, OneTouch4, OneTouch5, OneTouch6 Triggers one specific one-touch action. Not all radios have 6 of these. The GD-77 and RD-5R have 6 one-touch actions, while the GD-73 has only 5. ToggleTalkaround Enables/disables the talkaround feature for repeater channels. The radio then also transmits on the RX frequency. Consequently bypassing the repeater. ToggleScan Enables/disables the scan. ToggleEncryption Enables/disables the encryption for the channel, if configured. ToggleVox Enables/disables the VOX for the channel, if configured. ZoneSelect Brings up the zone selection dialog. BatteryIndicator Shows the battery charge indicator. ToggleLoneWorker Enables/disables lone-worker feature, if configured. PhoneExit WTF?!? ToggleFlashLight Enables/Disables the flash light. Not all devices have one. ToggleFMRadio Enables/disables the FM broadcast radio. RadioCheck,RadioDisable, RadioEnable If configured, the radio will transmit tones, that cause other radios --- if configured to do so --- to either response, disable or reenable themselves. This allows to control other radios remotely. TBST Sends the TBST tone (usually 1750Hz). Some radios have a fixed button combo for that. CallSwell WTF?!?
    qdmr-0.12.3/doc/manual/codeplug/radioddity/radiosettings.xml000066400000000000000000000013301501654372000241210ustar00rootroot00000000000000
    Radio settings extension This extension allows to set the device-specific general settings for Radioddity radios. It extends the settings section of the codeplug and is split into several sub-extensions.
    qdmr-0.12.3/doc/manual/codeplug/radioddity/radiotonesettings.xml000066400000000000000000000044201501654372000250120ustar00rootroot00000000000000
    Tone settings This section collects some settings relates to tones and other audio stuff. Tone settings extension example showing default values. Tone settings fields lowBatteryWarn Enables the low battery-charge warning. This can either be a notification on the screen or a waring tone. The warning interval and tone-volume might be set by lowBatteryInterval and lowBatteryWarnVolume. lowBatteryWarnInterval Specifies the interval, at which low battery warning are issued. lowBatteryWarnVolume Specifies the volume of the low-battery warning tone in a range from 1 to 10. keyTone, keyToneVolume If true, the key-pad tones are enabled. Don't do it. The volume of these tones might be set using keyToneVolume in a range from 1 to 10.
    qdmr-0.12.3/doc/manual/codeplug/radioids.xml000066400000000000000000000037031501654372000207120ustar00rootroot00000000000000
    Radio IDs The radioIDs-element specifies a list of radio IDs. Each radio ID is a map that contains a single entry to specify the type. The key of the entry specifies the type name and the value specifies the actual radio ID definition. Currently only the type dmr is supported. As an example consider this radio ID list, containing only a single ID. Radio ID definitions This DMR radio ID got the identifier id1, name DM3MAT and number 2621370.
    DMR Radio IDs (<token>dmr</token>) The DMR radio ID definition consist of an optional id (necessary to reference that ID later), a name and the DMR ID number. DMR Radio ID Fields id Specifies the identifier of the radio ID. This identifier can later be used to associate the radio ID to channels. Any unique string is valid. name Specifies the name of the radio ID. This name may also be used as the radio name. Any non-empty string is valid. number Specifies the DMR ID for this radio ID. That is any integer between 0 and 16777215.
    qdmr-0.12.3/doc/manual/codeplug/radiosettings.xml000066400000000000000000000072121501654372000217720ustar00rootroot00000000000000
    Radio settings The radio settings section contains all radio-wide settings. For example the microphone level, boot text etc. For now, there aren't many. As an example, consider the following general settings General radio-wide settings. Here, the microphone amplification is set to 6, the speech synthesis is disabled, the two boot text lines are set to qDMR and DM3MAT respectively and the default DMR radio ID is set to id1. The latter is the id of a radio ID defined below. Also, the radio-wide default squelch, VOX and power level is set. These values can be referenced later in channels. Also, some radios do not allow for these settings to be applied on a per-channel basis. For these radios, these values are used. Radio-wide Setting Fields micLevel Specifies the microphone amplification. Must be an integer between 1 and 10. speech Enables/disables the speech synthesis. Some radios can announce the current channel etc. for the visually impaired. To enable that feature (if supported by the radio) set this field to true. Must be a boolean value. introLine1 introLine2 Sets the two boot text lines (if supported by the radio). These text lines will show up on the boot of the radio. If the radio is set to show a picture during boot, these lines are not shown. defaultID Specifies which radio ID will be used as the default DMR ID (see below). If none is specified, the first defined DMR radio ID will be used. squelch Specifies the default squelch level. This value may be referenced in channels or represent the radio-wide squelch setting. Any value in [0-10] is valid, where 0 implies an open squelch (if supported by the radio). vox Specifies the default VOX sensitivity. This value may be referenced in channels or represent the radio-wide VOX sensitivity. Any value in [0-10] is valid here, where 0 disables the VOX. power Specifies the default transmit power. This value may be referenced in channels or represent the radio-wide power setting. Possible values are Min, Low, Mid, High and Max.
    qdmr-0.12.3/doc/manual/codeplug/roaming.xml000066400000000000000000000106071501654372000205510ustar00rootroot00000000000000
    Roaming The roaming configuration consists of a list of roaming channels and roaming zones. While roaming channels specify those channel settings, that might be overridden during roaming, the roaming zone specifies a collection of roaming channels, that can be used to maintain a connection to a particular talk group.
    Roaming Channels Roaming channels contain those settings of a DMR channel, that are specific for a particular repeater. This allows to override these settings in a channel, when the repeater of that channel gets out of range. Then, the radio may switch to a different repeater to maintain the connection to the current talk group. The example above shows the definition of two roaming channels. Both are used to stay in contact with a regional talk group within the Brandmeister network. While the repeater DB0LDS is located within this region, DB0AFZ is not. Consequently, the time-slot of the channel needs to be overridden. Roaming channel fields id Specifies the ID of the roaming channel. This ID can then be used to refer to this channel within a roaming zone. Any unique string is valid here. name Specifies the name of the roaming channel. Any string is valid here. rxFrequency, txFrequency Specifies the receive and transmit frequencies of the channel. colorCode Specifies the color code of the channel. If set, overrides the color code of the current channel. If omitted, the color code of the selected channel is used during roaming. timeSlot Specifies the time slot of the channel. If set, overrides the time slot of the current DMR channel. If omitted, the time slot of the selected channel is used during roaming.
    Roaming Zones Roaming zones are collections of roaming channels, that are scanned for the strongest signal to maintain connection to a particular network or talk group. As an example, consider the following roaming zone: This zone has the ID roam1 and the name Berlin/Brand. This group collects all roaming channels that provide access to the Berlin/Brandenburg talk group. Roaming zone fields id Specifies the ID of the roaming zone. This ID can then be used to refer to this zone. Any unique string is valid here. name Specifies the name of the roaming zone. Any string is valid here. channels Specifies the member channels for this roaming zone. This must be a list of references to roaming channels.
    qdmr-0.12.3/doc/manual/codeplug/scanlists.xml000066400000000000000000000055221501654372000211200ustar00rootroot00000000000000
    Scan lists Scan lists are simple lists of channels to scan. A scan list might be associated with a channel. As an example, consider the following scan list: This scan list has the ID scan1, the name KW and contains several channels (both analog and digital). Scan list fields id Specifies the ID of the scan list. This ID can then be used to reference this scan list in . name Specifies the name of the scan list. Any string is valid here. primary Specifies the primary priority channel. Usually this channel is scanned very frequently. If set, this must be a reference to a channel. If the tag !selected is used here, the channel from which the scan got started is used as the primary priority channel. secondary Specifies the secondary priority channel. Usually this channel is scanned frequently. If set, this must be a reference to a channel. If the tag !selected is used here, the channel from which the scan got started is used as the secondary priority channel. revert Specifies the revert channel. That is, the channel to transmit on irrespective of the current channel being scanned. If set, this must be a reference to a channel. If the tag !selected is used here, the channel from which the scan got started is used as the transmit channel. If omitted the radio will transmit on the currently scanned channel. channels Specifies the list of channels to scan. Must be a list of channel IDs.
    qdmr-0.12.3/doc/manual/codeplug/smsextension.xml000066400000000000000000000052151501654372000216530ustar00rootroot00000000000000
    SMS Extension This extension allows for configuring the SMS (text message) feature of all DMR radios. For now, it is implemented as an extension, but might be changed into a common core setting in the future. This extension only has two elements: the SMS format and a list of predefined text messages. The latter is important for devices that do not have a keypad and thus do not allow for entering any messages. E.g., the Radioddity GD-73. As this extension configures a common feature, the extension is always present. Example of a single SMS pre-defined text or template.
    Common SMS settings format Specifies the SMS format to be used. This must be one of Motorola, Hytera or DMR. If an incompatible format is chosen, you cannot receive or send text messages.
    Message templates The templates key defines a list of SMS templates to be programmed onto the radio. Each template is a config object with a name and ID. The latter can be used to reference the template somewhere else in the codeplug. Each template has the following properties: id Specifies the unique ID of the message. This can be used to reference the message. name Specifies the display name of the message. Please note, that not all radios store descriptive names alongside with the preset messages. message The actual preset message. The length might differ but is usually limited to 144 chars.
    qdmr-0.12.3/doc/manual/codeplug/tyt/000077500000000000000000000000001501654372000172075ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/codeplug/tyt/buttonsettings.xml000066400000000000000000000054611501654372000230330ustar00rootroot00000000000000
    Button settings extension This extension to the codeplug allows to specify the function for each of the programmable buttons on the radio. Please note that not all TyT radios have all buttons described here. Button settings fields sideButton1Short sideButton1Long sideButton2Short sideButton2Long sideButton3Short (Retevis RT84, Baofeng DM-1701) sideButton3Long (Retevis RT84, Baofeng DM-1701) progButton1Short (Retevis RT84, Baofeng DM-1701) progButton1Long (Retevis RT84, Baofeng DM-1701) progButton2Short (Retevis RT84, Baofeng DM-1701) progButton2Long (Retevis RT84, Baofeng DM-1701) Specifies the different functions for each button press. Must be one of: Disabled, ToggleAllAlertTones, EmergencyOn, EmergencyOff, PowerSelect, MonitorToggle, OneTouch1-OneTouch6, RepeaterTalkaroundToggle, ScanToggle, SquelchToggle, PrivacyToggle, VoxToggle, ZoneIncrement, BatteryIndicator, LoneWorkerToggle, RecordToggle, RecordPlayback, RecordDeleteAll, Tone1750Hz, SwitchUpDown, RightKey, LeftKey or ZoneDecrement. longPressDuration Specifies the long-press duration in milliseconds.
    qdmr-0.12.3/doc/manual/codeplug/tyt/channel.xml000066400000000000000000000135321501654372000213450ustar00rootroot00000000000000
    Channel extension This extension to the codeplug allows to set device specific channel settings for many TyT radios (and therefore also many Retevis radios). Not all settings are present in all radios. Unsupported settings are ignored during encoding or set to the default value during decoding. Example channel settings specifying the default values for <trademark>TyT</trademark> <trademark>Retevis</trademark> devices. Common channel setting fields loneWorker If true, the lone worker feature is enabled. See also Lone Worker autoScan If true, the auto-scan feature is enabled. talkaround If true, talk around is enabled. That is, the radio will receive on the input and transmit on the output frequency of the repeater effectively bypassing the repeater. See also Talkaround. dataCallConfirmed privateCallConfirmed emergencyAlarmConfirmed Enables the confirmation of data, private and emergency calls. These fields are usually disabled as the radio will first establish the connection before the actual call can be started. displayPTTId If true, the received analog PTT will be shown. rxRefFrequency rxRefFrequency Specifies some weird reference frequency setting for RX and TX. By default the value Low is used. Possible values are Low, Medium and High. Channel settings for <trademark>TyT</trademark> <productname>MD-390</productname> and <trademark>Retevis</trademark> <productname>RT8</productname> tightSquelch If set to true, the silent squelch is used. compressedUDPHeader Some unknown flag. Usually disabled. reverseBurst If enabled (default), avoids a noise burst at the end of a transmission, when the squelch is controlled by a sub tone. Channel settings for <trademark>TyT</trademark> <productname>MD-UV390</productname> and <trademark>Retevis</trademark> <productname>DM-2017</productname> killTone Specifies the kill tone. Possible values are Off, Tone259_2Hz and Tone55_2Hz. Disabling or setting the kill tone to 259.2Hz or 55.2Hz, respectively. inCallCriterion Specifies the in-call criterion. Possible values are Always, AdmitCriterion and TXInterrupt. allowInterrupt Enables/disables interrupts if inCallCriterion is set to TXInterrupt. dcdm If enabled, the dual-capacity direct mode is used for simplex operation. See also DCDM. dcdmLeader If DCDM is enabled, this flag specifies whether this radio is the channel leader. That is, if this radio provides the clock for the time-slots. dmrSquelch Allows to set the squelch level for DMR channels. This prevents the LED to light all the time. The radio, however, remains silent irrespective of this setting.
    qdmr-0.12.3/doc/manual/codeplug/tyt/extensions.xml000066400000000000000000000022241501654372000221300ustar00rootroot00000000000000
    <trademark>TyT</trademark> Codeplug Extensions This section collects all device specific extensions to the codeplug for TyT, Retevis and some Baofeng devices. These extensions are applicable to TyT MD-390, MD-UV380, MD-UV390, MD-2017, Retevis RT8, RT3S, RT82, RT84 and Baofeng DM-1701 radios.
    qdmr-0.12.3/doc/manual/codeplug/tyt/menusettings.xml000066400000000000000000000135421501654372000224630ustar00rootroot00000000000000
    Menu settings extension This extension to the codeplug allows to specify which menu items are enabled. This feature is frequently used in commercial applications to restrict the user to change certain settings in the radio. Menu settings fields hangtimeIsInfinite hangTime Specify the menu hang time. That is the duration, the menu is shown without any user action. If hangtimeIsInfinite is true, the menu is shown indefinitely, irrespective of the hangTime setting. If hangtimeIsInfinite is false, the hangTime specifies the menu hang time in seconds. textMessage Enables the text-messaging menu item. callAlert Enables the call alert menu item. contactEditing Enables the contact editing menu item. manualDial Enables manual dial. remoteRadioCheck Enables the remote radio-check menu item. remoteMonitor Enables the remote monitor menu item. remoteRadioEnable remoteRadioDisable Enables the remote radio enable and disable menu items. scan scanListEditing Enables the scan and scan list editing menu items. callLogMissed callLogAnswered callLogOutgoing Enables the menu items for the list of missed, answered and outgoing calls. talkaround Enables the talkaround menu item. alertTone Enables the alert tone settings menu item. power Enables the power settings menu item. backlight Enables the backlight settings menu item. bootScreen Enables the boot-screen settings menu item. keypadLock Enables the keypad lock settings menu item. ledIndicator Enables the LED indicator settings menu item. squelch Enables the squelch settings menu item. vox Enables the VOX settings menu item. password Enables the password settings menu item. displayMode Enables the display mode settings menu item. radioProgramming Enables radio programming from the keypad. E.g., editing channels etc. gpsInformation Enables the GPS information menu item. This setting has only an effect on radios supporting GPS.
    qdmr-0.12.3/doc/manual/codeplug/tyt/radiosettings.xml000066400000000000000000000234611501654372000226160ustar00rootroot00000000000000
    Radio settings extension This extension allows to set the device-specific general settings for TyT and Retevis radios. It extends the settings section of the codeplug. Radio settings extension example showing default values. Radio settings fields montitorType Specifies the monitor type. Possible values are Open and Silent. This setting only affects analog channels. If Open is specified, the squelch will open if monitoring is enabled. allLEDsDisabled If enabled, all LEDs are disabled. talkPermitToneDigital If enabled, a talk-permit tone will sound on digital channels. talkPermitToneAnalog If enabled, a talk-permit tone will sound on analog channels. passwordAndLock Enables and disables the passwords and keypad locks globally. They can also be enabled/disabled individually below. channelFreeIndicationTone If enabled, a tone will sound after a transmission has ended to indicate that the channel is free again. allTonesDisabled If enabled, all tones (talk permit, channel free, etc.) are disabled globally. powerSaveMode Puts the radio is a sleep mode when in idle state (no traffic on the channels). This allows for some power saving. However, the radio will need some time to wake up. Consequently, all other radios in the network need to transmit a wake-up preamble. wakeupPreamble Enables the transmission of a short wake-up preamble allowing receiving radios to wake-up in time for the actual transmission. bootPicture If enabled, a picture is shown during boot rather than the boot text (see introLine1 and introLine2 in the general settings section). channelMode, channelModeA, channelModeB Controls the mode of the radio, VFO A and VFO B. If channelMode is true, the entire radio operates in channel mode (memory mode). This also overrides the channelModeA and channelModeB settings. If channelMode is set to false, the channelModeA and channelModeB specify the mode for the VFO A and B, respectively. txPreambleDuration Specifies the transmit preamble duration in milliseconds. If this preamble is the wake-up preamble, is unknown. channelHangTime Specifies the channel hang time in milliseconds. groupCallHangTime, privateCallHangTime Specifies the group-call hang time in milliseconds. This is the time a group or private call can be answered directly even if that group call is not the default contact of the channel. See also hang time. lowBatteryWarnInterval Specifies the period of the low-battery warn tone in seconds. callAlertToneContinuous If enabled, a call-alert tone will sound until the operator reacts. This setting overrides the callAlertToneDuration setting. callAlertToneDuration Specifies the call alert-tone duration in seconds. If callAlertToneContinuous is enabled, the alert tone will be continuous irrespective of this setting. loneWorkerResponseTime Sets the lone-worker response time in minutes. See also lone worker. loneWorkerReminderTime Sets the lone-worker reminder time in minutes. See also lone worker. digitalScanHangTime, analogScanHangTime Specifies the time in milliseconds, the radio will continue monitoring a DMR or FM channel after the transmission on that channel ended. backlightAlwaysOn If enabled, the backlight will stay on. backlightDuration Specifies the backlight duration in seconds. keypadLockManual If enabled, the keypad lock is enabled manually. If not, the keypad lock gets enabled automatically after a specified period (see keypadLockTime). keypadLockTime Specifies the time, after which the keypad lock is engaged automatically unless keypadLockManual is enabled. powerOnPasswordEnabled Enables the power-on password. powerOnPassword Specifies the power-on password. An 8-digit number. This password must then be entered when booting the radio. radioProgPasswordEnabled Enables the radio-programming password. radioProgPassword Sets the radio-programming password. An 8-digit number. This password must then be entered when making any changes to the radio/channel settings through the keypad. pcProgPassword Specifies the PC programming password. This password is then needed when programming the radio through the CPS. An empty string disables this password. privateCallMatch, groupCallMatch If true, the private and group call IDs must match.
    qdmr-0.12.3/doc/manual/codeplug/tyt/scanlist.xml000066400000000000000000000020111501654372000215430ustar00rootroot00000000000000
    Scan-list settings extension This extension to the codeplug allows to specify device specifics settings for scan-lists. Scan-list settings example showing the default values. Scan-list settings fields holdTime Specifies the hold time in ms. prioritySampleTime Specifies the sample-time in ms for priority channels.
    qdmr-0.12.3/doc/manual/codeplug/zones.xml000066400000000000000000000041271501654372000202530ustar00rootroot00000000000000
    Zones The zones element collects all zones defined within the codeplug. It is just a list of zone definitions. Each zone has an ID, name and one or two lists of channels. One for VFO A and one for VFO B. Depending on the radio, a zone will be split automatically into two zones if the radio handles separate zones for each VFO. As an example, consider the following zone: Zone definition This zone has the name KW and contains two lists of channels. One for each VFO. On radios, where each VFO is assigned a zone individually, this zone will be split into two: KW A and KW B to match the radios configuration. Zone fields id Specifies the ID of the zone. For now, there are no codeplug elements that refer to zones. name Specifies the name of the zone. Any string is valid here. A Specifies the channel list for VFO A. This must be a list of references to channels. B Optional channel list for VFO B. If present, must be a list of references to channels.
    qdmr-0.12.3/doc/manual/conf/000077500000000000000000000000001501654372000155125ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/conf/analogchannels.xml000066400000000000000000000111321501654372000212070ustar00rootroot00000000000000
    Analog channel table The analog channel table collects all analog (FM) channels. As digital channels have some different options compared to analog channels, they are not defined within the same table. However, they share the same IDs. So be careful not to assign the same identifier to analog and digital channels. The analog channel table has the form The analog channel table starts with the "Analog" keyword and ends with an empty line. The remaining keywords right after "Analog" (i.e., "Name", "Receive", "Transmit", "Power", "Scan", "TOT", "RO", "Admit", "Squelch", "RxTone", "TxTone" and "Width") are ignored but are part of the self-documentation of the config file. Each line within the table specifies a single channel. The first column specifies the unique ID of the channel. This ID can by any number that is unique among analog AND digital channels. The second (Name) column specifies the name of the channel as a string. Any string can be used here. The third (Receive) column specifies the RX frequency of the channel in MHz. The fourth (Transmit) column specifies the TX frequency in MHz or alternatively, an offset relative to the receive frequency in MHz by prefixing "+" or "-". The 5th (Power) column specifies the transmit power. This must be either the "High" or "Low" keyword. The 6th (Scan) column specifies the scan-list ID for this channel or "-" if there is no scan-list assigned to the channel. A scan-list (see below) is just a collection of channels that gets scanned whenever scanning is started on a particular channel. The 7th (TOT) column specifies the transmit time-out in seconds or "-" if disabled. The 8th (RO) column specifies whether this channel is receive-only with either "-" meaning disabled and "+" enabled. If enabled, it is impossible to transmit on that channel. The 9th column specifies the admit criterion on that channel. This must be either "-" meaning that there is no restriction when to send on that channel, the keyword "Free" meaning that the channel must be free to transmit or the keyword "Tone" meaning that the channel must be free and the RxTone must match. The 10th (Squelch) column specifies the squelch level for the channel. This must be a number between [0-10]. The larger the value, the stronger the signal must be to open the squelch. The value 0 disables the squelch. The 11th (RxTone) specifies the receive CTCSS tone frequency in Hz. The squelch will then only open, if the signal is strong enough (see previous column) and the specified tone is received. If set "-" the RX tone is disabled and the squelch will open if the signal is strong enough. The 12th (TxTone) column specifies the CTCSS tone to transmit in Hz or "-" if disabled. This feature is used by some repeaters to open their squelch and to start repeating to avoid conflicts between repeaters operating on the same frequency (e.g., in case of DX conditions). The 13th (Width) column specifies the bandwidth of the channel in kHz. This can be 12.5kHz narrow-band or 25kHz wide-band. Finally, the 14th column specifies the APRS system ID to use or "-" for APRS disabled.
    qdmr-0.12.3/doc/manual/conf/aprssystems.xml000066400000000000000000000040311501654372000206270ustar00rootroot00000000000000
    APRS Systems The APRS system list specifies the various information for transmitting your position using analog APRS. As digital channels may use either DMR or analog APRS for position reporting, this list shares a namespace with the GPS system list. That is, the ID must be unique across both lists. The first column specifies the ID of the APRS positioning system. This must be unique across APRS and DMR position reporting systems. The second column specifies the name of the system as a string. the third column specifies the revert channel. That is, the analog channel the APRS information is transmitted on. The 4th column specifies the period with which the position gets reported. The 5th and 6th columns specify the source and destination calls and SSIDs respectively. The 7th column specifies the path string. This is list of calls and SSIDs stored as a string without any separators. The 8th column specifies the map icon name. The name does not need to match the official icon name exactly. The icon is identified as the closes matching icon name with respect to the Levenshtein distance between the given and all icon names. That is, jogger and jogging will select the same icon. Finally, the 9th column specifies a freely selectable text to be sent with the position report.
    qdmr-0.12.3/doc/manual/conf/contacts.xml000066400000000000000000000041271501654372000200560ustar00rootroot00000000000000
    Contact table The contact table is a list of DMR contacts like These contacts can be personal contacts like DM3MAT, so-called all-calls and group calls. The contact table starts with the "Contact" keyword and ends with an empty line. The remaining keywords ("Name", "Type", "ID", "RxTone") are ignored, however, they are part of the self-documentation of the config file. Following the "Contact" keyword, each line represents a single contact in the contact list. The first column represents a unique internal ID for the contact. It must not necessarily be in ascending order, any unique number will do. The second column is the name of the contact. Any string can be used here. The third column specifies the type of the contact. This must be one of the keywords "Private", "Group" or "All", meaning private, group or all-calls, respectively. The fourth column specifies the DMR ID for the contact. Please note, that an all-call requires the specific DMR ID 16777215 to work as an all-call. The last column specifies, whether an incoming call from this contact will cause a ring-tone. Here "+" means enabled/yes and "-" disabled/no.
    qdmr-0.12.3/doc/manual/conf/digitalchannels.xml000066400000000000000000000116371501654372000213750ustar00rootroot00000000000000
    Digital channel table The digital channel table defines all digital DMR channels. As digital channels have some different options compared to analog channels, they are not defined within the same table. However, they share the same IDs. So be careful not to assign the same identifier to analog and digital channels. The digital channel table has the form The digital-channel table starts with the keyword "Digital" and ends with an empty line. The next keywords (Name, Receive, Transmit, Power, Scan, TOT, RO, Admit, CC, TS, RxGL and TxC, GPS, Roam, ID) are ignored and are maintained for the self-documentation of the configuration file. Each channel is defined within a single line. The first column is the unique channel identifier (any unique number among analog AND digital channels). The second column specifies the channel name as a string. The third column specifies the RX frequency in MHz and the fourth column the TX frequency in MHz. Alternatively, a TX frequency can also be specified in terms of an offset relative to the RX frequency. In this case, the offset must be prefixed with either "+" or "-". The 5th (Power) column specifies the power level to use. Here, either the "High" or "Low" keyword must be used. The 6th (Scan) column specifies the ID of the scan-list (see below) attached to the channel. This list will be used whenever a scan is started on this channel. The 7th column (TOT) column specifies the TX time-out-timer in seconds or "-", if disabled. The 8th column (RO) specifies whether the channel is RX only ("+") or not ("-"). If enabled, you cannot transmit on that particular channel. The 9th (Admit) column specifies the TX admit criterion for the channel. This must be either "-" or one of the keywords "Free" and "Color". "-" indicates that there is no restriction in transmitting on that channel. The radio will transmit whenever PTT is pressed. The "Free" keyword indicates that the radio will only transmit if the channel is free. The "Color" keyword indicates that the radio will only transmit if the channel is free and the color-code of the repeater matches the specified color-code of the channel (see next column). The 10th (CC) column specifies the color-code of the channel. To avoid interference between neighboring radios and repeaters on the same frequency (in case of DX conditions), the repeater and radio will only react to transmissions on a channel with the matching color-code. The color-code can be any number between 0 and 15. The 11th (TS) column specifies the time-slot for this channel. Due to the audio compression used in DMR, it is possible to operate two independent channels on a single frequency by using time-slicing. DMR uses two time-slots. This option specifies which of the two time-slots is used for the channel. On simplex channels, this time-slicing is irrelevant, as there is no central instance (the repeater) that defines what time-slot 1 or 2 is. The 12th (GPS) column specifies the GPS or APRS system (see below) to use on that channel. The 13th (Roam) column specifies the roaming zone. This can either be '-' meaning roaming disabled or an ID of a roaming zone specified below. Finally, the 14th column (ID) specifies the DMR ID to use with this channel. That is either '-' for default ID or an index (0-based) of the ID list above.
    qdmr-0.12.3/doc/manual/conf/format.xml000066400000000000000000000044321501654372000175270ustar00rootroot00000000000000 Table Based Codeplug Format This chapter describes the old and deprecated table based conf text-file format. This format is already incomplete and will remains so. This documentation is maintained for the sake of completeness but you should not use that format in future. qdmr will still be able to import this format but it will not save codeplugs in that format anymore. The new codeplug format is documented in . This configuration file format represents a generic configuration for a wide variety of radios. It is a simple text file containing simple key-value definitions like the DMR ID as well as tables like the table of channels, contacts, etc. The aim of this config format is to be human-readable and writable. This would allow users to write config file by hand and share them easily, as well as enable users to modify shared configurations using a text editor. To this end, the format must be intuitive and to some degree self-documenting. Within the following sections, I will describe that text format in some detail.
    Line comments To document your configuration, you may use so-called line-comments. These comments start with the character # and end at the end-of-line.
    qdmr-0.12.3/doc/manual/conf/gpssystems.xml000066400000000000000000000020421501654372000204530ustar00rootroot00000000000000
    GPS Systems The GPS system list just specifies the contact to which some positional information is sent to (which usually gets forwarded to the APRS system) and at which period this information is sent. The first column specifies the ID of the GPS system. This can be any number >0. The second column (Name) specifies the name of the GPS system. The third column specifies the destination contact ID (see Contacts above), the position information is sent to. The fourth column (Period) specifies the update period in seconds. The fifth column (Revert) specifies the revert channel. In amateur radio, this can be left blank ("-").
    qdmr-0.12.3/doc/manual/conf/grouplists.xml000066400000000000000000000031561501654372000204540ustar00rootroot00000000000000
    Group list table Group lists are simple named lists of one or more contacts. These lists may include group, all or even private calls. Group lists are assigned to channels. They form a group of contacts (e.g., talk groups) you may want to listen to on a particular channel. Usually these group lists form a collection of talk groups that are specific for a particular region. Group lists are defined within the config file like The group list table starts with the keyword "Grouplist". The following keywords (Name & Contacts) are ignored, but form a kind of self-documentation for the config file. Following the "Grouplist" keyword, each group list is defined by a single line. The first column specifies the internal unique ID for the group list. This can be any number as long as it is unique. The second column contains the name of the group list as a string. This can be any non-empty string. The third column contains the comma-separated list of contact IDs that form that group list.
    qdmr-0.12.3/doc/manual/conf/radiosettings.xml000066400000000000000000000050301501654372000211110ustar00rootroot00000000000000
    General configuration The general configuration settings of some radios can be overly complex with a huge amount of options. The vast majority of these settings, however, are useless for ham-radio purposes. Thus the possible settings for the general configuration of the radio are reduced to 6 key-value pairs. The DMR ID of cause, is absolutely necessary and specifies your personal DMR number. Keep in mind, that you do NOT need to get a unique DMR ID for each radio you own! All your radios can share the same DMR ID. The DMR ID is specified using the "ID" keyword as In rare situations, where you actually need several different radio IDs (e.g., if you use the same radio for HAM and commercial applications), you may specify them as a comma separated list. The first ID in the list will be handled as the default ID for the radio. The radio name is a string, that the radio may display somewhere on the screen. It does not have any effect on the behavior of the radio or gets transmitted. You may set this entry to your call-sign. For example: The two intro lines might be shown on the screen of your radio on startup. You may set these to any string you like. They are also cosmetic and don't have any effect on the behavior of your radio. For example The microphone sensitivity/amplification can also be set (on some radios) using the MicLevel entry. This entry is a number between 1 and 10. The larger the level, the larger the microphone amplification. This value may vary heavily from model to model. The "Speech" option enables the speech synthesis of the radio if supported. Possible settings are "on" and "off".
    qdmr-0.12.3/doc/manual/conf/roaming.xml000066400000000000000000000020621501654372000176700ustar00rootroot00000000000000
    Roaming Zones Roaming zones allow to stay in contact with a particular talk group when moving round and the current repeater gets out of range. In this case, the radio will search for the strongest repeater in a list (the so-called roaming zone) and switch to this repeater. Therefore, a roaming zone is a simple channel list. The first column specifies the ID of the zone. This ID can be used in the digital channel table to associate a channel with a specific roaming zone. The second column specifies the name of the zone and the third column holds the comma-separated list of channel in each zone.
    qdmr-0.12.3/doc/manual/conf/scanlists.xml000066400000000000000000000042101501654372000202340ustar00rootroot00000000000000
    Scan lists A scan list is list of channels, that are scanned whenever scanning is started on a channel, the scan list is associated with. A single scan list might be associated with several channels. For example, all channels within that scan list. The list of scan lists has the following form The list of scan lists starts with the "Scanlist" keyword and ends with an empty line. The remaining keywords (Name, PCh1, PCh2 & Channels) are ignored but part of the self-documentation of the configuration file format. A scan list is defined with every other line. The first column specifies the unique identifier of the scan list. The second (Name) column specifies the name of the scan list as a string. Any string will do. The third and fourth columns specify the first and second priority channels for the scan list respectively. These priority channels are visited more frequently during the scan. That is, the first priority channel is visited 50% of the time while the second is visited 25% of the time. These channels might also be set to "-" indicating that there is no priority channel. The 5th column specifies the transmit channel during the scan. Possible options are "Last", "Sel" and any valid channel index. The "Sel" keyword implies that the radio will transmit on the selected channel when the scan started. The "Last" keyword implies that the radio will transmit on the channel at which the scan stopped on, while specifying any channel index implies, that the radio will transmit on that channel. Finally the 6th column specifies the comma-separated list of channels that form the scan list.
    qdmr-0.12.3/doc/manual/conf/zones.xml000066400000000000000000000044111501654372000173720ustar00rootroot00000000000000
    Zone lists Zones are just collections of channels. Typical radios can hold thousands of channels. To keep large numbers of channels manageable, they can be organized into zones. Usually, these zones represent a geographical area and all repeaters in that area are then grouped into zones. Of cause, a single channel can be added to multiple zones. Please note that for many radios, channels can only be accessed via a zone. That means, a channel that is not a member of any zone may not be accessible. The zone table is defined within the configuration file as The zone table starts with the keyword "Zone" and ends with an empty line. The remaining keywords (Name and Channels) are ignored but are part of the self-documentation of the configuration file. The first column specifies an unique identifier for each zone. This can be any integer as log as it is unique. The second (Name) column specifies the name of the zone as a string. Any string is valid here. The third column specifies the VFO (either A or B) for that zone. This allows to specify different channels for the two VFOs of the radio. For example, it allows to specify a list of repeater channels for VFO A and some simplex and calling frequencies on VFO B. The fourth column contains the comma-separated list of channel IDs for the zone anc VFO. A reference to any channel-type can be used here, analog and digital.
    qdmr-0.12.3/doc/manual/epub/000077500000000000000000000000001501654372000155205ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/epub/Makefile000066400000000000000000000002111501654372000171520ustar00rootroot00000000000000all: manual.epub %.epub: %.xml pandoc -f docbook -t epub3 -o $@ $< %.xml: clean: rm -f manual.epub rm -f manual.xml rm -rf fig/qdmr-0.12.3/doc/manual/gui/000077500000000000000000000000001501654372000153515ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/gui/aprs.xml000066400000000000000000000100741501654372000170420ustar00rootroot00000000000000
    Setup GPS/APRS Position Reporting The GPS/APRS tab allows to specify several so-called positioning systems. A positioning system is a collection of settings on how GPS information is sent to APRS network. Screen-shot of the GPS or APRS settings. The list GPS/APRS systems.
    Edit/Create GPS Systems Double-clicking a GPS system or clicking on the Add GPS System button will open the GPS system edit dialog. The digital APRS editing dialog. This dialog allows to specify how the GPS information is sent through the DMR network. That is the Name field specifies the name of the GPS system. The Destination field specifies the private-call contact, the information is sent to. For example 262999 in the Brandmeister network. The Update Period specifies the period in which the current GPS information is sent to the contact. The Revert Channel specifies to which channel the radio should switch to, to send the position information. This should always be Selected. That is, the radio will always send the information on the currently selected channel. If Show Commercial Features is enabled in the settings dialog (see ), a tab bar is shown at the top. There you can also access the device specific settings for the GPS system.
    Edit/Create APRS System Double-clicking an APRS system or clicking on the Add APRS System button will open the APRS system dialog. Screen-shot of the edit APRS dialog. The analog APRS editing dialog. This dialog allows to specify how the GPS information is sent through the APRS network. That is the Name field specifies the name of the APRS system. The Channel field specifies on which channel the APRS information should be send. This must be an analog channel with one of the typical APRS frequencies. The Source field specifies your call and the source SSID, while the Destination field specifies the destination call and SSID. Path is an optional string containing the APRS path for the packet. The Icon combo-box allows to select an icon for the packets. These icons are then shown on APRS maps. The Update Period specifies how frequently an APRS packet should be send. Finally, the Message field allows to set an optional text message for the packet. If Show Commercial Features is enabled in the settings dialog (see ), a tab bar is shown at the top. There you can also access the device specific settings for the APRS system.
    qdmr-0.12.3/doc/manual/gui/channels.xml000066400000000000000000000305551501654372000176760ustar00rootroot00000000000000
    Creating Channels Creating the list of channels for the DMR radio is the most cumbersome task. Remember, each repeater has two time-slots with possibly multiple talk-groups assigned to each time slot. For the sake of convenience, it is reasonable to define a channel for each talk-group you are interested in on every repeater. Thus, instead of a single channel per FM repeater, you will likely define at least 3-4 channels per DMR repeater. To ease the burden of creating a lot of channels, qdmr implements some features that should help you in creating these channel. One feature is the automatic retrieval of repeater input and output frequencies from repeaterbook.com. This is a world-wide database of ham-radio repeaters. Both, FM and DMR. When you enter your locator into the settings dialog (see ), qdmr will provide you with a list of nearby repeater and fill in the input and output frequencies. This feature works for both, FM and DMR repeaters. Screen-shot of the list of channels. List of channels. The Channels tab shows the list of all defined channels, irrespective of whether they are FM or DMR channels. You may add an analog or digital channel by clicking on the Add FM Channel or Add DMR Channel button on the bottom, respectively. The Clone Channel button allows for cloning of a selected channel. This enables one to create a set of channels that differ only in the default transmit contact but share the same remaining settings. You can also delete a channel, by selecting that channel in the list and clicking on the Delete Channel button at the bottom. You may move a channel up or down the list by selecting that channel and clicking on the arrow-up or -down button to the right, respectively. Finally you can edit a channel by double-clicking it in the list. The number of channels usually grow fast and it becomes hard to find channels within the list. To search the list for any channel name or frequency, just hit Ctrl+F to open a search box. This search feature is present in all lists. The channel list, however, is likely the largest.
    Edit DMR channels When you double-click on a DMR channel or click on the Add DMR Channel button, the digital channel editor dialog will be shown. This dialog allows you to edit or create digital channels. Screen-shot of the DMR channel editor dialog. The DMR channel editor. The dialog is limited to the DMR-channel settings that are relevant for amateur radio. Thus, it is much smaller that the typical dialogs to edit DMR channels in commercial CPSs. However, if you enable the Show device extensions option in the settings dialog (see ), an additional tab will appear called Extensions. There all implemented device-specific settings are hidden. They can be used to set all options you also find in the manufacturer CPS. See also . The Name, Rx and Tx Frequency fields contain the chosen name of the channel as well as the transmit and receive frequencies. The latter can be set automatically by using the repeater auto-completion feature: Start to enter the call-sign of a repeater. A list of matching nearby repeaters is shown. Select a repeater from this list and the RX/TX frequencies will be set using the information from repeaterbook.com. For simplex-channels RX and TX frequencies must be identical. The retrieval of the repeater information may time some moments. Hence, the auto-completion list of repeaters near by may not appear immediately. The found repeaters are cached locally for faster retrieval later on. The Power setting specifies the power used on that channel. For a nearby repeater, you may reduce the power. If you check the Default box, the global power setting will be used. See above. Tx Timeout (TOT) specifies the transmit timeout in seconds. This limits the continuous transmission time to this period. A value of Off disables the timeout. If you check the Default box, the global TOT setting will be used. See above. VOX Level specifies the sensitivity of the VOX for this channel. If OFF is selected, the VOX is disabled for this channel. If you check the Default box, the global VOX setting will be used. See above. Checking Rx Only will disable transmission on this channel. Scan List allows to specify the scan list associated with this channel. If a scan is started on this channel, this scan list will be used. Each channel may have a different scan list. The DMR ID field allows to select the Radio DMR ID for this channel. Some radios allow to program several DMR IDs to be used with one radio. This option makes not sense for HAM-radio usage. However, if you use the same radio for HAM as well as commercial applications, you may need to set your HAM DMR ID for HAM-radio channels and your commercial ID on commercial channels. The Tx Admit field specifies the Admit Criterion, under which you are allowed to transmit on the channel. For DMR repeater channels this should be set to Color Code. This means that you may only transmit if the radio received the correct color code of the repeater before. On simplex channels Channel Free should be chosen and thus the radio will only transmit on the channel if the channel is free. The Color Code specifies the color code of the repeater. For simplex channels, this should be set to 1. For repeater channels, this must match the color code of the repeater. If the auto-completion feature is used, the color code is set automatically. The Time Slot specifies the time-slot of the repeater for this channel. All repeaters have two time slots but different talk groups might be associated with each time slot. You may need to consult the webpage of the repeater. In the Brandmeister network, usually the time-slot 2 is for local/regional communication while time-slot 1 is for DX. The Group List specifies the list of group-calls you want to receive on this channel. See above for details. The Tx Contact specifies the default Transmit Contact you want to call on this channel when pressing the PTT button. The Positioning System (DMR ARPS) specifies how you location information is send over this channel (selecting None disables GPS for this channel). Please note, that this setting is ignored for radios without GPS. The Roaming allows to associate a roaming zone (see below) with this channel. If the radios has the roaming feature, this zone is then used to search for a another channel of another repeater that is reachable once this repeater get out-of-range. If Show Commercial Features is enabled in the settings dialog (see ), a tab bar is shown at the top. There you can also access the device specific settings for the channel.
    Edit FM channels When you double-click on an analog channel or click on the Add FM Channel button, the analog-channel-editor dialog will be shown. This dialog allows for editing or creating FM channels. Screen-shot of the FM channel editor dialog. The FM channel editor. The left column of the editor are identical to the DMR channel dialog, that is Name, Rx and Tx Frequency, Power, Tx Timeout, VOX Level Rx Only and Scan List. Like for DMR channels, analog channels may also have an Tx Admit criterion. Possible options are Always, Channel Free and Tone. For FM repeaters the Always option should be chosen to allow for a quick turn-around in a QSO. For simplex channels Channel Free should be chosen, as it only allows to transmit when the simplex channel is free. Selecting Channel Free on repeater channels, would prevent transmission while the repeater is active although the last transmission already ended. The Squelch field specifies the squelch threshold. If Open is selected, the squelch is disabled. If Default is enabled, the global squelch level is used. See above. RX and TX Tone specify the CTCSS/DCS sub-tones for this channel/repeater. The RX Tone specifies the sub-tone that is needed to open the squelch. The TX Tone specifies the sub-tone that gets transmitted (e.g., to open the repeater). If the auto-completion feature is used, these tones are set automatically. Bandwidth specifies the band-width of the transmission. Possible values are 12.5kHz (Narrow) or 25kHz (Wide). APRS allows to specify an APRS system to be used on this channel. If the radio supports analog APRS, your position will be sent using the frequency, destination and path specified in this APRS system. If None is selected, APRS is disabled for this channel. If your radio does not support APRS, this setting is ignored. If Show Commercial Features is enabled in the settings dialog (see ), a tab bar is shown at the top. There you can also access the device specific settings for the channel.
    qdmr-0.12.3/doc/manual/gui/contacts.xml000066400000000000000000000112351501654372000177130ustar00rootroot00000000000000
    Creating Contacts The second tab is the Contact List. Here all DMR contacts are defined, irrespective of their type. It is not only possible to define digital DMR contacts (i.e., private, group and all calls) but also DTMF contacts (and in future two-tone, five-tone contacts too). This eases the control of the EchoLink feature of repeaters. Screen-shot of the contact list tab. The contact list. You may add a contact by clicking on the Add Contact button at the bottom. You can also delete a contact by selecting the contact in the list and clicking on the Delete Contact button at the bottom. You may also reorder the contacts by selecting a contact in the list and use the arrow-up and arrow-down buttons on the right to move the contact up and down the list, respectively.
    Editing DMR contacts When you create a code-plug, the contact list should contain all talk groups and reflectors you are interested in, as well as a so-called All Call contact to the number 16777215. Additionally you may add private calls to several operators you know, as well as some service numbers. Screen-shot new/edit DMR contact dialog. The new/edit DMR contact dialog. When you click on the Add Contact button or when you double-click a contact entry in the list, the Edit Contact dialog will appear. The first drop-down box allows to choose the type of the call. The possible options are Private Call, Group Call and All Call. The second entry is the name of the contact. Here any text can be entered. The third entry is the number of the contact. This entry gets disabled when All Call is selected as the call-type. Finally, if the last option Rx Tone is enabled, you will hear a ring-tone whenever this contact calls you. qdmr tries to download the current list of all registered user DMR IDs. The contact dialog will use this information (once downloaded) to resolve call-signs to DMR IDs. Just start entering the call-sign into the name field and matching call-signs are shown. The same holds true for Brandmeister Talkgroup names. That is, start typing the name of a talk group and a drop-down list of talk group names will appear for auto-completion. Select the talk group and its DMR ID gets set. If Show device extensions is enabled in the settings dialog (see ), a tab bar is shown at the top. There you can also access the device specific settings for contacts.
    Editing DTMF contacts Screen-shot new/edit DTMF contact dialog. The new/edit DTMF contact dialog. Beside DMR contacts, the contact list may also contain analog DTMF contacts. You can add a new DTMF contact by clicking on the Add DTMF contact button. This will open the Edit DTMF contact dialog (see above). This is a rather simple dialog. Name specifies the name of the DTMF contact, while Number specifies the DTMF number. There, any DTMF number can be entered, this includes symbols like *, #, A-D. The Ring option specifies whether the radio should ring if a call by this contact is received.
    qdmr-0.12.3/doc/manual/gui/extensions.xml000066400000000000000000000057751501654372000203100ustar00rootroot00000000000000
    Edit Device Specific Extensions Since version 0.9.2, qdmr supports the editing of so-called device-specific extensions. These extensions were introduced with version 0.9.0, to allow for configuring all features of a radio, although not represented within the common codeplug structure used by qdmr, to program all supported radios. These extensions can be added to every element of a codeplug. For example contacts, channels, zones etc. They are stored in a common way to allow for an unified method for manipulating them. All extensions are displayed in a so-called Extension View, a tree-like structure present for all codeplug elements. As qdmr is intended to be a clean and easy-to-use CPS for all radios, these device-specific settings are normally hidden. To show them, you need to enable the Show device extensions option in the settings dialog (see ). As this extension view is common to all codeplug elements, this section will discuss the usage on the codeplug extensions. When Show device extensions is enabled in the settings dialog, the codeplug extensions are shown in the last tab called Extensions. Screen shot of the codeplug extensions. Extension view for the codeplug extensions. Unless a codeplug is read from a device, the extensions are usually not set (see screen shot above). In these cases, the Value column of the extension view shows [None]. You can add these extensions to the codeplug by selecting the corresponding extension and click on the Create button at the bottom of the extension view. Similarly, an extension can be removed from the codeplug by selecting it and clicking on the Remove button at the bottom of the extension view. Screen shot of the codeplug extensions for TyT devices. Example for codeplug extensions for TyT devices. Once an extension is added to the codeplug, it can be expanded to view and edit the contained settings. To edit a particular setting, simply double-click on the value.
    qdmr-0.12.3/doc/manual/gui/fig/000077500000000000000000000000001501654372000161165ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/gui/fig/Makefile000066400000000000000000000000051501654372000175510ustar00rootroot00000000000000all: qdmr-0.12.3/doc/manual/gui/fig/qdmr-channels.png000066400000000000000000006463341501654372000214000ustar00rootroot00000000000000PNG  IHDRMOsBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeMo 29 Nov 2021 11:04:05 CET\O%_ IDATx{|}d3l9#9yC/PH*0*Ӊ;8$䬤B9!sl[vz>{<]^{}sI؄!I*!S$EWI1Ifm& ʹ׫QK j04W-٬ثW?$$}X3HVJ'$"`0A*b:qpC0fMUTU˗ III2L9 O&IIIIOH0x-Zbd-hq(V+WXl+W ŋ^G%Yd+Q%(hPիN9(bccT4XRqK[dtpt, 6܍fJ򲤽I6SJo 6T\%m-M9L&Ow"TX%m-R(H L*`$EdTNZ,NAqGMPIv4ex/2٨W%]HHW􉗳!Ug>&uLC8VF[5daoLgɤ\m,b,!뱇k˛@x%X`Kg~7g퉯U4Ѥ~>7n'}Ug"`;w6?f f0J6-ֵe47J5Yy%hEە$dH.I 'eh6)~jy<;QrtRI "NЕHKj➤]emd[2n΍USWyު&^U^|DQ&ɜݓCzwOFiP54/_NZAKuʶJ6JU Sv\{J ܦl:dI?rYfIIhSNAnn2djPNrtkf%%% Ddf,3REF"oӗH˅$'))'i?d|#ft r.;;o~<$oE$llZvlh9l9Hͤ>S)Ru"y9:9eljz4R綥3-;MAJe%W3῍ 5t3RҤ/%:)$FL'k/TL[xYRjFӿk֩^5-'I);X*?}_rCr6HM~o$ZM % <Ŧ1nMLgr˚]wRIL2 3W+Ox]b+v(.&qMO${],dۦ,-n#=PZfG?Hoo@e,iU.so,iGv( ; :dS>B٠/ۖLE w.1Iݼ=I>KD&c>vQKDGHܑ J8p=bԹ$n( YRDQI[?"p#X$`%lHV"X$`%GKF]d8;I_g@+ZE( X$`%l?f>o?!Wr~3z5:iX >w|H]Wc#^)Z~J[Ȝ؉YV>(ݑ>I:ݩVI7)$ۭʹI6^%Uћ~7C),]QKG8v[%Tv u }Kj"2T)P% 6Z:_}v pU_)MÔ?Q.!Byn=;7֨ey6 \,Ѻ!76-Lt%VUMj_ 5xU}u4^R6 =Ii*hS9lFpb&>R5ʖTIJmI( 直GSQ (-Ի?WӥJ.;P%XF\O~%TR}uyyN/U[\~3.mTuxu(%P:MIw+3DW(%a+f &Z?1[YeJʷdyj&Ky3ȞfVJrSRRdSf]5ubGViRiB ]!sþPobkzI9H2[! 7NnrnzA7JHW|? 2wk:sY cLXgߍ>oM^|N_H_IuteW rzQ1{7t:UQ"3_,k@ GIu#nV:Zo6in^OɯM;ռeTSt|T}w%ҝ9B r(;x8mViϑd*"*UU-]?w}{=7j;JRs=pB^/OvRh9 UnG+vQ]7}AQuNMj֤A5UƘ=}KSsWIz@ݪ|ST,ͽĝnn}7;M|9e޷KjӲ%hDzays @fdsiWSH3 N*VI>-Ov0^17(m)9QΛbvO4tWxI"CV]]SC)Z=u,)1C 2W{]M|oʘ̗g& x9{8JJ!%~,;hT U*a$m,jpP:u埮[6TH2:ed6gy͎I6 dY+f5-T*eQRϦ1d*{]F9W⦕:xGzgpo񄺖zD/]+F#ZHjVfnU&p)oj6 4,squ.)Yr*Yoެc5T5G%_Rj՗Us"bpS}4qoFQ*I?ժtYLmJ;YUHU/妹s8(S'TŝE'ٌ^"w7P~ztHOk7kx`ܑ3W-Jr $sqپM)AS-3m.Irn8@.#z|#åu(~=ӥ$ɥUEM}YǜjJ |ᙃ*LJ)NQĩ|vҪRC_U9w)Ŧùz-UQ>@e{U܉~eImufB=u,cr Z- _ʽj%A.$k:AnJPP/h߯erUsjabvQ=.U9}"Ym|~eI2s5ঈfUIjօ5x\JV _e%=.]ݮ: lPVrx-Zٵ}-BC "( ؉ Ϟ=k8;I@{1,NL&{1x&`%l,.i6p`%`%l,.i ٶK{=a qwssw;]vm{nmk׮Ӑ!C4dyzz9IM&l=>7KJJݻpB\RW\ɲm…ձcGW50L_4w\mڴI2L5SV3hEY'[!KknXgA!vܩ޽{%h"5l̈#4w\%''Kɓ'ϱcjJLL}V^paf%^XҎr7'񈉉ɓYb%K6&IW^!Chٲe6!11QK,h"Y&ؤ 3gh…8p-[fqw8_^[k׮չs~з~a=e˖%$)999mU[N-\Ц󼠯"s"&&F&L$J/7+wz\NԩS M$XBO?RRR$I˗WfԡC5h@vvܩZ[lщ'_)44TԣG\hKLLTXXíN~f8p@C 5rH aUe~`|'Lp%&&uٳ|||4|洶fYWuy-YD7n^zI ٔ%%"$ۮ]Ըqcdxm#E2d$fKeٽ{z-`0QF3f4h!QvکUVڵkƍ]vd2i P-rd[zqqq6m*V=zحt$?p%$$H֯_Qf9m[z֭$ͯxM:UW6mkϞ=5{lNNNիݎ=lWjܸq{_FVFѣGώGZmĉidkTT,X'NH5jɓ'vY䤦Mj9rvڥ'OO?U5cyJҾ}r`kР ø8+((H<@(1%x-ZԮ03Z"gϞ|X[nULL<<<ԼysU\YDzFV `\%RKDґz;uZf盾tdΝ;^wYMggguQ .$ر#G1DDDX_Æ `;u-ZL9{vyW%,+B Tx[yyyiΜ9_e0={gUtttZ[ ___œ+Vӑ#GB r:ty?_5Xebcc`ٶI]Ae/Đ?uuĉ3}ѹN9sFAAAYӕ+Wd0t9Y}(-[VfYpU?ڲe-]\\ J_"tҒG޽{3ѲeK^ڪx&N3g4lӤS^̕Cv5**JGպu4|EEEٳgcǎf 80CGW۶mUR%-Z4DZ EjΜ9СCOm>ݽlWss={H9H}FRfR߷sҋ$9::(&]_e?k>9r$ڵSxx-'۷o7x#-v駟nѣV"]_%dQj͖ \$K/';3!&IM4mziӦEFFV>}/*O>$- IDATaÆaÆK.?UXF2}$)ؐWUW\QJJjuW.\gOEFF޲Ai k֬gj޽ VXXXlcǎirUu2IYJntY)s%j֬#FJ*6?vz˗/@y{{gon2j裏rݻ^O6M+W֟`I9+ (rdKNN%'aҗ9P,{yjժڳgL&ݫf͚YGBjl$) @ ,Њ+Եk }f-<==ӒUˏOmkD%1o߾]f͚>@A Ѐ8,a4Nӧof2^e88ɖي={QFJNN:儭k㉉ѬYn[r/͠Ar}Y={HV^F~7ߤެY3c-VΜ9aݻ/kرTsϥÇod` 4HmN+3hР\%,\r_V^S%}]詧YcNsN]x6GQdd//^̴4`Pbl8͙3'^A [FKMXXܬč-Kެe˖*R$iƍڷoUO$Ijժ1TT)}ƍw %"sڷ<==5hР>cڹ^YfI E.\ailll/f&<<\ ,РAnY }vݻ7}ݫ۷gk i;wԖ-[$InnnҥM|+ĹW_ʨW^yΑIkVΝtR;vLǏפITFl=p|M?~\MnnnVj֭JIRBBVZgO?Zȟ9~Fڴicu<7LNNJfYrJxﷴ=tMc4i-ZwJLL$]tIԜ9sTn]H̿{j...Z`|AƘz ԩS2j۶ׯoFVٳG]|9%"BѢEշo_/_^fY[nըQgϞ,)))ڻwFm۶m߿ƍSNYCF{ѨQ7hȑ%$TRjذUN6OO-[C_ .WjϞ=oyf;v,C&I[lѣ5qD[[``BBBhѢ,{n"FQ!!! 8zxx(444?{\N<ϫE/2׼yf>K,QBB-/]5kdBB,Yݻ[_v֮]HI}ݗZ ̕ jYRRR-qeg"NfgEț9::k׮'uq}$pydv^fYVRLL&Mjժe{|OOO 0@ׁ2mϩF0`Rґ:'''UVM#GTvTzu={6~uш#r}ˍM6_UZlV^2d-jӛ6m#u*w^999gM-KvY3fPϞ=.ٜc22Oc6?護޲صj?/WW\j 䤺u_f͚%WWWM:UǏ5uT9;;k֬Y駟NNDD-%knxNNNV%o-uܹՓ$kҤI4iLuF ,W+RKD`8*UӦMsN矺v * 5lP[d2ed2￷FQ]t_ӧ+...GaÆsη< S|y5JqqqzuI[NO?qIVr-[!!!8p̙$W^3}Z|rG }LYqh=K06ّ/KkY `IZhQKfUOyάuz}n]t`~|*7|]lK?/ m׵dER#ŇS,npU*못: yjEhQO$I'ƚSd/T $Y/rY.[Y[tՠO WI[b 5 M~wP`*̷u s6B hr13%JD[J=v-͊sJTڿiP^9,n5)bӪYJ,TZ}_B7Zr MUC hGn!KIնHzFMDXSr/ՠ9{u%OƙQ*U,%߹r~Mjsr$-EЯ{>w\%kv;ߘn7J=􄞛TWe=t~8zYf{h/jg/"IQ:uMw\ &hy#lSƘZz]]2Q;7V_Xev+կFaIRʁյGuAqTy_)$/}5n:vb]T,V$mӤ##:yT>81Df^X[~;b~MT}%ЪOp_CO^SXXi[^n'^z[o@^>>T_{tР7V G]=}@Qy-nM|QQ)4|kj隤ȓڷj?~Nw3h0Nv8V*zci:A}2{7ڲ=~m %򅿴 A})ioz@j pӹ?O/џ#ę?9VB ͗ߵmW)C]jXwрnզmg4V3=7n'XIOTruz~v=ʤ_TV T#K(vQ3TrLit*Q_L)2)ve-U:n˱}R&Q̮Si~SOB^$͟Jٙ`ըx}`>IKhp (!OhJg}}z$-4FsR^UFu~YiͤM/j@'$ɬ?StM[Бuב_QFUU_k4Rrl6[uk:*e;,lѧTο6饉X]+DO,{ȵ#Z1 u}cu ZvdϕleXި7-C9כuDZW쮹z}ғ^l>^z3:\/u .$bMꇟv<=VIj[c?r6ܕ?gcO1Y\.i2+ _֐"eg^[^hg_z gN#кOYcT67-y;L={hŚUIخfx:W%nܜ4|9 9 e |jvѰ{b.1/ihJr0֒]5b7bƕwQ̩k*SALci^-Y\ZM׈f޷&]طN[ ԈoWLwH3ǔj:|>7]u*ev(ԹyzbAOn]/K.1֊#Eu nuK_i-TA1't(N9CGʱz=v8|^z"Zw7ՠ .mUZ?mRڵn!gI-kgAfSU.}8TvjRDRگu8FZ>]0_wAR]N}R q^eոM3-ij׳Y'>(M}Ҵ'؅.,D^ ?R'{譋iŪVLz]CrQWUG6JKٰS?g=#p}и QdV\O筿ɭL}t鐾|g^J_N6NxVoh7?މ8rR3Uslwk*f.+јn0ѷ~[9jy}xIgP;=?{D'R8xA)o6x *bڪU~QȻ$)-yF^CW$}L%5{f+.3a:^UwERJCo区gL~E{kѳGz]Ӈ;稓AY=b:Ջ?Q35.]>ը[yzT,( ;&٪(t::V4VU[wU>O[HsiՌ%loGuAR ]][/ܤKzFUTw+S~sMdQ!jR(5V?$moHn$s II1/(PF2\J9#ǯɡJm$p}u|;Q=K$5S8m;O3{V{ǟj܃n\nN):cj< |~yZpQUlr}$=RsՕ$j8ےCBB BUz]A`" MA4A={o) $Hݙ $Axsywݹ[dBw-ƅJѰ7iE ;fږ~hTr;ܡ! ^ 膟gC-n_1*;O_QUE&7-JfO&\Zgt8Z%`H,tiiKS v޺Bt,Yb)0q\Q})b(dMt%LJUkχߛmx NNQ'ԿMYJ_S+416")o,Ǵ xשK:c&ڗp&q͠=ջ;*9ݩ:ۙ忔q-՚Ѥ-PZv\Fug8Ū DǠuF(nUR_3v4hhH&?ƔP7ϢJ/NⓆATVTz IDAT|>{\ZYErp8Zes:dbÃ83-SܴtmF0A ߞ֡=ݳ5͟=O!rw.SvekrnPˍF=6 }7BڤU.4T׀:EPӝY5}:|iyַ_ɡF:KdGNcN0c#;8LpҜ !B!B<\9z˲})/3w@cjwp9HO.ꉧ'E|[[aMc)Qc㕓NR؞dO겛䃟=r>iSCOVhH=ׇ2+[n =1P%;4nGo4FŲؐ$&'*:I* jgs czA4O6qjTâKer31lPxuz :٘*Mrsӯ2$|4מ""̪*mDDZ 2L K~'9ӍGd G٥sk3lwgM=fzPL"K TTU;q N.ΜLߚֳ s_dNXAt+L%OASW>"&S~}?( )c:ϩ GB4\br)ӓhE-%fxωlԇҷV0u-Z]GA'8@`<5Dr3f53m~H?ӲI/QsskF>.SEJPV|0 >͂!U0#&|qڱ%oK}pKS{w(vDmQg~{60ڈ]%RԺe9ٟ atӈ H;r˴ Os][uҜm^k>s1gX}/ZծB2wהD3΍nZEyZׂEdt6cy$)Bi)~GN!B!"ru _ħ㖰{Hbȸ=$ެ6m;w3uٵgOih -=;tz= Уg6mLybuh6åW΃,0]:_zO=rC+VL)=0aST~{->+A4)*IS~T[cI1DDDYAԌbpO5Z-: soC3ve1#L>/Je/;~uZ&DWΥ+PFSR7.I~xЬ|Tk%?}/Ѣ#נRWzWO]y|uǻcbv^/>GBP)f|<2'~'ѧ ;1jѶ Ce&=~_tU䋬~_Zu høv][Z0Y ~&*~*|;XOxOǫ~uCw#K&G);uJ+|JŰN 'ܺ&O+[1Z@jv1# y?cnM+ȅuwoSַ|G"5AmB|H- ee> {ڎ msM"}/=iT<H ;Ś܂zO66}+u7Q3EQ%R=Q;2mMBux ܢ~_Zɤ/Spo)P;o"B!BmvHM9gfH$\KTJMGr;&.'VICwueh]+/-hY.p l?L3? Xp\ sj4=,m?ܼVݗ#{R8+Bg /etعrWѸc$];Ϳ?5nȖ?1L*Dww2&OŻbZcW{p ǎfքȀ4b^ӴdzԌw7&Z/զOlj$IZk܋zT솴^.b~%%c_6oN^ŷ] ոք}7{ Z Qv39I5ڰ 4F'#]F6kJ3tǔ4?&ƅrz,BA}o?fsk?6ϚWŭBOO䧡wހI0)7b^EWz(>c͏ >_!{Ofw1q6m cMOq_5W޽ $ sec/7a%;e y4>y;@y7Y_4)c32|`%VcXx; sbT>ȿe9_jDQ|:gOgWKX X+L3Yd8Fڑ5Y7Nũa,V@<[ɲ`8PDj(,cuFp{}Cc:4U2Ofecgi=~83+,[1&<(W/g|Abs#C+׬hG}1rpM9/i6_?> )昇aV8߾d31hSuB!B!ٗݻ'uo~jժ)2A(ܦt:uW"yS!TȐk_{صk;wV^M&Msy/%/z ~Am_U3</zKRٟH~^}|H}>_ϼ%Θ>;a,wwr4mEQPUUU[Q}a_0:&B!B!B!^,itSUV~ y~h44T!򂴻{b*zb$r}|9 *|$yK1'1}v!q9ϗ 5 !B!B!BC$ DA 򃴻{b*/(!`7!@ɧSPsuh_MѦ.wU1^z4q}R|\"|fIWD^}=Fx-w$6Ӝwajy.Tz 1|I\sY^\dj q5]VOׯGUUڷoEyaɓlB!+xO*Wµ ؑ~!+7fЗ?3ii? -<[Ɏau)R fOj=&| GH8q-ڌ_:TowŸ!' GƪTuw\N4ts!^CJb$Os%Zܫ.iZ[`Y+K䄗kyxO>ܛ-Tq!r3^fGzȨi8oߢ?ߘ׭_ya [{?bgaEFM F6<|獍`]|Z {y%,m#V:W=:ͪ 1O_QiUSv%~gS7inw/Kk; F>|(ϸ@UL΁vq[ Gƽx%}h%gϕ\$/Qba \,u: b@b}?W 3*E֣9M{x'sݔ>% k0}MrүѲsُZLj\S?KpڞJSYO}->Srrޕb x1lJ:L=`m=omQobŒ6PKC:zW=΅JRQ_͹גʽս)`iuE躔۬㉕u=hwG/nM/2Ȼ(VXZXbe[Q/uLjzsWoЫ.6(B:yeKi秴l!'--ޥ0kパ9 wOWP#,^ gu}KbmZ9eı?߱\(vVXZZbm{*4~{(w_')x8'5+ٚw\%e4\;=:OjyeNۉAq'#g}G:hk?%l;y1Mu+k[݊Q~g>(_fGmdՏ|uX~@[^"Ut((&UAMCi7 ̚JJR>:aǒw)e+ݝEVo.9_uVF^O2ԟ5iژEE)4՚Ybe{z+4ƴNMkDZ`!!:,޵^ Mqj|>M90eE14:m7ith5`q/qv|o*&NoV\;5;~$sbӟزUC8e=΅ :u?Tʊ3yfժqUc_QGR@%;cFCGD6lʷqƒxhÄ, ,FF#ԂşB߱3=9F4έ5=Jh5j/?vo)kӜ":ȲMWTU!V07"QtV[Grv͗ٲ;\N_;ZNCM?iѺR(ZgZwk#*p}$<pt}F?g[$?Sl%ph>wq%6,nahu]B IDAT\=5>ذZGtAՠ#FSN_+]_GWoeSAx>x>>k̍6kʜ[Z}ϝ{}y#Ø]-fvN}f3ΜYwNxb#W}S?>vAvu^h/tZ~ȸ8⸳'3Bb#jo;ԵʮM}xUڑV3qgH6A9ς8QzݾeGsz׊N:o@c_ 6~A{ O)זq#o9ag{ZtzSUBu([; xR~]j#n}v : _~7 >JbF;??%'9aТr'2v k}ә}"^8;S0ǥ-YE:ޔpw xgF؜JN8(w,<{xvMO?s=+`pFE] nkP{'*6|Kj)lCAWkˠ?O3~ xRy<|ў\w(G*4fi٢VVY%ghηt Bei1E*7}~>s!!\:'Q\*s<ϖ]6eE?ʝBOrUV.d[t%q\*w_7!3.q͘-?l}Q9y5wot|3::$0wʻ;R?cvYmټ>P_id~`;ѻA9 9VfƹS_l9fȖKn\-m$'S`$ƾ?J7ƮcbQ]:[0d5\K\{ z(3g2z|Vh_.No@>ݿPnGKu$f'S#^@B bJ{-;}>w]`=4$ˏfq9R*q{`2Ql4J krRT-]%8ZSXg"|׏53{G݈`\PU0oȔTNdξ7,`ơ%DݐSl^9}c'jr6VlO)4izҢ7fJ0!KqMz>j PN_fUn 1w9RКu`Ȏ]0*+{{ňƬm["o[_ $ʄk,B8;ZɤI)21V K Δ(bw{ܖT dUW:עxZ%rn ^y#Qcg$R",j!,ׄTSIo;…h\#;f零/ [qX;|VJ]I}>܏9;)5 M<4DnCsz`&``83?zNSmpMU"MV8A3!rPZwGnbVIWطK9KFUёi 6ƕܹzvl`חa^_(Ҷ 0enV`E"5t6#f luӃ}n)u=sxN|/pdM'sodv)1}c {65^ 95M)uԤ0B#DnVębS/~<4L| 3dTiC5px(&Z4Řuʨn)>&:G6z'\l:/]A$XC5,Fs~<+pCz?u_Lւ'p+N%g]Ɛ6ߨںSlYʖ-M]s/UWwf,pǵ\sϝ2z0]cDg3g;3֣PBxqh^Mj~e?"8ٚf^&Pv%(^\!3.9t۩G3wp1Ξ2+RFl̠Q,e˖}c;%~7ݵ(2D&ipm7I<%r.1㦔+hҭWc<4)Lf}`̪$[`mF?Vy[!:;S٩L^Wn^?x(xx]ʙx h V.)[,eK94u1*V$qnbODZx1xwԶI3N.ˑ{*ZtUh=ZӒrmGg]6$g՘迂Pd*UTȓkZcgz˔t)uz#Y49vx%dDUDAS!w M^ME)kѸ]( 7=~T5CvH:>zj>D| G)w競ƕҵߠo2*SM(1‘=gKs4hZa-UtxtJ딿GoKFzo1fEc='hțMEt7OWcGg,SjIEG ~v/1lK[˸-dUB g[c:JjŖg k}/ad[D/laoLM'ٶolLdnt痣)4^{3n/̋Sjq,cqf$ H.v_W|&~eڇXdЃs7vOFMfMUuh^D3SvTF4uBmQ|62?/ŵ9_F26T`.*|O'1& j"1&$³nl,6ӰU-,5 Ǐrɠ| TLG8r˄}/E>~#[׿Y#T86$gB_NՒlF/#*EVEѩNndavp̻֨ - +Ư:.s-Pcۄ 5F)҃Eq9?7p6LL܉ ܏T?fL3/&>Lvak?u]M,H&`^ż }/pd 4q˂Ka\+4J4{!><H>MP\Q<&\APwUSOM$nN1GIpl&֍˲N]i==k?AWnz~47/ i7Nܩ9t)!\>!tc8=lN.qaJTӣmUͶHLQԱ` ȺK hvcɫ\!$hmmA 1c޻(Z'Z.NgXrCG~S5R,S7ˉMĵkQ Sql5s7Aȟ+tyDr<΃0<#}=IY8ey.\`;EЩBp5ռ"V'J!ȝcb˜|;{5w1*h, LR?.ĔדY0b~ \[+]ἦ1k4 !ú)}.hNx-o~Db0f' >a rYՀiIu-c6vSO>YV'=-sAbJS&=?]__|{էϬg4Q)|^3*|2/kXdh. UiL}|q ̅SZg"thff^?ZcVy2~-LyP[9 kף0^R@"FnF$D\HC֩=́39hIQZmnJ=7?aeȺ\)뷦fʘ[5J>j\ɷp`/d6${"[Xau:n,ԔrŽi +gT]:tшʙSGW*@Ϧvty'.Mݥ!!c}aOԇ5Mjޔ/QRo%Ф{ŨǿJt^2u~f$$$7ct/CǮ OM@N~g-h,0dAgYpncHͥoa3$XV18'k,I}:Z|A}=k7潼۳W>q ~?@|G5^aӿI|cl櫜߻y|,cGra?b}-]T3ƽgpA_'𖻒eܞl.rjz; Q;׳+*{0r[Qw @q˷b|qK^50C!/ Q{s *Waʍy1u ,j7?~<jUU?ã$*U٨#$< UUyTzaF' QG ;S>[RϭBnoT̝TA =ĄHnLHli,R۞: t 8z΀&pZP]V+`ƕ`#p"^EcِwWM}rW9޳zר%TSaDO>换KT(ؖۢ1uqogW mVO)RHĭh fVB[i˭b&Ǐ`BATtً&cL8sɨZgZؓҖgR}>QO|;׬P"na2p"0ޛOWUTu6&MoEGW<[3lC(&^}uyxU;ҽZ5GNHЩq;t{sSuACϱx}ִ*0J˻>_nBA%.6.٨'cF5vpөzʭ <x]@IٗITM~9p$lzOj#O̾SܟWӷ37%Dw9]owe#"[${hmOզZEUUըT$޳ "zx|3~ zcWVUnҷc9,Eqy]dYFQ}[9z;Vכ4vێؖdBBuYsQ&fQF 9WhO?_)e;Gf b XFmZ$#u1.ʤKX-tCH6+\w wo ɂ&L;ि鮳t(Vǩ'F.^{xlNt*I0uBҨHʍfRJe+e$xe)9P* mo| q騩$%קc3!Wm'O ){GѲ٫cΰcHyRL Re(H+Yi!?ɪm!葹&ڵ/]L ˆ iP'NA9Jf3}&~fڍtKP+ 2yɏ72vlsTʪ|ORIYI;L/dMe[̖O^e}QP30a(6uR?,NJr@ wG9Ԋ.=JO#nW~/ӪؽT7m:Z#%bq&?.ERWH\ 4ԲoS?c̶]͛;8s۵eYRɻZ39eLJcs:N\_:4#Jn=}鐎݌kROc2ޔFǭsI vW>=H*,k؟FwJ41挅bYr=N(xaZ˾QF;IBcg2Toޓh@V4fQ>u:l8n]n&FVݶ=e]22kͨ>i>,%j#4FXڻ]kE;$_Spom{D} ūmlUG d &i}{*Gtr/z(-4Djei #&j&e[A߳`b^_NB8} n% 4IYI 9k'|sN3 [$'ur久:6p/,3Υ/Πoti3_=cG+.Oh*%/CmߊC̨k$;} 3ĉHlS2dYgZ+ V2=֍<}_ƦF:z8l .RE:&o⋿ 8p8q" gR,酻JB9Ė2ڸ8r8)@~‰THNKrįJt\ۼ]VƆmQƽi_D,:O(;H%#7rOI~EˁpEfeMM#ݲT??D +ZP:Ѻ[=ө{l,?ىԗ[ְ;BF2ī#hm`mp4eTHP\ ע*IC+.Y0u @o⋟ |NĽ܌AOԮ kR zw4v˲!aXȬ 9c(>*;}CT 1W8w=.y ݬц e>|WdSѴ?FLtЗ|0r.? ,"ǷϱoҪdGNdkBF6(r _qϵȲW,9P %:o[C`92& 2VYIO?|RԿHsSBb=0~):ʜ&VH~X B $t9p#N7&v(BU-ќ;xGEV-(`2ѳ䮵l9U5ceeem\(W6٨\j}3G{?;_(x:YFy]dY&>`e Z=i@և,\3s;qDXݏG1fCl_őqR뵖w^2ƿ{Ww0W< <^Fֿ:.iS߻}Z쏛fbxF([6sI4:YFi怓U`bd8zMl<mRo WѢAQ+5ɞ_ n-K'yg`7t3(ЇɛrlA-)X[gfP|7YB珚`LF3ĜaA'?Jz^0E}<3".5oG`,,Fde|MH6E.nx,Ky6 U-2'%KzxIb$ ~x*'&AE]-vJY3Ϥ;TFE[f\1?D|־2Ŋ^5c7]'S|arC䝣tm7J]N9u;b`bdq$^1K{lu?|I;d1*gc,͐B):xI+lNIYFMˡRx8)<2%JQX)gLhPV:1~[H ތBҼpvC%<6΄qw&j-[(]pPzעFa%ğz>. L % _LQ?sBî|"F+ cY#8|8-*ySXqӺt'׎;ԣť`[3Bk 6>H]Ff,:ߢvؗaܡ3{GA"bږ*IIlG#Z`CV3O'iTs6r򐗻/v.Jݿ"}%KE!SNL$=O~GYxcL(L^_'c3`JOf?%UwExt*}A~ר.p,Kݟٵa] kZMFae#N[ʃbLZUY԰B!`JxlKΒwr%Bz{PTuD#ddٜzCFөA}I2ƘO*MYNȸ~'/r-#EK7`#Fo9C|^M"= bom~yMz/i ӄ nNc_#x^ڨ^J-W~Sq)ӎ6 dYŁlCC#? LuvCJNJJΝqyL$=N㻠1Y9i:etA :4fiʟn 7 lhnҊս2U"r(בkz'2?lԯƙN_/d*1eOX LFb۞ޤsGf:.ؖ~.Ys8jkC;,Y*Z_Z-z4fAf)Т*pnޖ*FIHg؏LUIK*S=(8X[[SZ5VJժUR˫rTT+RbE*TI7d݄PJnajWX¢$l1pzյ09ǟӵ ֓]LO8_&ҹvX>}ދJȀ™.1ke[|޵(t˜VGW#"$+T~RX*5]PȪ2bD>D22U@2buc;&q~ыXK!(ꏟ٦. Bgo@1bd@߆Φh#Pq&yS?O]b S,cV0`ƭאN&?{ #\MYl[;vcIiq*mhhg tGJdž4+ohY dd-ȯwվHԞg#pFIX&a˄ڋAk}xHDEIyէ[Y;pTʗa˗2NF{cʞ8Yu2jsGH;ٽl]RR3U[ҭv1Rq>KZA]؏f#~bΤqrhڤaDjЯli3s)cZhKhHfH#승`]1&6Eޚq`Z58arse&@Ӈ6fB}CQ2IMOdDdGeATVu-FBCɋ0y^]ZRΖ)AVcA瞕c (ѥQQ:P[\6&0֞/ih7%@${^BS=MvԷT ǞdK9 h݋+7Xz[SmjHBWbj".dZG*e> r0"DS (J;E|ڵ 5֠ATUj:G]eTPn^H7gۧ3+ꙸY捌d2DDcYw4آv!hY=n63%9ԶgOS͍"VGܻ{F8xT,Hd礬MntZcIPf2M@KUʍ]`ZqA?D;նچ modݻwSR\ /cttJ!cv{t#^ؒJパO!Ϗ7Bxxxو5iOLjlkdSd@ӡT*?8rŪ-?-Tp!oʚgt_ws՗ܣ5x+2z'k n S9Eg"fy?>jM_QVzhLbP1ҡbK2 >7-ڷ^ BG,)+;<ժU{9yt"-]z+ iР[Kw^i&:vXd5k@Ӧ -G$<EL~d^ sh#i(Q:sMsۂ B @ X&"w tlZ wƜo1P!y$Fv坐ѻb'^wߟ)sO?8r~HxP*ǿ1]qO#kt0]ƑS}-89C_wCF9""/o?SDy.Bo7.oEUq9 ]|k$ IDAT@m/:a߸Lez7f!8 =$[``ی>_÷{л(>}>H@}(E">B6#d7f!]F }$[6mؼy3:tx7:t*Ud3D9AvC_AF""C'Dy.Bo!}L_!üA5m'C_@ @ @ d@ @ @ [demF/@ @ @ M @ @ @ &bM @ @ @ &H@ @ @  l@ @ @ A6;r ݉ƿM"sYq[M#`in9Vվ&(k!9'}JW~[hhcȗF^"@ ee-zBN,  B qDXxAs?3vƘ՜ `H^&5;q\2n)ngM {La4w"4+cÏuǡtb`ũ,_Ӥ]Gj|ˍӓS&f4kN̕ul䍽 + B "O-lL1qey[9kۚ`TnܗټnG%q+f&>V)(* ځ.VL%E'>l`n_T84Tqs!w@s"5ô-WHj^͌: dzvO lYfO~صvoC0gr`|݉7sXKw+uMtUL`|g:ѡ< 7음 X w.v>n҇V$*8u;ئ:ul`ヘ3뼄=J֞XDN?L]^?y+k)L_qqx:66]V$.ƨp~$fuuPyqL@,ۘ2/M-8i8rd W>\> Š&IyIm,!LTW~<gz›SIxOwa-s(&fICn` P4S:K&2蹹-'\ΔZۦg+p< 5̀۲?uͱ831c[8[?tA!?Fu_fH[N-ofYrЎg#0t\,h>5f3~I|ݢ1XÏn'6qĿ ø>;LYrLfo;EϻǬB=1!9QS|, BGL Ȍ|̀dF5Y1z懝{L Oɸݑ!5fͦwML5hLpL/!@ok'ّQ;Ń#*jgFmkuzxgoi5xVdaxPS*TRM:0`Zjeà@9Ut0K u6 _Aa^^T T:5jԦA. #+h~k:ОYv4oڊ.'IuUkסN:ԩ凣Rk%j1ܫUƑ{ㆢWMgP4mё~Ƿ}} 3kdBtQx6©ǰVZᗋ \:̓(a< p.]8RԼ tLp*U *'Ke?cLz%ŸVHr,7Pdd6ncEc:9]={1'3{Nڽ)M{Lgʹͪl#ȄAɯc6 G0+^5jRQkzoG]6 c]ȫ⯌jUfڹSN&n &ZÎ>($ I2͚LG-1NU噕1.2($h6nU}-b9:/S㓝@vA'f& 1-DqFRwn.K9ciFc_1lߙ9o.>V]rHLGvc8p g p9>OpP s="$N!(k^0or>BfAfIwdIձMZ]il22w.vmYJ7>xRq Ξ=i95Hi=9wq^ݘOFղ }Sq[onHQƒiPӊ'fKteJ\t({nUk9񺞞žXꎲ#+cי={ aqY"-2eĒUGuV/SCFyڐ?ӳA%6?aHߙ/ 9X&Y}tVKqvVkڋul+_4gez^{/;wb/Za{رr M ʍo;9ytȂ.(xȏ2~'>ټ?EOi)I +{fZ(ldL |4u9g|?υ{~ !tQY͜q\~,cKi_7Oz<Oq$,Bʵ~~\ Uڰ뜹U JlDOra })X;<bKՁ[6\e[3ɇ<]ǧº/KkSRngJ&WL:t}5?,<YДE^C%LKgt]CI(%= ݅YBK֯U-6}Y"="u6 ~iU.J'a[~}VjӱW_}؁*ί$)_;K0nO~__k)$ͧU-އQzE۰`PJ8Wv! =@15qr#8bR^L)Q̤!$<.J)P G4ả)tfU0?G oXNV@^ 9LmN~FG7T2GrWU==l y@lc389H=!Vp$GgԱ0UAZeQI(")&|'O,#&M֪v `ǞϫHWWQ6ܰ#G^ y){o"xi%w*_ǍU8`_\suV&zY?R%G̚@5,[C!m]( Ӂn֝+gMf*&T;e{ ;mv4 fs]Bs'xmӿ`6ʀw/T[P#  gF]ֻIf'3)2vJ>pydۉ~X$a0=tkθM7kkvh[=A6tto6d2i[iڔ|2.7AWz39lG$K7Iȓ;2'/ww)m1661E^X-]ka "2"F/ RA9Wq2sNސ-C EOWWS9y"\%Ր,lam_bXY;SkIcyۮ-h;9C7Erq#"#Y`mB*-P~KS@&&&HˀEd퓋88p ˡ:X !#d&[ɖ"szMOV'Re=Ki<\ݏVc`B/e\^j4eƵԫ]yPd(ai'GAH`>FrpA9!cIf8@>t=/B䀓{'oG P ;+qs2V*dzP`ھ$-$ɌŽ`biYŤSAǀ ? wMÄ>Phۓ%6Cn\ +j 罁G%mgn"t1PZR»8N]E\ RRHaY}(],tjL D0or>BfAoK~音>4$D*JyJ8xnxԓr):2zx+ůBY\Rb-LI$F*PzkB؄bO'0 ݑnaUEF8o2WCђ V"Wn ˶ 9}gNԩS:IU4Xϡ KBK+P[T7~dDN7L'=T,'WOȅK^L h2;VZ0~GFlIE~HXUzԨQxy2<;FͩuLQ*U]9oneM*(55*atm/{o&9d5:)(Tb J2T2 I;Rxq'ui㄄ zS,䉷˫Vg>88bʅHL415AFxnHTwOoo"RLErxt6=s{d,'ӵcq"#ir/OY0sDK@V2IΙLkNޒ- cٽuS˕aypuȷgb 5}+Pڷ,}uS ߒk3(̋]„y17H$YQ"]"1Ί)bn&bj2V~2\@|M~v{A3n +MhFTQCutn~hct?|p(C٢,=ՖqxvR=)̞_a5`Wi3'h*E%Z, 9gV<;b^uÆ~0{1nǂ7^D<žOy'J*kS+xC;Xdlh ܕ=@z)b˧[W~ܳxz[9KаziYw_gv- (-iҪvh`ٜ c˔8U/1vh?zOkƌޟb?g1wx; -!*]JnDQ"H) E)"ғ@z w%H@\vgߙ۝=REޡOtԙ}ݠ8[@G3xv(M{ IDAT!,gV\(G&QK_ZL̀'T} 7Ωt\u>,M(lMֺzb,GXY X\B7ḵ0ReL7ahjc%( IcFus!龡 Vpi]=w]NȽzԘ}*Q{X':{5G.À%:܃ 6)k"e_ܳud'փ+[:%/u!a`W2ջ 䍩oҷmL_pn~<#%?arKw4$pѫ|K[IJ33g0'e ^z|N~R|Ed䣠UhΏ틧lr?+Z9#^pᇡ Fq=}p*?=a֐fLGYe-y`X9MFkpͷj^tJ5xƪ~a$oϹѿEtZR9G5w:NlFSd/zWCc炧OiLjߐφb ]ݒ{eѝB|f~^& 6vx,AVc^Iun8%x״S|GK^K93C^-o/ίE|J9JVހ%ތb׹y7p~?hftv}\4at+@=5o0m8le֙:n2#8xQJF'Un80:CJ/aZQdVQ*q]kʾҋ#':wa=M~!Xo`覸it|;F>5'%KlE;bM4xzsq~`wۿ dm[!AЁc.Z,mFSK}tn?c=ޘFǟju+Whi; v'0ghl94NN*MɄy}UbXOYcΫUi̤P^=iiPoJ@ڮ'uِ8\k֬ P \ЯI;BGd7ɪY=c#fr]J? ï&oʹupM_*41} 3M>/sҫ }<>O&i6E`FK^j:IEX )GcO"L7m)YͩS?qsfΞ-o?ƽeK9} 'bBލByì!q8IoɬڪULuVm۶ʹ>D]|o46kimFjr:#:H><,#caގ^Cϝ{^i eZA"D3sI%{KM/Tu_Ej|XꤖG 7SF)rpz0kH\3NburkJ51>^c|H~f.gxNb$Nb5$'1:۶m$lE !B!B!BM6!B!B!B!2(Go_S!B!B!BDf !B!B!BArM!B!B!B ɝrB!B!B!EѤћlB!B!B!"DDDn:t:]>ITIrt\ 1[n{FnIdS+2|F(7 +'6gGڄ"Pyg 1r8:kQ ʚa2yo񳜝LrtٚR<~ES-zHŶ}z'!!!wRz8[0-9GI 5$f/:GGG(QD^ŋXb9lɦԑByj0j_:/uv3j['2{׮dn8kd_n:Mv4grZ8v ?,ܕB[=E;n2{f^"g[9.z^v-N 4J7oZ'z;O*oXn{y&yQDf$}D#Cpli[@>\ȼ^+{2@bl_#LO"Ŭe2*Q/Vz=*Q1 ق5f kĪx:Vx.+ɠCբs+ ;ɬ+1zQxQ|ykWE1j|n/'>}U-z~hggpPR¿X|0mZڳ8.piN[|mjf]p/7fw{!6Z-:=EyE/^# 2n{ /{0Ɨ'r.k/G.wS G[_l_y"zbڧ#v6h@)gz#N^Tm)A"w3Pm4Ǭv榁Tu$; ׏k2s蘗uHmwJ1o)ˡ;O݅>6,x; џ떥5;Xn lkK4鳀ɾk9Ϗއ·|Sb3SO6ZT:P3v^x/O<\ [H`s4~C~|T4ye1f2[O (!a+ʺbijmuPWy֟M{cYsw).ALX* ֘'dDK}m  ʜ}iۃ no%eLjH^[Kb6.G/9`>4N]Ld`El3=/C6Jt-D3ႜz~1EH~fk!e1 hagAnq,,Gh_t[;;l |G5I 5$fBdX^%JyH QׯWcMjc3ك|p-\5\gm PW.sCOD^'b;ʾ6DKy<2sql[v}G(Ў^, 7 d0q\!1kص7u~sDZrǔ0s ڒCĹ4ŝ|^RKz4熒5AXztM_92s6,U>;:.0&|5dDiOy][qqOښ{~o~`ű3ltg練|s.W\!Z uþW[L];P7QX_n(65&ϥ|QG:)柊"ZqR늟]Dl`pЀ a (0gTQ-|53흋򉕝&2ZGW1m gU3.hrzr!^LE|R2񤘁; +}ѩx;@&4̯)@ :; }s5'=E;bT qEݽɅD395M蚸lOI_ӹ;VO/6m=4އCq{m6UY1h%L7UNCזn⠱ E]=fÅEqwoaǭxlsmX,QGX5u$_Y &9cn귋I~fk!e1Xz= \Kb5$'1" R&)^k֬QSAAAaj^K[e:kNE`zRJ)WiƟAq\+PyEݠ"R|RiTy>XRcT5= ^'4^)-_ Τ%))WgFF;~qLaaa;>SUCu^y//mP= U_W?76*ciCIP;SzǂDѼhT*שf:=2ضV<=T1QS)oBm|mU9}|ntVf]VIK3('IJ9y r]|ͨFcb\utQ}T|*_5j:]fuQƗSG3LhreRj=k&%1)CMIRʢ"f7Sƺj7 S+;jG h"ӑoUj3T)E5*ebL*.>wͧ 'u.CTYR9&Cos*<–Vⷩ~EuRtxjtVF()Gc55A!WJ R;sfcz`:0LU0wV$O׎*YUMU%'S=?־.1zT/w{@Mhtyڪο;ΎϚWR#@uۿGlshw,fP[`RGFUUF8)\dT>ޘTGƪ+ guݢ,.6}}+{gN#1!hd|0iZ.Xz{JI9pq}aZbb-+Ͱ]RNWlm>R8t^B]-Mv%dZ2e-crgBnGIizМ_ٖae"nMYqj !SW/ ޯ֧e7!;`-G'E/eƋ4+=8:q[L$: dUW" O@o =6p36lg*W>%RV,j ]`]c[H`OQÅ: ;˖;W32KlWkv1jSл&tĤV:?ڮ4 {[+,\^ Lߖ6fj +{z|E#uGmKlbb3,kv8G?Pf9} Hb3 I{߷H5ytʟX5-;1}CQZ&oYm_Het<ƼNp<MHt˻/R;qGX$?3eYw5-ɩ݋"tr.A/jEX)ƭ0<~sѩ}/ERC\K\4%)S % Í`*ӭՏeRHԙC  ì!q8Ofx:Tj);3$rvH=@7g'm7R.n¯ATI2۶R# ۦk taڜtC*AwiQ3)㞜 wشOnE3^֣є6ˁ\Y_+CN;ӻ;]{uN؋BO@YX輨t)D\I_nhMXб3:|4nM nG4ؕj-c^pBY g;E_* l?|e4KuԳ´ (1|![&59tizSKR),xz!QEhU*RX*%u,>r˖&OL>EKSɖ8GFR)me#Ta\RJ<*oƍ݁ozb}XNKik ;Q4z-d'օ_R9?.Y/b%30mg6h>S"?PQ9wSw'b a_OF;+{{WM<_!$qMb-c2Nb5$'1Ire2<$bX4JuVGY2/+㏛besE_ =KnݪRBN%3Xڷ&Y -@¶$D\v&L:{w^vlY9:M fţ+-ÛQkzۮ'7LLz Ejc͕1oJ][̀h9 j89K &mcӟ3[j^iϧǝ+bqcB]|lRΩi'#s#JFIg:/#ݨU҃hɧ": IDAT±[=(>:X3n~\ps&-iC@1E&,8`8zÌ#>Yb5$'1"59:-CE&'c?TLllHR2+- Q? oۛKm1RQ|ԙowulN;Q,`uK nwTm6`6g"'Cdkh|(V4e\ /t\f$kWnَ%r9U*7 01?"qiFi)XMx޽ЪxH=OodK$)n?‹IZLh/ >r6օ+Ste6*bqb9m/ʉai%jM#ZUy&e],UK=bni  S\@A-k5Aٿ6]'޿_cvm+ JlKBar4_k~riJ+f:o4|5r+n%1jvWw*/|si Qj4;>oG٠JmgO"%AaM,߅B3I@^o ĤG Z $azUZ*GeiX˙c#U1j8qVXK~{w^YjZMa(FHrGuR]?V7L%=l/S݄KzX@86oqfЪ'`KFZk% 5kբVZԪQY3x6lJ'.]v̴SP?xΥ6jSVU'6HQ߁p h\ Oӎw?sҭA{9Tŋ*VTGۧ>yyb>N+O&yfɻ(FozQ4Ǎdm:*&o+H1ߑUl+Qp&@dˆX*P.:5NzB](mvdEdi;3Lu,Hq?;n>Ь\#Fͭ,eAmz{4;{퓽lV{H 5$fBCȬ3k=tkX6Z߽Nmgơ-dL Ivˌi4E+~}!xyۗLe&[jOhK1芼C83CmRKmAkqjd9Fux=e5LY ?u]O\΄_NPS*g4knbI r2~_|VJ璟.PWvРs-@QO Dy8W3>_F0?}۔OTqp8'cL~`ryoUcԵ?ϟpٿ2E{~Ox6 Xk=xǿ!3z(7_כKxS13j;,{&_Hx}$Ɩl[f]-*[,'S׷v܆J}+=mCt>#G`1TGt)enQ-=^m>M3'8NJEЦ\>;gvyS$bxK ѼtҗS;3}D;xs*Wij-i=izSh`+99~#nxzheѦ;%*0}C'g9-x=s,T4MڸG?5qs?0y9J~'CҘjxedoh9CZW]{+Grc5oJ։Nn#^͑ 0`f?MJ#xX뷋'.oBb`(~9]|tqӛ}MwEKz3g+7#0v7nV\'1׌ (+L9O +'Vh B܋ռSIvXՏ~?970WN Z]S50}0zH&.Cljhy*ŐQo{h\)MITٰV֟[},snN;(;ե0u¶ҩXuO7]M2Bg#?*B_'ӳ]9doB't| ,WY4޴[}>aTf—*'r\+$;pr뽉c+YppQ,yq67'fh gG*DpOw`e^o57rŭ3;udGbqH&\=O&ݕ4auՇ^ô0>~ȬT471v} F|AORu|V{(B ߜMqӀv'|Rk*OJ^C-LoxdŴv& Wh#[3c18q- ;=v^ki3c>c>τN4aPwoyD@zY9]ϯr eh>b9&-}s5wxvĚhԟ`7^S;0g0\ŵ@YڌlMT/hɗVܘ{1(e?V xv,:OF7a?ϐ0 :(>s.h1T8 =)\. IǜWӘI!4zCױ87xrd]?NN!qYޡ@bN_wMv6l a-,f=3޴ŧdY4NqAΙu:{P0"-m+ ? {7 sTK 5$fB<$%FPPPhV2m֭Zj?m6ӶuEE\۬KٶmժU˜+ҬLIJwrn]2܉&4vVҎ<~ }^z;zqd'<+Qi eZA g~Kܧ%e11UD}QZ>ּ:Q Tzì!q8Yɭm.}, pׯũ~K.]v5i}j|1upAŵb KpӛB+W0pgQ݊$>&jb&n3Rzt!Dn=4&ӑ Bl"gʤ"̧pU΁]Ϋau,!q8O9\Z!B!B!B%7لB!B!B!ȠɖE !B!B!B=r&l !B!B!BTr&d !B!B!BT4\VZeڎ׭[Gtt4ϴ !B!B!Bjժt2lcgg[,, ԯ_?S.]Jvj[M6'U-[dٲeem۶I,Drg$ދՋ#9E8gxf/w$6Ob$YCqm۶Nf9\B!B!B!dB!B!B!"r&R*'w/B!B!B!SlB!B!B!BddB!B!B!"dH!B!B!B!2Hf !B!B!BA2ile»%!S"";Hv<`фE9\qttĵ(s4A飮fX'Lޛj,g'=dc|&$!_&*r;c9ۚ|1qs$ע3o bō슰[#NBBB !t9XYm֤l=uKNQì!q8O+dS\ H<5/?MV5-/wJ_fJə,S#!gV%rP_weeLgӿii| <88gkA;r;©"D0+&&#|^nBe_ W7mغG^< 8}3~|'3O)"I!DfrL}DV)wX߻8>gKl|j/Ÿڣ[SR%*^}N{ +Ubk/1׌ t&]Vv+AVEoW@%vYaWcX:8h1cԀ<>lѕ_YaOr)h4_?2) k no@oN8ONu 6#F[W Viϰ/2 *y`>2^II%|&.e}psħ\kăAckHSL9{^cVVg=?%k׸I^*Ff(f_v ؓT`74gNOh\g[[\IYKqs@oCh1tmX|ζRnw~h;Wbx☷{Н'Bi6,x; џ떥5;Xғ#·|SrY=t-BujAg(Z_xK책iX<v8|N㷒|ೊˌnup={rP [a|y"p*23܎X'~pÐAe]1h6:%o,;QoD &u,MAz tf"+7v.x&g{l7^+_7;=<VmNi)?:rӋZʉ"g9uxnqعr!:g}v{![bUgշ(/ŔkdD 7ŊQ۞ W+lDSMt1:W`ٱ'uh3E)^4?.Vx3IEX )Y@; ucal?rG l akVb5$'1"r*QEZ~hR;Mw~vΠWkRw8kf`"tBZFmZ~%oY I1d#.F7kĠ.49>q >k;+K4{ӻM{wmbVLPw.q4~Utܻ}c1vt{[ᦁhv~˼183 i/O3 EdjU{2ǶbweL_ +XښcS׆{ 9#]_7A%6 [WycS;nEZI'ltp}R=F1 C&՘δw.ή o]^C=Ǐ! }A6= xRiq^g͊aKm fwWn^S?wЪk,єJoT[ӷMc=;zLݳl'FʊA+{h-\4`ڿr|ew>ҬtX8-Ѥ,Y?0~pQܹ[q+\y/KVMWcp¯IN(OrЧ(s1nH|ϛ egOppOԖ<"e.w1@ӊ7zYYGdosd~|s4Odch[s+?XQpTOB?˄M6oBb{( zvFr0kH\3Nb&DfL&S֬YݻâռPu֜mm*OXR*.XK?u&uyXչV+)ZU5AE*WӨ|F)LǨjz$tOhR[j*+?{1*7I-KRRPvXRIUURKe52Jk+KjV̐=*{涶ůSB a UҐO}!.c7Mj{BW>Wa/T}VT|QwR*o۠J}4&ڸޏ?=WϯSTPWK-)Sur1)S=O$)}-26I]N5CKjreRj=k&%1)CMIRʢ"f7Sƺj1 S+;j敔c:b_]9nN|QmtY.R@:">D)l͹ʑT/Zy)cUx|jI#fTzފ:R)e:b^FhQ7g7SF{IlqJ)SQɨ |Q%X]w}[ܘ=^?FjǠ`W^ piT[m>!wWMv̆ZjxuzBmeH>{jUVf<~IcI럈cWJegtP>eNlU*4&*S]ΣڻYpϟǏѬOk*S_/^>+mT}Ԫ |OVFԿ+7=u' ӁoTyjrCF,\lJ୪}C?zMMR jQ~yFٺ'm7Mt+658*QuT_(ӁE lQzt~j:9[>@y;*Q9_.']٤Fw\mWo\G1?]N^dKOW zwG~3ĩ+RQylkck1U9_'e1(B+GMJc@1e,1P{N\S+7R*W{1UCk*/G18۪뮨Ɋ]r2SS/fMߒ^%+j IDATiIY|Ԣ"OnW[\PtMutfm>cVuʽ+-jd=Sr|weKڱ_nXb.=mjjK6PYԽ*4x ٶ[PqXl9x=!H 5$fY'֭S'OT.]J… ܹsj͚5zZ^AAA g9:nTedT |E{,)|$&d aO&-]3y}Bs/]i=i7}74Nx/?,ZKULleefeKoY16=S%+@SaPc<Ć) R0&4&/71郶_ەPyząf3dogâ8һ`A`cF$ؒ&5DD+{"n?E r<>wݝ]G>GVSդtEۚ*pYM'^f- @[#K*Aht*;lScĹZ5pBp/HE}{_g?3[<=F+E> |:oa6!~_z51盭|퍥޷ٶ{;O=J|<~k*^FkCQ/na5LnKw]$rl" {Xcl sy !X>ϧs uΖG-T[$#W+MEK/fLFw;Ʌ{xWQKp'0H!ֽ*W|i{WԪfփGD8k~ͅ#9t3&`dR5vn֦LE(p- cU,{\?on`*_JD-clzl@Uw [ ĵ^} 7GHg%[ -yZq'W1c|)ʵjsW̥?fC.I3s̹߯dV@U x{[=¼(^)&xھ*"/p!RI>XWFZP5jhERSUvsU#Z([Z;b#Dz&燾uUpqT2PQ^{jlLyBi4g [eNZFg00`0P9Rzg(0-ۄv~HvDD66truWB8]A[9 ߔekZMN]35Zii}h9?5)uc#\S)G㷛QC 4om qϮh~緣{,md`rfH޵ 6On*2my"?/ UG%b{L&]gJz+)7)]V͛ֈ'gu+:uHYij؜bU<]-zK^zc@JecNpI+ppOyLJc!ܺ2vu$ ;{lM[[qQG'FcHޭ0qwLn@#\c#s2C5d$)3ZeIg8{2Ցm{LUKboKҽY@|\QUX ˇ1nGhܛ@{"ןѺwG-A-,Zñ<##88ٹj2LVn~l'R |I:M% \bF\{8>ɞ[n9#7,g{#"0rE:LzNaӎ遇ػi*gp.q5߉h6jFcJ))~l~|6c8Ir @0 VaT۟3{L w/e4Oj39 wXp:wba$/B;vvaԒY7ԟ=hO]p2A2#HRM(kHZ5Ն` ,WFmK,G6x:Cdxd!Oȉ$ޞOk{SLMM155@NT&B!)Sd|aE|h;4&~P^:Ɖ/)7B0q{ rnfFGc)2hv&d0Gof.nF!$JU"si*۵[g<R#v4x83UIzCU>4I%R**keOFյt8Y4sϖ'Bof0~7Ty/m-C4ufb8S\7ޒ7dٙ乘;Zx28ĘX٦IS`gL.)033CDcbE#p_HL Y0ȸ朌$e@hH9|Zo94KN@.)H-X/FoTy`ğ rRE<3&8XI󝈏&<O=N OT|gS ֖cc*e9S5Vƥs4J.N84 z"prI os1”e(_&ͱ7)JS X݋°X=i53D;xvZd N#{1no”O Dt>kC5w JzנQgB=:3eIzAgrB!}V\_jSlר҂b5㧙Ys kAV؇D [%*(l 5 ;WͺaC# 氍rӥkBAWrX'f5|RbrvIEhx|p]ygL8FK$!.EU^ܟ3Jz *6hgӼq1\c'=_uKB(/|Yk#*Ӂ"-EbE;>ǛSѾ3k{6QzKޑeg^bQV$/sɺXRT.a|t Y%c?d\sNLUxj[!&etݜ&&RBYW 403njLSK]w7x¬AI11ġD ԯc8 vDP^ƣJ!RFO;I^[.G*\QY+M/OYy5l l:ɳ9Lpp0'vM-vgqh̀콴B]77lW R4hv ғL{ X$z?{7 jary7^=u}zuQbU f{)B8JZ+=C)G '!\kC.(0û\ UYʕ+ϫȺ|4srF2n!P(FD3s3DCb`sG9b7h_&;?lJ%cbR$f$INU(9AjNxA3 @]jj_rO/7()ZIIIЉ{صiL5oo|j |<#<#<,I973aLki!a#>24;N9 b9iL5Fx;2C5d$eF;Mۉ-q~ +:ѧ>J579MQ1`I AF>"Q{[wp<}LRy$ (ń%xoWc`_n}~,x9O{CeS Bʶe݋fITt,.ϰUSbGع\y;1k޿7ߣ|7WAb/uAYG:IS8[37J,toC2b8Esn6WGWDaPF4-s!/>k7괞lu5?1~NI|Z)H[wH|C+&~%c`}j&Ö';/>jPNS`t/ƚOw`u~@#x|FCC `vi,N7gba݋?"vx7jhyY: ,Gmq^C(5#^z.ylҎ" a*tOes0vzUVQr;̆6caBƿc[€AS \;nRMbDqɘ|3߷˹ҩ']8L@\3&nɰi8=&IKn=T?k^FrDrV„O87BV^dB<̑~f3}) vKB74YSVs޵#3Mv.gќ,sJy;$5u3RkatsuM]/sKރޱ> ;=G'ݻ!=w">Ui|Ze ?}?cBVllY=gUb3Iy\AL[o} e?6JC!-bS`^,nA\NM1JڅsYLűxx*:k܋ۢ$;33O9(l@4E\+2129'c&I/2f%֎hf3Qj-s-C10q a 6ĽeTJPQȹpqok5s%>ކf`.MPPś%S95~c I[LZ&19xkۂJ6 d̦U(c|>QcW&nwI{¢(nҏ:j3kJflџ&)-M|FnU"F~;Kbjk~cdK?iwЊ㯲8Z?-ت`jH h6r>ievUyfq51*X=Q9Ut's423:QcF r~C C0<"1e&goEcpLVL{,Jy;ϬnB>'Aݪa" b<ߦNbDjTn2?|E7Z6᧭KˈVmRcϭSRV"Ga<7_}@ZVc]>%J[t9ً迌1tjZ7T믉4NmٲjƤ- Q fXm™s53~ ?Jwf嗎8.F(G’"NY 8Wn uQ΢*ƎOWXmtЉ6()Uhݒk+0ŝ:(tN峟ߢ_q ~Ę" F#N4(ͿǨ>}SN䓩kPڧ[/:3FܜR8dFj#s֌HBnM5h2b gԌOh3&φ`w/|^SrIʉ:˓q S/`C?1^JKc{]OE۹YC.w&w"Ui&/)Uk.S<y1 KGJVi[V?SR)LXQOC4}{:;׻2j"vhakn~ևQb:Ń119`PJ7FȳdM+X^H;=2rH `d33Yo<_9ʔމi>4ťBeWrinUZri\3%j2T6ڍ0Zs;Jm1͘aq93IzNvzW>бc<Ν;S/].1]~oWVqgQ[^i\:D:u :FwAI9>&e==޹ `!miuw7 񿡰caTB!X}^L;Gp í@[^o7–N\s ]=GŨUQOmzԫSf2C5dOa-wڅ;fff00 ={??|N~zvZ8ovH<'4T,:s[3I$8rD$p/, +,qxz#I,$$I2n Z-yG>`{ wf"ɂY""}M#lg" y.'11BF2&t$ x[0ȸ朌$.igo#FoAr2oƵ3 A$I2BI žFSu,Ǐpc.D@Y~ I.+ūwW*St IDAT }6WGmFXP*윒Xs1G ҍ>(W=Cy"q,Rɐaq93IʹH6%.qzS$IzsrDCIM*t=t2$I;L}y#SfTg t: HByOuZ4*]&SEd kɘIRng$I$I$I$I$I$I%I$I$I$I$I$I$IRC.R$I$I$I$I$I$I^}Ȧ r$I$I$I$I$I$I+M%I$I$I$I$I$I$)W]իWرcmxΝPx<[$I$I$I$I$I$IRASNA'!vڅ;fff``0pY9uڵk}`VUdClذPtСC2k'; C {U 1 M7Mm\SNїټ)?QG 7IOHxmw@ű1`jɧ[c8/L3/G̵Z,6er3H{:_E]i7z.#A 9h<.<'LgN+0y_vIn^*/ ;Yo<yq*UOWo١ħzu|

    0.N_ZF= g)jL,PrS>n]۾Ce,,q҉;X5t{fJSG3J%J ThЉlBlUmQւ"nhN{R\A| L!`ZVvbi5tu eƩvChήImQb֦X'~ƏO`?/bY"-,L6d%]̔άxq;)YĎ?O>s, וʅO-=N!M\m+W3ka(f"tVIk,(0\Nxn+ڢfXM);*"&yao'ɩX P,:ax:7_kK>12 "yzQmAFWDDTcj;ln0S2~==җkk53>: :uܿFTdwhsZ7k*[YS'.s-|vovbo&F0lpxlT] L-׻cn(]Y:@^F_yekObv>ΦJߚ+0|ZL֟/h6(G%2 ca߲'}6Gּ \7SZ^#9Hѫ}pEQ&n7'LœZ;?w[>6 НK* Z?,X&or{ޤ/M=ϘMYZ~D%bZh^F}MNsQXϖq dSDP,$O:=oFm=m8|UT6<[gOI+y(pkU)cO1;iCbr~3w{;@܌_2Jd.8b֙?[y|ğ/+]cҥ#똫eEFVS|~K7ݒHB@|Nk5T)m[F aq93I+V^-t:]m۶MʼnիWňL4qCcCqB&x$">I1ju"tdѫp0p)[K[D !pxHB.Mu4HSy0D-nB6vnDE!%)[&f5"/fNZ'=q|LՖw _$jm;+1du=6ƦDAұѢ;!cŚnVB5BNJBLMEYpM<BVTHJ'E)*HxkK;U²4qI'DbDi%.hd ~a>ߟr}:&HZaw12wOӂOĮ*,ůWB\FRcg 3b~Xѝ(j.^%\E>]]z?ƱH CK6[AπXIX7,&f,bxH /.7'BݹE-kqI0_FX?_H]CgQ-?ZKJGX6 BS ;2m&o_/.mŖ텂g2igpherŋ#֬2-~U]ˊOOQDf ĔԆB&}Jbk~L) b!RW4) L,K_A&eDQ1&R {a3rOa^qoQV[D>DgP v#{Y2BJv}o;kDQGӭo/^ܚZU*[J|m"<eAzeZbBbs4IUN-}n[xdxӔ=qTMS45,w6pu/^v)y",^JJ]5]zbΠ7g sw[wn׼pwZq@,0]tѮ6Q EQjX-鶮,/|zgkSQEwдU7[StU[6e&Ќo$&O.$3Ke"-W# \{/ OOtq>q(88,?O !Sir7]tJ=_@9yQ}hb'C}C"m3kLaq8$|\<''>2C5dOa-w)\"޽۷o7om۶s#c[zeY98UaCݑN[\/1$6`4-ZKfy$;\".M)un!!!/i@aEØj2-Cg|+x?Ƥ(+a/)ãDcó!k>T̩їc`HxݳXHM)t81IiuxWyߺ=~ UI/0I} Pn?/wj]V[>0*f9x]C捰NYF鋯{ND'ьfS{Шy-TWj p'E:A QD} x%2;制[|9k-l!^Af\0f Q[X!BWQ>ltal햁O4CcqȷQSc;NBNExaf$#<_F*;|4l=<}ڇN&s_(|+l;0+(9}XF@L[}ŷ2$2=o ۍ]ߺq~,ˇdfc\wuiw[M{etז78gB4#eTS9r1:L:κѴ|9ћr8e\cF FSI񾛸oۗ #4ZMs4hep:bj:.W<$͜r;ٵa.c;%0C4wÊoJ/ynXF [}Rl۽k}>{صl/M瀿H~嵁!{̗K}u%໮_\;NmߞϺ{^#qoz6GвwyUrI\Y!~OkcE`ojE2>\|ejT$9ndv$ +]G.:Ý !G[ T\^y=KuSgCٺQreʹ.Y;^ל1OBvXP%s[ii6ʏQ$@j~{JعU[3vtjW,káC00!"#xծieJ]b$"_>LcPp͙ zP`^KKnׇ.Nip*ꈈ%n$QEC١s ~Үj\9X=Gbek[勝"<_t,ٛM w`Ax֫wfO qV<D"/M✲x%E/sCF%VB{8.MGFAi)A[=?TB&qJc՜Ξgpn8|}*Ԙ[YMGuM\£3V,5dbᡄ ;*;9[N8) 'A<&,,+ik\kqIDSIfEqR&zV֛# Z| ;*@f[~;?@jO( [w?bS4kQ$箠ÙKzՇl]^*@_eίَƕTnJh;h|+G[ᛲ"õMbێOuEf>|Φ7ޒ2rDJecNp xt8W{;>l$ ֝K7,5lmEqa"{Hr1ݣC\x@sQ\ל1hUK×'HPʏ$ npQ.t1jyRJ=\d-i7לѳTὋk9"&}E=iC4a3IDSc4K~}S}qP1suuJ?6Z_z CǤUk9 ` vt |Pϙ=v' ;,8wQ1D]O0֌CZ{;;sjɬQwO.dx f{~m$-ŚjCp0g+\# JP UC@j(/×eҩo*EC/|rObI 8FOطtO~%OXx'1$t(IeSH27u338KgԵFD0'93Gpat3ZM!UߜO(DtKkVqܮ-ݚZ R#v4x83UIzCU>4I|4QM 4vsnΣO%.anRuqXoN%od~! y.掖"( {8%1&x5virH v 333II$ @<&]9?  }e kɘIRF hx|çuXKt ڂaKn&LeVO.,NY;kC 4߉h#DHlQw,p;A|ot{ FVC9\gvM]'\a{hxh=aP8ऄܷL~a2/؛zϩq_E?aXf񴚙l}"_M|m-C},^DMa7зsbaʧ]":b5PPһ%kШ3$VX3p9!z>z+[.AUW6kTiA 95tA@Cuܭ[UrHTBk+f԰PsFwORP5J|%ubV'*f*gQ8b IDATǧ ؕwDasQkġXJÉV8 ><4DqϠCPXZ:[z4g,ϔNlՁ4%߬*Osy~M-П@E~X)i,dKxq.kf*tX1?ޏό,UF ^Shs%pjNuMȴX$ ϴ2Ow%*#؃yp>EU^ܟ3Jz *Ck0INpzG,(4YWv0yʽH.cx-y')乘GT6+\.'T(@r1:ǻQj0+dJaq93IJWm0UKЭws^H] e\=p30:mMFguE֌Z(;'ϞTp07uuš5߳b AR wk\ܰ_-Kž4G3t^wj+U"!ԓ@s׸YB9P-oЀ 8;"XQĆ`W4*{7KL,11Ɩhb75Xb^ް^+TTyyv33TZ=lQKץ3U*T)ϑ/ע7MX,4xWnv|qD(U²budN%㽻8Z4*#\m]P0ųl1MK/S,?6).Qzh䌵*C( NHS3SDxj\q cF(#F.̍kL;m5>mSt4veB9/JDwPe]m'ȕwmh1Pv.'=iJպ50de:J`U--3wRv]OT>>_?-YgG@kVh*Vwy[˽ҝ@NPP:)gT*U2ٛWv&xU/ e|ɪ]ovmx&anZbf}P-RȼT(^ȔЛwKR[X%Ga@caIZ Q 3$Mì!~2f;M]о\?3Y1{"yy*ħ߶`l!ƈ^M|qX28lhNp FL.LQ^bWiU,p0Eމ.j5_fޖ WK~K명3A-/owq.‚ 8lڎz4ǃ3ШzsX0e }EZ&B^=Z9ndT^41Wm^jF<-,Kv2k׸A8Rv%JBEQk tgT/jcFǹrT LPeA8:M \Am7` @)W+ƾy#Y^&8G/pNl_ʌ9;3K|{ kL]6NXCx8jy~0MBVXUttǜ^)3%#8[K3R=z..ƘҔsEq}F3?(߶)k:\Ea]5§7ofCh.#=)ѻ&s.%t(~C`.lk0کtِѝqHcLXjO튷vOI|>CMR]A۱lr8 CH#xv8CL/?t0,J[gPSK_Āf_կ-]4D9GtbBUMTGide- Y <( ZeT{dMv8Xh0h.2Ae.-Ts-w(F2#4g3!龻#UbϯiYv<|:Ԁ}|ށ^ق{[1`QrԷ6c]d=n 3A’~ͨeҙic58/g55<X1tz39^UM./A]ф]ڙLkSQ2T&s;8r\[0o~wIvb[ϼzR6Ur=}ie\.nߗ^w +ډ`Z39E)WS)$Z,DߔQ|)ZU@N~D*fJp:FΥ|jctɢ2EZITޟ[Kl{8|̔8n4c2ՍMQZ92>M H˙ѣ ,SJuρbBylя :#S+ Bl߃GkveOGaY.?ezn-K% /;_=(L)X"ueiHO竘=p7!L)T6W g`ϪItpI$Wgҥ}mA5a:c0wh:*&h.1m &Pܧ&g۶KBf<'hlQ/&/ޤjmYqo8pmÖU@w5sGģsl;oob[z 0r<ݵ\;Ѽ[xƛ"=!;L@çZ v毿FSEϖem^/i>84]Uř66;to\t6OhA{QꑜDɌK{DgTi0vWBTڳp1<|&FC*V?Z5`ILdPYDSl]ܨ\yԀLA?d1MjK ~Kچzq@O2@:KF}r1 {eщo( pJc0k0BbA"em[(1eodi?NN!X]h?!_'ӮIul=h|dJtՎ|*eJӱW:5i7ZR*?Q<8z҄f}\*a{mjNF1uŽLuz/Yx$<$Pit&/$+W_#9@#ɮa\!&$z5Hs['}>&WM]+b9tcvaރg((Sݳ}f:%]QM{2%v.׾# B\)h0Jr@Mw|W?5Z]2eG#vSȼ(f-ZS^(7np9՘X9Pī0)#xyjy/Fc3[ \wc2ì!~2f̪V:ТELΝ;\2;vu%xuSޕ_؄l,rٝ #] CeirH=1sǒ9ڋK{{s"d $߆̪p !cV2bnvf/od^vL7q:"/A^=E)f 1:n޵knnnmx``0pRsFpppf%5QdLuѦMj6.qW2󔆒y3 ։9`$IrVH=B<'NOO?sB# '.cB$)gʵuO6)vSȼ8EFYM2mì!~2fR&:w:__,ɓ'9"2/f>qnXǻ 9I2G.>Ixǝ!"ì!~2fRf lg~Rɦ¥T[L$I>PeUt;Oٝ ILݩ>h"2/f>Q8vFjRf 12_pppf}e Ԓ$I$I$I$I$I$IRvBӷo_&O\0D$I$I$I$I$I$Iti}6`&O(t:铦%{5֙lQ$I$I$I$I$I$I> bĉ@s0~lNUdL6^$I$I$I$I$I$I@Zs,c!w2 ~Iʐl}&I$I$I$I$I$I$l67$I$I$I$I$I$IL%e$I$I$I$I$I$I$)C6I$I$I$I$I$I$IJ!* I$I$I$I$I$I$I\.R$I$I$I$I$I$ItY=MQ }fEJ$I$I$I$I$I$I},ݓu> :Ο0j|vD$IzD8GDgekBt XXX`Su ٚA3]?ԵK8. "|'&$5eu*0oYWX7Sug{AlzNL:[[;`";sjdE)d^̂y8p尢H| 8x`CvM2YC5d̤![Z>&xs :Ve4ޔN2#n_}3G/͜W$IR.zD<%`Dx@}sJa@NNQuAw{ybi]NnXKaklu?Q 7 ω}ovO>LlzKͥ%S3ccb)/v$|o^ +M@}$xqnɕGfНa٨l)Inm8،lyAi &|T {I[BŬ a>>xt,{Wzoآ 2YC5d$)crClVKc5* NE|gzڨ}!2WbI^bxCnڕysjK8mK# 6Tjڋbt;%jkT,`%eWw?QJBɍ2`ak\͌PT5fظxPqWF$ރXLJq07ԖkăD$M>3hQ%:=ā~H ',L])^5n{Yܿ `ĹDm>AQ BN]p 5JEJ_m_+ yJѠb?{Ǯsdڊonw9lۥkA̡?z+zѰr.$pk')Fa匓b^1WVЧ^qM*Pt dN3Ν=q&lhkNqџ֎N1H8Pݝ"m1? /j*& uA3u;&/Kp?u'f/V1RYt#f؎W弑5yS)s176 Lu^J} WHZt$<tW{6JOCVSټϟKGӲh[m;w~ؚj06wĽbz:O-U|5}Nxuص;s`{s5]KYRLQ⸻s_.Sۂo5-wrj<#n,3E.*c6-߃Rcksp>? -KSjhіnG=4s$^+bG:}i-p(T_dW(4ڸRJs/*TJ,`bjEr?G.f[T9>z-1&%If 12Yzf-3ٲ(7߲ؒrZu}+Ыl ^ˆձQk`{tP].ߜkɚiqEN [wQ~,F<{;PwUP+?̟}%8܌ү\9dv:E4 Mn0x(4DɌҏKӬ"* ܜ9M~Gp06 G|l<m\c1dHqD>틘թO`o2؃iT~(d̓s9ocyT9|Jwˢ 3@ђ[T'fy`Gadm W b:2a85ÿiE6ͼӝ=;>4i`cVXs,Zqq:MA<7 bW.4Լ'DrJ-XYl($nhϼtbQHAԀ~$zaqik˸N|bUM(D$jœ\F(h*GN%hUi^gj.) xWHuM:]7ƮhՙДNV'eAT1k{4jb?Q=%{s #9~>]=Cw!us\ImtMQwگtgܜ"5`)u!n?50UE6 ̶ձV@wf/e4z9Yx^i?fŐOmGg'c4;Byr?GC0ɵ'/%}?ӸlT߬c0_l-KX~ ɛ㏌~]Ԙ_'n̙{㕃~C%/s6LPav(=?̤grLL2v_8svGO a闶v{fQ}-2xԝ{;|A[Pꉻ>s`GI-x~0v≞wlœpY1O󐤏v# ySѺA8T(*FĆq!ck86yA2/f1A9gLa|̵d kɘIgoo`@Q Y=GRa0ϒLUWZ%t:]}-VZ%^)475$?6?UFDB!b"Ť/P'/:Uw1.EEB诋 մDBwuA@G]D9'!Tl_yQLh¶PUqjHLJ IDATT|:FdC%Ž$Н&jLDB!%ov&6Jnk)GW6yDhB9Wn^ѫbBâF|)!B1JXDޱ/R,zA[{b|\ 8hO )2:qiLEalX8 bn}T+R'0thf冟)mm]-5yy$'jean\MLBē~EՋ{3Y1QMwi`VEOB_,n*vY/$xKXXh!e\SL4NvrCĢNwߤT[ZXXh+_|A10.1HO(GhDEB ܆7IٜR,;߬GE9c[qSBAzX%Z H[{=#k1N.Lc6Xx~VLk/Ԏ$߳ M|0/"jv-N'>lhj~D4[Jh!  kxB٫uZB.>Z="!ƈ͝«}i) B MS3qt06-+JC803}'.A䘍I7$S!d~1VRFkK^;kEq⣢Tk.\_^:vM*ۈcbg턩F#L틋mǘz6]EEQinlj=F]W?;ԷPn_]15c"{1YDrmįW<½ARў$W%ɣȼ\$v!R) OŮDD#4΢"4lhjX6 !O-Hغ!o zqgzmafN M{unu 7G alօ>TѴ yٰXw3微/!j-"LH+#vIlWlL(۠(j~:bfyp0+*ի,lEx5(]Ϟ L[ޯ GgVFpqwaZ;Ś*~d$*`K s4;\%:]h3$gϞKJ4˔߿TleBBGԃfdbC{_1@SSF"Rrsw8`6S/aAPn R^n8/|C"}^4A K$ۆ̬k,`#Yn,eyڨ(Xއ<\R=ign0B1`Yj)>!A/$kipqR}D,;|*~ˡPDԑ~FY5iө ]lMe׌tiu9u`,Ԍ]ZJ+,SKU~fvzn{X6JP%BѢl|f@93 ת(g%7/ykQac}00ΌB3xotD{hTN_J1y&D8} :6/rEbÑǺX]1ʮEG,֐péafic4bD4Nj)]Y,oj׭xƣG(ή8'sUa:u>\TI"Nx>ŀe=bWrQ0-ՀϚa ,ٲ˛#k*RVϑycY̖EcPYvSb OkH,Ĕdbb<)$xU<'*W*› %լtKH.x:Eն,kM@<ȥ*<*{5MQۘ0(* aX-Aѳ80f`r)PL93΍y'X}*XU'B^ZΚкS,?0Z"Nʛrrϊ?ڦHҌ{Q6wݍv=R+ &ԯ/8 ;W9UcjVMm-kH/2\ÊV4{4o)߰!ܒv4i?(x{=K\Xf:7>*FOmY6u%֗]#Y%MqyS68R/T@=ꔌXmZ4#g-Y!d )xnWyR~Dtf߄e-|Q.U"G(LACErQ0r51hF~d2!.'4n<)E&%0^ zxAt]Ǡg1:Z&IϽ{&yRaZ S\xrE{U&\$n [kc⏟0\&_s[;lLC |HaGG1]a<!99e\f 12tٝ,}:]&<cvE &АNX'ufx5^w)Mz0{7Ρo5Hw)?trm\͍ИRZG8%_M =Fs,Zį[oXT{҇VSݻ C{ %Ʉ {~n@f X_Oo 4}f~ֈ9Y)}cgk-N q1cA;jK=&ٻHWŏf)2iaC:s`I7ԡzD.3< zKfGpͥ &&&Q!~;V?&{EvTr=Cu8RFj*|\с[ {gebڭUs,8vKfK-?B*2x~]ʸw6+}XX`EOCon,e.>KU^vosYumj֊ jš v'!1T,OșEZ1ii: HĻ2vO[pmxy꭬DUTQ9y+|o/$VТcc D#1)XϏ 7xT Y/E ;1͗'>kל7_4FC>KeMaZe+%3|=d3SnsX!? @bAA+N8kptqBc$>4<~DAslɡX\ʯ[,7H#4F sG8h"!0to6,B'I1'Q}KjJB!4f]Y8'(oz%'N?L;El >uH}"}Fb*: 1vYZleweyYke94hKŌzGeqS\kաlTʯ~da'7<3/7i43Xꕧql:ox#2{fy1S, P)7$3[X% ,€} Q 3$Mì!~2f;M]о\?3Y1{"y*ħ߶`l!ƈ^CT;g[7)1%;S?V3aF5zUϋ_Up4qz'*dm78 Ƒ3ɭ)Nm.xp5qQO{n ~T$/}ըxȕ[X0g>eܯqOH4s! x>2M?CE4rR@Uk2R"K1¶ AS٩$z*{JIc^mv aU:f)dUm|˸`O LwjS^ &3dfi&l8Q%vΦ@m$?A8+m1>v(ja7o$KKзUB%G/pNl_ʌ9;3K|{ kL]6NXCx8jy~0MBV[=M)(_GZ9(1#~9~k&_eA8빸lcJSuu|ۦ|p9t޼-*7H6pO=fJ׭Szs|ȫN- Y <( ZeT{dMv8Xh0h.2Ae.-Ts-w(F2#4g3!龻#UbϯiYv<|:Ԁ}|ށ^ق{[1`QrԷ6c]d=n 3A’~ͨeҙic58/g55<X1tz39^UM./v5JdtgcgVk U} j+%̫g,umC [5!ǚVUz1}ieOpi.R{6/Q^2szyym;ӇN 6Mvs!-YIP,9KhSH P̱07,AԮX$=4'L;7XW*};mFE2{* IDAT|ы~-c({y1)Г\OS h:+٠"-ϙ{p"pF6< W}f 17Am*1ژ&fz/iGA(8xR@4Uz0ggl)962d)?|ЛP^=cN %b|DӳMlU6O/WyCkR=d0WaŨaZJV{(rK%'Ԩ@KRxBo ӹp7E+4d֡|W1Fwmk0)ȅ>5i?߶-]2Gn~<+1ɏGX>'xGe ƽҿ<µn[6V#{Dži-)?-Iz3>zf!ěV7oTw"WN4/lV#^-fin-0n=O9 LP*δ>1{cJOyB r+2P$Nf_% f68JQl% yPkjԯ ^Ւ)vțZhՀk&3Amg!LuqrrQ3ĺn4.'/kYVc%?ɜŲ2wg(&/MAh(]W⇖sD'86`Z*ì: ъym- Ɣ鿑8;b˧w1}|L&mm+T;PӪ)M^Iڋwפ!kIlL*OD0yK -~i(sN_޾N[3y'Y[y:Nq5,Dp"zcmp^6ua܋AۅyX8RL>vY⢮-;d,k@Ծ6+r-OJGտ%K1vpr l Sod% %?B;c4T|~UCЄl/;, 8: P[%Xbbc/Qc%{oQc/ {(h)~G۝ywvfvwfn3O&;ˬbsϚ%Sډ)]UnFѡt*%dSyə?NHK3xvWpKgpIz0Ϗg1S|s7s;,wc4aƐLIiɬv~~~v;wÎ;hGNC9gg:_B֎(1L}ɦ2B!B!B!BId !B!B!B&nWaڵ;w$<<WWtۯB!B!B!Df$ڮ]pssCզh{^^ܹsԬY3Smܸ-ZT'IM4aӦMYeJ,'0yHO!vBק#Aq)$;i'1}Ì!qM=Y9x`f'A|`\B!B!B!YdB!B!B!"2%(yx!B!B!B!Df !B!B!BJM!B!B!BT"B!B!B!H%&B!B!B!D*Lx-?&S"CRB9p0&6S!^XZbiimejRF 438oΤ}\,'&$!D$x_p,@V3ex#KE )8¯% K{ԧ{tч%1ԓ VYx&õmɓO6/8fǭ&(@}¡{"sj 'JB,, ?=2PEG]/ێslu7bonf@b8[:=,~ky9q}u+ؓ\=!м+6l=z VF옛`N\_#Hs]7sÅF ndr(R*f&OYC3xɮ>V{Gf" Ō(zsW*wU)adwRxۡ1Ì!qM=ic/"5V&Fj4ZkKQP=רly)X.V)UV,Py1U nԅEg '~ZLL[w3?mjO-Qq?3ղk1VQiںPBS lʣD*[;Eo%烘CC6 Fj#4f8W~+(X5&Xj-qjȿfYtaJa:`aJߦ_$̜B_۰$ИX$,[| *OKQ+Lt e;qswD.NNk3L-sPz7 %a_YFϚq072GQYٗ8TjkL]uuOr;Q qeG9=邵69=w>4fF5șn;SiF4I|̵w=J*fcLnCh-)\D㑗Vӷv![h]vS*Ex0Vs{t_ĩdםnG ́$&Q1r g5ZŮ|z®_2quƭKLo[tA4cb>W6t1}:>;JMNȘ͗HX(61+6سy=Y;;v]V4Ɩ}rM$K OJj0ȎGfBMGb׳“=)mmGa<4/ mn <0qN?C>

    %J|H=aU[LoSKr?,dt1?ӑIT^egYfߕ `}j61~ "~&Nl/n6ihG #pi;%] D<ι6.L f64ȑ5QM?о}}xc t˴nXD9LhV?oaH_lT{j/a^|; iVxq/gLzT7$1&46܋) `3gјeٓҰ<=6eT%xU)}_cHn<A,0OujH=w;/?ُG??䉮ö6АX1aqj9ClJ~wഐ~0R3Ȑ16s>fYÌ!qM=1lqT6Kjuhڡ?ӶeǀݎA;3*1;:LJq07DkKr405~5K0#?`^-|s)&3*~aqgP\WzfF~iŋ O~JcĮSp>2w ||SFZCzz%; صq&7]2mnޞGμoj6lEѿxx #~Wس{Жf1sSkDÙ1povgURjUV.KT^.ݟ{ku$箠w=B ^}x&lBUdo0ÿ¯Vuj7!0- X ೲ%i2ëx6=LB?3(T$%J̋snXS`,cdԮXB9{`%Fǹ|!xLK x>WQ\-"(]t܏3-B,6$3b0ݘ%[Pa~Y:k Of͒ Z:֡eJÎ8 ˘u6v,+"td~aG@!t[Jڠ5¹xCl#߇ %[ӶD `r[ؓ!\WѯQie]s",ѯx֎PTTZO}Tx35Uşa?جőqH.vM{֯~mz q K"ҿq\S 4ƥȡ dЙaAN|?CF4j֞cb`EL'ٕj07ūW_xwg"2M5 gK 35$fB$'S_%>- *AK۬^D6y0sй7ߛ$;\URR;bqN>}I (ܸ͖IOՔX"#LugMDA2zWM-=‹vJA)8.0cq WR.5Ğ@S =RԬd\z}㳛b/?dyd`E$VYS`\e=y5dMN2ڹƕ{mfKߩT6Pс,Z|CQ#[9=1uƮt6n]Ũ,mW+&Q3QعƟغt"m<.t?jG[-LYc|Ja/_1G(Z"_Mw('YQڧI/%mQ^j?}]Mč =<*5,}z*.CmfoC=^ S^ݚ!s7+`Tntv>~RQyl޶LUN-sCoa쮸Fz?p8gC:FvۨED$w\Z—ue!cWmQ>jE).E- |[IUp<8XQwrY^%*ga nE(YU]}fgW9u>y)I!SnRKaƐLw3 )aM٢,t;:pO7%u?b]1UxؘOߵScXk_R +rm?h(`:*;<˫g8ᅣ^þH**wZUW|5>WB;S9QXNl4(z=zEAm[G *1^#sdGyx&OR/XM:1wyolՑtQ 4|uOq}7 ӎ kc It+ٯ/6aR@KaTP 'S]{HMq_WPɝAԖaDO+T蹿Ѿ+ {m5֮M8d@ ^ ϴy›" -x_̃|4x!27ntrͤ_DA b3>!N S;#T9(X}*/rBeNbL#]}}}\sbs{s "1#ObKғ<)P? b;'ƥ>V)WVvaڍɖ(ϔS{Hwg&Фc#|ʯ 2)cOQ<j1qlC = IDATXu`vt)TݙI>C2><¶GBMCiS) 4*u|kV1}u1ۅVPQ|76z5-ޕ"O}z3DhV̴EвHf< ݾG 0a2}e ~w_īZ,qiX~{^AjR`eLe]+p>]a.spHf3:˄*.v}L+W:ƢA)[;#DHY|_j.)~bU2VPr Ep;b8o;T,5lm1yOWUEb-{k brSbr|@f kĪxLjƦ1g9|<ZuՑbGLZ%loC'UTtX4%)0~J^R<[JË&sk䈫pnmO|Qb5,gB-pM`Dk8ɓG9ׯ ~۷q˭!oөܼIЂ+]zPjiqz0)1p%LBw~Eѳjs^w)=?XѺPn)xA39kwx"AsYCz=DܿU)uhfD^wUgh=3gথ|㩲d{γ]fn /TDo!찳αnSh=䥓B_%z=םQٸCgّ>q; h`offD˻}f|V( *U]#÷F7Pej͒/D"ka3MX:e}W\$UB%@Dݖ]"N‰#HRQNJ%8 {5Ld'wy\E*2kKeJN+B׵jr8yƳXX Jev+tbEg9ZV1GT.F^G;lݞ(WD){$g")[42" >rx :s}NW8rL[=d*fغ+Tx`q_qE(ꋏᬍFN%}>B)icG!rpʣ,H5v *V#qK4@*Z-b@yIK?A\|C2ɒf kĪHJdKr/qL, IWTI7NwOYretij)G\j rf^dg)į xNh,`*W.lzȭ!Weg_mAvD?.J h426MMچ|.+X92һJ.fFN8q?$ă v0UY=B.W!!qvҐ#]!58Ȗ#9R\wv$1c tH !NDi_cz0.އ+;'IRYI/Kr 1ĤxA՚*̮gձ`ɏy6~]Vl#ÇJfq.Mubs&V,-7r2d=z>Mk0~$j#95KRJiSr\*d?~Ky}H-)Ȇr< s.Uj7,~j fN)Qkfp%Jb1$'1"QY<ؑy;? H 4К%Cdbt,-߻Rt X*֧3㩌E Te=/x2*Fڕҏ0k:f/:Oݨ/H|$U`jfʸ}x'^'+8\g@ ӪPbK_R&F/z.8^<΁P эl k_gH2h0Vo\3=0M.n:wJ2gu7}U1ΞU(Ds>j ϥ[Y2-`0itz(U e.7]*{ +lB Bm. 9`BfN}X+[4׬D|^9ܺıW2k"k¶AqwYߞiC?S6O^}nJ6q r4qYΗn߳^tHv~wVh젂~ڬ W[Z)fQU-|ۢty<:HecJwl`>cG1S{*,ۛNc360༝$nfcaL}ub}j&~.x:m:3Ҭ~~7V-V-Ԏ7oo(D>6.\wc4>wRFԏF3qg^oGG,oDdG&4tZܞ}3ȏ:.}eT<_Ǧ3^4+fOaoOL-f|j{”20̿>#pǷ ( OfFat݌k7_Ӵ;\g*thBIS9ϹrQ O gn䪏BsiTFeq?X8Tlbt4K,hU~ߕ4ьg݂^#8DO: ٭׼/C/8 MB^lbH|`򨁗WEvjW&PSMţ3G6ddkDCh5so؁m(!, L£OeY6T(<Љr0ءB!dm{|QJ-PHYx*s \ M[ s\5Zj̆6Չ;gnq̔g BA|yI~!nfLȥ0ͅg՞3^eU~ں>#P)j>['4Nqq475eʅ}q&;מc y *fy,WsYH|VwkR30½J6EߩĔ<:l8hU[`6ECq>i]^'Ng}{1[CB32ُGihG GsN*Lmqr+Nzcggg@ڵ Kw1rD}/ئKɵ~ ?3)Z^Fʉ惇TzMgf23:3Ԏ­@L*+8Jicbݦq w f-5-D 4UzL8@ 㙻n]&=ʒ^T/&x m ލãwc\q:~MLRi-G׎ݩxt-m]ըեY5s~8_K8HC2k5R:.40SMFP0ʆSre`é)֔xeo̿Kﱃ[h# l]j”+0MA4&G2.&l2w ~#Z⩱ % c~ǻ oƇ;B0cZʍqUN~ۻcݣ h]܈"vq2?~0R?99щ ~isOT׮qn0v3.I,fz&abnG`nL&1ԓ /)-֮]{/sN|||رc͛7Or9s w~Y%ӗ[>0{)Ie1Uva9)[@ ärs:W(c{fGNC9gg:_B֎(1YP~eb'fv:ҟڭQ}2;B!H7oBbSa,e¡Sȱ( $Bz3!#\Z!B!B!B%/لB!B!B!HL}ɖE !B!B!BadK6NB!B!B!"M2%[lllf^!B!B!B4Qp k׮=n޹s'~B!B!B!2Of'!vڅZ6Ezz=Νf͚oƍhѢ"ܶ =IjҤ 6mʒ.#8)wAC} >I?ǏçO%$Iؤ?f kI23; "B!B!B!Ȋ%B!B!B!/E !B!B!B&2M!B!B!BTlB!B!B!B,)B!B!B!D*L6!B!B!B!RIfŋ@vh1!ᅃ4XKKKl+.S2JnGIOs&eiN=4$!PoBb$G  'n#0>}yMb1$'1"L6kے'{ƟL.x { T"ת-+Y!4#@AAtѾl;̱oG leF )7eBǮD-Ko8eb"`o3CRؘj0/W\XѓcnbEjL:XӛkBd,C!e1#(ܕJ]sՇ;vJ{]vtz q0cH\SOb&DKt̓jGRn?A5j4[^ *U x9 @^{LUuaYC <oKeSLK hT"_gNm[4p(`w9T龈S/]f0ER%c7ﱨYۘQk2fvs[ȠBˁz{(cT֘]B_۰$ИX$,[KQ+Oӽ(kccąU]R890Aݘs4m[mlQ\6Z]LPXݯŝ-15%w~M9WBO|sۢ5©pu;FhFʍiiz8;3ZK2{dN3I׃\{׳@vOɳP65Z G Ǻ+D"/oBdb TxL~ڷ}ݙ6<10*3zx@{bjF6ʁ^9?%'uni5Xdǣg|3ȿHa>Ǜǔc 3vȌä҄rhax1 T~ iܭLHE )@LEkjJyƥ ]xDalg9L0j13I7Mb1$'1"2)QE {"߲fz b_IOX2lbRaSG:4w눾 td7AUxiv ᱎ3~g=Ҙl! !S^a.m糤K93߬Ɣ9R6&0{4.r^a\z ;@n}6 \? -l鋍 bO% ̋ov! 7iVfĽ<Ƅu|؆{1eXy9,,{>-Bc*8q&w\ֹ>t}ٺ-. jYIW"9gC" '2"gL4x܇Y0cH\SOb&Dr ?qlE_*UC(;v @_nV_`d:Tʏ &Z[\ =Y9j2PO,0јaV3Or~ܵ?/b(>D֌kIdnImON?}rxzW~g<]6q Ft~ö Y8& \!3by]%Jxz8%; صq&7]2mn"28OsR.]:ۙK˖pFGX٭k ؁e֜KQ(G1GnZ’-Ӱ ,L'fI-W r2~iayM1q٩nGPև6hͬp.ސaTX.IEJTݘ}&HbfǞ8~J/T:WdklU*T*-MV'>*<[ffz3ELşa? լ"K{őqH.vM{֯~mz q K"ҿq\S 4ƥȡ dЙaAN|?CF4j֞cb`EL'ٕj07ūW_xwg"2M5+U|1Og֝2J,߹t &/9gRpy)Pw|n3W&Fx:d-Me\Ay1d-׋]]/,_<_ܩ(;p#BqJXWz3Ώ6ݩo%D"Cj=?=N^&&y_$/B`Ylއl;Nxx8a_~3tI,:g|S$ņc'<+~WC:h9LY^Ь JscOKc#>nh gv;;[~Ӏߪ,QVmO}h:~veLެaޤy+/90QJգg/-,e_m`J%P1|;)"ރjV$=ɕ:҈:y1GjV6gz*.I% ܫRFux8D}w*]yEDH5q3% DI?1,2'c&IVfxZ֡A5 +ΜVN|U/у'Xƻ/siӜUS6yUVu_UͺS4lVBp싯ݛ-C\mOj>%bOrꦊk]Vf:=1fZӿ+R M:r!~dY_"W^skKτM23=i_ȘZo $ޡtFdGz^vKQzag@ŧ͛IG8y<ֵ+@c@$^mjUq{[2sz<p+Z,30I#-vJ1Wt{ Kt!yLyQ 2CƵd$QkSK+©#'8{Q`zֵI?zMtN{J=bf5FlNSOw2Z*v˗ ^< jEA ?1qs㧜̏hbN a]0[ ND*qɟ~7^QP`ʠ|^f ҝ_+Pq]q(\"3r/׎魡G9w&Qe;FӾb.g3MG"ȸfaffAqd\z#JV?f|I$@Po:ݹ MǯU]NWiPCc-'٦x<,$I*>fj0!W .cE7=WVx\sz hNf;|t;)/NhȺȼdLp*3bZb?#)4{\ij[mTY3@ (d @G{@ -="#d!Zx2f:PEqV>}hğ^A))),Z1NN0+~ N ¥x= GVX[+(=؝&sԯ %ku \Q룉i ݌"Ơťc9`ȸ=j}7;wTl?Unuݢd,}% l3Kje#w`255ʖ([-]%G;|wӆf0>=ykb)ۘbjὡ?ѭ #&WG uA3Z8}3gjt1o3WEQ1(ή8N-Go`]*[<.#՜.ӍI& anP0uS`_S6DCH&AqE[DHP+PaFvꭶcoPz$ #].8z#Z*UD`MlX5 eQ,)@r^{y\RP$V.ea$Q ?ơzl;zjT7H8I<|"͏lS,UJR g8)z)Z|[|y9GjɒJjԢ'TWc@ T q!Ir{-EG֝EI"Oyq֦LRQ)@ր1TQi1/aʓaq-<3ISimxMš[xkzTje Q [`N [2:#ų'cDը^, 4",Xd&$ 搑e!<" +^*K ~ c;7wR 1) ׸W7eroy=i>~Z3?Ը :gcL8α0;ILﺐuEA232AT2_FMQEqRC˻,9}Oi.| : Ims/ryhҴ=HzCc&nz.l=???T-'lNmSKip^PCsN@tIni+UNlcWiW -Z5Nv^̹ qG1ORwf⏜@.([·qѺ2>>>~aoGsglg8d՟9倹9"!fҔ~}H3'>P$q`4ǻf(`e|Lz$m܃yK-8:BNIޝϣBY uGwl!i\?d/l7>maj~=I[d>_N (2Gπ*S= |AXl5Թ[_7:P=m9bL1>Kس;d5]9W]@M鴨/;EW%qn{Oc|j<S?4B%|7|>슷'nLQ!&pIw_ԩK2CkeKךԱ0L lǔ80^ie%[Ȃ߯PuF&?fFq ٝ4wN (&eȺ4ڛȊkdj:8ښ5FFdpN+n(#UMh4tY3޺3?ww+8.^H" ET |Bږ}4gF`/VG Dlp/k|Ϝ)e]v '-jw:bod߫d kɘI҃DMRl^2ޭ^gApz5r' .=cKX~=7Խ*P󞲧m'qf7 jKuz@ǾX~~8xѩ(8Һ|4U6ڕcS1.l~˹ޤ4d wcFA{_^%edS+.n3q#7꿲ͳ>{ a˶XtnTSqnm WǞB$ Nw 0wbMƭʇ%lMIbH$!~ ,T{ ^8+ 錙4-ǒ jf1K YfBdU&q џ2GS"hO6k.(:xKW93!+r%GSCMNm.YOq# ݪҴb~җri &a?X{fԺo_Һs'9wCY-Z喕¸v?`ZƯWxYņUo2B~tZXeԺ37/E=cFvYHSu:~yݳէ-G#Dق뫆R4n;m¿½!ol)d;®\ OF|;Ty\hӖYE?r^Da;kSZ (.~{3^ϝq}s6Oz&Jt(֍!ΛW+3hҴNxik2VߍQSJe~>}Y*VIW_"`Bq|,Lz5%;rY:vc$|2-[re~oMFBC]9ip(.]q [֔sgE F[$c!ⳣXPRb\G3\@psE C4kQ5&T+ &zUưxȸ$ݧ9ڵkuڵ}v5j?C=l7'Ye1+5j!`d3:yW.]?Jd+!!!23'qɽ{i1 E8EKْΟMѓ1}z2CƵd̊OHHN="%I$I$I$I$I$I$$/$I$I$I$I$I$I$K6!<$I$I$I$I$I$I$=9M$I$I$I$I$I$I Id$I$I$I$I$I$IBEJ$I$I$I$I$I$IR!əl$I$I$I$I$I$ITHr&ۓHW^aI$Y q㖆{ qZYaee]/8 *S{3`AX3\^@k]4/$ٮ$WH8Ͼd[IS2oO%iW'ֳlHBbHIDPP\! o}L$1睢“1'Ug nERMhuLmP pKḿ#ŝu3G,IT=A="ndK}20L%uAwyjm܉:y]mB7eSA¹@68IrGTez0L~ҟY蝟ӣnYlMyΖd S+ʻ&&X~YF)dF߃-Fŗ>nƈA$qQte<6J})yR1jc؛쎪_6z:lHB aQuRʳ;vAS.u??/ɬjϢ?uL9 ]̉^^x$s?jaVRi0vBxmBv\.dϝ*?÷Lm7A;2W54E~}ޅkEӠOYCh[ۖz@>p\q0ωAڰwZ?Ft=l[5n>O%[g coIJ^ :2+#%~qYSiLtR(?lQ2hT*&عUa,w xXPRdӯPZFRi4%CS+*֢uL7/0 v*S,Fvd,d^,*-f昛jPDg9p*ۢh [:j131Vưxȸ$s%{H17I:5{K!QvͿg0;E3J XĀ_QU.sj0p X^&L (2; X=B`l @jK/#,eSAZ_3nqͭtGzhbd8q߇gΪM^fB4G–t³bHF3bg`X `U&^I\"??|gѵuZxivm3nsXԫ"*@e_ _ucIsӐt98j;!`Wʘ^ٓZJE#|C sLR1{ox`x_~*\9qz=#?F eþɃGz\UzK%#K>exd9"9n,k/ʪ,1n=fc:+gʏнf0sb`ݹWnexEڶ{;M~l7՝Ⱥ \?:KO~doGP~߃RdbeaR2ju\͞O=jZ/nl]rz5\I\<_ϤG|{poE5U*idE22/iѧ9n|,d kɘIRQivZbڵ~Ibe3akqYφxRSQ ٖ("O̽A5KZ5Sv!ПRo-҅3E#-Ԟb ! qbDEG ՘ MDD!;)?M+DQ ?:qu}L'&&Um1ߎ3wSQ}es| rp\Iؚ?fPwvhlj#:,NAXNX5u-wo4 ۓ !RűY-e 1j}ů MKgaU{JȉP88[ ۴ ˝QݜGC|gw b@ےfw$),M9糮.bg&~o? '&by;(H5d\-͵ b~l+,*!?w/bs#sC>1V [snJ>¦,_>_+J7ty?#D7[Ww Ft]+ vVČ9;&U&Cñ˧ B§&ϦLd0qU3S+Qڷ|9 0}[@Qmaq-p'\|QKL)Q w a)Z ^攓E'\#fԏq{iajMs#]5Y6Ml ˴" μ`}8Fi_ab^K 4$bdН5@8r{!#.ɕ>c8/#<|Ӫb? "e%Ganj)\ktc~=y˳]mDb-Er\>mw{>G]{QvOɺ5b_."sK[HT3&VnV梒T|V ږ_+Lᗧ۟EƕD)K3aWxc^q3w#X5f-L4&ҩ-N)15#*p[޿GǣRQ"|^q,2WKG,+'=#n$޻"ȿ"d_ػg"BO^ ]0H(NPq%>{\Dl “1+>%n޾}8qF~]&\""}od,?k׮@<{FEaNUli[|6{ ,ДӺSF%:;$qT$%4+@ C(A ?cxebcKAS+W?mo Y"uyoس|XZmtʽK@`KXmKO,\Iî.U\.;Eoj{ݠx؞gO?:h9LY^Ь Jsc0kŗAμ4f?;زl&o0so(;p#BqJX<6*,#F(kYF4 PxıR}Xi;;)]*0n⟐ ^?ůۡ0cpl*m n=;&;˜!4qTP5Kش;Qyg]ݬ:~ӎOz2dEV}u`b~ϠZ1YR9`IE H1{@'ޱe2+Wdw2Kx>i9y9n_?j@¾FUI͙o䏭l{yEQW$`LV'oes3 Փ{Q-yi=iEٓ\Iʯ3&G9#pto5OJns TI% ܫRFux8D}w*]yEDH5q3% DY?^!“1G+Y3<-РgNqM'Y"NWϡ[:hvb5|}s_Wa]AЪ\hؓ»aG7TxuNZ{ڎua4\Q ٳ5|;[с޼9 >[_;"@ld4:uYܜlQ9YIt- Xگ2{ҁȫSql͌_?&;:WpU|$}: x?oGZNyP5u8JL֛cD)1ŀlmo)ݘn:@~wOkp%On9pR ZJWYѸQyƎ 裉 .ιҥ7#.EIHJ!J`amIN X6}m0y>{0w'5}z+]622ёD {j(*g\"I)DE%릴p)8ˍ"ZF(*[bO6(p=`^-ot hQAeGx1/;E[L39p<:\8 ֫exZYk;joGGV}_?zxW|=m=X>`WiUUլ;uHC;f%ʾ]̐9B5_|ZiT_;k $oe êcU=;)`)ߤ3/~pEe2e+m5 &0`0P; VzN=5P0ܒN)v\U^]/6Z3៽#X}Іcy|sBvx)2u=T|p>bChM._>DJ}7:05|` sW7CF1C4s;UЈHVXBl * Р A= \=C: Rfԣ Z R4+s-rI.;mD=cittj`7݋n)J/L ȼTyS9'OǺv2x+\U1|5!P8WZ){ ;38n"I8`ds5LGo긗ꗱD™t)g_Q cXjbٜ؏N{:+W2yT$fn1v_V[CvsM8 w}\>fy(JG`o=mYx^t~:&@( uޯ~ujRhr2vdU{XTs1 o [zzDbcdl?AƵŴw0 33 &#:QB1Ga&̗Dts`_}8268pdP*Jfۨ04 &HaG8wVNӔO ^n^d]yWxtzz 7JRٻ|2RITeFfM ÉVסYG4:SA6?#y1Lkn܍F@C\V76ߓ]%*jP~|=+΁]>|/1{q-EC֝G'ceTQ9ÃP2ISlF<`nnID I)zt'  $00`N#2(A1,2'c&Iy \$@aaCه&4B^}4ӾOY+\j O{I\i D߼= +FyЯ;rhr.As@$đ CGFvɜ|kkBIZAB|*`G)Wgh"oBB71hq)X7z};q{+2n'=v~͎0=G0-MݮS0FK73E+~{ ׼MRgvLXDKr6F]Fə52Љ;0@eKyzG.DzvgZq MiC3xww@Ci| ̣"1i;2;clywW(fP$q`4ǻf(`-b|Lz$m܃y4^ueǾ]G) ٻ3gmjZg2]Sm#B)mdH!!Qf*OZS<Ne }#v/hoZ4\L.kSFw6e֬Iͺ]Y~o Z*p# xw=+:b>>8dfopH_B`&҂ھԩW ^(1N~Q-EF֝EF"*OEs.^!>WUfbeH".>׀E}"VV٩X`e|-c-i$%cX+3ޜLw5Ly:&v@:\**YasUMU0v6e_,tHV 'L_A4p}^V Z~wvwcj&6M6^2l4~b/_N؆u_^rC$m+K d\T0/[^yOmS1cAvv\ǂay * R%AK]_ͽЈ(VLKDL{-UUqVۖ}"qTPە&$gl:ԧ ğcO* Bmj?dЯqe3=F|gb1xO~;0bzգ]JbBeWwjXWL3XYJ)%ƚ z,Zm>ϣBY uGyٻ?{oro!1#wjXՖ**?UF(EVhڴGj{KHq=?$$ ?y}sι<~{6Vtn[{w](CGodzk@Snj2T4KF|?-?パ A_<4TKɝ ϖ$Q;-#!WBYO\OMm/ x>4ށ>$۔&nwN)4\u=~6lfEAv2q&5h]qϡ!lb)Mn (~lM.CZ._w~xp, T42A '>6U}w?1uE]<j5d*]79cZTM{'v5OrTў5DG㟺Q*v"MudQZ;ٽ=w"ofגLlCI(ӡIW}ȡ8Ĕ*O ָD.sf`Ok}&}܃I8]CR9..TV;3s:*tA<o>㛍;A׎]ƽGbF7>.=ےsL/x+9g1Yuy(^̙Ma;*g/䫷͝uFk$;v3\3I꜋78u*)7F\95]В# =y1ݏW {rI 35$fB<I8yx_5wLEէ em=hB5x(ޝs o uZ꒟Qi]3sti8+ܘ&ۙ ӖZ7uݻ&>jWGqhDiΪ=l$]&mNqa: 'FkO_4OIg +0dB}3UUݷµ@MZ7mI{j"n4JbC.)X"F$<ɦxz*"zdLƌ m۠gW^1z}x:ɉURnobRi%JF 9pgoC _4!.{m8'҆fsow_=JgZxL4/Խ1Lf<ѿDR포W nYݓޓ|?_GSaϖcƭ]|q%i2r9;&/z?[t#D〻Ww< z;|KR(s66p?k)EQ-hb\ϫ0ωzO`3lq%vYr}Γ9M 251bm2@u{(7y捦XS -\-X\'nZi<0%x;Ǝat9JS76)kl1pJWٿ:Iɴkin-/nѺ$Nk|>oS(it<}Jcpi..ʐ'Dz4@I'L2ܺt^a;?_𦿥=g%;GYu[Yԇ#лD*|cPO̽[sd4Zt(es9t ٟU•Khb]SF{`I2[hU0a' J9{KpѤRIyXT .\L۹%'ӔI 35$fB<&9ʢEe؁ׯ_O`` ֭U~KL%uE\ӄlZ;wwACej,y [:{p|S;Pq^~?:Cr9WCv;}(V{-fLENy3vs 7'wЯP nny[y2I^tLwX&E/muP3 _a搸,d׺yÆ bknfLhh(uԽ|K.uU.q2}5sfɱ5-MWA!en"u Gr䰷|aw$.cB!,[,! p]KeV=퉿_Cx/ig=BL-RH^D\\kke7l$hF-!qM;iJ%u6,!RQ&{rBkղ !(#!_R>חX=C( י/̤Y9 B.f; ^p֔ze Z9wZD V)[ K+Pg[s 9!?ezL)'NI0sH\Nb&DڽBOi̶O!TXSy):Oۋm:B갩 1Yדu9zGNGv3Q J:TI 35$fB+B!B!B!KلB!B!B!H,d"B!B!B!x9td2eB!B!B!H,d3Yyx!B!B!B!EUYh ;뉊 w_!B!B!BIH 6닭m>o61̈́RnLN˷tRZn]Zy,[,[f̰sNx$Y:u8:H jxu"3cI<_.w$6Ob$Cv̳sάNxɲtH!B!B!B!#dB!B!B!"tM)B!B!B!"]I6!B!B!B!HلB!B!B!H#Y.R!B!B!B4'لB!B!B!H#y-=w06)B2Hv4Ƙ 18;8K15eiRG4@3_J=rp禸גk||],Y,я["ɦ3~AvMO2{v3!qDCpp0l{VxBّ3gu$1״ ^I6EQ1Sø=[ Ɓu5mwBQ=ۯ5wY{t.c`J֝!p׮6/b;Xщj/!TOK]4scHZˍ>^ <)쁝^}7|H5BX'SәhӇΧ>REDdp}dV)yfcOOSev'a)$/f(ܔ,Wb>i^ޱSCO@r1!qM;clY%zZk['W4/千ޥ[0lUEL^V|6aByv)R gQ1}O?~3ǜ\@E)o%Oܑ0Q.RJ4l LG&FU zhl_W;TNVk;JA>p^r̗^n&x"+UA#z:n?VV3c1g|7͇[\ۑOr/T2z3!MgI%{LG'ѾTɬڲGӢ`GĜ]xL^\m{_ gX),_t.H!gQ^QJOrJEŧ`yW0HsQnMh괥GKMwr[B[ bkB"8]È}-jm8|%ttzllm[Ē$Cv3!,K{_.ݰSZeG؅l^#=jKX>. εfm4PWNsEiz|:E3~Nr1dT4r{{MvG=8p.϶{4?kDSp2lO2wiˆQɃ\f"wՑu $UZ6"19 DBS7Yҭ7ߔ?2O7u(tt:Boi)+@Cim|U7X3#ƞ $ۣug;_5oEt~nحb GFovZB]E:إ}Q|əVLl:kw aEێ^^ם8{{FӼ\&mkK^|$4Gَ̜&_ПQ+G}Sw< hҁ_ݺ0vA#Mtpˊ}܌ِzĒhoǧݏҹm.t qab]M;>e(껥RDF ߝF]{3y//۲"+apրf/єfͥ9bxCޡMM~ؓ FmewD6^wz((7eWm2.&@w c4z7|s/voo ztm-gf5 [G",Lb& )]%k;-a搸LReѢEh4fQ]4^urZ+kg]j4lTJ)mUo2QS)ePW^a 5]*œ?7 BBBRQSQQK;d;닪kÔ9MƩzfA{<<\PG'EꯁzTd Wy;"|>nZFy*mRh<5NU68Ƴ%٬jm'[[R8;z\K7NܧIT]mʩq,CJ|}T0j1{ͣ&9 [+{}U5L3R >mr7TQߞ1=mA_?[F5NbT\|m"~SlUGY@v&T:z)}U`¹,))0]Vy*UhJtEm vZNկR=uƨ'47\Yr0'Z~N/:HOY] eRxx*wULGJSDl *gFߒ3)sqjGecQ{cjRJJͿ_t/wyrNS5;Tw~SMmԏبfs>~4ۿ4VGUKBeXmUJCrM*!ƨU) 'fV|kjhSuY>{P -mxiT;( W'Mrjjٸ$I9:ՙysLlSl(s U05+RMT?=*mwK)P^ח6_RybFx u\:Ln˽&-3:I:pz$Z߂.MgթfNq K0JA˛Ph(`ΖۉP8rm[OF|*f/aN,ne{{WuY￈|cv&ԏ2}Տa>̾-p͈iqe1`@U Go y߯ϝ5.͙/!}GPR|Ml4HqxF\;La?^79k+))gX56z8+eHvoyU<+"۷JӖp.TZ!r=dXBUrhTY9 Swɯ$̭ÛYVߋ=܄QƷh̗hݟd ]Y=f/ [A:mZeӻ{`4^%ϙ9|-ޛωkum#yYq1*-r"L÷8%᡽ͩCG2"ŷ%Kh7u3g#p(F+U">?֏F!qM;ϖbq+x" ]̛`"IOAp-4g1{"Z*j/[ŰXwNo+ӗnru }h~l޳ȫEpsuWL_~f7 oT (M!8S9H6D{Qs&.ۛe;B g.NC7/SA&CHc`ټlO_NThՍO0ѸA's?G3-}BgHItQMm\55s$5T/ue3z/柰o/ڍ$!&n^C鍧EBl}/K֙4Ղi՟^o w4BTx yYnyy㩉ڍso*\za *7̐+1 n R㉷uϙC㘏irX]=iGXamf. Xk  V%{|~-ƞ^Nn/ϫ5huZ4d^ƽ0=QUѢ &?&"O#JiF3bm2ųBd׶Qh)\DbYիas,Yđ^_f +k'`:h55zvKR<fxP{~t_Sߺle&f* s0IHprW,hܫ: ߦ}u2^uld;3 s&9pSN2dHph 6^E)hfJ0sH\Nb&DOkKaa,;1+ lw"d{{~T)]ːlaj:Q+1d[spK^F ]BlOT6 \HatԿ9; ѵW݄뤵Vy'3X8a_B77o U+n,>/ryOzr5^'NtԩK)=Μ xkZ}blՉ9Ё8p708Pk4v."eF(HAãV:g{t/;GrWً WYإD}R`K8U]uc? T%姛g TVjժQJI|xǽ~#*v!Ǹe<:+S9_bػ%_̜]+)sb)W( ;c(WV;XuwVZ4>܂pOiUnV1\ >gEԥ#;{W_iۖ?w_ QwPA6l1{hJ;`w:9uߧwԁa6}͠8vZn}Gs$` M!c&Jd:OFڕ( Mygb)/kc3wDBKbT8%bc:zxkй䡐;zet@q's-?d~dQ,֥@FwG]`݄ljhWN i͟&P;_-j̔!M*{p|HtN'1df?A\JHƋBE'ţR1q`)Gnű,ѝR |z/~eأ6~&r\c9F?=r+V7Ϗ=zQ.v 5@^5t*%#X>t w 3=[~#2x iӡ=ړ;`-I:ˣw~[6GBXy̛^@GN}h:}Ht)mM#Rh{NlNcы|(dMjкCCض,;R%(P \\L|X0h5dX@N|)mbO"Aj@>=]G5Ёk1`?uUro#D,mPwN{{0D8ͮ%әنZQXC74EC q')U~A =q-F\&|uqLXSK_.q҇r]\27tI7х`<; mӪ7/S f? b= I^`?Ʌ7qx^잪/sjM-RH^|r">D&s.ԩP ܸZs,׌NvAKj3g3\$rƠ3w?kwo\-'1״ $ &~Tީ3WS]W zwxw- iAK~FuӥTJnrctogz0dL[jxw˛]}>ġ9>̲t|u;Ǎ懅C86=9||Ҥ>%5@oQ":q_ƣSSif,N<8ӯ4~hWƶtX |^|LG^ZqkNBع_6IU}*ɋŧGt' *C~Hp&={3p yf bN|ÛYqW?k`y 5L㶕e fո>TNnԶ }as|ECLyٱKhd0iK!yؑ`>OGBs,NI;~ :Or+H]Ι ѡs%KNg/)fkĪxLjsfEm ʰ_@֭[GViJ*?\ 2 Oٴ,;w$000!^3,\YH=9ҷuvwUnBɽ3= u_ rv;P*Zn=̘Yݮv˝'7e3>/>Sݳrn.*eAo'$D*ǃ%Cv̓] 6닭mwff3ԭ[7S-]֭[Wv<\dz\υU$L`4]\͢ !!#*>1ɑOO;}n%6"l !l,\%wi l"HEX ɋ@Cam&(x[b9$i'1"^H IDATD3%^ #dO"Uh5s ZV ez$>K2ػg%-pWZ/̤Y9 B.f; ^p֔zG-RH^x9C㐟r/O:;{V''E!qM;i =ɦ;h2ۂ&g>B>S=bMq0tdkғ͛lٲl2Ν;%⥓|g:_#9Wu8׉\ό%|$)d<鋓fkI2Ν;: %"B!B!B!ȎI6!B!B!B9rw!,,,SၛŊ˔F)B!B!B!"9rH38ѣ/^q{f.JA]xa9Q;Cx]m-_w;Nr1?Վ_=-CؗkƑ*g56.|:xy=S;o0vJiaOMg~AN:HbGF0QYyAVLg7\MwLdܤG",̠eP}):7ʕ#Ɍ%fkIDfPJ'#},p>QCbm넧_9w!cȑE ;^HB) Lq|R[WIrbբSJM{0mMN2JSĘYapp'_7h7t ''gSHدV-7ŪǷ+9䗳Ym-WwpﴆJW4/千ޥ[0lՄ4.&6,]_GL@4/ŸH,pnǿ94 )[cMa0˙ei]z)&LaЃ|p9W)1iقTt³?̕ \$l08h0uy808PIzFgAFv`B mϕG|k6aByv)R gQ1}O?~Ĝ\@E)o%OA *?vvn3B}'0V%c q'?~y\`P) :-Z6 uA>p!?uyk3oXĔv3a8G6u~x珟g4cn4(7;ι(Y##^e~g$%1C0JN}Yek珦EA [m;<[ejkM>dmab-sqG ̀9ǥa"x:uGwqQ}{GM6ް+Ӭ1ј1hԨF]5{Wb7V&y**( ȡ|xwnfvnlmX=(fN,I1'32]T]'Nӝw^n̋9@e)(|9z /E5%8tFbbKR12'c&ea-W{>yQ"H3;⤉ލX5udz6` |2q@uqz,Gʯ++ևu3;OAl+$/<Ԉػ\ gp5qC7S&}DÛ9/5D""h6uʥ[Yk;y-Eڰ;jX2i_ FXEA;&ҮT:L`B|8w C<|bЎLeդt4O<#"ҟ_k[}Z ٻm0S5nn ОC>uߖ5,Af{.IQ8眍- h_=ee7QU)N\ya da3Q|ƵA0:ʱ!1O0U㏭ ))אoZ`D^>R4hJ-FĢF|$ǺRح uy!ctiNǧP9ЖˮV5l mE7l*σ3QSb4'hqk! >1܏xK,Y!a0_@ۘ<f$c"gLv1Ǚм)#N9Y'Wa[9 h/͋?c"6qV\=ŞdR& ;fM/1mD xC{Ix]Ϩ+ ~,(,S͞as-(L9O>w"xwᴿX=YVnbA54^y;r>.uI_ȼCtć_䬑!ټ7}$aΐq<3 Xnek֬&~v!Ě5kċ6&¨6՟u*ܠ+J!Dv5"tѭ75&¹DU|>)ګbjMc"AXX7+t"}IǏX7`$гhXTU1I*% ܌~SU#B&T%J#S]5[-#a6 vL}o07)~񢲱z-}.kW Ww5eFϲ룥h%yߢ0HΪ:qoaSaVb8FT]tio: S>sG,k"(j1p2R ([ Љ%-tM] ĉrh8T$g}4lY閝/#BMXQVt79d1F.G+abl!{5_M3( 瘰St2?/|:_"UA{CJLڻ=Ȕ?[2IU?b"Z/zC&?1O&V._ By1(~mŖvB.tB,L_2xql02-/>/ͺH?03(!=IA꜍ji鵅Q?[sbƫ N,'=DEՏW^ -Š!ʽ45oF3>'Ծnד8ϫ6E+t?]-~2EzE{^uLLHLGK OmN3Ym W@YEX Cs'=H|{o$lB$->]Fg) m8A+nfv52nMRѫQB 뢝?: !ZvV݄040V*aĤޢ0p E?C/ײYVgτq1NrJs:$l! j0pe[(6\͝/l[BݙU2/fWcWCn?{=#AűClX<)L:$΄jhbD3!qaq%q7OGCC~GD`mӉŹ!w$P~;tVuo ̓19yn޳gz{n~n߾-n޼)vؑК5kD||OF=~fYT>t9bDa4n6#=a6-39|r@ >A Atz2->cl@.XhlMji~L+f7BS*S ~ʸ朗px +/$ޏ, qib?e2n}ܳ8ʽNjB8hQJ4mdO8>ueĚٴsy1۪ t`8A9J*(8~KJ& a zh,Z tIK̍^Q4*s<q "P5Q - ༺ -?b*/59 q/T}33BFlP:Exs cS%c)z?C*e>q;EI;F[{:{ښ:O@~hX1eSw,DKBCB|<5}fwF"M4{]D޹ 3/hS/xc(ЅF=h؁)cN'D>Ghacau&`{eu$fZ ehMg+VT<6WYh?cP;N's_t)6LNї,Z|f>҉OnM6 [W2a$K}ŝt唁m.ga ZΆ{ٺt27MT칙kպ8o\GʣqEr-Nlq3Xm3=8mJ0`D_Ŷ]Y9Mh?hMlXyiltk;1;zS^AR/.~]1˿H~嵎(ˠ[عc-w^\9ǔv-׎.c0I&n߉އuH ڂmyf6/@k3yjE?2/5bxYy9 \'wӍ|\>s;O+hE l\=(S 4r|(x-K)c;ǥ\{K29C5d̤!"yEznk-D8kC9Gu;5'^^_X)[J-8rn|1BN'@H_ңdm]42c.iḄjQT#toft]ٗyn^nrM1IzjXŎU(۠ >*_+V'6'eGXů^Gʉ͚Q5՗Ю"-? EnldNk%嘊u) z%ߋ1[#?=fLCs|?ˏG{NP\^ !N? hLxB*NbQD ![Sw2/-.%)}XV(˯2͇ N'7 lH:̭;q.n\~)ac`D ;lD #ɾ$]%wtZ:¥$9G_eaΐq<3)f,2*WZM6.I:DZx\5IwUG7K/ >gCq `1:]:ıC`HE.k@)Wmx 8}2=J>݋&:=|ڠ ֩=Y8T7C8`bmIC$+9|+[;_. ӹZ5xո~>0}WJDßwriV FܖA+QgOMi5όҵH޿{k:m&yS[1#6gIk)NDWVVQP?~ f b\'Eiy[,@-/]ˉ>ɴvcnZ3 Uʭ8O/lyH9fv˜ȁ[Ǧ̾M''}>=p1Lo}t͸Q ߟa~}Ѯ7%'FA #oْظڧ)GʶwPlZv^ٔ/ C ġN)7]*k={̦Vq@;f~CŌ !1H `Y6\#3#B~tؖvX1)]uAN∳!Qk A ."{:CLŲy0Skt˒# 04H^EOT!Ka@2ٴ/ P@LZ{ySRPH")=9׽8d>+OMq^yWL'BX,^}3Nԫ қgeP{4X%L[27g)]բi"iɛ⑁}uH!@Ƀ+e?vKugvy1)Z0p٪@RQ)@򄳔18ƫQb•&Ü!y2fRnlĉ7.m-, 6,ӗNkK<Ѭ~P?o+CF(pa2@S3LqdꖌK'uZQ'e*b.fbҀ Ư)\w'^_ΰߏc\RA63LU.?͋aݕ흒+,0Q<ԁ5myph/Rv^GW*dFK;|љb\9?fMŎ`tK74K;R+m}_g9DPPA2۶9(ARb >{•Rō&Ta\/[33ýdg VƵɳouwYݫFwgݖR{Z~+M]QvBZUjٓJ5غ?7k]gk |z:χ(Ԩ Vz$}}nJZ1Đb|e?@w1jPYeZ%=\WAāQS~JOWVꑼI0-3 x/ WnYRJ=)Yt ~4utZ{!Pi9`jf|Ldvwդ{Rl$ʝhNφǮt^kiDo̴ci`^V8OKC DVrקl^SJyN܉_n<~_[^hgMМ[ÚsN˽ oAףڠ2^V17**T(;>9^:tvKugy1[, S)qcͣǩ&,jx} 1O 3T?&ymH2]29C5d̤4tPF 2Do@WǗ 8x'eqWbcfVX5ĤxFʮs ̉L^Ɇ9;?2v~ޤ$-r#"PYiok^c$so,voi_.J^5$ VNZc/c1ۘ!: AA),(X={oOf R:D8N jBpu(Κi*xX+\4ؾ<(XlH5wo@!qݿM⠡7ֲFa|{#Oz̑QӤsa8fIǧ3rvin%'O1qD)gLiWg$ƒ60m!,/#_q 4"/g##n>qlzյ1zZ;(Tw,=J'uGly"AE{ {kƄ7Vgf1|fwe@it֍&%]Xv\Ig׼3R=H̝~lXo3IxH9 bLegCotnNUG#6pZܾ)c[JZi|գ*[ertŲ[SN0kRǷ1U-`o3F77QMWc#`lfǠ͌|ÊPfNj_{~i\>_Ӯv']<_\ k= ]lMfz5-AZ7o#I"X5zg|'p¦EmX}oQT=tѥ5\pi~#ڊ^,;.)mfQh9j՝+krK0Mq9`͖4S713{޻%Ys0JsI_ȼ @Fu ۆsy55B5VAEF3gcA'n(dZG 3/12'c&eƁ (3&8Çv{ƻ$*h,>m0 9.ũPSfS:Vp8Qx>[cWoUR=ҰCR?lNe<-}9ǡZE)Tض;ڰ IWaW}Xw:Bm j ~j@kԐMS+ J)қzi8WLg˶٪bb"Ui0t=+jAIZeFXi94D m}~y)|ۤgwSeh/ӡG}oF-H ƶݛG!QOvTjSZLpymp!3tQ<M[)^㷍_aMuܔ3Ro0XQ|FmBw5RV \Uaըal>1XZO_?qBAGرcxK=,Ur JHT|ȵc 2wŃ0nJo/J)fJ#>ibQ_.Ǩh4[Dqa2cq뽂M10 SvwkulY6`%b@$:M\*NtG1Lą*bԶo~īB=O ͸7 +ZMdzy>@UKdf|X6~|;AGLY7 \)ήxT P0l8zNߤ&5ΝG#-q3@fT1'p (V߯^JE'P5eaMܵc9>qQ(7d3L̯h12k|x/t*h׼qIʌ9Ó14Ʉ")2+cvTC&W(~>/BFZ4imZ#mq$VXƤI=Xt7"5{n3X\տelWN?uT.iT({cydFxʮqP9N[$}!⻣Qx/Mp(rVnes_a`W^\7Р̖"6(ݘO0gȸf yg?ZfԨQ/gĈo>ɖќYc͚5۶mm޳g޽2{A.6rcxY}xȑ#x{{v2wA^I=9&0ȃra;Тt<!|999&g 3an'GK\nyZy.ɼy՘ tT/׏=@y-kx☁G4Ü!y2f9'{SӌMvtt:Ο?OÆ ̆ hҤI{?c FѣGgh;JΝ; 6бcǚ>̊ӫsD7jm-.$I$ͽ0-7陧{{Q$<$1cB$I%z=?㣊A[$}!bZQ5jzoNCFe skɘI_~T*3<ދD2uh9Kՠümia_$I^= aќ<12z8+t7Ѻ%I$uY.%?H/ϑA[$!bQ bQJ`G7O9r?w*NNd skɘI-#LO>϶1r{Ms$I>y1h&v:ʵ? s;$If݈THuI_ȼUN\8t=/12'c&e?{{{t:dRt3\;$I$I$I$I$I$Iޱr{r'cd skɘ#Gvw,W)I$I$I$I$I$I$Iyd$I$I$I$I$I$ILA6!Dn^$I$I$I$I$I$IDd$I$I$I$I$I$ILl$I$I$I$I$I$IIq$I$I$I$I$I$IIr%$I$I$I$I$I$I$e\ɖ1L%vJ$IwIDrl0, F plx9-,Dis5A#1 =f?ݍY4K>/ +7O|I v$IR6quX=;JCL0gȸf$eU^&Xә"j0to=47@ѻF'ٱr 7Y$)B="7Km=-0:{!ͽp4ԁ#x!C7aυ$W|wo1)Q̒AI'$EѡRA 1q1@A|a>Sߏ.ϧdF$)f7{o$+SԗylHBŜ aV2*Q3;#vTTn(Z8_0gȸf$e=aK"XQTZV~bq@ vRT \,3W,)P$%arnՓE%Xh/̢cg 0pHty=ާ;FѦ38k]wy6y+q5"0RP_oCU|X`?/GݻVcNՖ߳KS򑩂ck2kiIAИ 8I\8wNg22"?}/\ݱ21tl!c&qoa Ye➯qQ3 _iS(q߸;g>e)`K_/L#zF1q;nl1JgXY*T*L,Of|Rbay߇ w 3;{bg߸6tO5qsu̞}uvNM E33Ժe~θMM1h#>yaM 9ss e׊ +x+sJ/W0ýjK=NS>6{okNؙv/+u" sSSL P#.r=^nj9Kp;l015(GTaΐq<3Iʴ\%Jq:E-gvI˽9j>}cɈlQ EvdXG_c={7 &q(|fʤhxs#gVĠ0X>-ΰ0?Ȑ2oLQ…y|[vԡG )iK@,M9jZ8n{3mrW9Kcuy~D}hGijRrQ9~tQ:g<&$hl h2LS0 *yYVN)x:0Ǧl]HIq|K &mcPivK|>t=d}ѦTH"ֶzJvVY9%˽mUjwz.Yкku}hۉEIHu[#:LᗩBz=߱ O9s-]/&jՙ ڊnV'ET"gg8䁽ioOugC}&bYҳCncÂa4>+1yͮI8~Djn:1Ǚм)#N9Y'Wa[9 h/͟MiDl8Y&)>&z6i=ɤMv̚_cڈ끇8(<{>,B?nj%IB[$!b~F+fyaΐq<3Iz=\ɖLvjZԮ߄v3c v)ɱ]qO4; Im_:uGK؁t]3#Lmp)YcoLRtakl"@ʁ[ODf}6|W3l*T!_1K6q~ӜM.jfE[:ǵQgLkN:iY,*WrT\#X SJyć #4TPʺ;<|?יQ)+S3Z…X4"imˊğ<@㚕(?I}|;KvIt(eg릲F~\:/[4a':S^bh2ovjJY>?;mPSK˴`ȆkzY6sɬ;j7y_V?Ȟd`7zs**qi=*EAQLi< Җg%bZ.Z ߰5J<ERV4[lo8>?/ێqwtnۊv u'x@$K$ k3'O=^Fjlӏ $8 F8ͦcl1[Ѫ}7Ckb pdV/pr˽7mS=fFF9>ԇІ1YRpu#?HA+ݩ7%pb]210ԞIkM 1pL!lZf" ;!\ o_ D/?skE]BNpC=|Scr)BnEU29C5d$Mru-lP>t9bݤu7qK IDATFrĹ37bצ|fw=/ޡTtHpp03!(n=WVfWeg Г^ B+f7BSЄp8!vuQ8&hXۆfj.gqZߏKYēGvP:ny0(FIw5w,߫0|(s3W3uq65ѦYеNsaT- ' s<q "P5Sd ӄ~|?!L7È~}oˆ6|9JO{nSiQSfj*8oHY] ~b_MԻ$4$'Qgvg1*҄AױE/y靻0󢞮6⛱_h 7"]jhԃq;thKZN x$~ >Ɍ9V?V]g &pNUo+0K`b(CkZ8XaʲEy64dP;N's_t)6LNї,Z|f>҉OnM6 [W2a$K}ŝt-m.ga ZΆ{ٺt27M>6̇p@]{Ľne[8iӟ9|$.Nq|-C*rɗc?_du"ߚm1<, xBNs/O7r9<}KB46)[U_} UGW9s>MQ<ʖ1QReaΐq<3IzӼ"U= Xv"b顜X#O~Kbx`[/ϯSSKZ%ߊ9r ] 7XR,2sN fXStm8jtUc|Pҝx"rW(DJi?*BU"u*\4=7PY'zE&%ЮSTqm<-a._탭ģi[s6PlÜ]R(v<{vti |ZƎL9gl::s\,m)<1pJAL핷&.] !3K ' ˈj:5\=btgT.I~&};Mڋᡄ [:(*G b Dqr!S~e'q?4 .Tȏ*̵?PWR6 2\(z6V 1Vx_غ7K%eR8!WEؠֿ so/84-P,聗 J~,FV)f)ET{ۉE.WQJEҪ,*RC 5`rBp.ᅗh-EPF6 \Ş![Sw2/-.%)}XV(Ku<=pO6"D0<ƹmrbnk `cQ#"-Qw+$Tt͗G`e&"}#y29C5d$urdґs ǵQtWud~ӿQb8Ps=g x 8}2=J>݋S.ty(Gw|<3?)Cڃьޟ6/fpj9ź3p`|zo,2D~ |iUl,_`VF @. }hdT~+" ?;]p#oߕD (-:6eӜY<;>t#v:JނZQ1gdc-gkA!z4c'!50<9%ӛ^zDv OgU'yo/ &&&QzHw55]3`>/B (J_:͕FRA% +kXJYY&g,$IFi@=寑u]8ull]3IqĿuW}8!Μ#b^0.]#cWIUBR%/)$%$Kt|}7|e skɘIRzru%[ {g5X+4h ?%azj9κnX>4caUy[Gg O%"3*x]aR 2Bp8g4xvY3 !5>ٴ_lyMޟm9V,IN#)g)U^Sωw5^?%C++ڭQ{RTUjRAkhP1CP#VljHr=?䦉}^u\ l˔6jwqu;Iv}Q5dm XUʦ[i)r$s/le&e"[ =Ubs甛.+E/idO9߆ummC\ nU3:_57u+ R>4wAmHSp'*X7>z|љt `rhru$ƹ0<4tGX^$Ao+MÜ!qL4֖a1,V=D6.֭dvs-#vGp+=CmA)[\Q,RI6l&Z!\ބ/$!4LvG 2^]˖)c3|jEi؈zA8l]voOhpm5"?""2*3پoF( d1zԢ2H%Ld(ޔ&2_9?]\Rvmj׮MeJ!/QKP6DŽ=wf"sM_:jPp;mө?mOУRjNg{n%t'5ŹZm)Uqec(5U-iV&+H^p_Ih{T('#8\%K>+Qt/G{o\Mlj<~F\+Q1f4ZtZ_';qb+bܽ-j x}!VOd4{u Llݰ{%:X֙׮79 eyvWQri8MW.I7+?LXދB"3y]vR1ޠ eJWz)n/k%Hݙm$/f+S!JĤײur j&17蝜LS〓`C?;}^{$.aΐZNb&v&9N4ڙlX<nRy 7LT[WiGӮ c^+|#!hS~1][s]\Z폃1wU䉧}Vݨ曳LB%rqV:q`( Å"oҭQ~[2} 3sA0Y+2T3N$tT3l-"HhÁ~RI,݌s㹼3Nq*ģ&|?¯}py4fN7qxwZNKO hg.=K%vhEzWmߣMF7e @idb3 VlZL[bXÅ0V̛ow4:h{Ŭn| tԃkpAhҊ> ㇮?=u3-Xyu{ǏGb0]o$).](̼g΀w?\/˧.Lm^^#3-fa4_Ȃ/ͅxn(bOJ׊/Twł/cWu' /`K*{{?0ƿDMMTK~+֟W%t;8\(X%jt`Neess$j9W9sb8S\f~ԍ)@u38q7:?V{k{?S]>[ہc[b ik>o@G`E]iz@:蔲QL\]1L=ʧ+ \~jn 3?v/Ze&'< jup',5cm7_m|^ǘa\ŭ`9ڎkCt;gT?4a/S0 q ~E톏`E %^%7Q{':‡wȞsEi㘺p=&z←>7S8w gM:\< ,WG1ʕ1peNF[w g^ $fB<$9ҥKn:vn:BBBi׮]m!"_7 d.| v9l}vBBBr;9#:yȺ#c`gl{FUg(5$lc|6<<󙽤tIMϋTq?O XkG4Qfisqz0gH\-'19yn % { 2͘f"##iذa[b۷lV\dV$[¬ztK>Z,mBG h-g$܈%1cB!vs2FCY{Ff_-1WĪ3іHfک0}sę_&[W  z$)|$UG0ݻFS GpY|Z|D@޹ !Bd ik!y1~S*W*<ezvr%1WĪ34Moili=-)=5&81ӑْ00!Bl#a-$/f? .EB\u&KSǺ_V'1WĪȪgpj!B!B!B!rHXRw)MA"as޹q?0i=_Фba< ػSa>]u՘A~Jd, cêTZj;՛vxH'wIyB!:zTXSN/Wig} rV; IDAT ,=6Z-:[SEfnm %JPJu~ހA'{};oKvsV=4·H4 EQܲbKGg=vV8$5aΐZNb&r("܌BB;,-c9ZΪQqhp k[C]1zFϔZMcX| _Sѧwˉwƛl>Z| )nLJbϛޔCyt6kT`/ԍ ܿ7um}Ih1Qߞ4ua-hNsmaA9Ɗyi\-)ԭ< :\ؖy]yqט9M^qcl~;-Iq(&/}/"RO9 ײ`F.et-jỲ{̭ ' qg";JG}Ł,]>I6ߎsեZ@^ϛΔfq;|98qtÁQ$5V~}ꓬ x ?Av6?<@.ƶh,|f%!]Zj(1[KiCV[yIGMiBuTMnaOLTߔ8&FLM/n< '߅#޶v61>[bctAZNTáYhԽRW3ugMjD>6BIndڊȇt{|.g2tsͯoFO3f_63qpGV% 5PN6lK\k5a^O5ip}|m560H3-Ҭb# /xԷգIۭ|%t28켑]=y#5M1"Ms;E;ĵ3'.ڏ_ճ^ͭ[ 8:jS,V]ZM:mB>MIEX ɋ9ݨS[v>YÜ!qLRsҥh4fߟܹ.]S/|ΘR}lQFTU6ET7RJ%Syj)}Ѩ.mRJo|WSƬWJ)e:eP^oRU)@5hkRjUT|JocPEjקP7)I^2ثXNMQmgG1շ>R U 51(C9禓jz='eSUtReVW4Wjƹ{0*{&jCLcJܭjj|JN-J]Ү*_]jYGgeSbڑJܮ+%GUZxUo'6vRҏz:%jgܚUgMJ%SVI'j*}rs%&$QLR +l;ٛxKfJ,VHM}u%핣m-5dr1FSU ~u~qPS}qҔV,g:2R}w?_U&ʡ=VQ{U_\zn#.yCkoRgLL;ԏmT+EceVQ +'C ĨUW]Ih j>RJ>6s*g:朕ٲS)ƪߒ3A[eWR(;_dGc68H٧>F ^z*A T/FMT T#v73`Fo/FudB5e{[&(U *9U{( eh2OE2_ڧ]g.Fwe}5dKz2Ǫm#*+jPSyjSmm5T:=M>mjMN'x啭!XJFNf¿˧ ʷ+ꃟ/ldu5lAe+|%ջko1>{?F:7*Rh~ MWindt3TuǪNݍ=5_SUJ)W;;wGӘT Y)ozNYUYll{m~ђΨ<:72nTFU1("]OWx`jQWl1*tZq:N.WT>.vJocP.+vSR\ި>)['?U A5RZ6-q+ԈA{ݾP.ra e(5LRj jJ{[t=ugVI^~ԬbOPWtuut&=bڹy:x΃<:y)UfWWl۬6oުv;.|p6T[Mj*fVwVP[lVaȳ1U**{Hb3$圼Z7[N7K*ffQe©Wj|?:xcK+ait4}{2}Z֮kX8wە֓TԃƙR-咵lټimcí%Z7ܴ;ZȾK9߆5 ڎЏ K/8E|L,F;e->+RxG-XǾ }ǾAЍ_y) sbLoI`zmlWOHpI.>=lo)9<d%¯2oyn% .M?,Vfէv˱hQ$7 N1gv4׋Ow3 iT(9h})xMm V_:N *j)#{Z{3WĝLܣxx]"viXǍ[9V Qkxs\_J3 th.VF{p,/= ^934(z/CBjNn#aSiX;%kאr=LoM٧DI{O\;m`/۷ ,q]2ΏN5_ZzRߎI+خGA{O؞\?ʹ9V? c8p=$ϸ84k>F+L@9U qܸp l*Ny7N?F"+GI?7=ı(*aΐZNb&ēVچ Sbg8ZCv-Fo \oWןQ܋L(Z#ESU"lQso?f`M7'}zMzJ3RN`^.;A6sRK U!Ob/9jll6, )Qʝr>zƥ(thPdZ]GPVÿIOfw.}ߦR4ԃ7{uE,],ڂlLCāO\:MۏQ绲|I}5Qi~Kօz-3ʤYцs6Ҳgsn+'YїV}L4[>73~{o^^qߵ.96~TmS&F> t||SwPW.ee;7`Ndk\ڼ6EGtR+!4bǞ5R|9> \J(ykSm"?$.]ÌsH>?4KG4ؗnk-b X6abMe+o/;}Bם5?|H>R,f)жүU(]B_WգdJUq;UjeGpܔ xn f6qLv?żĬ [6ժNeX)\TFKݹLyyRՇϙ<[sSw>+-~%(ÇO\4x t0)pw%i]{rD\FMH$+A>G7su|c-c,'1WĪx\mj8u$;%K4s4aSߤQzY\7sNB4X7Ka%3hkCzY̲P5R(4hIb3}FWΏ_9ڠFU^Ά,7`p-DGtӛ_)STΜ!|noJ^Ղ{1nU5ӛ{Zg|4-apێyRƝ;DץTBL\ۗx^}T͝Sn̦=?-fybz!Hʥ`A8ŕft>\[G|򻠶_$ )IE_=^LQ\b_0_LYO|:C\CirZCH263qelYӖLH~ ħG Z I$ezPBuk~OgDџ)_~DtwQneɊlמr ۔@Dzf6}93qgwH\O}ň)m꥿Nn૑8ޜ|,L6lAIX;%Lֳ=jW`6y?^ęG&O-a̬}8Ni]:4'_w?;>Rıq szOXrjjBĭȔi`T .7غa/Ju6߃9"S3y]corBXW{)2" v4Y-Sm?-e!ZuqPSZ=TL T/m f{M;3zaGʥz6]yrюKq,KEoˌN홼.=Z_>7vK3H^VZB,bύgIU:9aPq܈IbIM3z''28!!6;NIKb3$ 8ɦbi#'Mvf?aT xӚ;]_ƼVnGK{ZT(p_GזxǜnpW*-e|1k`i8yiĶ?v7>VM^|3oϐֵXʋ2 o/KXy/Uߙ1$FT7tw|8g{"f aH_q]l wcƊy|Q.F#uU"o%L#19\(&ف*}AҳTo&Q'J5L%raziRekm>tfڌ&5)!ZO6MK`F(_:]Kq3 A_0nO]1S,7qoq/)C\Z⩗aTQ8jйx'dӌ/Q~.sM~ʢR n[4jHu0PPɟIg]ј/9؞_vL~La:ζѸV #'Cl)CQ+fe/ H(d"f\炜aWdXA܇3@ٛ฿hV;-4_[mߣMF7e @idb3 VlZL3R=b}2S(s0-fuÖo?#U|ĝ=^% GېV4)<5%\L?tQ;4DoYʃi[ƃRa|L@Z_vq: IDATxX:|.4eL}p/d+3j;zoo'kp۴6 N& oA3+]Vȇu4ׇ6ik2QYle흜bNsk;F`YLhG(ӡRujqedk &́f`nX'ǚY53Bn˅)2yS1 ]PjFYn(zwFMHf@C}"t g^#u"/d|nBb_"Wp26էrH \8% В.g< |\1$N@鋻W9$9Cj9n-.^^خ fuݯkʌ-N5͚C8^鴠ե=R_ű_ѻ l|eZs^6NgNԍO24*2Mա1mC{+Vk)\LWHz9U1y4OxWn; Ihfv|)Q"gNNJxqہ p[7|R8<8槈4dQ ޅJQ$ڇnɍ>X?ASuy1ݷΚMX:-s=;gs7d!gc8STEɹ*Q|3l&nQ|}.˰p{lKӸǩ~iGs;)Mn￘AX̎ڔqa@s+֟W%t;8\(X%jt`Neess$j F{S;1i.Ɓ_0eG(W |I"|~ǹD#_;aiUrG[ٚЦk(DNsXy35k˹W/@ j5Dϲ* H.W5>0Lfx!FYGe@g[c̰L{g.VmǵkZ3]0)MfxTR"uƇvGT||_U= xC;dO9QqL]8FqWIJ xoRoS*:q WmczMSc=Ͷ+ah>:נ::?O:Lik2H:;Q03iTw?fEߡxC,<~0yI{X!Kw9bOS7LGwo*euEd#c=-4N^*߀rqtnz GEnؔ6^¨3nGfX=q05zj_M`c9){rҨ/s?L-ZH^|z4+LS}hop94NqAΚtعxX(~b+c˜<vy4}Ü!qLd6g\t֭[gێ׭[GHH7YzI"_7 d.| ٴ.۷o'$$IJJr-IǗXvr.Gcg%ǘD4ٟ#<޹gQ`f7Ap 1> y &jn>jO^lyΧv'7}?/>SS?*[rkG4Qfisqz0gH\-'19yn % { 2͘f"##iذa[b۷lV\dV$[¬ztK>Z,mbUcTM!V8/_`p&_>Gkn9mG$F,yBa_sAѡ<`G-ZH^Dbb"^g]M2I4QF+0gH\-'1bD["kXr"Egr$ɚw>nn]BvQ7k KM!W$Hư{hZ.K|C˒h;$yݭ,^q=Sy[ ?Ʃ+ъnLWwi=s;9 $fBXɦŷ4۴rw:,NNB<R='v:6` [v2aWgv*sM-ZH^~\VㅢLyuNb#$ U2I!B!B!B C6!B!B!B!,ٲwH!B!B!B!\}f2rsB!B!B!BdI>d3{!B!B!B!D\t֭[gێ׭[G\\ ȶ !B!B!B䆐NBCC >S7͘f"##iذa[b۷l6O!=jժ+W̓.'l߾]b!:wAC={zb$lc|6<<$O;}'1$9Cj9Yپ}{n'A$|r)B=62^ SmK=^tٻU mV_g[T5_gϪ;q=BAĦTMٻϸ(>;;z"h{-hl5Xcb-Dh4${WP]EnA)9|?{q;ٙ7ֻ'ltVk O>sez;`Vc^ ӂ]i ]#IRS]o*BsQ ·LfDqY9y[tJ=}sPȼ#̊SZ5sL涝FvTV(tz qaq93ImJa6BT2ѣ-|Fh] l KyyjF}]4Ԗ0X`Z}`ӵ^u9iמnuwTBǴ+S҆3hjz8`QabB]" k~z i捽 Ej2.f䣌hHRi=*6w'o3MqL`][(;L V3EM*Y^38?`^SMmTWe;6$A1mJؚRcZKoyKJȃvD8Eʮyo*[T o'_m< 9Kjܑ gݍBϣÿЧ;l"Ŧl5q}7̔s>ڙza=`ĩ.直3̃ӹ/EwߊOWx>`3˸|K[EY hfv4q8Vt4Ehw_Tg[>10 {\==(a:'eoVs@o~ e[1=Htniؚbx9M> URؙi0.Ff}a5I"r>K>cXϟi-stWՈNvȏ+^鼏Ŀp]NؚP;Y?A(T W!wϮ(fkR /j¼#%fNxx{dFJ񈭫h>mh Rvd(d^J&jQHqc_njøGPl115DmKғ129'c&I9VDO'#}ƚ]p>s\! `ɸ lWk훃 2ib1}uQl4^4HtlS AM,Ņl:#?/VrbjO6uԃ b5,*=T/'yy _ːQjs&C&D~u;'Qң)͑J 0x9F^O=q&g6$G YNެ$$̞@v95q'Ҧf0fwϝju6vMSϾHC6䂦%IMƞ-sܚf}Όq^ɭӇ9I=xoFȩ>xr~vgq=?yS<=LjrytȜCsCF4_؊"7nIzN[$C!b>ѓv jm В129'c&IyڵkVͳΝ;E||XvxQXDwtGckscW1hwBq3Zr`SS1U p)L/B,~B #1HHµPkk]{1OMJRc*Ky) [ݜ!UOW6ݯ&JRbȁҥ,lP}ubv a\^r&ҋ%mW̽?05k%TD'()Z_FŊ7V½0-_N3bBeUZXB[VXhj}ܻP#NAcM@lPed$/qg[|& Ľ*]ĄukW>9H&۫.>tWxoQ1&#qksu=1=X'rgH/pUeEM,\^-h)4fĪű^B=J >;6FT4um"o1_ s7mFf#2oVf!S⯎ª4q){ "J$j3v>UXCR& ҕ+eBh&*j[{kJzD-.eDbVDc!#7gz~>&=i}I+xzi]5fg&LxFSho~D,&Q/"iJ9qDKBh/iD bۇvB?ҴS< X6-*^d_Q8>GM+~Q⅒G-&̌0g)O+q3IPKH_=?kJB)' Vcp0h\0՘ ĈUӴgԮbBXT(a'LU*aZb|Wpvcj;WOWhU&]k_N^Yam*s'Q"4/$lB$'>hPA:X 0uu/ :qw0{_~ͫ7 6kמb#: +P>Go+x [C8YFX&:W =(~Y[ژܪ(m~ ɼ\c9iKIQXES;Q/PhڏBSkq"Y!ĩ) ]C1TJ%ؽR_.f ugnɼ^^D[q!x8~Oy*Cҕn8z8q8|8~x=ΜGCa@q}Ӌ[caxZ\B ,_a]A2C5dOa+Ç{عsg72ڵkP7;7 tHS-]zb3IZOs?fo nwG٦t4AAA**PXR\ۙIɶaݙ`S)mQ ŧO˹kR=dk=?C'ѥxfqQhl)b]8UBqE_T>|:#<#xSHJK01I;|z0f=FOD1E8a@2gєijiؼ&FG )\Jzi͡Vp!&̦x-bsNTY8+pjҌoВ@BOsvgQjɈyٱuCk^̾jGgPCQ~lRufڦ;lc`JtYm~TU Rdeڎft=G+3r Yc\;ϥѫxJWt`DCCy5(_XmZ$IM}cg6f,ˇ+dRfc+pv3ٸgۗNG?6B %iu4e dQݱYu/6^d,I&dj޽USZٷbϤ#zQEF,Ʈ ],>B{i?;z\ώ PNA5o!uf66@chj_y\2K?O|dn ̋;=,~n ^>ǂ"n)2/ 2H%2F`V +Ps)8 U`_ AkJ)!EF?^nל1+\OxWfyc]}xcOZv4'M@"cZupo ԿjUhK`=u= }R,ofi<ە&a|oԯ[?^亾k|V%.uh4Nn|DH" y_OU&5y->msϚ΁ifr?mŲvV9Zz~Jhh4 '%̩(V:P;VK/O ?}eBYHqk`ņ5*2%3|!7yFBSؔ8;* #l2#A*^ӉJ؞%f>;0%t A%rبڻ9?I<1am| O)P c,-P6K,#\3d zBಱ;}>@%0yt?I&>bY]du%֨8.t(2',I~_eիPR1](0-ߊ훡Uylʗ e/N\_3>d_ijP9<5P`Y>>/+ѝRŻ%ӟ^[WLXeTZTF9#]B5S>ϧb֧tNR>٬_~+xըJ9\.~v!X1&W^S~\靖e`DɺҲk3- K M_xH>k-Y IDAT0RA9?m踒UP`՘VGظҁM[름&pF+s"5+ZVӝ~'nMU]?jP&6}wsڏpIݦº iJuc767朰kY'߆z'2qt\[4Q-0 QƯL%kO-{34^8CnKoMȼo)1u+ /cY4v/5DF(p*_wmZI>ĽQ8M)7*m1llPGrb6$ %7UR`rQNE11r9'c?d\sNL^@ZZmJENyL6cq޽/#$'H%2R`h_dԆ-|S޾YwˍN]kd+=D9U1,O~ ulpB'D klԔ?+.Q3jm kIdH lǍ ~L_ IģuxSR<)3+ [0)4̟7SƝKIz­!AE1R{n=K慒D!I*A129'c&I)'r4]$S· _ݧES`l]RQ_ tYڏHɿPn,yFUm$[tYbnB))fZ"k6ptVKGBGOR.T :KnLN5땳,tgU.1I4XZ*Pصc@7N͗N1z }ܔ(4J":*8B>$LRFg)J_Pg|udxao( /Aޑ%Ihio8rl<5%}Sҧ: ;щgRUsgqTDic }$-=7Ƭ OzCwV9u#;i0 #^d[ϝ]a)~jFè1|3{^t)qwI-Wjn*"ZKjXX( 7y:Yo*1҅HR~c E t$²Uj~!,bɘz$1*c tH^.{z=W[ 9EZ((H&9ۃ dT{gթRWfE;:y\HGw+=ճLyuDf#_,/^WJaR;]'άH1\БYyXJ}2- U֭9Ci=ק3@q؋vd6UqA;7tNRE/t&) 舥}}T_>elLkzMؽ-vKޑug^y1YZ$QgopݪXRT)Rĩ,>%_Ri! Wd kɘIR OkKDqdD<)Ayeb"ҔRpf--NGF~ўSzeJzofD¼*URrM KK8š)ӹ s7l\+*94s #qes!Bfȳ |'mS07S** FGYR SAtT4zTT_͍쿕֡߾R`zJpiڌFO z뎿qΟ=ٳg9{fj8uR&]>nN)bcb ԛl(ڕq?9=ʦ sAӗ|Ck|8m:?+՟gg*֢l1+_ ?rֵ:أ@PyVbƶBz&l! |]r~2g^lI@7[b9h JYǁ\7WϿn2wۭ ń)Gp|+@U|SͰfsL& 8Ocvce= w61<){(o8a7՝U޽@g_zױʤjGv:-!C)uGt\4J[(x췎M*ǎU+c1gUfp%%Ci~u,4q~ت6W?0z.Fla|O6sE.x`|Fc7eژV Vzv2k[p社`D?Q8vMH1uGvc3 ~팷\9._+.]>d8`La[b:>.](kkDDpXK]g_7_Χb`8U,OqOY/D5Q`Z:u}M30c;p?MJϙEU`=wZ6PTfW"9+4dE22/?fżp ?MptoQ6/V%MBVAIv3/e(dHOҳDT{sO0ȸ朌$IX986E6֥4U\!I8u1#9s2ݗ>AVFJPeQZcC󒩏I9?it%?UU8;>l#^i(s~eQk<{+2},V8ُ-EKЪM 5dF3e5|֭ v+,w4A4[@mf[괚![* .&vA5ڀYT{a})7b"FMm FeXy$W߭L2Zz7Þ"LZɗQk'o!{'+"oҽh掔њO6yFR `Ǡla |7F v_ȶQ|=Z|&z8!\WN$bB/9LAIܪ`^<Ǣq:G:[R9dј/G=t"Z/"ZS:/`mU@q:Nt}>ǁmjt˶_:P1$.{AYK1d!OؔH+%ŲZ1m tæ3Slݨ[F‰cƲ|sK]{œ? -,qk8r]@aYq{NRmd~]>ǽjS\+c¡P}wN‚hЭ qz5Fmoӑ|={ m'bـ%Ru,$%JJa]0Rcfg ]z$U˫i&/4kݠD~a|`ҝ z_9ȯf0tujm֒lȝkb9q ?܏Epd,Yh~G#\ڌ}r& q]aB66u8u2jZK`xlkNneP_|4lHB7GaFҥx|v/Mp.Wܻ͛q:#LKخ4}4ܺJ;h1BmfK).t129'c&I/nάv#;v̳ ݻڵkg:wUb6܃`i;^iX]vBnWKʱ\;){;G^dF!\9k?Xx%-57YW{! r >&N9mt:ď5 oNa;N[0{3'bWc*ys1ŨY<;.(]<xaq9SX}憩iwzz=.]Yf7oƍtҥp4ttn Ϩ(Y ;h.=~]}.=[ql$IRaGDr,CCh,)RO93"ǐMR!yDŽ$IaK$`yvkUyM*8" БBaJedXMkIZaq93IʱD["'7Y.{LΜeayEDkkOc)yM$)|Hr7T/ 9ur< pWNe? Pe@A'H$K8ۅ?PȼD-NBaJ07']U]OyǝҾ)!s2fs'ٔ8w_YyNt:$I S=ht:m(~C :$I&̽PЩj" +4t/Mgcyd kɘIRnǟ$I$I$I$I$I$I$)M6I$I$I$I$I$I$IʡɖEJ$I$I$I$I$I$IқQ7t:]An^$I$I$I$I$I$Ir@oiڂܼ$I$I$I$I$I$I$"v#;v̳ ݻX/g$I$I$I$I$I$I*k.$ؾ}pss4[z.]Df9uoƍҥK=hV5~TشiSt!00PBzd3 8a!Fr>7 63oxY2ޙ{2aq9XIް.R$I$I$I$I$I$I #yM$I$I$I$I$I$Ir@o ! r$I$I$I$I$I$I+I6I$I$I$I$I$I$I!yM$I$I$I$I$I$IrHN)I$I$I$I$I$I$I9$d$I$I$I$I$I$IOFQ~+'tJ$I7IDs1Z@JX[X`aaM+ec^{6̥]~YXy7ķlHTDc?Zf]c㤑:M!Z4x2>ySPȼ#Iș<5[GS2ל1*O I)x.L[M<@RgwoÜ%v9y&K$bGOgl %=#0>}SR(oQm|pT2u׮>ӛײJTjǩ []&.'b=Yr,gF3X?йZq5*Ll߰8 Q;"x2g’b^xڡQr>iߟE % E Np]NRؙQQbSz­QYϮzx*8[1dL QP?o3Mq"MZؠűt س,;3ãsl^ȧ]^W#:b!?v{>JmvM;KbkBmgv PI-)=": JGep`j[Ƴs-]C+Pc#6.^j?yGJ(̜Ɍ[W@}:$t/^k,wL?9eM /xw5n ̋@S1 ɵ?]yLmQm }-#5& 129'c&I9VDO'#}ƚ]p>s\! `ɸ lWk훃 2iJ:LCH=7CP -sܚΌqX%sŸQIJVV M;bv0_y\0ePH ɨ}3=ݝ[Mn_4:0u8 0x9F^O=q&gǣB6C=u~YgqbXVcI|~ԓ>=Dsh,vJOV=RRw'udJ^^E7ӓS2BMuB<_0g;1X5i,C:uy. lVDlíL8=E^ [4'|:i$ڃӉi40ݍ9tk7307hX [ct 'F^<Ԟ lDJE%:ֈ#KJ^UP cI~O!lLL؃Ew<NrG)RNAF6XǨcu-B !E=bDᏞsK{!Ep?傅 ^738D."Sڴb{ /P؅ IDATƬn?CSMƮVAc*7i|Hƃ\дd5)нXv@>g8/L¤> O۩.gr>w}?c0/4wwɼ׉=q5a9ɝѓv jm В129'c&IyڵkVͳΝ;E||XvxQXDwtGckscW1hwBq3Zr`SS1U p)L/B,~B #1HHµPkk]{1OMJRc*Ky) hŕk b LWjuY1xclI~bFTF$}T31?"~ne,x ӒŎ=#&T6^I+(<%SEh8$5zpakai ݱL]őaC]J|~()L&O,zZ "-cd\R 9BfNY+]Ry5]BEL+K;:q~3ajJ, &^*wHB?+qY [Rh:Uqi %c#{8*#*?HDۥو̛ }WGGa`Y=sm?;q_VXhj Jl]Tˎ'sv>WcI&MUԶ֔ wZ]ʈrŬBi#Fnȟ}LG{ KwRߜ/ςį͋#bE饕vՄ0-!CN_I,GXVhL+)e/Q$~%>;Z&m lKOX/bڴ;{}5F 6,FEXGѱՄ,%|; {e}V3n&[6tS+~W1)' ϼsƥSpNXuYOMRf!Dy [7Q,rɠw 3[{yмpsjcv)V> m,Ѿ yQzx+=PkUj{EHmB{6&Bm"*j(JkDB2/Xv3D}JHi%[۾]&¦T-}a(}#X1b)jan_JTlXrU+HG{ Mٯʼn;WHlRTr6f*aΠ-tDLl)-Xm%W,*Ғ4Oezܒy1S>&/Wq!x8~Oy*Cҵtq"yqaqq50柣{ .9!Ç"?t7.?|Aetmל1?n޻w>ݻbΝyzP>k׮@<+q"'*3[c=f81h=@~޸ݛ{\'>M)i:UTÙf7~3mú3)K:ʢд 3%eTO7#nFSA.UvUo/c dI] >!(xa socNSch15 44r0vӈ;5YƿQy'ALh.ݫ~8j1 {4s鈻syKQHJ@Ȗ"6iE*U(N Un<}u' (r18vfW`l6˷A' G$>c}ϸ6|ٸ}5fi/_>\ r0$^a#=ؾt*=*jo_Ӕᅤ1+Z.ҭ4fBKӬfiVF+8c+zp^m*L'Dɫ{VMin0:u.W`޺-lnCl8GoWs.VND" .xbgߊQ>ޟW^ytG|vcf'taвEזo5s=;6f@9rp&)mQ #D];~ӑRݪÒM4;X5&Gr:48寈Aśvk|yŶ+͆NeَCZ&ɶ7{[-y_3u/s76B/cAAyV$#q+G(nO|*md0/q\Ŋqs" /d kɘI'<ͫR1ˮ]k:̧%[,kcB 5*?xEqT&=o_7{ [Th݃~Ы7$F{{9Coq[ 3~/6VD_t,ٗ-Oݟc ĥn=<1F5I͛HI$23BTtJ7gG!a$M&xy>Yk:Q)bۓdՌ)St`^HwxD\"*gJA:3C4Q0uھot2bZex=L֠'. ^Gi(USBCQ8.fNEyls$2`%-3Z ޾jQg}|]_vbM*k9v9-N\_3>d_ijP9<5P`Y>>/+ѝRŻ%ӟ^[WLXeTGZTF9#]B5S>ϧӧtNR/4 @Z;FWv;8,fa -kȾMRRD)Ie,EeIDIeI~eMDX̽ 3^f;-s|=о?3gH DOI3I;NE,ٚ&u}oCm[x/1]29eA/R!{Fҕ/<În%ޚ%,k=LxGFp1N:psn5lBp3ԯKSw2~oF-M~4 l/PY3'&1-_*G7kBinӆTqԪ2f E5'cd4xΪ)kq3=meqHi,q/ߞ3X?#&ZIί)H/ǙG צ),C؂hޔi߳HM=X~8}XJvmfE̫1u[iUw%#7?mn-A ػO|+F$rҥJl=Hؼ#_D@1ɝrJ OH.S<9NBP *N< q, !yqk䜧:WDn'K |yn6m#&i0)L|+!_`.tgY;qWaLWp|x\<].Lm)fa' {wkd9COOL۫s;0羛x]|sYؓ |*,yz'1&w*KDMWţ F3Y2N -'0p;9ӞߊI7Ӓ (:0`u'Uل؀Мn{RW6r76;g8Q]y^^,&7.O~-q%T<]%e03ſ1s%o:]3huw/fx~I`@y;>utہ|i;v9ݷ2oAJy֘?g3Hu_HQݑqܶ#< ///rP_:јąlqq@;l v\Oר"vJd۷Y9Y첌 K" Vm0o߶闥|J% nHq;IPDgO&>cK@K:Oy{rʾy l_r+ l#! qi ŋyp$v K%TAgy YLX<ـkWKx$a+[ԍP;'ز-i(Sה+d"FVf<לa `9dJ?+[2bgѶxg<.I88_Hɏ\:ỉZA&1%$`%.\a=WzjV^Ͼ36gJVfիTg"i؜.6 ܀ej3*H\Ϟ+i4z" j^ЏΕ6әQ4΂;H }frY.ۀK3$,/a7=1*w[yXt!|}M[2W85' 䅐eX:p38?|-Ğ"wb8v"]m?%C΁{†z|{+ȑY.H"7nw"6;%ↁnex~D 1^|4a1@ȦyM%([snv6߲׏#ͷ;N][(ُ=ɃOB+.wyF0Gebf9ԩ<|v{97'ݙm FVS]ҩ- a#>gxgd*pdӴ^fεV'L+̒]g?^2%ujH:f40 }7/Oѱ6y&Z`J "m"&?*.OqԍUXPMBz(&VVԮ]5zLu7˾ͩzۡ/[W~դس3)V@Sv5<ȏyV܄HMj3g!wG;yЂma›2J9[ȕfۖ~|m?{}dJN"zƈ=]X}+Z^]uy] ߌML֮Ƶ,{ъO6gnJset&O[었=oO/|ofk43o?ʿۓRcM`J!Oxfy%rkߎ) {]qSwG\6Ow{ְh )B'1X(Z[ϝ wqXwK=P4Ն!3ַF@pS/HJp&#&Ufq:#b_gyV89/SaJarRoz؞`3iW(߷5W2nC _:р BOS-#8_x=1elߞ\P8W6a;k]!6~>~X0Q~teeǼږ5n6LObNh4%,3yїA~N-;:vFrCE<Ňjܶڄnk&: ^B9pl9gx3-_Q^叾ѳBȤ|}fٗaovމ+ W &h IDAT1/jˎQ0by',&ZęsN͖)o [2󀑌Pc(RVq70x"G9(^T9) d;x'BoojR)|3-"8/N0i})Vo,ed ssYfr?#ӛf>?=bϙ|UKJ8[gjxD>"+ץ<۱"l$'1Z(_|O yO?C]y|70hRo<5g(e?ǗRZ8N`*\·\IX:=F7#!ehۃn\bߕO~0bX&c*Ph'̄bw8ox/kL!t4ZGո/+L{]4 "Du&^YFu[1j #`ж1 ,M/*A96 ^~-Wд[`xroX@\y#uLӦ:WDRptˬ9m۶ͰX(~:tp _>x(CqzͦkٰaQQQY] hs \֝đ[1/ᦾ<02΢EA_7L2a:춎 P7e'oV.wO})&ߓŁ㖌ڣŌoSaTA=6"j&O6̡zu,dؼrJvb7ݎngϞ=4j(K[hѵuMEމ6wJ #-' Z- G :~OO_rγ_\%>6=;spV'M̡zuLyНlf궓2p""rNqĝccx~/!"rj㲺'_Y],q3 "xH,A<Sf իTg"wJ2ܧ.^SȿEI65knO|aדia`ۯ}m5ku1p""""""""""""r'nNl⹌,͖#YdZYx;brp_۶m [+pϰd.V\I ,^ŋ[lIͯf5jE-"::.ilmڴ믿Ζ]fذaBs\~;w?w~#Axo~3c>[ﴩn23y̳aÆ.]kݺ5axlْ-[b۳dIO 1֪U_KɭN6q-גlaТEFJJYdӏ""""""""""""zZja"64\dqR&4\dGYL6q-&)'~hMԜ?ڶmf؂WXaVFnѢEDGG֥7mv/Vl"""""""""""""ّl"""""""""""""NRMDDDDDDDDDDDDIJ8II6')&""""""""""""$%DDDDDDDDDDDDD$dql"""""""""""""NRMDDDDDDDDDDDDIJ8II6')&""""""""""""$̚]8wNʬEVܹ TRN1KDD\█dY""]8T$vڅjȌً8l֭޽ҥKb-.DD$H/fݍLI;w:u`Ff^DDa+Vdi~%""DqKDD ,.ҋYw#SlNRqIDD(nHv%""Ef ]idSB1KDD-.DD^gd7v'j/^Sܒ4mxƵ'*e;3I\~SqϪR˯Ʊґd]}8w_aA ߻S\drh:,9ƕ8pVc.]WrQܒ{}eOS:RemU %n{m-cSy@>;gܘ4zGP<" ?#B/n!U֎!z974x%-qE~z"otPY/DIW[L=qKf ΃fd 7"mUs,,YާMZ̋Uĝ;_qdF4SH"'-~ل,wokfPɶjs'$4:8 ?;Kݸvi+۶`f< cE9ھ1 x0{Oqdo>=!K:_ ?AX0&\NV>i^X/~9x"4/^L^8%~*$/J7eǰ'fوT/"bΟV0n}lKj('>)VoqۍiAբy % ,Mݗn,HqN>КJOfs)؞j#(8U?? ӟ2s{f)/w}fԋ!>exSKH oՋ!0*e\6{ ԤDh >-QS%ƞyŽitkXsC{r bI?!-Y6@_|RYa`G}[PX%rmSR+_& L-EP4=}̪5y<>=VH`0- IߵǰhV.@_lz߀ɇlחE"rxYyai 㗇QyخcoFūtʠxWv{)n^Ԑ2 tnEo43fqK2EH:<NR+UC5 6iަ+ 0pX`;]^b|F٦3{O/i`XOЈ֏gkYb!ЕOqqK{8}Mľw|٥x} "Z`{=up,=LmW R]SP0y} *D6YW|2$6_ Wfpz|?U"po>^Ӿp^4]֏VO.!P>z$^'7ń:oXZLr{@L~31*0N.ƙ]?TF̜L8zawٰ|5/&L=J@wbw~ưWWX%V |\>bO=sϩ,kAfwaf?քg6k3S܇>!?Rm{0k]Wm~EޛO˽K,֑|&0Ꮑa.HgGS-w#2?>;rԇ̨xu2x%-za'IU$gq}tl ίz]S~ N;W/n|zɆMQKfPp qg?nû 6]|ptQTp/ymoTw1Q;fbУ[=}\qEY5n\{ߏj/h7|ϛPm޳ox&`2"Bnދ-U1f^]N|[MKZs.wI-&a\?S6h(=+'2"67~/(&.'~^ǦV MHV'x؏`NGɽJ]Ѓ Ӥmw"^,;@^&ɖk+NF)韫2۵\+!"fZ ~r(ߍ3A¦s>cx3?L@ֆ Bwc~f~[m1 7|=aJDa``«dcnVvˬ˷lD(f~=Ju_gU/\s06na3s`oϫΉUU`?Z u|wJYK^NK!x[@ղo,X'}&ώA1KmZ 7sEʶמ^z4ɗ%)ylV~dK,G/R/@%&Jog4NVNgb9mШA?༤*C}^1ؑy ۟r 6FdwU\E~ S1 ^5ӭ f}?u;(-4<+D,`˨e5K$)bcN'0l|6szDض,R' OuWbmcKm[9l+ qh&'ǖ6S4:y;Y7u?=%!/;8yF1ǜⴍT[Q]_F^N%!ޝmέHȘ؅#dlVjsoӑ#:uOw.ؼsAtDDa``29?ԧ-߻wB(ܭP('lI^Ԥ E 6-|D^ES#Ii7%]7<y~Gv-)~ KuYqw]OJك*;nGJ7~='/Z}H/2;NH;ĥg87E}z|dC+/d`77]ī$Mc)$^aвR"66*wϥ7x(¼vN6XRSSzi0 V0f1IέpQ cIlQ?|-7ðf݊a9>~|/G^aXwXݨ.#wĽC7Vg2{~K2b܍]N|)H7o儹%ZRa`$Xᆻ%])y7L^|?0/Koͤyd =TIqgLNޣ%YFpO۷+x>ٝ}mv B.YЯl&` SS`䲳h..$o“4ycvl֔!evGq1G5=W~nWj$P̒z_XY8ft7!jnnI&pMTfft|=yk8*X8:y:NK edlV{lBj6+L1! :ǧKNТK$ʸ٪1"%NHyIJf&.5E0Hew Lx㽄=i-ЗӨ| $v O ھ⧵yIu78# HXɚ Gz^_[5K+.cǶi5/7Lohn8I 0q0!^z0tB_Hza30n%> 5kYr2.vnLsK-M݂G:D1 ke.\lZI*,^k7fo^l[ Y3)sv;6qS-C52(^ɽGqK |fLe*>ٖ+1 #Knn=6?gOdR9{Jx/~^ʋ-5R7eXTKl='j'cSZm㪈+R̒Z lx{ _ #ipO8Z~u {{a1bc6GOJ;+gQ濹h}ܴ|i'^ϖf_(cbU c1E*SlY8w=^ ӹJٔС; +wE`JtkS ;$2 ۷"MG?A'j/vCTl/Ǎwg)Ve?Ȋe IDATGq7:[9 J`㒹lB=_o.F舾}ӆ3+0Nzq>F}2./AX-exvBo6GhjG~4X mi$[)\83f(zȇu/n}=&2 ܣsҿ'ЬTC쎯@eq7`Y K|m9:4bܑ]?I6#c; '8lЁI |S0 oF.h7 zFj~qMW&ϏvكՊh̉Yo sc;%'n}v0|ShStz'z=xG3ZL"1,q]Ip6+gq>7EӍC>5B qXmCGG֩oOżn\<"й- >Gbm뫜zĿ%٘ۖ,:Bͦ\/Ҥg- PC;Aicqֽb7 ZOyJ06~Η3% R|?چmf+Or͸Mv-hy?+Ѱr[Nz9X"GX3΍RaK+NwhK|~Ƙc جżƢhE^ϘstGy^/V>/uUgdƒ^Sm0KWͦ+ESC`i87e"ӟ Ӭn]4՞%)] A߆[q `<ܪ5]^'d<0WotED\@.&9W~hkZlG^C&?g֓I}?CGymp7a6XL}x4en~>̿v_1Q'٧֧3Q"LY_f =TJr*2d+,xx|36^ϏϞ+Ɲ}e"WY:;AҪÓ gŦ5L2H}}@&.Z\;27UWqЊvdڷsU0ye Z?/]āh&|oZW![%)&lئ/'lf7Ѕ֭;g6zǑX(C3c :4^tE*F;2׉{^ \_cH6虇_۶m[&^hM6n#YyWLʋl6bbH ^Gb#w18EqKDD ,.ҋY7[hѵuMgdc4[+N9pK4)l־&rYrqۏY_bax\53^g.<(nHv%""EfƢL}&d v;ĹXr\|>"u8--99|*G0ᕛ2at h""""""""Нl"ف[y>|LtuHjrPgb J#&DD$P"nd2ie2fNPPP(fPB1KDD GbȔ$[`` {dɒLXHv;{%000iDDU(nHv%""م#1ndJL2ܹ۷s̙XH L2iN%""BqKDD ,.Yw#ӞVl̚HRDqKDD ,י"""""""""""""ٍl"""""""""""""NRMDDDDDDDDDDDDIJ8II6')&""""""""""""$%DDDDDDDDDDDDD$ܜxѢEUl$[j2"""""""""""""نI6??,Hg8II6')&""""""""""""$%DDDDDDDDDDDDD$dql"""""""""""""NRMDDDDDDDDDDDDIJ8II6')&""""""""""""$%DDDDDDDDDDDDD$dql"""""""""""""NRMDDDDDDDDDDDDIJ8II6')&""""""""""""$%DDDDDDDDDDDDD$dql"""""""""""""NRMDDDDDDDDDDDDIJ8II6')&""""""""""""$%DDDDDDDDDDDDD$0 VfDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD2\oCIENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-codeplug-extensions.png000066400000000000000000001070131501654372000235660ustar00rootroot00000000000000PNG  IHDR_sBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeSo 14 Nov 2021 16:17:44 CETVɋ IDATxyxLgL6H"!FN,A)ZJVRJUjZJUꢵP[B:3HHdL>ו뚜99yə;""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""rL'fHv11@p2P4]]]UTWs62bX0LQ7nv?EFFkH&TXpo11XL&\b0ё|NNsǏʤbF*Xb^^E\fl6g*pf8cb  rvvv jF+Zh½]"""""""""9µ EZuSemBhE7n8d>Dk"##+/?(b&Kgȃb`go_ fkNf+aH^e6NkmBl6[5ZDDDDDDDDD/f9?V.lom,""""""""7o3\3 ^FrV'EDDDDDDDD$w7ӵ` 7x.,Ȟ|qʼnL8%EDDDDDDDDOxlh &,do{ Ų$,Z5%b)("""""""" 1u4Xxpf]vy,QDDDD<{>ƓSw""""yRurq6<罉oS(]-r);{P4k14YDDDDDDDD {M59I9g[K5QW׹ew:r>`IH":,<P }VBQDDDDDDDDJi% K6fh?.Fp{q";gk^IJ&& pcA3/qJ(<(܄*>^'YS" J]jK=?O7k?dC\1F_Z|. PD!\crK~q>ĥ[13R}5@BRDNR1Bf:gPySkL,sU)V^&?"XGߚΰfQ\ #bToV-F^W,(ES˟%q ])tnIw Wo8j$ܔ!"""K{1`@'Wv E-""""0y纔rSzXt>Х;FSk_yybS+r?N.o5 XLB=x`uTJi|Jg:kţWb۹EDDD$PE]*Tp4`ú!ܧ kX{ID3&xpqq`ı17(v%Xեgbs*)ee""""1aI|qiζmI@K2F  WDDD$#rcuBx](kTs&&ΩxgR1,E w%EDDDg1AuhV?_!>G֘mKsH䥄av cno4פ]s&&ΩБXywVx%EDDD3[s/땤ˌu\G q,I&w*A+8 'O""""Hlģn8n-f"a(j*vI\N6I<X75|'(?΅6ixm~=w{w/ O""b4Yvة$z|6h7k׬ǦglD6"ǜ֪"""" CC#M!"""""""""yܼΜ3\ßk1Qh>6Dvs"%EDDDDDDDD:clb׆?"Sqv~33g_Ȝ5 WBnd]6$;=E ?EM%Qޯ8nv?Eeh5|bՂ.AY|ɦĜ, 5{"ȩE y6xR~C6ӠZ k0"˿M6u)_?Bja2iАFM[ѱ!%9ދ!2i?K|2+UK]ܟOW'hHoq0vۇRhS5%c^clf-Z|S{#wS%Yy!|%zz1g.JJ,gWP ^EGWWr,*V9j](i!=W#e]6 _[:6';;XB:IZ֭@b^x+CV2a_D`""""""""s(Z0c2aj8 vI׾l_ϼ947CV_ [3Qxk t[5|r}gp(&[7NvJb]{mI w嶄nfxgYou9`ijM)G׮$-ƱMl:ưzZ0v^ޭcp8Kt _bg+YZּDq`p'>s6Ͳ7peP/8 0ZKC jV.|Jb/eЙwoz=96,D}'&6K6^|wɉ}9Us?5K)<ʦ """""""A79f#/ߍ>lNr>XNأ](hs}U|={S8;ԠN%{V>&(}p1+Πgѿ=PSN:2we=m=ðJ尋?w[CnӎwժӟO狫 zMv |߮;vd]jem<O:)TD%du\V;4S ^_t"|ZD"pmHq Hٍf-iXթ{NzjP;fh0+N}Viuޤn7~A[L'GR/۩LCڴl#Тu^Wʌ<9>行HJٗPtj[k8Hwĸcz4 *yLlq =boq![03X{^0aߊ.\Bf6:8).ןb^wd/|6aK389pݔ{w+qeBmАr~"J9,N'A\cv1pٗ!l^So}/] 1{巋7ktU iuɴ)g3x[^VXGcx0FBew/"""""""ٗPă5jQ%=S`QyzZ Q*k7px7-Oq;jCC^8R$q (#ۣ]3ܰeZu;`1[Z-/\o$6q1+zBmsZ]$;.uŀ`tjƓ1>]cO19qg]_yi63{/fͱrd.2]ZǶH\}^.>1礪ughgX,i%iEDDDDDDDrN6&g0l`_Xi(݅i )Kޟ<+W8VB:+кq\[j_Z41x;d\(٤7o7N﹏xB6jCjaoZV%zl;+݉|TJ/>m?5\**("""""""/F<=!lz6YNybp>9, _h.8g>(ud,9g79ʏk?Sdžpo \Õ#&/tS=Y)L}l ~Q4O˝V(ӥ]_+qeB>څV]LpRLfф^3xeX`re7Wdq#U7lfTY/N]9{>U_\-9%)Ϟ_GA%"tVQ{"ɕx^,i9t3+@¢D uuy_GISSDDDDDDD$~EaqOCcp,M͞DU< vT?f h|^oF13*.8ߙR%ׯGÒ3GׂK y{7^9,,׎7=F{gPզ4,=3$WcI+38wPasgǨ{Y8qv.Z8񗎲i\+h+*5畅xݘS~a_rUVG+pcs3VN|QYOGT&|K+Y-"""""""ʕ+{5 s7df ܎GDDDDDDD* lMo"""""""""b5 y y۳zgg"""""""""r:痝q}ꄢl8DDDDDDDDD>9EDDDDDDDDjJ(լlX3XM ECm9}O0Ak1E̞=p>޽{9~88::O&MhӦ u֥pիW9y${e˖-?~0 .?m۶qƔ/_>&d6dM{ k 1dUXXC/[n̘1B eS|ʕ+ѣ/2>>>.̛7e˖q…tO]\\prro߾̜93񈈈ȃ`~o޼''sqqIw7o&=f <ww>S15ĄNqqq8pe˖n:_cǎӇƍh8f3?3 ,`\pٜFoooZj /@͚513nZVmȚębȪ}ѫWD7˗/aÆ~Ԍ1 =AAAL2%e3Yfki{X,8;;'%EDDDDD؛~yz3!>"""r~֮]ˠAXb=7n`͚5 ;we˖1`VZeuEoQQQlݺ_5͛7sŤ/\7|b#GqZ*)[1-[fv{jH!ϙ '^rY鑕ӽ3S?!!!3aڵky1L)Sf͚ѡC4h;7od߾}|9s wL<3gf8cX'NM|PC VzDDDDDDDcmƑ{z2WWtI]!+b'ѣ8IJ&6jԈCҰaC\’%KTHnRqƩ>̱&|N1LDEE2lʟ? k[ 3W^LFLv3f̠B ;vF@DDDDDD$*Nɇetރ0$/a9RJ>M6%ެY ZpaΝ;bہx3f ˗LJ^z)Z?;3n2+Ku7p@IE[N2VK6l`IB seicNطows R] 444… ӨQ#~DEE1gXry96=YCQC.19cAFŦM9rUDŋ'k =suΞ=˹sR(;O kīWСC;v,cǎeȐ!ΕmѣI=w ,H(\pKDDDDDD$;eia%㜔&ٳg3w\8 lz*nݺ=~a2صk/"-[Cԯ_s <ȦMرcNJQl~`L8b#00˗~ˁNFY=T^vN1?\t-Z_xy晊͚cVXALL]ۯ\ƍ=>&&+VSOe8l޼9ijժ% m%-y96=YRck\v-C~5yd'՗ÜdooO׮]8q"7O,ZHXH, ׯ'""I&Y곻;ȑ#Å UҿßvT\#GҮ];TS#FȖs>|8`Vl߾_~5j 6lؐ|8̝;gggOθq7nӧOёsrС #^.\p״ \y %uŤ ::I&1i$Nudd,P*'19[ޞFQbE|Ioξ}8v7o$_|ӰaCZn''OᄏkAl*̉F#]t᯿b֬YDEEe:t(;wk~=2e0j(x뭷زe ?⧟~tL<-[I ѨQ#z쉗/]Xp!.]?8880p@5kf+\0Ν/[nZI&ȑ#3]wWw˗/끋J㈈=Y=YXF;ivbs{&5ʖap3GZ=hJ* 2Ν;蘡f!X-՞2j AAA3VZ`X|'>Wre{ϲ3^o޼ɶm(\05Jo2{줶c0Y?EDDDDDDDDĘC E"""""""""b5͡("""""""""V:CDDDDDDDDDV'5kq}blllv!"""""""""-""""""""""VSBQDDDDDDDDDXM E"""""""""b5%EDDDDDDDDjJ(ՔPv"""""r9uׯ_',,,Cy*Twwwʖ-ۡCL EɐSNa4 PJ?O\r<2P ͛DFFfW<$WWW\]])Q0~mOu5>[ANQ=<չ%וLEuaǎ<ĬN(Oٲe1  aXX,={Ǐ:!կN&Gu:)眧:uaιO&7oL 3h4U@k{ӼGIQݪr9m/NO>ۡH.:L2FbbbT@k{ӼGIQݪr9m/N!Ǣ<YC2/TCk{ӼGIQݪr9mOu)"""J(M E% 2i:Nr;⎳n[|ۑۍ`ܟYr;aVoNv(гqG'9xV.Ag """bE~a+/n:)=V!O&.dɏo6BӱTQyͦ/O!6f!t@jWGB sN~vukh}WÒY2f]n#IH>hǎ="Ke'xݭ୏(*TlQ4smٚP\%#ѴZ9|JPr~ X-tKF>3Ϗnp_9Jy9? :6e})V}PNK 7mx{J|eE Yc̈́/zꥊSX1K|F<{3ռi ~"~)G&y8ksG坍!تShZYuh14(^E)^ >(-D{/%|JSN+9? t?[S-VܗӦp>zVߟYBٲnOЦ!`~ ޾muS>m{dsn6<~֣bi|JS Zgn}[r95;wk41K >a;3:y@_؆}l/ti+z3|71mϤuazX(ϳh5E/lZ4 -UuHel AQ] %T^ckX:QESv4UvFyg(34.=}'aQ {ugn4ݟqP .sƁ8ww^gu@nǑj¾,\מdɍ??7}*e_<7n0j9pTzV~&ǰUo9Ȱ8~}CӷUVޟY.~v4ъ79[Q>hMdǤ{)˵qyL=,Y <^|f%s;bM'm9VzM{_o#K27KN7V90+ k(n1!GaC,g6Mⵉï!AAS1)^Fu}_Ͼcg8% C_jwӠ-Oj4.C?:i6iJZ޼?3zi )Oa߅r\KR~4H_.u78%cJs~,ſ/~´9T)}~X"ldwkT*<6O%̽|7Bxټڳj}3itG0,W9H LiRjЯaNϪbc0_ìmO n 'Vz^{[zQ~cZ9KV*>HH5gfۼt8EK8YٚչTޜ:}4c0j\^(so?9ΏN|],Og.͞4Zҕ 6\}}ZjJe-QR5>5LiRT mo'X;ZHZRxU3bd'R7aXX\hzk/ƿx;n n$Pw[/¡9OۆS8[A%VvłClK +ٴVLlipk L= mqbx> öp:LGkIts6]X+||Nټw/[>OP3Z"""(Oؕ¯⻾Ph863` !rޚ, %f^ xrilc؞/)Sdi"Oof1Ϯ4gƕ8ÓR,jӵ}ye{_Ү(E23fUscݮuF7܌EE'ܗ1-\W9v~Qi߇-`ʖQti3q|/gPq9ݧ^I|RǕA] 1maALfn/~+phX 1SY#p.O}YΎϾL I4maDl) }RѿڍQ++@heޘ7 ٵ cD]|T[_Č3EJaBջ0'*&*Cb3eŀ8Fal'"BnR\&}ٸ`V8SYhyf.1b{4'싏]ξw۳F{ ydmN ,\c z]F@_̛!יӋ|;O`Gc~E˩ϕ;ޤէYyc=ʱ糞#p%>Ԅ8l" ~+.}|9mbsw!eS,4/e잔Ɓ< G1؝Zόe|;9_wƇ>詟SoNz9 j] ?8]g/9ְ gxj|EMJ`R>m/Y&&{7ES˅S.-lMљ.M1[f!\Zϧ{&K7,YojZ'u׈*L~~ Km; 0݌"bH0bi36yk8K+^ߪbʾ'S'iӹOKϣ-FB.)\7#GuC}qgPV|ϲr ?s0ϕ?eԸ]vLr9WQy LcCo,Ζ#ya7ȋߥVop){8ZcO=ԯĵݥyqz?;ы-X-uj++7q(\lh٨.@4;09Lx,Gb&ބh{vn{/ 'a{}~8̜<_G q=I5r}K`ڇ'HZםG'zЫӴop=F\>rXh/Up ck"0Z[A^{g,㉏څP IDAT}CZP1[nd<2٭!&^eЧgE-[ShlB=M\?}=%;5k7CwaUT{/l( "*徧i.ii.e[-2++ӴLJ-5-5rTdTTԾ)gΜ;3wxsZƤj`W=ԵH{YSJ[?^ ن]4fju>JfWٺUU 곲{Frtm=Ckvնn #Ҩ%RO'6~Hr!?ƜoVc;)nXU,^e7Gߛ"~YtQ.mܞs1gKTQp*zJEzN;$7S5Qŵճw=֡©7谭7|_U[jk :hlZ~*jGGջO/=X?4[T圿tg}h~5T }V!Zr)])_* UhhBUs&qU,. /&KiqH%[TOaSOcezQs|sHYּEUhI{g&LrnjQ"#"U:LQJ!9jըqllxլ_?iPA[-浀kL3ChOk&1 9dܠؼKFpy-T'0Y6rيdx~9i:xh@ʕVD*UFjR Nmޤ#ʪ_)y{̦?#rmmS~~^ժtː^JMTPoٗ{f[=|NmQ?lVL6Kew捻ig?k?Ɉ W-s}r&>_ILysŷלMSPdJ)7Wz.3_} 7^5xim^qOUS/o gҮ7ʿuz:ujVVڙ>=kA:u8Oˊ-P!V%Ɯր*s:gؿ:eXI-WrƇp1u-Y|M|Ll Y:\o6ppJ吋\\_5F.YC[}p=ԠzM=3?VzZS-ђ_&JJU7dpw ѡf|hҠ Kɒ>in-Y8B%},kBP ?iuşs(P-X%ɱgvpjoԪ6Wt9Z{JyǪgLO; I,_~ /Ykpu]JwƅxĜS*bسX)M Ze&9*eDB+iV}o< .)CR&})M~h޼)SlttMϼ  WkR{Bj\y-鿻aH.w9dɒmb i얷EnUUo HKޭة/ꍹge8 9/+W/E~ i_Q`WMvҧ/ܒ߾9˴rݹB l̡u$9ǹ;TE;5y/YSplEecw Bl߭^XK/']KŞ_+Uw.~j}>7Msή~dwF&N[lnrdܿ:p8$K5vA֐mڙz<=lhwwqtERFU\Od)s'x1{U>-qm__[T:6hˋiɺdXEafxxG?:8c9_o|[nљkP26ܾO*?Bòʯ$_z&[XJՆo<[vxA,X%j2M <J-go^=ϊ1$9iiWϵ eʨB[rTr:|l.J*WmqNkVlQ `ѡE4zY";vT, UUi_꧘\qf=1K&y)U,#Zէ/)m*=_Q"L_+p٢ jRDUU".\,UNbM|E<ȫc.uh[T7_go{V*h;}E_[V鏘|,~<@&zod*c&ϒn<{ƹrȪZ=S*Y^U# υ Ngw?Ϯ59VW7K*G:|>NXRY˩i3ƟuJejR%q=Ϋ65h(k?.Kxzim/wGՓhү9:nn^[- XI˗UDhAgsѻ)jy_G0sZeK)+tR!a:u}K,e?iڧ_hq;.k_^6֒tF*uڵS_ "{ݭ<[[/>cC?/hqR-xBB*G0/羧L^TZͺT<뷉|UTK{vzs-H֩?˿e75Z(JSjF*w@";E,77_%`Qu ==PݫЙi#,CgWO1+kn?zA.~~f\R! \-!jjt`E.sKk|w5KO]H=ۮQQ$ms_{5ՓWTO)^Sыh{jΗ3e }3},Ϧ?T:b*²Pߏ]*?}%ך鉻f׋uwK;IhVAG83#Ywy=XFtW{65rA'3l3Ǟw]j?w|a1]|@WЎ=9r|褐ݚYZ\qÛsGV+ݫ_m[2 rO*rR{-~g7ŷqjڤV>V;+n^U.X/oޛ_^NyXv/Ӏeek>V'K3ܐkGDzGAJd}?UUDoYֺ)kŻG=mս~Q\0M::Z)@{꽧)mdu 4Cz7@ukzk"[ZQeVVJ48: Ǵ~;╃MuoX|g7tNZB=,}zj׈o9sEOnӜQ#5`o .D/O~_]3#3.䓿~>;rV)OWRWрUow4cbAF*T`znW5ZC>|A^]'%xn5(J^7[qz^]IA(dX$9ufr}b :(TG4꭮ o3* T/:7xG-{@'Jn^~ (UUaxS&iptaѠ gzfs|QKUZj?j^?T$gYu렏ln VJuȘazM|3 qϟ7QKeߘju9VTIߎ֘jO;*I,j{b]T vnz2Y/5{C~C_>Y݋Rf`qB*:(Rty {GZTV]VYMǬſQUƎsӳFF*{&=K>^dDq?+]>SU>^ ݴ<Œ{#=V9- _\yu;thRM/_^kiy]&O(XF _h&?^g}ػbtTPun%<8Wɶcޟ_'r VdG4Ԧ`⺲ Կ|}>]w׻DwVWo})}ZTMJ55*_*b5ieVh3-j;Hq3˪+_-_w-_ϲX_3->WI/^Gogbn!,*яѣgnKZ_ mLE\r,wySi%Qt)xlڼymhߛvnӴ%ϫ#ԧX7h[ WLU1Vwy^I]=P;oԣKo0ͦKC9T;1k,5k,Օ:nsC~*|ɇj0-װy? @I9u,V3DdpoA -䡴4i:[y5/GTQ?M|q3uhMeaȼvᭁn6ui(/m[\*_7_6϶#og%0gѾ7mz=9-m[hG<;P9U0gѾ7mz=9-m[hG} ۂjUviVҩS;Lǎo,۷u*0g6.S0@iL#P`"F4E(0%' ]hQN oh֬M/3GEIjڴiN ٳGy] `ŋs\y`ZP4 #, P`=r{(:t`U|m?oqf[ p'r*z,F.PL҂'+cJz'*5h^ ȒSW -vOcuU3=5Ѳ궴fTͯ:7o@'tUnp1.7:r!Pt(>&F>oGvTgݢcG:g=[.Ǫ/y!vEo5} M)62siمЩl:!En#ȳ@׺K5VY徭c&L"euQ*%W@ dd߮^O ʷ`*&lRط#ͪtH!xPXgZwyvOQRҖRR6jl,RY/-Nu/.wwwyԏ~>~RľItJͅgծ94fG5W69vU* VBUw(:^O}{xg}:SۨڨfD S4ϔYp\xy&$'Ֆ P?}6 +NɁdu[Jzo8V}kGI.rߨſ\E-Ѹwލgˢ_r 5^ѻ7T ދg֏mvkɜ5uL/&xywmHE鉶dhx{UT[3zPAzYIYQN%5A.p` R1hNz+ͳzrqնwpcю4[k~5[i/W|*zj6(EL&9 I]RRHЩ}`ԛ1)Xݚ#aQuբA-e2e`:}^o>b~xl,)Y |&jf5R㲆D#= ={‡kS#wI,U)C6Rj{elݩsNCxHDigj޲r~oRšb5eoaZp82UU?yO>*L-4u!9vɻXJ%yhsC p4$9ee7r8%N9v٭ {#}I?ՈU!zz("*[6_Q\sqɵ^VB//Ȼx]u3Lo4Sɸϩ6騭N$I5հf~}y)̸_M*ڱeRwcɗ++X K\Y,Pl*;bLTmڸrҪ>Fpx~e>%:{4߽J:j*tj佳51ZaN$㞬 %;3FӷveK8tj6HݥMhĥ8ꢨvƭ8+w\۵R?} . )i6gfdRIMB3iT`ae!-A G3|@-^7U\XաW[}lBzr z[}[~*)zs fJ4]oE0$`Zg bw0;r!Y<UZ/p9/4!/*VTcW$H *j5.{ZuT\F>-_JZUJIթ_RWrs/8 L:h_Fs#Q*Lay IDATVO*դoob3tz(ʖ٭|Ъ_Q^  H bYEr:t2, =+Pqw]6(}݀(v֎\;kbzr%PIY\ʉ.VFo=p}-ߖKHu՛-$C|?AU]t.Ŗ5ex/r-WIe4L_ Za)`Bk*^R@nzV|Z[ 3pǺxl_sV֣ҸAeCwRQKE2}MjҾ6?#<3qpf._wR{Qџgo֓N]TzŮ;)WPڙZY}:#(ntC? ]^ԄFzgַz"2St`5Gxu~rRÌNt8(CMwf#7Qh+Sq~.~ґKFzWy9]W[%bUT;$]XO4k. 4i@j=<+G(*I6?t\^]vɭM7]_ԏ)ԸjlvZ9)u?Z?m;D|r Rӧj߾-Z5kZ QCDXz V4lgjg)ۏj՞={U&j֬ugϞ;֕:uo2-'Z~ Ԓ00bκSEư9RnN ,!׫ϪU#zL=}м-5$~VثEiy]+noiE|U,U^԰9ɰ}.E' I6_'R5?I_vQ}x .٨)7#-MGϥ!'q"7(G>}X//2i{ˆuNZ[=u=YU\)LLS3ÿrUQkI+32ipH[3@ﲑM#IIEKRSYW4:MYSknՐvrUV5zM==dqdhm(!= XMBUHvJ/#<*Sb5oAݛ4IV=4L{աgEQ+Rgׇ)I.PU- J8=J$o=&LMCǢOkCwMNq1tHW eqPzazL:}FV'2$5I*̧BRjvևNh[ʥR\%9I+7{= 6#[ԋ唔آJ]m=mH\\ΜD)D1NKr*^4j-ӨӐSRCzgGh+^*Ӣ˞1aׯԸhC23d,7:T9'N~$\#-K[l*R@a z}qX,gvhY֏P߀8 _[- hRVaQD2֭=;X"XO*`6+UN[~U,楔{>CjӠ6tX=;d+!g1}ٴz١q'-Ԯu9Z8ISk]TJ^y]Ҕ^lEj9C~zAq w^P_b(ɫXqm.#^AI[guLƁԤ;ti Eˑ=-Bi9SuH4w 3PC+V0uiUVSk>-:xAIme*uPÊԓu"աcGil(μîfm7avFƧIgqܶ]$h¯G$Hw=)I 귽R,RRBB82(Z\RHOESP>RSR!Ik5{=LT)Xp.^?Scv1-3YSSӮE#E)g0=Ҹz:u qX3ΘIUzMQyɡq֝gn6Cՙ>}ۛZyѢEzy{^˒_tugp3YTae :?~kG y1|5kfjٳgcǎu%n۴bV *.ê aqZm!LrjKvRE,ro]sj$ws?}^2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(2A(_}^% zj'^e.yELPELP:-Ilٲlܸ1Ýz ^yիwҞrL:5'Oٳ;~Ge2::ڱdɒ,[,3g|ޟyɉ' [n]wݕ?_#Aqèi 8IDAT ={v:,Z><<N8|8ꨣr7vdŵk6v3k׮}^|ԭj:vE'|@[cg(&>]Zv;Cu{9:zɳ٘F2nh :w ةw$"ko˳bpڴ%28m̚6!c:#kf]9䘉۩w4ک '6Î>xl^5U5T{QΤisqH6oX#M8j6=gǎMO\ѷ}77~Lxw_(g({&Ye{299pSO4MڛVWfx2ҤwJ{HiydznÖ4=3Ϭ@zYϣ6gݕgցoR3&ߛsoÎ>$C]`]]ؙ4I'~lM4rູy7 if|S r?Of֛E_8g:.I;~K!ȓlfmOdxL|݇>OGW~2?9=9c'5 杔[/ޚ$Hn]0/'\srRc揮ܶtC&zr̥` g{A35걍<83Ƽyz]֬CwddxiDN+{uIz3L͘fs֮x(P<8CcI5)3g`WjuUY`ev퇛=fphBZ[6fHf|o'{[>s%gg̛/1cGrg۟uȅazE9ӷ|*{K?*'i=ob_,<4rS{gͿ_|ZNM~(7@&tnOWaM} 7]~Pj?A3m٘M#ɸjѨi}?8c?9x,#S(̌u5ٲ'yBؘ NhmD=cח.|Lͳ3\5W\3&o2_~ _oNrl=WnD>iҚxHpy}w>kzVrBXzM'?xvf4eSqIr1_~T_w~2GL[y=i/>1!oLrnJrߛ%}vg[{ٔ Y[s7oK6IO{CV/_Uyk;16]ikVucGWkoמgg?__|FY~Yeqp޹'e떞}t45i;a4<&MJW65][SMFV$O9j[LLx[r&GcU{wyGeO&7~ybFܞ;={>~DpMFfGM;"s"gɓ6fC[ێYl2vs?-H7˾3P_w䁵Ϯ4a[]ؙNS]|:Ls|fOvLj7 ?;&VW&ۛs3Vӓdtd4fN+팎6igyM:g,̭[Qߤi7iZyϧO6 H\==ԄL㏯ʧ&ejqyf`zæ4Cg 7 [=LV56ݭijF22Ҥ)kvq;`gvkL>pNM"_Ԝqٹ옿;6{>'WSrѓWg>m˪wxAGu7ܞMtmixV $Mӓ׿_7}OIrOfugJ}gf%YqϢl{jǥ5OoS̘>d)yI{MLoW;[7mI oLF֮'2ilWysFw<ӱo|H[XzM{hz~ү IUY3\9o~97|+{fV}8֓͞-G757=;~\sAq?P|w[3 g3!+,fL1z{xI.8 if5njfJkùlՓٴ~?do>7]y;~=e;ˆ=^fGZp\=S]3{ݵ_u2qsON99Pؗ/:Wl3ޑw~ڻ?k7~;駟^?a8∴/v]EWWWnwq f;DWWWT{… wMInEۺ"|g8C`ɹ#Aq`` v;V}vvq]]]ixmLbMlLґ;bŊL>_Q ?_ʕ+L<9-k_Z.ngř)tEXtCreation TimeSo 14 Nov 2021 16:19:09 CET] IDATxyxLgL6H"!FN,A)ZJVRJUjZJUꢵP[B:3HHdL>ו뚜99yə;""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""rL'fHv11@p2P4]]]UTWs62bX0LQ7nv?EFFkH&TXpo11XL&\b0ё|NNsǏʤbF*Xb^^E\fl6g*pf8cb  rvvv jF+Zh½]"""""""""9µ EZuSemBhE7n8d>Dk"##+/?(b&Kgȃb`go_ fkNf+aH^e6NkmBl6[5ZDDDDDDDDD/f9?V.lom,""""""""7o3\3 ^FrV'EDDDDDDDD$w7ӵ` 7x.,Ȟ|qʼnL8%EDDDDDDDDOxlh &,do{ Ų$,Z5%b)("""""""" 1u4Xxpf]vy,QDDDD<{>ƓSw""""yRurq6<罉oS(]-r);{P4k14YDDDDDDDD {M59I9g[K5QW׹ew:r>`IH":,<P }VBQDDDDDDDDJi% K6fh?.Fp{q";gk^IJ&& pcA3/qJ(<(܄*>^'YS" J]jK=?O7k?dC\1F_Z|. PD!\crK~q>ĥ[13R}5@BRDNR1Bf:gPySkL,sU)V^&?"XGߚΰfQ\ #bToV-F^W,(ES˟%q ])tnIw Wo8j$ܔ!"""K{1`@'Wv E-""""0y纔rSzXt>Х;FSk_yybS+r?N.o5 XLB=x`uTJi|Jg:kţWb۹EDDD$PE]*Tp4`ú!ܧ kX{ID3&xpqq`ı17(v%Xեgbs*)ee""""1aI|qiζmI@K2F  WDDD$#rcuBx](kTs&&ΩxgR1,E w%EDDDg1AuhV?_!>G֘mKsH䥄av cno4פ]s&&ΩБXywVx%EDDD3[s/땤ˌu\G q,I&w*A+8 'O""""Hlģn8n-f"a(j*vI\N6I<X75|'(?΅6ixm~=w{w/ O""b4Yvة$z|6h7k׬ǦglD6"ǜ֪"""" CC#M!"""""""""yܼΜ3\ßk1Qh>6Dvs"%EDDDDDDDD:clb׆?"Sqv~33g_Ȝ5 WBnd]6$;=E ?EM%Qޯ8nv?Eeh5|bՂ.AY|ɦĜ, 5{"ȩE y6xR~C6ӠZ k0"˿M6u)_?Bja2iАFM[ѱ!%9ދ!2i?K|2+UK]ܟOW'hHoq0vۇRhS5%c^clf-Z|S{#wS%Yy!|%zz1g.JJ,gWP ^EGWWr,*V9j](i!=W#e]6 _[:6';;XB:IZ֭@b^x+CV2a_D`""""""""s(Z0c2aj8 vI׾l_ϼ947CV_ [3Qxk t[5|r}gp(&[7NvJb]{mI w嶄nfxgYou9`ijM)G׮$-ƱMl:ưzZ0v^ޭcp8Kt _bg+YZּDq`p'>s6Ͳ7peP/8 0ZKC jV.|Jb/eЙwoz=96,D}'&6K6^|wɉ}9Us?5K)<ʦ """""""A79f#/ߍ>lNr>XNأ](hs}U|={S8;ԠN%{V>&(}p1+Πgѿ=PSN:2we=m=ðJ尋?w[CnӎwժӟO狫 zMv |߮;vd]jem<O:)TD%du\V;4S ^_t"|ZD"pmHq Hٍf-iXթ{NzjP;fh0+N}Viuޤn7~A[L'GR/۩LCڴl#Тu^Wʌ<9>行HJٗPtj[k8Hwĸcz4 *yLlq =boq![03X{^0aߊ.\Bf6:8).ןb^wd/|6aK389pݔ{w+qeBmАr~"J9,N'A\cv1pٗ!l^So}/] 1{巋7ktU iuɴ)g3x[^VXGcx0FBew/"""""""ٗPă5jQ%=S`QyzZ Q*k7px7-Oq;jCC^8R$q (#ۣ]3ܰeZu;`1[Z-/\o$6q1+zBmsZ]$;.uŀ`tjƓ1>]cO19qg]_yi63{/fͱrd.2]ZǶH\}^.>1礪ughgX,i%iEDDDDDDDrN6&g0l`_Xi(݅i )Kޟ<+W8VB:+кq\[j_Z41x;d\(٤7o7N﹏xB6jCjaoZV%zl;+݉|TJ/>m?5\**("""""""/F<=!lz6YNybp>9, _h.8g>(ud,9g79ʏk?Sdžpo \Õ#&/tS=Y)L}l ~Q4O˝V(ӥ]_+qeB>څV]LpRLfф^3xeX`re7Wdq#U7lfTY/N]9{>U_\-9%)Ϟ_GA%"tVQ{"ɕx^,i9t3+@¢D uuy_GISSDDDDDDD$~EaqOCcp,M͞DU< vT?f h|^oF13*.8ߙR%ׯGÒ3GׂK y{7^9,,׎7=F{gPզ4,=3$WcI+38wPasgǨ{Y8qv.Z8񗎲i\+h+*5畅xݘS~a_rUVG+pcs3VN|QYOGT&|K+Y-"""""""ʕ+{5 s7df ܎GDDDDDDD* lMo"""""""""b5 y y۳zgg"""""""""r:痝q}ꄢl8DDDDDDDDD>9EDDDDDDDDjJ(լlX3XM ECm9}O0Ak1E̞=p>޽{9~88::O&MhӦ u֥pիW9y${e˖-?~0 .?m۶qƔ/_>&d6dM{ k 1dUXXC/[n̘1B eS|ʕ+ѣ/2>>>.̛7e˖q…tO]\\prro߾̜93񈈈ȃ`~o޼''sqqIw7o&=f <ww>S15ĄNqqq8pe˖n:_cǎӇƍh8f3?3 ,`\pٜFoooZj /@͚513nZVmȚębȪ}ѫWD7˗/aÆ~Ԍ1 =AAAL2%e3Yfki{X,8;;'%EDDDDD؛~yz3!>"""r~֮]ˠAXb=7n`͚5 ;we˖1`VZeuEoQQQlݺ_5͛7sŤ/\7|b#GqZ*)[1-[fv{jH!ϙ '^rY鑕ӽ3S?!!!3aڵky1L)Sf͚ѡC4h;7od߾}|9s wL<3gf8cX'NM|PC VzDDDDDDDcmƑ{z2WWtI]!+b'ѣ8IJ&6jԈCҰaC\’%KTHnRqƩ>̱&|N1LDEE2lʟ? k[ 3W^LFLv3f̠B ;vF@DDDDDD$*Nɇetރ0$/a9RJ>M6%ެY ZpaΝ;bہx3f ˗LJ^z)Z?;3n2+Ku7p@IE[N2VK6l`IB seicNطows R] 444… ӨQ#~DEE1gXry96=YCQC.19cAFŦM9rUDŋ'k =suΞ=˹sR(;O kīWСC;v,cǎeȐ!ΕmѣI=w ,H(\pKDDDDDD$;eia%㜔&ٳg3w\8 lz*nݺ=~a2صk/"-[Cԯ_s <ȦMرcNJQl~`L8b#00˗~ˁNFY=T^vN1?\t-Z_xy晊͚cVXALL]ۯ\ƍ=>&&+VSOe8l޼9ijժ% m%-y96=YRck\v-C~5yd'՗ÜdooO׮]8q"7O,ZHXH, ׯ'""I&Y곻;ȑ#Å UҿßvT\#GҮ];TS#FȖs>|8`Vl߾_~5j 6lؐ|8̝;gggOθq7nӧOёsrС #^.\p״ \y %uŤ ::I&1i$Nudd,P*'19[ޞFQbE|Ioξ}8v7o$_|ӰaCZn''OᄏkAl*̉F#]t᯿b֬YDEEe:t(;wk~=2e0j(x뭷زe ?⧟~tL<-[I ѨQ#z쉗/]Xp!.]?8880p@5kf+\0Ν/[nZI&ȑ#3]wWw˗/끋J㈈=Y=YXF;ivbs{&5ʖap3GZ=hJ* 2Ν;蘡f!X-՞2j AAA3VZ`X|'>Wre{ϲ3^o޼ɶm(\05Jo2{줶c0Y?EDDDDDDDDĘC E"""""""""b5͡("""""""""V:CDDDDDDDDDV'5kq}blllv!"""""""""-""""""""""VSBQDDDDDDDDDXM E"""""""""b5%EDDDDDDDDjJ(ՔPv"""""r9uׯ_',,,Cy*Twwwʖ-ۡCL EɐSNa4 PJ?O\r<2P ͛DFFfW<$WWW\]])Q0~mOu5>[ANQ=<չ%וLEuaǎ<ĬN(Oٲe1  aXX,={Ǐ:!կN&Gu:)眧:uaιO&7oL 3h4U@k{ӼGIQݪr9m/NO>ۡH.:L2FbbbT@k{ӼGIQݪr9m/N!Ǣ<YC2/TCk{ӼGIQݪr9mOu)"""J(M E% 2i:Nr;⎳n[|ۑۍ`ܟYr;aVoNv(гqG'9xV.Ag """bE~a+/n:)=V!O&.dɏo6BӱTQyͦ/O!6f!t@jWGB sN~vukh}WÒY2f]n#IH>hǎ="Ke'xݭ୏(*TlQ4smٚP\%#ѴZ9|JPr~ X-tKF>3Ϗnp_9Jy9? :6e})V}PNK 7mx{J|eE Yc̈́/zꥊSX1K|F<{3ռi ~"~)G&y8ksG坍!تShZYuh14(^E)^ >(-D{/%|JSN+9? t?[S-VܗӦp>zVߟYBٲnOЦ!`~ ޾muS>m{dsn6<~֣bi|JS Zgn}[r95;wk41K >a;3:y@_؆}l/ti+z3|71mϤuazX(ϳh5E/lZ4 -UuHel AQ] %T^ckX:QESv4UvFyg(34.=}'aQ {ugn4ݟqP .sƁ8ww^gu@nǑj¾,\מdɍ??7}*e_<7n0j9pTzV~&ǰUo9Ȱ8~}CӷUVޟY.~v4ъ79[Q>hMdǤ{)˵qyL=,Y <^|f%s;bM'm9VzM{_o#K27KN7V90+ k(n1!GaC,g6Mⵉï!AAS1)^Fu}_Ͼcg8% C_jwӠ-Oj4.C?:i6iJZ޼?3zi )Oa߅r\KR~4H_.u78%cJs~,ſ/~´9T)}~X"ldwkT*<6O%̽|7Bxټڳj}3itG0,W9H LiRjЯaNϪbc0_ìmO n 'Vz^{[zQ~cZ9KV*>HH5gfۼt8EK8YٚչTޜ:}4c0j\^(so?9ΏN|],Og.͞4Zҕ 6\}}ZjJe-QR5>5LiRT mo'X;ZHZRxU3bd'R7aXX\hzk/ƿx;n n$Pw[/¡9OۆS8[A%VvłClK +ٴVLlipk L= mqbx> öp:LGkIts6]X+||Nټw/[>OP3Z"""(Oؕ¯⻾Ph863` !rޚ, %f^ xrilc؞/)Sdi"Oof1Ϯ4gƕ8ÓR,jӵ}ye{_Ү(E23fUscݮuF7܌EE'ܗ1-\W9v~Qi߇-`ʖQti3q|/gPq9ݧ^I|RǕA] 1maALfn/~+phX 1SY#p.O}YΎϾL I4maDl) }RѿڍQ++@heޘ7 ٵ cD]|T[_Č3EJaBջ0'*&*Cb3eŀ8Fal'"BnR\&}ٸ`V8SYhyf.1b{4'싏]ξw۳F{ ydmN ,\c z]F@_̛!יӋ|;O`Gc~E˩ϕ;ޤէYyc=ʱ糞#p%>Ԅ8l" ~+.}|9mbsw!eS,4/e잔Ɓ< G1؝Zόe|;9_wƇ>詟SoNz9 j] ?8]g/9ְ gxj|EMJ`R>m/Y&&{7ES˅S.-lMљ.M1[f!\Zϧ{&K7,YojZ'u׈*L~~ Km; 0݌"bH0bi36yk8K+^ߪbʾ'S'iӹOKϣ-FB.)\7#GuC}qgPV|ϲr ?s0ϕ?eԸ]vLr9WQy LcCo,Ζ#ya7ȋߥVop){8ZcO=ԯĵݥyqz?;ы-X-uj++7q(\lh٨.@4;09Lx,Gb&ބh{vn{/ 'a{}~8̜<_G q=I5r}K`ڇ'HZםG'zЫӴop=F\>rXh/Up ck"0Z[A^{g,㉏څP IDAT}CZP1[nd<2٭!&^eЧgE-[ShlB=M\?}=%;5k7CwQTmHH !A MD " J" |ҋwBz~$@ HBfgΜ9;svSR}amڿڃ^=Q錴{>!\0/s"{~MWvgƬ}fOu)ԭOmO pЊa Դ;ú+FOGm|08r >&UjfW\O{'EO9d-'x?&b#^UtpP#YIv:|Di,3㺼< t߀T ~0z׵}umղ`#\34;T֫ҩ"g:y~܃B!3Pr6/ۍqt(vo6Vl 7<'S8 %)Y$%=+_)nUIIWq"ΆMettqZo|v\I*Y3F/Qu;3ooޝAw9jIړ닸nL%5UΜ3Yg_9x;j8PA'8JR/mOC 0*(CZ{qx0j@[ˤl=긦p؅\G[2dDEs9[[ Z,0zo:jz*骑ǸDZlt.8`nS7 j,ʭ{\<̃P9'.i=[i~|<>yLKMdVԋk׷ *["BBZb[o&OݬTJ#/< uJ¾ ~zc1?[CWRwhp_Ǚ0~ ;LJĻL.eq;t/L(8=ʆzajbZeQNQ~r[PiVΌE㞌igci#׭iG oߌ+ A._ tծ9K[72o?ڛq؜]>\goz^J +zgԸek !Qs)xTw'-a/tgNIJ֗{m۲^wfh3orukyAhu:PF@ǀ.4n~֍kjPK̏&Ad69,ДV5D? 4#+ׯex=$q_ L^ ܂1?zc1_)`Kyٷt8uuiB (GNi/3 NYn3K#XjM`4dC 7sͯ~Le}ڨJuta!z}{~OV0a<[目%+Z{Lmή=[_w#͉Y!_F5ȷMPph0 zug:~մ.#_[w._G+{&3p?֝ed⚲7V\Jظzgf^HsB |a+۶ndz֮}w:?OŒ:6~ ɝ_>J>-םbb Q!+}EkNɺ-`%N~q|^o/V]CXV9XXZŒ#Χr硌?^~ah v~-4n׉ʕ)a8¶='d*Vƫ+J5|+j\]kÊ'1!(b6R[!b_*k ;} J{S!3Xfn9'H减Y G}nT7nfIl: E @ZO_P/b~0H욾Pߟ Au[T΄k;wpťM׋\ehE[&2mG~]PN3Œuz0r_lU"RhܩB=r!x7")^[q̱0s'8@^IrV!QxsI!:gMkҩ} _A=C1\g/3y?144m;83Rs<jiN\Pp֯_y:?/m>VG'OXҖqzah}~7?FBᢦA:P|leS7i_֠d^HUz5뿢s ͠oXd#d<֭=^X>ߒNXS?3:֝u˵2`lYfVn0&{J Wl)Y>8OMj$q9k;vd{:MOIh?:6f쌁X"P1JLgٱ}.Mt'ѵǗ,ڸ;Dzg5w ` s>m?E8;>XOMn^][3gD۲&~qv `錓n?*B\N"F]J]5p]{Ry[-JX~6{M9Vڶ9 i~]iY:.ž%^-A]fKRfD[% Gp798EDW{#ۦZW֖ms^p&w{aHo~3WJX$s\l?[7e[i?e0Fһ![g0n߽^ݿ}f$E]ĎJ/^f'ͭ,ڑo` ;Ѳ<EQ /˶xotڟOVI}Wc//~kN\(;Q ҁVI@_?c mU:lf^g4.G{P2їk^4GywIܮ.TZY |8>{`Jחз{K# fܳKV >ޤ%iǯt m8ǪfbߪgYƨywJ~ݻbjS:GL+f`.Ϲ?y~c)e/nd)vkث1~rYX9h+8V+^V==|?~]bOjkm >4cx4t+%P&5+q HGثgѯC ڧXOfجpU Ju.NFgSq@iѻ[֠]>*uL*W-ce|MHzˉhL^n.3֮[u7[S1;*䜘M- y+͏+2xl%'fD/3%iX{Rog?wlN U~ iOhˁ9X[ެ6iEĬ \w oRX2ojmV81M\hގ777%]P3J^5Z|CaFJtL@Tm9?W3rBuOj{@3F4KS&ꯘ=*1:*Enh )M%~:׉xȔMy\f҇b~! ɢy wBgWݑif8:Ftt#T  oo+1n2F ײ yn&\1a=s iG (5F POFQ#Qwۭ M |j/{Q,gXFiG_|K_Y K _'.>U|ȱ3K3ᛅ=zl]ޠXfhpLYjg`=ޝ +[%,Ϥc8#l<֏{/:kv!LJQD噼 V,G)T50~23]#5bЪe:<>7:Z}9?uYGg;gV˄0sǯL9=V\9a 4_a!ɂU^zӊ&2'bkLp\GL "$?l őod73޳{R,nyɚCXƙ ב1~9綱]O~Ocb[7~7~0_x7B!e˖*UEU!iQ~ĺ^iZ? )'y.ӌmC%O-=ϟɳNVʠ<mkm{iI7ݎ Sjl߾N:RDA^f͚=l!x8883?4s2pi{S?J}&6| !Bt-NbEmv-h7_J:[ve_/#PZzDgB!x^>YFD<fxWnΨ>ӪI!"_iX1)C|g"JX\*:;O*4ޢ쿟L!At K%ud\RDTpw+g<-<&:)[)Li]&[y>.󢢫ț \z2ϖB!z`g%)%I>{3){3)vB!B<u}YBYfMוB!B!&E!B!B!DI@Q!B!BQ`PB!B!BB!B!&E!B!B!DI@Q!B!BQ`PB!B!BB!B!+DlR !B!B!Af͞xPhڴia%-Eܹs=l!B!e֭tyB!B!BXPTUBB( J]&B!BGZ( !B!B! LZ( !D| EWV}3B!B<>!*pEs"x1 ٿ]!I~sB!B*lDEHҫhL,s-MP`ڢV&#)oեΏk7蹽=H5W^)6WRwLв.B!B"( -m~ ōDyN8zb/J%(3;8zT⣭)YﭥXijDDžcbGaI/B=ҡx,['1ƛ]}tb83.qֹ:~kyc|7TYj ^ǡ*R,=!놣'g;X!B!!:9ZYcԔNli!4ň#* zf 7E1;1l=N)VFGm̚0F疑T6c*zMT q ̤;g6,G'u$)!*5aiiƞq.)S!ᵗ!ْ:*aIBIIYгk^ʆNtxe5hi]gצϨY qC8#>Cq͝Ng}EtVhEflm3u U-.bGf羱T7+2B!B! -ߙĀQT=i$tSG;=[GG`TԥeZfmz`W6stk` sɜNZF#FQp*9.^}H.3M'O*Z!B!DQ+❦9&eɹrIdoCP4ra߬,Ψa*ϲb4[ IDAT[W5=e_OgƴvzKoEwH|]u$ &tOUAfxgw ŸWHl .m˞{01%o< s=?p]T55j(ەJVfG@qK} s9 #VYڙ}hՊR~I|NQ3#\k)iU % Wb`B!B!ڳIWS޿6~ك?biB#Zoe{V,фm4N{`/iըQޅϢO3AO:v;W(/^tԃa3q:̐U#_[ ޙRvI ! ]2iޯv {Mk2|WV3K 4mkC䋋@Oty'XՆ[vs#5UJk=S厼6l;F!B!){Xboǎ -[Qo}^UQP &v-J!@p9|}}vVB!Br!5kVu׬YC.][W}DjENЄocʈMxHk & !B!B"Cp Ct47}d,BpE!B!+bzzza%Sɜ (vi|*'N@NiZ222.B!B!PܳgOa%-E&44igA!B!x,666n_xQ42^𩪊igE!B!x,Jͤ,8LlR!&=6[ !B!E6B>E[B!B!yx< \lQ:@[ZS' eJo~J˱krB΂cV0pd̀,iEU+ZSJ9PvFJEg5!B!xv<_RwZ(*kM8P+y3&RHk̩m@a#d NHIc;]gESRB*7cI6HC*! bJW031t8ʄ_&%<ڮ %\;W^J7WHKNh~sOd j*gѶ5Lފ)h Rܫ{c[L[ne+1>|{35G8XtYBB*WcI'B!B!U@i侍jH`[g[nsE?ͿrE* jV3ؾ.({3\:?~cF@U ׇQ*ƆJ~_aĨ[m\bžPYYP'VT‚+u6iGOOks({i33|,}?|!B!IWNO=VAyo# U)?*eqY~aG4×"8k]k,eβDs+%ȸq^25i=bZ3o fu*:6`} uʴPXf9л+uqT38w%Y#MmsH|\"[d42 ]zRI 1qqt e0T,VpA_΃7o!16moTVڬM(fE a龛l&B򙍜 !B!D(Y1/b-p_PQkekZJHeƟlݓJ\xRZScT@]lUP1DfcF:XdՀ6[wDUU1o0?z7:{W+ևrA5ݻ/8 P%X3%Oʆ2x,QtGJYƷ{IGc/bWguTQNgSNY/e_Uc2܈+7Us30jMw _3^3=7WpVOR5NFrB9 .hh½9)|ڥ, ˜tVtɌ:,i=h宰sy6ƪX:8ҷA)&g YQp%i豹ogf)Q),so I;`}̽UmQyUcfw_^l LpR|T&Bg j$&NEc]x&߭#ҁMNI˟1$*ZJy[IҘP'[uE uK_dɹ* \\KNrkNIp:F9=I=y/ܒ6=Յ9U7B!PE=M^#,[PN0Qge-ltfr-:JH 3Q„eis]4Dފ'DUqSU nF'#iv|Ի]hh9t?KN=#qTo%&eeIN3KTycQ8WcY\ҡAq*d7 XT; \2RN#[̞Sx%4X's)CC@qsL.aI\Jc?1"$_8KQdž+Q3ЖS(,T \T*Òَllm|ؽ! 5VTuw_V%K22_\ѣEIf7lRD [ឞtc1$e.hW hnxQp1ɏ(F#\ߢ,os(Q ˾-?##jf('f*)Wi#DkG%2JaD~˸ ɜ=_]C9'V !B!DW@.#_gՐ3s3 \{+qJ8[n{txż]4aa\H<֚=nT=7b/DRRc$uJh9:Z6 քYM4t'jbsizCׁt\F2&$ |@g]F,Bi!yI~vB!B{Ό1"GPN0RRk:1Zf>7-?O˱_QXXϻm?r_|\9y?-м Hqs-Y:iSqTz [CA/eY@У1, kha'470n~hC;]汛!|{'DFsMG6lb4q6u G͊Q#>o#`Y&tLJ26;4jA*(Tf[gu4ZɆ]fDy_EM}yw2Qm1MK_?-Pِ \%ɇpw'?9@Z ?sج:!zٿáT GX~@ 7?idIX>0R-JQeCirB!BW<]^P1P]"˧8Pgcx7nB %7pOW""1} _̭se+sk_+&E%S!^r4ssK>Oz-n4eR" 2BMŻyK^Τ>ncy7H- QVtkmqwTTG.H" 6@Ă+E j,ш5Xy-&1%jƚ؍ K]{QTTTPL$_9{N2;{g;هgXru'κP^?]i*p4GٷmRӟաؘ\y~%:QNE.N8'd=W<6y#%|u39(٪/6^J4Ɇsw_ +B!/(R(, JC3ZH#͔]3fWI/_::X2žD`m|汝ApФX@W'+E:zGfN~ h_v9ף `d֠5 IDAT=#ڗKsjݗ'/W/]Wp57v"W9FwpKΘyҰD >/KZ 8l#4g4hhH#- GN9ϙHMٕNG͙~ &4v&ba[!B!P &D?Hy;:gN]z/`ckJL Q\_J\e+RpyNkR̋3ebW7jc _Gs!7^>qnJ|⼲+AykuN.=@'%(B!㟡2mp2_|6u_؇дgN=땇GRb=u&C&[9-:;5)EO0PZt12M1tvYFk/n76ڇR\Q\i[Sc*؇Ƒ=kIz\<h[(3 '߭iF6a/xQ^{SF;Ҫ ֗2{o Lb}-S7P'y-q;tm6!KRWźValC!雍p};wbN"9.dV#&4 ߢaLOco񛙂/:էOچ6Nhǥ O@Nx ֭=~@94-~hM ^Gd5O*ȇ6OF쌃LGB!Bª/YDL֭[Q,qjMpUD~` ʳjOgLW7a}*Nwwszj{T{k3:oQҞQRŵ_({[(@5BO~y5AvZRt"u[ҠJ<̗UԨT ENiʩpyUjWaX6 ;No\ /5L/SWN28zmRw+O sUcQ5wa{cT!זDuhZW6ʵT^Z},JWvz28Ak.% 2ԩUߵZ*bU۲x_%TmU+coSk{R JQZAٹUڨA cTa I}`픋gUTu_t>2ǩ_W5SF<*_=s1uA/e>MCTC?Weӫ*~,ٕ?ѧYY L-xp"ϜLwTjޮ*SZx:)GwB!Bn֭[xޒ%KP='œ\T_dΦMrTT9޸}y7Ln䵶Basl/8qgf7eK#Kd<:1ŲiT[j{agctpǷFsJn=sY3ciV"oW|XB!B!^)JCWl}GUb9BV$%\݂9:!fۧX9/,$&A fNCjG(q7p;>,A~=?EQ{*>_ʊkɝ.B!B$+81;u|\6`_{IR׽.<ȭ_vK{Օ'2Rm`ZT'@V7j)H`` B5ll];8kHaMgAs:3XL҃X2 w= 4>g:ΤF%q3bT]vh{[t &5_!m+@3β&_KP*5jR+$f1lċCiɱH?5hoGr `v+[5>6e86% c4:Y?˳u0pq6O~ba#B!BؔnGY-)7utfnOp|_[ֺ1uAc.g]W~*؅捦X!f~9'eTK6IѺ;g3/ ȋ+)A&/%}Q31sK+:~;v*bu9fn#1M*uNbY`^y ڄy? [B^1>I )7V5"3AMX4s#6Ð}>ۋ(!N!8J!- 0$gұpev/Ez]qO[>SĐƍI۾A O|Ȉp5f~9If!'sB!B5 (+ę ~@,+^fE/Sz"~QG]a\o8C[OT)Ep7t Kd][4uᇁaؿPu8yQΜe !ݓPPs*'0f,~)֕u - m h6 h~EZ2R>fn&ؙέBTM[ͩWT !5q9Z3H]ϸp TH?Jf-w0j? Ze}MxJ T)cޙS\2CpNl ޶ _T-Ktu;=U;EVyTeVM ɐbɌA^NTSj=B!B!D6ds.W' sS fm*Nr丙ի7vC1I$,kшƝcrnZ7ZN=@tt4G&jXZN.ύdǠ!8p_(AI1Q§ڛq\zL4UI.^zFv~_zܕM6bEa:CNԬS5={ %r!kruL0u:3|=\9ŵ_nmܜptTJ)ݚUɚ11\zjYMlqWK3Fjt._1lB!B!!?%s/Ժ/ N^NP_x{HE=+Pʊmp :c'Y ԑ ND/NF?1{]@V 4{&r2yY3,X^amZK~ gi |6TvyN5ٟOwLyayIc~wXfuħg0H#9ݝVŽQ9)44YܠJ)d/?yr6!B!Ybn+#<Ⱦѕm$_{ǻ4Zs!j=J#6 9 fHs$t :@k7ԯ'9e$`c HHLAKCf~{t^i'=Hޮ,ot~()o[PdCXP!쮉vi١.yݾ^CO<-QI)PjYUL!3*a kwB2 - @ٲ$W|2>޸ir6!B!ِg(k!R~NBݖMF?:6+'/V'3 *أ;١" 8YLW'څ e7IZm㬽FUÇiQ# dCΑ׎Y7)-#ߡO^ECL| >b[^NfhJrEq1lf\ O~O[>*Rkh8 k~CV4>:nklIi٘L%e/[9oύߏe4fmMŁE8|bO!T.mӡ__J挜_ 9dꭙײѣ[X1ns1Os&]沯f;Fs-Q47Z-ˡ#B!BE՗,YL&S^֭Sf){Gz~^ pfjК*M)r1T_jΨmR[ƩAVyga:ezxDuaN`1lW:*B}weΣS=S`>0(NTcſ=saGQFYKU5sUfrkv l~+VMM[ҏylN7UUJgȫ\ =W[jDQ謔uB=լL=uhR*|MYR[n=]~:ur2!B!'[n]yK,Q@ sDV}ɒ%;6mQQQB!B!Bmeˈz^]yB!B!"$(B!B!1 ( !B!B!rLB!B!BkGq}ZiB!B!J5yĔM3Qsz kYB!B!ב셏7y5/Si\Vh: v.x ड़ǹ4̌l)~s鼔컛L妃(m(H׍ھdh4YK=\p\Ћ `gdW]ũf0-{fgW N|Jk Xz:l>#ruΚRXcD.>ΌFl"8L}##kIW޻ҞYr~9Uވs j϶k:f:Ie)fQ&_1j["bE4-e[`.x`ký\c-:LϚS< y)X!,dfp*x9[!_h s71ScX^6Q=Z+7kU9c¯?N@ 8`7P6=5~׎`?wm)Д/~wt{ .?O4ŨP*J[6*m"'Ҭ`ƴo[S|yʗ/OrK?tsn}꒸hs{91"OfKZWa)yPB!BI@1LSNf=la{tLqce7굛I|Ϙz5sh[j*SM FB`mbvw lJydޒrs EP.DL=DLL 1G7@m_uZݕ ]9f_F{{R 7eWk~`<:CIIl_Rӄ>qj7ekeX"ۄ~pzVxtb_>%N0u&e|WAL8[߯"ja\ȡ 7b#,}Xu?ͻdkHg)g66/k&fܯV?eջ<>|Q/ߣ:n%_;{wpD#,-3Qr2q6LcT,fE9CLL GKSwޥQ?!B!,yW3;5+8!YV≋1_K E4#¢3p:$R;e:n%ȺL,3Jiðp*(9r3'gAB8'ѡ#>U*O`̊YSzU+eZT !A@Ђ5Om@5>acw3n>{0R1#-$ #ln(VuCB*%l>8?k8?Pv1sfD^4իC%C\{m#3'S|ݱZ ڷX+hs[3~waֆ™>/O*t]f_wGҺMƵ+7'0ym]lhTV/KqJ+fk`NJ^ !B!xHb.W' sS fm*Nr丙ի7vC1I$,kшƝcr2I- ::#wx,- yVVƫlW$?UB rOdRXG bhoq%dP5TL&h  @{>wcIJ׮c!l!O "x0v= hb2zyckNMgʥ'+[@|x;s f[wu~j\&՛|=a O[ޝDxr,uz'>\@9Ҵ5!B! `KǩmS'MW8Fuϖ&{4 zVOaur}a+V WQa@ An7fo@w Xi`JvU Y +'cfV=tz=( dCeruuٜ`dN/BEvLlXPOA3k;E}QWúj"tE^_?dTZt|+qL%*= 8?k6%+'!R(`U0smx}|VB!BWNRDr&^AAo'!}i*BzNgӕ"_Gl(3z|Zڝ;$hZ\ ~=,%GBbZʍXb6KܣJ;FU[s)o[PdCXP1'a |mc/gLJ.GOܢo1C6/ԣ[ְ=2o?qS} [&e&jk|F0TuG5Q%Oɒ, b@Z; !B!I2uC yǾCW1lߕ V :is-keﶌme1T IDAT2Q|֡I\9y :VwoDZMf:.h(ӸLOmSg5ZxFSn(2ϡ n HkGcuЧy/LޢX!>S~1|/7Irf4nY3~ӧWEj mCt cԊ'@_#|nlޏϚP|Ջa"e 玉ln&OȘ4!-3 1%sI;?#3' ger:au<k%Q)= d[z5֟c56F} i]TKV a6G Cs4zyyGrB!Ft-lj8md@~ϊԭR$FW9[4.ZOlHٕ7cJ 7u9ڋol&k>Ϛep]\KOVM?>ԕRp(QWq U2ۓׇ*h+rLe YǺ|2h&& N./PT79sx=Tn_=ƪ)2כ)F6N)#FvmHܩb25bٶb7LnSw}6B!B!^79YX}ɒ%;6mQQQ^ ʙ96faɱT{ !B!B֯_OXXX.[Օ E!y?\TQ +G')a2LB!B!xHBSl33~O?]0s`usB!B! ( XywdގB!B!=oB!B!BcP|(A 9m~}g0_X>c^uWtU7d< !B!J+EܒxVgS6uFE2v.ZA7fo%g |JNͅk~ɹCxn;6Kfu3 VrʫB!BH@ofpvL4.F^k+Z;<ˆzRN\wfqS_T{qۃXs1S[X6jKmo7 zhW0_'JЅ:7ϿJV![>ކFs^j_nK[aAe?.KL]!B!Dٔo!+6ܣ{um_e+pf\ƎnA|#ڝozycqNl솗oxTpSüqH%".f$YWli-v60i7qG^JO/7ɒ( m#}o- -uUB!B 1W$qbnwbkm{Wc-&\xs zQt 68 +Odޥۚ N^;v;+i)H`` B5ll];8kHaMgAs:3XL҃X2 Czfw= 4>:q&5*'P73 !.p/ט~N0Sd+5v6tz&k",\ OQޞ?ϻv0Q5JqΠ:ݘ}ã?=w@♑Yhl0*m%`һ!-⌭5.xՂ &v[i+Zܱ[(N6Fr+8o5#s̾%2|?oX 0}r'IG I\hIM^JN,cˤ #mG0wZiZTb4!fMbٚ|69׮k[_ŦSww5B@S={QlV&_]q3_o:m3q1)áf8irV)DmĚcxg~wgk4[_7%zcט:=֠o67zN74jB QkY8 ;8Cc-W^ݎ,7gͬ|hk-9҄B!Bd9X_!@hԬfY2'."(~/-@ӌ: Fzڒ|J)n(BѦ#NX"2X(֯ ? zɫ4KY9=q8 ‰=5@>RycV|wҫj])['ТZ ajB3mؖ {P^`Ԇý[ m;vsl3}T[ͩWT !5q9Z3H]ϸp TH?Jf-w0j,e}MĿ7)??2z(JªLPA+Rx)\#Q۽XQrC4F=Ue=Ukl Sڟ"4twU*R AysvGDrD艏P7t.=\62n^\n`WHys=0i&3|-D_͝f]z1eVY3?ul*3fJ}m&g B! \ԙO(4FAYU4q3EW#o"bHXw#F;WtңnzhMⱴ4,Z]XK*S B rO!>+!M1@G10ϡD'j֩EgО=Y:)rHTԀ>{cӺ?m=u88:ܴXOun|7\ӎp*ECB)0ZK:4 ӹ}LpvjħXv[U^39{k21Oz{̗1m)nR]-B!B*Hbnݶ&+t:gKd=VQE=GB)+ 4FW>q#_QGYtoi1`Ղ)x崜p^V=tz=( q/DNۨ0)yz!B^nmuCa}^*gSYWQ 3:2Lѡ< tezb~G<3Eet !B!ī E FWٓX~퉟T,kͅ.ʧ+E3P_G//g7a4-Pl钀-6#!1-FV, 1%yX#*8Zt+똓e0 vey;kGѕHy[r&6Rp> y|} rB!B$C1X~_ )*I^K]l?5iٿ+&u>o=lE[2HGYZx&q%\D{FLX{t';V޽61i\D#LF3>]KsMy׈jMGߏ<&%#ٯ.oSZFC0 xbɇN},'J]eѐ9Q'+Dt/[ߍ*WcV0aJK֧Oچ6Nhǥ OYפlrzPk*u[[)ڣ!N y{azJU(ԙ Xn\F[Uta|bdH2hO/bo^Z4toIqhg?.w-H` ycXw(0'u #f*.VFm>-YvV8~-TPϝǸYmKB!Bƪ/YDL֭[RR+;VYTԠ5UR*emGb՜QۤbS^Y(g2* uš!bFtF'U$xzɉW>e*JmT!ՖKzvOMvQ6:+ͧ]5B!B2_]l5]ϫ+KBfάm%.FdflLU\/䋗[b2\HLB!B ( !x t`)>oH3QJCFN{grtT{+9yƧ, ?' !B!?Bv~/QE_^+oB!Bܠ}B!B!BsH@񵣸s -UEʥ`ɐDIξ]Lh߂BC2!B! (qKZZGL1ٻj[;Wް !CI7[֟>X`+c=B!B!Q$738{{^K;dp#Z eCh=x)q.;3)G/G*DP_ 8yRA-6\h_q]<3ƚ=n+F}#$7ƺB!B:MYVBbC=PwU") ge]\ qz]cǑ\ 71\\˸AyC~b6vKBbv|)~Y<){~ѣTf}Q;c]!B!xH&W$qbnwbkm{Wc-&\x>'~lp.BUWx|a*L޸ȻcF5 AHt6mkg)1h"|N]gF#6IzpUa@Ѡѕg_f7b\ZdzڣFҪvYcW'J`[QOᛞa}5t)I LB F;5ߢSpcސeɲ_ɜju.Mp073?ơ8UjԤVH8cӨ;c]2k#Gߎy(W8zkl]}m.6qlJK*htij)g=P%`+mŢәØG!B!xsd:uda'VOTXٍzf_3^/T'qSo#:E݃=y^l?-)79ܞJ|_$UBCsd-}ٖ_]yYS`eD(-ظ'yZhyE/;M} gUD4_AxA ?X4s{!>ۋ|]e/;Ng`۪hqЊSi~]8d➶$mFgղoXp/C7fءc>KdD8}v?eoϢ]!MI?!B!"k9X_!@hԬfY2'."(~#cyoӌ: Fzڒ|J)n(BѦ#NX"2X(֯ ? zɫ4KY9=q8 ‰=5@>RycV|wҫj])['ТZ ajB3mؖ {P>'w0@5mFLH/ UAJ5Oz4>(>gƣ]BO@:9%39 0T mpZeOղ$O]C)SWlAY.mp Ot%udlit/Sq)\_R5!B!" :IŜh=Y 99nhxdMPL nch4bq\z֍S͑Z誜3cϗNP~14ס_79:u m]>|yJU%R{~+ZZCziIٱ)kK=#w9:\m%{z|mҕ*_8)'ߓl<&c<5zdt{%Ɋ'Ȓ4)ICl00Ss.L=mY Vޚ[oL&Z1nzk&l=8-s9_"#11I&[grõUknM}㉙лdc[F pʧӹƷϺQCz'~W|͇3⶯盓?Ήyߑ}q͖C7W7.ϠWi+ zl0~|6O͔dnj17ˆc< ւ쫖e #ң>;7:3]'_3Ampw}OAo'%#g!y̗B}s޶sFt_SpV IDATNޞ=7\Ur}sc/n:|;ydsnIjgwnGl~ 2yf}NVC仧}<:05K6{?y7ߔ=!;(ޚ}5}9렾E,|s9eŹs'_xzz}L#/cw7'uv9)yyק#?o~{W&6%/Mc7ޟPt`67tfWjgɵ?Nιl=%'vMϺ)&ː5S27ӿoSm1_/ c lY{f?]y䏗7C7Hü>/>/7tT؜y9؃o=v_,:ǽqBK=/ /:;;?ٲX3vPP5m}H_MVTU-;U_k֝kO޶fՀUcs{oĖ.o~uW՛UkkZT'+7zFڑ} UX5wS CuqgT>|=[Tqښ}o_[իWo쀪1ջ6?UV|5[{d_zǟ֫ٯji=߅UU-M UVWkuWUUU+n9Se;U5WZ4oڭ4vo>]UR xղ'o[~cM[->jʍםVj}jmWM=.Yoe`Qu1êZUժƖFXgYZi͟z_[SZ-_S_9[1f@սjjQ6˪y__}~1ښn=T_w|uU}ê-^YU[ݻa[Ss1htˇ[c/eW\qEqϻ $;¢Ed |pƓ&M{]84unm~G弪y}?+̞{Y_;l$m I-7(rSD1`5AgߕߜL}tE|}{M˻,EP|Y9l{nuE9刌~ja8;ksϾ&+8){!:ao)V\#WWι_!^qR.|d͗ʎ]˱g,?;4^ܪp Xu?YtoȆȇ~rW?y_-KGKsu{޾i,|fd4wkOa[dC? k6ζ;*;zwf߮%m zgەkV~{Qu3C&CYe[w{hƽڕ$Kry{`tv?5Vm>|bviOx}NnZzqɬ*Iy??mϏW:=fy\sinnUc:Fӭ5}71G}_sIf}f~MKjZjM_,8? Xٲ.yElM~d~%z2ysޝ=&n z[skz &|uf:g?D=|b97>E_w}\ޒ3μ&twnN;o9%W䴜@h Xubyz`>9\3{.=o#+W- t?Ws?fclnZrjΥyn{i&]yQO-L{s!km=є,]4Uy/sCcsk~sQ޸,:y{3k.'&ޗqwWysN>LڛrǏ.C{t|V N{vgi|ɼKf]/_^:)W_rv>}fi_7+qSmξ5SLɔ.'6s~w|ko#CoDSyI3wʵ~|rk:,:5א2ρ3n|,}w+hYu[syrOgMjBɕ mόߚo^T4OM{#,2}lxo󣏽*I9moL&&I3䕯޻H{vj+^y.l7sJ.|woHRe=_IÏtvvfy[S?_,71OfZqkݳ͓?O\qG^գ![U'5?~vfΩ=ǫ&{WzGgdvWk+zٮ<~lղ"7M/\ͬu]sN5n3j՟ۺ?/C[g7nGS7o<3o\Kgv8۾=5&ݮ3qegёUVkxSfedk[Ә H5{Ffջ1cV:fÁOm0p lXY3cP\:ܺOvc11Ijp-g_ֺ茨=sI7%}׶[/÷z]>vy/ͩ{>wKs {kkZ[[333zyGvSw͸=)?%Vܼ1k{O=uq+ߞ}{gճtɲ`LT[]==l$<°l֬xtv =$$k3IP\wmqz~v e~OhRb?=Ϯ)Zo.ËH ̞1k̞9'31Lc׬̘33ޜAK N_%ZRS%idls^nYc[Sxb&.]m=vTۯ{uwa֙8~l4M&d\y>fLƬڪ!FcLqIKV=i{FrtNOKKf=~.{~yASK6pPzuݛ;Gbg,|d [dmʈ5mӖEmH\~jWL6ޚ [q}^X}̽nKv覤Ƕ;e|&gYVq1`}'2w'͉'l?{DsJ޹}GޕE Mc7ޟPt`67tf}{.=۩G搶O{m^+䞿vn> fj~~M6q\t_}R}ղl>aDzg[gk{2UY--H#w^\3Uw7qgG]^`_c6r|뢜sNCҽL_Tl8![ ] ,{TqѼoʉΏQKo>qjuPԞgXC{:،y{FÇ>o><1=n9ᐯ-'OœJ~lP6GPCOX.>K{gs̄2nP>z;'&;?:;|X2ƍߕri>ן'zζúoXjږ>}E|>ζAtf;&-?;9g[ޔ#&浟 ڵ[:8%8+sG6G>z9w?{ѻt;62\ug-OB}}5]'|%'yjKe'f 99/)ZZ[<.{ o_yrٟ=}=xYier~G :$ߞtA>u7|#5mD.;ļJšP+N<ȧ򁋾LX@A9ۗe ޹_ 7N;8jczTz.>ࢍ'M+ unm~G弪+2{gѶ_|q;B%{A*N(15LXŗs?ICZAP| i{{7CP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP @1A(&(EX+\[CZ {XϜ b"PLP @)Irg3gz  }fرϻݴiӲx̟?޽{=Æ [+㯕x3~z뭹;[<ӦMKWW FG^L6-ÇV;1<f'N~ѢE;vKĨQrwJP3g&K]fw/1 ,X+㮵k(pgV(&xjEۙ4kgN?Uܩs3rߝZ}`1 7b߫{ffQC ]zIo^k * ~}R|o|3}m<g~Kiy|E=UjihlNK8$CH[x nxZtkN;x|^ԏn>&.[VPzgVt63G-Y`v_26[P^giʧow|R{߬Uλ/?pfL"mCə/_/RNOrgE-ϻ?/ooH9e߷[=y[3h9!UR%YzIך#zW.|{y]EXTRKsX9ٻOZ:fʂa=ҧ!I̚Pf{"K;ҳ~m+'WK?O_K4tӼhn0KjiaL8w扙2m,\^OSk:"ZiMɬz4e IDAT>>ʢieʜ^|FU[g=m@P^ߘsν#{7''<e/0GȲMټO|8 )9Kó3O!ih{J0(=:_P>֓_\uk='ORnKj 3o@9;`]zpM I+]U4i+dFcѸ4sz0n[OGC+gЕEe xH2itl>(-x]22u4fy;e6ߖ޽ecDwz֒di<,Mz֕O]32`Veiy{8nlIK,Uޕ4d'g> }vU4 ׼&*U3GFġSKca 7Xoώ4,-=2+ M޽?5:g%i:!#tKwEǖfІV)/y,=NvOLVܕw,ɂGޱzdG2[49~3U,IK<ՖKqVd]7g[kNe~iN|,/;nɌՏR}E<[kn ɒyWԪtv%ՊEYٔ8{)={6Qw%ҷ̟8:ڳb,lM{6Z(Ym㴧'˗'i5+ZQջ'ߞ'M9[yVz1C;-vψ'rG~yݘ[9V^z)WRMS ;jz74umռJ:2dFnkd$U-UjUrի=TsUӷoKf=674e'ghx8٘#6ϰOMs^DP^mC;=g޲W>unqje\uj,6弿ܛj!ٴꜚɷ=ޯ968xtK2*:L6y ꩺ5^uiƖՒҩ3}Lw-͙3> sՏH{,||Q=WuU Ϟo%,Ѓ|q}$ ^"XWVmVlH!#3艻35f̠y{A֔t.˒{ړҮ\EUOs5̠Ass_ hoȊ3ZzO7;fi3 3s̠m{alIcՙj1wZ]@XGKr EezU|1ǝ17KkіWga=Oŧ?2R.yjڳsGf=ړ>|=EUuMɨ+؍|fJՏ?g;쟉;5g<4O6ؚAN#g2ZSK O?wyZ46ROckYvw`Lb~[#6}Mi#>?+UCsZH.>ࢍ/>444+s?d„ w/ ۲m1_h[Phf~ұ6m.B+^fm~^V\CCCz%]Ϟ=k%(7wyg6|zN}>6>}zl~"W3cƌJP7n\Lɓ'gܹk)7ׯ_q=6#F<z(O<:;Q=zHGGGFV_kPr-#G\߻Oy @1A(&(Eb"P/^[mvmP{6x p Eb"PLP @1A(&(Eb"PLP @1A(&(Eb"PLP ۱C`adWɘ @&(d" @&(d":Fa~1cIENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-contact.png000066400000000000000000002452421501654372000212310ustar00rootroot00000000000000PNG  IHDRvDsBIT|dtEXtSoftwaregnome-screenshot> IDATxu|U׍% `Gm.QBişb" 4PB@I8?6Xx?8;g9&.s4Brz׶X-E[S_}ȇ@" ã\M+Zbb1'$""""""""""<ÉfBhĮwu~[vNljtp\_Vmhn XQBb 0 ;ObZ&1!AZ3셝\'RUeÉwV4sYDDDDDDDDD$$$ڈ'!!#jpus;.7t I3 YDDDD#<"jժRT bcc(v;˔jp0Gxq|wi|,"]$\jCDDD@lm|Ng3sEY[DDDVb{J7,,~ɩY,jMQ<` $YQ.n@xݸ5bC0ݸKOT3juU Y\p=5ort{mG& g6vKtx>s@fcI| +""""77W+gΜt8YLIO8{ ™۽{OU㭋0(Δ/7#,U7ܰ㇒$ """"r33ņ3߇nًMYɁo1=ۮKGV\HJ#KY[DDD`11-[檭,Ҭgp L&XL)3׼yƊjh+\p@1_ *A+_]gq+Uda`ڹa6_@NlLq6&\aӍ1QcV'q &ر; ٌ oO+fNTx,_/lo1(xzQÌ=.X&W]0tDGP\YM;9#fHN:W"i?9G*G[ϸ"s- %tjdL<<8bRf\jw ww|3X00UK+]~7n)X=wOKDDDDn&xzs)/Ҳ"gA"L[G<Ow>>ܹTdN^Rx,|hOq0^vU`V=U|b6bd$x{{%+q81LxqdJz"?yxz2¯qk<~Bsžw6%xkvūUSj!""""7-#1;_,^EDDDD$ל'6É4lj1bbd&{=S RkҴF/H1M٭^.諃"""""""""""3 4YDDDDDDDDDDOQYDDDDDDDDDDr$p8 ;ϟe6sa' """"""""""7&EDDDDDDDDD$GT`ɿq5S1}m#k:O"""""""""CyX`e˭ l&g^Ñm^6yȍ-cMnҔ.4]15[MdgCm[6hEpJTjx;fo3峙OE5mJw(\5uolNYΈͼQ}i]e*ԡ #Sl]y׎J^9v9~aݩTҕjilOͪKynԯHj-cDeeոجS":`Piu., v;g\:>w_32O}||hs]Km$qO˫K/X` 3ѩHtiVx `"ѩ%^sw6}>Na#d-5$$$1Re Y~t)%xu-3-qB;Ǫ'a|:O"""""""""rC~TɿТ]'|7KBʗzQ4 &j[#^m=Ш*c斲 ]6jY&>ۗ̚t(ƶF;\#kb`ҍ鎖#Ly J&n}^OɗZ;p6?ZϴShlԉ.vԌ ԒxvB9"""""""""r AVtHlhMmٳ?2AӜs.߻35) l6+:15i=jHC+h=6HK.qEץS̛ܓ6K _߾=I<}, JdaCtTk 5}s_`6Rn=xfwEðPwYCe8k6L8 Ιx~w(}+Df:v6xrp.Osw?Ͻ%Y6)oUx곯y'fj\n[2GJb3JYg.{sр|01?QI#ӝ$8v}2{Wӛ """""""""r+2?]pN9cv϶LǵVAi """"""""""#j!""""""""""9""""""""""#gv""""""""""r2%ǨEdiv""""""""""rRf+CdP}YDDDDDDDDDDr@-2DDDDDDDDDD$GT`ɓůL\TX:dE}}sdC=uߛ5#OrqE^Ȭse&,,?vfڵl޼ UVUtҙƍoyDDDp6o/VsABCΗU֥3-[6'8J̮rFA\롡< |zwޞ_o;#Ǐ`o׷7#! \c>}9| rk.q͕?w8 s;}  o0 P_=###z}|?G6mC,~Qi^O`Ϟ?ٳOw#?Hquuͳ?&Os>&111K_'0LBiqe!S$22@ђ1t,ZUbbc6YM吘Ȣ_1hp,SN+.t:9y/XȠ#X7Y.Iųj3WٳR~?s,?"2vf'{& `SF3 5gDDDDDDDDHO (M FN'Z6qQ.cX20@JhۦfMKll,[nc?~ï@j$sbb"z3v0 ǰpQ^x~d.}=])+ $Dz{*g"11Ν:ү/ǟH5d0 >tڵܹs,Z5g߻CDDDDDDDd؃ykj-1tdÆ d{?!i$~rjqqL1}_˸ilظ3gtV^ï6aK-""}ޙ>MѵK<~}z3烏RF1@)smlא {0j^s#[1~m$۽2l &Lz+edhNQf11ZhiP^빸Ҧu+&O /fw}ڵjnr\\nִ ۶\ (^vL4\{-| +V<_߰׷C lא [dSVgsrLdht߬Rb:8F2ðxY3_s~d'|H漰k~NP`e^JJqZ5lk#1~Gn]d+>c+q[Lж7+Ww>>&Ow]<2/evfMкU4ˉvmиQ<*c1i8&M۹>g"""""""rȰR[ -1t~K%HJ.>~ߕtoHӻ7ztkc^!\9חm!!9sks|/b\+=dG-Z֭ZPJ&3bo[O+뛧9ٸi]TVɔʎΙH~Ȱs-.Mi&宽V Fȫ<)=}fA䌎I=t@|}}ue֔^wގjFLpuu=l޲5['琓5o֔I^*.?q3O9Ö[iܨAcgO]YVrSdt-gҴIc/d",,!;xjp"".?&9/C?EZ #r:-Z9gO?3"O>(ݺv!JŊqCGXb޻~[O-?+}?[9CnZn\侬I~=.jQqDċve8ݙ>muIrm/<^|.ơC $/i6-kw).nق3oҤQ#ڴjf^hh(SNG^Un>R\nѼ9όFgr_DDDDDDDFw[H;-7S?,o[ \ATTx<ݳ#3@Ol K0f͚2iBRujsBvN͙4al˯1WgX>|6rtpfrSr|VJŋg>ϯ8Ut:b IDAT9u4_}-~yZmߑ2=mdUТu{6oٖ""""""""7 {0mNgEtW5qÃnL=CR!l.s_z5tB6y5kdl1Kq cYtsʖ ?#>+e{>~q@F_n1*ۼRvȔeztȝf3I9f'M? tL*~s5wA˖-'&Ίlv+ȳ8Y'?d7H{ë:H-h9d\ofhތ!I?--Z`Xn+R~oۦMsSim^z^y* `!)rA^s5%K 4z9/S^+%3dPΊ̙Ý=`֭\nC B8AɄ-7t\|<qڵ.sK=oe% '7hԱC{Xn=ܙw6 @Ա}sq9qdIm1Ʊr*___ x՞q{.?̳|O0d@Ҽ-ؑɚ6oI3ˋbO>uJeV֯Ozp+O㋈\2zrVG.'.v[n^<5GľQ^8ԯG;_s7RVkw~Ǝȑ#GTΡKNluS |N>gs?"\Km1?]7t9\7\.yz29*<-lD JuoזyD Az3iܸ~ŋFH XyХS<344_,fuB&M|;""""""""ף ̹i!ŋ9nQ+#=ȶ!:tnѯ0敗hؠVkKpΝ1v~ۜ2=1v<ߝN,вy3ʔ.MF!!8,Rql24o4y\Kv@MXx8W>1""ÞU);t0mޒH찰|m)+=n/_%"""""""r^e`f z'+9z}{,:2h Ob5nԐ/p8&<q=hڸE!&&!;Y~#It~FF< E*UС ~i+~Yɶm!g)lCvT ̳ˏSu$[b ,+x5?XIhhRNZ)97.uxɔarnjٍ~^ 6J\\3MK^1+Qr߼np֨MTk.풥L/#k&)~d_&m0q8j֨/?pquK7}о-ڶra[f͜F>qia84{zzRv-,K8aM01ۮW.# =ÇR^\ǒΜ9u ...cZӽ™3gt% R]:ӼYSf؞A Vժ4oތeEW)*V[dzhW,X%ϟdɒ!rBL0/,3<^DDDDDDD$wEz䄹 """"""""""#*0Hu텝܀L15YDDDDDDDDDD͚_9 HQYDDDDDDDDDDrDfEDDDDDDDDD$GT`QYDDDDDDDDDDrDfEDDDDDDDDD$G\=JTTB ;>V.TDDDDDDDDD)iuQf 7+Tn lI""""""""r3?m?q ;)@^^^x܅tԵӹ[? H\\Yt?[::!161ad*씤 8_R[ cSB)UH}-8xWWÓOР ;Gv/Cͥ39e&5L;LWW+Jy7p{oEK91qGжprtp}",L-;Rڜ}:v U8I|h%fL~Zofʗ9nX+3|حX~/o7>6p |j;6D^ 1Q>ЋRhBǏĝ;<3vy ;il%maս%ԕy,_#YyKR}{.v[9{Vijz{8ݫz>hظM[>ÙHGmA,!;S,N1i&.۰==ϤcqW?Tď`ӌl׈ʕҀv'ǘzôLz2 D]G|,Md;(`4lN-ͽ]ZRje썜JFo^uX2Ar̝Ϥ?T̀ӵkO >BBhT 僮W<Ү!U*%vOPVnQ5*VN0)S0yU^4k40D\Ӗb&;-Z9*MngԵH9(N[viEjATJ<9c(c\/>H5TGe?-> w]:vGe⋷S&}mQR63KNtdmԨtK:TTL@`mv3QlV#8 },""""""'cȢX` 'dF^73'z(Gxu_ޥ0'{|x&v<7kFɹ?VTF͜Dv qֹgCbV"̈́e`:0EWզ4.W, 3ƿ㖊l/ fdh..O3F2|F ◌a'ggc 0V"Zj` ;GΨjX,g'Ũ׻$&?.tei/K&ư'\)%(R.nQlf 7Cu+߅vO9K/Q˲N`(3~W;w%7DzfPm&иx"gP/*nó`,(Y~Rc)L/0;NrH, w:qg/&5 ޔ_G}}B hE3}}rk@KCO5unEVmG jRF!`|4=^;=Iunݒ9,ݡGӿj2HKyF/ޑ+ I;Gh] ZZ]gʪYè$K+D8<xoNf]{ *xv؋QMڕk-;vOYS;SprqlFON7E H0Dƌir~Od,} xU oM豽|}6Q!]ެ  ;/wf;ġ &45w„[`Ki.ߧ3s{?]1m$Ch LM>Uhձm{MyKY2Ç-Ѭ&j0ˑs)~V>uݯmv^@K[d*+uz{(b?;(}Z1۶hU/M_bկy*\~X#i-D8vθxihZ'Kqߝ?f;ORmEmY|3 O$-jBAnۉ};1{[OԴ}uQԡ/sپ=C^z>]ӭVsz漿O|'/4ݯwj?sуĈKקCTr:u/f0Wjի|-0 NlJ<ָ#~AşN@W |<:d@JA7q!jF\;wwQk%b$Ɠ& h=7}:2?-[?1H௽^5.] - #s!*UqP`?=Tl۞ GY{07L<1v>]k0]썿r22e?¸J| N2x)œkظ35H.X7!Y?qgzQ#öu (S:ϘJ d.ֽ|!Ɣ'ٴGyQ3.tM+/v~]w.(X]pb8؝&,̿\l.{;Vʲ7uٻ?sf7zt4J)H*t0I AAJ@B~ 0JAJ$T{ cظO=v=9{q>{NߋdO O*4׿s6wًH IDATΕ<9a:6{ؚrȡ_ >F^߬8=Hu=);knxõWG&NޭX87$OJg&5MroǗ7r߹&H^ 7Y40윷sVb튩|םkjLh3.!ۿ'el~B2╞LN?Hߙwqn?1PB |✆sfN[wSr=V>Gzv07 &*d7w4v˖|Ǩz<V۟R\_׉c^İn3VqIiQ)ygu-`T"\׬aÆ ,>܊Vn\ Kv;`Dq}9 qg4L)W1܍:XYDDDDDDFB$ },f!O6' e&KC4kE@hn~Ԅ9Ȟ9R8=546^NJ_ u#{Lpg|$,Ri%kqlg%{O 3}~1MPt6#rJohFd7v݈m7YNvt wm%oV-+aInϘfβ?qa &,X]mSh^"y$'[Μ[>p~d=O+24w]L~D>ԩ'2ywXH'){v[; yHcwooڏs09v2+Kx M}rj7F0#!ej#љŌ瓯g`^9וyɞ-Y׋ꓼ5nppu[FU<:>wS6tԖ͜z37%'?OI2ONBGI/u+K 6'HWھueN^9po ݶ; w7~ę,""""""R4i4ҋ5vᳺےw+#MnΙm1xZ3ܠpxUnDDz Ϛ);YU7 z.DȘX$m?T˞a92+f}L[(d(>]iԖ~ݸپ9q\P~ $Iw=y$?$3>a|@Kj̸hL.mǏS/C",?wqJb5-gHF-~ʕ{NxBR疉SI+g205%ҋe?(UL?=jpO_ۯ;'GR盋ܳ#Q䫝*Ust 2vPK&^%^ܴPsR/tHpM/+>x]OHFEsh9ehq_V.DP$Bqg4PrxS5ՊT)I;Ż|Ѱ&G[z$v˙m`!ZЩB>!{@ɤW:m8]aٷmHݱOW1t*˩ L_Q+?n]LM|Ev;=7_v=Tu'ץ4 Kvn9]m=yߍeO5$peOiE=5Ո/P]N=CV6-䗃0XJz9[#4ꥱyȹ{|XjTqCҪFN3tN978 ٞ0E$>70pW|82.i}h1bm/Mp&sfؐNFid0F?qY0|qG>+֐\24x8Κ:瑖V7m#QZ-US$-Ӌ{{E-z;BZ鵤"[N "}R1,C&Ьt_ G^G L̥<w-$NBJiFKZLe`&|֒nƷ2潬ᗎe?30,nx/ 7" 7NK)nOGڣ׫: 9S0E@q}0whԬ;nil BHv9߹]')|k۝&CRxX~9g%rז3{L_'轻L@鑾v)ϢI̟IQhW6JʒySfZOL ·8au+Ēv跜p -1ist@93k >GNϸE}JEF3m/A_oKdpIjC&;NA1CSRh*%ty,""""""cY?/6۫jS<b{ǖCZ[:u=G\ E L:erT_YeIXVvމg1OVU|΀%2fs&k$^گ-1~kOԲ(%$;NƲpEnxty-%m sLyGV@DDDDDD$2c*, 5kSB0ش)s9:XPNG!r 2 zc/Z`T{U$FJڋț]iqp""""""""""RYDDDDDDDDDDE f%EDDDDDDDDD$Z`hQYDDDDDDDDDDE f%EDDDDDDDDD$Z`hQYDDDDDDDDDD):3Y&y+::hVko$Cd˖ёȫs>nQyI7]ut"""""ٱc'eWnn4Mnf4&&D;i`#LT."oqCDDDDDD05Φib1 GҢ`6@7E!""""""o`FqEms>nQy%j0dk܋[M?m'W2fw@9Л^"""""""QQ 7jωMsvd _OZ<Û_""""""=ki_9:X'vƩ;]߅ 9>X1hxƉqH"~#AO|qjCqo^׎.{[[(5 {/%_}6=W:0gY234cο_cCB$LY GBY~q.]JHhݺpt8h")o;6wM d d Xe"b ^~yu+O3y/ӨL)bp1`yaɅI aܺW(4k~.ɨ1r,J5s(}XL:v__ x?#1)"""""{ ȵW|v OOU{w83)OI2x|)Q"l\앜ߝü?_.>+݂ggguϗ$$NGR2`1D\,P/ɼSxc84[>dn͆=w<2I $IH蓕u佰ү<7"K|9+:'sd$Jmfux<!+_2~xV)Qc-z'vi:i?~䷝/J6U'9#Fд ~8 ~f1mgG @ѣl)}|y=ڥ۰6Ǽ:ipycaryG1(.mu',їI탩ҷ -һ5y=Rk"Gq{ӈCQH?uuN)ïA:3c7L,gS5iݪZn?۱&fgiwt1& P[ 6;v 3PRM[ը׷mf >K". Plq\tޝ\W1fA|?Ʃcu3,=F2Ȕ6=Db˱{bܕsmFl;NH ϛT'~bmۄNo_JŅ仳-O;T`j!6nn9Hq3nlο1J1i>'ҵbcb'Oipݱk|ո ~N@AMٴwf7);A: Pw8X2 ˺3DzҘ;Q ?c@Q Y?כP%J`bzzy&Cm6Bn]ȟ;EX~=FT6 =#,cjBw1>=]5?%%J̘#*'m""""""xXJ5TmI@'vo'ķP,}b3L/KG9ל4d+" |P3?2|$~q37-x!fǯs$e'.0;I%m&, JX9|K38,$O9.LL+`' \|[5P8=8F{s~6Y\8}َyb8'JutL"d^_6FiLM13qKF>+uT-ǯ3.j4svvưgyX?_5,X  yU6qqq- 3v#=2݆iraΔƤ၏7p3{B0M0dxw3Zgz~ʼnd)-._i\ 5~atWY눸-IfB ӊ'WM_Ǘձ#w#ygŠ9rgu9?eS=^I@L{5`dȕ;N`ʘ#J'|,mX6%V~֕nVbdO~:JTn<ךo5c>$֍S/,Am^fӆ=ӿKnO#{(󕓈l޸>3MVz[`o>m#$՚S{Ȼ h~Hچ69~2?y&f@>lԭ -Kg$aEGC)N0o8$9s!+s>nQy-::7^̵E(S[u8fOß{IG#"Mt!*e $N0DDDDDr8D;qByT<M!e`G=6bѮlf1ND&0*y^ %4EDDDDDDDDDf/DƓ>@5EDDDDDDDDD$Z`>'G6vmWB_EDDDDDDDDD^`ř{a;GXt /^Q ""zsvDmh IDATHlIpq!^2`EDDDDDDJ0*yOw+#oLdʚbyj}<\?M9ɜ1 AY=#lYJL=yR|?sjmmkO%LM}Xz[*HM[љ/E Z!"/~MhF">@jwQ{^0Dz ,vUL]TmӅ~w$[Nbd,뵜4 t_G>msekEkDY7ncb8'I# M,aFWk6EALP `^aWm[0f"S?j!0obр(,})ۤ/s]Oњ\%YaĚd)܇!v^҇F͙LS;|#h57Q9TI2Ñbbxlփ&(77;wsH ǍBp{ [OٱY[QnN׫Kf1O;EKOGw>q6{~>PR7]⓾gl4y2fܽAĈi"#+< "5^…ܶ=@X!Xa`#ҦOA }v/Ia[ۏ2wb{'6^(WFIlrǮ| Ŋ5;ϱog`v[ S. K'"e]ˎ^ɣ=u_W)Ŋ$W3-;vBn~?h3[6RpDDDDDD5s΅Y%7WOf/#,WnReӶŦwH=,MydL;~ #P.G3nyXGo㯽*m/{VE*w#B~ӎn>2fwhْY &wX"5/ N@J'3^z> W@ZwѲ@ $/şͲscpFn2}me 4i^w(""""""򲢝qX_h0Mal:{T)Dֻ=57j}ѷW{|>iM~1ѳUq\|+R6f}6*)G`i◢ϤMadY0L9IjLWg.OS܎5G]Ϫ$Ag6ʲ37m!3-܍P\$sޒ4Jzs_LhO w0,+%Rc`b}nuga,)vJA t;1*M$Oo= '׮EQ#ZUFaԢE4?5kf#㄰mH%j1I0ea|dO HP/h(cf͘q&o9>uAcf:ux֬YCѢEYz5.S(GX EgDy꭪•=_$]IG""Mt!*E!"""""ڦ R<$vic7Mv38aL``qE^|rNDDDDDDDDDD^V;{""""""""""扡qzhOG!"""""""""f~ 3̪,"""""""""։~qľ7?y`00_ ADDDDDDDDD^h'CCC(+=[nXL ӎL{ѡ[$ Msc,#{/uG""E!""""""oh'kթÏQԿdL#/a *E(/"""""jX=a& fyE;lfL!""""""""""`hyj0^ CDDDDDDDDDD^7,""""""""""Ѣ1$t44Ų;f4 ! LDDDDDDDDD$s 9K~߻a[br;60yM9vR[| 6fw\x/yF ut"""""""K% VnCpËyѴRbe1Υ3~q$ٍDsSI?|]7;aD,oSrŽ܈4݅ p-?lHŸqK:#)ٿ)֟uʽgT./W=S'Cf?ҥ݋̺%SiW$ch<̺A,_UN'mHD~B5FV^yRu'웙.]lpW#kMvN/z2MR|ؿ#ݹl]{VGrwfNP=|{k<מ$ӛV Df}ҁwȊݙ{.&& z!=8qzaU+w7Ut!_:օY0z-g< +ѡOf19WVd^Fusֈ<&@cVݜ7Uyo"+:üڡo?-~=o!71OOF]ϟ㑡;t>V~\Ym]qa! 8<8ʺ9>G`߱\c%Qz?*ЮIQo\&7h0j#~j?w (d)@J0I-`5@?L~c?5Pg; @%:Ȋjt ol֒uկ |pO=j-KV 1oֽD\~t+e{vazPe|mX'Q9O L~ о!nmYIlS΅k >&q7NgȀj[#%^8euPHٍ5R~ ӹKc}q)Nhh(̆גN$Y/u=F.&ΜKF{_Ոmb̃xT'^sыZ3k:W K,xHm:`$ń( Sb#by YV 4pZ% 2a+ipC= H<9W>Xc1vs|I=1/rZ!W!5Aɝ~yع`ML\N3 ǻ2Mx|"}|}I]I9c,WsvsNdX!پ/4rÂ~z^yǴ|Jw,dǓ6qo7Ç-SiJ$7N =R(.I_3^6H<#;gЬ\{~AwXo'uW """"""v1&2,ޙɔΟq#wr%n=-I'qՂA!!/a`1(VD,ǮO ̬s=aM[ЛI^$? y R`d.ႛy&7/&7Ď+.jHCa]ˎ^ɣ=u_W)Ŋ$ps` ;·oJ=pkFvV 9 lHzNDDDDDDD4z'חS%/mv㵇one$WtVpO惧(} 6sI3)jH L:1rdnnfzkP<_.OxjCG\I5# 9sĶc%kGۼaK\>zv W@ZwѲ@صϼnuGWTjz9ӼYvn,"rme 4i^w(""""""b?<(>*P+/;>:IwthۀTûs _}ʔKQ֢7L>M6D4FDבlfxg︒,?M'Mf`1H, aېJԘc6Waʶ5Ȟ;t)S,$m3LYF];~AC3{mƜ厳7}t]_kX0wYNh͚5-ZիWSAX /:BinJ,Cޡ|2[7mIG!"EpC;R.[R<\\ ::XOq+GRtxyEڼho)m`Ι|R8Y}FԹ)ӤNI[,  \X,,7[1=7\Sf=E+"/Ve,+Qn?~:x2va_F%AMo[γeRG*MIB77x:iC,o<&~׊׈np;Gd]{1w%~ynY빌} aY}z2bz ߡhŒ i'P9h=r+QU_v-O(<7~2A/lEOt>.OYUSQ뗌m_8'h Ly|LL";!=؉ډjP/Q~Jlt_Rk*EIHZZcA2s~6%IHy3ss9| ؝?ê:DI .n{T^pjOifu*]a!W6g_1ʒ+җl {Q1 5+ QΌi;crV3S#zKsh<g'O~AHH$T0_ű0F7߅/pdOX;1 G 4ާJzbơG7i%f:"ΖaМp~\5Ilݔ~ktOVN.E6K]L)b Ħ+̊0vKl)arן|u<"j2\˘ Mf) =ןqkSݿe /ҝ[|5.M2,TBb=qw/#::_;ٔ5Y;fOXjK.p IDATez"""""""rZlY3ŪT$C&HZToKL_f7j&t#&N@=r,&i\@@#]kQnx K|I;DKm*=:mJo'j9tõ_@Z9^Dy.7fк;1bq(LJɒ5ѭvyvm֝PcQVޜRrb?h*oiN2; !+ѿS߆gCu^Pk3q~r_ eɥiHJ܃9IpE AmP{Ӻ/78q Cn3UЀoݣ-y' 6ƧַNb Ms~}3%ŲX8wq-'19m+`>ד\7n.Йi˨ۯUlOB…cq<'Ѱt ?fUO$dܿᅧu +nIZy%߰FG*Oq[~?}WP%$I/{:֚-̞Ylb5f>[^Qk0t .""""""!ݷj֮Dٹ}+TO2E8y.dr xurZȈʅڦEsf1Y~$3j:+L&,(T-IHʄ)嘛kn=z1_d-~[gj5ˈşkߣsps9[d剄{^iQx/9``d<hypqt&O%Zxޖgȍ܃F|pq=/:1nab̸·?G~gY˾tFe?}FP%EDDDDD.^\J$*qbLAT0`bӆlu~20Yn]ldOR\(ՖPۙSZwvi\9w%{`Lݧ _X̲5Op vے#:ǿb{;a)s(dqy,ףCNʹEq< ]DE3mShzW~g2p> H د 3W'36š4b./tq~E1UeCVzt MgW4)rcpv:6仑uJӭܹs>l%P93#6NJ̨E[wqu9w {㺝iD%E3!L Jj/3`|(+cW1h `Єj^UGgQy2G?vd-PFG:>lqv}{|:ӉXғbwHp;/~=g?_]f0WeOɽ !A<>v`X=|X?и̩q)w=qX6L^?ÙsVۗR ;`ZKEG0.f`Fmf[B8ܽ>uk{u zyl {Q1 5+ QΌi;crV3S#zKsh<g'O~Ac 炑ŠAo؞ഹ-|^ cT2.pdOX;1 G 4ާJzbơ{<+1{q L d[˥{¶rrg_,J]$bL'6m\VfV[D:crן|>@f2|4noWѯ5ZI];{%o2"2\˘ Mf) =ןqk;Sq+E7-*JӭH}CZDS!w|;ۗgMίXlȬDGG'b,;VDDDDDyb`oȐ!L:A{?r)o/U+_(8Zs5͘MF2C[hv_pt_a*G~pzfbw܊qK.q nh?=.&2`ӅA^̙ɒ._eAtn)9m-ypp\w\p,|㔣eh3Sq鬼T7$3yXv+vNѵl?`X1rgˉ=ԅo=71A5Mjxd1,T7Cwߟ8c^JX:Ux4%ϰbȾ{[*XN,crX!V|t)㇯'K˹+xoN\3BQ_'H (Dž -yzOa8~=ͪNyqIɸ OVD^q9KakT}<-9|_;K H^6nu5%y[=)Yy6^Ⴋ]a.G:Z8fqW pa75|yήn{\]EOcN`jȴTG Fib 7̮x)Hf (ٗ.`2IZ0 >Uz҈ɋ`>!m?L[\ILN_;Gٗ|6JvxGJYb:f+ 8zDe!o>ߎarv+b"19> ʼ -ހc[gF dEi0A,ٳb&fbvp%["M}x?Viʤ96?%ǿ׮pŘZ?N W7W8|Va2y4˟KԎk\?5$-eid*3%,JPLr EkgٻC>n8ߏ]g`q)?#lP 3oz[Aq{jDDDDDD9 T&?*sAA^v-Ym_y"m,=TLz: yJO_t<y"7nZZX +N [wbk]ca0 &$%KФi fDZWmB%hٲ#B,Y8-2l]PDDDDDDDDDDq?.OZDDDDDDDDDyfZdb?|vaiH =`GZ65_T0ۼ<0ɟ)M0鋎Gd< <<<`%Jd6TjX9O;ʘݻvq'2'*e'EDDDDDDDDD*]/Cx 񳈈d|6'EDDDDDDDDD*EDDDDDDDDDg)haHF f*SIž6w̵L} 6g=\DDDDDDDDD*U0똷l+L|l\ϏW9kTsxOoԻHf̺qc&k@^88:X E qՓRY<\A<S ;g3M5̌c&'zQHg$uVt!f{G7 ~q,=ؓ5_Q.W+"A؄ҹptJJmoLC7zrgĸeIz&6..8_oVqŲo*~j:+~O߆T>6GDDDDDD$)PȈĦ N.o1eDa܊#$+'wNP-=G3q4W3lʕsgY7 >hGnUih]}3m K 9 V%")p5ڊRgx̾mP,Þ2kk҈hҽ?Gq}V Y5p7Až D\)CwӤȝcepv:6仑uJӭG*W0bΌ؜KeմtlՂyk,L3`\`ݸ(8m$Ƶh\8D^|g7ɹ)phBɺ[V{z|ɑʼnLd/Z&gз\39L`@0mt^6q}+QLNxNr)t 1rT݀CjQ>[? oql(T+J7gMG~ jNټYp[Ӆه-@ۆǩdcmWҫ~3=YG2 Ώ3)ؓ-om2 a+'}FŢtۅbY)3%Z dڂشq%[Ynxwd5]h\;~^DӒsN&&s:6/Uk`\\ɥ{o= #[,Y91ݡ.w:a.|t5~BjfGŮ}'1_ش3¨Yq*. ǚD\ںv/=nE4&Pup3y޾<hw~MJfS@f&::=c)o}jG#Ϛ)V" ÄSiP oKL_f7j&t#&N@=r,&i\@@#]kQnx K|I;DKm*=:mJoW IDAT'j9dusGw>i2(f%7N/i9[ƶT.t#;A֭3 ^v SZ{b,0oM~kyoVoW ?,A>h\@7R'+>Mby1P-(J辇U`+=>t`-PFZwS (OsC1fW*O䮉nTҎ䔵1&rEVeç~ɬGDDDDDD$ \`r0 L{W3kp`D@$^n̹;8nn؀}w%51W\X1 #+^Xtʍ.6 ;,*^x9Tp쌎Ғ섓NyxcuNi3;sܮ /x>xݖm켜*OP'/|d;WĔErB̙qy'ՄJEՃpr}k4?70'gܤMW """"""Hffۧq2g^7a3c"Njd6c6 ao_bu~ӕC~![,Zé-L޾\f|$k^w={ !!LdzԢa$NjWq@sz.dܷȲ \$mDH[ƜfRqzMۋщML^(ʡvm7Xp/V:H5~هᓘq'2|y-Y)I(EY>v#'#?8f+6GpT2%Β؎N{4t%Izo7ZRX$ S &VC'ٚSҙ\RLTd93^/A9?{N> S3#~>t,("wqΓ6ۉP9d}:h v:GiSr~~XI߱"+=Dsp1o4İJՂ8vTP;L|/ޮ{,T9BI)&;.! qwoZW'#ay-t {%BUIlHӜ_q kI,i\ˉeL+D>.edi9vͩ tfZ2D3ӱpE OI4,]ǏYS#/. 9>0{xi]Êhҫ>n^x9dzy7lёʹ7{e_+L]CN}ܲ:_טb< B:%S+ y8A2s׵^Xs5$s#@ͱ-njC{Q;~.8$o{4yvH"#1m;uj\6YSEԾisRgԘэ9foT/k1,'(9h9#ku"""""""6K嚼2mc${# g5k@i$;t[$_ 7WR=_FL^4?%Ԛ[\I299r~D6&xZ^՟!W;RrGc-ߞ捛D ,!/xREz'?JwDbr|k> ʼ?Ҁc[F4Bh8 Æk&4KP!_eg?^d*> hއ/h2c;ȁ2C*:>Nv_7=. 'gt3|FU*VV^,n y>y9Tb]pc[W\m/M v9.SfFj\]F[кX-K>#L+""""""~Οk z֮]?k֬uW`xH`0?j. "bO(mx"7_S>k7v_@DRx/:"""""En@V0 HlkFbWca0 &udb)hHf8rWĔ{\y^^߫՞ ~So` cT~YDDDDDDDD V0S) 7:gH _i+O~3""""""""""؞`VYDDDDDDDDDfsYe fK%5 sBBBj!"""""""""" FFF1Ղy3xs`PK/ƿzԌGR *rsDE#}Q_DDDDD鰹EVk*!"""""""""" fyٜ`6 #5 F""""""""""b$U,""""""""" 7zrw^*냇\V.6(33G# ?Q3s"n<|Щ:ٜqM譴v('Q9<\qvKN[v$/3..8_oVqŲo*~j:+~O߆T>6GDDDDDD$)PȈĦ N.o1eDa܊#$+'wNP-=G3q4W:>G`=6aБLZ0·37mm>m#I#FILYljZ[U&(6.oXg:s3fw am+V+(XlRpT,Nxʝc963XΠE<;e-7 a鱌wZ$Zh,zׯ@L/%%} $d[ږ~e餷yesZÄE[qha:3fsF\,oE1=& cMM8لd V(]DDDDDD҉LLffr/1*+C8{gky',_ף_Eq%1Уgg0hN8?Ϥ`OnJ@$\ml9g̔h1i ~bƕLoefEW%ӣ^>:Uw`ubkݯٯ8<~ϐT/ ԑiלTKzqRe)v}{n;=nE4&Pup3sʵ/#::_;ٔ5Y;fOXjez"""""""rш9fUH0TE3[:-$Yx)݈S7zɽuק=Z=y3_Ҽ#RJ~c÷N[|o?R ۦDEZG+pP]jȇ[f) bש\i*cẢ/5sQwŋ̔(1yY;묄,:GED*5}˦Br=(5r7ojͧ̈9ġ3+'KrDT*iro WuUl!!+,atju@|r欜ޱv˭U9sm.pͶ+ A߿z]_֗YĢ VneԤ vo(]s%R;OΞ7:>io%-`hȗP+l7⸑1wH֐o;uuMdϕ *r DDDDDDD"UB٬ow4~˜9{J?N܄Όxͯll2"F38=+͇BSY> S3kwFrUYo2|M̿ʘLU}?kS,^D !!'/b_Ur)Q,C;v'V!n^upDDDDDD$J[ZILvw*_ovmӱQóL9|9GN-Gq2VNm$eJܝ4?5iJ6ǜ-2nN2(],>IR&`MN:56#v z7t҉2M\K۟ef{s4ի_T)*I)qO)29ͻ :j17kSP;L|U~}/% :8Q""""""")es!IpE AmP{Ӻ/78q Cn3UЀoݣ-y' 6ƧַN4%ϰbȾ ܝƵXBR_OsiWޜ@g.nNT=>P: [D%p{ޛU>5⒰q5&j慗s<|~>yƥDM8H7v lݟ#&q[ Dv" EoXwڿCIVfZ0Ncav3OA5k@it:6eB1_ &\sJS~@'҈ɋ`>!m?L[\I(&'GίHУ\KW>guXr%;#,gk 3=QRz޲|{FoNjg09ۏ]1f e^o-3vWsx~̤}I@ k2j໌h3 y(d+& jr = ޢlQpq4~\p0ΒSykbC}BUz"OT<{ L^٣93GLf;2unݧF&/|ף߂ *[Z)""""""-k&kϚ5kh'-6̏˂JWQ^+DE#}-rըհa`Fb\0[[ +a`6 |&K/N$d8O""""""""""}Ԍ"US) 7:g[ *S1 plO0+,"""""""""\$""""""""""b̖Kj!"""""""""" 愄ԌCDDDDDDDDDD2{[ *rc#)!|g>} .^Z@TF>鋎Gdas T CDDDDDDDDDD2""""""""""\9lFj!""""""""""*EDDDDDDDDD&6O`)&?M&3 L .6'mI1βv=Z0^U0} HZ,X =ȞLad| {>{1636nO=?&|NHQc8}֜ӛKDDDDDDDD$#H$J&ecmq ~;gMݱ?v)"&M]¾kzoʙuh]wL8e-Lϕ׀8Cbqqp5W&G =Ԓ8y.dr xurZȈʅڦEsf1Y~$8yq[ٜ7sp!kTiއYgICa%|zL4'?<-\'#|j$+Oܛ`NTJ00-,Q,NN}sF2ӗ ?ߍ܃F|pq=/:1nab̸·?G~gY˾tFe?}FPtIE)>U;ش!![ݩ-(L[1ydbbq4%XڲrrqW sT{;wj?QNs5eio/hMkĜʙxbnbPɔmqjbGiz\bJ9Q772vrWMm ]u:(X"rSLISڝs3Ii8;FMhXγ{4ƌFk+6w׶֤#vyѤ{,D-*գkn=RtI;JK6ױ!ߍ SnwqU_AA p;57ijiȕ4WJ5W\)*"qfq*nD\~{=pyWDDDȎ ܔ l\/6mLtsǷ$eͰfT),NgIš[11L^.8dh|BJA+4ѩ)7Ҿ2ycM"Cޣfo:bgB6jcF5v;N\Dn'Z00.b<z邽#^i9xS. JՂhFo&~ۍ;ٰ]7Dj%NT~k[ݾ-Mëx՝ԟyTޭwLq7^w|Cܔj<vȋǹ#clP0K$ldgJ zMl;n-Ry3%mKeݟokL_+lBo5|6sg5l GMѤuWF~ MXp51>>  U{իXO,K|ʻ&"""""""c3718A:Xbveuys`c9-kqGiz KPγ#:+曖W q[Osl̰! :Ad1ܺy߱2Sqo5K=[鿗*٧a\'vx|:w]r|i8q6oҟ ד]=nle9ʂٛq7Yޟo_w:cfZ23 qH9(֫6ap!HŴChe~6@ GDsRϩ0zDA ff {}]ާHt%(t("- 4add{ЍsMlCDVwzR{JȏS`*zM4Rjkr=llr`5Cj۬k"""""""O\qcߗˑ)m}^QP5g6%|%ax*+QcaR4VjQKK5 ά>tʗ:q=HrNyݱ])\JDER4iHZەjbMvF| QfP:ZIYKٚXf94ٿR R~ԟ1գc`[Mb Ʉ?ʔI3 gDt-6Bbѧ# eILf6 (yl IDATEzt /Ok)R׻2S ? K=ߍsK6Y)Py8"0jbpgB)1ׄyNnQUO镩A8c;G=-')1ɰbIDmOrςw;杵gJmM~1CRZ7Ƴ8f4YҢF /U[U{vND&-BVK)b&4Ȗ|~Dl5"4:J~-2̹J!Ʊ4ƙ~ Ц"q DDDDDDD+1[ ga eWt o_?br~Aމ01DCF* F#F.]76Wby^rr;4Y˼Y;C=%+I x{,뛈2i٥d$,09Qsd|7X-XV Eđ9 |s9ƭ&CTۃ9.Xn.˛?'eXVM||1+O_|Y8=VͶ}f|}|\NDDDDDDDUhZ.-Ř<5oc͆7_bMT<P|&lĦHQUA~6lc6hz>7R(W.B.`IΆNݳ^O .ew`] dl=m,t>_Զ>RVޘs`̍SS4sbkZMqeytGFy"\y8#7].Y {><2v?ytZڕߥ *ynØp mW ѥc IPПoV${,`v@&9,qG1:-*xh!?\kugLX0qaюӵmmJvt0D#wQ)Q~ⳉZ%Φ=bɌ \V:_W=ZɗCr4,"r Ifޒ +{)/RЏYܾ'J;Q|-~ݒB6 t=9ZGdMd"oxCpCp.4˕l7/?3HexRo¨p3v#H0ۋJ kS>)FoGfј)rcVc \2z=?g@hu"k./ː3rGά;aٛʭgExsr޵{w} l49M8htJ4$++18ˏZ]0f1Q$Ԧ&ڗ!˂Y)|܈{>/Lc57KUj `s{>٤/5[#߽~Œ I{&gcv2WO|BmORH,lRX0L!gZ˜6y1[ ! *N͐9Qv|cgS$y_s+an khj*h&"wQq==~6XaxޟݓKx<[tyѤ; \-## jz>g""""""OFKd`d0DDDDDDDDDDy`Z9y(YDDDDDDDDDD`y=B92`H~h=G K1٠ :bgg2~{v%>\KI[!o?NyDYEȞ(tK4mgvZ֛Y{cʷ-̧UW̍#nyJRgRo|_ZP.+NY h5'xfOO[!VH2pwv-%1lџ\IBz^5㽗]0˛tj-4+y?5'dǗ>Kj;~B)ۖKG :׈=|ޠy@W= E~c]=Y 1;!Q1]٠0^oU)hriZ4"|X8^EO =kwnGk.-&bF<o_̷%lԃbZO |VeNOۓpzy_1iXŠ!Up3i:"/GR+ ֱۡ$28Ȏ ܔK3'ݮz5a4c3檍;e.f\aG}]rkFhY7 X97:c<^w0s`D%J/˲C phVڏ\Ns.,R7t aj.{lCS9Wc< :p`ZvyE{LyҰu@g2h=kYLףx iݧM^pQ>6^5 >Tzra Øݏ`B|I)+r 7]!ok¶d_{%7m!ZL7a[qLϮյ-2vOte6u vؤU['u+\M-}0fc9?N0zcԻ<0]2718[˷n|_+[[_~Ð`l,eMBh8|.K}Nst„i7i!n"cK,_lҀ9ǰ-ahT2SqoYɆKȒ!kM~y]NY`Sֲ^v$).뜉" uV|1dK>YNsD"Rr l?c\v>=ZXQ]}LShS nA/=ﯴ/G{G˘&]%pnq DDDDDDD+2Dѳ0Z+:˷pSgz  mH")k01wߡQXڹDRrR|>hޥӜH>]\K/<+,)lllFeđ9 |s9ƭ&CQn g=X.沼s™X\_feeȮӿ-l6d#9u?:ƙ0(]Oh/O_|Y8=:9 vٶό/ˉ2֡ -_+ţ󃇒ߟmp+QǣT ؀SD-AC1J*Sq7߆]c&͕BFT*UJS%U`,Щ{_Զ>RVޘs`̍SS4sbkZMqeyWA#NʓĊ6S#:zQ)y"\y8#7].Y {8=!b6>`TƌLCrLZ`9Z|EYsyXj8z$jfo*y(g~1_4k)L :g.?jw@rp|hM ǬF+R^Ckh_, fIh'>U;WM} V' ܡ̌c퓕6WgL\Ky헹zjGOeaؕǂyfFH8 9dƴɋ0ָDN܄TMWae$~T}{ YKPG,՞ȝ jeLJ1vF8=E'51g.BhSyF̰6 yV^M@@VYVqxabk?. !rW2<ڸ21I&7x_6dSY䙓yyd:y~] /B89lbw?$9yXljQOEDDDDDDDD._Y!2yc߅C*!"""""""""wI2rʖIxWt~g#d0g(DDDDDDDDDD乓"""""""""""/4M'""""""""""dqs&fɔmz7ܸ13l0tfe0f-:E1ɿ[eyZ2֌==ySk$V>5ͤ6чΟD jK<%\DDDDDD d2QzcgǶٳg/ŋ?|JdGaV~WK;gw~,1~+#3vdGփX|y [3 ʐ/#v.x,CwgWkb`C6 9wg0{95>+?xCPgkQiKM.|t\uQvZ??#439bQk3cp;^-ME1)l+uK<%3 6qlE&L*yfOO[!VH2pwv-%1lџ\IBz^5㽗]0˛tW?oкn(._lPA̪4Xu - oK>,'Ӟo5b;gz 7NG~ǣܵHNHψoΌQ8ս'>#S12qz&$uXϲ&flf\qllX1L/#"~K.xUz^-+4"WG{~.fDeYvh"Bˉq%ؐ`Y$W-esq*j'rA'|LW:2}/^0)oYøLg-c|\zT߶T>oO9?U"WY 8K/̰߼+5u>x_=ũ[P쳑̜ow#zf-K[p{"&}v)?#ٙԙ`=-8x[&lKeݟKtZ⬥iª_=5Z=;ly:u6u vؤU['u+\M-}0fc9?N0zcԻ5֛Ť{y8]"㮑MLa7u38ò'ֆׅ0$a~hYй&ya)k0_; q[/fF`X;mX|·Cۧ .Ɛ{3nJ6_Fli=HOM~_ ;^i3}^IkGԥ*q3';׷רڼ1y%8S>/_ ɔ,~d4VbT, IDAT<@k{6-l9oTd+7ˠG\ ^ cF3=|]/dU{ks3 `dd+T(ai7Žz-/T5 q7gZO/č8e ѯۭD+ Lmbbb17&g23qG 111lX/+Vk~2ۥ?K:Ck0[?ֈGWhTA] Ӗ/_aGV=jC"F]GƒLݺ[QjԫE͠5d.:v{=e3 ɹ wnw4 kFǼyxV+4S:g]'y4%si~L𮔶¦5[HW7|͢52{@ d. [/n|u |#WT̆5S`;8#Ezkb&㇟~Ž~YJˋCg3""""""?f4{NVa"[Sif`0`08Q}$Lak7XljQqf SLhAkWY={b0f53\8f ޭ.ЬXwĞS=[s:6OYI#G'E9/=`p gb9s}5j<93._L 7?6ɕbOJ-3'OcȞiI0x\ ٶό/=gwPQz{CO^6lxs(DQ l){{a"Nl䠡>˟Sq7߆]c&͕BFT*UJS%U8`,Щ{_Զ>RVޘs`̍SS4sbkZMqeytkŨ3egPvk9<^oO\[omvĖXpދoոsRUz9ݡw+OӺ֮.]hVwG[h]4.[gxOre|{"k$g |ݵ;7=g糖l>hQC Z\u;cƂ [D`vT>3_hW CyZ~`o`6/G[GL#Kԧe֣,WgP'͠E.[qqNYfS}m72b=uVn )ܖn fk0cd֓2%V*{S^0S}JO>+wͣg[^%l+zr 7D"wVΔ3 u>LϤce5='J́cz м.WyDb̠zDDDDDD>Fݘ1=gƭHQ|5gFƑ`t#֦} i 0GcF\v!Y9Rf:ryc0-Չ,CT8z$jfo*y(g~1_4k)L :g.?jw@rp|hM ǬF+R^Ckh_, fIh'^!ϫsD>uKwPWIvqwľm hh~w1] TkY/_N7R'nˀK&<,12x-i".i7Q/s'$ 4֙+Տt0qrɨ%iPtp [;!rR5a_1̟H~i>2}k/W ϐ9Qv|cgS$y_s+܌%Q207bIHCEW^M@@VYVqxabk?. !rW^ب \-[5s`D%Jc_S%t{dNw""""""""""|n]:c48ː0Ltw*"""""""""" :vGz;qc$Qקsx:"/}-:""""""<<ܟt1#&f7;wr̙҇/`ǪD =2wG),""""""""""ϿtEDDDDDDDDDfe0DFFCDDDDDDDDDD7`tQs1w[ `7&ƾ՜IOw`""""""""""235Z;^Kzu/#i<',Z3fe`ctZzg͆=.9k0:+""""""bZ9dM3̏ `)k^ qs$K^?j#k238P}毺Rdn\qSz=#8pgolCnp;RLܝprCv ['WRf=-o3dxe{;6ϒ6rf8!/>I-w{ȸ 0w7jƘqM㟃;wCz{|,5ZOltNğKԚ\yIQ1]٠0^oU)hriZ4"|X8^E/=kwnGk.rѸ孄ׯ'yrQbd ,CfӮuD^,EޥK/VNyo :D–Hv$[ϯw18ֹ#ֲ.U7S?ے}YۆF gM[r5l GMѤuWF~ MXp51>>  U{իXO,K|ʻ&""""""&K0q؍C 䞡yY̮lmnj~] C}懖5 k,9ҹVޤ]k-|YZzyHzK@y߱2SqoYɆKȒ9˷@BD-5ZN!,kkX>5K:Ӹ/\ʩh38U/fSZ~}:WKDlX^M)Xk/-3L钗XmJFlzp)]"3;{TxN% X86k4OJ6Z[ſ7N6ۥ4b9 "!mq>_e[W*@>y-חgc*oMd6d fbbb9jvOx_DDDDDDD!ClX#/# 8Bjا,I<v775"D]F]GI5<[QjԫE9[ ..(3d8)_긺{\~cF S~{ș KBiӐ>.|?y$ lb~t(P٬PHp*H:)e'a]:(deʦo=?{6mP3o1%k!5T}"6N-C9δJi[+lZ| xOG*ŭ=&lP֋?W1_ 5)ݮ)_̆焥y uʒ&+s]?_s)*i/;b9 RuKb:qwS7Ƴ=BWGR`wvF2ys gWѸ8K; |n9#BBj''[$E)Mۯ˘z55Ed.ils{Ӹ 1$zdC%Mi}Nݚ=_-jv?נh6w/eg>uN͢p_{GT}`}%Km[ɝ /[l?_lfL (Fo} ާ9:qn.oG:R3`"q$%ߧWY fr_8xB XinF4J*=NըT'f {T{CBb`%]_sƿÛSt.O~X6E߂|@r~̭f`M"Gx9' B: ǁhl;o{X=I$Uo!=vˆz͛ kP4F:̕8=y;2rt.3_ڌ;[/|#h5SWqɐBej%F2 */T偅m6wH_f4o;1Ϡ}̬9Z1i gtn%]iH??^1)X3?z%x&WqG^EqBZY5?O+rŤ"[=ْ{E1ve{87NB+ cA86z "|,%h<| # DmaQ:_j¹"#JoCې,pĨiC4,N^-Yn`AR=2g480f"Zj~~~\[>#m|0]ANzʵez+'w"9?RV/psO@v 1ݳ'`0޼ǘ/)Nf~gy$~"I9Rf~"$w""""""""""϶Ǩ`N(DDDDDDDDDD䩓2""""""""""5M'""""""""""-ږqS& 褌CDDDDDDDDDD2}ƍlٸ>ɂǧyOY""""""ItQ` ϿrR#aE(/"""""HIC?D+7D"yu_-l;>nGRyɔ7~͹qZXY2~YBXp% hqhMk IDAT';.=}Ws>AfLxO^7fu5a$ߝ'Gtvdpw-mv㳅^->cofZ۞Q8 d4K~C|16GDDDDDD$DC5#Ùr9#'2bvdؕaԼTi5EFlYskn9y^دg >>ˆ_[7^ ӛŠ[s5Vs*׬޴>nb.Ëz1z77e85)KX>"i-kWN}΅pn:vdTuM֭ϓ)_%w >Ւf&M̹.w҈oyeN{9yFHz,I}6?DvN۳i;~U?n{^a./w~m|-RTGl@Ŏi4YY "?<Ԡ/[ mx;9@Dzz+ظ'/9Au®J}YTu{Gݿ/pT/S 9wmCm9yw:VL{[N0= 7LunDmOG8Wd54TMَŧ~ν'"""""" c+Wѹ"~Bg\Ssc9Cd=,)԰7_^Kʒ^-F!򼰒"G _91cSO;x{'c^?Z|q k Q qpm|4R' `z,7B]|rt/cܷEϟ%8c7cGF&Nڇ{OgIC>i/;;ϲc2svKFJhh(S9}-"""""" ˜eعuIV EѩEf]DXfqhnQ;JsT\jx`˖yyX:FTβg< ׬fV҈vqo/rsN:ܚq}Z؏2iT@oǥxh@itp-yvyb}&J,2R>2MO*]q׵'692a8f`kSRLa.;F;Y oo<\fғN* """"""")DѶӄn-:w;1EX̚_? /[l?_lxyZ8'.F_Obۚ=_-jv?נh6w/eg>uN͢p_{GTdɴ }2SCf{z?Dߜ&>˓`#Ʒ ?'-B#"O ewHs~ 9RwI/? & zÛ!g^ L4o}1ޥ)хoa҄O+&t~diT9u߻](}[:W+ԩOMLޡrҔk]}) TfאƐd;"PHe~x vK&sb9?qȖ|yI?Hvl ťT%,%&n%dC54V@M tgo!T1[RYԿז+ iq0X;%ҧpEDDDDDDeiq`#Dj*Xr%M|ɣزq=~; OY)CDDDDDkw|'l D͆C@@ X?ocLS'Cd>%CM ITsbĽ8୵Q_c\'oz7\DXt$* 8f%;^^㘭\R\RǶӬjNipuK_+9M4? "|DgjMwfh@;>[x^f.-W۟髃Y;g;4͒_7b_pL-PM|cd\ΰz55-;&*븤LS:Rd.iNS2e9$w|c./Z)] G|Hl\;BitkfY3˕d=-ޘ ?`q!{1cZ];TGS SvY6w?u_HcX7W~A^uh㺃ozBֈ<фЛ6z1^}lOz4luzQ2ɅocH|u;b@o`b1E%_8;:̅L@Mmw0z1.]wW}} nR;qg^qn 7/j254ǴquY\}8jak'옴n;3f]b'i0a2>6;9.^4e}q*d{?8żutl 6Q]Ϥ{ØHqv+nl4qw,`z0s71sE3p92qVvfj^ȓjRԉc ZN;l,[r)f0sfN31_Qݸ^`.bvelwWz jTQ~lQs?tK~I3U=$tI;a|M]̲g2'#*.Ssc9>.4kIMj 6_&VjQ!9!~2W0CQNf߲hjoKuNwYKͫ Eftb KzRl{5ӳi_ 85eĂ5, %oѰ/\ '3 =mWY73YU>j|I\ w_Ku=bW._ICR`N>s9J~W!gH)_ 'wႆ{IZQ8i??4ӟFibkcI,Ihh(;&:JfKFJhh(S?x$&lY)WMZp-2 *̭̓܉VsTjV6Fͱ%m!ԩNiG 6~)1h(3TKyuK}7vViNޘGJc˖WȗK֜o0~>^څ_j;:tlK^xAʩ>j)TazC6z/Ƭ(k& UkPE ^0{ڷawh)}P'PY,{=:v#ӬtSyUzL:Hpt,љnqn.`y&wT.À|V'NW<8v*F%?w&얣iNĩCb0H <L&¬,S8/_JR?i{rXZ_;Sv8'w_|hGDDDDDD$%9vwf#'OΆ|{< پex)VW wKtϑ5kPVBפּ6o༊<:Px^fHWv̽h'2&)ω<>v߽ISxl~J9 u [lXΟ%N9f"kFt2[Z)?~?u瑄Q7+SD]?ɘc;Y3'"[}ǕS'f$LU!QjO?dt9y|ĚK2fɄ^=Z;2=ЛNNf,k?Ş! TM()ÎcKfZo2=rC:`mFn9S2h $t"""""""ϊ$"ꕟ|Js$C[:Xp[8$jj1q‘/z(/8Ùio=k7vz>%,j_ic'zKPbl6lw^lqɂǗ^,Vx&4X wޝ?j4a=m*άͺei~LZz?vqZ;|,OSpΜ_GVV̒"<8vQakqy A:QBi@vl]|~g#Y{%WZx%7g{y%KɒΚ W>}} kDY-SfУd!Kawp+J%L͉8s|T(V#VͳoWC^9U Eu$OjTl:B3z( }.@.>9Ipt!?nGt11?1iq!}or9NY+o1*3raZFNa$%yql+oMՀZz'O _4a5~"iն nJqWGc.5*[7VKHNN7` :7'^d1by+_#e{_c|8}5؎/dĐ|4/ 4)k|=Qz^܉xm85eO|1ƟSw5,oU&w*/niV|Y=ɉMQ)Vr(?ʖHhh?A%)| vГ4F/d$`l'A(#cp;|[2lq{L~DC;?gܼ!7|H{qlt]rtO0 r<~%Iz.nà~ڍfx\?cfq T;.""""""O$Uo!=vˆz͛ kP4ּt1gC>w'w@F΅c HSsGb%q#}Vo ݦ,.\X5AG:/-gfds.龈>#h5SWqɐBej%F2 */T偅m6wH_f4o;1Ϡ}|( xݘ÷pu'yU9W$ъ,XO69U1sTKOۏmO_80pFwV|&A.w}q0f5LN@>o>f}?͚#RZ4aU,Uߚ7P0^ɛ+aغiS! CRߣkH(k|㚌1?,pĨiC4,N^-Yn`AR%w"""""""2g480f"Zj~~~\[>#m|0]ANzʵez+Z֞2`߯:\D8%~ꏔE!""""";o>Viٰ={x= {I2y$*EDDDDDDDDDٓI2$H!q<k}BDDDDDDDDDcT0'a""""""""""yYٔayi?ID'mѶCDDDDDDDDDD2N0GGG'e""""""""""qL7n `I<>E|ʢM̍Y0o~2y [6WO'8!sR0ǣE${ZL\pKMH[v}܎Z)}!zovSlqu%mk&"c;>ыYI喊/wO{^R=,Gtvdpw-mv㳅K̥j莰 IDAT{}3}u0k~GruY&YVLB ɶ9"""""""%oV&za~D?vKb$95#UZMbt;%sqz/[֜CrbG@a2p#;ֳRs&߳ 9w>Xc\-=rj7U ^mЕo `m$͛~ɲmiωw{&;(y(S|7|4u)D %D4´99:m4uӯ_ sa!]kw`uA6O-oPJ)s Ɛ/vx8Ɩ֤,/a@u_-F:Q;jncv6E2`@W31mʼnk~hhY㒭Y}cl.jqo1qtr7^/T20ߊ'3+McbdSt7N.&} ׿K19IR͌= iS| &LL6WQfC<ƥ +:?\n:fv6>i>9568W0#clL/̩]sj7`1ѡMBYMGciY~MS@68:2Y|>27c̛/`<ݜ{¦j_ڹz7{gw2܍g>Ro͎+1[|c g `<)PAdnrn3\s_Y6q9fy}mta1/937''Yм,65+cr}LQTh21)D݌u24y1/|9LumnF>6_Vt6.Ǚvc 7Ke6N>wNN&[e溱KUwڱc{mݙx?zG87}6\5~lPʸ;0caY+̑Q3wz3K<%CDDDDDIZl~sı-'6Ǐe-]HDE93ǘ/ Mn\j/-R3 e12ūX[5*(?F9:sRު:ޤ]k>W.f3“̓s Ω߱RxP|5{/et+KzdE}E9e5``9|K{Zk>pf{񉴩ՍzE?uQ~z6Xec[-+QGٲj;crO@s`~}x"}ߋyXs5;~Vۈcr5K' Ջ.R wY'FԲmoOC)ֹ3Wz@;'f`ig_odGVrKN>q& 2G: /ꔼst-E@tmhC+D;3ݝvd.Q{ؕk3 j#'ý^_5ؒ}(_La-ww,Ie; %tdZ]lI׈1;B eW`*;"""""""$фc9+>JL ^A@ecy0C;j2^bVŠfبu9Zc-D:)ϣ2% eFt;nОr2@*\)Czl7 N(Svb(ѢII\۱5]YI绯zJRɝe}l >dyK ԩ^gJH6g铅=GވIsUQ)v~X9M|,җj3 }:_ס>d/SUSU²&˾O)W,&"7m@ d_a|#*ʁ\RwhxWf te >:3M$?lwNqGlZQ9ZKmc٣-(+lm]Uq9Ȝ5SAȔ%#iڭOqr/_̖>|[&țD&¬,S4q]SLa~ڿ:/wk]_8IF`?1cbوɓ!o<;goC^U镪8c;]H5(p+mV췃~p<:Px^fHWv̽h'2&)5]r?vفg=$;g)|R*V߆3l6)OI>"it/Bf;<E*!m$D\quu՝Bo&IN^{E|m ܅'ptH7ٳ䏭0/֢F'39[.^7yN±pDb%+4Os@7a6 uI}}˽N3Z\Ul8e|y7ɒ:⛮@DDDDDDYCdX/?CB7h"pEn<(IoV,֭Gbb8VHp=?Ԏ4xWҷܵY;gU=Sv#v\Yp͔"-I1`WJӶ !IL8[~!8Ƶ)yGn,7X wޝ?jxNN8bю1Ŋ V+D=;vptJDͱzZ7 qw^8g~Wwˑ+ \8?sx~H̀eW6C&fsrΜǒ)+%5%ߚA GϝfkqX 3OF;} }65eH%wJRָBR$9lfCAooa͖< eLZ!z4ӛ9d)LB&7j*\w3!h6'L8Fsp{BwPX.NnXK=|ĿlBs+d8#>?,`cJ;qkQo巹V*uDj-Rr2=AJђarVo?[ H,Xⓓt ߦ{EXκs/UGd̒ sx"7| ^Ϧ*e'/Oi~%\ a孙(R$3s6ط3Ns4Ǵ4_:^%Ʊ0~鹰~5܌yV_³\|b_bҐ5O~)Չkws1;ڔO ٹz%pڟ+f(؟I PA} ޝ޺8ֳvoҘwѴ77$Kf&* L$af {T{CBb`%]_sƿÛSt.O~X6E߂|@r~̭f`M"Gx9{P[8{8ovMPҏ'JЯ{U̾y WLḡDVB؅2 uTۏ]G? KD;%sRT/'CL_ɻ_ڇ9oے 0ve{87NB+ cA86z "|,%h<| # DmaQ:_j¹"#Jo+.%3ov~Lλ?bӼ2#f`h5~3ݚ sx[R)5:QӆiY"[=g$t QI9_`3\1w˜LGژUVʕ+iڼ /1ϗJ  DgOl\C)=k||%"s>eQ,^3Un$`u&5!88Y,̟71H`Da&N;BBdIc n %\yMLNՒc$>"WΜkz2D$:H>84i8M4|02Fd$:l%e""""""""""ϔ/#NN>,y"=stttR!""""""""";f رgsLD'7n `I<>E|ʢM̍Y0o~2y [6W1kSRTڪtХU-QUQU#9G!!S$>9;u:[>>*]# 3ImNuY:)~859rX*0_8^ԩ'cRJU<9rX*0GEEɈP '**ϑ)߉q6Llw= 98% ̒!U Lz}9 fc=栗57R {2GپH>Rک~(uer09hwI7CsFPn6-<]>FN^1dɣץkĶj]RBw>?fd8`L5n0Z]&O*\Ya-U"G˾]*׾9'fȬ|O+M-2= 0iB#wiT.=-C1*Ruj>纪uxc5o[#f.F9%.g[b_Ho4+BTRgU3wQUj69 qBW@q9edk*GZS+% #`02dˢVָzZ-_S+k4q<~4^*n?ǧoS+lfw]ܥO5W/͇j H!':OԠb1 PjbR9o=GOVosu^m %;rThAZ~rߗXb/@?_ Vauޤ<'`a-2@1\HKҬz5٧捝6qtX脶.oХs<ޗZj<ܥS暙΃d{?J-ɯZxSV_Z6% ?+_~}>KQ]~_FS"_o5^qޙN6ɖ#rȖau9GOy=z |H{@{No/Ŕ㴼v>Ϝ+4@6sJCz,֪%Me7iyK M+m4QB#hu֌YTpUKkJzM]K%]ڻ U^[7$LUVVLkfNzjNwtأ9ԲvAGoDʳ/ۯ/U9T۱jV.o d*?NyT;o>dFi(0` s8:#&zk[ղoAs~qE9"ӝqayRk__3j>*d :]n-0_?|HG]V!l)fcvO4lVЙ5mzF_!uNaHML l֮돪Nm*^*Xn~vɻy *p}mC{c:+o̧>}nҊUL16e/RLx_ݽ/)9@zgo@ pҏ?Ȕ<5SL2u=H*Хzr':Y~ Z^ccmϧ_RP7IUU~a}`މSȉmԌ]zs~%U 5q{pT)%deWHWkWVsrIr(⣪_;ڪu>68RC-fe?~"Rvv, +zs ~[Vz0xEmX &,w\7.Z,[C=*qU,wPUo%CkaOwȩB*_6j\5KF6ٔzHpTԈZ9{eZG'bm*VԙX#'$qzylq!oϫW\FGWEoUSVI #EYgwj cs.b}o⊍BNSOiR@Mbz*6sA/]O*kp8r#璷j) vXY?ܭ.T(R\m[u֙G IDATh8f~  iOu5 z3sUZc^!lF2.Dz1˸̭V 6i(9L)u?onM7u6<14}o-W漪\32r)gOy7 ~^lL%^:+RTCƸdCځel*d7s˻rMSs/+gP}k126/OPk%M$S=+Kƕ8Ɵ$;%ɸM.9iredSy89 =KENI[dX>/ Coi0o$X]ǵO7]E/ToJIS?U|q6aR*[>D$f:BZU,M3EI?bW\/v s=|;}5-7$}CE]tIoƜۧ1yCQ`qH"e킶WW*'j!)O4J2260R}l^[Y62*Gd Rqſ>n]yB(^gZj|Nt)/(_ʖ5t,9Wٜ͗{u^ee$s^[A"]U&s%f[-+ -ЪoU,I NnNHҐoHǀpMlY^Y>WJ?VsujFwh.iJ_tX mjL7n=Y]c5*yU]+ɩqj_znZ~~ݳ]Bjd^z[2WQc2 RUF_5Gk@Xwʕ[2L?+צ.к郵 ꊌ !WdlE;J3wдTrSMX?Z=JݾmVO%xLvqꃘ*TcJ=_ׅs4l]r+wn-_OmF2YUiq^T$o/LS4WMR3XXAew1Ro9[V缢Gfn5Rߕs8͝_K\o!U0Q -{9Z3V㗌S[ŪRX2׻feЁhڍ.f.Hd|jV뽬+hԫ4DVѰ*^I%@z|k[zi٪?djԸ\.nצ>T˖-} n98Y6֯[Kj-9MZ--2h(0s ; `f2.%??! IMKf___ IDATxu|U׍%c6z"R*T@ŠQ@, DJtX166xݹ|=;~1q[aTSj)i#*,ʁS'~ב&۷ϷD#Ŝiఽ[=&& pճ2K62-NgAg+""""""""""l6bp:x1dϏ뷴 ^!B| 0 :OGa`w8X,`iҸ޼ŋU Ébb*Vpjıt8)^X}jrq,"""""""""q6cHZ-&\(鎫Mi8Z^V0p:5XDDDDCvbbm;-xyb)|DDDD`w8 B/_OOb9wpDR7@3[fE+s.!aξSݏoNIc^N"'S\=(Ўi'2<+1v^0DGk' ~ݰFopĄōbfl1 0[p/EIo)l6&jdz nLNv\\۸q cuJ^8"2Q8N}dHx < ]2k#zaGz"e^p%җv?Fs\ ;\~h@>xmIATx׬HR^x >MAaTmӆF! ֯QU/DDDD8*Ungqi*sV 'k֬p0e3>.iIo24`x$ߜKu'jt/%R ]b JQ^T_?n_"4ԃ棟^J2anƕI{lW(!Y-+D(]j#2AUh}g'Q'%M[DDDD$Wl6J*02 'J$`Ӌn~x ㈎#i{_xzEfSfB8\ lѰ RՈU<#VQTts,&<lp9Α"^"=0ODDDDn^f38uhUQ[?%夽 7GoysTKu7{ԳZDDDqZ =~, j&'6(! o_D,K,&4oF`0 &4oƪ?=كL&D8Z]նBDDDD wЭTkP^͢ux<>qLw<۵4-MWs {*.VΟHrep:3_K5qE<=ܯ{Ӊotc9)f8nZ_hGQ ȫm8yil&&H6k1EDDD(1ҟ^܅+WI%3өJCS{4V\^H"Sto-"""RXfL&8u *a{Y,W[P`47 ,x,ՅV8nIX=sOKDDDD <=9{,11Jj#X[M  X1Lԭ";po(ܹZ@NjX,|HOsO3^zU<"#͞*芈H.0xzzp%<0/;9lvcd2U)u;X ?G=Z Ab7gn]۫mF|aQv +>.X2^DDDDD$K'6É:Rlj1bbdZm=.(ZEDDD$&\]]pM[ cm+zWy,"""""""""")y""""""""""""""""""""JRpt.""""""""""RB/ӰcI\ HⱈⱈwcgfJ$tDDDDDDDDD$ XGsA\3w|ʲ>bS-qׄwCn,"""""""""Rxe}$-ZҺյV+'\iIwgRMoc肝\v7SB-iӾ3}bgkY5{g-IX<~ע|e>ıƔi*l4*vg!񛞤Fž,7b{ѸfujO.eD:M*`ODV_is(SM[DDDDDDDDD P6L8v?L55yx)⣯`#=lKX<eg&)OX~m&AɍsaĊeҭ?,o)^<6Pa#xfuAðPX1^R% b`0ArnX_.?JC b{ȯN|^V1׎SZ+zgRXDDDDDDDD fJ4[w)]#o1dZQ# GQ :ƳcͮCCj)˶EE VcT/y陇(ua-)2z6ぁ5h΋siV#j EEc0xLJp}Cyu5p{3\nCxw!~D$4NuXScׂGDDDDDDDDD D~N""""""""""VH*y,""""""""""Xϝ?_9H!cR I :)dXDDDDDDDDDDRVHJVCcVH**H*أx 牉Iz=e FN ;㲽JYQ+_ ׏QzBBB}>6lr"puuf͚kӚ=ӼY,0; >rK@zN۶ &)Ρ̜9zpp0? _~~͛fN˩S7m>KQ#Νge?!޾2l#"""""""8zZG'Ͼυtxx8~ofsW|U߬&"22qOxmWW\tg>}6mp:)9s,gΜe֟)_ݺtaЇiܨfs/Pr#߷#GӺT6v̘6%1>sZ@|||~cɧKys"""""""i[q}R c/h75fW|p|hkF}_|k9dz| 5?s 9NΜ9'K1|XV|ez}11߰H1?rŤϟ?M1}ذqqy/L*QezZ,)ڬpLoNKVWEvѩӧY>1acXj:@UءV-[Ctt4;~ɚoc9ߌ5x>9ra0 8豏s y\ |^fH(v>k6s'>>ݺ2p}*-H60 >h1:uŋ,_?m͕'O>[^oZL f_~]68l)^LߥN:zcyT@I)y-G>48QQܵSp80LmӚ/>O-R`]]}tڙ_w)sNϦ?;uvB/:[bbbys<ѿ߽¢(Ӑlٺ9po۶a$MK.,,~$8ޘ3-ѳG\F0h}1̺9$"""""""y!͞ڴ#wf9&k.l.~rv1 orzkw܆jBLpuumelkO!;!kݪ%ӧMZ868u4K/O*T;JfM;k\̹soO2SM+Q,G)U$&Pv汑c > JԺ2frj#GjdQyGւyiz,s=c *W ИqO9of8Of$쇜>L8ui?gr<ٳԬS4/_L&.=GBZ;Ο[J !..>[o&SBN:Ő=JRڥ3W~|N vFx,W \ n=|k(Q i.?0/>z F%KvkƦdɒ[v-َ\^\Wz,"""""""E9~ܼ}PӒ~n9N.&&՚1$eNGMsœ7gѫG6oW^z!p|Q}v{GR.R qn%(,>CfsRڷmC ;WF7kʄa#p8lyC.޴lތbŊŮ|y+ǎOtyV&0_?5ҥK}nk\ΝAf*lKNkۛ M)\Iŧʷ}qq,slx#?]GppBaAzI qJOaMDDDDDDD ӕ9"x*Jl/\VȖ?7gg8zے[2O||f0[7[ ;uz9yZ^pxd2ڎd2ѹcML:3={5q<3ҰA}}ƍJ"/?=W~߳.oqd2 4j GqWq3N<+J7gAqssu%;.n^tܑN;TJḠ[ s;qua8{zzҠ~=,K8amL:-nԨ!cG=θ1hԨacIj_HtZwqq߅ ^h ظ8͘ɴ3y}9w]*HIxܶM4_׸{йSB(I,hV6[1t"ƍEjE&M91>Y!o}ݻa6>mN'of3w՗qcG稀>o+KN 64ܦu+ޘ5Ow㏘=s:[d2ء}ΒO&?/_|Ͽ8a >eb-Ҝ^%y!3F9$8~s3_@:r)O_x/_~Ll&ҹ,ŋr7[6tkLzu7z}胫k`R¯\Ϫ˹36)7>Y9Wf|}iӺU'I:nWپWCB޵K|`̙7͖g.Ua:l|rtܙGyF f_$ʹIz~-X\o% >Yscs){J )Z\\ Hⱈⱈ""""""""""ם :)dLQWB5XDDDDDDDDDDRt""""""""""Ry""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""؉DDD|9SSܛիt*"""""""""9b t($`;qB :"ᷠ`8 PYDDDDDDDDnbKSEdddA# b^\|.zt:EOU>I<+VgKV |[DDDDDDnfS`ۨ &SA$0ZUhy L }As𑣸h[Rsm9}4HY8Glƿr8QЩIsOz@ \}}_~NܖǏt*""""""9bĄ4>ӉlFII*$qqqLV -7k DfJ}{Ӿ>/Ei?mtRHhై$\걈\c.qMoEt.Cќܵ{scYgnEvUGc :{g򉈈H3?~-ǮxZEl6jNQʕF$~C6bbz|ݎCq,x}zt2F}ET`L9 ?a}1GٴjԕF5chZ/2.pfOl:靖dxlDq7ӯ+jUb@jO-dB^_ۮժTRc37_Tqqk2en(MՀ>?ڏ̦z\~+՛g>)3,Úx7F~#bX?իT\JTR-{yp":;O e@VԪ^Ѥ[F\ܪ(~BZթX6;#H|A'ws~V)[!O/V?b⃽hRɖmFi~#ǵd%jԨNbɇN?Lܗ8]PRvSy>c{${=0MȐwb3)ctN{a{Cy`i ){a)'Ceoa׷A\zgƗ1꿴_lW;x8Y̸0,'!.iv&OKDzfɉc6k>KHGz:yu91/;u -gjpf·= %Ҧ̝Cz9ر$"2ԾuD|r?}G3r6p0SӠy퉪pAvDZx5#$Uu?c4 KA'xs~dז?ĵx҃:ƕ 0IȗQY Q7% ye»3|޾ȁLޗ\OB'bY7 ADDDDD0En G79mCtnjÈ;IFu.SJU7}/đNήu~̈́рM'gz.QrU=pL&7 FC#ݣ =n'3[l1G!iQ7*qe b\FUj˻O5V `ב\ӼAm*UJ:mEw{g{jW:4l;)NB5R:mȬk+g,۟O6X:9/axρs /, Rq5vƀ6RC~9J _zgR, 5-}{ٷo7K3~M0aߺ/'u$dHy/EQt}i1kV/cޓiUɒP6q;A۷}{V/!oUl۾gᖚ28wU6oF6}$# )ݩV$ki6Zo? ?,em}@>9&y@ƶөժ_ ܗ⋉бQ Ѿ ,qu >JՆ*ipc nR l89jhKݚ #봻 r6ic,G|{7Jj4_3gS{ ={aЈ?Aj%lK5p -NMQ5avz`*?J.~`zoF̀TR]0釓׶3aB7^/YaL-i2epV=1'hbGJy>#fhqfotNq繧G; ruj4ͣs .ƕ|`4Cƴ*OSpvv{ssyzړ;|iFĿF}8-5m!VyGNy-U6+^@Ԯw"l=1f5n> X +'2 =m1s#8<~8;fCkNqpdt> PyЕNijVeE&ZtՅVSM.ԘIBp+]z&;a <_ݫOxxn"EL/dm>څ8.UPkR_ ֣ zvy934n$}Jm31xə@>Wk w(~VlZ!a̙ܕ҄WG1]: cǢy1]Ѐ trv^#3;W0g];bxf%.Lgp 37皫3>ag;]fʴ|wsfNKvs̛()7>zu*/KR( xkj/י5z,~bD2f|MMy .u"lY4)r{o ՋnԮ xp ;ZXpŬډ&'ng:0 =b9~~bq2x &_ɄiVxяRi4:]p痥b"Jj՟ůkoZxVrNY̘߹ ϗdƩyޝJR?EUĊj,bGpÂgx_3ڽADDDDD$cR)nT0.͂A $]-yꛟwX(׸+]{hvMƣja]N8y5;ѷMycI >7WZ?6/I/ wc-{},{k<Q/_tռ}*բvk[HgㄍnNlvmg w^[W[/fbqI_D?-;q3otíUit2i,v^˵Ng:V޿E,Yً9Aɵ30s>K)fhC$bn4gbLr-|<£MZG6t0kxHj-X;l&#{&|X=td|~'63gh(FQ^6QiC|2nrKVIos=˄1Z!,xNwM5h׵cgCyC X9C.rMhi9x=ݘlJЖ?XHʊDzޖ8o#*^oA@]VnNmq5cwY% (wk 7 Z*E|HÞcϯ@0.[K86(5,@v:Lϗ:¬&>i@ް~{1 vgH]+МŏS5|< (\aeӽ{t>^J%|7={k>|)׏ku܍~/åKW0kLv ^}`շ jJ(K1v6S[WhWҍ7Goz ̦T ;/|bwQ`?b8TNթT)oq=SϭjL!\hO;g6e]]i֠. /gbɻ1̾ml7 m}87C#Yj#[~fIb8u˚ f gsdhԈ>>#V~HSWM11*c>\ G3Q?-ػ_f|Ka$gSyOӬ|r&~N.isc:.~^9yp?8eYֹӁKAf]},>y)]T?ٵ*&?z5$v3x X9LըVB.,_S:e;^gs v0{Sm{& O5Sm{gZu, -2N's{׼zNpvs Jޯ&hL _Tvm`{/Kв}TL0 o/KħX: ;p۸DR͒=j8tz%zHD (i, HQiRCw$~Hzذ }ȖyR IDATIr5~;"x,nF'hi!]^1uֱ~ѣ +vltl?ÁB}F6$KӴG,^v0dFEꖳ=_[dӏSϸ>ǟL; ('ZdM,Ң|E}}xyOɨҴ:cC| G}Gm1LͲ%(cNw v#:Sx: b+㴇=_v[sD0Mp+FEkְqJ>,m(P-X \].;i7q||2)-3i:NJe3 .?F j]ªCwzJ>g="""""".c#)ꇒń32`N 7N=[)iAKRp.r.Dn֚(7cOGy3OV8~;{t$>FprRӲf!(ΟQOݙl*]cy._a|0 {U|aL_Ugѳ߲whtf抍66->P9xcsLIwH/\_cAyia/Xc{ogVܙ!יա=.ǍY݆͗%u~ `f#ɲ_oqvLZm?a5l=~O0!(wnrs_ə;-~ԓ1Teys1!xA\]=OčB|HgK+wCF+x~|DΩv򧑃SSX'uL?fE`m v-SlwKn |i,ΕVҔ)CKǎ97|.=F>曛Fi4ԯv^pҕ}OsnfO|i{~FpP""""""/xLh[/ U)@$p=6!)% u;6bi&%}Gŷz3ޗ$<9k֜d7V1h?ϛy6VbINgrS+g# ڏ I);#^Q;OOYG~ٝ!ڛu(ڍB4.KMw9⛤Ut9^jCOhHՙe4mX]?'noA[3(%9ˇHqE5Ϳ'(|%}[n$KB_uÿSV~w3٧wKK}Y6mJ<銒Pc$.H80s?tџ5S@ѧП0lTVw巷&ӡjd=#3ɫ>ҞNF&O-Ks|oCxgT YRRvD 5k Z87{i]iMHvGbt/Қne޻H ʦȶ#unը7/ ]-?_/TIPP,'V6b IBLcֆdϝs vD~yk`CkMOlgO iع[ə׳Ƴȿٓ[xx\롞j$JW;Xb'nD/&{Iqd[ޖd-oWӾ'4ʅ句|4r;͠QF d^Xd1æ0%4].j+E`ۨsrhH(Uӆ=9WO4 h1bsn⫏25#[)tc17<=7`xCoƌ\cLhum>yZcuq0mrچTt_8~Oma!]]RipG %gZ8q_w9Ne[ MKܴ"MFU)KGL:y.CA>M<r:T`ưbz?#EB:g¦;AD}kڀ3Y1m㆑!t>58yF 4_M` Y6ˑn$$HcjE-rO2?/ò'8zӻuPƬvG'sM"gv8O{fj~̟fӘAm)vH'm7l5#G2-_G 4!?_F,a_ruLdLשy+$/hzbnb(oL IS sbE}h(CFbGXI8c × )(\k(; wGL҂0x} Ւ:{?~K """""X;`lkL_4)ͰS7.?VuҠa'Vah7TPQQV+(?W=ʞ=ԶjejR]q] ȳuw'G4$^ک۞jӋ?èSl,""""""""")9=hRzN~wm/z""""""ۢllQήE~u(WYWԶjejg}O@txl"*%McG~jU(¹ea .$ gn^:PHX9WM kܬd.Sm08{1/;ə) I&m*'.G2VUJSP3mPk*JJ_2nN޳뇿ȕ1 RbwvIW*dACz϶j-"RSm?1!b+_ PPf|S?%Z`ƧѸl>ҦJ_`y3mǘVrOM2 7IJ7rc7ԤX7S1zp~f(S~J(Npj4 dϷ÷&wtQҿg\6O^˄5{>/ 9vLFYM,%waÀ "Mj?#s[z[NDDDDDDD^c*J80EUW`d@0~C~r;[Pg8yԏBHuj|0 n&4kC&18ξ6$ݲ[1[3Ib$/?QgF-oa*˜T cj' ~_2^#em(3ah™mSxwpolŀAҋl èt0dWw>/V3^4姉I$#fd*c.ljo{8W#GҢU4Ix|Kcm9deqOvKi{N"ae3pj@f]> #pl[׌COSQ|],1 v9/bo[=[L($Q`{TVv ?"""""""]۶mأjo7.aG\lvie,YR-;g`ZT+۷ZŽY#s B˗([4)/֘/Q7y"V1nA a3` Ȯ-Y:tφ3cl6l=OvUM~Y=Jn30\1ggflf!n%%9KdےLUj(d?TƽS¡/ƳҜv7@AaT%{=Hc1h;ޓvsc fEӼ[Obcb?_yBJ&k1^qo捿qtlvrҸכ4 qeØ7Mqq ŪQT;[vK l6":;.o@nEwF~ymVPsjDl?vm c&˘ޣ^!ouPs| (Frx"ؖ?Nq /_g%Wݺ;eQ%ԍ;,܍k#5BaWLrǁ79ܯiYeg`N* ML>|"›CX;Fig9~:_mNqNM ҧ1v,`JM*.rӼŁ}uW^%$Vz f1fC:tCҷ_cab@Tdǃ`E06pwn{H?vib``m*-W>'Zm _Kl۴D6o܋=kMf،@;k_ǮOuj=@ZSk 龋 ;Q3%z;b^wyTϸLpVo)CD<&fP:I o«m4uLjdulFn~ P[B\l)(Fi:̿e_fwYFgЫ>+_}]QߝBf4-}}Gi#bĠQM|6] 2hH~lbip"yX3j5cs߰\=i>DT-)&Pk%tCjU zegZ&KT L}[CKaW<ؖ"]:tH֏׋&䚉 \njXcI]sq5)|SR:lbb E,"<<٥?gU$ 3)K6y;=ٸuVÔ?.᙮5cp,XM/5 aؗh?"6TmKޖpꖃ7FvgiQh>=ڇaޠ֙^IEĸG'fIۈZ9x"Z̩MkҤ/ =0HQՖ5X~2a贞45/Bn} Yſ bàYp9 w,>ZSȃ'nCHxvb5C?\NO22價 i~GAڊE zoч{Ã]gsɈ$R,F}rK5H vl&X0oYAFf(e%+ޢۑH1h?Τ!%8Z,+"{.EPk2LKy"~=kqtXloq}{8<,]R=9g.h{E!"""""""^9ֱ4}[$N֓>!:kQZ["YG ~|Hnt[;n}`ډzep-+wxɑuIg ?\õVDDDDDDDkЀo-Q掬G)\eH kQm7/yB]SX⽤2DDDDDDDDDDĕ?<Vc4Y<?8e=xVfT,U% rV8[Ֆ[R|?'֥~t0aHgڈIG4(Yk7M"&W7R$&wChx[vLBNꅻG"R(k#WC8|w <;5^z4{/<̋|׽Yo`ZME#_aZϰgwFgV0g~3O.{p#?E{9y4 F!RC1rJwr@M֐<%Ҹm3#=ηߢE:+#ne{?-n[x'a`xa(_~q6u ׆0\DDDDDDDEؑǷ0_އ~̋aRek۩ 5DQ~Lcxr>9c0ݷ={+f):oy@k,7#!ok$!W^M#=3xDF]1ߛ5qm|ߘP>=;<($ɣ/_5Ga $}ǰ<}>Y Ϋ­|A|j{=^I)N_%3qq¼b/g/i!#y=&>[:1y N[*Q2iҵ/#FZi7o5FT&_^eo;fVݤp,u~ojTֈ~)Yibp'֌}4grZcv ɼ LW >K޺#a`3Q*`Ӽӑ;Yzf߸eol(W?nt:H8Za7lbWii#"""""""(N8̇[qmjUdTn׍Y:3^ӈS/9 vpo"mk=o17w*'DcǙ٢&Ta0Bc{]h\&?M9V|؛y~dbiIh;ϋN׬N١aq-¨Yw9+̀} \.wfӋgUЌ^Nr[Ֆ[RqF>G츫/l嗣gnȻԿ֫߯$"KW9bm$uHу.i"G@X?:vBQ+I.ܰ(Q MWIWض/쥳>q]pkd|!=Ϋ[Jɒ/ԑ#9}(0y}:6m3'̳9|BbIUmC^ is%oa 5y$"""""""HEi&o-3eꉇ]vqZrF&.Mh)ox_͍lU#r;Gŭ{tяk?CvT.HZfy;7"\{^,۱;cn[<.qUD}( wdk/JiYM~$vZ/M.""""""""?h _6\8u<?61X-Dt |*~~>Lݞ:n Eq,\u Q13HQ"`xi`^Ƶ{ڕkC[8_,;rǏpYN[VJHsggq` MBK/qZ#29xfrf]{Guu?,(/Ʈ."""""""PD9ۼ̎d82m3ΖgX,ٿ3 $w$ȡCyRto%9ܹ 1.E'{)z8Sܰ$"e,]|Yi7o5־v]F^!DƮI1KZ1 b1}|eӛiPb!ixyrn(;$7I{RvM`YL'/G雑\Ū閍&3돮 є"06/3% :s^*\}eX6ӂȽд'>UY-EP˜}>nx:(S>öը3$ST|s.;!g۟޶#QXYH{V]ͱ$%' cܜtw~гAN7-"""""""4x+GY%Kzj5iG;̒Fs捔 .2$õ?\CDDDDDD^$7tHYL45ML3z*[3ݴc& <k䱈pd""""""""""J4XDDDDDDDDDDoeٹq7fH&"""""""""y ?2w}߼++-gs sjP@2OwgwWJTxx(Mrv""""""""Gqc:P`>)%Z XQ[5.M$x{%ly<ܩ>h}k]Hc8Ξp1 Xs$fvR39}  q&r)pnEլ4^Q}p(A1y޾"WJtt4ѻwR){3wE1ߋFA3SZer<42TyZ2aWg5O sKLLZL֦ۤq\^g?LK=(÷n;|o?V flE õ"U3!@?ٕ4߅W=@,­xypOd/V-l}߿y4azhb1bs8L*ɖ[EC8mm7Ʌ+|3Ě]|ncDl Sbw˾D܊]Yȉ]qe^R:ppK\wÿ""""""""JHı&!/p-b"?<+mMWy:v+Y>v+788鱕ӛ#9j*MRwO0S0=i|L1?N=yoF2\dXzNtd\&*l4/]h'nǻ鲆N׈ms.ʔɍ5z 8*z{.r"GfvY]ݞ{4xcI/DŽW k^/ëuqNEDDDDDDDғ3S\0̘58ԧmjaϩXdއЧg;@`l>O/V'_ݤM gX+JdptEh-'2)`>qד7/7԰ߙg7ҷSCs!q~iT}{3[3?.890{xi]9hڻ6>n^x9'y7lىyߓg\>ʖ7NewTz{1ɢc^ZSIZȧR8w's~U={nLB~@@] L[&2{^8"vՌ{w#;#?م%⥙*1bSF7}hPlY8NReޅ4]0Ո:1揤ͮx+L )?7uBbbO`&8o3id,'\5ɑ0*'#N[1nd%Yޖ<)ޑ2}Mfqj"(U!nYC/#ǵqGH ǼC"%2pL+A,+F ɖ3'gRfWr+F!ԗsߵEV i̊96ӓ?5#Z>΍а;~DX!+Y eB \=S ɯxc[LvPɇ~_ _3b*M cʜpzL9K|/|+֧ .zEDDDDDDʴ`UpcvZYf mڽ˳E"ۇQki0{èxƵ5j#ӻ M#cXt=DDDDDDYq5jjX00 #i\a`Ilۏ00LDFF?>ŋb,FDDDDDDDDDD21:EDDDDDDDDDbyiYEJ!ӻgW*:Ӱ PldZZ0ODDDDDDDDDDasxlIe""""""""""'&&e""""""""""ۺaTT$[6Y1zGƢ!"""""""lYhӲIQu=2]E#cQ/""""""<5 XDDDDDDDDDD2-c0Ҳ@y,""""""""""HExc*c+Ҳ Hy,""""""""""uCu[=7KsO\̔>SBkgIDDDDDDDDD$ٷ?/_sOd9s#GJ.m6<7똻,&'=6n`ռ7oa"F]N>d;t=|ԎCsyl9vG׬aPT|Ho~U=ήؿe<~ՓzBy<\Ae<#+wbhϝ,NdnDxV,CV;f3YH%vz/!1+wc^zMd28d}iGSuLekoTl⏵ѥ^Ir9ÇʭGd=dΨμPɗ\9J1(*Z4sH^8:ç GDp"iL$'q<\qvOΌ]zd/3.-a@XO_WsŲ }zď߆\LDZ;Ьi &(J[1qt7bbq4z'ʩEݨk.iGQ}Ll]wkv]\=0N_ݧ _X5™Nxcڑ+>'b{y 1۸M&sc+z}:8'དa5)d0}2ڌ篪e -@RI&G7}h7k2stBDžZ}] }]NxƌE607NuGLh]]w$@ryMP08x9g>c"nkBFlEwøȺ}s΃JC"4 vף]sp8..1apiV t૘wpx| Mc3`>tNq)ގ*Ѹ6;wlO_s4"㝟΋.>_W,yWL]Ն2j@M ܩ!#`LGUˇ >+>kb8.{;[Iel*=o}ƥ6w!ߋs-էE;7A!q흋p>ǔI<˪2c!)tn=5Fm/70ߟW^DЈzwF{x/#.2=j2:na<γ0{v&40xx{7|^ܨb*~k3_|u!9_᳈Fz/CeA"-`i븂 ~?Oh}k5s1 ϙz+~}FR&]Mm&wp 3W0.F!;+ΝqS { ?!cW q+7A-+eӭ(ufC+S6%7f;Wd_&ztN@6ebh9)"""""""0ߐ!C2e o5{Kuiyl93%U&CmNşE`%!|u:,\@X0~ͼĔm̄)aG^MPlkm*U yfn$ԡ߃;|j "m֭`Q0\+R=O {m8wf풽/e.^2Ȗ7XLLuC_C6åXm&Jϥ}h Jf9=yN}t Cru* kOK#Mli 1X!=J#F064t8Oxcۤ WQ=\W8{"7?0ۙ1@Bk21ao1_b}~ݍC~&G),\é /O_%V$ޏw7Meud.@:tϚ|]y+ 9+ٲ•Wu[ɔ91+:Ե^XsW.qss\H;*Fy@y~h+>73yDN+wIyt=8hD >ÿ"""""""q=,5j4&L#F$|ܸȱ&!/p-b"?<+mMRحg ؁sܸ[rVNo䨩4J=Lrǧa38#;[EقPry|\*L59ָLTh_(@ x^Wפ1O|=uEE䊛 ^-ɌvK.k(*T6LXqO@"͔oI y#'!W;qL|k^/ëuqNEDDDDDD$s{PΘ1}5cÞHxl7^ӰM _<9yC",04lGq}ӇԡD_q~ kElẃR&]Uǖ0nzCB]z;SFvjH|.$^<ίg"ʖyof}k%9f/gM{ /6/m5;Q5V{G~‰SZJo/&<0YtlXkJv[=)]6]Ⴋܮ(} o$qs8{8۰[fkѓ9\?*=`&{!? ew kw.{Vm?-y{=/|B;qSB9g1o'{h='͑*1bSF7}hPlY8NReޅHf#g?vvubj}9t<W#np>?b7♯057./].-&o.T fYvz1N2}B}UÙy _yufvŽq#//}I黌m%> l70SWq(@* uBNo}9=ϟW^?tDR=  -ِcZwF by\鰠l9s|!g`vp%GbTŒM}y?o"+4fEW?A0&uoh\rٰM̶b^>΍а;~DX!+Y eB \=S ɯ$7c w!`SVFAiaLN)go%q{nDDDDDDDDlt?*ӂysVL]֬YCv/=la~ZL0*y^fyhAEDDDDDDDDDR̹{}c8**Qm>=]E#cՊdj>cfV+6mosx̒Em.$mڨ둁zd,|*UVx1O( Ǟݻp9')c""""""""""beһ2ۺaj-Hfsx,""""""""""W*cudV[eHFc:H. OLy-aҷLs."""""""""iFi:._77jr~=oMžX~W2o1s.dY9n,m*yE:ǡ9}_"'.ˤ.VDDDDDDD$R?@qVO }pq #qD\5mkPގHZ907"v'5"t; Ev+?fSL,&[$Y9u;|25/;}p D\=0\,:6h[] TiLk6eO4׺-.;723{=o'icFiLY'غ#&JDrV n j9݃\+s7ϱߍ S4ƝǷ5 #6_X9u ZUH Y7 5K'ny>f;q=o5/+N(ՠ7r{HYqrnͼ[-_ cs_~_res"+9k>ƹ5N#2j6 !y!C:o/ẙ~U(C'< m\~w܋PFu4$PZTF[_15͙0i{o|l8fq-g!ʬ# JT:wαq3S.Nn+ׂƝY1OW8f%of Y;&a3.Fdqt@Y>p~eӔx]jQ*֣{omӻlQ 0p/,&/jA}p %: lƭ .ndX)q#ٴt6a2zrhg@*K!5/܇ca MeޜYZ~Z cg=Η! bu\AL?o%f:"ϕZ9!l݌kS{Z}LWJ8gj9dLkmfK=n6O\oY}v+>ZOKuyee +U]gRro=%#G,^1w0c=^}8#]rjV/>E0L?|{]!Md?igtϽQS;U6 IDATiˮE D\޶ve܊Yqi62ePc뉻}Ehw}MGdS f&::c)' Ms19tLj&?O8~&aWg5O sKLLZL֦ۤq\^g?LK=(÷n;|o?V flEduv1q(à楰7N݄/i9[ƴT-Ȃt _#/~m<1x|yo6oW ?Y(E;T} }kФA]R+~:C1Q#8N쾗P"->3e>t`:-RF4ήP ,Os1fJVH>*R:ikL.-^\ˁO?R؏3D˽a``2ݻ۸](T%+gv]$tA`ug ۳hO; IE߿Sz)]iPޗ‹Vn'W&>v&d=DeȢe]+:ˋ_!NNN897H<'gV jst[Jj֯#`M<XϬSb 1gcƕ]|FSˏgv }1KP &u1'?5w3trĸ{v`_ ~sd(f,GN6n}V|KD;Oٸn"(-'ٵ<9'm*Qr1'cs3*˩Dwh o N'~?ɣN4Kr}8&lz0_.LWN%'Ǧ`.<3!1 kpO>SDZ<$RCOv78}^NxI#hK>a(;\ʤ" _OVsh_޼\ SÖRgHN )υċ5 LQR8~= o$T?ᅧu liy߰f'J|O}n|2e U:ep O08ڄvKWPWqۓ^f3[{GDϋjt4tc0zʉ ]dG{R`w+@\a:oVגvsG~|^L&Nݩ3CK2Ww>mT 0?_Cۀ¸g™Xto-cd-.TDDDDDDD$]c1ucz#Zt`Dj4oHY|f_[Ç-Yп% Z뫁Lq J^.[c&-LKm ^h׫@dkv݂2r-n/}Oے';R2K-ЁM ,! (qyJe{N~-芉{+> ?ҐcZF"LX8z Mxak_b#̐~;*2lBU97}[آ/_Ld.;ȁrC2,ӟY6 Y@/⛎FIpO^ Z=7J9CF.ϛw2M4oxaJH%m3bڊpzؒ6ư\yuaOɴ`UpcvZYf mڽ˳E"ۇQki0{èlsogkFj?cЗ\=!둱zȳ$jjԬհa`FҸZ0ƪ[ +a`6$000|&-J؊,F26_<%)˕S"4+XDDDDDDDD$3/YLҀ {#>_~xsBa,7ʎEDDDDDDDD2LylOCćwHJ X]<5/qcI%c""""""""""ᱢcؒhI:DDDDDDDDDD$9WG.-Of1m Ίc'{QH"N<umci|VB&c\:lTh6ˎJ⏱zROWg إGM2_(z5W,бwMm8툈~s+bw2Y#BӠLQlbn6hOSQ\,ӎ^Rۙغ һ>'妼u!OfCf7iD\һ oژhcF . գknĽZ>oi; o`c#B=t}7""""""""i*;okBFlE%r@:nI.1L !0.nljOvgP}vz4k_W\sQAo>yKܚyZ8<N_g-H:++Y\Y6_ßKr|Ku5wz *Qf?G}kcIc7`_X2M^nՂV2~V -Jtxˍ[[\ɄɱS%rF˳ild2a+L."""""""@*K!5/܇ca MeޜYZ~Z cg=Η! bu\AL?3D]Gr5;U+11ē-m aS ?#RqJ ֔R-1ulڸi,c<)q~=5x{r|_Fи(~*Ac/a 9_]|IĆH~veN׿dz2Wpc)v{a'dVjA\ eh?L٨>8 ߘzuUMԁl]DGG7r <""""""""`N# Ι)Q29:„SiX$lC,tXIaNy)ۘ S6\ɽu4=T=ѫ@IpC%>vmG*D~ی[Z#וqX8Es/7-|T2K3}֤~b 9Li>sb K̯]ȧqb_&5rgOV''p1.`pM-*19cddKʭ"UJC6B~b]|~DDDDDDDDQ6w'Zm-5 -B-+Y9s''X'Y[ ;s~NNܚiOF )ww0X4iogh*9Q;|7 q?'rkDT?*9FǺK&rVvIfa{~pEn<(~hclgD 9Vd6c6)bF383-LSX> SYNFJydɋ &柟%lVlu}?+%Ӷ=S1U'1Ĝ8ѓ1\PZOɫ%rZ9sORݮaA %JWGDDDDDDD6qIȋ>\w/%OŊx[vnʺqx8ʍ(>NzlHJSѦA崏Ogq"Gv!ȋ ~5ʕl&]M&Ŝ֌:׉*u|g)'U'6?e+h"k4ԯ_T+Z)3#9 SaaqVASKr}8&lz=z_ ctx.THz9Jq<\xfkC0cP>዇}<"cyHz~C?n/q0>X~uqY/o?|D6 G_rr)‹ЮcK7|=Z͡}{rs.L [J~v#};5l>/ȃ3FeK735xSq5,k慗sö~+=y#D\g& Vkha{@ |w*3<0lGBt!'oS%j"s/Y|/R*۳94q~,W~ \c?m+jWlunϗ_ĤG~ƈMM؟ïm@a\ceLf,:I鷖1w%""""""" yɫS7FQub8I 1ě]WR;tзؿ%㯻S-?gۋek̤ xvsp&G׫@dɑ k'-7oKHV&Ì8u5GҐ^,!֗8LxAAWL$c^mВ 8{ a wOPth8#3v귁#xF zmgr>`ATO)/s.ϠYP oŽu9w2Nobq5G.VjD.3`M,67&4- 2Z5(X*zeF̚^w3b*M cʜpzL9K|/|+֧ .[z)"""""""NL 1Z?Qk׮ߟ5kЦKO<[$}7i[6P;ːt=2]ECDDDDDD%Q7Pfm 04ָjX1 Ddd$]3XhQ*KbDDDDDDDDDD$C}Tg eiʞ*ᇈO:DDDDDDDDDD]/\19brq,AYqDvo?vIĉ,ʒ?3f3v.dWj-23ir)sD[~{{⏵ѥ^Ir9ÇʭGA%f'sFuJQAQ;-Q_?lNN&q8> ?#:ՓzҸ.8l`g.=Bl0 d,BH/׫b?ݾh=?oo .@""""""""D*̻OwhN&4kD6wt}#V ͦXM)oZԍbv՗ĝ>ugtd‰q޷7 ٻ?{ؔƊ+vEP;B)ZQFZ-*1CvPFQE*fWjDlHn 6MG{9stb4=[ӝ2bEi p3<=b#=~ݙ ?1~h0wEԺn2X3-GO)GGմM.]ʅs_r.ƮaeD.'Ld0Vs$Qqѻ}4D,1-*| L{7~ ?zF/߼-D/>? =t3=&!reVMKh&C?g65 lْ%,2b'&r-Y {ۍi.ɰpqFZvkJ`GW>{lH0st{)INg{llUqIn^@.3X{[gI64 ML DF\bex;akcG5:|+ lTjMя)TvۛNmxtWO5>Y3''UZuC3y9Sfx{I:IG R eKfYWid>5 k'M/#=)-ЯM(öshzi@ϒ|uK`[f@{6lFЧ_oAKxK \sPtE&Sΐ}W`yySEY=Q!A|-Kub"e``W GMa4f Vo*!OMDDDDDDD$mLnv cC+%"Z}>D\./M.KQ }͟DݾEg֪.ç1Lg\I$Ԣi,E(jf Y)ެ'Cmq9ѯLb,#tOƺLmRBuaNkȭ'X2;N R///,t*kJ %m82kpZz~s_M8{2F)d`q>mK, .Eug `M99^)|뾶*9-}ј֙=86j.w~^0_b|Z!V>;gy[q0d `ʆ԰uv """"""""ϕt-i9ȡh#E+W #;z5&nA-ZHp0N<Ĕj c~Ek[Hζ ;H(И7wZ\9޽hnζ|`-IOܐI]1T3Xbs8H>8CVp `p$7޷[3WS7H""""""""#)E,   6,B->g̮]TTſvxHzx%F3ɝ~x0i>GN-Il{vd3q7++ fH"˙6Ãp\EzbM̭1H ؕ kgIjhuGx@Ց琉˺SJ}ɍs${s:u֢B:M$uV~/%5׎ \:ŃJS+[at/Bf_lb?21`2b 'L! F#F>eOG:gwh:9Eq=)EKz] 5y421c!3IIBa~X;9Sł`|Af8~ou.jư-`|ۯ)7,Ѭ[YprUr~AͿ^9}_䷃I-JQIWUk|,;ݕ>>O [~z"p+SV3.IG8pIc3sP2a䥀שaop?vErk)U˔TtJ 9ys#4:s 3?iBE@8<@5lvlyA~A(jfvƜ, svn=> Il)h;Ù+'=ǐd[,ƵG1fە*;ު{\DDDDDDD +O@FԫʡiYWxN#M E.7.y P^gh8w|գ5sG_62ƅu.eԇqn>=ˍ rv3=:ԣT^GL{AvKRűOFp4Tk`tuͼsШ[M fq!mKfkT=˥?uk8Iq\>';Oe&{jLhIQ+0Vn< 9u/oͨ?uӟPTolv4KeŦ@,JׇVǮ }~v$p`r &j¶:~}3deȺ(?) sBxwYmC X *°9恁Oreݺulv/-LMe ‹P1El݌o8Qo-6G13?y<E!""""""ϓ׬ó0WҜb?=K/~ҳ3HKZ(8yQoܟ`@2)|Y"""""""""5i_0/={r ޏ/gt/DDDDDDDDD{ T+=yai<G$SRzCDDDDDDDDDD24&)=!""""""""""uZwܺ5ӭ34#sx<4,]j5ӳ?"nxd"E㑹(\9!""""""""""Icya9YGq6,Rf }GdQ.¹/xlZ9cװXc"jl nƤ|S:]S{ӫY _R$Qqѻ}4D,1-*| L{7~ ?zF/Pd3h3lDDDDDDDDU:'I Đm9b3뺓:+y l9wV-C*bu- `t/y wT0A]0`⌦b1x ãRfRyVijwVd+TAC[9cKzuړ_ҕv7 M]bet}.'%y ebJd˔8odst6Z|=L 1)F)X|r5`L+>&оw}"Ɯ{w4!9dXڤ<xzL gOb7 ف ~6vv,]MX?bkr_-͕-m1NŬq}'Bߪ=Pn$WgհWO:3ΐ}nbTu[Wv `!A|R?]'6!^N a4f0ud4{DDDDDDDD3O`}f'lcwk0aww|ȟ7G{-_KfxVc|צA M4>J dǭ!j&f£^6nltO͜Z41cZo2xf1;靷hZ=e~NW:id;niֺQ*™K2|E8ۿFt<瓙o#zP:6 @c߅jreTz a;\f'=rC&  5|~D뢎ĝ:‰,x,ou[,19cXE/Ň%!NH/o=n~ `%lcyy)E,   6,B->g̮]TTſvxWHzx%F3ɝ~xf>GN-Il{vd3q72LZ N qOLӳ\:Dn lEʷ ~6eO{c?nL\֝mWRLn#3K4%(i"!܅M}ZbQ~3h_033g:̎=ي5 t ۜq ynt.[at/Bf_lb?21`2b 'hhx(~P,MB7'hV6.?$M>1{~ q1ʺ0V]L@,)m!)vvlNֹ8X̗/r|/ќ%}0=+^ΠJw|a c6٣IGShC٪py`p/JfL=|kv0 E<"""""""")}#y W%t񣻒LJIa˕/_`CDneQ r%[^Qbۘ%saέrC")uM1mg83x"<ȗȚF1KS}/^(xb\qc]/c|0K.y8dPEDDDDDDD2Rkw<y)tȈzU9Խ;-zjϩc$="ҝޥ5F?6O7ҫv MzuN fM:q!i] ƣKFu{3c &/ïwGL(Ӆc~RTqSѣz>M8w]q3cŜ-4VYqwHdےYZ%ީ_?'˅_{꣛_ӳaPEpXDFa$[!o eZΔ)T!K?_ яּU [0jOn7Ŋn G{l= >u.;OcȖ oӪN׏}X|-"""""""M`>7o\0cb7:׃MQ!ѓNac{7c5G V$thHYι3+ׯS5q~ >JlVO5!}QǏZsCgEbε+֣kB:Lez0tr6 y10d 3X=h*A9u\q]׃X(TƏ G0/+5vNbV>h]׈= |>I=fG]Vl dɢ$z}L` | B`gL&Rar&lzzy[5@6RT+u¿=r/CV_򓂙0'w'%O:^Ŵgͱ4 |֯_/֭eϰ{iab oj, $|o0=ϼ"nƷZir_=9?tyjE!""""""ϓ׬ó0WҜb?=K/~ҳ3O_ow"""""""""9id3yc查 O*Y!""""""""ҳʚ!~<ό3׻et?DDDDDDDDD$^EDDDDDD䅷o~L&5j=W?@ɒO ?Eي?ƚqjق:bK9L\=++v6dMvCY~y -\E([' ;߲7-MQ#"O\|60E* ]6+L:rMpz%`sk+&M}{x3 RPV0ݳeR. ȫ=ə8&q_A8㒯4 q8>&Ny␅[lKU<&-Xj# (`eN嗱S&Ȋ7/u6&w/@?C~$9`NlB`[C|i6f`tSixŒbcsgؐ-2e+'lcwk0aW@ IDAT?-goL[~/Mh2j!+}F+tiٻ2|0{9W1ۇ7ƿz.m3McE/:*8!+śc₵lټI-Ӗw}V/e~5}Wt&{W=դk̝r'ޠFf3c wO?|9rgRv33lP\op.  W4mBU B̊y-ÛoEks+8=^1-us99 Nt/Z.>7eFAVqX+3U&̞[DEE{&oOlQDEE7|/"""""""XX={nNךǖ6Rr?26` MkRÿ)J`9!klhBjHW`xطtr˜ T[w)Nu߄/d>y'_đIt,GE%>VxnMSjTR7?5DDMfl,6_FM]aq,:~ .2RMWWwaOzMs'mDלxc۸ǔD &H7"Gv $gUm.aSZNl$U7>5Bo#CzP:6 @%68@1{'\f'=rC>=@YNGC<9S=V̝Kif֛5Xbs8H>8*KXq!N߼nGxywIn~dXe`KGL"""""""uz5YtL|܍5 N qOLӳ\:D Ka)5ݶ>%7b{&7 /ql.8OZzrΟ^Wh2=J:.\H DlQ̞fb˫A,ɘ8;uۮg˙8GJS.єZT(_s7kaG h#Π}.YiKeDDDDDDDDqg?㧩e+E(̟Q\{˜@G$&>Y F#F>eOG:gwh:9Eq='I]n79ݻ[|itf&\   jMLvV˘v3ĥd͂Xvquy;A;cVKo. xG}Ǧ&Ϟ9ZѥCJ0L_ 7WSbz:kڂE.op>7_Do=gTuI\}lID9!gr>'^9mp-&R(ytDDDDDDDxaN ruXBw?+}|ȟ\6DV!g.\pȓfNo 爡e߽b^> ^&¯T-SRR)Ui0Y̩lLu[ǟVߑH80v̝ٳϮ%,vRB53,O]}Z7~+4_e-NNJ/9G~W|lYܮmGΝ)s~`}]^ۧ ZGV3w6d\s\PHJ]dLiG ^>>YJlVO5!}QǏZsCgEbε+֣kB:Lez0tr6 y10d 3X=h*A9 V.ASu,n*ǀ#Tu};Efޓk[Hq|-bYiԯ.Ω\UnVSknˀS'͐ C}m Ŏ?'ްhˊM,YD !w9]Ah0td;c809 S5a[CÈ`/Ha}EX_>ϐ՗!~`& gqӧ,o`1<4yO3qͱ4 |zn:Zn &vƲ@Sᅟz[Vέ&qxtULf)=xd.yfqp|$ ,YFa<^[w~ly'h޼cmK/~Һ<_Âqoښ EDDDDDDDDҍ K}Wk Ӣ%/d\4k]S# ^.$dY[F$Wμ鱈H>'#>چ!Cc""""""""""ifyDx зOoF>Ջ!}üe+DDDDDDDDDD2 ' b~|<7C$SR*"""""""""wٽF x2Έd2""""""""""u;nuߓ#o@i>?G\4"""""""Ya61 gr шl-M9< dV&$ElݬD4#sQ/""""""u8K`0C:b1( yy{{={839nnx,H*]x(cZw|B""""""""""9<Sǚy,"""""""""z L4XDDDDDDDDDDĴ[m=S.̵mLx3c""""""""""i$ [+H{a+9c;&" #iY>?.v6g+Bו׀8A9p)wmE2""""""""O)㇈?ƚqjق:bK9L\=++v6dMvCY~y 1v&qSH)>vtXq#cWYp^ -ĽAcR. ȫ=ə8`O:/{Рt^qW}8؆)(kuA:Rrq)}ڌ`exI';󱴘Ċ_vn/[g鈈4'խ13WvI"8'=l!G|̜ZBtĝ>@Ć3\ !6S9ם};z\DF!#;Cir4Tz~_axO~TMݔ2٥\8@+bV6~xʵ4j=4zz{E_2E%^q1i&¯G lb]}m}HJ Đm9t&Cf4oDϡpR6lI`mT`Ŭ^ 턽SNƴ]SdX8 -5%0ã+c=6$9:=$=6NI8$D/W\=kЦ`&Mm~#X.WE<ܝգ>K/P؃akZO7'݋Jos%lPcJɂ]r5W0Hp pэ>TF^@B5D@xt>իƍ{}^3McE/:DQ4kJf`-[6dR #+e %O|O/ ,iɸX=+ѬO\ٹ=i?G-g[F~}uӠ6 ,ʠ[رeSLN#`}7\3{~">j;Oa̧~c]DjO4$iYZ|W3r=K668Ξe Y99oOWpNO_ڌ\?n+lmR=h@…fx6|? 2˝WgL=}#q7_߉"jL̟<ِ-)boj"""""""" ߱[br(HI^MlS^I>56[&aNy)Հ16yj8֥5_>yZ ufđItEE%>Vxngo[ۘ1W- [7 ) @a[5::͓u,Vr\6,hފDa(W>}i߲}Dk\ @\ a9۸sPN}l'_^nȤ~$WLx6,19cXEyŇ%!NH/o=n~Nxy)E,   6,B+b̮]TTſvx۳Hzx%F3ɝ~Li9'tnYOfݳ#.Af/`SKmg?[t1=H89FcoHI\4rx#ZJWSY6ӬO ee컘2Pǥ%ҮC^^IVHQOɴ+>-'unەl9H8u:M E2>uHH>w!{Sߺ6~Ԁ_;2p Ly枤 AEDDDDDDD$V݋P8?#v9}؇Lr H$1k01,95/{bu==9 gfC ,ڸ ?-KF?#)W_?KVZnlRX;Ȕ8wˀ@K'4qpxGǰoL&\z,X,Vh?"owٻ5㟙'AVj-VPR]ԾNKm-AJi]kCM}33;ǝדg.翉O ::9cO OX@@e|%3;k8;.oo˕';ù[~y& KǀeW>Hvn EJd춑LJOƭ."""""""7%R|wd+]\?XWCYgRu,Y`Ϯ=srC-E)Q[Ӱ9U\w01x6DzoKW BէRb/[iUX*;Dd@-q0]! .|ᓛ.9}&o :,|:ΗW msi綎Ͽ cJ:r"l(q}y,?ꏏi~EG.ckWmkˊ=|#> w|"Dzol+ZKҫt Qcm]EC] 37V-BIIInyckkVbOn4hNSn B9Yϳ~L<_!=ݬkH3{kOzwBF )@y\kHhͽ͗[lGkZMp'!&G*^?XWΉ{.ND>V/<+X2mu߬J޴^x`)l'_j@{SkmО>(݉+Gvp`;TLǹ߾g鼔)6ӓ (6^)řc ^X9ivF4J=հ9rNsYFƮdKwD>O犤ep[KZP\ЏגLq{*\/D!q\(_WA۵MIsyG("VqO@EDDDDDDDRDekS~HaD[ÛJ/פxt8=i;y+1O\4n11w^#l{Plw$,.[9.?-edw.뾈M>_{*0kVځA3LN^14u4ݷo?^'"1Y)\3ߍ~D2fX\< Pɗ|߫8XVA?땏dҒH ؞)};ɵQGwJgX*N%2 %[) _¨6gsD w \Q{~cmt~ʌ f!14N^/]vPa>xeiQPCrJXbM|%E,Rea!ۃ)+tZtGh>R͇D՟ФL.28ᚱ ]FQ֝>qwv&Mj\vV ]̴_p>*| .Gr1kQK x&3_eoggf~&Y2]j6&ní13&|4Y>3K 9)؎OPͅj |3}Uk~C iM뎳0ǰu2}A9ለ$$g8}V9>c8S.f!s$FlX3윘בj&sX3|؝ҹ܈:է䁟~yK_4s; W6YO9^-{ŀ.rVu>`#pt:nߟcsf/ݚ19}35aNkr4q͡=j\՛gӭHEaH>;,r3C?F/ &͵ÄϰY U&bJt{ >w"7+;\j1k` \ShDDDDDDDD) !o >ђ'O̙< 2 >.^iHNt=km._6W^~{YIϒ>43{,wNFYx;n1 7]zݒ1?yԦ_H hL Ğڧ4nÓv3Nԡz, w'eX!lҗE//n{9.JQC]gpsnWVT|8= 37LUńҧ#}+3jj^~q* ܦ`lQy<‚y WGǪӣsejՉ"ekIܓsaBtD],(Ҡ7uk2%Z2xIg#"l 1'9:.ޞȜߏx|-t3/8>x#J+>ܕ"N1?cS期UܺVed>V}~/5=Mqv(gwbRFz&z}ϒ}0Ln Q;ϲmu2s]oX_Bnê kL֕_yl0>,V +M%miu=uZ^___|yypDb7+lu%Ǔgg_%mdsg8wGD,aڲTi٘>dץ+"""""""ϠMD0i~\Ė?*+'vlr)i+y6&XXm jWT*QeK7 `Y5{;cxh>,,+{F6Gm3Wؽ0+H;Z _^u7QV+cwCxլKnJzfWXer8 RQ?gڶ.L䢡 pk ޴j[~<ݒZKoq9Q7_Cig }f`o 6̟¦ʯQ>[&<-]# v'w~ښ=/$g;*^KjR<;-d79 V"ӽrNcwq"vV/<+X2mu߬J޴wŐ=ߞ\'3:Qӹ"i{a89҄gn;QZP!G1gCٸߑ2K~c?q FHޜ ,%,4I?Sg͐ȔzOY̜f=Q+WϏ+VФYRGI$'`<&g<Bׯ|"E(/""""""%mv{2!""""""""""Iғ""""""""""JrqH*c#$Uy,"""""""""z&<{8n7gmFl-O=cRq2X͌8~ssmf.aԛ1=rPC<(#BpU<^ M" ei$ŞiݩwggdƈؔVDDDDDDD=B>|T2/8{:pme?зi% eM+sR@N 3\ Bzȝ'4x+E͎߲=,Y)CXR:Ԥglu,K\&{qM e;gr5}.J5zEca IW8v")}N*es-~$yDd.w2x`;>Yw\X@˷?Wf7tѴ8 x K~[/ӇcHrIr$ԷիEM-F_1'"*vNHV9_]>N\nDES\qH\\[/njA>es8rp!G3F)fY3p ^vxקdX\Y)qפ0߽Zwd1TvԾhZ:6Oc,4o#ޫm\<KK Ϋc3yXCҧ.r3C?F/ &͵ÄϰY U&bJt{ >wÙå 5EFDDDDDDD$y͙9DXth̜f2qv.b嬱ϙU76Er N)MV&scL_mS,qql v57ݜTϸ5{7.9U׍1fLbJz\zi=sF᜞a^h2kD'eM>Owb2d|w[t.͸qhj?&4`ۿDŽ4=kK)i0֙z~H.8fJ6d&|4(drs6i0?]g"ƶE_3qvt6i1o|i6݊¬ϸd]3/1ͳyf%1&ҬqL:7_M"Pb9m 3__ǯbbs=MTsgҌn9f>DϑKc&ٜ:u2{ap{i-Mg'u`q'{!_|ob C&9qDDDDDDDDPI<ݻ1cbwبɗ_Ih9;lC9_ 0m%+qv'y0G ֪Ial`r@{NtuRoAFr0DST e;@"*es`9{[昅$4ne^,^&]Nw7s8Vs+׏?j$VMk2m-LD('/XȜ- VxLl o99ᚹ~ G6$Ad嗓keٕk3js,Yx|%Ժ]DDDDDDDD!y_ WA dWX8w?ϜX:XpJ~Iij G?7 vhYϚ9|By.(mq7=;"QIoggٰz '' ƞPXqZXT`c,FXXcvmYR1Xr i9l~HZ/פxH~ wTRV6q)?ȩҳ(6އC0Nd_8L9%+ny4/|2ٻ%f"c >>dBnu䔼)ʴx%/WgSu%[uoo(O(^$yQlUw~I9U$r?@6r v;voI; QpdtN :Ҁ7ͣt)7y y\ܕKqHޙ97?c |v܇7M2alkpv8MK$Bݚw)߱ w2zb[jsѸk3b(lFҿ.72e/nequat >ȵto-ˇ4%[wXElQSGnI9_ǔ3phs^<-^}' n&@!}Q?kFl.~jȒYb<67O9.%ӎ,imr"փݘ9Cpt[{MnՊV;0ptɫV2~Ap}0f5(N@>m6}?S?dhKԟaS<o)OͤSg)Du[DuD#j;Ty}5bX:\%,xiCxci_:=g¤M EDDDDDDDReiQPCrJXbM|%E,Rea!ۃ)+tZY֞ 1`o􈧪Y! |.yZ 枸F͆vI``c1X?o#,HRчT'KK(#!zHJlp~[Ft"""""""""8TV8SXDDDDDDDDDiDDDDDDDDDDINbm"IN&g""""""""""8&ׇ~m#Nh>R͇H%9y0(_59G~#|.E|/m+ۓ1 IM<VƘCDDDDDDDDDDRU=!yc#H0DDDDDDDDDD$5Q屈C$vѬ?ŝ n۔7R6q`;>%qGaT,9,""""""""Rq2X͌8~ssmf.aԞsbÏ 9'dJcZgfv XvN&er ׌4bϴTɌ3iUcDxlJ+"""""""!y|чX> ^*wg=|u^Dzd`۴ɕ|j DbNT2ܜpN_ r^<8`u5=Y ø IDATX~$? ѕ>0YҺ)/ed{-L/OLE>vSwu%Ck&{t,ǖ}@糓--K1y]1>x6[1jΫS,љڥ[l' q)1Ы'U!Y ]+s4;4Ò!=b#"""""""\0 ajSjtxQ$}LGɺXΉyj25ˇ)ˍ]}+)#c>ۿŨ n]˦ 1>h>%uLͿea0Ws|J&~7Df0*'Gʨ8Ѝ{|60MY W6YO9^-{ŀ.rV9?JCZ<gC4}a|XG &l(AM># ͡i߸ ~Ns ZJ _kb>:/*$r3C?F/ &͵ÄϰY U&bJt{ >wÙå @}vsb\qqdFe3̑z3YӹGt&Wݯ̾XcbǘE.)M6[0&n3fɻLB9LFw'dk\c_o:ts6Yj&s)38vWw0o\x{7慎_:\a8;'7/S(EX;\cn 4ͼ~gө@.軿8w5clKc"͒vY;fK/jyqj&bR=`:zk96.и6ߜJ};fS_VYw0qw,dz\3s׍qfԁsptVvfi'wIV.[j޷ǜ8v8Qc#6˖.zkyD93GX0/ |\j@/,`4 y13+Yk5,߷N9N)p '&w=^鸼y={4e#?\bGo1R vg k,w44w K̘3'9mR?b3n:^x Y-9p 7L ןEM^FX)\4?Җ\Q~ܳ:/޷ή+[_xyB%8v"ra'_|}$ڲ#|*٭[8)X&n9Jf̵%dtjOvxovZmQk(y6[ض/G5.3n5'ΦoɝrPuȟ܈":fe؉b綽P2~ C4"7pWWw9~!!%3Rb1X-FK)#&3ٲ<54SZ#$k[a*HLv 3v39V,ƍbbxvHp5?&Ԏ725Y3g;|~:2,f)H1__|RW0sZei$&>jԚ+oτ6P#6}`0wk4Ϛ@ Gؒ^FW2Sn挳3/]\y36g4.-_SYȞΜ<3bɒ,Vo 9CgN28d@B)Q2,^>dwKXGd춑LJV[JKޔKeZ+3j+R %[uoo( w|"Dzol+ZKҫt(~8v^v}+B.Y>w^Nɞ/|ɀOPc=K:Ӷua" e؆w ޴j[g鴈ܒ2xÝ?K_\{uIx8Fs"`*vounFΡz_c Qf_Ce {_#|ؿk˥(݉!q;P}'O3fwʑ/؎6'n0P}Ym;mZQ.Y'-Ƹݱ-)f Ů{\^1/2:ߡ%>+yo#/MhA r9C?^KV3 !ҒpAP_Ç0~@ ښTz&LfO+p{6HwVb/ҹh\2}mFH7eF"< VcAˏoY>)ݽ ź/bϗW~X\m?kuk~`Kzrzg̎TdNlc}hWtgŅGѽH.ٜI) "u-Y(umHvRG'ɪ?P%O` lek0qYB```J,ϛm+3'KoDL6`D?e""""""""""ؑrC=$yr%#p<Cٶۋ +TZp'm+=g:'o{ɝvm_K$':g>CϐO$'mCDDDDDDDDD1899ĈR:"=ccc3'ʸc1Ǝ<}<^>k-ytEh>DDDDDDDD/AA,7?.""""**SuNu}'݃OgϞŠ/"""";{}_^y;$ }'^y=;oB<s뛴ȓ)QߣWDDDD`I?B~gJ'Mxa-_HOR?QDD)qc}schd +""OG?J }gӍ3jL(N Ī92^mBxB¤M1qt:v;F G? 'ޝEU ,"&jV[>jejϯԲ 5++D5Eađ2GcƙssuVBA=g3dAgpD\Px*{gnWGǐ<~pԟӝh`ܗ}x,E寊cG:4F*G/Q߰D͛maEТ?MX Ǚ7ycU/DаwȖQmǵpPm3hws=ձuKQsjl oIlWjBT`o@- R!B-&jE#K7> ë =LIsթZi+$Rymƭ ڻpZV _a~N*Cuxr}* /_5~L/Ihw߈{i4ZlvB%Un]kL>ҶgÔ8F<-e=N5˅ȯpǮeϱqDumZITGlGOݬ83^֩* _(=0u}t|i8NGn5D׶j:F=O*b.[OW,J~fbuԢNB"(n.9WyxGOo0.ꝡ Ux}q\ Q^95;f"*j]4Hz}h4ZlwҔd^< ud=u/Ѱ\J>^6UM=vmUGFiKu8 "ZǨ}Qss턖=ZOA/.WӠd>'ʺ>{Cg-Sl)W]vXK_k.oWiV{V=d*XLd\[=GSE}e:|A} =bNYӪ?hV4*&sI٢d2NKt%Z_^*KmĽ%VgF.Zb&Nﵠk{>JfmTu~0uSh8twH,dy>lQ Z1%TW^sbxZ5ؾ{SM|o{J&ި6k_ᚴWS>N}Zsv7tHgy SIh`Kcԯb‹jx^5<:C%Ҹ稊gN|Uc&vӓ?׺yW6kdmW䮀p*mҴT#U߽ K:n턖l*(~"M*W^,l}r䱘oWvjj<6Ve&sO םPPesrZ^{`m=7_ۨ:9f*"k ow}o Q2 흧iﻪ˪G&IM)VGxezj*!\XqYjX_ߡ}Tc7{& 751 ɷZy*qbޞN5k>o50.#͐aV*DDd^Dȿ~n^yszURJ7TPL?xY]$R!qjԾYu!#~+ULt)!BOOcŐdVȬyfjm\xIRU9M٠=V#Ǯʥb5Up36ҷ˦ꍋ4{Tu6IjeQW5sTSe&+qd?|C+("f}bCaVxg|RM o<{vIY?_i(vMjp0dͱ?=5K:eZiA@{a"j|H5>U|uk3n= OiºR1zl6TiɐI&4t61Q'-4^Դ&& slQ2'tCruOq\v3Umw\gXyMiJJ %nJ:>߀k;˘I9zYU~u@o&!Mi7\}:]2,{JUJR.kR)*f.UF7NRLrQ7n%&y8$0ll6C2w9F o[j2]UzοʔrWs IDATsJ)%)a8gՇ:|2Yi^eJĵTUgܧjYy{QBܾ WT=\tYVk٧4d2aa5ǭ׌Llo7ee%BMsɦym+B2[[\\O>M%~}5eKdѩO˂s~9@>fÚĽVH-QNWFeUl(ٰIu4ݙjX7dwQ7Fzffi6ðx<9(āɣa8D>u)15PP`;9EU:]TCoys8ODžY|PǙHtY3cukjdnRR%hJ:ѯf{gd'Aw$e+pOYӊ/ z"a*]zX4cv^m ~]*JVڡc#*Um\i젘5ut0U>bVJUԺI칬{?>:E/S\*` &it=j6ɰWV|uu>,F/26=t1%]qDM<%;kp|K{*eqIaV(Y֮/s+6g'AelE. }nU}[ZA+EW cgq Aq]32fASbW;NSlt Jx^Ӂ#uyYD1ez~؟4cz: SE P"3&']ndvxkrQʑr]JEh~9ږWDU7BګE?My?"jvousyN+~ BU9[S7дVJIK F"@dx@^]^Hǭt!}qV~tYU&εN_=YgF=U~gW2%CiU?&J2ɯwiÎ }bivLumhH*ᕪ3ߜR@jb[ʪZ0qbt^TŏkL쓺28FU\t#J.jy;%ێ뉑|?=ڢJ/G>wu|)^ջ=E}4Hiԯ|+_JJ7xo8V(EMղ_ZV L9]Tb9Y//uC~T݊]4r`ǫy9_YNH]thv}ƩUDl;[wf}R?њ7TP6閥>,ضxY+7VL֚mJE;O5aROmK+zUx.!^%|U} 4kxMXzξ$W'!jL?5FsEU=4TO;g{I&yEvn^ .Ju;jƻԥf\n#Q9aMI]'C5Usp-D͛NPf F#zL%Pp&Rd)[3}AFZC~[hL֪Zo1kҸ5 :sլB"~L,&sq==6+Wk즩JI'4S6h4Mxt]UP1I gn320$zf}"=i,Rs? ęjDL> Pbǖ2b<'&ktyTTYmuP$A.bY11M6EVl6kC~O blLڴqcnjS۝ijĽrR^?ݽH+O?_y &D7KfY6M9nC NbsWc>rHe2$# |ŐͰ#@םż箒QQQ:p'&wu`^ @"#s܆ٝļkGGG@A + !y pB1 c'$NH<8q|g2aIڴq 17ƽ oqc1<01 c'$NH<8!y pB1 c'$NH<8!y pB1 c'$NH<8!y pB1 c'$NK_qIENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-edit-analog-channel.png000066400000000000000000001424341501654372000233670ustar00rootroot00000000000000PNG  IHDRWvC#sBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeMo 29 Nov 2021 11:00:56 CETh IDATxutg=#B.-Kq+R(b-VC)N)VN)HHBb;$hh9C'3wfG+.!B!B!B!B!B!B!B!B&"B!8z ﺂwMT@NR^ h4*hB!CQz}TDd䅋/O8 ]"1({ull,zEIB!JB`ffK._:Dwi1,KgTqqq \!X ∎QiYZZ_ByuceccSٹpB!'OQ\]];ؔ [5rȟODd(B񉈈0ȗ/Ӛ\kLLrJB!>7118e&WB!x W⇟z&WvF!BO`&/u$(B p75ਉ^ͣ8uzŻAiNB!f1TCkQ:5~l1.wN+!B||b*Z&'cc` ;J!.Q4IӲf*Q2cpubBNmUмA1լY݇=P%!kjҐnFӛ1"4uIsb\#8FF!>bf3y,#U1z[%34)J)=_ILobPNN/Tzxoq6IߟfA!:]-[.ɸo0ڵ0 8`)Fk6=)Ǐ[GR wc坴@TNuqwT[vqu&7U[IȢŒ(̩kY(3:@!;kr++9Q־eVWhH؎U bX~̓\ aT1QD%Kѥ `ÝLjM5? TFz4i҆/lKw.P{ ~?ԮvȠch˂G|wˏf`tBYǽ|]241F᪵\!E:[XRLZkLbC>~]7PvX^t@A73?څ,r;7JAp0ꎹ]{Wשrʀa4eyfouV-J~NqU4ksb_tWZMqd۾tkR.,q2d25B^u b9'%[9p35(.}%cvi ESܳڣ(1> tѧ m8>+r3x+roB93Qb X+F JxDmW&.fф<~֍e[snwZȕ7oM*舊6{) "}/,Fʞ2q40Sn/l\Ш#j%3?˔Y'r8L47\1s 6tKcgE>3&7&q<|c4Nnl!>VZMw}P¶xx09'M%WRbg?#] Z_أeаa]#6-䷁mhӪ~IPj#uj5)5qGwK WrF!6Q68v|BmؚnzӽlNFIqI֣vCg*nNui?K<ڴ б^fLGޚV1jӝ7&fmN|dҨ^BQ;E%wۯ7/vM0 `aƴhޘRqR9B# 5WBB G7ir% CIy^kW4yRJ>v Cч{vRAu\lL9Qc({'4@ڕc`dTHtVXoTf* !~ pݜ l<(CyLUBX(J~KIyh'\TNsR:6iX|^xP ) njx."'>RF>V͂`* zW\ aT\_;>W]~4!PzkVaWҀڡ?y鯚"}3+o\MCS']RFiF.<χ61{Si'UBB(in3<#Ise4^%&Bb^=6iZFF (1ٷ/OXOrB|ũ ֩_weQe}``S H k)ѸvRD:sY&&)_ũS#<'qGp4?ujO\6kՠvg_܉NW7+TFWbe_ LVELT(׸|G Ԏ_PZ,puzc?bPT5!k-Y n[LY(I,vrSX0NM'Al KD\ !>.*+b/r'4'nήƆp?[/`-Im 5p)-b`[:5#LYjtn LNBQnFqo fA!B|JyB!D:J!"Ir%B$B!HG\ !B#IB!ґ$WB!(\= q!B|Ҝ\9{C!BaӴ4 !B#IB!ґ$WB!H+!Bt$ɕB!D:J!"Ir%B$B!HG\ !B#IB!Q]~ !B5j:?HrV-Bm޼WB!ґ$WB!H+!Bt$ɕB!D:J!"Ir%B$B!HG\ !B#IB!]| !(((C"9;;䄧gFI  44'Odt(#V5U+$ ^wF"Ѹu/^PB') NGJ2: 8}Q\\$!!!TR%¨hZ8a|BCCX"dt(PX1>aHr%^@!R&ƇID2eIBd(IħFB!HGRs%Hέ¯ ߵGwsګf +V4J!Rs%C(GMb;sR?K6,_b#ԥgS&Kn{2}z10*5l+6ߠW&+Y1g9zLZ<%;?"Ve.&,k39M\}|:Mowl,mp/Ҕv?x MLx ȃ BCzI+L3#H_MS8KgOُuozlM1GM8ԜGk7跍bDSpVoYŰ"WԬ9S.ĥ)"%*aVԛC)jͱM)^#_taѭ|t+[~_Üa>wck+F?ٻw/{aYwL,|Uk_Y̔? yͳt;%j&ڿi \1M0d@)ěIr(~{ ǩ]=J(;!ָH' h_(y29`mam4#R;vVdj]_6~y-C&n>K@e+Pjmvʬ'MNN4ݫ,e˖j+?5#\Dgfâi\ ~^D'5{iΩUf{BhК>cWre4x^sC9؁fV0kSmD^39Y\2+WT.[W*VW ZK͝rPf S[k$,JlPu3Xs"qw} Jpډ庳duQ~Ը*-'̥&i_AM%Bύui˞v*У',P x9\'@SSʅڶa/K{Th :j*M.M(e|ra4)oG(d)>ly it-?ы1R T0Lu^W"p8ty#9z2(o2QQ 6V݄8AR0j/A]㬊 b 6 GPz6 H 4@Q㏤y 6Lǒ*\2S~9}!vp..NʦbRnd&[f50K B5L4tkk[=Fl}}:+~>ٳg9wuShe+`듄{x'LJaJΦ38rJzKk]B+c [ӡ/W;ԉf<:bAt7\K򞤽GX1qBCBWpk-8ӢsUlԙi޵&^ѪQ4rf1ZI;M:qQ,7w^g@%: [C9= rh0yk  T(ӔAq)ysӰaC6lڿ%Wʕэf'O!G{C8ٻ[:i03EC'L0U0i068ũS әs\8VnѥDxdM*ꚠ剗z/yeи&y *Ww\ՙGd !_x{U(9 "YnT )֑P&lR7,N&X':|h/g;Ɯfʔh3O`^=R4d^oM$_c ~MzzmgQPf*!\gdS82<(blj[YbaX= jx[a??,h_Wr*EAؑ\E>ƹVz(U2MYb>4̮BsA8 x,HbẄsC4Ȫ5޾ ШIR vU<ʼ%u)}|yYc`]PvRmb@B^/ȴW! VK<7RgA4\؅>݉ҒӼPH^G8JuosgE,mR+%8b!6"{Y6ssvSU @E}^7}r0]6mjXXb?T&{W@v䀩gvl;状 M^ ΢VYP ӽR^=ŧ)~m*c@M\!+VO]X4K_%{[_/uT=MMGүvu79h ܫüы>Q7Cּ%TzO~e#Iϗ Le :2eh-8u '˶D&sTӼ(M]}T >ҷgr5~jHVʂG;B,`i!⥇U'G͹Bc)b(QYN%lF䭣WuO42ڍ9cEookM&Kǹ,9ѓRmP*K{ Q#: IDAT{'AmYGSw-#8d+Lq>+5v..mKsQY9*a/t33CjN Rg ~Ӌ*GѼp6߰i*5aÊ$Hս:,-m7=aE~-.vZ*S,\Y6kNFtK4bB|%oݕ@k)70fLRM0!c~PZsr!!!~i]/>>>(7oyFgÆ rO@6oL:u0kL 'FUy@_6t&Qvj5vJyfjԨ;::m9IBd(c+O!nG|0VdQ%G\ !/ϝi},s +ڸdBN+!jL)7'%? rIr%8;;gtB%6>''' *ʈ]Z`0ѡHr%^prrԩS(Q"ChN˗/j3:I \x᜝qrr3CdyyyqΞ=KpppF#>bNNNhZ2:I *!!>3 HW2bB!D:J!"Ir%B$B!HG\ !B#y[P$ Ad(!J$ @ѡa4nݺŋe󄆆~\999$'F+$$$*UdtBVˁ2:O牉!gΜ̙3N:Ef3F1H\$(DpBCC)VXFǃ/ΡC>P4"#Hv!sl L$B!ѻ\͙3g {r5IBtRr;/,,%KgN<1znnΨތގaGBD3x8^bAq}Eyiݻܽ{oܹ`HL%VALXZڑL.J <#7y(8¶}' z^q4s+{v ~-V^Y011yy2ͦ 7`tU,vd`,8F[Ed (6 QU\||--ըTe3~{39V&#'^AΌY77F'.s ;=ľ,Q\\\#..'^O֭$&mϸ6/E.XvvF ։^U4wrl#rk_RߏMi5֗i84 x11Vyr$gMe%N\jq!'{Vip.`"OH֫xdJ6WYt0gHnonBsf JۍE~`遺<|I0;,im)WW0S;22Aӎ%q5e"{hE xB~bUa{ɝhiƟJ8a΢K*S$T+ԫ>}y2tF+<8qqϙBxj~> G(R}|їɳr%6!./X1\~QɔN"eݻd͚S~z._̠A077ɞ㦋ɝs\U|6Wh1G]ƪ:kUmsb5WT)EYUIN߲dH>ec/1mi\JHcH9]YيR|y'NRIhEA8ZcaHF1Pw{#$/vf5,(Uw0[vtuźlIشZOR>CӺPp6m^?켛Pb0psM_j(@M6rՄ}Y9>=W?(ӳz3^Aikٶs K{\ M h4:E.eZFim104~O 3hVvXiE8 uƲOwVNOќ]>i{Ege*cO_KcnLS;fe|] (D8└q+VrI A<|^l{:`,jqT_?8k03MvF(\yQ3lIC=~-9?~sJjTuM(%pZoiMⓥtÏh2F}C: is$6#Ky6Tngl,,uN/1f|3T[g'K X ȯCQ,6ZnʼK@W[2Y?+fߊ ϽrU$&L6668::8ؼsrSNMXkէO?h=e]-%0<\O:ݴ6cn[̶g.^Bt'ϝܹ]S-1]<3>=㏽!sJuΝ\Up:öSُ'xY N=GY) :.X_1+7P9xQ~UJ$+&/#" (3\Z';tkVf׌Um'iLs.2Kv@" w5e7jh=\Z||dm#Csl"w3m ;`lJTȎ7V(Uf5S;[elZW GU`K)!+ޅ4Y0w?Ld.W<̟vTM7JITVc*X+rVlșm In:u%ax? ~{ NoA䙶ߘXVYê%'%"SwSIq3,/ zN̮/Vv̈́:5Ri'EVb{d6{,c3]|a.~ףjZ:'6Y3-g0hT3zaC̯7}Mz,HQ˙ׂ;ʦ!D|F*pzJASs 9Jft4ƆǏo>:uV}ZTY ]^t2[4c7T({𚰎3 !8ijP!6$w¿KW[ӈcb"7KeF~BdnF\W/z4bȆi +uh .Ĺ 8=a O y-{PX8PGU+RbrM:tnHG~L)Ey}p&RH`o,c }sпJsTG5ӒY56FV#bJN2| jLͩ`L_z | <mEun@3oGz_%^uJkDEV ۶9^lOԒ[:g l[U4'ʹe%$ٺ0:Kɼөު]uagLcGMۙ)T az.!i݈uo,uĝ?Mhaۿ| h]->5m*5=͎;ue QJqY[[3atѣTRR_Ukh^&,}1۠#Vm2Qf`\8+[TO dIQ1ϴ<62qT,C4>oRܽ KDT+EC)N%Lg]>(] MF] ((*{iP0lul?9eWe_MD!dJ>ÒFXXX`a~{cZm1KlPۓݫ$tsifsTﻆ ܿqۏ 2ښ,0qn?j4uK< R *JSSL0cUsV1c*U})i 냳ѡs līH~=-)ؤ~y0䫆][aFnpFLJA_p(-,QByT}MrIn,Kٮ֤K;yBq,F~Ͳڿ5G:%r`Le;a+*ܸ+3kԏs{37DK.&`9^ g$}Qԁ{d Ϯ_.D)in9^5ak7̨ҫaE뾅>--MGXG?UyZUΊڬ]f=mIߪ%OD Z!s9c3Ν9m) e\2_ 2YD%nsSeޢ=DWɎo MY`iT֩y$]y~k&ORfBE\Xkg|7YӸC=ڐ#/fz.\#KѯE|t:wTP1qnl[FD|^4dVšu}.qm_rrvʄ&kWaѷ^ @M&oo2鷰pmJ*44qppxWX޽{RBopAn|<-fgi]pat'bHKJOOvCZ4|F/G Y]AԋȬLc-}X ~)$YFr˗u[n><lr.J"j=f -(+PTƭ-IX˿?HS &?| % +*kύ&ؐ٣*I̠ޙc6d:Z&./e=-0Km߱vQ_OkCЙ㖣85JeO8ؖ#H:xҍeݽ!iԨlwh;9 SjJWΊ5; fj׏U?@m!Ob\. `JR*Qwy*1=iП~Mv1(J '>̵̘Q8dûJ~?_M&n_?M%g֋>>*]?ΉX.nJ5+:W,98 'r3|Buʍ 2+~ـLs6vZ|`s9UKS0-t=Sb9{T ĕUI OwӚ7H|Fe㌳e[Vq|GJ*fHF~E߆"1s<`*Ѽyj\\0>v561s^o}iW-)1Pgm=Ǝœky![a}ɕ,FOjKAQ,qv/D a6?L='eM@Q5 /^ɔk;YǺVԉ%Wšx0*r!!!~i]/>>>%@_Q(݇}LTu8:P͛7Ӽy 6zO{̎bDN華4΋`<ȴ!Ù,uZ<*%/]=2 8s5NY]2@,wNdĸb=<5s}6[t,5:]aRdlj_K1JEM^ܼy35jH}%WbDחڵk4/y VJZfΝcΝK,5)R>9z GGѷ-g5WDs7X.,̔'mXSd!> +vL߰ڭ2W18qFFZ;u|mA[U/Mu-f;uG Eپf9묝t9 *K =xOߩ-*I>>讳Gu?&[L<:<ʚB|RJL9}"cHr/Ucؤ0Î0,B!>c\$ίC(pm7㷈fΏSFޥ&JVc0j0"_3~"#899qԩCrO20NNN\|9HW**͓` @O\$\x᜝qrr3Cd*TΜ9CpppF.ٓeZ-Z… M+B etBόWF DfA!Bt$ɕB!D:J!"Ir%B$B!HGxI@@!!!2P B#ɕH^;Chܺu/0%P@@O6$W"4 26>P*VN'Dj{TDT ސ{/1%v%c"6%~QQT EAѠ\u;;;sv-˱c I\IHHHHђSayYW$$4bC$q%!!!!)pfffa=͂ IDATj2ohm}f6II{".6@Tftp{&pdS~ Dh>e|<9F%sKv&\ gs}L Sl;g >Ǘ'VT'M4'gAL0-I븞\.tdIaG/Y͇X÷r.['!!Nޜ$I$c鳜ZӅ3'z3 ;{P'"MRJM@jtY][t)zJu|c52dF8+ꯋsShf5ere$&[8cE*鍯/>Tp2F75ϧa)fPnv71-ANUυ%%*WZT=ױ&e1Ɲ\&)nmM {,#y">~4,m&8yryԀL滃яC(aXqyom'!dƶïW\^ҭm] 'KC p9s50!peTFH}Q8?2#>Nv)0u<3kqf.4O}yd}t\!g݃w0mXC' 6A &.wrޖivc_~ޑ .8+㺷Nrڦ90KuQ|;q1*yucNJ|<"d/>FM,Yi/3aBBB : d= vŨ<Ȱl0yArO4<2mx(^z9'QQJ $IȖ(fyvF;]Z T͙Avoή>4X<=Z} uOgrvos:MqAŨUto=3iHA:,leƅG1-Z=AMCz!_set,*GwP;Y8:R!ts:=M ϟ>y /Jhe3y| [`n[o~W#UQ<Eb~!GU(pI"O}/NXZ~i Zx 8~& 쐙:SJeԦqoEe 4טYZ>;nÃϠnf\,d-S7-ۍr5CұZ1 pL{E ZۋJP}=C,*z!fjL|vScx4̆9yw^_QSEAt_sKA> /fpc,0(LX~BˋT<% 3[WjtϙB6{|)li7Ajm)sL111ҲBfA mBJ<# .ĭtԣӫr+w 4qA &`"oP>y'@ccg` ,9r}!\vHP3w}-Tת}Z.QXvq, \RVc\Heu#l\(Rh8E&,LOLҥ:U LcOKZ^f)D7IJJBDP"};ы ez4Dž]p̬XeQl~ϫ'F|Όü(Cit8 ͋P+ S:2-=x{%Z3? :]8]7`[Zڽ24^8sGq}#F7nVN\sÄE{0x'p'd.#Ƿe6wCE}T  ֏HpK]N'\vnYPrT (T=|e^>:էoxiMXNO%9LùI;0(.ƈ<F`m8ox3CǶ;kaD`aa(رcǵW̼tdOq`¾iU b*i $I+X-Ir:fx׫̇Ȕܶ]z.8XR?s}6%ZԴl=GzJ=Er!Դzb&MV!9;6⧓x{揧ONlfT)kT(LDej xp!7;P]QBWQ31bwSKfي5>I}خ!孛6@vϤ}VDH~A9s&1(5u-ٛEcwQuL7zF$acVmm]]8u7K~P( xa,|lU Ż,ay?:wN9Rf>O$B~<P-10kQ!E[Qʷ&rՍn:qo_@ˀr%x~7qNX2~KWyO@@+000=_,y4Ffjsq9eQu]aA{?CfRf 29򷸗2)s*v{MBFd V+7-=3;"tolxM#kV dbqN^԰A^q ^pɰ 7m/R-r@GG/ԵNq FP=Z-ӳɛgl>4oo Y{?JhfqJ ƹQ8RLx&|)UL^H %Y d4n5s/Gfg>(E*1$c4_%=V_Ə"oT#h$j1])j-vu2#>:رg DCz8Ie 6z'fk}Kp6v}6SgQO pD$)Kg;-m)xP9 8V2.0'Q}q}Ei׵6Rǃy/ô".U~D|Ʈ5c7#ӛP<'G&C.IINy֣ya'Lc0`$U7eStp0'/$Ѳ. Z$UWj=V LA6:eYVإėk[N=XXY ߓSx66A'g]1 T)AIעj ]@LdrL }W_L˂iSUeԒAĀߥ_S溲Fe+" D FD 햲GRkEy~,|[U ...ZzaLnFuwfQ\L@[ }ӮҊv*^bXI=z}[:3;l8f<՗SVZ5&5hRˈ?~r4$ ^N.l-cK2y=X}ִLl>= #w#boÊ=xu&zɩKn)aXV2xb~7OkKnDabXh\;u2G&GOaţnY>u",މf xiǸ::8Iq+'rO&5~|3.in컡,#<ۚC鹬:ۻ}?HF9O9ߚrW?r=讂7^x9 -u;;qY\et=pDf!&CDDQu2紪6 G$bIay&FʒpMoBtlF׽42Y*H?aZ/\7űmdn¤1~0G/A^+(0GZσ89f4ؔMaH٠7?wq [44hp= čchYp3粝\Z ?ba.6͙Q+Br0I5|Fci9Ooיv7К0y8Nجf 'գZRk zR8ӝ1]k?a @6K5!2Pu1\ԐNufyN<p )z8èm\-m̆wt`_v`*j>;g6J"(waѬTÛTeycKY՟Qg1L0%SX4dGgem|>+veNIȯo`tL{G9WS;W[>Ͱ|؏+dOnr%[y~E\3ʉc=2b?#2)+tc` ׉Peӊ鍮(fAɳ-%+%$~h<%nO|x<9$0}sh´=[^aL5Ss2OHqh8.pu]lƏ0":N6) ",nP<VL*[rwʶ8ݚ̻A2K }灼0MfɌܝ.Mhb2 a]:gPk}.-%5,;+)r>d&^t_v^NB"{n3Љtg#8s0d Z ŸQކqؗoĔm5QYݖ~gD< -`N}B"q%_csLA |ZBB1旴}=[nOmYTcltdV#*ں?p7)s. ~"/̼n6#>>9CeAs~uBeʒcFt㪚>iD}. lmNiϕ-KBxq| گ㹩+zamߒҗ+!!YO1)/~1Z5D&4AI\} ݁8o;0i{q+ <#DjND1#E?};<&@yO$$ұo$$o|\ތ\)0<1YOjWdȰ̜]}H+ ҒS\6EBBk8wm% K|8rAJJ"777^Ços$$+++,--qssoSX,,,v%KԚ%>OA 22 6EWY)UT~ !!Ε+Wmg%)_<<< .^Hll{cjjJٲe?Ey$$4$q%lc9G(Z3W0R|ܹsŤ|FíM3'gY iȒȂ(6Q/1aĿh>/' SSJkSP\"5R'7-Ey(b -$kH(/,033{WG3afm6MRҞxvpL_J)6e< W=͇k[b\shSGL-JDETEpq)"辬̱s@Ѭ<{ե!bV=6h1w;GۄF8+d¦4<n N'٭NԚ.͜ $YfBD6I)5iZ GGT-, ݈o/dˮCh3Dυ, ר QxIH ~TXN~T P*'"9˚\^Nu)=ANLג"82CtMp ףX~ +>nh3NFs)O@\"3uBTaA,6zS,__Fz`L04ýn~nlAEEpbpq OjzQ;9ByY{cndCs7mm8!z86Z_yCAM[y9i_w ߚjCR47G4tN |`*9[cfjErM~-~Ha9dTZ54hsvf"\Ȑ#3IE[`lSf@\_aTT`}?3vD1 )P̛gD_chԒqZp}z z-|l:b0`1Z06BB+J} I;eTIsI=wO-)Wgi'gn%vŨ<Ղ^B"'vL#%$&$" Yپ6=/fԴ}±SOwTT駈%inq<;ù"dhn#k|K$ h+RR |PmPytf`TP)W09ыiJMbB &YYxVid)1k}%ly̞߰ qE)fB78q~. @EvƱZ̔CGryS6Rtv/ףP|32գ3}Qn0ĩJ L7^˜f(_;0érKϙJRi$s{TO^ˑ+wI~Prg;dq ? }*(n޾H|unzė37K+7qt.zW퀥~L@ fds4)n ?wZ}XZaY*)Y? VE;V < }ͭO@RTsV8HF:u3F^d IDATR8|6 ST}ix̀ IXw8}J(!6C.m >~[za2uDb A-8 YExqlF7E6u.G.f5&KQؑ{ |z=VuO?'-G׉?.J4.r&s7v0aQ:sqA]y:p;ߦ;2gmwVgY.u9u;%QEUA J Sb÷f3EyFyfFUYY_.5ߠ$\r#;y v]HnуgЩ_CP_ rl".WhR4@OU龬LoG'Dr.<"mkQ=2'ZChc>Oq`ǎH$"<{W,"#[P,Sls8U&瑌_ջOH10FD>!{IEfA mBJ<# . R,x9&*(T>u?]b_=s<=0c%1 r|\+,d@W9c>w'.rꘊ]0;Bj)3'rf!?TՌm` QٖM5(s"ȭ([_G9TFu7|R|U87b/ tT(icZ{i8.Ա&"%⊶eHe1+зTs)P>'wFv c3Y#i0UgIBFU}ǕleLo˂by2j b@R(d^ug054P@ hDD(*(n){d,eVaYhstTF\ U =Rhj<輑,{RI}knjqS[˫)»?=ĸɯԳHaLR3-GңVqLS jWd*k*I"̞e"(ٲ:cd(r*wB@|vSGpK;9V~TA.]y9T'':ӱI$.̬U]kmTjLC,;M(akO3i59Vd6_ײ,]г{Mݺ:6Zv,cI*-MP;1_sh+eJ)Y?( W- b<@׳? G?nb2TADeX.CA{N2g8r& ?O _sUVO7%] ѱ_Oi6dTWQ3Wȝ}(~AMҀ"2sz {R?mޚ *+34~78f7G2f(:yZ߆bz5]}4,[Ο?O} OIDDj6E … \z5ߝUI\I3ʖ-f|߷ѣ ٳgx{{k߄)[,ǎo3$q% hIXI)ڰ,+ @^O/^$66155"ҐĕG "%%JR|:ܹsݎ֦}̓,,!!! YQd޽ݻwINN~xn055}ks d$\cuZ5F_߄Bu <8-o/8CAqi8rG_$F]}b,gUl}N)\7١P-^׏ƝrMH'_Չ+4,dO_n#1oL/.~w @{ܸvK\Q >&)))ܻw,K4 mڴ͍wgWguPTcRzYy{N"fcszd/Mhï%{ ҌD;ExƉYtgE6uٶ:.kxpp3=# eߥdc~иb&%賀w!} !'|6HfOۚM@ P*'"9˚\^Nu) E]\p1@Q쿢e*/sr706)`yƽۏHԳűXq;;`e GL?O \8 bU',RRR`6nHdd$۷GWWGGTBspzlk(K a<˭9b'FbPo?rŪWt]|Y$`iU7Ҿ$Ucቨ=]KG\JP(}1 :5gu9A`mK3`sjS]|@k4ιeZҏyx{ͮ,|q3ؼ %};3heAmctuu3=& ~r^}ל? #ud*۾aIHp>>iKSx>BBB 9ʮ(7$5zΝ?$?҆f_տQ5fXk"xݼέ/[SEm0UunЊ /C##{Ak[s 146D7ߟǫN Y < d.h?~< 2$uLPkԡi<;E0)!/Ggh6ئ=<}1'6PD~gXb6&R7Ϧ66Ш%үӫ`ЛзSଓ0 F+Q $ک'څ2_ia7G1CyHth3%ȢmjthmO>|.PO?i3`Z/Jոj3͏s b_*(;p+'OӜ>+~ϼBYԋc­<Ҋ~q魠7PworU((m zD }'@ǡ5Q)dl =wyoNKUMytMle,(Ѱ??6(~"7Nb ,\`jewh"݈JJB/F|)Y8 _:5 m_jd\I NKɍ+"skՌ_s8/~8S^Lpo^]*-#t(j/^LhbzD{Ot3{K$%Lo]i91mc`iDeR@B4kE9N߮T@"" >;ƄC8V iU Y O}M ɂ(cǎ7V\\2 NOphzwWyf'ap‘* 9hf̽cG[S _Rvr DW¡qPX Eq+U*cRKգm`Mt29C_[IWi0ʊi̔2U.U dDv841eDNoΙwTrNWnM:uqן]_g,pN-Eqx?~`n"~ÎŅ _ldԫ;;cڱQYGqn+dٖ_&5Ȫ?`tLkKH]LeJ.Æi5Mdf٫cᄫ &N:… i۶mE.p#W +Dr<ϧrZ44<\ӝ&#baC~~r)IW #{.^m,ZPbE֖pjۚDs#%&go]&"ނJiA^%C͟xDpZAKRkVp 2Ӣls'6ïm!pb^O*¹@bb kL_o¾~h.s>"j3ơ#49|5~{!F#C9u} '=щK\[U0ʡ̢9/FrGUeYpsƄyM֡ Z8yQCfPWz,M#L]HKwH``v?r@GOmdl<;, oޫ"VDEGcQ^bO5kb5wQ7Lb/ѨX7Y31LswX9sJULk16eQ*1FE(߀<WsHD<l776oӸIcbh!`d #4 lLS=2JǽZR99}"[бK-0Hq78pVnɓBs֚ IDATb3yY-{Gga"BÍ5̲;a;ߍH(7Gs(PC"n^0s\GÚo欥Q uk442.0k{JUuk d Ԅ?Kxt׀FEywW!F(oL#G\Dj-MZƝmG1>~못bÌ#_P|"5>S*W5z]EEJ*{՟r^ylͭM{ȟJq3o=yp0BCN$+@r:7_,9 i$+",U?ЄebŊI8TY+lX:v2C\+<+S!5ucF ߠ/9u33ߠaÆXY s8z?X,=r4ΰ2kPwhQu9|Yv/_a8/I>Ft hL48؛2IRǏcooLLlcJ```Sh :aV\>}Y+5;ʑ{ fߥ7R 0-M;Gkg91Zۘx7=sy`\3it΃9rcjZPo`kF,UL\#c1c/)%!Ϣcg`@FB //J.Mh U0a=-u[~\弩T/eY%\DJ's\Kd)< IqS&^U/,,g/)ԧ|wճyRV6N5;/Ҹ RL1#=2J0O >7mRn6qEiS[4t,n1,:'o i0IHkrI^ JI3W/|R/w3J!,粣LGԌ?f}jZ4hsjpG6I*#T&yظ;5sj1З5ҿo[rMDM[8O+05L7JLޑM(lz%1Y Jзls>h)/9^kr̆1Z%< 4+9,%Jaaa.{W-z-Xgˆoh_{Zc rD6,8ڏN X߀Ig&b(&7CTZruU0nf;j CcfZDoc][=߶]3 ;gW/;߾8tkϢbY'^+dz{H|Np[F ޾GM|ns ӆ{2{9\ֿNC5 Ey0jYOgZh&oJ>P]?/P5SMgzU*kP5HU Y뻟s/aO7*3g̢xAUh6+ Ϡצ|_1$۬u 5[R״3sP:dd?EQ ]X33F-SԿ9ڝIA+&:2:%<ѯ%`dܖD3-V4w6uǍE|hg>.@&%tOv$0ߦ }"OUaeqjL`7Ljp4F+A :3̰ Hj* cHs֌ 7aʸ4E]1C vumΌZӽ#vfN@Jcp i+ה9{3&!E4&(X=^@ [ʼn춯{ӔajjqYܼXo:!q4.%4Xr%oWGkҚ)-Ժ2K%oB);h4%R5ؖO_mΆ:ܐYqezi :-}z8ZbZWj`]N Ā5 {z ɲ/0ᅊNnp}GM(>b1v:{L59\Pl[u*'Mx~F%вw̶JGԟܳ:ɘ zO#͙j9XQ?3,7,y|s7oВ,=)sPq8SkrxP=;uNt~y8PFzOX1((m =pM6}*̲}vjժ8@:uO\%jٻw݈O۩Wk;}Dԧ<gG}"W iP\H-z.MkgggW 8~W"C^C@+ ___Ξ=K```fW'Kۿ[koo=E |2zȚEO/.T%)R$ )R/r9{{{h$B$k+>6C B!WB!H+!Bt$B!D:J!"ӂ"ٳg I6666,Y2j$L@@@fWEdaٲeOO̮W"^HHeʔI6Nʠ!>DEEQJ̮9sK.啩m>e8[odv`*W7Y[ɒ%9~xfW㣎ҏ3;rn 6 J+}C+fabnSR>^WQOdp3*\^Tm?%HB=L2ۤ RxWx8neؖ8K*m%ՕePa;^P Vlw}>׿K%BtU4+e?@*iy?oCgi tSjs Өhv,]gI5Ȇ{?3ͼѺ8V;Č Yg$FWQСZ :aajeBTR ;f32~nZe ل"g"{L)lmű .Ee~F$ls;r6G:ɤ?WIimժ{XJ\L~D:Ϝ3y;kCD]?4` ,5y ? (NsHM)_9#Л* \ 16#W-,ȰM)5 q볈ogԞYg68;[Ŝ-T_$ ~߱Jd}_ɗerU2pkgm5u8bc7  =i1)BOP FҶ8+Yۻ3QBD]3*-{Mgp.1*+8}|d e `hdbzjo}l֟iZ `_E^`#:fr."β %O5K0X7f>y@xW1H~z.rsǢaΑgTF@ pR%ya@cQ_a·|/qМ'Х  RZ_W(8 enJ4P6<{f͌MOCQp5f> ;ehACm{r'cѠe5, !՟_Ҹ͜5zvgl\'IHBwl%C?:4`x1x"nmBi1^%F )7J2ЗKqsd`Y/f#Z_uV W-s#,:U&UTAB=~*2w OΪ1R/گ8eJjYuhfhq-pƈ^7}0@퉙%:c0o9y?kZ3bWD5q3@ӏ)vUYVd! r}&^$x%͆E\yCѩZU5 welX4Qc1q$6d!5l777&3&z\e5smW ev@[V)ħ*܇Un1W~'A_(mNﵫ95t:`AR%^{Έ탚W[fr ?7NqV@á7x<%R bM\ &xAjL/AN/yFVEѿñ^Uyn4dͅ.(4CJp *e+*gMƟӒ\+flK=;8TOՋCk>]WD;7d捬:6Uޭ>}#TڗsQRP,\=yg IM*e{Wq{c_fX'm`N5ղ= *1QU"HX~Lp.ƙf_e^9t0CE84Lugmz ~Y0e C~d12FBLgNz6{8wCL:al>#8:!tlTzz0u/ԝ3*L9C)gQպykc64n3 ϫ3m`[k&ԔF-d(Jۆx|޶дiwBO۩WJA#{L9ũ&^[̽YGHղw޷AoNZ޺|;;J@pŐۂB!2UUP##c+RC '"(EۂB_eBOYߓ^ gM| H<{OB!>A3Wx'W!x2ߧJ_2W\ !4(FIJj(J$Kpp0]!2]lٰ3Ѳxxxc"kR???3*\xDEEQ;#G̙3\t //̮GH"\xsY)R$"Lʕ枵dɒ?~-zRyJh5NJS\UO\w8V؍o"/Ss/zƆ7J]c@>WW~ƽV%b5+P>Tf";.a6 L3 !DZdzJ Ͷ)g~K<{6gz`S}"{gv=E$7ElRB .Is%?ْo3כ WzqF1k1i_o6msvKu~2w)w}b|f8/\3" l_%#N'/45mvQ_Cȩ '4;5Ficoh]ˣ04)kfCYب-{f͌MOCQp5fDɓ'S]^bExmƧ… z \\\2:4o<"}dzpeǍ![Kw:I Dsʃ~cW1)w_?hpo4sd_&0hhK]dgq*xgX?q Z[~f*ߣQ0Pc1g#P (sy`3zXA*UeD/h6Lw7֏+G˪N_\qLZGOY<zd'} =<#h0o m.f};6]T+Tw?53gǷ?ll};0i=HUUٵkWeqŇLɒ%3*ߣRJqر \ite+te ~G֝eg;bD~/j`sZSR*>P>+ӡ̀\eNu+:L{=2r< PqhQJ`$>=h0VO/?iveFr)?N.#){{j&N%>`Dk_ PBk܅::ZU#^UkRE U>AnWuj-sE"d<;Y#m'5'4 Dj6l,/>OV`JM꣛ IDATwE0~`0 sSx{>CO2?5#^k븳֥nԄi'h>ӿL(Ym-B)k[%|qӠ÷,?v;TϨ0 yu|@mhfWgڎ uۓZ3ٵ~gT4j%cWvDQ"s)yzǫibgF+<= rC徎[dN}A < vӃY7 %)<{|W.q9_]JuܿOi\ k^͵767 Q%'6&X%XVlz[lϏۿ!PxWxE[x3$߶:cNü m] *R\~TOAg̋gx:Rt.є4ʆ6}YrHf,I&xwWʚy3@cfZ~J&Ug =<`1}&^fY~ìd;Cj.ش㶱0"OT{Du)s%ۛmNE ΍~Uma[{RlO _QIC~V-@X \ q9WGs) 51x&4m71U̿2hJ(7o0kaV 4 o_噓+-1԰TȖSTs5ָLXX)5eTu:'ŇaÆuWVD^-@vIqu;3?;Qs~ 4Δm:3SZj/nj-ٺKq 1(UV7!w_c~Bk Wg~NߍTOep5IeG=M,"mRsjsH=/pR‚A(jjA&}zjmi34k9DmGkwG:[CeK[mԏ< { Ci?6/]Y<ۛԠDpgD|%*q P̧5+_,)w_Ƕ[ slnWj̓yK7gž{%S }tFd/P\ٰ.9QɄ0y2&((K|gruG({ʭg_g+{cii][bӴiS̙7/6kշo_޽* KK{l^yp =}ho_t^{AkoSs9Qr.o巡ΨH;燦zf [{2 x셉/wX齝g]1Nm |~ o$:"V/?;ăcۓ?q|m׻/`A-ܱ gGөc.-Jȡaocٰ+毱M](8p[<=ΡsFT*ּF4qW&FܶYp5N Bk턓e<Xdw /ojq*79N6q_ș7VYZZ_1i$9k*4ŵ+鸲btf4[arsjnIՇl' qbB+5@Yw Б 3inlN CqBZh໅)3sE /rfo36AoADUZ00ÉĚHZ M0MpK)@c)0zLTZYXX0uTvʸqR ~~~ 2V) /mEl% #Ma ʏW"hPyZV(o|At67r )Y"[L~{ilGv1Vf].;po]c5nEբӗD( FrhatUg!9.seVj.RPH*˨ SN,XŋxZ<ߗNn ]bfX)/ƠԠ5Т! %q|2(aooh٧)Zf Yņ輺*P qꚎEqYq/sZVcclЙ4Z2 >ޝi鸒C%1F~0(ܒ6%֣(-jkHv TUcL#) M010Lх6tV￧J**]jaEsܶrNcǠl˰ޔ6"B7Yv'lt' eR j@QQbjH3$q)ŋ9wK)RZC'תZ|! l,]_%W߃e6y}%8F¤/`^}-]~o eA3hn狿(T]=]Н^V7n( M#̏<ĐQ{+j٢lkLb~35oJJj Wm:VUFvL>0DZqwjZr6c;3bJaSu}.0MZ cA J~k0wK7ytakaVepOB"1J=M26xJ`O`kѢE︥l0mk/Bkl]΂ԆGѾS\`ݎ 4z6 mK-ylrS:\iǠ5q3QcH3;u=fnn|v)T3](_N<~eԯ=f1ؚzV<ϖ\׿bPPz6mUB,*t+Ҍv#9yz"eZŬqܗ,syPѻF|Io)ki-4>.pFάlx߀Ihhkkg'{tk35;2Q%9>4D9Q|_:Ơ<#dSvEŷ_}vjժByWQ(bbn]6'ۚuvWW8y"{׹"y@=8@ݺuKJ)jٳ'vvvv/^d1Oba_MϷήpMIJ!sV],dɒ=z4WO]EkZLh]!eKp% 6?帾nemT B|0i ® `/<;`;'֕˓}3 ̨jf9*i;ip6sq?{@nkgBbpw,!hJ; B52"`VPD¯$MVn$[V7Q&T+]Oc ѠOos3.?^Cm_m{%BuA_חSxb#oSp͂`>u[O76oaQ9Evk7Oo) *:5tWdDVL þ}hXN6XģRS/`%f=UXs~[VZML6p=qyw4F9R~Of΋EGY[>muV)@6n/vJp3Ip%B/n؎olk|7iDܼ|FGlk|7O)n_3iM&1kF645iF$۴w [#Gs{vwDP5wk|Y/N1q%ZN;Qi˾JmS|s'7S>ۆ_g]o-Ǭhb1v(\ !`^y~J7w"P=ڛEdo#J7xv1mE :`:S#-K5jlEJ.vM aN*UJ~Wfj2:HX(N|̣˟fl:7NrS,E~ L2h B#g'^ݸh^o<\5U\ؙZG9 ]q ߩK:ϮC Ĕk#hf6՞.犽6#SY9GSl dL,S<-; l5PQKocŀaȋ~vm9+44<9+.=ͼ陹*hJklݜh01IDATжhK عØ0m]z].rzrVv60iqaFʋ&#/qJ!M"nj}J1D4oe X(UaF{q7MevB4Eq `X-I-fkPT]D`-TJmIC/VAJ'EAZzU,L aec&Yu}癃jR + 2U6h{A\]g-}Qn-ּu`UG7볶~|X]'u_KNZ֦9yZH̶y]Sݨ]i[yuiDR]u%w>Crm>c:ݡۺ[TE5oioڗULjUZ~]Qz'_>[rާYyLZyק&TJ>="ۓ4}ܢ][e:mUccc=42^MWʤt}m*:Y{ĕXSEvWP![}|q]w K`LpX{w,sRIUf"Ӭkdx,8&l6ŗ:?xQ7d_zqVEWeiK|)tEXtCreation TimeSa 13 Mr 2021 14:14:52 CETmZ IDATxwtUnzOH.:z1 )JQ Dzґ&H MHRSHݙ$@,E=vg;{̀B!B!B!B!B!B!B!kEylO "!BdO[ ޾Bbٕ6B!E4L&SB;Ϝ;7)>>pвZ PhѢKNNd2iYO!/(z,--R]\HT,Y fȠ@ѢE?rw35\!/XEDF.W̑cl\dB׌i)ˑ:(x-\;w,B!x-<|V'_2 B!M4C>9+g5PRM&goB!^zz{'jWU5KcB!xjG/h0dPvB!^Pqѧ^mxۤf.7CxYBbyTZ$Q.WC[Duc(+Y xz(!ItuKR͠),fSt<5 BWL_+G*m-U9|@Z*,!3j᜔ AMh=΄&zո|[?z7MUʙ\>xZԦVd7A[b7t}-} =0]!-^{ӾW#?ZGsnJV@n1&>:YLLדTKIx`3M߶^#ȎA=SO"!2e"dA7z-cb4ߟv5a0wYCIDCh3{w B۾kىMEZ7+/'ݩP<-unv&/Nes.I/^9WO-cZvhD٠~)׋֢V %Vxoǀ}.J hx:huacDIBd^*n $]cqhd{#n(~\nO )ٓ BAÑ0 9M풵Dfw  pSH<⦪'(1,^3\u[ig/z=z@#N7 W(M{k:t]CgHt)OA=Ai|4#J[#J _g.s%,GwKXէz@-83A jD/bMvkۖNȴF*aG4.^J9kD<:2/f<yi%~xcK[O؇ Ң?ӭ)[5#Q\4l 6|Ԍ Z2Lhۍ h:GU*&U!2!HMxxx+; }>Ԩc= 0 jL"C⥻#Ҩ{du6I7g!H[4?$ 31]KF cU6ȅEђO@Ev(`Gh]uiFnͧXa&HxyG(a) Op^o[i7)yS^U {ڦYH%<*IیuT"p0FP뺡aמO}1]Z˂3FW΃6._QCpnV̛9sXz3o0(^CpCGEG$ܛ`U#R8oʈdȔ"C{~؆`V|$5bk$YbON4ԫL|ygbCj20~7P fwK޺)<3 *lMб`^iʦQ~ޙz M5aB^h*$$YXG;ȦoPM.m5P33g6:xԃvn2f]lgKƽꃗO v܄qwHem EG.Hػ>Kf3$ DVۧ{YY((Z 'eܝv;ND">DZdTΎcXy2&k\\KH84P̃FB t9kSsݢ}Ĩ 6ޡ]9twHtL {ʀɇ4%k$qr&ΧhF{L+KX, ag5g8wGet>Cfb0Z;hLJaڊ0XmH32la5$ɬ-SBt: >a񸺒;9"S}h{>ۉ'-'CB3%`ja-Qg%Hfp#EG-pK\ƍL)!Bdδ`i3u[8|~TR6uGӘGl~ô QjꀚiRӖE2m(g8 졕H+Y7O,f?&6zR,=-Y90#:%k`u|u9lϒIݨeg쑕~?!T0\n0Wu jGBg9 Mxgᅌӥ=Eپ}59_M@;?K!`26|} r!DˑsXݷts?eKZX~͢)cWlf׭1#v?Kg&a`j4ߓb0g'gioI$$.svYp7P">zX–O.?U ͟ñӿ~ Nʙl~x, {J`!omKBbɌe?b '_'4Qjf0^k*tnVUkҴh2`(֘.MkSV hO#|:2g AUS0/G ̭iX?k=j޴V0FY5k3Ū 1۷ACq͵AU)VMV>^J,}I>Z6vմ-Q->b!dg@.f-Nޝ)ב]tۘ/Yq{ ZX44;;QM_kT)X5 _Ƀ !^pՃ|1my4Yi^ E PlVIJL&cދ\^ u^H̩$__F^xyy啓J|7_d;JAE^A~슍W KlsQll-ioט z+GÃ> ۰OY`S+ ŽSiuoy Cq3hA#l<6Mx' 87W{O,\ʙtS߾Hr l*wSz@,uSls!0ŝF㿡Kݶ -C5 ݨϲ'fROiaXXw5@`ej*aD }X#w.pߚH`QWC%[V}aUa4:蛉4uZ"Ɣk߳=3:'4v PjNㆫh^C|67e8߻~ %[̝ >`hs?[I|Wo{S)?d-[YfLmS2A["f1[#ߦC6}z~hIzp\9 1Iand$@$!^|mCY^Vwy߇)Xu|(;t]204oގ~_?{CsD {oE_gDGvS UiM[6߬;_쀆–eO$`wjAGJ(٢wxG'5{*x֩HXއo; g{tF!^U%UiF~=^vSHi &CY^8d_!@A!@Ad ّ^vB,wwxBhw\~wB/M%r0c 5(_T9BAX[Y0}o,uU"[}ע8~%e7E!(Vn)Te(lѯB!^2GA!@A!@A!@A!@A!D %..ȗ!xDR ⹅( /)BF߸x" %P-..5k3%c߾}f)["##%HB\C(l!B7UB!K(<6 /%gʦ|q$~نZeJ Br(JV^jpufoGM*7GPLi&B8o|3VK>F)) _w1-%p0ϒ36XY`E20|1G#Lia[mo 4zo<,5fmD9ps-W1f%'/yK^/pڶdqG:b\毓8$╢r}dVƸ#qSDkDY,Un4aWc(K;؈pa6xrM3.3n[,{))DǏ2D]q8xB<+3 ɜ㎺|oФ]?һ .G1jwA5Z ?oΐ5b4 ᤀBzY s+:sQo\;+ʕu0'_NTX{>TΒ_0c\mEު6~$-|ͳy2hSN1o>g7qh % WR2;d@捦DE,9)kX*GTlRz8 ׇҩo|aE,Ҋz !ī.22CeyժUqss{-2W+Wêl| HȈma E5/=/2(㉢Sq?l wD.mGhzw~:BIhS@|6AgC~^{Tn97։,F?X" dTx#>z-~6'ɒ>YNfB|l^ P?]T_3%ފhFZ,m= ;;H&!єzR89nlq+AӂB\]]4-[i(i*RfŸiM'S9OƦP?meDָ)p?0III&n+RF-Ѵ{ix"Sb hTΒ s0:Lйۻ닣<!^# AAArwԗkrQ(% IDATbT@Ȭ4^"QL_6G$%&2P'?"LƧ:TOfy&͍^^^n4ܸizt˫YSZw=.'7b6\}dW5psr&v˦L\7VCPO ḭÅpZF6E;2>Vݹo[ Bj "(( f(\ PYSgiL&T4 ݨϲ'fRO51}ÿu:FnQON!0]CrZqo.xG3 FN._ʯwB9TǕ.Xde݆Ei]8+ N A/ *2Hx.Rf@o2 hh(X{LR(S3T)Y<^4ukH5~G8QP᧋m A!^3/}2zIt߉I-{ g]as$c?2(^E2*{cc<ύmeC:ևP.s6 =[9\ЮĆ[G3Cw~ZƛEѱoF]S?B@_ &%`> :ZS;5 qBO!pW)53αuc'3-ʍ߹-m:e_3yⰱv!S%v-6Q<㯤x_z-IM80/5G-hz|G,s2dZ;Wm#XE7u4w̕j?6-~C-u<[WdYSZ;1dopm.!x{ABdd$L8l2b@A#jFA^{P3r5a/+G#rH~>[҈OG۱Kٺ}:˗xw5_'Φ_P󌧙>l&"nczc|\N2sNbt4`gQ 6ucp{{2"U]M]N,('@UiL0!$H&L@xx9,CZNNA|0v~9ᫎ`Aq*DEj4E^Yzc;IS̪\S(XēNsiV@If]>8FXV XIVT376Wc$?ܺ~ S(f @hop ogw;}&*ʮ{io +?oH<,]ރ5z1S(-yג\JGԒ#8~YӫDEKp|~M[!$ߎ_󑟞[ZFf1jP*@GB,)\<˧dܟq1{Y*UJ*/$`@AۇV\sq4^ݛ& ?_e iމzXVu{6o\„~5ɭO7h1|/MGhwۓRHvEJ~0[3eftΜ 'E+KimK.q{$\!fOR-b&Bd{zwDNwKh?}I K)X܊1C*ae @#SfIQ j@.i <5zb>kԬIdLu.ꈯT5Mdƌ#c[v9~|qsʿ'vu`KhȘI愋%E{ n|̬|&Wp6Q J#Mσ+Eji80&s;*3.~*N΁nsMt rvSVC΢E')m!-ڋG0,g B2&q7IMV+l|Sd-b#f]k r5QU&3Z7h<;N])|k5딥 \IQ ;q;{D@k"O87^<Ήg*(>HNq&6C#ʓ ///geohr[d;#[ZL1ࠠ1W~1&C0С9O&&:!]Q\6% =mCT ;أ@^C ]?eƩT ;t88CLiŻ?gY]~J<1. r]v!^KW@ϟXUZ5mjc>j2N-L`oߦ>1/t1A^Rih#&lj>28rC<-F&2lU JӻrYU67cPq UPRH1iv(uq븎.tZl41*E}:n!xj; Fx"VvgHa7 )JIN(ǦK[uFͼ9W)AU3\x Zɩnd8ZwH]QK%ys$.?͟ԇ`IFsS$N?`(Xvw=$)Ӣ/pxO\4%SX}u84>-Pū # "= spz??!˄fkJ\\ks%777(XYʗ@AlߟҗغSǗ6dGY=(x' V ^S*V}Ųhܯ-fҚ뚵x"<=[3O9"Vfj3xdP&883ڐIi&8x7 R#p({C7~H+lЙ*ڳ8F!-_wb^Gl<(p0BWiL0!$H&L@xx9LC*q$0!?8a)C/aeR'3u:@h/Dǹ;h^?)'3-|v2z{LGV=N.UM44[G39WSvuK~SXyCza<5Ug5$EZasX%Q+#+~B`P`Չ_VBT.HЈ Zؒl;NM$e:3\&$$77!fzP PReTCPLX̦y9U/F?70]-̉w\©DBVi=h:;'q=K{{H"!1uA 5&Z B63bVJ`!o3c!VQfOދui,5 {0mǔ4)"1^J*eR (9 # 56&Pom_lhYuk,w y|0pذk~Jjyn4CqZxZt,#fZ~Vu:PBGҭh26ej )Aݛ& ?_e iމYlv֣6MKoCnqU6𾬚0SHι}4d\T 0t&;Ы#Muz[Es1ֵFhh>vvrרSjJe] Q !ԫ8tłjާ6 $p9M/Ҕ+f` \7ЖOg= @Y\OI84un"BuFT.B)ʧډq[b,#J%j֦F>Tr0ooa EмHڲKw-;>zsr|Sht15tDjV$jk:@S/d.\g6{/>5rf\ElƥOّrnD[OR~gѢ趐zuE#i>9!7܁NΜh z;!㌉qMRS6vX>) ni.M Q7nd[p_gN'X۪d75,ta@ PM:ҵ[ZTɅuf>EEpu4FO^/nd(@FZ$*a'NpUW;0JTf٩c 3rbBܹk~#łsD񴌐BݗOqllȑ;9i>Brb>NWv reOr=:'4v PjNㆫhm_\ :0-J≠_'߫ߛ(=sFSqZC>ߑvx+˲#:sg1 v֯9el0` {pLSֱ _-dg>ؖ^/@AY9c[FN~ݜKstD*`]r(/\m1O wH72;C ^)Aޛl8RZil_r uWloQ97J7n%r?ws3Lå'V4onx;t={O}!dvzؒ׿#fT3m7-6%&$~O#wN^?p3m,գãtir8R߾{q (E7O߳׋Ɣ- ħWx(*||9ZfnO˕n2uGufS Aq%qt/Ĉ&y٣6Pd!pR]*RXgڽ`m8 X{n? )R 6C5Pb.\6%p3q +|/-è?~⯞#`ީnMpSk2>f]b͊= [<,QgvTer#3ngOK8<Ꙇ ^5fLC11d4ru !{,*{`M8w(%'f9:^ p0[!G,v罦YUwTWRe4 {{bbKl5XhL4wc`I>+4Ehxsݹse;ޙŧ|sލVXԒ[5L;E6 Jֹ0F6uK.mwL \ Z %CпRl jJ]aws2t7tX89ZG6rJR2Ng?/GtZ7;stC&nç^٠URYj}[:߷X IDATǷI]|?{7QۏVڏqHl^k7*7%يkq?HS\*ʕx5;H9鉢(ϒ//g%3#3ZsCB|vI-: !D6@͚53, ڵkիWIPl߾΃N&B|`ҚBQt:]V 2;}vBFIZFcmmmXGA3Vɓ': !D6t Zm|T /zcee'dF) '444CBd:VG{u"""RVm'{BU޴!BK!BK!BK!BK!BK!BKnڵkDDDs=3$I ٵkh4(Q"CBl͛$YDAjԨa!Demmͮ] RQL"X!BK!BK̑sB!`GWnſz6[;WH BPNHxȑ}D9:P:D PMy$"=ʊv5|75ҭNk*GHKbXǟͧr]JR?9)%8SD1AQ8uXž{ؾq&npTSWYh8|ٳn:ʴ ƴ(% YRwl<51p Wy3(OC 7]'NҌ:zTĸxHu a _L9Eqzfvvδ|bwx9떤{xO"2Ѓ*+ʖlŪiٗYkfqV?KêS ;[\ա璓g:t:.=ze)ũk1' C`R$E49s\yw }sPH'*r+oTݘAfo?)s E*RRoq'[MEf3Ŋ٣?r8Nd᧿xr9NZ8֪"TTm5n|):FT(8+R+7*+^=:Ѩ~c>/ÕHwZOQiliΎ"xsTf4az4RcDWOq%fZsa9J&Qki3E~o>Nv}Hʶ4e?v'(m WkZ%{ICOO9s<@7؁Yz _YTXJBqOq4~0غc^ 3\e׷)o$J8QHB}ʝ Y2hE9Qh_tMr]ʖ)荷7 8bEv|Ԩ\1S~w T,%1 ͇es@%¡\yްm2)Kq8];]%rbllW*5鵓(< #,,p"ƿ~T*S"u sdCkĺCрBĩ\Vķٛ(j\JAȥkbWe4 p}ӏ?:9{~Qqɇ !7& S{'7Tn͸tqr> Ѵrq\]p/ޛ-azbc301%#򻻠 &DV?9(S ; #^h^-׏L@oQr xx#4Ǐ'-2Tbt:k<"#9՗q?xطzQu[Q+_]ZՊbD˩Clj+KiKf_ǟ둥s~OY fh؜?@'Q:K>Vm̔]b}Y5hmq˙C i;2FNbU5W,azaxu\Z@ѣHPaWaڳSFiI+ɬ,]䌺,S+ɸCؿq:Oow16JQk~ھ9'LC%/%g#8ʟPK9% WW7kKW{iIW,S4K .-2zFx*G:0 NMn !/& .^iD!W Vy͕ݿXCL=K~u[-Grd^n<( ޾Ec~5Ձ9ނ<=y!D6EǶ3Ytsgua4v/9*@Ɯc]s%Q(QiPЪzȠΝ;iѢEV!Z@@5kpyN ~]9B!D$QB!D$QB!D$QLeuBXδZ-'O0":qZ ugÃ@vEhhhV#يNCa%QPP!YKB.IB.IB.IB.IB.Ad@"## P"[ɝ;7Z777/xgT*Uա!Dt)_{-xgTZEQ:!ȖJ(} R$ ❅H YPC(L!B7]"K<|!Q& ];#ƍKs8\%_5*G(M*?%QBQ ' 668<01.!(QxB@wz& /pa#x̉AFi9$\o^weM%t! = ⽉ҳs#n".Z*ЄB]+X>&e=qrp$* P\dTrɓMLחy}_y鵘a2Cܣɣ݊X3K*`BnWȑS/@עoatrj9s8 !67ϫIg=V鄽'чrri?ϋ%Zۼ*ϐ-wIH>Ky :Zs*D^8O{&+VXG'{EY̖-[]PSB浥T;Q}{ (G@5P/ϗMt4B4{OQw Z8pƎ+ͭ\f|P'>&mOHW9 ͛YGڳ͡ cO VKFal=f(4YH\.,<S},U\ݜzcG &s*/w@ s0&mJ;2x-N7cS{"=(֭[_Zװagߧ(DfS8vG&GUqǗ0k-W~ϨPUR%sjZ*.j=4ZT1y/G!^!#1ݶ9S tj,4\N|l讇]%7k?LeCXMAGGe(TϬ}X"3s .Lac2bᣤd.f:g[qTer)l9`@QLh:iT3({)9'LM}SI%QBdKBÆ '9nE*>t %曘$ VFzGLL֨t9rftXeOV?K4 PTdä`U*;F]ę\8Zotx.|kML/{{kkJzwyoɓ'qAI;/B7%K~"s;!zWzB!6MI B|`J{ lllk,C6Lb3gu(W])ggg][2(U4uM*_5lB!RQ%CL”)SxK Lz};8q_oRŭ(GC59/J|4O8{MsҬF؊v!"V1!2L:BBB:u#06!'/GZ̻&>S$vX}Cm4hp"R>nX/\8bYڹ)!]؏F33ƃJ7(ei㈅Ś1% KUskXoB|$^IH9i<$$$͞C9 SYˎ>YG&RDOϦѻ_6|-b]U{f2ZiI[G0y rWEA bS]!6g4͙ICD!>z +WM̕+Wȝ;c=lU[;'*O{GA2/q#֢iRYيyGq5oHfc*t'G,1@B<ھQjАr3't1?;˺>+c I \v.4XhUJ2~M᩵ cu bՄԝ#S y7QɇږjQ%EO((AlExj*Gf?Ye似FB0r7J2>?'opƹrU qB\VBt,mUƱ~[5jV)I\wDOs}VJܟV"52q drB^1#GAe >9}+D=~)&*HWըUʳB}@!hCڎLXUGi7=ȗ*e26c3RǪ(,`}Oin\2|GC:JZ3aÊE,W9ʜxtĻ/#(Ru6 dO ;[G;4 }}ꍱwȍj'W 1ky/;'PU+3G@HXh*!uUi]yIDATbyp(U^-`M1Xsx jT9GTQ/qH 1ys,Q3r]_ wjvX1§CfxqG{Sk䟄ku Ӧ1F_w^2JM.|>[V,%޺wFEqqQ!|똱cjQ9ŮT/#0-[ ƳsCvGB]UgLٜzե|n|`TmgD!Zr믿믿Yb9 Ɣ =?"fKٱ]htߘ?˕%wbAU}S7n5<sAzZ&+0Yyw,2>_mSNѬ? c~mӡ/dL<ߌF<}P)cF/buik7Ο+ j_f8qwEZ s;Ɋ{єJfwӞƀ g4rabj n`Xꉉg'5Ec0L-U 1LJ<ۻ6!xgɉJ5]Y9'.bٰycG9Z͜^vaʣs?,S<6n 6nM`b4+ARyРƱLY]*G.ce/ bd[VSRaKl0e\ ȃ(5Z4ِ$ UUceDD(9qcPTs4M9q.[X߳tp-3t^^Oy|[fØ ӠCQKZ ` #GnWJB/4,`Zr86gZKZ|0Œ_ ԏeZ}˯3b%6'6N`[aRV3ڻ/Y)C PYd*[3ZsCxNHШakbv'Oq}%23u89,ߝ;wҢELT!j׮)joN͚53N ~]D&<( srX#SB# ÙO#FZ4h:CN~o'Jx kiWԋeWn7Bl-ӧ0?! j5pIJc2: = iZNGhk+DaI*[cnA98p9)aJzi14w-0 ((<`MƁ'/Ȣ1?p3^OHP0 Bo*-FLM1=] -Alр[Mi;o#Dsxu?l9ye--)Y&r<6Fe)3Xx*B1!C{_ y]C|vҫNq\pp)DN˹͕_Ҹv*C'PcXu;agk:\r`rw y!g⟕?&ix:;ߧ]!Xl_vj˔+0f @ w#IM:V1}=u _?UJQtĥ#WW~Y>$fY{B,Qycloo|;=ɗqY:s9ۍD!33zu]3&?E $3`lɎy|٦jH\BOcӟy|V)az4RcDWOlĔ|Ѩ ܵq%j8"}M?^e(]817Gri1!E/2w\˺c8{"o5Pޯ&>S'5۬|b/!D6g'3&(XR ,ʜͅXOZGi "B'KzJ3g̃`܍Rf@=z@>U*U|1-*|(, +7#JS8Sl>nOQ ߢޘMsWRwhN*@"߸G;`2YhcмTv۝o EMxMj;q<?Lz2W}3Lа84XXբ瞜XOz6Z M^ ,0=8˅(8oǚx}޴ ]'h?g%θxgRX׾uMd͑;D'Ǣ~4\/W܋fKXb8{J9s*.sB41).GoxJƒIBcA?g~1tg*d+ru8JB&az"9'j(I?-sB1z,9 rBD ((\ԛQLR͋19(5g7ÆXدsc#IUG;/`D_gԃ i;2FNbU5W,aza@OB_q"EsW4u9ܸh27t_R?{] ҃KbC1SdZ+K{BaxQGfU`ǁ[nЋj@YNs'BFnE( ]ը\5|lOKX7@eN>a/Ɩߵ03ߘ~=QHA /JޜɁ+?ʥM>4?w35&io%^l%|8νnUڻ`~ (#'Z!zX,Gل28 .`Pa3'D&& X֡g4׃f_ҥ; aB.p*[G8#X4vPH^,;F*Ɋ-_V1̽h.ʰΣTWuv|x*'Q;ҴO[浛E@^h\uNTs{ޔƳ͢Ό1J~ ˖|*rL!D>s gCjǺ|5nB*rZ'>D*#Q OBIa[n\Noc%,N6F-ah%O]`ۂy#hUjd!LѨr<]?ehc;-汅R[TX՘?:0vZFtN/?UH56=-J~Ld([~/q-&nMaBa`zȠ|04Uzlevڹs'-Z0"[ f͚.|ï+g#?J_l\Txtf,J)*IBl.' Wٳn{(JS!*}o%7B!Ӳ}: !$S;tYBd{.DA3Vɓ': !ȶN8V5H~A;ٵkYBd+:VADAd C}@Bd-zB!D$QB!D$QB!D$QB!D$QB!D$QB!D$QB!D9 B$$$$C(ΝV+y9~{VݞN:zV6H z !G(00JEɒ%:ʩSpwwOsNoZVxa}Fn8LNc5k6:8rң o!22U(JVQ)QKw{Y^ۢ.a%QB"I[zP伾7 3T#B7iО^ MD8Fq"Wrضo^J27;oë9=p;yr9Z>(hiJљoN!0 %.GOSzy:l;"/PgQEQ2=QIeI<T*{&CaUt.і1Նa!$ B^ՠ)qq\ЇC=A+}&}\ɂ"z z{m+_}&' q OŸF݊GVZkiJ ן QB|1_$qfkG޺]Þ5[2fϤIȿ1v?o$ B^u?*oR/rO-2ksbs۷)'L Tby OɌbUV.CIZ$`噵\zx-u}QsV_/@Zl(WS\qr'GS*?}w>5o㎭wG\ʹK^Rd:IBnFIxmU-eW^s-ªzg) zB'ѨMJbkMW [Ca>,WL[֞ %ܚ:7_9='ؗc׏)Vd`jY`vJ4w0}~'dm5VwmȐey g1&/"BW]5GH= w.ű[G\}Ep֑{W`%={̞?Ф \E!(;](d8[n&֡o#R+֖XTSrFضעC9ɥ |ռ9Ve ?!Hl3G zݲhCt.&brgQ@R=ќޝɧ{>~׋iЪC w.ɔQI=,xz?r{WpLfB/^wl}ūg䔗޹Xo_yu2c>~NZThH>MHW' ̬e-äO`{OРo_lݞߍewjpo#=tdaLPv0eZ8]r%zwy|/ЪH16>̽su.ɚVKr݉Iۻ=5W0~,_8N J:a>QJs,ܖї}BZ߀DA!ҫt_R4 ?9JeWp?6ΊJ8ۿhĚLػ9dZ!0cG{boz-qByֳϨM>)q1Dxxhy"l~9ebY_6nv;o"q2c| ȘǴX :cePJp Ww ,:6SIg.*IPiZ'cRMxlx~rmx,J6_I L) r HBxgOO狇3ԉT*29 y'@PˇOBh~lAiƠREe+O~c$G$'o.4k[%!bV3l]*Ax0]ң  ]f6遗IQDA! ax?b3T·,CzMeSiAٳX !Z{עTlVFbaÿepD$`ne[fXԂ8jA v^MW"έXIcpFG:膴)kJeG;̮A 0{̾BR CopΉ?xIe\nPr_n,Ҁ YckT$&$?GAq]Z1}EKi<;TL2:bhh˩BdS;wnݺ~$ǃ ]1RRiеoW}Pj5۷of͚i/7v3{F4%>sqL?:"CJօZ0 <iM̱M3Nw L! uatma+O'N;;RYZc =p)Za- 6gӕ_8t'?8X8\^q׹g8 =!Gh++ז {ې>Xfrt6N݇oۻɌB2ԃu1|LG]\iɪGA!>0:^Jz<HVt)bC|yM!x.6_!x Z/흡[z.]V棣(lͬf@]4L뙵,>y ׮]#"""CC"Frs8ϥ'=Eq+bO^kz(FeIBl()tEXtCreation TimeMo 29 Nov 2021 10:56:06 CET} IDATxw|Mf{՞Rjkf*jWEQUjըU;YEkK" !F|߯=s~<\B!B!B!B!B!B!B!B!B!xc^# v!"B!"EOٹt{9996 :B!ubDGݽ{ĩScem/5 |%c18, JY=!Bt lmmӮ\3g&'2} ۻmۺx4M{B!I4㉉ՙ<<;88؅")r;Υ}>}t !BRܾsGٹ$ =ɧo|>{CB!o(yv|Y`4O!BR јpf}kO;b~B!ěJ4\&MӬB!oMӜFvʐB!ěAd0$~lhB=šmyo&q8]k޺u7îb ߶cr| !F$l5V3~=ϱf[$Fr۶zfGKT׌-%=>fqzr;<ė9y|fAʶ-xN񏾨4bvƵp0i>՛qwfyx&P~ $BX™m8_9~⇏m.c Ӡrڿ$^N4f]sNә>Z-_O?I`L[ 6Ыpf]t2Om YۏGd09a[Y0iG[wuT/rڣeYo̎)F>qYcu>Nv<΅~):>#CzcrNu,̔gnZv]6p12i<Zt+g2C1[$$2q8wfy&iѸ2j$B7zj Gh"nɘ =$>Bq`~Ԣqd0Ff.sunr #?Ȍ cE|%S'dcooKM[Oqc-Z oӬU;-Iq9: ӮSgwh0f-FQoRCdс"^~U|H[0`4JE~`e<{֧^L8{Sru<{nc|0~@rԟՙAUu!x@z>VPG7v7.CoC ˎXΞ❆ѣNQqr6`yjWm=Ɠ2,Y~1Og֢լZXBԧwkUtz+K$Ɍٺ;cWesSa?o^=dzTSn3^f*[N3aO|CQ;,@V9LCs΀P=C%b ,kͮ>%>rK+wɮLYKw\$Ͳ\% ͣwtsF~.x HRݏF(ak<8=O=ēB+뙷dRIr) 1|ou.HKYJ#bh'uۖ<^ұ$]z>VasFNEwLcWbtC迧8xUaX{o8B![HPЫC!B<<B!DS!BI>B!DS!BI>B!DS!BI>B!DS!B^ElٲUT+B!RQ@@@JO B!+bŊWR !BT#ɧB!H5| !BT#ɧB!H5| !BT#ɧB!H5| !BT#ɧB!H5| !BT~H '444C內'~~~ixAHHHZ"ޘLt6I)b֡Zp'NPBix c6\rZ"0Nm$pVa3Ll߾=*UR*CoŋsδCO r$^VHH$IS9$B!I>śLvB!FO!xk/gaixR*;{Is#*YVˤhik| !J`QLtdJKIyfW|hNqzX<z?Ư#'tt,A:H>X_%g|eGL6Ţأ=0lqϔwu7|QYNO^\l F윽_,̽kG5z[LYؐncp&*953aoL\7+*6#D<<\Y,YDf+5{ejdsPy>]/]V~B59>T^RtT6F;呣x^z#UUv/geg4*vjMUAy^M㿨uK&G[Un7Mf,K,QQԷWէ˖OWs•RJl3϶&)6sQ}Ծx2TMwajŠNTRʢ.Ll훨%1IVݮe3*϶URJzQۮQqعCm&HNC0o)U2SߕV6vuԬ0-zƬN(l[IC Q?ղS6eGT\c>O}`VDֵֹZz7xuc'*m*lVjɇYsAjT:fS.&=?@UԼo \._/^ɖymɒ%V~ (oMR37bx.iGigOn% }_7c&4ԳG 3aJz- 3Gnvw ?9kfŭ+tj*FZSZb zm 2v?Sff˱,${s[`8VF[тa!vl~=ܐ"#Z@1vy{.Pf<<թ3?wI\ZZ63?dȗ7OԹ7&vdZu6$9ZunZȝ'cLnpbuޛɗR7W2uF=ZQIw>YIn$˞4w|Mcڅlu7IoeE~&Xq3}Ƿt(yڍaLjց1O(GaTMCQeؿכ~U=2lK~DZh\Ud'h,*a]%l2ٺj/ǰhz4fm_M|'߬!Cv{rBV.ǧltB=ú|7#m'`ܞGکL&R^%-A:vOT36z| 6%)ؽy \RJ9P>#Ąhߨ&v@Ec, 3ӸӳFК5sPS/oXofndJC|9O]ejMGfA)~e@ ^$9 =., %{ o߸ 偿ooe}t_A,Ft3P8oE5 MZBOS6KTnG!2 |/V}jHnIql$ dž&d 3)V_K>;U)'͛lζ"k25_빗9}aPk d,^j\8ZL ehҶ>@96`玿T)`@Jւ(dŔz3!z,+ I63aA2tȬ`P4NnOk8Ă[Q0l]jTt~$ƔL;AP-RŽ{sߝn;&ӱxbcΰ9`9Tٗ[.J)֬Yg@6әc?o@ aW"/Y=)oiD&?ȐD !Kz xNXt3 v '' 'qwpeHAG~S3j{>4 AAA=-WW82jAAG9;+t"ێTn6(/C6rQ}kj"4~_L:2Xz+gm-{vK'sZ9Vk-IaIz<:cX=ܻEx%N|sïukJ(к—2s}8 0?F=TfdDrϚч^Ajf~NNCv|قł%1`ѡH͔xϧΔ\+\!:U ö)_ta7|Oqd{RZݺuɗ/{m=|&йUcn8/Ȕ3u(ws7pbdw/A\pG.x[NKm3cC&/x i^Y_({,(=}[vr[z2@/,X?6SV$;3Qfi\sx?V̄v,*;4f6,[+Cq{`:Li^f4`RA츝 Y˸U~<ġC]fl`].ܴ0r;G*py\=۲4n t0*ٹ|\w`$b׎ vAB|z IDATY,/PF%Ϛ"/JF6oS0N\$O޼M\&{Fjz^kAϩ}G2p/ctGIFiNoK)2p`nie]xqwe&9-41 Wq\=]6¸;'Jv#ztm ޾?1{NZ0I{j!ܵ] 4^}i:O=?瓂t.Ϊ!# {=8]^_slg]@7y'B+\$G9>Aƍ(ixD}ɎU:g:|J`\AFWeXu-{}0mK;suD6Pk'80dzoBmI /0g%/)tGr;Lp5p<ٔmOr ߳97>}?Ot8W7߳Et|#|=k2=[Ma<%տFWBdsz$Q%6&wYĵzܳ|V'zLYsbML@lqDl`q@6}*+> R6DU']~u:]W}c];'a畍lne`œJPm*tC&=ܝ*;='|avdԆs#ҷXק6c3]ޘ V: vxf!ɪt;.Mа{b8:j;ԉY -*gOg^4E/q+ֈ[{6|Ӊ\zKs1g?yI ~c)?e99+G6b^O} F/zC?׸l.ǗcL's|`.~=}ְs0&}oct%CR,-^!g&wN|)o]X.2_j2z&JM+G0btN,Bl)"9 dȴi:,\iC]w0qg̻vY W! mc>:-X÷0mlgfbcAGQCHkG3ʇﶶ-[РA I+VиqC7ҥK/eŊԪU E' z 6X6Xyzo"B3i~xss*]p$B!L)B3)^rNI)JxcI,OOO4MCɼObz=֡H)СC,Y2Cvtדdɓ,X0<G4MԩSLEO񐟟'N`턆u8B/C… sq KpD…:I>ţ *!![H"i.B!R$B!"H)B!R$B!"H)B!R.LxxTZ5g2ؾ}{Z!^SO,[nQF/TСCXO r$^VDDUTI0k@)B(Q;wh'7 ! W)=_)BzўBO!BPrg//j2.L9OHg9ʡ}G$gR%$$#G2rHBBBx?H=uʅ-6669᛻}:Ug\qbB)qkהq3~#qh\}4-BfW{3PzG| n\ c>'gzt:š>^& feuyyPai,NFmLٟ{ʙ$eKLf6 9 bF69hCN?s,E!Rwq,DKPg>ί#&ttZG"%44QFFXXFJ7>SvMb v;ٶa)?jBƠq4ܜ^Q5 fTMw~GKۘh*~eY3n-[~lVYvoìeZsOu5aOSwnsGOȑ# ˾rHNV(Eәl?13ra|81uMjMAn;J|C8le-&-cM0D/%"v9r5~@ε[*Rr!W{y+xgXX؃’M͜O[.J+K6=P&P/3f&[a `@Ve*9c~ӌd:|ƥU_u*X[2 g5qq_Ȏ㗈gEEo zFμ-Gi3dzӬ49?SwKʬtsMȢ&8Xnľ #p_ON8l+ܺpXG0ou2$0y9^;;`{ EThQg7!n{xXL&3u] .t4qkO, i}̢y~9#X0f˓̵v?fqi2}߇gbksl-n,{Ah בpyJsb2vYMǷf@G-#ױHMft 8FO2v@>LsNabշ/v4g_9~ZG$Zb2y*T:gΜ35zt-w7WDGǠl>uŎI=NZw<$xާY0װr~^˖г ȭ_~>71,Ԉ\EԍUtшdzv"YrqqvNDX{57/ņ-_oƵIg,ƭ)—VŌo5_Ҏ F#XfFuATx*.n;k?~ULLI'՞OrawaNj&dDEDb6x[8Y'8vƜlMZ B-Kܵī8bb=1Rns$G8q$m]Сqm2CTVoncTȞ:#镈d3-}[qRQdtg_dd-M ȷ`k70aez q- {&^q{5g3'7cqi@onPDuocغ&-Ed=>4؃O,kۨHؽ擌m\W+{d̦|9I5֬6n2tZJ;cmbG(cBz_zt  Ixxy&l/=.q-8GH?0`yIoGk<?ͼI̹Yɢ[ɭ#D!s/k8(|Z '"n֗O=q,2ӽ ;U%B+ȝ̮d#_{$Bs8AI<tѬ ZŞ=y}7"`L1l^ Nus)}9Kً(O-UVIϋGmV1=+4R?"V'zeÖ >Cۀ ]Z͸/ѡ܏ͥƵ\*ѨM]*3uV՗=zU(ԣ GClʬSM3/eѰnlY#dG…c 1BOq=\TEfۘ߯lg;\*L38dE_L1 d+Ww+vO燃&ڮ!5VIc _Mީݕ/N(GgTy KLip:4E'EӦW ԚbSlG&^8kƿ[y)Q9B)Y?/II&AqBz3ztz0?}R(1#em<=5A4bI"Kr`jg遍r ggQ`j8b:<QV rU_nYGim :JtYӹv% ;-Żߡ|zz'2n)gRzS1o;BO?o'%-s35T؃ӧwZO=?f\Q4PѷM%;Fse> J)٬w{&?fn"TK2R8.L~D}T O ^x0>=:Ƙ64vOŇp-U;Z7/}zt)D-ij@jpu 4mI>m_)u``u~)# nYu_'-1e,GM)"αa,,HFX\?usЦ0c315& ~iQMXo/z,E>o;]s2C#VNH]kuUwѭE'vF|KQ1|?9d)/[ guLpovnkR },y[ѳx>ךo{QQ~pi+ɝ"%ؕyl̞5j1Pcs g b-s[v-w{!423"-^̖ElzMɤz2A[B%o4zR!Drϵkײvdש[.uMͰ+]&'K ]TνҪDu A&t\PonOtx72qrRCJMJmEƟAqܾ̚-=Y]F cu4ѱ.#DSX;(RULö& USuAܘ~f?3Z|٣UP:uoc@(f^]9ZQ:ˤf|]zK{B~.i< Йӻ[C<N"^ntm/BX!1|<M(],Yf[Ye+9<ĞR#qV%wTXYfɒ%J2iC}|ATfJ82]S;ƶVr{O}>|PmupbcU]7[Q&l W憧B|fe<~UO FeQ+U[ ٬Ci erQF_U^ħ4 Gv*O=*.uNg-YDݾ}ee˖e˖jOB9ڒ%KPޚ~ửTlB ]y}/]RI8>*}?Pú?+hܸ+QҥKߚvP,i5.]ʻ뉽xz6l`u۴b ãyawRK ғLSFgu)B׋J憣:u<^K |GU'Ozc oZ!B|:!y/;Ch.~]#СCi^L&*:E4E$=???N8 Mpx-yyy鉟_Z"^Sm; IpD:yfd2QPWu$H'B-o2vB!FO!Bj$B!FO!Bj$B!Fv&<<\$KG- !ijI)b֡Zp'N列LDD|8]\K)v!R|ˊRJ/BXxܹ3ÐS!HOBBB$Lz!ɧBH)d%!BjS!Eebk;+٤u4o)Mӥu@:Zјbn^6W1J~KNqkהq3~# +Ejs Vȴ<\Zxxzm쳷m+{,o09⮰kFZV'F` anτ=ny篜IRVjvt!#7?܎Čmrє*~XK:!^],v<Ԡdu Ѭ/wױ @B),bI<`F~w~}3P!~Oxm"{+l ڸ;.܆\mpwo>ItDcWs&/[Ϛ9(wkc[õ/U)Qoo?\?׏FT{WS[PMg^#oAj 3[gOˑ/_ĸ865MGa~P"(:9‘#G8rx ?MwC5ve7u",}|nkY?ofR݊UiV./88{h yEqqw(H{îb/b%j4{M4{T-PFMTv"((V IDAT̼wޙ4$ztC}wz5+w,LA/u/JCѧYܻ! [alhefm՛"Dɩ-[+7d=s_>ߣ:Ps1 kPC@"zg/#!0sw6I\R Vy[8q#-),,xǶ,@!v'2#RLn3/OIRQRK͞L'W'x;Idzfj ͊ƌ(3 7@uQXW{Wg{x;-zZ^{f~ﵩ*cCPBvP=Yğ)Y|[ood~5~-O=3,%`9,3U A{tdb-mBf-9t}:ک*oufDI1'1ݫY ;Է~e4XC҉)g\q؋k/]T]{Xs+G~9O+u}>, ҷ)<)es˙%ҿU0(F}sR:y([áDGGsĉlWRKK/h,h0qMC 3;(aVyBV:Y)Š{,pe?{ 5W&Nc3Ak~@9=8 f:]Ͻl!/f*3'wC}5~-cUBr7{5# .KAo /hdO#T;{3AGl8ˣ/"ʂMYܬT*S6w),wF"11 GG rw?ӱ\!"g(;q~gw3H)8s&펑'ό?zR:~4/.j>F,H~W)lQ$a4"ƹ?_ECI!Gb(G8pڌfX'\`БtB==)HD?k0y\DBehf@ 1U`ʿi<VO"l? >9ldչwէmԢhxOʴAˢ JmN9&u(Jήn4q0"oFhke<E1Pfbaa$I7}2~vGv7YnPݷeЭ/{P_?mkguO ,$^j"I˿1ęQݻ"rZOV98QXOD$r/x[[MY֣/hŹ#H*7:V߅"61lu^OCv 1) eqV F n,Ny( mS31lP #D4C5>Hn+ҢR?bvSWrQlŪVwN~B_kjfVMu]39 &NTEY-mЕԠݭ|3=),f+}QKIӌ noh ^=fMnD2/Zt S16>f}Y;RJVEy2.han|M|f>&…~+r'G:ݎ[6t :b޲,3@Ws27}&G8/?rH#3 n; 8)KٲӃ"&٩ Q-Υ \:ֶH@%((I&9y9'+BDN+C!k-tnEqXg`4 Se]?AOO="9H*_,ʎ#4<v/wLڸ>f ÖXAЍ({QYꍁV1*WChXT*DD"7>MW3rO+d0)Zn f[6#%lP0 ]16vy塑 RXMTEr 1$TB.bhh?k"T<o?WRgnݽ*Gj0^Q;n|9Uk{* XRNi7r!V},>q]SiaN)Yo$=~hmJ˕ ^veϚN|J<=< :I}4'fP#[A>^ p؉xyn'IWvX+qDo,JɁ%K;`DW]zL-nW~ R1jh6v|9<)Y9BHNJ~3yFŀXm+Raђ&DZVD_ 'Z۞⅔ n #@Ow'W޸B5<ClDġ\Js}|ZtZF@DI4;'^y,+m kh`*9Hק:K >Ƀ;W/_[0b z^FCΫR$7F+Fl S'$ '''xq~.F{9b9)9Д{J2XZZr9ʖ-ۦiΞ=,XZZ"" >e> Q57S&WWW._̡Cmsdd$VVVXZZۦQ,,,r ...j(caaۦS&3nnnm7;.]"$$6G+ sY|h%Kmdd>eddddddddbSFFFFFFFF!O/H&aaaۦXXXȯ푑jŧL:aaaT*r ?˗/˯2M fS&XW1%B2e8zhn!##2 M fS&HYxj(rCFA|.4%!OLNFFF&w2MfSF&s+)eʔL|,ŧ|1'*[3|3f oGrޫ̳g~IۛF0fA~aLBv2$HĦ9f6kN6һ055}ňھuq3F_߄ BELۼ$ncp223N0mt$铔'ƞflK&uƼñmNϗ這bPp)s ޼YGKS:>UD* HE}vy4}92?zFs*whVzR63=7 {?$OjF_De* dG@}ۏqnRKnHu_q"A48\o9e>;-4 "O##Ht͟rho +wB:Vc֕ē|Ӿ'W2_ c!X‘p5PwI8~j]JPBQt?y,:xs8wg2 hxɾAۚb6Eܨ֬?ODhQ^q@ѷɉ6rvn8P%,?#r_3L(_צqA,6o)V AHX;&ýA_VC6Ҡ91r㹜 u>%z~̑.Ys#s mS#;" Ie g`"n|p博:S0ͼ;*Mn77p@TtԊ"e6~۳MQQQ,SWJ<;NwhOҏ{M+hcm j\NT{x^ċ<C--tcIR,V zXNSED̯M0$F\Z}N椣pBKK CUDi̝͆=X:%Bfʫ +|X7JTO( sgCy-<gQ:vQnے먉w=30wofŤ@,P5a}Lolg(Gi -7:1FZ$euztۤwz6N.6hk*Lա*'OǤ$m@3IiԷOq 5jE"'7b,sm3y|&߻Pe)~$ gLI*2M8c׉ڰ8x&<:lݴ>ט?^SXIk|JCG>V >H7ln-ٵ:;?x X4[PBCCSmgAs1;0[e.;=!߷:UѤpVnEY&+̨iE͙\IN9>$݆!O,~ ;>þ-;ϰX%@9wC6cl)(bnM joGՔR #=)^5cxY^)⹲\07+u'!9@ڛt@-#GAx/OŪhܦ?s-)瞦"|?6s> R BĔ'VؚclYgIN3R?ff#KT% Zcjd35.Ljl zg9[uu-WĹ&Fؖ`C*l-Jccje6Iu/\)lc)6+vAe&~|^5Ī(Z-$TGt5(`FVhcJE)oc1lQ΍KÙqL٧McԮҍ>0֢'xc7\o>GlTauVfͿBїPP=ذ&@0s^ZՠyLt|V M8c)7J i$7-? RVjT+}>5w IAAA|}}ӷk4e(źWyV#VegHmr,zp8:!>Js{\PCV8~)q?NXفE0ՍyQY`AEYm5I9|2φ:VOı(x' XEyQI] zonu  Y=v qϔ Hϸz7 f'?iLnSVL/w![:sI a4AaF!d3uEkVnWS9#$IB|{pp&mLܟb~.lC]YcGmq@CZR9p&s~MQ- bO?Fˁ `fxmNcཚڦi~v9#/7u ݍHEe^JY2|+NȖ.S;̉HwƮGigv)pTj݌qUe?nK?j͆`Eƶ$ __Ȫp2EbB-8=e9F{ UN%}V??Ԥa#F/$?J9 v^gIjgЩ b\W`<4@ u|>Bؗv~Mhx7zvߒupΆ$Rm]JX`Rb6'>!1=o@XH8I.kfQEBc$&y#&?$ IDATl`DP|7PLzF 1`\]Tӝ^al|Ԅ5[aET7nZp%+Q+M26ug9t'uQ"J0},[*F]R|?yÛ#FGDMUU˘!P @Z'B.n~xBv)a:$Q@iM@T\[3lY>}p)9:>tS:ūӨVN6]U [VըC {xUl͹H-HMg_cIJo;|ԯ},Y)6IēB4;cO0qZݚxjՋuR\,OLCs[%x=;0+;ɑJL_r|nL,.#.^@R CS"iO^v=r񲒶i{޿c!Yq릋=0Le-  (B~eTF=nr"hfNu &N?dߒg\HWjiFTW_gKX5MUWŻvLjݕUݙd:m^qVRR[ jԒ@j1CZ;ɋ<1]C$W[~}?Gl`Bn,RAjz\iڝ&AϗfҢNقA[-RI#Kb <ὛY X.ם$Ew_LOe9vfLdUgvv|)7boraS۰s&1 (ݘ~/dڹzmU\B rTlBcۛ,7%p\ɠeXX#φ}׉q.S+k7/C;Y1w~ qó MfB7`;.˿ɲ9oCY)-t92o Oѣ+s$UOh11)R,]Bt('R {0~ߜ\gc ]}8bF.ƛGω{]]tť#ܾ]b8::J `ȢӟlWx7KҶ%_B` >*^vg]"*u$mkŹ$圬R[~%=JO7?By¦<҂ڦD>ߊ~ߋό=#P.~) `E0}ka>bH,EOPRb/<7#`?F1z tR e|(0FgB[B)] }Et(؎U)et^ x:T2&@cx}SQaJonzl~j>X4J%%:,FQ4ZdLCqmY _f{HlUaZtctZ35`r|?t<84`bMڞpxL".}l~I˜n{O A4]+a/՚Rk&(0/Pp!R}3;L+,hŒ &<6襀iwM'=ס(_.Oͤ}>ll%em 4GLs rfw:j#0|u00¡LsڱT]M˜ٽKC+ נCN^ 9ȩs<߿4I& AL dEmJ8\k-`$ 2/B`޽3%U$S]gI}r@?/$ {}eeB`Ϟ=9nO[n777 xq:yr[goڃh8VɜMl OIM"glߢ-Cv{ho#OL|:'`x2g5 y^hɒl&  &2XZZ"" ឯB(XZZ)22&y2M|VgڥG)SI‚+W1+uDQ$<< 6EFF !d>̐ŧL:\tsR! s/hR0C2(Ydn ###M#d>̐ŧ!dfud)########ŐŧC222222222_ Y||12x"=4KKK,--qssmSddd aaaۦ|%X[[caakn"OW\xDmsJΝ;G;OXXFMFFJ+M8e҉L2mW'GLhT^]c[L2=z4͐ŧ+v%***MBDFFS泡 S9d)#ԍʅ {ed0Y5*hkkwyqqq+W={4G=7=;;ddddۖH)%%%q}߿ORRs[?(߿WX۷..vP[Ó շи@!Ƽ۠=ε0.r HBn^DhHn9BñHNNSViӦ $&&這ϩ<Y}YŜosɚ$OjF_DOCBə>݅VG_c~E`A ̠lj5U|Q] dm\y.OMK@E :8O+KKqPR [zf5bO#se.z1(.1Wc/Ca H s/ɬ Ϗf4(6~ 7n$<<ۣEFFқ?zls~Yz'6H8~j]JPBQe{e]ꔤ!zp?>GQ^M{ʧROwǷD6~-)^o'odM75ҏK/ je|{K"L@l2c/1)8ڀK~΁Sij_%Kߺ×JLl(hBJelwցbPRs^XYbA\➑ `ogqwyr?>>,GKOIkV7n 2$eR wN1 h03#+_`A]c^3a(m"ϋx'a+v Ԁ~7NQAO|dV 16`/V>HkC5צVs~YvܜO-mN::z/㟠4^*G4й(jL^Q}QE{fXu?݋3}2..{TFԨE(6Wͫ"{Pܠ8wE2+E18 K0/WD_G^嗵7D[~b:,M1;3 0+,Վ.010݇Q{T$Gj.35ªX:8Fd<}(`I>es@"&djMa>gz4)CUD}NG66JP 3S̭_ԉb?ǣ'/ … r$ QOz!e{<Ռ'iddy{?ߟ3foKz [[7Z0Ѵφy7ͻ+۴3u~v-hξër64TA[0oCAn`L8Ѿ hƞPBCC{?*յn\Vb[" "ﲡOMmM1|Ql둿!EEE}lSTTp,Mf~J47J <4<OAO[D18ٕ/O8i" jf=#C,,,$7T4\| &. )}zhqf<ҟ@yĭFmU@|lw?5.P"q䢊5"0m5Qqll+,3ODwBӦ1EjWGTL{n jsAU%=CJ 6!dLR-=}24 I:zz}s9CCC&NH@@cƌˋp qQ,I ,$^j"I˿1eU[OH60Fx"R(>u_{{\-rϦ<1'/V[%@Z&ænH]J9Tס2 DY6Sz#(ZeTʷt(칹f2cEtɗH&rqWGV @JID%PZSk"::fX >2(pQ 4ظs3w1kb!PP~}&IhzWu*3Ys^m,4|݅M߮`^;%6%?ߢe~ W<"M ;!:Vp. 144Ш]wDEGqOY:^.ԩOsѯ|KNԩ,Zm~'󜹬əJ Q-Υ \:ֶ5+#wG9~8,\B@ /:=IB%DeΆ$QVm`((T6%ԡq跿D4t%߱HB|" ^29ENSٷKscL#͹L-9٣$*-Rskf.5Wח,*TI"|sLvgMG# i"0s?Y%E^PIڂ(HHBW}=k*;BNX qZjAxDtyc€: 4_m9{ ٳgs=Ӑb91y ѱK]ǽᎱ eяRq>2/eg1z-cLrUb7G) RAՅfB[B[ D1Ed:W3~&Q&F*9 $vwv!2U*{͉kp+RKAB Q:LΖ͗j<sR՟vQ(tvOʼ>>>8994@S1t_gI5e^έVF˙nZr9*DvT#W^&tz$.>u& LJ{Rub#D`duДy?n_Jέ )_L ::h#!d J!5xŒFc9Mu/EYO]3"CPXɍ,7%IRr<6zJX},OU I*`6rU2/㢈OAײ E:%$$dzהcla\B%؂zѺUEC^Әk5cjNq ic:-96maɭҊPڕjE 1ŔQ-LڎXihPhzǯST|[Me.|?9K/Y>'@ÉLo|΃E)b;C, ?O|5}6im'7䖥,YnR˸Iti:g\_zWA?5 ; ޯX;O7B;(ެ.n^831 ocO­8l4ѿMȚ('sq79}(DE=KL(T{پ5Vҷ'e}HJ%bF bj9LٶimU4P -^˼sȯ& ּ/y951HZ6` !._d1w5@dH!j ґBVDcd6d }k^ kujPDI1RQvVXZ$I ^ƴw033cϞ=;S76aێss&mhΤfʋK=]ˎz7Qo*ôl/ 1u_S3Lyi;/wqp(JG2i2bx51)W@;02mM 3vu#p!j\%hU $l=(M+7ZT|۴෿6]8Q+L{̙xS`!+5C2+9f6E1;C43-Hq Z5*QZ`1ly6q )SqG(s[Y*Z(WwFw>/,OM}>}z$ݽ{7 6! yeƍTV-޽5jd9|\.gǎ3%߈7RV-Tw$HcPZ}C33-5ɩIWWD |{L$NnC4ךoEk  |OacJ4h v2u߄)QT( |Tfǯw(>KͧH> \rgg!OT*.]$OARd_ߧmx]M"2ٳgpZWU(( ?cD B˗qvvVqORqɧ[^ Msssŋ;wNT9 B-"A5#Z;x]AAbD)  |1"AA|  _H>AA/F<.d8{, ^^^)"AoSDDqqqDEEu(WBK^"Oxʔ)A>}3E#m 55??Eʄs%\]]4|CX@XR_I311AiqqqT\Ym~[zxyyqСΣ>U,ױBGĥ@AD)|6ЕC 8zş݋a:&Xf l򣶜a4DZ &ٸR(V\I $IERԁ$*EE܋Uo:n4k۪դiS^PdZIf1ߟOD* AFfR|Nvm(|fếQ&_f .-ta"fEhuXytQp=w҃{ z eKlZz166@WC *wɧ9nSSL[SÚ{$RZAu (__Qj|MnT.B8tݧ(h&3Zg;K+Y|<}GQꭹ.t$@ O?n%[wɊQnD!;x-2]*^\WOKᓤTsA\¡DW;ceOAJtD'/|$''O)Qޢ.T%?ɵ,2} M@˹7aߗ&b\Wz”yz#rRn\&.]0,f7uUkHәɞȝ8}}C`\f[i^ WAS A^9u/EcYs'iaJ:`\$mco 3 t>Y$qsWpX=ؕ c< r ?ʞ4b@oLEˢTjԏ^NyU0*=sH>WLyOw̑%07CWW}#ey"X.OT/&Eq܄arRwjڠ02آ(UZ( Yڊ:PݓvWr1x zkOıG(#7f2,J=ZRM;#$Z uդy-#۴;`.@ih} }vs ^{,[L{ypx7\>?84 e;p҃t=wߟAұ1k1ja/v <8eSS '%1nmߝU3a>wyyx/5@$ߚÈEm>S%D/ɓ;f*x $*}Uų({fX+u ̙IK9vrXSkfB|4I#˘>"mVqtnM"nNQP~IrOFDjȄOBmZރt8'3[yQ*fITq&F8v;2D1s&I@҅\LաyeM--Tr܇ [GuSݷ3ըA,·u_ R@nL-q~[~T@zAxc`l[0<>fTd?[N%teCLF%^*ǗEWSMM=*LRu~XwF}ڷŶu(RzRT=q KwdlK0@W ;*7ExJH%?< =ܰ پ!%sv̹vҫzS~Nyص} Sj]'"/&ӬTZ]fnԬfCoM^ȱ򫂣t b3П]yl^6y?Zi6MBt=&S:4IѶ K⯍өa|?ʚxg\$79&Rx,~z(c$I⣈I67fҢM*B؝ dL=f0uh=.y tR_&veTuWcO _5$>3l6/ Ӯ']ZZqA&Ȁ˴Y2ǃ'[F@YkKEq\\]\jI9%0uy{9ay/VmtrmɑN Ԯ6_:{mc,%YU{~{9 x}&9ii,ޞ?TaM@玍)_HЦLpm dkz|7< ->GI7FUO-屚ٹ@ea^@. rӏѮ<+MeJ'guTi݆rIt=xHɝBdOr`%MIaA| G$Q`]-tR,t+,iF6y6HStH`U8˜PlT+"OUTnL\}'~^;ǣL`MZ?<'ֱŒ"c^gXY#k̖c9~8w#t'7ԡϗHTOc15ƱCV3s}wb_% LL1"w д}@ZM:Asb6<ȺN8`dfg+΀k[ X`bʮU-hV/Jq p>u8>1^~<^gSOCZA˷'mi _{u~E^zٲg{ֿG]s>Ñav/fz]y[/Ȳ " G~PTA)Z^Y796:zXbke~QVޚ 恓8r[1{vkJOa- (֥]VX[}s\-MP@SoH.:Z]o?u'p/}Ъyq~]c\ue ʆrX>o2m`6JcW,I@"c8 cw\BI9LYv RəBrW|+s͑n6p OC6};8k!;h; C% IoWUr~RN >eZ,Yq!Nhǣ՟5sQPu}:3/r6hz32>:f4}x7(ðXeZLuebpM~9cVLE舣x5,\|C;0-%c{C/B>#3,7g$r z{SXy}3}~y}'0/Er zxPPutru)H!x}u3wFQ ttF"%>W裯[TZSt鏁$!0Е>QQS5:r#a-w$GoMI77>vse""orzBwr}JUD?{$3LO>%lȐ)Wmc➡,W]TKoju,;<2bKOD5{1A *˟cȏ v}OBEߧ^گnS3tgѧ 8AɋiN4F0}`CЧhL1n.Zz#fS1/NƳ10z?av:L+w4,aL4wc Mȑ94ϩPq~?,GR M%e\՟m:zӥ9a"i&E==wFWYBFpo(݃BѴrOu rqBHfOD͋n&zez1)فiAWׂQ 4PK$-K3F]<;L3di➡"PSvν*[ק>ua_ 64Șu;bÒ0Ppk O{dM)d wN~r>}Hn7{n6lCAmJ.+G1JSm2ާ 3ڸq#jBN+r;v:G۸q#ժU*G߷Aw+nclOAcs8*1c@v#GqO:ɧ TWdxHKƅ]u9 WC7Np+CɖxfVv'A | yJñ}_vfKdO$j>A]S"A5bnnJB&~'#QTu(" ..E%  ...yO) ._ #*+WP(:| oDDD_^"Zxx8.]5C!777.^ȹs爉p9 77E$qqqT\YmB~šC: !swwɧ!**J$+ 9ɧH>AAg  #j>lD | »3$"׵ç-;uw WR96$6AsRSyr~>uG&֋;CI9?xe;9:v8Ԥ{ɔm̶f ,e'9ײ'/Xլ0]csiW/lF74{g8hֻ˿s LJl>_+O1OM H~u:޴!L!M=Ǒ&(8P9g IDAT](:"^F?-HеXa^#Тs)ǝ.{ͧR%IEQ4hYlwKTkù$a Bp;N1/I ®H~ApwU.besQ1<԰un<Rդ6qڙ^0.qw_bo2# dfpajCL w8'KXlX1vݚ>rz b \F4(u9kz9 =+{O%)} RT<>}eZPEqv9V"1!5j)"!2Fqa+"OUTnL\}')Iu&(C2]>!o| ;| ph8IMޘ@4 |xA2玝!DOcߺ`[Apj|)w>L[wtqBFRه͓x Ĭ 9Ğ0!ii c6l..[CG,K!|u盒V)gìhz,'˷ lR埿UG#(]8p`F |NSSS T̥Լ\c\;:#(R2LۗqE]OABUqxͿcAAikTne& LJ{RK&~rLJ֤-B 'C_?#W߰h-la h:IR>"nv6Ue=$I°j+a/JBQDeԳ)HNLJeOHMzEK_68).V[֔sݮ2[ֳj[^y22,8zSGP:I!Eywc<lc,e:W.~Ş]b<uHZί1חޕlỌ=nd74vDŔWzm/7ly"R@ vx2fIL iS=FLQ~zlJn\Zxj/P<]ԂgQ$9bX+u4gM1Bm}5Li8aA:|L;e7ej}xW]~o>5ёn9^Q6-ModSk[安3'SqHTfw}L^ubyīG\>1K:דziv._i5ʓ$' a h4gkDLe1 _$٫i Qۺa#4]nKswU|C"w^e;8H Dsܐl|S0~[7 gYoҺp$X(rv&نۢ0EϤ0NӏRۆסK 顣goG-AϺ) 0Pq_oPu~XwE2\"gTc饗D3X +#= ͱu@,QkbR+Ҙ_w2;],1ָ 3jR/ߓfbh@s84Oζ䌵!DP9 1}&>|DXyx#K?>Qe,4B.T﹐sHHMd{BꢫkJӕϐTO6ȗP?BOT%!I*y:TM}%vH$._LJ5{f,umSP/~ LGFR~?074eԌck`&uE:6"2#xεa;C1Jub@[d7V0bP?:W(7\HjBJֲh-A7g:SBWyfi5n;,n]gK1`j rU^Jjzt'Rŵx~}fq"|#EOf匢}00 M k'q,KI ZY>ʑ)jzKe(rwBjG _uSݿ2Tt~VMm*.\M׃G)c2^{ 맄q)M Ś~^z-uv/ e.-Z]͸aV&ڳ.f/iUk`/?,oEmNM?9C32ώ}NbtBۖsL>$u)͚|I՛H\2mذ` zk94u$6N8;i$v~K]SƜԟb_1ru3 ɗ8}.vU)ۀT(Cµ8'6?/40K %AKNu\\`eet?8EB6^m\DIfPتÎiu2%*9=24a}X~@mPc#V\lDqQ5|ӃU nx'H$?Qo&RIk(Nw=㦊hh&,;!{61YBx=-["2Rd r9**W=?ZueX>5ҿѥb\v,0f]Pz0_vJR.CK[ J᲌JRNk*}C H*T*@CB4n1zdiDk'9737g{3)%iQ IBM^^Ԗ"ede/LYP WGfcH!%%]@3Ֆ91#漉SȠP(|2ι*FT*\Bx2B8{xPDS\/qXiPCs[Y ܝܴbq斜J]U#@˨Ygˏ \\yk!}?N{kVegc6 H<ЋI5tdPهʪ}gfWۦd gws; ,){&g/ʾ)z[Hy[Xy+7Jk$g: T*e8H!=#^%Q8ӭM)z B$B777.^ȹs爉p!_277GPAi:t&ln|:eh_~oM.Tw<-}w0naڬY n6JBMp6 V *xfr;ZjᄃYoMo,=Dөa1ժ28p Lz6(ӓe ,a|t65.  Ь)?WP~' FirɛU/s`<$clYp ,@8Cݢ'/6ƫ&/\i6,g,r\^Э5zW'MV jY.ACJ_0}\r\Z;MΣHc6"#.K"pww%p[]!h3U&m)!cӮc.\1ھ\>~oLYR»_,ad.[ eȬIY}yJhA6&v}rezE:؉lȔ-א;&%U2j֌;O ^2S5VH`OV/2-gm?ꭑg+媏mbܦn3 뿞ǯdظQL0-c\O9Zۃ?=u"c^Й񴍪3~$fCx.bjeGY?4%  bB )RŸ m3Őpj9i:m #ݻiذ IAפGZӑQ6(ظq#jz} 'Gޙnڝl-_r9@XGر#9ƍVZoffV8Dͧ Q/%.%lPhseL&6'xMEt޿w $<'cP(*H{F뾬PUoA"OȸQ5); c,_GR+"{iRCQ*OAAP#bes1?7| YDDDGTTT^" EȧĘ!cn"2DDD_^"Zxx8.]5C!191?| \ ˋCuB>&\f"2DEES>uE!g"ɧ  W  ɧJ 'Gycnj)*OR2jXT1CϑewCev\ GKE* n"~#Udl:q;S\io)w73' )7]e,gg8T!ed-Ypvt+CR-J) B |ş݋afX;V,>̋0_}1IaSZ]a|b1Fz6zFyT\> ؝ L EqkB[ɼ&*1hbۘ|Q-1EWWcskJN!a콛a7i}ƭ};V03"H;IؔvLa}1Z/EA!gjgQ$9bլ&X$yYH_,#+b*OxXIY ]؎=eʞ%b6ax&OVvt ^Qկ,e .l'lj'{CX=u+I'xj-hNؖ*9Oz^ sYU,eӢK61U$d*Ij ;Ђ  477\Q3̤e}+RDPmbLMjoLj g((RFV6ڄQ8})e(H"_H:_ԋ >^iX)iᓋQ/+euӹCMwg2?Jh|eH{FOm)|*x Ztj͜5ڥ,euu-cGdk<LL؁R2HJ !5] @EX\_qGϔxQTu/L"nYhjtW_Cq/@S3ۡH#EX oѣ?U'|].\@RRvvvu8ԙ3ghܸ $L Mfn&Uka|FT6ٰN_8j@"j$eo'Q*0W6(@a LɆ] ~_(\k=,HKHқut{t?4dx=vLaL\S';<MT8Zc$$tqj2 #!Qn?ձR/Ϡ!0 gVF1eK$ZރYkse 3$1Rm IزeKyS+l>3?S4B^P.,~Jp"3܊եcoW]~!P o-<:)'*dP"e #v3w&mӠ{eJSP7RkM>|f,54]K㡓©7I,d5ڰa@`?ޓrhZ H2 mpv6 ;إ.)qtucN}8s2-~2}K紬H>삂%S֛s!z'oQ'cjS4Չi+Pf&^/O*? `L,1OULO)ӥh^,=ՌHDJ^f*d6$0xǪhf²\K*l{K `˖-g}/mgϞ%>>111k՝z'/9y)' kx 12!S1~@mh4_E[{9Dc4Kt7Aο3O2.XJz;NX^'>s{Zɬ}>U|璴(h$Rk/V/jޟ7L+ ^R^4u'<F0n ˑBJJn3f-}scpttQO#III@KލL2ӧ?x?Cɭu}ȃ3[SL -PIcYcK0Ry>2@ ''#zqc切Hkzmmb}߶4/C-eIN^]mZ Igwu;qppxSJ`,ϔfV 7^}CCy7'\<՗eġSc\)&)1dR)CzGc|"JeQrk]-oV>'99?}LLLܽ{JI̲Uùzz+xcxkU>ֽWxjS]'|E _%{ϱ~8͘Ͷ{6g39*CDgssE%BQ|*$o!cq6;\x\>?Ѥ`u1 pO `u7?5+? w7uY1C{iRV<'wȦF t e ~npլ2m+)Wy.`*lwC!ʴ&2 -vc.G$FɳD&b\(X*q"NnR&$$pǏSX[$uFDDG!o޼)+q.%Z8u^$juSY&Sa^ KZK ϖ+6{VL hãP!\Bgܬ)t+8׍v26eO,/FcFi|||VUJQz>`xSc|A |v|<.nt 6kI~)_oGiOÍ\9bmkC0a\c!Lne+څMb5mve+߱ݴQ4f?SSҲaCzy+P[Z·h@=ŕT 0 n(^B<~]dJ?~7:dҢmT{S|{۬3igw)?arѐJEpsįCt5_RP{v+|w^[gVk /;y{bOq3^f<޿صs;w&QNG^&]0Q*,eJ߆"P,ױ a4۱b BCC ALko1cL,gh4k>լ3c([ Sf,͍Z_ z>ja"gݨ?3Ŝ3~%ƧߥN\U~Oe]E 슙#>lčp =se51^\udIzA]X㜋r2Zm `YY9V\1y}Olzuf=Ph,Ln8R6^nUq&+Ь3gk{JU p&YL6?O_?usF ]}u)zv1:ĩ'<| SO̅9w bdRȯ ebdqKX<<<,E˲F?~< [Aהu]W?k,y8.b6GKe^$d= (Nv `ْb8vg.~6mwhFiW$o|Ƣ1V/L=uzßYDTl~6cL֝xŏͳiaCsl }:%.Qk8tCE~|5oC(#ߌ[ {SD iAg{Z# +H ǩh\8vw;%k뎩XĜ>ʩ<.S^"Q7/apRJib8g۷og0M +<ܞܩwwwv{{_Oh߾=SLIۗ9x`|}}oѩkc x5y:!|a\~֓~wl۹;wq\j3wgkףzƯ> Yc';wcϥ>%:Std<6Ts4?k^GnD}31_hԻ m&ǿo"+0`l0\Veauq^,}zeWv}xʧEѲ.4˒ 1wp1SNBf4/!J=fߑ6ăfL:@sY11{0KDɊ<|urҠn}tw#?ePJ'ɓA=֭[0ayM)kG$Νj>׫IN0sdMά^Ü쥨X*U$;N4BPvc_K'kO#/*VRJT,uJI.vo<8wf3\(1ua37lQjM`($X-?{D

    j'=i  `[L5Ѓ[)OV.xA\R˾\^z<f}Kifd᭦>Qkx=<0/+ UX CVw%^r"s˟uS~ qq$AȬ |0L`8/R̩Y777|MǸqhР9z=օ6D~8 g1l#c}ޤx: `a6 4oNeyD_ʡ'FRRӛ8^v:8C})k?Bb0ȕݱ\Kq~ix|0әJ:QY|S '$5Dkĝ;Q(|ZHJHiL>rK~޽>}:3gd̙wX{b Εja+ֱvZ/\Ӌ݋{hTV4-Cͼe攺uvvu]+|JoooC T-2MP_V΃3|Xr,}7$?N 9j'ѲʕhI};@S,,˝6|զ3#UOP,,ˉȪ|(N;ӭDDthVl0ic6 ɲ OZd3`^!9/y!"&W&Nri٘6m 42=ޫoS Jj d Y|˲'R<75; Qt3R'3'B&z6 0-̔S+*OISreOvءE ۛʕ+߰#АfdRKߪo6CW3{,I ؉V0]x:lɱ2bD O|[ӟ<779*93o~(Ӆ>ޯoUbؐ Z`eR=C#(1AJ(+?@: w z\xa P-㳙ĥ3LrKllle\]]qu_!_> bƍyǡb/Os]x?Q|`_zվt'*ϙ:+J=M\j<>2Q#)X'c:QѲU?>Wq'ͺ7^٣)TRn7A~ Ç3Oaah7ӏ7O[jiцY''1uŸ́^W ~e8[_>,b|z85R.#z1t ݷ>%r*8>mI`H'i5/bQt`ShɰAhN_:a`Ӳx& ӕԀu-_!NeBx`3t! +߽xpyejƌxd_ l.n؋r_nL4|ӂXnpb˜wedëx:ޞ 6ʸ{xd,'>hZ"ekb I#rhb/EK^' Qil2uzL_/@Ss }׉ؒJCBBh߾-6ID?$+܁xj\hrvgL~{k6 &E+P<“s+נ,ؾNڭ[^e^/~KbNF<͂GMb uH˝"AM=!*F8&3}xc&F#0n7~<ǫ_ 'a kϚNjdժU4m4\ LEb.'bLWV,K`T.]!!!4o<Ӿ=YOƺu뮙+vIDDMnSZ:YuoTNSDD${IHHe)""TTݻw}6lvYooo ࿌§H.)Wi]DDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDDFSDDDD9*^jUNU-"""" 5k̉jEDDD##'{|(|(|(|(|:"""rso)zwyܥey9)q'20 ,+8x0" S;6z'=>EDDr$+!=!j_GəK;pj/{83UaPNyg|RW>ί~Kt;-g'T͹~O)""k]k;;7 ]i+OajK:{/m{^ov{uy#x퇷-(| 1{pvL{UW;;9f/Mc w{*mϐٿq'9ܹd3Ķ~\i3Rbw,!4@bgi_ zR7Z-P.qEDDrEm<~8( OFmޚReOk[}yjO2ZѪ]0O:jbY$N; v/:pӚ:UJ]wJߞ {-uC'>Ũț|ۥ!H#LLNZw&џxqW>ϫ/^~AL|,NlOkG7<[7p`:?֒V]2nFV(_S1ט`+qc1rS]r9§.8%Nj?oLȐ'82r'N% Esx#}~̐"[WxbH ;?,:SS )~S -uk<̗3U*yJ;[$_d I,¶HmSZ}w'TTХr;7yn˟dǩ?YC|I@s{v¢pm)CjqshI//Kr'stg޺L]3.9CK*| W^mW]}Υuv/m_ +ƣmSSGײvJ胟srPr)UG7hP=ni)ˡYфN/KdgSҩ'_IjLZwZ&df;M_39mqa/OlBlFj[9ekT=!5wb}NzV$GaqL aJɑǔ*VÙxy8E}S/ztۙ҃K? 5xyj!NG;KeH 2gavcUGWOw%n:L$ ݙ4 GM}Rwi'"3g1M LiYuK!ݦ y _)u|rn-)|+PVո^ZLj7@/"7n4ӽAm, dՎDYez"]d}>z|8d(]?02L g'0Ĵ@AKRΕfޔȘydg8y,m,׽ls^H&^&v" yɎIx42G_<.׾[= """(?uk7L fL;.ij#p÷tth@smFϝȅ>dTB~/7P6KmE:1+7ǾgvمÏRh>Wc9cY2mǵ[Faڍgg&ȨZ#2~ʳ2gT<ޔڐs1UYHL]"qJvY{5|?^zSFF&q+#BBBhѢy{"}i g'}<~&iڴizmǴ'V% TY|zjroaA׊U{yl8;xUl>7v&"""wN}ynmXK[~#)ǭ^M:?Fiuek;8pfC)#_[Öp::yݟEб|[dEDD$4䦎a .jr&+Q3׼龜)R(}2v;t@N-*""%nýn|ގC'">+w혦a7)l6ibۯYzISkk8l$&KH """'{B Z>H4S} IDATMBCCr|*u;֢*NN)Wɵi]T-3wr]DD$8p/fkx\{=== nM=렖۪jqo7tSejwOm yMqOqOqOqOqOqOqOqOqOqL$/V IENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-edit-dmr-contact.png000066400000000000000000000417321501654372000227320ustar00rootroot00000000000000PNG  IHDRwDsBIT|dtEXtSoftwaregnome-screenshot> IDATxwx+i!BE{HE HQT"J# \DCnwf;s)7_.2`]]]Ki'TTT:6-)6.ԍV=i1`שUe[YNF#;l Vc9h4-vM;^V5X6Ey֪ȲN؈{tOi )T,k0-!**/=B6;;9)_q5p".ͷ"G/| lJA\zuՄHM{$IwT:~  =*G% d% [ iTI9fKt㪷auc0I0C S )BaDSb(A`I'耛(EDgߚ& Ohvn(ۈ&ƍhxm c99;ODWBg{lɉҏ+y7(?ZYbśR ?Bgn=*ƇXv.>+!lQ圐l8} _J¡[|5\P< O-?bs8hx!ghj族|Z ? է5 nNeoaL]BٟhzƑX_r{ҷ*dmӣ1\⌃kEMz2~!wk-4/cf|lEB'V_$l6O()&|>Dy5Sʒb6O,xJK6Bd3Z7ݻ)da_=V\$uB w' ZF)Ȥe)؃_XAA$DIvJ[Oս '8r=c*}.E)右3(2f#9^Kv#4o;f`$D1~5 'o7%Yd/ՂLb 1d!~i$$kN]Ɋ]W7McܖpRр{hJY%LoRZ,p"Ɵ%\ {gIXmL>YjMqH}gaH1b.ً-K`HȎowqX'N!ɩ|DJ2@ȲDɊv"phfUNS)Ҟ,J! 6#B|gTr#E!$%kn^U 9qM7,Y˄HUI=I1DĦ`y<;:7lɹ KxD V>T(Z'3͖cdlLXB$c+Ud{)S!GZmyLduc-gF*>bIxRt.h򕐹ʩs&js`gԱtG!!e<hXe2vG\A6jBhS?j },b?rT=$Ƈj[ϵ+׉QY#MdG !35ޘNd.c3,N%,V 섒^*v7[RMq0Bi,7.&$L z'CB=cQ=#vZl[>ks6܄ m 1pMLYf3/|6;\]u$%Y@(@cSL_Nr%*͜Dss{-zG+dښJAk/c ?G؊YL|Gla^f/ùp+SD>#td윬z|D28p Mv_ee=GiPlņYl 1m 1M,QeRFuwS3H#cg\$@lL!9Ŋ&,>wv" + 1ɘ!uBhdUPh8ء{+bBb4I1۰)qtХZfɊUH z{{5isI!t֥.<6d"ɔVN@e4-{d@`N$bСa_E@w#uqd!,ف/c089!^Q bTQ!6 D3G#흓3mτN舓=GhM}Q\E%Ȳ;M~OSHi OM(mNس'ŁdYI$IZ"5!I`fU)Έ׵$YBkq{:**Iu,+g| ĪW**yQFJ!]6Z.yC ۷ ~3g?9qb&O^ZLiUl8$ǢQsTϰcvNDڲ_ 9%Gb\Ҷ Ycyr&/(Y`bx:;aiC֞{0p!X1yk>ޝxw5و>q]Q77|*иd^ˎk[!hꔵ*cZFNu /OAŃC񑑤Ī9ɧ{8[}Bwvf]oK>~Ԯ?a^ 7I m(㝳59;Xr3F5U: 9#kQ#X\T*8[߇N,#YF)R_/Q6rnF4k\0+J 鿆x_*k Uj|#cX]>B Ep;:ӎ`t.۵4t{y+WoS98?.n(K8+g脽>9_Ur˺,"P"ԙŮ]Kп-cQZ>N%D6lyƿ2;6av_Q9T9z(⽨-9ίi2 :H%LmI9;AoЮv 1 ڛvs><2 8id4(&ѧ(8V߀%3g{wv\Kpr=:N1o20ao]A!hp-l.ٜIn%Mz~)o2No[A^<#|-=g?|:իѻwr:tՄ[5F^W M#yQ~v]j1wcId6|MfVK@`V䭍ad3uY)X,R D\>O59ݚA+"B®hm׫q1JLDW1ٓ؊Wqa?WT ! iRIbZˠ *BH)93 ,Pny~jGQjF'V.š&Blq0d|zطon߹3/X z|DrPjG#էmC'Ja]^ҏn_>9qVyN1}_ i~9T8J`;R"z{>8GkcsRJnMh/lݛ "-j˫0sc z7Jh{)FR,eBq$1?xxzxwu@|E$,y/–G݋Ə}W=>]y+..4 =6R?Q <&ʌ$:!485CKf6ޓBkmNSX=jw=CzPv7Fb$`V3X/|NnI!s?J~x~߁<3]Ct^߆@qΧ, )&4[i^*늶]|-"Y|~љt{3ǖIzbxPk[Kql8otCv/FkLU.CV?;}dudOɲ( ?(xo{yAt^链cud~oW=FvJoѼ ;  2HuqD<%Zٔ,o uRhKPLηWR[YRi eK;3slfնQ1J^C}ǸKx"\ᴥۖzh !4. (5:'ty-U**yU**yU**yU**y9c6Iu @tT'Eu ,x2pP6Eʥ %&!! >o3T^ <<ˎQ@䆜U**yU**yU**yU**y1{}r<= lPgY9S"Mzg;X3FU2v&p$JQy7t!r0-H8ɪw;PvL [y|>_}' $X@|}rbR9teQn҉e#m;vC*ys63K{!'OߠB|d)%voھ'tx)C{n޶a>1wL6+4ɿa '*M 1,gÓ;gմ[Ei‡%>`$KVQ8m;3~Y8{G Ez~aagɧܟ #,gY縡\ SukS,zG2 9eC/D`x㫣4=\ʄϿ!u8eK?DӪȗ?52;&&tkJbATUH[\Pf-joJ^?[?t罟R#p+B"(ė33>C|6e= ADbZe,wnNc+aѳi=B;]"qોG# X!nI욷g$ Tuգ^z)lOruWեRao`::ۡs@|KxmR >.t8xQ|_6mcQ9$?Nztvn/cp{o4lA7|)l$kΙ9b( CqBK}s%Fٿs cXz)IN{ [-%v?GRc+w} VY0In֬C߀S<-i_?6] ,3ۢ]&">+M$WHGY $LqSh]͇_]H r4\ɘ/E:_j[UJ\!*"H/f,?/rqL݌']^CTxT&1,\kTݼ%'~a%d”17Ga7h٭:Oԟ;iͱmIw~ @$pnϯM;رi1Cnݙ.VdM/n'lLC϶8 7p56 q? >o8>x8py̷VөhT;~y%&ኻG&e53W_'vlƈu1]c2aZʎ́;Y3=ר?z7܍1quٽ9pp<$^`'p6BL4N-GY8%{@ChԉYWbr]lڷ6/dɛR98?nlU IDAT+6FӣhH݈Hn+;-iV5`oo=Z8g߷m*mJQ|vWgJ^lz6[.nFy9ꍛ̊1$dV \~dfzt: ي5 V%k1Іg@r;ؖOY14(ND\ qpv=z>MNB;cjiAQBvNI2.@_N5,)#|}V՟RK-ޟۏ=mFH.ܖ74$RQz=nanZγDIJw$FG8Iqq$ሓ(;h$Mfٕ |8ܲHH 6 %8ptrD$ּjN_gYD NX@İ[s2a$FhRV_;Ş v?F|c;Eqs+S?e-'ox3g֋Majӈg1?yJp[ZUq#%AC.ye|}$ZQWKͳ땝LEKd l͗|O$' $CAȄGyi _rqvn 7&Z^v&niqk $% z* 18Eu$ NK DXH}S>&B]iA Qߒ̘)N'$[ 6n|-5WrkJZq](ڬ|ѧvΞTSƴys/8ǼiYaBD^sݱ,N'l޹Qv&_r&mܞ _vѾSm kV%\xwQŨGPŨGPŘ << */ 9m7 >Py8r .Kg¥WOW7Ry"^X8GT1aJAJAJAJAJAu|MTp*Eu Q]©%3Bu STpuhTp**/(UT/"-e-TT^L^81ܵߜWE%Au c6(BTj>ۮ?>MA2joW[$?+ebok24:4_vĩ g]l'\^7Zv33_&vr`UG빠)3bKbwYk뽿jzo-H8<3G-`u /$zKONg5lj=8}'4h}>m+`9y8MQÇLWn+cZ䛄&L[gk9y[d?\_6uE{m8v.N8wF܉-/*}DuGl(b7DLm{hW-TpvZߜ.fo%y  n F~wTDE17/o:Rn iEE~ľk.~[$k ąQ^JBNB ĒYu߭1"W\h^LGĀ"E H1]k]>5 U=gmK s,3K3㘘XF/5m7Ŋ6>¥,q,0U33{ }Xqkdu߭ʁng.-039WįI[H볊Zou,ϑykvc?[&SE|;LIl2ll$Nr`05 7zn|*xUG?wzAPDw"[kJ ?ޞ]e&͍~Spt!R4aнm]Ӹ K[mKl#p-UЌaû_ kT믿#wD7Znw_!ykcw;v@|.Oְ·fZGz|7rZ$:@ D"^Ĉi#g2+]ıw|לycp߫D:.WY6m%,`TM&qNo>|3\q[ x^pK\e$pY![ E8ϩVqۇ:.,Cw&<]Q|Nɭf%^oۺT*[23WDɟ"l+ؑ1ѣPNǸh鉯YσNړK;'[<#p 2,X_/k.kԕTqwϚ%3ޓBOђkkѸoT3zM^J\ q c`{n"o7÷Lq=#+re#]ǥoLfop.[Urq I>?{. _'{I[xeʕ_㢦$C5-FP wN]E  W0}YICɒ('s81HנpѴy e c^yu=UJLQV.bו{[vp=a{1:jf~z]W> mw`~ ٥*LG.1LÙ2ÙZ13ǎq+\nzj@?dFUC?;ovFEga xQqx{F`-GUxF$n>jδ^~ \N,`Է)4ן yuK8@'_rk|?P5,QjgbS4(CW0qH]\|5MZx_ٙz~S#ۓiRarZʎazMm($򴙹9=녃2sf~[*ϐsfx$q] 6Ze2Ϲ!3~fDF^٫4-?̼/ګY/ꏋN%7.TT^PT1T1T1f%JnP]=Tp*9Eu Q]©d% :LUQ#bTQ#bTQ#bTQ#bTQ#?o8ȟ(M,^Y21y.;F7й7h6scK*lrF uY @?OB[_8u3_ًypo:!DNtO?ΎAQ;F\GK!*VDuyXlc߲[~ιSNϥq`yn ϼSIHԪYZ5*SMBv/BiǪKr{%J\-Ofh~N%H_R2ݻuHPPA}̨mPW~oeb,lѲL3 XLϪ鑓QXiG^P䋛9>ұ 䡯ڛ!Ug^xY a@Y3|% qU4֏Vq%*+Q$PTX3S<|m'F8ekz>@2賮=>Iȁ_R 5dza/d`g|Wo7#h2p:M㋹Yν :HV|*koT***{ਨ3T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1T1˗IHH$:*yB八3AAYyIɲA>wzEsHLU)/9y.(Q/-:%sR`2$I\v"E|+W#,\yys`ZT G5GU#ish(Ȳ((PӧOkȭ[$ NKrrrZ> V3]5BEj6Nή#F/לQ<~`oOݺur%QQHDA\ 0(ȈH, wcx{K:'^#49cN,WHL 细$2XNI6&²g?)S>>ܽd"2"&S8: &alMnΫ0䃅,U =EPwWfԎLJ+leRէ%McJ깤x}-=|ͱx%=mk#c'k.z$t:ǏY89RLISPL)hVjϟGa4&#r7EڔŌޒƍӦf ?I XvCB>{X!o|:B][@s/:.?d]cQSO @(V ͇O U[a0Dc6+*48C &1! Bttt>-8mY~0#$KGzi!}Z-@( ;dMo|cyk0ro6΢kexk6ǘu l3lѵfښml #iAJ:R SSGٻ#%''# h"..>|DYEPģGneyս)p}+[;~t4_=P%FCҊYi_/򣕳?MW6|9z{7>^ﳚ\/+0G63rd2p {{{<jVPx!8mBd"9guE4j ̧Nۮtٕf=LZm)v euتѸi03V4qD5&|p&㍟ѷBF6K*9c唗JO3#|l9~>ĠbbcaUZT9{%UHcc,`I`_[% zCXx4pSS8uOӶ+fYϭ|2VNNINNWWWB`q6tZ){{{L&Fl\|5o1-s_ g2,ٖB(~1Yٛ1X H F6XքY}4jha߰՚B'c>˧1~eb5k cE_㽹+kwwPKI5DFEʒKIHHAzi%@6?M5 X`Z,uNjfPWߍ{ =РՂX"u(F3'Yҍ'A{F);bT"㮔,+6;;; KIO1Ża4{.ɩ >Sk)ʼڗA|PӔtt:㧲n ~B(hMY|7+$9L&xpKU-iNd \~<%'tFk׮iE9#K2LNG\l,zk׮l!5}5ڷ`Ŋy?ҘqI^1j,1y}rw=%g=b}3# - NrP [jϭ( Jvvhr;d͛Fʔ)fh4-QpڵLӧOjXO7r Ǟ$p;oFY-`I T%0d߱_^eAS^%Q6qCZz<4`'/KXyVٳ\!**&1- `;dldYDϞ=sMŁZ΢oݙѺexF/~S>BKgֿ4\F/-^]21ƴQp޵-0ڣ;0F¿eDc eB6̨ٖ[ae a9D,1(7Fߘ( Z_4L`]{>/=/=f8k3l,+!40UWFIԀ ]$/ؗɮXT714''')** L||<]]]x^\ߟdlM kV𘩸?kihڙGu^#TIDAT:z;+rfɏԹx2}T ?t#\g ջ3o)bdS#O6S]2RSh:v [8ظoI"Nro:mm ic<ϔ*乽3 Ұ-n3F9 kDKkcعR,;JNc}eLv5h$]_gi+Zn:[ƌ6[}}vlnf3fɄ` ::F,NÁiWQS6ظb'#x [9 G;MoGy'3@kJA7o= |t>GA=]JѾl N(w]¾ L6??#2>f[Q,;z}:]:j5Z(Z vq:}sXgffp\LNN7|[VXz2x=]ݩ;_<'Ɂ;L{k+Ch8^9C7V5o75XkY/ AH⍎`jB~;S_anF^Zf&{V쾮 ./:"aFEQx&>g ~-a' N_ .R#J_5Y{?wl`h*tEXtCreation TimeSa 22 Okt 2022 12:12:46 CEST,= IDATxwtnzHH#;RDQAD,+MEPAET( *( Q"H'^$eIv ! >s읻d3]g`?Gv ȱC7),VJf:ϟ?dr2228NN'.W^ `łfϏÇ'oذ{2CAfHV\V>| lgC!"R\.N'v4BBB 9""ev l,YdW\Q?99Ya!"RApp0V֣G1d@{!=sz~zV{Wݻw*EDeddPfͲeʔ)tdN4amAVT-\iȥ#88m>iӦ@@·df-8<ã޽{ +*Ka:uHll r_ye9y~k׮mTTCDH||<$&&n7UL2Zlنs;~;܆*;u1*U*WYDDSFFIIIsUddd`XZ>p8TRħ~=DޗA9EP=ެYED$%%w]Abb"III>߼yd^)$:H~W~R`PPP-@zz祧Ӻ!;k>d?y>}DD@%%%pg}l6熇^nUu=/.rsbOKg9qxʞɇ>+y@q&utx)0Yޟ1]|=m>{8))))^w:s?ʖ o!Uu]DqbKO$;b#0$jӸ\۹Y;}Od?qhǧ1KgUj9p{ 6}wVͱS8ÉӆރӱJ`rƱd:v lmZ_ۏj8_~̼s4JY"5:dĵ]DJNq̙3v&8lY^qG^튔4>œpٿ r%'2"qoiǃ45UJfOW`ҷd_[?W|=RrUbϿr! Q^3@clLùΟZ j`a'y`|0u^~9^0qh;;Nx wyKaD~ >`6لPR|'* ǽ^dl8"=o(.+Z ^y>zlXu)y?ٓoB6XWPoKî_vFxc7 3DX,y3qd[ρVg>rm/2(mwKd 1of^c`3 !uΫ,b{:/ gNk7#j:ia=[yoyۓ=+)\d_7ߟiQS'jqra\`щ=:T p[ŰbDHتզ+~#߭9 ];.ԬhZ*ä'pTώ9N~WxNHtЁaÆ\6۬^GsyBCJ'>@OfqCurkYQ˟2G$'RJl&Ѽ:4Z{0ӟP^KpGm?gvR3l6V5Еba^:t@v>fZ}.Ocd'y@t2:FBj^5Ӽcz~Yg^֏Cy:1l*Oj jW=g}ڽ~J۟sw"iѣ?P}!m>@4a)Yi,6&Ŕ sܟEnlNldgs+M;6×Gq.G)/'iT"STDLIJJ[˔)sp+vϒ9_@xd;+)8Ĵ3_t>l6O arg^OR/Sp<v0*5jU7ʊ9?J:=\# /,49:u;?)))^fj[yÁr0b???83fg2 gC!""Rd,.s!k[xr."")a~͛7_xSDDsx4mڴ0!""%eY,X~Nތx}%3 ]"y4Re8S4RȾZ.LEDM&fLf+$Y},5Szǂԣ׌Ǘ?OW4Ϧ]Ădn.:=6<ŢgqPnD:81?7- O?^ `O9\3ic#\>/?ԝ6}κeD(&~gut=~x'eH\1wvZ)׎{ZPhՋC2_gy Av.Ra[l;)QUxjd 2+V8a1~6gmđ\ 㤓)iޏЭc!1RRe)'Ζw~` ח;sA۩5oBMZJQEw'߅#$䧆q-۩CF閌!WܵLzOj[:s%afaf30g?u8<*b`3]/z:pqm\d>&[ư;W 0g8q:3t:p_|8i#r.Ghe5k^@sޥoi~ԪW/J4 h:f@-WF8qz8SDgns/\uO ]7O1ōh`B#XqD/<#3ͼUg5mI5ned$P%#<DkMZnPH´aOĵ<ջF3*NqYg`)߆6U3|5 דRcw|/筊Ftk3o ÏhԿ^\$2<B<ƌKYGVj\V?gʛm]E;8z!SV>£7%q4GmmøŮӻYj%{7j^D9;n6K;4!&E_Y4;,:P- 1#?g/;Wol&YK ՑYcF2^\§^jCX;u˷a]pyu38u!SGH4lt6< Vν_vc*6{FH FP8еry-V?i}ʡ$ +] fEMskϽ3z>8[\'YP?НNsPspt?4_Fϳ;R5{on]*a F@(*nJ'ЈXԼ{. G0fm\s"ngΜ 8 e^41uiuk,ynjX^xo(Cf?bM˗ӻwo%d۷!"%мy|.X]\wDDp`#|n^.A~) 1M!"")9ä Ydh vGVx!SCU"bNqo(۷m[!Р *\ۏz1Qu< ǖhT !'䆫Э^߃CtHc~"%&<,0qD$#xw=ɠˋͲż7*G0~E8<> *3qC?yvJM#X[wøyv8݃\s;s-XD__X*oÉhe8v GRլ/8GNaK=āU;9v   Lc+ȑa}Z{le7 A+>G[UU͟\d*SpGpo}~Sϳ"K4 ;vC5 aeLmp=w}|q.?lӷS.Zzxxl]hnKQ0M|=ǧo&Z%2N pK9Aj#urq4Oߐ+1 塦TAxWyO,k !ln;d[ -nz)G M0lߍiv/[77T̗INz3c}4,VI"R؊ᢕjd'iW(1up\s/5c+k7$|5}@z}xmʢޓdFQI7_82?\:}woO *TO_3 sgHYj~-ȥʐ"ϭ؀&qqq}tݻ[{ i|ȍ(^Mdx rO/X]\DDp`#`2܊ݽDD$Pxi "7_D*.Ҏ5odbjQ7EDJѲfnJq>֨J4Ӗ}bՒ"R a;IDATz?΋rDD.)`\'=sm\P =v,wY"u3xa `Da5DX]W#"ri*6=գG@H$\؟cy<ߧc- Ǐ#m`%ݮ )8&u/GӰq$DfҥYb爈Pq/~n4k˗/w j$ ]ݿfl-}T]D.} ,k׮>1؁ bw/ŴQYbԡZ2g ȅtq-__r8.[DU_&CD\הQ1!"r *S""R(m:W_䖕l3oνW/5l%RhҤIQ7iڴ)>ǡ<է5NKDyov{P~gy'Nr%R^|z-a>Hu'fZnaܢK e۹a mT1P_ oK!RܹX){u 5jcGD ņ/;9UqAT!b]v,a<,ќHرmTC5܈Q$$$+>˸>73[Xn]t {AUtzq:d3ࢴKaQx0Wr^IfJKn+W=tKʟU˂9?>Y$}Ί+B()|8N\.iivxe;4hK]>.>]"NgpWX8)(jϓS亪~:Β'ܗs8IbI{utQVAz1 ϱ۝nOcĻyJܻ` O ;;xnt/]g[z̅ g|47ml f/ֿg"ErT)U ' 'zkyvb|\ mی 8 o=8w/n jvW|;Pq_yAedm+yMOQT28=͆§:Ǝ&W w0&6t zp~U\\˵;iwUk#yVg:'NZmr9!ȱu,O9QNdL/4a3S" U9×xp4<~pStgIg[~⋔Z9Ēb?2S_%99v97"S=v­wі<|LڙG.|جtdntR ǟ笲W֯As¦ *SW1 5k[gٰf;Ç}.9Ofݿo&۶7Oa1\sF 7_@ӽ0q,=d/u K^pDs8e+hYߏ=;^m~Zw RvV%­kF~AD Cs SorԴTl{|.]y֒oE7SK|foLI`ôI|s*7ځ3s5.pBp;x˷6uw}; [܎IHLM8lກh۩ʊ%K0v/ Y5ຖ5$sdz֤tbˆ:ԫ`/i]Jܾ8 qf,0MsjTo.pj2oI#f]1(R OҾC'Ng.mfҕ݃r]˪'gW0{j;Qڝ,Wc֊$0VwSf.Q=Hknݺ$G`-7[6+W!͓w~X].YG^Euh6u2-x_;AB$X[л/pj;x?J qzWЇ ^xr2 G)[7:]Mxs~?HAxņ\u" eXθ]姠Nu8dddЧo?N>ůp6t$QX,V " 0@yA4cg+h8s |>:F_$UuΙ1g3BTWfMp+yG(#(CfXe[rvi7Z!rX|9ݻwϜڵk 223CS_z+3ߢ^btRvVO;r_}e %%$Y|xS"88b?AAAp۾lV ','o9Á@[g ={_LD|pV>`ҽVBDqSÙGbJ@`ǰ#q8f͆j04Ϋ>˝%)bbb|<""":'/%}&Պjߟ|FŲ>ogվp?r^s P;yOm(I̜[>e `ۦ϶rߖH 3 =,ł̳Gդj4va`łIykOP(<<;vPn]}N';v <<}9'z"" ~9N^V"""܂bܰ0IL}V[hxz86/ql5֗Gn=%G^rp+9&=#{l;uf8CP`\8_~-@-{eܳ{qxy܊eóۙT넹7 rVUn!c찗{xeٓDDw>N/i [yn[f9""&Ⱦo[v_a+ "izy]r ۤyr~g_y 3< g s{>[e0yW[o$rL{nΆ 񼄉/jJD`5t徜[x&FvGf'+IENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-edit-gps.png000066400000000000000000000546421501654372000213140ustar00rootroot00000000000000PNG  IHDRJ3 sBIT|dtEXtSoftwaregnome-screenshot> IDATxw|T_rGtЖU*"CT@A[F@AEDEe ^*(gw$-ЖھEOW='9=M$wY ѾFCAϳ- I\zs_.| $F jTEӢȷAT,LLlɽOOO!Virݹ,k0-(K+ b&=ÄQ`0<b!>! ;YCy:fk@LZ&'nų$ZgKUD;Ve9DPb3f;<(mHSIhqpA &Ӣ$u! D\\"^+Ռj1Yђ)k 6#:]EQЪ*?"2G1$=n>N\)$wgΰȗ7,Cq2O_t-劍1 'NLjriFf V5e $dXFFckF#I d1[@謬wà˿ShtbXPEQMdT@xڣb)༯$^ rYG/oKDUU@2eGˉV&N(iZi\A]LQ09Q%9*e8VJ2ޞ.#5D]xVRI_'ÎSD3x +bb&#%p`rވD9/q2`N'ٱ~Oc' 4bД +1(u!Lײn4raIØAW.\\I@.AI_c&;93Z̤$nƢ>J$-}XiC]k:nB#h쓮j&!"D+^6h’I1& Ei}&[v(fE V14;e5!Ju–Lbaz}4Z1A+{BW_cRr#^o)ѱfhv*1)$3=n֨'fiVP% g-<.2SILhY`m&%.EAABFƔAIYgzL&Pk\q1~lhF}*"U}EW0O\[5W@@t܊YٖѨV*4^zL;69Nr i P>(| FQo0bc&n_MdwHAt6ު6>ю"Yn|g zHr-vvjcN$LA4QybUc&CMlR˩U1 2J;L&Sܯ.Ło˗֢ R#6X"2 C|(bF6{;$Pn]9@ Nm7q#FBNi陴/e.#ŘyIg/ NLۛQ F(e`kL&Zm х%={r{"`d!5NK#xLxx!sg}FH196 d UU醴dfNHj&onj90cHܫ;`ľ7xB03)&;X)˧$qRvv:49 v<6j2_\CAdfٟ:]_|s KQp,O#De{k,i)X|޹B@S&1nf:6߼)%"Ős_P6o~Ȩoo!4UU;$K$?MĊ?Ri8ef_y"Hr~ķ[d$-XMe \lrwX9\o4Op\ʗf5~U0+ 1hiNs<>>xmt̩ݺP3SQKHRYz^9R*n*ɹ荗9wBCG +t.^E썝r>'i!M)WQ$&"2 kXyn*d,c,'1.ݦ7Oaw e5\$n޳)'t):H֓Ll /j`2H3ض=W8dL%?:׫KlH^ wz>= 6O8 +Xa̅lj[j}pm[5BhN֪ʗMDuJ22qǓD8ԋZHaƜeYIl$O-Y0SJo{p&NaFz;S.tY%&ӂdrB1]pgRRIB~溔B3gw=ut*XWZOt68;s’X'`)_͆]336NwbY 6eZ-6V8IlA縚aƢM%>)&U%NQQ2>0z=irGE5차6Y4]ƽOWqgӵ WjC}a6| T8iX|Hw KR:STtQrw dK|:cj ,-T15k*Y,$N$+hzх%L@Y)g&N.Â#io iN[:@O^gР=L| L-ю9Zֆ4/S%oNCvWt'ܻ_l 8e Rݟ(QQYTbKV9py$ 6ٶ?ݱQ8yuXfމ7:64-2DE&!f2|k3F5q'DVH&M`kd70DJ:] *JU cW%5kм0z%L(Id(;v:@qwo譱u+-V^31ӹq%ܝp0WNV]|b D(БQ<)bhZ,趒$QA" " P," RW/x<)WXA, tY gFow;a(,) 8ߎ\gЅ!6AZ!#ؗqϗ|{YZpB'$ԂҔJ˾\Uo˴hJ7wa¶bՕ 7~f9.tzHx\Yr7z~y kj mՉgC%,OeUb7c/X#䳙;n|>W;4rhY<| mDIY@ZծOeb/<Ϥ̸ޭWh]ڏXd1S"9RN5jA#Yt('hrVK7$CKlOʑt8GrK>iǶK#Dzuר9{dw3hcekO`Γ~gg 6Oݤ,<5N`9ѳCu~0,S"{g!|;W^_"Ɏ63unY˴bѬJ)v3dr6mZ'pf>@a^)}wY'%r ]{&g<~hzIR~eLl\lТ}lji0v2]p?*ћb}ޝ);R~Yys` O,ٛbI6AAIvrIVIOKG F= +wXbm cه5} fPiNt\nr荻 aRnCTҠ-ך;4aNݍC XgB~tc΢!tSzbjl iZZ=>5MꍛEP J\48؞wVJgtwؿ4xg=^{23cUfu>1G'#6>\\ϲ_j d e|y-wY鰲sůj;ͫ+cF C4Hh,׌Φ-WCT7:ˍgRkk؞rz V ﳰ$7|4[Ȱ tsD}9؉|ָ C7LN1Vg*h^cݒ }-ndEhٰg}KxIĀҁ7]M #e8`1XقVp0շfVX_[MG#Iw|lq*o)=;lY/ x4뛧"P)}7ψ7ز_A` ~Ѐ^eSY@3OAZڿմ)_[>A !>b~cAI%1 VXI`JL$[l%@G#xf~_V_·o(0بJHD!L}ZoaK93sjs_#S'dr/(|(_ ~r*|>3gXcD; y7b| @ =6|sd_= V36s:496+2 yj\tGA>5<͒NbymZ4ۅPJtR2d yveԣxwXvA` It/o~m^Qr}:ކYguZtY-f4?eKNͼ $A(ǹPs؅Uxuƒc>ϳi.bɨD8z{&C&Ex.F7e4uykRRPgYS|j\UvIOD;Qql]-q),w߅Kٸ'|ؿGsE5Hf1a*&_#.Sw9jwO $P4t@RBj#?~@G]GJ$T2-)w1*6S6qhlBW B"  }}%'X@Xĥi@"+YXӨ# dp\I X[q*ymŒ%pVG.F=zPۑ) B1AXDAEAXDAEAX} }xbcbuQAxH\p'00H4^@/^Ddר"Cva@%AD@%'%Ӹi ,(AV#=~.R@Ƣ@AVEJ/p_YDWw"\^CUJYKb;g32'N5;E,by*bIgۈ|Oeݑ(E|,761HedN@ ʸ+Zv;HvxQ ޝ,#kP36ػQ :Qo\쬖K|ڮvr;u V|yOA!amc^~C6RPZym*ߞK.Mv|f}qq#JMz0i{XRTERHҏS;aN#fow\VZ8>\ 3y8+IH[S󎁘r|19ͩ\;͏Kf1 R`D܈`+{u@@-Ѯ$,ĘUT>Ϙp*BRt4nNt⮟ųl77vmͰi,)D_>s ~eT׃R,l1sGup@.%ObuaIԨU СL'6|'Ns׻UC K8|@cxN-_scYb ~8{Y kz.Ca>W;4J`<| M!9p{ ׿J*+>Ͼ%eێr dg0uWZ]FTҝI.˓I'@ZnЄv=2oT0btnFKxph,v$X4| AA8dD䳝i.܋kXՎ V98PN-j7hLCYGb'Si،/dc.`Vm)Lh mIN~ IDAT=,ղ*|G{cX4zcVטԱ*>{&W^ǭ^s???y񎢏= s{.qxsZ  yǤ>>s&:mz3vQ Wl޲X!|o7`vxw%a[uKу᪤u[xvF!&9U6R|pτ6 >l$\=MD(nAZ3iW.5 GroMK̛&F^Z|NfQoH݊Z9E] Yg <9? KJd-NTӉ߽BךwV({?od}%6&]ﵣ[=d .ƒ! kiP7g {9NnI"Mk,s'{]<&GRTj t#&2>KF[&9o!=iH8g9/]cIP:t)C B*&ܱo7T}y*UV ;HϺNPV˽NK'Zi뱆Mv0IpI/O ׍tz5-777jp f|3e-ObJ nEyqA(Yǭhެyİp_}ā OQ&N1 D(i" "(gggEA$1"O1YQgg"D(gIXXy$] $QQTgXbp_@RR( <$8::R6(HDA| DA(@AbDA(@AbDA(q#p_/^$99ؘG]AW77 (R>qP/"?P;wD=CH:hJNNigu1A(>zZs"Ht[ O׍.1/ Sx  O!{ȀX|riۏ44~rמXw| | ~5Q_y(V\pv:p-#sepÇc20L >'<@${c)Lzc$шeGiۀ93I7O?<ՙE;~-#nJ( K (Av@׷/}`xH W6˳Mkw ekwb=mbζиr0>Tj= 9[0jǶJh ^TNUX^8{#Y63w+Z#q:9*dY1quLOTok }mKВRk ?we֦֞eJW f5cTNdPT}WsDj@f-xt&v$ .ZR1'Kb!<۪>]GN˅5:VS>cR6=u$q,>vG “%GazH-38SS&?g֌tk-;&7QJ^\[3_݉3[Y4;/!u:pui\4{oԂǵ%r DLZSר_9f4=OmC˪wmqK%ܗan;'k4MZ̋ dj),fRMsHa;hoa放t^/ӚTzy f |eGxsSB+^%}sZmO )g[.[R*;u Z4ts܊2qJ>$J5mAeef} TiC [4h܌ph8c_n@?|%'*_mg 4x2YE/r ۟jq %,,;` AӍ *sno909XV(6-gf}8K@``7#+(InTkٜ~D曹Xi8{` ФL>fFw*L<Cd,vlDU ?oD%\NDZDr+ JyyhjtGEbp+4wSx&"oŢ`'Ͽqȿxj h֘?8~ւIwfPteQb9-kRPjɖx }&eso[Y<} cS#PJ]VL8}&ĭC+U=K- boЀ;2> 3˃ZD-|Ϗd7*F}W~6&&Ҳ4Nf4odwߋqA(QE*6tXYIN vwz4TX$ O,29H((Z%ˑ7MNe0bfq\>;d_/glp/ܙ*wYUTUO׏0BJvſYO-AR#mqr>ގy`RߡdԒaս^fAITl<{^`ۭHU QH^xܽw#rU3B=|_c{-dƱX:PC$W<| #Dֵ2 Iyfu' *kɔt'`m i$&5a`\5ܧ8eEB0 e%8ޏ j Amڿ0עuyPC٦aSzuJvYM귷) ޔ Ж~gf-md}6Gj52h\ ݿpZʄZv%_>A݅| 9o:uЧY >P hKyis3QMm \W~bط[Hg?f y mN/H7 |ߣUpP>Y21V_Rv@% ,Fָ%]"3+zʡLxq)CZ)-)">;-Y=yn |Ǽ_eDdy.m{ijAJ*C{bt02cAdY>v V k= 6i?tͦ:!4l-ߺLc$L4nXry, }t=.6+ja=Vu/G-iy#3E,4Gt|  /Sؼړfmdl$x4fD4,|Ǧ5N]P]Cio)jKԔn'^#t%Zw{T%Ǻq+,~&]m`j֪6m42 t]}hKjVɾJ’13o».YQFA IJLс1͕F*FyH/2s&?zʚC)J*^R7Эd|>;ʜ&In=K ڰgZ`|'2?͛z;ORȗIbtQV8:b_ON|l2Ƅ$L=ToYs 8~E #jV!k|^xg*7At,E$މ.ĩ╶jzZhY,*ǎzժW ;zQCb$(89:m"υHJNG N)R>@Ab]X B"  B"  B" xp_/^$99E"O1W77 (R>qP/"I2kzE!;~0T "Z BiܴE)“ZpHyD [* 𤊍-Rz@j| "OTA0xƛ5c0ዳuYGYχWNUE͵8<&.89h:)^ɔo=a\ܱF:? %hkG_> U.⧽*?aݡH(â$p}>u2ݻ)iۃ8UoUΰmVNG[J@PH8w%*3+Ni6ϲdQ^,b)ɥ@6&}p'-xn<~V䍗,g=?z#ҁae)&t8=!_Nd` >L79U*5X Z =>j!ܮkW?vX7b˓U@cc0cgs$-Ym0{^{5 ߏ//ב~o shR4|5oV 6 hch SNs϶U<+ * Xg{5]1cꑄo/c3۶>e(S-S*.dmoT gw/<Nl\_C~ܺ1}]t~iS~HU8Uӧ,^dW0^;#>!ThFgwy)\mZ2xxW}JI=hVepo@͌݊!x֥uNZj?wSm)Lh mIN=,ղ>/yK\"-X$e(X6#!^8jw\zfnMru'n#^Zq( vXi51jdhY=wRm0* 'Fx:_S[#~ԟ*YX˸|0w5l/jw=~> R]票a5p3Gr&Y+U?銗G28> /GKpz{~~bhZu|a\crC_@вe9|Hխ[}vS;?-X^B*ɦ_l~g L fq]{PU3tPtC!J*%M~ 1yklf0; Lv= Ƴ2wy[:'Uux~;' qԆ M;r{ŬZq^%;n [259:U!'ۦ#0z8XVw{5aAX6!XI[X4i Woe_B~$^e8VP/s&?@؆_c]d=^!ӺC*f#L\t , ]qˢ]59XN2lsN&D p- vY= ذ)=o$q˙3(w¯I|, cO/CȁN%m z$3eץ1r<1qyt '㉱_`2uay\;wftK4kT)8U͒gn<5tX?_  h7%Leo^3= t%ܸ:n;ؼ;u3b;ض z}<6CdXC 'S n#=j_s2c"]X/Y@17sh&uh˳Cfhl=zK{V;R1 vlq͠C~:˅oeO''D *:[6?ڿ 7|YxTݱ,;ozrʫ K0SLfL+2U&ZTuESO8kmvI(7Ww$G|bMy}_6o Mly>wo*ㅓDREvwl1c9uɬ%[2c3<0 VV/ O7W9O\XVn\@ ?a)2ܬfoQu#-8o;NöEp5T XSǮEnn~ NP5[5){vJ)sNrsmm>H߼z3 FY"W {XOfN\Kcy]c 58єjurz̸ٕ3yI{ W{/;qsR^<|)Q?ZvjJpcZ&?Oy[,>!4ڭ2kz?}`ߟč=vp&MʰTt'$v[c_,?-OG!*|쌂98_|:HkjP+m+~4LU.Fm()#;c;a=˧弲a;l; eC=<2vuc%ekV}vWwn6 7I@ ym4/fҼ ?Zu+CG~@zx=BVeų;Γ%E^qk3 N<ܕ=H.4s_[tX3 E0~-zj:=ZLV$ !5[0Ć*V}0/h"|^4K7'3`qr*~6 ?BZT26CèOj1}8g@ejŴ|(/m?]t<} h@h@u-*wf2Së|đT 㨑|22߽r곕rs"ѧv̱#G\27Rfr2뤝?@c 2|27kycXa5튔ɺ'l9’- )i^xO8cfl'aF햿 ]H Ķm,:kDD\.l&00X R(??mJTT,џ,r7n߶b,,v!))Ү\"Vr 1Y""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""bĽ+ w;.K*W*KXX{y.vNfCT+[`+3(6XcKT'qqX vU(?m\8""v㙮.oŮpOoʺ; ,r]dZ9\q4l5_C,~-4U*)% 8 g"!!6=P6ao "(8]X])j θy^:׳)@DJs*Eps ;l$%#:-kY>/k=.'M)My<ƎǾ}y~ª}?g|Q׶hW*^Oh~ݷ?yD ;rh&MiӰ\.dE,SR?}UP-ȟ샜8Q?ub#?p9ycA%us2=23ǹ֏&#W͖ʹϙ˧_/ SZT@PH8M?H)Οq`(Vg.׻GT>ގ(v@DJs.o//Z-"!>˲cѢ88r1+!F#9Վ=@GԘ>~l]"իK>}HO 33#GR"W;z O'(8l1f{kxp sXux␽m.]oP ;=yɎ46 F<[J Or $}jW "5?'e7 .ZWIcc;ϥe$f˛4}5)`R?nnM1 Y5D<+>i}jymtppv<S?Z@o[mD;*6C &%$YYY$$$GrR #GrJ܆m066A{+)}ʅ3~#`c']7X˗|=z1w=FY‡oⱾ7sh)>w2iɧ|<^~ݿ'vкa8Na|k|tG& ;bx_UJvn}rvs#0/8N߀UЂU+1z:?L R\YN_Wk> \ ضMVF6eƱc`Bi26U.el%ى|I`a`̝Jnxw3W`8{A^HٜܣdT/xR16(Uq>t'[,+놰BIMK{IyapVÉ(o$;R~]9N;6u5{cE wv:ʼ&:ĭDu6H):uR_#55Ή&=5qc牱cIO ;+ǏIMM;rgUR>'ϻ -lytoCT4{p8uEֵ;ꜵ~/FBDY# ~.lƾ([ T-_Mdg[g&P9)dX<@ u u+8?k b|LLEwRNbXO7rP+EӱN9.tm $ "Bѫt]7I{~Ǜ=zɵ#^bwݘ~ϭ۟;TLk,OjY̨^r[8W裸i$ҷR?fd D4tqx31ԩ];ȈpzIx[*3/Zż~8o'%&ŭ -H;{wؖm0a# pˀ*ufWC{ұSo9*uҷVI;3xRq"U;z,KvV[^!##qddBN'S=!QlC%hL^~0EV_Àd\3| qN؎FW^;e 8Y J/WEĥh\.>[-7:M'~ƚqι4ն9=*峈;6Q0]1\Ƶ-zKXj;(Gع3. 7Oq|eѽdۻ\DTuTP@.s<%%Q.,~h{ꇶޅU )zed)@DJѥI_ҮaUy˖VM=+@DJI`` mcYAra6.S<ٶeYhES|)@DJ۶o%*ˢHjovloێT`ƪ͌.nnܸf73-ԫP%Rz~۽$K*W@r3Wdl=2S<0ebSH(""F ""bD"""F ""bD"""F ""bD"""F ""bD"""F ""b=NIENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-edit-roamingchannel.png000066400000000000000000000666041501654372000235110ustar00rootroot00000000000000PNG  IHDRcr9sBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeMo 09 Jan 2023 09:26:52 CETڠ IDATxwxE^K%!TjHBNzQz&(M#7RBiHH## t>ϳeowvٹ=P׽mwۖ$Iro׽m7#@Sf%Ic)og5xVx9o/-&ݝ &KˑC$I>봊2 YچbvHi!dl,& %Iҫ'y桤we%T[$IRadwfxBnN[$IGI72gҷ^D~[ ?Jd@I$eWF*^ 02%2Xm2 (߶+@@"<#Iҷ2`$ Oy~l7eKQ eB(PF>䓎իW/fccS*E#Io$h4N>4y-O> 򼕔6y )-WOjp*U|w%''c00LL&l$IRJJBѠj yҮ]ϟ?LJ0I &筤P|.]F}0A$I'd2a0HLLκo߾ ۷M}Xq V%3nݺ>}*H$IRZN=zZ@@)-%R -$-~]`mڴQJ$Iod]]\\{AYT6:@U?rȧ2$IRV.]ŖK )C-Q@*UvI$IQ'O宻x&d?j׮]+_|2$I Mtt4c0^,!...nժUɋ /6j.nQxQ`A1K$IHrr2qqq$$$0b|$]RRɨT*ju5,Xi͇82Dѳ/KBJ?9VR_y$IRGll,FFbccr~~~EHOFYxvW];m%k,@$IbbbHJJzҲva tꬶ$I\/.. 3 Yj)i4 /f} Ỉ':©̾Er^rrjv^3$oxBYuJ56ESWcŏ˾dݶx"_+Roqɔ񛩌˨4ّ,SWt!Iz3S6]$&* vθ-N:MiѠNӦS5.yJ@<@W0 0L&ΝKᾟ3g0bTh4b4_5ҷE"HEJ":"D* :Ablw/r ̸(i*ЄsR_5eIuT*Wڵk1 4hH kҵkח(MRR666m6}Fcӯ`)$_BwYh[¸~h=Kל fl-%ngwMpCP^%q:_OuxIݨW:M\ݵM{vD26ũѢ+*}ګA߰y=<8X myN̆w֮] FyFieQBt32.I:*kW4[|;?s[ ʼp"poC“~##l(8+ޖ OfSvA2jmu>/or7fS:x1\X13`Rpyt*e IˡԣG^L^-/ jCG÷/V B4(AMKXrMʬi=)g _iy<ֽHVSg2֭[͂+aO~sOAeP F/bɜ_yV,S^BT$k !R_XM\ذ?]!\cp6 $)W sz6J:#IDZ #Ζ9$)W9/$Q$B$֝.Iu&Mg˔r^v>e I$I $I$)W$I$ HqlgLhǒnٳq_)!NJBceOi4gy䓌/+ Ju)߀.r~ [B+5X٧,}vn<5[PDpbN7vΕRMGp#Xyo xT*j-y=SE~c~"#5ܓDj@6זӻB.gV3a$c'8o;+'u#s:ʻWD=ų ;̞m_1{;D#Mɣ[:pN4 H/;FxGA&$Hcm>{w͢Ѵթz1S/Dwj;;|ɤnq넝 d琴%QL[=b|7MyV| }ME%8~Ni1m:=Ps;?Y37Uܩrӧ|~(TU*[gݒY6.nv+QNtד3!ԸREGԨA2/ºvVm1TμQ[=U~J֪C;tM߶z̬/+rTrtz9֯EǿI$[!<[1o87_"B=ś foJޏ0SEKؾc+_ *Ư>ӣo7-BY;Sb m[ۚ)~`ۜ|H׏XG:W%O{l2p{w`sRVD (ӃF8.iHCxNhyڣd5rmZN'p/,?]$J Grz o _9jh_~t_b2SRuQ {KJ5gX5]fN\5_ŭ ٭ʷˇS29Y\LI1߽ٟs2jYv`mjTJ;D0=bY[&!lKeÄ8ɡ 'ʻ4*W<\$6)TjoC6sIm-GǞ^jxZ-HeHkE?"8l(m+R« I)A)j;.0bFy2=IƷ_Ow0mmVq;#Q绯bb ܰnfk$]>qHz Vl` ڪ^#ѻT21i&E{MI7-Ppv/E:8TYSͬO|6Bp,PV[YNE1}S%LƋh`ovbO~W[nO :ۼyft yEw)v-`d߇aP8ZU~1WxȎء"(ߋkgP|1ۼVcig5o4yD_A+0a̛u| X9VmH,( )d>i& PI/ͩJJ?ƵcNW%Q!PaAaQΙOfy''E$MI^N$Id I$I $)ב;eN )_RR V: c^$IX[iR%[GIRpb@"#יKR.,sre2\eP2,>pks^xNWG$3UPɖe IٳKsH$IR I$Id I$I $I$)W$I$ rܺuhz}NWE$3NNN8::-@r[nVTRNWE$ ޽۷%d IJtt45jHggg<-esHR"$)ˮש $I$)W$I$ 2LA,kY P@aM8ErNT㏕*RBsRB7o%L$%$*XkG9P G'jH|N5]J!*!I!ZH^pvv&[vv~cg`ٺ1:^mYq壗!p 5\prJ7aU)h<*E|ր)5p-؂wO⻩C /Fix![wNlgk*WlX>{Iigxxp6}*Gk)հ_¼FqW2q Zc^ɦ~K:_ERɠz2*G9#P q3mjSfc#_"EXrOnܹ鲞RMAUa,vW4#tPڀ9XSV N& M@nE> &F{73:e*/Em̖8Ķ: }^S(]5`.PL֗s$>ݓN%9?bʋNY3坡%wj;Vs*Eۨ-1{T'#pUk)8vafކ߾'2\N%slm%J:|oDg+?L!1݆Lam"7(:Z[D%whp-5N/%ʸ +<µx31A1VxlЕHH\D"&YX7łFVW*%F 1[~Qa+jEoR &z^,'wcn}aeD, z,!D{;Ef"t(Bz8hhblڲeHC-,ڭ:)7./-"vsGa-1 !L"bu akP,ØQI$pN:ϻC!njFwch~2?۵bMlݴ8sa_jij&(z#ߙŹD@rqn{`Ɇml۰no?L?~kmyJ1*>"ei֫cQת: 3=&"ܚ~z=a}[)cGS)n/0$@³)DC'9y$'OcZСcUtY|,?Aݟ3X٣ Fba誷Ia }ADi; J(hOhC:QrJ>)-茓{ ^ȩȴgLNkooAhMB0'~9sK[Ɣ7]80\ĩ8kBeԕ?/NpS@ȠSR,/g"Q!Mmؖcgwsٲit}Eכ)5g껔작+1;=?o IDATl'%RB8mXo0e5*f\-E3q,zσc)F&_!4nͅ})jfJDqbPj± N#/gTH#%LiF"C1 0;dB#1C 7Wj\]afM$"HI'R.ԿT6c|P= )'6lV8r&>V}h|2]Zt"7?s= Ŕ~!yWsd$ eC,jVvap#PUfLz;ZzWW3'lU^5m,r?TjW<`opno$,$Ճ Vrlm[J-KيdW2p[ k;Sh/X|%oXYJq k6yZJ,2 5U~ ݼMw*\˔tkv|pJhq'-ܯBw ӡxjjB,) 谵M9@rWٴ>GbgqQѯAZAɌ\D9Z mүWH`` 2K9u %-剢RIjuhQѪG3YlX'rK i_-O!5`nt]ݙo%O:'GSŽR@S5]~MHvetGWknf۟;L#o5jμxu\?˝4"68a]iu1O1u1:4E> 7~=UFѥH]@uǻӟԎ)n)W~tDst.9W<ѯ1ڧ&r߷+h?Ҹ1=d5|#̥0g ^~wf<~uoԺ)M-V N})I3Ne;+SmU Lʞ%}ȯ3rPJ^Np epGķ#ͣ|j=:#OMtRv42gw`w+uX4O<ߒQ*;fUy<7'1x$:%O&ܺҸFI1 )CҤfT%skh}-]E\9C.ke0lxMT?T!ǯ%\S l¼08@InJƍ@"`&))Lac)\̼f{LxXZ['@GĐM$IcqC=[dp/[QDiO>CW^Mr%(YmGr$1d )Cʌ19]=3+}?aW,QOr$ d IUr $BvNe I#ϟjHse'*>>>ܾ}s:$qrrl)_d.IR&$I\A$I+@$IrH$IR I$I(;)׹urط$2rطr-j5*UHdݻw}v $)WQF9] I2lyIUd7$~:$I$ 2$I\Aҿ)e-R@ (L rV֫@J= UbI9P IrPÿ0۫f`I=ȁ//GщaMdȁJHȖWprr<+ͨcٯ,%z5df '>W[V~eB  eaUȎS:yŰBSjZ-, pw{wS'I^.C-بy8U ".(G5G }2lUXٻSa?8˵2 ՝sWb[ҟa)dCA*w-r|6,ONԞFc8B@yOq,Pf#6q#l],n`('?ߎ dEߺx;bC_s9ּ6=ǣB;>K]IzdK (xm7y2z(jj +;1~&jJA TI6')NL[f)xrF‚C1bδ.fO鏍LZ|DaF]qfЦzB^H6]B_wo·bԱ"̦mb"v&yczMwԀ Fκ@3Y_ΑKdvO:9)/:CTfEWϔwީmYt4m8QiUɧ@RLuC|l]{>b|X<ʘr9& ^/P5z^!~REڋu!{,_Z9&Emb:Kq-ryG:Wk{?;&Ջ +ں E^/7Į)]Dn^k.For_j=o&.:ى|ˈ:ݗz/z[<_'JϾ&"S=wH{(SAX۸vx#Q]8hFg'\K%(BǃAo2nv*p-L>~P(|#=[6Dl*t?'#=I~/MJCy(v_TXyZGBIݘ[_XY5K }:N٤<6^<Z"XgK7mٲEg !zVX ėJsSW֋mR?kwMuD -lQSWtVldQIO#6ub};O{`HL2$ &_`S꼤bD1[bRpki Z )ģDk1/(uSi'Y\+<q>BIlZY Hl)5^`,ϓӯp6$fxVd`0NF&@DqeM߳c|5AM"syYw;W3]otnĞDٮWlb릕ř#3e^gwW{&u-4F,%"[s۳K6lcۆ-ve}ej=0[3hSYΦoWِ)KD8̅ N½Vm|Y1#F f՜ K2ȜMq4;NDǘuy !ĄM$̞]}&:ɓ'9yʇӗ9=Ͱ!l$>z[N݉]s_w}F6E?! >.^ 1\|#`w=Euu u :^zr*2YSӚmFHx{7B#hy8Y̙\hU4Ɓ:'N=ƹ^*[d]y8u|A T3>h-͖M+Laɚg34ɉ΀Oz~ Œש<w/ + s`[z֎@yQ#I:<t`}jjTu,gUM+&pH c +wPMu?̟&~}}vAB,s,mW13˗qhͶc"?)9g&oL&){<H9CWߥ]Lo,*#wWbwؾ!NJť~m q&۰l`ʚي\M:rŎ_ҧR*M&췻D"HLLD`"akIW|6x4ͻ[saw_|Ռcb6;6G^ΌLFK4 +DbPya*Ww\UɄGb2nf!LhVHIR.Á$9}밷}ʉ [n3;*δU]ƽ, A͏|d#nB1%?$j2nH?vD+$o6?9IC˧Go]hX$&x?TS0ގi&U& .[աWM,V1 舍_YˌE{?5E0=Jkp'zLHjv[|y<=^܄>sgVa[MڬZvdlc+:͈{idr{+c:cƆO yoU]RZǼ U]Qa"A<Ղ::,El34u&"8<V2vzyJN 0}Nˆ1\+KKқ#GIЕg[m;Ksx2ZIѐϻ*K P뼸ƱZN!JKQRѐq?5k-_X<= Z L)%L&@ztlWU;RD:';dET"CQiHGw]U!)Mq< "LPX"}#>ٳ3؞/,KަlvYx6f 3n7j Gq5wI,ɹVGbgqQѯAZAɌ\D9ح~_r90@/db Nmrz@JZJEAG+ zQXw银Xg +i ^JcxSS^'R[BjͥƁ3 #3P=oVrHxAؑ\V*RhJSr18x\O(!?a(rhDʘM4]<=fMasc3滆Q x-]Ct[A~sFˆ%?H2ThԬ`\5}z]Wj|6kRE+<}\s3t%UtDw/`G!(y6}azB_7e*1nHCl-?ZIr&.D֙ϛi֕6]DŽܳЄoV9MfYhB})3 mY^ϞM XD%G~_2r4ӾFɨ)XlSolNeAHD\]gЭmYf_SX6?_=6`QZZzZ,CfͲ<"%b6mڃ[|)[{;E ܳX~Da:kO6_Wk&w@kLc%G&;W|6c2Z/"GJK$3ӚA2h4hKDھ>vo@7S4(첂ݟ5Ií3įlC:(ý務d|xUz;1v9Aōw1Z0OvZ®9m}~̪xoNbItZKMu1c+屍#R>I7i& PI/ͩJ \ՀFk,j t6'^+a,hϦ{ 9=rHҟR`5`y:tL?c֭4n8;99 . l2O9}o(=vL%̫1lƸDŽu"})}D IQ$+;I r,wˑq_a0rkY;J(Mgrj߫Z%HNT@7l!Y̘ H*ГA=ڇa}UU: k!IқB\) I+dT8::r$I8w^O]vR۷9x z>#I'''ɖe INv$nN$Id I$I $I$)W$I$ 2$I\A0[2qqqɖe IPXXPL ",, WW׿le'IA x{{t57PbHHHȖe IbbbB~'dK2$?J@r7nZSDlѿ %u)me@Jwe.#?&66??ZܹsT#'2af'3Oy[GwM}ǿ#eUPf){R"C)*DE@ ZPPP+(ɒeٖAHyK IDATh NJsgD>>bq~=|gmL;/\Jݾ}!yXGd2e[.^(wٸqѐsXe, UUHb+69Zb1FhI}qX%W[oY5bgVlP=\c++y|8Jo|b+Ʌ pppfic+2s9EXwTC/%%`cwr!1+8zPlQIuBq򫃟BClHo)5c ~ ]( ޭK;n}| c?o S!m%*٪U8~8#G!@˜΃YoIYH⎧qe$#OU@%!37"fwx5F|k|{IjϿgŢQT#qEHgmYӆ%p}`錽zrf:96;>BNcيy4? ׹; ?عs';^Ű K/g}vѠA(VX0*C4X+AY~3zUlcZDϠ^Sٗ`m*|H>Kj__OP'W$',E٘ FR)}V/JO 8Mv/2iϬ+G}V/Ǟ||5_@瀓ĨDf̈gTCa7/r)5l"""2?:==fCV͞T?}>tjY)si#L}SJ{2g)@jpFq#',9aSRct|'}z@,}nogx#дW3`7/bO%*W~cWWWӧyjKdV)Ii֩t]5/@EU[ԣSlku?N TCY;w97ױ+۟kѭ'-';i{-absdΊ#hPY֊Is/G$bW!b8j5@MiѤ>w2ğ#8 ShQ r$ǸQSo% BUջ!yMST '"P)=EAgŭXs85%ƥbIPZ( ~?$4o +q8Ϟsy*d1;B4lZs:O7 @ADUlŅɓ'ӷo_&L@ӦMuSmU+1 uw;bw(!'ٲﮣh?I'8ÓT.>Dr0E!.k1Q0ZGNx-ne{X1w-sAwv$+NXfx8!gpɛr~1eW95%T֤Odd$,@Ávہb1١CAUH>sӹn&7N:1Jg5zVe|m(ߎ.n 8Nb\)aF/zͼy "(((OtRjǐuim=݄ɪ v x,ڧ=í`ΰ!U5P V SS@Z  dDwhС1"@R;ޓy v.xxRX_7ʘ6$B 舣#=ap eOBB:80J>tqDEozٳgӴiS\èwT5x5JQeWQ3 ()a`H@ˏ2rSjU*cg4]Ző`Q.EI+tFs>Z4^spOADnmFe:@/Ѽ޼pΜ(E>ߛzcHÕ|8;n uBXn\jhcҦP-G݇;5rHR kKsSޤ|aͲҵ_@uuj&>:8N]0ZmMPvTɥrY1H,E>2t+XL[mPI*7|:Ym1Yѫ`&~6ݦ$`*ېΟî%&1bhμ//X /OmKJ;!O/K2xugK&.8TGO|0|Xn ֲ8``'`Yݧ% >~%y&->e2o&N}f]i܋ހя߮)pA`qU9Z~iۡ+A L8!4NdU^ׁnu@+Y L_P8 7ڥrP2c7C/hFŀ(Q)wyۢ@e(Br0-viyG!ySwwwlTSpp0(\J^ȑ#yqGAAfsR[Hq$ddF+I'-$!PB][m...(Nq$kzEQpqq%x999q5/ni( ׯ_) $!@Ŋ#$$K.qG]Q@_PϚO(ooG]!ґYvB!4AI!&H ! $! $B$BhL ubbby森( 0LdHB<Μ9^zꏺ*ھ};;w:ܹs6 % $!@111<3BzZjڤ.2$Hăv$4ɓiWWW*Vh xOH MN:yZg߾}6 &ӟ?}2O9o5,@Jm.\ESLn%hP_fLz5=6NyGb2TlG!dz6@\~g&rt)Q]>\[OjL-7n`ԩL:7ndzޖ4Hj lO?Tb`lT-M%(ДNÿ#8re%t懿s!`-n޼͛7z(6/m4w(O0DkFmaL,XޓL= )}\J8~U?!@xx8ӦM#""cL6f22suf~wzɣ-.$Tj~⁙Op/ bLkǔG)2jAN dѯ8O2N~6 $rיLUV>fN=}US>b8sK&^-}%%PC`=;Z ذ$:ojwI pKy#&]fL^zTK03s}MA0GR)\n_hOjɓ`(U_hY {P-Y,_֙ncgt3=>LC-D!lI|=4Oz2?|7z2|T{ u$m*e0@Eky*cC:`1LZߘh+%9ı'+}*<2/fw3^ڏ™,ُz> '56ll6eˏK4KsOvӔ0Ʋ:\m.:pI.ŭ:aƞYRioGR&ӛO0W7&_oOqxl_̌i6烞/=tFҬ|uA>iW #wsw`971ڭcS/P]Ͷul'Dr0E!.k1{':%|ׂVv3zR1DZ;oP [t\[<.@RUTts8]MsJIf9ȑ(5S f\|b~<ㆾ:8g9$lΡ(5F~ Y7VfM`׀7΢}:J8gébX/2@:t)%60VE=I7șEtCx<,KB V)t9?ulX?]ʂ(z1ݬ9U ?ql(^RTYGU/FɪVJJ+PgL'KZFMtrƉ8@j#\r-GV&V5עYQf ɡ_,eb+ޯyǾ5v!EHvuLM:L^VI9jU?E(atma倦O9@t`>A!7Cnb/{Lw#_cpϰ.<kп?v,zwnOr4UuE7UѐN񄞺iǾA?4\ɇCZW(%֍>T+ϷYΑPUJPbwf1#WۃW꺡sm!5xnʛC~/p4LYvɓBڸq#7nLi߾bYv4~wًZq7P w;MNKNi`ku]\>`ͼ 0 OG3{7RX<5nׇҡ'D W9$<𺳎%sF'>Md,o7uxkzY0s0K,CS?twCmcf<Hn7>3V] 4EsoGo8O8Œs-cќyw!_^H^ږԉsYBJVqV T0y`pOM]*HyH?EQLҐnLZHBh͛7QUQW@ʮsާ٦SI ]vѠAt:kxPk{f3 iwn mfDN'N0u0p,dZ&,,۷oSlY[US3rsTTtU|ʖdG)`4[iv~Q]Q-ʖO^_aIEyQ, !5x(ub+HBjʙ|vfڜNC*Vw}nQ/zySĕ˗sMMK7aۉ[w[69ܼ ʝx~;,$$$'d,lQJ QC=rt| a!89F-wOBo߄XZic};w.́[b&\=JP_n)W#!y:RdĭL%!!yB)l/ fQal#K_-G*᜕mSC`q|X:]ђt(Y?GC'.O/1ndfg3xp f_d"X3UMJN&?$$$0t蛴z1nEQ fM;{?:w^~c(S+s%_Me֌m.e ߐp1}W\ƮOI Q6Ws/Q wpyrMn'&yNd1Ul+C3bb_3vv IDAT7xbJ`>+=pk܃ ');Pnn*|Wu 3]buY1,b3YswW /aBwc Ǜshk'S;Lɑ,7K({Ĕߠ^g *nq\((!k~ϔ Q#誖`mpO􉉉כ/6"$JyRgly7w8Y̰zn ]W s{?^))GYܻ;N>uAt[$(rBRS%xs7%4MzAcNd ;t@إ7O-5###Y-Yںu+mڴI<ݻ//r} [51z.reڧzlB˖->_O%5IIܹs۷oȲe:fZ :;;;F;qrrf4yuRsJI,9c )+ !ׯou}_61 xyy/ZsJo0⊃CޥX,t:;F;;F#N/~}9Wb`BSDlq"'}m6`goɢϹaUWtV!vZIHB!DNG*$cIzizԵ.}}Ro3B j69 ($~[Fb=2SV)p4 !xt2N^ւ(mDwhe'i謅GRnsJʪe$$@JK(Ld1RH1(V0ez?5rFBBM鮳6Z(You<)72V0ZFClnBhKnƑ2rA1Ԑcs3.s/m a~FNBhYB J{_r?YN&M][ (/FHB=9RmNIւ)2ueaNJeCvfɄ!Ц&6i֝Pl 2̡#} ڕrBFok)Yk5eXI wLPxyL:!Ю* ,(uᜪ/WIENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-edit-roamingzone.png000066400000000000000000001173051501654372000230470ustar00rootroot00000000000000PNG  IHDR9Q sBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeMo 09 Jan 2023 09:25:45 CET-S IDATxw|M׹fDČXň-f)~(UFmRV-Z*QhkjEY#Fܐ(\q|{W4Ky̾63ks !B}S~f B B!,PO:a“'i.gsi_Biiי;5ԒPbl̼& !UfCYa%u٩^I]B!^-{{{W>ԩSoћ^tO%\SBWKQݻו'v֤}pQF\rI !d0HHH 66s}bccIHH`0d,UUɝ;W*Ux=ѥO>e̙3˗|f!¢zxcwA^GѠj3Th$_|nk֬ I%vq (_>B:#66mF#eJ*$ =Se5\NvvvB!111%&&mmmmx>OY Oo[k3ڗ B2}=5Nxțmœ*5 ])i9zӦBd^|Z)!u.7룖<dTP9VFڳaO9OM $$$}dJd:u镕"9Nn.ӝ#n[>U#I4F;y |F4 *[3S䟜|x9N\q'r3 fٙL&ONժUUcϑ#G2dk41qonfش3)j6B*hBcm;\=3ٱFƧ>oJPT!BI ^h4TZ˗c0 ~tቀO6xC^Ji(S>iǮo Xܵeޥ8VlYPU[ =WNfɞ څūѲkGMP(nZq)Bg1vcb87FpXă>ua`S?^w"D[RZ˗/@= lɞz->e#]7v;"hl=)դ?}o]a: S-ćÎ"v'Dؑoz+\Lz[{4[^j$&)Eۘ4Nx(67%F y{uwx2tD}fy;x<%v)Z(6 os;9-z_0tRW؂i~[W<|f)ޮ#zƃzg/vaTr4 ~>a }dYi9̖YLUU*(U"9 |϶&|^^Eq@jhujSrI~F*E_/RQH~!^멯tקF^ϐn=\"[܉?ITA㞗JP[ _&ơ8nD ۫l]y שe_[hc DZ6w>utݧx\2[T;-D%Tsd4I`ݺ}o[SA[6Pvy|lRmhj5T@WE'KFܻ? */E|h$VA]n 1{=oubee~Dd_K.yZҥ ǎKڻkZ .D3qiP:2h0&52Ua<א7",gԈCuZlMC/ ͛7O780]-F|hдff`̾OUbj>?#GBX";\pl4N?ħft,"-zڋϯٓΝ]O.M<#-w!Ŀh?lpuػD I 888`2{&:NO,יyi;!^Gjֹs:QW6m;$D)E|:ks!Cq+7u~Lݬ9$$$]NѠj(RɖB񂤴_riϮB!D`6V!BI !i!/]B!Dn^KK^!P yi !SsiO{ӧ{UBd8+V2!BL !JB^!PB!B!,Ba$B %!/BX( y!BI !JB^!P~mf]t"##_!T\pqq7"BRBҥKh4^FƱcǸ|2E!Ox)!cݻ'666dqtt'"9K yK7'22WWW .((S*r _N|JBck򪪾Ed2euU,FLJWfuU"G/e2PE.!D&IȿEw,'!/BX( y!B!o8Ī%(5B>֧-l},j8Sw C2O-54KЇqvv~?%jOWxX!B9F"Do_j!NyT5>N >ڢtf_\,jEqGЧYÓf`Dsy^UgE(ZףG!rlZq% 0굃iq(s@?;7 Vn]agMa{] a똛•z LWQ۞@[Fw`©b\_g֔4Mc}z|Gq*fL 4UJժ.UIQ>+3dG*)pS;|.?y!O KHG]PDa հo9|YAda/|Pn'&^SAfj@_z89jM!QzoV/Iׅ .av7]󑿐?5ڌd͟ Bڍ=)l;T& ZO,{zrkПU'v4iT"VP! y4cVXϴ7\Ț;UDAբQB> 6cި4>d/@Y0(4@ƽ=NVʇKk+|'3Ǝɛ=ӃqVcAx`8pO_@MڜeW]0~Baq_YޛbJ(KZ5 ݗB kI-5[̅uۖ7?lZٳ;05O~Đ4AT@!Kf]Qk>]\ ?ZaԤ輌ESZC8{?X|oF5^sa|s^hXo/;vS'9s߅Cqz2([OrJ3w#(Q!^l2Atmobw6XqD8ncdXF?\4UCѼGs hF2UTULCٹ훸+˖}zsq"\ 'RCҮTL&ЕŲȟz(bOH~[KkZE}S1Vn4A8sa 8 !*[{Aį^[jNoMq\OĎu {y@tp/(׉E_t!_gmM!Sy՝ǖxc@Jw-%Js\s%(;νMNqFܕE+*oS,*&28|4jΔrV@ !D&!=awϧ],T5ny)RY්M]ɇd (V)^Ū릀)W1m&WWK#~an̏0SߤaQWM\8|CS*Y"VG|rLV1/E'sH_F|[OX1 kq*~wil &_ {Y{On[20~X% &\7w'u >ei1?\G%[;OZOoBG6SlOÙg1[iF]6sfu T(9gz~:G#L&SqُXӌyCф1$h#Uf\:~.w |\ofZM܉ןߠMFBŵ_C:1F R'Si׭(9+l'0y0:}s@Z\y齙BzQ$}*բ*DEEh;vUV/[.FRiZN:E@@7B 8}ŐzBaz!Bh|Ζ3ҽ&B̓F\BHȿFÿB!3ӓ_~%رcGVWA!^^իguBp/ܗNzB!ӯ.Bf$B %!/BX( y!BI !JB^!PB!B!,<:{,#"""""xxxVWEBE9{,&efuUBdWr9J*AݻGݺuB!wwwٓՐk/t !H-;䂄Ba$B %!bfLާ!ũGOYB{X?q8Ffu]"ρٲjM//q4_̗Fďyo]/4M?&Bd6θTʎ4m5Ufq W<̚=W7;t Zk{\j;x >bEG6ڍܞz !x-t*gС76}V2%}mm%I6 ~qfw+m\6zKyBre|+zTkoNz_{4$<.9W~^3_9~n8]棿dḎ.gK7cܖJ\VU)sYI[_27~H^P\}H!44yR&{?V1y%|s9`Gދ8|lԻԆ>Yc혇j+Ύ+5Oe)錭vG~CWs?xV.tYb<-bk7eA_GǺ]+k+[\ TI;)l/uFa7;l| NfáBdfxE//`vHDC#3԰yX'&Nt(;7{~޶Ba"8ɴ#X7)Lw^ljyNöqȗj q[E@B:%{ = Ԫ Bwe~G7Ő9 7`gB>jYgM7p"AWӶYIylk_ M56u{bwlٵz.M=k69Ε#(uq]. DSWlb떵ʁo[I߰lt?Vˈ;!x.٤>}Q|qj̤_)Z mKߪTT9G\T7MԣV! T˥ۘY9o P4Yɾp~4B]qpTbݔ=g5K#|4@A끠0TzzFj*H*]x5hB3buiRr;8&7eRyiuoM\ՙ79N)ߋJMT8y{EN2%t!Nz7K 4 [ݥq]N0!zM{L|B!d-.`t1{>9cl7BorDZoN`h<$L@"gNA34\856܌R.CU͖ETL<-sht0M}˙x4oıoD&EsSv ܚ1.1.Qί3%njqK@ /kk P;309C":O-lIY(JGkUnCQx:kp)¬5ΈAPtX)&L2M&ЕȺoS0 /xJ(KZ5#?ݖ}'{lX#ͱ*e^:01At ! BP\xQ6<xq6:@C{)!,v.W v/Ǣ4(∾o;J/Orթ 7L+>|΋jaMԻ'9vEC1% P)陉A*TǸ-;ұ{oBl2-eJ`f,N>V}ſ}كo"vX[v~] [ؙ- 8Uk<3{ڑ;<"$s<&&_ڮeϙ 4+C[)Qr&K=aw:3Qػů\M[mGȲ]Z>]tei=M:?%oЉ8=qԒMm2Y=q֬D֋|>Ǻ|73d:Fk< hgFZ_zكG:_݌CqMr4{fB4 7IazJ QQQ2Z;hٲ勯m6J6M۬?4 zh SXeuu"]6YJHHHvss >bHdū_֬6(/LBu ߤx(/!ƶs~յB HK*!FC.Hȿ ;v,!"8z(Y] Q9w{!"u/"T !YBB %!/BX( y!BI !JB^!PB!B!,Ba$B %!/BX( y!BI !JB^!PB!Sܹ3 "*9"6lUBŶnݚUQ^!PB!B!,T x6.FB}?貮h5h4:l<(X6mjs.xtz+ZehYSSƻt 5jeo:EcS`.l;cȦ8!~wdff\JTIMz4nӃޭQ0:OosuhBXd'ׯ[BarhK<;2܋mW{Co.v Lq/+9y;_=NShYqnr5)Vo^+.X!k"BޔC,~(Ef%3EaU m\2Gd~}{t::\ nnL3` Eꆷi+;!3;$҆|'I}\.G4fs4dѵ4q͍vި$ NQ<.}ϥ[p0B{>\zlSV^16Fy!)@n>bje;vвeVɝ;w,tB!غuEN5JHHHwss >bHkB!HBa$B %!/BX( y!BI !*| L?貮h5h4:l<(X6okYm)?1]eAWH=! JU<*``E1hoMjb66*leClHX?״mr!#> ;<Ϛ96~(f}FXF.E%Nxtz+ZehY0^2u :/QʤS̙Ī`w~%W3xb{o}]'P6y0r3'kץnl\1 Nཌྷ+y5U` .lSעm?5(8i^yQnўF≷WFf#È}xj:=csœ3Ћz'"3{U@!E!8_Z2iͥ۠ eQ9cp٭)[zh^(g fb^NTEcŠ5`si bN5}+/XȈ}.d N0;!xmbT>jen3]*qehxp`%lKi*1cP%O[M9wTszLX8z0&5n8˰Rov/*f[6bi'9gO vSL)g1Bבń\rߏhlR>dHy$z٤ U8c%l!GocZ 3ʧ=c2`C0 3r,!MRا^R-:BTTԼcZl*sN6lB3mݺମ JHHHwss >bHkB!HBa$B %!/BX( y!BI !*| L֢?貮h5h4:l<(X6okYm)?͔eʂXT{pa5C y!"Uty6(b~߶UKgصe!ȡ1q?fN{1_=ʺYѦ! 9QcONXګj]~GpboI:1-#g1bqwMu6: ;1a$[f]YޛC3wJpL{g-!!o0 G-HjSG8Њ%(qIAqoưw HO]֠88M$^(xqF IF{BXъ'^9kBCQ|/Wr-4m2|A?ÜK֔-Q}t4q''oE>Chpg5Bp8h}/VTYKu՘\}ouyY!DGק+ڋ3SҖtu{=n~K/}W̦܏AUC3kWr*x$00)57ࢱ)UX0}ֱtdSV~23BGQcOԈǪ0g7t/nÓT"v_Yϓ%/9OKꎫFZ|+M߉=_s6_ޢ;y.W.oD _VsvZz+IA*hmSC1bݏ 0R$'ɓ: ;L*͏ gKѺ3aһSnfB*=8[ !HOTT*TxǩX"w}yrt[ '9yt=Ck RZ~̷ӗx'ZQdT=: StiT$!,\ԏ"fX^x*Q KǦpnQqţ+-ݿ3 1MC馑4Lq X#ֳOoD!^_Ju9E!8_Z2iͥ۠ e9cp٭)[z_akC 9c?1WvUjOsX/Khh;"Ki;Qq mD/ϓVB!ғٰ:t(3g~9E\™۩}[`if[% q_}b6R&rAXiI䟳;Y:k7 M [Lx zn(6B=ɂU6|S<1AoӾ}!nDg\4y g^csE!9 #F0k, C ~s"[Lȃ|Uxg|AcՊ#Xه x.CƓ6' C ~ :;g< rilч HlȐiiCZX8>QGtfʳԪ?NrpR'7R\5q Uw>8=N!x%2=z43fxȑ#1 6eVˡ!H tH&?7F.Vՙ|3P6_ 5=;7Z0s'yWTN%L}Ϣ-D a?B6vX>'3f &АB!]?n8 Fz{$BXĉ2e3裏03EWBa1cǎeر~l}||cQ鑐Ba1-niI !B CC>M dEѠqģPywDt&sbtYWl4N,DaO79~qLڶuO}FR;OJ6Kcվ%8akHn9d!~Jc73\'5{bt5kfJ@ݪ!ӟ^MN=M>ɾٰ|8T ŠZIr5yťUWӀuZb8D>*PfŤޠʬu}gG*x$00iFFMChlJ7L߅mu,g .̌Q nbPoiCQ8ߢk:zwXFݍ=ɟ-5Y3~!Mӫg#:J+Su@¢k9)shK<;24}'/j_2].D _V֯svZz+'cKٻ󸨪;3Ⱦ naIn}--5CM-\s)%T"iiVn,& 3QQ4y^>;wx9rϰ+I4}xgDcrv`}i#E>̈'P,rLwBadNfYG(ЀhOK-gNK۠hXW+dN;%@r6\ IDAT3V%Tnx)d\N!gOΏKkO;͉/ ϧQYgUL!ʤ G੎(I}L47UUnZ lR&f4zֲp4t&+B'w2$o,Of,IwpX^uE\j[|@PX nJSnui\I5%jz{:S:kt r=F o/ `C^69wsѫpkBR$ڵ{-6LI+Uiܘ˜. BJy,9. x`fe+K jzj9YuaO5`|4m-5z^zY=6ҳ P}Wzb_AJBcik&49MbR#',*T* Vx6 %$v󎪺_ӭ[q| vEuMĩ\ty Xۺi q$ Rz YYdud $&'1ї蹑Jk_]iGٴ}^hćlZw]IOͥJ!Nds$:.f/\eT!~sl&eNXM{= =bI^֏vj }mz$(ZfÞ7$f=_Z~5Q*zTRLqB!ME:,z\Pِ\? gY{`[Ïx6ƠQ>^mT7g;urF> 54ɼ7iA^EXV^˟>%xB5/'<Mc`-VpupB(Tq½VC;d!,ɦmejVձs|([ 4y('I _˖;?^ij@VE=/S)^IiS-GLcNBT( 2225ݻ {dLHHcǎ,BOHHȓn#Ghhh;;;֨+B)IB!$/B)IB!$/B)IB!2$W=FRõfAW&1j*+{W<{~yg]yIJ[/%>;8aM PJ8xj"qㅵWJ.;u, n&Ng?O\l,vJAr-w`(( 2{Bhs#-|Xvn"zjOHAMNL$11݋zRE"11DS2B~XPAdCrH7ePdRm ?ZjZotoFz+¥TO{nME7B]/~O%5q1yRO ]:U,؀4It*գNLMm4i NT拉 55Gνnxu= v´Ǚij76UVTmD6坮G!0ў|TN.8 :^*k_FLB݌0d^`O3Yw m_[2q; uB}‘6QKz@OzU:yol?v(3`7poIOI/"tĖw_q 0raC' EdrjCºzk¢?M,9yB@-8Z ^¹E'X '3 CW`ʕL*Og^C R􀎫iWٝ'W-[1n|{W#?5LOq*]EMq(m4Qs_\ !n& GVP[:P~;˄M[Ҭ_ϸJg Iweƌ q58JZFjG'읟e܈8`!ϰrN&G'Gȸʵ[a3mBaĤEH8Ǐnf6h՞^܍|cxyA23{{ť+܉y3Խ'0 {u@ŽKRRTpW!LC"zc>E?41آ23a)ԷoY=0OR!`ҩAO4nuQ,f`RESb8܅Aa(63lQ C|u=@e豠i+,aOop t[}&3P\xcHeWn$/l ;[ 7*d`do{I7z sfz-^_?B!̌I?]oL5ew|("{$6R)reLy'9&mj3bBz+Y$J垼5(xջ:;[ȼ~ n2kx*3n]>fs#p;C~LKQ]pVllu2u4[=[6<_m\M3~+oBSc6ITT31l4~@bW7. 'tB'k5$LvB;OodccEWxsIFF&z@mcQ/cÖPڐ3phC7ŇjWaW٢2ek>v™_3aSiJ!Y7n !(@%q0*/##cIY޽GȄ:v !0M񄄄Qb,Q믔}\!Lwc[RCu[ #pAP߻5h٪iюwNaXн-o E^>:hܩ3GsBfYG(ЀhOK-gNvK,moFAc] $: dP]eR x[Rᦐq9lؐƥчMA ksuK.cB!"N#T+-\ce¦LiVWkQ?~Qޭ_ekڣ_JWnM_[^ú2 \BQ&}עY$[CaY|ӓցձ(gs,/:h5׷/W9ܔ .ӸjRd+: [Z1xbA_xuO)!ec=yŶ*>ӸYE[H)OG;GMsy2} mVyhԠHûU\$[ߪ/4n$3f+;Ʊ/Xc?BtOޘk(QDhIlXRGٷ7n^≃laۅڌX^Fk !Z)Ⱦ?%`9k€`+hN F0[l:5Ulz]:bM%#8:B!&Ƀj}1?3ZfŵᄮQ`{vQChY2VfU;LJPAc[ o"~B8m@Ml~UK<[dժYӸ;Ū-\k00{Z@YGr|B(@%q0*/##cIY޽GȄ:v !0M񄄄:gF!x8/:'3a|lVxyt5٤Mbͬineh:5RmgnM>`a|b7E%9[_zs !fIޢY$[CaY|ӓց>] ?̱| thp僯o_Ar8`'̧3)\N ѧq%Հ;S,p׌.pm'^YSC#qbB!ğ`I^OP5(N ý#޹?hJ 眻05kCU@=}>a~Þdk'7[ `tiP% hP:grIBQ?3?X?j&il.EdĆU*%a9}{3u.8]͈5 e|e`(8h *@\ 9ᣃ`o-felL|/n)*+ѫ ل҆#72l/e?SXXH6mv;N8al- kjxӼGijꄯeK댝ߟgZz V"QƯ$~!DԞx⅖=N߻!\[NDvlaDF- 2\~N"IIهLzv2 EmH^֏ڈ>AHF M@u!*6<Cx/&t\5]+\؉;~wIlHёJ霃BHI+uoH ԁnA6$׏$zYEC}eE;ޡ; `A1h?ϭW* hbNq=A賈up&b'v{#Mf2/MZT$Ni۩+h8/~eo>)Ju8â ؏[c"Z^#-t={¬pL'_: N*yye!ʸ˥Ͽ=KXy׀BI-hzhW+TPckg#A](]Tܤ` IDAT1U\£z9$FORQ? %-R3BEdrjCºzk¢?M,9yA/ uEB&4SBqt)I18nUH冇BtjJ=ןEjх~2p#- UC2%4Xk(z`HO!S !y̙3]fwߕ'`T΄MyYsʍ^Kw0_AM$̚FZsY诰-PP6GPWM棽k18>*' dv9|s7< ')xG\6鼕I!%'Rxf )ɤIGx7h}'zXu`u;;k9ow4ʻ\j[|@PX nJSnui\I59픊3= !}~PT\tiӦy=S+@%q0*/##cIY޽GȄ:v !0M񄄄,..ۣ?NJb׮]4Z#c@!5*ŌzB!\\\(JBX?j&Bq 'OG8qdB___~'?εk6\\\pqq$ !0|.2\/B)IB!2$W=FRõfAW&1j*+{W<{~yGU/V׭8KnTQEC:漄 m43K@ * {:42mB!hs#-|Xvn"zjOyBM.6|: WY3%@r3ܭJpSȸB>`]8w3q _ox%Z2#*.>K!ĿI'x#Ra_;6fzp`6ԳI5ޭ?MG[z77Nϭ\wv掬?</I'yfl]}2gMOZV/˽ ?̱| thp僯o_Ar8`'̧3)\N ѧq%Հ;%s=8N޶E avbo/3t57T˾{.œ=yŶ*>ӸYE[H)ϓ?hJ 眻0(kCU@=}_'VMxadglԄMj<}73B!01PEAђذJ$l9珲oo&ܼö f!܍0pCPS}?~J vr.|V`ќAD` 32u6k&>̵dSڣ~M\9NiUb !%&Ƀj}1?3ZfŵᄮQ`{vQChY2VfU;LJPAc[ o"~B8m@Ml~UK<[dժYٕ'N=u#Y4iw4s2B!J(@%q0*/##cIY޽GȄ:v !0M񄄄ltM#&,G'SBbIXPAdCrH7ePd=m ?ZjZotoFzzR& sN:] Gpn"vbк8d&ޤKxCI>=s#*nsB90\pRͻbKaˈCqK]O{@Ճ$[оuѮV_=4 w(,R˙m_bi{4 xL i sR.bphbn S/e;wOxU>X:-29:]Q!(N#T+-\ce¦LiVWkQ?~Qޭ_ekZy沽?ɟџLdšwPNN^!>L:[4sfhfVI˽4X^uF4ח~8eGRO8!nJSקq%Հsew,KNT8 f+~&U;ۯϷ_S!xNmU|7q0f"8e1#Rnp,9. (eڬ,`eAU-@O-w x=IƿUr_h`׈?_zS_!x0~ޘk(QDhIlXRGٷ7n^≃laۅڌX^Fk !Z)Ⱦ?%`9k€`+hN F0[l:5ZmCabtUiU#odC;Z70E2-tهBRMc'~ 7lgX͆k ]P Z <γ$*YUl)TX;VÛ=N['PS'|-[_g<{YjA5\ ܸY~>Z8l>58V3pjBDQ7.FEedd,)kݻw@ǎY>'݌G...2wvv Q)4._B!̔$y!LIB!̔$y!LIB!̔$y!LIB!̔$y!LIB!̔$y!LIB!̔$y!LIB!̔$y!LIB!̔$y!LIB!̔$y!LIB!̔$y!LIB!̔$y!LIB!̔$y!LIB!̔$y!LIB!̔I7tBbI>$$I7A!092\/B)IB!$/B)IB!$/B)IB!$/B)IB!$/B)IB!$/B)IB!$/B)IB!GL5?Azzn0czr> crވ*9wxI6m<馈'N'(7v]p}FF~~~O_iӦ\v|FΙɧc0t3ĿȃS|#(­'r9ş!'G$y/{(Rzr> crވ*9={ [fɢ﫦SKif많cWyO% an ` =i9}g=uk1qwM{\ٻI7sO#yD^KY9#`x4Eoɻom[_? GtV1=ɶ㓹؝}AE{_Cexr)#J>utI}F?=]oI?<6wWο7:G{I>gc``3}c<+9`kJm=a'n(9OKrvvGh]n+.箎K{dֶNh@pf?ջ.w MUzd#=/w9|>.t~e[Ng?xγsp4-vUnكחހɖkMQ)<1UG7OI-|⑗9Rs'_sӹ^ Ul,/N_J"=5K~>JU)eQC {;ѨŸ˿FqƼ]݊~0{;/1dwʳ\L.7/ZeL{O[m3v> z0?HݞKt~yqR7Jɩyk ?#k@&m{vd%slZ_iM̺lZ=6Eu/].bshkg'OZ:*QhpDii{gͼ"w-K_ YJ sGi7p$YgsAf`Y,==Im3<)K2a]%5foa3j]旽ӑ]vkD9s{?h5fqO[v};R3wp+;X6\Prwьؐ.Tєe(OAFck|6 ƲzZ.&Tvv½^+KjdkMMfi|9+-զvnxܕy?uݝprh.yqݍsA4=.h9? o>CC| ;njd7LesL}f0OW}y^Ix EǍіTy+:`Xz0y˯jK-_KmRՙOa༃yW_I߲2{X=>ϸǜO#WGgvdsŴqɝzm\ѹ{y *R@[!l  ӗ&+>~:\^?]yh_OZXKUj(BC-=O@ϲO&LV+7-B>qQuS*_!MkR w+>'uגU+`G`^YZ3OSN8{x߂NaOXיӫ>fQ~psv3=o^YC;Ѵf%*a mߦT{sߦ8zf͇IΑbOK#V]lk~ЋzqlG ^g |>J.CpVl/6΅[G\łuYn? 7I5p1tg"޸:RQ<_ _Ć^rGAEo3'o h\MR3^9$(ێ]D4VzJ䌣@^qۋ|ǫN`1.a~7izW3^bԺ+E1;2] ud_oM[mqݶ \y?IiY[~o'; ^ǎf}fr$ \@>k3 ӹM6)*P9ڴ-񻹏 ~u>ts#$oMUSYGB ,ggxy}y2<W@>Vki1<Ƨ{~XEۭնB6k>m)۾|Phw|x׼z辥aj;/!pVfOq#)^۰}^W /8 )P^T?fJ# /HNTʯ8oᆪgKzOBt"]*TD4iTE}P'*  UDA BBGfIBH9dwvٻ-; ebhA7y 9GޮB~q*1qU )|WOje5cgm8۝W5(`̩7=wo.bLx0U㜸#A]ȯdlOW.;Q tTk՛jhXOc׺M3>]Zu5jAt|3S;)#+d2Y"vHЄzo_h_@/-NعW֜ŷ4sxI4`ΠF3}#ߦ3~:(c=Ci֦ ,"|T+y'~9Jn1ܑK@-QoeaMlke CoRGbճG~wMCS{hg^/ŰpQbh-罹h)H#1ZǼA^LDϹ9).Ti3^vҨ:tYBVm7/f4$Y3gtCk2)Hb;DkF RWzJYuв?xIDATĜ¨@U3Z=JGn*g|sjP𘿍|RYٞ}+@1&V}dc?"gLiP<~Ys'O ѡiMXrV[~#fjB%3G8Kf50d:l+ȓ= huPopz:z/oܽ;2axY®F׸[ǣWfg|$iLSk01PՌ|lײܖP*\͙Wnp';kyĩTNN9neV ]\ & h#]%B0>Ŵhd~f%]'2+"9XƏ/f+ D#!&p⹺Q k'oiƋh 3&].шM-|`@٤i >w!kKbhF+|?}GcЃj6eׅMETzt)OUr׉ կpE&sttWtsMPU[nTf|A>-'4vA-{aޠŃ{7MO!ypLu%nǑjh5uo=L9NҔ4o\1B8䣲Q0mIeffMҧcb4mgᓶAx$bgbnB~g!Zw<4P506cږt 4SÃ?FwaslAMϬ5MNOz5#NN`2P5  (MF?w95o*j=t]K+7;dԑbɧOPcgs';{$ckV{u)oǶBtVpd;?ҍo\CNI, fc# geCOզ^-#ZaioSuhT̝=؆Pj*ص7~?cʓ0wCu4pi '<<'UE5Braj)2z_JѦ4cQq;!ܾW^&"=J##n(ߕ/I6A:@ -1xãUFd$Q]yi'[_0fY[,p3'GwIdP٭(lڔUOp|au%<,Yy)Yw Nd1m8d| !tOT,sxXho.pgk֭eފi}G?3 Z>Wf\*6ŏOqxJYzM|za8Wݩ/. W'5+fY;{ ǒzrZ&=-]uZqm5Wc ]uy0M`J6<㫾c+,Z8/DqRִZWte z =(/'V yg{BZCxx@^% lći31W  _Ŋ)㋆fl³+fq};L6 f'|jObm/#dZUce>5 X /{o0dMUׁNb#ȣDߥ۩b!o~ /mK|r!m=nhJ!ƾ99ٱo'xiGNLnm)Ҏq/tڅ>Z0EcAWڍkS`F0֝:C LFѤM Ms":C\!I`_ oW-te kɐ3Gm|(D%>+ๅ:n(j<U4DP{f@yt:P;,Z0ͫ( Y$mwPх,s 3 sL "h[l7c'p}ޛ?WR1U##s ˧cۙ45So[/. 4og^lVS׳e^_ C߉HHzAu6;eK]TЮ[ OHv^.Ζ:|)@2e2Z$ލn۶=zصƍԩSv|iXѣ_I{߰{IغukS꣰EꍸUk7nCvow4p0dWK^XqH8;ۗW5ɾ EiS-2QzHԅb#+_.ĉ Wav#01}| 1BXyv.lYNn+]E&nqK]AUUQEƳCt:TU/u> kRoĭ)"GGGSFbQ2Jttt'k’q3w]kѣ(nnsBBBlֶcapm/~w5|]/(uŸԯ_|_?q1z7EU5I^ӌ;3Թz*bܓ e9iސ /( FwiӦ( e23v`$'Qsdݒ /( FǏgΘ8yyD֩g rT1 {i/\JP|+ a{Fzy(cBc\ φILLW_3#ۻ.?.u`{SQmRTJFf H:9y!De8 JQ p*& Xʼ̳ٯ8i27~IwrDysfJkشǯg KW)9%k?ƬhFjj׮I))ߑɑ==J*\vq")= WL'No?$Nlo'/[0B(펇o"r]->hؽ~Q[M)));-6]ϓRSSm|gٶh,S%r42yNd^3h[-rUX1icEE' _d3Q V䩡CV10wo^ff/K:^]ϛv0wj(\/. g4x~/ez[&uZTRSS7n?Ԃ|\kW9dHO5?i? {`!:ǜvo}EZyzOk9 kROvCl|NȾ BRɞL>Td6cV+HhԎwZqN\ڠ.^uYԧMl+!#(9UkBvpZE'bwae9<ŵgoX9K W-+@X66nԟ9?ёN9k]w=E?nߗص?a҄4l$;IN]Uw4iԫgɓ\4whly;kwt5B){cO7i)@O/*{xݤa42PՌPpM>fpdSʑ9)/ϘW!cL,f͋CE5eуIp^>(4Ta :Y?q3̕e+'˺x3-Z[>Ly;"Bf !Jf;/=_$ӛ+7_=ϙ*%sM1LޯrQ'm ØLz =4yqcЃjϬjh(6oVPk4]2ӯouQPt'e( 4đ$#EvyJ郇I׍8i+421&)E?13lhr ء!(Ο?ȯ /_b f5#Шz[h}PnWO.ԬG(9)%\T'9㏏M.9qԴ<)%5#O0j$ڵh{;1\i܇qbP5Ow˧; 3W'5+fY;{ <-\>p"]๾>:}^]9po`v'(Z 5,P7nR5coT'{0ԪЗ6b}j=o*xWC3H#tlgZ1+LQl6N>oaVM>?t:z/⌳ NNH7`۩B_ȳ_!G:=)O"PU}yFӻ+׀/oX1; ^3 Y)*..nJ!wжmԩS|燿v+>e+H)}"""H6N֭[СכMɜq)=dHKKc'F.\;Nh4b0qrv77wswA̍ɔ3`n!(A H֤I`u^ߡ%-Njɫj\7+fqca fEAoc41 z=IwVOZ/{GL!B [6&$E}/v^^ 7wB\1|s[+ xY<KpBX ׮] [ ]qpe׽e.oyvfX<>-!("3 9t:CU5SLr\b0*QUn1f67[AD&::5j0[h]VE?n9NuޮDP2nfp!T((^gd̬7X%cT\\BQܜ:u9.x{{Zz;q\*ٽ^VctZ>__ߑ̮7{v}V2[vk$!(1vJ k"vlp B!DdOt,^!( -y B!DgOӒF53 !xȊͶ|t !BwAcUB!D`oiL^%wּBQ|Xh@NAց^!C~>W2;+VIxB!D`]o_ך /B/k=בqG:Ų⹬GDB!DIXP -3[<7o˔kvmXXjBzzaܭ 62잷 |5_ ^BVzVr@nL³Y+djg-gw{v!=]~g+Z9>o;Zoo)Q!(n7s:;`=Бo䱱lMB֍䬃jc9,3 2/A^!DiPXz,l|V<Z- e}wZͦIwB!J&e-6V/< yB;azY!( cA-z[3:s ڐ-ot !(M 겷\/ YߓmazIENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-edit-rx-group.png000066400000000000000000000461601501654372000223020ustar00rootroot00000000000000PNG  IHDRJ3 sBIT|dtEXtSoftwaregnome-screenshot> IDATxw|TOrg-meFFP"*@QA2PET Ced+KDe=G ("u_f'ޓ$ WJ(֫Pᰮ>>^eMfB!x.+)6.S'OuSt-졺տ[l1c2>Bq4'bnڲMJJ? `kެ@ jtiڝ.BX&\/Yƚ%X,`U5@;]N!w]q\L&l6kHzZ9Se&zyy9;]>!w9Kd2a2^5&-!94<ʚf9!xP;HNN%--+co6)Xm6K5_yc >dEE ByaIjb ?d|i8b!]S1~@5ĒO3eRM|H柱wm8R8KViV=bϝ-|l6RwPLlC\1llHOAcX$3@n=-!rK+=4gJLÉ:.'qħ8q`:Ōg>F-S̽$lq6OB0_SB@5fDJAX`/ sU8}{SKgi0-?8VpӸ@'{ppY8sAty .SKVԷG30tmU{z7Aߞ[ϿJ mP1%{S۷ ݺBKlE L_;6_/]2UX\B1V̚ʽI_e.r͆)viZ}pWĝZG#WrBQIW^jRR`E,NLbE^*21|QH|܏u3O<|TY! 75m&#\PNӏU")IqD"(>=FmN&$w{@9{q%'~2 y'0 ͠U㯌tPߥDD&Y듉Of~8l$AoH(s3M­+Lb~Sf% X>=N :JV_}w?(zy]ww]#~5QЈ49G׏7 WPB hg`S=0NZ.Gj &3oשۛ oשEYGZzꕑEǥ]\D&Wȝ ħ^.\!)`Rc_o+ɹ O?<5ˠ` "DL%?KFYRq L=Dfv7cQ|؎tAB\M|Q_&\:'Z1 (X#@+̡ne^u~9wݪ ׼q-m]2)OX͌x )hN*@\qq()\k:+*.ERJNJSfsdnM.^ɛ̘=D v)qnX/! ^7NE"JaڙNJ={4XE9G[ .֫&&s3liomN3F,[W42 c\7rEAUz5UU8$8Θc\UaO;Gy,:i.̐JAuM%MtGRNt(^hZǗG]őBS5B$r>6 GʠlaƢ#ؠ ;Op$3PHF;D2&Bws Tif{k*bcy)I$뗳2{&ū{F@6+)'㌂O\j%Kxvm, I'9. *(f=,GY ꄼb6/!\_Ƥ0j$_dBVae\UΞ ٜ >P;|H1wMM(cFx,ޘΦInE/ұl5hQ>vt N++fuAos1doMs-mOcĻlXHJr`ݨ xXOb.t[DsF)h 7+nY=35SXc7 $9urV|w0qL_cHs9I=DZ}w ,CycgFa;,&pxD;$_o8y]az^COvH<Ǿ86S< LJjhSƑSͅH?qWB] 18|wXm"ddRҬI} 4I9q>6HOI#9͉ӥ+ br7NbIU|<3NkNSIujLVmYZח[F=SIp-=T 3^37ݑJL|:. EJJ.QUL&nnM,C2ĩ(&VJ.RRIIp:LXv %ynԷGA| Plx[0Wl@DŽu2F1s}5 bef]Xmۮ)ovbww#Eȸ,eRl_׳T=~fza2l!nMQV7/"'yK: !]+9. !\JBk(1Qg!"פBC$@B"" !H!0DD!! B! BaBC$@B@=G9|6!]ZxeB|xA\ GIMtVt ܁X!PW;NnxA@M`l&=w8Bq׸K0{C{`΁<~tKC!i'&@\EEB`d@B#r;$n1j#O&mgYrԙ'S,7!;!, |W/JZr?5Ek{5StI\H܈Rxl,fׇ5V⵻2b٫Y]'Sd8!h';SfMj?ԄOi/S.}IOJbǧPU:Yi62[-a ң{B~D iÄ}_*lO+l_8Aq0d?>ZĪœUB=d'{W>r? KT!Lێ"Unj*D֩GB*T3T z>6A9ys'3 >K}7L8㗱mLcZ䲏c}_ [_]Ct00.KL=n4vg#r6Mj6{m9鋣RqsUa}}Oy߱32';i= 9.SU+2wK5 X|y*>ߢ1U@֗e-$uu>Oϕ Fi\w!_D1V^oa6Ep.)G2l8ƙDn)8RCIQ T8N'V2jxR !m^XJ>Z7ZtwHy| ELθC9~ÉYXd'NP@1[qC]ssq e=ЊGejǬhcܞj~f%E,G`ۑԿf}rLv3p`ݘ"D&݇voU.3\hyFG: ! nx폞eK1L38fؾ_I.Ҟ{5p$R]I܊Ȳ`o qm¢%Fي^*I:4->.q5_#q ݮ*wJ I0Jsgڋt}?$ !+yr7^jauPyEK:kϬF!9rJrD'^̗yk$mG`3&hTM=Ǝ[J!1/#&J)l otjrt})`ےsN/á2'vn^nlY`nh'"L\^9.V_j:z/sJ[.ݨhzt~̨&*p)LI mϧKT XʹdҴҚι E_ nJ u7g.?y%UZgb[c|$,OQvx#2l#uf7(a5m/w ux{b^@VsY xb 繅6>秓P<)T1͋ߓ@b[ E%Ƕn0N'WEAu q9~>}p#ºŜ*ޑ*[!xHmR`i":vH!ĽEiiow+!MF>5/T f5s19 !fZxP{`Z eB|x^^j^x[! aYz7Bsws sL|/jA w !y9""oI!0DD!! B! BaHl߱vC!]Fj96 z8!Y҉֞#kBdﶜQ}UuRSщ[:;M#ѻ,X#^*C幯Ng쬵|DCϊ۳Y*N}6y:ƶyu=5%gx8fukDdLSOȖ -,3aҩT >ٍIG2b SW6ytzĻ]`EX+BqJi DcDZ28~$ ~IB zc2VAK1pl_ݑq;kahTStNhצUӆXUS|,FnHDkRg? k}ͶNV`%^2}+6Wv \!oUBAvH!6;nO]GsC9i i;x`s$ʚ(L)/U 6M=n]U{Hk^~MvEc\'ٽ' V2XjQ@CunNB`1öˢ0:ޝBl\L' |(> eo:y9n0 OFaz1^^l͛}X4Li.1", IDATśٵw/{ !KFG+lDBnUX*)? ~-&㒁TgujNVO2pZ)[+֒EU-}Ҥm_>?- e:ڛ6:2p.32[.7mgXsLلU3ߐQb~ Kѽ0GɼSrqBdDW;vgwMЗ;xa6()υv[rmÏ3V"A8Nowf]imIx!Y/LL*f"TjKƼFi!+bk;]!9v]!ƟO8xKvoF?9y/[-"?d#,^5?At!˿ D>ꆭDNJ7 !x9!Ic)CW1Mn\IbgG~Dҡ-X=C(z Krb ! zԷiSfW+Xi/ lJ˖#ڡ1wsa`匛*uxvLoڹ)GtKN!3!nc?G?ż|$Q&4HzUvTwrjQo2E{*yåiV,}gNBqH*{w@/טWnn-݈)yi|+/~΅J# yC-" CHMA$wm>IYJtjY㟼|G1\-b[bJRwIy!xSJ8ąz6Sʕ PND42ϝ~ P K@i!oI ı2]5iK<\9_ǚѲD8FP= U14g^Ooϛ@[}1}[բZ4ūDJ0 qB^` y0B!r}BVn(%=хB"" !H!0? <3s,9k%9׈B[Ic޲fdloG-^Ë~KB_H k# bZmyJu,q !ȑŅLj E:L$^>/7EҎ (Z e,h(~B!r&Oa)TY5d˔K_޹Sxuc BA,=nؚB<ƶ3Yb~w5L; ?ß,b.e\C[qy,$KG4\mdDO3H6S& 3~d\#ѹE"~H#<]Bq_˓qKaˠkh ?79RqsUa}}Oy߱3h.boZhB}{SU+J0t/h\:C?`ҷɀlo=/zyr\}}{ٱi9VȠƴw=Lpԋ牾|&,dtn2 N.BK@"ʔ%L(Ozu*HY_krIA,E#|pmGR#w+(>"nO?-x'2`\/Rvay*V))O:B\ȣJc-sw v}ؑh~`Ŝʹ~!Bn9E'~GT۞ݴ?ž?c3y؇(?Ym;K/Fe,OTCtwPWK9syS}o~ymڭ&oQ4jr?Z5v`{9+Nv^9%&J9ttt1-}~B!JŜ*ޑ*Kx! ԭ,Xr(/gu퍘|"NB!-}-! !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0DD!! B! BaBC$@B"" !H!0Ĝ֬\~;!Dt!B{BC$@B"" !H!0$ח;~D.DGߎqGˇEo:q?iV.=v8RJ\P_v:nI[@Mu:"?Tcoq?UN… #_.\gRf?;>K6"q?Rn9quVK[m?R}*a @t?# :4+u`t%My9d ?’ ql9&L.*$ک]Foum6 ~Rx49s3hZPI܆:~}vVCw^KrE o Zƹ@ޑڥ7asd:h">*R}3#!Dx|.r|8E===H9{ΑvwSwrNeV~6g aʔm$_5Fآ<~B1[Hг$-H*E \ >AOuΐe%<={,uOXA~>xS!|{"xEvL{ $.ttSOwQw/7A)J՚թYwϴqotkvK@ 鋟7>l XuzQL&Eke6.W~:qqg+C]c7[Z"^;Ʊn >׉[ᱞX:)w&<45]Sqq|+YB'd]=gsˮ899ho%g/7فjAp|z(c1a?ؽ.睯M9qg v~Ijֻ6 ʊAqS;=۾/}>9E9ŖYcId ߽JV`.?Υu\]65{vjS;ɬZ2Kǰu>J򆥼PŒ.Hn!&]LEIshuX*΢):J)/DmA+ٻ;!v͈3ye$ C>V.E?(L[p!TH}zׯk/>Ωd4]yvU{ "~9}<{p=muyihzqf xFTz[4i6_PT u^WYt2]|vwJX߮m՝?ͻ3FP$uG]XVBU7~ȶ |"R9n2o$/ɂ'}W؉7&=I q/ӻ@eֿI;ɗ=ZwW) b6N&S~N5/cP (6ﲺ{ }Y:~uv@gh9>Pqqpa*=07\'95{SXCX:[6@Ҏ-+Gױc ~yΗhP+&!(S1PXKװ+V܂)Q*ڛh*Q nwȇ"jZ {r]W+J9rw-~?]q%}b/+?F"yeILó&+JwJLg#_qh_xp۵W}6T5.M[Vut|cZUSj=}zK?C:Gw2e]N'í#ou,mG-4^dzh2V쎞ۄwI6Oe3P8ћj0z6OuјoT-C nʸt;-&f| "2z*\ G/û_Q~Z+z<(My v~9CN"kRe'rQFu”k>4tnT6ᔩ2zeupNv39K(VjyW }1"VfHM!Uq=qԪ_{v廯A6vHh6 )g`E ̢}@'tU%$,%<\?Ģ夎j!<ܡ&ANV|Q\a.\uT L}^YkD=ꅤoamjm8.DwiY[sj<ݬE R:omJGOK!Msqn.N*ӰWekO.5rnkY?w.B]i.~~k\Y@):eIz׮.sbRV@!٤u|} Fׯ|17=s?̖j}qb㽘,M3U:O\&MG#coֳ,}B}?o` 'u"~y/bjZjWŬdsY0I ep'WY,V \8Kunj٢2-B֝?d D6t)+*ӷ]QT=ej=(\0{I]ri:{:&\Ό,MǓI4F Ϫ!9]MӲw}є](Z.?^C K>l=[l4KImP eiWRT,cMạXwAfuaք+'dT˖5Ȯqg=.,_贋JgxJD*GՒASѲqeۆ\W67+e_d"ʖl |$N7VrN]Y?_)QW0+ӹvs~嬎؆l7yߠ 9/Sm@ԶqKۙݺQb$lWG6KbfwYwWyA*e,[2KLY]Y%ܰDlחQeV&tv/hEٲ~Vm3،^=ywJgXѕ`ڿ4;K'o>Un)8*k~|~7>3:RD wg dhxAiφEL5}.6t/OJk$K3%+FO)sTND;?/1W3^Iz z/{9λפCBT(eWEhNҥQaJV3Kf*֝Ƭ Ҧ:ST27<252]zYI^~eBpƜOG 5'|ӟapA8H>|o"G#z6vrelξw*QJROV -ߣWGg[k>כ}h^WI(י&R.4YF0߻qIxVB~ ?1n݆j֬_L~a"Oeghߣ5׺2,AJ-)y^īλ|MGdP5#Xv۪Q&ćxrwȱQUSv)v(j ] ΋ Jxiq$'PaV.U{wp$d2"ɸ :RWOQq*((EU( kIKuVUuW%#19'3D1zz^]$~yU?;,keÖ'V* "z7톏^}/AA/WnOAp!Jx3\1\(O噈=?MAp)"fG?'_M" >>1zT%q RqGg6} RSSyŋ?s69Ĺo0"8EvoOeJ7ʎC!ө.VY" NrС IDAT?,\IbilV=ǶɞYvqyuv4[Ԟ]7ySJ %%0_oE3X>ںWsUD@;4oތao>SOaa!jȭ[zL&73`,Jmؒ9ȨMغh<{gr'? q"@ZymiW3esf4O#~4lݏR0)tys=G&4>f۾34t9riZFp^PP`";;|$g{9(!k %l'snGN2q0r .[FCgY]l- tn}<13vRz2nqs—  BBXw*]F^ I3+H[&-j"+xKExT { aB]E%ĉl(b8;MS#k %'%? `0 \4d $!a=nnme[g%ITXN_ʎ#)SU@II.S2u|C zdEÉçQ⹀1qD2Wз/ U\3Xx:1ѝt^nfKPDV!xz [.eXCdri7Vڭ)V[0X8Jo@!v/q'K&.<]MI"£d"M_|LdYlA$FMU?ဩ=s5& T;F~j ԸURw_E1\6ĐjJ+ o"zbFޯ0 b`[$5kB-l?۔ȚT[s ˙a 鴘N1@wr+^\.#+(Ӄ1{Uʥa#xmȎ5+ƮgB;#2[KP/Fl0N~̖#K@(q,T"4)}=<,3g8ܴ(1s}| @cyE2yL[=v&JNaHe@(g`v_Vl[P1>yg}$jg嶕 w{ cm/ŝV-zOD,=@ 2νNV}lq2ňV)lM ]7hϿeMLr)Ç0rBvf>U8)|[QnȊ,a>فxJ |;УtA\J"qNCr3^b}8U->7S׿jFu_Cj0=2*:/؟!GpT]2mW= jє`;Mʫ*J. x1YVCaxepOZ,-"!uuvrU/G9rDz#C惿aQ0E7ծK^XWۑ˞EjȲ$IbN* Yj=&?YA$ERaW* "__Ξ;CDD$J®]Ssgϡ:~,I IDATxw|Orwݓ{"CQ@.D?@T\8Cp@\@%(Q@-.Z(҆b7h2O'O3ϿIԪ]}Xxxh |^_fJjښm[O_񏗁< 00]>qrEoI{ILJYݢ1^gw[4]x>,˪Ҋ14M{W~巧6Cz5/7Myyض]cmx}>\.~yy\m4\=?[cgrp\Uܡ!, nv]!>En^>YY9.?mvCܶmcYj}1򒝓O"$WeGHNI#tq~괍j?Yܑ̼!gVxElOp9i> &V-j旿!vsI-- 6)|MeHZ iRۋBUrkfFTp<[#em[rxIٹR2Z=>4S;`!w;Iu8oYOnrR!,>4]">2RIlltx ",Ѕx3Sّ*Ą{|YilOИ*DbA,ҳZ OϤYޤTnPnqIqw֯B 7O.U#KmضK`z>8r*W~ +"UC"?>h6u7_h-/_EG_̸Q^${=q7ZD^r2g:|9_l"/u'/Ϥ>t2`ʳaD"Έ]AДa٤դ㧖Tt.jP3<2KC@W[SR%Q )/O\r*7/`dfg_nBڴ@NŸq&.hl{IٹTqPCTEm rC14hӁ=KfFJݐ$Kq% Dbs[ <(rٓאKﻖ5=!M85x' >XAL2ry}Xhp\\qUΤW` .? $Ѽz}}X@X3w, '4fjY 7څ!ʶ!(kۼ)7Ew3""9xUk'k֦˰zMMAjѤ>077ٸ1Nx#L1؞xdDKM/_6{j`cysSƽաishp[`u |>(rs=]n|*|`pOSib$ʰYlܛNZN'l ዯI %DUG & (YXjDa߿8g)V>~uiV'x4auR\Go>cT_V>u[gFSGbe\<vj,M'"ڏ~bt C8ڴL-6}<6Zlʶw#'%˛m;=_j*ibݵ aRTS'= } í+(*{.7OqziQ8!}ly7w/i]_J(7,T-&V.Wnq3Wlس]>8Z4 ca؅e+ږE}x<o`xm00M}; 4 vxXǣ'3SGv|_|~+o[ڵ·ŵN&Ÿv-{A$_砜m[- GcD/bCn0}lxqrpF%yr?_{'֡P$otq/k@ 5NYs~#$p7ݖY\3l_4Zqg+ٴ+\]le :B* 6sLsqd·&2aN]yuvfGᗟ?ױT5Ǘw_z2y[$vypRmݣ%GEVj %,s؆i rc^ғȱMC :-/9x\~k\wP&9= HɱpD@`琜x򑝝KN.h&.@\C2e璕k.~#;3< mc˅' P?F\=&>OH:YMfF&##9#(弥2], 䕔@B1̓+%GA`X(2FA3Pظ oڦ`Aq2s@*꾙۟*U5  U(u(VR߶L v "]l '=À^J-y0(4*Hi\I d~FFu߶"r;Dvi9iE8= ~9D $ea۶1EDk>2SrȵMB r"[GDeq1= %SX"'Szj}K)K& F""R>+""N]DDQ#;M%"""0PCDDM7# qD"""(@DD8G ""DDDQ# qndtm]E9ftCp֠\ӝ4znFrl:&ç'`7ӗn!eqCeiߙûvQ%$#"r؛Í"o5Iln>I >bO[Q,N'UHQ# qɻեZ{Xe[ZNnŕ(kj4MAcӏ| HsJ9Sﺗnc/Q;уڎՇ~D1\ҟ:uhm{YUhtNeBB?]Msy93S`f0mxj":Mˏ9q`p;q=7\Q&.jSpGU%߶Gxl"70#٦FNprR=$#10c&6on$o:7:GώtcȞeTÓጾ.%wnc6Z%Lb<93?< ˋ 0~x(xmxi(|3\5^B7bf)"r":)a]癫X4Q^q(8ge/kȪ3E[7A _phڢ>|qVp Ϝ\:'P^ٯBvY.6kܴOw#;rXM.n᷐1?mcdC1AY7"i˯, /L0Ghg2m.3j#X\==c&pgx_xk'# *M8zPEjxzn]Y&՚q}\1 z~DVkH.CyQ\rJl c;{i{qU\Du|T? ^Lx0 l}16NY2wѳDD8rRHEN_ȉ bs7#""rLIL"Cz1嚮:5Wqc6lIL9@e]dt\7nGZ8^DDֱ^ -3krMwRTDDD*DDDQ# qD""">%Kr1SB2[ {9qyk|G ""DDD9Në4;q}쟬w4V, vvTa/;uE;^6Ŏ5?WmkPtj.Cײ-\{X}{|fY/Ngޮ߫V˯[xO'""ǎ# # ,,YA&6?}梺?';(lV-[OcDDqdg9-!KXXۙ"kìfu,9NPuӉ1إ#mi]/n̠h3h Ng&֎g M'""NJ# 3{І_nqfCЅم\ޜ+?-bY]Bp:9VMf(>̔|`Ꚁ1C/ğӧԧiՃn1O'""dž#2݂έO{1r[]NybK}WL'""DŽ M Χ%`ĞψsҜઐDDX`$q -YLO9A}>r=]7w# qD"""(@DD8G0@2yA a0 }\k+xL22M#+T wEf4&"v߽&>]DDN< FX=:vB3DDDU!^oQ&.("r⪐NP߃S8u?O(""ǭiwOL\Q Vu]%"r">j>ۏ;+LBӲL?9fE{=NK4yZ*""ǣ iةX7l.ޘ0Ҵs Nv g>{]Ɵ/eD:>̣q7pE@DDk <ːϖ|1<9K_s=y9'qg.\٬.PJDD}G ""DDDQ# qD"""ad5 sa&0bfx]12)""DŽ# =mv#sa4=QgRLDDxR1S O¾/ŀF2t|Oz>8+/ ň1z׽\Tu}IQYT׉܎n26oQ[T]%""\5^B5-EDD* ;9I5<'B9TJo_I[~eWx}a =Ň?+QlfNoK5OEDDD+G &a}(g=;.t KDDQ# qD"""(@DDđr_ƻif3HJL<QUJzuu<9\nڼ0i>9|)`S;꿜WKSHzZ:]m9mߑo~uUDv_rHRR6yĕˉiRK;Î/';@.ڋDN,eTUp }ҟg7zb IDATҷZiWB$8g cQ([~ˉA~89(<5'2>mv/ۿ~Iou.amcǏ,E3R!b۲Yב8t3Qy;廟m~ 'w^DW?B`j]{MsmMC.w":ӚT'24$IW!^ЇfWj^1;?؂UpL>QVF Qn8Wуo1n4MhH1dbqw,nx= lCt̲SlSqgonnxsNvJql>MopI|f%p<O6V/, Qyl#mr\ˌ`_?O1̟xwQDkc =b&{dҦz ;׳]9uX?G-o ǰqUL#Y%ưHY*7ӊjQTݞ]D]|kdnΝ׾.QQķ<;:Rͼ2v>50y< ۛ^øɯs9_@:фWM~7}_ LqX‰֜n׼ҔKE0$`DƻEOLd8h|%ot^2mkS%* O[ace & (.On[ɊwFx]:)wc锾c&s;9 MGY=oxUiu%Gx&m *N$ {W]ITS2GK?tP#2qa(yo|+=q먙$1 2 9O?.6&l{F*DVoAk&n_\|l=sNiA(BêӨ C[NJ>κ Ad֜s ~*6]o̺k ^%⺂s#  & wWzV~m*$Ӧsyi<xOƞƠ*g>mӹ[YZxuƢ7`){?yWBax^ۓVcSJ.շm^1NCjfUSCoƷ}_/ͣo0?w1U/"Vݭ7" 0B׉ǿM"0,ᥱrzR|>}!A1aŰw._96ƅA`l-LI;{h%Ai-+ ˶a\:Vt j'.+~zg+oc~UҤi}ĨeZW{m q*==oiލp^ֿ+cЖ|Kgӧ׺;]; \}hπ ~=oX΢OG- aD9,fWrNm OL˦ؙvMuO0',Y 73OnA!DX#J;7Хub¼z!x@иIr3v#/ 9 Ê&n"j5iS}SU?9Zt,|:Y}h ɕŰ͙`gv"Xw u>jwJq^cʕls{ЂۆL q*yiM~:ɻIVCYm\8{/dcx7-gejvmE@i;A)vZuevk~@?o0f4UKKdI|x$ _sU%*$`Z;>$ rI؟f˴o ZRY 97]9=z5>sezfu'?iEZ:m?Έ;ѴNM$sɵ|y[]8sheB)eYŦ;hF{ *C?xOn6o-/-Eu[ Iiֺ ۭ`Ц- fcSڲ]/`@S_|Iv)ĴjM5J!}vYUZY<Qs@`vj*>Y{W/ӮЮmiڤ%ET\u[<(H`'+eݎḬO-hѺ/g~wc@Ӗ aj i@Æ fρWϋϾU:Jt?.fm8ke/S}F߁濎'~Ҷ?4߾ NZi}xUbӣ}shGjѼY(@&)ʯ/GPQ&<~cmh_& ;42+wuv!c=/\p`6bp8!{ fbWaݜ'&2\y 4“ğ~dG;jn6nj@d~x^O}w}3)M> SZruK~nc9qQ?..ln)}KwpE:bw) R-Cyb$&sP"\ҽ6 FV K)>z7&LcOWN][k8![ܤWx[yeOv`%лyQ^{.橌 jvDŽ?{ .|' i~O&]S&e܋6y|"צ;ц ?z#ࡩr3iV׿uS^zɜf-_Q&1As{]} g⥏L&,SƼ,O?t Q/-c2m0jಖx8cWuy2~4f4x/屳ؙ/:۝UyxfL{"]6dwzΜn$!fCoLyf;wNzo%Dx8Zm#G$/Aב[MwM^݌!wciuPꫯpN>8LdgqVRWiN@D+KVe"r$SX"}Z r2;-H,0 iXEdd!QUYi aa]M60 }ȱFuk~ȱTT_r]?HKK#99\ 9EFFNu<9"""N(""DDDR# qD"""8xTM7ARbbeU*ᡡԫW_{nFLgQɎo<>5(tWlڼ0i>r\yR!Bمɱ\_"4'8[+]%Rr?Q\pjcjV :>^o$<uuis6D7s+|=@D*d NNRb"aPA=OMzuٳ{MJ֛o>x~+yryܳ<]w5 Zɹ`Wn?] H|H"M$׎] 4d2W x(ξrUm,*O;n{ƝJVџh=r`5ceGgoDvjՒ_NRbyyyڵXbbbػ7}CtLSI #|'ov)a]"'&2zn>ڒWx5i[5jw؏6[lI9~NA\cj ,-x/s;9 MGY=oxUi־i˾5Z$˶lkٹ,\& Mꐑ|:z ˯ظ9<:7  E#w޽>=yU0~:~ܺ8a *|݊/< ";wӦMH$??}ᑞ7Z\K@-F\'."yrLll+?~Ϧjh\[]q/l\|M}V~a6Ϝ]#ΠƷ=w{gO`>Aw~N꾃T),˪؎,ɣ `u^ftTϝkdhUgy,fƌ M ߳Chӭ 9o~GecY둱W/=;~Ĥ_ah}؉^SX" V>eeeacBZZadg琚V|lcҠ]+°}(5Җ[? ?kpgpF4g wrcXxk"0έޅ;%k7Ы-%o'Φ& ۶&CPk6v m[33-/|Au=N@D3)lr^MbbHNN*Lll ӧvбC[+_ ,>X+v݃j^1 0iti:.%fʜ sUzIުD}?Ԑ͖-[xܤ˸cś%5%???lBVVy kd]Y2YiWeh׶4mҒ EV}ЬUC[m0lWWH6lhP0}-$˲=`оQN5KQ|x}IVՖE4,}>.!Y=sKx?Mb^j- _l ]DfxM T~K@`]v)'{|6cƌfqm<9iie]orDn8߃ W_ɹ ubf,9G5igN|EH3 C\%u`M_2SI^ۜͪo2ªq~$~,)Yiԍ񐞟Tv ƕ=ezܼx#܄Zemc'1ӿit t "+Xl]\E7s5yoHWcl/@D*SY>n6{11ߝ4kd ㉎/uZ#,?)W6f5|0/\JFE+>Lcnt6AKS C~{,ݖZxЪJU@ĢbolК\yrsusylޞIZs;_莤4w\9oȼ\w*mr,Ƭ}!_Iܒ22+x, D*W_/}uK٘X8<]-^ϝS=*;]<5Ka:n]&g>K.4G<"r}dPGXW .i37etk>@OQ[. "EDEv=GmGEYNawۗ6u[޿W=#*SNDHraxka1^VV\$Xa)#4,CӶV^0 |ڮe2MMZQN"RIX76maNj6mn:aݢZO{2r(oWӪFD&UX"MFrrreIxx8 z붧rkT[ZQg6*t qD7# qD"""(@DD8G ""DDDQ# qǚ}IENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-edit-scanlist.png000066400000000000000000001131521501654372000223330ustar00rootroot00000000000000PNG  IHDR{GIsBIT|dtEXtSoftwaregnome-screenshot>*tEXtCreation TimeMi 29 Sep 2021 12:24:16 CEST4Ǩ IDATxwXld \d{q:jZG]uオujպZG:[k]({ܛ[/+fO~}‡Szُ8~4ũk}i*2C/IyuzT;T腬q{dr{'癥7:|Jm%n :!m^jԏlδ&5b54H!#E^dt q%j$ţfo0Nf0mB6PReAƥ=gm] P2$rX^1W%Mm!|R@#e?vbk" u-5ېDrږ|5$n`/ :^k¼0;~E7s%jx;ͿL}mkmF_@(ץ{B%!^iA2g&p!-1 _Qޥ%\ K)H~¶A-gc`9;(3$v٣sH}3J " LRt/^7%c\pU+p|$2M1u/95 :M _BjL_-Ac?ض2%F(4Aʢ1QY5$.κ\9R9W,mP;'9=n'ɖ9|;_.YM/toΤѿNɲ>¿揽3+o, >L)7dI9)Ј~ 3LҪ k 9`nns̔_S[`-`atm9+hQxQ 'EϓBq&9TA=fRwmzh /VKLS0^~ӊM P # 7W9{):=v'`A]sEdf n*YҡCJ@!K<;yZe֌@Q2%qJs/NݦaW/KVAT>}&A8v-J] cB x@ɣڨlo ӳ%ˆo/'ty{%@ܡMޒYP*#S F +NNoңԖvX 3CxPMwx3ƈ 3xZ2*;Cȳ(e%J]/_yh_pdҗ|=S+̉~Ōˏ)KD>܉ETL=dg^H rWmɷ< *Y}2̘6e)UG-Y?]M>8;^8,B;wBUDVӉ4 , 5I2yka|%q,F6U̓Ur,ڀ-/x!^^"+\dq KLp0Mm.{eN^!$X@7 B B B B B B B~/""YAA_z uiA<@{A<@{A<@{A<@{A<@{A<@{A<@{A<@{A<@{A<@V0﷿%gvUnv-`͗e" B& QMJաܮ Ԭ W談/ dZqnWG{Oz ^/rwCIDܘDNoB! :IB2O>5AAA=vrĩڷO{%A? D F~s/'~x(,<jk?HryIⅡS ZqAg^&>:Kq+Er̋##ib)>e\uS/\z;eҟU*'Sʥ Q(ǥ 5cPȏR&ٰ&_פvK֣J^{e%T3;;*fJVzqrpīBkx.bnlcTJy8YF~RAȄN`A;)[,@ɒHқ٭?r1~̛щ9{Rgz J&Z=}fƗ s|eٳ)Mуi y~?5l۱_z{ĒipܠH>bgKv4e45ÈDsU*67VCS Z2Y>udZ|[&*ܪ-r%eNmiӏ*'TRU\Pёۻp6P27s/-ͪ+v.Uشd~&e(o:_Si* 8]5tW`Q2 >'7SDMlIox|ky4הeQބ52BqTvì/g,G^aƩE_Ѽz)|],%;_J$'ᕭJC0SF)5$ +x m5h44'*9Mb/_>Ҿ*5{UHgH #[ u ֧MX9@;~$T?/g#p+. /sܓZO Ov"poJigtm|IAiB:50Y{.'_kv`Q+-[|7,,"BдPfv%1u*E{ d_ x˗_uTQS?͎2C?m;Ǐob685$‚ öД{C_`|ySұ+R>0[;'M͟fiR4j/195ܐyݖQ~&Kxq}4@&9f\"Φӄҩ5v- H;'N?^VTu#XY&(i`VY.K6\Δ@yԲȭ yMviz |*-k9 Y%ϻwͳ=uUDVӉ B B d!'@nvySqEbld@Y7;,_C2Q5_oR ̑W y062`swK7 da+(Bo/H,lMZE?h-{ABv §HAAAAAGBTT]?KKK<<<|!!!!( s*Bp9nݺG_(ijkQti9e`/^QJ`/Y^/qAHwo3GPا2^YDZEfri,8],uO} ~+#r!QeʊէpE+&wФJӉbrNbzЧO.o!x)+/Bvp^|^w([Ib&^ŀ8 _Q$pBZ1=(% $ώ?Osj]w<(RfgpiO:6$)$ `qƆ,^f'jw/oMr'|9;Ը" jspB}ܮG$b-: mwmt`mmvta[C1E4MxH$d@ų =Ϣ}H\-&uC^F81YshœT{+ ~>ʃiiiAUH90q]c= Os=ݠ %ѷQ%%.CzGyNʸhC#nj}LrG6;hЍjDXy-=]:,*yEҕW'eҒ)3}hՕ ƌmO| ?1j}{Zpҫm#6Sq,9^)5+W g)g^={.CBa_*mư~RrmEqs*m7y}JzCA_'YrxV=$,[wq(oJ"xޥKPxhA,Ϛh"]բLn@h|Ѩl~NwգzK†ZMj;9s[`뙶E­F%bw׺hӷR-E]fݐ)h3%g幗oZIХV9|47JS R=W g[+|uSFNS?saaa9޲ќ35$' ˯>o܍?cl7Bny~?5l۱_z{ĒipܠHPǒ\3wxvf/zx3;~tI:N>I̛)0M8f+ZMҘeJKJ͒[a\}#2JMEX_c ^L<9' ĩ O:zbM]S}*6ou.C&HZvW5rpi6$p(c{ Nl.ao&q9s{ZP%,ʻ~&*NA_y9),݁mcy#QȗA8_֞'rR5X2<(3 V߳ab1N 1)㓧3.?t $'- Ưcۆ)4ˀXx= Y=9Dƭaq|=tD2,74W#Xg7˃ < ?FY=ŠY3 ?`dV%QE={bP _m0`1J[ W󣊫*:r{!n@צ51*[^f{fYuPjиa(equw|CK` ׫A7[@KL]Mk 1OrYUvLG7cQ)MtwHfM媄ꕰ 9H-9X.I&'.˃_hRfV:1'`~=>ގoOQpUB9Lí)e(5)~/ϑ:2r=4R [a?<$/bڮ_˘(J*4c|6',2?Ej禄rz[r6.~O2s a 9_& 9qڵƍ;75ǚ-p42Ү>B1Z)GBmBB98=+ iPե>U>i]j:s~:B=X8Ov f%NYSQANkYt L\8*oDe.5H)C)#* [ĦFȴw SL!>.>ϕ8iɘ7BvLQCS!~s7/>9 ;4 s, @{s)#nM/QuEt,_S{JɉgC(Z@ R `jbc>Ö=2ɩ-yy̫0j` e.Ӷ3ZHkdNFVJȲ : 0ʏ2yiM$dQF+-f(Z9yg@±ۛAͫb{:Jz(N9ځv [NP#mPP :t)WB@yC[ץft^R}oFUvo`Dז4kݓ~<ʝW)!|Fsʬ1(}%:(w]RNr47lgj֒ny:_w.7cƅ5ψmZ#׳kڝ.4ێm8:&1##b% #*7v(*_zB'P]W`VCۇil `Ҏ )ݓSٵ~ %gx4i՝?y Bj=bDDDkM>!Qb$\Bc_Ge~ͱqͥ&.d[Hԯ_IJ}L"xTř_r6M-4ܥT*ٷokP>;apj/"'S,55de+ǜ$q:9_{_K9)gO10̇'u0}ObD<,8/$9+ ֪F$[Ϊ Omj̙ґr+Z94;Dn&~VNuCփ>`/嗟VPՅfᲽ\ꕞQ9ǓLqAbZ SN8~ I5x&ĒX?;<ͩuT~H=՟ӧ9>6ܯ7苞LߛѶE4;Y-Jv$> UbiwLv YuO;lсLhRUCiU.  Wm܌O5e~;2pʎ$=aw-[:|ofOd?M8f¾! ʭy6H- 7zXP賭K9&$psouG4k7H6J7JIo4vvh)=4X ktbofkyh`h~99XG΢o9 >Û 4+Pxq/Nbp2S:_Nt4+{&Tw+!Zg;:#`.Wi=.˽l8ȸNi[d*jT"vqWz6}k-Re iJ6X8SAV{ti ]jKss4タ)U 3~p:]gA.m=e4eACXX;w|h٫0\ܑl[֒1&6a8}:Rw3Shv9R: <5!"tŤ}=y\&5tQ&1l1?懈+l2]w3zZ oOF;8f&oTˈuDz4ؓӛ:Ǖ}?"[ڔWȱPҠϜ䲢,m*]w:&ҒFӥL~tww3} [s/ي;lm60n:aTO$O_Ew5!56u \юKt1ϾnΪ}ɛ=CThCyyp0@FhSPTػx՟~ўOR Mc84eG|q}ՈJȼ2rH(GQoPg&˷eLLf9=~WJ:4/8h,\'Q{r4vjCv1m7S{p~a=!ywA&fjAc67)dYf׮]|ָq7${eNZtCWr>~-:ҥkۢ3_Cbj`|[ʕ`. , ԵͲ/kQEffR"8K@ *_`8pc)~ ,q-lAe =tMQ o2H˟.#'maفEHRXSq*ھO-XS}}Y}3qZo[%oh3glu{OT4-{FU gmϦ:|2I~2 ͘9( @)ˠOQZ)F5ޖM\CXBj2*K _n]ȯxs^F 7~3>Obߥ:>61VlnD̾' [TA> eĩp}ijOpոqc7ns~oSQϽc Vg,A%2Fi^ $ (jDETQ!!RUeIg)3qTJ:xie(4llJjs3zgc+mqq x!W/.d ݻ"fq$Gar H=.YFVX8e7 Ӯ SL!>.>x9C#[mk~#_4x~sLis`+vʕyRZbēcu6SV8q IbfR3qS[j3ӬOqwi5¨9^o0+N>˦mmgp*2*:$eu@4(?ʔ|}d4j|||rEZQj;wn=öZQ쥟Ȗ2UK/bǸ+gڣHU.cܺRREKY8$n޽#.sCqɟ F|d ]ox~*А% ޝШ-:n< t^_ҕZ@ ?Z\(b/q*&MP,WiKAgb= =!gSeƧvA&Mgt>DjMYQI( QQbêil4JEĝȘgcf} {/c(փ)ՆNLoQݿq7q g` _Ge~ͱq͕m"lSg 2z:j2ā_>g*l/I kii۵T*ٷokɕ5e{vWDƻ6!"iN뎧R_ ! )e±=OM%!O4b/z +:`mm`[!QGgdSoo/4 S3:ma"NbXjGhُyǞ^xH- 7zs$}̃?2 оwW;LO mR$4~  QGHxwIy*}+AC:8cƂ XUFjU)jRU%ZO ,јt盙+U"ſ2Fh4Vi˘_G~ůkR {; %{Y^ RR볔3/S&ul+j/=vȯγzPKsɇFfG+2nd"3}IU[Y==CLPO{ Y;]1͏tI1768զ_OnN}Q1عs{F>K7}ן;gms c"1+jGy;irxηLE,"0ċ+p*(͢q<~_Xk9 ,߀d[3O ^IAT9Eu_Ά>Yç0dUz<7z2l0SgXu85(͝J:ݵͬ9Dq#it3⫁(h31g/C_o(iЗɁ3 ےPy~?Dh&:d7Ǝڝu0yBhj5szx.CwZ2&43P6& OgC rc.@06' Q$D*geBo)zi -b? _ `9TMh+RU] L\Qow> ,k׮w>kܸsR.J+6VUT4Th0(equw|CK` oEU$aػ(68MpHpw Ib-R RXpmq--^1 Dv#!H ,sΞܻ;{vTJ"Z S6TRyl%yJcGn,d+g<#s2b]ޕE]?I*7r IDAT9Pp$ b@:{(r|mNzt^UM+>jہYRsbї,'ؓ q̍.Ӯ)PMl:7%Faƙ=$J^qL:(E1X_eǮP q-NJ%io?FEE*6+GcYuH%8'FiL3B/Qո֦Vr BCs|NwW,JU [DNSrgGLH2WȕІqezg23ףx/5,K6baw2f"wKޑY}flbuRnw?-gܔ>{ju$;yZf- ,àz?3MS[NUz*f)2em='Y߹w02:cMLA*kzte*9< `f 5wm2Ԭ,D7 +غYuI㨉ivRg-=v߄HŹk;~*fؾbsfҌܼv$8N:Νܹsd8g;}N_QhNy3t~(_-VO L ֧~>cs?m7gE+JKb7$ N िG(:$D{q |]Ξ ƾz:Dysyǂ>ߎQf/=IjD>M2-Ͱn)^6ΙI&#t3Zv}g ,EYQ?ު03)ҕLW5< Jr8%~4-H)=p2}Ѻ]#*x,*[g`Ix&ȟaY:u*2"yvt)!؉ӣbtQ7(GG9.^>Gכ/A˫Ѝ?~~O},J1ť`c{.6[nY g,Z`[Gh&(RЖI&3SG`I/icoǎ~ {ljBgqo\'xdosf3f0/56ȸ!Oퟂct+cǷA *$7o=`SjbT?6LÂVɚ45 D~pHHkө̻#cjM(v8Jt)PJS:،̛څQ9fhV JFݗm=;_ͭea2:E5[?eQz}bn㈓GIr+c~8c1wAo&3[LQ1tbNLthY|S5q/QJs|hhThԬYprHi:&{eVjSCW8~*i h4x{{x -g@gBwRnԘcfncbGNTX8ѯa| Z!01'GS9o |=JI%4-}eL3% ٛRjiGZڑ:79o0q0@zEyk^FA*ۗ=Z-/^$_|zYzK.j?? a/{s5 M0Z-} {!># 'qH !B# a/F@^!B {!0Ba$H !B# a/F@^!B4ĉiմ)S&t>5jeB#cǎBa!0Ba$F0$}3L}q˾͎Y41KHh>t tq'Ur:XaibVt2`[lؚh0)W cM,;/kMB"6c0)슍 E0bCtI{ c \JVj4hḺvL~t+DKDu3sk2d/Fv 9Íׅ&_El?=]vnYZTZ=B4d5f.=~By0łLEi&֩Z|v!Es?BwXԼ6UgҚxZ]eoW׉bϩITHvzƒߕ5399Awα|Ucɐ l≽ܸ >Sc)"qr&OlM_XώA)D6IYr62P#C|ZSc) /qo,ЀE~M۾fPöѽf+7!t3%YӒg֬XO(9ʸø|4ώ8&C6:g{g?:gE!-Ag1lR r0 !b Os܊T WNء1q`TxL40dOD[_,ܭtJPhuμéP:(\IV @ @!>36~As\ lYs 7\ir0tu(m]3~l׍NTf̽t;7IvׂkǻNڵI-ضevw'8hOCe mb=$9{yy#*E&sƫr~L◉92+ܟW}17@X02 բ$~B0qR"(_ ~3Ӈ+ӿT)boJEK9a4pՋwT[QOw6EJ8r ^>FeY;դmR,{S(Mԑ%zYI<[>U\W?|x{& 61W.%0h_Lث!O8'XLs{R>cg$^E*QQQ|T$ZtS]WI)V֟}-:;])5%#j#_Ӑd \׹t-M[څL;Q% /t Z0wqn0j4ʸ|~~bɝ̯11G~ ,Hbemow!׽, LpuG>8O:ny$…{0u?V]qb{@v7lVK1!Ŀ {,+[+8Z$IKV͛&ϹqAʿHۋf|ԎwqOnAy8)fiFU/]iT>'/yx6:SwK7ro,VMX#I㫵®q~n^r k;+aLJ>~ƷH4q{e'jLm~Cr;9ovɖ4|7CĵmHk0QғHUŀI_b>^ hEsO~|9B =o6| RYر?;űC臖S1!0Ba$H !B# a/F>= !hy:rS^7f L5h 1@҂|Z,M_-3h0G4tV;W/G%{z,\+)z.m[:S4 &fwM:,Oޘ,-md6ʁ,|Qޘ2`bg  3,nЈ;ԧXѿ>%ceast;OTS GU:B\'(glDg=o&нi~Ij.0rx؛U"UfcN1w^,:O8Ut,?mg){ -5@UjԚP0Fx|,-ӽηIl,``;CLQ̟[adk+0߇<9v&xόBf@8oQcyП57p-]Y$=YDRb It[WzO(GoQ֧R{U3مTs$je$Sg6Slzq#= ɪ?&PgM.wW>Xbpߘ}fn9vJ!LuD* boI&֦`F눩7 OWD%;4N:)> v>HRh.s%쿙/riXrFQ+9`A2'5r Z|A_<3Le,޴@v9sg9yp+ jԚ3$ՙx,gϞlՑS%JS;~!r[?*.w"~ž4=]miɑuț&㏆1qdē^ۛ2_4 {ڕ< R`aJVC10v,ǗB X2@;(H(Rʋo:IJey>c슓ʣ8PwJ95?O^G;PTtCj\ACִoĄ6B8juN9L]$Osbf췤F1!4އ $I6EC.Γrj;))EǞ^,݃bT(s6{kv|ض̓3{٭$͚X$@>1< +JɬPs^ڍS Ÿ|'N\OX:T)o+j}ˮ* e A63m-+ĜЍ-q^ܪ3"_ e2%{g4Ts9^ē+WѧqnL] $X(XdJV͌^ba,Sefw\WYwzOZ!dؒeᕸRo;cQw}FM63vUƘX`>3yJTYt=:[ΰ%x4 ,ؑ+cW(lpcxVǓ(Sny)dHN{'Or;W[aEUM 4#FrRD'K1{!L^!B {!0Ba$Pؿ`[lEI4Yᔫ[ f$h ra|S+\4`· PI{ 'ߢ~LѓӻN18XbfnMŨn!瓜ZR!>/ {=ADʚٻ}-s|GtTj#'TJ\n.qbbcn#4~ih 7dĆW&!~]dbtb (^5lkb-N^ŦͿo}r;iI/?BB"E<ɪAh:Q?0yk&5gfy x9qDefK7KQ>\$8hOCe jz4wwBctf EK9a4pՋw%L"&Kk-B( Lf$Be}RRPDURc^37;w8=y-}iwJ_7mB/#쟟ąXlr%sT(`bÅ3> IA(N8}#O艅 %`έÓ&B5{5#cG&$ ߵ!q(`b e93JFp(˔''|5'&Usn\{ BOepWa8v`?7t/s}d_8%odw`n](f;gcl,Y>91[DWY3EWq{e'jL'Ŗ7,wʊD8ZfsTd/RkG1g{B>RD'K1{!L^!BV!׉ $$$U>}zZ-9sL¨]~EQJvqrJv#8BAbRŋ$ǀ> e`a{,L4(9`eZ&9sg&Zj-Osϸa$- 6Il;?i5!D* AUrKa0O%3_l>bϖo{ ZQν]eqfN;%{ϷW׋2rڡ.a~^,DYB$۷/ӦMҒ% يQ\]Nj!+NH9oʒкR9&0m돬l˴=Ch;*aoӮWj9!ħHi2m4E!66޽{h=EIwSO5?{kG,yd'_ C4,ӊ`UM gǂUC~mB/:u*ɓӸW)caxJ`nA74K=z=NMRX(=NG|<ǴpYJޡPdfI0a|axI,.&#ʷfuV>g]G#nnL h]"21^杝Yrq!- Ç3qd2dƍdZĄ\~ϳg9c4UtRV k[!OM( iA0ɂGNKb/_^HK)c"!z_@9q}pÇg̘1X/QtM"E(Z}+HE/sbv(uq gμF,S:8_WJB/ΐ!CL-}L]$ .M=߮,J,7X05VE&^\HqL`wiѳ^ӡn)i!Iys]!3>cO) *^őc ھt:~75ר/ IDATz-e ʟ&ˎx&.\D#16cfryէj+H3RD%wVZ)m7Y.P۷흢S*K ^!҆1B!> ! =t B9qzG^a!e^!}ʋ24H^[,-m^8{c+aaՐ_F¹t%.x>eGik0O699: ҌYv"_;귳1S%~z7YoBO1s҇qR7Oyqmzcɽ45k3uaVwrY~39=Bך?5HA?U E]}L7єQO:]]F AלB6CЅPoR6N:ũW+hג ۽Bz=|t1EAfSRD}+=: ]ݒͼhZݻ-6dy,F:G>H"t5nM0x%}0C%1Gwkfަu4u@ N݋X[; Ml۬)5eԇ_`xۮj\x|3zK.զݤ)xUd׬^>~!=͏毃>i<#V+$rW mmnqXӝ"5jGǟqey&w;spϱG9zی+͟Wj!,=|f$A XqIl@0r\v BCCS Z-0%+^NK׈FIupmXLa|o#, N$VM̼gbm呴a'7D(຀Gv| W'GDAܜ l |!;]]Hu_)~dfa,ΠmK~H?U鞉k}6%-?RR"3o%rꂏL})H_FؿQ|F+$qB5Htm?䲯 ۈ*-Vp"#ﰦdd1sW%ޘ'%G/_Q@g,-b*6IBrV/H/ѐAKjͼz5stU[8[OĽlvK]Gd.Gl1}(ͽeEiϢ*!v_ɷB4bp'))^qmƌY;Ҳ-[vV3rT[_6 'mU8UAYdV=XлЩP(l&Q&5B[, no !Oʋ Y,DL0?nL`{}MkݙyA[srȯLqubA-Yl^/猾ƥQ̒ݖB.)^"{_/]JBO&a/F@^!B v&NeWPJ0qsm?ﶂ/-J_I6^%pmse,sGױM_8 hByq!%巻)&b9=涕q-FsnTI,3ƿ{m>;y6m !kw udY9Ҥ;Ę|Zg Sy q?G!I ڪyh )S&!>? tp;iZ4{a蹹Պ㢵\U:c>[V-܍_%3ÙG#bh4rSL@w_,Y.XZ;ϧ N? -~XXغQlz!eEݒAz!g8a%Io$Av=!pPQ|WkQ1 zט ^~L{hi!wT}0Rw4=`5 y%] _- {SLُ᩷" hnlk_Q4Q/xz{Q< ۺXlGPL2t?'";JTCѩfj>ۄb92cܻpan~r& =џq;h~-cjQj~w;i rhTK'2s04OB$ŠrsW|S2Nd'Q3VGP@Hg'41F$q>'*vZҕA;MJȣ5L^N]2^ OУ5)u?l]Ί Ifh4'圣5ͯ03XiOۃ-rQ]XNK?757CA>a0¢llmP464׌_O&]nm9MM v4,~L^F@B%:s4"?XxPX[M6l]']g[xDqs@&<{J4\'OJ48{GH1%sqMZSDdQ˻303{1G=[ϞC}d癚t2&3kk-h}w> llLQW\W|ӵwdo'9O1g,h}@ns"dֈi)<긆Qof^+7Z^ 'c.p3^<I#a+ꓛPGޫ7s/`mc Wx7u&1Lxle t AV2'ӿ+`_Oa$]܄<-uGwGS MH!x !HBa$H !B# a/FgFNJP`{2LdE!1Kq[<9jŘ ,lEcy:GLA+8Xcg  3,nЈ;j6WWӻz^2Z[a ?tUP  $*W8k;;[TyL=#MޜG%dFy.(W|[8r=[2u>-hEZ8:.MmH|5u[Xh=]pgۂ%z :pط-AfBm "8Aci wanVwlC2)oVJbX;:`A$#{Qlzq#=W19|\ըPjPK%Ҙ8ʱGrn/0)]gW/s_'A@u߽ gfLe3 "~bA*愣$^%y07 ћxK$fM3h>nx<~ D{1 vj$CCU8ؙݏԏZŤUw 19Wҫ6Z{xBHhЍ%0S4d$W֬nÊߧcn;}iză+UӒ#7OM $g ,9q!ylPbXrph/&äIZРXakӰD2!W_YMf`^CAf>ж85!O#x8eZ!q0DR82vkB]+lOyΫy(ұ7Wxn:@cӰQtM"E(Z}+HEHl S ܌1gi,eWkshIt9Swx"j5 Bg,Nm DG*6 +.2O\C>_564,<\`^bﭴ͞(6va]U*A.lfڢ[XWEI h9Z#:؝E_: n !E16f1̓bMu:wVY3TsgzoI3(xsawfd@őc W}KWj6mze +|Wc߻B//u< gWD7%#j#fB {@Ui}dB$p^1BA}oI<9z,0W Wg|hzP\q2G$pQIOnN`oPWItN(T(@=_&ʟ=CI_#eES~b*YXӭ L}lm;0~1t*W!D ($OW6f鹆8@&Bg-dÚhgϺ-bBCePa ⭙5߻)YkfKџ_Be31{!~:ӧOV%gΜBׯ( ^^^ڎ?7n W\NrdGa"""(VXSxqSș¨J+B1aoffƿcb% ]2f/F@FOUT?OkB'a/F^șÙI !01{!{IƍMm3f{$=|}}2d߿? =gdG!xpL~P#G&y߾}4h{%H !^JY郉 Æ {zbȐ!\?q$F/c=z@Q #SFZBн{w4 1"bBWt%ai#-I !zEQ҆FA*OQҬe!0Z/&~jz=/^Dզ3{!Qswwڵk*m888jqwwOyxxuR !B# a/F@^!B {!0Ba$H !B# a/F@^!B {!0Ba$H !B# a/F@^!B {!0Ba$H !B# a/F@^!B {!0Ba$H !B# a/F@^!B {!0Ba$H !B# a/F@^!B {!0Ba$H !B# a/F@^!BӴl|ǎiټB4 2eʤUBatdG!B {!0Ba$H !B# a/F@^!B {!0Ba$H !H)_NDD!!!Մ>}zZ-9s|rrcJ~:Prؼߟ7n+W$U"U>""///TUM Q+Ɓ_Wa(>t~T y9^!IT*j!0n{;6hYFO?_͕؎>3`_cNct >k4po:ɀO_'uic%7u^jJO')un1׷u{OxgS?=8!Y:G3ǜM3[׳rnzMǽ]s4z6ɳbaZfK @!}% dNN9b&EHskiiIgi5ZlԘ~ hgyS;QT.8B}?Eձc9O-A ㏟s MSY:6==LRQ=6uz˕<-th&+"_"Ryf'-Xք' Sz`׵_?SaZ¤834K%;h|^w{7lRf;9Gx2zBӥzApRzcڟsiu?Jz;{\ To'u%^SػwC5n4n#M;7%]%鄽6JgDV1T+@ v9lS qn,Nhqr/G{yjhIAu)_(Ύ%ц7,G^WGy!gŠ;=KSϖ>K?PA1å@J.WFt9puD/im)Q[F~/{w[|BƵAl:ϧkE%j?{&ҢR8yT߹3-lYν'_~Ooߢ83##N8aa6OIojSft>}bBbȠM e{qs{y*f)B?ǜ$OhmlwK嶳8L@UчfA:rw1 EcDS}4g/?{:U@uB^2VW\S&hTTTUOȶk2?`z A}pbyfhK< `՛Wi\z\V}%6~MVtˢM;ٽe6N0@?k'lټIM9:{z7у\ [زg:FGX[M3nl=iO4.?yT[;ؠDE|}LNJǬ+][W0LeMaR__@KjGRD.xtaU3V]jSZ&Ϩ~pMUSC}zqFcɲ#+;$|XsXi ?5Oϱ(6ݓ+{|a{;|d4^#q&nk AA5a1WZMMpe~~Ƿ>w9'xZGpQىlOncӲW q\x+O3M~** ӸV"/8{LW74 eՉ_b} Cqm+vLqS;J1#<6PzJDm8suT4gÂ>TNPS|ZMN-]V1g>88T} ard#W¡ *X0}%:ÄeA*4߱n :ںS'M\@~ V4G"oo9-~|n.xU(ޠI]V췛DS; U{} |+AK_ K*ǢnX#A}u!{d_dQr=ter=_ Wv T$=X;2ׁyt$LB@N. LɲB*٫m]}m9hk;'^KP$])c#ʒ)ET Y*v9C~e#G`ynEb5#Bg˗*$u, QO$96hsІ . 2/{>h9Tp!@,bB3< 8Dž\-BVRpy=9٤̈́+!أ9#Pž`v܋ !WrqB L!IDAT5~vcmkm VLJ|QL쉏`ޝUQ+^..( Vh hQ*VZ=eEY~]B%_fn䖊 +BqY.Ÿ)zy;s̙3Μ93sp;|v/gOFekiE4,87j*.tEl#y <tŢd2-izK<;7̳e/_5?vkGb_>Y!Sާ򒂬y[mYh XP߹)9~>^ET$%__8bd9EIbL&5v%2>*5`aN߯OIPeP7Ȇ5égL]Mey9ߴ:·0%I>b䌃htШ!b=) Q6 7u;*tEA)HȲ.q64Yʊ=?2i,<G&8= Yf7@ mzTͺ}q9 g y'CQX%Q֜8(PbUk|Βn^(f{$) 2 &#%.3o(8mH5NkՙmksC J9~Iq`8kכ|hZ7wjZvwXG0ym5I/}g;L <<#Sb*$3 Jd޿_>CdzΙrĭq5~^FJtoP._5IaA o8p תq3xɓ}&Z N ii$iJ;-O{5;_mU㜥Lߛ?i4ڃc=XV.T˼GE+:A{?܆W v 2%a TbAj dP]0 ^mYo,@+{Z33jo r.h4}[:aJvl4R;AHh߲dekƵRf 7r}sf]y`z,Yiޞ@/H 8|b6^phJzLiX2vxӢBx??vMZ}t:&]`մ!xek7A>m~:?}|QVǮҭ zT0w # 鐞aNX5Ú tԅN6eWYTYJF9ŲCo>?П0R- ?DzmٌTzA;Я SCeng}0b&w~QF=Fr:3j=ͮ2*G|%]K'FwŲ1n6I~ݯ 9S&Y.)*uW.AN-UpI*U$u#^>ٿh<1tj8?? zg}̲2kpDw/,xDT*;w䥗^*wQ_EIu0Zp̲^0/CQ_)u !Ѳye/ ZV,H$A'JR!2ZeD}$^pM:HJ"2111h4? Oca8<|Ȗ VFťD}4cj!=8JZʜ³NA0" À f@{A3`_\qqq$%%R.T^FS}4go=ȅQʷv4q2I78{A($f͚=鬔+Gٹߗ>Mo_f{{AXu8{A(ԩx/N)yyyw"?wkDEƻ~mLY1umQEt{`/sq]~Vu֡5Pqu?hs<'7>= @jtiؑV|>mw3`/FP22:gw#6A7c@ 2Rwt$K'ӧ)^HΜ=z$%Xݿą+3W0,,q& P| T 6uQ|:;kHKĒ 6N/108ا2i}D?EF]{D.Wߔ\-j{{KLt9}3w61'H4r-4wϼ@_ ]wɗm)'S^0{ŝx)gcvSm+FyH깸^fiጝ U!x'da삒"{b:s<' *)+I܍͛^e~x>QvNbI6X.9i`gj#Bz_d+UDbįߊ=m-j9ߟ33pZM9L|Զ܄9"6Ȃ5Q_Ƅn5yAg)ٴ7(FQʶ8|@`Mi%G_;e`Vc!t&<=}?@]MfMeؾy% d`2TD^qפ}yoύOWa4ϠӰ$?ϲIgc^vf?M!oz`e iys^h@-MUקyPOCˏҶiClZ" vq;iM:4!mpEA 9@m#[ٵKVIӯWcx3֦b99ikRS~,'^ٗNG㣣uy/kߟ^sa4gN/O)YW:J~c>Lno DWWP2n*08V`7˯OS*B;kA)0;hf-#!FtV.6WeQ<3YWz:kPzoDXdZvrοW-՛Iag6p_tlF_ `8>XyJ= uJok06F%UOY?!9ێۖde M^0{"ϽQ$qʽ¥TCɿX.y_QvR[LV ZѣkG^iv̎=xՎ  ؐЍO~Y°چW"-ӹ>OJoطGQ] ^֎ݼb$?b충NK 8ԌG_V6RxdOZ@8R ܉GFÉ#P<СVqYүb@sg˙@-ʣ47h&zWR%5ER$$݋ߺ@YO$eEWdY)xWV@-ˠPvO]Doжz%^0{ŵ@;m ]7R  yvl(HN8l=~$ŕ@F4a6 y΂'3hAa:bd93'eP>pLdÚ3URej˰,[s&n(? ۀZ'rS XDкR]f$)NC Q s"`npgse^ۢL+?S^5+v7Ei~a<.O,ThӱjOw$:Or첊cf3nMyQ *+}w|W+\<*θN.TS闓$e]/*~nm? ڡJSh$|X+);l# +EUTj2m_Ig7^6HS-{AKCCp?"w}➂o?O臕st+~bF dwWoK'lU)܎Pjg>n/>bcmXp>2U WVx<{U3'8?MkHB תq3xɓ}&Z N ii$iJ;-O{Öڮ&GV. .:666%.Yzj򆣓!"]`Fۿ!n l ,I S'oѲjȲ$I% z* YjE.UYj LYƫ~ /`4 gΜݤ{ȲLLL Mh;N0簰0ꕕLRew乺لˉ83+66"识4 ..o]+o^k\][&tkR# B9flKA0" À f@{A3 ^ `/`DA0" _H               <}NU" IENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-edit-zone.png000066400000000000000000002566261501654372000215040ustar00rootroot00000000000000PNG  IHDR&+sBIT|dtEXtSoftwaregnome-screenshot> IDATxw|MwϹ#^ABGE=*GQ+Jd{yH$|߯'>r߹9w7̑5K;886!B&)"4n-8{4 @0 ˖.1NCqNl޽6(͚5K,=[h4ZbcPUCS!UU1LhZ,--d mg2Xs2NI֜*/#al4h;N9aoTpn6琅v4ɟIǦy9 EtWXp9 42"IqߕH#Ї4qSxH3b)bJfWV_ M%HDC1l*&#a<2bR"q"ۇ5@ l;VѠ$;Yƀ.UP̱DFFa(ZZaU@aFU50XbM*56z&#1U*h: v8x[=J'Xhtzmmpxj!Ļ f{bz A#4c`bc>VUDWegDIXnCp V̸#}_q &"Q D=qEzdL3:"\ O:Z^m7娠\*''Ԅ. '*"^݁8k sh6TI-6*.q`< &߷tNZ"g%,9,!{CMwcc~K|΍| g{ϙ6Jew[Š=]+M9B|䞻sDD.=ik pKk IPcyL#AqOk hѺdR >01+n`j 6,__t1G.ʂ0m Ds"[d6@\Fpȉ_.C/ң3}T=d.7]#p0?aV/wN;Oӣ( p>E}1>x1|vzVz~y L%o|3%<[GշQ+P . ̬StgF M3I|Ç.h-pqDl`p֔тn qW3vaζ1ZSej0{& eB2e^|NM38(Q5FaE'8[ě^u :Cg#YyÄk&/E*fddƅX1ѐh.Z*/ *AchpQTL'wDTcCD?zs1eF0yxAfY#Dh -N6${}(hgY4q ׭lp@93 ꃓl=4߳3P:YӪ8E 0_Ɯ;G;5cX((MFOI=̮ipQWUk";`6ơqO&Laa( ƻ1鞙U`R4t~cj+w]P{YhmqT~hu=(QPIPj^~6G<$UK.?~m{s5 ϕqQDUr+CprxjT4TQVv/ &`E.ŕ$]^*cB@~ɥSP#r6/PukdatUo. NHLz!NqVɚ(&g}PUfՙJ(}hF!>-[ 0Nqlf&ˊbۣ̆^%KANMv:\qXF0jx49:}DA6_3l(-7֎WZUR ComX.G烥g%E!$*ҫ|ĬTe爞̺!;б-T#X7lB05jo]?m!`aDO͓;WAkaSe=:XoHե^xqEȭ\:r"`taH<'wԱ$HKVVI20QT.'k2ND2}Y-lpT3Q1D1TTEAѠZF50$hU6HDx4F3fZ -?7EE: N'y6b4(Z-VVXikKȃ1*H;}%ϟVڀͳB3fJDx흢(k԰$il40=Ԁ lFTV!Bd"**jQԷHjB:GeI [ccc6CB!kh4 lֲ+҂2VQ䵈E7i9c lm,PpcD~!BS2X`2d;Ԅ)NAoà}4c1t~B![h,,$70Ub `M!L&BTC1B!D!B!RǁIQB!%$T!"U)_BjH`"BTC!B!"ՐD!&B!H5$0B!D!B!R LBjH`"BTC3>Mvwrҽ!V$=X'E}6kLvhUK"50ɊB!;,:LY_yϦ읇jVKg[!MN=E}6}L^G$AB8Zk4gFQr!"5{g!"uD!&B!H5RD7¶gڍW^6+&pw*%"$r-]xG>Xwq!5&*QgҪdND }*;Q44V? NVy)[í'g~eQ@~ H8vm>_BOk5sѴ쳉:ܪطg##ޅjqQ7KZɗ݇xd/Ks=U!]zo|J6a;%unV@xd/A͞ 9MFۨmM:Q;apQMzw|0' q^6w1w:$O.Tg#B L45mfWWGnPlV#g,fN:qX?:zszR~iO~X7Zg8ńN6$>0]C߱N_|T],6[yZotЄ20%FqY6v9ڬLq4>Bwൟ3'ҧOgВdJo~}˛qME~8TJzihZ.Ƥh^@ Ӭ=SZEXm])g(ovSljWZ N3IAطJTm}Q6pp0j4!~ZCk0TmCJF!B$rdlD?\7Fp sZDCM)60w_ƭ(Urenz.qgɻ%qp!bdDVkW5Erfˑ=ViG!xoR}XWkF{[G_.ݪ?M4bTzįٖS8Rq{@VAШwiUYܭ6rg&[e)Ti\ S9IW9G!x#o(UK6;:m7F.?EdtkW: .OkX3O/l3S)ۙEѥlpWVώJŰ`3vv_5US׶.3g&=F!o<ݭd\/b#UhΕ+L's܎~3Zr|Ӊ6ج']d:|tЫR)JVbG/w*I#>r MS2C9Z4JǮoSe$B!jaFGL혅w_U*5]9ȕޚ퉡IW)+'"ijWej|@q%LŒXx#~ť*XcfU1mތ|\Jo!B(!ϒX!wdvsxRU,V!"YQ} Z9B!H5$0B!DY&dG!xg솥gQB! +K=E2hfJLp.U p5&j!3#MA'ͨIs^"B|Ҋdv#o:ڗI~U`"Bc"BO!B!"ՐD!&B!H5$0B!D!B!R LBj̽38jWMo?ĕ5h{9W:/B|B<&7U'Nsʚs}WLS%ey\gˤ_02x敛BU凹xYݸfnHl|62%3>t~6!C8e=c.G9s'DՅ#O0F >bO&mCf}RyLb3m~l9e6GŇΔw|* ZNZeLunfx??AHaIЃih=o75Rf:EL|s9J'CxJvqQ}?{f<2]6g%, xz.mjU$$mbT:C^\Y_V(A̙pHv9Q@v}A9H[Es}t~M| %fn2r WX1?ZX⵵l2bʪ x < WYy)-S //~8F%3qwW9Fl<@dZؠ%S/ɔo~}˛qME~lyzit \+6s$e >+*?F,.Ȅcatt8u@ ]`kXuEɰ9vYɃ=9'rt|,-Z%M,8݌y7)+oY&TU){տR!>) LTP6jғ%;A>P ѱ4Bzj3fScdaܓ3 .NGfkŲ(MnEfx]Jo5i:X,niY e&41y ݰRX"eEsrQCA-1ˇX:n,kq ](0Ѹx`f3CJӂɄ{K(we6r?[ ׃jF}З'TEf$5mhmMnK8XcK`,Z3.8 ˃Оpgܻ3#eEbZ3gB䒷02pt9_پ·"#e!,Po#yH#"2S-JU 7oA[\-|яOf<:f3f~>hZg1kg3iz1[*{3;=ٱ{ob@+>oRVÉF4 ֔a-vf59ת9dQŽ G]=J$ -޹b1ןо9nGGEt.?n;rF |=]QdŶxU0c,˭]ggg"V,ݏUI4S#fGN?pAklIYǝا3X4m=иeKqTdZ|'c69hlpE:x,c<`ꕾ ߁(Vv8BzO .0lS`nP z'/ 4MGEOiϣʺ ];!C^MRO7[*t*s5%,=߂k : ^iMl؀F-Qw/,gH -B!HA!&B!H5$0B!D!B!R LBjxpdd$Ajh2Vύdd2⌵uIY!Uˊg80  EIZBf31'W򅍔B|RRV<+ŁNڀ^܊UBO(DFEp)+RV<+}LZ-ț7EyiBW)+Z_JV۫RVy{2@F!"ՐD!&B!H5$0B!D!B!R LBjx8tȻL^OoB: Bϡx @!HlXOEjU3?%31*YԄmTPUv*HY!ċmXCgx灉'5! y_5!@yF& BHO6#ṙ+0qii/麀KfPeҠ9.{d Ex `\ Tyxr&@}KIO&;KzvzLɛx[z%w/vFP=m:jϸ0?͖ջdLI~ XƊݗPmJ1'MI2B|0mEe }t54@KQ5w4ŧ=&ˈF`[Tiޏ{f:;>n 갴u%c 4"i?[-[}vi-I=3MR!FFksrK1k/Ũs{XWӍnx'._06FPמ~H+iaw Ğ1M(eƍ~`مG7DۻzKL0|ɂә)c騾M|,k֪(N9UQ/ ÿ~~ӹ1dY|0901 1dN_bGA?: IDATx l# `,>?]ԭ?p~#gWTs/̚cs&VM 6QBetPUkYi3zVğ".ҭضm6lge[6|`]2x V/&pst&#&v,Oc9Ouw:2~ {Vv'+P;1o {&;ϧRvuԉ|l|d^7x̹gdlG⮔/ _Pg.՚sw'u 6F pdܯ,3b?35V}d4UP)d&jeW½ ,]+\W??r"W;W:gg0j u:xz_8QG+oH|>HS␅JSBU L܌ޛ&<)^էE m͝ݜ7DiDe]jONtV"s#O)J.G嚍4x>P3O:{9E4 '3ߢ☕S|yʗ͋VwQʕ]ܹK2(Ԥzͯiom-߿t`X6/ ٺ"K֧G ¹s`ju/;6XٻS*߯ l_ YƶooY0lV,[:YklKĆ2ԯk D&=8#q8~GU _tﴀէ}&6vig:fR;nݺzJ:}t^N"@Фk̈~?7W.ϢW(i ݊7Tٖs&ߥ>* v^(닯/72fz;&USknWdN̙X1cS'gҢT*CZz+|zɠ$~Ǽ}2bo 7_Ymh}*R;}lzw)"wC&Cw>qh,$K{+DSAw3O&zЛrB6M=k:,*v/ȮPOo7]+n\KurGVұb=ƝKkټa)ܰk]NJ|{W'& )D:; d0b5Sل*Xj~o~]nwDn-niӠ޻75d-LZ\Μ7ӊsдSG yriۂ\_KTp3?~K<_7oE)V)J{˷w'hpS}FOZoX1weI= &)MTG(CV>}*LDPCU(my俛o-+4$s¶ ^T}c.ٸAOKZTڃ9n;T, ,oۧKd?7_S)l5dL"Iݗƍq1%ihʴcM`{?㻘BO}*{g#+o\zR;n,U(^ϖSOzT"jT&0Xp8ԁK`_#E:Ӹl&*:Z'k" x;iݚ3g0ctfLƌiS616[}}zRzk<)]-V&Щ?_vP7Sgҭ8wϾV/))Qۙ: Zv!"1`c2{qrl-S6d)3օ$_"qA5@vk_x8;⚹%˃DG$;sM[|ն%\@\5Es |ըL P<5DFK=Ia2b- pE-&qYnJ&oo=X&6r"VKFy_X۪&ķt/?|6 0B`k9SӠ䡐oV01'-UdJq6w蹲BC-f/S-[x} N9u4l/u+|`.8iM6K7&C Jdc]ș.meeEKIחXo$$[ ' 7\L!Rv(G> YJp;|,_cڔ̟h|`6 i "[w%p_ZseF=1F_x-Aٻc;Mܿzm3g [ɰʉ5['[%6>OmaԆɷd:edhO͔ݵb7ز9I EGrdt;خ6cunтڎMșvfm4senWτ_VK'Oru8Tv+Xw-*(W;Ҫ8j ,{2|RjWIW(V Mҥ6{'&8FQ{sZ )-Gů~bj Ws={18)-eC79/i/+m$,**];5 qxQG&I{x5,1b:3\ȵP3sQ8nK/*4x%RoS~(q?;ۊ,:=5}K2iy(xOy\޼â8_rc\{es\Ix[q~4Y8}G/7]ex#ߍB18?/u}[g_ŨdD5巍#+LY:hM4Fz.;E=-P45YŪ_|)*ܽ#:˟Cߧt[]?/=XFpU[y'݋AndԩմLqRٹ˾RFPҺ%oxb0Ŋϡq&VLvk4P&2v7YPt.^N'7GCv Z!ח !"x;̷1c^m*`<1 z/ǑuES>^}H|=\\9{P$0oʝ%=h;t=E <-=r;_5TO3`LOe.^}\ʟp30fߌsN)>mчު;s}ISP?AmgU&M&WDIFm:oKx,D_aT+gk xD_p$ز XgWXs9J$2bgEѠ7㖥0Uc;O-m:;>n 갴u%c 4ZȍP'[<׍fi֒<^tz,i4h8g Wt$*OΧg͂x&,% U33|e4$=S {J|鰷!}>j^8D0j\F@|Xj5h4:F?Lf pFl^y>ڝ,ٳ?Eqx]qð8?v KJ!:%sX WCxztˢC'_1,s,la Gu~դM 1fٙV~%!0#kVt>qQvE`jWaԒ 6c]hP3-GQ!uf#&W5gt-;9WiP#nqƏEDܮـVqbfM=6U Gyrea_ԫU~z[ը ƘXDCtm_u5ϢI=j?ݪdc]~t:^=<ҭ_q ;+ 3i&4)􌧦#0y=>HQ ]7y1sh5{k:9mFsnVC3h=} ~ {Oew|o {p`m&;O^^af.44'mť&M(懺&zHoӦ1 ҹ8SNbcئA8*ǂt˒Gu#O<64#Z?;[2s'(L?.xnHIĪ8dh SSWn4f.q)~ Q+x)NSUV׋4 ySM"4a"2tbԮʹq}y&ӪJ (x)Eǩ\m[WithSbmHiţk(R\2Z>AH\iYr$<W)xTBY/iھ9,-R8;eVe˳!ޗp!U'|-ʌ{jPFBc@qh `PL:zX+B]=jBCta5\G7iQU:A Şw㷑s}Tz݌+OrF FK\AURx=1W1 l>M>kubȈuG49Op3B{&rkDŽSXN=8;uBWIlf²zoVvM 0 8<)Z !mÊ%~@,[O+& ['3{W,*Cϰgo0(*L2ùX} ^=s Wv|1y >)id1?H)ħxbC gIwrjLm*u]Hl؄d&_?bYNrINFy @=F0v{G$JB{2/9νu{T%^XRӵ)[&3F%zt(Mnyf&Sw&jUІ٫6a4f\fh^j:TjKo?#T>QYKNJw>/=feepp~%VQ$HRU`= ً􂇜.[iJ di 69{g/sUhn>YY|)~t:tVm-ۯ2ܾn@-niӠ޻,=r2&gxbΤޑByy4+v!0 a5^#o)xz{R>_Q Xq1\9_T(G/ʑ=UO+y!w)&%tqCZ#t?gl2jۣH_)GM\tsLx%.Q|ru:PCA%5=SXf.I Tޘ>amRx=87k(VeԊP2UܿzFAą9CYXaӣ*T3h=/_{yx+0Q=S5{ultYvݾH8:UgMDE'*6XIxt:M= k 61_ZČ-4nS9~aMz,☕t73)9v$jJw\).YEu[E@ [1 v_; 0Tf|?@%~>09}^kAN"/D(^YEci)"9.NRL&66T0[ӅhBa6}"~g;'9Je쑢C!ۧ9&9Oj߆_,WU',W@SԬݾ}X#i"W@y{VY3BΟ "[wii!W7&!)6総`/ҡ F>: ƒX{r|DR̋a:7N&ܿ۸kVFt)OQSxsLQn6~ٖ{SOcf (! &Oqqd+Q{zSѐٵj^ƅ4r.(2lH=5^A94̩4LgJY8 5)ϛSڅtq01io ʣoN({㜕T;2FMśxVP#:rO纔vL?wlXKl#u%;mzFV<mTX22Rï6ثP)\_b鳔5[ifbm-ؠ&N" ^iY@rٌOφff'>U6E(U/|!΋U>GyG.Bim2#zR5ukhvnMȉ] l%Ҿ<؋6P;YgkD8 ~zy(P#wG}tR$1$~G-qh1V%m s]yĿ(͉ꕙw?7Ri dw?#@/7cu-̬PǛ#^035CY t'`Q ubq=]o|fc 0mW&tIGbdƘٕpWZLiFa #γ? n U[G?͙~BЈFز*^.X7ѸB' `!fT/Mkhhߔe62qTƵXP*jU=ٴ7!c3"bt(gH,N5+yo'uk-G T% +70 J\F r*~T3qqbﻖn; 'fn0V7iYV8ϞbeWAP*?bR+%  ĹvUPmyL3=+/M ,y~7_#8㼶:'̦U7fV}X9Do+#&¯`uvG.t d,{a"m N+b vBkeF:džO0(jM^=x/u'J%!,~ӈC\TJ b; &_E,~AV1Iŕ,X{Q$L)WtjK7"(qh5~Kw43f guʰ]7c_'B/#U{lf؊hҍK<.*~eT8?&NBtp|[ZL.2tϽ_}^EHl;bL~¾TP}P٭UPB  *DENp&kPMiGg Zds|tC*Tdfv6 u <*53,ΤTdy|&r'6CJ1Q3h̨7]~)`i79:O l[[ r@ E4,QڞmOΖ]xFOŏEqw:}\ɫsLeD d@y^OL \Xُ.snȐ$px 5HԵ0|~,>Lqׇ6KtL(Xm'^|%+K<)%hǡrSǽt@5GCXKUnӿtiCzhjRP?˯̜G3/%zb%E,#Qܖ1/7fFy{D[uMC,vg3|KW#OI q5EGׇ+QO#&_i9!>0[U|OrDpvq e8%]̕4c肋K%&7"\ Nn>SʕߊSs/7ӳ3e \pFƝf!ܙ[),{ ٜZ`H?^ݑ97kMXʮ'3ZnUPt_MS9]Fcn碟 :%\Z*UQb)USϭE8w:΃X1wԡ^ 7]41ޥL҄2fxjoLνqlʶm1s+k`'mܿl֐cEj]Y{?IG |N$2Qw5(n/jTofeGؔ&yAh2,_Ҵ<t/B@'(yKِW_u5 WLN1s]dڏ-2Y0yv"NeNq̚A-B-c^m4M)Z+FOҳ 5ZPhv?T~F}(]4[IV_'qLOz(HL񘱊iW,2RR -lQQb :!r-wȡX vZiK%J R2C_*RL#MQx:@F9BdD6[pm7bgJ4W9ė.V+/v)Vs2*Mf`nvO> y2N,*?3T~&( =0[h%ӌZ"=[*{,ؿ}hUU)t{p1>_~UF FNm֙g8K9p>mp-[F$ʋ3mvlû>"zes"0s~ݗx(b;b&Sz&NZ[5?O^.nbty˯9eUYd" {ƴ2V7Ikn-jn><\N:r#G3NiL4]}^vӖqdJ?ʜYۦVE j{(1KsT|?FszA7tz+Gԝ}%BlJW :-ҝ9ކ5y)\.8ז<ٳi 'UmhfȠ=fBU5ͤ76L֦8D[[w( z`Iyj{|{{[٬E-``h$'&iqī|~ӗc?wg/Id| C?,1k8-oc2x(޼3GeC܂Y>{2{;썓6 J\3IYNDT(G ^j>u*+HK-U o|Lͱ+戣cqԧfՓsa6nZ)F8::Tʅ|E ?c<|qI7բ@1G(Q֍ƽn o9Mt20üf\ʥ(cƋWj\:=t6k`n1z68~5O3:w+wLóE2Z$hm> A$M5+cEhF$0 KgcHOM-4UQo{C8,8ƖExudP(>*Ru5D(Qf$F QDHlIam3!rԢ+ 뢧 Q>$WDS6D\+" -4ŤEeŎWD肅8uWԑy淊Cљ'USKV{9Ŀ.MrHQfR!!2QkqDL#-11$ EC'2JhePI̻X 5P0|\U<>'\v8bړkE/1NqjV@>+vdFLGvO8Abi%y<;&&=FW9}>Q44mgq*&Ѿ3JRi-RL3yXZS@e9I2.Gq{ul{#廷D hktT[&6>m@/J"mt .*$-]+8Y$'.$`\3W M2ET/+J^l[aQ>׎OB>V ͑~nHjSzo9{:?P.E B"p,}.*uW'>&BC. D@tC.\ N Iy@5 I<}+xiՕ27 Iq5 ߽]X6i2 TO&+"4XO=Ss+sdz9eYupcO!OpLvM*%R?%G6beDfuTQtm?F߰F%G, Hxy-3weRjNTHQ=wD#{ ڕ,KL1|~)4̰7wFb]y¹r jie۶/Ѩ֓^%6խyyd 5.5LZX>%ļѧfS)Xl ,SP›G(Iubb,"vv.A3Gc$\M~r5Tr!7iwx JA#V1>%6tʃm2:Οr#E2c=}{$xQxSi¹q(lՉyxI .> 3v:!3)@=xte"RdXږڄ=_ 1=7'?緣nU:n4t\Xxu؟2Zxy0;^+S|Ӽb% c$73 -)V'k63% ޣc#y?{)oCzRHKvu.9QJ`w'Ǖad=@ʮ(% T~[RR+S}"8)++}E^3LQߑ;Ls\X2Ck/ͻ*؞w2!d}Sșu{,T˜r?rd)Gԩ/S۬ЙtLٌKGD>{LI~u k(5)7踷QJTh2bb-f_`+߻I6Z#BWǧ1D3fQ 8MkR"T!2 u@eC}#^MDjhE5}0?LaPhPJF. bYG #xqe' ih2Slg7qh5~Kwf*DE{0b7V{1(/ݝiTl}qneRU+C*PTPB  bl8KdJBN.dĤQ A+2o߱ާDb'sTL7e IDAT"';o8W5y[,Gr&(0{m:\|!oPr,G'ї[S}oNQO90\DŽ.MW!zo/ϔBDu5 +KD}.yqRσkUVJURj:'i_] 88!YMXʮ'3Znzzm]dPg*zаPSu\Iɴx9;b5m=SԡN[o6z osX_r*SZ-vW8:ŠS1X o9w:΃X1wԡ^ 7]> ;)S\;/d4[m1ehjFU]J벅̲ww_s6+n`m@#MMox^S&,UȲ<&,sy="矩eP)? <ЁFhǎ}Oh܌rQy*' U <ؖv9Sp$ۗ|* х7+Q X^WXħܱ8>%,tܡ1>( !i *`iL usJ5Ѹ5FZ2tK930_%B -2q4^,7?ETyylm*Xc~rttSsө&6t"o>#Ȋ,[p}XZWI Yv0VGMS}FziK>d"PcY9^C\f}h7c&Z6T8ӡiP0U`v?$)TSI;l49~e "]6ʂG8sz? ܚ);ԑr7 Ѳ>Ɯ؜^@iɌ?kq!ٷ '5.-<ʧ$ LS_"̪yP\yK9Oh"}*@ 9JH"vbɗnF‹Ck(㷴(<>6$@Qwƥd6ۖQiތrogvm_Y6} 6=tn]gsk$S5cxIvxfm:/Ҁؼ<.q\W6o<<8>-Oeơ$",Q\sgpC^xAE8 e;{Y;$5ctN-{-dVf6Qڴ\+2Y:{VNeȸýʚ݇9{9}me~v,Zg)^IwLl3wd[uEng8:u&r)=Yw<;hYIk;{?#̲whۻѪ]uaIԊH(T%R/_F pS}.\xbT-*Dg.×Шe\0n6/$@7'@e >r{o\S&GA1c$ס%0 Mb*N&u$JsՖth\1)兢)U&na֋veh߹ZWkJ~) e]8mCniWM#α(֦@N+kl we^ zv"ޒU"dy S1h:ԢCʼn>NC/sH,*ϟ~Wr2TwρI@Jćn-5<}҄˛IH 5v\\Ͷ#i_P>LqOIXWsS1ժcLDTR[[ Xޔ7 uH#,G \ɶ˵ȠEh=ɇ/"ČH[Nթj2Y$ppZ"5jQ4+Jh²i2;qcc}ެЭЍ#Nan>;N1⻣[8SMR5)(9vɕKQpͱ+戣cqԧfՓsa6n%17Euا)BWawړ?̀gSWp4a|Y~_H[1bAn^ytL]02/MmMH'G2lJ|$֞Ƕ/K"9gVH >Q@%U]P!Kɹ$ta-_%M UWz`'S]{\qDLtU8A O@RoX8=T#e9:ũY)oDVlDD<| !%`g;۶ibPiνLR-Mx6:B**78;DZD9}1*Ӿy,,ێ Q:́կKbH\Џ_x[WN*&oU\PhAK;eˮȤ1C$4Db@6»(Z1KT@@J^䞸8e2I e(Ś2#*#yK w\o3nrNg\aAnF)GGJ; ,Ju@e $wNr%#Vgyآ?4'ZH)^<%p7jȖngJ-Jԁ7;3͖ Ȓ #X2Z:L:M]zȥ(}ŝKXAI@R=ΣLJe43FAZkR8fXr&ySD:&h&snz.U@9s\zK ?T?}Jll?|ctt*C/҉qe?ׇ[gѽwAX~K^bT?}ReC6Bo5DJv3g˻b8طXKnmmxEP7̭35 =?=Y]|'41Tpet&Ԧ8f2i~Bbc+tKiu Sq=XRA71k `Ү6L0Vqž(Z.iP4P5V]46B$6䯾MfdwO+~3Ma84A6QhTYG?UCicbHڈؿc 752{ `$كW=w$2tcW*=Чy 8~YZctb1#H2DenĐ)Ȍy;?{g:m&Iۘ@+ŁɃUAEJU 3hb01t} {ʷZȡI=)KEBH84f6%*ǩġleU*8gm˨=ٴ7!c3"bt(gH,Kb( ff~^|Wd($I%KbU!Sps&h^EڬwSb n<~5]ēXRʉgwaM/hvnMȉ] l%R R;8̟Ԟr9yY.t>9k D)B 2dY`R >+>緱)_埅TV"`)YΘG_Lslڎ;Xw ?M#qcdd$srDFF|?y$ʒj8H͝s:cL핑DƊD1QrXS"{43f gu)2.GMDĉHY3"ٟȑX1ti(7' 61`\\E%U4I~AI./f *?XRLxB'-TdU9u529FBPt+UH˹<Y,j>drmYżT Z4@M]R춊?Nbv,&cքGb| IĈR4tM(X.xp 2c+m͙B#2̴!Z7X;Df6эs'5sj&Jޅޡ/[5LGx Vu9vg\e D"$=Z8ڧP-J r8&FJ"%B;{֣ /A%4^1AkxQ*%7|3*jHFyП}3HʁQ]&nv Qgи-槶&}jDptO[`I[+Āw݉M:{Rb|qR%L[QVu@~ֽvûO锭uB,aX]o7[77]+GSkJl:zy#B}*GslAlބO5xwq5o{M),D ɞn0AkFLdɾ6BvٗXPG!2gw>>9syy  ; 񾄭MvX7Ș7uYx ں"G:-bRyxyuG",rnw $8n9Qv/#o֍&)1ߗnQ޳Y` rٱ7˼ q$k2vd53ywXح:gk*ҲXIþ5q; ;@ۍݘeLw+45=U!~AsiCTP>HÓ8R>s5uA[LY {d斗4Ni95듲a&aI:uS6 Hqo)kc <<e9EN!nM[SLH 6>t$DgN,`QכD2gnuC63񻻷f5_ұ&;-JȪ\UjH(wȸeh2欫㷍9vt7%ґ95^z {ͿpcC!|GMI;}[!^ƲÏ,[dq! Qk+H;3\Py4RڊRf#(c]HU9?Yj/g0@bU3"CkGRot'4#[>y-^p^tŷaV/o#9(œ1f|9Љ !~ ȮNҽ9&b<1 @NXdR ޖļ(,b&)z~KYޫҚ0i}G=?mzuObDb».H &!Xۖ(zEW*ifqƝiYΟMkаu4GVprr|bdH)U|jRݽNEJL{{.+sYXWE$+58i;)־Mj_όihG \|iH%&&–:E1cy0C.t &T+$/n&΄Ƭ>ͩWۋ1tb_>[>K!*4kOt?72Ϟ x56% oxŽqxޮe{w?SI˺ƙ󯰭ߐr_]O쟨_&FΖg ^g :˴h!!RЙ̂ <|FƵI. 4PFdޙWRZ0reX5-{AFax(QP"Džmi=8YK Eqi9繴Ƈg0lc'AKm J+o\PlJejjW#`e6[@L?t4 E"S~|xҽqE.D]c9uVYt.L]~GUcA\OI%E0-h+ $b"J d2P/v|( [vaQ;]>KBL'핈yL*a]m&b2ǃ'1њGJI!:} n+Ng7]ERSPT"# 8e5]u t)0$eF3C *KZ.Ë9Yrħ#Ū7ܹݤ%~]a%1{ ՖZY?Σޠ6jb I)[QI-ݖ|\ U+QR7WdW}syZccۓ^YaRH"- ; 57{54Bqwԩ;ty[jֶX&'vˢ|籔P(,x,6bJ2S|vj3B%)8y|=mKQ) eű$6tВҦ:V@Q+Ng@YkV# .Mƺm+JW(̜W?bȮ$W Sv9F© ȝQ//yDtHl`N~ɥ3Dv瀁m1"f/n_)`~܄)?X .1l]^DxT+Ϲ͵ ^ǵDhf2QWhY1EWBn_~v^^):u0,S~'PKSJE4—1!%<1A+GY0Vm)g,ٝD3Sc~ՙNg9s_j'2ߋǑ"EFzaH4t1*Ke󣳛E|Q7WKP\r7l'eςm"i@a*VTkƮ-?'Sޟ o{/ ֬,`(]͑OIr)5,mm#7U0ϻSU}4{oewXTA3s #,l+8~=*_!q |#~ D4m(+D&Gش DL1VKvϪ;~ڞ:چY:Q.>':HmNuWzR?8%^ͧu.ϢӞVBm 1=S=lʆheah@1/$~=fZ8GYMAh/[i 7Z63g:;)hDM,Dߴ8.u2m1o~Qm[P *?_dQ)s,;dɞߖzn(c"i,"@؜2. 1mFlNoHHܹXUU|e]!$?^bb066BM7Ĺ4l/NE! M8W*5` &FhVajEA2f\AwocQǏFPxRU(^={iJDKm8BÆ)FE_$ A: tL.'))Gv9nqu^,#gw#쾒E Ukɟ\N~þ=;K]{GǓaw Ryi#/X&j)cnj ILEt߰Q5YPRB{o\\9Gg%{Yr* `)_؈ C~?ϐBCIGTߓP&*T|Ô*e˝8ERRWJ|UPt0QqgvŠEK?>&)ݤqאJw/@WEL>ʜGxl'cc06 %dv/62j˹* +ф*/wy JK$nSgJ|sAv "vNd>]ίX~{O8 @S9*Ͷ¡lume%C0-?DR>C7dՊg߱%#"N&xU3LWOAW*DGuJTGoދI۽&2(N喣^~4v.D]hm4NƼ[Nq<5Ѭ4s n?ӹN_\dQoOJcR}r%*} TM^ cQp7Ok1%5K x:S\_X!*TX u ]l*Ӡ.gd}p&=0D۸$U[eb2pb3@uQДJH7dź6C^s vXМ)fg g^,NL_!R<_YfsGA"ECk:t\ͥ,M늧9zhZP żYLX4e.yF 5". Feq3>Kl]]͋63uJ5}&͗dZ 4sGJ<:{h$c^ HlQ%{5hN>yvF:Xdѓb-nxm%Y<u0+aONSY;.x:2-j}_h -͐7_3`u+Bk ./ڵhcʲsoȰd%9[25sƣcQCqv%b'_ jBQ6g,4m1N1.ftG|YS>ؒP]/Al>.S9;c qG;h6,; @L'!S`2iDN!nM[SLH 6>Xxq:K3u_ X㷍9vt7%ґ@4^zߡd651FtR,AV׷cZ\š>+w1fV/oF hε+9wK=>Е9=W `+\r[a,Ī&?-cgD$֎o%lI,V9OOUx7e 7+N}(5;jgGh8@N_HD$###"][jxO"Ƙa@'.p% N;H昈+9 OcI-2sJ̋b."i'UV"NQ;cI#`RfVZF.ƦD{XH˨*|[(" _VX7'2e\PzǟIEM)I 16Zfeڇ$ aM7~R=C# xFb"d6wN~%tC~Vš?2%*ӌPǦl"<8<]Eo2TX=2@q"oSL#5 tt| a5Μml(≜+RLYAGx VFƵI. 4PFdޙWRZ0rec CAWHLDΝEQ앸e2jXLYgز b XȽ,`D1W"j::[$To0AӃmKb fdQ_ĸ-%4<~+QydO`c5?t>B*:9/"uAV`o$i$gPw"98e5] 1I~w1ƞpF0LbItx11+_tX4;#MM8nv[#)M Rd^c|_]N<* jM*)=2nE]$t[F sy*TDI\= ]E? ^j*d6- ; 57{[ЫH+o, -Ds?cɉ3)(ߥ+ny%up$+' ӦLJ.]$ꮄZ?Ok8Drz [# IDAT/<`mcڹP2Pg<~hEP5`mjDO"# [EUn}3YSYwNɷ9y$;4\E,?ۘ⭟ޛPc2ɗrtӅ爀lWEQݘ5 (FGz:SW6(JhΫܫ=Ee*Wj)]rU* ,Q! Z9TLΊAjK9c)$ G$Ƙ δv2E=˙Tsw^(*OƑ`֐ߎulK bjeKf `(Yu"%ʻa}}q7!"EFzaH4t1*Ke󣳛E!KD t2Z坋Vrϓg_R w4d S0+ c̖yC)vO@w7=ϗRlGpkV~1L39R)5,mm#7 '9iP%w1@-c{om VMZR}d~A3}03ھx1¶iv |#~ D4m(+D^1K&aYTsͺg?NmOtmC,_BPw@POgo$6'ٺ+ |U[q*R0a,:i-4_qgT6G,C C+ })9g 7Z63g:;)hDM,Dߴ8.u2m1ί*lۂbV2%ӌ M$H#`i&sOd4vCnICg$qi,h3*mf3pzClFέoĪۂBE~RVLlFI?Eaf}q*ܩ<=cp@ 8*xOXP46xEc]jEA2f\AwD!*@E1("KDbg@E$@dd$U]|Zr)ja1*]qd]U2"o|~N0+Y֯P6ۚɥ^U۳y<&*SZIhɌX߂ }v@S楍̿cbpcO)B1acPrQ:(QsP0IvtENxޟP<$s\'Y>n GSk26g(5'|(QQh _ 5, M`ύVx7Ӷr=:r]:ԠM&v|8UT WC0E!r3c34j6TnCpjː2ֵ?mŇP _uM&ڐ`2c-C ث'æx}o"z3r63P~c>UPUPB  *D!&%tZjI9w7 j*D^\YİSH+c# =."wM?0 2LT|>b {ɟR949@3%`% 1v^ɉlKb֏c }*%>Wd r-m$輼22x8W2"/dblW&* 0Ig 3FM"A}w;ݭALmE7FzԵ 0/BnYv2b橉f1PҔ n?ӯO:FhJ%):&X;աCj.g!ȞphZWTǼ]RdEtԐH$Hu0O/&mX)fg ;+s!C8 f̎RQQhrM|lӆ"$C?0#IIO~ʭcuz;nĥdFT*)NfٴV, oOt*Y293OWΝK1c+GKLȼ8Vmff kfXsM ZAF}x^̒azuJ)[쭖SN0QmIohˡƒC{FP_"k:3W3 gSى14mJf_ZVt\=5#ct][i)w*+Ƹhg@JJJ rٱ7˼ >h2w-0~ן `L=u[@L?׷ p9m1[1#詜;1rqw/k%}  7%AN_UEBj"/a:c8bU΍~@~{a3gQ34BF1-cE*M$_k KY2 39DyMf&/ U'|Lvm@E@Z#QSeh1 MŢ?ʿ+G0*EU77IiZ)3U9vr 7lPQcXD$>*rAKxxdӞ^=ѹV7TRF ' ؕܠҙ9r&.MlQcIP4sXG 6'tB_xC= &pD@"F:EWĭ&v?ltf@\s zzOA3"]Զm*զGpWM`~4iЧf*$?9*QEs iJo~7dfOq^=gI[H-; 1Veha쏞kqGlUA@9j7񤫙a_+AahaPsjEKz`mjai\kzPBАVaL}u!2<'q-89}* K eb0"o] %+SýJsy ݗ:j(ƌĉȪĢAM[w{sH^۹CM!&pvoNܫf!5<qn2՝]P%ӑvhQ4Y7ޟik#%p!e! %ZuDIǧi|PQ'8o+u𮮼 "9e2[Ox{t` W2/g_zyB ){L38Qڏ"d78~&SuR -kp8>y;d˘Mh'Xnlcοg˃ve1eÒ3ٳ5Jdq$@ҥgy;3\h.e]2:ӗz~Qo5keJ? Z%ի\0@bU3"CkGRot'jӧy\xUU|-C, SI#0vLZ|;gYX$~?5ńb?tM$T_1LQRC 55 ) 5̤Gi 렦)eя dU* diP{=a-ċ^Ì54𣎥 bB,r@8]eSy"qOȳ2!ޡk|FZ kgsqd'NiQVƸGϠ5zlAS8>q R0Kiwy=buAD><`鳂MѴnm^ËNQ;cI#`RfVZF\WcamS**W孿x$ųx1Ƭ#:q!4/u݉~@W6D'Fa"Sk_ZV[*OZ3~"sQFG~>1o_!4r͸V&JӦ} &T+$/n&og #x/Y}S+b _(2$ƦD{X*T|0Qs x"gd*U&^yC9.^ȥ+?h}E %#aV#hL^Kl,ed 3 zFbNЦ(Y?=ۡ?-&Bv-X&_ eIH,/axuw ]~Xxwtэ  T>DĴT~SK}dC<?H͍vurWՁTT /Q$%$blCXˌIn26zFĤ܆k\P$htl3y+2x EP#-OSO_D1U#&<ƆMCpRV”!Qx N#o/Ά2$蠫- UQ(+kcWj-zL^ǔ2N7V&899Q!|QJGo`šO&sHR z 1X0v AKi4gAo>)$ 5Ƣn\L7"*-uVe 5We9P\ bJ)衯/ 4cX[HۏA}i&)ɹҽqE.D]c9uVIH0(S.%QF?؟fiDPl7O!1sgFQL&A흥S2$JDMG|l0*>o#"2ַ/HG -͂d[è΢Y"0~Il2? 2 +>')Ejt3&!.^8ϡ$L@#uCga0n(A0"x@2j! &Ԩ_ z dܚ׉+ { C|8^jm5܏gRk&rصhh#$S|vj@VJ xq;qXqʎ>袧-kr!ea3&↾Zz`Kzia-f'l݆i/~dˮI6\Ô}Yҕ7I(IVGO|seH@Pɣ 67S +9s<3y:)sz|B#5 RɓljT2談X"D3yrR2?֩,_L:pϔ|QGTÙ>DnӴk Oō3ݯ$y**EA );l/SE3S~n,:S|fA9W1 k:amNsd3VP{:(-sNQܡ fyBR[W{ڽUHz"2[IH7`zpz)+_uUF2&VG14hS19+֪-匥<~y ELdþ4PqR(嚳KLŷ|G"#O#eoXq)ȋ/7K"Bvz$_zp.z<`j BY_,O60𢖳-fZY_Э U5!ln|A[/q1:k- ,Wq\;Af/5VEh+\z%gL՞SS2+wrOgʐ3Sc~ՙNg9s_j]m* xI0y=Qe=f-Ce \(6%Qٗ:J E R3u Pѕz[Y{EB.5{:N_n{+*غ1i%y.s^.e_'kRקxִlQr#SV1nm4|,[36EdNs6!sZQuNS5<ykkѲ9KI?EY9a?dU'4$eւe=ٓ S[O eY$ _RSƥar1`@ YH3qwNUTYL%$)Ʈ0: CUY*{t ,r/5,}ʹpKr I3{货|m۷˭bzz0 G#BT("" +P"bg@E$@dd$U]=Cr)ja1*]q eDa .`WZdyuO.MwLoRWEyS٨U;ϰ*.{ APQT(Ŋ%kbԨ5{`Ec{W({|?l 51~sD;;{f9gz!>deI?r8 +Tw88gȪ32ȊcRE?hD'CG]p5"IPT.g SJt|2tdAq:>z>$ȴ%K'6?W@cDT-wqtd|1 `fœdȦqplJCeE@ݖ6b㣐j0hǶbL8SQN0+ݍ-O2^,>CZb?zM!СC:tW[F2@#=t1vhΘi~ Yw?q:t|DaXɕvn b 50O0{MٛR0u»aϠ9Sr^egdqz|̉f2" 2TV]xΎLa̖b&l4FdӻV3kK;ʰ06r*tZBWT[O^v`Sr>W  1h~G*n%<ИETYO42籤GQh_9uC̻5\!(3.Frif3݁)gV Z&23HȶMRǧU4+oHnh3}&a{2>4݈#OVߠnءߎ5'w34oHYr 0ﻁL@Y~ANә;գQ>32f^ UTJe_\-%d.Vywܱdߞ毹Ϋ>O6AUբYM\w1哆猵zu-Gxvv 'UHDci b޷,ScYgl-YQY58]Ouwr4Why)Q5Ǐct g iR$eJ¨`)|WfˤpzC3Tm1;p2?2o|۠.4mOf:2V6mTZ _qb#i'<45^5nXuuaHB7upڎ{el%\aOklg.,ԜtCgΤ37\z y*@si bD؞ bݬX(Ш)ޱx(G=uȖ/#D2_hn5_gH*UW9zkRuv}/jQgUP_Zj* 1X-(;V*O=y@>ɟmES|Ir!?S0NwbƦ2o-ϳ dŋ\)Q[HYZ"B.qe >ŒqHD" `hd).sl2j֦VF2 5KLKrRJJ#}HLH)&:EM0Ti +k2scc3cҏ-5GydPķ:|$jfb̽*mcd}kK$//H'8#3ڲuenTEb=@DQǗpHMSsT{$tbd IrPN^RcF7 x)#x^/D>Hdkl((BFdzO9 jE 5Jmɠ;pz*eM=dzdŧjO2sʹ_FP(r-:(_Ah; r`]լy1$S(Y:>2Ȯ!cLL$$ G&둷DB2$#SLŦrt]ř0EU l0uJa 9x0??°.}cMPbYUkՈ`Ք5d 1$ Y͙S7ڹjP )Pxf1N1q[E'pBBv0ۇwI$ CCrVk]e(&"c6ځkG%i/^}T >ν&r}!1X5AhMr2LΌSr9M!X:rƿR+G$}cx >"̓KX QO/J@ځ{}6Tp>KgP`_2'F_GR՜g V\ 4 $B0>9L >cgS|J!=k752GZnhSo KNsWzR2' aYClcoEQ]ϤS1+֏N+xB2~3tcVՈ06Ye7ܛE51 l܋=,~Q0u*H'AAʸ0-+f3jxMgNASܤTu˙4R퉬 *_;=<3kK}Wo2}^%EK@/xçfjG> K~ʼV4VQGֳ'M="Ns e˻늯$r$mhU}2=#,:Sb+fC=E,ɧ,SMs~|yTٟ#֯rȈ5!Yh ՍXmDz~-s)qwʇ"Nf+W|>KK>X\¥wPͨ 0!#AG]p5"IPH~֐>:D'C|e(z3oS[ f-ѷCXF<;7_Ɉ1 Ue2w|T/ ɂ:1t}4ܥ:H& [yObQX8RV %5r ۋb%WrCDsd[: CM26fH]f/^1YŰ3 KoVFss.zн\xPƗqYp[skw_*O۽YU(˪YRNu@ı/g]q;3gmCy U8 y>u+DڟTDu+LBecIjv-4X3Is34gmwUJR/+.KKSkC}Qr.gbI;%Qed M)X*-f߽w5 'WϨ@e6nZ<)G'ƻ)F37SouhT|'Z.%nv&cT&9 xqqB֎u24zYlik/{'ѹ* , R0 b6r,J2mdo#n.5ə:#案+"+ߕ~]T)O#Zo#3ޝ+V!,:n&U]UR*93)4Z(]qdL3 s>+ A}yŭB3~57DcꝬ^9-VfBZ/iF^C2 s{ʨ5afL⊋%s\c&H%dt#F*6kLeb[ ,+') ׏,cԸ c!i܆A7FzIJu@C3"4<\ׅjVtzC<QvxVl+h[Xu:޼ &L^/ o_Y/"cm w[cNGdO09ܯ֛i\Pach14jfmTpiG&-aVm\eyqOYo՗{?qפ+fG@Խ\3>YbCzԢA!sjZRc#T`خP"6{r/Ķ5)aȝ>/gub}˞B8v#%0es [[}Dj~蹜uqlM N@֕iۑ,]>Hs/o^EefOhLʯrΧ`\BT杤 +[UzW.ZxC8 ·o䨪+j庿Swv&_]=DB2@C :mx. .JX9*qihi~$[J '`yʤ l&_Q^4?i1L< |o:rdV _ԦIh5&5G%4M8#% qnLW g H"oM FK[ըOp u\xOYp9ʌdp~5 i>'saRktj~.K;?]c\oY[Fը۬RW#fr%wy_Oª_ʃ\LAinOwڻNz෥ !PԘz @Uww 4Q$)q܎F1|2 4jlTQEm-1 ӗWs~:ׄ9Kݣ!xI@t.A|ljV_FRȯ̅k0xsdgbhL)ϸ6P*n ><ƱJ|jV)wFBI9G޶ wyBQ{zw!khpI?CVrsҞRDDxB8fO!Cє(}rw|Ԫ7i7[=z_fM@Y.^;$69rx;ӛ[F`K/ Q~/4~E{x(P1h +5ƫjAƱ̺]cOzJDԇFH"!H&8E\Ģp[ ه},;'8nw"a[gu\^M{,*Re2ܻAYY&.rEi2r[o ݘFK* Kf_9yj u-bM}AĵCi")FEߚpαnM 4 \ki^ m> |d@>L>Bx gܾIH%9%woe oJҸqA}̜( V8WLRS@a}teJ< 37[4ܼzq)ʺe|q*S u\W#sKz`J^l {'ܵ%ρ XkH}(j{/΁4f߯m׊f݊@ZծBZ u0±xs@N-Vrf/X? xS/j@,h,.\A{pnd+O܊I :RDb"HKx88u5jF3 E|%h{qa=VQxtsξڕ〣C2IxuBbޟNb)5zjԭQlƔdhk1<<

    CXXCwDZVү|o]Qd*!q|~7ܞ,eɢTjO-v&oeF}oY$FQ62&Sfm~labm!SPda-ͱ.ܑ ϴ$'F~ahcfXu SV2a}bwMf5~Wc3sLyFtLzDQ^(Q$ IDAT%z6,1ݖ̽*mcd}kEʹIYn;m?0zHL 7U?==y9԰qtm- y4iI$g%!Fi XE1Qz d0l!iwHp|F87y( ;(= ZA("_I$JZňhݼ'lhcܦ. I=s)JI;/C~EkX/y{lDa"Ԝc ,jI˷d`!HE4gNDk熫VAṞgbD̋(O`n;#$ CCrh֡+3dwOO<1nDF̢[xL>Mqk@\ (дO{GGn$hcuAn%^dܲOF\xA U dS`'ūw X2 |M:P2Xɽ U sjh^8+'o'pƣO`z}3hywˇ-  Ixpٽ[ጻGyS!$ܣzh E?(^$]QuXhZt G,JM2ܰL gi304@Nų0Bo1vQ$ھ$jo5}Б3Z9 OCَ}ظِ|x #<P,wߜ vN#{Ӛ*EmQFtց$¯^^y{H>?K@$=áqˣ+!lZ stZ#Ry|(!J9<ȱ ꃲ,Vf~T4c8;Hѹҽ&~>k7U;tj}:y]x<#dNשOVL^%dj еS D8ˇOJJJXGʒ|J!=ft.M8ҪwcƷF [Yr23'%'156&m*^L]>at2Kk{ضDAӑ "&{8f3oiY_;=<3kK}Wo2}^%EK@/xçfjG> K~ʼV4Z^dX[IDYK4B8ͩ&-Pt||r$#)Y]Cq$}i{۸0(1Y3;fD&v=MY_nQ)yUݢTցb1ֶxAuٞ^Tw]Z.dj< \X-ܩd*RM[b܎H@XYAFX-ۄ)m{9O*="ݙ4\ndטrG 7UgxQ 2p]r䙛 >3Vo a٭> ג*IHfX{P,ߛ1x`V0QU"mhU}2=#,:Sb+fC<&/Ax^fxuW^)&c^ZL2Y$KjB?0$p W`Z)[sk8֌yfy(r:;w'Sdvи- 7mVp -*LUKg+WMβ>Mx)+T yHطkRM(Ti03lbOOi0ɫ62@Cu*ߏM ޺&uXV9mu:t )&IlXXg4 =Kz rwG/S nȑN_~f1=()_xܚEʷGKdqɲ&.+ڗBվnZEL Wrg FCLL,vyl<_ #)糘3ڧNruO1|&'K)TfS_^i3ϭ_\/g _xӼI/t|$+  \裶㦞_˴śr{XKgF¿Y6i3kFQ7ZkY<`<?>K;o.<ȉ39LzRH~֐}tJOIaך$qAZ­7?e@՘Dao #|i'#[|v,Z3[NL,3nC'GO#Ewфsak03OIL2 GJ dӸ~T5r ۋLe\9:tG9zΕ\CǧEѡC:tݺT_jnǘڡ9c{'fĝ b3}J<ɿ`ȶtdnmĝl:>)_bagf߬lܜK39t/5tA6e\;2>Ho uLR{'DՕs6GiAJ)豐:-Yǧ@ı/{FΖuêON<}Wjqv:@"VFtOUjCq$m b'G&PcXGzf 7֯ጥ?-|w@B4gwmwUJR/+.: |¥ኵ>vŨw9Z2d2&,Qf^j2 UɡɬskttkW #L c/'#fWWeڽ:}@/O0{MٛR0u» z3,b6r,J2mdAi`m/j¢SY9BLUedB>W\-QI]Yx# yO8=/?q].\́>¥ܽ{{s[c.ݘdr -qBJCP?fobf^qЌ_w V;IгY\Cb5m&kf5T ɐ+ 1w\ZƳp.LJFnBgt3ZЩ:9__\<*ެ{ a 4p%} 5֯;h*y=18)>_ RL i7ТZ O6Gܺpe(us8V~ىq[UƁ.yH6XWB4gjϛO2Z&O4ޚƠ+gA桌,^ nОL/f`ڛGg{Mރ{`U/:-ǰ-CS|ŋy2fw!;cN4^`*_l3_&b^6ojfmTpiGaFy D;5S)C7ehE% j]-&w:\!n][oع,=;FG~Ѹ~P?'^:FǜbLkeI 8AQb~P" Ŗªv!I=$/e\y5iĝ~BOX@pUDg^AB_+4yMBOY%ti`W*(Nm,1@DiP_. SB(GbӝW"F{Coƺ QEFtܖ|sE!3\&HJLJDu }sGsy9 BR;YqX"#ORW_µ\ <93N1XT*z%xZDmˬ<)DDi4΅"Hu~ҌJ5:'R00Nr\[e~>_:}2ESgj Nش[yARVn5gx"RJL(pLo*c[oy |d@>L>Bx gܾIH%9%wA\ޔTq-ԀYX`]>WvAt,e"8: dWY'dq-vjcv@ML!+WnYzݬ<9r@,MS?LJ2\cxpcW2W+I((LsFN CNiװN!6/{UBi䉃ΫAYgOCj5R4K>/l.D#̰0í@d̜k>د2fWd \Xx!Of,@v~`%"D042J̩Y9,F2 5KLKrRJPn>$&$#ryRMo9\RglCNl0$ {wU]:U7:;E㈏.nL3$1xS#z3ܓN _I2iy:`nJcϑDrV""bd :E1Qz d0l!iwHp|F87y* ;(= ]A("_L(k#ytw# rr.8IΥ(q.ZeL]\ qcb"!Y6`w82y0?_ !b"O%.6 $#ؙ""8Ad}&R%f驔6!B9yIQk$+=A:E۩l8p남fMkHLCOd!r~d\7)ug8kU-4`烟..ԩ*-úL 4桴t?)A20Đdr4$D4gNDk熫RVARBrN1q[E'pBBv0ۇwI$ CCrhJ'OF[5"^?oQnz[JBOPx6Eq-et{GGn$hcuAII! eQ쑀80| Rٽ"װ cA\lZ~y={=ȾSh R@U&U$޼-2 >O{5"c6ځkUጻGy00Āb;\ #H׳˔VB{Ju2kGqH3Ȼ۳gMfw:JRg`L&XIx}Syr+FbF'aTKnKbU2n4ʷ2cL"n`UW께"}Jrs~]摌Q"큚z93-' 4g(&afU^)c]J1=!WqY-;A{iJ˴7~&,Am!٬5l%ONhafΒ3A2=, x;f;#{z;d^ưkj2 M*ib 6eQZxYf2A,gǫ~1fKѴ\21S/M@nϢc٨YE5[e]oBDmC̬]ɫ~ IDATSޣryTFCH[ l܏X|~Ll΍(s.)Pm4!ob1 =S=ڱya.'J6 d׈䗉D<K7e9ukZhV41oeLʌkoGt^6n ]1L!h1`FFܿ(RMA\9ͧ0D;oHd}ܨ7CWOeiWJB]c WB48j\V8 _ޛ'."U~o+]@JC;!Fyaڅn}/W|<{^ӥ5MgDq{*ݦR`54EՅ:LZu4u?p }*U߇ˣ(鵄R!Uf'K:z8TloN3Rd-sՅe]19c!{ײ'C嗌 :CXr am;Nq.XnʘP6NPT7a~ OQ+ҝѰDZrP>eDvBgX2l)Qg]\ٛ9/jK2JrYk!kuIfSw`*F_+ʆ 8 ]J h'#1 Uٴ٦UgoN?E0w|0C&?BCIހ foP3xl lm*g^7ZJ''芜e4ZF?و:0ڐa]7uSz^j c ߔ(圷7SoF&gcƎ| dHH.B2L$ċ 1 *2c<3F=1Lk=?(?  {$Xc8r׳!kTKξH*O3 9Ʒ_UCqwXւ֠xt´p_Hu1p]kBogdlTӛCgzz޽4p&IAe p R36[y˓HGپYۋ緮g_U~g~] STD?؟SN__$cyMt1)@΋Pu ǬW}9}(lm&7 b-lV{S4u)Pʡ>150>%bF689P\fV ik*u70d_GV217# ܿ{dAYj#g2rEДdr4tLqHA+ZQ=Mu sTw}(3O OEkfL 73s$QZOɳ ȥL&C%:0z- vp, GD2cpޯ_S~g~]_eԙJ@h<7C: hU1ѯO/ )Ar\hT|*E ީʤuվ͊4KbId˝W$9wğX_>%C6uHY@Ɠ Π6q۲H!0(i#(Fs p̍0Ogj1Zط^ q\Zi4j\euuWLM{oX_3Mh{ѽsfπ}BVA!d >Q?Æҹ90n#*"\̓YڥB|4.d6;p ŪSSkCJtD:}5Cw0nsСPeG^|dN"t|.;wd$9&hEf2#qnr.IЊ)NQ@^+BiH<. kyr)dr \W ɜ֎!-r>f],ܧ9t1)2_`ᔩ!yC{<˔% Zw|Fd@E";Zrbx K Qq"h (.c8[ΨVR,2ݧ|XMzyP6 h˴]g̟Zܟˤm<.Sko0ޢkTH:UnoǮҸ§]75K~ KVDyZz!?%{.4^-Jv$pMR93r[zy,j tEjΝ80TANjEa9!dS~#t8_S'ӿwC&GDeU8$WҥaصYȊsq-Aė?xQOAtKFQ>cUsǹXin)6m{K|ZfS鲔 ұn7C83 =wf|< >:0a!nD$nd7ϧ XV<RDZ~ yk)fR Q *,̧JĜ9 }!!qt\wsvd_3O(/PNNx W\`u;;0wkƠzc*bi'tȬKNPY ʥ6,G&˚eŵ[.>%N2II"OA\7Sk (1ʑ m9IINY 5dXd:ז.*!KU*]}f'q>,{VWr XY2Bٿ?̠Ue<ˤ#OYw9*u2Kz;R|[P3nF)DܼO4"III_!ga5Vܾ8$c\/<r\4y;vЦE֋E* *C.Ÿ܅qy/Liۡ#-9b3V? EUcVJǏƵGmd,+#3P0]S\vq`j;Ws/%{m5tS 6/<nM"#ط䪋|BֻcllRD*Od2Jccli2 +{=z 6M[RyIOZ2K֦UthV [%ϏZ~ZNK"2{?j|,Vy mM0Dd{nrHy?N9=@UPDvXX%#5na\JmG.U]&y:~']<&i§J[=R^5ƭ0B]CJ8RnJ2!8uGݤULZS{ˋ{U@^Ro$^H'?4&E(X>zzsk -tb*ebA N_F,K<:Sxhw# zLBLJ$)@J|w þ@ .Bptrl\q D -G@)*~:KNlK =sKen;V3E#Ozn[!h?}<%ϟw~us÷sV%J=A!&ē0;CyI|MY$ZG@R18] M"o~K<:o1vE6,ty#=R;kKD"Pឪq>T* y}ׇ>9Y@N[day%?7*CqtΏ8_O h/[k縔ZQ;k+x[هgTB;l e逫+jae vC7+Jղ96R-nYNOaYM@IDR@rv |EA!۝BR'˰rr$e7GN'k:G[G16 EݓQnZUDr -i_L~g~] t8l]cTޝcgPMFܸ[wdNj7ad1w8qw D?,9 &_F޹WF 뗵X(qA=]y WiؚZ}(+a=7\C /I8H)SV)_pk\< 2w0"cy"i7i~m?z0(C N]NA _׏QXnTӃ 46;J*Tߏn0TЍ܋fEplb۸aC=3̴S8y9˶ZeN?ЧD)&]2)22'{;3l ^ʔsL+ȫ[>eC |;ʮ*=q׻>M6Q[-y$gGQC]#8B\]YA3&nJVtq]Mߓ*\Y (~)VŌ,F7WO Z Ū\ )d,ݴ8a2T \u)$Oߙ7j8r I㘷n(m&FFi.;J׬JCH_j[ؓV3s-3{a`nFlVLC\)^)2} D^!jXndpLSro% _xrQlB'Śo03 gɌ D<NE($]#:_UdŽhAꋈK&XCu0ukZhV41oeLʌkoGt^6n ]1=GÍ75lFh 2Tb X7¬:!U+s ]=_}* t-(^  X` 6w_QsYM%y/#&PiwyU;'Pp/Etu ե(!ya\ĬSwy jƶ:{m'mJ?ڍ* dxH>؍<̏׬2JW˯86Zkbf[r9 ЮzhãPv<c5E0e^d)XM L -I9arzMS[ȕi> (K,k u7wg]/R1WB t̗dk`aNփY|"*(f!UfϼF;3kA&GC4K1̛gЊie 2 ;]"7ҫ-Iiy[\X|6o-UӀ3+9Τa0> {ˣc#-uyҟMs6T H8ɴrwfr@?L:&m,'NO0{S*Iˢ#*"\-Y1Oy@/۪La ʣS+ mW6,Ӵ'b;4 %Q*2$x"^`d&)(x\A_Іgx2cQ My0KB̝ІrpPdTpcj}j ~Hv09[Ϡf>5[̤#Ԣ9ˇ̼>s{Jo/'T1OEbvf 斶W>yYe}Q)rxxy[Mbvѳl~.^xyiJ`vך&CSU ]Pl9~MΗZӹ} JrP?#\!fZG-J7t0rQvxNL <]92/MOU$p\"bLd JWH]tY8Ti$f\F'*:c9aDe vm-mD<@MC=}I*yT`(RaI8Y>K( -N%֦ͫĘ]m񺲎ɻڑm8LO; t#C^:uĭxV S;3VWK/eAU5  g>t_q S4ftL{0}v9x6292eU^N4SG IDATϨ (u>Y@~GKNLϞA-~rY6B< "NͷF@7_qJJ~Zaf!+5c?%^ cS{P0}NըDl‡}-'N B1f]tjx[HŲi6"DjZP&{1v: ,`Kdoo6TO) dk2jʎ3Jkf_xċd-\y,TT킗Q+1&b*qγn&x]RU]P&sX|4Jp sJݶ)czxtl kn+"Ԏ3ol98yԡ Bd8J NnoZx3"1noBבZ^Tᨿ3J EfW[!'Ρ(Zˉ]:^xsWqZ}('2sl]@3K;v^df4 :ϕ+WrWAB0cA`}!1Df֚rC~8gp!1k5 FOAci {D"7c2On!/ I"OPAof*%Rz$*TV )qN<ɜ֟ ٷ$*-*'$P_s]W8*jr[{Nb?9{-O0y?B^e9I%1I9O>@TT {B:fxZ*1GR@t;r7 <ܱR.ƝT(,Ud)ҋ0q#k铕LZH`*&_B 'R<KU6XX#F<'b52_z]f7&mhOj3S* ُ>thƟbye$ƣ/fY0YՋzRL3S7J?0a. pv$b^|U5m я.'OH*?N|V,8*PzEuó+n^D1.h S9q.Z V;hS"H"|IX&-q(M _ä'-Ŕ%kӪm:4 ` cixgJɰ s&XɏL.o?El]pXXEzu2JTjteK(u@\K9}d; qk`OBʻY8,/^ҹ[7ƚkhR#"eS1FY?1193{Gl;ӿZh :=pVӬ?;ۡ:!'tr:zZd<.'[lNS3\u@GW,ϿΧ5ż2)k#a %o2] tt!)wG2!8uGݤULZS{ˋ{U@^2[D*F.3Ǿt[6c?SQ<Oo0WcpreyIOyPVOU%i$(@['gɣ{ՠDܘNoH|I(5DеƱ ..E(G1W#Ksݲ&vθPU"(ײt<8s0YA TDb_ƂhAR7/F-ķo\Ao>Ħyt-!y8 FE"XXUhZ^S7J?^,DuQTkO喻p5 e]AGtKNB*<4*ԲPEVtVƃ8~M??bbhGN9N]ME o%gKLK1$7Eܽxv4 y.v|-9yu/$\a5MO՘ZA;Ɠ.uiM<1=>wxQҮ)Q|( )66c86vmh} %6xtzvEՈ,=@tr!e逫+Ū`eP#ĵ~vj\(^:ne==GF42*f *UBuT]:yyɗ 򨩣ˌ98!3QQbs%y6~#UB1S$/6%7QQMLcI,S;̳K9.'sn4i]ңE^!x7^jM z7DǮ܆0IqWVHHq\B ^? tX)ҡ|0V=ϔad낭vŽ#vs ]n]Y$Y71< $?]WXն"-Meb4ƈfExrYR,O" fam,bsK܋Bs)^Yh=ʽ kDA۸`Ո!b ?]t!6U&22'{;3Ѝ)jV W2}}tEsLu~\MIT[MpER S/m: ȣf7! kiGlV!/ɤM:ׇ{yaP%yV~a"shXa:\\p-Kϕ(mB fky QdvLE{Dm ٯ*;u IM䇣sx:%mi6qއ+bғh dӺd ъ8}a8&%'h#jbZG1.$8 gjQ{E[%d^ưkj郠OWu{vE(Xr|0 U?KhyQP.\N4AC?gfY~l=w:  1v'.^;Z'`\k`{M!PKښ"42.ƹ RlSfe{a;m{@36nz1Q9-F<`#W5|o Iqwx֎}̋$5 s9QI Ft& Я¤%Ȼ W9$昭v0Dߗɷ&!!!!!!c lF9BU\qLN={eYy}e#g'<0NXjˀ tŽzI_>6XӯÏJU$$$$$l$1rd2XػS`#c^*`~zf+Z7q+!c<5:Eڵ#<{)1y4qrUEקR\5 \]<8܊hM^~ű1B]C3T˙Uܘˠ%4zfCSKZ? C5W貄/6L ɹ;/J< b3>)9~06c`MNMlw^|q<L@VnYA-&o+)w#bb4:T#udWzbݴvĘmC-[W3+.۔D\vH!+n.`62ĜɴZxNB0z϶؝GЙsY,*Uޟym97%p.^xyo:YZ: +]=,3Y>.e([~M:j9O//a5_^;ȑ#O\ưvG-tʬ1 P1=ǥ^]?ؔρ9̦Չ_;U3ProU/6Z=LaGؙ颡]Z:"1 {VJ-] tcs'?axcOl=B&6)`]Mj.-T{$$$$$$c"R*`v^^8Dadl*UI H/0MZx3"1nSݶ)czxtl kn*uV,j"@Or* ϒ#63gƳQZe+J$4(}W{f47"txC:xfH݇pՌQ'2YSR>`q*[Jٱq*4wͧ3o]YN~=lg L8.ݨNiV IHHHHH|9'Xq&OuϘ|@T :Y9E8#"%1ǿ<ܱR.ƝTdu2 Zt킛}?UL *6Էh(w7gUrtj[7 C'*VC HAFߪ!z_1ӏ-.ʏ',mI_RNbnuo\ؚ@Dqn%/L;L2@Tdk2mG ȗ3Z ߈hY8,/^ҹ[7ƚkhR#"Ӽi?[y ձ7Ї9#NϗѳrOEȅO*rŋ/QM<4_gT=yq(EJT1J@Iˏf۰7I*Pv%`~dJ]JxxÅӗR5禄$ē mVIVIHHHH&&8u5='s2ab猋 E{Qa/ax-KǁkKx{爂H k,<8s0YA Ѵ½nn:ϥ~X܉>>1mtxCB%Z[ ( rNyM*KpS}" ף] Sģt3X (Q" ͍<8aOILԵ88:Gz/>gĤ#T\BBBB")1&bN>]E/spm0K}fyg0Kd]>Fh_ϒ0W U`e|WfOM,s0 CPG|ΞT ĪN~8]'YnYz^x<)~*~z^ԭi[ۻ7Z/4S?qS FhGK91h2Z_ܭ?C7bx"!f.EKugT `uP~^2L!_zL3iJY 1L^;i CU@)*"R (yDLj KHHHHQZBK̪r ciޘL)XXW9[߽>QQ~bL -+JBBR]8l Q+ҝѰDZΗY1Y>F27k3wl ViNQ"!!!!mHIv";>!ɞmeM.ǾO0BNY<4ZjoW6rc0fSoٽ2r,;6O :2 Tq)V1Cl%iݺODD[!;> o/vPMA ]A* EE"JGiJ&Uz/ IHH ׷/%K}%ճGH!&b)usv֜=-d?s&|ǿf< kqՅƌ !l4n2'olǚgl;)+-DykE3`?Bg$ }KhNá ^krXv_3sh=<(U57ƯlTm&p}ǴS'|?n>5~Nñ`aJTnD18٥Zʼ8۹bu2%^ h\bqp,oM3|>>xbPs? i˔ ox~#=ɼs7ΆBX3&*%Ovlc;S X&!9VUh:c'bo ODw7^hqj5U,:td7iߍhڻ[ ZLX11=8yg9J\ώQ i&2jO nAj>o5d,]ҹf|_Bʓ[9ki_Of_m<֑qxbw>jEG|*ץnjޑ~Y޷ -> ѦY[|Ewe8JQH8)ܰ)5!פI7cG{**'p0iV<0Ѵ?0AB~Me`A&?9|ۅ=B=&d> z*}~;%9 &߾~: !0UEK` oPմ]抢$Z$2eV4M+ 63gk{B!gDž<@||BeH'<=̛;7%,_LjyZJuAzL)eÑIK[gSoB<5&jdsf A/ #2IϧeZoB<=Q5Hrtd^Ƙ/"(Yi{B!fr23C|42E2BɺnkբO_'=!1í5z莚n%w?>O+8"[=3Uc'xf*ǨsNHC2sBb$[s<$_Nд$9!D0y`g";N<9B۲q}^gAaLX뙅yC !D2yQ!C!BX LBa1$0B!ŐD! _|ȇzW=+P6vn{r)g265CA g¤IHH k2)gUcRlmǘ$Rۓ#lTRlm_⇼%UZ%HV>3fyO|ȱ3)%U#jSpHj#< l,>>uHڊgm9n<P c,^eN ~:P hb3Q¤:$mųQH`/X3=tؚgI=uHڊgS1˭r|Y1Xgi= uHڊgS1I855Jwӏ>bݕGk_]_Weg4k(㎋?q,Zȓ<]ajNѻ/Qhh<<+#;֧|1O+t}&ǴAT/SwIJ6E?v[g#D{?5_~|5գZ #h%'mEMN^fv龊04,ܚe}{ *@zv}P3l2nn<;. uZR}/ 6uvXp Ս]Ԙ_ER:jiPW򏚴ފZ@H_|os3 "v`4,nfOd^)s3{L *L_Ә7~U@~MWjqȃ\V1Ѭ~b<~_vn&FHpz-Cq>>Oq׮:/*j [ Vw> )/9u9!%IڗѺjq:GnP(}:>|PbBP5w] wFIW@ݑ\KhDŶm0}FJxv߅DZQNy#f<qTʔIi+OY·V`eQat}ΛF"vyITPPQTǕ!INT kZj+H1q^76iXFbL}R)DOSpptAA314g!{]Vm:WIdܮxzs<иP1f:碎yw(cS"y&#m3C[a[9KeFKC'@çg` Q)=zL^KR* ӫĎ ӊX ,C~*T);NpɟeNe.ÏUїëj5Vtj朸 PQU@Ï׺gJQlߊů^*SD] oS8Ch^ü&m&'m3~2%f Q%ŧ eJ#qzA|ro^wI&epI{*x&c`_ C:|kV* q]ˊ߼.g-]t>3wڞ"D^?M_7:':]rj-Ֆ~6`Оd Bٻ`߅fCCC͛4؏14YOBqۇw̘ӟQB/q2:W&[B:MVd mţ來9cC O"l:`\~-1|:`)f܎ZmƵNKY]m',RJUMǗveVdAJjŔ]>e}e~(k%oΟw~\CO?e|CRt7V-ƠZBB S陡{w׻aodi.UUsAt:7m$QLK9['qz axz:V<{}h+ғ)egna,Y?i+lMcbuݳ"#KONB`"ѱ_ANY}^GUUE{VJӡ*zH9[7cԤY?i+l &.Μ:} *,akri\\\53MMꐵ0S9.^"""eP-^ ~e2Sr@ꐵHcLJzH9%u ,$ܻB!yD!BX LBa1$0B!ŐD!C!BX LBa1r{xH[!ij-'m@ oYimABV_A4Ew4 MU!!>>uٕ"l&'*:;#߁!GI[!ijض"lO!T!BX LBa1$0B!ŐD!C!BXl?.,²xHB:+VWgg|\8s;)g֭|\?[qa!!/^DQtԨY*:h~Lp2{9bT;&y3fB1:! xAà&l Y];=v[,Ut|^s7g+&BaCBBBАgNIB[t53z._رFo>6OIyG7b}c9U1 l8(?u^7^m<'$0Bc{LH~ϞL1EcQydcUl?jd:6&$a(^_Ӽ?d&Bact/\\)[,]9x?,[Fre(T 1w̷}?f U`9T8^hS%ɭ if%IJ<UDl y4ԅNi׏/rpӻ4uZesD?S[ˠƜ.xV|cs8r!5Zַ& OÆ/ru2BQ~,[#~ܻ{xС3:f!W{8K~w.Iؚ3kVHG\B,!aa\uuڧЎIy *EkZ/Ȧ+Y9y\P$8b/S6|p6]@v|Y!1VB= !..;wS///!&&{w1qxzyKˑYo/3FlەdD]H855Jwӏ>bݕ[qlU󧈇Nn^Ձ.jO16I{իOalⲇg~ed/找xeԔƟ?rvs7v?M}sLn Lgglb^kՂ64'gg_Q'}e_C5f1sR3;$0BcEB 8ȈD7mFf I#a'&B!Bdeo/'88UUM@Q|W+,!""AR*}sSY}$Z\3Aat}ΛF"vyIGCT5MSH ~N2JY J*(♼1Q LT4-&2\11\%U9s_DEbs;t}qJR.>z7c9D9.J$AGٮ?x2)4˕J/'q1B OTT4W\0ehhbbIO <, GGG\BTTm8֥cDnهf \Qh4 B**xI^/,OŪe.vm??'O2e}#GGUU'#;ruEq!{ObB([ԉ8C$ 7B(Qo2o>:!j,4#8L6qL5ٙBo1oZ@ߎcZkc"6'!Oor%4 B̘9Nk!zx3e Pt;6۽y59 뢹aǼȤAc`iӆnv^ AcIB0†+oAh?^ȡ (bOӄuK3΍…!x֟DɪO@1VNnE{4aQr ">$\MbTC/sW^ ,Ik-K~Nف?2NG=HGi{i?En(]Q._Ó%=&Bak4&|</O/F8˧|_ƿ4'N$ӴK|y3{ک ޙo iȨœhgDǗiԜnKnWO%2HoO{[@#~m9j@ӑЫIF]0e%'] =h{4]_'.LTrw1Upw.[MQƁ͇T,BYO`V&ƷCj >ݝOgcpe^c9U5XUЕzD}~||jVB2p\!fm"e 0Nc4yqL1I0漪j[sm;52c5tMZc^1> #"-gQSҫO$ͫ⟋8FSd3͈6'BcGs搛 g1r{\qo˔eů۽xS+rr$0BcF?բ[FW! (2H:UU]'; (N#A(鞭t! quqTPE`T6 US9}4hZ6gh*錙(٦$2k w^Ry*G!l/BCC:+VAi,rYN 3̺tjq7V: LBa1d5!BX LBa1$0B!ŐD!C!BX LBa1$0B!ŐD!C!BX LBa1UYIIENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-general-config.png000066400000000000000000001054331501654372000224530ustar00rootroot00000000000000PNG  IHDR v6sBIT|dtEXtSoftwaregnome-screenshot> IDATxwxotJhi{S"lPTd *EADADV%{ɦn:Zz e!B! d2jj:GfYZ>B!9l2h_^i̪B!"/!66)\U++sϬt:UU1C!f#1Zuzd4 GSϗ… akg@tTwK8%<6kW*\pAT,[OcB_kj4O^BHўEƩ7^*!B )[ʕAU͘LJhjh4N{3g^o!gѵLQ.jD5! VGF˪X}ni3TUB!RQەêQ_`GnY !D0SV-<< u(*퇃/UKC cduorD})x:E-W` ƁZ TS,Lػvoᛋf\l!CWW ؤ饪f\]դⅆd2>1^EGLQlJZre`zAQt&cjV(@EG=oR3"/S,=,ĬȲa["T#nhYtSݩHlc }.Iy:vDsߙ~zFL5:EkhRQFG;HDH1[g{qAv`$$ʄbe<K!f#(A|O}F!$4;o`oFH"F%v)k9]Q|jk1^|L!KCb`-E75; zm,,hqpq!aGӭNZTEkcMXBrSDG%*<޽Gpp((Jꮬ9Άšp-::Fbf@B eC;m,K\T@%dqT "i4l  #44GGGlm㛘M#QQD<|(8۠(6d<[;[h+ʊ6TM_ 2i9kþX[4V8[=1(Mԓ.XBRb 4jpvң}"B!rd`4b4Q -V^C5O` !LoE| m!sFJUgY'/"B!ByB!" t!B!D B!"$U@E!B! %iB!B yB!"HD!Bk"B!59WQc`{cDd'!BŞ] PNݗxqF0 1 rB'!B@_`¸<)xgL:S\͖(k{[ʀ{zg __o 9(5"Y۫8_]Ms_LgаHil˛ !B /aU~6&)Za;{ L18wY:QLl!BDg)ш1c2aNƻ+ >3zȤws 9rM8QctpͺU{y`wBW |{&G⬩RbS ̳!M*h w773xrV/ȷ)˅UMƦw0~Z~:V[Gӝ[ Gw\#?/;%>B!xid Vo5\uMҿI˯ ~",nT%P5Epnİ T+e6Ae;3SNԠ%v#ߒvٺ#x5gnޒrUYֈNGd,7T/yg% ٤/2o֬?)c[E5KІoΏk7aB!BK&ƀ/8q [S}.pL aAC(94id2wg5s) ‡9IU Qj4&s;7VIUR?gPˌ^j X6|'٘'/w,]Jz4j"NN ӱ"M7qN>YKB!xye8Sb%etAUR|V.r6n˩GI5a_-*k2z4^qJ6`M7ZF~ą2UUAԍ9'U3YG:_&%FEUAW+whw4&4Z JZZ!B!# P0Q@'[3u!t4X-KNvcx5Fz-6\bNS6()"/BʔݧM"OB!Bd"B!5:XB!BܡS!B!%K!Bk"B!52DG'}8y*'sםha^s% ͇gQXx8 .}`;;ٿ _ad$VVzS͛QZs,\tev_@PPrwwח͛Qnm|}KxޤU fEn ?Sי=c*nnn9nKnܸɼ_ӚtډY$1 doXG>u kk+zٓ9g:=B!DV뙿+Ts.\>Mny܄˯XW9 Y">L{LL,'Oɓ|JڽҖA:ʶtf?⯗snb6Ssmnݺ͞Qp!6nL>RR%4mH|Pr.I~.\Lm׼̝%Yh`W߀ S'OtEaťk'{LL,XRBw ِOrem c5*_~eٖ8V^CACX'n߾l֭۬X# ek}a(2/::mwp)y wK`]ssy8;tymRh4&d֊?fkɶXB!DfjaaaL<ٻ gObn/Q}+3͛)OtDh }c2(Q8 ӶMkjլ3QQQ8xobzWХS,1e ̛SUU9u4+`D.OKOY i3f2ghִ ݺvՕ%-CMv_UU| p=VZ͎]ba|0XetB!'ǀ u0R|/*!Itm<@R+Hza&LdBQ֩OFSfVVδnՂMqPOØfN'vB|KϿGr1̞;t1cHe={aμmw׾}*IӒ SnI6&&YsRF5Z4o[N,ZMR+^{>KϺ^2$ybq :%N=+Z>Fem%X(ə0iʴ;˙Gi588?k[6S'OJJi.[Q?S'OdGc8x0׮_+)_l=v9dQf  ::9sG*3/#^2 Oߞ|\RU4,kAitڅA?s^2$b 1Yk1-aAIwaϠ9 y<ɷYEyTx%$;?qmh4]~TxjU٧3`P]oK7h٢yop ښ-[0uJQam:}|I+dJnUl ԧzٔ*<aʤ L4!G4yτB'X9hK=%efJ8I>

      +++zt뚭iRU/w>qrraz*凢(ٺHk ! c@I>iuQ;Vv5xq,,\ \Ժa)ϝƄo}qvv"IۿN`e]6G8Ӑkd 7orժ;8xժd8v^YLOYɺoOSM˗/_-GquuAQC8p~hp@nZWz ֭_rEbbcT)? kC˛g|RzqgB!?Y>9{>jhȰY>Γ%=Y;LFe۷+S0OX*UEfx)p?f޳7tkk+\]\  &66.֭Z1uxxp zGIIFlXǮN2g3JPVx2[ncw~4_|XpڶI*!nnn۴lߒ>d:7ACSOW_ΧmV\NWrx,BDž 9T1.^@|^'V>ͳRV>p4oz椨|ԫ[WZ5M1-((3gwxGYJ۲'U>Ԯ𡃩]!!!,]} !/2}&uJ]>7۷[p?[␖T^򬔕5?o%Kv +%Kn6}k~I4I>{TJqyk2[!xYb4XC]9˟{ƺ$^4j؀6[&"x/mַeJpf34_/ÑH^l4ו!G˳˗-a k( \q~bc2zgg'|4 ]- ]:Ѳ4oV2vFC|3cβg7nE3I߭?=iBӨ H9p:>ɥ5=>KG ҕΜHONh_P>+Mrσ~M`M 7lssRg8n+ÇOp E<2h ) IDAT=PU8w>䩕3w }jFv짬[L&44,[ʫ^!~w-\.^"(($esBw8EQȟߝW۽myW_/IZO -vNm|#'c@.XL+x}꧟xQXmҸصǎeh#e޿puuiFNoI7oʸ yVݮ&uLN/ggJHZrb?e$˯ζ̙9Ke`8888r$%! ?bco3sz`;[[^>[ !//"==]R?.37n3$wI>ÑO9TDW_?se0S\٧.{OW(^(vvvNCfM׾]fbcmܹs~g" ݮzѧW4 -7pz윴/W rr?pŸi=GddFNK ;I |pիW`)kk+/Md{:YJnܼFeԬQ##B(,V@9Jݺu0Ŧ.}Iw#yw[={pp/^b_h'd4UTc7~Ra?qq'p<=ӝkQP!nݾm|4ůU(\ڵj;OO:ӳ&8${b2`0Ip~%bKO.ϜIjsqG6qwwˑu !/'ԭSTUUS|Myғ4,+twؿ/)[IWʘF^L&~ФqCڶmCհ'22GǟڽK/k6ٽg/c>ˤ Lg%dI7nJ3lС{vZ4 5w壓I)r$ZfIxqi ;4>ɟ_P\R+˳,M!ċO 3k2lrܼ={"::g>u%'O "qAvw_D;Ov`lu錃jBKBrhTqTUe_0ir]RE<ƆaCQR,M5Oף\Fӥ]{nիәɓҐѨ3Y)޽_S޻Ͼ`_R=JrNX7-ӧϤF\ <*s/3*AO9 *i)I?p`5i#&Maμ ?UU32̘58CUr|!a)XY2nαY*XЯO6ȲB X`|1n,al۾3ؑe8N_ϯ$K]{ LUIh(\M5oSbL]'2RVh׶M[dܻ';mZJ1O:'ѭ+'Nˮ{0 Iӭh֌.:f)~Ν8~w$.Bg\W@^f={vgUDdk\[#K1+y*;,Pƍ3XǍT'!DjAu2D^ zD>gg}|QPPNIUAEkp++i.|9ErM<==:IB6 K[/̸ XzƘ]A|J]Q:C2iǩ=9z/}_`g^9$e[dʃCZ=<1Y\,5Sqiˇ `ELbm̑.E~3rvv'M,yk@89R|53vf%ϻ[ec,5f1cc*HҔ߁_B\æ`8˷{dPL~eTxYzJ4F»XQ £k畨EҵY-Jyw94[,eA^ʍ1Z^H) yܠ/}%UJ{S/k8oJ`ڿXbXxQ7r,Fҩ J%wt}WMCy(׊G6;bw1|Ł׸IvÙ9s \&c~:Em+?m}>O`p, 63E򽑃PMFWhٓ!Q')U;xqz@|3=M ~MK#Lc=f2$ c4._8`n]9͡(INrѕ~mELcp|0v3S%x `sk~6JD<͡Nv o]ܧ/g˷?q='>}iΧs$|KHm zrplG PrpQ:g?ذl45-o3wod ][V3wDSܫte̢ٶw;k7 x@z?(,UzEyLw~w>׿_7 6_Nǯ8q8'm'NOфٴwVMlitp3 D -_a/ebdKD*km'g1V~vptK>OVhю>a̷(prɽ[Qϛbeyux̵uy?KYԛ}Kq9)IAZxfn2Qhqocr>:be\/W)W2Th؛[=Z"?1'ae?SL')Z;?G-iZ7 QHC{n_5oxB'"7iP/J1+OF$5oUiL\Z ^Uư`1Ѽ.eH r;6l ~*RX |*5yȭ-` `f{-OEvN-孆U)Y<>9?o$lĶ=hY~>)RbmgazSXq>S~51`!qeZq(J5U3S; xm@#wZvu+Mm;s'72%}5$+>Ae(Sz؄_|@|2yQ[ '^ɪ~4TxUku0?~]P|}=>c3_*wyՖJ _Kh٦=dh|n=Tzm܆7?/-Jߧ0V{z4oۉLO5\!^|ARt3#UEU',ayk WQ}z A=&?ZߴXnʏqz|WX7ii5S;qƭ/s&4!?aĸ{5`.:YcӠ@(N.}%}|s L9,Lm,f^t?&eo:X]㖍's8}e95fk>8Ey=;PUB9j6 9w&1xt ^ލBDr.{ b:8qi<0G q7gIޜw- ,̚l*K?)ߌ+Cᾝ >gL.tb+w[?9TՑY ̞ڛyRC)8FcYLxЂ1WP*~VxmMyWE|(a8W +4rn;t~>曺(7QDcSKVRŎI~ʟ'Q5KwK5>&sۼ;v{)*5٠b1:Suf~߁ BXFpUlVAIiNqyhBᅢû1AIdG1e;̤=F3e &G썱գM;nJU?0Sw_^ɿ,Xs+$ c&Ҥ7{<0JlAG>-ާNJ޼W6M>=hV!wnqrҕhL\i>7bt+/C߰pKi8NzTu|߄镄 B+b*]*EkH{T-C 8C|;=vk;5wL/ZJQtbt>16lT4PRW&  ~_~>7~m0bFv1wrW ~ P/rBɪq~jPKN0o7,fLB֯Y&=YޚSI4S \ȺA6V'S5ݑ(]e9b;жP{;.jYJ h8o%nnYpԍB WaVEӬA]XͶϧPNKMh\iv2Ap5AgǨWɧU|8\EHǮHgLGsl&+cXSVW~;gGI-\TO|m0=/{hTUs)9ф'9Ji UC P1Nzv߁,Zgp5۔]|oy|'>Ǡb6PZwYq+L6sInj* yWA5[/72=UGF 1YJTeͻMxe ~:tا*˖YS Ap|) Jj-]WMũ%C4Z+q <CèPxјy"<eZ%4.CqSO?GѺ(t\GzNslOcJq0si />O{SW\6<._j34%mi1r `8'Q^T',-Ey ŕޥ hco\ؓcݟxz-nL6}~KbTOa;e ޢf7'}χ Ӌz0HxxQe)X3-*|wشuRNۏҥ}JsWOQN+޴YSC Sf5=P1_U+ϡdt+B> 6IhX\8iMO<ѕ•i~B^XV1|/%#}*F Dw1kLNsOmIk`2ϣX,4]}r WϣwGՄJK'UhmVMFMxo\}ҋ@VmKP֓=Rv0\[:qoFqq-N|*WO#p?%I>l(h2tZEQАPAҗ;nN i:j l^3&3`8w~5`zGt?ѡׂ|sٶ}+;>vbwoǷ <&˴7?P-<%?F Qܾ{E#-_Suov34#d|1լbai8R> IDATPp1]2tlgNLф@X; m'-[ٳeW_0y X.]Gdݮ_qi/ij$n!x{IMhή]}SIc4t_ONYꯖeTyJ+Y1{>Ǹp?QAセ}頔'[5 {2dlY=o8#]žnCYk)h簛ѯcٻXw?4*RH4[f{ٶ;619=4:=G㯿پjJ`2y5]mDM+/a͚o7Pbt""R\icyAe(] e ƌ(Su0BH[l&<¾*HYq+sFe:.f Įʖ\V|S?{_quoGH.EW-J,gE[{*ժUE,BmZ5!$V!7Gp{s{3~ ΋O^&ad듿R4uә(F| "w~ّ_\+(l6ホGO>\ܵߞD2?k-kų>=g,1~H6hR0lg̱ P>[.ys'O6{Bٜ;6>l;fuxŰG ;ܾ-tf.lqcE?7ĭuq&OklJ|= 6^ rQ)|qaDX~2{Hd q>IN<-^)R(3-_'9"LM6l>a̗sd1f!Tk9fl$2JJ9EƗ3ǨA4}g<2QA?KXɼ;:ufNMK..X<ţr-Q8 ~F ks欳aqLWxd-DaiY ]wӹ l3ר/˳<+4TV]OIier"FzCʝeLn ʘ)_6!4 OGOYDަqɨwUve y.uz;IQAFTDי7'476eJ\㎰ l==m%dПsCkdҫ$1S.9MQ-`yIzwsH^#i#㚽α[";7,fTx2ï_ TZȫTYWq1sH7&d/?0i6\kM~v#z b h4XGe7x?E_Ody:z99aџ׻4fZ۩tx»M_!u"ǫVs*f4bp+C;_Rq2,>7McWg lcXA@Lo1N8лq';m1`W ]K-eHxEvF~/ 19XIWRur!/v,fsfZ[tiSߦmZˁӍD8#R?c˖Q[2(M{ξp«(y|N/ɍN0?GCx}{Z(B6W? = 7YY;|]Q:(^\N%;QчG@RƗi̿l`Y?kOy{sV\il{ w*9|? &G[N`] r^ЗՇ޳ ?UޟN9^eo|ו{_}\3z[/j5Rd~l?fj`ķW94l0ub@\-sS|\`SDFuK?`ޠΔx|.T3?n-!EڎnL{FvA4F'|2?t|!NG3sܻ̾~R*J'rbzOB-/^GҊC9&~7C'Ь\NƉW2ͰR'^ç*tJ [AiSXޝzF'y,Ft+ gFKv Do{KJ5ˢu3@baΚG.qLyg@j?Sz#ٕ״!~4~-?'wr32Qc~a[bq&c`EzmFŌc%yC3vCu3mMKAh}+1n7(;_3d f|Tl7=6\̚_]g4JD^׾_q&,<T}(kb5-\P)}qbh<" ϤPJFɹ+&=sRg8;CɯqO,2eB>vqIxe"gZW͒3WNeŤs$1!VЃ@D Ò, 5~bM 2cA㏵? U<]3eK\ &s\ h]S7*uŪ}obFtӎR~tML=X:J_ `2%?gtMӇ{ѵ5]g6ò ,ׯY^Zn.DDDDD^w`KBruEDDD|9fͧBK̢_DDDDҖ_LO.@É&*22Kt닷AAA_D`x8&Ew)""""8u~"Mh]<\\Ld_W;|Ĝ_/|S&UPvHP̬O2?Nfp#39դŇ cI=C(욋0D,^8dp7<܌w'H|ǐGI௟>C8虃>dIliͼ!xxn< [+">; g-Cܳ{ 9r=] gp. GMw[`^7('Gڀ%.X\pu5`MN9e z}M^i솫M1ɧB!\~۹ݫiR8kϣO.3;RZ&· $ḵĚ.˚.Ep2K?A/]vJ?_ EKWIY,_#3&:Z,Xđ, yg2ax.^$GZDDD$<;-K,Fg4iSW 168h(;T\W;Щ{e\θ8ZM6@ .DjFXt<ȍ 8y7eZ4F?kѸ:ƭ6yqHGppF32ynI9׍^3ʀ`S"dM I4>5^=)_ p)-Ô,e2)j-?stKgiln#"Wq,\bp߸ 1p2żF 'Bˍd)V̩eǒ[lgÍ ݱ_.g>ϡ=QwkaOoQaRKc>?0jdlj= Y1,bTX<p7r'Pn[%W!eQOȶaO72!jI3293(Z 7t+~l!J<{?)!?%$ θ8Õ- )'5~›7Á+UC|o艏?|߯.k vdp!GT`!tE>buw<JVdo"X"} [fǀ4, |Y1Iv3*CRbrCؗg2)>k3eq"㕽 :Ixt0L~`/תn6{SW2S%"""3,Y8ҠaC򠰰07}3EiР`0|+"""""֣""""""V""""""V"%s( P^^޹#Ȯ;NվzoP&Nx(IA8xw"_a7C#uypKg&C)F2Zbg0`HǎH=E Ly$33q?af jJEtXC!v*0]y '9K+1t},ȋD 7rnR&fǨ-ğ;G)10,N$զ?>g̷;@66ndƍ4 m3h:6nƍ?ylyi=U,ʄN^ҟ,U<}pt7oMuF5,ZV-I@?pBak*gù{n(c #0 R"Y2P\y*TIöfdǭӕ.p.@|(~Kvߙ2۵"oo7ȋl4å|Tm.̀%Jhh(eј,E+Jhh%JtƐSDDDeTĘ{igy/F 63uJ\~Jb6D]'X"˜Dm(]ZnaԵJDDDʞzs>|3~Ў]Ma rWjL2*]ٻ7Fo+UB45Em K>jEm| 0ǜqiӠ|I*4^%X-מI8P*ؖpˑ\uNANwT!> hW/K$ I]YsĥK]i!= WeAuyp#"wLj|[й tn̑3٤""""V f!WiLoxO;~ȆGu,DV2h3ׯHq16ɖH|D$1?p) \?H{u=qv͛wpǨ}h;nԛ73f0GJJ{e.E' 鹒_Ȣ1̪Gޛx.G'`gm;;&Nct-:-}˷ٳ'];JPmg*F/)EDDD$M>|;v:+4s<4s?i0U?Ò*qg+Û}=`D0;1f-؍Z=y28nvL¥J~ڀ%6X\pu5`sQv\_zf\MŴep Wbcw/~-]bC:+5Xp[;. {8cb=$M8۷}m%kY4!U<C6m6T$ ı=)t{@r8@őEa~NR#ӝ%#>blnWWjl,WqQ{ԘԎB6Fg\-ŌG q #I4YKL,89;a >aF-ûR3N.Xb5h|冲qB] F?ZМ%ٟDl$eUo T2mDn.5۸ڲ!g,e*+l &dQ~%{Q݃3\͂eCDE\Ʃ'ή} JFU %wvH|d#o@"1?3/n8y^yG.ӳ?K bg f xҖ )e.O̦b iݓ`-FxfrprMtH(^؎)S6f3GhUOϖB7/Y_z%"""""^)X gڐ9jk'c֨RO3(!-1Yʰ![b-rK1KzeFo +k_3=Χ;M؍:Or !no"#M޷2b?]< ^|ZqkxswvǼ~5K4kΚ1JZ=i{f̊W.BZmBO9 ɰdDDDDDRbR(δ!s"NƒQ &gBQ4{;CZcx{{T@WG JDDDD?B]DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDj@DDDDDjlӻy '&&Ȉ.EDDDDҙ^jyL&  IRDDDD$ lXFM?>w <OB@bbbTZz!""""ibќڻ1 deX9= ~y/03HZ08xLc_~O18!G6;]EDDD$۵}vi2x"7fwb[!BL>"bf@iH_P-~3žVguOFpmz?~?ƟWH&f'TWyQL,H:0=f.&b畛үw!(jrB)T*qݷZroMm """B,X6s)2-nLuqV%`pi5EmDs~eCdx7*{=8D~ߢ[tZERpx*+֬۩yF O,XMa{n~O; kV^_սYo{*,8\>1!"""|R*KQÏ2oi)c9 WOeyWy;s#""űKG#H`"xâ눹x]g3{iyww Z v2s`>q/s& d@H9Qܓӻ4y)=Q{V9Xprr S˜x9ֹGg,q1ĦtPf1gx&.)lt/͗V_.݂J7aNV,!kᚪcvсB)%wH$11 'u_|%;wLqҥKӶm0X;X≻bq{=1?Zx 8;;ÕXbSLxkSs.oѠtnpN^ >Uq}-]7= AmȌi_ҫ4K@C,5:EȔpo#[>/q”fp=J6^|Lu_zY\>?ܰ4y#U Ϯs.ɓ|]?vU{%>/9H\֛}@Mw=DDDDE|A~}ݷq7'brswlcxbXpAÆOb ~JGͩ~Fow""""F.O>w{0iF~ < –/OEDDDD"I&Nf&ݓ g_c:w """"ƞEh4b6Nչ4Aӓݻvw"""" c0 ?WZ@䡂??刈HX//oSu.y.ADDDD#KDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDDFDDDDDD6 ={}(R<*p=,1{սqA:}X| L<U=#K5壵gIދƩ i^\~8{g'lqP6('AT8ߢSdaI,ޝcGjPAKzOW2`>=J>ԝy *A[HQ B)wW7=T__9NX<=ɸm|1,_L=S3jtDnۯ΃-27lF]#r\̛ o"3fJ$~][eI'U*}vl؋c,Y-+Co|%?CFm{vCd8u>:\e6|{^0G} Zme-W:={."""",Ô1kkK|ڤڷnd!kZԬ TF\\iΘ[i?"3tkz2J2(?V^ƒ=(UO=LAm6+E,Yۄߞ8TeSh cf5j@͜FT먴{v%Ԥ}(?+F݁RuhզU"`H2llZc+۾Brw^o5 R.~~hkL6771@OPG\^^\1sqj :=W\c֨@F(guurx9G:=ϧK`J)aD2\ػf3\aFg7O+OH`"쏽Ζ zks7ą0s}R2g-s`$>ʲ.(kj !dVf/93਩XcRV4%ZMMc09 PKk 0Qu͜=<~{?mgNy`O6/]r+L[F~xF:?$sns?ٛͣw ǹyh ʸSOEg{䔯_%+8fGO[_O~WO$,}2n@nu3}͹cցY1L_Em#F#I3$f3w-unǭYL{lߒ,8sK3ͤ KMВͱ8yMxSΙtaX8"*W̽>O<WnyF}h_H) }IDAT7/⹯WsȠKfq;fc3|wq[v%.e!o^r Zþ,_},cu:֕[Z]wWC?u/?v46;]]__]3tI{ascot倽O2$~yٙ]9x7럶檬kyxusi37sr\[bT3n%|2W|Gkwe⌇3j^M[Y8\N!i$7Zȁrs󭇻2wg${bNIAiX#'ٝ~̩ߖ:!G?#-<,$c?(Z[qθoM='feYq3z3|}NlΓ3=IsvmY)9tG޵{vyfߜgN{mo\<<\[o-=O62pp?4 3ѝ3zhgZV-ɯXA!yM\ ~z~f,{4w-f͓I{Sfgl}@>yxNl6mZf?1qPҚ0>]ݒsϜ '϶g=`3@VgY:oz`n˖1㿒Ξ!/ 7ԕg>r?~Zw=>L`r~3Z桱<i^Λ?\AC2 s)#Ӗ^qy j\{U~/Ҙ7{;,iƴrNzv>1x?`U}ڝF#ϟV@66ɝG͗>}e Xod<wܬ'ʜF8x ޘ-+zC!t23^ʴFc%oqKnnA =`#a:PFePFePFePFePFePFePFePFePFePFePFePFePFePFePoUH(IENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-general-settings.png000066400000000000000000001776561501654372000230660ustar00rootroot00000000000000PNG  IHDRMOsBIT|dtEXtSoftwaregnome-screenshot>*tEXtCreation TimeMi 29 Sep 2021 09:20:56 CEST< IDATxy\T ([KijjYf*iR4M3[L+̥-4s\]3[Zjs( 23z><Ý{3g@9W.L9 z/^g9[;A{%BZm-lF;`8=`վ&COSEs.۽͑X]^Z=G#_JLuI:`Kw3ք̣uT4٢~7ns~*vӽr"`w݋&d,W*aWۼ,þ{ȭYgEG2kn&C$hE-IdeIRI7X#'H.SIR֏oly&iEwB6'jw'6U@o9%^Qsק2g.Z$g=٢^f9KJ)X]<.+ ;' n^}KjddRAO[jPO t \uPЕt/7/A$#9Q^OOЕ^K sEI!؋ yG W%hŹydO h._}d):Y2+]f.raCt⭀կdrleוWrYSY5Zu KI.)iO$3ާneN9=Ν;;YY}9Au6DY$#Q{'wS=8f/mX%4uUmt:ezn`&؉SH5Ur5S֝RB7S6XeMDж/ːt?5]&yq, KNnmPRR. MTJs߃-uhYmڲ!؋|}$%ey'mM]HRt &rX.;ݻo|ƍ󨒼dVdjh`KGA[dYTDvrNHUE+rvqɸϟGgD]ۗkS4)D.'CΫ.FYvlhskYq>.C!؉BQ*2A~>rpuZ#ԥY+I)X;X+?¾tW\M7b2â7dRm^-+2<`"ͮ5oN,gj w]wQIL2ghzaz|檜+&jbYEtLb!E&7v&ԮӮg@V2{_ć:qqH+v܋VСveE0炻&_KGf!0郋&ƾD"dcUuQw5Ls%C">E+e%"cHܑLJXt=hL1\Yv{, 4CRDYI?#p#dlD؈ !`#B6Fl1%G1~w|ٮiۋ Tѫ/YGcd}1bªN3~o')QG:iXtz|⠢nY܎*QU$\J^N?i ,'___ RH?oDne&;j+{]+ǪD{GJDy:}SpT(Q^4gֲkS^Zwp;}rcc*ȄEW.^Pbhʃ*|UO_.Ԩ賑Fu.fWes{Ԝyuw-iUHFj߯5J]:8J6J'SH*W>w.iAWymHmνj-Z5mMψQXͪU@P5|%}y,^R6}Iy*W(q3 .As݌,:Mx-!-K( כOS:Uh` (j-+C _g%o;rqu*@^X߯=լZyW@p}u}~KUY6R]+x U.(~WzR P=0|Űؼpnb\ԆquJ*S_%˫V'?59lR"U72tyz'i>Z^9KaY A}G_;Ҳy]/k}j屹e+O|TF}Dz~v+HZm?Pty~CDžlnꕕ*l&lo–~>pU ْ(UG-R1 ]E$im5#DgUHAuk߬ש!tl֝Tu*)1C{ 2f7%fz%O7_tb\\t%n?eUa#U4ףIR9NoqӾcg"HNS!y .azɾָ )ACVmޒDFrs9XO}P]b w'+Nd.VՔ9Vm@9.dժ:!pRhٳ52槲 ҁߓUr CriN rTH/ᆱd?ZFG?wd6=ɐa1$SfSAXmzFlce_%>_79t9b0d2)<3]WyC7PBG5)ug]Gv}K]fiF} \5\L *o:rTgj֭t*2Ϻ3dIad\z&dXdHs%լſuSHKٕoOʨzw+y,jTܲJiX=L ϖRj:j>gڣTfv8PeO*כrZ?)vPSJJq oӿatIї FVwdow':+C5^zڔ1KJЩ)"!&iWPd|}==+M,RL'K8:**՗rόRqU$`#__)rj}{uuԻo?C{7V{=#N}Բd. I2bNh_JN+?g_Q?ؖ$F522|χ~2]~KݪE^t^My2xJaC0E?E5wwk*ʕti|ڔNRl6\ReMo.Tew2Flqd\>WGYRPR=R,j 5UW-%c-6 $Ot^*ZYÞQZg;pVuBo=V,͋)L7Jdy5ࡈms$5ɽ@~M:y:GgQfV}ZR,K}QX%+feܜ̬"Ŋe@fWuhvstxC '[N{MT$INlpfv9aʤIcghֳt>U]FU-߯:^>&^5YTŃ SSz_܌1PъrǙW^5*Q3E']ڎZCZ$@͞F=^a!bѥ$%?+^TS%so]srqh/tYޥ+գ~̥n_?.C¢'bV:^\ҥK;Z p{iF:<\BEm*iWv~-B.C؈"X.pgkw<{#VlcXY,G1'`#B6FV/i#dlD"=}]~~~?wfN:wyGK.$S>sٳg5g-ZHU{xxM}Ռ3r\יܯɵkvdsڵ2d "ooܞ>G54[jfYRRݫEiժUrJ.\X;wV>}ԤIڭŢQ͛!ŒfYjӦ ڵklv̤ͬƓƐ557 Ԑ[wV޽§-^X5r33rH͛7Oɒ$gggk9nsر9s?~/\ Ð{Z wve_^.@ J돘<韕+WjZdm6IzVX!Chٲev!11QK,xb?Yl@̙3Zhe˖Y< 6ΰ}ݺu:w\o2s!mܸQsٲei$%''j˩EutȜћo)I^nf,ϩSzѓrJ=SJII$/_^͛7WN԰aCy{{ڵkڽ{+mݺU'OԟpIR=r5-11Q&MҌ3l? Я!Cĉ5j]g兂02kfJC)ShƌJLLT۶mճgOi2 #m_0`hBϟג%Ki&izUP!G_]YD$~rٳGM6UJJJǎ~yļ !CHRlT{7PJJL&4iW_}U 6TCjӦ٣qiϞ=X,3Śr#.0aBϏ3&aڙ3gҙrL&Ν;'e0 … 6h֭lwss2+K$۷ߟ֭[k͚563a{9^[kc ԐxұcǴ~z-X@QQQihΜ9ܹsǮ]V_~j߾BBBThW_)<<}*U?C7TCBL[#}~3[}N:={}L&_O밆l9൧ӧO߲b=vssrvduȖٌ}I&JNN8儽ڱ[뉉ٳo^z͠Ar}Y߸qc۷Of5iDNNNV:͛\kbt̙ ݫ^xAcǎUHH3Ϥ]ˑ#Gke $A=_TR4hP6kk(W4h5khڴiiao-???=6;~ҥvޭ/޲ѣGŋgzŋ3]d2Xbjܸ]iܹi߷jժ K -WdcK ,oVjpcϥoֺuk)RDi&-?dۮlVvlfiEɑӋ/iY\o5)>>^-ov_j֬Zl޽{+11Qt% 8PsUz'=߯f ܴpBw}v1 .ԩSd6վ}{5h\d۷ooyۻ|r EU>}T|y۷kڷo_ajJJ߯ѣGkǎi:qԩS6ФId}4zh}5jU$*UJ5ISAvi9sF^^^ ϵh"GYs̒%KpK.iڵ%KG[N5j͖+c%+6ozck\|٦3s4iU^+/~aI҄ O'NЉ'KL&-בuիW+&&F'NTժU=C_tEDDX}MիWW>/ysqqQժU5j(uAժUٳgWn]9!>x`Lؼy~'ժUUk͚5i!ChѢvm?ӧ+,,Lnnn;r\rcܞ-[~/=صkW͚5K={ HX,P5tK;ah۶mz7>wZ=C=8\\\T^=1B...={5m4?^Ǐ״iٳg6q˒...|...6:w\K5qDM8QSLUwݻw'`L%"o~K ؼggg5nX+W#<͛7k?t5*THjԨڶm+___M4I},K,;m6խ[7矚9srt 6lvz`?˗ѣW^yE֯_zJ=$iÆ ?\r4iZnmjJܹsƍW^׀n=ϴw^hРAj޼]jJXb:s$/Pݵl2M8QQF>}Z|rPB/)k&111;s{􁑭aGVYf585ܮlGgYDDD(44%;u9Z|-Al֥Klj/11Q+W;CÇ[}(٬jժiСڵ\]]m:M*::.d5l}o䶆p;Vmڴo&0|>WjUڵm2^]M6Xbjܸq_~YfJ;&IC 0r׮]T۶m2{fof̘ b\]]5jԨ\-9ydM:Uv 5FNT?V.g况A*THi;̧lv(s%KtX&I%J 6(%%%իܞwP-X@7oVDD-^*٬nZSZr:Sy{{]GIa:zh2^=<<Թs[wA˖-%Iرc}=w駟e˖ d]vѣGt!mܸQ-| KXX/^>>PL&L&S~tG2 Ca:r\]]|k8-}WGW<}$g=ِ{2LX,]l6+00PGI@>_&Cy~{}ߥM!agaXd6@&Cy~{}'Kn`6,d?}I&Cy~{}я 6"dlTCij:Zbs1.8)h$ѪI]]ݯI] pW0.Ԓ] 2 ٌ=NKyk!yQ.õO{u'[QB+URnә7idثɤsu t,C =U:}9/gmҍڼ;z.y۝3ʬ 5}rSUwv'w-㸐rZ?ICWĨѰwt՗ZjTL޾99!uw. ٌ:v+6.j2RWOWS@P{bqM~S?Dh~B/IN5|o\Oc=7rNU0b6dž+Np*ᬫ3ET@ʟI/**/{YݓO<&-{^_N=1}iUydLlsj.'FSK}9[*ol# >uŸMQN3;SI}%3(#N~߯?|GG3~V" ϗa9WhN:~5},4B?.ǃKk+3J1 $s1=2~'Q I#ڰ'Q-\%:(e=2r. #AUmZMֲoO_[%Y ">{V}GM'ю]gum}q+'bϯз;]5hfMBn|Hxr&>V+l\LE}9JieX:Huکߌu>J):2>.QB%Jw+ESIeKl&zxrKw?0YAAeUY_}:{8E^=SON7ԢFyW4nKlvIys~>{#\=_#&jzY7^a7VgU HH2.GƕU6jz\WSjo:GԔ%vm{*W`@LG~Q9[Z3isEH2tЇz2!>ֱ :E=ܤb&6`̈́vKI?V*rUZǵvRX>j*8PޯkݩWaDj}Z -ҁTE^3~fuԽu*|`iRNmxK;5VeZgQdV/]>wgjP ^jP1FU\ hlDnZ&H׀u:SaҢQ=ԪNʖfS׉˿|dv_:6`8R:(=ҬUh6Ygx^dS3S@{>UgS;zO^&j١׊֦OT/ڱ'VezMД!C۶H?7¤kG:V9}r[][=O_y[nڧaS~q?[ʟ6蒑v_=>z= Q混.ߵ^j^UU.7_owmjU:0Xu_HY-UJ.U @*XVU҄Mgo̮L֡7*JwȵZ9qu}c*uD ɑ3ٜk's#XUѡ_a*,8S>N s5IjPEwK+2I(hj^5,+%ٺZ7K-Z$vP6u|寷TaIJ]$#+޽kz%R]іO4%2aDȇk}WU6g-ykzʵ/N!.S{/VKsfx>OoO#KmǏYOI&/+'WS}sx& 崖 |X#Fqsʔv.H25droYg]rn?̪ud vϛ.Mگ%K[6ߢ k?9-5IV%HNI}Vr^zꟍiu*eu #'m!IMG*(mhګ%Z3Brq?jF^q]J%uQ^Eu։_ʥ-RA1'uP`}Pl8IF>,?Ŕ74B{\$s =S2]<_{]C^RIޝ:ޝTE :hm\`')~<./NHwCVz@W&>ŶGK4%G 'xV/U1 z,cִ*jMHNj%??Rz荋k̔TZ9 yMA^RSr*nWkQOU.r>I~\NPAe:%jqxM״ͧ֯'o"Ǫ\Ԥٚ {J4UpyڼLNxXvZz_d\FsoU=2>wYI~C*>N>*eRʵ8fXAE,۵zj7dҒ֨_+j^Y1Q}_} ?v%9QqZYu^St0[vhN z/|Rj7Ъ>4`@{K)u59U~'I): }y|3A=K$5W8o?_}Sd*ƭUmgIM**rGsa=WN*W00 RSUW|gRBzg+C\$gyUVʩ6 /vqT} FQhV=mŋh5gt;͙J[MՕ?M5xBZ[leRj]դ{k~]w\U׽) *+rg^ef{+ĽrRsd9  L|cS߆BH5۸޴#-3x?pC1PH{%q؇l3*)9* ̊U3q'?lÔx:? ײH~ח1G }~w?CӨܨjΫ<[ۓkݷO%:O^_ۄ`$b ymx@{ѾiSB q\9O3y/tJ^"3e,LJјoMe}kP[ M[>Y Whݲ .`{[[v1=e? hf|=ԣ̂^$ IDAT4x%oO)o[CuBLnC9_+tWVSM13zSnJruә^ L U|n#PFU^tuVx Rx94I۠y 0y<g◣78›&R8x `Xx ~#+ߥ 0q X̘@B&~|k\uO|8Kbl'V&V6*R6&!Y@>{##~ u"4L)BUf8,qw ;|T*sbE74v>Zs7y4roR_MOJfaJS%I8w1([)x;*pbH&?-sZн,aSQLZsgS-װm;{?:Om4\#"iN;g2}*Sȟp6wsZ ¯n#GO!fS7E\ȕ8lG_Y{$'LSd@/>G{+c: 8Wek$W.՟oW|՜6;qbk ^ kPZ.CكDJ ؞|Kyo]/];{ nEKԓg?zw G|~S `_9m9̏0)fwJC`̳rĭ L&KpXfkGQm6%sS -ǟ>_@'?Zƾá|٭B'I> :̈́9T.S*'.sşr)L6 |;ws[yjJkH\4ZetZGNDDDDDDD$^|Ȗ]4q ׌E,jͥ#@yS8ofl&Qя${=Q.}5vy:Ȭڱl/#eN9In>n Q~R!#5XOB2#Vy 쾂gU~X F;oKtk7zoR^T_) 3&m04O^K۬Sʳ'Hͦy6%'{Dz^k.1 gl\%ِޓn ?,~>mBVۦumVtb-Nt5,f fRj<À{Al/P=~!iwl޺m$fp G>z ۿJ339zGbc 뎓ՆTpLEvC* ///f{u6/^so[m&h{|ʗ #d^3,ieͦ}Yx%^ORb65}6o+֯exbe-X 7Xbm0cIá'L&0$ޓEDDDDDDD)^\vk;[uөKX9oW2;|v *A^o4k9c,6S"m0E[yliep{pr[BY,B=!c.SC!~ܹ=#s* ~nWV1i;IZ]jOT j=܈8ӯG8PM~)ZlpZԏ|'&&11)J!_lg5nU~+ 'MaG?uDc·%=ŒE] ݳ*\z訔$OvP2Z.b۪y|6vq#*|3//aUD̑ FʵCJ%(R%}<ߛŋXq >>~ r'\fs^;ӗ1lx ”(jwz\g~tY(O| )f^_X]2E82|Euf2ڣ(u˪94Ǘi1_|re)7=ۼUg 9!uœگK\?Ŗ^cVo֭~6ͮ%:K\WJt z d uL@./+FZF'?3Á;x-N.dޅVx"Ԝ/ p=o-/-wɒ*'6:U[zpu߭Na l8g֠_TT8ԝWqZMKy=gZt!xzbNd!{b0ӛ3kz]E]=^'u1^Cz nHy?9-^N|u0}P ν;QǍߡXS*JwX4m$+\k%w 7L ք" XR\;PhjDŽfu9ݾ.W]SF8Wݮ4ЍnӺNa&+jNlCqk%:UE/FdB܎=|>5ʉSq{8^q2sNjΠW?U֭Ψ!/lsEv'+ݭ{m3>jrmYEEfݡYb[Vd/4ȷޙ1boăxrxZ4)2lbp*ԻeYq+2Ӊ4!Kișҧz93 Wd0.N,8uA: qf{pŸzT_ofӫ\=}i2:` r<)X $KyG0n pdM/q舻wnʼZ_'=h,vYi5:|h4i%H|kɸ\?y1Gl-WnFƼ7+ TC?m>4_`ycI&'[L{#?cD[ᚳ85>GlkN͌\$w xV);9Rq?uMYzBk1f3e!_@0Y[`Vf؄e 8' UრTu81a(b9wr:.x#o3ZUȲyno%s*͌k~8=Xq[<4FEq O+Y0|.oaw'wъt՗ h K܆3z:O.9:=r{&cV3r0.ŘuKk {{ӓ];<6/d(?h˜`gq{as`<~o3)_:Kӓ !!!xZg|W>ώi͇/mTgժU';"D>6s:<='N}>xY2z?)Luhd>O/qSAڅnH^*UV'"""""""fAmlKz"Foe2j0Z0 WDD,k7g7hPIev.k>H,k{R+_TDDDDDDDEHEC2 g?̑Oӻy?v h`uLav!j3x7+HGT/b^?:x'~OZOO>'V}d~ b7Tϐe˂)>t ^2ɤV3iƣso/S(d{L& ]ƿ49yqԷꃗEEDDDDDDDY ܮʕ+w5lok.һ yMFDAAA]<իS1g*FDDDDDDDDDDDD&!$R&""""""""""""F DDDDDDDDDDDDDH!H)dI#l"""""""""""""iMDDDDDDDDDDDD$B64R&""""""""""""F DDDDDDDDDDDDDH!e:Śa}|g4EDDDDDDDDDDDD^ lW1 ,hM~>ޕ B[l7[lllwv'[2n^|Uq,9wSh|eޯ?`Ȍ 68x׿.hTL'&R;llqΜ{^L2PBdw6<[q)ٱEig.y~HLLxw}4t;[6,c'(U9t0Ub"d>v,D̰mј¯q/ ~-g6ˈv\݁*p4Y\#<ڑc-_8ncYգ5$LCfo\ZfuV6dh3ځ#}&""""""""""""/ .Ґ)?e*Qj-6>y88 l /qcL z; 8 |T^=`غ.? [/Or ӌwǮd:o.;}Q;zb5/ĭMd\ fq-7dC턷S&}%QT,Nx/G!_s! =c@*UJ1};["S;`U'"""""""""""25 (gM\d-,DĹoM0|iY_=i*g0Dp5Ic 8ycmnNmd3e0؂VyVBVڂwĚN0I[Cظbl@8V;_Y^%mRc^;l̡( iOhHDDDDDDDDDDDD)2~,: еp"qX(|;MIf߀i[0+,\0Nh!f$´k&}r n_/?gWg=Jo9բ_ve:9 |q5=ha)Vw Swq=\%dt~ zP Lg;E<Ϟs/C]:8hlܓaYχNW脳he3`UjeXKtK ;3'eѻ$8ф߉WI ׌x#n|ƏR6۱s22d1g$~f9^ ={;""""""""""""f/tM6KY-"/eC+^p#sR ql7iENu1ᖑl>cZ}U_KO nŇ;3Ϩ1Qذc׶$Yp*,Gluyk\E##>ѬO'>;]]ΰA?JZ==ɭٹ#D]; 㝅ːvDDDDDDDDDDDDyxA!WOOl7NUͩmp^A|G7Z╴c 1Xlp_=%;}F%l7FYM*|ϧeOey7M;s"r-kvȁU~:.݊בnbp$5^-4.UFi}M+XQe,_̩ dt%Rk[G7k[{إyR;BHHȮFbDDDDDDDDDDDDDիiڴii۾5DDDDDDDDDDDDDB64R&""""""""""""F DDDDDDDDDDDDDE5|MñEFDDDDDDDDDDDD/L&& OOO{/,dz*8::b41 /jW"""""""""""""Y,f3o&,,|=}'''lmm_.DDDDDDDDDDDDD`0p֭ [JDDDDDDDDDDDD$  l""""""""""""n^dVBC6#l"""""""""""""iMDDDDDDDDDDDD$B64R&""""""""""""F]<Ǐ'**.EDDDDDDDDDDD$M2gΌ] ?X,һgr9N8_z"**.CDDDDDDDDDDDyxxeZ?CSDȿAF<B64R&""""""""""""F DDDDDDDDDDDDD(lnA2;;`ooS,,R:3Uoj]p))Д<_ɊSx{X*+.;M=h1G,)Vpp%\rmk#;8f|FNŦF-PYppCƃ{_4E`ѧxT~{W|ޝq'S|_擄Ǚ4Npeˍc,) f^(Z m9y: 5Erx (SwWΣB<9t6`RՂ_O\7PV˖S@QX  z1X S/xP ;e/gmVZc8luɗh1|3f `HJx([RVEUtEpl V KRyc0ntw?r@u翗nfrͲX=5MDDDDDDDDDDD^8RϚӿ "ա{E9$v '=q#έѺDV<,\ O)d3s1h:s֧a$@Od$w@̿˙i񙹺;zdwJXd}*[}fvV/ΉhѿL%]Cgzt7z%Yo6]f2^d23WAIɕ+l?^޸ZrB6y12~H[2jKhxw)^$j$f*K* s-*yȒp#Etfc[Mut;tkߣfˍy&[p.]گިB递T8opdqGlBԦ]3yzIYs-{w}DzMl@p<20ݛ-yg&r)$"""""""""""dA~:OarX%&qq1`KNy5> fV^#'W\∎!+wXȞ~Ǎ _bAY rX"I %payG ~ohڂ9: aD{wj>f=ʺ>SeՇjЬL/Ư'1Z.YƯ9ޠe%/vrݾzhCVye[DDDDDDDDDDD2v abamGGs {P#<|Խ.F',D {1`ja&XBޣ/a+6 [=ha3~aAB%ӘE0OIYnp[0iݶNMmE߸K:?  F0oJ6-8>OZu)̞6X3vY?b_2-"""""""""""\j%4{Y-"/cGlA~Zs'1S&vlQP$)28Q1XRܓ'0mo ii-\:=;msf&Sft?%c\a&rFʼnÇ0▷yl ؙsѧky{ðg,Yڌ-īRj gr)HT,sguVX,vw|x5F[yt{0&=)ջ$7k k㞴^m?sP=\NT܉ Cq̂tNg+H@ЛL֍dM*B썛Xlp_=%;Nd N8;Ah@ٗCf   x~u`kr!,XBUfXn]BCaOïX^iOFF ށ4sbN㧈8N|?OքmE&==L Mb⑻:g'-H>-INz~NGRam0>,ũ?|-;4EDDDDDDDDDD,P]5Ju×.]"sX[g?nݺu4i$[VXA RmBBdϞ=Uۯ^MB64R&""""""""""""F #2gΜ%m%Pһ g~<<<һ ӻy9|}}9qDFFw9""""""""""""i9sf<<Ǥ`#,6P[|6ZԡZ<~=K(93W/]Bϧ$ ,Τ9)@Gl<jTn.fZ*UVl~WVY)VjkULEDDDDDDDDDDD"Cl2fr'[li,Vmw wf zP"eΚ5Y<>03_`ۯeeu/a14D:͘z*q˜ .,+:n)Sފ,;u9߄~DzUntKbw}d*6}Ú)ɴsOެdCчs; 0|9r\=tVV)'t.GrD:.+7"q/oGҦz >9Ț3>;b{Y=Wv aDe퉷'\3aGr9+zꊫ+9wKWX33_%ԇFw3vD};iUp+׈|̙5=ײww,ArzVpqCw od~=kpx/X-\ڽf1wkGi>mۅCy2H6X59,֎wrS񕜉S ѕ'eşޣ{6ْ2O^k?L1NOuľ9)#˓>M}_g ӽ 6_X,`Ġ-_0S:fs"Kuqŀ>„ڈUgp8nC>(LņLf*pz48tƈ5E {`'sX-uEp¥ƝS=*08 pU¦ӡz,`~u[(@"Ə⶜߶S)/#^%Jtb|GBÿ5gpY>i p GJк_Gj-NQ2a`\*^ /I~# B>ds~DkX7&89~He[.脳h*xM0G NCй+zO0Dm,4gׂz=Y]le3ODDDDDDDDDDDQj$[r;0mVw~ 35_f7լ,ޘ"nV\?{ sboGѤetLn[=q1ؔ{-n>y])súWyzlr* SKͱ?e] j||[iwa Olil+MڝzE3c~JnSҺ"%4{vl7-"/%tLPjjFp,܈G'tMJ! N8;AtT 2F7Ypj& :fS,17,C6{p};` b6:lMИTP:lӊfWG9$0^"""""""""""򯑁#9epZW' $z6D'7O gRZy$߬3(-嫕;1{|jV2ƌu$wq8}Y9JI5f'bɇtn$qVNyfÿQlK -bNG>J@&$Sa ShQ} F['ܳXS[弒. 1XlpSdJ%o.)d8ϛň3߱#w1*uOZ$ &37S4Q7L\\1-P/__^iMgZ-ϞOfy2BBBv5j( _t̙3cmsuѤI.CDDDDDDDDDDDoYbٻ8313!,cnY*IhVtkSB(%ڣPZh3ٚsKqz>g{]8vklqq1yyyTReO:={{ՙl$I$I$IҾM$I$I$I!$I$I$I!C'233KI$I$Ilo< `]$I$I$Iҟ6o<222J J=Yt)f"//ˑ$I$I$IHff&dggv)!+/$I$I$I n)I$I$I$EȐM$I$I$I!$I$I$I!C6I$I$I$)Bl$I$I$IR $I$I$II$I$I$I2d$I$I$I"d&I$I$I$EȐM$I$I$I!$I$I$I!C6I$I$I$)Bl$I$I$IRhN/I$I$I$R4l`АM$I$I$I& 2w\Tfٴip@$I$I$IvÄB!6mDQQQTֈZVR%rssٴiZF$I$I$I`0H0$+++*G-dKNN&999ZK$I$I$I&jgI$I$I$I+C6I$I$I$)Bl$I$I$IR $I$I$II$I$I$I2d$I$I$I"d&I$I$I$EȐM$I$I$I!$I$I$I d SL)&^]GxwmGT^ 7H$I$I$im3]фRH'!)iyҥ}/]9N[`m^Z&+- ܤ#g9}}guHj:>(x,I$I$I$3mo"F&ՃXǼt\v|6}Fg]ypvje7r\+Gr4B}0$I$I$IJWT Nˣ:+g4,wc_SɤVKsd[Ҋ!THI&e)Z/fBqqq$w}5N ❷Xӑnゞ]8);hwf{ΟσOUQxfٖ_ղL\qqIl[Z C˹M2{Bs~bkרDz2$Vd㯟jUHLRnQłb,}_ԤZN>H$I$I$IX:~#\̆?aة|YpzHz9Es:+-GPsFt)7$0g0O@ϛwsCbMCzpl̽oB?=ԋHV1TSdȸhQWO 6MG "~}ܳ6 ⻤=$I$I$IjV֥L !B0V a< ;#.S <~jr=_JXS{%Ww⃶(E ^bo^r'GQ6>^%¹cnU.5Z|2^k3 na[IX vqbz>hO2 y˼-h x2(FTNb&m2otڷ=)-/$I$I$I?#EZ^-o! IDAT|t"pb ;6뗶}Fq}\H]+wnp!>`Ę,:ye_SWݔ^?K2dIo3e?Ï=^+Sy䅀-|'JY=X&uMLx;8R$I$I$IN@reo@Fzdk|z0N’K(1"w-.>οYWsD7C|knu'1UM.O!A!PMYD01\i4y7=QK%I$I$I7 m`}1$&(Ca,%J+lWqx-Vh& oyL#7 l|:@-;FZ 6DƍUnz[*) eu rW#D5 /ܵAܦ=!sՑ<$I$I$Iv"_29uccs >pON<3#qtw<([?SjQ-@L 1@q'L? "1TiЁk&r{'$I$I$InV'Oӽ{ x#o.]$I$I$Ikԩٳ5vL8.Of˯džfLD}hSx[\:!&6rUd7`tI$I$I$)m_ ͼ4s(s"g]v;~.{ `m]tU PnsG;S{n}Mr6yHFh(:t9FM偳3XüW I$I$IKC |;|Q\2SOw|;0l,XwnMlǘt|Zf0vy&,']:.!{9r̘4z3^\*/yWW[~צSoヒ],sWǜO [#gi0L{'α|ܭ1!o=CM$ rYSʑ'm0]ޚkj` sW]"3RMn)I$I$I1q]X%ulKж[g/\%u: F4HB,=8-C q|:>7:@v4K^;H=o cshЦknƿ_Ƴz0w,Twra8%G/SFZ|1{༷ /sǨyTuٖd,Sq{|4M,O*Ĭ%4ٕ]ҐͬcTf)xԳgЧL*7~ټn%~!-'ʹ)S$I$I$wv Py9kx%۶'jm;P7ybbv2ɥ "g"~,\ʈ#3HKK#--wy*V^+WmoICuy S>-zOrߒh.;˯hKJz9XK޺~*H &qXpiTY$I$I$Iid8  2aJ2  ߚ!oÉ;CZt'…ήEPTRHM Ujj5\5ӟ1[HAf~\=ƾVθ|6嚴Q $I$I$I3lX6pFB_z;|i,ʷF4o5$+)@uS6D/h; ˮ9K_o'3!*ߕ>'@(*(`#eI.ir3 C: LJR_qdjB&Mi!&̫%H'I$I$Iwۇ:~-a3ozg`|8F% T1P_!g6 f4JgNAE[ b2nݔMhu!zA$c6$n9 ӿgcM;䜔}X9>c <~wC}M?qe0?p e[ʧryfoۋ3LJ2Ea9: }+nϿ/o_ChI$I$I=o bcЅ\t՜Dpˑ'm2cry<>]WάO k#sWdᵼ|]7^ZьEΊฺ\4n½#-$dԠ٣9(fd:)e1ۚjN RP,?nKxyLEl [V1@ P'*w$I$I$Im_ ͼ4sn0Lދs}0V 7Ƭ ) _Y:o~6ۧA oI_g֬Y̚&#.#6k뵙Y|I$I$I$i![8JU>L7?^X`O}㧭Xf,kwh!,R9lؖ%4^б g8/bGm z*XZi;&3fi5 wuo̼>>=b0 Iww! *MЮ];ڵkKiVav[mIr0$I$I$I6dU)31>{w;0l,XwnMlǘt|Zf0vy&,']:"ނB-~rq1iFJgέh/yWW[~צSoヒf  y &SV=iy͍Z;&=:?\rC9InŶ@,Lsøwykڃ55|L'x͚<z3=h!}ޣY,kVUΙ}9shq꥜Uq6? 'I$I$IJmX%ulKcOc}ё4s/AL j hРUah1Yzq= Ǟ؊9L9\SнcN%?NSw cӶ}gξ.kY/a#P8w,T҇ra疴hwGO2eAxbpˎ'ezӢA>ZKP+.iGw޺cR"g_OfW![1_[J48dx huu > fw#903sVt\/'Ϥ1i߹ XiOI$I$I?gv(wT5D-aDžܶ5wC\ļO 3bv2ɥ "g"~,\ʈ#3HKK#--wy*Vpש]mY<4HnZǺp:w@$?-f##W/91S?x0uĦ#|g_ŻQϓt5}Pa]kO[<}pX@,ٽfdX_n>$I$I$I x"8q/WP&h%6qq n ao͐7k!Jb׿ψO?1yʹmIPj.XG)dt~5h5.ϚApN[H- 3Ke*զi4==BKix]ܖ^+j)G~6nd%_,jΥړL(X565ǖmkf$?4=Ru%I$I$If)&.`HӼq<_~w4X[|ӈ Y*CVd>eS.u怔³ЏL:wO&MBgЛy&@Ns4@QA)KrO ILo]։TdR좣0@ "a_eN*3g|S+P6%| B@x/= o=“_O$I$I$)N_Kl،lnƙ5ؼ McKi=\4$8|Йtl&>oiK$I$I}>Qǘ 90kw#O,9dR e y}&Y5q.C@GvDe9¥TQRfN{Żs=LΔYHF, $I$I$I'D?d6I$1*l֩C2H { IDAT q:PL4jqݻkv>ywCbԩ[*)`I$I$I$N⇧JT2Q?+G}\<-ם+F*q>oј[$I$I$Iv-ޢEOqd^3%;vH n$dׂ,ْ3kG&>}*4Jz*UWuh9wI~<~EWWM#9;PnKbvN9N(CNqZTvygt:4:)~hn~;~㖩G\\񙽙z>2fE8кTH!L25ZpЙ 4ƥ<=$W/O2eI\f2ӅVŰ[ppFYR+QLa $I$I$ID5d0s,ЁKg0nfyܖiLx*n.10 5y4O)I8z&ĀcOe?-:*S;?Œ%3 #Wu&w/ 2,# 6R0kŜF\4^~<sn9O9C0NO M>Z6grº zm={8ƕ#$I$I$I.2Љ8qY{("CڝF32~VØ9bhCGKZՉof+' _r:[' q`v$x+:t[ mq@:jm3񟧐X<,zdQ>mKmbb8cZдn_I$I$I$)Qku -fV: SSv%nncXѪv&RZ$roygr~| K ay1 0lRINN&Bw&ꇝܓbS2!?by|ۂc;f|]'\~1  ݸi,Y$I$I$I_0d∋Bϧ4 bbcPDQѮ @ L(!/;KL.)XX=z!'!m?>^7kA|ɱ8 #~}2;gg0,LmpnK5H$I$I$d ٶa_}|#Y,~|" !R/]li(o vCͩϬwryz3MqPKi\IVclOQ^=ԥVVe ߮G&Q?GUhIW &;]~}vI$I$I$)"Q9mǘ2 ONӕ!<̈́wp'%Q]rկ9iAX-p꠾e'^堤M|44џ:r:f{g#%~u@»7f۷ jfΣy3XU\va:ћSNI//hwA7jBbfds 5ΪxJzgv ;|2i:_(63q,Ђzջ|SN$I$I$I%JcX[RN2zЭx&OxaNbQ{c}8훒ִyuFnA>mk(W-;4%>Ȩ+3rCYjѝQ _ /͠¦)~,Wo$3p j @_sR! b^=rWra2jw#O]\4d#<;Uĥ'1 f!6b$7~.$I$I$IRa011brsswۛpK aJsvP:v);}]{{A*!H9bKfFԕѡ~]m2XˇUa8WǗ+7u4eK\AQ3=қ.3WRԍ/$I$I$IRl͖-[F֭Kl2233βv^q5TMs#pԀZۂ0ORw%!%l|47_~ϿԺy>O꠮olC0kyb|LۏmcxwKýW&3ş<={~"3jrrM}9^@~*{}&v9$I$I$o=dkٲnۛ%iO59yjͰK?wm_-/ϑ\4я xB/̘#CWa,_)c4>ʦBL7;kw$I$I$I{$d vZ(W#Gx|!¬q'~݊˯ȁ)%OYCÚRn6MMvE֏.1/3nҷ bܬ:Nn^ՉM>執]OS&GKv8<\t7g*?դ;›ok׫3׼$I$I$IQYk[![xk^qu{K&gIg qAZ֖ʶBػ(N[H[@zzi (J?_)QH M!%PIvF E.ƿsϘI %-?}bUddRw-]~;gQhPO)%` ΜN͹NGj|A|L;׻|\6Dr_MU\_&GJ+Uc] 0_҉viGZMz>^=(`MTMܳ:.e&hW[Yii:T&$}#FM_A6id)korAi_354wu:>BmIN(yzgl94F߈6$Oo/gzU:w2y|cg\}095^uf7_/=P1 (J#Г' ?0A[llLpVR7>>~{s=c(fHQF~^K8w`ԩd5ole6fi&Wkdff*%%El2uܹLpٻԲsZ;ɖGܚ] rt Iәf6elgGdf ptْd]Dm޼YɎ.8кur=600PÊr w.D؉ !`'B6Nl\][8s___UT)*!sΩzv}端ʣjn![.Y^ioVaC4rn^%#ɖK7W*R$I֔DmZMG2~^֙O/+~˕N:pR!Whٚj=`Iwtm)6M"(KU1~[wR -MrwSi֓Qv:i/B{Yk]a/HͺO_YMYʨ׻u8S= 5)Oѽ۩UXuyY6B͒vhjdž*YP\R5ߜWS =zgp.ET)v⿻)mW*W_J쬱k*Wԋt_Qf/ijf*)Yu&s3ڹy2 ћSjվOgK̽z ݢҾ*./y/ -C#]WV[ܽJ;!_$WES$e۩jypx\wޞ!XJ;-йBjX%o>)]5{14[F UhIjv}2^HNChg 4O@FJ nMoRYCH,Q7RikuTlLr>nL>*VHF'1KsѦj/7lӅkَ?y^rKE_n+vkъUZ6gj&^Ү쓹7{w^2^ rS>ӈ7j%D{EOЩCgq՛Lo]Seg~s{Sh.K[}%ujWM]7ize{a DHֺKdլFLPѴ^EeT}k:)~4~z6Ռ-3ճ/K$saAKFuZ5YT[{o5`Ͼ{#d)14ajm^ _nZz R͢}$EnZ3}틪e:"+؇u9Cyt r*W6.))](:ZY\3MrUv/k?h[X בaV:n:o~tu<=AW hK?c'q9{YۆG*}C:꽤L?zB6]]ȨD r^O\Tl ϜR5_B|9*Vԝ2Ȏy3}+ڻ Wks^;_03%+-s[-'*둪SxmeQTzZ?rxA9!8Iv{WZ7&L&I IaHڽ;Gޥ%ÐaS۳}߯ujUl6C2I$U2l42˷t#=Zo.]/E{3%KxguvN+oniEjvE6YW`R`rhUTfP͊55ٻiô6^TV~RNslYTqU\O?9Iۣ{ȿ|6RBl:x4Haʩ\+ yR:JMy0*!C*I7iKRcyWTӺdyRַ2VӲ+o.,C.Yq}6.(Jm_-RO1wrl*t/Ҝ-_ivˋZB}KN35`qEETxa_&k4(-]s^*NyA;QQO?P$=QC=xg@U5s\}[G#>1J6-#d}UER>~RJfJuS9{HъghoGTP %Z~WU]L*QuI/ إ3mFl{*8|~JUTV_>iςw]Pu vş &PyZr!}eϾF;{Sl^ZwF֑SexxTc [FD{Ig%$/`<J6@zvT ~urz,~<<.->$W\WjtyE+{D%hpz5s^{:\/:d-tZ?X1ulKFߐ r) ;a ó맷_"׆ r׾LrsT7>>~{s=c(;P 魣ZDέJ2꾹j@ jeԹszvjStiq'kT%`g9Ԯ" }A{#뮥g;;Ʒznm2;^C؉ !`'B6Nl;v"dD؉ !`'B6Nl;v"dD IDAT؉ !`< ˬ*B6J1 CV5OvɓY%effҥK2 CfY&)3 C6M.]RFFFg![K.b2 VVBBBd< ټWgwVl;v"dD؉ !`'l6Mm}s~Ӝ4;&oԖuzr5Yu~͚ UK _Q7cLy ) .dWScj)ōRAzx-~wkV@gҕϔg+]%']Ra5ժJ} ]}"&y=Ԭj։VrrVY»[ ixkwK-Z[?ց (dK׾ik!lVMJ=;)Cz|u~m+{y)dMu}iNdRFV?YKeB|KeJ<[z0W|.}Ю3YU615VN/5${ZJ@baZ|؞.4!Ԥ;iJ˼} E..y)cE+4yDzikᝡ3ﵕwg-UԑWAڔ~30CusW:_G._/CEj@3ɦ_V!lJte3/EpWVW:z=}R>x\ WӉ7![N}zF5 N,r0m}#x(P;vǯfQx5| M`֪ajW#۱=Ze/5V.O#tfmO_OW/֫k{5뭮4bG~c6|2YS?H Geάһ\]v<^Z>O\;jGzfw/K;t8Kc$Kq52Yڤ?}[|=FjչP,*B=p@>%efSM=k}K|:QzF{I2+J7zsm٠q u;#GQʭ,=2`2Ч;vi@twu_d$'Sf^Yoq9tij.։ Qj.$oYT*J][ /~Nkbj\,E7Tש*]b WM(LI*i5vНX:am/f׊5T=CI scKOxJRuyt*X[_kثʧH"#}:^Z-ÙqђInU>2u_/PwI)*?j]kG(\_R͊:vܣ󊐟uY-E %YԸQ3ի_&UVM7Mf2 d.sQ-ON)&y+Ig<6o#i].*VRӧ|6*K1Z&c~KO:~|}M鮪x*ݱ:?QGQ\?޶e*W41k:&w?Wz(rλpYU|"0V Z{)yZu^xЯkI1W^WdTOӕ*ZHC:VZݑg< AeTߦ•ߞ\]$U6:1Q{&AEMKVM~JM6ߍZCaH4f,6doawI9O _+*^J.2dI.hT,A&/ 1Ir>N3/6y 62[2)C̎ӽS-MBZӒwm_XLхh֔*V؛m,Y3n'K^n]wxMÚ?mT0n.OB6ݹS6&{bnxգ3ΫoC6U0*Jm:[xL͐͞άK59?FϷ-$lP)LZ_G|Լh. oڻ/I &^F~ׇxuȐ&f]g>,*K9Lw8]ۨ̄:^O-J%]/]e:ilY9H!~5xÆ{nL^*Ѱ7|P7Wg'BvvO܆ FgM5e=%K+|I'&L-氨Tp^WެA 33Anzh <1Dd:IA]*eYkWVa!1t篴T]ϝGKj%$ɢRվ. \u׃:]z9Qdy?^-TR>O|mpV1!ǟTTתhR֥P:DPJzi$MjdSCSRK6+&tz\Ƕ>Yi}.aTδBOyW{UU]j& ydhhwTPQhS]GG&ZԯN.֯8dF[wEvJճ=ݑg뙂[ϷjN:tN}5zNϴ-[*L)դVMi6Tb^78 .t͓m4B I\Xc=:SM ywZך}5=M6鮹Sb_:Evzx"}ٛ1'I=wn:5k+RUR)kRS_V5ݭZSƕbd QgQx=1ucҠ#Pk<8ZlYi7+=ZMjH_EdK,=YulXSҤU%ɳ|6>! ~O -gW-նV2W[SojӰj7Id\R YU۷bGYl:w\OҎ[ͳN6ߊ !`'B6Nl\j/*))IE%n`ZeZ,//;>l'O<==e6e2j):0dٔSNdɒw|< \]]%77Z)ɤK.yv'b{ c2Z< $a2Ӑ 7"dD؉ !`'B6N..m JMMUrrKN,((H wt)@ ZjԨK{*11Q. 0 GUTі-[]$B68Prr2\AE"d" 5tZ7qVߝePǘL&Gp90 %-:ѣn\)R}t o1="eU$ .i(xw]4dvTRjV}Nr\wҍ!-RDCkSlZMT:+rnr (0 y0dmpv9Ifӹ$]wQ![ݯgfgz 'a_ Z:S%W&ҪQj4P Мk\8njҴ[/QOLUtnIw}DOަ-2cxS;wRER Ѧ뫱Ub=-3Ѹ.X}ƾQ-E6]OYM)Zpsc'-f׺͌c%CƵ4CkT5r: *ad-oh}eUzwT#$C SRu$&2 /kLrOWJJvq֭X*d+]ie~Ѽ-5h_@3sͦLgөj+/hz(ksQE 4.O&IRJ8ME}7V(nީK=䙲M'z+}ePM:H#K?k3To4WQ%ժdhߚN_ +%I2}Uv"N6IG**h_0W;ծ}0 ZS6g"]%NNa3)_DsuiXnWT6TWy 66-..Ω2%B*]zLx侽R]M\ $I޵դկ*"tazImݠaV&m]ҍڲaVC/)emoHGGOȍ\+=E7iӦ?^QٛjoT\sɷJRNp)..8UkZF{*H)}@2(ukXե϶+'ԫҏzyg:zVnLn,?d5 &?ž^f$W#?pc'^6wQEUq~^i;ٲ eZMrqd;JߒdXbl?A X),,`T!ɻ*V`*B*WVam~NZ^}.m2MUV1Aʽ P/˴RA/kG.#ZS[$ɥt"/hOP7T׏ N?cfH2,UI wJ*fIu2![lٲT&9Yw\k&P'$䴶9gjkF'I.չKM;YG Ч]eri?ɧjpIVzH5xңM/#YGbUȧoNk%de C:=C-zs܎mRi0-S2Lt8՝l\R}>XqZ:Zwݧ58"pGv.VE5\%CJDW?mZ)4kURyD&h_(/^VwX̒r{An㾺۷o뉏;@3r˖/_-[fqi[YcCշoYfYk֬Q6mr5>33S))) e˖s$X08ԍwݾ4kW+UHs:nWjˍ 9ӽml_uh.r P@@ʗ/R$"##]̎.v"dD؉ !`'G*99ѥ';Ilpl6EEE9pmܸQ;v;{Qbb"""#dä*::e1 îUV֭[p'#"]$: v9B68DN![IxAAAwp\dvvj5ykƭǦ'h δ _0?4ydM+CWзG[𭦱_g阮_>"U$O!e۔wC^8#کz@XNz+z}/7RB_XZ . Oaz|e=_C",.䑬:u(...W:u)B6K0wV[/ԡs9lg߾QN&{ 3^N&x孇U| W^uHM'Zjq_/0MQ ?_ׯY}I'~Zl:+jr{ɲ*fJV;t dn_¹ڭCT Um;@CU iaH&,tfY̛0dyp>~2On9\e I&r칺E6Y _L+d΢B%^?pBA*@*m}Q]x s@,ekQ=pUtѼK:sd6MY!+(6m;[@VȶrJ\21eSlfj㊊¾2YMFkQiPZui`B*B5(k )jR,W<2]jhģ3zZ+3gj옽 9k&9IȖnW॥zgl9uQGG5ְ/hDH _&ӈzNeGSAu@?f?U^2+ TPy$545D5U(>=ݨTOVLxV?m{iO19GS2ZA=g'd#0mb_y/ԍ޾}\O|1%r+{^:3{X|:u2<^<IDAT,YD͛7!_fYFmڴJIIQhhh/[L;w'iǭ:E'{weg9(fGkN6lGW ((%3l6f)000+=:0ڳgb2rl:x]{ p%&&jJNNvt9.[n]_yXQ{p\$`'B6Nl;v"dD-!!AJNNvt)) @.E!(!!A6MQQQ.e7nTǎΞ={<*0vtA ðk|ժUu<>H`$G N68l΄ SȖu^PP..lplɚ2e$iĈ7m&v+vJZKe 7ԯ3%:6.y/jSUHm|g*3't`j:ntruVD~ʗ )MM^k7w.`-%%E)))2e6g![-|ˣ|s-ʕU!tY&lЙeNW>Rw<+.+ZVn>-5/YiժJqxï*6 |نg>֓&((}a6}tHF׵ZDu(K&0 $MՐdo#CQ*In rupa_Z\M uoH9y^17I2+]O|OUNnMi)'%u}-MXS+Fg/֗IzAUL٬Щeuн=MM9iMdfVzrsaͲr): E5PÆ հa(+"7^mɷWz6S҅[CԆ4)c&W^E}RiҦS8fӑբfWۤM:y,ME|.eHRvMiPJRf zaT7L(X v\ M>¾M4?Zҷ>2W;|ۼ9X =g 7_Vuf`s]QӞLIF>}6Vu+PH~oyztʜWC}\Z doҖ}լRqDh(0S6NQφa Upzz`z>WHMg귬3n_u[azDؙa_G/.Vуۨ5yNYFL쫱;h]ԥ/4"E&I&~$S.ɖzd2d FC$YiuQ\\\:uvۮֆwʥۊXLnɫd)&'vpGa0{U8)f8UQR4uMٯ [">Vv*i6{w7 ԊDQ?Hęg?Ehƙ jr4&hP'FE2Ԩ3N4SbPKoMuUkA[o=׽r_j2I>x9Ol:CO/+^`ӲgU5('N2Sfu+$I􁳲eP1 *aْ$.axѿͪd[lo}l9blVdxvӜs贪}-oWgϰM;M2S\klֳd{g̸2*̺qtf=\\iՅ6kW/llՖi-/;oPlqws,80k=#_l6;mN~4[*|}R0cLL!G}CNe^xbUdq&L o#*q~߿R̫HR`oAܯ2SV)&IM5 /Y$^JYsL+WX.*-C~9Pl\!ےwҞ9zZ}For˹erGU-bPerәś;Vf'l"OWh- )W@캐eh2-S-ZP,.R)BpV|ɏ穧̉",$I)RYQ}ϋ w]k3o˺>i߻w^^`V"?Oצ12Ij=ҡ)zeOGfЩF_mVKi,.%ݾt_XՆogܷi~Q\y~Lb^C_10h.kt_{$IY-KhI-e}rgc9[T-?>7uaI$|l7oJ;z*jU7d\7[3.Onk%/%e7|?}6Z?6j.}9bzuvYc&ψ^'f=e>lTzT'w97Z&?3fed*TdO.I8+(&,Leu͕5=QZQl xyN{d9r;5{ھR\r 3i˕|hy頭lɪ:.6_62?xV:e^Cadžduo<;~vQNy6Vɉ>?uHWZLEOLșg퓝>hqBi-3dҝөrOJ]5cȑxȨL ?7wM阓qFJeo5-|?7{[k>k~mPmًbhN<ݟ]/K՚;gT.eU[hyNlssueEo5`v9)ޞ}7j+.سgϔJ޽{oB+\?J2eʬ=أܹsVF5}3ԩS3bĈ?~"3fȮڠke…ڵkO6-cǎ6V@XKm={~k*_…Fi&TNgZEEEb:w܈5N6LN2{.h"B[X3EKt)}iRhB>lb6h.==ztY̞=;sM߾}d-Z޺HT*kA#4R5M6%"r-XKH &Rn'[s"dI,d[^MMW]NY,l[N΁߽>/W*>-X \pA.,X3Ǜ![Q̫ߚ{14w+ɘMV-Z9⮷\wtFڴlkmͩkbQ[[ɓ'g…Ypa&OqW[s,Bǭ҂kg?dYDRߟ6mvNY7 wotV}^|tm/G|+ȬJYpl򙆱ϑe3_&ދGgSVi~ s޺-sN01vYv)ƭwoNarصGgdydNCMvp\~:{.}9L=$wL3/$uz(9 ٹgG洇LoId՚nϱ<47o^ 1KΝʲYLm;V}Jn '{L鍛s1g7/3SQF~EF}x[5yb3},57}L[m\.<*kH{ʾ[ϺڧK-sЅfA1)ٮcvt_lW.ݒIRfzi_fkϓOv~";]~G[ό{dy$)N~ث*B!V[t).6.P(f|o iC.mСͲYlYmXֆY2]ҭwjYM2,CO7U_V&QyL}iB.{Ǘ)dA:f/HVOgm}ZqOu{_K>#wy$KG-ŎSggW{oM; ̯^ͨsē*Tg{r̜9s2穫s: vϥOɜ9sGO3_T":00߶[i?^}dV sO^*pMҡ}nyoX!5;<NëJgʏ~k;[ "{i&x/|oVȝzgweݙUQפdF.lvڵѸwao)_mR̫HR WEK^[QBk@ 0cl>d/ƲbZC梙lIe}sVuMk}믟5FȎ+#e%ևMy+sߊpn~,}|o_T_?ݽ4u-o[|_4~V!u/<9ˣ'O2:W/k/XJhDrȶQ5c=g}MQ9鿯΍=},>=>⫹;dJ偹q.koo:Co?}?UJTu"'~j5Y2=&\ty{ۙz1]s($RJu9kxNb%e 5{bntg:|zOs~YDќBr5-Ir@ms/ozoNeFKḦ3'Wܘ/_[W_)m8cW$)wNgإnQWlힲr>ٸUsc{eX-sksnjTM I6(W;羕n.WaBR,O_^aw@\~7s+.$OL[=&|t_m7oJ;z*jff9숡)Gbv> Jk /?;3|cإ?WǕyK.tHݜ'Ho͙uH!Im~/~\붲VRy<[ UYk72sfJ9okms_InI*d)Jzؽfc&ψ^'f=e>lTzTVRzLw۱g[~CUO<=SyyT=my)ri.陴gcJ`մcBǑO׼_I9dpZ}|uL3wmޟ>):2^.kn/^#6[~u¯o(<}O"K:/1ionco~"U:[ˊZٳgz{ꕞ={6%IC-L2k=hįj:w,G1}3ԩS3bĈ?~"3fȮڠke…ڵkO6-cǎ6V@XYwZϞ=?XsfkfE@󧓍&SSS%Mδtܹ+j8l4N:eM]D Bbgy&:uj겓d 'sC=ڦ.͜9c;w;o߾XQ hR2 LB6( $d2 LB6( Ԩ![Tjs5fVh![}} &S*R__(sW5ʬItҔJTTTP(4֥cR)b1K.͇~(hm5̂ tTVV6e3S__.]4m6m۶m47+!Ie@lP&!Ie@)tEXtCreation TimeMi 11 Jan 2023 11:04:08 CET3*{ IDATxw|MϹ#[fH"V*(j*RTkuUTmRU{ƌإfmȒ{$2nHB~+39yhGoA_7 !"+5j :f?,C^Bq̯ 2+d/}?)쯳C!/$^ք唇5ֲSNikB+h_P`929&0!(S*G9W- 997k!BB^wpC4PBjEB!DqRUhJLLLٷo߅I&o߾=e { SF`9P_+BI ThРAիWV:55dd2yݾ)B¦( FNCs=zL?t6҂#iAAn{  @կ9s漢l $!L& Zknܸ,K2G=Xf{_3Zhᛐ BSvqC#Hkaʐ!K˂[Vꫯ֭[`BBBR!%55*Uoذi `(hR 6|vI BL6664idѣGWwcs  /\U.=!%(DGG#_0O>mڴiҥKK B3@bb"qqqK\\ *...n5 $kA+fݐۥ%\gΜ9\rnPg!B!55xuRSSh4hZ5+WiŊۀxVM- ' T| !|a4h$..xH˹ xw1%B!s)))/%%;w]+++[^]5n]К۶!B_~ Zt:B d {dGY !>PBv,@A(C)ɏ$,$?H& _Mg!\OACOig!{j%_}'p3dGJhІ=[Po Svɫ_sP~3?G'g3>͑$Nr|*&K! ;3L&ON@@͛7ٮ]ؿ?#GDz0ٟL-xlvt6]ItP/`O'z+#&ӓْ)ێA&'m%P".h4Xd VZi’%Kӧ}B6y@`!r x|~.$]u:2g*O&iND{P2HY/C8iֽ:MoJX+Y׉C|]пmel31 xl8rKH0ZP#?~Êi,~xV ۀUW'9~֓+̙ eL]e苯&/r."kת4ԇOUNHyxs~`?WKђ2U:wrR ؗnlm-Y͎$7 j沎Țera `Y gײTn՟᝼gv lb.Woy*O kE2Eaɒ%tBFP4W!Kl6J![j>6AUg$/Ih-qiĽ9#Agki bٓf(0N:V`7ngQS.b֬Y|+]2xDd冫*eh6b.KWcGg׬&$r*ntxMsG=^ǢE_Z]+Ǐ#7y-v Bwg1OݾltuŬYS4v@EA֊79Ҝ6 +}- 9% KUlTؕȯDDuCٕ鳜O*{~cqH8q~}iJOSw ߁ o["NCw)Bo 4oޜMV5) ! >>SN zagjFcUͩWV= Of\ߖzڣMC\JUpѧT0X[S]G=q$;wԮIy;gZ>n~&TS!@yOsXaH4bK>`B/o,)gqQi6l2}:Ԭׄj((ht::ܼE "qZt+3n^ q}.1{g!N+@.GdsU! J|D&G7J묛.K쳟0o\Z(h!a2&C)DIK<{8OYhqD%:mZT@ATrF%\X79NY G׳L>dO_g;8ͥ{8ъ}j!ƭ! ]nmO"jYHWØvЅW~¯Ot'b,1qmDzZyw\SBc S@uʻF~|xj>}SײhNʞ1.턓m5tPc85uxW Yp[DIw#_ToW%| 'gPǦ. <, }tsL|K)[ށLWk ?Upŝ Z1eEFsQt+YƯy_v1)lM\ |sVӇFGH,.Nu}₻W=OHt慛卟SuRv:k/ȩI&(Ne [B 0+CIfj}&i[S;|hs$}K%ww*n˟nJJnʉu?ů{Ѭz9+P-/ߝN/)z-2~? +N__X`~W yՑT|u*{MîYv*l'֠+.n֤/wWS>fnGZ)悫yxL̟IŻ~;>;|֑yeM[^EVFt|L~ep`5ʹ粟gy,U {E_9  4Q77zסY,;/#z>psqbYレDww_ N/i&gfv4 4=_NƑkE#Ow*l+bChV W<u/N-g;tm荇5GCѴ,ǟNan9,jVz]a̼4v2pXrd|֛^\>f+o[>Z477=P݄< {z6| hx<~G7KD Ʉ3kĕs蓁]v?vC!S߼@)W ?U"6Yÿdgܳ?U[9%>9ߒ^PL񕮌ZHGyiK:Ly俞j1ۛf״%E>Y뭫FPsflghĐ`r.I"t1?&9oPMU:ܵ9煕N>R|8{ex2xh~6]Ay7)UH:w&nh/vw<-Śv$QY_5g;MāG8;S:"~㯼A*f@i7i2#5+LРLݞٗrNzbN,cҸYΏ #xnFD?x:UA}k^R$ySqA-Q?)dZrhpdS_V>ht(e>; Ëk7|נf)J{P)]}kybK_|M(cXZ/D;4,&5[]#t8+>a[s}އm̛Ml^zw6a;:DX<6f:sJꮀ&07ĝV̤g9-0cOۣAݸ9[-;MijEE 4omZU jsdňVV>ş)3hy~OLio o X:5jd Zsjk %G f nZ/Ie/ݬ_&l[iZN-Kr@u!Bc7`PӒRtjz5:0Z| hk ;&fư9h2ެuoU6^jz4*l,^;FjYfU1L yG%cku4WE5i54sf-k-drret17[ ,G=a\u/;Z Myk9ŜC@W'r>>d\PsB+cTkӠj,jlL3K7+'RW@ZW9Yz.;HŞ]G1UC-c󰴕c,#ufD;׺u4f;p!I8ժCysNjryu]Tp&{ؾw=jE?m4.VwPAZ:.4`wqVI=ߌC&gzo4֬){bﯗ $p }":BYZAS ZmŬv5o彎}to,igt/ںoB~o*uF\N9ăcޱTOM[5oQߞTW8| !{1fξ)[gl$fߑk \ _h:F! pX>ƿ&5jqecS&{ņ Ml>g]x QDN/Eю3^sJ8j ;{}I-6jifO$˷*Ƽkink5][Q f6p^N˭!)xfCJ}F.`75` )2UF-ݷ_7hŞXՐ4NJ/g/0  R7I:s g=7li76ہd@zbP1婜N0Z{ ~DB2/'գ#=F dvyic{9ϧŵ<_A8qBw;B;l[7Ưf C1y#Pm*ԢgK @ri Ε;#p{;U.5ۛ̍8y%w (fl]i ҹ+ON(8p% 3QTphӓ^bK| ESNt\c5F}]+snǷC13.:5Nοg'mf1a%^/zKF̫[R63i e`Cw WSiEu;a*ndP=C_A_a]^jQ [Y7OM*eOTSd΅ nO/>)X5cz4 uRBŸMXϲ*YG(x>/i}R(SoWBj-UjMcWn[FUVg7uݔjSBZ{5LT7݊R"7i:YjUJ-R^tWX7s^l+e,*'K7vTiD{hU 'սr=y5>Gm\IQ]Q߫^33[+-ӎNO}WKղ,\dr>5] R/F7ONe.Z6D=~;cT)N'QjVPp@R걏ULN>zF^ީR)cZX9U P|QLO:EW--; esK=bÿlWuj:"_Wg#ڨ*vZUk頖뢎Zz4jKKyl,TwzTuTZԅV+WSKYZvuNV߼7S7TPP 9uwՠjuW[Bg:S\yET>Vm_jӫvu՗F^V`ivel:7gT/vV]{a9w|ȭF[Wʿ,S,2WgU,naz⠶ofg)BQno7ԅo䲍P׾VA?~{.2w}UOգ}u<0q,wvʪNh-UJ 'yxUpTvWWikղT9~::t}X.zƔquQZjzoZ~RzUzةԲ{Va8WmRj׫֥f6aQսA>eTkFYکCFfߞΨڦjרZK{l+NQQM:j项+ ~O?^qࢤ l>ӤGEE)`E<+sgq-&GH ލS`5)A%œ%y4+7}.:e9~z[#NNNCä i2dnGQ9~{R.E%v??5A|ѪG0NS&s$f4 O:O /HO*!!~xia@@Q,J$^˼e' ^ f ރ2X=a{Jju_񇄄QiBQ6ܳCqe%BBTBH*RBFAomk<5`<Đ集CiRYiB°f֮][I #MVz7C<ᗎ}Vf8ʣB :u*KhFqBͨhO„v"c1]G]D!e֮][C Ł1y/ĝG]!!ڵvID,֏F5ux4W#B<]ҥK|Y(JOV˂B! ݓ,b vUSN #B|OVF<#V.O!BW,gٻc; ܾtmöX_m3 j oe6-BD~C9+R}Jh?fBh,lq~keɺ:.,۳z` B!/qqqٳ'2.*,899e˖ϓ;:ͿL5ԩ[!x4FKKKN:U$)`ϯ8B!(f}h4b2OVG!B: Bb BLLLqpp߿J$BsKSHHHF!QlN:88WVB!(D%BVRZ4 zk{\4Y&L4GMK^ߔbFE|}֘)쏵eK!V]ppp( Q<[F4MY}sZXO [Y-(B㵊ТhYQR=ڽ4b>:Wzr8B!JhGa$ɊozaCV}9=aށ *8Ȓ<B!JڲFqB&4 znU w#)CoRW;k*5YNꕵRoK%3Fձ}CϺ]xwi2SY7iV+zRx{/{.D&%zK7x[n)B5!V||>nV&ʢĄ [ X:5ZTp !U}wY,}Ef'Uy`yҞ:Z_RQ/-\7@ѡ7w){Q 4827C37Bi+lKW"`zT0EW_OXyPGV!Qz"kyzƔ?rR(&NChO5S`:Ybu+lʠɋU%l/GЪ<uBb F|?e홻w/y(@[ZVKQÙ|۬OqD.}ٗxӐ~I{8jxOI!x"#nCK{wmMg"zjي9K`̳('->>LjR"IrB'0XojTrĄIUAE dٽ{=W`B<uԄ[v) 6v]nt5WS۸h:yC44Sf=jfYP&IYBP0gؽe3w+Xz#1FPNӾÙEDw2ˇt)gǕ(W:tVY#ϞQIKۘ(Ɖ2wuyViB'YRͦS3GK93nGOasyR hmdjwޛz|Hvi}0,AԨ 8Nx7hP8V:៵X? !B\H 2OL5DO %**@srrϯ@!O`QUIQ 0;''af biYBQr=BQ$XB!D2DHH111"*B!U,BLLLO!!!BޘzNsҹtNC4M(7&ɗNS۴s)x9BG.C8889]t t}a0^ʲp-AVX3&FR0\8I!DXY0___9'aDm:YhZY.Ø1򧆱m(mz%B!SŠL+NbԨQXZZ1ۗ^ 7'.8".aOvõ7~g-@iB!WeAU5F&M5NQ+߸1MZKY0)r'ͥO:xcc\g?*W[n˧R嗟^ʏʮ["m/R R>]x#B!D*`֭[tޝ/2Zۃ%ʊ98kӘrzjrܸ_>N[!xk˂-ӦM#66'>tl<|#Gp`Z{ϏoC/j^ xW&2]CNʮ˔Vf%sY W!(N̚5s2wnUPl=Ve5uo SWNia劣tBmY«hUA!c;8j4fΜyCUOL,TELT70_OѭW==H 0[5xܒ"B⑜7oΐ!Cضm*82Dbnc|݊3{RM &26m⏟v-7EVijbUo-QՂ;62\>ԃR!x|<sy58{0 IDATy όz*Z@&acXe7#K65+Q42܃y,hi/ּB ٓ(`v>NNNCÀH42OҲP'݇$PB!2<φx,$|u|:P!KNZ3Rң.B)`!$$H!*`!&&'"*MvF.1o2dPSJ9^۾aβ ,^I!g@S1,=>߁L\8< !%Z rRFvOwQQ\mK/bGD{ [5Xbh4hb%V={awTl*({;̬νwftY݋jfs2&y%hP:-,ɲ1B!jzzUo?n(>&8[v8$눢9dp50>b癩T-FF8cS#Q/Cx]Ecnd!l#B!4i4XgBz('ӹ_# еĊرl0^=g[Ohq' $VB_&!ޤŵr jpM'Gt {J X6*bQv: M njPyCBs !a2MLiI-СC>z(AAAjN;YyDϜڹ7.nMB|aGɒ%QU5IQGœ\N@lORed!E:d%TY G B?7rgҤLA!I&,|8 YsC!H;R%X $$$$YyI !"R%X IvByKFOͯ[P*իB!H6 aggZU}pOghnBH UBO-om/,051:/DjZ2^MPb ΒuA4ON#af[) o9#|4/+F9MGo#lC{2fֈ9h!H^ǵ\tk1u`=:#\ZyS>G&[Һ qEx"6GU+/L?B!R\ 4Y3d}+?FpЭLwJ]ZՐYWh6J"*%AM'<|Nh9=1x,ɾ)8ޝA <ȠSyq\yڜ4X,>?ݡc e265NBɥ`L^{o乷鼄*W*9J|ܙss&!@Ԕ:Nt44H%]z;J_'2g؍fHoA w?)H,`̨ޡR!D0s̈́G9wmKx&NAQŒuOxoMܻ"# VAd7-Z3~N`kG눢%-bbîZڢ s?91]B!Ҭ,ŵr jùr1gb!'ZcǎLGV+mJ(]4{ѡamBz>QwW+ݚKr9G_QAnBwCOӤ{TLPBwOl3dS_7g3eRc rB~5_I-njoj3T+?Ӻ~H.-3M966&y͋]ЫhZ댩":L'Y!&Gb8FewmFVXmflc_|uVEׯg&歸 VӪKP,_ (/ߗSSšYXCH0! WL*,VB)@F& B:Gt:'y \&gԴ~!"5۷%Kj(۷ŋ'N 8I0yvpKO7(!;F?goR! Τs7C!JHHHHB-B!DRJ?000Z#BHi;;ԪJ!P]zW Hi>YB,i4XB!Dj"-nX9S{62`fnC%fs#>d3ʁlY|PF 7@R8X^(k4/WN05ލmPfB/XھuRETGM-C[{ƭٻ;LUh=.ͧcnAo^Ɛ gw~2Nc wχTBh%elC$+[bփ1ԙ9M INͪmj+!`!fO?< *m[6,He+̸ϑK/eҾqрWѸ2AZJi ^EWd\מ́MWb<ۛ΋7U{4(XٗJ94P;B!ħ!Lzxݙ/B(N;KYԲ +< &w4j -С Ζa#}{7E k8d+6,GOE5Z~}{o|[kMpCR!FYPlUYNCl"Q1@,lObFېJyl~<:Y6'UpR ͛Δ$7o_^UՒ|S蕓Xd 515ՈBeJ# gI¹qz'O)w?])|(gv0M^ȹB;O&>H'^M{"J; 3 B!Ki׾?~0пa6')7OԽKd\JZVf}Z9lLjOspb,[!zrRT- SضʕFIF# Qmpʠ Z!t qACd ^AAAӒZC}}Q0Nw!dɒ( xIGuz 6A'L2 BvB!%B!*$+>>>)"!B$U !!!S5_.-Lv\=v4>`,1y}J!gj=]#5A*N@<8Q)Ykrt2{B!5 a,gy)|Ɇ#6?jeFAcbsNʄm7d>y<2cG?ccAƾJY`Y~ ׌SBi4XbPoFM@u YΫ~+ps[˾U)dܼ 붠%.:Fc%GL(߲!4SB`!v??˅9vd)҈Q0ruaW*{bg9+cl[|m1БBO_zdT)JHox^#3VX;ƈ<{iwq<=ޏ=7`Z K7oe伱g=-C6mȸ4fw#ɻgF!DjHӋcúߛ_(: >[Ju7Jcw%bL@|ٰ7;Ѵ ]_wr(+Y_J5D%.δ#KF @b*7ƺb3l { ]`!j%loj |z&twGz8skK ]/e* *TB6c*^}:zkԣs;\-/czIya)Md Ӓ2(WT_>̑`;([sGqYbnh̹凍x!Ǐ @MH)i:XPlUYLla eS??nϠ n9R07b) F +3z8[FqtZfI>d$^ETcCc:V-yvY|*R߷Ї^+y.^LM.L adK8X,Bؐ#G,-6j00;w.Eړ2Gj~͠1 HI,XH"Ŏl S M]ͳze굠w^wz9p 8Foqԧ~cJNV؈n Ro V^](`4 dnOs䪆Ӻ-EyZ6=gś7XXpnzk&؅B|^`A|yxWƮ-\A۹?@Si: nKȠhʳ|WlkпoʎhE+O|U†eJ-gwycBT Ͽh2vڎC"W ,gB4ڶG^g2,tmN+O0b GlTߜ6~sI;GƸ-ozw &d4%iut!H}˺t[پ }Qr"r&vIy3uPnNÄWq̝+ӻեJzlʓm3ЀSi2`MUDHB]UKݻ" 3  rRzKf~DԕYP]܋I`KkN֑U2pM0ShZ܂?E'ҵU"OB/d n!a2MLiI-СC>z(AAAjN;YyrfkMoAF=>xBœ\N@lOReB:'.1xdq<;a2Uu@A!ҠDr;p%<Ě;^6cDY/!`A|b6T1S?~v!TR%X $$$y򵳳'Z$BJ`!$$$``GjLܝ.C#~Y}Yjߏ c2dw!FNn !H!%+}<#Lc#qoLO9f^h{ !bѨlיꅳ4:}ƿH*B|iwcea;tġĵ9ܣ'B|itd!c:03fm_650oM|.Fp `6)JGkL-Q6S`T"j&s: )v|~jC;s̬ݑw"= fn_76~Gy~.jA^xxg2/ڙBT ?иN6貗Ÿ=<2?B.i3XϜ9a<4~76]7ߟKg4аosF@}.R k=w&Ԩ#]3wKphk@!'KU)͝f+~޿nPyr-R.1/i?X7ghS8rRTB+m#3ԣ_wxA!ćJsx "7 Pޝ~/aMp߷x ks-siO, e_iQP!wŊNb욯YDσ!h\[9MH|TY7vy7΄ο:OdξPJBt7LCУ P"SγR>Sǒ1sy\{6F4^yloV:/ēV S3B!>@YPUUEIfB.7 b?$wRGv#}zڱ⩑hT8)(ɱϡWۓ'P-zNX頸ű*KyF ZfP>]U 'ȃǹs`aa6eq!wlg9z*UqoaJ~9"B$SY8SgdSkbjFT g$VTɉmF6{-^x[H*c8pMgfxRћ7Я =T ?u*KK[xӭFf4@aߩQECLoi-RU-YZg7^a i9B!>49yi5JL<x89mo?JGd~XPH>8]_x==w-(T$ϋı+hgɳ,uQ2lK&/dCpeʦDʨ[1iZ4^ `A\8u+!WZ+`4Y x߄/B!J IDATwv>, !irdo|#\/{M/bode 2,?4=dJ&ӛҿa_uCr>o/~kB$c18~eCQM4+?? }eTQЈdto^}SNgBjt8:(<ڳէ z2أ^ƒ^3Mfh6SԮ,٬"w6u;R5mƅB!>4ۃ(j}뚗SZԬߎA79z LgAܜݔ E}(^vX@Vk@qόm~;CWF)\)<GP 0> iV>%m%S۷@\dAZ38߼26i^7o`^Ț?N:3  rRzK ZrF;w Sضj^wo#_lɀY\ 'T !p 4A2%Cbѣ%a:oodB! ȑ#I`0`49w{K:p  >aJiB+NC!"uH BD4D`` !!!cggO H!I*BHHH;8l?zL" *g 6R~^8E^r6G]QqPlBƤs޿'FeՉkdRfB,ZhBI"55 ;Mۜӣ2q)~!v{wfFmNIpP |Qҧ|*Na类~4|`6|]NEo"{벅B ʕ+|{N)i:X ەѥ 5wƳlfJVP+f.ȃ~#:&^ߟ~qҊݷ9g_J^$PT)TU} Qx.Lɇ&mtmvE8hˑU 8|/4:YO}d*%te̼Ulܲug0G朇<16q;ȗ?mB!7>SuI6`A}?4_}z sϊyF BPgL7rZ3o j֡Y1 12΂9;)P7O(ih?d-#ކT8EB![{čg n:"a2MLiI-СC>z(AAAjN;YyB*V]ϧ#j׮wo>/_t݀cM SYN_!x~~~<[!i!B* BT $$$$YyI !"R%X IvBT \]6! 1rrKdF!D QT(JcH!BiB!l2x.BDI BDI BDI BDI BDI BDI BDI BDI BD/\@XXXؐ;whB!>B aaa⨘;Uc1+C|215?o_wP'!_KV`Q'YvƧõ4̝fob"}drKװu"`^giBO$ YP ;Mʑ/ \Y{/L pEv'K>; |Ւ!koaB!4ħlf@Cф[8p;_|q ?w3BD}# {3UUAv2v?_>ÿT[F\\)ߟE"^)'F*O,.dtFD> `lΆKLBxѹsMQȍ/W\YF>}je>Q= Ջ{ L:(gKiBeaoNMW4&t!{<])/38ѽ(s>qȠӡ9Qe% ry}P&62zP`V^z8m%gs%s.h 0cj#3B[z)=LhW|YqFXy'`B/\7g[s%[i{-N%:1;"ϳƖ\{co&6 9lQ'Q4lº̓(R %ܵO\ Gi(MN#lQ}^_Di=|3'R2;b/M{w [©8؍Tʼn"rE)L9);[@ Ϯ&hU[4jHI-Ȭ3%"~܇mypk7?;FS6+nۀʑѭYw գQo[684c!xFp/ȑYGfpV10N~ؑ_N㧹(KadwU! L-8"|߂mSCk^u*e)fz-`|+\k+B*1~Z8+@䊤rO7攫Dq~=^ND1Xt#(E;:E%eVPV5)$V.G eQf3=KP֍jeGklf%^]z.dzvaxDT!y<=_r&) P¿\$jVO10peWd*!թNwUpEC<|MrEa  'B/B3%kt[Tb\}{dVi4écgPLYrvы*[ w';Tl9On|w^R+eop.Xb !DZxN݅Q vw/AO~ Z_Eq{W'T/BԞ-9ɶ]ކ-ػ !Ƿi!WqM S44Ҡ`0V%<8;TlT .Ϡc뉜wEشj&_y%yG*{سyCUQ*(o+.!H!XE$ R0-Gv0efށr3ԻN!2`-.+O+p9 {{玽_8&hT.Xr6FI;Gݾ6r߫.%typ WHL֍F-q1K/eymZՈП?iCAoCe|x&oƄSXZ@h 58ˇ"g8+^$w\_ ,[0 mN|Bj][4==)T WPh<F "&9{/DB!_~|hvM`!3Ka%W7C[F߿٣N`.qt(Z7r VeRЊI Y a΂&؃m4Ȧ!OIQ_1tku$W><,7"y4%0F_; e~-Mu{% R=J5sR-{Ssv5Cp/? 8jIeɓ;A[g:+*gKv{GސUݱ9`oɦɛqپ2qm?tƄB Z7|67EZؓ5Yz^]$S&v6&. KFkYwfXXa-G,B˺OA(<9oa ez8)$#X%}5:6relZD6wM?rSbV9fk>6T2w2㫆LpP_TZȑ̘Н?GcjEdlu Mbj%<6#?#+V|7*f6:}ȑ^7@<ߊ#)^應?XUù̀}|[_?B!? & B/]ijgIH.rJV/KQl}B!D= f/BI Bd=0}v!)CFB( B( B( B( B( B( B( B(y(B|˗/ʓ'O>wSpssKtp~^yJ-Kۊt&BR;PD EI7$~(^Ou@dx~#ERߖ Bש?_,-C&O@oCɑ#GJ5󋑔Nͨ),ܾs9\ez+w9ilt#%PY )ܙ3̔92WBCC_~|۠15!?S+?ͣi Hh07J>uJ),!GP㯀K^Ep):h圅x~ ۹ Xq֭$E%S[A#KI9HQQ,ZшDGi)**6EOɔ#1,!GHJǘ5kVX% O%::=;Rկ [6mh4<}ɓhab:@=e`v"nfZec\tJz }EX1ƮIin= B,,T\cw^İ5;o6KY19yVUͿF?;-Dzc"MU& N6SP\{+w7 -~90/. gxd%wWX%!2F Ěxߏ1?P:t,X 111t֎e |SFGh7,(yzc96 fM|_2݋ߠfYzK(a1;cTNJ0f,\+%XBT*y*r;  bXbpEw%iLzڡ<:Čұ;W%#\ܿ˙DŽphܾozlZ ym#7S0_A]8R;~^ZN%]JO`/&&ΝZSͿn ,U "|z,A|1g ;v4VOY.BLɱ #$edh4ҧSg>!z@ N,BY<ip/RtM7/D du?dl\L:7ũVf@9(yf̕cYTKD?'VےQ*)w2FM{i>x)]Ջ$iOѯ0|Q"jp*5*oV([˺gF&PCן^19RaA,!))7ݺq6vDq+Ymӑ& &&S7ƸA!8J~6Gp?LKzHb\_ƚt9;>ƠӨQ&ύ9M4LOƌ;AFTO? Awc4A\K)U+ug6 xb[N )`0"wC!GxG|lxΦ[Y6W7)`0md"""D׿_AF8M ~_֭+#jPU?LO F-`gt0ӭ()R h:r,:K޼%QNN#$muEɠqk r5~XZ)~dJI,XPI)!O}6P>gѕO&/"Hf kvZMPPk-Q̄m#oG :JuL ,Hq5hAB.CڴL2r1o(N@׎T1)Fƹ8ōH:!٨ߴ,/Z`}LjG竆C>)>_!#Jٳg3ĿRuKJUK^On_yZ6meo)ϒ2&~WKUB)憽&9YQr9YRZ=ňW/_LC}53$O] 8jA۲5g}Mֻ^.&>G[KEQ)<޿ugm"+{TWwEo0{92p=[g Eua)[Qzh&Uܮue*&Gfކ꾐>-KCɏ_EV7]:ea.xȂ BVϓ'vDjiؤ1=ٙ]0KwLЪ*逷r˜[(2isWB)ORqQ-΀9?<773Vq󪌣p]}3Xx>ݑ)9gn4 ҠasBB9|bΝzcFAp=-17 אXײ1+&2`#MZ<KZ^,~4>Ih5fs4h#=a DчPRp2@24hM~X+" ڶmq t:qppx1հ`\5P?f*4 7oJ*oU\5DFFNLL =V3q,Q4 bfn%VV֘tݩDV/yt:]7 S,`,B!{ŋkRɷZ}\$I# F'x&Fk  zΝzc0PSSLLM111Aբ(';js}|i !DLIDAT:e<__ujjae5j t=K)iP+^-xQPBmnL&ma!ی0'{ٮkw3<ˎvl!Z\&:rt*Wsxv[pŔej>osyPU?gۃ٣N+!x.'2ĤB/=|ﲻ,\ @XRIrhvfH3 V}A12˩5,T~c}=  pzOl/PZ )S~e +C mDۚ]5vC=Νkum"5D궭ډsg,ܓI%X![k2h{7/$چ_H8 Cen;2SfB3 |a*F pJc 5;gvRnR |!}_Hh;h(H Ҥ[6$78̃RN=BE2i[|/F!mWvI9 ])Hm/45!YSmل67Mhcn; mעaAjLiqfHyjlPul7h.i>:$Kt[pۡ P_J˿*IENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-grouplist-add.png000066400000000000000000000563251501654372000223560ustar00rootroot00000000000000PNG  IHDR*` 4%sBIT|dtEXtSoftwaregnome-screenshot>*tEXtCreation TimeSa 22 Okt 2022 12:27:31 CESTӴG IDATxw|M׹f'B[mأFTծمEU[سjowdK$#Cq{==SRLz;BGM徚όN'؟I$J~FC+B)`Jx;w_KlPuIL+xR{Lz;v!ގ$#B*sɭCIpiQl0wJn$st<.| )Se' +%_zBJ} +!L[f1mG]R n'HvŸIo !GJJ!ЊJڢJ+\`lX%$%ěV*!!q`ŅT°"׿UJ!R0%o 89`hI>HH( %mUցnQ@0ah%]F\`V)UJ4|ժU_Zja+++sVQ!rUU1 ƈC];vCna$TUh4y666{? `(%)6m-Pl˖-Sj׮]ӧLB4)5w>פI?ĴH%MiӆlٲӧOܻBh\]]=nztuf+i\r]v}-!%xYԨQSNBIܲJJ!fhAr!4_vmQ\dwOE!((~"E.`A#`Ln`fҁf͚5%ȁz=BHHaaaDDD3,UUquuuRJu7R0w/ݽnSN7o^&"::p"##Oxd/**h4 Z6]5 ͛qŊ;pR?U'h^Uǧa!i ',, k aaa{*U*@,) *?%q3YZYY٤{ &-44 Ehhh^kiiiC=(IHUeMoNa3^OWJHTɶHTiUOHZFۜ>߷#HjI%oْRՕP 'GDrN/盉?s˯ɋHq1w)=ҲfJrQV 4BlL'qn4ػw ݻ~!ٰ2 O*K}Tin\JjUxbv޽{Yx1>>>h4ɷeұZOfHnf8RVl;ʥ{lqtM~R'+l] .(ږ.*n笅l6aū :rcN \y)۝m`ƻ_'/q~ O oud$LZVZ,^Nŋԩ{eJ5^K@jCDv>/6h2*.X{Up1f#!$wC132.>0Vʃ;Yv%vZž\(9g#@geO.Hµ8HZ?V2I;suC0a{X_r,?}+AcCQ~>s2J%@ŝ&~aOC堀bF~b.gr Ii:-% R˚4[T-@dc뗸[77fNM5(eV6زYfy R3^^.hNKxh%(t2lMh4Iu)~OקRU)NOUNcWХףOP:1UXGZm C*a0V'$} I"1mր|b.'dt.a cls4 bfNO>PG&MlOi6|966i\quu LSJ9o9/Aӥ+Rl7RJ.=/077eWb$) 9Ea|[!͉HTVUGS@8B3330 DEEa0"H777ѽRq-7(QvBipBdyTB,O'!3GZTB,/VMH5k=Nӯ)BT+VBI"˓BdyTB,OJIP !< *!D'A%$Y"˓BdyTB,OJeѠ2pcl7o{[˖'jlY4;Mnu|sfVޓ{{XqF%_̖IAv>jSnxMK8~ `pOͯ\^P2M?7aTȭ# LY";˔^mxtr{zSh\LeQ[>xExPl#z8ȣ{sX9ϋG~U܋6>`akwqtȘ#/pa'+nyQWlVрB'N5=Ѹj xᖧ&G َ<,JVCY|2\õ4(ƠϛGPѣ&;\=S4_̺TjJ;Jٍ?н> x(>'r}W۝ɻe~jW#&Ҿ-BaBΔKRp&|1y%fH>jN_R"UBvM7vp,xz`b3 [|;y8r>b9^nGUGO{33ċ$G[׍ 1} e \_9W_sfι|̏5`"܊5[0T}3a.j>*#Ҍ3rVL#-'G0Ew6;c(a.| w,]ݱi<:&vȊ)AӅ)/{:;߭M8eAO5^u@wMֱ(*UI@>k3stkǛC/tLٮ+Qx-Syۭ8_ܽ份ᬾ+f3pmdV>MW2DKIJ;4[٣Ȕ:-^cN5whǴC]^77գېMTVM< ե?*Bgu=ە(k]ϑKz+Żz.IF 23݌ǖgszw0AE \Íh#Ϝ^E~h<<}ɼQA}p|]g>vdq}QCNdx{pBH"I+Id ۵mFf}(߬K^f-&*Uc)4O8􋦢ZtMeo3hdRJ!>w]ȱ'f}O9w-w^lĠ7,_hТ':h4h5UF>0y!J2x\wBa}nʅDoh:u)}q.U 7%LE)?&шQt([R˽3W`h~'Vn>꭬)!)wg8uS#X%%Hp$0# ni<~7{FX,qrEƕKw١ `C\UQ;i|ZRlQ8l_N~û}ܪ&eK\‰0bKdҢҟQv%sۡЯ?ې֋~|XgqIg뜏*O֥1C#]іh4n=UwN>H_Z/>1;a7z;`xx tAҔdY).xV}8Dn#LdG ^m[.9o#}ɳM)duS+Ƭr/ A~u ,}ka `Wfl?m R75Q'BΏS Ѫb>463e^ޛCIh)eMaTǁ}G A"(Ժŋ"pflAA[qskq2bˊ۫W%bq[M 50?_VEkqX¯qSTK'ŀT(Gjx~=̩ ;έJh)i8gO߲W~M"DX=ӕ(j8fۇyOX.E)~4ø/ h?kIטݞ̫ gfb4BLS[~YɘqaEkڇ)h~QfMJ͙+>(;,/7Ÿ Nސmg)<5oԮ `͗pK݆Yr*?~gz`|ƴ7#b'-_#nE*Q!fAf{1na`i*GD&SWb+d`g4 .LGeuxx{9u ތ?sO1p>$/I >Š7.ʓ3%wDL )!r d|x˧X7 1QpNR}\QZɿl/%6ltXNP'b)<ٻ1UPb\ؼ{<JP[%\L2)q7VT@ܧ8|~#l\nn8Bƫ(mn{<9u)ʓ_/9ߋwBdc[hiL&֢cA9Ǽhڴ(%65la8n[RJD~o ϏLST/p]͹hZ@HSyg ?95+Y|2ɔeL(yjPp$ϒiӌU[6帵v sw!DM:?SeŤ2"qkҨA~B6fZkFUAU^[jd)S2EIuh27JL FGI__ <#ŗ 'c;6BZ"爈Ki̒)w8|p Ǐ'000CHŊo8t fhB T^2/Y~ e)YtYFI#'Kٛ3"ST#5K{q)LPЬzɷBd !r.)'ȹp"3X*gl_3-ױR’lu-3l"L3=9flz[*h]cN,!kfA*L?i(A%DvbAFz=R4hdًQň3/ 3b':L.("1YšqM(t !aA棬OTs,-ԧᄫ`/ ,eQS=3%Kyb8`>-G0{|BdG&ݢRpp.%li\(Q4 A|ۍZQ! (j|ߏomdA}jHKKOX݆/+i[tg~XҴ߸9:::} ϓ2[:SKP؃iz0-CkBd5&S8OsI"˓BdyR8OIh\WFV?Fvo{RS}ėwMmB+l*RKW DJd ?zlW7F:dR?kV3K5oL}M؜1 _~=ݤvYγٜ[I{9:o8M-Zp"^BlIJIcʘ= G!&Z=!qDἈ )٢hl<3߶!ydOKM4 8(8f7eBdHjQbH+ IG쿛c PvK?sX*FE>.{ IDAT4mTs^HT,'5Z]h5*ٟfEǯJ/2oEԞx Hs:3=Ӫ[Ԫ-*%b6 +*fTR(|VŬh4lG?J3jRZtFvݶs#yL:<,WrZ65}@,lJXpxuً{/SV F6iOWćٿ|b>aώzS!2-xcAj_s`YMRQUI;Mgh/L#jY>[UC[ھ?św{,5t[Cr߫#_/ξ3s#S(]]*¢tf#P!=Gsz4j*pO1dVF ZB %lW59ǧ G9J׼UC<o ci++,L 3"3ѠB$6]]ǘɛk"{'l~{T*ΠEIO-0MF[{/Btٲ/X}> ڳ1wo=#i$R f0ÌUpU r)Iq Af\!o eà2pi2 >ȭ [)U*OeAŪ~J/9dI?4ow2nc ˆ~ ju^"żd}xFp r3 խGd:hS\qpNԵȆAq풫Ll'%WDalr:#}ddàO)87/dKG+AR:B)I]P7DRZ-(`4eoI7ԠKn #c$fwdhGpq}>%?efK'#uh%Qk@74/I<<(Yg{Wl9ڔvF\&Tѧgݙ Rp..MiWCyY=F {]w[KMpV2}Bh*PJժQvC e/R&{&zt]Bn43ʩ05p`]2# `Ec:4v?9..w+R 3K ueeOM ;ZbaIXwIqLE'i6Zej|P.ٗeۡ( iܦeڀ4]ʻ_n w2>,kG#:է~_y%㾦C08:^'"DbA??ՈѨ֍ͣGTv4mKBW+RgfjJ}&H.֐iܩԴ) =oFJyP iwiP2 gƆ/9ْ4B&맫2?O䩓ٳ+Ϩ4tU8QXa4O(GU+8؜l_,;Twƹӗgp^!rnQ)6)V4y4eT,/XX,V8xdl}21DNHI^` '8DX(VNs+YyΝj]%2 Vv=i 0t#b ]ԚږbZ0:lƦ^NszNXEV̨a:S۷ۜ+2,;E~ !2ѠN;fѮTT9֎)[ʐhmO2} NkY B5/xVQlޚ߂1w)B󛏩b !y DJh V1{"+yY3X"h_QBJ${uC(k;BdWcY7#V!!Y^.>mһ۳&3roLٓ>cPH &Fdy?/ư䲎\i4rYL 8c'c*g0Y9ZbfnK k2WzaڴAg+EIaj*"[aj&>mZ3tTz$n㜡wC݅cu'BcD;}3@F)3oH,NQRP5eINnW [RSSvwlmm)TVVi`h4r…L *M]>KBiz_y7p}}U}Kqm5b;;c4>t@"^7#ܿi-(Y1`ys{SzW)B\7'&.?`#ψo`@|ڷȫ 3haK= Ș˅gAVaqN֌' >b芇~wu*wgwNVC-Ъo]? $&"- RV˷xpN|6/f,:6 W^%` OkU~j!r *zrR"O.? Uk%igդ;y֟N#-wkA^)!< (.OW2g,Gr` _95%Wŷ{?ܥiGSZQ:!LA+c~ :\%x_zx{ w6[K9"!%Ia=4F[>s_:4q>!LIQ< m'!DFHN!/ӿ^Ҥ a۲<ߖεeRaA_8|akkG>_7mɓo,5_Jǘ>"[[xZ-i@ lbxMX8]c75QcJFfۉNNV`9eeAp^HJs3GĶ֦'xt*Z[Gn?}BYF3>At_ȁ+T*6-crtZ/$*p^?KBiokN>BEۘՐ4l槥7c;.`cNi;Vē@\XW:73i#`Ң2pGObO!w랴p8ï `nsN4 LlG,{iaw++LFE?xw}̹>hDAllQ4<3gg?ͳ'ZheAϫ7cB\_]z#ENvPŋ+uWvߜ8qtl- ğgK&ab $X5&Wɦ@>)ſ]΀T4l!<(#",һ7`o=źD]9'+1sR/K~|Lm_򉎴6Ř=c!Xpƿ:Zjha56ֱi\^iANyc[[ BTi7>m-]LSʚ,Db-y3-|ߥyQ<[X_#w QukJma;语&ulDRByPPg0G5 +.؝‘Oq8sJW 3w5(74ḳMsSHἬpU+N7zRBdi9vDn%+ιӢCm7C~^]ٯ8 !D:H֛bəw.9Ybii5%$*? P0ɇe+vlS_Jq>5Jޣ9[q/lV.чXR(:33P 2!DJLE6+J/WOrΛŋ?!.P,o{>SrRrؚd/>m̤Xՙu㫬G7D zcC >ߺ`46Mk{g@9ΫegX_fŲ#/Jӥ-4!ތT[(vYؓϧlOLCȵ}iPVYUO) KGg$_w+%jԠ;hܦ7~c`;jZP :3R4XQ},947]ų\i2o[qiFg_;nZވ9)FtiMrI Z sܔyo8n$h1Jccne.w2dݝj1CօqDӕ+W˗/O.]ի\~=ӶǴFQ8mAHu7 e.-/gvay E0' gy+k ~:}KRϾ*5$u^S"մ [ o GJg&M`R&3[=jv}"owʓHp[;A5'XpG(ɭ(MϚ!yPdM⩉d?Ea߳AA,w37:3bMə=Y-5j@UU/T䦸ߟ)cAv<@I-cTiVT_Ɂzľ<74C%SW q/:6qD.oe8їI.r2V~ͬcԜKn @jU8p:[-Yz2c1o= IG쿛c PvK?sX*$ߴmؓN5mux+l_rN:Uy{)psC<1ya\?b l&HaSg>\+IYzt__ >??NrafT911!/y \G4{$0T\ 8ӷ_i7sRݩ放\9; |bB @qn h6DJ-$*<3]Vpۻ=ޱLtg_#W*HO"U_9± {j7Ms3<{N?l@2^& 煲e}(ͨ)KiQwۚRͱ092nRgnBw v$[Erz?VZQN%\5Y1*)6/O]e j|^>c[u~LY8Z]҇3c1Lv]PL:dql:Q`,I-I@򔳁r?.;&Q&ZRbIv+@|i|@<3ߪXOس8FޔsH nm?՜Yg<&):L(dUj#uKL.zp4f ՙ*wͮ CPk\W:NjDFl^Ȼ{Q|3rA TZ-G\]źhS*}c`XَZ\}1:t+@ɝk|v<4Ƅk sFo^-*Ni֬Y,Y.&-ӣ!U+UxZ<^`V?֬p2*k CJ^f-iؠ Bu(춙Sn~Ps>ٻmɜ kT#uz3pC5AS;mG|>7˦৮TCbi?q3̞4mؖoF1 wLۊFr Y~~~7oNp|!XMKeIDATnWi1۷S^ŵZRI߿_ S`҆U|X<ήcؠiFR&/ة7%{Q aڢܸDE4M74L#A%^3[Ιo{;Dv"Y^6.g'}ƴI&v=? eJdNQ%S2"g2~P!H0!< *!D'A%$Y"˓BdyTB,OJIP !L?ylmm)VXfleJP.fb4U^PUk(͘oSPsK6:>q:/ &Dr2m>CK<ͪy3#B жXq_| c/OCgFYϦC7x{XqF و Q`tڔ*聫'^ekzΙ@vPtz.p8!,_S 6XoߜQ!g8o.֧Nތ3hB,ߢ_;󸨪̰*"l n.nh^XVhW34-35}MELTD}0XLB~ޙ9ۜ3s=hO7V3{xoBY,{as94&^yx2|&_^(RO, n{c{JllK8Yw7>H[ѵ2M3 ywLX? Q%˟{k?꺹RO-;614̓{7^!L$'5\pnI̯,2ZĽݙ88mt| / 8.8W X !n|꽌@Qo-Wj֡Q,ߗ`*' jTq݋Ɲwz~*M南U /ΘƥX^M8]<%$nQlw}.n>_ٲ5:c]7tVxp(xa^8|/9kijͭXY5v^IܮwĪOB2.ި/EU3n~{aJ!fx0vl:+R_9әR+}Q珃רu|2j$l\]e̿׃y˿ol^*[Rg_$-~%q3G9f|? n'9S'AioX2.aW/7ư\G۴tΕ49WJA 0W_ftjN2!hy: wHU,Ns.v_CϢؔ]o1U:Iאռ$>Bk{i.d)^IS'ZΧ} 'T͗Ou{bqnI+ё$ѹR*P{ҏvr(#0[};UڰKct_ H[&9EMGHX:yϯplrbױ2փɻalhE#3ѢCu\">cz*ͪ9zYK2~H%Hnt 4+@O%;Nl?{+G{kw ke=ojI ϵ;w<'k<:,t[gO^FԅVpCé`7 gOPdgLBp6)ks`l}ic3Tmݏ.=\% fl϶4G]99d8's9խnnnQ*spa='UJ6LH^u=  uYd܁GHN<_%x0E_`@rXq #sTF}!c~!btԝ0fD4qr+ۿ[znh ( X; gQW+(sA&ul/Gc@+AE^}iɝ֞aP~a۸Ӽ/O5ud9U]4,m_W iN Cf>O ]mRU4j|0yVPoSlTKϏͼ/R\PTYO5N<=5~PX>x c Ҟ[C;tlRu>]-FBÅ2jP3EsE3l\ v/$@ŀy9z6бmp_BuYQMŽ]NnȪhOFuc+oZUa Ln#[4o*xB*yh|+m`HSjXeOϭԥi&*X}DPck<DŽtwBJƅfE4Ax<և*ij6FҮŖwGgfO76N8?XBuA԰ !$ceR*WSf  kGt b^h[4 ]KoVĬeܷY4r=5n4}ߜJKL?NT^|9a}Kw$k+ɰG9.!T Ue _Ĉ8gx?ye \-4㑟*`ܟsY3~ +=h:]C:2Y5Q$1 z4n*EbI VMlKjKwW_S8].-.jf|>>Ubvi7'L>ׯr @ T`@ _C8* G!{@ !T#J TxP  *@PB%*Hm&YlbOBKdʸfHYY|Ft(dgArk͙i rR֩S֖dmR<ǏS:{jMOXvO'wvFMqBUcڇx(W Ǹ>nbؾ;BJzX6\c)oKg{\ܼhk˶\&]/_HZ9c:.sqΗ1JHļ|1k}=å&/MT\]fdX[[/Qfq{BFNWq!I9X`cl%߃GP^dgg3u;e w>*knh'ЗEg=6ǿ_NJ6s洣_(|9Nbk\ϢſPUrL&M=ٙX4ל~q8m66Q@r85G\io8> - yv2\K$SOP"WDһ(l 8[kpfe~u׶ٸi5 Nr*Orrr4i;3@A_m$ɢXVٜz{KNbrv浭V4 {oDv66UP3/ؾ>[1mi>2c4]`ecͭ١(ܒd>`t&|1cǢʽ-%ּ:1ޜ<wR|5gaTtw9~>_MCU$n%P~pʜxY|gN8ҙxp0c)8ø# oǝrĵcepW^^X6ad}1Ji3~|Z/'c!D^jOTjyY5mNMU%Jeb2utdj6ƍKCI=AMVW)= ^R,>qXZKc,Emru2N;?p7}9I?y :տ.oQs(c197\5)1#"-[ܿ̕*7 Ŵƶ)r2uT%/Efs]S:*A "T/OL@b2UIUvj|{0,,'N@,\ʝ45m2r#3?^,T>]DtJ5Q$5 ׅe8rnJZuLKʒ[I2K~R,ˠ?n]3)q8yw(Ҷ-dVxw~)Τ(b֯S,L%޸=2|x)d*_dffj=.s̯Nq?;Z1ڵ *^[d]^{:YIA2RNMI|!Or9WQ򩅽:/$Y0uG^\tb)+;s'.1q BBBףi>dlfՑ<+0rbr%ץ*) h>a-Og}?Q >K (BHII!5-5gqGKh׹3n~ܼrg+kjlQ#gC(^7pihzQ+lj̢i sGKƭ$]{LbLR= M׺q2h*72 anc&[Ϭ!t_ 2Ƭ,kyN#77gv3* Z۷+KK41*ALvF~S4#‘:M:1Ï˧0.zb;e/2`L]Z:FɌ OԀE& 5OJJzl%vMXXX^ж> 899w?}DJbهk3+*;vЭ[7^{E+y\MEKff&ض5uVARIXXXX`ie Umh*,Hd?888LNzIr5JBRP!)+8ܺukeIHX(j'''^Q0ɢnwQ5V :Sf1nl:IPkXh,XXhPHT.m_I1~1xxxCuMkjZ%Uٶ>^5]Ln[ /C)_ZJT%>x>T&gyBE w|}.{OV_KP TGT*dY.Rl^,$b\MBReu+uƨh(.JXUPx" 66Yx"%iTӑgZ2! ;Фv0@iSZɛ$ m󤤤5{Ǖ+WHIIWƉ́x{{r$N_OzViVיԵcsppϣY?-Eg NSH ϓ¿Ѐ'݅L%*@`nLEUBf!RL1X S4Ee)c"U̢@ xRAŨd$卡H*% ˚4&VeWPAq(.VE-*c+15 C2ܖfYJzX0ݰqx$)tEXtCreation TimeSa 13 Mr 2021 14:13:33 CET IDATxw\׽,QQQp"[DGiB--̕[SrYijjhhiܘYj.@p(e\0}?{~~?sH61duy€ d[D"""""""""""_DNF+h̟?Opʛ`lCDDDDDDDDDDDf3qq[w\>~ĸ7on̖֑ X|wzz6&&&xf'""""""""""" qpK[?qb8p ThqLL."""""""""""Dll,QѸv̛7$$G ܒ-O7(Xɍ75BDDDDDDDDDDDR0ܸyBߘҩJժQcl.$`0`0:tpc֫H#*\lll(I!"""""""""""1x,)oid2e>,y\)8XRDdt(y)XRJ5퓈ȣxڙp1orf2S8Q!"""""""""""B&:F/7[SBmgMDc<!&:D9B>ܢ%'2L HڹD[T`f`H`NHN8<] E!7"""""xn\37_\t,)x#s;:I+$Ff|d6{# @ϐn~8!)I()!w)Q!""""80S؀J^.$ wbS|E7H!\jHJbC1/Сriz6+]!4kIm7aC\7FoH(]'hqKGvUѡIg<Eog2~6\wuH3ʴMQ$$+s\ r ImM3q96{VPBDDDDQgȇV{}=HG. wb,D7r` HlrHDUkS>[׹arjUs|$7nő×$L~M tl_>sێ8",>e,~hp/!K&|VBDDDD$٤DԶ50n.~sɾDy N7_gk۞.wX`?Ϻ}t*4_OҾXÁhۦ;oltf )2/uCf~k< 8ZA-(!"""""yٔD9{=t ϽeJPր);M)k3nu`ʉW0-36bfbSM>Drb|U-%: 5k[ O{B5)~JfŽBө+y CbM9a!<[;˿$c0$|II`0c.b] {=|PuZ \]5go""""" r>ʹ$z~~ ]2Nq.Uز?}}1)Iv2{n%ߍ#ytoM5+e%MneGv}VBDDDDgs _4w=MBqt/=8 M,iw(FU|=(h7tˍrp7`6>ODDmܾ6sfk5&--""""3]dWYwEّ?_,\ #sf׋u6P"r+vV!qF> gպTi=4$qٛZtXիkXGDSf?.l:KDDDDDr ( lw`Q9rIDDDDDDDDDD1#b<4Pi10$#wʧDJT<և;-;&)w۔CCxˎetOi'ǜ(lg&!YoJZOS?EX#c7r"-9G\D%*DDDDDDDDDDD$(Q!"""""""""""F 5JTH`֌CDDDDDDDDDDDC'*~>rĚq#բrIDDDDDDDDDDDr"""""""""""k\ U~5K~#.c}+ȅDvVƱ7 0˷q*sc%f@;\$'mVHT{Q zOX&H^BQě~4Wu[qʖ!kͨ|ޫ.)3ϦQR};VپWQ7ySǡnݍV &n\#\>$nr(\ƨ6rrVKs e`v,""""""""""O7֩GzԭR G8<EN40yE-+BɪO}*МKQn=7lFØv;ȬLq+9g>~3KYԗOΗ( /Oop0z \8O/S5 7?71٦T.Q"^e~<%otoBE,Sv?כiLdaħiZŋxQ՚=˔Qר0G\O|<&70scX:,ݰrhJ/Uҫ/]Ɖk ycD$Qa`;(E ~=EDh<'m6@jVnoq9s{1/[*_P98?t0ާ7IQ_4t s7'kWu~BvmsCOد']KHV(aiDU c-蒵EDDDDDDDDDgD(Y}نb*)tpwp85,KQ7}=v)._C.p點G9U)Km pM՚J6%>1c6m V,E^RJ{hcfR;Z䬘& 3m9Vaogd'ʦ1P2Gp +FMpˆ}J2EwrKSt.$k?[gڻSm!77*T) o24+\O1G)$S@P0|81݆b4w$:> {@n _a[n]{ҭv~ O aL_zryB&o^!""""""""""r >_~~{?(l:?NVzvB'l °\`sX8QF,j1}̻/}p'x3¯Yc,~'O=՝ӿ O lDrWLƮٔY6""""""""""")Y:?X5o1{m1 ܎GDDDDDDDDDD* *#*DDDDDDDDDDD䱥DM$"""""""""""NS?C҂.]f"""""""""""8Qc8DDDDDDDDDDD1dqd2Y3y i 5JTHx'l8DDDDDDDDDDD1"""""""""""k\cOe"##O2!CP@\a 퓖0>֭[ٻw/'O֭[K hѢjբ`V#<<ӧOw^;N ݻ/:/]… Y|9/_~`=o޼888зo_͛xDDDDDDDDD,e;wdugy}`;w$=dС :>S1֭cȐ!Z*$۷Yv-CeCLL VbРAX'$$$5.^4hWEFFe~7mĕ+W~|2~m2Ga֭DEEY=իW'%)FWd˳?#DDDDDDDDёS?eFDDSLȁryNL?__t/DƲn:y(Umۖu;wطo_};v?ܹsеk,a̛7/f3GeС={QFe(0,鯐pa}̛̙77oNqwwgɒ%椲f%KШQ#^ʪUppp_W^!O<>letO""""""""",%*O OOuɊСCFU$9p'O&>>?ƍnݺ).Ӻuk5k8q"d21c iܸqb0L]6SI"##;w.eʔk׮VJrΝ;7olٲٜ-ptԿ3guԡEV{,\0iT=zDDDDDDDDDf'*ߍAI%i>cƌ>) IɊS&/3Vaaa,[?f̘_aÆ̘1QF~/>*WE>$EݺuKdd$ח5jdv?x=M (p_BA}'磢puu{N!CrX/c YQqq5ɧ92dHF$.""{HyIɈ{GVd#G~z(S &LHJR TC l­[hڴ)uիG`` ˗/lٲ%ÉӧO}LݦMƏ=z\|m۶qi_I!^zq.P`49}4w)ӳgll6sIvEDD4jԈra0u_{&""""""""ӲH_*+/ &HxOwax߾}I;v숭mƺ=۷gݻ7S1dfqz1}$YbEK.o߾G*QaI_yb(T} (ESnnn 8x $<<<`#I\~:u(Cr6lO>d?_%LDDDDDDDD$Yz;[dugNNN,x'XCVd5ĻNcƌrBŋ9 '7o`0p͛}QD f3\v-Co׮;vonnnX tOŊo߾iӦlܸ1CL:{/l0dȐlp}aA5,,ӧOw߱d’+P .}믿fРA)׏-[RlY\]]3W_}EPPPh"ڶmƹcy9995x'1+;9x`K$^M3>5gEFF`kk$$H^_Fp:uT[nͼyJM6L0!)Iq)^}U:t_OP,0Z%9W,!q-kzWݻ7gNHwڵkӰaBCC5kzbĈY?NJRԯ_ÇS^=_G}EDDDDDDDDY,X2ǒ ִ:ܼyx"##qttpΗ/_ o[ݺuuZ*.]"883}#)Əj$;d聴d%ɑ<(U2rHʗ/Nn͚5i>ホ[ϻsv?Ú5kXtic;p@sR\9N8AM&""""""""R"]D;SdtGajM*T1L:_Xbv7˖-cݺut9EO>* TȍR+X6%I>}c| O)P@㰄h/\p6ɔ!'y(YHO\\\Ǚ/3x"""X`A$x{Y>^_~}<ƍzo~ p ŋ)8pW_}SlYxɓL0I ,>%h_I.5RJ,I:uظq#sIJ([sexicطo!!!9u. 4c R~l=DFFhѢߛ4iНÒ{cGG֨OYxu/~[sM?~~>:t;wFf2CٲeSݾyf&Nxߝɧ{lݖrqqa9a)}%Q ,ȑ\]]`)F=ݺu+KS)f޼y,[7"kϞ=-\pp0{Iɉ!Cl2͛۷/iyҼ> IDATSl_DDDDDDDD(צ~JJ3)|׬ѱcG>3Μ9äI6m+W~k=ʛoٳg;3 O<]RL͆ t| ^^^I=Oh4ҢE s/"{kOZYyfITT}e?1yxxA@@7gϞ V4EQV-݁5P4hP˖-'l2Ο?he˖.-""""""""҈bkk{cIߍ72=SNpuuO>*U ̮]=z4L3!Opp0GfIۏ9ĉ9|b3<ѣo5jEI ER^ Œđ...9>=.\ŋqvv9kOvߚ1|p&Nĉ6lAAA JlǎKiJ۶m)XU%""""""""_5*{2)礇muKywY`S < xӫVZ3~Ϯ]8p M6m۶ԩS|qm<7|9sLzM&;v`̘1L:///bI&X"87ó,l4iҤ >>>֎%g)r3WҸqc>5j(SYUV}ׯ_?ѬZgy&=ȦM J*I696ytdiDlll-qƍ Oӧ[t0q-^tSpYΞ=|$,8|qv\f 6iӨXB9rGMLtt4/_*WL/%qgggGŊ5j[RJ\t)i5k2rHI#b۶m/TV-ƍ>C5[Onܹ CDDDDDDDD$di`{,H"ر#.ݻw ٜc2R7o^T}fvɓ-wjxqttq8::2lذl,VZ1;;;,X#saҤIL49s`ooς 8tP|}٥{^y;; %,uʕǵk **iӦ1m4fΜt$gdiDEtO>KLRjԯ_Om6lj'syחzѼysܘ>}:} aL&{m4ԩΝ&222Sر}H)UG&227xnݺe>C~'J,iڴiI&I gR~}z聇 o3>W^eʕ8p;;;L```Ĕ\x"_|]taL6 oooF===pk֬W^ɓ'끋XWm#""dug/gqZ,]!xҋ!q$ѣ3._o߲ԕ+W7nk֬/Ya4~zꋉaݺu̟?cǎYc\t)CHnѩDDDDDDDDDDDS?in@DDDDDDDDDDD_JTHQBDDDDDDDDDDDr֨\cqb֌CDDDDDDDDDDDC'*<,NTX3y i1m5JTHQBDDDDDDDDDDDr"""""""""""k\D%*DDDDDDDDDDD$(Q!"""""""""""63gpMBCCs;G;....]:C(%*DDDsF#M4P ?gϞL2e(Qqyܹí[999DbG4wݭGm6)|M%)rP͚5پ}v6H~y\X8Ejܡv $ogMBCC)Pv$.\+C'*ܹd2Y3&1xyyqIl6jwQ۪ rJb;={6Cy,yyyogQb>w\n""""""c,NTܺuK_<%U& HttH6Qm9%!tYY؇5}}ԦCn=j[ANQ}P;&C DH.2G3Xw*.CJT|ms0|Qt@D,$맿܎$}y/6#y 8f!Mn#""""""+H)6odXg9H.ێ^4ʜ5l׵>hY{;p  EƗ >awymXN4fB6F?>nsrvf_I9`ϝ՚{F0'](lq.C6Y"VcςT.B<8.K/\|u!|ޣuv?2E|~0sMLR]ќ]?gnڔsr{cr~ۀr '/:cyY?Q} ^[+Ekܙ3{{]wұ~EJyۧ* =ϬWuq>,>O% N)C?Jk,os }*>*Cy~—y-VfA/eV|:r$[FԠ'EW)lJatN֪~?%'c&6x?זak#7>[%zqqKgZ~K¹q0{l56Ƶ dຫhu?ʤ_~bNˑ3L\^Tʀsk 82+ó1no!}O${=(O|hѾ!Tb/2luRfz}ɓąkЊ[my׊Aa (ӄVj`godBv9MYGۮKZ1bo_Win\;/2V=oI>{-\ٹcNJ/1= }?Atm׊6Oi,E _ੀ)^j͞e֋IwkASbxڴ{q Ó]z@3ͩ]4^ż)[kCğXBxëT|M-A߈6&Sf<Ѯ+ޘFI"OgB.WԈOf99 .L^zݻKSnE v64+G)bxѬ>[5%5މ[ί!_85:We*9vҢse-6hّg)w"\̟\sDtxq%[1חܬ.}U#WS mIM\D1/JT5L'b|/T-wqei( U?pwѺlҋFUJU uZ3q-7fr%QuӺ0 nӍ'7Dxne\@It~<Q^*fY;K]"]F)̄w>sVxW{ݱ&^*զb~\~Gnoe%xv[j+w?鞓̡l܍-E1Tiԋ7-߶>KǷfk'4`ا NũV.u&)E_d*o K&M-|{S%foBRXv0Ga4+K6 g|ɽwr7Bi7]^і'ɾ#6=GxOTw |1ˋtbDwBÚ-«D9j4=srl'zK?:CьkM=e#Ny"2dž ?3Ӯ'/֭GK6ӕu,XO}zz%6#7QXW#i2P`Vxkqh4~2oԪۀz3 (u +$Mu 6:֣Ri/yD?x-ik5+ƻX1JTdկHIT.?,#S0#q`wNnboV+W1/JUI^bهj쐈Czj @ty-=ƽX_G?Ȅp~=|l0_?r<]Ҏ[g7Ϧ$6ɂ:L4fKyWF:6A.Bt› } {>AD2E0-@Ϥ;&l`xa1 vu+sǎ;6#,}ckӮ_=rbTFs԰s?v9cǟ_LdhOKy/?Jc'x37d_r)fU'_̡1s+ܛDԯ5#X1q= gS5Ow-fʬ~,)|֦Fܫvbs%j{aEŲؘ.jPgF#cb)"ߡx1FԲ=n%l~pN3>sL>9Ңi1:hbdfR{n{INk({ZY6Gp8^0yS كPp|l,8/xAM/n_Ӟ;"懧OǞ!YVۃwOL[1t͜Wcsfs?זyz ,8Gğ~]Ҋ13W립uϽeṠR7ٻ NN}H97̸LA1 a1W,A8 d~2YG33(Y01r9Z;CNp⪑rukM=M?'طMf^m}e^d!z[Ti݃Wr$EǗ9LW 3գ[p`ߐUbɒ9=p6p1yhn\>́'#DO=i]5ѵC5JDDDDDbD9CԨKz_ome'>r/zԬl~' P Sys[<[9U]!$>?[Uag :{~T͓f!ήǗ73۩t/liI˖|eߤ!m2-LSiS o:FEnnpy)G0 p]s5xSI}2B^`E_{ M9M8vC~_4k6fގ4=uCDFJ4LN@jʖϬ-IwqٗMF TPQl*}<KP1 "<ݑϗ~׻-E&">s0 ;_fۼ44_hD?#샗 WjSu_FQ_bݙ):TwzFn ;;Xgkr%q. {HHM0Gr EhGI;Rsi={0* GnGBx/_ƨvs7*ĈUS;b_><ԅnjYyF"F _'vmw؟_jP˒o'i0DMҔFt$$i6~3c+Y(ΜDkY: )"9ͫ|KLhBa#Gq0vwkI+n3QAC7B"v;*Ou];GK/{Wj<鉿7G`&;Iw y;)P ކ0Bh~8Mi[mW/47F<_$:ՒV`6c2`HgM4ǎB <-eH~t bERy !"ݕc\:R/e+2~O (b&4l64.w0q 1;uK xzrΌ . 8;zAEEػƒcO-{/F{{M, (wT4-338mhn`ϡ%`(ߗH4ˉ|,'͋XԄaZy9{Yv#{$ѓ$ۺx/G1+J IDAT[ mrWO]d:(NShW+Er&{@ K3"A'%D_erUZ3[0Ĺp6>-p^UKPӜ xw)o-s#֩^P|sƕoguVwq" 76S"xqo\ĩ\y.qDʌ\[8{|"ENݠ]]eͪsjL`2K덑FFХ;=fӿguOF/( ~%0M#y61d}s3هgڊLGl8fwï,EKM*#JyoNPI_zpGD=d#G65/=Nhj{ʓTȣsg.L@~!B!] TF틗y;%2GBy#fdA#gϞc/`]l= [}GE:e6-clcbB?MzMb݁ tw:Ƙ9Uvy9w2uUvIhУi``C^p#?r+SD:+Ӂ&;n.^rTr't$+u%h 5ŜDnΥS)\uk%ٴjm}L+*W%&uZLsëczt_|ݩ }lXHg_S9)Y4{{!oS+smȓs3rRRQ֨QM{nDRQ;YDĽg?M2d !^(7=oۦoaۉ+~Q^uc;'%*.P0({82~fo ]3*tyoo|WГx3bxc v0Yv.ґy12\F泱?MqL:wlq+\~ɗ~y0S*BnVR6|)Oԩǡ1ܿe7JjWY㇯_Ij }Qv\[\ _z0*Àͻٳ{ Ӛa5Y޼%gP ~]G3Yi~Fd2]l*-S)n;@& !B!x?2mԯ;3lͷ \g?7;SCPB/ro5%[@2~[Yg }*jr%߽Zpc'NH4 켩;&&e){eX>Qtr4ikfͬ; ̽Lj0 'X4Vz)zbQ[VEȱMlM|og*O6-P;vޖL4I6 ᮦ%y h;nUɭ W,wN,!9ެ&K]Vաv|]ه _clOf]#{5\"/\lj6RmD^wCSٴ Y";ʰx:X-ݞ6hC@FTp@~9VyQj?r+'[[y'a5'[?V/ΔjY^SPue!pL ~5J1Z`X-]WpЉ@PТ UMjT_΢٭::n] }SVfLMB9Qʔ>ԟE]NiۤvNh15)INW_]:QagĖU)k2NGJ_Ѣ7׏rz/U6):}&hۘyah! $x sU %ve*6}i[M/9%x8Yz~&_oJb`NmQoZjzѢ fV m܇zy }& ȖnΊEܴ+{VC^>έ`kguf} U ǍfABڜ,])]2Ubz?J Y<)=x;Wj !hF0Vsٔ4swͯ.N NlШ_/]~q3?]M=~dAsn ν0-ƶxtd4ϠNa\5)yJMy=3M)bO%ZmUVGk:b`N0=㺰sG/Ibtߓ}ѫ h8"Otn~zLq/ْÚKMzC鷮"Oo=t:7KĜ1z3& 7SkR|yqDG&iw3]-hۊ"EcB!"GR: Hvk`  Ǘ61y5\'~Z!%#!ì\V )QȌIHKwfD?<Ŋ1+xRUr~B!B!d S8p>O^pʇLY \!iɤ8AB!IB!B[.1B!B!B!ɒ !B!B!BdB!B!B!D !B!B!BdB!B!B!D !B!B!BdB!B!B!D !B!B!BdB!B!B!D !B!B!BdB!B!B!D1Hwڕ !B!B!_J*et]TTX1ի͛7!bIyd.RG"<< B!"?~"MF/2w}t2:%B!B!'*^Ͼ G侮cla4u/Igg+^]mBڊZ tfCǽMp1SsryP#WqZߨ hՈ>Fs,G.[H"9<MIk遧GGXKSMLqͯ-D6s'mg4I|7؛%9]qs͉ Ҥn|CkѺ/.V[fŹ`9YƟ|ה\Yf#rggrɲ 53ocY[4T5?N-1ٗfdBةY/y(,-.)V /T;cxu R#;Y-ˉO,mZ1Rr[ci Ryo0qw:OsSLMM+-bS'Nq'1[ &+oU^D a Kk{rO;0G%/_Dzjj,\Ȣ1)KNFfF yd ~|Q;:Ӧ"nI-U|8ڐ3GlNɕ?P Ym^ 7']Ϙqf]voզF>y3;uOf4(@ά69yQ NnswY;;agi]Nu?_WTY>gl,-}'g@V–0|8mBȆv%T< y[_Sȏj2m_ ^\fe߆psҚ9R<3KNuoNrqjc@ZM:L{!B!?Sb&rLk9b?;[f.㇡>s#Gk~YK':3Wê{EzsiooZ06\w&8PaSnFHo}EsߜgAdve|]5[dԱȘDSa26mܮn\ԃ{i4d>k̢Y&O?N5:ɠVneÔ&mN> |Iu%voZy +%JE]>ϭǶ s`Yi^ 'UL!O%?y'kKԯ=c>6dȚ}(5iS|_]g8[cKxl:q'Ordrߙ w6'b"~*ƒ6Uiߘ{Oε#bٸr.3ufdldhn1QХ\'#gQ4DZ-; SZ p[fZÜ>ܚ{^AQ<;-Qf aFsl|dvbrȪJz3˹Ј] a=P^o#އ&}{e-gm,%]8ri1Ye.fݦ,nS8RlyS)2A޾ϡ#$ *Ջ3DZ+}P3߱z4|5YL]MKṣfQ . LyۼE}vtgP=a{P|g픃?7׿'!B!B| }'6^VsJ5OF́8)Nh: .Are0+&˓t;~&͸L~<'jpaޔUѱGnYVtW.Z}LG^[L=Y6-Pd*NVE89G 9~oAmG>2tQC14괨Uc9VWY3Tsex63eݽ?W͎/=:}LWN%YS +\ i uH!Kݲ))P@*x}ĤYգP%X7<ˎR 4)ZZl1?ϚJ8r9":tzPړÓCVsVv&N:cǭU˜-Js8S?ӥcwnSM#""/r϶"AEϔdl,^r^'J33\Ӣa՘_UuC [b[@: )9:K Pl7V~ql>'_ƽ'ՠ0\)f*"e #9ԼxWφ=OyCQt*Q.(ikRq yn%6|7SFtY&ëQFl՚ojw&@r{KA ?ÙC^aleH΄WSo#zs}m(U7?cK@9[Ns.Tm"y`ct)n0.#ڌ3zpG.:i{"?9u>fV;⩛© Q(WpYcW@첲8n~zm1{E+Z]68˻想/+^d߹N[<.XFI c%(¯+X%HP7=8RdچnO)MEԐF*T5حpPn'x0 T?}Od]i22~X<?r+ 12@1@&Ocټq;b2'g>Uy™S;n{)EStȩ{X163ȤX'>"/ 0@N]sFͥF^;gӱ7iIݵD'rJ?oI b6s8Y.)P쪘bGUANfv冲Lc6Y3RfR1[wM>" AWcgg$yK004E^ݍYf`Ұd7 cFJ>f8RlL}3%pnZw>7/yVP2P*?fI}؜sGՁ*쒸ECYٵ@_T&YqA !B!"QL0WF3%l47b̕PN>w Ƶ^/8s)9[p'Ώ̰и`W&DAI%gҵ:U g3]&,[ʛߞcWwsy#ږZQ xۇ3a|ݛP@Ie]Ta^v#+Ҽ0*fn Sە%,3㞂hr\6GϺ1[`&&(arwP╥Տ;p E1Ppx9(`J׹鱢Tg׷m0bA^ 0`G\W{4A^ Youo .4vn"5ZT{@3f.:A1*orXQauXGϘH:5g\0wK*Rq2D GHTGQ~^.0053tv(&nE4SssgR۾%SWm. {lws 7mIh<됻 g'}!B!B|;Ҿf kn'yӅ귫%5/73jNAbQm5#_|Ory҆peT@8qrɠKK̿ a}Tbםajr{yb4S疠cI'LWJISt)q 3Qm^g7n)̀\[sL4ۙ9h͋ۜݻ_WAUa zb.rMsObㅓi4_ ٨Q4)` <|U.LʛLV]˛rPg'_^+͔TmLy]nxxc7F*Hj[Q~7a+b^.RmLG:ZU1 M ꁊ+L!TDe^VLk8V݌U~2vQZB#gU9[38CKM^qJ(wފvάZ "x#xN4gz9uNХu7A~8mLѝ%nYĖ=?-,fGr6 DjfRq٢Nm+P}i?feT"ҭ< `jfl6į`z¥Cc=S33#LHj1Ѷ0q@kXqܙ^mRr^Lm6mz6/i]sB!BK~Jy9:+Հ 4&8;na5FfXY&5Cpxb(в&Гz85~`6| ;G/4uFܤ!Ss _M큵**>uVl[߸65FX763Ѯnl_lϯه?#*&@fwl$Jr3T6?fAz`da{Q\mTlF0DFnE( \nWMnXe2!QX h#CҪNZ fo6 9 QQB]l Cذډa2xj vM>8&OZ¿:\)Zg ӿ>L44\d)C׏%,Z;ͨ*NyMN̘|O֘aՉ'Bäl1,֞M`Gyγ`fmW;UτqNRanapž)`B~hLR2玑/=,FgVДQYcJFNYđ!cѿ8sg7sԙ+B!B|RVzժUի^ڵŋ2gۻP{4̢ml6o3F)&y0$~ZիW<2)Ki=K3\*Z}JB!B}nݺ_Usϻ;B$ Jsƥ,Y2#b;vL#\<2)رcdt2B!B^};+SE'p5dt"B!B!BXOycfbB!B!B EQ#cB!B!B!m3J!#O@!B!B!Dɶ~B!B!B!Ho$B!B!B񖬨B!B!B!DI+*92Bϛ?bma6GsIA>?¤Vy :MRww0h~7]]\n3AG{ Ycg_ftB!B'^Qr!MA(l9}S+:_ɶsQZ+8Ȧݗyiol`ĕ}j9{M*:i_i͢$8{?&Ve$̑qB!B! T/>JnO|c+O=Dx}a~Cw܃q>2a8.K OQ,c=wn\\\CJ_ݘu\S2~ wb{5@FcdMvOJy39VɁ dd"bV 6xxy 0ɡw.XiP[ŗJ-bx%$rNn?*k/[逅!&ߐ)&#'J#SQslVWjɍ1fY f,X?Q S |3d=N,WeN@ؙafNN9=Q*vw'-F^EqhD] ɓ4>7y0mN#jMbw[BS{ZTRB!B!2OS? !ң3Vdٯ3ٵ&yodP*4J`눾2QF]c|8G<~ˁ ǰvkˆI),hZk&x3Vԫ1C8{'y,#WeVnCO|a[ݽj4 Y{vɱh^VN!B!o|I7S͋[׹SЇ+rUskO.- h7Bco;l='9ST _ԧ<;\ 000*Gr&ơQO[դO22n-Ӻv#$1ȘIxEmO *DΕm9(Y1f!5'|X3ʗv_R5˷jB!B!g ݻ?ڏfI %Lk72*t_}ΰ&^|y2Hhd)0pd?Ulc?߉~tW\8'Q̥ &&&(Q/w^ٳ܋`0Q/rT$f4jo|Xzzbmk}ҹ@I/rQ{ү|kv߭^fEσS)Foo T*eʣӧ;= pIpwWkAvؼ5N_d$P!B!R}?-egZ 0,TE#=mFd%-]ը"D 0N su0>^[|󿙑lX7kg5!6ٜqfN5,4I;~ʔrN0_{yZTV=eh@~j7JGP4'呹̝*Wa* @I<Ѓp?2BGGpPkwr@{`=ČHoH6,4T^aiCec6<(`g|5V9=zEUb I 2( ʚS3Q msa>&8E=TVek_oKuH504E^hEsEovIeb&PB!BY|HIԉZ `@tX8/ŠUռ=!X}ʍm0W)f ž ,nx"o2&~Yq7#/ ׂ * fC|;J ~=Igcav6X:{lԙ3ys`al\X0fX* g|ts"ulNU ,%J,Yv{{ *yeWWL)n/KOMNl^qQ w|O[ ?oڷoj<̪gfA@\rE 0M\BsM3Ms\PTZ.iIVf&kf- "̜"" dK[0IY~fwߌlp9f )Rdƌ7P.mV.QòGwV.&O# y<6 w IDAT_._:1C;mL|av_w?t<\?3t962nWSd7RTs`ukѤ~i?Df~i?;|Gg|7W?a-z~+珻5\0q hfvL>.|6+ ?|Ac<E߻+56̚+d姟gW3gjUzH65sʞkߗs2풤9ro6#3;C:reZ۾)^ |]do~#_f[62ogr^o޴cگν^?F6|H|YF2<_<"or^g5 QZVݏ'?al5+4NmIz0ο('Qu{ql⌽S19xXϬ8{JtlqYS^-GoO^g62y:3S,Zafɨ;N;2Cr\~ڦ9-&@jsWN{nf^x⹬<| ]տ1hV:U֦=.IuvYZrd~+Ż)#oΘekc[es$mB"3^N!Z13&;y`8 ;{t^.x;U5NdǥivVM__vtjҐgh-+}մɀ΀yg5~|yψ ѯȶsvX 9}VmY}rʕrȓϖǥ}oNoNo^{EV\u|n3ăN=verwLr?,u䎚x9+›Yi Sv7혪v[娓w#'o[?]{{}}9Ӫ:ϏSUU:m=.]J=hQNȀ?^{0?o]լY{Q| P{\YmlxYTu1۬;93zzC֮ޝJ^9gsz孩}t>]N9-?vVTN_:;]j;z>_/CWKRc||~wqEmݖ[oM{糽Y5dzg53pW9&Md<Ѵ|t4|)wxs7}9g5}[nСC^rf]vޅݷW~#S^|1[wH+,K3kL}라|~dMyz;^o鐞Gߗ {ūzniIN}.jϜH*+=ɧ: ]K#s,γTYūòÀҩm[Gq]pBo=۶Ovʉx>󾚋7/,ֶK Ȍ1˘&ZNy>x|{}-yXќԯ|C(4|tK?5W˦uM]3z1w1#VV?3W~}P6>;~  mH|9=G4\ó]bmhQLg4] gܐo9GYϚ r۝nm?5薯\uloC=l_|?_|}7襪ufِ ;#Ww:Yvat~}ݯ2j'3kۃ!7?&לKj;(~`Q5j ;lΜ~yc q's òbo<s=&t|!ݗxǏ_-4a„b̙`1fFwwqmT<2{߹VVEoܸOѭK)6qSYV-ϝdQ^[=FW̚,Wǣ_wz.>yШkvEQuOۤh~o_l]nmvq1.ZwlEQ۶X.ث*ۺ1ޏʎGQŌ}Ť-<&Yj?λK~{]ؤO,{c?=qZIŎzhXc+ccw^h9?inE.{2sbqԧ[~kBfQuOQlںcE/ٶ \th38뙏keGǣǏ(:ݸ8w9uϜS [ib˳*x?W\cע3= ݄ |SƏ_$|cDgT̸\sAaywyj>Nf׃^6+U-z=j܄Kr?>Uya[m3$\~Nnh?~JuMMKW&=Bj74=k:oܚs/~>[w>]S588W.?~cs1k施eஇdmSgg퐕ܩ:U B,ӗ[ꘪCr7ɋ]<7_iӦeڴ3}f]_z!/ҽ[W5ݻVe / REf~tkBVAS/rҭ{yv5J/ /[V=ճ ~5̂5F9'#OʷZl*I\r2}3rPۥ!@Y**g.=IRZv;pHcl-[JϖV;e܍?n?4:ófMUjZw*o#'ȱ!*IҶ>;eUOv = /:ڔ4(uȵ ;d*IJ_΁_I^>S ^oz5>B3_*j7vM._QxSsWA'swmr [%gѯ++om>۪JQ3n4Cﯯo/2[Vwk>9fAx/A_Ȁ}kyss_km9:sYe݇gܮ3}rO.Mn=:,oʀa[gS;|:_!3;<[1cIG of~}JkcΥz$#N귀Ags#7{LcOk}3 _i]i_sJ̀ J5sكZ&ia :#|{͜eC|=Α;F#'9}a+h5S1eR{E/KKZng уmVy1\g}=_~t&t'xZ6obw̰/n=F^6:&W:.[nRit~{Yc6夿&6s[v9dA,7c4*5OYwM+Vc?~wqf~me[;/Q&M^ 0MhZGb<[2tвWc\{e]%wa1m~L ΨJ 4BP~JSPQ___ETCE]]]4S-*'&I&MTEGb<ѴxKZCСC$+R43*@i 4BP(PFJ#T*@i 4BP(PFJ#T*@i 4BP(PFJ#T*@i 4BP(PFJ#T*@i 4BPe&OiӦeʔ).]R[[޽{Wd*}Tl.̴iү_/:ujE hfL(ڲ* EHȑ#3nܸE~\9ƆQFeܸqJ]]]f̘gԨQXF*ZP8sꩧ.tcǎM}}}FWф Xƌ3&cƌm۶3g\ګ h zlS!T@3$T*,+_ΨfgTTUU-PҸҸDPв*FPgT@3(gTL:#m2*MfsihhHUUU*444sTtW[[|nА<gT@3ӷo{,}2\ iӦ_~_N3uԊ.L2%EQ,eUPТ-[~gϞWg L-Y}\UUbQ 4Ψf(Q4 @iP7*8e !(Ͳ*F4CMzjڷo?wyoyUP˘QFe̘1 ߱ѣG/5x.Ԃx8SUUN:i92QUUqa hsi:*5559>#Fd̘1 }ҸPPcCOUUUN8E?|n2*MeaK9^*j9C>nQQiB43;wNCCCPeTWW!;w.sgXx>,  ӦMː!CRE%,e˟gXx>,  SLQ l˧J_ֱQC ,|`qTgT$QKLS !rf+j>wixǷGfUH2(`5=P3ه4,{m~$ʥ|H%d#fj!oY?v?2_^H_}"wjfp^*h,`4|y{oY/ow?w|)to,Yo=[9+n[hI-?'ͷX?+y|msդ2[6ΰ:EʺrwH1g (Uս!d2gz~st&lcZ}t'漣vΠWMmұ˚p=refQ(ۦM6iס6{l/}\54E!uI1+=7ɗ(3~>woen9/~Æ~|eo74gb/~\Ծ絆>뾌5ڶI6m~Ug̞/} mjy5z~~6̧VN:j2hǑWE>Nf?&2Wu>&ӂJc(MCG.Yr5Ge=rNK/^[dVN1<6WHBI(1@B/@t" HP:R *MbA_ч{IyHH$<\\ݳ;;{Μ/7QZҵ~WDcڽ=FyrS^Ll;SqNڮ5 F{ c S3U~Jmć4. 6XSv~UB~9 8頇I#8dˆ K- 4RIω$F;WSrUHagho2\a]sջn=䕯U Wo$ރAE2~fw+!Զ2$[!UJ5 fY5Vׂ=}pwW14cկPӔgʲ^ezӌ&7m~^JmjysWy$҈rn*65nT?8<]?Xϩ;+Ar֥y# mQE%:kԫ?omӄROn.r.c4o`ZBy<] L1⟸"߼`6$?fO]/5vj?{حY3V6I˪RX%UQO-{L1r=@޽MdbMՊ *~W]ڼ N]czjԗU=˱ !|VA΁zyG #|^ VB*gWュ>T]Eux XX,Yds\"j5RL7lfugG>23&H:o:!Ba4jxyJ:HŜRMِaF] x{(wuU2"~|a|}D{+@e]Wn-߮O*G'eZ2JU W=7%%HwBwS3u럗+?̫$ͪn-n8xǖU.(窼]fkhcr4V?'ueLuQRy]呻ʷCIG?i^F -[^=DImC.,OsTrc,->QC#VQ݋/DBpJitHS4רS4q"ёMs4~tO.V藴TOZ\_.|K; ״(Z=AGP3G|7 V5Eҝ۷e ("m:P5VnlYEXF)/M*|jvsR!Y-1p̗^T1&dP+գ)U~_;gԓ[q}nt_r)8doPǫ{alY+j@ds7ާH+ cp͎u+5f){{[gg+A$=t[7oeXRZ}ڵvzy9N*J&;yQ ۿѯhjrV ~[`0M 5b u+XI[1,x?#:inZZvq}M]㚩 [Ŏ% [^}>Vn[P)k-?D+gI2䕻̺PE_Ԓ uctW~T%z-YT=6$Ѯh'Gk`~drU^4Kt m~s! u~ȻG* aKM$kmUR:oiR [*]>*QFNVhL$[h2A/ęxoWɽKYu*D*C6 lB6jjX"M-YZ/m'tzG?= \.ͯ=SGxjp,AϟMP܇4+6Tuk#ӟHEKK *%]!)gǕ{;JYJ}MZz>IgNyx_3kw뼋[J:sRՏwݻ_ >_K%EVrЅ=_}\4m^I~rĜ:+_zZ*rZd@e;nIrBMg)Qҍj]5 w쫫*:v}F[wH݆st!U7w%Su$NyK.I{.7Z^)/R&3 4T)AlNcjݳ)XDMi\?CҬ/ix6$نWԖwo|S0UHt/Df\ R8B_P56F߿[M |ȭM/)cᗶ)GEn[3ШiֿHcJ ͒MpAg,ɚG2nfElmd%sؚd$ﻪ@lIϗX^Z`y3?g}WRoP\׵]7\m$:KziSYJ-R{khںfP}N##e_e'9 +VWvSieck+9YərY!nd%Q{ڪHB6rW.el052" e 0MYavsb@px&\*`uW]gcF66YI?X{TFN:jEnPBajݲZKꦭJꮒa8K봧ї`,ۻ&O{I{CUKfɶoVr#"*۝ZJH3P,H/~~-SV~+q%L ) CJJ6髓=9 2ߢ!w:Z 3&jKL2$0$s^ IzȓVS=RWد'dx=TZѸ9G[rպsMDo) 'J])Ny?7c;Ѫ4r>y5Jmdm\fN^I_q"uXD*C"˺)TϴǏɇ>В>j7gzhiZ+V\RGZ:.2l~QPPlQcѮ`6iѣ\K堛r5QZlIN`|/q#>Ȑ'd?ڀ#)K0/HyװF%ÜfO䔹 )v/˜3k\brլ 9uDTXxWK6Tu1ɵhmuW[[ũVYU }P.ao 6ҁX뾋2%2w I x>SR:Q]bhZu*V{Q"?-zK{I~ RN,V*`I[ڨB]~Ts 2XSLڣ".[%Jۑo8UUh҉ڒU :ٵ}.T3.ʽU.e>w.F2F-J瓫v9dDzt˪j*S@_U@*ۡ潨1t c'rj*S+92* 5MVd-hk RIDATim,~x]> 5Y9g\s)oRLf-u%9#krGOk͈ZR#u٦ +rB|YG6;{UrH%2^ rShѴ8}P>WNvmN`dBP}@Rը:Tn jFlN?8M}%s.UWŽ$o_Ҽmw,O RI@+_iYZ5f-9x7+/\h)۫`\?wKWK_Y1YVNʉ'u9Fn~UEӹzu-C "sw c"R54hhEKn4y~|`ȳhFTV"5{ƎY/ԙ[& R7Ryx?qZr7lPkQ`tc4UKלV@?2]K+GKVy+h?Bch>8lVyԘJvU "&i&[y3V&@Ձ*٬BjO'y4r|^}K g)QS#b?lUAu\Fqk+1rQ*65ԥd>;Vhʑ:y-Iv^tf--eEFLӋkfௐnifV@oT%#JUM>jܧEf0q)^$=_9OFIG:9ӬI=]zTԡ?l4/|m͚5{7_zׯ/Yb ?Q㿛94J6lrr9!'1.UI*lƆ;Zhzjj*\2G3.3ɲ:YW~!,'Orx)tEXtCreation TimeMo 09 Jan 2023 09:24:43 CETnv IDATxwxSw-2EVK (' D. PYleREQT 2~tm_u&9|Γ4#w~&OwGXݚόΧ?)Ѡ5yG;~7`5 HjLw"""""""""l V-)VHiԔ \Iagydt|մTC!5a|֤=W USRȚـ՞V{:IڂUTl۶I >'p#p$TɘWӺbUwrJ5:l!k5p5@5iZn4jԨ 4(f4HγZfKTTT={N3f={vjlA},44^I'7 N:-W\9DE0LX,, Vkj=uEDDDDDDDDD`h4b4qwwÃ_ԩ_~e!5ɚwz:*M>Yf}֭`U6Vłd"&&|۷o… ޴iQ"U$n@ 6Lk֬Y[n)L`ۛ;vj׮0=X{&њXuKӇ> oݺ}{)"""""""""qqqTPoɍ7$W]Px~WS۷o`UDDDDDDDDD%4j~m%A)jI9XMV9q ,DDDDDDDDD^b0vڅ+"I Ŭ'cy#qƍ-Z<666{JDDDDDDDDD0La6X,F]udՊo7 ; $~|U+wRۼ0yH}(7(:߿SZDDDDDDDDD͛7d2aXZXV, &XEՑ:f3-[;᪍5OlmҞI':uHݼyG&7DFFI|TG&[n8ϝު-GM4@J-\V4EDD8$ڋ%""Budy#槶;!ޫn+"""""""""ܼyɔM&7oT=~Vp5zq*kiw5Nj2C3)۸#CuIsd=W!P5!Z)WDDDD\#ƿ?Ͼ;u}!***W+aXR]&ҫv(%5QRjUDDDDDDDDD\d2a6>ٌl&~UNj/沟|X,L: дiDݻ!C`4&6#pFB5p5$VDDDDDDDDD\++ϧ&66///֑R iάh4ҠA,Xde˖@|`u,X@Isd*ՔH)dI~6̑=Su_116~9kr\~Y1c|M>g\A9*ԧUœi'y>U`9O b#p[fO|1dbw^Hn^YYP pwwOVmak&HsUۃܓܐڃHc(Ʈ=Mnx7+n'^"\a8R" 'fY.ރ|>7Fpm, ny)Fd?^g2a~ 3yD=M! p%"""wu-u$ X{lLԑ^NV8@U*""""fO3;G-~`nh<>9#J͏t4-d4>AZy1X#1Ii|"{R WEDDD$G DDDDDd' ԡm ?<$1=CWˏpH}C Twc5+`,J 08͈H@5pwDDDDrSS{^~U۰cfT{;2UqՂ>ALww`M8GYW#"""rpssK"Ni], RȔ[V:U06D@ju8&jpҕ?#;{FMDq,O wF9THN R O: uX,’j߻w/͛7Ouk5\""""AF<\m c_bw!O ;2oS-c /,*Dk777斩iՑuF^}ӦMҸq` _IJi"""""3#޸EN>CSC;76げޘ#¹aKY SDDD//\^GZcvuz+ @1H:yM/]4ۑ؋}z~Kg3QTܼy,ӓ|  M{Hʗ/ߝxf{MՑ 5~ XEDDDDDDDDrP2u(OOO (:SG9iJQoJ*hLx(Kt""""w|ITTTrss Ց:REe!"""""""""fbcc1X, Mnnnxzz:$﹪UDDDDDDDD$iqSc*w(dbvDDDDDDDDDD$ WEDDDDDDDDD2!pUC.՜T=WEDDDDDDDDD2!pUWEDDDDDDDD~f>ZBwsI N`6mMn|r=2qhR ~*Xq4f*7z`*hCfb%1nј|+)II tx\qa׾K["j-ITBZ^, H~lql9ݱJP*>/[ rfYMϷ8~%Ъ^:*Wԟ5{ԑӵ%/_xl:lvH$3vbG*V&cj6m>ɐ*fMOצ5_Mk-`4y>[ɭ$;o>OqT?oP5Zw9=?G׳?B $S 9;4Mrx>;'/GYЃs^bnr)FJ4uFl߰9SoS,<E|ŠG0{C|ϿЋN Kѥܢ;55+.u5'(nү_6d,AGjbx{?#p]J!֪MmՍf+5(e\4:4Rnpj`X7Z ekN5)$N; ܰ|vW^\!ӾJLͣ_ZKbb5`00KӸ8O78&D{hkϛ[$|}ܫh~uR[=ų8 !5n=|Jh*k9%nF ՚Z`-""""""""9 X,X+S~?e;ʩNe&24voI~xVFq*-ERxJ=%˸85{)h^ v~ww k>oPMCl'`SÝ4n\ +x߮߁3϶ms U1z?5y^41v l܃c43oGA ;@ҩ).ێJ:gNrz}a)U+XMr>\5H{5UɁ]4z2{Cp,EIYq~Q>+"x/"?}wvW(gT:cMy\,ɍ=~`85o:5J\<zӺl~8y:BWyVE&R)Dt8O[ ;{XӥGVa=KBQ[-|p)aY-|5Jo.bIT2> n/9Ky/γ `Mu)ҭϐد tˤO#h\ޛ;f3nu'Ha,_I9|8u ͈i7=ZOb՗%b5|ܒ!MEɬlHb8cP ~)Lw=\׸q̝:/.Q,vH'SFGC8tL7)f50iT(3_YO|+ud\Q-~#峁O0)^.w/jt]jpv1՛\l 4b>Ö0j`K| @~̚#>CĹyS̏"`xog6yuOgfuc n)]@z\6;K]!?j|[Ùs:yJ_3R EDDDDDDD${nrj_zuVN)r_V^ġzDDDDDDDDS  8d?esUL1WE$,6g@N!""""""""QULps\Y]pbŊCDDDDDDDDDpjX\Y]EcdULpxX:DDDDDDDDDD**""""""""" WEDDDDDDDDD2a+DEE%̏?PP!7~:3gpz&<CV+'[UWO\\{e…ZDGGGsA<Ȃ С={QFxzz:¯ܹsٺu+ϟb$zٳg9{,;vϏ֭[ӷo_|A!{ 9r`o;%ӧOgܹL&fϞ ɓ3ٳg3}tbccz|Z͂ 6mZaBB{\~=[gʕ 0ŋ' Vy&˗/g,]i5ƲxbǢE, gϞe…׏K:8-**͛7sD_ .$~yD9p[l!::u.]4!X0L X3k…N=s{~ [Ȍׯ3~xleoݺJҗ9}4*UJ3>>ܺuݻwvZoɓ'9q!!!t%K=Xccc4iV+ 6̩iCvOGW{=BCCk׮-Z'iZ?>͛7ŋ,^`,o{DFFЪU+ׯ僃i֬ .͛l޼9ѣGٶm[zεoߞQF@nػw/ϟg֭=zYvrXjѣaaa.TP!4iBŊ1=zW&z'ݻwwjMVÇsN_Ni޼9+W`08u[s&"""""""Rj 鼫-Cd-JMiYwNر#;L<==С .`׮]!3 fҤI ӧYhQBv9v}O+C ŋOv[?= IDATB3gקH"  㥗^ڵk 5 :ۅf̘#G&o޼T\AOd,=g"""""""8K|j?yM@YYX}Ǥ8RCVd~[o &xoٳTT)!`0p3pʔ)jˋK.eh{۷'=O<)RDkPti_zž}UVY&CL0>(C00`q;^9z(7nd'WP!fϞMR\vݺu/QZhQzM۶m p™}ڵ$ZM…3g>hom~?ɭ0qv~ez]K؂*)qVEEE`{گ/9r$۵kGhhhaڷoϻロ9rÇ?'[ѣG3T lHh Vyun8V6 +6lȤIX5j6mJ1h/ZLMVc%j֬СCRӷmoٲeWbE)EbŊnX,,[O?4ӵݻ7a~ڴiT\ 2CDDDDDDDr,腬7Y3zjpjzVgx0, Yf^}TjUg@@@ ,`ʕk֬aԩ (Z(/BZ;>޽˗/'{̑#GrJ|rCP +FÆ Ŝ9s~oٲe{k 4檆:[P踘Ε)jժ gZfǎ)R֭[go߰aGN#~(̮Q>>>?1T]((Γ' ,Gqjm.XӧOc4i۶m/K=WpwwO6/iqF gϞ+WΝ;1baaaf}1b~DŽ8ѣ9}tjhԨ~~~ƈ#o6lC*@R >>>n޼IXX~-۶mرckXؾ};o&&Lߡ*VH˖-YhQunذ{:FZlIŊ֎ $$$$a}x2+'k8u/^E|׉k޼yjsdŋWnݺta<3/=ׯʕ+ԨQ#wln8VRk 4...yGܸq#COjҤI=c(yꩧ0aǏ|@EGJeZYz5ׯ_gĉTZ5ЧO8cbbb8Tzuc~UVeذak׎jժqܹq{ԩСC]' ͊[oQV-'Tuǚ5k^pN]iӦѣGmHطo%m Vs;2sLvJZ bI75j-zV+;v`ܸqoVZ+xyyey?4h3ÃukYbԩ31c0uT<==5k?s)'#%wwd{xxd(wԅ ի@tt4'Ndĉ{YZs VEDDDDDDAzچH:/Yg VypwwaÆTR~[{nonݺE޼yT<)RI&iӦdX,lڴmF:uĉ'>}:QQQ///Lǎ)S\9FATTNbƍ<l޼O>_~e2i$Zjl2Uqqq4lؐnݺK߾}/X ŋ|WݻOf͜RbŊqY:wҥK8q" 6,̙3,[=z7oެ."""""""2ćԾ~n>(hȕZoOG-MWpjX1b3JTT)C38… 9e˖% XF#W^bccYr%3fСCi4V cǎxzzfh_hR;2j !!!5֭[bZS}Z*vJs9^oݺObhذao63gL8v Lԓz׮]\r~)7n%jՊ'Æ Ґ 'Ofʔ)::FEDDDDDD6>>>_g7,\͊PJ޼yvɺ؂Ր'U%K,X(Qcƌڵkl޼ٜp_3>OOO:wLJ?>[nɂ[шZwԪU+Sa.1>>>xyyqᄶK V+GIw9^СC۵kҥKx"o>c\{ol۶-piӆ.]di>,`˖-f\q@9G,Z(ӧˋ{.K9=3(QS@֮]Kll,5kdرZhvL0cǎg6oѣG|2uPPmڴ!88+SBldrxgk֬aÆe˶mʔ){ŋYh.]xՋ.]2i$,Y… 9w\ ɸl@DDDDDDDDD$s:EZDDDDDDDDDcN """""""""r7R*""""""""" WEDDDDDDDDD2Acd={\Y]pYfCDDDDDDDDDp:DDDDDDDDDD*H&(\""""""""""pUDDDDDDDDD$dULP*""""""""" WEDDDDDDDDD2= qcǎ+Wr-Zʗ/ӥH.pUDDDDiǎh4Ҳe˜.E ǏB 9] O֭[DFF{Rɟ??KVԦQ۪ 9<""B8U:uضm^)<@D$9>}Dr0  WuϰZXVΞ=ÇT:ԦQ۪ 9ٷWr k>g9s.IDsnJaX\Y=h4Rti96tMs='UdsDZZ9]܃J.}H9?9]FG?dX0 Ĩ ]@|jGωmElm.*z-ߟ~lρcpЇ,5ԾΧ6}Vm]Om-󜧶"C᪈S*"""""cN'o11t-i^`؞с]ܵޗ-`{8nZD$W;̪#l_tNWJJD Y2m&W,k3Aq*5W|?rhpYΖ%DDDrww?ofyCO~At̜?Ә*ư'&kWEq׭l>ا'ryT}.y.Qr7w(֨#l[CuJ^"|r,ZnoY q -WG^ϪÑ\'S:2Ae +e/_0T.Y^A#Y{Ҍzbhftpz>pwr=l(7q-"6֫ghV"TSu4=_caC|)Se|7buP ???J灺yfP(rv)U ~a ("n~~Yoj-Rf#xiZnluSxSSjPu6338pj\!4f k:T.^fӪ04}Gl _uJG#O1жp<˝&'ӻ]}(@@nщs-.SuK vrÌ[/,ɆﲍXoe_7ç˨,f1JM5"=8z焬~H3̒%([#7YB,Aa-"\.[K_[xJQz?GRk2]cL\%"\&'(nʿdzamXH@g'5C6 -%xm4˧mr܎_yGU/m/Ӭq\gsRXĕuV}yȸ[G&Z{3r 'w'2`i# tǼ툲 zb|W6Tq_\z}3ou̺ *t' squ|o[!Kަ+ؿf.ӟS7F9zawljk ^"> 9yajk -Kܸt}+0o'K ^@h>ƿ# kԐ@#:4d|1Z 7 j I?u aA4+ߞ?fԳqQŨ9KDzww [tjiQ=o“mXPq}q.\5=7 6"q2>5Z2T۞pp֋,/.o|M2ex-W"\|v߄miZrVN#Na|Z,O@@y6hϘm.k(Kzu߰ bQ7b'߫&ntlH QބpKGW[(CO v;801M'𛭻*|:=@jՓ1EX :e?w7xqUPFc?s$*-g'#ݹ=m>N#pTĞ\Ϥ{)[%a%[ [8b8O?L2Pzp{3=ɒ,@1cׯЪvePjc }oyjU L5>gު)r^eҾb|=ƅcD2[?5F^{| 3{fh`Ms{t~ ?nT ȼ->@JiM|wǾnJG;B '|MZtӶ+lוr*PE~w~Fes*'lm8w^Ԭ߀ z:ZQoQ&AGQL AuG)s&Q{^ßC([:Mbqy9w|ĞytL^(j^cgaχӹI5r,:^u K?1OjP uªsFmcG[o3#^=oHv2d槼Zӱ|>pnjz䉖n=qg*0Ve)8ھ%c4c1|,pj=Ќ>zVtc5퍅cu+ +`G0|CZP:l$>:DzT#z[wy0A o: W~Oj1|wl޸w/ɫY?dzڧѧ{5?Ht6ˀ 1^̼yzi~Ǫ35lڰطi/uѶߊZ>c^ VSY 5߮A'.nei==~?O=1ooZ6_Nh7~ !Nu`,ӻ틬xWlYv O)k>2c~b&wb߼LWityrFgF;oKxcL{)լ@@`e<&NŒ|p:wz3S^\zN!Wz l7Sm[v>cjR_L`]QGW1gW)Gy/=7|_v!4O 1p}ZR,FfTr4~nva)5b­g7lHM{9XDę\B2T(kdelYG&hxwɼ1DI/Zm|LaR^Osܞ< ر?6tYw"gQ^&?q='OOz21tvkQ;Uys͙?7JgJRj\9 0U<7$=:87(ͽ:aUp;il|ѧ .ɋxVMz_$`[:sn('WwgR >l ?yJa ?jq{d[lߏ)2vdћ)f^E>o\&֘,gnx?<>7Y/-<3nOOy_E׫~Vțc|(TЀ9*x88!FB@\{qwwwww)(@]Ki"H~g{dcÊSi4 I͓l8=xӣRNryV>+ijJQ ~1qcWV`*?jߒ/Nm`ÈlCtK;QPe]W ˷(˭6 ѷmqL %tq 8=[vߧÊ@&O[G5qe2U mI W0 7Wr' c`U.P,ojVagY㿖"\(z +IA=;{}0ARviB4Mؼ%M8?{MA{tZsJ/C@~ Tp2Gũ7+֢\ q,}k_̡֕L?q.͛ .AqދnS)K{w|W5%Kƽxk`y-zNGnK-wIсO^wMTN=H ҥ{nur:,'S. G={']h;FF |{L͞ ߋ,**3˧VjS{Uɷ7NQ򓯣zZ !5MVrIU<fj@yY+y\f [TBs NE6O<=cwD'8H8ַs:0gI25^¾E1&x C 3ʆo"ɬyHv.ocǬ9ǃeݣ Pmo 'lc")' 7w!ZM P[K";z9OaWt-!@@Lq2.TM>y]PƵ <֌(ɟM2?\g^|ri8㷌 ?[ʗϬmmXP-*\JbL(oTh0䌇gȽUܣŋr4pԏLߣG0Yjm(||huVy|kȟ<)V& *^Hy41*kr/IS TϬW,UrnrGq=8֤bqSgl7%*ܔ~ټd7~t?n 8t1<8^7;69(>o)J6P!>/B+#iAWXԦ8EgCD^0^'Gqto>qEN>"o*H .5:Pz/J%=c\_!G]x)]}jʋsЀrEA˸{G;WG_ߘG4y4^S80O̍ʒAQ8/6ci͊SxVW{A蒻yxQrR4fP$(b$4YbDēGH.GpgW-FAoܽ1t8Jx኎3[@?mUImF|MXv' u(OIp, hީgWXL 9Ch2/z0dQ=Q3a72EEy., mID,kJpVxyh,]f%`4Zy D^pp C 3#yG\fZp7PGդ -N}7TVo?SdƥI*-=+Ye˷-g$gy,>ɴrsFXt\yzȣnc<;=2[h,+Nɷ\z-m깒çv3}~~Ni Q/'9yt= q"/[qS?m%cIus0mGi/ $뿎гgG"| *hģgGL?*s{L<}]dFgRvM'D*Ad΅9]}ڃ^3z+v`t]<{?#-LDǽgtvYm;۷GO['S#V>̄V6_Ruo8mܔ!hp1Y|q lAk>ޢ(jd쎞?.n&Pc5)JTC m$Z$5C%:ȆDbAZu뫾AXߧ+~ŮicL&+~31}(ok6nA8Oh2($ +O9s&̮@ߕ?}kS׶/\G'Cq1Ws&s== hoT BڵW6E1}'P%zbAZYu&g-t<>S޽$3&=Z">ԨUDWLq .}Vz$ZF=OzjSLjځ<6n\ڮ<蒊G Kkb?cz,E-^~( gUgI]@b_?EB ɓݻoN0HAf|*b؏{vŰOP ]*,v¥m)sοK'_[ wa\eq3Φ T E}~{g\˩d~C^}W Ld ܼ];0^v"p-LZzlܟ9[`E}=~ԗeR銢|L'F/Oiuu bH^pn7<}X/w 5mаDi:Vzb%0μ iG]fw(q.mTdR,#ʧ$yB JZA&_~W1$tJ96 j~8K!L|p5 \T9Sr(Wx79bkeU ou*gorAllCpUGe|{3mQ+f.%W+޹ P{<{\bJDZzH~,-HK֒+ }7Y_mwm{CҢ$.lOϪV-ȕEeqN9ͩ?3PyԛӎΦiTЌ''acQZɆЬX~qCҊT% 4bgDp!;aƳ݋ŘN?|xGeKN_o*M[GҽaQgw2-E>Tܑi\{;g3r2+Fˇr ׏oqf:~^{ e~Gufw{gTBkd ^]T4(,tc :uڒ m9Ȯ8eŸ\uoɘ` T)y\ϑyy:u1y?Tw͉j;kfĽnn qQLNP ^Mأ-|7xעd΃>Yu֘=`s&P͛S ;.RR2\,gdEke3!M^ǯK:rI3L4xIiLUWU,>A.OL¹"O1R/rz鼞'7ƕO{Nf]5<]#⥒:U+#9u 4NdO֨'Wqjc G/vlzShӈ^sccS{ }x Z 5m ˇ(3Ch:y~!=?; ?nQ_O~.eAԘنz=hɻ<4.Ld0ά[5|Y\Vy/%>SGrq&U`oԃʣFAJNuҺw(7f_TmC duF_p'z;MȬ-*~d; ҮHFqپG196끟 @Q<=ѥk `eamXF P{&/3Zȏdxu ,N]ΜK"Clx,CۈboF+ҵ/ Ze+Ìu/DvwިgF ZEY~fT׏6:,e;\f[uFKw8Y|$} 6Sj6<ТQ,V1Ԅ%*ל"K2»Iw7^Bkd3YSa wnOզNIA;0ٯM QΌjҍVW\K]j4iz{yW}p≮GΞ]w#*aZn>91XI s[݇)^pmd%ƍG?: Iw^"bS!VZ0?!L<跌>mrŚ%!-R1ֽ]'n\#=,FƓZ#P#\S,gf,Nq v>Zu^ 0uD=/ñ>!5l?ܐy0j35Zb^d,:51f2ƳH}l(;>pF/IqᘹQ}ZtHQO5}sk,5ۘڠ&S5Z:ç(gyekC/}\|L[@:BʯdhRsV_V1j^DhhFAeS kf3aƵZS=;r(YSJHXоӍt b{ 6f+6JQ~X㱣?Dbb*R2{BA)\mUY`M5eީMs`bL嚂ք?%3K{Ja U&maBq܏-9"0cb>[`LL5X.g6Oeȿy,knJM~\űlm`U i'?'dr ѥU}\ GĢvZcL:Ej9CϢc݇(< jJ؂+-xNʲG0t燑ԫI^G#4dzeW#K贠'sbOi3%dB6%_}f鲉[k#5ͬ\X7xR~M:2}oS{EhMc2vŘZIp;*,|۳G.zTY%y;nemk}HS 3k$n,~)-F$8Μ2<-A_/-|tRaX_ C8b ?|lDR6DyьάMژl+2| G7k )c1B^A?ᠽCO:25=&>[}u&>!<|J+RI655 1h ɵƒ_>*HUD3i4+O IDATXbjcnؒm8G?|kC%#6x K@!T QAؓ~IȷsyA:K^nMvu3}5 gΜ?i@4bw74Ao]N|eIJ|-=# #*n|)4=h4ݻիuRҌ?EN^ǘsyt'!юE"~W/.崡r] q ITz/ uR)KK) NSZ=WNh})#`\2@Ka!BO8'6nK Σ"1عlkfY)gH=1֮#3*<9i Xw r B_qC_ahK2 ^ޟ쿟nB!zwqW4 WowƩݚ`>QRo!=ƾwOި0Ŀ\v0ՅB'5E&ZÞ]R\rATd.o-I$3gmt⻒.D.ӷMF>]_#$߅'WZ'/I~Yi#eHJ|-B|\RB|rp522K?A˒$O)/GVk| 粐: Bd=ǚ|k֬VB!"j5UTIdyaZ'C!_HڵH "{ժUB!BoOѢE: B!BBBBR C!B!B![\B!B!BTB!B!BWB!B!"$*B!B! U!B!B!H !B!B!D*HpU!B!B!RAB!B!B \B!B!BTB!B!BWB!B!"$*B!B! U!B!B!H !B!B!D*HpU!B!B!RAB!B!B \B!B!BTKڵ+v-B!B! J*IHSi\xxxu2D4)E#}H^zIB!B|EGI$4 *T*ErI'<)EC!B!Dz U+)E#}B!B$*`E"呾Hy/RB!B!Қ: B!B!"*␞`鋔G"呾Hy!B!HkisqM~^-wdϞUKtzXbbb6^WĴ]y3c)fc߰c~Ŝ]wTPg8dbE_)l= ,?γeֿ}@Hs7̜6;s%r54 lӬ(??dJSJIuH~̘g4&75zFdw8EϼϙLfpL','!B!IӞC7iCm[k~*ӓ?֏~GP/^i 3 F>ݘ3h7%cLٽYy l۱ixDVsYk,kv|@lFkKv j m; _ ^4a& л]وlj{q~(%["A Q,#.TJZU41JF"-Qp Ѡyۢo:FRB!BsUX7v-"| gVk,qYs,7fĹ4-gX)[>ς!)㈵E&ƐwbCWk2eE?r.M{%Ojk P JWmƠ?!=-:EdzlFcf{ΕyrsR4Wa"E(~+%D^ 3Kr6guEyǭFҰ6V{jconR﬙079E+a-XZcDreR9]֒r*39~`Ķx,s:F%[X{kLػfUwȎyIUulUwu*P7KjKW F$JҌ yM"b`EFN;VUE; ƺ'WRr`ge3'BQ=;Ε)nG&k'UÚ+ocSN؝ s,(E'2},)0ԇU¶Βs[/sEo'Ydƥ` lw(Ybّp J[XS}Ø^'⬟>[)RRO"Ow+OdZT*[Ju2hj/J}͔ ;fRB0붦R.#d>B!B\U@E,>ݛ0c DƇYȀ{.Fa\0^}) {]G#4} =4#s8_3 ]`oZ&TTvq`_F "ӃՍ::{*YdIdP"zKs57oxSPԀƅfDzX'ڷA yH"""xJAVyl+J[L\ {GNK{RF;bXG^Aau`}Q=Dِ*=>N݄}TGe{ucSդ}0g `@&N'7MtSaRfMLܗz.US"sj-*O@r9R0ӟ`ϕH yiw8p(jsn$PP Qzn{tjI:tDrqB1'6ű:S@KZw%,v 7f;u} h-jpe&PV*cJ=OE7ϹH~ +jpNڍWi-̭ *A.*coKiN7%֓(3 a۶tȁ&=d D)-QD-3F5V*0} 玢7]ai`A"\bfI:TS.ȏ6t=_|]bg;яv0uIl[zt&\QZF_ԹN\CӡՆs%s0\6tt1 _"3ׅϪB(g=v͗ !KݹYRYQ*u PUGQPTf`8[dC?NAw Ź:n{a*Zn0^x)U9SII[Ά?aE;72saaZcĽ֍pgcE()=CwU?yu}3&(̎Ć} `CY[f8V`"d΢9Ԟ-a( FlX όxH':֡.3Lq !q%SFSQ5%woAPGo.]/X6?w?ukF-7.=WA1-K9<- zѼxv"s>̓ɡI(}ʴ,[4i3&؏ҨiÖ0#.$mñ2Y3FQVD:mȔs3q s"]V[o{5A;{Yzz?#Ъz.MKӲQvhE }ל7|\uT| 1Γ4C *s% ЪqoЙE_ O;W%w&5nFRslyӑ.q_*M=[b~&v^KdL3٧x(u/>K?x~bW~5eY˚Y7~> e}VU]B!%$DߝVjNb>ڂʡa̰I&j3gqg0:Qcśʃ*CW?[Kw@16\#64Yۍh>~+=x~! Rcu'd`IP2`Q )f:!g1|ޚP4#B7`6#Z,b|QZZg̒ »I0q#HapF8_YV OԨq߁*Ʀ5?ߝf<ňZ/6GXW} ~Ϊ}Fύ6B05yS:ӣw[:XW ~Vh v|7]\[c>^Еc#ИZ3j٣/g8W5QءSy5`^%m h›#.QG {_RŒdʊyu1a ظkX|6} ٙS, }bl9 Q-J B!B'Xo͚5Uvk.|}}?ĿV|2nnni.|o9F];3EѯC{k3ίmyFڂJ^nHn=0JqCmy|<'u, !B!7GTRi.$$ڵkN@D)2$=WqKRsAxT=yQ_}-{;.P2=B!B!DzsUMa^4N/*Va95YWcQ2|nØJbCj=/)|> [Id "m`Ҕ՜~=:}% {| !B!ב{ask'2hPb휗2Fru«D5jFq)AݞWXEfQ5s04Z\~kA&52|O^{6 $Hݟj`ZFo;+bFxb|ΝU=85Y,?B!B$I/-wpU/BΫ#g^ЛY?m2&Zċ +x  ;ʛ'~~]؟r aVTC (r6޾>Ǖ-c:ET9!\EM#zӨ H`-Ck} y'v,c^_>ͫ:cb鄗ƈ8\$s]} SaÀ.앝 /γr:,Jx[+0 &oNs3NXL /.X9-p6̣ ހ#Ъ`p1CWaRFeVg Gs9=:WZ| ob%?kx'rI$2g4C&3 i"C52/|`~iwr1-B!B4դsPhu IDAT@T)fM,\sfQU* 4aZh sgS`??J=u窶c̝Yҷf@i';)!h)eƳhJ\9w jO-±yo/7ԥl4MW˼/@ya3v5?(ɰ8]~V7<Ù{:JfWdBֆ2an*gU>,s [GWu[#k!]Vij_6S'],+76b֩KD͐t{ؿSw壗p)ΏqB$w踻'Wtϔڈ i$Cy=(8|"b&q{sq%LdWSFLlI>RgAr qli(z -vOyl9y0c;r5ö%<>dߓF"vY[$-=*Zj m)bUjiHXk$""L2s$WOǃssf1sQT;&K;XƸ%8~-RxrcVyp)ѐJB!B7fUv)/yYN{Vm ig~Qn]Ja_7ƷA-lӺ_<ȃ')OdDcc~ V+k^^='[p2wqzocA ==n+VPYN_ȱ%C\,T7{?i[m 9`$QVu86\/D@ʼQݟ3f9O;h(s3We\+,r;i5% qhН혶q?wdk?Z Ĝ_"}#Jtm3#Y+$A*ܓ~!mɓָv>Ù3g8{%i>^A_s:Tv1r4&CsBVogϨaN^B!BlCgK4PR4#篐AX0E^] =6X՞f5qI!!:4ȹ"E(xWqGV ՅuAmwN-`{x\[܏1:~?Jl[ +h] |gh(Pn kջ:RrNT 6/eM7F,(V#(>Ft1Ε cvw/_F84Q_^ M;h#S;V[W2Rxf@1 ':_O&s N0EFϩ)5ǯ&Vg4* Vtޓ֥ Kd6m+S n5)͟iQwx.bȑE2v~kY5j\0S+RUO2e3xag23AVP15/3(؝sa1sմb鎈'B!E~fi ZSg!wof+8[D8y$"y4:c^l{e@g囧X3ԇCSw>wBGQ֚Ul[ɾ͋*ŀ:Xr5s|+OK# PmzP))hR:'">9 h^M1^ Dߊ`MUrAmnJ6zr#>D䡝 m%uσ {Q: )=;5aލqz) ;a@;\Ue}q+q*٘ʙZL330338ݣNx@ɓѝ%*k{3qQh=Ƒ4ש qS90Ymٷb oEc .B?|$B!B$^*06/5VԢԬV_ҭ;uixxaYWC7¡xiʖ j+R5#z,$ OV4sWәTSse kQ/FI b4܏j1-7o+s}K'gkБA Бx[LUǙh\v*aXSw鵀<2 PK V/^*3TZcbb H k ƎܽgII<[sƶѵ?2J&tJˬ\ZwkX j/zx{hqKغq(O`rٴ;EO}Œ ?ݒ`DŽi}D`S0`PUPr A*bK˯R`!9!B!x-eAJ{yQDEqrQa%\oI2F|o/^D s s$fKIܽ(ḵs ];{T`AʡB!B!rG\ȣ' gJ k]rT]?}ar0:dOdUwN⠱]r<7ً]c5*\C3O%iTNΎlkv:nJèy52mgNrv~ˢoEi4#10T1(Ka{cRn/csK/im Q"{u֮0%;bBNCٿt2= j[!kshf}Dt )Iݕzs! nyDL|<w39-\צqoȢ݋w0~!:nHƳD)bt9 `qD\2;1ˣ/.oPyxklޏ]6*Y:qmbkXʦZuexWATv1q߽]٠X9h +8ו~ⷂ~3Q_Λ0@fOG:FvF!dn&8ѤmU<+d2&U[14،Onhژئe~踠C8U]I:x)eB!B\g+W3 Y]A u=XdU}R$z_+glP02'UZOcGp)>ؔۂ-{WMm4ˇv3Yk{(]>̥_{/5v=q/q'1S”هe+.tH~oD NxTiJ u3.0C-fƺPʻ6DпCE,RIMIEqr&Pr )|<OVb2xMhZjf+|߶3'2q&8)K͞_dE_sID~xPSSHŘ|,c'[CM=+ʚO3¶@)|:ӄ>jBz #B d5я4.Oې6tfbѐ&`&l`kR ܰѣҍwS1yt{ {E,~ސfw W+^x i>;Ad5ʕׇxG:CKS.._^B!B{vAzrƍi߾}_gedW]@咎qՒ`shx;4wb۝yMtIqr˭.B!!mK]m^+Rf!nUrxGGrW@Ա{Ӧcgeh8`R+#B!IDޠ5?rVUN0Z%2Bs E(!B!Y@!B!B!^,(B!B!/A*WB!B!%jUB!B!o\ W322rsB!B!BҌrs 2227!!㑷x-2yB!Bܔj@@7n77!Cd<E#o[!B. Z !B!B!KpU!B!B!^BB!B!BpU!B!B!^, B!B!/A*WB!B!%HB!B!B\B(/l®Tcɇ>!_I_%͝1SqlCpnrwB!BnU!o 12C~$Qkz->>Or*U*a$2.ob̵L0Ƹ8y8| !B!+WWQMh4Lp,V&G9IaFTA1ڑ1ho>:kbZa rhp 0.̑N} C"96E|<0*`ͼz(M[~{s1TYKOuņ3Ŷfrrsq{w(=VcO}O +SSoRs jF9+L(R}&͓!QOmpk*gOz/"1Nu?B۴vM]3Lh2z\̇)hO/._ei׾gkjPT|MCv}ք.[0~nzđ},YI?֌d.;[DGX6I}b~IwWfg{f-8+]גD|9&S̙{`]&` wΒe&Bs_.:oZ@#6hNU:ٺgW?ʙMR&Uj\Z9f.Rc73䛺# eN' k?:ũhWvE{oH? 1?/e#wDSP~z[Vd>+iKў~;/x*VB9.qy_m Y΅ V pU;|EA \cA̦'HMoc,1ێp)ޔb5;1nvJem'zt.`ۤl`8?M@w`E0m[ۺhea[ג.U'1ijQ Gnbo IT !B!Dnzc\9`QIykə'lehT@MQ!_:Z%n^Tg9q(in$Vh&"ߞ۩rP}j3gJ]{}lǂuq\۝.,އ#M+ |$Ǽ1iM"K솫=o9D o'I.C9LAma܋kk+g<89m9|+YenpWoK֤6j͎CEv1Ӷ>F$jr>Ijw#"^Vi4깙2y\(x,zn ihН혶q?wdk?Z Ĝ_"}#Jtm3#Y$A*ܓ~!mɓָv>Ù3g8{%i>^A_s:Tv1r4&CsBVogϨaN^B!B|=Ak:=!qT0M+dP8Ⱨks>Xa3+fX8m ]ѭI֩q#bbQ -pΏz;(j>鶈~`yOrʿ,b( $L/+~ՙkWbsJMXA"ȀP;S6k6.GjCWXυ/6M,4E1+@ hߟ'|+ɡ2 a}B,lV<2XR ~9KMQ^)~ )IFt1Ε cvw/_F8\GΨ)5ǯ&Vg4* Vtޓ֥ Kd6m+S n5)͟iQwx.bȑE2v~kY5j\0S+RUO2e)p3Refv/ck6^fP;疇?Lc挫ioGO!B!1^d65!)r'xfF{mŐOj zu*BW3˧Cjcek OȪJU!_*,5- bf{nZw[ĂvL +MR~ :NmIXD$6/"LcլUSr~P{EA{+W$6_ۿsq1ukMY+p_ϱħ0m3~W֬B?Gͻ$%p^Yxc/hcߐR8cooSe{2s@ ޴ l_%/*7D؆d7#Ů*?[،C7TL=TΜ8b9aff{ Ğ<YB|7ou܎CoJx/Op:1CJ`զ}+ V4R8{"A|!B!K2{cq1\cEM-Jjs'SG8fF@zbXam8 wqΧv\KT:bs~;`VKℋz.Mb\q%mNϡ|+Jl˳5dȠGsX-EVHԪL[[f.;EIP86bϠݩҮ_$iG;- 3w|~Kiݗ~[pll9IHLbϩT̉F>\|ώ&T āNi[\\V!߭b6̫'C3…*wNK~LlPR 0*7MS4W,@- ~L?ֶٷ`S0`PUPrzy9GSJ IDAT*j?!Mؾ2S /,B!B". XE4^n pPm.sg3z W3>HOLXZ(1^}0__Xbe T{sMO%-/w ^z^u1F ;ec`OȒ?9FQm=SYY.se9 EAZR[Rz*OE;9gAɒpQjT+rdPk{㋇l`5'YfRuBAYxdžͨF/W˴cDO_f{

      %UQM+ W1Zzn]8CX7ֳ-ZɶHЦyPK}f2`S=Ⱦ^7C ׏ *M >U3c71tWt3PB#mF?w-'5%ə| c7E4Ij{*/::` Qn{w#DR}]´.p!X(M3o|s7F\;jR1&C|Vb2xMhZjfK*&jVYqeTք~J1&U{LJ'~pyچs5|65'3a{[{bXEؔn,{xΦ^+ fO\̀6I6r8[i:s"z0v|{)^k8߯τacd;۟ZŹ5 md02ɀC!kpB!"(@>2C7l7#u޽~gڵkMdd$w&./h-nx7^ (W^>D -Mw;|p>y}!Bٱcnr7n}}@햑_=_tYK:KntVKWNϥQtb\$QjCbbDFnw5%GB!BSސe[Ͷ%ˮ6Ei)37*Z9sX##FSh+L Xʽiӱ24]0B!BwY"oFA+ɪx'S-ݏ̈́F_B!Bw, B!B!/AaB!B!B B!B!Br5\B!B!B7S{!B!B!xiFÇϐ[d<EC!B!Dnp5 7뛛DFFx!2yG"㑷H-B!xB!B!%H*B!B!/!WU!B!B!xSI*B!B!/AB!B!B B!B!BrU!B!B!^T !xKdptVVXYYaW}*g |鯤Nf۩ܿ86Yۡq87 ;!B!x7HB^]!l?~5[9*J˿ey_Ak71}ZN&Sc^N<IMIG>]B!ȃګ(&Z4 FV8@n$0#aՠafHu47RpٵL10ci9ev H_w! K">tyx0f^=SE-Dy9+ |S_FP;`Pz6,GA3LߛroP$fX/Cr9Ù{މEl0t³q_~ =05\77ҩ-̽ MN㑏?^53Lh2ʃt9(Sў_\Ү}O2x 8[3GV; J8aejmJh1N'~.❛+_mB!B!QnZ%PֹlXX<1 U3sT Z}pE#F1w.śRf'CI-DNl;~T i vMpk[w 5qKvqZ2֥04-A j!A R-!I}~B!B;J_Kμ,}'=a+6Fjbt U|R֩*q0j r8ΉGIsG=')-TE{0UCWYܽG,cqWIybӚE4JD2gdipXBg9}1onuc|Iuǿx ONGct_'hV+, ?b\cސ<<9cqH_<.fh~DB/IaD*B=7sZƵ"#XQ ,{1DGl4nǴ{)&[jȏ$Ԧt=mgP%-k Z$6HT|v6.oOdƕGΜ96+iDMnN) {1wP?ӥq6z;}F tB!B\g+W0w8[B2W6._ 9 GjYիwIWu=FS;5N8)$DǒXϲa࣊mlK3[bLzx fDρoPe$(͐b&O:_O&s c*` ^ҵx:W.ݽ|>2^{ÏkhfJ ՙ1J§]u)f/٠ dۊԳ‚[{| jJgZK/rdx]`ZFMħ}b>+Ԋx+3}U,SL33n`FYET~ ` v0vtt̜q5X# !B!r5F&6/ϐChoOCB< -6&9i]v /6'>!*Uғw!PQ~ :NmIXD$6/"Lc5OeбoH)ީ nU9qz) ;a@;\;9 Dߊ`MUrAmnJ6z*;EsV&56)U1S?@%3'ΡffaffgpG?bO,!>VYۛa7hnG!7DQN]\_'8ʁ!%j̾f}+)=u.|!B!ě#^*06/5VԢԬVN@"VX[+(-]36ҵ%k)X5(ƒ86$l_[:<^C Zp|4RleL:DeStT #ŚJLDߎGomIiև%>.8) DǦYa!*Θbrx/>Úij|2.jo$MG;gpo++J挶U2+W5x28.lü|r1$? 1ë`]0v\=ol8.C1A-~J#0`6NS_$O*1a|Zf^MASAUA)}N0--OHm7/1TË/#B!ȳ()EeƹT^"P0g7g| #,Pc aX7Vc \%A"lWo皞JZ _vEHHRm=SYϨƳg=fax4K~c\YeB^F~ k@u{SuTIjǓjZņJ~^X]Z2^Mڻ_ҫ0[Pi`Vn9';\{m hs s$fKIܽ(ḵs ];{T`Az7B!BH\ȣ' gJ k]rT]?}ar0:8+äUV J3. --X$S'7eJPm}XO*xD,5gK]aJwĄ$ϝ)*)`.dV%zԶBK!9.;Q;W1o.RfJYuR+BT,2|R.MrXwv8ݠ.6<ōQ-K`s7RrŞjBz #B d5я4.Oې6tfbѐ&`&l`kR ܰѣҍwS1yt{ {E,~ސfw xuEbs}<҉ZzGpawp}B!ݳcիx6nH' =-#-Wz-tԭKG04޹IkK#6&!B!x!mK]m^+Rf!nUrxGGrW@Ա{˗{?+CqZ!B!xwH%o}kw1B/xLh.E!B!xȲB!B!BdF!B!B!x R*B!B!/!WU^j,B!B!x3jB!B!Bf;?|0 E#o[d<B!B\ Wظq# MddG"㑷x-2yޝtqd&Dv%b !vAlVk[KՔR{k_*%XKE oA!Add$«ܟ2gefo!B!ćH.h%B!B!B!B!B/dk*B!B!lsU!B!B~:uܹs=%K|#l WeZ!B!B!?N"555k9rӧOg[*!B!B!Tll,ʕ{)_<111o|9/"B!B!B,uΝi^T !xOP/l,-ĶN^ˇL dL27 o4kB!BVۗ_~ɴiSٽB!' }R|$P5{L8͖9ilqY[j:&O &]B!9ū b2yw"Fq IDATXYޅ25AV1,#||OI 2Q5(TgJSM gg1 fim;Zù ;ܑ.֘rģQ #?6Rf|M9YتnYzQKzCI}] /Lm5U?y*x/{2w5 S<͆^džD\ ;?uia':14R3 VPY15Seɦ|+eq*L1_&׃;m4;3_sPOݍsst}k.jNL8=#Lmy?& $7fFRa6olqB+~tj@Mz;LMWѿlԍkO;;޲#RӫƺRYA2>8˦\dLiMaWg(Fs6'(B!aÆ1y>tPF# ʆQl W_FeSժj5y- .ĂUtT,HE.rT)R Z铮LoQ/D0S<)Ξx'lu53CqGe/Lى5,k{ Ԑ!tβS#UߗҭY&kpgӵ6NbNDgB02#*4Mqq,҈oa1:AsӬ([P9LhF7]bq~ru>Qv`'k{=^6.nsv4v>÷^B.*8<~ gˎgMҙbsgIT_+|s;3{H %]GI8S<"iO5K8'fnj0S'uoʽ2q5:2rxץ0s2Geӑ$3`6[&cL`x!7uŞipG.?ĪD]z&?cpd~=tD+wKyHB!"F"((腏>T &%ޙ9Wնت_}.9sO{FDfB"'`*{_l9/\ 0iccYũ}SsTj Zϥ#lQxʊ^]7;L ѶaRNƾ3pm^6kC ,kJ9!4CX^fBOceڊC N8٭S%6.\rܮ3Υ.K~31~fVe+B!^v*1c0ac|2-e}&R6G֖LYH*Po2sр(ٛea DEQI##Ύ*b#H@4`QBE^B'Ӭԓ=sR 2zgbʴ.XoĤ?@Mj):SRkp T1\tEORr/%IuQ!z \ktb3D0vڟbVn?BBiʾAwӢ^ahv5n >LNHM|@|1 +KLeƿ黟yx&3YD3t1 i'G6͞ w%A1ƨ]XZ]up0pv5 `үkPwJ ~I <4~^feO0acR5Qv$ǖ~E>WE^L;3 n-SfҨL.?+C"0؝KfONCW*E['B!ea6lO,%$$Pe6l:Msf8 ,FmXx)mj+~Sqw/riКj5\U1Cдd/]\;0)n܇Օ&A\رB҂G%8С2v*&wl>UlK`ogvuʤ_ļP{o >#oa@?|{Qfh~2_g7RVgqn#c%7bB=8q4 hjCӡY1xFù?*uXL>H9ɡdjס+qc`1,ٵ`T"Gb$SA)%B!B;^Tܷ)^* ge~ҙIQ tv9CMRԣH{xz弩U.ӎiUqQKdTͭ vN<>_d^ͬ,p]>KY ,I1UO*Ͱ?2F6xVų"5k;Y5ShTV 'z%] VDH*T+Au.Wę^i*&QU{2Tv+CZ>X\r峩+s@tƘ*:ӏSͧ;njڙMߐi?:&j ? #\V̪MMSI?UX,-}ȓr:!j02~MF !)wPC_N*tOև{&c{5#FEUf! QEAQ=eT{ Zi)WG!B!r ?;4}U9vZUEE_&qZ%dr%a h(痞G"tfRP ˷%CSålNe(PaՇ8Iδkeݮ){fRU&'mgC |وf{XIqrEUش17\{ҵZ,8)|V:u&ڝrZ6ݜ@\oʞ(lqʚ_em\|т.{8u9Ll P,cuS.&))3s2+Q*5܋`Ϣq,A`X"Μ&y~_άH;Cʧ)X:<h~ODUX3䗠ۚok#77MaBT,EJ4-NjZQE;N?u)\a۔٣fM}4T,\`D/I>O<X7.n dVF}khmASlOcڢ*=泻u<ԎZ⮞$ƽ]Zt<еKӧ+}2g)}} v@s oV_0(;KU >Z#[Wƣ|L-fRs 7?}<ԅV~QlR9aJϚSCi?_pTv`_B!BTj t$p ~NsU88Zpc['Tm<ГS+/ U=֥И[A:Sj۪|u?g4kFQ%ktfԨ?NcŤSUS^Ep濓gr})Z 7=,dj3%*ԢkWǫщv07G >_aC]7N4wa*LG^ةT5ygccj5[l!+sːڶm8 )noĴ=0^(BokJ \3 c|%M}ffVΝ]9EDLL7B!BmL St6Y| 9 ADèڲ ;7?2ٳ:>>iמ^=ѩFW>Y*?-`Jz>x.y&n/ , F ĚgrrLԓr֪L:aMp 9}<[9# 0-|1B&0k뛺`bϴA\#bU.=`x 18p2B;ޥ ݶ!5k36͡7o a{ï Y2>=xj+'8qGfNL8?WOs]dr8~o.Y?K[%B!Bd_g}&R6G֖LYH*Po2sр(ٛea DEQI##Ύ*b#H@ę Z֥RFc{rB8}>dEP 5ę Pm/ïwfM/vL[)KiҍFL 3~xo}+Ew?Z"kv;3q|ttνq8kiGn=LMHԚDX IDAT'g"gc^+}B1.eI$hQWL9Us0RlL@Go~g4I`LkV#⌦0]{słn~hIǑMqswzߙQc wfxb,҂.{:Z8{֚iԵ;v%gzԇ$hoO?/ P 3β'1Ts(;cKSi"/Ky)3NQiYvwA xU͟! %3j_'!툫aƢB!Bekb03I&f- ^a2Q;na9vhg5z>}r{5_>P={v~*8pX*۰RUA/*cRar˖#*ޤHnĨpߕVƫJoYlSgJ\S[5btLWVCCšvnqi\yg/hgaggccfeX㋘jOmp~i$-VhOy/*6 \"S*| 7mdl#DFLB'F}Mut:t: <@~ Xa3sHnD $’q]Wr;5('bXk۩D^H"R>xк;|8s̨mb5H/&qɯ^RH+ʃo֌ބlZ/HU)b nlIow1R)ptLN\UP e%bVnjJª`Al;F Pc^uؗ1k2ghM/X;2\uRݧ{R>36۫o1* 2{ylh2}-ƴO#B!ȱ)*W~JzyU LNL#|L8rΏn/ /SHDL@UJĥ?nͩS-j )YΕԧJǰ jBX%}qZm+')Jigg//aV+K)Tشñ'9Nxx8Gw08ΊRV)SǸQ~SB}K?yP\<' r* ƛnĸ~`5GYQaS}|Zi=ڤ84hJwkp+]\G8p FNru+,C|>ϥs<\*F%܊6hL'o25ĵ<}1Y@AR.LN00G4^xZ>X\2Y8E˗c[vC˅B!"'ɱib޴mT֟A-?O&B߇ٳ>$}] g eTT<}ZrL מtm%> G eݨ vg*@ܑ3N@OG(",PU qe-:5Fg4,jݰL M^Zaք?%Wq־s|[̿Ll P,cuS.&))3s2+Q*5܋`Ϣq,A`hPaSUCtSM=Zo&ؠ"SgY=6^GRtļld˙7#UD 5kմk6<Ήb%ŏSOJI-R _q``(9pAC<* iRϱ{+֍>[)QDZZ8F|/۬}冃.똶2f&v|ݢ'qF֨,p0O!tRJe YJ`_rfP\䄾yUWLi> NzRH֕(_y=,}\eCMOOup>_hGR51PmW~#<A !B!D87(P*gVRRRRe%66VYj8eN19]bgUe_nE[3+CJ(ZJAVLs+=k)}9SwSMSP+ZR(Z ;%jʮ ˻^R1V|3 6(.T[{+yr)Zs޵YY^uVB90NMRj6S*ZSJTWNZ3>gV;po)s<=(Ɋ(k&J܊FS?)'>TpO3=0Ty~/7WBG=F 6)v*TB|<rGI)ȧB!B9r5 aejZFcfkYwG̟7el13QVkY9PLbǕΤcz,vs *w?#]1G@VG$՗&*Tofugq#*3jars_mYU&EnIۦ{m48ڊ#7vѫAi[0̋'9|/'KzqaS/؝$mkޚ)&oނuOffp}2`L9̏C)\zٺoo`>-'];"s˫Uʊ_ 8~XT:\)Rԛ؅4ӢGmo@Ͼq,^yW!6k;B!BR\x敫FD&d?l!ˇY3=6Cz= +s;|泤W1p.ԦyQJM.^w1 "9w)zWC8;:ljZX{l;IU;ے)|j$N]1S|eWI ǯ IVJ69(wѧqOvxf乹Q}5G5'e)qScy0Ku>;X*b6.`ut Y5PsЌNϧ{wʼnQDK*<ʹ_"!?l~B\L,KS\Y#u\Ώo%-;cS7YIg!l^ %Q} Fn]ά!'XBw%XOx>, F ĚgrrLԓr֙(3Pfn:ąfiW\@ 1MGndπl 2Y\c{ bmu9nÛLV6."B!Bޙ9Wնت_}.9sO{FDf.hFQ"iq;e&[z 2éLژXj.oqju2K?@\:Eሧ(Z<,u#}*#Q;!6L ɖ1ؗ~f}.0mCCkfvm%tm5Ci3p!`/~u3!KӧZm!~'NVة w wbxLnׁWХQ?b3IuDB!Bl8/aC$vRfh2)k¡3I%*Mf׷`.`4P%{lX=H;8tvQEldɀiVdf ޙ5ة2m.K71)PZua\'U .&Eѓy2$50DokN >2Vfg2 \任%&G_1uTlwDjⓍi?4XXYb8OgX=ӚUH8)L\ ۻ vqdSo\\F=wfްKK^.&lZ5u-*1N<]?43 [KTŒl &8l Uưz"ʎү(VZKRigfĭeʌSTu]Pe~geHsz ɴwH;jXh7B!B /HL;mR5yK2p;׬gX,5Վwp`{$ڙ}ެ2۫zyR-׋۩l3AvO`0nKiW[~҈|ɗK<U᪒&~:wځL w><[]P[_d|@Cšvnqi\yg/hgaggccfeX㋘jOmp~ag$-VhOy/*6 \"S*| 7mdl#DFLB'F}Mut:t: <@~ Xa3sHnD $’q]¯K`jQO⏁ŰHSgׂ%SDN;|(_!B!rxA4 X?cYIgf&FՂq4YKQ"h jJԖVO;WGU,QI>3LsdKY ,I1UO*Ͱ?2F6xVų"5k;Y5ShTV 'z%] VDH*T+Au.Wę^i*&IUf®嬍*K`V)b nlILyT \*W6(N/+9jtSS-ݓ(a9Ju~3QQ@{ wEeC0Sv.h1u_}Z!B!oE@+?%pQ{QI֟s?%a h(痞G"tfRP ˷%ػ(6]AHQw VXEvc5 A%11DQcK,h[bQhP11Ƹ(9ٹw3w` bUK.r*%_]q=%u.?pK[JK}EKI=r=2m{?+3HBRH>3ѩnEmISF/EQEW?Oy; 2=X5 {{d [; P/5{DaPs3-g!xٔALW-)U88U[oz=qi05ʔ!a eD<Ͽ|(WRW{vsU_gߚ,,QIʒjj >G~q;6 &iTU!B!*V>MMfг}h=6Th,p)C6T }V!u Cfw=s櫯RνIi͸qsY8x iLfH&#|5c>g'avrcBNd`;ςv5F ەIB4T.UJ w= IDATb0~C!)iuAE]mܟ&Vrx)ӿ8ϭL»Xtz={/P)y]2&Yg[lD7^FyagMplUKGiN# KP\~؇bj`eđahq(T֡TU>ܒS3qk`Xܩ]Yr' <Ũ/9(]+S6F85*lSM<.6_?=#b63 JSs|fё_b@8[Y`$ ic~z_-[*9JIޒJ4'KuL~&O/Zs.UtO3Yy\}d_p> (CagG?;Ͽ-]%mzW㘻,)G!GtYgjz`cn}~Ja¶[}Sh;iq˼EݰB!B1fWH#7} qWnNm&z? +ܽÉ g>b #r+H1ܸG{y#_, ½Lڙ>z2PF4WN|uN-`t;EPzI8~?iNslz0HM{ܙ&0w; E|޺ FmfDl66vğٚcr,獽!'(B!-cp=^5 Z h^ˊ%]yg.\2vA :œNWcuGE|;-]&O2oݵ;IU|?gܦY6~X\(AK8e 5bo@^oپiy1 Ac7;`%j&,(W5\.&5['d<;>t<wq5[4f@'֌ϧ{$J)lH(.t34 n\~_7>$&('S Q.Ǟ%nq{&_FrxU2nw'x/I/*^ :]R88&>sГ)jέbUZ g@I:u+4b q2?#B[S@u8}yQPԬ_8x{*`>R3?c{r'|Rބw3.E'sX5?UTvT6 |l7_(GWևCˉ\ @O{X-_X (&&3q$Mkԝԅ'40rW1Ct\3X0܍O̓@\lBʵRru);?ezL7?@djt1_qju{2K?`ύ\9-2vwTxϏrv=w̜ik^Qmvls?Z ImSmr ?=le0&GҥDeN5o$ Nh|V"CִH:ӧ9GҔS/R6KZ^x3\sl04%tV6CB!BWE)^K$A|Qi*3OC|pĹXS,3ٝ βd&l;ky8b '&& k~\-+|)܊5zGWhm+P4W2>N,I (w/pᮚ+mqM*x<:G{KBp|A$ ;F%)P=Xx}?|1oT+7X^$-K ~]a2I$&&6a/~MrhBY i74Ũ}˷fGG4&]MQy ̩1aaECo<_9MYbjTf,Z;WS6li' *Mߣj@g嵬9J,}KƳ~> ~R+.TV(^J·&)[9 'bVUkHVG]B3sfb3ռ.uwc8Oq!B!xk^d\6܎|%ߓ62ٿjӐpG;3JFG+ [ym3Yx;⹗4)]z俔T;`ؘ94g.R/|FGй{{HF&ph;1bo=o܃NHɣ ݆W2״(و{o2?㚒|.ٟffp &qbZW`_ GjՈ]Xv*åwHNpBNZ )#8 N-c' h >o`HTPJp\a㍷-Cw63?mJ⍘v 4N?FM, ޿̈́gߤ Ğ8-BdoM\m=ʡp]b/KqN%o2i؂;:n߸9yJVI|!B!Ir 2Vq3\eENO+BjiO#S|<I $a S3Bz|kFfć5J-b P 3 |K7W5(I$$MAh({Ӂx5 6@*k`óO=sz7 `c蛻p:6QV)\KUfmWn{ft\ʖ tܾy&W7ښ?,9A7J&Y3fh{Sjewg,@eBcbCc[\ze3kgJ QFڞb:} Z__nff $3 51ѱ5vuW!T&zQH Д̆(WYUޒ(`Z{>j  c^Ti^Bk=caL1dlN18B!B!ވ{u|˖,%<#x\iYeǦLrC'%q+T)e ܮ|y.-KۉIB*]WT2yałCI`em P9I O<0`ecH0'S "L ])DwzDo 8ʓzFk{e<~WvgJZ}fSS;܊ڒ˧^~??ڹ,oŚ^m,% ;^f8c *=*C3l87hB,^GG8xmgAgqϴ(VgS3io\ITWmiXPLpz^FTr%5|g7WD1K+KDϲI ܽzJWiqsB!BȱYizd*wD& SIDnKt}K;W WA}N;[Bszd~ VYcc I(pi1S35x(y5|B}DܞT$(OU0RM3)Uamc ɉ$̩<*?7fb}w05|ndT!L ❵:KZC!ݝ1#gpҩ2%A%.$BZ<s,+Ys:BF$R:c%4\g/^ <ͷa85̸w/^MEmq~3pŻ7 yfN̚ "hW_DE CӬ-q{ŮQ:7 |`o6&LݏKup~Ŕ{lяv(뎳E:qn`XL%sv .hѝafJʵ_JjvlqLޅ*1 B'f0J;imE+3:jչRB`*T(ْL[~e܈A&|ܷu:r,>=.Qx}4ch`Fu).PJYB!¨rl`;dMU68Xqse}WT e1\Q!59'{nA^sp59! `b[͑ϐhKltϘVI!9lY֍bmcJDb*T~qsCTzώ%ԙb|ҪYw3G2$1ד0XU1['2JqםgÅuDZtX|J!a*W%v1ovyơej`Ք4sRbwu~VtG3BϕKynm~_;F`BŢ/ݥs\ԻTaJ^Wզ6mxI慩>-7y ˄x+)cXzGj0@|˄#Ø*nWP C[ѩ*|2%&g 6w/;3SNfyQ1x]/Jc>'E0R5NxOŖU0y3nB[hmq9kPu4qKFaV<:KWSWsL@ڒ%{1j 1*ـSA'gv3L > =?K8{`.>ظ/!B!rm۶Qn]cw㵋m۶}Hϲ.Fw\_Cךvz.>~W/&K)){>:cw &K}"B![2-x7gMt|L(9l?LaKCc0-7GQOݟqD#B!Y"g0FDU+R-"0cM3aB!B>2-B!B!BQ!B!B!^T !B!B!+0j]B!B!o':Θ !B!B!+mngx,29G"!B!˜1!Ad?Nۙ5rocw2۩B;!B!rU!;BA};ս2[|pVmdH+.̦H} []Yk8`?qI{| !B!ě+WWlLPhmp.Z]쟒~Qe07QVkuH:Y?ҞZU~>=]2&cݎqAс*vQ]zvw? P_.0C{[H!~Hkp -n g&>Yt$/leM3/οo3= [뤌gjόy\{*T,Lվjo/|8#f~Gϒx =HΫq]Tģ#f[35]17>?%0a[ o@˾)AܸeޢxxzPnX!B!_+W $ok>GX7'6Xtx=ܟɄTy@DFyύX1[YM| Ͱ&yCa+&f@ ` ʽ βY'61o789ydߦC7s7zh Um2KIMc+o,2MArO ē?_Kky!/W.ƶ:(¿,˕ ٲ0a8P{$4fI=owp49E!B!oEeAQX j@ZV\-1:;HuWS Zбw洨']wz ft*3Jg@F8_v}\7 fuDhP3_ʅ2+j$;xQWƶD]*ԪtOxn[p~p,sQ1%v#?\Likh2OQ85yw}x S9jh8 2, ct IDATO5]OWMeX:.U bGˀR)M(coEiס/7a)[݉5ދ8n n aj+CNl0vğ>NzɄd&<ڃs7|:#FFVÙ5^ޔx𑣙SF ]q55M,Q*x!?L@ ͱpT=蔦4z!_}ZƖ̌v0Bdktnԟ O}V^B!BaT9ϠM!16Mg% i:mZ 9t2:g\oV, =ʷߌ{Xő2nH킛۱<a%~!hTh\һfndB85sя7:nMlRVвpW67`R0SJ&{xp^WUE\!_VGŹV} kNjxlʍ䊡x?Qd+3R3s*O8ϬnQ~/Q4ܓsfg>Qm;=!B!Ϩ1^d\6܎|%ߓ62ƚOB%G8q'5ig5z1$b3-z7C^{#(l4Yx=}sfpBJe@6,1FFܛ@~k 6`הsy|6xݵ>=\~ ǝ!4o=CJ舣#.,Ȳ3SXrv´1l+:)+RA,ox=8BdKPߝLndORx#HD!ϢA' ,,,gAor3'NpK{Ъ6[`S}:y[r#kסKtSIi4mZ8 7nc!gN^s(_!B!rxC 1C WYѻӊPZ!rړ8/4y7t N*ԖV{×;gӴ.nǦFGGWU'"}"-!)KWh߷A [W1]_Nf8C(.U¥*Q+=Z3kILFeK~rbn߼ցK+`MU~mvd}li/“YeBcbCc[\z`ל͔!ãU=?uv5 bA8RIґyz-Tp箁?xWC:Ǥ5#zFb0`6Fʚ|*ELܻYQ+`0( {yi^Bk=caL1dlN18B!B!ވ;-ʺeRX]N{TÑAt.4,cS&奻))+=ȳoMV(I$eII5e)eʑ ]+{ Ӷ zeO!B!Ƒc+WxdT:a- ʛM@p#ݓik1d|t&LDnl rp.uWKnPo%1fD_xgRbqVaPowgHftLvx˦2ɇ2OĜ;˥kqtJ Y!ũM=6__Bm3"㰯3gyΐ/nVĞXϬ)?a FyxEh^P1s+qKi\/?AEkԇ 0szᛲ S~-ޞɀ{=6ju"_70{U? E|aFNvTr3%گ{vs5;T68[s` WBR}!3fQ_洶"ڕT]C|A\f\Op!u|*lr&-GjnӠ}>[:aie9{aǫQsLL>1i0I:P͔}x(B!BaT9rij ~0U2z_P&*]Qaf倫{i}@wy!:E >t*4vrlއ*>+ِ:!;9EjWSe\瞦ij ė -<_8k"foC2 9{= ^u"dwyɬX7JTE߮'E0R5NxOŖU0y3nB[hmq9kPu4qKFaV<:KWSWsL@ڒ%{1j 1*ـSA9A`!^HP_sy}!Bl۶u]TTm۶ t@zEu1Wϗ@㒏p֔`sQ ~\4\JIO!.&{3o69ؘX!B!Uޒi;;9˯4Fo"gBaC`"W\"*i=2}ڴ$#0f5!B!=$9w>0&\jaхo 7v?B!"iB!B!$B!B!B rU!B!B!^QU^j,B!B!x;5\tl^!B!B!^Ƙ8phcvC:. 666873~Пr\?ۙ5rocw2۩B;!B!re7I*WB#hKa]/gafNƽR!lyװ5ݥ ̘ GȧB!"'p(WlLPhmp.Z]쟒~Qe07QVkuH:Y?ҞZU~>=]2&cݎqAс*vQ]zaє4UR=Xcu*[[VGX7'6Xtx=ܟɄTy@DFyύX1[YM| Ͱ&yCa+&f@ ` ʽ βY'61o789ynٷ iP */s]LQbl,KOϮNa""\ɐ|- CӉ EGOzlM1Ph^{ Ac]B!BwJnU͡TTy@4eÈX{ü TX. ;ՠ{waN|u~kf@2í~~fie7E}S(h.,>\G䈦<\(FRC'W=W7{~4",#~ ᣥ"#,&2usB,%v#?\Likh2OQ85yw}x S9jh8 2, O5]OW_&$Rrd3%nq{&_FrxU2nw'x/m'cb֪"x7LcwIIIWLAOhҺS=8qçn9bTnTi5Y{PMG9[q9Ekt`P\C]3>n1[$2? rcK7 =Kwpj*%c\6.Axt Cf?xh_I5ɊT !B!r5spP+<|璳,EL>(Kg-ԡ 7pwS.s=S*z:Eɥ*La=3;bYZ[o[ p(p9aJS=aA>̪~a5đ<w\kԝԅ\wm*ܹq+ zϥ[d.3LY |ũm.A칑+0Wf1TxϏrv=w2ȟ н~7L0=j?,~?ۦ" 6$@~z.Ma4LKʜ9&uH4B=DE7ЇYil?uӧOsla+U8:)M^lȗlߵ/3̽1YZ$g)1m{B!BY\}̠M!16Mg% i:mZ 9t2:0ba}+EQawxSwtRZeU6-wYAldP6R e e2d6(RiQs͹I|2y܀\zQCF| \}\ 3a72w8G3ay)Fqc'IfNMX, ne6[L#MFR+NiL#zpS.m"~BsiIc:'[RgS4I~y1nr% 'Wd֖bWܬ2Y,DlSfDBp:Zc~^݆oZ F޴WuaOA u *ec^Z4mٚ!?cMsVM9'L?OT[QcZ c (FGY{P4*K>Ɖ|^_&6}58"{ܱgfZ؉(<(Z峲P-?A^;Pf c{v쉈[Ϫ\1bÇc]\\޽ׯ+߫1Ğ6iލT9m?Wpч3o>ţi`%ڰ RPgR8?Sm't/rs֜[,v%Qk2qN6 {t*]Yb׶P_[+˒لcj;-OkJRW6Y`1pۗmlVڟx/hn9O*LUqb~͌p/tS> g/`q-H )H]:"QďQKp*Vv`'ɑ2#ïc%&Ly9vu}8uE]w%d4Ǣ"8a{Lː99 ވdkl8ŵYb8{ r(_DDDDD,OU޽߿?>gϞ7I^ bʰf^&L@trQw}[dqWC7)'s( &ņT/ꇏ#j\d3lVB|WRe;>:Fh5,z}Ffښy>̍%&jsH 0>w!.DҾDd˸9i6&+:mq fWN -DE)ą^8^٩avҕ+9uDm)}[ݏ\59p:ci0ijON׹Cm5]:<8ť,<*y[q/CehޭC*d(k,0ʊ-78WojmL{퍸,XV0$>%Zܩ{l{[bmjyeqDDDDDD @{聍 Nnݺѯ_gk=79\ͻbPr'=8YA~ly> kzc֝Z|1 Rgfu5+o_7!^5^{nz[c$|Jք Jttmʭ>ߜe8{%{,S g)L, swj~fqLyr|;~O0#\͈2y%/CR3dov$wn]Q)]qX,TyʲgRһh'o,e͛8m."Z8:9bq>Mv͞H߶TkS '\˺s]h8EDDDDD.1 4豿wڕu;oښQE'z.Д hV-o@uƲYlfϟH5ng QpN\iVl Ӯk)fvBˀh>sF/cKֆz?Gxgt.5fAj#H^S龢$#;L tsOryqOKY֋:HGLq-x IDATǰ߫0Rl=7^7_v Gs#)Wn)3<̠? Q?(7=n+Y;O)4,l0^Ɏ& vƿoUӵΧ%#n4SE:u%pBrZˠiZoo4-ɐ +zw`[YJf!W0~iH!{2Nͩ]{6-Hw1|a~vBS%}8s7S N(Q9ӘGyV09>µGW;J{[۝6 UؓDsr\ܓ"0*=1yC!zo#>[gHEo_M/LX}Z* s=(SdE?r,E; ?^wJH >|'gؿ+'|!'0[%}DoG$=\JuLaaVmbK N.ɢgz߄{m8#Hۈ/60(&~;F<:4 vTס&Uryc̸]l>i0Yi<뗥אt3&/+|LE h4I;Z1pP}*FdorS{> яFK֠mS6`͌KwSۇGINpEDDDD$zuQ;vh 2ؕ %!km _O }eoذE~zիڕgΝNZO <:ѫ͝+DLo|!O|DGNMz}$?k׮\r ޷|r*Th4n:^en|rׯbވe-wsg'%rfދBɘ&Iѷ|HDݼ̵zWy-X, ,`5X,Gr)I{M未FgClFgK2?6`H~(6}3/cJ&xDDDDDDb#G#G &X,9rD6dVaFɪ$ :N̨fGK{""""""Νo>^兗sOZ@DDDDDDDDD^ =_-("""""""""5\U媈5\5uUcy3%j݋DUxx8;wLah>GҢHZ4""""""5\ e%0$;wj>GҢHZ4InItA+pUDDDDDDDDD%$j*"""""""""R*""""""""",KP媈KP媈KP媈%b/w\\\(6Cp7[__%˙g$Ec&۫TX{t""""""Ƀ*WED-au\X>.jKV|p/ʍ?#~ŜXqw͖o}{7ѧ+WﲪM\l0]Θ -; ?$:y=1b4pp&C24> D>djĵo%vƃrn }L#wهYrvLJGLF#F[=R=8Οo\􋏋[ d"Sb%g»^I}E\܇"&|ZsFѺbҸ9`mc_XHnkjŒ w}C o`xf2Sʃα-KzoOz[䗜?ֹ\cI1y18xߟ,<9φOR2.8&Gz Z{_Ŷasǔ⟅4ay$XjKّ[凥~FS;<7Rmtԍشu3a- }_*-FՏD[Pa&÷iƮޡ|U\Ǝ跓g|0]E1.ߵ޻¥NT65*QU\ظc揹]~ޗ/}$Ξ6 x5`y.Lf ~ї T/m51RyFͮ5)%Ƶ҄li=?ۮe=)J*{+F+;"` 3WgV "dr(~ڔ1` #cRzU1cJ=6!rH.&~4gH`Hh]171O3>?CJPb-w7?mcL?ܢ.K$ v"(8e*ףąm7?1mFޛT5MSa[~QE<t庴:ffu r< Ȗ8.BZNeȔ5+ҳ^\Irbz5Ż@j4}jW(Kź_#?"v'AH JLjOߙ)8◳ /r4YDŽb9ςMXm{X 9R%< yQ$ϧK$zM]CvUV>D =5 93Nn/7+f|P89r7=MC)wq?A.d0w:"[4tԸ9&m>^sGr}tږɆ=ξ2t+Ir5aF/r*'6NaΏQ+S& 'w[*-n;t~y)688b0޳ N`\.qŘ.c&yPyMGZgp"da>̖Ue3fEfn)saW6^F,dv-n qp>n+JO9OֆOڃ')gddZ/f }l _p~ZO}N ?lO2L<sϹ4ܑ _aQQzي$$_h?l$kРm!}4$,Zbj5 E'y}U6a܇Vs 1V];áM;Q&g-`y/}IS=ΦgwIqv>ߣVX'JGE.׶3;0l..b[Yͣvc&m(SH2xaTfjN:ttBjLb}KN.=ȧ-ňYe2݋? BM1eYA4)KcO5vmȘdٓ(eI=%-d& yEkѴekZWNfe6Y=7 3F>YϫRo-FOfԏi+&'embB,t'}y=VljpJE\Ivkc'𠣌k#BY]zsxDZB14=Jeٳ'"""""",Qc&mH,ݶkJ}h0cc_< iX +/@aUs7ŚF,:;µױ{nJ{z ؚBqpw!*W^㢫KK=7BGӷ3d⎝l^F\{TNͯOwY[Ov16.&sD[<=ӧ Sx:^4T}"^ZͫR B0u`E6g3iyܼdɂ,)%bX<Mdʓ{}ĆAmFLsKŐ ɝ/;ilTJ0b}M7/,9p&%xs@\&ܼdK܈r=m5@Nu&qHH񙲶funщ374eA;aysl,=' ~"kfkC+98}q´Zݺ2 \cŠؒ/[:9׸[|đ[37b+W_d_etd72y +\?Xp#ua€{Èngl w D[v/Ϫ33}Y/dqx^Et16wɈ[v4TN] І2hv|.kyMWXѻR"0\>O\C ېewjNmFZݳlAio _6!(ҁn!!R#7'9UN 5a挭9QmLZۜCE(s<1Nņ}nʌjJ|P!+їe>IVZޜ}QϖNpO"""""" 4q _݇мqwo'-hG<͔*S+fxtڹ9ӘGyV09>µGW;J{[۝6 UؓDsr\=rV4x*ٴǛsvř\Ŵ{rdه2|`:?X>=|N=UoǗ*z)1'pChf'k="%WlKi>]=Uu670՟US٪MqIE#Y4 WpO 'vd6iFqLjG^&n*:ׄU:/ۀS~Lт"K'@< <+gT2Nuq)X(0iG+O(W2MwJPt|Շ3hUb}ԑmj y)YcjP޴5ɩH1) YlL@zJhh+xÆ -ZS^W֮;;w$((vj= |e'Wi׹O^|Dm_&dRzo$CfG݉:'wI~֮]Kr{˩_~{ wKԯϻ,na,4jQR+gX*.id}.pb{0I\pw̉=d Y@j5s*f zn9,DJ|ٻg yE\Ty `W2#"""""|(͒[$6JV%Y%xqbF%8^7;*]I~,K"""""""""""/A""""""""""/!QUYW57S111ٽK3%fܹ31!HZ4I#i|HbJp544˗ÐxvܩHB4I#i|$- EDDDD$9DDDDDDDDDD^U᪈țJ᪈Kв""""""""""/A""""""""""/A""""""""""/A""aw@]\pqqHeo~%#|,g~`\:poS9cIщ$\s]rucػ-YmewU*J+7Nȷpsbc-f5[_aDދF""""""G\˪6ph4bw;c>*$pƈh՛ yа6ئ棓׾ {mZʱ61( Gg*wgɉ/؎_# ш䀫o W$Eǔ\܇"&|ZsFѺbҸ9`mc_NK\T2glYx:쓃f֣^XHnkjŒ u2`H~; 7zVumaxl^{{Rӣ"%J;Hz5ɋdI]y6|ڔ|p=59c ( u\}ŝ;H,} 3%U 7/_~Ύ,޺-?,ew7zaZoץnƦY kYR)o1~%z<' c6Lo 7v};5v g4Fm?勆Q,Ǐw^~+b X훿)lUKf%/Vbg ₣!_ E> Y÷_%:˦{[X}YМ9w~ZC[RJþ%'OLA;?a,mG&@֬ǤKz7֌0Ψz3z~駇]zRՕUJW/WwfE@fXϬfE~ɒQ)c,92.5X3#h /w뢈aL=1gH`RYʉ9z2-Ȁs)SkѸm3&*G-:t`1t;rd@U>Gm7W'݈{Kۻ:n@yo8!QkkLI@b;rX3njFEJ$֋+tR\Le) dR.Kq67 e3j&K.`Դ^Hk2V~4#kאPʕkS/F3?'Cw.䊭J'WAH̫#g-e~[bGr?-j\= C,Yж McZ)Q,_؝΋h>}>e5aEbI^#|/DJ.Vc=Ft.(RFO~7]CX~L^WXOR IDATNq[8^sD:"w|?,Q[ UfqvuK-2'Zϧo㚣Ȁ>\;^ͮ?OP.#7TILIr5aF/r*'6NaΏQ+S& 'w[*-n;t~I߄y*O܍%2AP[3[cW9lۄ?63e6֦Bz#`xf7~c1/ѧYͪ"bOe?EU$6`Bf{ܱR$>ֆOڃ')LFPJ3UvJȎ*#Iz%~^ǵÑ\ɚ=5˘ ["ӪBKc|?v:PwHMq֯DԴMߎ͹43]q+ևՇ7]k>f }dï8p?O>{Uv6~̀']&]\V Ku/\ʰ(z(lEDDDDDUbvu˧QH.xA-B>hv9I 1X3Ōj)g/}1VO:Ɖ|^=R=W33n-cCtq-cY](K/ϝZK(_|3ޱG\ ׽^?{DDDDDD%jb6?9Mw#UtO+_wl=۷{w~h/a6e@NRu1`496UU,hyW5Wzi F~s~vLܱ+k/Jc֩-ԗVv6a똚NKӚT>OgN1MJ`obXq zE4ko뿊+n9O*L:^4T}"^ZͫR B0u`E6g7I]ܱU O#{eF_J$~9LٙTr'z 9=൅q>ËmKhEEp. ٵ>J!ss~npks/q6 8 !KQHRZ`OV46ardJEWڿ_ے5 & L9ɝ;.DW4/6zQ?| ׈ mc̅KV<|۩0./Mɯ7׎(UԖ `ښy>̍%&jsH 0>w!.DҾDd˸9i6&+:mq fWN -DE)mpgiCU7.4YRub${RwKݖ׎aՏ1=o[ w_Fc+Wd~A ?oaJgngg s#ۀktxpKYxTN'0_м[T(GQ>X,`ӕ Z!op&<ޒjoFmqKcXX`H(}J0jb5S- /eۜ∈kd08!g` 鍁hD.=7\Iw0,NzVgP3Oݍu{p7aLLph[V0ӎ[nθ!o>!pB<\O'$|4rZp=AyXh׭1o[> %kBC30DGmuZ[72rO7<~@'^>Y QikAe!8M<1:Lj]0Sx*,)%bX,6)ONa]<|+O[[%/CR3dov$wn]Q)]qX,TyʲgRһh'G,e͛8m."Z8:9bqRR ]n5ҷ-ڽf #ײ\$0NIIr5>S-:ѳvq憦L r=l|"s6͚ŷg3~DOdQu;lm}370wB2O2`[v]K1[ZDAk4}[6QNB5F9gzPv)s8lӒ-~u"l<ґ-7vd1*Lnp[fcw‘Ü7)a,75} G[v/Ϫ33}Z)X n &uD̆L{ٻP6 wSԶ#:Lr6OC`٬w㸉i0Yi<뗥אt3&/+|LE h4I;Z1pP}*FdorS{> яFK֠mS6`͌KwSۇGINpEDDDDDHIlffSBCC_Y6lhѢ_z꽲vٹs'AAASyV,+81O}r*#js'G6%N'7!O|DGNMz}$?k׮\r=Wnԯ_= DǻĿ%W]@r7x(`3s|^,ZNd4I2e x1=$e.^;O[\DDDDDD oȲVsɜSU1p|ls;aI&wP=7Ql(Ug^ؕL񈈈$J$i1ƀUIl uQ=͎Ja0%8DDDDDD- """""""""KP媈KHplUEDDDDDDDD͔jLLLbv/"""""""""Lyxx8;wLah>GҢHZ4""""""5\ e%0$;wj>GҢHZ4InItA+pUDDDDDDDDD%$j*"""""""""R*""""""""",KP媈KP媈KP媈%b/w\\\(6Cp7[__%˙g$Ec&۫TX{t""""""Ƀ*WED-au\X>.jKV|p/ʍ?#~ŜXqw͖o}{7ѧ+WﲪM\l0]Θ -; ?$:y=1b4pp&C24> D>djĵo%vƃrn }L#wهYrûc#mE%4d_%KGY=u8ZȁV2VM#o{$>= EHRNѓ_El @xH؝Xm1Vi>jщy7.4;TL*xa6Snὥ]7U~7q(ųoF_.4]ZnWv֋+tR\Le) dR.Kq67 e31\w>fg8&yml;~JT v"'OOAMXCG 5ۏpG:7}h֧W{2h Cf"6|*o8IB)K,P(޲!SDQDd ,eeÞeyh iz&'>wr;c*' E͙]?%N37UI|R+K7NO,T~3 TIN͜fo_O\r)ir~+.8|uNl?R%,`;RZy=3;Ծ Tɽ;S"Ŷ P=+\3f&~StM_LmR;2iZ6M[ip0fK_,|ZVFyj]e7!>Z:P9ߴOdlL:9YfYiðoײjIs0ڊ$g~<-6WNc1l"?}v-X)}$d lZ-_60*_g'~  ?ސ9=M^Cp<"-Ͼ'5r37>wSJd{+gt瓳olqe|LeXu׆KL]6Q/ɷ!o~:ې'Y%+PظCz/NEo{8zw&u3oT(kpזfzY2Rƛ?"C[& xOfyܪqafYӦ ,˛nξ5ɦsz%t,ǬQ,wi˚Q%@ :-b絨ka;`ϙ{:oRl+{R:ߍrpq̊L_M2'0Yn/gܤ#g *l,Zq}rrt$0gXEʥˬu;%kb9MpIIڞ>VѿX :`NOi{8pch[̪+0 wIϧ:y;n>&S:qhvҴGcMl\8{[b4j[E QLPMYu}rDz?JuwT8hmҁ;°(;a8w ٽ!+=Fϡ+7Sٗ#p6sq_REDDDDD%ZEr-! v]PtvvЕ\ys`oC$H)*dRquBHo ¥@:+\lןn݌gJP3ZytMCC)̏0];UZ/=nEA]|gf/'kpqWBF m>-wm qnoӳQ{J4:ǙT3?;R6~sv1n]ƆufźϨψT{z όs[;]Hg1pDlk027{Ѵ [2ܓG܌ m"M,xsWm<N'X ,eb7& ܒaZ[u/xfJذc9ɋzoeXd[ sᜈL3P! ڃ=+$:;O5x⩻os ),3+,gn7MzjNd¸>T/!_ɩppՄ_rg4S&Lfr <,0} '@rx]Re2/w1H絹3w@MnWgqƌݖVEgcɄWஈ#oצw[,sC .=5l̢o\cqJ 8&75`!:̴l  Ϥ㣐q'o-9m-KL nڥÞdRܽV #ֱ\O:;OIU{\6a%0A{غ&<|LVNhja?_\]s372{teHJZV`F[BY9hg]maۿLvO9-fO2 Ε+04˼G}Mv.^m?:˸j.YX FOqf^z7*^eڗ'*d1aøQlu-Ȳ^g{sެ!$K \99]+?ޟtf 0ޅ!T/:Į1wϻ̊v?ۇ0n K޾<47sי߆4Q jË8+1A4q}p7re\3wӧ,nJfY36>VEb<#cqྣַ\k;KA kț=1ΡO0xm;7?& oǴpKbm'BLԘO3g z4/9 !m9;;0`P!G{!G߾ ֏ҟ\ަ`zrL۲ej||FJ,_|I6& !jX"K,ҠA7O?Dƍʿk.J*m8j9,JN|Ue_a[z!?/`?,G8;?G_{w^郓y[*U$nVXA&M n_ON /[{lW^_Vod{&yI᷹r"%"w$.]O=# ɴBקjauBEgK"|ggk3/ԚqV>GDDDDD4K .^#٘pQ*/WJɽknԘ~q&aqF8!YUUj,"""""""""ϧd W#""yY;vk׮ EdzEdzEdzE$d WV wrSKiDDDDDDDDDDW WEDDDDDDDDDpUDDDDDDDDD WEDDDDDDDDDpUDDDDDDDDD WEDDDDDDDDDpUDDDDDDDDD WEDDDDDDDDDpUDDDDDDDDD WEDDDDDDDDDpUDDDDDDDDD WEDDDDDDDDDpUDDDDDDDDD WEDDDDDDDDDpUDDDDDDDDD WEDDDDDDDDDpUDDDDDDDDD WEDDDDDDDDD`Iwޝ͋8-*U$W""""""""""qU'(\qU'(\qU'(\qU'(\qU'(\qU'(\qU'XjG!44W&U""""ח|=q=ՙ""""X1$#G|IyyA۷G[Po$ɴ)R$)6-""""/ErxW)""""O+HW^0شtLqFRM)ds:SDDDDI _y:&)QJl$sUDYbоLv\Xs?ݵFQԝ{ON&uhΈÓ{ODDD9tk kg[&ۚ$VN/7]i!"hD$Z!9t=V*'V3~.:7Ef=5:xߵ%>պoMÔ{]K'^$ȸwuUۊa$ "R)"8E/QhAa9mFɀq(i &Wmĕ/|u~ԀW٧ʹKY݃m'$#"4mW|ҝ {FjBYޣEǎ_w1t}&Ea݋۞5w2VF)ӓ=wn,Sa;2_3NJ 7|SV nFGO}|6:SEb¼Kl\I6VtiTߧb opE nNzl"5dL.HG֠^k݋چMK; I<ʃ0u$?/rE|!Tk1C:K;YOy(\$`q̿0^jM#y±#f:\|fS9k9ic:QeY~Zߛɿ7ԋ!U#-kdӞK3'Ml.ٳ}|~?)"Guȋae[KTN+ft,̅#X6- J?Oч׸r+%X!.ܿ~?f{5*([w<+ דyw?a/*Ψ9=ax\':NQfɑX/mc\ e+]vBޘ¡x)fнF2z6kQ-΂TY R2W+բi ,Qfᾰ9qm+eiُv"Lq@闍b ۮC) xx'WNzo+INosQ މ4[?Ψ(+#~^iL={]"uO [6 I'.JuU!{cƔN5aZ<}ND='iґ[y=>ed!C W ڵh-3hysZrt ToЖ>dǢ֤Зc=5".mBG $Oz?!(m|UfLoT67mvuH2%)=ggdc=R"?iR,9~?~&7PTx]F|>ݏZv_vFLzX/۱eʔ4U߸a?*3FH̜M_4kQy+i+aԷ=W}LWi1bvw;.xJu<8}q]۟΋fŘ_[}T~nQt(tގڵ :R"gi<\=qn̾_g&o "@Ehyt""ϯ;[syTޞ<0{W8Ƅf݇cb8:uݱvf. f9KCi=D*_ܸc pL]MO܈ڶ94LuY;}=ssϜҁYְvHjF;_qd&.\΂ϪpotxGnOYbKgyto9xu^_3fܢլ^<>yIynEI߄~=&0*W2r2}ݏWh7Q}ԎiVjd;0Ok"fpkGk91wftįm"""Lj]͕ck8ClH'(s:O§fVv}`f.M>ZϭGM;ٳg{~]{\z-wW'Q\:5oۇxz` =+@G7^߭xǢe0-uIE# y˘yi260O[*֢j-F_,M ~$s.f|TlԞ~~m{_/en͎-bTщG,2o,хmGo= +ls9{WgCSYb1[egvnJ߲v,\{eX={HD$!@D[eVL[LK7Pf<OMHkrϥT(ceW;5 #s. IUiӶbRv:gldڈU܄z\1lX:z: ~ĮT@$fAJ ޅY?zEjVš6d[T._7PVr͚S3*%tym UÈBb5R@Yb@,;/z!Zzԧn}%_1 {j7bSUSҲG[g z<`wޞ"oʈ9]aɸ\9͟4íyx{z5oML%w>oˎyq Q&Lr3u@Cٷ)߭>I^QJV*V,>Se-T`;ky=ڧۿbL:.,@gCe,ן\cQ}f`¹ǷcȂG L ]!T(AE8z~B!l-j XԈ_wQp*Jrw^Wo֥x䮲}l/"?:^ڏ2ylՊrruk ;-qw#_PkRjFb&gBx_-ٮٷ0Ոll 6gXt{PEw>u>nyJ,@}{(s272p?Ԫ K̤c/Z1M@JҥOqV Ba\d5m5+FQ~;WW!%z?Ia`s:h-[N6Fkްqqaw|rJG2R w0UN] VڂՄahˈƼnXYcłzZHsȅW' B~f~~zv,j .y)Z‚_~kQr864<+F!mѢ`1OՖ4h. CUx 3vvB&OnC|~5*ƌ42fcykN W(@w 2/K*G36W0l^.Hq FƩԝFx>~H~mxբs,4ԞiҮ'eUn @JGU2;wnq=_%خK|6aع*C ՙ[!>i}0NofOMZ='oIDq1iDanBS;9L?3b=YLj!LߍaJG T:ߔ#pfj6}2ZrG"қƺV|:p%f6` tjx#m:ap ϬOՍT~d/R?3WW;gqiOCx89\`6crbLiRv34OȄɀvkK0{6C^*eN3]w"|6}st|HMO0=nj6?3P(&fI[QSl8^1nXք5yg spTx\?Qq{Ly;ꕆR*"|phBDDDD^0:3EDDDi$7lդaygODDDDDDDDD WEDDDDDDDDDpUDDDDDDDDD WEDDDDDDDDDpUDDDDDDDDD WEDDDDDDDDDpUDDDDDDDDD WEDDDDDDDDD`yWXT!"""""""""\ItZdɤJUOOϤ\qU'(\qU'(\qU'(\qU'<)\53R\qU'؇FgU#EDDDDDDDDDe~Z6 """""""""2HL6cZZ`f{Vy%&'3rmQ :uiDDDDDDDDDDK07}ҜX v6Tsڈj,bgu4-@UDDDDDDDDDe_c3L5Zlv""""""""""/8&F*\IB!rU3bwip Oq˄BV۬vcp5v ĺm""""""""""e89`5P5eQ(\8h~GAhuLF*\(\|QvK/F-vi#VG V!1Sc;51_h{chj쀕'\w5i|j_heՐ}ušy<})h{ZͿjsp=IKib7(d}R4*\+p52WgZ ڏB)oj|V!n`UDDDDDDDDDߗZE_/d}(քn{b qVqV1GF@UDDDDDDDDDKؗOh4k|B؍kmBXՔ"""""""""_y WFo7=IENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-roamingzones.png000066400000000000000000001310711501654372000223030ustar00rootroot00000000000000PNG  IHDRWimEsBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeMi 11 Jan 2023 10:49:18 CET3 IDATxwtUPB$^DZMAPDEPA&6i (UTJPD =ҶMߔMmGҹmKgfS˭ϴng6hMo^DDDDDDDDDQ󎆨 lo/!ϴ${04)IV虙ުiKmy jVTGM*8pܝ2f::ٯ>`5 HjVi VN$6jZjj2o3HZ2;jzA!~К8>kL-djjOa@=W V$ YU4b =W V S/ pL>P*"""""""""jz1@,p vNi&_GBȚ"`M+\M+PM;FUp3LFAy=͆bFEEٳǯڳg. ۽W >XMxiQ/ W^5k )UT8f3VՊ͖VO]c00F\\\puuܹsWt2㧟~N|j!>DMk%ivUGCUwY̝;Yϛ7o& UEDDDDDDDD$lXVf3111,X@[{{{ݼyQ:i*PUSyPeƍ3[hQ͛ SEDDDDDDDDd0dǎ:t0 8L|V^{&J=VM~5kֈGy͛7so/EDDDDDDDDD ..J*)iӦ_ʡ J2VM@ <ߎS*"""""""""%4i_~e Aj.RHI  """""""""%W C` @M6)^x+l6bj`41L:XfǧTÆ n?Yi¼0w %CCCʑq 1XVl66 Պl&66pQudłʕ+s;\M`K6yWzegEDDDDDDDD$nܸg[,"##qss`# uԯ_voU)!GM2@jSBZ#g[ADDDDDDDDDph/66Ց: (Pg&<^u39p fs7ܸqCudZBV W굚0bHEm|!2~]\\\zh&0Tyh4EUH;TMLem6}=oïqM_;EDDDuX4I뱌긕&FI=dMw{'3X,[bbQ?ӫgMwuXVf̘AFh޼yǾ;˰a0F֑|8#`%YVj+"""""""""ΕG֑Z 9YhQF,^L֭`u#E:)Un-Z*""""1U~SVn#"»/a@316}R\!|Kذ`rs?bSNŝҦ7'~/'A?XÒOt'_  ܓ.i+"""dN#ŋpqqI V,֑X OrIvGZOV*""""cySaˋ׹ϟ\6 gCqoߙ{aX"E-\8H<1z?x1 ngٟ`0ٽm\ahB,q.~/EsIQ p%"""wu)u$X{b,ԑQNV4@U*""""fUO 1/Ggcl2m!,E 0=>͋=gӿN ~=p Ke:%Uɼ {t>_q6 Ѿ/@LXΜņ5 a\\?oPLj#::ũTH..XH~`4s/a?j°cf4{;2Uq͊A͘m`KXSH+`u=WNt|5_~eԪ]ٜ9.)粓?"~bsj[:ɟ\]]IEZiFr8uvVc&ݻ-[KՁ:R뵚zBU$_s7cwQ7^'2lcf,-{U_UDDDndʱSM&SzWGVBޜh4/6oޜMX&HkHPj+I_MϦ(nF J{% M ނj=Ds\ﲁԮU)"""GXW~#Sz:ҼmPOvS݋/sd"""""b~xoZI=UDDD!7n 666[pss`#u?qv9d$^/ """""""" ,x"Y@Su$J5CM߀V%7t7_?KtD0^C i*[Dّ[+""""""""rqްb5 &8ԨVY<0\CJ7ivSx]}ʖ|n.Щ"(ҩ!x9}<۵hc4Y2iV6*%}6*3o't 6sbnCr)DNoO7.iQٯ>iq$%|ɳe{!+71-g :7 зeer(WЬF|}J؀/-ϨVtnoRWi3~$٣b|+CC?`͡esC%cĖq]iS })]:mfŚEDDDDDDD1NsՆlƜ0Y,XXXb4|ljNP/bgXt;|cV>oi{S>5c :ޚ9m.m`xy )+d)O@,+6|>}˜,"soŸovk.vmK37ǖEPd9 G5Z.ӕKXKڔ+ˆ2x,{lwaY4䞩GZ27ulra*a2-mׁ)ZOgּy&=Kl*MNz>9Ěv_vLz qdaVz -ɯ#M]k:Aɇ )o@+'.EVĕKbnrFJ5 m>FNoŸYQOs,E Ð1gc~O?Ӈ.Rѭ۫5s 4>ڃh#E뉥 ֭ǁ K^HŋHp^JQשK]ό D'ZI t/N9g7f 6 lgP@@2@Z?^}ֲD_Z[Ǒ NԺ#ڰYm`H-n7vg Uac!B䴺f\lV+Vɍ9!e \gO0%nAYֻVyB{-b-YzK" IhOJֲ)1 ]>~vĦe\c[h2blk N Wg0jRM,8BPN(5=5eygk aĭF JZrLq/jUSoڼRN9E 4Mk#4FQ +D۲i.ہgJ9;,@$;Scƾ]{'yR\_Qc&}ֳ1ќ?rbU_v8a?|ߖ(O5w'mU~ ^k#zRBT/{O3Fq̵9ӞŒLT5`̓C*a?QT!PUpeBғ SŭF2&.\rDFGd~Qٳfr1Yʆ ,]JuqEsS[x[ kSv\Q.T<9 uKyTɓ cKs)MgH~W -`4ɹEM'Je,_Ky|<%Ym0#ENa'7mu[AJWiͰfOѪtv6fpnZD΋0yRt%j? y~ s|DŽ ,1/Z;3-L7oG|Æ[!oJ֧Bb |C} JxCLw9#k6'VBו1Fᾑ;5^oגpLxepռ%(]%/.C^`zy_:n6 }F:ѭAt 2gH~ϭ8({ٺ<83_ZqI6M.@+Wͫ"E 1*K94-ny]]{ 3`&sEDDDDDDDDDB WEDDDDDDDDD \x xAy]8D=WEDDDDDDDDDឫgϞuf""""""""""w@g!"""""""""rGq8\ZάCDDDDDDDDD䎢1WEDDDDDDDDD@᪈H8<,fsf""""""""""w\""""""""""Y9#DEE%O8AQh|>ϑ#GvS3k,,Xl`޼yL:57of"66֡,^3gfA y($ǵkr}֬YàAXlY`57nj*̊+rX-[ƀXt)o`՞j̙3,Yb 'EEEe< 6pΝ믿NuV^+Uٜ؋5,Yy~/""""""Yq5&Nk=X===͛7se}N:Eʕ OXf >, *ТE |A5j7od||8qu떭L2L^6~sqFisCnOGWMFhh(w}tޝŋhѢ$=5m6-e˖\pe˖7ΰax饗(Pw-G92l{iӦX,$ΐ;3o {5AN޽{0a@&MxiԨQ͍:жm[øqسgVSOVTjeժUY VEEE1sL*UDnr};v 44lw}fKիWyhf̘AÆ i׮j޽;KJ==qlڴi#/4f̘lmÙ=~d"EI&%n/+V,^'NФINJݺu\Օ͛3uTFɞ={8y$15k҅ߟ`QF@|PEhh(+W{2#?e_Q-Z4Eѱx{{;5Lx_jРAz8-"""""""Vcfƍ7 ,24(K=4]vw}HEI P` `ڵf*Uof^z;N<֭۷T W^eݝ:$^hȑlܸ~ 2u\紎FѬY$ʊ-[R~*u~~~L2)S8u;LDDDDDDNpu޽4mٜdzhÀbU~ۃ lBdd$mڴaÆZ>88-Zdnܸ-[2=z۷g\ǎ;v,~~~у{r9mѣG&G^zNEYfb49z(=W\I<777z왣5l6>Ν;vEe˖TRʌ^3q\Մ;۝2D@v$ RS_! ¬ݻw'w&nnntԉ%Kk׮,Ր 3eʔ`ԩS,]41D;{,wO+wC %KLq_ѢE? IDAT> 6Xb  ի5 X= ={6G!:: PJ #ۜy,""""""_*Th 3`&˦%I@LM9=Ϊ(\\\B|Ze#Gz M1@ǎy7#G0j(~8zhj^Y ՜<:?+Ԑ0 3K=I ЬYthР͛7Or˗y뭷իÇVm}QbڸqcJpp0W\?EDDDDDD$oe+ mҤI5aq)DDD`X#V`L-y5j(ڵksYGƍ2eJcǎeͩE R ^iN0pdTCڵ1bUVm[reRX4/^8)Z↓\> ˵ݻ7q~̙TR?ƍY2CDDDDDDDl셬7Y3{jՌNmժU#,, ʾ}hѢEa*U^='ߟŋf{$j޽S]^^^!#=Bb(Ԟܜ xח޽{Sn]Ν{g`_~׏EfG>>j9:%b/af+`uӰaC֭[nj3 oSxqyLo;v|ٽ{7.]J#GpT/tpҥT0 (QƍgLTTOݺu|f/?&""""""dkU } Ab&vμHQ6m(\0| ?#;vXbm65zƍ7n\Cduݎb鎡)NJަsft퍏K,Iһ<222[٧&44ŋ3p=4o>~$*TAxbBCCsݻw'^ӓ.]EDDDDDD$wٰ *w'|嗝\ԭ[Ν;|r;?˗gʔ)i&GߺuċWѸqcz聏O[H",\ .駟w^\]]8p -Zȑ(Q3g㏳b &O ?#G}}}9}4+WW^(P H !j7u]67 3rqKS'z=:'JܹsT\9C38\2Ej4rJ˚5k={6rxHH52d;w-Su慗 BK9!)رci۶-;6-ycիWg׮];3͛7o(Q7NW_eΜ9ǎ``IzRڵ˗/s}Ho&L@hh(qqqIfȑ#5dԩS>}:9O:FEDDDDDD4^^^3g7l\͎PJvʺ$!!!9TQJ.`TR?We,Kc5kxǩ\2-b۶m;w.Eph4K6m۷/uRؤd>|8kCƑ#G2\ofWOOO:u:b .\;vLgٳ' ۷oO2ڵ[nZO>Ɂغu+-WDDDDDDDgj^իK.)i੧:ռ '*U'RpaKbcc]6ofg4[.&Mرcٳ-[pQ.]ğvDv&00?u!.!6/c'auиqcre ʕ+ǴiXlK.ŋ,Y>}Э[7LYd gϞk!"""""""w9>,N͘܉dU,И""""""""""Ypgg!"""""""""rGq8\mѢ38:;.h%""""""""" WEDDDDDDDDD@᪈H(\""""""""""YpUDDDDDDDDD$ dU,pDDDDDDDǎ#""˗/u)/+VRDD$P*"""""Iǎh4Һu.EO?ǩTR^"""y,SSy&Ϊ?PB*Te˪ @Ԧ^Q۪ r9wz}v}wpz)f3*T`0`0Yffq>0}s4k8q-ZT-}xi$9޼y3jufM9Fer}s4k8Cquql6[^#ylٲl ÿ;K###K#V+}s4k8Cq%%$5DdCB۩ CԦ^Q۪ r9ts_7u)خxsvm33wy߼X5G3oww߾sV{)4xz,1sW&߽֔-Ƴ'&kr욲i?$ٸ Uweg^+k~̎#l_CbN}G½2h&ɇ-/_SO`PEJ sɍ. ˖tSz\k#Uuu_jO=nxgٚo*hD/1g :9vk/nHXjR&fIBl?lD^ XNlJ VrU۪ #"WV 7nNk+!Ԭ_붤il>߾9b^䲥 nw۾f#iﶝlsk/aPNa_9m;v#qCj@G;7XLT;RqWd,Ч\wHrqښgYB^W'UÕ1EKudڎNYwްq{5ͥo}L۸9іnY̗!9[]$Ҁ_{7.oW,l,=rn{Yf')@/T"f.?cױN<Ȼ1"ٗ7_@pWt`HѬhudsp?@_1|̈׾ƣC;:7laWe}}{Jy7[|u\~ϰL_YOw~?M[[ʻdC|8[іvd'|>@,/žs+/Ĭ%79FjHG4axtwݝ+cWqJg~Yj)@#"d,AMh\;zoSB:?M "ekb'" RqWMyŇ[qxg?tyn;nS #VZ6ӷ_giP@0_AXG]q/'L[`fߘڴ\(;L"_͸qE"܂b#q+'b7xA0QTy5GG:4)p_a7n =v=0fh|y*c'ͱnfͮ?8g#yg[q'X7y^x˯&}Ĵv-51oC$~0ʷlo|=&2mthۖ]eԔ!J-ǩPjT,G8w gn+Ҿg)W?/ܚ5 \tz}_Kv_5O@`M76?!'UWzfKUmP[ѡf}0;YjvԬ䏟uKwNو蚱hQ1|?fLlJ@Iv>|.MQ>"ufLjL6hDfy'/N_͖z ~)Y2]V?جV0lT N;Үt:x*x=)LbB [9z] VP9}C5B gޏ_Ҕ.O9DZ`g/ЦnSzS^a'ы6u*P.͟Äުw_dʾhK'C>E10:4V`|/ʺ>/ {`@mF}{ּut~+U?k5牑cxEtf?VBT \庴|#&vJu3scQjy*TiѬv-˟ӧyXV=délНMJ+QU/ŽUp<< ݰO +]j=ƶF9s6l@r{ӿtb8a KF=I{+SBMwqɻ|\*ƒr_q?}?3䕬mwc[/̹=vq=ʬc5:6o\[ ۼStYŀ^3 =7>8%_?J=1ug?nzv}?|=_ ˆ睳4w}ʷ_Cex!3P9}hԨ!+yc&&|ͧY+tw\uXyze{XF 2|Mw6z-kU B =1o{QD$ׯTJ|m%{Wɻ)ڸX5~^gx?",)0v>xt 4}뻂b\l';vv쏥ݔt-B ̞8g]݌"?4:5. ױu(5tY rX˥ѫ(E6DE2Ӣ/QB1u|jfӾ+F )y .J紣8ۇ']^!]&~S,TH#3^\^M>`؈ո}+v$˥?"v<Ԑ:LY˛ѵ?KbQ.WOJYHr{:cEќ޹2N>|ԭ?JT=E „0R $e]~h%Ǝ`hٺ>4C2@eި7 (k>pc6n.hQ,ӆ\ؿo6; IDAT d;hm&⏟)?MsD{?&.kE"G3__Yn8ݳ :U;[%_*lX!י< G)uߚ*tCz % .ȇ&J|IJɱyfOjBa~\2 ֵT2A)~ysx}q;OFƿSwsTItc GȪc攧yb6Nn_|ܯ.gԥ7kϸʻQ_%*G{jl$U '6_ga2Oc^cF/٩&&`漹p*r ʕHAAO\&֘[gzx7^w.}G_Xy'ۼ]?OK5Bi}H9&wQT @HwBLBBz ҋ ]J ҫR4WRCodw?$@ʲ~9slvgܙ{;3HcC=Gnn>URH`i;1mx8ёC5?]'tw!}3xS͔BYo-Tz-ƽ`O.+O ͺz~Qn,ǬԾc%ess~}T+?+|e:I tJ[M͙y\yS|Hw.Ю%C4}JLF)rTAM,#U˗̤j3qZO gcfSyr?%h((!JV(BRXUYINoR7=eP:Bnm=O UXVvZ:gԩGjRqd7Jq2.Ld#ʻMǸ{?ja)V+* AzLh0X1ZaTTXܤ zUEPʥU(Q_ئϘeRԓ``QpױpS] DaSCthN ɘ: =7NB/hAq(nj痣ԫI=~ZOty.)Xj;uεK<~Jcd.J㵦g-y$m&lX1EgQx^&5 ,UVaٍRJ{v,I#+(,rO+jVѿFJ\'$ɬ;W)9 -F)8+.k#CqtmUހƌ*V՘wTQ|k~=*ݬtpRٌT\~Ƴ4}>eh\;$TaC9%:yۢ7*ܔFa%ʨh VgLSdO!qIIwjj J~7ߜk?絣{7_(WJJ_hO1A9́WN5bS Pw6/ǿ=!}_j&{+ j5SɸZF.OVL:?&Z[CZ-[^*(U3SSlj<v]QZ ER ~a;GJF(UOoM- 9^*d/G*W|a:jj1=aTLI9H*H?_Շ(cT|T.,0u:-b3M)mCNwd\I_L.8嶓T&LyuFL֒e2k[j?;3c"^Ra ?M5Vj V_̍5f C OWڍλT%W`Oۧ-WhVςzwڿI#'vԹ!.P{֯'rvywTB9.ukoD3e-M 'j|er!ÊdҙO*ʥ$+AUJi9H*P v{'G3DO o{c#͛Q\xm vϭ99OD,W4vj_W>5RcNf]ܻG i`i}UطGLwofk;&=HqcȪ,n&r]y 2dL'cMݼkQ"Wod6^ Yڿ,!U) xc[oAeJĺ?wS%+|#[e*qoˢ^/rwTY)>YK/6ڢ;F)6dZ ~k^2L9aT撥k:Gi?̺v.GU}${vԘSWt9y/9([Cr޸DݪSn\uzvWR!/ q/0x6-ptG}f _Úm/fQ`*j5L;A$I&YR<*Vɘ%,8"ƍcvc7Wjw,?=k'2e.eθ*of6=jɘN-}5]E4DuJ{[C #tIStڍz5*]2F֭{϶txQY˩AE6}or(Wv?>j\?=nKF "\ҥ[/.'yN5rn"]P=B"j]j̤jǭ|TAqd2 2ly%$ɬ(dRu"|WaSs V==1v,2%ⷽڻ7fP} 2fY{kryjOև:Ӝn]Xې}1iՊ7X݊Nx6[?{nl}׬kOc>.%Ua.gRıI߉,kèڴvBOT[PY,v`~ܸQcM[~-k5Wi|[^D%`܌knhUʔVg_oH{i68[mC\iU f9AMW5fU=]W~WUX7GA2[^D掍5ȜWW~U,~Ѐ%UW?.rpC:9k#OV$):T2]wSԶdFYeCR,f$C8H}S9=:s?7ˣ~K*h"+d Y^XwX_6RZصJVw,n~{{,IR>m^sGʕ5o"n^2't{V|/,qφ%Ԁn?,d_DW<7i\1y'+Zk 'mVI7t7dXdV܇Fb__ P)Kӭ3n+uw,~k5GQ-OL=U>obҜoN&%G !K`O_LfN9||ك-r3Dij#7mϛ&v8DzbyIweUHhQ3iYAWVm۩;$[kϾkIV /+'B=׉:{,grx_7;gS.9L>s]@inj}xn's qU NoUƘ8fRF}ն==8_ǢlؖJY! s+mQ֑c_rIJW7O}(s/,ζufy)I>1gi2eS;]9|J+_ߧSlѝVgW9n}'^Iv9tS{vOَUnM^A+׺ϗzxkIAA 3Z]H2^}\+s%_!:uNYY]Bvr,sAYr+(!I6|I#m|ҁ0*[3G ^3C[QiܓqUBrR`tlv?-SmuYA!oXy,Wg;)HTv~O * d޼_?,8ku,:5S5s@mwOO\LEQwkN̕r2*Mj>d6,GW[#vYnsFk]o U^K_"vV|/0[bmdkiǘ^ WBt~/:wy̺2QHkZvrB.z+w:͝[SŴɵNKVΞ$C q]0% RvtjKߣȔxljP#lrI{f~_USZŢ/͵ֆoWVZz_tZ=~Ygbt,ߐFY*(oA5#:qz5YG{mgC:S1EFeZGŜkĕdku'GM >|o,}`xufJrɯ_ Rѿ_tOCvQkA 4֚u~TVvYk۴8t^>땰Wh*m^.o?mkGBȤ?f|ҫZ=_mly: MtRmܺNiNVϪNwUԛԧNk]N[n깓;Ȣ5mFڷ_n]"d-OqcYczS~R͜g5z:r<_Ͼ']p2hۮO_~GD+(ZNI"k|e*RB!+NnJ4ʿSaϨmղE7*l.r⢼|G3eL#4o(-q{Si#U'׭{ Uj<>JGJ{նEƎZVqv=]UU:ЪE4jfѭ_hϑZ:j̐A:'Qn es~ÿߊT j&DuP{>z.a۩Ҹk^c;kc_?jޒ}RAQ?7jG ʪp=tMfr~lWR}: ZZʡʒʬЯUkxkojFZ TTqP?~4 \dn]Anz&4k4T<5 (XiTӳ"-նR Tn][/HAU>[\A4Y6JMиuE (CvtyyȃJjT2Fx|2\x~6ײ Q%2DגSt<[C ox^` |Ohrui1Tyyǻ'v+2qZpӭ7eV" c T^rү/־"o+eɶ??ESoM'vhU{x> x;S|~ ZP"K4W3y^myeyV"CԲV쒱:.EU#VVNJЁW~Qԩ{ )8mklόQ:rNdᅣ/ٵӢ *ҿ?PKKz*iG-ZzTm~bm6L=ƾƢ>q$Pf{E\گSW%ꇉb[L jBſ-U|F?<.J c&g XծTe'n]Б]' 5O%GMvoVUHÛv/y\r5RBn2ePxޗrir%*>ƿuCc{QƮ$9ة>oUYY ~E."BRStO:`4%\[ֽGۦ>gCwGoX.gj[HajO$?J64栗~.7#[wҵ?a?{f؇5y"UHbwu.2z8zN7'QUo66ꯡՋkbU/Gsh쬼jdūn*[h}D%}CXjzwP}ni5VA/) iީ呓~jݠWK.[& m̯wA,Fe3D}VTPcհUo:7ИcjO-1Op+_ͨchP4rP`s=s_tӻzWrˢNSilbq,M߽BV Ż&9zP#5Mː"cSDgݪ3R} :JYW/\) }CrYU.o${+96ȏWQݑമ5=5ՄiU;N_샕L:|_ӿРs4,K$IoBg.ї==2|e5w/wKCP7 IDATiR;}:|f\J9Pd0S;3wjYkq*er~Y>zuvY?}Ő&T'SI)C9vzdzBÏX/8Ӕώ|w}?>[3,QkTIY_m~. +"72xaȠV?WxT: m8?)*OCZמj~K T/a:NU4=2E'7exWJMѻH[g}i+}Zًwb Iw{1lF M5xKCWS?&y $!tYcO&{Iׯ_?-44T~j}X ޣD;;;8p@u ߤoȍ:myĿyt-uzuJQo}Y[!iJц)AiE`']:F/< ()r寤sah7D=ڭ_oJۚ1E%x`PL* M\XN GO3L7/[&Ku-᪒kS8dXq2=^ o7ש}q:r.'߼MR::x]Rt=;n<Rt&FF}8Bf} /n[ xpd2q?:|ߤGAEUZI T=]k-w#;ƾ5mxm5nLmpS?U^U<6O=U[MW?z/NА&%UNɷ !B=zV2^K'W{ ?_k~?{`%fLu4eK>knSYUmxN h(|SVўeZݦVI/]_a$=b~=[S'wlFF3xPb*QjM>H[BN%״*w'2c m^_aY(U|zΨ窖jϋojR:Dҧ\|Tdfb9oz=5r+Oz7998#{Zp"ɪYrLIoԊ/W],`]5 |eƼW\<]ELxok5Seկ5*ITή̖_-柗Y!TMy3sSfyz?Wk֏ j_*ܢ|ZœQid NrtM' OJ_{9~t^SR˩rq;hڅd pxo魆]4ctCSotih[ޮJSE'g0OesҚN+m*9:8+WI5[,nr7l[E=*종wn:UJQ.Yt3IQkS[klK7HA#WcҽKj2p-E$IKԲR Zt>]QNi/aW-sUM[*LE_N d .|7jL[ws;+4Wճϭc7;|Uܦ[l=UZ)"^AJC;4[/OWTfYmҶ!13pys'j툖I#nuzN\"ic|kH{w)d >y-R?.tZX6d$l\AmSQU1 mO+׃uPVD -< 1s|{-NqPUn 80JE_تןjRNw9ইfC{٬~ 5x )2*vpբ;聣0{zri2}(]e]tɣ4O{fӥ#v nS&̪ .:t>Di Vl+tBTQ55b*zR uRӺNʹw:=BQ5.Z?X_o,?SrX;Z㘢*!\#rSGpHe6[$jo'ɤ_rfZ3*JRa}FP['WS 'I2ٯjV,'y 7SH**(+'ToT>fF3KpwսX]'ϠBժ\X F')VhŐ:RcipP7+)ަ7Z|HC*$/m֦t3u4_s#>)uneUs1E#9蝥5 *eRxpUYL=ي3fP^e=^:sST sޯ~džhh\OvOՈk|}o$˸;I2?Uq \ђOfd:0Y葮F ASԲODтm_];w^D6 cۧjYej/ N:uah-:56 UR˦٥ g$Un `_~ܷ_4gާ}zHС}GYzYrvvU;_KQI#㍫ pN,Y=5԰ܳ7L{"W\ygfOߪe"ï٬ˇ]*ʺղ<k*oiubT  V%.٣vU.]tm]q]ss+!\W[ʞtήެ6KD_ ulZRq^4vy\eǚјMo<)$5mJ(4fE<|Њm|߯hD1ǜR}__☉Syuc*o7]}\ŠyWKq 2?(o%!*T*LDz׋uS͒}p'}7r2fHx!f$j ߵd9Le2.wZ@n >j_MVvsN՚X%If)&lmlpA]-,]<e)ٲ=?sTdC Gf=tmRKY}pYf%ʏ[tynkyDGLҊg I'suP>[fN`T,N w4N"=?jɚغFWwdy{;9/0;Q .ʛ_:_gt&xXK(QJDԑZ|$j7,%*woد=*vޯPHyh/Qʐ?viT|#3_8*8Mm۸O>Ȩ (7mӗƝ_F..ܾ۱ 2*c@"hJ[խn7&Jϣ|A:yμD@-Jٯ5U"4H)G!!h>CcOSH7lڏ_{*ǁ)>&7] Rd"['mJkzWNjײ隴Y&ԗ -UhBtamnCJhwXSqEu>+Q;wyjAuxKwYq_.G/_Ɓ;A[eN%)[!ʍnayˮajTzUm*˨.ᆶNoN,Yqi<)ڇewUr.FmCjxF8*dC VI*eM:}^V#ZҵCuMd=QfWsjX}/tfzUW0e)wp9XJcXFŇIy{ 1Q}j)z\PAFyz-\D9a.9ϫ,C.nnW~n*\<05@/!eFye%ԠYYa4o=-;zIm}<ވz/pUuhS_x{hIe=ttDǒx:^mRyo*JoM;^Ynh}RS Z2W=k٦QM A?xV=hm=teՂdקtO*GѺ'j`e! XMR5PnUr,˗et*8 FTݹ_nZAjy5B>*l*O TuiFTmۛmpuV7#G~U9O:J2ey_8RtYcO&{I/^\eX^۲ #W$9RS׮]5a„\rb*WgϞ8q$)**J]ts%UIڑ}^d2ԭ[WY$m$~iر/޷o_=:Jr %6ruРA3fL߿ԻwWQ$A %:TFJtdR߾}xIp@K(\ׯ]]]^WJD Y@>o@ ?.1r@J x`iIp@{#W{+*$@+#W$˄7nx.#9HR^^^22 2ͯdFQfY^^^dV!ٖ ?Kǎ;`}lѣG5\e*$7o^>|X%///͛\r waR߀?(uE]Dł@؍J,1XXbĊI(Zc5*b11Ŋ`G`wmYXufv洝,L5jihhȺۦygK/Ͷհj8p`ogVʨQ|;ӣ*9rdҶ5W/ܬڵѻ&@({U*RW/\Qmn =W/ܜsU |ᄫ%WJ0'5{nRՖN;-:u<5SN9e뚝 酟GqDjjjr)p;,GutST>>~U 7zhjkks'>zsietP*JN8ᄩ?sqtsm<|ɓ [\pS_jjjo䤓NjrpJ$fepBקZRZ6jjjRVS__&o>̶-H1b䀵-j^ziz__s=QF6S__[C |fge1,@ U%WJ \(A Pp*@ U%WJ \(A Pp*@ U%WJ \(A Pp*@ U%WJ \(A Pp*@ U%WJ \(A Pp*@ U%WJ \(A Pp*@ U%WJ \(A Pp*@ U%WJ \(A Pp*@ U%WJ \(A Pp*@ U%WJ \(A Pp*@ U%WJ \(A Pp*@ U%WJ \(A Pp*@ U%WJ \(A Pp*@ U%WJ \(A Pp*@ Ufٹyf[̮M|n(A Pp*@ U%WJ \(A Pp*@ U%ԵՊѣGgȑm h3=zH}}}YfΧu0'hm+MÇ!뮻n[䫫!oê٤+3qlylT+йGz9t3_*Ү~W>O6Z[wҹYYJR|Мuѭ12RYy3W 46>$;輸R~{]!;_|P{ag%"wzuK.%imC}嚭mY=z}#|su|^Sұ|YgMүS돂3o53\7 ^|293\]sYksAgnHޱg/u=;Nx];/mn5[1?X\v5aP{ϻ,v5Gl}J)f`3ȡf̨7CW܃6o_3lep-.\-2ɪ?{wȝ{MLm4N+JcyML!CWB?;[zbt>PհAl|>!QFsà'GeEfS\xv9iid˃Um3o+ߟ7מ]bH~y%~Θtlu]t-&.S(#4}0?O^?g6xdeWTzm~ńڢ[3G`~0eO}cl q`fg eL!>;^ru\\|cty>Ɉ+-]g刻L5:O)I}z-zV[[Y;d3~_=\{M3q{fH}Ytmr-/I멾{Y+7]:wKEOǿpivp,|ֹs4 ^oZW24L\~m1Eј~6^},ܫ{t^K~+;|6Ymڹkz,TVh\J4{:-X3k9aZcѹN[?S(,?wK~kgSc'uU}\Yu^w 1=gs&yB,}]M'g6Ϧ[l;.|ˤy_Z.}{tOͮ=w'?},?zѵs/7> fϏ7[#K/];wKVSSNp@k:w%1g4N\o{c:vn[^"Ey=큋gYl{=T,W_-kA6\GaNa3E_m.^:KEVG|{YkһK:u%3^u~b,ѫ[Z2yyu6wK}~~Ϟ ]ҥ{,:i~䚹ɋr}ߵWg~RE2+--B\79o G>kqf^3奡fKݖ;_]wyg4Os~"9e R1dr[k+ vO<5OIZs~_67މ;fJ2dl|W,r/o5N?' $ڙ[s߫۹(IϘ>'tձHosOUdU3f.[jȺd]e"߼Mq&?7K3(k-R'\fm !ɷ>[7Uy~|n~BS/ko8(+%Yg9[ÓUn}*IH&"ܲO'q;)u8 ANuY^[2a+s̒NI}tg^8"+lܹojܩyr]Ⱦ/gQiqFl91K瀍v+jGMmlߕN"Ϗ%& ]+V~ܲYgb5ɠdG;eӧi=\gZoι4_yCl$QQEiUd2~s鮋nrQ]lw9a.$F\m)G]oHM{ogvxؕ:>ٱvJ40忏OԨ:ńAk"Eј]sn)] ;IV}(+yK9٤HJX4^zr2YlkLcq?Kϸ3.8$[.GlVgQTNCW )j̭6*v5<Ӱ΀6yI'Q`?]u2O/r5w3(>Ieޙ/э)ifؚb2w~cc3w9lpcLh9f}n?eq_e"5~JXwP5/KѷvU<׼9nDZgΙjƏko5(1]j"ҬR}4t>\<7|L>gw)4Koj,2X[EBS::7'>kO:9W<|9rL:tm;fg)5hrN_⇟oGGM^S7Lc4GI=zhw>t#5ot9dv)ZU;:do"䭏kӭ㘌wL4rO}|y:lLh%I׳sэEzW-3<,W״NOi43WwH7wQ|Q[>Ԯ"$ϫr?{in+YbrE1&/>rli@մFWSh]۳(& /0̵̉UB9&)2Vm87ǟ}kx卼.al?gb‡SV5@TFE$=һGX>k4mf5c?ʳtZjnZ뛮;w}θ!f|cקX=ߔi}}OmWIHU \znϲ]~\A9cV4[TQIelyZ|3rkgN *鄻ռuӏ3|a4lZFޔ.%ŧ _-RTԶKʄF|KMO&&ޯK&3!ԦQ(I ?,lzIa&uUm|E+"ifoh6d㕚tc _ U@[C>YÔO3w8Zf.-.7YeYɱ1.h:}^29+i4tq),NsNj5EK69vo﨤]'l(v?^zW_ϯ#~7Zm v{*oGWθث6li&oaѮ]ڧ1 ɿw]vTj`-3z|+mv0^ ƼvOr 4=p5E83W.)i)jZfǠU:>WMјן~&o,m]=ZSZIDATEjRՕ;g[e/)J a1YikEf]5{-wˑZ5k.6W+E~av|i9ճ@s1&M]k>ilp/.u*)-.3Ӛ1~|SکR-+m4u5q I1&m 5mSϠ]:ՠ_k/*?5_vls߹~]r1k冇ǯ[3%/\_}2\ {fuH|g솫CKu2kc^}|&.q]ki/?i?w+8zpe.vKgr㣏ƕxmT#畚avLGIҫz[Gު>fV*]6N{oιZ2ZnL]dmү̈'XǸkOtsk}ƹOך'2S|A>lҚ[|,3'yk5#I*sϟ-5eNvxlE(F?Uoztv)IRD/QO<7+Nݐ],~9h`f֤׊+f?vM'ǿ|˧okz~#/ٸo >km&V*IEK&!wZ".Aa1@ʶ5*YnResw; bneV*ixаB=|B5I?}ڥVEg#2~2Q;ΑSv Ncyw|ׄv>hrsĮk\cˆѓH&&׻d/nltYv ~+f=V7w~{[K+n/[#{'FϚIr^9M>mly?g:&>an풞'A?a_ElgϜîOzdH~ɓ5ݳjۜ+GmLj~[շr'vrnd%X>+WtS9`4ߨz;>y_}\fњ"Ef~,tO}Dz^kN?&7MыVJYa@u9{Ƭ5vl,5*ezgAsݠ_gf+R,LUM\;64o[[=U^fmȉdf v^nu{O]?}6esQ*g<2U)KϥH֗w:" nE>k=Yh5ifZ ٟrJM7*훇Yz ro任;ɧ_~pvF7KދfA˧1V;=yjҖVO+wm~Nff3_ֹˉ:6]VR)>;K_;4k6yTy{/ ݤL(LhM*E ˴t E"EsץzաqfӡRʴoc?Kv4t  ]>1[>;){^+tpSWr9wǦ]"w1Y \`\ 3|PN=Ϣkmu0Nv ̝7 ?1G|V>,I~s?Ƞޕ Tgmk1m=INra}iiߩ>=X%v^CN/s) ,՚YaĜvI/GqY|ս.girf"I*sܭkufV;ڭï<=/acݭGAzTsrLnJ-No39ⳳaUxmMRS3{i=^,y5UwcrO:ʛOKյXۛ6DKѡ[zt4e\{oAݿ%bfDzU1;9's|noH.&g844,oZId7vvJMɷwL.s;Yq_fZ[y$;=߻|䊭f+RYj\t9õ2vt'39LL[i!ui͘'|K.pTνtl{J+,URSS&\ѿ7ԯKS\Z.l9㘟唝/Y~ϪsW5zmY*I˄ԮTd-rV[mժ~hR_j죇em>!s^S$IVjŜꌸ;&: I}s{oջn 2d$&iH2tsHo˨B=΍ߘ;$-R2s,y{7vYj>^a^y9Oݳ5Ou4wfZsƼzUy|ܢYbzS^}J/]i: \?cSm,?\ND5D8ƹӳj[rFk೼]},wrf]{JU [!Pm)L{6=:͝u=Sz Z__jJbljjjRVS__?y;:wG#FHS8 j#F̴u0'hM<$\0`@^|y酪V`NѪs4g4PM7'^Z4shiM R/ `$3I'ҘhyoӌZd*+m:,V0'hM6:հzL0̴yU+0kMN:MՖN2іiz`3sl4FgijX^ͳѦyf4,@`N0`u.SfS--P7q^sZ$Ι0jM&MU[~M&62[2l(j&56yɉ?7SZWB4&V V6mNWZHaZ Bl^^U*0+Z W'Jզ PL|Upu 4=O U[ T/Bkh6<`m~㯶 `[<` n>֌ڼitztЪن3іBؚLVi7'hfEa>O/`4Ͱ7R:PuVYI3uzM3,@ 6tjZ؁V^d U 1/tz!zɴk%LsuRښU IDATxwxn*!B'ХޫP^E&rEFA@(U+EA*lG )<;gΜL&/g1£nUVϯxZ )ҜgThXN# //J:67V%-)LNAlukLLݫβeJ6X8N\.WaVDDDDDDDDDD bfrrbZVcXcfaSDDDDDDDDDD i8NV+/X6iN*n"Ӆjj)VtiƱr(^Xfqq,"""""""""qvcLȭڬ톎2]l6i\u,""""".1v]V||ܱv{DDDDp: bqNZTP/oob9wtFR/VPXv%/Iio.GDZ\=Cӕi߫xؽt Ɉ"88֭[Sb3̩Aa]/fo c\t)_32glC1c_.]2Ȧi*y,"""".in}̈́#FPNmʕ+Ctt4\pPBYjתʼn'(U7S4pFs{X{w|u24M ^%UBDDDrNG66\9g`Y3ׯ_t#Yәi[-6 W|\v8AeiH:{իiң^KDe+DDDDfO祛+ᆓ}(S&#$qyĜy&c&6Ofk³=c}`6I mCFհ.7>):Nnn6G. 7w7.WŻ28683OnhXI[턆^7G8g偛-'p5HŰs-""""7:ݎiZvړZH0KSdZ#齌vLӞ*ylF ðX0Mp\8viÂӰ\/q{=0ODDDDnT aI=c7-W&ڰf{3?vtOoX0p8gEDDD ::2]vtm-DG`ILf68xcswBxbr:[b0~.XFR<>br9Ι*^=0ODDDDn\ ssA zkQxR>%ڀpіӳ׼Rڥ=rUto-"""RԸl噜 θ>qJV+`ْӋ\2?"LHouH /hN 0M^hќUg{#\l*[!""""E ;Y/so&,O⸑0<ÊGv҃gЉ|xo8qn؀wt|]rz)_,.Ws$ .y%ٛAdtfoc]%t%ߴج 8WtbOMj .ܔ<'nW .P/y-7Nc;sC)#ibm\]^L["Kto-"""RX- N:CŊ2-/j;'Oc`kӦMB3iqge\ T1!8#BR4痭XI/izt6-?'""""rp9#<߫Z"{`E|LqN0X,=(mb:&ִOR4Mb#%,8bvb{_ P;CDDDDeEJ)Q/UX,BB8w,>ŊaK̏xQQ<^2N1 Z^KcV|Rg&?t7ML,OFWn.(^MMޔLKDDDDooOΞ=KLL,KJ.#[M  X1/ #miɻO<]< OI&Ƌa^X1t%Wc)2ҭIłaaRxq<%|liwCDd$aSH["?yy{.Ot􌡡{,ք$rʁ8Oymqǧ{LޭVHaڛdpp0]3-67 5{1VHN 5XDDDDa1pww=o%H}""""""""""oeh汈扈HJHcYmr霦H*n\<4<4/yl^bQ |ۉΓ#l~5:ζ8ְXE|&Va~:XDRE<*>c͢xZuoٟ|,i괃NX@g׶={?ՀQ{=lp_vfẰQwone|1N=}@x1#9gU?]'H؝XOIDDDDDDDDD;/[C+34{K)X]&y/.%L=ߟЬ;ʍ3QFػ!ZQ:^`M6%Mc-Y֭8̺OSgo-'ǗŊ.n:KqyxU^MM;xXeO oJ_mJwۣZkzu;Х{;ʄv濳g6."""""""""ELj_I5 kظfc%, N^rڡ9.t'%]\w3W 6\pa|ݏ1G݉h;1tqfPLp[Y0eSRF=&}![sɕL'BM(Yҵ{CWq """""""""r~Z4&i=olMwOJG`NuEE+&Ӹ7z G?o8R6j]4x 5oN@#b?[!,zwUR)1){u;=aZ0L7%l0 p\4ge'ZjeP.!O5!8>Xp_<Ϙ%Mt*e+@t{ʓ aV~URN.M}¦AJ锸v^uc{!\"""""""""џlDز8$<|A^>Vi?a`:uEDDDDDDDD(ʿ QC<7w'v JtȪOF n8W>2Evcnq\͉ƚM `æM?˕j3grY6o ӽkWy&cy T<|[ޖ֟qqڔ*?p0'8wg[󈏏{ll ?[ě3<k+W')tN j5#x%>O8ZTt4˿Q˲+ ,Y9F>%K9{\qJ.3g 9eWd9'9?R-n\p1/wkSo~~\ظ|o+#yrN}hq<%"""""""EvS:ﭼ)IW}s^* :`K^:}:v_fa8NWF澴n???پc'kM_1|hpwf 3e̚Vi5z Ǐղ2V !9̚3xztƠ_|3T[4t܉/d2o܄;Oy/}hy*+*DDDDDDDDC5wǧz;:ifSﱌ8@$9;Gu]L8Ӊakۆ_z֭ZJѷO/wŽymDv܅bTK>NHA/s8N)&&7g!0:JX1 :[63k\?秭[1Mʀ{$8fΚMճGA`'>vss㾁P,+*jcHDDDDDDDC5۷m fB̔s##3I6{#ccԈaL2-yFgNQFm fgkۆ'ҴI ssscLMN_=9/U_pX:RrZ7k+{#Gɓ|7;.zꙭ6pn}{bꔄ= ]<{|I@楌JAiݪ%ڷK`ܩ#-7ˣV/R%LIu?RLDDDDDDDt;KU8^緤٭Jv䯤Rʇ]I3z?@duJ-}֭ԩ~h~q}Ggͩjf}z/P ݻ96l,HY+k4+Qڷf͚X Ge I;m2MÇeVsԩS0t_ّ9ɮtko6ud޵Ry9ya&*N}}'@N8ÇKn;_l6k6bnO?[ ;}Rr6[1eıɩӧYdIrl߱͛f;vXXɽkOٲe,+Q̛C-(U$a®{x|^ya@2YWVvVZ͜osQbN<9z$pG9K\DDDDDDD$sE)QD^n2ēMw[߼:Y2NF>L8iʴtgr=ٳԮ׀t߯P:'>X4wi4=<)U!ŧZ޷ON@8y?*F]XrE3i47%B9%ʤx^U^^^t܉N(<~|e;)Mzuk\.wSَsp_bqL*TbXj5wy;*VLN:Cy,W|yqY{TŸj+aaa&i;mX,$AkNvJ~ឫzn\/l}מ_i׮-T",NRKNyֆܴ'?d aaa{H))!9b\oF۷mӚ]fm^ ڶjf9&ߩcliΜ=j]yxU&xbc9|>2F  d@$c%mF Y9c߭l߱˗>rAA!>HrP0G)!bKӶM<>Hk5##sDDDDDDDnlER+utd`V&%$պuBXqݛ+@RޭKPVt~M3gR-O(U1u?YLjaC3a)c%o^`֌,CF O~޶{d%޽mOLJg2k~JKU _X,#_HҍsDHdW2)S4ڷKN# 6r4̛C/U `vðS%=$9^~*deŧY|{Ys13߭]GPPBbag7_)#sDDDDDDDa_uTsmJٍ[=DLL ߜq3:>/PRmݼ.pyjk@DkUL<ә=0GFe]:ud ԯW7Kܻw#G4j؀ߤIyz?Y+K}r4 -qsMs)V,['KRJyޜ1{CDDDDDDD$=>0o/M.ܩ $ B~2wΛ |||0gJ7 ܄jM4M6 &gyߍ7bxyz8<=y4n(ױ$/)g憛[l4ﻹq]!iO>b)i 0pwwgİt!Wڿt/V~EXX ?]E0af͙nOu3Mww7yz,7N3$>ޞg@WCDDDDDDD6۵Wɞ<=$Z[\ٲ˄ÏRJnlswwcwRvM>p6miI, *[.<6?4n(G Iooors}f+x)!NҥoTk&olܴݞݍ^=zps=طo??n@| _.QOc9:>((ESӧ (&-::*4p)@*U9!jI}zĉnʿV@yH AA)Jt,'-즈-22MrraX#I} 3Ϥi,-]'(sұܨe7DDDDDDX#wν@Q_.uB}PPR?cvD,M<"FcqY ¼Ć7G38 -"raմWhOla$sQ{xo0fЕODDDDDDDrh&]l>?s?}zο?ePf<Wm!Ùp0$eFPE5tjsˏ25I:бmV/}PIO|e ݸϣ*X1O|Nʚ1GٰG]Oc[&LO>D.D)@Ts~ Ǵv9ejۆ*SOn_7=LJˏȸ|=y"0]?鶒*ϯӇ2 [9=JNjĞye~R4-/;<ӖmXD1&O`p3? LBA||sprxZGm7_KG3dv!VJTkFG^dS=pg=i9 V*-F-`pYgr}ه?q,է'yXחq9+Q/%qe>Pȅ,ޗ!\˟d +9s9s'Ǻ\.{Y:w:c/؝}}<0t3}Ik9LHnՏw]~6:h@шvȲ?WpqvD# j'l'p86UQA'zacx3?f:;{v-4H^-Wʹ_7q-"Eļħ'̤ܞW82^%U/#pawEúTR*q똏5,em2ԭQhV)NzMjP6M=90SX z/1wCILɃФv omcc⟕sW4H@lq3JNKX>$ c 0_0]֡rz4˓ͨZd:z`yekYu,fv;}˫ <*(` wJU _w6j]F=5VenQrc$?jN9#uo<"lKʗ}/\^?98JgڰF 3h/R571Ar4t6v^Njs+S[>`Wq# M)kZX1*[+3,q/ѱy'aR I'C,zk 5/Q2c:XͩNL # _,r.ɟoVN}Qݩy^Y),:ap3 yZ)l[?[KWQ{_y]EӘa5%7-fL=ߝDR?E5wk2'>,~}IF,x{{b8cux{'g8r<kf4,a^y>s% i-"vbW { X)ߤ];iq mdghV?![=$ekkwiw Zezym^97):io hGe/'ǖ[=,~k$Sϴ=~P?+,:ypPЙU,о-+׳ލKcFQsKƇ..]"3V]Ф4TۄFoexϠrБ5c}}?' ߚ! $h[7f&& RnT31lrZ⩒jː<99V}(Tw3+f\3uZ<ۣ/WұxL40~ fPn}緬`H@z[8˯N~Wϧ-=x᯿est6jIfu3}d]M8x# :{;KYITxt? NV0J4-I6|7:Q>1^=: h8=營.x`ďdr=tkjT-D~ {8K|Bj$f>FM+Щ= bdGK]#,` vۀ4*~Ձq4"thי }1h@:rKŁ1(#ߣpR ײMg/ɇ4y]~e3uA.] R ]74lһ&O^?g$a7 ;mKOz;~|`ͯf.q?5/|؛kj}+A $s{.k?(E /=exBޗ$E5lԑ[\FDq,խ׺Gw~ZU=p9ȟW3tz2aq ӴP:6$~mܸM'aYzlXq$q9<2rhǠ fHm{M [î,Ɇ17jBUWr9qXfɹ?sԨGzքe7:=e;19:.rԇْ屜*>f<:O/5gXէL;&# IDATٷ;̊.HQ(wc%=1B bQ*jEQ&QD銰CA)YX}s̼3g'?p}~8ZO$)Nz"L~Z߱wGq_WЗs{DXL*̱g_?oR71{\p/y_0fܗG.Cϩw~bu1ՙmnϹSH OOޓ˫W7-ޓk<%3g?kz6^lf+~FX#2qi6-ܖޜmG$IYSE>~dɎY77zVż\Rv9jl YjYmŷBR,o:gڤNe,2;/U˴"I7H|I[>ʗNg2n\|my}aBٴimps K.%='}K7>t˞)Ӡ~2yoeI?| Ӻzt _:Qq\m3}Q9dsyWsһ&9c~f_~9gnqvչȈ{rssh|z{0<9kσruȍS?Iݵ{e6o#势>G}8R;s<^iݡyTsyGsal03Z℗;eYgc]|gճ|j횞36=j:y'T'esswF^e:~~!5y 䤫}w]{?5Yl by?TpBR*ݹ>U]]?+?sONR{yn.ZEZ,%9whfupV3B 1M i㤜ۧ e-WI&ɔ7Fgl1i$e;}.skW ecb]QI0ʗY>ȰfWNz_q!eҲy!㞾;9.oﬓ3Ys_O_?W^y`V[73闩ʪ+2e;e5enZ]q^ni=&/1;|mڧϾ[ߜO,ϱ}O1OesӓϞ?UVYY&+ϼe*7OӅ,|u ͳ9}G.Yqu|z.gɩ#ʒ_A]ח/l,7b X:5WO9\u'}kNo}@XvT{/Yil}vȚZtv]ak>7Ocٹע)$i9g[Isf,"y4k\qiٯV(-.햕ʖINH~) Z{ʯWU6OU9y2 .u6]%.+ԯOc:|d鬷X!>qƸ|Q$V4'\NkV't6ZSBe|۪ ommV O|<{=ШyZԯ?5ϭC|Z}iN,og=fYfscVHٛ}:pMvh_|oCoYk-9-Rv^%ɍ#̲՟?Iu|!D昫>Ί믖e6Ia{yzEY.{T*t->+^/_~ J9}/|,4j(oqIna&L/O6vgH=dY}re;24mB=qFt7ǟSjpfl{^8, rVN2l|.z::x_pNxjҠU:g.<}ѥ?W;~ŹdȰyLQ&m:;e/),u8va_XVvT:jnǟǜGoU韞'X&\=$ =7W\pd2[>#-=fYz|Myi(2!~Leg?>3?1m 7B.̿d3oۑٌQh%'>ll̓sȃg37_9ӵ'u<:ptx>ŵ)U;I璹07?#FwqUWOUU-̝ǹ~rv…[J󧼼4Sfю]7n,&񿘗?Wzzf\T$Iq~, icg^~;k8˵\'t$3ʪTUUv-ԆۺjPq:>,,? |WU'K = ǹuǡpA-aeeevI_'cv!? Iҧoڮ~~ Y1믿A5`,x?KiU `}<2IYwΈ,ӗL WK:5Y4]tԽ%)(-65<~xXͷ'*~:wZ{s@B͔|aLr.&\㬼<㬚b?01%POy2)g?6F+N|:םynnweW8.GNLk~~qWrߐ'I7/֋KNq=;G7*trsk_v%/X _V,uYvscSU3|аM)4La^w.͝CIa]j?53LU>}|zYrJƽ;:w_rnL9sͭ-k'3_ܑ#˳[ea SQm+ ::drUGdg&I^]ӡe4l1tU4o<|٠i֠n6^,t&n j.nuҭ~:iТC<=]C3Vy_ZSUݐ_oz:nzu{f'TtQzwnEIm6g|=[ub4H:]YYzmT<2nn ,\?V/K ߜ:.IGeɲogSQ̕3_Wn|uf6NVug7>{O(/Vo<8/S]IUU=,U=5'U&^AVZSr~6.}[oWaP͹6ٮi0U7)~6:|,|ZڼIb_<<,mV[-W }}|?VqqҪL<= |~6C:_l{L9ntm['})u7{{BVi`F}Vy2/ 9듋֫?u'UмsVZ*_qF1+4/go~o7Gu=<'n1(m~/}L>}:U';SCqYZ_:-4;n< iؼ]:A48\ʓWsډg!C2fjY= `)-zSÆ罎;dsO{"Co0v1]~6~qjr;j cُ0<[wϙW=O |ڭO¨ W>]n|"M sڸ:=pi.Ϲ[53s}SQZ-Z<0}Fa?vog `eT[[MOvzo߆ޚ#;u,u$V'ɤ{ŽSz޶i^HR:+Iޮ[j$<|<6Ya3_]AqB_^Wk1ʅfdaVǬ\wєoI:'Iq\jQHYrYoM}*ve{OY땲E'n~|M9~j$^-_S:֟7Kf|$+ =;/]G3vO/?^);9R@)W{o%{2& ̟Yy\~\qCL]^9Ay:ls:~55xozef&SпU8e-HI׊$wG{5q05ءN; y'O]}PscZc44oHRIF 8 [!m[4KϪ3/Y< s|ةo:[vmel1nl&TGKT]-5 ?mϮu $cr`z?2[y"|r6k'=8$L^;;].NERU1nnR>4on^?:S-3&gF͋szgrPj$<=0U6[;+,4oguggD9p[+䏗7X< +_ɇ_|8ge_fs{WcZW#i͹KSSl2KYY9]_`jRXQ )fC2jwj ' *7)L#FH=kGveۊK{eQ13p=w[n]Ƽ+r5P_3tdyMZ ~j9<ͨݳ-,{(S*Zo[Ϗ:<9AymW#wIz] /K L~ji߼~mVK-&/L/땧^g|p9>]we+ϓWL&dUxz?k[/|:v:Ydlr~i[}~Вf٩8վ?s7d֋eŷ6=/-dڭ_=yw*}0&SY%cWg3|IURoS|>bp;)$+푽֬Γo[_Ō*sR&gF՗^)=ǥr֍&?a6;x,W^ew|kepR̸dD&ٹb)$IiҢt$_䉁Tre|+_&-u!lQ;? k~jyuT;yꃲsZykF3r[uЧM I z׻߮!/86w}$vIօ^Ӵ"iΑ9fwre;a<Ь\i-0}Hf//7<I3,71#9bYϑ`y~ж_߰q)V!Wߗ ݐދR(R(4i/^kq;2tdy#-gfbB&|YH&S(kݏ= 8/wAlw@ݮU eMҴI2q|cs^K?7]GV\/S*ۼ[O͛%=:{*Wwzw2=<1C?]3|_0䭴7Yo߬0S֭&f-tS&er|f.>,4ׯ5j(8!g}_U;jtyZrTv` 7TӷoR,yF=zp)U׹e+eפG9cu=w[n]Ƽ+r |z- {>zMGW|s&w( j_ͳ*秝Zec~<P)ܧAm<0VZd﻾X<Dǟ:$zuMҰUlrUynR7g;N6+mgeɱONTHީk/R/u̒!w}iҺq4_j|2.nuҭ~:iТC<=O\EV̗Ӧe,tgg [}J߾iZ}tO<^"{ 0]zg8S}#U&{O5<vim5=69Z%)fCǤóg蟖OlB.>6==/==wپbzu4VǏr)}<]$Uyr[>y$Y3WpV8sh{LkTq.VWc?MLY5}Ld5f_]q^9- IZ!F-ܗO/$I٢yltYi=Yݚ<?m5ڶjG'SO}ĠzZ^|LlӢ~ׯfeYvcGSRLB>69;J}Lmk0s]rzvVc)llUYѕDž&Kfh<" bruQkSHYrЭ OKϋI}Inwϝ,/5nιӹb0oʳ~벽sB3帝ӵmL~|qn|: xɬѱE&ޙ-H<3ZYuL}{r!-ZHl|^k1'alEƼ~Zmw@[9OB_.i' ɘei־K?o\Q>{rSRl&oxp_g\6jH~7ļ72Z.w8r*RHfmrb[ana%O[!}MŹiĈޣ, {LnjyW(vc~ PBx @Qx\n>6U]۵-˳b귳r|->L-.I~j(<.f7mWKSnZjly5yazRh&tmP3U w刭-_ߧkL~gtFHZV$em}jb*Yy\#ylll }ֽveI&%4v&=M-Ӹ^m6xznͥӢa4njv8oT>ق˲F+gVSN4^s8<5a|5Z6JVAWI$S ]ҰvcL;7JD/_ ˗蔎'f䍃ҤH]gteC}rYϧd]]桓ȩ#%I?|&mz6=khx6][N4ce-uvH}ힺwGߛiN*m@;/yjR=\Q}@.?k, V#m++5{zHV_˾9}kV?0Ayڮe6ۨIu޵~}VE y|p<>ߩڤ̉Zg-N˒ͶV] Yc9#3ddž*]{^n+'wxB6o$k gcHφkwW}'T936l5:Yz kˏl%v nd~NJn\369YF~ Hx\9z`0&lvVXi ΨΈ%rFSkb >ZZ-mO?7/-O2T-R|kD޿[\Vܯ_z]wNsヤ:m/)Azpg[vt׹or-1KU#=HGN['fk5)Rh{̶M?7MtKIٯv%-B'[l+:$O;1wsq_!g*(n)9w 9w(i;Y14iRƏK׮ܩSzѢnjICCC =smƌ=ٲI'fI&}2q?[G3>eǏOQڹZBx M\֭iӦ6,Ks,Eg7wJ˳c1"1sm ,E<^cE@x @2kQfP2}'WZtDx H3ܖ-UmXbilY_suǨ,$9RWsq3N?m|`hNx$st*j:Ec>*gyz,iG}V*ywܸs֙_Xx di㙎8Y'h)1>Z-J%Z9GZMVK}}}t9c_ZQKZIZ*;6[y )իW^xaL09G}}}:קWϞ-_x lAE݅l%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @V-'yɟyu/KռӢx)`7~>`I7w~Z idPd[p;/-bf% Kf^`-ώ"<. k_˃ on94G89mXl5/;V=^)GsvyNvW|,hV(r^]^]: K4u/ߤϏef~bryMlQg%>cgr;ں.m~7nm7^:Г̗Y&Icrlq}Ogv(ȋ|k 7"b?ܙܐ>bޘ85m:}65ƗFf/9%J%u4w?SmohV43bHVl|?\~x|usիYW咟!G~qMWR;*'wo*_-#jص;CrwVyfoI Kbx{E`Bƿ.; 'lRɇN^ݏs%{zJfg3Ƶ mͩspsf|0͹k?KevîG60/9<+lv..&3vɞW9rPNOVkA+o&fJۊqZfٖmsOen{-Ǯ1/6٦qӹYotv~ya9mfoU:M7_uv\׷ٿ~G) )RTWW\ we\[@sՅ||sQgB?4$sб9sfmRH+]7uMBs?j)R??7hnlҥ Y {gO̴![f*752vi2irWӐ"{6?K =fg{w}:[nɓNotSrI_ E9׳^mz?'|-Oy(O9,kvmp涳}"Eçep ~Zc\{dpt53J= ~S7=u|[pҘ"^>o'5:tJϠͯ'4lOwk)V ?~]nyk@< ~Y-/ʈKOoRFG}˜pmmJ^{\^9m0sx.ft\.Eޜ<9[_%#IoN]/xd|srqk7Ik3uHSg o{ϔ ҽSR5h9snMʏvz6m7sFGɯ6:Kܵsd<|E9tyQ:u SX<{EmTdjCr桳n=S7dC2Vkszq^D5^Q9tlSrSOrIgHmҡHurǡ.[aT^u39&K)34&=sN,՜kh:-ə0]*wMN69촟-CIbnǿMlsm$y?mݜvټSC&>US∽yfo?[Z)Y+ͨ_HױHߟ~;5;]zs^U\7|hv=!?}l%S -X Lm.-.Y(J2{vn6o̓*gi(4Ny?xyy2sN릮Iqyssў?޷"ݾ~Oز}>LkEh5߱(TfKիsϤmr/~*IJu&3[U7 gtv^71hd@z|kR,nNXf0Q9+YuYvG^~mZU~+y)?\*&A~Пr ?syynߌltlo=_xd jU(*iY{Y!”g͈gVQO\C6hoz_jnT}Z6ݬ6-Lz' E&"ZFQ_<+e|Fu&KNN^}һݼ'U̜Q#[?y!?/6}4?8o&IM̛栋ow?-52;<:6lQ 6W&ζ=-v:#9,7;"YW=Ѐ/b|s{9xf\]li&!#I،6s d/ONX59><'n=>ŸStm<ՅjER86#v#.|`a<'ղQM?0ѳ۴mw&OUi|%n4֚Vh:`Eئϵ}Lrt+ȝG+MY%]iΏ=''!cnM߻>䏯7dP& j y?2)M\|3Ϝ]WIZMeZe+fG+J6,* ԽdGƣƼ[]+Ybj!;_H޲JZ/jZE̱C-ZV=7|-5eХiVdɆrڣ#򥦓j+,RҸY$I>7Cο~uQ8WkXvOxL~zGrQt9ow6o_~q+e՗J/d̔"|hڇ&}ujQˬknjIôi\k]IZIfԼVs?|+7KhC^r}yah ba:iFܤKgɈ'C i^IFSөc` 0S߸7g} Y.u{2s/Olr"$Chf.z+ڥ]_Kcui5]{ 2o:>] ]$J5ze^mW?!<zH}'6Ï2sN8tì9mrQe'_)?m1'|PάӚ\ _=|-&O>HӵKZI:W?`nnxWͻmҽ皩zd,X;|yr׶F=`wǜ;Sio儁cʲo]{vNuSvƳuk+??)7eoߟSz,sg8}F'C ݑV\40͚:l9oa?ʠ9szޖ'~.|wmy_%9 lJ"RKx\p%97dbnprϰ#ӷ>z9rкCQnJZ:^%K7;N-/Mkm3s܆mGKU^`,)/;9?=sp~Rig7Vݒ!,3#_KeS˨NrMk&mYsP79벳sNuڇ+;rv.ٻOTLv8}Dy|!J}|y`zKM֝?}g+MEw1&9.˷orꑜvͫYruխ ?l}ifGsbDՕ3{e/ޖ0P+g-vɀZcy3wn #=;g_{\:J: 9sf\:\' y3-wohk,[C.; e VM;e+u{AxӠj%{ʕ=ZǏ>w}ϫ{h?;:s5X,杫J%0kqgڬ5K_^dj{3 3.Yם[$0sY hQf`|-;$w5$`Q2C/oM{gקVRX %RZMVK}}<ۨ{X5束ǝ;wؗƤ{T+I>zUX4tO!`ռw~Z+/b0zt&LТT__ճ<ۨ{X5束yZ+,1Խ|VUuX(P"<Dx @1%cJ]U<0jԢzXԝ`ҪC,f01%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(P"<Dx @1%cJ(JӺIENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-scan-lists.png000066400000000000000000001050651501654372000216540ustar00rootroot00000000000000PNG  IHDRMOsBIT|dtEXtSoftwaregnome-screenshot>*tEXtCreation TimeMi 29 Sep 2021 12:59:11 CEST > IDATx{|}<8s!! "!:PDS"1*ItpH!r**";66xw{}mg%`\)$InX%J:#銭ؚd3IհV)Rɔ\ Pjjj|˿7⾗tTamdLjTVmx`@@DsRRRSSeV(P&INNNruu?~6[DpsV`I%iomٹ+_dK򵦽I67Kj 7RR5mM9Y,O"RD5mR(LL\,*d$Eu:٬flɇYd u+EUْ鹨]vѧt.ڢ#[z/AW9['Qx'(wk6#[`xhU[WJ+Ţ<ba8@&Z`˨O}0׆XAE2o4$us%un+EjvBC~3߃dK ߸4S[=ּb^+Wkn&C&jy$dH)&IM$ghX*y?5QrvQJ[?OKメ`K+Ys{Ȑ}d{2n'Xf#K:sBnPSfoydyF=[PI׋:q,gI9չV%QD}z^mګapq_ڳm{]H2ԵqMU*'/D]8^~j~J>\(JXEDŽ$jM$&һ'2sEs75l`/"7-M}\bҕj|foysIT=W&Kb#2$̥UECU+b().Z|Pזx]KQQ𪒏иMe8W.U|b.{+5Nѩ8`ŝZe2ɤO&I47~I6=zkE^N2.$3ޭ>2gWNe6%eաqJ_hd$i䮺cO;^[Wߓ?.ܡkXvTdݦk+ImINj2$$߼~=vMrV8#K 񖓤{0bS`K+m׋I_"b6_-lm\ɶJE3;]0%)mI\ ֲmǎ>,44"_͊J1p_6ey{]hJ1댝JEJ$nROTjjQ~sI..yPܣMե}YdE VSh9%ɐ:$d{7܃-mn;)~Gob8e@ rtV[! ˩ J\%nF zP˪%Z=0Zn")ckD n;Iki2â }3%o\Mdz.07d5qpԊ/hu.EE$럓'o 瓗/w}b *v(Χ~uw"swWT}b֖ozgݺzd 5|={:r7lVƸiyݷ9\$n yTY%[:2IIwתXLC 1n6E}+Y~ΙKDm>qSKDfDHܒLJXt5j֙dn( 4CdN';nZv-$`#lH6"؈$`#lm}#nV'~ܷϑqXbV\$`#lH6*$qV߼E) ̢7V똥c)ܲ vE[^lJM^IJm>+#qCV<;)F$5|CGm:NlU4I'>$,{NG*VLŮ*b~KuOʪ\FN?kݰ;XOŋ UJ]'hv]MVT Vi/{Хucڛpo i*u\.qjY%Lh 8;gftZT$H1d 1adK)?~Lw/Ȏ шEhD9/fw1=b΋:_ۋEJ&j߯wRw\#n<^?f*I5PUSAwEoDwQ^C5MZ1mKog+L??}~(AR6wqK*雧\fgY5hk*_Z~׵;onMX_!_jzTon9L!$~'^]X@Mn?o4KCR*gubY׬ n.?GUP (YJAU6?K=TRTS [/VnuZ?AnPUJ˿tEi^[1f3ޓPjJRҾRSeS.nn((rf(_룡N!2y/'|&IRh7x\Hըm yܢ-Ӗhm j6Uoi_];֐>҅4ѻTS9 ~5Gw+ɱZw)~n4ھ}g$$x˧5Ï5 5$ɬuEZ -L Ӑ*);zzاJu}~'$˙r`}E.^.׵#t1}HQbbJLJaߍ}tL_S}p >ݧ;h.\q\zL>~mrg[u]U$~.mPt>*uT9R e4xt ,N>o:ЊFs^TU,? hAoTh> ӛ_OSRN8 8VN^{zv+`:OKKU`)~9KRKtT _ώuVxܫUӺ?Ѡ]?*N!>ohZMȤjpmL|tꥥs$ݩilU7>)wM]Ҏ1jPf̍O|UZ|rvDZVm?&TN[?T|!ȊlnwvO6JTvQ7aKC?OPlT JlIIj NCS*2$g:*S6KZC)9N*))S2je Jşk zRoe4T&lѯ f0]~]N)VE噒$DCOC^z}c5ܕ?GvoIQMb do޹޸_%3b w9'+d.k4f~j!xgKWS=Ϝ[ff0TZtSyG%]UTI1 ɥ^\=MeJlT$wuG>Vh [J+>e<ɐa1$SVipqfLO __CMՊ+y͵-i,I&IiMHe/j)"k[Sa?fg(|~ \#\QL $=U$9eoGcTWjo ,2rϺ#dIad́I6Lb!IUUrDUU)gWFv9YˣaY5keӰųLmxzDܯO>^+#V#H3j*qSa8( s4TJ1,s.(!y^ߊn6maZzR~ܧ_SkSrfI:"ΰSzϬt<}9QE6eɚeo=V*wU)~VXپ_򏨚 6 O}Uc:WCz^ԯz*#AgT{U<.AdmxE߯o/G^R?V^$&4R4 =5d~K'֒$5#齩/]P%eIU UwjLgQ¬Uk?_SYU /<զt2Z)BVP{WXuIqmVI:s^OA}>փer}XU{Ց[KIr|lLTH yxV1wgU:Wz=]UN zEedo~$rtܯ*I.n.Y%O2[V&٤>YloWꡳ5jzuQm"7e ]4t0=*UD ])ҕUI7I=Ԡ\EO}R&hZp>Q.+nI@-$IU{?ӟOMVGi&EԌglChB=Y[T^}W/zE_.r* T) /z6rz.5Tr OoY~RI4˧\] IRՁ5zs,V|Je&)~}9{^^*]7^Hf&{KsLgS:I=\񙮟j\kx{9+tkdzK`풞.\H۝fvlȪzt<܆+L!p!؈r5چNrd-$[pp#nV',#nܓ I6FV4 Ñq V6"rcǯ,__0Ռ3$㓝hݻW6luU!!!j֬ڵk D#&&F۵n:8p@QQQ%J($$D۷WӦMUJMv^sȚYbȫ( 6L~${6m~?~\o>cIRϞ=3(000}:uJg֢Etg7rssS~xdeW\ٖ׃yzzʕ+4d 2D>>>y=|b(lliI6{k.-ZH+VХKm[HuI}UӦMj8,~G͝;W7nӧeXlk66m'Tݺue6;ffv^sȚYbȫ;vwɧ-^XM4q2b͝;W)))$gggkɹsر>}jݸ)RDa#= OOfԮP>Glll5x`-Y 6I|> 2DK.[ IIIZdŋ6&]MȝmذA sҥ 6IJIII_Ֆ[-$W 5m4U\Y=zpXH-[(22Roa2Q%)}~%$$hԩjԨڵkX{ٳgfsqqQ^v<Yd˸ $Mfҟ8/?4$=:OpNm'NL?^n'XpB=zTԴiSMy W3I9 2 wN_1r'-av=oe/IrvvUM.c}2)Ra[ƍKA֮][NҞ={L+Ǝ:䞭e]$SJN1Ԯ][#FPj~쌖-[s*^x)88E-[{/ױڵ+iTjU R!OI,[#c&c3[KʙTr/U^]wbў={ԢE Ș\Q=Ó$i…Z|u)ַo,W問OzʚbQ"2im&WӬY;d2i0`|}}5f ^{:q ,Kc77 `GV'ٲZ{n5mT)))綿ܰW?bk<5kMKe4hP=CCC{nIҪUԴiS999YoJJ[has%Jɓ'3m۵k^x;VUTQ``~s9p^ylyAIy͞S[JFi 4(O6kcP5jUViԩ7|S~~~zl>vvعsgرCϟ%Kf,Mj2TD 3&>>^sI>,,}eTc{Q.27+-qc҃kݺ-*Iow^eIRզMcRJ׮]qݰB(cm- 4Xsd\(m٬Y%bŊ_-ʴ4...O jРA7ݶmٓ{Ѷm2mpBEFF5;vh͒$OOOuծOLKܮVY[/meԋ/ȤzK.ua?^&MRZr_ի#GHnmݺ5Si=IJLLʕ+u)-X@%"5j׮\'Ștr/fovK.X JHH/rC~ͮ1_͛7WVԻwo%%%I.\jΜ9jР$]ߞ={4pL C777-\Pw}]cL;… uqfo^5q#O+v-gg.^Xb۷*V(0uV5Jw6={hԨQҷ۷OƍmiӦ ݻwkԨQ/5rHlTL5iĦXn&mEONى'tIy{{#}=bbbaÆiܸq7n,WEGG;$,V^(Q!8^ɖV"0LSag5̘1CfͲߠA.ooojРF+55U[nSO=֭[{UFTH]|Yw֗_~M6ÙX,ڼyF'*00Ъ؂ŋgڵkk."fYaaa 8z{{+<<>>0`ۧ_56:}TV- 0s)c۝jԨ#GCYN:~_kĈ9޽{WƍO?N:v_VJ 2DŊkM6M}Î<ܳg\\\nx%VХt3fgϞa_%;tC?ah˖-0aǮSyyxx<<<<4tP'N/5h@Çf͚%M:UǏ5uTj֬YmNӧo(Yr%ggwqq)yk3gΤ?nذ$)!!A&MҤI4eʔ<}׽{wl`yZɖV"ȻOPUVM>6nܨ;v?Е+W5iDm۶UZ%S_E_6ڵOM>]: 6L]t~]+jԨQK/cǎiݺuz'K֯_w}W?*Tn. 5g%''+44Tz|Vz{{k޼y:{>#ڵK...4hZha2*QN<)IԽ{w-]T&M$iȑ? @'N$-[L}{dڌCmy=XƄɎVY{_bpkY i+Feϰ29}BBBl.i3g_ֲenHf]p|r[ڿ2ͪY.]զV111v'd{#1kرjӦ~7<5jh7ۖz}7*QBCCӷ3F3fH;&IC ɴr튊R۶mzz&LPdd3aruuȑ#TJsz7dd(J|||Ikq%[^X(7www%$$8^OK).]a 6I*UƏ_^ժU\]]ս{whڸqN>}C/lV@@ZnN:J:UCHa:x`2_===թSwAK.ٳg%Iرc6^ۻwoOڴiS2j׮z葧~a۷O6lPRRR^lO>ZxqKfC;wj:tΟ?/j9*U]vjҤmM%2SRRǞdV$J*r4˗ה)Sd-^XΝSɒ%կ_?CAAAy\rEԩS6kH0{HJW :VC I6Fܓ I;w:2auEeXdKJJrd-\lH6"؈$`#lH6"ȹ֥KUСG*U*PB$qaft(Y?9ʕ+t(@fSrTlYN‡qҥK$~ڴim;o8fuJIIQJd2d2a2 C'NЁcZ8cy (?{͵۾2'X!8lW\IcXlV``8 cZ8c9rCmޖs-? :p:Dž\X,2JLLd 1-|xMw Ux\o+m<) ٖƔ.ʽc 1-|xMϽ 0}1 ؔd@ I6ZۓPБ=Mߑ\Бb'Bf)%W5Z/JܝN2.Ւio#9`_;ɖ|Fn^-VmO=@=k#=xm.f _5HwVWo"^>@[.ҍڸk폺F?Х_ix;U\ݼ_N]ϡ$}u{,v󫠪BThJY3SC^fE1TqڑXб8Clƅ ,J5}uSO|<Z UC[uh@sRʪլxsEGvhT($צYwj@-_UZuӠw! Wɛ5VZDyA RYr8oncfU2Tԍ_e팘qW=b-_~~sAGI>_պo[8YgE>˨t*]&Hj4RJ[jy[MP;?S⩚ jYHԜ'&IGD= UB˅^4h]ʗqgy*Ȼp;@8}t/oOxzFW+.O{.1W\̥:jҊ1nBw}TeyDOOj:d([`{hjg?WQEu_+\o I_ԲG0¹2\O=?+ytl*)7kY_u@cuȈ֮9c4ϴ8W+N@qu|+*њ>jzJNzؙJrUЇbdUXW5V,˙MxfEgPEKJROh F~ֶz浥E9O/эq+:[:F=uL{WGS&-ZڨصS뤒Mz|Z[ͰO{.S?^ۼ5c %P(@HA:+|9bM#Uuiux 8S~w;TDM:mרC'O] URgTTY-z(25vOr/~ʩgqWC J jTVPٲ*W{fdlhߢԽyMU TPj|w]sjZZ+z)avo#?UO5{GkEՌiC5X[խU3s\@Zt֦jS` klJ:0n-UJJꞷ+UiիeUzSuL3v}6jWRP`ѼK[Tcg<=%oZ}4!\=G'ikꘝ\3^} UWWULd\ T>j=+)mHRAw-#H3VZiʵrWfKYb>=}ҚUΚE35Tՠ@RW-@td:''/[*ʵy.%k~ P]M?|m^[AeXÖ }: tG>zcg.MQwիeXcXQwW9iX_jT9V+V|D?嵤DNm R.HwדolЉAÈݧE#{(^PS~Y+Չ2 /sP*jˢK%6I.j?|ܻMǫWׇ聺e2L*QFT~O'Pzߔb2jL}ZZ0Z~'zU;GW˴foZWÐai͋4~_F-ݢ[ks]Ut\?~A L R5۷k݇޲6Wڗl짐 ՠQSS|_Չ7$k~$YB\%Z2銾{}VWV'xs4n=QfɊg`؎m:fj3G.wݝW5W4ΫM˿1ԢVUGiO-GoviU60XoϥN}~J *[V7W h'u_h U,{i7NѾך)DK=A-Z׹b {Y-=R^HrJ6 h~5toX;*n\4Q=Wbf2 o"\Ut*ו3N-?$yz⎬ cS}7?fՃON ]ܡNet(s֐wN5F]9,ן\Ҧ?I4%[aDӈn#zq*𳖼=c| nх߷i7T=AǷk׈2[C]4b9z9$$jwհ{lqg_/?egrBKvӈ}!z|LWTtNYN;{drk3]#Z[tn:m'X#fƾ)))YS&^gLMMSinHwO+(-zX5\up)?*2em]}RSզǣ߿:-q5*;do"Nk\qmŮ[ǷK._I4vaE%>{pÏXΜLC k.RמK$+fZ?ȓT[I8l=T$]LP9W+g9jsuq+ҢN3z]5={D͛“n$'_6)J JVZGxdגi/>fZю&酇ʗK5 Yr R'ǪAJ=-3^U}j_4gVy}=T/jO>9z^ѶY/iWTku>(+>G,'4`=6j{)5~~9kQʅe$[5|]ɻRԳ#|DQ"ը.5IKiн׵ j-vl$jRPk_A۪yf͞W~iM)I:H$Y{܃(,TuK;:dzB\Sz?IJՑ"VD,eB͂վ<\޹Kd*ZEaw] ~$ʄ~Xާ67,CW-KoM{2ۿͅw ݮھ$I;yݤC3d>iWy΋o2xÍ^)ODGʲڐ͒ :O}FesGZ7kkw䎳Ʉ=a Va JeAḳ]V7,, ndpktF,Ssfd9qk[Iyy}Rd靗2s!} <>r׫"=ܻȌ|bxTf[4\s9`Sr޿ 5><>U6ܬʜzqu3%ӴjVjיY hYtOR?Vj88ux]{[ IDATwu[ޔCw ;|gH:VܚԦ*Usfgڛ瑛%]87XԼ׌||l~yK"oSnx*^{*c>=C/˓7eحg'e7IRʷ<1vp?c jl==̀ súR]VKi;y:zi\B6tsʳƎe對N24=_BZ_HmmjjjRifegRchUg3ԸSotn9モssf@nsh3;gfvmM?.!7_™ O?d[J;π o9$IRǞCzx~~|_:xpVݔeCӵzBO\ȑGM~zBޜlVSt8筼VM_i!}hκьyImMmjSB LzP.yo<`1++STەf3}:Ȁ[3<$ifSx h9gxK\*?O?uQ׎JXgwK2!IR:2Of4o뜧r&n_ewSowYܖ:O;Ze^cy,̲v{ۃdNȷ7(_L濢Rq' 9t`(䆌[6i rk{{ uGiP6;Υ?cLGAcθrurʭ)ڵb.IXʊfXg9$IsWdڷNɯvQ4E@>uM*OF~OF7?8*'nҠ~Ϗ<+o:,}ʧϿR~trƌyeDg?M~Oe*˷8ݐ5~0_Pq4NM8\j7'\H:}|Ϻϭ eȿQG'#jO[I__o[l +c_q'ۧ}Yirs5~:6. oѣnϯX#Uu39UqgEnѹexp/n?Vg1[G_l<AR[Sy/7HEiWKhHuuuR{NXm> V(:K/erkY~1YkvONKF(fڴLY=]Nc+\Rsw7e yE6qXZySλ^ 3i}Ly c䛴9ϏCo5J~]𚠍ҸQ?}\OdBUve=fRp[WϬX3>cϩiv䃿]Kū/WyϣvJ]wz5/ՊiSA^|  U~8Lj=;Ҽ˦?fMg gfas$6##.;wbzݥc2sDNy:OR7<Y+kv]I| [33ɧlݳVʼ:vl^x<5ys̃ygXǼ@sy2>t7t|U 3"]=2}oyE^5CvIErAy{dc_{[YO2w?<.?RLfKdQџeŌYuiΥ,+vVUO~,W ,/=|wج6l0WE2z*<<9m˖)-+VM^+[˹d&ٱKt-ݕ?m'mʿu׻!wl޵ey5K6i |zܧQ{cٻn/\-*f͇_K^lrPk@wԅ9l~SdAj9G-F6ɬ/eg~67sQZ~OW?., 4OV w|ovZ%jɸ'NMihY|(Kv/^ݲJ5sSҵEY<95iVwC-[yԴuZ7#_q^ގY/~ZuTZ=}~'K-uzfC/vv~pT ߥG^N{(v2;w*K WO~9Sr/m+]:m?!qq6nm>1Go22w[nk5"WSfaWgc}=:";^nf7MWN;~rª5?ʖPi~[|OͻyrG䵩UiYw r}RR:27^%~ximTFwN??)3Vʽ7Woƒ-K[緸ʪ2q-yVy?߹+53bgOUv_?01ۼF}sy?߷:)2wn޻nDN)f:7_5ZoAKύs4]g:=|tnJ\=eAiX>3ݻ5ϔїg՚%',ڦ^7#ϊ!+~grW^I_CXbK[Y Ӻ\cRӴ/ ?w*9k}rؠ=2Cʁ9#1=HE y`dKeq̅ OK:]3 $eR皳FoNOKy'푎_x.#x~.ڜ|ٙ6<+F-|XRs Wǟ~pE&WtHaG(,Ahaǜ{|.n9Jssog睜]39sʚyM4H.קi%g+ޫJ=d59mΓwZoG5k_t~~ˬ&ia bh/+됫sMsʥGegyy7昝:b>;yYqb>hFYZ)Yo#KMRm9fUt_~{O2d:8<84ˎW<ƿU's|> +T.sVgr% /ficHl 䢖8_5*͑]cvMޯLmӹ&i[g-Vßng3G}3^W7Ϟ9. 6=7<6]2'~a۩mfsY.9bXZ-~_yCvj~ws;Ҡjm^i:&}3ze?^3*rd|x_`\9d4o}=sdղ%ק'cSsҟ\fN9gLO4kv]gp;*+.Oqgc/i}ZA,etV[oO6{ƬZsu~m;?~|'2UާsF{%,۹{͎;XCڻ馛&,ڜGϐ6ܗ]7|`ǎ=}-c/0`Ru֪UAI.u㷗΋w>5:Jf̕Ґ/@]ױc92;-w1z6y#_סk:m9o=θ.9<\ߩ`il3&I2at^{?n+}f¬foۥo0_;og]W_[SgŪ, }EU[[5A$ߐ/%s̒h}a*ӊMWWmULwWry5SY?듿I*怫 x>݇2W(UWWWȗd=t3Yz[ڲg|lUUUKk,úg.|&K}˲X[sn믿~R ,FYYY~ޘ1co0^.] daA^|Kڲ8Z        Z=S_K6lX}^[dKn^ 8BsH(HdD6(h9lrﬔOmi\s\5yWI6^a n&WJ#ʗEkʪ`\3-Ӫ?3$Vl󅸊V%{+`*uA^Ckll-W]5vgsS35IjgIsRֶi~<;ev+dnRMҳm)@Y.#[Ϋۧ[6ϽAf7lޭZ+k(σLMoIyeӬٰ:0&PV޶cٮCL|.}?>ojw4XM6k45<8)l:[tIVlkg5|z["[oTe^3L}?O|PJMS &_~'MRM{HӔe͎R1<1^6.|85d{&~g4M>yrtm2tIfj*}]4i+WdkSlԱ.%Im쌹ob~4vfl#mʏLˬ-ŀ3x%m˓2zvпmmU^ U|m-m<9YI \Ҽg>xern[Lʓ󞨚[WJ9S@[#[T1W*÷V+}vjMk։Sk3a¤|<< ZE}չcùȿ(tSnS2[d#r[}dFn)ß^|SGҩ,IU|5sUUyzZ^{gNm*3gdN3 Z"ۜ{j MjkRSSa_}'ο.?#7/]}t6k$sF ʶ F6vÆG $UyitRR[Sڔ\7G|x6d 6fN-ky^L3Ѽitw ˙.U;"w;YT_͓w^FljϨS&f֭/M5U]0OM:,jv൹R^;;;1^vJ~&sLNɫwcN6czyVJUTwol+{X$}ΐiL|Æ&sXF^oV+ӡjW)V^{>F+5U|('x~i`3_mUM_uglS3=SU% aqz={|^Z[T[|iyvҰQÅNHXZ;'||k$Jʔ[tc:X"[1mL}<|9Fr^^ҾGe'T_}:u'{m|1Wn<1NMfI/?w^'?s`*}f\uOpf{7(ui=ѹ-o&VN~sɑvKyg~?<'k7;rߢ"[y:v^1^lreu*e>g`$ g=+z~cefvyӴ\q۩c.rlq71Ȇ_vءV|=d-lu]6lX}|e]%.flPlPlPlPlPlPlPlPaEt IDATlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPUzy3eʔL4i,]viݺuzT$D6O? 2dτ ҫWqH̔)Sk/?'O^Y33&MJmm2[W}1 :U[[[QGs=ꓙlԹ%`G}t=ܔJTUU#XJ/3/L6ň#rg'>y<%c&ujI.x.01">|^Sl'pB8㌅>Ǧ:G}^Sl'tRN=.OuuuFQë"̱c=oܸ񧞟1cƲ"PlzW@ _le=1 :gJ/"P""u!'d&uLɓ'K$PgڴiJ,u&mڴY*_1ۚiݺu&Nqh[jjj2qĴnݺ޶L6L޽SOeyw:ڴi֭[wKeKBdNӧԹ\$$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"ޝ5{sH" $H,fj1fPEXB P1W EM)RJ]c$5!Hr#sd8Kr}g^{u6O;֢&zX1"ƍ˘1ceڵKmmmV]uF~Cl,4#FH}}}ߨ3䥗^JϞ=}"Xhƍ>}47cǎmgЌ3&J?^BU$5mt4iޝF!}ճR'L6Jg-j" }"{@Ad`6&PlPз!' մ6eСiѢev3?wgs[I'!CwO<1Wr,t:NHTʙg9?r)FTZ}\" UC.xqǥ:v,s12d|_/)P5i JTiJAM熼Ǣ$uQʻロ38cQNl,T =m#8b늼Ǣ$ե\.T*\.7{TUU\.Qߠ}Xd b6#Gc)9rdjkkL6^z_s=>Qޣ.իWl!D6޽{/]ht. lPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlPlP5Yo,ʷdE-bQ5,D6(HdD6(HdD6(HdD6(HdD6(HdD6(HdD6(IcmxĈ7n\ƌXoh׮]jkkꪫs=@4t%1"hcgy&/Rz9ZoZƍR4K}ɣ>:ZoZƌc uH@quF'`3k|=T-oF;- K槒6uV=u[.̭_lԷq\Jg`Oȥ.gܘ7]{_; F\y[]n0< ]t~uy\}~`̱}L~iz_sAq*y%<6zli# f-fVldiT* U߷md]f^ϡx('6fT~r4ޯdX\l,D匾z=lp~`\释L<*֭dU*>O==Wy ift_{sud4l~y#?xY[4i|:L߆^%]٘S18*Uid_Wbt5 Ԭ3UC k!'^λӪc{n+. JvNnb>gOz176YQ>!|ZIǞ[Ny}guVj֭;kCs˫Ieǖ> _ˎ;;|ar%?歧_0m#۶Nmnglg5VH-Ҳsip}cו{-ӢR|k0`is|ݒڴɀ[?oޟ5;ץet?'=8T2uԤEbX,ŬeX,ߎ%)eVϺ뭟o=tNn}졜\yѹuǏ='^wMeV͖_>*ϴ2,Rnzl iצ]V\{@ÿ2is9I#ccZg)1[o]<8uX^_ɏ.3;nnS]Jerƽ^0}:T%ܔזh{̸Z6Mzվ3)xL˫7\{MHߏyw 6ӎ ӎޟKJSy/$k>;~2&TVIJTg57nd䁗OM޳JsJ{ygL%u;o^-K$Y}FfK=zLũ.su3/-?0{g?j6]c6ŤrVң2ع_N˄T&| sjVKḋ 6;ǯw/:km{Dο㙼7i6 3X`/~v>wX8l?M{l}{ez+[s>[^k|B6خ&͛'(+~'n]E&n}"5_N+nkW|wFyhv9\sI9 \yqؔ BKn#쓭9({69)n5&+'0,}h~v_X},?.?:OX';fA=ɞO{eϛemVIs/Knm3SN/ m3x_zl#S+y?/r}NU*|͹ҧKT}4*O"յm2MvizMzEҫ*o*~ڻ3bTjط+ZpS7ɣ(Q~7W,K*tl{9u]g&ߣ2v}-SZ65佷uΚKo#Y5]rH91o%+5FM&6Ǚc~G9޸_V2OG牛/#_)-CWͶ?69)mBNk^vZyt+ݗ[/)]=K5*3>ˏ}|<g$K0]WI{iڪļaڤMu*՝z97 'g%Ͽʞu:g}XՏ==<鬁-Iɶ; ʽw-3" c뛦+fMVO$NV;9s2pؘLh2\KCM勚վ_]qR.AZ2W-NuNji混ϩސmf'efsn?Ǔe6Ke>ĬI#,*WM=oOyt[`_*% :"?=g7E:l,bd5w^J~Y{qNUec;ҺSxs)5}ؿ] ;w==g_f^>6kڧZ?HMR1#Lnχӯ}JMmq'9v)y]Cr'M#N+/{;'^pzL^"uL-䫚ѷosnz=Wt]ӷ=Fg&ijksrfI廧^C:%WK.Y&kryvڬs⬡n;o;sV>|xz x#Roe(f~sll_ Z ט O@Adp&՘SDJ% hu@5dZjkk3r#R1`R.3jԨu@5dZz_|1=\>x J]]]jkkӫWchY ֻw4Y jQ|ӈlPlPlPlPP"+>1^{hpdkժUc|c'$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"$@A"Ԥ+mv`!h܊IENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-settings-datasources.png000066400000000000000000001054231501654372000237450ustar00rootroot00000000000000PNG  IHDR_*? pHYs+ IDATx^wxUB"(RH`/ X?NQB*GrrٖM>^ !|"QKq_[qFqQmB88(%⼮lO 5I#U h 5?Xoq-Hg[S 6B-:Ba( 67ؼ`g_6K)(ζ@ 4( !,Ln@*~mDbkw;} !(]nq+}]Du lӘS#B#M 99X0>m",V8giH ePrhJ %Wq&R`m8[YPj](qt !*؂/ߘ.. uP,V 5cgSAq!Lvcv1;vy9Oۄ_,]B=^`3`39fcW'e-ivq3f1@B&f1 X7cv̜PlB)-)f~};fN>)Vq 83B۱˱; w;!(-@ٍLfN}9j?Yi`lbcf1ݘ3S\!="i>}73Bl~ya+CɅ1SS[gCj>Bp[ۘS//P?PC IIPl]Sѷ)ǩmi)8 !D8Sm4W S[9冤$K6W;Nmg7xBSA4sWmmݶN} 6/$%YwyvlT.nWѢl+Nfٷ!)r8k3mmk5v}' II`LTn8B_s`tf6bgN &'d%Qgyvyٷ[+vqbgg!()r6N"oZ;l+}' JI`LgW8|.3ۺHC}!D898qZX0}n3N &'h%QHg89 bkm?3܇[mi5(ζBQSm ZEtz..nuf̮o'R9At fvk0bZ1\ӘS !Dy䯠99$wZei}sf\+vN"(3PAE,.f)n-f빊cuv6vpEj?B@**v΢pS1MUcv@aL?3׊ٯ PUۭ@9}ZmSI6Ba'6Nz\CY;mکmV1At!?c]}}L[1'8ч>bjmw\%$|!(m13]W16,4S`Z1_S9BoBm͸YͶ9 0mU܌٭͘6f[1c@BQV0s0z̩bƝƝwf̶qxJD8R`cfܜ`'vy*.U\㊿mLNq!( Y©X_zZ_wꃚ,<.1h+5uaa_4jQ& o79pkJNTn;}11vq.l+9n'\!'kzݸ*:szLME+Z }EqSm+v0㪯3>HS,7^v(F!8Zv?=}F*jI )'f-S =rKn?v1ESmsfq5\s“O>Vzff}z6B!xfJ;w=g^>~wI!vzj\mH='#SrtfLub7f ^P#,&@1s\1CI.:$B!D|>>_' Z:c6F)zc18 7,$k}t!T\c)n[wcq ٽ;Bq˯Tkꛓpj>f BVDqzx0'Tsq1/>3Yx^rr7BEjǁZR66}̾)x(pzvPN f > ٺukٹs')4>X<BZ-jáupUQVњNvcf %" ..NTS_]L5I- +3[nÆ U6O?5jwB! zMLq5(R_f_L. 5߯Ҭzz\c8 &ny} غm+O<15k֐剩3όB|ޮhk`c뗘n[bYsm̓5Qm=nU>a-*'q Klegg3_5jD˖-‰U,?e ILL7B9ZFN:BZz5yyyh`ŊpqǙCiܰA' ,E EkugBPsUw0̾b̵1X@~,}Mj1/$BI _nnF:,s?7LA|S-3ǟEڵi߾zmPre݊q\ve4j@_믿кu+c$x>$r[aCǯUoZsb})Q}G"7e\=qPyA-s,}[B۹s'C=POny1 3_dѢx>vµ#GŭƑ ors0k, _ڭ׌a.aرiӚɓPn]/_Ί+C8|wyطw/:wiӦ1h /f$&6?9ݼw~&Mp;7[:06ԘfLk^w}ҞH][\s7. !*={2} *UDV'W_eʕ̜9(^}5Jya:'NZjڵ,n;>Ls~z?_~;N:?,70eD4hEٯ%:5I0kS\S1sRC۔VDx 7vP󑞞NNN풞mQq5 ~|?_!@J4h _МzjG?xڴiMbB"[l˯o>4lM}_>nڵjGc]Cn@8̏ D%z}1YuQvmoHݑpzSz3sZ ͸>׾}xy뭷ԆrXn?O/Dq\ߏb &NLFF&\2ix8Ċ+G?}]{Ǐ~X6k}z}rc:շ`ݹP! v"adK?ڮm ڜ<豀,⯿⯿2HIM5CB-Zpgap۸q1@vIINa]IJ$&rUWr饗syGx)ZHTQwmL8k-111'[ cGNT=s`N,섫(n֊ُ$6eDDM}3v>& zL}V[oe1bFA\\[o%..Lo~,];wQPP͛X78˯¶ ::ʬY(,'%Uaxްu֕ ysᣎ~Xڵ(((`ʕx^bbbhԸ17P֭4qbD&wIҢE~σݎ`~2^KT[!V6׊ͼ2H Myv'ͮ1uAAߑx<Ԯ]<5j,<UVVZ6D\MF޽TMMG ~)/Wfb̘T QF\uL6 kT1÷KrңGn`uԉ7qؿ?۷;S$?A2Gtt4jfM7{Q$&]0v(N^4nb¹l^DUD 44~'}}Fw %q s#}nEFz:P[-U;9% #!47x>ݺu57l0l~Z,V9jҡ>&h@.k$O:rðaݻ7}ٜ}һwo  V#/,oe9fG@_M2k2U6ޠS\wq"dAp.E@*UK.,[vh"ZlIӦMYVB]gٰ?jTwAGzjUV\ZLS*;ΉӸS؂*γ7OkUWjGz,av>ď E^^撐@NN+W&**>_dcB!D68 C#>iv'j{aFSVm5>yHtb7l1EԤŒ 1!V['7¿K~@nn.{rsiպ5E !G[kS֙})^*JeTiO$1 Uۼ@g1?%(ŗ9Ic Qq@ժUQQA8k5A֩+1~I1cf_ taEC~K3͐(%?ƛ!!2Ai6:3n>f &'h}G"I k6 SNikE)駟3,DWPP`Dai̼CS;#T!G!oy.ZSC!Ǭ'f1c)f7 6/j"$؝pt̘B!Bacb>tv2S DcL+N'_&m!"NuE齃][g>c(DY0/~1#~BBu̚b@ $' <󄫘7/Ɍ;]P!"jW"S`BVPP8t,5cB!D^{̺ $SPNS>̘ e}! LƇg cux!!3;9MH{vc rge sw;T"n3:{Ѹr0+`@`u^/ɤ>Lfݸ;X倕o`^\nxӒ_Ġ%>s.\$0SZp5[䢉wFmd߿K-/qϡ\X"M.rLJ]yH U.ya9|I=b/#\fRŘߙ{ k'\oƧ5 *{FG: k_ ^i/:e4]v>+*Oj3:ѲЇi5p|ۦM O$\T9Za!Bc_r4ҝH;1&'i.nُ(wL9qO;<@<*Oypk|n<8"f\/lL#ʳx-9W=7sW>xm¥Tm@ {/eƤx$ql2No?1Ra̻ mũ?y|$v>۟ʇ9=OyXG:Uhx=tK'6<=w%IQ+{4_<4u;{}Q$5˘IcUj IDAT-58q@w}v}gs`͚Q}`fvߝH61>4w^v2/aMF8礝ogχZvdw ]]˰S&_ͼR|6_rs\v> Û߮%m_%r1_ڞjP-,y<'u'L}ٔ#qWJj/b%ڵ)'UƵ-oMx]O]1ߊa1}9ʫ}~R9d\5~kfŬܾWJs y'Vɲ7_dȇcuh;!ߞUo "{g2sؚW{a-Hv[Y7~%\]W9xַY᫓G:R`>p#H͉Ǒw &/`/d+aQx~^wjveOcxpyo;y:!on7ث/\lg;"?k /bUBsz@&v|ɬ~4nI!ڵӞN"ٻo^̣/$ ?3կ~[ۥa!<2}w>p!\sTؾqy3:F3@L:k~ 'rd#b-`ؖ˚yXo#jmex郓iuID JZWw~M7ʟ 8jFmwt.CyVR|WH| cI?k>m+>_k3j8Ng&*_Om-٩rٝ<աݿxx_سl>\}CT̘o.kN{Y2=>}? ;DYꉿc)4POV|qR!9%=tl[x7ZJxΏnӧ3̸lN|/|n1ֵ⒫:P=\q 8s#}؞Yy{-\ڶqw1=KGA4UWr~ÇqrWƜsN[v~.7y|:2nވ#~6.~9W:7ۆqrm|)t|CӋ'_WZ;wϾбaE|dg߇iQ—Ѿ}]WIvwӸ>m{%u>avA,0G\j*qٙdZ IҾV=7χEeZ_1Q>棧nո0x(NΩW\89pڦܩH`]Hi.qVڗ)ZZcfY^<>\y|yi<^;$lZ2g$ķ};\UZxz\. :u]AO/ЅK;cra^ O*Rkpr{[q4> /!ww̭➻kj}|%Ȼů/չ;)߿_3/Y7Uiևw&|?µ?E|SS9'/>|jW)CrTII?mA͢󑛖ޤf1QUi2Z̦贙$?z3݋͉#݅u-vUH/L`qSqMcҷwD~i>Q`_S9EѼ-ރE'dca _# ^֌xs{|[,͚!Ql_`g3ckOmL_7P.%<Ӹʬ߲uκ;Fųp+# X,|,$oJ*^Kt]dwXjم։nRZB @Ylο"'kѵQ?V.M!W/L[(i󼻷/#kΊeތY2.>\$Ɖ[MVֳc_kzٞRcW{؟vԺ-w-ﷳ߂x4܎ZǴrٲvѰI#~2E98M­QH9qr?K۩.< hx">kC:P; ޏk7 aAbֿ4SGB9TR|[SXZs7H8ɿ!DP>WR)-zХqe\z" !Dy؍ӷ]{眑Svaﶥ}IZQpUkB߫&1h|7&2i\/R_'_Ns~L%S=c7{%K>ۛ}0c)Xd/|N7?_WsFs̲xrQnr!*Hq)Xϳ~Ŝw"/|l?u\:' =$֦atl[x7ZJD˲Ͼ"+8IMwd./X]Jt8Ȼࣀݿo1`G0-Dp~t hqyn2 oS<@3t'?Ű޷m%dxC?;&4ƙE.#zCd: qTZ5N⌻&1E !Xgsm.Z?J DkEoqOHȜJ~:vho˂BQ,bُ?ѡ};s*M搣@PPx|ոO{κU߫ȑٶEcJCȣ !BM&B!L$B6H!"l2B!Dd"!BDB!aB!& !BM&B!L$B6H!"l2B!Dd"!BDB!a2Bؿ?VMFf&gr$'%Ѽy3bcca!8"DBVMժUоzYvV'4$6Ic=x<491Bd"!Bd:]&[j|nC%BD('G!Bq$D} n::pJ}|1TşGvҐ|>_wݕ $%g$ʀ}.7OnJ0uy3tZ& 6Ln]w\La(>{?4M!DDvo.ՄjZh-̾b|-]kW_Ă1-|h(e}Xs|1+ⵘ\DίN㑙_&MJӸS/Wƍ 8/g94swS^g rTSf9,/+οq'2k1˷#F;}r C+ٜ {7Gi,OYo>/Lb@OMϖ+?jr季tluI6y7fޚkڧx?B!B8<ѷoL_ wmӖuڢoB>37y}",~ΣUVxZ:ĝ2ɜ_Eҩ~v'/-`湼Lh!_Eˏn=<3][#zسr1 ^ywq X2lۄXw+:mΙzI,s{?t PSoüOcB[xdU+}{_/~obi|1jɧ<}Z6&*{BQ<2( ͹ztFםO﷓g#c/|s6D7y|:2nވT+esp9W;H5_͖D 9FAttn~ ^+Iڒ66,v3ψ>uuAL.v+?Zz^~t g\-SZziՉѸީ8&}';H }E.di~ t/ebh?xmF+:`&DB!"JmWq6!Na١=?Z0A]Fu%ARf͙Ԥ–$;ec^,KF:cAeBYˍ"1)m7^w$|ޕk>E|g ~}𥙼z,74y[Y|IJqu]ZY OԸxp[wBDL$ʚ'fpՒ<쁤^7(;u/JAL~ ýcn:p_5ߝ@J;9޸n;6pVC&z*|>ٶ=@Zm`w]yJ[2Ær@{GrR-)m; ย`V/Iw6;t51=6C'Kv]|Z=RԣDNoe@Atqo"WOន~tq|QC'uF!D]SL75q۴]G[~WQEV㪟9 /t gFsyE Dij'nz`!ݻu5eIJ,ڷ3ڴnm9JIN E7z[_@3]j}(>e-ElR!oR{e5N=Be"9)5kcv%'%CBqĒ['u2ZfCaRd>5oތUfuA ψ !B&剻1gg.66VBAn !"l2B!Dd"!BDB!aB!& !BM&B!L$B6TB`-CBrrOݺuWӗ!Ha`ǎ]мf¿#J<°iH&BTU$rlFlڼD&Brrrd!Dy Yfd"!BDB!aB!& !BM&B!L$B6nj IDATHQ=|sD!JL$Dx > 9VBrpmŋ1,3{tZrҩsѸ,rh]Nm)4~}? NVmhs7Lu|;JD !Dd"!*l2Ve+oG\Ե-ُX2~Aίf΢Ǣyoyٷ,TkϿ& |ȏȢ{qpD!JL$D[#zسr1 ^ywq X2lۄXw+:mΙzI,s{?t PSoüOcBHbnˤm6^k|/оN ԥwlG6ߩsl>p!z77;V1O'긡 _dnNgz+yQ^&יw_NJʮX uB/%vW {h65޸?{4Gŀ*ץݹ2qH@jy,qw6v'V<0g>EHR\2'Ӈ) wJ|v߭ (b@)rkYNG1jO>W;s$ĮثӭZmJ㙻`g<ΕO{>~)K6zqی/~ ݅G =& !D ]SL7ۦ<ڭZ@֏*ZW̩_~cf0,s;-^ĆS/QeJF+ÖQ]]|b uզ#/ч}6VB!ݫ6ײKM@{JT٥RUխMPHݫx|F$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b 1 JlF& RYYK.a@+[t*+;0JP՚lVeeMZҥ4٪)D ѽ$~e˸3-JսʦQ"-Gk E|F$F!QH($@b 1 #т.\ٟ}R@𿐊i9-d…9FWdK 8⭍2ϵ&)" uRm %B!B-[F@ee%o1 HB$F!QHX+e>v R)ZNW>_#*MO 2;-)|o]aS/L˥T* ߖiGlXGpGhu錇fi;ulEKkmy61w^OQ]ϻBkPJ@IC[9IRfLXEʭg kk򴯔鹫Oѯ W=X>TR_~q5j>I7͜$վk9Q.?v_ z8)뼡v>x/}e|>U}[G ']G㞰T #5~h s~{f0j}kï?~JBhCsϿ<C7+2^g>o=RU-QYLצgQS?&m?'#For.}sgSGnz]$)EvλA=3)zz]w x~2^=.{JyRWOw*;Q};'OUo3^V羜D5 #SNp ۢL*'wv7Um~5r=7zqY$e>ԝg_էꮩ4tXݚxTkTKjc&iX@iZq4hvst5=y`):l4F4c!#Ӂ[vVZ>_\74hR|C&즹ӧ:IJk㝇jRC-_#-^ 8:sڻϫ+K닚F[:IGO6PECRݧc:x/{G9ڱBR[zkeR;G/=UJ'?YG?H}:HNj^xXd+:Z7~\Ҁ着VWO[8jc}q::u]Ǻys4+нU,iuQt⅋?Ovje*3ߪ)/~QZa'#shջ&Z\/[^_HjM{ml}۽2EK?3OԒhF2~WT[9_UN Jo?jH(j_ҍWJ5#UU h m&N֡SNUS-GnԳrNx2lHhѼꋉ<}1'UJP}u:4Tuzsu.˔1{iCw9Tpzۻ s%R]CW>WfǞ]P_ΩΖF9|nzTy>M}<(՗=S2)Jmbx]r#%ƏЖ*1?ٷ]'frUv ^Ѳ/{_h_t˔Q~}ok;j}*w95vz(~nI%z6U=R;h(J׈5?'>ԒJ}]^c2+)z.f .߯OEuRf޽6=^mJo1VlߙZV|'n~@_ W{d+tWϾP|v~qj($6R'm_oo%+.}Gk1:4 7k;+Ҹcu՟i[%U[']wzW[{o<E]Zտu^?ǣm;YosNF#u\@WZB9Umw$@b 1 HB$F!QH($@b 1 d2p"4bѽ6bs TW力 ZF>#}c-mW $`"m6렊 m6ZpM@#HvjuXEEjWahBE6u@>hu@nH,bYqC!f`YqF!mu9Yf9:gqqÿUgS%֦־{!kՙ ЦZB-DC.k]j!c`NڼmNn9e+d2sv7p>]3g=kݦmMHRv:!Y9N]c( FglĢЯcYhtQ34+WI,׫3ƃi5[X†<ࡐhs)u貱v=t}S}<~̝'qhG뼻5=Z9h1Z#;Z&yn #F]8N;2e5lķ53t?W?rƜs$0:rGaMc/>kgEHK^׷;R=7]Uk:kUz& S~ORfL{Coi+5jixfZNTZ1:n<=[rwz:z9-upF(}>{nB"Vٶ2^VF D}6O_iKhNC[Hg<6?\?_~#t[=Σ6=:SG^79f״ԡsv ϐtW6)&\qWjAP86SO[8jc}q::u]/n_[=S2DE1Vh!j /߯}Y]SJԨnдgkլMuzφꝊ@rD+2V}~w~14pGcD)*K2V׫_/n 4Pzn5w"- DR'm_oo%+.}Gk1:4ZI5Ku\}{eBJ]Lu3gOШueoi_\47A?]]A[fkϦksW)[שHlIȣ4[JFFa;>.|Z^8->1<#ĸ#ȇ;H"b9w$XyZj**Z;)&jjjTށ@nܑ@bUݫVMMsTMM۪^eSw$mٟ֋3giժU6XǎճGߤM@#HJTJ{tW]]:L]vUnl @bTJUUU7HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b |` IDAT 1 HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b 1 HB$F!QH($@b-$JpMn9-.]fVt2UVvaH 45_]JKigU]]mS( ң{wID˖qgZ[*ReegUWW{UMD($ZPHB$F!QH($ķk|($Թ3X 뢥KsN6<"mԯfT+`s~(RU>Z|6TJ;wF6RnlyPH$PUU`}g$@b 1 Hl},$"\oO.عgBb+2'(f)fZ==n_8QR8ݾg$ Q-tI"~`nTQ|Sv=ܑHz 9-~0c^7Kti;ŧET6q$4(2FREoz$I[o=x;R\W#  kN!lD):qP X﵇իF!ǰ1s1@?=ǚzG>9x͙3nV5y1/|B| }}w iY#I|#έ֦B"ׁ˹ͻih553{2ֽ#vׄ1c.#5JHHSSg=°|>:w;((E<ꌉiݻѣFM;3&}n}MC~F d)qsrڕ($=8Ƈ*cBKc+/kjt9g\geMRۓt})7'ןܪ-gXnu-}Cb?W:orK/vw> K/_*v|v!Q/dlԬ%g79\劾xDw?T\m:jiCGgS}"EZ?רKK/Oc77/OuMfܥ;?nU2sݣ1??M?zUo)+̙SuKtewy|#n`RXhR\H1c5 Bzrn[j.>4Uv_teaXDS]WtUub-YSHi_._]j )1D T⊉T:p'пv=XnV&h?=^qU$mA~W욖] 'hH]:m>QGxK6GE3RVh#ꘝ{zcSr|9z6 c}{6ZgZEZ0!cCcL}g^Z=5j^R6}h;ؓo+&+Rv=1qcv>(Jqk>g>'oTͶЪ+uѤԣgO~%J |:UuUj|-D\Z-Q7u^KJ]?\O\>.pETg^Ѱu,lJEGnW%Ѻ~ EW)3lV-{_);ީWO>VT_,eT9[tĨ;)ӀK"Ek5eJg2( rcʵ.$܋+|A9{ 1߶}MD-ӱ(J_ZkԫO_M<"ˊzNvז Ӭ;ooO~Z7TV翮w-:rNvT qtÕ: 4P\5Yy pM:C-׃zxCXC:'詹ctXOIr͟B:_PW%Uڤ{ͥ:ݣzuB;7JIZӚ\EkRvwYiCw=\r1saK 9vU yDx@ZqLO1ݧ&Kw"O(S^_-Y]%.F7ftUIw=EgB]t^:dQDh9 *K~oe=t_ē~N:됃'4پaYJ}a߀4aOTEVjֽw j-y^5BN+*$IR㦷lndyNK?{K|ZQI%QwHQ}8()^Аܥ@"Eu+ջoeߟ~xY38Qv C8vl>Ōm1-yG"Ra%a8w@\;|2vQ[;hǟݨ^x5{Vw#ϯoԾꢔ}-$Uh#.я:V>]_ x=R)Me:׏~x AJoƛn֮ɿ,[F )h;kVN8PW}(bnFJu:ʓKZ[-tԕRus&ʍ#/Z+WyW3T~#tЄ"IJo.nts<:mI]|#F\sQocN\*t\5Bb~ܵuJ wB\ߏ~RmLR/fIKp7 @#M-F 7иcm(4C][m>@z9n3h=pݫNDRFd$Tgnڙ+(@ ~\k Bc _鸸Xc߶6B"n_z-Ԡ`%/d$R)v!y?)|xnw1ٸKB W($#Rp1{Iv$5B2{h'>v9~pʹU!ik˅HI~;1n_$.NYrBǕT[N?ZȪ|8]oSpC9K{B0:f:U geM:;tЪUTQьoQBIԨC %޾y? ڕ{pIM=AIsqvLvqmώgZOKeŪZgڶ֭͛zmZQMMzmuզ zíx(o>U#Rӷ,s9@۱l,*pq'go֋/cB}8]:k^4d6D? /RMy5UTtTUMcp["qmvBcm~hB"HMߞڏ1nsnmۑ($UUsvTuuS1ԥKuͦR*RUUǹ s [H(vB,k+.jZp/ND ߱.:)6RmOA$푛ܪl %q^sI½ank dg$P}*_|s?|.7GCVDB?mzd_Bs?︘?︘}׶Bv% |/ۃm׷K\] F9.q9ɵ|ZЁ}?.8n@n>K?o_g8ʷesDj×!>his)qGKjhY߷v%HF;.Wߵsyp(X/ m ~s]ڏ?iN!aX.1TA'_B9[9IvIը/$?l]ێqBPG")$J%_;{"lbTyEjz;4GrboSHXʕ;=.Q~meP,P綐|a'8ƅ~M~l_Sݝ2OecnlҪ/>\Iڡ}}uuڸڹ֎ߏc@M^Incu7u:v:/pJw"jX ďXFܶ2~ߏ;~ |~GŠlA~> T?gm%_)i [`-B1-sۺe6Є7Ik_bo8(\0um߇}|W,ɮ]ٶxhB"Rxk;ۖO+&\͹k+ˮ UqkxR},i=yTvqqW6Z=r=:jc-xv]sov* "oP<Ԏۮŵd!)<ŝP47Os}?nc΅Uʲ91qPc}m 47!Ckcm'gwܤ/.n [,9qlmmqfkBrg~{\۝@w@j]ǜtvEj("Rqn/5?!&q}]B.m (;i;~%Zˎ۶[G?in1y&\Ҫ*oqd}Ǿßl[f?ov[wݵ۸l\ߟd?dn-NBZ#/D.=x]L^ܲ\nH Ubw2~ʴ~Ը8w"ƅۧ{Xn~zLחlƭP<_PLjz-w};|şxƋX"/f 6 ٮYJUHJ{.n8ʮ]ۿɮ+6\&wa?~l޿`ۙ-mmP ZRh ]-wmccI?O/qc" .l?ڎYXiB"RJ*'@cwk}&"¿ʎsL6~˿ "oPQ\_۶ǵP ZmvBk۶k5];R]PIP6ƻع;gm HOhM.Xw\G٘˧Y W\lec1ml-jqsŴ=BBH ~ޟ[ +"۱vnqm̉"6V!)<ٸ\.t)5LNFM vw MQ/((ws)~۱y[Pj]]g}~ݵυ8cٶԸpy=qlxIV!aEj:kۘ}{2j|w mK ?&Ǻv:ww/Zf:Oa_.]?:$ %9vkm;vqwl/ 1nkKw(}mv/kqmQHD*|RscokOpW͡lm&z\me'}vvK.[]y+W.Nm $t '6I^ʼnC?έm%6m Lg6K1cK5 H'.~L^g('{wc"/&I/(j|Q6؂¶wXNh~B@>N`v߷9ܮ]_b&vW8رsm:xnl(7& [L HMND"A^bi5Mn;;cʳ|q}h/_ܶm߮P4ɹ[ 8ϧڊdjHsql;im!ԓN/f[ۜۼyo׶ C1'W.Nm $ĔkP.];6y͇Ƹ]k'6[I!+vLmavls}}vT*dehm۱];kIϵvBvR)H{eOvc,cǏ)c#ǥ9c@[u]ť?&qP̮]Ɠ)tRmۉ۱_l -9N衶۵K5&z~퇔jLZb)d Mv4|hL/kJ/F)Ri&}4'j9v"}mrv)d\!c IL:.4)1Rqv|v_$@{drMv{m<۵m;8W w!c&d;N Ocsv˷6R8Iq)w&&\(I:qv B1'4ζC8+dLZr"*tIMvkmo\vR16(dK2&np>/vҒQ.ttl:_Q.,w m<_ M䡘/.gm:qEi :64.nRĵ}qc!q/Ks$3q6.w&︶/nL\7&4> )tTآR+5osqqmϧRm5%ԊƎ/Mqm+׸||_آ֤S:αrrPl߉&G})s mgs)t|kɫib>+$ZK-v|(f1}6Wl?_֜t}R|;~>/t+  C|})˥'ړSWx)پYvKOV/n~HhپS6ŎOZA`mBBcUImuQkj|(M㛥&bNvC8 B'\rPL*>O;⎄dLm$VdYX'nR d$4[[R 66\RJ=a_\..."I)0 &}IG<ulϕ+T}$ݮ$dלќm}//6)f, b&B/_9ۖD{̚|U '79Zq ֚yB:Ws-:4y5g\oj?*դY~_9۶XOTikRZR{|N6hlSӢֶ oKnkɾ[rkϻX;/ualZhZkRoiV*tEXtCreation TimeDo 16 Mai 2024 08:38:45 CEST,5 IDATxwtUnBR!eSH =@%CJ.T:(w RD T/;.@BHGDX9gNvwfܝ$swF4<3qvυB<j.\~qv?50r 7r{,.2?kdZ!'O(0:/m(-@y+lׅB'S3./ays %!0FlVG;Sd8/!E63X!ēEu=oBz\e3`BǵF 0J߶B)r m'Z$>Bem%6] H=s8e-##J9RN!5UV>rVUT񵶶jEB瑪FSRRR={Ώ3={.x  l0L&OΝ;6oΝL>=P2y"=yD{ aR~g f@5┸1tGAFL$yφvcJҠSFJ*,[ @ݺu0ZlڵCɾmun͚-YoO [!`¬ \T;h]lm^-sI`a〝u"׏I(f=-M70ipAsNƦb8Ne"Q(b#"r;ON:=xퟖ%Rn+ aִMd/]车~D:;vU Oë%Do@ٷ7hB5޼2lz>5y9Rh2to旱}X|6kX([Y&FŅz+(Ȁ/>Xt7SU"ei7z(Fcx6)m!eWM›]l޼{Op3oVuǠzNs>UEUdE6ȴ&&Q&RA'08c׸P/AEq(O (CUJkv( hڢ)ռm 9hrb|(.9|[ExXP<mк6½w8f透jJ?jYMq؅ଁ*CnO)%"8n,%~\O[s~Z@?WBi6d}S( yʗ-+g-DfW -NEg%%Ϋa#͹`@Z-YPL`c@R$zقZ-JnA^>xiN7jEtlCƍ)8z.<}}nʯ|'C䋅{d2o߾3xw^"##m YZZ>nٵ6?Hg&FEA IEU48W O -"aʯ7ٷ]VcI&>CnE lͮCqуK-Hm?=U1sw4;g$7@2>s}aHrvMQMwPo:Vn;FC߾} ZjQFli<_O !ӌ+eWfdGq.bBXLVa֊=2}J{أMG|Jb2QQ4X{m2wQi'b|o,ۿ@r)S:ՂQPthw/[n_hj7j?GrPnreq C Lz]"T!**lU* ѷ09aҮn >=kL' CBB¿%E>F...=@Zɐu颩K'ASEpt;$*KJBSh7't:](.s[AQ$,]v,*Wb #QQK)--RN_B4;uzӮ*Z($%%=@Zkkk,,u\A ! XXX`4IMMh4>FAbiit[HLBzM3$BlF-,H !0 t !(h9fBB\3%+5<رc*B!DH+V,z!sHB!̂B $,H !0 HB!̂B $,H !0 HB!̂B $,H !0 HB!̂BRWnf;+[uRՄB䨐):q{ g 9J;ɢn Gfhq#{B }E(J6!";O.:ut =isoenNcW]o9 L S iW%o ATo=u2kiupqrɕ3`ѕWa;X܇p{\-#W!)=x!U悫?TGB_TZHj/kօnp4>GbxZ Cd {ТbL/dʀ7U v hU4tz8Dgt;@o|vZ]}{/]*nǫo9p +mi6㺿eVtGzێbe˚8CI<ޝ5p'n=>4n PIԯ1s~8S·f'㟵Na4~%IEJ\YǺ)8a^P,^Loh 2w!ֱVñVӗr ԯm ԦqtC /BBR8~$jHj(^ M }2ƐvGRYϬftT.O^~$տ/CsZ0PQ39v7Š r;OzB<ߞ9􃽒iܡ;ߪL߲~h%]9T!yL:ZL/&"B l}~ iwۍ"-NDhKvr)ˁ_9K պ~ Rs Ƒ[F[PX:Fٽ((dBQ $z,{o]ګbIWwaKL>9ԍi/9hyvk7ѿm \uA`BM\:ts*ߑaeh 4Qh4W4ᴩhl^0LNwaMVK9oRNN !Uݴyř^'Lx?,dic :=/ a X"?pcԔe 0|. Z.[^6wn-iAX94HXRJ}i31Rxmޏ k<^B!̚L'L3iUR!ijɩ'pi&CIB!̂B $,H !0 HB!B NLL$::VV+7 Bghh4쌍M_h(eB<TUEUUǧQh鰱¢6! S¹'\Cn:!x>P5H7B<_ .B $!fAI!Y@Ba$B $!fAn- ٳg:R $Q` :(!5Hprf\є4*ӮCJl9q oti'~>_Obk*qaiy=CPc$ggw^}#=² ūD0[gٌ~-@:ᄔ]f ^mN>xxQX}>M85w:5CJQ"j bѸGS/gқ 'fؗ cj}<{\7# ߆Pggg<^۪cQ}G Á%}iV9/oDg3zMcZӠJYJt^w'(2"q;NUmG]Aqg8ڈꖀOE_!wvDq.w 꼐oE<ي1э6+*X_`lT4q]ʈų)_$+F3r:S<8KAD|E2_݂B9b)1x_;Coy_z{=Z0pPB+8tDvM+kl3)FQz<ۥo¢|n hl=pT#Hi5&ObY-6a|\1mfkhx9̛\;-/ϴ=pٺr[!D0@xy1p *'E;:tzeÒ t Q=ӭ}9oGa0mb"5P.KEKͦYdTFRE6ֳ̩rXZh|pGga S^CE5/ZdzeD{5ЅZ#_3=?{R:tZ0 !?c^}HJkx/XP}XMdܐuWl}J\ɀ6p/6.'{ ej:׊@@h}UQ&UMf0MGعe`2NeB)"$Ndwt0^ر84N \y?2U KJQ.{w8@AacB)9$É]p2*n_`ɓN|+Y5o֟I`P1/O\oztZz8sxQo˘40 Bu }rB,)MD5,)ʉek!u=^gj6O#5:GNg٘+Ħiq*G0%`I!3xvoLkʿ9We03cYt N>@X >êCEߣ7C;0;J/1Wѳkn%iD1h>U&'X:P j fҔ7ijw5z]9Kʾ_Ybܢ}j6g5CZz沽=[l:B'\H̓EIToܸ:]ezKUߊfU` !@LL z>899>7-d"a$$_zS)}MiBϙ} i<;ՙOB! 5B $!fAI!Y@Ba$Be'-gϞ޽{>!|}}vU $QΞ=+V|BpΝ;Ӯ(8ݣAOB|(V7o~(@M'ij\w%B $!fAI!Y@"6rBBf&EenNs=_D%*ˆĪ`<PCwij>/=C쌳;/>ZRp`I_UÛ2 IaQf j ݖvBgy ,(Z)-!x6=d3n]D;7 .`/Y<5?| v|.~YߕS9^W~fݴ0t@ZĹ!!wH=5,AZV3?d@W&ԫn[A!o@] kSEyeP-j9v4S}wԭ[7O bbby.ɩ'p0i&CI> !0 HB!̂B $ Ӯ0] $Q`8xӮ"8Ӯ`fþųחsαyfbccvu舃O*(`~~~O Bgt !0 HB!̂B $,H !0 HB!̂BP\!ij0HF !xЮ`0HJJBUUEAQǯ$,$%%a0 eH...DGGV-!xBF#FB)oo*^!sFF !0 HB!̂B $,H !0 HB!̂B $,jx{/NMXާ;ީe?˚Ϙ:$f{&EG6KxKGX`6eSYt%==ٵ7jS>m='BBSKQrUjv|0㩅W ź˙;8PZV Mo&3k'R¦&Zz*#qGOfԋ+.n^Uoè.>(?}U7prȰ}uN:mRw49߲Î:ӸJ>zW\=h8d )lK1''61JzM:o[;FnEu[6ry$N߈X92?nC O\<А[BD{4N8(*WQ ],>uG; pn! &cuofM9\Lk3 ϩ Iޮϙvq֣7O?˔wg  Mq\ŠXRyy/f5)~s>H;g7^M9ܓ]\r(B?1QQ@ hc .nu&3>$C618)*RpV)*qۇZ絔5O' ߁.cnD7ܠG_1LQ^`]`g!'H R㉾ts氃`ka0TEJ\YǺ)8a^?|*Y755𸰊+NEUGe1= YՍ[1]t^<ԷH4[ KVd~ -XnyYmן!:̸*V{JC[(XפIdI\[`BBl3BO_8'Y;mI;8S·f'`/xT ޞ~3wڴ(RZ\5PbBB Z4H[>p|f5g L`DZd0ɉS&|:T#O&n9UME԰x.hDX{*~[19N'iSLU2)_*ojQVǹV$vGRiWYipd2wo ĽgB2i nWuY׶3hC]ߢrJTj YY-q3]X\jXdr|O!MnQ (*&5lЁd̨U6n=bq 6Pϖam鿩4&,bN-{-PU-j|c~?_phAٌ9;yr/Lۅ3h<} +Wwʖlx,󃟹su#+J@{Zpyӗr4a:̎=|9q?;$T% hkm5܍<ۊp˶~d_3oGK aYL#z8%#es8=OJp[ux&Kz0{~ n:⯞"Ʒ=G;LIp=12g K+<q1$ڿFxX$p-֙TŻmnR׾%y^D}[%(w}W+ !'8Nk}mB:x3> fbkk!HQ$3#>>|sBq]"'G1d=ĸoN\n>ە͈3ajv]!_ad˾/gtLg )C1r;^1c9jҟj'A%68MBoO>nqgɎ_p ?֍~BR %)jA4NHۈ1cʽyJ-R׾c@>|GϨg%&}iWC!D!1R6Nz%||{! oIGYv1k}ʕ+u":T/C)=ޥh~}h[+oO›{Abr3F{~/^^%)[7_CR;7ӫӦќnM)tmR oO*4ۼ=BKۓ tz5#קR@ yrTz)і2>xxt,VŲ8;;Έ}y;!I )_%&^0;sJD}k3nT"rL<>Ws%#s{~ dkH_knl]a7o&S9:-mY~^Z4uxIUH6xyzmd~P,jpgJFeĭ[q( ԫUpTF/g<וY7m?' va_Uxg wFYob?0h{CUn?_<IZ|j ~T|ߖc,ZWF$l`C^~U k9sN6D`rzo @u/f&?;f8)@@ -Ĵn;='aX̟,ʭoaDgKb]p:4{D%@_g/H-$O4AQ1Ejl3/-R8ZoX@ҍ S"s)6(3vBa+U2qzհy*/K>MW#8[qB~iSPqtg_wlQ &!hWXZ+玞 kNc뀴xlV.؛4} )Gt(kwZRzwPKuCQW+sA%Xr\6 \AdCl 6{ӊm&u2ֱ8^khMbE"qq@3@¶,OQ7co([!#}b p$+8sDH>f:j#%^ϖ8orx,mǝсR8Ŭ _ NEf0U>-s,٥1i)gkiޮꎍOn$?Lh䲒?J>/og1Z?+ŲL]Jײ߲nFi=8Ei.c~¬7Ѽ,h=] TVIii޳ s;΢;*}Uu27O_؋k,] &d [{|.^\wic:&w )bJMUޢgoѻv0CemMeHf |kI z٦y.S"Ӥ*O7n .rxD&.IHXңe؈Y q& l\lQEwz0qq]CxM+8]zMۆǰbO2qF\`_[Xћ.ˤ8beO17o ٭KA% 1=pt!|4#AZ 4_ ͤr#YaY8qsKU㵾28?g,pЗ$VwhG T.oi_uԢw't҂z oטWKL;ջf24-<ݣ0O6ij+x8%VbjSl牋}k!D 111/|SO0`2M̓BWd>_B&$,H !0 HB!̂B $,H !0 HB!̂B $,j @!ij0HFBЮ|j0HJJBUUEwB! *III BF $&&jBf41JH666x{{VB!32N!Y@Ba$B $!fAI!Y@Ba$B $!f ${2n*ȥ>eʽ2LOXuܑ 8ㅥ ˘veϽB $6 ˵8Ŋrh#֝M,(DtlZ=&=ظP?ȥqeg^tAotkt fӹ^oN>Z?:,\O)ke,inp]iܚ2u]ӧ] !Dv)T9*5R~_>{XyBU [g[bL%$qJ/3l͇Gk'R¦&Zz*#qv2Yz$Znjch`ϲaĵu#yA8A%L%3,~= ٖΐ?~D[j Or|}VM? f<}_|n.z|º*Z}ߨlݒz!twٵedkY2zeis)3doh{zCWw" `̭^#vͥgJ!8m^>v=O EjR2*WyGӽ^rcm?d`ͬ92_tmF9ubؽp[!it Um)TL< [Tu2p荗6gՉ8\4q_i+KZ1]˷^~ mI>#qZ] ԛelI#F˾!3W?Mm9V¨i4`0r[ ^%+2Nzێbe˚8CI<ޝ5pKH3x SVDfn,{$v' $Sj<ї~v{7FW2H+X7e' OV]e4fVvEI;h3F4G"qz+f}`} 1 u0?ωkTK(!iT {,a=sBmG[;kU>})'!bofV0Y1XlNn^B 3}jJԭUKS!e'^f64.r=o/juT/P-PjViO%~A}ٺ/-RBˢgBP)u3 /gD j&Ϻ*d=*`LtG:M:rԩa$|*ԉb#\1! {B!Ov%kiDF) dgDCsZ!fd94]#GsESu{P@&p&(lpqCvX賩cC= ;&HGSy_W!2+@?{pǁT)Qi"((6%-J74*v=^hl[Ԡ&DcXJ (g9|g'7sy)ƪNOsl)#wfE{DOVg%d[u{f,v0ަF&=ZI۶&A}V+ }}}$6)̫JТh%AA#;<}|ɰ%8{"l r ](ni IDAT CKU8V/s}/|w.f((C=}n?>΃iXJR{p^,Oq>N$:3=l=0Weϟo7mxJ\ݿ[K v)tmPk? "xiYh"klaGҕg U6̩?fmkUG'80׫wR~Qmޕ0$ILR)v ѿd\Q}Bg: u)cnm6-JP*&hK]-kpMe_k mNPЪk7+eǮ (Pе!D @Ӱ~ GӯlR[_yI_cǷ3d:S:r;3-Ƣ7r(35ezpxXjaE阹 n`zRߌgU&LͰoAI ^mtMi0͝LWE }jkSM?ۦJ$%%EJ~NwTô+=rCwXϷv9='f |,,,"#d̶oK@厓w| }y,irjb(͝؋wGZƳ~ϜHԙAuR Aޫى̂ NIA Ǐ>zzz}7LAS4 +++[@JHHĄ#I$ni |dYFeHHHp- )JOA\Hpl=$1L'yz_}A~]̲AtH NIA " :A$AA'$  蘋/Veqss{O5C$A1U‡{]{89I]'633n׮]C> |I̟˧h yy]NnCwm#OL츐)WƍSX1?vuэyUط\ˣWs{T̾`\Cf=YvV Yz5gΜYf˗cWGH J_#G?~k%'\/ax:sijt-"gL ~ךŝQ_gXr2[BIGlUA}JGL}VVVX d蟙<9f pw#/r45Ț;pt/Aաx(cKj Oƍ8@N>JL(!aMޛqQ%㎡&<Eæd/.c|['T`8YTD6>J4~c/{u?:қ?0˂HO̥o3!W `̘1iӆaÆ™3gݻȎ'6d%.+>ɑ3jWOq9%k!IxC{wv%f%0ZɭZeE)QaCS6\=ucW_lnzDAz\۳(wOig7by<x{c=雖TWl 䖻^3ZmVTXbc/\$%Ǩ;c=s̈́w+88.]PhQ 5էTΔ]1rSMr~z&8yPtGy0n'!Ra}3# dwM߳R Jk/]ɄZ6<9˄ogw+0SySk&FnfpctFN\x4%C/q6NC$iFAØ'oQ&cR s)/%p:c9]ç3vn.$fg\{ ̛̑ۘ:d"?9ͭ Km\ ypeRRRު)EyO5j5III}FE$pP6uMR^du57bYƄp핱gA vfӾ>͎҅eG+#wbJ}{SY#Aǩ-KXp*wQ9RbObvS#AOlNA琝nLAD@AtH NIA " :ALsERSSߪ1nnnFa$:&55*sTApD@$]ļ}4IP&eEWٿZW>i=$+<>vUD_qI{M츐A>}a`@! B|dGUu~݋9a_Hm ESkkk 嫮=`(zG4 U#%¨2> б -+,V0{5dcf:lBXIf"7/ cIZNL ã= G`+?l?5vr{:1l0E}(a$4}AVy%v~S<_Uʦ[|R gTEӗ]ٮ3 +++T(.NiTNx6cxB\:vKdkjtO+-iM)玓;O"̋T5gӼ7.*[윊xgt~##tPYWqujݕ/ylOXKoI΋gp|fwX9&ƵSt;c6۷@2udR.B]|85YigwGӯy-ND뾌Kt$'HG;V[QCK?]MgW-u+R_<9ւuЩWBԆZ4j5I|ͳƲ&U>ɳӥo_*&4)[6&RH |t&C^[; UPvmU mKprQ&/+HZQ?8GՉH;DZi$o슷 Jޛ=AswsR>g7I$鬊:qӞ| JQs:A8NIgߐ8֫H Vpd{,#Ϗ:msJ6'2 &@e&>`KZǩh6;ʖi-q#(EXec oEG.l՜\/_CK /IZD@"̷dr24e+B=z!㮸,ah6˻)%N-D- y-#YjюQkǭ{be )F%Sm`p9EWޮtL'O7L*-{8HoWL ;co]_jv¯f{"w%SoaqAxF7R~Vgeld-mSsrvKzZnTϹ˙Pծ7a~CHI&%[Rc'Ȩ]Ǒ]ZcV ^NJ9ZÓ*QW2vVo\iHhZd@8r.VJ^V,#Wk.ߋ6$bu\| bNO'G;W'VýI5Ƭ{Xj+X2{DIٲ}<|яYyi=S*FTk?jMB ,[Sv!ɤ\םy'ZGHm Bm7gDž\t:a*L&x? kF3*[N%ӓ4ЩhB s *whe,}ٹ]v}b-̥ NjA0o<3 4u69ɤJ61$gAOn\}'L 8FZXj5ˢ8GUbLoHVq'A tcrr+Oʚ4豞{9҄&.u6k-Ufȼ9tr '~e#-إS_=uwz5Ն.bQ5Jzihs"/: 緿<8jb,{,f =V%賌rjd9 wsIՂKW:f*]].|\7Xѱ+w+ZQ%IsqYAΤ-9jPZ7uYZ~"ۢYV gѯD:m U|,G%T4Y@3@$\΃j20 (~DƑ԰(+`$uqDZg+ÍMltdpN+B(Jzm:;rn[X\ضtNaX'<,OIILۮO \ )SF̜VBɧtOKoz5%lJr+!h[7  r "#Q2nˢΥVB4ᷝf0˂HO̥oIљv5(۪c#f8MDzmF` BhqG9}Jl 4mdM&x⮖epΪH >R`f:͖k((T*"y5WNp:%CJ0ZB%2-㦢岂JhǏqC,;Nr-1 S%k.<$Sv&'$!cBVHIN!KEp h)D±wh(DG+sekxitI~@^(Hd~r!䈧]^G' w%dYFYH,qʭG<Ė,hǨY֏i ȲS,Ȓ)60A?H;򛦋7}y5j=-POd)c>h4GB6ϣkJz}Z@VD_G ?y~VgeldƖ[k t5x]΄9w !%ld>AFa+9#ǬA/'#Z=|Xq8G/fOrD^ib yXJ_pQϛV^Y$eu2WG9Crj l|ӮcW$u( 8'bEgz-ډ IDAT1tLz]@zrw\UXR,A>Y:\;kZ_S&'VO``MY`ɀopY7 &e~F?f[LQ]5 ]*b/ NoMZAaGFj16e1wߢ`ھy>d恗'ֶz$]MB|!<{Q:71iQ-JcYP"1~xS۫*άHZM+tŀ m)񺬖*՞?ҿw{v!:GȐȧFQOnϬ_l WoІ6ޫ )3?u-U=Ӊufzv?{|A_n+VG2Q7*L@/6zdЖ8g2u_Jጚߛ&1u)K oTC c)Ɩo'dH)#|3BmpbA?C0RYW+ @AA`,޾!9Srt LvT/63+]Ym-ccFiCԆָX˼$i>-QØMsf<2!& >O?pKG2ظl3f182,)VIgzSԔ.#: &+0emJDRRRtn;ɝ;w@ԉ'|F2 a׮y j$lmrU"8l:&۴ٱWعhe!V[Ioo:`$ݦǩ-KXp*wQ9RbObvS#A$6}?"m'cCN$ w$ A  D@Ate':ŋUcccSItLjj*oU6ሀ$:?~8XXX| '!*q&}Iɯ7763qڞ+H7re;$;W7\-'ڛ, Y͛39z&*Pe&?Hz3Pbf /R~kp1iW^͙3gh֬{sAD!;]Wfs7ٿ3 ʌG/:̂=VelX0rHO߾}?rk^!GcR\QP9%RnPZDZ!tFek[5+ȩcKj OƍgHbה6,C{;으t5 '%OBXr {11FóDb ʴ{ e5x5 ;N*[< 3_ý=tU ;T&wwN8c"ӳZ`]Bz#&Иf2:.7ۘ_ʅ1Q@h&ut~r -Kpຆ2zI6ДL\2,Kπ| )ޕ1* ӹ 鈥^F6@s}/<46&-Q2 w7a7v mmF9Lq;u4í k&&1;`{2f\L) I:3y4FгZ'+pБ<ؘӪU+ ,(#᳦g²*@&l1JbIV DpaT(|oɨB~-r>7= ,P~tVw}n'<,LFxfST(5ֱJg%UfvFX剗׋%/]X0IдЫ]-kx߁f}yAMxɧ/s痏D:m U|,G%3O8JX %8߇vޣKQ˞}j?wqӻؕ"ٿR3Уp ?-' B[i{J6WBώ&ԝrBڹ+0x7CZؕJ@0e O:NbÌE\=)Ar%?*MK֔ 5C89 (1chӦ Æ #$$3gлwo %S_>̱SU.DI7g$j39I XM}ܵ'9rZ~$8̳LJBRTc63r?g] _$jٶ&_U +'8ξ!qx/Ux6W:b!my98? lLR>qïiIKsWWpo3ʄvT ő.xrLjqR0;7'_u؛ cJ|twm"s{R/4bnE_dz#3>y 9H" IZ<8UC?GU-+P8Vmŵlْ%K2g4i"#᳦F=vs=}%Zd-s$\,~LMeA_}8߆`^#,#KrJ;K?/Z_JfEԨyT@}v>]!ri7KS6_R|3y+ۙI-˶$d?VQ@4yYx;Z+A>Jh4j 뾨X}!ug3k:! Ct7GEQrmk\['yNߴg+rx (Ѥ1^76U9~.xxĩs8dߗֆraWq78_ 8~</:ӳn9{yY1o)ɤ&X+]1If/&(Iu٥8>ff´u((ֲ59fV A޾ntRj.;c:|1>E)G,+\ݿ[hN1xQ[s}|6%= K.-ZTdGgM'3$<h8#]M*ДCu_&$"{+4D^аLu8)r͒ %GkI{V) 3cVސ3xT2LҹmQ̊aoڄbyKW &"tn=ltcL,FcF3oOGߢ0~Mд.ԗ(TmSq;PM\CfMQZ؞b-u@WZ'1~t{^z>ݿddX5GW$c`l{f QH maSxyjt_Jጚߛ&1u)K oSn~?Q^H ԫaŪؒ4zov'2MoOiH,EÇAu){8MUN8 HdW=k djjJ\\[o'd~M HJJzK;w`aa:GXRI/wX\/v 999 FVmXXXDGȚmSgt4Cpibff_'سh !LH'ױ>.vX(S8mS ((˂ :;dƭ0oiK&3EJVe4Q< ?IKҵ<&L$Uc{)UŅ OgNA琝H7A  D@AtH NIA b 蘋/Veqss{O5C$A1Ȧ;x{ |8" @䡬ʔv>Kffy؝ |=wD}c3#WXa;l=8_:sM츐<[q&}Iɯ7_>[su1i:Z@;7s-uAy:=.ahoW$3dz6.d\"ȍdב[憳26r8eAM|2 ʌGoWfs7؅PM\>ZH s:!@~ј+WG;;TE ԀԧѱV%=Qb@n90{it / /h?a7]EI҆|(boᓎi@_Rb/\׌4;VVXYbM* U"Q٪(R 2hW4NF$f3oebx8`g+J y)*>~VBUQe׳$7+_vgZyjC9CUYOU}%⢼+5%׶}=yC^# Ϩ߳V:!U|pu3~պ1ki4BGnK8|SV>KNVXYYa]$_3@*=) )3͉A݆0 =9gkآs26{ziF҅}Ӫ` <Eæd/.c|['T\NDܯŒU!'֮i.mFgĕ1еNۇ1_ L=8j>ͽIKԣJ69H7[j'K"%l"N;~)P{QVɣhOه25eplJקٰ rrpI"ІݜcPpGxA*`j;/j(dpt!2[r*CMq%bfD}T՗].at+ȶ9q Bq A3A1qv`F-H) \H^ n;yVkX8KdY=I(HdrYFL  ^*o>O3WԆZ9+p8gJ5hKi~8&at-7 ͳJB@inCCruR ]H ̐ {fġsIU/JhC`gr/U( i*dt!T@leA l:MmsfPOƳ`85t"j2u–\,Xy| nV>7AH8#[uԭnu+u}quuUj(E]nJ""$;?Ay^G3̮s޶jRDs)YJ<^\ A!tzG2{Y?ZNq(ZB}Hl2g̔l~ڗ_d`8V/Sy-j &xV>Ȳ'Ѧ1XL¢d6G908$0>U_)0P8aי|6zc;.!;7A}Ek0vt0J`}1BKc0&Ӎl䨉t_x-](>1C6|<˜$0s""VҠ@Aֽh0=lҪʋG4?1lNu-&'0GJNLs_ :(W[u<$P[sF0>fffݻ7»Nl QEMHHcbbDׁy-]D`IֵfBPx4zRGv3 oNՒCs]sdii @fE}1뇎K?P;^NVӮIԪ C9Ɋ ή͜$~*I7cאtng~'B6x⳵#i1y-ڋl߈)86Փ~LAwNA(pe' A$ H A$ H A$ H A@mgA u=IBAx=~FAՒ,H$AxWɲ,ˤվo&֖wRi@N- NN6_Ϸ@2555A3b `D A `D A `D A `D pIiڜAޒS IDAT+H~?_HDנfiu[ 7үwN?;5Z1h$/?ʎ|^OvnWl@yGx dj7nv&OYׁ6XYYamH1`[ͧHȞGZ[uplU}273v ,~ˎn̨ٔڮ;{a}<ıL C"<<2Q'#4(3mGl2HЗc7Z(丫5#Ya_/ 'qJ,M簶'rJYisKqVUS eprv&nK3տ-;vxҕVxUj͈/FP5\U1khX>c6> ͹ȚEjz}'3>ڟp;UA[W|?e=(ˮq= 'Eʳn_Tr)o PFmPڌixGJv.EpVJF͑zB GUm˟+^V9G4c r1`~]^Hyv鎳y1s( l؞Jul&\ԴgޚoflygA,;/R`[c8kkzfP]qwxI>U$H|i!.]^1Iלd@ vL=im;^4R3MpPa^o"Ku([C;ʡNg݊L>N @<tٚ ;?dy v~P%:-?Ks%>ɒj^gNkCIȗ9踳{3Re+_2gCNL(TP>MMA0L4ΔC⣚\ҙ!nX(Ҹ1ӫ0wq[>MPp2YPX`emcǹ4,~V<Az¸ӮT+W' 鷏oIMhgS@E]l(:q_bֳh }×8w ՙ͚rqY>LVLʍNd^?lK+ey |dw<{˿'sI cV U&EU!K'q|4GS [VH40}0tv .>H:s+fK$P[8W IOQޑ%9~%c/&Aj$ⵛjs,3EC+r;2|A%x\d K:F&f~ KH H-Y}1ʶ ?:MFua|wx<& Z 3ۢ;/5#lx sbwj]5 J0AO>狷*QTS/eޠD :^mYmAWCA?! @A Bu٥r]J%J~NA0t:N o| 8- $wAÕ2QX| $J)FFb B$RSS|Ct M'~뺘e' H A$ H A$ H AOڷ /m)W>H $A( iGSm@2D6k-cp `0މ1$'buUB*Fǻ~{ t%.VkKjfA$Ky6 iL3.5]]AW*Ҹ3'B^H M{'4 s3-n^^ٙz?ZoO Eю~&A\m^Ay=D)v& S?e'f}G!jݟ[N?siD(%=jE wɷ<\?s ;e{DȷV.:i 7 E2HЇ0x$9*'m`eZDme]7Бp2:bJCu)ܻy]˾dXNo߂a7 WBzurݦI3V e}qpٷ"-gĮ|⋋ a-ǗGSq'p`ccy@_748ya%x v~e'dzN[IHfV:T,퍫GT4#iatͽ܊.T/FA]u 橻 [Q o}i<*DNW$N8QhygѽN NԤ=x|<餤$o;|Ds5!)_!8s?Edh"g|j܍2TN^L? f84 +Fɪ|?OU9kzullq֗ѽ<={&>I9_5cH މҳ1',W]AE)cȅ 7ϯF0hJF/e/b/ )dP{R|{f*w~NT6GHŬk=1>t< s:#&dj`4S*= ˘N7OȊ)L s .FOx`ƮuB[ّ_fc&'qjNXؑH'϶=R'ТrØ5ŭLԞV{ju,g`:BBeBr0UH_sl`P֒{_mI0mr:њK3>zNѝQtDz)s^)qﰘ]|P"ajRt|~A 651vy| bd  YSR*S@c_]:4(o~6r茖F_0 `bWq?_Nl(Q:P"6p.g(/] E`LNl=4nS@"z]%G1Aӣi֥j%'WndfWa5 @Y 3۔pj?[!J sWY˹\j쁗JIPl*] ~~/{ ;}s1QM˙Rqzz6.ɻ/fB3 ={9/{} ٸS)){t]=>8Q9w7S: Op{2ai^nH wJ5ǽe(mI!j/kĊs縥+SɼuVk]G֑)}_\T%OeRuhI *W[?grlI8w&4[zxrI0*갿({FMm_Dv|T 1i_r$ANբ}?JzZPBLv+8wfcLMVh^- Z1[#*_c{1q\ràINsBl)Q:-.E n1ZnV_ڸ]˜("ӱ2i>5(SِV*FIYދqmIc| mhAuRJ4 Z|Aڢ[bR4-h[+I_~Bܽʉ7jqjcY/'9 ZNҝ+۱ſS~bCF*Zhn$ұ'fwtEMu+Qڿ(3ŬJz*2a9Wo~߅"8zX02@<͖e{Ekm$a|˔hQ׊}m@u`NaYDn [:ڕf] ͘SZzf4cV%SaĦ*0Sd}Z6"F1ʜ1Sҳdk_:ŒXI:6X(~`FP)b+2΍Jb*p*/`!sGKED6~~& F6ZgrD/.h@5Ϥ,тY&tM0\߫η6?,ϘKEpBQ% dff޽{sJl QEMHHcbbD2AހV%!!\ocii @fE}í A$ H A$ H A$ H A$ H A? ŋ$''PU,--177|$ŋȲGAWExO;vMy+WD(@LPxvrޞ6y#Ɛn:PtBAx䵅dHD Gr L^'whZޟo-`0dY~jƍܸqݐ@@ͽKq "2Kq@̠)]<;ρ2wo]f 0$33C@)o;699k̯dL3 gl[=NOy U_gm& foK'iӿIq#ݼ%Y= L؏N"X^o1kIOFao8թw}V>iڇA2ֱ{>|meG~߇rIӗyWGM=^a g#rwx6cL;U ˦u1'SƠ^'6]]cHWHehI Z5 )^ ) ā.VC 7?z9tFKYCs]zoG@ʪR~6_o@;l{g&XJ@e/ -"( Q&e ÃŜ¦Y_Хv͢us𐳧Γy,<#SRs vB2ţYd|Vٕ>F4oہmjg17AI6ԙ8[3>6, )$hif#5ȩZGR1B!Y/# #JZcmO85>,g 7ThLe15mJS-cD4)zPug\7$Sl_4BJ zC>/^u%af\ŋRi=% 3,GY҂[ K{\6b8+C?sQO?|(dݩK^=Y][%j>jScXV)˛ঢ়e5\5ͧڿ4ɤُ1Kzϧ^ēN9\(dbJN~ y \ouI·m2j;>J)^kQg٥LL o*i`Kgyr[2LAUWS2N\|m4$!DY'pXKS$GGdnt&ӿM\L҉p 搻h4 6.8ImL *!Q=*Sc2Op$HOXidgoĜ9BJIf=K[ZU~ExDo,Ÿ}I1eHy6h?sM^k Wү&Ԩ!%rshW NlZE@mդ^R]8eP211yod8c :zdO_qkg,1$L-q+붗XRz/C.e1h-zU\1Qb=: I.OUW ,C&b] c;-_ToXۚp|IBmbm1?>1M]W$S;\#~JbCzcT24cV%SaĦ*0Sd}Z2^:4A 2uQ^Uע0ixIҡt#̍,ajAWǐOzzѳ4 %+/FP6!!a~^ |%`߾}t5Ȟ={Ysgg) vIjrVKBBdii @fE}i!) $AH^ N KK˂K( z|Yw9Ν+jIrzΜ9a-$A( \|cǎP{ᑏ5=HP B @t A `D A `D A `ĴoA( /^$999O)R//|JN. IDAT ,HP@ 6GͧB{Ot\6nC7r-O2Cannڵk' = $=WIy?\}8_&sw[eim>Cxt:ZjE%xaAWGL j]Ҟ])[fWs&~ktlbPD?arНO#_'mmsūu`\| ;8pI90ҭ=>yv=1;l-" GM{'4 sSƮe(h-NnUYImƍ9w*cccHr.÷5veT$9=I>eϐ^& q|TC9QKq*Ŗ/tek}^4{RαitkqrDv,Khwj`F|}ݑr{ُW'$WF [1l]{욕 )OթߺO\99=6-ҤIOǭH vct£P6l܃4,Ԝѿ;nzKa*rR$MQ$~ʲ-Eщ1=k5ϦT YuKҫʬ"$k//7лQJ&aD.s't'$u;{wh7`R3svbج`]FDO5Ktd?aKN܁7eji3v2bz2m׬S= zjT/ϿV1G8|ŔrP?~ĈdY濳I/{R O՞Y,>MK༹qHq}ٰK34Xd}C"EpaƏO\\h Y!pjy9x0֤ m۷NIkT@˘]3RR0o~p+N %(XY˽ڳqX,% Ї߲Y?hrj=Pb⋟_n/ғx'&^2v (U囘ySzʨJ^̴]Ѥle E1swr];hR;&8FB^sIəĜ9įFJ>][-[|ČyiJr b踋Vg=\JȣH1%Ĝ`\>s8MS8G$']CrUƽg85u QaGYϟ_Uc*[ոT0ĥd(}NPSnQ[04ųu!Fh=6BRz]~Ɗ+YujLfsXϨ3H S"sT^t]IX45i1u,A Bߙ8t)[-CeGɵ]C֧fx||I\=uCuu³lmϋ&>{/\96nM14N؎eQ4`c#ϕ7L9548uJ:@u~ߚ8j\ ͘_M}^z}3&UswA{@ :8M=1tCշ,a×3%!%pwrɋ O,ވ*8;:S?va #j킳O0 ԃGr:)2 [?~ο>/wǽ|?:ugRۭ$]Ģqh~/VɍUu$_F8Q/B* Jg/< %qh&Εo߳X,Ԝqw0rzʚ\aˤu(ϖ(t`4;n%r,&8+[:`m& Ew8y3;7,j~eėD5vADa5ح|Ҵǃ3vUec0}ʎ2+Gl0CBP!t#_]n3T. gK _= 28951f}^;wot;)UǑ8.ݵyr" IԆ.s`'IڶoE֨ߗ17 fҥ !`VJP3ʱl{g&XJ@e/ -"0c~R|*2{jC?.$މ㡉+WJ}&fT^2*+3mW4-c3BEQLhh,`!"Y j F%lWfUp0%PR<>zϘek/KL fl)~tJ.exOWfEt![c?dJ@n/C.%srO;~=r'^ƪQU>( P:W]wAZ";j-lڰ53t6Bu11+l#T繞;ag>:#3l]=~f=IA6ŻBGWS_ Q04Y֔(eZR|IB(8l(.%RHȇflGw^vYWh1oxfbSk?>-rHrL+nvU)9?);?.Andx&GMhm BupOʼnf53;&|"TzI& : %+/FP6!!a~^  2tPʵN~ϳӾטߠ}Vsjfue۷ gUuZ- 888|KKH8YmEŻBkvrS1X7e=RMtc ̏+Yv*1QPZ?FW H "#2+VA(`:[]A (w|M׽7[$dHHbĬjQjK{޴TmRTUKU+y=?E]|<{ss~s.}!Μ9n CvB6\xcǎTP!lmm~M$x|||^w02d'(H !0 HB!B $(H !0 HB!AIQCn^!+fI{t !xn+5hZQJJBaEAQj`ݻwIHH@!ěNӡprr2 HVVVxyyjB!edB $(H !0 HB!B $(H !0 HB! Z?QOPwx&mOB<7# $Kع<^EuXeųDǭ3ֿĶ!.SBap Gl䂝]b{$~ 3yg>b^'S)Υ+n˹+<5v.ු\!x v-;2tjs ?з fԘh8q53v-r%t!0萝`Q+UrR ~Ry#/\jڲ̢?7G)J9ez.mn>4TOOC;1y~ӣQ5i{;gGe*WpzB'(톫O0MVbQɅbWb3w=my jK6'E݉xL [n5ff@e_I<2rcޢ(?:ʤNep{ҖS;P*6EPN]R,^u {X8,s-2*ߝ{= ufVˎٽRTl2ͪnz ~wqpt |h0aX_3 IDATflz4PM_|B=z}ŌW Q+<{fP2E䵯%C0*g]BÞC Z> 5?B}þip=r?;9?قY@J2%WS]/ޢ9ASi +?&qlLkpKaVWTntbo7A!e@Jc1ݛ-~E,<ξI?Sl{sJȻ3yNosL?ڟISkTLU(F6ٍcҪ  (ݵ!XhNf7I&s]5)DMXYOcٶѕSV2LbغbXY L3[l0ug,z jlKldSU(kY,kfK;I,lw񧖇*@۝cN1:|`ӐUS'>qbA!"-s+}$^73Y5*R5zި%|T?-3!?H 4S1bbb濮F !xsMLEȷFB $!FAI!Q@Ba$BMNHHݻh44nBt::GGG?c@s JJ%B7((BRRwܡH"/ VVV6BR/`dN! u|7ceN!U y\YvB!B $(H !0 HB!B`; !pϵ5jEIW$>>?:ǎ3Pk0>oƐ]YL#I%"W:lM6pEblmms-W\A?Y7#Ni ~}-]^I;Rq3Fͣ_e)/;ƶN@W\܋SkQRi#SczNANG  99u7GWHI!zFvIY7\\=)T_r:uMeDQ___0D5e\[3M +k"3Ƈt2>>xH5nDnDG8?7蹶ae}pqţD%Zfuk7qt.Ϩ#GI7;(ʻ2m?yu-BJ >l)\= +Rɽ?TgfwCbnzQ`ߐatt4-[d̙_gg|mGh<|6DU>;kpܝa-}I<Ʃx1N..|p E~gɴmǶ>"8 <ѫ:*hPby6h[WoBoǸhQ!<O7”`teQ|ޙ6S 2孹s&m=,i:o?uŬi Ȫh<݋skLOôHT3XGp(o*sͼ[z<ք%t*eM= % Q?CR;x>a-;ҩK{v@5EB[Ӥ5P '^*.ͦZ9* jԫ߽ZT4/lj#8nQ3? >+ Vr0~Z{. ߰ zUYQ&ekQ5P[y{O0-:6&"rcgr]Rh{\X9;4X`A̼q*phJ]ꛥ5ܷ ª#f1ڬqo<=rbF?Ƒ?4ϻڭ֏_I\Cn~6=J '?4ʶRiQ"}R;*Ԯ𙻉OߙR9Llw?wezݘJɨ9榠=2}!r_GAуo\g?BeEaۓ*?i=DLXLvh.o}y+EQnIcVS )1clLXJBsOSP 汶Ҩ0 #ٵngq}KovZfΜ9ȹ#!0RV\BP'p| S*$9O!2t&qoԜ e&vdžҠ- EٷE#\3{eKLS+,1)H邱O+1<;J2}Us,(Y.Uk_wls㰚¥JQXeBwmܢ*e)[·D( |\2?ϙ>n\ Н=tx uBJ_:W'!da =?'3bB{*s !"Rv'~˹|',,]A?ap gOgCԄY2u,&w_N e}WcQ2c!gvDŽѰU(Ӫ#_o &yvu* ՞Յ7K>:G&(VsC(=p ,0a:LIƦXZ}OZ.ڭ5j6s5nh2 ms $L|r3&hZ_b@ǙM$ `R>x,G fv a=~#VQ8*&~ZIY>ֱ@-¸H S|ZرcjK9˖#Xv$u${W1i.<߭pmʕ+? _fR)6]W6]wuovy%KU+ʫߙQ!ngt:۷' !^9# $3١&e}pqxPMZG|锼7PY:Qחﱐ^{O;̍k̆ "<<sO8~mm ⯝" FBS;7fƯCMv(oӫ7n\EI{g޵ ԄzuL -LȴyYJ-.*\I]kQ~i> +k"3n9u(wE͠[.I摴揗;~Z0jWӷ/~4ZE)] S\GBƮu{2pRlkQwZ.>n1,GQlՁREps/F@,;~- C', N=yl+82.Yw=}0 #zR食d刣#N}ٕް"% OysLdNT WBeo+e 7vDī<:g '㞼W(::-[2sǯeV(Iዷyo$*ǃx uD;_ӏVʽ iٝ+_'Ӷ4~K2#8 nh=ȕ#DM%oP-uDa#+ cjR@Jtsqr>({y{Bu>o?>Gtl?SPoojɀ [W_-eJРVִcʆm0~F^/oEš㼏1o!LJ}gf䎯fa}p`H*46V[Cm̼n؅n)slI,LcX3?_=yߴ1H¼mן2]H!ْA۴u+'l7ʒ '^f}dmmÇ?~_ ]gaO{ (I֭G&44( "#f=$0ūDv<9]^*P'b WR_ìNH3J5'n5ϣI plA‰G~{ \xH&TYOkY=E*V-=g94"nnݙꈾ^FQ"e}Rݞ{i M֭}8AV2!3ΑzKAqGI}t0E`f.́/!xU sO̡kX^-;~oWnk~(YlNp>9p?[5S^,PT[(hwDkFclviIʕ2'jb/bqSʩ1Y~Cy;PST) ӡg,(Y.UkL3ASc ]oW|#C;%qB8e5:u=]_/{̲6#Gjl|}:o饬؟ j Up&aW 9 jԨQ>}PdI ,&ba4jPܵ +^3zwTƄ9Q-{$&.2zxklm[Ma ͖6M:J=[yu+^흫P-`E?uģ-(lJ(bu]Y4J|j>& 篥a3aoiV0 (K lx"r,͛Ңo;vn":V27oԉ0||ѝ~Ŭ?)8pbolV|8%XSU'_y^翶06FHj;a\AIDATvx0NBԄY2u,&w_N e}WcQ2c!gvDŽѰU(Ӫ#_o &yvui V.Lf@kKQ*7x&HZ8S]2 +C+ߎnΞė+7c9ie}-Vtٔ:[3oi?gNPcV&#@[.ˇϹPe4K:~>SG%w6l8+i*)Wg2W0~:FvER5NKgz>u @j\B $!FAI!Q@Ba$B $!FAI!Q@Ba$B $!F-B%C H:]7B1`W>j$&&( * ճwBPEQHLLDw%!!oF!ˡt899d $+++ y!22N!Q@Ba$B $!FAI!Q0,;!s>|Ƚ{^wSrsX^~E-{Uq}jdHB .R {Myo\xl$`Ueo2o xۖ!;!Ç zx݌eL(c[W!gCݻwO.TrMeYM#w/> `<|O?aAv;z5P5z.8˺ִW ^:=il[J𲋡H ȸn%($?P=0ǿ9.kFUt9}{>xܳɫg P/(Brr >SR R!y>!˓fIA<\p’Y5(1L~;Nw35 p/C@nw6ѧ{Zc&!zS?LōcccNҧЫgj^[1Wp>u~GC'>?kלò2V,̡kMhY֯'c>˺tdjں6fx`>oz={"n,E ڢus*oJ)6g9wGoH 6  a񼲝#=+.&iPxv(f@hPݾ[s:9]2ǞfU?#S7p`[42{$AzA\u1_Gp!FSB+}5A@L \ ϟ-@eY} hU )=$!HCX|my߱qxچ "%%SgR>L=z[ Y&BbgS~}b#Mu߫JgIo{0-)j3Q !;9^j_ZđDNj5[Q27ߗ\'u`w0 ϢĒhj %!!DZSQ'aNcr]! 7{ft(VQ29ȼ ui Tl_R}YAL/G8vh 7eLF"$2NERbrCgkj?;=NuR!,븘ンLtj9]7m(a /k8^*;q"_]< ř4k$Ģ(-[@_߼ mZzS(z9qYMX3T(j-(sNCe)XRx'[MR*UZe^EvEꍹx )ϔ$Nz>=P[oǤ"mlK>.uL-k`ܗz o$i5[৻zcbl"CvB!rG,:y[TvD՚5p ڟWp4^ޝlgIgNLkVsӴ7<ơĚLXWkOaKK}X?#y1~435C(hӇβPi'd,Vlʀ*s2Ty5pd 3ê#AupTe><HCX]ѳ:n'Qx &l@)6,ܮfOYi6)*XƉC>` Sc>˚HMMU>=Zz#jF͛710s33L+ {)d66EwH6ǫl=WcäBU]-DѪ8La'sG/Be3DZ0i&3JPLL|B={P~9{{{}|.]CKo}Zͮ][n4v @y65D=zDJJ _Yw;2{R R0553s3,--*?U3Ѩ5q+G:vvv}@jM/f!|@!kIJ*e@nP[h4 zc>sZcUk-,t ?^=PThL4bbj JR~js$>)nnn.mdL7yfh4h4̰*P Ϻ3v/z.]B{ʚ'<F~&yiµ߅u%ܳLn egg>}fVnj^k/#ZJVkhz8$\1ɺ-QQQ,YRnMOz(lmms\=wehT$ο*ͳwgAngH ;,4cPLL̢;̟I\\ܿO(gB'SyfC@X C֠Q oI !qp]vS g{>)?60< !0.9 A㤆wlK͓!́D6ϳpB+ d Ӿ'}BBSQ(M3Zi#}٧NT ~CW %ns_NlBmLvv1=HZ_d'izփ>v`cvb !abvꂍsܟP HJqzփ>f̜}d֘ss<\}HZlFWqԛ@s;fM@N!N6`>Թ&ct}XJ9z?Eu3stNO!JK .~vyɬ u3s)V ~SP:=grf̜+Nq!7`SY/3s >غt fNT4֙P灄Z#)>f}si46 4$PjCRM'[kWjisqq;ŕ@yB(+оNy؝:ΩƮ.f':6$%\=vup|mwn?&f2S\!NuN ь+zg)vq}[l]HJa{p욻9֩]>[s8Zsnj<ɅSԬ:[dWkN.$%و9v05p|S6"wi1.1] ?'剿&申atf5bWgN &d%ш=fuvڬufΜm]L3v`BE49slց}ssnk`̹ JI4`L _g׈8>g\łc]qCQB(tbN[2b:YgΝSLMJspj5hŬӷvqxfy ۚc)o֚Pe_!(IEibN׬U,b:=nWc7:s_3f7S\5A+B fvʫ0bV1ZS)sBQkhN987`N[ݱ,mnn،[/Iq% f49ŭ̼^5Vgwlsl7WqG!Yuml,_4˩m0c})揿ww<98Qcs3n6mso.̜1+f,(/'Jf[3fr =o.[.4//j8jlMc8P0aw\}n $yy9!8509ov1}יM^m:=b[f\9U̸SIQW tyi\5qJyk0rns !Dyg65sjbM1J@5zܬ[.46A|@%S6;&k6mbPjS*誅956#؜ L !?4$4HƜ+0Tܬ3oz\}^BڭbVQ1V֢S)~Btߥf @_DoIy|s8~LNysn*j^![j^͸銄fUh9ny|=f=g̱S^o.iq{ؾ};5kŗ^AAN!A)v W9Cz2*frq|.O~fO>z̬WS^jAd-^=1c[l!--mcƴXhB\oz1ovjbvS;.Oܚ1󤩛Z7V_0xo&~_WlӧW4h瞋e-\gYׯg۶m,l)Bkzwq;͚53SBK7o&;;M)6nHTT76SiX^{ coYxooՕ Bͭ>V*_03vrv`nѠ،s/"TZDZP殻K/5Mn|S]ŗ^brQV-nzڴicgUly>+V޲۸W.]Au]G B-32;?&G pqxEjz/3c7c^8lKS6\xfSq* n7TP+''$J}}mnVիgK/yjJVVڵ%..PL6oƍq6n͛p~?c￿`AQTM6y|ҤBTƏwZaC}7zfr91s\qҾ"Γ2OWTpP7},}_\ۿ?C )hm^zXl)^;pۭc9x #Gݍ+`Μ9dO?ѱS'nq3C{[3S]66l`Mr͐!\|q߂9z/&Mп,[w~CbРA\>p`ݼwq~)&th⼂Ca_'3omfL{^Wc^HXrfyS/c !ʭ.]2kl*Ty5/bӦMDFD0yrM7\ܯ?ONjU8p`?}=0$>l~g?ۓXwc3OQ^=.[3LI&2eT^xy5lȡCdf1/8ywяC-&(`Ȅ WZ$=)T\T8ա}JMYkǾcw͹`pk95|>RRRȰ f[Yfqu3}9W L|\*T_Y*v}~ ٵKzAqٓuvU|2}PZjWwNo ѐ 8~BH^Ue76& S\W$w:15s~N7Uf\uQ&O{WSb֭=<rѻw/zƍygHM=İaCI>̔Sxy}QBSjBrJrr2MY(_Z o!1ms{o1fn+"ʗb|k7=~N|Xz<2aWIJy\etjnwn?2r8CB|R%ɼ/D[*T[s,__LHLJ2CBMri| Jbb"kQ XansMbD+sM72|0^~yO<$NUD5O=eo-QQQ-Zǣ!**LGo~bEps9*8x_n3p? sbԂAOתq@1113È#1b111 <1cc&D:z4+Vr`rssNV\E ^zƛog^ضmTd˖-@b9!^7:v/ϭi&^/QQQ4hؐ,}wQhqO?T,"7nw4m4`骇~#Lɧ5V}HMMsDq Iuv'nי1W!"xUx<WeYx<TB͚5ombL>#GD.]\;|8oΙ=KFF *14hniӧcMs>K=zjJt]w, ۷gǎ{?YYYiӺH~Uo"##Y8(cQ]j/Oc:}6(**Ո`EPMuNy9m+ N+B=>b^o[ھ?Rѣ))@wnoTʉ,]\B3򮻹b :uh(֯׏l~YاmokԢC9Fc[S\ /h'0rܼ=c9bСtޝh { :4'䔔/,b__8R!Nd s$pzNq.؋M7 ǮRTB:tO֭Xl{.M4aʕ^BZ5ojPZ$"Tok^d)|EBQ9q;ŋ,&J:yܪn֨Z mX"- s?ÏTzFvv6dff@FF+V$""(>[ʕ+GBq2jXޥaޢ[j˱EٽaEUcsj/v1]|A>Q p|\_`c=ZT1}q^$33_DLkтBSͭnj)v}̹/%:slw D7B:0fB!TQom[Z._aj:34v'P]6b8;aGnj5VB!D3v7'NyA|(텄O\? `w3=WspBOvGys^g'TQd1klw3_\}h!8ж>wQv1BVPYonw͘VQs>7f0C!]OQCfb̾dWkZH1ǟ`}3@>!x(OՙЙRs";9N բB8}Ϯ/:]L)_H /U`nA,d35ӢرuaqZiJk2cI/)e[K9|N%<:a'N7EٺS*T̘9 o|8hɤ"B|eF,"`^ِfDWo ssS `]3JXx?== qJ[kDVF!Qؽas̸9)o!̓ `ngnk'\lN1sX#T͋JgF2{ pG_tιkM ܝ~̦i\1zǛdT=T߂@%n,vቌukZWeʉT) OJ}uOdU]8Lh;b},B80Tc+uem!'Tٽ0 k拑Cb5 '}aיi=Џ~]|>w9ǵqE3韕W_A̰1h_ IҗHuϯ_'i(:T* $I$3z"E|\^Z;Ypw:ߜƢNKx!"i:a!Bc󋍙_]ZD@.$s8>f΋+2gu18:%[݁+c^ko,d?dfyڬ?#F>|esȉ4>Eܯ+?ߡե_EkѤe7y-O;_kzpF r;K1O1'wa~纶_۝WWʧg?rZ盙0919{o0oVҬXjvn4XwYA|n5wu|9\x*V>9# IDAT>d#yt.aќ%ѓ O!?/~W%Ml?v!/lg9bܕ/ޘ86~~y_I|Ϣu~sG+pzksxC8b*w~ٌgMҾL|f/fբL߰#CoB/к7hn7{> q K2tT&qﯤ Q=c9n.OOy!zs.w7;w= ~װ?H][$Ųyă*Ml3:U.fZwYx;<=mCA#{]k-f-Yy%-9i&>ПQ2wf{^Ou9I_-s-R[$.cqu{E?ܒz0kάA̍9Y4{;tLBOS%N[/B{ N5dfTfa|T֎ȷo}Kafx"<їM47 jL ڵ亳B?cF2[bfGywS3"~~%]bN~.s&n$>zo nF&qYs<ͻ4;GG^bIZf*7>8Ne@ w}؊)IYl]\:w;g:Wci[Gڎ&TEN۳|H3Yҳ6+yf_UH_:o#hvv b;换8]Q>ĕur# YϙxŅwگXs#ٳr}8ήBLt߳zW]يU#UΑ}8lYA=ehѢ[y=/[$5oj `D:D<$>ְnj QY_n/_\nщ`XJԫW->bx+T3wl3aA})Pj}NSNͼzAx r(5JDaR>\-q]d;JdO@ʑ\xy' w"=&cO۝XD9nAvżW,5k K;DZL"r(3GK޻$WYĎwHg|` D~Wsu+UO0/:jϋCH,HH\ c̲uȰܸsSX7q 5kwt,#ݸ]`Y5Dei=n|+;79~J~ G~5d~z ͊ÙyKcǃk/q4{oKBGVElĦ$UwRU\χE|IfJŶ y׶C|zq_e}ʜ 8bJ\4jaKw2O TGCotO .N|I& yKgH'2kW#eA$PR]nug3I߶YSjr[h~&;yW)+-CTuX>}ѥT[2 >\*Dļ?VM~'ƽ}r/n>=0f{j$UKou{r8S0w5hsX\y[?èѳ\Z><{a+!ܓ>Nu*/VTv]¬utҲۨ*..IGS)o?^ۻ~Ɂ,ޣ$DqzjD֖$炕.hwUj$xWk9짣x괤MX/ȶrgW ^t0>Ϧ,,T,,vlaWF.njN8/~7綢__`vȚ +NN W/L>^7gǙ1cxxpœ߶ X*vۻXt;Ϳ33Y3/T&+S"SW>Q |ͬ7rvn?0iemfdKRŕMNLQ-NqSupiV9qje;qͻ.ܑ1$v;^ͨ/KƔ.6VGMF3 |v+/ٻS9 F#Z_rW劥םwrtλ8BEjwo.u=^–ןޕ $szv4lhSKAm38lUnۙpuS_~/7}_fդiU.akٟATt<7~k?/5zp~c#DP%s<Y}j\EsFv,,\!?pveyGx /^ƙQ~u^j˛GkeѣcIm/bⳙL{>.|,*P~T Q+fQּ?yyj4N>OaUM&UpNceۂyz_dzO{rqI)P j8v9T<1jn_ h[6ȟEh[5<*zhAڲO3$Jю;[.e^{'Seb5^B{~gЪUK3,Bkh޼@b儻 Ȼ@1^նjs+?fcySSB]i2 v`;M;9oXu*gu Qz/9)*7Q('8Zqe,L+gu:T>9OJ ( :8q,qNOnj,N'ĉS=H'[ ,a8_(!Nw,e}!+i(u\!WO)㔸򴐀}BP!"座ׅܾB!NI'M:YBqR:zTYϢWzzgyIO0âTJP!3,Nڪ槟̐(%.+RfMtSl}9Y/n/jzzڶ1B6'^FavbH};B!BK:uؽgBB!bcc4"YH!"lB!Dd!!BBB!aB!& !BTT&%%fJ!D s\Vv$VlE1D IIIa}4lPX3-ddf?;d1QB䭍]ԫ[GBqRvmgD%òB2 66Vb.AB!Dd!!BBB!aB!& !BMB`\kgfyNA,3'TB#2l-9w[ןwn% ۩ߺ2ٟr{>8BB":v~tr4C wm.3PH8ٟ>ܸ]={^ߦ=pt#/\݋Es kػd^•H-BϮw84 p+ >r0tžC&`oѳS:_4oAVi#أ;{ IH~ΗwGsy.tv >cڲy`qhݻ~ ]ו1佹cW3ݣ;_~ ||̈́3mñ0&M.Ɠ7Nsh]~ ]tSK|!1k]CW_C{eW P/B*>%A1bd~zқه3,7/xW.ܮ#륛e, ;z λsG_eoR-CFŵo$OB$>.ڒLnhu,ok*$vOGc%y~Bo]w8-ZGH?'Iչ| JOƢz^J'-p)"6*Uh'*DļSx?<ͨv2t֋<8wZ i\\MK%ͧ}Ws5hsX\y[?èѳ\w$#!D|>^o͇a뻏3+c#7#(Jdnl|F=h \#>ݿǞ >y]ڻY~؂d~~#~G4E,&?"7mG -ohGz.~ qQS?n{|GٿwvfZ&_>O-8ȺwC8~ϡ 9w,jHW69ExgE! !A.k]FVm9U[o՞ao7 {xgFy7n^øE|zojquFJݹLCHQOpCBnӕbn\R_}ŹN>\ء ??%cEx_&^׋1v\3)7 k\g"s'wY¶,:/{ ?6o a>w1X߬]Gwl[8C{uS^w ﹇,`9T\s3n h[6"yDMǥFڶ1BQ,~mfUw~fQbQ@9\O{} Hͽʏkhr)) +B!,$B6YH!"lB!Dd!!BBB!aB!& !BMB!,$B6YH!"lB!Dd!!BBB!aB!a 9bMY{74O0Y]fZqGvqo Dy22uQ&1BP_)^." jF.9KBr//T繁}y3#(375Sߎa,>͵u.} YrrCd=+BS_&Dyٻz6\̅mҪCo8;仸MӁwsGEGק~i?`Ի  .73R͔$'8 ylbGOӋPFZEb.t0!2v3.Iv{̛u^͹/GNf] M'BYQ 0{ABTUq^64RdW_B.]z(^>wjfB.]}Mdž4 =2emn^aZoKsnègmhsA7ea~ n:u{%pOz wL|1..ݍsSH/ǽn\j]ǦOr㥽Сoɷ)f&s%ܵnҝoG}ͼӇwj^o;^Hq&zrK彵+sǏಾѥs7 OοO{-2٬9~w/гkw΃o'M=|^;}d :QN M|̈́W^=3~3].cߐ~ǬCݭ; SX~ +_i)ɶWEi7bc!IN'A'zY^竭iǚW-_,~y|[3a}o+w:v5;V: j]Ś#u~ƙZQ7Yh&;oW&\ʙQi+0jv2}+>c3Y}ޤn6aW|d^9ynEaT\49/nH_>˖s[xhV.y3eazfԏb杯t 7>̲ye V}O="ӬHf~/:1?pԬxθIn',x*곝wxe5G!~+~Nu.#1bīOaiwHb:3w鏗d9#/,D_۟/c=rK ~IKIfۙE]O@|DmO!ĩ (xDͳfqG̾}}. 6pUqvݻ]^m]1즶TWL=.2jRbi,-/W|kOX Guhצz>w?KM7rA x+SjDs}ELXDPk.HEbA\Ҡ".W,gwIlL"-g +b=Dע~͊]4F7xsA&OW5eMsy'_;?t?:WL'n+P!*9VxVYuVB\GrVf[L"Bg$NW\c. ./~ y[ IDAT]ϼÃ-w,II{<-_U䃤UẔ^jV S\Tm݆3dmF3\uuS2jF2~d]|k>$d%Ҩ\_UkđTH+)n$Ϻn<1T6 |>_[3$]n<8=&ǕWKg߁#Zq/|dOW/`aM9Hj\ jTq;ޖ.\}S؛iAB^o1Ʌ;5m*Vkz;8c|Vwr;ȅ&ZOդHُE`{TJAvz΂G 4e޽rɼ)Vv [7!|]^gYV^_ +TZgM(6/UoՄݨ'ᵙ+y8Wؓ~"+S9z7lI²9N";~YK!:k~9e`&nUpPZ7 e! aOĿ።rta*ww+oKr''q 5 ha=ܛ.(;͉8Nm1|D+fFϘʣWvga7I3 F1c <9v /%z3Ma43ɔ WžL|QI4z+ƟNYq&7<0Yϼnsxjl(>xnc{ƚ)!N˲iۦ*D՝ߢrX9aAޛ1X}ܛ?%N5cۼ˞y̱pSS!ĩyU{r>]!BrIBݬS7幍YAswN3%8IBBa+)% \\B!Dd!!BBB!aB!& !BMB!,$B6YH!"lB!D7[ !l%gYL])^A ;otnVh'8BBakTpB vMpT~ <!N5kS\X3"E@:Ԍ`]r0^_"DosLqaw,J /PGY@ *E^_ TQnkB//wlF ۶U q wߴ"wtUJ8g6gbMӁwsG?hzj i)fJqHcց<6nEH{T Eץ]fHgXԩ#Gb"i:2*=5dpFdvuS/y{gС$DE[uiCÂA7}%tҕQ{W3szuJn<6Ypt1)ks s2[:ܿ |lsF- ոmoѳS:_4oAVv%!{'Kd`t1&}–#9v9]{'WUq;="P0(HT@AD(Q6"4D@F*fw=w-I>o^uyޙC<{gGWQ8uZ1[w]runv;l$-˷o3YwM7S> >:jtcY̋sG.vc?%-|:}4iҮũ*I/.5qlj_Ʒ׋)wфIhЕO,U=xڮmӿIIw= xBsKhѭ'^=\첳z9뤃Nvքz^բw&l/y-X}dv_jk/_=|$AWN׽ݮZߜ$;T]}z3^[jV So$Hߍ3$% ֘ѠV6,cs.^ozx4t['7ݭoXߺ\_6s؝zծ|^3D;쵣zghoңOӵ1-yR}MfuƖ'^0iY\wO2uSK:ƻ3tӥGk1d?)sN=~z~JyRuf_DD{5OGk/HImk`tQU_`#tُ/XMν\ 6NM֑kOU4/k!uR68^jJݫm=BwӺY3gmן'MzGU9i=LY=h;@+g龇4/kEp;hY7>2W;5}rpQ>Ք)[k̙z#i 7wIg#Hk]5ojur=~_U7&T,'Sc^_e:rSQ#&OՉ[=2ZǜF$?V=]GwNn#ҽO.)饱_C*צ;o˯Aݺh'uOhҢ1ߺ3k~fзw:;mכPRMN[Ho9GIq_^t|513B_MLDp}?A7Sv?l~B4 ~Tzթ2̟Ԉ- Wq:,м f6I=d 7u&~pzRFn`Vd,GGoUב W?>|OcJT!j&id"-ZY^5~=&M"?.\=kzon1 hX}mtU6ʾVy ttwW#LJ+]cRT+p{(5 dqd}kŏNk״cx5EՏ{Gh;ji4k.R~zm:s4lwX1!Cs/oC4$>XxB<~56-ӻ[[c6p]Ϧ{Lﭔfk{hǡnۺIݝ3~>Kdo|P { =?IuonKKq~t=>k鱅ؽz%JԸ zeJ C'hun#]{nd;I=?]w{k=5oTZ9ւR_kg-~4k%% ?_תp2J3uSNOW/4k@ \/eaҸ^$k/eA*ys$PO+T?aom4]{ZH <ﱳM6%/knCI*רM?als#fb >u7(MIܑX%^Q?}}4ҐћkC/Q;Ro2*/{>824itNd뙹z[~o+Po˔G|N?h.Hv"%Fiy_^_UwGR$3eu{p sDC\j yMBui1nn(Iu޾{xs%=ʛ?vy7*~ZǍ6jTl8 %Y+te.$IM>OTC-i$ic/yQ͟y7O1w?ю6䍵R['wP ڲ`mGۢ.z~."ѣXЖEMDT߻?"?h4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#Hh4 F#A FOK,a@'[dgè65j=joNdR75j(BHtCHl-]ʝ lBAӨQ4d`FHtCj(X3 F#Hh4 Dh=z4U׏X k%K_6 =UZwuohJXC߀:*oG^ײeL) ׯ]g] 4ȦQDj(X3 F#Hhkc# Xו5_̵tКH/`ƭQkXc;=4Jzչzqr-[Yid׳MhǍZTE h$=zAX55fZ] {@=YQnidXIxuvC6g+ImիW/F'ihhK/Ko̞ 6Ц;juJUCV7/[KO:x]8oM…3c4]W^3czBl::5Ey5҆&vӳNKlƕ4D^ظ҆+I֔Zu6jH=[»RRף6giYku]H­/`T5V穹HG5Z`[Yk'fvѝ^yחui{|Mu#[@?0ۈсjz]oukDETZh?Z:R)/I{\t ZE+Kiy|7 \<5$ 7ޤM/"Idu~SWm|=*-iq+o=Hu;u1r0W =cë<0O{ァCqni8;{_tn6{ê.#)KRfJ_t<:$msթȺi9yB8UʆtJ4Ogԯ/N|vyԙyZ$c{Gt_TA#5̮IDuGi >|F&Mo~ m'5|5]ŭ hQCQ$mMI[VTC/;y[РN!%58 n5g[$K{NVq+WN֗>ƍ8_w^D [i&h┣i.kk>v{_W$(d=!i亩:wb%JȟIgMOW6h^7veݫ=p~cZ!4zT8tiq_Nq~wշֵ{-/Ͼ^ϟTz=v.+)=.eBml$rJZ3Q(.@%}4=LJ;tÓ >wE8P[|@m7o[+mu6j@>{N:;idw'%gԨ[S}![ S}5k{{lN)hR/Ѱ#u)XW3h ?Wh-7ּMf ?E Yo} 9JG~x)ǯW6WKU|!)o~_iU:7V\t9x]|ltf=cɻ7Nӝs>/KJi޼g򹚳xR~JyV]:\L3~Rj萢;GFڤ=]YQÆFؚ9J9#}ys=/-n孋^Xg ͺRqvu)귿sO;uҗӝg[x]W6aznxz}{,.URO_~n?=漵/$YuW"te]y7v]tyGo~)moٖTݦh¨m?)?n$\F%MN 6)*ɓO$qM?k~vf/kR4igŷW*Ijv[fƤIKzE/*))G|$ҰIhWŷEMK/hRy4O<*NRBP.ӑw$k sbxڋ BvZw[5xőWu/=L׿@+{ը'j[P~-$G-fe|yj<p ؏c5<<=J#Zkc"K;6-;#昪tH_TؗvA8/ϩr @c[Si-w({|h)+t1 k^L7뎋뎋ٜ?wc+:Ey{vvKۜ+@ n]c얖sTGFª.?ݟq)|;b_c7Z|a/}iqu,Ö푨_?|b_#ZrV(m;e8+f΃'r6 ZF}XyL{ {jB1h_8 l$ߴՠ7&9>ƶ C1QHJ8w/ mߖTkڮ+Rۻޡ56|쟑HKYYJj>;oSy޽T˿mF;v}ʻYR)!bR](B]Υ;%9)J3B9~]&ʝH\U(>]gVcU@^"g5vNmήC@ɋf8PF/Vi?.VR3wLQs6>ks..gNZStf#~<4:Ύ5~.)\̍U7nTq{^jy<97w㞏|c b=}Bqw]۹&@M>-gP<4N;ud#(ŝP^4Wc/~A/ss?nc΅U91:(}A 1f>رiڛŐnf6 ~^??ccB1_Z>-nHT}na\b%߾n^@w@jY]ǜbyMji" :w^7Z6nY~,`;-C1͟\l3y\瑶:C\F"QxQs!Zs1o.zq/ZuTϫrMʱ&o?nmiJy˾'[~>mi qs\o.oI6J;5Y(e v_4ژxI-z*GDZiBy,S?s!iqlisi㴘Բ͏ۆw\̏=SϹ?y#o]Mtv#bbJ彋W!KI-w;#ߙpw;s9kusގ;gC}o9[q~w&\6qۘwnDF"d;jJn]\q޽>'")U'j9 ^o.彼]m&vʇbýB1Xkqisq}w__*n,\mNBڔRԹ6 Oɋ[q_Xw@`Mj;R={,wnq=}6oV(@GߟPLj^|_[xɋX/f u?vssq|ykZ5XMj+n](~5prqP9IR6nKq;1V()X-kٹj$Dm6p6^Z(RfŽZ7.;%/xy j9c7ڇrt$,c]DލZ{gnn[UM_ۻ~¯7ǟ{;5PuE#(jk_˿p/1cwnw[$^mB-[ ŤxFIټc/ZA-?w ċ7b]֧}9\sRjfv khq;N^渦 ͯ۹=6156?{;#T•mR^Ĥh!4(uw'T{'\׸g흴6f]j;Zb6gv'?X5nDzv?} sIcNV.ZG4Hz Ucǿ(~I- F!11ܽ_9\̎j*j!ֺu\, %!sac *;UW7Dma^ЂZ ;#acMf?O[CP.M(@WR8nlX m,anVc ųb\F"Q"C\޿h{ jioÿ;jysٜ/TkN,i~w ؏gP,kKU.is ic{|۹;xIu p ^+qy7a?w/؏9P,MV>+])8+'λq(PۜͻcvlvxZz:_VNJ_DC?iUy7Ί۱ccV<tJ WKmlR.49?=.4gR'|Y9'bvC~C5v^M>N(g켒jUbjkֹ:ٸ۸PɪU֋NUfMۻgmm?{;⡘Ks ,LYDŽr^`ޱm>Tv_)c;ڛϭ IU[sظ݇bo1{|:juȫV=Ohc.v$^؏wBqZ~2F ]i5q HἭ>nFJK9c@Wz_J˥ť_}(fnl1*->13&l㵪vmW=@Gjbi zhvZՄyHjr'9HmB Bsd}\Wx5jqj8=8tٽ۽/uv&O]:b!{غТlk:[Su/Tgǡy1@wcb['e/޶Cع۱Ժ.\;OԶ.mAuRx{_sPLJK9Xd-niP< u(uv B1'TgǡytZ/|i mCB56fR8VIT[:A `R1xX{yQ;q`MT=5+ʅb!1)=coZ}j띴㪉bijruʻPfePm(&U㪭o#,18i*%@X5e5N1sl1֕:>-sګu\Z\s|15{/Y9y@窴ض'+DwY=vH<9V{W{wa1syǫ=ku<buZ\j\kuZ Vqsy:궀ulwyknu]@;yw^;5&,5tZ;q:Ě8vU hZĻqknM^BSm 4r 4 !,q`V7 HVo4 "ع9h2͜R$IENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-settings-programming.png000066400000000000000000001535671501654372000237660ustar00rootroot00000000000000PNG  IHDR_*? pHYs+ IDATx^wx&z(E` "("x ʵ(]()#90-3睳ɾ'B!B!B!B!B!B!B!B!B!a2RBaQ9C!dQRs$7Yh ER΁B9[=BY\ʅ:>xPT yuOvx !(UVCVcBQ=TU۩58VBL]+{ W feʚW!ġ e[jRZQTx&SESjBXQ󄪪o/vVTU<:޷FyBqFyյ~@7|75BTU@Ý'Pg[)[*)ϱP 5-!TUpn'PBQsl.E<<lBQB\85`9CUysl,q@s !Zny*9"=BbGr.ncnq<O!5]86cHhErۑ[Q@9&\!8S4U܎x("96c`po7|pN17rBBU3P^1SS q(Zf~S<Ԙ<h !(`ws9q 5@=&r텛ǘ11c9HU|BqUtt)n̾3f9LDŽ,FVE䛱`}'fN`B5O!P3?ܾ3'XcTv~D8{;3;qq邍8!*`9;LfN}9n?[UPo'<̼@c:13fBgnEҌ}p)hLg&P"V+Ʌ1[[[gn>DvBTH[ǘ[(/X?p Kep5)ϭ;{[[v6WRc")^uw+nݭsqkrbNB̓rR%ԹC̓so۱N9{b&3ŅpVx1U80t[?PsC KeP4ϩm;:clͶ6n}'#5A(E.bmtLcMNf۩廉R(CɁ܊f`s:q!B$ۘS))3oXl**C!b;mB;uNcfwJ^(9!Bʜ+Њ|f9sڛmm5(ϱBQSc ^Mtz))nuf̩濫rBV' NWa;̽bΥ\ۘ[ !Du;&w;Ŝ沵Wm3n`m7(3PCE,)f)-f빊cuNsm~*j!Ub9Ojo8⅂0bBƜ~*+fFI UW}/ wk{ma(7:p!cI$0cjw;>+*Gͽ[igƜ򎇬 I)N)f}(m\:fQ}LvPbf?p[|cNE\Z9phLCi{żn@ KE@ 67f6"3Uq37czܛmŌ뛂 !\`ͽ3ǝ+hp:i,P[+Nq@RQ'<᎛7q}  N@}s.p_(fr7 !DE VW}3vE©ﶙoUژt:Ɯ_mE3cfۉ۸[}"4SLPq)NJfwZxvmW*tcS\q5Jqp; 'W! 嚅4NSsӯ&K"`_I\T8}~6̸̸9g NN+D%P!6 bcPs馛?##z<%!~ά/ j"|%{JńYbBy{}99B!#%:::s7U[n)ΉpURWa۰a=v"5%G׫_BqP\K|%m(YOI_ =n)i4f=^)*z!?P8;Šb@Փis|E6d?Cƍj 'BⴘPjLP^̾,; 7?zz\㴹'T-(< ض}ӦMeݺudee񯿘6i.X`%WyM5Ci܉[S̩_isCN:{3f4ŏjqsrdS9 6dQvv6gGV8⅜e=۶믿XbÅn&F:CBHk׮ۛC^ڴicu=le+(٫+{rU\a3f89ł6B/EWm3M_D\Pqaќwy0~!coBZ[lesᅢeѴqcjN:$3˖}C.SP5ңGҜVZj? :u4F"SPP{=`Οy&>X&3ڪKP̌Z܌)z)VTlnڌ}~)U 7Ay<]'Ť#n]ޜ;7xn͛i|x9r-˖,޽qebvZV^mzj֮]kϿ0w] j߾={/?H9"{uZ@uEqVܘcf_qW"Ƀ2OWTpP>~TQڵC?t˯݋n(74ns+^_ٳ)(*Uݧ7\Æ gرt܉'xMz|PPz.'жm[4/,asܳZ1\tE\8xp鱢 ӼOΝ;IJJqƥCi r)yn̏ E%z}1XuQNmor4uEAqXs_ݻvSTT-z7?X?W_{;v_@tt4uXnPXNJJdƍ|ުݧ7-[X~EEEYGLL Z&??_?\@:uɓ+dЦM}!'nW=fJ Cfm275=GyEW$*Js:iNm=ό'T  KƍҠAlRn]5j$kC61< y{R75}rWpWvXrrrHJLdȥChժ#<=u*O?˹K۷/GߢXP'ɦMs]sI]K7H^Mƍs &DU t#7WڠjHFW!B*sO>P[`"]+a?@Fz:FfFPW-U;9%ŋ[Bhn}+_t}6Zl>llV9jѡ>Aځ&lPl"/:saذa~ԪU??Zjq3lذpVB32J߾mo]cT!Kd֠@@cUeӸ'AM7 RUvmz?еkW ?xڶm˖G qx)煙/?Р~CƏOeCCEq-1?`P.i+딊y({EBQǹqw[HEyN S}sj1Wm*i0vTI;n>$L;#>>ԩCTT111|x B!E[6?ȥ-"E_d =Zjq}LŃB) 6^Zf[)*fL_p= tԉܼNTs frZ,8Z7q<>~P683D(y"ͽIq!̅m)d{[TU5h[[{3洁#B!BTSTm5됙1.9PN'Xb1G!eiT:sٯ2s!*D;=Az\- !gN39ŪT0O$@{e mۤCQQ9,*Y fij2/lu=|$xR')9D$tQQQ$$ēeɉz)O_1 '@߫v4â|W9IfXTlTpnj1*BUTTTzRn]cTnuŬ?^-¥u*MEIq1cf_ j뛊E`ZgA$GŐו- f[g>8L䄬H<>^y:v5.e=' 䧟VfXI^Wj4uŬEnc 5Opy x 1%m!Ĭ5fL߻LtUptNw{̘wB!acb:AS DJ+n'_'IB!D$W1Ό}=ļO2OfxP!;XF̅D'-<*f'dݞP!"jW"SPV pP(;5cB!D^{̺ &JS pN[f,e}!!6ico*r;CBg uL1ӹMHzNc rہgg0WxbH߂zҴv(7cˏ+kߓ6uBHC^oΤ@|5nXj[wEXET$ݝoU'իs`|e߀j|OKrey}-痨t?"@Ѵ>u(n¬#N}Eٿ,hU8P%И1Xsyi3t26v_ɂf1aN?^ o-=iS w2g#ٴ>%2MleI7=J|ګoNkhF&/mN⁳|Yx< ߄y\ܹGHG3,Dx̺b~yʳFڅDc tƜf eESDo/ŸS}+~o[2R9\&?ϗ=q Fd=c8[eoϿg7i#⣁ƴҸtܗ+6KE'#:ͰqTEIs2C;gFp7~+O'흹,>L3[&&\DŽ'[*@GapD-ʣ IDAT3YoGߴ:tXD7e噬ǟ;knQ^>6b.aּ{93+כđ}rms\ضt^;ݷԟgӈ49fkؒKQ|[}OmM87̑?ջ7f ۘti4o5,g}Ѝw_[p 'o,g}F)b1Ř5R[5O>uoz;_>1`)17KE6g!n'*-_~5tH㍻_؉/a͎\c\x\"o? Ef!$ഫpQhֽ.n.൹۶<؆w\yN{=6_?҄,Mq}hmnf/y=uhx|2p`̩^B!Иpr˥*|"6W'-^%!gW.D%5c[kݹo[.{1G-P.ynIei7YĻDܐ4nZ>y9V#t ٷo^z)/3ity\9+8w[ޙ vSBֿ?wM6=0SmcC[i|3Ggt'҈IO~滬JE-0n|nPZ^w?KOx7Nьy8&'uLai (X.&y]KQQlr> ~Sm6ͻO1 rڋYn[ně4ۮ3pWp:a3}F?3n^>FXz3b7-<4:W3X#پn<8w\b3ӟSkxftЌM|lɱ%Ett1GY?7f8.OdƢ쟧ӾSx=L+g^b:֞g6lrXoŤ+=3'[?tflϽ|=={?Œ3?;,"uN$PqE-"?l$ܓ,iXGQ^ Ns:kH9g>!Gd=qIyXF?Oo[fHVcN4{ə]_vuw,ږAH^ԣIX,v mVSN=X2Wo& ]Wl&ǮMMIgߧ]Bn{{m{~=K]T@֎ KX#Gt~4X9 h#I__[]ȈC| װ79M:h,bh֣+-2vÏ)}.Vu8LNq;"\w^4/|ۀu)6{V}oNn $w:=Ry% Ѵ?c 풼`Ҳ 4J.?x5aj.{=5lA8f/\Э1DEd߳4PۂΜxڙmѨQm6ڃV -H8ę_Xy] 6n 7ܪDy:9nc*n'ԌW"{2s@ 6Y[{`mz"o>ϰ\oiɗq<vy+=dǦZk%ld{ɼ+F_g!+.u2ؓï/U_v/m#y03>In{-.3ڐP!HF͜aTSZ4KO#3cҠ 2|ebo@μu(gO #?- WbIN6<<O>?<,a@Բ Y(dxi{lL!FiDcy־9F9H޿IϷ!J&JߛvN_fhn<”^)nTL<][?ؑgC+iեwD4mc|sW3̘6^_eذVO/r9 U"SHY&.{!1)1yq_njkރG`p|8~1OmxHLIH`ޤtK3}!l^Sy)sJu' '*ٜ`"PqSq dӯxaE\5+?$8,(#cv?5GbhѪ>QMqJu,wҋ^tJSw}iX+*YNj_>yv؅Ylٚ{N&_o&k:a!a (* pCӪo`3c[NRg_]Me o/ނh(ɹVv6~͆j=<~|>_/Yyы njn8 g~|:$$Xl߸}m[$wq[6s_;}#ۑJlJ-:Kxom(Lw]ic,Af$YEVP7VZ>,'x|{i቎%Q߁z^ >9bmؙSzf:<g]x3^gb=79j"fnd/G۸8,+Fo<0r:{Cӎgs?iYM7\z˒HMHEYhےn澛k3nfeisXF:Ɏ7U#q3c?EN WMw:0t4ogNe(: {̍cܩ6+BjҲ7GAYû= Rڜˆ!G5>lir31ny%v=m$7:M[k˷CL\:]~%O"=Hq-Ǎ#U";SqĨ5jq j{֏*髽~L>>#s4Bnz3$Pnn' W1ݷhBOԯ(O+spQGiD 4|@AFHkVmm̧풘m1}sL 5VF uU\x+Cռ`:]-.ۡ:֗Pܔ u(Ns+;>㶒]dmBs'rϡ\ԑprk꺐橈ۖOe?TO"k'[ U:"yB7Uk*V꾐Uԉ4W!Ǭ'US*jJWPOBQԸ:WJ#BF״0'@!a鐩[BBwH<1B!I\Y^JB/mE?=Y{̐U̐"eQN7jDRR9)TXnՅD Cn'a!Kϕ Q1rrrټy@$rՕP|kC!K||͚5cBB!G^^ABB!B!"& !BDLB!,$B1YH!"b2l޲\sH!D%,:4mڔdsXTYHT IV-3BT\6o J"omT[fB(>>fMeVsHTYHT\YD!D5'o1W"YH!"bB!Dd!!BBB!B!"& !/\Ãky"6icoBT&TB[0˼Usy|<׌n¸R/"*).;/dv?ܹ\s{&׌2Ys ˙t_$3;n-Ƕ3|~Rݙ辍^Ö\2hC~jC] _N mVO`8IwFcMA7}Rnգ73`H>E;pZcOcV!/IBw};Z+'RZzO/f,Xݼ֏,aGS k|c=E_-W#u=O~_,.:sb7\[?䉷Yu60{,m:7>?s}&NXJsOy}l3y-sFӤ,rs෢Χ^{c^2y?d܈\2hS_?@XyKi1n6~ʳy[x-X—sSgTfn,T&|*W.d?OhOYH"~v ݺēqЭNhL.hHqm{Х~;vp5_~S@+.}m`:FM7 Y"הZ4ōÎeG踒P}/m]og_i3<1a 7tdnԏ+9gpt?˗k ѷ1xo3ay0IǞE ztE.vN_nJQ4u2튶-C`"mUˊ]4#E֙lݶO@YQ04ڻ m!=>!Dh !Esyux|$+nߏ_W%ce6{IurVR}ꕾ;7ưk؀4W3bՔԗFf|}OFԷ3H𑞖U=O(M?`NY/wɲ=x kGJJ]OB"6<^!ykCY$뎾ϯW1jɣ<0d( 4z`7u9Qyt275ftl0И<0%)SFr֣YuG|iY{sci\^ru<}Գf[8RN͍!> o9M硣8gWHM~ML! b*Cжh(6J6U?>#sTBr*w; `?'DCVMgDkClI]͡2T^N!W)IcdF_oq[]S̶)zSBQ!?!1mz_6.b]t|qP|BT!B淹rǸdxb܉3P|BT!YH!mЧ2Ԍ*'D6B1YH!"bB!Dd!!BBB!B!"& !BDLB!,$B1YH!"bB!Dd!!B?BKz͔ku )^nPZ6d!!()blĄxl/bxK9,ABBgf8QTȋjo!^BT[a| Q|}.g溚SV=ɀOS9.ڷV/^+E~ !j@xih|g¥}󋂲JQĆ% -[ZeGƓn !j(YHQ+oY{!aq(0;3iwodsOBJ>l)jc9Y'F&-/6A=:_tؓWǘ& /Ya74~%&\Jߘ9A>ؔF̵z1x~2&35@Sᾱr}ۗ7A"-ϻYw$Mhs9AGOo'< |6ZN3JZK;(mJHi\pk&w,|~6/aSgQۖ`=Kvg26,>v1e H]iHLI-WI#2r\q!D%W$D=~/b Kf]Ix裝c3wBZ?~M傌v+ V8c[_}]=njұcn[\, .jRyu:wW¯Wx|XG/cȃeKa)z3|kC^k/]CK+o/Ͽ೗c6|0kŷ{fyԑ&mgziQ.>t7 Iڭ< .e*%b4И`fgYr圐O\|-=3||a DztC "b=CGt~D7> |b&}~mu!#6"m3_È^,ö70ń,"8[ ,e?3IN&4^?!_TZ7l%է^[~yێ}x9sb;*ؕŷ7jJ_!^-5#4c|NZV kb4WrgPHW*;I'/:gJ=/$· *.}~27^CjBʭ(]8hS\YdWK; &6GP*95qI1.T6]<)$]{mT%e}gQ}¿b[/oR2HJn—E] oJ]Rpnv IDATGүN~68'6-ۯ=@=%oX$$bƞEV )~lzbEH^ Qqdzӓo=Ŭ.Ϟ7K׺M3w&6{7~9==,kCQ:?> eJMmA%,Zt^Iv`q,?,Ʀ(kץo뾜~~[lظ _q ʲYK^Si Zo^nsgB?-}[s :r%k6~r#XHx6qYŏٟ=x׳/Ǯ}V߲o9ెJ-&hMe!!NH̢^x6YyZf/mӮW;͏ dP.=zykgͤ(.̹-?S?7\o;},c0}{Y8^|'8M9>s0bQGst~vG J!ٱK13hun#8gnawn񼞖\NyOf:M:piۨ ({-w&޺&53zPqOx聭Lt)Ɛzdvk74aIHNam!ġ˼)-z[*qh[#BGlz[~|F枩hUtv>)O;RrpVǟ32r,Sj9a OgU`6?Nj:wdJINE%(4z[_D񛐪vIL],4۶˦}sL 5VFBD?!(·ע`:%R|0 QTy  uBq(m1n+E6&t:w" xt{LcOgDy,Opq!!D # QX$41㇫ >޶ }z.CxRkY8uE!BDLB!,$B1YH!"bB!Dd!!BBB!B!"& !BDL\m_3|B^O踽CmRk򇸅Օ,$2}XaPOO5\B '7 y^-=KBj+o}!DdlFkbc 6^ px !L<|N$+ ̱U|fj`-.:c)== n}؀zұIt:]OϹ<+?.9W>v,CrrnLο>Tg2;3OVF9$3z$plWMt9%pgWrӬz- X'=p=9o}]ZhgsZ$l(3W7ş&1~d8?&gDeogIlXg1;3iwbq$#CQU+b,o}w_t$++Ӈ+hW e쟅l]0dǢ 3\.y' ߎ{*O?&,]+yP8tNp;,p7 2}O3r5L)i(OS撻bu69ѻŌzn9;]~we.'isEW'TӍ7poJΥc_ǟuQ$W9D26苈&-dqf:uFſIN~ϏXťҖH=s"?ycO$ڿgiѼ|4..˘?gCQo/Ͽ೗c㫏˺YzɱP26zw?[sn$u<ߊ z?BVsVPgŲY*3'mՇ3Z ftCց'DYBT:ߺOp)\;Oɜ{;_L nݨyx "&/?KmEѐEHKmEPKڊRUmZKRkKQ[_J=!۝ܸ"TC?'s朙̝;3s9w5(vei5#%gWv1 5ޯV`]|tv$gntV~4peS Q9+ x=_w C)єmq40*IltN[Hkoؑl*Y]l_;W`]_[1`{^ ʹCPk_`֨`B!}2FBv#o26@OMwsif7ElaLP=úqmk*`M?UDfj|Y^KKAvf\1`jj:}l5pF2A!1" z>N8g튂 z\ :1-Y%U퉏!P+ӗ! AZe|3#Jʴzk"ވ얹|8rW,}O{ˢ*s_2Z*$%(f}˥2:Oc{~*f~'dpSܸSF ݸM o7YA;熫O=gWH\FK;4t1XTBJ*abY-&JfBm\欨ޠL̮hoZ*`倛ZC.nUZ;| gU eمA6R!m9}8≻c+1s;{ֆrKdi.;- 6}ET#:GD*蚎iWK94\b5\k˽Z16pn"hFҵ3zɱĦRFI )zu'[Vxi;KGCʕ,&uSbW NW }ڣMЍwKkhrO!xְ-U L(W*H!3._Zx6;Avc(R)Ԛ+y5x r;V˓f-ԇλZ8|16yw>Ep)L&ݙ1E#@-ϫJڴ4456s+~*Uud@|6p(CV;B{C:MH,n4yZvw?,.Ya\o&]M `K2ToO<\z|8>_Ght `" &}5/xv>Sܖϛ-'Za^ fUY4dt!V)ߔ.7<:r(<Z.ǁ橜O6w6LR5q: y}V:ƳaˬlL֩c+g!@"94)_3K3d 2fX9L&i<.!/}!ȮI}~܂xS!rnBL;̌)y +U Yf !2H!"?.+ !"$B!DI !B|@B!&B!M !BB!7 $BoH!"ɖBGBxYbе8;9Qjlmm-O $$<,nnnx6l`Αh?Kڵ,OB<ظ8*U `0Pbb,O $Nx][_dvL؟j5!D%ȃT~֞mr!&=(wG d@ӡeB{Ps'8}bc_/~GmtiIgXA0>Mmk=s9BaF qU6;CQy[Y+y`TJGKc6Uc]`ȱ*5Cٽq/.cȧ۹uO hX>z,;ò;mw'!$˷:p[B4>d]h%}Ϊ[ eζu!.6cn'o06 e V]_ll"l i'9sڕَC[%L'?\d-9dWbvJx! !  $}ot.Z1~|wUhv 牻5(vei5#%gWv1 5ޯV`]|tv$gntV~4peS Q9+ x=_w C)єmq40Na@*;QF%zKaM&ѣ:nx& `EH$Vi׭>nV`]1(Ӭ m@uw-_ɥ5>(4!JuGl<݉:2BG qv#o26@OMwm,g7Ela߫qcaݸ65MR3s ,x;Wq.jv"R5R5Ј>C8q#ՠMW=kU'g%PpqsA!^"ŀò5,K鬝҈ yEeo`&ݠ^"z֎朹b0(8:;C5ci1D%Pn膛B"wXÉ>_R'߿1kI}?U_;q&%ܭ K)n)n\↷ GҠs',1J.uICc>Ae(A+暕bѡdF(N.ئ~A*p:75($^AkܸqﱾD^ui9k̋\UpqV*g~39Ĵ#!tm\ڱ}eBqȘJx9ȝ=kCR 4… ;"Pő#"tMG洫zfw.y5 N@GX8o7w4@#w=XbS\\$HGd+c#H!r~)l+kkXRKOS-ȟrJ"1:%V[BNeYנi+BX˹:h\3, !DΤEBjTo7MAlҚWG~ /nkUJ(؛?ӔFNe}ނ3m[n_'YFǁ}V_:X{J ˭i4h}&LV_.A^woYP!rӭ;'9miyr2j `0ҦI%ڔʜMtظёGir=v0O(p2*._@vCtK!x[f XCl2+SuXfigNF "m>ot:LiY:cWDz.y2$˲y]B I8;'$f|'=#߯pڼg''y11he)6Y!3-J(3g\d bKeoMy;TZyB;gąO<*&7a,fgSBR!"H!"$B!DI !B|@B!&B!M !BB!7 $BodK!#III!<,qqzDe)"⑄ φ  ?+)']BGGD *U@l\e)#x’i^GVq-szǫoN/3HzB'jHXɥ?O2[G&OytM{ `0+DQpIƿ r$OęX;*ɔ la0oٕψbӎhZ%E]PxΖ(b2wVZ5% \{PY:Ά zk0լ-s≑@_0o:LܑrR@ .5hP2 U|}Gf@ мE@%o_˥BDLO:Ê io`F/^˔?ג 6;] q@wF,$=s:}kA7;LkП7aH d;th =a=MhX? Z}*9$ ķiKڿ!iO2,:ɯWD$g\憓z~ cȝS|%ڼ)GܺГ_;~hoaoGܡ ͚6ŷ@c3&FojZud܎7 ū e}{;~Mn֞T|./WI\miğ6Ogi,D: ³Aw>Y6e/6̓FH9Ê11~ 3%Ke6w卶44)'ѩ3-dxzҴ:ǷC:lRM~vmCϏ7prtjǯa/(ttN.v4ǧY;ߖ/Y!,H Q $sdhlvkTDs2r`V\.`KX(&œ);/яٳs5|#9 gEij:GϪ[h#p>i;axKF|}ZǖYw[M7!" _,炩2g{!!;fӵSP#f]m^_v| S2>diP˨dZBB-/!]/_Ŗ_qGeWA,x9uC>έå] Mm0Cُ *ԧęO84 a~O*fwfa;W0oLf?ə[Ӯ̔ Zڟj VI sZ%YtBV(VlɮUC(wp:wģc Vfv,瓆'Y!2#(Ѯ~b|ְmq<1{"Gr(O_TwTޝ[ٷ]AEŹ sٲ-ޯR#U_Kku;ޭ:^V9_nݫTBСv-~Ax 4@\m؆n)ܸCTU-[S|{fFz*;'^q aoApض\h2TJEUWkl(bDOڅl=yn ]YZ H}ٕѴVUM PlԻ2IWH0e <_8ΤF*t _qCӸ|wxRPݨbMR^ϑM=ݝ.[B6 (THZ&WMkՈHtp(d?EZ]y;Ŏ=hklY{X3 S} !HCT nj]碮fo+U#UA&*>/FxT: IDAT{286i"2*_Ƕei5cC+nkYˀKFv_xg=pu076%DulYEZ"Q1s#oB}UԴ(.kXʑ1:ݪQw=lՍf'VЍh:d4sG&>z+y3X˯Vuү`&ݠ^")8*ELU\KUv3YP]j+]еDc)Zm)*\eO߳L;TB{H Q(N8q+x ɉ$eSfR])wnr5Y0u?1F+EhҦ Sgmt4vk!*+,(qqRq  #g͸̙5 hGp2@F= n$m2vNqޮ $Au=Mq* ha;k~NȮ^cPkOEщY7֛ͷbe~RX-[1wDbYb46]–) 9 y-m !{4mbEk9w[-+pz6]ٳAK O,KgIP+Bᛩ_?7l, Աó?kt 逞Å?&.:! Ћ߰L/͞F&Чzl o 6?Oi@:1<f ޘ2%%}F%ڴmhT-)KHwVIv{&>4nt枱|_sb4ŀ9cEol^LKdΊ#zQrZa/I-K9@fڍc[P4܌QOK\tƻ (4jL0뽆t؉ҹ{/zY@'v*ĶYл%+ ke6JSz6ŧY mСvB.Sܖϛ-'ӭTa^!W5K[M X2'yS)]8&6n&ytQy6\|S\%mehU]Xm#Kk,ߍ72enڽFCuٰeV6ruԱʕ tYMYژN'O֔6evBy=s3c0-\ۻ5Hsmg''y1]NNYB , R2s(E)ָTfľu\=*; v= Q`pj,'f S _eo8w"-[MWלo-Ƴ8./glY{zٙf>ug 5-iKvrM3p'G|}_ez'۪;C!5."m 8dkx}Zl~dֹaa/cK3FdWbvJxμ5˷:p[B4>d5 HL~Ξ̡_?&MW9Z}V@[N/ɄS/O 8_2W5ãD6nZրNL̻B^RdȼH#tj:1˹ƆQbe [[Q^-'}L8QK?l ƽDF ϿgT-{iޚUɤr`.ʍ\LȮ_<ê"o杄.,>f1ĦOOlY?Ύ3, #,lۗ~ɾ}DŨ6 1Y-Al͜eҰ<~(۸9A>U9k?1:Hz]հ0%o%=m8}q5,o/$dlRw/V^hg?mceI"Юq(1h að҄u)vثvmW<[/K?MHh2~RQzwy!gntV~4pkf˶x|{JmgT}/l8x7,{S[wvRQ UCzDE>; l8qѩ=Z;0^M־0ؑ>ŰAšKڼhO|]_=V1ڨ#-ݏpbfps-ח`LrZUKm}Q:xh٦CUܠәvg8Pͻ.mj8`[ /VdUݯmk+PQ r L7[sҒH17.7Of+r'uTJEUWkl(bDi T*vNp8F9J`oJ؀b[ޕIBo'cJѼs`&bJp@qt-IŹىbJHtТϾ>;ŋ#^ MױmkZ͘Њ`\ve)mZWK;\=^FlL{1q\P~`5l@#*2CV1ŊjI)}1T4?> jO6jZXT%/>'7C*"ɊFT h܇uMY0/bb9h5ְ~{hSŕd dTN8pV:<=HP+m;IǨz(LMA23Ui?t^e쑼?0 ~rtva/ AD:Ը ?$`{dZ%i]e!4C=P1rь͜Yxـv;z7ۚiYr(8A"Z*SK(C"3ǺMXZAʟ95MkW,cF8B~1ӦA!jʁsf*::9}>*~Xvx'qZnXN;_|oV62474|g*V>']Ы=? Z }%pkk^cbSj>#~-f+6Mkޖ~k;|EمAdФ%hj=|jf1Tc"z b`Mol^LKZ3|ɅIhDPɡ}8 QpSܖϛ-'ӕ0knҦIV)ߔ.7<:r(<Z.ǁ\-WQwz>/ >NYqvy 6w6LR5q: FO9#gt[:΋vG:ƳaˬlL֩c+g!@"94)_3K3d8F,b^e21O[uY6ygN9q%#@z4G_ᢵy;[5Yv1z:boѮZfz5N{[Y9u`4L?.J(3g\d bKeoM~?-eקa)żxkB̦׻5 1T23N3TZ*ße,HOIJ>* :]I[|+ij"wNr[fZn>o,3M]ҵ!HX-L,g]Q!OB!7 $BoH!"$B!DI Qr~ Bܓ9HCILL\, KLLrxLG\J"JKY}BlID\TRY1@ ſ/$-B)=J2[<&H\ !Y$c$BoH!"$B!DI !B|@B!&B!M !B?Ӏ9~xT^ 8@c<- 8nE5/$'f((9E=yJm/I9ܒ+g;|+U%V(D^}9 CbHNM%)"'¯'Sj5m;%4Uh\?^%*)cJ4g~YϾ;fib]Ma-nsr߰ӈr_eF:ۍrlj0x1w\r%o !?!CNLƑs2Tn$)RjF3j}:";VU@ewXn/7F<GJUѱ KhL̛F+\:߼Cc70FPs.IzMD8ud2#eX0Zd!5ϩ\nLӖUDa^ fUY4Y*s27҅cbfGGgC8<⩓-WQwzFMQCl2+SuXfigNF "m>ot:fi=sr^e21O[uY6rB܇N9q%1Q~%慜3!skCq_WB9s"Sk\*{3hb_ $;,3 !B< $BoH!"$B!DI !B|@B!&B!M !BB!7 $BoH!"$B!Dɏv !$%%škevUɉU`kkk-xH !ȓ𳸹ٰ2;GF^ <,kײB<kC'qqTX!A`R Yf !H0ek31eV$2Yvy =d˜''ϟJK}v¯ܶ,Tz2ﻮiD ҵocYN;x.㻞Esj o AE'li^R`޲+MnV pHd_p,uYnﳌ+(ua*}ᄐ\ dwA|?EQP v8z0g=q֣A>s IDATϓ,w!xSؖQ\jѤ= `9f~7AJ~bAEt*֮bcskŹ-#[=7b頾|hE!k8r7F򷣙qohܹ/.?!.OR+~0ct ۟g~oweS_z ʞqtNU g.Ƞmij# h6j)&Ax qyIl"!k6 eW^&Ԝ>&NXŘh_,9y;V—O6-mEp,Δwb9c@`|}dYtp_ E,l.׈=ߠ`F.<- H>wgd~eZaw&2۾Xt2XL×h^)25&R3%lwKXI͟GumeaR./w=7ҰNom<mڒo~AHDZ_ewtȼCIfAB< $ ?XndwȷbK6`r-fԅGZefmQ: !+_}+dת!;8;\hW6f;f!l[3>fHhz>kpsp+sz!t`[ 2S1btnmwDyu.[Cb[ޟճ[Q?8̡{@Ky*~\P|Mr`V\.`KX(&œ%[ᣎqm1~YϪa?3e%:gZU'qL̋$p:Bwf|c ZĤ=ezö!"-Bdž_ nTC w +5iLk\ɡL cu1քy$Nnqbi\@f]-nuka 7MgUvfxզqI4p1*+j({aG;YԻbƠ̌5 *E].DVFCnV(W Z7oan4^ZUfs;?A60r_BHx# NˊQ4Tiqdg0yb!V7FESgW7ltfռNfz$_*p:75($^AkG6E*ᛶq/ 췀^'WE}kْkʤ[s5u¸bX޿bAc@N ,=˲fhM!%xBTRSSIɜҲ>?jg[Fʐ#ҥ9c:tz=0aeLٻ(4H4Bo "J&**P@JQDybG" JED!3KnnfKBB8_>{Ϲ3;ClS`-0E|4-q R9ws]ĵI}1kRmHw-3gʅS3 3} /xHk%|ίK=T.,)ngO:A]v*MI zLN? yO2 ,T$3AL!yrdiMScNOȅ}h]߉+g;4g<;ySL?T\fk<p */^'9-nbC(*vRpz={4Jpa?/|Ѕǧ$1eY+k]1ۉ xBpR: 'Э^Yڍ3ӳzesY1K ;5&c4jju~NO2g9\ˇ6&?qM v2~LMcC6Hhؓ]&OB|u4WL7S;]@vkP7CVy/s4TaF7kjsYAljtox<,]mZ2SB ۶4kzAQ#3SlLP . m$h7>m.^o`c9BMqf;x=NѮh3%(!䊦"(ccΠvgą%BDDDȟB"om!"ߤB!DI!!B|BB!&B!M !BB!7)$BoTBHI!1iiY\ExD81DF6B(RH!:dccO 8z, @ Q[B]N< 6&̔%Bҥȇp̰%B!7)$BoRH!D%&&h8'I!!yȦͿi/RLB-11-[r%eV.>111H!rEB!U8pٲe\4)$D̋}̺ 3Sd17\M9~-)怳E#vG̜(fH1!&;yQgXs߽6d~ r2fįƳd2hNN_͘VMi8ki|eGz  t0Y-4)4-i33~=˂a{_irޒg'ړ_6 p.em\Em۷cY% 3#(Wѩ+xI@go&u6 &<<!չ߆~{;ҺmG:+?pZ%3EƏo<闬^9Sҽ^i^EpQ|U*Fq8@BN sU{?ʐxqЋu}C!U\U?ЦR8եDNi4(wY\͎a6__`kٜP܁~mV~nt#D}fv.g˸f$t ӗg߫ 'grO_ 훵+s5OکBELpa9ܸ-WK tΠƕ*]U͑U roTWqruO]&ՒuMPBs|Ĺ]e-j7eN9|%>ƻMHe_CpcQ۹"^G_8G,<)87mea_t$ԨNys0e*P:{B%>jC68/#8"6ܩB!XڝXIr C91Ŕ~Cu[[6>?L.Kq,!7{P.<֍j}cwDnwn\ !DRHU{3?+׹_m[jc']2)9zi20Q-MHxF~wW ~3wF2|6y2òf%j}? _0uSb5*oe'~ܯ*b i+M?b]\+ڜN'zfI 8o w!uW_8],~35FX/ĿY?o C|I-s~Aql ~ljeƧ\-KʟٓeN\ķA??`+gOw~ޓU#pP,b ~'"116ȯsYU;9~\xru@\t t?sl7͸Z7Rv[=ƉȘ2%1MdK~D.<ύi 8}SLgݜWEȶm~q=͚^arP.oL=H20z[-3zɣo{c겗ٶ},7sJ|B _\!W4"Hش.b)"9O !ȃSF\!B<Bl !BB\Kr;QI!!(<<43,HKK'UB@ !D@1=DlL4a21reG6B(RH!^GIO+'6&w QH!!JddSB( !BB!7)$BoRH!"ߤB!DI!!B|BB!&B!MJ#i6S6Q: qCqǫ(BeTJ]<ԠA^L3K*˷b Qt 8|UB."вR(y CqS(yYD'؊Lq6۽KbceyL6B"O xx`wj֌&-,]43}d# W`oELxT >1L !JiD!~O]ZKtx8Q5mSj{tjׁv1zfgc>`̠^mۉ3sݡSW<~˫oKhڪ+&eq7}>Anz2r8 fл_o/{PߕdI;sAf]OAH:z̴Qq<詸YD\Qo߂֟zw#n1q!{)7p%Q>u^$.WJ}텔uCD\Y;R!>Wrױhйq%uvUKk[|E<=GYFxvٕ44 Cy||9ݧ`\nifRDqBp9C&0{\^0<p8m=w}δ؝NGHMn9kl7)d=LbTV.6/Uja0aL|B}aTS!>;W_+wAxZs t4X"WQޘ[8SqE=. D[~W ۪]ʫ~Ǧ 7ҼYS3˺j(<MۤL~Uw?3yhN'Z~ˤ,9OgCI_X)L Q$lӬf*5F̔O1Cd ӻx Uo?Gޘim͜l,<>%Q\u,#-i+ ^N("8f87;%N7g WӲekڏς{3m !U͖}?gH Z(W/>ET[jXir%Tb%jKhKoL2ėN9T!)$#"yt!Q]kz_orE2[X.ZH@HFthF){fRC\n/iO sBA-JȿԥeD_6 xm@:@n={3Iv}d9 E۶<`83B[^~3]Z]EV]0y-;ؿ zvŀ֓cY05sj?dxyHe qMڴmO:{ $-F;>ddn~3u ؓ6ЮP^ꡤEVSݰΚ'W3~thӆ=/w'̜7Ѿyk-:{j؁6{s8 nᣙ=_<Ƣ#i|h[XHv|_:X9FEiݡ.!D#pr^k:OɪI6FJXG1eS𩡇ßqKW,áX2z<[Gۘd5|ojDcxo5<ł/CM3w44+fq nW /x>Kjqޙ![=g;oo g,y >d}ΪFƈwY+>^uO?{W_`c|| uY#e<9g+@1si.㹡niZÒ=Ε}Sau;)7gI}gGBkhWYr9+4<!I>5s&ClvSn?W,ڀw6'!*w_k+6͑U roTWqruOƳs9KlȠ!BxԪ6]OJ9_ӿi**`8M~ؿ\׻q㭍 K+o38qZ0*w۽ 1э<"GF_-70ڍTmiUħ)Cjy 40]Ϗ5jD,rOխ q !M?O2u{ߗwppz&KޱVF)F!aq~[;w*I )d=LbT93ɩ!apLXfCwҾu wqܶx&LJg߿D|nFgKn&aSpPzv!9IۓoO'++)D*nk4n0] <?m}#1y7'DS0\}5㉋j]'1,nchԟ_Gy^l%:yOPL% lK{1S!Aæ{^Vxo,Nd'k9fޝSlW}Ĝ'HUoiײZ%tWd&Mxh}Q>ޛ#3_;?Ρt QT_xW:ܠB8+‘y?⩿ 'Z>ۗ*ǍǑ1ewwKb*p\P)ؕ^Ha!(E…=Tǒ6i:7uxvY^ o<@n$nQdKpz}A?bB46KY錽,2OAT+k cPC(U>];4sktrzin<ϱ¹)<6E& B֠m|xsp Lz!Ajnz3o}~*qL\)!DEa# W#쳇8_SWq7,vhȺJn&"z_hⱩiƍ4o '.MۤL~PA:kԊxpyy5a!ۆwZ5mÏi 3wyFfʧؘ@2]<@*oi}IǽTߣmoL]?4۶EfN 6JBF:_ /J!ϜB(ֆ>y80r+Slf<@ ̡BqBBJ|vwN !D 'om!"ߤB!DI!![(\.gBIKK3B ii鄗r|ȇ-DsX1ф(\"Hb"1fZ)$EFf}GIO+pNlL4} QؤB%2B!B!M !BB!7)$BoRH!(>9H!QH"IN>aBDEEaQ@? IժUUJTi!8O=TZL"D!௿wq\BrjժD7_)$ QlL̩B!(3B!7)$BoRH!"ߤB!DI!!B|B"фB""#+$JN>Addi3,ȣʕ*gnU&X)%ʕ*)yv?#+lr,MJ)WH!ѧ !\&B!DI!!B|BB!&B!\,$lcB!N9=BB?BqƘsOJb!Q"OBQ%{$Jl'ę,qGRIɰ̴#2MRSke{n%#33L"Jthר;9+ %f:[  \ݰ6B̴#5;;a]ضMnEIOO߷{.j֪e Y=IU-fT֭,ED> il=jrIG^摼=kBtp;N!ȋ``r`{@'m9]A+`Q |;;X+W${9 qΒ'h ̙"+G40upUW^jZ1 f `6|:O?;8ۇk iQphn7;Nw:i+W$ +W~0232زg3|3r۝|23{˖| =wr۝8p,271{w) Ϯa,:if W0ഉJgS!ʩW'Ҍ !N9?L08Lf׏a썝к }:tdȃXaN;e]ňd?1$~kc6x }mZS|<h׶ ]֐#Gg'34ʑ_&|_s9HbLy=8;N(8DŽN"^2C2 9+Oh|kd-|:wLa{xc}rjl6ɛgYFPR~ j ɧsHKOcW}Cf<|9_26e~z}8/wwInGSL(oVԅDA>@_2~bB3;i9R<:n"!?e[2àQSu;N˝Op]4k֚ηMd^u2m#•-ZӦσۃYW271ML3wy+ic)3i>78ϏW@EYH9|W)n!w !d2nPJEF3,"R~W#y-[}=[<Ģa}J :ގؾ}7h@fM} \Ď955biǡ?g˭Y>!,۵nSƴ mR*?.kʻ=QUʧְꝛscc9ÏX55٩L[W/xuŌ}C\+׮ೇ+ȧ_yӓ4s-cV9a6'·scN=-EYH߃9pBPx?"gž]別̈*[G~wH00dq(GYg3wt,eIBÞ GIM ׿NOCξ⫘p@.ϗ[E7Н|^Uh:..wY.Z^mjө{ݣ~Eeʔ.EKnB!J'Ydr rY<ᡥ)_ԇ6ߠT "ѾI͑J?wN++[z!{#h~í\ 2x5i0y;#Ev:<^ qiI\Bcq/J瑖$6.aO].+0Im̥cG8N9ʔQEV1d^9~vOE0%K~ֶm?.+ڶ9k ^2 )hެ)7Bݺu ̝mUHI"U kډr$6&<{?qėMH'ʻRf.;{P<۫Hjy OdRIWG8b<=pj \+BB=X_Ły:f<6B\B~].[i vաy8e 'ʫ3?m}6.. ˊ1?`>MXj!z卧jwly˴%# 6͡`&;/7qD\>y`< 9}]9ܟW| ZXQw`!R}s;/d޿Npa=P>C{ JD^Q a|$n64/̵rw/*O>*~)EOȆcV̿ NK}q0ut<~ҬLOW%9q'L9[nh WrunKt|/LMydNz;h*2p ԫJ|L;4䅃Uj͈؝R?u֡ok߯"BoW_qU-ͰO1RJ_, mXmϢ ۡ =Z `c9 u=W__16EV!k_Bl_O5"h\%f ~B`χFm&p3|ufC1'˓($NsbA0OZ. } !?B"9~E1sa9\8Θ3UH.rN\_;M~^ ]Oqk>yB_t+PgM+o1jqs[!Wgt3DH̹1"3k[_)v }Aqc̾bT0OE-U[]w qϫ3 IQ|x=G+z[_m733&h]Hb@0cf?U .,T\a+OH>a8M:/m>8 MQ.*Z9[!ߊ%ÜO|-j6c?[!& :X'[!!2 J OOjz&§oaa~ߢ$ Dzz:a>{q&Xy17s@'H?i*֪myv!P/.u!5-3O,~i|ř3s@BaN־cj"*f!JVWN@wycj̸*>TXvڗWkvzf\WmkEc B`&9ssvzjv)WŀvN#JD}oqȢň;=N.PȯHl'33m=o5ҶOF< pik0 6hmUd7Bq&9Mh&)nӟ},Zo)f\0MVfNK߇yXPULϙmW|ŋDQ69'6Nmۙm:As7bw $>or`qk}u;]EUo^i+&őzӟG͘VSrNyW녀YD+ Ԃ3)Nq ]a6Γ7c|Y'Iϩ7cʅ~B !?P7&dSvoS-mƜ־n^!?/: 6=l.5닊ńY,9v̘mSL++~ Ħrѷ< Nz*"}]s"}ufqN1s !ę`>wkvnEMz[-z_=[ƢƩ,&ڏy~Z8EŝrgTQ6Γ:N9ȹj'Abrz_ҋ 9'wcnW݆ǛEm@\ޘGko_S@y!lz_='dI\;.! kNWI+^PlqUq6Y'MMf -n=&?oqX޾cß.oc~?ڊs+.Eפ?sdO͂@WTL_=8=z̫x0W EjS1}2}}jrwj͡ӯ"j~BGzB+ uC?꫘sZm!(Yq|n7s}5q]Ao15N2bfoj׊>FΈ` ;[XM}VqbUkuu* uBa{x6۩KŅۻFkǨƙŀ^Dqvw !D~TSLms[kng}ݜmr_}cxYXfqt[N ~bZ1)_Dݸ91q[[ZdS1s;drubu,oe~=,+PTnK[/nS1:3oMNqB&YqAr7ǫV961[EʛfL13v b{SV̓9s.Z wT 5C~o^`-m P7&sSL! dnRϽf[;1cN>ɫ fbxVT\_̜)vƜB&{rS'@cj{˻ꄫ"Bxǩͫ^үw**km}B&Yqz6fsh6B8+ xcj1}1obәygDQ6yok@5q)z`^yPo{c*&P~xc6[m7Uqsyi !Dqb>wVz)n/sL1'Qszlc+梮oL/ 1jj+X}Q6SНB&IMyLW:xjj;5lo[/sW9ڷZBEk=o%?!@m4i&w>N͜*J̸ڷYc-:3nQMΉKC1xdj^$ń>IW Ta{sjYPmEբi6;N! v3}39͵j닯X]X󊆞Sm}wt{hm5)St!aiS rSۨ-o\/rGާv8WV` !Dqa>>bZ_&w6Łc83_N}39ŝbu_]Hb<9+69'q3NYh4跡Ʃ*NmUtcU[_;~2V! B^&,ym}m1&p[֗` mj?&s)ㅢ fU_s[Ȟm-O2^h苊9܇[++.ׄ+bkq1Phq}Q޵s7@BQP@tk͘W13g.[q_>V1 /~ڪoΌ}SB@7>:Mfl8O梨80},F36fI^TW$ԝ5qybu v k;ƨo}+cf_! Cv̙qsLL?Xw cf_=6̘S0 99}CwA.\dSA]M}}u6?3}3bfߘ@2V! s/XNcqX}Q18z+NctEL:ܓ Ev!5F"Hm69F_;Ŝr8bBq&y6N1O̩o z^}1 })/;#T!a=m'*4}[:.[kЯN1}?>E̜iVwI^ !??q1Nyۈ1}W15S+z,ظcn[ j"_qib4׊9ꋿsZ%(noBw&?iӤkncNξ9-N@q9}mL]&kbTk=Sm=鷣 &͘b}}9ݼB4֔׼kEc̜sӯB9̜bs*3]H䜌_LL}{EqfN[[Ccq3ю6 a}g^B3$J37#ٛ"h[7zz,uG9k=e&Gg(jQ6^?~{Zo{]L_>Ze>lu^![Q]׺gճw?DoAݟim~za־{먖{=xŽj~sQյQt{}6Zosz! Y\ղ_S׍F{5G{=Q/Z֑^+9=pzzM4`3+VkMӜGy[ui>1崧w}#LWrz+6{{}i]Z}ק՚^/r"+w&E5x[guGML{۟vzWDkCjܺ5Ϟ=n]GUY Z]7:>WMT֙S)O-W߃g˨2_(c5k(c=w^zYD5ϞQMmwE0>+f\eSu5paguG&S}TfgΙk.1{E^Q^N+NfgSvN=ZG紧Gjo5]Ӌz}f&7v s5 e͘s_xQN~xgA9ּ֣3ק3sSD3wd^s@֜YIoeA<ӋzQrLA4{j.ؚkz>:3͞u'r+lHk,G5fqV>s:Wr {6g]֣_ sQ?)MV.ZhG5/G=Gg}lv6Wr?rPfy&[{Y&[G~VoFbgpfd;[{Y&[{Y&Glά-zTd^,=W#ռP|udC?[^n+ْ{fs{CE=X6{yګGfew {vս>e7gV]V\ԏjJ3ս~|ٝCyT#|!8k4_G43ڛŵKNUf,),Uw;=t2ZӽY\T`5o공]3Qx/|ߩ~ԋj(7[3=3w {&;WGL/y9fe/(j~;EY+gz@2,+&;s>cʙmElmz^V7^V7f_9s7zwzf}FvZcOv;߱s3eJ0V3Qfԟs#6vg笚k6gV=Wylv67c箝G=u|ٞ|=N ={w^CjvyY!sv;v_٩oiSoO!{NsO`W}UU{zw_}?3]=௾ow w w soy^w woOٞM8M~`m?/=?_KC!:Ɵn> {peNVIENDB`qdmr-0.12.3/doc/manual/gui/fig/qdmr-zones.png000066400000000000000000001202631501654372000207270ustar00rootroot00000000000000PNG  IHDRMOsBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation TimeMo 29 Nov 2021 11:10:08 CET7W IDATxyXT ("[ijjif-Zh2m=43r235r-KEPdfzgs{^ 0p?I%$yKrdJ )FYIWm$IR9OO5V}p5MeRRRb\￿WqI}ؒ3IVJg'$"ðx@2Lrrr YN{:!7Yhc*U)H̥K$Y,'Ţ$'$|v!2Io8 J+ˬ\atx<==If?kl% r%!SllkJI*nM{klNX"0\V5MYRRsPY,ܬikmbXUY,’ilm@AbbQQ'$)*Ŭ3If4eK>$PwGN%sfm.|EmG }_\M:swJ"ڪ[ٌ$n twUm]M(w{,[#ٻ귾W[W`u- ßo\ k' @ ϸt4 =b7ܻ-( m۱7kY4!=LfP [T܆ں%.~ud\K =W6$lU"-\N$5-qaQUzb좸o?$j<1I}%-*3I.ː}d{2NHf-ˊ<{RlHldzJ[VSqK:y,gIi9'յFVa}rAm;AtOY^_W-Aj3 uoT]J9A~ .eR1~çt-&HNuWҒn9[TŢ3II6Sa|\+歒j?%Xs肹yM=G&K-%%UEU)l(1.YPՒ8]MV/p*fڨna)EE_qgJRq<|ZOeIE$I<7L~I6=z{yuj۹zoUdս)'ԥKGuMuhާ._Ժ(d$jܩ'zfI}t7uV D'{_oud;qT]]Mlӊ5'(ЍmIȽ%n]ш=_I2Yࡳ4*'Oo/9ICII:57Q)YKrv[e+l`/fr!IJNƵl;XY4ӵ?S7dan/v͚4ɣH֙$"7ݗP3[=nLE&uN"%l`7)'DTh=SD?,gm8?Q-֡~X}*p)HI~dHvE]h2P7݃-mn]c715EY?dd;_?UH2>}ZG&GR 7ZY_?b7_HJZ[vMx%t=MfX=q@$Z KqY E5&;V{^]]Tp9 10=?~>uEvׅ=G_@v!٬#ou& {B!E&7-fmwթͮG;@v2yG>o@e,in{o&i{v( œ:d#~Baһ iU}D"Ɗh7}뢞> Lsƒ!f}ens( ےIR %"S:d֭׸"fH:d֙$ǭXJ͑dlD I6F$dlD .^tdm$ۏ;2 -ZԪvlD I6Fd3ikOh_߱,:m^}}w,sV>$ٮj U/^Mcd}0bU3"~oORi;tܦӱw5k>Edrpgp@͢vREU-'yJdZ֎~ UJ&jsv]M[F*Ɏ^kqKt`+_I|1ad¢+`?>O '~/jtExTcy:/fWE {ĜyuwǕ4Rj߯F5K]7J6\J #R*Wn ~{.ةɫ5j&۪KlV(=b1?gz(FY Q/K׆UT;~7T5@?5'ۨFْ*XI |vkZz ,!eTzc9e!K=VڪD߲CMuY.n PB*5T?WӞOZ+ =ƥ-n P*7P7*uzV P.;C|ٰj߼pn`\кM*S_%˫vۇ5iØ@xO6C)JNJIŪOEbZ{+WjR;F|zFK ~^+I)=2} ԰] yڬGS%m joi\Xg=wo} kUΥS y}5w+qZ{9~n4~vm߾]׍Su m?K˞T'W!IfXUkԢ⒑>R$}GOL/֭/2rd9\\ū=(w;f$+!!^~%$&H7dz>cuxV-5@?LN#G5{5 #+ZߛvlzY}bE_w5I;kh)W?sDyCڪWY;Rr4\YRCՙzX;ЊЎjR߮;gVSA}wTF5E>~SENWN8 8T.?Y_uhy}8Y륦(JUUH׏V]IO>3@u%r:/uUX9 Un+vQ]6,]?*V!: /mƵeR-տ>>zK_9y#]_h6}s/qNj_ś0d=.Vڷi)WIڅ?PVx>Ow5|e8Z]KX$|Tv]ȾeF IO:*tu+mKcz͐0H)7IԺ=BRUtznf˓ C2ev*/ֳ ԩ!a7j)]veΰXd1L2LJ9:OLו&j*U־SVE;z`LE[oai~*a$,j Rz›6TFYw$2I2 90f=IX,2$ɹjUs_I庫R%ʈѮ7jyTi=ԷҸV$ƃ5R4|υj~2]S%kE*zuԥ=XA/\VI UWYQd$kۆ瑨S;7+'zYαbZO"rwKiZN|7$(F<9Ykh!gU:W<UbOEddo^$tܯ,I.m.YS1I*}T3с-VGzh&̧\Cib{T=Ϸ:^~\1YTŃScz_tX^jhŎzɽ{\}GPtYeO4nxky$U3ј7&ߢ(%9yȻXjv-%7I9oTɪ- [2{Ԝ/4u[:{,.ՠ- Rtܟ]Xwc1r+]W'}W* Kz^xqC#tթ"*/ցmp*Zh3I۳kZ$`#EQ.pgk>}ڑq lmXdX,mpO6F$Y].0 G6X؈$`#Eڃ{mO4IÆ OPh̙dJTTۧN۷oסC+WWWYfj߾ׯbŊ9,h9rD۷oڵku!EFFJ+uAM6UJ>6Y'{!kknXgA!"##5rH}g={js3s [O$I{SO=yi͙3G-ҙ3gmո{xxMTDDD:^zu[nmW=5|py{{9IM&=>7JJJҮ]h"XB/_βm…եK 0@M6X,5o˗/װaôdɒ[&$ʕ+ZlK-D-YDC ŋ?d`%N:EiȐ!ZtՉn:YFgϞM̙33ٿ;;<ΥK%$)999mU[N-ZȮ󼠯"s"&&F&M$J/7+wz\Nĉ IIIZ|{1Hʗ/-ZsjԨuUرC_~6mڤǏ?PXX$W^ZіpEDD؜0 Ccǎiv]] ª*]K M6MJLLTvԻwoi2 #aZ`Zlsiɒ%Z~3SB}jveMH*ɶsN5kL)));Byh+.IiR{|vڥ'*%%E&IM6/FeHTcǎj۶vܩkΝX,:uԪU`Xlٲ%ҋUbEa#6oެ%$$H֭[^amK/::Z={7|S 6Tk޽5gΜl...ӧÎ?lWj֬YsFVFcώWZmidkTT.\ǏK6mSnݺY͛kԩ=zvܩK~jԨ!???Sۗ[F$]K)""B!!!뮻rԟ-=DٍMɴNfǫhѢ{ְatXs,1d-W+n-\pV퓾4arb+q ͚5KRJKݸ߯+V(99Y+V+`Nz4n8鯿իիW/u5o}h׮]:s6lؠ#G8<+wB ԯ_?޽[(44T2:r䈶nݪ/fhꪾ}5&0t!mٲE111R˖-UreL&Y]3urdK-ycG]JGFߩ2;#%%5vؑ[nrvmK.Zh$i9̙36׸qc%N8ŋ%SN>;vl̕;!I*^M|||4w\5lP2LݻO(:::dIJJ[oÊWBTre1Bwͯ/{c2r{0OOlۤrkbȍƟdə>?v\'N:,Kg2L:{<<FTTʖ-+0۴ޫM6ݴMp%"K.-I5p@ٳ'CmڴѪUlgɚ5kVKsz{{kذavM:\)1d7_t]V ,PTTTs>>>3gt龫W֐!C2$4h uA*URѢEs_~ *ZΝΝ;c(<==Iv97ٽ{w*3 {$e&y{߇-8IslҵUvÇ3ޱcGEDDT~SNzWl?{Ǒ#GlEJ0ɢ=KbM 9SgHIRhh-˝6h@͛7ϰ-22Rg}6W}i &Mhȑjܸyy IDAT$ŋzs? *+ִiL#gI&6$Ǖsww˗8G0 S7mkԨQZ)Zjڳg4i +ƍo6䞭e\$RJv1ԪUKFR*U~>, o)88E>S{9m׮]iOʕ+jҤR J%''g0˜ZP,{yjժڽ{,٣-ZGBj$)((H .գG d-ӒU֬ˏOmkD51h[fϞwyG&I8a6mNɓ'ofXe8;:ɖيݻwiӦJNN8儽[㉉ٳoYr/СCs}YߤI޽[j*5mTNNNV*-Zkbtԩ vڥ^ƍSJ'|2\:W^y%nСnhuJe\I/uC*fm ʕSÆ j*i7xC~~~zGl>vVصkWرC.\Çŋg 2-Mj2TX15iĮ1qqq;wn[.pa l̽ԄJMسڴi"EH֯_}ٴ޽{yfIڶmks *Ut7|ߴB(}Ȝm-ooo :XsuulyCѢEEeXm򋙉… 5tЛVn۶M{r={h۶myzzjذaZp""";i&Iwny'E&qT+-2^ppdRݺuխ[7}':z&L)SF믿W_ձc$][as w}lْ$%$$hʕ:}>Cl6}6s#ook> /doV˗/[ qqq/rS~ͮ1_jժDIŋ5d͝;Wחk۳g !a榅 k\pN8!٬:aÆv? oj%ݻ|cڥKr\"2/-ZT Pel٢1chY&SSRRg3F[nM۾~?^'N)M* wޭ1c諯ѣJIRRԸqcbmy~ 'OԩS9Gޏ-**{EGG+,,L#G5~x1BaaaȋrHlH[YhQuYŊsȱ{4 #TdM<3gٳ.7tPˮ{ׯcjРAJIIі-[OM6ܹ6l… ʕ+ڽ{+mܸQGЯbѦM4vXMhs|Yf"##%I5kL[-WJV rlr,)))ָtM3[s {E9;;Gɓ'?Աct1;$tydv^ahʕє)STZlk_3m3gX}N5j>җӹZj=z:vիiWFc۷/mXnlذA?j׮mjժUhQկ_?9pU={Ǹ5[l]z[n9sz-OOOeX2|ᡚ5k~ ͛5qD]vm=SrwwykĈvO_..._}Yhrwwכo &h„ z7ٳgk޽6'o̙37uqqMϻؔٳg7h@)Shʔ)6mZOٳ' 6p\dK-yc^j;_pvvV&MTJ=ڰavءի*TBBBԸqckN ׷~+Œ/Ţocfu]f̘Fnݺt.O5f饗^_kC=$IZn}]*WզMu2dΝ$5iD}Vzyyi:w>cڵK...:tZha+VN:%IճgO-]TSL$i9? @'O$}ׯ *64&&f[n>adk#_,xnC 1c3 Μ9KvZٳz駟ޔh3ͺxM%&&jz뭷te6Uzu1BݺuM񱩽OVFnc ӸqԶm[o2 #ysժUoٷ-իZ~+&Mm5s̴c24| ++o߮Hk.'o4qDEDD()))ø!WWW=:W4N_]vK&kۻ[x\d k%VP!;~0` SD+YlTD M0AZnRRRҞQgϞ т a9s^*٬iF Rڵstȯ9z:6vC0tlezxxK.7mر.]sIթS mo߾駟q e2]]]վ}{+W?Cڿ;%&&6\@6-ɖ_ŋ`Vտ\`PD M4IEї_~DժUKj3ͪ[&OGjΝZn9 .HVRJj߾7n`yyyp d=n%U$IM4QJةʖ-iӦiɒ%ZxΟ?ŋkիr2eO>D-ӧmiH {HJט;vC I6Fܓ I;w:2auEmXdKLLtdmÜlH6"؈$`#lH6"9Xѣ|"##;<'oooUP!CI$qQfn:C?;+w(`[ĉzbcc<==UtiNӂk8c{;6&Iƍ_81~<$ۉ' *d2d292 0dN>>/usJ?'OT```~N]z5Șsfu!Nӂk8c{ر@ d~9ǘWx Xd僟X,2JHH` 1-x&:ҵw::{}'%7@^0ҘE9:v0q1818bS I6f$iEKzoO|~GrkWhS5cGR~GKdXr~;LN%կiUS5jad+N~\ ^a.~3CV ]U;}W~? FamXD'mN\2or)7B w EK3w钷S~4WTxHXǡI6zTO5YQUZ#uVb)'?C)Vm7\pEwYZA*XQ5uc|Év:Fozwze Ywuu*NhPTlJzhԵMzFZ>~w Rp*wuwɦ-ڲ߯ogQwU^pߗ~mU7$H˩Jz[^'qYj-%Kd UP{?W:U0.hm25_L;'+ fq9~&m7T~5w);mu|M@U*L궼_C<~Zg~pW T s>bO)^ON*Rq犼|󉋹D'MY!}#Z['9UzrJ}J҅c%}{i^jo&__RUC5pB[vnD;c_vH5𧻓#&WBۢzjR}˩꛱10bЗ^5gTUNQ5c+[.]PTJ=EqORl_ڷrf<|V4i?gNj9^?GV*s@٨GStd{^Jԥhs;6[TLI{w;Jl9Dcf4TGG|G?G3~v"˗q/ں3VeLִcv :BS?I҉uip&VBuT읊L_]8ED*QIz4L Yt'պne-UǘOu(.nWqԺV VЁz/uZf)_4I?|Z{iau=t ~vLzSk&=MTUUu?R${&UT6j +(uHRNA5\Z|I{ƩQP+M^lW`@ިLu- ]*L:j:#^PUU60Pk4Ul%\JҞ;^ *WUM{҃WI9@Bk)8T˾j͉H:4r*XA5[ӫ_yҌ~Լne,7ê< WA G<`k/?VN/;+ֱ{ZEY F.Q[s}o,SDom|,!#GhЬZj_@!+Z}~4Qa-ȜUqKדIWuzvr8ɋVǷЦQ3̟˟ʏ频vl_zvX=n<&<2kM+kً}ԢF9ОcKMڣ75V *wRJo>Uj.UCwp㺷I5 ,}4y+R3NdCզScn&$Gds*f}aXM˹Хǩנ8dy5P;)b%53.Ue]Ylq'm!I\{lfLGivT&Ta/ArC.XBQiFºzOYo<G}SkZX΢Vz U6g-y-\{hUEߦ]s^WB:e&6HJmYջI&/+'WF[}s`&F$'I-CQ4bN\UNyerwLhɒrk;CZ?-:o6Q3_S#dEY蔤gPi)n&)k5{Uaͻ'\fEXjTDRq^Z%uA\E:;z/2xv ט:U(0>(n_ըwPOgyud.PPXLW^+jG}TR:k<4Ssڻh^:Lž[`')~<{IONw߷2)#CR]IžzuI-wVhגZZ<]E-:Q}gI-Ct~S-z@gzz| ,EԷ-iuN\Z[wȤjGgT:X.kuqoU)r>Ig׃SϨЗN2EQٽe;_rU^^}g&X+*`c[`vWat)^~rJ_+vs&zE]fRǿ' Pfm|Tˤq7*^Xh~]-(+#3)8%O_j(jQ&IuCtyW]=b.#!zUQ*~Zl$5o2fYs['Sh&͙O~?ՂC-I(y RMTYY!Õ:|쪜UL?8B_\ji_OV&I-,8N:׬o;yJLE*I,iEEnm7^OlWNT01 VUM7~W*Fg=WVSK7_15"#o+>6yUU˻C$]VsfVhrzX]W5k1SVmբY m6Ljw|Z;Xz$I :)vik]R:'; )EڴI5uW#UN¤$'ȶu_`C ]ՒG_hLZ2^{YFУ{E^|q,s|qi?eJ"ˢ)xnY,lE`w1+nۈNtS(7?p8!i (ca)N7kPM-cSs191B;P#j&u‚iWkC&htRwfdSѱ%@\|[L&{Hk IDAT1<6:z;&R, !7$I$I?tT-.N_gos/yoKvC%a;vRq/NqI? cQ")U-[²9htr} #my ?G$C0PUKԜ&lU) fŒH>n(S*6o:>|;7Nܜ;$NCb8K!Xf sB!_uԩՃ7~-a\oV@W#2VlG[deUtSz_UOtyMMZWWQsAszhd^`ˡ߷T*H")WzӽJi~x/SѨ}Ʈ3e1X;Ʋq2~HrkX *PYÞ=\)Z0 q6mM-`mNnII$I$QMh3Whz4=0݄ #(rH>1?3g{Q6b{ýhImyZDDFB8D8$'B$,ڑGfc͛qɸ9&DtRXþZ;{}(@7pY|Q7S&N5 '~' Wټ4=% ޞ|:x=e&|%CR`A ,HZю|ʔCi?MKKF5(2{ w?#y綛ycAN[Gv0v mnS&1vP{h!J&eh"_CRRR ,I$It8IDlAR~;6QbJ\O8bŶmꆓt(Q1YmIх}fgɴc"/:ggP?*v9\|ˌ*jޜ*]|/JURd_z}؈SW<ț3pn|zeIY>N5kPj%Kz|Ay~ڟp߰-Le >S!jΫA7]ZxTZnf@y=עٓ7?4 Rz5ɧk,C*(OX\rmTXj5J24@ٲ$$/c]DzbI$Isy7H߄ -?~˃T># ypz_sR,~7<ϧy |UZd/s'[lڕNeyݎQsU i85J&sȄr6O5wtKmuf/X$3ZݯrgA\۞{W#>7 >yի)h7CD,[ri6|ϫF|è"_)m&yZڍ4՛Ju tY˪~5u#Nmj@&thx.~/o<OFϙ?AR*k%xˁ$\ QrbEDWH$~$oBdkٯxDU)FF,_O(* ⤤:?RX8fNd =B ȖCAѰZCbg+y?;{&fugl?{VEŞ_2ٖ-M*eRwِlu{su |c vog►ַ 1{P>'[f<ȩq*#;帶@LӋYu߇Y]ykkC؆"I$IfG-Pxo yl`ӷFR58DOן/Đ3GF-IwqCg9"W{rJT3izˌcPDC0B,^yC q)";E(F'ޥ8L~mI(X]aOcOpϱ9;n\Z+N[w2<05z>Jǁ}:n}l=_LR0 QBDD1^F6%P%v:'>yjM͙ۦ<2mj=1&?5obo0B'Q]K*QTE^{H$gty nZtߋ@A, o'vO(LɆ e*C|u kڛf9R;޸s8`ssk~dR?l꣌["Ymn ÞSsO2NJ͸zt9pPT}wd{(\(U2,8s+%ԁͰOPf]T{i(qnKe}$]ͮ{^C18I w..8Lo7#@S{so.± T)W=s347`^wgvQZ5Gx#i mb7i?oK!:$;cU"@l=r0(zԉ|TK@tl+?o(]4kL 4'I%>cIYJ" 'MRgҩډ`~_Tœ"ϦjqJ?S$I$IOᗥSyjj~ذ|ՖA/ʹENE$~ƤwQNT9᤬gyvuNZ˫y(oM$I$eQzEO^4u>04vۉ׆xnVz|N:+KMo ;t+;fUz?ۆ$I$KW-%%ŹB%0٦YDGױa;{$I$I%ImlӬsذ=\$I$Hv'L0F"I[`Ν;gvjΜ9_>Ð$I$It֭[`KWO.]$I4i!H$I$I:M81b$I$I$I $$I$I$IN&$I$I$It2&I$I$I$I6I$I$I$)LI$I$I$IdM$I$I$IJ'l$I$I$IR:d$I$I$I$$I$I$IN&$I$I$It2&I$I$I$I6I$I$I$)LI$I$I$IdM$I$I$IJ'l$I$I$IR:d$I$I$I$$I$I$INU3jI$I$I$Kmڴ2U%6iƼ? $I$I$I&4XEsggv(.SlV^ $I$I$Ia/37Edj A$I$I$Id$I$I$ItD=v2O-l]L @ޯY^跍 28f wY4dΞԔKӨ} $I$I$I$[A?o˧e=f̀z H0ij6Rh?%#OhpK\[g6-ի&X#"I$I$I$[d pMs/i^ BT+< $b_^JH(~Nerz$I$I$IϘs2;pPO}d~9[~Ke̵} jW%,6" y9NJMY4\ѐeښٖ$I$I$IR$[9(.黙e5uIhjG,]pż$-Ļ[r덭pSHXoU8zAuH$I$I$1,&-j&o'եNd/XA"tiޛ$V.\FRȔ$I$I$I҉+ vhO@ xy͆/0d}*4mM{xZ?܄lѼcsRnx MlԘra$I$I$Iż I6„Dŭ/367=\@ :8If U8I$I$I$[f^ ӓl'@6j^Ow"3e^@&mi=瞈\ZLX|r%cM$I$I$I.v@Nx;oe܀KhoKJ*]ϬxP0gI%Bdkz*MgV$I$I$I:elt-<0X^هlO̿erI$I$I$)dj-@$I$I$I:~Bv lӕ#($I$I$Ip&ʰ`J$[8Q A$I$I$IJ`f I$I$I$oLUW޻Bz\Jrqhn4/Ral$I$I$I:Lj'^[Un=@h'M`[H}:~>a/e]E*I$I$Ic+k$B?D?7s?֜fqu0?w=e]G#KhCƱHr/OQ*I$I$IAdf@0?g<7?ڏ^/eФhb(Z# J3K"8/Fv㜱'qrZ^Βϡ5F uLf΀Q$I$I$IelDSFsZlI˖-_:'Tmr߲(;ϊQ%8)0ũ,_2flc|w5k&dRY$lXjW)D8kŀ)p`k_M| 7U;ե|=FA{$@ܱ,$I$I$IH-=RHڳ=|I 2SZD9C,ߟJ-1Oga~["kӨvt~zslh(5złV+{!s(+iϞWk&BF;ѹUT]vukUOb}&fl؃"qˍ+Bwˏ .HV(Mpz6֑$I$I$IRVzrVtp٧0DVIi@6  8g42tq?ef(:)ϏbcQF?m** #(c,oP㐆 T㍌pap$I$I$I:elGG՚|<+V$I$I$IorIys(gZ7[!1^_KVrN3Nd19 2,V(Mpz6dT$I$I$I:d$[ [a*TJ @u6kJ|a+P"]ˤWzQ"xȎ*X"b%I$I$ItrIdd%&G՚|<3c&I$I$I)%[9/e7[obG4{,*DDu=F]͝{s[8kWh#_\s'gM/~1=,I$I$I$@,Sy~n-ӲmGλATk_~/ mvם㘷v++>݅w$I$I$I7:Y &ҥKU<}tڵkaI$I$I$ؘ6mmڴ02ĉ֭[`B=$I$I$II6I$I$I$)LI$I$I$IdM$I$I$IJ'lf6h_dv,$I$I$IjN$[oplMWQ {a.B$I$I$INY(ɶ Gt`Ș+Q60޾}p%I$I$Ibۆ|%=~.EB&xǹ3P% +I$I$I,ԓm@2o܈[s3zʃO3/-!Xvjlf+@xfC{ys`^:{ϮK\1DDKcAbͫi_c["C}"jC|T4+кXVڿz'ױ%{L S] wo9K[kO(_"?@&ԋ"Ehb.;(q$:bb˲ؿ/c͕HO/"2s~xӯl,?Oko;ΈӘ$I$I$IY0"99mB&=x;v`7-ߘsZ7-jGz_\,Rv'קLԪ{u?lhF_E @ }37*r;ή:.T"7} '74"~xZ[{u~$8W:3{֗NQnEI$I$I$=YnďLL1rRӸu^.3ijn"FԣM} 67M86,^RR)4x+78y VPqcI}w7J .P3޻1"ŋ+s2M[R$I$I$Iǣ,d}=S>[et_Xl#MhHuy6CB*ΣVSxh}„/YUJ2O>xm$1ѐB b$I$I$I:e$[ {qשC(U0AY)T6R2c;&oZ8"[ټI|5rXg?%) uGi|tW-bYr-zևSTjT+jJO$I$I$I',8'[]m^ΡiSp75k[ TMedPRv ŻGET,ofy!(97p %vt?uPɌ9*_\1{r}rf6$I$I$I2qd#XK}@x<# QDT,0yw&8n ?lM&&o8F$P[[̀n))EJӸu- !5ۜHl $I$I$ITOD rŤxwډN]/{四OGU|fHN8sv\ThOC  Ys~9ɬ}u$667%}L)_Qla~GIsP6:w,Kl]0MJ'6ŨukR$I$I$I (dK˘Va,w;%δcoĆ9“>eݷY0ݞzA CQS* J>9:4{j9ϸL0f(r}~f$I$I$I2NI7A׫EԆ1}Ffʭ8!9~ąfwQHJVj?vMIe$[ .µsжEq6Uk˨'?ƧuSֳnC|кQ-oI$I$I$'L_p8L8 @%[hںξ@ /-"Bl gL0hV7M+VcZ|9+onʺ[R;n!&y7I$I$I$L-,eB|dS( OpiԖVQ8  ԶFŊPg!d JI$I$I$)K2I6bp^<َnY5snf%}axXLL-j@\}:ڵ˰$I$I$ItlL66mdvnĉt֭ 0Hf9$I$I$II6I$I$I$)LI$I$I$IdM$I$I$IJ'l$I$I$IR:0Io}"tz{B|xmEVd@p$I$I$I:d$^GLY@|AT@u,.^勑+_&I$I$ILdfQ.TZ uLf΀ *N$I$I$I',ԓvR:o6eY-o֗/o9߹!>:\Ũٱ?co' w"s }p2{If-<+ofSWжrAEG ec[?g$I$I$I:7I/ѻm/ފ:aoQv?:!;潿l]G7hnՕQ_@t~+VbPl1޽-uUS:s(M>U[$I$I$IRVr c21'S_ * #:EP:КvmKZW1#ZRRᒧƠx}~$Zx>$@Lv/aK$I$I$)K:Nl{Xd5?.Xw9Ӯ?JnB>Y6f}ꜭθ7OJsmXPHdBOdjF}gJg|nl9vǾYiquվ9߿a;~ C{O`;svsc2Yy_#KKq%C]Ki;,<ٝ1kF?yqdKeژkdN$Iyt/<Wbu|<oF;gIUY4݌0%"h;Ldkqajj<sf=ㅵ7漋7Q_Y!6+?(n鵳Ίsy%wˤν:U,xFg̘1mrnݺ!={l<&7{7䒓. MmƬV9椭~?*dl1zb*,6+/6uȎKG4lQ:,'^cv구m$>##GLSSSצyOW^mzٙg"ۢ}97n@U; V控Om 0GǏo$뮻nz6?3 Ͽ1cƤVy`+Եۙ/j3lذ'wZN< 6,J%MMM9c[Ji v1hРIFO<1{n;Ϫe@jveP4hPsǷZ@Sd;sg9'xb[LUCE;Y%/Tt9]WY;[wHV5L,a~k_;5:.?㮺Nfnѵ,8GY=8 :t2dHp͹lȞYnĹ}=sًJTĉg<>j 3lynʒݳ&g$jvضk^]\v0t݌~嵤[tpe\l/>IR˘Q+*ΘcR^q76;oh^\\vs|t>͝f}ZKCwZeӹC2iRM9^g D.my9o}F>. RW&rI'm8-?{?K`d){3/=yw~z}1;fl2A٣9RM-oqn~W2dyљ0mu+ּw;؂湾4cҽ1SP%V< ݺsR{#o%dÿ _?wkJL>4d˱H{ߜ{u+o\<i6nܸhO"1j5J%jMQWWj69~ng`АQFmmV5jT:@ݻwzꩌ1"cǎms466!{n㷄@ӧO{O. lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! uh 09rdƏ1cƴu떆ٳM"fȑijjJ~<=X~իM3;Ռ?>}m7n\gv@3fLjgv"ЪJ"[ǎ?)Stڄ@ۻZJ2Wo- VUN&ЪD6( D6@ZdhU"Z"dUMlz :4;w_;3?XId3s'fsw 'dРA"hut~T*q|}9?JShU-K}}}N;=1ds|{o)ЪZi HRiZ=l>:uuu=ztN?Nl6G1}%hO"1j5J%jMQWWj69~ng`АQFmmV5jT:@ݻwzꩌ1"cǎms466!{n㷄@ӧO{O. lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdB"D6($@! lPHdBwuW{JE޺N "D6($@! lPHdB"D6($@! lPC[xȑ?~|ƌVot- ٳ'hVD#G)mY[`c駟N^f@9>6lǏO~R}桇Ze4$3Ƣ`.|6Zj6N6 ?g0ok Mɖ KZ_?~xpW%c`^TTZ4Z לo:9>q>Y͒ \KYsMMy\0吽%cZ O9yH_֒%ă^eT9Nd}`]E_& -931[dS){W%y]^jt φWv6>TM~,޸Bz/͵Uߩ)s~G͑ K ]h.66WzY)[AįIyҦE'ߜk'tKIW炛nEU}VawR\Syq9'dǝO6;7ܛ<ټqNlir~X6:?尜܆٥R;dLy2;r;^{3>j9ΤݳaϺ]iɘ,\lSs5NSw_!Z-ڛ/gE/%{l?3,$7Gy#6uJSi;n\}6+f]9Cg%,{Z iw5FgŒdlr{(wz|9SdÆܣ7M׺LSwX"'I99 =F:ϓ2r.ɕrMW睑Sro,U ٿ$izx̥k'yŬQ9òA"n޹-rAg۱e ՗ʁ;uXθj+Om&-3;Wn߽̑0v"vR<}G w].jz8W}kU}0ϭp\.=_o?^>$Cn!zԧMfE~?uO~|mROZme5\{T23f M0r346.iX2\KDv8 L+etzVMV47~χҩkVzJuR9/\vTNydAxFh-"IϚG&?{oSOuΚysX8uZjN-Ϣʅ7q8`OdԯNOdWO$j{ǟ9X4dX|uŏoQܳXM_pLm3>]g{1{gԯ;={7m\V8+YGMzj5Zj=rHmݲFǏ!NdἛV>|+rܝcY#g,:Y[kO}??MJÖ_ʊu3|>\ ǧ(M`V޽ښ9xmvӱ>_o1<<(|g`y9yC3oee;G`O99]DtjyL-Z]gu:.sZjՙ|f~.k{tМyg7wyg2n}fJҲOk-ʹuGuLy\!?=|?KIv9uX {we17*Yr崝d,Uۜ9S:7,5~q^FtXY$97/5$gxѸ4/topHz}goCA.?~/o}vv 9ڜ9yXVol\ s?$G/3{]sgʟ{Ǧ)\]}V>O6w-.9Co;#[f.sh%-7MnGoh-ܒ.jugnePfN묏[{l9l󈖬Z-O6!BdG uM"[cccj* hT466v@˵diIdkhhȨQZkR)+j5

      Assembling Group Lists RX group lists or simply Group Listss are just lists of Group Calls you may wish to receive on a channel. Of cause, you may want to receive calls to multiple talk groups on one channel, hence you have to create these lists of talk groups beforehand. Screen-shot of the RX group lists. The list of group lists. The Group Lists tab is just a simple list of all group lists you created. You may add a group list by clicking on the Add RX Group button at the bottom. You can delete a group list by seleting it in the list and clicking on the Delete RX Group button there. You can also edit a RX group list by double-clicking on that group in the list. Screen-shot of the group list edit dialog. The group list dialog. When creating a new group list or when editing one, the Edit Group List dialog will open. Within this dialog, you can change the name of the group at the top. In the center of the dialog, you will find the list of group calls of this group list. You can add group call to the list by clicking on the Add Contact button on the bottom. You can also remove contacts from the list by selecting the contact and clicking on the Remove Contact button. When you are done editing the group list, click on the Ok button. The Cancel button will discard all changes and closes the dialog. Screen-shot of the group call selection dialog. Selecting the group calls to add. When adding contacts to the group list, the a dialog appears, that allows for selecting the group calls to add to the group list. Some radios (in particular the OpenGD77 firmware) also allow to add private calls to group lists. To this end, the Show private calls option allows to add private calls too. All other devices do not allow for private calls in group lists. They will simply ignored by qdmr when the codeplug is generated for these devices. If Show Commercial Features is enabled in the settings dialog (see ), a tab bar is shown at the top. There you can also access the device specific settings for the group list.
      qdmr-0.12.3/doc/manual/gui/gui.xml000066400000000000000000000030471501654372000166630ustar00rootroot00000000000000 The Graphical User Interface This chapter describes the graphical user interface (GUI) in some detail. qdmr aims at being a device and manufacturer independent CPS. To this end, the GUI reflects a device independent code plug. This means, that not all of the shown features are present in all radios. For example, not all supported devices implement APRS or roaming. These settings are then ignored when generating the device specific binary code plug. Some radios provide settings, that are important but also specific to a single model or family of models. qdmr aims at supporting these settings through so-called extensions. These extensions can be attached to all elements of a codeplug. For example to channels, contacts, zones, etc. qdmr-0.12.3/doc/manual/gui/programradio.xml000066400000000000000000000106061501654372000205640ustar00rootroot00000000000000
      Programming the radio Once the code plug is finished, it can be programmed onto the radio. Just select the write cod plug button in the tool-bar at the top of the window or select write from the device menu. In a first step, qdmr will try to detect a connected radio. This will be done automatically (unless disabled in the settings menu) if there is only one radio connected and it is save to access the USB device. Some radios use some generic USB to serial chips in their cable instead of connecting the micro controller of the radio directly to the computer. This way, it is not possible for qdmr to safely assume, that the found serial port is actually a radio. If such a generic interface is detected, qdmr will ask which interface the radio is connected to. Once a radio is found it will verify the code plug with that radio. That is, it will check whether any limits are exceeded. For example the number of channels, contacts, group lists, etc. There are several levels of issues that can be detected when verifying a code plug with a radio. The lowest level is the Information. These are just messages generated to inform you about minor changes made to the code plug to fit it into the specific radio. For example when zones are split. These information are usually ignored and qdmr will proceed writing the code plug. Warnings are one level more severe. They are issued if changes are made that may change the behavior of the code plug. The result, however, will still be a working code plug. They are usually issued when names are too long. When warnings are issued, qdmr will not automatically proceed writing the code plug. The user, however, can ignore the warnings and continue. In the application settings (see ), you may choose to always ignore verification warnings. In this case, qdmr will write the code plug automatically, even if there were some warnings. Finally Errors are the most severe verification issues. They simply prevent writing the code plug to the device. The user cannot ignore errors as they would result in invalid and even damaging code plugs being written to the device. If, however, everything fits into the radio, qdmr will start writing the binary code plug to the device. Writing the code plug is a two-step process. First, the current code plug is read from the radio. This includes all settings. Then the device-specific code plug is updated and then re-uploaded to the device. This two-step process will maintain all device-specific settings made earlier unless explicitly set within qdmr. During the reading or writing, the qdmr GUI will turn gray (inactive) to prevent any changes to the code plug during the transfer. However, a progress-bar is shown in the bottom-right to indicate the progress.
      Permissions When running qdmr or dmrconf under Linux, you may need to change the permissions to access USB devices. Along with the software, a udev rules file was installed. This file specifies that members of the dialout group have access to the radios. Consequently, you need to be a member of this group. You can check your group membership with groups. This command lists all groups your user is a member of. This list should contain dialout. If your user is not yet a member of the dialout group, you can add your user to it by calling sudo adduser YOUR_USER dialout
      qdmr-0.12.3/doc/manual/gui/radiosettings.xml000066400000000000000000000073271501654372000207630ustar00rootroot00000000000000
      General configuration The figure below shows the General Configuration tab of qdmr. This tab is divided into 4 sections: DMR Radio ID, Boot Settings, Audio Settings and Channel Default Values. Screen shot of the general configuration like the DMR ID and the radio name. General settings tab. The general configuration of your radio is the simplest step: You only need to enter your DMR ID and your radio name. The latter is usually just your call sign. If you do not have a DMR ID yet, you can request one at ham-digital.org. You will always need only one DMR ID, even if you have several radios. The DMR network is able to handle multiple endpoints with the same ID. Never request more than one ID, they are a limited resource. In the rare case, where you actually need more than one DMR ID, for example if you the same radio from HAM as well as commercial applications, you may add your additional DMR IDs using the Radio IDs tab. This tab is usually hidden and can be accessed by enabling Show commercial features in the application settings (see below). Within the Boot Settings, the Intro Line 1 & 2 specify the text that some radios show on startup. You may enter any text here. Some radios show an image during boot. For those radios, these settings have no effect. Within the Audio Settings, the MIC amp. option specifies the microphone amplification (if supported by the radio). This must be a value between 1 and 10, where 1 is the smallest amplification and 10 the loudest. The Speech Synthesis option allows for enabling speech synthesis by the radio. Some radios support speech synthesis to help visually impaired operators to handle the radio and navigate its menus. If this option is checked, the speech-synthesis will be enabled if the radio supports this feature. The Channel Default Values block allows to specify some default values that can be referenced by channels (see below). This serves two use cases. First, it allows to set some channel properties for all channels that reference these values at once. More importantly, however, some radios do not allow to set these options on a per-channel basis. For those radios, these values are used for all channels. If Show Commercial Features is enabled in the settings dialog (see ), an additional block labeled Extensions is shown at the right side. This view allows to add, remove and edit device specific radio settings. All device specific settings are handled and displayed in the same way. See for some description of this process.
      qdmr-0.12.3/doc/manual/gui/roaming.xml000066400000000000000000000144361501654372000175370ustar00rootroot00000000000000
      Roaming Roaming is a feature that allows DMR radios to select an alternative repeater once you leave the range of the currently selected one. To do that, you have to specify so-called roaming zones. Within these zones, you collect all repeaters that provide access to a particular talk group. When roaming is enabled, the radio will check periodically, whether the current repeater is still reachable. If not, the strongest repeater from the selected roaming zone will then be selected instead. In order to stay connected to a particular talk group, not the entire channel settings must be changed. But only those properties, that are specific to the repeater we change to. These are the RX and TX frequencies, color codes and sometimes the time-slot. The latter is necessary, as those repeaters may be located in other regions. Usually — at least for Brandmeister repeaters — the time slot 2 is reserved for regional communication while time slot 1 is intended for inter-regional communication. So you may need to override the time-slot of the active channel, whenever you roam outside of the region associated with the talk group. Some talk groups, however, are over-regional anyway. For example, the world-wide talk group 91. Here, the time slot will always be TS 1 and thus does not need to be overridden. As only some properties of the current DMR channel needs to be overridden, there are special channels called roaming channels that contain only those settings, that are changed during roaming.
      Roaming Channels The Roaming Channels tab collects all defined roaming channels. Screen-shot of the list of roaming channels. List of roaming channels. Each roaming channel has a name, for easy reference in the roaming zones. This should be the call of the repeater. Additionally, each roaming channel has a transmit (TX) and receive (RX) frequency overriding the TX and RX frequencies of the current channel on roaming. There are two optional settings, that may override the time slot (TS) and color code (CC) of the current channel. If [Selected] is show, the time slot and color code of the current channel is used instead during roaming.
      Roaming Zones The Roaming Zones tab collects all defined roaming zones. Screen-shot of the list of roaming zones. List of roaming zones. There are two ways to create a new roaming zone. The easiest way is to generate a zone and the associated roaming channels automatically. Click on Generate roaming zone, to generate a roaming zone automatically or Add Roaming Zone to assemble one manually.
      Creating a Roaming Zone When generating a roaming zone automatically, a dialog will you to select a talk group or several talk groups to create a roaming zone for. That is, the talk group you want to stay connected to in case of losing contact to the repeater. Screen-shot of the talk-group-selection dialog when generating a roaming zone automatically. Selecting talk groups to generate a roaming zone. Once a talk group is selected, a roaming channel created for each DMR channel, which has the selected talk group as its transmit contact. These newly created channels are then collected into a new roaming zone. The roaming zone editing dialog is then opened, allowing you to edit the newly created zone. Alternatively, you may create a roaming zone manually. Simply click the Add Roaming Zone button and the roaming zone editing dialog will open. There you can add roaming channels manually.
      Editing Roaming Zones Double-clicking on the roaming zone or clicking on the Add Roaming Zone button will open the Roaming Zone Editor. Screen-shot of the edit-roaming-zone dialog. The roaming-zone editing dialog. Here you can edit the name of the roaming zone as well as adding the roaming channels that are part of this zone. However, you may also add DMR channels to this zone by clicking on the Add DMR Channel button. This will not add DMR channel directly to the roaming zone, but will create a new roaming channel from this DMR channel. If Show Commercial Features is enabled in the settings dialog (see ), a tab bar is shown at the top. There you can also access the device specific settings for the roaming zone.
      qdmr-0.12.3/doc/manual/gui/scanlists.xml000066400000000000000000000100771501654372000201030ustar00rootroot00000000000000
      Assembling Scan Lists Scan lists are simple lists of channels that are scanned sequentially, when scanning is started. A ScanList may be associated with an analog or digital channel (see ). For many radios, you need to associate a scan list with a channel (see above) in the analog or digital channel edit dialog. This determines which scan list is used, when a scan is started on a particular channel. Screen-shot of the list of scan lists. The list of scan lists. A new scan list can be created by clicking on the Add Scan List button. A scan list can be deleted by selecting the scan list and clicking on the Delete Scan List button. The order of the scan list can be changed by selecting a list and moving it up/down using the up and down buttons on the right. Double-clicking on a scan list or clicking the Add Scan List button will open the scan-list edit-dialog. This dialog allows to alter/assemble the scan list by adding, removing or reordering the channels in the scan list.
      Edit Scan Lists Screen shot of the edit scan-list dialog. The scan-list editor. By double-clicking an existing scan list or clicking on the Add Scan List button, the scan-list dialog opens. The Name field allows to specify the name of the scan list. The optional Primary and Secondary Channel fields allow to specify channels that are visited more frequently. The Primary Channel will be visited 50% of the time. That is, after a channel of the scan list was visited, the primary channel is visited again, and after that, the next channel from the scan list is visited. In the end, the primary channel is scanned half of the time. The secondary channel is similar but gets visited only 25% of the time. The drop down list allows to select none, any channel or the Selected channel. The latter refers to the channel, the scan started on. The optional Transmit Channel or Revert Channel specifies the channel to transmit on during a scan. Here none, any channel, the selected channel and also the Last channel can be chosen. The Last channel refers to the last active channel on the scan list. This allows to answer a heard call during a scan. Channels can be added to the scan list by clicking on the Add Channel button at the bottom. Similarly, channels can be removed by selecting them and clicking on the Remove Channel button. Like for all other lists, the channels can be moved around within the list by selecting channels and using the up and down buttons to the right. If Show Commercial Features is enabled in the settings dialog (see ), a tab bar is shown at the top. There you can also access the device specific settings for the scan list.
      qdmr-0.12.3/doc/manual/gui/settingsdialog.xml000066400000000000000000000202251501654372000211140ustar00rootroot00000000000000
      Application Settings Dialog The application settings dialog controls the behavior of qdmr. The dialog is divided into 3 sections, accessible via the tabs on the top.
      Data Sources The Data Sources tab collects the settings for the location service and repeater database. Screen shot of the application settings dialog for the data sources. The settings dialog: Data sources The first section concerns the location of the user. You may enter your Maidenhead Locator here or you may enable System location. The latter tries to obtain the current location from the operating system. This information is then used in the channel editors (see ) to provide auto-completion for repeaters nearby. The second section allows to set the source for the repeater database. This enables qdmr to automatically complete some information for a repeater if its callsign is entered as the name, when creating a new channel. For some weird reason, the single data source Repeater Book provides two identical APIs for North America and the rest of the World. You must select your source accordingly.
      Radio programming settings The second tab controls, how the radios are programmed. That is, how the codeplug is assembled and also, how the callsign database gets curated. Screen shot of the application settings dialog for programming the radios. The settings dialog: Programming The Radio Interfaces section contains settings, controlling, how the radios are accessed. For now, there is only one setting, called disable auto-detect. This will disable all means to detect and identify connected radios. You will then have to select an interface and choose a radio model, every time you access it. The second section specifies how codeplugs are assembled. The first option Update codeplug specifies whether a codeplug is generated from scratch or whether the codeplug currently programmed on the radio gets updated. qdmr does not implement all settings possible for all radios, consequently Update code plug should be chooses to maintain all settings of the radio that are not touched by qdmr. For some radios, the GPS and roaming functionality must be enabled explicitly. The Auto-enable GPS and Auto-enable roaming options can be used to automatically enable GPS or roaming. If selected, whenever any channel has a GPS/APRS system or a roaming zone associated with it, the GPS and/or roaming gets enabled globally. As described in , the upload of a code plug will be paused if some verification warnings are issued. The Ignore verification warnings option allows to continue silently even in the presence of verification warnings. The may be needed for some radios with some rather short communication timeout. The radio may reset the connection to the computer while the warnings are shown. To prevent this, this option might be used. The Ignore frequency limits option does exactly what it says. Usually, programming a channel outside of the radios frequency range would issue an error. However, many radios are able to receive and even transmit outside of the frequency range specified by the manufacturer. But be aware, that transmitting outside the declared frequency range may destroy the radio! The Call-Sign DB section collects options that control the automatic curation of the call-sign DB. Many radios allow to write a large database of call-signs and DMR IDs to the radio. These DBs are then used to resolve DMR IDs to call-signs, names etc. and display them. Usually, curating these databases is a cumbersome task. qdmr tries to automate this task. Usually, qdmr will select as many call-signs from the global database it can fit into the radio. Although modern radios will provide a huge amount memory, not all registered IDs can be programmed. In these cases, qdmr will select only the closest IDs to your DMR ID (default Radio DMR ID, see ). The DMR IDs are compared by the longest matching prefix. This makes sense as DMR IDs are not random. They share the same prefix for countries and regions. This way, qdmr will first select all IDs from the same region followed by all IDs from the same country etc. Of cause, there is no rule without any exceptions. Some countries have several prefixes assigned. The Limit number of DB entries option and Number of DB entries field allow to limit the number of DB entries written to the device. If the Limit number of DB entries option is disabled, as many entries are written to the device as it can hold. The Select using my DMR ID option and the Select using prefixes field can be used to control the selection of entries. If the Select using my DMR ID option is enabled, the aforementioned algorithm is used to select the entries based on the default DMR ID. If this option is disabled, a list of prefixes must be specified in the Select using prefixes field. This must be a list of comma-separated prefixes like 262, 263. Whitespace are ignored. Then the DMR IDs closest to these prefixes are used to assemble the final call-sign DB.
      Extension settings The last tab collects settings, controlling several extended settings for the devices. Screen shot of the application settings dialog for extensions. The settings dialog: Extensions DMR originated as a standard for commercial radios. Consequently, there are many features that are not relevant or even illegal for ham-radio use (e.g., encryption). However, some operators use their handhelds for both ham-radio and commercial applications. Consequently, qdmr cannot ignore commercial features. It can, however, hide them. Enabling the Show commercial features options will show these features. Starting with qdmr version 0.9.0, there are some device specific settings, allowing to configure features only present in one device and not represented in the general code plug. These are called extensions and are usually hidden. To show these device specific extension, select Show device extensions.
      qdmr-0.12.3/doc/manual/gui/zones.xml000066400000000000000000000045151501654372000172360ustar00rootroot00000000000000
      Assembling Zones You may program a myriad of different channels for you radio. To organize them in handy chunks, zones are used. That is, a zone is just a named list of channels that are relevant for a particular area or a particular situation. The list of zones. The Zones tab just lists all defined zones. You may add a Zone using the Add Zone button or you may delete one by selecting the zone in the list and clicking on Delete Zone. You may also alter the ordering of the zones by selecting one from the list and using the up and down buttons on the right. How zones are implemented differs from radio to radio. For example, some radios allow to set a different zone for each VFO (A or B), consequently these zones are simple lists of channels. Other radios allow to select a single zone for both VFOs. For these radios, a zone consists of two lists of channels. One for each VFO. qdmr zones follow the second approach. That is, a zone consists of two lists. One of each VFO. When programming radios that support only one channel list per zone, the zone is split into two (unless the second list is empty). One for each VFO and the A/B label is added to the name. Double-clicking a zone or clicking on the Add Zone button will open the zone editor dialog. Screen-shot of the edit-zone dialog. The zone editor. This dialog allows for adding or removing channels, and changing the order of the channels within the zone using the up- and down-buttons on the right.
      qdmr-0.12.3/doc/manual/html/000077500000000000000000000000001501654372000155315ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/html/Makefile000066400000000000000000000002071501654372000171700ustar00rootroot00000000000000all: manual.xml xsltproc dm3mat.darc.de_manual.xsl manual.xml clean: rm -rf fig/ rm -f manual.xml rm -f *.html rm -f docbook.cssqdmr-0.12.3/doc/manual/html/dm3mat.darc.de_manual.xsl000066400000000000000000000105151501654372000223040ustar00rootroot00000000000000 appendix nop article toc,title book toc,title chapter toc part nop preface nop qandadiv nop qandaset nop reference toc,title section nop set toc #include virtual="/menu.html"
      Up Index
      qdmr-0.12.3/doc/manual/html/manual.css000066400000000000000000000011051501654372000175150ustar00rootroot00000000000000main > nav { margin-top:20px; } dl.variablelist { margin-left: 2em; } dl.toc { margin-left: 2em; margin-right: 2em; background-color: #f2f2f2; overflow: auto; padding: 1em; border: 0px solid black; border-radius: 5px; font-weight: bolder; } pre.programlisting { background-color: #f2f2f2; overflow-x: auto; padding: 1em; margin-left: 16px; margin-right: 16px; } table { margin-left: 16px; margin-right: 16px; } thead td { font-weight: bold; } tbody td { vertical-align: top; } .token { font-family: 'Courier New', Courier, monospace; }qdmr-0.12.3/doc/manual/intro/000077500000000000000000000000001501654372000157205ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/intro/codeplug.xml000066400000000000000000000612631501654372000202540ustar00rootroot00000000000000
      Codeplug Assembly After the basic concepts and technical details of the DMR mode has been discussed, it is time to consider the actual configuration of the DMR radios. This usually not done via the keypad of the radio but with the help of a separate software. The so-called CPS or codeplug programming software. Before we can start, we need like any other participant in the DMR network a unique number, the DMR ID. You can get your personal and unique DMR ID from radioid.net. There you need to verify that you are a licensed ham operator. You will receive your personal DMR ID usually within 24h per Mail. Once you've got your ID, you can start. As this script is intended for the beginners, it is very likely that you do not own a top-shelf Motorola device but rather one of the cheap devices of the common manufacturers. If you do not own a DMR device yet but consider to by one, you should explicitly check whether it supports DMR Tier I and IIAs usual, DMR is not a single standard but a family of standards. Tier I describes the simplex operation while Tier II considers the repeater operation and time-slots. You will therefore need a device that also implements Tier II to be able to work with repeaters.. Ignore any marketing BS and check the technical description of the product for Tier I & II. If it is not mentioned there, simply skip that product. This is particularly true for the Baofeng MD-5R but not the RD-5R. The manufacturer of the device of your choice will provide the CPS for download you need to program your radio. Usually you will also find there firmware updates for your device. The manufacturers usually provide a separate CPS version for every device and even firmware revision. So please check whether you've got the correct CPS version. The configuration of the device differs from device to device and even more from manufacturer to manufacturer. The basic setup, however, remains the same. When you start the CPS for the first time, you will likely note two things. First, that the user experience stems from the last millennium (about Windows 3.11). And second, that there are a tone of obscure and badly translated options for your device. These options are usually named cryptic and are not documented. The configuration of your device usually happens in five to six steps: General settings, creating contacts, assembling group lists, creating channels, assembling zones and optionally assembling scan lists. Within the following sections, I want to guide you through these steps.
      General radio-wide settings. The single most important options within the general settings is your DMR ID and your call sign. These options are usually located under the label Radio Settings or General SettingsThe actual name may vary from manufacturer to manufacturer.. Your DMR ID is entered in the field name Radio ID. Many radios support to enter several DMR IDs. This feature is usually not used in ham radio. In fact you will only always need a single DMR ID even with several radios. Your call can be entered in the Radio Name field.
      Creating Contacts Once you have made these basic settings, you may create some contacts in your contact list. This list should contain all talk groups you are interested in, some private contacts to OM you know as well as some service numbers for the echo-service, SMS service etc. A sample is shown in . Example contacts for germany
      Name Typ Nummer Name Typ Nummer
      Local group call 9 Ham/SlHo group call 2622
      Regional group call 8 NiSa/Bre group call 2623
      TG99 group call 99 NRW group call 2624
      All call all call 16777215 RhPf/Saar group call 2625
      World wide group call 91 Hessen group call 2626
      Europe group call 92 BaWü group call 2627
      D-A-CH group call 920 Bay group call 2628
      Germany group call 262 Sa/Th group call 629
      Austria group call 232 Echo Test private call 262997
      Switzerland group call 228 SMS Serv. private call 262993
      EMCOM EU group call 9112 DAPNET private call 262994
      EMCOM WW group call 9911 APRS GW private call 262999
      MeVo/SaAn group call 2620 DM3MAT private call 2621370
      Ber/Bra group call 2621 ... ... ...
      Of cause there are much more talk groups. There are also talk groups for specific topics which are not necessarily targeted at a specific region. A rather complete list can be found in the Brandmeister Wiki.
      Assemble group lists The next step is to assemble so-called Group Lists. These are simple lists of talk groups that you want to receive on a particular channel. As mentioned in the introduction, the network does not know which talk groups you are interested in. This must be programmed into the radio. Group lists do exactly that: The specify which talk groups you want to receive. All others are ignored. You should at least create two group lists. One for the simplex operation, one for regional communication and optionally one for the trans-regional communication. You should also create one for each region you frequently visit. The simplex group list is theoretically not necessary as simplex calls should always use the so-called All Call. Frequently, however, also the talkgroups TG99, TG9 and TG8 are used in simplex operation. Hence a group list with these talk groups is needed for simplex operation. Your trans-regional talk group should include the talk groups for the entire world TG91, your continent (e.g., Europe TG92), your country (e.g., Germany TG262) and also the emergency talk group (e.g., 9112 in Europe). Finally the talk group for the local/regional communication should contain the local TG9, regional TG8 and the talk group for your region (e.g., TG 2621 for my region Berlin/Brandenbug). As I am also frequently in saxony, I also created a group list for that region. My group list settings are shown in . Example group lists
      Name Group calls
      Simplex Local, Regional, TG99
      WW/EU/DL World wide, Europe, D-A-CH, Germany, EMCOM EU
      Ber/Bra Local, Regional, Ber/Bra
      Sa/Th Local, Regional, Sa/Th
      Creating channels Before we start assembling any channels, I should mention that DMR radios are also able to transmit and receive analog FM. Thus, you can also use them for classic FM simplex and repeater operation. In this section, I describe the configuration of digital DMR channels usually called digital channels. The configuration of analog FM channels is not described. To create a DMR channel, you have to select digital for the channel type, for FM channels analog. When you already have some experience with the analog FM repeater operation, the configuration of DMR channels may appear quiet weird. For analog FM repeaters, you usually configure exactly one channel. For DMR repeaters you will configure at least two (one for each time slot), but usually many more. To cut a long story short, let me explain it with a concrete example.
      Creating Simplex Channels Example simplex channel configuration
      Name RX Freq. TX Freq. TS CC TX Contact Grp.List
      DMR S0 433.4500 MHz 433.4500 MHz 1 1 all call simplex
      DMR S1 433.6125 MHz 433.6125 MHz 1 1 all call simplex
      ... ... ... ... ... ... ...
      In an example for a simplex channel configuration is shown. Of cause, you should extend it to all 8 simplex channels. The first column simply specifies the name of the channel. The second and third columns specify the transmit and receive frequencies for these channels. For simplex channels, RX and TX frequencies are the same. In simplex operation, there is no repeater. That is, no instance that dictates a beat. To this end the choice of the time slot (TS) is irrelevant and usually TS1 is chosen. The color code, however, matters. Repeater as well as your radio will ignore calls with a mismatching color code. For simplex channels, the color code 1 has been established. The sixth column specifies the default transmit contact. For simplex channels, the so-called All Call should be chosen, to ensure that really everyone can receive the call irrespective of the receivers group list settings. The default transmit contact specifies the contact (private, group or all call) that is called whenever the PTT is pressed. As mentioned earlier, there is an exception to that rule. Whenever you directly answer a call within in the so-called Hang Time, you will answer with the same call you received. The last column specifies the to so-called Group List. This list specifies which talk groups are received on that channel. As mentioned earlier, no entry should be needed here, as the all-call should be used for the transmit contact on simplex channels. Unfortunately, it is not uncommon to find several talk groups being used as transmit contacts on simplex channels like TG9, TG8, TG99. For these cases, a group list simplex was created earlier. Within your CPS, you will find many more options for channels. The majority can be left untouched. At the end of this section, I will describe some of these settings briefly. Many of these settings are quite uncommon in amateur radio or even straight illegal. The Admit Criterion specifies under which conditions your radio is allowed to transmit. For simplex channels, the option channel free should be chosen. This configures the radio to only transmit if the channel is currently free.
      Creating repeater channels Creating repeater channels is slightly more complex than creating simplex channels, as we need to create several channels per repeater. Before you can create any channels, you need to know which DMR repeaters are near to you. A good overview provides the repater book. There you can also filter for DMR repeaters and you get all information you need to configure the DMR repater channels. That is input and output frequencies and the color code of the repeater. Example channels for a single repeater DB0LDS
      Name RX Freq. (output) TX Freq. (input) TS CC TX Contact Grp.List
      DB0LDS TS1 439.5625 MHz 431.9625 MHz 1 1 - WW/EU/DL
      DB0LDS DL TS1 439.5625 MHz 431.9625 MHz 1 1 Germany WW/EU/DL
      DB0LDS Sa/Th TS1 439.5625 MHz 431.9625 MHz 1 1 Sa/Th Sa/Th
      DB0LDS TG9 TS2 439.5625 MHz 431.9625 MHz 2 1 TG9 Ber/Bra
      DB0LDS TG8 TS2 439.5625 MHz 431.9625 MHz 2 1 TG8 Ber/Bra
      DB0LDS BB TS2 439.5625 MHz 431.9625 MHz 2 1 Ber/Bra Ber/Bra
      I think, it is the best to explain the creation of repeater channels using a concrete example for a repeater near to me shown in . This repeater has the call DB0LDS and has the input frequency 431.9625 MHz and output frequency 439.5625 MHz. According to repeater book, this repeater expects the color code 1. These are the elementary information you need to set for all channels using this repeater. Many CPSs allow to copy or clone channels. This way you only need to enter this basic information once. At the end of , I mentioned that trans-regional communication is happening on time slot 1 while regional communication is happening on time slot 2. This is visible in this example. The repeater is located in the region Berlin/Brandenbug (Ber/Bra), consequently all channels with within-region talk-groups have the time slot 2, all others have the time slot 1. The first channel DB0LDS TS1 is a generic channel for the time slot 1. There is no default transmit contact defined for this channel. This channel can be used to perform arbitrary direct and group calls by selecting a contact or talk group from the contact list. This means, that a call cannot be started by simply pressing PTT on that channel. First, a contact must be selected that should get called. The second channel DB0LDA DL TS1 is almost identical to the first except for the default transmit contact. Here Germany (TG262) is selected. This means, if this channel is selected and the PTT is pressed, the talk group 262 is called. By configuring a separate channel for this talk group allows to start a call to it without having to search for it in the contact list. This also allows to temporarily subscribe this talk group on a repeater easily, by simply pressing PTT briefly. Irrespective of the default transmit contact, you can always answer to a call within the hang time. The third channel DB0LDS Sa/Th TS1 is also similar to the first two. Here the default transmit contact is the talk group for Saxony/Thuringia (TG2629) to be able to subscribe that talk group at my local repeater and call it easily. Please not that for this channel the time slot 1 is used. The repeater is located in Brandenburg and therefore any communication with Saxony is inter-regional and should happen on the time slot 1. The group list contains only the talk group for Saxony/Thuringia and thus other inter-regional talk groups are not received on that channel. Channels four, five and six are for repeater-local (TG9), regional (TG8) and the talk group Berlin/Brandenburg (TG2621) calls. As this is all regional communication, it happens on the time slot 2. Also they all have the group list Ber/Bra set (see ). Therefore, all regional talk groups (TG8, TG9, TG2621) are received on that channel. As the default transmit contact, the corresponding talk group is set. If the channel DB0LDS TG9 TS2 is selected and the PTT is pressed, a call to TG9 is repeated only by the repeater DB0LDS. If the channel DB0LDS BB TS2 is selected and the PTT is pressed, a call to TG2621 is repeater by almost all repeaters in the region Berlin/Brandenbug. Therefore, chose a talk group that is sufficient for you intended communication. On any channel, you can start an arbitrary call (group, privat, all) by either selecting the contact from the contact list of even simply entering the DMR number into the keypad of the radio. This is independent from the default contact on the current channel. In the end, the default transmit contact is a convenience feature. With the default contact, channels for frequently used contacts can be created. The so-called Admit Criterion should be set to Color Code for DMR repeater channel. This means, that the radio will only transmit if the channel is free and the color code of the repeater matches the color code of the channel.
      Other channel options The user interface of the manufacturer CPS where you configure the channels, is usually very extensiv. There is a huge amount of options that control the behavior of the channel. The majority of these options are not used in ham radio applications. Some of these, however, I want to describe here briefly. The Admit Criterion was mentioned before. It controls under which conditions the radios can transmit. There are usually three options. Always does exactly what it says: it allows to transmit always. This option should be chosen for analog FM repeater channels. Channel free means that the radio will only transmit if the current channel is free. This option should be choses for simplex channels. When Color code is selected, the radio will only transmit if the channel is free and the color code of the repeater matches the color code of the channel. This option should be chosen for DMR repeater channels. The TOT setting or transmit timeout secifies the maximum duration of continuous transmission. After that period of continuous transmission, the radio will stop the transmission automatically. The feature is used in commercial applications to avoid the blocking of a channel or talk group by a participant. This option has little sense in amateur radio and can be set to infinity. The Emergency System is a method to signal an alarm or an emergency situation. Also this feature is not used in amateur radio. The option Privacy Group or Encryption Key refers to a built-in method of encrypting the traffic. This is actually forbidden in amateur radio. The flags Emergency Alarm Confirmed, Private Call Confirmed and Data Call Confirmed specify how the radio starts these calls. The radio will first establish a call to the destination and will signal once the call is confirmed. Once the confirmation is received, the actual call starts. These options are not used in amateur radio and should be disabled as they may interfere with the normal operation. The option Talkaround allows to operate simplex on a repeater channel. That is the radio transmits and receives on the repeater input frequency. This allows to bypass the repeater and to communicate directly with other participants on the same repeater channel. Also this option makes little sense in amateur radio. When the RX Only flag is enabled, the radio cannot transmit on that channel. This may be useful for out-of-band monitoring channels where you are not allowed to transmit. The VOX feature is actually used in ham radio. It stands for voice operated switch and allows to start a call using the voice without the need to press PTT. Some radios allow to enable this option on a per-channel bases others only radio-wide. The Power option allows to specify the transmit power level. This can usually be set in predefined steps like Low, Middle, High. Some radios may also allow a fine grained setting of the power level. The Scan List specifies a list of channel that are scanned if a scan is started on that channel. This feature might be used as an alternative to a missing roaming feature (see ).
      Assembling zones Once you have assembled all channels of interest, you may notice that the list is quite long. Hence all DMR radios organize the channels in so-called Zones. Zones are simple lists of channels that group them into relevant sets, usually based on the location. You may therefore collect all channels for Home, Work and Holidays into one zone each. How you organize your zones is up to you. You may also organize these channels by talk groups. This way you may implement some kind of a manual roaming. Once you left the range of a repeater you may search for another one in the same zone. This way you stay connected to a particular talk group. In contrast to the automatic roaming, you have to select the repeater by hand. Channels that are not assigned to any zone are usually not selectable by the radio. It is, however, perfectly fine to assign a channel to several zones.
      Assembling scan lists Scan Lists are simple lists of channels. When the scan is started on a particular channel, the channels scan list is used. The radio will then step though that list and may stop on a channel that shows activity. It is then possible to answer the received call. This function allows for observing several channels. Additionally, it is usually possible to specify one or more priority channels for a scan list. This channel is then visited more frequently and thus monitored more intensively.
      qdmr-0.12.3/doc/manual/intro/fig/000077500000000000000000000000001501654372000164655ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/intro/fig/Makefile000066400000000000000000000011301501654372000201200ustar00rootroot00000000000000IMG = fm_simplex_a.png fm_simplex_b.png fm_duplex_a.png fm_duplex_b.png fm_echolink_a.png fm_echolink_b.png fm_echolink_c.png \ trunk_net_ex1.png trunk_net_ex2.png trunk_net_ex3.png trunk_net_ex4a.png trunk_net_ex4b.png \ simplex_allcall.png simplex_groupcall.png simplex_privatecall.png \ repeater_local.png repeater_privatecall.png \ talkgroup_ex1a.png talkgroup_ex1b.png talkgroup_ex1c.png \ timeslot.png #.PRECIOUS: %.pdf # keep PDFs all: $(IMG) %.png: %.pdf convert -density 300 -depth 8 -quality 85 $< $@ %.pdf: %.tex pdflatex $< clean: rm -f $(IMG) rm -f *.aux *.log qdmr-0.12.3/doc/manual/intro/fig/fm_duplex_a.tex000066400000000000000000000006541501654372000214770ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{U1}{0,0}{DM3MAT}; \activerepeater{R1}{3,1}{DL0LDS}; \user{U2}{6,0}{DL2XYZ}; \path[->] (U1) edge node[above,rotate=17] {$431.9625 MHz$} (R1) ; \path[->] (R1) edge node[above,rotate=-17] {$439.5625 MHz$} (U2); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/fm_duplex_b.tex000066400000000000000000000006541501654372000215000ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \user{U1}{0,0}{DM3MAT}; \activerepeater{R1}{3,1}{DL0LDS}; \activeuser{U2}{6,0}{DL2XYZ}; \path[->] (U2) edge node[above,rotate=-17] {$431.9625 MHz$} (R1) ; \path[->] (R1) edge node[above,rotate=17] {$439.5625 MHz$} (U1); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/fm_echolink_a.tex000066400000000000000000000006521501654372000217700ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{U1}{0,0}{DM3MAT}; \activerepeater{R1}{3,1}{DB0SP}; \repeater{R2}{6,1}{DB0LDS}; \user{U2}{9,0}{DL2XYZ}; \path[->] (U1) edge node[above,rotate=17] {$DTMF: 662699$} node[below,rotate=17]{$431.825 MHz$} (R1) ; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/fm_echolink_b.tex000066400000000000000000000010161501654372000217640ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{U1}{0,0}{DM3MAT}; \activerepeater{R1}{3,1}{DB0SP}; \activerepeater{R2}{6,1}{DB0LDS}; \user{U2}{9,0}{DL2XYZ}; \path[->] (U1) edge node[above,rotate=17] {$431.825 MHz$} (R1) ; \path[->,dashed] (R1) edge node[above] {via Echolink} (R2) ; \path[->] (R2) edge node[above,rotate=-17] {$439.150 MHz$} (U2) ; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/fm_echolink_c.tex000066400000000000000000000010161501654372000217650ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \user{U1}{0,0}{DM3MAT}; \activerepeater{R1}{3,1}{DB0SP}; \activerepeater{R2}{6,1}{DB0LDS}; \activeuser{U2}{9,0}{DL2XYZ}; \path[->] (R1) edge node[above,rotate=17] {$439.425 MHz$} (U1) ; \path[->,dashed] (R2) edge node[above] {via Echolink} (R1) ; \path[->] (U2) edge node[above,rotate=-17] {$431.875 MHz$} (R2) ; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/fm_simplex_a.tex000066400000000000000000000004711501654372000216540ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{U1}{0,0}{DM3MAT}; \user{U2}{6,0}{DL2XYZ}; \path[->] (U1) edge node[above] {$144.500 MHz$} (U2) ; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/fm_simplex_b.tex000066400000000000000000000004711501654372000216550ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \user{U1}{0,0}{DM3MAT}; \activeuser{U2}{6,0}{DL2XYZ}; \path[->] (U2) edge node[above] {$144.500 MHz$} (U1) ; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/repeater.tex000066400000000000000000000025021501654372000210150ustar00rootroot00000000000000\newcommand{\repeater}[3]{% \node ({#1}) at ({#2}) {% \begin{tikzpicture}% \draw [black,thick] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);% \draw [black,thick,domain=-45:225] plot ({0.2*cos(\x)}, {0.5+0.2*sin(\x)});% \draw [black,thick,domain=-45:225] plot ({0.4*cos(\x)}, {0.5+0.4*sin(\x)});% \node (xxx) at (0,-.2) {{#3}};% \end{tikzpicture}% } % } \newcommand{\activerepeater}[3]{% \node ({#1}) at ({#2}) {% \begin{tikzpicture}% \draw [black,thick] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);% \draw [red,thick,domain=-45:225] plot ({0.2*cos(\x)}, {0.5+0.2*sin(\x)});% \draw [red,thick,domain=-45:225] plot ({0.4*cos(\x)}, {0.5+0.4*sin(\x)});% \node (xxx) at (0,-.2) {{#3}};% \end{tikzpicture}% } % } \newcommand{\user}[3]{% \node ({#1}) at ({#2}) {% \begin{tikzpicture}% \draw [black,fill=black] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);% \draw [black,fill=black] (0,.5) circle (.2); % \node (xxx) [text width=0.6cm, align=center] at (-.35cm,-.4) {{#3}};% \end{tikzpicture}% } % } \newcommand{\activeuser}[3]{% \node ({#1}) at ({#2}) {% \begin{tikzpicture}% \draw [red,fill=red] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);% \draw [red,fill=red] (0,.5) circle (.2); % \node (xxx) [text width=0.6cm, align=center] at (-.35cm,-.4) {{#3}};% \end{tikzpicture}% } % } qdmr-0.12.3/doc/manual/intro/fig/repeater_local.tex000066400000000000000000000016101501654372000221660ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT}; \user{u2}{ 2,0}{DL1XYZ}; \user{u3}{ 4,0}{DL2XYZ}; \draw[dotted] (5,4) -- (5,-1); \activeuser{u4}{ 6,0}{DL3XYZ}; \user{u5}{ 8,0}{DL4XYZ}; \user{u6}{10,0}{DL5XYZ}; \activerepeater{R1}{1,3}{DB0ABC}; \repeater{R2}{3,3}{DB0DEF}; \activerepeater{R3}{7,3}{DB0GHI}; \activerepeater{R4}{9,3}{DB0JKL}; \draw[->] (u1) -- node[above,rotate=70]{GC: TG9} (R1); \draw[->] (R1) -- node[above,rotate=-70]{GC: TG9} (u2); \draw[->] (u4) -- node[above,rotate=70]{GC: TG8} (R3); \draw[->] (R3) -- node[above,rotate=-70]{GC: TG8} (u5); \draw[->] (R4) -- node[above,rotate=-70]{GC: TG8} (u6); \path[->] (R3) edge[dashed,bend left] node[above]{via network} (R4); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/repeater_privatecall.tex000066400000000000000000000011001501654372000233740ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT 2621370}; \activerepeater{R1}{1,3}{DB0ABC}; \draw[dotted] (2,4) -- (2,-1); \user{u2}{ 4,0}{I/DL2XYZ\\2621234}; \activerepeater{R2}{3,3}{I0ABC}; \draw[->] (u1) -- node[above,rotate=70]{PC: 2621234} (R1); \draw[->] (R2) -- node[above,rotate=-70]{PC: 2621234} (u2); \path[->] (R1) edge[dashed,bend left] node[above]{via network} (R2); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/simplex_allcall.tex000066400000000000000000000011721501654372000223550ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT}; \user{u2}{ 6,2}{DL1XYZ, TG99}; \user{u3}{ 6,0}{DL2XYZ, TG99}; \user{u4}{ 6,-2}{DL3XYZ}; \path[->] (u1) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{All Call} (u2); \path[->] (u1) edge node[above]{$433.450 MHz$} node[below]{All Call} (u3); \path[->] (u1) edge[bend right] node[above, rotate=-10]{$433.450 MHz$} node[below, rotate=-10]{All Call} (u4); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/simplex_groupcall.tex000066400000000000000000000010101501654372000227300ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT}; \user{u2}{ 6,2}{DL1XYZ, TG99}; \user{u3}{ 6,0}{DL2XYZ, TG99}; \user{u4}{ 6,-2}{DL3XYZ}; \path[->] (u1) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{GC: TG99} (u2); \path[->] (u1) edge node[above]{$433.450 MHz$} node[below]{GC: TG99} (u3); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/simplex_privatecall.tex000066400000000000000000000010531501654372000232550ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT}; \activeuser{u2}{ 6,2}{DL1XYZ, TG99}; \user{u3}{ 6,0}{DL2XYZ, TG99}; \user{u4}{ 6,-2}{DL3XYZ}; \path[->] (u1) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{PC: DL1XYZ} (u2); \path[->] (u2) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{PC: DM3MAT} (u1); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/talkgroup_ex1a.tex000066400000000000000000000013331501654372000221350ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT}; \user{u2}{ 2,0}{DL1XYZ}; \user{u3}{ 6,0}{DL2XYZ}; \draw[dotted] (7,4) -- (7,-1); \user{u4}{10,0}{I/DL3XYZ}; \activerepeater{R1}{1,3}{DB0ABC, TG2621}; \activerepeater{R2}{5,3}{DB0DEF, TG2621}; \repeater{R3}{9,3}{I0ABC}; \path[->] (u1) edge node[above,rotate=70]{GC: TG2621} (R1); \path[->] (R1) edge node[above,rotate=-70]{GC: TG2621} (u2); \path[->] (R2) edge node[above,rotate=-70]{GC: TG2621} (u3); \path[->] (R1) edge[bend left] node[above]{GC: TG2621} (R2); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/talkgroup_ex1b.tex000066400000000000000000000015551501654372000221440ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \user{u1}{ 0,0}{DM3MAT}; \user{u2}{ 2,0}{DL1XYZ}; \user{u3}{ 6,0}{DL2XYZ}; \draw[dotted] (7,4) -- (7,-1); \activeuser{u4}{10,0}{I/DL3XYZ}; \activerepeater{R1}{1,3}{DB0ABC, TG2621}; \activerepeater{R2}{5,3}{DB0DEF, TG2621}; \activerepeater{R3}{9,3}{I0ABC, (TG2621)}; \path[->] (u4) edge node[above,rotate=-70]{GC: TG2621} (R3); \path[->] (R1) edge node[above,rotate=70]{GC: TG2621} (u1); \path[->] (R1) edge node[above,rotate=-70]{GC: TG2621} (u2); \path[->] (R2) edge node[above,rotate=-70]{GC: TG2621} (u3); \path[->] (R3) edge[bend right] node[below]{GC: TG2621} (R2); \path[->] (R3) edge[bend right] node[above]{GC: TG2621} (R1); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/talkgroup_ex1c.tex000066400000000000000000000015531501654372000221430ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{u1}{ 0,0}{DM3MAT}; \user{u2}{ 2,0}{DL1XYZ}; \user{u3}{ 6,0}{DL2XYZ}; \draw[dotted] (7,4) -- (7,-1); \user{u4}{10,0}{I/DL3XYZ}; \activerepeater{R1}{1,3}{DB0ABC, TG2621}; \activerepeater{R2}{5,3}{DB0DEF, TG2621}; \activerepeater{R3}{9,3}{I0ABC, (TG2621)}; \path[->] (u1) edge node[above,rotate=70]{GC: TG2621} (R1); \path[->] (R1) edge node[above,rotate=-70]{GC: TG2621} (u2); \path[->] (R2) edge node[above,rotate=-70]{GC: TG2621} (u3); \path[->] (R3) edge node[above,rotate=-70]{GC: TG2621} (u4); \path[->] (R1) edge[bend left] node[below]{GC: TG2621} (R2); \path[->] (R1) edge[bend left] node[above]{GC: TG2621} (R3); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/timeslot.tex000066400000000000000000000024311501654372000210470ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \usetikzlibrary{patterns,snakes} \input{repeater} \begin{document} \begin{tikzpicture} \draw[|-,dotted, semithick] (-1,-0.2) -- (0,-0.2); \draw[|-,semithick] (0,-0.2) -- (1,-0.2); \draw[|-,semithick] (1,-0.2) -- (2,-0.2); \draw[|-,semithick] (2,-0.2) -- (3,-0.2); \draw[|-,semithick] (3,-0.2) -- (4,-0.2); \draw[|-,semithick] (4,-0.2) -- (5,-0.2); \draw[|-,semithick] (5,-0.2) -- (6,-0.2); \draw[|->,dotted,semithick] (6,-0.2) -- (7,-0.2); \node at (7, -.5) {$t$}; \draw [thick,decoration={brace,mirror},decorate] (0,-0.4) -- (1,-0.4) node [pos=0.5, anchor=north,yshift=-0.55] {$30\ ms$}; \fill[red!30] (0.1,0) -- (0.1,1) -- (0.9,1) -- (0.9,0) -- cycle; \node at (0.5,0.5) {TS 1}; \fill[blue!30] (1.1,0) -- (1.1,1) -- (1.9,1) -- (1.9,0) -- cycle; \node at (1.5,0.5) {TS 2}; \fill[red!30] (2.1,0) -- (2.1,1) -- (2.9,1) -- (2.9,0) -- cycle; \node at (2.5,0.5) {TS 1}; \fill[blue!30] (3.1,0) -- (3.1,1) -- (3.9,1) -- (3.9,0) -- cycle; \node at (3.5,0.5) {TS 2}; \fill[red!30] (4.1,0) -- (4.1,1) -- (4.9,1) -- (4.9,0) -- cycle; \node at (4.5,0.5) {TS 1}; \fill[blue!30] (5.1,0) -- (5.1,1) -- (5.9,1) -- (5.9,0) -- cycle; \node at (5.5,0.5) {TS 1}; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/trunk_net_ex1.tex000066400000000000000000000010541501654372000217750ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \user{r1}{ 0,0}{Clean 1}; \user{r2}{ 2,0}{Clean 2}; \draw[dotted] (3,4) -- (3,-1); \user{s1}{ 4,0}{Security 1}; \user{z} { 6,0}{HQ}; \draw[dotted] (7,4) -- (7,-1); \user{s2}{ 8,0}{Security 2}; \user{r3}{10,0}{Clean 3}; \repeater{R1}{1,3}{Terminal 1, TG: C,S}; \repeater{R2}{5,3}{Terminal 2, TG: C,S}; \repeater{R3}{9,3}{Apron, TG: S}; \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/trunk_net_ex2.tex000066400000000000000000000016441501654372000220030ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{r1}{ 0,0}{Clean 1}; \user{r2}{ 2,0}{Clean 2}; \draw[dotted] (3,4) -- (3,-1); \activeuser{s1}{ 4,0}{Security 1}; \activeuser{z} { 6,0}{HQ}; \draw[dotted] (7,4) -- (7,-1); \user{s2}{ 8,0}{Security 2}; \activeuser{r3}{10,0}{Clean 3}; \activerepeater{R1}{1,3.5}{Terminal 1, TG: C,S}; \activerepeater{R2}{5,3.5}{Terminal 2, TG: C,S}; \activerepeater{R3}{9,3.5}{Apron, TG: S}; \draw[->] (r1) -- node[above,rotate=74] {PC: Clean 3} (R1); \path[->,dashed] (R1) edge [bend left] node[above] {via network} (R3); \draw[->] (R3) -- node[above,rotate=-74] {PC: Clean 3} (r3); \draw[->] (z) -- node[above,rotate=-74] {PC: Security 1} (R2); \draw[->] (R2) -- node[above,rotate=74] {PC: Security 1} (s1); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/trunk_net_ex3.tex000066400000000000000000000015161501654372000220020ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{r1}{ 0,0}{Clean 1}; \activeuser{r2}{ 2,0}{Clean 2}; \draw[dotted] (3,4) -- (3,-1); \user{s1}{ 4,0}{Security 1}; \activeuser{z} { 6,0}{HQ}; \draw[dotted] (7,4) -- (7,-1); \user{s2}{ 8,0}{Security 2}; \user{r3}{10,0}{Clean 3}; \activerepeater{R1}{1,3}{Terminal 1, TG: C,S}; \activerepeater{R2}{5,3}{Terminal 2, TG: C,S}; \repeater{R3}{9,3}{Apron, TG: S}; \draw[->] (z) -- node[above,rotate=-74] {TG: C} (R2); \path[->,dashed] (R2) edge [bend right] node[above] {via network} (R1); \draw[->] (R1) -- node[above,rotate=74] {TG: C} (r1); \draw[->] (R1) -- node[above,rotate=-74] {TG: C} (r2); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/trunk_net_ex4a.tex000066400000000000000000000015151501654372000221430ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{r1}{ 0,0}{Clean 1}; \activeuser{r2}{ 2,0}{Clean 2}; \draw[dotted] (3,4) -- (3,-1); \user{s1}{ 4,0}{Security 1}; \user{z} { 6,0}{HQ}; \draw[dotted] (7,4) -- (7,-1); \user{s2}{ 8,0}{Security 2}; \user{r3}{10,0}{Clean 3}; \activerepeater{R1}{1,3}{Terminal 1, TG: C,S}; \repeater{R2}{5,3}{Terminal 2, TG: C,S}; \activerepeater{R3}{9,3}{Apron, TG: S,(C)}; \draw[->] (r3) -- node[above,rotate=-74] {TG: C} (R3); \path[->,dashed] (R3) edge [bend right] node[above] {via network} (R1); \draw[->] (R1) -- node[above,rotate=74] {TG: C} (r1); \draw[->] (R1) -- node[above,rotate=-74] {TG: C} (r2); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/fig/trunk_net_ex4b.tex000066400000000000000000000017441501654372000221500ustar00rootroot00000000000000\documentclass{standalone} \usepackage{tikz} \usetikzlibrary{shapes.geometric} \input{repeater} \begin{document} \begin{tikzpicture}[every node/.style={scale=.8}] \activeuser{r1}{ 0,0}{Clean 1}; \activeuser{r2}{ 2,0}{Clean 2}; \draw[dotted] (3,4) -- (3,-1); \user{s1}{ 4,0}{Security 1}; \activeuser{z} { 6,0}{HQ}; \draw[dotted] (7,4) -- (7,-1); \user{s2}{ 8,0}{Security 2}; \activeuser{r3}{10,0}{Clean 3}; \activerepeater{R1}{1,3}{Terminal 1, TG: C,S}; \activerepeater{R2}{5,3}{Terminal 2, TG: C,S}; \activerepeater{R3}{9,3}{Apron, TG: S,(C)}; \draw[->] (z) -- node[above,rotate=-74] {TG: C} (R2); \path[->,dashed] (R2) edge [bend right] node[above] {via Netzwerk} (R1); \path[->,dashed] (R2) edge [bend left] node[above] {via Netzwerk} (R3); \draw[->] (R1) -- node[above,rotate=74] {TG: C} (r1); \draw[->] (R1) -- node[above,rotate=-74] {TG: C} (r2); \draw[->] (R3) -- node[above,rotate=-74] {TG: C} (r3); \end{tikzpicture} \end{document} qdmr-0.12.3/doc/manual/intro/foreknowledge.xml000066400000000000000000000150731501654372000213030ustar00rootroot00000000000000
      Basics: Repeater operations This section briefly describes the common amateur radio FM-repeater operation. The majority of all licensed operators will be familiar with this topic. If you are not yet licensed and interested into amateur radio, consider reading this section. Otherwise, skip right to The majority of connections between HAM operators are made in the so-called simplex mode. That is, the two operators transmit and receive alternately on the same frequency This is actually called semi-duplex, however the term simplex stuck. The term simplex actually refers to the situation, where there is only one transmitter and possibly many receivers (e.g., broadcast). and the connection is direct. This works very well on HF where world-wide direct connections can be made. Typical FM simplex operation For this example, DM3MAT transmits directly to DL2XYZ on the frequency 144.500 MHz. The latter then also answers directly on the same frequency. On higher frequencies, however, radio waves behave more like light and it gets increasingly difficultAlso on VHF and UHF, larger distances can be bridged using an elevated location and larger antennas. to bridge significant distances beyond the horizon. This fact limits the operating range of simple hand-held radios. To still cover a large area, repeaters can be used. Repeaters are autonomous amateur radio stations that are usually located on a mountain, hill or high tower. This allows them to easily cover a large area. They receive signals from HAM operators and retransmit them at the same time. To do that, they cannot send and receive on the same frequency (otherwise they would interfere with themselves). Therefore, repeaters operate in the so-called duplex mode. That is, the repeater receives on one frequency (the so-called input frequency) and transmits the received signal on another frequency (the output frequency) simultaneously. Simple FM repeater operation For this example, DM3MAT sends on the input frequency 431.9625 MHz to the repeater DB0LDS. The repeater transmits the received signal on its output frequency 439.5625 MHz. On that frequency, DL2XYZ receives the original call. The shows a common repeater operation on UHF. Here, the operator DM3MAT transmits its call to DL2XYZ not directly but on the input frequency of the repeater DL0LDS (431.9625 MHz). The repeater receives the call and transmits it simultaneously on its output frequency (439.5625 MHz). This signal is then received by DL2XYZ. Consequently, the call has reached its destination, although DM3MAT and DL2XYZ may not be able to communicate directly. The reply of DL2XYZ to DM3MAT follows the same path. Here DL2XYZ transmits on the repeater input frequency, and DM3MAT receives that call on the repeater output frequency. This way the two operators can communicate with each other even if they are not able to reach each other directly.
      Echolink However, there are situations, where two operators are far away and they cannot reach the same repeater. For these cases, it is possible to connect two repeaters via the EchoLink network. Repeater operation with Echolink DM3MAT connects repeaters DB0SP (near Berlin) and DB0LEI (near Leipzig) via EchoLink. Now, they are able to communicate with each other. This network allows to link FM repeaters via internet or to connect directly to a remote repeater via internet. Many FM repeaters are connected to the EchoLink network, allowing for world-wide communication with simple hand-held radios. Frequently, it is also possible to control a repeater over-the-air and to connect that repeater to some other repeater via EchoLink. Usually, this can be done by sending the EchoLink number of the destination repeater using DTMF to the local repeater. This is shown in above. There, DM3MAT sends the EchoLink number 662699 of the repeater DB0LEI near Leipzig to his local repeater DB0SP near Berlin. Then the local repeater (DB0SP) will link with the destination repeater (DB0LEI) via the EchoLink network. For some limited time, both repeaters will act like one logical repeater. That is, everything that is received by one repeater will also be transmitted by the other. This way, the two operators DM3MAT and DL2XYZ can communicate although they cannot reach a common repeater. Once two repeaters are linked via EchoLink, they behave like a single repeater. All over the world, there are FM repeater that are part of the EchoLink network. Therefore, it is possible to communicate world-wide at any time using simple hand-held radios that are as cheap as 40€ or even less.
      qdmr-0.12.3/doc/manual/intro/introduction.xml000066400000000000000000000033221501654372000211630ustar00rootroot00000000000000 Introduction The chapter tries to provide an introduction to DMR (digital mobile radio) targeted at the unexperienced operator and anyone interested in this topic. I try to hide details until it gets absolutely necessary to explain them. The majority of DMR introductions I've found, are more or less extensive glossaries (if you are interested in that, see ). They are hard to comprehend, unless one has at least some experience with DMR. The perceived complexity of DMR comes from its origin as a radio standard for commercial applications at large events and companies (i.e., trunked radio). Therefore, I will first describe an example how DMR is used commercially before I describe how it is used in amateur radio. I hope that this approach will make some of the weird terms and concepts of DMR clearer. qdmr-0.12.3/doc/manual/intro/local.xml000066400000000000000000000046761501654372000175510ustar00rootroot00000000000000
      Local Repeater Operation One central objective of DMR is to be repeater transparent. That is, it does not matter which repeater you use. You will always be able to reach the same groups and be always reachable through the same means (private or group call). This concept is violated by the talk groups 8 & 9. The are the regional and local talk groups. Two regions with two repeaters each. The talk group 9 (TG9) is the so-called local talk group. Group calls to that talk group are not forwarded though the network and only retransmitted locally. Usually locally means only the repeater. Sometimes, however, these calls are also forwarded to other repeaters nearby. This case is shown in on the left side. Here DM3MAT sends a group call to TG9 via the repeater DB0ABC. This call is not forwarded to any other repeater and thus is only received in the local area around the repeater. DL1XYZ is in that local area and may receive that call if he configured his radio to receive calls from the TG9. The talk group 8 (TG8) is the so-called regional talk group. A call to that talk group is usually forwarded to all repeaters within a specific region. Which repeaters are part of a region, is a decision of the repeater administrators. So it is hard to predict to which repeaters a regional call gets forwarded. In on the right side, DL3XYZ sends a group call to TG8 to the repeater DB0GHI. This call gets forwarded to all repeaters in the region. In this case, also to the repeater DB0JKL within the same region. Therefore, all participants within that region are able to receive the group call if they configured their radios accordingly. In this example, not only DL4XYZ received the call but also DL5XYZ who is not close to the repeater DB0GHI and would have missed a group call to the local talk group.
      qdmr-0.12.3/doc/manual/intro/networks.xml000066400000000000000000000064441501654372000203260ustar00rootroot00000000000000
      DMR Networks Within the previous sections, I tried to outline the concepts and some technical details of a DMR network and how a codeplug might be assembled. These concepts, however, apply only to the so-called Brandmeister network. This is the network in the background that routes your private and group calls, connects repeaters etc. In Germany, this is the dominant network. But also world wide, it connects the majority of repeaters (about 5000). However, there are also other networks. There is the DMR-MARC network and the DMR+ network. Which network you are likely to encounter, depends on your location. In countries like France, Spain, BeNeLux, Poland, Czech Republic and Slovakia, almost all repeaters are connected to the Brandmeister network, while in Denmark the DMR+ network dominates. In the USA and Austria, DMR-MARC repeaters aren't rare. All these networks do not differ on the technical level. That is, you DMR-ID is valid in all of these networks and you can use any DMR Tier II radio. The concepts however, in particular how group calls are performed, depends heavily on the network. This means, that you need to configure the repeater channels for a DMR+ repeater in a different way compared to a Brandmeister repeater.
      Reflectors (DMR+) Reflectors play an important role in the DMR+ network. They represent a talk group within the DMR+ network. The major difference between a reflector and a talk group is, that they cannot be simply called using a group call. They are subscribed to a local repeater by a private call to the reflector. Then all repeaters subscribed to that reflector behave like a single repeater. You will then participate on that reflector by performing a group call to TG9, the local talk group. Your call will then be sent to the reflector as well as to all other reflectors currently subscribed on the repeater and consequently to all repeaters also subscribed to that reflector. This has the advantage of a much simpler codeplug assembly, as only two channels are configured for each repeater. One for each time slot. The default transmit contact will always be the local talk group TG9. To subscribe a reflector, a private call is started to the reflector from the contact list. This implies that the contact list should contain all reflectors you are interested in. This concept is also much closer to the semi-analog concepts of EchoLink. However, advanced features like roaming are not possible this way. Also the repeater transparency gets lost. Instead of simply starting a group call to the destination talk group, the local repeater needs to be configured. Once that configuration is done, the communication will happen on the local talk group TG9, even if the communication is not local anymore.
      qdmr-0.12.3/doc/manual/intro/origin.xml000066400000000000000000000230701501654372000177330ustar00rootroot00000000000000
      DMR Introduction & Origin DMR (digital mobile radio) is a digital radio standard to transmit speech and data. That is, the speech is not directly modulated on the carrier but is first digitalized and compressed using a lossy compression codec (VOCODER). The compressed speech is then transmitted as data packets. The latter allows to attach meta-information to the data packet like source and destination of the packet. DMR was designed to be the digital replacement for analog trunking networks in commercial applications. A classic example for such a commercial application of DMR would be a civil air port. With this, I do not mean the air-traffic radio but all the communication of the ground staff in and around the actual air port buildings. At such an imaginary air port, there is a huge staff with a wide variety of tasks. For example (without any claim of completeness): The cleaning crew, technicians, security staff, apron staff for refueling, luggage and catering, the fire brigade and the headquarters. All these people carry a radio and should be able to Directly call the headquarters. All staff should be able to call the headquarters directly. Direct communication between members of the same group, without interfering with other groups. For example, the cleaning staff should be able to communicate with each other, without interfering with the fire brigade. Each person should be able to call a complete group. For example, the headquarters may call the entire fire brigade or one member of the security staff may call the entire security for help. An air port, however, is a rather large area. Consequently, not all staff members are able to reach all others. Therefore, some repeaters must be installed to cover the entire air port including all interiors. If you compare this scenario with the classic FM repeater networks (see ), it gets clear that is hard to implement these concepts using analog FM repeaters. Especially, if several repeaters are connected though a network. In this case, a single call on one repeater may block the entire network There are means to implement this concept on analog repeater networks using tone-signaling techniques (e.g., DTMF, five-tone etc.).. Certainly, it would be much better if only those repeaters get activated that are actually required for the communication between two parties. Then, the remaining repeaters are still available for the rest of the staff. This routing, however, should happen automatically. An operator may not know, which repeater to use to reach a particular person. DMR was designed to implements such complex communication networks without requiring from every participant to have detailed knowledge about the structure of the network. That is, the knowledge about where every repeater is installed and which participants are reachable on which repeater. DMR is more similar to a phone network that to classic FM repeater networks. Speaking of phone networks: Each participant and thus his/her radio is uniquely identified by a number. The DMR-ID. This is a number between 1 and 16777215. And like for any other phone network, each participant may call any other directly using this number. This call is called Private Call. And there are groups. Each of these so-called Talk Groups is also assigned a unique number. A talk group can be used to group all staff with a specific task (e.g., the security, fire brigade, etc.). It is then possible to reach all members of this group at once, by performing a Group Call to that talk group. The network, however, does not know which participant is member of which group. Consequently, the radio of the participant needs to know which group calls to accept and which to ignore. This point is important to remember: The DMR network does not know which participant is member of which group. The radio needs to be configured to react on specific group calls. Simplified air-port network There are 3 cleaning staff, two security and one headquarters. To cover the entire area, three repeaters are required. One in terminal 1, one in Terminal 2 and one on the apron. is a simplified air port network (in reality, it is much larger and way more complex). Consider the situation, where a cleaning stuff 1 & 3 want to communicate. At the same time, the headquarters want to talk to security 1. In an simple analog network, the call between cleaning 1 & 3 would block the entire network and therefore the call between the headquarters and security 1. Simultaneous calls Two Simultaneous private calls in the example network between cleaning 1 & 3 as well as between headquarters and security 1. Private calls in DMR networks only use those repeaters, that are actually required to establish the communication. This is shown in : Cleaning 1 starts a private call to cleaning 3. As the DMR network knows that cleaning 3 was last active on the apron repeater, this call gets routed only though repeaters terminal 1 and apron. The repeater in terminal 2, however, is not affected. Consequently, this repeater remains available for the call between the headquarters and security 1. The network only knows at which repeater each participant was last active. The network will therefore try to establish a connection though that repeater to the participant. During the call between cleaning 1 & 3, the repeaters in terminal 1 and apron are blocked. This means, that the headquarters may not be able to reach cleaning 2 and security 2 immediately. This sounds worse than it actually is. In contrast to classic phone networks, a direct call is considered interrupted, once the calling participant releases the PTT button. To this end, the headquarters may use the pauses between calls to reach the other participants. In the next , the headquarters want to reach all cleaning staff. Therefore, they start a group call to the talk group cleaning (C for cleaning and S for security). With this call, it can reach cleaning 1 & 2 immediately. However, cleaning 3 does not receive that call. This is due to the fact, that the DMR network does not know which participants are members of which groups. As the cleaning crew is usually not on the apron, the apron repeater has not subscribed the talk group cleaning. Therefore, it does not forward group calls to that talk group. Temporary subscription of talk groups. To remain reachable for group calls, cleaning 3 needs to temporarily subscribe the apron repeater to the cleaning talk group. This can be done by starting a group call to that talk group on the apron repeater. Then the repeater will temporarily subscribe to that talk group for a limited amount of time (usually between 10-30min). During that time, the repeater will forward group calls to that talk group and cleaning 3 remains reachable via that repeater. This temporary subscription will be renewed every time a participant starts a group call to that talk group on this repeater. With these examples, the most basic terms of DMR (DMR-ID, talk group, private and group calls as well as talk group subscriptions where introduced and explained on an example network. The following sections will concern the use DMR in ham radio.
      qdmr-0.12.3/doc/manual/intro/private.xml000066400000000000000000000052731501654372000201230ustar00rootroot00000000000000
      Private calls Private calls allow to call other participants directly without interfering with other calls (except for using the repeater). In the introduction into DMR above, the private call over several repeaters has been described. I consider this aspect of DMR particularly interesting. With the exception of TG 8 & 9, private and group calls are transparent with respect to the repeaters used. It simply does not matter which repeaters are actually used to establish a connection. Therefore, I do not need ot know where the other participants are located. Direct calls between countries. Consider the typical vacation situation: An OM at his holiday location may want to participate in his local afternoon net. He can do that by simply starting a group call to the nets talk group over the repeater at his holiday location. Now the local holiday repeater has subscribed to the local talk group at home and the OM can participate as usual in the net. The other participants in the net may not even recognize that the OM is at his holiday location. In a similar fashion, private calls can be started and received at the holiday location. The DMR network knows the repeater a participant was last active on. By briefly pressing the PTT at the holiday repeater, the OM is registered and can now receive private calls at his holiday location. The friends at home may not even know where the OMs current location is nor which repeater to use to reach him. The network takes care of that. shows such a private call between countries. DM3MAT starts a private call to DL2XYZ using his local repeater DB0ABC. DL2XYZ, however, is at his holiday location in Italy. As he had registered himself at that repeater, the private call gets forwarded to the holiday repeater I0ABC in Italy, where DL2XYZ can receive it. DM3MAT does not need to know the location of the callee nor which repeaters are near to him. This automatic routing of calls (group and private calls) is a major advantage over the analog FM repeater network and EchoLink. For the latter, the ID of the destination repeater needs to be known.
      qdmr-0.12.3/doc/manual/intro/roaming.xml000066400000000000000000000047241501654372000201050ustar00rootroot00000000000000
      Roaming Usually all repeater within a region will subscribe the same talk groups. This allows to operate in these talk groups in a repeater transparent way. Therefore, it does not matter which repeater is being used within the region, the same talk group remains reachable. In the region Berlin & Brandenburg, this it the TG2621. It therefore makes sense to enter all repeaters into one list that have the same talk groups subscribed. If the radio now would automatically select a reachable repeater, one could drive around in the region and would stay connected to these talk groups irrespective of the own position in the region. This feature exists in many radios and is called Roaming. Many of the slightly more expensive devices support this feature (e.g., AnyTone). The cheapest ones usually do not This is somewhat weird, as this feature is a pure firmware feature and does not need any additional hardware.. To use this feature, all channels with a certain talk group should be added to a list. The so-called Roaming Zone. This could actually be done automatically, but the programming software for these devices is usually not very user friendly. If the signal strength of the currently selected repeater falls below a certain threshold (usually -105dBm), the radio will start to search the roaming zone for a repeater which is stronger than this threshold. This only happens if the radio is in standby. That is, if neither something is received nor transmitted. If the radio finds a stronger repeater in the roaming zone, it automatically changes to that repeater. The new repeater does not necessarily needs to be the strongest in the zone. It only needs to be stronger than the threshold. If no stronger repeater is found, the radio remains on the currently selected one. This roaming can also be set to manual. That is, the roaming search will only start if the signal strength is lower than the threshold and the PTT is pressed or the search is started from the menu.
      qdmr-0.12.3/doc/manual/intro/simplex.xml000066400000000000000000000122051501654372000201230ustar00rootroot00000000000000
      DMR Simplex Operation The most simple form of a DMR QSOQSO is a code for call or connection between two amateur radio stations. is the simplex QSO. That is a direct connection between two two DMR radios. Like for the DMR repeater operations, this could be a private, group or so-called All Call. Simplex private call. In a simple simplex private call from DM3MAT to DL1XYZ is shown as well its reply. Both operators transmit and receive on the same frequency (here the DMR calling channel at 433.450 MHz). Although other operators are in the area (DL2XYZ & DL3XYZ) which receive the signal, their radios remain silent. This is because this is a private call to a specific operator and only the radio of that operator will receive the call. All other radios will ignore the call. The channel, however, remains occupied during that call. At that point it is worth mentioning, that if DL1XYZ answers directly to the initial call by pressing the PTT, he will answer with a private call to DM3MAT. He does not need to search for number of DM3MAT in his address book. The direct answering to calls is only possible for several seconds after the end of the initial call. After that period (called Hang Time) a press on the PTT will start a call to the default contact (see ) associated with the simplex channel. Simplex group call It is not only possible to call single operators in simplex mode. Also groups can be called using group calls. A common talk group for the simplex mode has the number 99, (TG99, for talk group 99). These group calls are then received by all radios that are configured accordingly. Like for the repeater operation, also in simplex mode, the radio needs to know which groups the operator belongs to and therefore, which talk groups to receive on which channels. This is done using so-called Group Lists, which are discussed later. In such a simplex group call is shown. There DM3MAT calls the talk group TG99. As DL1XYZ as well as DL2XYZ configured their radios to receive that call on simplex channels, they do so. DL3XZY did not, so he does not receive the call. DL1XYZ & DL2XYZ can now respond to that call by pressing on the PTT within the hang time irrespective of their default contact for the channel. Simplex all call To be sure that a simplex call gets received by all operators in the area, a so-called All Call should be used. This is a special call type to the reserved number 16777215, that gets received by all radios irrespective of their configuration. For the , the all call by DM3MAT gets received by all operators including DL3XYZ. By directly answering within the hang time, all participants are able to respond to that call with an all call as well. In short: A DMR channel consists of a transmit and receive frequency (identical on simplex channels), a default contact that gets called whenever the PTT button is pressed and a list of group calls the radio will receive on that channel. DMR simplex frequencies
      The list of eight common DMR simplex channels. The channel S0 is the calling channel.
      NameFrequencyNameFrequency
      S0 (call)433.4500 MHz S4433.6500 MHz
      S1433.6125 MHz S5433.6625 MHz
      S2433.6250 MHz S6433.6750 MHz
      S3433.6375 MHz S7433.6875 MHz
      lists the common simplex channel frequencies. The channel S0 is the calling channel. Especially in densely populated areas, you should switch to another channel for the actual QSO and use S0 only for the initial call.
      qdmr-0.12.3/doc/manual/intro/talkgroup.xml000066400000000000000000000104221501654372000204510ustar00rootroot00000000000000
      Talk Group Operation As mentioned in , DMR aims at being repeater transparent. That is, it does not matter which repeater is used for the operation. This is also true for talk groups. In this section, I'll continue with the example of an OM at his holiday location, who wants to participate in his afternoon net. For example, the afternoon net is happening on the talk group 2621 (Berlin/Brandenbug, BB). This talk group is usually statically subscribed on all repeaters in the states Berlin and Brandenbug. That means that the local afternoon net can be performed in this talk group without any additional action in this area. (see top image). Talk group operation between countries For the OM at his holiday location, this it not true. An Italian repeater will certainly not subscribe the BB talk group statically. Therefore, the OM at the holiday location will not hear his afternoon net. He knows, however, when this net starts. So he can perform a group call to the that TG shortly before the net starts (see second image). This will subscribe the BB talk group temporarily at the holiday repeater (I0ABC). Now, the OM can hear and participate in the afternoon net. The subscription will be renewed whenever he starts a group call to that TG. Once the OM subscribed the TG to the holiday repeater, he can participate normally at his afternoon net. All other participants will not even notice that he is not in the area and is participating from an italien repeater. Some talk groups
      Name Talk group number
      Global 91
      Europe 92
      Germany 262
      Mecklenburg-Vorpommern & Sachsen-Anhalt 2620
      Berlin & Brandenburg 2621
      Hamburg & Schleswig-Holstein 2622
      Niedersachsen & Bremen 2623
      Nordrhein-Westfalen 2624
      Rheinland-Pfalz & Saarland 2625
      Hessen 2626
      Baden-Württemberg 2627
      Bayern 2628
      Sachsen & Thüringen 2629
      Cluster In contrast to the regional talk group TG8, all other talk groups are reachable from everywhere in the DMR network. This means, that the OM at his holiday location can easily participate in his afternoon net from everywhere as described above. If the net, however, is happening in the regional TG8, the OM at his holiday location cannot participate. This talk group is only reachable from within the region. If the OM starts a call to TG8, he would only reach the region in italy and not the region at home. For this reason, some regional clusters of repeaters are linked to a so-called Cluster. These clusters provide normal talk groups for the repeater within a region. These clusters are then also reachable from the outside. A list of regional clusters and their associated talk group numbers can be obtained under bm262.de/cluster/.
      qdmr-0.12.3/doc/manual/intro/technicalbackground.xml000066400000000000000000000072361501654372000224440ustar00rootroot00000000000000
      Technical background In the previous sections, I tried to explain the basic concepts of the DMR operation. That is, the repeater independent private and group calls. This section concerns the more technical details of the DMR mode. In particular the Time Slot and Color Code.
      Time Slots As mentioned before, DMR is a digital mode. The speech signal is first digitized and compressed with a lossy compression codec. The latter is also called VOCODER. Modern codecs are very efficient and allow to transfer two independent speech signals within a single 12.5kHz wide channel. This is exploited in DMR using a technique called TDMA. TDMA means time-division media access and describes that two independent calls can happen simultaneously on one physical channel. To achieve this, each call is assigned a Time Slot (time slot 1 & 2) and both are transmitting and receiving only within their assigned time slot. These time slots are very short. For DMR they are only 30ms long. This short time, however, is sufficient to transfer audio for at least 60ms. Therefore, DMR allows for two independent and simultaneous calls on a single physical channel. When time slot 1 or 2 happens, is determined by the repeater. The repeater defines the beat. This also implies that time slots are irrelevant for the simplex operation. Thus you can ignore the time slot settings when programming simplex channels. (see ). What happens on each time slot, is a convention defied by the repeater. A general suggestion is that regional communication happens on the time slot 2 while trans-regional communication should happen on time slot 1.
      Color Codes Color Codes are a technical tool to avoid conflicts between repeaters operating on the same frequency. This usually happens in commercial applications of DMR. One company usually gets only a small number of frequencies assigned. To cover the entire campus, much more repeaters are needed than frequencies are available. Consequently, overlapping repeater ranges on the same frequency will occur and two repeaters may receive the call of a participant. Then, the color code allows the repeater to detect whether a call was intended for it. Only if the color code of a call matches the color code of the repeater, the repeater will react on that call. This issue usually does not arise in amateur radio applications. We just have enough channels. Hence the color code is usually set to 1. In very densely populated areas, however, overlapping repeater ranges may still occur and different color codes might be used there. To use a repeater, you not only need to know the input and output frequencies but also the color code of the repeater.
      qdmr-0.12.3/doc/manual/intro/textmessages.xml000066400000000000000000000071361501654372000211650ustar00rootroot00000000000000
      Data services As DMR is a digital mode that transports digitalized speech, it is possible to transfer other data too. Consequently, there are some other digital services provided with DMR. First, there are text messages similar to the one provided by mobile phones. It is also possible to forward the own GPS position to the APRS network.
      Textmessages (SMS) With this service, you can transfer short text messages to other participants (like a private call) or to a talk group (like a group call). The latter is rather uncommon and should be avoided. In principle a text messages works like a private call. If the destination is reachable, the text message will be routed to it. There are also service numbers (free of charge). If messages are sent to them, certain information can be retrieved to forwarded to other networks (e.g. to the DAPNET). In Germany (and other countries) there are: 262993 -- GPS and weather Send help and you will receive a list of commands. Send wx and you will receive the weather at the location of the repeater you used. Send wx CITY and you will receive the weather at the specified city. Send gps and the last GPS position is returned that you have sent to the DMR network. With gps CALL you can retrieve the last position sent by the specified call. Send rssi and you will receive a signal report from the repeater. 262994 -- Repeater information & pager messages Send rpt to receive a list of static an dynamic subscribed talk groups at the repeater. Send CALL MESSAGE to send the given message to the given destination call in the DAPNET.
      Position reporting to the APRS network As mentioned in the previous section, it is possible to report the own position via the DMR network to the APRS network. This position can then be tracked at for example aprs.fi. To do that, a radio with a built-in GPS receiver is required. But not even these devices are expensive anymore. Simple DMR hand-helds with built-in GPS receivers are available for about 120€. Beside the text message services, it is also possible to transmit the position to the DMR network using a special service number 262999. How the radio is configured to do that, depends heavily on the device being used. qdmr helps with the configuration by providing common means for these settings across all supported radios.
      qdmr-0.12.3/doc/manual/manual.code-workspace000066400000000000000000000000741501654372000206730ustar00rootroot00000000000000{ "folders": [ { "path": "." } ], "settings": {} }qdmr-0.12.3/doc/manual/manual.xml000066400000000000000000000027411501654372000165700ustar00rootroot00000000000000 qdmr - User Manual A universal codeplug programming tool for Linux and MacOS X. May 30th, 2025 2022-2025 Hannes Matuschek This document covers qdmr version 0.12.3. This document is released under the conditions of the Creative Commons Attribution-ShareAlike 3.0. qdmr-0.12.3/doc/manual/manual_fo.debian.xsl000066400000000000000000000016511501654372000205020ustar00rootroot00000000000000 7pt always appendix nop article toc,title book toc,title chapter toc part nop preface nop qandadiv nop qandaset nop reference toc,title section nop set toc qdmr-0.12.3/doc/manual/meta/000077500000000000000000000000001501654372000155135ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/meta/abstract.xml000066400000000000000000000024371501654372000200460ustar00rootroot00000000000000 About This Document This document contains a brief manual for the qdmr graphical user interface (GUI) application to program DMR radios. The aim of this application is to provide a simple to use, platform and vendor independent and well documented codeplug programming software (CPS) for popular DMR radios. This document is available in several formats: HTML, PDF and EPUB 3. qdmr-0.12.3/doc/manual/meta/authors.xml000066400000000000000000000007361501654372000177300ustar00rootroot00000000000000 Hannes Matuschek dm3mat@darc.de OV Y07 OV Y07, DARC qdmr-0.12.3/doc/manual/meta/glossary.xml000066400000000000000000000417611501654372000201110ustar00rootroot00000000000000 Glossary Admit Criterion The admit criterion specifies a criterion under which transmissions on a channel are allowed. These criteria can be defined for both, analog and DMR channels. Irrespective of the channel type, the admit criteria always and channel free can be selected. The former simply always allows to transmit. The former requires the channel to be free. For analog channels, usually there is also the criterion tone. This criterion would only allow transmissions if the matching CTCSS tone or DCS code has been received. For analog repeater operation, the criterion channel free should not be used as this would prevent any transmissions while the repeater is active. For digital channels, usually there is also the criterion color code. This criterion would only allow transmissions if the matching color code has been received. To this end, this criterion is similar to the tone criterion for analog channels. All Call An all-call is a special type of an DMR call to a special number (16777215). All radios are supposed to receive this call irrespective of their particular configuration. This call type is usually used in emergency situations or on simplex channels. Unfortunately, the local talk group (number 9) is frequently used on simplex channel although only those radios will receive calls to this talk group if it is in the group list assigned to the simplex channel. To be on the safe side, use the all-call for simplex calls. Automatic Packet Reporting System APRS The Automatic Packet Reporting System is a protocol that uses single broadcast frequencies to transmit small amounts of information though a network of repeaters. Usually but not limited to position reports. This allows other services (e.g., aprs.fi to display that information on a map. The usual APRS frequencies are 144.390 MHz (North America), 144.800 MHz (Europe) and 145.175 MHz (Australia). Codeplug The term codeplug is rather loosely defined. Usually it refers to the binary representation of the configuration of a radio that is written to the radio to configure it. It contains all contacts, channels, zones, settings, etc that form the configuration of the radio. Color Code A color code is some additional information attached to each DMR call. It is used to prevent interference between repeaters with overlapping ranges that operate at the same frequencies. In amateur radio, this may only happen in very densely populated areas. It is much more frequent in commercial applications, where a single company may got only a few frequencies assigned but needs much more repeaters to cover a campus reliably. The color code is a number between 0 and 16. A radio or repeater may only react to calls with matching color codes. Hence beside the actual input and output frequencies, the color code of a repeater must be known to be able to access it. Continuous Tone Coded Subaudio Squelch CTCSS Means to control the squelch of a radio or to open a repeater, that does not rely on the strength of a carrier. Instead a tone is transmitted along with the audio, which is usually filtered out (e.g., less than 300Hz) by the receiver. There are a set of common CTCSS tone frequencies, but may radios allow for specifying arbitrary sub-tone frequencies. DCS Decentralized Amateur Paging Network DAPNET DAPNET, short for Decentralized Amateur Paging Network does exactly what it says. It is a network of transmitters that transmit pager messages in the UHF band, usually at 439.9875 MHz. As the old pager operated at a frequency near by, they can be modified to operate at that frequency and can therefore be used in amateur radio. The DAPNET is particularly popular with emergency operators as it allows for an convenient and reliable multicast notification. DCDM Stands for dual capacity direct mode. This allows for two independent connections on a single simplex channel by TDMA. Lacking a single central repeater, one of the participants must act as the so-called leader. This radio then defines the clock and thus time slot 1 and 2. All remaining participants must act as so-called follower. They must synchronize to the clock of the leader. This obviously requires that all participants can reach the leader directly. Digital Coded Squelch DCS Similar to the CTCSS tones, the DCM allows to open the squelch or repeater by transmitting sub-tones along with the audio. These tones are usually filtered out by the receiver (e.g., below 300Hz). In contrast to CTCSS tones, DCS uses these tones to repeatedly transmit a digital code. EchoLink EchoLink is a network of analog FM repeaters that allows to link repeaters within this network temporarily. That is, two FM repeaters that are linked via EchoLink behave like a single repeater irrespective of their location. This network also allows to access the repeaters directly though the internet. This is particularly helpful if one cannot reach any repeater. Group Call A group call is simply a call to a talk group. That is, not to a single participant but rather to a group of participants. Every participant that has this talk group in their group list can receive this call. See also Group List and Talk Group. Group List A group list is a simple list of talk groups. A group list is then assigned to a channel to specify which group calls to receive on that channel. If a talk group is not listed in the group list of a channel, the radio will ignore calls to this talk group on that channel. The DMR network cannot know which talk groups you are interested in. You have to tell your radio using group lists. Hang Time The hang time specifies the time period, a DMR call remains active after it ended. For this time period, the call will replace the default transmit contact on that channel. This allows to directly answer a call received on a channel, even if that call is not the default transmit contact on the channel. Maidenhead Locator The Maidenhead locator, also known as QTH locator or IARU locator is a means for transferring a geo-location over radio. Using a so-called grid-square system. Lone Worker Lone worker refers to a feature of many DMR radios used in some commercial settings, where a participant in the network needs to report regularly to some other station. This feature is certainly not very useful for the HAM radio context. Private Call A DMR call to an individual participant. Usually, radios do not receive private calls not addressed to the radio. This therefore can be used to talk to a specific HAM over a repeater without disturbing other participants. The repeater however, will be blocked during that time. Revert Channel A revert channel is a designated channel the radio resorts to, to transfer some information or the radio switches to, when the PTT is pressed. This is usually used in the context of APRS (i.e., the channel to switch to send the position) or scan lists (i.e., the channel the radio will switch to, when the PTT is pressed during the scan). Roaming Channel Specifies which settings of the current channel needs to be overridden to stay in contact with a particular talk group. These settings are usually those, specific for a repeater. That is, receive and transmit frequency and color code. The time-slot might also be overridden. Roaming Zone A collection of roaming channels usually sharing the same transmit contact. When roaming is enabled (and supported by the radio) this list of channels is scanned for a reachable channel once the current repeater gets out-of-range. This way, one can stay in contact with a particular talk group when being mobile. Secondary Station Identifier SSID AX.25 addresses (and thus APRS) consists of a call sign and a so-called SSID (0-15). This number can be used as a replacement for the lack of ports in AX.25 or to provide some additional information about the station. Talkaround Talk Group TG The term Talk Group refers to a DMR ID, which represents a virtual contact. DMR repeater may subscribe to one or more talk groups. Every call to these talk groups are then transmitted by those repeaters, that are subscribed to these talk groups. Usually, it is possible to subscribe a repeater to any talk group temporarily by simply starting a transmission to this talk group on a repeater channel. For some time period after the initial transmission, the repeater will transmit all calls to this now subscribed talk group. The subscription gets renewed with every further call the talk group. Group Call Time-Division Multiple Access TDMA Time Slot Due to the lossy compression of audio data, it is possible to send more than twice the amount of audio data through a single 12.5kHz wide channel than needed. This allows to provide two independent audio streams within a single physical channel. There are basically two ways to separate these two audio streams. Either by frequency, effectively splitting the single 12.5kHz channel into two 6.25kHz wide ones. Alternatively, one can split the channel in time. That is, the band width remains 12.5kHz but each of the two simultaneous participants sends one after the other. One in time slot 1 and the other in time slot 2. This is called TDMA and DMR uses this technique to provide two independent audio and data streams within a single physical channel. For this TDMA to work, one participant needs to define the clock to specify when time slot 1 or 2 happen. This role is usually taken by the repeater but on direct simplex connections, one of the participants might provide that clock. This is then called DCDM (Dual-Capacity Direct Mode). This mode, however, makes little sense in amateur radio context. Transmit Contact A transmit or default contact can be specified for each DMR channel. Some radios actually require that each channel has an transmit contact. This contact can be any contact you want to call whenever the PTT is pressed on that channel. It is, however, possible to answer any received call directly by pressing the PTT button within the so-called hang time, although the call may not match the transmit contact. These hang times can be set within the radio settings, usually for private and group calls separately. It is also possible to call any contact by selecting it from the contact list or by entering the contacts DMR ID into the keypad. Transmit Timeout TOT Specifies the time of continuous transmission, after which the transmission is interrupted automatically. This prevents the accidental blocking of a repeater or talk group by transmitting continuously. Scan List A scan list is just a list of analog and digital channels that one wants to scan. A scan list can be associated with each channel. This scan list is then used whenever a scan is started on that channel. Zone As there are usually several channels defined per repeater (at least two), the number of defined channels grows rapidly. To organize this large number of channels, zones are used. These zones usually collect all relevant channels for a region. That is all (analog and digital) channels associated with the local repeaters. The relevant zone is then selected via the keypad on the radio and only those channels in that zone are then accessible. A defined channel that is not member of any zone is usually not accessible in the radio. Some radios allow to specify a different zones for each VFO. Others set a zone globally. In the latter case, the zone consists of two channel lists. One for each VFO. qdmr-0.12.3/doc/manual/meta/preface.xml000066400000000000000000000060631501654372000176470ustar00rootroot00000000000000 Foreword Before you start programming your DMR radio, get familiar with this digital mode. I have written a brief introduction into DMR too. You will find it in (also available in german). If you are already familiar with DMR, skip this chapter and head directly to , where I describe how qdmr is used. If you are a fan of command line tools, have a look at . Digital mobile radio (DMR) was not invented for the use in amateur radio. It was rather designed to be a radio standard for commercial applications in large companies (e.g., airports etc.). Therefore many features of this standard are of no use for ham radio or are even illegal (e.g., encryption). This complexity of the standard makes the programming of the radios cumbersome. Moreover, the resulting configuration (codeplug) is highly device-dependent. These codeplugs cannot be shared between different devices let alone between different vendors. For commercial applications, this is not a big problem, as a company will likely buy identical radios at once from one company. Thus codeplugs can be shared between all radios. For ham radio applications, this incompatibility is a real issue. Since assembling a decent codeplug for one region is hard enough, doing the same work all over again for different models of different vendors is not manageable. Finally, the typical code-plug programming software (CPS), particularly those for cheap Chinese DMR radios, is by no means user-friendly and seldom documented completely. Many options are named cryptic and it is not possible to identify which options are necessary for basic DMR operation. Moreover, the CPSs provided by the vendors usually only run under Windows. The aim of the qdmr project is to overcome these shortcomings of typical CPSs. It has a reduced feature set only supporting those options necessary of amateur radio usage. It tries to be user-friendly by finding repeaters nearby and importing their input and output frequencies. Moreover, it stores the final codeplug not in a device-specific binary format but in a human-readable text format that is device independent and can therefore be shared across multiple device and even across vendors. Finally, I try to keep the application well documented. This manual is part of this effort. It is a guide on how a codeplug is set up using qdmr. qdmr-0.12.3/doc/manual/reveng/000077500000000000000000000000001501654372000160535ustar00rootroot00000000000000qdmr-0.12.3/doc/manual/reveng/codeplug.xml000066400000000000000000000277601501654372000204130ustar00rootroot00000000000000
      Reverse engineering of the code plug format Once the communication protocol is known, it is time to focus on the code plug format. Depending on the code plug complexity, this can be a very cumbersome task. Moreover, depending on the device, it can be pretty hard to access the binary representation of the configuration (code plug) that is actually written onto the device. In general, one cannot expect, that the binary file, created by the manufacturer, does relate to the binary code plug written onto the device at all. To figure the relationship out, between the code plug file and the actual code plug written onto the device, the code plug must first be extracted from the Wireshark capture. As we already know the protocol, this can be done easily using Python and pyshark. When doing so, be careful with the addresses, data is written to or read from. The binary code plug might not be written sequentially and with gaps. A best practice is to extract the data read or written along with its addresses and sort it with respect to the address. Then dump everything as a hex-dump. Once the code plug is extracted from the Wireshark capture, compare it to the corresponding code plug file created by the CPS. If you are lucky (usually for very cheap DMR radios), the CPS code plug file is simply the binary code plug written to the device (e.g., for GD77, RD5R). Then it is very easy to reverse engineer the code plug format, as you can study changes to the CPS code plug file. If they are not identical, you will need to somehow extract the binary code plug written to the device from the manufacturer CPS. If the protocol is based on Serial-over-USB, you may be able to write an emulator for the device, knowing the communication protocol. This then allows to trick the CPS to talk to the emulator instead of the device and dump the code plug. If not, you are likely stuck with writing a lot of code plugs to the real device and extracting the code plug from the Wireshark captures. This can be considered the worst-case scenario.
      Differential analysis Irrespective on how you gain access to the binary code plug written onto the device, the analysis of this code plug is always the same. This is called differential analysis. The idea is, to change a single option in the CPS and compare the resulting binary code plugs bit by bit. Ideally, only a single place in the binary code plug will change too and this change then reflects the encoding of the particular setting touched. This is then repeated for all possible settings in the CPS until the entire code plug structure is known. There will be some bits and bytes in the code plug that will never changed. These are usually reserved bits/bytes. There may also be some undocumented and hidden options in the code plug, that cannot be set via the (normal) CPS. Before you can start reverse engineering the code plug, you need to create a base code plug. That is, one small simple code plug, that touches every feature of the radio. It should not be overly complex with hundreds of channels, but should contain everything the radio provides. For example, it should contain the APRS settings, if the radio has GPS or a roaming zone if the radio supports roaming. You should also create two of every kind. That is, at least two channels, contacts, group lists, zones and scan lists. Otherwise, it will be hard to figure the index scheme out. Once you created the base-code plug, get its binary representation (e.g., from file, emulation or Wireshark capture). Change something simple. For example the name of the first contact. Then get the binary representation of this modified code plug and compare the two binary code plugs. As an example, consider the following difference between two hex-dumps of the binary code plugs for a BTech DMR-6X2UV Pro. Here, the name of the first contact was changed. Difference between two binary code plugs for a DMR-6X2UV Pro. Only the name of the first contact was changed. 02680000 : 01 56 65 72 79 20 6c 6f 6e 67 20 6e 61 6d 65 2e | .Very long name. > 02680010 : 2e 00 00 55 6e 6b 6e 6f 77 6e 00 00 00 00 00 00 | ...Unknown......]]> This difference already shows some very important information about the code plug encoding. First, the list of contacts appears to start at address 2680000h. This is very important for the coarse structure of the binary code plug. We also learn about the structure of the encoded contacts, that the name starts at offset 01h. So, the first byte is still unknown. The length of the name is limited to 16 bytes and is terminated/padded with 0-bytes. However, we still don't know how large the encoded contact element in the code plug actually is. To figure out the size of the contact element, one can use the fact, that all elements very likely have identical sizes. Hence the offset from one name to the name of the next contact will be the size of the contact element. We already know, that the contact list starts at the address 2680000h. Hence, we can look in the code plug directly at that address. Codeplug hex-dump at the contact-list address.
      The offset between the two names Contact 1 and Contact 2 is exactly 100 bytes (64h). With this, we know the exact size of the contact element. However, wo only know what 16 of these 100 bytes mean. So there is a lot to figure out. The differential analysis, however, makes it quiet easy. First we may change the call type of the first contact. From group call to private call and study the differences in the code plug. 02680000 : 00 43 6f 6e 74 61 63 74 20 31 00 00 00 00 00 00 | .Contact 1......]]> We see, that only one byte has changed. The one at offset 0. It changed from 01h to 00h. We may conclude that this byte encodes the contact type, where 00h means private call and 01h means group call. Analogously, we find out that 03h means all-call. The next step is to find the encoding and offset of the contact DMR ID. To find it, we reload the base code plug and change the DMR ID of the first contact from 1 to something more complex. E.g., to 1234567 and compare the binary . 02680020 : 00 00 00 01 23 45 67 00 00 00 00 00 00 00 00 00 | ....#Eg.........]]> Here we find some changes at the offset 23h. The content changed from 00000001h to 01234567h. This is somewhat weird, as the binary representation of 1234567 in hex is actually 12d687h. However, for DMR code plugs, it is quiet common to store integers in so-called BCD. Here each digit is stored in 4bits. This has the consequence, that the decimal digits are readable in hex. But this storage is inefficient, as the DMR ID is actually a 24bit number, that can be stored in 3 bytes. Its BCD encoding requires 4 bytes. The last setting for contacts remaining in the CPS is the call-alert. This setting is only valid for private calls. Changing the contact 1 from group to private call, changing the call alert from None to Ring and comparing the code plugs, one gets: 02680000 : 00 43 6f 6e 74 61 63 74 20 31 00 00 00 00 00 00 | .Contact 1...... < 02680020 : 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 | ................ > 02680020 : 00 00 00 00 00 00 01 01 00 00 00 00 00 00 00 00 | ................]]> Of cause, we find two changes. The change of the first byte, indicating the change from group to private call, as well as one byte changing at offset 27h from 00h to 01h. This is the only unexplained change and thus must reflect the alert-type setting. Consequently, the alert type None is represented by 00h and Ring by 01h. Analogously, we find that alert-type Online is encoded as 02h. Obviously, we still don't know the majority of the 100 bytes of the contact element. Lets have a look at the structure of the contact element. There are some additional Pad bytes (00h) that terminate ASCII strings. This is nothing, we found by the differential analysis, but it is some common scheme, found in many binary code plugs. Obviously, the firmware developer did not knew the strnlen C function. The remaining bytes of the contact element appears to be empty, unused space. This is also quiet frequent in code plugs. They contain a lot of unused bytes that might be reserved for future uses. This allows the firmware developer to extend the code plug, while maintaining backward compatibility with older versions of the CPS. Having reverse engineered the contacts, does not mean that you are done. There are a lot more elements to decipher. Did I mentioned that it is a lot of work? But the general scheme of things remain the same. Change a single setting in the CPS and compare the resulting binary code plugs. Document your findings. Step by step, you will figure out the entire code-plug structure.
      qdmr-0.12.3/doc/manual/reveng/protocol.xml000066400000000000000000000341771501654372000204520ustar00rootroot00000000000000
      Reverse engineering communication protocols There is no one ultimate way to successfully reverse engineer a communication protocol. But some methods are very common. First, one needs to get some captures of the communication between the manufacturer codeplug programming software (CPS) and the radio of interest. The majority of these radios will communicate via USB. Hence, some means are needed to capture USB traffic. There is an application, called Wireshark (). This application is usually used to capture and analyze network traffic. It can, however, also capture traffic from USB. How this is done, depends a bit on the operating system you are using. If you want to capture and analyze the communication under Windows, read the instructions at . This makes sense, as you need a windows installation, to run the manufacturer CPS. I personally prefer to work under Linux, to this end, I've installed Windows in a virtual machine, but capture the USB traffic under the host Linux system. To do that, the usbmon kernel module must be loaded. If loaded, Wireshark will show USB as a capture source. Once, capturing USB traffic works, connect the radio to the host. You may need to allow the guest system to have access to the USB device. Then fire up the manufacturer CPS, start capturing in Wireshark and start a codeplug read. Stop capturing once the read is complete. Save the captured data and restart the capturing. Then write the codeplug back to the device, stop the capture and save it in another file. These files now contain all packets send and received by the PC over USB. In a next step, the packets must be filtered and inspected. To do that, I personally prefer to write short Python scripts. The pyshark package provides means to read packages from the capture file and access their content. This allows to filter those packages to and from the device, that contain the actual payload to and from the device. Please note, that may radios may misuse some already existing protocols over USB. Some Radioddity and Baofeng devices use the HID specification to transfer data to and from the device. Others may use the DFU specification (TyT, Retevis), mass storage (CSi) or simply serial-over-USB (AnyTone). Irrespective of the underlying specification used to send data to the device, the payload of these packages must be extracted. To do that, use Wireshark to inspect these packages by hand. Search for a packet, that was definitely send to or by the device and note the USB (URB) device address. This is the best way to filter all packets to and from the device. Some simple example to filter packages by device address and destination/source. This example shows how to filter only those packets to and from a specific device and also dispatch depending on the destination and source of the packet. That is, if the packet was send by the host or the device. In a next step, the captured payload must be inspected. If the packet contains any additional payload data, the usb.data_length field will be set. Then, there is a field called usb.capdata_raw. Unfortunately, that field name contains a dot, so you need to access it differently. That is The extracted data is hex string. Now comes the difficult part. Staring at these packets and making sense of them. Usually, the communication is performed in a strict command-response structure. That is, the host sends a request packet and the device responses with a single packet. This eases the analysis a lot, as the communication is always initiated by the host and the association between the request and the response packets are trivial.
      Identifying the communication structure A typical communication with the device is performed in three steps: First, the radio is identified and brought into a programming mode. Several packets might be needed to perform this task. In that state, the radio usually displays some message on the screen or blinks an LED. The second step consists of the actual codeplug transfer. There, a large amount of packets are send. This step is usually easy to find within the captured packets, as a large number of equally sized packets are exchanged. Sometimes, a relatively large amount of data is read or written at once. This can be spotted easily, as basic control commands of the first step are usually pretty short. Frequently, these packets contain an address field or sequence number used to specify where the codeplug data chunk is written to or read from. These fields are of upmost importance and need to be identified. They are, however, easy to spot, as this field is likely constantly increasing, with a fixed increment from packet to packet. The final step usually is to reboot the radio or leaving the programming mode. This is commonly performed by a single packet. Sometimes, the device reboots immediately and no response to the command is received by the host. Once the protocol structure is identified, that is, the purpose of the single packets is known, the actual work of identifying the packet structure can start.
      Identifying the packet format The actual packet format will be much harder to reverse engineer. I cannot give any general suggestions on how to figure out the meaning of each byte in a packet. However, I can give you some examples. This might help in reverse engineering new protocols as the structure of the packets will be different, but the concepts remain the same. For example, read and write commands must somehow specify an address to read from and write to as well as the amount of data to read or write. This might be helpful Let us inspect some packets send and received from an AnyTone device. Capture of a codeplug read from an AnyTone AT-D578UV 50 52 4f 47 52 41 4d | PROGRAM < 51 58 06 | QX. > 02 | . < 49 44 35 37 38 55 56 00 12 56 31 31 30 00 00 06 | ID578UV..V110... > 52 02 64 00 00 10 | R.d... < 57 02 64 00 00 10 fe ff ff ff ff ff ff ff ff ff | W.d............. | ff ff ff ff ff ff 65 06 | ......e. > 52 02 64 00 10 10 | R.d... < 57 02 64 00 10 10 ff ff ff ff ff ff ff ff ff ff | W.d............. | ff ff ff ff ff ff 76 06 | ......v. > 52 02 64 00 20 10 | R.d. . < 57 02 64 00 20 10 ff ff ff ff ff ff ff ff ff ff | W.d. ........... | ff ff ff ff ff ff 86 06 | ........ ... > 52 01 64 08 80 10 | R.d... < 57 01 64 08 80 10 00 ff ff ff ff ff ff ff ff ff | W.d............. | ff ff ff ff ff ff ee 06 | ........ > 52 02 48 02 00 10 | R.H... < 57 02 48 02 00 10 01 08 00 00 ff ff ff ff ff ff | W.H............. | ff ff ff ff ff ff 59 06 | ......Y. ... ]]> The shows a capture of the data exchanged between the host and an AnyTone AT-D578UV during a codeplug read. Everything starting with > is sent from the host to the device, while everything starting with < is sent by the device to the host. The first packet send, is the ASCII string PROGRAM. This causes the device to enter the programming mode. A message will be shown on the screen of the device. This is then acknowledged by the device with a short message QX followed by a single 06h byte. The last byte appears to be weird, but it will always be present on any response from the device. This might be seen as an end-of-packet byte. The next command send by the host is pretty simple. It consists of a single 02h byte. The device responds with a 15 bytes long ID string, identifying the device name and hardware version followed by the 06h end-of-packet byte. After this, the actual codeplug read appears to start. The host sends a series of equally size commands, each starting with an R followed by 5 bytes of payload. As mentioned above, this payload must contain some sort of memory address and length field, to specify how much to read and from where. To figure that out, one may study subsequent read requests. The payloads of the first tree read read requests are The only difference of these requests is the fourth byte. Hence, one may assume, that this byte is part of the address field. However, we do not know which bytes are part of the address as well. The responses to these requests also contain these bytes as well as 18 more bytes of payload. It is reasonable that the transfer size is a power of two. So the request likely reads 16 bytes from the device. 16 in hex is 10h, hence one may assume that the last byte specifies the amount of data to read. Hence we identified the size field as the fifth byte of the read request payload data. As the address increases by 10h from request to request, one may infer, that the read request specifies the address to read from, not the sequence or block number of a read from. As the entire Codeplug will not fit into 256 bytes, the address field must be larger. Even 64kb will be too small, to hold the entire codeplug, the address is likely a 32bit integer. This does not need to be true. Some radios will implement special commands to select the memory bank beforehand. Then each 64kB bank can be accessed with a 16bit address. To verify our assumption, we will study some read requests, that appear much later and check if the first bytes change as well. And indeed, much later, we observe read request payloads like Obviously, these bytes change too. Consequently, we may assume that the address is stored as a 32bit unsigned integer in big-endian byte-order. Finally, we may summarize the read request format as Where N is the number of bytes to read. Now, it is time to study the structure of the read response. As we already reverse engineered the read request, the read response is then easy to understand. Each of these responses start with a W char, followed by the same address and length as send by the request. That is 52 02 64 00 00 10 < 57 02 64 00 00 10 fe ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 65 06]]> Obviously the 16 byte data read, follows the length byte. The last byte is the common 06h end-of-packet byte, we have already seen. So a single unknown 65h byte is still unknown. This byte might be some sort of checksum over the payload. Checksums are notoriously difficult to figure out. Frequently, common techniques like CRC16 are used. Here, a single checksum byte is used, hence one of the common checksums is unlikely. Now starts some guesswork. We may compare some very similar read responses to get an idea, how this checksum may work. For example The only difference between these two responses is one byte in the address, the remaining payload is identical. The address also only differs by 10h like the checksum. So one may assume, that the checksum is simply the sum over the payload bytes. Summing all bytes of the latter up to the checksum gets 10ddh. Of cause, this is not the checksum directly. The checksum is a single byte. But the least significant byte of the sum (ddh), however, does not match. Maybe, the sum is not taken over the entire payload, but over the part that actually matters. That is, the address, length and data fields. This time, the sum is 1086h and this time, the least significant byte (86h) matches the checksum. Now, we can summarize the read response format as where the CRC is the least significant byte of the sum over the address, length and data payload.
      qdmr-0.12.3/doc/manual/reveng/reverseengineering.xml000066400000000000000000000034211501654372000224630ustar00rootroot00000000000000 Reverse engineering The majority of the development time needed for qdmr, consists of reverse engineering the code plug and communication protocols of the radios. Fortunately, many radios share the same communication protocol, in particular those from the same manufacturer. Sometimes these protocols are even used by other manufacturers. Even if the protocol is already implemented in qdmr, you may need to reverse engineer the code plug format. This step has to be performed always, even if an already implemented code plug is reused, you will need to verify that format. Any mistake here, may brick your device. Before you attempt to reverse engineer anything, consider to invest some significant time in research. Reverse engineering is an cumbersome and frequently frustrating task, there is no need to waste your valuable time on something, that someone else has already done. Also, consider documenting and publishing your results, so others can find it, even if it is incomplete. Someone else might pickup your work and complete it. In the following sections, I attempt to describe, how I approach reverse engineering the communication protocols and code plugs. To this end, I hope it might help you in your reverse engineering work. And if you like, you may contribute your work to qdmr. qdmr-0.12.3/doc/qdmr.in.xml000066400000000000000000000106611501654372000154060ustar00rootroot00000000000000 qdmr 1 qdmr ${PROJECT_VERSION} User Commands Hannes Matuschek dm3mat@darc.de Main author qdmr Graphical tool for programming DMR radios. qdmr Description qdmr is a graphical tool to program DMR radios. That is, reading, editing, generating and uploading codeplugs to these radios. The configuration of these radios is usually stored in a highly vendor and device specific binary codeplug. qdmr stores this configuration in a human-readable and device independent format, thus allowing for sharing codeplugs across devices. Options If the optional codeplug file is passed to qdmr, it gets loaded on startup. Otherwise, the application starts with an empty codeplug. This option allows to set a Qt style for the application. This can be used to alter the style of the widgets. So call qdmr -style=windows if you fancy. However, this option is of real use if you want to use a dark theme. By installing a dark Qt theme like kvantum-dark and starting qdmr by passing this theme to the style option. That is qdmr -style=kvantum-dark You may set the Qt style for all Qt applications using the QT_STYLE_OVERRIDE environment variable. This option allows for specifying a style sheet to alter the appearance of some or all widgets of qdmr. Logging Qdmr writes a lot of debug and error messages to stderr. This can be used to inspect some issues during reading/writing the a codeplug to the device. There is also a log file containing these messages usually at ~/.local/share/DM3MAT/qdmr/qdmr.log. Bugs This program is still under development and may contain bugs that may cause harm to the radios and may even destroy them. Hence you may use this software on your own risk. If you want to have guaranties, consider using the CPS (code-plug programming software) supplied with your radio. There are some issues with the so-called dark mode under Linux. The Qt library is not able to detect that the desktop has a dark mode theme. To this end, qdmr cannot react to it too. To enable a dark theme for qdmr, see the option above. Writing a single application supporting several radios of different manufacturers is a hard task. To this end, there are plenty of bugs to be expected. If you stumble across one of them, consider opening an issue at . qdmr-0.12.3/doc/reveng/000077500000000000000000000000001501654372000145765ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/README.md000066400000000000000000000034301501654372000160550ustar00rootroot00000000000000# How to reverse engineer code-plugs? This question is certainly not easy to answer. It heavily depends on the particular device. In general, reverse engineering the code-plug requires two steps. First, the communication protocol between the host and the device must be known. This step can be skipped if the protocol is already known (e.g., for Radioddity, Baofeng, Tytera, Retevis or AnyTone devices) or when the protocol is actually an industry standard (e.g., DFU). Once you know how the host communicates with the device, the actual code-plug can be tackled: The general approach is a so-called differential analysis. That is, using the original CPS, you start with a compact code-plug that covers all features for the radio. Then you change only ONE thing using the original CPS and compare the two results. Sometimes, the manufacturer is nice to you and the binary code-plug saved by the original CPS is basically a one-to-one image of the memory content written to the device. This is true for the Radioddity GD-77 code-plugs and to some lesser extend for the RD-5R, TyT MD-UV390 code-plugs. Sometimes, however, the manufacturer CPS saves something that has basically nothing to do with what is written to the device. For example the AnyTone D-878UV. In these cases, the content being written to the device must be captured. This can be done using wireshark on a linux host. That is, fire up a virtual machine running the original CPS. Start wireshark on the host system and monitor the USB interface using *usbmon*. Then, write a small script that extracts the actual data written to the device from the capture files. These can then be compared to perform the differential analysis. An example script including captured traffic and extracted data can be found in the anytone directory.qdmr-0.12.3/doc/reveng/anytone/000077500000000000000000000000001501654372000162535ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/anytone/README.md000066400000000000000000000062511501654372000175360ustar00rootroot00000000000000# Reverse engineering of AnyTone codeplugs The transfer protocol of the AnyTone devices is based on the USB-Serial protocol (ACM-CDC) and is well known. To this end, it is possible to *emulate* the AnyTone devices using a simple python script. You will find a matching script in every subdirectory. ## Emulation under windows These scripts expect a serial loopback device to be present. Under windows, such a device can be created using a *virtual null-modem cable*. For example [com0com](http://com0com.sourceforge.net/). This will create a pair of COM ports (e.g., COM5 and COM6). One will be used by the emulation and the other will be used by the manufacturer CPS. First, start the emulation ``` python3 at_dxxx_emulator.py COM5 ``` Here, the COM5 port will be used by the emulation. Then, start the CPS and select `COM6` as the port to be used. ## Emulation under Linux It might be possible to emulate the device under Linux and using Wine to run the CPS. First, a pair of *serial ports* (pts) must be created. This can be done using `socat` by calling ``` socat -d -d pty,raw,echo=0,b4000000 pty,raw,echo=0,b4000000 ``` This call creates a pair of pseudo-terminals, for example `/dev/pts/3` and `/dev/pts/4`. The emulation may then use one of them by calling ``` python3 at_dxxx_emulator.py /dev/pts/3 ``` Now, the second must be associated with a COM port in the Wine emulation. This is done using the registry editor. Run `wine regedit` and change ``` HKEY_LOCAL_MACHINE/Software/Wine/Port/COM1 /dev/pts/4 ``` You may need to create that key first. This will bind the Wine emulated COM port to the second pseudo-terminal. Now, start the manufacturer CPS under Wine and select COM1. ## Differential Codeplug Analysis Create a small codeplug containing at least two channels, two contacts, a group list and a zone. Save that codeplug and write it to the device. The emulator will save that codeplug as a hex dump in a file called `codeplug_0000.hex`. Any further codeplug write will be saved as a separate hex dump with an incremented number. The differential analysis of the codeplug allows to figure out how and where a specific setting is encoded within the binary codeplug. To do that, make a single change in the codeplug. E.g. changing the first radio ID. Then write that changed codeplug to the emulation. The hexdump of the codeplug is then save as `codeplug_0001.hex`. You can then use a diff tool to inspect the changes. E.g. ``` $ diff codeplug_0000.hex codeplug_0001.hex 1530c1530 < 02580000 : 01 23 45 67 00 52 61 64 69 6f 20 31 00 00 00 00 | .#Eg.Radio 1.... --- > 02580000 : 00 12 34 56 00 52 61 64 69 6f 20 31 00 00 00 00 | ..4V.Radio 1.... ``` Here the first radio ID 1234567 was changed to 123456. The only differences between the codeplugs are at the address 2580000. Apparently, the first radio ID is stored there together with the radio name. The first 4 bytes at this address have changed from `01 23 45 67` to `00 12 34 56`. As the numbers are readable in hex, they are stored in BCD and the most-significant number is stored first. So the radio ID is stored in an 8-digit BCD in big-endian. This step is then repeated for all settings within the CPS to figure out the entire codeplug.qdmr-0.12.3/doc/reveng/anytone/d578uv/000077500000000000000000000000001501654372000173155ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/anytone/d578uv/at_d578uv_emulator.py000066400000000000000000000143411501654372000233300ustar00rootroot00000000000000#!/usr/bin/env python3 # # Emulate anytone d878uv radio to customer programming software. # Send intercepted data stream over network to server script for further investigation. # # This script connects to a virtual com port COM26 which is connected via a virtual # null modem cable to the virtual com port COM18 which is used by the programming software. # This virtual ports and cable can be provided by the COM0COM tool. # # Linux users can use # socat -d -d pty,raw,echo=0,b4000000 pty,raw,echo=0,b4000000 # for emulating a virtual null modem cable. import serial import time import sys import struct # config filebase = 'codeplug' filecount = 0 comport = 'COM6' # connected to COM18 with com0com. use COM18 in CPS def hexDump(s): h = " ".join(map("{:02x}".format, s)) t = "" for i in range(len(s)): c = s[i] if c>=0x20 and c<0x7f: t += chr(c) else: t += "." return( h + " | " + t) # parameters? if len(sys.argv) == 2: comport = sys.argv[1] elif len(sys.argv) >= 3: print("Usage: " + sys.argv[0] + ' [comport]') exit() # open serial port serialPort = None try: print("Trying comport " + comport) serialPort = serial.Serial(port = comport, baudrate=4000000, bytesize=8, timeout=1, stopbits=serial.STOPBITS_ONE) # 115200 921600 4000000 except (err): print('ERR: Could not open port ' + comport) print("Usage: " + sys.argv[0] + ' servername [comport]: ' + err) exit() out = None nextaddr = None # wait for data try: while 1: command = '' command = serialPort.read() while serialPort.in_waiting > 0: command += serialPort.read() # respond to command on com port if ( len(command) == 0 ): pass elif ( command == b'PROGRAM'): print("Program session requested.") resp = b'QX\x06' serialPort.write(resp) filename = "{}_{:04}.hex".format(filebase, filecount) out = open(filename, "w") nextaddr = None elif ( command == b'\x02' ): print("Device info requested.") resp = b'ID578UV\x00\x00V110\x00\x00\x06' serialPort.write(resp) elif ( command == b'R\x02\xfa\x00\x20\x10' ): print("Read special memory request.") resp = b'W\x02\xfa\x00\x20\x10\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x24\x06' serialPort.write(resp) elif ( command[0:4] == b'R\x02\xfa\x00' and command[5] == 16 ): # 0x02fa00.. print("Read local information.") resp = b'W\x02\xfa\x00' + bytes([command[4]]) + b'\x10' if ( command[4] == 0x00 ): resp += b'\x00\x00\x00\x03\x01\x01\x01\x00\x00\x01\x01\x20\x20\x20\x20\xff' elif ( command[4] == 0x10 ): # Radio Type resp += b'\x44\x38\x37\x38\x55\x56\x00\x01\x00\xff\xff\xff\xff\xff\xff\xff' elif ( command[4] == 0x30 ): # Serial Number resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x40 ): # Production Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x50 ): # Manucfacture Code resp += b'\x31\x32\x33\x34\x35\x36\x37\x38\xff\xff\xff\xff\xff\xff\xff\xff' elif ( command[4] == 0x60 ): # Maintained Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x70 ): # Dealer Code resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x80 ): # Stock Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x90 ): # Sell Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xa0 ): # Seller resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xb0 ): # Maintained Description resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xc0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xd0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xe0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xf0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' else: resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' resp = resp + bytes( [sum(resp[1:]) & 0xff] ) + b'\x06' #print(resp.hex()) serialPort.write(resp) elif ( command[0] == ord('W') ) : resp = b'\x06' # just ack serialPort.write(resp) res = struct.unpack(">cIB16sBB", command) addr = res[1] if nextaddr != addr: out.write((8+3+16*3+16+2)*"-" + "\n") out.write("{:08X} : {}\n".format(addr, hexDump(res[3]))) nextaddr = addr+16 elif ( command == b'END' ): print("End session.") resp = b'\x06' # just ack serialPort.write(resp) out.close() filecount += 1 elif ( command == b'UPDATE' ): # for firmware update the device has to be switched on while pressing PF3 (blue button on top) and PTT keys print("Start Firmware Update. Only useful if device is in update receiving mode. (Switch on while pressing PF3 (blue button on top) and PTT keys)") resp = b'\x06' # just ack serialPort.write(resp) elif ( command == b'\x18' ): print("Firmware Update Send Complete. Switch device on while pressing PF2 (top left side) and PTT keys to start installer.") resp = b'\x06' # just ack serialPort.write(resp) elif ( command[0] == 0x01 ): print("Firmware data.") resp = b'\x06' # just ack serialPort.write(resp) else: #print("> " + str(command)) pass finally: print('QRT') serialPort.close() qdmr-0.12.3/doc/reveng/anytone/d868uve/000077500000000000000000000000001501654372000174645ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/anytone/d868uve/at_d868uv_emulator.py000066400000000000000000000143361501654372000235050ustar00rootroot00000000000000#!/usr/bin/env python3 # # Emulate anytone d878uv radio to customer programming software. # Send intercepted data stream over network to server script for further investigation. # # This script connects to a virtual com port COM26 which is connected via a virtual # null modem cable to the virtual com port COM18 which is used by the programming software. # This virtual ports and cable can be provided by the COM0COM tool. # # Linux users can use # socat -d -d pty,raw,echo=0,b4000000 pty,raw,echo=0,b4000000 # for emulating a virtual null modem cable. import serial import time import sys import struct # config filebase = 'codeplug' filecount = 0 comport = 'COM6' # connected to COM18 with com0com. use COM18 in CPS def hexDump(s): h = " ".join(map("{:02x}".format, s)) t = "" for i in range(len(s)): c = s[i] if c>=0x20 and c<0x7f: t += chr(c) else: t += "." return( h + " | " + t) # parameters? if len(sys.argv) == 2: comport = sys.argv[1] elif len(sys.argv) >= 3: print("Usage: " + sys.argv[0] + ' [comport]') exit() # open serial port serialPort = None try: print("Trying comport " + comport) serialPort = serial.Serial(port = comport, baudrate=4000000, bytesize=8, timeout=1, stopbits=serial.STOPBITS_ONE) # 115200 921600 4000000 except (err): print('ERR: Could not open port ' + comport) print("Usage: " + sys.argv[0] + ' servername [comport]: ' + err) exit() out = None nextaddr = None # wait for data try: while 1: command = '' command = serialPort.read() while serialPort.in_waiting > 0: command += serialPort.read() # respond to command on com port if ( len(command) == 0 ): pass elif ( command == b'PROGRAM'): print("Program session requested.") resp = b'QX\x06' serialPort.write(resp) filename = "{}_{:04}.hex".format(filebase, filecount) out = open(filename, "w") nextaddr = None elif ( command == b'\x02' ): print("Device info requested.") resp = b'ID868UVE\x00V102\x00\x00\x06' serialPort.write(resp) elif ( command == b'R\x02\xfa\x00\x20\x10' ): print("Read special memory request.") resp = b'W\x02\xfa\x00\x20\x10\xff\xff\xff\xff\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x26\x06' serialPort.write(resp) elif ( command[0:4] == b'R\x02\xfa\x00' and command[5] == 16 ): # 0x02fa00.. print("Read local information.") resp = b'W\x02\xfa\x00' + bytes([command[4]]) + b'\x10' if ( command[4] == 0x00 ): resp += b'\x00\x00\x00\x00\x01\x01\x01\x00\x00\x01\x01\x20\x20\x20\x20\xff' elif ( command[4] == 0x10 ): # Radio Type resp += b'\x44\x38\x37\x38\x55\x56\x00\x01\x00\xff\xff\xff\xff\xff\xff\xff' elif ( command[4] == 0x30 ): # Serial Number resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x40 ): # Production Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x50 ): # Manucfacture Code resp += b'\x31\x32\x33\x34\x35\x36\x37\x38\xff\xff\xff\xff\xff\xff\xff\xff' elif ( command[4] == 0x60 ): # Maintained Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x70 ): # Dealer Code resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x80 ): # Stock Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x90 ): # Sell Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xa0 ): # Seller resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xb0 ): # Maintained Description resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xc0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xd0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xe0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xf0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' else: resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' resp = resp + bytes( [sum(resp[1:]) & 0xff] ) + b'\x06' #print(resp.hex()) serialPort.write(resp) elif ( command[0] == ord('W') ) : resp = b'\x06' # just ack serialPort.write(resp) res = struct.unpack(">cIB16sBB", command) addr = res[1] if nextaddr != addr: out.write((8+3+16*3+16+2)*"-" + "\n") out.write("{:08X} : {}\n".format(addr, hexDump(res[3]))) nextaddr = addr+16 elif ( command == b'END' ): print("End session.") resp = b'\x06' # just ack serialPort.write(resp) out.close() filecount += 1 elif ( command == b'UPDATE' ): # for firmware update the device has to be switched on while pressing PF3 (blue button on top) and PTT keys print("Start Firmware Update. Only useful if device is in update receiving mode. (Switch on while pressing PF3 (blue button on top) and PTT keys)") resp = b'\x06' # just ack serialPort.write(resp) elif ( command == b'\x18' ): print("Firmware Update Send Complete. Switch device on while pressing PF2 (top left side) and PTT keys to start installer.") resp = b'\x06' # just ack serialPort.write(resp) elif ( command[0] == 0x01 ): print("Firmware data.") resp = b'\x06' # just ack serialPort.write(resp) else: #print("> " + str(command)) pass finally: print('QRT') serialPort.close() qdmr-0.12.3/doc/reveng/anytone/d878uv/000077500000000000000000000000001501654372000173205ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/anytone/d878uv/README.md000066400000000000000000000063621501654372000206060ustar00rootroot00000000000000# How to reverse engineer the AnyTone code-plugs Unfortunately, AnyTone CPSs store the code-plug in a completely different format on the disc compared to the content being written to the device. This requires to observe the communication between the host running the original CPS and the device. Moreover, a complete code-plug must be programmed to the device for every option in the CPS, to be able to reverse engineer the format. So to reverse engineer the code-plug format on the device, you first need to get the original CPS running in a virtual machine on a Linux host (I use VirtualBox for that, but any other will do too). Then the *usbmon* kernel module must be loaded to be able to grab all traffic between the host and the radio. So call ``` $ sudo modprobe usbmon ``` Then fire up wireshark ``` $ sudo wireshark ``` and start monitoring on the *usbmon* device. * Then start to upload a base code-plug to the device. Wireshark will capture everything that gets sent to the device. * Once the code-plug is written to the device, stop capturing in wireshark and save the data under a recognizable filename, e.g. *base.pcapng*. * Change a single thing within the CPS, e.g., enable GPS. Remember, we are doing a differential analysis, so change only one setting at a time. * Restart capturing in wireshark and upload the modified code-plug to the device. * Once the code-plug is written, stop capturing and save the data in another file, e.g. *gps_enabled.pcapng*. * Extract the data written to the device using the *extract.py* script with `python3 extract.py PCAPNG_FILE > HEX_FILE` for both captured code-plugs. * Using *diff* you can now compare the two hex files. As only one feature of the code-plug was changed, the difference between the two hex files should be tiny. In fact if only a single setting is changed, frequently only a single bit/byte will change in the code-plug. Repeat the steps above over and over again to find the memory location of each option, setting and feature of the original CPS. Over time, you will be able to reverse engineer the entire code-plug. But keep in mind, that you will not be able to reverse engineer every single bit of the code-plug. At the end, there will be some reserved, fixed or hidden options left. ## An example In this directory, you will find two examples. The captured and extracted base code-plug (*capture_base.pcapng* and *d878uv_base.hex*) as well as a captured and extracted code-plug, where only a single setting was changed (*capture_set_gps_on.pcapng* and *d878uv_set_gps_on.hex*). In this example, the GPS was enabled in the CPS. The *diff* of the two hex files reveals ``` 3029c3029 < 02500020 : 01 00 00 00 00 00 04 01 00 01 01 00 00 01 02 01 | ................ --- > 02500020 : 01 00 00 00 00 00 04 01 01 01 01 00 00 01 02 01 | ................ ``` This tells me, that at the address 0x02500020 + 8, a single byte changed from 00 to 01. The entire remaining code-plug (about 100kB) stayed the same. Comparing the address 0x02500028 with the [coarse memory layout](https://dm3mat.darc.de/qdmr/libdmrconf/class_d878_u_v_codeplug.html) of the code-plug tells me, that byte 40 (hex 0x28) of the general settings section of the code-plug enables or disables the GPS.qdmr-0.12.3/doc/reveng/anytone/d878uv/capture_base.pcapng000066400000000000000000115654001501654372000231630ustar00rootroot00000000000000 M<+6Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz (with SSE4.2)Linux 5.4.0-62-generic:Dumpcap (Wireshark) 3.2.3 (Git v3.2.3 packaged as 3.2.3-1)H@usbmon0  Linux 5.4.0-62-genericH`%'o@@zS<%N` !`hI(oGGzC-%N`ɳ %h`4o@@zS<%N` !`h65oGGzC-%N` %h`Ao@@zS<%N` !`hBoGGzC-%N` %h`&No@@zS<%N` !`h6OoGGzC-%N` %hh[oGGzS%N`N ! h`\o@@zC->%N`f ``ro@@@{DwS<%N` !`h+soGG@{DwC-%N` h`Vo@@DwS%N` !"``o@@DwC->%N` `hoGGzDwS%N`: ! h`o@@zDwC->%N`B ``o@@@rDwS<%N`{% !`hoGG@rDwC-%N`& hh=oGG'zS-%N`}PROGRAMh`o@@'zC->%N``d^~oCC@zC-%N`QXddoAA'zS-%N`Vd`)o@@'zS-<%N`i``do@@'zC->%N``poPP'zC-%N`DCID878UVV100p`/o@@'zS-<%N`oQ`h2oFF'zS-%N`{R h`2o@@'zC->%N`|`x\oXX'zC-%N`W &x`*mo@@'zS-<%N`j`xR`oXXNxS-%N`WCbPvx``o@@NxC->%N`ө`doAA'zC-%N`HdxoXXNxS-%N`<W zx`o@@NxS-<%N`B``^o@@NxC->%N``doAANxC-%N`dxMoXXNxS-%N`W DB0LDS TS1Bx`Qo@@NxS-<%N```o@@NxC->%N``doAANxC-%N`<dxoXXNxS-%N`W0x`o@@NxS-<%N```㯸o@@NxC->%N`#`doAANxC-%N`dx%oXXNxS-%N`eW@CbPvYx`*o@@NxS-<%N`j``po@@NxC->%N``d幸oAANxC-%N`%dxoXXNxS-%N` WP x`o@@NxS-<%N` ``o@@NxC->%N` `dBøoAANxC-%N` dx/ʸoXXNxS-%N`oW`Sa/Th DB0LDS ix`8ʸo@@NxS-<%N`x``Vʸo@@NxC->%N``d`˸oAANxC-%N`dxӸoXXNxS-%N`WpTS1x`Ӹo@@NxS-<%N```Ӹo@@NxC->%N``d>ոoAANxC-%N`~dxooXXNxS-%N`0WCbPvx`uo@@NxS-<%N`0``o@@NxC->%N`0`doAANxC-%N`1dxoXXNxS-%N`:W x`o@@NxS-<%N`:``o@@NxC->%N`%:`dYoAANxC-%N`;dxoXXCGxS-%N`CWTG8 DB0LDS TSx`o@@OGxS-<%N`C``5o@@CGxC->%N`uC`doAAOGxC-%N`DdxoXXOGxS-%N`RLW2rx`o@@CGxS-<%N`]L``|o@@OGxC->%N`L`dwoAACGxC-%N`Mdx oXXCGxS-%N`UWCbPvx` o@@OGxS-<%N`U``' o@@CGxC->%N`gU`dM oAAOGxC-%N`VdxoXXOGxS-%N`*^W Ax`o@@CGxS-<%N`1^``o@@OGxC->%N`O^`doAACGxC-%N`_dxoXXCGxS-%N`FgWTG9 DB0LDS TSx`o@@OGxS-<%N`Ng``io@@CGxC->%N`g`doAAOGxC-%N`hdx'oXXOGxS-%N`ZpW2x`!'o@@CGxS-<%N`ap``y'o@@OGxC->%N`p`d(oAACGxC-%N`qdx/oXXCGxS-%N`yWCbPvx`/o@@OGxS-<%N`y``'0o@@CGxC->%N`gy`d61oAAOGxC-%N`vzdx8oXXOGxS-%N`W x`8o@@CGxS-<%N```49o@@OGxC->%N`t`dM:oAACGxC-%N`dxAoXXCGxS-%N` W BB DB0LDS TS2x`Ao@@OGxS-<%N```Bo@@CGxC->%N`R`d7CoAAOGxC-%N`wdxJoXXOGxS-%N`W0x`Jo@@CGxS-<%N```Jo@@OGxC->%N`3`d$LoAACGxC-%N`ddxuSoXXCGxS-%N`W@C%vx`~So@@OGxS-<%N```So@@CGxC->%N`ݜ`dToAAOGxC-%N`1dx\oXXOGxS-%N`NWP x`\o@@CGxS-<%N`U``Y\o@@OGxC->%N``ds]oAACGxC-%N`dxdoXXCGxS-%N`ĭW`DM0TZN TS1x`do@@OGxS-<%N`ϭ``do@@CGxC->%N`'`deoAAOGxC-%N`dx+moXXOGxS-%N`kWpx`6mo@@CGxS-<%N`v``mo@@OGxC->%N`ж`dnoAACGxC-%N`ɷdxuoXXCGxS-%N`WC%vx`uo@@OGxS-<%N`%``;vo@@CGxC->%N`{`d3woAAOGxC-%N`sdxY~oXXOGxS-%N`W x`e~o@@CGxS-<%N```~o@@OGxC->%N``doAACGxC-%N`dxoXXCGxS-%N`DWTG8 DM0TZN TSx`o@@OGxS-<%N`R``ho@@CGxC->%N``dPoAAOGxC-%N`dxoXXOGxS-%N`W2sx`o@@CGxS-<%N```׏o@@OGxC->%N``d oAACGxC-%N`Kdx(oXXCGxS-%N`hWC%v@x`3o@@OGxS-<%N`s``o@@CGxC->%N``doAAOGxC-%N`dxӠoXXOGxS-%N`W Ax`ޠo@@CGxS-<%N```o@@OGxC->%N`>`doAACGxC-%N`XdxPoXXCGxS-%N`WTG9 DM0TZN TSx`\o@@OGxS-<%N```o@@CGxC->%N``d᪹oAAOGxC-%N`!dxoXXOGxS-%N`@W2x` o@@CGxS-<%N`L``So@@OGxC->%N``dPoAACGxC-%N`dxoXXCGxS-%N`WC%vx`o@@OGxS-<%N```o@@CGxC->%N`.`d໹oAAOGxC-%N` dx)ùoXXOGxS-%N`i W x`5ùo@@CGxS-<%N`u ``ùo@@OGxC->%N` `dxĹoAACGxC-%N` dx˹oXXCGxS-%N`W BB DM0TZN TS2x`˹o@@OGxS-<%N`#``̹o@@CGxC->%N`B`d͹oAAOGxC-%N`YdxoXXOGxS-%N`+W0x`o@@CGxS-<%N`+``o@@OGxC->%N`,`doAACGxC-%N`-dx6oXXCGxS-%N`v4W@Cuv x`Io@@OGxS-<%N`4``fo@@CGxC->%N`4`doAAOGxC-%N`5dxoXXOGxS-%N`=WP x`o@@CGxS-<%N`=``o@@OGxC->%N`6=`doAACGxC-%N`H>dxoXXCGxS-%N`EW`DB0LOS TS1x`o@@OGxS-<%N`E``o@@CGxC->%N`,F`doAAOGxC-%N`/Gdx oXXOGxS-%N`LNWpx`o@@CGxS-<%N`]N``=o@@OGxC->%N`}N`dkoAACGxC-%N`Odx oXXCGxS-%N` WWCuvMx` o@@OGxS-<%N`"W`` o@@CGxC->%N`9W`doAAOGxC-%N`RXdxoXXOGxS-%N`_W x`o@@CGxS-<%N`_``o@@OGxC->%N`_`doAACGxC-%N`adxmoXXCGxS-%N`hWR/TG9 DB0LOS mx`o@@OGxS-<%N`h``o@@CGxC->%N`h`d oAAOGxC-%N`jdx,(oXXOGxS-%N`lqWTS2x`=(o@@CGxS-<%N`}q``^(o@@OGxC->%N`q`dy)oAACGxC-%N`rdx 1oXXzS-%N`IzWCPvx`1o@@zS-<%N`Wz``;1o@@zC->%N`{z`dO2oAAzC-%N`{dx9oXXzS-%N` W Bx`9o@@zS-<%N```:o@@zC->%N`A`de;oAAzC-%N`dx[BoXXzS-%N`WR/TG9 DM0TT Tx`nBo@@zS-<%N```Bo@@zC->%N``dCoAAzC-%N`dxKoXXzS-%N`^WS1x`+Ko@@zS-<%N`k``lKo@@zC->%N``deLoAAzC-%N`dxSoXXzS-%N`WCPv<x`So@@zS-<%N```So@@zC->%N`7`dToAAzC-%N`.dxk\oXXzS-%N`W x`x\o@@zS-<%N```\o@@zC->%N``d]oAAzC-%N`dx-eoXXzS-%N`mW TG8 DM0TT TS2 x`:eo@@zS-<%N`z``}eo@@zC->%N``dfoAAzC-%N`dxmoXXzS-%N`W0x`mo@@zS-<%N` ``7no@@zC->%N`w`dooAAzC-%N`YdxvoXXzS-%N`W@CPv|x`vo@@zS-<%N`#``"wo@@zC->%N`b`d6xoAAzC-%N`vdxoXXzS-%N`WP x`o@@zS-<%N```o@@zC->%N`!`doAAzC-%N`>dxQoXXzS-%N`W`TG9 DM0TT TS2Kx`]o@@zS-<%N```o@@zC->%N``doAAzC-%N`dxsoXXzS-%N`Wpx`o@@zS-<%N```o@@zC->%N``d㑺oAAzC-%N`#dx9oXXzS-%N`yWCPvx`Go@@zS-<%N```o@@zC->%N``doAAzC-%N`dxǡoXXzS-%N`W x`ԡo@@zS-<%N```o@@zC->%N`U`doAAzC-%N`Tdx?oXXzS-%N`WBB DM0TT TS2;x`Jo@@zS-<%N```o@@zC->%N``doAAzC-%N`dx޲oXXzS-%N`WCx`겺o@@zS-<%N`*``)o@@zC->%N`i`d$oAAzC-%N`ddxdoXXzS-%N`WC7Pvx`mo@@zS-<%N```o@@zC->%N``doAAzC-%N`5dxĺoXXzS-%N`\ W Cx`(ĺo@@zS-<%N`h ``mĺo@@zC->%N` `d`źoAAzC-%N`dx̺oXXzS-%N`WTG9 DB0KK TS1x`̺o@@zS-<%N```̺o@@zC->%N`5`dͺoAAzC-%N`(dxպoXXzS-%N`^Wx`)պo@@zS-<%N`i``lպo@@zC->%N``dnֺoAAzC-%N`dxݺoXXzS-%N`&WC7Pvx`ݺo@@zS-<%N`&``ݺo@@zC->%N`-'`d޺oAAzC-%N`;(dxKoXXzS-%N`/W x`Vo@@zS-<%N`/``o@@zC->%N`/`doAAzC-%N`0dxoXXzS-%N`8W BB DB0KK TS2x`o@@zS-<%N`"8``'o@@zC->%N`g8`d5oAAzC-%N`u9dx|oXXzS-%N`@W0x`o@@zS-<%N`@``o@@zC->%N` A`doAAzC-%N` BdxoXXzS-%N`YIW@C%vx`$o@@zS-<%N`dI``do@@zC->%N`I`dboAAzC-%N`JdxoXXzS-%N`RWP x`o@@zS-<%N`R``" o@@zC->%N`bR`d oAAzC-%N`YSdxXoXXzS-%N`ZW`DB0OUD TS1x`eo@@zS-<%N`Z``o@@zC->%N`Z`doAAzC-%N` \dx oXX@MwS-%N`McWpx`o@@MwS-<%N`[c``\o@@@MwC->%N`c`daoAAMwC-%N`ddx"oXXMwS-%N`lWC%vx`"o@@@MwS-<%N`l``#o@@MwC->%N`Ul`d$oAA@MwC-%N`Hmdx+oXXMwS-%N`tW x`+o@@MwS-<%N`t``+o@@MwC->%N` u`d,oAAMwC-%N` vdx 4oXX@MwS-%N`M}WTG8 DB0OUD TSx`4o@@@MwS-<%N`X}``X4o@@@MwC->%N`}`da5oAA@MwC-%N`~dx%N`&`d=oAAMwC-%N`=dxWEoXXMwS-%N`WC%vAx`fEo@@MwS-<%N```Eo@@MwC->%N``dFoAAMwC-%N`dx NoXX@MwS-%N`LW Dx`No@@@MwS-<%N`X``VNo@@@MwC->%N``doOoAA@MwC-%N`dxVoXXMwS-%N`WTG9 DB0OUD TSx`Vo@@MwS-<%N```Wo@@MwC->%N`U`dXoAAMwC-%N`Edx_oXX@MwS-%N`̨W2x`_o@@MwS-<%N`ب``_o@@@MwC->%N``d aoAAMwC-%N`IdxhoXXMwS-%N`VWC%vx`"ho@@@MwS-<%N`b``aho@@MwC->%N``dsioAA@MwC-%N`dxpoXXMwS-%N`W x`po@@MwS-<%N```qo@@MwC->%N`Z`droAAMwC-%N`ZdxyoXX@MwS-%N`W BB DB0OUD TS2x`yo@@@MwS-<%N```yo@@@MwC->%N``dzoAA@MwC-%N`#dxLoXXMwS-%N`W0x`Wo@@MwS-<%N```o@@MwC->%N``doAAMwC-%N`dxoXXMwS-%N`FW@Cuv!x`o@@MwS-<%N`R``Yo@@MwC->%N``dboAAMwC-%N`dxɓoXX@MwS-%N` WP x`֓o@@@MwS-<%N```o@@@MwC->%N`[`d.oAA@MwC-%N`ndxoXXMwS-%N`W`TG9 DB0TU TS1Ax`o@@MwS-<%N```˜o@@MwC->%N` `d᝻oAAMwC-%N`!dx%N``doAAMwC-%N`dxoXXMwS-%N`3WCuvax`o@@@MwS-<%N`>``%N`|`dEoAA@MwC-%N`dxwoXXMwS-%N`W x`o@@MwS-<%N```o@@MwC->%N``dҷoAAMwC-%N` dx2oXX@MwS-%N`r WBB DB0TU TS23x`>o@@@MwS-<%N`~ ``o@@@MwC->%N` `doAA@MwC-%N` dxȻoXXMwS-%N`B WEx` Ȼo@@MwS-<%N`M ``LȻo@@MwC->%N` `d_ɻoAAMwC-%N` dxлoXXMwS-%N` WC%vQx`лo@@MwS-<%N`  ``ѻo@@MwC->%N`O `d5һoAAMwC-%N`u dxLٻoXX@MwS-%N`" W ?x`Yٻo@@@MwS-<%N`" ``ٻo@@@MwC->%N`" `dڻoAA@MwC-%N`# dx oXXMwS-%N`M+ WDM0MOT TS1x`o@@MwS-<%N`Z+ ``Yo@@MwC->%N`+ `dkoAAMwC-%N`, dxoXX@MwS-%N` 4 Wx`o@@MwS-<%N`4 ``o@@@MwC->%N`;4 `doAAMwC-%N`U5 dxoXX@8yS-%N`< WC%vx`o@@8yS-<%N`< ``o@@@8yC->%N`= `doAA8yC-%N`> dxHoXX8yS-%N`E W x`Wo@@8yS-<%N`E ``o@@8yC->%N`E `doAA8yC-%N`F dxoXXMwS-%N`N W TG8 DM0MOT TS#x`o@@@MwS-<%N`N ``o@@MwC->%N`[N `doAA@MwC-%N`WO dxoXXMwS-%N`d W02x`o@@MwS-<%N`d ``o@@MwC->%N`e `doAAMwC-%N`%f dx $oXX@MwS-%N`Im W@C%vx`$o@@@MwS-<%N`Wm ``Z$o@@@MwC->%N`m `dn%oAA@MwC-%N`n dx,oXXMwS-%N`v WP x`,o@@MwS-<%N`$v ``'-o@@MwC->%N`gv `d#.oAAMwC-%N`cw dxl5oXXMwS-%N`~ W`TG9 DM0MOT TSdx`z5o@@MwS-<%N`~ ``5o@@MwC->%N`~ `d6oAAMwC-%N` dx#>oXXzS-%N`c Wp28x`3>o@@@zS-<%N`s ``\>o@@zC->%N` `dj?oAA@zC-%N` dxFoXX@zS-%N`9 WC%vx` Go@@zS-<%N`I ``)Go@@@zC->%N`i `dXHoAAzC-%N` dxOoXXzS-%N`Ę W x`Oo@@zS-<%N`Ә ``Oo@@zC->%N` `dPoAAzC-%N` dx&XoXXzS-%N`f WBB DM0MOT TS2x`AXo@@zS-<%N` ``WXo@@zC->%N` `doYoAAzC-%N` dx`oXXzS-%N`# WFx``o@@zS-<%N`2 ``ao@@zC->%N`Q `d#boAAzC-%N`c dxioXXzS-%N`޲ WCuvx`io@@@zS-<%N` ``io@@zC->%N`0 `dkoAA@zC-%N`O dxProXXzS-%N` W @x`_ro@@zS-<%N` ``ro@@zC->%N` `dsoAAzC-%N`ۼ dx {oXXzS-%N`I WDB0TA TS1x`{o@@zS-<%N`W ``;{o@@zC->%N`{ `dK|oAAzC-%N` dxoXXzS-%N` Wx`o@@zS-<%N` ``o@@zC->%N` `d܄oAAzC-%N` dx2oXXzS-%N`r WCuvx`?o@@zS-<%N` ``co@@zC->%N` `doAAzC-%N` dx3oXXzS-%N`s W x`Eo@@@zS-<%N` ``lo@@zC->%N` `doAA@zC-%N` dx圼oXXzS-%N`% W TG8 DB0TA TS2x`o@@zS-<%N`6 ``o@@zC->%N`^ `d%oAAzC-%N`e dxoXXzS-%N` W0x`o@@zS-<%N` ``̥o@@zC->%N` `doAAzC-%N`1 dx5oXXzS-%N`u W@Cuv"x`Ao@@zS-<%N` ``^o@@zC->%N` `dioAAzC-%N` dxoXXzS-%N` WP x`o@@zS-<%N` ``o@@zC->%N`A `doAAzC-%N`N dxFoXX@zS-%N` W`TG9 DB0TA TS21x`Po@@zS-<%N` ``o@@@zC->%N` `doAAzC-%N` dxeǼoXXzS-%N` Wpx`nǼo@@zS-<%N` ``Ǽo@@zC->%N` `dȼoAAzC-%N` dxмoXXzS-%N`C WCuvbx`мo@@zS-<%N`Q ``Xмo@@zC->%N` `dZѼoAAzC-%N` dxqؼoXXzS-%N`! W x`|ؼo@@zS-<%N`! ``ؼo@@zC->%N`! `dټoAAzC-%N`!# dxpoXXvDwS-%N`* WBB DB0TA TS2!x`yo@@wDwS-<%N`* ``o@@vDwC->%N`* `d9oAAwDwC-%N`y, dxoXX}DwS-%N`3 WGx`o@@@uDwS-<%N`3 ``o@@}DwC->%N`4 `doAA@uDwC-%N`P5 dx!oXX@~DwS-%N`a< WCPv}x`'o@@pDwS-<%N`g< ``fo@@@~DwC->%N`< `doAApDwC-%N`= dxoXX'zS-%N`7E W Ax`o@@@'zS-<%N`?E ``:o@@'zC->%N`zE `doAA@'zC-%N`F dxoXX'zS-%N`N WDB0FX TS1x`o@@'zS-<%N` N ``o@@'zC->%N`XN `d'oAA'zC-%N`gO dx oXX'zS-%N`V Wx` o@@'zS-<%N`V `` o@@'zC->%N`%W `da*oAA'zC-%N`s dx8oXX'zS-%N` WCPvx`8o@@@'zS-<%N` ``9o@@'zC->%N`] `dU:oAA@'zC-%N` dxNoXX'zS-%N`: W x`Oo@@'zS-<%N`@ ``>Oo@@'zC->%N`~ `dPoAA'zC-%N` dxWoXX'zS-%N` W TG8 DB0FX TS2x`Wo@@@'zS-<%N` ``Xo@@'zC->%N`P `dbYoAA@'zC-%N` dx `oXX'zS-%N`` W0x`)`o@@@'zS-<%N`i ``d`o@@'zC->%N` `draoAA@'zC-%N` dxjhoXX'zS-%N` W@CPvx`sho@@'zS-<%N` ``ho@@'zC->%N` `dioAA'zC-%N` dxpoXX'zS-%N` WP x`po@@'zS-<%N` ``po@@'zC->%N`9 `dqoAA'zC-%N`% dx+yoXXNxS-%N`k W`TG9 DB0FX TS2;x`9yo@@NxS-<%N`y ``Xyo@@NxC->%N` `d^zoAANxC-%N` dx*oXXNxS-%N`j Wpx`2o@@NxS-<%N`r ``Oo@@NxC->%N` `dɃoAANxC-%N` dxӊoXXNxS-%N` WCPv>x`ۊo@@NxS-<%N` ``o@@NxC->%N`H `d#oAANxC-%N`c dxoXXNxS-%N` W  x`o@@NxS-<%N` ``ۓo@@NxC->%N` `d oAANxC-%N`J dxÜoXXNxS-%N` WBB DB0FX TS2+x`ɜo@@NxS-<%N` ``ꜽo@@NxC->%N`* `d7oAANxC-%N`w dxΥoXXNxS-%N` WHx`եo@@NxS-<%N` ``o@@NxC->%N`3 `dToAANxC-%N` dxUoXXNxS-%N` WCvx`[o@@NxS-<%N` ``zo@@NxC->%N` `dįoAANxC-%N` dxoXXNxS-%N`_ W Bx`&o@@NxS-<%N`f ``Co@@NxC->%N` `doAANxC-%N` dxͽoXXNxS-%N` WDB0PDM TS1x`ͽo@@NxS-<%N` ``ͽo@@NxC->%N`' `d4ϽoAANxC-%N`t dxֽoXXNxS-%N` Wx`ֽo@@NxS-<%N` ``ֽo@@NxC->%N` `dؽoAANxC-%N`C! dx߽oXXCGxS-%N`( W Cv_x`߽o@@OGxS-<%N`( ``߽o@@CGxC->%N` ) `doAAOGxC-%N`O* dx@oXXOGxS-%N`1 W  x`Jo@@CGxS-<%N`1 ``io@@OGxC->%N`1 `doAACGxC-%N`2 dxoXXCGxS-%N`2: W TG8 DB0PDM TS x`o@@OGxS-<%N`>: ``o@@CGxC->%N`[: `dSoAAOGxC-%N`; dxoXXOGxS-%N`B W 02x`o@@CGxS-<%N`B `` o@@OGxC->%N`MC `doAACGxC-%N`OD dxeoXXCGxS-%N`K W @Cvx`qo@@OGxS-<%N`K ``o@@CGxC->%N`L `doAAOGxC-%N`L dx oXXOGxS-%N`9T W P x` o@@CGxS-<%N`GT ``e o@@OGxC->%N`T `dm oAACGxC-%N`U dxoXXCGxS-%N`\ W `TG9 DB0PDM TSMx`o@@OGxS-<%N` ] ``o@@CGxC->%N`-] `d oAAOGxC-%N`L^ dxJoXXOGxS-%N`e W p2;x`Wo@@CGxS-<%N`e ``yo@@OGxC->%N`e `doAACGxC-%N`f dx$oXXCGxS-%N`$n W Cvx`$o@@OGxS-<%N`3n ``%o@@CGxC->%N`Un `dC&oAAOGxC-%N`o dx`-oXXOGxS-%N`v W   x`l-o@@CGxS-<%N`v ``-o@@OGxC->%N`v `d.oAACGxC-%N` x dx86oXXNxS-%N`x W BB DB0PDM TS2ox`F6o@@NxS-<%N` ``e6o@@NxC->%N` `d7oAANxC-%N`׀ dx>oXXNxS-%N`3 W Ix`?o@@NxS-<%N`@ ``A?o@@NxC->%N` `dX@oAANxC-%N` dxGoXXNxS-%N` W CPvx`Go@@NxS-<%N` ``Ho@@NxC->%N`̑ `dIoAANxC-%N`Ւ dxHPoXXNxS-%N` W  Cx`WPo@@NxS-<%N` ``Po@@NxC->%N`י `dQoAANxC-%N`š dxXoXXNxS-%N` W DB0BRB TS1x`Xo@@NxS-<%N` ``Xo@@NxC->%N` `d ZoAANxC-%N`K dxoaoXXCGxS-%N` W x`ao@@OGxS-<%N` ``ao@@CGxC->%N`# `dboAAOGxC-%N`3 dxjoXXOGxS-%N`N W CPvGx`!jo@@CGxS-<%N`a ``Kjo@@OGxC->%N` `dpkoAACGxC-%N` dxroXXCGxS-%N` W  x`ro@@OGxS-<%N` ``so@@CGxC->%N`A `dtoAAOGxC-%N`Y dx{oXXNxS-%N` W TG8 DB0BRB TSx`{o@@NxS-<%N` ``|o@@NxC->%N`] `d>}oAANxC-%N`~ dxoXXNxS-%N` W 02x`o@@NxS-<%N` ``o@@NxC->%N` `doAANxC-%N` dxoXXNxS-%N` W @CPvx`o@@NxS-<%N` ``ύo@@NxC->%N` `dَoAANxC-%N` dxsoXXNxS-%N` W P x`o@@NxS-<%N` ``o@@NxC->%N` `d՗oAANxC-%N` dx4oXXNxS-%N`t W `TG9 DB0BRB TSCx`Eo@@NxS-<%N` ``~o@@NxC->%N` `doAANxC-%N` dx姾oXXOGxS-%N`% W p2<x`o@@CGxS-<%N`: ``!o@@OGxC->%N`a `dSoAACGxC-%N` dxoXX@GxS-%N` W CPvx`o@@CGxS-<%N` ``o@@@GxC->%N` `dѱoAACGxC-%N` dxsƾoXXOGxS-%N` W   x`ƾo@@LGxS-<%N` ``ƾo@@OGxC->%N` `dǾoAALGxC-%N`  dx)ϾoXX@KGxS-%N`i W BB DB0BRB TS2ex`BϾo@@CGxS-<%N` ``YϾo@@@KGxC->%N` `dоoAACGxC-%N` dx׾oXX@NGxS-%N` W Jx`׾o@@CGxS-<%N` ``׾o@@@NGxC->%N`$! `d پoAACGxC-%N`I" dx\oXXNxS-%N`) W CPvx`mo@@NxS-<%N`) ``o@@NxC->%N`) `doAANxC-%N`* dx"oXXNxS-%N`b2 W  Dx`/o@@NxS-<%N`o2 ``Po@@NxC->%N`2 `doAANxC-%N`3 dxoXXNxS-%N`0; W DB0SPN TS1x`o@@NxS-<%N`?; ``wo@@NxC->%N`; `doAANxC-%N`< dxoXXNxS-%N`C W x`o@@NxS-<%N`D ``o@@NxC->%N`CD `doAANxC-%N`QE dxsoXXNxS-%N`L W CPvHx`o@@NxS-<%N`L ``o@@NxC->%N`M `doAANxC-%N`N dx? oXXNxS-%N`U W  x`M o@@NxS-<%N`U `` o@@NxC->%N`U `d oAANxC-%N`W dx oXXNxS-%N`J^ W R/TG9 DB0SPN x`o@@NxS-<%N`X^ ``\o@@NxC->%N`^ `dwoAANxC-%N`_ dxoXXNxS-%N`g W 0TS2x`o@@NxS-<%N`g `` o@@NxC->%N`Mg `doAANxC-%N`Uh dxu&oXXNxS-%N`o W @CPvx`&o@@NxS-<%N`o ``&o@@NxC->%N`p `d'oAANxC-%N`q dx8/oXXNxS-%N`xx W P x`D/o@@NxS-<%N`x ``/o@@NxC->%N`x `d0oAANxC-%N`y dx7oXXNxS-%N`8 W `DB0NLS TS1x`8o@@NxS-<%N`D ``C8o@@NxC->%N` `dN9oAANxC-%N` dx@oXXNxS-%N` W p x`@o@@NxS-<%N`̉ ``@o@@NxC->%N` `dAoAANxC-%N` dxIoXXNxS-%N` W CPvTx`Io@@NxS-<%N` ``Io@@NxC->%N` `dJoAANxC-%N` dxYRoXXNxS-%N` W   x`cRo@@NxS-<%N` ``Ro@@NxC->%N` `dSoAANxC-%N` dx[oXXNxS-%N`\ W R/TG9 DB0NLS ux`+[o@@NxS-<%N`k ``c[o@@NxC->%N` `d\oAANxC-%N`ȥ dxcoXXNxS-%N` W TS2$x`co@@NxS-<%N` `` do@@NxC->%N`` `d+eoAANxC-%N`k dxloXXNxS-%N`ȵ W C%vDx`lo@@NxS-<%N`Ե ``lo@@NxC->%N` `dmoAANxC-%N` dx oXXNxS-%N`J W  Ex`o@@NxS-<%N`V ``Ro@@NxC->%N` `d`oAANxC-%N` dx‹oXXNxS-%N` W DB0LS TS1x`͋o@@NxS-<%N` ``o@@NxC->%N`\ `d(oAANxC-%N`h dxoXXNxS-%N` W x`o@@NxS-<%N` ``Дo@@NxC->%N` `d̕oAANxC-%N` dx>oXXNxS-%N`~ W C%vx`Jo@@NxS-<%N` ``o@@NxC->%N` `dÞoAANxC-%N` dxoXXNxS-%N`6 W  x`o@@NxS-<%N`A ``?o@@NxC->%N` `dYoAANxC-%N` dxoXXNxS-%N` W TG8 DB0LS TS2x`o@@NxS-<%N` ``o@@NxC->%N`4 `doAANxC-%N`2 dxdoXXNxS-%N` W 0x`oo@@NxS-<%N` ``o@@NxC->%N` `doAANxC-%N` dx%oXXNxS-%N`e W @C%vx`3o@@NxS-<%N`s ``ro@@NxC->%N` `dooAANxC-%N` dxȿoXXNxS-%N` W P x`ȿo@@NxS-<%N`+ ``Nɿo@@NxC->%N` `dMʿoAANxC-%N` dxmѿoXXNxS-%N` W `TG9 DB0LS TS2@x`zѿo@@NxS-<%N` ``ѿo@@NxC->%N` `dҿoAANxC-%N`  dx:ڿoXXNxS-%N`z# W p x`Fڿo@@NxS-<%N`# ``ڿo@@NxC->%N`# `dۿoAANxC-%N`$ dxoXXNxS-%N`&, W C%vx`o@@NxS-<%N`1, ``2o@@NxC->%N`r, `dJoAANxC-%N`- dxoXXNxS-%N`4 W   x`o@@NxS-<%N`4 ``o@@NxC->%N`5 `doAANxC-%N`U6 dxoXXNxS-%N`K W BB DB0LS TS20x`o@@NxS-<%N`K ``o@@NxC->%N`XK `d+oAANxC-%N`kL dx oXXNxS-%N`S W Lx` o@@NxS-<%N`S `` o@@NxC->%N`T `d oAANxC-%N`2U dxEoXXNxS-%N`\ W CPvx`Po@@NxS-<%N`\ ``o@@NxC->%N`\ `doAANxC-%N`] dxoXXNxS-%N`4e W  Fx`o@@NxS-<%N`@e ``Ao@@NxC->%N`e `dUoAANxC-%N`f dx$oXXNxS-%N`m W DM0LC TS1x`$o@@NxS-<%N`m ``$o@@NxC->%N`2n `d%oAANxC-%N`4o dx(-oXXNxS-%N`hv W x`4-o@@NxS-<%N`tv ``-o@@NxC->%N`v `d.oAANxC-%N`x dx5oXXNxS-%N` W CPvIx`5o@@NxS-<%N`) ``'6o@@NxC->%N`g `dM7oAANxC-%N` dxv>oXXNxS-%N` W  x`>o@@NxS-<%N` ``>o@@NxC->%N` `d?oAANxC-%N` dx+GoXXNxS-%N`k W TG8 DM0LC TS2x`6Go@@NxS-<%N`v ``pGo@@NxC->%N` `dHoAANxC-%N`ɑ dxOoXXNxS-%N`% W 0x`Oo@@NxS-<%N`/ ``-Po@@NxC->%N`m `dFQoAANxC-%N` dxXoXXNxS-%N`١ W @CPvx`Xo@@NxS-<%N` ``Xo@@NxC->%N` `dYoAANxC-%N` dxKaoXXNxS-%N` W P x`Vao@@NxS-<%N` ``ao@@NxC->%N` `dboAANxC-%N`ի dxioXXNxS-%N`7 W `TG9 DM0LC TS2<x`jo@@NxS-<%N`A ``Djo@@NxC->%N` `dDkoAANxC-%N` dxroXXNxS-%N` W p x`ro@@NxS-<%N` ``so@@NxC->%N`P `dtoAANxC-%N`V dxj{oXXNxS-%N` W CPvx`v{o@@NxS-<%N` ``{o@@NxC->%N` `d|oAANxC-%N`! dx*oXXNxS-%N`j W  x`7o@@NxS-<%N`w ``wo@@NxC->%N` `doAANxC-%N` dxoXXNxS-%N` W Sa/Th DM0LC Tx`ʌo@@NxS-<%N` `` o@@NxC->%N`K `doAANxC-%N`_ dx|oXXNxS-%N` W S2x`o@@NxS-<%N` ``Ǖo@@NxC->%N` `doAANxC-%N` dx oXXNxS-%N`J W CPv x`o@@NxS-<%N`Y ``6o@@NxC->%N`v `daoAANxC-%N` dx̦oXX@"{zS-%N` W  Nx`٦o@@"{zS-<%N` ``o@@@"{zC->%N`\ `dMoAA"{zC-%N` dxoXX"{zS-%N` W BB DM0LC TS1jx`o@@@"{zS-<%N` ``o@@"{zC->%N`$ `doAA@"{zC-%N`$ dxFoXX@"{zS-%N`W x`To@@"{zS-<%N```o@@@"{zC->%N``doAA"{zC-%N`dx oXX"{zS-%N`J WC7Pvx`o@@@"{zS-<%N`W ``Xo@@"{zC->%N` `dvoAA@"{zC-%N` dxoXX@"{zS-%N`'W x`o@@"{zS-<%N`5``o@@@"{zC->%N`Y`dAoAA"{zC-%N`dxoXX"{zS-%N`W DB0FLW TS1Vx`o@@@"{zS-<%N```o@@"{zC->%N`.`doAA@"{zC-%N`1dx)oXX@"{zS-%N`i$W0x`6o@@"{zS-<%N`v$``wo@@@"{zC->%N`$`doAA"{zC-%N`%dxoXX"{zS-%N`-W@C7Pv<x`o@@@"{zS-<%N`!-``"o@@"{zC->%N`b-`d(oAA@"{zC-%N`h.dxtoXXzS-%N`5WP x`o@@zS-<%N`5``o@@zC->%N`5`doAAzC-%N`6dx,oXXzS-%N`l>W`TG8 DB0FLW TSYx`Ko@@zS-<%N`>``zo@@zC->%N`>`doAAzC-%N`?dxoXXzS-%N`0GWp2@x`o@@zS-<%N`>G``@o@@zC->%N`G`d%N`'P`doAAzC-%N`\QdxoXX@KGxS-%N`XW x`o@@LGxS-<%N` Y``o@@@KGxC->%N`5Y`doAALGxC-%N`XZdxQoXXOGxS-%N`aWTG9 DB0FLW TSx`_o@@CGxS-<%N`a``o@@OGxC->%N`a`doAACGxC-%N`bdx!oXX@GxS-%N`TjW2x`!!o@@CGxS-<%N`aj``D!o@@@GxC->%N`j`du"oAACGxC-%N`kdx7oXXOGxS-%N`BWC7Pvx`7o@@LGxS-<%N`R``77o@@OGxC->%N`w`dk8oAALGxC-%N`dx?oXX@KGxS-%N`W Qx`?o@@CGxS-<%N```?o@@@KGxC->%N`8`d.AoAACGxC-%N`ndxQHoXX@NGxS-%N`WSa/Th DB0FLW x`_Ho@@CGxS-<%N```Ho@@@NGxC->%N`Ǒ`dIoAACGxC-%N`ߒdxQoXX@KGxS-%N`ZWTS2gx`(Qo@@LGxS-<%N`h``OQo@@@KGxC->%N``dRoAALGxC-%N`śdxYoXXOGxS-%N`WCuvx`Yo@@CGxS-<%N`'`` Zo@@OGxC->%N`M`d [oAACGxC-%N`LdxboXX@GxS-%N`W x`bo@@CGxS-<%N` ``bo@@@GxC->%N`,`ddoAACGxC-%N`XdxjkoXXOGxS-%N`W DB0LE TS1x`vko@@LGxS-<%N```ko@@OGxC->%N`ڴ`dloAALGxC-%N` dxtoXX@KGxS-%N`^W0x`+to@@CGxS-<%N`k``to@@@KGxC->%N``dluoAACGxC-%N`dx|oXX@NGxS-%N`9W@Cuv+x`}o@@CGxS-<%N`H``*}o@@@NGxC->%N`j`dA~oAACGxC-%N`dxoXX@KGxS-%N`WP x`o@@LGxS-<%N```مo@@@KGxC->%N``d'oAALGxC-%N`gdxeoXXOGxS-%N`W`R/TG9 DB0LE T1x`to@@CGxS-<%N```o@@OGxC->%N``dԏoAACGxC-%N`dx–oXX@GxS-%N`WpS2x`ϖo@@CGxS-<%N```"o@@@GxC->%N`b`d"oAACGxC-%N`bdxoXXOGxS-%N`WCux`o@@LGxS-<%N```o@@OGxC->%N``doAALGxC-%N`,dxHoXX@KGxS-%N`W  x`Uo@@CGxS-<%N```xo@@@KGxC->%N``doAACGxC-%N`dxoXX@NGxS-%N`TWDL DB0AFZ TS1x`"o@@CGxS-<%N`b``lo@@@NGxC->%N``doAACGxC-%N`dxɹoXX@KGxS-%N` WOx`ֹo@@LGxS-<%N```o@@@KGxC->%N`8`doAALGxC-%N`NdxKoXXOGxS-%N` WCux`Xo@@CGxS-<%N` ``o@@OGxC->%N` `doAACGxC-%N` dx oXX@GxS-%N`JW Px`o@@CGxS-<%N`W``5o@@@GxC->%N`u`dloAACGxC-%N`dxoXXOGxS-%N`WBB DB0AFZ TS1x`o@@LGxS-<%N```o@@OGxC->%N`X`d'oAALGxC-%N`gdxvoXX@KGxS-%N`%Wx`o@@CGxS-<%N`%``o@@@KGxC->%N`&`doAACGxC-%N`,BdxoXX@NGxS-%N`WCux`o@@CGxS-<%N```o@@@NGxC->%N``d0oAACGxC-%N`0 dx oXX@KGxS-%N`W x` o@@LGxS-<%N``` o@@@KGxC->%N``d oAALGxC-%N`dxmoXXOGxS-%N`mW TG9 DB0AFZ TSx`zo@@CGxS-<%N`z``o@@OGxC->%N``doAACGxC-%N`dxoXX@GxS-%N`#W02x`$o@@CGxS-<%N`$#``|o@@@GxC->%N`|#`dzoAACGxC-%N`z$dx$oXXOGxS-%N`+W@CuNx`$o@@LGxS-<%N`+``#%o@@OGxC->%N`#,`d &oAALGxC-%N` -dxY-oXX@KGxS-%N`Y4WP x`e-o@@CGxS-<%N`e4``-o@@@KGxC->%N`4`d.oAACGxC-%N`5dx6oXX@NGxS-%N`=W`Hes DB0AFZ TSx`$6o@@CGxS-<%N`$=``x6o@@@NGxC->%N`x=`du7oAACGxC-%N`u>dx>oXX@KGxS-%N`EWp2Bx`>o@@LGxS-<%N`E``(?o@@@KGxC->%N`(F`d;@oAALGxC-%N`;GdxHGoXXOGxS-%N`HNWC4P x`UGo@@CGxS-<%N`UN``Go@@OGxC->%N`N`dHoAACGxC-%N`OdxHPoXX@GxS-%N`HWW x`TPo@@CGxS-<%N`TW``Po@@@GxC->%N`W`dQoAACGxC-%N`XdxjYoXXOGxS-%N`j`WDMR S0x`wYo@@LGxS-<%N`w```Yo@@OGxC->%N```dZoAALGxC-%N`adxaoXX@KGxS-%N`hWPx`ao@@CGxS-<%N`h``Bbo@@@KGxC->%N`Bi`dHcoAACGxC-%N`HjdxljoXX@NGxS-%N`lqWC6 x`vjo@@CGxS-<%N`vq``jo@@@NGxC->%N`q`dkoAACGxC-%N`rdx soXX@KGxS-%N` zW Wx`so@@LGxS-<%N`z``2so@@@KGxC->%N`2z`dtoAALGxC-%N`{dx{oXXOGxS-%N`WDMR S1x`{o@@CGxS-<%N```{o@@OGxC->%N``d|oAACGxC-%N`كdx9oXX@GxS-%N`9Wx`Do@@CGxS-<%N`D``o@@@GxC->%N``d~oAACGxC-%N`~dxoXXOGxS-%N`WC6% Hx`o@@LGxS-<%N```o@@OGxC->%N``doAALGxC-%N`dx^oXX@KGxS-%N`^W x`io@@CGxS-<%N`i``o@@@KGxC->%N``doAACGxC-%N`dx!oXX@NGxS-%N`!W DMR S2Jx`,o@@CGxS-<%N`,``io@@@NGxC->%N`i`d}oAACGxC-%N`}dxdzoXX@KGxS-%N`ǺW0x`ѳo@@LGxS-<%N`Ѻ``*o@@@KGxC->%N`*`doAALGxC-%N`dxkoXXOGxS-%N`kW@C68 x`yo@@CGxS-<%N`y``ռo@@OGxC->%N``dȽoAACGxC-%N`dxoXX@GxS-%N`WP x`o@@CGxS-<%N```Ao@@@GxC->%N`A`d7oAACGxC-%N`7dxoXXOGxS-%N`W`DMR S3x`o@@LGxS-<%N```o@@OGxC->%N``doAALGxC-%N`dx5oXX@KGxS-%N`5Wpx`@o@@CGxS-<%N`@``o@@@KGxC->%N``d{oAACGxC-%N`{dxoXX@NGxS-%N`WC6Psx`o@@CGxS-<%N```o@@@NGxC->%N``doAACGxC-%N`dxoXX@KGxS-%N`W x`0o@@LGxS-<%N`0``Io@@@KGxC->%N`I`dGoAALGxC-%N`GdxoXXOGxS-%N`WDMR S4x`o@@CGxS-<%N```!o@@OGxC->%N`!`doAACGxC-%N`dx\oXX@GxS-%N`\WQx`ho@@CGxS-<%N`h``o@@@GxC->%N``doAACGxC-%N`dxoXXOGxS-%N`WC6c Fx`o@@LGxS-<%N```ao@@OGxC->%N`a`dUoAALGxC-%N`U dxi oXX@KGxS-%N`iW Xx`s o@@CGxS-<%N`s`` o@@@KGxC->%N``d oAACGxC-%N`dx oXX@NGxS-%N` WDMR S5 x`o@@CGxS-<%N```oo@@@NGxC->%N`o`dKoAACGxC-%N`KdxoXX@KGxS-%N`!Wx`o@@LGxS-<%N`!``o@@@KGxC->%N`"`doAALGxC-%N`#dxg#oXXOGxS-%N`g*WC6u x`t#o@@CGxS-<%N`t*``#o@@OGxC->%N`*`d$oAACGxC-%N`+dx+oXX@GxS-%N`2W x`+o@@CGxS-<%N`2``{,o@@@GxC->%N`{3`dz-oAACGxC-%N`z4dx4oXXOGxS-%N`;W DMR S6Ox`4o@@LGxS-<%N`;``4o@@OGxC->%N`;`d5oAALGxC-%N`<dx=oXX@KGxS-%N`DW0x`%=o@@CGxS-<%N`%D``=o@@@KGxC->%N`D`d~>oAACGxC-%N`~EdxEoXX@NGxS-%N`LW@C6 x`Eo@@CGxS-<%N`L``Eo@@@NGxC->%N`L`dGoAACGxC-%N`Ndx}NoXX@KGxS-%N`}UWP x`No@@LGxS-<%N`U``No@@@KGxC->%N`U`dOoAALGxC-%N`VdxVoXXOGxS-%N`]W`DMR S7x` Wo@@CGxS-<%N` ^``gWo@@OGxC->%N`g^`deXoAACGxC-%N`e_dx_oXX@GxS-%N`fWpx`_o@@CGxS-<%N`f```o@@@GxC->%N`g`d`oAACGxC-%N`gdxbhoXXOGxS-%N`boWCbPvx`rho@@LGxS-<%N`ro``ho@@OGxC->%N`o`dioAALGxC-%N`pdx qoXX@KGxS-%N` xW x`qo@@CGxS-<%N`x``lqo@@@KGxC->%N`lx`dsroAACGxC-%N`sydxyoXX@NGxS-%N`WDB0LDSx`yo@@CGxS-<%N```yo@@@NGxC->%N``dzoAACGxC-%N`dxUoXX@KGxS-%N`UWRx`bo@@LGxS-<%N`b``o@@@KGxC->%N``doAALGxC-%N`dxoXXOGxS-%N`WCv6x`o@@CGxS-<%N```o@@OGxC->%N``d=oAACGxC-%N`=dxoXX@GxS-%N`W Hx`o@@CGxS-<%N``` o@@@GxC->%N` `doAACGxC-%N`dxުoXXOGxS-%N`ޱWDB0RAGx`o@@LGxS-<%N```o@@OGxC->%N``d/oAALGxC-%N`/dxԳoXX@KGxS-%N`ԺWx`o@@CGxS-<%N```o@@@KGxC->%N``dHoAACGxC-%N`HdxoXX@NGxS-%N`WCuvx` o@@CGxS-<%N` ``0o@@@NGxC->%N`0`dMoAACGxC-%N`MdxoXX@KGxS-%N`W x`o@@LGxS-<%N```Ko@@@KGxC->%N`K`dGoAALGxC-%N`GdxoXXOGxS-%N`W DB0LUD^x`o@@CGxS-<%N```o@@OGxC->%N``doAACGxC-%N`dxgoXX@GxS-%N`gW0x`uo@@CGxS-<%N`u``o@@@GxC->%N``doAACGxC-%N`dxoXXCGxS-%N`W@Vx`o@@OGxS-<%N```2o@@CGxC->%N`2`dEoAAOGxC-%N`EdxoXXOGxS-%N`WP x`o@@CGxS-<%N```o@@OGxC->%N``doAACGxC-%N`dxoXXCGxS-%N`W`DB0SP-2x` o@@OGxS-<%N` ``o@@CGxC->%N``d-oAAOGxC-%N`-dxdoXXOGxS-%N`dWpx`mo@@CGxS-<%N`m``o@@OGxC->%N``dzoAACGxC-%N`zdxoXXCGxS-%N`WC%vx`o@@OGxS-<%N```o@@CGxC->%N``doAAOGxC-%N` dx oXXOGxS-%N`W x` o@@CGxS-<%N``` o@@OGxC->%N``d0 oAACGxC-%N`0dxoXXCGxS-%N`WDB0SP-700x`o@@OGxS-<%N```o@@CGxC->%N``dRoAAOGxC-%N`Rdx2&oXXOGxS-%N`2-WSx`:&o@@CGxS-<%N`:-``z&o@@OGxC->%N`z-`d'oAACGxC-%N`.dx;/oXXCGxS-%N`;6WVbP x`E/o@@OGxS-<%N`E6``/o@@CGxC->%N`6`d0oAAOGxC-%N`7dx-8oXXOGxS-%N`-?W Ix`38o@@CGxS-<%N`3?``w8o@@OGxC->%N`w?`d9oAACGxC-%N`@dx6AoXXLGxS-%N`6HWDB0ZOD-2x`>Ao@@@KGxS-<%N`>H``zAo@@LGxC->%N`zH`dBoAA@KGxC-%N`Idx8JoXXCGxS-%N`8QWx`?Jo@@@NGxS-<%N`?Q``zJo@@CGxC->%N`zQ`dKoAA@NGxC-%N`RdxRoXX@NGxS-%N`YWC%vx`Ro@@CGxS-<%N`Y``;So@@@NGxC->%N`;Z`dUToAACGxC-%N`U[dx[oXX@KGxS-%N`bW x`[o@@LGxS-<%N`b``[o@@@KGxC->%N`b`d\oAALGxC-%N`cdxgdoXXCGxS-%N`gkW DB0ZOD-70x`pdo@@OGxS-<%N`pk``do@@CGxC->%N`k`deoAAOGxC-%N`ldx=moXX@GxS-%N`=tW0x`Fmo@@OGxS-<%N`Ft``mo@@@GxC->%N`t`dnoAAOGxC-%N`udxuoXXCGxS-%N`|W@W%x`vo@@LGxS-<%N`}``Tvo@@CGxC->%N`T}`d_woAALGxC-%N`_~dx~oXX@KGxS-%N`WP x`~o@@CGxS-<%N`…``~o@@@KGxC->%N``dLoAACGxC-%N`LdxVoXX@NGxS-%N`VW`DB0BRLx`eo@@CGxS-<%N`e``o@@@NGxC->%N``dˆoAACGxC-%N`dxۏoXX@KGxS-%N`ۖWpx`o@@LGxS-<%N```Ao@@@KGxC->%N`A`doAALGxC-%N`dxoXXCGxS-%N`WCuvlx`o@@OGxS-<%N```o@@CGxC->%N``dęoAAOGxC-%N`ĠdxoXX@GxS-%N`W x`o@@OGxS-<%N```lo@@@GxC->%N`l`d^oAAOGxC-%N`^dxĩoXXCGxS-%N`İWDB0BLOx`ҩo@@LGxS-<%N`Ұ``o@@CGxC->%N``doAALGxC-%N`dxoXX@KGxS-%N`WTx`o@@CGxS-<%N```o@@@KGxC->%N``doAACGxC-%N`dxHoXX@NGxS-%N`HWCPvx`Vo@@CGxS-<%N`V``yo@@@NGxC->%N`y`doAACGxC-%N`dxoXX@KGxS-%N`W Jx` o@@LGxS-<%N` ``Eo@@@KGxC->%N`E`d\oAALGxC-%N`\dxoXXCGxS-%N`WDB0SXx`o@@OGxS-<%N```o@@CGxC->%N``dmoAAOGxC-%N`mdxoXX@GxS-%N`Wx` o@@OGxS-<%N` ``.o@@@GxC->%N`.`dHoAAOGxC-%N`HdxoXXCGxS-%N`WC%vx`o@@LGxS-<%N```o@@CGxC->%N``d'oAALGxC-%N`'dxoXX@KGxS-%N`W x`o@@CGxS-<%N```o@@@KGxC->%N``doAACGxC-%N`dxhoXXAzS-%N`hW DB0TAx`|o@@y&zS-<%N`|``o@@AzC->%N``doAAy&zC-%N`dxoXXy&zS-%N` W0x`o@@ƯzS-<%N```(o@@y&zC->%N`(`d(oAAƯzC-%N`(dxxoXXƯzS-%N`xW@VuRx`o@@JyCzS-<%N```o@@ƯzC->%N``doAAJyCzC-%N`dx-oXXJyCzS-%N`-WP x`=o@@vS-<%N`=``o@@JyCzC->%N``dqoAAvC-%N`q dx oXX@vS-%N`'W`DB0PDMx` o@@@vS-<%N`'`` o@@@vC->%N`'`d!oAA@vC-%N`(dxx)oXX@vS-%N`x0Wpx`)o@@@vS-<%N`0``)o@@@vC->%N`0`d*oAA@vC-%N`1dxC2oXXvS-%N`C9WCPv>x`\2o@@@vS-<%N`\9``~2o@@vC->%N`~9`dd3oAA@vC-%N`d:dx:oXX@vS-%N`AW x`:o@@@vS-<%N`A``:o@@@vC->%N`A`d%N`J`dDoAA@vC-%N`KdxBLoXX@vS-%N`BSWUx`QLo@@@vS-<%N`QS``Lo@@@vC->%N`S`dMoAA@vC-%N`Tdx UoXXvS-%N` \WC7Pvx`Uo@@@vS-<%N`\``ZUo@@vC->%N`Z\`dYVoAA@vC-%N`Y]dx]oXX@vS-%N`dW Kx`]o@@@vS-<%N`d``^o@@@vC->%N`e`d _oAA@vC-%N` fdxfoXXvS-%N`mWDB0AFx`fo@@@vS-<%N`m``fo@@vC->%N`m`dgoAA@vC-%N`ndxooXX@vS-%N`vWx`oo@@@vS-<%N`v``>oo@@@vC->%N`>v`dHpoAA@vC-%N`HwdxwoXXvS-%N`~WCPvx`wo@@@vS-<%N`~``7xo@@vC->%N`7`d:yoAA@vC-%N`:dxooXX@vS-%N`oW x`o@@@vS-<%N```o@@@vC->%N``doAA@vC-%N`dxoXX@vS-%N`W DB0NFL\x`o@@@vS-<%N```:o@@@vC->%N`:`doAA@vC-%N`dxroXX@vS-%N`rW0x`o@@@vS-<%N```o@@@vC->%N``d˟oAA@vC-%N`˦dxoXX@vS-%N`W@Cvx`o@@@vS-<%N```6o@@@vC->%N`6`dToAA@vC-%N`TdxįoXX@vS-%N`ĶWP x`үo@@@vS-<%N`Ҷ``o@@@vC->%N``doAA@vC-%N`dxoXX@vS-%N`W`DB0LMMx`o@@@vS-<%N```o@@@vC->%N``d߹oAA@vC-%N`dx`oXX@vS-%N``Wpx`oo@@@vS-<%N`o``o@@@vC->%N``doAA@vC-%N`dxoXX@vS-%N`WCPvHx`o@@@vS-<%N```=o@@@vC->%N`=`dNoAA@vC-%N`NdxoXX@vS-%N`W x`o@@@vS-<%N```o@@@vC->%N``dAoAA@vC-%N`AdxoXX@vS-%N`WDM0LEIx`o@@@vS-<%N```o@@@vC->%N``doAA@vC-%N`dxFoXX@vS-%N`FWVx`To@@@vS-<%N`T``~o@@@vC->%N`~`doAA@vC-%N`dxoXX@vS-%N`WWux` o@@@vS-<%N` ``9o@@@vC->%N`9`dfoAA@vC-%N`fdxoXX@vS-%N`W Lx`o@@@vS-<%N```o@@@vC->%N``d@oAA@vC-%N`@dx4oXX@vS-%N`4WDB0LEIx`Fo@@vS-<%N`F``xo@@@vC->%N`x`doAAvC-%N`dxoXXvS-%N` Wx`o@@@vS-<%N` ``o@@vC->%N` `d oAA@vC-%N` dxZoXX@vS-%N`ZWCuvx`ho@@@vS-<%N`h``o@@@vC->%N``doAA@vC-%N`dxoXXvS-%N`W x`%o@@@vS-<%N`%``o@@vC->%N``doAA@vC-%N` dx oXX@vS-%N`'W DB0SMLix` o@@@vS-<%N`'`` o@@@vC->%N`'`dI"oAA@vC-%N`I)dxY)oXXvS-%N`Y0W0x`g)o@@@vS-<%N`g0``)o@@vC->%N`0`d*oAA@vC-%N`1dx2oXX@vS-%N`9W@VbPx`+2o@@@vS-<%N`+9``q2o@@@vC->%N`q9`dj3oAA@vC-%N`j:dx:oXXvS-%N`AWP x`:o@@@vS-<%N`A``:o@@vC->%N`A`d;oAA@vC-%N`BdxtCoXX@vS-%N`tJW`DB0LSAx`Co@@@vS-<%N`J``Co@@@vC->%N`J`dDoAA@vC-%N`Kdx3LoXXvS-%N`3SWpx`ALo@@@vS-<%N`AS``Lo@@vC->%N`S`dMoAA@vC-%N`TdxToXX@vS-%N`[WCPvx`To@@@vS-<%N`[`` Uo@@@vC->%N` \`dVoAA@vC-%N`]dx3]oXXvS-%N`3dW x`?]o@@@vS-<%N`?d``~]o@@vC->%N`~d`d^oAA@vC-%N`edxeoXX@vS-%N`lWDB0LEx`eo@@@vS-<%N`l``>fo@@@vC->%N`>m`dPgoAA@vC-%N`PndxnoXXvS-%N`uWWx`no@@@vS-<%N`u``oo@@vC->%N`v`dpoAA@vC-%N`wdxzwoXX@vS-%N`z~WFuH~x`wo@@@vS-<%N`~``wo@@@vC->%N`~`dxoAA@vC-%N`dx%oXXvS-%N`%W Nx`;o@@@vS-<%N`;``o@@vC->%N``d7oAA@vC-%N`7dx׈oXX@vS-%N`׏WDOK Y07Ex`o@@@vS-<%N```&o@@@vC->%N`&`d8oAA@vC-%N`8dx{oXXvS-%N`{Wx`o@@@vS-<%N```ґo@@vC->%N`Ҙ`doAA@vC-%N`dxWoXX@vS-%N`WWFPZx`eo@@@vS-<%N`e``o@@@vC->%N``doAA@vC-%N`dxoXXvS-%N`W x`*o@@@vS-<%N`*``ko@@vC->%N`k`d{oAA@vC-%N`{dxoXX@vS-%N`W DOK D20lx`o@@@vS-<%N```0o@@@vC->%N`0`dToAA@vC-%N`TdxoXXvS-%N`W0x`o@@@vS-<%N```/o@@vC->%N`/`d%N``doAA@vC-%N`dxoXXvS-%N`WP x` o@@@vS-<%N` ``Oo@@vC->%N`O`d_oAA@vC-%N`_dxoXX@vS-%N`W`DOK D23x`o@@@vS-<%N```o@@@vC->%N``d&oAA@vC-%N`&dxoXXvS-%N`Wpx`o@@@vS-<%N```go@@vC->%N`g`dDoAA@vC-%N`DdxEoXX@vS-%N`EWG%x`So@@@vS-<%N`S``o@@@vC->%N``doAA@vC-%N`dxoXXvS-%N` W x`o@@@vS-<%N` ``So@@vC->%N`S `dioAA@vC-%N`i dx oXX@vS-%N`WDOK S31x` o@@@vS-<%N``` o@@@vC->%N``d oAA@vC-%N`dxToXXvS-%N`TWXx`ao@@@vS-<%N`a``o@@vC->%N``doAA@vC-%N`dxoXX@vS-%N`#WSx`'o@@@vS-<%N`'#``fo@@@vC->%N`f#`d|oAA@vC-%N`|$dx$oXXvS-%N`+W Ox`$o@@@vS-<%N`+``$o@@vC->%N`+`d%oAA@vC-%N`,dxn-oXX@vS-%N`n4WS20=x`z-o@@@vS-<%N`z4``-o@@@vC->%N`4`d.oAA@vC-%N`5dx5oXXvS-%N`<Wx` 6o@@@vS-<%N` =``I6o@@vC->%N`I=`d7oAA@vC-%N`>dx>oXX@vS-%N`EWUx`>o@@@vS-<%N`E``?o@@@vC->%N`F`d2@oAA@vC-%N`2GdxToXXvS-%N`[W x`To@@@vS-<%N`[``-Uo@@vC->%N`-\`d)VoAA@vC-%N`)]dx5]oXX@vS-%N`5dW 2m Mobil{x`B]o@@@vS-<%N`Bd``]o@@@vC->%N`d`dz^oAA@vC-%N`zedxeoXXvS-%N`lW0x`eo@@@vS-<%N`l``eo@@vC->%N`l`dfoAA@vC-%N`mdx7noXX@vS-%N`7uW@C5ix`@no@@@vS-<%N`@u``no@@@vC->%N`u`dooAA@vC-%N`vdxvoXXvS-%N`}WP x`vo@@@vS-<%N`}``3wo@@vC->%N`3~`d%N``doAA@vC-%N`dxgoXXvS-%N`gWpx`so@@@vS-<%N`s``o@@vC->%N``doAA@vC-%N`dxoXX@vS-%N`WHx`o@@@vS-<%N```@o@@@vC->%N`@`dXoAA@vC-%N`XdxoXXvS-%N`W x`o@@@vS-<%N```͙o@@vC->%N`͠`dƚoAA@vC-%N`ơdx.oXX@vS-%N`.WAPRSx`;o@@@vS-<%N`;``|o@@@vC->%N`|`doAA@vC-%N`dxoXXvS-%N`WYx`o@@@vS-<%N```%N`<`dNoAA@vC-%N`NdxoXX@vS-%N`WX%x`o@@@vS-<%N```o@@@vC->%N``d!oAA@vC-%N`!dxoXXvS-%N`W Px` o@@@vS-<%N` ``zo@@vC->%N`z`dnoAA@vC-%N`ndxoXX@vS-%N`WISS APRSx`o@@@vS-<%N```o@@@vC->%N``doAA@vC-%N`dxpoXXvS-%N`pWx`}o@@@vS-<%N`}``o@@vC->%N``doAA@vC-%N`dx)oXX@vS-%N`)WCp)3x`8o@@@vS-<%N`8``vo@@@vC->%N`v`dwoAA@vC-%N`wdxoXXvS-%N`W x`o@@@vS-<%N```(o@@vC->%N`(`dCoAA@vC-%N`CdxoXX@vS-%N`W ISS FM Repx`o@@@vS-<%N```o@@@vC->%N``doAA@vC-%N`dxoXXvS-%N`W0x`o@@@vS-<%N```Zo@@vC->%N`Z`dWoAA@vC-%N`WdxoXX@vS-%N`W@Cr)(Px`o@@@vS-<%N```o@@@vC->%N``doAA@vC-%N`dxtoXXvS-%N`tWP x`o@@@vS-<%N```o@@vC->%N``doAA@vC-%N` dx: oXX@vS-%N`:W`CAS-3H FM<x`I o@@@vS-<%N`I`` o@@@vC->%N``d oAA@vC-%N`dxoXXvS-%N`Wpx`o@@@vS-<%N```&o@@vC->%N`&`d>oAA@vC-%N`>dxoXX@vS-%N`"WY)Hx`o@@@vS-<%N`"``o@@@vC->%N`"`doAA@vC-%N`$dx^$oXXvS-%N`^+W x`k$o@@@vS-<%N`k+``$o@@vC->%N`+`d%oAA@vC-%N`,dx-oXX@vS-%N`4WPO-101 FM[x`(-o@@@vS-<%N`(4``n-o@@@vC->%N`n4`d.oAA@vC-%N`5dx5oXXvS-%N`<WZx`5o@@@vS-<%N`<``6o@@vC->%N`=`d7oAA@vC-%N`>dxKoXX@vS-%N`RWCg) @x`Ko@@@vS-<%N`R``-Lo@@@vC->%N`-S`dWMoAA@vC-%N`WTdxToXXvS-%N`[W Px`To@@@vS-<%N`[``To@@vC->%N`[`dUoAA@vC-%N`\dx]oXX@vS-%N`dWSO-50 FMqx`]o@@@vS-<%N`d``]o@@@vC->%N`d`d^oAA@vC-%N`edxeoXXvS-%N`lWx`eo@@@vS-<%N`l``fo@@vC->%N`m`dgoAA@vC-%N`ndx%N`u`dooAA@vC-%N`vdxvoXXvS-%N`}W x`wo@@@vS-<%N`~``Dwo@@vC->%N`D~`dPxoAA@vC-%N`PdxoXX@vS-%N`W AO-92 FMx`o@@@vS-<%N```o@@@vC->%N``doAA@vC-%N`dxoXXvS-%N`W0x`*o@@@vS-<%N`*``ko@@vC->%N`k`dgoAA@vC-%N`gdxoXX@vS-%N`W@Y`(HOx`o@@@vS-<%N```o@@@vC->%N``doAA@vC-%N`dxLoXXvS-%N`LWP x`Xo@@@vS-<%N`X``o@@vC->%N``d`oAA@vC-%N``dxoXX@vS-%N`W`AO-91 FMx`o@@@vS-<%N```Io@@@vC->%N`I`deoAA@vC-%N`edxNoXXvS-%N`NWpx`Zo@@@vS-<%N`Z``o@@vC->%N``doAA@vC-%N`dxoXX@vS-%N`WCpx`ɲo@@@vS-<%N`ɹ`` o@@@vC->%N` `d oAA@vC-%N` dxaoXXvS-%N`aW x`lo@@@vS-<%N`l``o@@vC->%N``doAA@vC-%N`dxoXX@vS-%N`WU0x`o@@@vS-<%N```Yo@@@vC->%N`Y`dRoAA@vC-%N`RdxoXXvS-%N`W[x`o@@@vS-<%N``` o@@vC->%N` `dBoAA@vC-%N`BdxCoXX@vS-%N`CWC%@x`Oo@@@vS-<%N`O``o@@@vC->%N``doAA@vC-%N`dxoXXvS-%N`W Qx`o@@@vS-<%N```6o@@vC->%N`6`dRoAA@vC-%N`RdxgoXX@vS-%N`gWU1x`uo@@@vS-<%N`u``o@@@vC->%N``doAA@vC-%N`dx oXXvS-%N` Wx`+o@@@vS-<%N`+``lo@@vC->%N`l`dloAA@vC-%N`ldxoXX@vS-%N`WCPAx`o@@@vS-<%N```o@@@vC->%N``d9oAA@vC-%N`9dxoXXvS-%N`W x`o@@@vS-<%N```@o@@vC->%N`@`dqoAA@vC-%N`qdxoXX@vS-%N`W U2Sx`o@@@vS-<%N```o@@@vC->%N``d oAA@vC-%N`dxPoXXvS-%N`PW0x`]o@@@vS-<%N`]``o@@vC->%N``doAA@vC-%N`dxoXX@vS-%N` W@Cux`o@@@vS-<%N`!``Fo@@@vC->%N`F!`dHoAA@vC-%N`H"dx"oXXvS-%N`)WP x`"o@@@vS-<%N`)``"o@@vC->%N`)`d#oAA@vC-%N`*dxK+oXX@vS-%N`K2W`U3x`X+o@@@vS-<%N`X2``+o@@@vC->%N`2`d,oAA@vC-%N`3dx3oXXvS-%N`:Wpx`3o@@@vS-<%N`:``54o@@vC->%N`5;`dD5oAA@vC-%N`D<dxw%N`C`d=oAACGxC-%N`Ddx.EoXXCGxS-%N`.LW x`9Eo@@@NGxS-<%N`9L``Eo@@CGxC->%N`L`dyFoAA@NGxC-%N`yMdxMoXX@NGxS-%N`TWPMR 1x`Mo@@CGxS-<%N`T``KNo@@@NGxC->%N`KU`d\OoAACGxC-%N`\VdxVoXXCGxS-%N`]W\x`Vo@@@NGxS-<%N`]``Wo@@CGxC->%N`^`dWoAA@NGxC-%N`^dxc_oXX@NGxS-%N`cfWD`u@x`n_o@@CGxS-<%N`nf``_o@@@NGxC->%N`f`d`oAACGxC-%N`gdxhoXXCGxS-%N`oW Sx`!ho@@@NGxS-<%N`!o``xho@@CGxC->%N`xo`dwioAA@NGxC-%N`wpdxpoXX@NGxS-%N`wWPMR 2x`po@@CGxS-<%N`w``po@@@NGxC->%N`w`d:roAACGxC-%N`:ydx~yoXXCGxS-%N`~Wx`yo@@@NGxS-<%N```yo@@CGxC->%N``dzoAA@NGxC-%N`dxoXX@NGxS-%N`WD`1%@x`o@@CGxS-<%N```Wo@@@NGxC->%N`W`dVoAACGxC-%N`Vdx̊oXXCGxS-%N`̑W x`يo@@@NGxS-<%N`ّ``.o@@CGxC->%N`.`doAA@NGxC-%N`dxqoXX@NGxS-%N`qW PMR 3x`o@@CGxS-<%N```o@@@NGxC->%N``doAACGxC-%N`dx+oXXCGxS-%N`+W0x`7o@@@NGxS-<%N`7``o@@CGxC->%N``doAA@NGxC-%N`dxoXX@NGxS-%N`W@D`CuIx`ͤo@@CGxS-<%N`ͫ`` o@@@NGxC->%N` `doAACGxC-%N`dxoXXCGxS-%N`WP x`o@@@NGxS-<%N```o@@CGxC->%N``doAA@NGxC-%N`dxoXX@NGxS-%N`W`PMR 4Px`o@@CGxS-<%N```+o@@@NGxC->%N`+`d#oAACGxC-%N`#dxoXXCGxS-%N`Wpx`ʾo@@@NGxS-<%N```o@@CGxC->%N``doAA@NGxC-%N`dxkoXX@NGxS-%N`kWD`V%Lx`wo@@CGxS-<%N`w``o@@@NGxC->%N``doAACGxC-%N`dxoXXCGxS-%N`W x`o@@@NGxS-<%N```0o@@CGxC->%N`0`d%oAA@NGxC-%N`%dxoXX@NGxS-%N`WPMR 5x`o@@CGxS-<%N```o@@@NGxC->%N``doAACGxC-%N`dxGoXXCGxS-%N`GW]x`To@@@NGxS-<%N`T``vo@@CGxC->%N`v`doAA@NGxC-%N`dxoXX@NGxS-%N`WD`hu@.x` o@@CGxS-<%N` ``-o@@@NGxC->%N`-`dfoAACGxC-%N`fdxoXXCGxS-%N`W Tx`o@@@NGxS-<%N```9o@@CGxC->%N`9`dKoAA@NGxC-%N`KdxioXX@NGxS-%N`iWPMR 6x`vo@@CGxS-<%N`v``o@@@NGxC->%N``doAACGxC-%N`dxoXXCGxS-%N` Wx`o@@@NGxS-<%N` ``o@@CGxC->%N` `d+oAA@NGxC-%N`+ dx oXX@NGxS-%N`WD`%@8x` o@@CGxS-<%N``` o@@@NGxC->%N``d oAACGxC-%N`dxCoXXCGxS-%N`CW x`Po@@@NGxS-<%N`P``so@@CGxC->%N`s`doAA@NGxC-%N`dxoXX@NGxS-%N`#W PMR 7x`o@@CGxS-<%N`$``Vo@@@NGxC->%N`V$`dYoAACGxC-%N`Y%dxd%oXXCGxS-%N`d,W0x`o%o@@@NGxS-<%N`o,``%o@@CGxC->%N`,`d&oAA@NGxC-%N`-dx-oXX@NGxS-%N`4W@D`u@x`.o@@CGxS-<%N`5``W.o@@@NGxC->%N`W5`dP/oAACGxC-%N`P6dx6oXXCGxS-%N`=WP x`6o@@@NGxS-<%N`=``6o@@CGxC->%N`=`d7oAA@NGxC-%N`>dx-?oXX@NGxS-%N`-FW`PMR 8Ux`??o@@CGxS-<%N`?F``^?o@@@NGxC->%N`^F`d@oAACGxC-%N`GdxGoXXCGxS-%N`NWpx`Go@@@NGxS-<%N`N``&Ho@@CGxC->%N`&O`dZIoAA@NGxC-%N`ZPdxPoXX@NGxS-%N`WWDa%x`Po@@CGxS-<%N`W``Qo@@@NGxC->%N`X`d9RoAACGxC-%N`9YdxYoXXCGxS-%N``W x`Yo@@@NGxS-<%N````Yo@@CGxC->%N```dZoAA@NGxC-%N`adxiboXX@NGxS-%N`iiWPMR 9x`|bo@@CGxS-<%N`|i``bo@@@NGxC->%N`i`dcoAACGxC-%N`jdxjoXXCGxS-%N`qW^x`ko@@@NGxS-<%N`r``%ko@@CGxC->%N`%r`dgloAA@NGxC-%N`gsdxsoXX@NGxS-%N`zWDaux`so@@CGxS-<%N`z``so@@@NGxC->%N`z`duoAACGxC-%N`|dxr|oXXCGxS-%N`rW Ux`|o@@@NGxS-<%N```|o@@CGxC->%N``d}oAA@NGxC-%N`τdx:oXX@NGxS-%N`:WPMR 10x`Ko@@CGxS-<%N`K``ho@@@NGxC->%N`h`doAACGxC-%N`dxÍoXXCGxS-%N`ÔWx`ҍo@@@NGxS-<%N`Ҕ``o@@CGxC->%N``doAA@NGxC-%N`dxoXX@NGxS-%N`WDa1%@x`o@@CGxS-<%N```Ɩo@@@NGxC->%N`Ɲ`doAACGxC-%N`dx oXXCGxS-%N` W x`o@@@NGxS-<%N```@o@@CGxC->%N`@`dqoAA@NGxC-%N`qdxoXX@NGxS-%N`W PMR 11@x`ҧo@@CGxS-<%N`Ү``o@@@NGxC->%N``d oAACGxC-%N` dxoXXCGxS-%N`W0x`o@@@NGxS-<%N```o@@CGxC->%N``dұoAA@NGxC-%N`Ҹdx2oXX@NGxS-%N`2W@DaCuLx`No@@CGxS-<%N`N``_o@@@NGxC->%N`_`doAACGxC-%N`dxoXXCGxS-%N`WP x`o@@@NGxS-<%N```o@@CGxC->%N``d?oAA@NGxC-%N`?dxoXX@NGxS-%N`W`PMR 12x`o@@CGxS-<%N```o@@@NGxC->%N``doAACGxC-%N`dxPoXXCGxS-%N`PWpx`Yo@@@NGxS-<%N`Y``xo@@CGxC->%N`x`doAA@NGxC-%N`dxoXX@NGxS-%N`WDaV%Ox`o@@CGxS-<%N```:o@@@NGxC->%N`:`dqoAACGxC-%N`qdxoXXCGxS-%N`W x`o@@@NGxS-<%N``` o@@CGxC->%N` `doAA@NGxC-%N`dxoXX@NGxS-%N`WPMR 13x` o@@CGxS-<%N` ``Go@@@NGxC->%N`G`d:oAACGxC-%N`:dxoXXCGxS-%N`W_x`o@@@NGxS-<%N```o@@CGxC->%N``doAA@NGxC-%N`dxoXX@NGxS-%N` WDahux`o@@CGxS-<%N` ``\o@@@NGxC->%N`\ `dIoAACGxC-%N`I dxoXXCGxS-%N` W Vx`o@@@NGxS-<%N` ``o@@CGxC->%N` `doAA@NGxC-%N` dxoXX@NGxS-%N` WPMR 14x`o@@CGxS-<%N` ``^o@@@NGxC->%N`^ `doAACGxC-%N` dxoXXCGxS-%N` Wx`o@@@NGxS-<%N` ``o@@CGxC->%N` `d 4oAA@NGxC-%N` ; dx8oXX@NGxS-%N`? W x`8o@@CGxS-<%N`? ``9o@@@NGxC->%N`@ `d9oAACGxC-%N`@ dxAoXXCGxS-%N`H W x`Ao@@@NGxS-<%N`H ``Ao@@CGxC->%N`H `dBoAA@NGxC-%N`I dxLJoXX@NGxS-%N`LQ W x`SJo@@CGxS-<%N`SQ ``qJo@@@NGxC->%N`qQ `dKoAACGxC-%N`R dx\SoXXCGxS-%N`\Z W 0x`bSo@@@NGxS-<%N`bZ ``So@@CGxC->%N`Z `dToAA@NGxC-%N`[ dx\oXX@NGxS-%N`c W @x`\o@@CGxS-<%N`c ``6\o@@@NGxC->%N`6c `d]oAACGxC-%N`d dxdoXXCGxS-%N`k W Px`do@@@NGxS-<%N`k ``eo@@CGxC->%N`l `dfoAA@NGxC-%N`m dxnoXX@NGxS-%N`u W `x`no@@CGxS-<%N`u ``]no@@@NGxC->%N`]u `dooAACGxC-%N`v dxAwoXXCGxS-%N`A~ W p x`Gwo@@@NGxS-<%N`G~ ``wo@@CGxC->%N`~ `dxoAA@NGxC-%N` dxloXX@NGxS-%N`l W 0x`qo@@CGxS-<%N`q ``o@@@NGxC->%N` `doAACGxC-%N` dxoXXCGxS-%N` W @x`o@@@NGxS-<%N` ``o@@CGxC->%N` `dƉoAA@NGxC-%N`Ɛ dxoXX@NGxS-%N` W Px`o@@CGxS-<%N` ``o@@@NGxC->%N` `doAACGxC-%N` dx!oXX@vS-%N`! W `x`*o@@@vS-<%N`* ``Io@@@vC->%N`I `d6oAA@vC-%N`6 dxoXX@vS-%N` W px`o@@@vS-<%N` ``o@@@vC->%N` `dȢoAA@vC-%N`ȩ dxԹoXX@vS-%N` W x`ݹo@@@vS-<%N` ``o@@@vC->%N` `dloAA@vC-%N`l dxoXX@vS-%N` W x`o@@@vS-<%N` ``o@@@vC->%N` `doAA@vC-%N` dxoXX@vS-%N` W x`o@@@vS-<%N` ``yo@@@vC->%N`y `d]oAA@vC-%N`] dxoXX@vS-%N` W!x`o@@@vS-<%N` ``)o@@@vC->%N`) `duoAA@vC-%N`u dxoXX@vS-%N` W!x`o@@@vS-<%N` ``-o@@@vC->%N`- `dqoAA@vC-%N`q dxoXX@vS-%N` W! x`o@@@vS-<%N` ``*o@@@vC->%N`* `doAA@vC-%N` dxcoXX@vS-%N`c W!0x`ko@@@vS-<%N`k ``o@@@vC->%N` `doAA@vC-%N` dxoXX@vS-%N` W!@x`o@@@vS-<%N` ``(o@@@vC->%N`( `dkoAA@vC-%N`k dxoXX@vS-%N` W!Px`o@@@vS-<%N` ``o@@@vC->%N` `doAA@vC-%N` dx_oXX@vS-%N`_ W!`x`ho@@@vS-<%N`h ``o@@@vC->%N` `d oAA@vC-%N` dx.oXX@vS-%N`. W!p!x`<o@@@vS-<%N`< ``|o@@@vC->%N`| `doAA@vC-%N` dxoXX@vS-%N` W!1x`o@@@vS-<%N` ``.o@@@vC->%N`.! `dBoAA@vC-%N`B" dx"oXX@vS-%N`) W!Ax`"o@@@vS-<%N`) ``"o@@@vC->%N`) `d#oAA@vC-%N`* dxp+oXX@vS-%N`p2 W!Qx`~+o@@@vS-<%N`~2 ``+o@@@vC->%N`2 `d,oAA@vC-%N`3 dx3oXX@vS-%N`: W!ax`3o@@@vS-<%N`: ``'4o@@@vC->%N`'; `d5oAA@vC-%N`< dxl%N`C `d=oAA@vC-%N`D dxDoXX@vS-%N`K W!x` Eo@@@vS-<%N` L ``REo@@@vC->%N`RL `d_FoAA@vC-%N`_M dxMoXX@vS-%N`T W!x`Mo@@@vS-<%N`T ``Mo@@@vC->%N`T `dNoAA@vC-%N`U dx3VoXX@vS-%N`3] W!x`BVo@@@vS-<%N`B] ``Vo@@@vC->%N`] `dWoAA@vC-%N`^ dx^oXX@vS-%N`e W"x`^o@@@vS-<%N`e ``T_o@@@vC->%N`Tf `dn`oAA@vC-%N`ng dxsoXX@vS-%N`z W"x`so@@@vS-<%N`z ``9to@@@vC->%N`9{ `d/uoAA@vC-%N`/| dx|oXX@vS-%N` W" x`|o@@@vS-<%N` ``|o@@@vC->%N` `d}oAA@vC-%N` dxXoXX@vS-%N`X W"0x`fo@@@vS-<%N`f ``o@@@vC->%N` `doAA@vC-%N` dx'oXX@vS-%N`' W"@x`;o@@@vS-<%N`; ``o@@@vC->%N` `doAA@vC-%N` dxĖoXX@vS-%N`ĝ W"Px`іo@@@vS-<%N`ѝ ``o@@@vC->%N` `d(oAA@vC-%N`( dxooXX@vS-%N`o W"`x`~o@@@vS-<%N`~ ``o@@@vC->%N` `doAA@vC-%N` dx,oXX@vS-%N`, W"p"x`;o@@@vS-<%N`; ``ao@@@vC->%N`a `duoAA@vC-%N`u dxoXX@vS-%N` W"2x`İo@@@vS-<%N`ķ ``o@@@vC->%N` `d"oAA@vC-%N`" dxqoXX@vS-%N`q W"Bx`o@@@vS-<%N` ``͹o@@@vC->%N` `dغoAA@vC-%N` dxoXX@vS-%N` W"Rx`o@@@vS-<%N` ``Qo@@@vC->%N`Q `dNoAA@vC-%N`N dxoXX@vS-%N` W"bx`o@@@vS-<%N` ``o@@@vC->%N` `doAA@vC-%N` dxqoXX@vS-%N`q W"rx`o@@@vS-<%N` ``o@@@vC->%N` `doAA@vC-%N` dxSoXX@vS-%N`S W"x`eo@@@vS-<%N`e ``o@@@vC->%N` `doAA@vC-%N` dxFoXX@vS-%N`F W"x`Uo@@@vS-<%N`U ``o@@@vC->%N` `doAA@vC-%N` dxoXX@vS-%N` W"x`o@@@vS-<%N` ``+o@@@vC->%N`+ `d>oAA@vC-%N`> dxoXX@vS-%N` W#x`o@@@vS-<%N` ``o@@@vC->%N` `doAA@vC-%N` dxoXX@vS-%N` W#x`+o@@@vS-<%N`+ ``Oo@@@vC->%N`O `d|oAA@vC-%N`| dxoXX@vS-%N` W# x`o@@@vS-<%N` ``/o@@@vC->%N`/ `dCoAA@vC-%N`C dxoXX@vS-%N` W#0x`o@@@vS-<%N` ``o@@@vC->%N` `d oAA@vC-%N`  dxmoXX@vS-%N`m W#@x`zo@@@vS-<%N`z ``o@@@vC->%N` `doAA@vC-%N` dx0!oXX@vS-%N`0( W#Px`%N`~( `dz"oAA@vC-%N`z) dx)oXX@vS-%N`0 W#`x`)o@@@vS-<%N`0 ``)o@@@vC->%N`0 `d+oAA@vC-%N`2 dxB2oXX@vS-%N`B9 W#p#x`O2o@@@vS-<%N`O9 ``2o@@@vC->%N`9 `d3oAA@vC-%N`: dx:oXX@vS-%N`A W#3x`:o@@@vS-<%N`A ``;o@@@vC->%N`B `d%N`J `dDoAA@vC-%N`K dx7LoXX@vS-%N`7S W#Sx`QLo@@@vS-<%N`QS ``Lo@@@vC->%N`S `dMoAA@vC-%N`T dxToXX@vS-%N`[ W#cx` Uo@@@vS-<%N` \ ``MUo@@@vC->%N`M\ `dqVoAA@vC-%N`q] dx]oXX@vS-%N`d W#sx`]o@@@vS-<%N`d ``]o@@@vC->%N`d `d^oAA@vC-%N`e dxSfoXX@vS-%N`Sm W#x`afo@@@vS-<%N`am ``fo@@@vC->%N`m `dgoAA@vC-%N`n dxnoXX@vS-%N`u W#x`no@@@vS-<%N`u ``0oo@@@vC->%N`0v `d0poAA@vC-%N`0w dxIwoXX@vS-%N`I~ W#x`Two@@@vS-<%N`T~ ``wo@@@vC->%N`~ `dyxoAA@vC-%N`y dxoXX@vS-%N`ن W$x`o@@@vS-<%N` ``'o@@@vC->%N`' `d7oAA@vC-%N`7 dxvoXX@vS-%N`v W$x`o@@@vS-<%N` ``܈o@@@vC->%N`܏ `doAA@vC-%N` dx7oXX@vS-%N`7 W$ x`Co@@@vS-<%N`C ``o@@@vC->%N` `doAA@vC-%N` dxٙoXX@vS-%N`٠ W$0x`o@@@vS-<%N` ``o@@@vC->%N` `doAA@vC-%N` dxoXX@vS-%N` W$@x`o@@@vS-<%N` `` o@@@vC->%N` `d>oAA@vC-%N`> dxoXX@vS-%N` W$Px`o@@@vS-<%N` ``o@@@vC->%N` `d:oAA@vC-%N`: dxoXX@vS-%N` W$`x`o@@@vS-<%N` ``Ѵo@@@vC->%N`ѻ `doAA@vC-%N` dxLoXX@vS-%N`L W$p$x`Xo@@@vS-<%N`X ``o@@@vC->%N` `doAA@vC-%N` dxoXX@vS-%N` W$4x`o@@@vS-<%N` ``"o@@@vC->%N`" `dKoAA@vC-%N`K dxoXX@vS-%N` W$Dx`o@@@vS-<%N` ``o@@@vC->%N` `doAA@vC-%N` dxoXX@vS-%N` W$Tx`o@@@vS-<%N` ``!o@@@vC->%N`! `dOoAA@vC-%N`O dxoXX@vS-%N` W$dx`o@@@vS-<%N` ``o@@@vC->%N` `doAA@vC-%N` dxvoXX@vS-%N`v W$tx`o@@@vS-<%N` ``o@@@vC->%N` `doAA@vC-%N` dx0oXX@vS-%N`0 W$x`%N`~ `doAA@vC-%N` dxoXX@vS-%N` W$x`o@@@vS-<%N` ``Eo@@@vC->%N`E `dZoAA@vC-%N`Z dxoXX@vS-%N` W$x`o@@@vS-<%N` ``o@@@vC->%N` `d oAA@vC-%N` dx] oXX@vS-%N`] W%x`i o@@@vS-<%N`i `` o@@@vC->%N` `d oAA@vC-%N` dxoXX@vS-%N` W%x`o@@@vS-<%N` ``[o@@@vC->%N`[ `dooAA@vC-%N`o dxoXX@vS-%N`# W% x`o@@@vS-<%N`# `` o@@@vC->%N` $ `d"oAA@vC-%N`"% dx=%oXX@vS-%N`=, W%0x`I%o@@@vS-<%N`I, ``l%o@@@vC->%N`l, `d&oAA@vC-%N`- dx-oXX@vS-%N`4 W%@x` .o@@@vS-<%N` 5 ``H.o@@@vC->%N`H5 `dL/oAA@vC-%N`L6 dx6oXX@vS-%N`= W%Px`6o@@@vS-<%N`= `` 7o@@@vC->%N` > `d8oAA@vC-%N`? dxz?oXX@vS-%N`zF W%`x`?o@@@vS-<%N`F ``?o@@@vC->%N`F `d@oAA@vC-%N`G dxCHoXX@vS-%N`CO W%p%x`YHo@@@vS-<%N`YO ``Ho@@@vC->%N`O `dIoAA@vC-%N`P dxPoXX@vS-%N`W W%5x`Po@@@vS-<%N`W ``Po@@@vC->%N`W `dQoAA@vC-%N`X dx+YoXX@vS-%N`+` W%Ex`9Yo@@@vS-<%N`9` ``ZYo@@@vC->%N`Z` `doZoAA@vC-%N`oa dxaoXXO}zS-%N`h W%Ux`ao@@M}zS-<%N`h ``ubo@@O}zC->%N`ui `dYcoAAM}zC-%N`Yj dxjoXXM}zS-%N`q W%ex`jo@@O}zS-<%N`q ``jo@@M}zC->%N`q `d,loAAO}zC-%N`,s dxIsoXXO}zS-%N`Iz W%ux`Vso@@M}zS-<%N`Vz ``so@@O}zC->%N`z `dtoAAM}zC-%N`{ dx |oXXM}zS-%N` W%x`|o@@O}zS-<%N` ``A|o@@M}zC->%N`A `dq}oAAO}zC-%N`q dxoXX@GxS-%N` W%x`o@@CGxS-<%N` ``o@@@GxC->%N` `d?oAACGxC-%N`? dxoXXCGxS-%N` W%x`o@@@GxS-<%N` ``эo@@CGxC->%N`є `d׎oAA@GxC-%N`ו dxʕoXX@GxS-%N`ʜ W&x`וo@@CGxS-<%N`ל ``o@@@GxC->%N` `doAACGxC-%N` dxuoXXO}zS-%N`u W&x`o@@M}zS-<%N` ``ƞo@@O}zC->%N`ƥ `dџoAAM}zC-%N`Ѧ dxoXXM}zS-%N` W& x`o@@O}zS-<%N` ``2o@@M}zC->%N`2 `d+oAAO}zC-%N`+ dxoXXO}zS-%N` W&0x`o@@M}zS-<%N` ``o@@O}zC->%N` `doAAM}zC-%N` dxOoXXM}zS-%N`O W&@x`\o@@O}zS-<%N`\ ``o@@M}zC->%N` `doAAO}zC-%N` dxoXXO}zS-%N` W&Px`o@@M}zS-<%N` ``Oo@@O}zC->%N`O `dQoAAM}zC-%N`Q dxoXXM}zS-%N` W&`x`o@@O}zS-<%N` ``o@@M}zC->%N` `doAAO}zC-%N` dxFoXXO}zS-%N`F W&p&x`Ro@@M}zS-<%N`R ``o@@O}zC->%N` `doAAM}zC-%N` dxoXXM}zS-%N` W&6x`o@@O}zS-<%N` ``o@@M}zC->%N` `doAAO}zC-%N` dxMoXXO}zS-%N`M W&Fx`Zo@@M}zS-<%N`Z ``o@@O}zC->%N` `doAAM}zC-%N` dxoXXM}zS-%N` W&Vx`o@@O}zS-<%N` ``Vo@@M}zC->%N`V `dgoAAO}zC-%N`g dx5oXXO}zS-%N`5 W&fx`Ao@@M}zS-<%N`A ``o@@O}zC->%N` `doAAM}zC-%N` dxoXXM}zS-%N` W&vx`o@@O}zS-<%N` ``1o@@M}zC->%N`1 `d*oAAO}zC-%N`* dxsoXXO}zS-%N`s W&x`o@@M}zS-<%N` ``o@@O}zC->%N` `doAAM}zC-%N` dx8oXXM}zS-%N`8 W&x`Io@@O}zS-<%N`I ``o@@M}zC->%N` `doAAO}zC-%N` dxoXXO}zS-%N` W&x`o@@M}zS-<%N` ``Io@@O}zC->%N`I `d<oAAM}zC-%N`< dxuoXXM}zS-%N`u& W'x`o@@O}zS-<%N`& ``o@@M}zC->%N`& `d oAAO}zC-%N`' dx2(oXXO}zS-%N`2/ W'x`>(o@@M}zS-<%N`>/ ``(o@@O}zC->%N`/ `d)oAAM}zC-%N`0 dx0oXXM}zS-%N`7 W' x`1o@@O}zS-<%N`8 ``E1o@@M}zC->%N`E8 `d2oAAO}zC-%N`9 dxd9oXXO}zS-%N`d@ W'0x`p9o@@M}zS-<%N`p@ ``9o@@O}zC->%N`@ `d:oAAM}zC-%N`A dxBoXXM}zS-%N`I W'@x` Bo@@O}zS-<%N` I ``cBo@@M}zC->%N`cI `dvCoAAO}zC-%N`vJ dxJoXXO}zS-%N`Q W'Px`Jo@@M}zS-<%N`Q ``Ko@@O}zC->%N`R `dLoAAM}zC-%N`S dxOSoXXM}zS-%N`OZ W'`x`ZSo@@O}zS-<%N`ZZ ``So@@M}zC->%N`Z `dToAAO}zC-%N`[ dx \oXXO}zS-%N` c W'p'x`\o@@M}zS-<%N`c ``Y\o@@O}zC->%N`Yc `dZ]oAAM}zC-%N`Zd dxdoXXM}zS-%N`k W'7x`do@@O}zS-<%N`k ``do@@M}zC->%N`k `dfoAAO}zC-%N`m dxmoXXO}zS-%N`t W'Gx`"mo@@M}zS-<%N`"t ``dmo@@O}zC->%N`dt `d_noAAM}zC-%N`_u dxuoXXM}zS-%N`| W'Wx`uo@@O}zS-<%N`| ``vo@@M}zC->%N`} `dwoAAO}zC-%N`~ dxJ~oXXO}zS-%N`J W'gx`U~o@@M}zS-<%N`U ``~o@@O}zC->%N` `doAAM}zC-%N` dxoXXM}zS-%N` W'wx`o@@O}zS-<%N` ``Co@@M}zC->%N`C `d:oAAO}zC-%N`: dxoXXO}zS-%N` W'x`o@@M}zS-<%N` ``o@@O}zC->%N` `doAAM}zC-%N` dxQoXXM}zS-%N`Q W'x`\o@@O}zS-<%N`\ ``o@@M}zC->%N` `doAAO}zC-%N` dxՠoXXO}zS-%N`է W'x`o@@M}zS-<%N` ``!o@@O}zC->%N`! `d_oAAM}zC-%N`_ dxoXXM}zS-%N` W(x`o@@O}zS-<%N` ``@o@@M}zC->%N`@ `dFoAAO}zC-%N`F dxFoXXO}zS-%N`F W(x`Ro@@M}zS-<%N`R ``o@@O}zC->%N` `doAAM}zC-%N` dxoXXM}zS-%N` W( x`o@@O}zS-<%N` ``o@@M}zC->%N` `doAAO}zC-%N` dxoXXO}zS-%N` W(0x`o@@M}zS-<%N` ``o@@O}zC->%N` `doAAM}zC-%N` dx@oXXM}zS-%N`@ W(@x`Mo@@O}zS-<%N`M ``o@@M}zC->%N` `doAAO}zC-%N` dxoXXO}zS-%N` W(Px`o@@M}zS-<%N` ``[o@@O}zC->%N`[ `dpoAAM}zC-%N`p dxoXXM}zS-%N` W(`x`o@@O}zS-<%N` ``o@@M}zC->%N` `doAAO}zC-%N` dxDoXXO}zS-%N`DW(p(x`Oo@@M}zS-<%N`O``o@@O}zC->%N``doAAM}zC-%N`dxoXXM}zS-%N`W(8x`o@@O}zS-<%N```Co@@M}zC->%N`C`do oAAO}zC-%N`odxoXXO}zS-%N`W(Hx`o@@M}zS-<%N```o@@O}zC->%N``d oAAM}zC-%N` dx&oXXM}zS-%N`& W(Xx`1o@@O}zS-<%N`1 ``po@@M}zC->%N`p `dooAAO}zC-%N`o!dx!oXXO}zS-%N`(W(hx`!o@@M}zS-<%N`(``-"o@@O}zC->%N`-)`dE#oAAM}zC-%N`E*dx*oXXM}zS-%N`1W(xx`*o@@O}zS-<%N`1``*o@@M}zC->%N`1`d+oAAO}zC-%N`2dx^3oXXO}zS-%N`^:W(x`j3o@@M}zS-<%N`j:``3o@@O}zC->%N`:`d4oAAM}zC-%N`;dx"%N`yC`dw=oAAO}zC-%N`wDdxDoXXO}zS-%N`KW(x`Do@@M}zS-<%N`K``'Eo@@O}zC->%N`'L`dDFoAAM}zC-%N`DMdxMoXXM}zS-%N`TW)x`Mo@@O}zS-<%N`T``Mo@@M}zC->%N`T`dNoAAO}zC-%N`UdxNVoXXO}zS-%N`N]W)x`YVo@@M}zS-<%N`Y]``Vo@@O}zC->%N`]`dWoAAM}zC-%N`^dx_oXXM}zS-%N`fW) x`_o@@O}zS-<%N`f``j_o@@M}zC->%N`jf`dh`oAAO}zC-%N`hgdxgoXXO}zS-%N`nW)0x`go@@M}zS-<%N`n``go@@O}zC->%N`n`dhoAAM}zC-%N`odxOpoXXM}zS-%N`OwW)@x`[po@@O}zS-<%N`[w``po@@M}zC->%N`w`dqoAAO}zC-%N`xdxxoXXCGxS-%N`W)P x`xo@@@GxS-<%N```xo@@CGxC->%N``dyoAA@GxC-%N`dxvoXX@GxS-%N`vW)`x`o@@CGxS-<%N```Ӂo@@@GxC->%N`ӈ`dւoAACGxC-%N`։dxoXXCGxS-%N`W)p)x`o@@@GxS-<%N```o@@CGxC->%N``doAA@GxC-%N`dxZoXX@GxS-%N`ZW)9x`fo@@CGxS-<%N`f``o@@@GxC->%N``doAACGxC-%N`dxoXXCGxS-%N`W)Ix`o@@@GxS-<%N```yo@@CGxC->%N`y`dUoAA@GxC-%N`Udx}oXX@GxS-%N`}W)Yx`o@@CGxS-<%N```o@@@GxC->%N``doAACGxC-%N`dx?oXXCGxS-%N`?W)ix`Mo@@@GxS-<%N`M``to@@CGxC->%N`t`doAA@GxC-%N`dxoXX@GxS-%N`W)yx`ʴo@@CGxS-<%N`ʻ``o@@@GxC->%N``doAACGxC-%N`dxWoXXCGxS-%N`WW)x`eo@@@GxS-<%N`e``o@@CGxC->%N``doAA@GxC-%N`dx)oXXO}zS-%N`)W)x`;o@@M}zS-<%N`;``}o@@O}zC->%N`}`dmoAAM}zC-%N`mdxoXXMwS-%N`W)x`o@@MwS-<%N```o@@MwC->%N``d$oAAMwC-%N`$dxoXXMwS-%N`W*x`o@@MwS-<%N```o@@MwC->%N``d&oAAMwC-%N`&dxToXX@MwS-%N`TW*x`do@@@MwS-<%N`d``o@@@MwC->%N``doAA@MwC-%N`dxoXXMwS-%N`W* x`o@@MwS-<%N```*o@@MwC->%N`*`d%N``doAAMwC-%N`dxZoXXMwS-%N`ZW*@x`lo@@@MwS-<%N`l``o@@MwC->%N``doAA@MwC-%N`dx oXXMwS-%N` W*P x`o@@MwS-<%N` ``o@@MwC->%N` `dqoAAMwC-%N`q dx oXX@MwS-%N` W*`x`0o@@@MwS-<%N`0``xo@@@MwC->%N`x`dloAA@MwC-%N`ldxoXXMwS-%N`&W*p*x`o@@MwS-<%N`&``< o@@MwC->%N`<'`d2!oAAMwC-%N`2(dxb(oXX@MwS-%N`b/W*:x`m(o@@@MwS-<%N`m/``(o@@@MwC->%N`/`d)oAA@MwC-%N`0dx0oXXMwS-%N`7W*Jx`0o@@MwS-<%N`7``1o@@MwC->%N`8`d2oAAMwC-%N`9dx@9oXX@MwS-%N`@@W*Zx`L9o@@MwS-<%N`L@``9o@@@MwC->%N`@`d:oAAMwC-%N`AdxAoXXMwS-%N`NW*jx`Ao@@@MwS-<%N`Y``Ao@@MwC->%N``dCoAA@MwC-%N`dxIoXXMwS-%N`XW*zx`Io@@MwS-<%N`a``Io@@MwC->%N``dJoAAMwC-%N`dxQoXX@MwS-%N`W*x`Qo@@@MwS-<%N```;Ro@@@MwC->%N``d?SoAA@MwC-%N`dxZoXXMwS-%N`@W*x`Zo@@MwS-<%N`Q``Zo@@MwC->%N``d[oAAMwC-%N` dxboXXMwS-%N`'W*x`co@@MwS-<%N`'``Kco@@MwC->%N` (`d\doAAMwC-%N`)dxkoXX@MwS-%N`P0W+x`ko@@@MwS-<%N`\0``ko@@@MwC->%N`}0`dloAA@MwC-%N`1dx2toXXMwS-%N`8W+x`%N`G9`d}uoAAMwC-%N`=:dx|oXX@MwS-%N`uAW+ x`|o@@MwS-<%N`~A``}o@@@MwC->%N`A`d)~oAAMwC-%N`Bdx$oXXMwS-%N`VW+0x`/o@@@MwS-<%N`V``xo@@MwC->%N`8W`doAA@MwC-%N`EXdxoXXMwS-%N`q_W+@x`o@@MwS-<%N`}_``o@@MwC->%N`_`d/oAAMwC-%N``dxXoXX@MwS-%N`hW+P x`co@@@MwS-<%N`#h``o@@@MwC->%N`uh`doAA@MwC-%N`idxoXXMwS-%N`pW+`x`o@@MwS-<%N`p``Mo@@MwC->%N` q`d>oAAMwC-%N`qdxnoXXMwS-%N`.yW+p+x`zo@@MwS-<%N`:y``ôo@@MwC->%N`y`dյoAAMwC-%N`zdx oXX@MwS-%N`ʁW+;x`o@@@MwS-<%N`ׁ``^o@@@MwC->%N``daoAA@MwC-%N`!dxoXXMwS-%N`jW+Kx`o@@MwS-<%N`w``o@@MwC->%N``d oAAMwC-%N`̋dx+oXX@MwS-%N`W+[x`6o@@MwS-<%N```o@@@MwC->%N`b`doAAMwC-%N`QdxoXXMwS-%N`W+kx`o@@@MwS-<%N```o@@MwC->%N`ߛ`doAA@MwC-%N`ܜdx]oXXMwS-%N`W+{x`ho@@MwS-<%N`(``o@@MwC->%N`p`doAAMwC-%N`ddx oXX@MwS-%N`ʬW+x`o@@@MwS-<%N`լ``[o@@@MwC->%N``dQoAA@MwC-%N`dxoXXMwS-%N`fW+x`o@@MwS-<%N`x``o@@MwC->%N``doAAMwC-%N`϶dxoXXMwS-%N`ԽW+x`o@@MwS-<%N`޽``o@@MwC->%N`U`doAAMwC-%N`Udx~oXX@MwS-%N`>W,x`o@@@MwS-<%N`G``o@@@MwC->%N``doAA@MwC-%N`qdx oXXMwS-%N`W,x` o@@MwS-<%N```R o@@MwC->%N``d[ oAAMwC-%N`dxboXX@MwS-%N`"W, x`no@@MwS-<%N`.``o@@@MwC->%N`x`doAAMwC-%N`}dx$oXXMwS-%N`W,0x`2o@@@MwS-<%N```{o@@MwC->%N`;`doAA@MwC-%N`mdx#oXXMwS-%N`W,@x`#o@@MwS-<%N```-$o@@MwC->%N``d+%oAAMwC-%N`dxc,oXX@MwS-%N`#W,P x`q,o@@@MwS-<%N`1``,o@@@MwC->%N`p`d-oAA@MwC-%N`ldx5oXXMwS-%N`W,`x`&5o@@MwS-<%N```m5o@@MwC->%N`-`dy6oAAMwC-%N`9dxtKoXXMwS-%N`4W,p,x`Ko@@MwS-<%N`B``Ko@@MwC->%N``dLoAAMwC-%N`ydx,ToXX@MwS-%N`W,<x`;To@@@MwS-<%N```To@@@MwC->%N`A`dUoAA@MwC-%N`Sdx\oXXMwS-%N`!W,Lx`\o@@MwS-<%N`!``F]o@@MwC->%N`"`d#^oAAMwC-%N`"dxeoXX@MwS-%N`]*W,\x`eo@@MwS-<%N`n*``eo@@@MwC->%N`*`dfoAAMwC-%N`+dxUnoXXMwS-%N`3W,lx`gno@@@MwS-<%N`'3``no@@MwC->%N`3`dooAA@MwC-%N`y4dxwoXXMwS-%N`;W,|x`,wo@@MwS-<%N`;``pwo@@MwC->%N`0<`d}xoAAMwC-%N`==dxoXX@MwS-%N`DW,x`o@@@MwS-<%N`D``;o@@@MwC->%N`D`dBoAA@MwC-%N`FdxoXXMwS-%N`aMW,x`o@@MwS-<%N`qM``o@@MwC->%N`M`d oAAMwC-%N`Ndx͑oXXMwS-%N`VW,x`ݑo@@MwS-<%N`V``o@@MwC->%N`V`doAAMwC-%N`WdxQoXX@MwS-%N`_W-x`ao@@@MwS-<%N`!_``o@@@MwC->%N`i_`deoAA@MwC-%N`%`dxoXXMwS-%N`gW-x`"o@@MwS-<%N`g``ko@@MwC->%N`+h`dvoAAMwC-%N`6idxҫoXX@MwS-%N`pW- x`o@@MwS-<%N`p``(o@@@MwC->%N`p`d7oAAMwC-%N`qdxoXXMwS-%N`SyW-0x`o@@@MwS-<%N`cy``o@@MwC->%N`y`d oAA@MwC-%N`zdxJoXX@8yS-%N` W-@x`Xo@@@8yS-<%N```o@@@8yC->%N`Z`doAA@8yC-%N`YdxoXX@8yS-%N`ՊW-P x`'o@@8yS-<%N```ho@@@8yC->%N`(`d|oAA8yC-%N`<dxoXX8yS-%N`W-`x`o@@8yS-<%N```o@@8yC->%N`ԓ`doAA8yC-%N`ՔdxaoXX8yS-%N`!W-p-x`no@@8yS-<%N`.``o@@8yC->%N`O`doAA8yC-%N`dxoXX8yS-%N`٤W-=x`%o@@@8yS-<%N```fo@@8yC->%N`&`doAA@8yC-%N`CdxoXX@8yS-%N`W-Mx`o@@@8yS-<%N``` o@@@8yC->%N``doAA@8yC-%N`خdxoXX8yS-%N`W-]x`o@@8yS-<%N```7o@@8yC->%N``doAA8yC-%N`vdxoXX8yS-%N`W-mx`o@@8yS-<%N``` o@@8yC->%N``d+oAA8yC-%N`dxoXX8yS-%N`XW-}x`o@@8yS-<%N`e``o@@8yC->%N``doAA8yC-%N`dxM oXX8yS-%N` W-x`Z o@@8yS-<%N``` o@@8yC->%N`d`d oAA8yC-%N`qdx2oXX@8yS-%N`W-x`?o@@@8yS-<%N````o@@@8yC->%N` `doAA@8yC-%N`hdxoXX@8yS-%N`[W-x`o@@8yS-<%N`f``o@@@8yC->%N``doAA8yC-%N`dx &oXX8yS-%N`W.x`&o@@8yS-<%N```\&o@@8yC->%N``d-'oAA8yC-%N`dx.oXX8yS-%N`W.x`.o@@8yS-<%N```.o@@8yC->%N``d/oAA8yC-%N`dxv7oXX8yS-%N`6W. x`7o@@@8yS-<%N`D``7o@@8yC->%N```d8oAA@8yC-%N`Gdx'@oXX@8yS-%N`W.0x`5@o@@@8yS-<%N```R@o@@@8yC->%N``drAoAA@8yC-%N`2dxHoXX8yS-%N` W.@x`Ho@@8yS-<%N` `` Io@@8yC->%N` `dJoAA8yC-%N`dxQoXX8yS-%N`[W.Px`Qo@@8yS-<%N`i``Qo@@8yC->%N``dRoAA8yC-%N`ndx ZoXX8yS-%N`W.`x`.Zo@@8yS-<%N```LZo@@8yC->%N` `dK[oAA8yC-%N` dxboXX8yS-%N`'W.p.x`bo@@8yS-<%N`'``co@@8yC->%N`'`dcoAA8yC-%N`(dxykoXX@8yS-%N`90W.>x`ko@@@8yS-<%N`G0``lo@@@8yC->%N`0`dmoAA@8yC-%N`1dxsoXX@8yS-%N`8W.Nx`so@@@8yS-<%N`8``so@@@8yC->%N`8`duoAA@8yC-%N`9dx{oXX@8yS-%N`@W.^x`{o@@@8yS-<%N`@``|o@@@8yC->%N`@`d}oAA@8yC-%N`AdxeoXX@8yS-%N`%IW.nx`o@@@8yS-<%N`?I``o@@@8yC->%N`YI`doAA@8yC-%N`fJdxoXX@8yS-%N`QW.~x`o@@@8yS-<%N`Q``eo@@@8yC->%N`%R`dOoAA@8yC-%N`SdxoXX@8yS-%N`mZW.x`o@@@8yS-<%N`zZ``o@@@8yC->%N`Z`doAA@8yC-%N`[dx|oXX@8yS-%N`%N`c`doAA@8yC-%N`ddx0oXX@8yS-%N`kW.x`=o@@@8yS-<%N`k``~o@@@8yC->%N`>l`doAA@8yC-%N`QmdxoXX@8yS-%N`tW/x`o@@@8yS-<%N`t``;o@@@8yC->%N`t`d+oAA@8yC-%N`udxloXX@8yS-%N`,}W/x`yo@@8yS-<%N`9}``o@@@8yC->%N`z}`doAA8yC-%N`q~dxoXX8yS-%N`W/ x`o@@8yS-<%N`ƅ``Go@@8yC->%N``doAA8yC-%N`SdxoXX8yS-%N`xW/0x`o@@8yS-<%N```o@@8yC->%N`Ȏ`doAA8yC-%N`ӏdx%N`M`doAA@8yC-%N`MdxoXX@8yS-%N`ٟW/Px`'o@@@8yS-<%N```po@@@8yC->%N`0`droAA@8yC-%N`2dxoXX8yS-%N`W/`x`o@@8yS-<%N```)o@@8yC->%N``d$oAA8yC-%N`dxoXX8yS-%N`XW/p/x`o@@8yS-<%N`e``o@@8yC->%N``d oAA8yC-%N`ɲdxoXX8yS-%N`SW/?x`o@@8yS-<%N````o@@8yC->%N``doAA8yC-%N`ƺdxWoXX8yS-%N`W/Ox`do@@8yS-<%N`$``o@@8yC->%N`c`doAA8yC-%N`tdxoXX@8yS-%N`W/_x`o@@@8yS-<%N```^o@@@8yC->%N``d\oAA@8yC-%N`dxoXX@8yS-%N`W/ox`o@@8yS-<%N```(o@@@8yC->%N``d4oAA8yC-%N`dxoXX8yS-%N`_W/x`o@@8yS-<%N`m``o@@8yC->%N``doAA8yC-%N`dx8 oXX8yS-%N`W/x`G o@@8yS-<%N``` o@@8yC->%N`J`d!oAA8yC-%N`Odx(oXX8yS-%N`W/x` )o@@@8yS-<%N```M)o@@8yC->%N` `dM*oAA@8yC-%N` dx1oXX@8yS-%N`}W/x`1o@@@8yS-<%N```2o@@@8yC->%N``d#NoAA@8yC-%N`dxSoXX8yS-%N`nW0x`So@@8yS-<%N`|``So@@8yC->%N``dToAA8yC-%N`dxy\oXX8yS-%N`9!W0x`\o@@8yS-<%N`H!``\o@@8yC->%N`f!`d]oAA8yC-%N`R"dx+eoXX8yS-%N`)W0 x`:eo@@8yS-<%N`)``Veo@@8yC->%N`*`dlfoAA8yC-%N`,+dxmoXX8yS-%N`2W00x`mo@@8yS-<%N`2``no@@8yC->%N`2`d"ooAA8yC-%N`3dxvoXX@8yS-%N`J;W0@x`vo@@@8yS-<%N`Z;``vo@@@8yC->%N`x;`dwoAA@8yC-%N`y<dx>oXX@GxS-%N`CW0Px`Po@@CGxS-<%N`D``wo@@@GxC->%N`7D`doAACGxC-%N`XEdxoXXCGxS-%N`LW0` x`o@@@NGxS-<%N`L``2o@@CGxC->%N`L`d/oAA@NGxC-%N`MdxoXXOGxS-%N`nUW0p0x`o@@@KGxS-<%N`|U``ߐo@@OGxC->%N`U`d-oAA@KGxC-%N`VdxooXXLGxS-%N`/^W0@x`o@@@KGxS-<%N`?^``o@@LGxC->%N`d^`doAA@KGxC-%N`r_dxoXXOGxS-%N`fW0Px`o@@@NGxS-<%N`f``%o@@OGxC->%N`f`dZoAA@NGxC-%N`hdxoXXCGxS-%N`toW0`x`o@@CGxS-<%N`o``o@@CGxC->%N`o`d oAACGxC-%N`pdxxoXX@GxS-%N`8xW0px`o@@CGxS-<%N`Ix``o@@@GxC->%N`qx`doAACGxC-%N`ydx-oXXCGxS-%N`W0x`;o@@@NGxS-<%N```o@@CGxC->%N`[`doAA@NGxC-%N`RdxoXXOGxS-%N`W0x`o@@@KGxS-<%N```o@@OGxC->%N`݉`dRoAA@KGxC-%N`dxoXXLGxS-%N`nW0x`o@@@KGxS-<%N`{``o@@LGxC->%N``doAA@KGxC-%N`dxsoXXOGxS-%N`3W0x`o@@@NGxS-<%N`@``o@@OGxC->%N`c`doAA@NGxC-%N`dxBoXXCGxS-%N`W1x`Ro@@CGxS-<%N```o@@CGxC->%N`u`doAACGxC-%N`ZdxoXX@GxS-%N`W1x` o@@CGxS-<%N`ͬ``,o@@@GxC->%N``djoAACGxC-%N`*dxGoXXCGxS-%N`W1 x`Yo@@@NGxS-<%N```wo@@CGxC->%N`7`dxoAA@NGxC-%N`8dxoXXOGxS-%N`ڽW10x`)o@@@KGxS-<%N```Oo@@OGxC->%N``dloAA@KGxC-%N`,dxoXXLGxS-%N`W1@x`o@@@KGxS-<%N```o@@LGxC->%N``dDoAA@KGxC-%N`dxw oXXOGxS-%N`7W1Px` o@@@NGxS-<%N`F`` o@@OGxC->%N`m`d oAA@NGxC-%N`dxJoXXCGxS-%N` W1`!x`Yo@@CGxS-<%N```o@@CGxC->%N`@`doAACGxC-%N`WdxoXX@8yS-%N`W1p1x`o@@8yS-<%N```So@@@8yC->%N``d@oAA8yC-%N`dx$oXX8yS-%N`mW1Ax`$o@@8yS-<%N`z``$o@@8yC->%N``d &oAA8yC-%N`dx-oXX8yS-%N`AW1Qx`-o@@8yS-<%N`P``-o@@8yC->%N``d.oAA8yC-%N`dx66oXX8yS-%N`W1ax`C6o@@@8yS-<%N```6o@@8yC->%N`F`d7oAA@8yC-%N`]dx>oXX@8yS-%N`OW1qx`>o@@@8yS-<%N`]``>o@@@8yC->%N``d?oAA@8yC-%N`dxFoXX8yS-%N` W1x`Fo@@8yS-<%N` ``=Go@@8yC->%N` `dHHoAA8yC-%N` dxOoXX8yS-%N`}W1x`Oo@@8yS-<%N```Po@@8yC->%N``dKQoAA8yC-%N` dx~XoXX8yS-%N`>W1x`Xo@@8yS-<%N`K``Xo@@8yC->%N``dYoAA8yC-%N`dxAaoXX8yS-%N`&W1x`Nao@@8yS-<%N`&``ao@@8yC->%N`O&`dboAA8yC-%N`G'dxioXX@8yS-%N`.W2x`io@@@8yS-<%N`.``jo@@@8yC->%N`.`d)koAA@8yC-%N`/dxroXX@8yS-%N`E7W2x`ro@@8yS-<%N`Q7``ro@@@8yC->%N`7`d%toAA8yC-%N`8dxzoXX8yS-%N`?W2 x`zo@@8yS-<%N`?``>{o@@8yC->%N`?`dw|oAA8yC-%N`7AdxzoXX8yS-%N`:HW20x`o@@8yS-<%N`DH``΃o@@8yC->%N`H`doAA8yC-%N`IdxoXX8yS-%N`PW2@x`o@@@8yS-<%N`P``,o@@8yC->%N`P`dFoAA@8yC-%N`RdxoXX@8yS-%N`VYW2Px`o@@@8yS-<%N`aY``o@@@8yC->%N`Y`doAA@8yC-%N`Zdx9oXX8yS-%N`aW2`"x`Go@@8yS-<%N`b``o@@8yC->%N`Nb`d|oAA8yC-%N`%N`j`dloAA8yC-%N`,ldxoXX8yS-%N`QsW2Bx`o@@8yS-<%N`]s``o@@8yC->%N`s`doAA8yC-%N`tdx/oXX8yS-%N`{W2Rx`:o@@8yS-<%N`{``~o@@8yC->%N`>|`doAA8yC-%N`U}dxoXX@8yS-%N`W2bx`o@@@8yS-<%N```8o@@@8yC->%N``d1oAA@8yC-%N`dxoXX@8yS-%N`cW2rx`o@@8yS-<%N`n``o@@@8yC->%N``doAA8yC-%N`׎dxboXX8yS-%N`"W2x`oo@@8yS-<%N`/``o@@8yC->%N`r`doAA8yC-%N`pdxoXX8yS-%N`W2x`o@@8yS-<%N```+o@@8yC->%N``d$oAA8yC-%N`dxoXX8yS-%N`_W2x`o@@@8yS-<%N`k``o@@8yC->%N``doAA@8yC-%N`dx,oXX@8yS-%N`W2x`Jo@@@8yS-<%N` ``wo@@@8yC->%N`7`doAA@8yC-%N`WdxoXX8yS-%N`W3x`o@@8yS-<%N```Eo@@8yC->%N``d?oAA8yC-%N`dx oXX8yS-%N`qW3x` o@@8yS-<%N`}`` o@@8yC->%N``d oAA8yC-%N`dx6oXX8yS-%N`W3 x`Bo@@8yS-<%N```o@@8yC->%N`D`dxoAA8yC-%N`8dxoXX8yS-%N`W30x`o@@8yS-<%N```Ho@@8yC->%N``dGoAA8yC-%N`dx%oXX@8yS-%N`rW3@x`%o@@@8yS-<%N```%o@@@8yC->%N``d'oAA@8yC-%N`dxi.oXX@8yS-%N`)W3Px`u.o@@8yS-<%N`5``.o@@@8yC->%N`v`d/oAA8yC-%N`dx 7oXX8yS-%N`W3`#x`+7o@@8yS-<%N```7o@@8yC->%N`C`d\8oAA8yC-%N`dx?oXX8yS-%N`W3p3x`?o@@8yS-<%N```+@o@@8yC->%N``d"AoAA8yC-%N`dxHoXX8yS-%N`q W3Cx`Ho@@@8yS-<%N`} ``Ho@@8yC->%N` `dIoAA@8yC-%N`dxfQoXX@8yS-%N`&W3Sx`qQo@@@8yS-<%N`1``Qo@@@8yC->%N`r`dRoAA@8yC-%N`dxZoXX8yS-%N`W3cx`+Zo@@8yS-<%N```jZo@@8yC->%N`*`dm[oAA8yC-%N`- dxboXX8yS-%N``'W3sx`bo@@8yS-<%N`k'``bo@@8yC->%N`'`ddoAA8yC-%N`(dxlkoXX8yS-%N`,0W3x`|ko@@8yS-<%N`<0``ko@@8yC->%N`[0`dloAA8yC-%N`|1dx!toXX8yS-%N`8W3x`,to@@8yS-<%N`8``Tto@@8yC->%N`9`dhuoAA8yC-%N`(:dx|oXX@8yS-%N`AW3x`|o@@@8yS-<%N`A``}o@@@8yC->%N`A`dH~oAA@8yC-%N`CdxoXX@8yS-%N`lJW3x`o@@8yS-<%N`wJ``ׅo@@@8yC->%N`J`doAA8yC-%N`KdxeoXX8yS-%N`%SW4x`oo@@8yS-<%N`/S``o@@8yC->%N`ZS`dŏoAA8yC-%N`TdxoXX8yS-%N`[W4x`%o@@8yS-<%N`[``Mo@@8yC->%N` \`dfoAA8yC-%N`&]dxoXX8yS-%N`ddW4 x`o@@@8yS-<%N`od``Οo@@8yC->%N`d`dנoAA@8yC-%N`edx3oXX@8yS-%N`lW40x`?o@@@8yS-<%N`l``mo@@@8yC->%N`-m`doAA@8yC-%N`indxðoXX8yS-%N`uW4@x`аo@@8yS-<%N`u``o@@8yC->%N`u`doAA8yC-%N`vdxtoXX8yS-%N`4~W4Px`o@@8yS-<%N`A~``o@@8yC->%N`_~`doAA8yC-%N`Pdx8oXX@GxS-%N`W4`$x`Lo@@CGxS-<%N` ``to@@@GxC->%N`4`doAACGxC-%N`RdxoXXCGxS-%N`W4p4x`o@@@NGxS-<%N`ȏ``/o@@CGxC->%N``dkoAA@NGxC-%N`+dxoXXOGxS-%N`W4Dx`o@@@KGxS-<%N```o@@OGxC->%N`Ø`doAA@KGxC-%N`ڙdxoXXLGxS-%N`QW4Tx`o@@@KGxS-<%N````o@@LGxC->%N``doAA@KGxC-%N`dx oXXOGxS-%N`˸W4dx`o@@@NGxS-<%N`ڸ``Ao@@OGxC->%N``dUoAA@NGxC-%N`dxoXXCGxS-%N`W4tx`o@@CGxS-<%N``` o@@CGxC->%N``d oAACGxC-%N`dxoXX@GxS-%N`ZW4x`o@@CGxS-<%N`h``o@@@GxC->%N``doAACGxC-%N`dxoXXCGxS-%N`W4x`o@@@NGxS-<%N```:o@@CGxC->%N``d0oAA@NGxC-%N`dxoXXOGxS-%N`W4x`o@@@KGxS-<%N```o@@OGxC->%N``d oAA@KGxC-%N`dxoXXLGxS-%N`GW4x`o@@@KGxS-<%N`T``o@@LGxC->%N`x`d oAA@KGxC-%N`dxI(oXXOGxS-%N` W5x`W(o@@@NGxS-<%N```y(o@@OGxC->%N`9`d)oAA@NGxC-%N`hdx0oXXCGxS-%N`W5x`0o@@CGxS-<%N```:1o@@CGxC->%N``d,2oAACGxC-%N`dx9oXX@GxS-%N`[W5 x`9o@@CGxS-<%N`i``9o@@@GxC->%N``d:oAACGxC-%N`dxSBoXXCGxS-%N`W50x`bBo@@@NGxS-<%N`"``Bo@@CGxC->%N`D`dCoAA@NGxC-%N`fdxKoXXOGxS-%N`W5@x`%Ko@@@KGxS-<%N```^Ko@@OGxC->%N``dMLoAA@KGxC-%N` dxSoXXLGxS-%N`W5Px`So@@@KGxS-<%N```To@@LGxC->%N``d8UoAA@KGxC-%N`dxZ\oXXOGxS-%N`!W5`%x`g\o@@@NGxS-<%N`'!``\o@@OGxC->%N`}!`d]oAA@NGxC-%N`y"dxqoXXCGxS-%N`g6W5p5x`qo@@CGxS-<%N`s6``ro@@CGxC->%N`6`droAACGxC-%N`7dxjzoXX@GxS-%N`*?W5Ex`yzo@@CGxS-<%N`9?``zo@@@GxC->%N`S?`d{oAACGxC-%N`p@dxoXXCGxS-%N`GW5Ux`+o@@@NGxS-<%N`G``o@@CGxC->%N`GH`doAA@NGxC-%N`UIdx؋oXXOGxS-%N`PW5ex`o@@@KGxS-<%N`P``;o@@OGxC->%N`P`d-oAA@KGxC-%N`QdxoXXLGxS-%N`kYW5ux`o@@@KGxS-<%N`wY``o@@LGxC->%N`Y`doAA@KGxC-%N`ZdxooXXOGxS-%N`/bW5x`|o@@@NGxS-<%N`%N`_b`d؞oAA@NGxC-%N`cdx.oXXCGxS-%N`jW5x`:o@@CGxS-<%N`j``o@@CGxC->%N`Sk`doAACGxC-%N`HldxoXX@GxS-%N`sW5x`̮o@@CGxS-<%N`s``o@@@GxC->%N`s`doAACGxC-%N`tdxoXXCGxS-%N`E|W5x`o@@@NGxS-<%N`S|``o@@CGxC->%N`v|`doAA@NGxC-%N`}dxoXXOGxS-%N`ЄW6x`o@@@KGxS-<%N`܄``?o@@OGxC->%N``dsoAA@KGxC-%N`3dxoXXLGxS-%N`W6x`o@@@KGxS-<%N```o@@LGxC->%N`ҍ`dMoAA@KGxC-%N` dxoXXOGxS-%N`_W6 x`o@@@NGxS-<%N`n``o@@OGxC->%N``doAA@NGxC-%N`dxJoXXCGxS-%N` W60x`Xo@@CGxS-<%N```o@@CGxC->%N`j`doAACGxC-%N`[dx%N`\`doAACGxC-%N`SdxoXXCGxS-%N`W6Px`o@@@NGxS-<%N```%o@@CGxC->%N``d)oAA@NGxC-%N`dxoXXOGxS-%N`KW6`&x`o@@@KGxS-<%N`X``o@@OGxC->%N``doAA@KGxC-%N`dxL oXXLGxS-%N` W6p6x`Y o@@@KGxS-<%N``` o@@LGxC->%N`l`d oAA@KGxC-%N`idxoXXOGxS-%N`W6Fx`o@@@NGxS-<%N```]o@@OGxC->%N``dqoAA@NGxC-%N`1dxFoXXCGxS-%N`W6Vx`Qo@@CGxS-<%N```o@@CGxC->%N`a`doAACGxC-%N`kdx$oXX@GxS-%N`W6fx`$o@@CGxS-<%N```e$o@@@GxC->%N`%`dk%oAACGxC-%N`+dx,oXXCGxS-%N`tW6vx`,o@@@NGxS-<%N```-o@@CGxC->%N``d-oAA@NGxC-%N`dxr5oXXOGxS-%N`2W6x`~5o@@@KGxS-<%N`>``5o@@OGxC->%N``d+7oAA@KGxC-%N`dx>oXXLGxS-%N`W6x`(>o@@@KGxS-<%N```v>o@@LGxC->%N`6`d?oAA@KGxC-%N`PdxoFoXXOGxS-%N`/ W6x`zFo@@@NGxS-<%N`: ``Fo@@OGxC->%N` `dGoAA@NGxC-%N` dxOoXXCGxS-%N`W6x`'Oo@@CGxS-<%N```xOo@@CGxC->%N`8`dvPoAACGxC-%N`6dxWoXX@GxS-%N`XW7x`Wo@@CGxS-<%N`d``Wo@@@GxC->%N``dXoAACGxC-%N`dxJ`oXXCGxS-%N` %W7x`U`o@@@NGxS-<%N`%```o@@CGxC->%N`c%`daoAA@NGxC-%N`f&dxhoXXOGxS-%N`-W7 x` io@@@KGxS-<%N`-``Qio@@OGxC->%N`.`dNjoAA@KGxC-%N`/dxqoXX@KGxS-%N`w6W70x`qo@@LGxS-<%N`6``ro@@@KGxC->%N`6`dsoAALGxC-%N`7dx?zoXXLGxS-%N`>W7@x`Lzo@@@KGxS-<%N` ?``jzo@@LGxC->%N`*?`d{oAA@KGxC-%N``@dxoXX@KGxS-%N`GW7Px`o@@LGxS-<%N`G``ao@@@KGxC->%N`!H`dRoAALGxC-%N`IdxƋoXXLGxS-%N`PW7`'x`Ӌo@@@KGxS-<%N`P``o@@LGxC->%N`P`doAA@KGxC-%N`QdxoXX@KGxS-%N`TYW7p7x`o@@LGxS-<%N``Y``o@@@KGxC->%N`Y`doAALGxC-%N`ZdxըoXXLGxS-%N`mW7Gx`o@@@KGxS-<%N`m``1o@@LGxC->%N`m`d@oAA@KGxC-%N`odxoXX@KGxS-%N`QvW7Wx`o@@LGxS-<%N`\v``o@@@KGxC->%N`v`dٲoAALGxC-%N`wdxoXXLGxS-%N`~W7gx`&o@@@KGxS-<%N`~``to@@LGxC->%N`4`dwoAA@KGxC-%N`7dxoXX@KGxS-%N`W7wx`o@@LGxS-<%N```o@@@KGxC->%N``dLoAALGxC-%N` dxoXXLGxS-%N`LW7x`o@@@KGxS-<%N`W``o@@LGxC->%N``doAA@KGxC-%N`dx8oXX@KGxS-%N`W7x`Co@@LGxS-<%N```o@@@KGxC->%N`R`doAALGxC-%N`AdxoXX8yS-%N`W7x`o@@8yS-<%N`á``Ao@@8yC->%N``dGoAA8yC-%N`dxoXX8yS-%N`lW7x`o@@8yS-<%N`w``o@@8yC->%N``dHoAA8yC-%N`dxHoXX@8yS-%N`W8x`Wo@@@8yS-<%N```vo@@@8yC->%N`6`dcoAA@8yC-%N`#dxoXX@8yS-%N`W8x`o@@8yS-<%N```o@@@8yC->%N``doAA8yC-%N`dxboXX@8yS-%N`"W8 x`so@@@8yS-<%N`3``o@@@8yC->%N`s`doAA@8yC-%N`dx0!oXX8yS-%N`W80x`>!o@@8yS-<%N```!o@@8yC->%N`H`d"oAA8yC-%N`jdx)oXX8yS-%N`W8@x`*o@@8yS-<%N```G*o@@8yC->%N``dL+oAA8yC-%N` dx2oXX8yS-%N`HW8Px`2o@@8yS-<%N`W``2o@@8yC->%N`w`d3oAA8yC-%N`adxN;oXX8yS-%N`W8`(x`\;o@@8yS-<%N```|;o@@8yC->%N`<`d%N`4 `dqEoAA8yC-%N`1 dxLoXX@8yS-%N`W8Hx`Lo@@@8yS-<%N```.Mo@@@8yC->%N``dFNoAA@8yC-%N`dxUoXX@8yS-%N`WW8Xx`Uo@@@8yS-<%N`d``Uo@@@8yC->%N``dVoAA@8yC-%N`dx)^oXX8yS-%N`"W8hx`9^o@@8yS-<%N`"``x^o@@8yC->%N`8#`d~_oAA8yC-%N`>$dxfoXX8yS-%N`+W8xx`fo@@8yS-<%N`+``@go@@8yC->%N`,`d`hoAA8yC-%N` -dxiooXX8yS-%N`)4W8x`woo@@8yS-<%N`74``oo@@8yC->%N`q4`dpoAA8yC-%N`5dxxoXX@8yS-%N`<W8x`!xo@@8yS-<%N`<``hxo@@@8yC->%N`(=`dbyoAA8yC-%N`">dxoXX8yS-%N`EW8x`o@@8yS-<%N`E``5o@@8yC->%N`E`d5oAA8yC-%N`FdxoXX8yS-%N`fNW8x`o@@8yS-<%N`sN``o@@8yC->%N`N`doAA8yC-%N`OdxjoXX8yS-%N`*WW9x`xo@@8yS-<%N`8W``o@@8yC->%N`W`dғoAA8yC-%N`Xdx'oXX@8yS-%N`_W9x`3o@@@8yS-<%N`_``to@@@8yC->%N`4``doAA@8yC-%N`JadxۣoXX@8yS-%N`hW9 x`o@@@8yS-<%N`h``)o@@@8yC->%N`h`d*oAA@8yC-%N`idxoXX8yS-%N`ZqW90x`o@@8yS-<%N`gq``o@@8yC->%N`q`doAA8yC-%N`rdxFoXX8yS-%N`zW9@ x`Uo@@8yS-<%N`z``o@@8yC->%N`Tz`doAA8yC-%N`q{dxͽoXX8yS-%N`W9Px`ٽo@@8yS-<%N```o@@8yC->%N`ׂ`d+oAA8yC-%N`dxoXX8yS-%N`NW9`)x`o@@@8yS-<%N`[``o@@8yC->%N``doAA@8yC-%N`dxOoXX@8yS-%N`W9p9x`[o@@8yS-<%N```o@@@8yC->%N`\`doAA8yC-%N`ldxoXX8yS-%N`W9Ix`o@@8yS-<%N```)o@@8yC->%N``d[oAA8yC-%N`dxoXX8yS-%N`ZW9Yx`o@@8yS-<%N`g``o@@8yC->%N`ĥ`doAA8yC-%N`dxXoXX8yS-%N`W9ix`do@@8yS-<%N`$``o@@8yC->%N`h`doAA8yC-%N`bdxoXX@8yS-%N`ܶW9yx`(o@@@8yS-<%N```go@@@8yC->%N`'`dioAA@8yC-%N`)dxoXX@8yS-%N`W9x`o@@@8yS-<%N```/o@@@8yC->%N``dKoAA@8yC-%N` dxkoXX8yS-%N`+W9x`xo@@8yS-<%N`8``o@@8yC->%N`z`doAA8yC-%N`dx oXX8yS-%N`W9x` o@@8yS-<%N```J o@@8yC->%N` `da oAA8yC-%N`!dxoXX8yS-%N`W9x`o@@8yS-<%N```o@@8yC->%N``d&oAA8yC-%N`dxoXX8yS-%N`@W:x`o@@@8yS-<%N`L``o@@8yC->%N``doAA@8yC-%N`dx;&oXX@8yS-%N`W:x`G&o@@8yS-<%N```&o@@@8yC->%N`F`d'oAA8yC-%N`Xdx.oXX8yS-%N`W: x`/o@@8yS-<%N```@/o@@8yC->%N``dZ0oAA8yC-%N`dx7oXX8yS-%N`qW:0x`7o@@8yS-<%N`}``7o@@8yC->%N``d8oAA8yC-%N`dxO@oXX8yS-%N` W:@ x`[@o@@8yS-<%N` ``@o@@8yC->%N`Z `dAoAA8yC-%N`q dxIoXX@8yS-%N` W:Px`Io@@@8yS-<%N` ``PIo@@@8yC->%N` `dsJoAA@8yC-%N`3 dxQoXX@8yS-%N` W:`*x`Qo@@@8yS-<%N` ``JRo@@@8yC->%N`  `dXSoAA@8yC-%N` dxaZoXX8yS-%N`! W:p:x`pZo@@8yS-<%N`0 ``Zo@@8yC->%N`L `d[oAA8yC-%N`t dxcoXX8yS-%N`' W:Jx`'co@@8yS-<%N`' ``Uco@@8yC->%N`( `dddoAA8yC-%N`$) dxkoXX8yS-%N`0 W:Zx`ko@@8yS-<%N`0 ``!lo@@8yC->%N`0 `d1moAA8yC-%N`1 dxtoXX8yS-%N`]9 W:jx`to@@@8yS-<%N`j9 ``to@@8yC->%N`9 `dvoAA@8yC-%N`: dx-}oXX@8yS-%N`A W:zx`9}o@@8yS-<%N`A ``y}o@@@8yC->%N`9B `d~oAA8yC-%N`MC dxoXX8yS-%N`J W:x`o@@8yS-<%N`J ``:o@@8yC->%N`J `d:oAA8yC-%N`K dxoXX8yS-%N`rS W:x`o@@8yS-<%N`S ``o@@8yC->%N`S `doAA8yC-%N`T dxSoXX8yS-%N`\ W:x`go@@8yS-<%N`'\ ``o@@8yC->%N`d\ `doAA8yC-%N`T] dxoXX@8yS-%N`d W:x` o@@@8yS-<%N`d ``ko@@@8yC->%N`+e `dyoAA@8yC-%N`9f dxoXX@8yS-%N`Km W;x`o@@@8yS-<%N`Xm ``Өo@@@8yC->%N`m `doAA@8yC-%N`n dxJoXX8yS-%N` v W;x`Wo@@8yS-<%N`v ``o@@8yC->%N`Xv `doAA8yC-%N`ow dx oXX8yS-%N`~ W; x`o@@8yS-<%N`~ ``_o@@8yC->%N` `doAA8yC-%N`z dxoXX8yS-%N` W;0x`o@@8yS-<%N` ``o@@8yC->%N`݇ `dAoAA8yC-%N` dxgoXX8yS-%N`' W;@ x`so@@@8yS-<%N`3 ``o@@8yC->%N`u `doAA@8yC-%N`U dxoXX@8yS-%N`ۧ W;Px`(o@@8yS-<%N` ``Co@@@8yC->%N` `dHoAA8yC-%N` dxoXX8yS-%N` W;`+x`o@@8yS-<%N` ``o@@8yC->%N` `doAA8yC-%N`ñ dxoXX8yS-%N`C W;p;x`o@@8yS-<%N`T ``o@@8yC->%N`s `doAA8yC-%N`x dxoXX8yS-%N` W;Kx`o@@8yS-<%N` ``o@@8yC->%N` `doAA8yC-%N` dxoXX8yS-%N`X W;[x`o@@8yS-<%N`j ``o@@8yC->%N` `doAA8yC-%N` dx*oXX8yS-%N` W;kx`9o@@8yS-<%N` ``Wo@@8yC->%N` `dgoAA8yC-%N`' dxoXX8yS-%N`v W;{x`o@@8yS-<%N` ``o@@8yC->%N` `doAA8yC-%N` dxhoXX8yS-%N`( W;x`yo@@8yS-<%N`9 ``o@@8yC->%N`S `d oAA8yC-%N`V dx(oXX8yS-%N` W;x`-(o@@8yS-<%N` ``I(o@@8yC->%N` `dI)oAA8yC-%N` dx0oXX8yS-%N` W;x`0o@@8yS-<%N` ``0o@@8yC->%N` `d1oAA8yC-%N` dx9oXX8yS-%N` W;x`-9o@@8yS-<%N` ``n9o@@8yC->%N`. `dk:oAA8yC-%N`+ dxAoXX8yS-%N`@ W<x`Ao@@8yS-<%N`L ``Ao@@8yC->%N` `dBoAA8yC-%N` dx1JoXX8yS-%N` W<x`>Jo@@8yS-<%N` ``aJo@@8yC->%N`! `d_KoAA8yC-%N` dxRoXX8yS-%N` W< x`Ro@@8yS-<%N` ``So@@8yC->%N`d `dToAA8yC-%N`h dxu[oXX8yS-%N`5 W<0x`[o@@8yS-<%N`B ``[o@@8yC->%N` `d\oAA8yC-%N`! dxcoXX8yS-%N`( W<@ x`do@@8yS-<%N`( ``Ado@@8yC->%N`) `dXeoAA8yC-%N`* dxloXX8yS-%N`s1 W%N`1 `dmoAA8yC-%N`2 dxvuoXX8yS-%N`6: W<`,x`uo@@8yS-<%N`E: ``uo@@8yC->%N`: `dvoAA8yC-%N`; dx"~oXX8yS-%N`B W%N`C `dtoAA8yC-%N`4D dxoXX8yS-%N`K W<Lx`o@@8yS-<%N`K ``?o@@8yC->%N`K `d@oAA8yC-%N`M dxoXX8yS-%N`fT W<\x`o@@8yS-<%N`}T ``o@@8yC->%N`T `doAA8yC-%N`U dxfoXX8yS-%N`&] W<lx`so@@8yS-<%N`3] ``o@@8yC->%N`u] `d̙oAA8yC-%N`^ dx*oXX8yS-%N`e W<|x`7o@@8yS-<%N`e ``}o@@8yC->%N`=f `doAA8yC-%N`qg dxoXX8yS-%N`n W<x`o@@8yS-<%N`n ``:o@@8yC->%N`n `d?oAA8yC-%N`o dxoXX8yS-%N`Zw W<x`o@@8yS-<%N`gw ``o@@8yC->%N`w `doAA8yC-%N`x dx\oXX8yS-%N` W<x`io@@8yS-<%N`) ``o@@8yC->%N`i `doAA8yC-%N`| dxoXX8yS-%N`ۈ W<x`'o@@8yS-<%N` ``ho@@8yC->%N`( `doAA8yC-%N`L dxoXX8yS-%N` W=x`o@@8yS-<%N` ``o@@8yC->%N`Α `d oAA8yC-%N`͒ dx}oXX8yS-%N`= W=x`o@@8yS-<%N`P ``o@@8yC->%N` `doAA8yC-%N` dxEoXX8yS-%N` W= x`To@@8yS-<%N` ``o@@8yC->%N`M `dwoAA8yC-%N`7 dx oXX8yS-%N`̫ W=0x`o@@8yS-<%N`ܫ ``;o@@8yC->%N` `dOoAA8yC-%N` dxoXX8yS-%N` W=@ x`o@@8yS-<%N` ``o@@8yC->%N` `doAA8yC-%N` dxyoXX8yS-%N`9 W=Px`o@@8yS-<%N`K ``o@@8yC->%N`i `doAA8yC-%N`f dx@oXX8yS-%N` W=`-x`Qo@@@8yS-<%N` ``po@@8yC->%N`0 `dUoAA@8yC-%N` dx oXX@8yS-%N` W=p=x` o@@@8yS-<%N` ``0 o@@@8yC->%N` `d( oAA@8yC-%N` dxoXX8yS-%N` W=Mx`o@@8yS-<%N` ``o@@8yC->%N` `doAA8yC-%N` dx{oXX8yS-%N`; W=]x`o@@8yS-<%N`J ``o@@8yC->%N`g `doAA8yC-%N`h dx0$oXX8yS-%N` W=mx`D$o@@8yS-<%N` ``|$o@@8yC->%N`< `dE%oAA8yC-%N` dx,oXX8yS-%N` W=}x`-o@@@8yS-<%N` ``4-o@@8yC->%N` `da.oAA@8yC-%N`! dx"6oXX@8yS-%N` W=x`26o@@8yS-<%N` ``M6o@@@8yC->%N` `d7oAA8yC-%N`C dx>oXX8yS-%N`~ W=x`>o@@8yS-<%N` `` ?o@@8yC->%N` `d@oAA8yC-%N` dxGoXX8yS-%N`G W=x`Go@@8yS-<%N`W ``Go@@8yC->%N` `dHoAA8yC-%N`u dxPoXX8yS-%N` W=x`)Po@@8yS-<%N` ``LPo@@8yC->%N`  `d7QoAA8yC-%N` dxXoXX@8yS-%N` W>x`Xo@@@8yS-<%N` ``Yo@@@8yC->%N` `d6ZoAA@8yC-%N` dxaoXX@8yS-%N`]& W>x`ao@@@8yS-<%N`l& ``ao@@@8yC->%N`& `dboAA@8yC-%N`' dx`joXX8yS-%N` / W> x`ojo@@8yS-<%N`// ``jo@@8yC->%N`l/ `dkoAA8yC-%N`i0 dx8soXX8yS-%N`7 W>0x`Iso@@8yS-<%N` 8 ``so@@8yC->%N`E8 `dtoAA8yC-%N`^9 dx{oXX8yS-%N`@ W>@x`|o@@8yS-<%N`@ ``P|o@@8yC->%N`A `d}}oAA8yC-%N`=B dxoXX8yS-%N`MI W>Px`o@@@8yS-<%N`\I ``؄o@@8yC->%N`I `dօoAA@8yC-%N`J dxJoXX@8yS-%N` R W>`.x`Yo@@8yS-<%N`R ``o@@@8yC->%N`VR `doAA8yC-%N`yS dxoXX8yS-%N`Z W>p>x`"o@@8yS-<%N`Z ``^o@@8yC->%N`[ `dboAA8yC-%N`"\ dx۞oXX8yS-%N`c W>Nx`o@@8yS-<%N`c ``+o@@8yC->%N`c `d=oAA8yC-%N`d dxoXX8yS-%N`cl W>^x`o@@8yS-<%N`ql ``xo@@8yC->%N`8m `dfoAA8yC-%N`&n dx3oXX@8yS-%N`t W>nx`Ao@@@8yS-<%N`u ``o@@@8yC->%N`Bu `doAA@8yC-%N`Xv dxoXX@8yS-%N`} W>~x` o@@@8yS-<%N`} ``Oo@@@8yC->%N`~ `d^oAA@8yC-%N` dxoXX8yS-%N`L W>x`o@@8yS-<%N`Z ``o@@8yC->%N` `doAA8yC-%N` dxaoXX8yS-%N`! W>x`ro@@8yS-<%N`2 ``o@@8yC->%N` `doAA8yC-%N` dx#oXX8yS-%N` W>x`3o@@8yS-<%N` ``so@@8yC->%N`3 `doAA8yC-%N`J dxoXX8yS-%N` W>x`o@@@8yS-<%N` ``/o@@8yC->%N` `dAoAA@8yC-%N` dxoXX@8yS-%N`a W?x`o@@8yS-<%N`o ``o@@@8yC->%N` `doAA8yC-%N`Ī dx%N`L `doAA8yC-%N`` dxoXX8yS-%N` W? x`o@@8yS-<%N` ``,o@@8yC->%N` `d,oAA8yC-%N` dxXoXX8yS-%N` W?0x`co@@8yS-<%N`# ``o@@8yC->%N` `doAA8yC-%N` dxoXX@8yS-%N` W?@x`,o@@@8yS-<%N` ``mo@@@8yC->%N`- `d}oAA@8yC-%N`= dxoXX@8yS-%N`o W?Px`o@@@8yS-<%N`{ ``o@@@8yC->%N` `doAA@8yC-%N` dxboXX8yS-%N`" W?`/x`po@@8yS-<%N`0 ``o@@8yC->%N`\ `doAA8yC-%N` dx!oXX8yS-%N` W?p?x`!o@@8yS-<%N` ``T!o@@8yC->%N` `dd"oAA8yC-%N`$ dx)oXX8yS-%N`i W?Ox`)o@@8yS-<%N`v ``)o@@8yC->%N` `d+oAA8yC-%N` dxQ2oXX8yS-%N` W?_x`\2o@@@8yS-<%N` ``2o@@8yC->%N`_ `d3oAA@8yC-%N`e dx:oXX@8yS-%N` W?ox`;o@@8yS-<%N` ``N;o@@@8yC->%N` `dY%N` `dDoAA8yC-%N` dx LoXX8yS-%N` W?x`Lo@@8yS-<%N` ``ZLo@@8yC->%N` `dlMoAA8yC-%N`, dxToXX8yS-%N` W?x`To@@8yS-<%N` `` Uo@@8yC->%N` `d VoAA8yC-%N` dx']oXX@8yS-%N`! W?x`=]o@@@8yS-<%N`! ``a]o@@@8yC->%N`!" `d]^oAA@8yC-%N`# dxeoXX@8yS-%N`u* W?x`eo@@@8yS-<%N`* ``fo@@@8yC->%N`* `d.oAA@8yC-%N`F dxoXXzS-%N`eL WDa%x`o@@@zS-<%N`wL ``o@@zC->%N`L `djoAA@zC-%N`*o dx;oXX@{DwS-%N`~ W {x`Ro@@zDwS-<%N` ``o@@@{DwC->%N`V `doAAzDwC-%N`Q dxoXXDwS-%N` W PMR 15)x`o@@@rDwS-<%N` ``1o@@DwC->%N` `ddoAA@rDwC-%N`$ dxnoXXvDwS-%N`. W0x`o@@}DwS-<%N`@ ``o@@vDwC->%N` `doAA}DwC-%N` dx6oXXwDwS-%N` W@Dau@x`Go@@@uDwS-<%N` ``o@@wDwC->%N`G `doAA@uDwC-%N`c dxoXX@~DwS-%N`Ǫ WP x`o@@yDwS-<%N`ת ``To@@@~DwC->%N` `dsoAAyDwC-%N`3 dxoXXpDwS-%N` W`PMR 16jx`o@@vDwS-<%N` ``!o@@pDwC->%N` `dDoAAvDwC-%N` dxoXXvDwS-%N`W Wpx`o@@pDwS-<%N`e ``o@@vDwC->%N` `doAApDwC-%N` dx[oXXyDwS-%N` W x`io@@@~DwS-<%N`) ``o@@yDwC->%N`j `doAA@~DwC-%N` dx oXX@uDwS-%N`d W x` o@@wDwS-<%N`r `` o@@@uDwC->%N` `doAAwDwC-%N` dxhoXX}DwS-%N`( W x`vo@@vDwS-<%N`6 ``o@@}DwC->%N`x `doAAvDwC-%N` dx/oXX@rDwS-%N` W 0x`<o@@DwS-<%N` ``o@@@rDwC->%N`B `doAADwC-%N`t dx&oXXzDwS-%N` W @x`&o@@@{DwS-<%N` ``'o@@zDwC->%N` `d.(oAA@{DwC-%N` dxR/oXX8yS-%N` W Px``/o@@8yS-<%N` ``/o@@8yC->%N`a `d0oAA8yC-%N`_ dx8oXX8yS-%N` W `x`8o@@8yS-<%N` ``8o@@8yC->%N` `d9oAA8yC-%N` dx@oXX8yS-%N` W p$x`@o@@8yS-<%N` ``Ao@@8yC->%N` `dKBoAA8yC-%N`  d`SIo@@8yS-<%N` `xQoXX@8yS-%N` WCQ%;x`Ro@@@8yC->%N` `dlzoAA8yC-%N`,? dxoXX8yS-%N`G W&Ox`o@@@8yS-<%N`G ``So@@8yC->%N`H `d{oAA@8yC-%N`;I dxoXX8yS-%N`~P W Channel VFO AZx`ˋo@@8yS-<%N`P `` o@@8yC->%N`P `d oAA8yC-%N`Q dx@oXX8yS-%N`Y W0Lx`Mo@@8yS-<%N` Y ``o@@8yC->%N`PY `doAA8yC-%N`bZ dxȜoXX8yS-%N`a W@Q%Lx`Ӝo@@8yS-<%N`a ``o@@8yC->%N`a `d#oAA8yC-%N`b dxLoXX@8yS-%N` j WP&x`Xo@@@8yS-<%N`j ``o@@@8yC->%N`Vj `doAA@8yC-%N`qk dxpoXX@8yS-%N`0s W`Channel VFO Bx`|o@@@8yS-<%N`%N`s `dׯoAA@8yC-%N`t dxoXX8yS-%N`U| Wpx`o@@8yS-<%N`a| ``o@@8yC->%N`| `doAA8yC-%N`} dxRoXX8yS-%N` W(4x`]o@@8yS-<%N` ``o@@8yC->%N`ԅ `d9oAA8yC-%N` dxoXX8yS-%N`g W(Dx`o@@8yS-<%N`r ``o@@8yC->%N` `doAA8yC-%N` dx_oXX@8yS-%N` W( Tx`ko@@8yS-<%N`+ ``o@@@8yC->%N`h `doAA8yC-%N` dxoXX8yS-%N` W(0dx`o@@@8yS-<%N` ``\o@@8yC->%N` `doAA@8yC-%N`أ dxoXX8yS-%N`m W(@tx`o@@8yS-<%N`{ ``o@@8yC->%N` `doAA8yC-%N` dx6oXX8yS-%N` W(Px`Bo@@8yS-<%N` ``o@@8yC->%N`Z `doAA8yC-%N`h dxoXX8yS-%N` W(`x`o@@8yS-<%N` ``/o@@8yC->%N` `d=oAA8yC-%N` dxyoXX@8yS-%N`9 W(px`o@@@8yS-<%N`D ``o@@@8yC->%N` `doAA@8yC-%N` dx*oXX@8yS-%N` W 6x`5o@@@8yS-<%N` ``so@@@8yC->%N`3 `d?oAA@8yC-%N`dxUHoXX8yS-%N` W JKLMx``Ho@@8yS-<%N` ``Ho@@8yC->%N`b `dIoAA8yC-%N`pdx QoXX8yS-%N`W !x`Qo@@8yS-<%N```jQo@@8yC->%N`*`dWRoAA8yC-%N`dxYoXX8yS-%N`W01x`Yo@@8yS-<%N```Zo@@8yC->%N``dR[oAA8yC-%N` dxUboXX@8yS-%N`'W@Ax`bbo@@8yS-<%N`"'``bo@@@8yC->%N`a'`dcoAA8yC-%N`z(dxjoXX8yS-%N`/WPQx`jo@@@8yS-<%N`/``'ko@@8yC->%N`/`d$loAA@8yC-%N`0dxsoXX8yS-%N`Q8W`ax`so@@8yS-<%N`\8``so@@8yC->%N`8`dtoAA8yC-%N`9dxI|oXX8yS-%N` AWpqx`T|o@@8yS-<%N`A``|o@@8yC->%N`XA`d~}oAA8yC-%N`>Bdx oXX8yS-%N`IWx`o@@8yS-<%N`I``So@@8yC->%N`J`doAA8yC-%N`?KdxoXX@8yS-%N`}RWx`ȍo@@@8yS-<%N`R``0o@@@8yC->%N`R`d1oAA@8yC-%N`SdxGoXX@8yS-%N`[Wx`Ro@@@8yS-<%N`[``o@@@8yC->%N`][`doAA@8yC-%N`g\dxoXX8yS-%N`cWx`o@@8yS-<%N`c``8o@@8yC->%N`c`dXoAA8yC-%N`edxoXX8yS-%N`elWx`o@@8yS-<%N`ol``o@@8yC->%N`l`dڨoAA8yC-%N`mdxڼoXX@{DwS-%N`Wx`o@@zDwS-<%N``` o@@@{DwC->%N`Ɂ`d>oAAzDwC-%N`dxoXXDwS-%N`UWx`o@@@rDwS-<%N`b``o@@DwC->%N``doAA@rDwC-%N`dxLoXXvDwS-%N` Wx`Yo@@}DwS-<%N```o@@vDwC->%N`Y`doAA}DwC-%N`_dxoXXwDwS-%N`ěWx`o@@@uDwS-<%N`Л``Qo@@wDwC->%N``dmoAA@uDwC-%N`-dxoXX@~DwS-%N`Wx`o@@yDwS-<%N```7o@@@~DwC->%N``d"oAAyDwC-%N`dxroXXpDwS-%N`2W "x`}o@@vDwS-<%N`=``o@@pDwC->%N``doAAvDwC-%N`dx>oXXvDwS-%N`W02x`Jo@@pDwS-<%N` ``o@@vDwC->%N`K`doAApDwC-%N`PdxoXXyDwS-%N`W@Bx`o@@@~DwS-<%N```o@@yDwC->%N`Ͼ`d&oAA@~DwC-%N`dx}oXX@uDwS-%N`=WPRx`o@@wDwS-<%N`H``o@@@uDwC->%N``doAAwDwC-%N`dx& oXX8yS-%N`W`bx`2 o@@8yS-<%N```r o@@8yC->%N`2`dk oAA8yC-%N`+dxoXX@8yS-%N`Wprx`o@@8yS-<%N```.o@@@8yC->%N``doAA8yC-%N`dxoXX8yS-%N`cWx`o@@@8yS-<%N`q``o@@8yC->%N``doAA@8yC-%N`dx(%oXX8yS-%N`Wx`5%o@@8yS-<%N```u%o@@8yC->%N`5`d&oAA8yC-%N`Ddx-oXX8yS-%N`qWx`-o@@8yS-<%N```.o@@8yC->%N``d/oAA8yC-%N`dx86oXX8yS-%N`Wx`E6o@@8yS-<%N```6o@@8yC->%N`F`d7oAA8yC-%N`Xdx>oXX@8yS-%N`Wx`>o@@@8yS-<%N```?o@@@8yC->%N``d#@oAA@8yC-%N`dx}GoXX@8yS-%N`= Wx`Go@@@8yS-<%N`J ``Go@@@8yC->%N` `dHoAA@8yC-%N` dxWPoXX8yS-%N`Wx`fPo@@8yS-<%N`&``Po@@8yC->%N`k`dQoAA8yC-%N`udxXoXX8yS-%N`Wx`Xo@@8yS-<%N```HYo@@8yC->%N``d6ZoAA8yC-%N`dxaoXX8yS-%N`q&W_cdeBCDEx`ao@@8yS-<%N`&``bo@@8yC->%N`&`dcoAA8yC-%N`'dxwoXX@8yS-%N`F<WFGHI9x`wo@@8yS-<%N`T<``wo@@@8yC->%N`<`dxoAA8yC-%N`=dxoXX8yS-%N`W #x`'o@@@8yS-<%N```Ho@@8yC->%N``dLoAA@8yC-%N`d`o@@8yS-<%N`+ `xoXX8yS-%N`W03x`Do@@8yC->%N``duoAA8yC-%N`dxoXX8yS-%N`a%W@Cx`o@@8yS-<%N`s%``o@@8yC->%N`%`d oAA8yC-%N`&dxoXX8yS-%N`l-WPSx`o@@8yS-<%N`{-``Go@@8yC->%N`-`dSoAA8yC-%N`.dxoXX@8yS-%N`&6W`cx`o@@@8yS-<%N`56``o@@@8yC->%N`y6`doAA@8yC-%N`y7dxCoXX@8yS-%N`>Wpsx`Ro@@@8yS-<%N`>``o@@@8yC->%N`?`doAA@8yC-%N`@dx oXX8yS-%N`GWx`o@@8yS-<%N`G``6o@@8yC->%N`G`dRoAA8yC-%N`HdxoXX8yS-%N`HPWx`o@@8yS-<%N`XP``o@@8yC->%N`P`d%oAA8yC-%N`QdxhoXX8yS-%N`XWx`uo@@8yS-<%N`X``o@@8yC->%N`8Y`doAA8yC-%N`UZdx0oXX@8yS-%N`aWx`=o@@8yS-<%N`a``o@@@8yC->%N`b`doAA8yC-%N`cdxoXX8yS-%N`zjWx`o@@@8yS-<%N`j``Lo@@8yC->%N`j`daoAA@8yC-%N`kdxoXX8yS-%N`JsWx`o@@8yS-<%N`Zs``o@@8yC->%N`{s`dDoAA8yC-%N`tdxoXX8yS-%N`|Wx`o@@8yS-<%N`|``o@@8yC->%N`W|`doAA8yC-%N`m}dxOoXX8yS-%N`τWx`\o@@8yS-<%N`܄``o@@8yC->%N``doAA8yC-%N`dx oXX8yS-%N`Wx` o@@8yS-<%N```b o@@8yC->%N``dq oAA8yC-%N`dxoXX8yS-%N`QWx`o@@8yS-<%N`^`` o@@8yC->%N``d9oAA8yC-%N`dxoXX8yS-%N`W $x`o@@8yS-<%N```o@@8yC->%N`c`doAA8yC-%N`vdx%oXX8yS-%N`W04x`+%o@@8yS-<%N```m%o@@8yC->%N``dO&oAA8yC-%N`Ϩdx-oXX8yS-%N`nW@Dx`-o@@8yS-<%N`{``=.o@@8yC->%N``dF/oAA8yC-%N`Ʊdx:6oXX8yS-%N`WPTx`G6o@@8yS-<%N`Ǹ``6o@@8yC->%N` `d7oAA8yC-%N`Sdx>oXX8yS-%N`GW`dx`>o@@8yS-<%N`Q``%?o@@8yC->%N``dJ@oAA8yC-%N`dxuGoXX8yS-%N`Wptx`Go@@8yS-<%N```Go@@8yC->%N`F`dHoAA8yC-%N`GdxOoXX8yS-%N`mWx`Oo@@8yS-<%N`y``>Po@@8yC->%N``d``Yo@@8yC->%N``dZoAA8yC-%N`dxKaoXX8yS-%N`Wx`Vao@@8yS-<%N```ao@@8yC->%N``dboAA8yC-%N`Mdx joXX8yS-%N`Wx`jo@@8yS-<%N```Xjo@@8yC->%N``d_koAA8yC-%N`dxroXX8yS-%N`Wx`ro@@8yS-<%N`,``ro@@8yC->%N`n`dsoAA8yC-%N`qdx_{oXX8yS-%N`Wx`k{o@@8yS-<%N```{o@@8yC->%N`E`d|oAA8yC-%N`*dxoXX8yS-%N`Wx`o@@8yS-<%N```_o@@8yC->%N``doAA8yC-%N`dx̌oXX8yS-%N`LWx`،o@@8yS-<%N`X``o@@8yC->%N``dZoAA8yC-%N`dxoXX8yS-%N`Wnopqx`o@@8yS-<%N```ҕo@@8yC->%N`R`d͖oAA8yC-%N`Mdx6oXX8yS-%N` Wx`Do@@8yS-<%N` ``o@@8yC->%N`!`doAA8yC-%N`"dxoXX8yS-%N`p)W %x`o@@8yS-<%N`)``Bo@@8yC->%N`)`d]oAA8yC-%N`*dxoXX8yS-%N`'2W05x`o@@8yS-<%N`32``o@@8yC->%N`p2`doAA8yC-%N`z3dx5oXX8yS-%N`:W@Ex`@o@@8yS-<%N`:``}o@@8yC->%N`:`doAA8yC-%N`<dxoXX8yS-%N`2CWPUx`o@@8yS-<%N`=C``o@@8yC->%N`}C`doAA8yC-%N`vDdxjoXX8yS-%N`KW`ex`uo@@8yS-<%N`K``o@@8yC->%N`3L`doAA8yC-%N`LMdxoXX8yS-%N`nTWpux`o@@8yS-<%N`yT``7o@@8yC->%N`T`dAoAA8yC-%N`UdxoXX8yS-%N`]Wx`o@@8yS-<%N`*]``o@@8yC->%N`q]`doAA8yC-%N`s^dxSoXX8yS-%N`eWx`]o@@8yS-<%N`e``o@@8yC->%N`Mf`doAA8yC-%N`UgdxoXX8yS-%N`MnWx`o@@8yS-<%N`Xn``o@@8yC->%N`n`d$oAA8yC-%N`odxoXX8yS-%N`wWx`o@@8yS-<%N`w``o@@8yC->%N`Ow`doAA8yC-%N`xdx oXX8yS-%N`Wx`o@@8yS-<%N```7o@@8yC->%N``d8oAA8yC-%N`dxoXX8yS-%N`4Wx`o@@8yS-<%N`>``o@@8yC->%N``d~oAA8yC-%N`dx[oXX8yS-%N`ېWx`fo@@8yS-<%N```o@@8yC->%N`E`doAA8yC-%N`:dxoXX8yS-%N`Wx`)o@@8yS-<%N```ho@@8yC->%N``d}oAA8yC-%N`dx,oXXzS-%N`~Wx`-o@@zS-<%N```8-o@@zC->%N``dy.oAAzC-%N`dx5oXXzS-%N`3Wx`5o@@zS-<%N`@``6o@@zC->%N``d6oAAzC-%N`|dx?>oXXzS-%N`W &x`L>o@@zS-<%N```>o@@zC->%N`"`d?oAAzC-%N`dxFoXXzS-%N`W06x` Go@@zS-<%N```cGo@@zC->%N``d=HoAAzC-%N`dxOoXXzS-%N`>W@Fx`Oo@@@zS-<%N`J``Oo@@zC->%N`l`d%QoAA@zC-%N`dxXoXXzS-%N`WPVx`Xo@@zS-<%N```Xo@@zC->%N`s`dYoAAzC-%N`qdxKaoXXzS-%N`W`fx`Xao@@zS-<%N```|ao@@zC->%N``dboAAzC-%N`5d`!jo@@zS-<%N``xknoXXzS-%N`Wpvx`no@@zC->%N`?`dooAAzC-%N`"dxwoXXzS-%N`Wx`+wo@@zS-<%N```Mwo@@zC->%N``dxoAAzC-%N`KdxoXX@zS-%N`Wx`)o@@zS-<%N```o@@@zC->%N``doAAzC-%N`dxЈoXXzS-%N`P Wx`o@@zS-<%N`` ``o@@zC->%N` `d,oAAzC-%N` dxoXXzS-%N`Wx`o@@zS-<%N```ȑo@@zC->%N`H`dՒoAAzC-%N`UdxoXXzS-%N`Wx`'o@@zS-<%N```Io@@zC->%N``d[oAAzC-%N`dxRoXXzS-%N`$Wx`]o@@@zS-<%N`$``o@@zC->%N`%%`doAA@zC-%N`5&dxoXXzS-%N`|-Wx` o@@zS-<%N`-``6o@@zC->%N`-`dNoAAzC-%N`.dxoXXzS-%N`<6Wx`ͳo@@zS-<%N`M6``o@@zC->%N`u6`d#oAAzC-%N`7dxwoXXzS-%N`>W>@Ax`o@@zS-<%N`?``o@@zC->%N`/?`doAAzC-%N`8@dx2oXXzS-%N`GWx`Co@@zS-<%N`G``ao@@zC->%N`G`doAAzC-%N`IdxoXXzS-%N`rPW 'x`o@@@zS-<%N`P``#o@@zC->%N`P`d9oAA@zC-%N`QdxoXXzS-%N`YW07x`o@@zS-<%N`)Y``o@@zC->%N`RY`doAAzC-%N`cZdxloXXzS-%N`aW@Gx`~o@@zS-<%N`a``o@@zC->%N`'b`doAAzC-%N`Ocdx+oXXzS-%N`jWPWx`;o@@zS-<%N`j``do@@zC->%N`j`doAAzC-%N`*ldxoXXzS-%N`usW`gx` o@@zS-<%N`s``go@@zC->%N`s`d]oAAzC-%N`tdxoXX@zS-%N` |Wpwx`o@@zS-<%N`|``o@@@zC->%N`C|`doAAzC-%N`c}dxQoXXzS-%N`фWx`_o@@zS-<%N`߄``o@@zC->%N``doAAzC-%N`dx oXXzS-%N`cWx` o@@zS-<%N`r`` o@@zC->%N`=`d oAAzC-%N`CdxoXXzS-%N`BWx`o@@zS-<%N`S``o@@zC->%N`z`d)oAAzC-%N`dx@+oXXzS-%N`Wx`Q+o@@@zS-<%N`ѭ``y+o@@zC->%N``d,oAA@zC-%N`3dx3oXXzS-%N`XWx`3o@@zS-<%N`g``4o@@zC->%N``dH5oAAzC-%N`ȷdx%N`d`d=oAAzC-%N`zdxgEoXX8yS-%N`Wx`vEo@@8yS-<%N```Eo@@8yC->%N`>`dFoAA8yC-%N`Ldx4NoXX8yS-%N`Wx`HNo@@8yS-<%N```jNo@@8yC->%N``dOoAA8yC-%N`*dxWoXX8yS-%N`Wx`Wo@@8yS-<%N```HWo@@8yC->%N``dsXoAA8yC-%N`dx_oXX@8yS-%N`eWx`_o@@@8yS-<%N`s```o@@@8yC->%N``dAaoAA@8yC-%N`dxhoXX@8yS-%N`&W (x`ho@@@8yS-<%N`4``ho@@@8yC->%N`f`djoAA@8yC-%N`dx3qoXX8yS-%N`W08x`Aqo@@8yS-<%N```tqo@@8yC->%N``droAA8yC-%N`dxyoXX8yS-%N`yW@Hx`zo@@8yS-<%N```0zo@@8yC->%N``dI{oAA8yC-%N`dxoXX8yS-%N`1WPXx`o@@8yS-<%N`A``o@@8yC->%N`h`doAA8yC-%N`dxioXX8yS-%N` W`hx`xo@@8yS-<%N` ``o@@8yC->%N``doAA8yC-%N`0dxoXX8yS-%N`wWpxx`o@@8yS-<%N```'o@@8yC->%N``dWoAA8yC-%N`dxoXX8yS-%N`.Wx`o@@8yS-<%N`<``ܜo@@8yC->%N`\`doAA8yC-%N` dxoXX8yS-%N`'Wx`#o@@8yS-<%N`'``Co@@8yC->%N`'`d]oAA8yC-%N`(dxέoXX8yS-%N`N0Wx`ܭo@@8yS-<%N`\0``o@@8yC->%N`|0`d%oAA8yC-%N`1dx}oXX8yS-%N`8Wx`o@@8yS-<%N` 9``o@@8yC->%N`,9`dȷoAA8yC-%N`H:dxoXX8yS-%N`AWx`o@@8yS-<%N`A``/o@@8yC->%N`A`dboAA8yC-%N`BdxoXXzS-%N`LJWx`o@@zS-<%N`cJ``o@@zC->%N`J`d#oAAzC-%N`KdxoXXzS-%N`SWx`o@@zS-<%N`S``o@@zC->%N`6S`doAAzC-%N`[TdxMoXXzS-%N`[Wx`bo@@@zS-<%N`[``{o@@zC->%N`[`doAA@zC-%N`,xdxtoXXzS-%N`}WdBCDEFGH`x`o@@zS-<%N` ~``o@@zC->%N`%~`doAAzC-%N`Xdx]oXXzS-%N`݆WIdx`po@@zS-<%N```o@@zC->%N` `doAAzC-%N`)dx oXXzS-%N`W )x`# o@@zS-<%N```@ o@@zC->%N``dzoAAzC-%N`dxoXXzS-%N`ZW09x`o@@zS-<%N`m``co@@zC->%N``d`oAAzC-%N`dxoXX@zS-%N`+W@Ix`o@@zS-<%N`J``o@@@zC->%N`\`d oAAzC-%N`dxd'oXX8yS-%N`WPYx`t'o@@8yS-<%N```'o@@8yC->%N``d(oAA8yC-%N`Edx/0oXX8yS-%N`W`ix`@0o@@8yS-<%N```l0o@@8yC->%N``d1oAA8yC-%N` dx8oXX8yS-%N`uWpyx`9o@@8yS-<%N```R9o@@8yC->%N`һ`d:oAA8yC-%N` dxAoXX8yS-%N`Wx`Ao@@8yS-<%N```Ao@@8yC->%N`9`dBoAA8yC-%N`RdxDJoXX8yS-%N`Wx`TJo@@8yS-<%N```|Jo@@8yC->%N``dKoAA8yC-%N`dx SoXX8yS-%N`Wx`So@@8yS-<%N```ASo@@8yC->%N``diToAA8yC-%N`dx[oXX8yS-%N`fWx`[o@@8yS-<%N`v``\o@@8yC->%N``dN]oAA8yC-%N`dxdoXX8yS-%N`Wx`do@@8yS-<%N```do@@8yC->%N`H`deoAA8yC-%N`ydxKmoXX8yS-%N`Wx`Xmo@@8yS-<%N```mo@@8yC->%N``dnoAA8yC-%N`dxuoXX8yS-%N`~Wx` vo@@8yS-<%N```4vo@@8yC->%N``dhwoAA8yC-%N`dx~oXX8yS-%N`4Wx`~o@@8yS-<%N`C``Vo@@8yC->%N``d!oAA8yC-%N`dxhoXX8yS-%N` W  x`vo@@8yS-<%N` ``o@@8yC->%N`! `d͈oAA8yC-%N`M dxoXX8yS-%N`nW x`o@@8yS-<%N`{``!o@@8yC->%N``dBoAA8yC-%N`dxoXX8yS-%N`@W *x`͘o@@8yS-<%N`M``o@@8yC->%N`y`d.oAA8yC-%N`dx6oXX8yS-%N`#W 0:x`Do@@8yS-<%N`#``mo@@8yC->%N`#`doAA8yC-%N`%dxoXX8yS-%N`=,W @Jx`˩o@@8yS-<%N`K,``o@@8yC->%N`,`d-oAA8yC-%N`-d`>o@@8yS-<%N`4`xoXX8yS-%N`9W PZx`¶o@@8yC->%N`B9`doAA8yC-%N`p:dx$oXX8yS-%N`AW `jx`6o@@8yS-<%N`A``Wo@@8yC->%N`A`dzoAA8yC-%N`BdxoXX8yS-%N`WJW pzx`o@@8yS-<%N`hJ`` o@@8yC->%N`J`d5oAA8yC-%N`KdxoXX}DwS-%N``W x`)o@@vDwS-<%N````Jo@@}DwC->%N```dKoAAvDwC-%N`adxoXX@rDwS-%N`ZkW x`o@@DwS-<%N`jk``o@@@rDwC->%N`k`d;oAADwC-%N`ldxoXXzDwS-%N`tW x`o@@@{DwS-<%N`)t``o@@zDwC->%N`Jt`doAA@{DwC-%N`Nudx6oXX@{DwS-%N`|W x`Ko@@zDwS-<%N`|``ko@@@{DwC->%N`|`doAAzDwC-%N`!~dxoXXzS-%N`WW x`o@@zS-<%N`k``o@@zC->%N``doAAzC-%N`dx5 oXXzS-%N`W x`L o@@zS-<%N`̍``d o@@zC->%N``d oAAzC-%N`$dxLoXXzS-%N`̖W x`_o@@zS-<%N`ߖ``~o@@zC->%N``doAAzC-%N`3dxoXXzS-%N`.W x`o@@@zS-<%N`G``o@@zC->%N```doAA@zC-%N`Sdx?%oXXzDwS-%N`W "M x`a%o@@@{DwS-<%N```s%o@@zDwC->%N``d&oAA@{DwC-%N`dx.oXXDwS-%N`W NOPQRST`x`(.o@@@rDwS-<%N```u.o@@DwC->%N``d{/oAA@rDwC-%N`dx6oXXzS-%N`KW +x`6o@@zS-<%N`]``7o@@zC->%N``d8oAAzC-%N`dxr?oXXvDwS-%N`W 0;x`?o@@}DwS-<%N```?o@@vDwC->%N`P`d@oAA}DwC-%N`Hdx.HoXXwDwS-%N`W @Kx`HHo@@@uDwS-<%N```Ho@@wDwC->%N` `dIoAA@uDwC-%N`dxPoXX@~DwS-%N`vW P[x` Qo@@yDwS-<%N```Qo@@@~DwC->%N``dRoAAyDwC-%N`dxYoXX'zS-%N`7W `kx`Yo@@@'zS-<%N`H``Yo@@'zC->%N``d1[oAA@'zC-%N`dxfboXX'zS-%N`W p{x`ubo@@'zS-<%N```bo@@'zC->%N`0`dcoAA'zC-%N`[dxjoXX'zS-%N`:W x`jo@@'zS-<%N`H``ko@@'zC->%N``d loAA'zC-%N`dxJsoXX'zS-%N`W x`[so@@@'zS-<%N```so@@'zC->%N``dtoAA@'zC-%N`&dx|oXX'zS-%N`W x`|o@@'zS-<%N```e|o@@'zC->%N``d}oAA'zC-%N`$dxoXX'zS-%N`gW x`o@@'zS-<%N`u``;o@@'zC->%N``dZoAA'zC-%N`dxoXX@'zS-%N`|W x` o@@'zS-<%N```+o@@@'zC->%N``d$oAA'zC-%N`dxoXX'zS-%N`&W x`o@@'zS-<%N`5``o@@'zC->%N`y`doAA'zC-%N`dxjoXX@'zS-%N` W x`xo@@'zS-<%N` ``o@@@'zC->%N`@!`dӟoAA'zC-%N`S"dx8oXX'zS-%N`)W x`Go@@'zS-<%N`)``ho@@'zC->%N`)`dmoAA'zC-%N`*dxoXX'zS-%N`q2W  x`o@@@'zS-<%N`2``,o@@'zC->%N`2`dPoAA@'zC-%N`3dx{oXX'zS-%N`:W x`o@@'zS-<%N` ;``o@@'zC->%N`8;`doAA'zC-%N`f<dxoXX'zS-%N`CW ,x` o@@'zS-<%N`C``*o@@'zC->%N`C`d3oAA'zC-%N`DdxoXX'zS-%N`vYW 0<x`o@@@'zS-<%N`Y``Wo@@'zC->%N`Y`dXoAA@'zC-%N`ZdxoXX'zS-%N`%bW @Lx`o@@'zS-<%N`4b``o@@'zC->%N`Tb`doAA'zC-%N`?cdxBoXX'zS-%N`jW P\x`Ro@@'zS-<%N`j``qo@@'zC->%N`j`dboAA'zC-%N`kdxoXX@'zS-%N`ysW `lx` o@@'zS-<%N`s``*o@@@'zC->%N`s`d2oAA'zC-%N`tdxoXX'zS-%N`|W p|x`o@@'zS-<%N`$|``o@@'zC->%N`C|`doAA'zC-%N`Z}dx9oXX@'zS-%N`W x`Jo@@'zS-<%N`ʄ``ho@@@'zC->%N``dYoAA'zC-%N`مdx oXX'zS-%N`SW x` o@@'zS-<%N`c`` o@@'zC->%N``d oAA'zC-%N`dx1oXX'zS-%N`W x`Ao@@@'zS-<%N```_o@@'zC->%N`ߕ`dcoAA@'zC-%N`dxoXX'zS-%N`WW x`o@@'zS-<%N`g``o@@'zC->%N``doAA'zC-%N`vdx$oXX'zS-%N`W x`$o@@'zS-<%N`!``$o@@'zC->%N`A`d%oAA'zC-%N`Rdx-oXX'zS-%N`W x`)-o@@@'zS-<%N```Q-o@@'zC->%N`ѯ`dW.oAA@'zC-%N`װdx5oXX'zS-%N`W x`5o@@'zS-<%N`*``5o@@'zC->%N`L`d6oAA'zC-%N`Ndxg>oXX'zS-%N`W x`s>o@@'zS-<%N```>o@@'zC->%N``d?oAA'zC-%N`dxToXXxzS-%N`.W `adeBCDEx`To@@@vS-<%N`;``To@@xzC->%N`]`dUoAA@vC-%N`Xdx_]oXXvS-%N`W FGHICx`h]o@@@vS-<%N```]o@@vC->%N``d^oAA@vC-%N`dx&foXX@vS-%N`W -x`.fo@@vS-<%N```Ofo@@@vC->%N``dmgoAAvC-%N`dxnoXX@vS-%N`W 0=x`no@@vS-<%N` ``no@@@vC->%N`?`dooAAvC-%N`{dxQwoXX@vS-%N`W @Mx`Zwo@@vS-<%N```wo@@@vC->%N`&`dxoAAvC-%N` dxoXX@vS-%N`VW P]x`o@@vS-<%N`_``o@@@vC->%N``doAAvC-%N`dx҈oXX@vS-%N`R W `mx`ڈo@@vS-<%N`Z ``o@@@vC->%N` `doAAvC-%N` dxoXX@vS-%N`@W p}x`ʑo@@vS-<%N`J``o@@@vC->%N`j`dJoAAvC-%N`dxoXX@vS-%N`W x`o@@vS-<%N```o@@@vC->%N`2`dЛoAAvC-%N`PdxȢoXX@vS-%N`H%W x`Ϣo@@vS-<%N`O%``o@@@vC->%N`%`doAAvC-%N`y&dxooXX@vS-%N`-W x`vo@@vS-<%N`-``o@@@vC->%N`.`doAAvC-%N`#/dx-oXX@vS-%N`6W x`5o@@vS-<%N`6``vo@@@vC->%N`6`d@oAAvC-%N`7dxoXX@vS-%N`n?W x`o@@vS-<%N`w?``5o@@@vC->%N`?`doAAvC-%N`AdxoXXzS-%N`@HW x`o@@zS-<%N`KH``o@@zC->%N`H`d oAAzC-%N`IdxoXXzS-%N`PW x`o@@zS-<%N`P``Oo@@zC->%N`P`dKoAAzC-%N`QdxoXXzS-%N`YW x`o@@zS-<%N`Y``Go@@zC->%N`Y`doAAzC-%N`[dxoXX@zS-%N`JbW x`o@@zS-<%N`Rb``o@@@zC->%N`b`doAAzC-%N`gcdxoXXzS-%N`>kW x`o@@zS-<%N`Hk``o@@zC->%N`k`doAAzC-%N`}ldxoXX@vS-%N`7sW .x`o@@vS-<%N`@s``o@@@vC->%N`}s`doAAvC-%N`itdxoXX@vS-%N`u{W 0>x`o@@vS-<%N`{{``o@@@vC->%N`{`d(oAAvC-%N`|dxoXX@vS-%N`W @Nx`o@@vS-<%N```o@@@vC->%N`^`d oAAvC-%N`dxE oXXzS-%N`ŌW P^x`N o@@zS-<%N`Ό`` o@@zC->%N``dZ oAAzC-%N`ڍdx*oXXzS-%N`W `nx`1o@@zS-<%N```ro@@zC->%N``doAAzC-%N`dxoXXzS-%N`LW p~x`o@@zS-<%N`T``o@@zC->%N``doAAzC-%N`udxt$oXXzS-%N`W x`{$o@@zS-<%N```$o@@zC->%N`=`d%oAAzC-%N`Gdx-oXX@zS-%N`W x`-o@@zS-<%N```-o@@@zC->%N`1`d.oAAzC-%N`[dx6oXXzS-%N`W x`6o@@zS-<%N` ``6o@@zC->%N`M`d7oAAzC-%N``dx>oXX@zS-%N`uW x`?o@@zS-<%N```E?o@@@zC->%N``d$@oAAzC-%N`dxGoXXzS-%N`TW x`Go@@zS-<%N`a`` Ho@@zC->%N``dHoAAzC-%N`~dxPoXXzS-%N`KW x`Po@@zS-<%N`U``5Qo@@zC->%N``dRoAAzC-%N`dxYoXXzS-%N`W x`Yo@@zS-<%N```Yo@@zC->%N`:`dZoAAzC-%N`wdxboXXzS-%N`W x`bo@@zS-<%N`&``co@@zC->%N``d doAAzC-%N`dxlkoXXzS-%N`W#$%&'()*Sx`wko@@zS-<%N```ko@@zC->%N``dloAAzC-%N`KdxtoXXzS-%N`WMNUx`to@@zS-<%N```2to@@zC->%N``d}uoAAzC-%N`dx|oXXzS-%N` W /x`|o@@zS-<%N`.``|o@@zC->%N`O`d~oAAzC-%N`dxoXX@zS-%N`{W0?x`o@@zS-<%N```1o@@@zC->%N``d?oAAzC-%N`dxoXXzS-%N`W@Ox`o@@zS-<%N```o@@zC->%N`8`d؎oAAzC-%N`XdxoXX@zS-%N`WP_x`6o@@zS-<%N```Go@@@zC->%N``dGoAAzC-%N`dxoXXzS-%N`9!W`ox`Ϟo@@zS-<%N`O!``o@@zC->%N`l!`d oAAzC-%N`"dxoXX@zS-%N`*Wpx`o@@zS-<%N`*``o@@@zC->%N`?*`doAAzC-%N`+dx#oXXzS-%N`2Wx`bo@@zS-<%N`2``o@@zC->%N`3`dOoAAzC-%N`3dxoXXzS-%N`0;Wx`o@@zS-<%N`@;``o@@zC->%N`i;`doAAzC-%N`<dxyoXXpDwS-%N`PWx`o@@vDwS-<%N`Q``o@@pDwC->%N`[Q`doAAvDwC-%N`RdxoXXvDwS-%N`YWx`"o@@pDwS-<%N`Y``bo@@vDwC->%N`Y`dcoAApDwC-%N`ZdxoXXyDwS-%N``bWx`o@@@~DwS-<%N`rb``=o@@yDwC->%N`b`d/oAA@~DwC-%N`cdxoXX@uDwS-%N`&kWx`o@@wDwS-<%N`8k``o@@@uDwC->%N`k`doAAwDwC-%N`ldxeoXX}DwS-%N`sWx`xo@@vDwS-<%N`s``o@@}DwC->%N`2t`doAAvDwC-%N`Qudx9oXX@rDwS-%N`|Wx`Qo@@DwS-<%N`|``o@@@rDwC->%N` }`doAADwC-%N`~dxoXX@{DwS-%N`zWx`o@@zDwS-<%N```5o@@@{DwC->%N``dkoAAzDwC-%N`dxm oXXzDwS-%N`W x` o@@@{DwS-<%N``` o@@zDwC->%N``d oAA@{DwC-%N`YdxoXXDwS-%N`W 0x`o@@@rDwS-<%N```6o@@DwC->%N``doAA@rDwC-%N`dxoXXvDwS-%N`:W0@x`o@@}DwS-<%N`J``o@@vDwC->%N``d8oAA}DwC-%N`dxI%oXXwDwS-%N`ɧW@Px`Y%o@@@uDwS-<%N`٧``%o@@wDwC->%N``d&oAA@uDwC-%N`4dx9.oXXzS-%N`WP`x`L.o@@zS-<%N`̰``x.o@@zC->%N``d/oAAzC-%N`dx6oXXzS-%N`rW`px`7o@@zS-<%N```/7o@@zC->%N``dH8oAAzC-%N`Ⱥdx?oXX@~DwS-%N`XWpx`?o@@yDwS-<%N`t``5@o@@@~DwC->%N``d9AoAAyDwC-%N`dx_HoXXpDwS-%N`Wx`qHo@@vDwS-<%N```Ho@@pDwC->%N``dIoAAvDwC-%N`-dx'QoXXvDwS-%N`Wx`:Qo@@pDwS-<%N```cQo@@vDwC->%N``duRoAApDwC-%N`dxZoXXyDwS-%N`Wx`#Zo@@@~DwS-<%N```CZo@@yDwC->%N``de[oAA@~DwC-%N`dxboXX@uDwS-%N`YWx`bo@@wDwS-<%N`i``co@@@uDwC->%N``d7doAAwDwC-%N`dxkoXX}DwS-%N`%Wx`ko@@vDwS-<%N`4``ko@@}DwC->%N`Y`d moAAvDwC-%N`dxatoXX@rDwS-%N`Wx`pto@@DwS-<%N```to@@@rDwC->%N``duoAADwC-%N`Idx}oXX@{DwS-%N`Wx`}o@@zDwS-<%N```1}o@@@{DwC->%N``dY~oAAzDwC-%N`dxoXX@'zS-%N`0Wx`Ņo@@'zS-<%N`E``fo@@@'zC->%N``d֢oAA'zC-%N`V%dxAoXX'zS-%N`)WdeBCDEFGx`Ro@@'zS-<%N`)``so@@'zC->%N`)`doAA'zC-%N`*+dxoXX@'zS-%N`2WHIx`o@@'zS-<%N`2``5o@@@'zC->%N`2`d;oAA'zC-%N`3dxoXX'zS-%N`=;W 1x`˸o@@'zS-<%N`K;``o@@'zC->%N`u;`d+oAA'zC-%N`<dxoXXzS-%N`DW0Ax`o@@zS-<%N`&D``o@@zC->%N`6D`doAAzC-%N`eEdxNoXXzS-%N`LW@Qx`jo@@zS-<%N`L``}o@@zC->%N`L`doAAzC-%N`"NdxoXXzS-%N`UWPax`.o@@zS-<%N`U``Co@@zC->%N`U`dzoAAzC-%N`VdxoXX@zS-%N`'^W`qx`o@@zS-<%N`8^``o@@@zC->%N`Y^`d%oAAzC-%N`_dxloXXzS-%N`fWpx`}o@@zS-<%N`f``o@@zC->%N`g`doAAzC-%N`Thdx2oXX@zS-%N`oWx`Ko@@zS-<%N`o``_o@@@zC->%N`o`dVoAAzC-%N`pdxoXXzS-%N`Wx`2o@@zS-<%N```Go@@zC->%N`Dž`d}oAAzC-%N`dx oXX@zS-%N``Wx` o@@zS-<%N`q`` o@@@zC->%N``dG oAAzC-%N`ǏdxpoXXzS-%N`Wx`o@@zS-<%N```o@@zC->%N``doAAzC-%N`/dxoXXzS-%N`Wx`*o@@zS-<%N```o@@zC->%N`n`doAAzC-%N`Cdx%oXXzS-%N`"Wx`%o@@zS-<%N`2``%o@@zC->%N`N`d&oAAzC-%N`pdxt.oXXzS-%N`Wx`.o@@zS-<%N```.o@@zC->%N`Y`d/oAAzC-%N`bdx6oXXzS-%N`3Wx`6o@@zS-<%N`@``7o@@zC->%N``d#8oAAzC-%N`d`X?o@@zS-<%N``xCoXXzS-%N`#Wx`Do@@zC->%N``dEoAAzC-%N`dx7LoXXzS-%N`W"x`HLo@@zS-<%N```rLo@@zC->%N``dMoAAzC-%N`dxToXX@zS-%N`GW 2x`To@@zS-<%N`W``To@@@zC->%N``d:VoAAzC-%N`dx;]oXXzS-%N`W0Bx`J]o@@zS-<%N```n]o@@zC->%N``d^oAAzC-%N`dxeoXXzS-%N`KW@Rx`eo@@zS-<%N`Y``6fo@@zC->%N``d5goAAzC-%N`dxnoXX@zS-%N`WPbx`no@@zS-<%N`$``no@@@zC->%N`{`dooAAzC-%N`zdxooXXzS-%N` W`rx`o@@zS-<%N` ``o@@zC->%N`u `doAAzC-%N`w dxoXXzS-%N`v Wpx`o@@zS-<%N` ``*o@@zC->%N` `d_oAAzC-%N` dxoXX@zS-%N`@ Wx`ӕo@@zS-<%N`S ``o@@@zC->%N`{ `d*oAAzC-%N` dxoXXzS-%N`! Wx`o@@zS-<%N`! ``Ȟo@@zC->%N`H! `doAAzC-%N`s" dxRoXXzS-%N`) Wx`do@@zS-<%N`) ``o@@zC->%N` * `dɨoAAzC-%N`I+ dxƯoXXzS-%N`F2 Wx`կo@@zS-<%N`U2 ``o@@zC->%N`}2 `doAAzC-%N`3 dxoXX'zS-%N` ; Wx`o@@@'zS-<%N`; ``Ƹo@@'zC->%N`F; `doAA@'zC-%N`d< dx'oXX'zS-%N`C Wx`7o@@'zS-<%N`C ``]o@@'zC->%N`C `doAA'zC-%N`D dxoXX'zS-%N`kL Wx`o@@'zS-<%N`yL ``%o@@'zC->%N`L `dkoAA'zC-%N`M dxoXX'zS-%N`.U Wx`o@@'zS-<%N`%N`hU `d&oAA'zC-%N`V dxioXX@'zS-%N`] W+,-./012x`{o@@'zS-<%N`] ``o@@@'zC->%N`]^ `doAA'zC-%N`k_ dx-oXX'zS-%N`f WVWX.x`%N`f `doAA'zC-%N`h dxoXX'zS-%N`{o W 3x` o@@'zS-<%N`o ``To@@'zC->%N`o `dWoAA'zC-%N`p dxoXX'zS-%N`?x W0Cx`o@@'zS-<%N`Lx ``3o@@'zC->%N`x `dKoAA'zC-%N`y dxOoXX'zS-%N`π W@Sx``o@@'zS-<%N` ``o@@'zC->%N` `doAA'zC-%N`. dxoXX'zS-%N` WPcx`o@@'zS-<%N` ``eo@@'zC->%N` `doAA'zC-%N`? dxoXX'zS-%N`T W`sx`o@@'zS-<%N`a ``%o@@'zC->%N` `d?oAA'zC-%N` dxoXX'zS-%N` Wpx`o@@'zS-<%N`- ``o@@'zC->%N`n `d#oAA'zC-%N` dx!oXX'zS-%N` Wx`!!o@@'zS-<%N` ``e!o@@'zC->%N` `dv"oAA'zC-%N` dx)oXX'zS-%N` Wx`)o@@'zS-<%N` ``)o@@'zC->%N`T `d*oAA'zC-%N`N dx1oXX'zS-%N`d Wx`1o@@'zS-<%N`u ``82o@@'zC->%N` `d3oAA'zC-%N` dxB:oXX'zS-%N`¼ Wx`W:o@@'zS-<%N`׼ ``:o@@'zC->%N` `d;oAA'zC-%N` dxBoXX'zS-%N`< Wx`Bo@@'zS-<%N`J ``Bo@@'zC->%N`n `d$DoAA'zC-%N` dxmKoXX@vS-%N` Wx`Ko@@vS-<%N` ``Ko@@@vC->%N`& `dLoAAvC-%N`# dxSoXX@vS-%N`h Wx`So@@vS-<%N` ``To@@@vC->%N` `d!UoAAvC-%N` dx?\oXX@vS-%N` Wx`V\o@@vS-<%N` ``q\o@@@vC->%N` `dv]oAAvC-%N` dxdoXX@vS-%N`z Wx` eo@@vS-<%N` ``*eo@@@vC->%N` `dfoAAvC-%N` dxmoXX@vS-%N` W$x`mo@@vS-<%N`% ``mo@@@vC->%N`C `dnoAAvC-%N`6 dxuoXX@vS-%N`r W 4x` vo@@vS-<%N` ``#vo@@@vC->%N` `d%N`m `doAAvC-%N`l dx1oXX@vS-%N` W@Tx`Co@@vS-<%N` ``_o@@@vC->%N` `dpoAAvC-%N` dxÏoXX@vS-%N`C WPdx`׏o@@vS-<%N`W ``o@@@vC->%N` `doAAvC-%N` dxwoXX@vS-%N` W`tx`o@@vS-<%N` ``o@@@vC->%N`c `dߙoAAvC-%N`_ dxoXX@vS-%N`# Wpx`o@@vS-<%N`# ``o@@@vC->%N`%$ `dȢoAAvC-%N`H% dx oXX@vS-%N`+ Wx`&o@@vS-<%N`+ ``no@@@vC->%N`+ `dcoAAvC-%N`, dxޱoXX@vS-%N`^4 Wx`o@@vS-<%N`o4 ``o@@@vC->%N`4 `d;oAAvC-%N`5 dxoXX@vS-%N`%= Wx`o@@vS-<%N`7= ``o@@@vC->%N`d= `d oAAvC-%N`> dxZoXX@vS-%N`E Wx`qo@@vS-<%N`E ``o@@@vC->%N`F `doAAvC-%N`G dxoXX@vS-%N`tN Wx` o@@vS-<%N`N ``,o@@@vC->%N`N `d=oAAvC-%N`O dxoXX@vS-%N`W Wx`o@@vS-<%N`#W ``o@@@vC->%N`:W `doAAvC-%N`)X dx.oXXMwS-%N`_ Wx`Co@@MwS-<%N`_ ``o@@MwC->%N`` `doAAMwC-%N` a dxoXX@MwS-%N`_h Wx`o@@MwS-<%N`h ``o@@@MwC->%N`h `doAAMwC-%N`i dxoXXMwS-%N`~ WdeBCDEFGx`/o@@MwS-<%N`~ ``yo@@MwC->%N`~ `doAAMwC-%N` dxoXXMwS-%N`> WHIx`o@@MwS-<%N`R ``o@@MwC->%N` `d(oAAMwC-%N` dxx oXX@MwS-%N` W 5x` o@@MwS-<%N` `` o@@@MwC->%N`+ `doAAMwC-%N`5 dxOoXX@MwS-%N`Ϙ W0Ex`po@@MwS-<%N` ``o@@@MwC->%N`( `doAAMwC-%N`$ dxoXX@MwS-%N`? W@Ux`o@@MwS-<%N`T ``o@@@MwC->%N`} `doAAMwC-%N`{ dxK'oXXMwS-%N`˩ WPex`^'o@@MwS-<%N`ީ ``'o@@MwC->%N` `d(oAAMwC-%N`D dx/oXXMwS-%N`9 W`ux`/o@@MwS-<%N`M ``/o@@MwC->%N`n `d1oAAMwC-%N` dxz8oXX@MwS-%N` Wpx`8o@@MwS-<%N` ``8o@@@MwC->%N`> `d9oAAMwC-%N`L dx3AoXXMwS-%N` Wx`GAo@@MwS-<%N` ``gAo@@MwC->%N` `dBoAAMwC-%N` dxIoXXMwS-%N`d Wx`Jo@@MwS-<%N` ``Jo@@MwC->%N` `dKoAAMwC-%N` dxzRoXX@MwS-%N` Wx`Ro@@MwS-<%N`% ``Ro@@@MwC->%N`8 `dSoAAMwC-%N`$ dx-[oXX@MwS-%N` Wx`H[o@@MwS-<%N` ``[o@@@MwC->%N` `dm\oAAMwC-%N` dxcoXX@MwS-%N`c Wx`co@@MwS-<%N`{ ``do@@@MwC->%N` `d0eoAAMwC-%N` dxloXXMwS-%N`= Wx`lo@@MwS-<%N`U ``mo@@MwC->%N` `dnoAAMwC-%N` d`9uo@@MwS-<%N` `xyoXXMwS-%N` Wx`zo@@MwC->%N` `dzoAAMwC-%N`{ dxoXX@MwS-%N`w Wx` o@@MwS-<%N` ``+o@@@MwC->%N` `d*oAAMwC-%N` dxoXXMwS-%N`1 Wx`Ċo@@MwS-<%N`D ``o@@MwC->%N`g `d܋oAAMwC-%N`\ dx^oXXMwS-%N` W&x`yo@@MwS-<%N` ``o@@MwC->%N` `doAAMwC-%N` dxoXX@MwS-%N`l W 6x`o@@MwS-<%N` ``Bo@@@MwC->%N` `d%N`J' `dåoAAMwC-%N`C( dxAoXX@MwS-%N`/ W@Vx`To@@MwS-<%N`/ ``o@@@MwC->%N` 0 `doAAMwC-%N`(1 dxoXXMwS-%N`p8 WPfx` o@@MwS-<%N`8 ``=o@@MwC->%N`8 `dmoAAMwC-%N`9 dxoXXMwS-%N`>A W`vx`Ҿo@@MwS-<%N`RA ``Co@@MwC->%N`A `doAAMwC-%N`B dxXoXX@MwS-%N`I Wpx`uo@@MwS-<%N`I ``o@@@MwC->%N`6J `doAAMwC-%N`2K dxoXXMwS-%N`}R Wx`o@@MwS-<%N`R ``/o@@MwC->%N`R `doAAMwC-%N`S dxoXXMwS-%N`;[ Wx`o@@MwS-<%N`N[ ``o@@MwC->%N`[ `d oAAMwC-%N`\ dxvoXX@MwS-%N`c Wx`o@@MwS-<%N`d ``o@@@MwC->%N`Ud `doAAMwC-%N`}e dx,oXX@MwS-%N`l Wx`@o@@MwS-<%N`l ``]o@@@MwC->%N`l `dEoAAMwC-%N`m dxoXX@MwS-%N`=u Wx`o@@MwS-<%N`Wu ``o@@@MwC->%N`u `d)oAAMwC-%N`v dxoXXMwS-%N` ~ Wx`o@@MwS-<%N`~ ``o@@MwC->%N``~ `doAAMwC-%N`@ dxoXXMwS-%N` Wx`o@@MwS-<%N` ``2o@@MwC->%N` `dToAAMwC-%N`ԇ dx oXX@MwS-%N`? Wx` o@@MwS-<%N`W `` o@@@MwC->%N`x `d oAAMwC-%N`x dxPoXXMwS-%N`З W35678:;<x`_o@@MwS-<%N`ߗ ``o@@MwC->%N`$ `doAAMwC-%N`L dxoXXMwS-%N` W=YZ[\]^x`o@@MwS-<%N` ``>o@@MwC->%N` `dBoAAMwC-%N`¡ dx&oXX@MwS-%N`] W 7x`&o@@MwS-<%N`| ``'o@@@MwC->%N` `d'oAAMwC-%N`~ dx/oXX@MwS-%N` W0Gx`/o@@MwS-<%N`9 ``/o@@@MwC->%N`{ `d1oAAMwC-%N` dxK8oXX@MwS-%N`˺ W@Wx`\8o@@MwS-<%N`ܺ ``8o@@@MwC->%N` `d9oAAMwC-%N` dx@oXXMwS-%N`[ WPgx`@o@@MwS-<%N`s ``5Ao@@MwC->%N` `dIBoAAMwC-%N` dxIoXXMwS-%N`% W`wx`Io@@MwS-<%N`8 ``Jo@@MwC->%N` `dJoAAMwC-%N`d dxiRoXX@MwS-%N` Wpx`xRo@@MwS-<%N` ``So@@@MwC->%N` `dSoAAMwC-%N`V dxZoXXMwS-%N`X Wx`Zo@@MwS-<%N`g ``[o@@MwC->%N` `d\oAAMwC-%N` dxcoXXMwS-%N` Wx`co@@MwS-<%N` ``Wco@@MwC->%N` `drdoAAMwC-%N` dxkoXX@MwS-%N` Wx`ko@@MwS-<%N`# ``ko@@@MwC->%N`h `dloAAMwC-%N`i dxUtoXX@MwS-%N` Wx`cto@@MwS-<%N` ``to@@@MwC->%N`' `duoAAMwC-%N`& dx|oXX@MwS-%N`d Wx`|o@@MwS-<%N`u ``7}o@@@MwC->%N` `dH~oAAMwC-%N` dx|oXXMwS-%N` Wx`o@@MwS-<%N`  ``o@@MwC->%N` `doAAMwC-%N` dx%N` `doAAMwC-%N` dxoXX@MwS-%N`z Wx`o@@MwS-<%N` ``Jo@@@MwC->%N` `dPoAAMwC-%N` dxoXXMwS-%N`|/ Wx` o@@MwS-<%N`/ ``Mo@@MwC->%N`/ `daoAAMwC-%N`0 dxoXXMwS-%N`98 W(x`Ƶo@@MwS-<%N`F8 `` o@@MwC->%N`8 `doAAMwC-%N`9 dxloXX@MwS-%N`@ W 8x`yo@@MwS-<%N`@ ``o@@@MwC->%N`:A `doAAMwC-%N`>B dx/oXX@MwS-%N`I W0Hx`%N`I `doAAMwC-%N`K dxoXX@MwS-%N`pR W@Xx`o@@MwS-<%N`}R ``Do@@@MwC->%N`R `dMoAAMwC-%N`S dxoXXMwS-%N`[ WPhx`o@@MwS-<%N` [ ``o@@MwC->%N`P[ `doAAMwC-%N`\ dx0oXXMwS-%N`c W`xx`@o@@MwS-<%N`c ``o@@MwC->%N`d `d|oAAMwC-%N`d dxoXX@MwS-%N`pl Wpx`o@@MwS-<%N`}l ``?o@@@MwC->%N`l `dWoAAMwC-%N`m dxoXXMwS-%N`%u Wx`o@@MwS-<%N`1u ``o@@MwC->%N`u `duoAAMwC-%N`v dxWoXXMwS-%N`} Wx`eo@@MwS-<%N`} ``o@@MwC->%N`%~ `doAAMwC-%N`) dxBoXX@MwS-%N`† Wx`No@@MwS-<%N`Ά ``o@@@MwC->%N` `doAAMwC-%N` dx oXX@MwS-%N`~ Wx` o@@MwS-<%N` ``\ o@@@MwC->%N`܏ `dboAAMwC-%N` dxoXX@MwS-%N`: Wx`o@@MwS-<%N`G ``o@@@MwC->%N` `dEoAAMwC-%N`ř dx{oXXMwS-%N` Wx`o@@MwS-<%N` ``o@@MwC->%N`K `doAAMwC-%N`? dx9'oXXMwS-%N` Wx`E'o@@MwS-<%N`ũ ``'o@@MwC->%N` `d(oAAMwC-%N` dx/oXX@MwS-%N`s Wx`/o@@MwS-<%N` ``@0o@@@MwC->%N` `dLoAAMwC-%N` dxQoXXMwS-%N`Z WbdeBCDEFx`Qo@@MwS-<%N`h ``'Ro@@MwC->%N` `dSoAAMwC-%N` dxZoXXMwS-%N` WGHIx`Zo@@MwS-<%N` ``Zo@@MwC->%N`[ `d[oAAMwC-%N`f dxPcoXX@MwS-%N` W 9x`\co@@MwS-<%N` ``co@@@MwC->%N` `ddoAAMwC-%N`. dx loXX@MwS-%N` W0Ix`lo@@MwS-<%N` ``glo@@@MwC->%N` `d~moAAMwC-%N` dxtoXX@MwS-%N` W@Yx`to@@MwS-<%N` ``to@@@MwC->%N`a `duoAAMwC-%N`` dxK}oXXMwS-%N` WPix`W}o@@MwS-<%N` ``}o@@MwC->%N` `d~oAAMwC-%N`$ dxoXXMwS-%N` W`yx`o@@MwS-<%N` ``Ro@@MwC->%N` `dpoAAMwC-%N` dxoXX@MwS-%N`8 Wpx`Îo@@MwS-<%N`C ``o@@@MwC->%N` `doAAMwC-%N` dxooXXMwS-%N` Wx`|o@@MwS-<%N` ``o@@MwC->%N`f `d˜oAAMwC-%N`B dxoXXMwS-%N`v" Wx`o@@MwS-<%N`" ``Co@@MwC->%N`" `dOoAAMwC-%N`# dxqoXX@MwS-%N`* Wx`}o@@MwS-<%N`* ``o@@@MwC->%N`+ `doAAMwC-%N`, dxoXX@MwS-%N`3 Wx`!o@@MwS-<%N`3 ``do@@@MwC->%N`3 `d[oAAMwC-%N`4 dx۹oXX8yS-%N`[< Wx`o@@8yS-<%N`k< ``+o@@8yC->%N`< `ddoAA8yC-%N`= dxkoXX8yS-%N`D Wx`o@@8yS-<%N`E ``o@@8yC->%N`qE `doAA8yC-%N`mF dx*oXX8yS-%N`M Wx`:o@@@8yS-<%N`M ``wo@@8yC->%N`M `dBoAA@8yC-%N`N d`o@@8yS-<%N`gV `x.oXX8yS-%N`Z W x`}o@@8yC->%N`Z `doAA8yC-%N`\ dxoXX8yS-%N`pc Wx`o@@8yS-<%N`c ``Jo@@8yC->%N`c `dboAA8yC-%N`d dxloXX@8yS-%N`k W*x`o@@@8yS-<%N`l ``o@@@8yC->%N`>l `doAA@8yC-%N`[m dxoXX@8yS-%N`wt W :x`o@@@8yS-<%N`t ``Oo@@@8yC->%N`t `deoAA@8yC-%N`u dxoXX8yS-%N`F} W0Jx`o@@8yS-<%N`W} ``!o@@8yC->%N`} `doAA8yC-%N`~ dxoXX8yS-%N` W@Zx`o@@8yS-<%N` ``o@@8yC->%N`f `d?oAA8yC-%N` dxQ oXX@MwS-%N`ю WPjx`c o@@MwS-<%N` `` o@@@MwC->%N`% `d oAAMwC-%N`D dxoXXMwS-%N`; W`zx`o@@MwS-<%N`I ``o@@MwC->%N` `doAAMwC-%N` dxoXXMwS-%N` Wpx`+o@@MwS-<%N` ``no@@MwC->%N` `dyoAAMwC-%N` dx%oXX@MwS-%N`W Wx`%o@@MwS-<%N`d ``$&o@@@MwC->%N` `d9'oAAMwC-%N` dx.oXX@MwS-%N` Wx`.o@@MwS-<%N`# ``.o@@@MwC->%N`f `d30oAAMwC-%N` dxU7oXX@MwS-%N`չ Wx`b7o@@MwS-<%N` ``7o@@@MwC->%N`# `d8oAAMwC-%N`8 dx?oXXMwS-%N`^ Wx`?o@@MwS-<%N`k ``1@o@@MwC->%N` `d'AoAAMwC-%N` dx{HoXX@8yS-%N` Wx`Ho@@8yS-<%N` ``Ho@@@8yC->%N`T `dIoAA8yC-%N`e dxPoXX@MwS-%N`K Wx`Po@@MwS-<%N`X ``Qo@@@MwC->%N` `d,RoAAMwC-%N` dxYoXX@MwS-%N` Wx`Yo@@MwS-<%N` ``Yo@@@MwC->%N`I `dZoAAMwC-%N`I dx6boXX@MwS-%N` W x`Cbo@@@MwS-<%N` ``bo@@@MwC->%N` `dcoAA@MwC-%N` dxjoXXMwS-%N`x Whmlkjix`ko@@MwS-<%N` ``Hko@@MwC->%N` `dIloAAMwC-%N` dxsoXX8yS-%N`! W+x`so@@8yS-<%N`= ``so@@8yC->%N`c `dtoAA8yC-%N`~ dxb|oXX8yS-%N` W ;x`s|o@@8yS-<%N` ``|o@@8yC->%N`u `d}oAA8yC-%N`@dx oXX8yS-%N`W0Kx`9o@@@8yS-<%N```mo@@8yC->%N``doAA@8yC-%N` dxQoXX8yS-%N`W@[x`co@@8yS-<%N```o@@8yC->%N`0`doAA8yC-%N`5dxڕoXX8yS-%N`ZWPkx`o@@8yS-<%N`j``4o@@8yC->%N``dAoAA8yC-%N`dxoXX@8yS-%N`!W`{x`o@@@8yS-<%N`!``ޞo@@@8yC->%N`^!`dܟoAA@8yC-%N`\"dxFoXX@8yS-%N`)Wpx`Vo@@@8yS-<%N`)``o@@@8yC->%N`*`doAA@8yC-%N`%+dxЯoXX8yS-%N`P2Wx`o@@8yS-<%N`f2``,o@@8yC->%N`2`d!oAA8yC-%N`3dxoXX8yS-%N`;Wx`o@@8yS-<%N` ;`` o@@8yC->%N`;`doAA8yC-%N`q<dxoXX@8yS-%N`CWx`+o@@8yS-<%N`C``zo@@@8yC->%N`C`doAA8yC-%N`DdxoXX8yS-%N`&LWx`o@@8yS-<%N`5L``o@@8yC->%N`L`doAA8yC-%N`MdxGoXX8yS-%N`TWx`_o@@8yS-<%N`T``o@@8yC->%N`%U`doAA8yC-%N`VdxoXX8yS-%N`]Wx`o@@@8yS-<%N`]``jo@@8yC->%N`]`doAA@8yC-%N`_dxoXXzS-%N`ffWx`o@@zS-<%N`vf``o@@zC->%N`f`dboAAzC-%N`gdxoXXzS-%N`oW x`o@@zS-<%N`*o``o@@zC->%N`o`doAAzC-%N`kpdxZoXXzS-%N`wWx`go@@zS-<%N`w``o@@zC->%N`=x`doAAzC-%N`&ydxoXXzS-%N`YW,x`o@@zS-<%N`i``2o@@zC->%N``d8oAAzC-%N`dx{oXXzS-%N`W <x`o@@zS-<%N```o@@zC->%N`Z`doAAzC-%N`LdxoXXzS-%N`W0Lx` o@@zS-<%N```bo@@zC->%N``dPoAAzC-%N`ВdxoXXzS-%N`W@\x`o@@zS-<%N`"``o@@zC->%N`q`doAAzC-%N`edx@ oXXzS-%N`WPlx`T o@@zS-<%N`Ԣ``x o@@zC->%N``d!oAAzC-%N` dx(oXXzS-%N`yW`|x`)o@@zS-<%N```()o@@zC->%N``dn*oAAzC-%N`dx1oXXzS-%N`*Wpx`1o@@zS-<%N`9``2o@@zC->%N``d3oAAzC-%N`dx3:oXXzS-%N`Wx`A:o@@zS-<%N```c:o@@zC->%N``d;oAAzC-%N`dxBoXXzS-%N`rWx`Bo@@zS-<%N`~``TCo@@zC->%N``d@DoAAzC-%N`dxKoXXzS-%N`Wx`Ko@@zS-<%N```Ko@@zC->%N`@`dLoAAzC-%N`Xdx:ToXXzS-%N`Wx`HTo@@@zS-<%N```To@@zC->%N`!`dUoAA@zC-%N`.dx]oXXzS-%N`Wx` ]o@@zS-<%N````]o@@zC->%N``dy^oAAzC-%N`dxeoXXzS-%N`Wx`eo@@zS-<%N```eo@@zC->%N`u`dfoAAzC-%N`Hdx>noXXmzS-%N`Wx`Kno@@@mzS-<%N```no@@mzC->%N``dooAA@mzC-%N`dxvoXXmzS-%N`VW x`vo@@mzS-<%N`b``#wo@@mzC->%N``dQxoAAmzC-%N`dxoXXmzS-%N`Wghmlkjix`o@@mzS-<%N`"``o@@mzC->%N`g`doAAmzC-%N`vdxoXX@mzS-%N`k W-x`o@@mzS-<%N`{ ``1o@@@mzC->%N` `dfoAAmzC-%N` dxoXXmzS-%N`,W =x`o@@mzS-<%N`9``o@@mzC->%N`}`doAAmzC-%N`dxmoXXmzS-%N`W0Mx`yo@@@mzS-<%N```o@@mzC->%N`:`dҚoAA@mzC-%N`RdxoXXmzS-%N`$W@]x`o@@mzS-<%N`$``Zo@@mzC->%N`$`droAAmzC-%N`%dxʪoXXmzS-%N`J-WPmx`֪o@@mzS-<%N`V-``1o@@mzC->%N`-`d oAAmzC-%N`.dxoXX@mzS-%N`6W`}x`o@@mzS-<%N`6``o@@@mzC->%N`e6`d۴oAAmzC-%N`[7dxoXXmzS-%N`>Wpx`,o@@mzS-<%N`>``~o@@mzC->%N`>`duoAAmzC-%N`?dxoXXmzS-%N`Wx`o@@@mzS-<%N`2``1o@@mzC->%N`q`d0oAA@mzC-%N`pdxoXX@mzS-%N` Wx`o@@mzS-<%N` ``o@@@mzC->%N``doAAmzC-%N`:d`o@@mzS-<%N`S`x|oXXmzS-%N`Wx`o@@mzC->%N``doAAmzC-%N`"d`Ko@@mzS-<%N`#`xoXX@mzS-%N`'Wx`o@@@mzC->%N`(`doAAmzC-%N`;)dx}oXXmzS-%N`0Wx`o@@mzS-<%N`0``o@@mzC->%N`0`doAAmzC-%N`2dxoXXzS-%N`G9Wx`o@@zS-<%N`Y9``;o@@zC->%N`{9`dmoAAzC-%N`:dxoXX@zS-%N`BWx`o@@zS-<%N`,B`` o@@@zC->%N`JB`d+oAAzC-%N`kCdxx oXXzS-%N`JW x` o@@zS-<%N`J`` o@@zC->%N`J`d oAAzC-%N`$Ldx=oXXmzS-%N`}SWx`Mo@@mzS-<%N`S``mo@@mzC->%N`S`doAAmzC-%N`TdxoXXmzS-%N` \W.x`o@@@mzS-<%N`\``o@@mzC->%N`B\`d8oAA@mzC-%N`x]dxb$oXXmzS-%N`dW >x`q$o@@mzS-<%N`d``$o@@mzC->%N`d`d%oAAmzC-%N`edx,oXXmzS-%N`mW0Nx`,o@@@mzS-<%N`"m``-o@@mzC->%N`Em`d.oAA@mzC-%N`]ndx\5oXX@mzS-%N`uW@^x`j5o@@mzS-<%N`u``5o@@@mzC->%N`u`d6oAAmzC-%N`vdx">oXXmzS-%N`b~WPnx`3>o@@mzS-<%N`s~``[>o@@mzC->%N`~`d?oAAmzC-%N`dxSoXXzS-%N`W`~x`So@@zS-<%N`$``To@@zC->%N`F`d9UoAAzC-%N`ydxa\oXXzS-%N`Wpx`s\o@@zS-<%N```\o@@zC->%N`М`d]oAAzC-%N`dx!eoXXzS-%N`aWx`3eo@@zS-<%N`s``Peo@@zC->%N``dfoAAzC-%N`dxmoXXzS-%N`Wx`mo@@zS-<%N`1``no@@zC->%N`O`dBooAAzC-%N`dxsvoXXzS-%N`Wx`vo@@zS-<%N`̶``vo@@zC->%N`5`dwoAAzC-%N`dxoXX@zS-%N`AWx`o@@zS-<%N`R``0o@@@zC->%N`p`dgoAAzC-%N`dxoXXzS-%N`Wx`Ƈo@@zS-<%N```ۇo@@zC->%N``doAAzC-%N`EdxooXXzS-%N`Wx`o@@zS-<%N```o@@zC->%N``doAAzC-%N`dx.oXX@zS-%N`nWx`@o@@zS-<%N```]o@@@zC->%N``doAAzC-%N`dxoXXzS-%N` Wx`o@@zS-<%N`5``o@@zC->%N`P`dIoAAzC-%N`dxoXX@zS-%N`Wrstuvwxyx`o@@zS-<%N```ªo@@@zC->%N``doAAzC-%N`%dx*oXXzS-%N`jWz{|}~+x`;o@@zS-<%N`{``Wo@@zC->%N``d´oAAzC-%N`dxoXXzS-%N`W ?x`ϻo@@zS-<%N```o@@zC->%N`/`d.oAAzC-%N`ndx|oXX@zS-%N`W0Ox`o@@zS-<%N```o@@@zC->%N``doAAzC-%N`dxoXXzS-%N`T W@_x`+o@@zS-<%N`k ``Ho@@zC->%N` `d[oAAzC-%N`dxoXX@mzS-%N`1WPox`o@@mzS-<%N`E``o@@@mzC->%N` `dqoAAmzC-%N`dxoXXmzS-%N`W`x`o@@mzS-<%N` ``o@@mzC->%N`=`d8oAAmzC-%N`x dxeoXXmzS-%N`'Wpx`vo@@mzS-<%N`'``o@@mzC->%N`'`doAAmzC-%N`(dx oXXmzS-%N`I0Wx`o@@@mzS-<%N`X0``\o@@mzC->%N`0`doAA@mzC-%N`1dxoXXmzS-%N` 9Wx`o@@mzS-<%N`9``/o@@mzC->%N`o9`doAAmzC-%N`&:dxoXXmzS-%N`AWx`o@@@mzS-<%N`A``o@@mzC->%N` B`doAA@mzC-%N` Cdx oXX@mzS-%N`JWx` o@@mzS-<%N`J`` o@@@mzC->%N`HJ`dL oAAmzC-%N`KdxRoXXmzS-%N`RWx`_o@@mzS-<%N`R``o@@mzC->%N`R`doAAmzC-%N`Sdx oXX@mzS-%N`M[Wx`o@@mzS-<%N`Z[``Go@@@mzC->%N`[`d%oAAmzC-%N`e\dx#oXXmzS-%N`cWx`#o@@mzS-<%N`c``#o@@mzC->%N`d`d%oAAmzC-%N`Fedxn,oXXmzS-%N`lWx`z,o@@mzS-<%N`l``,o@@mzC->%N`l`d-oAAmzC-%N`ndx,5oXXmzS-%N`luW x`85o@@@mzS-<%N`xu``a5o@@mzC->%N`u`d6oAA@mzC-%N`vdx=oXXmzS-%N`}W0x`=o@@mzS-<%N`}``=o@@mzC->%N` ~`d>oAAmzC-%N` dxFoXXmzS-%N`BW @x`Fo@@@mzS-<%N`N``FFo@@mzC->%N``dSGoAA@mzC-%N`dxNoXX@mzS-%N`W0Px`No@@mzS-<%N```No@@@mzC->%N`#`dPoAAmzC-%N`GdxWoXXmzS-%N`XW@`x`%Wo@@mzS-<%N`e``IWo@@mzC->%N``dkXoAAmzC-%N`dxo_oXX@mzS-%N`WPpx`|_o@@mzS-<%N```_o@@@mzC->%N``d`oAAmzC-%N`dxgoXXmzS-%N`;W`x`ho@@mzS-<%N`D``'ho@@mzC->%N`g`dQioAAmzC-%N`dx[poXXmzS-%N`Wpx`spo@@mzS-<%N```po@@mzC->%N`˰`dqoAAmzC-%N`dx"yoXXmzS-%N`bWx`Cyo@@@mzS-<%N```yo@@mzC->%N`ù`dzoAA@mzC-%N`údxoXXmzS-%N`Wx`o@@mzS-<%N```o@@mzC->%N`"`d oAAmzC-%N`Jdx1oXXmzS-%N`qWx`=o@@@mzS-<%N`}``ho@@mzC->%N``doAA@mzC-%N`dxoXX@mzS-%N`)Wx`o@@mzS-<%N`5``$o@@@mzC->%N`d`d@oAAmzC-%N`dxoXXmzS-%N`Wx`o@@mzS-<%N```o@@mzC->%N``d oAAmzC-%N`Jdx>oXX@mzS-%N`~Wx`Lo@@mzS-<%N```o@@@mzC->%N``dƳoAAmzC-%N`dxʺoXXmzS-%N` Wx`ֺo@@mzS-<%N```o@@mzC->%N`E`doAAmzC-%N`\dxjoXXmzS-%N`Wx`wo@@mzS-<%N```o@@mzC->%N``doAAmzC-%N`; dx-oXXmzS-%N`m%W rstuvwxyx`;o@@@mzS-<%N`{%``o@@mzC->%N`&`dLoAA@mzC-%N`&dxoXXmzS-%N`-W z{|}~-x`o@@mzS-<%N`-``o@@mzC->%N`.`doAAmzC-%N` /dxPoXXmzS-%N`6W Ax`]o@@@mzS-<%N`6``|o@@mzC->%N`6`dioAA@mzC-%N`7dxS oXX@mzS-%N`MW 0Qx`b o@@mzS-<%N`M`` o@@@mzC->%N`M`dqoAAmzC-%N`NdxoXXmzS-%N`"VW @ax`o@@mzS-<%N`0V``o@@mzC->%N`OV`doAAmzC-%N`UWdxpoXX@mzS-%N`^W Pqx`~o@@mzS-<%N`^``o@@@mzC->%N`^`doAAmzC-%N``dx&oXXmzS-%N`3gW `x`'o@@mzS-<%N`Qg`` 'o@@mzC->%N``g`d<(oAAmzC-%N`|hdxm/oXX8yS-%N`oW px`/o@@8yS-<%N`o``/o@@8yC->%N`o`d0oAA8yC-%N`pd`8o@@8yS-<%N`Dx`x[%N`|`d=oAA8yC-%N`~dxDoXX@8yS-%N`<W x` Eo@@@8yS-<%N`M``/Eo@@@8yC->%N`o`dFoAA@8yC-%N`[dxMoXX@8yS-%N`W x`Mo@@@8yS-<%N```Mo@@@8yC->%N``d3OoAA@8yC-%N`sdxXVoXX8yS-%N`W x`eVo@@8yS-<%N```Vo@@8yC->%N`ܖ`dWoAA8yC-%N`dx_oXX8yS-%N`WW x`%_o@@8yS-<%N`e``J_o@@8yC->%N``d`oAA8yC-%N`ɠdxgoXX@8yS-%N`W x`go@@8yS-<%N```go@@@8yC->%N`)`dioAA8yC-%N`AdxmpoXX8yS-%N`W x`ypo@@8yS-<%N```po@@8yC->%N`ް`d roAA8yC-%N`IdxoXX8yS-%N`NW x`o@@8yS-<%N`^``Ho@@8yC->%N``dtoAA8yC-%N`dx̎oXX8yS-%N` W!"x`؎o@@@8yS-<%N```o@@8yC->%N`<`d+oAA@8yC-%N`kdxoXX8yS-%N`W!2x`o@@8yS-<%N```ėo@@8yC->%N``d$oAA8yC-%N`ddxfoXXmzS-%N`W! Bx`to@@@mzS-<%N```o@@mzC->%N``doAA@mzC-%N`dxܨoXXmzS-%N`W!0Rx`o@@mzS-<%N`(``o@@mzC->%N`R`d&oAAmzC-%N`fdxoXXmzS-%N`W!@bx`o@@mzS-<%N```ѱo@@mzC->%N``doAAmzC-%N` dxfoXXmzS-%N`W!Prx`so@@mzS-<%N```o@@mzC->%N``doAAmzC-%N`dxoXXmzS-%N`DW!`x`o@@mzS-<%N`V``Do@@mzC->%N``ddoAAmzC-%N`dxoXX@mzS-%N` W!px`o@@mzS-<%N` ``o@@@mzC->%N`) `doAAmzC-%N`: dxvoXXmzS-%N`W!x`o@@@mzS-<%N```o@@mzC->%N``doAA@mzC-%N`dxoXXmzS-%N`NW!x`!o@@mzS-<%N`a``;o@@mzC->%N`{`d:oAAmzC-%N`zdxoXXmzS-%N`&W!x`o@@mzS-<%N`&``o@@mzC->%N`5&`d oAAmzC-%N`M'dxvoXXmzS-%N`.W!x`o@@mzS-<%N`.``o@@mzC->%N`.`doAAmzC-%N`/dxoXXmzS-%N`;7W!x`-o@@mzS-<%N`m7``ho@@mzC->%N`7`dAoAAmzC-%N`8dx|oXX@mzS-%N`?W!x`o@@mzS-<%N`?``o@@@mzC->%N`?`doAAmzC-%N`@dx#oXXmzS-%N`cHW!x`?o@@@mzS-<%N`H``Zo@@mzC->%N`H`dX oAA@mzC-%N`IdxoXXmzS-%N`QW!x`o@@mzS-<%N`3Q``Ao@@mzC->%N`Q`dKoAAmzC-%N`RdxoXXmzS-%N`YWDC3x`o@@mzS-<%N`Y``o@@mzC->%N`-Z`dFoAAmzC-%N`ӆdxLoXXmzS-%N`CW%x`Lo@@mzS-<%N`S``=Lo@@mzC->%N`}`dxMoAAmzC-%N`dxToXXmzS-%N`W 3x`To@@mzS-<%N```Uo@@mzC->%N`G`dnVoAAmzC-%N`dxQ]oXX@mzS-%N`W0Ex`_]o@@mzS-<%N```]o@@@mzC->%N`ɝ`d^oAAmzC-%N`ޞdxfoXX8yS-%N`\W@Sx`*fo@@8yS-<%N`j``Tfo@@8yC->%N``d}goAA8yC-%N`dxnoXX@8yS-%N` WPex`no@@@8yS-<%N`1``-oo@@@8yC->%N`m`dKpoAA@8yC-%N`dxwoXX@8yS-%N`W`sx`wo@@@8yS-<%N```wo@@@8yC->%N`$`dyoAA@8yC-%N`SdxoXX@mzS-%N`Wpx`o@@mzS-<%N```o@@@mzC->%N`=`d oAAmzC-%N`IdxdoXX@mzS-%N`W Dx`wo@@mzS-<%N```ljo@@@mzC->%N``dΐoAAmzC-%N`dxoXXmzS-%N`W Ex`Εo@@mzS-<%N```o@@mzC->%N`S`d=oAAmzC-%N`}dxuoXXmzS-%N`W x`o@@mzS-<%N```ٞo@@mzC->%N``doAAmzC-%N`dxoXXmzS-%N`W05x`o@@mzS-<%N```o@@mzC->%N`7`doAAmzC-%N`dxoXXmzS-%N`UW0Ex`&o@@mzS-<%N`f``ro@@mzC->%N``dwoAAmzC-%N`dxoXXmzS-%N` W0 Ux`o@@@mzS-<%N`) ``3o@@mzC->%N`s `d4oAA@mzC-%N`t dxjoXXmzS-%N`W00ex`xo@@@mzS-<%N```o@@mzC->%N``doAA@mzC-%N`dx4oXX@mzS-%N`tW0@x`Bo@@mzS-<%N```lo@@@mzC->%N``doAAmzC-%N`dxoXX@mzS-%N`4%W0Px`o@@mzS-<%N`C%``+o@@@mzC->%N`k%`d:oAAmzC-%N`z&dxoXXmzS-%N`-W0`x`o@@mzS-<%N`-``o@@mzC->%N`#.`doAAmzC-%N`X/dxkoXXmzS-%N`6W0px`xo@@mzS-<%N`6``o@@mzC->%N`6`doAAmzC-%N` 8dx0oXXmzS-%N`p?WKx`=o@@mzS-<%N`}?``go@@mzC->%N`?`d'oAAmzC-%N`"hdx0oXXmzS-%N`pWWx`0o@@mzS-<%N`q``0o@@mzC->%N`*q`d2oAAmzC-%N`BrdxuBoXXmzS-%N`W JK_dBnHx`Bo@@@mzS-<%N```Bo@@mzC->%N` `dCoAA@mzC-%N`%dx"KoXXmzS-%N`bW09x`2Ko@@@mzS-<%N`r``QKo@@mzC->%N``dfLoAA@mzC-%N`dxSoXX@mzS-%N`5W@Ix`To@@mzS-<%N`D``LTo@@@mzC->%N``dAUoAAmzC-%N`dx\oXX@mzS-%N`WPYx`\o@@mzS-<%N``` ]o@@@mzC->%N`M`dB^oAAmzC-%N`dxweoXXmzS-%N`W`ix`eo@@mzS-<%N`ǥ``eo@@mzC->%N``dfoAAmzC-%N` dxnoXXmzS-%N`QWpyx`$no@@mzS-<%N`d```no@@mzC->%N``dPooAAmzC-%N`dxvoXXmzS-%N`ضWx`vo@@mzS-<%N```vo@@mzC->%N`3`dwoAAmzC-%N`dxoXXmzS-%N`˿WBx`o@@mzS-<%N```o@@mzC->%N`/`doAAmzC-%N`\dxȋoXXmzS-%N`WerBrax`܋o@@@mzS-<%N```"o@@mzC->%N`b`d7oAA@mzC-%N`wdxKoXXmzS-%N`W "x`\o@@@mzS-<%N```}o@@mzC->%N``doAA@mzC-%N`dxoXX@mzS-%N`.W0&*2x`o@@mzS-<%N`?``Ho@@@mzC->%N``dSoAAmzC-%N`dxoXX@mzS-%N`W@Kx`ȥo@@mzS-<%N```o@@@mzC->%N`Q`doAAmzC-%N`*dxĭoXXmzS-%N`WP[x`խo@@mzS-<%N```o@@mzC->%N`Y`d oAAmzC-%N``dxoXXmzS-%N`W`kx`o@@mzS-<%N```o@@mzC->%N`,`doAAmzC-%N`_dxVoXXmzS-%N`Wp{x`go@@mzS-<%N```o@@mzC->%N``doAAmzC-%N`dxoXXmzS-%N`%Wx`o@@mzS-<%N`:``?o@@mzC->%N``d/oAAmzC-%N`o d`o@@mzS-<%N``xkoXX@mzS-%N`Wdvx`o@@@mzC->%N``d^oAAmzC-%N`Bdx\ oXXmzS-%N`KWdx`r o@@@mzS-<%N`K`` o@@mzC->%N`K`d oAA@mzC-%N`Mdx0oXX@mzS-%N`pTWd x`Fo@@mzS-<%N`T``|o@@@mzC->%N`T`doAAmzC-%N` VdxoXXmzS-%N`A]Wd0x`o@@@mzS-<%N`U]``_o@@mzC->%N`]`d]oAA@mzC-%N`^dx%oXXmzS-%N`eWd@x`%o@@mzS-<%N`e``%o@@mzC->%N`/f`d'oAAmzC-%N`Fgdx*.oXXmzS-%N`jnWdrx`?.o@@mzS-<%N`n``].o@@mzC->%N`n`d4oAAmzC-%N`*udxC;oXX8yS-%N`{Wd}x`W;o@@8yS-<%N`{``u;o@@8yC->%N`{`di%N`*`dDoAA8yC-%N`/dx$LoXX%N``djMoAA%N``dUoAAmzC-%N` dx]oXXmzS-%N`GWdPx`$]o@@mzS-<%N`d``=]o@@mzC->%N`}`d2^oAAmzC-%N`rdxeoXX@mzS-%N`Wd`x`eo@@mzS-<%N`ץ``eo@@@mzC->%N``dfoAAmzC-%N`dxmoXX%N`a`d2ooAA%N``dwoAA@mzC-%N`ڷd`~o@@@mzS-<%N``xoXXmzS-%N`WHello!;x`o@@mzC->%N``dDoAA@mzC-%N`dxoXX@mzS-%N`W6x`ͼo@@mzS-<%N` ``o@@@mzC->%N`D`d oAAmzC-%N``dxoXXmzS-%N`^W Fx`)o@@@mzS-<%N`i``lo@@mzC->%N``duoAA@mzC-%N`dxoXX@mzS-%N`W0Vx`o@@mzS-<%N`#``o@@@mzC->%N`B`doAAmzC-%N`EdxyoXXMwS-%N`W@fx`o@@MwS-<%N```o@@MwC->%N``doAAMwC-%N`dx1oXXMwS-%N`qWPvx`;o@@MwS-<%N`{``yo@@MwC->%N``dqoAAMwC-%N` dxoXXMwS-%N`.(W`x`o@@@MwS-<%N`9(``6o@@MwC->%N`v(`dXoAA@MwC-%N`)dxsoXX@MwS-%N`0Wpx`~o@@MwS-<%N`0``o@@@MwC->%N`0`doAAMwC-%N`2dxoXX@MwS-%N`9Wx`o@@MwS-<%N`'9``$o@@@MwC->%N`d9`doAAMwC-%N`U:dxoXXMwS-%N`AWx`o@@@MwS-<%N`A``o@@MwC->%N` B`doAA@MwC-%N`Cdx< oXX@MwS-%N`|JWx`H o@@MwS-<%N`J`` o@@@MwC->%N`J`d oAAMwC-%N`KdxoXXMwS-%N`6SWx`o@@@MwS-<%N`@S``=o@@MwC->%N`}S`dVoAA@MwC-%N`TdxoXX@MwS-%N`[Wx`o@@MwS-<%N`[``o@@@MwC->%N`3\`doAAMwC-%N`C]dxS$oXXMwS-%N`dWWelcome!x`]$o@@@MwS-<%N`d``$o@@MwC->%N`d`d`,oAA@MwC-%N`ldxc1oXXmzS-%N`qW7x`p1o@@mzS-<%N`q``1o@@mzC->%N`q`d2oAAmzC-%N` sdx:oXXmzS-%N`\zW Gx`(:o@@mzS-<%N`hz``F:o@@mzC->%N`z`dR;oAAmzC-%N`{dxBoXXmzS-%N`;W0Wx`Co@@mzS-<%N`F``"Co@@mzC->%N`b`dDoAAmzC-%N`SdxLoXXmzS-%N`^W@gx`*Lo@@mzS-<%N`j``FLo@@mzC->%N``dAMoAAmzC-%N`dxhToXXmzS-%N`WPwx`rTo@@mzS-<%N```To@@mzC->%N``dUoAAmzC-%N`dx\oXXmzS-%N`(W`x`\o@@mzS-<%N`0``>]o@@mzC->%N`~`dK^oAAmzC-%N`dxeoXXmzS-%N`¥Wpx`eo@@mzS-<%N`ӥ``eo@@mzC->%N``dfoAAmzC-%N`dx&noXXmzS-%N`fWx`9no@@mzS-<%N`y``tno@@mzC->%N``dnooAAmzC-%N`dxvoXXmzS-%N`#Wx`vo@@mzS-<%N`0``5wo@@mzC->%N`u`dJxoAAmzC-%N`dxoXXmzS-%N`ɿWx`o@@mzS-<%N`ֿ``o@@mzC->%N``d#oAAmzC-%N`cdxHoXXmzS-%N`Wx`Vo@@mzS-<%N```o@@mzC->%N``doAAmzC-%N`dxڐoXXmzS-%N`Wx`o@@mzS-<%N`&``+o@@mzC->%N`k`d/oAAmzC-%N`odxoXXmzS-%N`WThank you!x`o@@mzS-<%N```ۙo@@mzC->%N``doAAmzC-%N`dxoXXmzS-%N`W8x`o@@mzS-<%N```֦o@@mzC->%N``d1oAAmzC-%N`qdx:oXXmzS-%N`zW Hx`Fo@@mzS-<%N```o@@mzC->%N``doAAmzC-%N`dxoXXmzS-%N`+W0Xx`o@@mzS-<%N`7``:o@@mzC->%N`z`dLoAAmzC-%N`dxloXXmzS-%N`W@hx`{o@@mzS-<%N```o@@mzC->%N``doAAmzC-%N`dx'oXXmzS-%N`g WPxx`4o@@mzS-<%N`t ``o@@mzC->%N` `doAAmzC-%N` dxoXXmzS-%N`W`x`o@@mzS-<%N` ``o@@mzC->%N`Q`d)oAAmzC-%N`idxqoXXmzS-%N`Wpx`o@@mzS-<%N```o@@mzC->%N``doAAmzC-%N`dxoXXmzS-%N`1#Wx`o@@mzS-<%N`:#``1o@@mzC->%N`q#`d5oAAmzC-%N`u$dxoXXmzS-%N`+Wx`o@@mzS-<%N`+``o@@mzC->%N`0,`doAAmzC-%N`(-dx[oXXmzS-%N`4Wx`go@@mzS-<%N`4``o@@mzC->%N`4`doAAmzC-%N`5dxoXXmzS-%N`V=Wx`"o@@mzS-<%N`b=``Lo@@mzC->%N`=`doAAmzC-%N`>dxoXXmzS-%N`FWx`o@@mzS-<%N`F``o@@mzC->%N`8F`doAAmzC-%N`CGdxLoXXmzS-%N`NWGood bye!3x`Wo@@mzS-<%N`N``o@@mzC->%N`N`d~oAAmzC-%N`VdxWoXXmzS-%N`[W9x`eo@@mzS-<%N`[``o@@mzC->%N`[`doAAmzC-%N`\dx$oXXmzS-%N`YdW Ix`&$o@@mzS-<%N`fd``O$o@@mzC->%N`d`d%oAAmzC-%N`edx,oXXmzS-%N`#mW0Yx`,o@@mzS-<%N`/m``-o@@mzC->%N`\m`dI.oAAmzC-%N`ndx5oXXmzS-%N`uW@ix`5o@@mzS-<%N`u``5o@@mzC->%N`v`d6oAAmzC-%N`$wdx[>oXXmzS-%N`~WPyx`f>o@@mzS-<%N`~``>o@@mzC->%N`~`d?oAAmzC-%N`dx%GoXXmzS-%N`eW`x`1Go@@mzS-<%N`q``\Go@@mzC->%N``dHoAAmzC-%N`ňdxOoXXmzS-%N`#Wpx`Oo@@mzS-<%N`/``Po@@mzC->%N`Z`d+QoAAmzC-%N`kdxhXoXXmzS-%N`Wx`tXo@@mzS-<%N```Xo@@mzC->%N`ט`dYoAAmzC-%N`dx,aoXXzS-%N`lWx`?ao@@zS-<%N```cao@@zC->%N``dvboAAzC-%N`dxioXXzS-%N`Wx`io@@zS-<%N`$``7jo@@zC->%N`w`dkoAAzC-%N`ȫdxroXX@zS-%N`Wx`ro@@zS-<%N```ro@@@zC->%N``dtoAAzC-%N`NdxA{oXXzS-%N`Wx`O{o@@zS-<%N```s{o@@zC->%N``d|oAAzC-%N`dxoXX@zS-%N`4WHappy every day!x`o@@zS-<%N`B``(o@@@zC->%N`h`d#oAAzC-%N`cdxCoXXzS-%N`W:x`Po@@zS-<%N```oo@@zC->%N``doAAzC-%N`dxoXXzS-%N`HW Jx`o@@zS-<%N`U``9o@@zC->%N`y`d\oAAzC-%N`dxoXXzS-%N`W0Zx`ʦo@@@zS-<%N` ``o@@zC->%N`^`doAA@zC-%N`TdxMoXXzS-%N`W@jx`[o@@zS-<%N```o@@zC->%N``doAAzC-%N`dxoXXzS-%N`OWPzx`o@@zS-<%N`\``@o@@zC->%N``doAAzC-%N`dxoXXzS-%N`W`x`o@@zS-<%N`!``o@@zC->%N`@`d;oAAzC-%N`{dxIoXXzS-%N` Wpx`Xo@@zS-<%N` ``~o@@zC->%N` `doAAzC-%N` dxoXXzS-%N`GWx`o@@@zS-<%N`W``5o@@zC->%N`u`dboAA@zC-%N`dxoXXzS-%N`Wx`o@@zS-<%N```o@@zC->%N`7`d#oAAzC-%N`cdxoXXzS-%N`#Wx`o@@zS-<%N`#``o@@zC->%N` $`doAAzC-%N`B%dx[oXXzS-%N`,Wx`ho@@zS-<%N`,``o@@zC->%N`,`doAAzC-%N`-dx#oXX@zS-%N`c5Wx`1o@@zS-<%N`q5``Uo@@@zC->%N`5`dcoAAzC-%N`6d`o@@zS-<%N`!>`xCoXXzS-%N`BWHcx`no@@zC->%N`B`d+oAAzC-%N`ldx%N`.}`dBoAAzC-%N`dxIoXXzS-%N`WHmx`Io@@zS-<%N`,``Jo@@zC->%N`P`d;KoAAzC-%N`{dxoRoXXzS-%N`WH }x`|Ro@@zS-<%N```Ro@@zC->%N``dSoAAzC-%N`dx/[oXXzS-%N`oWLF4Vxx`=[o@@@zS-<%N`}``a[o@@zC->%N``d҂oAA@zC-%N`dxoXXzS-%N`WL .x`o@@zS-<%N```ʋo@@zC->%N` `doAAzC-%N`UdxoXXzS-%N`WL x`o@@zS-<%N```o@@zC->%N``dۙoAAzC-%N`dxoXXzS-%N`WL kx`o@@zS-<%N```o@@zC->%N`?`doAAzC-%N`dxoXXzS-%N`%WL {x`o@@zS-<%N`2``Mo@@zC->%N``d6oAAzC-%N`vdxoXXzS-%N`WL x`o@@@zS-<%N```˶o@@zC->%N` `d#oAA@zC-%N`cdx'oXXzS-%N`gWL 0x`7o@@zS-<%N`w``Xo@@zC->%N``doAAzC-%N` dxoXXzS-%N`" WL @x`o@@zS-<%N`. ``Eo@@zC->%N` `dKoAAzC-%N` dxoXXzS-%N` WL Px`o@@zS-<%N` ``o@@zC->%N`@ `doAAzC-%N`% dxaoXX@zS-%N` WL `x`mo@@zS-<%N` ``o@@@zC->%N` `doAAzC-%N` dxoXXzS-%N`! WL px`o@@zS-<%N` " ``o@@zC->%N`Y" `doAAzC-%N`Z# dxoXX@zS-%N`S* WL x`%o@@zS-<%N`e* ``Bo@@@zC->%N`* `dJoAAzC-%N`+ dxaoXX%N`2 `d}oAA%N`; `doAA%N`C `doAA%N`iL `dK oAA%N`;U `doAA%N`] `doAAmzC-%N` _ dx&oXXmzS-%N`Nf WL [x`&o@@mzS-<%N`\f ``^&o@@mzC->%N`f `dg'oAAmzC-%N`g dx.oXXmzS-%N`o WLlx`.o@@mzS-<%N`&o ``'/o@@mzC->%N`go `d;0oAAmzC-%N`{p dx7oXXmzS-%N`w WL|x`7o@@mzS-<%N`w ``7o@@mzC->%N`0x `d 9oAAmzC-%N`Ky dxW@oXX@mzS-%N` WL x`e@o@@@mzS-<%N` ``@o@@@mzC->%N` `dAoAA@mzC-%N` dxIoXXmzS-%N`] WL0x`+Io@@mzS-<%N`k ``pIo@@mzC->%N` `dJoAAmzC-%N` dxQoXXmzS-%N`, WL@x`Qo@@mzS-<%N`; ``?Ro@@mzC->%N` `d:SoAAmzC-%N`z dxZoXX@mzS-%N` WLPx`Zo@@@mzS-<%N` ``Zo@@@mzC->%N`6 `d\oAA@mzC-%N`Z dxocoXXmzS-%N` WL`x`|co@@mzS-<%N` ``co@@mzC->%N` `ddoAAmzC-%N` dx4loXXmzS-%N`t WLpx`Clo@@mzS-<%N` ``lo@@mzC->%N` `dmoAAmzC-%N`ڭ dxtoXXmzS-%N`= WLx` uo@@mzS-<%N`J ``Kuo@@mzC->%N` `dpvoAAmzC-%N` dx}oXXmzS-%N` WLx`}o@@mzS-<%N` ``~o@@mzC->%N`O `d)oAAmzC-%N`i dxoXX@mzS-%N` WL x`o@@@mzS-<%N` ``Նo@@@mzC->%N` `doAA@mzC-%N` dxoXXmzS-%N`N WLx`o@@mzS-<%N`\ ``7o@@mzC->%N`w `d>oAAmzC-%N`~ dxǗoXXmzS-%N` WL,x`՗o@@mzS-<%N` ``o@@mzC->%N`0 `dޘoAAmzC-%N` dxoXX@mzS-%N` WL<x`o@@@mzS-<%N` ``o@@@mzC->%N` `doAA@mzC-%N`( dx8oXXmzS-%N`x WLLx`Fo@@mzS-<%N` ``do@@mzC->%N` `dhoAAmzC-%N` dxoXXmzS-%N`0 WL\x`o@@mzS-<%N`> ``o@@mzC->%N`Z `doAAmzC-%N`H dxoXXmzS-%N` WL]h)P-16;%N` `doAAmzC-%N`8 dxoXXmzS-%N`" WL NU`m%"eT$x`o@@mzS-<%N`6 ``o@@mzC->%N`Q `doAAmzC-%N`N dxoXX@mzS-%N`! WL Fdtx`o@@@mzS-<%N`! ``o@@@mzC->%N`! `doAA@mzC-%N`" dxoXX%N`* `dcoAA@%N`M3 `doAA%N`< `doAA%N`H `dx oAA%N`Y `djoAA%N`Yb `d8#oAA%N`k `d+oAA%N`w `d8oAA%N`; `d AoAA@%N`Ԉ `dIoAA%N`H `dXoAA%N`E `dcoAA%N` `dkoAA%N` `dXyoAAmzC-%N` dxoXXmzS-%N`B WLx`o@@mzS-<%N`T ``3o@@mzC->%N`s `d3oAAmzC-%N`s dxyoXX@mzS-%N` WL x`o@@@mzS-<%N` ``o@@@mzC->%N` `doAA@mzC-%N` dxBoXXmzS-%N` WL0x`Xo@@mzS-<%N` ``zo@@mzC->%N` `dzoAAmzC-%N` dxљoXXmzS-%N` WL@x`o@@mzS-<%N`$ ``o@@mzC->%N`> `doAAmzC-%N`G dxoXXmzS-%N` WLPx`o@@mzS-<%N` ``Ƣo@@mzC->%N` `doAAmzC-%N`# dxFoXXmzS-%N` WL < <x`So@@mzS-<%N` ``o@@mzC->%N`Z `doAAmzC-%N` dx{oXX@mzS-%N` WL x`o@@@mzS-<%N` ``o@@@mzC->%N` `dθoAA@mzC-%N` dx;oXXmzS-%N`{ WL@x`Ho@@mzS-<%N` ``so@@mzC->%N` `doAAmzC-%N` dxoXXmzS-%N`] WLPx`*o@@mzS-<%N`j ``Ko@@mzC->%N` `d`oAAmzC-%N` dxoXX@mzS-%N`& WL`4Vxx`o@@@mzS-<%N`7 ``o@@@mzC->%N`X `dToAA@mzC-%N` dxooXXmzS-%N` WLcx`|o@@mzS-<%N` ``o@@mzC->%N` `doAAmzC-%N` % dx~oXXmzS-%N`+ WLx`o@@mzS-<%N`+ ``o@@mzC->%N`+ `doAAmzC-%N`- dxEoXXmzS-%N`4 WL x`Ro@@mzS-<%N`4 ``{o@@mzC->%N`4 `doAAmzC-%N`6 dxoXXmzS-%N`[= WL0x`+o@@mzS-<%N`k= ``Io@@mzC->%N`= `dfoAAmzC-%N`> dxoXX@mzS-%N`ER WL@x`o@@@mzS-<%N`RR ``@o@@@mzC->%N`R `dloAA@mzC-%N`S dxoXXmzS-%N`[ WLPx`o@@mzS-<%N`-[ ``o@@mzC->%N`X[ `d'oAAmzC-%N`g\ dx#oXXmzS-%N`c WL`x`#o@@mzS-<%N`c ``#o@@mzC->%N` d `d %oAAmzC-%N``e dxw,oXX@mzS-%N`l WLpx`,o@@@mzS-<%N`l ``,o@@@mzC->%N`l `d-oAA@mzC-%N`'n dx 5oXXmzS-%N`Ku WLx`5o@@mzS-<%N`Yu ``@5o@@mzC->%N`u `di6oAAmzC-%N`v dx=oXXmzS-%N`*~ WLx`=o@@mzS-<%N`9~ ``>o@@mzC->%N`]~ `d6?oAAmzC-%N`v dxFoXXmzS-%N` WLx`Fo@@mzS-<%N` ``Fo@@mzC->%N`' `dHoAAmzC-%N`[ dx4OoXXmzS-%N`t WL#x`EOo@@mzS-<%N` ``Oo@@mzC->%N` `dPoAAmzC-%N`֐ dxWoXX@mzS-%N`; WL3x` Xo@@@mzS-<%N`I ``3Xo@@@mzC->%N`s `d0YoAA@mzC-%N`p dx`oXXmzS-%N` WLCx``o@@mzS-<%N`% ``ao@@mzC->%N`C `d4boAAmzC-%N`t dxioXXmzS-%N`ѩ WLSx`io@@mzS-<%N`ߩ ``io@@mzC->%N` `djoAAmzC-%N`; dx(roXX@mzS-%N`h WLcx`=ro@@@mzS-<%N`} ```ro@@@mzC->%N` `dsoAA@mzC-%N`ų dxzoXXmzS-%N` WLtx`zo@@mzS-<%N`. ``{o@@mzC->%N`U `dD|oAAmzC-%N` dxoXXmzS-%N` WLx`o@@mzS-<%N` ``σo@@mzC->%N` `doAAmzC-%N`+ dxToXXmzS-%N` WL x`bo@@mzS-<%N` ``o@@mzC->%N` `dloAAmzC-%N` dxoXXmzS-%N`F WL0x`o@@mzS-<%N`T ``=o@@mzC->%N`} `dioAAmzC-%N` dxoXX@mzS-%N` WL@x`o@@@mzS-<%N` ``ѝo@@@mzC->%N` `dݞoAA@mzC-%N` dxdoXXmzS-%N` WLPx`ro@@mzS-<%N` ``"o@@mzC->%N`b `doAAmzC-%N`T dx!oXXmzS-%N`a WL`x`-o@@mzS-<%N`m ``Wo@@mzC->%N` `dzoAAmzC-%N` dx۷oXX@mzS-%N` WLpx`o@@@mzS-<%N`* ``o@@@mzC->%N`U `dKoAA@mzC-%N` dxoXXmzS-%N` WLx`o@@mzS-<%N` ``o@@mzC->%N` `doAAmzC-%N` dxjoXXmzS-%N` WLx`yo@@mzS-<%N` ``o@@mzC->%N` `doAAmzC-%N` dx0oXXmzS-%N`p WLx`=o@@mzS-<%N`} ``fo@@mzC->%N` `doAAmzC-%N` dxoXXmzS-%N`) WL$x`o@@mzS-<%N`5 ``o@@mzC->%N`Y `dFoAAmzC-%N` dxoXX@mzS-%N`# WL4x`o@@@mzS-<%N`$ ``o@@@mzC->%N`*$ `d oAA@mzC-%N`J% dx_oXXmzS-%N`, WLDx`mo@@mzS-<%N`, ``o@@mzC->%N`, `dxoAAmzC-%N`- dx#oXXmzS-%N`c5 WLTx`1o@@mzS-<%N`q5 ``\o@@mzC->%N`5 `doAAmzC-%N`6 dxoXX@mzS-%N`+> WLgx`o@@@mzS-<%N`9> ``$o@@@mzC->%N`d> `deoAA@mzC-%N`? dxoXXmzS-%N`F WLx`o@@mzS-<%N` G ``o@@mzC->%N`SG `d6oAAmzC-%N`vH dx'oXXmzS-%N`gO WL Mx`6o@@mzS-<%N`vO ``To@@mzC->%N`O `duoAAmzC-%N`P dxoXXmzS-%N`6X WL x` o@@mzS-<%N`KX ``;o@@mzC->%N`{X `dYoAAmzC-%N`Y dx oXXmzS-%N`` WL0 mx` o@@mzS-<%N`a `` o@@mzC->%N`%a `d"oAAmzC-%N`Cb dx)oXX@mzS-%N`i WLvx`)o@@@mzS-<%N`i ``)o@@@mzC->%N`i `d5oAA@mzC-%N` v dx:oXXmzS-%N`{ WLx`:o@@mzS-<%N`#{ ``;o@@mzC->%N`P{ `d5%N`" `dDoAAmzC-%N`0 dxWLoXX@mzS-%N` WL0x`eLo@@@mzS-<%N` ``Lo@@@mzC->%N`Ќ `dMoAA@mzC-%N` dxUoXXmzS-%N`[ WL@x`)Uo@@mzS-<%N`i ``RUo@@mzC->%N` `dfVoAAmzC-%N` d`]o@@mzS-<%N` `xboXXmzS-%N`E WLPx`Ubo@@mzC->%N` `dUcoAAmzC-%N` dxjoXXmzS-%N` WL`x`jo@@mzS-<%N` ``ko@@mzC->%N`E `dloAAmzC-%N`Z dxrsoXXmzS-%N` WLpx`so@@mzS-<%N`³ ``so@@mzC->%N` `dtoAAmzC-%N`) dxO|oXX@mzS-%N` WLx`^|o@@@mzS-<%N` ``|o@@@mzC->%N`ܼ `d}oAA@mzC-%N` dxoXXmzS-%N`[ WLx`*o@@mzS-<%N`j ``go@@mzC->%N` `djoAAmzC-%N` dxoXXmzS-%N`& WLx`o@@mzS-<%N`7 ``3o@@mzC->%N`s `dPoAAmzC-%N` dxoXX@mzS-%N` WL&x`o@@@mzS-<%N` ``o@@@mzC->%N`@ `d#oAA@mzC-%N`c dxnoXXmzS-%N` WL6x`}o@@mzS-<%N` ``şo@@mzC->%N` `dѠoAAmzC-%N` dxoXXmzS-%N`Z WLFx`)o@@mzS-<%N`i ``lo@@mzC->%N` `doAAmzC-%N` dxoXXmzS-%N`! WLVx`o@@mzS-<%N`/ ``1o@@mzC->%N`q `d+oAAmzC-%N`k dx|oXXmzS-%N` WLfx`o@@mzS-<%N` ``o@@mzC->%N`  `doAAmzC-%N` dxAoXX@mzS-%N` WLwx`Oo@@@mzS-<%N` ``o@@@mzC->%N` `doAA@mzC-%N` dxoXXmzS-%N` WLx`o@@mzS-<%N`. ``o@@mzC->%N`Y `d@oAAmzC-%N` dxoXXmzS-%N`! WL x`o@@mzS-<%N`! ``o@@mzC->%N`" `doAAmzC-%N`9# dx.oXX@mzS-%N`n* WL0x`%N`* `doAA@mzC-%N`+ dxoXXmzS-%N`33 WL@x`o@@mzS-<%N`A3 ``+o@@mzC->%N`k3 `dZoAAmzC-%N`4 dxoXXmzS-%N`; WLPx`o@@mzS-<%N` < ``o@@mzC->%N`5< `dNoAAmzC-%N`= dxoXXmzS-%N`D WL`x`o@@mzS-<%N`D ``o@@mzC->%N` E `doAAmzC-%N`DF dxO oXXmzS-%N`M WLpx`] o@@mzS-<%N`M ``| o@@mzC->%N`M `doAAmzC-%N`N dxoXX@mzS-%N` V WLx`o@@@mzS-<%N`.V ``o@@@mzC->%N`VV `dFoAA@mzC-%N`W dxoXXmzS-%N`^ WLx`o@@mzS-<%N`^ ``o@@mzC->%N`_ `d oAAmzC-%N`O` dxm'oXXmzS-%N`g WLx`}'o@@mzS-<%N`g ``'o@@mzC->%N`g `d(oAAmzC-%N`i dx30oXX@mzS-%N`sp WL'x`@0o@@@mzS-<%N`p ``h0o@@@mzC->%N`p `dx1oAA@mzC-%N`q dx8oXXmzS-%N`&y WL7x`8o@@mzS-<%N`3y ``9o@@mzC->%N`Ry `d2:oAAmzC-%N`rz dxAoXXmzS-%N` WLGx`Ao@@mzS-<%N` ``Ao@@mzC->%N` `dBoAAmzC-%N`- dxfJoXXmzS-%N` WLWx`sJo@@mzS-<%N` ``Jo@@mzC->%N`ފ `dKoAAmzC-%N`0 dxRoXXmzS-%N`; WLgx` So@@mzS-<%N`I ``2So@@mzC->%N`r `dMToAAmzC-%N` dx[oXX@mzS-%N` WLxx`[o@@@mzS-<%N` ``[o@@@mzC->%N`3 `d]oAA@mzC-%N`W dxydoXXmzS-%N` WLx`do@@mzS-<%N`Ť ``do@@mzC->%N` `deoAAmzC-%N` dx%N` `dnoAAmzC-%N` dxUoXX@mzS-%N` WL0x`do@@@mzS-<%N` ``!o@@@mzC->%N`a `d-oAA@mzC-%N`m dxoXXmzS-%N`T WL@x`"o@@mzS-<%N`b ``No@@mzC->%N` `duoAAmzC-%N` dxՔoXXmzS-%N` WLPx`o@@mzS-<%N`" ``o@@mzC->%N`Q `dUoAAmzC-%N` dxoXXmzS-%N` WL`x`o@@mzS-<%N` ``ѝo@@mzC->%N` `d oAAmzC-%N`I dxMoXXmzS-%N` WLpx`Yo@@mzS-<%N` ``o@@mzC->%N` `doAAmzC-%N` dx oXX@mzS-%N`L WLx`o@@@mzS-<%N`X ``Go@@@mzC->%N` `d^oAA@mzC-%N` dxַoXXmzS-%N` WLx`o@@mzS-<%N`" `` o@@mzC->%N`K `dAoAAmzC-%N` dxDoXXmzS-%N`WLx`Ro@@mzS-<%N```{o@@mzC->%N``doAAmzC-%N`dxoXX@mzS-%N`= WL(x` o@@@mzS-<%N`K ``3o@@@mzC->%N`s `dVoAA@mzC-%N` dxoXXmzS-%N` WL8x`o@@mzS-<%N```o@@mzC->%N`A`doAAmzC-%N`TdxoXXmzS-%N`WLHx`o@@mzS-<%N```o@@mzC->%N``doAAmzC-%N`6dx>oXXmzS-%N`~#WLXx`Jo@@mzS-<%N`#``so@@mzC->%N`#`doAAmzC-%N`$dxoXXmzS-%N`5,WLhx`o@@mzS-<%N`B,``-o@@mzC->%N`m,`dYoAAmzC-%N`-dxoXX@mzS-%N`4WLyx`o@@@mzS-<%N`4``o@@@mzC->%N`5`doAA@mzC-%N`66dxcoXXmzS-%N`=WLx`qo@@mzS-<%N`=``o@@mzC->%N`=`doAAmzC-%N`>dx"oXXmzS-%N`bFWL x`.o@@mzS-<%N`nF``oo@@mzC->%N`F`doAAmzC-%N`GdxoXX@mzS-%N`NWL0x`o@@@mzS-<%N`N``o@@@mzC->%N`.O`doAA@mzC-%N`3Pdx2oXXmzS-%N`rWWL@x`?o@@mzS-<%N`W``}o@@mzC->%N`W`doAAmzC-%N`Xdx oXXmzS-%N`G`WLPx` o@@mzS-<%N`R```O o@@mzC->%N```d_!oAAmzC-%N`adx(oXXmzS-%N`iWL`x`(o@@mzS-<%N`i``)o@@mzC->%N`Ti`d*oAAmzC-%N`Xjdx=oXXMwS-%N`~WLpx`=o@@MwS-<%N`0~``>o@@MwC->%N`Q~`d;?oAAMwC-%N`{dxFoXXMwS-%N`WLx`Fo@@MwS-<%N```uGo@@MwC->%N``d_HoAAMwC-%N`dxOoXXMwS-%N`WL x`Oo@@MwS-<%N```Oo@@MwC->%N``d#QoAAMwC-%N`cdxmXoXX@MwS-%N`WLx`xXo@@@MwS-<%N```Xo@@@MwC->%N``dYoAA@MwC-%N`(dx`oXX@MwS-%N` WL)x``o@@@MwS-<%N````o@@@MwC->%N`=`d boAA@MwC-%N`LdxQioXX@MwS-%N`WL9x`]io@@MwS-<%N```io@@@MwC->%N`ũ`djoAAMwC-%N`dxroXXMwS-%N`BWLIx` ro@@MwS-<%N`M``7ro@@MwC->%N`w`d]soAAMwC-%N`dxzoXXMwS-%N`ĺWLYx`zo@@MwS-<%N`κ``zo@@MwC->%N``d|oAAMwC-%N`Sdx=oXXMwS-%N`}WLix`Ho@@MwS-<%N```ro@@MwC->%N``doAAMwC-%N`dxNjoXX@MwS-%N`WLzx`ҋo@@@MwS-<%N```o@@@MwC->%N`;`d)oAA@MwC-%N`idx}oXX@MwS-%N`WLx`o@@@MwS-<%N```o@@@MwC->%N``d˕oAA@MwC-%N` dx3oXX@MwS-%N`sWL x`>o@@MwS-<%N`~``fo@@@MwC->%N``doAAMwC-%N`d`o@@MwS-<%N``xoXXMwS-%N`3WL0x`2o@@MwC->%N`r`dYoAAMwC-%N`dxmoXXmzS-%N`WL@x`}o@@mzS-<%N```o@@mzC->%N``doAAmzC-%N` dx2oXX@mzS-%N`rWLPx`@o@@@mzS-<%N```ho@@@mzC->%N``doAA@mzC-%N`dxoXXmzS-%N`1WL`x`o@@mzS-<%N`=``'o@@mzC->%N`g`d?oAAmzC-%N`dxoXXmzS-%N` WLpx`o@@mzS-<%N` ``o@@mzC->%N`) `d%oAAmzC-%N`edxroXX@mzS-%N`WLx`o@@@mzS-<%N```o@@@mzC->%N``doAA@mzC-%N`dxoXXmzS-%N`BWL x`o@@mzS-<%N`O``%N`|`dkoAAmzC-%N`dxoXXmzS-%N`'WLx`o@@mzS-<%N`'``o@@mzC->%N`9'`d%oAAmzC-%N`e(dxoXXMwS-%N`<>WL*x` o@@MwS-<%N`M>``5o@@MwC->%N`u>`d[oAAMwC-%N`?dxoXXMwS-%N`WL:x`o@@MwS-<%N```o@@MwC->%N``doAAMwC-%N`dxvoXXMwS-%N`v WLJx`o@@@MwS-<%N` ``o@@MwC->%N` `doAA@MwC-%N`dxoXXMwS-%N`WLZx`o@@MwS-<%N```=o@@MwC->%N`=`dgoAAMwC-%N`gdx oXXMwS-%N`WLjx` o@@MwS-<%N``` o@@MwC->%N``d"oAAMwC-%N` dxI)oXXMwS-%N`I'WL ` x`X)o@@@MwS-<%N`X'``)o@@MwC->%N`'`d`=oAA@MwC-%N``;dxBoXXMwS-%N`@WL x`Bo@@@MwS-<%N`@``%N`%N`/J`d*MoAA@MwC-%N`*Kdx1ToXXMwS-%N`1RWL 0x`>To@@@MwS-<%N`>R``\To@@MwC->%N`\R`dUoAA@MwC-%N`Sdx\oXXMwS-%N`ZWL @x`\o@@MwS-<%N`Z``%]o@@MwC->%N`%[`dM^oAAMwC-%N`M\dxeoXXMwS-%N`cWL Px`eo@@MwS-<%N`c``eo@@MwC->%N`c`d,goAAMwC-%N`,edxmoXXMwS-%N`kWL `x`mo@@@MwS-<%N`k``mo@@MwC->%N`k`d3ooAA@MwC-%N`3mdxvoXXMwS-%N`tWL px`vo@@@MwS-<%N`t``vo@@MwC->%N`t`dwoAA@MwC-%N`udx]oXXMwS-%N`]}WL x`jo@@@MwS-<%N`j}``o@@MwC->%N`}`doAA@MwC-%N`~dx%oXXMwS-%N`%WL x`3o@@@MwS-<%N`3``[o@@MwC->%N`[`doAA@MwC-%N`dxoXXMwS-%N`WL x`o@@MwS-<%N```o@@MwC->%N``d6oAAMwC-%N`6dxdoXXMwS-%N`dWL .x`qo@@MwS-<%N`q``o@@MwC->%N``doAAMwC-%N`dx*oXXMwS-%N`*WL >x`9o@@@MwS-<%N`9``bo@@MwC->%N`b`doAA@MwC-%N`dxoXXMwS-%N`WL Nx`̪o@@@MwS-<%N`̨``o@@MwC->%N``doAA@MwC-%N`dxkoXXMwS-%N`kWL ^x`xo@@@MwS-<%N`x``o@@MwC->%N``doAA@MwC-%N`dx.oXXMwS-%N`.WL nx`:o@@@MwS-<%N`:``bo@@MwC->%N`b`doAA@MwC-%N`dxoXXMwS-%N`WL!x`o@@MwS-<%N```o@@MwC->%N``doAAMwC-%N`dxEoXXMwS-%N`EWL!x`To@@MwS-<%N`T``{o@@MwC->%N`{`doAAMwC-%N`dxoXXMwS-%N`WL! x`o@@@MwS-<%N```>o@@MwC->%N`>`d^oAA@MwC-%N`^dxoXXMwS-%N`WL!0x`o@@@MwS-<%N```o@@MwC->%N``d$oAA@MwC-%N`$dx|oXXMwS-%N`|WL!@x`o@@@MwS-<%N```o@@MwC->%N``doAA@MwC-%N`dx5oXXMwS-%N`5WL!Px`Ao@@@MwS-<%N`A``jo@@MwC->%N`j`dzoAA@MwC-%N`zdx oXXMwS-%N` WL!`x`o@@MwS-<%N```@o@@MwC->%N`@`dUoAAMwC-%N`UdxpXXMwS-%N`WL!px`p@@MwS-<%N```p@@MwC->%N``d;pAAMwC-%N`;dx pXXMwS-%N`WL!x` p@@@MwS-<%N``` p@@MwC->%N``d pAA@MwC-%N` dx-pXXMwS-%N`-WL!x`9p@@@MwS-<%N`9``Vp@@MwC->%N`V`dpAA@MwC-%N`dxpXXMwS-%N`WL!x`p@@@MwS-<%N```p@@MwC->%N``d0pAA@MwC-%N`0dx$pXXMwS-%N`"WL!/x`$p@@@MwS-<%N`"``$p@@MwC->%N`"`d&pAA@MwC-%N`$dxa-pXXMwS-%N`a+WL!?x`m-p@@MwS-<%N`m+``-p@@MwC->%N`+`d.pAAMwC-%N`,dx5pXXMwS-%N`3WL!Ox`5p@@MwS-<%N`3``#6p@@MwC->%N`#4`dE7pAAMwC-%N`E5dx>pXXMwS-%N`<WL!_x`>p@@@MwS-<%N`<``>p@@MwC->%N`<`d @pAA@MwC-%N` >dx]GpXXMwS-%N`]EWL!ox`hGp@@@MwS-<%N`hE``Gp@@MwC->%N`E`dHpAA@MwC-%N`FdxPpXXMwS-%N`NWL"x`)Pp@@@MwS-<%N`)N``FPp@@MwC->%N`FN`dQpAA@MwC-%N`OdxXpXXMwS-%N`VWL"x`Xp@@@MwS-<%N`V`` Yp@@MwC->%N` W`d9ZpAA@MwC-%N`9XdxapXXMwS-%N`_WL" x`ap@@MwS-<%N`_``ap@@MwC->%N`_`dbpAAMwC-%N``dxJjpXXMwS-%N`JhWL"0x`Vjp@@MwS-<%N`Vh``~jp@@MwC->%N`~h`dkpAAMwC-%N`idxspXXMwS-%N`qWL"@x`sp@@@MwS-<%N`q``7sp@@MwC->%N`7q`dktpAA@MwC-%N`krdx{pXXMwS-%N`yWL"Px`{p@@@MwS-<%N`y``{p@@MwC->%N`y`d|pAA@MwC-%N`zdxrpXXMwS-%N`rWL"`x`p@@@MwS-<%N```p@@MwC->%N``dpAA@MwC-%N`dxpXXMwS-%N`WL"px`p@@@MwS-<%N```?p@@MwC->%N`?`dCpAA@MwC-%N`CdxpXXMwS-%N`WL"x`Öp@@MwS-<%N`Ô`` p@@MwC->%N` `d pAAMwC-%N` dxpXXMwS-%N`WL"x`p@@MwS-<%N```˟p@@MwC->%N`˝`dpAAMwC-%N`dxpXXmzS-%N`WL" x`(p@@mzS-<%N`(``Fp@@mzC->%N`F`dhpAAmzC-%N`hdxpXXmzS-%N`WL"0x`p@@mzS-<%N```p@@mzC->%N``dpAAmzC-%N`dxpXXmzS-%N`WL"@x`p@@mzS-<%N```ҹp@@mzC->%N`ҷ`d#pAAmzC-%N`#dx>pXXmzS-%N`>WL"Px`Jp@@mzS-<%N`J``up@@mzC->%N`u`dpAAmzC-%N`dxpXXmzS-%N`WL"`x`p@@mzS-<%N```9p@@mzC->%N`9`d_pAAmzC-%N`_dxpXXmzS-%N`WL"px`p@@mzS-<%N```p@@mzC->%N``d+pAAmzC-%N`+dxpXXmzS-%N`WL#x`p@@mzS-<%N```p@@mzC->%N``dpAAmzC-%N`d`zp@@mzS-<%N`z`xpXXmzS-%N`WL#x`p@@mzC->%N``d.pAAmzC-%N`.dxmpXXmzS-%N`mWL# x`|p@@mzS-<%N`|``p@@mzC->%N``dpAAmzC-%N`dx:pXXmzS-%N`:WL#0x`Hp@@mzS-<%N`H``jp@@mzC->%N`j`dpAAmzC-%N`dxpXXmzS-%N`WL#@x`p@@mzS-<%N```=p@@mzC->%N`=`dpAAmzC-%N`dx pXXmzS-%N` WL#Px` p@@mzS-<%N` `` p@@mzC->%N` `d@ pAAmzC-%N`@ dxpXXmzS-%N`WL#`x`p@@mzS-<%N```p@@mzC->%N``dpAAmzC-%N`dx-pXXmzS-%N`-WL#px`;p@@mzS-<%N`;``op@@mzC->%N`o`dpAAmzC-%N`dx%pXXmzS-%N`#WL#x`&p@@mzS-<%N`$``&p@@mzC->%N`$`d:'pAAmzC-%N`:%dxw.pXXmzS-%N`w,WL#x`.p@@mzS-<%N`,``.p@@mzC->%N`,`d/pAAmzC-%N`-dx#7pXXmzS-%N`#5WL#!x`27p@@mzS-<%N`25``Q7p@@mzC->%N`Q5`dy8pAAmzC-%N`y6dx?pXXmzS-%N`=WL#1x`?p@@mzS-<%N`=``?p@@mzC->%N`=`d ApAAmzC-%N` ?dxeHpXXmzS-%N`eFWL#Ax`vHp@@mzS-<%N`vF``Hp@@mzC->%N`F`dIpAAmzC-%N`Gdx.QpXX8yS-%N`.OWL#Qx`@Qp@@8yS-<%N`@O``bQp@@8yC->%N`bO`dRpAA8yC-%N`PdxYpXX8yS-%N`WWL#ax`Zp@@8yS-<%N`X``$Zp@@8yC->%N`$X`dZ[pAA8yC-%N`ZYdxbpXXvDwS-%N``WL$ A$x`bp@@yDwS-<%N````6bp@@vDwC->%N`6``dspAAyDwC-%N`qdxՃpXXpDwS-%N`ՁWL$x`p@@@uDwS-<%N```p@@pDwC->%N``dopAA@uDwC-%N`odxpXX@~DwS-%N`WL$ x`p@@}DwS-<%N```ٌp@@@~DwC->%N`ي`dpAA}DwC-%N`dxOpXXwDwS-%N`OWL&x`^p@@@rDwS-<%N`^``p@@wDwC->%N``dpAA@rDwC-%N`dx9pXX@MwS-%N`9WL@x`Lp@@MwS-<%N`L``vp@@@MwC->%N`v`d,pAAMwC-%N`,dxVpXXMwS-%N`VWL@x`hp@@MwS-<%N`h``p@@MwC->%N``dpAAMwC-%N`dxpXX@MwS-%N`WL@ x`p@@MwS-<%N```2p@@@MwC->%N`2`dkpAAMwC-%N`kdxpXXMwS-%N`ƾWL@0x`p@@MwS-<%N`վ``p@@MwC->%N``d-pAAMwC-%N`-dx[pXX@MwS-%N`[WL@@x`ip@@MwS-<%N`i``p@@@MwC->%N``dpAAMwC-%N`dxpXX@MwS-%N`WL@Px`-p@@MwS-<%N`-``Wp@@@MwC->%N`W`dpAAMwC-%N`dxpXX@MwS-%N`WL@`x`p@@MwS-<%N```!p@@@MwC->%N`!`dMpAAMwC-%N`MdxpXXMwS-%N`WL@px`p@@MwS-<%N```p@@MwC->%N``d-pAAMwC-%N`-dxpXX@MwS-%N`WL@x`p@@MwS-<%N```p@@@MwC->%N``dpAAMwC-%N`dxipXXMwS-%N`iWL@.x`yp@@MwS-<%N`y``p@@MwC->%N``dpAAMwC-%N`dx6pXX@MwS-%N`6WL@>x`Fp@@MwS-<%N`F``op@@@MwC->%N`o`dpAAMwC-%N`dxpXX@8yS-%N`WL@Nx`p@@8yS-<%N```Hp@@@8yC->%N`H`d<pAA8yC-%N`<dxpXX8yS-%N` WL@^x`p@@@8yS-<%N` ``"p@@8yC->%N`"`dpAA@8yC-%N`dx}pXX@8yS-%N`}WL@nx`p@@8yS-<%N```p@@@8yC->%N``dpAA8yC-%N`dx!pXX8yS-%N`WL@~x`!p@@@8yS-<%N```Y!p@@8yC->%N`Y`dc"pAA@8yC-%N`c dx)pXX@8yS-%N`'WL@x`)p@@8yS-<%N`'``)p@@@8yC->%N`'`d*pAA8yC-%N`(dx)2pXX8yS-%N`)0WLAx`92p@@@8yS-<%N`90``|2p@@8yC->%N`|0`dM3pAA@8yC-%N`M1dx:pXX@8yS-%N`8WLAx`:p@@8yS-<%N`8`` ;p@@@8yC->%N` 9`d;pAA8yC-%N`9dxCpXX8yS-%N`AWLA x`Cp@@@8yS-<%N`A``Cp@@8yC->%N`A`dDpAA@8yC-%N`BdxLpXX@8yS-%N`JWLA0x`3Lp@@8yS-<%N`3J``RLp@@@8yC->%N`RJ`dMpAA8yC-%N`KdxTpXX8yS-%N`RWLA@x`Tp@@8yS-<%N`R``Tp@@8yC->%N`R`d VpAA8yC-%N` Tdxs]pXX8yS-%N`s[WLAPx`]p@@8yS-<%N`[``]p@@8yC->%N`[`d^pAA8yC-%N`\dxfpXX8yS-%N`dWLA`x`fp@@@8yS-<%N`d``@fp@@8yC->%N`@d`d^gpAA@8yC-%N`^edxnpXX8yS-%N`lWLApx`np@@8yS-<%N`l``np@@8yC->%N`l`d#ppAA8yC-%N`#ndxwpXX8yS-%N`uWLAx`wp@@8yS-<%N`u``wp@@8yC->%N`u`dxpAA8yC-%N`vdx%pXX@8yS-%N`%~WLA/x`5p@@@8yS-<%N`5~``_p@@@8yC->%N`_~`dxpAA@8yC-%N`xdx͈pXX@8yS-%N`͆WLA?x`߈p@@@8yS-<%N`߆`` p@@@8yC->%N` `dpAA@8yC-%N`dx]pXX8yS-%N`]WLAOx`lp@@8yS-<%N`l``p@@8yC->%N``dpAA8yC-%N`dx.pXX8yS-%N`.WLA_x`?p@@8yS-<%N`?``ip@@8yC->%N`i`dpAA8yC-%N`dxpXX@8yS-%N`WLAox`p@@8yS-<%N```ߢp@@@8yC->%N`ߠ`d pAA8yC-%N` dxpXX8yS-%N`WLAx`p@@8yS-<%N```p@@8yC->%N``dҬpAA8yC-%N`Ҫdx/pXX8yS-%N`/WLAx`@p@@8yS-<%N`@``jp@@8yC->%N`j`dpAA8yC-%N`dxpXX8yS-%N`WLBx`p@@@8yS-<%N```/p@@8yC->%N`/`d]pAA@8yC-%N`]dxpXX8yS-%N`WLBx`p@@8yS-<%N```p@@8yC->%N``dpAA8yC-%N`dxnpXX8yS-%N`nWLB x`|p@@8yS-<%N`|``p@@8yC->%N``dpAA8yC-%N`dxpXX@8yS-%N`WLB0x`p@@@8yS-<%N```Ap@@@8yC->%N`A`dNpAA@8yC-%N`NdxpXX@8yS-%N`WLB@x`p@@@8yS-<%N```p@@@8yC->%N``d-pAA@8yC-%N`-dx\pXX8yS-%N`\WLBPx`jp@@8yS-<%N`j``p@@8yC->%N``dpAA8yC-%N`dxpXX8yS-%N`WLB`x`p@@8yS-<%N```'p@@8yC->%N`'`dQpAA8yC-%N`QdxpXX@8yS-%N`WLBpx`p@@8yS-<%N```p@@@8yC->%N``dpAA8yC-%N`dxpXX8yS-%N`WLB x`p@@8yS-<%N```p@@8yC->%N``dpAA8yC-%N`dx@ pXX8yS-%N`@ WLB0x`M p@@8yS-<%N`M ``v p@@8yC->%N`v `d pAA8yC-%N` dxpXX8yS-%N`WLB@x`4p@@@8yS-<%N`4``Pp@@8yC->%N`P`dGpAA@8yC-%N`Gd`p@@@8yS-<%N``x !pXX8yS-%N` WLBPx`7!p@@8yC->%N`7`dj"pAA@8yC-%N`j dx)pXX@8yS-%N`'WLB`x`)p@@8yS-<%N`'``)p@@@8yC->%N`'`d]+pAA8yC-%N`])dx2pXX8yS-%N`0WLBpx`2p@@@8yS-<%N`0``2p@@8yC->%N`0`d3pAA@8yC-%N`1dx!;pXX@8yS-%N`!9WLBx`2;p@@8yS-<%N`29``a;p@@@8yC->%N`a9`df<pAA8yC-%N`f:dxCpXX8yS-%N`AWLBx`Cp@@@8yS-<%N`A``Cp@@8yC->%N`A`d#EpAA@8yC-%N`#CdxLpXX@8yS-%N`JWLCx`Lp@@8yS-<%N`J``Lp@@@8yC->%N`J`dMpAA8yC-%N`KdxWUpXX8yS-%N`WSWLCx`dUp@@@8yS-<%N`dS``Up@@8yC->%N`S`dVpAA@8yC-%N`Tdx]pXX@8yS-%N`[WLC x`]p@@8yS-<%N`[``!^p@@@8yC->%N`!\`d0_pAA8yC-%N`0]dxffpXX8yS-%N`fdWLC0x`sfp@@@8yS-<%N`sd``fp@@8yC->%N`d`dgpAA@8yC-%N`edx opXX@8yS-%N` mWLC@x`-op@@8yS-<%N`-m``Wop@@@8yC->%N`Wm`dlppAA8yC-%N`lndxwpXX8yS-%N`uWLCPx`wp@@@8yS-<%N`u``xp@@8yC->%N`v`d~ypAA@8yC-%N`~wdxpXX@8yS-%N`~WLC`x`p@@8yS-<%N`~``p@@@8yC->%N`~`dpAA8yC-%N`dx6pXXvDwS-%N`6WLCpx`Jp@@@{DwS-<%N`J``mp@@vDwC->%N`m`dpAA@{DwC-%N`dxpXX8yS-%N`WLC!x`‘p@@@8yS-<%N```p@@8yC->%N``d pAA@8yC-%N` dxypXX@8yS-%N`yWLC1x`p@@8yS-<%N```p@@@8yC->%N``dЛpAA8yC-%N`ЙdxKpXX8yS-%N`KWLCAx`Yp@@@8yS-<%N`Y``p@@8yC->%N``dpAA@8yC-%N`dxpXX@8yS-%N`WLCQx`p@@8yS-<%N```p@@@8yC->%N``d9pAA8yC-%N`9dxpXX8yS-%N`WLCax`p@@@8yS-<%N```մp@@8yC->%N`ղ`dpAA@8yC-%N`dxDpXX@8yS-%N`DWLCqx`Sp@@8yS-<%N`S``|p@@@8yC->%N`|`dpAA8yC-%N`dx pXX8yS-%N` WLCx`p@@@8yS-<%N```Cp@@8yC->%N`C`dpAA@8yC-%N`dxpXX@8yS-%N`WLCx`p@@8yS-<%N``` p@@@8yC->%N` `d5pAA8yC-%N`5dxpXX8yS-%N`WLDx`p@@@8yS-<%N```p@@8yC->%N``dpAA@8yC-%N`dx7pXX@8yS-%N`7WLDx`Dp@@8yS-<%N`D``np@@@8yC->%N`n`d\pAA8yC-%N`\dxpXX8yS-%N`WLD x`p@@@8yS-<%N``` p@@8yC->%N` `dpAA@8yC-%N`dx<pXX@8yS-%N`<WLD0x`Lp@@8yS-<%N`L``ip@@@8yC->%N`i`dpAA8yC-%N`dxpXX8yS-%N`WLD@x`p@@@8yS-<%N```jp@@8yC->%N`j`dWpAA@8yC-%N`WdxpXX@8yS-%N`WLDPx`p@@8yS-<%N```p@@@8yC->%N``dpAA8yC-%N`dx} pXX8yS-%N`} WLD`x` p@@@8yS-<%N` `` p@@8yC->%N` `d pAA@8yC-%N` dx?pXX@8yS-%N`?WLDpx`Kp@@8yS-<%N`K``p@@@8yC->%N``dpAA8yC-%N`dxpXX8yS-%N`WLD"x`p@@@8yS-<%N```Vp@@8yC->%N`V`dkpAA@8yC-%N`kdx%pXX@8yS-%N`#WLD2x`%p@@8yS-<%N`#``&p@@@8yC->%N`$`d'pAA8yC-%N`%dxw.pXX8yS-%N`w,WLDBx`.p@@@8yS-<%N`,``.p@@8yC->%N`,`d/pAA@8yC-%N`-dx?7pXX@8yS-%N`?5WLDRx`L7p@@8yS-<%N`L5``7p@@@8yC->%N`5`d8pAA8yC-%N`6dx @pXX8yS-%N` >WLDbx`@p@@@8yS-<%N`>``W@p@@8yC->%N`W>`dsApAA@8yC-%N`s?dxHpXX@8yS-%N`FWLDrx`Hp@@8yS-<%N`F``Hp@@@8yC->%N`F`dIpAA8yC-%N`GdxeQpXX8yS-%N`eOWLDx`rQp@@@8yS-<%N`rO``Qp@@8yC->%N`O`dRpAA@8yC-%N`Pdx$ZpXX@8yS-%N`$XWLDx`/Zp@@8yS-<%N`/X``oZp@@@8yC->%N`oX`dx[pAA8yC-%N`xYdxbpXX8yS-%N``WLEx`bp@@@8yS-<%N````-cp@@8yC->%N`-a`d6dpAA@8yC-%N`6bdxkpXX@8yS-%N`iWLEx`kp@@8yS-<%N`i``kp@@@8yC->%N`i`dlpAA8yC-%N`jdxctpXX8yS-%N`crWLE x`ntp@@@8yS-<%N`nr``tp@@8yC->%N`r`dupAA@8yC-%N`sdx}pXX@8yS-%N`{WLE0x`}p@@8yS-<%N`{``Z}p@@@8yC->%N`Z{`dg~pAA8yC-%N`g|dxpXX8yS-%N`WLE@x`Ʌp@@@8yS-<%N`Ƀ`` p@@8yC->%N` `d)pAA@8yC-%N`)dxMpXX@8yS-%N`MWLEPx`Yp@@8yS-<%N`Y``wp@@@8yC->%N`w`dpAA8yC-%N`dxԖpXX8yS-%N`ԔWLE`x`ߖp@@@8yS-<%N`ߔ``p@@8yC->%N``dEpAA@8yC-%N`EdxpXX@8yS-%N`WLEpx`p@@8yS-<%N```p@@@8yC->%N``d֠pAA8yC-%N`֞dx9pXX8yS-%N`9WLE#x`Ep@@@8yS-<%N`E``lp@@8yC->%N`l`dpAA@8yC-%N`dxpXX@8yS-%N`WLE3x`p@@8yS-<%N```,p@@@8yC->%N`,`dwpAA8yC-%N`wdxϹpXX8yS-%N`ϷWLECx`۹p@@@8yS-<%N`۷``p@@8yC->%N``d+pAA@8yC-%N`+dxpXX@8yS-%N`WLESx`p@@8yS-<%N```p@@@8yC->%N``dpAA8yC-%N`dx=pXX8yS-%N`=WLEcx`Hp@@@8yS-<%N`H``sp@@8yC->%N`s`dpAA@8yC-%N`dxpXX@8yS-%N`WLEsx`p@@8yS-<%N```'p@@@8yC->%N`'`dQpAA8yC-%N`QdxpXX8yS-%N`WLEx`p@@@8yS-<%N```p@@8yC->%N``dpAA@8yC-%N`dx0pXX@8yS-%N`0WLEx`<p@@8yS-<%N`<``[p@@@8yC->%N`[`dpAA8yC-%N`dxpXX8yS-%N`WLFx`p@@@8yS-<%N```1p@@8yC->%N`1`dFpAA@8yC-%N`FdxpXX@8yS-%N`WLFx`p@@8yS-<%N```p@@@8yC->%N``dFpAA8yC-%N`FdxepXX8yS-%N`eWLF x`qp@@@8yS-<%N`q``p@@8yC->%N``dpAA@8yC-%N`dxpXX@8yS-%N`WLF0x`p@@8yS-<%N``` p@@@8yC->%N` `d4 pAA8yC-%N`4dxpXX8yS-%N`WLF@x`p@@@8yS-<%N```p@@8yC->%N``dpAA@8yC-%N`dx=pXX@8yS-%N`=WLFPx`Ip@@8yS-<%N`I``tp@@@8yC->%N`t`dpAA8yC-%N`dx!pXX8yS-%N`WLF`x`"p@@@8yS-<%N` ``0"p@@8yC->%N`0 `d>#pAA@8yC-%N`>!dx*pXX@8yS-%N`(WLFpx`*p@@8yS-<%N`(``-+p@@@8yC->%N`-)`d%,pAA8yC-%N`%*dx73pXX8yS-%N`71WLF$x`A3p@@@8yS-<%N`A1``j3p@@8yC->%N`j1`d4pAA@8yC-%N`2dx;pXX@8yS-%N`9WLF4x`;p@@8yS-<%N`9``<p@@@8yC->%N`:`d=pAA8yC-%N`;dxSpXX8yS-%N`QWLFDx`&Sp@@@8yS-<%N`&Q``QSp@@8yC->%N`QQ`dzTpAA@8yC-%N`zRdx[pXX@8yS-%N`YWLFTx`[p@@8yS-<%N`Y``[p@@@8yC->%N`Y`d ]pAA8yC-%N` [dxdpXX8yS-%N`bWLFdx`dp@@@8yS-<%N`b``dp@@8yC->%N`b`d fpAA@8yC-%N` ddxEmpXX@8yS-%N`EkWLFtx`Rmp@@8yS-<%N`Rk``{mp@@@8yC->%N`{k`dnpAA8yC-%N`ldxupXX8yS-%N`sWLFx`up@@@8yS-<%N`s`` vp@@8yC->%N` t`d9wpAA@8yC-%N`9udx~pXX@8yS-%N`|WLFx`~p@@8yS-<%N`|``~p@@@8yC->%N`|`dpAA8yC-%N`}dxXpXX8yS-%N`XWLGx`ep@@@8yS-<%N`e``p@@8yC->%N``dЈpAA@8yC-%N`Іdx"pXX@8yS-%N`"WLGx`1p@@8yS-<%N`1``Lp@@@8yC->%N`L`d}pAA8yC-%N`}dxܘpXX8yS-%N`ܖWLG x`p@@@8yS-<%N```'p@@8yC->%N`'`d>pAA@8yC-%N`>dxpXX@8yS-%N`WLG0x`p@@8yS-<%N```p@@@8yC->%N``dpAA8yC-%N`dxpXX8yS-%N`WLG@x`)p@@@8yS-<%N`)``lp@@8yC->%N`l`d}pAA@8yC-%N`}dxpXX@8yS-%N`WLGPx`p@@8yS-<%N```_p@@@8yC->%N`_`dpAA8yC-%N`dxpXX8yS-%N`WLG`x`p@@@8yS-<%N```Cp@@8yC->%N`C`dOpAA@8yC-%N`OdxpXXDwS-%N`WLGpx`p@@zDwS-<%N```p@@DwC->%N``dpAAzDwC-%N`dxupXXzDwS-%N`uWLG%x`p@@DwS-<%N```p@@zDwC->%N``d$pAADwC-%N`$dxpXX@{DwS-%N`WLG5x`p@@vDwS-<%N```cp@@@{DwC->%N`c`dlpAAvDwC-%N`ldxpXX@rDwS-%N`WLGEx`p@@wDwS-<%N```p@@@rDwC->%N``dpAAwDwC-%N`dxYpXX}DwS-%N`YWLGUx`mp@@@~DwS-<%N`m``p@@}DwC->%N``dpAA@~DwC-%N`dxpXX@uDwS-%N`WLGex`+p@@pDwS-<%N`+``Ip@@@uDwC->%N`I`d~pAApDwC-%N`~dxpXXyDwS-%N`WLGux`p@@vDwS-<%N```?p@@yDwC->%N`?`dOpAAvDwC-%N`OdxpXXvDwS-%N` WLGx`p@@yDwS-<%N` ``p@@vDwC->%N``d%pAAyDwC-%N`%dxpXXpDwS-%N`WLGx`p@@@uDwS-<%N```p@@pDwC->%N``d4pAA@uDwC-%N`2dxr:pXX@~DwS-%N`r8WLHx`:p@@}DwS-<%N`8``:p@@@~DwC->%N`8`d;pAA}DwC-%N`9dxFCpXXwDwS-%N`FAWLHx`WCp@@@rDwS-<%N`WA``Cp@@wDwC->%N`A`dDpAA@rDwC-%N`BdxKpXXvDwS-%N`IWLH x`Kp@@@{DwS-<%N`I``=Lp@@vDwC->%N`=J`dMpAA@{DwC-%N`KdxTpXXDwS-%N`RWLH0x`Tp@@zDwS-<%N`R``Tp@@DwC->%N`R`dUpAAzDwC-%N`SdxB]pXXzDwS-%N`B[WLH@x`R]p@@DwS-<%N`R[``]p@@zDwC->%N`[`d^pAADwC-%N`\dxepXX@{DwS-%N`cWLHPx`fp@@vDwS-<%N`d``Rfp@@@{DwC->%N`Rd`dzgpAAvDwC-%N`zedxnpXX@rDwS-%N`lWLH`x`np@@wDwS-<%N`l``op@@@rDwC->%N`m`d,ppAAwDwC-%N`,ndx{wpXX}DwS-%N`{uWLHpx`wp@@@~DwS-<%N`u``wp@@}DwC->%N`u`dxpAA@~DwC-%N`vdxIpXX@uDwS-%N`I~WLH&x`Yp@@pDwS-<%N`Y~``p@@@uDwC->%N`~`dpAApDwC-%N`dxpXXyDwS-%N`WLH6x`p@@vDwS-<%N```Mp@@yDwC->%N`M`dApAAvDwC-%N`AdxpXXvDwS-%N`WLHFx`ґp@@yDwS-<%N`ҏ``p@@vDwC->%N``d#pAAyDwC-%N`#dxPpXXpDwS-%N`PWLHVx`ap@@@uDwS-<%N`a``p@@pDwC->%N``dpAA@uDwC-%N`dxzpXX@~DwS-%N`zWLHfx`p@@}DwS-<%N```ǣp@@@~DwC->%N`ǡ`dpAA}DwC-%N`dxpXXwDwS-%N`WLHvx`p@@@rDwS-<%N```p@@wDwC->%N``dpAA@rDwC-%N`dxZpXXvDwS-%N`ZWLHx`op@@@{DwS-<%N`o``p@@vDwC->%N``dpAA@{DwC-%N`dx߽pXXDwS-%N`߻WLHx`p@@zDwS-<%N```{p@@DwC->%N`{`d|pAAzDwC-%N`|dxpXXzDwS-%N`WLIx`p@@DwS-<%N```:p@@zDwC->%N`:`d pAADwC-%N` dxZpXX@{DwS-%N`ZWLIx`hp@@vDwS-<%N`h``p@@@{DwC->%N``dpAAvDwC-%N`dx pXX@rDwS-%N` WLI x`p@@wDwS-<%N```gp@@@rDwC->%N`g`dlpAAwDwC-%N`ldxpXX}DwS-%N`WLI0x`p@@@~DwS-<%N```,p@@}DwC->%N`,`d9pAA@~DwC-%N`9dxpXX@uDwS-%N`WLI@x`p@@pDwS-<%N```p@@@uDwC->%N``dpAApDwC-%N`dxVpXXyDwS-%N`VWLIPx`cp@@vDwS-<%N`c``%p@@yDwC->%N`%`dpAAvDwC-%N`dxpXXvDwS-%N`WLI`x`%p@@yDwS-<%N`%``lp@@vDwC->%N`l`d|pAAyDwC-%N`|dxpXXpDwS-%N`WLIpx`p@@@uDwS-<%N```*p@@pDwC->%N`*`dDpAA@uDwC-%N`Ddx pXX@~DwS-%N` WLI'x` p@@}DwS-<%N` `` p@@@~DwC->%N` `d pAA}DwC-%N` dxApXXwDwS-%N`AWLI7x`Qp@@@rDwS-<%N`Q``p@@wDwC->%N``dypAA@rDwC-%N`ydxpXXvDwS-%N`WLIGx`p@@@{DwS-<%N```Sp@@vDwC->%N`S`dYpAA@{DwC-%N`Ydxv&pXXDwS-%N`v$WLIWx`&p@@zDwS-<%N`$``&p@@DwC->%N`$`d'pAAzDwC-%N`%dx4/pXXzDwS-%N`4-WLIgx`C/p@@DwS-<%N`C-``/p@@zDwC->%N`-`d0pAADwC-%N`.dx7pXX@{DwS-%N`5WLIwx`7p@@vDwS-<%N`5``?8p@@@{DwC->%N`?6`d69pAAvDwC-%N`67dx@pXX@rDwS-%N`>WLIx`@p@@wDwS-<%N`>``Ap@@@rDwC->%N`?`d5BpAAwDwC-%N`5@dxiIpXX}DwS-%N`iGWLIx`wIp@@@~DwS-<%N`wG``Ip@@}DwC->%N`G`dJpAA@~DwC-%N`Hdx"RpXX@uDwS-%N`"PWLJx`1Rp@@pDwS-<%N`1P``zRp@@@uDwC->%N`zP`dSpAApDwC-%N`QdxZpXXyDwS-%N`XWLJx`Zp@@vDwS-<%N`X``:[p@@yDwC->%N`:Y`dF\pAAvDwC-%N`FZdxcpXXvDwS-%N`aWLJ x`cp@@yDwS-<%N`a``cp@@vDwC->%N`a`ddpAAyDwC-%N`bdxKlpXXpDwS-%N`KjWLJ0x`Zlp@@@uDwS-<%N`Zj``lp@@pDwC->%N`j`dmpAA@uDwC-%N`kdxtpXX@~DwS-%N`rWLJ@x`tp@@}DwS-<%N`r``#up@@@~DwC->%N`#s`dvpAA}DwC-%N`tdxW}pXXwDwS-%N`W{WLJPx`e}p@@@rDwS-<%N`e{``}p@@wDwC->%N`{`d~pAA@rDwC-%N`|dxpXXvDwS-%N`WLJ`x`p@@@{DwS-<%N```bp@@vDwC->%N`b`dUpAA@{DwC-%N`UdxpXX@8yS-%N`WLJpx`Ҏp@@8yS-<%N`Ҍ``p@@@8yC->%N``d؏pAA8yC-%N`؍dxUpXXDwS-%N`UWLJ(x`jp@@zDwS-<%N`j``p@@DwC->%N``dpAAzDwC-%N`dxpXXzDwS-%N`WLJ8x`"p@@DwS-<%N`"``mp@@zDwC->%N`m`dLpAADwC-%N`LdxpXX@{DwS-%N`WLJHx`p@@vDwS-<%N```p@@@{DwC->%N``dpAAvDwC-%N`dx`pXX@rDwS-%N``WLJXx`pp@@wDwS-<%N`p``p@@@rDwC->%N``dzpAAwDwC-%N`zdxpXX}DwS-%N`WLJhx`/p@@@~DwS-<%N`/``Np@@}DwC->%N`N`dQpAA@~DwC-%N`QdxpXX@uDwS-%N`WLJxx`p@@pDwS-<%N```3p@@@uDwC->%N`3`dpAApDwC-%N`dxipXXyDwS-%N`iWLJx`p@@vDwS-<%N```p@@yDwC->%N``dpAAvDwC-%N`dx3pXXvDwS-%N`3WLJx`Ep@@yDwS-<%N`E``p@@vDwC->%N``dpAAyDwC-%N`dxpXXpDwS-%N`WLKx` p@@@uDwS-<%N` ``2p@@pDwC->%N`2`d}pAA@uDwC-%N`}dxpXX@~DwS-%N`WLKx`p@@}DwS-<%N```p@@@~DwC->%N``dpAA}DwC-%N`dxpXXwDwS-%N`WLK x`p@@@rDwS-<%N```p@@wDwC->%N``dpAA@rDwC-%N`dxBpXXvDwS-%N`BWLK0x`Yp@@@{DwS-<%N`Y``p@@vDwC->%N``dpAA@{DwC-%N`dxpXXDwS-%N`WLK@x`p@@zDwS-<%N```Yp@@DwC->%N`Y`dpAAzDwC-%N`dxpXXzDwS-%N` WLKPx`p@@DwS-<%N` `` p@@zDwC->%N` `d( pAADwC-%N`( dxOpXX@{DwS-%N`O WLK` x``p@@vDwS-<%N`` ``p@@@{DwC->%N` `dpAAvDwC-%N` dxpXX@rDwS-%N` WLKpx`p@@wDwS-<%N` ``6p@@@rDwC->%N`6 `dPpAAwDwC-%N`P dx"pXX}DwS-%N` WLK)x`"p@@@~DwS-<%N` ``"p@@}DwC->%N` `d#pAA@~DwC-%N`! dx_+pXX@uDwS-%N`_) WLK9x`o+p@@pDwS-<%N`o) ``+p@@@uDwC->%N`) `d,pAApDwC-%N`* dxApXXyDwS-%N`? WLKIx`Bp@@vDwS-<%N`@ ``ABp@@yDwC->%N`A@ `dCpAAvDwC-%N`A dxJpXX8yS-%N`H WLKYx`Jp@@8yS-<%N`H ``Jp@@8yC->%N`H `dKpAA8yC-%N`I dx/SpXX8yS-%N`/Q WLKix`>Sp@@8yS-<%N`>Q ``Sp@@8yC->%N`Q `dTpAA8yC-%N`R dx[pXX@8yS-%N`Y WLKyx`\p@@@8yS-<%N`Z ``E\p@@@8yC->%N`EZ `d@]pAA@8yC-%N`@[ dxdpXX@8yS-%N`b WLKx`dp@@@8yS-<%N`b ``ep@@@8yC->%N`c `dfpAA@8yC-%N`d dxmpXX8yS-%N`k WLKx`mp@@8yS-<%N`k ``mp@@8yC->%N`k `dopAA8yC-%N`m dxvpXX8yS-%N`t WLLx`vp@@8yS-<%N`t ``]vp@@8yC->%N`]t `dwpAA8yC-%N`u dx~pXX8yS-%N`| WLLx`~p@@@8yS-<%N`| ``~p@@8yC->%N`| `dpAA@8yC-%N`~ dxhpXX8yS-%N`h WLL x`up@@8yS-<%N`u ``p@@8yC->%N` `dʈpAA8yC-%N`ʆ dxpXX8yS-%N` WLL0x`"p@@8yS-<%N`" ``cp@@8yC->%N`c `d|pAA8yC-%N`| dxӘpXX@8yS-%N`Ӗ WLL@x`p@@8yS-<%N` ``!p@@@8yC->%N`! `dCpAA8yC-%N`C dxpXX8yS-%N` WLLPx`p@@8yS-<%N` ``p@@8yC->%N` `dآpAA8yC-%N`ؠ dx(pXX8yS-%N`( WLL` x`5p@@8yS-<%N`5 ``vp@@8yC->%N`v `dpAA8yC-%N` dxpXX@8yS-%N` WLLpx`p@@@8yS-<%N` ``Op@@@8yC->%N`O `dpAA@8yC-%N` dxpXX@~DwS-%N` WLL*x`p@@}DwS-<%N` ``p@@@~DwC->%N` `dpAA}DwC-%N` dxpXXwDwS-%N` WLL:x`#p@@@rDwS-<%N`# ``lp@@wDwC->%N`l `d}pAA@rDwC-%N`} dxpXXvDwS-%N` WLLJx`p@@@{DwS-<%N` ``p@@vDwC->%N` `dpAA@{DwC-%N` dx|pXXDwS-%N`| WLLZx`p@@zDwS-<%N` ``p@@DwC->%N` `dpAAzDwC-%N` dx=pXXzDwS-%N`= WLLjx`Op@@DwS-<%N`O ``mp@@zDwC->%N`m `dpAADwC-%N` dxpXX@{DwS-%N` WLLzx`p@@vDwS-<%N` ``2p@@@{DwC->%N`2 `d?pAAvDwC-%N`? dxmpXX@rDwS-%N`m WLLx`p@@wDwS-<%N` ``p@@@rDwC->%N` `dpAAwDwC-%N` dx" pXX}DwS-%N`" WLLx`3 p@@@~DwS-<%N`3 `` p@@}DwC->%N` `d pAA@~DwC-%N` dx pXX'zS-%N` WLMx` p@@'zS-<%N` `` p@@'zC->%N` `d pAA'zC-%N` dx- pXX'zS-%N`- WLMx`< p@@'zS-<%N`< ``| p@@'zC->%N`| `d pAA'zC-%N` dx pXX'zS-%N` WLM x` p@@'zS-<%N` `` p@@'zC->%N` `d pAA'zC-%N`  dx' pXX'zS-%N`% WLM0x`' p@@'zS-<%N`% ``' p@@'zC->%N`% `d( pAA'zC-%N`& dx0 pXX'zS-%N`. WLM@x`0 p@@'zS-<%N`. ``0 p@@'zC->%N`. `d1 pAA'zC-%N`/ dx29 pXX@%N`p7 `d: pAA%N`? `dB pAA%N`xH `dvK pAA%N`jQ `d~T pAA@%N`Z `d\ pAA%N`b `df pAA%N`xk `dn pAA%N`;t `dFw pAA%N`| `d pAA%N`X `d pAA%N` `d$ pAA%N`Ζ `dϙ pAA@%N` `d pAA%N` `d8 pAA@KGxC-%N`8 dxa pXXCGxS-%N`a WLN0x`n p@@LGxS-<%N`n `` p@@CGxC->%N` `d pAALGxC-%N` dx pXXCGxS-%N` WLN@x` p@@@GxS-<%N` ``4 p@@CGxC->%N`4 `dY pAA@GxC-%N`Y d` p@@%N`. `d= pAA%N`y `de pAA%N` `dC pAA%N`| `d pAA%N`' `d pAA%N` `d pAA%N` `d pAA@%N`B `dF pAA%N` `d pAA%N` `d pAAdzC-%N` dx5 pXX%N` `d pAA%N`=$ `d5' pAA%N`, `d;0 pAA%N`5 `d8 pAA WLO0x`@@ p@@ ``@ p@@%N`> `dqA pAA%N`G `dJ pAA@%N` P `dS pAA%N`X `d\ pAA%N`za `dd pAA%N`%N`r `du pAA%N`{ `d~ pAA%N` `d pAA%N`` `dx pAA@%N` `d pAA%N` `d pAA%N`q `d pAA p@@MwC->%N`> `dy pAA@MwC-%N`y dx pXX@8yS-%N` WLPx` p@@@8yS-<%N` `` p@@@8yC->%N` `d pAA@8yC-%N` dxg pXX8yS-%N`g WLP x`t p@@8yS-<%N`t `` p@@8yC->%N` `d pAA8yC-%N` dx$ pXX8yS-%N`$ WLP0x`J p@@8yS-<%N`J `` p@@8yC->%N` `d pAA8yC-%N` dx pXX8yS-%N` WLP@x` p@@@8yS-<%N` ``B p@@8yC->%N`B `d; pAA@8yC-%N`; dx pXX8yS-%N` WLPPx` p@@8yS-<%N` `` p@@8yC->%N` `d$ pAA8yC-%N`$ dx@ pXX8yS-%N`@ WLP`x`L p@@8yS-<%N`L `` p@@8yC->%N` `d pAA8yC-%N` dx pXX@8yS-%N` WLPpx`  p@@8yS-<%N`  ``N p@@@8yC->%N`N `dZ pAA8yC-%N`Z dx pXX8yS-%N` WLP.x` p@@8yS-<%N` ``  p@@8yC->%N`  `d  pAA8yC-%N`  dxS pXX8yS-%N`S WLP>x`a p@@8yS-<%N`a `` p@@8yC->%N` `d pAA8yC-%N` dxz! pXX@8yS-%N`z WLPNx`! p@@@8yS-<%N` ``! p@@@8yC->%N` `d" pAA@8yC-%N` dx:* pXX@8yS-%N`:( WLP^x`G* p@@@8yS-<%N`G( ``* p@@@8yC->%N`( `d+ pAA@8yC-%N`) dx2 pXX8yS-%N`0 WLPnx`2 p@@8yS-<%N`0 ``3 p@@8yC->%N`1 `d4 pAA8yC-%N`2 dx; pXX8yS-%N`9 WLP~x`; p@@8yS-<%N`9 ``; p@@8yC->%N`9 `d< pAA8yC-%N`: dxHD pXX8yS-%N`HB WLPx`UD p@@@8yS-<%N`UB ``D p@@8yC->%N`B `dE pAA@8yC-%N`C dxL pXX8yS-%N`J WLPx`M p@@8yS-<%N`K ``jM p@@8yC->%N`jK `dsN pAA8yC-%N`sL dxU pXX8yS-%N`S WLQx`U p@@8yS-<%N`S ``U p@@8yC->%N`S `dV pAA8yC-%N`T dxp^ pXX@8yS-%N`p\ WLQx`{^ p@@8yS-<%N`{\ ``^ p@@@8yC->%N`\ `d_ pAA8yC-%N`] dx1g pXX8yS-%N`1e WLQ x`=g p@@8yS-<%N`=e ``|g p@@8yC->%N`|e `dh pAA8yC-%N`f dxp pXX8yS-%N`n WLQ0x`p p@@8yS-<%N`n ``Pp p@@8yC->%N`Pn `dYq pAA8yC-%N`Yo dxx pXX@8yS-%N`v WLQ@x`x p@@@8yS-<%N`v ``y p@@@8yC->%N`w `d'z pAA@8yC-%N`'x dx~ pXX@8yS-%N`~ WLQPx` p@@@8yS-<%N` ``Ɂ p@@@8yC->%N` `d͂ pAA@8yC-%N`̀ dx8 pXX8yS-%N`8 WLQ`x`C p@@8yS-<%N`C `` p@@8yC->%N` `d pAA8yC-%N` dx pXX8yS-%N` WLQpx` p@@8yS-<%N` ``D p@@8yC->%N`D `dy pAA8yC-%N`y dx pXX8yS-%N` WLQ/x` p@@@8yS-<%N` `` p@@8yC->%N` `d pAA@8yC-%N` dx@ pXX8yS-%N`@ WLQ?x`L p@@8yS-<%N`L `` p@@8yC->%N` `d pAA8yC-%N` dx pXX8yS-%N` WLQOx` p@@8yS-<%N` ``Q p@@8yC->%N`Q `df pAA8yC-%N`f dx pXX@8yS-%N` WLQ_x`͵ p@@8yS-<%N`ͳ `` p@@@8yC->%N` `d* pAA8yC-%N`* dx| pXX8yS-%N`| WLQox` p@@8yS-<%N` ``ɾ p@@8yC->%N`ɼ `d pAA8yC-%N` dx: pXX8yS-%N`: WLQx`F p@@8yS-<%N`F `` p@@8yC->%N` `d pAA8yC-%N` dx pXX@8yS-%N` WLQx` p@@@8yS-<%N` ``P p@@@8yC->%N`P `dP pAA@8yC-%N`P dx pXX@8yS-%N` WLQx` p@@@8yS-<%N` `` p@@@8yC->%N` `d pAA@8yC-%N` dxn pXX8yS-%N`n WLRx`y p@@8yS-<%N`y `` p@@8yC->%N` `d pAA8yC-%N` dx! pXX8yS-%N`! WLRx`1 p@@8yS-<%N`1 ``l p@@8yC->%N`l `d~ pAA8yC-%N`~ dx pXX8yS-%N` WLR x` p@@@8yS-<%N` ``I p@@8yC->%N`I `d: pAA@8yC-%N`: d` p@@8yS-<%N` `x pXX8yS-%N` WLR0x`W p@@8yC->%N`W `d pAA8yC-%N` dx} pXX8yS-%N`} WLR@x` p@@8yS-<%N` `` p@@8yC->%N` `d pAA8yC-%N` dx= pXX@8yS-%N`= WLRPx`L p@@8yS-<%N`L `` p@@@8yC->%N` `dW pAA8yC-%N`W dx pXX8yS-%N` WLR`x` p@@8yS-<%N` ``! p@@8yC->%N`! `d  pAA8yC-%N`  dx" pXX8yS-%N` WLRp x`" p@@8yS-<%N` ``# p@@8yC->%N`! `d;$ pAA8yC-%N`;" dx:+ pXX@8yS-%N`:) WLR0x`G+ p@@@8yS-<%N`G) ``+ p@@@8yC->%N`) `d, pAA@8yC-%N`* dx3 pXX@8yS-%N`1 WLR@x` 4 p@@@8yS-<%N` 2 ``O4 p@@@8yC->%N`O2 `dj5 pAA@8yC-%N`j3 dx< pXX8yS-%N`: WLRPx`< p@@8yS-<%N`: `` = p@@8yC->%N` ; `d> pAA8yC-%N`< dxlE pXX8yS-%N`lC WLR`x`zE p@@8yS-<%N`zC ``E p@@8yC->%N`C `dF pAA8yC-%N`D dxM pXX8yS-%N`K WLRpx` N p@@@8yS-<%N` L ``-N p@@8yC->%N`-L `dJO pAA@8yC-%N`JM dxV pXX8yS-%N`T WLRx`V p@@8yS-<%N`T ``V p@@8yC->%N`T `dW pAA8yC-%N`U dx|_ pXX8yS-%N`|] WLRx`_ p@@8yS-<%N`] ``_ p@@8yC->%N`] `d` pAA8yC-%N`^ dxJh pXX@8yS-%N`Jf WLRx`Yh p@@8yS-<%N`Yf ``h p@@@8yC->%N`f `di pAA8yC-%N`g dxp pXX8yS-%N`n WLSx`p p@@8yS-<%N`n ``.q p@@8yC->%N`.o `dar pAA8yC-%N`ap dxy pXX8yS-%N`w WLSx`y p@@8yS-<%N`w ``y p@@8yC->%N`w `d{ pAA8yC-%N`y dxt pXX@8yS-%N`t WLS x` p@@@8yS-<%N` ``Ƃ p@@@8yC->%N`ƀ `dՃ pAA@8yC-%N`Ձ dx< pXX@8yS-%N`< WLS0x`J p@@@8yS-<%N`J `` p@@@8yC->%N` `d pAA@8yC-%N` dx pXXzS-%N` WLS@x` p@@zS-<%N` ``* p@@zC->%N`* `d pAAzC-%N` dx՜ pXX8yS-%N`՚ WLSPx` p@@8yS-<%N` ``( p@@8yC->%N`( `d9 pAA8yC-%N`9 dxh pXX8yS-%N`h WLS`x`v p@@8yS-<%N`v `` p@@8yC->%N` `d˦ pAA8yC-%N`ˤ dx1 pXX8yS-%N`1 WLSp!x`B p@@@8yS-<%N`B `` p@@8yC->%N` `d pAA@8yC-%N` dx pXX8yS-%N` WLS1x` p@@8yS-<%N` ``K p@@8yC->%N`K `d> pAA8yC-%N`> dx pXXzS-%N` WLSAx` p@@@zS-<%N` ``/ p@@zC->%N`/ `d\ pAA@zC-%N`\ dxk pXXzS-%N`k WLSQx`y p@@zS-<%N`y `` p@@zC->%N` `d pAAzC-%N` dx1 pXXzS-%N`1 WLSax`A p@@zS-<%N`A ``f p@@zC->%N`f `d pAAzC-%N` dx pXXzS-%N` WLSqx` p@@zS-<%N` ``+ p@@zC->%N`+ `dV pAAzC-%N`V dx pXXzS-%N` WLSx` p@@zS-<%N` `` p@@zC->%N` `dS pAAzC-%N`S dx{ pXXzS-%N`{ WLSx` p@@zS-<%N` `` p@@zC->%N` `d pAAzC-%N` dx: pXX@zS-%N`: WLSx`G p@@zS-<%N`G ``j p@@@zC->%N`j `d pAAzC-%N` dx pXXzS-%N` WLTx` p@@zS-<%N` ``+ p@@zC->%N`+ `dZ pAAzC-%N`Z dx pXXzS-%N`WLTx` p@@zS-<%N``` p@@zC->%N``d pAAzC-%N`dxB pXXzS-%N`B WLT x`O p@@zS-<%N`O `` p@@zC->%N` `d pAAzC-%N` dx pXXzS-%N`WLT0x` p@@zS-<%N``` p@@zC->%N``d* pAAzC-%N`*dx pXXzS-%N`WLT@x` p@@@zS-<%N``` p@@zC->%N``d pAA@zC-%N`dxL( pXXzS-%N`L&WLTPx`X( p@@zS-<%N`X&``( p@@zC->%N`&`d) pAAzC-%N`'dx0 pXXzS-%N`.WLT`x`0 p@@zS-<%N`.``1 p@@zC->%N`/`dR2 pAAzC-%N`R0dx9 pXXzS-%N`7WLTp"x`9 p@@@zS-<%N`7``+: p@@zC->%N`+8`d8; pAA@zC-%N`89dxB pXXzS-%N`@WLT2x`B p@@zS-<%N`@``AC p@@zC->%N`AA`d:D pAAzC-%N`:BdxL pXXzS-%N`JWLTBx`L p@@zS-<%N`J``hL p@@zC->%N`hJ`dM pAAzC-%N`KdxU pXXzS-%N`SWLTRx`U p@@zS-<%N`S``fU p@@zC->%N`fS`dOV pAAzC-%N`OTdx] pXXzS-%N`[WLTbx`] p@@zS-<%N`[``^ p@@zC->%N`\`d _ pAAzC-%N` ]dxof pXXzS-%N`odWLTrx`yf p@@zS-<%N`yd``f p@@zC->%N`d`dg pAAzC-%N`edx$o pXX@zS-%N`$mWLTx`/o p@@zS-<%N`/m``No p@@@zC->%N`Nm`dp pAAzC-%N`ndxw pXXzS-%N`uWLTx`w p@@zS-<%N`u``0x p@@zC->%N`0v`d$y pAAzC-%N`$wdx pXXzS-%N`~WLTx` p@@zS-<%N`~`` p@@zC->%N`~`d pAAzC-%N`dxR pXXzS-%N`RWLUx`^ p@@zS-<%N`^`` p@@zC->%N``d pAAzC-%N`dx pXXzS-%N` WLUx` p@@zS-<%N```d p@@zC->%N`d`dg pAAzC-%N`gdxƚ pXXzS-%N`ƘWLU x`Қ p@@@zS-<%N`Ҙ``C p@@zC->%N`C`d8 pAA@zC-%N`8dxO pXXzS-%N`OWLU0x`Z p@@zS-<%N`Z`` p@@zC->%N``d pAAzC-%N`dxӫ pXXzS-%N`өWLU@x`ޫ p@@zS-<%N`ީ``. p@@zC->%N`.`d pAAzC-%N`dx{ pXXzS-%N`{WLUPx` p@@@zS-<%N```ٴ p@@zC->%N`ٲ`dڵ pAA@zC-%N`ڳdx2 pXXzS-%N`2WLU`x`> p@@zS-<%N`>``] p@@zC->%N`]`d pAAzC-%N`dx pXXzS-%N`WLUp#x` p@@zS-<%N```E p@@zC->%N`E`d2 pAAzC-%N`2dxu pXXzS-%N`uWLU3x` p@@zS-<%N``` p@@zC->%N``d pAAzC-%N`dx- pXXzS-%N`-WLUCx`M p@@zS-<%N`M``X p@@zC->%N`X`dz pAAzC-%N`zdx pXXzS-%N`WLUSx` p@@zS-<%N```N p@@zC->%N`N`d5 pAAzC-%N`5dx pXX@zS-%N`WLUcx` p@@zS-<%N```{ p@@@zC->%N`{`d{ pAAzC-%N`{dx; pXXzS-%N`;WLUsx`H p@@zS-<%N`H`` p@@zC->%N``d pAAzC-%N`dx pXX}DwS-%N`WLUx` p@@wDwS-<%N```T p@@}DwC->%N`T`dF pAAwDwC-%N`FdxpXX@rDwS-%N`WLUx`p@@vDwS-<%N```p@@@rDwC->%N``dpAAvDwC-%N`dxv pXX@{DwS-%N`v WLUx` p@@DwS-<%N` `` p@@@{DwC->%N` `d pAADwC-%N` d`p@@zS-<%N``xipXXzS-%N`iWLVx`p@@zC->%N``d}pAAzC-%N`}dx!pXXzS-%N`WLVx` !p@@zS-<%N` ``G!p@@zC->%N`G`dE"pAAzC-%N`E dx)pXXzS-%N`'WLV x`)p@@zS-<%N`'``*p@@zC->%N`(`d+pAAzC-%N`)dx2pXXzS-%N`0WLV0x`2p@@@zS-<%N`0``2p@@zC->%N`0`d4pAA@zC-%N`2dx#;pXXzS-%N`#9WLV@x`0;p@@zS-<%N`09``;p@@zC->%N`9`du<pAAzC-%N`u:dxCpXXzS-%N`AWLVPx`Cp@@zS-<%N`A``Dp@@zC->%N`B`dKEpAAzC-%N` dxLpXXzS-%N`sWLV`x`Lp@@@zS-<%N```Lp@@zC->%N``dNpAA@zC-%N` dxBUpXXzS-%N`WLVp$x`RUp@@zS-<%N```Up@@zC->%N`^`dVpAAzC-%N`vdx]pXXzS-%N`uWLV4x`]p@@zS-<%N```]p@@zC->%N``d_pAAzC-%N`dxYfpXXzS-%N`"WLVDx`gfp@@zS-<%N`'"``fp@@zC->%N`J"`dgpAAzC-%N`\#dxnpXXzS-%N`*WLVTx`np@@zS-<%N`*``op@@zC->%N`*`d6ppAAzC-%N`+dxwpXXzS-%N`D3WLVdx`wp@@zS-<%N`S3``wp@@zC->%N`3`dxpAAzC-%N`z4dxLpXX@zS-%N` <WLVtx`[p@@zS-<%N`<``p@@@zC->%N`C<`dpAAzC-%N`t=dx&pXXzS-%N`DWLVx`5p@@zS-<%N`D``p@@zC->%N`RE`dpAAzC-%N`\FdxpXXzS-%N`MWLVx`p@@zS-<%N`M``p@@zC->%N`M`d-pAAzC-%N`NdxpXXzS-%N`mVWLVx`p@@zS-<%N`zV``ݚp@@zC->%N`V`dpAAzC-%N`WdxfpXXzS-%N`&_WLWx`tp@@zS-<%N`4_``p@@zC->%N`Y_`dpAAzC-%N`m`dx&pXXzS-%N`gWLWx`4p@@@zS-<%N`g``[p@@zC->%N`h`dpAA@zC-%N`@idxpXXzS-%N`pWLW x`p@@zS-<%N`p``'p@@zC->%N`p`dTpAAzC-%N`rdxpXXzS-%N`AyWLW0x`p@@zS-<%N`Py``p@@zC->%N`wy`dpAAzC-%N`zdx^pXXzS-%N`WLW@x`op@@@zS-<%N`/``p@@zC->%N`L`dpAA@zC-%N`ƃdxpXXzS-%N`ۊWLWPx`(p@@zS-<%N```Lp@@zC->%N` `dipAAzC-%N`)dxpXXzS-%N`iWLW`x`p@@zS-<%N```p@@zC->%N``dpAAzC-%N`dxmpXXzS-%N`-WLWp%x`zp@@zS-<%N`:``p@@zC->%N`]`dpAAzC-%N`dxpXXzS-%N`WLW5x`p@@zS-<%N`Ĥ``Zp@@zC->%N``d(pAAzC-%N`dxpXXzS-%N`oWLWEx`p@@zS-<%N`|``p@@zC->%N``dpAAzC-%N`dxopXX@zS-%N`/WLWUx`|p@@zS-<%N`<``p@@@zC->%N``dpAAzC-%N`dx2pXXzS-%N`WLWex`?p@@zS-<%N```bp@@zC->%N`"`dpAAzC-%N`Tdx pXXzS-%N`WLWux` p@@zS-<%N```% p@@zC->%N``d' pAAzC-%N`dxPpXXzS-%N`WLWx`]p@@zS-<%N```p@@zC->%N`_`dpAAzC-%N`Tdx pXXzS-%N`WLWx`p@@zS-<%N```Bp@@zC->%N``dupAAzC-%N`5dx%pXXzS-%N`TWLWx`%p@@@zS-<%N````%p@@zC->%N``dBpAA@zC-%N`dxGpXXzS-%N`rWLXx`Gp@@zS-<%N`~``Hp@@zC->%N``dIpAAzC-%N`dxlPpXXzS-%N`, WLXx`yPp@@zS-<%N`9 ``Pp@@zC->%N` `dQpAAzC-%N`x dxXpXXzS-%N`WLX x`Xp@@@zS-<%N```Xp@@zC->%N``dZpAA@zC-%N`dxapXXzS-%N`IWLX0x`ap@@zS-<%N`h``ap@@zC->%N`u`dbpAAzC-%N`dxQjpXXzS-%N`&WLX@x`^jp@@zS-<%N`&``jp@@zC->%N`A&`dkpAAzC-%N`x'dx spXXzS-%N`.WLXPx`sp@@zS-<%N`.``csp@@zC->%N`#/`d|tpAAzC-%N`<0dx{pXXzS-%N`r7WLX`x`{p@@zS-<%N`~7``|p@@zC->%N`7`d }pAAzC-%N`8dxpXXzS-%N`@@WLXp&x`p@@zS-<%N`O@``p@@zC->%N`u@`dƅpAAzC-%N`Adx@pXX@zS-%N`IWLX6x`Pp@@zS-<%N`I``up@@@zC->%N`5I`dpAAzC-%N`IJdxpXXzS-%N`QWLXFx`p@@zS-<%N`Q``Wp@@zC->%N`R`dSpAAzC-%N`SdxpXXzS-%N`tZWLXVx`p@@zS-<%N`Z``p@@zC->%N`Z`dpAAzC-%N`[dx1pXXzS-%N`bWLXfx`=p@@zS-<%N`b``p@@zC->%N`Nc`dopAAzC-%N`/ddxpXXzDwS-%N`kWLXvx`p@@DwS-<%N`k``;p@@zDwC->%N`k`dCpAADwC-%N`mdxpXX@{DwS-%N`atWLXx`p@@vDwS-<%N`mt``p@@@{DwC->%N`t`d3pAAvDwC-%N`udx7pXX@rDwS-%N`|WLXx`Ep@@wDwS-<%N`}``ap@@@rDwC->%N`!}`dpAAwDwC-%N`j~dxpXX}DwS-%N`ՄWLXx`"p@@vDwS-<%N````p@@}DwC->%N` `dpAAvDwC-%N`YdxpXX@~DwS-%N``WLYx`p@@@uDwS-<%N`m``p@@@~DwC->%N``dpAA@uDwC-%N`dx^pXXpDwS-%N`WLYx`jp@@yDwS-<%N`*``p@@pDwC->%N`i`dpAAyDwC-%N`gdxpXXyDwS-%N`ݞWLY x`+p@@pDwS-<%N```jp@@yDwC->%N`*`d[pAApDwC-%N`dxpXX@uDwS-%N`CWLY0x`p@@@~DwS-<%N`T``p@@@uDwC->%N``dpAA@~DwC-%N`dx5pXXvDwS-%N`WLY@x`Dp@@}DwS-<%N```p@@vDwC->%N`H`dpAA}DwC-%N`QdxpXXwDwS-%N`WLYPx`p@@@rDwS-<%N```@p@@wDwC->%N``d9pAA@rDwC-%N`dxpXXvDwS-%N`lWLY`x`p@@@{DwS-<%N`z``{p@@vDwC->%N`;`d]pAA@{DwC-%N`dxkpXXDwS-%N`+WLYp'x`xp@@zDwS-<%N`8``p@@DwC->%N`y`dpAAzDwC-%N`qdx$pXXzDwS-%N`WLY7x`1p@@DwS-<%N```qp@@zDwC->%N`1`dpAADwC-%N`idxpXX@{DwS-%N`oWLYGx`p@@vDwS-<%N`}`` p@@@{DwC->%N``d!pAAvDwC-%N`dxs(pXX@rDwS-%N`3WLYWx`(p@@wDwS-<%N`@``(p@@@rDwC->%N``d)pAAwDwC-%N`dx1pXX}DwS-%N`WLYgx`1p@@vDwS-<%N```R1p@@}DwC->%N``dh2pAAvDwC-%N`(dx9pXX@~DwS-%N`WLYwx`9p@@@uDwS-<%N```:p@@@~DwC->%N``d ;pAA@uDwC-%N`dxOpXXpDwS-%N` WLYx`Op@@yDwS-<%N` ``8Pp@@pDwC->%N` `dCQpAAyDwC-%N` d`Xp@@yDwS-<%N`D`x\pXXpDwS-%N`WLYx` ]p@@pDwC->%N``d3^pAAyDwC-%N`dxepXX@uDwS-%N` WLYx`-ep@@@~DwS-<%N` ``Jep@@@uDwC->%N` !`dfpAA@~DwC-%N`K"dx}mpXXvDwS-%N`=)WLZx`mp@@}DwS-<%N`L)``mp@@vDwC->%N`)`dnpAA}DwC-%N`*dxDvpXXwDwS-%N`2WLZx`Rvp@@@rDwS-<%N`2``vp@@wDwC->%N`T2`d]wpAA@rDwC-%N`3dx~pXXvDwS-%N`:WLZ x`p@@@{DwS-<%N`:``-p@@vDwC->%N`:`dpAA@{DwC-%N`;dxpXXDwS-%N`XCWLZ0x`p@@zDwS-<%N`kC``ɇp@@DwC->%N`C`d pAAzDwC-%N`Ddx؏pXXzDwS-%N`KWLZ@x`p@@DwS-<%N`K``p@@zDwC->%N`K`d#pAADwC-%N`LdxkpXXzS-%N`+TWLZPx`p@@zS-<%N`@T``p@@zC->%N`T`dpAAzC-%N`UdxpXXzS-%N`\WLZ`x`p@@@zS-<%N`\``,p@@zC->%N`\`dPpAA@zC-%N`^dxpXXzS-%N`veWLZp(x`ũp@@zS-<%N`e``p@@zC->%N`e`dpAAzC-%N`fdxHpXXzS-%N`nWLZ8x`Zp@@zS-<%N`n``xp@@zC->%N`8n`dpAAzC-%N`Uodx pXXzS-%N`vWLZHx`0p@@@zS-<%N`v``:p@@zC->%N`v`d}pAA@zC-%N`=xdx*pXXzS-%N`~WLZXx`8p@@zS-<%N`~```p@@zC->%N` `dpAAzC-%N`BdxpXXzS-%N`uWLZhx`p@@zS-<%N```p@@zC->%N``dpAAzC-%N`dxspXXzS-%N`3WLZxx`p@@zS-<%N`B``p@@zC->%N`k`dpAAzC-%N`qdxpXXzS-%N`ۘWLZx`4p@@zS-<%N```Qp@@zC->%N``d~pAAzC-%N`>dxpXXzS-%N`WLZx`p@@zS-<%N```p@@zC->%N`Ρ`dBpAAzC-%N`dx}pXX@zS-%N`=WLZx`p@@zS-<%N`K``p@@@zC->%N`t`dpAAzC-%N`mdxFpXXzS-%N`WL[x`Xp@@zS-<%N```p@@zC->%N`B`dpAAzC-%N`qdx pXXzS-%N`ʻWL[x`p@@zS-<%N`ػ``@p@@zC->%N``dqpAAzC-%N`1dxpXXzS-%N`]WL[ x`p@@zS-<%N`n``p@@zC->%N``d) pAAzC-%N`dxpXXzS-%N`WL[0x`p@@zS-<%N```1p@@zC->%N``dUpAAzC-%N`dxdpXXzS-%N`$WL[@x`rp@@@zS-<%N`2``p@@zC->%N``dpAA@zC-%N`dx"pXXzS-%N`WL[P x`&"p@@zS-<%N```J"p@@zC->%N` `dZ#pAAzC-%N`dx*pXXzS-%N`WL[`x`*p@@zS-<%N```+p@@zC->%N``d2,pAAzC-%N`dxz3pXXzS-%N`:WL[p)x`3p@@@zS-<%N`J``3p@@zC->%N`p`d"5pAA@zC-%N`dxC<pXXzS-%N`WL[9x`Q<p@@zS-<%N```x<p@@zC->%N`8`d=pAAzC-%N`bdx EpXXzS-%N`WL[Ix`Ep@@zS-<%N```BEp@@zC->%N``d\FpAAzC-%N`dxMpXXzS-%N`T WL[Yx`Mp@@zS-<%N`j ``Mp@@zC->%N` `dNpAAzC-%N`p dxUpXXzS-%N`WL[ix`Vp@@zS-<%N```+Vp@@zC->%N``d,WpAAzC-%N`dxa^pXXzS-%N`!WL[yx`w^p@@zS-<%N`7``^p@@zC->%N`Q`dy_pAAzC-%N`9dxfpXX@zS-%N`"WL[x`fp@@zS-<%N`"``fp@@@zC->%N`"`dgpAAzC-%N`#dx.opXXzS-%N`*WL[x`Aop@@zS-<%N`+``]op@@zC->%N`+`d`ppAAzC-%N` ,dxwpXXzS-%N`R3WL[x`wp@@zS-<%N`d3``wp@@zC->%N`~3`dxpAAzC-%N`4dxpXXzS-%N`;WL\x` p@@zS-<%N`;``$p@@zC->%N`;`d)pAAzC-%N`<dxwpXXzS-%N`7DWL\x`p@@zS-<%N`RD``p@@zC->%N`kD`dpAAzC-%N`gEdx pXXzS-%N`LWL\ x`2p@@@zS-<%N`L``p@@zC->%N`M`dpAA@zC-%N`NdxpXXzS-%N`UWL\0x`p@@zS-<%N`U``p@@zC->%N`U`d]pAAzC-%N`WdxpXXzS-%N`p^WL\@x`p@@zS-<%N`~^``p@@zC->%N`^`dpAAzC-%N`_dx`pXXzS-%N` gWL\P x`pp@@@zS-<%N`0g``p@@zC->%N`Wg`dpAA@zC-%N`nhdx pXXzS-%N`oWL\`x`2p@@zS-<%N`o``Pp@@zC->%N`p`d@pAAzC-%N`qdxpXXzS-%N`xWL\p*x`p@@zS-<%N`x``'p@@zC->%N`x`depAAzC-%N`%zdxupXXzS-%N`5WL\:x`p@@zS-<%N`C``p@@zC->%N`j`dpAAzC-%N`dx>pXXzS-%N`WL\Jx`Mp@@zS-<%N` ``up@@zC->%N`5`dpAAzC-%N`YdxpXXzS-%N`WL\Zx`p@@zS-<%N```p@@zC->%N`Ȓ`d3pAAzC-%N`dxpXX@zS-%N`ZWL\jx`p@@zS-<%N`h``p@@@zC->%N``dpAAzC-%N`Ɯdx?pXXzS-%N`WL\zx`Np@@zS-<%N```up@@zC->%N`5`dpAAzC-%N`Zdx pXXzS-%N`ʬWL\x`p@@zS-<%N`׬``?p@@zC->%N``dVpAAzC-%N`dxpXXzS-%N`WL\x`p@@zS-<%N```-p@@zC->%N``dspAAzC-%N`3dxpXXzS-%N`sWL\x`p@@zS-<%N```p@@zC->%N``dpAAzC-%N`dxEpXXzS-%N`WL]x`Vp@@@zS-<%N```sp@@zC->%N`3`dpAA@zC-%N`idx pXXzS-%N`WL]x` p@@zS-<%N``` !p@@zC->%N``d6"pAAzC-%N`dx)pXXzS-%N``WL] x`)p@@zS-<%N`o``)p@@zC->%N``d:+pAAzC-%N`dxq2pXXzS-%N`1WL]0x`2p@@@zS-<%N`?``2p@@zC->%N`g`d3pAA@zC-%N`dx:pXXzS-%N`}WL]@x`:p@@zS-<%N```:p@@zC->%N``d<pAAzC-%N`dxwCpXXzS-%N`7WL]P x`Cp@@zS-<%N`E``Cp@@zC->%N`i`dDpAAzC-%N`dxLpXX@{DwS-%N`WL]`x`Lp@@vDwS-<%N```^Lp@@@{DwC->%N``doMpAAvDwC-%N`/ dxTpXX@rDwS-%N`WL]p+x`Tp@@wDwS-<%N```!Up@@@rDwC->%N``d,VpAAwDwC-%N`dx]pXXzS-%N`NWL];x`]p@@zS-<%N`b``^p@@zC->%N``d_pAAzC-%N`dxfpXXzS-%N`!WL]Kx`fp@@zS-<%N`!``Cfp@@zC->%N`"`dogpAAzC-%N`/#dxnpXXzS-%N`*WL][x`np@@zS-<%N`*``"op@@zC->%N`*`dTppAAzC-%N`,dxwpXX@zS-%N`^3WL]kx`wp@@zS-<%N`k3``wp@@@zC->%N`3`dxpAAzC-%N`4d`kp@@zS-<%N`+<`xpXXzS-%N`x@WL]{x`p@@zC->%N`@`dpAAzC-%N`AdxpXXzS-%N`RIWL]x`p@@zS-<%N`gI``Ǎp@@zC->%N`I`dΎpAAzC-%N`JdxkpXXzS-%N`+RWL]x`|p@@zS-<%N`%N`gR`d̗pAAzC-%N`Sdx!pXXzS-%N`ZWL]x`/p@@zS-<%N`Z``Np@@zC->%N`[`dupAAzC-%N`5\dxpXXzS-%N`pcWL^x`p@@@zS-<%N`c`` p@@zC->%N`c`dpAA@zC-%N`ddxIpXXzS-%N` lWL^x`Yp@@zS-<%N`l``p@@zC->%N`Al`dpAAzC-%N`kmdxpXXzS-%N`tWL^ x`6p@@zS-<%N`t``Up@@zC->%N`u`dpAAzC-%N`HvdxpXXzS-%N`}WL^0x`p@@@zS-<%N`}``p@@zC->%N`}`d?pAA@zC-%N`~dxpXXzS-%N`VWL^@x`p@@zS-<%N`d``p@@zC->%N``d pAAzC-%N`ˇdx;pXXzS-%N`WL^P x`Kp@@zS-<%N` ``kp@@zC->%N`+`dpAAzC-%N`wdxpXXzS-%N`WL^`x`p@@zS-<%N`Η``+p@@zC->%N``dkpAAzC-%N`+dxpXXzS-%N`WL^p,x`p@@zS-<%N```p@@zC->%N``dpAAzC-%N`ǡdxpXXzS-%N`OWL^<x`p@@zS-<%N`l``p@@zC->%N`|`dpAAzC-%N`êdx6pXX@zS-%N`WL^Lx`Gp@@zS-<%N```op@@@zC->%N`/`dpAAzC-%N`Ndx pXXzS-%N`WL^\x` p@@zS-<%N```7 p@@zC->%N``d9 pAAzC-%N`dxpXXzS-%N`WL^lx`p@@zS-<%N```p@@zC->%N``dGpAAzC-%N`dxcpXXzS-%N`#WL^|x`sp@@@zS-<%N`3``p@@zC->%N`Z`dpAA@zC-%N`tdx.&pXXzS-%N`WL^x`>&p@@zS-<%N```^&p@@zC->%N``d'pAAzC-%N`Xdx.pXXzS-%N`WL^x`.p@@zS-<%N```/p@@zC->%N``d70pAAzC-%N`dx7pXXzS-%N`tWL^x`7p@@zS-<%N```7p@@zC->%N``d$9pAAzC-%N`dx?pXXzS-%N`WL_x`?p@@@zS-<%N```@p@@zC->%N``dBApAA@zC-%N`dxHpXXzS-%N`mWL_x`Hp@@zS-<%N`{``Hp@@zC->%N``dJpAAzC-%N`dxgQpXXzS-%N`' WL_ x`vQp@@zS-<%N`6 ``Qp@@zC->%N`^ `dRpAAzC-%N`rdx-ZpXXzS-%N`WL_0x`;Zp@@zS-<%N```[Zp@@zC->%N``d[pAAzC-%N`WdxbpXX@zS-%N`WL_@x`cp@@zS-<%N```/cp@@@zC->%N``ddpAAzC-%N`A dxkpXXzS-%N`E'WL_P x`kp@@zS-<%N`S'``kp@@zC->%N`'`dlpAAzC-%N`(dxEtpXXzS-%N`0WL_`x`Rtp@@zS-<%N`0``vtp@@zC->%N`60`dupAAzC-%N`D1dx|pXXzS-%N`8WL_p-x`|p@@@zS-<%N`8``}p@@zC->%N`8`d~pAA@zC-%N`9dxpXXzS-%N`LAWL_=x`p@@zS-<%N`[A``p@@zC->%N`zA`dpAAzC-%N`BdxJpXXzS-%N` JWL_Mx`Wp@@zS-<%N`J``p@@zC->%N`wJ`dpAAzC-%N`gKdx pXXzS-%N`RWL_]x`p@@zS-<%N`R``9p@@zC->%N`R`dqpAAzC-%N`1Tdx͟pXXzS-%N`[WL_mx`ڟp@@zS-<%N`[``p@@zC->%N`[`dpAAzC-%N`\dxpXXzS-%N`NdWL_}x`p@@@zS-<%N`[d``p@@zC->%N`zd`dpAA@zC-%N`edxRpXXzS-%N`mWL_x`_p@@zS-<%N`m``p@@zC->%N`Cm`dpAAzC-%N`}ndx߹pXXzS-%N`uWL_x`p@@zS-<%N`u``Ap@@zC->%N`v`dpAAzC-%N`vdxpXXzS-%N`_~WL_x`p@@zS-<%N`l~``p@@zC->%N`~`d'pAAzC-%N`dxpXX@zS-%N`wWL`x`p@@zS-<%N```p@@@zC->%N``dpAAzC-%N`١dxkpXXzS-%N`+WL`x`yp@@zS-<%N`9``p@@zC->%N`]`dpAAzC-%N`Ydx*pXXzS-%N`WL` x`6p@@zS-<%N```p@@zC->%N`I`d=pAAzC-%N`dxpXXzS-%N`WL`0x`p@@@zS-<%N```p@@zC->%N`H`dpAA@zC-%N`LdxXpXXzS-%N`WL`@x`fp@@zS-<%N`&``p@@zC->%N``dpAAzC-%N`dxpXXzS-%N`WL`Px`"p@@zS-<%N```wp@@zC->%N`7`drpAAzC-%N`2dxpXXzS-%N`WL``x`p@@zS-<%N```3p@@zC->%N``d5pAAzC-%N`dxX!pXXzS-%N`WL`p.x`e!p@@zS-<%N`%``!p@@zC->%N`x`d"pAAzC-%N`tdx*pXXzS-%N`WL`>x` *p@@@zS-<%N```P*p@@zC->%N``du+pAA@zC-%N`5dx2pXXzS-%N`|WL`Nx`2p@@zS-<%N```3p@@zC->%N``d4pAAzC-%N`dxF;pXXzS-%N`WL`^x`R;p@@zS-<%N```;p@@zC->%N`f`d<pAAzC-%N`UdxDpXXzS-%N`WL`nx`Dp@@zS-<%N```gDp@@zC->%N`'`dCEpAAzC-%N`dxLpXX}DwS-%N`WL`~x`Lp@@vDwS-<%N```*Mp@@}DwC->%N``dENpAAvDwC-%N` dxkUpXX@~DwS-%N`+WL`x`xUp@@@uDwS-<%N`8``Up@@@~DwC->%N`V`dVpAA@uDwC-%N`dx#^pXXyDwS-%N`WL`x`1^p@@pDwS-<%N```q^p@@yDwC->%N`1`d_pAApDwC-%N`@dxfpXXpDwS-%N`"WL`x`fp@@yDwS-<%N`"``gp@@pDwC->%N`"`d(hpAAyDwC-%N`#dx|pXX@uDwS-%N`8WLax`|p@@@~DwS-<%N`8``8}p@@@uDwC->%N`8`dS~pAA@~DwC-%N`:dxpXXvDwS-%N`mAWLax`p@@}DwS-<%N`yA``p@@vDwC->%N`A`d pAA}DwC-%N`BdxkpXXwDwS-%N`+JWLa x`wp@@@rDwS-<%N`7J``p@@wDwC->%N`wJ`dˏpAA@rDwC-%N`KdxpXXvDwS-%N`RWLa0x`p@@@{DwS-<%N`R``Ep@@vDwC->%N`S`dKpAA@{DwC-%N` TdxpXXDwS-%N`l[WLa@x`p@@zDwS-<%N`x[``+p@@DwC->%N`[`d!pAAzDwC-%N`\dx0pXXzDwS-%N`cWLaPx`;p@@DwS-<%N`c``yp@@zDwC->%N`9d`d{pAADwC-%N`;edxpXX@{DwS-%N`lWLa`x`p@@vDwS-<%N`l``8p@@@{DwC->%N`l`d@pAAvDwC-%N`ndxpXX@rDwS-%N`euWLap/x`p@@wDwS-<%N`ou``p@@@rDwC->%N`u`dpAAwDwC-%N`vdx]pXX}DwS-%N`~WLa?x`lp@@vDwS-<%N`,~``p@@}DwC->%N`f~`dpAAvDwC-%N`gdxpXX@~DwS-%N`цWLaOx`p@@@uDwS-<%N`ۆ``Xp@@@~DwC->%N``dYpAA@uDwC-%N`d`p@@yDwS-<%N`N`xpXXpDwS-%N`WLa_x`2p@@pDwC->%N``d?pAAyDwC-%N`dxpXXyDwS-%N`cWLaox`p@@pDwS-<%N`p``p@@yDwC->%N``dpAApDwC-%N`dxhpXX@uDwS-%N`(WLax`up@@@~DwS-<%N`5``p@@@uDwC->%N`v`dpAA@~DwC-%N`dxpXXvDwS-%N`WLax`p@@}DwS-<%N```7p@@vDwC->%N``d4pAA}DwC-%N`dxpXXwDwS-%N`JWLax`p@@@rDwS-<%N`Z``p@@wDwC->%N``dpAA@rDwC-%N`dxpXXvDwS-%N`ҾWLax` p@@@{DwS-<%N````p@@vDwC->%N` `dppAA@{DwC-%N`0dx pXXDwS-%N`WLbx` p@@zDwS-<%N```& p@@DwC->%N``dt pAAzDwC-%N`4dxpXXzDwS-%N`XWLbx`p@@DwS-<%N`e``p@@zDwC->%N``dpAADwC-%N`dx[pXX@{DwS-%N`WLb x`gp@@vDwS-<%N`'``p@@@{DwC->%N`h`dpAAvDwC-%N`xdx&pXX@rDwS-%N`WLb0x` &p@@wDwS-<%N````&p@@@rDwC->%N` `d]'pAAwDwC-%N`dx.pXX}DwS-%N`WLb@x`.p@@vDwS-<%N```/p@@}DwC->%N``d&0pAAvDwC-%N`dx.7pXX@~DwS-%N`WLbPx`;7p@@@uDwS-<%N```7p@@@~DwC->%N`@`d8pAA@uDwC-%N`Mdx?pXXpDwS-%N`WLb` x`?p@@yDwS-<%N```V@p@@pDwC->%N``d(ApAAyDwC-%N`dxHpXXyDwS-%N`WLbp0x`Hp@@pDwS-<%N```Ip@@yDwC->%N``dJpAApDwC-%N`dxQpXX@uDwS-%N`e WLb@x`Qp@@@~DwS-<%N`q ``Qp@@@uDwC->%N` `dSpAA@~DwC-%N`dxcZpXXvDwS-%N`#WLbPx`oZp@@}DwS-<%N`/``Zp@@vDwC->%N`n`d[pAA}DwC-%N`dx7ppXXwDwS-%N`+WLb`x`Cpp@@@rDwS-<%N`,``pp@@wDwC->%N`W,`dqpAA@rDwC-%N`\-dxypXX@zS-%N`4WLbpx`+yp@@zS-<%N`4``Nyp@@@zC->%N`5`dizpAAzC-%N`)6dxpXXzS-%N`=WLbx`p@@zS-<%N`=``4p@@zC->%N`=`d~pAAzC-%N`>?dxpXXzS-%N`xFWLbx`Ŋp@@zS-<%N`F``p@@zC->%N`F`dpAAzC-%N`GdxmpXXzS-%N`-OWLbx`}p@@zS-<%N`=O``p@@zC->%N`yO`dҔpAAzC-%N`PdxpXXzS-%N`WWLbx`+p@@@zS-<%N`W``Hp@@zC->%N`X`d}pAA@zC-%N`=YdxڤpXXzS-%N``WLcx`p@@zS-<%N```` p@@zC->%N```dUpAAzC-%N`bdxgpXXzS-%N`'iWLcx`up@@zS-<%N`5i``ȭp@@zC->%N`i`dѮpAAzC-%N`jdx(pXXzS-%N`qWLc x`6p@@zS-<%N`q``Zp@@zC->%N`r`dpAAzC-%N`MsdxpXXzS-%N`zWLc0x`p@@zS-<%N`z``p@@zC->%N`z`d6pAAzC-%N`{dxpXXzS-%N`mWLc@x`p@@@zS-<%N`y``p@@zC->%N``d1pAA@zC-%N`dxdpXXzS-%N`$WLcPx`pp@@zS-<%N`0``p@@zC->%N``dpAAzC-%N`dxpXXzS-%N`WLc`!x` p@@zS-<%N`˔``,p@@zC->%N``d?pAAzC-%N`dxpXXzS-%N`fWLcp1x`p@@zS-<%N`q`` p@@zC->%N`˝`dpAAzC-%N`ŞdxepXXzS-%N`%WLcAx`sp@@zS-<%N`3``p@@zC->%N`y`dpAAzC-%N`dx0pXX@zS-%N`WLcQx`=p@@zS-<%N````p@@@zC->%N` `dpAAzC-%N`hdxpXXzS-%N`WLcax`p@@zS-<%N```p@@zC->%N``d)pAAzC-%N`dxpXXzS-%N`AWLcqx`p@@zS-<%N`N``p@@zC->%N``dpAAzC-%N`dx@ pXXzS-%N`WLcx`L p@@zS-<%N` `` p@@zC->%N`]`dpAAzC-%N`\dx pXXzS-%N`WLcx`p@@@zS-<%N```5p@@zC->%N``djpAA@zC-%N`*dxpXXvDwS-%N`uWLcx`p@@@{DwS-<%N```p@@vDwC->%N``d pAA@{DwC-%N`dxb'pXX@{DwS-%N`"WLcx`n'p@@vDwS-<%N`.``'p@@@{DwC->%N`n`d(pAAvDwC-%N`dx90pXXvDwS-%N`WLdx`E0p@@@{DwS-<%N```0p@@vDwC->%N`T`d1pAA@{DwC-%N`Idx8pXX@{DwS-%N`WLdx`8p@@vDwS-<%N```79p@@@{DwC->%N``d8:pAAvDwC-%N`dxApXXvDwS-%N`\WLd x`Ap@@@{DwS-<%N`j``Ap@@vDwC->%N``dBpAA@{DwC-%N`dxJpXX@{DwS-%N`WLd0x`"Jp@@vDwS-<%N```bJp@@@{DwC->%N`"`drKpAAvDwC-%N`2dxRpXXvDwS-%N`WLd@x`Rp@@@{DwS-<%N```4Sp@@vDwC->%N``dTpAA@{DwC-%N`dx[pXX@{DwS-%N`QWLdPx`[p@@vDwS-<%N`]``[p@@@{DwC->%N``d\pAAvDwC-%N`dxOdpXXvDwS-%N` WLd`"x`[dp@@@{DwS-<%N` ``dp@@vDwC->%N`X `depAA@{DwC-%N`!dxmpXX@{DwS-%N`(WLdp2x` mp@@vDwS-<%N`(``Hmp@@@{DwC->%N`)`dfnpAAvDwC-%N`&*dxupXXvDwS-%N`J1WLdBx`up@@@{DwS-<%N`V1``up@@vDwC->%N`1`dvpAA@{DwC-%N`2dxM~pXX@{DwS-%N` :WLdRx`X~p@@vDwS-<%N`:``~p@@@{DwC->%N`U:`dpAAvDwC-%N`i;dxpXXvDwS-%N`BWLdbx`p@@@{DwS-<%N`B``Ep@@vDwC->%N`C`dbpAA@{DwC-%N`"DdxpXX@{DwS-%N`gKWLdrx`p@@vDwS-<%N`rK``p@@@{DwC->%N`K`dpAAvDwC-%N`Ldx(pXXvDwS-%N`SWLdx`5p@@@{DwS-<%N`S``vp@@vDwC->%N`6T`dpAA@{DwC-%N`IUdx֠pXX@{DwS-%N`\WLdx`p@@vDwS-<%N`\``.p@@@{DwC->%N`\`d1pAAvDwC-%N`]dxpXXvDwS-%N`JeWLdx`p@@@{DwS-<%N`Ue``Щp@@vDwC->%N`e`dʪpAA@{DwC-%N`fdxFpXX@{DwS-%N`nWLdx`Qp@@vDwS-<%N`n``p@@@{DwC->%N`Nn`dpAAvDwC-%N`jodxpXXvDwS-%N`vWLex`p@@@{DwS-<%N`v``Np@@vDwC->%N`w`dPpAA@{DwC-%N`xdxpXX@{DwS-%N`sWLex`p@@vDwS-<%N`}``p@@@{DwC->%N`D`dpAAvDwC-%N`Bdx\pXXvDwS-%N`WLe x`gp@@@{DwS-<%N`'``p@@vDwC->%N`s`dpAA@{DwC-%N`}dxpXX@{DwS-%N`ԐWLe0x`p@@vDwS-<%N`ސ``_p@@@{DwC->%N``dpAAvDwC-%N`DdxpXXvDwS-%N`WLe@x`p@@@{DwS-<%N``` p@@vDwC->%N`̙`d&pAA@{DwC-%N`dxMpXX@{DwS-%N` WLePx`Xp@@vDwS-<%N```yp@@@{DwC->%N`9`dpAAvDwC-%N`tdxpXXvDwS-%N`ĪWLe`#x`p@@@{DwS-<%N`Ѫ``Lp@@vDwC->%N` `dfpAA@{DwC-%N`&dxpXX@{DwS-%N`{WLep3x`p@@vDwS-<%N```p@@@{DwC->%N``dpAAvDwC-%N`d`Sp@@vDwS-<%N``xpXX@{DwS-%N`[WLeCx`p@@@{DwC->%N``d pAAvDwC-%N`dx pXXvDwS-%N`WLeSx` p@@@{DwS-<%N```V p@@vDwC->%N``dbpAA@{DwC-%N`"dxpXX@{DwS-%N`KWLecx`p@@vDwS-<%N`Y``p@@@{DwC->%N``dpAAvDwC-%N`dxIpXXvDwS-%N` WLesx`Wp@@@{DwS-<%N```p@@vDwC->%N`X`dpAA@{DwC-%N`mdx'pXX@{DwS-%N`WLex`"'p@@vDwS-<%N```b'p@@@{DwC->%N`"`d(pAAvDwC-%N`Pdx/pXXvDwS-%N`WLex`/p@@@{DwS-<%N```#0p@@vDwC->%N``d21pAA@{DwC-%N`dxZ8pXX@{DwS-%N`WLex`h8p@@vDwS-<%N`(``8p@@@{DwC->%N`t`d9pAAvDwC-%N`udx@pXXvDwS-%N`WLex`@p@@@{DwS-<%N```;Ap@@vDwC->%N``dJBpAA@{DwC-%N` dxYIpXX@rDwS-%N` WLfx`fIp@@wDwS-<%N`& ``Ip@@@rDwC->%N`i `dJpAAwDwC-%N`_ dxQpXXwDwS-%N` WLfx`Qp@@@rDwS-<%N` ``Rp@@wDwC->%N` `d.SpAA@rDwC-%N` dxQZpXX@rDwS-%N` WLf x`^Zp@@wDwS-<%N` ``Zp@@@rDwC->%N` `d[pAAwDwC-%N` dxbpXXwDwS-%N`s WLf0x`bp@@@rDwS-<%N` ``cp@@wDwC->%N` `d#dpAA@rDwC-%N` dxwkpXXzS-%N`7' WLf@x`kp@@zS-<%N`T' ``kp@@zC->%N`h' `dlpAAzC-%N`( dx?tpXXzS-%N`/ WLfPx`Otp@@zS-<%N`0 ``ktp@@zC->%N`+0 `dupAAzC-%N`^1 dx}pXXzS-%N`8 WLf`$x`}p@@zS-<%N`8 ``0}p@@zC->%N`8 `dQ~pAAzC-%N`: dxۅpXXzS-%N`A WLfp4x`p@@zS-<%N`A ``Yp@@zC->%N`B `d_pAAzC-%N`C dxpXXzS-%N`eJ WLfDx`p@@@zS-<%N`tJ ``܎p@@zC->%N`J `dpAA@zC-%N`K dxnpXXzS-%N`.S WLfTx`{p@@zS-<%N`;S ``p@@zC->%N`cS `dpAAzC-%N`tT dx5pXXzS-%N`[ WLfdx`Ep@@zS-<%N`\ ``ep@@zC->%N`%\ `dpAAzC-%N`k] dxpXXzS-%N`d WLftx`p@@zS-<%N`d ``'p@@zC->%N`d `dapAAzC-%N`!f dxpXXzS-%N`Pm WLfx`p@@zS-<%N`am ``Ʊp@@zC->%N`m `dpAAzC-%N`n dxEpXX@zS-%N`v WLfx`Rp@@zS-<%N`v ``xp@@@zC->%N`8v `dpAAzC-%N`\w dxpXXzS-%N`~ WLfx`p@@zS-<%N`~ ``7p@@zC->%N`~ `dfpAAzC-%N`& dxpXXzS-%N` WLfx`p@@zS-<%N` ``p@@zC->%N` `dUpAAzC-%N` dxzpXXzS-%N`: WLgx`p@@zS-<%N`I ``p@@zC->%N` `dpAAzC-%N` dx;pXXzS-%N` WLgx`Hp@@@zS-<%N` ``kp@@zC->%N`+ `dpAA@zC-%N`H dxpXXzS-%N` WLg x`p@@zS-<%N`ơ ``*p@@zC->%N` `dDpAAzC-%N` dxpXXzS-%N`} WLg0x`p@@zS-<%N` ``p@@zC->%N` `d.pAAzC-%N` dxpXXzS-%N`? WLg@x`p@@zS-<%N`M ``p@@zC->%N`p `dpAAzC-%N` dx2pXXzS-%N` WLgPx`?p@@zS-<%N` ``bp@@zC->%N`" `dpAAzC-%N`c dxpXXzS-%N` WLg`%x` p@@@zS-<%N` ``( p@@zC->%N` `dX pAA@zC-%N` dxpXXzS-%N`t WLgp5x`p@@zS-<%N` ``p@@zC->%N` `d pAAzC-%N` dxrpXXzS-%N`2 WLgEx`~p@@zS-<%N`> ``p@@zC->%N` `dpAAzC-%N`v dx"pXXzS-%N` WLgUx`"p@@zS-<%N` ``#p@@zC->%N` `d$pAAzC-%N` dx+pXXzS-%N`K WLgex`+p@@zS-<%N`k ``+p@@zC->%N` `d,pAAzC-%N` dxE4pXX@rDwS-%N` WLgux`_4p@@wDwS-<%N` ``}4p@@@rDwC->%N`= `dg5pAAwDwC-%N`' dx<pXX}DwS-%N`c WLgx`<p@@vDwS-<%N`w ``<p@@}DwC->%N` `d>pAAvDwC-%N` dxDpXX@~DwS-%N` WLgx` Ep@@@uDwS-<%N` ``JEp@@@~DwC->%N`  `d_FpAA@uDwC-%N` dxXMpXXpDwS-%N` WLgx`iMp@@yDwS-<%N`) ``Mp@@pDwC->%N`h `dNpAAyDwC-%N`M dxUpXXyDwS-%N`| WLgx`Up@@pDwS-<%N` ``Vp@@yDwC->%N` `d0rpAApDwC-%N`- dxvpXX@uDwS-%N`2 WLhx`vp@@@~DwS-<%N`2 ``wp@@@uDwC->%N`2 `dwpAA@~DwC-%N`3 dxpXXvDwS-%N`S; WLhx`p@@}DwS-<%N`b; ``p@@vDwC->%N`~; `d̀pAA}DwC-%N`< dx4pXXwDwS-%N`C WLh x`Fp@@@rDwS-<%N`D ``fp@@wDwC->%N`&D `dlpAA@rDwC-%N`,E dxpXXDwS-%N`L WLh0x`p@@@{DwS-<%N`L ``p@@DwC->%N`L `dpAA@{DwC-%N`M dxpXX@{DwS-%N`\U WLh@x`p@@DwS-<%N`kU ``p@@@{DwC->%N`U `dpAADwC-%N`vV dx]pXX@rDwS-%N`^ WLhPx`jp@@wDwS-<%N`*^ ``p@@@rDwC->%N`S^ `dpAAwDwC-%N`_ dxpXX}DwS-%N`f WLh`&x`p@@vDwS-<%N`f ``'p@@}DwC->%N`f `d?pAAvDwC-%N`g dxpXXzDwS-%N`_o WLhp6x`p@@vDwS-<%N`lo ``׳p@@zDwC->%N`o `dpAAvDwC-%N`p dxdpXX@~DwS-%N`$x WLhFx`qp@@@uDwS-<%N`1x ``p@@@~DwC->%N`Zx `dȽpAA@uDwC-%N`y dx*pXXpDwS-%N` WLhVx`7p@@yDwS-<%N` ``ap@@pDwC->%N`! `dpAAyDwC-%N`b dxIpXXyDwS-%N` WLhfx`Xp@@pDwS-<%N` ``p@@yDwC->%N`F `dpAApDwC-%N`j dx pXX@uDwS-%N`ɟ WLhvx`p@@@~DwS-<%N`֟ ``?p@@@uDwC->%N` `dgpAA@~DwC-%N`' dxpXXvDwS-%N`D WLhx`p@@zDwS-<%N`Z ``p@@vDwC->%N` `dpAAzDwC-%N` dxpXXvDwS-%N` WLhx`p@@}DwS-<%N` ``*p@@vDwC->%N` `d[pAA}DwC-%N` dxMpXXwDwS-%N` WLhx`_p@@@rDwS-<%N` ``p@@wDwC->%N`L `dpAA@rDwC-%N`p dxpXX@zS-%N` WLhx` p@@zS-<%N` ``4p@@@zC->%N` `dYpAAzC-%N` dxpXXzS-%N`K WLix`p@@zS-<%N`Z ``p@@zC->%N`y `dpAAzC-%N`y dxGpXXzS-%N` WLix`Vp@@zS-<%N` ``tp@@zC->%N`4 `dpAAzC-%N`` dxpXXzS-%N` WLi x`p@@zS-<%N` `` p@@zC->%N` `d(!pAAzC-%N` dxu(pXXzS-%N`5 WLi0x`(p@@@zS-<%N`N ``(p@@zC->%N`k `d)pAA@zC-%N` dx!1pXXzS-%N` WLi@x`31p@@zS-<%N` ``Q1p@@zC->%N` `dl2pAAzC-%N`, d`9p@@zS-<%N` `x">pXXzS-%N` WLiPx`>p@@zC->%N`H `d?pAAzC-%N`w dxFpXXzS-%N` WLi`'x`Fp@@zS-<%N` ``$Gp@@zC->%N` `d>HpAAzC-%N` dxNpXXzS-%N`s WLip7x`Np@@zS-<%N` ``Np@@zC->%N` `dPpAAzC-%N` dx#WpXXzS-%N` WLiGx`5Wp@@@zS-<%N` ``\Wp@@zC->%N` `dmXpAA@zC-%N`- dx_pXXzS-%N` WLiWx` `p@@zS-<%N` ``4`p@@zC->%N` `d`apAAzC-%N`  dxipXXzS-%N`$ WLigx`,ip@@zS-<%N`$ ``Uip@@zC->%N`% `djpAAzC-%N`_& dxGrpXXzS-%N`. WLiwx`Vrp@@zS-<%N`. ``~rp@@zC->%N`>. `dspAAzC-%N``/ dxzpXXzS-%N`6 WLix`zp@@zS-<%N`6 `` {p@@zC->%N`6 `d5|pAAzC-%N`7 dxpXX@zS-%N`h? WLix`p@@zS-<%N`w? ``߃p@@@zC->%N`? `dpAAzC-%N`@ dx?pXXzS-%N`G WLix`Mp@@zS-<%N` H ``vp@@zC->%N`6H `d׍pAAzC-%N`I dxpXXzS-%N`P WLix`p@@zS-<%N`P ``p@@zC->%N`P `dVpAAzC-%N`R dxpXXzS-%N`_Y WLjx`p@@zS-<%N`oY ``؝p@@zC->%N`Y `d pAAzC-%N`Z dxepXXzS-%N`%b WLjx`sp@@@zS-<%N`3b ``p@@zC->%N`Zb `dpAA@zC-%N`{c dxpXXzS-%N`j WLj x`p@@zS-<%N`j ``+p@@zC->%N`j `dfpAAzC-%N`&l dxpXXzS-%N`vs WLj0x`ŷp@@zS-<%N`s ``p@@zC->%N`s `dpAAzC-%N`t dxzpXXzS-%N`:| WLj@x`p@@zS-<%N`H| ``p@@zC->%N`q| `dpAAzC-%N`} dxpXXzS-%N`΄ WLjPx`p@@zS-<%N`܄ ``Dp@@zC->%N` `dnpAAzC-%N`. dxpXXzS-%N` WLj`(x`p@@@zS-<%N` ``p@@zC->%N` `dpAA@zC-%N` dxpXXzS-%N`] WLjp8x`p@@zS-<%N`p ``p@@zC->%N` `dpAAzC-%N` dxspXXzS-%N`3 WLjHx`p@@zS-<%N`C ``p@@zC->%N`n `dpAAzC-%N` dx?pXXzS-%N` WLjXx`Rp@@zS-<%N` ``op@@zC->%N`/ `dpAAzC-%N`g dxpXXzS-%N`İ WLjhx`p@@zS-<%N`Ұ ``;p@@zC->%N` `dkpAAzC-%N`+ dxpXX@zS-%N` WLjxx`p@@zS-<%N` `` p@@@zC->%N`ʹ `dUpAAzC-%N` dxppXXzS-%N`0 WLjx`p@@zS-<%N`D ``p@@zC->%N`` `dpAAzC-%N`| dx(pXXzS-%N` WLjx`8p@@zS-<%N` ``bp@@zC->%N`" `dpAAzC-%N`J dxpXXzS-%N`x WLjx`p@@zS-<%N` ``p@@zC->%N` `d'pAAzC-%N` dx pXXzS-%N`L WLjx` p@@@zS-<%N`\ `` p@@zC->%N`{ `d!pAA@zC-%N` dx#)pXXzS-%N` WLkx`2)p@@zS-<%N` ``)p@@zC->%N`k `d*pAAzC-%N` dx1pXXzS-%N`u WLkx`1p@@zS-<%N` ``1p@@zC->%N` `d 3pAAzC-%N` dx:pXXzS-%N`D WLk x`:p@@zS-<%N`R ``:p@@zC->%N`{ `d;pAAzC-%N` dxPpXXzS-%N` WLk0x`Pp@@zS-<%N` ``3Qp@@zC->%N` `d!RpAAzC-%N` dxYpXXzS-%N`M WLk@ x`Yp@@@zS-<%N`\ ``Yp@@zC->%N` `dZpAA@zC-%N` dxRbpXXzS-%N` WLkPx``bp@@zS-<%N`  ``bp@@zC->%N`E `dcpAAzC-%N`s dxkpXXzS-%N`& WLk`)x`*kp@@zS-<%N`& ``Rkp@@zC->%N`' `dlpAAzC-%N`C( dxyspXXzS-%N`9/ WLkp9x`sp@@zS-<%N`G/ ``sp@@zC->%N`/ `dtpAAzC-%N`0 dxB|pXXzS-%N`8 WLkIx`P|p@@zS-<%N`8 ``w|p@@zC->%N`78 `d}pAAzC-%N`l9 dxpXX@zS-%N`@ WLkYx`p@@zS-<%N`@ ``*p@@@zC->%N`@ `dXpAAzC-%N`B dxpXXzS-%N`I WLkix`̍p@@zS-<%N`I ``p@@zC->%N`I `dpAAzC-%N`J dxpXXzS-%N`IR WLkyx`p@@zS-<%N`VR ``p@@zC->%N`yR `dpAAzC-%N`S dxݞpXXzS-%N`Z WLkx`p@@zS-<%N`Z ``Sp@@zC->%N`[ `dYpAAzC-%N`\ dxpXXzS-%N`cc WLkx`p@@@zS-<%N`sc ``اp@@zC->%N`c `d pAA@zC-%N`d dxfpXXzS-%N`&l WLkx`sp@@zS-<%N`3l ``p@@zC->%N`Vl `dıpAAzC-%N`m dx&pXXzS-%N`t WLkx`3p@@zS-<%N`t ``Vp@@zC->%N`u `dpAAzC-%N`dv dxpXXzS-%N`} WLlx` p@@zS-<%N`} ``p@@zC->%N`E~ `dnpAAzC-%N`. dxpXXzS-%N`m WLlx`p@@zS-<%N`y `` p@@zC->%N`͆ `dpAAzC-%N` dx$pXXzS-%N` WLl x`1p@@@zS-<%N` ``Op@@zC->%N` `d4pAA@zC-%N` dxpXXzS-%N`J WLl0x`p@@zS-<%N`S ``p@@zC->%N`p `dpAAzC-%N` dx;pXXzS-%N` WLl@ x`Gp@@zS-<%N` ``p@@zC->%N`d `dpAAzC-%N`L dxpXXzS-%N` WLlPx` p@@zS-<%N`̨ ``Ep@@zC->%N` `dopAAzC-%N`/ dxpXXzS-%N`m WLl`*x`p@@zS-<%N`z ``p@@zC->%N` `dpAAzC-%N`Dz dx_pXX@zS-%N` WLlp:x`op@@zS-<%N`/ ``p@@@zC->%N`U `dpAAzC-%N`^ dxpXXzS-%N` WLlJx`p@@zS-<%N` ``p@@zC->%N` `dpAAzC-%N` dxpXXzS-%N`x WLlZx`p@@zS-<%N` ``p@@zC->%N` `dpAAzC-%N` dxmpXXzS-%N`- WLljx`zp@@zS-<%N`: ``p@@zC->%N`] `dpAAzC-%N` dx !pXXzS-%N` WLlzx`!p@@@zS-<%N` ``e!p@@zC->%N`% `db"pAA@zC-%N`" dx)pXXzS-%N`X WLlx`)p@@zS-<%N`f ``*p@@zC->%N` `d*pAAzC-%N` dxM2pXXzS-%N` WLlx`Z2p@@zS-<%N` ``~2p@@zC->%N`> `d3pAAzC-%N`n dx!;pXXzS-%N` WLlx`.;p@@zS-<%N` ``;p@@zC->%N`R `d<pAAzC-%N`V dxCpXXzS-%N` WLlx`Cp@@zS-<%N` ``(Dp@@zC->%N` `d)EpAAzC-%N` dxlLpXXzS-%N`, WLmx`xLp@@@zS-<%N`8 ``Lp@@zC->%N` `dMpAA@zC-%N`~ dxUpXXzS-%N` WLmx`-Up@@zS-<%N` ``Up@@zC->%N` `dWpAAzC-%N` dx]pXXzS-%N` WLm x`]p@@zS-<%N` ``=^p@@zC->%N` `d_pAAzC-%N` d`yfp@@zS-<%N`9" `xjpXXzS-%N`& WLm0x`kp@@zC->%N`& `dlpAAzC-%N`' dxzspXXzS-%N`:/ WLm@ x`sp@@zS-<%N`K/ ``sp@@zC->%N`s/ `dtpAAzC-%N`0 dx6|pXX@zS-%N`7 WLmPx`E|p@@zS-<%N`8 ``m|p@@@zC->%N`-8 `d}pAAzC-%N`B9 dxpXXzS-%N`@ WLm`+x`p@@zS-<%N`@ ``$p@@zC->%N`@ `d:pAAzC-%N`A dx,pXXzS-%N`H WLmp;x`9p@@zS-<%N`H ``p@@zC->%N`WI `dopAAzC-%N`/J dxpXXzS-%N`Q WLmKx`p@@zS-<%N`Q ``(p@@zC->%N`Q `dXpAAzC-%N`S dxpXXzS-%N`wZ WLm[x`Şp@@@zS-<%N`Z ``p@@zC->%N`Z `dpAA@zC-%N`[ dxpXXDwS-%N`@c WLmkx`p@@@{DwS-<%N`Uc ``p@@DwC->%N`qc `dpAA@{DwC-%N`d dxpXX@{DwS-%N`k WLm{x`p@@DwS-<%N`k ``+p@@@{DwC->%N`k `d2pAADwC-%N`l dxupXX@rDwS-%N`5t WLmx`p@@wDwS-<%N`Ct ``ĸp@@@rDwC->%N`t `dpAAwDwC-%N`}u dx!pXX}DwS-%N` WLmx`6p@@vDwS-<%N` ``vp@@}DwC->%N`6 `dupAAvDwC-%N`5 dxpXXzDwS-%N`R WLmx`p@@vDwS-<%N`` ``p@@zDwC->%N` `dpAAvDwC-%N` dxUpXX@~DwS-%N` WLmx`dp@@@uDwS-<%N`$ ``p@@@~DwC->%N`f `dpAA@uDwC-%N`u dx pXXpDwS-%N`ɣ WLnx`p@@yDwS-<%N`أ ``8p@@pDwC->%N` `d&pAAyDwC-%N` dxpXXyDwS-%N`Ƭ WLnx`p@@pDwS-<%N`֬ ``Wp@@yDwC->%N` `dYpAApDwC-%N` dxpXX@uDwS-%N`N WLn x`p@@@~DwS-<%N`\ ``p@@@uDwC->%N` `dpAA@~DwC-%N` dxLpXXvDwS-%N` WLn0x`Zp@@zDwS-<%N` ``p@@vDwC->%N` `dpAAzDwC-%N` dx pXXvDwS-%N` WLn@ x` p@@}DwS-<%N` ``& p@@vDwC->%N` `d& pAA}DwC-%N` dxpXXwDwS-%N`[ WLnPx`p@@@rDwS-<%N`i ``p@@wDwC->%N` `dpAA@rDwC-%N` dxZpXXDwS-%N` WLn`,x`hp@@@{DwS-<%N`( ``p@@DwC->%N`h `dpAA@{DwC-%N` dx%pXX@{DwS-%N` WLnp<x`-%p@@DwS-<%N` ``j%p@@@{DwC->%N`* `d&pAADwC-%N`V dx-pXX@rDwS-%N` WLnLx`-p@@wDwS-<%N` ``$.p@@@rDwC->%N` `d/pAAwDwC-%N` dx6pXX}DwS-%N`Q WLn\x`6p@@vDwS-<%N`^ ``6p@@}DwC->%N` `d7pAAvDwC-%N` dxH?pXXzDwS-%N` WLnlx`T?p@@vDwS-<%N` ``?p@@zDwC->%N`S `d@pAAvDwC-%N`K dxHpXX@~DwS-%N`WLn|x`Hp@@@uDwS-<%N```THp@@@~DwC->%N``dIpAA@uDwC-%N`SdxPpXXpDwS-%N` WLnx`Pp@@yDwS-<%N` ``,Qp@@pDwC->%N` `dFRpAAyDwC-%N`dxkYpXXyDwS-%N`+WLnx`wYp@@pDwS-<%N`7``Yp@@yDwC->%N`v`dZpAApDwC-%N`ydx?bpXX@uDwS-%N`WLnx`Kbp@@@~DwS-<%N` ``bp@@@uDwC->%N`J`dcpAA@~DwC-%N`NdxjpXXvDwS-%N`&WLnx`kp@@zDwS-<%N`&``Fkp@@vDwC->%N`'`dlpAAzDwC-%N`@(dxspXXvDwS-%N`E/WLox`sp@@}DwS-<%N`T/``sp@@vDwC->%N`u/`dtpAA}DwC-%N`0dxUpXXwDwS-%N`CWLox`dp@@@rDwS-<%N`$C``p@@wDwC->%N`KC`dpAA@rDwC-%N`_DdxpXXDwS-%N`KWLo x`p@@@{DwS-<%N`K``Hp@@DwC->%N`L`d]pAA@{DwC-%N`MdxјpXX@{DwS-%N`TWLo0x`ߘp@@DwS-<%N`T``p@@@{DwC->%N`T`dpAADwC-%N`UdxpXX@rDwS-%N`K]WLo@ x`p@@wDwS-<%N`W]``šp@@@rDwC->%N`]`dpAAwDwC-%N`^dxEpXX}DwS-%N`fWLoPx`Qp@@vDwS-<%N`f``p@@}DwC->%N`Wf`dǫpAAvDwC-%N`gdxpXXzDwS-%N`nWLo`-x`p@@vDwS-<%N`n``'p@@zDwC->%N`n`dRpAAvDwC-%N`pdxpXX@~DwS-%N`qwWLop=x`p@@@uDwS-<%N`}w``p@@@~DwC->%N`w`d pAA@uDwC-%N`xdxnpXXpDwS-%N`.WLoMx`zp@@yDwS-<%N`:``p@@pDwC->%N`_`d pAAyDwC-%N`́dx0pXXyDwS-%N`WLo]x`=p@@pDwS-<%N```gp@@yDwC->%N`'`dpAApDwC-%N`SdxpXX@uDwS-%N`zWLomx`p@@@~DwS-<%N```p@@@uDwC->%N``dpAA@~DwC-%N`Òdx{pXXvDwS-%N`;WLo}x`p@@zDwS-<%N`J``p@@vDwC->%N`q`dpAAzDwC-%N`dxpXXvDwS-%N`ޢWLox`+p@@}DwS-<%N```Tp@@vDwC->%N``dpAA}DwC-%N`DdxpXXwDwS-%N`WLox`p@@@rDwS-<%N```p@@wDwC->%N`֫`d8pAA@rDwC-%N`dxpXXDwS-%N`XWLox`p@@@{DwS-<%N`d``p@@DwC->%N``dpAA@{DwC-%N`dxMpXX@{DwS-%N` WLox`Zp@@DwS-<%N```p@@@{DwC->%N`D`dpAADwC-%N`dx#pXX@rDwS-%N`WLpx`)#p@@wDwS-<%N```X#p@@@rDwC->%N``d$pAAwDwC-%N`@dx+pXX}DwS-%N`WLpx`+p@@vDwS-<%N```,p@@}DwC->%N``d#-pAAvDwC-%N`dxa4pXXzDwS-%N`!WLp x`m4p@@vDwS-<%N`-``4p@@zDwC->%N`V`d5pAAvDwC-%N`dx!=pXX@~DwS-%N`WLp0x`1=p@@@uDwS-<%N```[=p@@@~DwC->%N``dR>pAA@uDwC-%N`dxEpXXpDwS-%N`WLp@x`Ep@@yDwS-<%N```Fp@@pDwC->%N``dAGpAAyDwC-%N`dxXNpXXyDwS-%N` WLpPx`fNp@@pDwS-<%N`& ``Np@@yDwC->%N`g `dOpAApDwC-%N` dxWpXX@uDwS-%N`WLp`.x`!Wp@@@~DwS-<%N```wWp@@@uDwC->%N`7`dXpAA@~DwC-%N`cdx_pXXvDwS-%N`WLpp>x`_p@@zDwS-<%N````p@@vDwC->%N``dapAAzDwC-%N`dxhpXXvDwS-%N`P$WLpNx`hp@@}DwS-<%N`\$``hp@@vDwC->%N`$`d#jpAA}DwC-%N`%dx}pXXwDwS-%N`M9WLp^x`}p@@@rDwS-<%N`b9``}p@@wDwC->%N`9`d~pAA@rDwC-%N`:dxRpXXDwS-%N`BWLpnx`^p@@@{DwS-<%N`B``p@@DwC->%N``dpAA@{DwC-%N`(dxpXX@{DwS-%N`WLp~x`p@@DwS-<%N```^p@@@{DwC->%N``dupAADwC-%N` dxʗpXX@rDwS-%N`JWLpx`՗p@@wDwS-<%N`U``p@@@rDwC->%N``d2pAAwDwC-%N`dxpXX}DwS-%N` WLpx`p@@vDwS-<%N```Ҡp@@}DwC->%N`R`dpAAvDwC-%N`jdxFpXXzDwS-%N`"WLpx`Qp@@vDwS-<%N`"``p@@zDwC->%N`#`dpAAvDwC-%N`*$dxpXX@~DwS-%N`+WLpx`p@@@uDwS-<%N`#+``ݱp@@@~DwC->%N`]+`d޲pAA@uDwC-%N`^,dxIpXXpDwS-%N`3WLqx`Tp@@yDwS-<%N`3``p@@pDwC->%N`4`dpAAyDwC-%N`5d`p@@yDwS-<%N`f<`x=pXXpDwS-%N`@WLqx`p@@pDwC->%N`A`dpAAyDwC-%N`-BdxpXX@uDwS-%N`%N`I`dpAA@~DwC-%N`Jdx{pXXvDwS-%N`QWLq0x`p@@zDwS-<%N`R``p@@vDwC->%N`KR`dpAAzDwC-%N`_SdxpXXvDwS-%N`ZWLq@x`,p@@}DwS-<%N`Z``kp@@vDwC->%N`Z`dgpAA}DwC-%N`[dxpXXwDwS-%N`#cWLqPx`p@@@rDwS-<%N`0c``p@@wDwC->%N`tc`d pAA@rDwC-%N`ddx-pXXDwS-%N`kWLq`/x`:p@@@{DwS-<%N`k``p@@DwC->%N`k`dpAA@{DwC-%N` mdxpXX@{DwS-%N`9tWLqp?x`p@@DwS-<%N`Ft``p@@@{DwC->%N`t`dpAADwC-%N`udxpXX@rDwS-%N`|WLqOx`(p@@wDwS-<%N`|``op@@@rDwC->%N`|`dpAAwDwC-%N`~dx pXX}DwS-%N`]WLq_x` p@@vDwS-<%N`j``* p@@}DwC->%N``d- pAAvDwC-%N`dxpXXzDwS-%N`WLqox`p@@vDwS-<%N`)``p@@zDwC->%N`j`dpAAvDwC-%N`dx4pXX@~DwS-%N`WLqx`Dp@@@uDwS-<%N`Ė``p@@@~DwC->%N``dpAA@uDwC-%N`dx%pXXyDwS-%N`WLqx`%p@@pDwS-<%N`)``%p@@yDwC->%N`G`d&pAApDwC-%N`adxQ.pXXpDwS-%N`ѧWLqx`_.p@@yDwS-<%N`ߧ``.p@@pDwC->%N`"`d/pAAyDwC-%N`dx7pXX@uDwS-%N`WLqx`'7p@@@~DwS-<%N````7p@@@uDwC->%N``dL8pAA@~DwC-%N`̱dx?pXXvDwS-%N`_WLqx`?p@@zDwS-<%N`l``-@p@@vDwC->%N``d_ApAAzDwC-%N`ߺdxcHpXXvDwS-%N`WLrx`qHp@@}DwS-<%N```Hp@@vDwC->%N`X`dIpAA}DwC-%N`Bdx,QpXXwDwS-%N`WLrx`;Qp@@@rDwS-<%N```Qp@@wDwC->%N``dRpAA@rDwC-%N` dxYpXXDwS-%N`KWLr x`Yp@@@{DwS-<%N`\``Zp@@DwC->%N``d[pAA@{DwC-%N`dx~bpXX@{DwS-%N`WLr0x`bp@@DwS-<%N` ``bp@@@{DwC->%N`>`d%dpAADwC-%N`dxxpXX@rDwS-%N` WLr@x`xp@@wDwS-<%N```xp@@@rDwC->%N`X`dypAAwDwC-%N`^dxLpXX}DwS-%N`WLrP x`\p@@vDwS-<%N```p@@}DwC->%N``dpAAvDwC-%N`0dx pXXzDwS-%N`WLr`0x`p@@vDwS-<%N```Yp@@zDwC->%N``dpAAvDwC-%N`dxĒpXX@~DwS-%N`D WLrp@x`ђp@@@uDwS-<%N`Q ``p@@@~DwC->%N` `dpAA@uDwC-%N` dxVpXXyDwS-%N`WLrPx`cp@@pDwS-<%N```p@@yDwC->%N`%`dpAApDwC-%N`/dxpXXpDwS-%N`WLr`x`#p@@yDwS-<%N```dp@@pDwC->%N``dhpAAyDwC-%N`dxܬpXX@uDwS-%N`\&WLrpx`p@@@~DwS-<%N`i&``*p@@@uDwC->%N`&`dWpAA@~DwC-%N`'dxpXXvDwS-%N` /WLrx`p@@zDwS-<%N`./``̵p@@vDwC->%N`L/`d pAAzDwC-%N`0dx^pXXvDwS-%N`7WLrx`jp@@}DwS-<%N`7``p@@vDwC->%N`+8`dpAA}DwC-%N`%9dx*pXXzS-%N`@WLrx`:p@@zS-<%N`@``p@@zC->%N`A`dpAAzC-%N`BdxpXXzS-%N`jIWLrx`p@@zS-<%N`vI``Jp@@zC->%N`I`d3pAAzC-%N`JdxpXXzS-%N`0RWLrx`p@@zS-<%N`@R``p@@zC->%N`eR`dpAAzC-%N`SdxqpXXzS-%N`ZWLsx`}p@@zS-<%N`Z``p@@zC->%N`![`dpAAzC-%N`<\dx.pXXzS-%N`cWLsx`:p@@@zS-<%N`c``p@@zC->%N`Cd`dpAA@zC-%N`edxpXXzS-%N`olWLs x`p@@zS-<%N`|l``p@@zC->%N`l`d<pAAzC-%N`mdxzpXXzS-%N`tWLs0x`p@@zS-<%N`u``p@@zC->%N`#u`dpAAzC-%N`&vdxpXXwDwS-%N`}WLs@x` p@@@rDwS-<%N`}``?p@@wDwC->%N`}`dFpAA@rDwC-%N`~dx pXX@rDwS-%N`WLsP!x` p@@wDwS-<%N`*`` p@@@rDwC->%N`j`d pAAwDwC-%N`dxapXXwDwS-%N`WLs`1x`mp@@@rDwS-<%N```p@@wDwC->%N`.`dpAA@rDwC-%N`Ddx!pXX@rDwS-%N`WLspAx`.p@@wDwS-<%N```rp@@@rDwC->%N``dpAAwDwC-%N`%dx)3pXXwDwS-%N`WLsQx`83p@@@rDwS-<%N```}3p@@wDwC->%N``d4pAA@rDwC-%N` dx;pXX@rDwS-%N`kWLsax`;p@@wDwS-<%N`|``;<p@@@rDwC->%N``dQ=pAAwDwC-%N`ѶdxDpXXwDwS-%N`,WLsqx`Dp@@@rDwS-<%N`9``Dp@@wDwC->%N`{`dEpAA@rDwC-%N`qdxkMpXX@rDwS-%N`WLsx`xMp@@wDwS-<%N```Mp@@@rDwC->%N`8`dNpAAwDwC-%N`ddx*VpXXwDwS-%N`WLsx`7Vp@@@rDwS-<%N```{Vp@@wDwC->%N``d{WpAA@rDwC-%N`dx^pXX@rDwS-%N`LWLsx`^p@@wDwS-<%N`^``_p@@@rDwC->%N``d`pAAwDwC-%N`dxpgpXXwDwS-%N`WLsx`}gp@@@rDwS-<%N```gp@@wDwC->%N`>`dhpAA@rDwC-%N`Ydx2ppXX@rDwS-%N`WLsx`?pp@@wDwS-<%N```pp@@@rDwC->%N``dqpAAwDwC-%N`+dxxpXXwDwS-%N`gWLtx`xp@@@rDwS-<%N`s``4yp@@wDwC->%N``dEzpAA@rDwC-%N`dxfpXX@rDwS-%N`WLtx`sp@@wDwS-<%N```p@@@rDwC->%N`A`dpAAwDwC-%N`@dxpXXwDwS-%N`wWLt x`p@@@rDwS-<%N```Ep@@wDwC->%N``dXpAA@rDwC-%N`dxpXX@rDwS-%N`= WLt0x`̒p@@wDwS-<%N`L `` p@@@rDwC->%N` `dpAAwDwC-%N` dxvpXXwDwS-%N`WLt@x`p@@@rDwS-<%N```Ûp@@wDwC->%N`C`dpAA@rDwC-%N``d`Kp@@@rDwS-<%N``xpXXwDwS-%N`"WLtP"x`Ϩp@@wDwC->%N`O"`dǩpAA@rDwC-%N`G#dx pXX@rDwS-%N`*WLt`2x`p@@wDwS-<%N`*``Wp@@@rDwC->%N`*`djpAAwDwC-%N`+dxιpXXwDwS-%N`N3WLtpBx`۹p@@@rDwS-<%N`[3``p@@wDwC->%N`3`d5pAA@rDwC-%N`4dxpXX@rDwS-%N` <WLtRx`p@@wDwS-<%N`<``p@@@rDwC->%N`X<`dpAAwDwC-%N`t=dxpXXwDwS-%N`{DWLtbx` p@@@rDwS-<%N`D``Lp@@wDwC->%N`D`d`pAA@rDwC-%N`EdxKpXX@rDwS-%N`LWLtrx`Yp@@wDwS-<%N`L``p@@@rDwC->%N`M`dpAAwDwC-%N`%N`{U`d pAA@rDwC-%N`Vdx pXX@rDwS-%N`]WLtx`5p@@wDwS-<%N`]``^p@@@rDwC->%N`]`drpAAwDwC-%N`^dxpXXwDwS-%N`fWLtx`p@@@rDwS-<%N`f``p@@wDwC->%N`5f`dpAA@rDwC-%N`jgdxpXX@rDwS-%N`hnWLtx`p@@wDwS-<%N`|n``%p@@@rDwC->%N`n`dspAAwDwC-%N`od`Ip@@wDwS-<%N`v`x pXX@rDwS-%N`{WLtx` p@@@rDwC->%N`?{`d pAAwDwC-%N`[|dx pXXwDwS-%N``WLux` p@@@rDwS-<%N`r`` p@@wDwC->%N``dB pAA@rDwC-%N`„dxB pXX@rDwS-%N`‹WLux`S p@@wDwS-<%N`Ӌ`` p@@@rDwC->%N``d pAAwDwC-%N`!dx pXXwDwS-%N`#WLu x` p@@@rDwS-<%N`5`` p@@wDwC->%N`V`d pAA@rDwC-%N`wdx-# pXX@rDwS-%N`WLu0x`H# p@@wDwS-<%N`Ȝ``d# p@@@rDwC->%N``dY$ pAAwDwC-%N`ٝdx+ pXXwDwS-%N`AWLu@x`+ p@@@rDwS-<%N`T``+ p@@wDwC->%N`u`d#- pAA@rDwC-%N`dx4 pXX@rDwS-%N`WLuP#x`4 p@@wDwS-<%N```4 p@@@rDwC->%N`c`d6 pAAwDwC-%N`dx< pXXwDwS-%N`xWLu`3x`= p@@@rDwS-<%N```1= p@@wDwC->%N``dE> pAA@rDwC-%N`ŷdxE pXX@rDwS-%N`IWLupCx`E p@@wDwS-<%N`W``F p@@@rDwC->%N``d!G pAAwDwC-%N`dxWN pXXwDwS-%N`WLuSx`gN p@@@rDwS-<%N```N p@@wDwC->%N` `dO pAA@rDwC-%N`>dxW pXX@rDwS-%N`WLucx`(W p@@wDwS-<%N```UW p@@@rDwC->%N``dX pAAwDwC-%N`$dxk pXXwDwS-%N`2WLusx`k p@@@rDwS-<%N`B``k p@@wDwC->%N`j`dm pAA@rDwC-%N`dxyt pXX@rDwS-%N`WLux`t p@@wDwS-<%N```t p@@@rDwC->%N`2`du pAAwDwC-%N`dxF} pXXwDwS-%N`WLux`T} p@@@rDwS-<%N```} p@@wDwC->%N``d~ pAA@rDwC-%N`/dx pXX@rDwS-%N`WLux` p@@wDwS-<%N```] p@@@rDwC->%N``d pAAwDwC-%N`dxz pXXwDwS-%N`WLux` p@@@rDwS-<%N```Վ p@@wDwC->%N`U`dď pAA@rDwC-%N`D dxܖ pXX@rDwS-%N`\WLux` p@@wDwS-<%N`n``- p@@@rDwC->%N``d= pAAwDwC-%N`dxA pXXwDwS-%N`WLvx`T p@@@rDwS-<%N``` p@@wDwC->%N`h`d pAA@rDwC-%N`udx pXX@rDwS-%N`'!WLvx` p@@wDwS-<%N`9!`` p@@@rDwC->%N`|!`d pAAwDwC-%N`g"dx pXXwDwS-%N`)WLv x` p@@@rDwS-<%N`)``] p@@wDwC->%N`)`dL pAA@rDwC-%N`*dx pXX@rDwS-%N`2WLv0x` p@@wDwS-<%N`2``޸ p@@@rDwC->%N`^2`d pAAwDwC-%N`u3dx pXXwDwS-%N`d:WLv@x` p@@@rDwS-<%N`y:``5 p@@wDwC->%N`:`dB pAA@rDwC-%N`;dxk pXX@rDwS-%N`BWLvP$x` p@@wDwS-<%N`C`` p@@@rDwC->%N`CC`d pAAwDwC-%N`.Ddx pXX'zS-%N`}KWLv`4x` p@@'zS-<%N`K``Y p@@'zC->%N`K`d\ pAA'zC-%N`Ldx pXX'zS-%N`7TWLvpDx` p@@'zS-<%N`HT`` p@@'zC->%N`T`d pAA'zC-%N`Udx pXXwDwS-%N`#]WLvTx` p@@@rDwS-<%N`5]`` p@@wDwC->%N`w]`d pAA@rDwC-%N`^dxY pXX@rDwS-%N`eWLvdx`k p@@wDwS-<%N`e`` p@@@rDwC->%N`f`d pAAwDwC-%N`4gdx pXXwDwS-%N`nWLvtx`6 p@@@rDwS-<%N`n`` p@@wDwC->%N`o`dp pAA@rDwC-%N`odx pXXwDwS-%N`QwWLvx` p@@DwS-<%N`aw`` p@@wDwC->%N`w`d7 pAADwC-%N`xdx!pXX@{DwS-%N`WLvx`!p@@DwS-<%N```!p@@@{DwC->%N`=`d!pAADwC-%N`idxT!pXXwDwS-%N`ԈWLvx`b!p@@@rDwS-<%N```!p@@wDwC->%N``d!pAA@rDwC-%N`7dx,!pXX'zS-%N`WLvx`=!p@@@'zS-<%N```h!p@@'zC->%N``d!pAA@'zC-%N` dx.!pXX'zS-%N`WLvx`*.!p@@'zS-<%N```W.!p@@'zC->%N`ק`d/!pAA'zC-%N`dx6!pXX'zS-%N`-WLwx`6!p@@'zS-<%N`;``6!p@@'zC->%N`e`d7!pAA'zC-%N`tdx?!pXX}DwS-%N`WLwx`?!p@@vDwS-<%N```?!p@@}DwC->%N`A`d@!pAAvDwC-%N`YdxIH!pXXvDwS-%N`WLw x`WH!p@@}DwS-<%N```H!p@@vDwC->%N``dI!pAA}DwC-%N`dxQ!pXX}DwS-%N`WLw0x`$Q!p@@vDwS-<%N```NQ!p@@}DwC->%N``dR!pAAvDwC-%N` dxY!pXXvDwS-%N`dWLw@x`Y!p@@}DwS-<%N`r``Z!p@@vDwC->%N``dM[!pAA}DwC-%N`dxb!pXX}DwS-%N`#WLwP%x`b!p@@vDwS-<%N`1``b!p@@}DwC->%N`e`dc!pAAvDwC-%N`pdxbk!pXXvDwS-%N`WLw`5x`pk!p@@}DwS-<%N```k!p@@vDwC->%N``dl!pAA}DwC-%N`/dx%t!pXX}DwS-%N`WLwpEx`3t!p@@vDwS-<%N```gt!p@@}DwC->%N``du!pAAvDwC-%N`.dx|!pXXvDwS-%N`\WLwUx`|!p@@}DwS-<%N`m``}!p@@vDwC->%N``d.~!pAA}DwC-%N`dx!pXX}DwS-%N`WLwex`!p@@vDwS-<%N`-``q!p@@}DwC->%N``dU!pAAvDwC-%N`dx\!pXXvDwS-%N`WLwux`j!p@@}DwS-<%N```!p@@vDwC->%N``d!pAA}DwC-%N`- dx$!pXX}DwS-%N`WLwx`2!p@@vDwS-<%N```\!p@@}DwC->%N``d!pAAvDwC-%N`'dx!pXXvDwS-%N`gWLwx`!p@@}DwS-<%N`t``"!p@@vDwC->%N``d-!pAA}DwC-%N`dx=!pXX}DwS-%N`!WLwx`J!p@@vDwS-<%N`!``t!p@@}DwC->%N`!`d!pAAvDwC-%N`#dx!pXXvDwS-%N`*WLwx`!p@@}DwS-<%N`*``0!p@@vDwC->%N`*`dc!pAA}DwC-%N`+dx!pXX}DwS-%N`3WLwx`!p@@vDwS-<%N`3``ƹ!p@@}DwC->%N`F3`d!pAAvDwC-%N`Odxh!pXXvDwS-%N`TWLxx`t!p@@}DwS-<%N`T``!p@@vDwC->%N`!U`d!pAA}DwC-%N`1Vdx&!pXX}DwS-%N`]WLxx`3!p@@vDwS-<%N`]``^!p@@}DwC->%N`]`dp!pAAvDwC-%N`^dx!pXXvDwS-%N`ifWLx x`!p@@}DwS-<%N`xf``"!p@@vDwC->%N`f`de!pAA}DwC-%N`gdx!pXX}DwS-%N`(oWLx0x`!p@@vDwS-<%N`4o``!p@@}DwC->%N`^o`d!pAAvDwC-%N`pdxg!pXXvDwS-%N`wWLx@x`t!p@@}DwS-<%N`w``!p@@vDwC->%N`#x`d!pAA}DwC-%N`3ydx"pXX}DwS-%N`WLxP&x` "p@@vDwS-<%N```V"p@@}DwC->%N`ր`dv"pAAvDwC-%N`dx"pXXvDwS-%N`(WLx`6x`"p@@}DwS-<%N`5``"p@@vDwC->%N`]`d"pAA}DwC-%N`Tdx"pXX@'zS-%N`WLxpFx`"p@@'zS-<%N```"p@@@'zC->%N`3`d"pAA'zC-%N`9dx "pXX'zS-%N`=WLxVx` "p@@'zS-<%N`K``7!"p@@'zC->%N``d,""pAA'zC-%N`dxk)"pXX'zS-%N`WLxfx`y)"p@@'zS-<%N```)"p@@'zC->%N`(`d*"pAA'zC-%N`Sdx1"pXX'zS-%N`aWLxvx`1"p@@@'zS-<%N`m``2"p@@'zC->%N``d43"pAA@'zC-%N`dxw:"pXX@'zS-%N`WLxx`:"p@@'zS-<%N```:"p@@@'zC->%N`>`d;"pAA'zC-%N`RdxC"pXX'zS-%N`WLxx`+C"p@@@'zS-<%N```[C"p@@'zC->%N`ۼ`dD"pAA@'zC-%N`d`K"p@@@'zS-<%N`*`xO"pXX'zS-%N`KWLxx`P"p@@'zC->%N``d0Q"pAA@'zC-%N`dxe"pXX}DwS-%N`WLxx`e"p@@vDwS-<%N` ``e"p@@}DwC->%N`I`df"pAAvDwC-%N`xdxcn"pXXzDwS-%N`WLxx`rn"p@@vDwS-<%N```n"p@@zDwC->%N``do"pAAvDwC-%N`Gdx w"pXX@'zS-%N`WLyx`1w"p@@'zS-<%N```Pw"p@@@'zC->%N``d8x"pAA'zC-%N`dxz"pXX'zS-%N`WLyx`"p@@@'zS-<%N` ``"p@@'zC->%N`/`d"pAA@'zC-%N`*dx"pXX@'zS-%N`WLy x`"p@@'zS-<%N```I"p@@@'zC->%N``df"pAA'zC-%N`dx"pXX'zS-%N`, WLy0x`"p@@@'zS-<%N`: ``"p@@'zC->%N`n `d"pAA@'zC-%N`u dxj"pXX@'zS-%N`WLy@x`x"p@@'zS-<%N```"p@@@'zC->%N`1`dښ"pAA'zC-%N`Zdx4"pXX'zS-%N`WLyP'x`D"p@@@'zS-<%N```{"p@@'zC->%N``d"pAA@'zC-%N`,dx"pXX'zS-%N`p$WLy`7x`"p@@'zS-<%N`$``!"p@@'zC->%N`$`d"pAA'zC-%N`%dx"pXX'zS-%N`'-WLypGx`"p@@'zS-<%N`9-``ֳ"p@@'zC->%N`V-`dԴ"pAA'zC-%N`T.dxX"pXX'zS-%N`5WLyWx`i"p@@'zS-<%N`5``"p@@'zC->%N`6`dн"pAA'zC-%N`P7dx*"pXX'zS-%N`>WLygx`>"p@@'zS-<%N`>``x"p@@'zC->%N`>`du"pAA'zC-%N`?dx"pXX'zS-%N`iGWLywx`"p@@'zS-<%N`xG``5"p@@'zC->%N`G`d9"pAA'zC-%N`Hdx"pXX'zS-%N`2PWLyx`"p@@'zS-<%N`@P``"p@@'zC->%N`P`d"pAA'zC-%N`Qdxx"pXX'zS-%N`XWLyx`"p@@'zS-<%N`Y``"p@@'zC->%N`GY`d"pAA'zC-%N`]Zdx9"pXX'zS-%N`aWLyx`G"p@@'zS-<%N`a``"p@@'zC->%N` b`d"pAA'zC-%N`cdx"pXX'zS-%N`GjWLyx`"p@@'zS-<%N`Uj``"p@@'zC->%N`j`d"pAA'zC-%N`kdx"pXX'zS-%N`sWLyx`"p@@'zS-<%N`s``"p@@'zC->%N`Vs`d"pAA'zC-%N`ntdxM#pXX'zS-%N`{WLzx`[#p@@'zS-<%N`{``#p@@'zC->%N`|`d#pAA'zC-%N`:}dx# #pXX'zS-%N`WLzx`3 #p@@'zS-<%N``` #p@@'zC->%N``d #pAA'zC-%N` dx#pXX'zS-%N`}WLz x`#p@@'zS-<%N```S#p@@'zC->%N`Ӎ`df#pAA'zC-%N`dx#pXX'zS-%N`9WLz0x`#p@@'zS-<%N`G``#p@@'zC->%N``d#pAA'zC-%N`dxt%#pXX'zS-%N`WLz@x`%#p@@'zS-<%N```%#p@@'zC->%N`F`d&#pAA'zC-%N`edx7.#pXX'zS-%N`WLzP(x`C.#p@@'zS-<%N`ç``.#p@@'zC->%N``d/#pAA'zC-%N`dx6#pXX'zS-%N`}WLz`8x` 7#p@@'zS-<%N```Y7#p@@'zC->%N`ٰ`d:8#pAA'zC-%N`dx?#pXX'zS-%N`1WLzpHx`?#p@@'zS-<%N`=``?#p@@'zC->%N`~`d@#pAA'zC-%N`|dxpH#pXX'zS-%N`WLzXx`|H#p@@'zS-<%N```H#p@@'zC->%N`>`d J#pAA'zC-%N`dx#Q#pXX'zS-%N`WLzhx`/Q#p@@'zS-<%N```LQ#p@@'zC->%N``dR#pAA'zC-%N`!dxY#pXX'zS-%N` WLzxx`Y#p@@'zS-<%N`,``Y#p@@'zC->%N`l`d[#pAA'zC-%N`dxab#pXX'zS-%N`WLzx`pb#p@@'zS-<%N```b#p@@'zC->%N`/`dc#pAA'zC-%N`Mdx&k#pXX'zS-%N`WLzx`2k#p@@'zS-<%N```vk#p@@'zC->%N``dl#pAA'zC-%N`dxs#pXX'zS-%N`_WLzx`s#p@@'zS-<%N`k``,t#p@@'zC->%N``dLu#pAA'zC-%N`dx|#pXX'zS-%N` WLzx`|#p@@'zS-<%N`-``|#p@@'zC->%N`n`d}#pAA'zC-%N`kdxY#pXX'zS-%N`WLzx`e#p@@'zS-<%N```#p@@'zC->%N`$`d#pAA'zC-%N`#dxP#pXX'zS-%N`WL{x`\#p@@'zS-<%N```#p@@'zC->%N``d#pAA'zC-%N`,dx#pXX'zS-%N`WL{x`#p@@'zS-<%N````#p@@'zC->%N``dq#pAA'zC-%N`dx#pXX'zS-%N`&WL{ x`#p@@'zS-<%N`-&``#p@@'zC->%N`m&`d#pAA'zC-%N`m'dx]#pXX'zS-%N`.WL{0 x`j#p@@'zS-<%N`.``#p@@'zC->%N`,/`dn#pAA'zC-%N`/dx#pXX'zS-%N`7WL{@x`#p@@'zS-<%N`7``^#p@@'zC->%N`7`d#pAA'zC-%N`9dx#pXX'zS-%N`@WL{P)x`#p@@'zS-<%N`,@``#p@@'zC->%N`@`d#pAA'zC-%N`oAdxL#pXX'zS-%N`HWL{`9x`X#p@@'zS-<%N`H``#p@@'zC->%N`I`d#pAA'zC-%N`Jdx#pXX'zS-%N`zQWL{pIx`#p@@'zS-<%N`Q``[#p@@'zC->%N`Q`dU#pAA'zC-%N`Rdx#pXX'zS-%N`9ZWL{Yx`#p@@'zS-<%N`EZ``#p@@'zC->%N`Z`d#pAA'zC-%N`[dxt#pXX'zS-%N`bWL{ix`#p@@'zS-<%N`c``#p@@'zC->%N`?c`d#pAA'zC-%N`Yddx/#pXX'zS-%N`kWL{yx`;#p@@'zS-<%N`k``z#p@@'zC->%N`k`d#pAA'zC-%N` mdx#pXX'zS-%N`3tWL{x`#p@@'zS-<%N`>t``#p@@'zC->%N`{t`d #pAA'zC-%N`udx$pXX'zS-%N`|WL{x`$p@@'zS-<%N`|``S$p@@'zC->%N`|`d?$pAA'zC-%N`}dx $pXX'zS-%N`3WL{x` $p@@'zS-<%N`>`` $p@@'zC->%N`{`dX $pAA'zC-%N`؆dxg$pXX'zS-%N`WL{x`t$p@@'zS-<%N```$p@@'zC->%N``d$pAA'zC-%N`_dx$pXX'zS-%N`WL{x`$p@@'zS-<%N```]$p@@'zC->%N`ݖ`dZ$pAA'zC-%N`ڗdx%$pXX'zS-%N`*WL|x`%$p@@'zS-<%N`4``%$p@@'zC->%N`R`d&$pAA'zC-%N`qdxg.$pXX'zS-%N`WL|x`r.$p@@'zS-<%N```.$p@@'zC->%N`1`d/$pAA'zC-%N`Bdx 7$pXX'zS-%N`WL| x`+7$p@@'zS-<%N```|7$p@@'zC->%N``d8$pAA'zC-%N`dx?$pXX'zS-%N`^WL|0 x`?$p@@'zS-<%N`i``)@$p@@'zC->%N``d=A$pAA'zC-%N`dxbH$pXX'zS-%N`WL|@x`mH$p@@'zS-<%N```H$p@@'zC->%N`*`dI$pAA'zC-%N`>dxQ$pXX'zS-%N`WL|P*x`*Q$p@@'zS-<%N```hQ$p@@'zC->%N``dzR$pAA'zC-%N`dxY$pXX'zS-%N`^WL|`:x`Y$p@@'zS-<%N`j``0Z$p@@'zC->%N``dy[$pAA'zC-%N`dxb$pXX'zS-%N`WL|pJx`b$p@@'zS-<%N```b$p@@'zC->%N`d`dd$pAA'zC-%N`dxVk$pXX'zS-%N`WL|Zx`ak$p@@'zS-<%N```k$p@@'zC->%N`"`dl$pAA'zC-%N`>dxt$pXX'zS-%N`WL|jx`t$p@@'zS-<%N```Mt$p@@'zC->%N``d_u$pAA'zC-%N`d`|$p@@'zS-<%N`&`x$pXX'zS-%N`WL|zx`/$p@@'zC->%N``dU$pAA'zC-%N`dx$pXX'zS-%N`WL|x`$p@@'zS-<%N`"``$p@@'zC->%N`d`d$pAA'zC-%N`odx$pXX'zS-%N` WL|x`$p@@'zS-<%N` ``a$p@@'zC->%N` `dl$pAA'zC-%N` dxǚ$pXX'zS-%N`GWL|x`Ӛ$p@@'zS-<%N`S``$p@@'zC->%N``d $pAA'zC-%N`dxt$pXX'zS-%N`WL|x`$p@@'zS-<%N```£$p@@'zC->%N`B`d$pAA'zC-%N`adx9$pXX'zS-%N`%WL|x`I$p@@'zS-<%N`%``h$p@@'zC->%N`%`d$pAA'zC-%N`7'dx$pXX'zS-%N`q.WL}x`$p@@'zS-<%N`.``0$p@@'zC->%N`.`d5$pAA'zC-%N`/dx$pXX'zS-%N`)7WL}x`$p@@'zS-<%N`77``ս$p@@'zC->%N`U7`d־$pAA'zC-%N`V8dx$pXX'zS-%N`?WL} x`$p@@'zS-<%N`?``s$p@@'zC->%N`?`da$pAA'zC-%N`@dx$pXX'zS-%N`HHWL}0 x`$p@@'zS-<%N`UH``$p@@'zC->%N`H`d$pAA'zC-%N`{Idxx$pXX'zS-%N`PWL}@x`$p@@'zS-<%N`Q``$p@@'zC->%N`EQ`d$pAA'zC-%N`wRdx$pXX'zS-%N`aYWL}P+x`$p@@'zS-<%N`nY``V$p@@'zC->%N`Y`d\$pAA'zC-%N`Zdx$pXX'zS-%N`bWL}`;x`$p@@'zS-<%N`b``$p@@'zC->%N``b`d$pAA'zC-%N`rcdxB$pXX'zS-%N`jWL}pKx`P$p@@'zS-<%N`j``$p@@'zC->%N`k`d$pAA'zC-%N`ldx$pXX'zS-%N`RsWL}[x`$p@@'zS-<%N`_s``'$p@@'zC->%N`s`d$pAA'zC-%N`tdx%pXX'zS-%N`|WL}kx`%p@@'zS-<%N`|``%p@@'zC->%N``|`d%pAA'zC-%N`}dxP %pXX'zS-%N`ЄWL}{x`] %p@@'zS-<%N`݄`` %p@@'zC->%N` `d %pAA'zC-%N`.dx%pXX'zS-%N`_WL}x`%p@@'zS-<%N`l``-%p@@'zC->%N``dU%pAA'zC-%N`Վdx%pXX'zS-%N`WL}x`%p@@'zS-<%N`+``%p@@'zC->%N`l`d%pAA'zC-%N`cdx#%%pXX'zS-%N`WL}x`0%%p@@'zS-<%N```r%%p@@'zC->%N``d&%pAA'zC-%N`dx-%pXX@'zS-%N`FWL}x`-%p@@'zS-<%N`V``".%p@@@'zC->%N``d'/%pAA'zC-%N`dxt6%pXX'zS-%N`WL}x`6%p@@'zS-<%N```6%p@@'zC->%N`G`d7%pAA'zC-%N`Gdx&?%pXX'zS-%N`WL~x`4?%p@@'zS-<%N```x?%p@@'zC->%N``dx@%pAA'zC-%N`dxT%pXX@'zS-%N`sWL~x`T%p@@'zS-<%N```CU%p@@@'zC->%N``d`V%pAA'zC-%N`dxv]%pXX'zS-%N`WL~ x`]%p@@'zS-<%N```]%p@@'zC->%N`F`d^%pAA'zC-%N`Ydx1f%pXX@'zS-%N`WL~0 x`?f%p@@'zS-<%N```f%p@@@'zC->%N``dvg%pAA'zC-%N`dxo%pXX'zS-%N`WL~@x`o%p@@'zS-<%N```bo%p@@'zC->%N``dkp%pAA'zC-%N`dxw%pXX'zS-%N`<WL~P,x`w%p@@'zS-<%N`I``x%p@@'zC->%N``d#y%pAA'zC-%N`dxu%pXX@'zS-%N`WL~`<x`%p@@'zS-<%N```р%p@@@'zC->%N`Q`d%pAA'zC-%N`idx%%pXX'zS-%N`WL~pLx`2%p@@'zS-<%N```u%p@@'zC->%N``dL%pAA'zC-%N`dx%pXX@'zS-%N`f WL~\x`%p@@'zS-<%N`u ``%p@@@'zC->%N`" `d%pAA'zC-%N` dx%pXX'zS-%N` WL~lx`%p@@'zS-<%N```%p@@'zC->%N`5`d%pAA'zC-%N`{dx5%pXX'zS-%N`WL~|x`A%p@@'zS-<%N```%p@@'zC->%N` `d%pAA'zC-%N`dxѫ%pXX@'zS-%N`Q%WL~x`ܫ%p@@'zS-<%N`\%``%p@@@'zC->%N`%`d,%pAA'zC-%N`&dx%pXX'zS-%N`.WL~x`%p@@'zS-<%N` .``ϴ%p@@'zC->%N`O.`dȵ%pAA'zC-%N`H/dx,%pXX@'zS-%N`6WL~x`7%p@@'zS-<%N`6``z%p@@@'zC->%N`6`ds%pAA'zC-%N`7dx%pXX'zS-%N`V?WL~x`%p@@'zS-<%N`a?``$%p@@'zC->%N`?`dU%pAA'zC-%N`@dxt%pXX'zS-%N`GWL~x`%p@@'zS-<%N`G``%p@@'zC->%N`BH`d%pAA'zC-%N`UIdx%pXX@'zS-%N`PWLx`)%p@@'zS-<%N`P``l%p@@@'zC->%N`P`df%pAA'zC-%N`Qdx%pXX'zS-%N`HYWLx`%p@@'zS-<%N`TY``%p@@'zC->%N`Y`d+%pAA'zC-%N`Zdxk%pXX@'zS-%N`aWL x`w%p@@'zS-<%N`a``%p@@@'zC->%N`4b`d%pAA'zC-%N`hcdx%pXX'zS-%N`jWL0 x`'%p@@'zS-<%N`j``h%p@@'zC->%N`j`d%pAA'zC-%N`ldx%pXX'zS-%N`"sWL@x`%p@@'zS-<%N`-s``%p@@'zC->%N`ns`d%pAA'zC-%N`tdx\&pXX@'zS-%N`{WLP-x`i&p@@'zS-<%N`{``&p@@@'zC->%N`'|`d&pAA'zC-%N`@}dx &pXX'zS-%N`WL`=x` &p@@'zS-<%N```Z &p@@'zC->%N`ڄ`dw &pAA'zC-%N`dx&pXX@'zS-%N`KWLpMx`&p@@'zS-<%N`V``&p@@@'zC->%N``d0&pAA'zC-%N`dx&pXX'zS-%N`WL]x`&p@@'zS-<%N```&p@@'zC->%N`^`d&pAA'zC-%N`\dxM%&pXX'zS-%N`͞WLmx`Y%&p@@'zS-<%N`ٞ``%&p@@'zC->%N``d&&pAA'zC-%N`dx-&pXX@'zS-%N`[WL}x`-&p@@'zS-<%N`g``%.&p@@@'zC->%N``d&/&pAA'zC-%N`dx6&pXX'zS-%N`WLx`6&p@@'zS-<%N```6&p@@'zC->%N`Y`d 8&pAA'zC-%N`dxB?&pXX@'zS-%N`¸WLx`N?&p@@'zS-<%N`θ``?&p@@@'zC->%N` `d@&pAA'zC-%N`$dxG&pXX'zS-%N`EWLx`G&p@@'zS-<%N`O``H&p@@'zC->%N``dI&pAA'zC-%N`dxiP&pXXwS-%N`WLx`uP&p@@wS-<%N```P&p@@wC->%N``dQ&pAAwC-%N`5dxY&pXXwS-%N`WLx`%Y&p@@wS-<%N```vY&p@@wC->%N``du&pAAwC-%N`dx"{&pXXwS-%N`WPx`/{&p@@@wS-<%N```{&p@@wC->%N``d&pAA@wC-%N` dxf&pXXmzS-%N`& WP x`w&p@@mzS-<%N`& ``&p@@mzC->%N`>' `dٮ&pAAmzC-%N`Y( dx/&pXXmzS-%N`/ WP x`>&p@@mzS-<%N`/ ``|&p@@mzC->%N`/ `d&pAAmzC-%N`1 dx&pXXmzS-%N`8 WP0x`&&p@@mzS-<%N`8 ``B&p@@mzC->%N`8 `dZ&pAAmzC-%N`9 dx&pXXmzS-%N`A WP@x`&p@@@mzS-<%N`A ``_&p@@mzC->%N`A `dh&pAA@mzC-%N`B dx&pXX@mzS-%N`IJ WPPZblx`&p@@mzS-<%N`YJ ``"&p@@@mzC->%N`J `d&pAAmzC-%N`K dx&pXXmzS-%N` S WP` bx`&p@@@mzS-<%N`S ``&p@@mzC->%N`_S `d&pAA@mzC-%N`ST dxL&pXX@mzS-%N`[ WPpx`\&p@@mzS-<%N`[ ``&p@@@mzC->%N`&\ `d&pAAmzC-%N`u] d`&p@@mzS-<%N`]d `x>&pXXmzS-%N`h WPXx`&p@@mzC->%N`Ai `d&pAAmzC-%N`?j dx'pXXmzS-%N`} WP l x`('p@@mzS-<%N`} ``C'p@@mzC->%N`} `d8'pAAmzC-%N`~ dx 'pXXMwS-%N`) WP x` 'p@@MwS-<%N`8 `` 'p@@MwC->%N`z `d 'pAAMwC-%N`w dx'pXXMwS-%N` WP *x`'p@@MwS-<%N` ``'p@@MwC->%N`= `d'pAAMwC-%N` dxN'pXXMwS-%N`Η WP Zbx`\'p@@MwS-<%N`ܗ ``'p@@MwC->%N`# `d'pAAMwC-%N` dx&'pXXMwS-%N`^ WPlx`&'p@@MwS-<%N`l ``.''p@@MwC->%N` `d?('pAAMwC-%N` dx/'pXXMwS-%N`( WP Yx`/'p@@MwS-<%N`6 ``/'p@@MwC->%N`z `d0'pAAMwC-%N`n dxe8'pXXMwS-%N` WPcx`r8'p@@MwS-<%N` ``8'p@@MwC->%N` `d@'pAAMwC-%N`b dxI'pXXMwS-%N`v WPsx`J'p@@MwS-<%N` ``EJ'p@@MwC->%N` `dBK'pAAMwC-%N` dxR'pXX@MwS-%N`K WP x`R'p@@MwS-<%N`Y ``S'p@@@MwC->%N` `d0T'pAAMwC-%N` dx~['pXXMwS-%N` WP0x`['p@@@MwS-<%N` ``['p@@MwC->%N`N `d]'pAA@MwC-%N` dxJd'pXXMwS-%N` WP@x`\d'p@@@MwS-<%N` ``d'p@@MwC->%N`" `de'pAA@MwC-%N`. dxm'pXX@MwS-%N` WPPx`m'p@@MwS-<%N` ``Tm'p@@@MwC->%N` `d_n'pAAMwC-%N` dxu'pXXMwS-%N` WP`x`u'p@@@MwS-<%N`- ``u'p@@MwC->%N`n `dv'pAA@MwC-%N`u dxW~'pXX@MwS-%N` WPpx`g~'p@@MwS-<%N` ``~'p@@@MwC->%N` `d'pAAMwC-%N` dxՆ'pXXMwS-%N`U WPx`'p@@@MwS-<%N`d ``%'p@@MwC->%N` `d'pAA@MwC-%N` dxE'pXX@MwS-%N` WPx`T'p@@MwS-<%N` ``r'p@@@MwC->%N` `d}'pAAMwC-%N` dx'pXXMwS-%N`h WPx`'p@@@MwS-<%N`v ``7'p@@MwC->%N` `dB'pAA@MwC-%N` dx'pXX@MwS-%N`  WPx`'p@@MwS-<%N` ``۠'p@@@MwC->%N`[ `d'pAAMwC-%N`k dx 'pXXMwS-%N`" WP#x`'p@@@MwS-<%N`" ``['p@@MwC->%N`" `dO'pAA@MwC-%N`# dx±'pXX@MwS-%N`B+ WP3x`ұ'p@@MwS-<%N`R+ ``'p@@@MwC->%N`+ `d'pAAMwC-%N`, dx'pXXMwS-%N`3 WPCx`+'p@@@MwS-<%N`3 ``k'p@@MwC->%N`3 `d'pAA@MwC-%N`5 dx'pXX@MwS-%N`< WPSx`'p@@MwS-<%N`< ``'p@@@MwC->%N`>< `d'pAAMwC-%N`!= dx:'pXXMwS-%N`D WPdx`H'p@@@MwS-<%N`D ``f'p@@MwC->%N`D `di'pAA@MwC-%N`E dx'pXX@MwS-%N`rM WPtx` 'p@@MwS-<%N`M ``'p@@@MwC->%N`N `dm'pAAMwC-%N`N dx'pXXMwS-%N`V WP x`'p@@@MwS-<%N` V ``'p@@MwC->%N``V `d'pAA@MwC-%N`W dxQ'pXX@MwS-%N`^ WP0x`^'p@@MwS-<%N`^ ``'p@@@MwC->%N`F_ `d'pAAMwC-%N`S` dx'pXXMwS-%N`_g WP@x`'p@@@MwS-<%N`kg ``,'p@@MwC->%N`g `dB'pAA@MwC-%N`h dx'pXX@MwS-%N`p WPPx`'p@@MwS-<%N`,p ``'p@@@MwC->%N`kp `d'pAAMwC-%N`q dx!'pXXMwS-%N`x WP`x`.'p@@@MwS-<%N`x ``n'p@@MwC->%N`x `d(pAA@MwC-%N`z dx(pXX@MwS-%N` WPpx`(p@@MwS-<%N`. ``(p@@@MwC->%N`l `d (pAAMwC-%N` dx.(pXXMwS-%N` WPx`:(p@@@MwS-<%N` ``~(p@@MwC->%N` `d(pAA@MwC-%N` dx(pXX@MwS-%N`N WPx`(p@@MwS-<%N`Z ``!(p@@@MwC->%N` `d"(pAAMwC-%N` dxQ!(pXXMwS-%N`њ WPx`]!(p@@@MwS-<%N`ݚ ``!(p@@MwC->%N`) `d"(pAA@MwC-%N`b dx)(pXX@MwS-%N`g WPx`)(p@@MwS-<%N`u ``*(p@@@MwC->%N` `d+(pAAMwC-%N` dx2(pXXMwS-%N` WP$x`2(p@@@MwS-<%N`. ``2(p@@MwC->%N`N `d3(pAA@MwC-%N`v dx3;(pXX@MwS-%N` WP4x`C;(p@@MwS-<%N`ô ``;(p@@@MwC->%N` `d<(pAAMwC-%N` dxC(pXXMwS-%N`v WPDx`D(p@@@MwS-<%N` ``"D(p@@MwC->%N` `d'E(pAA@MwC-%N` dxL(pXX@MwS-%N` WPTx`L(p@@MwS-<%N`. ``L(p@@@MwC->%N`K `dM(pAAMwC-%N`< dx%N` `dlV(pAA@MwC-%N` dx](pXX@MwS-%N`v WPux`^(p@@MwS-<%N` ``#^(p@@@MwC->%N` `d9_(pAAMwC-%N` dxf(pXXMwS-%N`. WP x`f(p@@@MwS-<%N`= ``f(p@@MwC->%N`Y `dg(pAA@MwC-%N`g dxRo(pXX@MwS-%N` WP0x`ho(p@@MwS-<%N` ``o(p@@@MwC->%N` `dp(pAAMwC-%N` dxx(pXXMwS-%N` WP@x`&x(p@@@MwS-<%N` ``cx(p@@MwC->%N` `d)y(pAA@MwC-%N` dxҀ(pXX@MwS-%N`R WPPx`(p@@MwS-<%N`` ``$(p@@@MwC->%N` `dG(pAAMwC-%N` dxl(pXXMwS-%N` WP`x`z(p@@@MwS-<%N` ``(p@@MwC->%N`* `d(pAA@MwC-%N`0 dx4(pXX@MwS-%N` WPpx`H(p@@MwS-<%N` ``(p@@@MwC->%N` `dR(pAAMwC-%N` dx(pXXMwS-%N`r WPx`(p@@@MwS-<%N` ``A(p@@MwC->%N` `dT(pAA@MwC-%N` dx(pXX@MwS-%N`n WPx`(p@@MwS-<%N`~ ``>(p@@@MwC->%N` `dX(pAAMwC-%N` dx(pXXMwS-%N`f& WPx`(p@@@MwS-<%N`t& ``5(p@@MwC->%N`& `d2(pAA@MwC-%N`' dx(pXX@MwS-%N`*/ WPx`(p@@MwS-<%N`%N`/ `d(pAAMwC-%N`0 dx|(pXXMwS-%N`7 WP%x`(p@@@MwS-<%N` 8 ``ξ(p@@MwC->%N`N8 `d(pAA@MwC-%N`d9 dxR(pXX@MwS-%N`@ WP5x`t(p@@MwS-<%N`@ ``(p@@@MwC->%N`A `d(pAAMwC-%N`B dx(pXXMwS-%N`I WPEx`(p@@@MwS-<%N`I ``U(p@@MwC->%N`I `dN(pAA@MwC-%N`J dx\(pXX@MwS-%N`Q WPUx`j(p@@MwS-<%N`Q ``(p@@@MwC->%N`.R `d(pAAMwC-%N`$S dx (pXXMwS-%N`Z WPfx`.(p@@@MwS-<%N`Z ``p(p@@MwC->%N`Z `dg(pAA@MwC-%N`[ dx(pXX@MwS-%N`@c WPvx`(p@@MwS-<%N`Nc ``(p@@@MwC->%N`c `d (pAAMwC-%N`d dx(pXXMwS-%N`l WP x`(p@@@MwS-<%N`l ``(p@@MwC->%N`Pl `d(pAA@MwC-%N`sm dx)(pXX@MwS-%N`t WP0x`7(p@@MwS-<%N`t ``y(p@@@MwC->%N`t `dr(pAAMwC-%N`u dx)pXXMwS-%N`r} WP@x`)p@@@MwS-<%N`} ``A)p@@MwC->%N`} `dT)pAA@MwC-%N`~ dx )pXX@MwS-%N`; WPPx` )p@@MwS-<%N`K `` )p@@@MwC->%N` `d)pAAMwC-%N` dxm)pXXMwS-%N` WP`x`)p@@@MwS-<%N` ``)p@@MwC->%N` `d)pAA@MwC-%N`h d`)p@@@MwS-<%N` `xm")pXXMwS-%N` WPpx`")p@@MwC->%N` `d#)pAA@MwC-%N` dxB+)pXX@MwS-%N`¤ WPx`\+)p@@MwS-<%N`ܤ ``+)p@@@MwC->%N` `d,)pAAMwC-%N` dx3)pXXMwS-%N`o WPx`3)p@@@MwS-<%N`} ``>4)p@@MwC->%N` `dX5)pAA@MwC-%N`خ dx<)pXX@MwS-%N`; WPx`<)p@@MwS-<%N`I `` =)p@@@MwC->%N` `d0>)pAAMwC-%N` dx=E)pXXMwS-%N` WPx`KE)p@@@MwS-<%N`˾ ``E)p@@MwC->%N` `dF)pAA@MwC-%N` dxM)pXX@MwS-%N`^ WP&x`M)p@@MwS-<%N`k ``.N)p@@@MwC->%N` `d$O)pAAMwC-%N` dxV)pXXMwS-%N` WP6x`V)p@@@MwS-<%N`- ``V)p@@MwC->%N`M `dW)pAA@MwC-%N`= dx]_)pXX8yS-%N` WPFx`n_)p@@8yS-<%N` ``_)p@@8yC->%N` `d`)pAA8yC-%N`[ dxh)pXX8yS-%N` WPVx`&h)p@@8yS-<%N` ``Uh)p@@8yC->%N` `dci)pAA8yC-%N` dx:q)pXX8yS-%N` WPWx`Gq)p@@8yS-<%N` ``kq)p@@8yC->%N` `dr)pAA8yC-%N` dxy)pXX8yS-%N` WPgx`z)p@@8yS-<%N` ``8z)p@@8yC->%N` `dd{)pAA8yC-%N` dxʂ)pXX8yS-%N`J WP wx`)p@@8yS-<%N`k `` )p@@8yC->%N` `d5)pAA8yC-%N` dx)pXX8yS-%N` WP0x`)p@@8yS-<%N`  ``ċ)p@@8yC->%N`D `d)pAA8yC-%N`q dx6)pXX8yS-%N` WP@x`D)p@@8yS-<%N` ``h)p@@8yC->%N` `d)pAA8yC-%N`  dx)pXX8yS-%N`q WPPx`)p@@8yS-<%N`~ ``-)p@@8yC->%N` `dA)pAA8yC-%N` dxN)pXX8yS-%N` WP`x`\)p@@8yS-<%N` ``)p@@8yC->%N` `d)pAA8yC-%N` dxȭ)pXX8yS-%N`H' WPpx`ѭ)p@@8yS-<%N`Q' `` )p@@8yC->%N`' `dC)pAA8yC-%N`( dxW)pXX8yS-%N`/ WPx`f)p@@8yS-<%N`/ ``)p@@8yC->%N`0 `d)pAA8yC-%N`>1 dx)pXX8yS-%N`8 WPx`-)p@@8yS-<%N`8 ``Y)p@@8yC->%N`8 `d)pAA8yC-%N`: dx)pXX8yS-%N`kA WPx`)p@@8yS-<%N`|A ``$)p@@8yC->%N`A `d3)pAA8yC-%N`B dx)pXX8yS-%N` J WPx`)p@@8yS-<%N`4J ``)p@@8yC->%N`eJ `d#)pAA8yC-%N`K dxl)pXX8yS-%N`R WPx`})p@@8yS-<%N`R ``)p@@8yC->%N`!S `d)pAA8yC-%N`*T dx )pXX8yS-%N`[ WP'x`)p@@8yS-<%N`[ ``C)p@@8yC->%N`[ `dJ)pAA8yC-%N`\ dx)pXX8yS-%N`Jd WP7x`)p@@8yS-<%N`[d ``)p@@8yC->%N`d `d7)pAA8yC-%N`e dx)pXX8yS-%N`m WPGx`)p@@8yS-<%N`m ``)p@@8yC->%N`cm `d)pAA8yC-%N`n dxY)pXX8yS-%N`u WPOpen GD77x`g)p@@8yS-<%N`u ``)p@@8yC->%N`*v `d)pAA8yC-%N`Bw dx*pXX8yS-%N`N~ WPDM3MATx`*p@@8yS-<%N`]~ ``*p@@8yC->%N`~ `d2*pAA8yC-%N` dx *pXX8yS-%N` WP 12345678,x` *p@@8yS-<%N` `` *p@@8yC->%N`_ `d*pAA8yC-%N`s dxU*pXX8yS-%N`Տ WPhx`d*p@@8yS-<%N` ``*p@@8yC->%N` `d**pAA8yC-%N`I dx.0*pXX8yS-%N` WP x`<0*p@@8yS-<%N` ``0*p@@8yC->%N` `d1*pAA8yC-%N` dx8*pXX8yS-%N`t WP  x`9*p@@8yS-<%N` ``C9*p@@8yC->%N`ò `dZ:*pAA8yC-%N`ڳ dxA*pXX8yS-%N`5 WP0x`A*p@@8yS-<%N`B ``B*p@@8yC->%N` `d3C*pAA8yC-%N` dxwJ*pXX8yS-%N` WP@:x`J*p@@8yS-<%N` ``J*p@@8yC->%N`E `dK*pAA8yC-%N`S dxR*pXX8yS-%N`. WPPx`R*p@@8yS-<%N`< ``R*p@@8yC->%N`~ `dS*pAA8yC-%N`} dxi[*pXX8yS-%N` WP`x`u[*p@@8yS-<%N` ``[*p@@8yC->%N`3 `d\*pAA8yC-%N`4 dx d*pXX8yS-%N` WPpx`-d*p@@8yS-<%N` ``nd*p@@8yC->%N` `de*pAA8yC-%N` dxl*pXX8yS-%N`S WPx`l*p@@8yS-<%N`_ ``m*p@@8yC->%N` `d-n*pAA8yC-%N` dxu*pXX8yS-%N` WPx`u*p@@8yS-<%N`# ``v*p@@8yC->%N` `dw*pAA8yC-%N` dx}*pXX8yS-%N`p WPtx`}*p@@8yS-<%N`} ``1~*p@@8yC->%N` `d*pAA8yC-%N` dxw*pXX8yS-%N` WPx`*p@@8yS-<%N` ``Ċ*p@@8yC->%N`D `dڋ*pAA8yC-%N`Z dx4*pXX8yS-%N` WP x`C*p@@8yS-<%N` ``w*p@@8yC->%N` `d*pAA8yC-%N` dx*pXX8yS-%N`n WP0x`*p@@8yS-<%N`y ``>*p@@8yC->%N` `dN*pAA8yC-%N` dx*pXX8yS-%N`# WPx`*p@@8yS-<%N`1 ``դ*p@@8yC->%N`U `d8*pAA8yC-%N`$ dx*pXX8yS-%N`=+ WPx`ʱ*p@@8yS-<%N`J+ ``*p@@8yC->%N`+ `d*pAA8yC-%N`, dx}*pXX8yS-%N`3 WPx`*p@@8yS-<%N` 4 ``к*p@@8yC->%N`P4 `d*pAA8yC-%N`d5 dx0*pXX8yS-%N`< WPvx`;*p@@8yS-<%N`< ``|*p@@8yC->%N`< `d*pAA8yC-%N`/C dx*pXX8yS-%N`I WPx`"*p@@8yS-<%N`I ``V*p@@8yC->%N`I `dy*pAA8yC-%N`J dx*pXX8yS-%N`OR WP  Zbx`*p@@8yS-<%N`[R ``*p@@8yC->%N`R `d2*pAA8yC-%N`S dx*pXX8yS-%N` [ WP0l'x`*p@@8yS-<%N`[ ``*p@@8yC->%N`W[ `d*pAA8yC-%N`X\ dxM*pXX8yS-%N`c WP@x`Y*p@@8yS-<%N`c ``*p@@8yC->%N`d `d*pAA8yC-%N`@e dx*pXX8yS-%N`tl WPPx`*p@@8yS-<%N`l ``@*p@@8yC->%N`l `d<*pAA8yC-%N`m dx*pXX8yS-%N`%u WP`x`*p@@8yS-<%N`1u ``*p@@8yC->%N`pu `d*pAA8yC-%N`v dxU+pXX8yS-%N`} WPpx`a+p@@8yS-<%N`} ``+p@@8yC->%N` ~ `d+pAA8yC-%N`7 dx +pXX8yS-%N` WPx` +p@@8yS-<%N` ``Z +p@@8yC->%N`چ `dR+pAA8yC-%N`҇ dx+pXX8yS-%N`G WPx`+p@@8yS-<%N`R ``*+p@@8yC->%N` `dK+pAA8yC-%N`ː dxQ+pXX8yS-%N`ї WPx`]+p@@@8yS-<%N`ݗ ``+p@@8yC->%N` `d+pAA@8yC-%N`2 dx '+pXX8yS-%N` WP&x`'+p@@@8yS-<%N` ``T'+p@@8yC->%N`Ԡ `df(+pAA@8yC-%N` dx/+pXX8yS-%N`= WP6x`/+p@@8yS-<%N`H ``0+p@@8yC->%N` `d1+pAA8yC-%N` dx8+pXX8yS-%N` WPFx`8+p@@8yS-<%N` ``8+p@@8yC->%N`Q `d9+pAA8yC-%N`w dx4A+pXX8yS-%N` WPVx`AA+p@@@8yS-<%N` ``A+p@@8yC->%N` `dB+pAA@8yC-%N` dxI+pXX8yS-%N`q WPfx`I+p@@@8yS-<%N`~ ``AJ+p@@8yC->%N` `dLK+pAA@8yC-%N` dxR+pXX@8yS-%N`< WTKW Aix`R+p@@8yS-<%N`H `` S+p@@@8yC->%N` `d|+pAA8yC-%N`l dxB+pXXwS-%N`WTvx`P+p@@@wS-<%N```+p@@wC->%N``d+pAA@wC-%N` dx+pXX@wS-%N`wWT KW Bx`+p@@wS-<%N```"+p@@@wC->%N``dm+pAAwC-%N`dx+pXX@wS-%N` WT0x`+p@@wS-<%N```+p@@@wC->%N`@`d͟+pAAwC-%N`Mdx+pXXwS-%N` WT@TH Wildaux`&+p@@wS-<%N` ``I+p@@wC->%N` `dj+pAAwC-%N`!dxԯ+pXXwS-%N`T)WTPx`+p@@wS-<%N`a)``+p@@wC->%N`)`d9+pAAwC-%N`*dx+pXXwS-%N`2WT`Hessen Ax`+p@@@wS-<%N`+2``Ѹ+p@@wC->%N`Q2`d+pAA@wC-%N`g3dxd+pXXwS-%N`:WTpx`r+p@@@wS-<%N`:``+p@@wC->%N`;`d+pAA@wC-%N`9<dx+pXX@wS-%N`LCWTHessen Bx`+p@@wS-<%N`ZC``+p@@@wC->%N`~C`d+pAAwC-%N`}Ddx|+pXX@8yS-%N`KWTx`+p@@8yS-<%N`L``+p@@@8yC->%N`OL`d+pAA8yC-%N`CMdx+pXX8yS-%N`bWTBerlin Ax`+p@@8yS-<%N`b``+p@@8yC->%N`Qb`d+pAA8yC-%N`dcdxE+pXX8yS-%N`jWTx`S+p@@8yS-<%N`j``+p@@8yC->%N`k`d+pAA8yC-%N`ldx+pXX@8yS-%N`{sWTBerlin Bx`+p@@8yS-<%N`s``\+p@@@8yC->%N`s`dZ+pAA8yC-%N`tdx,pXX8yS-%N`Q|WT6x`,p@@@8yS-<%N`^|``,p@@8yC->%N`|`dk,pAA@8yC-%N`}d`c ,p@@8yS-<%N``x=,pXX8yS-%N`tWTPotsdam Ax`S>,p@@8yC->%N`ӷ`dk?,pAA8yC-%N`dxF,pXX8yS-%N`FWTVx`F,p@@8yS-<%N`U``G,p@@8yC->%N``dH,pAA8yC-%N`dxO,pXX8yS-%N`WTPotsdam Bx`O,p@@@8yS-<%N`$``O,p@@8yC->%N`a`dP,pAA@8yC-%N`gdx;f,pXX8yS-%N`WTwx`Lf,p@@@8yS-<%N```f,p@@8yC->%N``dg,pAA@8yC-%N`$dxo,pXX@8yS-%N`WT Cottbus Ax`o,p@@8yS-<%N```No,p@@@8yC->%N``dfp,pAA8yC-%N`dxw,pXX@8yS-%N`SWT0x`w,p@@8yS-<%N`c``x,p@@@8yC->%N``dy,pAA8yC-%N`dx,pXX8yS-%N`:WT@Cottbus Bx`̀,p@@8yS-<%N`M``,p@@8yC->%N``d!,pAA8yC-%N`dxt,pXX8yS-%N`WTPx`,p@@8yS-<%N```̉,p@@8yC->%N`L`d׊,pAA8yC-%N`WdxB,pXX@8yS-%N` WT`Leipzig Ax`Q,p@@8yS-<%N` ``,p@@@8yC->%N` `d,pAA8yC-%N`# dx,pXX8yS-%N`WTpx`,p@@@8yS-<%N```ۛ,p@@8yC->%N`[`dޜ,pAA@8yC-%N`^dxͣ,pXX8yS-%N`MWTLeipzig Bx`ߣ,p@@8yS-<%N`_``,p@@8yC->%N``d,pAA8yC-%N`dx,pXX8yS-%N`&WTx`,p@@8yS-<%N`&``ެ,p@@8yC->%N`^&`d,pAA8yC-%N`z'dxH,pXX8yS-%N`.WTSat Ax`U,p@@@8yS-<%N`.``,p@@8yC->%N`8/`d,pAA@8yC-%N`+0dx,pXX8yS-%N`7WTx`,p@@@8yS-<%N`7``8,p@@8yC->%N`7`d4,pAA@8yC-%N`8d`,p@@@8yS-<%N`R@`x,pXX8yS-%N`YWTSat Bx`i,p@@8yC->%N``dZ,pAA@8yC-%N`dx,pXX@8yS-%N`WT7x`,p@@8yS-<%N```,p@@@8yC->%N`5`d ,pAA8yC-%N`Idxj,pXX8yS-%N` WTPMR Ax`x,p@@8yS-<%N` ``,p@@8yC->%N` `d,pAA8yC-%N`*"dx),pXX8yS-%N`i)WTWx`8,p@@8yS-<%N`x)``|,p@@8yC->%N`)`d,pAA8yC-%N`*dx,pXX@8yS-%N`42WTPMR Bx`,p@@8yS-<%N`C2``D,p@@@8yC->%N`2`d;,pAA8yC-%N`{3dx-pXX8yS-%N`:WTxx`-p@@@8yS-<%N` ;`` -p@@8yC->%N`L;`d-pAA@8yC-%N`G<dx -pXX@8yS-%N`CWXbpDM3MATx` -p@@8yS-<%N`C`` -p@@@8yC->%N`D`d9-pAA8yC-%N` qdx>-pXX8yS-%N` vWXzx`>-p@@@8yS-<%N`-v``.?-p@@8yC->%N`nv`d?-pAA@8yC-%N`7wdxG-pXX@8yS-%N`~W\jx`G-p@@8yS-<%N`~``G-p@@@8yC->%N`5`d o-pAA8yC-%N``dxw-pXX8yS-%N`=W\~x` x-p@@@8yS-<%N`K``Px-p@@8yC->%N``dZy-pAA@8yC-%N`dx-pXX@8yS-%N`W\ x`ŀ-p@@8yS-<%N```-p@@@8yC->%N`D`d-pAA8yC-%N`Adx{-pXX8yS-%N`W\0x`-p@@@8yS-<%N```ɉ-p@@8yC->%N` `d-pAA@8yC-%N`dx?-pXX@8yS-%N`W\@x`M-p@@8yS-<%N```-p@@@8yC->%N``d-pAA8yC-%N`dxɚ-pXX8yS-%N` W\Px`՚-p@@@8yS-<%N```-p@@8yC->%N`U`d-pAA@8yC-%N`Vdx-pXX@8yS-%N`W\`x`-p@@8yS-<%N```٣-p@@@8yC->%N``d-pAA8yC-%N`+dx-pXX8yS-%N`\W\px`(-p@@@8yS-<%N`h``i-p@@8yC->%N``di-pAA@8yC-%N`dxڴ-pXX@8yS-%N`W\x`-p@@8yS-<%N`'``6-p@@@8yC->%N`v`dA-pAA8yC-%N`dx-pXX8yS-%N`W\x`-p@@@8yS-<%N```-p@@8yC->%N`2`d-pAA@8yC-%N`)dxe-pXX@8yS-%N`W\x`q-p@@8yS-<%N```-p@@@8yC->%N``d-pAA8yC-%N`dx,-pXX8yS-%N`lW\x`9-p@@@8yS-<%N`y``{-p@@8yC->%N``d-pAA@8yC-%N`dx-pXX@8yS-%N`W\.x`-p@@8yS-<%N`*``)-p@@@8yC->%N`i`d/-pAA8yC-%N`odx-pXX8yS-%N`W\>x`-p@@@8yS-<%N```-p@@8yC->%N`H`d-pAA@8yC-%N`Qdx#-pXX@8yS-%N`c W\Nx`0-p@@8yS-<%N`p ``p-p@@@8yC->%N` `dm-pAA8yC-%N`!dx-pXX8yS-%N`(W\^x`-p@@@8yS-<%N`(``-p@@8yC->%N`])`d-pAA@8yC-%N`N*dxm-pXX@8yS-%N`1W\Status Message 1)x`y-p@@8yS-<%N`1``-p@@@8yC->%N`1`d-pAA8yC-%N` 3dx0.pXX8yS-%N`p:W\x`=.p@@@8yS-<%N`}:``.p@@8yC->%N`:`d.pAA@8yC-%N`;dx .pXX@8yS-%N`%N`C`dG .pAA8yC-%N`Ddx.pXX8yS-%N`LW\0x`.p@@@8yS-<%N`L``!.p@@8yC->%N`aL`d#.pAA@8yC-%N`cMdxp.pXX@8yS-%N`TW\@x`|.p@@8yS-<%N`T``.p@@@8yC->%N`T`d.pAA8yC-%N` Vdx2&.pXX8yS-%N`r]W\Px`>&.p@@@8yS-<%N`~]``&.p@@8yC->%N`]`d'.pAA@8yC-%N`^dx..pXX@8yS-%N`1fW\`x`/.p@@8yS-<%N`@f``@/.p@@@8yC->%N`f`d;0.pAA8yC-%N`{gdx7.pXX8yS-%N`nW\px`7.p@@@8yS-<%N`n``7.p@@8yC->%N`1o`d8.pAA@8yC-%N`>pdx]@.pXX@8yS-%N`wW\x`i@.p@@8yS-<%N`w``@.p@@@8yC->%N`w`dA.pAA8yC-%N`xdxW.pXX8yS-%N`9W\x`X.p@@@8yS-<%N`G``IX.p@@8yC->%N``dIY.pAA@8yC-%N`dx`.pXX@8yS-%N`W\x``.p@@8yS-<%N```a.p@@@8yC->%N`E`db.pAA8yC-%N`Od`zi.p@@'zS-<%N``xm.pXX'zS-%N`W\x` n.p@@'zC->%N`L`do.pAA'zC-%N`Vdxpv.pXX@'zS-%N`W\/x`v.p@@'zS-<%N```v.p@@@'zC->%N``dw.pAA'zC-%N`dx?.pXX'zS-%N`W\?x`O.p@@'zS-<%N```.p@@'zC->%N`ж`d.pAA'zC-%N`dx.pXX@'zS-%N`=W\Ox` .p@@'zS-<%N`J``K.p@@@'zC->%N``dq.pAA'zC-%N`dxÐ.pXX'zS-%N`W\_x`А.p@@'zS-<%N```.p@@'zC->%N`P`d#.pAA'zC-%N`cdx.pXX'zS-%N`W\px`.p@@'zS-<%N```ՙ.p@@'zC->%N``d.pAA'zC-%N`&dx'.pXX@'zS-%N`gW\x`4.p@@'zS-<%N`t``x.p@@@'zC->%N``dS.pAA'zC-%N`dx.pXX'zS-%N`W\ x`.p@@'zS-<%N```.p@@'zC->%N`%`d .pAA'zC-%N`Idx.pXX@'zS-%N`_W\0x`1.p@@'zS-<%N`q``S.p@@@'zC->%N``d.pAA'zC-%N`dx.pXX'zS-%N`W\@x`λ.p@@'zS-<%N```.p@@'zC->%N`U`d.pAA'zC-%N`Hdxt.pXX'zS-%N`W\Px`.p@@'zS-<%N```.p@@'zC->%N``d.pAA'zC-%N`dx=.pXX@'zS-%N`}W\`x`L.p@@'zS-<%N```.p@@@'zC->%N``d.pAA'zC-%N`dx.pXX'zS-%N`. W\px`.p@@'zS-<%N`; ``?.p@@'zC->%N` `d.pAA'zC-%N`Ydx.pXX@'zS-%N`W\x`.p@@'zS-<%N```.p@@@'zC->%N`6`d.pAA'zC-%N`3dxH.pXX'zS-%N`W\x`V.p@@'zS-<%N```.p@@'zC->%N``d.pAA'zC-%N`dx.pXX'zS-%N`^'W\x`+.p@@'zS-<%N`k'``{.p@@'zC->%N`'`d5.pAA'zC-%N`u(dx.pXX@'zS-%N` 0W\ x`.p@@'zS-<%N`.0``..p@@@'zC->%N`n0`d<.pAA'zC-%N`|1dx/pXX'zS-%N`8W\0x`/p@@'zS-<%N`8``/p@@'zC->%N`Y9`d/pAA'zC-%N`:dx) /pXX@'zS-%N`iAW\@x`5 /p@@'zS-<%N`uA``v /p@@@'zC->%N`A`dT /pAA'zC-%N`Bdx/pXX'zS-%N`IW\Px`/p@@'zS-<%N`J``/p@@'zC->%N`"J`d/pAA'zC-%N`$Kdxt/pXX'zS-%N`RW\`x`/p@@'zS-<%N`R``/p@@'zC->%N`R`d/pAA'zC-%N`Sdx$/pXX@'zS-%N`_[W\qx`4$/p@@'zS-<%N`t[``T$/p@@@'zC->%N`[`dh%/pAA'zC-%N`\dx,/pXX'zS-%N`cW\x`,/p@@'zS-<%N`c``,/p@@'zC->%N`c`d-/pAA'zC-%N`ddx5/pXX@'zS-%N`DlW\ x`5/p@@'zS-<%N`Tl``45/p@@@'zC->%N`tl`d26/pAA'zC-%N`rmdx=/pXX'zS-%N`tW\0x`=/p@@'zS-<%N` u``=/p@@'zC->%N`)u`d>/pAA'zC-%N`/vdxxF/pXX'zS-%N`}W\@x`F/p@@'zS-<%N`}``F/p@@'zC->%N`}`dG/pAA'zC-%N`~dxO/pXX@'zS-%N`BW\Px`O/p@@'zS-<%N`R``2O/p@@@'zC->%N`r`dHP/pAA'zC-%N`dxW/pXX'zS-%N`W\`x`W/p@@'zS-<%N` ``W/p@@'zC->%N`,`dX/pAA'zC-%N`-dxi`/pXX@'zS-%N`W\px`x`/p@@'zS-<%N````/p@@@'zC->%N`ڗ`da/pAA'zC-%N`dxi/pXX'zS-%N`\W\x`/i/p@@'zS-<%N`o``Oi/p@@'zC->%N``dLj/pAA'zC-%N`dxq/pXX'zS-%N`W\x`q/p@@'zS-<%N```r/p@@'zC->%N`^`d$s/pAA'zC-%N`ddx9z/pXX@'zS-%N`yW\x`Rz/p@@'zS-<%N```z/p@@@'zC->%N`Ա`d{/pAA'zC-%N`dxR/pXX'zS-%N`W\!x``/p@@'zS-<%N``` /p@@'zC->%N`L`d/pAA'zC-%N`6dx/pXX@'zS-%N`ZW\1x`(/p@@'zS-<%N`h``n/p@@@'zC->%N``dl/pAA'zC-%N`dx/pXX%N``d[/pAA%N`H`dP/pAA%N` `dӦ/pAA%N``dW/pAA%N`T`dE/pAA%N`%`d/pAA%N``d/pAA@W\@x` /p@@%N`p`d/pAA%N``d/pAA%N``d/pAA%N`|"`d=/pAA%N`#+`d/pAA%N`3`dw/pAA%N`u<`d50pAA%N`D`d0pAA@%N`M`dm0pAA%N`BV`d 0pAA%N`^`d(0pAA%N`g`d|10pAA%N`xp`d90pAAqdxA0pXX%N`)y`dB0pAA%N``dK0pAA%N`m`d/T0pAA@%N`I`d]0pAA%N``de0pAA%N``don0pAA%N`i`d&w0pAA%N`,`d0pAA%N``dȌ0pAA%N``dm0pAA%N`B`d0pAA@%N``d0pAA%N``d0pAA%N``dm0pAA%N`C`d0pAA%N``d0pAA%N``dI0pAA%N`Q`d40pAA%N``d0pAA@%N`"`dc0pAA%N`F+`d0pAA%N`3`d0pAA%N`B<`dE1pAA%N`E`d1pAA%N`M`d1pAA%N`rV`dI 1pAA%N`=_`d3)1pAA@%N`g`d11pAA%N`p`db:1pAA%N`hy`dC1pAAmzC-%N`EzdxJ1pXXmzS-%N`́W\dx`J1p@@mzS-<%N`ہ``J1p@@mzC->%N``dL1pAAmzC-%N`BdxfS1pXXmzS-%N`W\ux`uS1p@@mzS-<%N```S1p@@mzC->%N``dT1pAAmzC-%N`dx*\1pXXmzS-%N`jW\x`8\1p@@mzS-<%N`x``|\1p@@mzC->%N``d]1pAAmzC-%N`єdxd1pXXmzS-%N`7W\ x`e1p@@mzS-<%N`G``He1p@@mzC->%N``dEf1pAAmzC-%N`dxm1pXXmzS-%N`W\0x`m1p@@mzS-<%N```m1p@@mzC->%N`=`d-o1pAAmzC-%N`mdxu1pXXmzS-%N`W\@x`u1p@@mzS-<%N`&``-v1p@@mzC->%N`m`dBw1pAAmzC-%N`dx~1pXXmzS-%N`W\Px`~1p@@mzS-<%N```~1p@@mzC->%N`6`d1pAAmzC-%N`dx11pXXmzS-%N`qW\`x`?1p@@mzS-<%N```1p@@mzC->%N`¾`d1pAAmzC-%N`ֿdx1pXXmzS-%N`6W\px`1p@@mzS-<%N`F``V1p@@mzC->%N``dQ1pAAmzC-%N`dx1pXXmzS-%N`W\x`ǘ1p@@mzS-<%N```1p@@mzC->%N`G`d1pAAmzC-%N`]dx,1pXXmzS-%N`lW\x`:1p@@mzS-<%N`z``{1p@@mzC->%N``d1pAAmzC-%N`dxש1pXXmzS-%N`W\x`1p@@mzS-<%N`$``&1p@@mzC->%N`f`d1pAAmzC-%N`\dx1pXXmzS-%N`W\%x`1p@@mzS-<%N```۲1p@@mzC->%N``d1pAAmzC-%N`-dxR1pXXmzS-%N`W\5x`_1p@@mzS-<%N```1p@@mzC->%N``d1pAAmzC-%N`dx1pXXmzS-%N`RW\Ax`1p@@mzS-<%N`^``c1p@@mzC->%N``d1pAAmzC-%N`dx1pXXmzS-%N`W\Ux`1p@@mzS-<%N```1p@@mzC->%N`Y`dB1pAAmzC-%N`dx]1pXXmzS-%N` W\ex`j1p@@mzS-<%N` ``1p@@mzC->%N` `d1pAAmzC-%N`()dx1pXXmzS-%N`.W\rx`1p@@mzS-<%N` .``(1p@@mzC->%N`h.`d^1pAAmzC-%N`/dxc1pXXmzS-%N`6W\x`q1p@@mzS-<%N`6``1p@@mzC->%N`6`d2pAAmzC-%N`7dx2pXXmzS-%N`/?W\ x`2p@@mzS-<%N`%N`|?`d6 2pAAmzC-%N`v@dx2pXXmzS-%N`GW\0x`2p@@mzS-<%N`G``2p@@mzC->%N`>H`d2pAAmzC-%N`EIdx]2pXXmzS-%N`PW\@x`h2p@@mzS-<%N`P``2p@@mzC->%N`P`d2pAAmzC-%N`Qdx"2pXXmzS-%N`[YW\Px`'"2p@@mzS-<%N`gY``f"2p@@mzC->%N`Y`d#2pAAmzC-%N`Zdx*2pXXmzS-%N`bW\ zx`*2p@@mzS-<%N`#b``"+2p@@mzC->%N`bb`d12pAAmzC-%N`hdxx62pXXmzS-%N`mW\ ?x`62p@@mzS-<%N`m``62p@@mzC->%N`n`d72pAAmzC-%N`,odx*?2pXXmzS-%N`jvW\ x`6?2p@@mzS-<%N`vv``v?2p@@mzC->%N`v`d@2pAAmzC-%N`wdxG2pXXmzS-%N`3W`xx` H2p@@mzS-<%N`I``PH2p@@mzC->%N``do2pAAmzC-%N`զdxBx2pXXmzS-%N`W`x`Ox2p@@mzS-<%N```x2p@@mzC->%N`ԯ`dy2pAAmzC-%N`dxĀ2pXXmzS-%N`W`  x`؀2p@@mzS-<%N```2p@@mzC->%N`X`d!2pAAmzC-%N`adxy2pXXmzS-%N`W`0 x`2p@@mzS-<%N```Ή2p@@mzC->%N``dۊ2pAAmzC-%N`dx/2pXXmzS-%N`oW`@x`<2p@@mzS-<%N`|``2p@@mzC->%N``d2pAAmzC-%N`dx2pXXmzS-%N`%W`Px`2p@@mzS-<%N`2``(2p@@mzC->%N`h`dO2pAAmzC-%N`dxg2pXXmzS-%N`W``x`u2p@@mzS-<%N```ǣ2p@@mzC->%N``d2pAAmzC-%N`dx2pXXmzS-%N`SW`px` 2p@@mzS-<%N````g2p@@mzC->%N``d\2pAAmzC-%N`dxȴ2pXXmzS-%N`W`x`մ2p@@mzS-<%N```2p@@mzC->%N`^`d$2pAAmzC-%N`ddx|2pXXmzS-%N`W`x`2p@@mzS-<%N```ͽ2p@@mzC->%N` `d2pAAmzC-%N`Rdx92pXXmzS-%N`yW`x`G2p@@mzS-<%N```2p@@mzC->%N``d2pAAmzC-%N`dx2pXXmzS-%N`W`x`2p@@mzS-<%N```2p@@mzC->%N`D`d2pAAmzC-%N`Ndxs2pXXmzS-%N`W`"x`2p@@mzS-<%N```2p@@mzC->%N``d2pAAmzC-%N`d`2p@@mzS-<%N`B`x`2pXXmzS-%N`W`2x`2p@@mzC->%N``d2pAAmzC-%N`dx#2pXXmzS-%N`c$W`Bx`/2p@@mzS-<%N`o$``r2p@@mzC->%N`$`d2pAAmzC-%N`%dx2pXXmzS-%N`$-W`Rx`2p@@mzS-<%N`0-``22p@@mzC->%N`r-`dC2pAAmzC-%N`.dxf2pXXmzS-%N`5W`cx`u2p@@mzS-<%N`5``2p@@mzC->%N`5`d2pAAmzC-%N`6dx 3pXXmzS-%N``>W`sx`.3p@@mzS-<%N`n>``r3p@@mzC->%N`>`d3pAAmzC-%N`?dx3pXXmzS-%N`GW` x`3p@@mzS-<%N` G``"3p@@mzC->%N`bG`d3pAAmzC-%N`XHdxf3pXXmzS-%N`OW`0x`s3p@@mzS-<%N`O``3p@@mzC->%N`O`d3pAAmzC-%N`Qdx!3pXXmzS-%N`ZXW`@x`'!3p@@mzS-<%N`gX``i!3p@@mzC->%N`X`dg"3pAAmzC-%N`Ydx)3pXXmzS-%N``W`Px`)3p@@mzS-<%N````)3p@@mzC->%N`%a`d*3pAAmzC-%N`&bdx13pXXmzS-%N`%N`i`d?33pAAmzC-%N`jdx:3pXXmzS-%N`qW`px`:3p@@mzS-<%N`q``:3p@@mzC->%N`9r`d<3pAAmzC-%N`Psdx.C3pXXmzS-%N`nzW`x`%N`z`dD3pAAmzC-%N`{dxK3pXXmzS-%N` W`x`K3p@@mzS-<%N```L3p@@mzC->%N`[`d5M3pAAmzC-%N`udxT3pXXmzS-%N`‹W`x`T3p@@mzS-<%N`Ћ``T3p@@mzC->%N``dU3pAAmzC-%N` dxA]3pXXmzS-%N`W`x`O]3p@@mzS-<%N```]3p@@mzC->%N`Ԕ`d^3pAAmzC-%N`ҕdxe3pXXmzS-%N`;W`#x` f3p@@mzS-<%N`I``Nf3p@@mzC->%N``dBg3pAAmzC-%N`dxn3pXXmzS-%N`ɥW`3x`n3p@@mzS-<%N`ե``n3p@@mzC->%N`+`do3pAAmzC-%N`2dxv3pXXmzS-%N`)W`Cx`v3p@@mzS-<%N`7``9w3p@@mzC->%N`y`dVx3pAAmzC-%N`dx3pXXmzS-%N`W`Sx`3p@@mzS-<%N```3p@@mzC->%N`:`d 3pAAmzC-%N`Jdxi3pXXmzS-%N`W`dx`v3p@@mzS-<%N```3p@@mzC->%N``dω3pAAmzC-%N`dx&3pXXmzS-%N`fW`tx`<3p@@mzS-<%N`|``[3p@@mzC->%N``d3pAAmzC-%N`dxۙ3pXXmzS-%N`W` x`3p@@mzS-<%N`'``)3p@@mzC->%N`i`d<3pAAmzC-%N`|dx3pXXmzS-%N`W`0x`3p@@mzS-<%N```3p@@mzC->%N`*`d3pAAmzC-%N`Fdxi3pXXmzS-%N`W`@x`u3p@@mzS-<%N```3p@@mzC->%N``dɬ3pAAmzC-%N` dx3pXXmzS-%N`7W`Px`3p@@mzS-<%N`D``N3p@@mzC->%N``da3pAAmzC-%N`dx3pXXmzS-%N`W``x`ȼ3p@@mzS-<%N``` 3p@@mzC->%N`K`d3pAAmzC-%N`\dxm3pXXmzS-%N`W`px`x3p@@mzS-<%N```3p@@mzC->%N``d3pAAmzC-%N`dx83pXXmzS-%N`xW`x`E3p@@mzS-<%N```3p@@mzC->%N``d3pAAmzC-%N`dx3pXXmzS-%N`4W`x`3p@@mzS-<%N`@``?3p@@mzC->%N``dU3pAAmzC-%N`dx3pXXmzS-%N`W`x`3p@@mzS-<%N```3p@@mzC->%N`O`d@3pAAmzC-%N`dxM3pXXmzS-%N`W`x`\3p@@mzS-<%N```3p@@mzC->%N``d3pAAmzC-%N` dx$3pXXmzS-%N`d(W`$x`03p@@mzS-<%N`p(``o3p@@mzC->%N`(`d3pAAmzC-%N`)dx3pXXmzS-%N` 1W`4x`3p@@mzS-<%N`1``3p@@mzC->%N`X1`d33pAAmzC-%N`s2dx4pXXmzS-%N`9W`Dx`4p@@mzS-<%N`9``4p@@mzC->%N` :`d4pAAmzC-%N`6;dx 4pXXmzS-%N`4BW`Tx` 4p@@mzS-<%N`FB``E 4p@@mzC->%N`B`dC 4pAAmzC-%N`Cdx4pXXmzS-%N`JW`ex`4p@@mzS-<%N`J``4p@@mzC->%N`3K`d4pAAmzC-%N`1Ldxo4pXXmzS-%N`SW`ux`|4p@@mzS-<%N`S``4p@@mzC->%N`S`d4pAAmzC-%N`Udx$4pXXmzS-%N`;\W` x` %4p@@mzS-<%N`I\``P%4p@@mzC->%N`\`dv&4pAAmzC-%N`]dx-4pXXmzS-%N`dW`0x`-4p@@mzS-<%N`e`` .4p@@mzC->%N`Je`d/4pAAmzC-%N`GfdxA64pXXmzS-%N`mW`@x`L64p@@mzS-<%N`m``64p@@mzC->%N`m`d74pAAmzC-%N`ndx>4pXXmzS-%N`2vW`Px`?4p@@mzS-<%N`@v``??4p@@mzC->%N`v`d]@4pAAmzC-%N`wdxG4pXXmzS-%N`~W``x`G4p@@mzS-<%N`~``G4p@@mzC->%N`5`dH4pAAmzC-%N`,dxZP4pXXmzS-%N`W`px`fP4p@@mzS-<%N```P4p@@mzC->%N``dQ4pAAmzC-%N`dx Y4pXXmzS-%N`KW`x`Y4p@@mzS-<%N`V``YY4p@@mzC->%N``dnZ4pAAmzC-%N`dxa4pXXmzS-%N`W`x`a4p@@mzS-<%N```b4p@@mzC->%N`C`dc4pAAmzC-%N`Xdxsj4pXXmzS-%N`W`x`j4p@@mzS-<%N```j4p@@mzC->%N``dk4pAAmzC-%N`dx0s4pXXmzS-%N`pW`x`;s4p@@mzS-<%N`{``ys4p@@mzC->%N``dt4pAAmzC-%N`ԫdx{4pXXmzS-%N`,W`%x`{4p@@mzS-<%N`7``4|4p@@mzC->%N`t`dO}4pAAmzC-%N`dxr4pXXmzS-%N`W`5x`}4p@@mzS-<%N```4p@@mzC->%N``d…4pAAmzC-%N`dx&4pXXwS-%N`fW`Ex`24p@@wS-<%N`r``4p@@wC->%N``d4pAAwC-%N`dx4pXXwS-%N`$W`Ux`4p@@@wS-<%N`0``A4p@@wC->%N``dF4pAA@wC-%N`dx4pXXwS-%N`W`fx`4p@@@wS-<%N```4p@@wC->%N`;`d"4pAA@wC-%N`bdxd4pXX@wS-%N`W`vx`r4p@@wS-<%N```Ƨ4p@@@wC->%N``dè4pAAwC-%N`dx4pXX@wS-%N`VW` x`"4p@@wS-<%N`b``t4p@@@wC->%N``di4pAAwC-%N`dxӸ4pXXwS-%N`W`0x`߸4p@@wS-<%N```54p@@wC->%N`u`d(4pAAwC-%N`hdx4pXXwS-%N`W`@x`4p@@wS-<%N```4p@@wC->%N`6`d4pAAwC-%N`XdxK4pXXwS-%N`W`Px`V4p@@@wS-<%N```4p@@wC->%N``d4pAA@wC-%N`dx4pXXwS-%N` W``x`4p@@@wS-<%N` ``54p@@wC->%N`u `d4pAA@wC-%N`% dx4pXX@wS-%N`W`px`4p@@wS-<%N```4p@@@wC->%N`E`d4pAAwC-%N`Fdxh4pXX@wS-%N`W`x`s4p@@wS-<%N```4p@@@wC->%N``d4pAAwC-%N` dx 4pXXwS-%N``$W`x`.4p@@wS-<%N`n$``4p@@wC->%N`$`d4pAAwC-%N`%dx4pXXwS-%N`&-W`x`4p@@wS-<%N`3-``H4p@@wC->%N`-`dJ4pAAwC-%N`.d`4p@@wS-<%N`5`x5pXX@wS-%N`*:W`x`@5p@@@wC->%N`:`dD5pAAwC-%N`;dx 5pXXwS-%N`BW`&x` 5p@@@wS-<%N`B`` 5p@@wC->%N`C`d 5pAA@wC-%N``Ddxj5pXX@wS-%N`KW`6x`x5p@@wS-<%N`K``5p@@@wC->%N`L`d5pAAwC-%N`Ldx$5pXXwS-%N`dTW`Fx`25p@@@wS-<%N`rT``5p@@wC->%N`T`d5pAA@wC-%N`Udx%5pXXwS-%N`8]W`Vx`&5p@@wS-<%N`E]``\&5p@@wC->%N`]`dC'5pAAwC-%N`^dx.5pXXwS-%N`eW`gx`.5p@@wS-<%N`f``/5p@@wC->%N`]f`d05pAAwC-%N`Wgdx}75pXX@wS-%N`nW`wx`75p@@wS-<%N`n``75p@@@wC->%N`7o`d95pAAwC-%N`Bpdx@5pXXwS-%N`NwW` x`@5p@@@wS-<%N`\w``q@5p@@wC->%N`w`daA5pAA@wC-%N`xdxH5pXX@wS-%N`W`0x`H5p@@wS-<%N```H5p@@@wC->%N`7`dI5pAAwC-%N`#dxGQ5pXXwS-%N`W`@x`TQ5p@@@wS-<%N```Q5p@@wC->%N``dR5pAA@wC-%N`dxZ5pXXwS-%N`NW`Px`(Z5p@@wS-<%N`h``9Z5p@@wC->%N`y`ds[5pAAwC-%N`dxb5pXXwS-%N`W``x`b5p@@wS-<%N`+``c5p@@wC->%N`H`dAd5pAAwC-%N`dxak5pXX@wS-%N`W`px`ok5p@@wS-<%N```k5p@@@wC->%N``dl5pAAwC-%N`dx$t5pXXwS-%N`dW`x`1t5p@@@wS-<%N`q``t5p@@wC->%N`ȫ`du5pAA@wC-%N`Ȭdx|5pXX@wS-%N`!W`x`|5p@@wS-<%N`/``E}5p@@@wC->%N``dJ~5pAAwC-%N`dx5pXXwS-%N`W`x`5p@@@wS-<%N```5p@@wC->%N`U`d5pAA@wC-%N`Hdx5pXXwS-%N`<W`x` 5p@@wS-<%N`I``^5p@@wC->%N``dK5pAAwC-%N`dx5pXXwS-%N`W`'x`5p@@wS-<%N```5p@@wC->%N`8`d5pAAwC-%N`<dx5pXX@wS-%N`[W`7x`'5p@@wS-<%N`g``5p@@@wC->%N``dg5pAAwC-%N`dxӧ5pXXwS-%N`W`Gx`5p@@@wS-<%N`"``5p@@wC->%N`@`dH5pAA@wC-%N`dx5pXX@wS-%N`W`Wx`5p@@wS-<%N```5p@@@wC->%N`A`d5pAAwC-%N`Bdx5pXXwS-%N`PW`hx`"5p@@@wS-<%N`b``?5p@@wC->%N``dq5pAA@wC-%N`dx5pXXmzS-%N`<W`xx` 5p@@mzS-<%N`L``P5p@@mzC->%N``dp5pAAmzC-%N`dx5pXXmzS-%N` W` x`5p@@mzS-<%N` ``5p@@mzC->%N`3 `d 5pAAmzC-%N`I dxR5pXXmzS-%N` W`0x`_5p@@mzS-<%N` ``5p@@mzC->%N` `d5pAAmzC-%N` dx5pXXmzS-%N`? W`@x` 5p@@mzS-<%N`L ``Q5p@@mzC->%N` `dF5pAAmzC-%N` dx5pXXmzS-%N` W`Px`5p@@mzS-<%N` ``5p@@mzC->%N`T `d'5pAAmzC-%N`g dx{5pXXmzS-%N`$ W``x`5p@@mzS-<%N`$ ``5p@@mzC->%N` % `d5pAAmzC-%N`& dx85pXXmzS-%N`x- W`px`D5p@@mzS-<%N`- ``5p@@mzC->%N`- `d5pAAmzC-%N`. dx5pXXmzS-%N`?6 W`x` 5p@@mzS-<%N`K6 ``M5p@@mzC->%N`6 `db6pAAmzC-%N`7 dx6pXXmzS-%N`> W`x`6p@@mzS-<%N`> ``6p@@mzC->%N`!? `d 6pAAmzC-%N`_@ dx66pXXmzS-%N`vG W`x`C6p@@mzS-<%N`G ``6p@@mzC->%N`G `d6pAAmzC-%N`H dx6pXXmzS-%N`

      %N`P `dv6pAAmzC-%N`Q dx!6pXXmzS-%N` Y W`(x`!6p@@mzS-<%N`Y ``"6p@@mzC->%N`[Y `d@#6pAAmzC-%N`Z dxR*6pXXmzS-%N`a W`8x`^*6p@@mzS-<%N`a ``*6p@@mzC->%N`a `d+6pAAmzC-%N`c dx36pXXmzS-%N`Uj W`Hx`"36p@@mzS-<%N`bj ``c36p@@mzC->%N`j `dz46pAAmzC-%N`k dx;6pXXmzS-%N`s W`Xx`;6p@@mzS-<%N`s ``<6p@@mzC->%N`Ys `d=6pAAmzC-%N`Yt dxD6pXXmzS-%N`{ W`ix`D6p@@mzS-<%N`{ ``D6p@@mzC->%N`4| `dF6pAAmzC-%N`B} dxM6pXXmzS-%N` W`yx`M6p@@mzS-<%N` ``M6p@@mzC->%N`2 `dN6pAAmzC-%N`8 dxV6pXXmzS-%N`͍ W` x`V6p@@mzS-<%N`؍ ``V6p@@mzC->%N` `dW6pAAmzC-%N` dxI_6pXXmzS-%N` W`0x`V_6p@@mzS-<%N` ``_6p@@mzC->%N`Ӗ `d`6pAAmzC-%N` dx h6pXXmzS-%N`L W`@x`h6p@@mzS-<%N`X ``Xh6p@@mzC->%N` `d|i6pAAmzC-%N` dxp6pXXmzS-%N` W`Px`p6p@@mzS-<%N` ``q6p@@mzC->%N`W `d(r6pAAmzC-%N`h dx}y6pXXmzS-%N` W``x`y6p@@mzS-<%N`Ȱ ``y6p@@mzC->%N` `dz6pAAmzC-%N` dx=6pXXmzS-%N`} W`px`M6p@@mzS-<%N` ``6p@@mzC->%N`7 `d#6pAAmzC-%N`c dx6pXXmzS-%N`' W`x`6p@@mzS-<%N`4 ``56p@@mzC->%N`u `dn6pAAmzC-%N` dxe6pXXmzS-%N` W`x`q6p@@mzS-<%N` ``6p@@mzC->%N` `dĔ6pAAmzC-%N` dx(6pXXmzS-%N`h W` x`46p@@mzS-<%N`t ``u6p@@mzC->%N` `d6pAAmzC-%N` dx6pXXmzS-%N`% W`x`6p@@mzS-<%N`1 ``16p@@mzC->%N`q `d:6pAAmzC-%N`z dx6pXXmzS-%N` W`)x`6p@@mzS-<%N` ``6p@@mzC->%N`+ `d֮6pAAmzC-%N` dxU6pXXmzS-%N` W`9x`e6p@@mzS-<%N` ``6p@@mzC->%N` `d6pAAmzC-%N` dx6pXXmzS-%N`B W`Ix`6p@@mzS-<%N`P ``06p@@mzC->%N`p `d16pAAmzC-%N`q dx6pXXmzS-%N` W`Yx`6p@@mzS-<%N` ``6p@@mzC->%N`( `d6pAAmzC-%N`N dxi6pXXmzS-%N` W`jx`w6p@@mzS-<%N` ``6p@@mzC->%N` `d6pAAmzC-%N`! dx6pXXmzS-%N`P) W`zx`6p@@mzS-<%N`^) ``?6p@@mzC->%N`) `d-6pAAmzC-%N`m* dx6pXXmzS-%N`1 W` x`6p@@mzS-<%N`1 ``6p@@mzC->%N` 2 `d6pAAmzC-%N`&3 dxX7pXXmzS-%N`: W`0x`h7p@@mzS-<%N`: ``7p@@mzC->%N`: `d7pAAmzC-%N`< dx 7pXXmzS-%N``C W`@x`1 7p@@mzS-<%N`qC ``t 7p@@mzC->%N`C `d 7pAAmzC-%N`D dx}7pXXmzS-%N`K W`Px`7p@@mzS-<%N`K ``7p@@mzC->%N`L `d7pAAmzC-%N`'M dx7pXXmzS-%N`8T W``x`7p@@mzS-<%N`FT ``[7p@@mzC->%N`T `dX7pAAmzC-%N`U dx%7pXXmzS-%N`\ W`px`%7p@@mzS-<%N`\ ``%7p@@mzC->%N`=] `d&7pAAmzC-%N`8^ dxo.7pXXmzS-%N`e W`x`|.7p@@mzS-<%N`e ``.7p@@mzC->%N`e `d/7pAAmzC-%N`g d`77p@@mzS-<%N`Rn `x1;7pXXmzS-%N`qr W`x`;7p@@mzC->%N`r `d<7pAAmzC-%N`s dxC7pXXmzS-%N`z W` x`C7p@@mzS-<%N`z ``C7p@@mzC->%N`2{ `dD7pAAmzC-%N`.| dxeL7pXXmzS-%N` W`x`rL7p@@mzS-<%N` ``L7p@@mzC->%N` `dM7pAAmzC-%N` dxCU7pXXwS-%N` W`*x`TU7p@@@wS-<%N` ``U7p@@wC->%N` `dV7pAA@wC-%N`ҍ dx ^7pXX@wS-%N`K W`:x`^7p@@wS-<%N`X ``7^7p@@@wC->%N`w `d^_7pAAwC-%N` dxf7pXXwS-%N` W`Jx`f7p@@@wS-<%N` ``1g7p@@wC->%N`q `dh7pAA@wC-%N`Y dxo7pXXmzS-%N` W`Zx`o7p@@mzS-<%N` ``o7p@@mzC->%N`3 `dp7pAAmzC-%N`7 dxCx7pXXmzS-%N` W` kx`Rx7p@@mzS-<%N` ``tx7p@@mzC->%N` `dpy7pAAmzC-%N` dx̀7pXXmzS-%N` W` {x`܀7p@@mzS-<%N` ``7p@@mzC->%N`? `d$7pAAmzC-%N`d dxT7pXXmzS-%N` W` x`c7p@@mzS-<%N` ``7p@@mzC->%N` `d7pAAmzC-%N` dxґ7pXXmzS-%N` W` 0x`7p@@mzS-<%N`" ``7p@@mzC->%N`B `d 7pAAmzC-%N`I dx]7pXX$yS-%N` W` @x`x7p@@@$yS-<%N` ``7p@@$yC->%N` `d{7pAA@$yC-%N` dx7pXX$yS-%N` W` Px`Ţ7p@@$yS-<%N` ``7p@@$yC->%N`$ `dң7pAA$yC-%N` dx67pXXmzS-%N`v W` `x`I7p@@mzS-<%N` ``j7p@@mzC->%N` `d7pAAmzC-%N` dx7pXXmzS-%N`) W` px`7p@@mzS-<%N`: ``=7p@@mzC->%N`} `dg7pAAmzC-%N` dxh7pXXmzS-%N` W` x`w7p@@mzS-<%N` ``7p@@mzC->%N` `dϽ7pAAmzC-%N` dx$7pXXmzS-%N`d W` x`27p@@mzS-<%N`r ``r7p@@mzC->%N` `dl7pAAmzC-%N` dx7pXXmzS-%N` W`  x`7p@@mzS-<%N` ``7p@@mzC->%N`: `d7pAAmzC-%N`< dx 7pXXmzS-%N`I W` x`7p@@mzS-<%N`Y ``\7p@@mzC->%N` `db7pAAmzC-%N` dx7pXXmzS-%N` W` +x`7p@@mzS-<%N` ``7p@@mzC->%N`R `d-7pAAmzC-%N`m dxI7pXXmzS-%N` W` ;x`Y7p@@mzS-<%N` ``7p@@mzC->%N` `d7pAAmzC-%N` dx7pXXmzS-%N`& W` Kx`7p@@mzS-<%N`& ``7p@@mzC->%N`1' `d 7pAAmzC-%N`K( dxg7pXXmzS-%N`/ W` [x`t7p@@mzS-<%N`/ ``7p@@mzC->%N`/ `d7pAAmzC-%N`1 dx8pXXmzS-%N`Z8 W` lx`'8p@@mzS-<%N`g8 ``h8p@@mzC->%N`8 `d8pAAmzC-%N`-: dx 8pXXmzS-%N`@ W` |x` 8p@@mzS-<%N`A `` 8p@@mzC->%N`HA `d% 8pAAmzC-%N`eB dx8pXXmzS-%N`H W` x`8p@@mzS-<%N` I `` 8p@@mzC->%N`LI `d-8pAAmzC-%N`mJ dxK8pXXmzS-%N`Q W` 0x`X8p@@mzS-<%N`Q ``8p@@mzC->%N`Q `d8pAAmzC-%N`R dx#8pXXmzS-%N`OZ W` @x`#8p@@mzS-<%N`\Z ``b#8p@@mzC->%N`Z `du$8pAAmzC-%N`[ dx+8pXXmzS-%N`c W` Px`+8p@@mzS-<%N`c `` ,8p@@mzC->%N``c `d4-8pAAmzC-%N`td dx48pXXmzS-%N`k W` `x`48p@@mzS-<%N`k ``48p@@mzC->%N`6l `d68pAAmzC-%N`]m dx=8pXXmzS-%N`\t W` px`+=8p@@mzS-<%N`kt ``k=8p@@mzC->%N`t `dv>8pAAmzC-%N`u dxE8pXXmzS-%N`} W` x`E8p@@mzS-<%N`#} ``&F8p@@mzC->%N`f} `d6G8pAAmzC-%N`v~ dxN8pXXmzS-%N`ۅ W` x`N8p@@mzS-<%N` ``N8p@@mzC->%N`* `dP8pAAmzC-%N`C dx[W8pXXmzS-%N` W`  x`oW8p@@mzS-<%N` ``W8p@@mzC->%N` `dX8pAAmzC-%N`# dx_8pXXmzS-%N`9 W` x``8p@@mzS-<%N`F ``E`8p@@mzC->%N` `dMa8pAAmzC-%N` dxQh8pXXmzS-%N` W` ,x`^h8p@@mzS-<%N` ``h8p@@mzC->%N` `di8pAAmzC-%N`֠ dxq8pXXmzS-%N`U W` <x`#q8p@@mzS-<%N`c ``Kq8p@@mzC->%N` `d|r8pAAmzC-%N` dxy8pXX@mzS-%N` W` Lx`y8p@@@mzS-<%N`" ``&z8p@@@mzC->%N`f `d={8pAA@mzC-%N`} dx8pXX@mzS-%N`߹ W` \x`8p@@@mzS-<%N` ``8p@@@mzC->%N`0 `d*8pAA@mzC-%N`j dxn8pXXmzS-%N` W` mx`{8p@@mzS-<%N` ``8p@@mzC->%N` `d8pAAmzC-%N` dx#8pXXmzS-%N`c W` }x`28p@@mzS-<%N`r ``s8p@@mzC->%N` `d8pAAmzC-%N` dx8pXXmzS-%N`$ W` x`8p@@mzS-<%N`1 ``48p@@mzC->%N`t `d8pAAmzC-%N`R dxC8pXXmzS-%N` W` 0x`R8p@@mzS-<%N` ``t8p@@mzC->%N` `d8pAAmzC-%N` dx̭8pXXmzS-%N` W` @x`ح8p@@mzS-<%N` ``'8p@@mzC->%N`g `d68pAAmzC-%N`v dx[8pXXmzS-%N` W` Px`h8p@@mzS-<%N` ``8p@@mzC->%N` `d8pAAmzC-%N` dx8pXXmzS-%N`[ W` `x`(8p@@mzS-<%N`h ``m8p@@mzC->%N` `dp8pAAmzC-%N` dx8pXXmzS-%N` W` px`8p@@mzS-<%N` ``8p@@mzC->%N`/ `d8pAAmzC-%N`C dxZ8pXXmzS-%N` W` x`h8p@@mzS-<%N` ``8p@@mzC->%N` `d8pAAmzC-%N` dx8pXXmzS-%N`\ W` x`)8p@@mzS-<%N`i ``k8p@@mzC->%N` `d~8pAAmzC-%N` dx8pXXmzS-%N` W`  x`8p@@mzS-<%N`, ``.8p@@mzC->%N`n `d78pAAmzC-%N`w dx8pXXmzS-%N`! W` x`8p@@mzS-<%N`! ``8p@@mzC->%N`5" `d8pAAmzC-%N`C# dxY8pXXmzS-%N`* W` -x`g8p@@mzS-<%N`* ``8p@@mzC->%N`* `d8pAAmzC-%N`, dx8pXXmzS-%N`N3 W` =x`8p@@@mzS-<%N`[3 ``]8p@@mzC->%N`3 `do8pAA@mzC-%N`4 dx9pXX@mzS-%N`; W` Mx`9p@@@mzS-<%N`; ``9p@@@mzC->%N`)< `d9pAA@mzC-%N`H= dxZ 9pXXmzS-%N`D W` ]x`g 9p@@mzS-<%N`D `` 9p@@mzC->%N`D `d9pAAmzC-%N`E dx9pXXmzS-%N`[M W` nx`(9p@@mzS-<%N`hM ``j9p@@mzC->%N`M `dh9pAAmzC-%N`N dx9pXXmzS-%N`V W` ~x`9p@@mzS-<%N`V `` 9p@@mzC->%N``V `d8 9pAAmzC-%N`xW dx'9pXXmzS-%N`^ W` x`'9p@@mzS-<%N`^ ``'9p@@mzC->%N`_ `d(9pAAmzC-%N`"` dxR09pXXmzS-%N`g W` 0x`_09p@@mzS-<%N`g ``09p@@mzC->%N`g `d19pAAmzC-%N`h dx99pXXmzS-%N`Rp W` @x`99p@@mzS-<%N`^p ``99p@@mzC->%N`q `d:9pAAmzC-%N`"r dxA9pXX@mzS-%N`y W` Px`A9p@@@mzS-<%N`$y ``/B9p@@@mzC->%N`oy `d_C9pAA@mzC-%N`z dxJ9pXX@mzS-%N`Ձ W` `x`J9p@@@mzS-<%N` ``J9p@@@mzC->%N`! `dK9pAA@mzC-%N`0 d`2S9p@@mzS-<%N`r `xW9pXXmzS-%N`Î W` px`W9p@@mzC->%N` `dX9pAAmzC-%N` dx`9pXXmzS-%N`S W` x`#`9p@@mzS-<%N`c ``c`9p@@mzC->%N` `dja9pAAmzC-%N` dxh9pXXmzS-%N` W` x`h9p@@mzS-<%N`Ο ``h9p@@mzC->%N` `di9pAAmzC-%N` dx+q9pXXmzS-%N`k W` x`;q9p@@mzS-<%N`{ ``[q9p@@mzC->%N` `dr9pAAmzC-%N` dxy9pXXmzS-%N` W` x`y9p@@mzS-<%N` ``#z9p@@mzC->%N`c `d{9pAAmzC-%N`] dx9pXXmzS-%N`Ĺ W` .x`9p@@mzS-<%N`й ``Ђ9p@@mzC->%N` `d9pAAmzC-%N`$ dxP9pXXmzS-%N` W` >x`^9p@@mzS-<%N` ``9p@@mzC->%N` `d9pAAmzC-%N` dx9pXXPUzS-%N`$ W` Nx`9p@@fzS-<%N`3 ``G9p@@PUzC->%N` `dI9pAAfzC-%N` dxk9pXXfzS-%N` W` ^x`z9p@@fzS-<%N` ``ɜ9p@@fzC->%N` `dϝ9pAAfzC-%N` dxӤ9pXX@zS-%N` W` ox`9p@@zS-<%N`& ``9p@@@zC->%N`P `d 9pAAzC-%N`` dx\9pXXzS-%N` W` x`n9p@@zS-<%N` ``9p@@zC->%N` `d9pAAzC-%N` dx 9pXXzS-%N`` W` x`/9p@@zS-<%N`o ``X9p@@zC->%N` `do9pAAzC-%N` dx9pXXzS-%N`. W` 0x`9p@@zS-<%N`? ``!9p@@zC->%N`a `d`9pAAzC-%N` dx9pXXzS-%N` W` @x`9p@@@zS-<%N` ``9p@@zC->%N`* `d 9pAA@zC-%N`K dxe9pXXzS-%N` W` Px`s9p@@zS-<%N` ``9p@@zC->%N` `d9pAAzC-%N` dx,9pXXzS-%N`l W` `x`;9p@@zS-<%N`{ ``c9p@@zC->%N` `d9pAAzC-%N` dx9pXXzS-%N`4 W` px`9p@@zS-<%N`B ``!9p@@zC->%N`a `dS9pAAzC-%N` dx9pXXzS-%N` " W` x`9p@@zS-<%N`" ``9p@@zC->%N`B" `d69pAAzC-%N`v# dx\9pXXzS-%N`* W` x`k9p@@@zS-<%N`* ``9p@@zC->%N`* `d9pAA@zC-%N`+ dx&9pXXzS-%N`f3 W` x`49p@@zS-<%N`t3 ``]9p@@zC->%N`3 `d9pAAzC-%N`4 dx:pXXzS-%N`%< W` x`:p@@zS-<%N`4< ``:p@@zC->%N`[< `d`:pAAzC-%N`= dx :pXXzS-%N`D W` /x` :p@@zS-<%N`D `` :p@@zC->%N`ME `d:pAAzC-%N`SF dxi:pXXzS-%N`M W` ?x`w:p@@zS-<%N`M ``:p@@zC->%N`M `d:pAAzC-%N`N dx&:pXX@zS-%N`fV W` Ox`3:p@@zS-<%N`sV ``:p@@@zC->%N`V `dY :pAAzC-%N`W dx':pXXzS-%N`^ W` _x`':p@@zS-<%N`_ ``(:p@@zC->%N`X_ `d(:pAAzC-%N`?` dxu0:pXXzS-%N`g W`px`0:p@@zS-<%N`g ``0:p@@zC->%N`g `d1:pAAzC-%N`i dx99:pXXzS-%N`yp W`x`I9:p@@zS-<%N`p ``h9:p@@zC->%N`p `d::pAAzC-%N`q dxA:pXXzS-%N`/y W` x`A:p@@@zS-<%N`?y ``B:p@@zC->%N`^y `dPC:pAA@zC-%N`z dxJ:pXXzS-%N` W`0x`J:p@@zS-<%N` ``J:p@@zC->%N`# `dK:pAAzC-%N`' dxtS:pXXzS-%N` W`@x`S:p@@zS-<%N`Ċ ``S:p@@zC->%N` `dT:pAAzC-%N`> dx[:pXXzS-%N`& W`Px`\:p@@zS-<%N`@ ``n\:p@@zC->%N` `d]]:pAAzC-%N` dxd:pXXzS-%N`ޛ W``x`d:p@@zS-<%N` ``d:p@@zC->%N` `de:pAAzC-%N`? dxfm:pXXzS-%N` W`px`vm:p@@@zS-<%N` ``m:p@@zC->%N`դ `dn:pAA@zC-%N` dx"v:pXXzS-%N`b W`x`1v:p@@zS-<%N`q ``Nv:p@@zC->%N` `dw:pAAzC-%N` dx~:pXXvDwS-%N` W`x`~:p@@}DwS-<%N` ``~:p@@vDwC->%N`< `d:pAA}DwC-%N`5 dxY:pXX@rDwS-%N` W`x`d:p@@wDwS-<%N` ``:p@@@rDwC->%N`ھ `d:pAAwDwC-%N` dxݏ:pXXDwS-%N` W` x`:p@@@{DwS-<%N`) ``:p@@DwC->%N`^ `d<:pAA@{DwC-%N`| dx:pXX@{DwS-%N` W`0x`:p@@DwS-<%N` ``ј:p@@@{DwC->%N` `d:pAADwC-%N`P dxe:pXXwDwS-%N` W`@x`r:p@@@rDwS-<%N` ``:p@@wDwC->%N` `dҢ:pAA@rDwC-%N` dx:pXX}DwS-%N`D W`Px`:p@@vDwS-<%N`R ``P:p@@}DwC->%N` `dl:pAAvDwC-%N` dxDz:pXXzDwS-%N` W``x`ղ:p@@@~DwS-<%N` ``:p@@zDwC->%N`= `d:pAA@~DwC-%N`N dx:pXXvDwS-%N` W`qx`:p@@pDwS-<%N` ``ɻ:p@@vDwC->%N` `d:pAApDwC-%N`: dx;:pXXyDwS-%N`{ W`x`G:p@@@uDwS-<%N` ``o:p@@yDwC->%N` `d:pAA@uDwC-%N` dx:pXX@uDwS-%N`<W` x`:p@@yDwS-<%N`H``1:p@@@uDwC->%N`q`d@:pAAyDwC-%N`dx:pXXpDwS-%N` W`0x`:p@@vDwS-<%N` ``:p@@pDwC->%N`0 `d:pAAvDwC-%N`Hdxp:pXX@~DwS-%N`W`@x`|:p@@zDwS-<%N```B:p@@@~DwC->%N``dI:pAAzDwC-%N`dx0:pXXvDwS-%N`pW`Px`<:p@@}DwS-<%N`|``^:p@@vDwC->%N``d:pAA}DwC-%N`dx:pXX@rDwS-%N`''W``x`:p@@wDwS-<%N`2'``:p@@@rDwC->%N`['`dW:pAAwDwC-%N`(dxo:pXXDwS-%N`/W`px`|:p@@@{DwS-<%N`/``:p@@DwC->%N`/`d:pAA@{DwC-%N`1dx&;pXX@{DwS-%N`f8W`x`1;p@@DwS-<%N`q8``[;p@@@{DwC->%N`8`dj;pAADwC-%N`9dx ;pXXwDwS-%N` AW`x` ;p@@@rDwS-<%N`.A`` ;p@@wDwC->%N`MA`d4 ;pAA@rDwC-%N`tBdx;pXX}DwS-%N`IW`x`;p@@vDwS-<%N`I``;p@@}DwC->%N`J`d ;pAAvDwC-%N`JKdxf;pXXzDwS-%N`RW`!x`q;p@@@~DwS-<%N`R``;p@@zDwC->%N`R`d;pAA@~DwC-%N`Sdx'$;pXXvDwS-%N`g[W`1x`3$;p@@pDwS-<%N`s[``\$;p@@vDwC->%N`[`dl%;pAApDwC-%N`\dx,;pXXyDwS-%N`2dW`Ax`,;p@@@uDwS-<%N`>d``'-;p@@yDwC->%N`gd`d<.;pAA@uDwC-%N`|edx5;pXX@uDwS-%N`lW`Qx`5;p@@yDwS-<%N`l``5;p@@@uDwC->%N`m`d7;pAAyDwC-%N`Pndx]>;pXXpDwS-%N`uW`ax`g>;p@@vDwS-<%N`u``>;p@@pDwC->%N`u`dZ;pAAvDwC-%N`)dxQ`;pXX@~DwS-%N`W`rx`_`;p@@zDwS-<%N````;p@@@~DwC->%N`ɗ`da;pAAzDwC-%N`٘dxi;pXXvDwS-%N`RW`x` i;p@@}DwS-<%N````Ii;p@@vDwC->%N``doj;pAA}DwC-%N`dxq;pXX@rDwS-%N`W` x`q;p@@wDwS-<%N```q;p@@@rDwC->%N``d s;pAAwDwC-%N`Jdxz;pXXDwS-%N`FW`0x`z;p@@@{DwS-<%N`T``Ez;p@@DwC->%N``di{;pAA@{DwC-%N`dxǂ;pXX@{DwS-%N`W`@x`҂;p@@DwS-<%N```;p@@@{DwC->%N`B`d;pAADwC-%N`^d`{;p@@wDwS-<%N``xُ;pXX@rDwS-%N`W`Px`;p@@@rDwC->%N`W`dR;pAAwDwC-%N`dx;pXX}DwS-%N`W``x`;p@@vDwS-<%N```;p@@}DwC->%N` `d;pAAvDwC-%N`;dxR;pXXzDwS-%N`W`px`_;p@@@~DwS-<%N```;p@@zDwC->%N``dl;pAA@~DwC-%N`dx-;pXXvDwS-%N`mW`x`?;p@@pDwS-<%N```~;p@@vDwC->%N``de;pAApDwC-%N`dxQ;pXXyDwS-%N`W`x`c;p@@@uDwS-<%N```;p@@yDwC->%N``d´;pAA@uDwC-%N`dx-;pXX@uDwS-%N`mW`x`>;p@@yDwS-<%N`~``;p@@@uDwC->%N``d;pAAyDwC-%N`dx;pXXpDwS-%N` W`"x`;p@@vDwS-<%N` ``;p@@pDwC->%N`\ `d7;pAAvDwC-%N`w dx;pXX@~DwS-%N`W`2x`;p@@zDwS-<%N```;p@@@~DwC->%N``d;pAAzDwC-%N`%dx;pXXvDwS-%N`VW`Bx`*;p@@}DwS-<%N`j``k;p@@vDwC->%N``d{;pAA}DwC-%N`dx;pXXwDwS-%N`-$W`Rx`;p@@@rDwS-<%N`>$``G;p@@wDwC->%N`$`d;pAA@rDwC-%N`%dx;pXXDwS-%N`,W`bx`;p@@@{DwS-<%N`-``;p@@DwC->%N`N-`d$;pAA@{DwC-%N`d.dx;pXX@{DwS-%N`5W`sx`;p@@DwS-<%N`5``;p@@@{DwC->%N`6`d;pAADwC-%N`7dx:W`x`L``%N`>`d%N`'`d%N`4`d5%N``dr"%N`!`d(+%N`'`d3%N`0`d<%N`r9`dE%N`8B`d(N%N`J`dV%N`S`d_%N`i\`dh%N`d`dp%N`{`d7%N``d%N`w`dy%N`N`d=%N``d.%N``d%N``d%N`^`dG%N``dQ%N``d%N`d`di%N`&`d$%N``d%N`e`d%N`S`d]=pAAzC-%N`]dx=pXXzS-%N`W`Dx`=p@@zS-<%N``` =p@@zC->%N``d =pAAzC-%N`dxb=pXXzS-%N`bW`Tx`m=p@@zS-<%N`m``=p@@zC->%N``d=pAAzC-%N`dx=pXXzS-%N`W`dx`%=p@@zS-<%N`%``F=p@@zC->%N`F`dz=pAAzC-%N`zdx"=pXXzS-%N`W`ux`"=p@@zS-<%N```K#=p@@zC->%N`K`d<$=pAAzC-%N`<dxN+=pXXzS-%N`N W`x`Z+=p@@zS-<%N`Z ``+=p@@zC->%N` `d,=pAAzC-%N`!dx9A=pXXzS-%N`96W` x`EA=p@@zS-<%N`E6``A=p@@zC->%N`6`dB=pAAzC-%N`7dxI=pXXzS-%N`>W`0x`I=p@@zS-<%N`>``]J=p@@zC->%N`]?`diK=pAAzC-%N`i@dxR=pXXzS-%N`GW`@x`R=p@@zS-<%N`G``%S=p@@zC->%N`%H`d6T=pAAzC-%N`6Idx\[=pXXzS-%N`\PW`Px`g[=p@@zS-<%N`gP``[=p@@zC->%N`P`d\=pAAzC-%N`Qdxd=pXXzS-%N`YW``x`d=p@@zS-<%N`Y``rd=p@@zC->%N`rY`de=pAAzC-%N`Zdxl=pXXzS-%N`aW`px`l=p@@zS-<%N`a``l=p@@zC->%N`a`d'n=pAAzC-%N`'cdxu=pXXzS-%N`jW`x`u=p@@zS-<%N`j``u=p@@zC->%N`j`dv=pAAzC-%N`kdx@~=pXXzS-%N`@sW`x`L~=p@@zS-<%N`Ls``~=p@@zC->%N`s`d=pAAzC-%N`tdx =pXX@uDwS-%N` |W`x`=p@@yDwS-<%N`|``T=p@@@uDwC->%N`T|`de=pAAyDwC-%N`e}dx=pXXpDwS-%N`W`%x`ŏ=p@@vDwS-<%N`ń`` =p@@pDwC->%N` `d=pAAvDwC-%N`dxG=pXX@~DwS-%N`GW`5x`X=p@@zDwS-<%N`X``=p@@@~DwC->%N``d=pAAzDwC-%N`dx=pXXvDwS-%N`W`Ex` =p@@}DwS-<%N` ``U=p@@vDwC->%N`U`d_=pAA}DwC-%N`_dx=pXXwDwS-%N`W`Ux`=p@@@rDwS-<%N```=p@@wDwC->%N``dߪ=pAA@rDwC-%N`ߟdxH=pXXDwS-%N`HW`ex`W=p@@@{DwS-<%N`W``=p@@DwC->%N``dͳ=pAA@{DwC-%N`ͨdx=pXX@{DwS-%N`W`vx`"=p@@DwS-<%N`"``j=p@@@{DwC->%N`j`dM=pAADwC-%N`Mdx=pXX@rDwS-%N`ڸW`x`=p@@wDwS-<%N```3=p@@@rDwC->%N`3`d%=pAAwDwC-%N`%d`=p@@}DwS-<%N``x=pXXvDwS-%N`W` x`B=p@@vDwC->%N`B`dJ=pAA}DwC-%N`Jdxq=pXXzDwS-%N`qW`0x`=p@@@~DwS-<%N```=p@@zDwC->%N``d=pAA@~DwC-%N`dx=pXXvDwS-%N`W`@x`#=p@@pDwS-<%N`#``c=p@@vDwC->%N`c`di=pAApDwC-%N`idx=pXXyDwS-%N`W`Px`=p@@@uDwS-<%N```=p@@yDwC->%N``d =pAA@uDwC-%N` dxV=pXX@uDwS-%N`VW``x`f=p@@yDwS-<%N`f``=p@@@uDwC->%N``d=pAAyDwC-%N`dx =pXXpDwS-%N` W`px`=p@@vDwS-<%N```c=p@@pDwC->%N`c`dk=pAAvDwC-%N`kdx>pXX@~DwS-%N`W`x`>p@@zDwS-<%N```>p@@@~DwC->%N``d6>pAAzDwC-%N`6dx >pXXzS-%N`W`x` >p@@zS-<%N``` >p@@zC->%N``d>pAAzC-%N`dx9>pXXzS-%N`9 W`x`F>p@@zS-<%N`F ``>p@@zC->%N` `d>pAAzC-%N` dx>pXXzS-%N`W`&x`>p@@zS-<%N```^>p@@zC->%N`^`d >pAAzC-%N`d`'>p@@}DwS-<%N``x+>pXXvDwS-%N` W`6x`+>p@@vDwC->%N` `d->pAA}DwC-%N`"dx04>pXXwDwS-%N`0)W`Fx`A4>p@@@rDwS-<%N`A)``4>p@@wDwC->%N`)`d5>pAA@rDwC-%N`*dx<>pXXDwS-%N`1W`Vx`=>p@@@{DwS-<%N`2``J=>p@@DwC->%N`J2`dS>>pAA@{DwC-%N`S3dxE>pXX@{DwS-%N`:W`fx`E>p@@DwS-<%N`:``F>p@@@{DwC->%N`;`dG>pAADwC-%N`<dxwN>pXX@rDwS-%N`wCW`wx`N>p@@wDwS-<%N`C``N>p@@@rDwC->%N`C`dO>pAAwDwC-%N`DdxW>pXX}DwS-%N`LW`x`W>p@@vDwS-<%N`L``0W>p@@}DwC->%N`0L`d=X>pAAvDwC-%N`=Mdx_>pXXzDwS-%N`TW` x`_>p@@@~DwS-<%N`T``_>p@@zDwC->%N`T`d`>pAA@~DwC-%N`UdxYh>pXXvDwS-%N`Y]W`0x`ih>p@@pDwS-<%N`i]``h>p@@vDwC->%N`]`di>pAApDwC-%N`^dxp>pXXyDwS-%N`eW`@x`q>p@@@uDwS-<%N`f``.q>p@@yDwC->%N`.f`d+r>pAA@uDwC-%N`+gdxy>pXX@uDwS-%N`nW`Px`y>p@@yDwS-<%N`n``y>p@@@uDwC->%N`n`dz>pAAyDwC-%N`odx?>pXXpDwS-%N`?wW``x`Q>p@@vDwS-<%N`Qw``p>p@@pDwC->%N`pw`do>pAAvDwC-%N`oxdx>pXX@~DwS-%N`W`px` >p@@zDwS-<%N` ``'>p@@@~DwC->%N`'`d'>pAAzDwC-%N`'dx>pXXvDwS-%N`W`x`“>p@@}DwS-<%N`ˆ``>p@@vDwC->%N``d>pAA}DwC-%N`dx6>pXXwDwS-%N`6W`x`F>p@@@rDwS-<%N`F``f>p@@wDwC->%N`f`df>pAA@rDwC-%N`fdx >pXXDwS-%N` W`x`)>p@@@{DwS-<%N`)``g>p@@DwC->%N`g`do>pAA@{DwC-%N`odxݻ>pXX@zS-%N`ݰW`'x`>p@@zS-<%N```>p@@@zC->%N``di>pAAzC-%N`idx`>pXXzS-%N``W`7x`n>p@@zS-<%N`n``>p@@zC->%N`Ź`d>pAAzC-%N`׺dx>pXXzS-%N`W`Gx`)>p@@zS-<%N`)``>p@@zC->%N``d>pAAzC-%N`dx>pXXzS-%N`W`Wx`>p@@zS-<%N```5>p@@zC->%N`5`d5>pAAzC-%N`5dx>pXXzS-%N`W`gx`>p@@@zS-<%N```>p@@zC->%N``d>pAA@zC-%N`dxc>pXXzS-%N`cW`xx`p>p@@zS-<%N`p``>p@@zC->%N``d>pAAzC-%N`dx>pXXzS-%N`W`x`>p@@zS-<%N```#>p@@zC->%N`#`d_>pAAzC-%N`_dx>pXXzS-%N`W` x`>p@@zS-<%N```>p@@zC->%N``d>pAAzC-%N`dxh?pXXzS-%N`hW`0x`v?p@@zS-<%N`v``?p@@zC->%N``d?pAAzC-%N`dx- ?pXXzS-%N`-W`@x`: ?p@@@zS-<%N`:`` ?p@@zC->%N``d ?pAA@zC-%N`dx?pXX@{DwS-%N`W`Px`?p@@DwS-<%N```?p@@@{DwC->%N``d?pAADwC-%N`dxd?pXX@rDwS-%N`dW``x`u?p@@wDwS-<%N`u``?p@@@rDwC->%N``d?pAAwDwC-%N`dx#?pXX}DwS-%N`W`px`$?p@@vDwS-<%N```)$?p@@}DwC->%N`)`d(%?pAAvDwC-%N`(dx,?pXXzDwS-%N`!W`x`,?p@@@~DwS-<%N`!``,?p@@zDwC->%N`!`d-?pAA@~DwC-%N`"dx45?pXXvDwS-%N`4*W`x`P5?p@@pDwS-<%N`P*``5?p@@vDwC->%N`*`d6?pAApDwC-%N`+dx=?pXXyDwS-%N`2W`x`=?p@@@uDwS-<%N`2``>?p@@yDwC->%N`3`d??pAA@uDwC-%N`4dxF?pXX@uDwS-%N`;W`(x`F?p@@yDwS-<%N`;``F?p@@@uDwC->%N`;`dG?pAAyDwC-%N`<dxN?pXXpDwS-%N`CW`8x`O?p@@vDwS-<%N`D``WO?p@@pDwC->%N`WD`dLP?pAAvDwC-%N`LEdxW?pXX@~DwS-%N`LW`Hx`W?p@@zDwS-<%N`L``X?p@@@~DwC->%N`M`dY?pAAzDwC-%N`Ndxy`?pXXvDwS-%N`yUW`Xx``?p@@}DwS-<%N`U```?p@@vDwC->%N`U`da?pAA}DwC-%N`Vdx2i?pXXwDwS-%N`2^W`hx`@i?p@@@rDwS-<%N`@^``ai?p@@wDwC->%N`a^`dj?pAA@rDwC-%N`_dxq?pXXDwS-%N`fW`yx`q?p@@@{DwS-<%N`f`` r?p@@DwC->%N` g`d3s?pAA@{DwC-%N`3hdxz?pXX@{DwS-%N`oW`x`z?p@@DwS-<%N`o``z?p@@@{DwC->%N`o`d{?pAADwC-%N`pdxs?pXX@rDwS-%N`sxW` x`?p@@wDwS-<%N`x``?p@@@rDwC->%N`x`d?pAAwDwC-%N`ydx?pXX}DwS-%N`W`0x`?p@@vDwS-<%N```:?p@@}DwC->%N`:`d:?pAAvDwC-%N`:dx?pXXzDwS-%N`W`@x`?p@@@~DwS-<%N```?p@@zDwC->%N``d?pAA@~DwC-%N`dxh?pXXvDwS-%N`hW`Px`|?p@@pDwS-<%N`|``?p@@vDwC->%N``d?pAApDwC-%N`dx(?pXXyDwS-%N`(W``x`5?p@@@uDwS-<%N`5``l?p@@yDwC->%N`l`dç?pAA@uDwC-%N`Üdx?pXX@uDwS-%N`W`px`î?p@@yDwS-<%N`ã``?p@@@uDwC->%N``d0?pAAyDwC-%N`0dxq?pXXpDwS-%N`qW`x`~?p@@vDwS-<%N`~``?p@@pDwC->%N``dѸ?pAAvDwC-%N`ѭdx?pXX@~DwS-%N`W` x`?p@@zDwS-<%N````?p@@@~DwC->%N```df?pAAzDwC-%N`fdx?pXXvDwS-%N`ؽW`x`?p@@}DwS-<%N```,?p@@vDwC->%N`,`d)?pAA}DwC-%N`)dx?pXXwDwS-%N`W`)x` ?p@@@rDwS-<%N` ``N?p@@wDwC->%N`N`dS?pAA@rDwC-%N`Sdxn?pXXzS-%N`nW`9x`|?p@@zS-<%N`|``?p@@zC->%N``d?pAAzC-%N`dx?pXXzS-%N`W`Ix`$?p@@zS-<%N`$``?p@@zC->%N``d?pAAzC-%N`dx?pXXzS-%N`W`Yx`?p@@zS-<%N```?p@@zC->%N``d?pAAzC-%N`dxV?pXXzS-%N`VW`ix`g?p@@zS-<%N`g``?p@@zC->%N``d@pAAzC-%N`d`@p@@@zS-<%N` `x@pXXzS-%N`W`zx`@p@@zC->%N``d@pAA@zC-%N`dxF#@pXXzS-%N`FW`x`T#@p@@zS-<%N`T``|#@p@@zC->%N`|`d$@pAAzC-%N`dx ,@pXXzS-%N` !W` x`,@p@@zS-<%N`!``C,@p@@zC->%N`C!`dl-@pAAzC-%N`l"dxd4@pXXzS-%N`d)W`0x`s4@p@@zS-<%N`s)``4@p@@zC->%N`)`d5@pAAzC-%N`*dx=@pXXzS-%N`2W`@x`=@p@@@zS-<%N`2``?=@p@@zC->%N`?2`dS>@pAA@zC-%N`S3dx"F@pXXzS-%N`";W`Px`3F@p@@zS-<%N`3;``uF@p@@zC->%N`u;`dG@pAAzC-%N`<dxN@pXXDwS-%N`CW``x`N@p@@@{DwS-<%N`C``DO@p@@DwC->%N`DD`dWP@pAA@{DwC-%N`WEdxrW@pXX@{DwS-%N`rLW`px`W@p@@DwS-<%N`L``W@p@@@{DwC->%N`L`dX@pAADwC-%N`Mdx `@pXX@rDwS-%N` UW`x`)`@p@@wDwS-<%N`)U``i`@p@@@rDwC->%N`iU`doa@pAAwDwC-%N`oVdxh@pXX}DwS-%N`]W` x`h@p@@vDwS-<%N`]``h@p@@}DwC->%N`]`di@pAAvDwC-%N`^dx|q@pXXzDwS-%N`|fW`x`q@p@@@~DwS-<%N`f``q@p@@zDwC->%N`f`dr@pAA@~DwC-%N`gdx.z@pXXvDwS-%N`.oW`*x`Gz@p@@pDwS-<%N`Go``z@p@@vDwC->%N`o`d{@pAApDwC-%N`pdx @pXXyDwS-%N` xW`:x`@p@@@uDwS-<%N`x``_@p@@yDwC->%N`_x`di@pAA@uDwC-%N`iydx@pXX@uDwS-%N`W`Jx`΋@p@@yDwS-<%N`΀`` @p@@@uDwC->%N` `d3@pAAyDwC-%N`3dxY@pXXpDwS-%N`YW`Zx`i@p@@vDwS-<%N`i``@p@@pDwC->%N``d@pAAvDwC-%N`dx@pXX@~DwS-%N`W`jx`@p@@zDwS-<%N```I@p@@@~DwC->%N`I`df@pAAzDwC-%N`fdx@pXXvDwS-%N`W`{x`@p@@}DwS-<%N```ݥ@p@@vDwC->%N`ݚ`d@pAA}DwC-%N`dx@@pXXwDwS-%N`@W`x`X@p@@@rDwS-<%N`X``@p@@wDwC->%N``d@pAA@rDwC-%N`dx@pXXDwS-%N`W` x`@p@@@{DwS-<%N```@p@@DwC->%N``d@pAA@{DwC-%N`dxX@pXX@{DwS-%N`XW`0x`g@p@@DwS-<%N`g``*@p@@@{DwC->%N`*`d#@pAADwC-%N`#dx"@pXX@rDwS-%N`"W`@x`5@p@@wDwS-<%N`5``o@p@@@rDwC->%N`o`d@pAAwDwC-%N`dx@pXXzS-%N`W`Px`%@p@@zS-<%N`%``F@p@@zC->%N`F`dd@pAAzC-%N`ddx@pXX}DwS-%N`W``x`"@p@@vDwS-<%N`"``h@p@@}DwC->%N`h`d@pAAvDwC-%N`dx@pXXzDwS-%N`W`px`@p@@@~DwS-<%N```$@p@@zDwC->%N`$`d@pAA@~DwC-%N`dxS@pXXvDwS-%N`SW`x`d@p@@pDwS-<%N`d``@p@@vDwC->%N``d@pAApDwC-%N`dxApXXyDwS-%N`W` x`)Ap@@@uDwS-<%N`)``oAp@@yDwC->%N`o`duApAA@uDwC-%N`udx ApXX@uDwS-%N`W`x` Ap@@yDwS-<%N```F Ap@@@uDwC->%N`F`dG ApAAyDwC-%N`GdxApXXpDwS-%N`W`+x`Ap@@vDwS-<%N```$Ap@@pDwC->%N`$`d/ApAAvDwC-%N`/ dxqApXX@~DwS-%N`qW`;x`Ap@@zDwS-<%N```Ap@@@~DwC->%N``dApAAzDwC-%N`dx3$ApXXvDwS-%N`3W`Kx`C$Ap@@}DwS-<%N`C``$Ap@@vDwC->%N``d%ApAA}DwC-%N`dx-ApXXwDwS-%N`"W`[x`-Ap@@@rDwS-<%N`"``W-Ap@@wDwC->%N`W"`dq.ApAA@rDwC-%N`q#dx5ApXXDwS-%N`*W`kx`5Ap@@@{DwS-<%N`*``J6Ap@@DwC->%N`J+`d<7ApAA@{DwC-%N`<,dx>ApXX@{DwS-%N`3W`|x`>Ap@@DwS-<%N`3``>Ap@@@{DwC->%N`3`d?ApAADwC-%N`4dxGGApXXzS-%N`G<W`x`YGAp@@zS-<%N`Y<``zGAp@@zC->%N`z<`dHApAAzC-%N`=dx PApXXzS-%N` EW` x`PAp@@zS-<%N`E``:PAp@@zC->%N`:E`dQApAAzC-%N`FdxXApXXzS-%N`MW`0x`XAp@@@zS-<%N`M`` YAp@@zC->%N` N`dJZApAA@zC-%N`JOdxMaApXX@rDwS-%N`MVW`@x`[aAp@@wDwS-<%N`[V``aAp@@@rDwC->%N`V`dbApAAwDwC-%N`Wdx jApXX}DwS-%N` _W`Px`jAp@@vDwS-<%N`_``ZjAp@@}DwC->%N`Z_`dXkApAAvDwC-%N`X`dxrApXXzDwS-%N`gW``x`rAp@@@~DwS-<%N`g``rAp@@zDwC->%N`g`dsApAA@~DwC-%N`hdx0{ApXXvDwS-%N`0pW`px`={Ap@@pDwS-<%N`=p``{Ap@@vDwC->%N`p`d|ApAApDwC-%N`qdxApXXyDwS-%N`xW`x`Ap@@@uDwS-<%N`x``gAp@@yDwC->%N`gy`doApAA@uDwC-%N`ozdxApXX@uDwS-%N`W` x`Ap@@yDwS-<%N```Ap@@@uDwC->%N``dApAAyDwC-%N`dxjApXXpDwS-%N`jW`x`wAp@@vDwS-<%N`w``Ap@@pDwC->%N``dApAAvDwC-%N`dxApXX@~DwS-%N`W`,x` Ap@@zDwS-<%N` ``Ap@@@~DwC->%N``dApAAzDwC-%N`dxApXXvDwS-%N`W`<x`Ap@@}DwS-<%N```IAp@@vDwC->%N`I`dMApAA}DwC-%N`MdxcApXXwDwS-%N`cW`Lx`pAp@@@rDwS-<%N`p``Ap@@wDwC->%N``dĽApAA@rDwC-%N`IJdxApXXDwS-%N`W`\x`Ap@@@{DwS-<%N```AAp@@DwC->%N`A`d@ApAA@{DwC-%N`@dxApXX@{DwS-%N`W`lx`Ap@@DwS-<%N```Ap@@@{DwC->%N``dBApAADwC-%N`BdxqApXX@rDwS-%N`qW`}x`Ap@@wDwS-<%N```Ap@@@rDwC->%N``dApAAwDwC-%N`dx1ApXX}DwS-%N`1W`x`=Ap@@vDwS-<%N`=``Ap@@}DwC->%N``d}ApAAvDwC-%N`}dxApXXzS-%N`W` x`Ap@@zS-<%N```+Ap@@zC->%N`+`djApAAzC-%N`jdxApXXzS-%N`W`0x`Ap@@zS-<%N```Ap@@zC->%N``d7ApAAzC-%N`7dxyApXXzS-%N`yW`@x`Ap@@zS-<%N```Ap@@zC->%N``dApAAzC-%N`dx BpXXzS-%N` W`Px`,Bp@@@zS-<%N`,``vBp@@zC->%N`v`dxBpAA@zC-%N`xdx BpXXzS-%N`W``x` Bp@@@zS-<%N` ``v Bp@@zC->%N`v`dT BpAA@zC-%N`TdxBpXXzS-%N`W`px`Bp@@zS-<%N```1Bp@@zC->%N`1 `d/BpAAzC-%N`/ dxTBpXXzS-%N`TW`x`_Bp@@zS-<%N`_``Bp@@zC->%N``dBpAAzC-%N`dx=%BpXXzDwS-%N`=W` x`I%Bp@@@~DwS-<%N`I``%Bp@@zDwC->%N``d&BpAA@~DwC-%N`dx-BpXXvDwS-%N`"W`x`-Bp@@pDwS-<%N`"``L.Bp@@vDwC->%N`L#`d>/BpAApDwC-%N`>$dx6BpXXyDwS-%N`+W`-x`6Bp@@@uDwS-<%N`+`` 7Bp@@yDwC->%N` ,`d 8BpAA@uDwC-%N` -dxn?BpXX@uDwS-%N`n4W`=x`|?Bp@@yDwS-<%N`|4``?Bp@@@uDwC->%N`4`d@BpAAyDwC-%N`5dxGBpXXpDwS-%N`<W`Mx`HBp@@vDwS-<%N`=``GHBp@@pDwC->%N`G=`dHIBpAAvDwC-%N`H>d`PBp@@@~DwS-<%N`E`xTBpXXzDwS-%N`IW`]x`TBp@@zDwC->%N`I`dVBpAA@~DwC-%N`KdxE]BpXXvDwS-%N`ERW`mx`T]Bp@@}DwS-<%N`TR``]Bp@@vDwC->%N`R`d^BpAA}DwC-%N`SdxfBpXXwDwS-%N`[W`~x`fBp@@@rDwS-<%N`[``OfBp@@wDwC->%N`O[`dlgBpAA@rDwC-%N`l\dxnBpXXDwS-%N`cW`x`nBp@@@{DwS-<%N`c``nBp@@DwC->%N`c`doBpAA@{DwC-%N`ddxvBpXX@{DwS-%N`kW` x`wBp@@DwS-<%N`l``wBp@@@{DwC->%N`l`doxBpAADwC-%N`omdxhBpXX@rDwS-%N`htW`0x`{Bp@@wDwS-<%N`{t``Bp@@@rDwC->%N`t`dԀBpAAwDwC-%N`udxBpXX}DwS-%N`}W`@x`Bp@@vDwS-<%N`}``ȈBp@@}DwC->%N`}`dBpAAvDwC-%N`dxTBpXX@~DwS-%N`TW`Px``Bp@@zDwS-<%N````Bp@@@~DwC->%N``dɒBpAAzDwC-%N`ɇdxޙBpXXvDwS-%N`ގW``x`Bp@@pDwS-<%N```Bp@@vDwC->%N``d@BpAApDwC-%N`@dxBpXXyDwS-%N`W`px`Bp@@@uDwS-<%N```ݢBp@@yDwC->%N`ݗ`dBpAA@uDwC-%N`dxdBpXX@uDwS-%N`dW`x`uBp@@yDwS-<%N`u``Bp@@@uDwC->%N``dBpAAyDwC-%N`dxBpXXpDwS-%N`W`x` Bp@@vDwS-<%N` ``7Bp@@pDwC->%N`7`djBpAAvDwC-%N`jdxBpXXzDwS-%N`W`x`ļBp@@@~DwS-<%N`ı``Bp@@zDwC->%N``dBpAA@~DwC-%N`dxwBpXXvDwS-%N`wW`.x`Bp@@}DwS-<%N```Bp@@vDwC->%N``dBpAA}DwC-%N`ػdx;BpXXwDwS-%N`;W`>x`HBp@@@rDwS-<%N`H``rBp@@wDwC->%N`r`dBpAA@rDwC-%N`dxBpXXDwS-%N`W`Nx`Bp@@@{DwS-<%N```9Bp@@DwC->%N`9`d~BpAA@{DwC-%N`~dxBpXX@{DwS-%N`W`^x`Bp@@DwS-<%N```Bp@@@{DwC->%N``d,BpAADwC-%N`,dxmBpXXzS-%N`mW`nx`|Bp@@zS-<%N`|``Bp@@zC->%N``dBpAAzC-%N`dx/BpXXzS-%N`/W`x`%N``dBpAA@zC-%N`dxBpXXzS-%N`W`x`Bp@@zS-<%N```%Bp@@zC->%N`%`d\BpAAzC-%N`\dxCpXXzS-%N`W` x`Cp@@zS-<%N```<Cp@@zC->%N`<`dECpAAzC-%N`EdxCpXXzS-%N` W`0x`Cp@@zS-<%N` ``-Cp@@zC->%N`-`d7CpAAzC-%N`7dxk!CpXXzS-%N`kW`@x`~!Cp@@zS-<%N`~``!Cp@@zC->%N``d"CpAAzC-%N`dxB*CpXXzS-%N`BW`Px`O*Cp@@@zS-<%N`O``*Cp@@zC->%N``d+CpAA@zC-%N` dx3CpXXzS-%N`(W``x`3Cp@@zS-<%N`(``g3Cp@@zC->%N`g(`dk4CpAAzC-%N`k)dx;CpXXzS-%N`0W`px`;Cp@@zS-<%N`0``%%N`%1`d)=CpAAzC-%N`)2dxDCpXXzS-%N`9W`x`DCp@@zS-<%N`9``DCp@@zC->%N`9`dFCpAAzC-%N`;dxMCpXXzS-%N`BW`x`(MCp@@@zS-<%N`(B``nMCp@@zC->%N`nB`dsNCpAA@zC-%N`sCdxUCpXXzS-%N`JW`x`UCp@@@zS-<%N`J``UCp@@zC->%N`J`dVCpAA@zC-%N`Kdxl^CpXXzS-%N`lSW`/x`y^Cp@@zS-<%N`yS``^Cp@@zC->%N`S`d_CpAAzC-%N`Tdx,gCpXXzS-%N`,\W`?x`;gCp@@zS-<%N`;\``_gCp@@zC->%N`_\`dhCpAAzC-%N`]dxoCpXXzS-%N`dW`Ox`oCp@@zS-<%N`d``pCp@@zC->%N`e`dRqCpAAzC-%N`RfdxyxCpXXzS-%N`ymW`_x`xCp@@@zS-<%N`m``xCp@@zC->%N`m`dyCpAA@zC-%N`ndx3CpXXzS-%N`3vW`ox`@Cp@@zS-<%N`@v``Cp@@zC->%N`v`dzCpAAzC-%N`zwdxCpXXzS-%N`W`x` Cp@@zS-<%N` ``uCp@@zC->%N`u`ddCpAAzC-%N`ddxCpXXzS-%N`W`x`Cp@@zS-<%N```!Cp@@zC->%N`!`dICpAAzC-%N`IdxfCpXXzS-%N`fW` x`sCp@@zS-<%N`s``ǛCp@@zC->%N`ǐ`dƜCpAAzC-%N`Ƒdx+CpXXzS-%N`+W`0x`7Cp@@@zS-<%N`7``Cp@@zC->%N``dCpAA@zC-%N`dxCpXXzS-%N`W`@x`Cp@@zS-<%N```QCp@@zC->%N`Q`dNCpAAzC-%N`NdxCpXXzS-%N`W`Px`ƵCp@@zS-<%N`ƪ``Cp@@zC->%N``d&CpAAzC-%N`&dxuCpXXzS-%N`uW``x`Cp@@zS-<%N```Cp@@zC->%N``d߿CpAAzC-%N`ߴdxCpXXzS-%N`W`px` Cp@@@zS-<%N` ```Cp@@zC->%N```dDCpAA@zC-%N`DdxCpXXzS-%N`W`x`Cp@@@zS-<%N```Cp@@zC->%N``dCpAA@zC-%N`dxCpXXzS-%N`W`x`Cp@@zS-<%N```+Cp@@zC->%N`+`dOCpAAzC-%N`OdxCpXXzS-%N`W` x`Cp@@zS-<%N```Cp@@zC->%N``dCpAAzC-%N`dxCpXXzS-%N`W`0x`+Cp@@zS-<%N`+``{Cp@@zC->%N`{`dCpAAzC-%N`dxCpXXzS-%N`W`@x`Cp@@@zS-<%N```3Cp@@zC->%N`3`d5CpAA@zC-%N`5dxCpXXzS-%N`W`Px`Cp@@zS-<%N```Cp@@zC->%N``d/CpAAzC-%N`/dxJDpXXzS-%N`JW``x`UDp@@zS-<%N`U``Dp@@zC->%N``dDpAAzC-%N`dx DpXXzS-%N`W`px` Dp@@zS-<%N` ``. Dp@@zC->%N`.`dIDpAAzC-%N`IdxDpXXzS-%N` W`x`Dp@@zS-<%N` ``$Dp@@zC->%N`$ `d DpAAzC-%N` dxzDpXXzS-%N`zW`x`Dp@@@zS-<%N```Dp@@zC->%N``dDpAA@zC-%N`dx8'DpXXzS-%N`8W` x`D'Dp@@zS-<%N`D``'Dp@@zC->%N``d(DpAAzC-%N`dx/DpXXzS-%N`$W`0x`/Dp@@zS-<%N`$``"0Dp@@zC->%N`"%`d1DpAAzC-%N`&dx8DpXXzS-%N`-W`@x`8Dp@@zS-<%N`-``8Dp@@zC->%N`-`d9DpAAzC-%N`.dxODpXXzS-%N`DW`Px`ODp@@@zS-<%N`D``hODp@@zC->%N`hD`dkPDpAA@zC-%N`kEdxWDpXXzS-%N`LW``x`WDp@@@zS-<%N`L``XDp@@zC->%N`M`d#YDpAA@zC-%N`#NdxQ`DpXXzS-%N`QUW`px`\`Dp@@zS-<%N`\U```Dp@@zC->%N`U`daDpAAzC-%N`VdxiDpXXzS-%N`^W`x` iDp@@zS-<%N` ^``^iDp@@zC->%N`^^`djDpAAzC-%N`_dxqDpXXzS-%N`fW`x`qDp@@zS-<%N`f``rDp@@zC->%N`g`d sDpAAzC-%N` hdxszDpXXzS-%N`soW`!x`zDp@@@zS-<%N`o``zDp@@zC->%N`o`d{DpAA@zC-%N`pdx-DpXXzS-%N`-xW`1x`7Dp@@zS-<%N`7x``XDp@@zC->%N`Xx`d=DpAAzC-%N`=yd`ދDp@@zS-<%N`ހ`x%N``dDpAAzC-%N`dxDpXXzS-%N`W`Qx`Dp@@zS-<%N```|Dp@@zC->%N`|`dDpAAzC-%N`dxDpXXzS-%N`W`ax`Dp@@zS-<%N```Dp@@zC->%N``dТDpAAzC-%N`ЗdxDpXXzS-%N`W`qx`#Dp@@@zS-<%N`#``Dp@@zC->%N``dDpAA@zC-%N`ջdxDpXXzS-%N`W` x`Dp@@zS-<%N```@Dp@@zC->%N`@`dMDpAAzC-%N`Mdx}DpXXzS-%N`}W` x`Dp@@zS-<%N```Dp@@zC->%N``dDpAAzC-%N`dx?DpXXzS-%N`?W` x`TDp@@zS-<%N`T``qDp@@zC->%N`q`dDpAAzC-%N`dxDpXXzS-%N`W` 0x`Dp@@@zS-<%N```*Dp@@zC->%N`*`dDpAA@zC-%N`dxDpXXzS-%N`W` @x`Dp@@@zS-<%N```Dp@@zC->%N``dDpAA@zC-%N`dx}DpXXzS-%N`}W` Px`Dp@@zS-<%N```Dp@@zC->%N``dDpAAzC-%N`dxAEpXXzS-%N`AW` `x`REp@@zS-<%N`R``Ep@@zC->%N``dEpAAzC-%N`dx EpXXzS-%N`W` px`* Ep@@zS-<%N`*``L Ep@@zC->%N`L`d EpAAzC-%N`dxEpXXzS-%N` W` x`Ep@@@zS-<%N` ``;Ep@@zC->%N`; `d7EpAA@zC-%N`7 dxjEpXXzS-%N`j W` x`wEp@@zS-<%N`w ``Ep@@zC->%N` `dEpAAzC-%N` dx4#EpXXzS-%N`4 W` "x`B#Ep@@zS-<%N`B ``k#Ep@@zC->%N`k `d$EpAAzC-%N` dx+EpXXzS-%N` W` 2x`+Ep@@zS-<%N` `` ,Ep@@zC->%N` ! `d,EpAAzC-%N`! dx34EpXXzS-%N`3) W` Bx`B4Ep@@zS-<%N`B) ``4Ep@@zC->%N`) `d}5EpAAzC-%N`}* dx~%N`1 `d=EpAA'zC-%N`2 dxDEpXX@'zS-%N`9 W` bx`DEp@@'zS-<%N`9 ``DEp@@@'zC->%N`9 `dEEpAA'zC-%N`: dx$MEpXX'zS-%N`$B W` rx`3MEp@@'zS-<%N`3B ``oMEp@@'zC->%N`oB `dkNEpAA'zC-%N`kC dxUEpXX@'zS-%N`J W`!x`UEp@@'zS-<%N`J ``%VEp@@@'zC->%N`%K `dWEpAA'zC-%N`L dx^EpXX'zS-%N`S W`!x`^Ep@@'zS-<%N`S ``^Ep@@'zC->%N`S `d_EpAA'zC-%N`T dx[gEpXX'zS-%N`[\ W`! x`igEp@@'zS-<%N`i\ ``gEp@@'zC->%N`\ `dhEpAA'zC-%N`] dx"pEpXX'zS-%N`"e W`!0x`0pEp@@@'zS-<%N`0e ``spEp@@'zC->%N`se `dqEpAA@'zC-%N`f dxxEpXX'zS-%N`m W`!@x`xEp@@@'zS-<%N`m ``'yEp@@'zC->%N`'n `d9zEpAA@'zC-%N`9o dxƎEpXX'zS-%N`ƃ W`!Px`ԎEp@@'zS-<%N`ԃ ``Ep@@'zC->%N` `dMEpAA'zC-%N`M dxWEpXX'zS-%N`W W`!`x`eEp@@'zS-<%N`e ``Ep@@'zC->%N` `dØEpAA'zC-%N`Í dxEpXX'zS-%N` W`!px`&Ep@@'zS-<%N`& ``iEp@@'zC->%N`i `dfEpAA'zC-%N`f dxEpXX@'zS-%N` W`!x`Ep@@'zS-<%N` ``3Ep@@@'zC->%N`3 `dJEpAA'zC-%N`J dxEpXX'zS-%N` W`!x`Ep@@'zS-<%N` ``Ep@@'zC->%N` `dEpAA'zC-%N` dxEpXX@'zS-%N` W`!#x`Ep@@'zS-<%N` ``ٺEp@@@'zC->%N`ٯ `dEpAA'zC-%N` dxKEpXX'zS-%N`K W`!3x`]Ep@@'zS-<%N`] ``Ep@@'zC->%N` `dEpAA'zC-%N` dxEpXX'zS-%N` W`!Cx`Ep@@'zS-<%N` ``XEp@@'zC->%N`X `dmEpAA'zC-%N`m dxEpXX'zS-%N` W`!Sx`Ep@@@'zS-<%N` ``Ep@@'zC->%N` `dQEpAA@'zC-%N`Q dxEpXX'zS-%N` W`!cx`Ep@@@'zS-<%N` ``Ep@@'zC->%N` `dEpAA@'zC-%N` dxMEpXX'zS-%N`M W`!sx`ZEp@@'zS-<%N`Z ``Ep@@'zC->%N` `dEpAA'zC-%N` dx EpXX'zS-%N` W`"x`Ep@@'zS-<%N` ``]Ep@@'zC->%N`] `dlEpAA'zC-%N`l dxEpXXzS-%N` W`"x`Ep@@@zS-<%N` `` Ep@@zC->%N` `d(EpAA@zC-%N`( dxrFpXXzS-%N`r W`" x`Fp@@zS-<%N` ``Fp@@zC->%N` `dFpAAzC-%N` dxFpXXzS-%N` W`"0x` Fp@@zS-<%N` ``+ Fp@@zC->%N`+ `dL FpAAzC-%N`L dxFpXXzS-%N` W`"@x`Fp@@zS-<%N` ``Fp@@zC->%N` `dFpAAzC-%N` dxJFpXXzS-%N`J W`"Px`[Fp@@@zS-<%N`[ ``Fp@@zC->%N` `dFpAA@zC-%N` dx#FpXXzS-%N` W`"`x`)#Fp@@@zS-<%N`) ``M#Fp@@zC->%N`M `d$FpAA@zC-%N` dx+FpXXzS-%N` W`"px`+Fp@@zS-<%N` ``+Fp@@zC->%N` `d-FpAAzC-%N`" dxw4FpXXzS-%N`w) W`"x`4Fp@@zS-<%N`) ``4Fp@@zC->%N`) `d5FpAAzC-%N`* dx4=FpXXzS-%N`42 W`"x`C=Fp@@zS-<%N`C2 ``l=Fp@@zC->%N`l2 `dw>FpAAzC-%N`w3 dxEFpXXzS-%N`: W`"$x`EFp@@@zS-<%N`: ``&FFp@@zC->%N`&; `dGFpAA@zC-%N`< dxNFpXXzS-%N`C W`"4x`NFp@@zS-<%N`C ``NFp@@zC->%N`C `dOFpAAzC-%N`D dxWFpXXzS-%N`L W`"Dx`+WFp@@zS-<%N`+L ``VWFp@@zC->%N`VL `dwXFpAAzC-%N`wM dx_FpXXzS-%N`T W`"Tx``Fp@@zS-<%N`U ``<`Fp@@zC->%N`%N`] `dEjFpAAzC-%N`E_ dxqFpXXzS-%N`f W`"tx`qFp@@@zS-<%N`f ``qFp@@zC->%N`f `drFpAA@zC-%N`g dxyFpXXzS-%N`n W`#x`yFp@@zS-<%N`n ``yFp@@zC->%N`n `dzFpAAzC-%N`o dx~FpXXzS-%N`~w W`#x`Fp@@zS-<%N`w ``Fp@@zC->%N`w `dFpAAzC-%N`x dxEFpXXzS-%N`E W`# x`SFp@@zS-<%N`S ``|Fp@@zC->%N`| `dFpAAzC-%N` dxFpXXzS-%N` W`#0x`Fp@@@zS-<%N` ``AFp@@zC->%N`A `dqFpAA@zC-%N`q dxԜFpXXzS-%N`ԑ W`#@x`Fp@@@zS-<%N` `` Fp@@zC->%N` `d%N` `dƦFpAAzC-%N`ƛ dx6FpXXzS-%N`6 W`#`x`HFp@@zS-<%N`H ``tFp@@zC->%N`t `dyFpAAzC-%N`y dxFpXXzS-%N` W`#px`Fp@@zS-<%N` ``Fp@@zC->%N` `dWFpAAzC-%N`W dxFpXXzS-%N` W`#x`Fp@@@zS-<%N` ``տFp@@zC->%N`մ `dFpAA@zC-%N` dxWFpXXzS-%N`W W`#x`eFp@@zS-<%N`e ``Fp@@zC->%N` `dFpAAzC-%N` d`Fp@@zS-<%N` `xfFpXXzS-%N`f W`#%x`Fp@@zC->%N` `dFpAAzC-%N` dxFpXXzS-%N` W`#5x`"Fp@@zS-<%N`" ``CFp@@zC->%N`C `dFFpAAzC-%N`F dxFpXXzS-%N` W`#Ex`Fp@@zS-<%N` ``Fp@@zC->%N` `d FpAAzC-%N` dx.FpXXzS-%N`. W`#Ux`=Fp@@@zS-<%N`= ``eFp@@zC->%N`e `dFpAA@zC-%N` dxFpXXzS-%N` W`#ex`Fp@@zS-<%N` ``@Fp@@zC->%N`@ `dEFpAAzC-%N`E dxgGpXXzS-%N`g W`#ux`vGp@@zS-<%N`v ``Gp@@zC->%N` `dGpAAzC-%N` dx& GpXXzS-%N`& W`$x`5 Gp@@zS-<%N`5 ``Z Gp@@zC->%N`Z `dn GpAAzC-%N`n dxGpXXzS-%N` W`$x`Gp@@@zS-<%N` ``,Gp@@zC->%N`, `diGpAA@zC-%N`i dxGpXXzS-%N` W`$ x`Gp@@@zS-<%N` ``Gp@@zC->%N` `d,GpAA@zC-%N`, dxy#GpXX'zS-%N`y W`$0x`#Gp@@'zS-<%N` ``#Gp@@'zC->%N` `d$GpAA'zC-%N` dxF,GpXX@'zS-%N`F! W`$@x`W,Gp@@'zS-<%N`W! ``,Gp@@@'zC->%N`! `d-GpAA'zC-%N`" dx+AGpXX'zS-%N`+6 W`$Px`:AGp@@'zS-<%N`:6 ``}AGp@@'zC->%N`}6 `dBGpAA'zC-%N`7 dxIGpXX@'zS-%N`> W`$`x`IGp@@'zS-<%N`> ``!JGp@@@'zC->%N`!? `dVKGpAA'zC-%N`V@ dxRGpXX'zS-%N`G W`$px`RGp@@@'zS-<%N`G `` SGp@@'zC->%N` H `dTGpAA@'zC-%N`I dx[GpXX@'zS-%N`P W`$x`[Gp@@'zS-<%N`P ``[Gp@@@'zC->%N`P `d\GpAA'zC-%N`Q dxEdGpXX'zS-%N`EY W`$x`SdGp@@@'zS-<%N`SY ``dGp@@'zC->%N`Y `deGpAA@'zC-%N`Z dxlGpXX@'zS-%N`a W`$&x` mGp@@'zS-<%N` b ``DmGp@@@'zC->%N`Db `d0nGpAA'zC-%N`0c dxuGpXX'zS-%N`j W`$6x`uGp@@@'zS-<%N`j ``5vGp@@'zC->%N`5k `d%N`t `d?GpAA'zC-%N`?u dx{GpXX'zS-%N`{| W`$Vx`Gp@@@'zS-<%N`| ``χGp@@'zC->%N`| `dވGpAA@'zC-%N`} dx GpXX@'zS-%N` W`$fx`Gp@@'zS-<%N` ``_Gp@@@'zC->%N`_ `diGpAA'zC-%N`i dxјGpXX'zS-%N`э W`$vx`ޘGp@@@'zS-<%N`ލ ``"Gp@@'zC->%N`" `dGpAA@'zC-%N` dx0GpXX@'zS-%N`0 W`%x`%N` `dŢGpAA'zC-%N`ŗ dxGpXX'zS-%N` W`%x`Gp@@@'zS-<%N` ``Gp@@'zC->%N` `d[GpAA@'zC-%N`[ dxGpXX'zS-%N` W`% x`Gp@@'zS-<%N` ``tGp@@'zC->%N`t `d|GpAA'zC-%N`| dxGpXX'zS-%N` W`%0x`Gp@@'zS-<%N` ``Gp@@'zC->%N` `dGpAA'zC-%N` dxSGpXX'zS-%N`S W`%@x``Gp@@@'zS-<%N`` ``Gp@@'zC->%N` `dGpAA@'zC-%N` dxGpXX'zS-%N` W`%Px`%Gp@@@'zS-<%N`% ``hGp@@'zC->%N`h `dGpAA@'zC-%N` dxGpXX'zS-%N` W`%`x`Gp@@'zS-<%N` ``Gp@@'zC->%N` `d#GpAA'zC-%N`# dxGGpXX'zS-%N`G W`%px`UGp@@'zS-<%N`U ``Gp@@'zC->%N` `dGpAA'zC-%N` dx GpXX'zS-%N` W`%x`Gp@@@'zS-<%N` ``8Gp@@'zC->%N`8 `d>GpAA@'zC-%N`> dxGpXX'zS-%N` W`%x`Gp@@'zS-<%N` ``Gp@@'zC->%N` `dGpAA'zC-%N` dx+GpXX'zS-%N`+ W`%'x`8Gp@@'zS-<%N`8 ``zGp@@'zC->%N`z `dGpAA'zC-%N` dxGpXX'zS-%N` W`%7x`Gp@@@'zS-<%N` ``;Hp@@'zC->%N`; `dRHpAA@'zC-%N`R dxxHpXX'zS-%N`x W`%Gx`Hp@@'zS-<%N` ``Hp@@'zC->%N` `d HpAA'zC-%N` dx2HpXX'zS-%N`2 W`%Wx`?Hp@@'zS-<%N`? ``Hp@@'zC->%N` `dHpAA'zC-%N` dxHpXX'zS-%N` W`%gx`Hp@@@'zS-<%N` ``>Hp@@'zC->%N`> `dXHpAA@'zC-%N`X dx"HpXX'zS-%N` W`%wx`"Hp@@@'zS-<%N` ``"Hp@@'zC->%N` `d/$HpAA@'zC-%N`/ dx+HpXX'zS-%N` W`&x`+Hp@@'zS-<%N` ``+Hp@@'zC->%N` `d,HpAA'zC-%N`! dxD4HpXX'zS-%N`D) W`&x`Q4Hp@@'zS-<%N`Q) ``4Hp@@'zC->%N`) `d5HpAA'zC-%N`* dx%N`M2 `d]>HpAA@'zC-%N`]3 dxEHpXX'zS-%N`: W`&0x`EHp@@'zS-<%N`: ``FHp@@'zC->%N`; `d9GHpAA'zC-%N`9< dxNHpXX'zS-%N`C W`&@x`NHp@@'zS-<%N`C ``NHp@@'zC->%N`C `dOHpAA'zC-%N`D dxCWHpXX'zS-%N`CL W`&Px`PWHp@@@'zS-<%N`PL ``WHp@@'zC->%N`L `dXHpAA@'zC-%N`M dx`HpXX'zS-%N`U W`&`x``Hp@@'zS-<%N`U ``X`Hp@@'zC->%N`XU `dmaHpAA'zC-%N`mV dxhHpXX'zS-%N`] W`&px`hHp@@'zS-<%N`] ``iHp@@'zC->%N`^ `d/jHpAA'zC-%N`/_ dxqHpXX'zS-%N`f W`&x`qHp@@@'zS-<%N`f ``qHp@@'zC->%N`f `drHpAA@'zC-%N`g dxzHpXX'zS-%N`o W`&x`"zHp@@@'zS-<%N`"o ``hzHp@@'zC->%N`ho `d|{HpAA@'zC-%N`|p dxHpXX'zS-%N`w W`&(x`Hp@@'zS-<%N`w ``7Hp@@'zC->%N`7x `d.HpAA'zC-%N`.y dxjHpXX'zS-%N`j W`&8x`wHp@@'zS-<%N`w ``Hp@@'zC->%N` `d׌HpAA'zC-%N`ׁ dx+HpXX'zS-%N`+ W`&Hx`:Hp@@@'zS-<%N`: ``[Hp@@'zC->%N`[ `dHpAA@'zC-%N` dxHpXX'zS-%N` W`&Xx`Hp@@'zS-<%N` ``*Hp@@'zC->%N`* `d,HpAA'zC-%N`, dxHpXX'zS-%N` W`&hx`Hp@@'zS-<%N` ``Hp@@'zC->%N` `dHpAA'zC-%N` dxHpXX'zS-%N` W`&xx`Hp@@@'zS-<%N` ``kHp@@'zC->%N`k `dfHpAA@'zC-%N`f dxHpXX'zS-%N` W`'x`Hp@@'zS-<%N` ``Hp@@'zC->%N` `dHpAA'zC-%N` dx\HpXX'zS-%N`\ W`'x`gHp@@'zS-<%N`g ``Hp@@'zC->%N` `dHpAA'zC-%N` dxHpXX'zS-%N` W`' x`Hp@@@'zS-<%N` ``)Hp@@'zC->%N`) `d1HpAA@'zC-%N`1 dxHpXX'zS-%N` W`'0x`Hp@@@'zS-<%N` ``Hp@@'zC->%N` `dHpAA@'zC-%N` dxpHpXX'zS-%N`p W`'@x`Hp@@'zS-<%N` ``Hp@@'zC->%N` `dHpAA'zC-%N` dxHpXX'zS-%N` W`'Px`Hp@@'zS-<%N` ``GHp@@'zC->%N`G `dbHpAA'zC-%N`b dxHpXX'zS-%N` W`'`x`Hp@@@'zS-<%N` ``Hp@@'zC->%N` `d HpAA@'zC-%N` d`&Hp@@'zS-<%N`& `xoHpXX'zS-%N`o W`'px`Hp@@'zC->%N` `dHpAA'zC-%N` dx&IpXX'zS-%N`& W`' x`2Ip@@'zS-<%N`2 ``tIp@@'zC->%N`t `dlIpAA'zC-%N`l dxIpXX'zS-%N` W`'x`Ip@@@'zS-<%N` ``2 Ip@@'zC->%N`2 `do IpAA@'zC-%N`o dx{IpXX'zS-%N`{ W`')x`Ip@@'zS-<%N` ``Ip@@'zC->%N` `dIpAA'zC-%N` dx:IpXXzS-%N`: W`'9x`KIp@@zS-<%N`K ``nIp@@zC->%N`n `dIpAAzC-%N` dx"IpXXzS-%N` W`'Ix`"Ip@@zS-<%N` ``7#Ip@@zC->%N`7 `dV$IpAAzC-%N`V dx+IpXXzS-%N` W`'Yx`+Ip@@zS-<%N` `` ,Ip@@zC->%N` ! `dL-IpAAzC-%N`L" dxE4IpXXzS-%N`E) W`'ix`S4Ip@@@zS-<%N`S) ``|4Ip@@zC->%N`|) `d5IpAA@zC-%N`* dx =IpXXzS-%N` 2 W`'yx`=Ip@@zS-<%N`2 ``B=Ip@@zC->%N`B2 `dYIpAAzC-%N`N dx?_IpXXzS-%N`?T W`(x`R_Ip@@zS-<%N`RT ``~_Ip@@zC->%N`~T `d`IpAAzC-%N`U dx\gIpXXzS-%N`\\ W`(x`ogIp@@zS-<%N`o\ ``gIp@@zC->%N`\ `dhIpAAzC-%N`] dxoIpXXzS-%N`d W`( x`pIp@@zS-<%N`e ``,pIp@@zC->%N`,e `dEqIpAAzC-%N`Ef dxUxIpXXzS-%N`Um W`(0x`dxIp@@@zS-<%N`dm ``xIp@@zC->%N`m `dyIpAA@zC-%N`n dx$IpXXzS-%N`$v W`(@x`1Ip@@zS-<%N`1v ``ZIp@@zC->%N`Zv `dIpAAzC-%N`w dxIpXXzS-%N`~ W`(Px`Ip@@zS-<%N`~ ``!Ip@@zC->%N`! `dZIpAAzC-%N`Z dxIpXXzS-%N` W`(`x`Ip@@zS-<%N` ``ҒIp@@zC->%N`҇ `dIpAAzC-%N` dx!IpXXzS-%N`! W`(px`1Ip@@@zS-<%N`1 ``Ip@@zC->%N` `dIpAA@zC-%N` dxIpXXzS-%N` W`( x`Ip@@@zS-<%N` ``Ip@@zC->%N` `dIpAA@zC-%N` dxyIpXXzS-%N`y W`(x`Ip@@zS-<%N` ``Ip@@zC->%N` `dҭIpAAzC-%N`Ң dxGIpXXzS-%N`G W`(*x`UIp@@zS-<%N`U ``}Ip@@zC->%N`} `dIpAAzC-%N` dxӽIpXXzS-%N`Ӳ W`(:x`Ip@@zS-<%N` `` Ip@@zC->%N` `d;IpAAzC-%N`; dxIpXXzS-%N` W`(Jx`Ip@@@zS-<%N` ``Ip@@zC->%N` `dIpAA@zC-%N` dxbIpXXzS-%N`b W`(Zx`pIp@@zS-<%N`p ``Ip@@zC->%N` `dIpAAzC-%N` dxIpXXzS-%N` W`(jx`!Ip@@zS-<%N`! ``wIp@@zC->%N`w `d~IpAAzC-%N`~ dxBIpXXzS-%N`B W`(zx`PIp@@zS-<%N`P ``Ip@@zC->%N` `dIpAAzC-%N` dxIpXXzS-%N` W`)x`Ip@@zS-<%N` ``1Ip@@zC->%N`1 `dsIpAAzC-%N`s dxIpXXzS-%N` W`)x`Ip@@@zS-<%N` ``"Ip@@zC->%N`" `dIpAA@zC-%N` dxIpXXzS-%N` W`) x`Ip@@zS-<%N` ``Ip@@zC->%N` `dIpAAzC-%N` dxBJpXXzS-%N`B W`)0x`OJp@@zS-<%N`O ``Jp@@zC->%N` `dJpAAzC-%N` dx JpXXzS-%N`W`)@x` Jp@@zS-<%N```7 Jp@@zC->%N`7`d JpAAzC-%N`dxJpXXzS-%N` W`)Px`Jp@@@zS-<%N` ``Jp@@zC->%N` `dJpAA@zC-%N` dxPJpXXzS-%N`PW`)`x`]Jp@@@zS-<%N`]``Jp@@zC->%N``dJpAA@zC-%N`dx&JpXXzS-%N`W`)px`!&Jp@@zS-<%N`!``v&Jp@@zC->%N`v`d'JpAAzC-%N`dx.JpXXzS-%N`#W`) x`.Jp@@zS-<%N`#``.Jp@@zC->%N`#`d/JpAAzC-%N`$dxd7JpXXzS-%N`d,W`)x`q7Jp@@zS-<%N`q,``7Jp@@zC->%N`,`d8JpAAzC-%N`-dx:@JpXXzS-%N`:5W`)+x`G@Jp@@@zS-<%N`G5``@Jp@@zC->%N`5`dAJpAA@zC-%N`6dxHJpXXzS-%N`=W`);x`HJp@@zS-<%N`=``IJp@@zC->%N`>`dqJJpAAzC-%N`q?dxQJpXXzS-%N`FW`)Kx`QJp@@zS-<%N`F``QJp@@zC->%N`F`dRJpAAzC-%N`Gdx)ZJpXXzS-%N`)OW`)[x`5ZJp@@zS-<%N`5O``ZJp@@zC->%N`O`d[JpAAzC-%N`PdxbJpXXzS-%N`WW`)kx`cJp@@zS-<%N`X``tcJp@@zC->%N`tX`dQdJpAAzC-%N`QYdxkJpXXzS-%N``W`){x`kJp@@@zS-<%N````lJp@@zC->%N`a`dmJpAA@zC-%N`bdxrtJpXXzS-%N`riW`*x`}tJp@@zS-<%N`}i``tJp@@zC->%N`i`duJpAAzC-%N`jdx1}JpXXzS-%N`1rW`*x`<}Jp@@zS-<%N`%N`r`d~JpAAzC-%N`sdxJpXXzS-%N`zW`* x`Jp@@zS-<%N`z``FJp@@zC->%N`F{`dJpAAzC-%N`|dxJpXXzS-%N`W`*0x`Jp@@@zS-<%N```bJp@@zC->%N`b`dfJpAA@zC-%N`fdx`JpXXzS-%N``W`*@x`lJp@@@zS-<%N`l``Jp@@zC->%N``dJpAA@zC-%N`dxJpXXzS-%N`W`*Px`,Jp@@zS-<%N`,``Jp@@zC->%N``dzJpAAzC-%N`zdxJpXXzS-%N`W`*`x`Jp@@zS-<%N```Jp@@zC->%N``dJpAAzC-%N`dxvJpXXzS-%N`vW`*px`Jp@@zS-<%N```Jp@@zC->%N``dJpAAzC-%N`dx"JpXXzS-%N`"W`* x`.Jp@@@zS-<%N`.``Jp@@zC->%N``dJpAA@zC-%N`dxJpXXzS-%N`߷W`*x`Jp@@zS-<%N```?Jp@@zC->%N`?`dAJpAAzC-%N`AdxlJpXXzS-%N`lW`*,x`zJp@@zS-<%N`z``Jp@@zC->%N``dJpAAzC-%N`dx&JpXXzS-%N`&W`*<x`2Jp@@zS-<%N`2``Jp@@zC->%N``dZJpAAzC-%N`ZdxJpXXzS-%N`W`*Lx`Jp@@zS-<%N```PJp@@zC->%N`P`dAJpAAzC-%N`AdxJpXXzS-%N`W`*\x`Jp@@@zS-<%N```Jp@@zC->%N``dJpAA@zC-%N`dxlJpXXzS-%N`lW`*lx`wJp@@zS-<%N`w``Jp@@zC->%N``dJpAAzC-%N`dx$JpXXzS-%N`$W`*|x`0Jp@@zS-<%N`0``Jp@@zC->%N``dJpAAzC-%N`dxJpXXzS-%N`W`+x`Jp@@zS-<%N```Kp@@zC->%N``dKpAAzC-%N`dx7KpXXzS-%N`7W`+x`BKp@@@zS-<%N`B``Kp@@zC->%N``d KpAA@zC-%N`dxKpXXzS-%N`W`+ x`Kp@@@zS-<%N```WKp@@zC->%N`W`deKpAA@zC-%N`edxhKpXXzS-%N`hW`+0x`uKp@@zS-<%N`u``Kp@@zC->%N``dKpAAzC-%N`dx%"KpXXzS-%N`%W`+@x`1"Kp@@zS-<%N`1``R"Kp@@zC->%N`R`d#KpAAzC-%N`d`*Kp@@zS-<%N``x.KpXXzS-%N`#W`+Px`/Kp@@zC->%N`$`d0KpAAzC-%N`%dxj7KpXXzS-%N`j,W`+`x`7Kp@@@zS-<%N`,``7Kp@@zC->%N`,`d8KpAA@zC-%N`-dx@KpXXzS-%N`5W`+px`$@Kp@@zS-<%N`$5``@Kp@@zC->%N`5`dAKpAAzC-%N`6dxHKpXXzS-%N`=W`+ x`HKp@@zS-<%N`=``IKp@@zC->%N`>`d)JKpAAzC-%N`)?dx6QKpXXzS-%N`W`+x`CQKp@@zS-<%N```QKp@@zC->%N`N`dRKpAAzC-%N`EdxYKpXXzS-%N` W`+-x`YKp@@zS-<%N` `` ZKp@@zC->%N` `d[KpAAzC-%N` dx+bKpXXzS-%N`W`+=x`%N``dcKpAA@zC-%N`CdxjKpXXzS-%N`W`+Mx` kKp@@zS-<%N```3kKp@@zC->%N``dVlKpAAzC-%N`dxsKpXXzS-%N`^&W`+]x`sKp@@zS-<%N`q&``sKp@@zC->%N`&`duKpAAzC-%N`'dx|KpXXzS-%N`.W`+mx`(|Kp@@zS-<%N`.``|Kp@@zC->%N`?/`dc}KpAAzC-%N`#0dxKpXXzS-%N`r7W`+}x`ńKp@@@zS-<%N`7``Kp@@zC->%N`7`dKpAA@zC-%N`8dxvKpXXzS-%N`6@W`,x`Kp@@@zS-<%N`D@``Kp@@zC->%N`m@`dKpAA@zC-%N`AdxʕKpXXzS-%N`HW`,x`ڕKp@@zS-<%N`H``Kp@@zC->%N`H`d(KpAAzC-%N`Idx6KpXXzS-%N`PW`, x`GKp@@zS-<%N`Q``eKp@@zC->%N`%Q`dcKpAAzC-%N`#RdxKpXXzS-%N`~YW`,0x`ΦKp@@zS-<%N`Y``Kp@@zC->%N`Y`d&KpAAzC-%N`ZdxKpXXzS-%N`bW`,@x`Kp@@@zS-<%N`b``!Kp@@zC->%N`b`dBKpAA@zC-%N`ddxKpXXzS-%N`kW`,Px`Kp@@zS-<%N`k``jKp@@zC->%N`*l`dTKpAAzC-%N`mdxKpXXzS-%N``tW`,`x`Kp@@zS-<%N`ot``Kp@@zC->%N`t`dKpAAzC-%N`udxKpXXzS-%N`[}W`,px`Kp@@zS-<%N`i}``Kp@@zC->%N`}`dKpAAzC-%N`~dxTKpXXzS-%N`W`,x`bKp@@zS-<%N`"``Kp@@zC->%N`K`dKpAAzC-%N`dxKpXXzS-%N`W`,x`Kp@@@zS-<%N`ώ``)Kp@@zC->%N``d0KpAA@zC-%N`dxKpXXzS-%N`mW`,.x`Kp@@zS-<%N`{``Kp@@zC->%N``d"KpAAzC-%N`dxpKpXXzS-%N`0W`,>x`~Kp@@zS-<%N`>``Kp@@zC->%N`g`dKpAAzC-%N`dx-KpXXzS-%N`W`,Nx`:Kp@@zS-<%N```Kp@@zC->%N`O`dKpAAzC-%N`OdxKpXXzS-%N`W`,^x`Kp@@@zS-<%N```Kp@@zC->%N``d)LpAA@zC-%N`dxLpXXzS-%N`@W`,nx`Lp@@@zS-<%N`N``Lp@@zC->%N``dLpAA@zC-%N`dx0LpXXzS-%N`W`,~x`>Lp@@zS-<%N```Lp@@zC->%N`R`dLpAAzC-%N`IdxLpXXzS-%N`W`-x`Lp@@zS-<%N```TLp@@zC->%N``d>LpAAzC-%N`dx!LpXXzS-%N`hW`-x`!Lp@@zS-<%N`u``"Lp@@zC->%N``d#LpAAzC-%N`dxo*LpXXzS-%N`/W`- x`|*Lp@@@zS-<%N`<``*Lp@@zC->%N``d+LpAA@zC-%N`{dx2LpXXzS-%N`W`-0x` 3Lp@@zS-<%N```-3Lp@@zC->%N``df4LpAAzC-%N`&dx;LpXXzS-%N`W`-@x`%N``dQ=LpAAzC-%N`dxDLpXXzS-%N`@W`-Px`DLp@@zS-<%N`L``DLp@@zC->%N``dELpAAzC-%N`dx MLpXXzS-%N`W`-`x`#MLp@@zS-<%N```DMLp@@zC->%N``dNLpAAzC-%N`SdxULpXXzS-%N`W`-px`ULp@@@zS-<%N```0VLp@@zC->%N``dOWLpAA@zC-%N` dx^LpXXzS-%N`SW`-x`^Lp@@zS-<%N`a``^Lp@@zC->%N``d_LpAAzC-%N`dxOgLpXXzS-%N`W`-x`]gLp@@zS-<%N```gLp@@zC->%N`v`dhLpAAzC-%N`\dx pLpXXzS-%N`"W`-/x`pLp@@zS-<%N`"``lpLp@@zC->%N`,#`dpqLpAAzC-%N`0$dxxLpXXzS-%N`+W`-?x`xLp@@@zS-<%N`+``'yLp@@zC->%N`+`dzLpAA@zC-%N`,dxZLpXXzS-%N`4W`-Ox`hLp@@@zS-<%N`(4``Lp@@zC->%N`O4`doLpAA@zC-%N`/5dxLpXXzS-%N`<W`-_x`.Lp@@zS-<%N`<``RLp@@zC->%N`=`dmLpAAzC-%N`->dxLpXXzS-%N`EW`-ox`Lp@@zS-<%N`E``DLp@@zC->%N`F`dfLpAAzC-%N`&GdxLpXXzS-%N`kNW`-x`Lp@@zS-<%N`N``Lp@@zC->%N`N`dLpAAzC-%N`OdxpLpXXzS-%N`0WW`.x`~Lp@@@zS-<%N`>W``ӤLp@@zC->%N`W`d֥LpAA@zC-%N`Xdx$LpXXzS-%N`_W`.x`0Lp@@zS-<%N`_``Lp@@zC->%N`M``dmLpAAzC-%N`-adxLpXXzS-%N`hW`. x`Lp@@zS-<%N`h``Lp@@zC->%N`ei`dLpAAzC-%N`fjdxLpXXzS-%N`bqW`.0x`Lp@@zS-<%N`nq``Lp@@zC->%N`q`d%LpAAzC-%N`rdxaLpXXzS-%N`!zW`.@x`mLp@@zS-<%N`-z``Lp@@zC->%N`z`dLpAAzC-%N`{dxLpXXzS-%N`ĂW`.Px`Lp@@@zS-<%N`ׂ``5Lp@@zC->%N``drLpAA@zC-%N`2dxLpXXzS-%N`tW`.`x`Lp@@zS-<%N```Lp@@zC->%N`؋`dLpAAzC-%N`ȌdxvLpXXzS-%N`6W`.px`Lp@@zS-<%N`F``Lp@@zC->%N`l`dLpAAzC-%N`dxLpXXzS-%N`W`.x`Lp@@zS-<%N```]Lp@@zC->%N``diLpAAzC-%N`)dxPLpXXzS-%N`W`. x`\Lp@@@zS-<%N```Lp@@zC->%N`m`dLpAA@zC-%N`[dxLpXXzS-%N`حW`.0x`&Lp@@@zS-<%N```}Lp@@zC->%N`=`d~LpAA@zC-%N`>dxMpXXzS-%N`W`.@x`Mp@@zS-<%N```HMp@@zC->%N``dDMpAAzC-%N`dxMpXXzS-%N`W`.Px`Mp@@zS-<%N```ZMp@@zC->%N``dFMpAAzC-%N`dx!MpXXzS-%N`]W`.`x`!Mp@@zS-<%N`o``!Mp@@zC->%N``d"MpAAzC-%N`xdxP*MpXXzS-%N`W`.px`]*Mp@@@zS-<%N```*Mp@@zC->%N`t`d+MpAA@zC-%N`zdx3MpXXzS-%N`W`.x`3Mp@@zS-<%N```r3Mp@@zC->%N`2`d4MpAAzC-%N`Rdx;MpXXzS-%N`W`/x`;Mp@@zS-<%N```,%N``d+=MpAAzC-%N`dxDMpXXzS-%N`LW`/x`DMp@@zS-<%N`\``DMp@@zC->%N``dEMpAAzC-%N`dxHMMpXXzS-%N`W`/ x`UMMp@@zS-<%N```MMp@@zC->%N`h`dNMpAAzC-%N`hd`UMp@@zS-<%N``x ZMpXX@zS-%N` W`/0x`ZZMp@@@zC->%N` `dK[MpAAzC-%N` dx7cMpXXzS-%N`W`/@x`EcMp@@zS-<%N```cMp@@zC->%N`R`dgdMpAAzC-%N`'dxkMpXXzS-%N`WW`/Px`kMp@@zS-<%N`f``kMp@@zC->%N``dlMpAAzC-%N`dx[tMpXXzS-%N`'W`/`x`itMp@@zS-<%N`)'``tMp@@zC->%N`R'`duMpAAzC-%N`(dx}MpXXzS-%N`/W`/px`)}Mp@@zS-<%N`/``R}Mp@@zC->%N`0`dw~MpAAzC-%N`71dxMpXXzS-%N`8W`/x`Mp@@zS-<%N`8``Mp@@zC->%N`8`dGMpAAzC-%N`:dxMpXXzS-%N`@W`/!x`Mp@@zS-<%N`@``sMp@@zC->%N`3A`dgMpAAzC-%N`'BdxÖMpXXzS-%N`IW`/1x`ҖMp@@zS-<%N`I``*Mp@@zC->%N`I`dMpAAzC-%N`Jdx~MpXXzS-%N`>RW`/Ax`Mp@@zS-<%N`LR``Mp@@zC->%N`uR`dMpAAzC-%N`SdxMpXXzS-%N`ZW`/Qx`Mp@@zS-<%N`Z``AMp@@zC->%N`[`d[MpAAzC-%N`\dxMpXXzS-%N`xcW`/ax`̰Mp@@zS-<%N`c``Mp@@zC->%N`c`d0MpAAzC-%N`ddx1MpXXzS-%N`kW`/qx`EMp@@zS-<%N`l``Mp@@zC->%N`l`dܺMpAAzC-%N`mdxMpXXzS-%N`tW`/x`Mp@@zS-<%N`t``Mp@@zC->%N`t`d]MpAAzC-%N`dxMpXXzS-%N`W`0x`Mp@@zS-<%N```%Mp@@zC->%N``d.MpAAzC-%N`dxMpXXzS-%N`cW`0x`Mp@@zS-<%N`u``Mp@@zC->%N``dMpAAzC-%N`ХdxIMpXXzS-%N` W`0 x`ZMp@@zS-<%N```Mp@@zC->%N`E`dMpAAzC-%N`dxANpXXzS-%N`W`00x`TNp@@zS-<%N```Np@@zC->%N`s`dNpAAzC-%N`kdx\ NpXXzS-%N`W`0@x`k Np@@zS-<%N`+`` Np@@zC->%N`T`d NpAAzC-%N`zdxKNpXXzS-%N` W`0Px`YNp@@zS-<%N```Np@@zC->%N`B`dNpAAzC-%N`rdx NpXXzS-%N`W`0`x`Np@@zS-<%N```ANp@@zC->%N``dNpAAzC-%N`Kdx&NpXXzS-%N`W`0px`&Np@@zS-<%N```&Np@@zC->%N``d(NpAAzC-%N`dx/NpXXzS-%N`DW`0x`/Np@@zS-<%N`R``/Np@@zC->%N`{`d0NpAAzC-%N`dx"8NpXXzS-%N`W`0"x`48Np@@zS-<%N```^8Np@@zC->%N``d9NpAAzC-%N`Kdx@NpXXzS-%N`W`02x`@Np@@zS-<%N```ANp@@zC->%N``dJBNpAAzC-%N` dxINpXXzS-%N`QW`0Bx`INp@@zS-<%N`_``INp@@zC->%N``dJNpAAzC-%N`dxRNpXXzS-%N`W`0Rx`*RNp@@zS-<%N```PRNp@@zC->%N``dSNpAAzC-%N`AdxZNpXXzS-%N` W`0bx`ZNp@@zS-<%N` ``[Np@@zC->%N` `d\NpAAzC-%N`dxlcNpXXzS-%N`,W`0rx`}cNp@@zS-<%N`=``cNp@@zC->%N`a`ddNpAAzC-%N`dxlNpXXzS-%N`W`0x`$lNp@@zS-<%N```LlNp@@zC->%N` `dmNpAAzC-%N`G dxtNpXXzS-%N`'W`1x`tNp@@zS-<%N`'``uNp@@zC->%N`'`d:vNpAAzC-%N`(dx}NpXXzS-%N`S0W`1x`}Np@@zS-<%N`d0``}Np@@zC->%N`0`d~NpAAzC-%N`1dxNpXXzS-%N`8W`1 x`Np@@zS-<%N`8``uNp@@zC->%N`59`dfNpAAzC-%N`&:dxNpXXzS-%N`UAW`10x`Np@@zS-<%N`eA``ƎNp@@zC->%N`A`dُNpAAzC-%N`BdxSNpXXzS-%N`JW`1@x`gNp@@zS-<%N`'J``Np@@zC->%N`RJ`dNpAAzC-%N`rKdx֟NpXXzS-%N`RW`1Px`Np@@zS-<%N`R`` Np@@zC->%N`R`d%N`[`dNpAAzC-%N`\dxNpXXzS-%N`AdW`1px`Np@@zS-<%N`Xd``Np@@zC->%N`{d`dNpAAzC-%N`edxDNpXXzS-%N`mW`1x`SNp@@zS-<%N`m``yNp@@zC->%N`9m`d̻NpAAzC-%N`ndxNpXXzS-%N`uW`1#x`%Np@@zS-<%N`u``MNp@@zC->%N` v`dyNpAAzC-%N`9wdxNpXXzS-%N`~W`13x`Np@@zS-<%N`~``Np@@zC->%N`~`dNpAAzC-%N`dxNpXXzS-%N`dW`1Cx`Np@@zS-<%N`v``Np@@zC->%N``dNpAAzC-%N`dxqNpXXzS-%N`1W`1Sx`Np@@zS-<%N`G``Np@@zC->%N`c`dNpAAzC-%N`dx'NpXXzS-%N`W`1cx`5Np@@zS-<%N```^Np@@zC->%N``dNpAAzC-%N`OdxNpXXzS-%N`W`1sx`Np@@zS-<%N```Np@@zC->%N`ߡ`d6NpAAzC-%N`dxNpXXzS-%N`qW`1x`Np@@zS-<%N```Np@@zC->%N``dNpAAzC-%N`۫dx^OpXXzS-%N`W`2x`mOp@@zS-<%N`-``Op@@zC->%N``dOpAAzC-%N`dx/ OpXXzS-%N`W`2x`= Op@@zS-<%N```f Op@@zC->%N`&`d OpAAzC-%N`VdxOpXXzS-%N`W`2 x`Op@@zS-<%N```Op@@zC->%N``d3OpAAzC-%N`dxOpXXzS-%N`OW`20x`Op@@zS-<%N`]``Op@@zC->%N``dOpAAzC-%N`dxS#OpXXzS-%N`W`2@x`b#Op@@zS-<%N`"``#Op@@zC->%N`J`d$OpAAzC-%N`dx ,OpXXzS-%N`W`2Px`,Op@@zS-<%N```y,Op@@zC->%N`9`d-OpAAzC-%N`Pdx4OpXXzS-%N`W`2`x`4Op@@zS-<%N```5Op@@zC->%N``d=6OpAAzC-%N`dxAKOpXXzS-%N`W`2px`OKOp@@zS-<%N```KOp@@zC->%N``dLOpAAzC-%N`odxTOpXXzS-%N`W`2x`TOp@@zS-<%N```eTOp@@zC->%N`%`dgUOpAAzC-%N`'dx\OpXXzS-%N`W`2$x`\Op@@zS-<%N```,]Op@@zC->%N``d$^OpAAzC-%N`dxeOpXXzS-%N`GW`24x`eOp@@zS-<%N`T``eOp@@zC->%N``dgOpAAzC-%N`dxFnOpXXzS-%N`!W`2Dx`SnOp@@zS-<%N`!``nOp@@zC->%N`i!`doOpAAzC-%N`l"dxvOpXXzS-%N`)W`2Tx`wOp@@zS-<%N`)``dwOp@@zC->%N`$*`dZxOpAAzC-%N`+dxOpXXzS-%N`^2W`2dx`Op@@zS-<%N`k2``Op@@zC->%N`2`dOpAAzC-%N`3dxbOpXXzS-%N`";W`2tx`oOp@@zS-<%N`/;``Op@@zC->%N`w;`dȉOpAAzC-%N`<dx$OpXXzS-%N`CW`2x`:Op@@zS-<%N`C``UOp@@zC->%N`D`dOpAAzC-%N`]EdxOpXXzS-%N`cLW`3x`Op@@zS-<%N`pL``Op@@zC->%N`L`dOpAAzC-%N`Md`Op@@zS-<%N`T`x|OpXXzS-%N`%N`Y`dߧOpAAzC-%N`Zdx,OpXXzS-%N`aW`3 x`:Op@@zS-<%N`a``Op@@zC->%N`Lb`dvOpAAzC-%N`6cdxOpXXzS-%N`jW`30x` Op@@zS-<%N`j``-Op@@zC->%N`j`dMOpAAzC-%N` ldxOpXXzS-%N`ysW`3@x`Op@@zS-<%N`s``Op@@zC->%N`s`dOpAAzC-%N`tdxOpXXzS-%N`{W`3Px`'Op@@zS-<%N`{``Op@@zC->%N`|`dOpAAzC-%N`}dxOpXXzS-%N`W`3`x` Op@@zS-<%N`̄``8Op@@zC->%N``dcOpAAzC-%N`#dxOpXXzS-%N`wW`3px`Op@@zS-<%N```Op@@zC->%N``dCOpAAzC-%N`dx|OpXXzS-%N`<W`3x`Op@@zS-<%N`K``Op@@zC->%N`t`dOpAAzC-%N`dx"OpXXzS-%N`W`3%x`2Op@@zS-<%N```]Op@@zC->%N``dOpAAzC-%N`DdxOpXXzS-%N`cW`35x`Op@@zS-<%N`u``Op@@zC->%N``d OpAAzC-%N`ɨdx{OpXXzS-%N`;W`3Ex`Op@@zS-<%N`R``Op@@zC->%N`r`dOpAAzC-%N`dxVPpXXzS-%N`W`3Ux`fPp@@zS-<%N`&``Pp@@zC->%N`P`dPpAAzC-%N`dxPpXXzS-%N`W`3ex`"Pp@@zS-<%N```EPp@@zC->%N``dPpAAzC-%N`AdxPpXXzS-%N`W`3ux`Pp@@zS-<%N```Pp@@zC->%N``d2PpAAzC-%N`dx PpXXzS-%N`W`3x` !Pp@@zS-<%N```2!Pp@@zC->%N``d"PpAAzC-%N`dx)PpXXzS-%N`W`4x` *Pp@@zS-<%N```q*Pp@@zC->%N`1`dz+PpAAzC-%N`:dx2PpXXzS-%N`W`4x`2Pp@@zS-<%N```3Pp@@zC->%N``d4PpAAzC-%N`dxHPpXXzS-%N`^W`4 x`HPp@@zS-<%N`m``HPp@@zC->%N``dJPpAAzC-%N`dxbQPpXXzS-%N`"W`40x`pQPp@@zS-<%N`0``QPp@@zC->%N`Y`dRPpAAzC-%N`dxYPpXXzS-%N` W`4@x`ZPp@@zS-<%N` ``)ZPp@@zC->%N` `dh[PpAAzC-%N`(dxbPpXXzS-%N`W`4Px`bPp@@zS-<%N```%cPp@@zC->%N``dcPpAAzC-%N`dxkPpXXzS-%N`?W`4`x`kPp@@zS-<%N`N``kPp@@zC->%N``dlPpAAzC-%N`dx tPpXXzS-%N`&W`4px`-tPp@@zS-<%N`&``tPp@@zC->%N`C'`dyuPpAAzC-%N`9(dx|PpXXzS-%N`/W`4x`|Pp@@zS-<%N`/``J}Pp@@zC->%N` 0`d-~PpAAzC-%N`0dxPpXXzS-%N`X8W`4&x`Pp@@zS-<%N`e8``Pp@@zC->%N`8`dPpAAzC-%N`9dxXPpXXzS-%N`AW`46x`ePp@@zS-<%N`%A``Pp@@zC->%N`{A`dPpAAzC-%N`BdxPpXXzS-%N`IW`4Fx`)Pp@@zS-<%N`I``Pp@@zC->%N`?J`dhPpAAzC-%N`(KdxߟPpXXzS-%N`RW`4Vx`Pp@@zS-<%N`R``Pp@@zC->%N`cS`dPpAAzC-%N`RTdxPpXXzS-%N`k[W`4fx`Pp@@zS-<%N`w[``Pp@@zC->%N`[`dPpAAzC-%N`\dxaPpXXzS-%N`!dW`4vx`nPp@@zS-<%N`.d``Pp@@zC->%N`Od`dѲPpAAzC-%N`edx'PpXXzS-%N`lW`4x`6Pp@@zS-<%N`l``ZPp@@zC->%N`m`dPpAAzC-%N`qndxPpXXzS-%N`luW`5x`Pp@@zS-<%N`xu`` Pp@@zC->%N`u`dPpAAzC-%N`vdxiPpXXzS-%N`)~W`5x`uPp@@zS-<%N`5~``Pp@@zC->%N`~`dPpAAzC-%N`dx:PpXXzS-%N`W`5 x`HPp@@zS-<%N```Pp@@zC->%N`]`dPpAAzC-%N``dxPpXXzS-%N`W`50x`Pp@@zS-<%N```IPp@@zC->%N` `dYPpAAzC-%N`dxPpXXzS-%N`AW`5@x`Pp@@zS-<%N`N``Pp@@zC->%N``dPpAAzC-%N`dxKPpXXzS-%N` W`5Px`XPp@@zS-<%N```Pp@@zC->%N`q`dPpAAzC-%N`Ydx=QpXXzS-%N`W`5`x`IQp@@zS-<%N` ``Qp@@zC->%N`Z`dQpAAzC-%N`sdx QpXXzS-%N`W`5px` Qp@@zS-<%N```O Qp@@zC->%N``d>QpAAzC-%N`dxwQpXXzS-%N`7W`5x`Qp@@zS-<%N`C``Qp@@zC->%N``dQpAAzC-%N`dxQpXXzS-%N`W`5'x` Qp@@zS-<%N```PQp@@zC->%N``dBQpAAzC-%N`dx&QpXXzS-%N`xW`57x`&Qp@@zS-<%N```&Qp@@zC->%N``dE(QpAAzC-%N`dx/QpXXzS-%N`AW`5Gx`/Qp@@zS-<%N`M``/Qp@@zC->%N``d0QpAAzC-%N`dx>8QpXXzS-%N`W`5Wx`I8Qp@@zS-<%N` ``8Qp@@zC->%N`a`d9QpAAzC-%N`[dx@QpXXzS-%N`W`5gx`@Qp@@zS-<%N```$AQp@@zC->%N``d*BQpAAzC-%N`dxIQpXXzS-%N`AW`5wx`IQp@@zS-<%N`L``IQp@@zC->%N``dJQpAAzC-%N`dx=RQpXXzS-%N`W`5x`IRQp@@zS-<%N` ``RQp@@zC->%N`\`dSQpAAzC-%N`ddxZQpXXzS-%N` W`6x`ZQp@@zS-<%N` ``[Qp@@zC->%N` `d\QpAAzC-%N`dxxcQpXXyDwS-%N`8W`6x`cQp@@zDwS-<%N`D``cQp@@yDwC->%N``ddQpAAzDwC-%N`dx8lQpXXzDwS-%N`W`6 x`DlQp@@yDwS-<%N```lQp@@zDwC->%N`A`dmQpAAyDwC-%N`C dxtQpXXvDwS-%N`'W`60x`tQp@@vDwS-<%N`'``=uQp@@vDwC->%N`'`d[vQpAAvDwC-%N`)dx}QpXX@~DwS-%N`_0W`6@x`}Qp@@@{DwS-<%N`k0``}Qp@@@~DwC->%N`0`d~QpAA@{DwC-%N`1dxXQpXX}DwS-%N`9W`6Px`dQp@@@rDwS-<%N`$9``Qp@@}DwC->%N`c9`dQpAA@rDwC-%N`|:dxQpXXDwS-%N`AW`6`x`Qp@@wDwS-<%N`A``?Qp@@DwC->%N`A`dFQpAAwDwC-%N`CdxQpXXwDwS-%N`cJW`6px`Qp@@DwS-<%N`nJ``Qp@@wDwC->%N`J`d QpAADwC-%N`KdxbQpXX@rDwS-%N`"SW`6x`qQp@@}DwS-<%N`1S``Qp@@@rDwC->%N`pS`dܡQpAA}DwC-%N`TdxQpXXzS-%N`[W`6(x`Qp@@zS-<%N`[``FQp@@zC->%N`\`dMQpAAzC-%N` ]dxoQpXXzS-%N`/dW`68x`yQp@@zS-<%N`9d``˱Qp@@zC->%N`d`dֲQpAAzC-%N`edx#QpXXzS-%N`lW`6Hx`.Qp@@zS-<%N`l``|Qp@@zC->%N`%N`v`dQpAAzC-%N`DwdxlQpXXzS-%N`,~W`6hx`xQp@@zS-<%N`8~``Qp@@zC->%N`|~`dQpAAzC-%N`dxQpXXzS-%N`݆W`6xx`)Qp@@zS-<%N```Qp@@zC->%N`?`dhQpAAzC-%N`(d`Qp@@zS-<%N`p`xQpXXzS-%N`ǓW`6x`dQp@@zC->%N`$`dCQpAAzC-%N`dxQpXXzS-%N`W`7x`Qp@@zS-<%N```Qp@@zC->%N``d)QpAAzC-%N`dxQpXXzS-%N`RW`7x`Qp@@zS-<%N`_``Qp@@zC->%N``dQpAAzC-%N`ڦdxQpXXzS-%N`حW`7 x`%Qp@@zS-<%N```gQp@@zC->%N`'`dZQpAAzC-%N`dxRpXXzS-%N`W`70x`Rp@@zS-<%N```Rp@@zC->%N`]`dRpAAzC-%N`Kdx RpXXzS-%N`bW`7@x` Rp@@zS-<%N`o`` Rp@@zC->%N`ſ`d RpAAzC-%N`dxkRpXXzS-%N`+W`7Px`}Rp@@zS-<%N`=``Rp@@zC->%N`_`dRpAAzC-%N`dx(RpXXzS-%N`W`7`x`5Rp@@@zS-<%N```VRp@@zC->%N``dRpAA@zC-%N`Tdx&RpXXzS-%N`W`7p x`&Rp@@@zS-<%N```Q'Rp@@zC->%N``dS(RpAA@zC-%N`dx/RpXXzS-%N`lW`7x`/Rp@@zS-<%N`y``0Rp@@zC->%N``d1RpAAzC-%N`dxm8RpXXzS-%N`-W`7)x`~8Rp@@zS-<%N`>``8Rp@@zC->%N`\`d9RpAAzC-%N`dx8ARpXXzS-%N`W`79x`FARp@@zS-<%N```ARp@@zC->%N`\`dBRpAAzC-%N`VdxIRpXXzS-%N`W`7Ix`IRp@@zS-<%N```RJRp@@zC->%N``dWKRpAAzC-%N`dxRRpXX@zS-%N`VW`7Yx`RRp@@zS-<%N`d``RRp@@@zC->%N``dSRpAAzC-%N`dx[[RpXXzS-%N`W`7ix`j[Rp@@zS-<%N`*``[Rp@@zC->%N``d\RpAAzC-%N`dxdRpXXzS-%N`W`7yx`,dRp@@zS-<%N```IdRp@@zC->%N` `d|eRpAAzC-%N`<dxlRpXXzS-%N`BW`7x`lRp@@zS-<%N`P``lRp@@zC->%N``dRpAAzC-%N`;dxRpXX@zS-%N`^AW`8x`Rp@@zS-<%N`qA``ΎRp@@@zC->%N`A`dُRpAAzC-%N`BdxRpXXzS-%N`IW`8x`-Rp@@zS-<%N`I``Rp@@zC->%N`FJ`dyRpAAzC-%N`9KdxRpXXzS-%N`RW`8 x` Rp@@zS-<%N`R``)Rp@@zC->%N`R`dSRpAAzC-%N`TdxRpXXzS-%N`u[W`80x`ȨRp@@zS-<%N`[``Rp@@zC->%N`[`dRpAAzC-%N`\dxRpXXzS-%N`LdW`8@x`Rp@@@zS-<%N`\d``Rp@@zC->%N`d`dRpAA@zC-%N`edxJRpXXzS-%N` mW`8Px`[Rp@@@zS-<%N`m``Rp@@zC->%N`hm`dRpAA@zC-%N`~ndxRpXXzS-%N`uW`8`x`%Rp@@zS-<%N`u``KRp@@zC->%N` v`d{RpAAzC-%N`;wdxRpXXzS-%N`~W`8p x`Rp@@zS-<%N`~``?Rp@@zC->%N`~`d+RpAAzC-%N`dxRpXXzS-%N`aW`8x`Rp@@zS-<%N`n``Rp@@zC->%N``dRpAAzC-%N`dxkRpXXzS-%N`+W`8*x`{Rp@@zS-<%N`;``Rp@@zC->%N``dRpAAzC-%N`dxRpXX@zS-%N`W`8:x` Rp@@zS-<%N`ʘ``]Rp@@@zC->%N``dIRpAAzC-%N` dxRpXXzS-%N`|W`8Jx`Rp@@zS-<%N``` Rp@@zC->%N``d RpAAzC-%N`ˢdxRpXXzS-%N`RW`8Zx`Rp@@zS-<%N`_``Rp@@zC->%N``d RpAAzC-%N`dx`SpXXzS-%N` W`8jx`qSp@@zS-<%N`1``Sp@@zC->%N`S`dSpAAzC-%N`~dxSpXX@zS-%N`W`8zx`Sp@@zS-<%N```) Sp@@@zC->%N``d SpAAzC-%N`ӼdxSpXXzS-%N`FW`8x`Sp@@zS-<%N`T``Sp@@zC->%N``dSpAAzC-%N`dxFSpXXzS-%N`W`9x`XSp@@zS-<%N```{Sp@@zC->%N`;`dSpAAzC-%N`hdx#SpXXzS-%N`W`9x`#Sp@@zS-<%N```4#Sp@@zC->%N``d$SpAAzC-%N`Adx+SpXXzS-%N`W`9 x`+Sp@@@zS-<%N```,,Sp@@zC->%N``d -SpAA@zC-%N`dxv4SpXX@{DwS-%N`6W`90x`4Sp@@@uDwS-<%N`H``4Sp@@@{DwC->%N``d5SpAA@uDwC-%N`edx5=SpXX@uDwS-%N`W`9@x`E=Sp@@@{DwS-<%N```=Sp@@@uDwC->%N`P`d>SpAA@{DwC-%N`PdxESpXX@{DwS-%N`W`9Px`ESp@@@uDwS-<%N```+FSp@@@{DwC->%N``dAGSpAA@uDwC-%N`dxNSpXX@uDwS-%N`GW`9`x`NSp@@@{DwS-<%N`U``NSp@@@uDwC->%N``dOSpAA@{DwC-%N`dxWSpXX@{DwS-%N` W`9p x`#WSp@@@uDwS-<%N` ```WSp@@@{DwC->%N` `dXSpAA@uDwC-%N`A dx_SpXX@uDwS-%N`\W`9x`_Sp@@@{DwS-<%N`k``_Sp@@@uDwC->%N``d`SpAA@{DwC-%N`dxOhSpXX@{DwS-%N`W`9+x`]hSp@@@uDwS-<%N```hSp@@@{DwC->%N`h`diSpAA@uDwC-%N`bdxqSpXX@uDwS-%N`#W`9;x`qSp@@@{DwS-<%N`#``^qSp@@@uDwC->%N`$`drSpAA@{DwC-%N`D%dxySpXX@{DwS-%N`~,W`9Kx`ySp@@@uDwS-<%N`,``zSp@@@{DwC->%N`,`d{SpAA@uDwC-%N`-dxtSpXX@uDwS-%N`45W`9[x`Sp@@@{DwS-<%N`B5``ʂSp@@@uDwC->%N`5`d׃SpAA@{DwC-%N`6dx1SpXX@{DwS-%N`=W`9kx`?Sp@@@uDwS-<%N`=``Sp@@@{DwC->%N`E>`dySpAA@uDwC-%N`9?dxSpXX@uDwS-%N`FW`9{x`Sp@@@{DwS-<%N`F``DSp@@@uDwC->%N`G`deSpAA@{DwC-%N`%Hdx|SpXX@{DwS-%N`%N`O`dSpAA@uDwC-%N`PdxSpXX@uDwS-%N`WW`:x`Sp@@@{DwS-<%N`W``\Sp@@@uDwC->%N`X`d[SpAA@{DwC-%N`YdxíSpXX@{DwS-%N``W`:x`ӭSp@@@uDwS-<%N````Sp@@@{DwC->%N```d'SpAA@uDwC-%N`adxtSpXX@uDwS-%N`4iW`: x`Sp@@@{DwS-<%N`Bi``жSp@@@uDwC->%N`i`dSpAA@{DwC-%N`|jdx1SpXX@{DwS-%N`qW`:0x`@Sp@@@uDwS-<%N`r``Sp@@@{DwC->%N`Ar`dSpAA@uDwC-%N`csdxSpXX@uDwS-%N`zW`:@x` Sp@@@{DwS-<%N`z``RSp@@@uDwC->%N`{`ddSpAA@{DwC-%N`$|dxSpXX@{DwS-%N`nW`:Px`Sp@@@uDwS-<%N`}``Sp@@@{DwC->%N`ă`dSpAA@uDwC-%N`dxSpXX@uDwS-%N`?W`:`x`Sp@@@{DwS-<%N`M``Sp@@@uDwC->%N``dSpAA@{DwC-%N`dx>SpXX@{DwS-%N`W`:p x`MSp@@@uDwS-<%N` ``Sp@@@{DwC->%N`X`dSpAA@uDwC-%N`dxSpXX@uDwS-%N`W`:x`Sp@@@{DwS-<%N`Ɲ``jSp@@@uDwC->%N`*`diSpAA@{DwC-%N`)dxSpXX@{DwS-%N`?W`:,x`Sp@@@uDwS-<%N`O``Sp@@@{DwC->%N``dSpAA@uDwC-%N`dx0SpXX@uDwS-%N`W`:<x`=Sp@@@{DwS-<%N```Sp@@@uDwC->%N`D`dSpAA@{DwC-%N`QdxTpXX@{DwS-%N`W`:Lx`Tp@@@uDwS-<%N```>Tp@@@{DwC->%N``dFTpAA@uDwC-%N`d` Tp@@@uDwS-<%N`M`xTpXX@{DwS-%N`W`:\x`Tp@@@{DwC->%N``dTpAA@uDwC-%N`dxnTpXX@uDwS-%N`.W`:lx`|Tp@@@{DwS-<%N`<``Tp@@@uDwC->%N`a`dTpAA@{DwC-%N`vdx##TpXX@{DwS-%N`W`:|x`6#Tp@@@uDwS-<%N```V#Tp@@@{DwC->%N``d$TpAA@uDwC-%N`Hdx+TpXX@uDwS-%N`W`:x`+Tp@@@{DwS-<%N```C,Tp@@@uDwC->%N``dS-TpAA@{DwC-%N`dx4TpXX@{DwS-%N`gW`;x`4Tp@@@uDwS-<%N`v``4Tp@@@{DwC->%N``d6TpAA@uDwC-%N`dx2=TpXX@uDwS-%N`W`;x`C=Tp@@@{DwS-<%N```=Tp@@@uDwC->%N`?`di>TpAA@{DwC-%N`)dxETpXX@{DwS-%N`W`; x`ETp@@@uDwS-<%N```|FTp@@@{DwC->%N`<`diGTpAA@uDwC-%N`)dxNTpXX@uDwS-%N`e W`;0x`NTp@@@{DwS-<%N`u ``NTp@@@uDwC->%N` `dOTpAA@{DwC-%N` dx`WTpXX@{DwS-%N` W`;@x`pWTp@@@uDwS-<%N`0 ``WTp@@@{DwC->%N`r `dXTpAA@uDwC-%N` dxs_TpXX@uDwS-%N`3 W`;Px`_Tp@@@{DwS-<%N`E ``_Tp@@@uDwC->%N` `d`TpAA@{DwC-%N` dx hTpXX@{DwS-%N` W`;`x`#hTp@@@uDwS-<%N` ``lhTp@@@{DwC->%N`, `dgiTpAA@uDwC-%N`' dxpTpXX@uDwS-%N`^# W`;p x`pTp@@@{DwS-<%N`o# ``pTp@@@uDwC->%N`# `dqTpAA@{DwC-%N`$ dxcyTpXX@{DwS-%N`#, W`;x`uyTp@@@uDwS-<%N`5, ``yTp@@@{DwC->%N`, `dzTpAA@uDwC-%N`- dx(TpXX@uDwS-%N`4 W`;-x`:Tp@@@{DwS-<%N`4 ``zTp@@@uDwC->%N`:5 `dTpAA@{DwC-%N`j6 dxTpXX@{DwS-%N`= W`;=x`Tp@@@uDwS-<%N`= ``QTp@@@{DwC->%N`> `dNTpAA@uDwC-%N`? dxTpXX%N`F `dTpAA%N`{O `dTpAA%N`5X `drTpAA%N`` `dQTpAA%N`i `dͷTpAA%N`Dr `dTpAA%N`{ `dTTpAA@%N`ǃ `d-TpAA%N`% `dTpAA%N` `dETpAA%N` `dTpAA%N`y `dTpAA%N`8 `dTpAA%N` `d6UpAA%N` `dUpAA@x`Up@@%N` `dUpAA%N`? `d UpAA%N` `d(UpAA%N`n `d1UpAA@zC-%N` dxE9UpXXzS-%N` W`<~x`S9Up@@zS-<%N` ``9Up@@zC->%N`j `d:UpAAzC-%N`b dxAUpXXzS-%N` W`<x`BUp@@zS-<%N` ``EBUp@@zC->%N` `dmCUpAAzC-%N`- dxJUpXX%N` `dKUpAA%N`z `dTUpAAzC-%N`_ dx[UpXXzS-%N` W`= x`\Up@@zS-<%N` ``$\Up@@zC->%N` `d6]UpAAzC-%N` dxdUpXX@zS-%N`U W`=0x`dUp@@zS-<%N`e ``dUp@@@zC->%N` `d3fUpAAzC-%N` dx[mUpXXzS-%N` W`=@x`mmUp@@zS-<%N`- ``mUp@@zC->%N`U `dnUpAAzC-%N`{! dxuUpXXzS-%N`( W`=Px`uUp@@zS-<%N`( ``LvUp@@zC->%N` ) `dKwUpAAzC-%N` * dxe~UpXXzS-%N`%1 W`=`x`s~Up@@zS-<%N`31 ``~Up@@zC->%N`1 `dUpAAzC-%N`c2 dxUpXX@zS-%N`9 W`=px`Up@@zS-<%N`9 ``CUp@@@zC->%N`: `dFUpAAzC-%N`; dxUpXXzS-%N`YB W`=x`Up@@zS-<%N`gB ``Up@@zC->%N`B `dސUpAAzC-%N`C dx UpXXzS-%N`J W`=/x`!Up@@zS-<%N`J ``JUp@@zC->%N` K `dyUpAAzC-%N`9L dxWUpXXzS-%N`S W`=?x`jUp@@zS-<%N`*S ``Up@@zC->%N`ES `dUpAAzC-%N`mT dxUpXXzS-%N`[ W`=Ox`Up@@@zS-<%N`[ ``DUp@@zC->%N`\ `dYUpAA@zC-%N`] dxUpXXzS-%N`~d W`=_x`̱Up@@@zS-<%N`d ``"Up@@zC->%N`d `dUpAA@zC-%N`e dxUpXXzS-%N`Nm W`=ox`Up@@zS-<%N`am ``ǺUp@@zC->%N`m `d+UpAAzC-%N`n dxZUpXXzS-%N`v W`=x`kUp@@zS-<%N`+v ``Up@@zC->%N`Qv `dUpAAzC-%N`~w dxUpXXzS-%N`~ W`=x`&Up@@zS-<%N`~ ``}Up@@zC->%N`= `dzUpAAzC-%N`: dxUpXXzS-%N` W`>x`Up@@zS-<%N` ``>Up@@zC->%N` `d>UpAAzC-%N` dxUpXX@zS-%N`] W`>x`Up@@zS-<%N`j ``Up@@@zC->%N` `dUpAAzC-%N`Ñ dx-UpXXzS-%N` W`> x`;Up@@zS-<%N` ``Up@@zC->%N`Q `dUpAAzC-%N`w dxUpXXzS-%N` W`>0x`Up@@zS-<%N` ``AUp@@zC->%N` `dUpAAzC-%N`â dxUpXXzS-%N`B W`>@x`Up@@zS-<%N`R ``Up@@zC->%N`z `dUpAAzC-%N` dx=VpXX@zS-%N` W`>Px`NVp@@zS-<%N` ``oVp@@@zC->%N`/ `dtVpAAzC-%N`4 dx VpXXzS-%N` W`>`x` Vp@@zS-<%N`ѻ ``< Vp@@zC->%N` `dy VpAAzC-%N`9 dxVpXXzS-%N`V W`>px`Vp@@zS-<%N`f ``Vp@@zC->%N` `dVpAAzC-%N` dxNVpXXzS-%N` W`> x`bVp@@zS-<%N`" ``Vp@@zC->%N`K `dVpAAzC-%N`t dx #VpXXzS-%N` W`>0x`#Vp@@@zS-<%N` ``D#Vp@@zC->%N` `dH$VpAA@zC-%N` d`+Vp@@zS-<%N`g `xX4VpXX@zS-%N` W`>@x`4Vp@@@zC->%N`} `d5VpAAzC-%N` dx!=VpXXzS-%N` W`>Px`5=Vp@@zS-<%N` ```=Vp@@zC->%N` `di>VpAAzC-%N`) dxEVpXX@`x`EVp@@%N` `d?GVpAApx`NVp@@%N` `dOVpAAx`mWVp@@%N`U `dXVpAAx`_Vp@@@%N` `dOaVpAA@%N` `diVpAA%N`l$ `drVpAA%N`, `d]{VpAA%N`5 `dVpAA W`?@x`mVp@@ ``Vp@@@%N`J> `dVpAA%N`,G `diVpAA%N`O `dVpAA%N`X `d%VpAA@%N`[a `dVpAA%N`#j `dyVpAA%N`r `dVpAA%N`c{ `dVpAA%N`W `dVpAA%N` `d[VpAA%N` `dVpAA%N`] `d>WpAA@%N` `dWWpAA%N` `d#WpAA%N` `dWpAA%N` `d\(WpAA%N` `d0WpAA%N`` `d9WpAA@%N` `dOBWpAA%N` `dcKWpAA@%N` `d"SWpAA%N` `d,\WpAA@%N` `ddWpAA%N`o `dmWpAA@%N`]( `dvWpAA%N`0 `d#WpAA@%N`9 `dWpAA%N`aB `dWpAA@%N`K `dqWpAA%N`S `d;WpAA@%N`\ `dժWpAA%N`]e `dWpAA@%N`-n `dWpAA%N`v `dWpAA@%N`k `dWpAA%N`$ `dQWpAA@%N`̐ `d1WpAA%N`a `dWpAA@%N` `dAWpAA%N` `dWpAA@%N` `dNXpAA%N`$ `d} XpAA@%N` `dXpAA%N`b `dXpAA@%N` `dx$XpAA%N` `d'-XpAA@%N` `d5XpAA%N` `dF>XpAA@%N` `d=GXpAA%N` `dOXpAA@%N`U `dXXpAA%N` `dsaXpAA@%N` `dnXpAA@%N`|( `dvXpAA~XpXX%N`M1 `dXpAA@%N` : `doXpAA%N`B `dېXpAA@%N`J `d2XpAA%N`SS `dXpAA@%N`[ `dBXpAA%N`vd `dXpAA@%N`,m `d?XpAA%N`u `d'XpAA@%N`~ `dXpAA%N`C `dXpAA@%N` `dXpAA%N` `dAXpAA@%N`t `dXpAA%N`! `dXpAA@%N`ӱ `dNYpAA%N` `dYpAA@%N`G `dYpAA%N` `dDYpAA@%N` `d"YpAA%N`| `d+YpAA@%N` `dV4YpAA%N` `d%N`H `dEYpAA%N` `dSNYpAA@%N``dWYpAA%N`l`d_YpAA@%N`'`dwhYpAA%N`"`dcqYpAA@%N`+`dyYpAA%N`q@`dYpAA@%N`,I`djYpAA%N`Q`dCYpAA@%N`Z`dۨYpAA%N`b`d%N`k`dYpAA%N`Tt`dYpAA@%N`|`d;YpAA%N`܅`d;YpAA@%N``dYpAAYpXX%N`N`dYpAA@%N` `dcYpAA%N``d8YpAA@%N`U`dYpAA%N``dXZpAA@%N``dZpAA%N`l`dZpAA@%N`=`d"ZpAA*Zp@@%N``d?+ZpAA@%N``d3ZpAA%N`|`d%N`>`dOEZpAA%N``dNZpAA@%N`x`dVZpAA%N`7`d_ZpAA@%N``d hZpAA%N`"`d qZpAA@%N`h+`dyZpAA%N`!4`dpZpAA@%N`<`d6ZpAA%N`y`d ZpAA%N`/`dZpAA@%N``dZpAA%N`>!`dѱZpAA@%N`m)`dZpAAZpXX%N`2`dZpAA@%N`:`dZpAA%N`^C`dZpAA%N`K`dZpAA%N`xT`d ZpAA@%N`\`dCZpAA%N`)e`dZpAA@%N`m`d[ZpAA%N`{v`d[pAA@%N`R`d[pAA%N``d[pAA@%N`j`d![pAA%N``dj)[pAA@%N``d1[pAA%N`q`d;[pAA@%N``dhC[pAA%N``d&L[pAA@%N``dbT[pAA%N`5`d][pAA@%N``d+f[pAA%N`4`d[pAA@[p@@%N``d[pAA%N``d?[pAA@%N`A`dǡ[pAA%N``dR[pAA@%N`"`d[pAA%N`6+`d[pAA@%N`3`dg[pAA%N`<`d[pAA@%N`E`d[pAA%N`M`d*[pAA@%N`OV`d[pAA%N`^`da[pAA@%N`g`d[pAA%N`Ep`d\pAA@qdx.\pXX@%N`x`d \pAA%N`ǁ`dU\pAA@%N`T`d\pAA%N`0`d#\pAA@%N``d+\pAA%N``d4\pAA@%N`y`d=\pAA%N`)`dE\pAA@%N`ν`ddN\pAA%N`~`dW\pAA@%N`.`d_\pAA%N``d^h\pAA@%N`Q`dp\pAA%N``dy\pAA@%N``d\pAA%N``dD\pAA@%N`p`d\pAA%N`7 `d\pAA@%N``dY\pAA%N``d1\pAA@%N`L&`d\pAA%N`.`dy\pAA@%N`;`dF\pAA@%N`D`dE\pAA%N`M`d\pAA@%N`U`d1\pAA%N`]`d\pAA@%N`af`d\pAA%N`n`dX\pAA@]pXX@%N`v`d]pAA%N``dj]pAA@%N`c`d]pAA%N``d!]pAA@%N``d*]pAA%N`>`d2]pAA:wC-%N``dx9]pXX:wS-%N`_W`Kx`9]p@@@:wS-<%N`y``:]p@@:wC->%N``d&;]pAA@:wC-%N`dxB]pXX@:wS-%N`,W`K x`B]p@@:wS-<%N`C``B]p@@@:wC->%N`k`dC]pAA:wC-%N`dxZK]pXX:wS-%N`ڻW`K0x`kK]p@@@:wS-<%N```K]p@@:wC->%N``dL]pAA@:wC-%N`dx#T]pXX%N``dkU]pAA%N``da^]pAA%N`5`df]pAA:wC-%N`:dx{]pXX:wS-%N`W`Kpx`{]p@@@:wS-<%N`0``{]p@@:wC->%N`q`d#}]pAA@:wC-%N`dx&]pXX@:wS-%N`W`K-x`<]p@@:wS-<%N```S]p@@@:wC->%N``d]pAA:wC-%N`dx]pXX:wS-%N`kW`K=x`]p@@@:wS-<%N`|``]p@@:wC->%N``dX]pAA@:wC-%N`dx]pXX@:wS-%N`>W`KMx`Օ]p@@:wS-<%N`U``]p@@@:wC->%N`t`d]pAA:wC-%N`dxy]pXX:wS-%N`W`K]x`]p@@@:wS-<%N```]p@@:wC->%N`4`d՟]pAA@:wC-%N`Udx]pXX:wS-%N`W`Kmx`]p@@:wS-<%N`<``Ϧ]p@@:wC->%N`O`d]pAA:wC-%N`dxU]pXX:wS-%N`W`K}x`k]p@@:wS-<%N```]p@@:wC->%N` `d]pAA:wC-%N`4!dx ]pXX:wS-%N`(W`Kx`)]p@@:wS-<%N`(``;]p@@:wC->%N`(`dU]pAA:wC-%N`)dx]pXX:wS-%N`X1W`Kx`]p@@:wS-<%N`l1``]p@@:wC->%N`1`dC]pAA:wC-%N`2dx]pXX:wS-%N` :W`Lx`]p@@@:wS-<%N`5:``]p@@:wC->%N`S:`d]pAA@:wC-%N`p;dx4]pXX@:wS-%N`BW`Lx`O]p@@:wS-<%N`B``c]p@@@:wC->%N`B`d]pAA:wC-%N` Ddx]pXX:wS-%N`tKW`L x` ]p@@:wS-<%N`K``)]p@@:wC->%N`K`dS]pAA:wC-%N`Ldx]pXX:wS-%N`ITW`L0x`]p@@:wS-<%N`jT``]p@@:wC->%N`T`d]pAA:wC-%N`Udx]pXX:wS-%N`]W`L@x`]p@@:wS-<%N`]``]p@@:wC->%N`?]`d]pAA:wC-%N`Q^dx,]pXX:wS-%N`eW`LPx`I]p@@:wS-<%N`e``]p@@:wC->%N`?f`d]pAA:wC-%N`+gdx]pXX:wS-%N`nnW`L`x`]p@@:wS-<%N`n``,]p@@:wC->%N`n`d<]pAA:wC-%N`odx^pXX:wS-%N`,wW`Lpx`^p@@:wS-<%N`>w``^p@@:wC->%N`fw`d^pAA:wC-%N`yxdx}^pXX:wS-%N`W`L.x`^p@@:wS-<%N```^p@@:wC->%N`1`d^pAA:wC-%N`dxG^pXX:wS-%N`LjW`L>x`[^p@@:wS-<%N`ۈ``^p@@:wC->%N``d^pAA:wC-%N`dx ^pXX:wS-%N`cW`LNx` ^p@@:wS-<%N`}``!^p@@:wC->%N``dG"^pAA:wC-%N`ǒdx)^pXX:wS-%N`.W`L^x`)^p@@@:wS-<%N`B``)^p@@:wC->%N`j`d*^pAA@:wC-%N`ydx}2^pXX@:wS-%N`W`Lnx`2^p@@:wS-<%N```C3^p@@@:wC->%N`ã`d'4^pAA:wC-%N`dxG;^pXX:wS-%N`ǫW`L~x`Z;^p@@:wS-<%N`ګ``;^p@@:wC->%N``d<^pAA:wC-%N`3dxC^pXX:wS-%N``W`Lx`C^p@@:wS-<%N`~``D^p@@:wC->%N``d6E^pAA:wC-%N`dxL^pXX:wS-%N`1W`Lx`L^p@@:wS-<%N`E``L^p@@:wC->%N`c`d#N^pAA:wC-%N`dxU^pXX:wS-%N`W`Mx`U^p@@:wS-<%N` ``U^p@@:wC->%N`8`dOW^pAA:wC-%N`dxY^^pXX:wS-%N`W`Mx`k^^p@@@:wS-<%N```^^p@@:wC->%N``d_^pAA@:wC-%N`Odxf^pXX@:wS-%N`W`M x`f^p@@:wS-<%N`!``f^p@@@:wC->%N`~`dh^pAA:wC-%N`dxno^pXX:wS-%N`W`M0x`o^p@@:wS-<%N```o^p@@:wC->%N`*`dp^pAA:wC-%N`Ndx#x^pXX:wS-%N`W`M@x`1x^p@@:wS-<%N```Yx^p@@:wC->%N``dky^pAA:wC-%N`dx^pXX:wS-%N`jW`MPx`^p@@:wS-<%N`x``!^p@@:wC->%N``d^^pAA:wC-%N`dx^pXX:wS-%N`2W`M`x`‰^p@@:wS-<%N`B``%^p@@:wC->%N``dۊ^pAA:wC-%N`[dx=^pXX:wS-%N`W`Mpx`M^p@@@:wS-<%N```n^p@@:wC->%N``d^pAA@:wC-%N`dx^pXX@:wS-%N`l W`M/x`^p@@:wS-<%N`z ``^p@@@:wC->%N` `dP^pAA:wC-%N` dx^pXX:wS-%N`W`M?x`^p@@:wS-<%N`)``^p@@:wC->%N``d^pAA:wC-%N`dxg^pXX:wS-%N`W`MOx`v^p@@:wS-<%N```ˬ^p@@:wC->%N`K`dҭ^pAA:wC-%N`Rdx)^pXX:wS-%N`%W`M_x`5^p@@:wS-<%N`%``^p@@:wC->%N` &`dv^pAA:wC-%N`&dxս^pXX:wS-%N`U.W`Mox`^p@@:wS-<%N`b.``5^p@@:wC->%N`.`d?^pAA:wC-%N`/dx`^pXX:wS-%N`6W`Mx`m^p@@@:wS-<%N`6``^p@@:wC->%N`7`dc^pAA@:wC-%N`7dx^pXX@:wS-%N`?W`Mx`^p@@:wS-<%N`?``2^p@@@:wC->%N`?`d/^pAA:wC-%N`@dx^pXX:wS-%N`1HW`Mx`^p@@:wS-<%N`>H``^p@@:wC->%N`H`d^pAA:wC-%N`Idx^pXX%N`P`du^pAA@%N`Y`dP^pAA%N`f`dt^pAA:wC-%N`gdx^pXX:wS-%N`/oW`N0x`^p@@:wS-<%N`Go``^p@@:wC->%N`qo`d _pAA:wC-%N`pdxv_pXX:wS-%N`wW`N@x`_p@@:wS-<%N`x``_p@@:wC->%N`%x`d_pAA:wC-%N`Pydx"_pXX:wS-%N`W`NPx`;_p@@@:wS-<%N```i_p@@:wC->%N``d_pAA@:wC-%N` dx_pXX@:wS-%N`qW`N`x`_p@@:wS-<%N```$_p@@@:wC->%N``dm_pAA:wC-%N`dx!_pXX:wS-%N`<W`Np x`!_p@@:wS-<%N`K``!_p@@:wC->%N`s`d"#_pAA:wC-%N`dx[*_pXX:wS-%N`ۚW`N0x`k*_p@@:wS-<%N```*_p@@:wC->%N``d+_pAA:wC-%N`:dx"3_pXX:wS-%N`W`N@x`23_p@@:wS-<%N```\3_p@@:wC->%N`ܣ`d4_pAA:wC-%N` dx;_pXX:wS-%N`sW`NPx`<_p@@:wS-<%N```,<_p@@:wC->%N``dH=_pAA:wC-%N`ȭdxD_pXX:wS-%N`7W`N`x`D_p@@@:wS-<%N`E``D_p@@:wC->%N`m`d,F_pAA@:wC-%N`dxM_pXX@:wS-%N`W`Npx`!M_p@@:wS-<%N```yM_p@@@:wC->%N``dHN_pAA:wC-%N`ȾdxU_pXX:wS-%N`5W`Nx`U_p@@:wS-<%N`C``U_p@@:wC->%N`d`dV_pAA:wC-%N`qdx^^_pXX:wS-%N`W`Nx`l^_p@@:wS-<%N```^_p@@:wC->%N``d__pAA:wC-%N` dxg_pXX:wS-%N`W`Nx`g_p@@:wS-<%N```?g_p@@:wC->%N``dKh_pAA:wC-%N`dxo_pXX:wS-%N`;W`Ox`o_p@@:wS-<%N`J``o_p@@:wC->%N`p`dq_pAA:wC-%N`dx/x_pXX:wS-%N`W`Ox`Bx_p@@@:wS-<%N```dx_p@@:wC->%N``dJy_pAA@:wC-%N`dx_pXX@:wS-%N`W`O x`_p@@:wS-<%N`%``ʀ_p@@@:wC->%N`J`d _pAA:wC-%N`dx6_pXX:wS-%N`W`O0x`E_p@@:wS-<%N```m_p@@:wC->%N``d_pAA:wC-%N`dx_pXX:wS-%N`W`O@x`_p@@:wS-<%N```=_p@@:wC->%N``dm_pAA:wC-%N`dx_pXX:wS-%N`{W`OPx`_p@@:wS-<%N```._p@@:wC->%N``dN_pAA:wC-%N`dx_pXX:wS-%N` W`O`x`_p@@:wS-<%N`- ``ί_p@@:wC->%N`N `d_pAA:wC-%N`4!dx _pXX:wS-%N`(W`Op!x`_p@@@:wS-<%N`(``=_p@@:wC->%N`(`dY_pAA@:wC-%N`)dx_pXX@:wS-%N`#1W`O1x`_p@@:wS-<%N`11``_p@@@:wC->%N`W1`d _pAA:wC-%N`2dxq_pXX:wS-%N`9W`OAx`_p@@:wS-<%N`9``_p@@:wC->%N`$:`d_pAA:wC-%N`q;dx:_pXX:wS-%N`BW`OQx`H_p@@:wS-<%N`B``n_p@@:wC->%N`B`d_pAA:wC-%N`"Ddx_pXX:wS-%N`FKW`Oax`_p@@:wS-<%N`TK``_p@@:wC->%N`|K`d_pAA:wC-%N`vLdx_pXX:wS-%N`TW`Oqx`_p@@:wS-<%N`T``_p@@:wC->%N`7T`d_pAA:wC-%N`&UdxF_pXX:wS-%N`\W`Ox`T_p@@@:wS-<%N`\``z_p@@:wC->%N`\`d_pAA@:wC-%N`.^dx_pXX@:wS-%N`@eW`Ox`_p@@:wS-<%N`Me``_p@@@:wC->%N`e`d)_pAA:wC-%N`fdx_pXX:wS-%N`nW`Ox`_p@@:wS-<%N`n``_p@@:wC->%N`kn`d`pAA:wC-%N`dx`pXX:wS-%N` W`Px``p@@:wS-<%N`1```p@@:wC->%N`W`d!`pAA:wC-%N`dx(`pXX:wS-%N`W`Px`(`p@@:wS-<%N```s(`p@@:wC->%N``dv)`pAA:wC-%N`dx0`pXX:wS-%N`WW`P x`0`p@@:wS-<%N`d``=1`p@@:wC->%N``d:2`pAA:wC-%N`dx9`pXX:wS-%N` W`P0x`9`p@@@:wS-<%N```9`p@@:wC->%N`l`d:`pAA@:wC-%N`edxKB`pXX@:wS-%N`˲W`P@x`XB`p@@:wS-<%N`ز``xB`p@@@:wC->%N``dC`pAA:wC-%N`dxK`pXX:wS-%N`W`PPx`K`p@@:wS-<%N```vK`p@@:wC->%N``dUL`pAA:wC-%N`ռdxS`pXX:wS-%N`DW`P`x`S`p@@:wS-<%N`P``"T`p@@:wC->%N``d'U`pAA:wC-%N`dx\`pXX:wS-%N`W`Pp"x`\`p@@:wS-<%N```\`p@@:wC->%N`k`d]`pAA:wC-%N`XdxEe`pXX:wS-%N`W`P2x`Re`p@@:wS-<%N```e`p@@:wC->%N`&`df`pAA:wC-%N`$dxn`pXX:wS-%N`W`PBx`n`p@@@:wS-<%N```n`p@@:wC->%N``dto`pAA@:wC-%N`dx]v`pXX@:wS-%N`W`PRx`jv`p@@:wS-<%N```v`p@@@:wC->%N`>`dw`pAA:wC-%N`'dx.`pXX:wS-%N`W`Pbx`=`p@@:wS-<%N````p@@:wC->%N``dz`pAA:wC-%N`dxχ`pXX:wS-%N`OW`Prx`ۇ`p@@:wS-<%N`[``.`p@@:wC->%N``d`pAA:wC-%N`dx`pXX:wS-%N`W`Px``p@@:wS-<%N` ```p@@:wC->%N`,`d͑`pAA:wC-%N`Mdx2`pXX:wS-%N` W`Px`>`p@@:wS-<%N` ```p@@:wC->%N` `d~`pAA:wC-%N` dx`pXX:wS-%N`2W`Px``p@@@:wS-<%N`>```p@@:wC->%N``d`pAA@:wC-%N`}dxQ`pXX@:wS-%N`W`Qx`]`p@@:wS-<%N````p@@@:wC->%N`4`d`pAA:wC-%N`dx`pXX:wS-%N`#W`Qx``p@@:wS-<%N`#``o`p@@:wC->%N`#`du`pAA:wC-%N`$dx`pXX:wS-%N`,W`Q x``p@@:wS-<%N`+,```p@@:wC->%N`,`d`pAA:wC-%N`x-dx`pXX:wS-%N`4W`Q0x``p@@:wS-<%N`4``q`p@@:wC->%N`4`dn`pAA:wC-%N`5dx`pXX:wS-%N`<=W`Q@x``p@@:wS-<%N`J=```p@@:wC->%N`=`d`pAA:wC-%N`>dx`pXX:wS-%N`5SW`QPx``p@@@:wS-<%N`BS```p@@:wC->%N`S`d!`pAA@:wC-%N`Tdxo`pXX@:wS-%N`[W`Q`x`{`p@@:wS-<%N`[```p@@@:wC->%N`O\`d`pAA:wC-%N`A]dx`pXX:wS-%N`wdW`Qp#x``p@@:wS-<%N`d``V`p@@:wC->%N`d`dN`pAA:wC-%N`edx`pXX:wS-%N`4mW`Q3x``p@@:wS-<%N`@m```p@@:wC->%N`m`d`pAA:wC-%N`gndxlapXX:wS-%N`uW`QCx`xap@@:wS-<%N`u``ap@@:wC->%N`4v`dapAA:wC-%N`wdx apXX:wS-%N`J~W`QSx` ap@@:wS-<%N`Z~`` ap@@:wC->%N`{~`dapAA:wC-%N`}dxapXX:wS-%N`UW`Qcx`ap@@@:wS-<%N`]``ap@@:wC->%N`}`d1apAA@:wC-%N`dxWapXX@:wS-%N`׎W`Qsx`bap@@:wS-<%N```ap@@@:wC->%N`>`dapAA:wC-%N`dx'apXX:wS-%N`W`Qx`'ap@@:wS-<%N```f'ap@@:wC->%N``dc(apAA:wC-%N`dx/apXX%N`o`d0apAA%N``d=apAA%N`ٵ`dKFapAA:wC-%N`˶dxfMapXX:wS-%N`W`Rx`sMap@@:wS-<%N```Map@@:wC->%N`J`dNapAA:wC-%N`Ldx*VapXX:wS-%N`W`R x`7Vap@@:wS-<%N```pVap@@:wC->%N``dWapAA:wC-%N`dx^apXX:wS-%N`qW`R0x`^ap@@@:wS-<%N`~``[_ap@@:wC->%N``dQ`apAA@:wC-%N`dx}gapXX@:wS-%N`W`R@x`gap@@:wS-<%N` ``hap@@@:wC->%N``diapAA:wC-%N`dxpapXX:wS-%N`W`RPx`%pap@@:wS-<%N```Epap@@:wC->%N``dDqapAA:wC-%N`dxxapXX:wS-%N`*W`R`x`xap@@:wS-<%N`<``xap@@:wC->%N`\`dyapAA:wC-%N`IdxapXX:wS-%N`{W`Rp$x` ap@@:wS-<%N```-ap@@:wC->%N``dapAA:wC-%N`dxZapXX:wS-%N`W`R4x`cap@@:wS-<%N```ap@@:wC->%N``dapAA:wC-%N`dxapXX:wS-%N`W`RDx`/ap@@@:wS-<%N```Vap@@:wC->%N``dapAA@:wC-%N`dxqapXX@:wS-%N` W`RTx`ap@@:wS-<%N` ``՚ap@@@:wC->%N`U `dapAA:wC-%N`f dxapXX:wS-%N`W`Rdx`-ap@@:wS-<%N```ap@@:wC->%N``dzapAA:wC-%N`dxǫapXX:wS-%N`GW`Rtx`ԫap@@:wS-<%N`T``ap@@:wC->%N``d)apAA:wC-%N`dxapXX:wS-%N` %W`Rx`ap@@:wS-<%N`%``ap@@:wC->%N`o%`dapAA:wC-%N`u&dx;apXX:wS-%N`-W`Rx`Jap@@:wS-<%N`-``rap@@:wC->%N`-`dapAA:wC-%N`,/dxapXX:wS-%N`6W`Rx`ap@@@:wS-<%N`6``9ap@@:wC->%N`6`dgapAA@:wC-%N`7dxapXX@:wS-%N`?W`Sx`ap@@:wS-<%N`,?``ap@@@:wC->%N`O?`dapAA:wC-%N`@dx*apXX:wS-%N`GW`Sx`8ap@@:wS-<%N`G``ap@@:wC->%N` H`drapAA:wC-%N`HdxapXX:wS-%N`zPW`S x`ap@@:wS-<%N`P``1ap@@:wC->%N`P`dapAA:wC-%N`RdxapXX:wS-%N`AYW`S0x`ap@@:wS-<%N`QY``ap@@:wC->%N`nY`dapAA:wC-%N`wZdxapXX:wS-%N`bW`S@x`ap@@:wS-<%N`b``ap@@:wC->%N`ab`dapAA:wC-%N`PcdxIapXX:wS-%N`jW`SPx`Wap@@@:wS-<%N`j``zap@@:wC->%N`j`dapAA@:wC-%N` ldxbpXX@:wS-%N`xsW`S`x`bp@@:wS-<%N`s``.bp@@@:wC->%N`s`dVbpAA:wC-%N`tdx bpXX:wS-%N`4|W`Sp%x` bp@@:wS-<%N`A|`` bp@@:wC->%N`|`d bpAA:wC-%N`}dxGbpXX:wS-%N`DŽW`S5x`Tbp@@:wS-<%N`Ԅ``bp@@:wC->%N`*`dbpAA:wC-%N`0dxbpXX:wS-%N`%W`SEx`bp@@:wS-<%N`3`` bp@@:wC->%N``dbpAA:wC-%N`dx`%bpXX:wS-%N`W`SUx`n%bp@@:wS-<%N```%bp@@:wC->%N``d&bpAA:wC-%N`dx$.bpXX:wS-%N`W`Sex`2.bp@@@:wS-<%N```X.bp@@:wC->%N`؞`d/bpAA@:wC-%N`.dx7bpXX@:wS-%N`W`Sux`7bp@@:wS-<%N```b7bp@@@:wC->%N``d8bpAA:wC-%N`dx?bpXX:wS-%N`;W`Sx`?bp@@:wS-<%N`K``?bp@@:wC->%N`n`d AbpAA:wC-%N`dxKHbpXX:wS-%N`˸W`Sx`[Hbp@@:wS-<%N`۸``~Hbp@@:wC->%N``dIbpAA:wC-%N`dxQbpXX:wS-%N`W`Sx`Qbp@@:wS-<%N```FQbp@@:wC->%N``doRbpAA:wC-%N`dxYbpXX:wS-%N`WW`Tx`Ybp@@:wS-<%N`e``Zbp@@:wC->%N``d<[bpAA:wC-%N`dxbbpXX:wS-%N`W`Tx`bbp@@@:wS-<%N`%``bbp@@:wC->%N`z`dcbpAA@:wC-%N`|dx,kbpXX@:wS-%N`W`T x`=kbp@@:wS-<%N```ckbp@@@:wC->%N``d}lbpAA:wC-%N`dxsbpXX:wS-%N``W`T0x`sbp@@:wS-<%N`m``Dtbp@@:wC->%N``dBubpAA:wC-%N`dx$|bpXX:wS-%N`W`T@x`/|bp@@:wS-<%N```M|bp@@:wC->%N``d}bpAA:wC-%N`dxbpXX:wS-%N`kW`TPx`bp@@:wS-<%N`|``!bp@@:wC->%N``dobpAA:wC-%N`dxbpXX:wS-%N`W`T`x`bp@@:wS-<%N```{bp@@:wC->%N``dbpAA:wC-%N`dxbpXX:wS-%N`@W`Tp&x`ҕbp@@@:wS-<%N`R``Hbp@@:wC->%N``dNbpAA@:wC-%N`dxKbpXX@:wS-%N`W`T6x`Wbp@@:wS-<%N```bp@@@:wC->%N`3`dbpAA:wC-%N`-dx bpXX:wS-%N`W`TFx`-bp@@:wS-<%N```bp@@:wC->%N``dŨbpAA:wC-%N`EdxݯbpXX:wS-%N`] W`TVx`bp@@:wS-<%N`j ``Dbp@@:wC->%N` `dWbpAA:wC-%N`!dxjbpXX:wS-%N`(W`Tfx`vbp@@:wS-<%N`(``bp@@:wC->%N`=)`dbpAA:wC-%N`*dx1bpXX:wS-%N`1W`Tvx`>bp@@:wS-<%N`1``bp@@:wC->%N`2`d~bpAA:wC-%N`2dxbpXX:wS-%N` IW`Tx`bp@@@:wS-<%N`I``bp@@:wC->%N`iI`dbpAA@:wC-%N`nJdxQbpXX@:wS-%N`QW`Tx`^bp@@:wS-<%N`Q``bp@@@:wC->%N`R`ddbpAA:wC-%N`RdxbpXX:wS-%N`ZW`Tx`bp@@:wS-<%N`Z``ibp@@:wC->%N`Z`dEbpAA:wC-%N`[dxbpXX:wS-%N`cW`Ux`bp@@:wS-<%N`$c`` bp@@:wC->%N`c`dbpAA:wC-%N`ddxIbpXX:wS-%N`kW`Ux`Xbp@@:wS-<%N`k``{bp@@:wC->%N`k`dbpAA:wC-%N`)mdxcpXX:wS-%N`tW`U x`cp@@:wS-<%N`t``qcp@@:wC->%N`t`dtcpAA:wC-%N`udx cpXX:wS-%N`G}W`U0x` cp@@@:wS-<%N`U}`` cp@@:wC->%N`y}`dUcpAA@:wC-%N`~dxcpXX@:wS-%N`W`U@x`cp@@:wS-<%N```cp@@@:wC->%N`a`dcpAA:wC-%N`udxEcpXX:wS-%N`ŎW`UPx`Qcp@@:wS-<%N`ю``cp@@:wC->%N`$`dcpAA:wC-%N`dx&cpXX:wS-%N`xW`U`x`'cp@@:wS-<%N```?'cp@@:wC->%N``dQ(cpAA:wC-%N`јdx/cpXX:wS-%N`1W`Up'x`/cp@@:wS-<%N`=``/cp@@:wC->%N`\`d0cpAA:wC-%N`Kdxn8cpXX:wS-%N`W`U7x`y8cp@@:wS-<%N```8cp@@:wC->%N`I`d9cpAA:wC-%N`cdx@cpXX:wS-%N`vW`UGx`Acp@@@:wS-<%N```SAcp@@:wC->%N`ӱ`dUBcpAA@:wC-%N`ղdxIcpXX@:wS-%N`*W`UWx`Icp@@:wS-<%N`5``Jcp@@@:wC->%N``dJcpAA:wC-%N`vdx5RcpXX:wS-%N`W`Ugx`CRcp@@:wS-<%N```Rcp@@:wC->%N``dScpAA:wC-%N` dxZcpXX:wS-%N`wW`Uwx`[cp@@:wS-<%N```W[cp@@:wC->%N``dy\cpAA:wC-%N`d`ccp@@:wS-<%N`-`xhcpXX:wS-%N`W`Ux`thcp@@:wC->%N``d}icpAA:wC-%N`dxpcpXX:wS-%N`LW`Ux`pcp@@:wS-<%N`_``qcp@@:wC->%N``d#rcpAA:wC-%N`dxycpXX:wS-%N`W`Ux`ycp@@@:wS-<%N```ycp@@:wC->%N`r`dzcpAA@:wC-%N`Qdx?cpXX@:wS-%N`W`Vx`Ncp@@:wS-<%N```cp@@@:wC->%N`$`dcpAA:wC-%N`dxcpXX:wS-%N`dW`Vx`cp@@:wS-<%N`s``ncp@@:wC->%N``d9cpAA:wC-%N`dxycpXX:wS-%N` W`V x`cp@@:wS-<%N` ``cp@@:wC->%N`0 `dԔcpAA:wC-%N`T dxOcpXX:wS-%N` W`V0x`]cp@@:wS-<%N` ``cp@@:wC->%N` `dcpAA:wC-%N`0 dxcpXX:wS-%N` W`V@x`cp@@:wS-<%N` ``ecp@@:wC->%N` `dcpAA:wC-%N`' dxcpXX:wS-%N`: W`VPx`ϭcp@@@:wS-<%N`O ``cp@@:wC->%N`p `dcpAA@:wC-%N` dx%N`(' `dcpAA:wC-%N`( dxcpXX:wS-%N`~/ W`Vp(x`cp@@:wS-<%N`/ ``6cp@@:wC->%N`/ `dGcpAA:wC-%N`0 dxcpXX:wS-%N`;8 W`V8x`cp@@:wS-<%N`J8 ``cp@@:wC->%N`r8 `dcpAA:wC-%N`9 dxkcpXX:wS-%N`@ W`VHx`ycp@@:wS-<%N`@ ``cp@@:wC->%N`OA `dcpAA:wC-%N`YB dx4cpXX:wS-%N`I W`VXx`Bcp@@:wS-<%N`I ``gcp@@:wC->%N`I `dcpAA:wC-%N`"K dxcpXX:wS-%N`CR W`Vhx`cp@@@:wS-<%N`RR ``cp@@:wC->%N`zR `d%cpAA@:wC-%N`S dx[cpXX@:wS-%N`Z W`Vxx`hcp@@:wS-<%N`Z ``cp@@@:wC->%N`D[ `dcpAA:wC-%N`7\ dx#cpXX:wS-%N`c W`Vx`1cp@@:wS-<%N`c ``Ycp@@:wC->%N`c `drcpAA:wC-%N`d dxcpXX:wS-%N`Il W`Vx`cp@@:wS-<%N`Zl ``cp@@:wC->%N`l `d5cpAA:wC-%N`m dxdpXX:wS-%N`Nt W`Vx`dp@@:wS-<%N`]t ``dp@@:wC->%N`t `ddpAA:wC-%N`u dx\ dpXX:wS-%N`| W`Wx`j dp@@:wS-<%N`| `` dp@@:wC->%N`A} `d dpAA:wC-%N`C~ dx%dpXX:wS-%N` W`Wx`6dp@@@:wS-<%N` ``]dp@@:wC->%N`݅ `dwdpAA@:wC-%N` dxdpXX@:wS-%N`Y W`W x`dp@@:wS-<%N`f ``>dp@@@:wC->%N` `d;dpAA:wC-%N` dx&dpXX:wS-%N`% W`W0x`&dp@@:wS-<%N`4 ``&dp@@:wC->%N`Y `d-(dpAA:wC-%N` dxI/dpXX:wS-%N`ɟ W`W@x`V/dp@@:wS-<%N`֟ ``/dp@@:wC->%N`1 `d0dpAA:wC-%N` dx%8dpXX:wS-%N` W`WP x`48dp@@:wS-<%N` ``8dp@@:wC->%N` `dm9dpAA:wC-%N` dx@dpXX:wS-%N`J W`W`x`@dp@@:wS-<%N`W ``-Adp@@:wC->%N` `d0BdpAA:wC-%N` dx{IdpXX:wS-%N` W`Wp)x`Idp@@@:wS-<%N` ``Idp@@:wC->%N`( `dJdpAA@:wC-%N`j dx5RdpXX@:wS-%N` W`W9x`ARdp@@:wS-<%N` ``Rdp@@@:wC->%N` `dSdpAA:wC-%N` dx[dpXX:wS-%N` W`WIx`p@@:wS-<%N` ``u[dp@@:wC->%N` `d[\dpAA:wC-%N` dxcdpXX:wS-%N`\ W`WYx`cdp@@:wS-<%N`i ``@ddp@@:wC->%N` `d3edpAA:wC-%N` dxldpXX:wS-%N`" W`Wix`ldp@@:wS-<%N`@ ``ldp@@:wC->%N`W `dndpAA:wC-%N` dx`udpXX:wS-%N` W`Wyx`wudp@@:wS-<%N` ``udp@@:wC->%N`% `dvdpAA:wC-%N`\ dx}dpXX:wS-%N`h W`Wx`}dp@@@:wS-<%N`w ``~dp@@:wC->%N` `d=dpAA@:wC-%N` dxdpXX@:wS-%N`% W`Wx`̆dp@@:wS-<%N`L ``Նdp@@@:wC->%N`U `ddpAA:wC-%N`g dxOdpXX:wS-%N` W`Wx`]dp@@:wS-<%N` ``dp@@:wC->%N`) `ddpAA:wC-%N`g dxdpXX@mzS-%N`6! W`Xx`ǰdp@@@mzS-<%N`G! `` dp@@@mzC->%N`! `ddpAA@mzC-%N`" dxidpXX@mzS-%N`) W`Xx`zdp@@@mzS-<%N`) ``dp@@@mzC->%N` * `ddpAA@mzC-%N`2+ dx(dpXXmzS-%N`2 W`X x`8dp@@mzS-<%N`2 ``xdp@@mzC->%N`2 `dfdpAAmzC-%N`3 dxdpXXmzS-%N``; W`X0x`dp@@mzS-<%N`q; ``0dp@@mzC->%N`; `dVdpAAmzC-%N`< dxdpXXmzS-%N`D W`X@x`dp@@mzS-<%N`)D ``dp@@mzC->%N`iD `ddpAAmzC-%N`[E dxkdpXXMwS-%N`L W`XP x`dp@@@MwS-<%N`M ``dp@@MwC->%N`CM `ddpAA@MwC-%N`N dx dpXXMwS-%N`U W`X`x`dp@@@MwS-<%N`U ``;dp@@MwC->%N`U `d%N`^ `dKdpAAMwC-%N`_ dxXdpXX@MwS-%N`f W`X:x`edp@@MwS-<%N`f ``dp@@@MwC->%N`'g `ddpAAMwC-%N`Ah dx&dpXXMwS-%N`o W`XJx`4dp@@MwS-<%N`o ``dp@@MwC->%N`xp `depAAMwC-%N`sq dxepXX@MwS-%N`ax W`XZx`ep@@MwS-<%N`ox ``1ep@@@MwC->%N`x `d? epAAMwC-%N`y dxepXXMwS-%N`& W`Xjx`ep@@@MwS-<%N`3 ``ep@@MwC->%N`v `depAA@MwC-%N` dxyepXXMwS-%N` W`Xzx`ep@@@MwS-<%N` ``ep@@MwC->%N`I `depAA@MwC-%N`D dx;"epXXMwS-%N` W`Xx`I"ep@@@MwS-<%N`ɒ ``"ep@@MwC->%N` `d#epAA@MwC-%N` dx+epXXMwS-%N` W`Xx` +ep@@@MwS-<%N` ``P+ep@@MwC->%N`Л `d\,epAA@MwC-%N`ܜ dx3epXXmzS-%N` W`Xx`3ep@@mzS-<%N`= ``3ep@@mzC->%N`` `d4epAAmzC-%N`n dx%JepXXmzS-%N` W`Yx`6Jep@@mzS-<%N` ``vJep@@mzC->%N` `dKepAAmzC-%N` dxRepXXmzS-%N`] W`Yx`Rep@@mzS-<%N`t ``.Sep@@mzC->%N` `dWTepAAmzC-%N` dxi[epXX@mzS-%N` W`Y x`[ep@@@mzS-<%N` ``[ep@@@mzC->%N`< `d\epAA@mzC-%N`X dxcepXX@mzS-%N`i W`Y0x`cep@@@mzS-<%N`z ``;dep@@@mzC->%N` `dFeepAA@mzC-%N` dxlepXXmzS-%N` W`Y@x`lep@@mzS-<%N` ``lep@@mzC->%N`/ `dmepAAmzC-%N`> dx>uepXXMwS-%N` W`YP x`Nuep@@MwS-<%N` ``uep@@MwC->%N` `dvepAAMwC-%N`& dx}epXX@MwS-%N`u W`Y`x`~ep@@MwS-<%N` ``C~ep@@@MwC->%N` `dVepAAMwC-%N` dx̆epXXMwS-%N`L W`Yp+x`چep@@MwS-<%N`Z ``9ep@@MwC->%N` `d epAAMwC-%N` dxepXX@MwS-%N`5 W`Y;x`ďep@@MwS-<%N`D ``ep@@@MwC->%N` `depAAMwC-%N` dxrepXXMwS-%N` W`YKx`ep@@@MwS-<%N` ``Řep@@MwC->%N`E `dęepAA@MwC-%N`D dxBepXXMwS-%N` W`Y[x`Oep@@@MwS-<%N` ``ep@@MwC->%N` `d̢epAA@MwC-%N`L d`ep@@mzS-<%N`s `xXepXXmzS-%N` W`Ykx`ep@@mzC->%N` `depAAmzC-%N`8 dxepXXmzS-%N`' W`Y{x`ep@@mzS-<%N`' ``iep@@mzC->%N`' `dUepAAmzC-%N`( dxԿepXXmzS-%N`T0 W`Yx`ep@@mzS-<%N`d0 ``/ep@@mzC->%N`0 `depAAmzC-%N`1 dxepXXmzS-%N`9 W`Yx`ep@@mzS-<%N`%9 ``ep@@mzC->%N`f9 `depAAmzC-%N`: dxepXXmzS-%N`A W`Yx`-ep@@mzS-<%N`A ``fep@@mzC->%N`A `dcepAAmzC-%N`B dxepXX@mzS-%N`sJ W`Zx`ep@@@mzS-<%N`J ``Qep@@@mzC->%N`J `dQepAA@mzC-%N`K dxepXX@mzS-%N`+S W`Zx`ep@@@mzS-<%N`:S ``ep@@@mzC->%N`S `d epAA@mzC-%N`T dx?epXXMwS-%N`[ W`Z x`Qep@@@MwS-<%N`[ ``ep@@MwC->%N`\ `depAA@MwC-%N`<] dxepXXMwS-%N`zd W`Z0x`ep@@@MwS-<%N`d ``*ep@@MwC->%N`d `dDepAA@MwC-%N`e dxepXXmzS-%N`4m W`Z@x`ep@@mzS-<%N`Fm `` ep@@mzC->%N`m `depAAmzC-%N`n dxffpXXmzS-%N`u W`ZP x`yfp@@mzS-<%N`u ``fp@@mzC->%N`Bv `dfpAAmzC-%N`3w dx&fpXXmzS-%N`~ W`Z`x`6fp@@mzS-<%N`~ ``{fp@@mzC->%N`~ `dgfpAAmzC-%N` dxfpXXmzS-%N`= W`Zp,x`fp@@mzS-<%N`W ``fp@@mzC->%N` `d&fpAAmzC-%N` dxfpXXmzS-%N` W`Z<x`*fp@@mzS-<%N` ``jfp@@mzC->%N` `d fpAAmzC-%N` dx'fpXXmzS-%N`i W`ZLx`'fp@@mzS-<%N`{ ``D(fp@@mzC->%N`Ę `d<)fpAAmzC-%N` dx0fpXX@mzS-%N`/ W`Z\x`0fp@@@mzS-<%N`? ``0fp@@@mzC->%N`[ `d2fpAA@mzC-%N` dxs9fpXX@mzS-%N` W`Zlx`9fp@@@mzS-<%N` ``9fp@@@mzC->%N`K `d:fpAA@mzC-%N`U dx.BfpXXmzS-%N` W`Z|x`>Bfp@@mzS-<%N` ``Bfp@@mzC->%N` `dCfpAAmzC-%N` dxKfpXXmzS-%N` W`Zx`+Kfp@@mzS-<%N` ``oKfp@@mzC->%N` `dgLfpAAmzC-%N` dxSfpXXmzS-%N`n W`Zx`Tfp@@mzS-<%N` ``LTfp@@mzC->%N` `dOUfpAAmzC-%N` dx\fpXXmzS-%N`= W`Zx`\fp@@mzS-<%N`U ``\fp@@mzC->%N`q `dO^fpAAmzC-%N` dxefpXXmzS-%N` W`[x`efp@@mzS-<%N`$ ``efp@@mzC->%N`n `dffpAAmzC-%N`b dx nfpXXmzS-%N` W`[x`)nfp@@mzS-<%N` ``=nfp@@mzC->%N` `dCofpAAmzC-%N` dx7fpXX@mzS-%N` W`[ x`Ufp@@@mzS-<%N` ``tfp@@@mzC->%N` `dfpAA@mzC-%N`( dx̌fpXX@mzS-%N`L W`[0x`fp@@@mzS-<%N`j ``fp@@@mzC->%N` `dfpAA@mzC-%N` dxOfpXXMwS-%N` W`[@x`dfp@@MwS-<%N` ``fp@@MwC->%N`, `dfpAAMwC-%N` dxfpXX%N` `dfpAA%N` `dfpAAmzC-%N`! dxĮfpXXmzS-%N`D W`[p-x`֮fp@@mzS-<%N`V ``fp@@mzC->%N`} `d fpAAmzC-%N` dx@fpXX%N`' `dfpAA%N`N0 `dfpAA%N`8 `d%N``A `dfpAA@%N`I `dTfpAAmzC-%N`J dxfpXXmzS-%N`5R W`[x`fp@@mzS-<%N`QR ``fp@@mzC->%N`mR `dfpAAmzC-%N`S dxfpXXmzS-%N`{Z W`[x`fp@@mzS-<%N`Z ``-fp@@mzC->%N`Z `d9fpAAmzC-%N`[ dxZfpXX@MwS-%N`b W`[x`sfp@@MwS-<%N`b ``fp@@@MwC->%N`c `d~fpAAMwC-%N`c dxfpXXMwS-%N`k W`\x`fp@@MwS-<%N`)k ``fp@@MwC->%N`k `dfpAAMwC-%N`l dxgpXX@MwS-%N`vs W`\x`gp@@MwS-<%N`s ``!gp@@@MwC->%N`s `dgpAAMwC-%N`t dxY gpXX8yS-%N`{ W`\ x`g gp@@8yS-<%N`{ `` gp@@8yC->%N`| `d gpAA8yC-%N`} dxgpXXmzS-%N`j W`\0x`gp@@@mzS-<%N`w ``:gp@@mzC->%N` `dgpAA@mzC-%N`' dxgpXX@mzS-%N` W`\@x`gp@@mzS-<%N`" ``gp@@@mzC->%N`a `d$gpAAmzC-%N` dxw%gpXXmzS-%N` W`\Px`~%gp@@@mzS-<%N` ``%gp@@mzC->%N`B `d&gpAA@mzC-%N`/ dx3.gpXX@mzS-%N` W`\`x`:.gp@@mzS-<%N` ``o.gp@@@mzC->%N` `d/gpAAmzC-%N`/ dx 7gpXX8yS-%N` W`\p.x`'7gp@@@8yS-<%N` ``e7gp@@8yC->%N` `d8gpAA@8yC-%N` dx@gpXX8yS-%N` W`\>x`@gp@@@8yS-<%N` ``c@gp@@8yC->%N` `dAgpAA@8yC-%N` dxIgpXX8yS-%N` W`\Nx` Igp@@@8yS-<%N` ``JIgp@@8yC->%N`ʹ `d1JgpAA@8yC-%N` dxQgpXX8yS-%N`F W`\^x`Qgp@@8yS-<%N`N ``Rgp@@8yC->%N` `dKSgpAA8yC-%N` dxhZgpXX8yS-%N` W`\nx`oZgp@@8yS-<%N` ``Zgp@@8yC->%N`t `d\gpAA8yC-%N` dxcgpXX@8yS-%N` W`\~x`cgp@@8yS-<%N` ``+cgp@@@8yC->%N` `d`dgpAA8yC-%N` dx~kgpXX@8yS-%N` W`\x`kgp@@8yS-<%N` ``#lgp@@@8yC->%N` `d?mgpAA8yC-%N` dxctgpXX@8yS-%N` W`\x`jtgp@@8yS-<%N` ``tgp@@@8yC->%N`% `dugpAA8yC-%N`o dxO}gpXX8yS-%N` W`\x`W}gp@@8yS-<%N` ``|}gp@@8yC->%N` `d~gpAA8yC-%N`J dxWgpXX8yS-%N` W`]x`_gp@@8yS-<%N` ``gp@@8yC->%N` `dgpAA8yC-%N`j dx@gpXX8yS-%N` W`]x`Ggp@@@8yS-<%N` ``gp@@8yC->%N` `dgpAA@8yC-%N` dxٗgpXX8yS-%N`Y W`] x`gp@@@8yS-<%N`` ``gp@@8yC->%N` `dZgpAA@8yC-%N` dxgpXX8yS-%N` W`]0x`gp@@@8yS-<%N`' ``Ƞgp@@8yC->%N`H `d gpAA@8yC-%N` dx\gpXX8yS-%N` W`]@x`dgp@@8yS-<%N` ``gp@@8yC->%N`! `dgpAA8yC-%N`k dx%N` # `dgpAA8yC-%N`$ dxgpXX@8yS-%N`:+ W`]`x`ĺgp@@8yS-<%N`D+ ``gp@@@8yC->%N`+ `dgpAA8yC-%N`, dxhgpXXmzS-%N`3 W`]p/x`vgp@@@mzS-<%N`3 ``gp@@mzC->%N`@4 `dgpAA@mzC-%N`75 dx(gpXX@mzS-%N`< W`]?x`5gp@@@mzS-<%N`< ``vgp@@@mzC->%N`< `dgpAA@mzC-%N`/> d`gp@@mzS-<%N`EE `xgpXXmzS-%N`hI W`]Ox`@gp@@mzC->%N`I `d&gpAAmzC-%N`J dxgpXXmzS-%N`-R W`]_x`gp@@mzS-<%N`%N`R `dgpAAmzC-%N`S dxogpXXmzS-%N`Z W`]ox`|gp@@mzS-<%N`Z ``gp@@mzC->%N`>[ `dgpAAmzC-%N`V\ dx gpXXmzS-%N`c W`]x`/gp@@mzS-<%N`c ``cgp@@mzC->%N`c `dgpAAmzC-%N`e dxgpXXmzS-%N`il W`]x`gp@@mzS-<%N`wl ``9gp@@mzC->%N`l `degpAAmzC-%N`m dxhpXXmzS-%N`)u W`]x`hp@@mzS-<%N`7u ``hp@@mzC->%N`u `d hpAAmzC-%N`v dxo hpXX@mzS-%N`} W`]x`| hp@@@mzS-<%N`} `` hp@@@mzC->%N`>~ `dhpAA@mzC-%N`Y dx2hpXX@mzS-%N` W`^x`Ahp@@@mzS-<%N` ``hp@@@mzC->%N` `dhpAA@mzC-%N` dxhpXXmzS-%N`c W`^x`hp@@mzS-<%N`s ``Lhp@@mzC->%N`̏ `dV hpAAmzC-%N`֐ dxt'hpXXmzS-%N` W`^ x`'hp@@mzS-<%N` ``(hp@@mzC->%N` `d)hpAAmzC-%N` dxA0hpXXmzS-%N` W`^0x`T0hp@@mzS-<%N`Ԡ ``0hp@@mzC->%N` `d1hpAAmzC-%N`, dx9hpXXmzS-%N` W`^@x`9hp@@mzS-<%N` ```9hp@@mzC->%N` `d:hpAAmzC-%N` dxAhpXXmzS-%N`P W`^Px`Ahp@@mzS-<%N`_ ``"Bhp@@mzC->%N` `d4ChpAAmzC-%N` dxJhpXXmzS-%N` W`^` x`Jhp@@mzS-<%N`" ``Jhp@@mzC->%N`i `dKhpAAmzC-%N`b dx^ShpXX@mzS-%N` W`^p0x`lShp@@@mzS-<%N` ``Shp@@@mzC->%N`1 `dThpAA@mzC-%N`C dx%\hpXX@mzS-%N` W`^@x`3\hp@@@mzS-<%N` ``v\hp@@@mzC->%N` `d]hpAA@mzC-%N`> dxdhpXXmzS-%N`j W`^Px`dhp@@mzS-<%N`x ``>ehp@@mzC->%N` `dVfhpAAmzC-%N` dxmhpXXmzS-%N`4 W`^`x`mhp@@mzS-<%N`B ``nhp@@mzC->%N` `dohpAAmzC-%N` dxvhpXXmzS-%N` W`^px`vhp@@mzS-<%N` ``vhp@@mzC->%N`[ `dwhpAAmzC-%N`Q dx>hpXXmzS-%N` W`^x`Lhp@@mzS-<%N` ``hp@@mzC->%N` `d߀hpAAmzC-%N`_ dx͇hpXXmzS-%N`M W`^x`܇hp@@mzS-<%N`\ ``hp@@mzC->%N` `d,hpAAmzC-%N` dxZhpXXmzS-%N`W`^x`ghp@@mzS-<%N```hp@@mzC->%N`(`dőhpAAmzC-%N`EdxhpXX@mzS-%N` W`^x`'hp@@@mzS-<%N` ``hhp@@@mzC->%N` `dhhpAA@mzC-%N` dxۡhpXX@mzS-%N`[W`_x`hp@@@mzS-<%N`h``-hp@@@mzC->%N``d'hpAA@mzC-%N`dxhpXXmzS-%N`"W`_x`hp@@mzS-<%N`/``hp@@mzC->%N`r`d"hpAAmzC-%N`dxshpXXmzS-%N`#W`_ x`hp@@mzS-<%N`$``³hp@@mzC->%N`B$`dдhpAAmzC-%N`P%dx-hpXXmzS-%N`,W`_0x`8hp@@mzS-<%N`,``xhp@@mzC->%N`,`d^hpAAmzC-%N`-dxhpXXmzS-%N`x5W`_@x`hp@@mzS-<%N`5``Fhp@@mzC->%N`5`dFhpAAmzC-%N`6dxhpXXmzS-%N`7>W`_Px`hp@@mzS-<%N`E>`` hp@@mzC->%N`>`dhpAAmzC-%N`?dxJhpXXmzS-%N`FW`_`!x`Vhp@@mzS-<%N`F``hp@@mzC->%N`G`dhpAAmzC-%N`5HdxhpXX@mzS-%N`OW`_p1x`hp@@@mzS-<%N`O``Nhp@@@mzC->%N`O`dhhpAA@mzC-%N`PdxhpXX@mzS-%N`@XW`_Ax`hp@@@mzS-<%N`aX``#hp@@@mzC->%N`X`d"hpAA@mzC-%N`YdxhpXXmzS-%N``W`_Qx`hp@@mzS-<%N` a``hp@@mzC->%N`La`d'hpAAmzC-%N`bdx;hpXXmzS-%N`iW`_ax`Ghp@@mzS-<%N`i``hp@@mzC->%N`j`dhpAAmzC-%N`*kdxipXXmzS-%N`;rW`_qx`ip@@mzS-<%N`Gr``ip@@mzC->%N`r`dipAAmzC-%N`sdxz ipXXmzS-%N`zW`_x` ip@@mzS-<%N`{`` ip@@mzC->%N`E{`d ipAAmzC-%N`8|dxipXXmzS-%N`cW`_x`ip@@mzS-<%N`q``ip@@mzC->%N``d/ipAAmzC-%N`dxfipXXmzS-%N`W`_x`tip@@mzS-<%N```ip@@mzC->%N`8`dipAAmzC-%N`Kdx*$ipXX@mzS-%N`W`_x`7$ip@@@mzS-<%N```%ip@@@mzC->%N``d'AipAA@mzC-%N`dxEipXX@mzS-%N`wW``x`Fip@@@mzS-<%N```PFip@@@mzC->%N`ж`dLGipAA@mzC-%N`̷dxNipXXmzS-%N`8W``x`Nip@@mzS-<%N`F``Oip@@mzC->%N``dPipAAmzC-%N`dxyWipXXmzS-%N`W`` x`Wip@@mzS-<%N```Wip@@mzC->%N`F`dXipAAmzC-%N`Wdx`ipXXmzS-%N`W``0x``ip@@mzS-<%N```Z`ip@@mzC->%N``dZaipAAmzC-%N`dxhipXXmzS-%N`GW``@x`hip@@mzS-<%N`S``iip@@mzC->%N``d*jipAAmzC-%N`dxqipXXmzS-%N` W``Px`qip@@mzS-<%N```qip@@mzC->%N`U`dsipAAmzC-%N`dx^zipXXmzS-%N`W```"x`izip@@mzS-<%N```zip@@mzC->%N`+`d{ipAAmzC-%N`Ddx$ipXX@mzS-%N`W``p2x`0ip@@@mzS-<%N```{ip@@@mzC->%N``dipAA@mzC-%N`dxipXX@mzS-%N``W``Bx`ip@@@mzS-<%N`~``;ip@@@mzC->%N``d;ipAA@mzC-%N`dxipXXmzS-%N`W``Rx`ip@@mzS-<%N`(``ip@@mzC->%N`f`dipAAmzC-%N`kdxRipXXmzS-%N` W``bx`]ip@@mzS-<%N` ``ip@@mzC->%N``dΞipAAmzC-%N`Ndx ipXXmzS-%N`W``rx`ip@@mzS-<%N```Vip@@mzC->%N``deipAAmzC-%N`dxˮipXXmzS-%N`KW``x`֮ip@@mzS-<%N`V``ip@@mzC->%N``d-ipAAmzC-%N` dxipXXmzS-%N`(W``x`ip@@mzS-<%N` (``̷ip@@mzC->%N`L(`dipAAmzC-%N`)dxCipXXmzS-%N`0W``x`Nip@@mzS-<%N`0``ip@@mzC->%N` 1`dipAAmzC-%N`C2dxipXX@mzS-%N`M9W``x`ip@@@mzS-<%N`Y9``ip@@@mzC->%N`9`d(ipAA@mzC-%N`:dxipXX@mzS-%N` BW`ax`ip@@@mzS-<%N`B``ip@@@mzC->%N``dipAA@mzC-%N`-dxOipXXmzS-%N`W`ax`[ip@@mzS-<%N```ip@@mzC->%N``dipAAmzC-%N` dxipXXmzS-%N`@W`a x` ip@@mzS-<%N`M``Mip@@mzC->%N``dWipAAmzC-%N`dxipXXmzS-%N`W`a0x`ip@@mzS-<%N```5ip@@mzC->%N`u`d%N`!#`dipAAmzC-%N`$dxcipXXmzS-%N`+W`aPx`qip@@mzS-<%N`+``ip@@mzC->%N`+`dipAAmzC-%N`-dx jpXXmzS-%N``4W`a`#x`+jp@@mzS-<%N`k4``ojp@@mzC->%N`4`d{jpAAmzC-%N`5d`jp@@@mzS-<%N`=`x:jpXX@mzS-%N`zAW`ap3x`hjp@@@mzC->%N`A`djpAA@mzC-%N`Bdx-jpXX@mzS-%N`mJW`aCx`:jp@@@mzS-<%N`zJ``]jp@@@mzC->%N`J`dgjpAA@mzC-%N`Kdx$jpXXmzS-%N`SW`aSx`$jp@@mzS-<%N`+S``/%jp@@mzC->%N`oS`d,&jpAAmzC-%N`lTdx-jpXXmzS-%N`[W`acx`-jp@@mzS-<%N`[``-jp@@mzC->%N`2\`d'/jpAAmzC-%N`g]dx26jpXXmzS-%N`rdW`asx`@6jp@@mzS-<%N`d``6jp@@mzC->%N`d`d7jpAAmzC-%N`edx>jpXXmzS-%N`mW`ax`>jp@@mzS-<%N`m``?jp@@mzC->%N`Wm`d'@jpAAmzC-%N`gndxGjpXXmzS-%N`uW`ax`Gjp@@mzS-<%N`u``Gjp@@mzC->%N`&v`dHjpAAmzC-%N`wdxcPjpXXmzS-%N`~W`ax`qPjp@@mzS-<%N`~``Pjp@@mzC->%N`~`dQjpAAmzC-%N`dx?YjpXX@mzS-%N`W`ax`NYjp@@@mzS-<%N```Yjp@@@mzC->%N`Շ`dZjpAA@mzC-%N`dxajpXX@mzS-%N`4W`bx`bjp@@@mzS-<%N`B``Hbjp@@@mzC->%N``dKcjpAA@mzC-%N`dxjjpXXmzS-%N`W`bx`jjp@@mzS-<%N```jjp@@mzC->%N`8`dljpAAmzC-%N`OdxssjpXXmzS-%N`W`b x`sjp@@mzS-<%N`¡``sjp@@mzC->%N``dtjpAAmzC-%N`!dx|jpXXmzS-%N`UW`b0x`(|jp@@mzS-<%N`h``j|jp@@mzC->%N``dt}jpAAmzC-%N`dxjpXXmzS-%N`ݲW`b@x`jp@@mzS-<%N```jp@@mzC->%N`/`djpAAmzC-%N`0dxhjpXXmzS-%N`W`bPx`vjp@@mzS-<%N```jp@@mzC->%N``dʎjpAAmzC-%N` dxEjpXXmzS-%N`W`b`$x`Sjp@@mzS-<%N```jp@@mzC->%N``djpAAmzC-%N`dxjpXX@mzS-%N`GW`bp4x`jp@@@mzS-<%N`W``]jp@@@mzC->%N``djjpAA@mzC-%N`dxjpXX@mzS-%N`W`bDx`§jp@@@mzS-<%N```jp@@@mzC->%N`E`djpAA@mzC-%N`ZdxujpXXmzS-%N`W`bTx`jp@@mzS-<%N```İjp@@mzC->%N``djpAAmzC-%N`dx-jpXXmzS-%N`mW`bdx`=jp@@mzS-<%N`}``~jp@@mzC->%N``dxjpAAmzC-%N`dxjpXXmzS-%N`W`btx`jp@@mzS-<%N` ``jp@@mzC->%N`T`djpAAmzC-%N`5dx}jpXXmzS-%N`W`bx`jp@@mzS-<%N```jp@@mzC->%N`"`djpAAmzC-%N`/dx=jpXXmzS-%N`}W`bx`Kjp@@mzS-<%N```jp@@mzC->%N``djpAAmzC-%N`dxjpXXmzS-%N`? W`bx` jp@@mzS-<%N`L ``Njp@@mzC->%N` `dMjpAAmzC-%N` dxjpXX@mzS-%N`W`bx`jp@@@mzS-<%N```jp@@@mzC->%N`T`d!jpAA@mzC-%N`adxjpXX@mzS-%N`W`cx`jp@@@mzS-<%N``` jp@@@mzC->%N`I`djpAA@mzC-%N`8dxjpXXmzS-%N`4$W`cx`jp@@mzS-<%N`F$``Fjp@@mzC->%N`$`dUjpAAmzC-%N`%dxjpXXmzS-%N`,W`c x`jp@@mzS-<%N`,``jp@@mzC->%N`;-`dkpAAmzC-%N`S.dxakpXXmzS-%N`5W`c0x`nkp@@mzS-<%N`5``kp@@mzC->%N`5`dkpAAmzC-%N`7dx;kpXXmzS-%N`{>W`c@x`Jkp@@mzS-<%N`>``skp@@mzC->%N`>`dkpAAmzC-%N`?dxMkpXXmzS-%N`FW`cPx`\kp@@mzS-<%N`F``kp@@mzC->%N`F`dkpAAmzC-%N`Hdx !kpXXmzS-%N`IOW`c`%x`!kp@@mzS-<%N`VO``W!kp@@mzC->%N`O`dh"kpAAmzC-%N`Pdx)kpXX@mzS-%N` XW`cp5x`)kp@@@mzS-<%N`X``*kp@@@mzC->%N`\X`d+kpAA@mzC-%N`[Ydx2kpXX@mzS-%N``W`cEx`2kp@@@mzS-<%N````2kp@@@mzC->%N`!a`d3kpAA@mzC-%N`/bdxT;kpXXmzS-%N`iW`cUx`b;kp@@mzS-<%N`i``;kp@@mzC->%N`i`d%N`jr`dGEkpAAmzC-%N`sdxLkpXXmzS-%N`zW`cux`Lkp@@mzS-<%N`z``Lkp@@mzC->%N`{`dMkpAAmzC-%N`0|dxUUkpXXmzS-%N`W`cx`dUkp@@mzS-<%N```Ukp@@mzC->%N``dVkpAAmzC-%N`dx^kpXXmzS-%N`WW`cx`&^kp@@mzS-<%N`f``g^kp@@mzC->%N``d_kpAAmzC-%N`̍dxfkpXXmzS-%N`W`cx`fkp@@mzS-<%N```gkp@@mzC->%N`Y`dhkpAAmzC-%N`EdxqokpXXmzS-%N`W`cx`okp@@mzS-<%N```okp@@mzC->%N``dpkpAAmzC-%N`dx/xkpXXmzS-%N`oW`dx`?xkp@@mzS-<%N```xkp@@mzC->%N``dzykpAAmzC-%N`dxkpXXmzS-%N`/W`dx`kp@@mzS-<%N`=``Dkp@@mzC->%N``djkpAAmzC-%N`dxkpXXmzS-%N`W`d x`̉kp@@mzS-<%N` ``kp@@mzC->%N`P`dkpAAmzC-%N`_dx!kpXXmzS-%N`aW`d0x`1kp@@mzS-<%N`q``tkp@@mzC->%N``dkpAAmzC-%N`dxkpXXmzS-%N`)W`d@x`kp@@mzS-<%N`8``;kp@@mzC->%N`{`dPkpAAmzC-%N`dxkpXXmzS-%N`W`dPx`kp@@mzS-<%N```kp@@mzC->%N`B`d kpAAmzC-%N`KdxkpXXmzS-%N`W`d`&x`kp@@@mzS-<%N```جkp@@mzC->%N``dkpAA@mzC-%N`8dx7kpXXmzS-%N`wW`dp6x`Fkp@@mzS-<%N```kp@@mzC->%N``dkpAAmzC-%N`dxϽkpXXmzS-%N`W`dFx`ܽkp@@mzS-<%N```kp@@mzC->%N`>`d$kpAAmzC-%N`ddxkpXX@mzS-%N`W`dVx`kp@@mzS-<%N```kp@@@mzC->%N``dkpAAmzC-%N`4dxVkpXXmzS-%N`W`dfx`ckp@@mzS-<%N```kp@@mzC->%N``dkpAAmzC-%N`dxkpXXmzS-%N`_W`dvx`,kp@@mzS-<%N`l``kp@@mzC->%N``drkpAAmzC-%N`dxkpXXMwS-%N`W`dx`kp@@@MwS-<%N```kp@@MwC->%N`^`d-kpAA@MwC-%N`mdxkpXXMwS-%N`W`dx`kp@@@MwS-<%N```kp@@MwC->%N``dkpAA@MwC-%N`5dxTkpXXMwS-%N` W`dx`akp@@@MwS-<%N` ``kp@@MwC->%N` `dkpAA@MwC-%N`!dxkpXXMwS-%N`(W`dx`kp@@@MwS-<%N`)``kp@@MwC->%N`,)`dkpAA@MwC-%N`Z*dxvlpXXMwS-%N`1W`ex`lp@@MwS-<%N`1``lp@@MwC->%N`1`dlpAAMwC-%N`3dx4 lpXX@MwS-%N`t:W`ex`A lp@@MwS-<%N`:``n lp@@@MwC->%N`:`d lpAAMwC-%N`;dxlpXXMwS-%N`:CW`e x`lp@@MwS-<%N`GC``-lp@@MwC->%N`mC`dDlpAAMwC-%N`DdxlpXX@MwS-%N`KW`e0x`lp@@MwS-<%N`K``lp@@@MwC->%N`#L`d$lpAAMwC-%N`dMd`B&lp@@MwS-<%N`T`x.lpXX@MwS-%N`\W`e@x`.lp@@@MwC->%N`-]`d0lpAAMwC-%N`G^dxe7lpXXMwS-%N`eW`ePx`s7lp@@@MwS-<%N`e``7lp@@MwC->%N`e`d8lpAA@MwC-%N`fdx@lpXXMwS-%N`XnW`e`'x`'@lp@@@MwS-<%N`gn``@lp@@MwC->%N`"o`dAlpAA@MwC-%N`pdxHlpXXMwS-%N` wW`ep7x`Hlp@@MwS-<%N` w``Ilp@@MwC->%N`Cw`dJJlpAAMwC-%N`xdxPlpXX@MwS-%N`!W`eGx`Plp@@MwS-<%N`/``1Qlp@@@MwC->%N`q`d7RlpAAMwC-%N`wdxYlpXX@MwS-%N`W`eWx`Ylp@@MwS-<%N```Ylp@@@MwC->%N`;`dZlpAAMwC-%N`2dx`blpXXMwS-%N`W`egx`nblp@@MwS-<%N```blp@@MwC->%N``dclpAAMwC-%N`dxjlpXX@MwS-%N`.W`ewx`jlp@@MwS-<%N`<``klp@@@MwC->%N`\`dllpAAMwC-%N`[dxxslpXXMwS-%N`W`ex`slp@@@MwS-<%N`ȡ``slp@@MwC->%N``dtlpAA@MwC-%N`dx(|lpXX@MwS-%N`hW`ex`6|lp@@MwS-<%N`v``y|lp@@@MwC->%N``dy}lpAAMwC-%N`dxۄlpXXMwS-%N`W`ex`lp@@@MwS-<%N`/``/lp@@MwC->%N`o`d6lpAA@MwC-%N`vdxlpXX@MwS-%N`W`ex`ȍlp@@MwS-<%N``` lp@@@MwC->%N`L`dlpAAMwC-%N`8dxՖlpXXMwS-%N`W`fx`lp@@@MwS-<%N`"``lp@@MwC->%N`X`d lpAA@MwC-%N`KdxulpXX@MwS-%N`W`fx`lp@@MwS-<%N```ğlp@@@MwC->%N``dՠlpAAMwC-%N`dxlpXXMwS-%N`WW`f x`$lp@@@MwS-<%N`d``Clp@@MwC->%N``dIlpAA@MwC-%N`dxհlpXX@MwS-%N`W`f0x`lp@@MwS-<%N`#``lp@@@MwC->%N`C`dlpAAMwC-%N`_dxlpXXMwS-%N`W`f@x`lp@@@MwS-<%N```lp@@MwC->%N`.`dlpAA@MwC-%N`BdxTlpXX@MwS-%N`W`fPx``lp@@MwS-<%N```lp@@@MwC->%N` `dlpAAMwC-%N`dxlpXXMwS-%N`W`f`(x`lp@@@MwS-<%N`*``,lp@@MwC->%N`l`d/lpAA@MwC-%N`odxlpXX@MwS-%N`W`fp8x`lp@@MwS-<%N```lp@@@MwC->%N`1`dlpAAMwC-%N`$dxlpXXMwS-%N`\ W`fHx`-lp@@@MwS-<%N`m ``Tlp@@MwC->%N` `dxlpAA@MwC-%N` dxlpXX@MwS-%N` W`fXx`lp@@MwS-<%N`'``lp@@@MwC->%N`<`d4lpAAMwC-%N`tdxklpXXMwS-%N`W`fhx`xlp@@@MwS-<%N```lp@@MwC->%N``dlpAA@MwC-%N`dxlpXX@MwS-%N`$W`fxx`lp@@MwS-<%N`&$`` lp@@@MwC->%N`L$`d9lpAAMwC-%N`y%dxlpXXMwS-%N`,W`fx`lp@@@MwS-<%N`,``lp@@MwC->%N`,`dlpAA@MwC-%N`..dxAmpXX@MwS-%N`5W`fx`Nmp@@MwS-<%N`5``pmp@@@MwC->%N`5`dmpAAMwC-%N`6dxmpXXmzS-%N` >W`fx`mp@@@mzS-<%N`>``mp@@mzC->%N`F>`d;mpAA@mzC-%N`{?dxmpXX@mzS-%N`FW`fx`mp@@mzS-<%N`F``mp@@@mzC->%N`G`dmpAAmzC-%N`FHdxX!mpXXmzS-%N`OW`gx`e!mp@@@mzS-<%N`O``!mp@@mzC->%N`O`d"mpAA@mzC-%N`Pdx*mpXXmzS-%N`\XW`gx`)*mp@@mzS-<%N`iX``Z*mp@@mzC->%N`X`d2+mpAAmzC-%N`rYdx2mpXXmzS-%N`aW`g x`2mp@@mzS-<%N`%a``3mp@@mzC->%N`Ea`d 4mpAAmzC-%N`Kbdxo;mpXXmzS-%N`iW`g0x`;mp@@mzS-<%N`i``;mp@@mzC->%N`i`d%N`r`dpEmpAAmzC-%N`sdxLmpXXmzS-%N`${W`gPx`Lmp@@mzS-<%N`0{``Mmp@@mzC->%N`]{`d0NmpAAmzC-%N`p|dxUmpXXmzS-%N`W`g`)x`Ump@@mzS-<%N```Ump@@mzC->%N`"`d#WmpAAmzC-%N`cdx7^mpXXmzS-%N`wW`gp9x`E^mp@@mzS-<%N```v^mp@@mzC->%N``d_mpAAmzC-%N`ԍdxfmpXXmzS-%N`W`gIx`fmp@@mzS-<%N`!``(gmp@@mzC->%N`h`d^hmpAAmzC-%N`dxompXXmzS-%N`͝W`gYx`omp@@mzS-<%N`ٝ``omp@@mzC->%N``dpmpAAmzC-%N`2dxLxmpXXmzS-%N`W`gix`Yxmp@@mzS-<%N```xmp@@mzC->%N`Ʀ`dympAAmzC-%N`dx mpXXmzS-%N`MW`gyx`,mp@@mzS-<%N`l``Jmp@@mzC->%N``dmpAAmzC-%N`ŰdxZmpXXMwS-%N`W`gx`imp@@@MwS-<%N```mp@@MwC->%N`Է`dmpAA@MwC-%N`dxmpXXMwS-%N`&W`gx`mp@@@MwS-<%N`3``!mp@@MwC->%N`a`dSmpAA@MwC-%N`dxmpXXMwS-%N`W`gx`mp@@@MwS-<%N```mp@@MwC->%N`3`dmpAA@MwC-%N`UdxmpXXMwS-%N`W`gx`mp@@MwS-<%N```ģmp@@MwC->%N``dmpAAMwC-%N`-dxnmpXXMwS-%N`W`hx`{mp@@MwS-<%N```mp@@MwC->%N``dmpAAMwC-%N`dx6mpXXMwS-%N`vW`hx`Cmp@@@MwS-<%N```nmp@@MwC->%N``dmpAA@MwC-%N`dxmpXXMwS-%N`6W`h x`mp@@@MwS-<%N`C``0mp@@MwC->%N`p`d\mpAA@MwC-%N`dxcmpXXMwS-%N` W`h0x`qmp@@@MwS-<%N` ``mp@@MwC->%N` `dmpAA@MwC-%N`dxmpXXMwS-%N`W`h@ x`mp@@@MwS-<%N```mp@@MwC->%N`3`dmpAA@MwC-%N`HdxBmpXXMwS-%N`W`hPx`Omp@@MwS-<%N```mp@@MwC->%N``dmpAAMwC-%N`dxmpXXMwS-%N`A'W`h`*x`mp@@MwS-<%N`P'``7mp@@MwC->%N`w'`dImpAAMwC-%N`(dxnpXXMwS-%N`0W`hp:x`np@@@MwS-<%N`0``np@@MwC->%N`10`d)npAA@MwC-%N`i1dx npXXMwS-%N`8W`hJx` np@@@MwS-<%N`8`` np@@MwC->%N`59`d npAA@MwC-%N` :dxnpXXmzS-%N`?AW`hZx`np@@mzS-<%N`OA``Znp@@mzC->%N`A`dLnpAAmzC-%N`BdxnpXXmzS-%N`IW`hjx`np@@mzS-<%N`I``np@@mzC->%N`2J`dnpAAmzC-%N`:Kdx\$npXXmzS-%N`RW`hzx`m$np@@mzS-<%N`R``$np@@mzC->%N`R`d%npAAmzC-%N`Sdx,-npXXmzS-%N`l[W`hx`<-np@@mzS-<%N`|[``|-np@@mzC->%N`[`de.npAAmzC-%N`\dx5npXXmzS-%N`+dW`hx`5np@@mzS-<%N`%N`d`d97npAAmzC-%N`yedx>npXXmzS-%N`lW`hx`>np@@mzS-<%N`l``>np@@mzC->%N`9m`d@npAAmzC-%N`@ndxsGnpXXmzS-%N`uW`hx`Gnp@@mzS-<%N`u``Gnp@@mzC->%N` v`dHnpAAmzC-%N`wdx PnpXXmzS-%N`K~W`ix`Pnp@@mzS-<%N`]~``XPnp@@mzC->%N`~`dtQnpAAmzC-%N`dxXnpXXmzS-%N`W`ix`Xnp@@mzS-<%N```Ynp@@mzC->%N`U`dZnpAAmzC-%N`Sd`5anp@@mzS-<%N`u`xbenpXXmzS-%N`W`i x`enp@@mzC->%N`ғ`dfnpAAmzC-%N`dx"nnpXXmzS-%N`bW`i0x`/nnp@@mzS-<%N`o```nnp@@mzC->%N``donpAAmzC-%N`ŝdxvnpXXmzS-%N`W`i@ x`vnp@@mzS-<%N```vnp@@mzC->%N``d xnpAAmzC-%N`Jdx~npXXmzS-%N`W`iPx`~np@@mzS-<%N`+``np@@mzC->%N`X`d;npAAmzC-%N`{dxnpXXmzS-%N`еW`i`+x`np@@mzS-<%N`ݵ``χnp@@mzC->%N``d؈npAAmzC-%N`dx npXX@mzS-%N``W`ip;x`-np@@mzS-<%N`m``[np@@@mzC->%N``dnpAAmzC-%N`¿dxnpXXmzS-%N`8W`iKx` np@@@mzS-<%N`K``Onp@@mzC->%N``dCnpAA@mzC-%N`dxnpXX@mzS-%N`W`i[x`ánp@@mzS-<%N``` np@@@mzC->%N`K`d5npAAmzC-%N`udxrnpXXmzS-%N`W`ikx`np@@mzS-<%N```˪np@@mzC->%N` `dϫnpAAmzC-%N`dxFnpXXmzS-%N`W`i{x`Unp@@mzS-<%N```np@@mzC->%N``dnpAAmzC-%N`dxnpXXmzS-%N`FW`ix`np@@mzS-<%N`Y``_np@@mzC->%N``dSnpAAmzC-%N`dxnpXXmzS-%N`W`ix`np@@mzS-<%N```np@@mzC->%N`R`dSnpAAmzC-%N`dxinpXXmzS-%N`W`ix`xnp@@mzS-<%N```np@@mzC->%N``dnpAAmzC-%N`dx,npXXmzS-%N`lW`ix`=np@@mzS-<%N`}``np@@mzC->%N``dznpAAmzC-%N`dxnpXXmzS-%N`" W`jx`np@@mzS-<%N`2 ``;np@@mzC->%N`{ `dFnpAAmzC-%N`dxnpXXmzS-%N`W`jx`np@@mzS-<%N```np@@mzC->%N`.`dnpAAmzC-%N`dx_npXXmzS-%N`W`j x`pnp@@mzS-<%N```np@@mzC->%N``dnpAAmzC-%N` dxnpXXmzS-%N`'W`j0x`np@@mzS-<%N`*'``np@@mzC->%N`G'`dnpAAmzC-%N`F(dxlopXXmzS-%N`/W`j@ x`op@@mzS-<%N`/``op@@mzC->%N` 0`dopAAmzC-%N`1dx- opXXmzS-%N`m8W`jPx`: op@@mzS-<%N`z8``i op@@mzC->%N`8`d opAAmzC-%N`9dxopXXmzS-%N`)AW`j`,x`op@@mzS-<%N`8A``$op@@mzC->%N`dA`d~opAAmzC-%N`BdxopXXmzS-%N`IW`jp<x`op@@mzS-<%N`I``op@@mzC->%N`J`dopAAmzC-%N`ZKdxh$opXXmzS-%N`RW`jLx`u$op@@mzS-<%N`R``$op@@mzC->%N`R`d%opAAmzC-%N`Sdx-opXXmzS-%N`W[W`j\x`'-op@@mzS-<%N`g[``s-op@@mzC->%N`[`dq.opAAmzC-%N`\dx5opXXmzS-%N`cW`jlx`5op@@mzS-<%N`c``6op@@mzC->%N`Gd`d 7opAAmzC-%N`Iedxo>opXXmzS-%N`lW`j|x`>op@@mzS-<%N`l``>op@@mzC->%N` m`d?opAAmzC-%N`3ndxFopXXmzS-%N`>uW`jx`Gop@@mzS-<%N`Nu``\Gop@@mzC->%N`u`doHopAAmzC-%N`vdxOopXXmzS-%N`}W`jx`Oop@@mzS-<%N` ~``Pop@@mzC->%N`Y~`d QopAAmzC-%N`MdxXopXXmzS-%N`ĆW`jx`Xop@@mzS-<%N`Ն``Xop@@mzC->%N``dYopAAmzC-%N`0dxaopXXmzS-%N`TW`jx`%aop@@mzS-<%N`e``haop@@mzC->%N``dbopAAmzC-%N`ݐdxiopXXmzS-%N`W`kx`iop@@mzS-<%N```iop@@mzC->%N`:`dkopAAmzC-%N`]dxbropXXmzS-%N`W`kx`rrop@@mzS-<%N```rop@@mzC->%N``dsopAAmzC-%N` dx{opXXmzS-%N`\W`k x`,{op@@mzS-<%N`l``t{op@@mzC->%N``d||opAAmzC-%N`dxopXXmzS-%N`"W`k0x`op@@mzS-<%N`1``op@@mzC->%N`T`dopAAmzC-%N`UdxvopXXmzS-%N`W`k@ x`op@@mzS-<%N`ƺ``ьop@@mzC->%N``dݍopAAmzC-%N`dxؔopXXmzS-%N`W`kPx`op@@mzS-<%N`'``op@@mzC->%N`G`dFopAAmzC-%N`dxopXXmzS-%N`W`k`-x`op@@mzS-<%N```op@@mzC->%N`<`d opAAmzC-%N`IdxcopXXmzS-%N`W`kp=x`sop@@mzS-<%N```op@@mzC->%N``dʧopAAmzC-%N` dx+opXXmzS-%N`kW`kMx`%N``dopAAmzC-%N`dxopXXmzS-%N`+W`k]x`op@@mzS-<%N`:``8op@@mzC->%N`x`dWopAAmzC-%N`dxopXXmzS-%N`W`kmx`op@@mzS-<%N```op@@mzC->%N`>`dopAAmzC-%N`BdxTopXXmzS-%N`W`k}x`bop@@mzS-<%N```op@@mzC->%N``dopAAmzC-%N`dx"opXXmzS-%N`bW`kx`1op@@mzS-<%N`q``|op@@mzC->%N``d|opAAmzC-%N`dxopXXmzS-%N` W`kx`op@@mzS-<%N`! ``$op@@mzC->%N`d `dFopAAmzC-%N` dxbopXXmzS-%N`W`kx`rop@@mzS-<%N```op@@mzC->%N``dopAAmzC-%N`dxopXXmzS-%N`^W`kx`,op@@mzS-<%N`l``vop@@mzC->%N``dopAAmzC-%N`dxopXXmzS-%N`#W`lx`op@@mzS-<%N`+#``3op@@mzC->%N`s#`d'opAAmzC-%N`g$dxopXXmzS-%N`+W`lx`op@@mzS-<%N`+``op@@mzC->%N`+,`d opAAmzC-%N`K-dx[ppXXmzS-%N`4W`l x`opp@@mzS-<%N`4``pp@@mzC->%N`4`dppAAmzC-%N`6dxppXXmzS-%N`V=W`l0x`%pp@@mzS-<%N`e=``hpp@@mzC->%N`=`d]ppAAmzC-%N`>dxppXXmzS-%N`FW`l@x`pp@@mzS-<%N`F``pp@@mzC->%N`[F`d&ppAAmzC-%N`fGdx{ ppXXmzS-%N`NW`lPx` pp@@mzS-<%N`N`` pp@@mzC->%N`O`d!ppAAmzC-%N`5Pdx1)ppXXmzS-%N`qWW`l`.x`?)pp@@mzS-<%N`W``)pp@@mzC->%N`W`d*ppAAmzC-%N`Xdx1ppXXmzS-%N`_W`lp>x`1pp@@mzS-<%N````2pp@@mzC->%N`N``d3ppAAmzC-%N`[adxu:ppXXmzS-%N`hW`lNx`:pp@@mzS-<%N`h``:pp@@mzC->%N` i`d;ppAAmzC-%N`jdx*CppXXmzS-%N`jqW`l^x`9Cpp@@mzS-<%N`yq``Cpp@@mzC->%N`q`dyDppAAmzC-%N`rdxKppXXmzS-%N`yW`lnx`Kpp@@mzS-<%N`z``Kpp@@mzC->%N`'z`d/MppAAmzC-%N`o{dxhTppXXmzS-%N`W`l~x`vTpp@@mzS-<%N```Tpp@@mzC->%N``dUppAAmzC-%N` dx]ppXXmzS-%N`ZW`lx`(]pp@@mzS-<%N`h``K]pp@@mzC->%N``d^ppAAmzC-%N`ŒdxeppXXmzS-%N`W`lx`epp@@mzS-<%N```fpp@@mzC->%N`^`dgppAAmzC-%N`]dxnppXXmzS-%N`W`lx`npp@@mzS-<%N`֜``npp@@mzC->%N``d#pppAAmzC-%N`cdx wppXXmzS-%N`IW`lx`wpp@@mzS-<%N`]```wpp@@mzC->%N``d{xppAAmzC-%N`d`pp@@mzS-<%N`ݭ`xppXXmzS-%N`'W`mx`pp@@mzC->%N`S`dppAAmzC-%N`QdxppXXmzS-%N`ߺW`mx`pp@@mzS-<%N```܌pp@@mzC->%N``dppAAmzC-%N`0dx~ppXXmzS-%N`W`m x`pp@@mzS-<%N```pp@@mzC->%N``dppAAmzC-%N`$dxNppXXmzS-%N`W`m0x`[pp@@mzS-<%N```pp@@mzC->%N``dppAAmzC-%N`dxppXXmzS-%N`NW`m@x`pp@@mzS-<%N`[``Epp@@mzC->%N``d`ppAAmzC-%N`dxѯppXXmzS-%N`W`mPx`ޯpp@@mzS-<%N```pp@@mzC->%N`_`d!ppAAmzC-%N`adxppXXmzS-%N`W`m`/x`pp@@mzS-<%N```ոpp@@mzC->%N``dعppAAmzC-%N`dxcppXXmzS-%N`W`mp?x`spp@@mzS-<%N```pp@@mzC->%N``dppAAmzC-%N`dxppXXmzS-%N`VW`mOx`"pp@@mzS-<%N`b``Mpp@@mzC->%N``d]ppAAmzC-%N`dxppXXmzS-%N`W`m_x`pp@@mzS-<%N```pp@@mzC->%N``dppAAmzC-%N`FdxfppXXmzS-%N` W`mox`vpp@@mzS-<%N` ``pp@@mzC->%N` `dppAAmzC-%N` dxppXXmzS-%N`[W`mx`(pp@@mzS-<%N`h``Vpp@@mzC->%N``dppAAmzC-%N`dxppXXmzS-%N`W`mx`pp@@mzS-<%N```pp@@mzC->%N``dppAAmzC-%N`3dxDppXXmzS-%N`#W`mx`Qpp@@mzS-<%N`#``zpp@@mzC->%N`#`dppAAmzC-%N`$dxppXXmzS-%N`9,W`mx`pp@@mzS-<%N`G,``'pp@@mzC->%N`g,`dppAAmzC-%N`[-dxqpXXmzS-%N`4W`mx`qp@@mzS-<%N`5``qp@@mzC->%N`05`dqpAAmzC-%N`U6dxwqpXXmzS-%N`=W`nx`qp@@mzS-<%N`=``qp@@mzC->%N`=`dqpAAmzC-%N`=?dxqpXXmzS-%N`HFW`nx`qp@@mzS-<%N`WF``Bqp@@mzC->%N`F`dsqpAAmzC-%N`Gdx qpXXmzS-%N`NW`n x` qp@@mzS-<%N`N`` qp@@mzC->%N`O`d!qpAAmzC-%N`6Pdxd)qpXXmzS-%N`WW`n0x`q)qp@@mzS-<%N`W``)qp@@mzC->%N`W`d*qpAAmzC-%N`Ydx&2qpXXmzS-%N`f`W`n@x`52qp@@mzS-<%N`u```^2qp@@mzC->%N```dp3qpAAmzC-%N`adx:qpXXmzS-%N`hW`nP x`:qp@@mzS-<%N`h``:qp@@mzC->%N`i`d;qpAAmzC-%N`jdxCqpXXmzS-%N`qW`n`0x`Cqp@@mzS-<%N`q``Cqp@@mzC->%N` r`dDqpAAmzC-%N`%sdxfLqpXXmzS-%N`zW`np@x`tLqp@@mzS-<%N`z``Lqp@@mzC->%N`z`dMqpAAmzC-%N`|dx+UqpXXmzS-%N`kW`nPx`8Uqp@@mzS-<%N`x``aUqp@@mzC->%N``dVqpAAmzC-%N`ބdx]qpXXmzS-%N`,W`n`x`]qp@@mzS-<%N`9``'^qp@@mzC->%N`g`d_qpAAmzC-%N`EdxfqpXXmzS-%N`ƔW`npx`fqp@@mzS-<%N`֔``fqp@@mzC->%N``dgqpAAmzC-%N`*dxnqpXXmzS-%N`<W`nx` oqp@@mzS-<%N`I``5oqp@@mzC->%N`u`dHpqpAAmzC-%N`dxwqpXXmzS-%N`W`nx`wqp@@mzS-<%N` ``wqp@@mzC->%N`5`dyqpAAmzC-%N`PdxwqpXXmzS-%N`W`nx`qp@@mzS-<%N`Į``qp@@mzC->%N``d؁qpAAmzC-%N`dxqpXXmzS-%N`CW`nx`qp@@mzS-<%N`Q``Fqp@@mzC->%N``d]qpAAmzC-%N`dxőqpXXmzS-%N`W`nx`ґqp@@mzS-<%N```qp@@mzC->%N`<`d+qpAAmzC-%N`kdxqpXXmzS-%N`W`ox`qp@@mzS-<%N```qp@@mzC->%N``dqpAAmzC-%N`'dxLqpXXmzS-%N`W`ox`Yqp@@mzS-<%N```qp@@mzC->%N``dqpAAmzC-%N`dxqqpXXmzS-%N`W`o x`~qp@@mzS-<%N```qp@@mzC->%N``dЬqpAAmzC-%N`dx5qpXXmzS-%N`uW`o0x`Bqp@@mzS-<%N```}qp@@mzC->%N``dqpAAmzC-%N`dxqpXXmzS-%N`W`o@x`ȼqp@@mzS-<%N```qp@@mzC->%N`7`dqpAAmzC-%N`ZdxqpXXmzS-%N`W`oP!x`qp@@mzS-<%N```qp@@mzC->%N` `dqpAAmzC-%N`&dxBqpXXmzS-%N`W`o`1x`Oqp@@mzS-<%N```yqp@@mzC->%N``dqpAAmzC-%N`dxqpXXmzS-%N`&W`opAx`qp@@mzS-<%N`3``qp@@mzC->%N`X`d6qpAAmzC-%N`vdxqpXXmzS-%N` W`oQx`qp@@mzS-<%N` ``qp@@mzC->%N`#`dqpAAmzC-%N`TdxjqpXXmzS-%N`W`oax`wqp@@mzS-<%N```qp@@mzC->%N``dqpAAmzC-%N`dxqpXXmzS-%N`^W`oqx`+qp@@mzS-<%N`k``Nqp@@mzC->%N``d]qpAAmzC-%N` dxqpXXmzS-%N`'W`ox`qp@@mzS-<%N`'``qp@@mzC->%N`#(`d7qpAAmzC-%N`w)dxfrpXXmzS-%N`0W`ox`rrp@@mzS-<%N`0``rp@@mzC->%N`0`drpAAmzC-%N` 2dx rpXXmzS-%N`19W`ox` rp@@mzS-<%N`>9``( rp@@mzC->%N`h9`dW rpAAmzC-%N`:dxrpXXmzS-%N`BW`ox`rp@@mzS-<%N`B``rp@@mzC->%N`:B`d rpAAmzC-%N``Cdx(rpXXmzS-%N`hJW`ox`3rp@@mzS-<%N`sJ``Zrp@@mzC->%N`J`d8rpAAmzC-%N`fdx=rpXXmzS-%N`lW`px`=rp@@mzS-<%N`l``>rp@@mzC->%N`Rl`d4?rpAAmzC-%N`tmdxyFrpXXmzS-%N`tW`px`Frp@@mzS-<%N`t``Frp@@mzC->%N`t`dGrpAAmzC-%N`vdx%OrpXXmzS-%N`e}W`p x`3Orp@@mzS-<%N`s}``eOrp@@mzC->%N`}`dPrpAAmzC-%N`~dxWrpXXmzS-%N`W`p0x`Wrp@@mzS-<%N```Xrp@@mzC->%N`R`d7YrpAAmzC-%N`wdxv`rpXXmzS-%N`W`p@x``rp@@mzS-<%N````rp@@mzC->%N``darpAAmzC-%N`dxirpXXmzS-%N`PW`pP"x`irp@@mzS-<%N`[``Pirp@@mzC->%N``dhjrpAAmzC-%N`dxqrpXXmzS-%N`W`p`2x`qrp@@mzS-<%N```qrp@@mzC->%N`5`drrpAAmzC-%N`dxqzrpXXmzS-%N`W`ppBx`}zrp@@mzS-<%N```zrp@@mzC->%N`4`d |rpAAmzC-%N`MdxԂrpXXmzS-%N`W`pRx`߂rp@@mzS-<%N```rp@@mzC->%N`O`dGrpAAmzC-%N`dxqrpXXmzS-%N`W`pbx`|rp@@mzS-<%N```rp@@mzC->%N``d֌rpAAmzC-%N`dxrpXXmzS-%N`AW`prx` rp@@mzS-<%N`K``%N`|`dMrpAAmzC-%N`dxrpXXmzS-%N`W`px`rp@@mzS-<%N```rp@@mzC->%N`(`drpAAmzC-%N`1dxLrpXXmzS-%N`W`px`Xrp@@mzS-<%N```rp@@mzC->%N``dӦrpAAmzC-%N`dxrpXXmzS-%N`2W`px`rp@@mzS-<%N`<``=rp@@mzC->%N`}`drrpAAmzC-%N`d`Crp@@mzS-<%N``xrpXXmzS-%N`W`px`ܺrp@@mzC->%N``drpAAmzC-%N`5dx]rpXXmzS-%N`W`px`krp@@mzS-<%N```rp@@mzC->%N``drpAAmzC-%N`dxrpXXmzS-%N`2W`qx`rp@@mzS-<%N`A``.rp@@mzC->%N`n`drpAAmzC-%N`dxrpXXmzS-%N` W`qx`rp@@mzS-<%N` ``rp@@mzC->%N` `drpAAmzC-%N`@ dxirpXXmzS-%N` W`q x`urp@@mzS-<%N` ``rp@@mzC->%N` `drpAAmzC-%N` dxrpXXmzS-%N`^ W`q0x`+rp@@mzS-<%N`k ``Urp@@mzC->%N` `d}rpAAmzC-%N` dxrpXXmzS-%N`" W`q@x`rp@@mzS-<%N`0 ``'rp@@mzC->%N`g `drpAAmzC-%N`S dxrpXXmzS-%N`% W`qP#x`rp@@mzS-<%N`% ``rp@@mzC->%N`$& `drpAAmzC-%N`<' dx8spXXmzS-%N`x. W`q`3x`Esp@@mzS-<%N`. ``qsp@@mzC->%N`. `dPspAAmzC-%N`/ dxspXXmzS-%N`>7 W`qpCx` sp@@mzS-<%N`O7 ``Y sp@@mzC->%N`7 `d^ spAAmzC-%N`8 dxspXXMwS-%N`? W`qSx`sp@@@MwS-<%N`? ``sp@@MwC->%N` @ `dspAA@MwC-%N`A dxspXXMwS-%N`UH W`qcx`%sp@@@MwS-<%N`eH ``Dsp@@MwC->%N`H `dspAA@MwC-%N`I dx"spXX@8yS-%N`Q W`qsx`"sp@@8yS-<%N`*Q ``#sp@@@8yC->%N`SQ `dB$spAA8yC-%N`R dx+spXX@8yS-%N`Y W`qx`+sp@@8yS-<%N`Y ``+sp@@@8yC->%N`*Z `d,spAA8yC-%N`[ dxp4spXX8yS-%N`b W`qx`4sp@@8yS-<%N`b ``4sp@@8yC->%N`c `d5spAA8yC-%N`d dx=spXX8yS-%N`]k W`qx`*=sp@@8yS-<%N`jk ``n=sp@@8yC->%N`k `dN>spAA8yC-%N`l dxEspXX8yS-%N`t W`qx`Esp@@@8yS-<%N`-t ``4Fsp@@8yC->%N`tt `ddGspAA@8yC-%N`u dxbNspXX8yS-%N`| W`qx`tNsp@@@8yS-<%N`| ``Nsp@@8yC->%N`| `dOspAA@8yC-%N`} dx&WspXX8yS-%N`f W`rx`4Wsp@@@8yS-<%N`t ``xWsp@@8yC->%N` `dsXspAA@8yC-%N` dx_spXX8yS-%N`, W`rx`_sp@@8yS-<%N`: ``0`sp@@8yC->%N`p `dQaspAA8yC-%N` dxhspXXMwS-%N` W`r x`hsp@@@MwS-<%N` ``isp@@MwC->%N`] `dXjspAA@MwC-%N` dxqspXX@MwS-%N`ҟ W`r0x`qsp@@MwS-<%N` ``qsp@@@MwC->%N`& `dsspAAMwC-%N`G dx[zspXXMwS-%N` W`r@x`izsp@@@MwS-<%N` ``zsp@@MwC->%N` `d{spAA@MwC-%N` dx!spXXMwS-%N`a W`rP$x`/sp@@@MwS-<%N`o ``rsp@@MwC->%N` `dspAA@MwC-%N`Dz dxspXXMwS-%N`, W`r`4x`sp@@MwS-<%N`F ``-sp@@MwC->%N`m `dFspAAMwC-%N` dxspXX@MwS-%N` W`rpDx`sp@@@MwS-<%N` ``sp@@@MwC->%N`= `dspAA@MwC-%N`6 dxqspXXMwS-%N` W`rTx`~sp@@@MwS-<%N` ``sp@@MwC->%N` `dמspAA@MwC-%N` dxspXX8yS-%N` W`rdx`sp@@8yS-<%N` ``Ƴsp@@8yC->%N` `dspAA8yC-%N`5 dxIspXX@8yS-%N` W`rtx`Vsp@@8yS-<%N` ``sp@@@8yC->%N` `dspAA8yC-%N` dx spXX@8yS-%N`L W`rx`sp@@8yS-<%N`Y ``Nsp@@@8yC->%N` `dgspAA8yC-%N` dxxspXX@8yS-%N` W`rx`sp@@8yS-<%N` ``sp@@@8yC->%N` `dspAA8yC-%N` dxspXX8yS-%N` W`rx`sp@@8yS-<%N` ``sp@@8yC->%N`G `d9spAA8yC-%N`y dxuspXX8yS-%N` W`rx`sp@@8yC->%N` ``sp@@8yS-<%N` `dspAA8yC-%N` dxspXX8yS-%N` W`rx`sp@@@8yS-<%N` ``sp@@8yC->%N` `dspAA@8yC-%N` dxspXX8yS-%N`A W`sx`sp@@@8yS-<%N`S ``>sp@@8yC->%N`~ `dKspAA@8yC-%N` dx_spXX8yS-%N`% W`sx`psp@@@8yS-<%N`% ``sp@@8yC->%N`% `dspAA@8yC-%N`& dxspXX8yS-%N`- W`s x`sp@@8yS-<%N`. ``sp@@8yC->%N`=. `dtpAA8yC-%N`Z/ dxtpXX8yS-%N`^6 W`s0x`.tp@@8yS-<%N`n6 ``\tp@@8yC->%N`6 `dQ tpAA8yC-%N`7 dx}tpXX@8yS-%N`> W`s@x`tp@@8yS-<%N`> ``tp@@@8yC->%N`> `dtpAA8yC-%N`? dxtpXX@8yS-%N`'G W`sP%x`tp@@8yS-<%N`;G ``,tp@@@8yC->%N`lG `dLtpAA8yC-%N`H dx tpXX@8yS-%N`N W`s`5x` tp@@8yS-<%N`O `` tp@@@8yC->%N`,O `d"tpAA8yC-%N`VP dx )tpXX8yS-%N`JW W`spEx`)tp@@8yS-<%N`ZW ``)tp@@8yC->%N`X `d*tpAA8yC-%N`X dx1tpXX8yS-%N`` W`sUx`1tp@@8yS-<%N`` ``1tp@@8yC->%N`>` `d3tpAA8yC-%N`]a dx:tpXX8yS-%N`h W`sex`:tp@@@8yS-<%N`h ``:tp@@8yC->%N`'i `dCtpXX8yS-%N`~q W`sux`KCtp@@@8yS-<%N`q ``Ctp@@8yC->%N`q `dDtpAA@8yC-%N`r dxKtpXX8yS-%N`z W`sx`Ktp@@@8yS-<%N`z ``Ktp@@8yC->%N`1z `dMtpAA@8yC-%N`D{ dxTtpXX8yS-%N`+ W`sx`Ttp@@8yS-<%N`> `` Utp@@8yC->%N`` `dVtpAA8yC-%N`B dxjtpXX8yS-%N` W`sx`jtp@@8yS-<%N` ``ktp@@8yC->%N`B `dAltpAA8yC-%N` dxstpXX@8yS-%N`ҡ W`sx`stp@@8yS-<%N`ߡ ``stp@@@8yC->%N` `dttpAA8yC-%N`& dx|tpXX@8yS-%N`@ W`sx`|tp@@8yS-<%N`Q ``=|tp@@@8yC->%N`} `df}tpAA8yC-%N` dx^tpXX@8yS-%N` W`tx`rtp@@8yS-<%N` ``tp@@@8yC->%N`[ `dtpAA8yC-%N`R dxtpXX8yS-%N` W`tx`Ҍtp@@8yS-<%N` ``tp@@8yC->%N`W `dtpAA8yC-%N`\ dx!tpXX8yS-%N`a W`t x`2tp@@8yS-<%N`r ``rtp@@8yC->%N` `dtpAA8yC-%N` dxtpXX8yS-%N` W`t0x`tp@@@8yS-<%N` ``ӝtp@@8yC->%N` `dtpAA@8yC-%N`B dxtpXX8yS-%N` W`t@x`tp@@@8yS-<%N`0 ``)tp@@8yC->%N`i `dQtpAA@8yC-%N` dxCtpXX8yS-%N` W`tP&x`Stp@@@8yS-<%N` ``tp@@8yC->%N` `dtpAA@8yC-%N` dxtpXX8yS-%N` W`t`6x`tp@@8yS-<%N` ``tp@@8yC->%N` `dܷtpAA8yC-%N` dxttpXX8yS-%N` W`tpFx`tp@@8yS-<%N` ``ſtp@@8yC->%N` `dtpAA8yC-%N` dx+tpXX@8yS-%N`k W`tVx`8tp@@8yS-<%N`x ``{tp@@@8yC->%N` `dtpAA8yC-%N` dxtpXX@8yS-%N`/ W`tfx`tp@@8yS-<%N`C ``.tp@@@8yC->%N`n `d tpAA8yC-%N`I dxtpXX@8yS-%N` W`tvx`tp@@8yS-<%N` ``tp@@@8yC->%N` `dtpAA8yC-%N`^ dxqtpXX8yS-%N` W`tx`~tp@@8yS-<%N` ``tp@@8yC->%N` `dtpAA8yC-%N` d`4tp@@8yS-<%N`t `xtpXX8yS-%N`! W`tx`tp@@8yC->%N`" `dtpAA8yC-%N`S# dxbtpXX8yS-%N`* W`tx`otp@@@8yS-<%N`* ``tp@@8yC->%N`* `dtpAA@8yC-%N`, dx!upXX8yS-%N`a3 W`tx`.up@@@8yS-<%N`n3 ``[up@@8yC->%N`3 `dqupAA@8yC-%N`4 dx upXX8yS-%N`< W`tx` up@@@8yS-<%N`*< `` up@@8yC->%N`J< `dupAA@8yC-%N`5= dxzupXX8yS-%N`D W`ux`up@@8yS-<%N`D ``up@@8yC->%N`D `dupAA8yC-%N`E dx)upXX8yS-%N`iM W`ux`8up@@8yS-<%N`xM ```up@@8yC->%N`M `dz upAA8yC-%N`N dx'upXX@8yS-%N`U W`u x`'up@@8yS-<%N`U ``'up@@@8yC->%N`)V `d )upAA8yC-%N`JW dxq0upXX@8yS-%N`^ W`u0x`~0up@@8yS-<%N`^ ``0up@@@8yC->%N`^ `d1upAA8yC-%N`_ dxZ9upXX@8yS-%N`g W`u@x`k9up@@8yS-<%N`g ``9up@@@8yC->%N`g `d:upAA8yC-%N`h dxBupXX8yS-%N`Ap W`uP'x`Bup@@8yS-<%N`Rp ``:Bup@@8yC->%N`zp `djCupAA8yC-%N`q dxJupXX8yS-%N`:y W`u`7x`Kup@@8yS-<%N`Ny ``-Kup@@8yC->%N`my `d/LupAA8yC-%N`oz dxSupXX8yS-%N` W`upGx`Sup@@@8yS-<%N` ``Sup@@8yC->%N`+ `dTupAA@8yC-%N` dxS\upXX8yS-%N` W`uWx`f\up@@@8yS-<%N` ``\up@@8yC->%N`ʊ `d]upAA@8yC-%N`! dx'eupXX8yS-%N`g W`ugx`:eup@@@8yS-<%N`z ``jeup@@8yC->%N` `dfupAA@8yC-%N`֔ dxmupXX8yS-%N`8 W`uwx` nup@@8yS-<%N`I ``5nup@@8yC->%N`u `dLoupAA8yC-%N` dxvupXX8yS-%N` W`ux`vup@@8yS-<%N` ``wup@@8yC->%N`C `d5xupAA8yC-%N`u dxupXX@8yS-%N`ݭ W`ux`up@@8yS-<%N` ``up@@@8yC->%N` `dupAA8yC-%N`? dxyupXX@8yS-%N` W`ux`up@@8yS-<%N`϶ ``܈up@@@8yC->%N` `dupAA8yC-%N`6 dxupXX@8yS-%N`8 W`ux` up@@8yS-<%N`J ``Oup@@@8yC->%N` `d`upAA8yC-%N` dx™upXX8yS-%N` W`ux`Йup@@8yS-<%N` ``up@@8yC->%N`P `dupAA8yC-%N`Z dxupXX8yS-%N` W`vx`up@@8yS-<%N` ``up@@8yC->%N`- `d/upAA8yC-%N`o dx\upXX8yS-%N` W`vx`jup@@@8yS-<%N` ``up@@8yC->%N` `dupAA@8yC-%N` dxupXX8yS-%N` W`v x`up@@@8yS-<%N`0 ``up@@8yC->%N`V `dupAA@8yC-%N`U dxɼupXX8yS-%N` W`v0x`up@@@8yS-<%N`. ``up@@8yC->%N`P `d5upAA@8yC-%N`u dxupXX8yS-%N` W`v@x`up@@8yS-<%N` ``up@@8yC->%N`. `dupAA8yC-%N`; dxZupXX8yS-%N` W`vP(x`kup@@8yS-<%N` ``up@@8yC->%N` `dupAA8yC-%N` dxupXX@8yS-%N` W`v`8x`up@@8yS-<%N` ``up@@@8yC->%N` `dupAA8yC-%N`E dxDupXX@8yS-%N` W`vpHx`Rup@@8yS-<%N` ``up@@@8yC->%N` `dupAA8yC-%N` dx upXX@8yS-%N`J W`vXx`up@@8yS-<%N`W ``@up@@@8yC->%N` `duupAA8yC-%N` dxupXX8yS-%N` W`vhx`up@@8yS-<%N` ``up@@8yC->%N` `dupAA8yC-%N`A dx,upXX8yS-%N`l' W`vxx`=up@@8yS-<%N`}' ``~up@@8yC->%N`' `dupAA8yC-%N`( dxvpXX8yS-%N`0 W`vx`vp@@@8yS-<%N`!0 ``%vp@@8yC->%N`e0 `d0vpAA@8yC-%N`p1 dx vpXX8yS-%N`8 W`vx` vp@@@8yS-<%N`8 `` vp@@8yC->%N`(9 `d vpAA@8yC-%N` : dx8vpXX8yS-%N`xA W`vx`Evp@@@8yS-<%N`A ``vp@@8yC->%N`A `dvpAA@8yC-%N`B dxvpXX8yS-%N`7J W`vx`vp@@8yS-<%N`DJ ``Ivp@@8yC->%N`J `dwvpAA8yC-%N`K dx$vpXX8yS-%N`R W`vx`$vp@@8yS-<%N`S ``#%vp@@8yC->%N`cS `d&vpAA8yC-%N`VT dxL-vpXX@8yS-%N`[ W`wx`Z-vp@@8yS-<%N`[ ``-vp@@@8yC->%N`[ `d.vpAA8yC-%N`\ dx5vpXX@8yS-%N` d W`wx`5vp@@8yS-<%N`d ``6vp@@@8yC->%N`[d `d"7vpAA8yC-%N`be dxG>vpXX@8yS-%N`l W`w x`U>vp@@8yS-<%N`l ``>vp@@@8yC->%N`l `d?vpAA8yC-%N`m dxFvpXX8yS-%N`8u W`w0 x`Gvp@@8yS-<%N`Eu ``JGvp@@8yC->%N`u `dtHvpAA8yC-%N`v dx OvpXX8yS-%N`K} W`w@x`Ovp@@8yS-<%N`[} ``^Ovp@@8yC->%N`} `dtPvpAA8yC-%N`~ dxWvpXX8yS-%N` W`wP)x`Wvp@@@8yS-<%N` ``Xvp@@8yC->%N`F `dYvpAA@8yC-%N`F dx>`vpXX8yS-%N`~ W`w`9x`J`vp@@@8yS-<%N` ```vp@@8yC->%N`Ў `davpAA@8yC-%N` dxhvpXX8yS-%N`. W`wpIx`hvp@@@8yS-<%N`: ``>ivp@@8yC->%N`~ `dSjvpAA@8yC-%N` dxqvpXX8yS-%N` W`wYx`qvp@@8yS-<%N` ``qvp@@8yC->%N`5 `drvpAA8yC-%N`& dx&zvpXX8yS-%N`f W`wix`3zvp@@8yS-<%N`s ``vzvp@@8yC->%N` `dj{vpAA8yC-%N` dxӂvpXX@8yS-%N` W`wyx`vp@@8yS-<%N` ``%vp@@@8yC->%N`e `dvpAA8yC-%N`_ dxvpXX@8yS-%N`ǹ W`wx`vp@@8yS-<%N`Թ ``܋vp@@@8yC->%N` `dӌvpAA8yC-%N` dx1vpXX@8yS-%N`q W`wx`%N` `d{vpAA8yC-%N` dxvpXX8yS-%N`' W`wx`vp@@8yS-<%N`4 ``vp@@8yC->%N`] `d)vpAA8yC-%N`i dxvpXX8yS-%N` W`wx`vp@@8yS-<%N` `` vp@@8yC->%N`K `dvpAA8yC-%N`] dxvpXX8yS-%N`O W`wx`vp@@@8yS-<%N`Z ``Ԯvp@@8yC->%N` `dvpAA@8yC-%N`Y dxvpXX8yS-%N` W`xx`vp@@@8yS-<%N`" ``/vp@@8yC->%N`o `dUvpAA@8yC-%N` dxWvpXX8yS-%N` W`xx`bvp@@@8yS-<%N` ``vp@@8yC->%N` `dvpAA@8yC-%N` dxvpXX8yS-%N`A W`x x` vp@@8yS-<%N`L ``+vp@@8yC->%N`k `dvpAA8yC-%N`^ dxvpXX8yS-%N` W`x0 x`vp@@8yS-<%N` ``vp@@8yC->%N`T `dvpAA8yC-%N`H dxmvpXX@8yS-%N` W`x@x`zvp@@8yS-<%N` ``vp@@@8yC->%N` `d vpAA8yC-%N``" dx,vpXX@8yS-%N`l) W`xP*x`9vp@@8yS-<%N`y) ``{vp@@@8yC->%N`) `dvpAA8yC-%N`* dxwpXX@8yS-%N`,2 W`x`:x`wp@@8yS-<%N`82 ``Cwp@@@8yC->%N`2 `d8wpAA8yC-%N`x3 dx wpXX8yS-%N`: W`xpJx` wp@@8yS-<%N`: `` wp@@8yC->%N`E; `d wpAA8yC-%N`3< dxzwpXX8yS-%N`C W`xZx`wp@@8yS-<%N`C ``wp@@8yC->%N` D `dwpAA8yC-%N`D d`%wp@@8yS-<%N`eL `xw"wpXX@8yS-%N`P W`xjx`"wp@@@8yC->%N`P `d#wpAA8yC-%N`Q dx+wpXX8yS-%N`AY W`xzx`+wp@@@8yS-<%N`OY ``Q+wp@@8yC->%N`Y `dZ,wpAA@8yC-%N`Z dx3wpXX8yS-%N`b W`xx`3wp@@@8yS-<%N`b ``4wp@@8yC->%N`Sb `d(5wpAA@8yC-%N`hc dx%N`k `d=wpAA8yC-%N`4l dx]EwpXX8yS-%N`s W`xx`mEwp@@8yS-<%N`s ``Ewp@@8yC->%N`s `dFwpAA8yC-%N`t dx[wpXX@8yS-%N` W`xx`[wp@@8yS-<%N` ``[wp@@@8yC->%N` `d\wpAA8yC-%N` dxzdwpXX@8yS-%N` W`xx`dwp@@8yS-<%N`ʒ ``dwp@@@8yC->%N` `dewpAA8yC-%N`< dxmwpXX8yS-%N`_ W`yx`-mwp@@@8yS-<%N`m ``smwp@@8yC->%N` `dynwpAA@8yC-%N` dxuwpXX8yS-%N`ԣ W`yx`uwp@@8yS-<%N` ``uwp@@8yC->%N` `dvwpAA8yC-%N`6 dx~wpXX8yS-%N`լ W`y x`~wp@@8yS-<%N` ``~wp@@8yC->%N`' `dwpAA8yC-%N`6 dxOwpXX@8yS-%N` W`y0 x`_wp@@8yS-<%N` ``wp@@@8yC->%N`ߵ `dwpAA8yC-%N` dxwpXX8yS-%N`D W`y@x`wp@@@8yS-<%N`R ``bwp@@8yC->%N` `dtwpAA@8yC-%N` dxwpXX8yS-%N` W`yP+x`wp@@@8yS-<%N` ``wp@@8yC->%N`" `dwpAA@8yC-%N`1 dxCwpXX8yS-%N` W`y`;x`Rwp@@8yS-<%N` ``qwp@@8yC->%N` `d_wpAA8yC-%N` dx۩wpXX8yS-%N` W`ypKx`wp@@8yS-<%N`) ``+wp@@8yC->%N`k `dCwpAA8yC-%N` dxwpXX@8yS-%N` W`y[x`wp@@8yS-<%N` ``wp@@@8yC->%N`@ `dwpAA8yC-%N`_ dxpwpXX@8yS-%N` W`ykx`~wp@@8yS-<%N` ``޻wp@@@8yC->%N` `d˼wpAA8yC-%N` dx4wpXX8yS-%N`t W`y{x`Bwp@@@8yS-<%N` ``mwp@@8yC->%N` `dwpAA@8yC-%N` dxwpXX8yS-%N`< W`yx` wp@@8yS-<%N`J ``Nwp@@8yC->%N` `dcwpAA8yC-%N` dxwpXX8yS-%N` W`yx`wp@@8yS-<%N`/``!wp@@8yC->%N`a`dwpAA8yC-%N`OdxwpXX@8yS-%N` W`yx`wp@@8yS-<%N` ``wp@@@8yC->%N` `dwpAA8yC-%N`+dx%wpXXzS-%N`eW`yx`7wp@@zS-<%N`w``_wp@@zC->%N``dwpAAzC-%N`dxwpXXzS-%N`3W`yx`wp@@zS-<%N`C``'wp@@zC->%N`g`d,wpAAzC-%N`ldxwpXXzS-%N`&W`zx`wp@@zS-<%N`'``wp@@zC->%N`Y'`d7wpAAzC-%N`w(dx:xpXXzS-%N`z/W`zx`Gxp@@@zS-<%N`/``xp@@zC->%N`/`dxpAA@zC-%N`0dx xpXXzS-%N`G8W`z x` xp@@zS-<%N`T8``n xp@@zC->%N`8`dm xpAAzC-%N`9dxxpXXzS-%N`AW`z0 x`xp@@zS-<%N`A``xp@@zC->%N`8A`d/xpAAzC-%N`oBdx{xpXXzS-%N`IW`z@x`xp@@zS-<%N`I``xp@@zC->%N`I`dxpAAzC-%N`@Kdx@$xpXXzS-%N`RW`zP,x`M$xp@@zS-<%N`R``$xp@@zC->%N`R`d%xpAAzC-%N`Sdx-xpXX@zS-%N`D[W`z`<x`-xp@@zS-<%N`R[``i-xp@@@zC->%N`[`du.xpAAzC-%N`\dx5xpXXzS-%N`cW`zpLx`5xp@@zS-<%N`c``5xp@@zC->%N`d`d6xpAAzC-%N`=edx\>xpXXzS-%N`lW`z\x`k>xp@@zS-<%N`l``>xp@@zC->%N`l`d?xpAAzC-%N`nd`%N`<`dYxpAAzC-%N`>dxbaxpXXzS-%N`W`z|x`oaxp@@@zS-<%N```axp@@zC->%N`я`dbxpAA@zC-%N` dx$jxpXXzS-%N`dW`zx`1jxp@@zS-<%N`q``jxp@@zC->%N`ǘ`dkxpAAzC-%N`ݙdxrxpXXzS-%N`W`zx`rxp@@zS-<%N`,``Esxp@@zC->%N``dGtxpAAzC-%N`dx{xpXXzS-%N`ةW`zx`{xp@@zS-<%N```{xp@@zC->%N`<`d|xpAAzC-%N`&dxTxpXXzS-%N`W`zx`axp@@zS-<%N```xp@@zC->%N``dxpAAzC-%N`dxxpXXzS-%N`[W`zx`)xp@@@zS-<%N`i``Rxp@@zC->%N``dxpAA@zC-%N`мdxڕxpXXzS-%N`W`{x`xp@@zS-<%N`&``9xp@@zC->%N`y`d%N`.`dxpAAzC-%N`)dxExpXXzS-%N`W`{ x`Rxp@@zS-<%N```xp@@zC->%N``dxpAAzC-%N`dxxpXXzS-%N`?W`{0 x` xp@@zS-<%N`M``-xp@@zC->%N`m`dlxpAAzC-%N`dxxpXX@zS-%N`W`{@x`˸xp@@zS-<%N` ``xp@@@zC->%N`]`d&xpAAzC-%N`fdxIxpXXzS-%N`W`{P-x`Vxp@@zS-<%N```xp@@zC->%N``dxpAAzC-%N`dxxpXXzS-%N`AW`{`=x` xp@@zS-<%N`M```xp@@zC->%N``d]xpAAzC-%N`dxxpXXzS-%N`#W`{pMx`xp@@zS-<%N`.``8xp@@zC->%N`x`d-xpAAzC-%N`mdxxpXXzS-%N` W`{]x`xp@@@zS-<%N`" ``7xp@@zC->%N`w `dSxpAA@zC-%N` dxxpXXzS-%N`W`{mx`xp@@zS-<%N```xp@@zC->%N`7`dxpAAzC-%N`2dx^xpXXzS-%N`W`{}x`kxp@@zS-<%N```xp@@zC->%N``dxpAAzC-%N`dxxpXXzS-%N`R$W`{x`xp@@zS-<%N`_$``sxp@@zC->%N`$`dqxpAAzC-%N`%dxxpXXzS-%N`-W`{x`xp@@zS-<%N` -``xp@@zC->%N`@-`dTypAAzC-%N`.dxypXXzS-%N`5W`{x`yp@@@zS-<%N`5``yp@@zC->%N`.6`dypAA@zC-%N`27d`Typ@@zS-<%N`>`x[ypXXzS-%N`[ W`{x`yp@@zC->%N` `dypAAzC-%N` dx%ypXXzS-%N`W`{x`%yp@@zS-<%N``` &yp@@zC->%N` `d1'ypAAzC-%N`1dxf.ypXXzS-%N`fW`|x`s.yp@@zS-<%N`s``.yp@@zC->%N``d/ypAAzC-%N`dx7ypXXzS-%N`#W`|x`,7yp@@zS-<%N`,#``7yp@@zC->%N`#`d8ypAAzC-%N`$dx?ypXX@zS-%N`+W`| x`?yp@@zS-<%N`+``?yp@@@zC->%N`+`d@ypAAzC-%N`,dxgHypXXzS-%N`g4W`|0x`vHyp@@zS-<%N`v4``Hyp@@zC->%N`4`dIypAAzC-%N`5dxQypXXzS-%N`=W`|@x`)Qyp@@zS-<%N`)=``Qyp@@zC->%N`=`dtRypAAzC-%N`t>dxYypXXzS-%N`EW`|P.x`Yyp@@zS-<%N`E``.Zyp@@zC->%N`.F`d![ypAAzC-%N`!GdxnbypXXzS-%N`nNW`|`>x`|byp@@@zS-<%N`|N``byp@@zC->%N`N`dcypAA@zC-%N`Od`5kyp@@zS-<%N`5W`xsypXXzS-%N`_W`|pNx`syp@@zC->%N`_`duypAAzC-%N`adx{ypXXzS-%N`gW`|^x`{yp@@zS-<%N`g``{yp@@zC->%N`g`d }ypAAzC-%N` idxNypXXzS-%N`NpW`|nx`\yp@@zS-<%N`\p``yp@@zC->%N`p`dypAAzC-%N`qdx܌ypXXzS-%N`xW`|~x`yp@@zS-<%N`x``>yp@@zC->%N`>y`dypAAzC-%N`zdxypXXzS-%N`W`|x`yp@@@zS-<%N``` yp@@zC->%N` `dypAA@zC-%N`dxEypXXzS-%N`EW`|x`Xyp@@zS-<%N`X``uyp@@zC->%N`u`dypAAzC-%N`dxypXXzS-%N`W`|x`yp@@zS-<%N```yp@@zC->%N``dypAAzC-%N`dxeypXXzS-%N`eW`|x`ryp@@zS-<%N`r``ȯyp@@zC->%N`ț`dʰypAAzC-%N`ʜdx$ypXXzS-%N`$W`|x`1yp@@zS-<%N`1``yp@@zC->%N``dvypAAzC-%N`vdx ypXX8yS-%N` W`}x`yp@@@8yS-<%N```iyp@@8yC->%N`i`dwypAA@8yC-%N`wdxypXX8yS-%N`W`}x`yp@@@8yS-<%N```$yp@@8yC->%N`$`d2ypAA@8yC-%N`2dxuypXX8yS-%N`uW`} x`yp@@8yS-<%N```yp@@8yC->%N``dypAA8yC-%N`dxnypXX8yS-%N`nW`}0x`yp@@8yS-<%N```yp@@8yC->%N``dypAA8yC-%N`dxypXX@8yS-%N`W`}@x`yp@@8yS-<%N```>yp@@@8yC->%N`>`dMypAA8yC-%N`MdxtypXX@8yS-%N`tW`}P/x`yp@@8yS-<%N```yp@@@8yC->%N``dypAA8yC-%N`dx zpXX8yS-%N` W`}`?x`&zp@@@8yS-<%N`&``gzp@@8yC->%N`g`dgzpAA@8yC-%N`gdx zpXX8yS-%N`W`}pOx` zp@@8yS-<%N```. zp@@8yC->%N`.`d zpAA8yC-%N`dxzpXX8yS-%N`W`}_x`zp@@8yS-<%N```zp@@8yC->%N``dzpAA8yC-%N`dxIzpXX@zS-%N`IW`}ox`Yzp@@zS-<%N`Y``zp@@@zC->%N``dzpAAzC-%N` dx$zpXX@8yS-%N`W`}x`%zp@@8yS-<%N```?%zp@@@8yC->%N`?`dL&zpAA8yC-%N`Ldxy-zpXXzS-%N`yW`}x`-zp@@zS-<%N```-zp@@zC->%N``d.zpAAzC-%N`dx?6zpXXzS-%N`?"W`}x`M6zp@@zS-<%N`M"``v6zp@@zC->%N`v"`d7zpAAzC-%N`#dx?zpXXzS-%N`+W`}x`?zp@@zS-<%N`+``o?zp@@zC->%N`o+`ds@zpAAzC-%N`s,dxGzpXXzS-%N`3W`}x`Gzp@@@zS-<%N`3``Gzp@@zC->%N`3`d IzpAA@zC-%N` 5dxuPzpXXzS-%N`u<W`}x`Pzp@@zS-<%N`<``Pzp@@zC->%N`<`dQzpAAzC-%N`=dx3YzpXXzS-%N`3EW`~x`AYzp@@zS-<%N`AE``Yzp@@zC->%N`E`dZzpAAzC-%N`FdxazpXXzS-%N`MW`~x` bzp@@zS-<%N` N``bzp@@zC->%N`N`dRczpAAzC-%N`ROdxjzpXXzS-%N`VW`~ x`jzp@@zS-<%N`V``kzp@@zC->%N`W`dlzpAAzC-%N`XdxlszpXXzS-%N`l_W`~0x`xszp@@@zS-<%N`x_``szp@@zC->%N`_`dtzpAA@zC-%N``dx4|zpXXzS-%N`4hW`~@ x`A|zp@@zS-<%N`Ah``|zp@@zC->%N`h`d}zpAAzC-%N`idxzpXX8yS-%N`pW`~P0x`zp@@@8yS-<%N`p``8zp@@8yC->%N`8q`d_zpAA@8yC-%N`_rdxzpXX8yS-%N`yW`~`@x`zp@@@8yS-<%N`y``zp@@8yC->%N`z`dzpAA@8yC-%N`{dxzpXX8yS-%N`W`~pPx`zp@@8yS-<%N```Ozp@@8yC->%N`O`dZzpAA8yC-%N`ZdxzpXX8yS-%N`W`~`x`ʞzp@@8yS-<%N`ʊ``zp@@8yC->%N``dzpAA8yC-%N`dxzpXX@8yS-%N`W`~px`zp@@8yS-<%N```֧zp@@@8yC->%N`֓`dzpAA8yC-%N`dxRzpXX@8yS-%N`RW`~x`dzp@@8yS-<%N`d``zp@@@8yC->%N``dzpAA8yC-%N`dxezpXXzS-%N`eW`~x`szp@@zS-<%N`s``zp@@zC->%N``dzpAAzC-%N`dx3zpXXzS-%N`3W`~x`Dzp@@zS-<%N`D``czp@@zC->%N`c`dzpAAzC-%N`dxzpXXzS-%N`W`~x`zp@@zS-<%N```Ezp@@zC->%N`E`dJzpAAzC-%N`JdxzpXX@zS-%N`W`~x`zp@@zS-<%N```zp@@@zC->%N``dzpAAzC-%N`dx3zpXXzS-%N`3W`~x`Dzp@@zS-<%N`D``jzp@@zC->%N`j`dzpAAzC-%N`dxzpXXzS-%N`W`x`zp@@zS-<%N```ezp@@zC->%N`e`dzpAAzC-%N`dxzpXXzS-%N`W`x`zp@@zS-<%N```zp@@zC->%N``dzpAAzC-%N`dxJ{pXXzS-%N`JW` x`Y{p@@@zS-<%N`Y``{p@@zC->%N``d{pAA@zC-%N`dx {pXXzS-%N`W`0x` {p@@zS-<%N```f {p@@zC->%N`f`de {pAAzC-%N`edx{pXXzS-%N`W`@!x`{p@@zS-<%N```{p@@zC->%N``d({pAAzC-%N`(dxV{pXXzS-%N`V W`P1x`c{p@@zS-<%N`c ``{p@@zC->%N` `d{pAAzC-%N` dx&{pXXzS-%N`W``Ax`&&{p@@zS-<%N`&``F&{p@@zC->%N`F`d~'{pAAzC-%N`~d`.{p@@zS-<%N``x2{pXX@zS-%N`W`pQx`.3{p@@@zC->%N`.`d?4{pAAzC-%N`? dx;{pXXzS-%N`'W`ax`;{p@@zS-<%N`'``;{p@@zC->%N`'`d ={pAAzC-%N` )dxzD{pXXzS-%N`z0W`qx`D{p@@zS-<%N`0``D{p@@zC->%N`0`dE{pAAzC-%N`1dxM{pXXzS-%N`9W`x`M{p@@zS-<%N`9``M{p@@zC->%N`9`dfN{pAAzC-%N`f:dxU{pXXzS-%N`AW`x`U{p@@zS-<%N`A``U{p@@zC->%N`A`d/W{pAAzC-%N`/Cdx^{pXX@zS-%N`JW`x`^{p@@zS-<%N`J``^{p@@@zC->%N`J`d_{pAAzC-%N`KdxQg{pXXzS-%N`QSW`x`hg{p@@zS-<%N`hS``g{p@@zC->%N`S`dh{pAAzC-%N`Tdxo{pXXzS-%N`[W`x`o{p@@zS-<%N`[``o{p@@zC->%N`[`dq{pAAzC-%N`]dxx{pXXzS-%N`dW`x`(x{p@@zS-<%N`(d``Gx{p@@zC->%N`Gd`dʔ{pAAzC-%N`ʀdx{pXXzS-%N`W`x`{p@@zS-<%N```֙{p@@zC->%N`օ`d {pAAzC-%N` dx{pXX@zS-%N`W`x`{p@@zS-<%N```${p@@@zC->%N`$`d {pAAzC-%N` dxf{pXXzS-%N`fW` x`{{p@@zS-<%N`{``{p@@zC->%N``d{pAAzC-%N`dx5{pXXzS-%N`5W`0x`H{p@@zS-<%N`H``g{p@@zC->%N`g`d{pAAzC-%N`dx{pXXzS-%N`W`@"x`{p@@zS-<%N```{p@@zC->%N``d{pAAzC-%N`d`^{p@@@zS-<%N`^`x {pXXzS-%N` W`P2x`B{p@@zC->%N`B`dm{pAA@zC-%N`mdx{pXXzS-%N`W``Bx`{p@@zS-<%N```{p@@zC->%N``d{pAAzC-%N`dx{pXXzS-%N`W`pRx`{p@@zS-<%N```{p@@zC->%N``d{pAAzC-%N`dxX{pXXzS-%N`XW`bx`h{p@@zS-<%N`h``{p@@zC->%N``d{pAAzC-%N`dx{pXXzS-%N`W`rx`{p@@zS-<%N```{p@@zC->%N``d={pAAzC-%N`=dx{pXX@zS-%N`W`x`{p@@zS-<%N```{p@@@zC->%N``d>{pAAzC-%N`>dx_|pXXzS-%N`_W`x`o|p@@zS-<%N`o``|p@@zC->%N``d|pAAzC-%N`dx|pXXzS-%N`W`x` |p@@zS-<%N``` |p@@zC->%N``d3 |pAAzC-%N`3dx|pXXzS-%N`W`x`|p@@zS-<%N```|p@@zC->%N``d|pAAzC-%N`dx|pXX}DwS-%N`W`x`&|p@@@uDwS-<%N`&``H|p@@}DwC->%N`H`dh|pAA@uDwC-%N`hdx"|pXXpDwS-%N`W`x`"|p@@zDwS-<%N```"|p@@pDwC->%N``dJ$|pAAzDwC-%N`Jdx8|pXX'zS-%N`$W`x`8|p@@'zS-<%N`$``8|p@@'zC->%N`$`d#:|pAA'zC-%N`#&dxnA|pXX@'zS-%N`n-W`x`{A|p@@'zS-<%N`{-``A|p@@@'zC->%N`-`dB|pAA'zC-%N`.dx#J|pXX'zS-%N`#6W` x`1J|p@@'zS-<%N`16``SJ|p@@'zC->%N`S6`dyK|pAA'zC-%N`y7dxR|pXX'zS-%N`>W`0x`R|p@@'zS-<%N`>``R|p@@'zC->%N`>`d(T|pAA'zC-%N`(@dxC[|pXX@'zS-%N`CGW`@#x`U[|p@@'zS-<%N`UG``z[|p@@@'zC->%N`zG`d\|pAA'zC-%N`Hdxc|pXX'zS-%N`OW`P3x`c|p@@'zS-<%N`O``d|p@@'zC->%N`P`dYe|pAA'zC-%N`YQdxl|pXX@'zS-%N`XW``Cx`l|p@@'zS-<%N`X``l|p@@@'zC->%N`X`dm|pAA'zC-%N`Ydxt|pXX'zS-%N``W`pSx`u|p@@'zS-<%N`a``#u|p@@'zC->%N`#a`d_v|pAA'zC-%N`_bdx}|pXX'zS-%N`iW`cx`}|p@@'zS-<%N`i``}|p@@'zC->%N`i`d|pAA'zC-%N`kdxg|pXX@'zS-%N`grW`sx`q|p@@'zS-<%N`qr``|p@@@'zC->%N`r`dƇ|pAA'zC-%N`sdxV|pXX'zS-%N`V{W`x`_|p@@'zS-<%N`_{``~|p@@'zC->%N`~{`d|pAA'zC-%N`|dx@|pXX@'zS-%N`@W`x`H|p@@'zS-<%N`H``h|p@@@'zC->%N`h`d|pAA'zC-%N`dx*|pXX'zS-%N`*W`x`2|p@@'zS-<%N`2``R|p@@'zC->%N`R`dn|pAA'zC-%N`ndx|pXX'zS-%N`W`x`|p@@'zS-<%N```8|p@@'zC->%N`8`d>|pAA'zC-%N`>dx|pXX@'zS-%N`W`x`|p@@'zS-<%N```%|p@@@'zC->%N`%`dI|pAA'zC-%N`Idx|pXX'zS-%N`W`x`|p@@'zS-<%N```|p@@'zC->%N``d|pAA'zC-%N`dx|pXX@'zS-%N`W`x`|p@@'zS-<%N```|p@@@'zC->%N``d|pAA'zC-%N`dxj|pXX'zS-%N`jW`x`r|p@@'zS-<%N`r``|p@@'zC->%N``d|pAA'zC-%N`غdxT|pXX'zS-%N`TW` x`]|p@@'zS-<%N`]``||p@@'zC->%N`|`d|pAA'zC-%N`dxE|pXX@'zS-%N`EW`0x`N|p@@'zS-<%N`N``l|p@@@'zC->%N`l`d|pAA'zC-%N`dx|pXX'zS-%N`W`@$x`|p@@'zS-<%N```|p@@'zC->%N``dO|pAA'zC-%N`Odx|pXX@'zS-%N`W`P4x`|p@@'zS-<%N```|p@@@'zC->%N``d|pAA'zC-%N`dxk|pXX'zS-%N`kW``Dx`t|p@@'zS-<%N`t``|p@@'zC->%N``d|pAA'zC-%N`dx:}pXX'zS-%N`:W`pTx`G}p@@'zS-<%N`G``p}p@@'zC->%N`p`d}pAA'zC-%N`dx }pXX@'zS-%N`W`dx` }p@@'zS-<%N```% }p@@@'zC->%N`%`dZ }pAA'zC-%N`Zdx}pXX'zS-%N`W`tx`}p@@'zS-<%N```}p@@'zC->%N``d}pAA'zC-%N`dxr}pXX@'zS-%N`rW`x`}p@@'zS-<%N```}p@@@'zC->%N``d}pAA'zC-%N` dxg%}pXXvDwS-%N`gW`x`t%}p@@yDwS-<%N`t``%}p@@vDwC->%N``d&}pAAyDwC-%N`dx9.}pXX@~DwS-%N`9W`x`F.}p@@vDwS-<%N`F``t.}p@@@~DwC->%N`t`d/}pAAvDwC-%N`dx7}pXXvDwS-%N`#W`x`7}p@@@~DwS-<%N`#``<7}p@@vDwC->%N`<#`dg8}pAA@~DwC-%N`g$dx?}pXXyDwS-%N`+W`x`?}p@@vDwS-<%N`+``?}p@@yDwC->%N`+`d A}pAAvDwC-%N` -dxH}pXXzDwS-%N`4W`x`H}p@@pDwS-<%N`4``H}p@@zDwC->%N`4`dI}pAApDwC-%N`5dxQ}pXX@uDwS-%N`=W`x`#Q}p@@}DwS-<%N`#=``HQ}p@@@uDwC->%N`H=`d~R}pAA}DwC-%N`~>dxY}pXXwDwS-%N`EW`x`Y}p@@@rDwS-<%N`E``Y}p@@wDwC->%N`E`dZ}pAA@rDwC-%N`Fdx*b}pXXDwS-%N`*NW` x`3b}p@@@{DwS-<%N`3N``gb}p@@DwC->%N`gN`dc}pAA@{DwC-%N`Odxj}pXX@{DwS-%N`VW`0x`j}p@@DwS-<%N`V``j}p@@@{DwC->%N`V`dk}pAADwC-%N`Wdx2s}pXX@rDwS-%N`2_W`@%x`?s}p@@wDwS-<%N`?_``gs}p@@@rDwC->%N`g_`dt}pAAwDwC-%N``dx|}pXX}DwS-%N`hW`P5x`,|}p@@@uDwS-<%N`,h``W|}p@@}DwC->%N`Wh`dn}}pAA@uDwC-%N`nidxՄ}pXXpDwS-%N`pW``Ex`}p@@zDwS-<%N`p`` }p@@pDwC->%N` q`d$}pAAzDwC-%N`$rdxk}pXXvDwS-%N`kyW`pUx`}p@@yDwS-<%N`y``}p@@vDwC->%N`y`d}pAAyDwC-%N`zdx}pXX@~DwS-%N`W`ex`}p@@vDwS-<%N```ޖ}p@@@~DwC->%N`ނ`d }pAAvDwC-%N` dx}pXXvDwS-%N`W`ux`&}p@@@~DwS-<%N`&``d}p@@vDwC->%N`d`dW}pAA@~DwC-%N`Wdxڧ}pXXyDwS-%N`ړW`x`}p@@vDwS-<%N```2}p@@yDwC->%N`2`d#}pAAvDwC-%N`#dx}pXXzDwS-%N`W`x`}p@@pDwS-<%N```}p@@zDwC->%N``d}pAApDwC-%N`dx}pXX@uDwS-%N`W`x`}p@@}DwS-<%N```U}p@@@uDwC->%N`U`d}}pAA}DwC-%N`}dx}pXXwDwS-%N`W`x`}p@@@rDwS-<%N```}p@@wDwC->%N``d}pAA@rDwC-%N`dxh}pXXDwS-%N`hW`x`w}p@@@{DwS-<%N`w``}p@@DwC->%N``d}pAA@{DwC-%N`зdx.}pXX@{DwS-%N`.W`x`<}p@@DwS-<%N`<``l}p@@@{DwC->%N`l`d}pAADwC-%N`dx}pXX@rDwS-%N`W`x`}p@@wDwS-<%N```5}p@@@rDwC->%N`5`dK}pAAwDwC-%N`Kdx}pXX}DwS-%N`W`x`}p@@@uDwS-<%N```}p@@}DwC->%N``d}pAA@uDwC-%N`dx'}pXXpDwS-%N`'W` x`5}p@@zDwS-<%N`5``U}p@@pDwC->%N`U`d}pAAzDwC-%N`d`}p@@vDwS-<%N``x}pXXyDwS-%N`W`0x`}p@@yDwC->%N``d}pAAvDwC-%N`dxZ~pXX@~DwS-%N`ZW`@&x`m~p@@vDwS-<%N`m``~p@@@~DwC->%N``d~pAAvDwC-%N`dx1~pXXvDwS-%N`1W`P6x`A~p@@@~DwS-<%N`A``r~p@@vDwC->%N`r`d~pAA@~DwC-%N`dx~pXXvDwS-%N`W``Fx`~p@@yDwS-<%N```;~p@@vDwC->%N`;`dg~pAAyDwC-%N`gdx!~pXXzDwS-%N` W`pVx`!~p@@pDwS-<%N` `` "~p@@zDwC->%N` `d3#~pAApDwC-%N`3dx*~pXX@uDwS-%N`W`fx`*~p@@}DwS-<%N```*~p@@@uDwC->%N``d,~pAA}DwC-%N`dx_3~pXX'zS-%N`_W`vx`s3~p@@'zS-<%N`s``3~p@@'zC->%N``d4~pAA'zC-%N` dx!<~pXXwDwS-%N`!(W`x`4<~p@@@rDwS-<%N`4(``U<~p@@wDwC->%N`U(`dn=~pAA@rDwC-%N`n)dxD~pXXDwS-%N`0W`x`D~p@@@{DwS-<%N`0``E~p@@DwC->%N`1`d&F~pAA@{DwC-%N`&2dxM~pXX@%N`:`d#O~pAA%N`B`dW~pAA%N`L`da~pAA%N`}T`di~pAA@%N`B]`dr~pAA%N`e`dz~pAA%N`n`d~pAA%N`mw`d~pAA%N`K`d~pAA%N`e`d~pAA%N`%`d;~pAA%N`ޙ`d~pAA@%N``d~pAA%N`e`d~pAA%N``d#~pAA%N`Ӽ`d~pAA%N``d~pAA%N`Z`dv~pAA%N`=`d)~pAA%N``d~pAA@%N``d~pAA%N`2`d2pAA%N``d pAA%N``dpAA%N` `dpAA%N`j`d(pAA%N`4`d1pAA%N`$`d :pAA@%N`-`dBpAA%N`v6`doKpAAW`x`Rp@@``9Sp@@%N`9?`d6TpAA%N`G`d ]pAA%N`P`depAA%N`@Y`dDnpAA%N`b`dvpAA%N`j`dpAA@%N`{s`d~pAA%N`7|`d=pAA%N``dpAA%N`̛`dpAA%N``dpAA%N`1`dpAA%N` `dpAA%N`ƾ`dpAA@%N``dpAA%N`B`d`pAA%N``dpAA%N``dpAA%N`.`dHpAA%N``dpAA%N``dpAA%N`^`d4pAA@%N`Z&`dv;pAA%N`2`dGpAA%N`[;`duPpAA%N`D`dYpAA%N`L`dapAA%N`WU`dqjpAA%N`]`drpAA%N`f`d{pAA@%N`mo`dypAA'zC-%N`ypdx܋pXX'zS-%N`wW`zx`ꋀp@@'zS-<%N`w``5p@@'zC->%N`5x`dApAA'zC-%N`AydxpXX@'zS-%N`W`x`p@@'zS-<%N```Mp@@@'zC->%N`M`dVpAA'zC-%N`VdxpXX%N``dhpAA%N``d촀pAA%N``dpAA'zC-%N`dxĀpXX@'zS-%N`W`x`Āp@@'zS-<%N```Bŀp@@@'zC->%N`B`dQƀpAA'zC-%N`Qdx̀pXX'zS-%N`W`x`̀p@@'zS-<%N`͹``΀p@@'zC->%N``d+πpAA'zC-%N`+dxրpXX'zS-%N`W`x`րp@@'zS-<%N```րp@@'zC->%N``d؀pAA'zC-%N`dx"߀pXX@'zS-%N`"W`x`0߀p@@'zS-<%N`0``r߀p@@@'zC->%N`r`dpAA'zC-%N`dxpXX'zS-%N`W`  x`p@@'zS-<%N``` p@@'zC->%N` `d#pAA'zC-%N`#dxpXX@'zS-%N`W`0x`p@@'zS-<%N```p@@@'zC->%N``dpAA'zC-%N`dx_pXX'zS-%N`_W`@+x`lp@@'zS-<%N`l``p@@'zC->%N``dpAA'zC-%N`dx&pXX'zS-%N`&W`P;x`5p@@'zS-<%N`5``up@@'zC->%N`u`dpAA'zC-%N`dx pXX@'zS-%N`W``Kx` p@@'zS-<%N` ``L p@@@'zC->%N`L`dc pAA'zC-%N`cdxpXX'zS-%N`W`p[x`p@@'zS-<%N``` p@@'zC->%N` `d"pAA'zC-%N`"dxpXX@'zS-%N`W`kx`p@@'zS-<%N```p@@@'zC->%N``dpAA'zC-%N` dxL%pXX'zS-%N`LW`{x`Y%p@@'zS-<%N`Y``%p@@'zC->%N``d&pAA'zC-%N`dx-pXX'zS-%N`W`x`-p@@'zS-<%N```U.p@@'zC->%N`U`da/pAA'zC-%N`adx6pXX@'zS-%N`"W`x`6p@@'zS-<%N`"``6p@@@'zC->%N`"`d7pAA'zC-%N`#dx_?pXX'zS-%N`_+W`x`l?p@@'zS-<%N`l+``?p@@'zC->%N`+`d@pAA'zC-%N`,dxHpXX@'zS-%N`4W`x`+Hp@@'zS-<%N`+4``kHp@@@'zC->%N`k4`dkIpAA'zC-%N`k5dx\pXX'zS-%N`HW`x`,\p@@'zS-<%N`,H``v\p@@'zC->%N`vH`du]pAA'zC-%N`uIdxdpXX'zS-%N`PW`x`dp@@'zS-<%N`P``9ep@@'zC->%N`9Q`dDfpAA'zC-%N`DRdxmpXX@'zS-%N`YW`x`mp@@'zS-<%N`Y`` np@@@'zC->%N` Z`dopAA'zC-%N`[dxvpXX'zS-%N`bW`x`vp@@'zS-<%N`b``vp@@'zC->%N`b`dwpAA'zC-%N`cdx~pXX@'zS-%N`jW`  x`~p@@'zS-<%N`j``#p@@@'zC->%N`#k`d%N`s`d߈pAA'zC-%N`tdxspXX%N`|`d⑁pAA%N``dpAA%N``dpAA%N`Z`dCpAA%N``dpAA%N`A`dIpAA@%N``dƁpAA%N``d΁pAA%N`E`d@ׁpAA%N``d߁pAA%N`j`dkpAA%N``dpAA%N`j`dqpAA%N``dpAA@%N`z`dU pAA%N``dpAA%N` `dpAA%N` `d$pAA+p@@ ``}+p@@@%N`} `dL,pAA%N` `d5pAA%N`( `d=pAA%N`1 `dFpAA@%N`s9 `dzNpAA%N`dB `dYWpAA%N`K `d?`pAA%N`8T `dipAA%N`sa `dpvpAA@%N`j `dpAA%N` `dpAA%N` `dYpAA%N`! `dpAA%N`` `dpAA%N`ʧ `d pAAx`Âp@@%N`) `d-łpAA%N` `d΂pAA@%N` `d ׂpAA%N` `dpAA%N`u `dpAA%N` `dKpAA%N`[ `dwpAA%N` `dpAA%N` `d pAA%N`N `dpAA'zC-%N` dxpXX:wS-%N` W`x`p@@@:wS-<%N` ``p@@:wC->%N` `d pAA@:wC-%N` dx%pXX:wS-%N` W`x`#%p@@:wS-<%N`# ``f%p@@:wC->%N`f `dd&pAA:wC-%N`d dxx-pXX:wS-%N`x W`x`-p@@:wS-<%N` ``-p@@:wC->%N` `d.pAA:wC-%N` dx6pXX:wS-%N`" W` x`6p@@:wS-<%N`" ``P6p@@:wC->%N`P" `dN7pAA:wC-%N`N# dxKpXX%N`8 `dMpAA@%N`@ `dUpAA%N`I `d^pAA%N`R `dgpAA%N`[ `dppAA%N`,d `dBypAA%N`l `dpAA%N`u `dpAA%N`<~ `dKpAA@%N`І `dpAA%N`~ `dpAA%N`T `dWpAA%N` `d&pAA%N`U `d\ʃpAA:wC-%N`\ dxуpXX@:wS-%N` W`x`уp@@:wS-<%N` ``уp@@@:wC->%N` `dӃpAA:wC-%N` dxiڃpXX:wS-%N`i W` x`xڃp@@@:wS-<%N`x ``ڃp@@:wC->%N` `dۃpAA@:wC-%N` dx*pXX:wS-%N`* W`0 x`:p@@:wS-<%N`: ``cp@@:wC->%N`c `dpAA:wC-%N` dxpXX:wS-%N` W`@0x`p@@:wS-<%N` ``p@@:wC->%N` `dpAA:wC-%N` dxCpXX:wS-%N`C W`P@x`Rp@@:wS-<%N`R ``sp@@:wC->%N`s `dpAA:wC-%N` dxpXX:wS-%N` W``Px`p@@:wS-<%N` ``@p@@:wC->%N`@ `dhpAA:wC-%N`h dxpXX@:wS-%N` W`p`x`p@@:wS-<%N` ``p@@@:wC->%N` `dpAA:wC-%N` dxpXX:wS-%N` W`px`p@@@:wS-<%N` ``p@@:wC->%N` `dpAA@:wC-%N` dxpXX:wS-%N` W`x`)p@@:wS-<%N`) ``p@@:wC->%N` `d_pAA:wC-%N`_ dxpXX:wS-%N` W`x`p@@:wS-<%N` `` p@@:wC->%N` `d,!pAA:wC-%N`, dx(pXX:wS-%N` W`x`(p@@:wS-<%N` ``(p@@:wC->%N` `d*pAA:wC-%N` dxg1pXX:wS-%N`g W`x`v1p@@:wS-<%N`v ``1p@@:wC->%N` `d2pAA:wC-%N` dx9pXX@:wS-%N`% W`x`9p@@:wS-<%N`% ``9p@@@:wC->%N`% `d';pAA:wC-%N`'' dxoBpXX:wS-%N`o. W`x`}Bp@@@:wS-<%N`}. ``Bp@@:wC->%N`. `dCpAA@:wC-%N`/ dxJpXX%N`$7 `dQLpAA%N`@ `dTpAA%N`H `d]pAA%N`3Q `dxfpAA@%N`HZ `dWopAA%N`q `dpAA%N`xz `dpAA%N`7 `dQpAA%N`, `dpAA%N` `dpAA%N`e `dMpAA:wC-%N`M dxùpXX:wS-%N`å W`x`ҹp@@:wS-<%N`ҥ ``p@@:wC->%N` `dpAA:wC-%N` dxj„pXX:wS-%N`j W`x`z„p@@:wS-<%N`z ``„p@@:wC->%N` `dÄpAA:wC-%N` d` ˄p@@:wS-<%N` `xӄpXX:wS-%N` W`x`ӄp@@:wC->%N` `dԄpAA:wC-%N` dxX܄pXX:wS-%N`X W`x`l܄p@@:wS-<%N`l ``܄p@@:wC->%N` `d݄pAA:wC-%N` dxpXX:wS-%N` W`x`(p@@:wS-<%N`( ``Pp@@:wC->%N`P `dpAA:wC-%N` dxpXX:wS-%N` W`x`p@@:wS-<%N` ``Sp@@:wC->%N`S `d| pAA:wC-%N`| dxvpXX%N` `dpAA%N`, `d3pAA@%N` `d!pAA%N` `d*pAA%N`[ `dg3pAA%N`4 `dIpAA%N`o= `dRpAA dxYpXX%N`F `d[pAA%N`O `d&dpAA%N`W `dlpAA@%N`` `dupAA%N`_i `dQ~pAA%N`+r `dOpAA%N`z `dЏpAA%N`w `dzpAA%N`C `dkpAA%N` `d&pAA%N`՝ `dֲpAA@%N` `dpAA%N`d `dbąpAA%N` `dMͅpAA%N` `dՅpAA%N` `dޅpAA%N`X `dxpAA%N`% `d\pAA%N` `dpAA@%N`h `dpAA%N`7 `dK pAA%N` `dpAA%N` `dpAA%N`U `do$pAA%N` `d,-pAA%N`(. `d-CpAA%N`6 `dLpAA@%N`:@ `d#UpAA%N`bH `di]pAA%N`HQ `d]fpAA%N`Y `dnpAA%N`eb `dWwpAA%N`j `dpAA%N`s `d̈pAA%N`h| `dpAA@%N`# `dDpAA%N` `dˢpAA%N`Q `dpAA%N` `d+pAA%N`| `dwpAA%N` `d!ņpAA%N` `d͆pAA%N` `dֆpAA@%N`e `dv߆pAA%N`" `d9pAA%N` `dJpAA%N` `dpAA%N`b `dvpAA%N`%`dpAA%N` `d pAA%N``d$)pAA@%N`%`d:pAA%N`-`dBpAA%N`6`dKpAA%N`C?`dPTpAA%N`G`d\pAA%N`lP`dyepAA%N`\f`df{pAA%N`n`dكpAA@%N`w`dpAA%N`Z`depAA%N``dDpAA:wC-%N`DdxpXX@:wS-%N`W`PFx`p@@:wS-<%N```ĥp@@@:wC->%N`đ`dꦇpAA:wC-%N`dx"pXX:wS-%N`"W``Vx`/p@@@:wS-<%N`/``p@@:wC->%N``d_pAA@:wC-%N`_dx涇pXX:wS-%N`W`pfx`p@@:wS-<%N```Kp@@:wC->%N`K`dNpAA:wC-%N`NdxpXX:wS-%N`W`vx`p@@:wS-<%N```qp@@:wC->%N`q`dpAA:wC-%N`dxȇpXX:wS-%N`ԴW`x`ȇp@@:wS-<%N```7ɇp@@:wC->%N`7`d9ʇpAA:wC-%N`9dxoчpXX:wS-%N`oW`x`~чp@@:wS-<%N`~``чp@@:wC->%N`˽`d҇pAA:wC-%N`Ǿdx+ڇpXX@:wS-%N`+W`x`8ڇp@@:wS-<%N`8``ڇp@@@:wC->%N``dۇpAA:wC-%N`dxpXX:wS-%N`W`x`p@@@:wS-<%N```'p@@:wC->%N`'`d[pAA@:wC-%N`[dxpXX:wS-%N`W`x`p@@:wS-<%N```p@@:wC->%N``dpAA:wC-%N`dxCpXX:wS-%N`CW`x`Rp@@:wS-<%N`R``wp@@:wC->%N`w`dpAA:wC-%N`dxpXX:wS-%N`W`x`'p@@:wS-<%N`'``}p@@:wC->%N`}`dRpAA:wC-%N`RdxpXX:wS-%N`W`x`p@@:wS-<%N```>p@@:wC->%N`>`dJpAA:wC-%N`JdxhpXX@:wS-%N`hW`x`vp@@:wS-<%N`v``p@@@:wC->%N``dpAA:wC-%N`dx3pXX:wS-%N`3W` x`@p@@@:wS-<%N`@``p@@:wC->%N``dpAA@:wC-%N`dx-pXX:wS-%N`W`0'x`-p@@:wS-<%N```-p@@:wC->%N``d.pAA:wC-%N`dx6pXX:wS-%N`"W`@7x`6p@@:wS-<%N`"``6p@@:wC->%N`"`d7pAA:wC-%N`#dxC?pXX:wS-%N`C+W`PGx`Q?p@@:wS-<%N`Q+``?p@@:wC->%N`+`d@pAA:wC-%N`,dxGpXX:wS-%N`3W``Wx`Gp@@:wS-<%N`3``/Hp@@:wC->%N`/4`dIpAA:wC-%N`5dxPpXX@:wS-%N`<W`pgx`Pp@@:wS-<%N`<``Pp@@@:wC->%N`<`dRpAA:wC-%N`>dxSYpXX:wS-%N`W`wx`dYp@@@:wS-<%N`$``Yp@@:wC->%N`G`dZpAA@:wC-%N`adxbpXX:wS-%N` W`x`bp@@:wS-<%N` ``jbp@@:wC->%N`* `dNcpAA:wC-%N` dxjpXX@%N``d"lpAA%N``dupAA%N`F&`d{}pAA%N`.`dpAA%N`h7`dpAA%N`@`dXpAA@%N`H`d?pAA%N`Q`dרpAA%N`FZ`dpAA%N`c`d`pAA%N`k`d6ÈpAAʈp@@%N`Tt`dˈpAA%N`|`d0ԈpAA%N``d܈pAA@%N`G`dpAA%N``d^pAA%N``dpAA%N`^`dpAA%N``d#pAA%N`/`dpAA%N`s`dpAA%N``d-pAA@%N`>`d6pAAp@@%N``dN?pAA%N``d!HpAA%N``dPpAA%N`P`dYpAA%N` `dibpAA%N``d5kpAA%N``dspAA@%N`-`dpAA:wC-%N`.dxtpXX:wS-%N`46W`x`p@@:wS-<%N`B6``ٌp@@:wC->%N`6`dߍpAA:wC-%N`7dxpXX:wS-%N`>W`x`p@@:wS-<%N`>``wp@@:wC->%N`7?`d{pAA:wC-%N`;@dxԝpXX@:wS-%N`GW`x`ᝉp@@:wS-<%N`G``7p@@@:wC->%N`G`d;pAA:wC-%N`HdxpXX:wS-%N`PPW`x`p@@@:wS-<%N`^P``릉p@@:wC->%N`P`dѧpAA@:wC-%N`Qdx*pXX%N`:Y`d{pAA%N`a`dLpAA%N`j`d/݉pAA%N``d8pAA%N``dpAA%N`"`dpAA%N`ۦ`d pAA%N``dpAA@%N`3`dpAA%N`:`d$pAA,p@@@%N``dl-pAA%N``dP6pAA%N``d>pAA%N``dGpAA%N``dhPpAA%N``d-YpAA@%N`) `dapAA%N``dcjpAA%N``drpAA%N`V$`d{pAA%N`(-`dpAA%N`5`dOpAA%N`K`dOpAA%N`T`dpAA@%N`<]`depAA%N`e`d4pAA%N`n`dŊpAA%N`ew`dΊpAA%N``dD׊pAA%N``dpAA%N`c`dpAA%N`:`dpAA@%N`ۢ`d pAA%N```dpAA p@@%N``db pAA%N`ټ`dGpAA%N`z`dpAA%N`A`d%pAA%N``d .pAA%N``d6pAA@pXXp@@p@@%N`V`d?pAA%N``dHpAA%N``dQpAA%N`m`dYpAA%N` `dgbpAA%N``djpAA%N`;`dspAA%N`$`d$|pAA@%N`-`dpAA%N`v6`dÍpAAW`x`Ip@@%N`'?`dXpAA%N`G`dpAA%N`|P`dӧpAA%N`X`dhpAA%N`la`d㸋pAA%N`j`dtpAA@%N`Ar`dɋpAA%N`l`dڋpAA%N``dpAA%N``dpAA``p@@%N`^`dpAA%N`.`dpAA%N``d9pAA%N``d/pAA@%N`=`dpAA%N``dpAA%N``d )pAA%N`V`d1pAA%N``d@:pAA%N``d'CpAA%N``dEYpAA%N` `dՓpAA@%N`M`dpAA%N`hV`d㭌pAA:wC-%N`Wdx/pXX:wS-%N`^Wd0x`>p@@:wS-<%N`^``fp@@:wC->%N`&_`dpAA:wC-%N`M`dx(pXX%N`9h`dzpAAԌp@@:wC->%N`}`dYՌpAA:wC-%N`dx܌pXX:wS-%N`Wd`x`܌p@@:wS-<%N``` ݌p@@:wC->%N`Ɇ`d ތpAA:wC-%N`ʇdxGpXX@:wS-%N`Wdpx`Up@@:wS-<%N```p@@@:wC->%N``dpAA:wC-%N`dxpXX:wS-%N`ЗWdx`p@@@:wS-<%N`ޗ``Cp@@:wC->%N``dqpAA@:wC-%N`1dxpXX:wS-%N`Wdx`p@@:wS-<%N```p@@:wC->%N``dpAA:wC-%N`ӡdxpXX:wS-%N`PWdx`p@@:wS-<%N`m``p@@:wC->%N`~`dpAA:wC-%N`֪dxUpXX:wS-%N`Wdx`cp@@:wS-<%N`#``p@@:wC->%N`I`d pAA:wC-%N`dxpXX:wS-%N`Wd&x` p@@:wS-<%N`˺``^p@@:wC->%N``dspAA:wC-%N`3dxpXX@:wS-%N`Wd6x`p@@:wS-<%N```#p@@@:wC->%N``d%pAA:wC-%N`dx"pXX:wS-%N`EWdFx`"p@@@:wS-<%N`T``"p@@:wC->%N``d$pAA@:wC-%N`dxC+pXX:wS-%N`WdVx`P+p@@:wS-<%N```+p@@:wC->%N`t`d,pAA:wC-%N`Ydx4pXX@%N``dg5pAA%N``d>pAA%N``dFpAA%N`O`dOpAA%N``dyXpAA%N` `d.apAA@hp@@%N`A`dipAA%N` `drpAA%N`#`d){pAA%N`x,`dpAA%N`-5`dOpAA``p@@%N`G>`dpAA%N` G`dYpAA%N`O`dpAA@%N`X`dگpAA%N`?a`dpAA%N`Dj`dpAA%N`s`dAʍpAA%N`{`dӍpAA%N`d`dۍpAA%N`1`dpAA%N``dMpAA@%N``dpAA%N`m`dpAA%N`$`dapAA%N`x`dpAA%N`@`dppAA%N``d:!pAA%N``d*pAA%N`b`d2pAA@%N``dX;pAA%N``dDpAA%N`.`dPpAA@%N``d,YpAA%N` `dapAA%N`9`dwjpAA%N``d;spAA$WdPx`zp@@%N`$`d{pAA%N`8-`dpAA%N`5`dpAAWdx`\p@@@``p@@%N`c>`dpAA@%N` G`d9pAA%N`O`d:pAA%N`sX`dįpAA%N`Xa`dpAA%N`j`dOpAA%N`r`d ʎpAA%N`N{`dҎpAA%N`ۃ`d5ێpAA@%N``dpAA%N`=`dzpAA%N``drpAA%N``d pAA%N``dpAA%N`F`dpAA%N` `djpAA%N``d pAA@%N`q`d)pAA%N``d>2pAA%N`q`d:pAA%N`M`dCpAA%N``dRLpAA%N``dUpAA%N``dpAAA``Ǘp@@%N`A`dpAA%N`I`dSpAAvDwC-%N`KdxXpXX@~DwS-%N`RWh0x`ep@@vDwS-<%N`%R``p@@@~DwC->%N`gR`dpAAvDwC-%N`ySdxpXX%N`Z`dpAA%N`p`d2ȏpAA%N`Yy`dЏpAA%N``ddُpAA%N``dpAA%N`b`dpAA%N` `d_pAA%N`Ť`dpAA%N`v`dpAA%N`2`dj pAA%N``dWpAA%N`b`dpAA%N``dz'pAA%N``d0pAA%N``d8pAA%N`'`dApAA%N``d$JpAA%N``dXSpAA%N`N`dY[pAA%N` `dsdpAA%N``dlpAA%N`L`dupAA%N`'`dg~pAA%N`/`dpAA%N`8`d㏐pAA%N`6A`d^pAA%N`M`d"pAA%N`V`dpAA%N`a_`dpAA%N`g`dDpAA%N`rp`dǐpAA%N`9y`dАpAA%N`ׁ`dYِpAA%N``dpAA%N`q`dpAA%N`5`dpAA%N``d>pAA%N`׭`dpAA%N`D`d pAA%N``dbpAA%N``dWpAA%N`w`d'pAA%N`_`d0pAA%N`"`dQ9pAA%N``dBpAA%N``dJpAA%N`I`dSpAA%N` `d\pAA%N` `ddpAA%N`I `dmpAA%N` `d&vpAA%N` ' `dO~pAA@MwC-%N`( dxpXXMwS-%N`q/ WhHessendx`Ņp@@MwS-<%N`/ ``⅑p@@MwC->%N`/ `d↑pAAMwC-%N`0 dx_pXXMwS-%N`8 Wh x`wp@@MwS-<%N`78 ``p@@MwC->%N`S8 `dpAAMwC-%N`N9 dxpXXzS-%N`z@ Wh&&ix`іp@@@zS-<%N`@ ``ꖑp@@zC->%N`@ `dpAA@zC-%N`A dxpXXmzS-%N`H Wh-x`9p@@mzS-<%N`H ``Mp@@mzC->%N` I `dJpAAmzC-%N` J dxpXXmzS-%N`Y_ Wh=x`p@@mzS-<%N`u_ ``ѵp@@mzC->%N`_ `d׶pAAmzC-%N`` dxpXX:wS-%N`}g WhMx`ӽp@@:wS-<%N`g ``p@@:wC->%N`g `dpAA:wC-%N`h dx3ƑpXX@MwS-%N`o WhAll Calx`GƑp@@@MwS-<%N`p ``fƑp@@@MwC->%N`&p `dmǑpAA@MwC-%N`-q dxΑpXXMwS-%N`Ux Whlx`Αp@@@MwS-<%N`jx ``Αp@@MwC->%N`x `dϑpAA@MwC-%N`zy dx֑pXXmzS-%N` Whwrx`בp@@@mzS-<%N`ր ``2בp@@mzC->%N` `dؑpAA@mzC-%N`߁ dxrߑpXX@MwS-%N`2 Whx`ߑp@@MwS-<%N`E ``ߑp@@@MwC->%N`d `dpAAMwC-%N`a dxpXXMwS-%N` Wh x`p@@MwS-<%N` ``p@@MwC->%N` `dpAAMwC-%N` dx"pXX@MwS-%N` Wh0x`1p@@@MwS-<%N` ``ip@@@MwC->%N`) `dSpAA@MwC-%N` dxpXXMwS-%N`m Wh@Simx`p@@MwS-<%N`{ ``p@@MwC->%N` `dpAAMwC-%N`ܣ dxepXX@MwS-%N`% WhPplex TG99x`rp@@@MwS-<%N`2 ``p@@@MwC->%N`o `dpAA@MwC-%N` dx pXX8yS-%N`̳ Wh`x`$ p@@@8yS-<%N` ``` p@@8yC->%N` `dQ pAA@8yC-%N` dxipXX8yS-%N`) Whpx`xp@@8yS-<%N`8 ``p@@8yC->%N`x `dpAA8yC-%N` dxpXX8yS-%N` Whx`p@@@8yS-<%N` ``!p@@8yC->%N` `d3pAA@8yC-%N` dxL#pXX8yS-%N` Whx`W#p@@8yS-<%N` ``#p@@8yC->%N`U `d$pAA8yC-%N`k dx+pXX8yS-%N` Whx`+p@@@8yS-<%N` `` ,p@@8yC->%N` `dF-pAA@8yC-%N` dxs4pXX@8yS-%N`3 WhCl Brandenburghx`}4p@@8yS-<%N`= ``4p@@@8yC->%N`j `d5pAA8yC-%N` dx(=pXX8yS-%N` Wh>x`2=p@@8yS-<%N` ``q=p@@8yC->%N`1 `d>pAA8yC-%N`K dxEpXX@8yS-%N` Whb x`Ep@@8yS-<%N` ``Fp@@@8yC->%N` `d%GpAA8yC-%N` dxLNpXX8yS-%N` Wh^x`UNp@@8yS-<%N` ``Np@@8yC->%N`V `dOpAA8yC-%N`k dxVpXX@8yS-%N` Whnx`Wp@@8yS-<%N` ``AWp@@@8yC->%N` `dPXpAA8yC-%N` dxx_pXX8yS-%N`8 Whx`_p@@@8yS-<%N`D ``_p@@8yC->%N` `d`pAA@8yC-%N` dxtpXX8yS-%N` WhCl Berlinx`tp@@8yS-<%N` ``up@@8yC->%N`l `dvpAA8yC-%N`l dx}pXX8yS-%N`C' Wh x`}p@@@8yS-<%N`N' ``}p@@8yC->%N`' `d~pAA@8yC-%N`( dx(pXX8yS-%N`/ Wh0b%x`3p@@8yS-<%N`/ ``mp@@8yC->%N`-0 `dvpAA8yC-%N`61 dxpXX8yS-%N`L8 Wh@x`p@@@8yS-<%N`\8 ``׎p@@8yC->%N`8 `dƏpAA@8yC-%N`9 dx pXX@8yS-%N`@ WhPx`p@@8yS-<%N`@ ``Sp@@@8yC->%N`A `d]pAA8yC-%N`B dx͟pXX8yS-%N`I Wh`x`ߟp@@8yS-<%N`I ``p@@8yC->%N`I `d)pAA8yC-%N`J dxKpXX@8yS-%N` R WhpRefl BrLx`Vp@@8yS-<%N`R ``p@@@8yC->%N`RR `dpAA8yC-%N`SS dxpXX8yS-%N`Z Whandenburgx`p@@8yS-<%N`Z ``Bp@@8yC->%N`[ `d`pAA8yC-%N` \ dxpXX@8yS-%N`kc Wh@Dx`p@@8yS-<%N`uc ``p@@@8yC->%N`c `dpAA8yC-%N`d dxT’pXX8yS-%N`l Whx`c’p@@@8yS-<%N`#l ``’p@@8yC->%N`fl `dÒpAA@8yC-%N`Tm dxʒpXX8yS-%N`t Wh/x`ʒp@@8yS-<%N`t ``5˒p@@8yC->%N`t `d̒pAA8yC-%N`u d`SӒp@@@MwS-<%N`} `xےpXX8yS-%N` Wh?x`ܒp@@8yC->%N`߅ `d>ݒpAA@MwC-%N` dxHpXX@8yS-%N` WhReflx`]p@@8yS-<%N` ``p@@@8yC->%N`f `dpAA8yC-%N`t dxpXX8yS-%N`ע Whl BerlinGx`*p@@8yS-<%N` ``p@@8yC->%N`H `dCpAA8yC-%N` dxpXX@8yS-%N` Whox`p@@8yS-<%N` ``p@@@8yC->%N`֫ `dpAA8yC-%N` dxg pXX8yS-%N`' Wh@x` p@@8yS-<%N`O `` p@@8yC->%N`Z `d pAA8yC-%N` dxpXX@8yS-%N` Whx`p@@8yS-<%N` ``p@@@8yC->%N`μ `d/pAA8yC-%N` dx}pXXMwS-%N`= Wh x`p@@MwS-<%N`V ``p@@MwC->%N`t `dpAAMwC-%N` dx7$pXX@MwS-%N` Wh0x`J$p@@MwS-<%N` ``g$p@@@MwC->%N`' `d%pAAMwC-%N`B dx,pXX8yS-%N` Wh@BM SMSbx`,p@@8yS-<%N` ``-p@@8yC->%N` `dF.pAA8yC-%N` dx5pXX@8yS-%N`_ WhPx`5p@@8yS-<%N`o ``5p@@@8yC->%N` `d7pAA8yC-%N` dx>pXX8yS-%N` Wh`&)x`.>p@@@8yS-<%N` ``I>p@@8yC->%N` `d^?pAA@8yC-%N` dxFpXX8yS-%N`` Whpx`Fp@@8yS-<%N` ``Fp@@8yC->%N` `dHpAA8yC-%N` dxPOpXXvDwS-%N` Whx`qOp@@@~DwS-<%N`1 ``Op@@vDwC->%N`e `dPpAA@~DwC-%N`l dxXpXXvDwS-%N` Whx`:Xp@@yDwS-<%N` ``uXp@@vDwC->%N`5 `dYpAAyDwC-%N`l dx`pXXzDwS-%N` WhBM DAPNETx``p@@@uDwS-<%N` ``Hap@@zDwC->%N` `dNbpAA@uDwC-%N` dxipXXpDwS-%N`C Wh0x`ip@@}DwS-<%N`X ``ip@@pDwC->%N` `djpAA}DwC-%N` dxQrpXXwDwS-%N` Wh&)#x`lrp@@DwS-<%N`, ``rp@@wDwC->%N`l `dspAADwC-%N`y dx {pXX@rDwS-%N`$ WhPx`;{p@@@{DwS-<%N`$ ``}{p@@@rDwC->%N`=% `d|pAA@{DwC-%N`J& dxpXX@{DwS-%N`- Wh`x`p@@@rDwS-<%N`- ``Ip@@@{DwC->%N` . `dppAA@rDwC-%N`0/ dxtpXXDwS-%N`46 Whpx`p@@wDwS-<%N`I6 ``Œp@@DwC->%N`6 `dpAAwDwC-%N`7 dx;pXX}DwS-%N`> WhBM Parrx`Rp@@pDwS-<%N`? ``p@@}DwC->%N`V? `dpAApDwC-%N`c@ dxpXX@uDwS-%N`tU Whottx`ƫp@@zDwS-<%N`U ``p@@@uDwC->%N`U `dpAAzDwC-%N`V dx~pXXyDwS-%N`>^ Wh &)x`p@@vDwS-<%N`P^ ``ٴp@@yDwC->%N`^ `d۵pAAvDwC-%N`_ dxpXX@~DwS-%N`f Wh0x`p@@vDwS-<%N`f ``)p@@@~DwC->%N`f `d pAAvDwC-%N`g dxœpXXvDwS-%N`o Wh@x`œp@@@~DwS-<%N`o ``0Ɠp@@vDwC->%N`o `dCǓpAA@~DwC-%N`q dxΓpXXvDwS-%N`Wx WhPx`Γp@@yDwS-<%N`ox ``Γp@@vDwC->%N`x `dϓpAAyDwC-%N`y dxYדpXXzDwS-%N` Wh`BM x`jדp@@@uDwS-<%N`* ``דp@@zDwC->%N`v `dؓpAA@uDwC-%N` dx$pXXpDwS-%N` WhpAPRS'x`8p@@}DwS-<%N` ``p@@pDwC->%N`NJ `dpAA}DwC-%N`Nj dxpXXwDwS-%N`k Whx`p@@DwS-<%N`} ``p@@wDwC->%N`ƒ `d1pAADwC-%N` dx\pXX@rDwS-%N` Wh&)x`vp@@@{DwS-<%N`6 ``p@@@rDwC->%N`u `dpAA@{DwC-%N`z dxpXX@{DwS-%N` Wh!x`p@@@rDwS-<%N` ``&p@@@{DwC->%N` `dpAA@rDwC-%N`٤ dxpXXDwS-%N`U Wh1x`p@@wDwS-<%N`f ``p@@DwC->%N` `dpAAwDwC-%N`í dxP pXX}DwS-%N` WhAx`b p@@pDwS-<%N`" `` p@@}DwC->%N`k `d pAApDwC-%N` dxpXX@uDwS-%N`Ͻ WhDG2RON Ronny3x`-p@@zDwS-<%N` ``Yp@@@uDwC->%N` `d~pAAzDwC-%N`> dx)pXXyDwS-%N` Whax`)p@@vDwS-<%N` ``<*p@@yDwC->%N` `d<+pAAvDwC-%N` dx2pXX@~DwS-%N`m WhbF.x`2p@@vDwS-<%N`} ``2p@@@~DwC->%N` `dKOpAAvDwC-%N` dx^TpXXMwS-%N` Whx`pTp@@MwS-<%N`0 ``Tp@@MwC->%N`r `dUpAAMwC-%N` dx]pXX@MwS-%N` Whx`]p@@MwS-<%N` ``^]p@@@MwC->%N` `dn^pAAMwC-%N`. dxepXXMwS-%N` Wh x`ep@@MwS-<%N` ``$fp@@MwC->%N` `dggpAAMwC-%N`' dxnpXX@MwS-%N`Z Wh0DM9KS SilviAx`np@@MwS-<%N`g ``np@@@MwC->%N` `dppAAMwC-%N` dx%wpXXMwS-%N` Wh@o1x`5wp@@MwS-<%N` ``\wp@@MwC->%N`! `dvxpAAMwC-%N`6" dxpXX@MwS-%N`) WhPbEx`p@@MwS-<%N`) ``p@@@MwC->%N`) `d0pAAMwC-%N`* dxpXXMwS-%N`]2 Wh`x`p@@MwS-<%N`l2 ``Ԉp@@MwC->%N`2 `dpAAMwC-%N`3 dxopXX@MwS-%N`/; Whpx`~p@@MwS-<%N`>; ``p@@@MwC->%N`h; `dpAAMwC-%N`< dxpXXMwS-%N`[R Whx`p@@MwS-<%N`hR ``Ԩp@@MwC->%N`R `dpAAMwC-%N`S dxⰔpXX@MwS-%N`Z WhDM3MAT x`ﰔp@@MwS-<%N`Z ``p@@@MwC->%N`Z `d[pAAMwC-%N`\ dxpXXMwS-%N`_c WhHannesx`p@@MwS-<%N`nc ``ֹp@@MwC->%N`c `dpAAMwC-%N`d dxi”pXX@MwS-%N`)l Whbpx`x”p@@MwS-<%N`8l ``”p@@@MwC->%N`il `dÔpAAMwC-%N`vm dxʔpXXMwS-%N`t WhBx` ˔p@@MwS-<%N`t ``4˔p@@MwC->%N`t `db̔pAAMwC-%N`"v dxӔpXX@MwS-%N`z} WhRx`Ӕp@@MwS-<%N`} ``Ӕp@@@MwC->%N`} `d*ՔpAAMwC-%N`~ dxrܔpXXMwS-%N`2 Whbx`ܔp@@MwS-<%N`@ ``ܔp@@MwC->%N`b `dݔpAAMwC-%N` dx+pXX@MwS-%N` WhDL24x`8p@@MwS-<%N` ``bp@@@MwC->%N`" `dvpAAMwC-%N`6 dxpXXMwS-%N` Wh BQF Thomasx`p@@MwS-<%N` ``%p@@MwC->%N` `dpAAMwC-%N`Ę dxpXX@MwS-%N`j Wh x`p@@MwS-<%N`v ``p@@@MwC->%N` `d$pAAMwC-%N` dxCpXXMwS-%N` Wh b7Qx`Qp@@MwS-<%N` ``~p@@MwC->%N`> `dpAAMwC-%N`v dxpXX@MwS-%N` Wh 0x` p@@MwS-<%N`ʱ ``3p@@@MwC->%N` `dJ pAAMwC-%N` dxpXXMwS-%N`y Wh @x`p@@MwS-<%N` ``p@@MwC->%N` `dpAAMwC-%N`׻ dxspXX@MwS-%N`3 Wh Px`~p@@MwS-<%N`> ``p@@@MwC->%N`g `dpAAMwC-%N` dx-"pXXMwS-%N` Wh `DL7JOM Olaf2x`8"p@@MwS-<%N` ``m"p@@MwC->%N`- `d#pAAMwC-%N`Z dx*pXX@MwS-%N` Wh px`*p@@MwS-<%N` ``+p@@@MwC->%N` `dC,pAAMwC-%N` dx3pXXMwS-%N`] Wh b|x`3p@@MwS-<%N`j ``3p@@MwC->%N` `d4pAAMwC-%N` dxX%N`M `d=pAAMwC-%N`s d`Dp@@MwS-<%N` `x#MpXXMwS-%N` Wh #x`Mp@@MwC->%N` `dNpAAMwC-%N` dxUpXX@MwS-%N` Wh 3x`Up@@MwS-<%N` ``Vp@@@MwC->%N` `d,WpAAMwC-%N` dx^pXX8yS-%N`k Wh DG1RPH Petex`^p@@8yS-<%N` ``^p@@8yC->%N` `d_pAA8yC-%N` dxgpXXMwS-%N`Y Wh rx`gp@@MwS-<%N`k ``gp@@MwC->%N` `dhpAAMwC-%N` dxVppXX@MwS-%N` Wh b(x`epp@@MwS-<%N`% ``pp@@@MwC->%N`Q `dqpAAMwC-%N` dxypXXMwS-%N`" Wh sx`+yp@@MwS-<%N`" ``Vyp@@MwC->%N`# `dzpAAMwC-%N`@$ dx́pXX@MwS-%N`+ Wh x`ہp@@MwS-<%N`+ ``p@@@MwC->%N`+ `d2pAAMwC-%N`, dxpXXMwS-%N`U4 Wh x`p@@MwS-<%N`c4 ``ъp@@MwC->%N`4 `d2pAAMwC-%N`5 dx^pXXMwS-%N`= Wh DO1RPH rx`lp@@MwS-<%N`,= ``p@@MwC->%N`V= `dpAAMwC-%N`O> dxꛕpXX@MwS-%N`E Wh 0Richardqx`p@@MwS-<%N`E ``p@@@MwC->%N`E `d$pAAMwC-%N`F dxpXX@MwS-%N`uN Wh @cA`x`ɤp@@@MwS-<%N`N ``p@@@MwC->%N`N `d pAA@MwC-%N`O dxpXXMwS-%N`WW Wh Px`p@@MwS-<%N`fW ``ŭp@@MwC->%N`W `dpAAMwC-%N`X dx]pXXMwS-%N`` Wh `x`lp@@MwS-<%N`,` ``p@@MwC->%N`W` `d׷pAAMwC-%N`a dxξpXXMwS-%N`h Wh px`޾p@@@MwS-<%N`h ``p@@MwC->%N`h `dpAA@MwC-%N`i dxǕpXX@MwS-%N`q Wh x`Ǖp@@MwS-<%N`q ``Ǖp@@@MwC->%N`q `d-ɕpAAMwC-%N`r d`_ޕp@@@MwS-<%N` `xpXXMwS-%N` Wx`p@@MwC->%N` `d1pAA@MwC-%N` dxJpXXMwS-%N` Wx`Xp@@@MwS-<%N` ``p@@MwC->%N`B `dpAA@MwC-%N`] dxpXXMwS-%N` W x`p@@@MwS-<%N` ``>p@@MwC->%N` `dxpAA@MwC-%N`8 dx&pXXMwS-%N` W0x`&p@@MwS-<%N` ``'p@@MwC->%N` `do(pAAMwC-%N`/ dx/pXXMwS-%N`] W@x`/p@@MwS-<%N`k ``/p@@MwC->%N` `d0pAAMwC-%N` dxT8pXXMwS-%N` WPx`a8p@@@MwS-<%N`! ``8p@@MwC->%N`M `d9pAA@MwC-%N`u dxApXXMwS-%N` W`x`,Ap@@@MwS-<%N` ``VAp@@MwC->%N` `dlBpAA@MwC-%N`, dxIpXXMwS-%N` Wpx`Ip@@@MwS-<%N` ``Jp@@MwC->%N` `dBKpAA@MwC-%N` dxRpXXMwS-%N`_ Wx`Rp@@@MwS-<%N`l ``|Sp@@MwC->%N`< `d%N` `d``pAA@MwC-%N` dxgpXXMwS-%N`W x`gp@@@MwS-<%N```hp@@MwC->%N``dipAA@MwC-%N`dxppXXMwS-%N`PW0x`pp@@MwS-<%N`]``pp@@MwC->%N``dqpAAMwC-%N`dxRypXXMwS-%N`#W@x`_yp@@MwS-<%N`#``yp@@MwC->%N`I#`dzpAAMwC-%N`$dxpXXMwS-%N`+WPx`p@@@MwS-<%N`+``Fp@@MwC->%N`,`d[pAA@MwC-%N`-dxpXXMwS-%N`Y4W`x`p@@@MwS-<%N`m4``Њp@@MwC->%N`4`dpAA@MwC-%N`5dx`pXXMwS-%N` =Wpx`mp@@@MwS-<%N`-=``p@@MwC->%N`O=`dpAA@MwC-%N`g>dxpXXMwS-%N`EW4PContact1>x`p@@@MwS-<%N`E`` p@@MwC->%N`E`dĖpAA@MwC-%N`pndxd͖pXXMwS-%N`$wWx`p͖p@@@MwS-<%N`0w``͖p@@MwC->%N`Xw`dΖpAA@MwC-%N`yxdx-֖pXXMwS-%N`Wx`<֖p@@@MwS-<%N```b֖p@@MwC->%N`"`dpAA@MwC-%N`tdxxpXXMwS-%N`8Wx`p@@MwS-<%N`D``p@@MwC->%N`l`dpAAMwC-%N`dxpXXMwS-%N`W x`(p@@MwS-<%N```Tp@@MwC->%N``d|pAAMwC-%N`<dx pXXMwS-%N`W0x` p@@@MwS-<%N```!p@@MwC->%N``dn"pAA@MwC-%N`.dx)pXXMwS-%N`YW@x`)p@@@MwS-<%N`e``)p@@MwC->%N``d +pAA@MwC-%N`dx&2pXXMwS-%N`WPx`22p@@@MwS-<%N```R2p@@MwC->%N``dq3pAA@MwC-%N`1dx:pXXMwS-%N`W`x`:p@@@MwS-<%N```;p@@MwC->%N``d+%N``dDpAA@MwC-%N`dxTLpXXMwS-%N`Wx``Lp@@@MwS-<%N` ``Lp@@MwC->%N`I`dMpAA@MwC-%N`dxUpXXMwS-%N`W*x`(Up@@MwS-<%N```SUp@@MwC->%N``dVpAAMwC-%N`Vdx]pXXMwS-%N`W:x`]p@@MwS-<%N``` ^p@@MwC->%N``d$_pAAMwC-%N`dxfpXXMwS-%N`PWJx`fp@@@MwS-<%N`Z``Ygp@@MwC->%N``dohpAA@MwC-%N`/dxAopXXMwS-%N`WZx`Mop@@@MwS-<%N` ``yop@@MwC->%N`9`dppAA@MwC-%N`ddxwpXXMwS-%N`{!Wjx`wp@@@MwS-<%N`!``wp@@MwC->%N`!`d)ypAA@MwC-%N`"dx?pXXMwS-%N`)Wzx`Jp@@@MwS-<%N` *``np@@MwC->%N`.*`dpAA@MwC-%N`Z+dxpXXMwS-%N`2Wx`p@@@MwS-<%N`2``.p@@MwC->%N`2`d_pAA@MwC-%N`4dxpXXMwS-%N`w;WDL;x`‘p@@@MwS-<%N`;``ꑗp@@MwC->%N`;`dpAA@MwC-%N`<dxTpXXMwS-%N`Wx``p@@MwS-<%N```p@@MwC->%N` `dpAAMwC-%N`<dxpXXMwS-%N` Wx`$p@@MwS-<%N` ``Sp@@MwC->%N` `d+pAAMwC-%N`dxpXXMwS-%N`!Wx`p@@@MwS-<%N`-``ִp@@MwC->%N`V`dpAA@MwC-%N`}dx\pXXMwS-%N`$W x`hp@@@MwS-<%N`$``p@@MwC->%N`%`dƾpAA@MwC-%N`F&dx0ƗpXXMwS-%N`-W0x`?Ɨp@@@MwS-<%N`-``Ɨp@@MwC->%N`.`dǗpAA@MwC-%N`/dxΗpXXMwS-%N`i6W@x`Ηp@@@MwS-<%N`x6``8ϗp@@MwC->%N`6`dGЗpAA@MwC-%N`7dxחpXXMwS-%N`3?WPx`חp@@@MwS-<%N`>?``חp@@MwC->%N`}?`dٗpAA@MwC-%N`@dxhpXXMwS-%N`GW`x`sp@@@MwS-<%N`G``p@@MwC->%N`>H`dpAA@MwC-%N`JIdx-pXXMwS-%N`PWp x`:p@@MwS-<%N`P``]p@@MwC->%N`P`d{pAAMwC-%N`QdxpXXMwS-%N`*YWx`p@@MwS-<%N`5Y``p@@MwC->%N`^Y`dpAAMwC-%N`Zdx^pXXMwS-%N`aW,x`ip@@@MwS-<%N`a``p@@MwC->%N`b`dpAA@MwC-%N`$cdxpXXMwS-%N`jW<x`p@@@MwS-<%N`j``Cp@@MwC->%N`j`dmpAA@MwC-%N`kd` p@@MwS-<%N`.s`xpXX@MwS-%N`cwWLx`p@@@MwC->%N`w`dcpAAMwC-%N`xdxpXXMwS-%N`W\x`p@@MwS-<%N```p@@MwC->%N`D`dpAAMwC-%N`wdx!pXX@MwS-%N`Wlx`'!p@@@MwS-<%N```E!p@@@MwC->%N`ň`dZ"pAA@MwC-%N`ډdx)pXXMwS-%N`!W|x`)p@@@MwS-<%N`.``)p@@MwC->%N`W`d*pAA@MwC-%N`gdxg2pXXMwS-%N`Wx`t2p@@MwS-<%N```2p@@MwC->%N``d3pAAMwC-%N`_dx";pXXMwS-%N`WBerlin/Brandx`/;p@@MwS-<%N```];p@@MwC->%N`ݢ`dw%N``d0EpAA@MwC-%N`dxrLpXX8yS-%N`Wx`Lp@@@8yS-<%N```Lp@@8yC->%N`+`dUpAA@8yC-%N`dx]pXX@8yS-%N`oWx`]p@@8yS-<%N`|``&^p@@@8yC->%N``dT_pAA8yC-%N`dxfpXX8yS-%N`%W x`fp@@@8yS-<%N`3``fp@@8yC->%N`R`dgpAA@8yC-%N`gdx8opXX@8yS-%N`W0x`Iop@@8yS-<%N```qop@@@8yC->%N``dppAA8yC-%N`dxwpXX8yS-%N`}W@x` xp@@@8yS-<%N```5xp@@8yC->%N``d;ypAA@8yC-%N`dxpXX@8yS-%N`<WPx`ʀp@@8yS-<%N`J``p@@@8yC->%N`t`d,pAA8yC-%N`dxGpXX8yS-%N`W`x`Vp@@@8yS-<%N```p@@8yC->%N``dpAA@8yC-%N`6dxpXX@8yS-%N`Wpx`#p@@8yS-<%N```Op@@@8yC->%N``dpAA8yC-%N`dxޚpXX8yS-%N`^Wx`욘p@@@8yS-<%N`l``p@@8yC->%N``dFpAA@8yC-%N`dxpXX@8yS-%N` W.x`p@@8yS-<%N`& ``ǣp@@@8yC->%N`G `dpAA8yC-%N` dxbpXX8yS-%N`W>x`pp@@@8yS-<%N```p@@8yC->%N``dƭpAA@8yC-%N`Fdx$pXX@8yS-%N`WNx`0p@@8yS-<%N```Zp@@@8yC->%N``dpAA8yC-%N` dxڽpXX8yS-%N`Z%W^x`罘p@@@8yS-<%N`g%``p@@8yC->%N`%`d"pAA@8yC-%N`&dxƘpXX@8yS-%N`.Wnx`Ƙp@@8yS-<%N`*.``Ƙp@@@8yC->%N`S.`dǘpAA8yC-%N`i/dx]ϘpXX8yS-%N`6W~x`iϘp@@@8yS-<%N`6``Ϙp@@8yC->%N`7`dИpAA@8yC-%N`58dxטpXX@8yS-%N`n?Wx`טp@@8yS-<%N`{?``&ؘp@@@8yC->%N`?`d٘pAA8yC-%N`@dxpXX8yS-%N`nHWSa/ThNx`p@@@8yS-<%N`wH``Ap@@8yC->%N`H`dIpAA@8yC-%N`IdxpXX@8yS-%N`/QWx`p@@8yS-<%N`8Q``p@@@8yC->%N`Q`dpAA8yC-%N`qRdxppXX8yS-%N`YWx`yp@@@8yS-<%N`Y``p@@8yC->%N`CZ`dpAA@8yC-%N`cdx pXX@8yS-%N`IuWx` p@@8yS-<%N`Ru``2p@@@8yC->%N`u`d*pAA8yC-%N`vdxupXX8yS-%N`}W x`p@@@8yS-<%N`}``p@@8yC->%N`~`dpAA@8yC-%N`9dxpXX@8yS-%N`W0x` p@@8yS-<%N```hp@@@8yC->%N``db pAA8yC-%N`dx'pXX8yS-%N`;W@x`'p@@@8yS-<%N`E``'p@@8yC->%N`g`d(pAA@8yC-%N`ddxP0pXX@8yS-%N`ЗWPx`]0p@@8yS-<%N`ݗ``0p@@@8yC->%N`(`d1pAA8yC-%N`Hdx9pXX8yS-%N`W`x`9p@@@8yS-<%N```R9p@@8yC->%N`Ҡ`dH:pAA@8yC-%N`ȡdxApXX@8yS-%N`RWpx`Ap@@8yS-<%N`[`` Bp@@@8yC->%N``dBpAA8yC-%N`dxJpXX8yS-%N` W x`Jp@@@8yS-<%N`*``Jp@@8yC->%N`H`dKpAA@8yC-%N`GdxSpXX@8yS-%N`AW0x`Sp@@8yS-<%N`J``Sp@@@8yC->%N`h`dTpAA8yC-%N`kdxw\pXX8yS-%N`W@x`\p@@@8yS-<%N```\p@@8yC->%N`@`d]pAA@8yC-%N`.dxepXX@8yS-%N`WPx` ep@@8yS-<%N```-ep@@@8yC->%N``dfpAA8yC-%N`dxmpXX8yS-%N`FW`x`mp@@@8yS-<%N`N``\np@@8yC->%N``d^opAA@8yC-%N`dxvpXX@8yS-%N`CWpx`vp@@8yS-<%N`M``vp@@@8yC->%N`m`dxpAA8yC-%N`dxpXX8yS-%N`7Wx`p@@@8yS-<%N`@``p@@8yC->%N``dpAA@8yC-%N`dxDpXX@8yS-%N`Wx`Op@@8yS-<%N```p@@@8yC->%N``dpAA8yC-%N`)dxpXX8yS-%N`}WSA/MVx`p@@@8yS-<%N```Qp@@8yC->%N``dWpAA@8yC-%N`dxHpXX@8yS-%N`Wx`Qp@@8yS-<%N```p@@@8yC->%N`&`dpAA8yC-%N`dxpXX8yS-%N` W x`p@@@8yS-<%N` ``%N` `d-pAA@8yC-%N`dx)pXX@8yS-%N`Wx`4p@@8yS-<%N```vp@@@8yC->%N``dmpAA8yC-%N`dx廙pXX8yS-%N`e#W x`p@@@8yS-<%N`n#``p@@8yC->%N`#`dCpAA@8yC-%N`$dxępXX@8yS-%N`(,W0x`ęp@@8yS-<%N`6,``ęp@@@8yC->%N`_,`d1ƙpAA8yC-%N`-dxj͙pXX8yS-%N`4W@x`w͙p@@@8yS-<%N`4``͙p@@8yC->%N`5`dΙpAA@8yC-%N`U6dxՙpXX@8yS-%N`q=WPx`ՙp@@8yS-<%N`|=``*֙p@@@8yC->%N`=`d0יpAA8yC-%N`>dxޙpXX8yS-%N`+FW`x`ޙp@@@8yS-<%N`7F``ޙp@@8yC->%N`aF`dpAA@8yC-%N`GdxepXX@8yS-%N`NWpx`sp@@8yS-<%N`N``p@@@8yC->%N`O`dpAA8yC-%N`1PdxpXX8yS-%N`vWW"x`p@@@8yS-<%N`W``%p@@8yC->%N`W`dHpAA@8yC-%N`XdxpXX@8yS-%N`3`W2x`p@@8yS-<%N`A```p@@@8yC->%N```dpAA8yC-%N`fadx[pXXMwS-%N`hWBx`mp@@@MwS-<%N`h``p@@MwC->%N`.i`dpAA@MwC-%N`&jdx pXX@MwS-%N`qWRx` p@@MwS-<%N`q``^ p@@@MwC->%N`q`dj pAAMwC-%N`rdxpXXMwS-%N` zWbx`p@@MwS-<%N`"z``p@@MwC->%N`Bz`dpAAMwC-%N`D{dxpXX@MwS-%N`yWrx` p@@@MwS-<%N```Fp@@@MwC->%N`Ƃ`dnpAA@MwC-%N`dx#pXXMwS-%N`8Wx`#p@@@MwS-<%N`H``$p@@MwC->%N``d%pAA@MwC-%N`dxZ,pXXMwS-%N`ړWx`m,p@@MwS-<%N```,p@@MwC->%N`/`d-pAAMwC-%N`dx4pXXMwS-%N`NW Hessenx`4p@@MwS-<%N`d``%5p@@MwC->%N``d36pAAMwC-%N`dxu=pXXMwS-%N`W x`=p@@@MwS-<%N` ``=p@@MwC->%N`J`d>pAA@MwC-%N`RdxFpXXMwS-%N`W  x`Fp@@@MwS-<%N```QFp@@MwC->%N`ѭ`dNpAA@MwC-%N`ad`Vp@@@MwS-<%N`^`x__pXXMwS-%N`W x`_p@@MwC->%N``d`pAA@MwC-%N`?dx$hpXXMwS-%N`W x`;hp@@@MwS-<%N```]hp@@MwC->%N``dhipAA@MwC-%N`dxppXXMwS-%N`W 0x`pp@@@MwS-<%N```pp@@MwC->%N`E`drpAA@MwC-%N`dx ypXXMwS-%N`W @x`"yp@@@MwS-<%N```Oyp@@MwC->%N``dzpAA@MwC-%N`dxypXX8yS-%N`W Px`p@@@8yS-<%N` ``p@@8yC->%N`+`dpAA@8yC-%N`*dxpXX8yS-%N`pW `x`p@@8yS-<%N``` p@@8yC->%N``d pAA8yC-%N`dxpXX8yS-%N`.W px`p@@@8yS-<%N`<``撚p@@8yC->%N`f`dpAA@8yC-%N`dx?pXX@8yS-%N`W $x`Lp@@8yS-<%N```pp@@@8yC->%N``dupAA8yC-%N`dxpXX8yS-%N`v W 4x`p@@8yS-<%N` ``'p@@8yC->%N` `d)pAA8yC-%N` dxpXX@8yS-%N`W Dx`p@@8yS-<%N`*``ˬp@@@8yC->%N`K`d쭚pAA8yC-%N`ldxšpXX8yS-%N`*W Tx`šp@@8yS-<%N`+*``šp@@8yC->%N`P*`dĚpAA8yC-%N`+dxT˚pXX@8yS-%N`2W dx`c˚p@@8yS-<%N`2``˚p@@@8yC->%N`3`d̚pAA8yC-%N`4dxԚpXX8yS-%N`;W tx`)Ԛp@@@8yS-<%N`;``QԚp@@8yC->%N`;`dt՚pAA@8yC-%N`<dxܚpXXMwS-%N`GDW x`ܚp@@MwS-<%N`[D``ݚp@@MwC->%N`D`d)ޚpAAMwC-%N`EdxipXXMwS-%N`LW x`tp@@MwS-<%N`L``p@@MwC->%N`1M`dpAAMwC-%N`eNdxpXXMwS-%N`UW Simplexx`#p@@@MwS-<%N`U``Dp@@MwC->%N`U`dpAA@MwC-%N`WdxpXXMwS-%N`_^W x`p@@@MwS-<%N`n^``p@@MwC->%N`^`d1pAA@MwC-%N`_d`}p@@MwS-<%N`f`xpXX@MwS-%N`;xW4ux`p@@@MwC->%N`nx`d$;pAAMwC-%N`dxKpXXMwS-%N` W4#%x`Kp@@MwS-<%N```Kp@@MwC->%N`D`dLpAAMwC-%N`kdxOTpXX@MwS-%N`ϻW4 3 rx`\Tp@@@MwS-<%N`ܻ``Tp@@@MwC->%N` `dUpAA@MwC-%N`:dx\pXXMwS-%N`]W40ALx`\p@@@MwS-<%N`i``]p@@MwC->%N``d+^pAA@MwC-%N`dxepXXMwS-%N`%W4@CLML x`ep@@MwS-<%N`H``ep@@MwC->%N`t`d(gpAAMwC-%N`dxlnpXXMwS-%N`W4PSL,x`znp@@MwS-<%N```np@@MwC->%N`'`dopAAMwC-%N`4dxKwpXXMwS-%N`W4` x`Zwp@@@MwS-<%N```wp@@MwC->%N``dxpAA@MwC-%N`dxpXXMwS-%N`W4p% &SLx`'p@@@MwS-<%N```Sp@@MwC->%N``dpAA@MwC-%N`dxވpXX@MwS-%N`^W4(SL.SLx`툛p@@MwS-<%N`m``p@@@MwC->%N``dDpAAMwC-%N`dxpXXMwS-%N`(W42SL x`p@@MwS-<%N`7``ᑛp@@MwC->%N`a`d,pAAMwC-%N`dxepXX@MwS-%N`W4 $x`tp@@@MwS-<%N```p@@@MwC->%N``dڛpAA@MwC-%N`ZdxBpXXMwS-%N` W4&n(Qx`Pp@@@MwS-<%N` ``p@@MwC->%N` `dpAA@MwC-%N` dxGpXXMwS-%N` W4P*x`Tp@@MwS-<%N` ``p@@MwC->%N`!`dӺpAAMwC-%N`S"dx›pXXMwS-%N`)W4*, Bx`›p@@MwS-<%N`)``C›p@@MwC->%N`)`dSÛpAAMwC-%N`*d`ʛp@@MwS-<%N`I2`dpCC@MwS-%N`}ENDd`Lp@@@MwC->%N`}`d77pAAMwC-%N`d`@p@@MwS-<%N```UQp@@@MwS%N`ո!"`` Rp@@@MwC->%N``l:TpCounters provided by dumpcapU}o6Tp`lqdmr-0.12.3/doc/reveng/anytone/d878uv/capture_set_gps_on.pcapng000066400000000000000000115654001501654372000244110ustar00rootroot00000000000000 M<+6Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz (with SSE4.2)Linux 5.4.0-62-generic:Dumpcap (Wireshark) 3.2.3 (Git v3.2.3 packaged as 3.2.3-1)H@usbmon0  Linux 5.4.0-62-genericH`|$_@@MwS@!`h|2_GGMwC@-M`?%h`|j>_@@@MwS@M`ef``|e_@@MwS@M`I`h|_GGMwS@M`! h`|_@@MwC@->M```|_@@MwS@zS@-M` PROGRAMh`|f@@>zC@->M` `d|hCCzC@-M`0OQXdd|ϨhAAYcyS@-M`Osd`|h@@QcyS@-M`ss`p|6iPPQcyC@-M`ID878UVV100p`|i@@QcyS@-M``x|YiXXQcyC@-M`$W &x`|*ji@@QcyS@-M``d|rjAAQcyC@-M`R=dx|jXXQcyS@-M`1KW zx`|j@@YcyS@-M`VK`d|ajAAYcyC@-M`Ldx| jXXYcyS@-M`TW DB0LDS TS1Bx`|j@@QcyS@-M`T`d|΋jAAQcyC@-M`NVdx|jXXQcyS@-M`k]W0x`|j@@YcyS@-M`]`d|jAAYcyC@-M`_dx|^jXXYcyS@-M`fW@CbPvYx`|cj@@QcyS@-M`g`d|ҝjAAQcyC@-M`Rhdx|^jXXQcyS@-M`oWP x`|cj@@YcyS@-M`p`d|jAAYcyC@-M`1qdx|jXXYcyS@-M`xW`Sa/Th DB0LDS ix`|j@@QcyS@-M`Cx`d|YjAAQcyC@-M`ydx|jXXQcyS@-M`yWpTS1x`|j@@YcyS@-M``d|jAAYcyC@-M`dx|jXXYcyS@-M`WCbPvx`|"j@@QcyS@-M``d|jAAQcyC@-M`dx|jXXQcyS@-M`W x`|j@@YcyS@-M`*`d|*jAAYcyC@-M`dx|jXXYcyS@-M`KWTG8 DB0LDS TSx`|j@@QcyS@-M`n`d|jAAQcyC@-M`dx|^jXXzS@-M`ޮW2rx`|gj@@zS@-M``d| jAAzC@-M`dx||jXX@zS@-M`WCbPvx`|j@@zS@-M`;`d|jAAzC@-M`dx|jXXzS@-M`W Ax`|j@@zS@-M`f`d|jAAzC@-M`dx|jXXzS@-M`WTG9 DB0LDS TSx`|j@@zS@-M`I`d|&kAAzC@-M`dx|kXXzS@-M`W2x`|k@@@zS@-M``d|< kAA@zC@-M`dx|kXXzS@-M`WCbPvx`|k@@zS@-M`;`d|kAAzC@-M`<dx|kXX@zS@-M`W x`|k@@zS@-M``d|6kAAzC@-M`dx|!kXXzS@-M`W BB DB0LDS TS2x`|!k@@zS@-M`:`d|"kAAzC@-M`;dx|)kXXzS@-M`iW0x`|)k@@zS@-M``d|9+kAAzC@-M`dx|X2kXX@zS@-M`W@C%vx`|a2k@@zS@-M` `d|3kAAzC@-M`:dx|:kXXzS@-M`)WP x`|:k@@zS@-M``d|;kAAzC@-M`mdx|"CkXX@zS@-M` W`DM0TZN TS1x`|'Ck@@zS@-M` `d|WDkAAzC@-M`dx|KkXXzS@-M`EWpx`|Kk@@zS@-M``d|MkAAzC@-M`dx|1TkXXzS@-M`WC%vx`|8Tk@@zS@-M``d|kUkAAzC@-M`dx|\kXX@zS@-M`A'W x`|\k@@zS@-M`e'`d|^kAAzC@-M`(dx|3ekXXzS@-M`/WTG8 DM0TZN TSx`|M`/`d|fkAA@zC@-M`0dx|mkXXovS@-M`)8W2sx`|mk@@ovS@-M`p8`d|nkAAovC@-M`|9dx|vkXXovS@-M`AWC%v@x`|vk@@ovS@-M`uA`d|wkAAovC@-M`{Bdx|kXXovS@-M` JW Ax`|k@@ovS@-M`YJ`d|kAAovC@-M`Kdx|kXXovS@-M`{RWTG9 DM0TZN TSx`|k@@ovS@-M`R`d|kAAovC@-M`M`\`d|kAAovC@-M`l]dx|kXXovS@-M`dWC%vx`|k@@ovS@-M`d`d|kAAovC@-M`5fdx|AkXXovS@-M`mW x`|Gk@@ovS@-M`n`d|kAAovC@-M`todx|kXXovS@-M`uvW BB DM0TZN TS2x`|k@@ovS@-M`v`d|kAAovC@-M`xdx| kXXovS@-M`W0x`|k@@ovS@-M``d|M``d|kAAovC@-M`dx|ckXXovS@-M`WP x`|nk@@ovS@-M` `d|kAAovC@-M`mdx|GkXXovS@-M`ǙW`DB0LOS TS1x`|Ok@@ovS@-M``d|kAAovC@-M`Gdx|=kXXovS@-M`Wpx`|Ek@@ovS@-M``d|kAAovC@-M`Cdx|kXXovS@-M`|WCuvMx`|k@@ovS@-M``d|ckAAovC@-M`dx|jkXXovS@-M`W x`|vk@@ovS@-M``d|kAAovC@-M`dx|?kXXovS@-M`WR/TG9 DB0LOS mx`|Gk@@ovS@-M``d|kAAovC@-M`Idx|kXXovS@-M`wWTS2x`|k@@ovS@-M``d|bkAAovC@-M`dx|lXXovS@-M`0WCPvx`|l@@ovS@-M`y`d|lAAovC@-M`}dx|l lXXovS@-M`W Bx`|y l@@ovS@-M`;`d| lAAovC@-M`zdx|lXXovS@-M`{WR/TG9 DM0TT Tx`| l@@ovS@-M``d|elAAovC@-M`dx|lXXovS@-M`>WS1x`|l@@ovS@-M``d|lAAovC@-M`dx|}&lXXovS@-M`WCPv<x`|&l@@ovS@-M`K`d|'lAAovC@-M`Pdx|>/lXXovS@-M`W x`|L/l@@ovS@-M``d|0lAAovC@-M`#dx|7lXXovS@-M`sW TG8 DM0TT TS2 x`|8l@@ovS@-M``d|V9lAAovC@-M`dx|@lXXovS@-M`6 W0x`|@l@@ovS@-M` `d|BlAAovC@-M` dx|pIlXXovS@-M`W@CPv|x`|}Il@@ovS@-M`>`d|JlAAovC@-M`@dx|2RlXXovS@-M`WP x`|NRl@@ovS@-M``d|SlAAovC@-M`dx|ZlXXovS@-M`p%W`TG9 DM0TT TS2Kx`|Zl@@ovS@-M`%`d|j\lAAovC@-M`&dx|clXXovS@-M`.Wpx`|cl@@ovS@-M`o.`d|dlAAovC@-M`x/dx|RllXXovS@-M`6WCPvx`|`ll@@ovS@-M`7`d|mlAAovC@-M`8dx|ulXXovS@-M`?W x`|!ul@@ovS@-M`?`d|svlAAovC@-M`@dx|}lXXovS@-M`BHWBB DM0TT TS2;x`|}l@@ovS@-M`H`d|@lAAovC@-M`Idx|NlXXovS@-M`PWCx`|[l@@ovS@-M`Q`d|lAAovC@-M`@Rdx|lXXovS@-M`{YWC7Pvx`|l@@ovS@-M`Y`d|\lAAovC@-M`Zdx|lXXovS@-M`:bW Cx`|Ɨl@@ovS@-M`b`d|lAAovC@-M`cdx|lXXovS@-M`kWTG9 DB0KK TS1x`|l@@ovS@-M`Nk`d|ϡlAAovC@-M`Oldx|IlXXovS@-M`sWx`|Ul@@ovS@-M`t`d|lAAovC@-M`=udx|lXXovS@-M`||WC7Pvx`| l@@ovS@-M`|`d|8lAAovC@-M`}dx|lXXovS@-M`9W x`|źl@@ovS@-M``d|lAAovC@-M`dx|dlXXovS@-M`W BB DB0KK TS2x`|tl@@ovS@-M`3`d|lAAovC@-M`%dx|lXXovS@-M`lW0x`|l@@ovS@-M`ʖ`d|klAAovC@-M`dx|tlXXMwS@-M`W@C%vx`|l@@MwS@-M`A`d|lAAMwC@-M`idx|lXX@MwS@-M`zWP x`|l@@MwS@-M`ŧ`d|\lAAMwC@-M`ܨdx|lXXMwS@-M`5W`DB0OUD TS1x`|l@@@MwS@-M``d|lAA@MwC@-M`ydx|MlXXMwS@-M`͸Wpx`|[l@@@MwS@-M``d|lAA@MwC@-M`dx|lXXMwS@-M`WC%vx`|l@@MwS@-M``d|WlAAMwC@-M`dx|lXXMwS@-M`>W x`|l@@MwS@-M``d|&mAAMwC@-M`dx|mXXMwS@-M`WTG8 DB0OUD TSx`|m@@@MwS@-M`P`d| mAA@MwC@-M`adx|mXXMwS@-M`W2vx`|m@@@MwS@-M``d|qmAA@MwC@-M`dx|mXXMwS@-M`WC%vAx`|m@@MwS@-M`l`d|mAAMwC@-M`hdx|1"mXX@MwS@-M`W Dx`|="m@@MwS@-M``d|#mAAMwC@-M`1dx|*mXXMwS@-M`oWTG9 DB0OUD TSx`|*m@@@MwS@-M``d|`,mAA@MwC@-M`dx||3mXXMwS@-M`W2x`|3m@@@MwS@-M`F`d|4mAA@MwC@-M``dx|2M``d|w=mAAMwC@-M`dx|DmXXMwS@-M`sW x`|Em@@MwS@-Em@@MwC@->M``d|uFmAAMwC@-M`dx|MmXXMwS@-M`'W BB DB0OUD TS2x`|Mm@@@MwS@-M`n`d|NmAA@MwC@-M`ydx|_VmXXMwS@-M` W0x`|kVm@@@MwS@-M`(!`d|WmAA@MwC@-M`;"dx|^mXX>zS@-M`o)W@Cuv!x`|^m@@>zS@-zC@->M`)`d|S`mAA>zC@-M`*dx|gmXX>zS@-M`,2WP x`|gm@@@>zS@-zC@->M`2`d|hmAA@>zC@-M`t3dx|"pmXX@>zS@-M`:W`TG9 DB0TU TS1Ax`|1pm@@>zS@-zC@->M`:`d|oqmAA>zC@-M`;dx|xmXX>zS@-M`XCWpx`|xm@@>zS@-ym@@>zC@->M`C`d|OzmAA>zC@-M`Ddx|mXX>zS@-M`LWCuvax`|m@@>zS@-zC@->M`6L`d|̂mAA>zC@-M`LMdx|9mXX@>zS@-M`TW x`|Hm@@@>zS@-zC@->M`T`d|mAA@>zC@-M`Vdx|mXX@>zS@-M`u]WBB DB0TU TS23x`|m@@@>zS@-zC@->M`]`d|HmAA@>zC@-M`^dx|{mXX>zS@-M`eWEx`|m@@>zS@-zC@->M`*f`d|mAA>zC@-M`egdx| mXX>zS@-M`nWC%vQx`|m@@>zS@-zC@->M`n`d|mAA>zC@-M`pdx|ĬmXX>zS@-M`DwW ?x`|Ҭm@@@>zS@-zC@->M`tw`d|mAA@>zC@-M`xdx|LmXX@>zS@-M`WDM0MOT TS1x`|`m@@>zS@-zC@->M``d|mAA>zC@-M`,dx|@mXX>zS@-M`Wx`|Om@@>zS@-zC@->M``d|mAA>zC@-M` dx|mXX>zS@-M`uWC%vx`|m@@>zS@-zC@->M``d|@mAA>zC@-M`dx|mXX@>zS@-M`#W x`|m@@@>zS@-zC@->M`O`d|mAA@>zC@-M`~dx|=mXX@>zS@-M`W TG8 DM0MOT TS#x`|Pm@@@>zS@-zC@->M``d|bmAA@>zC@-M`dx|mXX>zS@-M`kW02x`|m@@>zS@-zC@->M`ʹ`d|OmAA>zC@-M`Ϻdx|mXX>zS@-M`W@C%vx`|m@@>zS@-zC@->M`C`d|mAA>zC@-M`vdx|5nXX>zS@-M`WP x`|En@@@>zS@-zC@->M``d|CnAA@>zC@-M`dx|nXX@>zS@-M`W`TG9 DM0MOT TSdx`|n@@>zS@-zC@->M`H`d| nAA>zC@-M`{dx|nXX>zS@-M`Wp28x`|n@@>zS@-zC@->M``d|(nAA>zC@-M`dx|nXX>zS@-M`<WC%vx`|n@@>zS@-zC@->M`q`d|nAA>zC@-M`dx|v"nXX@>zS@-M`W x`|"n@@@>zS@-zC@->M`,`d|#nAA@>zC@-M`&dx|K+nXXQcyS@-M`WBB DM0MOT TS2x`|^+n@@YcyS@-M``d|,nAAYcyC@-M`dx|3nXXYcyS@-M`MWFx`|3n@@QcyS@-M``d|95nAAQcyC@-M`dx|M`R`d|=nAAYcyC@-M`idx|EnXXYcyS@-M`W @x`|En@@QcyS@-M``d|UFnAAQcyC@-M`dx|MnXXQcyS@-M`RWDB0TA TS1x`|Mn@@YcyS@-M``d|/OnAAYcyC@-M`dx|hVnXXYcyS@-M` Wx`|uVn@@QcyS@-M`7!`d|WnAAQcyC@-M`i"dx|_nXXQcyS@-M`)WCuvx`|+_n@@YcyS@-M`)`d|`nAAYcyC@-M`+dx|gnXXYcyS@-M``2W x`|gn@@QcyS@-M`2`d|,inAAQcyC@-M`3dx|pnXXQcyS@-M`;W TG8 DB0TA TS2x`|pn@@YcyS@-M`f;`d|qnAAYcyC@-M`g<dx|WynXXYcyS@-M`CW0x`|fyn@@QcyS@-M`)D`d|znAAQcyC@-M`,Edx|nXXQcyS@-M`LW@Cuv"x`|%n@@YcyS@-M`L`d|mnAAYcyC@-M`Mdx|ʊnXXYcyS@-M`JUWP x`|؊n@@QcyS@-M`U`d|.nAAQcyC@-M`Vdx|nXXQcyS@-M`^W`TG9 DB0TA TS21x`|n@@YcyS@-M`T^`d|ΔnAAYcyC@-M`N_dx|JnXXYcyS@-M`fWpx`|Wn@@QcyS@-M`g`d|nAAQcyC@-M`hdx|٤nXXQcyS@-M`YoWCuvbx`|n@@YcyS@-M`o`d|M`gx`d|nAAQcyC@-M`wydx|UnXXQcyS@-M`ՏWBB DB0TA TS2!x`|bn@@YcyS@-M`"`d|nAAYcyC@-M`Cdx|!nXXYcyS@-M`WGx`|.n@@QcyS@-M``d|mnAAQcyC@-M`dx|nXXQcyS@-M`_WCPv}x`|n@@YcyS@-M``d|#nAAYcyC@-M`dx|nXXYcyS@-M`#W Ax`|n@@QcyS@-M`o`d|nAAQcyC@-M`~dx|SnXXQcyS@-M`ӲWDB0FX TS1x`|bn@@YcyS@-M`#`d|nAAYcyC@-M`dx|nXXYcyS@-M`Wx`|$n@@QcyS@-M``d|z oAAQcyC@-M`dx|oXXzS@-M`WCPvx`|o@@zS@-M``d|yoAAzC@-M`dx|oXXzS@-M`EW x`|o@@zS@-M``d|oAAzC@-M`dx|}$oXXzS@-M`W TG8 DB0FX TS2x`|$o@@zS@-M`I`d|%oAAzC@-M`>dx|6-oXXzS@-M`W0x`|A-o@@zS@-M``d|.oAAzC@-M`3dx|5oXX@zS@-M`?W@CPvx`|5o@@zS@-M``d|)7oAAzC@-M`dx|v>oXXzS@-M`WP x`|>o@@zS@-o@@zC@->M`D `d|?oAAzC@-M`a dx|0GoXXzS@-M`W`TG9 DB0FX TS2;x`|;Go@@zS@-M``d|HoAAzC@-M` dx|PoXX@zS@-M`Wpx`|Po@@zS@-M``d|LQoAAzC@-M`dx|XoXX@zS@-M`C#WCPv>x`|Xo@@zS@-M`#`d|0ZoAAzC@-M`$dx|saoXXzS@-M`+W  x`|~ao@@@zS@-M`<,`d|boAA@zC@-M`N-dx|ioXXzS@-M`F4WBB DB0FX TS2+x`|io@@@zS@-M`4`d|koAA@zC@-M`5dx|aroXXGGxS@-M`<WHx`|nro@@@NGxS@-M`@=`d|soAA@NGxC@-M`C>dx|!{oXX@NGxS@-M`EWCvx`|.{o@@GGxS@-M`E`d||oAAGGxC@-M`Gdx|oXX@GxS@-M`NW Bx`|o@@LGxS@-M`cN`d|oAALGxC@-M`sOdx|7oXXCGxS@-M`VWDB0PDM TS1x`|Do@@CGxS@-M` W`d|oAACGxC@-M`!Xdx|oXX@KGxS@-M`e_Wx`|o@@OGxS@-M`_`d|KoAAOGxC@-M``dx|oXXOGxS@-M`hW Cv_x`|o@@@KGxS@-M`>h`d|oAA@KGxC@-M`ridx|DoXXCGxS@-M`pW  x`|Oo@@CGxS@-M` q`d|ϧoAACGxC@-M`Ordx|îoXXLGxS@-M`CyW TG8 DB0PDM TS x`|Үo@@@GxS@-M`y`d|:oAA@GxC@-M`zdx|qoXXGGxS@-M`W 02x`|}o@@@NGxS@-M`K`d|oAA@NGxC@-M`3dx|*oXX@NGxS@-M`W @Cvx`|6o@@GGxS@-M` `d|roAAGGxC@-M`dx|oXX@GxS@-M`^W P x`|o@@LGxS@-M``d|0oAALGxC@-M`dx|oXXCGxS@-M`'W `TG9 DB0PDM TSMx`|o@@CGxS@-M``d|oAACGxC@-M`dx|oXXzS@-M`W p2;x`|)o@@zS@-M`Ȥ`d|hoAAzC@-M`dx|oXXzS@-M`cW Cvx`|o@@zS@-M``d|?oAAzC@-M`dx|oXXzS@-M`#W   x`|o@@@zS@-M`X`d|oAA@zC@-M`Wdx|eoXXzS@-M`W BB DB0PDM TS2ox`|qo@@zS@-M``d|oAAzC@-M`odx|oXX@zS@-M`W Ix`|'o@@zS@-M``d|oAAzC@-M`dx|pXXzS@-M`QW CPvx`|p@@zS@-M``d|pAAzC@-M`dx|YpXXzS@-M`W  Cx`|gp@@zS@-M``d|pAAzC@-M`5dx|pXX@zS@-M`sW DB0BRB TS1x`|p@@zS@-M``d|MpAAzC@-M`dx|pXXzS@-M`4W x`|p@@zS@-M``d|L!pAAzC@-M`dx|q(pXX@zS@-M`W CPvGx`|(p@@zS@-M`?`d|)pAAzC@-M`dx|Q?pXXzS@-M` W  x`|_?p@@zS@-M` `d|@pAAzC@-M`X dx|HpXXzS@-M`W TG8 DB0BRB TSx`|)Hp@@zS@-M``d|IpAAzC@-M`dx|PpXX@zS@-M`VW 02x`|Pp@@zS@-M``d|RpAAzC@-M`dx|YpXXzS@-M`.$W @CPvx`|Yp@@@zS@-M`~$`d|[pAA@zC@-M`%dx||bpXXzS@-M`,W P x`|bp@@zS@-M`T-`d|cpAAzC@-M`x.dx|M` 6`d|lpAAzC@-M`7dx|tpXXzS@-M`>W p2<x`|tp@@@zS@-``|Utp@@zC@->M`>`d|eupAA@zC@-M`?dx||pXXzS@-M`GW CPvx`||p@@zS@-M`mG`d|}pAAzC@-M`}Hdx|apXX@zS@-M`OW   x`|mp@@zS@-M`2P`d|†pAAzC@-M`BQdx|pXXzS@-M`qXW BB DB0BRB TS2ex`|p@@zS@-M`X`d|@pAAzC@-M`Ydx|pXXzS@-M`1aW Jx`|p@@zS@-a``| p@@zC@->M`a`d|pAAzC@-M`lbdx|spXX@zS@-M`iW CPvx`|p@@zS@-M`@j`d|ǠpAAzC@-M`Gkdx|"pXXzS@-M`rW  Dx`|0p@@zS@-M`r`d|pAAzC@-M`&tdx|pXX@zS@-M`b{W DB0SPN TS1x`|p@@zS@-M`{`d|@pAAzC@-M`|dx|pXXzS@-M`$W x`|p@@zS@-M`r`d|pAAzC@-M`odx|.pXXzS@-M`W CPvHx`|=p@@zS@-M``d|pAAzC@-M`dx|pXX@zS@-M`kW  x`|p@@zS@-M``d|NpAAzC@-M`Ζdx|pXXzS@-M`+W R/TG9 DB0SPN x`|p@@@zS@-M`u`d|.pAA@zC@-M`dx|apXXzS@-M`W 0TS2x`|mp@@zS@-M`.`d|pAAzC@-M`'dx|pXXzS@-M`W @CPvx`|*p@@zS@-M``d|qpAAzC@-M`dx|pXXzS@-M`WW P x`|p@@@zS@-M``d|pAA@zC@-M`dx|`pXXzS@-M`W `DB0NLS TS1x`|lp@@zS@-M`M`d|pAAzC@-M`tdx|pXX@zS@-M`mW p x`|p@@zS@-M``d|^qAAzC@-M`dx|qXXzS@-M`)W CPvTx`|q@@zS@-M`t`d| qAAzC@-M`dx|jqXXzS@-M`W   x`|vq@@zS@-M`6`d|qAAzC@-M`Adx|)qXX@zS@-M`W R/TG9 DB0NLS ux`|7q@@zS@-M``d|qAAzC@-M` dx|!qXXzS@-M`hW TS2$x`|!q@@zS@-M``d|J#qAAzC@-M`dx|*qXX@zS@-M`!W C%vDx`|*q@@zS@-M`j`d|+qAAzC@-M`hdx|[3qXXzS@-M`W  Ex`|f3q@@zS@-M` `d|4qAAzC@-M`&dx|M` `d|}=qAAzC@-M` dx|DqXX@zS@-M` W x`|Dq@@zS@-M`c `d|FqAAzC@-M` dx|VMqXXzS@-M` W C%vx`|aMq@@@zS@-M`9 `d|NqAA@zC@-M`2 dx|7VqXXMwS@-M` W  x`|HVq@@MwS@-M` `d|TWqAAMwC@-M`! dx||^qXX@MwS@-M`( W TG8 DB0LS TS2x`|^q@@MwS@-M`*) `d|_qAAMwC@-M`9* dx|&kqXXMwS@-M`5 W 0x`|5kq@@@MwS@-M`5 `d|TlqAA@MwC@-M`6 dx|sqXXMwS@-M`1> W @C%vx`|sq@@@MwS@- ``|sq@@MwC@->M`f> `d|.uqAA@MwC@-M`? dx|N|qXX@MwS@-M`F W P x`|\|q@@MwS@-M`(G `d|}qAAMwC@-M` H dx|҄qXX@MwS@-M`RO W `TG9 DB0LS TS2@x`|߄q@@MwS@-M`O `d|#qAAMwC@-M`P dx|^qXXMwS@-M`W W p x`|kq@@MwS@-M`*X `d|qAAMwC@-M`.Y dx|qXX@MwS@-M`0` W C%vx`|q@@MwS@-M`\` `d|ʖqAAMwC@-M`Ja dx|9qXXMwS@-M`h W   x`|Kq@@@MwS@-M`i `d|qAA@MwC@-M`3j dx|qXXMwS@-M`~ W BB DB0LS TS20x`|q@@MwS@-M`n~ `d|qAAMwC@-M`} dx|PqXX@MwS@-M`І W Lx`|_q@@MwS@-M`( `d|qAAMwC@-M`2 dx|qXXMwS@-M`9 W CPvx`|q@@@MwS@-M`h `d| qAA@MwC@-M` dx|rqXXMwS@-M` W  Fx`|q@@MwS@-M`O `d|qAAMwC@-M`` dx|qXX@MwS@-M` W DM0LC TS1x`|q@@MwS@-M` `d|-qAAMwC@-M` dx|qXXMwS@-M`= W x`|q@@MwS@-M` `d| qAAMwC@-M` dx|qXX@MwS@-M` W CPvIx`|q@@MwS@-M`[ `d|qAAMwC@-M`I dx|@qXXMwS@-M` W  x`|Pq@@@MwS@-M` `d|qAA@MwC@-M`@ dx|qXXMwS@-M` W TG8 DM0LC TS2x`|q@@MwS@-M` `d|rqAAMwC@-M` dx|rXX@MwS@-M`$ W 0x`|r@@MwS@-M`g `d|rAAMwC@-M`P dx|b rXXMwS@-M` W @CPvx`|q r@@@MwS@-M`9 `d| rAA@MwC@-M`L dx|'rXXMwS@-M` W P x`|9r@@MwS@-M` `d|rAAMwC@-M` dx|rXX@MwS@-M`k W `TG9 DM0LC TS2<x`|r@@MwS@-M`D `d|rAAMwC@-M`F dx|1rXXMwS@-M`L W p x`|1r@@MwS@-M` `d|43rAAMwC@-M` d`|m:r@@@MwS@-M` `d|#DrAA@MwC@-M` dx|zKrXXMwS@-M` W  x`|Kr@@@MwS@-M`Q `d|LrAA@MwC@-M`R dx|ATrXXMwS@-M` W Sa/Th DM0LC Tx`|QTr@@MwS@-M` `d|UrAAMwC@-M` dx|]rXX@MwS@-M`' W S2x`|]r@@MwS@-M`' `d|U^rAAMwC@-M`( dx|erXXMwS@-M`@0 W CPv x`|er@@@MwS@-M`0 `d|0grAA@MwC@-M`1 dx|}nrXXMwS@-M`8 W  Nx`|nr@@@MwS@-M`K9 `d|orAA@MwC@-M`X: dx|@wrXXMwS@-M`A W BB DM0LC TS1jx`|Owr@@MwS@-M`B `d|kxrAAMwC@-M`B dx|rXXMwS@-M`J W x`|r@@MwS@-M`J `d|erAAMwC@-M`K dx|rXXMwS@-M` S WC7Pvx`|r@@@MwS@-M`dS `d|rAA@MwC@-M`T dx|rXXMwS@-M`[ W x`|#r@@@MwS@-M`[ `d|srAA@MwC@-M`\ dx|ЙrXXMwS@-M`Pd W DB0FLW TS1Vx`|r@@MwS@-M`d `d|rAAMwC@-M`e dx|rXX@MwS@-M`m W0x`|r@@MwS@-M`am `d|rAAMwC@-M``n dx|SrXXMwS@-M`u W@C7Pv<x`|ar@@@MwS@-M`u `d|rAA@MwC@-M`w dx|rXXMwS@-M`w~ WP x`|r@@@MwS@-M`~ `d|hrAA@MwC@-M` dx|rXXMwS@-M` W`TG8 DB0FLW TSYx`|r@@MwS@-M`9 `d|rAAMwC@-M`l dx|rXXMwS@-M` Wp2@x`|0r@@MwS@-M`ˏ `d|mrAAMwC@-M` dx|rXXMwS@-M`A WC7Pv|x`|r@@@MwS@-M`n `d|rAA@MwC@-M` dx|)rXX@>zS@-M` W x`|>r@@@>zS@-zC@->M`ܠ `d|lrAA@>zC@-M` dx|rXX>zS@-M` WTG9 DB0FLW TSx`|r@@>zS@-zC@->M`F `d|rAA>zC@-M`{ dx|BrXX>zS@-M`± W2x`|Sr@@>zS@-zC@->M` `d|rAA>zC@-M` dx|rXX>zS@-M`> WC7Pvx`|r@@@>zS@-zC@->M`i `d| rAA@>zC@-M` dx|]rXXMwS@-M` W Qx`|or@@@MwS@-M` `d|rAA@MwC@-M`% dx|sXXMwS@-M`t WSa/Th DB0FLW x`|s@@MwS@-M` `d|OsAAMwC@-M` dx|o sXX@MwS@-M` WTS2gx`| s@@MwS@-M` `d| sAAMwC@-M`w dx|sXXMwS@-M` WCuvx`|+s@@@MwS@-M` `d|vsAA@MwC@-M` dx|sXX@>zS@-M`% W x`|s@@>zS@- ``|s@@@>zC@->M`Q `d|sAA>zC@-M` dx|0sXX>zS@-M` W DB0LE TS1x`|0s@@>zS@-zC@->M`4 `d|1sAA>zC@-M` dx|8sXX>zS@-M`N W0x`|8s@@>zS@-zC@->M` `d|:sAA>zC@-M` dx|%AsXX@>zS@-M` W@Cuv+x`|7As@@@>zS@-zC@->M` `d|MBsAA@>zC@-M` dx|lJsXX@>zS@-M` WP x`|uJs@@@>zS@-zC@->M`4 `d|KsAA@>zC@-M`( dx|SsXX>zS@-M`? W`R/TG9 DB0LE T1x`|Ss@@>zS@-zC@->M` `d|2UsAA>zC@-M` dx|/\sXX>zS@-M`& WpS2x`|=\s@@>zS@-zC@->M`' `d|]sAA>zC@-M`E( dx|1esXX>zS@-M`/ WCux`|=es@@@>zS@-zC@->M`/ `d|fsAA@>zC@-M`?1 dx|2nsXX@>zS@-M`8 W  x`|;ns@@>zS@-zC@->M`9 `d|osAA>zC@-M`<: dx|vsXX>zS@-M`]A WDL DB0AFZ TS1x`|vs@@>zS@-zC@->M`B `d|xsAA>zC@-M`C dx|sXX>zS@-M``J WOx`|s@@>zS@-zC@->M`J `d|asAA>zC@-M`K dx|sXX@>zS@-M`S WCux`|s@@@>zS@-zC@->M`+S `d|҉sAA@>zC@-M`RT dx|fsXX@>zS@-M`[ W Px`|rs@@@>zS@-zC@->M`K\ `d|sAA@>zC@-M`j] dx|6sXX>zS@-M`d WBB DB0AFZ TS1x`|>s@@>zS@-zC@->M`e `d|psAA>zC@-M`e dx|:sXX>zS@-M`m Wx`|Bs@@>zS@-zC@->M`n `d|sAA>zC@-M`q dx|sXX>zS@-M`? WCux`|s@@@>zS@-zC@->M` `d|FsAA@>zC@-M`ƙ dx|sXX@>zS@-M`W W x`|s@@>zS@-zC@->M` `d|NsAA>zC@-M`ΰ dx|sXX>zS@-M` W TG9 DB0AFZ TSx`|s@@>zS@-zC@->M`M `d|sAA>zC@-M`` dx|FsXX>zS@-M` W02x`|Rs@@>zS@-zC@->M` `d|sAA>zC@-M` dx|sXX@>zS@-M` W@CuNx`|s@@@>zS@-zC@->M` `d|vtAA@>zC@-M` dx|tXX@>zS@-M`R WP x`|t@@@>zS@-zC@->M`| `d|\ tAA@>zC@-M` dx|tXX>zS@-M` W`Hes DB0AFZ TSx`|t@@>zS@-zC@->M`8 `d|tAA>zC@-M`q dx|NtXX>zS@-M` Wp2Bx`|^t@@>zS@-zC@->M` `d|tAA>zC@-M` dx|"tXX>zS@-M` WC4P x`|#"t@@@>zS@-zC@->M` `d|r#tAA@>zC@-M` dx|*tXX@>zS@-M`` W x`|*t@@>zS@-zC@->M` `d|Q,tAA>zC@-M` dx|3tXX>zS@-M` WDMR S0x`|3t@@>zS@-zC@->M`@ `d|5tAA>zC@-M` dx|RzS@-M` WPx`|czS@-zC@->M` `d|=tAA>zC@-M`4 dx|EtXX@>zS@-M` WC6 x`|Et@@@>zS@-zC@->M` `d|lFtAA@>zC@-M` dx|MtXX@>zS@-M`U W Wx`|Mt@@@>zS@-zC@->M` `d|EOtAA@>zC@-M` dx|bVtXX>zS@-M` WDMR S1x`|yVt@@>zS@-zC@->M` ! `d|WtAA>zC@-M`E" dx|"_tXX>zS@-M`) Wx`|2_t@@>zS@-zC@->M`) `d|`tAA>zC@-M` + dx|gtXX>zS@-M`j2 WC6% Hx`|ht@@@>zS@-zC@->M`2 `d|5itAA@>zC@-M`3 dx|ptXX@>zS@-M`3; W x`|pt@@>zS@-zC@->M`; `d|mrtAA>zC@-M`< dx|{ytXX>zS@-M`C W DMR S2Jx`|yt@@>zS@-zC@->M`*D `d|ztAA>zC@-M`WE dx|StXX>zS@-M`L W0x`|bt@@>zS@-zC@->M`L `d|tAA>zC@-M`6N dx|tXX@>zS@-M`U W@C68 x`|&t@@@>zS@-zC@->M`U `d||tAA@>zC@-M`V dx|ғtXX@>zS@-M`R^ WP x`|t@@@>zS@-zC@->M`^ `d|tAA@>zC@-M`_ dx|tXX>zS@-M`g W`DMR S3x`|t@@>zS@-zC@->M`Ag `d|tAA>zC@-M`h dx|GtXX>zS@-M`o Wpx`|Tt@@>zS@-zC@->M`*p `d|tAA>zC@-M`$q dx| tXX>zS@-M`x WC6Psx`|t@@@>zS@-zC@->M`x `d|dtAA@>zC@-M`y dx|ƶtXX@>zS@-M`F W x`|Ҷt@@>zS@-zC@->M` `d|+tAA>zC@-M` dx|KtXX>zS@-M`ˉ WDMR S4x`|kt@@>zS@-zC@->M`? `d|tAA>zC@-M`E dx| tXX>zS@-M` WQx`|!t@@>zS@-zC@->M` `d|mtAA>zC@-M` dx|tXX@>zS@-M`C WC6c Fx`|t@@@>zS@-zC@->M` `d|*tAA@>zC@-M` dx|tXX@>zS@-M` W Xx`|t@@@>zS@-zC@->M`9 `d|tAA@>zC@-M`l dx|KtXX>zS@-M`ˬ WDMR S5 x`|Zt@@>zS@-zC@->M` `d|tAA>zC@-M` dx|tXX>zS@-M` Wx`|t@@>zS@-zC@->M` `d|tAA>zC@-M` dx|tXX>zS@-M`, WC6u x`|t@@@>zS@-zC@->M`b `d|tAA@>zC@-M` dx|utXX@>zS@-M` W x`|t@@>zS@-zC@->M`" `d|tAA>zC@-M`< dx|7uXX>zS@-M` W DMR S6Ox`|Eu@@>zS@-zC@->M` `d|uAA>zC@-M`K dx| uXX>zS@-M`; W0x`| u@@>zS@-zC@->M`l `d|uAA>zC@-M` dx|}uXX@xDwS@-M` W@C6 x`|u@@@{DwS@-M`\ `d|uAA@{DwC@-M`P dx|EuXX@{DwS@-M` WP x`|Wu@@@xDwS@-M` `d| uAA@xDwC@-M`+ dx|'uXXvDwS@-M`x W`DMR S7x`|(u@@qDwS@-M` `d|Z)uAAqDwC@-M` dx|0uXXwDwS@-M`< Wpx`|0u@@DwS@-M` `d|<2uAADwC@-M` dx|9uXX@~DwS@-M` WCbPvx`|9u@@}DwS@-M`P `d|:uAA}DwC@-M`X dx| BuXXvDwS@-M` W x`|)Bu@@yDwS@-M` `d|eCuAAyDwC@-M` dx|JuXXyDwS@-M`% WDB0LDSx`|Ju@@vDwS@-M`Z `d|KuAAvDwC@-M`k dx||SuXX}DwS@-M` WRx`|Su@@@~DwS@-M`1 `d|TuAA@~DwC@-M`f dx|f\uXXDwS@-M`& WCv6x`|t\u@@wDwS@-M`' `d|]uAAwDwC@-M`:( dx|,euXXqDwS@-M`/ W Hx`|:eu@@vDwS@-M`/ `d|sfuAAvDwC@-M`0 dx|muXX@xDwS@-M`f8 WDB0RAGx`|mu@@@{DwS@-M`8 `d|GouAA@{DwC@-M`9 dx|vuXX@{DwS@-M`A Wx`|vu@@@xDwS@-M`GA `d| xuAA@xDwC@-M`B dx|uXXvDwS@-M`I WCuvx`|u@@qDwS@-M`I `d|wuAAqDwC@-M`J dx|χuXXwDwS@-M`OR W x`|݇u@@DwS@-M`R `d|uAADwC@-M`S dx|uXXQcyS@-M`=[ W DB0LUD^x`|Ӑu@@YcyS@-M`p[ `d|uAAYcyC@-M`\ dx|uXXYcyS@-M` d W0x`|u@@QcyS@-M`Ad `d|9uAAQcyC@-M`e dx|IuXXQcyS@-M`l W@Vx`|Uu@@YcyS@-M`m `d|uAAYcyC@-M`2n dx| uXXYcyS@-M`u WP x`|u@@QcyS@-M`u `d|XuAAQcyC@-M`v dx|ԳuXXQcyS@-M`T~ W`DB0SP-2x`|u@@YcyS@-M`~ `d|4uAAYcyC@-M` dx|uXXYcyS@-M` Wpx`|u@@QcyS@-M`G `d|ݽuAAQcyC@-M`] dx|SuXXQcyS@-M`ӏ WC%vx`|`u@@YcyS@-M` `d|uAAYcyC@-M`@ dx|?uXX@>zS@-M` W x`|Ou@@@>zS@-zC@->M` `d|uAA@>zC@-M`' dx| uXX@>zS@-M` WDB0SP-700x`|u@@@>zS@-zC@->M` `d|iuAA@>zC@-M` dx|uXX>zS@-M` WSx`|u@@>zS@-zC@->M`> `d|uAA>zC@-M`m dx|TuXX>zS@-M`Կ WVbP x`|du@@>zS@-zC@->M` `d|uAA>zC@-M`, dx|uXX>zS@-M`o W Ix`|u@@@>zS@-zC@->M` `d|WuAA@>zC@-M` dx|vXX@>zS@-M`> WDB0ZOD-2x`|v@@>zS@-zC@->M`p `d|'vAA>zC@-M` dx|vXX>zS@-M`t Wx`|v@@>zS@-zC@->M` `d|avAA>zC@-M` dx|PvXX>zS@-M` WC%vx`|]v@@>zS@-zC@->M` `d|vAA>zC@-M`L dx| vXX@>zS@-M` W x`|) v@@@>zS@-zC@->M` `d|!vAA@>zC@-M` dx|(vXXYcyS@-M`a W DB0ZOD-70x`|(v@@QcyS@-M` `d|;*vAAQcyC@-M` dx|Y1vXXzS@-M` W0x`|g1v@@zS@-M` `d|2vAAzC@-M`* dx|:vXXzS@-M`W@W%x`| :v@@zS@-M``d|Z;vAAzC@-M`dx|BvXXzS@-M`8 WP x`|Bv@@@zS@-M` `d|'DvAA@zC@-M`dx|jKvXXzS@-M`W`DB0BRLx`|vKv@@zS@-M`*`d|LvAAzC@-M`8dx|SvXX@>zS@-M`XWpx`|Sv@@@>zS@-zC@->M``d|3UvAA@>zC@-M`dx|\vXX>zS@-M`'WCuvlx`|\v@@>zS@-zC@->M`G'`d|]vAA>zC@-M`(dx|=evXX>zS@-M`/W x`|Mev@@>zS@-zC@->M`/`d|fvAA>zC@-M`51dx|mvXX>zS@-M`h8WDB0BLOx`|mv@@@>zS@-zC@->M`8`d|JovAA@>zC@-M`9dx|vvXX@>zS@-M`'AWTx`|vv@@>zS@-zC@->M`SA`d|wvAA>zC@-M`WBdx|evXX>zS@-M`IWCPvx`|qv@@>zS@-zC@->M`zC@-M`/Kdx|vXX>zS@-M`RW Jx`|v@@>zS@-zC@->M`oR`d|vAA>zC@-M`hSdx|UvXX@>zS@-M`ZWDB0SXx`|av@@@>zS@-zC@->M`5[`d|vAA@>zC@-M`/\dx|vXX@>zS@-M`cWx`|$v@@@>zS@-zC@->M`c`d|zvAA@>zC@-M`ddx|ɡvXX>zS@-M`IlWC%vx`|աv@@>zS@-zC@->M`l`d|:vAA>zC@-M`mdx|vXX>zS@-M`uW x`|v@@>zS@-zC@->M`cu`d|ϫvAA>zC@-M`Ovdx|vXX>zS@-M`{}W DB0TAx`|v@@@>zS@-zC@->M`}`d|vAA@>zC@-M`dx|vXX@>zS@-M`W0x`|v@@>zS@-zC@->M`d`d|vAA>zC@-M`mdx|?vXX>zS@-M`W@VuRx`|Kv@@>zS@-zC@->M`!`d|vAA>zC@-M`dx|vXX>zS@-M`qWP x`|v@@>zS@-zC@->M`ė`d|vAA>zC@-M`dx|vXX@>zS@-M`2W`DB0PDMx`|v@@@>zS@-zC@->M`b`d|vAA@>zC@-M`~dx|LvXX@>zS@-M`̨Wpx`|^v@@@>zS@-zC@->M``d|vAA@>zC@-M`+dx|vXX>zS@-M`HWCPv>x`|v@@>zS@-zC@->M``d|5vAA>zC@-M`dx|vXX>zS@-M`W x`|v@@>zS@-zC@->M`h`d|vAA>zC@-M`Udx|DvXX>zS@-M`WDB0CBSx`|Pv@@@>zS@-zC@->M`$`d|vAA@>zC@-M`#dx|wXX@>zS@-M`WUx`|-w@@>zS@-zC@->M``d|wAA>zC@-M`dx| wXX>zS@-M`\WC7Pvx`| w@@>zS@-zC@->M``d|C wAA>zC@-M`dx|wXX>zS@-M`W Kx`|w@@>zS@-zC@->M`x`d|wAA>zC@-M`zdx|TwXX@>zS@-M`WDB0AFx`|aw@@@>zS@-zC@->M`4`d|wAA@>zC@-M`dx|$wXX@>zS@-M`Wx`|$w@@@>zS@-zC@->M``d|%wAA@>zC@-M`5dx|,wXX>zS@-M`TWCPvx`|,w@@>zS@-zC@->M``d|4.wAA>zC@-M`dx|[5wXX@zS@-M`W x`|i5w@@zS@-M``d|v6wAAzC@-M`dx|=wXXzS@-M`AW DB0NFL\x`|=w@@zS@-M`o`d|>wAAzC@-M`v dx|SwXXzS@-M`|W0x`|Tw@@zS@-M``d|]UwAAzC@-M`dx|\wXXzS@-M`'W@Cvx`|\w@@@zS@-M`L'`d|]wAA@zC@-M`[(dx|EewXXzS@-M`/WP x`|Vew@@zS@-M`/`d|fwAAzC@-M`1dx|nwXXzS@-M`8W`DB0LMMx`|nw@@zS@-M`8`d|GowAAzC@-M`9dx|vwXX@zS@-M`8AWpx`|vw@@zS@-M`lA`d|:xwAAzC@-M`zdx|6wXXzS@-M`vWCPvHx`|Bw@@zS@-M``d|wAAzC@-M`dx|wXXzS@-M`2W x`|w@@zS@-``|&w@@zC@->M`f`d|;wAAzC@-M`{dx|wXX@zS@-M`WDM0LEIx`|w@@zS@-M``d|ǑwAAzC@-M`dx|;wXXzS@-M`{!WVx`|Hw@@@zS@-M`A"`d|ޚwAA@zC@-M`#dx|ϡwXX@zS@-M`*WWux`|ܡw@@zS@-M`E*`d|3wAAzC@-M`s+dx|wXXzS@-M`2W Lx`|w@@@zS@-M`3`d|ګwAA@zC@-M`4dx|owXX>zS@-M`;WDB0LEIx`|w@@>zS@-zC@->M`;`d|شwAA>zC@-M`=dx|AwXX>zS@-M`DWx`|Uw@@@>zS@-zC@->M`D`d|wAA@>zC@-M`Edx|VwXX@>zS@-M`LWCuvx`|ew@@>zS@-zC@->M`L`d|wAA>zC@-M`Ndx|wXX>zS@-M`$UW x`|w@@>zS@-zC@->M`PU`d|HwAA>zC@-M`Vdx|wXX>zS@-M`]W DB0SMLix`|w@@>zS@-zC@->M` ^`d|wAA>zC@-M`&_dx|fwXX@>zS@-M`fW0x`||w@@@>zS@-zC@->M`f`d|wAA@>zC@-M`hdx|,wXX@>zS@-M`loW@VbPx`|;w@@@>zS@-zC@->M`o`d|wAA@>zC@-M`pdx|wXX>zS@-M`#xWP x`|w@@>zS@-zC@->M`Ox`d|!wAA>zC@-M`aydx|wXX>zS@-M`W`DB0LSAx`|w@@>zS@-zC@->M``d|wAA>zC@-M`*dx|mxXX>zS@-M`Wpx`|~x@@@>zS@-zC@->M`ى`d|xAA@>zC@-M`dx|. xXX@>zS@-M`nWCPvx`|J x@@>zS@-zC@->M``d| xAA>zC@-M`ӓdx|xXX>zS@-M`/W x`|x@@>zS@-zC@->M`]`d|yxAA>zC@-M`dx|xXX>zS@-M`WDB0LEx`|x@@>zS@-zC@->M`'`d|"xAA>zC@-M`bdx|x$xXX@>zS@-M`WWx`|$x@@@>zS@-zC@->M``d|%xAA@>zC@-M`dx|9-xXX@>zS@-M`yWFuH~x`|P-x@@@>zS@-zC@->M``d|.xAA@>zC@-M`dx|5xXX>zS@-M`8W Nx`|6x@@>zS@-zC@->M`f`d|q7xAA>zC@-M`dx|>xXX>zS@-M`WDOK Y07Ex`|>x@@>zS@-zC@->M`P`d|?xAA>zC@-M`,dx|XGxXX>zS@-M`Wx`|fGx@@@>zS@-zC@->M``d|cxAA@>zC@-M`5dx|XixXX@>zS@-M`WFPZx`|iix@@>zS@-zC@->M``d|jxAA>zC@-M`dx|rxXX>zS@-M`OW x`|rx@@>zS@-zC@->M`{`d|YsxAA>zC@-M`dx|zxXX>zS@-M`W DOK D20lx`|zx@@>zS@-zC@->M``d|e|xAA>zC@-M`dx|^xXX@>zS@-M` W0x`|ox@@@>zS@-zC@->M` `d|xAA@>zC@-M` dx|1xXX@>zS@-M`qW@Sux`|Ax@@@>zS@-zC@->M``d|xAA@>zC@-M`dx|xXX>zS@-M`'WP x`|x@@>zS@-zC@->M`S`d|ExAA>zC@-M`dx|xXX>zS@-M`%W`DOK D23x`|x@@>zS@-zC@->M`%`d|ŞxAA>zC@-M`'dx|&xXX>zS@-M`f.Wpx`|:x@@@>zS@-zC@->M`.`d|xAA@>zC@-M`/dx|ծxXX@zS@-M`7WG%x`|x@@zS@-M`@7`d|*xAAzC@-M`j8dx|xXXzS@-M`?W x`|x@@zS@-M`(@`d| xAAzC@-M`JAdx|xXXzS@-M`IWDOK S31x`|x@@zS@-M`;I`d|/xAAzC@-M`oJdx|xXXzS@-M`QWXx`|x@@@zS@-M`'R`d|xAA@zC@-M`NSdx|vxXXzS@-M`ZWSx`|x@@zS@-M`Z`d|xAAzC@-M`\dx| xXX@zS@-M``cW Ox`|-x@@zS@-M`c`d|}xAAzC@-M`ddx|xXXzS@-M`lWS20=x`|x@@zS@-M`Fl`d| xAAzC@-M`Kmdx|xXXzS@-M`tWx`|x@@zS@-M`!u`d|xAAzC@-M`%vdx|[xXX@zS@-M`}WUx`||x@@zS@-M`}`d|xAAzC@-M`~dx|xXXzS@-M`W x`|x@@zS@-M`,`d|xAAzC@-M`Pdx|yyXX@zS@-M`W 2m Mobil{x`|y@@zS@-M``d|yAAzC@-M`dx|6yXXzS@-M`vW0x`|Cy@@zS@-M``d|yAAzC@-M`dx|yXXzS@-M`5W@C5ix`|y@@zS@-M`o`d|lyAAzC@-M`dx| yXX@zS@-M`רWP x`| y@@zS@-M` `d|!yAAzC@-M`(dx|)yXXzS@-M`ZW`70cm MobilSx`|+)y@@@zS@-M``d|*yAA@zC@-M`²dx|1yXX@zS@-M`"Wpx`|1y@@zS@-M`V`d|S3yAAzC@-M`dx|:yXXzS@-M`WHx`|:y@@@zS@-M`#`d|;yAA@zC@-M`6dx|GCyXX@zS@-M`W x`|VCy@@zS@-M``d|DyAAzC@-M`dx|KyXXzS@-M`WAPRSx`|Ky@@@zS@-M`O`d|JMyAA@zC@-M`dx|TyXX@zS@-M`WYx`|Ty@@zS@-M` `d|UyAAzC@-M`0dx|Z]yXXzS@-M`WX%x`|g]y@@@zS@-M``d|^yAA@zC@-M`dx|eyXX@zS@-M`W Px`|ey@@zS@-M`-`d|gyAAzC@-M`Adx|YnyXXzS@-M`WISS APRSx`|jny@@@zS@-M``d|oyAA@zC@-M`dx|vyXX@zS@-M`Wx`|vy@@zS@-M``d| xyAAzC@-M`Idx|qyXXzS@-M`WCp)3x`|y@@@zS@-M``d|ЀyAA@zC@-M` dx|;yXX@zS@-M`{W x`|Ly@@zS@-M``d|yAAzC@-M`dx|yXXzS@-M`UW ISS FM Repx`|%y@@@zS@-M``d|yAA@zC@-M`dx|˙yXX@zS@-M` "W0x`|ٙy@@zS@-M`B"`d|6yAAzC@-M`v#dx|pyXXzS@-M`*W@Cr)(Px`|y@@@zS@-M`*`d|yAA@zC@-M`+dx|M`3`d|yAAzC@-M`4dx|yXXzS@-M`:<W`CAS-3H FM<x`|y@@@zS@-M`<`d|`yAA@zC@-M`=dx|yXX@zS@-M`DWpx`|˼y@@zS@-M`ME`d|*yAAzC@-M`jFdx|YyXXzS@-M`MWY)Hx`|gy@@@zS@-M`M`d|yAA@zC@-M`Ndx|yXX@zS@-M`VVW x`|#y@@zS@-M`V`d|RyAAzC@-M`Wdx|yXXzS@-M` _WPO-101 FM[x`|y@@@zS@-M`Y_`d|(yAA@zC@-M`h`dx|_yXX@zS@-M`gWZx`|qy@@zS@-M`g`d|yAAzC@-M`hdx|yXXzS@-M`_pWCg) @x`|-y@@@zS@-M`p`d|jyAA@zC@-M`qdx|yXX@zS@-M`xW Px`|y@@zS@-M`Ky`d|yAAzC@-M`Rzdx|yXXzS@-M`ÁWSO-50 FMqx`|y@@@zS@-M``d|yAA@zC@-M`dx|DzXX@zS@-M`Wx`|Pz@@zS@-M`ϊ`d|{zAAzC@-M`dx| zXXzS@-M`WX(pHx`| z@@@zS@-M`1`d| zAA@zC@-M`Hdx|)zXX@zS@-M`iW x`|5z@@zS@-M``d|zAAzC@-M`dx|zXXzS@-M`%W AO-92 FMx`|z@@@zS@-M`r`d|5zAA@zC@-M`udx|y$zXX@zS@-M`W0x`|$z@@zS@-M``d|%zAAzC@-M`dx|U-zXXzS@-M`W@Y`(HOx`|b-z@@@zS@-M``d|.zAA@zC@-M` dx|6zXX@zS@-M`OWP x`|6z@@zS@-M``d|7zAAzC@-M`ƿdx|>zXXzS@-M`W`AO-91 FMx`|>z@@@zS@-z@@zC@->M`5`d| @zAA@zC@-M`Idx|GzXX@zS@-M`Wpx`|Gz@@zS@-M``d|HzAAzC@-M`dx|4PzXXzS@-M`tWCpx`|@Pz@@@zS@-M``d|wQzAA@zC@-M`dx|XzXX@zS@-M`(W x`|Xz@@zS@-M`U`d|EZzAAzC@-M`dx|BazXXzS@-M`WU0x`|Oaz@@@zS@-M``d|bzAA@zC@-M`dx|izXX@zS@-M`W[x`|iz@@zS@-M`-`d|kzAAzC@-M`_dx|^rzXXzS@-M`WC%@x`|jrz@@@zS@-M``d|szAA@zC@-M`dx|zzXXovS@-M`W Qx`|zz@@ovS@-M`@`d||zAAovC@-M`Bdx|,zXXovS@-M`l WU1x`|@z@@ovS@-M` `d|`zAAovC@-M` dx|zXXovS@-M`Wx`|z@@@ovS@-M``d|zAA@ovC@-M`dx|zXXovS@-M`%WCPAx`|z@@ovS@-M`Q`d|*zAAovC@-M`jdx||zXX@ovS@-M`$W x`|z@@ovS@-M`$`d|zAAovC@-M`%dx|zXX@ovS@-M`'-W U2Sx`|z@@ovS@--``|z@@@ovC@->M`Y-`d| zAAovC@-M`M.dx|FzXXovS@-M`5W0x`|Wz@@ovS@-M`5`d|zAAovC@-M`6dx|zXXovS@-M`=W@Cux`|z@@ovS@-M`=`d|zAAovC@-M`>dx|zXXovS@-M`:FWP x`| z@@ovS@-M`jF`d|yzAAovC@-M`Gdx|:zXXovS@-M`zNW`U3x`|Dz@@ovS@-M`N`d|zAAovC@-M`Odx|zXXovS@-M`VWpx`|z@@ovS@-M`W`d|zAAovC@-M`Xdx|zXXovS@-M`X_WD`%@;x`|$z@@ovS@-M`_`d|gzAAovC@-M``dx|zXXovS@-M`hW x`|z@@ovS@-M``h`d|/zAAovC@-M`oidx|fzXXovS@-M`pWPMR 1x`|vz@@ovS@-M`p`d|zAAovC@-M`qdx|/zXXovS@-M`oyW\x`|>z@@ovS@-M`.z`d|zAAovC@-M`zdx|zXXovS@-M`WD`u@x`|z@@ovS@-``|z@@ovC@->M`]`d|!zAAovC@-M`adx|{XXovS@-M`ӊW Sx`|{@@ovS@-M``d|{AAovC@-M`dx|Y {XXovS@-M`WPMR 2x`|h {@@ovS@-M`ғ`d| {AAovC@-M`dx|-{XXovS@-M`mWx`|<{@@ovS@-M``d|{AAovC@-M`ݝdx|{XXovS@-M`WD`1%@x`|{@@ovS@-M`A`d|6{AAovC@-M`vdx|%{XXovS@-M`ӭW x`|%{@@ovS@-M` `d|&{AAovC@-M`=dx|@.{XXovS@-M`W PMR 3x`|P.{@@ovS@-M``d|/{AAovC@-M`dx|7{XXovS@-M`ZW0x`|,7{@@ovS@-M``d|8{AAovC@-M`dx|?{XXovS@-M`W@D`CuIx`|?{@@ovS@-M`)`d| A{AAovC@-M`Kdx|vH{XXovS@-M`WP x`|H{@@ovS@-M``d|I{AAovC@-M`dx|?Q{XXovS@-M`W`PMR 4Px`|NQ{@@ovS@-M``d|R{AAovC@-M`dx| Z{XXovS@-M`IWpx`|Z{@@ovS@-M``d|W[{AAovC@-M`dx|b{XXovS@-M`WD`V%Lx`|b{@@ovS@-M`?`d|3d{AAovC@-M`sdx|Lk{XXovS@-M`W x`|Yk{@@ovS@-M``d|l{AAovC@-M`dx|t{XXovS@-M`UWPMR 5x`|#t{@@ovS@-M``d|gu{AAovC@-M`dx||{XXovS@-M`W]x`||{@@ovS@-M`/`d|}{AAovC@-M`dx|S{XXovS@-M` WD`hu@.x`|c{@@ovS@-M` `d|{AAovC@-M`dx|E{XXovS@-M`W Tx`|_{@@ovS@-M``d|{AAovC@-M`dx| {XXovS@-M`IWPMR 6x`|{@@ovS@-M``d|X{AAovC@-M` dx|Ÿ{XXovS@-M`(Wx`|џ{@@ovS@-M`W(`d|){AAovC@-M`i)dx|õ{XXovS@-M`>WD`%@8x`|Ե{@@ovS@-``|{@@ovC@->M`Y>`d|{AAovC@-M`D?dx|{XXovS@-M`FW x`|{@@ovS@-M`!G`d|{AAovC@-M`7Hdx|K{XXovS@-M`OW PMR 7x`|Z{@@ovS@-M`O`d|{AAovC@-M`Pdx|({XXMwS@-M`hXW0x`|:{@@@MwS@-M`X`d|{AA@MwC@-M`Ydx|{XXMwS@-M`aW@D`u@x`|{@@MwS@-M`pa`d|;{AAMwC@-M`{bdx|{XXMwS@-M`iWP x`|{@@MwS@-M`*j`d|{AAMwC@-M`jdx|F{XX@>zS@-M`rW`PMR 8Ux`|X{@@>zS@-zC@->M`r`d|{AA>zC@-M`sdx|{XX>zS@-M` {Wpx`|{@@>zS@-zC@->M`9{`d|){AA>zC@-M`i|dx|{XX>zS@-M`ԃWDa%x`|{@@>zS@-zC@->M`C`d|{AA>zC@-M`dx|]|XX@>zS@-M`W x`|k|@@@>zS@-zC@->M`ʌ`d||AA@>zC@-M`dx| |XX@>zS@-M`^WPMR 9x`|, |@@@>zS@-zC@->M``d|n|AA@>zC@-M`dx||XX>zS@-M`'W^x`||@@>zS@-zC@->M`_`d|N|AA>zC@-M`dx||XX>zS@-M`WDaux`||@@>zS@-zC@->M`i`d| |AA>zC@-M`Ydx|?'|XX>zS@-M`W Ux`|M'|@@@>zS@-zC@->M``d|(|AA@>zC@-M`԰dx|0|XX@>zS@-M`PWPMR 10x`|0|@@>zS@-zC@->M`}`d|A1|AA>zC@-M`dx|8|XX>zS@-M`Wx`|8|@@>zS@-zC@->M`,`d|9|AA>zC@-M`dx|A|XX>zS@-M`PWDa1%@x`|A|@@>zS@-zC@->M``d|B|AA>zC@-M`dx|I|XX@>zS@-M`W x`|I|@@@>zS@-zC@->M`g`d|K|AA@>zC@-M`Bdx|qR|XXMwS@-M`W PMR 11@x`|~R|@@@MwS@-M``d|S|AA@MwC@-M` dx|Z|XX@MwS@-M`9W0x`|[|@@MwS@-M``d|J\|AAMwC@-M`dx|c|XXMwS@-M`W@DaCuLx`|c|@@@MwS@-M``d|ye|AA@MwC@-M`dx|kl|XX@MwS@-M`WP x`|vl|@@MwS@-M``d|m|AAMwC@-M`dx|t|XXMwS@-M`<W`PMR 12x`| u|@@@MwS@-M``d|Zv|AA@MwC@-M`dx|}|XX@MwS@-M`Wpx`|}|@@MwS@-M``d|~|AAMwC@-M` dx|H|XXMwS@-M`WDaV%Ox`|U|@@@MwS@-M``d||AA@MwC@-M`dx||XX@MwS@-M`@W x`| |@@MwS@-M``d|k|AAMwC@-M`dx||XXMwS@-M`WPMR 13x`|×|@@@MwS@-M`D `d||AA@MwC@-M`V!dx|w|XX@MwS@-M`(W_x`||@@MwS@-M`)`d|١|AAMwC@-M`*dx|6|XXMwS@-M`v1WDahux`|C|@@@MwS@-M`1`d|{|AA@MwC@-M`2dx||XX@MwS@-M`7:W Vx`||@@MwS@-M`:`d||AAMwC@-M`;dx||XXMwS@-M`BWPMR 14x`||@@@MwS@-M`=C`d||AA@MwC@-M`UDdx|_|XX@>zS@-M`KWx`|n|@@@>zS@-zC@->M`L`d||AA@>zC@-M`5hdx|U|XX>zS@-M`mW x`|`|@@>zS@-zC@->M`m`d||AA>zC@-M`ndx||XX>zS@-M`NvW x`||@@>zS@-zC@->M`v`d|P|AA>zC@-M`wdx||XX>zS@-M`W x`||@@@>zS@-zC@->M`\`d| |AA@>zC@-M`Jdx||XX@>zS@-M`ȇW 0x`||@@>zS@-zC@->M`&`d|}AA>zC@-M`#dx|}XX>zS@-M`NW @x`|}@@>zS@-zC@->M``d|u }AA>zC@-M`dx|}XX>zS@-M`՘W Px`|}@@>zS@-zC@->M`4`d|}AA>zC@-M`Ndx|F}XX@>zS@-M`W `x`|Q}@@@>zS@-zC@->M``d|}AA@>zC@-M`΢dx|!}XX@>zS@-M`W p x`|!}@@@>zS@-zC@->M`q`d|"#}AA@>zC@-M`bdx|*}XX>zS@-M`ƲW 0x`|*}@@>zS@-zC@->M``d|+}AA>zC@-M`dx|?3}XX>zS@-M`W @x`|M3}@@>zS@-zC@->M``d|4}AA>zC@-M`߼dx|;}XX>zS@-M`.W Px`|;}@@@>zS@-zC@->M`c`d|8=}AA@>zC@-M`xdx|D}XX@>zS@-M`W `x`|D}@@>zS@-zC@->M``d|E}AA>zC@-M`*dx|7M}XX>zS@-M`wW px`|KM}@@>zS@-zC@->M``d|N}AA>zC@-M`dx|U}XX@MwS@-M`W x`|U}@@MwS@-M`P`d|?W}AAMwC@-M`dx|l^}XXMwS@-M`W x`|w^}@@@MwS@-M``d|_}AA@MwC@-M` dx|&g}XX@MwS@-M`fW x`|2g}@@MwS@-M``d|th}AAMwC@-M`dx|o}XXMwS@-M`W!x`|o}@@@MwS@-M`E`d|2q}AA@MwC@-M`rdx|x}XX@MwS@-M`W!x`|x}@@MwS@-M``d|y}AAMwC@-M`dx|+}XXMwS@-M`k W! x`|6}@@@MwS@-M` `d|}AA@MwC@-M` dx|}XX@MwS@-M`W!0x`|}@@MwS@-M``d|Њ}AAMwC@-M`dx|}XXMwS@-M`RW!@x`|$}@@@MwS@-M``d|}AA@MwC@-M`dx|Ś}XX@MwS@-M`#W!Px`|Қ}@@MwS@-M`<#`d|#}AAMwC@-M`c$dx|}XXMwS@-M`+W!`x`|}@@@MwS@-M`,`d|}AA@MwC@-M`--dx|_}XX@MwS@-M`4W!p!x`|o}@@MwS@-M`4`d|}AAMwC@-M`6dx|}XXMwS@-M`_=W!1x`|.}@@@MwS@-M`=`d|T}AA@MwC@-M`>dx|}XX@MwS@-M`EW!Ax`|ǽ}@@MwS@-M`.F`d|}AAMwC@-M`[Gdx|~}XXMwS@-M`NW!Qx`|}@@@MwS@-M`N`d|}AA@MwC@-M`Pdx|K}XXMwS@-M`WW!ax`|Y}@@MwS@-M`W`d|}AAMwC@-M`Xdx| }XX@MwS@-M`M`W!qx`|}@@MwS@-M```d|}AAMwC@-M`adx|}XXMwS@-M`iW!x`|}@@@MwS@-M`Ji`d|}AA@MwC@-M`$jdx|}XX@MwS@-M`qW!x`|}@@MwS@-M`r`d|}AAMwC@-M`sdx|D}XXMwS@-M`zW!x`|R}@@@MwS@-M`z`d|}AA@MwC@-M`{dx|}XXMwS@-M`W"x`|}@@MwS@-M``d| }AAMwC@-M`Jdx|`~XX@MwS@-M`W"x`|m~@@MwS@-M`׋`d|~AAMwC@-M`dx| ~XXMwS@-M`^W" x`|+ ~@@MwS@-M``d| ~AAMwC@-M`•dx|~XX@MwS@-M`W"0x`|~@@MwS@-M`J`d|~AAMwC@-M`Qdx|~XXMwS@-M`ۥW"@x`|~@@@MwS@-M``d|~AA@MwC@-M`?dx|P&~XXMwS@-M`W"Px`|]&~@@MwS@-M`Ů`d|'~AAMwC@-M`dx|/~XX@MwS@-M`NW"`x`|/~@@MwS@-M``d|G0~AAMwC@-M`dx|7~XXMwS@-M`W"p"x`|7~@@@MwS@-M`F`d|@9~AA@MwC@-M`dx|@~XXMwS@-M`W"2x`|@~@@MwS@-M``d|A~AAMwC@-M`5dx|$I~XX@MwS@-M`dW"Bx`|1I~@@MwS@-M``d|yJ~AAMwC@-M`dx|Q~XXMwS@-M`$W"Rx`|Q~@@MwS@-M`Z`d|BS~AAMwC@-M`dx|Z~XXMwS@-M`W"bx`|Z~@@@MwS@-M``d|\~AA@MwC@-M`Hdx|gc~XX@MwS@-M`W"rx`|wc~@@MwS@-M``d|d~AAMwC@-M`dx|&l~XXMwS@-M`fW"x`|3l~@@@MwS@-M``d|{m~AA@MwC@-M`dx|t~XX@MwS@-M`!W"x`|t~@@MwS@-M`T`d|*v~AAMwC@-M`jdx|}~XXMwS@-M`W"x`|}~@@@MwS@-M``d|~~AA@MwC@-M`?dx|R~XX@MwS@-M`W#x`|^~@@MwS@-M``d|~AAMwC@-M`dx|~XXMwS@-M`NW#x`|~@@@MwS@-M``d||~AA@MwC@-M`dx|~XX@MwS@-M`W# x`|~@@MwS@-M` `d|~AAMwC@-M`&!dx|D~XXMwS@-M`(W#0x`|P~@@@MwS@-M`(`d|~AA@MwC@-M`)dx|~XX>zS@-M`31W#@x`| ~@@>zS@-zC@->M`\1`d|P~AA>zC@-M`2dx|~XX@>zS@-M`9W#Px`|~@@@>zS@-zC@->M`:`d|>~AA@>zC@-M`~;dx|l~XX@>zS@-M`BW#`x`|~@@@>zS@-zC@->M`B`d|ػ~AA@>zC@-M`Ddx|(~XX>zS@-M`hKW#p#x`|9~@@>zS@-zC@->M`K`d|~AA>zC@-M`Ldx|~XX>zS@-M`#TW#3x`|~@@>zS@-zC@->M`NT`d| ~AA>zC@-M``Udx|~XX>zS@-M`\W#Cx`|~@@@>zS@-zC@->M`\`d|~AA@>zC@-M`3^dx|K~XX@>zS@-M`eW#Sx`|Z~@@>zS@-zC@->M`e`d|~AA>zC@-M`fdx|~XX>zS@-M`=nW#cx`| ~@@>zS@-zC@->M`fn`d|^~AA>zC@-M`odx|~XX>zS@-M`vW#sx`|~@@>zS@-zC@->M`w`d|~AA>zC@-M`5xdx|=~XX@>zS@-M`}W#x`|R~@@@>zS@-zC@->M``d|p~AA@>zC@-M`dx|XX@>zS@-M`DW#x`|@@@>zS@-zC@->M`p`d|DAA@>zC@-M`dx|XX>zS@-M`ϐW#x`|@@>zS@-zC@->M``d| AA>zC@-M`6dx|XX>zS@-M`NW$x`|"@@>zS@-zC@->M`z`d|oAA>zC@-M`dx|XX>zS@-M`ݡW$x`|@@@>zS@-zC@->M``d|AA@>zC@-M`)dx|X"XX@MwS@-M`W$ x`|f"@@MwS@-M`ͪ`d|#AAMwC@-M`dx|*XXMwS@-M`"W$0x`|*@@@MwS@-M``d|S,AA@MwC@-M`dx|[3XX@MwS@-M`W$@x`|g3@@MwS@-M`ݻ`d|4AAMwC@-M`dx|;XXMwS@-M`W$Px`|;@@@MwS@-M`L`d|6=AA@MwC@-M`vdx|DXX@MwS@-M`W$`x`|D@@MwS@-M``d|EAAMwC@-M`0dx|*MXXMwS@-M`jW$p$x`|6M@@@MwS@-M``d|tNAA@MwC@-M`dx|UXX@MwS@-M`W$4x`|U@@MwS@-M`R`d|IWAAMwC@-M`dx|^^XXMwS@-M`W$Dx`|i^@@@MwS@-M``d|_AA@MwC@-M` dx|gXX@MwS@-M`\W$Tx`|)g@@MwS@-M``d|hAAMwC@-M`dx|oXXMwS@-M`W$dx`|o@@@MwS@-M``d|pAA@MwC@-M`5dx|axXX@MwS@-M`W$tx`|mx@@MwS@-M``d|yAAMwC@-M`dx| XXMwS@-M`` W$x`|-@@@MwS@-M` `d|AA@MwC@-M` dx|ΉXX@MwS@-M`W$x`|܉@@MwS@-M`:`d| AAMwC@-M`Jdx|qXX@>zS@-M`W$x`|@@>zS@-zC@->M``d|AA>zC@-M`dx|6XX>zS@-M`v#W%x`|N@@>zS@-zC@->M`#`d|AA>zC@-M`$dx|XX>zS@-M`D,W%x`|@@>zS@-zC@->M`,`d|jAA>zC@-M`-dx|ɬXX@>zS@-M` 5W% x`|٬@@@>zS@-zC@->M`5`d|4AA@>zC@-M`t6dx|XXMwS@-M`=W%0x`|@@@MwS@-M` >`d|AA@MwC@-M`D?dx|0XXMwS@-M`pFW%@x`|?@@MwS@-M`F`d|AAMwC@-M`Gdx|XX@MwS@-M`1OW%Px`|@@MwS@-M`fO`d|AAMwC@-M`Pdx|mXXMwS@-M`WW%`x`|z@@@MwS@-M`W`d|AA@MwC@-M`(Ydx|3XX@MwS@-M`s`W%p%x`|@@@MwS@-M```d|AAMwC@-M`adx|XXMwS@-M`4iW%5x`| @@@MwS@-M`mi`d|JAA@MwC@-M`jdx|XXMwS@-M`qW%Ex`|@@MwS@-M`0r`d|AAMwC@-M`>sdx|vXX@MwS@-M`zW%Ux`|@@MwS@-M`z`d|AAMwC@-M`|dx|XXMwS@-M`9W%ex`|@@MwS@-M`|`d|FAAMwC@-M`dx|XX@MwS@-M`W%ux`|@@MwS@-M`/`d|!AAMwC@-M`adx| XXMwS@-M`͔W%x`| @@@MwS@-M``d| AA@MwC@-M`dx|JXX@MwS@-M`W%x`|V@@MwS@-M``d|AAMwC@-M`dx|XXMwS@-M`W%x`|@@@MwS@-M`K`d|8AA@MwC@-M`xdx|d&XX@>zS@-M`W&x`|u&@@@>zS@-zC@->M`Ү`d|'AA@>zC@-M`dx|/XX>zS@-M`_W&x`|./@@>zS@-zC@->M``d|~0AA>zC@-M`dx|7XX>zS@-M`W& x`|7@@>zS@-zC@->M`E`d|C9AA>zC@-M`dx|@XX>zS@-M`W&0x`|@@@@>zS@-zC@->M` `d|,BAA@>zC@-M`ldx|&IXX@>zS@-M`fW&@x`|5I@@>zS@-zC@->M``d|JAA>zC@-M`dx|QXX>zS@-M`(W&Px`|Q@@>zS@-zC@->M`T`d|,SAA>zC@-M`ldx|ZXX>zS@-M`W&`x`|Z@@>zS@-zC@->M` `d|[AA>zC@-M`,dx|`cXX@>zS@-M`W&p&x`|nc@@@>zS@-zC@->M``d|dAA@>zC@-M`dx|kXX@>zS@-M`)W&6x`|k@@@>zS@-zC@->M`U`d|NmAA@>zC@-M`dx|tXX>zS@-M`W&Fx`|t@@>zS@-zC@->M``d|uAA>zC@-M`/dx|n}XX>zS@-M` W&Vx`|}@@>zS@-zC@->M` `d|~AA>zC@-M` dx|:XX>zS@-M`z W&fx`|P@@@>zS@-zC@->M` `d|AA@>zC@-M` dx|XX@>zS@-M`_ W&vx`|-@@>zS@-zC@->M` `d|DAA>zC@-M` dx|ǖXX@MwS@-M` W&x`|Ֆ@@MwS@-M`6 `d|AAMwC@-M`C dx|DXX>zS@-M`' W&x`|T@@>zS@-zC@->M`' `d|yAA>zC@-M`( dx|XX>zS@-M`=4 W&x`|@@>zS@-zC@->M`o4 `d|SAA>zC@-M`5 dx|XX@>zS@-M`< W'x`|@@@>zS@-zC@->M` = `d|AA@>zC@-M`C> dx|eXX@>zS@-M`E W'x`|s@@@>zS@-zC@->M`E `d|־AA@>zC@-M`G dx|ŀXX>zS@-M`-N W' x`|ƀ@@>zS@-zC@->M`_N `d|VǀAA>zC@-M`O dx|΀XX>zS@-M`V W'0x`|΀@@>zS@-zC@->M`W `d|πAA>zC@-M`X dx|%׀XX>zS@-M`e_ W'@x`|5׀@@@>zS@-zC@->M`_ `d|~؀AA@>zC@-M`` dx|߀XXMwS@-M`h W'Px`|߀@@@MwS@-M`Oh `d|@ဝAA@MwC@-M`i dx|耝XX@MwS@-M`p W'`x`|耝@@MwS@-M`q `d|ꀝAAMwC@-M`Zr dx|YXXMwS@-M`y W'p'x`|e@@@MwS@-M`y `d|AA@MwC@-M`z dx|XX@MwS@-M`O W'7x`|@@MwS@-M` `d|kAAMwC@-M` dx|XXMwS@-M`Ί W'Gx`|@@@MwS@-M` `d|AA@MwC@-M`9 dx|XX@MwS@-M`ˡ W'Wx`|@@MwS@-M` `d|AAMwC@-M`0 dx|F"XXMwS@-M` W'gx`|R"@@@MwS@-M`ª `d|#AA@MwC@-M` dx|+XX@MwS@-M`A W'wx`|+@@MwS@-M`z `d|k,AAMwC@-M` dx|3XXMwS@-M` W'x`|3@@@MwS@-M`0 `d|5AA@MwC@-M`_ dx|zM` `d|=AAMwC@-M` dx|gEXXMwS@-M` W'x`|tE@@@MwS@-M` `d|bAA@MwC@-M`_ dx|gXX@MwS@-M` W(x`|g@@MwS@-M`H `d|RiAAMwC@-M` dx|pXXMwS@-M` W(x`|p@@@MwS@-M` `d| rAA@MwC@-M`M dx|SyXX@MwS@-M` W( x`|`y@@MwS@-M` `d|zAAMwC@-M` dx|'XX@>zS@-M`g W(0x`|9@@>zS@-zC@->M` `d|uAA>zC@-M` dx|슁XX>zS@-M`, W(@x`|@@>zS@-zC@->M`[ `d|7AA>zC@-M`w dx|XX>zS@-M` W(Px`|@@>zS@-zC@->M` `d| AA>zC@-M`I dx|?XX@>zS@-M`$ W(`x`|O@@@>zS@-zC@->M`$ `d|AA@>zC@-M`% dx|ΤXX@>zS@-M`- W(p(x`|㤁@@@>zS@-zC@->M`=- `d|AA@>zC@-M`T. dx|XX>zS@-M`5 W(8x`|@@>zS@-zC@->M`5 `d|宁AA>zC@-M`%7 dx|zS@-M`|> W(Hx`|K@@>zS@- ``|k@@>zC@->M`> `d|AA>zC@-M`? dx|XX>zS@-M`DG W(Xx`|@@@>zS@-zC@->M`sG `d|tAA@>zC@-M`H dx|ǁXX@>zS@-M` P W(hx`|ǁ@@>zS@-zC@->M`P `d|DɁAA>zC@-M`Q dx|ЁXX>zS@-M`X W(xx`|Ё@@>zS@-zC@->M`Y `d|сAA>zC@-M`0Z dx|VفXX>zS@-M`a W(x`|fف@@>zS@-zC@->M`a `d|ځAA>zC@-M`b dx|⁝XX@>zS@-M`Vj W(x`|$⁝@@@>zS@-zC@->M`j `d|そAA@>zC@-M`k dx|ꁝXX@>zS@-M`s W(x`|ꁝ@@@>zS@-zC@->M`=s `d|2쁝AA@>zC@-M`rt dx|XX>zS@-M`{ W)x`|@@>zS@-zC@->M`{ `d|AA>zC@-M` } dx|:XX>zS@-M`z W)x`|L@@>zS@-zC@->M` `d|~AA>zC@-M` dx|sXXMwS@-M` W) x`|z@@@MwS@-M`+ `d|AA@MwC@-M` dx| XXMwS@-M`P W)0x`| @@@MwS@-M`s `d|AA@MwC@-M`Ŗ dx|XXMwS@-M`ǝ W)@x`|@@MwS@-M` `d|AAMwC@-M`$ dx|XX@MwS@-M`@ W)P x`| @@MwS@-M`o `d|)AAMwC@-M`i dx|w&XXMwS@-M` W)`x`|&@@@MwS@-M`߮ `d|'AA@MwC@-M` dx|.XX@MwS@-M`- W)p)x`|.@@MwS@-M`S `d|;0AAMwC@-M`{ dx|_7XX>zS@-M` W)9x`|m7@@@>zS@-zC@->M` `d|8AA@>zC@-M` dx|!@XXMwS@-M`a W)Ix`|'@@@@MwS@-M` `d||AAA@MwC@-M` dx|&IXX@MwS@-M`f W)Yx`|+I@@MwS@-M` `d|rJAAMwC@-M` dx|LRXXMwS@-M` W)ix`|SR@@@MwS@-M` `d|SAA@MwC@-M`6 dx|ZXX@MwS@-M` W)yx`|Z@@@MwC@->M`: ``|[@@MwS@-M`^ `d|eAA@MwC@-M` dx|!mXXMwS@-M`a W)x`|'m@@@MwS@-M` `d|nAA@MwC@-M` dx|cvXXMwS@-M` W)x`|iv@@MwS@-M` `d|wAAMwC@-M`$ dx|XXMwS@-M`V W*x`|@@MwS@-M`z `d|ȀAAMwC@-M` dx|ˇXXMwS@-M`  W*x`|ч@@@MwS@-M`/ `d|񈂝AA@MwC@-M`1 dx|㐂XXMwS@-M`# W* x`|鐂@@@MwS@-M`F `d|YAA@MwC@-M` dx|XXMwS@-M`! W*0x`|@@MwS@-M`5" `d|fAAMwC@-M`# dx|XX@MwS@-M`* W*@x`|@@MwS@-M`+ `d|>AAMwC@-M`~, dx|dXXMwS@-M`3 W*P x`|m@@@MwS@-M`3 `d|ҬAA@MwC@-M`5 dx|0XXMwS@-M`p< W*`x`|9@@@MwS@-M`< `d|AA@MwC@-M`= dx|XXMwS@-M`YE W*p*x`|!@@MwS@-M`E `d|AAMwC@-M`F dx|ƂXXMwS@-M`BN W*:x`| Ƃ@@MwS@-M`iN `d|\ǂAAMwC@-M`O dx|΂XXMwS@-M`V W*Jx`|΂@@@MwS@-M`V `d|ЂAA@MwC@-M`FX dx|nׂXXMwS@-M`_ W*Zx`|wׂ@@@MwS@-M`_ `d|؂AA@MwC@-M`` dx|)XXMwS@-M`ih W*jx`|4@@MwS@-M`h `d|ႝAAMwC@-M`i dx|肝XX@MwS@-M`q W*zx`|肝@@MwS@-M`Iq `d|4ꂝAAMwC@-M`tr dx|XXMwS@-M`y W*x`|@@@MwS@-M`z `d| AA@MwC@-M`K{ dx|]XXMwS@-M` W*x`|k@@@MwS@-M`Ԃ `d|AA@MwC@-M` dx|XXMwS@-M`\ W*x`|+@@MwS@-M` `d|AAMwC@-M` dx| XXMwS@-M` W+x`| @@MwS@-M` `d| AAMwC@-M`P dx|-XXMwS@-M`m W+x`|9@@@MwS@-M` `d|AA@MwC@-M`Н dx|XXMwS@-M`) W+ x`|@@@MwS@-M`_ `d|KAA@MwC@-M` dx|%XXMwS@-M` W+0x`|%@@MwS@-M` `d|&AAMwC@-M`- dx|`.XX@MwS@-M` W+@x`|m.@@MwS@-M`ֶ `d|/AAMwC@-M` dx|7XXMwS@-M`O W+P x`|7@@@MwS@-M` `d|o8AA@MwC@-M` dx|?XXMwS@-M` W+`x`|?@@@MwS@-M`< `d|AAA@MwC@-M`Q dx|HXXMwS@-M` W+p+x`|H@@MwS@-M` `d|IAAMwC@-M` dx|IQXXMwS@-M` W+;x`|VQ@@MwS@-M` `d|RAAMwC@-M` dx|YXXMwS@-M` W+Kx`|Y@@@MwS@-M`< `d|[AA@MwC@-M`T dx|wbXXMwS@-M` W+[x`|b@@@MwS@-M` `d|cAA@MwC@-M` dx|/kXXMwS@-M`o W+kx`|;k@@MwS@-M` `d|lAAMwC@-M` dx|sXX@MwS@-M` W+{x`|s@@MwS@-M`. `d|uAAMwC@-M`N dx|p|XXMwS@-M` W+x`|||@@@MwS@-M` `d|}AA@MwC@-M` dx|XXMwS@-M`T W+x`|!@@@MwS@-M` `d|JAA@MwC@-M` dx|ύXXMwS@-M` W+x`|ۍ@@MwS@-M`D `d|AAMwC@-M`Q dx|XXMwS@-M` W,x`|@@MwS@-M` `d|ϗAAMwC@-M` dx|LXXMwS@-M`' W,x`|X@@@MwS@-M`' `d|eAA@MwC@-M`( dx|ӧXXMwS@-M`0 W, x`|ߧ@@@MwS@-M`a0 `d|LAA@MwC@-M`1 dx|XXMwS@-M`8 W,0x`|@@MwS@-M`9 `d|AAMwC@-M`5: dx|XX@MwS@-M`ZA W,@x`|'@@MwS@-M`A `d|nAAMwC@-M`B dx|XXMwS@-M`J W,P x`|@@@MwS@-M`dJ `d|1ÃAA@MwC@-M`qK dx|ʃXXMwS@-M`R W,`x`|ʃ@@@MwS@-M` S `d|̃AA@MwC@-M`UT dx|ӃXXMwS@-M`\[ W,p,x`|*Ӄ@@MwS@-M`[ `d|ԃAAMwC@-M`\ dx|ۃXXMwS@-M`d W,<x`|ۃ@@MwS@-M`dd `d|9݃AAMwC@-M`ye dx|y䃝XXMwS@-M`l W,Lx`|䃝@@@MwS@-M`m `d|僝AA@MwC@-M`&n dx|G택XXMwS@-M`u W,\x`|`택@@@MwS@-M`dv `d|"AA@MwC@-M`bw dx|AXXMwS@-M`} W,lx`|Q@@MwS@-M`} `d|UAAMwC@-M`~ dx|YXX@MwS@-M` W,|x`|i@@MwS@-M`dž `d|AAMwC@-M`Ç dx|XXMwS@-M`_ W,x`|0@@@MwS@-M` `d||AA@MwC@-M` dx|XXMwS@-M` W,x`|@@@MwS@-M`` `d|7AA@MwC@-M`w dx|XXMwS@-M`ݠ W,x`|@@MwS@-M`, `d|AAMwC@-M`@ dx|.!XXMwS@-M`n W-x`|M` `d|"AAMwC@-M`ݪ dx|)XXMwS@-M` W-x`|)@@@MwS@-M`L `d|#+AA@MwC@-M`c dx|2XXMwS@-M`Ժ W- x`|2@@@MwS@-M`' `d|3AA@MwC@-M`4 dx|Q;XXMwS@-M` W-0x`|^;@@MwS@-M` `d|M` `d|EAAMwC@-M` dx|LXXMwS@-M` W-P x`|L@@@MwS@-M`? `d| NAA@MwC@-M`I dx|iUXXMwS@-M` W-`x`|vU@@@MwS@-M` `d|VAA@MwC@-M` dx|'^XXMwS@-M`g W-p-x`|7^@@MwS@-M` `d|_AAMwC@-M` dx|fXXMwS@-M`, W-=x`|f@@MwS@-M`{ `d|PhAAMwC@-M` dx|oXXMwS@-M` W-Mx`|o@@@MwS@-M`. `d|pAA@MwC@-M`- dx|exXXMwS@-M` W-]x`|qx@@@MwS@-M` `d|yAA@MwC@-M` dx|$XXMwS@-M`d W-mx`|1@@MwS@-M` `d|AAMwC@-M` dx|ʉXX@MwS@-M`  W-}x`|ۉ@@MwS@-M`Y `d|6AAMwC@-M`v d`|@@MwS@-M`g# `d|6AAMwC@-M`v$ dx|zXXMwS@-M`+ W-x`|@@@MwS@-M`, `d|AA@MwC@-M`, dx|XXMwS@-M`N4 W-x`|@@MwS@-M`4 `d|[AAMwC@-M`5 dx|´XXMwS@-M`= W.x`|д@@MwS@-M`0= `d|=AAMwC@-M`}> dx|XXMwS@-M`]E W.x`|*@@@MwS@-M`E `d|AA@MwC@-M`F dx|ńXXMwS@-M`N W. x`|ń@@MwS@-M`VN `d|!DŽAAMwC@-M`aO dx|v΄XX@MwS@-M`V W.0x`|΄@@MwS@-M`W `d|τAAMwC@-M`X dx|;ׄXX@MwS@-M`{_ W.@x`|Iׄ@@MwS@-M`_ `d|؄AAMwC@-M`` dx|߄XX@MwS@-M`h W.Px`|߄@@MwS@-M`Qh `d|7ᄝAAMwC@-M`wi dx|脝XXMwS@-M`p W.`x`|脝@@@MwS@-M`q `d|鄝AA@MwC@-M`r dx|XXMwS@-M`Æ W.p.x`|@@MwS@-M` `d|AAMwC@-M`% dx|@XXMwS@-M` W.>x`|O@@MwS@-M` `d|AAMwC@-M` dx|XXMwS@-M` W.Nx`|@@@MwS@-M`D `d|&AA@MwC@-M`f dx|XXMwS@-M`Π W.^x`|@@MwS@-M` `d|AAMwC@-M` dx|+!XX@MwS@-M`k W.nx`|7!@@MwS@-M` `d|^"AAMwC@-M` dx|)XX@MwS@-M`2 W.~x`|*@@MwS@-M`` `d|e+AAMwC@-M` dx|2XX@MwS@-M` W.x`|2@@MwS@-M` `d|3AAMwC@-M` dx|e;XXMwS@-M` W.x`|w;@@@MwS@-M` `d|M` `d|pEAAMwC@-M` dx|MXXMwS@-M`@ W/x`|M@@MwS@-M`q `d|NAAMwC@-M` dx|UXXMwS@-M` W/x`|U@@@MwS@-M` `d|WAA@MwC@-M`] dx|c^XXMwS@-M` W/ x`|q^@@MwS@-M` `d|_AAMwC@-M` dx|1gXX@MwS@-M`q W/0x`|?g@@MwS@-M` `d|ohAAMwC@-M` dx|oXX@MwS@-M`? W/@x`|p@@MwS@-M`m `d|$qAAMwC@-M`d dx|xXX@MwS@-M`W/Px`|x@@MwS@-M``d|yAAMwC@-M`6dx|NXXMwS@-M` W/`x`|\@@@MwS@-M` `d|AA@MwC@-M` dx|XXMwS@-M`PW/p/x`| @@MwS@-M``d|bAAMwC@-M`dx|֒XXMwS@-M`W/?x`|㒅@@MwS@-M`M`d|AAMwC@-M`Adx|XXMwS@-M`#W/Ox`|@@@MwS@-M`$`d|AA@MwC@-M`Y%dx|gXXMwS@-M`,W/_x`|t@@MwS@-M`,`d|AAMwC@-M`-dx|'XX@MwS@-M`g5W/ox`|6@@MwS@-M`5`d|vAAMwC@-M`6dx|굅XX@MwS@-M`*>W/x`|@@MwS@-``|"@@@MwC@->M`b>`d|HAAMwC@-M`?dx|ʾXX@MwS@-M` GW/x`|پ@@MwS@-M`7G`d|=AAMwC@-M`}Hdx|eDžXXMwS@-M`OW/x`|sDž@@@MwS@-M`O`d|ȅAA@MwC@-M`Qdx|'ЅXXMwS@-M`gXW/x`|4Ѕ@@MwS@-M`X`d|셝AAMwC@-M`udx|XXMwS@-M`(W0x`|@@MwS@-M`a`d|LAAMwC@-M`dx| XXMwS@-M`W0x`| @@@MwS@-M``d| AA@MwC@-M`Ldx|~XXMwS@-M`W0 x`|@@MwS@-M``d|AAMwC@-M`dx|2XX@MwS@-M`rW00x`|>@@MwS@-M``d|iAAMwC@-M`dx|%XX@MwS@-M`W0@x`|%@@MwS@-M`$`d|&AAMwC@-M`dx|.XX@MwS@-M`W0Px`|.@@MwS@-M`1`d|/AAMwC@-M`,dx|C7XXMwS@-M`W0` x`|Q7@@@MwS@-M``d|8AA@MwC@-M`dx|?XXMwS@-M`W0p0x`|?@@MwS@-M`f`d|XAAAMwC@-M`dx|dHXXMwS@-M`W0@x`|sH@@MwS@-M``d|IAAMwC@-M`dx|"QXXMwS@-M`bW0Px`|JQ@@@MwS@-M``d|RAA@MwC@-M`dx|YXXMwS@-M`W0`x`|Y@@MwS@-M`E`d|:[AAMwC@-M`zdx|bXX@MwS@-M`W0px`|b@@MwS@-M`-`d|dAAMwC@-M`Bdx|*kXX@MwS@-M`jW0x`|8k@@MwS@-M``d|lAAMwC@-M`dx|sXX@MwS@-M`0W0x`|s@@MwS@-``|?t@@@MwC@->M``d|6uAAMwC@-M`vdx||XXMwS@-M`W0x`||@@@MwS@-M`6`d|}AA@MwC@-M`dx|pXXMwS@-M` W0x`|@@MwS@-M` `d|AAMwC@-M`dx|/XXMwS@-M`oW1x`|=@@MwS@-M`*`d|ُAAMwC@-M`dx|XXMwS@-M`W1x`|ʖ@@@MwS@-M`D`d|󗆝AA@MwC@-M`3 dx|~XXMwS@-M`'W1 x`|@@MwS@-M`(`d|ȠAAMwC@-M`)dx|XX@MwS@-M`0W10x`|@@MwS@-M`%1`d|1AAMwC@-M`q2dx|XX@MwS@-M`X9W1@x`|'@@MwS@-M`9`d|qAAMwC@-M`:dx|XX@MwS@-M`AW1Px`|@@MwS@-M` B`d|ӺAAMwC@-M`dx|C†XXMwS@-M`CW1`!x`|Q†@@@MwS@-M`q`d|XÆAA@MwC@-M`X dx|ʆXXMwS@-M`W1p1x`|ˆ@@MwS@-M`'`d|̆AAMwC@-M`dx|ӆXXMwS@-M`W1Ax`|ӆ@@MwS@-M``d|ԆAAMwC@-M`dx|܆XXMwS@-M`"W1Qx`|܆@@@MwS@-M`"`d|݆AA@MwC@-M`#dx|OXXMwS@-M`O8W1ax`|]@@MwS@-M`}8`d|AAMwC@-M`9dx| XX@MwS@-M` AW1qx`|@@MwS@-M`8A`d|@AAMwC@-M`@Bdx|XX@MwS@-M`IW1x`|@@MwS@-M`I`d|AAMwC@-M`Jdx|O XX@MwS@-M`ORW1x`|\ @@MwS@-M`R`d| AAMwC@-M`Sdx|XXMwS@-M`ZW1x`|@@@MwS@-M`-[`d|=AA@MwC@-M`=\dx|XXMwS@-M`cW1x`|@@MwS@-M`c`d|AAMwC@-M`ddx|+&XXMwS@-M`+lW2x`|8&@@MwS@-M`wl`d|'AAMwC@-M`mdx|.XXMwS@-M`tW2x`|.@@@MwS@-M`t`d|0AA@MwC@-M`vdx|c7XXMwS@-M`c}W2 x`|r7@@MwS@-M`}`d|8AAMwC@-M`~dx|!@XX@MwS@-M`!W20x`|1@@@MwS@-M`q`d|AAAMwC@-M`dx|HXX@MwS@-M`W2@x`|H@@MwS@-M`/`d|^JAAMwC@-M`^dx|LQXX@MwS@-M`LW2Px`|ZQ@@MwS@-M``d|RAAMwC@-M`Ędx|-ZXXMwS@-M`-W2`"x`|;Z@@@MwS@-M`{`d|u[AA@MwC@-M`udx|bXXMwS@-M`ǨW2p2x`|b@@MwS@-M``d|1dAAMwC@-M`1dx|kXXMwS@-M`W2Bx`|k@@MwS@-M`ױ`d|lAAMwC@-M`Բdx|WtXXMwS@-M`WW2Rx`|jt@@@MwS@-M``d|uAA@MwC@-M`dx| }XXMwS@-M` W2bx`|}@@MwS@-M`]`d|j~AAMwC@-M`jdx|XX@MwS@-M`W2rx`|@@MwS@-M``d|AAMwC@-M`dx|`XX@MwS@-M``W2x`|m@@MwS@-M``d|AAMwC@-M`dx|XX@MwS@-M`W2x`|@@MwS@-M`^`d|AAMwC@-M`dx|XXMwS@-M`W2x`|@@@MwS@-M``d|AA@MwC@-M`dx|YXXMwS@-M`YW2x`|e@@MwS@-M``d|AAMwC@-M`dx|XXMwS@-M`W3x`|'@@MwS@-M`h`d|dAAMwC@-M`ddx|칇XXMwS@-M`W3x`|@@@MwS@-M``d|[AA@MwC@-M`[dx|‡XXMwS@-M`W3 x`|‡@@MwS@-M``d| ćAAMwC@-M` dx|ˇXX@MwS@-M`W30x`|#ˇ@@MwS@-M`a`d|ȧAAMwC@-M`adx|ӇXX@MwS@-M`W3@x`|Ӈ@@MwS@-M``d|)ՇAAMwC@-M`)dx|܇XX@MwS@-M`"W3Px`|܇@@MwS@-M`"`d|އAAMwC@-M`$dx|V凝XXMwS@-M`V+W3`#x`|f凝@@@MwS@-M`+`d|i懝AA@MwC@-M`i,dx|퇝XXMwS@-M`3W3p3x`|퇝@@MwS@-M`4`d|:AAMwC@-M`:5dx|XXMwS@-M`<W3Cx`|@@MwS@-M`<`d|AAMwC@-M`=dx|(XXMwS@-M`(EW3Sx`|4@@@MwS@-M`]E`d|AA@MwC@-M`Fdx|XXMwS@-M`MW3cx`|@@MwS@-M`)N`d|5 AAMwC@-M`5Odx|XX@MwS@-M`VW3sx`|@@MwS@-M`V`d|AAMwC@-M`Xdx|dXX@MwS@-M`d_W3x`|p@@MwS@-M`_`d|AAMwC@-M``dx|!XX@MwS@-M`gW3x`|"@@MwS@-M`.h`d|Y#AAMwC@-M`Yidx|*XXMwS@-M`pW3x`|*@@@MwS@-M`p`d|,AA@MwC@-M`rdx|l3XXMwS@-M`lyW3x`|y3@@MwS@-M`y`d|4AAMwC@-M`zdx|;XXMwS@-M`W4x`| <@@MwS@-M`/`d|#=AAMwC@-M`#dx|DXXMwS@-M`W4x`|D@@@MwS@-M`NJ`d|EAA@MwC@-M`dx|#MXXMwS@-M`#W4 x`|2M@@MwS@-M`m`d|WNAAMwC@-M`Wdx|UXX@MwS@-M`W40x`|U@@MwS@-M`7`d|AWAAMwC@-M`Adx|^XX@MwS@-M`W4@x`|^@@MwS@-M``d|_AAMwC@-M`dx|OgXX@MwS@-M`OW4Px`|^g@@MwS@-M``d|hAAMwC@-M`dx| pXXMwS@-M` W4`$x`|p@@@MwS@-M`H`d|&qAA@MwC@-M`&dx|xXXMwS@-M`ܾW4p4x`|x@@MwS@-M`(`d|zAAMwC@-M`dx|XXMwS@-M`W4Dx`|@@MwS@-M``d|AAMwC@-M`dx|0XXMwS@-M`0W4Tx`|>@@@MwS@-``|@@MwC@->M``d|AA@MwC@-M`dx|XXMwS@-M`W4dx`|@@MwS@-M`L`d|gAAMwC@-M`gdx|XX>zS@-M`W4tx`|@@@>zS@-zC@->M``d|AA@>zC@-M`dx|pXX@MwS@-M`pW4x`|@@MwS@-M``d|AAMwC@-M`dx|XX@MwS@-M`W4x`|.@@MwS@-M`p`d|hAAMwC@-M`hdx|XX@MwS@-M`W4x`|@@MwS@-M``d|AAMwC@-M`dx|oXXMwS@-M`oW4x`|~@@@MwS@-M``d|ӿAA@MwC@-M`dx|8LjXXMwS@-M`8 W5x`|GLj@@MwS@-M` `d|ȈAAMwC@-M`dx|ЈXXMwS@-M`W5x`|Ј@@MwS@-M`S`d|VшAAMwC@-M`Vdx|初XXMwS@-M`+W5 x`|初@@@MwS@-M`,`d|爝AA@MwC@-M`-dx|XXMwS@-M`4W50x`|@@MwS@-M`4`d|AAMwC@-M`5dx|FXX@MwS@-M`F=W5@x`|S@@MwS@-M`=`d|AAMwC@-M`>dx| XX@MwS@-M` FW5Px`|@@MwS@-M`F`d|AAMwC@-M`Gdx|XX@MwS@-M`NW5`%x`|@@MwS@-M`O`d|- AAMwC@-M`-Pdx|XXMwS@-M`WW5p5x`|@@@MwS@-M`W`d|AA@MwC@-M`Xd`|.@@MwS@-M`d`d|AAMwC@-M`edx|&XXMwS@-M`lW5Ux`|&@@MwS@-M`2m`d|B(AAMwC@-M`Bndx|/XXMwS@-M`uW5ex`|/@@@MwS@-M`u`d|0AA@MwC@-M`vdx|g8XXMwS@-M`g~W5ux`|t8@@MwS@-M`~`d|9AAMwC@-M`dx|@XX@MwS@-M`W5x`|@@@MwS@-M``d|BBAAMwC@-M`Bdx|}IXX@MwS@-M`}W5x`|I@@MwS@-M``d|JAAMwC@-M`dx|&RXX@MwS@-M`&W5x`|4R@@MwS@-M`z`d|pSAAMwC@-M`pdx|ZXXMwS@-M`נW5x`|Z@@@MwS@-M`'`d|4\AA@MwC@-M`4dx|_cXXMwS@-M`_W6x`|lc@@MwS@-M``d|dAAMwC@-M`dx|kXXMwS@-M`ӱW6x`|k@@MwS@-M`&`d|;mAAMwC@-M`;dx|ZtXXMwS@-M`ZW6 x`|lt@@@MwS@-M``d|uAA@MwC@-M`dx| }XXMwS@-M` W60x`|}@@MwS@-M`c`d|i~AAMwC@-M`idx|XX@MwS@-M`W6@x`|΅@@MwS@-M``d|%AAMwC@-M`%dx|GXX@MwS@-M`GW6Px`|T@@MwS@-M``d|AAMwC@-M`dx|XX@MwS@-M`W6`&x`|@@MwS@-M`=`d|vAAMwC@-M`vdx|XXMwS@-M`W6p6x`|@@@MwS@-M``d|AA@MwC@-M`dx|)XXMwS@-M`)W6Fx`|9@@MwS@-M`[`d|^AAMwC@-M`^dx|ݰXXMwS@-M`W6Vx`|찉@@MwS@-M``d|uAAMwC@-M`udx|XXMwS@-M`W6fx`|@@@MwS@-M``d|AA@MwC@-M`dx|‰XXMwS@-M`W6vx`|0‰@@MwS@-M`P`d|YÉAAMwC@-M`Y dx|ʉXX@MwS@-M`W6x`|ʉ@@MwS@-M``d|ˉAAMwC@-M`dx|XX@MwS@-M`&W6x`|@@MwS@-M`&`d|≝AAMwC@-M`(dx|1鉝XXDwS@-M`1/W6x`|F鉝@@vDwS@-M`h/`d|ꉝAAvDwC@-M`0dx|XXqDwS@-M`7W6x`|@@@~DwS@-M` 8`d|AA@~DwC@-M`9dx|[XX@xDwS@-M`[@W7x`|j@@tDwS@-M`@`d|AAtDwC@-M`Adx|XXtDwS@-M`IW7x`|&@@@xDwS@-M`II`d|/AA@xDwC@-M`/Jdx| XX@~DwS@-M`QW7 x`| @@qDwS@-M`JR`d| AAqDwC@-M`Sdx|XXvDwS@-M`ZW70x`|@@DwS@-M`Z`d|AADwC@-M`[dx|:XX@{DwS@-M`:cW7@x`|J@@yDwS@-M`c`d|AAyDwC@-M`ddx|%XXvDwS@-M`kW7Px`|&@@yDwS@-M`Gl`d|)'AAyDwC@-M`)mdx|.XXyDwS@-M`tW7`'x`| /@@wDwS@-M`+u`d|w0AAwDwC@-M`wvdx|7XXvDwS@-M`}W7p7x`|7@@}DwS@-M` ~`d|"9AA}DwC@-M`"dx|@XXyDwS@-M`W7Gx`|@@@@{DwS@-M``d| BAA@{DwC@-M` dx|_IXXDwS@-M`_W7Wx`|lI@@vDwS@-M``d|JAAvDwC@-M`͐dx|#RXXqDwS@-M`#W7gx`|0R@@@~DwS@-M`s`d|SAA@~DwC@-M`dx|ZXX@xDwS@-M`W7wx`|Z@@tDwS@-M``d|[AAtDwC@-M`dx|@cXXtDwS@-M`@W7x`|Mc@@@xDwS@-M``d|dAA@xDwC@-M`dx| lXX@~DwS@-M` W7x`|l@@qDwS@-M`^`d|TmAAqDwC@-M`Tdx|tXXvDwS@-M`ʺW7x`|t@@DwS@-M``d|-vAADwC@-M`-dx|}XX@{DwS@-M`W7x`|}@@yDwS@-M``d|7AAyDwC@-M`7dx|XX}DwS@-M`W8x`|@@vDwS@-M``d|AAvDwC@-M`dx|XXwDwS@-M`W8x`|-@@yDwS@-M`o`d|AAyDwC@-M`dx|XXyDwS@-M`W8 x`|Ű@@wDwS@-M``d|9AAwDwC@-M`9dx|qXXvDwS@-M`qW80x`|@@}DwS@-M``d|AA}DwC@-M`dx|LŠXXyDwS@-M`LW8@x`|dŠ@@@{DwS@-M``d|ÊAA@{DwC@-M` dx|]يXXDwS@-M`]W8Px`|nي@@vDwS@-M``d|ڊAAvDwC@-M` dx|(⊝XXqDwS@-M`((W8`(x`|7⊝@@@~DwS@-M`z(`d|㊝AA@~DwC@-M`)dx|ꊝXX@xDwS@-M`0W8p8x`|ꊝ@@tDwS@-M`1`d|;슝AAtDwC@-M`;2dx|XXtDwS@-M`9W8Hx`|@@@xDwS@-M`9`d|AA@xDwC@-M`;dx|,XX@~DwS@-M`,BW8Xx`|=@@qDwS@-M`^B`d|NAAqDwC@-M`NCdx|XXvDwS@-M`JW8hx`|@@DwS@-M`BK`d|]AADwC@-M`]Ldx| XX@{DwS@-M`SW8xx`| @@yDwS@-M`T`d|1AAyDwC@-M`1Udx|pXX}DwS@-M`p\W8x`|@@vDwS@-M`\`d|AAvDwC@-M`]dx|3XXwDwS@-M`3eW8x`|B@@yDwS@-M`e`d|| AAyDwC@-M`|fdx|'XXyDwS@-M`mW8x`|'@@wDwS@-M`n`d|+)AAwDwC@-M`+odx|h0XXvDwS@-M`hvW8x`|x0@@}DwS@-M`v`d|1AA}DwC@-M`wdx|9XXyDwS@-M`W9x`|9@@@{DwS@-M`S`d|~:AA@{DwC@-M`~dx|AXXDwS@-M`W9x`|A@@vDwS@-M`8`d|CAAvDwC@-M`dx|JXXqDwS@-M`W9 x`|J@@@~DwS@-M`P`d|VLAA@~DwC@-M`Vdx|NSXX@xDwS@-M`NW90x`|]S@@tDwS@-M``d|TAAtDwC@-M`dx|O\XXtDwS@-M`OW9@ x`|a\@@@xDwS@-M``d|]AA@xDwC@-M`dx| eXX@~DwS@-M` W9Px`|e@@qDwS@-M`Z`d|tfAAqDwC@-M`tdx|mXX@MwS@-M`ճW9`)x`|m@@MwS@-M`(`d|(oAAMwC@-M`(dx|vXXMwS@-M`W9p9x`|v@@@MwS@-M``d|wAA@MwC@-M`dx|XXXMwS@-M`XW9Ix`|k@@MwS@-M``d|AAMwC@-M`dx|燋XXMwS@-M`W9Yx`|@@MwS@-M`P`d|^AAMwC@-M`^dx|XXMwS@-M`W9ix`|@@@MwS@-M``d|AA@MwC@-M`dx|FXXMwS@-M`FW9yx`|X@@MwS@-M``d|AAMwC@-M`dx|塋XX@MwS@-M`W9x`|@@MwS@-M``d|QAAMwC@-M`Qdx|XX@MwS@-M`W9x`|@@MwS@-M``d|AAMwC@-M`dx|JXX@MwS@-M`JW9x`|^@@MwS@-M``d|AAMwC@-M`dx|ۻXXMwS@-M`W9x`|컋@@@MwS@-M`5`d|HAA@MwC@-M`Hdx|ċXXMwS@-M` W:x`|ċ@@MwS@-M` `d|ŋAAMwC@-M` dx|b͋XXMwS@-M`bW:x`|r͋@@MwS@-M``d|΋AAMwC@-M`dx|ՋXXMwS@-M`W: x`|֋@@@MwS@-M`{`d|v׋AA@MwC@-M`vdx|ދXXMwS@-M`$W:0x`|ދ@@MwS@-M` %`d|ߋAAMwC@-M`%dx|q狝XX@MwS@-M`q-W:@ x`|狝@@MwS@-M`-`d|苝AAMwC@-M`.dx|)XX@MwS@-M`)6W:Px`|9@@MwS@-M`v6`d|AAMwC@-M`7dx|XX@MwS@-M`?W:`*x`|@@MwS@-M`V?`d|FAAMwC@-M`F@dx|XXMwS@-M`GW:p:x`|@@@MwS@-M`G`d|AA@MwC@-M`Hdx| XXMwS@-M`PW:Jx`| @@MwS@-M`/Q`d|6 AAMwC@-M`6Rdx|PXXMwS@-M`PYW:Zx`|^@@MwS@-M`Y`d|AAMwC@-M`Zdx| XXMwS@-M` bW:jx`|@@@MwS@-M`db`d|eAA@MwC@-M`ecdx|$XXMwS@-M`jW:zx`|$@@MwS@-M` k`d|&AAMwC@-M`ldx|x-XX@MwS@-M`xsW:x`|-@@MwS@-M`s`d|.AAMwC@-M`tdx|W6XX@MwS@-M`W|W:x`|e6@@MwS@-M`|`d|f7AAMwC@-M`f}dx|>XX@MwS@-M`W:x`|>@@MwS@-M``d|@AAMwC@-M`dx|dGXXMwS@-M`dW:x`|qG@@@MwS@-M``d|HAA@MwC@-M`Ȏdx|$PXXMwS@-M`$W;x`|2P@@MwS@-M`y`d|zQAAMwC@-M`zdx|XXXMwS@-M`ڞW;x`|X@@MwS@-M`$`d| ZAAMwC@-M` dx|aXXMwS@-M`W; x`|a@@@MwS@-M`ק`d|bAA@MwC@-M`dx|IjXXMwS@-M`IW;0x`|Uj@@MwS@-M``d|kAAMwC@-M`dx|rXX@MwS@-M`W;@ x`|r@@MwS@-M`F`d|4tAAMwC@-M`4dx|{XX@MwS@-M`W;Px`|{@@MwS@-M``d||AAMwC@-M`dx|ZXX@MwS@-M`ZW;`+x`|g@@MwS@-M``d|iAAMwC@-M`idx|XXMwS@-M`W;p;x`|@@@MwS@-M`6`d|pAA@MwC@-M`pdx|XXMwS@-M`W;Kx`|ĕ@@MwS@-M``d|AAMwC@-M`dx|nXXMwS@-M`nW;[x`|z@@MwS@-M``d|AAMwC@-M`dx|%XXMwS@-M`%W;kx`|3@@@MwS@-M`y`d|oAA@MwC@-M`odx|ݯXXMwS@-M`W;{x`|ꯌ@@MwS@-M`0`d|8AAMwC@-M`8dx|XX@MwS@-M`W;x`|@@MwS@-M` `d|󹌝AAMwC@-M`dx| XX@MwS@-M` W;x`|@@MwS@-M`X`d|UŒAAMwC@-M`Udx|ɌXX@MwS@-M`W;x`|Ɍ@@MwS@-M``d|ʌAAMwC@-M`dx|ҌXXMwS@-M`W;x`|#Ҍ@@@MwS@-M`g`d|gӌAA@MwC@-M`gdx|ڌXXMwS@-M` W<x`|ی@@MwS@-M`Y!`d|d܌AAMwC@-M`d"dx|㌝XXMwS@-M`)W<x`|㌝@@MwS@-M`)`d|䌝AAMwC@-M`*dx|g쌝XXMwS@-M`g2W< x`|w쌝@@@MwS@-M`2`d|팝AA@MwC@-M`3dx|XXMwS@-M`;W<0x`|,@@MwS@-M`K;`d|AAMwC@-M`<dx|XX@MwS@-M`CW<@ x`|@@MwS@-M`D`d|DAAMwC@-M`DEdx|XX@MwS@-M`LWM`L`d|AAMwC@-M`Mdx|FXX@MwS@-M`FUW<`,x`|[@@MwS@-M`U`d|AAMwC@-M`Vdx| XXMwS@-M` ^WM`h^`d|6AA@MwC@-M`6_dx| XXMwS@-M`fW<Lx`| @@MwS@-M`'g`d|"AAMwC@-M`hdx|X)XXMwS@-M`XoW<\x`|h)@@MwS@-M`o`d|*AAMwC@-M`pdx|2XXMwS@-M`xW<lx`|(2@@@MwS@-M`x`d|3AA@MwC@-M`ydx|:XXMwS@-M`ՀW<|x`|:@@MwS@-M`b`d|IM``d|DAAMwC@-M`dx|^LXX@MwS@-M`^W<x`|nL@@MwS@-M``d|MAAMwC@-M`ݓdx|UXX@MwS@-M`W<x`|.U@@MwS@-M`w`d|VAAMwC@-M`dx|]XXMwS@-M`ݣW<x`|]@@@MwS@-M`4`d|)_AA@MwC@-M`)dx|fXXMwS@-M`W=x`|f@@MwS@-M``d|gAAMwC@-M`dx|YoXXMwS@-M`YW=x`|io@@MwS@-M``d|pAAMwC@-M`dx| xXXMwS@-M` W= x`|x@@@MwS@-M`b`d|VyAA@MwC@-M`Vdx|̀XXMwS@-M`W=0x`|܀@@MwS@-M`$`d|AAMwC@-M`dx|XX@MwS@-M`W=@ x`|@@MwS@-M``d|䊍AAMwC@-M`dx|IXX@MwS@-M`IW=Px`|Y@@MwS@-M``d|AAMwC@-M`dx|XX@MwS@-M`W=`-x`|њ@@MwS@-M``d|.AAMwC@-M`.dx|XXMwS@-M`W=p=x`|@@@MwS@-M``d|̤AA@MwC@-M`dx|>XXMwS@-M`>W=Mx`|L@@MwS@-M``d|AAMwC@-M`dx|XXMwS@-M`W=]x`|@@MwS@-M`R`d|TAAMwC@-M`Tdx|XXMwS@-M`W=mx`|Ž@@@MwS@-M``d|#AA@MwC@-M`#dx||ƍXXMwS@-M`| W=}x`|ƍ@@MwS@-M` `d|ǍAAMwC@-M` dx|7ύXX@MwS@-M`7W=x`|Eύ@@MwS@-M``d|ЍAAMwC@-M`dx|׍XX@MwS@-M`W=x`|؍@@MwS@-M`M`d|ZٍAAMwC@-M`Zdx|XX@MwS@-M`&W=x`|@@MwS@-M`&`d|⍝AAMwC@-M`(dx|C鍝XXMwS@-M`C/W=x`|R鍝@@@MwS@-M`/`d|ꍝAA@MwC@-M`0dx|XXMwS@-M`8W>x`|@@MwS@-M`W8`d|KAAMwC@-M`K9dx|XXMwS@-M`@W>x`|@@MwS@-M`A`d|AAMwC@-M`Adx|XXMwS@-M`IW> x`|@@@MwS@-M`I`d|AA@MwC@-M`Jdx|8 XXMwS@-M`8RW>0x`|F @@MwS@-M`R`d| AAMwC@-M`Sdx|XX@MwS@-M`ZW>@x`|@@MwS@-M`[`d|AAMwC@-M`[dx|sXX@MwS@-M`scW>Px`|@@MwS@-M`c`d|AAMwC@-M`ddx|+&XX@MwS@-M`+lW>`.x`|?&@@MwS@-M`l`d|u'AAMwC@-M`umdx|.XXMwS@-M`tW>p>x`|.@@@MwS@-M`6u`d|O0AA@MwC@-M`Ovdx|G7XXMwS@-M`G}W>Nx`|[7@@MwS@-M`}`d|8AAMwC@-M`~dx|?XXMwS@-M`W>^x`|@@@MwS@-M`L`d|VAAAMwC@-M`Vdx|HXXMwS@-M`W>nx`|H@@@MwS@-M``d|JAA@MwC@-M`dx|sQXXMwS@-M`sW>~x`|Q@@MwS@-M``d|SAAMwC@-M`dx|#ZXX@MwS@-M`#W>x`|2Z@@MwS@-M``d|[AAMwC@-M`dx|bXX@MwS@-M`ĨW>x`|b@@MwS@-M``d|dAAMwC@-M`dx|qkXX@MwS@-M`qW>x`|k@@MwS@-M``d|lAAMwC@-M`dx|,tXXMwS@-M`,W>x`|8t@@@MwS@-M`w`d|uAA@MwC@-M`dx||XXMwS@-M`W?x`||@@MwS@-M`7`d|t~AAMwC@-M`tdx|vXXMwS@-M`vW?x`|@@MwS@-M``d|AAMwC@-M`dx|0XXMwS@-M`0W? x`|=@@@MwS@-M`{`d|AA@MwC@-M`dx|XXMwS@-M`W?0x`|@@MwS@-M`A`d|RAAMwC@-M`Rdx|XX@MwS@-M`W?@x`|ğ@@MwS@-M``d|9AAMwC@-M`9dx|uXX@MwS@-M`uW?Px`|@@MwS@-M``d|ЩAAMwC@-M`dx|4XX@MwS@-M`4W?`/x`|B@@MwS@-M``d|AAMwC@-M`dx|XXMwS@-M`W?p?x`|@@@MwS@-M`M`d|HAA@MwC@-M`Hdx|ŽXXMwS@-M`W?Ox`|Ž@@MwS@-M` `d|[ĎAAMwC@-M`[ dx|yˎXXMwS@-M`yW?_x`|ˎ@@MwS@-M``d|̎AAMwC@-M`dx|+ԎXXMwS@-M`+W?ox`|8Ԏ@@@MwS@-M``d|rՎAA@MwC@-M`rdx|܎XXMwS@-M`"W?x`|܎@@MwS@-M`8#`d|;ގAAMwC@-M`;$dx|>厝XX@MwS@-M`>+W?x`|S厝@@MwS@-M`+`d|掝AAMwC@-M`,dx|XX@MwS@-M`4W?x`|-@@MwS@-M`t4`d|AAMwC@-M`5dx|XX>zS@-M`<W?x`|@@>zS@-zC@->M`&=`d|>AA>zC@-M`>>dx|XX@MwS@-M`EW?x`|@@MwS@-M` F`d|ZAAMwC@-M`Zbdx|!XXMwS@-M`gWDa%x`|!@@@MwS@-M`,h`d|zDAA@MwC@-M`zdx| IXXMwS@-M` W {x`|%I@@MwS@-M`n`d|eJAAMwC@-M`edx|QXXMwS@-M`—W PMR 15)x`|Q@@MwS@-M``d| SAAMwC@-M` dx|ZXXMwS@-M`W0x`|Z@@@MwS@-M``d|[AA@MwC@-M`աdx|QcXXMwS@-M`QW@Dau@x`|fc@@MwS@-M``d|dAAMwC@-M`ͪdx| lXX@MwS@-M` WP x`| l@@MwS@-M`A`d|zmAAMwC@-M`zdx|tXX@MwS@-M`úW`PMR 16jx`|t@@MwS@-M``d|+vAAMwC@-M`+dx|L}XX@MwS@-M`LWpx`|]}@@MwS@-M`w`d|~AAMwC@-M`dx|҅XXMwS@-M`W x`|ᅏ@@@MwS@-M``d|#AA@MwC@-M`#dx|䑏XX@>zS@-M`W x`|@@>zS@-zC@->M`0`d|`AA>zC@-M``dx|sXX@>zS@-M`sW x`|{@@>zS@-zC@->M``d|ܛAA>zC@-M`dx|XX>zS@-M`W 0x`|@@@>zS@-zC@->M``d|AA@>zC@-M`dx|XX>zS@-M`W @x`|@@@>zS@-zC@->M``d|嬏AA@>zC@-M`dx|2XX>zS@-M`2W Px`|9@@>zS@-zC@->M`w`d|AA>zC@-M`dx|¼XX@>zS@-M` W `x`|ʼ@@>zS@-zC@->M`  `d|AAA>zC@-M`A dx|6ŏXX>zS@-M`6 W p$x`|?ŏ@@@>zS@-zC@->M` `d|ƏAA@>zC@-M` d`|͏@@>zS@-zS@-M` WCQ%;x`|;ҏ@@>zC@->M`; `d|&AA>zC@-M`&@ dx|sXX@KGxS@-M`sH W&Ox`|}@@CGxS@-M`H `d|AACGxC@-M`I dx|E XX@GxS@-M`EQ W Channel VFO AZx`|M @@OGxS@-M`Q `d| AAOGxC@-M`R dx|cXX@NGxS@-M`cZ W0Lx`|i@@OGxS@-M`Z `d|AAOGxC@-M`[ dx|zXX@GxS@-M`zc W@Q%Lx`|@@CGxS@-M`c `d|AACGxC@-M`d dx|(&XXCGxS@-M`(l WP&x`|.&@@@GxS@-M`kl `d|'AA@GxC@-M`m dx|.XXOGxS@-M`t W`Channel VFO Bx`|.@@@NGxS@-M`u `d|y0AA@NGxC@-M`yv dx|7XX@NGxS@-M`} Wpx`|7@@OGxS@-M`} `d|.9AAOGxC@-M`. dx|@XX@zS@-M` W(4x`|@@@zS@-M` `d|MHAAzC@-M`M dx|)MXXzS@-M`) W(Dx`|/M@@zS@-M`m `d|NAAzC@-M` dx|UXXzS@-M` W( Tx`|U@@zS@-M`( `d|WAAzC@-M` dx|_XXzS@-M` W(0dx`|_@@zS@-M`Y `d|`AAzC@-M` dx|'vXX@zS@-M`' W(@tx`|-v@@zS@-M`e `d|wAAzC@-M`ʽ dx|~XX@zS@-M` W(Px`|~@@zS@-M` `d|kAAzC@-M`k dx|XXzS@-M` W(`x`|@@zS@-M` `d|AAzC@-M` dx|XXzS@-M` W(px`|@@zS@-M` `d|XAAzC@-M`X dx|әXX@zS@-M` W 6x`|ٙ@@zS@-M` `d||AAzC@-M`| dx|2ːXXzS@-M`2 W JKLMx`|:ː@@zS@-M`u `d|̐AAzC@-M` dx|ԐXX@zS@-M` W !x`|#Ԑ@@zS@-M`e `d|OՐAAzC@-M`O dx|ݐXXzS@-M`# W01x`| ݐ@@zS@-M`# `d|ސAAzC@-M`$ dx|吝XXzS@-M`+ W@Ax`|吝@@zS@-M`, `d|琝AAzC@-M`- dx|XX@zS@-M`4 WPQx`|@@zS@-M`4 `d|AAzC@-M`5 dx|IXX@zS@-M`I= W`ax`|V@@zS@-M`~= `d|AAzC@-M`> dx|XXzS@-M`F Wpqx`|@@zS@-M`HF `d|cAAzC@-M`cG dx|XXovS@-M`N Wx`|@@ovS@-M`1O `d|_ AAovC@-M`_P dx|~XXovS@-M`~W Wx`|@@ovS@-M`W `d|AAovC@-M`X dx|@XXovS@-M`@` Wx`|M@@ovS@-M`u` `d|VAAovC@-M`Va dx|"XXovS@-M`h Wx`| #@@@ovS@-M`6i `d|P$AA@ovC@-M`Pj dx|+XX@ovS@-M`q Wx`|+@@@ovS@-M`r `d|-AA@ovC@-M`s dx|4XXovS@-M`z Wx`|4@@ovS@-M`z `d|5AAovC@-M`{ dx|S=XXovS@-M`S Wx`|c=@@ovS@-M` `d|>AAovC@-M` dx|EXXovS@-M`ߋ Wx`|E@@ovS@-M` `d|GAAovC@-M` dx|xNXX@GxS@-M`x Wx`|N@@CGxS@-M` `d|OAACGxC@-M` dx|VXXCGxS@-M`ܜ Wx`|V@@@GxS@-M` `d|NXAA@GxC@-M`N dx|N_XX@GxS@-M`N W "x`|c_@@CGxS@-M` `d|`AACGxC@-M` dx|sXXCGxS@-M` W02x`|"s@@@GxS@-M`A `d|ntAA@GxC@-M`n dx||{XX@GxS@-M`| W@Bx`|{@@CGxS@-M` `d||AACGxC@-M` dx|XXOGxS@-M` WPRx`|񃑝@@LGxS@-M` `d| AALGxC@-M` dx|XXovS@-M` W`bx`|@@ovS@-M` `d|čAAovC@-M` dx|#XXovS@-M`# Wprx`|5@@ovS@-M`Z `d|>AAovC@-M`> dx|XXovS@-M` Wx`|؝@@ovS@-M` `d|AAovC@-M` d`|U@@LGxS@-M` `d|AALGxC@-M` dx|XXLGxS@-M` Wx`|Ͳ@@OGxS@-M` `d|򳑝AAOGxC@-M` dx| XXOGxS@-M`  Wx`|@@LGxS@-M`6 `d|7AALGxC@-M`7 dx|{ÑXX@KGxS@-M`{ Wx`|Ñ@@@NGxS@-M` `d|đAA@NGxC@-M` dx|(̑XXLGxS@-M`( Wx`|3̑@@OGxS@-M`r `d|͑AAOGxC@-M` dx|ՑXXOGxS@-M` Wx`|#Ց@@LGxS@-M`t `d|֑AALGxC@-M` dx|ݑXX@ovS@-M`# Wx`|ݑ@@@ovS@-M`.$ `d|XߑAA@ovC@-M`X% dx|摝XXCGxS@-M`, W_cdeBCDEx`|摝@@CGxS@-M`*- `d|d葝AACGxC@-M`d. dx|XX@NGxS@-M`5 WFGHI9x`|@@@KGxS@-M`.6 `d|AA@KGxC@-M`7 dx|XX@KGxS@-M`> W #x`|@@@NGxS@- ``|5@@@KGxC@->M`5? `d|vAA@NGxC@-M`v@ dx|XXCGxS@-M`G W03x`|@@CGxS@-M`;H `d|QAACGxC@-M`QI dx| XXLGxS@-M`P W@Cx`| @@OGxS@-M`CQ `d| AAOGxC@-M`R dx|XXOGxS@-M`Y WPSx`|@@LGxS@-M`M`q `d|,AACGxC@-M`r dx| 4XXGGxS@-M` z Wpsx`|4@@CGxS@-M`Sz `d|5AACGxC@-M`{ dx|=XX@NGxS@-M` Wx`|=@@@KGxS@-M`V `d|>AA@KGxC@-M` dx|FXX@KGxS@-M` Wx`|F@@@NGxS@-M` `d|GAA@NGxC@-M` dx|NXXCGxS@-M` Wx`|N@@GGxS@-M` `d|PAAGGxC@-M` dx|WXXCGxS@-M` Wx`|W@@@GxS@-M` `d|XAA@GxC@-M`ܞ dx|_XXLGxS@-M`ޥ Wx`|_@@OGxS@-M` `d|aAAOGxC@-M` dx|hXXOGxS@-M` Wx`|h@@LGxS@-M` `d|jAALGxC@-M` dx|_qXX@GxS@-M`_ Wx`|jq@@CGxS@-M` `d|rAACGxC@-M` dx|zXXGGxS@-M` Wx`| z@@CGxS@-M`T `d|`{AACGxC@-M`` dx|XX@NGxS@-M` Wx`|˂@@@KGxS@-M`, `d|AA@KGxC@-M` dx|zXXzS@-M`z Wx`|@@zS@-M` `d|AAzC@-M` dx|NXXzS@-M`N W $x`|]@@zS@-M` `d|AAzC@-M` dx|XX@zS@-M` W04x`|@@zS@-M`T `d|]AAzC@-M`] dx|ѥXX@zS@-M` W@Dx`|ߥ@@zS@-M`. `d|M` `d|;AAzC@-M`; dx|궒XXzS@-M` W`dx`|@@zS@-M`; `d|zAAzC@-M`z dx|XX@zS@-M` Wptx`|@@zS@-M` `d|AAzC@-M` dx|jȒXXzS@-M`j Wx`|xȒ@@zS@-M` `d|ɒAAzC@-M` dx|BђXX@zS@-M`B Wx`|Oђ@@zS@-M` `d|ҒAAzC@-M` dx|璝XXzS@-M`- Wx`|璝@@zS@-M`- `d|钝AAzC@-M`/ dx|XXzS@-M`6 Wx`|@@zS@-M`6 `d|AAzC@-M`7 dx|SXX@zS@-M`S? Wx`|`@@zS@-M`? `d|AAzC@-M`@ dx|XX@zS@-M`H Wx`|&@@zS@-M`hH `d|~AAzC@-M`~I dx| XXzS@-M`P Wx`| @@zS@-M`+Q `d|< AAzC@-M`M`Y `d|AAzC@-M`Z dx|XXzS@-M`a Wnopqx`| @@zS@-M`Ob `d|AAzC@-M`c dx|$XXzS@-M`j Wx`|$@@zS@-M`j `d|%AAzC@-M`k dx|H-XXzS@-M`Hs W %x`|U-@@zS@-M`s `d|.AAzC@-M`t dx|6XXzS@-M`| W05x`|6@@zS@-M`Z| `d|O7AAzC@-M`O} dx|>XX@KGxS@-M` W@Ex`|>@@@NGxS@-@@@KGxC@->M` `d|?AA@NGxC@-M` dx|,GXXCGxS@-M`, WPUx`|8G@@GGxS@-M` `d|HAAGGxC@-M` dx|OXXCGxS@-M` W`ex`|O@@@GxS@-M`m `d|RQAA@GxC@-M`R dx|XXXLGxS@-M` Wpux`|X@@OGxS@-M` `d|YAAOGxC@-M` dx|KaXXOGxS@-M`K Wx`|Wa@@LGxS@-M` `d|bAALGxC@-M` dx|iXX@GxS@-M` Wx`| j@@CGxS@-M`] `d|-kAACGxC@-M`- dx|rXXGGxS@-M` Wx`|r@@CGxS@-M`ܸ `d|sAACGxC@-M` dx|I{XX@NGxS@-M`I Wx`|W{@@@KGxS@-M` `d||AA@KGxC@-M` dx|XX@KGxS@-M` Wx`|@@@NGxS@-M`d `d|gAA@NGxC@-M`g dx|XXCGxS@-M` Wx`|ʌ@@GGxS@-M` `d|򍓝AAGGxC@-M` dx|pXXCGxS@-M`p Wx`||@@@GxS@-M` `d|▓AA@GxC@-M` dx|&XXLGxS@-M`& Wx`|2@@OGxS@-M` `d|AAOGxC@-M` dx|ڦXXOGxS@-M` Wx`|榓@@LGxS@-M`9 `d|@AALGxC@-M`@ dx|XX@GxS@-M` Wx`|@@CGxS@-M` `d|ҰAACGxC@-M` dx| XXGGxS@-M` W &x`|@@CGxS@-M`2 `d|2AACGxC@-M`2 dx|vXX@NGxS@-M`v W06x`|@@@KGxS@-M` `d|AA@KGxC@-M` dx| ɓXX@KGxS@-M`  W@Fx`|+ɓ@@@NGxS@-M` `d|ʓAA@NGxC@-M` dx|ѓXXCGxS@-M` WPVx`|ѓ@@GGxS@-M`/ `d|1ӓAAGGxC@-M`1 dx|ړXXCGxS@-M` W`fx`|ړ@@@GxS@-M` `d|ۓAA@GxC@-M`! dx|P㓝XXLGxS@-M`P) Wpvx`|\㓝@@OGxS@-M`) `d|䓝AAOGxC@-M`* dx|쓝XXOGxS@-M`2 Wx`|+쓝@@LGxS@-M`{2 `d|x퓝AALGxC@-M`x3 dx|XX@GxS@-M`: Wx`|@@CGxS@-M`; `d|AACGxC@-M`; dx|,XXGGxS@-M`,C Wx`|9@@CGxS@-M`C `d|AACGxC@-M`D dx|XX@NGxS@-M`K Wx`|@@@KGxS@-M`IL `d|GAA@KGxC@-M`GM dx|XX@KGxS@-M`T Wx`|@@@NGxS@-M`U `d|AA@NGxC@-M`V dx|BXXCGxS@-M`B] Wx`|P@@GGxS@-M`] `d|AAGGxC@-M`^ dx| XXCGxS@-M`f Wx`| @@@GxS@-M`:f `d|e!AA@GxC@-M`eg dx|(XXLGxS@-M`n Wx`|(@@OGxS@-M`n `d| *AAOGxC@-M` p dx|O1XXOGxS@-M`Ow W>@Ax`|]1@@LGxS@-M`w `d|2AALGxC@-M`x dx|9XX@GxS@-M` Wx`|9@@CGxS@-M`L `d|p;AACGxC@-M`p dx|sBXXGGxS@-M`s W 'x`|B@@CGxS@-M` `d|CAACGxC@-M`É dx|KKXX@NGxS@-M`K W07x`|\K@@@KGxS@-M` `d|LAA@KGxC@-M` dx|SXX@KGxS@-M`љ W@Gx`|S@@@NGxS@-M` `d|1UAA@NGxC@-M`1 dx|\XXCGxS@-M` WPWx`|\@@GGxS@-M`ޢ `d|#^AAGGxC@-M`# dx|@eXXzS@-M`@ W`gx`|Oe@@zS@-M`ī `d|fAAzC@-M`̬ dx|mXXzS@-M` Wpwx`|n@@zS@-M`S `d|\oAAzC@-M`\ dx|vXXzS@-M` Wx`|v@@zS@-M`6 `d|9xAAzC@-M`9 dx|XXzS@-M` Wx`|@@zS@-M` `d|AAzC@-M` dx|zXXzS@-M`z Wx`|@@zS@-M` `d|󉔝AAzC@-M` dx|GXXzS@-M`G Wx`|T@@zS@-M` `d|AAzC@-M` dx|XXzS@-M` Wx`|@@zS@-M`W `d|mAAzC@-M`m dx|XXzS@-M` Wx`|@@zS@-M` `d|AAzC@-M` dx|%XXzS@-M`% Wx`|>@@zS@- ``|@@zC@->M` `d|AAzC@-M` dx|XXzS@-M`Wx`|@@zS@-M`;`d|K”AAzC@-M`Kdx|ɔXX@zS@-M`Wx`|ɔ@@zS@-M`&`d|E˔AAzC@-M`Edx|nҔXX@zS@-M`nWx`|{Ҕ@@zS@-M``d|ӔAAzC@-M`dx|ڔXXzS@-M` W (x`|۔@@zS@-M`I!`d|GܔAAzC@-M`G"dx|㔝XXzS@-M`)W08x`|㔝@@zS@-M`*`d|唝AAzC@-M`+dx|u씝XX@zS@-M`u2W@Hx`|씝@@zS@-M`2`d|픝AAzC@-M`3dx|3XXzS@-M`3;WPXx`|?@@zS@-M`;`d|AAzC@-M`<dx|XX@zS@-M`CW`hx`|@@zS@-M`D`d|AAzC@-M`Ddx|IXXzS@-M`ILWpxx`|U@@zS@-M`L`d|AAzC@-M`Mdx|XXzS@-M`UWx`|@@zS@-M`UU`d|fAAzC@-M`fVdx|XX@zS@-M`]Wx`|@@zS@-M` ^`d|hAAzC@-M`h_dx|t XX@zS@-M`tfWx`| @@zS@-M`f`d|!AAzC@-M`gdx|8)XXzS@-M`8oWx`|E)@@zS@-M`o`d|*AAzC@-M`pdx|1XXzS@-M`wWx`|1@@zS@-M`@x`d|O3AAzC@-M`Oydx|:XXzS@-M`Wx`|:@@zS@-M``d|M``d|DAAzC@-M`dx|KXX@zS@-M`Wx`|K@@zS@-M`.`d|JhAAzC@-M`Jdx|mXXzS@-M`ڳWdBCDEFGH`x`|m@@zS@-M`(`d|RoAAzC@-M`Rdx|dvXXzS@-M`dWIdx`|qv@@zS@-M``d|wAAzC@-M`ڽdx|XX@zS@-M`W )x`|@@zS@-M`\`d|]AAzC@-M`]dx|XXzS@-M`W09x`|@@zS@-M``d|䈕AAzC@-M`dx|RXX@zS@-M`RW@Ix`|]@@zS@-M``d|AAzC@-M`dx|XXCGxS@-M`WPYx`|@@@GxS@-M`d`d|bAA@GxC@-M`bdx|XX@ovS@-M`W`ix`|@@ovS@-M``d|梕AAovC@-M`dx|2XXovS@-M`2Wpyx`|?@@@ovS@-M``d|AA@ovC@-M`dx|ಕXX@ovS@-M`Wx`|첕@@ovS@-M` `d|1AAovC@-M`1dx|cXXovS@-M`cWx`|m@@@ovS@-M``d|AA@ovC@-M`dx|ĕXX@ovS@-M` Wx`|#ĕ@@ovS@-M`C `d|ŕAAovC@-M` dx|̕XXovS@-M`Wx`|̕@@@ovS@-M``d|-ΕAA@ovC@-M`-dx|HՕXX@ovS@-M`HWx`|RՕ@@ovS@-M``d|֕AAovC@-M`dx|ݕXXovS@-M`#Wx`|ݕ@@@ovS@-M`#`d|ߕAA@ovC@-M`%dx|敝XX@ovS@-M`,Wx`|敝@@ovS@-M`,`d|畝AAovC@-M`-dx|%XXovS@-M`%5Wx`|/@@@ovS@-M`r5`d|AA@ovC@-M`6dx|XXovS@-M`=W  x`|@@ovS@-M`=`d|AAovC@-M`>dx|XXovS@-M`W x`|@@ovS@-M` `d|ZAAovC@-M`dx|XXovS@-M`u W *x`|@@ovS@-M` `d| AAovC@-M` dx|pXXovS@-M`0W 0:x`|{@@ovS@-M`|`d|AAovC@-M`zdx|XXovS@-M`W @Jx`|$@@@ovS@-M``d|AA@ovC@-M`Udx|"XXovS@-M`{&W PZx`|"@@ovS@-M`&`d|#AAovC@-M`'dx|m+XXovS@-M`-/W `jx`|{+@@ovS@-M`_/`d|,AAovC@-M`_0dx|3XXovS@-M`7W pzx`|4@@ovS@-M`7`d|[5AAovC@-M`9dx|M`@`d|>AAovC@-M`Adx|EXXovS@-M`DIW x`|E@@ovS@-M`J`d|:GAAovC@-M`Jdx|9NXXovS@-M`QW x`|EN@@ovS@-M`3R`d|OAAovC@-M``Sdx|VXXovS@-M`wZW x`|V@@ovS@-M`Z`d|XAAovC@-M`[dx|O_XXovS@-M`cW x`|]_@@ovS@-M`Ic`d|`AAovC@-M`wddx|gXXovS@-M`kW x`|g@@ovS@-M`k`d|LiAAovC@-M` mdx|pXXMwS@-M`EtW x`|p@@MwS@-M`{t`d|qAAMwC@-M`udx|8yXXMwS@-M`|W x`|Ey@@MwS@-M`G}`d|zAAMwC@-M`\~dx|XXMwS@-M`_W "M x`|@@@MwS@-M``d|AA@MwC@-M`dx|\XXMwS@-M`W NOPQRST`x`|i@@MwS@-M`k`d|AAMwC@-M`hdx|XX@MwS@-M`ҥW +x`|@@MwS@-M` `d||AAMwC@-M`<dx|תXX@MwS@-M`W 0;x`|䪖@@MwS@-M``d|YAAMwC@-M`dx|fXX@MwS@-M`&W @Kx`|u@@MwS@-M`v`d|ʹAAMwC@-M`dx|$XXMwS@-M`W P[x`|4@@@MwS@-M`6`d|kAA@MwC@-M`+dx|ĖXXMwS@-M`W `kx`|Ė@@MwS@-M``d|-ƖAAMwC@-M`dx|͖XXMwS@-M`W p{x`|͖@@MwS@-M``d|1ϖAAMwC@-M`dx|w֖XXMwS@-M`7W x`|֖@@@MwS@-M``d|זAA@MwC@-M`dx|<ߖXXMwS@-M`W x`|Iߖ@@MwS@-M``d|AAMwC@-M`dx|疝XX@MwS@-M`W x`|薝@@MwS@-M``d|:閝AAMwC@-M`dx|XX@MwS@-M`W x`|@@MwS@-M``d|;AAMwC@-M`dx|vXX@MwS@-M`6W x`|@@MwS@-M``d| AAMwC@-M`dx|{XX@ovS@-M`;W x`|@@@ovS@-M``d|AA@ovC@-M`dx|2XX@ovS@-M`W x`|>@@@ovS@-M`=`d|AA@ovC@-M`Tdx| XX@ovS@-M`s$W x`| @@@ovS@-M`$`d|"AA@ovC@-M`%dx|q)XX@ovS@-M`1-W  x`||)@@@ovS@-M`|-`d|*AA@ovC@-M`.dx|02XX@ovS@-M`5W x`|<2@@@ovS@-M`<6`d|3AA@ovC@-M`W7dx|:XX@ovS@-M`>W ,x`|:@@@ovS@-``|@;@@@ovC@->M`?`d|>M`G`d|EAA@ovC@-M`Hdx|^LXX@ovS@-M`PW @Lx`|jL@@@ovS@-M`hP`d|MAA@ovC@-M`Qdx|uUXX@ovS@-M`5YW P\x`|U@@@ovS@-M`Y`d|VAA@ovC@-M`Zdx|/^XX@ovS@-M`aW `lx`|<^@@@ovS@-M`Eb`d|_AA@ovC@-M`Acdx|fXX@ovS@-M`jW p|x`|f@@ovS@-M`j`d|YhAAovC@-M`ldx|oXXovS@-M`[sW x`|o@@ovS@-M`s`d|qAAovC@-M`tdx|xXXovS@-M`{W x`|.x@@ovS@-M`,|`d|DyAAovC@-M`}dx|XXovS@-M`FW x`|@@ovS@-M``d|ρAAovC@-M`dx|CXX@ovS@-M`W x`|Q@@@ovS@-M`R`d|ȊAA@ovC@-M`dx|XXovS@-M`ǕW x`|@@ovS@-M``d|'AAovC@-M`dx|˚XXovS@-M`W x`|ؚ@@@ovS@-M`Ҟ`d|AAA@ovC@-M`dx|$XX@ovS@-M`W x`|1@@ovS@-M`F`d|qAAovC@-M`1dx|竗XXovS@-M`W x`|󫗝@@@ovS@-M``d|4AA@ovC@-M`dx|XX@ovS@-M`gW `adeBCDEx`|@@ovS@-M`-`d|^AAovC@-M`dx|1XXovS@-M`W FGHICx`|>@@@ovS@-M`?`d|yAA@ovC@-M`9dx|ŗXX@ovS@-M`W -x`|Ɨ@@ovS@-M``d|SǗAAovC@-M`dx|ΗXXovS@-M`zW 0=x`|Η@@@ovS@-M``d|ЗAA@ovC@-M`dx|RחXX@ovS@-M`W @Mx`|`ח@@ovS@-M`b`d|ؗAAovC@-M`dx|!XXovS@-M`W P]x`|4@@@ovS@-M``d||ᗝAA@ovC@-M`<dx|藝XX@ovS@-M`W `mx`|藝@@ovS@-M``d|3ꗝAAovC@-M`dx|XXovS@-M`fW p}x`|@@@ovS@-M``d| AA@ovC@-M`dx|SXX@ovS@-M` W x`|_@@ovS@-M`c `d|AAovC@-M`{ dx|XXovS@-M`W x`|@@@ovS@-M``d|UAA@ovC@-M`dx|XX@ovS@-M`W x`|@@ovS@-M``d|4AAovC@-M`dx|!XXovS@-M`I%W x`|!@@@ovS@-M`%`d|"AA@ovC@-M`&dx|*XX@ovS@-M`-W x`|*@@ovS@-M`.`d|d+AAovC@-M`$/dx|2XXovS@-M`v6W x`|2@@@ovS@-M`6`d|4AA@ovC@-M`7dx|f;XX@ovS@-M`&?W x`|s;@@ovS@-M`v?`d|M`*H`d|XEAA@ovC@-M`Idx|LXX@ovS@-M`GPW x`|L@@ovS@-M`sP`d|'NAAovC@-M`Qdx|TXXovS@-M`XW x`|T@@@ovS@-M`Y`d|\VAA@ovC@-M`Zdx|]XX@ovS@-M`eaW .x`|]@@ovS@-M`a`d| _AAovC@-M`bdx|0fXXovS@-M`iW 0>x`|@f@@@ovS@-M`Aj`d|gAA@ovC@-M`Pkdx|nXXovS@-M`mrW @Nx`|n@@ovS@-M`r`d| pAAovC@-M`sdx|awXXMwS@-M`!{W P^x`|{w@@MwS@-M`r{`d|xAAMwC@-M`|dx|TXXMwS@-M`W `nx`|c@@MwS@-M`A`d|AAMwC@-M`dx|XXMwS@-M`W p~x`|@@MwS@-M``d|YAAMwC@-M`dx|ɑXX@MwS@-M`W x`|֑@@MwS@-M`G`d|AAMwC@-M`Vdx|XX@MwS@-M`PW x`|@@MwS@-M``d|AAMwC@-M`dx|XX@MwS@-M`ЦW x`|@@MwS@-M``d|cAAMwC@-M`#dx|XXMwS@-M`W x`|@@MwS@-M``d|GAAMwC@-M`dx|XXMwS@-M`|W x`|@@MwS@-M``d|ØAAMwC@-M`dx|KʘXXMwS@-M` W x`|Xʘ@@MwS@-M`|`d|˘AAMwC@-M`dx| ӘXX@MwS@-M`W x`|Ә@@@MwS@-M``d|XԘAA@MwC@-M`dx|ۘXXMwS@-M`W x`|ۘ@@MwS@-M``d|ܘAAMwC@-M`dx|䘝XX@MwS@-M`TW#$%&'()*Sx`|䘝@@MwS@-M``d|嘝AAMwC@-M`dx|혝XXyDwS@-M`WMNUx`|1혝@@vDwS@-M` `d|PAAvDwC@-M`dx|XX5%zS@-M`YW /x`|@@5%zS@-M``d|AA5%zC@-M`dx|XX5%zS@-M`W0?x`|@@5%zS@-M``d|0AA5%zC@-M`dx|XX5%zS@-M`D W@Ox`|@@5%zS@-M`w `d|AA5%zC@-M`w dx|XX5%zS@-M`WP_x`| @@5%zS@-M``d|*AA5%zC@-M`dx|XX5%zS@-M`W`ox`|@@5%zS@-M``d|AA5%zC@-M`dx|l XX5%zS@-M`,$Wpx`|{ @@5%zS@-M`]$`d|!AA5%zC@-M`b%dx|&)XX5%zS@-M`,Wx`|@)@@5%zS@-M`*-`d|e*AA5%zC@-M`%.dx|1XX5%zS@-M`5Wx`|1@@5%zS@-M`5`d|2AA5%zC@-M`6dx|q:XX5%zS@-M`1>Wx`|:@@5%zS@-``|:@@5%zC@->M`l>`d|;AA5%zC@-M`?dx|BXX5%zS@-M`FWx`|B@@5%zS@-M`F`d|CAA5%zC@-M`Gdx|0KXX5%zS@-M`NWx`|DK@@5%zS@-M`9O`d|dLAA5%zC@-M`$Pdx|hSXX5%zS@-M`(WWx`|sS@@5%zS@-M`WW`d|TAA5%zC@-M`EXdx|[XX5%zS@-M`g_Wx`|[@@5%zS@-M`_`d|\AA5%zC@-M``dx|\dXX5%zS@-M`hWx`|hd@@5%zS@-M`Ih`d|weAA5%zC@-M`7idx|mXX5%zS@-M`pWx`|m@@5%zS@-M`p`d|(nAA5%zC@-M`qdx|vXX5%zS@-M`6W x`|@@5%zS@-M``d|݄AA5%zC@-M`dx|vXX5%zS@-M`6W 0x`|~@@5%zS@-``|Œ@@5%zC@->M``d|AA5%zC@-M`ёdx|uXX5%zS@-M`5W0@x`|}@@5%zS@-M``d|AA5%zC@-M`dx|tXX5%zS@-M`4W@Px`|{@@5%zS@-M`w`d|AA5%zC@-M`hdx|5XX5%zS@-M`WP`x`|>@@5%zS@-M`:`d|pAA5%zC@-M`0dx|կXX5%zS@-M`W`px`|ݯ@@5%zS@-M``d|_AA5%zC@-M`dx|׸XX5%zS@-M`Wpx`|޸@@5%zS@-M`޼`d|]AA5%zC@-M`dx|XX5%zS@-M`Wx`|@@5%zS@-M``d|™AA5%zC@-M`dx|ʙXX5%zS@-M`oWx`|ʙ@@5%zS@-M``d|0̙AA5%zC@-M`dx|әXX5%zS@-M`mWx`|ә@@5%zS@-M``d|ՙAA5%zC@-M`dx|ܙXX5%zS@-M`qWx`|ܙ@@5%zS@-M``d|9ޙAA5%zC@-M`dx|噝XX5%zS@-M`nWx`|噝@@5%zS@-M``d|0癝AA5%zC@-M`dx|XX5%zS@-M`Wx`|@@5%zS@-M``d|.AA5%zC@-M`dx|`XX5%zS@-M` Wx`|h@@5%zS@-M`i`d|AA5%zC@-M`^dx| XX5%zS@-M`Wx`| @@5%zS@-M``d|^(AA5%zC@-M`,dx|N-XX5%zS@-M`1WdeBCDEFGx`|V-@@5%zS@-M`T1`d|.AA5%zC@-M``2dx|5XX5%zS@-M`9WHIx`|5@@5%zS@-M`9`d|7AA5%zC@-M`:dx|]>XX5%zS@-M`BW 1x`|d>@@5%zS@-@@5%zC@->M`gB`d|?AA5%zC@-M`PCdx|FXX5%zS@-M`JW0Ax`|F@@5%zS@-M`J`d|HAA5%zC@-M`Kdx|:PXX5%zS@-M`SW@Qx`|BP@@5%zS@-M`9T`d|QAA5%zC@-M`VUdx|YXX5%zS@-M`L]WPax`|Y@@5%zS@-M`]`d|ZAA5%zC@-M`^dx|bXX5%zS@-M`afW`qx`|b@@5%zS@-M`f`d|cAA5%zC@-M`gdx|&zXXMwS@-M`}Wpx`|3z@@@MwS@-M`6~`d|v{AA@MwC@-M`6dx|XXMwS@-M`lWx`|@@MwS@-M`Æ`d|AAMwC@-M`Ƈdx|7XX@MwS@-M`Wx`|F@@MwS@-M`V`d|AAMwC@-M`Gdx|XX@MwS@-M`Wx`|@@MwS@-M`v`d|AAMwC@-M`~dx|XX@MwS@-M`jWx`|@@MwS@-M``d| AAMwC@-M`͡dx|eXXMwS@-M`%Wx`|p@@@MwS@-M`u`d|AA@MwC@-M`ldx|XXMwS@-M`Wx`|@@@MwS@-M``d|QAA@MwC@-M`dx|XXMwS@-M`hWx`|@@MwS@-M``d| AAMwC@-M`̻dx|aXX@MwS@-M`!Wx`|m@@MwS@-M`q`d|AAMwC@-M`dx|ǚXX@MwS@-M`Wx`|Ț@@MwS@-M`+`d||ɚAAMwC@-M`<dx|КXX@MwS@-M`{W"x`|К@@MwS@-M``d|)ҚAAMwC@-M`dx|BٚXXMwS@-M`W 2x`|[ٚ@@@MwS@-M`U`d|ښAA@MwC@-M`ydx|᚝XXMwS@-M`W0Bx`|᚝@@@MwS@-M``d|㚝AA@MwC@-M`dx|XXMwS@-M`\W@Rx`|@@MwS@-M``d|AAAMwC@-M`dx|\XX@MwS@-M`WPbx`|l@@MwS@-M`~`d|AAMwC@-M`dx|XXMwS@-M`z W`rx`|@@MwS@-M` `d| AAMwC@-M` dx|<XXMwS@-M`Wpx`|F@@@MwS@-M`R`d|AA@MwC@-M`gdx|XXMwS@-M`Wx`|@@@MwS@-M``d|oAA@MwC@-M`/dx|"XXMwS@-M`T&Wx`|"@@MwS@-M`&`d|9$AAMwC@-M`'dx|C+XXMwS@-M`/Wx`|R+@@@MwS@-M`Y/`d|,AA@MwC@-M`h0dx|3XXMwS@-M`7Wx`|3@@MwS@-M`7`d|/5AAMwC@-M`8dx|M`@`d|>AAMwC@-M`Adx|cEXXMwS@-M`#IWx`|sE@@@MwS@-M`tI`d|FAA@MwC@-M`Jdx|NXXMwS@-M`QWx`|%N@@MwS@-M`+R`d|zOAAMwC@-M`:Sdx|VXX@MwS@-M`nZWx`|V@@@MwS@-M`Z`d|XAA@MwC@-M`[dx|?_XXMwS@-M`bW+,-./012x`|M_@@@MwS@-M`Pc`d|`AA@MwC@-M`Nddx|hXX@MwS@-M`kWVWX.x`|h@@MwS@-M`l`d|kiAAMwC@-M`+mdx|pXXMwS@-M`JtW 3x`|p@@@MwS@-M`t`d|qAA@MwC@-M`udx|'yXXMwS@-M`|W0Cx`|5y@@@MwS@-M`8}`d|zAA@MwC@-M`a~dx|ہXXMwS@-M`W@Sx`|聛@@MwS@-M``d|"AAMwC@-M`dx|XX@MwS@-M`fWPcx`|@@@MwS@-M``d|AA@MwC@-M`Ədx|fXXMwS@-M`&W`sx`|s@@@MwS@-M`v`d|͔AA@MwC@-M`dx|)XX@MwS@-M`Wpx`|8@@MwS@-M`<`d|AAMwC@-M`qdx|XXMwS@-M`eWx`|@@@MwS@-M``d|AA@MwC@-M`édx|hXXMwS@-M`(Wx`|u@@@MwS@-M`w`d|ήAA@MwC@-M`dx|XXMwS@-M`Wx`|@@MwS@-M``d|\AAMwC@-M`dx|XX@MwS@-M`EWx`|@@@MwS@-M``d|ᅴAA@MwC@-M`dx|uǛXXMwS@-M`5Wx`|Ǜ@@@MwS@-M``d|țAA@MwC@-M`dx|ϛXX@MwS@-M`Wx`| Л@@MwS@-M``d|KћAAMwC@-M` dx|؛XXMwS@-M`Wx`|؛@@@MwS@-M``d|'ڛAA@MwC@-M`dx|uᛝXXMwS@-M`5Wx`|ᛝ@@@MwS@-M``d|⛝AA@MwC@-M`dx|3ꛝXXMwS@-M`Wx`|@ꛝ@@MwS@-M`?`d|뛝AAMwC@-M`adx|jXX@MwS@-M`*W$x`|w@@@MwS@-M`|`d|AA@MwC@-M`dx|!XXMwS@-M`W 4x`|.@@@MwS@-M`.`d|nAA@MwC@-M`.dx|XX@MwS@-M`wW0Dx`|@@MwS@-M``d|AAMwC@-M`dx|: XXMwS@-M`W@Tx`|F @@@MwS@-M`H`d| AA@MwC@-M`Sdx|XXMwS@-M`zWPdx`|@@@MwS@-M``d|AA@MwC@-M`dx|XXMwS@-M` W`tx`| @@MwS@-M`$!`d|AAMwC@-M`@"dx|%XX@MwS@-M`)Wpx`|%@@@MwS@-M`)`d|8'AA@MwC@-M`*d`|.@@MwS@-M`?`d|M`G`d|DEAA5%zC@-M`Idx|sLXX5%zS@-M`3PWx`|L@@5%zS@-M`P`d|MAA5%zC@-M`Qdx|7UXX5%zS@-M`XWx`|CU@@5%zS@-M`HY`d|VAA5%zC@-M`JZdx|]XX5%zS@-M`aWx`|^@@5%zS@-M` b`d|I_AA5%zC@-M` cdx|fXX5%zS@-M`zjWx`|f@@5%zS@-M`Ek`d|hAA5%zC@-M`aldx|oXX5%zS@-M`BsWx`|o@@5%zS@-M`s`d|pAA5%zC@-M`tdx|DxXXMwS@-M`|Wx`|Qx@@@MwS@-M`c|`d|yAA@MwC@-M`i}dx|XX@MwS@-M`WdeBCDEFGx`| @@MwS@-M``d|bAAMwC@-M`"dx|XXMwS@-M`xWHIx`|ʼn@@@MwS@-M`ˍ`d|AA@MwC@-M`Ǝdx|XXMwS@-M`?W 5x`|@@@MwS@-M``d|ޓAA@MwC@-M`dx|.XXMwS@-M`W0Ex`|:@@MwS@-M`Q`d|AAMwC@-M`Mdx|XX@MwS@-M`W@Ux`|@@@MwS@-M``d|VAA@MwC@-M`dx|XXMwS@-M`iWPex`|@@@MwS@-M``d|򭜝AA@MwC@-M`dx|;XX@MwS@-M`W`ux`|J@@MwS@-M`N`d|AAMwC@-M`udx|XXMwS@-M`Wpx`|ͽ@@@MwS@-M`5`d|tAA@MwC@-M`4dx|~ƜXXMwS@-M`>Wx`|Ɯ@@@MwS@-M``d|ǜAA@MwC@-M`dx|9ϜXXMwS@-M`Wx`|FϜ@@MwS@-M`F`d|МAAMwC@-M`]dx|לXX@MwS@-M`Wx`|ל@@@MwS@-M``d|ٜAA@MwC@-M`dx|SXXMwS@-M`Wx`|_@@@MwS@-M`^`d|᜝AA@MwC@-M`dx|蜝XX@MwS@-M`Wx`|蜝@@MwS@-M``d| ꜝAAMwC@-M`dx|XXMwS@-M`XWx`|@@@MwS@-M``d|AA@MwC@-M`dx|XXMwS@-M`Wx`|@@@MwS@-M``d|FAA@MwC@-M`dx|XXMwS@-M`Wx`|@@MwS@-M``d|AAMwC@-M`dx|k XX@>zS@-M`+Wx`| @@>zS@-zC@->M`a`d| AA>zC@-M`dx|XX@>zS@-M`W&x`|'@@>zS@-zC@->M``d|{AA>zC@-M`;dx|XX>zS@-M` W 6x`|@@@>zS@-zC@->M` `d|<AA@>zC@-M`!dx|%XX>zS@-M`^)W0Fx`|%@@@>zS@-zC@->M`)`d|&AA@>zC@-M`*dx|f.XX>zS@-M`&2W@Vx`|s.@@>zS@-zC@->M`2`d|/AA>zC@-M`3dx|6XX@>zS@-M`:WPfx`|7@@>zS@-zC@->M` ;`d|%8AA>zC@-M`;dx|?XX>zS@-M`qCW`vx`|?@@@>zS@-zC@->M`C`d|@AA@>zC@-M`Ddx|zS@-M`KWpx`|IH@@>zS@-zC@->M`dL`d|IAA>zC@-M`_Mdx|PXX>zS@-M`TWx`| Q@@@>zS@-zC@->M`T`d|LRAA@>zC@-M` Vdx|YXX>zS@-M`]Wx`|Y@@>zS@-zC@->M`]`d|:[AA>zC@-M`^dx|bXX>zS@-M`VfWx`|b@@@>zS@-zC@->M`f`d|cAA@>zC@-M`gdx|^kXX>zS@-M`oWx`|lk@@@>zS@-zC@->M`Uo`d|lAA@>zC@-M`pdx|tXX>zS@-M`wWx`|-t@@@>zS@-zC@->M`x`d|uAA@>zC@-M`@ydx||XX>zS@-M`Wx`||@@@>zS@-zC@->M`ǀ`d|A~AA@>zC@-M`dx|XX>zS@-M`FWx`|@@>zS@-zC@->M``d|҆AA>zC@-M`dx|EXX>zS@-M`Wx`|S@@@>zS@-zC@->M`d`d|AA@>zC@-M`adx|XX@>zS@-M`šW35678:;<x`|@@>zS@-zC@->M``d|PAA>zC@-M`dx|ŸXX>zS@-M`W=YZ[\]^x`|ϟ@@>zS@-zC@->M``d|$AA>zC@-M`dx|zS@-M`W 7x`|R@@>zS@-zC@->M`}`d|AA>zC@-M`^dx|XX@>zS@-M`W0Gx`|@@>zS@-zC@->M``d|2AA>zC@-M`dx|FXX>zS@-M`W@Wx`|S@@@>zS@-zC@->M`2`d|AA@>zC@-M``dx|XX>zS@-M`WPgx`|*@@@>zS@-zC@->M` `d||ÝAA@>zC@-M`<dx|ʝXX>zS@-M`W`wx`|ʝ@@>zS@-zC@->M``d|H̝AA>zC@-M`dx|ӝXX>zS@-M`IWpx`|ӝ@@@>zS@-zC@->M``d|ԝAA@>zC@-M`dx|RܝXX@>zS@-M`Wx`|aܝ@@>zS@-zC@->M`E`d|ݝAA>zC@-M`udx|坝XX>zS@-M`Wx`| 坝@@>zS@-zC@->M``d|z杝AA>zC@-M`:dx|흝XX@>zS@-M`Wx`|흝@@>zS@-zC@->M``d|-AA>zC@-M`dx|FXX@>zS@-M`Wx`|S@@>zS@-zC@->M`i`d|AA>zC@-M``dx|XX>zS@-M`Wx`|@@@>zS@-zC@->M``d|8AA@>zC@-M`dx|XX>zS@-M`a Wx`|@@@>zS@-zC@->M` `d|AA@>zC@-M` dx|`XX>zS@-M` Wx`|m@@>zS@-zC@->M`N`d|AA>zC@-M`fdx|!XX>zS@-M`Wx`|.@@@>zS@-zC@->M`B`d|{AA@>zC@-M`;dx|!XX@>zS@-M`%Wx`|!@@>zS@-zC@->M`%`d|#AA>zC@-M`&dx|*XX>zS@-M`U.W(x`|*@@>zS@-zC@->M`.`d|+AA>zC@-M`/dx|R3XX@>zS@-M`7W 8x`|_3@@>zS@-zC@->M`r7`d|4AA>zC@-M`^8dx|;XX@>zS@-M`?W0Hx`|;@@>zS@-zC@->M` @`d|b=AA>zC@-M`"Adx|ZDXX>zS@-M`HW@Xx`|hD@@@>zS@-zC@->M`H`d|EAA@>zC@-M``Idx|MXX>zS@-M`PWPhx`|M@@@>zS@-zC@->M`1Q`d|xNAA@>zC@-M`8Rdx|UXX>zS@-M`YW`xx`|U@@>zS@-zC@->M`Y`d|6WAA>zC@-M`Zdx|^XX>zS@-M`QbWpx`|^@@@>zS@-zC@->M`b`d|_AA@>zC@-M`cdx|JgXX@>zS@-M` kWx`|`g@@>zS@-zC@->M`:k`d|hAA>zC@-M`tldx|oXX>zS@-M`sWx`| p@@>zS@-zC@->M`s`d|YqAA>zC@-M`udx|xXX@>zS@-M`j|Wx`|x@@>zS@-zC@->M`|`d|zAA>zC@-M`}dx|7XX@>zS@-M`Wx`|L@@>zS@-zC@->M`&`d|AA>zC@-M`Zdx|򉞝XX>zS@-M`Wx`|@@@>zS@-zC@->M`ލ`d|DAA@>zC@-M`dx|xXX>zS@-M`8Wx`|@@@>zS@-zC@->M`d`d|ÓAA@>zC@-M`dx|0XX>zS@-M`Wx`|?@@>zS@-zC@->M``d|AA>zC@-M`Qdx|£XX>zS@-M`Wx`|ϣ@@@>zS@-zC@->M``d|6AA@>zC@-M`dx|ŞXX@>zS@-M`fWbdeBCDEFx`|Ş@@>zS@-zC@->M``d|ǞAA>zC@-M`dx|YΞXX>zS@-M`WGHIx`|hΞ@@>zS@-zC@->M`G`d|ϞAA>zC@-M`[dx|`䞝XX@>zS@-M` W 9x`|t䞝@@>zS@-zC@->M`M`d|垝AA>zC@-M`dx| 힝XX@>zS@-M`W0Ix`|-힝@@>zS@-zC@->M``d|~AA>zC@-M`>dx|XX>zS@-M`W@Yx`|@@@>zS@-zC@->M``d|"AA@>zC@-M`dx|XX>zS@-M`_ WPix`|@@@>zS@-zC@->M` `d|AA@>zC@-M` dx|MXX>zS@-M` W`yx`|b@@>zS@-zC@->M`; `d|AA>zC@-M`v dx|XX>zS@-M` Wpx`|@@@>zS@-zC@->M` `d|AAA@>zC@-M` dx|XX@>zS@-M`U Wx`|@@>zS@-zC@->M` `d|AA>zC@-M` dx| XX>zS@-M`$ Wx`| @@>zS@-zC@->M`$ `d|"AA>zC@-M`% dx|$)XX5%zS@-M`, Wx`|4)@@5%zS@-M`- `d|?*AA5%zC@-M`- dx|1XX5%zS@-M`|5 Wx`|1@@5%zS@-M`5 `d|3AA5%zC@-M`6 dx|::XX5%zS@-M`= Wx`|F:@@5%zS@- ``|e:@@5%zC@->M`%> `d|;AA5%zC@-M`U? dx|BXX5%zS@-M`F Wx`|B@@5%zS@-M`F `d|WDAA5%zC@-M`H dx|KXX5%zS@-M`kO Wx`|K@@5%zS@-M`O `d|LAA5%zC@-M`P dx|/TXX5%zS@-M`W W x`|M`'X `d|PUAA5%zC@-M`Y dx|\XX5%zS@-M`]` Wx`|\@@5%zS@-M`` `d|^AA5%zC@-M`a dx|dXX5%zS@-M`h W*x`|d@@5%zS@-M`h `d|afAA5%zC@-M`!j dx|nmXX5%zS@-M`.q W :x`|m@@5%zS@-M`q `d|nAA5%zC@-M`r dx|.vXX5%zS@-M`y W0Jx`|M`Dz `d|wAA5%zC@-M`P{ dx|~XX5%zS@-M` W@Zx`|~@@5%zS@-M` `d|@AA5%zC@-M` dx|eXX5%zS@-M`% WPjx`|u@@5%zS@-M`T `d|AA5%zC@-M` dx|XX5%zS@-M`ד W`zx`|&@@5%zS@-M` `d|qAA5%zC@-M`1 dx|ĘXX5%zS@-M` Wpx`|՘@@5%zS@-M`ۜ `d|(AA5%zC@-M` dx|XX5%zS@-M`T Wx`|@@5%zS@-M` `d|٢AA5%zC@-M` dx|QXX5%zS@-M` Wx`|`@@5%zS@-M`g `d|AA5%zC@-M`V dx|XX5%zS@-M`϶ Wx`| @@5%zS@-M`1 `d|pAA5%zC@-M`0 dx|wXX5%zS@-M`7 Wx`|@@5%zS@-M` `d|ټAA5%zC@-M` dx|9ğXX5%zS@-M` Wx`|Oğ@@5%zS@-M`M `d|şAA5%zC@-M`b dx|̟XX5%zS@-M` Wx`|͟@@5%zS@-M` `d|gΟAA5%zC@-M`' dx|՟XX5%zS@-M` Wx`|՟@@5%zS@-M` `d|PןAA5%zC@-M` dx|ޟXX5%zS@-M`S W x`|ޟ@@5%zS@-M` `d|ߟAA5%zC@-M` dx|W矝XX5%zS@-M` Whmlkjix`|f矝@@5%zS@-M`m `d|蟝AA5%zC@-M`{ dx|XX5%zS@-M` W+x`|%@@5%zS@-M`< `d|pAA5%zC@-M`0 dx|XX5%zS@-M` W ;x`|@@5%zS@-M` `d|'AA5%zC@-M` dx|XX5%zS@-M`` W0Kx`|@@5%zS@-M` `d|AA5%zC@-M` dx|V XX5%zS@-M` W@[x`|b @@5%zS@-M`L `d| AA5%zC@-M`{ dx|XX5%zS@-M` WPkx`|"@@5%zS@-M`  `d|&AA5%zC@-M` dx|XX5%zS@-M` W`{x`|@@5%zS@-M` `d|4AA5%zC@-M` dx|c$XX5%zS@-M`#( Wpx`|p$@@5%zS@-M`Y( `d|%AA5%zC@-M`) dx|-XX5%zS@-M`0 Wx`|,-@@5%zS@-M`1 `d|`.AA5%zC@-M` 2 dx|5XX5%zS@-M`9 Wx`|5@@5%zS@-M`9 `d|7AA5%zC@-M`: dx|>XX5%zS@-M`EB Wx`|>@@5%zS@-@@5%zC@->M`B `d|?AA5%zC@-M`C dx|IGXX5%zS@-M` K Wx`|UG@@5%zS@-M`@K `d|HAA5%zC@-M`UL dx| PXX5%zS@-M`S Wx`|P@@5%zS@-M`T `d|zQAA5%zC@-M`:U dx|XXX5%zS@-M`Z\ Wx`|X@@5%zS@-M`\ `d|YAA5%zC@-M`] dx|UaXX5%zS@-M`e Wx`|aa@@5%zS@-M`Ie `d|bAA5%zC@-M`qf dx|jXX5%zS@-M`m W x`|j@@5%zS@-M`m `d|KkAA5%zC@-M` o dx|zrXX5%zS@-M`:v Wx`|r@@5%zS@-M`ov `d|sAA5%zC@-M`w dx|){XX5%zS@-M`~ W,x`|5{@@5%zS@-M` `d||AA5%zC@-M`G dx|˃XX5%zS@-M` W <x`|׃@@5%zS@-M` `d|ꄠAA5%zC@-M` dx|qXX5%zS@-M`1 W0Lx`|@@5%zS@-M`` `d|AA5%zC@-M`G dx|XX5%zS@-M`ۘ W@\x`|(@@5%zS@-M` `d|AA5%zC@-M`@ dx|XX5%zS@-M`^ WPlx`|@@5%zS@-M` `d|瞠AA5%zC@-M` dx|)XX5%zS@-M` W`|x`|9@@5%zS@-M` `d|JAA5%zC@-M` dx|ԮXX5%zS@-M` Wpx`|箠@@5%zS@-M`IJ `d|AA5%zC@-M` dx|XX5%zS@-M`L Wx`|@@5%zS@-M`~ `d|AA5%zC@-M`g dx|PXX5%zS@-M` Wx`|]@@5%zS@-M`? `d|AA5%zC@-M`Q dx|2ȠXX5%zS@-M` Wx`|DȠ@@5%zS@-M`$ `d|HɠAA5%zC@-M` dx|РXX5%zS@-M`x Wx`|Р@@5%zS@-M` `d|ѠAA5%zC@-M` dx|g٠XXMwS@-M`' Wx`||٠@@@MwS@-M`~ `d|ڠAA@MwC@-M`E dx|᠝XXMwS@-M` Wx`|᠝@@MwS@-M` `d|㠝AAMwC@-M` dx|tꠝXXMwS@-M`4 Wx`|ꠝ@@@MwS@-M`j `d|렝AA@MwC@-M`O dx|XXMwS@-M` W x`|%@@@MwS@-M` `d|@AA@MwC@-M` dx|XXMwS@-M`v Wghmlkjix`|@@MwS@-M` `d|AAMwC@-M` dx|ZXXMwS@-M` W-x`|f@@@MwS@-M`G `d|AA@MwC@-M`H dx| XX5%zS@-M` W =x`| @@5%zS@-M` `d|2AA5%zC@-M` dx|XX5%zS@-M`X W0Mx`|@@5%zS@-M` `d|AA5%zC@-M` dx|<XX5%zS@-M`! W@]x`|I@@5%zS@-M`F" `d|AA5%zC@-M`u# dx|#'XX5%zS@-M`* WPmx`|+'@@5%zS@-M`-+ `d|(AA5%zC@-M`^, dx|0XX5%zS@-M`3 W`}x`|0@@5%zS@-M`4 `d|k1AA5%zC@-M`+5 dx|8XX5%zS@-M`< Wpx`|8@@5%zS@-M`< `d|A:AA5%zC@-M`> dx|AXX5%zS@-M`D Wx`|"A@@5%zS@-M`E `d|BAA5%zC@-M`jF dx|IXX5%zS@-M`M Wx`|I@@5%zS@-M`M `d|GKAA5%zC@-M`O dx|RXX5%zS@-M`@V Wx`|R@@5%zS@-M`hV `d|SAA5%zC@-M`oW dx|[XX5%zS@-M`^ Wx`|[@@5%zS@-M`^ `d|9\AA5%zC@-M`_ dx|cXX5%zS@-M`Tg Wx`|c@@5%zS@-M`g `d|dAA5%zC@-M`h dx|lXX5%zS@-M`o Wx`|(l@@5%zS@-M`?p `d|mAA5%zC@-M`lq dx|tXX5%zS@-M`|x Wx`|t@@5%zS@-M`x `d|@vAA5%zC@-M`z dx|}XX5%zS@-M`d W x`|}@@5%zS@-M` `d|AA5%zC@-M`܂ dx|ȓXX5%zS@-M` Wx`|ѓ@@5%zS@-M`ϗ `d|GAA5%zC@-M` dx|HXX5%zS@-M` W.x`|Q@@5%zS@-M`0 `d|AA5%zC@-M`T dx|5XX5%zS@-M` W >x`|>@@5%zS@-M`E `d|AA5%zC@-M`X dx|XX5%zS@-M`ݱ W0Nx`|%@@5%zS@-M`" `d|AA5%zC@-M`Z dx|XX5%zS@-M`w W@^x`|@@5%zS@-M` `d|?AA5%zC@-M` dx|KXX5%zS@-M` WPnx`|T@@5%zS@-M`R `d|AA5%zC@-M`? dx|!ȡXX5%zS@-M` W`~x`|*ȡ@@5%zS@-M`( `d|lɡAA5%zC@-M`, dx|СXX5%zS@-M` Wpx`|С@@5%zS@-M` `d|ҡAA5%zC@-M` dx|١XX5%zS@-M`Z Wx`|١@@5%zS@-M` `d|ڡAA5%zC@-M` dx|O⡝XX5%zS@-M` Wx`|Z⡝@@5%zS@-M` `d|㡝AA5%zC@-M` dx| 록XX5%zS@-M` Wx`|.록@@5%zS@-M`2 `d|졝AA5%zC@-M`G dx|XX5%zS@-M` Wx`|@@5%zS@-M` `d|)AA5%zC@-M` dx|XX5%zS@-M`C Wx`|@@5%zS@-M` `d|AA5%zC@-M` dx|XX5%zS@-M` Wx`| @@5%zS@-M`( `d|vAA5%zC@-M`6 dx| XX5%zS@-M`a Wx`| @@5%zS@-M` `d|AA5%zC@-M` dx|qXX5%zS@-M`1 Wx`|@@5%zS@-M` `d|AA5%zC@-M` dx|0XX5%zS@-M`" Wrstuvwxyx`|=@@5%zS@-M`=# `d| AA5%zC@-M`S$ dx|'XX5%zS@-M`+ Wz{|}~+x`|'@@5%zS@-M`+ `d|<)AA5%zC@-M`, dx|y0XX5%zS@-M`94 W ?x`|0@@5%zS@-M`4 `d|1AA5%zC@-M`5 dx|89XX5%zS@-M`< W0Ox`|E9@@5%zS@-M`U= `d|:AA5%zC@-M`b> dx|\OXX5%zS@-M`S W@_x`|kO@@5%zS@-M`oS `d|PAA5%zC@-M`T dx|XXX5%zS@-M`[ WPox`|X@@5%zS@-M` \ `d|ZYAA5%zC@-M`] dx|`XX5%zS@-M`d W`x`|`@@5%zS@-M`d `d|bAA5%zC@-M`e dx|iXX5%zS@-M`Gm Wpx`|i@@5%zS@-M`m `d|jAA5%zC@-M`n dx|LrXX5%zS@-M` v Wx`|Zr@@5%zS@-M`\v `d|sAA5%zC@-M`~w dx|zXX5%zS@-M`~ Wx`|z@@5%zS@-M`~ `d||AA5%zC@-M` dx|cXX5%zS@-M`# Wx`|p@@5%zS@-M`s `d|˄AA5%zC@-M` dx|#XX5%zS@-M` Wx`|0@@5%zS@-M`2 `d|AA5%zC@-M`J dx|XX5%zS@-M` Wx`|@@5%zS@-M`? `d|gAA5%zC@-M`' dx|XX5%zS@-M`g Wx`|@@5%zS@-M` `d|AA5%zC@-M` dx|]XX5%zS@-M` Wx`|h@@5%zS@-M`f `d|AA5%zC@-M`k dx|订XX5%zS@-M` Wx`|@@5%zS@-M`ٲ `d|3AA5%zC@-M` dx|rXX5%zS@-M`2 W x`|~@@5%zS@- ``|@@5%zC@->M`{ `d|¸AA5%zC@-M` dx|.XX5%zS@-M` W0x`|:@@5%zS@-M`: `d|AA5%zC@-M`N dx|ȢXX5%zS@-M`] W @x`|Ȣ@@5%zS@-M` `d|ʢAA5%zC@-M` dx|VѢXX5%zS@-M` W0Px`|cѢ@@5%zS@-M`d `d|ҢAA5%zC@-M`y dx|ڢXX5%zS@-M` W@`x`|#ڢ@@5%zS@-M`% `d|tۢAA5%zC@-M`4 dx|⢝XX5%zS@-M`~ WPpx`|⢝@@5%zS@-M` `d|䢝AA5%zC@-M` dx|}뢝XX5%zS@-M`= W`x`|뢝@@5%zS@-M` `d|좝AA5%zC@-M` dx|1XX5%zS@-M` Wpx`|=@@5%zS@-M`; `d|vAA5%zC@-M`6 dx|XX5%zS@-M` Wx`|@@5%zS@-M` `d|YAA5%zC@-M` dx|XX5%zS@-M`w Wx`|@@5%zS@-M` `d|AA5%zC@-M` dx|EXX5%zS@-M` Wx`|Q@@5%zS@-M`R `d|AA5%zC@-M`x dx|XX5%zS@-M` Wx`|@@5%zS@-M` `d|DAA5%zC@-M` dx|XX5%zS@-M`K# Wx`|@@5%zS@-M`# `d| AA5%zC@-M`$ dx|F(XX5%zS@-M`, Wx`|R(@@5%zS@-M`c, `d|)AA5%zC@-M`d- dx|0XX5%zS@-M`4 Wx`|0@@5%zS@-M`4 `d|!2AA5%zC@-M`5 dx|9XX5%zS@-M`R= Wx`|9@@5%zS@-M`= `d|-VAA5%zC@-M`Y dx|[XX5%zS@-M`E_ W rstuvwxyx`|[@@5%zS@-M`_ `d|\AA5%zC@-M`` dx|;dXX5%zS@-M`g W z{|}~-x`|Gd@@5%zS@-M`Fh `d|eAA5%zC@-M`[i dx|lXX5%zS@-M`p W Ax`|l@@5%zS@-M`p `d|M` `d|AAA5%zC@-M` dx|XX5%zS@-M`U W @ax`|@@5%zS@-M` `d| AA5%zC@-M`͐ dx|XX5%zS@-M`ޗ W Pqx`|+@@5%zS@-M`( `d|qAA5%zC@-M`1 dx|՜XX5%zS@-M` W `x`|@@5%zS@-M`ݠ `d|5AA5%zC@-M` dx|XX5%zS@-M`L W px`|@@5%zS@-M` `d|AA5%zC@-M`e dx|.XX@MwS@-M` W x`|@@@MwS@-M`A `d|AAMwC@-M`O dx|XXMwS@-M` W x`|@@@MwS@-M` `d|=AA@MwC@-M` dx|XX@MwS@-M`J W x`|@@MwS@-M` `d|AAMwC@-M` dx|ƣXXMwS@-M` W x`|ƣ@@MwS@-M` `d|PȣAAMwC@-M` dx|MϣXX@2lzS@-M` W x`|Zϣ@@;dzS@-M`\ `d|УAA;dzC@-M`x dx|ףXX;dzS@-M` W x`|ף@@@;dzS@-M` `d|8٣AA@;dzC@-M` dx|ࣝXX;dzS@-M`b W x`|ࣝ@@@;dzS@-M` `d| ⣝AA@;dzC@-M` dx|h飝XX;dzS@-M`( W x`|t飝@@;dzS@-M`w `d|꣝AA;dzC@-M` dx|0XXMwS@-M`W!"x`|A@@@MwS@-M`E`d||AA@MwC@-M`<dx|XXMwS@-M` W!2x`|@@@MwS@-M` `d|z AA@MwC@-M`: dx|XXMwS@-M`SW! Bx`|@@MwS@-M``d|AAMwC@-M`dx|'XXMwS@-M`W!0Rx`|7@@MwS@-M`:`d|wAAMwC@-M`7dx|!XX@MwS@-M`%W!@bx`|"@@@MwS@-M`&`d|X#AA@MwC@-M`'dx|*XXMwS@-M`s.W!Prx`|*@@@MwS@-M`.`d|,AA@MwC@-M`/dx||3XX@MwS@-M`<7W!`x`|3@@MwS@-M`7`d|4AAMwC@-M`8dx|6M`G@`d|=AAMwC@-M`\Adx|DXXMwS@-M`HW!x`|E@@@MwS@-M`I`d|[FAA@MwC@-M`Jdx|MXXMwS@-M`}QW!x`|M@@@MwS@-M`Q`d|"OAA@MwC@-M`Rdx|sVXX>zS@-M`3ZW!x`|V@@>zS@-zC@->M`mZ`d|WAA>zC@-M`[dx|^XX@>zS@-M`bW!x`|^@@@>zS@-zC@->M` c`d|I`AA@>zC@-M` ddx|gXX>zS@-M`gkW!x`|g@@>zS@-zC@->M`k`d|hAA>zC@-M`{ldx|gpXX>zS@-M`'tW!x`|tp@@@>zS@-zC@->M`t`d|rAA@>zC@-M`udx|'yXX@>zS@-M`|W!x`|Ay@@>zS@-zC@->M`}`d|zAA>zC@-M`T~dx|XX>zS@-M`W!x`|@@>zS@-zC@->M``d|=AA>zC@-M`dx|XX@>zS@-M`mWDC3x`|@@@>zS@-zC@->M`ˎ`d|AA@>zC@-M`Odx|BXX>zS@-M`W%x`|O@@>zS@-zC@->M`b`d|AA>zC@-M`Ndx|ƤXX>zS@-M`W 3x`| Ƥ@@>zS@-zC@->M``d|4ǤAA>zC@-M`dx|vΤXX@>zS@-M`6W0Ex`|Τ@@@>zS@-zC@->M`b`d|ϤAA@>zC@-M`dx|UפXX>zS@-M`W@Sx`|vפ@@>zS@-zC@->M``d|4٤AA>zC@-M`dx|ߤXX>zS@-M`WPex`|ߤ@@@>zS@-zC@->M``d|_ᤝAA@>zC@-M`dx|褝XX@>zS@-M`_W`sx`|褝@@>zS@-zC@->M``d|餝AA>zC@-M`dx|XX>zS@-M`Wpx`|@@>zS@-zC@->M``d|AA>zC@-M`Idx|?XX@>zS@-M` W Dx`|Q@@@>zS@-zC@->M`, `d| AA@>zC@-M`Idx|;XX>zS@-M`W Ex`|H@@>zS@-zC@->M`,`d|AA>zC@-M`ndx|XX>zS@-M`W x`|@@>zS@-zC@->M``d| AA>zC@-M`$dx|'XX@>zS@-M`+W05x`|'@@@>zS@-zC@->M` ,`d|-AA@>zC@-M`1dx|5XX>zS@-M`8W0Ex`|)5@@>zS@-zC@->M`69`d|6AA>zC@-M`N:dx|=XX>zS@-M`AW0 Ux`|=@@@>zS@-@@>zC@->M`A`d|6?AA@>zC@-M`dx|FXX@>zS@-M`W00ex`|F@@>zS@-zC@->M`t`d|GAA>zC@-M`v dx|OOXX>zS@-M`W0@x`|[O@@>zS@-zC@->M``d|PAA>zC@-M`dx|XXX@>zS@-M`W0Px`|X@@@>zS@-zC@->M``d|yYAA@>zC@-M`dx|`XX>zS@-M`R"W0`x`|`@@>zS@-zC@->M`"`d|:bAA>zC@-M`#dx|iXX>zS@-M` +W0px`|i@@>zS@-zC@->M`>+`d|jAA>zC@-M`h,dx|NrXX@>zS@-M`3WKx`|[r@@@>zS@-zC@->M`3`d|ݚAA@>zC@-M`]\dx|8XX>zS@-M`dWWx`|E@@>zS@-zC@->M`&e`d|AA>zC@-M`fdx|?XX>zS@-M`zW JK_dBnHx`|K@@@>zS@-zC@->M`{`d|AA@>zC@-M`|dx|XX@>zS@-M`_W09x`|@@>zS@-zC@->M``d|\åAA>zC@-M`܄dx|ʥXX>zS@-M`W@Ix`|ʥ@@>zS@-zC@->M`z`d|˥AA>zC@-M`Xdx|)ӥXX@>zS@-M`WPYx`|<ӥ@@@>zS@-zC@->M`ؔ`d|WԥAA@>zC@-M`וdx|ۥXXvDwS@-M`W`ix`|ۥ@@@{DwS@-M`D`d|ܥAA@{DwC@-M`=dx|㥝XXqDwS@-M`|Wpyx`|䥝@@@~DwS@-M``d|>奝AA@~DwC@-M`dx|_쥝XX>zS@-M`߭Wx`|u쥝@@>zS@-zC@->M``d|AA>zC@-M`!dx|XX>zS@-M`CWBx`|@@>zS@-zC@->M`t`d|AA>zC@-M`dx|XX@xDwS@-M`7WerBrax`|@@tDwS@-M`e`d|AAtDwC@-M`udx|J XXtDwS@-M`W "x`|b @@@xDwS@-M``d|m AA@xDwC@-M`dx|XX@~DwS@-M`W0&*2x`|@@qDwS@-M`G`d|AAqDwC@-M`Ndx| XXqDwS@-M`W@Kx`|@@@{DwS@-M``d|;AA@{DwC@-M`dx|"XX@zS@-M`WP[x`|"@@zS@-M`=`d|#AAzC@-M`?dx|*XXzS@-M``W`kx`|*@@zS@-M``d|,AAzC@-M`dx|.3XXGGxS@-M`Wp{x`|>3@@CGxS@-M` `d|4AACGxC@-M`5dx|;XX@NGxS@-M`WWx`|;@@@KGxS@-M``d|0=AA@KGxC@-M`d`|zD@@@KGxS@-zS@-M`cWdvx`|M@@>zC@->M``d|uAA@KGxC@-M`N7dx|ȆXX>zS@-M`HHWdx`|ن@@>zS@-zC@->M`xH`d|CAA>zC@-M`Idx|XX@>zS@-M`2QWd x`|@@>zS@-zC@->M`Q`d|AA>zC@-M`yRdx|OXX>zS@-M`YWd0x`|Z@@>zS@-zC@->M`7Z`d|͙AA>zC@-M`M[dx|rXX@>zS@-M`nWd@x`|@@>zS@-zC@->M` o`d|AA>zC@-M`?pdx|\XX>zS@-M`wWdrx`|h@@@>zS@-zC@->M` x`d|AA@>zC@-M`~dx|æXX>zS@-M`HWd}x`|æ@@@>zS@-zC@->M``d|2ŦAA@>zC@-M`dx|̦XX>zS@-M`VWd x`|̦@@>zS@-zC@->M``d|ΦAA>zC@-M`dx|զXX>zS@-M`0Wd0x`|զ@@@>zS@-zC@->M`֗`d|]צAA@>zC@-M`ݘdx|xަXX@>zS@-M`Wd@x`|ަ@@>zS@-zC@->M`i`d|ߦAA>zC@-M`jdx|B禝XX>zS@-M`¨WdPx`|U禝@@>zS@-zC@->M``d|覝AA>zC@-M`?dx|劣XX@>zS@-M`_Wd`x`|劣@@>zS@-zC@->M``d|*AA>zC@-M`dx|XX@>zS@-M`&Wdpx`|@@>zS@-zC@->M`V`d|AA>zC@-M`tdx|gXX>zS@-M`Wdx`|w@@@>zS@-zC@->M``d|AA@>zC@-M`Hd`| @@>zS@-zS@-M`oWHello!;x`|@@@>zC@->M``d|;AA>zC@-M`<dx|DXX>zS@-M` W6x`|D@@>zS@-zC@->M`O`d|FAA>zC@-M`dx|-MXX>zS@-M`W Fx`|zS@-zC@->M``d|yNAA@>zC@-M`dx|VXX@>zS@-M`W0Vx`|V@@>zS@-zC@->M``d|[WAA>zC@-M`dx|^XX>zS@-M`E W@fx`|^@@>zS@-zC@->M`{ `d|%`AA>zC@-M`!dx|fgXX>zS@-M`(WPvx`|vg@@@>zS@-zC@->M`)`d|hAA@>zC@-M`]*dx|/pXX@>zS@-M`1W`x`|zS@-zC@->M`2`d|qAA>zC@-M`3dx|xXX>zS@-M`x:Wpx`|y@@@>zS@-zC@->M`:`d|BzAA@>zC@-M`;dx|XX@>zS@-M`8CWx`|ā@@>zS@-zC@->M`kC`d|AA>zC@-M`Ddx|wXX>zS@-M`KWx`|@@>zS@-zC@->M`+L`d|㋧AA>zC@-M`cMdx|DXX>zS@-M`TWx`|U@@@>zS@-zC@->M`U`d|AA@>zC@-M`(Vdx|XX@>zS@-M`y]Wx`|@@>zS@-zC@->M`]`d|^AA>zC@-M`^dx|XX>zS@-M`fWx`|@@>zS@-zC@->M`>f`d|AA>zC@-M`?gdx|XX>zS@-M`nWWelcome!x`|$@@@>zS@-zC@->M`n`d|8AA@>zC@-M`vdx|幧XX@>zS@-M`e{W7x`|񹧝@@>zS@-zC@->M`{`d|VAA>zC@-M`|dx|§XX>zS@-M` W Gx`|§@@@>zS@-zC@->M`u`d|çAA@>zC@-M`gdx|3˧XX@>zS@-M`W0Wx`|>˧@@>zS@-zC@->M`݌`d|`̧AA>zC@-M`dx|ӧXX>zS@-M`_W@gx`|ӧ@@>zS@-zC@->M``d|0էAA>zC@-M`dx|k駝XX>zS@-M`WPwx`|u駝@@@>zS@-zC@->M``d|꧝AA@>zC@-M`'dx|XX@>zS@-M`W`x`|"@@>zS@-zC@->M``d|@AA>zC@-M`dx|XX>zS@-M`>Wpx`|@@>zS@-zC@->M``d|AA>zC@-M`dx|gXX>zS@-M`Wx`|s@@@>zS@-zC@->M``d|AA@>zC@-M`qdx| XX@>zS@-M`<Wx`| @@>zS@-zC@->M``d| AA>zC@-M`dx|8XX>zS@-M`Wx`|C@@@>zS@-zC@->M`!`d|AA@>zC@-M` dx|XX@>zS@-M`gWx`|@@>zS@-zC@->M``d|9AA>zC@-M`dx|s%XX>zS@-M`Wx`|%@@>zS@-zC@->M`L`d|&AA>zC@-M`Ldx|-XX>zS@-M`jWThank you!x`|-@@@>zS@-zC@->M``d|*6AA@>zC@-M`dx|?XXzS@-M`W8x`|?@@zS@-M``d|?@AAzC@-M`dx|GXXzS@-M` W Hx`|G@@zS@-M`7 `d|HAAzC@-M` dx|1PXXzS@-M`W0Xx`|BP@@zS@-M``d|HQAAzC@-M`dx|eXXzS@-M`U'W@hx`|e@@@zS@-M`'`d|gAA@zC@-M`(dx|nXXzS@-M` 0WPxx`|n@@zS@-M`80`d|oAAzC@-M`&1dx|EwXXzS@-M`8W`x`|Xw@@zS@-M`8`d|xxAAzC@-M`9dx|XXzS@-M`AWpx`|*@@@zS@-M`A`d|EAA@zC@-M`Bdx|XX@zS@-M`JWx`|@@zS@-M`HJ`d|ĉAAzC@-M`DKdx|ꐨXX@zS@-M`jRWx`|@@zS@-M`R`d|AAzC@-M`Sdx|XXzS@-M`[Wx`|@@zS@-M`1[`d|AAzC@-M`\dx|>XXzS@-M`cWx`|L@@@zS@-M`c`d|eAA@zC@-M`ddx|XXzS@-M`tlWx`|@@zS@-M`l`d|*AAzC@-M`mdx|XX@zS@-M`uWGood bye!3x`|@@zS@-M`?u`d|AAzC@-M`)}dx|XXzS@-M`W9x`|@@zS@-M`X`d|AAzC@-M`Udx| ɨXXzS@-M`W Ix`|+ɨ@@zS@-M``d|ʨAAzC@-M`dx|ѨXXzS@-M`WW0Yx`|Ѩ@@@zS@-M``d|ӨAA@zC@-M`dx|}ڨXXzS@-M`W@ix`|ڨ@@zS@-M`C`d|ۨAAzC@-M`Ldx| 㨝XX@zS@-M`WPyx`|㨝@@zS@-M`Ԥ`d|f䨝AAzC@-M`dx|먝XXzS@-M`BW`x`|먝@@zS@-M``d|AAzC@-M`dx|kXXzS@-M`Wpx`|v@@@zS@-M`8`d|AA@zC@-M`Tdx|"XXzS@-M`Wx`|,@@zS@-M``d|`AAzC@-M`dx|XXzS@-M`+Wx`|@@zS@-M`T`d|AAzC@-M`tdx|XXzS@-M`Wx`|@@zS@-M``d|:AAzC@-M`dx|XXzS@-M`Wx`|@@zS@-M`M`d|AAzC@-M`Ldx|9XXzS@-M`Wx`|B@@zS@-M``d|~ AAzC@-M`dx|'XXzS@-M`<WHappy every day!x`|'@@zS@-M``d|/AAzC@-M`vdx|4XXMwS@-M`VW:x`|4@@MwS@-M``d|S6AAMwC@-M`dx|=XXMwS@-M`W Jx`|=@@MwS@-M`F`d|>AAMwC@-M`Udx|FXXMwS@-M`W0Zx`|F@@MwS@-M``d|^GAAMwC@-M`dx|NXX@MwS@-M`@W@jx`|N@@MwS@-M``d|PAAMwC@-M`dx|GWXXzS@-M`WPzx`|VW@@zS@-M``d|XAAzC@-M`Kdx|"`XXzS@-M`!W`x`|/`@@zS@-M`!`d|aAAzC@-M`#dx|hXXzS@-M`-*Wpx`|h@@zS@-M`|*`d| jAAzC@-M`+dx|CqXXzS@-M`2Wx`|Tq@@zS@-M`3`d|rAAzC@-M` 4dx| zXXzS@-M`;Wx`|z@@zS@-M`;`d|`{AAzC@-M`<dx|XXzS@-M`DWx`|@@zS@-M`JD`d|󃩝AAzC@-M`sEdx|,XXzS@-M`LWx`|=@@zS@-M`L`d|xAAzC@-M`Mdx|XXzS@-M`zUWx`| @@zS@-M`U`d|JAAzC@-M`Vd`|@@zS@-M`b`d|ʩAAzC@-M`,dx|өXXMwS@-M`WHex`|ө@@MwS@-M`~`d|٩AAMwC@-M`7dx|੝XXMwS@-M`#WHmx`|੝@@MwS@-M`z`d|⩝AAMwC@-M`dx|驝XXMwS@-M`WH }x`|驝@@MwS@-M`ت`d|Y꩝AAMwC@-M`٫dx|yXX@MwS@-M`WLF4Vxx`|@@@MwS@-M`L`d|4AA@MwC@-M`dx|"XX@MwS@-M`WL .x`|,"@@@MwS@-M``d|a#AA@MwC@-M`dx|*XX;dzS@-M`nWL x`|*@@@;dzS@-M``d|1AA@;dzC@-M`dx|8XX;dzS@-M`WL kx`|.8@@;dzS@-M``d|6>AA;dzC@-M`dx|=EXX;dzS@-M`WL {x`|SE@@;dzS@-M``d|FAA;dzC@-M`#dx|NXX@;dzS@-M`WL x`|N@@;dzS@-M``d|wOAA;dzC@-M`dx|VXX;dzS@-M`AWL 0x`|V@@@;dzS@-M``d|*XAA@;dzC@-M`dx|_XX;dzS@-M`!WL @x`|_@@;dzS@-M`[!`d|`AA;dzC@-M`d"dx|FhXX;dzS@-M`)WL Px`|Vh@@;dzS@-M`*`d|iAA;dzC@-M`(+dx| qXX@;dzS@-M`2WL `x`|q@@;dzS@-M`2`d|irAA;dzC@-M`3dx|yXX;dzS@-M`;WL px`|y@@@;dzS@-M`j;`d|zAA@;dzC@-M`^<dx|RXX;dzS@-M`CWL x`|c@@;dzS@-M`+D`d|AA;dzC@-M`6Edx|XX;dzS@-M`ZWL x`|@@;dzS@-M`{Z`d|AA;dzC@-M`[dx|VXX@;dzS@-M`bWL  x`|d@@;dzS@-M`/c`d|AA;dzC@-M`;ddx|!XX;dzS@-M`kWL x`|1@@@;dzS@-M`k`d|AA@;dzC@-M`mdx|߲XX;dzS@-M`_tWL +x`|@@;dzS@-M`t`d|uAA;dzC@-M`udx|XX;dzS@-M`}WL ;x`|@@;dzS@-M`q}`d|AA;dzC@-M`~dx|^ĪXX@;dzS@-M`ޅWL Kx`|mĪ@@;dzS@-M`2`d|ŪAA;dzC@-M`&dx|ͪXX;dzS@-M`WL [x`|-ͪ@@@;dzS@-M`͎`d|ΪAA@;dzC@-M`dx|ժXX;dzS@-M`fWLlx`|ժ@@;dzS@-M``d|JתAA;dzC@-M`ʘdx|ުXX;dzS@-M`*WL|x`|ު@@;dzS@-M`~`d|ߪAA;dzC@-M`}dx|]窝XX@;dzS@-M`ݨWL x`|r窝@@;dzS@-M`7`d|誝AA;dzC@-M`(dx|瞧XX;dzS@-M`cWL0x`|瞧@@@;dzS@-M``d|>AA@;dzC@-M`dx|XX;dzS@-M`QWL@x`|@@;dzS@-M``d|1AA;dzC@-M`dx|`XX;dzS@-M`WLPx`|m@@;dzS@-M`8`d|AA;dzC@-M`Zdx|T XX@;dzS@-M`WL`x`|f @@;dzS@-M`'`d| AA;dzC@-M`Adx|XX;dzS@-M`WWLpx`|@@@;dzS@-M``d|'AA@;dzC@-M`dx|XX;dzS@-M` WLx`|@@;dzS@-M`^`d|AA;dzC@-M`Mdx|A#XX;dzS@-M`WLx`|M#@@;dzS@-M``d|$AA;dzC@-M`dx|+XX@;dzS@-M`yWL x`|,@@;dzS@-M``d|-AA;dzC@-M`dx|4XX;dzS@-M`SWLx`|4@@@;dzS@-M``d|;6AA@;dzC@-M`dx|=XX;dzS@-M` WL,x`|=@@;dzS@-M```d|>AA;dzC@-M`mdx|OFXX;dzS@-M`WL<x`|\F@@;dzS@-M`#`d|GAA;dzC@-M`; dx|NXX@;dzS@-M`PWLLx`|N@@;dzS@-M``d| PAA;dzC@-M`dx|WXX;dzS@-M`!WL\x`|W@@@;dzS@-M`n`d|YAA@;dzC@-M`dx|$`XX;dzS@-M`!WL]h)P-16;M`!`d|kAA;dzC@-M`-dx|*pXX;dzS@-M`1WL NU`m%"eT$x`|7p@@;dzS@-M`1`d|rqAA;dzC@-M`2dx|xXX@;dzS@-M`\:WL Fdtx`|x@@;dzS@-M`:`d|=zAA;dzC@-M`;dx|XX;dzS@-M`CWL0 223x`|@@@;dzS@-M`fC`d|݂AA@;dzC@-M`]Ddx|[XX;dzS@-M`KWL@Fx`|g@@;dzS@-M`'L`d|܋AA;dzC@-M`\Mdx|󒫝XX;dzS@-M`sTWLPx`|@@;dzS@-M`T`d|XAA;dzC@-M`Udx|XX@;dzS@-M`4]WL`Fx`|@@;dzS@-M`_]`d|䜫AA;dzC@-M`d^dx|\XX;dzS@-M`eWLpx`|h@@@;dzS@-M`'f`d|AA@;dzC@-M`9gdx|XX;dzS@-M`nWL 2dx`|-@@;dzS@-M`n`d|AA;dzC@-M` pdx|۵XX;dzS@-M`[wWLx`|赫@@;dzS@-M`w`d|2AA;dzC@-M`xdx|XX@;dzS@-M` WLx`|@@;dzS@-M`U`d|AA;dzC@-M`ndx|UǫXX;dzS@-M`ՈWLx`|eǫ@@@;dzS@-M``d|ȫAA@;dzC@-M`dx| ЫXX;dzS@-M`WLx`|Ы@@;dzS@-M`ّ`d|ѫAA;dzC@-M`dx|ثXX;dzS@-M`IWL A$qx`|ث@@;dzS@-M``d|૝AA;dzC@-M`kdx| ꫝXX@;dzS@-M`WLx`|ꫝ@@;dzS@-M``d|﫝AA;dzC@-M`Gdx|DXX;dzS@-M`ĸWL dx`|R@@@;dzS@-M``d|AA@;dzC@-M`&dx| XX;dzS@-M`WLpx`|@@;dzS@-M``d|9AA;dzC@-M`dx| XX;dzS@-M`WLx`|/ @@;dzS@-M``d|9AA;dzC@-M`dx|XX@;dzS@-M`1WL x`|@@;dzS@-M`i`d|AA;dzC@-M`dx|pXX;dzS@-M`WL0x`|~@@@;dzS@-M``d|Q AA@;dzC@-M`dx|8'XX;dzS@-M`WL@x`|F'@@;dzS@-M``d|)AA;dzC@-M`dx|/XX;dzS@-M`jWLPx`|/@@;dzS@-M``d|51AA;dzC@-M`dx|z8XX@;dzS@-M`WL < <x`|8@@;dzS@-M`'`d|s?AA;dzC@-M`dx|GDXX;dzS@-M`WL x`|XD@@@;dzS@-M``d|EAA@;dzC@-M`dx| MXX;dzS@-M`WL@x`|M@@;dzS@-M``d|1SAA;dzC@-M`dx|YXX;dzS@-M`}WLPx`| Z@@;dzS@-M``d|P[AA;dzC@-M`dx|bXX@;dzS@-M`<$WL`4Vxx`|b@@;dzS@-M`u$`d|dAA;dzC@-M`%dx|kXX;dzS@-M`-WLcx`|k@@@;dzS@-M`8-`d|rAA@;dzC@-M`3dx|ZxXX;dzS@-M`9WLx`|gx@@;dzS@-M`:`d|yAA;dzC@-M`W;dx|XX;dzS@-M`BWL x`|"@@;dzS@-M`B`d|WAA;dzC@-M`Cdx|݉XX@;dzS@-M`]KWL0x`|ꉬ@@;dzS@-M`K`d|'AA;dzC@-M`Ldx|XX;dzS@-M` TWL@x`|@@@;dzS@-M`\T`d|擬AA@;dzC@-M`fUdx|_XX;dzS@-M`\WLPx`|l@@;dzS@-M`']`d|AA;dzC@-M`3^dx|XX;dzS@-M`eWL`x`|(@@;dzS@-M`e`d|AA;dzC@-M`gdx|άXX@;dzS@-M`NnWLpx`|ڬ@@;dzS@-M`n`d|)AA;dzC@-M`odx|XX;dzS@-M` wWLx`|@@@;dzS@-M`Aw`d|AA@;dzC@-M`xdx|AXX;dzS@-M`WLx`|M@@;dzS@-M``d|AA;dzC@-M` dx|ƬXX;dzS@-M`;WLx`|Ƭ@@;dzS@-M`h`d|,ȬAA;dzC@-M`dx|zϬXX@;dzS@-M`WL#x`|Ϭ@@;dzS@-M`2`d|ЬAA;dzC@-M`\dx|;جXX;dzS@-M`WL3x`|Gج@@@;dzS@-M``d|٬AA@;dzC@-M`#dx|ଝXX;dzS@-M`zWLCx`|ᬝ@@;dzS@-M``d|<⬝AA;dzC@-M`dx|鬝XX;dzS@-M`6WLSx`|鬝@@;dzS@-M`n`d|꬝AA;dzC@-M`rdx|XX@;dzS@-M`WLcx`| @@;dzS@-M`˳`d|AA;dzC@-M`dx|hXX;dzS@-M`WLtx`|u@@@;dzS@-M``d|AA@;dzC@-M`/dx|&XX;dzS@-M`WLx`|2@@;dzS@-M``d|qAA;dzC@-M`dx| XX;dzS@-M`hWL x`| @@;dzS@-M``d|( AA;dzC@-M`dx|XX@;dzS@-M`-WL0x`|@@;dzS@-M``d|AA;dzC@-M`dx|sXX;dzS@-M`WL@x`|@@@;dzS@-M`$`d|AA@;dzC@-M`9dx|&XX;dzS@-M`WLPx`|&@@;dzS@-M``d|O'AA;dzC@-M`dx|.XX;dzS@-M`?WL`x`|.@@;dzS@-M`t`d|*0AA;dzC@-M`dx|EXX@;dzS@-M`WLpx`|E@@;dzS@-M`@`d|FAA;dzC@-M`jdx|0NXX;dzS@-M`WLx`|?N@@@;dzS@-M``d|OAA@;dzC@-M`dx|VXX;dzS@-M`qWLx`|V@@;dzS@-M``d|KXAA;dzC@-M`dx|_XX;dzS@-M`+!WLx`|_@@;dzS@-M`c!`d|`AA;dzC@-M`~"dx|bhXX@;dzS@-M`)WL$x`|nh@@;dzS@-M`*`d|iAA;dzC@-M`*+dx|qXX;dzS@-M`2WL4x`|'q@@@;dzS@-M`2`d|lrAA@;dzC@-M`3dx|yXX;dzS@-M`V;WLDx`|y@@;dzS@-M`;`d|*{AA;dzC@-M`<dx|IXX;dzS@-M`CWLTx`|W@@;dzS@-M`D`d|ăAA;dzC@-M`DEdx|XX@;dzS@-M`LWLgx`| @@;dzS@-M`L`d|JAA;dzC@-M`Mdx|“XX;dzS@-M`BUWLx`|Γ@@@;dzS@-M`U`d|)AA@;dzC@-M`Vdx|NXX;dzS@-M`]WL Mx`|Z@@;dzS@-M`^`d|AA;dzC@-M`!_dx| XX;dzS@-M`fWL x`|@@;dzS@-M`f`d|iAA;dzC@-M`gdx|ƭXX@;dzS@-M`FoWL0 mx`|ҭ@@;dzS@-M`o`d|AA;dzC@-M`pdx|íXX;dzS@-M`+WLvx`|í@@@;dzS@-M`u`d|ЭAA@;dzC@-M`dx|ԭXX;dzS@-M`&WLx`|ԭ@@;dzS@-M`r`d|խAA;dzC@-M`|dx|_ݭXX;dzS@-M`ߞWL x`|jݭ@@;dzS@-M`8`d|ޭAA;dzC@-M`?dx|歝XX@;dzS@-M`WL0x`|!歝@@;dzS@-M``d|M筝AA;dzC@-M`ͨdx|XX;dzS@-M`QWL@x`|@@@;dzS@-M``d|"AA@;dzC@-M`dx|hXX;dzS@-M`WLPx`|u@@;dzS@-M``d|AA;dzC@-M`;dx|XX;dzS@-M`WL`x`|"@@;dzS@-M``d|yAA;dzC@-M`dx|XX@;dzS@-M`JWLpx`|@@;dzS@-M``d| AA;dzC@-M`gdx|XX;dzS@-M`WLx`|@@@;dzS@-M`/`d|AA@;dzC@-M`dx| XX;dzS@-M`WLx`|@@;dzS@-M``d|AA;dzC@-M`dx|"XX;dzS@-M`:WLx`|"@@;dzS@-M``d|$AA;dzC@-M`dx|+XX@;dzS@-M`WL&x`|+@@;dzS@-M`n`d|,AA;dzC@-M`~dx|d4XX;dzS@-M`WL6x`|q4@@@;dzS@-M`3`d|5AA@;dzC@-M`Idx|$=XX;dzS@-M`WLFx`|1=@@;dzS@-M``d|{>AA;dzC@-M`dx|EXX;dzS@-M`- WLVx`|E@@;dzS@-M`{ `d|GAA;dzC@-M` dx|`NXX@;dzS@-M` WLfx`|nN@@;dzS@-M`- `d|OAA;dzC@-M`J dx|$WXX;dzS@-M` WLwx`|1W@@@;dzS@-M` `d|XAA@;dzC@-M` dx|_XX;dzS@-M`S! WLx`|_@@;dzS@-M`! `d|aAA;dzC@-M`" dx|#hXX;dzS@-M`) WL x`|3h@@;dzS@-M`) `d|kiAA;dzC@-M`* dx|?XXzS@-M`@ WL0x`|R@@zS@-M`@ `d|tAAzC@-M`A dx|؇XXzS@-M`XI WL@x`|燮@@zS@-M`I `d|^AAzC@-M`J dx|eXX@zS@-M`Q WLPx`|s@@zS@-M`R `d|ÑAAzC@-M`CS dx| XXzS@-M`Z WL`x`|@@@zS@-M`Z `d|vAA@zC@-M`[ dx|ҡXXzS@-M`Rc WLpx`|⡮@@zS@-M`c `d|袮AAzC@-M`hd dx|~XX@;dzS@-M`k WLx`|@@;dzS@-M`.l `d|AA;dzC@-M`m dx|9XX;dzS@-M`t WLx`|F@@@;dzS@-M`u `d|AA@;dzC@-M`'v dx|ǻXX;dzS@-M`G} WLx`|Ի@@;dzS@-M`}} `d|AA;dzC@-M`~ d`|Į@@;dzS@-M`L `d|箝AA;dzC@-M`J d`|ﮝ@@@>zS@-M` `d| AA@>zC@-M`6 dx|#XXzS@-M` WLGx`|0@@zS@-M` `d|AAzC@-M`( dx|XXzS@-M`Z WLWx`|@@zS@-M` `d|&AAzC@-M` dx|"XXzS@-M` WLgx`|"@@zS@-M`V `d|#AAzC@-M`f dx|L+XX@zS@-M` WLxx`|Y+@@zS@-M` `d|,AAzC@-M` dx|3XXzS@-M`h WLx`|3@@@zS@-M` `d|P5AA@zC@-M` dx|aM`1 `d|=AA@zC@-M`J dx|EXXzS@-M` WL0x`|)E@@zS@-M` `d|FAAzC@-M` dx|MXX@zS@-M`^ WL@x`|M@@zS@-M` `d|M` `d|XAAzC@-M` dx|m_XXzS@-M` WL`x`|}_@@zS@-M`M`) `d|iAAzC@-M`+ dx|pXXzS@-M`J2 WLx`|p@@zS@-M`2 `d|rAAzC@-M`3 dx|yXX@zS@-M`; WLx`|y@@zS@-M`V; `d| {AAzC@-M`< dx|MXXzS@-M`C WLx`|Y@@@zS@-M`D `d|AA@zC@-M` E dx|XXzS@-M`{L WL(x`|@@zS@-M`L `d|[AAzC@-M`M dx|XXzS@-M`9U WL8x`|Ǔ@@zS@-M`U `d|AAzC@-M`V dx|XXzS@-M`] WLHx`|@@zS@-M`M^ `d|ѝAAzC@-M`Q_ dx|XXzS@-M`f WLXx`|,@@zS@-M`f `d|AAzC@-M`h dx|"XXzS@-M`z WLhx`|0@@@zS@-M`{ `d|AA@zC@-M` | dx|XXzS@-M`X WLyx`|@@zS@-M` `d|<ïAAzC@-M` dx|bʯXX@zS@-M` WLx`|oʯ@@zS@-M`0 `d|˯AAzC@-M` dx|!ӯXXzS@-M` WL x`|/ӯ@@@zS@-M`̔ `d|HԯAA@zC@-M`ȕ dx|ۯXXzS@-M`1 WL0x`|ۯ@@zS@- ``|ۯ@@zC@->M`Y `d|ܯAAzC@-M`d dx|:䯝XX@zS@-M` WL@x`|R䯝@@zS@-M` `d|a寝AAzC@-M` dx|쯝XXzS@-M`N WLPx`|쯝@@zS@-M`| `d|AAzC@-M` dx|JXX>zS@-M`ʶ WL`x`|`@@>zS@-zC@->M` `d|eAA>zC@-M` dx|XX>zS@-M`- WLpx`|@@>zS@-zC@->M`Y `d|AA>zC@-M`D dx|XX@>zS@-M` WLx`|@@@>zS@-zC@->M` `d|xAA@>zC@-M` dx|tXX>zS@-M` WL x`|@@@>zS@-zC@->M`! `d|AA@>zC@-M`D dx|XX@>zS@-M`S WLx`|@@>zS@-zC@->M` `d|AA>zC@-M`x dx|KXX>zS@-M` WL)x`|`@@>zS@-zC@->M` `d|w AA>zC@-M` dx|'XX>zS@-M`C WL9x`|'@@@>zS@-zC@->M`y `d|(AA@>zC@-M`t dx|20XX@>zS@-M` WLIx`|M0@@>zS@-zC@->M` `d|z1AA>zC@-M` dx|8XX>zS@-M` WLYx`|8@@>zS@-zC@->M`5 `d|9AA>zC@-M` dx|@XX>zS@-M`c WLix`|@@@@>zS@-zC@->M` `d|BAA@>zC@-M` dx|4IXX@>zS@-M` WLzx`|BI@@>zS@-zC@->M` `d|wJAA>zC@-M` dx|~QXX>zS@-M` WLx`|Q@@@>zS@-zC@->M`D `d|RAA@>zC@-M`2 dx|YXX@>zS@-M`R WL x`|Y@@>zS@-zC@->M`| `d| [AA>zC@-M` dx|[bXX>zS@-M`# WL0x`|nb@@>zS@-zC@->M`2$ `d|cAA>zC@-M`1% dx|1kXX>zS@-M`, WL@x`|;k@@@>zS@-zC@->M`, `d|ilAA@>zC@-M`- dx|etXX@>zS@-M`5 WLPx`|lt@@>zS@-zC@->M`(6 `d|vAA>zC@-M`7 dx|j}XX>zS@-M`> WL`x`|q}@@>zS@- ``|}@@>zC@->M`1? `d|~AA>zC@-M`v@ dx|EXX>zS@-M`G WLpx`|L@@@>zS@-zC@->M`H `d|AA@>zC@-M`=I dx|QXX@>zS@-M`P WLx`|Y@@>zS@-zC@->M`Q `d|ېAA>zC@-M`[R dx|XX>zS@-M`Y WL x`|@@@>zS@-zC@->M`Y `d|`AA@>zC@-M`Z dx|젰XX@>zS@-M`lb WLx`|@@>zS@-zC@->M`b `d|AA>zC@-M`c dx|ѩXX>zS@-M`Qk WL*x`|ة@@>zS@-zC@->M`k `d|TAA>zC@-M`l dx|XX>zS@-M`t WL:x`|@@@>zS@-zC@->M`]t `d|AA@>zC@-M`u dx|XX@>zS@-M`} WLJx`|@@>zS@-zC@->M`]} `d|鼰AA>zC@-M`i~ dx|İXX>zS@-M` WLZx`|İ@@>zS@-zC@->M`_ `d| ưAA>zC@-M` dx|ͰXXzS@-M` WLjx`|Ͱ@@zS@-M`' `d| ϰAAzC@-M` dx|ְXXzS@-M` WL ` x`|ְ@@zS@-M`p `d|객AAzC@-M` dx|HﰝXXzS@-M`Ȱ WL x`|Rﰝ@@zS@-M` `d|AAzC@-M` dx|XX>zS@-M` WL x`|@@@>zS@-zC@->M`߹ `d|tAA@>zC@-M` dx| XX@>zS@-M` WL 0x`|@@>zS@-zC@->M` `d|oAA>zC@-M` dx| XX>zS@-M`7 WL @x`| @@@>zS@-zC@->M` `d| AA@>zC@-M` dx|qXX@>zS@-M` WL Px`||@@@>zS@-zC@->M`J `d|AA@>zC@-M`5 dx|<XX@>zS@-M` WL `x`|I@@@>zS@-zC@->M` `d|hAA@>zC@-M` dx|#XX@>zS@-M`F WL px`|#@@@>zS@-zC@->M` `d|N%AA@>zC@-M` dx|a,XX@>zS@-M` WL x`|r,@@@>zS@-zC@->M` `d|-AA@>zC@-M`D dx|(5XX@>zS@-M` WL x`|65@@@>zS@-zC@->M`y `d|6AA@>zC@-M`{ dx|=XX@>zS@-M`n WL x`|=@@@>zS@-@@@>zC@->M` `d|F?AA@>zC@-M` dx|FXX@>zS@-M`4 WL .x`|F@@@>zS@-zC@->M`i `d|&HAA@>zC@-M` dx|kOXX@>zS@-M` WL >x`|zO@@@>zS@-zC@->M`Y `d|PAA@>zC@-M`@ dx|,XXX@>zS@-M` WL Nx`|:X@@@>zS@-zC@->M` `d|YAA@>zC@-M`  dx|`XX@>zS@-M`i" WL ^x`|`@@@>zS@-zC@->M`" `d|KbAA@>zC@-M`# dx|iXX@zS@-M`%+ WL nx`|i@@zS@-M`T+ `d|kAAzC@-M`, dx|rrXX@>zS@-M`3 WL!x`|r@@@>zS@-zC@->M`*4 `d|sAA@>zC@-M`p5 dx|zXX>zS@-M`v< WL!x`|{@@>zS@-zC@->M`< `d|O|AA>zC@-M`= dx|XX>zS@-M`6E WL! x`|ȃ@@>zS@-zC@->M`eE `d|AA>zC@-M`F dx|hXX@>zS@-M`M WL!0x`|y@@>zS@-zC@->M`N `d|AA>zC@-M`/O dx|4XX>zS@-M`V WL!@x`|B@@>zS@-zC@->M`V `d|AA>zC@-M`+X dx|!XX>zS@-M`l WL!Px`|1@@@>zS@-zC@->M`l `d|wAA@>zC@-M`m dx|XX@>zS@-M`5u WL!`x`|ó@@>zS@-zC@->M`fu `d| AA>zC@-M`v dx|XX>zS@-M`~ WL!px`|@@@>zS@-zC@->M`7~ `d|ؽAA@>zC@-M`X dx|@űXX@>zS@-M` WL!x`|Mű@@>zS@-zC@->M` `d|ƱAA>zC@-M`$ dx|ͱXX>zS@-M`P WL!x`|ͱ@@>zS@-zC@->M` `d| ϱAA>zC@-M` dx|ֱXX>zS@-M`5 WL!x`|ֱ@@@>zS@-zC@->M`a `d|رAA@>zC@-M` dx|{߱XX@>zS@-M` WL!/x`|߱@@>zS@-zC@->M`, `d|ౝAA>zC@-M`e dx|0豝XX>zS@-M` WL!?x`|?豝@@>zS@-zC@->M` `d|n鱝AA>zC@-M` dx|XX>zS@-M`s WL!Ox`|@@@>zS@-zC@->M` `d|WAA@>zC@-M`׳ dx|XX@>zS@-M`D WL!_x`|@@>zS@-zC@->M`y `d|AA>zC@-M` dx|XX>zS@-M` WL!ox`|@@@>zS@-zC@->M`7 `d|AA@>zC@-M` dx| XX@>zS@-M` WL"x`| @@>zS@-zC@->M` `d|E AA>zC@-M` dx|XX>zS@-M` WL"x`|@@>zS@-zC@->M`M `d|AA>zC@-M`d dx|\XX>zS@-M` WL" x`|i@@@>zS@-zC@->M` `d|AA@>zC@-M`H dx|%XX@>zS@-M` WL"0x`|$%@@>zS@-zC@->M` `d|&AA>zC@-M`" dx|-XX>zS@-M`O WL"@x`|-@@>zS@-zC@->M` `d|"/AA>zC@-M` dx|6XX>zS@-M` WL"Px`|6@@@>zS@-zC@->M`] `d|7AA@>zC@-M`J dx|A?XX@>zS@-M` WL"`x`|O?@@>zS@-zC@->M` `d|@AA>zC@-M` dx|HXX>zS@-M` WL"px`|H@@@>zS@-zC@->M` `d|cIAA@>zC@-M` dx|PXX@>zS@-M`# WL"x`|P@@>zS@-zC@->M`O `d|+RAA>zC@-M` dx|XXX>zS@-M` WL"x`| Y@@>zS@-zC@->M` `d||ZAA>zC@-M` dx|aXX>zS@-M`;# WL" x`|a@@@>zS@-zC@->M`# `d|cAA@>zC@-M`$ dx|xjXX@>zS@-M`+ WL"0x`|j@@>zS@-zC@->M`O, `d|kAA>zC@-M`Z- dx|sXX>zS@-M`4 WL"@x`|s@@>zS@-zC@->M`4 `d|[tAA>zC@-M`5 dx|{XX>zS@-M`= WL"Px`|{@@@>zS@-zC@->M`p= `d| }AA@>zC@-M`> dx|XX@>zS@-M`E WL"`x`|$@@>zS@-zC@->M`E `d|AA>zC@-M`G dx|׌XX>zS@-M`WN WL"px`|㌲@@@>zS@-zC@->M`N `d|1AA@>zC@-M`O dx|XX@>zS@-M` W WL#x`|@@>zS@-zC@->M`@W `d|얲AA>zC@-M`lX d`|O@@@zS@-M`Dd `d|磲AA@zC@-M`ge dx| XX@zS@-M`l WL# x`|1@@zS@-M`l `d|AAzC@-M`m dx|سXXzS@-M`Xu WL#0x`|泲@@@zS@-M`u `d|:AA@zC@-M`v dx|XX@zS@-M`!~ WL#@x`|@@zS@-M`p~ `d|イAAzC@-M`o dx|aŲXXzS@-M` WL#Px`|nŲ@@@zS@-M`2 `d|ƲAA@zC@-M`G dx|ͲXX@zS@-M`v WL#`x`|β@@zS@-M`ď `d|cϲAAzC@-M` dx|~ֲXXzS@-M` WL#px`|ֲ@@@zS@-M`L `d|ײAA@zC@-M`] dx|@߲XX@zS@-M` WL#x`|M߲@@zS@-M` `d|ಝAAzC@-M` dx|粝XXzS@-M`\ WL#x`|粝@@@zS@-M` `d|鲝AA@zC@-M` dx|XX@zS@-M` WL#!x`|@@zS@-M`b `d|AAzC@-M` dx|}XXzS@-M` WL#1x`|@@@zS@-M`L `d|AA@zC@-M`U dx|XX@zS@-M` WL#Ax`|@@zS@-M`b `d|AAzC@-M`^ dx|A XXzS@-M` WL#Qx`|O @@@zS@-M` `d| AA@zC@-M`' dx|!XX@zS@-M`R WL#ax`|!@@zS@-M` `d|.#AAzC@-M` dx|7*XXzS@-M` WL$ A$x`|G*@@@zS@-M` `d|9;AA@zC@-M` dx|@XX>zS@-M`WL$x`|3@@@>zS@-zC@->M``d|AAA>zC@-M`dx|HXX>zS@-M`L WL$ x`|H@@@>zS@-zC@->M` `d|JAA@>zC@-M` dx|QXX@>zS@-M`WL&x`|Q@@>zS@-zC@->M`F`d|XAA>zC@-M`dx|G^XX@zS@-M`WL@x`|U^@@zS@-M` `d|hdAAzC@-M`%dx|skXXzS@-M`,WL@x`|k@@@zS@-M`E-`d|lAA@zC@-M`U.dx|8tXX@zS@-M`5WL@ x`|Ht@@zS@-M` 6`d|uAAzC@-M`7dx|}XXzS@-M`>WL@0x`|;}@@@zS@-``|G}@@zC@->M`>`d|~AA@zC@-M`@dx|ŅXX@zS@-M`EGWL@@x`|҅@@zS@-M`G`d|+AAzC@-M`Hdx|XXzS@-M`PWL@Px`|@@@zS@-M`NP`d|͏AA@zC@-M`MQdx|9XX@zS@-M`XWL@`x`|E@@zS@-M`Y`d|AAzC@-M`Zdx|XXzS@-M`yaWL@px`|@@@zS@-M`a`d|AA@zC@-M` cdx|XX@zS@-M` jWL@x`|@@zS@-M`[j`d|AAzC@-M`|kdx|XXzS@-M`rWL@.x`|&@@@zS@-M`r`d|AA@zC@-M`tdx|ϹXX@zS@-M`O{WL@>x`|۹@@zS@-M`{`d|AAzC@-M`|dx|³XXzS@-M`WL@Nx`|³@@@zS@-M`S`d|óAA@zC@-M`gdx|B˳XX@zS@-M`ŒWL@^x`|S˳@@zS@-M``d|̳AAzC@-M`,dx|WӳXXzS@-M`הWL@nx`|bӳ@@@zS@-M`!`d|ԳAA@zC@-M`Gdx|۳XX@zS@-M`,WL@~x`|۳@@zS@-M``d|:ݳAAzC@-M`dx|0䳝XXzS@-M`WL@x`|<䳝@@@zS@-M``d|峝AA@zC@-M`dx|쳝XX@zS@-M`2WLAx`|쳝@@zS@-M```d|AAzC@-M`{dx|sXXzS@-M`WLAx`|@@@zS@-M`?`d|AA@zC@-M`&dx|/XX@zS@-M`WLA x`|;@@zS@-M``d|AAzC@-M`dx|XXzS@-M`oWLA0x`|@@@zS@-M``d|;AA@zC@-M`dx|XX@zS@-M`WLA@x`|@@zS@-M`j`d|AAzC@-M`dx|XXzS@-M`WLAPx`|'@@@zS@-M``d|.AA@zC@-M`dx| XX@zS@-M`WLA`x`| @@zS@-M`f`d|!AAzC@-M`pdx|Z)XXzS@-M`WLApx`|f)@@@zS@-M`$`d|*AA@zC@-M`Odx|2XX@zS@-M`WLAx`|'2@@zS@-M``d|}3AAzC@-M`dx|:XXzS@-M`KWLA/x`|:@@@zS@-M``d|-M`F`d|DAAzC@-M`Edx|LXXzS@-M` WLAOx`| L@@@zS@-M` `d|cMAA@zC@-M`dx|TXX@zS@-M`8WLA_x`|T@@zS@-M``d|VAAzC@-M`dx|A]XXzS@-M`WLAox`|L]@@@zS@-M``d|^AA@zC@-M` dx|eXX@zS@-M`J'WLAx`|e@@zS@-M`'`d|gAAzC@-M`(dx|nXXzS@-M`0WLAx`|n@@@zS@-M`J0`d|oAA@zC@-M`e1dx|;wXX@zS@-M`8WLBx`|Iw@@zS@-M`8`d|xAAzC@-M`':dx|XXzS@-M`qAWLBx`|@@@zS@-M`A`d|QAA@zC@-M`dx|XX@zS@-M`WLB x`|@@zS@-M``d|ቴAAzC@-M`! dx|@XXzS@-M`WLB0x`|K@@@zS@-M``d|AA@zC@-M`dx|řXX@zS@-M`WLB@x`|֙@@zS@-M`B`d|'AAzC@-M`gdx||XXzS@-M`!WLBPx`|@@@zS@-M`"`d|AA@zC@-M`>#dx|XX@zS@-M`A*WLB`x`| @@zS@-M`*`d|bAAzC@-M`+dx|XXzS@-M`3WLBpx`|˳@@@zS@-M`I3`d|AA@zC@-M`G4dx|lXX@zS@-M`;WLB x`|w@@zS@-M`;`d|̽AAzC@-M` =dx|ŴXXzS@-M`_DWLB0x`|,Ŵ@@@zS@-M`D`d|wƴAA@zC@-M`Edx|ʹXX@zS@-M` MWLB@x`|ʹ@@zS@-M`TM`d|,ϴAAzC@-M`lNd`|Rִ@@zS@-M`Y`d|۴AAzC@-M`[dx|㴝XXzS@-M`YbWLB`x`|%㴝@@@zS@-M`b`d|䴝AA@zC@-M`cdx|a봝XX@zS@-M`jWLBpx`|o봝@@zS@-M`j`d|총AAzC@-M`kdx|XXzS@-M`sWLBx`|@@@zS@-M`s`d|]AA@zC@-M`tdx|sXX@zS@-M`{WLBx`|~@@zS@-M`|`d|AAzC@-M`}dx|XXzS@-M`)WLCx`|@@@zS@-M`w`d|JAA@zC@-M`dx| XX@zS@-M`WLCx`| @@zS@-M`B`d|AAzC@-M`:dx|LXXzS@-M`WLC x`|X@@@zS@-M`ە`d|AA@zC@-M`dx|XX@zS@-M`AWLC0x`| @@zS@-M``d|Q AAzC@-M`dx|'XXzS@-M`WLC@x`|'@@@zS@-M`l`d|=)AA@zC@-M`}dx|w0XX@zS@-M`WLCPx`|0@@zS@-M``d|1AAzC@-M`dx|8XXzS@-M`WLC`x`|8@@@zS@-M`R`d|:AA@zC@-M`Qdx|)AXX@zS@-M`iWLCpx`|3A@@zS@-M``d|gBAAzC@-M`dx|IXXzS@-M`WLC!x`|I@@@zS@-M`A`d|JAA@zC@-M`6dx|RRXX@zS@-M`WLC1x`|_R@@zS@-M``d|SAAzC@-M`dx|ZXXzS@-M`.WLCAx`|Z@@@zS@-M`{`d|\AA@zC@-M`Ndx|cXX@zS@-M`WLCQx`|c@@zS@-M`*`d|dAAzC@-M`;dx|^lXXzS@-M`WLCax`|kl@@@zS@-M``d|mAA@zC@-M`dx|,uXX@zS@-M`lWLCqx`|:u@@zS@-M``d|vAAzC@-M`dx|}XXzS@-M`+WLCx`|}@@@zS@-M`y`d|EAA@zC@-M`dx|vXX@zS@-M`WLCx`|@@zS@-M``d|AAzC@-M`dx|9XXzS@-M`yWLDx`|F@@@zS@-M``d|AA@zC@-M`dx|󗵝XX@zS@-M`3WLDx`|@@zS@-M``d|OAAzC@-M`dx|KXXzS@-M`WLD x`|X@@@zS@-M``d|AA@zC@-M` dx|XX@zS@-M`A(WLD0x`|@@zS@-M`(`d|fAAzC@-M`)dx|XXzS@-M`0WLD@x`|@@@zS@-M`71`d|AA@zC@-M`O2dx|nXX@zS@-M`9WLDPx`|y@@zS@-M`9`d|ϻAAzC@-M`;dx|@õXXzS@-M`BWLD`x`|Kõ@@@zS@-M`B`d|ĵAA@zC@-M`Cdx|˵XX@zS@-M`2KWLDpx`|˵@@zS@-M`K`d|A͵AAzC@-M`Ldx|xԵXXzS@-M`SWLD"x`|Ե@@@zS@-M`T`d|յAA@zC@-M`Udx|9ݵXX@zS@-M`y\WLD2x`|Gݵ@@zS@-M`\`d|p޵AAzC@-M`]dx|嵝XXzS@-M`'eWLDBx`|嵝@@@zS@-M`re`d|絝AA@zC@-M`fdx|zXX@zS@-M`mWLDRx`|@@zS@-M`n`d|ﵝAAzC@-M`*odx|+XXzS@-M`kvWLDbx`|7@@@zS@-M`v`d|AA@zC@-M`wdx|XX@zS@-M`~WLDrx`|@@zS@-M``d|AAzC@-M`9dx|'XXzS@-M`gWLDx`|5@@@zS@-M``d| AA@zC@-M`Ljdx|XX@zS@-M`WLDx`|@@zS@-M`E`d|AAzC@-M`]dx|XXzS@-M`٘WLEx`|@@@zS@-M``d|AA@zC@-M`Udx|"XX@zS@-M`VWLEx`|$"@@zS@-M``d|a#AAzC@-M`dx|*XXzS@-M`WLE x`|*@@@zS@-M`J`d|,AA@zC@-M`_dx|3XX@zS@-M`ײWLE0x`|3@@zS@-M` `d|4AAzC@-M`:dx|\M`ջ`d|=AA@zC@-M`dx|PXX@zS@-M`WLEPx`|P@@zS@-M``d|QAAzC@-M`;dx|cYXXzS@-M`WLE`x`|oY@@@zS@-M``d|ZAA@zC@-M`dx|aXX@zS@-M`-WLEpx`|a@@zS@-M`[`d|hcAAzC@-M`dx|jXXzS@-M`WLE#x`|j@@@zS@-M`G`d|`lAA@zC@-M`dx|hsXX@zS@-M`WLE3x`|us@@zS@-M``d|tAAzC@-M`+dx||XXzS@-M`YWLECx`|$|@@@zS@-M``d|a}AA@zC@-M`dx|҄XX@zS@-M`WLESx`|ބ@@zS@-M`^`d|AAzC@-M`[dx|XXzS@-M` WLEcx`|@@@zS@-M` `d|鎶AA@zC@-M`)dx|M``d|AAzC@-M`dx|XXzS@-M`WLEx`|Ğ@@@zS@-M`e`d|AA@zC@-M`Tdx|BXX@zS@-M`&WLEx`|M@@zS@-M`&`d|AAzC@-M`'dx|򯶝XXzS@-M`2/WLFx`|@@@zS@-M`|/`d|MAA@zC@-M`0dx|XX@zS@-M`7WLFx`|@@zS@-M`.8`d|չAAzC@-M`9dx|IXXzS@-M`@WLF x`|U@@@zS@-M`@`d|¶AA@zC@-M`Adx|ɶXX@zS@-M`IWLF0x`|ɶ@@zS@-M`[I`d|.˶AAzC@-M`nJdx|ҶXXzS@-M`QWLF@x`|Ҷ@@@zS@-M`(R`d|ӶAA@zC@-M`-Sdx|%۶XX@zS@-M`eZWLFPx`|0۶@@zS@-M`Z`d|ܶAAzC@-M`[dx|㶝XXzS@-M`bWLF`x`|㶝@@@zS@-M`b`d|䶝AA@zC@-M`ddx|T춝XX@zS@-M`kWLFpx`|b춝@@zS@-M`k`d|AAzC@-M`mdx|XXzS@-M`WtWLF$x`|"@@@zS@-M`t`d|rAA@zC@-M`ud`|@@@zS@-M`z`d|EAA@zC@-M`dx| XX@zS@-M`WLFDx`| @@zS@-M``d| AAzC@-M`(dx|<XXzS@-M`|WLFTx`|S@@@zS@-M`Ғ`d|AA@zC@-M`֓dx|XX@zS@-M`WLFdx`|@@zS@-M`e`d| AAzC@-M``dx|Q$XXzS@-M`WLFtx`|a$@@@zS@-M``d|{%AA@zC@-M`dx|-XX@zS@-M`EWLFx`|-@@zS@-M``d|c.AAzC@-M`dx|5XXzS@-M`дWLFx`|5@@@zS@-M` `d|6AA@zC@-M`6dx|P>XX@zS@-M`WLGx`|`>@@zS@-@@@zC@->M``d|?AAzC@-M`dx|FXXzS@-M`WLGx`|F@@@zS@-M`r`d|=HAA@zC@-M`}dx|OXX@zS@-M`WLG x`|O@@zS@-M``d|PAAzC@-M`dx|LXXXzS@-M`WLG0x`|`X@@@zS@-M``d|YAA@zC@-M`dx|`XX@zS@-M`WLG@x`|`@@zS@-M``d|kbAAzC@-M`dx||iXXzS@-M`WLGPx`|i@@@zS@-M``d|kAA@zC@-M`Ddx|(rXX@zS@-M`hWLG`x`|7r@@zS@-M``d|ZsAAzC@-M`dx|zXXzS@-M`WLGpx`|z@@@zS@-M``d|{AA@zC@-M`dx|XX@zS@-M`TWLG%x`|"@@zS@-M`}`d|HAAzC@-M`dx|XXzS@-M` WLG5x`|͋@@@zS@-M`( `d|-AA@zC@-M`m dx|XXzS@-M`VWLGEx`|(@@zS@-M``d|dAAzC@-M`dx|^XXzS@-M`WLGUx`|l@@zS@-M``d|~AAzC@-M`dx| XXzS@-M`J$WLGex`|#@@zS@-M`u$`d|AAzC@-M`^%dx|XXzS@-M`,WLGux`|ҭ@@zS@-M`!-`d|箷AAzC@-M`'.dx|ķXXzS@-M`CWLGx`|ķ@@@zS@-M`C`d|ŷAA@zC@-M`Ddx|"ͷXXzS@-M`bLWLGx`|3ͷ@@zS@-M`L`d|鷝AAzC@-M`hdx|﷝XX@zS@-M`OnWLHx`|+﷝@@zS@-M`n`d|aAAzC@-M`odx|XXzS@-M`vWLHx`|@@@zS@-M`Nw`d|AA@zC@-M`?xdx|XX@NGxS@-M`WLH x`|@@CGxS@-M``d|AACGxC@-M` dx|F XXGGxS@-M`WLH0x`|V @@OGxS@-M``d| AAOGxC@-M`dx|XXCGxS@-M`WLH@x`|@@LGxS@-M``d|AALGxC@-M`Xdx|jXX@GxS@-M`WLHPx`|y@@LGxS@-M``d|AALGxC@-M` dx|/#XXCGxS@-M`oWLH`x`|=#@@OGxS@-M``d|{$AAOGxC@-M`dx|+XXGGxS@-M`3WLHpx`|,@@CGxS@-M`b`d|-AACGxC@-M`ʬdx|!4XX@NGxS@-M`aWLH&x`|/4@@@KGxS@-M``d|5AA@KGxC@-M`ʹdx|M`_`d|<>AA@NGxC@-M`|dx|rEXXCGxS@-M`WLHFx`|E@@GGxS@-M``d|FAAGGxC@-M`dx|%NXXOGxS@-M`eWLHVx`|3N@@CGxS@-M``d|jOAACGxC@-M`dx|VXXLGxS@-M`'WLHfx`|V@@@GxS@-M```d|6XAA@GxC@-M`vdx|_XX@GxS@-M`WLHvx`|_@@LGxS@-M``d| aAALGxC@-M`Idx|LhXXCGxS@-M`WLHx`|Zh@@OGxS@-M``d|iAAOGxC@-M`dx|qXXGGxS@-M`NWLHx`|q@@CGxS@-M``d|xrAACGxC@-M`dx|yXX@NGxS@-M`WLIx`|y@@@KGxS@-M`L`d|={AA@KGxC@-M`}dx|XX@KGxS@-M`WLIx`|@@@NGxS@-M``d|AA@NGxC@-M`Qdx|kXXCGxS@-M` WLI x`|y@@GGxS@-M` `d|݌AAGGxC@-M` dx| XXOGxS@-M`IWLI0x`|@@CGxS@-M``d|iAACGxC@-M`dx|XXLGxS@-M`WLI@x`|Ŝ@@@GxS@-M`#`d|AA@GxC@-M`Hdx|nXX@GxS@-M`$WLIPx`||@@LGxS@-M`$`d|AALGxC@-M`%dx|筸XXCGxS@-M`'-WLI`x`|󭸝@@OGxS@-M`Q-`d|QAAOGxC@-M`.dx|XXGGxS@-M`5WLIpx`|@@CGxS@-M`6`d|AACGxC@-M`H7dx|LXX@NGxS@-M`>WLI'x`|X@@@KGxS@-``|@@@NGxC@->M`>`d|AA@KGxC@-M`?dx| ȸXX@KGxS@-M`JGWLI7x`|ȸ@@@NGxS@-M`yG`d|OɸAA@NGxC@-M`Hdx|иXXCGxS@-M` PWLIGx`|и@@GGxS@-M`:P`d|FҸAAGGxC@-M`Qdx|ٸXXOGxS@-M`XWLIWx`|ٸ@@CGxS@-M`X`d|ڸAACGxC@-M`Zdx|;⸝XXLGxS@-M`{aWLIgx`|G⸝@@@GxS@-M`a`d|㸝AA@GxC@-M`bdx|븝XX@GxS@-M`GjWLIwx`|븝@@LGxS@-M`sj`d|?츝AALGxC@-M`kdx|XXCGxS@-M`WLIx`|@@OGxS@-M`%`d|AAOGxC@-M`*dx|O XXGGxS@-M`WLIx`|[ @@CGxS@-M``d| AACGxC@-M`dx|XX@NGxS@-M`UWLJx`|"@@@KGxS@-M``d|wAA@KGxC@-M`dx|XX@KGxS@-M`.WLJx`|@@@NGxS@-M``d|OAA@NGxC@-M`dx|m#XXCGxS@-M`WLJ x`|y#@@GGxS@-M``d|$AAGGxC@-M`dx|&,XXOGxS@-M`fWLJ0x`|4,@@CGxS@-M`ͫ`d|f-AACGxC@-M`dx|4XXLGxS@-M`WLJ@x`|4@@@GxS@-M`I`d|6AA@GxC@-M`Ndx|j=XX@GxS@-M`WLJPx`|x=@@LGxS@-M`ؼ`d|>AALGxC@-M`5dx|&FXXCGxS@-M`fWLJ`x`|1F@@OGxS@-M``d|GAAOGxC@-M`d`|N@@GGxS@-M``d|@TAAGGxC@-M`dx|[XX@NGxS@-M`WLJ(x`|[@@@KGxS@-M``d|]AA@KGxC@-M`Fdx|adXX@KGxS@-M`WLJ8x`|nd@@@NGxS@-M``d|eAA@NGxC@-M`dx|lXXGGxS@-M`WLJHx`|l@@CGxS@-M``d|mAACGxC@-M`dx|JuXXGGxS@-M`WLJXx`|Wu@@OGxS@-M``d|vAAOGxC@-M`dx|}XXCGxS@-M`WLJhx`|}@@LGxS@-M`A`d|,AALGxC@-M`ldx|XX@GxS@-M`WLJxx`|@@LGxS@-M``d| AALGxC@-M``dx|[XXCGxS@-M`WLJx`|i@@OGxS@-M``d|AAOGxC@-M`dx|XXGGxS@-M`GWLJx`|@@CGxS@-M`}`d|jAACGxC@-M`dx|XX@NGxS@-M`WLKx`|@@@KGxS@-M` `d|꡹AA@KGxC@-M`*!dx|RXX@KGxS@-M`(WLKx`|c@@@NGxS@-M`(`d|AA@NGxC@-M`)dx|XXCGxS@-M`=1WLK x`|@@GGxS@-M`r1`d|AAGGxC@-M`\2dx|jXXOGxS@-M`9WLK0x`||@@CGxS@-M`9`d|ŻAACGxC@-M`;dx|¹XXLGxS@-M`BWLK@x`|¹@@@GxS@-M`>B`d|ĹAA@GxC@-M`QCdx|d˹XXCGxS@-M`JWLKPx`|w˹@@@NGxS@-M`J`d|̹AA@NGxC@-M`Ldx|(ԹXX@KGxS@-M`hSWLK` x`|;Թ@@@NGxS@-M`S`d|չAA@NGxC@-M`Tdx|ܹXXCGxS@-M`$\WLKpx`|ܹ@@@GxS@-M`R\`d|5޹AA@GxC@-M`u]dx|幝XX@GxS@-M`eWLK)x`|幝@@CGxS@-M`Be`d|U繝AACGxC@-M`fdx|XXzS@-M`S{WLK9x`|$@@zS@-M`{`d|AAzC@-M`|dx|XXzS@-M`WLKIx`|@@zS@-M`X`d|HAAzC@-M`dx| XXzS@-M`ɌWLKYx`| @@zS@-M`H`d|AAzC@-M`6dx|UXXzS@-M`WLKix`|c@@zS@-M``d|AAzC@-M`dx|XX@zS@-M`*WLKyx`|@@zS@-M`z`d|Z AAzC@-M`dx|'XXzS@-M`WLKx`|'@@@zS@-M`I`d|Y)AA@zC@-M`dx|=0XXzS@-M`}WLKx`|L0@@zS@-M``d|1AAzC@-M`dx|8XXzS@-M`WLLx`|8@@zS@-M`9`d|:AAzC@-M`Udx|kAXXzS@-M`WLLx`|xA@@zS@-M``d|BAAzC@-M`dx|;JXXzS@-M`{WLL x`|IJ@@zS@-M``d|KAAzC@-M`dx|SXXzS@-M`BWLL0x`|S@@zS@-M`w`d|QTAAzC@-M`dx|[XXzS@-M` WLL@x`|[@@@zS@-M`[`d|6]AA@zC@-M`vdx|dXX@zS@-M`WLLPx`|d@@zS@-M``d|eAAzC@-M`dx|HmXXzS@-M`WLL` x`|Um@@@zS@-M``d|nAA@zC@-M`dx|uXX@zS@-M`WLLpx`|u@@zS@-M`f`d|swAAzC@-M`dx|h~XXzS@-M`WLL*x`|u~@@zS@-M``d|AAzC@-M`dx|ꆺXXzS@-M`*WLL:x`|@@zS@-M`s`d|;AAzC@-M`{dx|XXzS@-M`WLLJx`|@@zS@-M`+`d|AAzC@-M`4dx|#XXzS@-M`cWLLZx`|0@@zS@-M``d|nAAzC@-M`dx|XXzS@-M`- WLLjx`|@@zS@-M` `d|rAAzC@-M`!dx|XX@zS@-M`(WLLzx`|@@zS@-M`D)`d|!AAzC@-M`a*dx|;XXzS@-M`{1WLLx`|R@@@zS@-M`1`d|AA@zC@-M`2dx|XXzS@-M`-:WLLx`|@@zS@-M`:`d|8AAzC@-M`x;dx|úXXzS@-M`BWLMx`|ú@@zS@-M`9C`d|ĺAAzC@-M`:Ddx|k̺XXzS@-M`KWLMx`|w̺@@zS@-M`K`d|ͺAAzC@-M`Mdx|ԺXXzS@-M`TWLM x`|Ժ@@zS@-M`}T`d|FֺAAzC@-M`Udx|ݺXXzS@-M`\WLM0x`|ݺ@@zS@-M`']`d|ߺAAzC@-M`A^dx|]溝XXzS@-M`eWLM@x`|i溝@@@zS@-M`e`d|纝AA@zC@-M`fdx|ﺝXX@zS@-M`[nWLMPx`|'ﺝ@@zS@-M`n`d|AAzC@-M`odx|XXzS@-M`wWLM` x`|@@@zS@-M`cw`d| AA@zC@-M``xdx|XX@zS@-M`WLMpx`|@@zS@-M` `d|AAzC@-M`4dx|a XXzS@-M`WLM+x`|m @@zS@-M``d| AAzC@-M`dx|XXzS@-M`]WLM;x`|*@@zS@-M``d|eAAzC@-M`dx|XXzS@-M`WLMKx`|@@zS@-M`c`d|4AAzC@-M`tdx|_#XXzS@-M`WLM[x`|k#@@zS@-M``d|$AAzC@-M`dx|,XXzS@-M`TWLMkx`|,@@zS@-M``d|o-AAzC@-M`dx|4XX@zS@-M`ڳWLM{x`|4@@zS@-M`3`d|5AAzC@-M`&dx|'=XXzS@-M`gWLMx`|2=@@@zS@-M``d|>AA@zC@-M`dx|EXXzS@-M`!WLMx`|E@@zS@-M`j`d|EGAAzC@-M`dx|NXXzS@-M`WLNx`|N@@zS@-M``d|OAAzC@-M`&dx|M``d|XAAzC@-M`dx|_XXzS@-M`WLN x`|_@@zS@-M`.`d|`AAzC@-M`-dx|hXXzS@-M`WLN0x`|h@@zS@-i@@zC@->M`~`d|6jAAzC@-M`vdx|qXXzS@-M`UWLN@x`|!q@@@zS@-M``d|orAA@zC@-M`d`|y@@@zS@-M``d|nAA@zC@-M`dx|XX@zS@-M`WLN` x`|@@zS@-M``d|̇AAzC@-M` dx|UXX@zS@-M`WLNpx`|e@@zS@-M``d|ԐAAzC@-M`dx|XXzS@-M`YWLN,x`|&@@zS@-M``d|AAzC@-M`dx|쭻XXzS@-M`,-WLN<x`|@@zS@-M`[-`d|SAAzC@-M`.dx|XXzS@-M`5WLNLx`|϶@@zS@-M`+6`d|AAzC@-M`O7dx|)XXzS@-M`i>WLN\x`|6@@zS@-``|@@zC@->M`>`d|qAAzC@-M`?dx|ǻXXzS@-M`&GWLNlx`|ǻ@@zS@-M`sG`d|)ɻAAzC@-M`iHdx|лXX@zS@-M`OWLN|x`|л@@zS@-M`2P`d|ѻAAzC@-M`,Qdx|7ٻXXzS@-M`wXWLNx`|Eٻ@@@zS@-M`X`d|ڻAA@zC@-M`Ydx|ờXXzS@-M`2aWLNx`|⻝@@zS@-M`a`d|M㻝AAzC@-M`bdx|껝XXzS@-M`iWLOx`|껝@@zS@-M`8j`d|컝AAzC@-M`Hkdx|fXXzS@-M`rWLOx`|v@@zS@-M`r`d|AAzC@-M`tdx|XXzS@-M`\{WLO x`|)@@zS@-M`{`d|AAzC@-M`|dx|XXzS@-M`WLO0x`|@@zS@-M`g`d|CAAzC@-M`dx|^ XXzS@-M`WLO@x`|k @@@zS@-M``d|AA@zC@-M`dx|XXzS@-M`PWLOPx`|@@@zS@-M``d|oAA@zC@-M`dx|XX@zS@-M`WLO` x`|@@zS@-M`c`d|: AAzC@-M`zdx|'XX@zS@-M`֦WLOpx`|'@@zS@-M`*`d| )AAzC@-M`Ldx|Y0XXzS@-M`WLO-x`|i0@@zS@-M`ǯ`d|1AAzC@-M`dx|8XXzS@-M`1WLO=x`|9@@zS@-M`^`d|Y:AAzC@-M`dx|AXXzS@-M`8WLOMx`|B@@zS@-M`d`d|(CAAzC@-M`hdx|JXXzS@-M`WLO]x`|J@@zS@-M`&`d|0LAAzC@-M`pdx|RSXXzS@-M`WLOmx`|_S@@zS@-M``d|TAAzC@-M`dx|[XX@zS@-M`WLO}x`|[@@zS@-M`R`d|C]AAzC@-M`dx|dXXzS@-M`WLOx`|d@@@zS@-M``d|eAA@zC@-M`&dx|\mXXzS@-M`WLOx`|jm@@zS@-M``d|܉AAzC@-M` dx| XXzS@-M`JWLPx`|@@zS@-M``d|AAzC@-M`dx|ŗXXzS@-M`WLPx`|ї@@zS@-M`<`d|4AAzC@-M`tdx|XXzS@-M`WLP x`|@@zS@-M``d|)AAzC@-M`i!dx|GXXzS@-M`(WLP0x`|U@@zS@-M`(`d|AAzC@-M`)dx|XXzS@-M`F1WLP@x`|@@@zS@-M`|1`d|lAA@zC@-M`2dx|źXXzS@-M`:WLPPx`|Һ@@@zS@-M`/:`d|AA@zC@-M`[;dx|üXX@zS@-M`BWLP`x`|ü@@zS@-M`B`d|ļAAzC@-M` Ddx|6̼XX@zS@-M`vKWLPpx`|B̼@@zS@-M`K`d|ͼAAzC@-M`Ldx|ԼXXzS@-M`SWLP.x`|Լ@@zS@-M`2T`d| ּAAzC@-M`MUdx|ݼXXzS@-M`\WLP>x`|ݼ@@zS@-M`\`d|޼AAzC@-M`#^dx|F漝XXzS@-M`eWLPNx`|R漝@@zS@-M`e`d|缝AAzC@-M`fdx|XXzS@-M`nWLP^x`|@@zS@-M`Fn`d|M`4w`d|AAzC@-M`_xdx|[XX@zS@-M`WLP~x`|i@@zS@-M``d|AAzC@-M`dx| XXzS@-M`XWLPx`|$ @@@zS@-M``d| AA@zC@-M`dx|XXzS@-M` WLPx`|@@zS@-M`5`d|bAAzC@-M`dx|}XXzS@-M`WLQx`|@@zS@-M``d|AAzC@-M`"dx|)#XXzS@-M`iWLQx`|4#@@zS@-M``d|s$AAzC@-M`dx|+XXzS@-M`WLQ x`|+@@zS@-M`N`d|;-AAzC@-M`{dx|4XXzS@-M`ԳWLQ0x`|4@@zS@-M``d|5AAzC@-M`dx|(=XXzS@-M`hWLQ@x`|4=@@@zS@-M``d|{>AA@zC@-M`dx|EXXzS@-M`WLQPx`|E@@@zS@-M`K`d|?GAA@zC@-M`dx|]NXX@zS@-M`WLQ`x`|hN@@zS@-M``d|pOAAzC@-M`dx|VXX@zS@-M`#WLQpx`|V@@zS@-M`V`d|-XAAzC@-M`mdx|_XXzS@-M`WLQ/x`|_@@zS@-M``d|`AAzC@-M`!dx|2hXXzS@-M`rWLQ?x`|M``d|iAAzC@-M`dx|pXXzS@-M`WLQOx`|p@@zS@-M``d|rAAzC@-M`Ddx|yXXzS@-M`^WLQ_x`|&y@@zS@-M``d|zAAzC@-M`dx|ׁXXzS@-M` WLQox`|ၽ@@zS@-M`K `d|"AAzC@-M`b dx|XX@zS@-M` WLQx`|@@zS@-M` `d|ϋAAzC@-M` dx|GXXzS@-M` WLQx`|R@@@zS@-M` `d|AA@zC@-M` dx|XXzS@-M`A WLQx`| @@zS@-M`t `d|RAAzC@-M` dx|XX@NGxS@-M`# WLRx`|@@@KGxS@-M` $ `d|AA@KGxC@-M`$ dx|XX@NGxS@-M`., WLRx`|@@@KGxS@-M`\, `d|AA@KGxC@-M`O- dx|0XXCGxS@-M`p4 WLR x`|:@@GGxS@-M`4 `d|]AAGGxC@-M`5 d`|x@@LGxS@-M`FA `d|ýAALGxC@-M`ZB dx|PʽXXCGxS@-M`I WLR@x`|^ʽ@@@GxS@-M`I `d|˽AA@GxC@-M` K dx|ҽXX@GxS@-M`R WLRPx`|ҽ@@CGxS@-M`:R `d|ԽAACGxC@-M`\S dx|v۽XXCGxS@-M`Z WLR`x`|۽@@LGxS@-M`Z `d|ܽAALGxC@-M`[ dx|㽝XXCGxS@-M`c WLRp x`|㽝@@OGxS@-M`Mc `d| 彝AAOGxC@-M``d dx|콝XXGGxS@-M`k WLR0x`|콝@@CGxS@-M`k `d|AACGxC@-M`m dx| XX@KGxS@-M`Jt WLR@x`|@@@NGxS@-M`t `d|AA@NGxC@-M`u dx|XX@NGxS@-M`| WLRPx`|@@@KGxS@-M`0} `d|AA@KGxC@-M`.~ dx|'XX@KGxS@-M`g WLR`x`|3@@@NGxS@-M`Dž `d|rAA@NGxC@-M` dx|XX@NGxS@-M`' WLRpx`|@@@KGxS@-M` `d|-AA@KGxC@-M`m dx|XXzS@-M`֖ WLRx`|@@zS@-M`% `d|AAzC@-M`\ dx|\ XXzS@-M` WLRx`|h @@zS@-M` `d|!AAzC@-M` dx|)XXzS@-M`P WLRx`|)@@zS@-M` `d|x*AAzC@-M` dx|1XXzS@-M` WLSx`|1@@zS@-M`W `d|3AAzC@-M`P dx|:XXzS@-M`͹ WLSx`|:@@@zS@-M` `d|;AA@zC@-M`. dx|CXXzS@-M`[ WLS x`|)C@@@zS@-M` `d|DAA@zC@-M` dx|KXX@zS@-M` WLS0x`|K@@zS@-M` `d|LAAzC@-M`, dx|uTXX@zS@-M` WLS@x`|T@@zS@-M` `d|UAAzC@-M` dx|:]XXzS@-M`z WLSPx`|H]@@zS@-M` `d|^AAzC@-M` dx|fXXzS@-M`A WLS`x`|f@@zS@-M` `d|lgAAzC@-M` dx|nXXzS@-M` WLSp!x`|n@@zS@-M`6 `d|pAAzC@-M`F dx|wXXzS@-M` WLS1x`|w@@zS@-M` `d|xAAzC@-M` dx|JXXzS@-M` WLSAx`|X@@zS@-M` `d|AAzC@-M` dx|݈XX@zS@-M` WLSQx`|눾@@zS@-M`s `d|cAAzC@-M` dx|XXzS@-M` WLSax`|@@@zS@-M`? `d|AA@zC@-M`W dx|`XXzS@-M` WLSqx`|m@@zS@-M` `d|ǛAAzC@-M` dx|'XXzS@-M`g" WLSx`|?@@zS@-M`" `d|AAzC@-M`# dx|竾XXzS@-M`'+ WLSx`|󫾝@@zS@-M`u+ `d|AAzC@-M`, dx|XXzS@-M`3 WLSx`|@@zS@-M`94 `d| AAzC@-M`M5 dx|7XXzS@-M`w< WLTx`|C@@zS@-M`< `d|AAzC@-M`= dx|žXXzS@-M`E WLTx`|ž@@@zS@-M`UE `d|ǾAA@zC@-M`\F dx|ܾXXzS@-M`[ WLT x`|ܾ@@@zS@-M`I\ `d|!޾AA@zC@-M`a] dx|j徝XX@zS@-M`d WLT0x`|v徝@@zS@-M`d `d|澝AAzC@-M`e dx|.XX@zS@-M`nm WLT@x`|<@@zS@-M`m `d|ンAAzC@-M`n dx|XXzS@-M`:v WLTPx`|@@zS@-M`zv `d|_AAzC@-M`w dx|XXzS@-M`~ WLT`x`|@@zS@-M` `d|AAzC@-M` dx|>XXzS@-M`~ WLTp"x`|J@@zS@-M`ʇ `d| AAzC@-M` dx|XXzS@-M`A WLT2x`| @@zS@-M` `d|]AAzC@-M` dx|XXzS@-M` WLTBx`|@@zS@-M`U `d|AAzC@-M`A dx||"XX@zS@-M` WLTRx`|"@@zS@-M` `d|#AAzC@-M` dx|)+XXzS@-M`i WLTbx`|A+@@@zS@-M` `d|C,AA@zC@-M` dx|3XXzS@-M` WLTrx`|3@@zS@-M` `d|4AAzC@-M` dx|QM` `d||=AAzC@-M` dx|DXXzS@-M`7 WLTx`|E@@zS@-M`a `d|)FAAzC@-M`i dx|MXXzS@-M` WLTx`|M@@zS@-M` `d|NAAzC@-M` dx|:VXXzS@-M`z WLUx`|KV@@zS@-M` `d|UWAAzC@-M` dx|^XXzS@-M` WLUx`|^@@@zS@-M`? `d|_AA@zC@-M`. dx|vgXXzS@-M` WLU x`|g@@@zS@-M` `d|hAA@zC@-M` dx|pXX@zS@-M`Q WLU0x`|$p@@zS@-M` `d|qAAzC@-M` dx|xXX@zS@-M` WLU@x`|x@@zS@-M` `d|yAAzC@-M` dx|FXXzS@-M` WLUPx`|U@@zS@-M` `d|AAzC@-M` dx|XXzS@-M`> WLU`x`| @@zS@-M` `d|AAzC@-M` dx|’XXzS@-M` WLUp#x`|ђ@@zS@-M`1 `d|5AAzC@-M`u dx|kXXzS@-M` WLU3x`|w@@zS@-M` `d|AAzC@-M` dx|6XXzS@-M`v# WLUCx`|F@@zS@-M`# `d|AAzC@-M`$ dx|笿XX@zS@-M`', WLUSx`|󬿝@@zS@-M`t, `d|CAAzC@-M`- dx|vXXzS@-M`4 WLUcx`|@@@zS@-M`5 `d|AA@zC@-M`5 dx|2XXzS@-M`r= WLUsx`|>@@zS@-M`= `d|AAzC@-M`> dx|ƿXXzS@-M`E WLUx`|ƿ@@zS@-M`F `d|ǿAAzC@-M`G dx|<ϿXXzS@-M`|N WLUx`|HϿ@@zS@-M`N `d|пAAzC@-M`O dx|׿XXzS@-M`=W WLUx`|ؿ@@zS@-M`W `d|JٿAAzC@-M`X d`|࿝@@zS@-M`=d `d|濝AAzC@-M`\e dx|fXXzS@-M`l WLVx`||@@@zS@-M`l `d|AA@zC@-M`m dx|XXzS@-M` u WLV x`|@@@zS@-M`Zu `d|3AA@zC@-M`sv dx|UXX@zS@-M`} WLV0x`|a@@zS@-M`} `d|AAzC@-M`~ dx|XX@zS@-M`N WLV@x`|@@zS@-M` `d|QAAzC@-M` dx|XXzS@-M` WLVPx`|@@zS@-M`7 `d|4AAzC@-M`t dx|DXXzS@-M` WLV`x`|Q@@zS@-M` `d|AAzC@-M` dx| XXzS@-M` WLVp$x`| @@zS@-M`F `d|!AAzC@-M`: dx|)XXzS@-M`ۨ WLV4x`|)@@zS@-M`( `d|*AAzC@-M`4 dx|U2XXzS@-M` WLVDx`|a2@@zS@-M` `d|3AAzC@-M`ܲ dx|;XX@zS@-M`O WLVTx`|;@@zS@-M` `d|HM`< `d| EAA@zC@-M`L dx|PLXXzS@-M` WLVtx`|aL@@zS@-M` `d|MAAzC@-M` dx|"UXXzS@-M`b WLVx`|1U@@zS@-M` `d|lVAAzC@-M` dx|^XXzS@-M`Q WLVx`|!^@@zS@-M` `d|T_AAzC@-M` dx|fXXzS@-M` WLVx`|f@@zS@-M`2 `d|gAAzC@-M`6 dx||oXXzS@-M` WLWx`|o@@zS@-M` `d|pAAzC@-M`' dx|VxXXzS@-M` WLWx`|fx@@@zS@-M` `d|yAA@zC@-M` dx|$XXzS@-M`d WLW x`|3@@@zS@-M` `d|AA@zC@-M` dx|XX@zS@-M` WLW0x`|ʼn@@zS@-M`e `d|AAzC@-M`9 dx|pXX@zS@-M` WLW@x`|@@zS@-M` `d|AAzC@-M` dx|YXXzS@-M` WLWPx`|h@@zS@-M` `d|nAAzC@-M` dx|wXXzS@-M`# WLW`x`|@@zS@-M`# `d|AAzC@-M`$ dx|XXzS@-M`E, WLWp%x`|@@zS@-M`, `d|gAAzC@-M`- dx|XXzS@-M`4 WLW5x`|@@zS@-M`A5 `d|AAzC@-M`Y6 dx|kXXzS@-M`= WLWEx`|y@@zS@-M`= `d|˿AAzC@-M` ? dx|;XX@zS@-M`{F WLWUx`|J@@zS@-M`F `d|AAzC@-M`G dx|XXzS@-M`0O WLWex`|@@@zS@-O ``|r@@zC@->M`O `d|yAA@zC@-M`P dx|XXzS@-M`W WLWux`|@@zS@-M`DX `d|AAzC@-M`!Y dx|mXXzS@-M`` WLWx`|z@@zS@-M`` `d|AAzC@-M`a dx|4XXzS@-M`ti WLWx`|D@@zS@-M`i `d|AAzC@-M`k dx|XXzS@-M`7r WLWx`|@@zS@-M`r `d|xAAzC@-M` dx|XXzS@-M` WLXx`|@@zS@-M`7 `d|AAzC@-M`] dx|h"XXzS@-M` WLXx`|u"@@@zS@-M` `d|#AA@zC@-M` dx|++XXzS@-M`k WLX x`|8+@@@zS@-M` `d|{,AA@zC@-M` dx|3XX@zS@-M`& WLX0x`|3@@zS@-M`q `d|95AAzC@-M`y dx|M`9 `d|&>AAzC@-M`f dx|XEXXzS@-M` WLXPx`|dE@@zS@-M` `d|FAAzC@-M` dx| NXXzS@-M`M WLX`x`|N@@zS@-M`w `d|QOAAzC@-M` dx|VXXzS@-M` WLXp&x`|V@@zS@-M`U `d| XAAzC@-M`M dx|_XXzS@-M` WLX6x`|_@@zS@-M`+ `d|aAAzC@-M`O dx|hXXzS@-M`D WLXFx`|h@@zS@-M` `d|piAAzC@-M` dx|pXX@zS@-M` WLXVx`|p@@zS@-M`G `d|rAAzC@-M`^ dx||XXzS@-M` WLXfx`|@@@zS@-M` `d|шAA@zC@-M` dx|XXzS@-M`V WLXvx`|)@@zS@-M` `d|bAAzC@-M` dx|ØXXzS@-M` WLXx`|Ә@@zS@-M`X `d|$AAzC@-M`d dx|}XXzS@-M` WLXx`|@@zS@-M`! `d|ڢAAzC@-M`" dx|AXXzS@-M`) WLXx`|Q@@zS@-M`) `d|AAzC@-M`* dx|XXzS@-M` 2 WLYx`|@@zS@-M`s2 `d|4AAzC@-M`t3 dx|XXzS@-M`: WLYx`|@@@zS@-M`; `d|ؼAA@zC@-M`< dx|IXXzS@-M`C WLY x`|W@@@zS@-M`C `d|AA@zC@-M`D dx|)XX@zS@-M`iL WLY0x`|4@@zS@-M`L `d|AAzC@-M`M dx|}XX@zS@-M`T WLY@x`|@@zS@-M`U `d|AAzC@-M`V dx| XXzS@-M`K] WLYPx`|@@zS@-M`] `d|OAAzC@-M`^ dx|XXzS@-M`e WLY`x`|@@zS@-M`Xf `d|AAzC@-M`Zg dx|vXXzS@-M`n WLYp'x`|@@zS@-M`o `d|AAzC@-M`o dx|*XXzS@-M`jw WLY7x`|9@@zS@-M`w `d|AAzC@-M`x dx|XXzS@-M`& WLYGx`|@@zS@-M`U `d|8AAzC@-M`x dx| XX@zS@-M`͈ WLYWx`| @@zS@-M` `d| AAzC@-M` dx|XXzS@-M`W WLYgx`|%@@@zS@-M` `d|8AA@zC@-M`x dx|XXzS@-M` WLYwx`|@@zS@-M` `d|AAzC@-M` dx|u#XXzS@-M` WLYx`|#@@zS@-M` `d|$AAzC@-M` d`|,@@zS@-M` `d|1AAzC@-M` dx|EXXzS@-M`' WLYx`|F@@zS@-M` `d|aGAAzC@-M` dx|NXXzS@-M` WLZx`|N@@zS@-M`B `d|OAAzC@-M`% dx|4WXXMwS@-M`t WLZx`|DW@@MwS@-M` `d|XAAMwC@-M` dx|_XXMwS@-M`5 WLZ x`|`@@MwS@-M`k `d|MaAAMwC@-M` dx|hXX@MwS@-M` WLZ0x`|h@@MwS@-M` `d|iAAMwC@-M`/ dx|qXXMwS@-M`P WLZ@x`|%q@@@MwS@-M` `d|]rAA@MwC@-M` dx|yXX@MwS@-M` WLZPx`|y@@MwS@-M`z `d|{AAMwC@-M`P dx|iXXMwS@-M`WLZ`x`|u@@@MwS@-M``d|AA@MwC@-M`dx|ӊXX@MwS@-M` WLZp(x`|@@MwS@-M`f `d|%AAMwC@-M`e dx|XXLGxS@-M`WLZ8x`|@@GGxS@-M``d|AAGGxC@-M`dx|JXXLGxS@-M`WLZHx`|Y@@@KGxS@-M``d|AA@KGxC@-M`;dx|ڤXXCGxS@-M`$WLZXx`|@@@KGxS@-M`J$`d|=AA@KGxC@-M`}%dx|XXLGxS@-M`,WLZhx`|@@GGxS@-M`-`d| AAGGxC@-M`I.dx|kXXOGxS@-M`5WLZxx`|y@@@NGxS@-M`6`d|1AA@NGxC@-M`q7dx|,XX@NGxS@-M`l>WLZx`|J@@OGxS@-``|U@@@NGxC@->M`>`d|AAOGxC@-M`?dx|XXOGxS@-M`CGWLZx`|@@GGxS@-M`vG`d|hAAGGxC@-M`Hdx|XXGGxS@-M`OWLZx`|@@OGxS@-M`\P`d|AAOGxC@-M`CQdx|XXOGxS@-M`XWL[x`|@@GGxS@-M`X`d|AAGGxC@-M`Zdx|>XXGGxS@-M`~aWL[x`|K@@OGxS@-M`a`d|AAOGxC@-M`bdx|XXOGxS@-M`OjWL[ x`|@@GGxS@-M`j`d|kAAGGxC@-M`kdx|XXGGxS@-M`rWL[0x`|@@OGxS@-M`9s`d|AAOGxC@-M`&tdx|LXXzS@-M`{WL[@x`|Y@@zS@-M`{`d|AAzC@-M`|dx| ÝXXzS@-M`IWL[P x`|Ý@@zS@-M``d|WÝAAzC@-M`dx| ÝXXzS@-M`WL[`x`| Ý@@zS@-M``d|FÝAAzC@-M`dx|ÝXXOGxS@-M`ƕWL[p)x`|Ý@@GGxS@-M``d|ÝAAGGxC@-M`dx|!ÝXXLGxS@-M`aWL[9x`|-Ý@@@KGxS@-M`ž`d|~ ÝAA@KGxC@-M`dx|'ÝXXCGxS@-M`WL[Ix`|'Ý@@@KGxS@-M``d|;)ÝAA@KGxC@-M`{dx|0ÝXXLGxS@-M`үWL[Yx`|0Ý@@GGxS@-M`9`d|1ÝAAGGxC@-M`.dx|O9ÝXXOGxS@-M`WL[ix`|[9Ý@@@NGxS@-M``d|:ÝAA@NGxC@-M`dx|AÝXX@NGxS@-M`WL[yx`|AÝ@@OGxS@-M`H`d|#CÝAAOGxC@-M`cdx|JÝXXGGxS@-M`WL[x`|JÝ@@LGxS@-M`7`d|KÝAALGxC@-M`0dx|XSÝXX@KGxS@-M`WL[x`|dSÝ@@CGxS@-M``d|TÝAACGxC@-M`%dx|[ÝXXCGxS@-M`WL[x`|[Ý@@@KGxS@-M`|`d|P]ÝAA@KGxC@-M`dx|`dÝXXLGxS@-M`WL\x`|mdÝ@@GGxS@-M``d|eÝAAGGxC@-M`dx| mÝXXGGxS@-M`LWL\x`|mÝ@@LGxS@-M``d|jnÝAALGxC@-M`dx|uÝXXLGxS@-M`WL\ x`|uÝ@@GGxS@-M`e`d|vÝAAGGxC@-M`dx|s~ÝXXGGxS@-M`WL\0x`|~Ý@@LGxS@-M`&`d|ÝAALGxC@-M`dx|,ÝXXLGxS@-M`lWL\@x`|7Ý@@GGxS@-M``d|pÝAAGGxC@-M`dx|ÝXXGGxS@-M`WL\P x`|Ý@@LGxS@-M`J`d|ÝAALGxC@-M`Cdx|*ÝXXLGxS@-M`jWL\`x`|GÝ@@GGxS@-M``d|hÝAAGGxC@-M`dx|ÝXXGGxS@-M`WL\p*x`|Ý@@LGxS@-M` `d|ǡÝAALGxC@-M`!dx|AÝXXLGxS@-M`(WL\:x`|LÝ@@GGxS@-M`(`d|ÝAAGGxC@-M`)dx|ÝXXGGxS@-M`51WL\Jx`|Ý@@LGxS@-M`_1`d|OÝAALGxC@-M`2dx|jÝXXLGxS@-M`9WL\Zx`|uÝ@@GGxS@-M` :`d|ÝAAGGxC@-M`:dx|ÝXXGGxS@-M`WL\jx`| Ý@@LGxS@-M`x`d|fÝAALGxC@-M`fdx|ÝXXLGxS@-M`WL\zx`|Ý@@GGxS@-M` `d|ÝAAGGxC@-M` dx|fÝXXGGxS@-M`fWL\x`|qÝ@@LGxS@-M``d|ÝAALGxC@-M`dx|ÝXXLGxS@-M`WL\x`|Ý@@GGxS@-M`[`d|QÝAAGGxC@-M`Qdx|%ÝXXGGxS@-M`%#WL\x`|0Ý@@LGxS@-M`t#`d|vÝAALGxC@-M`v$dx|ÝXXLGxS@-M`+WL]x`|Ý@@GGxS@-M`,`d|$ÝAAGGxC@-M`$-dx|ÝXXGGxS@-M`4WL]x`|Ý@@LGxS@-M`4`d|ÝAALGxC@-M`5dx|DĝXXLGxS@-M`D=WL] x`|Nĝ@@GGxS@-M`m=`d|ĝAAGGxC@-M`>dx|ĝXXGGxS@-M`EWL]0x`|ĝ@@LGxS@-M`F`d|Q ĝAALGxC@-M`QGdx|ĝXXLGxS@-M`NWL]@x`|ĝ@@GGxS@-M`O`d|ĝAAGGxC@-M`Odx|\ĝXXGGxS@-M`\WWL]P x`|jĝ@@LGxS@-M`W`d|ĝAALGxC@-M`Xdx|#ĝXXOGxS@-M``WL]`x`|#ĝ@@@NGxS@-M`t``d|$ĝAA@NGxC@-M`adx|x+ĝXX@NGxS@-M`xhWL]p+x`|+ĝ@@OGxS@-M`h`d|,ĝAAOGxC@-M`idx|/4ĝXXLGxS@-M`/qWL];x`|:4ĝ@@GGxS@-M`q`d|r5ĝAAGGxC@-M`rrdx|<ĝXX@KGxS@-M`yWL]Kx`|<ĝ@@CGxS@-M`ĝAACGxC@-M` {dx|kEĝXXCGxS@-M`kWL][x`|xEĝ@@@KGxS@-M``d|FĝAA@KGxC@-M`ǃdx|!NĝXXGGxS@-M`!WL]kx`|+Nĝ@@LGxS@-M`t`d|OĝAALGxC@-M`d`|Vĝ@@OGxS@-M`W`d|i\ĝAAOGxC@-M`idx|cĝXXOGxS@-M`ˠWL]x`|cĝ@@@NGxS@-M`+`d|eĝAA@NGxC@-M`dx|lĝXXzS@-M`WL]x`|lĝ@@zS@-M``d|nĝAAzC@-M`dx|uĝXX@NGxS@-M`WL]x`|,uĝ@@OGxS@-M`N`d|vĝAAOGxC@-M`dx|}ĝXXOGxS@-M`WL^x`|}ĝ@@@NGxS@-M``d|.ĝAA@NGxC@-M`.dx|ĝXX@NGxS@-M`WL^x`|ĝ@@OGxS@-M``d| ĝAAOGxC@-M` dx|uĝXXOGxS@-M`uWL^ x`|ĝ@@@NGxS@-M``d|ӐĝAA@NGxC@-M`dx|4ĝXX@NGxS@-M`4WL^0x`|Aĝ@@OGxS@-M`c`d|ĝAAOGxC@-M`dx|ɠĝXXOGxS@-M`WL^@x`|ؠĝ@@@NGxS@-M``d|ĝAA@NGxC@-M`dx|PĝXX@NGxS@-M`PWL^P x`|]ĝ@@OGxS@-M``d|ĝAAOGxC@-M`dx|ĝXXOGxS@-M`WL^`x`|!ĝ@@@NGxS@-M`F`d|ĝAA@NGxC@-M`dx|ĝXX@NGxS@-M`WL^p,x`|ĝ@@OGxS@-M``d|CĝAAOGxC@-M`Cdx|eĝXXOGxS@-M`eWL^<x`|sĝ@@@NGxS@-M``d|ĝAA@NGxC@-M`dx|%ĝXX@NGxS@-M`% WL^Lx`|2ĝ@@OGxS@-M`T `d|sĝAAOGxC@-M`s dx|ĝXXOGxS@-M`WL^\x`|ĝ@@@NGxS@-M``d|IĝAA@NGxC@-M`Idx|ĝXX@NGxS@-M`WL^lx`|ĝ@@OGxS@-M``d|ĝAAOGxC@-M`dx|mĝXXOGxS@-M`m#WL^|x`|{ĝ@@@NGxS@-M`#`d|ĝAA@NGxC@-M`$dx|%ĝXX@NGxS@-M`%,WL^x`|3ĝ@@OGxS@-M`,`d|rĝAAOGxC@-M`r-dx|ĝXXOGxS@-M`4WL^x`|ĝ@@@NGxS@-M`5`d|>ĝAA@NGxC@-M`>6dx|ŝXX@NGxS@-M`=WL^x`|ŝ@@OGxS@-M`=`d|ŝAAOGxC@-M`>dx|T ŝXXOGxS@-M`TFWL_x`|d ŝ@@@NGxS@-M`F`d| ŝAA@NGxC@-M`Gdx|ŝXX@NGxS@-M`NWL_x`|ŝ@@OGxS@-M`EO`d|8ŝAAOGxC@-M`8Pdx|pŝXXOGxS@-M`pWWL_ x`|}ŝ@@@NGxS@-M`W`d|ŝAA@NGxC@-M`Xdx|0ŝXXzS@-M`mWL_0x`|1ŝ@@@zS@-M`In`d|W2ŝAA@zC@-M`Wodx|9ŝXXzS@-M`vWL_@x`|9ŝ@@zS@-M`w`d|;ŝAAzC@-M`xdx|bBŝXXzS@-M`bWL_P x`|rBŝ@@zS@-M``d|CŝAAzC@-M`dx|'KŝXX@zS@-M`'WL_`x`|7Kŝ@@zS@-M`~`d|LŝAAzC@-M`ʉdx|SŝXXzS@-M`WL_p-x`|Sŝ@@@zS@-M`A`d|MUŝAA@zC@-M`Mdx|\ŝXXzS@-M`WL_=x`|\ŝ@@zS@-M``d|]ŝAAzC@-M`dx|XeŝXX@NGxS@-M`XWL_Mx`|geŝ@@OGxS@-M``d|fŝAAOGxC@-M`dx|nŝXXOGxS@-M`WL_]x`|!nŝ@@@NGxS@-M`<`d|VoŝAA@NGxC@-M`Vdx|vŝXX@NGxS@-M`ҳWL_mx`|vŝ@@OGxS@-M``d|WxŝAAOGxC@-M`Wdx|^ŝXXOGxS@-M`^WL_}x`|lŝ@@@NGxS@-M``d|ŝAA@NGxC@-M`dx|"ŝXX@NGxS@-M`"WL_x`|/ŝ@@OGxS@-M`R`d|wŝAAOGxC@-M`wdx|ŝXXOGxS@-M`WL_x`|ŝ@@@NGxS@-M``d|ŝAA@NGxC@-M`dx|qŝXX@NGxS@-M`qWL_x`|~ŝ@@OGxS@-M``d|ŝAAOGxC@-M`dx|ŝXXOGxS@-M`WL`x`|ŝ@@@NGxS@-M` `d|%ŝAA@NGxC@-M`%dx|ŝXX@NGxS@-M`WL`x`|ŝ@@OGxS@-M``d|ŝAAOGxC@-M`dx|]ŝXXOGxS@-M`]WL` x`|jŝ@@@NGxS@-M``d|ŝAA@NGxC@-M`dx|&ŝXX@NGxS@-M`&WL`0x`|6ŝ@@OGxS@-M`X`d|ŝAAOGxC@-M`dx|ŝXXOGxS@-M`!WL`@x`|ŝ@@@NGxS@-M` "`d|0ŝAA@NGxC@-M`0#dx|ŝXX@NGxS@-M`*WL`Px`|ŝ@@OGxS@-M`*`d|ŝAAOGxC@-M`+dx|JŝXXOGxS@-M`J3WL``x`|Vŝ@@@NGxS@-M`3`d|ŝAA@NGxC@-M`4dx| ŝXX@NGxS@-M` <WL`p.x`|ŝ@@OGxS@-M`h<`d|wƝAAOGxC@-M`w=dx|ƝXXOGxS@-M`DWL`>x`|Ɲ@@@NGxS@-M`D`d| ƝAA@NGxC@-M`Fdx|ƝXX@NGxS@-M`MWL`Nx`|(Ɲ@@OGxS@-M`{M`d|~ƝAAOGxC@-M`~Ndx|ƝXXOGxS@-M`UWL`^x`|Ɲ@@@NGxS@-M`/V`d| ƝAA@NGxC@-M` Wdx|W!ƝXX@NGxS@-M`W^WL`nx`|c!Ɲ@@OGxS@-M`^`d|"ƝAAOGxC@-M`_dx|*ƝXXzS@-M`gWL`~x`|2*Ɲ@@zS@-M`Og`d|m+ƝAAzC@-M`mhdx|2ƝXXzS@-M`oWL`x`|2Ɲ@@@zS@-M`Vp`d|F4ƝAA@zC@-M`Fqdx|;ƝXXzS@-M`xWL`x`|;Ɲ@@zS@-M`x`d|<ƝAAzC@-M`ydx|XDƝXX@zS@-M`XWL`x`|eDƝ@@zS@-M``d|EƝAAzC@-M`dx|MƝXXzS@-M`WLax`|(MƝ@@zS@-M`n`d|NƝAAzC@-M`dx|UƝXXzS@-M`WLax`|UƝ@@@zS@-M``d|,WƝAA@zC@-M`,dx|(^ƝXXzS@-M`(WLa x`|4^Ɲ@@zS@-M`r`d|_ƝAAzC@-M`dx|fƝXXzS@-M`WLa0x`|fƝ@@zS@-M`-`d|DhƝAAzC@-M`Ddx|oƝXXzS@-M`WLa@x`|oƝ@@@zS@-M``d|pƝAA@zC@-M`dx|SxƝXXzS@-M`SWLaPx`|hxƝ@@zS@-M``d|yƝAAzC@-M`¶dx|ƝXXzS@-M`WLa`x`|Ɲ@@zS@-M`\`d|VƝAAzC@-M`Vdx|ƝXXzS@-M`WLap/x`|ÉƝ@@zS@-M``d|ƝAAzC@-M`dx|gƝXXzS@-M`gWLa?x`|tƝ@@zS@-M``d|ϓƝAAzC@-M`dx|ƝXXzS@-M`WLaOx`|ħƝ@@zS@-M``d|9ƝAAzC@-M`9d`|IƝ@@zS@-M``d|ƝAAzC@-M`dx|0ƝXXzS@-M`0WLaox`|>Ɲ@@zS@-``|Ɲ@@zC@->M``d|eƝAAzC@-M`edx|ƝXXzS@-M`WLax`|Ɲ@@zS@-M``d|ƝAAzC@-M`dx|ƝXXzS@-M` WLax`|Ɲ@@zS@-M` `d|ƝAAzC@-M` dx|8ƝXXzS@-M`8WLax`|FƝ@@zS@-M``d|ƝAAzC@-M`dx|ƝXXzS@-M`WLax`|Ɲ@@zS@-M`E`d|&ƝAAzC@-M`&dx|ƝXXOGxS@-M`%WLbx`|Ɲ@@@NGxS@-M`%`d|ƝAA@NGxC@-M`&dx|ƝXX@NGxS@-M`.WLbx`|Ɲ@@OGxS@-M`;.`d|"ƝAAOGxC@-M`"/dx|ƝXXCGxS@-M`6WLb x`|Ɲ@@@GxS@-M`6`d|ƝAA@GxC@-M`7dx|ǝXX@GxS@-M`>WLb0x`|ǝ@@CGxS@-M`$?`d| ǝAACGxC@-M` @dx|d ǝXXOGxS@-M`dGWLb@x`|r ǝ@@@NGxS@-M`G`d|x ǝAA@NGxC@-M`xHdx|ǝXX@NGxS@-M`OWLbPx`|ǝ@@OGxS@-M`O`d|ǝAAOGxC@-M`Pdx|(ǝXXCGxS@-M`eWLb` x`|(ǝ@@@GxS@-M`3f`d|2*ǝAA@GxC@-M`2gdx|t1ǝXX@GxS@-M`tnWLbp0x`|1ǝ@@CGxS@-M`n`d|2ǝAACGxC@-M`odx|:ǝXXLGxS@-M`wWLb@x`|:ǝ@@@KGxS@-M`-w`d|;ǝAA@KGxC@-M`xdx|BǝXXOGxS@-M`WLbPx`|Bǝ@@@NGxS@-M``d|CǝAA@NGxC@-M`dx|JǝXX@NGxS@-M`WLb`x`|Jǝ@@OGxS@-M``d|GLǝAAOGxC@-M`Gdx|ESǝXX@KGxS@-M`EWLbpx`|OSǝ@@LGxS@-M`m`d|{TǝAALGxC@-M`{dx|[ǝXXCGxS@-M`WLbx`|[ǝ@@@GxS@-M``d|\ǝAA@GxC@-M`dx|dǝXX@GxS@-M`WLbx`|"dǝ@@CGxS@-M`?`d|&eǝAACGxC@-M`&dx|lǝXXLGxS@-M`WLbx`|lǝ@@@KGxS@-M``d|mǝAA@KGxC@-M`dx| uǝXXOGxS@-M` WLbx`|uǝ@@@NGxS@-M`0`d|6vǝAA@NGxC@-M`6dx|}ǝXX@NGxS@-M`WLcx`|}ǝ@@OGxS@-M``d|~ǝAAOGxC@-M`Ļdx|(ǝXX@KGxS@-M`(WLcx`|0ǝ@@LGxS@-M`M`d|[ǝAALGxC@-M`[dx|aǝXXCGxS@-M`aWLc x`|kǝ@@@GxS@-M``d|{ǝAA@GxC@-M`{dx|ǝXX@GxS@-M`WLc0x`|Ɩǝ@@CGxS@-M``d|ǝAACGxC@-M`dx|"ǝXXLGxS@-M`"WLc@x`|)ǝ@@@KGxS@-M`F`d|.ǝAA@KGxC@-M`.dx|ǝXXzS@-M`WLcPx`|ǝ@@zS@-M``d|ǝAAzC@-M`dx|ǝXXzS@-M`WLc`!x`|ǝ@@zS@-M``d|ױǝAAzC@-M`dx|5ǝXXzS@-M`5WLcp1x`|Dǝ@@zS@-M`b`d|[ǝAAzC@-M`[dx|ǝXXzS@-M`WLcAx`|ǝ@@zS@-M``d|ǝAAzC@-M`dx|XǝXX@zS@-M`XWLcQx`|hǝ@@zS@-M``d|ǝAAzC@-M`dx|ǝXXzS@-M`WLcax`| ǝ@@@zS@-M``d|ǝAA@zC@-M`dx|ǝXXzS@-M`WLcqx`|ǝ@@zS@-M``d|ǝAAzC@-M`dx|OǝXXzS@-M`O!WLcx`|^ǝ@@zS@-M`y!`d|ǝAAzC@-M`"dx|ǝXXzS@-M`)WLcx`|ǝ@@@zS@-M`%*`d|)ǝAA@zC@-M`)+dx|rǝXXzS@-M`r2WLcx`|ǝ@@zS@-M`2`d|ǝAAzC@-M`3dx|ǝXX@zS@-M`;WLcx`|(ǝ@@zS@-M`G;`d|PǝAAzC@-M`P<dx|ȝXXzS@-M`CWLdx`|ȝ@@zS@-M`C`d|@ȝAAzC@-M`@Edx|]ȝXXOGxS@-M`]LWLdx`|nȝ@@@NGxS@-M`L`d|ȝAA@NGxC@-M`Mdx|ȝXX@NGxS@-M`UWLd x`|ȝ@@OGxS@-M`:U`d|[ȝAAOGxC@-M`[Vdx| ȝXXGGxS@-M`]WLd0x`| ȝ@@CGxS@-M`]`d|"ȝAACGxC@-M`_dx|)ȝXX@KGxS@-M`fWLd@x`|)ȝ@@LGxS@-M`f`d|*ȝAALGxC@-M`gdx|32ȝXXCGxS@-M`3oWLdPx`|@2ȝ@@@GxS@-M`co`d|3ȝAA@GxC@-M`pdx|:ȝXX@GxS@-M`wWLd`"x`|;ȝ@@CGxS@-M`Ux`d|@<ȝAACGxC@-M`@ydx|CȝXXLGxS@-M`WLdp2x`|Cȝ@@@KGxS@-M`Ҁ`d|DȝAA@KGxC@-M`dx|-LȝXXCGxS@-M`-WLdBx`|9Lȝ@@GGxS@-M`m`d|MȝAAGGxC@-M`dx|TȝXXOGxS@-M`WLdRx`|Tȝ@@@NGxS@-M`#`d|VȝAA@NGxC@-M`dx||]ȝXX@NGxS@-M`|WLdbx`|]ȝ@@OGxS@-M``d| _ȝAAOGxC@-M` dx|hfȝXXzS@-M`hWLdrx`|ufȝ@@@zS@-M`<`d|IhȝAA@zC@-M`Idx|,oȝXXzS@-M`,WLdx`|=oȝ@@zS@-M`z`d|cpȝAAzC@-M`cdx|QxȝXXzS@-M`QWLdx`|`xȝ@@zS@-M`z`d|yȝAAzC@-M`Ӷdx|ȝXXzS@-M`WLdx`|ȝ@@@zS@-M`8`d|FȝAA@zC@-M`Fdx|ȝXXzS@-M`WLdx`|ȝ@@zS@-M``d|ƊȝAAzC@-M`dx|SȝXXzS@-M`SWLex`|cȝ@@zS@-M``d|ȝAAzC@-M`dx|ȝXX@zS@-M`WLex`|ȝ@@zS@-M`f`d|gȝAAzC@-M`gdx|ݣȝXXzS@-M`WLe x`|ȝ@@@zS@-M`H`d|2ȝAA@zC@-M`2dx|ȝXXzS@-M`WLe0x`|ȝ@@zS@-M``d|ЭȝAAzC@-M`dx|pȝXXzS@-M`pWLe@x`|~ȝ@@zS@-M``d|ȝAAzC@-M`dx|PȝXXzS@-M`PWLePx`|]ȝ@@@zS@-M``d|ĿȝAA@zC@-M`dx|ȝXXzS@-M`WLe`#x`|ȝ@@zS@-M`b`d|lȝAAzC@-M`ldx|ȝXX@zS@-M` WLep3x`|ȝ@@zS@-M` `d|ȝAAzC@-M` d`|$ȝ@@GGxS@-M``d|ȝAAGGxC@-M`dx|ȝXX@KGxS@-M`!WLeSx`|ȝ@@LGxS@-M`C"`d|3ȝAALGxC@-M`3#dx|ȝXXCGxS@-M`*WLecx`|ȝ@@@GxS@-M`*`d|ȝAA@GxC@-M`+dx|TȝXXCGxS@-M`T3WLesx`|bȝ@@@GxS@-M`3`d|ȝAA@GxC@-M`4dx|ȝXXLGxS@-M`;WLex`| ȝ@@@KGxS@-M`-<`d|AɝAA@KGxC@-M`A=dx|ɝXXGGxS@-M`DWLex`|ɝ@@CGxS@-M`D`d|ɝAACGxC@-M`Edx|ɝXXOGxS@-M`ZWLex`|ɝ@@@NGxS@-M`Z`d|ɝAA@NGxC@-M`[dx|H&ɝXX@NGxS@-M`HcWLex`|V&ɝ@@OGxS@-M`~c`d|'ɝAAOGxC@-M`ddx|/ɝXXCGxS@-M`lWLfx`|/ɝ@@GGxS@-M`9l`d|0ɝAAGGxC@-M`mdx|7ɝXX@KGxS@-M`tWLfx`|7ɝ@@LGxS@-M`%u`d|]9ɝAALGxC@-M`]vdx|@ɝXX@GxS@-M`}WLf x`|@ɝ@@CGxS@-M`}`d| BɝAACGxC@-M` dx|IɝXXCGxS@-M`WLf0x`|Iɝ@@@GxS@-M``d|JɝAA@GxC@-M`чdx|IRɝXXLGxS@-M`IWLf@x`|WRɝ@@@KGxS@-M`~`d|SɝAA@KGxC@-M`dx|[ɝXXGGxS@-M`WLfPx`|[ɝ@@CGxS@-M`E`d|\ɝAACGxC@-M`dx|cɝXXOGxS@-M`ѠWLf`$x`|cɝ@@@NGxS@-M``d|eɝAA@NGxC@-M`dx|lɝXX@NGxS@-M`WLfp4x`|lɝ@@OGxS@-M``d|mɝAAOGxC@-M`dx|]uɝXXzS@-M`]WLfDx`|luɝ@@zS@-M``d|vɝAAzC@-M`dx|!~ɝXXzS@-M`!WLfTx`|0~ɝ@@@zS@-M`p`d|ɝAA@zC@-M`dx|ɝXXzS@-M`WLfdx`|ɝ@@zS@-M``d|ɝAAzC@-M`dx|ÜɝXXzS@-M`WLftx`|ќɝ@@zS@-M``d|>ɝAAzC@-M`>dx|^ɝXXzS@-M`^WLfx`|mɝ@@@zS@-M``d|ɝAA@zC@-M`dx|ɝXXzS@-M`WLfx`|,ɝ@@zS@-M`l`d|jɝAAzC@-M`jdx|ɝXXzS@-M`WLfx`|ɝ@@zS@-M``d|ɝAAzC@-M`dx|jɝXX@zS@-M`jWLfx`|wɝ@@zS@-M``d|ɝAAzC@-M`dx|CɝXXOGxS@-M`CWLgx`|Oɝ@@CGxS@-M``d|ɝAACGxC@-M`dx|ɝXXCGxS@-M` WLgx`| ɝ@@OGxS@-M`]`d|%ɝAAOGxC@-M`%dx|ɝXXOGxS@-M`WLg x`|ɝ@@CGxS@-M``d|ɝAACGxC@-M`dx|9ɝXXOGxS@-M`9WLg0x`|Eɝ@@GGxS@-M``d|rɝAAGGxC@-M`r dx|ɝXX@KGxS@-M`'WLg@x`|ɝ@@LGxS@-M`'`d|!ɝAALGxC@-M`!)dx|{ɝXX@GxS@-M`{0WLgPx`|ɝ@@CGxS@-M`0`d|ɝAACGxC@-M`1dx|5ɝXXzS@-M`59WLg`%x`|Eɝ@@@zS@-M`9`d|zɝAA@zC@-M`z:dx|ʝXXzS@-M`AWLgp5x`|ʝ@@zS@-M` B`d|ʝAAzC@-M`Cdx|| ʝXXzS@-M`|JWLgEx`| ʝ@@zS@-M`J`d|ʝAAzC@-M`Kdx|>ʝXXzS@-M`>SWLgUx`|Jʝ@@@zS@-M`S`d|ʝAA@zC@-M`Tdx|ʝXXzS@-M`[WLgex`|ʝ@@zS@-M`%\`d| ʝAAzC@-M`]dx|K'ʝXX@zS@-M`KdWLgux`|X'ʝ@@zS@-M`d`d|(ʝAAzC@-M`edx|0ʝXXzS@-M`mWLgx`|0ʝ@@zS@-M``m`d|o1ʝAAzC@-M`ondx|8ʝXXzS@-M`uWLgx`|8ʝ@@@zS@-M`u`d|9ʝAA@zC@-M`vdx|ZAʝXXzS@-M`Z~WLgx`|iAʝ@@zS@-M`~`d|BʝAAzC@-M`dx|JʝXXzS@-M`WLgx`|Jʝ@@zS@-M`Y`d|fʝAAzC@-M`dx| lʝXXzS@-M` WLhx`|lʝ@@@zS@-M`[`d|mʝAA@zC@-M`dx|tʝXXzS@-M`αWLhx`|tʝ@@zS@-M``d|0vʝAAzC@-M`0dx|K}ʝXXzS@-M`KWLh x`|V}ʝ@@zS@-M``d|~ʝAAzC@-M`ϻdx|ʝXX@zS@-M`WLh0x`|ʝ@@zS@-M`a`d|dʝAAzC@-M`ddx|ʝXXzS@-M`WLh@x`|Ɏʝ@@@zS@-M` `d|ʝAA@zC@-M`dx|ʝXXzS@-M`WLhPx`|ʝ@@zS@-M``d|ʝAAzC@-M`dx|ʝXXzS@-M`WLh`&x`| ʝ@@zS@-M`J`d|ZʝAAzC@-M`Zdx|ʝXXzS@-M`WLhp6x`|Ũʝ@@@zS@-M``d|ʝAA@zC@-M`dx|qʝXXzS@-M`qWLhFx`||ʝ@@zS@-M``d|˲ʝAAzC@-M`dx|AʝXX@zS@-M`AWLhVx`|Nʝ@@zS@-M``d|ʝAAzC@-M`dx|ʝXXzS@-M`WLhfx`| ʝ@@zS@-M`H`d|[ʝAAzC@-M`[dx|ʝXXzS@-M`WLhvx`|ʝ@@@zS@-M` `d|ʝAA@zC@-M` dx|tʝXXzS@-M`tWLhx`|ʝ@@zS@-M``d|ʝAAzC@-M`dx|1ʝXXzS@-M`1WLhx`|<ʝ@@zS@-M``d|ʝAAzC@-M`dx|ʝXXzS@-M`"WLhx`|ʝ@@@zS@-M`]#`d|KʝAA@zC@-M`K$dx|ʝXXzS@-M`+WLhx`|ʝ@@zS@-M`+`d|ʝAAzC@-M`,dx|%ʝXXzS@-M`%4WLix`|7ʝ@@zS@-M`y4`d|wʝAAzC@-M`w5dx|ʝXX@zS@-M`<WLix`|ʝ@@zS@-M`+=`d|.˝AAzC@-M`.>dx|˝XXzS@-M`EWLi x`|˝@@@zS@-M`E`d| ˝AA@zC@-M`Gdx|˝XXzS@-M`NWLi0x`|˝@@zS@-M`]N`d|Z˝AAzC@-M`ZOdx|˝XXzS@-M`VWLi@x`|˝@@zS@-M`V`d|˝AAzC@-M`Wd`|J"˝@@zS@-M`c`d|(˝AAzC@-M`edx|b/˝XXzS@-M`blWLi`'x`|m/˝@@zS@-M`l`d|0˝AAzC@-M`mdx|8˝XX@zS@-M`uWLip7x`|8˝@@zS@-M`:u`d|n9˝AAzC@-M`nvdx|N@˝XXzS@-M`N}WLiGx`|Z@˝@@zS@-M`}`d|A˝AAzC@-M`~dx|H˝XXzS@-M`WLiWx`| I˝@@@zS@-M`L`d|?J˝AA@zC@-M`?dx|Q˝XXzS@-M`ʎWLigx`|Q˝@@zS@-M``d|(S˝AAzC@-M`(dx|rZ˝XXCGxS@-M`rWLiwx`|Z˝@@@GxS@-M``d|[˝AA@GxC@-M`dx|c˝XXLGxS@-M`WLix`|+c˝@@@KGxS@-M`R`d|d˝AA@KGxC@-M`dx|k˝XXzS@-M`ѨWLix`|k˝@@zS@-M``d|-m˝AAzC@-M`-dx|t˝XXzS@-M`WLix`|t˝@@@zS@-M`۱`d|u˝AA@zC@-M`Ȳdx|D}˝XX>zS@-M`DWLix`|Y}˝@@@>zS@-zC@->M`v`d|~˝AA@>zC@-M`dx|˝XX>zS@-M`WLjx`|˝@@>zS@-zC@->M`.`d|_˝AA>zC@-M`_dx|˝XX>zS@-M`WLjx`|˝@@>zS@-zC@->M``d|˝AA>zC@-M`dx|r˝XX>zS@-M`rWLj x`|˝@@@>zS@-zC@->M``d|ɘ˝AA@>zC@-M`dx|;˝XX>zS@-M`;WLj0x`|N˝@@@>zS@-zC@->M`i`d|֡˝AA@>zC@-M`dx|Ө˝XX>zS@-M`WLj@x`|˝@@@>zS@-zC@->M``d|]˝AA@>zC@-M`]dx|˝XX@>zS@-M`WLjPx`|˝@@>zS@-zC@->M``d|˝AA>zC@-M`dx|˝XX@>zS@-M`WLj`(x`|˝@@>zS@-zC@->M``d|˝AA>zC@-M`dx|U˝XX@>zS@-M`UWLjp8x`|f˝@@>zS@-zC@->M``d|˝AA>zC@-M`dx|#˝XX>zS@-M`#WLjHx`|4˝@@>zS@-zC@->M`O`d|˝AA>zC@-M`dx|˝XX>zS@-M` WLjXx`|˝@@>zS@-zC@->M` `d| ˝AA>zC@-M` "dx|A˝XX>zS@-M`A)WLjhx`|Q˝@@@>zS@-zC@->M`p)`d|˝AA@>zC@-M`*dx|˝XX>zS@-M`2WLjxx`|˝@@@>zS@-zC@->M`2`d|˝AA@>zC@-M`3dx|˝XX>zS@-M`:WLjx`|˝@@@>zS@-zC@->M`:`d|˝AA@>zC@-M`<dx|̝XX@>zS@-M`CWLjx`|̝@@>zS@-zC@->M`C`d|̝AA>zC@-M`Ddx|̝XX@>zS@-M`LWLjx`|0̝@@>zS@-zC@->M`OL`d|b̝AA>zC@-M`bMdx|̝XX@>zS@-M`TWLjx`|̝@@>zS@-zC@->M`U`d|@̝AA>zC@-M`@Vdx| ̝XX>zS@-M`]WLkx`| ̝@@>zS@-zC@->M`]`d|"̝AA>zC@-M`_dx|i)̝XX>zS@-M`ifWLkx`|y)̝@@>zS@-zC@->M`f`d|*̝AA>zC@-M`gdx|(2̝XX>zS@-M`(oWLk x`|?2̝@@@>zS@-zC@->M`So`d|3̝AA@>zC@-M`pdx|:̝XX>zS@-M`wWLk0x`|:̝@@@>zS@-zC@->M`x`d|<̝AA@>zC@-M`ydx|C̝XX>zS@-M`WLk@ x`|C̝@@@>zS@-zC@->M``d|D̝AA@>zC@-M`ˁdx|L̝XX@>zS@-M`WLkPx`|L̝@@>zS@-zC@->M`.`d|M̝AA>zC@-M`dx|T̝XX@>zS@-M`WLk`)x`|T̝@@>zS@-zC@->M`Ց`d|U̝AA>zC@-M`dx|3]̝XX@>zS@-M`3WLkp9x`|>]̝@@>zS@-``|]̝@@@>zC@->M``d|^̝AA>zC@-M`dx|e̝XX>zS@-M`ҢWLkIx`|e̝@@>zS@-zC@->M`5`d|g̝AA>zC@-M`dx|Un̝XX>zS@-M`UWLkYx`|an̝@@>zS@-zC@->M``d|o̝AA>zC@-M`dx|̝ׄXX>zS@-M`WLkix`|̝@@@>zS@-zC@->M``d|4̝AA@>zC@-M`4dx|̝XXGGxS@-M`WLkyx`|̝@@OGxS@-M` `d|̝AAOGxC@-M`dx|U̝XXCGxS@-M`UWLkx`|`̝@@@NGxS@-M``d|̝AA@NGxC@-M`dx|̝XX@NGxS@-M`WLkx`|̝@@CGxS@-M`t`d|̝AACGxC@-M`dx|̝XXOGxS@-M`WLkx`|̝@@GGxS@-M``d|̝AAGGxC@-M`dx|S̝XX@KGxS@-M`SWLkx`|^̝@@LGxS@-M``d|̝AALGxC@-M`dx|̝XX@GxS@-M`WLlx`| ̝@@CGxS@-M`^`d|I̝AACGxC@-M`Idx|̝XXCGxS@-M`WLlx`|̝@@@GxS@-M``d|B̝AA@GxC@-M`B dx|e̝XX>zS@-M`e WLl x`|r̝@@@>zS@-zC@->M` `d|̝AA@>zC@-M` dx|̝XX>zS@-M` WLl0x`|&̝@@@>zS@-zC@->M`z `d|^̝AA@>zC@-M`^ dx|̝XX@>zS@-M` WLl@ x`|̝@@>zS@-zC@->M`1 `d|:̝AA>zC@-M`: dx|c̝XX@>zS@-M`c! WLlPx`|r̝@@>zS@-zC@->M`! `d|̝AA>zC@-M`" dx|̝XX@>zS@-M`) WLl`*x`|̝@@>zS@-zC@->M`) `d|!̝AA>zC@-M`!+ dx|K̝XXLGxS@-M`K6 WLlp:x`|b̝@@@KGxS@-M`6 `d|m̝AA@KGxC@-M`m7 dx|͝XXGGxS@-M`> WLlJx`|͝@@OGxS@- ``|͝@@GGxC@->M`> `d|͝AAOGxC@-M`? dx|F ͝XXCGxS@-M`FG WLlZx`|V ͝@@@NGxS@-M`sG `d| ͝AA@NGxC@-M`H dx|͝XX@NGxS@-M`O WLljx`|͝@@CGxS@-M`P `d|>͝AACGxC@-M`>Q dx|͝XXOGxS@-M`X WLlzx`|͝@@GGxS@-M`X `d|͝AAGGxC@-M`Y dx|6$͝XX>zS@-M`6a WLlx`|O$͝@@@>zS@-zC@->M`ka `d|z%͝AA@>zC@-M`zb dx|z,͝XX>zS@-M`zi WLlx`|,͝@@@>zS@-zC@->M`i `d|-͝AA@>zC@-M`j dx| 5͝XX@>zS@-M` r WLlx`|5͝@@>zS@-zC@->M`dr `d|Y6͝AA>zC@-M`Ys dx|>͝XX@>zS@-M`{ WLlx`|%>͝@@>zS@-͝@@@>zC@->M`D{ `d|?͝AA>zC@-M`| dx|F͝XX>zS@-M` WLmx`|F͝@@>zS@-zC@->M`) `d|@H͝AA>zC@-M`@ dx|LO͝XX>zS@-M`L WLmx`|[O͝@@@>zS@-zC@->M`| `d|P͝AA@>zC@-M` dx| X͝XX>zS@-M` WLm x`|X͝@@@>zS@-zC@->M`n `d|fY͝AA@>zC@-M`f d`|`͝@@@>zS@-zS@-M` WLm0x`|}e͝@@>zC@->M`} `d|uf͝AA@>zC@-M`u dx|}m͝XX@>zS@-M`} WLm@ x`|m͝@@>zS@-zC@->M` `d|n͝AA>zC@-M` dx|͝XX@>zS@-M` WLmPx`|͝@@>zS@-zC@->M` `d|ӄ͝AA>zC@-M` dx|I͝XX>zS@-M`I WLm`+x`|V͝@@@>zS@-zC@->M`u `d|͝AA@>zC@-M` dx|͝XX@KGxS@-M` WLmp;x`|͝@@LGxS@-M` `d|͝AALGxC@-M` dx|<͝XX@GxS@-M`< WLmKx`|K͝@@CGxS@-M` `d|͝AACGxC@-M` dx|͝XXCGxS@-M` WLm[x`|͝@@@GxS@-M`7 `d|X͝AA@GxC@-M`X dx|͝XXLGxS@-M` WLmkx`|Ǯ͝@@@KGxS@-M` `d|͝AA@KGxC@-M` dx|E͝XXGGxS@-M`E WLm{x`|R͝@@OGxS@-M`q `d|͝AAOGxC@-M` dx| ͝XXCGxS@-M` WLmx`|͝@@@NGxS@-M`; `d|^͝AA@NGxC@-M`^ dx|c͝XX@NGxS@-M`c WLmx`|s͝@@CGxS@-M` `d|͝AACGxC@-M` dx|͝XXOGxS@-M` WLmx`|͝@@GGxS@-M`$ `d|)͝AAGGxC@-M`) dx|͝XX@KGxS@-M` WLmx`|͝@@LGxS@-M` `d|͝AALGxC@-M` dx|1͝XX@GxS@-M`1 WLnx`|>͝@@CGxS@- ``|a͝@@@GxC@->M`a `d|͝AACGxC@-M` dx|͝XXCGxS@-M`' WLnx`| ͝@@@GxS@-M`/( `d|q͝AA@GxC@-M`q) dx|͝XXLGxS@-M`0 WLn x`|!͝@@@KGxS@-M`C0 `d|s͝AA@KGxC@-M`s1 dx|͝XXGGxS@-M`8 WLn0x`|͝@@OGxS@-M`9 `d|5͝AAOGxC@-M`5: dx|tΝXX>zS@-M`tA WLn@ x`|Ν@@@>zS@-zC@->M`A `d|ΝAA@>zC@-M`B dx| ΝXX@>zS@-M`I WLnPx`| Ν@@>zS@-zC@->M`SJ `d|SΝAA>zC@-M`SK dx|ΝXX>zS@-M`R WLn`,x`|Ν@@@>zS@-zC@->M`R `d|ΝAA@>zC@-M`T dx|hΝXX@>zS@-M`h[ WLnp<x`|xΝ@@>zS@-zC@->M`[ `d|ΝAA>zC@-M`\ dx|.'ΝXX>zS@-M`.d WLnLx`|:'Ν@@@>zS@-zC@->M`Zd `d|(ΝAA@>zC@-M`e dx|/ΝXX@>zS@-M`l WLn\x`|0Ν@@>zS@-zC@->M`'m `d|B1ΝAA>zC@-M`Bn dx|8ΝXX>zS@-M`u WLnlx`|8Ν@@@>zS@-zC@->M`u `d|-:ΝAA@>zC@-M`-w dx|sAΝXX@>zS@-M`s~ WLn|x`|AΝ@@>zS@-zC@->M`~ `d|BΝAA>zC@-M` dx|IΝXX>zS@-M` WLnx`|JΝ@@@>zS@-zC@->M`] `d|XKΝAA@>zC@-M`X dx|RΝXX@>zS@-M` WLnx`|RΝ@@>zS@-zC@->M` `d|TΝAA>zC@-M` dx|s[ΝXX>zS@-M`s WLnx`|[Ν@@@>zS@-zC@->M` `d|]ΝAA@>zC@-M` dx|3dΝXX@>zS@-M`3 WLnx`|?dΝ@@>zS@-zC@->M` `d|eΝAA>zC@-M` dx|lΝXX>zS@-M` WLox`|lΝ@@@>zS@-zC@->M` `d|nΝAA@>zC@-M` dx|huΝXX@>zS@-M`h WLox`|wuΝ@@>zS@-zC@->M` `d|vΝAA>zC@-M`ó dx|~ΝXX>zS@-M` WLo x`|~Ν@@@>zS@-zC@->M`\ `d|TΝAA@>zC@-M`T dx|ΝXXMwS@-M` WLo0x`|Ν@@@MwS@-M` `d|ˇΝAA@MwC@-M` dx|PΝXXMwS@-M`P WLo@ x`|_Ν@@@MwS@-M` `d|ΝAA@MwC@-M` dx|ߗΝXX@MwS@-M` WLoPx`|Ν@@@MwS@-M` `d|ΝAA@MwC@-M` dx|ΝXXMwS@-M` WLo`-x`|Ν@@@MwS@-M` `d|¡ΝAA@MwC@-M` dx|1ΝXXFwS@-M`1 WLop=x`|AΝ@@@FwS@-M`_ `d|eΝAA@FwC@-M`e dx|ΝXX@FwS@-M` WLoMx`|DZΝ@@FwS@-M` `d|ΝAAFwC@-M` dx|kΝXX@FwS@-M`k WLo]x`|~Ν@@FwS@-M` `d|zΝAAFwC@-M`z dx|(ΝXX@>zS@-M`( WLomx`|=Ν@@>zS@-zC@->M`g `d|ΝAA>zC@-M` dx|ΝXX@>zS@-M` WLo}x`|Ν@@>zS@-zC@->M` `d|ΝAA>zC@-M` dx|ΝXX>zS@-M` WLox`|Ν@@@>zS@-zC@->M` `d|#ΝAA@>zC@-M`# dx|:ΝXX>zS@-M`: WLox`|GΝ@@>zS@-zC@->M`o `d|ΝAA>zC@-M` dx|ΝXX@>zS@-M`" WLox`|Ν@@>zS@-zC@->M`[# `d|1ΝAA>zC@-M`1$ dx|ΝXX>zS@-M`+ WLox`|Ν@@@>zS@-zC@->M`+ `d| ϝAA@>zC@-M`H dx|ϝXX>zS@-M`M WLpx`|ϝ@@>zS@-zC@->M`M `d|ϝAA>zC@-M`N dx|YϝXX@>zS@-M`YV WLpx`|fϝ@@>zS@-zC@->M`V `d|ϝAA>zC@-M`W dx|"ϝXX>zS@-M`_ WLp x`|,"ϝ@@@>zS@-zC@->M`O_ `d|x#ϝAA@>zC@-M`x` dx|*ϝXX@>zS@-M`g WLp0x`|*ϝ@@>zS@-zC@->M`&h `d|[,ϝAA>zC@-M`[i dx|03ϝXX>zS@-M`0p WLp@x`|>3ϝ@@@>zS@-p ``|e3ϝ@@>zC@->M`ep `d|4ϝAA@>zC@-M`q dx|;ϝXX>zS@-M`x WLpPx`|<ϝ@@>zS@-zC@->M`"y `d|U=ϝAA>zC@-M`Uz dx|DϝXX@>zS@-M` WLp`.x`|Dϝ@@>zS@-zC@->M` `d|FϝAA>zC@-M` dx|MϝXX>zS@-M` WLpp>x`|Mϝ@@@>zS@-zC@->M` `d|NϝAA@>zC@-M`ҋ dx|IVϝXX>zS@-M`I WLpNx`|WVϝ@@>zS@-zC@->M` `d|WϝAA>zC@-M` dx| _ϝXX@>zS@-M` WLp^x`|_ϝ@@>zS@-zC@->M`= `d|p`ϝAA>zC@-M`p dx| uϝXX>zS@-M` WLpnx`|uϝ@@@>zS@-zC@->M`; `d|vϝAA@>zC@-M` dx|}ϝXX@>zS@-M`̺ WLp~x`|}ϝ@@>zS@-zC@->M` `d|.ϝAA>zC@-M`. dx|[ϝXX>zS@-M`[ WLpx`|hϝ@@@>zS@-zC@->M` `d|ϝAA@>zC@-M` dx|ϝXX>zS@-M` WLpx`|)ϝ@@>zS@-zC@->M`L `d|`ϝAA>zC@-M`` dx|ϝXX@>zS@-M` WLpx`|ϝ@@>zS@-zC@->M` `d|jϝAA>zC@-M`j dx|ϝXX>zS@-M` WLpx`|ϝ@@@>zS@-zC@->M` `d|ϝAA@>zC@-M` dx|JϝXX>zS@-M`J WLqx`|Wϝ@@>zS@-zC@->M` `d|ϝAA>zC@-M` d`|ϝ@@@>zS@-zS@-M`c WLqx`|ϝ@@>zC@->M` `d|ϝAA@>zC@-M` dx| ϝXX>zS@-M` WLq x`|-ϝ@@@>zS@-zC@->M`L `d|}ϝAA@>zC@-M`} dx|ϝXX@>zS@-M` WLq0x`|ϝ@@>zS@-zC@->M`W `d|BϝAA>zC@-M`B dx|kϝXX@>zS@-M`k WLq@x`|yϝ@@>zS@-zC@->M` `d|ϝAA>zC@-M` dx|"ϝXX>zS@-M`" WLqPx`|/ϝ@@>zS@-zC@->M`S `d|ϝAA>zC@-M` dx|ϝXXFwS@-M`, WLq`/x`|ϝ@@FwS@-M`- `d|ϝAAFwC@-M`. dx|ϝXX@FwS@-M`5 WLqp?x`|ϝ@@FwS@-M`5 `d|ϝAAFwC@-M`6 dx|FНXX@FwS@-M`F> WLqOx`|XН@@FwS@- ``|wН@@@FwC@->M`w> `d|НAAFwC@-M`? dx| НXX@FwS@-M`G WLq_x`|# Н@@FwS@-M`CG `d|6 НAAFwC@-M`6H dx|НXXFwS@-M`O WLqox`|Н@@FwS@-M`O `d|НAAFwC@-M`P dx|;НXX@FwS@-M`;X WLqx`|VН@@FwS@-M`sX `d|_НAAFwC@-M`_Y dx|#НXXzS@-M`` WLqx`|$Н@@zS@-M`/a `d|%НAAzC@-M`b dx|,НXXzS@-M`i WLqx`|,Н@@zS@-M`j `d|.НAAzC@-M`k dx|G5НXXzS@-M`Gr WLqx`|e5Н@@zS@-M`r `d|6НAAzC@-M`s dx|=НXXzS@-M`z WLqx`|=Н@@zS@-M`z `d|>НAAzC@-M`{ dx|FНXX@zS@-M` WLrx`|FН@@zS@-M`σ `d|GНAAzC@-M`Є dx|{OНXXzS@-M`{ WLrx`|OН@@@zS@-M`͌ `d|QНAA@zC@-M` dx|;XНXX@>zS@-M`; WLr x`|SXН@@>zS@-zC@->M`s `d|YНAA>zC@-M` dx|4nНXX>zS@-M`4 WLr0x`|HnН@@@>zS@-zC@->M`j `d|oНAA@>zC@-M` dx|vНXX>zS@-M` WLr@x`|vН@@>zS@-zC@->M` `d|xНAA>zC@-M` dx|НXX>zS@-M` WLrP x`|Н@@@>zS@-zC@->M`ȼ `d|ڀНAA@>zC@-M`ڽ dx|eНXX>zS@-M`e WLr`0x`|tН@@@>zS@-zC@->M` `d|НAA@>zC@-M` dx|#НXX@>zS@-M`# WLrp@x`|3Н@@>zS@-zC@->M`S `d|hНAA>zC@-M`h dx|НXX@>zS@-M` WLrPx`|Н@@>zS@-zC@->M` `d|lНAA>zC@-M`l dx|НXX>zS@-M` WLr`x`|ĢН@@>zS@-zC@->M` `d|НAA>zC@-M` dx|lНXX@>zS@-M`l WLrpx`|{Н@@>zS@-zC@->M` `d|ҬНAA>zC@-M` dx|5НXX>zS@-M`5 WLrx`|EН@@@>zS@-zC@->M`e `d|НAA@>zC@-M` dx|НXX>zS@-M` WLrx`|Н@@>zS@-zC@->M`= `d|cНAA>zC@-M`c dx|НXX>zS@-M` WLrx`|Н@@@>zS@-zC@->M`: `d|)НAA@>zC@-M`) dx|OНXX>zS@-M`O WLrx`|]Н@@@>zS@-zC@->M` `d|НAA@>zC@-M` d`| Н@@@>zS@-zS@-M`' WLrx`|Н@@>zC@->M`' `d|НAA@>zC@-M`( dx|TНXX@>zS@-M`T0 WLsx`|aН@@>zS@-zC@->M`0 `d|НAA>zC@-M`1 dx|НXX>zS@-M`9 WLsx`|,Н@@>zS@-zC@->M`T9 `d|НAA>zC@-M`: dx|ѝXX@>zS@-M`A WLs x`|ѝ@@>zS@-zC@->M`B `d|PѝAA>zC@-M`PC dx| ѝXX>zS@-M`J WLs0x`| ѝ@@@>zS@-zC@->M`J `d|ѝAA@>zC@-M`L dx|3ѝXX>zS@-M`3S WLs@x`|Aѝ@@>zS@-zC@->M`eS `d|vѝAA>zC@-M`vT dx|ѝXX>zS@-M`[ WLsP!x`|ѝ@@@>zS@-zC@->M` \ `d|b ѝAA@>zC@-M`b] dx||'ѝXX@>zS@-M`|d WLs`1x`|'ѝ@@>zS@-zC@->M`d `d|(ѝAA>zC@-M`e dx|,0ѝXX>zS@-M`,m WLspAx`|90ѝ@@@>zS@-zC@->M`\m `d|1ѝAA@>zC@-M`n dx|8ѝXX@>zS@-M`u WLsQx`|8ѝ@@>zS@-zC@->M` v `d|&:ѝAA>zC@-M`&w dx|AѝXX>zS@-M`~ WLsax`|Aѝ@@>zS@-zC@->M`~ `d|%CѝAA>zC@-M`% dx|4JѝXX@>zS@-M`4 WLsqx`|BJѝ@@>zS@-zC@->M`d `d|KѝAA>zC@-M` dx|RѝXX>zS@-M`ď WLsx`|Rѝ@@@>zS@-zC@->M`S `d|KTѝAA@>zC@-M`K dx|hѝXX>zS@-M`å WLsx`|hѝ@@>zS@-zC@->M`% `d|zC@-M`< dx|YqѝXX>zS@-M`Y WLsx`|hqѝ@@@>zS@-zC@->M` `d|rѝAA@>zC@-M`կ dx|yѝXX@>zS@-M` WLsx`|yѝ@@>zS@-zC@->M` `d|+{ѝAA>zC@-M`+ dx|ѝXXFwS@-M` WLsx`|ѝ@@@FwS@-M` `d|ѝAA@FwC@-M` dx|^ѝXXFwS@-M`^ WLtx`|jѝ@@@FwS@-M` `d|ѝAA@FwC@-M` dx|͓ѝXXFwS@-M` WLtx`|ݓѝ@@FwS@-M` `d|)ѝAAFwC@-M`) dx|ѝXX@FwS@-M` WLt x`|ѝ@@FwS@-M` `d|ѝAAFwC@-M` dx|8ѝXXFwS@-M`8 WLt0x`|Dѝ@@@FwS@-M` `d|ѝAA@FwC@-M` dx|ѝXXFwS@-M` WLt@x`|ѝ@@@FwS@-M`@ `d|UѝAA@FwC@-M`U dx|ѝXXFwS@-M` WLtP"x`|ѝ@@FwS@-M` `d|ѝAAFwC@-M` dx|EѝXX@FwS@-M`E WLt`2x`|Rѝ@@FwS@-M` `d|ѝAAFwC@-M` dx|ѝXXFwS@-M`WLtpBx`|ѝ@@@FwS@-M``d|,ѝAA@FwC@-M`,dx|`ѝXXFwS@-M`` WLtRx`|mѝ@@@FwS@-M` `d|ѝAA@FwC@-M`dx|ѝXXFwS@-M`"WLtbx`|ѝ@@FwS@-M`=#`d|mѝAAFwC@-M`m$dx|ѝXX@FwS@-M`+WLtrx`|ѝ@@FwS@-M`,`d|"ѝAAFwC@-M`"-dx|ѝXXFwS@-M`4WLtx`|*ѝ@@@FwS@-M`p4`d|gѝAA@FwC@-M`g5dx|ѝXXFwS@-M`<WLtx`|ѝ@@@FwS@-M`=`d|!ҝAA@FwC@-M`!>dx|yҝXXFwS@-M`yEWLtx`|ҝ@@FwS@-M`E`d| ҝAAFwC@-M`Fdx|2ҝXX@FwS@-M`2NWLtx`|@ҝ@@FwS@-M`]N`d|ҝAAFwC@-M`Od`|ҝ@@FwS@-M`[`d|<ҝAAFwC@-M`<\dx|L&ҝXXFwS@-M`LcWLux`|[&ҝ@@FwS@-M`c`d|'ҝAAFwC@-M`ddx|.ҝXX@FwS@-M`kWLux`|.ҝ@@FwS@-M`l`d|D0ҝAAFwC@-M`Dmdx|7ҝXX@FwS@-M`tWLu x`|7ҝ@@FwS@-M`t`d|8ҝAAFwC@-M`udx|X@ҝXX@FwS@-M`X}WLu0x`|g@ҝ@@FwS@-M`}`d|AҝAAFwC@-M`~dx|HҝXXzS@-M`WLu@x`|Iҝ@@zS@-M`-`d|]JҝAAzC@-M`]dx|QҝXXzS@-M`WLuP#x`|Qҝ@@zS@-M``d|RҝAAzC@-M`dx|ZҝXXzS@-M`WLu`3x`|"Zҝ@@zS@-M`R`d|[[ҝAAzC@-M`[dx|bҝXXzS@-M`WLupCx`|bҝ@@zS@-M``d|=dҝAAzC@-M`=dx|*kҝXXzS@-M`*WLuSx`|:kҝ@@@zS@-M`k`d|lҝAA@zC@-M`dx|sҝXXzS@-M`WLucx`|sҝ@@@zS@-M``d|uҝAA@zC@-M`dx||ҝXXzS@-M`WLusx`||ҝ@@zS@-M`ι`d|}ҝAAzC@-M`кdx|PҝXXzS@-M`PWLux`|^ҝ@@zS@-M``d|ҝAAzC@-M`dx|ҝXX@zS@-M`WLux`|3ҝ@@zS@-M```d|ҝAAzC@-M`dx|ҝXX@zS@-M`WLux`|ҝ@@zS@-M``d|ҝAAzC@-M`dx|+ҝXXzS@-M`+WLux`|9ҝ@@zS@-M`Y`d|~ҝAAzC@-M`~dx|ҝXXzS@-M`WLux`|ͧҝ@@zS@-M``d|ҝAAzC@-M`dx|ҝXXzS@-M`WLvx`|ҝ@@zS@-M``d|ҝAAzC@-M`dx|ҝXXzS@-M`WLvx`|#ҝ@@zS@-M`N`d|ZҝAAzC@-M`Zdx|ҝXX@zS@-M`WLv x`|ҝ@@zS@-M``d|(ҝAAzC@-M`(dx|ҝXXzS@-M`WLv0x`|ҝ@@@zS@-M``d|ҝAA@zC@-M`dx|QҝXXFwS@-M`QWLv@x`|_ҝ@@FwS@-M``d|ҝAAFwC@-M`dx|ҝXXzS@-M`%WLvP$x`|ҝ@@zS@-M`%&`d|MҝAAzC@-M`M'dx|ҝXXzS@-M`.WLv`4x`|ҝ@@zS@-M`.`d|ҝAAzC@-M`/dx|iҝXXzS@-M`i7WLvpDx`|vҝ@@zS@-M`7`d|ҝAAzC@-M`8dx|'ӝXXzS@-M`'@WLvTx`|4ӝ@@zS@-M`t@`d|ӝAAzC@-M`Adx| ӝXXzS@-M`cWLvdx`| ӝ@@@zS@-M``d| ӝAA@zC@-M`dx|eӝXXzS@-M`%WLvtx`|rӝ@@@zS@-M`r`d|ӝAA@zC@-M`dx|*ӝXXzS@-M`WLvx`|8ӝ@@zS@-M`9`d|ӝAAzC@-M`Pdx|%ӝXXzS@-M` WLvx`|%ӝ@@zS@-M` `d|'ӝAAzC@-M`I"dx|.ӝXX@zS@-M`l)WLvx`|.ӝ@@zS@-M`)`d|0ӝAAzC@-M`*dx|r7ӝXX@zS@-M`22WLvx`|7ӝ@@zS@-M`2`d|8ӝAAzC@-M`3dx|0@ӝXXzS@-M`:WLvx`|=@ӝ@@zS@-M`>;`d|AӝAAzC@-M`N<dx|HӝXXzS@-M`CWLwx`|Hӝ@@zS@-M`C`d|AJӝAAzC@-M`Edx|QӝXXzS@-M`cLWLwx`|Qӝ@@zS@-M`L`d|SӝAAzC@-M`Mdx|;ZӝXXzS@-M`TWLw x`|KZӝ@@zS@-M`/U`d|x[ӝAAzC@-M`8Vdx|bӝXX@zS@-M`d]WLw0x`|bӝ@@zS@-M`]`d|dӝAAzC@-M`^dx|ZkӝXXzS@-M`fWLw@x`|gkӝ@@@zS@-M`Yf`d|lӝAA@zC@-M`Wgdx|sӝXXzS@-M`nWLwP%x`|sӝ@@zS@-M`n`d|-uӝAAzC@-M`odx||ӝXXzS@-M`dwWLw`5x`||ӝ@@zS@-M`w`d|~ӝAAzC@-M`xdx|\ӝXXzS@-M`WLwpEx`|kӝ@@zS@-M`V`d|ӝAAzC@-M`fdx|1ӝXXzS@-M`WLwUx`|Bӝ@@zS@-M`%`d|ӝAAzC@-M`tdx|ӝXXzS@-M`WLwex`|ӝ@@@zS@-M``d|UӝAA@zC@-M`dx|vӝXXzS@-M`6WLwux`|ӝ@@@zS@-M`e`d|ӝAA@zC@-M`dx|3ӝXXzS@-M`WLwx`|>ӝ@@zS@-M`&`d||ӝAAzC@-M`<dx|ӝXXzS@-M`WLwx`|ӝ@@zS@-M``d|XӝAAzC@-M`dx|ӝXX@zS@-M`WLwx`|ӝ@@zS@-M``d|FӝAAzC@-M`dx|ӝXX@zS@-M`WLwx`|-ӝ@@zS@-M`T`d|~ӝAAzC@-M`>dx|ӝXXzS@-M`WLwx`|ӝ@@zS@-M``d|VӝAAzC@-M`dx|ӝXXzS@-M`MWLxx`|ӝ@@zS@-M``d|ӝAAzC@-M`dx|JԝXXzS@-M` WLxx`|Xԝ@@zS@-M`V`d|ԝAAzC@-M`fdx| ԝXXzS@-M`WLx x`| ԝ@@zS@-M``d|R ԝAAzC@-M`dx|ԝXX@zS@-M`nWLx0x`|ԝ@@zS@-M``d|ԝAAzC@-M`dx|tԝXXzS@-M`4WLx@x`|ԝ@@@zS@-M``d|ԝAA@zC@-M`dx|%ԝXXzS@-M`WLxP&x`|%ԝ@@zS@-M`/ `d|q&ԝAAzC@-M`1!dx|-ԝXXzS@-M`(WLx`6x`|-ԝ@@zS@-M`(`d|/ԝAAzC@-M`)dx|~6ԝXXzS@-M`>1WLxpFx`|6ԝ@@zS@-M`1`d|7ԝAAzC@-M`2dx|?ԝXXzS@-M`9WLxVx`|?ԝ@@zS@-M`:`d|x@ԝAAzC@-M`8;dx|GԝXXzS@-M`BWLxfx`|Gԝ@@@zS@-M`B`d|-IԝAA@zC@-M`Cdx|PԝXXzS@-M`JKWLxvx`|Pԝ@@@zS@-M`K`d|QԝAA@zC@-M`Ldx|7YԝXXzS@-M`SWLxx`|DYԝ@@zS@-M`FT`d|ZԝAAzC@-M`AUdx|aԝXXzS@-M`\WLxx`|bԝ@@zS@-M`\`d|XcԝAAzC@-M`^d`|jԝ@@@zS@-M`i`d|pԝAA@zC@-M`jdx|M`Nr`d|xԝAAzC@-M`Tsdx|ԝXXzS@-M`zWLxx`|ԝ@@zS@-ԝ@@zC@->M`z`d|#ԝAAzC@-M`{dx|ԝXXzS@-M`DWLyx`|ԝ@@zS@-M``d|ЉԝAAzC@-M`dx|EԝXXzS@-M`WLyx`|Rԝ@@zS@-M`T`d|ӒԝAAzC@-M`dx| ԝXXzS@-M`ʔWLy x`|ԝ@@zS@-M``d|oԝAAzC@-M`/dx|ԝXX@zS@-M`TWLy0x`|ԝ@@zS@-M``d|ԝAAzC@-M`dx|[ԝXXzS@-M`WLy@x`|jԝ@@@zS@-M`R`d|ԝAA@zC@-M`|dx| ԝXXzS@-M`WLyP'x`|.ԝ@@zS@-M``d|ԝAAzC@-M`Edx|ԝXXzS@-M`WLy`7x`|ԝ@@zS@-M``d|hԝAAzC@-M`(dx|BԝXXzS@-M`WLypGx`|Sԝ@@zS@-M`U`d|ԝAAzC@-M`ddx|ԝXXzS@-M`WLyWx`|'ԝ@@zS@-M``d|eԝAAzC@-M`%dx|ԝXX@FwS@-M`WLygx`|ԝ@@FwS@-M``d|0ԝAAFwC@-M`dx|ԝXX@FwS@-M`PWLywx`|ԝ@@FwS@-M``d| ԝAAFwC@-M`dx|eԝXX@FwS@-M`%WLyx`|qԝ@@FwS@-M`s`d|ԝAAFwC@-M`dx|ԝXXFwS@-M`WLyx`|,ԝ@@FwS@-M`,`d|ԝAAFwC@-M`Adx|՝XX@FwS@-M`WLyx`|՝@@FwS@-M``d|E՝AAFwC@-M`dx|՝XX@FwS@-M`h WLyx`|՝@@FwS@-M` `d|՝AAFwC@-M` dx|m՝XX@FwS@-M`-WLyx`|{՝@@FwS@-M`}`d|՝AAFwC@-M`zdx|+ ՝XXFwS@-M`WLzx`|; ՝@@FwS@-M``d|M!՝AAFwC@-M` dx|(՝XX@FwS@-M`#WLzx`|(՝@@FwS@-M`#`d|D*՝AAFwC@-M`%dx|1՝XX@FwS@-M`n,WLz x`|1՝@@FwS@-M`,`d| 3՝AAFwC@-M`-dx|s:՝XX@FwS@-M`35WLz0x`|:՝@@FwS@-M`5`d|;՝AAFwC@-M`6dx|B՝XXFwS@-M`=WLz@x`| C՝@@FwS@-M` >`d|RD՝AAFwC@-M`?dx|K՝XX@FwS@-M`IFWLzP(x`|K՝@@FwS@-M`F`d|L՝AAFwC@-M`Gdx|MT՝XX@FwS@-M` OWLz`8x`|YT՝@@FwS@-M`ZO`d|U՝AAFwC@-M`nPdx|]՝XX@FwS@-M`WWLzpHx`|]՝@@FwS@-M`X`d|^՝AAFwC@-M`DYdx|e՝XXFwS@-M``WLzXx`|e՝@@FwS@-M```d| g՝AAFwC@-M`adx|Zn՝XX@FwS@-M`iWLzhx`|gn՝@@FwS@-M`Gi`d|o՝AAFwC@-M``jdx|w՝XX@FwS@-M`qWLzxx`|%w՝@@FwS@-M`#r`d|xx՝AAFwC@-M`8sdx|՝XX@FwS@-M`zWLzx`|՝@@FwS@-M`z`d|F՝AAFwC@-M`|dx|՝XXFwS@-M`RWLzx`|՝@@FwS@-M``d|׉՝AAFwC@-M`dx|%՝XX@FwS@-M`WLzx`|2՝@@FwS@-M`2`d|՝AAFwC@-M`Adx|՝XX@FwS@-M`WLzx`|՝@@FwS@-M``d|;՝AAFwC@-M`dx|՝XX@FwS@-M`lWLzx`|՝@@FwS@-M``d|0՝AAFwC@-M`dx|]՝XXFwS@-M`WL{x`|h՝@@FwS@-M`j`d|֬՝AAFwC@-M`dx|,՝XX@FwS@-M`WL{x`|6՝@@FwS@-M`8`d|o՝AAFwC@-M`/dx|՝XX@FwS@-M`WL{ x`|՝@@FwS@-M`η`d|0՝AAFwC@-M`dx|՝XX@FwS@-M``WL{0 x`|՝@@FwS@-M``d|՝AAFwC@-M`dx|W՝XXFwS@-M`WL{@x`|b՝@@FwS@-M`n`d|՝AAFwC@-M`dx|՝XX@FwS@-M`WL{P)x`|)՝@@FwS@-M`+`d|x՝AAFwC@-M`8dx|՝XX@FwS@-M`WL{`9x`|՝@@FwS@-M``d|՝AAFwC@-M`dx|՝XX@FwS@-M`GWL{pIx`|՝@@FwS@-M``d|՝AAFwC@-M`dx|>՝XXFwS@-M`WL{Yx`|I՝@@FwS@-M`N`d|՝AAFwC@-M`bdx|՝XX@FwS@-M`WL{ix`|՝@@FwS@-M``d|(՝AAFwC@-M`dx|֝XX@FwS@-M`@WL{yx`|֝@@FwS@-M``d|֝AAFwC@-M`dx|? ֝XX@FwS@-M`WL{x`|K ֝@@FwS@-M`N`d|y ֝AAFwC@-M`9dx|֝XXFwS@-M`WL{x`|֝@@FwS@-M``d|֝AAFwC@-M`dx|z֝XX@FwS@-M`:WL{x`|֝@@FwS@-M`f`d|֝AAFwC@-M`wdx|$֝XX@FwS@-M`WL{x`|%֝@@FwS@-M``d|&֝AAFwC@-M` dx|q-֝XX@FwS@-M`1(WL{x`|-֝@@FwS@-M`_(`d|.֝AAFwC@-M`F)dx|6֝XXFwS@-M`0WL|x`|6֝@@FwS@-M`0`d|?7֝AAFwC@-M`1dx|L֝XXzS@-M`qGWL|x`|L֝@@zS@-M`G`d|M֝AAzC@-M`Hdx|`U֝XXzS@-M` PWL| x`|nU֝@@zS@-M`MP`d|V֝AAzC@-M`UQdx|^֝XXzS@-M`XWL|0 x`|$^֝@@zS@-M`Y`d|_֝AAzC@-M`KZdx|f֝XXovS@-M`aWL|@x`|f֝@@@ovS@-M`a`d|.h֝AA@ovC@-M`bdx|vo֝XX@ovS@-M`6jWL|P*x`|o֝@@ovS@-M`j`d|p֝AAovC@-M`kdx|/x֝XXovS@-M`rWL|`:x`|M`M`"|`d|\֝AAovC@-M`}dx|֝XXovS@-M`yWL|Zx`|ʼn֝@@@ovS@-M`Ʉ`d|@֝AA@ovC@-M`dx|}֝XX@ovS@-M`=WL|jx`|֝@@ovS@-M``d|ē֝AAovC@-M`d`|֝@@ovS@-M`r`d|Ġ֝AAovC@-M`dx|֝XXovS@-M`WL|x`|֝@@@ovS@-M``d|H֝AA@ovC@-M`dx|֝XX@ovS@-M`lWL|x`|֝@@ovS@-M``d|%֝AAovC@-M`dx|:֝XXovS@-M`WL|x`|G֝@@@ovS@-M`S`d|֝AA@ovC@-M`Ydx|֝XX@ovS@-M`hWL|x`|֝@@ovS@-M``d|֝AAovC@-M`½dx|2֝XXovS@-M`WL|x`|?֝@@@ovS@-M`H`d|֝AA@ovC@-M`Sdx|֝XX@ovS@-M`WL}x`|֝@@ovS@-M``d|2֝AAovC@-M`dx|֝XXovS@-M`KWL}x`|֝@@@ovS@-M``d|֝AA@ovC@-M`dx|֝XX@ovS@-M`WL} x`| ֝@@ovS@-M` `d|V֝AAovC@-M`dx|֝XXovS@-M`yWL}0 x`|֝@@@ovS@-M``d|'֝AA@ovC@-M`dx|g֝XX@ovS@-M`'WL}@x`|{֝@@ovS@-M``d|֝AAovC@-M`dx| םXXovS@-M`DWL}P+x`| ם@@@ovS@-M``d| םAA@ovC@-M`dx|+םXX@ovS@-M`WL}`;x`|;ם@@ovS@-M`?`d|xםAAovC@-M`8dx|םXXovS@-M`WL}pKx`|ם@@@ovS@-M``d|aםAA@ovC@-M`!dx|%םXX@ovS@-M`K WL}[x`|%ם@@ovS@-M` `d|&םAAovC@-M`!dx|.םXXovS@-M`(WL}kx`|0.ם@@@ovS@-M`8)`d|/םAA@ovC@-M`a*dx|6םXX@ovS@-M`@1WL}{x`|6ם@@ovS@-M`o1`d|7םAAovC@-M`2dx|K?םXXovS@-M` :WL}x`|Z?ם@@@ovS@-M`\:`d|@םAA@ovC@-M`m;dx|GםXX@ovS@-M`BWL}x`|Gם@@ovS@-M`B`d|AIםAAovC@-M`Ddx|PםXXovS@-M`PKWL}x`|Pם@@@ovS@-M`K`d|QםAA@ovC@-M`wLdx|`YםXX@ovS@-M` TWL}x`|pYם@@ovS@-M`wT`d|ZםAAovC@-M`yUdx|aםXXovS@-M`\WL}x`|aם@@@ovS@-M`\`d|bםAA@ovC@-M`]dx|jםXX@ovS@-M`veWL~x`|jם@@ovS@-M`e`d|lםAAovC@-M`fdx|9sםXX@ovS@-M`mWL~x`|Fsם@@ovS@-M`Cn`d|tםAAovC@-M`Eodx||םXXovS@-M`vWL~ x`||ם@@@ovS@-M`w`d|w}םAA@ovC@-M`7xdx|םXXovS@-M`bWL~0 x`|ם@@ovS@-M``d|םAAovC@-M`dx|oםXXovS@-M`/WL~@x`||ם@@ovS@-M`|`d|َםAAovC@-M`dx|/םXXovS@-M`WL~P,x`|?ם@@ovS@-M`!`d|fםAAovC@-M`&dx|םXXovS@-M`WL~`<x`|ם@@ovS@-M`ҙ`d|(םAAovC@-M`dx|םXXovS@-M`dWL~pLx`|ם@@@ovS@-M``d|םAA@ovC@-M`dx|FםXXMwS@-M`WL~\x`|Xם@@MwS@-M`6`d|dםAAMwC@-M`$dx|םXXMwS@-M`WL~lx`|ם@@MwS@-M``d|םAAMwC@-M`ݴdx|םXXMwS@-M`vWL~|x`|ם@@MwS@-M``d|םAAMwC@-M`dx|lםXXMwS@-M`,WL~x`|yם@@MwS@-M`[`d|םAAMwC@-M`_dx| םXXMwS@-M`WL~x`|ם@@MwS@-M``d|8םAAMwC@-M`dx|םXXMwS@-M`|WL~x`|ם@@MwS@-M``d|םAAMwC@-M`dx||םXXMwS@-M`<WL~x`|ם@@MwS@-M``d|םAAMwC@-M`dx|2םXXMwS@-M`WL~x`|Gם@@MwS@-M`N`d|םAAMwC@-M`^dx|؝XXMwS@-M`AWLx`|؝@@MwS@-M`p`d|؝AAMwC@-M`dx| ؝XXMwS@-M`WLx`| ؝@@MwS@-M`+`d|c ؝AAMwC@-M`#dx|؝XXMwS@-M`|WL x`|؝@@MwS@-M``d|/؝AAMwC@-M`dx|<؝XXMwS@-M`WL0 x`|I؝@@MwS@-M`I`d|؝AAMwC@-M`Xdx|&؝XXMwS@-M` WL@x`|&؝@@MwS@-M`!`d|V'؝AAMwC@-M`"dx|.؝XXMwS@-M``)WLP-x`|.؝@@MwS@-M`)`d|/؝AAMwC@-M`*dx|g7؝XXMwS@-M`'2WL`=x`|v7؝@@MwS@-M`z2`d|8؝AAMwC@-M`3dx|3@؝XXMwS@-M`:WLpMx`|A@؝@@MwS@-M`G;`d|A؝AAMwC@-M``<dx|H؝XXMwS@-M`CWL]x`|I؝@@MwS@-M`D`d|GJ؝AAMwC@-M`Edx|Q؝XXMwS@-M`LWLmx`|Q؝@@MwS@-M`L`d|7S؝AAMwC@-M`Mdx|Z؝XXMwS@-M`WUWL}x`|Z؝@@MwS@-M`U`d|\؝AAMwC@-M`Vdx|Pc؝XXMwS@-M`^WLx`|]c؝@@MwS@-M`_^`d|d؝AAMwC@-M`q_dx| l؝XXMwS@-M`fWLx`|l؝@@MwS@-M`g`d|jm؝AAMwC@-M`*hdx|t؝XXMwS@-M`[oWLx`|t؝@@MwS@-M`o`d|u؝AAMwC@-M`pdx|Z}؝XXMwS@-M`xWLx`|g}؝@@MwS@-M`hx`d|~؝AAMwC@-M`mydx|؝XXMwS@-M`ۀWLx`|(؝@@MwS@-M`G`d|؝AAMwC@-M`zdx|؝XXMwS@-M`WPx`|؝@@MwS@-M``d|؝AAMwC@-M`dx|؝XXMwS@-M`WP x`|؝@@MwS@-M``d|r؝AAMwC@-M`2dx|؝XXMwS@-M`WP x`|؝@@MwS@-M``d|;؝AAMwC@-M`dx|؝XXMwS@-M`OWP0x`|؝@@MwS@-M``d|؝AAMwC@-M`dx|=؝XXMwS@-M`WP@x`|I؝@@MwS@-M`H`d|؝AAMwC@-M`Hdx|؝XXMwS@-M`WPPZblx`|؝@@MwS@-M``d|3؝AAMwC@-M`dx|ٝXXMwS@-M`kWP` bx`|ٝ@@MwS@-M``d|ٝAAMwC@-M`dx|hٝXXMwS@-M`( WPpx`|tٝ@@MwS@-M`r `d|ٝAAMwC@-M` d`|6ٝ@@ovS@-M`z`d|ٝAAovC@-M`vdx|.$ٝXXovS@-M`WP l x`|;$ٝ@@ovS@-M`<`d|u%ٝAAovC@-M`5 dx|,ٝXXovS@-M`'WP x`|,ٝ@@ovS@-M`'`d|?.ٝAAovC@-M`(dx|5ٝXXovS@-M`0WP *x`|5ٝ@@ovS@-M`0`d|7ٝAAovC@-M`1dx|>ٝXXovS@-M`B9WP Zbx`|>ٝ@@ovS@-ٝ@@ovC@->M`m9`d|?ٝAAovC@-M`[:dx|GٝXXovS@-M`AWPlx`|Gٝ@@@ovS@-M` B`d|=HٝAA@ovC@-M`Bdx|OٝXXovS@-M`JWP Yx`|Oٝ@@ovS@-M`J`d|=QٝAAovC@-M`Kdx|XٝXX@ovS@-M`\SWPcx`|Xٝ@@ovS@-M`S`d|GaٝAAovC@-M`\dx|%jٝXXovS@-M`dWPsx`|3jٝ@@@ovS@-M`4e`d|kٝAA@ovC@-M`Ffdx| sٝXXovS@-M`mWP x`|sٝ@@ovS@-M`m`d|*tٝAAovC@-M`ndx|{ٝXX@ovS@-M`vWP0x`|{ٝ@@ovS@-M`v`d|D}ٝAAovC@-M`xdx|IٝXXovS@-M` WP@x`|Vٝ@@ovS@-M`X`d|ٝAAovC@-M`ndx|ٝXXovS@-M`·WPPx`|ٝ@@ovS@-M``d|pٝAAovC@-M`0dx|ٝ̕XXovS@-M`WP`x`|ٕٝ@@ovS@-M`ِ`d|;ٝAAovC@-M`dx|ٝXXovS@-M`OWPpx`|ٝ@@ovS@-M``d|ٝAAovC@-M`Κdx|ٝXXovS@-M`WPx`|ٝ@@@ovS@-M``d|`ٝAA@ovC@-M` dx|ɾٝXX@ovS@-M`WPx`|׾ٝ@@ovS@-M``d|ٝAAovC@-M`׺dx|TٝXXovS@-M`WPx`|`ٝ@@ovS@-M``d|ٝAAovC@-M`dx|ٝXXovS@-M`WPx`|!ٝ@@ovS@-M`"`d|]ٝAAovC@-M`dx|ٝXXovS@-M`WP#x`|ٝ@@ovS@-M``d|RٝAAovC@-M`dx|ٝXXovS@-M`BWP3x`|ٝ@@ovS@-M``d|ٝAAovC@-M`dx|IٝXXovS@-M` WPCx`|Vٝ@@@ovS@-M`U`d|ٝAA@ovC@-M`Wdx|ٝXXovS@-M`WPSx`|ٝ@@ovS@-M``d|PٝAAovC@-M`dx|ٝXX@ovS@-M`QWPdx`|ٝ@@ovS@-M``d|ٝAAovC@-M`dx|DڝXXovS@-M`WPtx`|Pڝ@@@ovS@-M`P`d|ڝAA@ovC@-M`ldx| ڝXXovS@-M`WP x`| ڝ@@ovS@-M``d|CڝAAovC@-M` dx|ڝXX@ovS@-M`WWP0x`|ڝ@@ovS@-M``d|ڝAAovC@-M`dx|CڝXXovS@-M`WP@x`|Oڝ@@ovS@-M`S`d|ڝAAovC@-M`Udx|&ڝXXovS@-M`!WPPx`| 'ڝ@@ovS@-M`""`d|p(ڝAAovC@-M`0#dx|/ڝXXovS@-M`~*WP`x`|/ڝ@@ovS@-M`*`d|1ڝAAovC@-M`+dx|H8ڝXXovS@-M`3WPpx`|X8ڝ@@ovS@-M`U3`d|9ڝAAovC@-M`s4dx|@ڝXXovS@-M`;WPx`|@ڝ@@@ovS@-M`;`d|?BڝAA@ovC@-M`<dx|IڝXX@ovS@-M`UDWPx`|Iڝ@@ovS@-M`D`d|JڝAAovC@-M`Edx|=RڝXXovS@-M`LWPx`|HRڝ@@ovS@-M`JM`d|SڝAAovC@-M`XNdx|ZڝXXovS@-M`UWPx`|Zڝ@@ovS@-M`U`d|B\ڝAAovC@-M`Wdx|icڝXXovS@-M`)^WP$x`|tcڝ@@ovS@-M`q^`d|dڝAAovC@-M`o_dx|!lڝXXovS@-M`fWP4x`|,lڝ@@ovS@-M`;g`d|dmڝAAovC@-M`$hdx|tڝXXovS@-M`oWPDx`|tڝ@@@ovS@-M`o`d|uڝAA@ovC@-M`pdx|j}ڝXXovS@-M`*xWPTx`|u}ڝ@@ovS@-M`Wx`d|~ڝAAovC@-M`ydx|ڝXX@ovS@-M`ހWPex`|+ڝ@@ovS@-M`)`d|ڝAAovC@-M`Idx|ӎڝXXovS@-M`WPux`|ڝ@@@ovS@-M`݉`d|ڝAA@ovC@-M`ۊdx|ڝXXovS@-M`SWP x`|ڝ@@ovS@-M``d|YڝAAovC@-M`dx|ڝXX@ovS@-M`ߚWP0x`|+ڝ@@ovS@-M`(`d|ڝAAovC@-M`Idx|ިڝXXovS@-M`WP@x`|ڝ@@ovS@-M``d|@ڝAAovC@-M`dx|gڝXXovS@-M`'WPPx`|rڝ@@ovS@-M`q`d|̲ڝAAovC@-M`dx|ڝXXovS@-M`WP`x`|ڝ@@ovS@-M`ִ`d|)ڝAAovC@-M`dx|ڝXXovS@-M`_WPpx`|ڝ@@ovS@-M``d|ڝAAovC@-M`dx|VڝXXovS@-M`WPx`|cڝ@@@ovS@-M`e`d|ڝAA@ovC@-M`ydx|ڝXX@ovS@-M`WPx`|ڝ@@ovS@-M``d|bڝAAovC@-M`"dx|ڝXXovS@-M`WPx`|ڝ@@ovS@-M``d|ڝAAovC@-M`dx|ڝXXovS@-M`NWPx`|ڝ@@ovS@-M``d|ڝAAovC@-M`dx|JڝXXovS@-M` WP%x`|Uڝ@@ovS@-M`V`d|ڝAAovC@-M`fdx|ڝXXovS@-M`WP5x`|ڝ@@ovS@-M`4`d|ڝAAovC@-M`Ldx|ڝXXovS@-M`VWPEx`|ڝ@@@ovS@-M``d|۝AA@ovC@-M`dx|O۝XXovS@-M`WPUx`|Z۝@@ovS@-M`[`d| ۝AAovC@-M`]dx| ۝XX@ovS@-M` WPfx`|۝@@ovS@-M` `d|o۝AAovC@-M`/ dx|۝XXovS@-M`WPvx`|۝@@@ovS@-M``d|L۝AA@ovC@-M` dx|31۝XXMwS@-M`+WP x`|?1۝@@MwS@-M`=,`d|2۝AAMwC@-M`g-dx|9۝XXMwS@-M`t4WP0x`|9۝@@MwS@-M`4`d|:۝AAMwC@-M`5dx|bB۝XXMwS@-M`"=WP@x`|mB۝@@MwS@-M`j=`d|C۝AAMwC@-M`q>dx|K۝XXMwS@-M`EWPPx`|&K۝@@MwS@-M`#F`d|bL۝AAMwC@-M`"Gdx|S۝XXMwS@-M`NWP`x`|S۝@@MwS@-M`N`d|4U۝AAMwC@-M`Od`|q\۝@@MwS@-M`[`d| b۝AAMwC@-M`\dx|%i۝XXMwS@-M`cWPx`|6i۝@@MwS@-M`d`d|j۝AAMwC@-M`Hedx|q۝XXMwS@-M`lWPx`|q۝@@MwS@-M`l`d|s۝AAMwC@-M`mdx|z۝XXMwS@-M`IuWPx`|z۝@@MwS@-M`u`d|{۝AAMwC@-M`vdx|@۝XXMwS@-M`~WPx`|O۝@@MwS@-M`Q~`d|Ƅ۝AAMwC@-M`dx|۝XXMwS@-M`~WP&x`|ʋ۝@@MwS@-M`Ά`d|۝AAMwC@-M`܇dx|r۝XXMwS@-M`2WP6x`|۝@@MwS@-M`~`d|۝AAMwC@-M`dx|۝XXMwS@-M`IWPFx`|۝@@MwS@-M``d|۝AAMwC@-M`dx|E۝XXMwS@-M`WPVx`|S۝@@MwS@-M`0`d|h۝AAMwC@-M`(dx|۝XXMwS@-M`WPWx`|۝@@MwS@-M``d|>۝AAMwC@-M`dx|۝XXMwS@-M`gWPgx`|۝@@MwS@-M``d|۝AAMwC@-M`dx|8۝XXMwS@-M`WP wx`|E۝@@MwS@-M`&`d|۝AAMwC@-M`?dx|۝XXMwS@-M`rWP0x`|۝@@MwS@-M``d| ۝AAMwC@-M`dx|Q۝XXMwS@-M`WP@x`|^۝@@MwS@-M`c`d|۝AAMwC@-M`ndx|۝XXMwS@-M`WPPx`|۝@@MwS@-M``d|X۝AAMwC@-M`dx|۝XXMwS@-M`[WP`x`|۝@@MwS@-M``d|۝AAMwC@-M`dx|>۝XXMwS@-M`WPpx`|K۝@@MwS@-M`O`d|۝AAMwC@-M`idx|ܝXXMwS@-M`WPx`|ܝ@@MwS@-M``d|XܝAAMwC@-M`dx|? ܝXXMwS@-M` WPx`|O ܝ@@MwS@-M`X `d|V ܝAAMwC@-M` dx|ܝXXMwS@-M` WPx`|ܝ@@MwS@-M` `d|WܝAAMwC@-M` dx|ܝXXMwS@-M` WPx`|ܝ@@MwS@-M` `d|mܝAAMwC@-M`- dx|h#ܝXXMwS@-M`( WPx`|#ܝ@@MwS@-M`_ `d|$ܝAAMwC@-M`e dx|+ܝXXovS@-M`& WP'x`|+ܝ@@ovS@-M`& `d|-ܝAAovC@-M`' dx|"4ܝXX@ovS@-M`. WP7x`|84ܝ@@ovS@-M`/ `d|O5ܝAAovC@-M`0 dx|<ܝXXMwS@-M`E7 WPGx`|<ܝ@@MwS@-M`u7 `d|=ܝAAMwC@-M`s8 dx|DܝXXMwS@-M`? WPOpen GD77x`|Dܝ@@MwS@-M`? `d|FܝAAMwC@-M`@ dx|MܝXX@MwS@-M`IH WPDM3MATx`|Mܝ@@MwS@-M`tH `d|NܝAAMwC@-M`\I dx|VܝXX@MwS@-M`P WP 12345678,x`|Vܝ@@@MwS@-M` Q `d|wWܝAA@MwC@-M`7R dx|^ܝXX@MwS@-M`KY WPhx`|^ܝ@@@MwS@-M`Y `d|rܝAA@MwC@-M`m dx|wܝXXMwS@-M`r WP x`|wܝ@@@MwS@-M`r `d| yܝAA@MwC@-M`s dx|iܝXX@>zS@-M`){ WP  x`|~ܝ@@>zS@-{ ``|ܝ@@@>zC@->M`X{ `d|ܝAA>zC@-M`| dx|ܝXX>zS@-M`ԃ WP0x`|"ܝ@@@>zS@-zC@->M` `d|{ܝAA@>zC@-M`; dx|ܝXX@>zS@-M`R WP@:x`|ܝ@@>zS@-zC@->M` `d|ߒܝAA>zC@-M` dx|ܝXX>zS@-M`n WPPx`|ܝ@@@>zS@-zC@->M` `d| ܝAA@>zC@-M`ͣ dx|VܝXX@>zS@-M` WP`x`|eܝ@@>zS@-zC@->M`N `d|ܝAA>zC@-M`~ dx|ܝXX>zS@-M`ܳ WPpx`|*ܝ@@@>zS@-zC@->M` `d|~ܝAA@>zC@-M`> dx|ܝXX@>zS@-M` WPx`|ܝ@@>zS@-zC@->M`м `d|7ܝAA>zC@-M` dx|ܝXX>zS@-M`O WPx`|ܝ@@@>zS@-zC@->M` `d|3ܝAA@>zC@-M` dx|QܝXX@>zS@-M` WPtx`|^ܝ@@>zS@-zC@->M`= `d|ܝAA>zC@-M` dx|hܝXX>zS@-M`( WPx`|tܝ@@@>zS@-zC@->M` `d|ܝAA@>zC@-M`} dx|*ܝXX@>zS@-M` WP x`|9ܝ@@>zS@-zC@->M` `d|tܝAA>zC@-M`4 dx|ܝXX>zS@-M` WP0x`|ܝ@@@>zS@-zC@->M` `d|PܝAA@>zC@-M` dx|ܝXX@>zS@-M`n WPx`|ܝ@@>zS@-zC@->M` `d|_ݝAA>zC@-M` dx|ݝXX>zS@-M` WPx`|ݝ@@@>zS@-zC@->M` `d|ݝAA@>zC@-M` dx|rݝXX@>zS@-M`2 WPx`|ݝ@@>zS@-zC@->M` `d|ݝAA>zC@-M`y dx|8ݝXX>zS@-M` WPvx`|Dݝ@@@>zS@-zC@->M`( `d|ݝAA@>zC@-M`c dx|T%ݝXX@>zS@-M` WPx`|b%ݝ@@>zS@-zC@->M`G `d|&ݝAA>zC@-M`u! dx|-ݝXX>zS@-M`( WP  Zbx`|-ݝ@@@>zS@-zC@->M`) `d|1/ݝAA@>zC@-M`) dx|6ݝXX@>zS@-M`b1 WP0l'x`|6ݝ@@>zS@-zC@->M`1 `d|7ݝAA>zC@-M`2 dx|c?ݝXX>zS@-M`#: WP@x`|o?ݝ@@@>zS@-zC@->M`S: `d|@ݝAA@>zC@-M`; dx|GݝXX@>zS@-M`B WPPx`|Gݝ@@>zS@-zC@->M`B `d|KIݝAA>zC@-M` D dx| PݝXX>zS@-M`J WP`x`|Pݝ@@@>zS@-zC@->M`(K `d|QݝAA@>zC@-M`@L dx|XݝXX@>zS@-M`S WPpx`|Xݝ@@>zS@-zC@->M`S `d|2ZݝAA>zC@-M`T dx|VaݝXX>zS@-M`\ WPx`|caݝ@@@>zS@-zC@->M`G\ `d|bݝAA@>zC@-M`b] dx|jݝXX@>zS@-M`d WPx`|jݝ@@>zS@-zC@->M`0e `d|dkݝAA>zC@-M`$f dx|brݝXX>zS@-M`"m WPx`|nrݝ@@@>zS@-zC@->M`pm `d|sݝAA@>zC@-M`Vn dx|${ݝXX@>zS@-M`u WP&x`|0{ݝ@@>zS@-zC@->M`9v `d||ݝAA>zC@-M`Tw dx|ݝXX>zS@-M`~ WP6x`|ݝ@@@>zS@-zC@->M`  `d|ݝAA@>zC@-M` dx|ݝXX>zS@-M`` WPFx`|ݝ@@>zS@-zC@->M` `d|ݝAA>zC@-M` dx|ݝXX@{DwS@-M`` WPVx`|ݝ@@@~DwS@-M`ҝ `d| ݝAA@~DwC@-M` dx|OݝXX@~DwS@-M` WPfx`|]ݝ@@@{DwS@-M`Z `d|ݝAA@{DwC@-M`p dx|ݝXX@{DwS@-M` WTKW Aix`|ݝ@@@~DwS@-M` `d|ݝAA@~DwC@-M` dx|SݝXXovS@-M` WTvx`|gݝ@@ovS@-M`H `d|tݝAAovC@-M`4 dx|ݝXXovS@-M` WT KW Bx`|ݝ@@ovS@-M` `d|ݝAAovC@-M` dx|ݝXXovS@-M`c WT0x`|ݝ@@@ovS@-M` `d|ޝAA@ovC@-M` dx|#ޝXXovS@-M` WT@TH Wildaux`|:ޝ@@ovS@-M`\ `d| ޝAAovC@-M`c dx||ޝXX@ovS@-M`< WTPx`|ޝ@@ovS@-M` `d|ޝAAovC@-M` dx|ޝXXovS@-M` WT`Hessen Ax`|ޝ@@ovS@-M`  `d|EޝAAovC@-M` dx|x!ޝXXovS@-M`8 WTpx`|!ޝ@@ovS@-M`z `d|"ޝAAovC@-M`z dx|!*ޝXXovS@-M`$ WTHessen Bx`|.*ޝ@@ovS@-M`% `d|l+ޝAAovC@-M`,& dx|2ޝXXovS@-M`- WTx`|2ޝ@@@ovS@-M`S. `d|t4ޝAA@ovC@-M`4/ dx|u;ޝXXovS@-M`56 WTBerlin Ax`|;ޝ@@ovS@-M`6 `d|<ޝAAovC@-M`7 dx|6DޝXX@ovS@-M`> WTx`|CDޝ@@ovS@-M`C? `d|EޝAAovC@-M`G@ dx|LޝXXovS@-M`G WTBerlin Bx`|Lޝ@@@ovS@-M`G `d|NޝAA@ovC@-M`H dx|UޝXXovS@-M`BP WT6x`|Uޝ@@ovS@-M`P `d|WޝAAovC@-M`Q dx|C^ޝXX@ovS@-M`Y WTPotsdam Ax`|O^ޝ@@ovS@-M`PY `d|_ޝAAovC@-M`WZ dx| gޝXXovS@-M`a WTVx`|gޝ@@ovS@-M`b `d|ehޝAAovC@-M`%c dx|oޝXXovS@-M`j WTPotsdam Bx`|oޝ@@ovS@-M`j `d|0qޝAAovC@-M`k dx|xޝXXovS@-M`Ns WTwx`|xޝ@@ovS@-M`s `d|yޝAAovC@-M`t dx|XޝXXovS@-M`| WT Cottbus Ax`|hޝ@@@ovS@-M`X| `d|ʂޝAA@ovC@-M`} dx|ޝXXovS@-M`Ǒ WT0x`|ޝ@@ovS@-M` `d|aޝAAovC@-M`! dx|ޝXXovS@-M`| WT@Cottbus Bx`|̟ޝ@@ovS@-M` `d|6ޝAAovC@-M` dx|ޝXX@ovS@-M`b WTPx`|ޝ@@ovS@-M` `d|ޝAAovC@-M` dx|ޝXXovS@-M`@ WT`Leipzig Ax`|ޝ@@ovS@-M`} `d|ϲޝAAovC@-M` dx|EޝXXovS@-M` WTpx`|Tޝ@@ovS@-M`A `d|ޝAAovC@-M`O dx|ޝXXovS@-M`ҽ WTLeipzig Bx`|4ޝ@@ovS@-M` `d|ޝAAovC@-M`@ dx|ޝXXovS@-M`q WTx`|ޝ@@@ovS@-M` `d| ޝAA@ovC@-M` dx|^ޝXXovS@-M` WTSat Ax`|mޝ@@ovS@-M`Z `d|ޝAAovC@-M` dx|1ޝXX@ovS@-M` WTx`|Dޝ@@ovS@-M`& `d|ޝAAovC@-M`T dx|ޝXXovS@-M` WTSat Bx`|ޝ@@@ovS@-M` `d|sޝAA@ovC@-M`3 dx|ޝXXovS@-M` WT7x`|ޝ@@ovS@-M` `d|0ޝAAovC@-M` dx|[ޝXX@ovS@-M` WTPMR Ax`|iޝ@@ovS@-M`n `d|ޝAAovC@-M`| dx|#ߝXXovS@-M` WTWx`|2ߝ@@ovS@-M`7 `d|ߝAAovC@-M`M dx|ߝXXovS@-M` WTPMR Bx`|ߝ@@ovS@-M` `d|h ߝAAovC@-M`( dx|ߝXXovS@-M`D WTxx`|ߝ@@ovS@-M` `d|ߝAAovC@-M` dx|BߝXXovS@-M` WXbpDM3MATx`|Pߝ@@@ovS@-M`R `d|(GߝAA@ovC@-M`A dx|LߝXX@~DwS@-M`G WXzx`|Lߝ@@@{DwS@-M`G `d|MߝAA@{DwC@-M`H dx|KUߝXX@{DwS@-M` P W\jx`|ZUߝ@@@~DwS@-M`9P `d||ߝAA@~DwC@-M`w dx|!ߝXXvDwS@-M` W\~x`|2ߝ@@@xDwS@-M`7 `d|ߝAA@xDwC@-M`[ dx|ߝXXqDwS@-M` W\ x`|ߝ@@tDwS@-M`ٕ `d|ߝAAtDwC@-M`ݖ dx|XߝXXyDwS@-M` W\0x`|kߝ@@yDwS@-M`G `d|ߝAAyDwC@-M`G dx|(ߝXX}DwS@-M` W\@x`|:ߝ@@wDwS@-M`# `d|oߝAAwDwC@-M`/ dx|ߝXXDwS@-M` W\Px`|ߝ@@vDwS@-M` `d|`ߝAAvDwC@-M` dx|ߝXXvDwS@-M`D W\`x`|ߝ@@DwS@-M`u `d|ߝAADwC@-M` dx|gߝXXwDwS@-M`' W\px`|uߝ@@}DwS@-M`V `d|ߝAA}DwC@-M`w dx|`ߝXXovS@-M` W\x`|wߝ@@ovS@-M`S `d|ߝAAovC@-M`W dx|ߝXXovS@-M` W\x`|ߝ@@ovS@-M` `d|mߝAAovC@-M`- dx|ߝXXovS@-M` W\x`|ߝ@@ovS@-M` `d|YߝAAovC@-M` dx|ZߝXXovS@-M` W\x`|kߝ@@ovS@-M`X `d|ߝAAovC@-M`_ dx|ߝXXovS@-M` W\.x`|+ߝ@@ovS@-M` `d|ߝAAovC@-M`D dx|ߝXXovS@-M` W\>x`|ߝ@@ovS@-M` `d|;ߝAAovC@-M` dx|XXovS@-M`c W\Nx`|@@ovS@-M`6 `d|dAAovC@-M`$ dx|0 XXovS@-M` W\^x`|> @@ovS@-M`( `d| AAovC@-M`V dx|XXovS@-M`| W\Status Message 1)x`|@@ovS@-M` `d|!AAovC@-M` dx|mXXovS@-M`- W\x`|{@@ovS@-M`h `d|AAovC@-M` dx|6&XXovS@-M` W\ x`|D&@@ovS@-M`/! `d|'AAovC@-M`X" dx|.XXovS@-M`) W\0x`| /@@ovS@-/@@ovC@->M`) `d|_0AAovC@-M`+ dx|7XXovS@-M`2 W\@x`|7@@ovS@-M`2 `d|,9AAovC@-M`3 dx|y@XXovS@-M`9; W\Px`|@@@ovS@-M`r; `d|AAAovC@-M`< dx|6IXXovS@-M`C W\`x`|DI@@ovS@-M`1D `d|JAAovC@-M`{E dx|QXXovS@-M`L W\px`|Q@@ovS@-M`L `d|DSAAovC@-M`N dx|$ZXXovS@-M`T W\x`|2Z@@ovS@-M`U `d|[AAovC@-M`CV dx|bXXovS@-M`] W\x`|b@@ovS@-M`] `d|?dAAovC@-M`^ dx|kXXovS@-M`Df W\x`|k@@ovS@-M`{f `d|lAAovC@-M`g d`|'t@@ovS@-M`ns `d|yAAovC@-M`t dx|.XXyDwS@-M`{ W\/x`|=@@tDwS@-M`&| `d|AAtDwC@-M`Q} dx|XXqDwS@-M` W\?x`|@@@xDwS@-M` `d|JAA@xDwC@-M` dx|zXXvDwS@-M`: W\Ox`|@@@~DwS@-M` `d|AA@~DwC@-M` dx|DXX@{DwS@-M` W\_x`|R@@@~DwS@-M`` `d|AA@~DwC@-M`z dx|XXvDwS@-M` W\px`|@@@xDwS@-M` `d|TAA@xDwC@-M` dx|XXqDwS@-M`K W\x`|@@tDwS@-M` `d|AAtDwC@-M` dx|EXXyDwS@-M` W\ x`|U@@yDwS@-M`8 `d|cAAyDwC@-M`# dx|XX}DwS@-M`ڸ W\0x`|+@@wDwS@-M`/ `d|jAAwDwC@-M`* dx|XXDwS@-M` W\@x`|@@vDwS@-M` `d|AAAvDwC@-M` dx|VXXvDwS@-M` W\Px`|i@@DwS@-M`G `d|wAADwC@-M`7 dx|XXwDwS@-M` W\`x`|@@}DwS@-M` `d|KAA}DwC@-M` dx|XXyDwS@-M`t W\px`|@@yDwS@-M` `d|AAyDwC@-M` dx|qXXtDwS@-M`1 W\x`|@@qDwS@-M`c `d|AAqDwC@-M`e dx|NXXovS@-M` W\x`|d@@ovS@-M`U `d|AAovC@-M` dx|XXovS@-M` W\x`|@@ovS@-M`U `d|xAAovC@-M`8 dx|XX@xDwS@-M`S W\ x`|@@vDwS@-M` `d|AAvDwC@-M` dx|J XXovS@-M` W\0x`|_ @@ovS@-M`@`d| AAovC@-M`Ndx|XXovS@-M`W\@x`|@@ovS@-M``d|4AAovC@-M`dx|XXovS@-M`zW\Px`|@@ovS@-M``d| AAovC@-M`dx|z&XXovS@-M`:!W\`x`|&@@ovS@-M`{!`d|'AAovC@-M`"dx|5/XXovS@-M`)W\qx`|C/@@ovS@-M`3*`d|0AAovC@-M`U+dx|7XXovS@-M`2W\x`|8@@ovS@-M`2`d|z9AAovC@-M`:4dx|@XXovS@-M`W;W\ x`|@@@ovS@-M`;`d|AAAovC@-M`<dx|#IXXovS@-M`CW\0x`|1I@@ovS@-M`D`d|qJAAovC@-M`1Edx|QXXovS@-M`tLW\@x`|Q@@ovS@-M`L`d|RAAovC@-M`Mdx|IZXXovS@-M` UW\Px`|WZ@@ovS@-M`AU`d|[AAovC@-M`rVdx|McXX@~DwS@-M` ^W\`x`|]c@@@{DwS@-M`J^`d|dAA@{DwC@-M`h_dx| lXX@{DwS@-M`fW\px`|l@@@~DwS@-M`g`d|lmAA@~DwC@-M`,hdx|tXXvDwS@-M`SoW\x`|t@@@xDwS@-M`o`d|uAA@xDwC@-M`pdx|XXqDwS@-M`jW\x`|@@tDwS@-M``d|AAtDwC@-M`dx|XXyDwS@-M`W\x`| @@yDwS@-M``d|^AAyDwC@-M`dx|XX}DwS@-M`NW\!x`|@@wDwS@-M`~`d|AAwDwC@-M`dx|FXXDwS@-M`W\1x`|S@@vDwS@-M`C`d|AAvDwC@-M`Mdx|XXvDwS@-M`ǧW\Ax`|@@DwS@-@@vDwC@->M``d|rAADwC@-M`2dx|XXovS@-M`fW\Qx`|@@ovS@-M``d| AAovC@-M`ʱdx|XXXovS@-M`W\ax`|e@@ovS@-M`J`d|AAovC@-M`vdx|XXovS@-M`W\rx`| @@ovS@-M` `d|qAAovC@-M`1dx|XXovS@-M`W\x`|@@ovS@-M``d|PAAovC@-M`dx|SXXovS@-M`W\ x`|_@@ovS@-M`A`d|AAovC@-M`dx|XXovS@-M`W\0x`|@@ovS@-M``d|M`h`d|~AAovC@-M`>dx|%XXovS@-M`W\Px`|2@@ovS@-M``d|AAovC@-M`Adx|XXovS@-M`W\`x`|@@ovS@-M``d|?AAovC@-M`dx|XXovS@-M`ZW\px`|@@ovS@-M``d|AAovC@-M`dx|Z XXovS@-M`W\x`|f @@ovS@-M`O`d| AAovC@-M`~dx| XXovS@-M`W\x`|@@ovS@-M``d|WAAovC@-M`dx|XXovS@-M`VW\x`|@@ovS@-M``d|AAovC@-M`dx|+&XXovS@-M` W\"x`|6&@@ovS@-M`&!`d|'AAovC@-M`J"dx|.XXovS@-M`D)W\2x`|.@@ovS@-M`)`d|/AAovC@-M`*dx|BXXovS@-M`G=W\Bx`|B@@ovS@-M`~=`d|CAAovC@-M`>dx|DKXXovS@-M`W\Rx`|QK@@ovS@-M``d|LAAovC@-M`&dx|SXXwDwS@-M`o W\bx`|S@@}DwS@-M` `d|SUAA}DwC@-M` dx|\XXyDwS@-M`)W\px`|\@@yDwS@-M`d`d| ^AAyDwC@-M`dx|JeXXtDwS@-M`W\x`|We@@qDwS@-M``d|fAAqDwC@-M`<dx|mXX@xDwS@-M`(&W\ x`|m@@vDwS@-M`Y&`d|nAAvDwC@-M`g'dx|AvXX@~DwS@-M`.W\0x`|Qv@@@{DwS@-M`.`d|wAA@{DwC@-M`0dx|XX@{DwS@-M`7W\@x`|@@@~DwS@-@@@{DwC@->M`7`d|TAA@~DwC@-M`8dx|XXvDwS@-M`?@W\Px`|̇@@@xDwS@-M`z@`d|*AA@xDwC@-M`Adx|XXqDwS@-M`IW\`x`|@@tDwS@-M`;I`d|БAAtDwC@-M`PJdx|CXXyDwS@-M`QW\px`|R@@yDwS@-M`Q`d|eAAyDwC@-M`Rdx|XX}DwS@-M`0ZW\x`|@@wDwS@-M`~Z`d|AAwDwC@-M`[d`|e@@DwS@-M`gg`d|ίAADwC@-M`Nhdx| XXDwS@-M`oW\x`|8@@vDwS@-M`o`d|xAAvDwC@-M`pdx|XXwDwS@-M` xW\#x`|@@}DwS@-M`[x`d|AA}DwC@-M`tydx|PXXyDwS@-M`ЀW\/x`|^@@yDwS@-M` `d|AAyDwC@-M`5dx|XXtDwS@-M`W\Cx`|"@@qDwS@-M``d|aAAqDwC@-M`dx|XX@xDwS@-M`W\Sx`|@@vDwS@-M`p`d|AAvDwC@-M`ydx|UXX@~DwS@-M`՚W\_x`|d@@@{DwS@-M`K`d|AA@{DwC@-M`Ydx| XX@{DwS@-M`W\tx`|@@@~DwS@-M``d|9AA@~DwC@-M`dx|XXvDwS@-M`PW\x`|@@@xDwS@-M``d|3AA@xDwC@-M`dx|XXqDwS@-M`W\ x`|@@tDwS@-M`d`d|AAtDwC@-M`dx|[XXyDwS@-M`۽W\0x`|l@@yDwS@-M`*`d|AAyDwC@-M`Pdx| XX}DwS@-M`HW\@x`| @@wDwS@-M``d|.AAwDwC@-M`dx|^XXvDwS@-M`W\Px`|l@@DwS@-M`.`d|AADwC@-M`Cdx|)XXDwS@-M`W\`x`|:@@vDwS@-M``d| AAvDwC@-M` dx|'XXwDwS@-M`hW\px`|'@@}DwS@-M``d|Y)AA}DwC@-M`dx|M``d|&>AAyDwC@-M`dx|TEXXtDwS@-M`W\x`|bE@@qDwS@-M`(`d|FAAqDwC@-M`:dx|NXX@xDwS@-M`W\x`|N@@vDwS@-M``d|ZOAAvDwC@-M`dx|VXX@~DwS@-M`TW\ x`|V@@@{DwS@-M``d|XXAA@{DwC@-M`dx|_XX@{DwS@-M`"W\4x`|_@@@~DwS@-M`Q`d| aAA@~DwC@-M`dx|+hXXvDwS@-M` W\Dx`|>h@@@xDwS@-M` `d|iAA@xDwC@-M`"dx|pXXqDwS@-M``)W\Px`|p@@tDwS@-M`)`d|(rAAtDwC@-M`*dx|yXXyDwS@-M`#2W\dx`|y@@yDwS@-M`2`d|{AAyDwC@-M`3dx|aXX}DwS@-M`:W\ux`|n@@wDwS@-M`.;`d|σAAwDwC@-M`O<dx|XXvDwS@-M`zCW\x`|@@DwS@-M`C`d|ZAADwC@-M`Ddx|XX@5zS@-M``LW\ x`|@@5zS@-M`L`d|FAA5zC@-M`Mdx|XX5zS@-M`>UW\0x`|̜@@@5zS@-M`U`d|AA@5zC@-M`Vdx|QXX5zS@-M`]W\@x`|b@@@5zS@-M`(^`d|AA@5zC@-M`:_dx|xXX5zS@-M`eW\Px`|@@5zS@-M`ff`d|֮AA5zC@-M`Vgdx|.XX@5zS@-M`nW\`x`|:@@@5zS@-M`n`d|xAA@5zC@-M`odx|XX5zS@-M`xwW\px`|@@5zS@-M`w`d|\AA5zC@-M`xdx|XX5zS@-M`W\x`|@@5zS@-M`B`d|AA5zC@-M`1dx|_XX5zS@-M`߈W\x`|k@@@5zS@-M`+`d|AA@5zC@-M`7dx|XX@5zS@-M`aW\x`|@@5zS@-M``d|9AA5zC@-M`dx|XXDwS@-M`#W\%x`|@@vDwS@-M`s`d|AAvDwC@-M`dx|]XXwDwS@-M`ݢW\5x`|j@@}DwS@-M`)`d|AA}DwC@-M`?dx|XXyDwS@-M`W\Ax`|+@@yDwS@-M`ɫ`d|nAAyDwC@-M`dx|XXtDwS@-M`bW\Ux`|@@qDwS@-M``d|.AAqDwC@-M`dx|XX@xDwS@-M`'W\ex`|@@vDwS@-M`q`d| AAvDwC@-M`edx|.XX@~DwS@-M`gW\rx`|.@@@{DwS@-M``d|P0AA@{DwC@-M`dx|7XX@{DwS@-M`W\x`|7@@@~DwS@-M`k`d|8AA@~DwC@-M`zdx|`@XXvDwS@-M`W\ x`|l@@@@xDwS@-M`<`d|AAA@xDwC@-M`<dx| IXXqDwS@-M`W\0x`|.I@@tDwS@-M``d|JAAtDwC@-M`dx|QXXyDwS@-M`0 W\@x`|Q@@yDwS@-M`| `d|SAAyDwC@-M` dx|AZXX}DwS@-M`W\Px`|OZ@@wDwS@-M``d|[AAwDwC@-M`*dx|bXXvDwS@-M`HW\ zx`|b@@DwS@-M``d|iAADwC@-M`%"dx|nXXDwS@-M`'W\ ?x`|n@@vDwS@-M`W'`d|oAAvDwC@-M`g(dx|LwXXwDwS@-M`/W\ x`|Yw@@}DwS@-M`0`d|xAA}DwC@-M`y1dx| XXyDwS@-M`8W`xx`|@@yDwS@-M`8`d|اAAyDwC@-M`X`dx|XXtDwS@-M`%iW`x`|@@qDwS@-M`qi`d|AAqDwC@-M`jdx|rXX@xDwS@-M`qW`  x`|@@vDwS@-M`Cr`d|ѺAAvDwC@-M`Qsdx|XX@~DwS@-M`zW`0 x`|"@@@{DwS@-M`z`d|AA@{DwC@-M`|dx|XX@{DwS@-M`ZW`@x`|@@@~DwS@-M``d|IAA@~DwC@-M`Ʉdx|`XXvDwS@-M`W`Px`|l@@@xDwS@-M`.`d|AA@xDwC@-M`Bdx| XXqDwS@-M`W``x`|-@@tDwS@-M``d|9AAtDwC@-M`dx|XXyDwS@-M`)W`px`|@@yDwS@-M`y`d|AAyDwC@-M`tdx|1XX}DwS@-M`W`x`|K@@wDwS@-M``d|AAwDwC@-M`+dx|XXvDwS@-M`kW`x`|@@DwS@-M``d|9AADwC@-M`dx|XXDwS@-M`W`x`|@@vDwS@-M`N`d|AAvDwC@-M`dx|ZXXwDwS@-M`ڿW`x`|e@@}DwS@-M`"`d|AA}DwC@-M`dx|XX@5zS@-M`fW`"x`|@@@5zS@-M``d|QAA@5zC@-M`d`|@@yDwS@-M``d|AAyDwC@-M`dx|t%XX5zS@-M`W`Bx`|%@@5zS@-M`Y`d|&AA5zC@-M`Vdx|6.XX5zS@-M`W`Rx`|B.@@5zS@-M``d|/AA5zC@-M`dx|6XX5zS@-M`mW`cx`|6@@@5zS@-M``d|+8AA@5zC@-M`dx|?XX@5zS@-M`ZW`sx`|?@@5zS@-M``d|;AAA5zC@-M`dx|HXX5zS@-M`JW` x`|H@@5zS@-M``d|JAA5zC@-M`dx|OQXX5zS@-M` W`0x`|]Q@@5zS@-M` `d|RAA5zC@-M` dx|ZXX5zS@-M`W`@x`|Z@@5zS@-M``d|[AA5zC@-M`dx|bXX@5zS@-M`JW`Px`|b@@@5zS@-M``d|!dAA@5zC@-M`dx|LkXXzS@-M`#W``x`|dk@@zS@-M`$`d|lAAzC@-M`:%dx|sXXzS@-M`+,W`px`|s@@zS@-M`_,`d|tAAzC@-M`m-dx||XX@zS@-M`4W`x`|1|@@@zS@-M`4`d|<}AA@zC@-M`5dx|~XXzS@-M`<W`x`|@@zS@-M`-=`d|؅AAzC@-M`X>dx|XXzS@-M`bEW`x`|@@zS@-M`E`d|DAAzC@-M`Fdx|NXXzS@-M`MW`x`|b@@@zS@-M`N`d|AA@zC@-M`'Odx|KXX@zS@-M`dW`#x`|b@@zS@-M`"e`d|AAzC@-M`1fdx|XXzS@-M`mW`3x`|@@zS@-M`Mm`d|AAzC@-M`sndx|XXzS@-M`;uW`Cx`|Ƽ@@@zS@-M`u`d|AA@zC@-M`vdx|$XXzS@-M`}W`Sx`|4@@zS@-M`}`d|AAzC@-M`dx|XXzS@-M`WW`dx`|@@zS@-M``d|&AAzC@-M`dx|XXzS@-M`W`tx`|@@zS@-M`W`d|AAzC@-M`>dx|kXXzS@-M`W` x`|y@@zS@-M`<`d|AAzC@-M`gdx|5XX5zS@-M`W`0x`|H@@5zS@-M``d|QAA5zC@-M`ѡdx|XXtDwS@-M`"W`@x`|@@qDwS@-M`~`d|AAqDwC@-M`idx|gXX@xDwS@-M`W`Px`|x@@vDwS@-M`=`d|AAvDwC@-M`Qdx|'XX@~DwS@-M`W``x`|7@@@{DwS@-M``d|AA@{DwC@-M`dx| XX@{DwS@-M`gW`px`| @@@~DwS@-M``d|F AA@~DwC@-M`dx|XXvDwS@-M`W`x`|@@@xDwS@-M`X`d|AA@xDwC@-M`pdx|YXXqDwS@-M`W`x`|f@@tDwS@-M`(`d|AAtDwC@-M`'dx|%XXyDwS@-M`W`x`|%%@@yDwS@-M``d|&AAyDwC@-M`+dx|-XX}DwS@-M`1W`x`|-@@wDwS@-M`|`d|,/AAwDwC@-M`dx|I6XXvDwS@-M`W`$x`|W6@@DwS@-M``d|7AADwC@-M`0dx|?XXDwS@-M`W`4x`|?@@vDwS@-M``d|R@AAvDwC@-M`dx|GXXwDwS@-M`KW`Dx`|G@@}DwS@-M``d|HAA}DwC@-M`bdx|PXXyDwS@-M` W`Tx`|P@@yDwS@-M`[ `d|RAAyDwC@-M` dx|gXXtDwS@-M`w W`ex`|h@@qDwS@-M` `d|qiAAqDwC@-M`!dx|pXX@xDwS@-M`7)W`ux`|p@@vDwS@-M`)`d|rAAvDwC@-M`*dx|;yXX@~DwS@-M`1W` x`|Hy@@@{DwS@-M`2`d|zAA@{DwC@-M` 3dx|XX@{DwS@-M`{:W`0x`|@@@~DwS@-M`:`d|aAA@~DwC@-M`;dx|XX@~DwS@-M`*CW`@x`|@@@{DwS@-M`yC`d|5AA@{DwC@-M`Ddx|BXX@{DwS@-M`KW`Px`|S@@@~DwS@-M`L`d|AA@~DwC@-M`3Mdx|XX@~DwS@-M`vTW``x`|@@@{DwS@-M`T`d|EAA@{DwC@-M`Udx|XX@{DwS@-M`(]W`px`|@@@~DwS@-M`s]`d|AA@~DwC@-M`t^dx|dXX@~DwS@-M`eW`x`|p@@@{DwS@-M`4f`d|ɮAA@{DwC@-M`Igdx|&XX@{DwS@-M`nW`x`|2@@@~DwS@-M`n`d|AA@~DwC@-M`.pdx|XX@~DwS@-M`2wW`x`|@@@{DwS@-w``|@@@~DwC@->M`}w`d| AA@{DwC@-M`xdx|:XX@{DwS@-M`W`x`|F@@@~DwS@-M``d|AA@~DwC@-M`dx|XX@~DwS@-M`W`%x`|@@@{DwS@-M`Έ`d|1AA@{DwC@-M`dx|XX@{DwS@-M`>W`5x`|@@@~DwS@-M``d|MAA@~DwC@-M`͒dx|vXX@~DwS@-M`W`Ex`|@@@{DwS@-M`H`d|AA@{DwC@-M`Rdx|M``d|AA@~DwC@-M`dx|XX@~DwS@-M`YW`fx`|@@@{DwS@-M``d|:AA@{DwC@-M`dx|XX@{DwS@-M`W`vx`|@@@~DwS@-M`T`d|AA@~DwC@-M`Hdx|;XX@~DwS@-M`W` x`|G@@@{DwS@-M``d|AA@{DwC@-M`)dx| XX@{DwS@-M`W`0x`| @@@~DwS@-M``d|AA@~DwC@-M`dx|cXX@~DwS@-M`W`@x`|u@@@{DwS@-M``d|AA@{DwC@-M`dx|XX@{DwS@-M`W`Px`| @@@~DwS@-M``d|wAA@~DwC@-M`dx|&XX@~DwS@-M`OW``x`|&@@@{DwS@-M``d|K(AA@{DwC@-M`dx|/XX@{DwS@-M`W`px`|/@@@~DwS@-M`;`d|0AA@~DwC@-M`Bdx|F8XX@~DwS@-M`W`x`|[8@@@{DwS@-M``d|9AA@{DwC@-M`&dx|AXX@{DwS@-M`W`x`|)A@@@~DwS@-M``d|oBAA@~DwC@-M`dx|IXXzS@-M`-W`x`|I@@zS@-``|I@@zC@->M`{`d|JAAzC@-M`zd`|?R@@@zS@-M``d|XAA@zC@-M`dx|^XX@zS@-M`1W`&x`|^@@zS@-M``d|E`AAzC@-M`dx|dgXXzS@-M`W`6x`|rg@@zS@-M`y `d|iAAzC@-M`!dx|pXXzS@-M`(W`Fx`|+p@@zS@-M`(`d|qAAzC@-M`*dx|xXXzS@-M`O1W`Vx`|x@@@zS@-M`1`d|yAA@zC@-M`z2dx|wXXzS@-M`9W`gx`|@@zS@-M`&:`d|ƂAAzC@-M`F;dx|MXXzS@-M`BW`wx`|_@@zS@-M`C`d|ƋAAzC@-M`FDdx|XXzS@-M`~KW` x`|@@zS@-M`K`d|iAAzC@-M`Ldx|XX@zS@-M`TW`0x`|@@zS@-M`TT`d|ڜAAzC@-M`ZUdx|XXzS@-M`\W`@x`|=@@@zS@-M`\`d|jAA@zC@-M`]dx|XXzS@-M`heW`Px`|@@zS@-M`e`d|HAAzC@-M`fdx|XXzS@-M`nW``x`|@@zS@-M`ln`d|AAzC@-M`zodx|hXXzS@-M`vW`px`|w@@zS@-M`M``d|AAzC@-M`dx|RXXzS@-M`ҔW`x`|`@@zS@-M`#`d|AAzC@-M`dx|XXzS@-M`W`x`|&@@zS@-M`ȝ`d|AAzC@-M`dx|XXzS@-M`lW`x`|@@@zS@-M``d|LAA@zC@-M`̧dx|XX@zS@-M`W`'x`|@@zS@-M`o`d|AAzC@-M`dx|fXXzS@-M`W`7x`|t@@zS@-M`8`d|AAzC@-M`dx|)XXzS@-M`W`Gx`|6@@zS@-M``d|o AAzC@-M`dx|>XXzS@-M`W`Wx`|M@@@zS@-M``d|AA@zC@-M`:dx|<XXzS@-M`W`hx`|H@@zS@-M`)`d|AAzC@-M`6dx|"XXzS@-M`yW`xx`|#@@zS@-M``d|T$AAzC@-M`dx|+XXzS@-M`4W` x`|+@@zS@-M``d|,AAzC@-M`qdx|t4XX@zS@-M`W`0x`|4@@zS@-M`e`d|5AAzC@-M`ydx|F=XXzS@-M`W`@x`|T=@@@zS@-M``d|>AA@zC@-M`+dx|EXXzS@-M`zW`Px`|F@@zS@-M``d|^GAAzC@-M`dx|NXXzS@-M`5W``x`|N@@zS@-M``d|OAAzC@-M`zdx|oWXXzS@-M`W`px`|{W@@zS@-M`<`d|XAAzC@-M`)dx|1`XX@zS@-M`W`x`|>`@@zS@-M``d|aAAzC@-M`;dx|hXXzS@-M`@!W`x`|h@@zS@-M`!`d|jAAzC@-M`"d`|^q@@5zS@-M`(C`d|AA5zC@-M`7Ddx|XX5zS@-M`LW`x`|@@5zS@-M`YL`d|AA5zC@-M`aMdx| XX@5zS@-M`TW`(x`|@@@5zS@-M`T`d|lAA@5zC@-M`Udx|ȤXX@5zS@-M`H]W`8x`|դ@@5zS@-M`~]`d|ܥAA5zC@-M`\^dx|XX5zS@-M`fW`Hx`|@@5zS@-M`f`d|AA5zC@-M`gdx|XX5zS@-M`nW`Xx`|,@@5zS@-M`n`d|AA5zC@-M`pdx|߾XX5zS@-M`_wW`ix`|@@5zS@-M`w`d|DAA5zC@-M`xdx|XX@5zS@-M`!W`yx`|@@@5zS@-M`X`d|AA@5zC@-M`dx|bXX@5zS@-M`W` x`|n@@5zS@-M``d|AA5zC@-M`Idx|8XX5zS@-M`W`0x`|E@@5zS@-M``d|AA5zC@-M`dx|XX5zS@-M`|W`@x`| @@5zS@-M``d|jAA5zC@-M`dx||XX5zS@-M`W`Px`|@@5zS@-M`1`d|AA5zC@-M`ddx|M``d||AA@5zC@-M`dx|XX@5zS@-M`0W`px`|@@5zS@-M`n`d|/AA5zC@-M`dx|AXX5zS@-M`W`x`|M@@5zS@-M``d|AA5zC@-M`,dx| XX5zS@-M`PW`x`| @@5zS@-M``d|-AA5zC@-M`dx|rXX5zS@-M`W` x`|}@@5zS@-M`.`d|AA5zC@-M`@dx|XX@5zS@-M`W`x`|#@@@5zS@-M``d|2AA@5zC@-M`dx|&XX@5zS@-M`CW`)x`|&@@5zS@-M`~`d|$(AA5zC@-M`dx|i/XX5zS@-M`W`9x`|t/@@5zS@-M`$`d|0AA5zC@-M`\dx|7XXzS@-M`HW`Ix`|7@@zS@-M``d|+9AAzC@-M`dx|~@XXzS@-M`W`Yx`|@@@@zS@-M`2`d|\AA@zC@-M`bdx|AgXX@zS@-M`W`jx`|Mg@@zS@-M``d|hAAzC@-M`!dx|oXXzS@-M`~(W`zx`| p@@zS@-M`(`d|gqAAzC@-M`)dx|xXXzS@-M`1W` x`|x@@zS@-M`31`d|yAAzC@-M`L2dx|LXXzS@-M`9W`0x`|\@@@zS@-M`9`d|ǂAA@zC@-M`G;dx|XXzS@-M`BW`@x`|@@zS@-M`DB`d|݊AAzC@-M`]Cdx|@XXzS@-M`JW`Px`|L@@zS@-M`J`d|AAzC@-M`'Ldx|XXzS@-M`|SW``x`|@@zS@-M`S`d|BAAzC@-M`Tdx|XX@zS@-M`.\W`px`|@@zS@-M`d\`d|$AAzC@-M`]dx|^XXzS@-M`dW`x`|l@@@zS@-M`e`d|­AA@zC@-M`Bfd`|˴@@zS@-M`q`d|%AAzC@-M`rdx|XXzS@-M`1zW` x`|@@zS@-M`z`d|AAzC@-M`{dx|XXzS@-M`>W`x`|@@zS@-M``d|AAzC@-M`dx|vXX@zS@-M`W`*x`|@@zS@-M`P`d|AAzC@-M`Udx|9XXzS@-M`W`:x`|J@@zS@-M``d|PAAzC@-M`Тdx|XXzS@-M`8W`Jx`|@@zS@-M`m`d|AAzC@-M`qdx|IXX@~DwS@-M`ɱW`Zx`|^@@@{DwS@-M``d|bAA@{DwC@-M`dx|XX@5zS@-M`>W` kx`|@@@5zS@-M`w`d|AA@5zC@-M`xdx|XXzS@-M`W` {x`|"@@@zS@-M``d|.AA@zC@-M`dx|vXX@zS@-M`W` x`|@@zS@-M`(`d|AAzC@-M`(dx|XXzS@-M`lW` 0x`| @@zS@-M``d|'!AAzC@-M`dx|T(XX@5zS@-M`W` @x`|l(@@@5zS@-M` `d|)AA@5zC@-M`odx|0XXzS@-M`EW` Px`|0@@zS@-M`z`d|2AAzC@-M`dx|"9XXzS@-M`W` `x`|:9@@zS@-M``d|}:AAzC@-M`dx|AXX@5zS@-M` W` px`|A@@@5zS@-M`A`d|BAA@5zC@-M`Ddx|JXXzS@-M`W` x`|J@@zS@-M``d|>KAAzC@-M`dx|zRXX@zS@-M` W` x`|R@@zS@-M`( `d|SAAzC@-M`- dx|ZXXzS@-M`tW`  x`|[@@zS@-M``d|)\AAzC@-M`dx|cXXzS@-M`W` x`|c@@zS@-M`-`d|dAAzC@-M`Vdx|kXXzS@-M`j$W` +x`|k@@@zS@-M`$`d|GmAA@zC@-M`%dx|3tXXzS@-M`,W` ;x`|Ft@@@zS@-M`,`d|duAA@zC@-M`-dx||XXzS@-M`5W` Kx`||@@zS@-M`75`d|}AAzC@-M`R6dx|ȑXXzS@-M`HJW` [x`|ӑ@@zS@-M`J`d|1AAzC@-M`Kdx|XXzS@-M`@SW` lx`|ǚ@@@zS@-M`S`d|GAA@zC@-M`Tdx|XX@zS@-M`<\W` |x`|ã@@zS@-M`\`d|=AAzC@-M`]dx|XXzS@-M`9eW` x`|@@zS@-M`~e`d|@AAzC@-M`fdx|XXzS@-M`4nW` 0x`|@@zS@-M``n`d|NAAzC@-M`odx|XXzS@-M`wW` @x`|@@@zS@-M`?w`d|AA@zC@-M`xdx|XX@zS@-M`W` Px`|@@zS@-M`<`d|AAzC@-M`{dx|XXzS@-M`W` `x`|@@zS@-M`<`d|AAzC@-M`dx|VXXzS@-M`֑W` px`|_@@zS@-M`Z`d|AAzC@-M`Tdx|XX@zS@-M`W` x`|'@@zS@-M`ǚ`d|AAzC@-M`'dx|XXzS@-M`W` x`|$@@zS@-M`ţ`d|AAzC@-M`$dx|XXzS@-M`W`  x`|"@@zS@-M`ì`d|AAzC@-M`0dx|XXzS@-M`W` x`|)@@zS@-M`ʵ`d|AAzC@-M`dx|XX@5zS@-M`W` ,x`|@@5zS@-M``d|nAA5zC@-M`dx|XX5zS@-M`pW` <x`|@@5zS@-M``d|WAA5zC@-M`dx|}XX@zS@-M`W` Lx`|@@zS@-M`(`d|AAzC@-M`xdx|= XXzS@-M`W` \x`|F @@zS@-M``d|!AAzC@-M`(dx|(XXzS@-M`zW` mx`|)@@zS@-M``d|<*AAzC@-M`dx|1XX@zS@-M`@W` }x`|1@@@zS@-M`v`d|3AA@zC@-M`dx|y:XXzS@-M`W` x`|:@@zS@-M`.`d|;AAzC@-M`vdx|CXXzS@-M`W` 0x`| C@@zS@-M``d|ODAAzC@-M`dx|KXXzS@-M`  W` @x`|K@@@zS@-M`6 `d|LAA@zC@-M`m dx|OTXX5zS@-M` W` Px`|[T@@5zS@-M` `d|UAA5zC@-M`3 dx|\XX5zS@-M`z W` `x`|]@@5zS@-M` `d|^AA5zC@-M` dx|eXX@5zS@-M` W` px`|e@@@5zS@-M`J `d|.gAA@5zC@-M` dx| nXX@zS@-M`& W` x`|n@@zS@-M`& `d|^oAAzC@-M`' dx|vXXzS@-M`/ W` x`|v@@zS@-M`5/ `d|wAAzC@-M`R0 dx|~XXzS@-M`a7 W`  x`|~@@zS@-M`7 `d|AAzC@-M`8 dx|VXX@5zS@-M`? W` x`|n@@5zS@-M`(@ `d|AA5zC@-M`*A dx|XX5zS@-M`H W` -x`|@@@5zS@-M`PH `d|ݐAA@5zC@-M`]I dx|XX@5zS@-M`zP W` =x`|@@5zS@-M`P `d|pAA5zC@-M`Q dx|wXX5zS@-M`X W` Mx`|@@@5zS@-M`|Y `d|AA@5zC@-M`Z dx|XX@5zS@-M`8a W` ]x`|Ĩ@@5zS@-M`a `d|AA5zC@-M`cb dx|pXX5zS@-M`i W` nx`||@@@5zS@-M`M`r `d|AA5zC@-M`t dx|XX@zS@-M`s{ W` x`| @@zS@-M`{ `d|hAAzC@-M`| dx|XXzS@-M`. W` 0x`|@@zS@-M`h `d|AAzC@-M` dx|[XXzS@-M`ی W` @x`|g@@zS@-M` `d|AAzC@-M`9 dx|XXzS@-M` W` Px`|+@@zS@-M`͕ `d|AAzC@-M` dx|XX@zS@-M`X W` `x`|@@zS@-M` `d|CAAzC@-M`ß d`|@@zS@-M` `d|AAzC@-M` dx|mXXzS@-M` W` x`|{@@zS@-M`< `d|AAzC@-M`: dx|-XX@zS@-M` W` x`|9@@@zS@-M` `d|AA@zC@-M` dx| XXzS@-M`p W` x`| @@zS@-M` `d|!AAzC@-M` dx|{XXzS@-M` W` x`|@@zS@-M`- `d|AAzC@-M`6 dx|AXXzS@-M` W` .x`|N@@@zS@-M` `d|AA@zC@-M` dx|&XX@zS@-M`g W` >x`|&@@zS@-M` `d|(AAzC@-M` dx|/XXzS@-M`" W` Nx`|/@@zS@-M`p `d|0AAzC@-M`; dx|W8XXzS@-M` W` ^x`|d8@@zS@-M`! `d|9AAzC@-M`] d`|@@@@zS@-M` `d|3cAA@zC@-M` dx|tjXXzS@-M`" W` x`|j@@zS@-M`&# `d|kAAzC@-M`$ dx|!sXXzS@-M`+ W` x`|.s@@zS@-M`+ `d|tAAzC@-M`- dx|{XXzS@-M`Z4 W` 0x`|{@@@zS@-M`4 `d|&}AA@zC@-M`5 dx|wXXzS@-M`< W` @x`|@@zS@-M`(= `d|AAzC@-M`&> dx|-XXzS@-M`E W` Px`|;@@zS@-M`F `d|fAAzC@-M`F dx|ߕXXzS@-M`_N W` `x`|@@zS@-M`N `d|RAAzC@-M`O dx|XX@zS@-M`W W` px`|@@@zS@-M`jW `d|ݟAA@zC@-M`]X dx|3XXzS@-M`_ W` x`|@@@zS@-M`` `d|AAzC@-M`` dx|XXzS@-M`fh W` x`|@@zS@-M`h `d|LAAzC@-M`i dx|XXzS@-M`q W` x`|@@@zS@-M`iq `d|AA@zC@-M`gr dx|EXX@zS@-M`y W` x`|R@@zS@-M`z `d|AAzC@-M`1{ dx|XXzS@-M`m W` /x`|@@zS@-M` `d|9AAzC@-M` dx|XXzS@-M` W` ?x`|@@zS@-M`h `d|AAzC@-M`| dx|GXXzS@-M`Ǔ W` Ox`|T@@@zS@-M` `d|AA@zC@-M`& dx|XXzS@-M`| W` _x`| @@zS@-M`͜ `d|qAAzC@-M` dx|XXzS@-M` W`px`|@@zS@-M`V `d|AAzC@-M` dx|5XXzS@-M` W`x`|C@@@zS@-M` `d|QAA@zC@-M`Ѯ dx|XXzS@-M`m W` x`|@@zS@-M` `d|AAzC@-M` dx|uXXzS@-M` W`0x`|@@zS@-M` `d|AAzC@-M`& dx|6XXzS@-M` W`@x`|J@@zS@-M` `d|AAzC@-M` dx|XXovS@-M`h W`Px`|@@ovS@-M` `d| AAovC@-M` dx|s XX@ovS@-M` W``x`| @@ovS@-M`F `d|!AAovC@-M`O dx|%)XXovS@-M` W`px`|;)@@ovS@-M` `d|m*AAovC@-M` dx|1XXovS@-M`[ W`x`|1@@ovS@-M` `d|@3AAovC@-M` dx|:XXovS@-M` W`x`|:@@ovS@-M`m `d|;AAovC@-M`z dx|SCXXovS@-M` W`x`|cC@@@ovS@-M`$ `d|DAA@ovC@-M`- dx|KXX>zS@-M`4 W` x`|K@@@>zS@-zC@->M` `d|LAA@>zC@-M`} dx|nTXX>zS@-M` W`0x`||T@@>zS@-zC@->M`! `d|UAA>zC@-M`: dx|]XX@zS@-M` W`@x`|]@@@zS@-M` `d|<^AA@zC@-M` dx|eXXzS@-M` W`Px`|e@@zS@-M`5 `d|fAAzC@-M`B dx|nXXzS@-M`& W``x`|n@@zS@-M`& `d|joAAzC@-M`' dx|vXXzS@-M`G/ W`qx`|v@@@zS@-M`/ `d|0xAA@zC@-M`0 dx||XX>zS@-M`7 W`x`|@@@>zS@-zC@->M``8 `d|AA@>zC@-M``9 dx|;XX@zS@-M`@ W` x`|N@@zS@-M`@ `d|}AAzC@-M`A dx|XXzS@-M`xI W`0x`|@@@zS@-M`I `d|`AA@zC@-M`J dx|XX@zS@-M`*R W`@x`|@@zS@-M`xR `d|AAzC@-M`qS dx|rXXzS@-M`Z W`Px`|@@@zS@-M`[ `d|AA@zC@-M`\ dx|;XX@zS@-M`c W``x`|I@@zS@-M` d `d|AAzC@-M`(e dx|XXzS@-M`rl W`px`|@@@zS@-M`l `d|UAA@zC@-M`m dx|XX@zS@-M`u W`x`|@@zS@-M`Tu `d|AAzC@-M`iv dx|BXXzS@-M`} W`x`|N@@@zS@-M`~ `d|AA@zC@-M`! dx|XX@zS@-M`t W`x`|@@zS@-M`Æ `d|AAzC@-M` dx|XXzS@-M`; W`!x`|@@@zS@-M` `d|!AA@zC@-M` dx|sXX@zS@-M` W`1x`|@@zS@-M`E `d|AAzC@-M`J dx|M` `d|AA@zC@-M` dx|XX@zS@-M`{ W`Qx`| @@zS@-M`ϩ `d|]AAzC@-M`ݪ dx|XXzS@-M`< W`ax`|@@@zS@-M` `d|*AA@zC@-M` dx|XX@zS@-M` W`rx`|@@zS@-M`h `d|AAzC@-M`d dx|W$XXzS@-M` W`x`|d$@@@zS@-M`/ `d|%AA@zC@-M`> dx|-XX@zS@-M` W` x`|)-@@zS@-M` `d|y.AAzC@-M` dx|5XXzS@-M`O W`0x`|5@@@zS@-M` `d|37AA@zC@-M` dx|>XX@zS@-M` W`@x`|>@@zS@-@@@zC@->M`X `d|?AAzC@-M`7 d`|:G@@zS@-M` `d|LAAzC@-M`c dx|SXXzS@-M`b W``x`|S@@@zS@-M` `d|oUAA@zC@-M` dx|\XX@zS@-M` W`px`|\@@zS@-M`H `d|]AAzC@-M`5 dx|^eXX>zS@-M` W`x`|pe@@@>zS@-zC@->M` `d|fAA@>zC@-M`; dx|nXX@>zS@-M`& W`x`|-n@@>zS@-zC@->M`& `d|oAA>zC@-M`,( dx|vXX@>zS@-M`i/ W`x`|v@@>zS@-zC@->M`/ `d|CxAA>zC@-M`0 dx|XX@>zS@-M`58 W`"x`|@@>zS@-zC@->M`q8 `d|AA>zC@-M`|9 dx|nXX>zS@-M`@ W`2x`|~@@@>zS@-zC@->M`A `d|AA@>zC@-M` B dx|XX>zS@-M`cI W`Bx`|@@@>zS@-zC@->M`I `d|AA@>zC@-M`J dx|_XX>zS@-M`Q W`Rx`|n@@@>zS@-zC@->M`R `d|њAA@>zC@-M`QS dx|XX@>zS@-M`cZ W`bx`|@@>zS@-zC@->M`Z `d|!AA>zC@-M`[ dx|bXX>zS@-M`b W`sx`|w@@@>zS@-zC@->M`c `d|AA@>zC@-M`d dx|۲XX@>zS@-M`[k W`x`|@@>zS@-zC@->M`k `d|=AA>zC@-M`l dx|IXX>zS@-M`s W` x`|b@@@>zS@-zC@->M`t `d|AA@>zC@-M`u dx|XX@>zS@-M`y| W`0x`|@@>zS@-zC@->M`| `d|AA>zC@-M`~ dx|XX>zS@-M` W`@x`|@@@>zS@-zC@->M`B `d|AA@>zC@-M`^ dx|XX@>zS@-M` W`Px`|,@@>zS@-zC@->M`ԍ `d|AA>zC@-M` dx|XX>zS@-M`F W``x`|@@@>zS@-zC@->M`x `d|AA@>zC@-M`y dx|'XX@>zS@-M` W`px`|9@@>zS@-zC@->M` `d|lAA>zC@-M` dx|XX@>zS@-M` W`x`|@@@>zS@-zC@->M` `d|'AA@>zC@-M` dx|EXX>zS@-M`ů W`x`|V@@>zS@-zC@->M` `d|AA>zC@-M`A dx|XX>zS@-M`o W`x`|@@@>zS@-zC@->M` `d|;AA@>zC@-M` dx|XX>zS@-M`# W`#x`|@@@>zS@-zC@->M`Y `d| AA@>zC@-M` dx|BXX>zS@-M` W`3x`|U@@>zS@-zC@->M` `d|wAA>zC@-M` dx|XX>zS@-M`y W`Cx`|@@@>zS@-zC@->M` `d|AA@>zC@-M` dx|"XX@>zS@-M`? W`Sx`|"@@>zS@-zC@->M`v `d|($AA>zC@-M` dx|+XX>zS@-M` W`cx`|+@@@>zS@-zC@->M`: `d|,AA@>zC@-M`C dx|J4XX@>zS@-M` W`tx`|Y4@@>zS@-zC@->M` `d|5AA>zC@-M`6 dx|=XX>zS@-M` W`x`|(=@@>zS@-zC@->M` `d|V>AA>zC@-M` dx|EXX@>zS@-M`` W` x`|E@@>zS@-zC@->M` `d|GAA>zC@-M` dx|nNXX>zS@-M` W`0x`|N@@@>zS@-zC@->M`% `d|OAA@>zC@-M`G dx|9WXX>zS@-M` W`@x`|GW@@>zS@-zC@->M` `d|XAA>zC@-M` dx|_XX>zS@-M`U W`Px`|_@@@>zS@-zC@->M` `d|8aAA@>zC@-M` dx|hXX@>zS@-M` ! W``x`|h@@>zS@-zC@->M`W! `d|jAA>zC@-M`" dx|xqXX>zS@-M`) W`px`|q@@@>zS@-zC@->M`/* `d|rAA@>zC@-M`]+ dx|AzXX@>zS@-M`2 W`x`|Oz@@>zS@-zC@->M`2 `d|{AA>zC@-M`#4 dx|҂XX>zS@-M`R; W`x`|@@>zS@-zC@->M`; `d|AA>zC@-M`< dx|XX@>zS@-M`)D W`x`|@@>zS@-zC@->M`WD `d|;AA>zC@-M`E dx|SXX>zS@-M`L W`$x`|`@@@>zS@-zC@->M`6M `d|AA@>zC@-M`-N dx| XX>zS@-M`U W`4x`|@@>zS@-zC@->M`U `d|kAA>zC@-M`V dx|̥XX>zS@-M`L^ W`Dx`|ۥ@@@>zS@-zC@->M`~^ `d|AA@>zC@-M`_ dx|XX@>zS@-M`g W`Tx`|@@>zS@-zC@->M`rg `d|AA>zC@-M`wh dx|UXX>zS@-M`o W`dx`|b@@@>zS@-zC@->M`Qp `d|ȸAA@>zC@-M`Hq dx|ƿXX@>zS@-M`Fx W`ux`|ҿ@@>zS@-zC@->M`x `d|&AA>zC@-M`y dx|XX>zS@-M` W`x`|@@>zS@-zC@->M`l `d|AA>zC@-M`[ dx||XXzS@-M` W` x`|@@@zS@-M`P `d|AA@zC@-M` dx|;XX@zS@-M` W`0x`|H@@zS@-M` `d|AAzC@-M`4 dx|XXzS@-M` W`@x`|@@@zS@-M`W `d|AA@zC@-M`^ dx|XX@zS@-M` W`Px`|@@zS@-M` `d|AAzC@-M` dx|XXzS@-M`P W``x`|@@@zS@-M` `d|)AA@zC@-M` dx| XX@zS@-M` W`px`| @@zS@-M`V `d| AAzC@-M`j dx|XXzS@-M`] W`x`|@@@zS@-M`. `d|AA@zC@-M`$ dx|XX@zS@-M` W`x`|@@zS@-M`i `d|AAzC@-M`q dx|X$XXzS@-M` W`x`|d$@@@zS@-M`$ `d|%AA@zC@-M`< dx|-XX@zS@-M` W`%x`|%-@@zS@-M` `d|c.AAzC@-M` dx|5XXzS@-M`R W`5x`|5@@@zS@-M` `d|7AA@zC@-M` dx|>XX@zS@-M` W`Ex`|>@@zS@-@@@zC@->M`K `d|?AAzC@-M`f dx|FGXXzS@-M` W`Ux`|RG@@@zS@-M``d|HAA@zC@-M`8dx|OXX@zS@-M`GW`ex`|O@@zS@-M``d| QAAzC@-M` dx|!XXXzS@-M`W`vx`|-X@@@zS@-M``d|zYAA@zC@-M`dx|`XX@zS@-M`gW`x`|`@@zS@-M``d|JbAAzC@-M`d`|i@@zS@-M`x&`d|nAAzC@-M`i'dx|vXXzS@-M`.W`0x`|'v@@@zS@-M`.`d|wAA@zC@-M` 0dx|~XX@zS@-M`X7W`@x`|~@@zS@-M`7`d|*AAzC@-M`8dx|XXzS@-M`@W`Px`|@@@zS@-M`M@`d|AA@zC@-M`hAdx|\XX@zS@-M`HW``x`|i@@zS@-M`I`d|0AAzC@-M`Jdx|XXzS@-M`a^W`px`|@@@zS@-M`^`d|/AA@zC@-M`_dx|XX@zS@-M` gW`x`|@@zS@-M`g`d|nAAzC@-M`hdx|aXXzS@-M`oW`x`|r@@@zS@-M`p`d|AA@zC@-M`1qdx|XX@zS@-M`xW`x`|)@@zS@-M`x`d|~AAzC@-M`ydx|XXzS@-M`^W`&x`|@@@zS@-M``d|3AA@zC@-M`dx|XX@zS@-M`W`6x`|@@zS@-M`U`d|AAzC@-M`dx|6XXzS@-M`W`Fx`|D@@@zS@-M``d|AA@zC@-M`#dx|XX@zS@-M`rW`Vx`|@@zS@-M``d|AAzC@-M` dx|XXzS@-M` W`fx`|@@@zS@-M`Z`d|AA@zC@-M`vdx| XX@zS@-M`W`wx`|-@@zS@-M`ݬ`d|AAzC@-M`dx|XXzS@-M`fW`x`|@@@zS@-M``d|&AA@zC@-M`dx|XX@zS@-M`2W` x`|@@zS@-M`j`d|AAzC@-M`dx|yXXzS@-M`W`0x`|@@@zS@-M`2`d|AA@zC@-M`dx|XX@zS@-M`W`@x`|@@zS@-M``d|iAAzC@-M`dx|XXzS@-M`QW`Px`|@@@zS@-M``d|9!AA@zC@-M`dx|(XX@zS@-M`W``x`|(@@zS@-M`B`d|)AAzC@-M`Ydx|i1XXzS@-M`W`px`|w1@@@zS@-M`#`d|2AA@zC@-M`cdx|1:XX@zS@-M`W`x`|?:@@zS@-M``d|;AAzC@-M`dx|BXXzS@-M`:W`x`|B@@@zS@-M`o`d|DAA@zC@-M`dx|{KXX@zS@-M`W`x`|K@@zS@-M`1`d|MAAzC@-M`dx|FTXXzS@-M` W`'x`|ST@@@zS@-M` `d|UAA@zC@-M`Cdx|\XX@zS@-M`UW`7x`|\@@zS@-M``d|:^AAzC@-M`dx|eXXzS@-M`$W`Gx`|e@@@zS@-M`[`d| gAA@zC@-M`dx|inXX@zS@-M`&W`Wx`|vn@@zS@-M`'`d|oAAzC@-M`Q(dx|+wXXzS@-M`/W`gx`|7w@@@zS@-M`/`d|xAA@zC@-M` 1dx|XX@zS@-M`8W`xx`|@@zS@-M`8`d|wAAzC@-M`9dx|XXzS@-M`>AW`x`|ˈ@@@zS@-M`tA`d| AA@zC@-M`Ldx|}XX@zS@-M`W` x`|@@zS@-M``d|ÒAAzC@-M` dx|XXzS@-M`SW`0x`|@@@zS@-M``d|yAA@zC@-M`dx|ТXX@zS@-M`W`@x`|ݢ@@zS@-M`B`d|>AAzC@-M`~dx|XXzS@-M`!W`Px`|@@@zS@-M`"`d|AA@zC@-M`(#dx|XX@zS@-M`Y*W``x`|$@@zS@-M`*`d|AAzC@-M`+dx|ټXXzS@-M`3W`px`|@@@zS@-M`N3`d|7AA@zC@-M`w4dx|XX@zS@-M`;W`x`|@@zS@-M` <`d|AAzC@-M`Y=dx|pXXzS@-M`DW`x`|}@@@zS@-M`D`d|AA@zC@-M` Fdx|XX@zS@-M`\W`x`|@@zS@-M`6\`d|AAzC@-M`\]dx|HXXzS@-M`dW`(x`|T@@@zS@-M`d`d|AA@zC@-M`edx| XX@zS@-M`ImW`8x`|@@zS@-M`m`d|mAAzC@-M`ndx|XXzS@-M`vW`Hx`|@@zS@-M`>v`d|AAzC@-M`Cwdx|XXzS@-M`~W`Xx`|@@zS@-M`~`d| AAzC@-M`%dx|EXXzS@-M`W`hx`|P@@zS@-M``d|AAzC@-M`Ɉdx|XX@zS@-M`EW`yx`|@@zS@-M`y`d|6AAzC@-M`vdx|"XXzS@-M`W`x`|"@@zS@-M`4`d|*$AAzC@-M`jdx|s+XXzS@-M`W` x`|+@@zS@-M``d|,AAzC@-M`3dx|+4XX@zS@-M`kW`0x`|64@@@zS@-M``d|5AA@zC@-M`̫dx|M`Q`d|">AAzC@-M`bdx|EXXzS@-M`ٻW`Px`|E@@zS@-M``d|FAAzC@-M`*dx|VNXXzS@-M`W``x`|aN@@@zS@-M``d|OAA@zC@-M`dx|WXX@zS@-M`HW`px`|W@@zS@-M`}`d|pXAAzC@-M`dx|_XXzS@-M`W`x`|_@@zS@-M``d|`AAzC@-M`4dx|ThXXzS@-M`W` x`|_h@@zS@-M``d|iAAzC@-M`dx|:qXX@zS@-M`zW`x`|Eq@@zS@-M``d|rAAzC@-M`dx|yXXzS@-M`0W`)x`|z@@zS@-M`j`d|a{AAzC@-M`dx|XXzS@-M`W`9x`|@@zS@-M``d|AAzC@-M`'dx|`XXzS@-M`W`Ix`|j@@zS@-M``d|AAzC@-M`dx|XX@zS@-M`T W`Yx`|"@@zS@-M` `d|]AAzC@-M` dx|ΜXXzS@-M`W`ix`|@@zS@-M`@`d|AAzC@-M`^/d`|@@@>zS@-zS@-M`9W`zx`|.@@>zC@->M`n9`d|7AA@>zC@-M`w:dx|gXX@>zS@-M`AW`x`|@@>zS@-zC@->M`A`d|AA>zC@-M`Cdx|XX>zS@-M`WJW` x`|$@@>zS@-zC@->M`J`d|oAA>zC@-M`Kdx|XX>zS@-M`RW`0x`|@@@>zS@-zC@->M`NS`d|AA@>zC@-M`zS@-M`[W`@x`|N@@>zS@-zC@->M`[`d|AA>zC@-M`\dx|XX>zS@-M`=dW`Px`| @@@>zS@-zC@->M`d`d|DAA@>zC@-M`edx|XX@>zS@-M`lW``x`|@@>zS@-zC@->M`(m`d|-AA>zC@-M`mndx|XX>zS@-M`vW`px`|@@>zS@-zC@->M`ev`d|AA>zC@-M`Nwdx|XX>zS@-M`~W`x`|@@@>zS@-zC@->M``d| AA@>zC@-M`Wdx|mXX@>zS@-M`W` x`|{@@>zS@-zC@->M``d|AA>zC@-M`dx|6XX>zS@-M`vW`x`|G@@>zS@-zC@->M``d|AA>zC@-M`dx|"XX>zS@-M`%W`*x`|"@@@>zS@-zC@->M`l`d|/$AA@>zC@-M`odx|q+XX@>zS@-M`W`:x`|~+@@>zS@-zC@->M``d|,AA>zC@-M`dx|4XX>zS@-M`EW`Jx`|4@@@>zS@-zC@->M`}`d|r5AA@>zC@-M`dx|zS@-M` W`Zx`|<@@>zS@-zC@->M`?`d|>AA>zC@-M`Ydx|pEXX>zS@-M`W`jx`|E@@>zS@-zC@->M`5`d|GAA>zC@-M`Ddx|5NXX>zS@-M`uW`{x`|CN@@@>zS@-zC@->M``d|OAA@>zC@-M`dx|VXX@>zS@-M` W`x`|V@@>zS@-zC@->M`S`d|EXAA>zC@-M`dx|_XX>zS@-M`W` x`|_@@>zS@-zC@->M``d|aAA>zC@-M`Pdx|bhXX>zS@-M`W`0x`|ph@@@>zS@-zC@->M``d|iAA@>zC@-M`dx|qXX@>zS@-M`ZW`@x`|*q@@>zS@-zC@->M``d|~rAA>zC@-M`dx|yXX>zS@-M`W`Px`|y@@@>zS@-zC@->M`y`d|6{AA@>zC@-M`vdx|XX@>zS@-M`W``x`|@@>zS@-zC@->M`>`d|AA>zC@-M`(dx|QXX>zS@-M`W`px`|^@@>zS@-zC@->M``d|͌AA>zC@-M` dx|XX>zS@-M` W`x`|ʓ@@@>zS@-zC@->M`^ `d|+AA@>zC@-M`k dx|PXX@>zS@-M`W` x`|^@@>zS@-zC@->M``d|AA>zC@-M`dx|XX>zS@-M`QW`x`|@@>zS@-zC@->M``d|_AA>zC@-M`dx|ɭXXzS@-M` $W`+x`|׭@@zS@-M`F$`d|XAAzC@-M`%dx|XXzS@-M`,W`;x`|@@zS@-M`-`d|ͷAAzC@-M` .dx|OXXzS@-M`5W`Kx`|\@@zS@-M`5`d|AAzC@-M`6dx|YXXzS@-M`KW`[x`|h@@zS@-M`K`d|AAzC@-M`Mdx|XXzS@-M`YTW`kx`|&@@zS@-M`T`d|_AAzC@-M`Udx|XXzS@-M`\W`|x`|@@zS@-M`7]`d|AAzC@-M`.^dx|iXXzS@-M`eW`x`|u@@zS@-M`e`d|AAzC@-M`fdx|2XXzS@-M`rnW` x`|?@@zS@-M`n`d|AAzC@-M`odx|XXzS@-M`>wW`0x`| @@zS@-M`w`d|fAAzC@-M`xdx| XXzS@-M`W`@x`| @@zS@-M`R`d| AAzC@-M`Ldx|~XXzS@-M`W`Px`|@@zS@-M` `d|AAzC@-M`$dx|9XXzS@-M`yW``x`|E@@zS@-M``d|AAzC@-M`ْdx|#XXzS@-M`W`px`|#@@zS@-M`R`d|%AAzC@-M`Zdx||,XXzS@-M`W`x`|,@@zS@-M``d|-AAzC@-M`dx|4XXzS@-M`9W` x`|5@@zS@-M``d|^6AAzC@-M`dx|=XX>zS@-M`ӳW`x`|=@@@>zS@-zC@->M`9`d|>AA@>zC@-M`/dx|RFXX@>zS@-M`W`,x`|^F@@>zS@-zC@->M``d|GAA>zC@-M`dx|OXX>zS@-M`ZW`<x`|&O@@@>zS@-zC@->M``d|PAA@>zC@-M`dx|4WXX@>zS@-M`tW`Lx`|@W@@>zS@-zC@->M``d|XAA>zC@-M`dx|_XX>zS@-M`W`\x`|_@@>zS@-`@@>zC@->M`~`d|/aAA>zC@-M`odx|hXX>zS@-M`W`lx`|h@@@>zS@-zC@->M`;`d|iAA@>zC@-M`<dx|]qXX@>zS@-M`W`}x`|iq@@>zS@-zC@->M``d|rAA>zC@-M`dx|zXX>zS@-M`]W`x`|*z@@>zS@-zC@->M``d|{{AA>zC@-M`dx|XX>zS@-M`W` x`|ǂ@@@>zS@-zC@->M`c`d| AA@>zC@-M`Kdx|qXX@>zS@-M`W`0x`||@@>zS@-zC@->M``d|AA>zC@-M`dx|CXX>zS@-M` W`@x`|S@@@>zS@-zC@->M` `d|ԕAA@>zC@-M` dx|XX@>zS@-M`BW`Px`|@@>zS@-zC@->M``d|>AA>zC@-M`~dx|XXzS@-M`W``x`|ť@@zS@-M`&`d|ЦAAzC@-M`dx|QXXzS@-M`$W`px`|_@@zS@-M`$`d|AAzC@-M`%dx|XXovS@-M`Q-W`x`|!@@ovS@-M`-`d|2AAovC@-M`r.dx|XX@ovS@-M`5W` x`|@@ovS@-M`6`d|AAovC@-M`@7dx|4XXovS@-M`t>W`x`|A@@@ovS@-``|@@ovC@->M`>`d|AA@ovC@-M`?dx|XXovS@-M`FW`-x`|@@ovS@-M`!G`d|AAovC@-M`&Hdx|WXXovS@-M`OW`=x`|f@@@ovS@-M`O`d|AA@ovC@-M`Pdx|XXMwS@-M`DXW`Mx`|@@@MwS@-M`vX`d|#AA@MwC@-M`cYd`|@@vDwS@-M`te`d|CAAvDwC@-M`fdx|XXvDwS@-M`mW`mx`|@@wDwS@-M`n`d|AAwDwC@-M`ndx|XX}DwS@-M`]vW`~x`|,@@tDwS@-M`v`d|ZAAtDwC@-M`wdx|XX>zS@-M`W`x`|@@>zS@-zC@->M`?`d|0 AA>zC@-M`pdx|XX>zS@-M`W` x`|@@@>zS@-zC@->M`1`d|AA@>zC@-M`Sdx|QXX@>zS@-M`W`0x`|Z@@>zS@-zC@->M``d|AA>zC@-M`dx|#XX>zS@-M`EW`@x`|#@@>zS@-zC@->M``d|]$AA>zC@-M`dx|+XX>zS@-M`W`Px`|+@@@>zS@-zC@->M`_`d|,-AA@>zC@-M`ldx|4XX@>zS@-M`W``x`|4@@>zS@-zC@->M`1`d|6AA>zC@-M`[dx|K=XX>zS@-M`W`px`|V=@@@>zS@-zC@->M`س`d|>AA@>zC@-M`شdx|/FXX@>zS@-M`oW`x`|>F@@>zS@-zC@->M`Ƽ`d|jGAA>zC@-M`dx|NXX>zS@-M`W`x`|N@@>zS@-zC@->M`^`d| PAA>zC@-M`Idx|dWXX>zS@-M`W`x`|nW@@@>zS@-zC@->M``d|XAA@>zC@-M`dx|`XX@>zS@-M`OW`.x`|`@@>zS@-zC@->M`y`d|TaAA>zC@-M`dx|~hXX>zS@-M`W`>x`|h@@>zS@-zC@->M` `d|iAA>zC@-M` dx|qXX>zS@-M`NW`Nx`|q@@@>zS@-zC@->M``d|}rAA@>zC@-M`dx|yXXyDwS@-M`W`^x`|y@@@{DwS@-M`5`d|{AA@{DwC@-M`Bdx|`XXqDwS@-M`W`nx`|o@@@~DwS@-M``d|AA@~DwC@-M` dx|'XXvDwS@-M`gW`x`|0@@@xDwS@-M``d|AA@xDwC@-M`dx|XX@xDwS@-M`7 W`x`|@@vDwS@-M` `d|)AAvDwC@-M`i dx|XX@~DwS@-M`W` x`|˜@@qDwS@-M`J`d|:AAqDwC@-M`zdx|XX@{DwS@-M`W`0x`|@@yDwS@-M`!`d|AAyDwC@-M`%dx|KXXDwS@-M`$W`@x`|U@@yDwS@-M`$`d|үAAyDwC@-M`&dx|ضXXtDwS@-M`-W`Px`|@@}DwS@-M``-`d|DAA}DwC@-M`.dx|XXwDwS@-M`HBW``x`|@@vDwS@-M`B`d||AAvDwC@-M`Cdx|XXvDwS@-M`KW`px`|@@wDwS@-M`fK`d|dAAwDwC@-M`Ldx|XX}DwS@-M`SW`x`|@@tDwS@-M`7T`d|AAtDwC@-M`XUdx|nXXyDwS@-M`\W`x`|y@@DwS@-M`\`d|AADwC@-M`^dx|+XXyDwS@-M`keW`x`|6@@@{DwS@-M`e`d|xAA@{DwC@-M`fdx|XXqDwS@-M`%nW`/x`|@@@~DwS@-M`n`d|OAA@~DwC@-M`odx|XXvDwS@-M`vW`?x`|@@@xDwS@-M`^w`d|AA@xDwC@-M`^xdx|= XX@xDwS@-M`}W`Ox`|J @@vDwS@-M``d| AAvDwC@-M`Ādx|XX@~DwS@-M`W`_x`|@@qDwS@-M`P`d|-AAqDwC@-M`mdx|`XX@>zS@-M`W`ox`|s@@>zS@-zC@->M`ݐ`d|AA>zC@-M`dx|#XX>zS@-M`VW`x`|$#@@@>zS@-zC@->M``d|w$AA@>zC@-M`dx|+XX@>zS@-M`ɡW`x`|+@@>zS@-zC@->M`%`d|,AA>zC@-M`,dx|4XX>zS@-M`XW` x`|%4@@>zS@-zC@->M``d|h5AA>zC@-M`dx|JXX>zS@-M`CW`0x`|J@@@>zS@-zC@->M``d|KKAA@>zC@-M`dx|RXX@>zS@-M`W`@x`|R@@>zS@-zC@->M`i`d| TAA>zC@-M`Kdx|[XX@{DwS@-M`W`Px`|[@@yDwS@-M``d|\AAyDwC@-M`,dx|XdXXDwS@-M`W``x`|hd@@yDwS@-M``d|eAAyDwC@-M`dx|&mXXtDwS@-M`fW`px`|8m@@}DwS@-M``d|nAA}DwC@-M`dx|uXXwDwS@-M`W`x`|u@@vDwS@-M``d|bwAAvDwC@-M`dx|m~XXvDwS@-M`W`x`|{~@@wDwS@-M``d|AAwDwC@-M`dx|XX}DwS@-M`1W` x`|@@tDwS@-M`l`d|AAtDwC@-M`Tdx|ďXXyDwS@-M`W`0x`|ӏ@@DwS@-M`3`d|AADwC@-M`2dx|XXyDwS@-M`W`@x`|@@@{DwS@-M``d|әAA@{DwC@-M`dx|=XXqDwS@-M`}W`Px`|L@@@~DwS@-M``d|RAA@~DwC@-M`dx|RXXvDwS@-M` W``x`|`@@@xDwS@-M` `d|AA@xDwC@-M`!dx|ӲXX@xDwS@-M`)W`px`|@@vDwS@-M`e)`d| AAvDwC@-M``*dx|ZXX@~DwS@-M`1W`x`|l@@qDwS@-M`1`d|мAAqDwC@-M`3dx|XX@{DwS@-M`9W`x`|@@yDwS@-M`:`d|AAyDwC@-M`%;dx|AXXDwS@-M`BW` x`|N@@yDwS@-M`B`d|AAyDwC@-M`Cdx|XXtDwS@-M` KW`0x`|@@}DwS@-M`:K`d|%AA}DwC@-M`eLdx|XXwDwS@-M`SW`@x`|@@vDwS@-M`T`d|AAAvDwC@-M`Udx|ZXXvDwS@-M`\W`Px`|h@@wDwS@-M`\`d|AAwDwC@-M` ^dx|0XX}DwS@-M`peW``x`|I@@tDwS@-M`e`d|{AAtDwC@-M`fdx|XXyDwS@-M`"nW`px`|@@DwS@-M`n`d|AADwC@-M`odx|XXyDwS@-M`vW`x`|@@@{DwS@-M`8w`d|AA@{DwC@-M`5xdx|m XXqDwS@-M`W`x`| @@@~DwS@-M``d| AA@~DwC@-M`+dx|9XXvDwS@-M`yW`!x`|H@@@xDwS@-M`ˈ`d|AA@xDwC@-M`ljdx|XX@xDwS@-M`=W`1x`| @@vDwS@-M``d|EAAvDwC@-M`d`|#@@5zS@-M``d|)AA5zC@-M`dx|0XX5zS@-M`W`Qx`|0@@5zS@-M`,`d| 2AA5zC@-M`Ldx|k9XX5zS@-M`W`ax`|{9@@5zS@-M``d|:AA5zC@-M`dx|AXX5zS@-M`W`qx`|A@@5zS@-M`Z`d|X^AA5zC@-M`dx|cXX5zS@-M`W` x`|c@@5zS@-M`S`d|&eAA5zC@-M`fdx|&lXX5zS@-M`fW` x`|6l@@@5zS@-M``d|qmAA@5zC@-M`dx|tXX@~DwS@-M`#W` x`|t@@qDwS@-M`x`d|4vAAqDwC@-M`tdx|}XX@{DwS@-M`W` 0x`|}@@yDwS@-M`3`d| AAyDwC@-M`Jdx|dXXDwS@-M`W` @x`|t@@yDwS@-M``d|AAyDwC@-M`dx|XX@5zS@-M`#W` Px`|@@5zS@-M``d|=AA5zC@-M`}dx|XX5zS@-M` W` `x`|@@5zS@-M`>`d| AA5zC@-M`Kdx|bXX@5zS@-M`W` px`|v@@5zS@-M``d|̡AA5zC@-M` dx|6XX5zS@-M`vW` x`|L@@5zS@-M``d|AA5zC@-M`"!dx|YXXtDwS@-M`6W` x`|i@@}DwS@-M`6`d|AA}DwC@-M`7dx|!XXwDwS@-M`a?W` "x`|1@@vDwS@-M`?`d|AAvDwC@-M`@dx|XXvDwS@-M`-HW` 2x`|@@wDwS@-M`H`d|8AAwDwC@-M`xIdx|XX}DwS@-M`PW` Bx`|@@tDwS@-M`1Q`d|AAtDwC@-M`/Rdx|fXXyDwS@-M`YW` Rx`|t@@DwS@-M`Y`d|AADwC@-M`Zdx|XXyDwS@-M`3bW` bx`|@@@{DwS@-M`b`d|JAA@{DwC@-M`cdx|XXzS@-M`jW` rx`|@@zS@-M`:k`d|/AAzC@-M`oldx|XXzS@-M`WsW`!x`|(@@zS@-M`s`d|yAAzC@-M`tdx|XX@zS@-M`|W`!x`|@@zS@-M`P|`d|JAAzC@-M`}dx|_XXzS@-M`W`! x`|l@@zS@-M`J`d| AAzC@-M`Idx|XXzS@-M`*W`!0x`|@@zS@-M`d`d|wAAzC@-M`dx|XXzS@-M`ĕW`!@x`|@@@zS@-M``d| AA@zC@-M`dx|(XX@zS@-M`PW`!Px`|(@@zS@-M``d|m)AAzC@-M`dx|0XXzS@-M`W`!`x`|0@@zS@-M`:`d|"2AAzC@-M`bdx|W9XXzS@-M`W`!px`|e9@@zS@-M`ί`d|:AAzC@-M`dx|AXXzS@-M`W`!x`|A@@@zS@-M`U`d|gCAA@zC@-M`dx|JXXqDwS@-M`W`!x`|J@@@~DwS@-M``d|KAA@~DwC@-M`-dx|JSXXvDwS@-M`W`!#x`|XS@@@xDwS@-M``d|TAA@xDwC@-M`dx|\XX@xDwS@-M`EW`!3x`|\@@vDwS@-M`|`d|S]AAvDwC@-M`dx|dXX@~DwS@-M`W`!Cx`|d@@qDwS@-M`?`d|\fAAqDwC@-M`dx|mXX@{DwS@-M`W`!Sx`|m@@yDwS@-M``d|nAAyDwC@-M`;dx|vXXDwS@-M`^W`!cx`|+v@@yDwS@-M``d|PwAAyDwC@-M`dx|~XXtDwS@-M` W`!sx`|~@@}DwS@-M`[`d|IAA}DwC@-M`dx|XXwDwS@-M`W`"x`|@@vDwS@-M``d|AAvDwC@-M`Ddx|uXXvDwS@-M`W`"x`|@@wDwS@-M``d|AAwDwC@-M`1dx|5XX}DwS@-M`uW`" x`|A@@tDwS@-M``d|AAtDwC@-M`dx|XXyDwS@-M`5W`"0x`|@@DwS@-M`k`d|DAADwC@-M`dx|XXyDwS@-M` W`"@x`|@@@{DwS@-M`(!`d|AA@{DwC@-M`6"dx|kXXqDwS@-M`)W`"Px`|w@@@~DwS@-M`)`d|ʴAA@~DwC@-M` +dx|XXvDwS@-M`=2W`"`x`| @@@xDwS@-M`}2`d|LAA@xDwC@-M`3dx|XX@xDwS@-M`:W`"px`|@@vDwS@-M`;`d|AAvDwC@-M`<dx|CXX@~DwS@-M`CW`"x`|O@@qDwS@-M`C`d|AAqDwC@-M`Ddx|XX@{DwS@-M`?LW`"x`| @@yDwS@-M`uL`d|AAyDwC@-M`Mdx|XXDwS@-M`TW`"$x`|@@yDwS@-M`8U`d|@AAyDwC@-M`Vdx|}XXtDwS@-M`]W`"4x`|@@}DwS@-M`]`d|AA}DwC@-M`/_dx|8XXwDwS@-M`xfW`"Dx`|D@@vDwS@-M`f`d|AAvDwC@-M`gdx|XXvDwS@-M`oW`"Tx`|@@wDwS@-M`3o`d|(AAwDwC@-M`hpdx|QXX}DwS@-M`wW`"dx`|_@@tDwS@-M`w`d|AAtDwC@-M`ydx| XXyDwS@-M`MW`"tx`| @@DwS@-M``d|V AADwC@-M`dx|XXyDwS@-M`׈W`#x`|@@@{DwS@-M` `d|AA@{DwC@-M`:dx|TXXqDwS@-M`W`#x`|`@@@~DwS@-M`ˑ`d|AA@~DwC@-M`ܒdx|$XXvDwS@-M`QW`# x`|$@@@xDwS@-M``d|a%AA@xDwC@-M`dx|,XX@xDwS@-M`W`#0x`|,@@vDwS@-M`H`d|E.AAvDwC@-M`dx|5XX@~DwS@-M`׫W`#@x`|5@@qDwS@-M` `d|6AAqDwC@-M`4dx|,>XX@{DwS@-M`lW`#Px`|9>@@yDwS@-@@@{DwC@->M``d|t?AAyDwC@-M`dx|FXXDwS@-M`W`#`x`|F@@yDwS@-M`Y`d|)HAAyDwC@-M`idx|OXXtDwS@-M`W`#px`|O@@}DwS@-M` `d| QAA}DwC@-M`Idx|bXXXwDwS@-M`W`#x`|qX@@vDwS@-M``d|YAAvDwC@-M`dx|aXXvDwS@-M`NW`#x`|a@@wDwS@-M``d||bAAwDwC@-M`d`|i@@}DwS@-M``d|VoAA}DwC@-M`dx|vXXyDwS@-M` W`#5x`|v@@DwS@-M`Y`d|`xAADwC@-M`dx|\XX@5zS@-M`W`#Ex`|j@@@5zS@-M``d|AA@5zC@-M`dx|!XX5zS@-M`aW`#Ux`|.@@5zS@-M``d|AA5zC@-M`dx|XXzS@-M`W`#ex`|@@zS@-M`$`d|AAzC@-M`Edx|1XXzS@-M`qW`#ux`|@@@zS@-M``d|AAzC@-M`dx|¡XXzS@-M`W`$x`|С@@@zS@-M`V`d|'AA@zC@-M`gdx|QXXzS@-M` W`$x`|_@@zS@-M` `d|AAzC@-M`!dx|ܲXXzS@-M`)W`$ x`|@@zS@-M`j)`d|.AAzC@-M`n*dx|lXX@zS@-M`1W`$0x`|}@@zS@-M`1`d|AAzC@-M`2dx|$XXzS@-M`d:W`$@x`|9@@zS@-M`:`d|iAAzC@-M`;dx|XXzS@-M`CW`$Px`|@@zS@-M`IC`d|6AAzC@-M`vDdx|XX5zS@-M`KW`$`x`|@@5zS@-M`4L`d|AA5zC@-M`Ldx|PXX5zS@-M`TW`$px`|^@@5zS@-M`T`d|AA5zC@-M`Udx|XX5zS@-M`Y]W`$x`|'@@5zS@-M`]`d|AA5zC@-M`^dx|XX5zS@-M`fW`$x`|@@@5zS@-M`ff`d|EAA@5zC@-M`gdx|XX@zS@-M`nW`$&x`|@@zS@-M`!o`d|AAzC@-M`Dpdx|,XXzS@-M`lwW`$6x`|:@@zS@-M`w`d|AAzC@-M`xdx| XXzS@-M`(W`$Fx`| @@zS@-M`y`d|5 AAzC@-M`udx|XXzS@-M`W`$Vx`|@@@zS@-M`?`d|AAA@zC@-M`dx|xXX@zS@-M`W`$fx`|@@zS@-M``d|AAzC@-M`1dx|#XXzS@-M`ؙW`$vx`|#@@zS@-M`(`d| %AAzC@-M`Jdx|M,XXzS@-M`W`%x`|Z,@@zS@-M`ۢ`d|-AAzC@-M`ܣdx|5XXzS@-M`XW`%x`|%5@@@zS@-M``d|M6AA@zC@-M`dx|=XXzS@-M`W`% x`|=@@zS@-@@zC@->M`U`d|>AAzC@-M`'dx|nFXXzS@-M`W`%0x`|{F@@zS@-M`"`d|GAAzC@-M`dx|NXXzS@-M`W`%@x`|N@@@zS@-M`z`d|PAA@zC@-M`_dx|WXXzS@-M`W`%Px`|W@@zS@-M`;`d|XAAzC@-M`>dx|_oXXzS@-M`W`%`x`|ko@@zS@-M``d|pAAzC@-M`dx|xXX@zS@-M`^W`%px`|*x@@zS@-M``d|yAAzC@-M`dx|XXzS@-M`W`%x`|ˀ@@zS@-M`I`d|AAzC@-M`\dx|CXXzS@-M`W`%x`|P@@zS@-M``d|XAAzC@-M` dx|XX@zS@-M`C W`%'x`|@@zS@-M` `d|LAAzC@-M` dx|ĚXXzS@-M` W`%7x`|К@@zS@-M`b `d| AAzC@-M`L dx|XXzS@-M` W`%Gx`|@@zS@-M`  `d|AAzC@-M`6 dx|}XXyDwS@-M`! W`%Wx`|@@@{DwS@-M`" `d|ìAA@{DwC@-M`# dx|سXXqDwS@-M`* W`%gx`|@@@~DwS@-M`P* `d|AA@~DwC@-M`;+ dx|&XXvDwS@-M`f2 W`%wx`|7@@@xDwS@-M`2 `d|[AA@xDwC@-M`3 dx|XX@xDwS@-M`: W`&x`|@@vDwS@-M`;; `d| AAvDwC@-M`L< dx|6XX@~DwS@-M`vC W`&x`|B@@qDwS@-M`C `d|AAqDwC@-M`D dx|XX@{DwS@-M`L W`& x`|@@yDwS@-M`[L `d|WAAyDwC@-M`M dx|XXDwS@-M`T W`&0x`|@@yDwS@-M`U `d|AAyDwC@-M`(V dx|FXX@5zS@-M`] W`&@x`|]@@5zS@-M`] `d|AA5zC@-M`^ dx|XX5zS@-M`e W`&Px`|@@5zS@-M`f `d|AA5zC@-M` g dx|XX5zS@-M`Qn W`&`x`|@@5zS@-M`n `d|pAA5zC@-M`o dx|^XX5zS@-M`v W`&px`|i@@5zS@-M`v `d|AA5zC@-M`w dx| XX5zS@-M`^ W`&x`|. @@5zS@-M` `d| AA5zC@-M`ۀ dx|XX5zS@-M` W`&x`|@@5zS@-M`? `d|AA5zC@-M`V dx|E'XX5zS@-M` W`&(x`|T'@@5zS@-M`Н `d|(AA5zC@-M`מ dx|0XX5zS@-M`G W`&8x`|0@@5zS@-M` `d|1AA5zC@-M`֧ dx|8XX}DwS@-M` W`&Hx`|8@@tDwS@-M`9 `d| :AAtDwC@-M`` dx|NAXXwDwS@-M` W`&Xx`|\A@@vDwS@-M`Ʒ `d|BAAvDwC@-M` dx|'JXXvDwS@-M`g W`&hx`|4J@@wDwS@-M` `d|KAAwDwC@-M` dx|RXXtDwS@-M`, W`&xx`|R@@}DwS@-M`e `d|UTAA}DwC@-M` dx|[XXyDwS@-M` W`'x`|[@@DwS@-M`{ `d|!]AADwC@-M`a dx|+dXXyDwS@-M`k W`'x`|8d@@@{DwS@-M` `d|eAA@{DwC@-M` dx|lXX5zS@-M` W`' x`|l@@5zS@-M`I `d|;nAA5zC@-M`{ dx|uXX5zS@-M` W`'0x`|u@@@5zS@-M` `d|vAA@5zC@-M`0 dx|Q~XX@5zS@-M` W`'@x`|^~@@5zS@-M` `d|AA5zC@-M` d`|ކ@@5zS@-M` `d|AA5zC@-M` dx|XX@5zS@-M`A W`'`x`|@@@5zS@-M` `d|\AA@5zC@-M` d`|@@5zS@-M` `d|tAA5zC@-M` dx|OXX5zS@-M` W`' x`|]@@5zS@-M` `d|AA5zC@-M` dx|XX5zS@-M`&( W`'x`|@@5zS@-M`v( `d|WAA5zC@-M`) dx|XX5zS@-M`0 W`')x`|@@5zS@-M`21 `d|AA5zC@-M`02 dx|XXzS@-M`9 W`'9x`|@@@zS@-M`9 `d|AA@zC@-M`: dx| XX@zS@-M`JB W`'Ix`|@@zS@-M`B `d|AAzC@-M`C dx|XXzS@-M` K W`'Yx`|@@zS@-M``K `d|@AAzC@-M`L dx|zXXzS@-M`S W`'ix`|@@zS@-M`S `d|AAzC@-M` U dx|1XXzS@-M`q\ W`'yx`|>@@@zS@-M`\ `d|AA@zC@-M`4y dx|XXzS@-M` ~ W`(x`|@@zS@-M`Y~ `d|- AAzC@-M`m dx|XXXzS@-M` W`(x`|g@@zS@-M`ʆ `d|AAzC@-M` dx|XXzS@-M` W`( x`|@@@zS@-M`@ `d|AA@zC@-M`\ dx|?!XXzS@-M` W`(0x`|N!@@zS@-M` `d|"AAzC@-M` dx|)XXzS@-M` W`(@x`|)@@zS@-M`Z `d|J+AAzC@-M` dx|2XX@zS@-M` W`(Px`|2@@zS@-M` `d|4AAzC@-M`A dx|g;XXzS@-M` W`(`x`|u;@@zS@-M`ر `d|D@@zS@-M` `d|EAAzC@-M`ѻ dx|LXX@zS@-M` W`( x`|L@@zS@-M`= `d|0NAAzC@-M`p dx|UXXzS@-M`V W`(x`| U@@zS@-M` `d|xVAAzC@-M` dx|a]XXzS@-M` W`(*x`|k]@@zS@-M` `d|^AAzC@-M` dx|eXXzS@-M` W`(:x`|e@@@zS@-M`a `d|fAA@zC@-M`= dx|nXX@KGxS@-M` W`(Jx`|n@@LGxS@-M` `d|oAALGxC@-M`. dx|LwXX@GxS@-M` W`(Zx`|\w@@CGxS@-M` `d|xAACGxC@-M` dx| XXCGxS@-M`J W`(jx`|@@@GxS@-M` `d|{AA@GxC@-M` dx|XXLGxS@-M` W`(zx`|@@@KGxS@-M` `d|߉AA@KGxC@-M` dx|)XX@KGxS@-M`i W`)x`|8@@LGxS@-M` `d|AALGxC@-M` dx|XXLGxS@-M`. W`)x`|@@@KGxS@-M`i `d|QAA@KGxC@-M` dx|XX@KGxS@-M`% W`) x`|@@LGxS@-M`W& `d|AALGxC@-M`1' dx|mXXLGxS@-M`. W`)0x`|z@@@KGxS@-M`. `d|AA@KGxC@-M`0 dx|.XX@KGxS@-M`n7 W`)@x`|=@@LGxS@-M`7 `d|AALGxC@-M`8 dx|XXLGxS@-M`@ W`)Px`|@@@KGxS@-M`9@ `d|SAA@KGxC@-M`A dx|~XX@KGxS@-M`H W`)`x`|@@LGxS@-M`H `d|AALGxC@-M`J dx|XXLGxS@-M`OQ W`)px`|@@@KGxS@-M`Q `d|YAA@KGxC@-M`R dx|XX@KGxS@-M`#Z W`) x`|@@LGxS@-M`_Z `d|MAALGxC@-M`[ dx|XXLGxS@-M`b W`)x`|@@@KGxS@-M`c `d|AA@KGxC@-M`Gd dx|eXX@KGxS@-M`k W`)+x`|r@@LGxS@-M`k `d|AALGxC@-M`#m dx|XXLGxS@-M`Ot W`);x`|@@@KGxS@-M`t `d|CAA@KGxC@-M`u dx|XX@KGxS@-M`} W`)Kx`|@@LGxS@-M`I} `d|3AALGxC@-M`s~ dx|XXqDwS@-M`Յ W`)[x`|@@@~DwS@-M`+ `d|AA@~DwC@-M`2 dx|rXXvDwS@-M` W`)kx`|@@@xDwS@-M` `d|AA@xDwC@-M`- dx|!XX@xDwS@-M`^ W`){x`|0!@@vDwS@-M` `d|_"AAvDwC@-M` dx|)XX@~DwS@-M` W`*x`|)@@qDwS@-M`g `d|+AAqDwC@-M`[ dx|2XX@{DwS@-M`٨ W`*x`|2@@yDwS@-M`9 `d|3AAyDwC@-M`+ dx|_;XXDwS@-M` W`* x`|m;@@yDwS@-M` `d|M`º `d|tEAAtDwC@-M` dx|LXXwDwS@-M` W`*@x`|L@@vDwS@-M`A `d|NAAvDwC@-M`R dx|~UXXvDwS@-M` W`*Px`|U@@wDwS@-M` `d|VAAwDwC@-M` dx|^XXtDwS@-M`N W`*`x`|^@@}DwS@-M` `d|]_AA}DwC@-M` dx|fXXLGxS@-M` W`*px`|f@@@KGxS@-M`$ `d|'hAA@KGxC@-M`g dx|{oXX@NGxS@-M` W`* x`|o@@GGxS@-M` `d|pAAGGxC@-M` dx|wXXCGxS@-M` W`*x`|w@@OGxS@-M` `d|/yAAOGxC@-M`o dx|XXOGxS@-M` W`*,x`|@@CGxS@-M`@ `d|AACGxC@-M`$ dx|+XXGGxS@-M`k W`*<x`|8@@@NGxS@-M` `d|ȊAA@NGxC@-M` dx|XX@KGxS@-M` W`*Lx`|ʑ@@LGxS@-M`` `d|AALGxC@-M`^ dx|XX@GxS@-M` W`*\x`|@@CGxS@-M` `d|͛AACGxC@-M`  dx|HXXCGxS@-M` W`*lx`|U@@@GxS@-M` `d|AA@GxC@-M` dx|)XXyDwS@-M`i" W`*|x`|?@@DwS@-M`" `d|AADwC@-M`# dx|XXLGxS@-M`* W`+x`|Ĵ@@@KGxS@-M`#+ `d|AA@KGxC@-M`U, dx|RXX@NGxS@-M`3 W`+x`|_@@GGxS@-M`3 `d|AAGGxC@-M`4 dx|XXCGxS@-M`< W`+ x`|@@OGxS@-M`r< `d|1AAOGxC@-M`q= dx|XXOGxS@-M`Q W`+0x`|@@CGxS@-M`R `d|AACGxC@-M`FS dx|>XXGGxS@-M`~Z W`+@x`|L@@@NGxS@-M`Z `d|AA@NGxC@-M`[ d`|@@@KGxS@-M`k `d|AA@KGxC@-M`l dx|XX@KGxS@-M`$t W`+`x`|@@LGxS@-M`]t `d|IAALGxC@-M`u dx|XXLGxS@-M`| W`+px`|@@@KGxS@-M` } `d|AA@KGxC@-M`C~ dx|QXXyDwS@-M` W`+ x`|c@@@{DwS@-M`ƅ `d|AA@{DwC@-M` dx|XXqDwS@-M` W`+x`|@@@~DwS@-M`X `d|<AA@~DwC@-M`| dx|} XX@KGxS@-M` W`+-x`| @@LGxS@-M` `d|!AALGxC@-M` dx|O)XXvDwS@-M` W`+=x`|d)@@@xDwS@-M`Ɵ `d|*AA@xDwC@-M` dx|1XX@xDwS@-M` W`+Mx`|1@@vDwS@-M`J `d|J3AAvDwC@-M` dx|:XX@~DwS@-M`ְ W`+]x`|:@@qDwS@-M` `d|;AAqDwC@-M`* dx|CCXX@{DwS@-M` W`+mx`|PC@@yDwS@-M` `d|DAAyDwC@-M`к dx|LXXDwS@-M`O W`+}x`|"L@@yDwS@-M` `d|sMAAyDwC@-M` dx|TXX}DwS@-M` W`,x`|T@@tDwS@-M`, `d|%VAAtDwC@-M`e dx|v]XXwDwS@-M` W`,x`|]@@vDwS@-M` `d|^AAvDwC@-M` dx| fXXvDwS@-M`M W`, x`|$f@@wDwS@-M` `d|IgAAwDwC@-M` dx|nXXtDwS@-M` W`,0x`|n@@}DwS@-M`o `d|IpAA}DwC@-M` dx|wXXyDwS@-M` W`,@x`|w@@DwS@-M`Q `d|)yAADwC@-M`i dx|XXyDwS@-M` W`,Px`|@@@{DwS@-M`* `d|AA@{DwC@-M`) dx|vXXMwS@-M` W`,`x`|@@@MwS@-M` `d|ǕAA@MwC@-M` d`|@@MwS@-M` `d|AAMwC@-M` dx|XX@MwS@-M`F W`,x`|@@MwS@-M` `d|gAAMwC@-M`! dx|вXXMwS@-M`) W`,x`|߲@@@MwS@-M`e) `d|7AA@MwC@-M`w* dx|XXMwS@-M`1 W`,.x`|@@@MwS@-M`,2 `d|AA@MwC@-M`;3 dx|/XX@MwS@-M`o: W`,>x`|=@@MwS@-M`: `d|zAAMwC@-M`; dx|XXMwS@-M`9C W`,Nx`|@@MwS@-M`C `d|WAAMwC@-M`D dx|wXXMwS@-M`K W`,^x`|@@MwS@-M`K `d|AAMwC@-M`/M dx|8XXMwS@-M`xT W`,nx`|G@@MwS@-M`T `d|AAMwC@-M`U dx|XX@MwS@-M`>] W`,~x`| @@@MwS@-M`] `d|SAA@MwC@-M`^ dx|XXMwS@-M`f W`-x`|@@@MwS@-M`Vf `d|AA@MwC@-M`Ug dx|XXMwS@-M`n W`-x`|@@@MwS@-M`o `d|AA@MwC@-M`&p dx|DXXMwS@-M`w W`- x`|P@@@MwS@-M`w `d|AA@MwC@-M`y dx| XX@MwS@-M` W`-0x`| @@MwS@-M`i `d|' AAMwC@-M`g dx|XXXMwS@-M` W`-@x`|e@@MwS@-M` `d|AAMwC@-M` dx|XXMwS@-M`X W`-Px`|%@@MwS@-M` `d|xAAMwC@-M` dx|#XXMwS@-M` W`-`x`|#@@MwS@-M`; `d|Y%AAMwC@-M` dx|N,XXMwS@-M` W`-px`|[,@@MwS@-M` `d|-AAMwC@-M` dx|4XXMwS@-M`9 W`-x`|5@@MwS@-M` `d|.6AAMwC@-M`n dx|=XXMwS@-M`ٳ W`-x`|=@@MwS@-M` `d|>AAMwC@-M` dx|FFXXMwS@-M` W`-/x`|RF@@MwS@-M`׼ `d|GAAMwC@-M`ν dx|NXXMwS@-M`1 W`-?x`|N@@MwS@-M`u `d|kPAAMwC@-M` dx|lWXXMwS@-M` W`-Ox`|zW@@MwS@-M` `d|XAAMwC@-M` dx|_XXMwS@-M`6 W`-_x`|`@@MwS@-M` `d|]aAAMwC@-M` dx|hXXMwS@-M` W`-ox`|h@@MwS@-M`. `d|iAAMwC@-M`1 dx|VqXXMwS@-M` W`-x`|bq@@MwS@-M` `d|rAAMwC@-M`5 dx|zXXMwS@-M`O W`.x`|&z@@MwS@-M` `d|r{AAMwC@-M` dx|QXXMwS@-M`W`.x`|^@@MwS@-M``d|AAMwC@-M`dx|XXMwS@-M`W`. x`|@@MwS@-M``d|ؙAAMwC@-M`dx|XXMwS@-M`W`.0x`|@@MwS@-M`)`d|ۢAAMwC@-M`dx|XXMwS@-M`V W`.@x`|"@@MwS@-M` `d|aAAMwC@-M`!dx|ʲXXMwS@-M` )W`.Px`|ز@@MwS@-M`X)`d|AAMwC@-M`U*dx|XXMwS@-M`1W`.`x`|@@MwS@-M`2`d|KAAMwC@-M`3dx|BXXMwS@-M`:W`.px`|N@@MwS@-M`:`d|AAMwC@-M` <dx|XXMwS@-M`WCW`.x`|#@@MwS@-M`C`d||AAMwC@-M`Ddx|XXMwS@-M`KW`. x`|@@MwS@-M`,L`d|AAMwC@-M`;Mdx|IXXMwS@-M`TW`.0x`|T@@MwS@-M`T`d|AAMwC@-M`Udx|XXMwS@-M`6]W`.@x`|@@MwS@-M`]`d||AAMwC@-M`^dx|XXMwS@-M`eW`.Px`|@@MwS@-M`Mf`d|AAMwC@-M`Tgdx|!XXMwS@-M`anW`.`x`|,@@MwS@-M`n`d|AAMwC@-M`odx|XXMwS@-M`(wW`.px`|@@MwS@-M`ww`d|5AAMwC@-M`uxdx|o XXMwS@-M`W`.x`|| @@MwS@-M``d| AAMwC@-M` dx|'XXMwS@-M`gW`/x`|2@@MwS@-M``d|AAMwC@-M`މdx|XXMwS@-M`W`/x`|@@MwS@-M`D`d|AAMwC@-M`Zdx|d#XXMwS@-M`W`/ x`|o#@@MwS@-M``d|$AAMwC@-M` d`|+@@@>zS@-zS@-M`W`/0x`|0@@>zC@->M`֦`d|1AA@>zC@-M`ߧdx|8XX@>zS@-M`W`/@x`|8@@>zS@-zC@->M`[`d|,:AA>zC@-M`ldx|AXX@>zS@-M`ķW`/Px`|A@@@>zS@-zC@->M` `d|BAA@>zC@-M`dx| JXX>zS@-M`KW`/`x`|J@@>zS@-zC@->M`z`d|uKAA>zC@-M`dx|RXX>zS@-M`W`/px`|R@@>zS@-zC@->M`G`d|6TAA>zC@-M`vdx|b[XX>zS@-M`W`/x`|q[@@>zS@-zC@->M``d|\AA>zC@-M` dx|(dXX>zS@-M`hW`/!x`|8d@@>zS@-zC@->M``d|eAA>zC@-M`dx|lXX@>zS@-M`#W`/1x`|l@@@>zS@-zC@->M`W`d|;nAA@>zC@-M`{dx|uXX>zS@-M`W`/Ax`|u@@@>zS@-zC@->M``d| wAA@>zC@-M`Ldx|T~XX@>zS@-M`W`/Qx`|a~@@>zS@-zC@->M``d|AA>zC@-M`dx|XX@>zS@-M`\W`/ax`|,@@@>zS@-zC@->M``d|yAA@>zC@-M`dx|XX>zS@-M`!W`/qx`|@@>zS@-zC@->M`O`d|AAA>zC@-M`dx|lXX>zS@-M`W`/x`|y@@>zS@-zC@->M` `d|%AA>zC@-M`e+dx|XX>zS@-M`0W`0x`|@@>zS@-zC@->M`%1`d|AA>zC@-M`)2dx|XX>zS@-M`!9W`0x`|@@>zS@-zC@->M`9`d|GAA>zC@-M`:dx|XX@>zS@-M`AW`0 x`|@@@>zS@-zC@->M`>B`d|AA@>zC@-M`dx|_XX>zS@-M`_W`00x`|l@@@>zS@-zC@->M``d|AA@>zC@-M` dx|XX@>zS@-M`W`0@x`|@@>zS@-zC@->M`S`d|AAA>zC@-M`Adx|XX@>zS@-M`W`0Px`|@@@>zS@-zC@->M``d|*AA@>zC@-M`*dx|gXX>zS@-M`g"W`0`x`|t@@>zS@-zC@->M`"`d|AA>zC@-M`#dx|XX>zS@-M`+W`0px`|/@@>zS@-zC@->M`T+`d|~AA>zC@-M`~,dx|XX>zS@-M`3W`0x`|@@>zS@-zC@->M`4`d|AA>zC@-M`4dx|pXX>zS@-M`p<W`0"x`|}@@>zS@-zC@->M`<`d| AA>zC@-M`=dx| XX@>zS@-M` EW`02x`|@@@>zS@-zC@->M`oE`d|wAA@>zC@-M`wFdx|XX>zS@-M`MW`0Bx`|@@@>zS@-zC@->M`7N`d|*AA@>zC@-M`*Odx|"XX@>zS@-M`VW`0Rx`|"@@>zS@-zC@->M`V`d|#AA>zC@-M`Wdx|R+XXLGxS@-M`R_W`0bx`|^+@@@KGxS@-M`_`d|,AA@KGxC@-M``dx| 4XX@GxS@-M` hW`0rx`|4@@CGxS@-M`lh`d|X5AACGxC@-M`Xidx|<XXCGxS@-M`pW`0x`|<@@@GxS@-M`(q`d|>AA@GxC@-M`rdx|zEXX@KGxS@-M`zyW`1x`|E@@LGxS@-M`y`d|FAALGxC@-M`zdx|BNXX@NGxS@-M`BW`1x`|PN@@GGxS@-M`o`d|OAAGGxC@-M`dx|VXXCGxS@-M`W`1 x`|W@@OGxS@-M`X`d|FXAAOGxC@-M`Fdx|_XXOGxS@-M`W`10x`|_@@CGxS@-M``d|aAACGxC@-M`dx|whXXGGxS@-M`wW`1@x`|h@@@NGxS@-M`؜`d|iAA@NGxC@-M`ޝdx|4~XXLGxS@-M`4W`1Px`|@~@@@KGxS@-M``d|AA@KGxC@-M`dx|XX@GxS@-M`W`1`x`|ņ@@CGxS@-M``d|AACGxC@-M`dx|qXXCGxS@-M`qW`1px`||@@@GxS@-M``d|͐AA@GxC@-M`dx|+XX@KGxS@-M`+W`1x`|6@@LGxS@-M``d|AALGxC@-M`dx|XX@NGxS@-M`W`1#x`|@@GGxS@-M`D`d|AAAGGxC@-M`Adx|XXCGxS@-M`W`13x`|@@OGxS@-M``d|AAOGxC@-M`dx|2XXOGxS@-M`2W`1Cx`|?@@CGxS@-M``d|AACGxC@-M`dx|XXGGxS@-M`W`1Sx`|@@@NGxS@-M`J`d|0AA@NGxC@-M`0dx|XXLGxS@-M`W`1cx`|@@@KGxS@-M``d|AA@KGxC@-M`dx|LXX@GxS@-M`LW`1sx`|W@@CGxS@-M``d|AACGxC@-M`dx| XXCGxS@-M` W`1x`|!@@@GxS@-M`; `d|pAA@GxC@-M`p dx|XX@KGxS@-M`W`2x`|@@LGxS@-M``d|AALGxC@-M`dx|eXX@NGxS@-M`eW`2x`|t@@GGxS@-M``d|AAGGxC@-M`dx|8XXCGxS@-M`8/W`2 x`|L@@OGxS@-M`e/`d|AAOGxC@-M`0dx|XXOGxS@-M`7W`20x`|@@CGxS@-M`7`d|AACGxC@-M`8dx|- XXGGxS@-M`-@W`2@x`|8 @@@NGxS@-M`Z@`d| AA@NGxC@-M`Adx|XXLGxS@-M`HW`2Px`|@@@KGxS@-M`I`d|DAA@KGxC@-M`DJdx|XX@GxS@-M`QW`2`x`|@@CGxS@-M`Q`d|AACGxC@-M`Rdx|Z&XXCGxS@-M`ZZW`2px`|n&@@@GxS@-M`Z`d|'AA@GxC@-M`[dx|.XX@KGxS@-M`bW`2x`|.@@LGxS@-M` c`d|90AALGxC@-M`9ddx|7XX@NGxS@-M`kW`2$x`|7@@GGxS@-M`k`d|8AAGGxC@-M`ldx|Q@XXCGxS@-M`QtW`24x`|e@@@OGxS@-M`t`d|AAAOGxC@-M`udx|IXXOGxS@-M`}W`2Dx`|&I@@CGxS@-M`}`d|JAACGxC@-M`~dx|QXXGGxS@-M`W`2Tx`|Q@@@NGxS@-M`Ӆ`d|RAA@NGxC@-M`dx|UZXXLGxS@-M`UW`2dx`|eZ@@@KGxS@-M``d|[AA@KGxC@-M`dx|cXX@GxS@-M`W`2tx`|+c@@CGxS@-M`B`d|ddAACGxC@-M`ddx|/xXXCGxS@-M`/W`2x`|Ex@@@GxS@-M`g`d|}yAA@GxC@-M`}dx|XX@KGxS@-M`W`3x`|@@LGxS@-M``d|AAALGxC@-M`Ad`|}@@@NGxS@-M` `d|%AA@NGxC@-M`%dx|XXCGxS@-M`W`3 x`|@@OGxS@-M``d|AAOGxC@-M`dx|\XXOGxS@-M`\W`30x`|l@@CGxS@-M``d||AACGxC@-M`|dx|XX@NGxS@-M`W`3@x`|ҧ@@GGxS@-M``d|"AAGGxC@-M`"dx|~XXLGxS@-M`~W`3Px`|@@@KGxS@-M``d|ܱAA@KGxC@-M`dx|EXX@GxS@-M`EW`3`x`|W@@CGxS@-M`y`d|κAACGxC@-M`dx| XXCGxS@-M` W`3px`|@@@GxS@-M`E`d|<AA@GxC@-M`<dx|XX@KGxS@-M`W`3x`|@@LGxS@-M``d|AALGxC@-M`dx|yXXGGxS@-M`yW`3%x`|@@@NGxS@-M``d|AA@NGxC@-M`dx|?XXCGxS@-M`?W`35x`|M@@OGxS@-M`t`d|AAOGxC@-M`dx|XXOGxS@-M`W`3Ex`|@@CGxS@-M`0`d|AACGxC@-M`dx|XX@NGxS@-M`.W`3Ux`|@@GGxS@-M`.`d|AAGGxC@-M`0dx|XXLGxS@-M`7W`3ex`|@@@KGxS@-M`7`d| AA@KGxC@-M` 9dx|G XX@GxS@-M`G@W`3ux`|Z @@CGxS@-M`@`d| AACGxC@-M`Adx|XXCGxS@-M`HW`3x`|@@@GxS@-M`I`d|#AA@GxC@-M`#Jdx|XX@KGxS@-M`QW`4x`|@@LGxS@-M`Q`d|AALGxC@-M`Sdx|Q&XXGGxS@-M`QZW`4x`|b&@@@NGxS@-M`Z`d|~'AA@NGxC@-M`~[dx|/XXCGxS@-M`cW`4 x`|)/@@OGxS@-M`Jc`d|0AAOGxC@-M`ddx|7XXOGxS@-M`kW`40x`|7@@CGxS@-M`l`d|69AACGxC@-M`6mdx|@XX@NGxS@-M`tW`4@x`|@@@GGxS@-M`t`d|BAAGGxC@-M`vdx|bIXXLGxS@-M`b}W`4Px`|uI@@@KGxS@-M`}`d|JAA@KGxC@-M`~dx|RXX@GxS@-M`W`4`x`|(R@@CGxS@-M`F`d|nSAACGxC@-M`ndx|ZXXCGxS@-M`ˎW`4px`|Z@@@GxS@-M``d|0\AA@GxC@-M`0dx|RcXX@KGxS@-M`RW`4x`|cc@@LGxS@-M``d|dAALGxC@-M`dx|lXX@>zS@-M`W`4&x`|l@@@>zS@-zC@->M`l`d|YmAA@>zC@-M`Ydx|tXX>zS@-M`W`46x`|t@@>zS@-zC@->M`#`d|WvAA>zC@-M`Wdx||}XX>zS@-M`|W`4Fx`|}@@>zS@-zC@->M`ޱ`d|~AA>zC@-M`вdx|@XX>zS@-M`@W`4Vx`|M@@>zS@-zC@->M``d|AA>zC@-M`dx|XX>zS@-M`W`4fx`| @@>zS@-zC@->M`d`d|vAA>zC@-M`vdx|×XX@>zS@-M`W`4vx`|ϗ@@@>zS@-zC@->M`"`d|$AA@>zC@-M`$dx|XX>zS@-M`W`4x`|@@@>zS@-zC@->M``d|AA@>zC@-M`dx|XX@>zS@-M`W`5x`|@@>zS@-zC@->M``d|AA>zC@-M`dx|NXX@>zS@-M`NW`5x`|Z@@@>zS@-zC@->M``d|0AA@>zC@-M`0dx|XX>zS@-M`W`5 x`|@@>zS@-zC@->M`o`d|kAA>zC@-M`kdx|XX>zS@-M`W`50x`|@@>zS@-zC@->M`=`d|'AA>zC@-M`'dx|XX>zS@-M` W`5@x`|@@>zS@-zC@->M` `d|AA>zC@-M` dx|FXX>zS@-M`FW`5Px`|R@@>zS@-zC@->M`s`d|AA>zC@-M`dx|XX@>zS@-M`W`5`x`|@@@>zS@-zC@->M`e`d|sAA@>zC@-M`sdx|XX>zS@-M`&W`5px`|@@@>zS@-zC@->M`!'`d|"AA@>zC@-M`"(dx|uXX@>zS@-M`u/W`5x`|@@>zS@-zC@->M`/`d|AA>zC@-M`0dx|XX@>zS@-M`8W`5'x`|@@@>zS@-zC@->M`a8`d|>AA@>zC@-M`>9dx| XX>zS@-M`@W`57x`| @@>zS@-zC@->M`@`d| AA>zC@-M`Adx|3XX>zS@-M`3IW`5Gx`|A@@>zS@-zC@->M`I`d|AA>zC@-M`Jdx|XX>zS@-M`QW`5Wx`|@@>zS@-zC@->M`CR`d|:AA>zC@-M`:Sdx|%XX>zS@-M`YW`5gx`|%@@>zS@-zC@->M`FZ`d| 'AA>zC@-M` [dx|R.XX>zS@-M`RbW`5wx`|_.@@>zS@-zC@->M`b`d|/AA>zC@-M`cdx|6XX>zS@-M`jW`5x`|7@@>zS@-zC@->M`(k`d|J8AA>zC@-M`Jldx|?XX>zS@-M`sW`6x`|?@@>zS@-zC@->M`Qt`d|AAA>zC@-M`udx|HXX>zS@-M`|W`6x`|*H@@>zS@-zC@->M`w|`d|IAA>zC@-M`}dx|PXX>zS@-M`W`6 x`|P@@>zS@-zC@->M``d| RAA>zC@-M` dx|\YXX>zS@-M`\W`60x`|jY@@>zS@-zC@->M``d|ZAA>zC@-M`dx|aXX>zS@-M`W`6@x`|a@@>zS@-zC@->M`C`d|IcAA>zC@-M`Idx|mjXX>zS@-M`mW`6Px`|yj@@>zS@-zC@->M``d|kAA>zC@-M`dx|sXX>zS@-M`W`6`x`|(s@@>zS@-zC@->M`}`d|tAA>zC@-M`dx|{XX>zS@-M`ܯW`6px`|{@@>zS@-zC@->M`B`d|B}AA>zC@-M`Bdx|XX>zS@-M`W`6x`|@@>zS@-zC@->M``d|AA>zC@-M`dx|VXX>zS@-M`VW`6(x`|c@@>zS@-zC@->M``d|AA>zC@-M`dx|XX>zS@-M`W`68x`|+@@>zS@-zC@->M`L`d|AA>zC@-M`dx|؞XX>zS@-M`W`6Hx`|@@>zS@-zC@->M`E`d|.AA>zC@-M`.dx|XX>zS@-M`W`6Xx`|@@>zS@-zC@->M``d|ըAA>zC@-M`dx|dXX>zS@-M`dW`6hx`|u@@>zS@-zC@->M``d|AA>zC@-M`dx|+XX>zS@-M`+W`6xx`|K@@>zS@-zC@->M`Y`d|AA>zC@-M`d`|@@>zS@-zS@-M`!W`6x`|Q@@>zC@->M`Q`d|AA>zC@-M`dx|XX>zS@-M`W`7x`|@@>zS@-zC@->M``d|AA>zC@-M`dx|hXX>zS@-M`h W`7x`|x@@>zS@-zC@->M` `d|AA>zC@-M` dx|!XX>zS@-M`!W`7 x`|/@@>zS@-zC@->M`X`d|AA>zC@-M`dx|XX>zS@-M`W`70x`| @@>zS@-zC@->M`z`d|LAA>zC@-M`Ldx|XX>zS@-M`%W`7@x`|@@>zS@-zC@->M`%`d|AA>zC@-M`&dx|;XX>zS@-M`;.W`7Px`|J@@>zS@-zC@->M`.`d|AA>zC@-M`/dx|XX>zS@-M`6W`7`x`|@@>zS@-zC@->M`7`d|+AA>zC@-M`+8dx|r XX>zS@-M`r?W`7p x`| @@>zS@-zC@->M`?`d| AA>zC@-M`@dx|0XX>zS@-M`0HW`7x`|@@@>zS@-zC@->M`H`d|AA>zC@-M`Idx|XX>zS@-M`PW`7)x`|@@>zS@-zC@->M`P`d|,AA>zC@-M`,Rdx|W%XX@GxS@-M`WYW`79x`|g%@@GGxS@-M`Y`d|&AAGGxC@-M`Zdx|-XXLGxS@-M`aW`7Ix`|-@@OGxS@-M`?b`d|,/AAOGxC@-M`,cdx|6XXCGxS@-M`jW`7Yx`|6@@CGxS@-M`j`d|18AACGxC@-M`1ldx|}?XXGGxS@-M`}sW`7ix`|?@@@GxS@-M`s`d|@AA@GxC@-M`tdx|EHXX@GxS@-M`E|W`7yx`|UH@@GGxS@-M`|`d|IAAGGxC@-M`}dx|PXXCGxS@-M`W`7x`| Q@@CGxS@-M`1`d|TmAACGxC@-M`Tdx|rXXOGxS@-M`W`8x`|r@@LGxS@-M``d|sAALGxC@-M`dx|{XXLGxS@-M`W`8x`|,{@@OGxS@-M`J`d|U|AAOGxC@-M`Udx|XXCGxS@-M`W`8 x`|@@CGxS@-M``d|AACGxC@-M`dx|pXXGGxS@-M`pW`80x`|@@@GxS@-M``d|ݍAA@GxC@-M`dx|XX@>zS@-M`W`8@x`|@@@>zS@-zC@->M` `d|0AA@>zC@-M`0dx|XX>zS@-M`W`8Px`|@@@>zS@-zC@->M`9`d|2AA@>zC@-M`2dx|XX@>zS@-M`W`8`x`|@@>zS@-zC@->M``d|AA>zC@-M`dx|AXX@>zS@-M`AW`8p x`|S@@@>zS@-zC@->M``d|AA@>zC@-M`dx|XX>zS@-M`W`8x`|ɶ@@>zS@-zC@->M``d|AA>zC@-M`dx|XX>zS@-M`W`8*x`|@@>zS@-zC@->M``d|AA>zC@-M`dx|(XX>zS@-M`(W`8:x`|;@@>zS@-zC@->M`d`d|AA>zC@-M`dx|XX>zS@-M`W`8Jx`|@@>zS@-zC@->M`!`d|5AA>zC@-M`5dx|XXOGxS@-M`W`8Zx`|@@LGxS@-M``d|AALGxC@-M`dx|^XXzS@-M`^$W`8jx`|}@@zS@-M`$`d|AAzC@-M`%dx|)XX@zS@-M`)-W`8zx`|<@@zS@-M`-`d|AAzC@-M`.dx|XX@zS@-M`5W`8x`|@@zS@-M`6`d|7AAzC@-M`77dx| XXzS@-M`>W`9x`| @@zS@-``| @@zC@->M`>`d| AAzC@-M`?dx|FXXzS@-M`FGW`9x`|Y@@zS@-M`G`d|AAzC@-M`Hdx|XX@zS@-M`OW`9 x`|@@zS@-M`.P`d|,AAzC@-M`,Qdx|}$XXzS@-M`}XW`90x`|$@@zS@-M`X`d|%AAzC@-M`Ydx|&-XXzS@-M`&aW`9@x`|>-@@zS@-a``|-@@zC@->M`a`d|.AAzC@-M`bdx|5XXzS@-M`iW`9Px`|5@@zS@-M`j`d| 7AAzC@-M` kdx|>XX@zS@-M`rW`9`x`|>@@zS@-@@@zC@->M`r`d|?AAzC@-M`sdx|TGXXzS@-M`T{W`9p x`|hG@@zS@-M`{`d|HAAzC@-M`|dx|2PXXzS@-M`2W`9x`|IP@@zS@-M`d`d|QAAzC@-M`dx|MfXX@zS@-M`MW`9+x`|]f@@zS@-M``d|gAAzC@-M`dx|oXX@zS@-M`W`9;x`|&o@@zS@-M`u`d|epAAzC@-M`edx|wXXzS@-M`W`9Kx`|w@@zS@-M`%`d|yAAzC@-M`dx|XXX@zS@-M`XW`9[x`|g@@zS@-M``d|AAzC@-M`dx|XX@zS@-M`W`9kx`|+@@zS@-M`r`d|AAzC@-M`dx|XXzS@-M`W`9{x`|@@zS@-M``d|'AAzC@-M`'dx|kXXzS@-M`kW`9x`|y@@zS@-M``d|AAzC@-M`dx|XX@zS@-M`W`:x`|,@@zS@-M`n`d|hAAzC@-M`hdx|۫XXzS@-M`W`:x`|@@zS@-M`3`d|'AAzC@-M`'dx|XXzS@-M`W`: x`|@@zS@-M``d|AAzC@-M`dx|[XXzS@-M`[W`:0x`|h@@zS@-M``d|AAzC@-M`dx| XX@zS@-M` W`:@x`|!@@zS@-M`d`d|[AAzC@-M`[dx|XXzS@-M`W`:Px`|@@zS@-M`$`d|0AAzC@-M`0dx|XXzS@-M` W`:`x`|@@zS@-M` `d|AAzC@-M` dx|!XX@zS@-M`!W`:p x`|5@@zS@-M`z`d|AAzC@-M`dx|XX@zS@-M`W`:x`|@@zS@-M`$`d|(AAzC@-M`(dx|8XXzS@-M`8%W`:,x`|G@@zS@-M`%`d|AAzC@-M`&dx|XX@>zS@-M`-W`:<x`|@@@>zS@-zC@->M`/.`d|*AA@>zC@-M`*/dx|QXX>zS@-M`Q6W`:Lx`|a@@@>zS@-zC@->M`6`d|AA@>zC@-M`7d`| @@@>zS@-`x|XX>zS@-M`CW`:\x`|^@@>zC@->M`^C`d|gAA@>zC@-M`gDdx|XXqDwS@-M`KW`:lx`|@@@~DwS@-M`K`d|AA@~DwC@-M`Ldx|XXvDwS@-M`SW`:|x`|@@@xDwS@-M`T`d|0!AA@xDwC@-M`0Udx|(XX@xDwS@-M`\W`:x`|(@@vDwS@-M`\`d|)AAvDwC@-M`]dx|g1XX@~DwS@-M`geW`;x`|u1@@qDwS@-M`e`d|2AAqDwC@-M`fdx|:XX@{DwS@-M`nW`;x`|$:@@yDwS@-M`Fn`d|;AAyDwC@-M`odx|BXXDwS@-M`vW`; x`|B@@yDwS@-M`v`d|CAAyDwC@-M`wdx|KXX}DwS@-M`W`;0x`|-K@@tDwS@-M`K`d|lLAAtDwC@-M`ldx|SXXwDwS@-M`ЇW`;@x`|S@@vDwS@-M``d|UAAvDwC@-M`dx|\XXvDwS@-M`W`;Px`|\@@wDwS@-M`ϐ`d|]AAwDwC@-M`dx|]eXXtDwS@-M`]W`;`x`|ne@@}DwS@-M``d|fAA}DwC@-M`Údx|$nXXyDwS@-M`$W`;p x`|8n@@DwS@-M`V`d|oAADwC@-M`dx|vXX@zS@-M`W`;x`|v@@zS@-M`Ҫ`d|wAAzC@-M`̫dx|PXX@zS@-M`PW`;-x`|^@@zS@-M``d|AAzC@-M`dx|XXyDwS@-M`W`;=x`|@@@{DwS@-M`9`d|#AA@{DwC@-M`#dx|XXqDwS@-M`W`;Mx`|@@@~DwS@-M``d|AA@~DwC@-M`dx|fXXvDwS@-M`fW`;]x`|t@@@xDwS@-M``d|ߨAA@xDwC@-M`dx|1XX@xDwS@-M`1W`;mx`|A@@vDwS@-M``d|AAvDwC@-M`dx|XX@~DwS@-M`W`;}x`|@@qDwS@-M`8`d|CAAqDwC@-M`Cdx|XX@{DwS@-M`W`;x`|@@yDwS@-M``d|AAyDwC@-M`dx|rXXDwS@-M`rW`<x`|@@yDwS@-M``d|AAyDwC@-M`dx|;XX}DwS@-M`;W`<x`|I@@tDwS@-M``d|AAtDwC@-M`dx|XXwDwS@-M`W`< x`|@@vDwS@-M``d|4AAvDwC@-M`4dx|XXvDwS@-M`W`<0x`|@@wDwS@-M``d|AAwDwC@-M`dx|FXXtDwS@-M`F!W`<@x`|S@@}DwS@-M`!`d|AA}DwC@-M`"dx|XXyDwS@-M`*W`M`E*`d|yAADwC@-M`y+dx|XXyDwS@-M`2W`<`x`|@@@{DwS@-M`2`d|*AA@{DwC@-M`*4dx|XXqDwS@-M`;W`M`;`d|AA@~DwC@-M`<dx|_XXvDwS@-M`_DW`<x`|l@@@xDwS@-M`D`d|AA@xDwC@-M`Edx| XX@xDwS@-M` MW`<.x`|@@vDwS@-M`CM`d|pAAvDwC@-M`pNdx|!XX@~DwS@-M`UW`<>x`|!@@qDwS@-M`V`d|2#AAqDwC@-M`2Wdx|W*XX@{DwS@-M`W^W`<Nx`|k*@@yDwS@-M`^`d|+AAyDwC@-M`_dx|3XXDwS@-M`gW`<^x`|&3@@yDwS@-M`Jg`d|4AAyDwC@-M`hdx|;XX}DwS@-M`oW`<nx`|<@@tDwS@-M`/p`d|F=AAtDwC@-M`Fqdx|DXXwDwS@-M`xW`<~x`|D@@vDwS@-M`x`d|EAAvDwC@-M`ydx|XXXvDwS@-M`W`<x`|X@@wDwS@-M`݌`d|YAAwDwC@-M`dx|WaXXtDwS@-M`WW`=x`|ca@@}DwS@-M``d|bAA}DwC@-M`dx|jXXyDwS@-M`W`=x`|$j@@DwS@-M`V`d|zkAADwC@-M`zdx|rXXyDwS@-M`ڦW`= x`|r@@@{DwS@-M``d|OtAA@{DwC@-M`Odx|{XXqDwS@-M`W`=0x`|{@@@~DwS@-M`ӯ`d||AA@~DwC@-M`dx|OXXvDwS@-M`OW`=@x`|]@@@xDwS@-M``d|AA@xDwC@-M`dx| XX@xDwS@-M` W`=Px`|@@vDwS@-M`B`d|uAAvDwC@-M`udx|ӕXX@~DwS@-M`W`=`x`|@@qDwS@-M` `d|XAAqDwC@-M`Xdx|XX@{DwS@-M`W`=px`|@@yDwS@-M``d|AAyDwC@-M`dx|XXDwS@-M`W`=x`|"@@yDwS@-M`L`d|CAAyDwC@-M`Cdx|ٯXX}DwS@-M`W`=/x`|@@tDwS@-M``d|3AAtDwC@-M`3dx|XXwDwS@-M`W`=?x`|@@vDwS@-M``d|AAvDwC@-M`dx|UXXvDwS@-M`UW`=Ox`|g@@wDwS@-M``d|AAwDwC@-M`dx|0XX5zS@-M`0 W`=_x`|<@@5zS@-M`^ `d|AA5zC@-M` dx|XX@5zS@-M`W`=ox`|@@5zS@-M``d|9AA5zC@-M`9dx|XX5zS@-M`W`=x`|@@5zS@-M``d|AA5zC@-M`dx|ZXX@5zS@-M`Z&W`=x`|f@@@5zS@-M`&`d|AA@5zC@-M`'dx|!XX5zS@-M`!/W`>x`|2@@5zS@-M`]/`d|AA5zC@-M`0dx| XX5zS@-M`7W`>x`| @@5zS@-M`8`d|. AA5zC@-M`.9dx| XX5zS@-M`@W`> x`| @@5zS@-M`@`d| AA5zC@-M`Bdx|M XX5zS@-M`MIW`>0x`|Y @@5zS@-M`I`d| AA5zC@-M`Jdx|  XX5zS@-M` RW`>@x`| @@@5zS@-M`DR`d|y AA@5zC@-M`ySdx|& XX@5zS@-M`ZW`>Px`|& @@5zS@-M`[`d|+( AA5zC@-M`+\dx|Q/ XX5zS@-M`QcW`>`x`|]/ @@5zS@-M`c`d|0 AA5zC@-M`ddx|8 XX@5zS@-M`lW`>px`|8 @@5zS@-M`0l`d|l9 AA5zC@-M`lmdx|@ XX5zS@-M`tW`> x`|@ @@5zS@-M`t`d| B AA5zC@-M` vdx|}I XX@5zS@-M`}}W`>0x`|I @@@5zS@-M`}`d|J AA@5zC@-M`~d`|#R @@5zS@-@x`|V @@5zC@->M``d|W AA5zC@-M`dx|^ XX5zS@-M`W`>Px`|^ @@5zS@-M``d|` AA5zC@-M`dx|fg XX5zS@-M`fW`>`x`|sg @@5zS@-M``d|h AA5zC@-M`dx|'p XX5zS@-M`'W`>px`|4p @@5zS@-M`^`d|q AA5zC@-M`dx|x XX5zS@-M`W`>x`|x @@@5zS@-M``d|/z AA@5zC@-M`/dx| XX@5zS@-M`W`>x`| @@5zS@-M``d| AA5zC@-M`dx|3 XX5zS@-M`3W`?x`|@ @@@5zS@-M`j`d|H AA@5zC@-M`Hdx| XX@5zS@-M`W`?x`| @@5zS@-M``d| AA5zC@-M`dx|W XX5zS@-M`WW`? x`|e @@@5zS@-M``d| AA@5zC@-M`dx| XX@5zS@-M`W`?0x`|! @@5zS@-M`K`d| AA5zC@-M`dx| XX5zS@-M`W`?@x`| @@@5zS@-M``d|F AA@5zC@-M`Fdx|q XX@5zS@-M`qW`?Px`| @@5zS@-M``d| AA5zC@-M`dx|' XX5zS@-M`'W`?`x`|3 @@@5zS@-M`]`d|\ AA@5zC@-M`\dx| XX@5zS@-M`W`?px`| @@5zS@-M``d| AA5zC@-M` dx|~ XX5zS@-M`~ W`?!x`| @@@5zS@-M` `d| AA@5zC@-M` dx|@ XX@5zS@-M`@ W`?1x`|O @@5zS@-M`y `d| AA5zC@-M` dx| XX5zS@-M` W`?Ax`| @@@5zS@- @@5zC@->M`> `d|N AA@5zC@-M`N dx| XX@5zS@-M` W`?Qx`| @@5zS@-M` `d| AA5zC@-M` dx| XX5zS@-M`$ W`?ax`| @@@5zS@-M`?% `d|3 AA@5zC@-M`3& dx|^ XX@5zS@-M`^- W`?qx`|l @@5zS@-M`- `d| AA5zC@-M`. dx| XX5zS@-M`5 W`?x`| @@@5zS@-M`6 `d|* AA@5zC@-M`*7 dx| XX@5zS@-M`> W`?x`|% @@5zS@- ``|v @@@5zC@->M`v> `d|& AA5zC@-M`Z dx|* XX5zS@-M`^ W`@x`| + @@@5zS@-M`L_ `d|, AA@5zC@-M`` dx|3 XX@>zS@-M`g W`@x`|3 @@@>zS@-zC@->M`^h `d|g5 AA@>zC@-M`gi dx|!< XX>zS@-M`!p W`@ x`|0< @@>zS@-zC@->M`Op `d|o= AA>zC@-M`oq dx|D XXtDwS@-M`x W`@0x`|D @@}DwS@-M`y `d|KF AA}DwC@-M`Kz dx|rM XXyDwS@-M`r W`@@x`|M @@DwS@-M` `d|N AADwC@-M` dx|/V XXyDwS@-M`/ W`@Px`|>V @@@{DwS@- ``|gV @@yDwC@->M`g `d|W AA@{DwC@-M` dx|^ XXqDwS@-M` W`@`x`|_ @@@~DwS@-M`' `d|Z` AA@~DwC@-M`Z dx|{g XXvDwS@-M`{ W`@px`|g @@@xDwS@-M` `d|h AA@xDwC@-M`ۜ dx|=p XX@xDwS@-M`= W`@"x`|Lp @@vDwS@-M`t `d|q AAvDwC@-M` dx| XX@~DwS@-M` W`@2x`| @@qDwS@-M`ݻ `d| AAqDwC@-M` dx|e XX@{DwS@-M`e W`@Bx`|s @@yDwS@-M` `d|ʑ AAyDwC@-M` dx|% XXDwS@-M`% W`@Rx`|2 @@yDwS@-M`\ `d| AAyDwC@-M` dx| XX}DwS@-M` W`@bx`| @@tDwS@-M` `d|* AAtDwC@-M`* dx|P XXwDwS@-M`P W`@rx`|] @@vDwS@-M` `d| AAvDwC@-M` dx| XXvDwS@-M` W`@x`| @@wDwS@-M` `d| AAwDwC@-M` dx|ϻ XXtDwS@-M` W`@x`|ܻ @@}DwS@-M` `d|! AA}DwC@-M`! d`| @@yDwS@-M`! `d|; AAyDwC@-M`; dx| XXyDwS@-M` W`Ax`| @@@{DwS@-M` `d| AA@{DwC@-M` dx|e XXqDwS@-M`e W`A x`|r @@@~DwS@-M` `d| AA@~DwC@-M` dx|/ XXvDwS@-M`/ W`A0x`|< @@@xDwS@-M`g `d| AA@xDwC@-M` dx| XX@xDwS@-M` W`A@x`| @@vDwS@-M`C `d|] AAvDwC@-M`]! dx|y XX@~DwS@-M`y( W`APx`| @@qDwS@-M`( `d| AAqDwC@-M`) dx|3 XX@{DwS@-M`31 W`A`x`|? @@yDwS@-M`h1 `d| AAyDwC@-M`2 dx| XXyDwS@-M`9 W`Apx`| @@DwS@-M`7: `d|T AADwC@-M`T; dx| XX}DwS@-M`B W`A#x`| @@tDwS@-M`C `d|R AAtDwC@-M`RD dx| XXwDwS@-M`K W`A3x`| @@vDwS@-M`K `d| AAvDwC@-M`L dx|E XXvDwS@-M`ET W`ACx`|T @@wDwS@-M`zT `d|! AAwDwC@-M`U dx|) XXtDwS@-M`] W`ASx`| ) @@}DwS@-M`5] `d|H* AA}DwC@-M`H^ dx|1 XXDwS@-M`e W`Acx`|1 @@yDwS@-M`e `d|3 AAyDwC@-M`g dx|t: XXyDwS@-M`tn W`Asx`|: @@@{DwS@-M`n `d|; AA@{DwC@-M`o dx|'C XXqDwS@-M`'w W`Ax`|3C @@@~DwS@-M`\w `d|pD AA@~DwC@-M`px dx|K XXvDwS@-M` W`Ax`|K @@@xDwS@-M` `d|@M AA@xDwC@-M`@ dx|T XX@xDwS@-M` W`Bx`|T @@vDwS@-M` `d|'V AAvDwC@-M`' dx|c] XX@~DwS@-M`c W`Bx`|r] @@qDwS@-M` `d|^ AAqDwC@-M` dx|e XX@{DwS@-M`֙ W`B x`|e @@yDwS@-M`, `d|2g AAyDwC@-M`2 dx|xn XXyDwS@-M`x W`B0x`|n @@DwS@-M` `d|o AADwC@-M`ˣ dx|)w XX}DwS@-M`) W`B@x`|8w @@tDwS@-M`^ `d|x AAtDwC@-M` dx| XXwDwS@-M` W`BPx`| @@vDwS@-M`' `d|k AAvDwC@-M`k dx| XXvDwS@-M` W`B`x`| @@wDwS@-M` `d| AAwDwC@-M` dx|^ XXtDwS@-M`^ W`Bpx`|j @@}DwS@-M` `d|Ò AA}DwC@-M` d`| @@>zS@-zS@-M`6 W`B$x`|p @@>zC@->M`p `d| AA>zC@-M` dx| XX>zS@-M` W`B4x`|Ц @@>zS@-zC@->M` `d| AA>zC@-M` dx|O XX>zS@-M`O W`BDx`|a @@>zS@-zC@->M` `d| AA>zC@-M` dx|' XX@>zS@-M`' W`BTx`|A @@@>zS@-zC@->M`g `d|L AA@>zC@-M`L dx| XX@>zS@-M` W`Bdx`| @@>zS@-zC@->M` `d| AA>zC@-M` dx|H XXDwS@-M`H W`Btx`|^ @@yDwS@-M`~ `d| AAyDwC@-M` dx| XX>zS@-M` W`Bx`| @@@>zS@-zC@->M` `d|K AA@>zC@-M`K dx| XX@>zS@-M` W`Bx`| @@@>zS@-zC@->M` `d|! AA@>zC@-M`! dx|Q XX>zS@-M`Q W`Cx`|d @@>zS@-zC@->M` `d| AA>zC@-M` dx| XX>zS@-M` W`Cx`| @@>zS@-zC@->M`. `d|f AA>zC@-M`f dx| XX>zS@-M`5 W`C x`|- @@>zS@-zC@->M`H5 `d| AA>zC@-M`6 dx| XX>zS@-M`= W`C0x`| @@>zS@-zC@->M`= `d| AA>zC@-M`> dx| XX@>zS@-M`F W`C@x`|! @@@>zS@-zC@->M`AF `d|b AA@>zC@-M`bG dx| XX@>zS@-M`N W`CPx`| @@>zS@-zC@->M`N `d| AA>zC@-M`P dx|c# XX>zS@-M`cW W`C`x`|# @@@>zS@-zC@->M`W `d|$ AA@>zC@-M`X dx|6, XX@>zS@-M`6` W`Cpx`|M, @@@>zS@-zC@->M`e` `d|- AA@>zC@-M`a dx|4 XX>zS@-M`h W`C%x`|4 @@>zS@-zC@->M`i `d|/6 AA>zC@-M`/j dx|m= XX>zS@-M`mq W`C5x`|= @@>zS@-zC@->M`q `d|> AA>zC@-M`r dx|E XX>zS@-M`y W`CEx`|F @@>zS@-zC@->M`1z `d|VG AA>zC@-M`V{ dx|lN XX>zS@-M`l W`CUx`|N @@>zS@-zC@->M` `d|O AA>zC@-M`ڃ dx|W XX@>zS@-M` W`Cex`|'W @@@>zS@-zC@->M`M `d|X AA@>zC@-M` dx|_ XX@>zS@-M` W`Cux`|_ @@>zS@-zC@->M`E `d|Ga AA>zC@-M`G dx|h XX>zS@-M` W`Cx`|h @@@>zS@-zC@->M` `d|j AA@>zC@-M` dx|mq XX@>zS@-M`m W`Cx`|zq @@@>zS@-zC@->M`Х `d|r AA@>zC@-M` dx|Iz XX>zS@-M`I W`Dx`|Zz @@>zS@-zC@->M` `d|{ AA>zC@-M` dx| XX>zS@-M` W`Dx`| @@>zS@-zC@->M`- `d|g AA>zC@-M`g dx| XX>zS@-M` W`D x`| @@>zS@-zC@->M`ȿ `d| AA>zC@-M` dx|l XX>zS@-M`l W`D0x`|| @@>zS@-zC@->M` `d|ƕ AA>zC@-M` dx|! XX@>zS@-M`! W`D@x`|/ @@@>zS@-zC@->M`X `d| AA@>zC@-M` dx|ϥ XX@>zS@-M` W`DPx`|ܥ @@>zS@-zC@->M`7 `d|/ AA>zC@-M`/ dx| XX>zS@-M` W`D`x`| @@@>zS@-zC@->M` `d|; AA@>zC@-M`; dx|/ XX@>zS@-M`/ W`Dpx`|< @@@>zS@-zC@->M` `d| AA@>zC@-M` dx| XX>zS@-M` W`D&x`| @@>zS@-zC@->M`U `d|^ AA>zC@-M`^ dx| XX>zS@-M` W`D6x`| @@>zS@-zC@->M` `d| AA>zC@-M` dx|p XX>zS@-M`p W`DFx`|~ @@>zS@-zC@->M` `d| AA>zC@-M` dx|' XX>zS@-M`' W`DVx`|3 @@>zS@-zC@->M`T `d| AA>zC@-M` dx| XX@>zS@-M`# W`Dfx`| @@@>zS@-zC@->M`I$ `d|3 AA@>zC@-M`3% dx| XX@>zS@-M`, W`Dvx`| @@>zS@-zC@->M`, `d| AA>zC@-M`- dx|^ XX>zS@-M`^5 W`Dx`|m @@@>zS@-zC@->M`5 `d| AA@>zC@-M`6 dx| XX@>zS@-M`= W`Dx`| @@@>zS@-zC@->M`> `d|O AA@>zC@-M`O? dx|f XX>zS@-M`fF W`Ex`|z @@>zS@-zC@->M`F `d| AA>zC@-M`G dx| XX>zS@-M`N W`Ex`|  @@>zS@-zC@->M`"O `d|N AA>zC@-M`NP dx|# XX>zS@-M`W W`E x`|# @@>zS@-zC@->M`W `d|$ AA>zC@-M`X dx|9 XX@5zS@-M`m W`E0x`|9 @@5zS@-M`m `d|: AA5zC@-M`n dx|QB XX5zS@-M`Qv W`E@x`|aB @@@5zS@-M`v `d|C AA@5zC@-M`w dx|J XX@5zS@-M`~ W`EPx`|J @@5zS@-M` `d|>L AA5zC@-M`> dx|S XX>zS@-M` W`E`x`|S @@>zS@-zC@->M`ۇ `d|T AA>zC@-M` dx|E\ XX@>zS@-M`E W`Epx`|X\ @@@>zS@-zC@->M`u `d|] AA@>zC@-M` dx|d XX@>zS@-M`Ę W`E'x`|d @@>zS@-zC@->M` `d|$f AA>zC@-M`$ dx|l XX>zS@-M`ˠ W`E7x`|l @@@>zS@-zC@->M` `d|&n AA@>zC@-M`& dx|Uu XX@>zS@-M`U W`EGx`|eu @@@>zS@-zC@->M` `d|v AA@>zC@-M`Ϫ dx|} XX>zS@-M` W`EWx`|} @@>zS@-zC@->M` `d|= AA>zC@-M`= dx| XX>zS@-M` W`Egx`| @@>zS@-zC@->M` `d| AA>zC@-M` dx|i XXyDwS@-M`i W`Ewx`|| @@@{DwS@-M` `d| AA@{DwC@-M` dx|5 XXqDwS@-M`5 W`Ex`|D @@@~DwS@-M` `d| AA@~DwC@-M` dx|ޠ XXvDwS@-M` W`Ex`| @@@xDwS@-M`6 `d|D AA@xDwC@-M`D dx| XX@xDwS@-M` W`Fx`| @@vDwS@-M`F `d|P AAvDwC@-M`P dx| XX@~DwS@-M` W`Fx`| @@qDwS@-M` `d| AAqDwC@-M` dx|b XX@{DwS@-M`b W`F x`|t @@yDwS@-M` `d| AAyDwC@-M` dx| XXyDwS@-M` W`F0x`|& @@DwS@-M`g `d| AADwC@-M` dx| XX}DwS@-M` W`F@x`| @@tDwS@-M`) `d|? AAtDwC@-M`? dx|a XXwDwS@-M`a W`FPx`|p @@vDwS@-M` `d| AAvDwC@-M` d`| @@>zS@-zS@-M`i# W`F`x`| @@>zC@->M`# `d| AA>zC@-M`$ dx| XX>zS@-M`+ W`Fpx`| @@>zS@-zC@->M`+ `d| AA>zC@-M`, dx|}XX@>zS@-M`}4 W`F(x`|@@@>zS@-zC@->M`4 `d|AA@>zC@-M`6 dx|9 XX@>zS@-M`9= W`F8x`|H @@>zS@-zC@->M`s= `d| AA>zC@-M`> dx|XXvDwS@-M`E W`FHx`|@@wDwS@-M`XF `d|AAwDwC@-M`G dx|XXtDwS@-M`N W`FXx`|@@}DwS@-M`CO `d|-AA}DwC@-M`-P dx|Z#XXDwS@-M`ZW W`Fhx`|m#@@yDwS@-M`W `d|$AAyDwC@-M`X dx|&,XXyDwS@-M`&` W`Fxx`|B,@@@{DwS@-M`_` `d|d-AA@{DwC@-M`da dx|4XX>zS@-M`h W`Fx`|4@@@>zS@-zC@->M`h `d|5AA@>zC@-M`i dx|{=XX@>zS@-M`{q W`Fx`|=@@@>zS@-zC@->M`q `d|>AA@>zC@-M`r dx|FXX>zS@-M`z W`Gx`|3F@@>zS@-zC@->M`Qz `d|GAA>zC@-M`{ dx|NXX>zS@-M` W`Gx`|O@@>zS@-zC@->M` `d|FPAA>zC@-M`F dx|cWXX>zS@-M`c W`G x`|vW@@>zS@-zC@->M` `d|XAA>zC@-M`Č dx|`XX>zS@-M` W`G0x`|`@@>zS@-zC@->M`H `d|oaAA>zC@-M`o dx|hXX@>zS@-M` W`G@x`|h@@@>zS@-zC@->M` `d|jAA@>zC@-M` dx|rqXX@>zS@-M`r W`GPx`|q@@>zS@-zC@->M` `d|rAA>zC@-M` dx|zXX>zS@-M` W`G` x`|z@@@>zS@-zC@->M`n `d|a{AA@>zC@-M`a dx|ǂXX@>zS@-M`Ƕ W`Gpx`|Ղ@@@>zS@-zC@->M` `d|.AA@>zC@-M`. dx|XX>zS@-M` W`G)x`|@@>zS@-zC@->M`ǿ `d|AA>zC@-M` dx|)XX>zS@-M`) W`G9x`|8@@>zS@-zC@->M`a `d|jAA>zC@-M`j dx|ʜXX>zS@-M` W`GIx`|@@>zS@-zC@->M` `d|>AA>zC@-M`> dx|XX>zS@-M` W`GYx`|@@>zS@-zC@->M` `d|AA>zC@-M` dx|XX@>zS@-M` W`Gix`|@@@>zS@-zC@->M` `d|ٯAA@>zC@-M` dx|bXX@>zS@-M`b W`Gyx`|o@@>zS@-zC@->M` `d|AA>zC@-M` dx|(XX>zS@-M`( W`Gx`|5@@@>zS@-zC@->M` `d|AA@>zC@-M` dx|XX@>zS@-M` W`Gx`|@@@>zS@-zC@->M` `d|CAA@>zC@-M`Cdx|{XX>zS@-M`{W`Hx`|@@>zS@-zC@->M``d|AA>zC@-M` dx|@XX>zS@-M`@'W`Hx`|N@@>zS@-zC@->M`|'`d|AA>zC@-M`(dx|XX>zS@-M`/W`H x`|@@>zS@-zC@->M`,0`d|0AA>zC@-M`01dx|XX>zS@-M`8W`H0x`|@@>zS@-zC@->M`8`d|AA>zC@-M`9dx|M XX@>zS@-M`MAW`H@x`|[ @@@>zS@-zC@->M`A`d|AA@>zC@-M`Bdx|XX@>zS@-M`IW`HPx`|@@>zS@-zC@->M`#J`d|SAA>zC@-M`SKdx|XX>zS@-M`RW`H` x`|@@@>zS@-zC@->M``R`d|XAA@>zC@-M`XSdx|&XX@>zS@-M`ZW`Hpx`|&@@@>zS@-zC@->M` [`d|%(AA@>zC@-M`%\dx|K/XX>zS@-M`KcW`H*x`|V/@@>zS@-zC@->M`c`d|0AA>zC@-M`ddx|7XX>zS@-M`kW`H:x`|7@@>zS@-zC@->M`Kl`d|09AA>zC@-M`0mdx|@XX>zS@-M`tW`HJx`|@@@>zS@-zC@->M`u`d|BAA>zC@-M`vdx|hIXX>zS@-M`h}W`HZx`|sI@@>zS@-zC@->M`}`d|JAA>zC@-M`~dx|)RXX@>zS@-M`)W`Hjx`|4R@@@>zS@-zC@->M``d|SAA@>zC@-M`dx|ZXXqDwS@-M`W`Hzx`|Z@@@~DwS@-M`8`d|J\AA@~DwC@-M`Jdx|cXXvDwS@-M`W`Hx`|c@@@xDwS@-M``d|+eAA@xDwC@-M`+dx|ulXX@xDwS@-M`uW`Hx`|l@@vDwS@-M`ʠ`d|mAAvDwC@-M`ѡdx|.uXX@~DwS@-M`.W`Ix`|M``d|wAAqDwC@-M`dx|}XX@{DwS@-M`W`Ix`|}@@yDwS@-M`@`d|2AAyDwC@-M`2dx|XXyDwS@-M`W`I x`|@@DwS@-M``d|AADwC@-M`dx|aXX}DwS@-M`aW`I0x`|p@@tDwS@-M``d|ՐAAtDwC@-M`dx|XXwDwS@-M`W`I@x`|@@vDwS@-M`1`d|LAAvDwC@-M`Ldx|XXvDwS@-M`W`IPx`|@@wDwS@-M``d|AAwDwC@-M`dx|QXXtDwS@-M`QW`I` x`|^@@}DwS@-M``d|AA}DwC@-M`dx| XXDwS@-M` W`Ipx`|@@yDwS@-M`j`d|gAAyDwC@-M`gdx|ƺXXyDwS@-M`W`I+x`|׺@@@{DwS@-M``d|#AA@{DwC@-M`#dx|}XXqDwS@-M`}W`I;x`|@@@~DwS@-M``d|AA@~DwC@-M`dx|4XXvDwS@-M`4W`IKx`|?@@@xDwS@-M`j`d|AA@xDwC@-M`dx|XX@xDwS@-M`W`I[x`|@@vDwS@-M`- `d|RAAvDwC@-M`R dx|bXX@~DwS@-M`bW`Ikx`|p@@qDwS@-M` `d|AAqDwC@-M`dx|XX@{DwS@-M`W`I{x`|@@yDwS@-M``d|2AAyDwC@-M`2dx|XXyDwS@-M`"W`Ix`|@@DwS@-M`"`d|AADwC@-M`$dx|RXX}DwS@-M`R+W`Ix`|\@@tDwS@-M`+`d|AAtDwC@-M`,dx|XXwDwS@-M`4W`Jx`|!@@vDwS@-M`L4`d|AAvDwC@-M`5dx|XX@>zS@-M`<W`Jx`|@@>zS@-zC@->M`<`d| AA>zC@-M`>dx|YXX>zS@-M`W`J x`|o@@@>zS@-zC@->M`F`d|AA@>zC@-M`fdx|*'XX@>zS@-M`W`J0x`|@'@@@>zS@-zC@->M``d|(AA@>zC@-M`jd`|/@@>zS@-zS@-M`%W`J@x`|4@@>zC@->M`%`d|4AA>zC@-M`&dx|t<XX>zS@-M`4.W`JPx`|<@@>zS@-zC@->M`.`d|=AA>zC@-M`/dx|XEXX>zS@-M`7W`J` x`|eE@@>zS@-zC@->M`r7`d|FAA>zC@-M`l8dx|MXX>zS@-M`?W`Jpx`|M@@>zS@-zC@->M`?`d|=OAA>zC@-M`@dx|VXX@>zS@-M`^HW`J,x`|V@@@>zS@-zC@->M`H`d| XAA@>zC@-M`Idx|'_XXDwS@-M`PW`J<x`|8_@@yDwS@-M`Q`d|`AAyDwC@-M`ERdx|gXXyDwS@-M`pYW`JLx`|g@@@{DwS@-M`Y`d|hAA@{DwC@-M`Zdx|upXXqDwS@-M`5bW`J\x`|p@@@~DwS@-M`b`d|qAA@~DwC@-M`|cdx|6yXXvDwS@-M`jW`Jlx`|Fy@@@xDwS@-M`Rk`d|zAA@xDwC@-M`~ldx|XX@>zS@-M`sW`J|x`|@@>zS@-zC@->M`&t`d|\AA>zC@-M`udx|ÊXX>zS@-M`|W`Jx`|ӊ@@@>zS@-zC@->M`|`d|AA@>zC@-M`}dx|XX@>zS@-M`FW`Jx`|@@@>zS@-zC@->M``d|AA@>zC@-M`dx|XX>zS@-M`֍W`Kx`|%@@>zS@-zC@->M`0`d|AA>zC@-M`Sdx|ؤXX>zS@-M`W`Kx`|@@>zS@-zC@->M``d|.AA>zC@-M`dx|LXX>zS@-M` W`K x`|_@@>zS@-zC@->M`;`d|AA>zC@-M`_dx|XX>zS@-M`W`K0x`|@@>zS@-zC@->M` `d|ZAA>zC@-M`dx|XX>zS@-M`W`K@x`|@@>zS@-zC@->M``d|AA>zC@-M`Ndx|XX>zS@-M`yW`KPx`|@@>zS@-zC@->M`Ϲ`d|AA>zC@-M`dx|CXX>zS@-M`W`K` x`|S@@>zS@-zC@->M`4`d|AA>zC@-M`Tdx|XX>zS@-M`W`Kpx`|@@>zS@-zC@->M``d|AA>zC@-M`dx|XX>zS@-M`VW`K-x`|@@>zS@-zC@->M``d|AA>zC@-M`dx|YXX>zS@-M`W`K=x`|i@@>zS@-zC@->M`H`d|AA>zC@-M`dx|XX>zS@-M`W`KMx`|@@>zS@-zC@->M``d|KAA>zC@-M` dx|XX>zS@-M`W`K]x`|@@>zS@-zC@->M``d| AA>zC@-M`dx|XX>zS@-M`GW`Kmx`|@@>zS@-zC@->M`v`d|AA>zC@-M`dx|LXX>zS@-M` W`K}x`|\@@>zS@-zC@->M`; `d|AA>zC@-M` dx|!XX5zS@-M`W`Kx`|!@@@5zS@-M``d|"AA@5zC@-M`dx|o*XX@5zS@-M`/W`Kx`|}*@@5zS@-M`\`d|+AA5zC@-M`\dx|2XX5zS@-M`$W`Lx`|2@@@5zS@-M`$`d|4AA@5zC@-M`%dx|n;XX@5zS@-M`.-W`Lx`||;@@5zS@-M`Y-`d|<AA5zC@-M`E.dx| DXX5zS@-M`5W`L x`|"D@@@5zS@-M`6`d|oEAA@5zC@-M`/7dx|iLXX@5zS@-M`)>W`L0x`|zL@@5zS@-``|L@@@5zC@->M`c>`d|MAA5zC@-M`b?dx|TXX5zS@-M`FW`L@x`|T@@@5zS@-M`F`d|UAA@5zC@-M`Gdx|-]XX@5zS@-M`NW`LPx`|>]@@5zS@-M`O`d|\^AA5zC@-M`Pdx|eXX@5zS@-M`MWW`L`x`|e@@5zS@-M`W`d|fAA5zC@-M`lXdx|mXX5zS@-M`_W`Lpx`|n@@5zS@-M`_`d|(oAA5zC@-M``dx|yvXX5zS@-M`9hW`L.x`|v@@5zS@-M`hh`d|wAA5zC@-M`idx| XX5zS@-M`pW`L>x`|@@@5zS@-M`p`d|lAA@5zC@-M`,rdx|XX5zS@-M`byW`LNx`|@@@5zS@-M`y`d|AA@5zC@-M`zdx| XX5zS@-M`W`L^x`|/@@@5zS@-M`2`d|pAA@5zC@-M`0dx|ԥXX}DwS@-M`W`Lnx`|@@wDwS@-M``d|HAAwDwC@-M`dx|XX@xDwS@-M`SW`L~x`|@@vDwS@-M``d|AAvDwC@-M`dx|XX@~DwS@-M`W`Lx`|@@qDwS@-M``d|EAAqDwC@-M`dx|XX@{DwS@-M`?W`Lx`|@@yDwS@-M`y`d|AAyDwC@-M`dx|@XXyDwS@-M`W`Mx`|N@@DwS@-M`8`d|AADwC@-M`Rdx| XXtDwS@-M`W`Mx`|@@vDwS@-M``d|AAvDwC@-M`Kdx|XXvDwS@-M`dW`M x`|@@tDwS@-M``d|AAtDwC@-M`dx|'XXDwS@-M`W`M0x`|6@@yDwS@-M``d|`AAyDwC@-M` dx|XXyDwS@-M`W`M@x`|@@@{DwS@-M``d|;AA@{DwC@-M`dx|XXqDwS@-M`UW`MPx`|@@@~DwS@-M``d| AA@~DwC@-M`dx|]XXvDwS@-M`W`M`x`|n@@@xDwS@-M`[`d|AA@xDwC@-M`}dx|DXX5zS@-M`W`Mpx`|_@@5zS@-M`B`d|AA5zC@-M`mdx| XX5zS@-M`W`M/x`|@@5zS@-M``d|wAA5zC@-M`7dx|XX5zS@-M`zW`M?x`|@@@5zS@-M``d|IAA@5zC@-M` dx|XX5zS@-M`BW`MOx`|@@@5zS@-M`|`d| AA@5zC@-M`dx|'XX5zS@-M`W`M_x`|'@@@5zS@-M``d|.)AA@5zC@-M`dx|0XX5zS@-M`_"W`Mox`|0@@5zS@-M`"`d|1AA5zC@-M`#dx|d9XX5zS@-M`$+W`Mx`|q9@@5zS@-M`]+`d|:AA5zC@-M`s,dx|*BXX5zS@-M`3W`Mx`|7B@@@5zS@-M`"4`d|CAA@5zC@-M`Z5dx|JXX5zS@-M`<W`Mx`|J@@@5zS@-M`<`d|RLAA@5zC@-M`>dx||SXX5zS@-M`M`wE`d|TAA@5zC@-M`Fdx|+\XX5zS@-M`MW`Nx`|7\@@5zS@-M`!N`d|]AA5zC@-M`HOd`|d@@5zS@-M`Z`d|jAA5zC@-M`o\dx|qXX5zS@-M`{cW`N0x`|q@@@5zS@-M`c`d|sAA@5zC@-M`ddx|yXX5zS@-M`kW`N@x`|y@@@5zS@-M`k`d|V{AA@5zC@-M`mdx|XX5zS@-M`tW`NPx`|@@@5zS@-M`t`d|^AA@5zC@-M`vdx|XX5zS@-M`W`N`x`|Θ@@5zS@-M`Ɋ`d|*AA5zC@-M`dx|}XX5zS@-M`=W`Np x`|@@5zS@-M`u`d|AA5zC@-M`dx|XX5zS@-M`śW`N0x`|@@@5zS@-M``d|lAA@5zC@-M`,dx|XX5zS@-M`SW`N@x`|@@@5zS@-M``d|AA@5zC@-M`dx|GXX5zS@-M`W`NPx`|U@@@5zS@-M`5`d|AA@5zC@-M`Fdx|XX5zS@-M`W`N`x`|@@5zS@-M``d|mAA5zC@-M`-dx|XX5zS@-M`IW`Npx`|@@5zS@-M``d|AA5zC@-M`dx|PXX5zS@-M`W`Nx`|^@@@5zS@-M`J`d|AA@5zC@-M`sdx|"XX5zS@-M`W`Nx`|9@@@5zS@-M`>`d|AA@5zC@-M`mdx|XXwDwS@-M`fW`Nx`|@@}DwS@-M``d|AA}DwC@-M`dx|nXX5zS@-M`.W`Ox`||@@@5zS@-M`w`d|AA@5zC@-M`dx|$XX5zS@-M`W`Ox`|2@@5zS@-M`6`d|AA5zC@-M`Bdx|XX5zS@-M`W`O x`| @@5zS@-M``d|AA5zC@-M`dx| XX}DwS@-M`[W`O0x`| @@wDwS@-M``d| AAwDwC@-M`dx|*XX@xDwS@-M`W`O@x`|<@@vDwS@-M`B`d|AAvDwC@-M`Sdx|XX@~DwS@-M` W`OPx`|@@qDwS@-M` `d|XAAqDwC@-M`dx|#XX@{DwS@-M`sW`O`x`|#@@yDwS@-M``d|%AAyDwC@-M`dx|r,XXyDwS@-M`2W`Op!x`|,@@DwS@-M``d|-AADwC@-M`dx|45XXtDwS@-M`&W`O1x`|D5@@vDwS@-M`H'`d|6AAvDwC@-M`A(dx|>XXvDwS@-M`/W`OAx`|.>@@tDwS@-@@vDwC@->M`.0`d|v?AAtDwC@-M`61dx|3GXXDwS@-M`8W`OQx`|BG@@yDwS@-M`D9`d|HAAyDwC@-M`a:dx|OXXyDwS@-M`AW`Oax`|P@@@{DwS@-M`B`d|ZQAA@{DwC@-M`Cdx|XXXqDwS@-M`vJW`Oqx`|X@@@~DwS@-M`J`d|ZAA@~DwC@-M`Kdx|aXXvDwS@-M`RW`Ox`|a@@@xDwS@-M`S`d|gbAA@xDwC@-M`'Tdx|}iXXwDwS@-M`=[W`Ox`|i@@}DwS@-M`[`d|jAA}DwC@-M`\dx|GrXX}DwS@-M`dW`Ox`|Wr@@wDwS@-M`_d`d| AAwDwC@-M`̀dx|QXX@xDwS@-M`W`Px`|c@@vDwS@-M`q`d|AAvDwC@-M`odx| XX@~DwS@-M`ɎW`Px`|@@qDwS@-M`!`d|BAAqDwC@-M`dx|XX@{DwS@-M`RW`P x`|@@yDwS@-M``d| AAyDwC@-M`ɘdx|XXyDwS@-M`ڟW`P0x`|)@@DwS@-M`U`d|AADwC@-M`Vdx|ȶXXtDwS@-M`W`P@x`|ֶ@@vDwS@-M``d|!AAvDwC@-M`dx|XXvDwS@-M`CW`PPx`|@@tDwS@-M``d|AAtDwC@-M`dx|8XXDwS@-M`W`P`x`|F@@yDwS@-M`N`d|AAyDwC@-M`dx|XXyDwS@-M`W`Pp"x`|@@@{DwS@-M``d|_AA@{DwC@-M`dx|XXqDwS@-M`aW`P2x`|@@@~DwS@-M``d|AA@~DwC@-M`dx|*XXvDwS@-M`W`PBx`|?@@@xDwS@-M`>`d|AA@xDwC@-M`Bdx|XXwDwS@-M`W`PRx`|@@}DwS@-M``d|KAA}DwC@-M` dx|XX}DwS@-M`eW`Pbx`|@@wDwS@-M``d|AAwDwC@-M`dx|<XX@xDwS@-M`W`Prx`|O@@vDwS@-M`v`d|AAvDwC@-M`|dx|BXX@~DwS@-M`W`Px`|N@@qDwS@-M`U`d|AAqDwC@-M`[dx| XX@{DwS@-M`W`Px`| @@yDwS@-M``d|-AAyDwC@-M`dx|XXyDwS@-M`DW`Px`|@@DwS@-M``d|AADwC@-M`dx|3XXtDwS@-M`W`Qx`|@@@vDwS@-M`B`d|AAvDwC@-M`~dx|&XXvDwS@-M`W`Qx`|&@@tDwS@-M``d|f(AAtDwC@-M`&dx|{/XXzS@-M`;!W`Q x`|/@@@zS@-M`!`d|0AA@zC@-M`"dx|-8XXzS@-M`)W`Q0x`|;8@@zS@-M`?*`d|~9AAzC@-M`>+dx|@XXzS@-M`2W`Q@x`|@@@zS@-M`2`d|DBAAzC@-M`4dx|xIXXzS@-M`8;W`QPx`|I@@zS@-M`;`d|JAAzC@-M`<dx|QXX@zS@-M`CW`Q`x`|R@@zS@-M`C`d|DSAAzC@-M`Edx|DZXXzS@-M`LW`Qp#x`|SZ@@zS@-M`QL`d|[AAzC@-M`KMdx|bXXzS@-M`TW`Q3x`| c@@zS@-M`U`d|9dAAzC@-M`Udx|]kXXzS@-M`]W`QCx`|ok@@@zS@-M`R]`d|lAA@zC@-M`t^dx|tXX@zS@-M`eW`QSx`|2t@@zS@-M`9f`d|uAAzC@-M`wgdx||XX5zS@-M`nW`Qcx`||@@@5zS@-M`n`d|1~AA@5zC@-M`odx|XX5zS@-M`GwW`Qsx`|@@@5zS@-M`w`d|AA@5zC@-M`xdx|XX5zS@-M`W`Qx`|@@@5zS@-M``d|OAA@5zC@-M`dx|ÖXXzS@-M`W`Qx`|ؖ@@zS@-M`݈`d|AAzC@-M`d`|G@@zS@-M``d| AAzC@-M`˖dx|XX5zS@-M`W`Rx`|@@5zS@-M`֝`d|DAA5zC@-M`dx|JXX5zS@-M` W`Rx`|\@@5zS@-M`[`d|AA5zC@-M`?dx|XX5zS@-M`nW`R x`|@@@5zS@-M``d|AA@5zC@-M`dx|$XX5zS@-M`W`R0x`|>@@@5zS@-M``d|[AA@5zC@-M`dx|xXX5zS@-M`8W`R@x`|@@@5zS@-M`m`d|AA@5zC@-M`dx|XX5zS@-M`W`RPx`|@@5zS@-M``d|MAA5zC@-M` dx|&XX5zS@-M`W`R`x`|9@@5zS@-M`(`d|AA5zC@-M`Fdx|XX5zS@-M`JW`Rp$x`|@@@5zS@-M``d|AA@5zC@-M`dx|/XX5zS@-M`W`R4x`|C@@@5zS@-M`&`d|tAA@5zC@-M`4dx|XX5zS@-M`W`RDx`| @@@5zS@-M``d|AA@5zC@-M`Ndx|XX5zS@-M`W`RTx`|@@5zS@-M``d|!AA5zC@-M`dx| XX@GxS@-M`lW`Rdx`| @@@NGxS@-M``d| AA@NGxC@-M`dx|XXLGxS@-M`IW`Rtx`|@@@KGxS@-M`{`d|AA@KGxC@-M`dx|XXzS@-M`FW`Rx`|@@zS@-M``d|AAzC@-M`dx|%XX@zS@-M`PW`Rx`|%@@zS@-M``d|&AAzC@-M`dx|e.XXzS@-M`% W`Rx`|v.@@zS@-M`Y `d|/AAzC@-M`V!dx|7XXzS@-M`(W`Sx`|*7@@zS@-M`)`d|8AAzC@-M`B*dx|?XXzS@-M`1W`Sx`|?@@@zS@-M`1`d|cAAA@zC@-M`#3dx|HXXzS@-M`j:W`S x`|H@@@zS@-M`:`d| JAA@zC@-M`;dx|QXXzS@-M`@CW`S0x`|Q@@zS@-M`}C`d|RAAzC@-M`Ddx|UZXXzS@-M`LW`S@x`|eZ@@zS@-M`SL`d|[AAzC@-M`cMdx|.cXXzS@-M`TW`SPx`|?c@@zS@-M`-U`d|dAAzC@-M`yVdx|kXX@zS@-M`]W`S`x`|l@@zS@-M`]`d|bmAAzC@-M`"_dx|tXXzS@-M`yfW`Sp%x`|t@@zS@-M`f`d|'vAAzC@-M`gdx|}XXzS@-M`DoW`S5x`|}@@zS@-M`~o`d|~AAzC@-M`pdx|MXXzS@-M` xW`SEx`|[@@@zS@-M`=x`d|AA@zC@-M`}ydx|XX@zS@-M`ŀW`SUx`|@@zS@-M``d|TAAzC@-M`dx|XXzS@-M`VW`Sex`|@@zS@-M``d|AAzC@-M`dx|kXXzS@-M`+W`Sux`|{@@zS@-M`e`d|AAzC@-M`udx|+XXzS@-M`W`Sx`|<@@@zS@-M`#`d|AA@zC@-M`\dx|XXzS@-M`W`Sx`|@@zS@-M`գ`d|DAAzC@-M`dx|XXzS@-M`fW`Sx`|@@zS@-M``d|AAzC@-M`dx|kXXzS@-M`+W`Tx`|x@@zS@-M`b`d|AAzC@-M`dx|(XX@zS@-M`W`Tx`|5@@zS@-M` `d|AAzC@-M`edx|XXzS@-M`pW`T x`|@@@zS@-M``d|+AA@zC@-M`dx|uXXzS@-M`5W`T0x`|@@zS@-M`n`d|AAzC@-M`dx|XXzS@-M`W`T@x`|@@zS@-M``d|RAAzC@-M`dx|XXzS@-M`W`TPx`|@@zS@-M``d|4AAzC@-M`dx|xXX@zS@-M`8W`T`x`|@@zS@-M``d|AAzC@-M`dx|<XXzS@-M`W`Tp&x`|K@@zS@-M`K`d|AAzC@-M`Idx|XXzS@-M`W`T6x`|@@zS@-M``d|? AAzC@-M`dx|XXzS@-M`fW`TFx`|@@@zS@-M``d|AA@zC@-M`dx|eXX@zS@-M`% W`TVx`|q@@zS@-M`q `d|AAzC@-M` dx|#XXzS@-M`W`Tfx`|(#@@zS@-M`5`d|`$AAzC@-M` dx|+XXzS@-M`W`Tvx`|+@@zS@-M``d|;-AAzC@-M`dx|WAXXzS@-M`3W`Tx`|dA@@@zS@-M`G3`d|BAA@zC@-M`P4dx|BJXXzS@-M`<W`Tx`|MJ@@zS@-M`S<`d|KAAzC@-M`a=dx|RXXzS@-M`DW`Tx`|S@@zS@-M` E`d|BTAAzC@-M`Fdx|[XXzS@-M`MW`Ux`|[@@zS@-M`M`d|G]AAzC@-M`Odx|dXX@zS@-M`fVW`Ux`|d@@zS@-M`V`d|fAAzC@-M`Wdx|kmXXzS@-M`+_W`U x`|vm@@@zS@-M`W_`d|nAA@zC@-M`w`dx|&vXXzS@-M`gW`U0x`|1v@@zS@-M`2h`d|xwAAzC@-M`8idx|~XXzS@-M`pW`U@x`|~@@zS@-M`p`d|;AAzC@-M`qdx|XXzS@-M`gyW`UPx`|@@zS@-M`y`d|AAzC@-M`zdx|eXX@zS@-M`%W`U`x`|q@@zS@-M``d|ڑAAzC@-M`dx|XXzS@-M`ފW`Up'x`|*@@zS@-M`*`d|}AAzC@-M`=dx|ѡXXzS@-M`W`U7x`|ݡ@@zS@-M`ޓ`d|.AAzC@-M`dx|XXzS@-M`KW`UGx`|@@@zS@-M``d|AA@zC@-M`ݝdx|<XX@zS@-M`W`UWx`|G@@zS@-M`F`d|AAzC@-M`_dx|XXzS@-M`W`Ugx`|@@zS@-M``d|QAAzC@-M`dx|XXzS@-M`fW`Uwx`|@@zS@-M``d|'AAzC@-M`d`|Y@@zS@-M``d|AAzC@-M`dx|tXXzS@-M`4W`Ux`|@@zS@-M`p`d|AAzC@-M`dx|$XX@zS@-M`W`Ux`|1@@zS@-M``d|AAzC@-M`Cdx|XX@zS@-M`W`Vx`|@@zS@-M``d|ZAAzC@-M`dx|XXzS@-M`ZW`Vx`|@@zS@-M``d|AAzC@-M`dx| XX@zS@-M`W`V x`|% @@zS@-M``d|R AAzC@-M`dx|XX@zS@-M`iW`V0x`|@@zS@-M``d|AAzC@-M`dx|fXXzS@-M`& W`V@x`|s@@zS@-M`` `d|AAzC@-M`sdx|-$XXzS@-M`W`VPx`|:$@@@zS@-M`#`d|%AA@zC@-M`qdx|,XXzS@-M`W`V`x`|,@@zS@-M``d|[.AAzC@-M` dx|5XXzS@-M`g'W`Vp(x`|5@@zS@-M`'`d|6AAzC@-M`(dx|8>XXzS@-M`/W`V8x`|E>@@zS@-@@zC@->M`.0`d|?AAzC@-M`^1dx|FXXzS@-M`8W`VHx`|F@@zS@-M`8`d|XHAAzC@-M`:dx|OXX@>zS@-M`YAW`VXx`|O@@>zS@-zC@->M`A`d| QAA>zC@-M`Bdx|XXX>zS@-M`IW`Vhx`|X@@@>zS@-zC@->M`(J`d|pYAA@>zC@-M`0Kdx|`XX@>zS@-M`RW`Vxx`|`@@@>zS@-zC@->M`R`d|.bAA@>zC@-M`Sdx|iXXzS@-M`p[W`Vx`|i@@@zS@-M`[`d|kAA@zC@-M`\dx|srXXzS@-M`3dW`Vx`|r@@zS@-M`d`d|tAAzC@-M`edx|{XXzS@-M`lW`Vx`|{@@zS@-M`m`d|L|AAzC@-M` ndx|ÃXX@zS@-M`uW`Wx`|Ѓ@@zS@-M`u`d| AAzC@-M`vdx|PXX@zS@-M`~W`Wx`|a@@zS@-M`c~`d|AAzC@-M`]dx|XXzS@-M`ֆW`W x`|#@@zS@-M`3`d|AAzC@-M`Gdx|ߝXX@zS@-M`W`W0x`|@@zS@-M``d|cAAzC@-M`#dx|nXX@zS@-M`.W`W@x`||@@zS@-M`\`d|ŧAAzC@-M`dx|!XXzS@-M`W`WP x`|0@@zS@-M`2`d|AAzC@-M`Gdx|طXXzS@-M`W`W`x`|@@@zS@-M``d|AA@zC@-M`֪dx|XXzS@-M`YW`Wp)x`|@@zS@-M``d|AAzC@-M`dx|bXXzS@-M`"W`W9x`|q@@zS@-M`r`d|AAzC@-M`dx|&XX>zS@-M`W`WIx`|7@@>zS@-zC@->M``d|AA>zC@-M`Edx|XX>zS@-M`rW`WYx`|@@>zS@-zC@->M``d|AA>zC@-M`dx|tXX>zS@-M`4W`Wix`|@@>zS@-zC@->M`c`d|AA>zC@-M`dx|6XX>zS@-M`W`Wyx`|C@@>zS@-zC@->M`Y`d|AA>zC@-M`Bdx|XX@>zS@-M`W`Wx`|@@@>zS@-zC@->M``d|.AA@>zC@-M`dx|XX@>zS@-M`IW`Wx`|@@>zS@-zC@->M``d|AA>zC@-M`dx|KXX>zS@-M` W`Wx`|X@@@>zS@-zC@->M`w`d|"AA@>zC@-M`dx|'XXzS@-M`W`Xx`|'@@zS@-M``d|])AAzC@-M`dx|0XXzS@-M`w"W`Xx`|0@@zS@-M`"`d|2AAzC@-M`#dx|9XXzS@-M`F+W`X x`|9@@@zS@-M`+`d|:AA@zC@-M`,dx|[BXXzS@-M`4W`X0x`|gB@@zS@-M`h4`d|CAAzC@-M`z5dx|KXXzS@-M`<W`X@x`|"K@@zS@-M`"=`d|tLAAzC@-M`4>dx|SXX@zS@-M`EW`XP x`|S@@zS@-M`E`d|UAAzC@-M`Fdx|\XX@zS@-M`YNW`X`x`|\@@zS@-M`N`d|-^AAzC@-M`Odx|JrXXzS@-M` dW`Xp*x`|Wr@@zS@-M`Xd`d|sAAzC@-M`Wedx|zXX@zS@-M`lW`X:x`|z@@zS@-M`l`d|@|AAzC@-M`ndx|XX@zS@-M`UuW`XJx`|@@zS@-M`u`d| AAzC@-M`vdx|RXXzS@-M`~W`XZx`|^@@zS@-M`_~`d|AAzC@-M`]dx|הXXzS@-M`W`Xjx`|@@@zS@-M``d|$AA@zC@-M`dx|XXzS@-M`SW`Xzx`|@@zS@-M``d|AAzC@-M`dx|XXzS@-M`ޗW`Xx`|*@@zS@-M`)`d|AAzC@-M`Ddx|ڮXXzS@-M`W`Xx`|@@zS@-M``d|WAAzC@-M`dx|XXzS@-M`[W`Xx`|@@zS@-M`˩`d|AAzC@-M`dx|HXXzS@-M`W`Yx`|S@@@zS@-M`Q`d|AA@zC@-M`Qdx|XXzS@-M`ǺW`Yx`|@@zS@-M``d|MAAzC@-M` dx|XXzS@-M`MW`Y x`|@@zS@-M``d|AAzC@-M`dx|UXX@zS@-M`W`Y0x`|a@@zS@-M`x`d|AAzC@-M`dx|XX@zS@-M`W`Y@x`|@@zS@-M``d|WAAzC@-M`dx|XXzS@-M`iW`YP x`|@@zS@-M``d|AAzC@-M`dx|nXX@zS@-M`.W`Y`x`|z@@zS@-M`z`d|AAzC@-M`dx|"XX@zS@-M`W`Yp+x`|-@@zS@-M`4`d|PAAzC@-M`dx|XXzS@-M`jW`Y;x`|@@zS@-M``d|AAzC@-M`dx|XXXzS@-M` W`YKx`|e@@@zS@-M`a `d|AA@zC@-M`z dx| XXzS@-M` W`Y[x`|@@zS@-M` `d|eAAzC@-M`% d`|@@zS@-M`6 `d|b%AAzC@-M`" dx|,XXzS@-M`e W`Y{x`|,@@zS@-M` `d|-AAzC@-M` dx|5XXzS@-M`& W`Yx`|-5@@zS@-M`' `d|6AAzC@-M`N( dx|AXXzS@-M`3 W`Yx`|A@@@zS@-M`3 `d|BAA@zC@-M`4 dx|JXXzS@-M`N< W`Yx`|J@@zS@-M`|< `d|KAAzC@-M`= dx|SXXzS@-M`D W`Zx`|,S@@zS@-M` E `d|ETAAzC@-M`F dx|[XX@zS@-M`OM W`Zx`|[@@zS@-M`}M `d|\AAzC@-M`~N dx|-dXX@zS@-M`U W`Z x`|:d@@zS@-M`V `d|HeAAzC@-M`W dx|lXX@>zS@-M`q^ W`Z0x`|l@@@>zS@-zC@->M`^ `d|nAA@>zC@-M`_ dx|iuXXzS@-M`)g W`Z@x`||u@@zS@-M`^g `d|vAAzC@-M`zh dx|/~XX>zS@-M`o W`ZP x`|>~@@>zS@-zC@->M`Vp `d|AA>zC@-M`Xq dx|XX>zS@-M`x W`Z`x`|@@>zS@-zC@->M`y `d|JAA>zC@-M` z dx|oXX@zS@-M`/ W`Zp,x`|@@zS@-M`c `d|AAzC@-M`| dx|XX@zS@-M` W`Z<x`|@@zS@-M`ډ `d|<AAzC@-M` dx|sXXzS@-M`3 W`ZLx`|@@zS@-M` `d|AAzC@-M` dx|GXXzS@-M` W`Z\x`|V@@@zS@-M`z `d|AA@zC@-M`w dx|NXXzS@-M` W`Zlx`|\@@zS@-M`_ `d|AAzC@-M`m dx|EXXzS@-M` W`Z|x`|S@@zS@-M`P `d|AAzC@-M`k dx|XXzS@-M` W`Zx`|@@zS@-M` `d| AAzC@-M`̶ dx|GXXzS@-M` W`Zx`|S@@zS@-M`W `d|AAzC@-M`O dx|XXzS@-M` W`Zx`|(@@@zS@-M`! `d|dAA@zC@-M`$ dx|XXzS@-M` W`[x`|@@zS@-M` `d|NAAzC@-M` dx|XXzS@-M`v W`[x`|@@zS@-M` `d|AAzC@-M` dx|AXX@zS@-M` W`[ x`|N@@zS@-M`P `d|AAzC@-M`j dx|XX@zS@-M` W`[0x`|@@zS@-M` `d|OAAzC@-M` dx|l XXzS@-M`, W`[@x`|z @@zS@-M`} `d|AAzC@-M`u dx|4XX@zS@-M` W`[P x`|B@@zS@-M`? `d|AAzC@-M`s dx|XX@zS@-M` W`[`x`| @@zS@-M`+ `d|x AAzC@-M`8 dx|'XXzS@-M`p W`[p-x`|'@@zS@-M` `d|(AAzC@-M` dx|w0XXzS@-M`7" W`[=x`|0@@@zS@-M`" `d|1AA@zC@-M`# dx|F9XXzS@-M`+ W`[Mx`|Y9@@zS@-M`[+ `d|:AAzC@-M`v, dx|AXXzS@-M`3 W`[]x`| B@@zS@-M` 4 `d|5CAAzC@-M`4 dx|JXXzS@-M`N< W`[mx`|J@@zS@-M`< `d|KAAzC@-M`= dx|XSXXzS@-M`E W`[}x`|eS@@zS@-M`fE `d|TAAzC@-M`tF dx|\XXzS@-M`M W`[x`|&\@@@zS@-M`+N `d|~]AA@zC@-M`>O dx|dXXzS@-M`V W`[x`|d@@zS@-M`V `d|6fAAzC@-M`W dx|mXXzS@-M`U_ W`[x`|m@@zS@-M`_ `d|nAAzC@-M`` dx|XvXX@zS@-M`h W`\x`|ev@@zS@-M`gh `d|wAAzC@-M`}i dx|XX@zS@-M`p W`\x`|#@@zS@-M`(q `d|AAzC@-M`Gr dx|XX>zS@-M`y W`\ x`|Ї@@>zS@-zC@->M`y `d|+AA>zC@-M`z dx|jXX>zS@-M`* W`\0x`|x@@>zS@-zC@->M` `d|ґAA>zC@-M` dx|XX@>zS@-M` W`\@x`|@@@>zS@-zC@->M` `d|=AA@>zC@-M` dx|XX@>zS@-M`u W`\Px`|¡@@>zS@-zC@->M`ԓ `d|AA>zC@-M`֔ dx|wXX>zS@-M`7 W`\`x`|@@@>zS@-zC@->M` `d|AA@>zC@-M` dx|:XX@>zS@-M` W`\p.x`|G@@@>zS@-zC@->M`Z `d|AA@>zC@-M`j dx|XX>zS@-M` W`\>x`|@@>zS@-zC@->M` `d|BAA>zC@-M` dx|XX>zS@-M`u W`\Nx`|@@>zS@-zC@->M`ڶ `d|AA>zC@-M`ٷ dx|wXX>zS@-M`7 W`\^x`|@@>zS@-zC@->M`d `d|AA>zC@-M` dx|5XX>zS@-M` W`\nx`|A@@>zS@-zC@->M`U `d|AA>zC@-M`A dx|XX@>zS@-M` W`\~x`|@@@>zS@-zC@->M` `d|SAA@>zC@-M` dx|XX@>zS@-M`N W`\x`|@@>zS@-zC@->M` `d|AA>zC@-M` dx|aXXzS@-M`! W`\x`|u@@zS@-M`V `d|AAzC@-M` dx|XX@zS@-M` W`\x`|@@zS@-M` `d|aAAzC@-M`! dx|dXX>zS@-M`$ W`]x`|v@@@>zS@-zC@->M`U `d|AA@>zC@-M`k dx| XX@>zS@-M` W`]x`|# @@@>zS@-zC@->M` `d|T AA@>zC@-M` dx|XX>zS@-M`} W`] x`|@@>zS@-zC@->M` `d| AA>zC@-M` dx|aXX>zS@-M`! W`]0x`|q@@>zS@-zC@->M`R `d|AA>zC@-M`a dx|$XX>zS@-M` W`]@x`|'$@@>zS@-zC@->M` `d|y%AA>zC@-M`9 dx|,XX>zS@-M` W`]Px`|,@@>zS@-zC@->M` `d|#.AA>zC@-M` dx|h5XX@>zS@-M`(' W`]`x`|z5@@@>zS@-zC@->M`d' `d|6AA@>zC@-M`o( dx|>XX@>zS@-M`/ W`]p/x`|->@@>zS@-@@@>zC@->M`0 `d|?AA>zC@-M`K1 dx|FXX>zS@-M`8 W`]?x`|F@@@>zS@-zC@->M`9 `d|OHAA@>zC@-M`: d`|eO@@@>zS@-zS@-M`D W`]Ox`|^S@@@>zC@->M`E `d|[TAA@>zC@-M`F dx|[XX>zS@-M`bM W`]_x`|[@@>zS@-zC@->M`M `d|\AA>zC@-M`N dx|^dXX>zS@-M`V W`]ox`|od@@>zS@-zC@->M`V `d|eAA>zC@-M`W dx| mXX>zS@-M`^ W`]x`|m@@>zS@-zC@->M`-_ `d|2nAA>zC@-M`_ dx|uXX>zS@-M`rg W`]x`|u@@>zS@-zC@->M`g `d| wAA>zC@-M`h dx|~XX@>zS@-M`Bp W`]x`|~@@@>zS@-zC@->M`pp `d|AA@>zC@-M`q dx|HXX@>zS@-M`y W`]x`|b@@>zS@-zC@->M`9y `d|AA>zC@-M`kz dx|XX>zS@-M`d W`^x`|@@@>zS@-zC@->M`ȏ `d|AA@>zC@-M`Ð dx|AXX@>zS@-M` W`^x`|R@@@>zS@-zC@->M`8 `d|AA@>zC@-M`e dx|XX>zS@-M` W`^ x`| @@>zS@-zC@->M`# `d|NAA>zC@-M` dx|XX>zS@-M` W`^0x`|з@@>zS@-zC@->M` `d|8AA>zC@-M` dx|XX>zS@-M`c W`^@x`|@@>zS@-zC@->M` `d|AA>zC@-M`г dx|XX>zS@-M`@ W`^Px`|@@>zS@-zC@->M`n `d|AA>zC@-M`u dx|#XX@>zS@-M` W`^` x`|5@@@>zS@-zC@->M` `d|rAA@>zC@-M`2 dx|XX@>zS@-M` W`^p0x`| @@>zS@-zC@->M` `d|AA>zC@-M`F dx|XX>zS@-M` W`^@x`|@@@>zS@-zC@->M` `d|5AA@>zC@-M` dx|rXX@>zS@-M`2 W`^Px`|@@@>zS@-zC@->M`j `d|AA@>zC@-M` dx|5XX>zS@-M` W`^`x`|D@@>zS@-zC@->M`/ `d|AA>zC@-M`] dx|XX>zS@-M` W`^px`|@@>zS@-zC@->M` `d|AA>zC@-M`H dx|XX>zS@-M` W`^x`|@@>zS@-zC@->M` `d|?AA>zC@-M` dx|QXX>zS@-M` W`^x`|a@@>zS@-zC@->M`I `d|AA>zC@-M`x dx|XX@>zS@-M` W`^x`|"@@@>zS@-zC@->M` `d|fAA@>zC@-M`& dx| XX@>zS@-M` W`^x`| @@>zS@-zC@->M` `d|K"AA>zC@-M`  dx|)XX>zS@-M`i W`_x`|)@@@>zS@-zC@->M` `d|+AA@>zC@-M` dx|h2XX@>zS@-M`($ W`_x`|2@@@>zS@-zC@->M`X$ `d|3AA@>zC@-M`r% dx|:XX>zS@-M`, W`_ x`|;@@>zS@-zC@->M`, `d|4<AA>zC@-M`- dx|CXX>zS@-M`t5 W`_0x`|C@@>zS@-zC@->M`5 `d|EAA>zC@-M`6 dx|KLXX>zS@-M` > W`_@x`|`L@@>zS@- ``|~L@@>zC@->M`>> `d|MAA>zC@-M`o? dx|TXX>zS@-M`F W`_Px`| U@@>zS@-zC@->M`G `d|gVAA>zC@-M`'H dx|]XX@>zS@-M`CO W`_`!x`|]@@@>zS@-zC@->M`O `d|^AA@>zC@-M`P dx|>fXX@>zS@-M`W W`_p1x`|Of@@>zS@-zC@->M`5X `d|gAA>zC@-M`ZY dx|nXX>zS@-M`` W`_Ax`|n@@@>zS@-zC@->M` a `d|8pAA@>zC@-M`a dx|wXX@zS@-M`gi W`_Qx`|w@@zS@-M`i `d|xAAzC@-M`j dx|8XXzS@-M`q W`_ax`|X@@zS@-M`0r `d|AAzC@-M``s dx|ވXX@>zS@-M`z W`_qx`|@@@>zS@-zC@->M`z `d|LAA@>zC@-M` | dx|XXzS@-M`s W`_x`|@@@zS@-M` `d|AA@zC@-M` dx|pXXzS@-M`0 W`_x`|~@@zS@- ``|@@zC@->M`` `d|ٛAAzC@-M` dx|0XXzS@-M` W`_x`|>@@zS@-M`# `d|~AAzC@-M`> dx|XXzS@-M`q W`_x`|@@zS@-M` `d|5AAzC@-M` dx|XXzS@-M`X W``x`|@@zS@-M` `d|AAzC@-M` dx|@XXzS@-M` W``x`|W@@@zS@-M`6 `d|AA@zC@-M`i dx| XXzS@-M` W`` x`|@@zS@-M` `d|sAAzC@-M`3 dx|XXzS@-M` W``0x`|@@zS@-M` `d|%AAzC@-M` dx|XX@zS@-M`r W``@x`|@@zS@-M` `d|AAzC@-M` dx|{XX@zS@-M`; W``Px`|@@zS@-M`t `d|AAzC@-M` dx|FXXzS@-M` W```"x`|V@@zS@-M`@ `d|AAzC@-M`U dx|CXX@zS@-M` W``p2x`|Q@@zS@-M`= `d|AAzC@-M`O d`|0!@@@zS@-M`_ `d|&AA@zC@-M` dx|..XXzS@-M` W``Rx`|<.@@zS@-M`& `d||/AAzC@-M`M`( `d|i8AA@zC@-M`)* dx|?XX>zS@-M`?1 W``rx`|?@@>zS@-zC@->M`n1 `d|@AA>zC@-M`2 dx| HXX>zS@-M`9 W``x`| H@@>zS@-zC@->M`: `d|vIAA>zC@-M`6; dx|PXX>zS@-M`cB W``x`|P@@>zS@-zC@->M`B `d|QAA>zC@-M`C dx|%YXX>zS@-M`J W``x`|1Y@@>zS@-zC@->M`K `d|UZAA>zC@-M`L dx|aXX@>zS@-M`kS W``x`|a@@@>zS@-zC@->M`S `d|cAA@>zC@-M`T dx|ajXX@>zS@-M`!\ W`ax`|nj@@>zS@-zC@->M`\ `d|kAA>zC@-M`{] dx|rXX>zS@-M`d W`ax`|r@@@>zS@-zC@->M`e `d|3tAA@>zC@-M`e dx|{XX@>zS@-M`Zm W`a x`|{@@@>zS@-zC@->M`m `d| }AA@>zC@-M`n dx|XXzS@-M` W`a0x`|@@zS@-M` `d|WAAzC@-M` dx|XXzS@-M`@ W`a@x`|@@zS@-M`v `d|˛AAzC@-M` dx|BXXzS@-M` W`aPx`|O@@zS@-M`9 `d|AAzC@-M`b dx|XXzS@-M`ǝ W`a`#x`|@@zS@-M` `d|AAzC@-M`D d`|@@zS@-M` `d|tAAzC@-M`4 dx|XXzS@-M` W`aCx`|@@zS@-M` `d|'AAzC@-M` dx|XXzS@-M`I W`aSx`|@@@zS@-M` `d|AA@zC@-M` dx|JXXzS@-M` W`acx`|[@@zS@-M`9 `d|AAzC@-M`j dx|XX>zS@-M` W`asx`|@@>zS@-zC@->M` `d|)AA>zC@-M` dx|XX@zS@-M` W`ax`|4@@zS@-M` `d|jAAzC@-M`* dx|XXzS@-M`e W`ax`|@@zS@-M` `d| AAzC@-M` dx|^XX@zS@-M` W`ax`|n@@zS@-M`X `d|AAzC@-M` dx|"XXzS@-M` W`ax`|1@@@zS@-M` `d|AA@zC@-M`b dx| XXzS@-M`W`bx`|@@zS@-M``d|kAAzC@-M`+dx|XXzS@-M`t W`bx`|@@zS@-M` `d| AAzC@-M`dx|$XX@zS@-M`QW`b x`|$@@zS@-M``d|&AAzC@-M`dx|-XXzS@-M`IW`b0x`|-@@zS@-M``d|.AAzC@-M` dx|D6XXzS@-M`(W`b@x`|S6@@zS@-M`?(`d|7AAzC@-M`a)dx|?XXzS@-M`0W`bPx`|?@@zS@-M`0`d|I@AAzC@-M` 2dx|GXX@zS@-M`9W`b`$x`|G@@zS@-M`9`d|AIAAzC@-M`;dx|PXXzS@-M`MBW`bp4x`|P@@zS@-M`}B`d|QAAzC@-M`Cdx|^YXXzS@-M`KW`bDx`|mY@@@zS@-M`UK`d|ZAA@zC@-M`Ldx|aXXzS@-M`SW`bTx`|b@@zS@-M`S`d|TcAAzC@-M`Udx|jXX@zS@-M`l\W`bdx`|j@@zS@-M`\`d| lAAzC@-M`]dx|~sXXzS@-M`>eW`btx`|s@@zS@-M`se`d|tAAzC@-M`fdx|!|XX@zS@-M`mW`bx`|.|@@zS@-M`n`d|}AAzC@-M`Fodx|XXzS@-M`vW`bx`|@@@zS@-M`v`d|8AA@zC@-M`wdx|XXzS@-M`pW`bx`|@@zS@-M``d|AAzC@-M`dx|8XXzS@-M`W`bx`|H@@zS@-M`(`d|AAzC@-M`jdx|XX@zS@-M`W`cx`| @@zS@-M` `d|IAAzC@-M` dx|XXzS@-M`}W`cx`|ʧ@@zS@-M`H`d|AAzC@-M`Hdx|uXXzS@-M`5W`c x`|@@zS@-M``d|ڱAAzC@-M`dx|:XXzS@-M`W`c0x`|H@@zS@-M`R`d|AAzC@-M`ddx|XX@zS@-M`W`c@x`|@@zS@-M`$`d|QAAzC@-M`dx|XXzS@-M`?W`cPx`|@@zS@-M``d|AAzC@-M`dx| XXzS@-M`W`c`%x`|@@@zS@-M``d|[AA@zC@-M`dx|XXzS@-M`|W`cp5x`|@@zS@-M``d|AAzC@-M`dx|XX@zS@-M`IW`cEx`|@@zS@-M``d|AAzC@-M`dx|KXXzS@-M` W`cUx`|X@@zS@-M`W`d|AAzC@-M`_dx| XX@zS@-M`W`cex`|@@zS@-M``d|gAAzC@-M`'dx| XX@>zS@-M`W`cux`| @@@>zS@-zC@->M``d|s AA@>zC@-M`3dx|XX@>zS@-M`yW`cx`|@@>zS@-zC@->M``d|'AA>zC@-M`dx|CXX>zS@-M`W`cx`|O@@@>zS@-zC@->M`c`d|AA@>zC@-M`hdx|&XX@>zS@-M`W`cx`| &@@@>zS@-zC@->M` `d|_'AA@>zC@-M`dx|.XX>zS@-M` W`cx`|.@@>zS@-zC@->M` `d|J0AA>zC@-M` "dx|7XX>zS@-M`n)W`dx`|7@@>zS@-zC@->M`)`d|9AA>zC@-M`*dx|b@XX@>zS@-M`"2W`dx`|l@@@@>zS@-zC@->M`2`d|AAA@>zC@-M`d3dx|IXX@>zS@-M`:W`d x`|"I@@>zS@-zC@->M`(;`d|tJAA>zC@-M`4<dx|QXX>zS@-M`SW`d0x`|Q@@@>zS@-zC@->M``d|ZSAA@>zC@-M`dx|\ZXX@>zS@-M` W`d@x`|iZ@@@>zS@-zC@->M`: `d|[AA@>zC@-M`E dx|cXX>zS@-M`W`dPx`|*c@@>zS@-zC@->M``d|cdAA>zC@-M`dx|kXX>zS@-M`LW`d`&x`|k@@>zS@-zC@->M``d|+mAA>zC@-M`dx|tXX@>zS@-M`$W`dp6x`|t@@@>zS@-zC@->M`$`d|uAA@>zC@-M`u%dx|a}XX@>zS@-M`,W`dFx`|p}@@>zS@-zC@->M`c-`d|~AA>zC@-M`e.dx|XX>zS@-M`q5W`dVx`|@@@>zS@-zC@->M`5`d|BAA@>zC@-M`6dx|XX@>zS@-M`5>W`dfx`|Ž@@@>zS@-``|{@@@>zC@->M`>`d|AA@>zC@-M`@dx|}XX>zS@-M`FW`dvx`|@@>zS@-zC@->M`YG`d|ǘAA>zC@-M`GHdx|XX>zS@-M`OW`dx`|!@@>zS@-zC@->M`O`d|AA>zC@-M` Qdx|ۨXX>zS@-M`[XW`dx`|@@>zS@-zC@->M`X`d|>AA>zC@-M`Ydx|XX@>zS@-M`!aW`dx`|@@@>zS@-zC@->M`a`d|AA@>zC@-M`~bdx|XX@>zS@-M`-wW`dx`|@@>zS@-zC@->M`w`d|AA>zC@-M`xdx|kXX>zS@-M`W`ex`|x@@@>zS@-zC@->M`N`d|AA@>zC@-M`Pdx|&XX@>zS@-M`W`ex`|5@@@>zS@-zC@->M` `d|AA@>zC@-M`dx|XX>zS@-M``W`e x`|@@>zS@-zC@->M`Ƒ`d|MAA>zC@-M`͒dx|XX>zS@-M`#W`e0x`|@@>zS@-zC@->M`X`d|AA>zC@-M`d`|3@@>zS@-zS@-M`aW`e@x`|@@>zC@->M``d|;AA>zC@-M`dx| XX>zS@-M`W`ePx`| @@>zS@-zC@->M`J`d| AA>zC@-M`dx|F XX@>zS@-M`ƼW`e`'x`|U @@@>zS@-zC@->M``d| AA@>zC@-M`)dx| XX@>zS@-M`W`ep7x`| @@>zS@-zC@->M``d|f AA>zC@-M`dx| XX>zS@-M`^W`eGx`| @@@>zS@-zC@->M``d|" AA@>zC@-M`dx|' XX@>zS@-M`W`eWx`|' @@@>zS@-zC@->M`M`d|( AA@>zC@-M`ddx|Q0 XX>zS@-M`W`egx`|`0 @@>zS@-zC@->M``d|1 AA>zC@-M`)dx|F XX>zS@-M`NW`ewx`|F @@>zS@-zC@->M``d|6H AA>zC@-M`dx|O XX>zS@-M`W`ex`|O @@>zS@-zC@->M`J`d|Q AA>zC@-M`dx|aX XX>zS@-M`W`ex`|oX @@>zS@-zC@->M``d|Y AA>zC@-M`1 dx|` XX@>zS@-M`uW`ex`|a @@@>zS@-zC@->M``d|Wb AA@>zC@-M`dx|i XX@>zS@-M`<W`ex`|i @@>zS@-zC@->M`s`d|%k AA>zC@-M`dx|r XX>zS@-M`"W`fx`|r @@@>zS@-zC@->M`7"`d|s AA@>zC@-M`^#dx|Q{ XX@>zS@-M`*W`fx`|`{ @@@>zS@-zC@->M`D+`d|| AA@>zC@-M`7,dx| XX>zS@-M`3W`f x`| @@>zS@-zC@->M`3`d|r AA>zC@-M`4dx|Ȍ XX>zS@-M`H<W`f0x`|֌ @@>zS@-zC@->M`<`d|/ AA>zC@-M`=dx| XX>zS@-M`EW`f@x`| @@>zS@-zC@->M`JE`d| AA>zC@-M`Fdx|r XXzS@-M`MW`fPx`| @@@zS@-M`nN`d| AA@zC@-M`cOdx| XXzS@-M`VW`f`(x`| @@zS@-M`V`d|Z AAzC@-M`Wdx| XXzS@-M`5_W`fp8x`|ï @@zS@-M`_`d| AAzC@-M`|`dx|{ XX@zS@-M`gW`fHx`| @@zS@-M`Mh`d|չ AAzC@-M`Uidx|9 XXzS@-M`pW`fXx`|F @@zS@-M`q`d| AAzC@-M`>rdx| XXzS@-M`FyW`fhx`| @@zS@-M`y`d| AAzC@-M`{zdx| XXzS@-M`W`fxx`|3 @@zS@-M``d| AAzC@-M`dx| XX@zS@-M`RW`fx`| @@zS@-M``d|: AAzC@-M`dx| XXzS@-M`W`fx`| @@zS@-M`g`d| AAzC@-M`idx| XXzS@-M`W`fx`| @@@zS@-M`ޛ`d| AA@zC@-M`dx|*!XXzS@-M`W`fx`|>!@@@zS@-M``d|!AA@zC@-M`dx| !XXzS@-M`W`gx`| !@@zS@-M`S`d| !AAzC@-M`cdx|D!XXzS@-M`W`gx`|R!@@zS@-M``d|!AAzC@-M`Fdx|!XX@zS@-M`W`g x`|!@@zS@-M``d|q!AAzC@-M`dx|z$!XXzS@-M`W`g0x`|$!@@zS@-M`O`d|%!AAzC@-M`Udx|,!XXzS@-M`sW`g@ x`|-!@@zS@-M``d|Z.!AAzC@-M`dx|5!XXzS@-M`;W`gPx`|5!@@@zS@-M``d|>7!AA@zC@-M`dx|>!XXzS@-M`W`g`)x`|>!@@zS@-!@@zC@->M`O`d|?!AAzC@-M`Sdx|G!XXvDwS@-M`W`gp9x`|G!@@@xDwS@-M``d|rH!AA@xDwC@-M`dx|wO!XXwDwS@-M`W`gIx`|O!@@}DwS@-M`F`d|P!AA}DwC@-M`Sdx|9X!XX}DwS@-M`W`gYx`|FX!@@wDwS@-M` `d|Y!AAwDwC@-M`" dx|`!XX@xDwS@-M`xW`gix`|a!@@vDwS@-M``d|Nb!AAvDwC@-M`dx|i!XX@~DwS@-M`IW`gyx`|i!@@yDwS@-M``d|k!AAyDwC@-M`dx|q!XXqDwS@-M`X!W`gx`|q!@@yDwS@-M`!`d|"s!AAyDwC@-M`"dx|dz!XX@{DwS@-M`)W`gx`|qz!@@DwS@-M`2*`d|{!AADwC@-M`E+dx|!XXtDwS@-M`s2W`gx`|!@@vDwS@-M`2`d|V!AAvDwC@-M`3dx|!XXvDwS@-M` ;W`gx`|!@@tDwS@-M`p;`d|%!AAtDwC@-M`Wdx|!XXDwS@-M`]W`hx`|!@@@{DwS@-M`L]`d|!AA@{DwC@-M`^dx|!XXyDwS@-M`fW`hx`|!@@qDwS@-M`Gf`d|!AAqDwC@-M`sgdx|#!XXyDwS@-M`nW`h x`|/!@@@~DwS@-M`n`d|!AA@~DwC@-M`pdx|!XXvDwS@-M`VwW`h0x`|!@@@xDwS@-M`w`d|F!AA@xDwC@-M`xdx|!XXwDwS@-M`W`h@ x`|!@@}DwS@-M`b`d|!AA}DwC@-M`kdx|Q!XX}DwS@-M`шW`hPx`|\!@@wDwS@-M``d|!AAwDwC@-M`%dx|!XX@xDwS@-M`XW`h`*x`|!@@vDwS@-M``d|G!AAvDwC@-M`ǒdx|b!XX@~DwS@-M`W`hp:x`|n!@@yDwS@-M`.`d|!AAyDwC@-M`Mdx|"!XXqDwS@-M`W`hJx`|.!@@yDwS@-M``d|x!AAyDwC@-M`dx|!XX@{DwS@-M`%W`hZx`|!@@DwS@-M`l`d|!AADwC@-M`dx|*"XXtDwS@-M`W`hjx`|6"@@vDwS@-M``d|"AAvDwC@-M`dx| "XXvDwS@-M`cW`hzx`| "@@tDwS@-M``d|@"AAtDwC@-M`dx|"XXDwS@-M`*W`hx`|"@@@{DwS@-M`z`d|"AA@{DwC@-M`dx|m"XXyDwS@-M`W`hx`|y"@@qDwS@-M`6`d|"AAqDwC@-M`Jdx|)'"XXyDwS@-M`W`hx`|4'"@@@~DwS@-M``d|("AA@~DwC@-M`ndx|/"XXvDwS@-M` W`hx`|/"@@@xDwS@-M`]`d|0"AA@xDwC@-M`jdx|'8"XXwDwS@-M`W`ix`|38"@@}DwS@-M``d|9"AA}DwC@-M`dx|@"XX}DwS@-M`6W`ix`|@"@@wDwS@-M``d|A"AAwDwC@-M`}d`|I"@@@xDwS@-M`-`d|N"AA@xDwC@-M`=dx|V"XX@~DwS@-M`W`i0x`|V"@@yDwS@-M``d|nW"AAyDwC@-M`dx|^"XXqDwS@-M`;W`i@ x`|^"@@yDwS@-M``d|`"AAyDwC@-M`dx|s"XX@{DwS@-M`,#W`iPx`|s"@@DwS@-M`c#`d|t"AADwC@-M`a$dx|d|"XXtDwS@-M`+W`i`+x`|y|"@@vDwS@-M`<,`d|}"AAvDwC@-M`-dx|ۄ"XXvDwS@-M`[4W`ip;x`|"@@tDwS@-M`4`d| "AAtDwC@-M`5dx|"XXDwS@-M`=W`iKx`|"@@@{DwS@-M`?=`d|Ŏ"AA@{DwC@-M`E>dx|T"XXyDwS@-M`EW`i[x`|e"@@qDwS@-M`(F`d|"AAqDwC@-M`Gdx| "XXyDwS@-M`NW`ikx`|"@@@~DwS@-M`N`d|D"AA@~DwC@-M`Odx|"XX@zS@-M`WW`i{x`|"@@zS@-M`BW`d|"AAzC@-M`?Xdx|"XXzS@-M`_W`ix`|+"@@@zS@-M`_`d|="AA@zC@-M``dx|ݸ"XX@xDwS@-M`]hW`ix`|"@@vDwS@-M`h`d|"AAvDwC@-M`idx|k"XXwDwS@-M`pW`ix`|"@@}DwS@-M`&q`d|"AA}DwC@-M`8rdx|"XX}DwS@-M`FyW`ix`|"@@wDwS@-M`wy`d|"AAwDwC@-M`kzdx|T"XXvDwS@-M`ԁW`jx`|c"@@@xDwS@-M``d|r"AA@xDwC@-M`dx|"XX@~DwS@-M`W`jx`|"@@yDwS@-M``d|/"AAyDwC@-M`dx|"XXqDwS@-M`KW`j x`|"@@yDwS@-M`~`d|"AAyDwC@-M`dx|W"XXzS@-M`כW`j0x`|g"@@zS@-M`8`d|"AAzC@-M`<dx|"XXzS@-M`W`j@ x`|"@@zS@-M`=`d|"AAzC@-M`mdx|R"XXzS@-M`ҬW`jPx`|e"@@@zS@-M`$`d|"AA@zC@-M`&dx|#XX@zS@-M`W`j`,x`|#@@zS@-M``d|W#AAzC@-M`׶dx|#XXzS@-M`3W`jp<x`|#@@zS@-``|#@@zC@->M`r`d|#AAzC@-M`dx|d#XXzS@-M`W`jLx`|o#@@@zS@-M``d|#AA@zC@-M`Fdx| #XXzS@-M`W`j\x`|) #@@zS@-M``d|H!#AAzC@-M`dx|(#XXzS@-M`4W`jlx`|(#@@zS@-M`d`d|!*#AAzC@-M`dx|[1#XXzS@-M`W`j|x`|e1#@@zS@-M``d|3#AAzC@-M`dx|:#XXzS@-M`W`jx`|):#@@zS@-M``d|z;#AAzC@-M`dx|B#XXzS@-M`%W`jx`|B#@@zS@-M``d|#D#AAzC@-M`dx|GK#XXzS@-M`W`jx`|SK#@@zS@-M``d|L#AAzC@-M`dx|S#XXzS@-M`oW`jx`|S#@@zS@-M``d|2U#AAzC@-M`dx|r\#XXzS@-M` W`kx`|\#@@zS@-M`' `d|]#AAzC@-M`J dx|'e#XXzS@-M`W`kx`|1e#@@zS@-M``d|f#AAzC@-M`dx|m#XXzS@-M`W`k x`|m#@@zS@-M`f`d|o#AAzC@-M`dx|?v#XXzS@-M`%W`k0x`|Lv#@@zS@-M` &`d|w#AAzC@-M`3'dx|~#XXzS@-M`K.W`k@ x`|~#@@zS@-M`.`d|#AAzC@-M`/dx|U#XXzS@-M`6W`kPx`|c#@@zS@-M`-7`d|#AAzC@-M`#8dx|#XXzS@-M`>?W`k`-x`|ɏ#@@zS@-M`?`d| #AAzC@-M`@dx|#XXzS@-M`GW`kp=x`|&#@@zS@-M`G`d|y#AAzC@-M`Hdx|#XXzS@-M`iPW`kMx`|#@@zS@-M`P`d|3#AAzC@-M`Qdx|#XXzS@-M`YW`k]x`|#@@zS@-M`qY`d|#AAzC@-M`>Zdx|#XXovS@-M`saW`kmx`| #@@ovS@-M`a`d|I#AAovC@-M`bdx|#XXovS@-M`(jW`k}x`|#@@ovS@-M`j`d|##AAovC@-M`kdx|X#XXovS@-M`rW`kx`|g#@@ovS@-M`s`d|#AAovC@-M`1tdx|#XX@ovS@-M`W{W`kx`|#@@ovS@-M`{`d|!#AAovC@-M`|dx|#XX@ovS@-M`W`kx`|#@@ovS@-M`i`d|#AAovC@-M`jdx|7#XXovS@-M`W`kx`|G#@@@ovS@-M``d|R#AA@ovC@-M`ҍdx|#XXovS@-M`FW`lx`|#@@@ovS@-M`z`d|#AA@ovC@-M`dx|[#XXzS@-M`۝W`lx`|k#@@zS@-M` `d|#AAzC@-M`dx|#XXzS@-M`W`l x`|.#@@zS@-M`/`d|#AAzC@-M`dx|#XX@zS@-M` W`l0x`|#@@zS@-M`?`d|$AAzC@-M`%dx|$XXzS@-M`W`l@x`|]$@@zS@-M``d| $AAzC@-M`2dx|$XXzS@-M`W`lPx`|$@@zS@-M`U`d|$AAzC@-M`ydx|A$XXzS@-M`W`l`.x`|S$@@zS@-M``d|]$AAzC@-M`dx|0$XXzS@-M`W`lp>x`|0$@@zS@-M``d|t1$AAzC@-M`dx|8$XX@zS@-M`BW`lNx`|8$@@zS@-M``d|:$AAzC@-M`dx|A$XXzS@-M`W`l^x`|A$@@zS@-M`S`d|B$AAzC@-M`[dx|VJ$XX@zS@-M`W`lnx`|fJ$@@zS@-M`*`d|K$AAzC@-M`'dx|R$XXzS@-M`W`l~x`|S$@@@zS@-M``d|rT$AA@zC@-M`dx|[$XXzS@-M` W`lx`|[$@@zS@-M`g `d|\$AAzC@-M`s dx| d$XXzS@-M`W`lx`|6d$@@zS@-M``d|]e$AAzC@-M`dx|pl$XXzS@-M`W`lx`|l$@@zS@-M`!`d|m$AAzC@-M`+dx|u$XXzS@-M`$W`lx`|#u$@@zS@-M`$`d|Rv$AAzC@-M`%d`|}$@@zS@-M`g6`d|$AAzC@-M`s7dx|'$XXzS@-M`>W`mx`|;$@@zS@-``|]$@@zC@->M`>`d|$AAzC@-M`@dx|$XXzS@-M`3GW`m x`|$@@zS@-M`G`d|$AAzC@-M`Hdx|y$XXzS@-M`OW`m0x`|$@@zS@-M`.P`d|ơ$AAzC@-M`FQdx|$XXzS@-M`XW`m@x`|$@@zS@-M`X`d|$AAzC@-M`Zdx|$XXMwS@-M``W`mPx`|$@@MwS@-M`Ya`d|ϲ$AAMwC@-M`Obdx|$XXMwS@-M`iW`m`/x`|$@@MwS@-M`i`d|[$AAMwC@-M`jdx|$XXMwS@-M`.rW`mp?x`|$@@MwS@-r``|$@@MwC@->M`r`d|$AAMwC@-M`xsdx|A$XXMwS@-M`zW`mOx`|T$@@MwS@-M`{`d|$AAMwC@-M`(|dx|$XXMwS@-M`NW`m_x`|$@@MwS@-M``d|($AAMwC@-M`dx|A$XXMwS@-M`W`mox`|S$@@MwS@-M``d|$AAMwC@-M`,dx|$XX@MwS@-M`>W`mx`|$@@@MwS@-M``d|#$AA@MwC@-M`dx|$XX@MwS@-M`W`mx`|$@@@MwS@-M`]`d|$AA@MwC@-M`Rdx|$XX>zS@-M`W`mx`|5$@@>zS@-zC@->M`ѥ`d|Z$AA>zC@-M`ڦdx|$XX@>zS@-M`WW`mx`|$@@@>zS@-zC@->M``d|;%AA@>zC@-M`dx|%XX@>zS@-M`W`mx`|%@@>zS@-zC@->M`H`d|%AA>zC@-M`^dx|W%XX>zS@-M`׿W`nx`|e%@@@>zS@-zC@->M``d|%AA@>zC@-M`$dx|:&%XX@>zS@-M`W`nx`|I&%@@@>zS@-zC@->M``d|'%AA@>zC@-M`dx|.%XX>zS@-M`pW`n x`|.%@@>zS@-zC@->M``d|:0%AA>zC@-M`dx|7%XX@{DwS@-M`3W`n0x`|7%@@DwS@-M`n`d|8%AADwC@-M`}dx|@%XXtDwS@-M`5W`n@x`|@%@@vDwS@-M`e`d|A%AAvDwC@-M`Xdx|MI%XXvDwS@-M`W`nP x`|_I%@@tDwS@-M``d|J%AAtDwC@-M`@dx| R%XXovS@-M`W`n`0x`| R%@@ovS@-M``d|XS%AAovC@-M`dx|Z%XXovS@-M`N W`np@x`|Z%@@ovS@-M` `d| \%AAovC@-M` dx|c%XXDwS@-M`W`nPx`|c%@@@{DwS@-M`i`d|d%AA@{DwC@-M`6dx|]l%XXyDwS@-M`W`n`x`|ll%@@qDwS@-M``d|m%AAqDwC@-M`Rdx|u%XXyDwS@-M`$W`npx`|u%@@@~DwS@-M`$`d|iv%AA@~DwC@-M`%dx|}%XXovS@-M`B-W`nx`|}%@@ovS@-M`-`d|~%AAovC@-M`.dx|1%XX@xDwS@-M`5W`nx`|F%@@vDwS@-M`6`d|h%AAvDwC@-M`6dx|%XXwDwS@-M`%>W`nx`|%@@}DwS@-``|ӎ%@@wDwC@->M`S>`d|ҏ%AA}DwC@-M`R?dx|%XXovS@-M`GW`nx`|%@@ovS@-M`bG`d|%AAovC@-M`sHdx|2%XXovS@-M`OW`nx`|C%@@ovS@-M`P`d|%AAovC@-M`Qdx|%XXovS@-M`}XW`ox`|%@@@ovS@-M`X`d|g%AA@ovC@-M`Ydx|ű%XX@ovS@-M`EaW`ox`|ױ%@@ovS@-M`{a`d|!%AAovC@-M`bdx|j%XXovS@-M`iW`o x`|}%@@ovS@-M`=j`d|%AAovC@-M`rkdx|!%XXovS@-M`rW`o0x`|4%@@ovS@-M`r`d|m%AAovC@-M`sdx|%XXovS@-M`3{W`o@x`|%@@ovS@-M`{`d|%AAovC@-M`x|dx|6%XXovS@-M`W`oP!x`|O%@@ovS@-M``d|%AAovC@-M`dx|%XX}DwS@-M`5W`o`1x`|%@@wDwS@-M`k`d|%AAwDwC@-M`zdx|%XXvDwS@-M`W`opAx`|.%@@@xDwS@-M`Δ`d|k%AA@xDwC@-M`dx|%XX@~DwS@-M`:W`oQx`|%@@yDwS@-M`n`d|"%AAyDwC@-M`dx|)%XXqDwS@-M`W`oax`|>%@@yDwS@-M`ܥ`d|%AAyDwC@-M`dx|%XX@{DwS@-M`RW`oqx`|%@@DwS@-M``d|#&AADwC@-M`dx|J&XXtDwS@-M`ʶW`ox`|_&@@vDwS@-M``d|&AAvDwC@-M`dx|&XXvDwS@-M`,W`ox`|&@@tDwS@-M`m`d|&AAtDwC@-M`Qdx| &&XXDwS@-M`W`ox`|!&&@@@{DwS@-M``d|j'&AA@{DwC@-M`dx|w.&XX@{DwS@-M`W`ox`|.&@@DwS@-M`1`d|/&AADwC@-M`Ldx|6&XXDwS@-M`MW`ox`|6&@@@{DwS@-M``d|S&AA@{DwC@-M`ddx|X&XX5zS@-M`W`px`|X&@@5zS@-M``d|Z&AA5zC@-M` dx|&a&XX@5zS@-M`W`px`|0a&@@5zS@-M``d|ob&AA5zC@-M`dx|i&XX@5zS@-M`bW`p x`|i&@@5zS@-M``d|Wk&AA5zC@-M`dx|r&XX@{DwS@-M`*"W`p0x`|r&@@DwS@-M`g"`d|s&AADwC@-M`j#dx|{&XXyDwS@-M`*W`p@x`|{&@@qDwS@-M`*`d|J|&AAqDwC@-M`+dx|&XXyDwS@-M`y3W`pP"x`|&@@@~DwS@-M`3`d|a&AA@~DwC@-M`4dx|C&XX5zS@-M`IW`p`2x`|N&@@@5zS@-M`J`d|›&AA@5zC@-M`BKdx|&XX5zS@-M`RW`ppBx`|&@@@5zS@-M`HR`d|ѣ&AA@5zC@-M`QSdx|!&XX5zS@-M`ZW`pRx`|*&@@5zS@-M`Z`d|[&AA5zC@-M`[dx|&XX5zS@-M`xcW`pbx`|&@@5zS@-M`c`d|&AA5zC@-M`edx|y&XX@xDwS@-M`kW`prx`|&@@vDwS@-M`%l`d|ݽ&AAvDwC@-M`]mdx|&XXwDwS@-M`tW`px`|"&@@}DwS@-M`t`d|G&AA}DwC@-M`udx|&XX}DwS@-M`}W`px`|&@@wDwS@-M`+}`d|&AAwDwC@-M`5~dx|h&XXvDwS@-M`W`px`|r&@@@xDwS@-M``d|&AA@xDwC@-M`ed`|&@@MwS@-M``d|c&AAMwC@-M`dx|u&XX5zS@-M`W`px`|&@@5zS@-M`"`d|&AA5zC@-M`}dx|X&XX@5zS@-M`أW`qx`|`&@@5zS@-M``d|&AA5zC@-M`;dx|&XX5zS@-M`?W`qx`|&@@5zS@-M`l`d|&AA5zC@-M`dx|'XX5zS@-M`W`q x`|'@@5zS@-M`)`d|'AA5zC@-M`4dx| 'XX5zS@-M`hW`q0x`| '@@5zS@-M``d|_'AA5zC@-M`߾dx|'XX@KGxS@-M`#W`q@x`|'@@@NGxS@-M``d|'AA@NGxC@-M`edx|'XXLGxS@-M`uW`qP#x`|'@@@GxS@-M``d|6 'AA@GxC@-M`dx|A''XXOGxS@-M`W`q`3x`|L''@@CGxS@-M``d|('AACGxC@-M`dx|/'XXCGxS@-M`{W`qpCx`|0'@@GGxS@-M``d|F1'AAGGxC@-M`dx|8'XXGGxS@-M`0W`qSx`|8'@@CGxS@-M``d| :'AACGxC@-M`dx|A'XXCGxS@-M`W`qcx`|A'@@OGxS@-M`{`d|C'AAOGxC@-M`dx|J'XX@GxS@-M`W`qsx`|J'@@LGxS@-M`9`d|K'AALGxC@-M`Udx|S'XX@NGxS@-M`W`qx`|#S'@@@KGxS@-M``d|T'AA@KGxC@-M`dx|y['XX@KGxS@-M` W`qx`|['@@@NGxS@-M`I `d|\'AA@NGxC@-M`. dx|2d'XX@NGxS@-M`W`qx`|=d'@@@KGxS@-M``d|e'AA@KGxC@-M`dx|l'XX@KGxS@-M`sW`qx`|l'@@@NGxS@-M``d|wn'AA@NGxC@-M`dx|Ru'XX@NGxS@-M`$W`qx`|\u'@@@KGxS@-M`B%`d|v'AA@KGxC@-M`9&dx|!~'XX@KGxS@-M`-W`rx`|/~'@@@NGxS@-M`-`d||'AA@NGxC@-M`.dx|ˆ'XX@NGxS@-M`K6W`rx`|׆'@@@KGxS@-M`6`d|'AA@KGxC@-M`7dx|~'XX@KGxS@-M`>W`r x`|'@@@NGxS@-M`.?`d|ސ'AA@NGxC@-M`^@dx|Q'XX@NGxS@-M`GW`r0x`|a'@@@KGxS@-M`H`d|'AA@KGxC@-M`#Idx|'XXzS@-M`PW`r@x`|'@@zS@-M`P`d|8'AAzC@-M`Qdx|'XXzS@-M`YW`rP$x`|'@@zS@-M`XY`d|'AAzC@-M`oZdx|h'XX@zS@-M`aW`r`4x`|x'@@zS@-M`%b`d|ܳ'AAzC@-M`\cdx|>'XXzS@-M`jW`rpDx`|V'@@@zS@-M`j`d|X'AA@zC@-M`kdx|'XXzS@-M`ysW`rTx`| '@@zS@-M`s`d|g'AAzC@-M`tdx|'XXzS@-M`G|W`rdx`|'@@@zS@-M`~|`d|'AA@zC@-M`b}dx|'XX@zS@-M`+W`rtx`|'@@zS@-``|'@@@zC@->M`]`d|'AAzC@-M``dx|9'XXzS@-M`W`rx`|N'@@zS@-M``d|u'AAzC@-M`dx|'XX@zS@-M`HW`rx`|'@@zS@-M``d|3'AAzC@-M`dx|'XXzS@-M`W`rx`|'@@@zS@-M``d|='AA@zC@-M`dx|I'XX@zS@-M`ɧW`rx`|['@@zS@-M` `d|'AAzC@-M`(dx|5(XXzS@-M`W`rx`|K(@@@zS@-M``d|i(AA@zC@-M`dx|v(XX@zS@-M`W`sx`|(@@zS@-M`M`d|(AAzC@-M`adx| (XXzS@-M`W`sx`| (@@@zS@-M``d|S!(AA@zC@-M`dx|((XX@zS@-M`W`s x`|((@@zS@-M`f`d|)(AAzC@-M`zdx|1(XXzS@-M`W`s0x`|&1(@@@zS@-M``d|}2(AA@zC@-M`dx|9(XX@zS@-M`qW`s@x`|:(@@zS@-M``d|b;(AAzC@-M`dx|B(XXzS@-M`W`sP%x`|B(@@@zS@-M`O`d|C(AA@zC@-M`rdx|XK(XX@KGxS@-M`W`s`5x`|iK(@@@NGxS@-M`)`d|L(AA@NGxC@-M`>dx|T(XX@zS@-M` W`spEx`|T(@@zS@-M` `d|=U(AAzC@-M` dx|\(XXzS@-M`3 W`sUx`|\(@@zS@-M`d `d|](AAzC@-M`i dx|e(XXzS@-M` W`sex`|e(@@zS@-M`a `d|f(AAzC@-M`o dx|n(XXzS@-M` W`sux`|n(@@zS@-M`= `d|o(AAzC@-M`? dx|w(XXzS@-M`& W`sx`|w(@@zS@-M`& `d|-x(AAzC@-M`' d`|(@@zS@-M`n3 `d|ӄ(AAzC@-M`S4 dx| (XXLGxS@-M`; W`sx`|"(@@@GxS@-M`; `d|F(AA@GxC@-M`< dx|(XX@>zS@-M` D W`sx`|(@@@>zS@-zC@->M`CD `d|ʕ(AA@>zC@-M`JE dx|М(XX@>zS@-M`PL W`sx`|ޜ(@@>zS@-zC@->M`~L `d|(AA>zC@-M`M dx|b(XX>zS@-M`T W`tx`|q(@@>zS@-zC@->M`?U `d|(AA>zC@-M`(V dx|(XX>zS@-M`m] W`tx`|(@@>zS@-zC@->M`] `d|8(AA>zC@-M`^ dx|(XX>zS@-M`.g W`t x`|÷(@@>zS@-zC@->M`ig `d| (AA>zC@-M`h dx|(XX@5zS@-M` | W`t0x`|(@@5zS@-M`\| `d|(AA5zC@-M`g} dx|(XX5zS@-M` W`t@x`|"(@@5zS@-M` `d|(AA5zC@-M` dx|(XX5zS@-M`U W`tP&x`|(@@5zS@-M` `d|(AA5zC@-M` dx|{(XXzS@-M` W`t`6x`|(@@zS@-M`T `d|(AAzC@-M`l dx|>(XXzS@-M` W`tpFx`|O(@@zS@-M` `d|(AAzC@-M`! dx| (XX@zS@-M` W`tVx`|$(@@zS@-M` `d|`(AAzC@-M` dx|)XX@GxS@-M` W`tfx`|)@@LGxS@-M`X `d|)AALGxC@-M` dx|` )XXLGxS@-M` W`tvx`| )@@@GxS@-M`$ `d| )AA@GxC@-M`A dx|)XXOGxS@-M` W`tx`|")@@CGxS@-M` `d|)AACGxC@-M`7 d`|)@@CGxS@-M`: `d|$)AACGxC@-M` dx|,)XXCGxS@-M` W`tx`|-,)@@GGxS@-M` `d|-)AAGGxC@-M` dx|4)XXGGxS@-M`0 W`tx`|4)@@CGxS@-M`h `d|6)AACGxC@-M` dx|U=)XXCGxS@-M` W`tx`|c=)@@GGxS@-M` `d|>)AAGGxC@-M`= dx|F)XXGGxS@-M` W`ux`|F)@@CGxS@-M` `d|ZG)AACGxC@-M` d`|N)@@CGxS@-M` `d|T)AACGxC@-M` dx|Z[)XXCGxS@-M` W`u x`|j[)@@GGxS@-M`: `d|\)AAGGxC@-M`/ dx|d)XXGGxS@-M` W`u0x`|*d)@@CGxS@-M` `d|e)AACGxC@-M` dx|l)XXzS@-M`s W`u@x`|m)@@zS@-M` `d|Vn)AAzC@-M` dx|u)XXzS@-M`K% W`uP'x`|u)@@zS@-M`& `d|w)AAzC@-M`' dx|~)XXzS@-M`- W`u`7x`|~)@@@zS@-M`. `d|_)AA@zC@-M`/ dx|7)XXCGxS@-M`6 W`upGx`|J)@@GGxS@-M`6 `d|)AAGGxC@-M`8 dx|)XXGGxS@-M`? W`uWx`|)@@CGxS@-M`? `d|\)AACGxC@-M`@ dx|)XXCGxS@-M` H W`ugx`|)@@GGxS@-M`=H `d|)AAGGxC@-M`qI dx|)XXGGxS@-M`P W`uwx`|6)@@CGxS@-M`P `d|o)AACGxC@-M`Q dx|)XXCGxS@-M``Y W`ux`|)@@GGxS@-M`Y `d|W)AAGGxC@-M`Z dx|)XXGGxS@-M`b W`ux`|)@@CGxS@-M`b `d|)AACGxC@-M`c dx|`)XXCGxS@-M`j W`ux`|q)@@GGxS@-M`k `d|)AAGGxC@-M`3l dx|)XXGGxS@-M`s W`ux`|%)@@CGxS@-M`s `d|K)AACGxC@-M`t dx|)XXCGxS@-M`>| W`ux`|)@@GGxS@-M`q| `d|k)AAGGxC@-M`} dx|)XXGGxS@-M` W`vx`|)@@CGxS@-M`0 `d|)AACGxC@-M`b dx|)XXCGxS@-M` W`vx`|)@@GGxS@-M` `d|O)AAGGxC@-M`ώ dx|)XXGGxS@-M` W`v x`|)@@CGxS@-M`} `d|)AACGxC@-M`i dx|a)XXCGxS@-M` W`v0x`|n)@@GGxS@-M` `d|4)AAGGxC@-M` dx|)XXGGxS@-M`v W`v@x`|)@@CGxS@-M` `d|\)AACGxC@-M`ܨ dx|*XXCGxS@-M`+ W`vP(x`|*@@GGxS@-M`d `d|*AAGGxC@-M` dx|v *XXGGxS@-M` W`v`8x`| *@@CGxS@-M`+ `d| *AACGxC@-M`f dx|1*XXCGxS@-M` W`vpHx`|?*@@GGxS@-M` `d|*AAGGxC@-M` dx|*XXGGxS@-M`] W`vXx`|*@@CGxS@-M` `d|*AACGxC@-M` dx|#*XXCGxS@-M` W`vhx`|#*@@GGxS@-M`8 `d|$*AAGGxC@-M`< dx|+*XXGGxS@-M`| W`vxx`|,*@@CGxS@-M` `d|0-*AACGxC@-M` dx|d4*XXCGxS@-M` W`vx`|t4*@@GGxS@-M`J `d|5*AAGGxC@-M`8 dx|<*XXGGxS@-M`? W`vx`|<*@@CGxS@-M`o `d|=*AACGxC@-M`^ dx|-E*XXCGxS@-M` W`vx`|?E*@@GGxS@-M` `d|HF*AAGGxC@-M` dx|M*XXGGxS@-M` W`vx`|M*@@CGxS@-M`C `d|N*AACGxC@-M`0 dx|U*XXCGxS@-M`p W`vx`|V*@@GGxS@-M` `d|1W*AAGGxC@-M` dx|\^*XXGGxS@-M` W`wx`|q^*@@CGxS@-M` `d|_*AACGxC@-M` dx|f*XXCGxS@-M`F W`wx`|f*@@GGxS@-M`z `d|g*AAGGxC@-M`g dx|Ao*XXovS@-M` W`w x`|To*@@ovS@-M` `d|~p*AAovC@-M` dx|w*XXzS@-M`Z' W`w0 x`|w*@@@zS@-M`' `d|y*AA@zC@-M`( dx|Q*XXovS@-M`/ W`w@x`|`*@@ovS@-M`0 `d|*AAovC@-M`1 dx|*XX@ovS@-M`j8 W`wP)x`|*@@ovS@-M`8 `d|C*AAovC@-M`9 dx|*XXzS@-M`A W`w`9x`|*@@zS@-M`JA `d|*AAzC@-M`B dx|@*XXzS@-M`I W`wpIx`|S*@@@zS@-M`J `d|*AA@zC@-M`K dx|ݢ*XX@zS@-M`]R W`wYx`|*@@zS@-M`R `d|@*AAzC@-M`S dx|*XX@ovS@-M`[ W`wix`|*@@ovS@-M`M[ `d|*AAovC@-M`\ dx|p*XXovS@-M`c W`wyx`||*@@@ovS@-M`Ud `d|*AA@ovC@-M`e dx|P*XXovS@-M`l W`wx`|Y*@@@ovS@-M` m `d|*AA@ovC@-M`2n dx|*XXovS@-M`Tu W`wx`|*@@ovS@-M`u `d|P*AAovC@-M`v dx|H*XXzS@-M`} W`wx`|Y*@@zS@-M`~ `d|*AAzC@-M` dx|-*XX@zS@-M` W`wx`|9*@@zS@-M` `d|*AAzC@-M` dx|*XX@zS@-M`r W`wx`|*@@zS@-M` `d|U*AAzC@-M`ի dx|+XXzS@-M`U W`xx`|+@@zS@-M` `d|O +AAzC@-M`ϸ dx|j+XXzS@-M` W`xx`|s+@@zS@-M`1 `d|+AAzC@-M`F dx|.+XXzS@-M` W`x x`|8+@@zS@-M`u `d|+AAzC@-M`y dx|!+XXzS@-M`` W`x0 x`|!+@@zS@-M` `d|E#+AAzC@-M` dx|*+XXzS@-M`% W`x@x`|*+@@zS@-M`r `d|,+AAzC@-M` dx|]3+XXzS@-M` W`xP*x`|i3+@@zS@-M`, `d|4+AAzC@-M`+ dx|3<+XXzS@-M` W`x`:x`|B<+@@zS@-M` `d||=+AAzC@-M` dx|D+XX@zS@-M`5 W`xpJx`|D+@@zS@-M` `d|-F+AAzC@-M` dx|GM+XX@zS@-M` W`xZx`|UM+@@zS@-M` `d|N+AAzC@-M`, d`|U+@@zS@-M`% `d|_+AAzC@-M`2 dx|/g+XX@zS@-M` W`xzx`|Bg+@@zS@-M` `d|h+AAzC@-M`  dx|o+XXzS@-M`b W`xx`|o+@@zS@-M` `d|yq+AAzC@-M` dx|x+XX@zS@-M`( W`xx`|x+@@zS@-M`W( `d|y+AAzC@-M`A) dx|+XXzS@-M`0 W`xx`|-+@@@zS@-M`0 `d|O+AA@zC@-M`1 dx|Ӊ+XX@zS@-M`S9 W`xx`|+@@zS@-M`9 `d|+AAzC@-M`: dx|(+XXzS@-M`A W`xx`|5+@@@zS@-M`A `d|d+AA@zC@-M`B dx|ǚ+XXovS@-M`GJ W`yx`|֚+@@ovS@-M`yJ `d|+AAovC@-M`K dx|b+XX@zS@-M`R W`yx`|o+@@zS@-M`S `d|+AAzC@-M`T dx|+XXovS@-M`([ W`y x`|+@@ovS@-M`_[ `d|Ĭ+AAovC@-M`D\ dx|+XXzS@-M`c W`y0 x`|+@@@zS@-M`c `d|E+AA@zC@-M`d dx|+XX@zS@-M` l W`y@x`|+@@zS@-M`Bl `d|ڽ+AAzC@-M`Zm dx|$+XXzS@-M`t W`yP+x`|6+@@@zS@-M`t `d|+AA@zC@-M`+v dx|+XX@zS@-M`1} W`y`;x`|+@@zS@-M`} `d| +AAzC@-M`~ dx|m+XXzS@-M` W`ypKx`|z+@@@zS@-M`< `d|+AA@zC@-M`; dx|+XX@zS@-M`x W`y[x`|+@@zS@-M` `d|+AAzC@-M` dx|+XXzS@-M`. W`ykx`|+@@@zS@- ``|+@@zC@->M` `d|+AA@zC@-M` dx|+XX@zS@-M` W`y{x`|+@@zS@-M`f `d|+AAzC@-M` dx|V+XXzS@-M`֨ W`yx`|c+@@@zS@-M`$ `d|+AA@zC@-M`/ dx|,XX@zS@-M`_ W`yx`|,@@zS@-M` `d|<,AAzC@-M` dx| ,XXzS@-M` W`yx`| ,@@@zS@-M`o `d| ,AA@zC@-M` dx|W,XX@zS@-M` W`yx`|f,@@zS@-M` `d|,AAzC@-M` dx|,XXzS@-M` W`yx`|$,@@@zS@-M` `d|w,AA@zC@-M` dx|$,XX@zS@-M`X W`zx`|$,@@zS@-M` `d|C&,AAzC@-M` dx|a-,XXzS@-M` W`zx`|n-,@@@zS@-M`/ `d|.,AA@zC@-M`G dx|'6,XX@zS@-M` W`z x`|46,@@zS@-M` `d|7,AAzC@-M` dx|>,XXzS@-M`f W`z0 x`|>,@@@zS@-M` `d|s@,AA@zC@-M` dx|eG,XX@zS@-M` W`z@x`|rG,@@zS@-M`4 `d|H,AAzC@-M`O dx|%P,XXzS@-M` W`zP,x`|6P,@@@zS@-M` `d|Q,AA@zC@-M` dx|X,XX@zS@-M`a W`z`<x`|X,@@zS@-M` `d|4Z,AAzC@-M` dx|a,XXzS@-M`- W`zpLx`|a,@@@zS@-M`\ `d|b,AA@zC@-M`~ dx|i,XX@zS@-M`V W`z\x`|i,@@zS@-M` `d|Lk,AAzC@-M` dx|r,XXzS@-M`" W`zlx`|r,@@@zS@-M`r" `d|s,AA@zC@-M`j# dx|${,XX@zS@-M`* W`z|x`|1{,@@zS@-M`* `d||,AAzC@-M`, dx|,XXzS@-M`j3 W`zx`|,@@@zS@-M`3 `d|J,AA@zC@-M`4 dx|w,XX@zS@-M`; W`zx`|,@@zS@-M`I< `d|ߍ,AAzC@-M`_= dx|G,XXzS@-M`D W`zx`|T,@@@zS@-M`E `d|,AA@zC@-M`/F dx| ,XX@zS@-M`M W`zx`|,@@zS@-M`M `d|n,AAzC@-M`N dx|,XXzS@-M`V W`zx`|,@@@zS@-M`mV `d|,AA@zC@-M`nW dx|(,XX@zS@-M`^ W`{x`|6,@@zS@-M`^ `d|,AAzC@-M`$` dx|ɷ,XXzS@-M`Ig W`{x`|ط,@@@zS@-M`g `d| ,AA@zC@-M`h dx|,XXovS@-M`p W`{ x`|,@@@ovS@-M`Vp `d|,AA@ovC@-M`gq dx|,XX@ovS@-M`x W`{0 x`|,@@ovS@-M`x `d|<,AAovC@-M`y dx|,XXovS@-M` W`{@x`|,@@ovS@-M`Q `d|,AAovC@-M`e dx|^,XXovS@-M`މ W`{P-x`|l,@@ovS@-M` `d|,AAovC@-M`X dx|%,XXovS@-M` W`{`=x`|3,@@ovS@-M`ђ `d|y,AAovC@-M` dx|,XXovS@-M`. W`{pMx`|,@@ovS@-M`| `d|,AAovC@-M` dx|a,XX@ovS@-M` W`{]x`|m,@@ovS@-M`/ `d|,AAovC@-M`. dx|$,XX@ovS@-M` W`{mx`|1,@@ovS@-M` `d|k,AAovC@-M` dx|-XXovS@-M`d W`{}x`|-@@@ovS@-M`. `d|-AA@ovC@-M` dx|-XXovS@-M`# W`{x`|-@@@ovS@-M`t `d| -AA@ovC@-M` dx|/-XXovS@-M` W`{x`|>-@@ovS@-M` `d|-AAovC@-M` dx|-XXovS@-M`q W`{x`|-@@ovS@-M` `d|>!-AAovC@-M` dx|(-XXovS@-M`8 W`{x`|(-@@ovS@-M`o `d|5*-AAovC@-M` dx|P1-XXovS@-M` W`{x`|]1-@@ovS@-M` `d|2-AAovC@-M`7 dx|9-XXovS@-M`L W`|x`|9-@@ovS@-M` `d|;-AAovC@-M` dx|~B-XXovS@-M` W`|x`|B-@@ovS@-M`2 `d|C-AAovC@-M`a dx|5K-XXovS@-M` W`| x`|BK-@@ovS@-M` `d|lL-AAovC@-M` dx|S-XXovS@-M`rW`|0x`|T-@@ovS@-M``d|qU-AAovC@-M`dx|\-XXovS@-M`< W`|@x`|\-@@ovS@-M`n `d|(^-AAovC@-M` dx|e-XXovS@-M`W`|P.x`|-e-@@ovS@-M``d|?f-AAovC@-M`dx|m-XXovS@-M`UW`|`>x`|m-@@ovS@-M``d|M`.`d|~-AAovC@-M`/dx|-XXovS@-M`7W`|^x`|-@@ovS@-M`P7`d|-AAovC@-M`~8dx|#-XXovS@-M`?W`|nx`|1-@@@ovS@-M`?`d|{-AA@ovC@-M`@dx|-XX@ovS@-M`cHW`|~x`|-@@ovS@-M`H`d|I-AAovC@-M`Idx|-XXovS@-M` QW`|x`|-@@ovS@-M`OQ`d|-AAovC@-M`}Rdx|j-XXovS@-M`YW`|x`|{-@@ovS@-M`3Z`d|-AAovC@-M` [dx|(-XXovS@-M`bW`|x`|6-@@ovS@-M`b`d|}-AAovC@-M`cdx|ٻ-XXovS@-M`YkW`|x`|-@@ovS@-M`k`d|,-AAovC@-M`ldx|-XX@ovS@-M`#tW`|x`|-@@ovS@-M`yt`d|-AAovC@-M`udx|l-XX@ovS@-M`|W`}x`|-@@ovS@-M`9}`d|-AAovC@-M`H~dx|-XXovS@-M`yW`}x`| -@@ovS@-M``d|$-AAovC@-M`dx|S-XX@ovS@-M`ӍW`} x`|`-@@ovS@-M``d|-AAovC@-M``dx|-XX@ovS@-M`W`}0x`|-@@ovS@-M``d|-AAovC@-M`dx|-XXovS@-M`W`}@x`|-@@ovS@-M`D`d|-AAovC@-M`Tdx|T-XXovS@-M`ԧW`}P/x`|c-@@ovS@-M`E`d|-AAovC@-M`>dx| .XXovS@-M`W`}`?x`|.@@ovS@-M``d|:.AAovC@-M`dx| .XXovS@-M`UW`}pOx`| .@@ovS@-M``d|, .AAovC@-M`dx|q.XX@zS@-M`W`}_x`|.@@zS@-M`.`d|.AAzC@-M`Gdx|.XX@zS@-M`W`}ox`|.@@zS@-M``d|J.AAzC@-M`dx|#.XXzS@-M`HW`}x`|#.@@zS@-M``d|%.AAzC@-M`dx|H,.XXzS@-M`W`}x`|Y,.@@zS@-M``d|-.AAzC@-M`4dx|4.XXzS@-M`YW`}x`|4.@@zS@-M``d| 6.AAzC@-M`dx|=.XXzS@-M`W`}x`|=.@@zS@-M`s`d|?.AAzC@-M`dx|lF.XXzS@-M`W`}x`|{F.@@zS@-M`_`d|G.AAzC@-M`Zdx|O.XXzS@-M`W`}x`|6O.@@zS@-M``d|gP.AAzC@-M`dx|W.XXzS@-M`%W`~x`|W.@@zS@-M`S`d|X.AAzC@-M`:dx|C`.XXzS@-M`W`~x`|Q`.@@zS@-M``d|a.AAzC@-M`*dx|i.XXzS@-M`W`~ x`| i.@@zS@-M``d|qj.AAzC@-M`dx|q.XX@ovS@-M`D!W`~0x`|q.@@ovS@-M`!`d|s.AAovC@-M`"dx|Uz.XXovS@-M`)W`~@ x`|bz.@@ovS@-M`'*`d|{.AAovC@-M`+dx|.XXovS@-M`2W`~P0x`|%.@@ovS@-M`2`d|q.AAovC@-M`3dx|.XXovS@-M`A;W`~`@x`|ы.@@ovS@-M`;`d|1.AAovC@-M`<dx|.XXovS@-M`W`~pPx`|.@@ovS@-M`L`d|.AAovC@-M`Tdx|[.XX@ovS@-M` W`~`x`|j.@@ovS@-M` `d|.AAovC@-M` dx|.XX@ovS@-M`FW`~px`|.@@ovS@-M`t`d|".AAovC@-M`bdx|Ʈ.XXovS@-M`W`~x`|Ԯ.@@@ovS@-M`h`d|V.AA@ovC@-M`dx|.XX@ovS@-M`$W`~x`|.@@ovS@-M`$`d|ո.AAovC@-M`&dx|*.XXzS@-M`j-W`~x`|8.@@zS@-M`-`d|N.AAzC@-M`.dx|.XXzS@-M`5W`~x`|.@@@zS@-M`H6`d|.AA@zC@-M`H7dx|{.XX@zS@-M`>W`~x`|.@@zS@-``|.@@@zC@->M` ?`d|.AAzC@-M`@dx|.XXzS@-M`HGW`~x`|.@@@zS@-M`tG`d|.AA@zC@-M`XHdx|.XX@zS@-M` PW`x`|.@@zS@-M`UP`d|.AAzC@-M`EQdx|}.XXzS@-M`XW`x`|.@@@zS@-M`X`d|.AA@zC@-M`Ydx|.XX@zS@-M`@aW` x`|.@@zS@-M`oa`d|(.AAzC@-M`hbdx|.XXzS@-M`iW`0x`|.@@@zS@-M`Bj`d|.AA@zC@-M`Rkdx|t/XX@zS@-M`rW`@!x`|/@@zS@-M`s`d|/AAzC@-M`>tdx|/XXzS@-M`Z{W`P1x`|9/@@@zS@-M`{`d|y/AA@zC@-M`|dx|]/XXovS@-M`W``Ax`|n/@@@ovS@-M``d|/AA@ovC@-M` dx|/XXovS@-M`TW`pQx`|"/@@ovS@-M``d|o /AAovC@-M`dx|'/XXovS@-M`W`ax`|'/@@ovS@-M`C`d|:)/AAovC@-M`zdx|~0/XX@ovS@-M`W`qx`|0/@@ovS@-M``d|1/AAovC@-M`-dx|9/XXovS@-M`OW`x`|9/@@@ovS@-M``d|_:/AA@ovC@-M`dx|A/XXovS@-M`W`x`|A/@@ovS@-M`a`d|B/AAovC@-M`?dx|vJ/XXovS@-M`W`x`|J/@@ovS@-M` `d|K/AAovC@-M`dx|;S/XXovS@-M`{W`x`|JS/@@ovS@-M``d|T/AAovC@-M`dx|[/XXovS@-M`1W`x`|[/@@ovS@-``|L\/@@ovC@->M``d|+]/AAovC@-M`kdx|-d/XXovS@-M`mW`x`|;d/@@@ovS@-M``d|/AA@ovC@-M`dx|؅/XX@zS@-M`W`x`|/@@zS@-M`n`d|./AAzC@-M`ndx|/XXzS@-M`W`x`|/@@@zS@-M`H`d|/AA@zC@-M`<dx|./XX@zS@-M`nW` x`|M``d|V/AAzC@-M`dx|/XXzS@-M`* W`0x`|/@@@zS@-M` `d|/AA@zC@-M`dx|i/XX@zS@-M`W`@"x`|v/@@zS@-M``d|/AAzC@-M`d`|0/@@@ovS@-M`'`d|ߺ/AA@ovC@-M`(dx|7/XXovS@-M`w/W``Bx`|E/@@ovS@-M`/`d|/AAovC@-M`0dx|/XXovS@-M`88W`pRx`|/@@ovS@-M`8`d|L/AAovC@-M`9dx|/XXovS@-M`@W`bx`|/@@ovS@-M` A`d|/AAovC@-M`8Bdx|E/XXovS@-M`IW`rx`|R/@@ovS@-M`I`d|/AAovC@-M`Kdx|/XX@ovS@-M`?RW`x`|/@@ovS@-M`oR`d|D/AAovC@-M`Sdx|/XXovS@-M`ZW`x`|/@@@ovS@-M`[`d|/AA@ovC@-M`\dx|//XXovS@-M`ocW`x`|M`c`d|o/AAovC@-M`ddx|/XXovS@-M`'lW`x`|/@@ovS@-M`al`d|-0AAovC@-M`mmdx|0XX@ovS@-M`tW`x`|0@@ovS@-M`2u`d| 0AAovC@-M`Rvdx|.0XXovS@-M`n}W`x`|>0@@@ovS@-M`}`d|S0AA@ovC@-M`~dx|0XXovS@-M`%W`x`|0@@ovS@-M`X`d|90AAovC@-M`ydx|!0XXovS@-M`ʎW`x`|!0@@ovS@-M``d|"0AAovC@-M`ߏdx|7*0XXovS@-M`wW` x`|D*0@@ovS@-M``d|+0AAovC@-M`dx|20XXovS@-M`-W`0x`|20@@ovS@-M`\`d|40AAovC@-M`Xdx|M;0XX@ovS@-M`W`@#x`|_;0@@ovS@-M``d|<0AAovC@-M`ߩdx|C0XXovS@-M`W`P3x`|C0@@@ovS@-M`)`d|D0AA@ovC@-M`=dx|[L0XXovS@-M`W``Cx`|gL0@@ovS@-M`ӹ`d|M0AAovC@-M`dx|U0XXovS@-M`VW`pSx`|#U0@@ovS@-M``d|JV0AAovC@-M`dx|]0XXovS@-M`W`cx`|]0@@ovS@-M`G`d|^0AAovC@-M`>dx|pf0XXovS@-M`W`sx`|f0@@ovS@-M``d|g0AAovC@-M`dx|$o0XX@ovS@-M`dW`x`|3o0@@ovS@-M``d|?p0AAovC@-M`dx|w0XXovS@-M`W`x`|w0@@@ovS@-M`L`d|%y0AA@ovC@-M`edx|q0XXovS@-M`W`x`|0@@ovS@-M``d|ف0AAovC@-M`dx|$0XXovS@-M`dW`x`|30@@ovS@-M``d|0AAovC@-M`dx|0XX@ovS@-M`W`x`|0@@ovS@-M`(`d|0AAovC@-M`Ddx|o0XXovS@-M`W`x`||0@@@ovS@-M``d|0AA@ovC@-M`dx|#0XXovS@-M`cW`x`|00@@ovS@-M``d|t0AAovC@-M`dx|0XXovS@-M`W`x`|0@@ovS@-M`y`d|&0AAovC@-M`fdx|:0XXovS@-M`z!W` x`|H0@@ovS@-M`!`d|0AAovC@-M`"dx|0XXovS@-M`1*W`0x`|0@@ovS@-M`j*`d|Y0AAovC@-M`+dx|0XX@ovS@-M`2W`@$x`|0@@ovS@-M`#3`d|0AAovC@-M`@4dx|g0XXovS@-M`;W`P4x`|s0@@@ovS@-M`;`d|0AA@ovC@-M`=dx|)0XXovS@-M`iDW``Dx`|60@@ovS@-M`D`d|0AAovC@-M`Edx|0XXovS@-M`LW`pTx`|0@@ovS@-M`#M`d|0AAovC@-M`;Ndx|^0XXovS@-M`UW`dx`|k0@@ovS@-M`U`d|0AAovC@-M`Vdx|0XXovS@-M`\^W`tx`|)0@@ovS@-M`^`d|0AAovC@-M`_dx|0XX@ovS@-M`gW`x`|0@@ovS@-M`gg`d|S0AAovC@-M`hdx|1XXovS@-M`oW`x`|1@@@ovS@-M` p`d|1AA@ovC@-M`1qdx|[ 1XXovS@-M`xW`x`|h 1@@ovS@-M`x`d| 1AAovC@-M`ydx|@!1XXovS@-M`W`x`|P!1@@ovS@-M``d|"1AAovC@-M`Ώdx|)1XX@ovS@-M` W`x`|)1@@ovS@-M`A`d|+1AAovC@-M`@dx|[21XXovS@-M`W`x`|h21@@@ovS@-M`ҟ`d|31AA@ovC@-M`dx|;1XXovS@-M`WW`x`|#;1@@ovS@-M``d|<1AAovC@-M`dx|C1XXovS@-M`W`x`|C1@@ovS@-M`R`d|EE1AAovC@-M`dx|5L1XXovS@-M`uW` x`|BL1@@ovS@-M``d|M1AAovC@-M`Ӻdx|T1XXovS@-M`W`0x`|T1@@ovS@-M`$`d|U1AAovC@-M`>dx|E]1XX@ovS@-M`W`@%x`|Q]1@@ovS@-M``d|^1AAovC@-M`dx|qe1XXovS@-M`W`P5x`|~e1@@@ovS@-M``d|f1AA@ovC@-M` dx|+n1XXovS@-M`kW``Ex`|7n1@@ovS@-M`%`d|p1AAovC@-M`Edx|v1XXovS@-M`4W`pUx`| w1@@ovS@-M`i`d|bx1AAovC@-M`dx|1XXovS@-M`W`ex`|1@@ovS@-M``d|݀1AAovC@-M`dx|e1XXovS@-M`W`ux`|q1@@ovS@-M``d|1AAovC@-M`dx|%1XX@ovS@-M`eW`x`|01@@ovS@-M``d|1AAovC@-M`dx|ۙ1XXovS@-M`W`x`|1@@@ovS@-M`H`d|&1AA@ovC@-M`fdx|t1XXovS@-M`W`x`|1@@ovS@-M``d|ã1AAovC@-M`dx|1XXovS@-M`7W`x`|1@@ovS@-M`s`d|P1AAovC@-M`dx|1XX@ovS@-M` W`x`|dz1@@ovS@-M`E!`d|1AAovC@-M`."dx|E1XXovS@-M`)W`x`|R1@@@ovS@-M`)`d|½1AA@ovC@-M`+dx|1XXovS@-M`62W`x`|1@@ovS@-M`2`d|X1AAovC@-M`3dx|j1XXovS@-M`FW`x`||1@@ovS@-M`F`d|1AAovC@-M`Gdx|1XXovS@-M`OW` x`|1@@ovS@-M`UO`d|!1AAovC@-M`aPd`|,1@@ovS@-M`[``d|>1AAovC@-M`~adx|a1XX@zS@-M`hW`@&x`|p1@@zS@-M`h`d|1AAzC@-M`idx|2XXzS@-M`6qW`P6x`| 2@@zS@-M`wq`d|2AAzC@-M`Trdx|w 2XX@zS@-M`yW``Fx`| 2@@zS@-M`y`d| 2AAzC@-M` {dx|2XXovS@-M`3W`pVx`|2@@@ovS@-M`r`d|J2AA@ovC@-M`dx|2XXzS@-M`W`fx`|2@@zS@-M``d|2AAzC@-M`$dx|$&2XX@zS@-M`dW`vx`|3&2@@zS@-M``d|'2AAzC@-M`͔dx|.2XXzS@-M`W`x`|.2@@@zS@-M`,`d|02AA@zC@-M`Tdx|m72XXzS@-M`W`x`|72@@zS@-M``d|82AAzC@-M`dx|@2XXzS@-M`HW`x`|@2@@zS@-M`z`d|[A2AAzC@-M`dx|H2XXzS@-M`W`x`|H2@@zS@-M`$`d|+J2AAzC@-M`kdx|sQ2XXzS@-M`W`x`|Q2@@zS@-M``d|R2AAzC@-M`ؿdx|Y2XXzS@-M`1W`x`|Y2@@zS@-M`^`d|L[2AAzC@-M`dx|\b2XXzS@-M`W`x`|hb2@@zS@-M``d|c2AAzC@-M`dx| k2XXovS@-M`IW`x`|k2@@@ovS@-M``d|ql2AA@ovC@-M`dx|s2XXzS@-M`W` x`|s2@@zS@-M``d|u2AAzC@-M`^dx|E|2XXzS@-M`W`0x`|U|2@@zS@-M``d|}2AAzC@-M`dx|Ӓ2XXzS@-M`W`@'x`|2@@zS@-M`H`d|a2AAzC@-M`dx|2XXzS@-M`W`P7x`|2@@zS@-M``d|2AAzC@-M`& dx|92XXzS@-M`yW``Gx`|C2@@zS@-M``d|2AAzC@-M`dx|ά2XXzS@-M`W`pWx`|ܬ2@@zS@-M`@`d|52AAzC@-M`udx|2XXzS@-M`"W`gx`|2@@zS@-M`#`d|2AAzC@-M`1$dx|2XXzS@-M`Q+W`wx`|2@@zS@-M`+`d|H2AAzC@-M`,dx|2XXzS@-M`3W`x`|2@@zS@-M`4`d|2AAzC@-M`35dx|#2XXzS@-M`c<W`x`|-2@@zS@-M`<`d|m2AAzC@-M`=dx|2XXzS@-M`EW`x`|2@@zS@-M`E`d|K2AAzC@-M`Fdx|t2XXzS@-M`MW`x`|2@@zS@-M`M`d|2AAzC@-M`Odx|2XXMwS@-M`5VW`x`|2@@MwS@-M`bV`d|K2AAMwC@-M`Wdx|2XXMwS@-M`^W`x`|2@@@MwS@-M` _`d|2AA@MwC@-M`?`dx|2XXMwS@-M`ZgW`x`|%2@@MwS@-M`g`d|t2AAMwC@-M`hdx|w3XXMwS@-M`oW`x`|3@@@MwS@-M`o`d|3AA@MwC@-M`qdx|6 3XX@MwS@-M`vxW` x`|D 3@@@MwS@-M`x`d| 3AA@MwC@-M`ydx|3XXMwS@-M`W`0x`|3@@MwS@-M`Q`d|L3AAMwC@-M`dx|{3XXMwS@-M`W`@(x`|3@@@MwS@-M``d|3AA@MwC@-M` dx|%3XXMwS@-M`YW`P8x`|)%3@@MwS@-M``d|w&3AAMwC@-M`dx|-3XXMwS@-M`W``Hx`|-3@@MwS@-M``d|.3AAMwC@-M` dx|163XXMwS@-M`qW`pXx`|@63@@@MwS@-M``d|73AA@MwC@-M`Ҥdx|>3XXMwS@-M`W`hx`|>3@@MwS@-3@@MwC@->M`(`d|>@3AAMwC@-M`~dx|jG3XXMwS@-M`W`xx`|yG3@@@MwS@-M`ݴ`d|H3AA@MwC@-M`dx|O3XX@MwS@-M`W`x`|O3@@@MwS@-M`5`d|$Q3AA@MwC@-M`ddx|vX3XXMwS@-M`W`x`|X3@@MwS@-M``d|Y3AAMwC@-M`dx|ba3XXMwS@-M`W`x`|va3@@@MwS@-M``d|b3AA@MwC@-M`dx|i3XX@>zS@-M`W`x`|i3@@>zS@-zC@->M`M`d|k3AA>zC@-M`Pdx|r3XX>zS@-M`W`x`|r3@@@>zS@-zC@->M`R`d| t3AA@>zC@-M`Mdx|'{3XX@>zS@-M`gW`x`|5{3@@>zS@-zC@->M``d|}|3AA>zC@-M`dx|Ƀ3XX>zS@-M` W`x`|׃3@@>zS@-zC@->M`c`d|3AA>zC@-M`[dx|g3XX>zS@-M`W`x`|w3@@>zS@-zC@->M``d|ˍ3AA>zC@-M` dx|&3XX>zS@-M`fW`  x`|33@@@>zS@-zC@->M``d|3AA@>zC@-M`dx|33XX@>zS@-M`s W`0x`|A3@@>zS@-zC@->M` `d|3AA>zC@-M` dx|Ӧ3XX>zS@-M`W`@)x`|ߦ3@@@>zS@-zC@->M`^`d|33AA@>zC@-M`sdx|(3XX@>zS@-M`hW`P9x`|83@@>zS@-zC@->M``d|D3AA>zC@-M`dx|3XX>zS@-M`"%W``Ix`|3@@@>zS@-zC@->M`%`d|(3AA@>zC@-M`h&dx|3XXzS@-M`-W`pYx`|3@@zS@-M`&.`d|3AAzC@-M`;/dx|@3XX@zS@-M`6W`ix`|M3@@zS@-M`6`d|3AAzC@-M`7dx|3XXzS@-M`??W`yx`| 3@@zS@-M`?`d|o3AAzC@-M`@dx|3XXovS@-M`GW`x`|3@@ovS@-M`;H`d|3AAovC@-M`TIdx|3XXovS@-M`PW`x`|3@@ovS@-M`5Q`d|3AAovC@-M`GRdx|e3XXovS@-M`YW`x`|r3@@ovS@-M`Z`d|3AAovC@-M`Zdx|3XX@>zS@-M`VbW`x`|$3@@>zS@-zC@->M`b`d|y3AA>zC@-M`cdx|3XX>zS@-M`kW`x`|3@@>zS@-zC@->M`{k`d|G3AA>zC@-M`ldx|4XX>zS@-M`sW`x`|4@@>zS@-zC@->M`t`d|j#4AA>zC@-M`dx|Q(4XX>zS@-M`W`x`|a(4@@@>zS@-zC@->M``d|)4AA@>zC@-M`d`|04@@@>zS@-zS@-M`W`x`|94@@>zC@->M``d|:4AA@>zC@-M`dx|B4XX>zS@-M`دW`  x`|B4@@@>zS@-zC@->M` `d|C4AA@>zC@-M`dx|6K4XX@>zS@-M`vW`0x`|HK4@@>zS@-zC@->M``d|L4AA>zC@-M`ȹdx|S4XX@>zS@-M`W`@*x`|S4@@>zS@-zC@->M`$`d|U4AA>zC@-M`Ydx|z\4XX@>zS@-M`W`P:x`|\4@@>zS@-zC@->M`%`d|]4AA>zC@-M`;dx|e4XX>zS@-M`DW``Jx`|e4@@>zS@-zC@->M``d|Lf4AA>zC@-M`dx|Wn4XX>zS@-M`W`pZx`|dn4@@>zS@-zC@->M``d|o4AA>zC@-M`dx|v4XX>zS@-M`W`jx`|v4@@@>zS@-zC@->M`$`d|w4AA@>zC@-M`<dx|>4XX>zS@-M`~W`zx`|L4@@@>zS@-zC@->M``d|4AA@>zC@-M`dx|4XX>zS@-M`EW`x`|4@@@>zS@-zC@->M``d|x4AA@>zC@-M`dx|ː4XXovS@-M` W`x`|ڐ4@@ovS@-M`\`d|24AAovC@-M`rdx|~4XX@ovS@-M`W`x`|4@@ovS@-M``d|4AAovC@-M`"dx|N4XX@>zS@-M`W`x`|`4@@>zS@-zC@->M``d|4AA>zC@-M`dx|ڪ4XX@>zS@-M`W`x`|4@@>zS@-zC@->M`J`d|?4AA>zC@-M`dx|Y4XX@>zS@-M` W`x`|q4@@>zS@-zC@->M` `d|4AA>zC@-M`!dx|4XX>zS@-M`(W`x`|4@@>zS@-zC@->M`)`d|4AA>zC@-M`)d`|4@@>zS@-M`J`d|4AA>zC@-M`Kdx|4XX@>zS@-M`2SW`  x`|4@@>zS@-zC@->M``S`d|%4AA>zC@-M`eTdx|4XX@>zS@-M`6\W`0x`|4@@>zS@-zC@->M`m\`d|F4AA>zC@-M`]dx|4XX@>zS@-M`dW`@+x`|4@@>zS@-zC@->M`*e`d|4AA>zC@-M`Tfdx|-5XX>zS@-M`mnW`P;x`|N5@@@>zS@-zC@->M`n`d|~5AA@>zC@-M`odx| 5XX@>zS@-M`FwW``Kx`| 5@@>zS@-zC@->M`w`d|t 5AA>zC@-M`xdx|5XX>zS@-M`W`p[x`|5@@@>zS@-zC@->M`V`d|5AA@>zC@-M`<dx|5XX@>zS@-M`]W`kx`|,5@@>zS@-zC@->M``d|5AA>zC@-M`dx|$5XX>zS@-M`'W`{x`|$5@@@>zS@-zC@->M`^`d|Q&5AA@>zC@-M`dx|-5XX@>zS@-M`ȚW`x`|-5@@>zS@-zC@->M``d|.5AA>zC@-M`dx|65XX>zS@-M`@W`x`|65@@@>zS@-zC@->M``d|>75AA@>zC@-M`~dx|>5XX@>zS@-M`W`x`|>5@@>zS@-5@@@>zC@->M`>`d|@5AA>zC@-M`Xdx|G5XX>zS@-M`=W`x`| H5@@@>zS@-zC@->M`q`d|]I5AA@>zC@-M`dx|P5XX@>zS@-M`нW`x`|P5@@>zS@-zC@->M``d|Q5AA>zC@-M`(dx|RY5XX>zS@-M`W`x`|_Y5@@@>zS@-zC@->M``d|Z5AA@>zC@-M`dx|b5XX@>zS@-M` W`x`|b5@@>zS@-zC@->M`n`d|d5AA>zC@-M`Zdx|k5XX>zS@-M`W`x`|k5@@@>zS@-zC@->M`-`d|l5AA@>zC@-M`dx|8t5XX@>zS@-M`xW`  x`|Et5@@>zS@-zC@->M``d|}u5AA>zC@-M`dx||5XX>zS@-M`7W`0x`|}5@@@>zS@-zC@->M``d|I~5AA@>zC@-M`dx|5XX@>zS@-M`W`@,x`|ƅ5@@>zS@-zC@->M```d| 5AA>zC@-M`Idx|;5XX>zS@-M`{W`P<x`|K5@@@>zS@-zC@->M``d|5AA@>zC@-M`dx|5XX@>zS@-M`>W``Lx`| 5@@>zS@-zC@->M``d|^5AA>zC@-M`dx|5XX>zS@-M` W`p\x`|ɟ5@@@>zS@-zC@->M`V `d|"5AA@>zC@-M`bdx|H5XX@>zS@-M`W`lx`|T5@@>zS@-zC@->M``d|ݩ5AA>zC@-M`dx|ɰ5XX>zS@-M` W`|x`|հ5@@@>zS@-zC@->M``d|?5AA@>zC@-M`dx|5XX@>zS@-M`&W`x`|5@@>zS@-zC@->M`)'`d|5AA>zC@-M`*(dx|5XX>zS@-M`/W`x`|5@@@>zS@-zC@->M`x/`d|5AA@>zC@-M`V0dx|5XX@>zS@-M`7W`x`|5@@>zS@-zC@->M`B8`d|5AA>zC@-M`09dx|]5XX>zS@-M`@W`x`|i5@@@>zS@-zC@->M`@`d|5AA@>zC@-M` Bdx|L5XX@>zS@-M`IW`x`|X5@@>zS@-zC@->M`I`d|5AA>zC@-M`Jdx|5XX>zS@-M`?RW`x`|5@@@>zS@-zC@->M`R`d|^5AA@>zC@-M`Sdx||5XX@>zS@-M`ZW`x`|5@@>zS@-zC@->M`[`d|5AA>zC@-M`\dx|6XX>zS@-M`pW`x`|6@@@>zS@-zC@->M`Hq`d|6AA@>zC@-M`>rdx|L 6XX@>zS@-M`yW`  x`|X 6@@>zS@-zC@->M`y`d| 6AA>zC@-M`zdx|6XX>zS@-M`W`0x`|6@@@>zS@-zC@->M`\`d|6AA@>zC@-M`Tdx|6XX@>zS@-M`ȊW`@-x`|6@@>zS@-zC@->M`9`d|6AA>zC@-M`9dx| &6XX>zS@-M``W`P=x`|,&6@@@>zS@-zC@->M``d|^'6AA@>zC@-M`dx|.6XX@>zS@-M`W``Mx`|.6@@>zS@-zC@->M`i`d|06AA>zC@-M`Bdx|r76XX>zS@-M`W`p]x`|~76@@@>zS@-zC@->M``d|86AA@>zC@-M`ץdx|1@6XX@>zS@-M`qW`mx`|=@6@@>zS@-zC@->M`ԭ`d|A6AA>zC@-M`dx|H6XX>zS@-M`,W`}x`|H6@@@>zS@-zC@->M`W`d|=J6AA@>zC@-M`}dx|Q6XX@{DwS@-M`W`x`|Q6@@tDwS@-M`X`d|)S6AAtDwC@-M`idx|RZ6XXyDwS@-M`W`x`|dZ6@@yDwS@-M``d|[6AAyDwC@-M`dx|b6XXqDwS@-M`;W`x`|c6@@@~DwS@-M`}`d|]d6AA@~DwC@-M`dx|k6XX@>zS@-M`W`x`|k6@@>zS@-zC@->M``d|8m6AA>zC@-M`xd`|Zt6@@@xDwS@-M``d|Z~6AA@xDwC@-M`dx|6XXwDwS@-M`W`x`|6@@}DwS@-M`.`d|6AA}DwC@-M`4dx|I6XX}DwS@-M`W`x`|W6@@wDwS@-M``d|6AAwDwC@-M`dx|k6XX@xDwS@-M`W`x`|6@@vDwS@-M``d|ј6AAvDwC@-M`dx|-6XX>zS@-M`m W` x`|F6@@>zS@-zC@->M` `d|c6AA>zC@-M`dx|ͨ6XX@>zS@-M` W`0x`|ݨ6@@>zS@-zC@->M`w`d|36AA>zC@-M`sdx|6XX>zS@-M`W`@.x`|6@@@>zS@-zC@->M`'`d| 6AA@>zC@-M`K dx|6XX@>zS@-M`T'W`P>x`|#6@@>zS@-zC@->M`'`d|C6AA>zC@-M`(dx|6XX>zS@-M`0W``Nx`|6@@@>zS@-zC@->M`I0`d|B6AA@>zC@-M`1dx|56XX>zS@-M`u8W`p^x`|D6@@>zS@-zC@->M`A9`d|6AA>zC@-M`B:dx|6XX@5zS@-M`8AW`nx`| 6@@@5zS@-M`A`d|m6AA@5zC@-M`Bdx|6XX5zS@-M`JW`~x`|6@@5zS@-M`UJ`d|16AA5zC@-M`qKdx|6XX5zS@-M`RW`x`|6@@5zS@-M`S`d|6AA5zC@-M`Sdx|S6XX5zS@-M`[W`x`|`6@@@5zS@-M`[`d|6AA@5zC@-M`\dx|6XX@5zS@-M`VdW`x`|$6@@@5zS@-M`d`d|6AA@5zC@-M`edx|6XX5zS@-M` mW`x`|6@@5zS@-M`wm`d|7AA5zC@-M`7ndx|7XX5zS@-M`uW`x`|7@@5zS@-M`=v`d| 7AA5zC@-M` wdx|7XX5zS@-M`W`x`|7@@5zS@-M`.`d|$7AA5zC@-M`ddx|7XX5zS@-M`ɇW`x`|7@@@5zS@-M``d|7AA@5zC@-M`'dx|J#7XX5zS@-M`W`x`|X#7@@@5zS@-M`ܐ`d|$7AA@5zC@-M`ڑdx|,7XX@5zS@-M`VW` x`|(,7@@@5zS@-M``d|j-7AA@5zC@-M`dx|47XX5zS@-M`W`0x`|47@@@5zS@-M`<`d|67AA@5zC@-M`Gdx|Y=7XXzS@-M`W`@/x`|m=7@@zS@-M`Ϫ`d|>7AAzC@-M`dx|E7XXzS@-M`=W`P?x`| F7@@zS@-M`w`d|zG7AAzC@-M`dx|N7XXzS@-M`ȻW``Ox`|N7@@zS@-M``d|O7AAzC@-M`:dx|W7XXzS@-M`XW`p_x`|%W7@@zS@-M``d|YX7AAzC@-M`dx|_7XX@zS@-M`W`ox`|_7@@zS@-M`3`d|a7AAzC@-M`Fdx|hh7XX@zS@-M`W`x`|th7@@zS@-M``d|%j7AAzC@-M`edx|*q7XXzS@-M`jW`x`|7q7@@@zS@-M``d|r7AA@zC@-M`dx|y7XX5zS@-M`-W`x`|y7@@5zS@-M`f`d|B{7AA5zC@-M`dx|7XXzS@-M`W`x`|7@@@zS@-M``d|7AA@zC@-M`)dx| 7XXzS@-M`KW`x`|7@@zS@-M``d|V7AAzC@-M`dx|ɓ7XXzS@-M`  W`x`|ԓ7@@zS@-M`> `d|7AAzC@-M`R dx|7XXzS@-M` W`x`|7@@zS@-M` `d|7AAzC@-M`7 dx|7XXzS@-M` W`x`|7@@zS@-M` `d|Ͳ7AAzC@-M` dx|7XX@zS@-M`[' W`x`|37@@zS@-M`' `d|i7AAzC@-M`( dx|7XXzS@-M`/ W` x`|7@@zS@-M`0 `d|7AAzC@-M`0 dx|Q7XX@zS@-M`8 W`0 x`|d7@@zS@-M`8 `d|7AAzC@-M`9 dx|7XXzS@-M`4A W`@0x`|7@@@zS@-M`aA `d|(7AA@zC@-M`hB dx|7XXzS@-M`I W`P@x`|7@@zS@-M`J `d|-7AAzC@-M`mK dx|Y7XXzS@-M`R W``Px`|o7@@@zS@-M`R `d|7AA@zC@-M`S dx|%7XX5zS@-M`e[ W`p`x`|47@@5zS@-M`[ `d|[7AA5zC@-M`\ dx|7XX5zS@-M`c W`px`|7@@5zS@-M` d `d|7AA5zC@-M`e dx|U7XX5zS@-M`l W`x`|`7@@5zS@-M`l `d|8AA5zC@-M`m dx|8XX5zS@-M`Hu W`x`|8@@@5zS@-M`u `d|e 8AA@5zC@-M`v dx|8XX5zS@-M`~ W`x`|8@@@5zS@-M`O~ `d|%8AA@5zC@-M`e dx|}8XX@5zS@-M` W`x`|8@@@5zS@-M` `d|8AA@5zC@-M`' dx|8"8XX5zS@-M`x W`x`|C"8@@@5zS@-M`Ə `d|#8AA@5zC@-M`ؐ dx|*8XX5zS@-M`= W`x`|+8@@5zS@-M` `d|^,8AA5zC@-M` dx|38XX5zS@-M` W`x`|38@@5zS@-M`< `d|58AA5zC@-M`X dx|l<8XX5zS@-M` W`x`|z<8@@5zS@-M` `d|=8AA5zC@-M`6 dx|.E8XX5zS@-M`n W`x`|;E8@@5zS@-M` `d|F8AA5zC@-M`ɳ dx|M8XX5zS@-M` W` x`|M8@@@5zS@-M`> `d|O8AA@5zC@-M`S dx|gV8XX5zS@-M` W`0!x`|rV8@@@5zS@-M` `d|W8AA@5zC@-M` dx|!_8XX@5zS@-M`a W`@1x`|,_8@@@5zS@-M` `d|i`8AA@5zC@-M` dx|g8XX5zS@-M`- W`PAx`|g8@@@5zS@-M`c `d|Si8AA@5zC@-M` dx|p8XX5zS@-M` W``Qx`|p8@@5zS@-M`9 `d|q8AA5zC@-M`6 dx|hy8XX5zS@-M` W`pax`|uy8@@5zS@-M`f `d|{8AA5zC@-M`Y dx|38XX5zS@-M`s W`qx`|?8@@5zS@-M` `d|8AA5zC@-M` dx|8XX5zS@-M`5 W`x`|8@@5zS@-M` `d|b8AA5zC@-M` dx|8XX5zS@-M` W`x`|ʓ8@@@5zS@-M`K `d|8AA@5zC@-M`Q dx|F8XX5zS@-M` W`x`|S8@@@5zS@-M` `d|8AA@5zC@-M` d`|8@@@5zS@-M`) `d|#8AA@5zC@-M`c dx|@8XX5zS@-M`# W`x`|O8@@@5zS@-M`# `d|8AA@5zC@-M`$ dx|8XX5zS@-M`C, W`x`|8@@5zS@-M`y, `d|P8AA5zC@-M`- dx|8XX5zS@-M`4 W`x`|8@@5zS@-M`"5 `d|F8AA5zC@-M`Q dx|8XX@zS@-M`V W`x`|8@@zS@-M`V `d|8AAzC@-M`0X dx|G8XXzS@-M`_ W`x`|T8@@@zS@-M`_ `d|8AA@zC@-M`` dx| 8XX@zS@-M`Jh W` x`|8@@zS@-M`h `d|Y8AAzC@-M`i dx|9XXzS@-M`q W`0"x`|9@@@zS@-M`Uq `d|.9AA@zC@-M`nr dx| 9XX5zS@-M`y W`@2x`| 9@@5zS@-M`z `d|9AA5zC@-M`D{ dx|_9XX5zS@-M` W`PBx`|l9@@5zS@-M`ڂ `d|9AA5zC@-M` dx|!9XX5zS@-M`a W``Rx`|39@@5zS@-M` `d|9AA5zC@-M`ˌ dx|&9XX5zS@-M`! W`pbx`|&9@@@5zS@-M`W `d|W(9AA@5zC@-M` dx|n/9XX5zS@-M` W`rx`|{/9@@@5zS@-M` `d|09AA@5zC@-M` dx|79XX@5zS@-M`9 W`x`|89@@@5zS@-M`f `d|99AA@5zC@-M`G dx|@9XX5zS@-M` W`x`|@9@@@5zS@-M`B `d|B9AA@5zC@-M`Y dx|I9XX5zS@-M` W`x`|I9@@5zS@-M` `d| K9AA5zC@-M`M dx|MR9XX@zS@-M` W`x`|]R9@@zS@-M`ɿ `d|S9AAzC@-M` dx|vZ9XXzS@-M` W`x`|Z9@@@zS@-M` `d|[9AA@zC@-M` dx|4c9XX@zS@-M`t W`x`|Ac9@@zS@-M` `d|d9AAzC@-M` dx|k9XXzS@-M` W`x`|k9@@@zS@-M`< `d|l9AA@zC@-M`9 dx|,t9XX@zS@-M`l W`x`|?t9@@zS@-M` `d|u9AAzC@-M` dx||9XXzS@-M` W`x`||9@@@zS@-M` `d|}9AA@zC@-M` dx|9XX5zS@-M`T W` x`|'9@@5zS@-M` `d|Y9AA5zC@-M` dx|Ѝ9XX5zS@-M` W`0#x`|9@@5zS@-M`C `d| 9AA5zC@-M`J dx|q9XX5zS@-M` W`@3x`|9@@5zS@-M` `d|ח9AA5zC@-M` dx|9XX5zS@-M`_ W`PCx`|,9@@5zS@-M` `d|x9AA5zC@-M` dx|Χ9XX5zS@-M` W``Sx`|ݧ9@@5zS@-M`@ `d|R9AA5zC@-M` dx|x9XX5zS@-M` W`pcx`|9@@@5zS@-M` `d|9AA@5zC@-M`! dx|09XX5zS@-M`p& W`sx`|=9@@@5zS@-M`& `d|9AA@5zC@-M`' dx|9XX@5zS@-M`,/ W`x`|9@@@5zS@-M`]/ `d|-9AA@5zC@-M`m0 dx|9XX5zS@-M`7 W`x`|9@@@5zS@-M` 8 `d|9AA@5zC@-M`69 dx|b9XX5zS@-M`@ W`x`|v9@@5zS@-M`@ `d|9AA5zC@-M`A dx| 9XX5zS@-M`LI W`x`|9@@5zS@-M`I `d|Y9AA5zC@-M`J dx|9XX5zS@-M`Q W`x`|9@@5zS@-M`,R `d|9AA5zC@-M`WS dx|b9XX5zS@-M`Z W`x`|l9@@5zS@-M`Z `d|9AA5zC@-M`[ dx|9XX5zS@-M`#c W`x`|9@@5zS@-M`Tc `d|U9AA5zC@-M`d dx|9XX5zS@-M`k W`x`|9@@5zS@-M` l `d|9AA5zC@-M`)m dx|L:XX5zS@-M`t W`x`|V:@@@5zS@-M`t `d|:AA@5zC@-M`u dx|:XX5zS@-M`7} W` x`|:@@@5zS@-M`f} `d|R:AA@5zC@-M`~ dx| ':XX@zS@-M`` W`0$x`|+':@@zS@-M` `d|c(:AAzC@-M` dx|/:XXzS@-M` W`@4x`|/:@@zS@-M`Q `d|"1:AAzC@-M`b dx|8:XX@zS@-M`ɥ W`PDx`|8:@@zS@-M` `d|9:AAzC@-M`. dx|IA:XXzS@-M` W``Tx`|SA:@@@zS@-M` `d|B:AA@zC@-M` dx|J:XX@KGxS@-M`U W`pdx`|#J:@@LGxS@-M` `d|0K:AALGxC@-M`p dx|R:XX@5zS@-M` W`tx`|R:@@@5zS@-M`L `d|4T:AA@5zC@-M`t dx|[:XX5zS@-M` W`x`|[:@@@5zS@-M` `d|]:AA@5zC@-M`Y dx|Xd:XX5zS@-M` W`x`|dd:@@5zS@-M` `d|e:AA5zC@-M` dx|m:XX5zS@-M`W W`x`|$m:@@5zS@-M` `d|cn:AA5zC@-M` dx|u:XX5zS@-M` W`x`|u:@@5zS@-M`@ `d|1w:AA5zC@-M`q dx|Y~:XX5zS@-M` W`x`|h~:@@5zS@-M` `d|:AA5zC@-M` dx|:XX5zS@-M`[ W`x`|*:@@5zS@-M` `d|v:AA5zC@-M` dx|:XX5zS@-M` W`x`|:@@5zS@-M` `d|:AA5zC@-M` dx|F:XX5zS@-M` W`x`|S:@@@5zS@-M` `d|:AA@5zC@-M` dx|:XX5zS@-M`E W`x`|:@@@5zS@-M`| `d|f:AA@5zC@-M` dx|۩:XX@5zS@-M` W` x`|:@@@5zS@-M`Q `d|R:AA@5zC@-M` dx|:XX5zS@-M` W`0%x`|:@@@5zS@-M` `d|:AA@5zC@-M`B! dx|g:XXzS@-M`( W`@5x`|s:@@zS@-M`( `d|ü:AAzC@-M`* dx|:XXzS@-M`P1 W`PEx`|:@@@zS@-M`1 `d|o:AA@zC@-M`2 dx|:XXzS@-M`9 W``Ux`|:@@@zS@-M` : `d|:AA@zC@-M`C; dx|:XX@NGxS@-M`+P W`pex`|:@@OGxS@-M`ZP `d|K:AAOGxC@-M`Q dx|:XXOGxS@-M`X W`ux`|:@@@NGxS@-M`Y `d|:AA@NGxC@-M`OZ dx|.:XXOGxS@-M`na W`x`|>:@@@GxS@-M`a `d|:AA@GxC@-M`b d`|:@@CGxS@-M`ds `d|;AACGxC@-M`^t dx|_;XXGGxS@-M`{ W`x`|o;@@CGxS@-M`{ `d|;AACGxC@-M`| dx|;XXCGxS@-M`ń W`x`|;@@@GxS@-M` `d|;AA@GxC@-M`- dx| ;XXOGxS@-M`ƍ W`x`| ;@@@NGxS@-M` `d|!;AA@NGxC@-M` dx|!);XXLGxS@-M`a W`x`|:);@@@KGxS@-M` `d|v*;AA@KGxC@-M` dx|1;XX@KGxS@-M` W`x`|1;@@LGxS@-M`> `d|[3;AALGxC@-M` dx|:;XX@NGxS@-M`ͧ W`x`|:;@@OGxS@-M` `d|;;AAOGxC@-M`8 dx|"C;XX@GxS@-M`b W`x`|2C;@@CGxS@-M` `d|D;AACGxC@-M`α dx|K;XXCGxS@-M` W` x`|K;@@GGxS@-M`W `d| M;AAGGxC@-M`` dx|a;XXGGxS@-M` W`0&x`|a;@@CGxS@-M` `d|c;AACGxC@-M`E dx|4j;XXCGxS@-M`t W`@6x`|Gj;@@@GxS@-M` `d|k;AA@GxC@-M` dx|r;XX@GxS@-M`" W`PFx`|r;@@CGxS@-M`^ `d|8t;AACGxC@-M`x dx|{;XXCGxS@-M` W``Vx`|{;@@@GxS@-M` `d|};AA@GxC@-M`E dx|e;XX@GxS@-M` W`pfx`|u;@@CGxS@-M` `d|;AACGxC@-M`# dx|5;XX5zS@-M`u W`vx`|H;@@5zS@-M` `d|;AA5zC@-M` dx|;XX5zS@-M`+ W`x`|;@@5zS@-M`} `d|R;AA5zC@-M` dx|;XX5zS@-M` W`x`|;@@5zS@-M`5 `d| ;AA5zC@-M`M d`|9;@@5zS@-M`, `d| ;AA5zC@-M`L dx|];XX5zS@-M`! W`x`|k;@@5zS@-M`! `d|;AA5zC@-M`" dx|;XX5zS@-M`,* W`x`|;@@5zS@-M`}* `d|S;AA5zC@-M`+ dx|;XX5zS@-M`2 W`x`|;@@@5zS@-M`E3 `d|;AA@5zC@-M`\4 dx|x;XX5zS@-M`; W`x`|;@@@5zS@-M` < `d|;AA@5zC@-M`6= dx|&;XX@5zS@-M`fD W`x`|>;@@@5zS@-M`D `d|u;AA@5zC@-M`E dx|;XX5zS@-M`7M W`x`|;@@@5zS@-M`gM `d|a;AA@5zC@-M`N dx|;XX5zS@-M`U W` x`|;@@5zS@-M`EV `d|;AA5zC@-M`VW dx|G;XX5zS@-M`^ W`0'x`|V;@@5zS@-M`^ `d|;AA5zC@-M`` dx|;XX5zS@-M`Ng W`@7x`|;@@5zS@-M`g `d|x;AA5zC@-M`h dx|M`^p `d|.M`x `d| M` `d|M`L `d|M` `d|&M` `d|\/M`R `d|7M`ެ `d|@M` `d|VM` `d|7_M` `d|hM` `d|pM`@ `d|yM` `d|M` `d|M` `d|LM`3`d| M``d|M`>`d|M`"`d| M`*`d|M`3`d|lM`N<`d|<M`E`d|>M`M`d|M`(V`d|M`^`d|tM`g`d|M`o`d|=AA5zC@-M`pdx| =XX5zS@-M`#xW` x`| =@@5zS@-M``x`d|H =AA5zC@-M`ydx|=XX5zS@-M`߀W`0)x`|=@@5zS@-M``d|=AA5zC@-M`Adx|S=XX5zS@-M`W`@9x`|e=@@5zS@-M`ʉ`d|=AA5zC@-M`؊dx|$=XX5zS@-M` W`PIx`|$=@@5zS@-M`U`d|>&=AA5zC@-M`~dx|-=XX5zS@-M`ךW``Yx`|-=@@5zS@-M``d|1/=AA5zC@-M`qdx|P6=XX5zS@-M`W`pix`|\6=@@@5zS@-M`ȣ`d|7=AA@5zC@-M`d`|>=@@5zS@-M``d|_D=AA5zC@-M`dx|K=XX@5zS@-M`W`x`|K=@@5zS@-M`I`d|5M=AA5zC@-M`udx|]T=XX@5zS@-M`W`x`|kT=@@@5zS@-M``d|U=AA@5zC@-M`dx|]=XX5zS@-M`NW`x`|]=@@5zS@-M``d|^=AA5zC@-M`dx|e=XX5zS@-M`W`x`|e=@@5zS@-M`'`d|g=AA5zC@-M`Adx|M``d|o=AACGxC@-M`dx|v=XX@GxS@-M`W`x`|v=@@OGxS@-M`^`d|x=AAOGxC@-M`Vdx|=XX@NGxS@-M`W`x`|=@@LGxS@-M` `d|"=AALGxC@-M`b dx|=XX5zS@-M`W`x`|=@@5zS@-M`K`d| =AA5zC@-M`Jdx|=XX5zS@-M`ZW` x`|*=@@@5zS@-M``d|o=AA@5zC@-M`dx|=XX5zS@-M` W` x`|=@@5zS@-M`X `d|^=AA5zC@-M`!dx|=XX5zS@-M`(W`0*x`|=@@5zS@-M`#)`d|=AA5zC@-M`H*dx|o=XX5zS@-M`1W`@:x`|}=@@5zS@-M`1`d|=AA5zC@-M`3dx|,=XX5zS@-M`l:W`PJx`|<=@@5zS@-M`:`d|=AA5zC@-M`;dx|=XX@5zS@-M`W``Zx`|=@@5zS@-M`'`d|i=AA5zC@-M`idx|G=XX5zS@-M`G W`pjx`|U=@@5zS@-M`u `d|=AA5zC@-M` dx| =XX@KGxS@-M` W`zx`| =@@LGxS@-M`G`d|q=AALGxC@-M`qdx|=XXLGxS@-M`W`x`|=@@@KGxS@-M``d|=AA@KGxC@-M`dx|(=XX@KGxS@-M`(#W`x`|6=@@LGxS@-M`[#`d|{=AALGxC@-M`{$dx|>XXLGxS@-M`+W`x`|>@@@KGxS@-@@LGxC@->M`E,`d|+>AA@KGxC@-M`+-dx| >XX@KGxS@-M`4W`x`| >@@LGxS@-@@@KGxC@->M`4`d| >AALGxC@-M`5dx|e>XX@NGxS@-M`e=W`x`|s>@@OGxS@-@@@NGxC@->M`=`d|>AAOGxC@-M`>dx|>XXzS@-M`FW`x`|+>@@zS@-@@zC@->M`oF`d|>AAzC@-M`Gdx|#>XX@GxS@-M`NW`x`|#>@@CGxS@-@@@GxC@->M`O`d|=%>AACGxC@-M`=Pdx|,>XXCGxS@-M`WW`x`|,>@@@GxS@-@@CGxC@->M`W`d|->AA@GxC@-M`Xdx|,5>XXCGxS@-M`,`W` x`|;5>@@CGxS@-@@CGxC@->M```d|6>AACGxC@-M`adx|=>XXGGxS@-M`hW` x`|=>@@CGxS@-@@GGxC@->M`h`d|?>AACGxC@-M`jdx|?F>XXCGxS@-M`?qW`0+x`|LF>@@@GxS@-@@CGxC@->M`q`d|G>AA@GxC@-M`rdx|N>XXOGxS@-M`yW`@;x`|N>@@@NGxS@-@@OGxC@->M`Xz`d|eP>AA@NGxC@-M`e{dx|W>XXLGxS@-M`W`PKx`|W>@@@KGxS@-@@LGxC@->M``d|Y>AA@KGxC@-M`dx|H`>XX@KGxS@-M`HW``[x`|W`>@@LGxS@-@@@KGxC@->M`z`d|a>AALGxC@-M`dx|h>XXzS@-M`W`pkx`|i>@@zS@-@@zC@->M`H`d|`j>AAzC@-M``dx|q>XXzS@-M`W`{x`|q>@@zS@-@@zC@->M``d|s>AAzC@-M`dx|uz>XXzS@-M`uW`x`|z>@@zS@-@@zC@->M`@`d|G|>AAzC@-M`Gdx|5>XXzS@-M`5W`x`|B>@@zS@-@@zC@->M``d|>AAzC@-M`dx|>XXzS@-M`W`x`|>@@zS@-@@zC@->M``d|>AAzC@-M`dx|U>XXzS@-M`UW`x`|`>@@zS@-@@zC@->M``d|>AAzC@-M`dx|>XX@NGxS@-M`W`x`|>@@OGxS@-@@@NGxC@->M`E`d|Q>AAOGxC@-M`Qdx|>XXOGxS@-M`W`x`|>@@@NGxS@-@@OGxC@->M``d|>AA@NGxC@-M`dx|\>XX@NGxS@-M`\W`x`|h>@@OGxS@-@@@NGxC@->M``d|>AAOGxC@-M`dx|>XXOGxS@-M`W`x`|>@@@NGxS@-@@OGxC@->M`;`d|t>AA@NGxC@-M`tdx|>XX@NGxS@-M`W` x`|>@@OGxS@-@@@NGxC@->M``d|>AAOGxC@-M`dx|T>XXOGxS@-M`TW` x`|_>@@@NGxS@-@@OGxC@->M``d|>AA@NGxC@-M`dx|>XX@NGxS@-M`W`0,x`|>@@OGxS@-@@@NGxC@->M`l`d|a>AAOGxC@-M`adx|>XXOGxS@-M`W`@<x`|>@@@NGxS@-@@OGxC@->M``d|>AA@NGxC@-M`dx|L>XX@NGxS@-M`L W`PLx`|Z>@@OGxS@-@@@NGxC@->M` `d|>AAOGxC@-M`dx|>XXOGxS@-M`W``\x`|>@@@NGxS@-@@OGxC@->M`T`d|o>AA@NGxC@-M`odx|>XX@NGxS@-M`W`plx`|>@@OGxS@-@@@NGxC@->M``d|%>AAOGxC@-M`% dx|>XXzS@-M`'W`|x`|>@@zS@-@@zC@->M`'`d|>AAzC@-M`(dx|2?XXzS@-M`20W`x`|>?@@zS@-0``|?@@zC@->M`0`d|s?AAzC@-M`s1dx| ?XXzS@-M`8W`x`| ?@@zS@-M`69`d|?AAzC@-M`:dx|?XXzS@-M`AW`x`|?@@zS@-M`A`d|?AAzC@-M`Cdx|a?XXOGxS@-M`aJW`x`|o?@@@NGxS@-M`J`d| ?AA@NGxC@-M`Kdx|'?XX@NGxS@-M`RW`x`|'?@@OGxS@-M`0S`d|0)?AAOGxC@-M`0Tdx|0?XXOGxS@-M`[W`x`|0?@@@NGxS@-M`[`d|1?AA@NGxC@-M`\dx|9?XX@NGxS@-M`dW`x`|9?@@OGxS@-M`=d`d|[:?AAOGxC@-M`[edx|A?XXOGxS@-M`lW`x`|A?@@@NGxS@-M`l`d|C?AA@NGxC@-M`ndx|,J?XX@NGxS@-M`,uW` x`|7J?@@OGxS@-M`Xu`d|K?AAOGxC@-M`vdx|R?XXOGxS@-M`}W` x`|R?@@@NGxS@-M`}`d|S?AA@NGxC@-M`~dx|W[?XX@NGxS@-M`WW`0-x`|b[?@@OGxS@-M``d|\?AAOGxC@-M`dx| d?XXOGxS@-M` W`@=x`|d?@@@NGxS@-M`n`d|e?AA@NGxC@-M`dx|l?XX@NGxS@-M`W`PMx`|l?@@OGxS@-M` `d|n?AAOGxC@-M`d`|M``d|{?AAOGxC@-M`dx|C?XXOGxS@-M`CW`pmx`|P?@@@NGxS@-M``d|?AA@NGxC@-M`dx|Ҋ?XX@NGxS@-M`ҵW`}x`|ߊ?@@OGxS@-M`2`d|?AAOGxC@-M`dx|?XXOGxS@-M`W`x`|?@@@NGxS@-M``d|?AA@NGxC@-M`dx|X?XX@NGxS@-M`XW`x`|e?@@OGxS@-M``d|?AAOGxC@-M`dx|?XXOGxS@-M`W`x`|ʤ?@@@NGxS@-M`"`d|?AA@NGxC@-M`dx|s?XX@NGxS@-M`sW`x`|?@@OGxS@-M``d|ڮ?AAOGxC@-M`dx|ϵ?XXOGxS@-M`W`x`|ߵ?@@@NGxS@-M`6`d|?AA@NGxC@-M`dx|?XX@NGxS@-M`W`x`|?@@OGxS@-M``d|?AAOGxC@-M`dx|W?XXOGxS@-M`WW`x`|d?@@@NGxS@-M``d|?AA@NGxC@-M`dx|?XX@NGxS@-M`W`x`|?@@OGxS@-M``d|%?AAOGxC@-M`%dx|?XXOGxS@-M`W`x`|?@@@NGxS@-M``d|?AA@NGxC@-M`dx|e?XX@NGxS@-M`e W` x`|s?@@OGxS@-M` `d|?AAOGxC@-M` dx|?XXOGxS@-M`W`0.x`|?@@@NGxS@-M`d`d|q?AA@NGxC@-M`qdx|z?XX@NGxS@-M`zWdbx`|?@@OGxS@-M``d|%@AAOGxC@-M`Pdx|x5@XXOGxS@-M`x`Wdvx`|5@@@@NGxS@-M```d|6@AA@NGxC@-M`adx|B>@XX@NGxS@-M`BiWd x`|P>@@@OGxS@-@@@@NGxC@->M`ui`d|?@AAOGxC@-M`jdx|F@XXOGxS@-M`qWd0x`|F@@@@NGxS@-M` r`d|JH@AA@NGxC@-M`Jsdx|O@XX@NGxS@-M`zWd@x`|O@@@OGxS@-M`z`d|Q@AAOGxC@-M`|dx|\X@XXOGxS@-M`\WdPx`|hX@@@@NGxS@-M``d|Y@AA@NGxC@-M`dx|`@XX@NGxS@-M`Wd`x`|`@@@OGxS@-M``d|Ib@AAOGxC@-M`Idx|yi@XXOGxS@-M`yWdpx`|i@@@@NGxS@-M`ݔ`d|j@AA@NGxC@-M`ٕdx|1r@XX@NGxS@-M`1Wdx`|>r@@@OGxS@-``|r@@@@NGxC@->M``d|s@AAOGxC@-M`dx|z@XXOGxS@-M`Wdx`|z@@@@NGxS@-M`=`d|.|@AA@NGxC@-M`.dx|@XX@NGxS@-M`Wdx`|ƒ@@@OGxS@-M``d|@AAOGxC@-M`dx|h@XXOGxS@-M`hWdx`|t@@@@NGxS@-M`ɷ`d|ύ@AA@NGxC@-M`ϸdx|,@XX@NGxS@-M`,Wd&x`|8@@@OGxS@-M``d|@AAOGxC@-M`dx|@XXOGxS@-M`Wd6x`|@@@@NGxS@-M``d|@AA@NGxC@-M`dx|@XX@NGxS@-M`WdFx`|@@@OGxS@-M`'`d|.@AAOGxC@-M`.dx|@XXOGxS@-M`WdVx`|@@@@NGxS@-M``d|ȯ@AA@NGxC@-M`dx|0@XX@NGxS@-M`0Wdgx`|>@@@OGxS@-``|]@@@@NGxC@->M`]`d|@AAOGxC@-M`dx|߿@XXOGxS@-M`Wdwx`|@@@@NGxS@-M`@`d|`@AA@NGxC@-M``dx|o@XX@NGxS@-M`oWd x`|{@@@OGxS@-M``d|@AAOGxC@-M`dx|+@XXOGxS@-M`+Wd0x`|7@@@@NGxS@-M``d|@AA@NGxC@-M`dx|@XX@NGxS@-M`Wd@x`|@@@OGxS@-M``d|@AAOGxC@-M`dx|k@XXOGxS@-M`k WdPx`|w@@@@NGxS@-M` `d|@AA@NGxC@-M`dx|9@XX@NGxS@-M`9#Wd`x`|E@@@OGxS@-M`#`d|@AAOGxC@-M`$dx|AXXOGxS@-M`+Wdpx`| A@@@NGxS@-M`_,`d|]AAA@NGxC@-M`]-dx| AXX@NGxS@-M`4Wdx`| A@@OGxS@-M`5`d| AAAOGxC@-M`6dx|gAXXOGxS@-M`g=Wdx`|sA@@@NGxS@-M`=`d|AAA@NGxC@-M`>dx| AXX@NGxS@-M` FWdx`|.A@@OGxS@-M`~F`d|eAAAOGxC@-M`eGdx|#AXXOGxS@-M`NWdx`|#A@@@NGxS@-M`N`d|$AAA@NGxC@-M`Odx|,AXX@NGxS@-M`WWd'x`|',A@@OGxS@-M`W`d|-AAAOGxC@-M`Xdx|4AXXOGxS@-M`_Wd7x`|4A@@@NGxS@-M`_`d|5AAA@NGxC@-M``dx|P=AXX@NGxS@-M`PhWdGx`|i=A@@OGxS@-M`h`d|>AAAOGxC@-M`idx| FAXXOGxS@-M` qWdWx`|FA@@@NGxS@-M`eq`d|VGAAA@NGxC@-M`Vrdx|NAXX@NGxS@-M`yWdhx`|NA@@OGxS@-M`z`d|PAAAOGxC@-M`{dx|JWAXXOGxS@-M`JWdxx`|XWA@@@NGxS@-M``d|XAAA@NGxC@-M`dx|`AXX@NGxS@-M`Wd x`|`A@@OGxS@-M`Z`d|RaAAAOGxC@-M`Rdx|hAXXOGxS@-M`Wd0x`|hA@@@NGxS@-M``d|jAAA@NGxC@-M`dx|PqAXX@NGxS@-M`PWd@x`|iqA@@OGxS@-M``d|lrAAAOGxC@-M`ldx|yAXX@NGxS@-M`ɤWdPx`|yA@@@GxS@-M`.`d|,{AAA@GxC@-M`,dx|AXXCGxS@-M`Wd`x`|A@@CGxS@-M`ܭ`d| AAACGxC@-M` dx|cAXXGGxS@-M`cWdpx`|qA@@CGxS@-M`ƶ`d|AAACGxC@-M`dx|6AXXCGxS@-M`6Wdx`|FA@@@GxS@-M``d|AAA@GxC@-M`dx|AXX@NGxS@-M`Wdx`|A@@OGxS@-M`P`d|AAAOGxC@-M`dx|AXXLGxS@-M`Wdx`|ǥA@@@KGxS@-M``d|AAA@KGxC@-M`dx|4AXX@KGxS@-M`4Wdx`|AA@@LGxS@-M``d|AAALGxC@-M`dx|AXXOGxS@-M`Wd(x`|A@@@NGxS@-M`"`d|AAA@NGxC@-M`dx|wAXX@GxS@-M`wWd8x`|A@@CGxS@-M``d|AAACGxC@-M`dx|>AXXCGxS@-M`>WdHx`|NA@@GGxS@-M`s`d|AAAGGxC@-M`dx|AXXGGxS@-M`WdXx`|A@@CGxS@-M`c `d|lAAACGxC@-M`l d`|A@@ovS@-M``d|AAAovC@-M`dx|FAXXovS@-M`F"Wdyx`|\A@@ovS@-M`"`d|zAAAovC@-M`z#dx|BXX@ovS@-M`+Wd x`|B@@ovS@-M`.+`d|KBAAovC@-M`K,dx|BXXovS@-M`3Wd0x`|B@@@ovS@-M`3`d| BAA@ovC@-M`5dx|[BXXovS@-M`[<Wd@x`|jB@@ovS@-M`<`d|BAAovC@-M`=dx|BXXovS@-M`DWdPx`|B@@ovS@-M`+E`d|@BAAovC@-M`@Fdx|M"BXXovS@-M`MMWd`x`|b"B@@ovS@-M`M`d|#BAAovC@-M`Ndx|%8BXXovS@-M`%cWdpx`|<8B@@@ovS@-M`Tc`d|;9BAA@ovC@-M`;ddx|@BXXovS@-M`kWdx`|@B@@ovS@-M`Sl`d|`BBAAovC@-M``mdx|GIBXXovS@-M`GtWdx`|`IB@@ovS@-M`t`d|JBAAovC@-M`udx|RBXX@ovS@-M`}Wd x`|RB@@ovS@-M`\}`d|SBAAovC@-M`~dx|ZBXXovS@-M`ɅWdx`|ZB@@ovS@-M`$`d|+\BAAovC@-M`+dx|cBXXovS@-M`Wd)x`|cB@@ovS@-M`֎`d|dBAAovC@-M`Ώdx|GlBXXCGxS@-M`GWd9x`|^lB@@@GxS@-M``d|mBAA@GxC@-M`dx|tBXXovS@-M`WdIx`|tB@@ovS@-M``d|uBAAovC@-M`ؠdx|s}BXXovS@-M`sWdYx`|}B@@ovS@-M`ƨ`d|~BAAovC@-M`ةdx|&BXXovS@-M`&Wdjx`|4B@@ovS@-M`v`d|BAAovC@-M`dx|BXXovS@-M`Wdzx`|юB@@ovS@-M``d|܏BAAovC@-M`ܺdx|_BXXovS@-M`_Wd x`|nB@@ovS@-M``d|BAAovC@-M`dx|*BXXovS@-M`*Wd0x`|CB@@ovS@-M`c`d|nBAAovC@-M`ndx|BXXovS@-M`Wd@x`|B@@ovS@-M`;`d|5BAAovC@-M`5dx|BXXMwS@-M`WdPx`|B@@@MwS@-M``d|BAA@MwC@-M`dx|UBXX@MwS@-M`UWd`x`|jB@@MwS@-M``d|лBAAMwC@-M`dx|BXX@MwS@-M`Wdpx`|B@@MwS@-M`1`d|WBAAMwC@-M`Wdx|BXXMwS@-M`Wdx`|B@@MwS@-M` `d|BAAMwC@-M`dx|wBXX@MwS@-M`wWdx`|B@@MwS@-M``d|BAAMwC@-M`dx|BXX@MwS@-M`Wd x`| B@@MwS@-M`O`d|]BAAMwC@-M`] dx|BXX@MwS@-M`Wdx`|B@@MwS@-M``d| BAAMwC@-M` dx|HBXXMwS@-M`HWd*x`|VB@@MwS@-M``d|BAAMwC@-M`dx| BXXMwS@-M` "Wd:x`|B@@@MwS@-M`9"`d|cBAA@MwC@-M`c#dx|BXXMwS@-M`*WdWx`|B@@@MwS@-M`+`d|'CAA@MwC@-M`',dx|CXX@MwS@-M`3WhWW)x`|C@@MwS@-M`3`d|=CAAMwC@-M`hdx|CCXX@MwS@-M`nWhx`|CC@@MwS@-M`n`d|ECAAMwC@-M`pdx|NLCXXMwS@-M`NwWh +x`|\LC@@MwS@-M`w`d|MCAAMwC@-M`xdx|UCXXMwS@-M`Wh0x`|!UC@@MwS@-M`O`d|uVCAAMwC@-M`udx|]CXXMwS@-M`Wh@x`|]C@@MwS@-M`)`d|R_CAAMwC@-M`Rdx|fCXX@MwS@-M`WhPx`|fC@@MwS@-M``d|hCAAMwC@-M`dx|soCXX@MwS@-M`sWh`EUux`|oC@@MwS@-M``d|pCAAMwC@-M`dx|>xCXX@MwS@-M`>Whpx`|KxC@@MwS@-M`}`d|yCAAMwC@-M`dx|"CXXMwS@-M`"Whx`|0C@@MwS@-M`O`d|oCAAMwC@-M`odx|ىCXXMwS@-M`ٴWh x`|C@@MwS@-M``d|#CAAMwC@-M`#dx|CXXMwS@-M`Whx`|C@@MwS@-M`ѽ`d|CAAMwC@-M`dx|XCXX@MwS@-M`XWh*x`|eC@@MwS@-M``d|CAAMwC@-M`dx|CXX@MwS@-M`WhDLx`|&C@@MwS@-M`S`d|}CAAMwC@-M`}dx|CXX@MwS@-M`WhJx`|C@@MwS@-M``d|CAAMwC@-M`dx|vCXXMwS@-M`vWhbx`|C@@MwS@-M``d|CAAMwC@-M`dx|0CXXMwS@-M`0Whjx`|M`[`d|CAAMwC@-M`dx|CXXMwS@-M`Wh{x`|C@@MwS@-M`0`d|YCAAMwC@-M`Ydx|CXX@MwS@-M`Whx`|C@@MwS@-M``d|'CAAMwC@-M`'dx|CXX>zS@-M`Wh DL Lx`|C@@>zS@-zC@->M``d|CAA>zC@-M`dx|mCXX@MwS@-M`mWh0MMEx`|C@@MwS@-M``d|CAAMwC@-M`dx|$CXX>zS@-M`$#Wh@x`|1C@@>zS@-zC@->M`U#`d|CAA>zC@-M`$dx|DXX>zS@-M`+WhPc0x`|D@@>zS@-zC@->M`!,`d|&DAA>zC@-M`&-dx|g DXX>zS@-M`g4Wh`x`|z D@@>zS@-zC@->M`4`d| DAA>zC@-M`5dx|DXX>zS@-M`<Whpx`|D@@>zS@-zC@->M`=`d|EDAA>zC@-M`E>dx|lDXX>zS@-M`lEWhx`|yD@@>zS@-zC@->M`E`d|DAA>zC@-M`Fdx|J#DXX>zS@-M`JNWhRegional=x`|X#D@@>zS@-zC@->M`N`d|$DAA>zC@-M`Odx|+DXX>zS@-M`VWhx`|+D@@>zS@-zC@->M`W`d|,DAA>zC@-M`Wdx|&4DXX@MwS@-M`&_Wh3x`|74D@@MwS@-M`\_`d|~5DAAMwC@-M`~`dx|M`Dh`d|O>DAAMwC@-M`Oidx|EDXXMwS@-M`pWhKx`|ED@@MwS@-M`q`d| GDAAMwC@-M` rd`|IND@@MwS@-M`ǁ`d|WDAAMwC@-M`߂dx|._DXXMwS@-M`.WhLokal_x`|B_D@@@MwS@-M`a`d|a`DAA@MwC@-M`adx|gDXX@MwS@-M`Wh|x`|gD@@MwS@-M``d|iDAAMwC@-M`dx|vpDXX@MwS@-M`vWh x`|pD@@MwS@-M`ϛ`d|qDAAMwC@-M`ٜdx| yDXX@MwS@-M` Wh x`|yD@@@MwS@-M`\`d|`zDAA@MwC@-M``dx|́DXXMwS@-M`ͬWh0x`|݁D@@MwS@-M``d|DAAMwC@-M`dx|\DXXMwS@-M`\Wh@x`|nD@@MwS@-M``d|wDAAMwC@-M`wdx|DXXMwS@-M`WhPBln/Brb.x`|-D@@MwS@-M`u`d|DAAMwC@-M`dx|DXXMwS@-M`Wh`x`|D@@MwS@-M`J`d|qDAAMwC@-M`qdx|DXXMwS@-M`Whp&!3x`|D@@@MwS@-M``d|DAA@MwC@-M`dx|EDXX@MwS@-M`EWhx`|WD@@MwS@-M``d|DAAMwC@-M`dx|õDXX>zS@-M`Wh x`|ֵD@@>zS@-zC@->M``d|DAA>zC@-M`dx|pDXX>zS@-M`pWhx`|D@@@>zS@-zC@->M``d|޿DAA@>zC@-M`dx|DXX@>zS@-M`WhSa/x`|,D@@>zS@-zC@->M`W`d|DAA>zC@-M`dx|DXX>zS@-M`WhThx`|D@@@>zS@-zC@->M``d|ODAA@>zC@-M`Odx|DXX@>zS@-M`WhLx`|D@@>zS@-zC@->M``d|DAA>zC@-M`dx|aDXX>zS@-M`a Wh&)x`|oD@@@>zS@-zC@->M` `d|DAA@>zC@-M` dx|(DXX@>zS@-M`(Whlx`|6D@@>zS@-zC@->M`^`d|DAA>zC@-M`dx|DXX>zS@-M`Wh}x`|D@@@>zS@-zC@->M`b`d|rDAA@>zC@-M`rdx|DXX@MwS@-M`&Whx`|D@@MwS@-M`*'`d|8DAAMwC@-M`8(dx|jEXX@MwS@-M`j/Wh SA/MVx`|yE@@@MwS@-M`/`d|EAA@MwC@-M`0dx| EXXMwS@-M`8Wh0x`| E@@MwS@-M`Q8`d|MEAAMwC@-M`M9dx|EXXMwS@-M`@Wh@& x`|E@@MwS@-M`A`d|4EAAMwC@-M`4Bdx|EXXMwS@-M`IWhPx`|E@@MwS@-M`I`d|EAAMwC@-M`Jdx|'EXXMwS@-M`RWh`x`|#'E@@MwS@-M`cR`d|e(EAAMwC@-M`eSdx|/EXXMwS@-M`ZWhpx`|/E@@@MwS@-M`[`d|0EAA@MwC@-M`[dx| 8EXX@MwS@-M` cWhHessendx`|8E@@MwS@-M`=c`d|9EAAMwC@-M`ddx|@EXX@MwS@-M`kWh x`|@E@@MwS@-M`l`d|+BEAAMwC@-M`+mdx||IEXX@MwS@-M`|tWh&&ix`|IE@@@MwS@-M`t`d|JEAA@MwC@-M`udx|REXXMwS@-M`}Wh-x`|RE@@MwS@-M`U}`d|XSEAAMwC@-M`X~dx|ZEXXMwS@-M`Wh=x`|ZE@@MwS@-M` `d|\EAAMwC@-M`dx|xcEXXMwS@-M`xWhMx`|cE@@MwS@-M`Ď`d|dEAAMwC@-M`Տdx|4lEXXMwS@-M`4WhAll Calx`|@lE@@MwS@-M``d|mEAAMwC@-M`dx|tEXXMwS@-M`Whlx`|tE@@@MwS@-M`D`d|;vEAA@MwC@-M`;dx|q}EXX@MwS@-M`qWhwrx`|~}E@@MwS@-M``d|~EAAMwC@-M`dx|>EXX@MwS@-M`>Whx`|QE@@MwS@-M`o`d|EAAMwC@-M`dx|EXX@MwS@-M`Wh x`|E@@@MwS@-M`(`d|LEAA@MwC@-M`Ldx|EXXMwS@-M`Wh0x`|E@@MwS@-M``d|EAAMwC@-M`dx|EXX@>zS@-M`Wh@Simx`|+E@@>zS@-zC@->M`R`d|_EAA>zC@-M`_dx|EXX>zS@-M`WhPplex TG99x`|E@@@>zS@-zC@->M``d|JEAA@>zC@-M`Jdx|EXX>zS@-M`Wh`x`|E@@@>zS@-zC@->M``d|EAA@>zC@-M`dx|;EXX>zS@-M`;Whpx`|IE@@@>zS@-zC@->M`y`d|EAA@>zC@-M`dx|EXX>zS@-M`Whx`|E@@@>zS@-zC@->M``d|0EAA@>zC@-M`0dx|EXX>zS@-M`Whx`|E@@>zS@-zC@->M``d|EAA>zC@-M`dx|EXX@~DwS@-M` Whx`|E@@qDwS@-M` `d|EAAqDwC@-M`dx|EXXyDwS@-M`WhCl Brandenburghx`|E@@yDwS@-M`^`d|UEAAyDwC@-M`Udx|EXXtDwS@-M`Wh>x`|E@@@{DwS@-M``d| EAA@{DwC@-M` dx|EXX5zS@-M`&Whb x`|E@@5zS@-M`&`d|5EAA5zC@-M`5(dx|`FXX5zS@-M``/Wh^x`|mF@@5zS@-M`/`d|FAA5zC@-M`0dx|# FXX5zS@-M`#8Whnx`|1 F@@5zS@-M`O8`d|MFAA5zC@-M`M9dx|FXX5zS@-M`@Whx`|F@@5zS@-M`@`d|FAA5zC@-M`Adx|FXX@5zS@-M`HWhCl Berlinx`|F@@@5zS@-M`I`d|FAA@5zC@-M`Idx|&FXX@5zS@-M`QWh x`|&F@@@5zS@-M`Q`d|'FAA@5zC@-M`Rdx|y/FXX@5zS@-M`yZWh0b%x`|/F@@5zS@-M`Z`d|0FAA5zC@-M`[dx|8FXX5zS@-M`cWh@x`|-8F@@5zS@-M`Hc`d|69FAA5zC@-M`6ddx|@FXX5zS@-M`kWhPx`|@F@@5zS@-M`l`d|AFAA5zC@-M`ldx|IFXX5zS@-M`tWh`x`|IF@@5zS@-M`t`d|JFAA5zC@-M`udx|\RFXX5zS@-M`\}WhpRefl BrLx`|nRF@@5zS@-M`}`d|SFAA5zC@-M`~dx|[FXX5zS@-M`Whandenburgx`|.[F@@5zS@-M`I`d|6\FAA5zC@-M`6dx|cFXX5zS@-M`Wh@Dx`|cF@@5zS@-M``d|eFAA5zC@-M`dx|lFXX5zS@-M`Whx`|lF@@5zS@-M`&`d|)nFAA5zC@-M`)dx|juFXX@5zS@-M`jWh/x`|wuF@@@5zS@-M``d|vFAA@5zC@-M`d`|~F@@@5zS@-M``d|FAA@5zC@-M`dx|FXX@5zS@-M`WhReflx`|F@@5zS@-M`-`d|.FAA5zC@-M`.dx|FXX5zS@-M`Whl BerlinGx`|ŗF@@5zS@-M``d|FAA5zC@-M`dx|dFXX5zS@-M`dWhox`|lF@@5zS@-M``d|FAA5zC@-M`dx|FXX5zS@-M`Wh@x`|F@@5zS@-M`$`d|*FAA5zC@-M`*dx|FXX5zS@-M`Whx`|F@@5zS@-M``d|FAA5zC@-M`dx|"FXX5zS@-M`"Wh x`|*F@@5zS@-M`G`d|NFAA5zC@-M`Ndx|FXXzS@-M`Wh0x`|F@@zS@-M``d|FAAzC@-M`dx|\FXXzS@-M`\Wh@BM SMSbx`|dF@@zS@-M``d|FAAzC@-M`dx|FXXzS@-M`WhPx`|F@@zS@-M``d|FAAzC@-M` dx|FXX5zS@-M` Wh`&)x`|F@@5zS@-M` `d|FAA5zC@-M` dx| FXX5zS@-M`  Whpx`|F@@5zS@-M`A `d|BFAA5zC@-M`B dx|FXX@5zS@-M` Whx`|F@@@5zS@-M` `d|FAA@5zC@-M` dx|FXXzS@-M`! Whx`|F@@zS@-M`;! `d|YFAAzC@-M`Y" dx|]FXXzS@-M`]) WhBM DAPNETx`|hF@@zS@-M`) `d|FAAzC@-M`* dx|IGXXzS@-M`I1 Wh0x`|RG@@zS@-M`t1 `d|{GAAzC@-M`{2 dx|GXX@zS@-M`9 Wh&)#x`|G@@zS@-M`9 `d|GAAzC@-M`: dx|GXXzS@-M`A WhPx`|G@@@zS@-M`A `d|GAA@zC@-M`B dx|&GXXzS@-M`&J Wh`x`|5G@@zS@-M`SJ `d|Z GAAzC@-M`ZK dx|'GXXzS@-M`R Whpx`|'G@@@zS@-M`R `d|(GAA@zC@-M`S dx|P0GXX@zS@-M`P[ WhBM Parrx`|W0G@@zS@-M`[ `d|1GAAzC@-M`\ dx|+9GXXzS@-M`+d Whottx`|A9G@@zS@-M`]d `d|X:GAAzC@-M`Xe dx| BGXX@zS@-M` m Wh &)x`| BG@@zS@-M`hm `d|gCGAAzC@-M`gn dx|WGXXzS@-M` Wh0x`| XG@@@zS@-M`G `d|RYGAA@zC@-M`R dx|`GXXzS@-M` Wh@x`|`G@@zS@-M` `d| bGAAzC@-M` dx|iGXXzS@-M` WhPx`|iG@@zS@-M`I `d|JkGAAzC@-M`J dx|UrGXXzS@-M`U Wh`BM x`|crG@@zS@-M` `d|sGAAzC@-M` dx|zGXXzS@-M` WhpAPRS'x`|{G@@zS@-M`B `d|_|GAAzC@-M`_ dx|GXXzS@-M` Whx`|˃G@@zS@-M` `d|&GAAzC@-M`& dx|GXXzS@-M` Wh&)x`|G@@zS@-M`ݷ `d|ٍGAAzC@-M`ٸ dx|BGXXzS@-M`B Wh!x`|TG@@@zS@-M` `d|GAA@zC@-M` dx|GXXzS@-M` Wh1x`|G@@zS@-M`Z `d|GAAzC@-M` dx|GXX@zS@-M` WhAx`|G@@zS@-M` `d| GAAzC@-M` dx|GXXzS@-M` WhDG2RON Ronny3x`| G@@zS@-M`` `d|UGAAzC@-M`U dx|ͷGXX@zS@-M` Whax`|޷G@@zS@-M`& `d|4GAAzC@-M`4 dx|GXXzS@-M` WhbF.x`|G@@zS@-M` `d|:GAAzC@-M`: dx|uGXX@zS@-M`u Whx`|G@@zS@-M` `d|GAAzC@-M` dx|;GXXzS@-M`; Whx`|UG@@@zS@-M` `d|GAA@zC@-M` dx|GXXzS@-M` Wh x`|G@@zS@-M`Y `d|qGAAzC@-M`q dx|GXXzS@-M`' Wh0DM9KS SilviAx`|G@@zS@-M`' `d|GAAzC@-M`( dx|HXXovS@-M`= Wh@o1x`|H@@ovS@-M` > `d|HAAovC@-M`? dx|HXXovS@-M`F WhPbEx`|H@@ovS@-M`F `d|HAAovC@-M`G dx|C$HXXovS@-M`CO Wh`x`|P$H@@ovS@-M`O `d|%HAAovC@-M`P dx|,HXXovS@-M`W Whpx`| -H@@ovS@-M`IX `d|a.HAAovC@-M`aY dx|5HXXovS@-M`` Whx`|5H@@ovS@-M`a `d|F7HAAovC@-M`Fb dx|>HXXovS@-M`i WhDM3MAT x`|>H@@ovS@-H@@ovC@->M`i `d|?HAAovC@-M`j dx|>GHXXovS@-M`>r WhHannesx`|KGH@@ovS@-M`r `d|HHAAovC@-M`s dx|OHXXovS@-M`z Whbpx`|OH@@ovS@-M`{ `d|1QHAAovC@-M`1| dx|^XHXXovS@-M`^ WhBx`|kXH@@ovS@-M` `d|YHAAovC@-M` dx|aHXXovS@-M` WhRx`|"aH@@ovS@-M`ӌ `d|bHAAovC@-M`ԍ dx|iHXXovS@-M`̔ Whbx`|iH@@ovS@-M` `d|*kHAAovC@-M`* dx|~rHXXovS@-M`~ WhDL24x`|rH@@ovS@-M` `d|sHAAovC@-M`՞ dx|{HXX@5zS@-M` Wh BQF Thomasx`|"{H@@@5zS@-M`c `d|i|HAA@5zC@-M`i dx|˃HXX@5zS@-M`ˮ Wh x`|؃H@@5zS@-M` `d|HAA5zC@-M` dx|HXX5zS@-M` Wh b7Qx`|H@@5zS@-M`̷ `d|HAA5zC@-M` dx|:HXX5zS@-M`: Wh 0x`|JH@@5zS@-M` `d|HAA5zC@-M` dx|HXX5zS@-M` Wh @x`|H@@5zS@-M`] `d|^HAA5zC@-M`^ dx|NHXX5zS@-M`N Wh Px`|[H@@5zS@-M` `d|HAA5zC@-M` dx|HXX5zS@-M` Wh `DL7JOM Olaf2x`|H@@5zS@-M`R `d|wHAA5zC@-M`w dx|HXX5zS@-M` Wh px`|H@@5zS@-M` `d|HAA5zC@-M` dx|HXXovS@-M` Wh b|x`|H@@ovS@-M`C `d|RHAAovC@-M`R dx|MHXX@ovS@-M`M Wh x`|[H@@ovS@-M` `d|HAAovC@-M` d`|H@@MwS@-M` `d|HAAMwC@-M` dx|*HXXMwS@-M`* Wh 3x`|AH@@MwS@-M`^ `d|HAAMwC@-M` dx|HXXMwS@-M` Wh DG1RPH Petex`|H@@MwS@-M` `d|HAAMwC@-M` dx|FHXXMwS@-M`F Wh rx`|XH@@@MwS@-M` `d|HAA@MwC@-M` dx|HXX@MwS@-M`& Wh b(x`|H@@MwS@-M`#' `d|HAAMwC@-M`( dx|IXX@MwS@-M`/ Wh sx`|I@@MwS@-M`/ `d|IAAMwC@-M`0 dx| IXX@>zS@-M`8 Wh x`|! I@@>zS@-zC@->M`H8 `d|eIAA>zC@-M`e9 dx|}IXX>zS@-M`}@ Wh x`|I@@@>zS@-zC@->M`@ `d|IAA@>zC@-M`A dx|IXX>zS@-M`H Wh DO1RPH rx`|I@@>zS@-zC@->M`1I `d|PIAA>zC@-M`PJ dx|A&IXX@>zS@-M`AQ Wh 0Richardqx`|O&I@@>zS@-zC@->M`rQ `d|'IAA>zC@-M`R dx|.IXX@>zS@-M`Y Wh @cA`x`|.I@@>zS@-zC@->M`Z `d|(0IAA>zC@-M`([ dx|7IXX>zS@-M`b Wh Px`|7I@@@>zS@-zC@->M`b `d|9IAA@>zC@-M`d dx|[@IXX>zS@-M`[k Wh `x`|h@I@@@>zS@-zC@->M`k `d|AIAA@>zC@-M`l dx|IIXXDwS@-M`t Wh px`|1II@@vDwS@-M`it `d|}JIAAvDwC@-M`}u dx|QIXXvDwS@-M`| Wh x`|QI@@DwS@-M`} `d|$SIAADwC@-M`$~ dx|nZIXX@{DwS@-M`n Wx`|}ZI@@tDwS@-M` `d|IAAtDwC@-M` dx|ߌIXX5zS@-M`߷ Wx`|I@@5zS@-M`0 `d|CIAA5zC@-M`C dx|IXX@5zS@-M` W x`|I@@@5zS@-M` `d|!IAA@5zC@-M`! dx|VIXX@5zS@-M`V W0x`|cI@@@5zS@-M` `d|IAA@5zC@-M` dx|IXX@5zS@-M` W@x`|+I@@5zS@-M`s `d|}IAA5zC@-M`} dx|IXX5zS@-M` WPx`| I@@5zS@-M`F `d|nIAA5zC@-M`n dx|IXX5zS@-M` W`x`|˸I@@5zS@-M` `d|IIAA5zC@-M`I dx|IXX5zS@-M` Wpx`|I@@5zS@-M` `d|IAA5zC@-M` dx|UIXX5zS@-M`U Wx`|eI@@5zS@-M` `d|IAA5zC@-M` dx|UIXX5zS@-M`U Wx`|cI@@5zS@-M` `d|IAA5zC@-M` dx|IXX5zS@-M` W x`|,I@@5zS@-M`n `d|IAA5zC@-M` dx|IXX5zS@-M` W0x`|I@@5zS@-M`6 `d|YIAA5zC@-M`Y dx|vIXX@5zS@-M`v W@x`|I@@@5zS@-M` `d|IAA@5zC@-M` dx|PIXX@5zS@-M`P$ WPx`|]I@@@5zS@-M`$ `d|IAA@5zC@-M`% dx|JXX@5zS@-M`, W`x`|J@@5zS@-M`)- `d|8JAA5zC@-M`8. dx|{ JXX5zS@-M`{5 Wpx`| J@@5zS@-M`5 `d| JAA5zC@-M`6 dx|"JXX5zS@-M`"> W4PContact1>x`|0J@@5zS@- ``|lJ@@5zC@->M`l> `d|;JAA5zC@-M`f dx|DJXXzS@-M`o Wx`|DJ@@zS@-M`o `d|EJAAzC@-M`p dx|LJXXzS@-M`w Wx`|LJ@@@zS@-M`x `d|tJAA@zC@-M` dx|^}JXXzS@-M`^ Wx`|n}J@@@zS@-M` `d|~JAA@zC@-M` dx| JXXzS@-M` W x`|J@@zS@-M`_ `d|\JAAzC@-M`\ dx|JXXzS@-M` W0x`|J@@zS@-M` `d|JAAzC@-M` dx|:JXXzS@-M`: W@x`|LJ@@zS@-M` `d|oJAAzC@-M`o dx|AJXXzS@-M`A WPx`|RJ@@zS@-M` `d|JAAzC@-M` dx| JXX@zS@-M` W`x`|J@@zS@-M`c `d|JAAzC@-M` dx|JXX@zS@-M` Wp x`|J@@zS@-M` `d| JAAzC@-M` dx|kJXXzS@-M`k Wx`|zJ@@zS@-M` `d|ʻJAAzC@-M` dx|YJXX@zS@-M`Y W*x`|jJ@@zS@-M` `d|JAAzC@-M` dx|JXXzS@-M` W:x`|J@@zS@-M`I `d|LJAAzC@-M`L dx|JXXzS@-M` WJx`|J@@zS@-M` `d|JAAzC@-M` dx|VJXXzS@-M`V WZx`|dJ@@zS@-M` `d|JAAzC@-M` dx|*JXX@zS@-M`* Wjx`|IJ@@zS@-M`y `d|JAAzC@-M` dx|JXXzS@-M` Wzx`|J@@zS@-M`< `d|.JAAzC@-M`. dx|JXX@zS@-M`" Wx`|J@@zS@-M`j# `d|OJAAzC@-M`O$ dx|WKXX@zS@-M`W+ WDL;x`|gK@@zS@-M`+ `d|KAAzC@-M`, dx| KXXzS@-M`4 Wx`|, K@@zS@-M`t4 `d| KAAzC@-M`5 dx|KXXzS@-M`< Wx`|K@@zS@-M`< `d|KAAzC@-M`E dx|"KXXzS@-M`M Wx`|#K@@zS@-M`1N `d|H$KAAzC@-M`HO dx|+KXX@zS@-M`V W x`|+K@@zS@-M`V `d|-KAAzC@-M`X dx|y4KXX@zS@-M`y_ W0x`|4K@@zS@-M`_ `d|5KAAzC@-M`` dx|_=KXXzS@-M`_h W@x`|l=K@@zS@-M`h `d|>KAAzC@-M`i dx|QFKXX@zS@-M`Qq WPx`|\FK@@zS@-M`q `d|GKAAzC@-M`r dx|NKXXzS@-M`y W`x`|OK@@zS@-M`0z `d|hPKAAzC@-M`h{ dx|WKXXzS@-M` Wp x`|WK@@zS@-M`̂ `d|XKAAzC@-M` dx|`KXXzS@-M` Wx`|'`K@@zS@-M`I `d|}aKAAzC@-M`} dx|hKXX@zS@-M` W,x`|hK@@zS@-M`ד `d|iKAAzC@-M` dx|^qKXXzS@-M`^ W<x`|jqK@@zS@-M` `d|rKAAzC@-M` d`|)zK@@@zS@-M` `d|KAA@zC@-M` dx|KXX@zS@-M` W\x`|K@@zS@-M`ƶ `d|KAAzC@-M` dx|HKXXzS@-M`H Wlx`|YK@@zS@-M` `d|KAAzC@-M` dx|KXXzS@-M` W|x`| K@@zS@-M`3 `d|IKAAzC@-M`I dx|KXXzS@-M` Wx`|˥K@@zS@-M` `d| KAAzC@-M` dx|_KXX@zS@-M`_ WBerlin/Brandx`|sK@@@zS@-M` `d|KAA@zC@-M` dx|KXXzS@-M` Wx`|K@@zS@-M` `d|HKAAzC@-M`H dx|yKXXzS@-M`y Wx`|K@@zS@-M` `d|KAAzC@-M` dx|KXX@zS@-M` Wx`|K@@zS@-K@@@zC@->M`> `d|UKAAzC@-M`U dx|kKXXzS@-M`kW x`|yK@@zS@-M``d|KAAzC@-M`dx|0KXXzS@-M`0 W0x`|CK@@zS@-M`h `d|MKAAzC@-M`Mdx|KXXzS@-M`W@x`|K@@zS@-M``d|KAAzC@-M`dx|KXX@zS@-M`WPx`|K@@zS@-M``d|KAAzC@-M`dx|JKXXzS@-M`J'W`x`|YK@@zS@-M`'`d|KAAzC@-M`(dx|LXXzS@-M`/Wpx`|L@@@zS@-M`0`d|@LAA@zC@-M`@1dx| LXX@zS@-M`8Wx`| L@@zS@-M`8`d|LAAzC@-M`:dx|SLXXzS@-M`SAW.x`|fL@@zS@-M`A`d|LAAzC@-M`Bdx|LXXzS@-M`IW>x`|L@@zS@-M`$J`d|P LAAzC@-M`PKdx|!'LXXzS@-M`!RWNx`|.'L@@zS@-M`YR`d|(LAAzC@-M`Sdx|/LXX@zS@-M`ZW^x`|/L@@@zS@-M`[`d|O1LAA@zC@-M`O\dx|8LXXzS@-M`cWnx`|8L@@zS@-M`c`d|:LAAzC@-M`edx|dALXXzS@-M`dlW~x`|rAL@@zS@-M`l`d|BLAAzC@-M`mdx|JLXX@zS@-M`uWx`|(JL@@zS@-M`Gu`d|LKLAAzC@-M`Lvdx|RLXXzS@-M`}WSa/ThNx`|RL@@zS@-M`~`d|TLAAzC@-M`dx|F[LXXzS@-M`FWx`|S[L@@zS@-M`|`d|\LAAzC@-M`dx| dLXXzS@-M` Wx`|dL@@zS@-M`A`d|]mLAAzC@-M`]dx|ouLXX@zS@-M`oWx`||uL@@zS@-M``d|vLAAzC@-M`ԡdx|0~LXXzS@-M`0W x`|<~L@@zS@-M`j`d||LAAzC@-M`|dx|LXXzS@-M`W0x`|L@@@zS@-M`$`d|>LAA@zC@-M`>dx|LXX@zS@-M`W@x`|L@@zS@-M``d| LAAzC@-M` dx|KLXXzS@-M`KWPx`|aL@@zS@-M``d|LAAzC@-M`dx|ˠLXXzS@-M`W`x`|ؠL@@zS@-M``d|1LAAzC@-M`1dx|LXXzS@-M`Wpx`|L@@zS@-M``d|LAAzC@-M`dx|KLXX@zS@-M`KW x`|WL@@@zS@-M``d|LAA@zC@-M`dx|LXXzS@-M`W0x`|#L@@zS@-M```d|LAAzC@-M`dx|LXXzS@-M`W@x`|L@@zS@-M``d|,LAAzC@-M`,dx|LXX@zS@-M`WPx`|L@@zS@-M``d|LAAzC@-M`dx|LXX@>zS@-M`W`x`|#L@@>zS@-zC@->M`s`d|zLAA>zC@-M`zdx|LXX>zS@-M`Wpx`|L@@@>zS@-zC@->M``d|LAA@>zC@-M` dx|QLXX>zS@-M`QWx`|^L@@>zS@-zC@->M``d|LAA>zC@-M`dx|LXX@>zS@-M`Wx`|L@@>zS@-zC@->M``d|LAA>zC@-M`dx|{LXX@>zS@-M`{"WSA/MVx`|L@@>zS@-zC@->M`"`d|LAA>zC@-M`#dx|MXX>zS@-M`+Wx`|M@@@>zS@-zC@->M`e+`d|dMAA@>zC@-M`d,dx|MXX>zS@-M`3W x`|M@@@>zS@-zC@->M`'4`d|MAA@>zC@-M`<dx|MXXzS@-M`Wx`|(M@@zS@-M`(`d|xMAAzC@-M`8dx|"MXXzS@-M`V W x`|"M@@zS@-M` `d|#MAAzC@-M` dx|Q+MXXzS@-M`W0x`|\+M@@zS@-M`}`d|,MAAzC@-M`kdx|4MXX@zS@-M`W@x`|4M@@zS@-M``d|n5MAAzC@-M`.dx|M`%`d|>MAAzC@-M`&dx|iEMXXzS@-M`).W`x`|uEM@@@zS@-M`.`d|FMAA@zC@-M`/dx|$NMXX@zS@-M`6Wpx`|2NM@@zS@-M`27`d|pOMAAzC@-M`08dx|VMXXzS@-M`?W"x`|VM@@zS@-M`?`d|PXMAAzC@-M`Adx|y_MXXzS@-M`9HW2x`|_M@@zS@-M`H`d|`MAAzC@-M`Idx|5hMXXzS@-M`PWBx`|AhM@@zS@-M`DQ`d|iMAAzC@-M`@Rdx|pMXX@zS@-M`~YWRx`|pM@@@zS@-M`Y`d|rMAA@zC@-M`Zdx|dyMXXzS@-M`$bWbx`|oyM@@zS@-M`mb`d|zMAAzC@-M`cdx|MXXzS@-M`jWrx`|#M@@zS@-M`!k`d|dMAAzC@-M`$ldx|ҊMXX@zS@-M`sWx`|݊M@@zS@-M`s`d|8MAAzC@-M`tdx|MXXzS@-M`V|Wx`|M@@zS@-M`|`d|ܔMAAzC@-M`}dx|/MXXzS@-M`W Hessenx`|>M@@zS@-M`;`d|MAAzC@-M`Hdx|MXXzS@-M`W x`|M@@zS@-M``d|@MAAzC@-M`dx|MXX@zS@-M`MW  x`|M@@zS@-M``d|MAAzC@-M`d`|M@@zS@-M``d|MAAzC@-M`dx|MXXzS@-M`FW x`|M@@@zS@-M`t`d|MAA@zC@-M`dx|MXX@zS@-M`W 0x`|$M@@zS@-M`$`d|yMAAzC@-M`9dx|MXXzS@-M`jW @x`|M@@zS@-M``d|MAAzC@-M`dx|\MXXzS@-M`W Px`|iM@@zS@-M`I`d|MAAzC@-M`vdx|MXXzS@-M`W `x`|M@@zS@-M``d|dMAAzC@-M`$dx|1MXX@zS@-M`W px`|AM@@@zS@-M`"`d|MAA@zC@-M`Ldx|NXXzS@-M`W $x`|N@@zS@-M``d|8NAAzC@-M`dx|] NXXzS@-M`W 4x`|i N@@zS@-M`[`d| NAAzC@-M`dx|NXX@zS@-M`W Dx`|N@@zS@-M``d|]NAAzC@-M`dx|NXXzS@-M`YW Tx`|N@@zS@-M``d|NAAzC@-M`dx|+&NXX>zS@-M`W dx`|?&N@@>zS@-zC@->M``d|\'NAA>zC@-M`dx|.NXX@>zS@-M`fW tx`|.N@@>zS@-zC@->M``d|0NAA>zC@-M`dx|o7NXX>zS@-M`/ W x`|7N@@@>zS@-zC@->M`d `d|8NAA@>zC@-M`|!dx|'@NXX>zS@-M`(W x`|8@N@@>zS@-zC@->M`)`d|ANAA>zC@-M`O*dx|HNXX@>zS@-M`1W Simplexx`|IN@@>zS@-zC@->M`1`d|gJNAA>zC@-M`'3dx|QNXX@>zS@-M`}:W x`|QN@@>zS@-zC@->M`:`d|SNAA>zC@-M`;d`|ZN@@>zS@-zS@-M`\W4ux`|VtN@@@>zC@->M`]`d|NAA>zC@-M`Udx|ݮNXXyDwS@-M`W4#%x`|N@@yDwS@-M`ϗ`d|;NAAyDwC@-M`dx|aNXXqDwS@-M`!W4 3 rx`|qN@@@~DwS@-M`M`d|NAA@~DwC@-M`idx|'NXXvDwS@-M`W40ALx`|4N@@@xDwS@-M`#`d|NAA@xDwC@-M`Hdx|NXX@xDwS@-M`W4@CLML x`|N@@vDwS@-M`ӱ`d|(NAAvDwC@-M`dx|NXXvDwS@-M`cW4PSL,x`|N@@@xDwS@-M``d|NAA@xDwC@-M`ڻdx|XNXXzS@-M`W4` x`|jN@@zS@-M`V`d|NAAzC@-M`xdx|NXXzS@-M`W4p% &SLx`|N@@zS@-M``d|LNAAzC@-M` dx|NXX@xDwS@-M`oW4(SL.SLx`|N@@vDwS@-M``d|NAAvDwC@-M`dx|vNXXvDwS@-M`6W42SL x`|N@@@xDwS@-M`m`d|NAA@xDwC@-M`dx|?NXX@xDwS@-M`W4 $x`|QN@@vDwS@-M`:`d|NAAvDwC@-M`Xdx|OXXvDwS@-M`W4&n(Qx`|O@@@xDwS@-M``d|sOAA@xDwC@-M`3dx|OXX@xDwS@-M`W4P*x`|O@@vDwS@-M``d|@OAAvDwC@-M`dx|OXXvDwS@-M`lW4*, Bx`|O@@@xDwS@-M``d|"OAA@xDwC@-M`d`|n O@@@xDwS@-M`\`d|OAA@xDwC@-M`^d`|VO@@vDwS@-M`Ý`l| hCounters provided by dumpcap| | h`lqdmr-0.12.3/doc/reveng/anytone/d878uv/cpsfileformat.md000066400000000000000000000212001501654372000224730ustar00rootroot00000000000000# AnyTone CPS codeplug file format The fileformat used by the CPS to store the codeplug in binary format is unfortunately not directly related to the binary representation of the codeplug written to the device (in contrast to the fileformat for the GD77, RD5R and MD-UV390). This document tries to collect my knowledge about this file format. ## General layout The geneal layout of the file consists of a header followed by the sequence of elements (usually lists) that form the codeplug. Each list starts with the number of elements in this list. At the end, a footer is added to the file. The order of the elements in the file is important. Due to the variable size of each element, the file-offsets differ from codeplug to codeplug. The order of elements in the file is * Header (fixed size) * Channel list * Radio ID list * Zone list * List of scan lists * List of analog contacts The format of each element is documented below. ## Header ``` 0 8 16 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | CPS version | Content size | Model name | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 | 0 0 0 | HW version | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 30 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 40 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 50 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 60 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 70 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 80 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 90 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ a0 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ b0 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ c0 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ d0 | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ e0 | | +---+---+ ``` | Field | Description | |---|---| | CPS Version | Version number of the CPS as a string E.g., "V1287" | | Content size | Content size in bytes, little-endian. | | Model name | Model name as a string. E.g., "D878UV" | | HW Version | Hardware version as a string. E.g., "V100" | ## Channel list The channel list consists of a simple list of all channels concatenated together preceded by the number of channels encoded as little endian. ``` +---+---+---+---+---+---+ ... + | #Chan | Channel data ... | +---+---+---+---+---+---+ ... + ``` ### Channel encoding Each channel is encoded in a variable size binary blob as ``` 0 8 16 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 00 | ChIdx | RX frequency |RPT| TX offset |TYP|PWR|BWD| 0 |RXO| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 10 |CAC|TKA| RXCTC | | TXCTC | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 20 |ADM| |CLC| |TSL|IGS| |WAL| | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+...+---+ 30 | Channel name N chars ... 0 | +---+---+---+---+---+---+...+---+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | 0 |RNG| ? | 0 0 |APT| 0 |FreqCor| 0 |SMC|ExR| 0 0 0 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | 0 |DAK| 0 0 | +---+---+---+---+ ``` | Field | Description | |---|---| | ChIdx | Specifies the channel index in little endian, 0-based. | | RX frequency | RX frequency in 10Hz, little endian. | | RPT | Repeater type, 0=simplex, 1=positive, 2=negative | | TX offset | TX offset frequency in 10Hz, little endian. Sign determined by RPT field. | | TPY | Channel type, 1=digital, 0=analog, ... | | PWR | Power setting 0=low, 1=mid, 2=high, 3=turbo. | | BWD | Analog band width 0=12.5kHz, 1=25kHz. | | RXO | RX only flag. | | CAC | Call confirm flag. | | TKA | Talkaround flag. | | RXCTC | RX CTCSS/DSC settings. | | TXCTC | RX CTCSS/DSC settings. | | ADM | Admit criterion, 0=always, 1=, 2=, 3=same color code. Also busy lock 1=repeater, 2=busy.| | COC | Color code. | | TSL | Time slot, 0=TS1, 1=TS2 | | IGS | Ignore time slot flag. | | WAL | Work alone flag. | | Channel name | Channel name of max 16 ASCII chars. Variable size (max. 16b?). Zero padded. | | RNG | Ranging flag. | | APT | APRS report type, 0=none, 1=analog, 2=digital. | | FreqCor | Frequency correction in Hz, little endian. | | SMC | SMS confirmation flag. | | ExR | Exclude from roaming flag. | | DAK | Data ACK flag. | ## RadioID list As usual, the radio ID list starts with the number of defined IDs (little endian) and is followed by the actual list of IDs. ``` +---+---+---+---+---+ ... + |#ID| RadioID data ... | +---+---+---+---+---+ ... + ``` ### Radio ID encoding ``` +---+---+---+---+---+---+ ... +---+ |IDX| DMR ID | Name 0 | +---+---+---+---+---+---+ ... +---+ ``` | Field | Description | |---|---| | IDX | Specifies the radio ID index in little endian, 0-based. | | DMR ID | The DMR ID as a 24bit little endian integer. | | Name | Variable length radio ID name. ASCII, 0-terminated. | ## Zone List As usual, the zone starts with the number of defined zones and is followed by the actual list of zones. ``` +---+---+---+---+ ... + |#ZO| Zone data ... | +---+---+---+---+ ... + ``` ### Zone encoding ``` +---+---+---+---+---+---+ ... +---+---+---+---+---+---+---+ ... +---+ |ZIX|NCH| Ch00 | Ch01 | ... | Chn A | Chn B | Zone name ... 0 | +---+---+---+---+---+---+ ... +---+---+---+---+---+---+---+ ... +---+ ``` | Field | Description | |---|---| | ZIX | Zone index. | | NCH | Number of channel in zone. | | ChXX | List of 16bit, little endian, 0-based channel indices | | Chn A | Zone channel A | | Chn B | Zone channel B | | Zone name | Name of the zone, ASCII 0-terminated. | ## Scan lists As usual, the scan lists starts with the number of scan lists and is followed by the actual scan lists. ``` +---+---+---+---+---+ ... + |#SL| Scan list data ... | +---+---+---+---+---+ ... + ``` ### Scan list encoding ``` +---+---+---+ ... +---+---+---+---+---+---+---+---+---+---+---+---+ |SLI| Name ... 0 | 0 |PCS| PC1 | PC2 |RVC|LBA|LBB|DOD|DWT| +---+---+---+ ... +---+---+---+---+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+ ... + |NCH| 0 0 | Ch00 | Ch01 | ... | +---+---+---+---+---+---+---+ ... + ``` | Field | Description | |---|---| | SLI | Scan list index. | | Name | Name of the scan list, max 16 x ASCII, 0-terminated. | | PCS | Priority channel select, 0=Off, 1=PC1, 2=PC2, 3=Both | | PC1 | Priority channel 1, 0=Off, 1=Current, else channel index + 2, little endian. | | PC2 | Priority channel 2, 0=Off, 1=Current, else channel index + 2, little endian. | | RVC | Revert channel, 0=Selected, 1=Selected+Talkback, 2=Last called, 3=Last used. | | LBA | Look back time A in 100ms. | | LBB | Look back time B in 100ms. | | DOD | Dropout delay in 100ms. | | DWT | Dwell time in 100ms. | | NCH | Number of channels in scan list. | ## Analog contacts As usual, the analog contact list starts with the number of contacts and is followed by the actual contact list. ``` +---+---+---+---+---+---+ ... + |#AC| Analog contact data ... | +---+---+---+---+---+---+ ... + ``` ### Analog contact encoding ``` +---+---+---+---+ ... +---+---+ ... +---+ |IDX|NoL| Number ... | Name ... 0 | +---+---+---+---+ ... +---+---+ ... +---+ ``` | Field | Description | |---|---| | IDX | Analog contact index. | | NoL | Number length. | | Name | Name of the contact, max 16 x ASCII, 0-terminated. | qdmr-0.12.3/doc/reveng/anytone/d878uv/d878uv_base.hex000066400000000000000000016665001501654372000221030ustar00rootroot00000000000000----------------------------------------------------------------------------- 00800000 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v.......... 00800010 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 00800020 : 01 00 00 44 42 30 4c 44 53 20 54 53 31 00 00 00 | ...DB0LDS TS1... 00800030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800040 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v.......... 00800050 : cf 09 00 00 07 00 00 00 00 00 03 ff 02 00 00 00 | ................ 00800060 : 01 00 00 53 61 2f 54 68 20 44 42 30 4c 44 53 20 | ...Sa/Th DB0LDS 00800070 : 54 53 31 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS1............. 00800080 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v.......... 00800090 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008000A0 : 01 01 00 54 47 38 20 44 42 30 4c 44 53 20 54 53 | ...TG8 DB0LDS TS 008000B0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 008000C0 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v.......... 008000D0 : cf 09 00 00 05 00 00 00 00 00 03 00 01 00 00 00 | ................ 008000E0 : 01 01 00 54 47 39 20 44 42 30 4c 44 53 20 54 53 | ...TG9 DB0LDS TS 008000F0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 00800100 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v.......... 00800110 : cf 09 00 00 06 00 00 00 00 00 03 00 01 00 00 00 | ................ 00800120 : 01 01 00 42 42 20 44 42 30 4c 44 53 20 54 53 32 | ...BB DB0LDS TS2 00800130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800140 : 43 88 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 00800150 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 00800160 : 01 00 00 44 4d 30 54 5a 4e 20 54 53 31 00 00 00 | ...DM0TZN TS1... 00800170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800180 : 43 88 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 00800190 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008001A0 : 01 01 00 54 47 38 20 44 4d 30 54 5a 4e 20 54 53 | ...TG8 DM0TZN TS 008001B0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 008001C0 : 43 88 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 008001D0 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008001E0 : 01 01 00 54 47 39 20 44 4d 30 54 5a 4e 20 54 53 | ...TG9 DM0TZN TS 008001F0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 00800200 : 43 88 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 00800210 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800220 : 01 01 00 42 42 20 44 4d 30 54 5a 4e 20 54 53 32 | ...BB DM0TZN TS2 00800230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800240 : 43 84 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v.......... 00800250 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 00800260 : 01 00 00 44 42 30 4c 4f 53 20 54 53 31 00 00 00 | ...DB0LOS TS1... 00800270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800280 : 43 84 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v.......... 00800290 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008002A0 : 01 01 00 52 2f 54 47 39 20 44 42 30 4c 4f 53 20 | ...R/TG9 DB0LOS 008002B0 : 54 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS2............. 008002C0 : 43 90 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 008002D0 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008002E0 : 01 00 00 52 2f 54 47 39 20 44 4d 30 54 54 20 54 | ...R/TG9 DM0TT T 008002F0 : 53 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | S1.............. 00800300 : 43 90 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800310 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800320 : 01 01 00 54 47 38 20 44 4d 30 54 54 20 54 53 32 | ...TG8 DM0TT TS2 00800330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800340 : 43 90 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800350 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800360 : 01 01 00 54 47 39 20 44 4d 30 54 54 20 54 53 32 | ...TG9 DM0TT TS2 00800370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800380 : 43 90 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800390 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008003A0 : 01 01 00 42 42 20 44 4d 30 54 54 20 54 53 32 00 | ...BB DM0TT TS2. 008003B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008003C0 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v.......... 008003D0 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008003E0 : 01 00 00 54 47 39 20 44 42 30 4b 4b 20 54 53 31 | ...TG9 DB0KK TS1 008003F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800400 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v.......... 00800410 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800420 : 01 01 00 42 42 20 44 42 30 4b 4b 20 54 53 32 00 | ...BB DB0KK TS2. 00800430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800440 : 43 86 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 00800450 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 00800460 : 01 00 00 44 42 30 4f 55 44 20 54 53 31 00 00 00 | ...DB0OUD TS1... 00800470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800480 : 43 86 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 00800490 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008004A0 : 01 01 00 54 47 38 20 44 42 30 4f 55 44 20 54 53 | ...TG8 DB0OUD TS 008004B0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 008004C0 : 43 86 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 008004D0 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008004E0 : 01 01 00 54 47 39 20 44 42 30 4f 55 44 20 54 53 | ...TG9 DB0OUD TS 008004F0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 00800500 : 43 86 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 00800510 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800520 : 01 01 00 42 42 20 44 42 30 4f 55 44 20 54 53 32 | ...BB DB0OUD TS2 00800530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800540 : 43 95 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v.......... 00800550 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800560 : 01 00 00 54 47 39 20 44 42 30 54 55 20 54 53 31 | ...TG9 DB0TU TS1 00800570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800580 : 43 95 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v.......... 00800590 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008005A0 : 01 01 00 42 42 20 44 42 30 54 55 20 54 53 32 00 | ...BB DB0TU TS2. 008005B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008005C0 : 43 95 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 008005D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 008005E0 : 01 00 00 44 4d 30 4d 4f 54 20 54 53 31 00 00 00 | ...DM0MOT TS1... 008005F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800600 : 43 95 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 00800610 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800620 : 01 01 00 54 47 38 20 44 4d 30 4d 4f 54 20 54 53 | ...TG8 DM0MOT TS 00800630 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 00800640 : 43 95 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 00800650 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800660 : 01 01 00 54 47 39 20 44 4d 30 4d 4f 54 20 54 53 | ...TG9 DM0MOT TS 00800670 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 00800680 : 43 95 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 00800690 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008006A0 : 01 01 00 42 42 20 44 4d 30 4d 4f 54 20 54 53 32 | ...BB DM0MOT TS2 008006B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008006C0 : 43 94 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v.......... 008006D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 008006E0 : 01 00 00 44 42 30 54 41 20 54 53 31 00 00 00 00 | ...DB0TA TS1.... 008006F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800700 : 43 94 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v.......... 00800710 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800720 : 01 01 00 54 47 38 20 44 42 30 54 41 20 54 53 32 | ...TG8 DB0TA TS2 00800730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800740 : 43 94 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v.......... 00800750 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800760 : 01 01 00 54 47 39 20 44 42 30 54 41 20 54 53 32 | ...TG9 DB0TA TS2 00800770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800780 : 43 94 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v.......... 00800790 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008007A0 : 01 01 00 42 42 20 44 42 30 54 41 20 54 53 32 00 | ...BB DB0TA TS2. 008007B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008007C0 : 43 94 50 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.P..v.......... 008007D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 008007E0 : 01 00 00 44 42 30 46 58 20 54 53 31 00 00 00 00 | ...DB0FX TS1.... 008007F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800800 : 43 94 50 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.P..v.......... 00800810 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800820 : 01 01 00 54 47 38 20 44 42 30 46 58 20 54 53 32 | ...TG8 DB0FX TS2 00800830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800840 : 43 94 50 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.P..v.......... 00800850 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800860 : 01 01 00 54 47 39 20 44 42 30 46 58 20 54 53 32 | ...TG9 DB0FX TS2 00800870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800880 : 43 94 50 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.P..v.......... 00800890 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008008A0 : 01 01 00 42 42 20 44 42 30 46 58 20 54 53 32 00 | ...BB DB0FX TS2. 008008B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008008C0 : 43 84 00 00 00 76 00 00 89 00 00 00 00 00 00 00 | C....v.......... 008008D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 008008E0 : 01 00 00 44 42 30 50 44 4d 20 54 53 31 00 00 00 | ...DB0PDM TS1... 008008F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800900 : 43 84 00 00 00 76 00 00 89 00 00 00 00 00 00 00 | C....v.......... 00800910 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800920 : 01 01 00 54 47 38 20 44 42 30 50 44 4d 20 54 53 | ...TG8 DB0PDM TS 00800930 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 00800940 : 43 84 00 00 00 76 00 00 89 00 00 00 00 00 00 00 | C....v.......... 00800950 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800960 : 01 01 00 54 47 39 20 44 42 30 50 44 4d 20 54 53 | ...TG9 DB0PDM TS 00800970 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 00800980 : 43 84 00 00 00 76 00 00 89 00 00 00 00 00 00 00 | C....v.......... 00800990 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................ 008009A0 : 01 01 00 42 42 20 44 42 30 50 44 4d 20 54 53 32 | ...BB DB0PDM TS2 008009B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008009C0 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 008009D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 008009E0 : 01 00 00 44 42 30 42 52 42 20 54 53 31 00 00 00 | ...DB0BRB TS1... 008009F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800A00 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800A10 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800A20 : 01 01 00 54 47 38 20 44 42 30 42 52 42 20 54 53 | ...TG8 DB0BRB TS 00800A30 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 00800A40 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800A50 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800A60 : 01 01 00 54 47 39 20 44 42 30 42 52 42 20 54 53 | ...TG9 DB0BRB TS 00800A70 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 00800A80 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800A90 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800AA0 : 01 01 00 42 42 20 44 42 30 42 52 42 20 54 53 32 | ...BB DB0BRB TS2 00800AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800AC0 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800AD0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 00800AE0 : 01 00 00 44 42 30 53 50 4e 20 54 53 31 00 00 00 | ...DB0SPN TS1... 00800AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800B00 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800B10 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800B20 : 01 00 00 52 2f 54 47 39 20 44 42 30 53 50 4e 20 | ...R/TG9 DB0SPN 00800B30 : 54 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS2............. 00800B40 : 43 95 12 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800B50 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 00800B60 : 01 00 00 44 42 30 4e 4c 53 20 54 53 31 00 00 00 | ...DB0NLS TS1... 00800B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800B80 : 43 95 12 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800B90 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800BA0 : 01 01 00 52 2f 54 47 39 20 44 42 30 4e 4c 53 20 | ...R/TG9 DB0NLS 00800BB0 : 54 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS2............. 00800BC0 : 43 82 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 00800BD0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 00800BE0 : 01 00 00 44 42 30 4c 53 20 54 53 31 00 00 00 00 | ...DB0LS TS1.... 00800BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800C00 : 43 82 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 00800C10 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800C20 : 01 01 00 54 47 38 20 44 42 30 4c 53 20 54 53 32 | ...TG8 DB0LS TS2 00800C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800C40 : 43 82 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 00800C50 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800C60 : 01 01 00 54 47 39 20 44 42 30 4c 53 20 54 53 32 | ...TG9 DB0LS TS2 00800C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800C80 : 43 82 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v.......... 00800C90 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800CA0 : 01 01 00 42 42 20 44 42 30 4c 53 20 54 53 32 00 | ...BB DB0LS TS2. 00800CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800CC0 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800CD0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 00800CE0 : 01 00 00 44 4d 30 4c 43 20 54 53 31 00 00 00 00 | ...DM0LC TS1.... 00800CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800D00 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800D10 : cf 09 00 00 04 00 00 00 00 00 03 ff 02 00 00 00 | ................ 00800D20 : 01 01 00 54 47 38 20 44 4d 30 4c 43 20 54 53 32 | ...TG8 DM0LC TS2 00800D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800D40 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800D50 : cf 09 00 00 05 00 00 00 00 00 03 ff 02 00 00 00 | ................ 00800D60 : 01 01 00 54 47 39 20 44 4d 30 4c 43 20 54 53 32 | ...TG9 DM0LC TS2 00800D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800D80 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800D90 : cf 09 00 00 07 00 00 00 00 00 03 ff 02 00 00 00 | ................ 00800DA0 : 01 01 00 53 61 2f 54 68 20 44 4d 30 4c 43 20 54 | ...Sa/Th DM0LC T 00800DB0 : 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | S2.............. 00800DC0 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v.......... 00800DD0 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800DE0 : 01 00 00 42 42 20 44 4d 30 4c 43 20 54 53 31 00 | ...BB DM0LC TS1. 00800DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800E00 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v.......... 00800E10 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 00800E20 : 01 00 00 44 42 30 46 4c 57 20 54 53 31 00 00 00 | ...DB0FLW TS1... 00800E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800E40 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v.......... 00800E50 : cf 09 00 00 04 00 00 00 00 00 03 ff 02 00 00 00 | ................ 00800E60 : 01 01 00 54 47 38 20 44 42 30 46 4c 57 20 54 53 | ...TG8 DB0FLW TS 00800E70 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 00800E80 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v.......... 00800E90 : cf 09 00 00 05 00 00 00 00 00 03 ff 02 00 00 00 | ................ 00800EA0 : 01 01 00 54 47 39 20 44 42 30 46 4c 57 20 54 53 | ...TG9 DB0FLW TS 00800EB0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 00800EC0 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v.......... 00800ED0 : cf 09 00 00 07 00 00 00 00 00 03 ff 02 00 00 00 | ................ 00800EE0 : 01 01 00 53 61 2f 54 68 20 44 42 30 46 4c 57 20 | ...Sa/Th DB0FLW 00800EF0 : 54 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS2............. 00800F00 : 43 95 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v.......... 00800F10 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................ 00800F20 : 01 00 00 44 42 30 4c 45 20 54 53 31 00 00 00 00 | ...DB0LE TS1.... 00800F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800F40 : 43 95 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v.......... 00800F50 : cf 09 00 00 05 00 00 00 00 00 03 ff 02 00 00 00 | ................ 00800F60 : 01 01 00 52 2f 54 47 39 20 44 42 30 4c 45 20 54 | ...R/TG9 DB0LE T 00800F70 : 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | S2.............. 00800F80 : 43 99 75 00 00 94 00 00 89 00 00 00 00 00 00 00 | C.u............. 00800F90 : cf 09 00 00 02 00 00 00 00 00 03 ff 00 00 00 00 | ................ 00800FA0 : 01 00 00 44 4c 20 44 42 30 41 46 5a 20 54 53 31 | ...DL DB0AFZ TS1 00800FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00800FC0 : 43 99 75 00 00 94 00 00 89 00 00 00 00 00 00 00 | C.u............. 00800FD0 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................ 00800FE0 : 01 00 00 42 42 20 44 42 30 41 46 5a 20 54 53 31 | ...BB DB0AFZ TS1 00800FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801000 : 43 99 75 00 00 94 00 00 89 00 00 00 00 00 00 00 | C.u............. 00801010 : cf 09 00 00 05 00 00 00 00 00 03 ff 04 00 00 00 | ................ 00801020 : 01 01 00 54 47 39 20 44 42 30 41 46 5a 20 54 53 | ...TG9 DB0AFZ TS 00801030 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 00801040 : 43 99 75 00 00 94 00 00 89 00 00 00 00 00 00 00 | C.u............. 00801050 : cf 09 00 00 09 00 00 00 00 00 03 ff 04 00 00 00 | ................ 00801060 : 01 01 00 48 65 73 20 44 42 30 41 46 5a 20 54 53 | ...Hes DB0AFZ TS 00801070 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2............... 00801080 : 43 34 50 00 00 00 00 00 09 00 00 00 00 00 00 00 | C4P............. 00801090 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................ 008010A0 : 01 00 00 44 4d 52 20 53 30 00 00 00 00 00 00 00 | ...DMR S0....... 008010B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008010C0 : 43 36 12 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6.............. 008010D0 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................ 008010E0 : 01 00 00 44 4d 52 20 53 31 00 00 00 00 00 00 00 | ...DMR S1....... 008010F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801100 : 43 36 25 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6%............. 00801110 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................ 00801120 : 01 00 00 44 4d 52 20 53 32 00 00 00 00 00 00 00 | ...DMR S2....... 00801130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801140 : 43 36 38 00 00 00 00 00 09 00 00 00 00 00 00 00 | C68............. 00801150 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................ 00801160 : 01 00 00 44 4d 52 20 53 33 00 00 00 00 00 00 00 | ...DMR S3....... 00801170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801180 : 43 36 50 00 00 00 00 00 89 00 00 00 00 00 00 00 | C6P............. 00801190 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................ 008011A0 : 01 00 00 44 4d 52 20 53 34 00 00 00 00 00 00 00 | ...DMR S4....... 008011B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008011C0 : 43 36 63 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6c............. 008011D0 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................ 008011E0 : 01 00 00 44 4d 52 20 53 35 00 00 00 00 00 00 00 | ...DMR S5....... 008011F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801200 : 43 36 75 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6u............. 00801210 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................ 00801220 : 01 00 00 44 4d 52 20 53 36 00 00 00 00 00 00 00 | ...DMR S6....... 00801230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801240 : 43 36 88 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6.............. 00801250 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................ 00801260 : 01 00 00 44 4d 52 20 53 37 00 00 00 00 00 00 00 | ...DMR S7....... 00801270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801280 : 43 95 62 50 00 76 00 00 88 05 01 01 00 00 00 00 | C.bP.v.......... 00801290 : cf 09 00 00 00 00 00 00 00 10 00 ff ff 00 00 00 | ................ 008012A0 : 00 00 00 44 42 30 4c 44 53 00 00 00 00 00 00 00 | ...DB0LDS....... 008012B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008012C0 : 43 93 00 00 00 76 00 00 88 00 00 00 00 00 00 00 | C....v.......... 008012D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 008012E0 : 00 00 00 44 42 30 52 41 47 00 00 00 00 00 00 00 | ...DB0RAG....... 008012F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801300 : 43 85 75 00 00 76 00 00 88 05 01 01 00 00 00 00 | C.u..v.......... 00801310 : cf 09 00 00 00 00 00 00 00 10 00 ff ff 00 00 00 | ................ 00801320 : 00 00 00 44 42 30 4c 55 44 00 00 00 00 00 00 00 | ...DB0LUD....... 00801330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801340 : 14 56 00 00 00 06 00 00 88 00 00 00 00 00 00 00 | .V.............. 00801350 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801360 : 00 00 00 44 42 30 53 50 2d 32 00 00 00 00 00 00 | ...DB0SP-2...... 00801370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801380 : 43 94 25 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.%..v.......... 00801390 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 008013A0 : 00 00 00 44 42 30 53 50 2d 37 30 00 00 00 00 00 | ...DB0SP-70..... 008013B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008013C0 : 14 56 62 50 00 06 00 00 88 00 00 00 00 00 00 00 | .VbP............ 008013D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 008013E0 : 00 00 00 44 42 30 5a 4f 44 2d 32 00 00 00 00 00 | ...DB0ZOD-2..... 008013F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801400 : 43 87 25 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.%..v.......... 00801410 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801420 : 00 00 00 44 42 30 5a 4f 44 2d 37 30 00 00 00 00 | ...DB0ZOD-70.... 00801430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801440 : 14 57 25 00 00 06 00 00 88 00 00 00 00 00 00 00 | .W%............. 00801450 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801460 : 00 00 00 44 42 30 42 52 4c 00 00 00 00 00 00 00 | ...DB0BRL....... 00801470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801480 : 43 92 75 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.u..v.......... 00801490 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 008014A0 : 00 00 00 44 42 30 42 4c 4f 00 00 00 00 00 00 00 | ...DB0BLO....... 008014B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008014C0 : 43 90 50 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.P..v.......... 008014D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 008014E0 : 00 00 00 44 42 30 53 58 00 00 00 00 00 00 00 00 | ...DB0SX........ 008014F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801500 : 43 91 25 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.%..v.......... 00801510 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801520 : 00 00 00 44 42 30 54 41 00 00 00 00 00 00 00 00 | ...DB0TA........ 00801530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801540 : 14 56 75 00 00 06 00 00 88 00 00 00 00 00 00 00 | .Vu............. 00801550 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801560 : 00 00 00 44 42 30 50 44 4d 00 00 00 00 00 00 00 | ...DB0PDM....... 00801570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801580 : 43 88 50 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.P..v.......... 00801590 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 008015A0 : 00 00 00 44 42 30 43 42 53 00 00 00 00 00 00 00 | ...DB0CBS....... 008015B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008015C0 : 43 86 37 50 00 76 00 00 88 00 00 00 00 00 00 00 | C.7P.v.......... 008015D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 008015E0 : 00 00 00 44 42 30 41 46 00 00 00 00 00 00 00 00 | ...DB0AF........ 008015F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801600 : 43 89 50 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.P..v.......... 00801610 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801620 : 00 00 00 44 42 30 4e 46 4c 00 00 00 00 00 00 00 | ...DB0NFL....... 00801630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801640 : 43 93 00 00 00 76 00 00 88 00 00 00 00 00 00 00 | C....v.......... 00801650 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801660 : 00 00 00 44 42 30 4c 4d 4d 00 00 00 00 00 00 00 | ...DB0LMM....... 00801670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801680 : 43 91 50 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.P..v.......... 00801690 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 008016A0 : 00 00 00 44 4d 30 4c 45 49 00 00 00 00 00 00 00 | ...DM0LEI....... 008016B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008016C0 : 14 57 75 00 00 06 00 00 88 00 00 00 00 00 00 00 | .Wu............. 008016D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 008016E0 : 00 00 00 44 42 30 4c 45 49 00 00 00 00 00 00 00 | ...DB0LEI....... 008016F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801700 : 43 92 75 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.u..v.......... 00801710 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801720 : 00 00 00 44 42 30 53 4d 4c 00 00 00 00 00 00 00 | ...DB0SML....... 00801730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801740 : 14 56 62 50 00 06 00 00 88 00 00 00 00 00 00 00 | .VbP............ 00801750 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801760 : 00 00 00 44 42 30 4c 53 41 00 00 00 00 00 00 00 | ...DB0LSA....... 00801770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801780 : 43 87 87 50 00 76 00 00 88 00 00 00 00 00 00 00 | C..P.v.......... 00801790 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 008017A0 : 00 00 00 44 42 30 4c 45 00 00 00 00 00 00 00 00 | ...DB0LE........ 008017B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008017C0 : 14 46 75 00 00 00 00 00 48 00 00 00 00 00 00 00 | .Fu.....H....... 008017D0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 008017E0 : 00 00 00 44 4f 4b 20 59 30 37 00 00 00 00 00 00 | ...DOK Y07...... 008017F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801800 : 14 46 50 00 00 00 00 00 08 00 00 00 00 00 00 00 | .FP............. 00801810 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801820 : 00 00 00 44 4f 4b 20 44 32 30 00 00 00 00 00 00 | ...DOK D20...... 00801830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801840 : 14 53 75 00 00 00 00 00 08 00 00 00 00 00 00 00 | .Su............. 00801850 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801860 : 00 00 00 44 4f 4b 20 44 32 33 00 00 00 00 00 00 | ...DOK D23...... 00801870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801880 : 14 47 25 00 00 00 00 00 08 00 00 00 00 00 00 00 | .G%............. 00801890 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 008018A0 : 00 00 00 44 4f 4b 20 53 33 31 00 00 00 00 00 00 | ...DOK S31...... 008018B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008018C0 : 14 53 00 00 00 00 00 00 08 00 00 00 00 00 00 00 | .S.............. 008018D0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 008018E0 : 00 00 00 53 32 30 00 00 00 00 00 00 00 00 00 00 | ...S20.......... 008018F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801900 : 14 55 00 00 00 00 00 00 08 00 00 00 00 00 00 00 | .U.............. 00801910 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801920 : 00 00 00 32 6d 20 4d 6f 62 69 6c 00 00 00 00 00 | ...2m Mobil..... 00801930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801940 : 43 35 00 00 00 00 00 00 08 00 00 00 00 00 00 00 | C5.............. 00801950 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801960 : 00 00 00 37 30 63 6d 20 4d 6f 62 69 6c 00 00 00 | ...70cm Mobil... 00801970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801980 : 14 48 00 00 00 00 00 00 08 00 00 00 00 00 00 00 | .H.............. 00801990 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 008019A0 : 00 00 00 41 50 52 53 00 00 00 00 00 00 00 00 00 | ...APRS......... 008019B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008019C0 : 14 58 25 00 00 00 00 00 88 00 00 00 00 00 00 00 | .X%............. 008019D0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 008019E0 : 00 00 00 49 53 53 20 41 50 52 53 00 00 00 00 00 | ...ISS APRS..... 008019F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801A00 : 43 70 00 00 29 10 10 00 88 04 01 00 00 00 00 00 | Cp..)........... 00801A10 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801A20 : 00 00 00 49 53 53 20 46 4d 20 52 65 70 00 00 00 | ...ISS FM Rep... 00801A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801A40 : 43 72 00 00 29 28 50 00 88 00 00 00 00 00 00 00 | Cr..)(P......... 00801A50 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801A60 : 00 00 00 43 41 53 2d 33 48 20 46 4d 00 00 00 00 | ...CAS-3H FM.... 00801A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801A80 : 14 59 00 00 29 16 00 00 48 00 00 00 00 00 00 00 | .Y..)...H....... 00801A90 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801AA0 : 00 00 00 50 4f 2d 31 30 31 20 46 4d 00 00 00 00 | ...PO-101 FM.... 00801AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801AC0 : 43 67 90 00 29 09 40 00 88 04 01 00 00 00 00 00 | Cg..).@......... 00801AD0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801AE0 : 00 00 00 53 4f 2d 35 30 20 46 4d 00 00 00 00 00 | ...SO-50 FM..... 00801AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801B00 : 14 58 80 00 28 94 70 00 48 04 01 00 00 00 00 00 | .X..(.p.H....... 00801B10 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801B20 : 00 00 00 41 4f 2d 39 32 20 46 4d 00 00 00 00 00 | ...AO-92 FM..... 00801B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801B40 : 14 59 60 00 28 92 90 00 48 04 01 00 00 00 00 00 | .Y`.(...H....... 00801B50 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801B60 : 00 00 00 41 4f 2d 39 31 20 46 4d 00 00 00 00 00 | ...AO-91 FM..... 00801B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801B80 : 43 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | C............... 00801B90 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801BA0 : 00 00 00 55 30 00 00 00 00 00 00 00 00 00 00 00 | ...U0........... 00801BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801BC0 : 43 02 25 00 00 00 00 00 40 00 00 00 00 00 00 00 | C.%.....@....... 00801BD0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801BE0 : 00 00 00 55 31 00 00 00 00 00 00 00 00 00 00 00 | ...U1........... 00801BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801C00 : 43 02 50 00 00 00 00 00 00 00 00 00 00 00 00 00 | C.P............. 00801C10 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801C20 : 00 00 00 55 32 00 00 00 00 00 00 00 00 00 00 00 | ...U2........... 00801C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801C40 : 43 02 75 00 00 00 00 00 00 00 00 00 00 00 00 00 | C.u............. 00801C50 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................ 00801C60 : 00 00 00 55 33 00 00 00 00 00 00 00 00 00 00 00 | ...U3........... 00801C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801C80 : 44 60 06 25 00 00 00 00 40 00 00 00 00 00 00 00 | D`.%....@....... 00801C90 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801CA0 : 00 00 00 50 4d 52 20 31 00 00 00 00 00 00 00 00 | ...PMR 1........ 00801CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801CC0 : 44 60 18 75 00 00 00 00 40 00 00 00 00 00 00 00 | D`.u....@....... 00801CD0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801CE0 : 00 00 00 50 4d 52 20 32 00 00 00 00 00 00 00 00 | ...PMR 2........ 00801CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801D00 : 44 60 31 25 00 00 00 00 40 00 00 00 00 00 00 00 | D`1%....@....... 00801D10 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801D20 : 00 00 00 50 4d 52 20 33 00 00 00 00 00 00 00 00 | ...PMR 3........ 00801D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801D40 : 44 60 43 75 00 00 00 00 00 00 00 00 00 00 00 00 | D`Cu............ 00801D50 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801D60 : 00 00 00 50 4d 52 20 34 00 00 00 00 00 00 00 00 | ...PMR 4........ 00801D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801D80 : 44 60 56 25 00 00 00 00 00 00 00 00 00 00 00 00 | D`V%............ 00801D90 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801DA0 : 00 00 00 50 4d 52 20 35 00 00 00 00 00 00 00 00 | ...PMR 5........ 00801DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801DC0 : 44 60 68 75 00 00 00 00 40 00 00 00 00 00 00 00 | D`hu....@....... 00801DD0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801DE0 : 00 00 00 50 4d 52 20 36 00 00 00 00 00 00 00 00 | ...PMR 6........ 00801DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801E00 : 44 60 81 25 00 00 00 00 40 00 00 00 00 00 00 00 | D`.%....@....... 00801E10 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801E20 : 00 00 00 50 4d 52 20 37 00 00 00 00 00 00 00 00 | ...PMR 7........ 00801E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801E40 : 44 60 93 75 00 00 00 00 40 00 00 00 00 00 00 00 | D`.u....@....... 00801E50 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801E60 : 00 00 00 50 4d 52 20 38 00 00 00 00 00 00 00 00 | ...PMR 8........ 00801E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801E80 : 44 61 06 25 00 00 00 00 00 00 00 00 00 00 00 00 | Da.%............ 00801E90 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801EA0 : 00 00 00 50 4d 52 20 39 00 00 00 00 00 00 00 00 | ...PMR 9........ 00801EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801EC0 : 44 61 18 75 00 00 00 00 00 00 00 00 00 00 00 00 | Da.u............ 00801ED0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801EE0 : 00 00 00 50 4d 52 20 31 30 00 00 00 00 00 00 00 | ...PMR 10....... 00801EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801F00 : 44 61 31 25 00 00 00 00 40 00 00 00 00 00 00 00 | Da1%....@....... 00801F10 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801F20 : 00 00 00 50 4d 52 20 31 31 00 00 00 00 00 00 00 | ...PMR 11....... 00801F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801F40 : 44 61 43 75 00 00 00 00 00 00 00 00 00 00 00 00 | DaCu............ 00801F50 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801F60 : 00 00 00 50 4d 52 20 31 32 00 00 00 00 00 00 00 | ...PMR 12....... 00801F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801F80 : 44 61 56 25 00 00 00 00 00 00 00 00 00 00 00 00 | DaV%............ 00801F90 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801FA0 : 00 00 00 50 4d 52 20 31 33 00 00 00 00 00 00 00 | ...PMR 13....... 00801FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00801FC0 : 44 61 68 75 00 00 00 00 00 00 00 00 00 00 00 00 | Dahu............ 00801FD0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00801FE0 : 00 00 00 50 4d 52 20 31 34 00 00 00 00 00 00 00 | ...PMR 14....... 00801FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008020A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008020B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008020C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008020D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008020E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008020F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008021A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008021B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008021C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008021D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008021E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008021F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008022A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008022B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008022C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008022D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008022E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008022F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008023A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008023B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008023C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008023D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008023E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008023F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008024A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008024B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008024C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008024D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008024E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008024F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008025A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008025B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008025C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008025D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008025E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008025F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008026A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008026B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008026C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008026D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008026E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008026F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008027A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008027B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008027C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008027D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008027E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008027F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008028A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008028B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008028C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008028D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008028E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008028F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008029A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008029B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008029C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008029D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008029E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008029F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00802FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008030A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008030B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008030C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008030D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008030E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008030F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008031A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008031B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008031C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008031D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008031E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008031F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008032A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008032B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008032C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008032D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008032E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008032F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008033A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008033B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008033C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008033D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008033E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008033F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008034A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008034B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008034C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008034D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008034E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008034F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008035A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008035B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008035C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008035D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008035E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008035F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008036A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008036B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008036C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008036D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008036E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008036F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008037A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008037B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008037C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008037D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008037E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008037F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008038A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008038B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008038C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008038D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008038E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008038F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008039A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008039B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008039C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008039D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008039E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 008039F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00803FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 00840000 : 44 61 81 25 00 00 00 00 00 00 00 00 00 00 00 00 | Da.%............ 00840010 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00840020 : 00 00 00 50 4d 52 20 31 35 00 00 00 00 00 00 00 | ...PMR 15....... 00840030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00840040 : 44 61 93 75 00 00 00 00 40 00 00 00 00 00 00 00 | Da.u....@....... 00840050 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................ 00840060 : 00 00 00 50 4d 52 20 31 36 00 00 00 00 00 00 00 | ...PMR 16....... 00840070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 00842000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00842010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00842020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00842030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00842040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00842050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00842060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00842070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 00FC0800 : 43 51 25 00 00 01 00 00 1d 00 15 15 13 00 13 00 | CQ%............. 00FC0810 : 26 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | &............... 00FC0820 : 01 00 00 43 68 61 6e 6e 65 6c 20 56 46 4f 20 41 | ...Channel VFO A 00FC0830 : 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 | ................ 00FC0840 : 14 51 25 00 00 01 00 00 1d 00 15 15 13 00 13 00 | .Q%............. 00FC0850 : 26 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | &............... 00FC0860 : 01 00 00 43 68 61 6e 6e 65 6c 20 56 46 4f 20 42 | ...Channel VFO B 00FC0870 : 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 00FC2800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00FC2810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00FC2820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00FC2830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00FC2840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00FC2850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00FC2860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00FC2870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01000000 : 00 00 03 00 04 00 01 00 05 00 07 00 08 00 09 00 | ................ 01000010 : 0a 00 0b 00 0d 00 0e 00 4a 00 4b 00 4c 00 4d 00 | ........J.K.L.M. 01000020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010000A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010000B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010000C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010000D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010000E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010000F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010001A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010001B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010001C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010001D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010001E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010001F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000200 : 5f 00 63 00 64 00 65 00 42 00 43 00 44 00 45 00 | _.c.d.e.B.C.D.E. 01000210 : 46 00 47 00 48 00 49 00 ff ff ff ff ff ff ff ff | F.G.H.I......... 01000220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010002A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010002B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010002C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010002D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010002E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010002F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010003A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010003B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010003C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010003D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010003E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010003F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000400 : 6e 00 6f 00 70 00 71 00 ff ff ff ff ff ff ff ff | n.o.p.q......... 01000410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010004A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010004B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010004C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010004D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010004E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010004F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010005A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010005B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010005C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010005D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010005E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010005F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000600 : 3e 00 40 00 41 00 ff ff ff ff ff ff ff ff ff ff | >.@.A........... 01000610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010006A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010006B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010006C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010006D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010006E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010006F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010007A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010007B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010007C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010007D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010007E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010007F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000800 : 64 00 42 00 43 00 44 00 45 00 46 00 47 00 48 00 | d.B.C.D.E.F.G.H. 01000810 : 49 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff | I............... 01000820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010008A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010008B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010008C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010008D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010008E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010008F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010009A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010009B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010009C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010009D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010009E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010009F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A00 : 0e 00 10 00 14 00 16 00 1a 00 1e 00 22 00 4d 00 | ............".M. 01000A10 : 4e 00 4f 00 50 00 51 00 52 00 53 00 54 00 ff ff | N.O.P.Q.R.S.T... 01000A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C00 : 60 00 61 00 64 00 65 00 42 00 43 00 44 00 45 00 | `.a.d.e.B.C.D.E. 01000C10 : 46 00 47 00 48 00 49 00 ff ff ff ff ff ff ff ff | F.G.H.I......... 01000C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E00 : 23 00 24 00 25 00 26 00 27 00 28 00 29 00 2a 00 | #.$.%.&.'.(.).*. 01000E10 : 4d 00 4e 00 55 00 ff ff ff ff ff ff ff ff ff ff | M.N.U........... 01000E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001000 : 64 00 65 00 42 00 43 00 44 00 45 00 46 00 47 00 | d.e.B.C.D.E.F.G. 01001010 : 48 00 49 00 ff ff ff ff ff ff ff ff ff ff ff ff | H.I............. 01001020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010010A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010010B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010010C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010010D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010010E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010010F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010011A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010011B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010011C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010011D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010011E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010011F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001200 : 2b 00 2c 00 2d 00 2e 00 2f 00 30 00 31 00 32 00 | +.,.-.../.0.1.2. 01001210 : 56 00 57 00 58 00 ff ff ff ff ff ff ff ff ff ff | V.W.X........... 01001220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010012A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010012B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010012C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010012D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010012E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010012F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010013A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010013B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010013C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010013D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010013E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010013F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001400 : 64 00 65 00 42 00 43 00 44 00 45 00 46 00 47 00 | d.e.B.C.D.E.F.G. 01001410 : 48 00 49 00 ff ff ff ff ff ff ff ff ff ff ff ff | H.I............. 01001420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010014A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010014B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010014C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010014D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010014E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010014F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010015A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010015B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010015C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010015D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010015E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010015F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001600 : 33 00 35 00 36 00 37 00 38 00 3a 00 3b 00 3c 00 | 3.5.6.7.8.:.;.<. 01001610 : 3d 00 59 00 5a 00 5b 00 5c 00 5d 00 5e 00 ff ff | =.Y.Z.[.\.].^... 01001620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010016A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010016B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010016C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010016D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010016E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010016F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010017A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010017B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010017C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010017D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010017E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010017F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001800 : 62 00 64 00 65 00 42 00 43 00 44 00 45 00 46 00 | b.d.e.B.C.D.E.F. 01001810 : 47 00 48 00 49 00 ff ff ff ff ff ff ff ff ff ff | G.H.I........... 01001820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010018A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010018B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010018C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010018D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010018E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010018F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010019A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010019B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010019C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010019D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010019E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010019F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A00 : 68 00 6d 00 6c 00 6b 00 6a 00 69 00 ff ff ff ff | h.m.l.k.j.i..... 01001A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C00 : 67 00 68 00 6d 00 6c 00 6b 00 6a 00 69 00 ff ff | g.h.m.l.k.j.i... 01001C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E00 : 72 00 73 00 74 00 75 00 76 00 77 00 78 00 79 00 | r.s.t.u.v.w.x.y. 01001E10 : 7a 00 7b 00 7c 00 7d 00 7e 00 7f 00 80 00 81 00 | z.{.|.}.~....... 01001E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002000 : 72 00 73 00 74 00 75 00 76 00 77 00 78 00 79 00 | r.s.t.u.v.w.x.y. 01002010 : 7a 00 7b 00 7c 00 7d 00 7e 00 7f 00 80 00 81 00 | z.{.|.}.~....... 01002020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010020A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010020B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010020C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010020D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010020E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010020F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010021A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010021B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010021C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010021D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010021E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010021F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ ----------------------------------------------------------------------------- 01040000 : 44 00 00 00 43 99 00 00 ff ff 00 00 00 00 00 00 | D...C........... 01040010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01040020 : 00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 00 | ................ 01040030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01040040 : 00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 00 | ................ 01040050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01040060 : 00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 00 | ................ 01040070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01042000 : 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01042010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01042080 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01043000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01043010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01043020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01043030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01043040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01043050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01043060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01043070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01080000 : 00 01 00 00 ff ff 0f 00 19 00 1d 00 1d 00 00 4b | ...............K 01080010 : 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | W............... 01080020 : 03 00 04 00 4a 00 4b 00 5f 00 64 00 42 00 6e 00 | ....J.K._.d.B.n. 01080030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080080 : ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01080200 : 00 00 ff ff ff ff 0f 00 19 00 1d 00 1d 00 00 42 | ...............B 01080210 : 65 72 42 72 61 00 00 00 00 00 00 00 00 00 00 00 | erBra........... 01080220 : 04 00 08 00 0e 00 10 00 14 00 16 00 1e 00 22 00 | ..............". 01080230 : 1a 00 26 00 2a 00 32 00 ff ff ff ff ff ff ff ff | ..&.*.2......... 01080240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080280 : ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01640000 : 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01640010 : 00 00 02 01 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01640020 : 00 00 03 02 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01640030 : 00 00 04 03 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01640040 : 00 00 ff 04 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01640800 : 00 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff | ................ 01640810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01640820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01640830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01640840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01640850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01640860 : ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01640870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01640880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02140000 : 48 65 6c 6c 6f 21 00 00 00 00 00 00 00 00 00 00 | Hello!.......... 02140010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021400A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021400B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021400C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02140100 : 57 65 6c 63 6f 6d 65 21 00 00 00 00 00 00 00 00 | Welcome!........ 02140110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021401A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021401B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021401C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02140200 : 54 68 61 6e 6b 20 79 6f 75 21 00 00 00 00 00 00 | Thank you!...... 02140210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021402A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021402B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021402C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02140300 : 47 6f 6f 64 20 62 79 65 21 00 00 00 00 00 00 00 | Good bye!....... 02140310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021403A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021403B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021403C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02140400 : 48 61 70 70 79 20 65 76 65 72 79 20 64 61 79 21 | Happy every day! 02140410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021404A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021404B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021404C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02480000 : 01 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02480200 : 01 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02480210 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02480220 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 024C0000 : 00 00 0e 46 12 34 56 78 9a bc de 00 00 00 00 00 | ...F.4Vx........ 024C0010 : 00 00 00 00 00 00 00 00 00 20 20 20 20 20 20 00 | ......... . ----------------------------------------------------------------------------- 024C0C80 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 024C0D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 024C1000 : c0 5d 68 29 50 2d 9c 31 b0 36 c4 3b 3c 41 7c 47 | .]h)P-.1.6.;.@.A........... 01000610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010006A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010006B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010006C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010006D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010006E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010006F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010007A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010007B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010007C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010007D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010007E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010007F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000800 : 64 00 42 00 43 00 44 00 45 00 46 00 47 00 48 00 | d.B.C.D.E.F.G.H. 01000810 : 49 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff | I............... 01000820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010008A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010008B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010008C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010008D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010008E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010008F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010009A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010009B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010009C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010009D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010009E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010009F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A00 : 0e 00 10 00 14 00 16 00 1a 00 1e 00 22 00 4d 00 | ............".M. 01000A10 : 4e 00 4f 00 50 00 51 00 52 00 53 00 54 00 ff ff | N.O.P.Q.R.S.T... 01000A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C00 : 60 00 61 00 64 00 65 00 42 00 43 00 44 00 45 00 | `.a.d.e.B.C.D.E. 01000C10 : 46 00 47 00 48 00 49 00 ff ff ff ff ff ff ff ff | F.G.H.I......... 01000C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E00 : 23 00 24 00 25 00 26 00 27 00 28 00 29 00 2a 00 | #.$.%.&.'.(.).*. 01000E10 : 4d 00 4e 00 55 00 ff ff ff ff ff ff ff ff ff ff | M.N.U........... 01000E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01000FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001000 : 64 00 65 00 42 00 43 00 44 00 45 00 46 00 47 00 | d.e.B.C.D.E.F.G. 01001010 : 48 00 49 00 ff ff ff ff ff ff ff ff ff ff ff ff | H.I............. 01001020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010010A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010010B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010010C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010010D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010010E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010010F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010011A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010011B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010011C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010011D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010011E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010011F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001200 : 2b 00 2c 00 2d 00 2e 00 2f 00 30 00 31 00 32 00 | +.,.-.../.0.1.2. 01001210 : 56 00 57 00 58 00 ff ff ff ff ff ff ff ff ff ff | V.W.X........... 01001220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010012A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010012B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010012C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010012D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010012E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010012F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010013A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010013B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010013C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010013D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010013E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010013F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001400 : 64 00 65 00 42 00 43 00 44 00 45 00 46 00 47 00 | d.e.B.C.D.E.F.G. 01001410 : 48 00 49 00 ff ff ff ff ff ff ff ff ff ff ff ff | H.I............. 01001420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010014A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010014B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010014C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010014D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010014E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010014F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010015A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010015B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010015C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010015D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010015E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010015F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001600 : 33 00 35 00 36 00 37 00 38 00 3a 00 3b 00 3c 00 | 3.5.6.7.8.:.;.<. 01001610 : 3d 00 59 00 5a 00 5b 00 5c 00 5d 00 5e 00 ff ff | =.Y.Z.[.\.].^... 01001620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010016A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010016B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010016C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010016D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010016E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010016F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010017A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010017B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010017C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010017D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010017E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010017F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001800 : 62 00 64 00 65 00 42 00 43 00 44 00 45 00 46 00 | b.d.e.B.C.D.E.F. 01001810 : 47 00 48 00 49 00 ff ff ff ff ff ff ff ff ff ff | G.H.I........... 01001820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010018A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010018B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010018C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010018D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010018E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010018F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010019A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010019B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010019C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010019D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010019E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010019F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A00 : 68 00 6d 00 6c 00 6b 00 6a 00 69 00 ff ff ff ff | h.m.l.k.j.i..... 01001A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C00 : 67 00 68 00 6d 00 6c 00 6b 00 6a 00 69 00 ff ff | g.h.m.l.k.j.i... 01001C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E00 : 72 00 73 00 74 00 75 00 76 00 77 00 78 00 79 00 | r.s.t.u.v.w.x.y. 01001E10 : 7a 00 7b 00 7c 00 7d 00 7e 00 7f 00 80 00 81 00 | z.{.|.}.~....... 01001E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01001FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002000 : 72 00 73 00 74 00 75 00 76 00 77 00 78 00 79 00 | r.s.t.u.v.w.x.y. 01002010 : 7a 00 7b 00 7c 00 7d 00 7e 00 7f 00 80 00 81 00 | z.{.|.}.~....... 01002020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010020A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010020B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010020C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010020D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010020E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010020F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01002190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010021A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010021B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010021C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010021D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010021E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 010021F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ ----------------------------------------------------------------------------- 01040000 : 44 00 00 00 43 99 00 00 ff ff 00 00 00 00 00 00 | D...C........... 01040010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01040020 : 00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 00 | ................ 01040030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01040040 : 00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 00 | ................ 01040050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01040060 : 00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 00 | ................ 01040070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01042000 : 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01042010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01042080 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01043000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01043010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01043020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01043030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01043040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01043050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01043060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01043070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01080000 : 00 01 00 00 ff ff 0f 00 19 00 1d 00 1d 00 00 4b | ...............K 01080010 : 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | W............... 01080020 : 03 00 04 00 4a 00 4b 00 5f 00 64 00 42 00 6e 00 | ....J.K._.d.B.n. 01080030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080080 : ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01080200 : 00 00 ff ff ff ff 0f 00 19 00 1d 00 1d 00 00 42 | ...............B 01080210 : 65 72 42 72 61 00 00 00 00 00 00 00 00 00 00 00 | erBra........... 01080220 : 04 00 08 00 0e 00 10 00 14 00 16 00 1e 00 22 00 | ..............". 01080230 : 1a 00 26 00 2a 00 32 00 ff ff ff ff ff ff ff ff | ..&.*.2......... 01080240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01080280 : ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01640000 : 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01640010 : 00 00 02 01 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01640020 : 00 00 03 02 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01640030 : 00 00 04 03 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01640040 : 00 00 ff 04 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 01640800 : 00 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff | ................ 01640810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01640820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01640830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01640840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01640850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................ 01640860 : ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01640870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 01640880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02140000 : 48 65 6c 6c 6f 21 00 00 00 00 00 00 00 00 00 00 | Hello!.......... 02140010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021400A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021400B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021400C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02140100 : 57 65 6c 63 6f 6d 65 21 00 00 00 00 00 00 00 00 | Welcome!........ 02140110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021401A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021401B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021401C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02140200 : 54 68 61 6e 6b 20 79 6f 75 21 00 00 00 00 00 00 | Thank you!...... 02140210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021402A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021402B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021402C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02140300 : 47 6f 6f 64 20 62 79 65 21 00 00 00 00 00 00 00 | Good bye!....... 02140310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021403A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021403B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021403C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02140400 : 48 61 70 70 79 20 65 76 65 72 79 20 64 61 79 21 | Happy every day! 02140410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02140490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021404A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021404B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 021404C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02480000 : 01 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 02480200 : 01 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02480210 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 02480220 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 024C0000 : 00 00 0e 46 12 34 56 78 9a bc de 00 00 00 00 00 | ...F.4Vx........ 024C0010 : 00 00 00 00 00 00 00 00 00 20 20 20 20 20 20 00 | ......... . ----------------------------------------------------------------------------- 024C0C80 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 024C0D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 024C0EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ ----------------------------------------------------------------------------- 024C1000 : c0 5d 68 29 50 2d 9c 31 b0 36 c4 3b 3c 41 7c 47 | .]h)P-.1.6.;=0x20 and c<0x7f: t += chr(c) else: t += "." return( h + " | " + t) def isWriteCommand(p): if not isDataPacket(p): return False data = getData(p) return 'W' == chr(data[0]) def isDataPacket(p): return ("host" == p.usb.src) and ("USB.CAPDATA" in p) def getData(p): if not isDataPacket(p): return None return binascii.a2b_hex(p["USB.CAPDATA_RAW"].value) def dumpWriteCommand(p, nextaddr): if not isWriteCommand(p): return data = getData(p) res = struct.unpack(">cIB16sBB", data) addr = res[1] if nextaddr != addr: print((8+3+16*3+16+2)*"-") print("{:08X} : {}".format(addr, hexDump(res[3]))) return addr+16 cap = pyshark.FileCapture(sys.argv[1], include_raw=True, use_json=True) nextaddr = 0 for p in cap: if isWriteCommand(p): nextaddr = dumpWriteCommand(p, nextaddr) qdmr-0.12.3/doc/reveng/anytone/d878uv2/000077500000000000000000000000001501654372000174025ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/anytone/d878uv2/at_d878uv2_emulator.py000066400000000000000000000143361501654372000235060ustar00rootroot00000000000000#!/usr/bin/env python3 # # Emulate anytone d878uv radio to customer programming software. # Send intercepted data stream over network to server script for further investigation. # # This script connects to a virtual com port COM26 which is connected via a virtual # null modem cable to the virtual com port COM18 which is used by the programming software. # This virtual ports and cable can be provided by the COM0COM tool. # # Linux users can use # socat -d -d pty,raw,echo=0,b4000000 pty,raw,echo=0,b4000000 # for emulating a virtual null modem cable. import serial import time import sys import struct # config filebase = 'codeplug' filecount = 0 comport = 'COM6' # connected to COM18 with com0com. use COM18 in CPS def hexDump(s): h = " ".join(map("{:02x}".format, s)) t = "" for i in range(len(s)): c = s[i] if c>=0x20 and c<0x7f: t += chr(c) else: t += "." return( h + " | " + t) # parameters? if len(sys.argv) == 2: comport = sys.argv[1] elif len(sys.argv) >= 3: print("Usage: " + sys.argv[0] + ' [comport]') exit() # open serial port serialPort = None try: print("Trying comport " + comport) serialPort = serial.Serial(port = comport, baudrate=4000000, bytesize=8, timeout=1, stopbits=serial.STOPBITS_ONE) # 115200 921600 4000000 except (err): print('ERR: Could not open port ' + comport) print("Usage: " + sys.argv[0] + ' servername [comport]: ' + err) exit() out = None nextaddr = None # wait for data try: while 1: command = '' command = serialPort.read() while serialPort.in_waiting > 0: command += serialPort.read() # respond to command on com port if ( len(command) == 0 ): pass elif ( command == b'PROGRAM'): print("Program session requested.") resp = b'QX\x06' serialPort.write(resp) filename = "{}_{:04}.hex".format(filebase, filecount) out = open(filename, "w") nextaddr = None elif ( command == b'\x02' ): print("Device info requested.") resp = b'ID878UV2\x00V100\x00\x00\x06' serialPort.write(resp) elif ( command == b'R\x02\xfa\x00\x20\x10' ): print("Read special memory request.") resp = b'W\x02\xfa\x00\x20\x10\xff\xff\xff\xff\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x26\x06' serialPort.write(resp) elif ( command[0:4] == b'R\x02\xfa\x00' and command[5] == 16 ): # 0x02fa00.. print("Read local information.") resp = b'W\x02\xfa\x00' + bytes([command[4]]) + b'\x10' if ( command[4] == 0x00 ): resp += b'\x00\x00\x00\x00\x01\x01\x01\x00\x00\x01\x01\x20\x20\x20\x20\xff' elif ( command[4] == 0x10 ): # Radio Type resp += b'\x44\x38\x37\x38\x55\x56\x00\x01\x00\xff\xff\xff\xff\xff\xff\xff' elif ( command[4] == 0x30 ): # Serial Number resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x40 ): # Production Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x50 ): # Manucfacture Code resp += b'\x31\x32\x33\x34\x35\x36\x37\x38\xff\xff\xff\xff\xff\xff\xff\xff' elif ( command[4] == 0x60 ): # Maintained Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x70 ): # Dealer Code resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x80 ): # Stock Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x90 ): # Sell Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xa0 ): # Seller resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xb0 ): # Maintained Description resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xc0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xd0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xe0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xf0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' else: resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' resp = resp + bytes( [sum(resp[1:]) & 0xff] ) + b'\x06' #print(resp.hex()) serialPort.write(resp) elif ( command[0] == ord('W') ) : resp = b'\x06' # just ack serialPort.write(resp) res = struct.unpack(">cIB16sBB", command) addr = res[1] if nextaddr != addr: out.write((8+3+16*3+16+2)*"-" + "\n") out.write("{:08X} : {}\n".format(addr, hexDump(res[3]))) nextaddr = addr+16 elif ( command == b'END' ): print("End session.") resp = b'\x06' # just ack serialPort.write(resp) out.close() filecount += 1 elif ( command == b'UPDATE' ): # for firmware update the device has to be switched on while pressing PF3 (blue button on top) and PTT keys print("Start Firmware Update. Only useful if device is in update receiving mode. (Switch on while pressing PF3 (blue button on top) and PTT keys)") resp = b'\x06' # just ack serialPort.write(resp) elif ( command == b'\x18' ): print("Firmware Update Send Complete. Switch device on while pressing PF2 (top left side) and PTT keys to start installer.") resp = b'\x06' # just ack serialPort.write(resp) elif ( command[0] == 0x01 ): print("Firmware data.") resp = b'\x06' # just ack serialPort.write(resp) else: #print("> " + str(command)) pass finally: print('QRT') serialPort.close() qdmr-0.12.3/doc/reveng/baofeng/000077500000000000000000000000001501654372000161775ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/baofeng/d6x2uv/000077500000000000000000000000001501654372000173355ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/baofeng/d6x2uv/dmr_6x2uv_emulator.py000066400000000000000000000144651501654372000234650ustar00rootroot00000000000000#!/usr/bin/env python3 # # Emulate anytone d878uv radio to customer programming software. # Send intercepted data stream over network to server script for further investigation. # # This script connects to a virtual com port COM26 which is connected via a virtual # null modem cable to the virtual com port COM18 which is used by the programming software. # This virtual ports and cable can be provided by the COM0COM tool. # # Linux users can use # socat -d -d pty,raw,echo=0,b4000000 pty,raw,echo=0,b4000000 # for emulating a virtual null modem cable. import serial import time import sys import struct # config filebase = 'codeplug' filecount = 0 comport = 'COM6' # connected to COM18 with com0com. use COM18 in CPS def hexDump(s): h = " ".join(map("{:02x}".format, s)) t = "" for i in range(len(s)): c = s[i] if c>=0x20 and c<0x7f: t += chr(c) else: t += "." return( h + " | " + t) # parameters? if len(sys.argv) == 3: filebase = sys.argv[1] comport = sys.argv[2] elif len(sys.argv) == 2: filebase = sys.argv[1] elif len(sys.argv) >3: print("Usage: " + sys.argv[0] + ' filebase [comport]') exit() # open serial port serialPort = None try: print("Trying comport " + comport) serialPort = serial.Serial(port = comport, baudrate=4000000, bytesize=8, timeout=1, stopbits=serial.STOPBITS_ONE) # 115200 921600 4000000 except (err): print('ERR: Could not open port ' + comport) print("Usage: " + sys.argv[0] + ' servername [comport]: ' + err) exit() out = None nextaddr = None # wait for data try: while 1: command = '' command = serialPort.read() while serialPort.in_waiting > 0: command += serialPort.read() # respond to command on com port if ( len(command) == 0 ): pass elif ( command == b'PROGRAM'): print("Program session requested.") resp = b'QX\x06' serialPort.write(resp) filename = "{}_{:04}.hex".format(filebase, filecount) out = open(filename, "w") nextaddr = None elif ( command == b'\x02' ): print("Device info requested.") resp = b'ID6X2UV\x00\x00V102\x00\x00\x06' serialPort.write(resp) elif ( command == b'R\x02\xfa\x00\x20\x10' ): print("Read special memory request.") resp = b'W\x02\xfa\x00\x20\x10\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x24\x06' serialPort.write(resp) elif ( command[0:4] == b'R\x02\xfa\x00' and command[5] == 16 ): # 0x02fa00.. print("Read local information.") resp = b'W\x02\xfa\x00' + bytes([command[4]]) + b'\x10' if ( command[4] == 0x00 ): resp += b'\x00\x00\x00\x03\x01\x01\x01\x00\x00\x01\x01\x20\x20\x20\x20\xff' elif ( command[4] == 0x10 ): # Radio Type resp += b'\x44\x38\x37\x38\x55\x56\x00\x01\x00\xff\xff\xff\xff\xff\xff\xff' elif ( command[4] == 0x30 ): # Serial Number resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x40 ): # Production Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x50 ): # Manucfacture Code resp += b'\x31\x32\x33\x34\x35\x36\x37\x38\xff\xff\xff\xff\xff\xff\xff\xff' elif ( command[4] == 0x60 ): # Maintained Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x70 ): # Dealer Code resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x80 ): # Stock Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0x90 ): # Sell Date resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xa0 ): # Seller resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xb0 ): # Maintained Description resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xc0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xd0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xe0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif ( command[4] == 0xf0 ): resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' else: resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' resp = resp + bytes( [sum(resp[1:]) & 0xff] ) + b'\x06' #print(resp.hex()) serialPort.write(resp) elif ( command[0] == ord('W') ) : resp = b'\x06' # just ack serialPort.write(resp) res = struct.unpack(">cIB16sBB", command) addr = res[1] if nextaddr != addr: out.write((8+3+16*3+16+2)*"-" + "\n") out.write("{:08X} : {}\n".format(addr, hexDump(res[3]))) nextaddr = addr+16 elif ( command == b'END' ): print("End session.") resp = b'\x06' # just ack serialPort.write(resp) out.close() filecount += 1 elif ( command == b'UPDATE' ): # for firmware update the device has to be switched on while pressing PF3 (blue button on top) and PTT keys print("Start Firmware Update. Only useful if device is in update receiving mode. (Switch on while pressing PF3 (blue button on top) and PTT keys)") resp = b'\x06' # just ack serialPort.write(resp) elif ( command == b'\x18' ): print("Firmware Update Send Complete. Switch device on while pressing PF2 (top left side) and PTT keys to start installer.") resp = b'\x06' # just ack serialPort.write(resp) elif ( command[0] == 0x01 ): print("Firmware data.") resp = b'\x06' # just ack serialPort.write(resp) else: #print("> " + str(command)) pass finally: print('QRT') serialPort.close() qdmr-0.12.3/doc/reveng/baofeng/dr1801/000077500000000000000000000000001501654372000171165ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/baofeng/dr1801/extract.py000077500000000000000000000025141501654372000211470ustar00rootroot00000000000000#!/usr/bin/env python3 import pyshark import sys import binascii from abc import ABCMeta dev_addr = 1 if len(sys.argv) != 2: print("Usage: exract.py PCAPNG_FILE") def hexDump(s: bytes, prefix: str = "", addr: int = 0) -> str: """ Utility function to hex-dump binary data. """ N = len(s) Nb = N//16 if (N%16): Nb += 1 res = "" for j in range(Nb): a,b = j*16, min((j+1)*16,N) h = " ".join(map("{:02x}".format, s[a:b])) h += " "*(16-(b-a)) t = "" for i in range(a,b): c = s[i] if c>=0x20 and c<0x7f: t += chr(c) else: t += "." res += (prefix + "{:08X} ".format(addr+16*j) + h + " | " + t + "\n") return res[:-1] def isFromHost(p): return ("host" == p.usb.src) and ((None==dev_addr) or (dev_addr == int(p.usb.device_address))) def isToHost(p): return (("host" == p.usb.dest) and ((None==dev_addr) or (dev_addr == int(p.usb.device_address)))) def isDataPacket(p): return ("USB.CAPDATA" in p) def getData(p): if not isDataPacket(p): return None return binascii.a2b_hex(p["USB.CAPDATA_RAW"].value) cap = pyshark.FileCapture(sys.argv[1], include_raw=True, use_json=True) nextaddr = 0 for p in cap: if isDataPacket(p): if isFromHost(p): print(hexDump(getData(p), "< ")) else: print(hexDump(getData(p), "> ")) #print(p) qdmr-0.12.3/doc/reveng/baofeng/dr1801/protocol.md000066400000000000000000000111651501654372000213050ustar00rootroot00000000000000# DR-1801A6 (BF1801) - Protocol A new version of the popular DM-1801 by Baofeng. Is sold as DR-1801UV or DR-1801A6 and calls itself BF1801. The communication is based on USB CDC-ACM (serial over USB). This device uses the AUCTUS A6 chip and thus its protocol. For a detailed description, see jhart99s brilliant [article series](https://jhart99.com/a6-hidden-interface/) on this chip. ## Basic structure The first few packets exchanged at a read: ``` > aa 06 01 04 03 bb < aa 07 81 04 01 83 bb > aa 07 00 2b 00 2c bb < aa 07 80 2b 02 ae bb > aa 0a 01 00 00 01 c2 00 c8 bb < aa 15 81 00 01 00 01 dd 90 00 00 00 68 00 02 e6 9e 09 2d ef bb > aa 06 01 01 06 bb < ... only packets from device to host from now on with no structure. Just plain data. ``` The first few packets exchanged at a write: ``` > aa 06 01 04 03 bb < aa 07 81 04 01 83 bb > aa 07 00 2b 00 2c bb < aa 07 80 2b 02 ae bb > aa 12 01 02 00 01 00 01 da 8c 7f f6 00 01 c2 00 0d bb < aa 08 81 02 01 00 8a bb > .... Codeplug is written in many large packets with no structure, just data. < aa 09 81 03 01 00 00 8a bb < 00 | . < 00 | . < 00 | . ``` There appears to be two kind of messages command/response messages and status messages from the device. The status messages do not appear to be triggered by by the host. ## Command/response messages They start with `aah` and end with `bbh`. The general structure of command/response messages are ``` +---+---+---+---+---+...+---+---+---+ |aah|LEN| CMD | Params |CRC|bbh| +---+---+---+---+---+...+---+---+---+ ``` The `LEN` field contains the length of the entire packet, including start and stop bytes. The parameter field is optional and of variable length. The checksum is computed by simply xor-ing the entire payload byte-wise. The command field has the following sub-structure: ``` +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |C/R| Command 15bit, big-endian | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ``` The most significant bit contains a flag, that is set for responses and cleared for requests. There are many commands already known see jhart99s [list of commands](https://jhart99.com/atecps/#cps-commands). ### 0000h Command -- Identify radio Not used by the manufacturer CPS but handled by the radio. This command is send without any parameters and the radio returns a sting, identifying the radio. The response looks like ``` aa 3a 80 00 01 20 2c 42 46 31 38 30 31 2c 41 36 ..... ,B F1801,A6 2d 30 30 30 30 2d 58 58 58 58 2c 70 6f 72 74 61 -0000-XX XX,porta 62 6c 65 2c 31 33 36 4d 2d 31 37 34 4d 2c 34 30 ble,136M -174M,40 30 4d 2d 34 38 30 4d 2c fd bb 0M-480M, .. ``` The response payload starts with a status byte (0x01 success) followed by an ASCII string, containing the information about the radio. The information is separated by comma (,). The first entry is empty (single space), followed by the device name (BF1801), followed by the firmware version (weird number here), device class (portable) and the frequency ranges. There is likely space for three bands, however, the device only supports 136-174MHz and 400-480MHz. ### 0104h Command -- Enter CPS mode (?) First command send without any parameters irrespective of codeplug read or write. Response contains a single parameter `01h`, likely status byte like *success*. ### 002bh Command -- Check Programming Password Checks the programming password. Request contains parameter `00h`. Likely, the length of the password. Needs to be tested with a proper password. The Response contains a single byte `02h`. ### 0100h Command Only send before reading the codeplug. Contains 4bytes of parameters. Seen `00 01 c2 00` = 115200 big endian. Set UART speed?!? Response parameters are quiet long, e.g. `01 00 01 dd 90 00 00 00 68 00 02 e6 9e 09 2d`. 0x0001dd90 is the size of the codeplug file. Then, it appears like two uint32 numbers i.e., 0x00000068 and 0x0002e69e. These numbers do not change. The last two bytes, however, appear to be a checksum. ### 0101h Command Only send right before reading of the codeplug starts. There is no actual response to this request, the radio responds with the codeplug. No parameters send with this command. ### 0102h Command Only send before writing the codeplug. Contains 12bytes of parameters. E.g., `00 01 00 01 da 8c 7f f6 00 01 c2 00`, again contains 0001c200h=115200. As not the entire codeplug file is written to the device, `00 01 da 8c` may encode the amount to be written. Response only contains two bytes. E.g., `01 00`. ### 0103h Command Only seen after codeplug write. Contains 3 parameter bytes `01 00 00`. qdmr-0.12.3/doc/reveng/cotre/000077500000000000000000000000001501654372000157125ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/cotre/README.md000066400000000000000000000036151501654372000171760ustar00rootroot00000000000000# Cotre DMR radio This is likely the cheapest DMR radio out there for about $45. The protocol appears to be a weird one: It is actually a stream of requests and responses that do not alternate. That is, the CPS bombards the radio with requests and the radio responses to it some time later. Consequently, it is harder to correlate a response to its request. Although such a protocol is common in networks (e.g., TCP) it is rather uncommon for USB devices as there is virtually no latency between request and response and thus, the bandwidth is not limited by the latency. Is this what happens when a network engineer writes embedded code? ## Packet format Requests and responses appear to share the same packet format. Please note, that due to the nature of the chosen transport, a packet may be split over two USB transfer packets. ``` 0 8 16 24 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | Preamble, fixed to 0xad | 16 bit payload length, big endian | Payload, variable size ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... However, only a few bytes are actually transferred here. | Checksum? | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ``` ### Checksum The checksum appears to be a simple XOR of all payload bytes. ### Example The very first request sent to the device when reading is ``` ad 00 07 ff 04 03 00 00 00 01 f9 ``` The would then decode into * Preamble `ad` * Length `0007` * Payload `ff 04 03 00 00 00 01` * CRC `f9` = `ff ^ 04 ^ 03 ^ 01` qdmr-0.12.3/doc/reveng/cotre/extract.py000077500000000000000000000015571501654372000177510ustar00rootroot00000000000000#!/usr/bin/env python3 import pyshark import struct import sys import binascii dev_addr = 6 def hexDump(s): h = " ".join(map("{:02x}".format, s)) t = "" for i in range(len(s)): c = s[i] if c>=0x20 and c<0x7f: t += chr(c) else: t += "." return( h + " | " + t) def isFromHost(p): return ("host" == p.usb.src) and ((None==dev_addr) or (dev_addr == int(p.usb.device_address))) def isToHost(p): return ("host" == p.usb.dest) def isDataPacket(p): return ("USB.CAPDATA" in p) def getData(p): if not isDataPacket(p): return None return binascii.a2b_hex(p["USB.CAPDATA_RAW"].value) cap = pyshark.FileCapture(sys.argv[1], include_raw=True, use_json=True) nextaddr = 0 for p in cap: if isDataPacket(p): dump = hexDump(getData(p)) if isFromHost(p): print("> " + dump) else: print("< " + dump) qdmr-0.12.3/doc/reveng/gd77/000077500000000000000000000000001501654372000153465ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/gd77/callsign-db.md000066400000000000000000000021241501654372000200460ustar00rootroot00000000000000# GD77 call-sign DB memory representation The encoding appears to be super simple: ## Header ``` 0 8 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... + |'I'|'D'|'-'|'V'|'0'|'0'|'1'| 0 | N entries | Entries ... | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... + ``` The first 8 bytes (at address 0x0000) contains the 0-terminated string `ID-V001`, followed by the number of DB entries encoded as a 32bit little endian integer. After this header all entries are just appended. These entries must be sorted by the DMR ID in ascending order. There is no index table written to the device. ## DB Entry Each entry of the table is encoded into 12bytes as ``` 0 8 +---+---+---+---+---+---+---+---+---+---+---+---+ | DMR ID | Name, ASCII | +---+---+---+---+---+---+---+---+---+---+---+---+ ``` where the DMR ID is encoded as 8 BCD numbers in litte-endian and the name is max 8 ASCII chars, 0-terminated and padded. qdmr-0.12.3/doc/reveng/gd77/dump.py000077500000000000000000000106671501654372000167020ustar00rootroot00000000000000#!/usr/bin/env python3 from json import dump from re import S import pyshark import struct import sys import binascii device = None # "1.13.0" def hexDump(s: bytes, prefix="", addr=0) -> str: """ Utility function to hex-dump binary data. """ N = len(s) Nb = N//16 if (N%16): Nb += 1 res = "" for j in range(Nb): a,b = j*16, min((j+1)*16,N) h = " ".join(map("{:02x}".format, s[a:b])) h += " "*(16-(b-a)) t = "" for i in range(a,b): c = s[i] if c>=0x20 and c<0x7f: t += chr(c) else: t += "." res += (prefix + "{:08X} ".format(addr+16*j) + h + " | " + t + "\n") return res[:-1] def isFromHost(p): b = ("host" == p.usb.src) if device: b = b and (device == p.usb.dst) return b def isFromDevice(p): b = ("host" == p.usb.dst) if device: b = b and (device == p.usb.src) return b def isRequest(p): return isFromHost(p) and ("SETUP DATA" == p.highest_layer) and (p.layers[-1].has_field("data_fragment")) def isResponse(p): return isFromDevice(p) and ("USB.CAPDATA" in p) def getData(p): if isRequest(p): return binascii.a2b_hex(p.layers[-1].data_fragment_raw[0]) elif isResponse(p): return binascii.a2b_hex(p["USB.CAPDATA_RAW"].value) return None class RawPayload: def __init__(self, payload): self._payload = payload def __len__(self): return len(self._payload) def dump(self, prefix="", addr=0): return hexDump(self._payload, prefix, addr) class Package: def __init__(self, payload): self._type, self._length = struct.unpack("=4: self._payload = ReadOperation(payload) elif (0x57 == payload[0]) and len(payload)>=4: self._payload = WriteOperation(payload) elif 0x41 == payload[0]: self._payload = ACK(payload) else: self._payload = RawPayload(payload) def type(self): return self._type def payload(self): return self._payload def dump(self, prefix=""): s = "" if 1 == self._type: s += prefix + "Request, len=0x{0:02X}".format(self._length) + "\n" elif 3 == self._type: s += prefix + "Response, len=0x{0:02X}".format(self._length) + "\n" s += self._payload.dump(prefix+" | ") return s class ReadOperation: def __init__(self, payload): self._addr, self._length = struct.unpack(">HB", payload[1:4]) self._payload = None if 4 < len(payload): self._payload = RawPayload(payload[4:]) def dump(self, prefix=""): s = prefix + "Read addr=0x{0:04X}, len=0x{1:02X}".format(self._addr, self._length) if None != self._payload: s += "\n" + self._payload.dump(prefix + " | ") return s class WriteOperation: def __init__(self, payload): self._addr, self._length = struct.unpack(">HB", payload[1:4]) self._payload = None if 4 < len(payload): self._payload = RawPayload(payload[4:]) def payload(self): return self._payload def addr(self): return self._addr def dump(self, prefix=""): s = prefix + "Write addr=0x{0:04X}, len=0x{1:02X}".format(self._addr, self._length) if None != self._payload: s += "\n" + self._payload.dump(prefix + " | ") return s class ACK: def __init__(self, payload): pass def dump(self, prefix=""): return prefix+"ACK" if 2 == len(sys.argv): cap = pyshark.FileCapture(sys.argv[1], include_raw=True, use_json=True) for p in cap: if isRequest(p) and len(getData(p))>=4: P = Package(getData(p)) print(P.dump(" > ")) print("") elif isResponse(p) and len(getData(p))>=4: P = Package(getData(p)) print(P.dump(" < ")) print("") elif isRequest(p): print(hexDump(getData(p), " > ")) elif isResponse(p): print(hexDump(getData(p), " < ")) elif (3 == len(sys.argv)) and ("write" == sys.argv[1]): print("Dump written memory from file {0}:".format(sys.argv[2])) cap = pyshark.FileCapture(sys.argv[2], include_raw=True, use_json=True) addr = 0 for p in cap: if isRequest(p) and len(getData(p))>=4: P = Package(getData(p)) if isinstance(P.payload(), WriteOperation): W = P.payload() if (addr != W.addr()): print("") print ("-"*80) print("") addr = W.addr() print(W.payload().dump(" ", addr)) addr += len(W.payload()) else: print("") print(P.dump(" > ")) print("") else: print("oops")qdmr-0.12.3/doc/reveng/pinspect/000077500000000000000000000000001501654372000164235ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/pinspect/__init__.py000066400000000000000000000001151501654372000205310ustar00rootroot00000000000000from packethandler import PacketHandler from devicefilter import DeviceFilterqdmr-0.12.3/doc/reveng/pinspect/auctus_a6.py000066400000000000000000000114561501654372000206760ustar00rootroot00000000000000from devicefilter import DeviceFilter from streamhandler import StreamHandler from cdcacmfilter import CDCACMFilter from rawstreamdump import RawStreamDump from datagram import Datagram, RawDatagramDump, DatagramHandler from struct import unpack from utilities import hexDump from binascii import crc_hqx def crc16_simple_sum8(data:bytearray): return sum(data)&0xffff def crc16_simple_sum16(data:bytearray): s = 0 for i in range(len(data)//2): s += unpack(">H", data[2*i:(2*(i+1))])[0] return s&0xffff def crc16_simple_xor16(data:bytearray): s = 0 for i in range(len(data)//2): s ^= unpack(">H", data[2*i:(2*(i+1))])[0] return s def crc_summary(data:bytearray): print("CRC16-HQX : {:04X}".format(crc_hqx(data, 0))) print("CRC16-SUM8 : {:04X}".format(crc16_simple_sum8(data))) print("CRC16-SUM16: {:04X}".format(crc16_simple_sum16(data))) print("CRC16-XOR16: {:04X}".format(crc16_simple_xor16(data))) class AuctusA6Datagram(Datagram): def __init__(self, command, parameters, crc): self._isRequest = (0 == (command & 0x8000)) self._command = (command & 0x7fff) self._parameters = parameters self._crc = crc def isRequest(self): return self._isRequest def isResponse(self): return not self.isRequest() def command(self): return self._command def parameters(self): return self._parameters def format(self, prefix=""): res = prefix res += "type={}, command={:04X}, CRC={:02X}".format( ("Req" if self._isRequest else "Resp"), self._command, self._crc) if len(self._parameters): res += "\n"+hexDump(self._parameters, prefix=" "*len(prefix)+" |") return res def __repr__(self): return self.format() class AuctusA6Handler(StreamHandler): MODE_PASS = 0x00 MODE_DATAGRAM = 0x01 def __init__(self): super().__init__() self._tobuffer = bytearray() self._frombuffer = bytearray() self._mode = AuctusA6Handler.MODE_DATAGRAM self._dataLeft = None self._handler = [] def attach(self, handler): self._handler.append(handler) return self def handleToHost(self, data:bytearray): self._tobuffer.extend(data) if AuctusA6Handler().MODE_DATAGRAM == self._mode: while AuctusA6Handler().hasDatagram(self._tobuffer): dgram = AuctusA6Handler().popDatagram(self._tobuffer) self._processToHost(dgram) for handler in self._handler: if isinstance(handler, DatagramHandler): handler.handleToHost(dgram) else: self._dataLeft -= len(data) if 0 == self._dataLeft: self._mode = AuctusA6Handler.MODE_DATAGRAM for handler in self._handler: if isinstance(handler, StreamHandler): handler.handleToHost(self._tobuffer) crc_summary(self._tobuffer) self._tobuffer.clear() def handleFromHost(self, data:bytearray): self._frombuffer.extend(data) if AuctusA6Handler().MODE_DATAGRAM == self._mode: while AuctusA6Handler().hasDatagram(self._frombuffer): dgram = AuctusA6Handler().popDatagram(self._frombuffer) self._processFromHost(dgram) for handler in self._handler: if isinstance(handler, DatagramHandler): handler.handleFromHost(dgram) else: self._dataLeft -= len(data) if 0 == self._dataLeft: self._mode = AuctusA6Handler.MODE_DATAGRAM for handler in self._handler: if isinstance(handler, StreamHandler): handler.handleFromHost(self._frombuffer) crc_summary(self._frombuffer) self._frombuffer.clear() @staticmethod def hasDatagram(buffer: bytearray): if not buffer.startswith(b'\xaa'): return False if len(buffer) < 6: return False length, command = unpack(">xBH", buffer[:4]) return len(buffer) >= length @staticmethod def popDatagram(buffer: bytearray): length, command = unpack(">xBH", buffer[:4]) length, command, params, crc = unpack(">xBH{}sBx".format(length-6), buffer[:length]) del buffer[:length] return AuctusA6Datagram(command, params, crc) def _processFromHost(self, dgram:AuctusA6Datagram): if dgram.isRequest() and (0x0102 == dgram.command()): self._dataLeft, baudRate = unpack(">xxIxxI", dgram.parameters()) elif dgram.isRequest() and (0x0101 == dgram.command()): self._mode = AuctusA6Handler.MODE_PASS def _processToHost(self, dgram:AuctusA6Datagram): if dgram.isResponse() and (0x0102 == dgram.command()): self._mode = AuctusA6Handler.MODE_PASS elif dgram.isResponse() and (0x0100 == dgram.command()): self._dataLeft, = unpack(">xI10x", dgram.parameters()) if "__main__" == __name__: import sys stream = DeviceFilter(14).attach( CDCACMFilter().attach( AuctusA6Handler() .attach(RawDatagramDump()) .attach(RawStreamDump()))) stream.process(sys.argv[1]) qdmr-0.12.3/doc/reveng/pinspect/cdcacmfilter.py000066400000000000000000000013421501654372000214150ustar00rootroot00000000000000from packethandler import PacketHandler from streamhandler import StreamHandler from binascii import a2b_hex class CDCACMFilter(PacketHandler): USB_URB_INTERRUPT = 0x01 USB_URB_BULK = 0x03 def __init__(self): super().__init__() pass def attach(self, handler:StreamHandler): self._handler.append(handler) return self def handle(self, packet): if (int(packet.usb.transfer_type,base=16) != CDCACMFilter.USB_URB_BULK) or ("USB.CAPDATA" not in packet): return for handler in self._handler: if PacketHandler.isFromHost(packet): handler.handleFromHost(a2b_hex(packet["USB.CAPDATA_RAW"].value)) else: handler.handleToHost(a2b_hex(packet["USB.CAPDATA_RAW"].value)) qdmr-0.12.3/doc/reveng/pinspect/datagram.py000066400000000000000000000012341501654372000205550ustar00rootroot00000000000000from abc import ABCMeta, abstractmethod class Datagram(metaclass=ABCMeta): """ Base class of all data grams. """ def __init__(self): pass @abstractmethod def format(self, prefix=""): pass class DatagramHandler(metaclass=ABCMeta): def __init__(self): pass @abstractmethod def handleFromHost(self, dgram: Datagram): pass @abstractmethod def handleToHost(self, dgram: Datagram): pass class RawDatagramDump(DatagramHandler): def __init__(self): super().__init__() def handleFromHost(self, dgram:Datagram): print(dgram.format("< ")) def handleToHost(self, dgram:Datagram): print(dgram.format("> ")) qdmr-0.12.3/doc/reveng/pinspect/devicefilter.py000066400000000000000000000006421501654372000214440ustar00rootroot00000000000000from packethandler import PacketHandler class DeviceFilter(PacketHandler): """ Filters by device address. """ def __init__(self, device: int = None): """ :param device: Specifies the USB device address of radio. """ super().__init__() self._source = device def handle(self, packet): if ((None == self._source) or (int(packet.usb.device_address) == self._source)): super().handle(packet) qdmr-0.12.3/doc/reveng/pinspect/packethandler.py000066400000000000000000000014501501654372000216020ustar00rootroot00000000000000#!/usr/bin/env python3 """ THis module implements some basic packet handler, that can filter and process packets captured by wireshark. """ from abc import ABCMeta, abstractmethod from pyshark import FileCapture class PacketHandler(metaclass=ABCMeta): """ Abstract base class for all packet handlers. """ def __init__(self): self._handler = [] def attach(self, handler): self._handler.append(handler) return self @staticmethod def isFromHost(p): return "host" == p.usb.src @staticmethod def isToHost(p): return "host" == p.usb.dest def handle(self, packet): for handler in self._handler: handler.handle(packet) def process(self, filename:str): for packet in FileCapture(filename, include_raw=True, use_json=True): self.handle(packet) qdmr-0.12.3/doc/reveng/pinspect/rawstreamdump.py000066400000000000000000000006371501654372000216760ustar00rootroot00000000000000from utilities import hexDump from streamhandler import StreamHandler class RawStreamDump(StreamHandler): def __init__(self): super().__init__() self._toAddr = 0 self._fromAddr = 0 def handleFromHost(self, data): print(hexDump(data, "< ", self._fromAddr)) self._fromAddr+=len(data) def handleToHost(self, data): print(hexDump(data, "> ", self._toAddr)) self._toAddr+=len(data) qdmr-0.12.3/doc/reveng/pinspect/streamhandler.py000066400000000000000000000004041501654372000216240ustar00rootroot00000000000000from abc import ABCMeta, abstractmethod class StreamHandler(metaclass=ABCMeta): def __init__(self): pass @abstractmethod def handleFromHost(self, data:bytearray): pass @abstractmethod def handleToHost(self, data:bytearray): pass qdmr-0.12.3/doc/reveng/pinspect/utilities.py000066400000000000000000000010111501654372000210010ustar00rootroot00000000000000def hexDump(s: bytes, prefix: str = "", addr: int = 0) -> str: """ Utility function to hex-dump binary data. """ N = len(s) Nb = N//16 if (N%16): Nb += 1 res = "" for j in range(Nb): a,b = j*16, min((j+1)*16,N) h = " ".join(map("{:02x}".format, s[a:b])) h += " "*(16-(b-a)) t = "" for i in range(a,b): c = s[i] if c>=0x20 and c<0x7f: t += chr(c) else: t += "." res += (prefix + "{:08X} ".format(addr+16*j) + h + " | " + t + "\n") return res[:-1] qdmr-0.12.3/doc/reveng/radioddity/000077500000000000000000000000001501654372000167325ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/radioddity/gd73/000077500000000000000000000000001501654372000174765ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/radioddity/gd73/README.md000066400000000000000000000001211501654372000207470ustar00rootroot00000000000000# Reverse engineering of the GD73 serial protocol See `protocol.md` for details.qdmr-0.12.3/doc/reveng/radioddity/gd73/extract.py000077500000000000000000000044011501654372000215240ustar00rootroot00000000000000#!/usr/bin/env python3 import pyshark import struct import sys import binascii device = "1.255" device_in = "{}.2".format(device) device_out = "{}.1".format(device) def hexDump(s: bytes, prefix:str="", addr:int=0, compact:bool=False) -> str: """ Utility function to hex-dump binary data. """ N = len(s) Nb = N//16 if (N%16): Nb += 1 res = "" last_line = None compressing = False for j in range(Nb): a,b = j*16, min((j+1)*16,N) line = s[a:b] if (not compact) or (last_line != line): h = " ".join(map("{:02x}".format, line)) h += " "*(16-(b-a)) t = "" for i in range(a,b): c = s[i] if c>=0x20 and c<0x7f: t += chr(c) else: t += "." res += (prefix + "{:08X} ".format(addr+16*j) + h + " | " + t + "\n") last_line = line compressing = False elif not compressing: res += (prefix + "*\n") compressing = True return res[:-1] def isDataPacket(p): return ("USB.CAPDATA" in p) def isFromHost(p): return (("host" == p.usb.src) and (device_in == p.usb.dst)) def isFromDevice(p): return (("host" == p.usb.dst) and (device_out == p.usb.src)) def getData(p): if not isDataPacket(p): return None return binascii.a2b_hex(p["USB.CAPDATA_RAW"].value) class Packet: def __init__(self, data:bytes): self._flag, self._cmd, self._sub, self._pcrc, size = struct.unpack(" self._crc: self._crc += 0xffff; self._crc -= v if len(data)%2: v = struct.unpack("B", data[-1:])[0] if v > self._crc: self._crc +=0xffff; self._crc -= v; def __repr__(self): if 0 == len(self._payload): return "Packet flag={:02x}, cmd={:02x}, sub={:02x}, crc={:04x}.".format(self._flag, self._cmd, self._sub, self._crc) return "Packet flag={:02x}, cmd={:02x}, sub={:02x}, crc={:04x}\n{}".format(self._flag, self._cmd, self._sub, self._crc, hexDump(self._payload, " ")) cap = pyshark.FileCapture(sys.argv[1], include_raw=True, use_json=True) nextaddr = 0 for p in cap: if isDataPacket(p): print(Packet(getData(p))) qdmr-0.12.3/doc/reveng/radioddity/gd73/protocol.md000066400000000000000000000054341501654372000216670ustar00rootroot00000000000000# GD73 Serial Protocol ## General Request/Response Frame Format ``` 0 1 2 3 4 5 6 7 8 n-2 n-1 bytes +---+---+---+---+---+---+---+---+---+...+---+---+ 00 |68h|FLG|CMD|SUB| CRC? | Size | Payload |10h| +---+---+---+---+---+---+---+---+---+...+---+---+ ``` | Bytes | Value | Description | |:-------|:------|:------------| | 0 | 68 | Start of frame byte. Fixed. | | 1 | 0f | Flags? Fixed. | | 2:3 | 01 04 | First command send on read. Probably *enter prog mode*. No payload. | | | 00 02 | Response to *enter prog mode* | | | 01 02 | Start read. | | | 00 00 | Read response, contains sequence number and 35h bytes codeplug data. | | | 04 01 | Read ACK, triggers read of next segment. | | | 01 00 | Write request, usually 55bytes payload. | | | 00 01 | Write ACK. | | 4:5 | | CRC | | 6:7 | | The payload size in little endian. | | 8:(n-2)| | The request/response payload. | | n-1 | 10 | The end-of-frame byte. | ### CRC The CRC is computed over the entire packet, including start and end bytes. For the computation, the packet is interpreted as a sequence of little-endian unsigned 16-bit integer. If the packet does not align with 16bits, it gets 0-padded to a multiple of 16bit. Let `v[i]` the i-th of `N` unsigned 16bit integer formed by the 0-padded packet, the CRC is then computed as ``` uint32_t crc = 0xffff; for (int i=0; i | | <----- Response 0000 + Data ------- | | --------- ACK seq. 0000 ----------> | | <----- Response 0001 + Data ------- | | --------- ACK seq. 0001 ----------> | ... ... | <----- Response 0a42 + Data ------- | | --------- ACK seq. 0a42 ----------> | | <----- Response 0a43 + Data ------- | ``` ## Write request (0100h) The write request sends a sequence number and codeplug segment to the device. The device then ACKs the written segment. qdmr-0.12.3/doc/reveng/retevis/000077500000000000000000000000001501654372000162575ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/retevis/rt84/000077500000000000000000000000001501654372000170605ustar00rootroot00000000000000qdmr-0.12.3/doc/reveng/retevis/rt84/extract.py000077500000000000000000000023311501654372000211060ustar00rootroot00000000000000#!/usr/bin/env python3 import struct import sys def hexDump(s: bytes, prefix="", addr=0, compact=False) -> str: """ Utility function to hex-dump binary data. """ N = len(s) Nb = N//16 if (N%16): Nb += 1 res = "" last_line = None compressing = False for j in range(Nb): a,b = j*16, min((j+1)*16,N) line = s[a:b] if (not compact) or (last_line != line): h = " ".join(map("{:02x}".format, line)) h += " "*(16-(b-a)) t = "" for i in range(a,b): c = s[i] if c>=0x20 and c<0x7f: t += chr(c) else: t += "." res += (prefix + "{:08X} ".format(addr+16*j) + h + " | " + t + "\n") last_line = line compressing = False elif not compressing: res += (prefix + "*\n") compressing = True return res[:-1] if 2 > len(sys.argv): print("USAGE: extract.py CODEPLUG.rdt") rdt_filename = sys.argv[1] # list of sections: (file offset, size, destination address) sections = [ (0x002225, 0x03e000, 0x002000), (0x040230, 0x090000, 0x110000), ] # Read all data = open(rdt_filename, "br").read() for section in sections: offset, length, addr = section print(hexDump(data[offset:(offset+length)], "", addr, True)) qdmr-0.12.3/examples/000077500000000000000000000000001501654372000143615ustar00rootroot00000000000000qdmr-0.12.3/examples/kw.conf000066400000000000000000000520041501654372000156520ustar00rootroot00000000000000# # Configuration generated So. Juni 27 17:49:02 2021 by qdrm, version 0.8.0 # see https://dm3mat.darc.de/qdmr for details. # # Unique DMR ID and name (quoted) of this radio. ID: 2621370 Name: "DM3MAT" # Text displayed when the radio powers up (quoted). IntroLine1: "qDMR" IntroLine2: "DM3MAT" # Microphone amplification, value 1..10: MICLevel: 6 # Speech-synthesis ('On' or 'Off'): Speech: Off # Table of digital channels. # 1) Channel number. # 2) Name in quotes. E.g., "NAME" # 3) Receive frequency in MHz # 4) Transmit frequency or +/- offset in MHz # 5) Transmit power: Max, High, Mid, Low or Min # 6) Scan list: - or index in Scanlist table # 7) Transmit timeout timer in seconds: 0, 15, 30, 45... 555 # 8) Receive only: -, + # 9) Admit criteria: -, Free, Color # 10) Color code: 0, 1, 2, 3... 15 # 11) Time slot: 1 or 2 # 12) Receive group list: - or index in Grouplist table # 13) Contact for transmit: - or index in Contacts table # 14) GPS System: - or index in GPS table. # 15) Roaming zone: -, + or index in roaming-zone table. # 16) Radio ID: -, index in radio ID list above. - means default ID. # Digital Name Receive Transmit Power Scan TOT RO Admit CC TS RxGL TxC GPS Roam ID 1 "DB0LDS TS1" 439.56250 -7.60000 High - - - Color 1 1 1 6 - + - # Local 2 "Sa/Th DB0LDS TS1" 439.56250 -7.60000 High - - - Color 1 1 3 8 - + - # Sa/Th 3 "TG8 DB0LDS TS2" 439.56250 -7.60000 High - - - Color 1 2 2 5 - + - # Regional 4 "TG9 DB0LDS TS2" 439.56250 -7.60000 High 1 - - Color 1 2 2 6 - + - # Local 5 "BB DB0LDS TS2" 439.56250 -7.60000 High 1 - - Color 1 2 2 7 - + - # Bln/Brb 6 "HRV DB0LDS TS1" 439.56250 -7.60000 High 1 - - Color 1 1 7 11 - + - # HamRadio Village 7 "DM0TZN TS1" 438.82500 -7.60000 High - - - Color 1 1 1 1 - + - # WW 8 "TG8 DM0TZN TS2" 438.82500 -7.60000 High - - - Color 1 2 2 5 - + - # Regional 9 "TG9 DM0TZN TS2" 438.82500 -7.60000 High - - - Color 1 2 2 6 - + - # Local 10 "BB DM0TZN TS2" 438.82500 -7.60000 High - - - Color 1 2 2 7 - + - # Bln/Brb 11 "DB0LOS TS1" 438.47500 -7.60000 High - - - Color 1 1 1 6 - + - # Local 12 "R/TG9 DB0LOS TS2" 438.47500 -7.60000 High - - - Color 1 2 2 6 - + - # Local 13 "R/TG9 DM0TT TS1" 439.08750 -7.60000 High - - - Color 1 1 2 6 - + - # Local 14 "TG8 DM0TT TS2" 439.08750 -7.60000 High - - - Color 1 2 2 5 - + - # Regional 15 "TG9 DM0TT TS2" 439.08750 -7.60000 High - - - Color 1 2 2 6 - + - # Local 16 "BB DM0TT TS2" 439.08750 -7.60000 High - - - Color 1 2 2 7 - + - # Bln/Brb 17 "TG9 DB0KK TS1" 439.53750 -7.60000 High - - - Color 1 1 2 6 - + - # Local 18 "BB DB0KK TS2" 439.53750 -7.60000 High - - - Color 1 2 2 7 - + - # Bln/Brb 19 "DB0OUD TS1" 438.62500 -7.60000 High - - - Color 1 1 1 1 - + - # WW 20 "TG8 DB0OUD TS2" 438.62500 -7.60000 High - - - Color 1 2 2 5 - + - # Regional 21 "TG9 DB0OUD TS2" 438.62500 -7.60000 High - - - Color 1 2 2 6 - + - # Local 22 "BB DB0OUD TS2" 438.62500 -7.60000 High - - - Color 1 2 2 7 - + - # Bln/Brb 23 "TG9 DB0TU TS1" 439.57500 -7.60000 High - - - Color 1 1 2 6 - + - # Local 24 "BB DB0TU TS2" 439.57500 -7.60000 High - - - Color 1 2 2 7 - + - # Bln/Brb 25 "DM0MOT TS1" 439.52500 -7.60000 High - - - Color 1 1 1 1 - + - # WW 26 "TG8 DM0MOT TS2" 439.52500 -7.60000 High - - - Color 1 2 2 5 - + - # Regional 27 "TG9 DM0MOT TS2" 439.52500 -7.60000 High - - - Color 1 2 2 6 - + - # Local 28 "BB DM0MOT TS2" 439.52500 -7.60000 High - - - Color 1 2 2 7 - + - # Bln/Brb 29 "DB0TA TS1" 439.47500 -7.60000 High - - - Color 1 1 1 6 - + - # Local 30 "TG8 DB0TA TS2" 439.47500 -7.60000 High - - - Color 1 2 2 5 - + - # Regional 31 "TG9 DB0TA TS2" 439.47500 -7.60000 High - - - Color 1 2 2 6 - + - # Local 32 "BB DB0TA TS2" 439.47500 -7.60000 High - - - Color 1 2 2 7 - + - # Bln/Brb 33 "DB0FX TS1" 439.45000 -7.60000 High - - - Color 1 1 1 6 - + - # Local 34 "TG8 DB0FX TS2" 439.45000 -7.60000 High - - - Color 1 2 2 5 - + - # Regional 35 "TG9 DB0FX TS2" 439.45000 -7.60000 High - - - Color 1 2 2 6 - + - # Local 36 "BB DB0FX TS2" 439.45000 -7.60000 High - - - Color 1 2 2 7 - + - # Bln/Brb 37 "DB0PDM TS1" 438.40000 -7.60000 High - - - Color 1 1 1 6 - + - # Local 38 "TG8 DB0PDM TS2" 438.40000 -7.60000 High - - - Color 1 2 2 5 - + - # Regional 39 "TG9 DB0PDM TS2" 438.40000 -7.60000 High - - - Color 1 2 2 6 - + - # Local 40 "BB DB0PDM TS2" 438.40000 -7.60000 High - - - Color 1 2 2 7 - + - # Bln/Brb 41 "DB0BRB TS1" 439.48750 -7.60000 High - - - Color 1 1 1 6 - + - # Local 42 "TG8 DB0BRB TS2" 439.48750 -7.60000 High - - - Color 1 2 2 5 - + - # Regional 43 "TG9 DB0BRB TS2" 439.48750 -7.60000 High - - - Color 1 2 2 6 - + - # Local 44 "BB DB0BRB TS2" 439.48750 -7.60000 High - - - Color 1 2 2 7 - + - # Bln/Brb 45 "DB0SPN TS1" 439.48750 -7.60000 High - - - Color 1 1 1 6 - + - # Local 46 "R/TG9 DB0SPN TS2" 439.48750 -7.60000 High - - - Color 1 1 2 6 - + - # Local 47 "DB0NLS TS1" 439.51250 -7.60000 High - - - Color 1 1 1 6 - + - # Local 48 "R/TG9 DB0NLS TS2" 439.51250 -7.60000 High - - - Color 1 2 2 6 - + - # Local 49 "DB0LS TS1" 438.22500 -7.60000 High - - - Color 1 1 1 6 - + - # Local 50 "TG8 DB0LS TS2" 438.22500 -7.60000 High - - - Color 1 2 2 5 - + - # Regional 51 "TG9 DB0LS TS2" 438.22500 -7.60000 High - - - Color 1 2 2 6 - + - # Local 52 "BB DB0LS TS2" 438.22500 -7.60000 High - - - Color 1 2 2 7 - + - # Bln/Brb 53 "DM0LC TS1" 439.38750 -7.60000 High - - - Color 1 1 1 6 - + - # Local 54 "TG8 DM0LC TS2" 439.38750 -7.60000 High - - - Color 1 2 3 5 - + - # Regional 55 "TG9 DM0LC TS2" 439.38750 -7.60000 High - - - Color 1 2 3 6 - + - # Local 56 "Sa/Th DM0LC TS2" 439.38750 -7.60000 High - - - Color 1 2 3 8 - + - # Sa/Th 57 "BB DM0LC TS1" 439.38750 -7.60000 High - - - Color 1 1 2 7 - + - # Bln/Brb 58 "DB0FLW TS1" 439.53750 -7.60000 High - - - Color 1 1 1 6 - + - # Local 59 "TG8 DB0FLW TS2" 439.53750 -7.60000 High - - - Color 1 2 3 5 - + - # Regional 60 "TG9 DB0FLW TS2" 439.53750 -7.60000 High - - - Color 1 2 3 6 - + - # Local 61 "Sa/Th DB0FLW TS2" 439.53750 -7.60000 High - - - Color 1 2 3 8 - + - # Sa/Th 62 "DB0LE TS1" 439.57500 -7.60000 High - - - Color 1 1 1 6 - + - # Local 63 "R/TG9 DB0LE TS2" 439.57500 -7.60000 High - - - Color 1 2 3 6 - + - # Local 64 "DL DB0AFZ TS1" 439.97500 -9.40000 High - - - Color 1 1 1 3 - + - # DL 65 "BB DB0AFZ TS1" 439.97500 -9.40000 High - - - Color 1 1 2 7 - + - # Bln/Brb 66 "TG9 DB0AFZ TS2" 439.97500 -9.40000 High - - - Color 1 2 5 6 - + - # Local 67 "Hes DB0AFZ TS2" 439.97500 -9.40000 High - - - Color 1 2 5 10 - + - # Hessen 68 "DMR S0" 433.45000 433.45000 High - - - Free 1 1 6 12 - + - # All Call 69 "DMR S1" 433.61200 433.61200 High - - - Free 1 1 6 12 - + - # All Call 70 "DMR S2" 433.62500 433.62500 High - - - Free 1 1 6 12 - + - # All Call 71 "DMR S3" 433.63800 433.63800 High - - - Free 1 1 6 12 - + - # All Call 72 "DMR S4" 433.65000 433.65000 High - - - Free 1 1 6 12 - + - # All Call 73 "DMR S5" 433.66300 433.66300 High - - - Free 1 1 6 12 - + - # All Call 74 "DMR S6" 433.67500 433.67500 High - - - Free 1 1 6 12 - + - # All Call 75 "DMR S7" 433.68800 433.68800 High - - - Free 1 1 6 12 - + - # All Call # Table of analog channels. # 1) Channel number. # 2) Name in quotes. # 3) Receive frequency in MHz # 4) Transmit frequency or +/- offset in MHz # 5) Transmit power: Max, High, Mid, Low or Min # 6) Scan list: - or index # 7) Transmit timeout timer in seconds: 0, 15, 30, 45... 555 # 8) Receive only: -, + # 9) Admit criteria: -, Free, Tone # 10) Squelch level: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 # 11) CTCSS/DCS for receive: frequency (e.g, 67.0), DCS number (e.g., n023 or i023) or '-' to disable # 12) CTCSS/DCS for transmit: frequency (e.g, 67.0), DCS number (e.g., n023 or i023) or '-' to disable # 13) Bandwidth in kHz: 12.5, 25 # 14) APRS system: - or index # Analog Name Receive Transmit Power Scan TOT RO Admit Squelch RxTone TxTone Width APRS 76 "DB0LDS" 439.56250 -7.60000 High - - - - 1 67.0 67.0 12.5 - 77 "DB0RAG" 439.30000 -7.60000 High - - - - 1 - - 12.5 - 78 "DB0LUD" 438.57500 -7.60000 High - - - - 1 67.0 67.0 12.5 - 79 "DB0SP-2" 145.60000 -0.60000 High - - - - 1 - - 12.5 - 80 "DB0SP-70" 439.42500 -7.60000 High - - - - 1 - - 12.5 - 81 "DB0ZOD-2" 145.66250 -0.60000 High - - - - 1 - - 12.5 - 82 "DB0ZOD-70" 438.72500 -7.60000 High - - - - 1 - - 12.5 - 83 "DB0BRL" 145.72500 -0.60000 High - - - - 1 - - 12.5 - 84 "DB0BLO" 439.27500 -7.60000 High - - - - 1 - - 12.5 - 85 "DB0SX" 439.05000 -7.60000 High - - - - 1 - - 12.5 - 86 "DB0TA" 439.12500 -7.60000 High - - - - 1 - - 12.5 - 87 "DB0PDM" 145.67500 -0.60000 High - - - - 1 - - 12.5 - 88 "DB0CBS" 438.85000 -7.60000 High - - - - 1 - - 12.5 - 89 "DB0AF" 438.63750 -7.60000 High - - - - 1 - - 12.5 - 90 "DB0NFL" 438.95000 -7.60000 High - - - - 1 - - 12.5 - 91 "DB0LMM" 439.30000 -7.60000 High - - - - 1 - - 12.5 - 92 "DM0LEI" 439.15000 -7.60000 High - - - - 1 - - 12.5 - 93 "DB0LEI" 145.77500 -0.60000 High - - - - 1 - - 12.5 - 94 "DB0SML" 439.27500 -7.60000 High - - - - 1 - - 12.5 - 95 "DB0LSA" 145.66250 -0.60000 High - - - - 1 - - 12.5 - 96 "DB0LE" 438.78750 -7.60000 High - - - - 1 - - 12.5 - 97 "DOK Y07" 144.67500 144.67500 High - - - Free 1 - - 12.5 - 98 "DOK D20" 144.65000 144.65000 High - - - Free 1 - - 12.5 - 99 "DOK D23" 145.37500 145.37500 High - - - Free 1 - - 12.5 - 100 "DOK S31" 144.72500 144.72500 High - - - Free 1 - - 12.5 - 101 "S20" 145.30000 145.30000 High - - - Free 1 - - 12.5 - 102 "2m Mobil" 145.50000 145.50000 High - - - Free 1 - - 12.5 - 103 "70cm Mobil" 433.50000 433.50000 High - - - Free 1 - - 12.5 - 104 "APRS" 144.80000 144.80000 High - - - Free 1 - - 12.5 2 105 "ISS APRS" 145.82500 145.82500 High - - - Free 1 - - 12.5 - 106 "ISS FM Rep" 437.80000 -291.81000 High - - - - 1 - 67.0 12.5 - 107 "CAS-3H FM" 437.20000 -292.85000 High - - - - 1 - - 12.5 - 108 "PO-101 FM" 145.90000 437.50000 High - - - - 1 - - 12.5 - 109 "SO-50 FM" 436.79000 -290.94000 High - - - - 1 - 67.0 12.5 - 110 "AO-92 FM" 145.88000 435.35000 High - - - - 1 - 67.0 12.5 - 111 "AO-91 FM" 145.96000 435.25000 High - - - - 1 - 67.0 12.5 - 112 "U0" 430.20000 430.20000 Low - - - - 1 - - 12.5 - 113 "U1" 430.22500 430.22500 Low - - - - 1 - - 12.5 - 114 "U2" 430.25000 430.25000 Low - - - - 1 - - 12.5 - 115 "U3" 430.27500 430.27500 Low - - - - 1 - - 12.5 - 116 "PMR 1" 446.00625 446.00625 Low - - - Free 1 - - 12.5 - 117 "PMR 2" 446.01875 446.01875 Low - - - Free 1 - - 12.5 - 118 "PMR 3" 446.03125 446.03125 Low - - - Free 1 - - 12.5 - 119 "PMR 4" 446.04375 446.04375 Low - - - Free 1 - - 12.5 - 120 "PMR 5" 446.05625 446.05625 Low - - - Free 1 - - 12.5 - 121 "PMR 6" 446.06875 446.06875 Low - - - Free 1 - - 12.5 - 122 "PMR 7" 446.08125 446.08125 Low - - - Free 1 - - 12.5 - 123 "PMR 8" 446.09375 446.09375 Low - - - Free 1 - - 12.5 - 124 "PMR 9" 446.10625 446.10625 Low - - - Free 1 - - 12.5 - 125 "PMR 10" 446.11875 446.11875 Low - - - Free 1 - - 12.5 - 126 "PMR 11" 446.13125 446.13125 Low - - - Free 1 - - 12.5 - 127 "PMR 12" 446.14375 446.14375 Low - - - Free 1 - - 12.5 - 128 "PMR 13" 446.15625 446.15625 Low - - - Free 1 - - 12.5 - 129 "PMR 14" 446.16875 446.16875 Low - - - Free 1 - - 12.5 - 130 "PMR 15" 446.18125 446.18125 Low - - - Free 1 - - 12.5 - 131 "PMR 16" 446.19375 446.19375 Low - - - Free 1 - - 12.5 - # Table of channel zones. # 1) Zone number # 2) Name in quotes. # 3) VFO: Either A or B. # 4) List of channels: numbers and ranges (N-M) separated by comma # Zone Name VFO Channels 1 "KW" A 1,4,5,2,6,11,12,13,15,16,76,77,78,79 1 "KW" B 97,101,102,103,68,69,70,71,72,73,74,75 2 "TH Wildau" A 112,113,114,115 3 "Hessen" A 64,66,67 3 "Hessen" B 102,68,69,70,71,72,73,74,75 4 "Berlin" A 16,18,22,24,28,32,36,79,80,81,82,83,84,85,86 4 "Berlin" B 98,99,102,103,68,69,70,71,72,73,74,75 5 "Potsdam" A 37,38,39,40,41,42,43,44,79,80,87 5 "Potsdam" B 102,103,68,69,70,71,72,73,74,75 6 "Cottbus" A 45,46,47,48,49,50,51,52,88,89,90 6 "Cottbus" B 102,103,68,69,70,71,72,73,74,75 7 "Leipzig" A 53,55,56,57,58,60,61,62,63,91,92,93,94,95,96 7 "Leipzig" B 100,102,103,68,69,70,71,72,73,74,75 8 "Sat" A 106,111,110,109,108,107 8 "Sat" B 105,106,111,110,109,108,107 9 "PMR" A 116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131 9 "PMR" B 116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131 # Table of scan lists. # 1) Scan list number. # 2) Name in quotes. # 3) Priority channel 1 (50% of scans): -, Sel or index # 4) Priority channel 2 (25% of scans): -, Sel or index # 5) Designated transmit channel: Last, Sel or index # 6) List of channels: numbers, Sel or ranges (N-M) separated by comma # Scanlist Name PCh1 PCh2 TxCh Channels 1 "KW" - - - 4,5,76,77,97,102,68,112 2 "BerBra" - - - 5,10,16,18,22,24,32,36,28,40,44,52 # Table of GPS systems. # 1) GPS system ID # 2) Name in quotes. # 3) Destination contact ID. # 4) Update period: period in ms # 5) Revert channel ID or '-'. # GPS Name Dest Period Revert 1 "BM ARPS" 21 300 - # Table of APRS systems (there is usually only one). # 1) APRS system ID # 2) Name in quotes. # 3) Transmit channel ID. # 4) Transmit period in seconds. # 5) Your (source) call and SSID as CALLSIGN-SSID. # 6) Destination call and SSID as CALLSIGN-SSID. # 7) Path string or '-'. # 7) Icon name. # 8) Message, optional message in quotes. # APRS Name Channel Period Source Destination Path Icon Message 2 "APRS APAT81" 104 300 DM3MAT-7 APAT81-0 "WIDE1-1WIDE2-1" "Jogger" "Y07, QRG 144.675" # Table of contacts. # 1) Contact number. # 2) Name in quotes. # 3) Call type: Group, Private, All or DTMF # 4) Call ID: 1...16777215 or string with DTMF number # 5) Call receive tone: -, + # Contact Name Type ID RxTone 1 "WW" Group 91 - 2 "EU" Group 92 - 3 "DL" Group 262 - 4 "DL MM" Group 263 - 5 "Regional" Group 8 - 6 "Local" Group 9 - 7 "Bln/Brb" Group 2621 - 8 "Sa/Th" Group 2629 - 9 "SA/MV" Group 2620 - 10 "Hessen" Group 2626 - 11 "HamRadio Village" Group 3177826 - 12 "All Call" All 16777215 - 13 "Simplex TG99" Group 99 - 14 "Cl Brandenburg" Group 26209 - 15 "Cl Berlin" Group 26212 - 16 "Refl Brandenburg" Private 4044 - 17 "Refl Berlin" Private 4016 - 18 "BM SMS" Private 262993 - 19 "BM DAPNET" Private 262994 - 20 "BM Parrot" Private 262997 - 21 "BM APRS" Private 262999 - 22 "DG2RON Ronny" Private 2621246 + 23 "DM9KS Silvio" Private 2621045 + 24 "DM3MAT Hannes" Private 2621370 + 25 "DL2BQF Thomas" Private 2621437 + 26 "DL7JOM Olaf" Private 2621004 + 27 "DG1RPH Peter" Private 2621528 + 28 "DO1RPH Richard" Private 2634160 - # Table of group lists. # 1) Group list number. # 2) Name in quotes. # 3) List of contacts: numbers and ranges (N-M) separated by comma # Grouplist Name Contacts 1 "DL" 1,2,3 2 "Berlin/Brand" 6,5,7 3 "Sa/Th" 5,6,8 4 "SA/MV" 5,6,9 5 "Hessen" 5,6,10 6 "Simplex" 6,13,12 7 "Ham Radio Village" 11 # Table of roaming zones. # 1) Roaming zone number # 2) Name in quotes. # 3) List of digital channels: numbers and ranges (N-M) separated by comma # Roaming Name Channels 1 "Berlin/Brand" 3,8,14,18,20,24,26,30,34,38,42,50 qdmr-0.12.3/examples/kw.yaml000066400000000000000000001453101501654372000156720ustar00rootroot00000000000000--- version: 0.12.0 settings: introLine1: qDMR introLine2: DM3MAT micLevel: 6 speech: false power: High squelch: 1 vox: 0 tot: 0 defaultID: id1 radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} contacts: - dmr: {id: cont1, name: WW, ring: false, type: GroupCall, number: 91} - dmr: {id: cont2, name: EU, ring: false, type: GroupCall, number: 92} - dmr: {id: cont3, name: DL, ring: false, type: GroupCall, number: 262} - dmr: {id: cont4, name: DL MM, ring: false, type: GroupCall, number: 263} - dmr: {id: cont5, name: Regional, ring: false, type: GroupCall, number: 8} - dmr: {id: cont6, name: Local, ring: false, type: GroupCall, number: 9} - dmr: {id: cont7, name: Bln/Brb, ring: false, type: GroupCall, number: 2621} - dmr: {id: cont8, name: Sa/Th, ring: false, type: GroupCall, number: 2629} - dmr: {id: cont9, name: SA/MV, ring: false, type: GroupCall, number: 2620} - dmr: {id: cont10, name: Hessen, ring: false, type: GroupCall, number: 2626} - dmr: {id: cont11, name: HamRadio Village, ring: false, type: GroupCall, number: 98638} - dmr: {id: cont12, name: Chaoswelle, ring: false, type: GroupCall, number: 26223} - dmr: {id: cont13, name: All Call, ring: false, type: AllCall, number: 16777215} - dmr: {id: cont14, name: Simplex TG99, ring: false, type: GroupCall, number: 99} - dmr: {id: cont15, name: Cl Brandenburg, ring: false, type: GroupCall, number: 26209} - dmr: {id: cont16, name: Cl Berlin, ring: false, type: GroupCall, number: 26212} - dmr: {id: cont17, name: Refl Brandenburg, ring: false, type: PrivateCall, number: 4044} - dmr: {id: cont18, name: Refl Berlin, ring: false, type: PrivateCall, number: 4016} - dmr: {id: cont19, name: BM SMS, ring: false, type: PrivateCall, number: 262993} - dmr: {id: cont20, name: BM DAPNET, ring: false, type: PrivateCall, number: 262994} - dmr: {id: cont21, name: BM Parrot, ring: false, type: PrivateCall, number: 262997} - dmr: {id: cont22, name: BM APRS, ring: false, type: PrivateCall, number: 262999} - dmr: {id: cont23, name: DG2RON Ronny, ring: true, type: PrivateCall, number: 2621246} - dmr: {id: cont24, name: DM9KS Silvio, ring: true, type: PrivateCall, number: 2621045} - dmr: {id: cont25, name: DM3MAT Hannes, ring: true, type: PrivateCall, number: 2621370} - dmr: {id: cont26, name: DL2BQF Thomas, ring: true, type: PrivateCall, number: 2621437} - dmr: {id: cont27, name: DL7JOM Olaf, ring: true, type: PrivateCall, number: 2621004} - dmr: {id: cont28, name: DG1RPH Peter, ring: true, type: PrivateCall, number: 2621528} - dmr: {id: cont29, name: DO1RPH Richard, ring: false, type: PrivateCall, number: 2634160} groupLists: - {id: grp1, name: DL, contacts: [cont1, cont2, cont3]} - {id: grp2, name: Berlin/Brand, contacts: [cont6, cont5, cont7, cont11]} - {id: grp3, name: Sa/Th, contacts: [cont5, cont6, cont8]} - {id: grp4, name: SA/MV, contacts: [cont5, cont6, cont9]} - {id: grp5, name: Hessen, contacts: [cont5, cont6, cont10]} - {id: grp6, name: Simplex, contacts: [cont6, cont14]} - {id: grp7, name: Ham Radio Village, contacts: [cont11, cont12]} channels: - digital: id: ch1 name: DB0LDS TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont6 roaming: ! "" rxFrequency: 439.5625 MHz txFrequency: 431.9625 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch2 name: Sa/Th DB0LDS TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp3 contact: cont8 roaming: ! "" rxFrequency: 439.5625 MHz txFrequency: 431.9625 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch3 name: TG8 DB0LDS TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont5 roaming: ! "" rxFrequency: 439.5625 MHz txFrequency: 431.9625 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch4 name: TG9 DB0LDS TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 439.5625 MHz txFrequency: 431.9625 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch5 name: BB DB0LDS TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont7 aprs: aprs1 roaming: ! "" rxFrequency: 439.5625 MHz txFrequency: 431.9625 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch6 name: HRV DB0LDS TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp7 contact: cont11 roaming: ! "" rxFrequency: 439.5625 MHz txFrequency: 431.9625 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch7 name: ChW DB0LDS TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp7 contact: cont12 roaming: ! "" rxFrequency: 439.5625 MHz txFrequency: 431.9625 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch8 name: BB DB0VIP TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont7 aprs: aprs1 rxFrequency: 431.75 MHz txFrequency: 439.35 MHz power: ! "" timeout: ! "" vox: ! "" - digital: id: ch9 name: DM0TZN TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont1 roaming: ! "" rxFrequency: 438.825 MHz txFrequency: 431.225 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch10 name: TG8 DM0TZN TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont5 roaming: ! "" rxFrequency: 438.825 MHz txFrequency: 431.225 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch11 name: TG9 DM0TZN TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 438.825 MHz txFrequency: 431.225 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch12 name: BB DM0TZN TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont7 roaming: ! "" rxFrequency: 438.825 MHz txFrequency: 431.225 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch13 name: DB0LOS TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont6 roaming: ! "" rxFrequency: 438.475 MHz txFrequency: 430.875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch14 name: R/TG9 DB0LOS TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 438.475 MHz txFrequency: 430.875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch15 name: R/TG9 DM0TT TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 439.0875 MHz txFrequency: 431.4875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch16 name: TG8 DM0TT TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont5 roaming: ! "" rxFrequency: 439.0875 MHz txFrequency: 431.4875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch17 name: TG9 DM0TT TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 439.0875 MHz txFrequency: 431.4875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch18 name: BB DM0TT TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont7 roaming: ! "" rxFrequency: 439.0875 MHz txFrequency: 431.4875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch19 name: TG9 DB0KK TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 439.5375 MHz txFrequency: 431.9375 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch20 name: BB DB0KK TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont7 roaming: ! "" rxFrequency: 439.5375 MHz txFrequency: 431.9375 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch21 name: DB0OUD TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont1 roaming: ! "" rxFrequency: 438.625 MHz txFrequency: 431.025 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch22 name: TG8 DB0OUD TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont5 roaming: ! "" rxFrequency: 438.625 MHz txFrequency: 431.025 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch23 name: TG9 DB0OUD TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 438.625 MHz txFrequency: 431.025 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch24 name: BB DB0OUD TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont7 roaming: ! "" rxFrequency: 438.625 MHz txFrequency: 431.025 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch25 name: TG9 DB0TU TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 439.575 MHz txFrequency: 431.975 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch26 name: BB DB0TU TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont7 roaming: ! "" rxFrequency: 439.575 MHz txFrequency: 431.975 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch27 name: DM0MOT TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont1 roaming: ! "" rxFrequency: 439.525 MHz txFrequency: 431.925 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch28 name: TG8 DM0MOT TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont5 roaming: ! "" rxFrequency: 439.525 MHz txFrequency: 431.925 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch29 name: TG9 DM0MOT TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 439.525 MHz txFrequency: 431.925 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch30 name: BB DM0MOT TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont7 roaming: ! "" rxFrequency: 439.525 MHz txFrequency: 431.925 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch31 name: DB0TA TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont6 roaming: ! "" rxFrequency: 439.475 MHz txFrequency: 431.875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch32 name: TG8 DB0TA TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont5 roaming: ! "" rxFrequency: 439.475 MHz txFrequency: 431.875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch33 name: TG9 DB0TA TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 439.475 MHz txFrequency: 431.875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch34 name: BB DB0TA TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont7 roaming: ! "" rxFrequency: 439.475 MHz txFrequency: 431.875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch35 name: DB0FX TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont6 roaming: ! "" rxFrequency: 439.45 MHz txFrequency: 431.85 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch36 name: TG8 DB0FX TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont5 roaming: ! "" rxFrequency: 439.45 MHz txFrequency: 431.85 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch37 name: TG9 DB0FX TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 439.45 MHz txFrequency: 431.85 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch38 name: BB DB0FX TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont7 roaming: ! "" rxFrequency: 439.45 MHz txFrequency: 431.85 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch39 name: DB0PDM TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont6 roaming: ! "" rxFrequency: 438.4 MHz txFrequency: 430.8 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch40 name: TG8 DB0PDM TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont5 roaming: ! "" rxFrequency: 438.4 MHz txFrequency: 430.8 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch41 name: TG9 DB0PDM TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 438.4 MHz txFrequency: 430.8 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch42 name: BB DB0PDM TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont7 roaming: ! "" rxFrequency: 438.4 MHz txFrequency: 430.8 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch43 name: DB0BRB TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont6 roaming: ! "" rxFrequency: 439.4875 MHz txFrequency: 431.8875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch44 name: TG8 DB0BRB TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont5 roaming: ! "" rxFrequency: 439.4875 MHz txFrequency: 431.8875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch45 name: TG9 DB0BRB TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 439.4875 MHz txFrequency: 431.8875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch46 name: BB DB0BRB TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont7 roaming: ! "" rxFrequency: 439.4875 MHz txFrequency: 431.8875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch47 name: DB0SPN TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont6 roaming: ! "" rxFrequency: 439.4875 MHz txFrequency: 431.8875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch48 name: R/TG9 DB0SPN TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 439.4875 MHz txFrequency: 431.8875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch49 name: DB0NLS TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont6 roaming: ! "" rxFrequency: 439.5125 MHz txFrequency: 431.9125 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch50 name: R/TG9 DB0NLS TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 439.5125 MHz txFrequency: 431.9125 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch51 name: DB0LS TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont6 roaming: ! "" rxFrequency: 438.225 MHz txFrequency: 430.625 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch52 name: TG8 DB0LS TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont5 roaming: ! "" rxFrequency: 438.225 MHz txFrequency: 430.625 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch53 name: TG9 DB0LS TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont6 roaming: ! "" rxFrequency: 438.225 MHz txFrequency: 430.625 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch54 name: BB DB0LS TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp2 contact: cont7 roaming: ! "" rxFrequency: 438.225 MHz txFrequency: 430.625 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch55 name: DM0LC TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont6 roaming: ! "" rxFrequency: 439.3875 MHz txFrequency: 431.7875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch56 name: TG8 DM0LC TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp3 contact: cont5 roaming: ! "" rxFrequency: 439.3875 MHz txFrequency: 431.7875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch57 name: TG9 DM0LC TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp3 contact: cont6 roaming: ! "" rxFrequency: 439.3875 MHz txFrequency: 431.7875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch58 name: Sa/Th DM0LC TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp3 contact: cont8 roaming: ! "" rxFrequency: 439.3875 MHz txFrequency: 431.7875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch59 name: BB DM0LC TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp2 contact: cont7 roaming: ! "" rxFrequency: 439.3875 MHz txFrequency: 431.7875 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch60 name: DB0FLW TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont6 roaming: ! "" rxFrequency: 439.5375 MHz txFrequency: 431.9375 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch61 name: TG8 DB0FLW TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp3 contact: cont5 roaming: ! "" rxFrequency: 439.5375 MHz txFrequency: 431.9375 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch62 name: TG9 DB0FLW TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp3 contact: cont6 roaming: ! "" rxFrequency: 439.5375 MHz txFrequency: 431.9375 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch63 name: Sa/Th DB0FLW TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp3 contact: cont8 roaming: ! "" rxFrequency: 439.5375 MHz txFrequency: 431.9375 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch64 name: DB0LE TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont6 roaming: ! "" rxFrequency: 439.575 MHz txFrequency: 431.975 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch65 name: R/TG9 DB0LE TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp3 contact: cont6 roaming: ! "" rxFrequency: 439.575 MHz txFrequency: 431.975 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch66 name: DL DB0AFZ TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp1 contact: cont3 roaming: ! "" rxFrequency: 439.975 MHz txFrequency: 430.575 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch67 name: BB DB0AFZ TS1 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp2 contact: cont7 roaming: ! "" rxFrequency: 439.975 MHz txFrequency: 430.575 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch68 name: TG9 DB0AFZ TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp5 contact: cont6 roaming: ! "" rxFrequency: 439.975 MHz txFrequency: 430.575 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch69 name: Hes DB0AFZ TS2 rxOnly: false admit: ColorCode colorCode: 1 timeSlot: TS2 radioId: ! "" groupList: grp5 contact: cont10 roaming: ! "" rxFrequency: 439.975 MHz txFrequency: 430.575 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch70 name: DMR S0 rxOnly: false admit: Free colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp6 contact: cont13 roaming: ! "" rxFrequency: 433.45 MHz txFrequency: 433.45 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch71 name: DMR S1 rxOnly: false admit: Free colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp6 contact: cont13 roaming: ! "" rxFrequency: 433.612 MHz txFrequency: 433.612 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch72 name: DMR S2 rxOnly: false admit: Free colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp6 contact: cont13 roaming: ! "" rxFrequency: 433.625 MHz txFrequency: 433.625 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch73 name: DMR S3 rxOnly: false admit: Free colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp6 contact: cont13 roaming: ! "" rxFrequency: 433.638 MHz txFrequency: 433.638 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch74 name: DMR S4 rxOnly: false admit: Free colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp6 contact: cont13 roaming: ! "" rxFrequency: 433.65 MHz txFrequency: 433.65 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch75 name: DMR S5 rxOnly: false admit: Free colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp6 contact: cont13 roaming: ! "" rxFrequency: 433.663 MHz txFrequency: 433.663 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch76 name: DMR S6 rxOnly: false admit: Free colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp6 contact: cont13 roaming: ! "" rxFrequency: 433.675 MHz txFrequency: 433.675 MHz power: High timeout: ! "" vox: ! "" - digital: id: ch77 name: DMR S7 rxOnly: false admit: Free colorCode: 1 timeSlot: TS1 radioId: ! "" groupList: grp6 contact: cont13 roaming: ! "" rxFrequency: 433.688 MHz txFrequency: 433.688 MHz power: High timeout: ! "" vox: ! "" - analog: id: ch78 name: DB0LDS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 439.5625 MHz txFrequency: 431.9625 MHz power: High timeout: ! "" vox: ! "" rxTone: {ctcss: 67} txTone: {ctcss: 67} squelch: ! "" - analog: id: ch79 name: DB0RAG rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 439.3 MHz txFrequency: 431.7 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch80 name: DB0LUD rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 438.575 MHz txFrequency: 430.975 MHz power: High timeout: ! "" vox: ! "" rxTone: {ctcss: 67} txTone: {ctcss: 67} squelch: ! "" - analog: id: ch81 name: DB0SP-2 rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.6 MHz txFrequency: 145 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch82 name: DB0SP-70 rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 439.425 MHz txFrequency: 431.825 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch83 name: DB0ZOD-2 rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.6625 MHz txFrequency: 145.0625 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch84 name: DB0ZOD-70 rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 438.725 MHz txFrequency: 431.125 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch85 name: DB0BRL rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.725 MHz txFrequency: 145.125 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch86 name: DB0BLO rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 439.275 MHz txFrequency: 431.675 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch87 name: DB0SX rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 439.05 MHz txFrequency: 431.45 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch88 name: DB0TA rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 439.125 MHz txFrequency: 431.525 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch89 name: DB0PDM rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.675 MHz txFrequency: 145.075 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch90 name: DB0CBS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 438.85 MHz txFrequency: 431.25 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch91 name: DB0AF rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 438.6375 MHz txFrequency: 431.0375 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch92 name: DB0NFL rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 438.95 MHz txFrequency: 431.35 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch93 name: DB0LMM rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 439.3 MHz txFrequency: 431.7 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch94 name: DM0LEI rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 439.15 MHz txFrequency: 431.55 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch95 name: DB0LEI rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.775 MHz txFrequency: 145.175 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch96 name: DB0SML rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 439.275 MHz txFrequency: 431.675 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch97 name: DB0LSA rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.6625 MHz txFrequency: 145.0625 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch98 name: DB0LE rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 438.7875 MHz txFrequency: 431.1875 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch99 name: DOK Y07 rxOnly: false admit: Free bandwidth: Narrow rxFrequency: 144.675 MHz txFrequency: 144.675 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch100 name: DOK D20 rxOnly: false admit: Free bandwidth: Narrow rxFrequency: 144.65 MHz txFrequency: 144.65 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch101 name: DOK D23 rxOnly: false admit: Free bandwidth: Narrow rxFrequency: 145.375 MHz txFrequency: 145.375 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch102 name: DOK S31 rxOnly: false admit: Free bandwidth: Narrow rxFrequency: 144.725 MHz txFrequency: 144.725 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch103 name: S20 rxOnly: false admit: Free bandwidth: Narrow rxFrequency: 145.3 MHz txFrequency: 145.3 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch104 name: 2m Mobil rxOnly: false admit: Free bandwidth: Narrow rxFrequency: 145.5 MHz txFrequency: 145.5 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch105 name: 70cm Mobil rxOnly: false admit: Free bandwidth: Narrow rxFrequency: 433.5 MHz txFrequency: 433.5 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch106 name: APRS rxOnly: false admit: Free bandwidth: Narrow aprs: aprs2 rxFrequency: 144.8 MHz txFrequency: 144.8 MHz power: High timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch107 name: ISS APRS rxOnly: false admit: Free bandwidth: Narrow rxFrequency: 145.825 MHz txFrequency: 145.825 MHz power: High timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch108 name: ISS FM Rep rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.8 MHz txFrequency: 145.99 MHz power: High timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch109 name: CAS-3H FM rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.2 MHz txFrequency: 144.35 MHz power: High timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch110 name: PO-101 FM rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.9 MHz txFrequency: 437.5 MHz power: High timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch111 name: SO-50 FM rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.79 MHz txFrequency: 145.85 MHz power: High timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch112 name: AO-92 FM rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.88 MHz txFrequency: 435.35 MHz power: High timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch113 name: AO-91 FM rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.96 MHz txFrequency: 435.25 MHz power: High timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch114 name: U0 rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 430.2 MHz txFrequency: 430.2 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch115 name: U1 rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 430.225 MHz txFrequency: 430.225 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch116 name: U2 rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 430.25 MHz txFrequency: 430.25 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch117 name: U3 rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 430.275 MHz txFrequency: 430.275 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch118 name: PMR 1 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.00625 MHz txFrequency: 446.00625 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch119 name: PMR 2 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.01875 MHz txFrequency: 446.01875 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch120 name: PMR 3 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.03125 MHz txFrequency: 446.03125 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch121 name: PMR 4 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.04375 MHz txFrequency: 446.04375 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch122 name: PMR 5 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.05625 MHz txFrequency: 446.05625 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch123 name: PMR 6 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.06875 MHz txFrequency: 446.06875 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch124 name: PMR 7 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.08125 MHz txFrequency: 446.08125 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch125 name: PMR 8 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.09375 MHz txFrequency: 446.09375 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch126 name: PMR 9 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.10625 MHz txFrequency: 446.10625 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch127 name: PMR 10 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.11875 MHz txFrequency: 446.11875 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch128 name: PMR 11 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.13125 MHz txFrequency: 446.13125 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch129 name: PMR 12 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.14375 MHz txFrequency: 446.14375 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch130 name: PMR 13 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.15625 MHz txFrequency: 446.15625 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch131 name: PMR 14 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.16875 MHz txFrequency: 446.16875 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch132 name: PMR 15 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.18125 MHz txFrequency: 446.18125 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch133 name: PMR 16 rxOnly: false scanListRef: scan3 admit: Free bandwidth: Narrow rxFrequency: 446.19375 MHz txFrequency: 446.19375 MHz power: Low timeout: ! "" vox: ! "" squelch: ! "" zones: - id: zone1 name: KW A: [ch1, ch4, ch5, ch8, ch2, ch6, ch7, ch13, ch14, ch15, ch17, ch18, ch78, ch79, ch80, ch81] B: [ch99, ch103, ch104, ch105, ch70, ch71, ch72, ch73, ch74, ch75, ch76, ch77] - id: zone2 name: TH Wildau A: [ch114, ch115, ch116, ch117] B: [] - id: zone3 name: Hessen A: [ch66, ch68, ch69] B: [ch104, ch70, ch71, ch72, ch73, ch74, ch75, ch76, ch77] - id: zone4 name: Berlin A: [ch18, ch20, ch24, ch26, ch30, ch34, ch38, ch81, ch82, ch83, ch84, ch85, ch86, ch87, ch88] B: [ch100, ch101, ch104, ch105, ch70, ch71, ch72, ch73, ch74, ch75, ch76, ch77] - id: zone5 name: Potsdam A: [ch39, ch40, ch41, ch42, ch43, ch44, ch45, ch46, ch81, ch82, ch89] B: [ch104, ch105, ch70, ch71, ch72, ch73, ch74, ch75, ch76, ch77] - id: zone6 name: Cottbus A: [ch47, ch48, ch49, ch50, ch51, ch52, ch53, ch54, ch90, ch91, ch92] B: [ch104, ch105, ch70, ch71, ch72, ch73, ch74, ch75, ch76, ch77] - id: zone7 name: Leipzig A: [ch55, ch57, ch58, ch59, ch60, ch62, ch63, ch64, ch65, ch93, ch94, ch95, ch96, ch97, ch98] B: [ch102, ch104, ch105, ch70, ch71, ch72, ch73, ch74, ch75, ch76, ch77] - id: zone8 name: Sat A: [ch108, ch113, ch112, ch111, ch110, ch109] B: [ch107, ch108, ch113, ch112, ch111, ch110, ch109] - id: zone9 name: PMR A: [ch118, ch119, ch120, ch121, ch122, ch123, ch124, ch125, ch126, ch127, ch128, ch129, ch130, ch131, ch132, ch133] B: [ch118, ch119, ch120, ch121, ch122, ch123, ch124, ch125, ch126, ch127, ch128, ch129, ch130, ch131, ch132, ch133] scanLists: - id: scan1 name: KW channels: [ch4, ch5, ch78, ch79, ch99, ch104, ch70, ch114] - id: scan2 name: BerBra channels: [ch5, ch12, ch18, ch20, ch24, ch26, ch34, ch38, ch30, ch42, ch46, ch54] - id: scan3 name: PMR channels: [ch118, ch119, ch120, ch121, ch122, ch123, ch124, ch125, ch126, ch127, ch128, ch129, ch130, ch131, ch132, ch133] positioning: - dmr: id: aprs1 name: BM ARPS period: 300 contact: cont22 - aprs: id: aprs2 name: APRS APAT81 period: 300 revert: ch106 icon: Jogger message: Y07, QRG 144.675 destination: APAT81-0 source: DM3MAT-7 path: [WIDE1-1, WIDE2-1] roamingChannels: - id: rch1 name: R DB0LDS rxFrequency: 439.5625 MHz txFrequency: 431.9625 MHz colorCode: 1 - id: rch2 name: R DM0TZN rxFrequency: 438.825 MHz txFrequency: 431.225 MHz colorCode: 1 - id: rch3 name: R DM0TT rxFrequency: 439.0875 MHz txFrequency: 431.4875 MHz colorCode: 1 - id: rch4 name: R DB0KK rxFrequency: 439.5375 MHz txFrequency: 431.9375 MHz colorCode: 1 - id: rch5 name: R DB0OUD rxFrequency: 438.625 MHz txFrequency: 431.025 MHz colorCode: 1 - id: rch6 name: R DB0TU rxFrequency: 439.575 MHz txFrequency: 431.975 MHz colorCode: 1 - id: rch7 name: R DM0MOT rxFrequency: 439.525 MHz txFrequency: 431.925 MHz colorCode: 1 - id: rch8 name: R DB0TA rxFrequency: 439.475 MHz txFrequency: 431.875 MHz colorCode: 1 - id: rch9 name: R DB0FX rxFrequency: 439.45 MHz txFrequency: 431.85 MHz colorCode: 1 - id: rch10 name: R DB0PDM rxFrequency: 438.4 MHz txFrequency: 430.8 MHz colorCode: 1 - id: rch11 name: R DB0BRB rxFrequency: 439.4875 MHz txFrequency: 431.8875 MHz colorCode: 1 - id: rch12 name: R DB0LS rxFrequency: 438.225 MHz txFrequency: 430.625 MHz colorCode: 1 roamingZones: - id: roam1 name: Berlin/Brand channels: - rch1 - rch2 - rch3 - rch4 - rch5 - rch6 - rch7 - rch8 - rch9 - rch10 - rch11 - rch12 commercial: encryptionKeys: [] sms: format: DMR smsTemplates: - id: sms1 name: signal strength message: rssi - id: sms2 name: weather message: wx ... qdmr-0.12.3/examples/minimal.conf000066400000000000000000000104641501654372000166630ustar00rootroot00000000000000# # Configuration generated Sa. März 27 11:44:12 2021 by qdrm, version 0.6.2 # see https://dm3mat.darc.de/qdmr for details. # # Unique DMR ID and name (quoted) of this radio. ID: 2621370 Name: "DM3MAT" # Text displayed when the radio powers up (quoted). IntroLine1: "" IntroLine2: "" # Microphone amplification, value 1..10: MICLevel: 2 # Speech-synthesis ('On' or 'Off'): Speech: Off # Table of digital channels. # 1) Channel number. # 2) Name in quotes. E.g., "NAME" # 3) Receive frequency in MHz # 4) Transmit frequency or +/- offset in MHz # 5) Transmit power: Max, High, Mid, Low or Min # 6) Scan list: - or index in Scanlist table # 7) Transmit timeout timer in seconds: 0, 15, 30, 45... 555 # 8) Receive only: -, + # 9) Admit criteria: -, Free, Color # 10) Color code: 0, 1, 2, 3... 15 # 11) Time slot: 1 or 2 # 12) Receive group list: - or index in Grouplist table # 13) Contact for transmit: - or index in Contacts table # 14) GPS System: - or index in GPS table. # 15) Roaming zone: -, + or index in roaming-zone table. # Digital Name Receive Transmit Power Scan TOT RO Admit CC TS RxGL TxC GPS Roam 1 "L9 DB0LDS" 439.56250 -7.60000 High - - - - 1 2 1 1 - - # Local 2 "BB DB0LDS" 439.56250 -7.60000 High - - - - 1 2 1 3 - - # BB 3 "DL DB0LDS" 439.56250 -7.60000 High - - - - 1 1 2 4 - - # DL # Table of analog channels. # 1) Channel number. # 2) Name in quotes. # 3) Receive frequency in MHz # 4) Transmit frequency or +/- offset in MHz # 5) Transmit power: Max, High, Mid, Low or Min # 6) Scan list: - or index # 7) Transmit timeout timer in seconds: 0, 15, 30, 45... 555 # 8) Receive only: -, + # 9) Admit criteria: -, Free, Tone # 10) Squelch level: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 # 11) CTCSS/DCS for receive: frequency (e.g, 67.0), DCS number (e.g., n023 or i023) or '-' to disable # 12) CTCSS/DCS for transmit: frequency (e.g, 67.0), DCS number (e.g., n023 or i023) or '-' to disable # 13) Bandwidth in kHz: 12.5, 25 # 14) APRS system: - or index # Analog Name Receive Transmit Power Scan TOT RO Admit Squelch RxTone TxTone Width APRS # Table of channel zones. # 1) Zone number # 2) Name in quotes. # 3) VFO: Either A or B. # 4) List of channels: numbers and ranges (N-M) separated by comma # Zone Name VFO Channels 1 "Zu Hause" A 1,2 1 "Zu Hause" B 3 # Table of scan lists. # 1) Scan list number. # 2) Name in quotes. # 3) Priority channel 1 (50% of scans): -, Sel or index # 4) Priority channel 2 (25% of scans): -, Sel or index # 5) Designated transmit channel: Last, Sel or index # 6) List of channels: numbers, Sel or ranges (N-M) separated by comma # Scanlist Name PCh1 PCh2 TxCh Channels # Table of GPS systems. # 1) GPS system ID # 2) Name in quotes. # 3) Destination contact ID. # 4) Update period: period in ms # 5) Revert channel ID or '-'. # GPS Name Dest Period Revert # Table of APRS systems (there is usually only one). # 1) APRS system ID # 2) Name in quotes. # 3) Transmit channel ID. # 4) Transmit period in seconds. # 5) Your (source) call and SSID as CALLSIGN-SSID. # 6) Destination call and SSID as CALLSIGN-SSID. # 7) Path string or '-'. # 7) Icon name. # 8) Message, optional message in quotes. # APRS Name Channel Period Source Destination Path Icon Message # Table of contacts. # 1) Contact number. # 2) Name in quotes. # 3) Call type: Group, Private, All or DTMF # 4) Call ID: 1...16777215 or string with DTMF number # 5) Call receive tone: -, + # Contact Name Type ID RxTone 1 "Local" Group 9 - 2 "Regional" Group 8 - 3 "BB" Group 2621 - 4 "DL" Group 262 - # Table of group lists. # 1) Group list number. # 2) Name in quotes. # 3) List of contacts: numbers and ranges (N-M) separated by comma # Grouplist Name Contacts 1 "Local" 1,2,3 2 "DL" 4 # Table of roaming zones. # 1) Roaming zone number # 2) Name in quotes. # 3) List of digital channels: numbers and ranges (N-M) separated by comma # Roaming Name Channels qdmr-0.12.3/examples/minimal.yaml000066400000000000000000000042171501654372000166770ustar00rootroot00000000000000--- version: 0.9.0 settings: introLine1: DM3MAT introLine2: qDMR micLevel: 3 speech: false power: High squelch: 1 vox: 0 tot: 0 defaultID: id1 radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} contacts: - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9} - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8} - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621} - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262} - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999} - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826} groupLists: - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]} - {id: grp2, name: DL, contacts: [cont4]} - {id: grp3, name: HamRadioVillage, contacts: [cont6]} channels: - digital: id: ch1 name: L9 DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont1 power: High timeout: 0 vox: 0 - digital: id: ch2 name: BB DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont3 aprs: aprs1 power: High timeout: 0 vox: 0 - digital: id: ch3 name: DL DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp2 contact: cont4 power: High timeout: 0 vox: 0 - digital: id: ch4 name: HRV DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp3 contact: cont6 power: High timeout: 0 vox: 0 zones: - id: zone1 name: Zu Hause A: [ch1, ch2, ch4] B: [ch3] positioning: - dmr: id: aprs1 name: GPS System period: 300 contact: cont5 ... qdmr-0.12.3/examples/sat.yaml000066400000000000000000000424221501654372000160400ustar00rootroot00000000000000--- version: 0.12.0 settings: introLine1: FM Sat introLine2: DM3MAT micLevel: 6 speech: false power: Max squelch: 0 vox: 0 tot: 0 defaultID: id1 radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} contacts: [] groupLists: [] channels: - analog: id: ch1 name: ISS APRS rxOnly: false admit: Free bandwidth: Narrow rxFrequency: 145.825 MHz txFrequency: 145.825 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch2 name: ISS AOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.81 MHz txFrequency: 145.9875 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch3 name: ISS UP rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.805 MHz txFrequency: 145.99 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch4 name: ISS APO rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.8 MHz txFrequency: 145.99 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch5 name: ISS DWN rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.795 MHz txFrequency: 145.99 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch6 name: ISS LOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.79 MHz txFrequency: 145.9925 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch7 name: CAS-3H AOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.21 MHz txFrequency: 144.3525 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch8 name: CAS-3H UP rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.205 MHz txFrequency: 144.35 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch9 name: CAS-3H APO rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.2 MHz txFrequency: 144.35 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch10 name: CAS-3H DWN rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.195 MHz txFrequency: 144.35 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch11 name: CAS-3H LOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.19 MHz txFrequency: 144.3475 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch12 name: CAS-5A AOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 435.61 MHz txFrequency: 145.9225 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch13 name: CAS-5A UP rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 435.605 MHz txFrequency: 145.925 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch14 name: CAS-5A APO rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 435.6 MHz txFrequency: 145.925 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch15 name: CAS-5A DWN rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 435.595 MHz txFrequency: 145.925 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch16 name: CAS-5A LOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 435.59 MHz txFrequency: 145.9275 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch17 name: CAS-7A AOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 435.7 MHz txFrequency: 145.8975 MHz power: ! "" timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch18 name: CAS-7A UP rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 435.695 MHz txFrequency: 145.9 MHz power: ! "" timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch19 name: CAS-7A APO rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 435.69 MHz txFrequency: 145.9 MHz power: ! "" timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch20 name: CAS-7A DWN rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 435.685 MHz txFrequency: 145.9 MHz power: ! "" timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch21 name: CAS-7A LOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 435.68 MHz txFrequency: 145.9025 MHz power: ! "" timeout: ! "" vox: ! "" squelch: ! "" - analog: id: ch22 name: PO-101 AOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.9025 MHz txFrequency: 437.59 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 141.300003} squelch: 0 - analog: id: ch23 name: PO-101 UP rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.9 MHz txFrequency: 437.495 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 141.300003} squelch: 0 - analog: id: ch24 name: PO-101 APO rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.9 MHz txFrequency: 437.5 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 141.300003} squelch: 0 - analog: id: ch25 name: PO-101 DWN rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.9 MHz txFrequency: 437.505 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 141.300003} squelch: 0 - analog: id: ch26 name: PO-101 LOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.8975 MHz txFrequency: 437.51 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 141.300003} squelch: 0 - analog: id: ch27 name: SO-50 EN rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.79 MHz txFrequency: 145.85 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 74.4000015} squelch: 0 - analog: id: ch28 name: SO-50 AOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.8 MHz txFrequency: 145.8525 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch29 name: SO-50 UP rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.795 MHz txFrequency: 145.85 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch30 name: SO-50 APO rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.79 MHz txFrequency: 145.85 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch31 name: SO-50 DWN rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.785 MHz txFrequency: 145.85 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch32 name: SO-50 LOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.77 MHz txFrequency: 145.8475 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch33 name: AO-27 AOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.805 MHz txFrequency: 145.8475 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch34 name: AO-27 UP rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.8 MHz txFrequency: 145.85 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch35 name: AO-27 APO rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.795 MHz txFrequency: 145.85 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch36 name: AO-27 DWN rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.79 MHz txFrequency: 145.85 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch37 name: AO-27 LOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.785 MHz txFrequency: 145.8525 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch38 name: AO-92 AOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.8825 MHz txFrequency: 435.34 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch39 name: AO-92 UP rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.88 MHz txFrequency: 435.345 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch40 name: AO-92 APO rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.88 MHz txFrequency: 435.35 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch41 name: AO-92 DWN rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.88 MHz txFrequency: 435.355 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch42 name: AO-92 LOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.8775 MHz txFrequency: 435.36 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch43 name: AO-91 AOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.9625 MHz txFrequency: 435.24 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch44 name: AO-91 UP rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.96 MHz txFrequency: 435.245 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch45 name: AO-91 APO rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.96 MHz txFrequency: 435.25 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch46 name: AO-91 DWN rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.96 MHz txFrequency: 435.255 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch47 name: AO-91 LOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 145.9575 MHz txFrequency: 435.26 MHz power: ! "" timeout: ! "" vox: ! "" txTone: {ctcss: 67} squelch: 0 - analog: id: ch48 name: TEVEL AOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.41 MHz txFrequency: 145.9675 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch49 name: TEVEL UP rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.405 MHz txFrequency: 145.97 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch50 name: TEVEL APO rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.4 MHz txFrequency: 145.97 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch51 name: TEVEL DWN rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.395 MHz txFrequency: 145.97 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch52 name: TEVEL LOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 436.39 MHz txFrequency: 145.9725 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch53 name: UVSQ AOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.03 MHz txFrequency: 145.9025 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch54 name: UVSQ UP rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.025 MHz txFrequency: 145.905 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch55 name: UVSQ APO rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.02 MHz txFrequency: 145.905 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch56 name: UVSQ DWN rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.015 MHz txFrequency: 145.905 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 - analog: id: ch57 name: UVSQ LOS rxOnly: false admit: Always bandwidth: Narrow rxFrequency: 437.01 MHz txFrequency: 145.9075 MHz power: ! "" timeout: ! "" vox: ! "" squelch: 0 zones: - id: zone1 name: AO-27 A: [ch33, ch34, ch35, ch36, ch37] B: [] - id: zone2 name: AO-91 FOX-1B A: [ch43, ch44, ch45, ch46, ch47] B: [] - id: zone3 name: AO-92 FOX-1D A: [ch38, ch39, ch40, ch41, ch42] B: [] - id: zone4 name: CAS-3H Lilac-2 A: [ch7, ch8, ch9, ch10, ch11] B: [] - id: zone5 name: CAS-5A FO-118 A: [ch12, ch13, ch14, ch15, ch16] B: [] - id: zone6 name: CAS-7A A: [ch17, ch18, ch19, ch20, ch21] B: [] - id: zone7 name: ISS FM Rep A: [ch2, ch3, ch4, ch5, ch6] B: [] - id: zone8 name: PO-101 Diwata-2 A: [ch22, ch23, ch24, ch25, ch26] B: [] - id: zone9 name: SO-50 SaudiSat-1C A: [ch27, ch28, ch29, ch30, ch31, ch32] B: [] - id: zone10 name: TEVEL 1-8 A: [ch48, ch49, ch50, ch51, ch52] B: [] - id: zone11 name: UVSQ A: [ch53, ch54, ch55, ch56, ch57] B: [] commercial: encryptionKeys: [] ... qdmr-0.12.3/i18n/000077500000000000000000000000001501654372000133225ustar00rootroot00000000000000qdmr-0.12.3/i18n/Makefile000066400000000000000000000001341501654372000147600ustar00rootroot00000000000000update: lupdate ../src/*.cc ../src/*.hh ../src/*.ui ../shared/ui/*.ui -noobsolete -ts *.ts qdmr-0.12.3/i18n/de.ts000066400000000000000000004707251501654372000143010ustar00rootroot00000000000000 APRSSystem [None] [Keine] Police station Polizeiwache Digipeater Digipeater Phone Telefon DX cluster DX Cluster HF gateway HF-Gateway Plane small kleines Flugzeug Mobile Satellite station mobile Satellitenstation Wheel Chair Rollstuhl Snowmobile Scheemobil Red cross Rotes Kreuz Boy scout Pfadfinder Home zu Hause X X Red dot roter Punkt Circle 0 Kreis 0 Circle 1 Kreis 1 Circle 2 Kreis 2 Circle 3 Kreis 3 Circle 4 Kreis 4 Circle 5 Kreis 5 Circle 6 Kreis 6 Circle 7 Kreis 7 Circle 8 Kreis 8 Circle 9 Kreis 9 Fire Feuer Campground Campingplatz Motorcycle Motorrad Rail engine Lok Car Auto File server Dateiserver HC Future Hurrikan Vorhersage Aid station Erste Hilfe BBS Mailbox Canoe Kanu Eyeball Augapfel Tractor Traktor Grid Square Grid Square Hotel Hotel TCP/IP TCP/IP School Schule Logon Logon MacOS MacOS NTS station NTS-Station Balloon Ballon Police car Polizeiauto TBD TBD RV RV Shuttle Shuttle SSTV SSTV Bus Bus ATV ATV Weather service Wetterdienst Helo Helo Yacht Yacht MS Windows MS Windows Jogger Jogger Triangle Dreieck PBBS PBBS Plane large großes Flugzeug Weather station Wetterstation Dish antenna Parabolantenne Ambulance Krankenwagen Bike Fahrrad ICP ICP Fire station Feuerwache Horse Pferd Fire truck Löschzug Glider Gleiter Hospital Krankenhaus IOTA IOTA Jeep Jeep Truck small kleiner Truck Laptop Laptop Mic-E Mic-E Node Knoten EOC EOC Rover Rover Grid Grid Antenna Antenne Power boat Motorboot Truck stop Rastplatz Truck large großer Truck Van Van Water Wasser XAPRS XAPRS Yagi Yagi Shelter Schutzhütte APRSSystemDialog Create APRS system APRS System erzeugen Edit APRS system APRS System bearbeiten [Selected] [Ausgewählt] AboutDialog About qdmr Über qdmr Supported Radios Unterstützte Geräte AnalogChannelDialog [None] [Kein] Edit Analog Channel Analog Kanal bearbeiten <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and CTCSS tones are filled in (if applicable).</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Hinweis:</span> qdmr bietet Autovervollständigung für Kanäle an, wenn Sie anfangen, das Rufzeichen eines Repeaters einzugeben. Nach drei eingegebenen Zeichen wird eine Anfrage an repeaterbook.com geschickt, um die Informationen der passenden Repeater abzurufen. Diese Anfragen können einen kurzen Moment dauern. Die erhaltenen Ergebnisse werden lokal zwischengespeichert.</p><p>Eine drop-down Liste wird erscheinen, die es Ihnen erlaubt, einen Repeater auszuwählen. Wenn Sie einen Repeater ausgewählt haben, werden die RX &amp; TX Frequenzen sowie die CTCSS Subtoneinstellungen ausgefüllt, insofern vorhanden.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">ausblenden</span></a></p></body></html> Basic Basis Name Name Rx Frequency RX Frequenz Tx Frequency TX Frequenz Power Leistung Max Maximum High Hoch Mid Mittel Low Niedrig Min Minimum Default Standard Tx Timeout TX Timeout Off Aus VOX Level VOX Sensitivität Rx Only Nur RX Scan List Scanliste Tx Admit TX Voraussetzung Always Immer Channel Free Kanal frei Tone Subton Squelch Rauschsperre Rx Tone RX Subton Tx Tone TX Subton Bandwidth Bandbreite Narrow (12.5 kHz) Schmal (12.5 kHz) Wide (25 kHz) Breit (25 kHz) APRS APRS Open Offen Extensions Erweiterungen Application Unsaved changes to codeplug. Nicht gespeicherte Änderungen am Codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. Es gibt nicht gespeicherte Änderungen am Codeplug. Diese gehen verloren, wenn Sie fortfahren. Open codeplug Codeplug öffnen Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*) Codeplug Dateien (*.yaml);;Codeplug Dateien, altes Format (*.conf *.csv *.txt);;Alle Dateien (*) Cannot open file Kann Datei nicht öffnen Cannot read codeplug from file '%1': %2 Kann Codeplug nicht aus Datei '%1' lesen: %2 Cannot read codeplug. Kann Codeplug nicht lesen. Save codeplug Codeplug speichern Codeplug Files (*.yaml *.yml) Codeplug Dateien (*.yaml *.yml) Please use new YAML format. Nutze bitte das neue YAML-Format. Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works. Das Speichern der Config im alten tabellenbasierten Format wurde mit Version 0.9.0 deaktiviert. Das Einlesen dieser Dateien funktioniert nach wie vor. Cannot save codeplug to file '%1': %2 Kann Codeplug nicht in Datei '%1' speichern: %2 Cannot save codeplug Kann Codeplug nicht speichern Cannot save codeplug to file '%1'. Kann Codeplug nicht in Datei '%1' speichern. Export codeplug Codeplug exportieren CHIRP CSV Files (*.csv) CHIRP CSV Dateien (*.csv) Cannot export codeplug Kann Codeplug nicht exportieren Cannot export codeplug to file '%1': %2 Kann Codeplug nich in die Datei '%1' exportieren: %2 Import codeplug Codeplug importieren CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml) CHIRP CSV Dateien (*.csv);;YAML Dateien (*.yaml *.yml) Cannot import codeplug Kann Codeplug nicht importieren Cannot import codeplug from '%1': %2 Kann Codeplug nicht aus Datei '%1' importieren: %2 Do not know, how to handle file '%1'. Kann Datei '%1' nicht verabreiten. No matching devices found. Kein passendes Gerät gefunden. Cannot connect to radio Verbindung zum Radio nicht möglich Cannot connect to radio: %1 Kann keine Verbindung zum Gerät herstellen: %1 Radio found Gerät gefunden Found device '%1'. Gerät '%1' gefunden. Verification success Verifizierung erfolgreich The codeplug was successfully verified with the radio '%1' Der aktuelle Codeplug passt zum Gerät '%1'. Read ... Lese … Read error Lesefehler Read complete Gelesen Upload ... Schreibe … Cannot write call-sign DB. Kann Rufzeichendatenbank nicht schreiben. The detected radio '%1' does not support a call-sign DB. Das Gerät '%1' unterstützt keine Rufzeichendatenbank. The detected radio '%1' does support a call-sign DB. This feature, however, is not implemented yet. Das Gerät '%1' unterstützt eine Rufzeichendatenbank. Diese Feature wurde jedoch noch nicht implementiert. QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set. qdmr kuratiert die Rufzeichendatenbank anhand Ihrer DMR ID. Es wurde keine Standard ID definiert. Write call-sign DB ... Schreibe Rufzeichendatenbank … Cannot write satellite config. Kann Satelliteneinstellungen nicht schreiben. The detected radio '%1' does not support satellite tracking. Das erkannte Funkgerät '%1' unterstützt kein Statellitentracking. The detected radio '%1' does support satellite tracking. This feature, however, is not implemented yet. Das erkannte Funkgerät bietet zwar Satellitentracking, jedoch ist das Feature noch nicht implementiert. Write satellite config ... Satellitenkonfiguration schreiben … Write error Schreibfehler Write complete Geschrieben %1 (alias for %2 %3) %1 (Alias für %2 %3) No radio found Kein Gerät erkannt No matching device was found. Kein bekanntes Gerät erkannt. ChannelListView Select a single channel first Wählen sie zunächst einen Kanal aus. To clone a channel, please select a single channel to clone. Um einen Kanal zu klonen, wählen Sie zunächst einen Kanal aus. Cannot delete channel Kann Kanal nicht löschen Cannot delete channel: You have to select a channel first. Kann Kanal nicht löschen: Wählen Sie zunächst einen Kanal aus. Delete channel? Kanal löschen? Delete channel %1? Kanal '%1' löschen? Delete %1 channels? %1 Kanäle löschen? Add Analog Channel Analogkanal hinzufügen Alt+A Alt+A Add Digital Channel Digitalkanal hinzufügen Alt+D Alt+D Clone Channel Kanal klonen Alt+C Alt+C Delete Channel Kanal löschen Alt+- Alt+- ChannelListWrapper FM FM DMR DMR [Default] [Standard] Max Maximum High Hoch Mid Mittel Low Niedrig Min Minimum Off Aus On Ein Always Immer Free Frei Color Farbcode Tone Subton [None] [Kein] Open Offen Wide Breit Narrow Schmal Type Typ Name Name Rx Frequency RX Frequenz Tx Frequency TX Frequenz Power Leistung Timeout Timeout Rx Only Nur RX Admit Sendevoraussetzung Scanlist Scanliste Zones Zonen CC CC TS TS RX Group List Empfangsgruppe TX Contact TX Kontakt DMR ID DMR ID GPS/APRS GPS/APRS Roaming Roaming Squelch Rauschsperre Rx Tone RX Subton Tx Tone TX Subton Bandwidth Bandbreite ChannelRefListWrapper Channel Kanal ChannelSelectionDialog Select a channel: Wählen Sie einen Kanal aus: ConfigMergeDialog Merging codeplugs ... Codeplugs vereinigen ... <html><head/><body><p><span style=" font-weight:600;">Conflict resolution strategies:</span></p><p>If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Konfliktlösungsstrategien:</span></p><p>Wenn einige importierte Objekte (Kanäle, Kontakte, ...) schon existieren, wähle aus, wie diese Konflikte aufgelöst werden für einzelne Objekte und Listen.</p></body></html> Items are all atomic objects like radio IDs, channels, contacts and roaming channels. Objekte sind atomische Elemente wie Radio IDs, Kanäle, Kontakte und Roamingkamäle. Items Objekte Ignore Ignorieren Override Überschreiben Duplicate Duplizieren Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones. Listen sind alle Elemente, die andere Objekte entalten wie Empfangsgruppen, Scanlisten und Roamingzonen. Sets Listen Merge Vereinigen Ignores any duplicate item. Doppelungen ignorieren. Replaces any duplicate item with the imported one. Ersetzt jedes doppelte Objekt mit dem importieten Objekt. Imports any duplicate item with a modified name. Importiert jedes doppelte Objekt mit einem veränderten Namen. Ignores any duplicate set. Ignoriert jede Doppelung. Replaces any duplicate set with the imported one. Ersetzt jede doppelte Liste mit der importierten Liste. Imports any duplicate set with a modified name. Importiert doppelte Listen unter veränderten Namen. Merges duplicate sets. Vereinigt doppelte Listen. ConfigObjectListView Cannot move items. Kann Elemente nicht verschieben. Cannot move items: You have to select at least one item first. Kann Elemente nicht verschieben: Wählen Sie zunächst mind. ein Element aus. Move selected item(s) to the top. Verschiebe die ausgewählten Elemente an den Anfang. Move selected item(s) ten positions up. Verschiebe die ausgewählten Elemente zehn Positionen nach oben. Move selected item(s) one position up. Verschiebe die ausgewählten Elemente um eine Position nach oben. Move selected item(s) one position down. Verschiebe die ausgewählten Elemente um eine Position nach unten. Move selected item(s) ten positions down. Verschiebe die ausgewählten Elemente um zehn Position nach unten. Move selected item(s) to the bottom. Verschiebe die ausgewählten Elemente ans Ende. ConfigObjectTableView Cannot move items. Kann Elemente nicht verschieben Cannot move items: You have to select at least one item first. Kann Elemente nicht verschieben: Wählen Sie zunächst mind. ein Element aus. Move selected item(s) to the top. Verschiebe die ausgewählten Elemente an den Anfang. Move selected item(s) ten positions up. Verschiebe die ausgewählten Elemente zehn Positionen nach oben. Move selected item(s) one position up. Verschiebe die ausgewählten Elemente um eine Position nach oben. Move selected item(s) one position down. Verschiebe die ausgewählten Elemente um eine Position nach unten. Move selected item(s) ten positions down. Verschiebe die ausgewählten Elemente um zehn Position nach unten. Move selected item(s) to the bottom. Verschiebe die ausgewählten Elemente ans Ende. ConfigObjectTypeSelectionDialog An instance of %1. Eine Instanz von '%1'. <p>%1<p><p style="margin-left:10px;">%2</p> <p>%1<p><p style="margin-left:10px;">%2</p> Create extension object Erweiterungsobjekt erzeugen Select the class of object to create Wählen Sie die Klasse des Objektes aus, das Sie erzeugen wollen. ContactListView Cannot delete contact Kann Kontakt nicht löschen Cannot delete contact: You have to select a contact first. Kann Kontakt nicht löschen: Wählen Sie zunächst einen Kontakt aus. Delete contact? Kontakt löschen? Delete contact %1? Kontakt '%1' löschen? Delete contacts? Kontakte löschen? Delete %1 contacts? Sollen %1 Kontakte gelöscht werden? Adds a contact to the list. Fügt einen Kontakt zu Liste hinzu. Add Digital Contact Digitalkontakt hinzufügen Alt++ Alt++ Add DTMF Contact DTMF-Kontakt hinzufügen Delete contact button Dieser Button löscht den ausgewählten Kontakt. Delete Contact Kontakt löschen Alt+- Alt+- ContactListWrapper DTMF DTMF On An Off Aus Private Call Direktruf Group Call Gruppenruf All Call Rundruf Type Typ Name Name Number Nummer RX Tone RX Subton DMRContactDialog Create DMR Contact DMR Kontakt erstellen Edit DMR Contact DMR Kontakt bearbeiten Private Call Direktruf Group Call Gruppenruf All Call Rundruf Basic Basis Type Typ Name Name Number Nummer Ring Klingelton Extensions Erweiterungen DMRIDDialog Basic Basis Name Name DMR ID DMR ID Extensions Erweiterungen DTMFContactDialog Create DTMF Contact DTMF Contakt erstellen Edit DMR Contact DTMF Kontakt bearbeiten Basic Basis Name Name Number Nummer Ring Klingelton Extensions Erweiterungen DeviceSelectionDialog Select a device Wählen Sie ein Gerät aus <html><head/><body><p>There is either more than one device detected or the one found is not considered save to access. Either way, select the device to use.</p></body></html> Entweder wurde mehr als ein Gerät gefunden oder das gefundene Gerät wird als unsicher betrachtet. Wählen Sie dennoch ein Gerät aus. DigitalChannelDialog [None] [Kein] [Default] [Standard] Edit Digital Channel Digitalkanal bearbeiten <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and color-code are filled in.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Hinweis:</span> qdmr bietet Autovervollständigung für Kanäle an, wenn Sie anfangen, das Rufzeichen eines Repeaters einzugeben. Nach drei eingegebenen Zeichen wird eine Anfrage an repeaterbook.com geschickt, um die Informationen der passenden Repeater abzurufen. Diese Anfragen können einen kurzen Moment dauern. Die erhaltenen Ergebnisse werden lokal zwischengespeichert.</p><p>Eine drop-down Liste wird erscheinen, die es Ihnen erlaubt, einen Repeater auszuwählen. Wenn Sie einen Repeater ausgewählt haben, werden die RX &amp; TX Frequenzen sowie die Farbcodeeinstellung ausgefüllt.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Basis Name Name Rx Frequency RX Frequenz Tx Frequency TX Frequenz Power Leistung Tx Timeout TX Timeout Rx Only Nur RX Scan List Scanliste Max Maximum High Hoch Mid Mittel Low Niedrig Min Minimum Default Standard Specifies the transmit timeout in seconds. A value of 0 means that the transmit timeout is disabled. Legt den Sendetimeout in Sekunden fest. Der Sendetimeout limitiert die Dauer einer einzelnen Aussendung. Ein Wert von 0 deaktiviert den Sendetimeout. Off Aus VOX Level VOX Empfindlichkeit Tx Admit Sendevoraussetzung Always Immer Channel Free Kanal frei Color Code Farbcode Time Slot Zeitschlitz TS 1 TS 1 TS 2 TS 2 Rx Group List Empfangsgruppe Tx Contact TX Kontakt Positioning System APRS System Roaming Zone Roamingzone DMR ID DMR ID Extended Erweitert Device specific settings Gerätespezifische Einstellungen ErrorMessageView Error: Unknown. Fehler: Unbekannt. Error: %1 Fehler: %1 Traceback: Fehlerverfolgung: ExtensionView Cannot create extension. Kann Erweiterung nicht erzeugen. Cannot create extension, consider reporting a bug. Kann Erweiterung nicht erzeugen. Bitte melden Sie diesen Fehler. Cannot create list element. Kann Listenelement nicht anlegen. Cannot create list element, consider reporting a bug. Kann Listenelement nicht anlegen. Bitte berichten Sie diesen Fehler. Create Erzeugen Remove Entfernen GPSSystemDialog Create DMR APRS Sysmtem DMR-APRS System erzeugen Edit DMR APRS Sysmtem DMR-APRS System bearbeiten [Selected] [Auswahl] Edit GPS System GPS System bearbeiten Basic Basis Name Name Destination Ziel Update period Aktualisierungsperiode Revert Channel Sendekanal Extensions Erweiterungen GeneralSettingsView DMR Radio ID DMR Radio ID DMR ID DMR ID Radio Name Radioname Enter your name or callsign. Geben Sie hier Ihren Namen oder Ihr Rufzeichen ein. Name/Callsign Name/Rufzeichen Boot Settings Boot-Einstellungen Intro Line 1 Begrüßungszeile 1 First greeting line (if supported by the radio). Erste Begrüßungszeile (insofern das Gerät dies unterstützt). Intro line 1 Begrüßungszeile 1 Intro Line 2 Begrüßungszeile 2 Second greeting line (if supported by the radio). Zweite Begrüßungszeile (insofern das Gerät dies unterstützt). Intro line 2 Begrüßungszeile 2 Audio Settings Audioeinstellungen MIC Amp. MIC Verstärkung Speech Synthesis Sprachsynthese Channel Default Values Kanal-Standardwerte Power Leistung Max Maximum High Hoch Mid Mittel Low Niedrig Min Minimum Squelch Rauschsperre Open Offen Transmit Timeout TX Timeout Off Aus VOX Level VOX Empfindlichkeit Extensions Erweiterungen GroupListWrapper Contact Kontakt GroupListsView Cannot delete RX group list Kann Empfangsgruppe nicht löschen Cannot delete RX group lists: You have to select a group list first. Kann Empfangsgruppe nicht löschen: Wählen Sie zunächst eine Gruppe aus. Delete RX group list? Empfangsgruppe löschen? Delete RX group list %1? Empfangsgruppe '%1' löschen? Delete %1 RX group lists? %1 Empfangsgruppen löschen? Add RX Group Empfangsgruppe hinzufügen Alt++ Alt++ Delete RX Group Empfangsgruppe löschen Alt+- Alt+- GroupListsWrapper RX Group Lists Empfangsgruppen MainWindow File Datei Device Gerät Help Hilfe Databases Datenbanken Toolbar Werkzeugleiste New Neu Creates a new Codeplug. Erzeugt einen neuen Codeplug Ctrl+N Strg+N Open ... Öffnen … <html><head/><body><p>Imports a codeplug from &quot;conf&quot; files.</p></body></html> Importiert ein Codeplug von "conf" Dateien. Ctrl+O Strg+O Save ... Speichern … <html><head/><body><p>Saves the codeplug in a &quot;conf&quot; file.</p></body></html> Speichert den Codeplug in einer "conf" Datei. Ctrl+S Strg+S Quit Beenden Quits the application. Beendet qdmr. Ctrl+Q Strg+Q Detect Detektieren Detect connected radios. Detektiert angeschlossene Geräte. Verify Verifizieren <html><head/><body><p>Verifies the current codeplug with connected radios.</p></body></html> Verifiziert den aktuellen Codeplug für angeschlossene Geräte. Ctrl+R Strg+R Read Lesen Reads a codeplug from connected radios. Liest den Codeplug vom angeschlossenen Gerät. Write Schreiben Writes the codeplug to the connected radio. Schreibt den aktuellen Codeplug auf das angeschlossene Gerät. About qdmr Über qdmr Read the handbook. Lesen Sie das Handbuch. F1 F1 Settings Einstellungen Shows settings dialog Zeigt den Einstellungsdialog an. Write Callsign DB Rufzeichendatenbank schreiben Writes call-sign DB to radio. Schreibt die Rufzeichendatenbank auf das Gerät. Refresh Callsign DB Rufzeichendatenbank aktualisieren Refreshes the downloaded callsign DB Aktualisiert die lokale Rufzeichendatenbank im Hintergrund aus den Onlinequellen. Refresh Talkgroup DB Sprechgruppendatenbank aktualisieren Refreshes the downloaded talkgroup DB Aktualisiert die Sprechgruppendatenbank im Hintergrund aus den Onlinequellen. Export to CHIRP ... Für CHIRP exportieren ... Exports all FM channels to CHRIP CSV. Exportiert alle FM Kanäle als CHIRP CSV. Import ... Importieren ... Imports and merges a codeplug into the current one. Importiert einen Codeplug und führt diesen mit dem aktuellen Codeplug zusammen. Refresh Orbital Elements Bahnelement aktualisieren Refreshes the orbital elements. Aktualisiert die Bahnelemente. Edit Satellites ... Satelliten bearbeiten ... Opens an editor to edit your satellite database. Öffnet einen Editor, um die Satellitendatenbank zu bearbeiten. Write satellites Satelliten schreiben Writes the orbital elements and transponder information onto the connected device. Schreibt die Bahnelemente und Transponderinformationen auf das angeschlossene Gerät. Radio IDs Radio IDs Contacts Kontakte Group Lists Empfangsgruppen Channels Kanäle Zones Zonen Scan Lists Scanlisten GPS/APRS GPS/APRS Roaming Channels Roamingkanäle Roaming Zones Roamingzonen Extensions Erweiterungen Unsaved changes to codeplug. Nicht gespeicherte Änderungen am Codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. Es gibt nicht gespeicherte Änderungen am Codeplug. Diese gehen verloren, wenn Sie fortfahren. MultiChannelSelectionDialog [Selected] [Auswahl] Select a channel: Wählen Sie einen Kanal: MultiGroupCallSelectionDialog Show private calls Direktrufe anzeigen Select a group call: Wählen Sie einen Gruppenruf aus: MultiRoamingChannelSelectionDialog Select roaming channels Roamingkanäle auswählen PositioningSystemListView Cannot delete GPS system Kann GPS System nicht löschen Cannot delete GPS system: You have to select a GPS system first. Kann GPS System nicht löschen: Wählen Sie zunächst ein GPS System aus. Delete positioning system? GPS System löschen? Delete positioning system %1? GPS System '%1' löschen? Delete %1 positioning systems? %1 GPS Systeme löschen? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support GPS or APRS. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Hinweis:</span> qdmr ist eine geräteunabhängige CPS. Jedoch unterstützen nicht alle Geräte GPS/APRS. Daher werden diese Einstellungen ignoriert, wenn der Codeplug auf Geräte geschrieben wird, die kein GPS/APRS unterstützen.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Verstecken</span></a></p></body></html> Add GPS System GPS System hinzufügen Alt+G Alt+G Add APRS System APRS System hinzufügen Alt+A Alt+A Delete Position System GPS/APRS System löschen Alt+- Alt+- PositioningSystemListWrapper DMR DMR APRS APRS [None] [Keine] [Selected] [Auswahl] Type Typ Name Name Destination Ziel Period [s] Periode [s] Channel Kanal Message Nachricht PropertyDelegate False Falsch True Wahr [None] [Leer] PropertyWrapper new element neues Element Property Eigenschaft Value Wert Description Beschreibung true wahr false falsch [None] [Leer] Instance of %1 Instanz von '%1' List of %1 instances Liste von Instanzen von '%1' QObject [None] [Kein] RXGroupListDialog Create Group List Empfangsgruppe erzeugen Edit Group List Empfangsgruppe bearbeiten Cannot remove group call Kann Empfangsgruppe nicht entfernen Cannot remove group call: You have to select at least one group call first. Kann Empfangsgruppe nicht löschen: Wählen Sie zunächst eine Gruppe aus. Basic Basis Name Name Add Contact Kontakt hinzufügen Alt++ Alt++ Remove Contact Kontakt entfernen Alt+- Alt+- Extensions Erweiterungen RadioIDListView Cannot delete radio IDs Kann Radio ID nicht löschen Cannot delete radio IDs: You have to select a radio ID first. Kann Radio ID nicht löschen: Wählen Sie zunächst eine ID aus. Delete radio ID? Radio ID löschen? Delete radio ID %1? Radio ID '%1' löschen? Delete scan lists? Scanliste löschen? Delete %1 scan lists? %1 Scanlisten löschen? Default Radio ID Standard Radio ID Add Radio ID Radio ID hinzufügen Delete Radio ID Radio ID löschen RadioIdListWrapper Type Typ Name Name Number Nummer RadioSelectionDialog Cannot auto-detect radio Kann Gerät nicht automatisch detektieren Select a specific radio Wählen Sie Ihr Gerät aus ReleaseNotes Cannot download release notes from https://github.com/hmatuschek/qdmr %1 Kann Releaseinformationen nicht von https://github.com/hmatuschek/qdmr laden %1 Cannot read release notes from https://github.com/hmatuschek/qdmr Release is not a JSON object! Kann Releaseinformationen nicht von https://github.com/hmatuschek/qdmr laden Informationen sind kein JSON Objekt! Cannot read release notes from https://github.com/hmatuschek/qdmr Release does not contain a release note. Kann Releaseinformationen nicht von https://github.com/hmatuschek/qdmr laden Release enthält keine Informationen. qDMR was updated to version %1 Eine neue Version %1 von qdmr wurde freigegeben. RoamingChannelDialog Time Slot Zeitschlitz Color Code Farbcode Selected Auswahl Name Name RX Frequency [MHz] RX Frequenz [MHz] TX Frequency [MHz] TX Frequenz [MHz] TS 1 TS 1 TS 2 TS 2 Edit roaming channel Roamingkanal bearbeiten Create roaming channel Roamingkanal anlegen RoamingChannelListView Add Roaming Channel Roamingkanal hinzufügen Delete Roaming Channel Roamingkanal löschen Cannot delete roaming channel Kann Roamingkanal nicht löschen Cannot delete roaming channel: You have to select a channel first. Kann Roamingkanal nicht löschen: Sie müssen zunächst mindestens einen Kanal auswählen. Delete roaming channel? Roamingkanal löschen? Delete roaming channel %1? Roamingkanal %1 löschen? Delete %1 roaming channel? %1 Roamingkanäle löschen? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p><span style=" font-weight:600;">Hint:</span> You do not need to add roaming channel explicitly, just create roaming zones and add channels there. These channels are then added to this list.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Hinweis:</span> QDMR ist eine Geräte unabhängige CPS. Wie dem auch sei, nicht alle Geräte unterstützen das Roaming. Diese Einstellungen werden für jene Geräte ignoriert. </p><p><span style=" font-weight:600;">Tip:</span> Sie müssen die Roamingkanäle nicht explizit anlegen. Sie können diese zusammen mit der Roamingzone automatisch erzeugen lassen. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> RoamingChannelListWrapper [Selected] [Auswahl] Name Name RX Frequency RX Frequenz CC CC Zones Zonen TX Frequency TX Frequenz TS TS RoamingChannelRefListWrapper Roaming Channel Roaming Kanal RoamingListWrapper %1 (containing %2 channels) %1 (mit %2 Kanälen) Roaming zone Roamingzone RoamingZoneDialog Create Roaming Zone Roamingzone erzeugen Set Roaming Zone Roamingzone bearbeiten Basic Basis Name: Name: Alt++ Alt++ Remove Channel Kanal entfernen Alt+- Alt+- Extension Erweiterung Add Roaming Channel Roamingkanal hinzufügen Add DMR Channel DMR Kanal hinzufügen Cannot remove channels. Kann Kanäle nicht entfernen. Cannot remove channels. Select at least one channel first. Kann Kanäle nicht entfernen. Wählen sie zunächst mindestens einen Kanal aus. RoamingZoneListView Generate roaming zone Roamingzone erzeugen Create a roaming zone by collecting all channels with these group calls. Eine Roamingzone aus allen Kanälen mit diesen Gruppenrufen erzeugen. Cannot delete roaming zone Kann Roamingzone nicht löschen Cannot delete roaming zone: You have to select a zone first. Kann Roamingzone nicht löschen: Wählen Sie zunächst eine Zone aus. Delete roaming zone? Roamingzone löschen? Delete roaming zone %1? Roamingzone '%1' löschen? Delete roaming zones? Roamingzonen löschen? Delete %1 roaming zones? %1 Roamingzonen löschen? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr ist eine geräteunabhängige CPS. Jedoch unterstützen nicht alle Geräte das Roaming, Diese Einstellungen werden ignoriert, wenn Sie den Codeplug auf solche Geräte schreiben. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Ausblenden</span></a></p></body></html> Add Roaming Zone Roamingzone hinzufügen Alt++ Alt++ Generate Roaming Zone Roamingzone erzeugen Delete Roaming Zone Roamingzone löschen Alt+- Alt+- SatelliteDatabaseDialog Edit satellite database Satellitendatenbank bearbeiten Add Hinzufügen Delete Löschen SatelliteSelectionDialog Select a satellite Wähle einen Satelliten aus ScanListDialog Edit Scan List Scanliste bearbeiten Basic Basis Name Name Primary Channel (50%) Primärkanal (50%) Secondary Channel (25%) Sekundärer Kanal (25%) Transmit Channel Sendekanal Add Channel Kanal hinzufügen Alt++ Alt++ Remove Channel Kanal entfernen Alt+- Alt+- Extensions Erweiterungen Create Scan List Scanliste erstellen [None] [Kein] [Selected] [Auswahl] [Last] [Letzter] ScanListsView Cannot delete scanlist Kann Scanliste nicht löschen Cannot delete scanlist: You have to select a scanlist first. Kann Scanliste nicht löschen: Wählen Sie zunächst eine Scanliste aus. Delete scan list? Scanliste löschen? Delete scan list %1? Scanliste '%1' löschen? Delete scan lists? Scanlisten löschen? Delete %1 scan lists? %1 Scanlisten löschen? Add Scan List Scanliste hinzufügen Alt++ Alt++ Delete Scan List Scanliste löschen Alt+- Alt+- ScanListsWrapper Scan-List Scanliste SearchPopup Ctrl+F Ctrl+F %1/%2 %1/%2 SelectiveCallBox None Kein CTCSS CTCSS DCS DCS Hz Hz Inverted Invertiert SettingsDialog Warning! Warnung! Settings Einstellungen Location Position System location Systemposition Locator Locator Repeater Info Sources Repeaterinformationsquellen enable aktivieren Radio Programming Geräteprogrammierung Update codeplug Codeplug updaten <html><head/><body><p>Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.</p><p><br/></p><p>If selected, QDMR downloads the codeplug from the radio and updates only those settings specified. The remaining settings within the radio are not touched (recommended).</p></body></html> <html><head/><body><p>Aktualisiert den Codeplug auf dem Gerät. Wenn nicht ausgewählt, wird der Codeplug auf dem Gerät überschrieben, mit mehr oder weniger guten Standardeinstellungen.</p><p><br/></p><p>Wenn ausgewählt, wird qdmr den Codeplug auf dem Gerät zunächst herunterladen, mit den Einstellungen aus diesem Codeplug aktualisieren und diesen aktualisierten Codeplug zurück auf das Gerät schreiben (empfohlen).</p></body></html> Auto-enable GPS GPS automatisch einschalten When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically. Wenn ein GPS/APRS System für irgendeinen Kanal gesetzt wurde, wird das GPS Modul (insofern vorhanden) automatisch eingeschaltet. When a roaming zone is defined and used by any channel, the automatic roaming gets enabled. Wenn eine Roamingzone definiert und mit einem Kanal assoziiert ist, wird automatisches Roaming aktiviert. Auto-enable roaming Roaming automatisch aktivieren Data Sources Datenquellen World Weltweit North America Nordamerika Programming Programmierung Radio Interfaces Schnittstellen disable auto-detect Autoerkennung deaktivieren Ignore verification warnings Verifikationswarnungen ignorieren <html><head/><body><p>As the communication interface to the radio is kept open after verification, time-outs may occur and the code-plug upload may fail when the verification dialog pops up. To prevent this, verification warnings can be ignored, eliminating the time-gap between verification and upload. Verification errors still prevent the upload.</p></body></html> <html><head/><body><p>Da die Schnittstelle zum Gerät nach der Verifikation offen gehalten wird, kann es zu Timeouts zwischen der Verifikation und dem eigentlichen Schreiben des Codeplugs kommen. In der Folge, kann der Schreibvorgang fehlschlagen. Um diese Zeitverzögerung zu verhindern, können Warnungen ignoriert werden und der Schreibvorgang startet sofort. Verifikationsfehler, werden weiterhin den Schreibvorgang verhindern.</p></body></html> Ignore frequency limits Frequenzbereiche ignorieren Do not set this option unless you know what you are doing. Bitte aktivieren Sie diese Option nicht, es sei denn, Sie wissen sehr genau, was Sie tun. Senden außerhalb der spezifizierten Frequenzbereiche kann das Gerät permanent beschädigen. Call-Sign DB Rufzeichendatenbank Limit number of DB entries Die Anzahl der Einträge beschränken When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent). Ist diese Option ausgewählt, wird die Anzahl der Einträge in die Rufzeichendatenbank limitiert. Wenn nicht, wird immer die maximale Anzahl an möglichen Einträgen auf das Gerät geschrieben (abhängig vom Gerät). Number of DB entries Anzahl der Einträge der Rufzeichendatenbank Specifies the number of DB entries (if enabled above). Legt die Anzahl der Einträge in der Rufzeichendatenbank fest, die auf das Gerät geschrieben werden (wenn oben ausgewählt). Select using my DMR ID Auswahl anhand meiner DMR ID If enabled, the entries are selected using the users DMR ID. Wenn ausgewählt, werden die Einträge der Rufzeichendatenbank anhand der eigenen Standard DMR ID ausgewählt. Select using prefixes Auswahl anhand der Prefixe If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries. Wenn ausgewählt, spezifizieren diese Komma-getrennten DMR ID prefixe jene Einträge der Rufzeichendatenbank, die auf das Gerät geschrieben werden. Extensions Erweiterungen Extended Features Erweiterte Features Show commercial features Kommerzielle DMR Features anzeigen Show device extensions Gerätespezifische Einstellungen anzeigen VerifyDialog Verify Codeplug Codeplug verifizieren ZoneDialog Edit Zone Zone bearbeiten Basic Basis <html><head/><body><p align="justify"><span style=" font-weight:600;">Note:</span> Zones are collections of channels that are usually valid for a specific region. I.e., a collection of channels for repeaters within a certain region. </p><p align="justify">QDMR manages zones by allowing for two independent channel lists for each VFO of the radio (if it has two). Many radios however, allow one to assign zones to each VFO individually. In these cases, QDMR will split the zone into two (A &amp; B) and program them individually into the radio.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> <html><head/><body><p align="justify"><span style=" font-weight:600;">Hinweis:</span> Zonen fassen Kanäle zusammen, die normalerweise für eine bestimmte Region gelten. D.h., alle Kanäle, für eine Sprechgruppe einer bestimmten Region. </p><p align="justify">qdmr verwaltet diese Kanäle in zwei Listen. Eine für jeden VFO des Gerätes (wenn es denn zwei hat). Viele Geräte erlauben es jedoch, eine Zone für jeden VFO festzulegen. In diesen Fällen wird qdrm die Zone in zwei Zonen (A &amp; B) aufteilen und beide separat auf das Gerät schreiben.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Ausblenden</span></a></p></body></html> Name Name Channels A Kanäle A add hinzufügen remove entfernen Channels B Kanäle B Extension Erweiterungen Create Zone Zone anlegen Select at least one channel first. Wählen sie zunächst mindestens einen Kanal aus. Cannot remove channel Kann Kanal nicht entfernen ZoneListView Cannot delete zone Kann Zone nicht löschen Cannot delete zone: You have to select a zone first. Kann Zone nicht löschen: Wählen Sie zunächst eine Zone aus. Delete zone? Zone löschen? Delete zone %1? Zone '%1' löschen? Delete zones? Zonen löschen? Delete %1 zones? %1 Zonen löschen? Add Zone Zone hinzufügen Alt++ Alt++ Delete Zone Zone löschen Alt+- Alt+- ZoneListWrapper Zone Zone aprssystemdialog Edit APRS System APRS System bearbeiten Basic Basis Name Name Channel Kanal Source Quelle Destination Ziel Path Pfad Icon Icon Update period [s] Periode [s] Message Nachricht Extensions Erweiterungen qdmr-0.12.3/i18n/empty.ts000066400000000000000000004514221501654372000150400ustar00rootroot00000000000000 APRSSystem [None] Police station Digipeater Phone DX cluster HF gateway Plane small Mobile Satellite station Wheel Chair Snowmobile Red cross Boy scout Home X Red dot Circle 0 Circle 1 Circle 2 Circle 3 Circle 4 Circle 5 Circle 6 Circle 7 Circle 8 Circle 9 Fire Campground Motorcycle Rail engine Car File server HC Future Aid station BBS Canoe Eyeball Tractor Grid Square Hotel TCP/IP School Logon MacOS NTS station Balloon Police car TBD RV Shuttle SSTV Bus ATV Weather service Helo Yacht MS Windows Jogger Triangle PBBS Plane large Weather station Dish antenna Ambulance Bike ICP Fire station Horse Fire truck Glider Hospital IOTA Jeep Truck small Laptop Mic-E Node EOC Rover Grid Antenna Power boat Truck stop Truck large Van Water XAPRS Yagi Shelter APRSSystemDialog Create APRS system Edit APRS system [Selected] AboutDialog About qdmr Supported Radios AnalogChannelDialog [None] Edit Analog Channel <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and CTCSS tones are filled in (if applicable).</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Name Rx Frequency Tx Frequency Power Max High Mid Low Min Default Tx Timeout Off VOX Level Rx Only Scan List Tx Admit Always Channel Free Tone Squelch Rx Tone Tx Tone Bandwidth Narrow (12.5 kHz) Wide (25 kHz) APRS Open Extensions Application Unsaved changes to codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. Open codeplug Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*) Cannot open file Cannot read codeplug from file '%1': %2 Cannot read codeplug. Save codeplug Codeplug Files (*.yaml *.yml) Please use new YAML format. Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works. Cannot save codeplug to file '%1': %2 Cannot save codeplug Cannot save codeplug to file '%1'. Export codeplug CHIRP CSV Files (*.csv) Cannot export codeplug Cannot export codeplug to file '%1': %2 Import codeplug CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml) Cannot import codeplug Cannot import codeplug from '%1': %2 Do not know, how to handle file '%1'. No matching devices found. Cannot connect to radio Cannot connect to radio: %1 Radio found Found device '%1'. No radio found No matching device was found. Verification success The codeplug was successfully verified with the radio '%1' Read ... Read error Read complete Upload ... Cannot write call-sign DB. The detected radio '%1' does not support a call-sign DB. The detected radio '%1' does support a call-sign DB. This feature, however, is not implemented yet. QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set. Write call-sign DB ... Cannot write satellite config. The detected radio '%1' does not support satellite tracking. The detected radio '%1' does support satellite tracking. This feature, however, is not implemented yet. Write satellite config ... Write error Write complete %1 (alias for %2 %3) ChannelListView Select a single channel first To clone a channel, please select a single channel to clone. Cannot delete channel Cannot delete channel: You have to select a channel first. Delete channel? Delete channel %1? Delete %1 channels? Add Analog Channel Alt+A Add Digital Channel Alt+D Clone Channel Alt+C Delete Channel Alt+- ChannelListWrapper FM DMR [Default] Max High Mid Low Min Off On Always Free Color Tone [None] Open Wide Narrow Type Name Rx Frequency Tx Frequency Power Timeout Rx Only Admit Scanlist Zones CC TS RX Group List TX Contact DMR ID GPS/APRS Roaming Squelch Rx Tone Tx Tone Bandwidth ChannelRefListWrapper Channel ChannelSelectionDialog Select a channel: ConfigMergeDialog Merging codeplugs ... <html><head/><body><p><span style=" font-weight:600;">Conflict resolution strategies:</span></p><p>If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.</p></body></html> Items are all atomic objects like radio IDs, channels, contacts and roaming channels. Items Ignore Override Duplicate Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones. Sets Merge Ignores any duplicate item. Replaces any duplicate item with the imported one. Imports any duplicate item with a modified name. Ignores any duplicate set. Replaces any duplicate set with the imported one. Imports any duplicate set with a modified name. Merges duplicate sets. ConfigObjectListView Cannot move items. Cannot move items: You have to select at least one item first. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTableView Cannot move items. Cannot move items: You have to select at least one item first. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTypeSelectionDialog An instance of %1. <p>%1<p><p style="margin-left:10px;">%2</p> Create extension object Select the class of object to create ContactListView Cannot delete contact Cannot delete contact: You have to select a contact first. Delete contact? Delete contact %1? Delete contacts? Delete %1 contacts? Adds a contact to the list. Add Digital Contact Alt++ Add DTMF Contact Delete contact button Delete Contact Alt+- ContactListWrapper DTMF On Off Private Call Group Call All Call Type Name Number RX Tone DMRContactDialog Create DMR Contact Edit DMR Contact Private Call Group Call All Call Basic Type Name Number Ring Extensions DMRIDDialog Basic Name DMR ID Extensions DTMFContactDialog Create DTMF Contact Edit DMR Contact Basic Name Number Ring Extensions DeviceSelectionDialog Select a device <html><head/><body><p>There is either more than one device detected or the one found is not considered save to access. Either way, select the device to use.</p></body></html> DigitalChannelDialog [None] [Default] Edit Digital Channel <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and color-code are filled in.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Name Rx Frequency Tx Frequency Power Tx Timeout Rx Only Scan List Max High Mid Low Min Default Specifies the transmit timeout in seconds. A value of 0 means that the transmit timeout is disabled. Off VOX Level Tx Admit Always Channel Free Color Code Time Slot TS 1 TS 2 Rx Group List Tx Contact Positioning System Roaming Zone DMR ID Extended Device specific settings ErrorMessageView Error: Unknown. Error: %1 Traceback: ExtensionView Cannot create extension. Cannot create extension, consider reporting a bug. Cannot create list element. Cannot create list element, consider reporting a bug. Create Remove GPSSystemDialog Create DMR APRS Sysmtem Edit DMR APRS Sysmtem [Selected] Edit GPS System Basic Name Destination Update period Revert Channel Extensions GeneralSettingsView DMR Radio ID DMR ID Radio Name Enter your name or callsign. Name/Callsign Boot Settings Intro Line 1 First greeting line (if supported by the radio). Intro line 1 Intro Line 2 Second greeting line (if supported by the radio). Intro line 2 Audio Settings MIC Amp. Speech Synthesis Channel Default Values Power Max High Mid Low Min Squelch Open Transmit Timeout Off VOX Level Extensions GroupListWrapper Contact GroupListsView Cannot delete RX group list Cannot delete RX group lists: You have to select a group list first. Delete RX group list? Delete RX group list %1? Delete %1 RX group lists? Add RX Group Alt++ Delete RX Group Alt+- GroupListsWrapper RX Group Lists MainWindow File Device Help Databases Toolbar New Creates a new Codeplug. Ctrl+N Open ... <html><head/><body><p>Imports a codeplug from &quot;conf&quot; files.</p></body></html> Ctrl+O Save ... <html><head/><body><p>Saves the codeplug in a &quot;conf&quot; file.</p></body></html> Ctrl+S Quit Quits the application. Ctrl+Q Detect Detect connected radios. Verify <html><head/><body><p>Verifies the current codeplug with connected radios.</p></body></html> Ctrl+R Read Reads a codeplug from connected radios. Write Writes the codeplug to the connected radio. About qdmr Read the handbook. F1 Settings Shows settings dialog Write Callsign DB Writes call-sign DB to radio. Refresh Callsign DB Refreshes the downloaded callsign DB Refresh Talkgroup DB Refreshes the downloaded talkgroup DB Export to CHIRP ... Exports all FM channels to CHRIP CSV. Import ... Imports and merges a codeplug into the current one. Refresh Orbital Elements Refreshes the orbital elements. Edit Satellites ... Opens an editor to edit your satellite database. Write satellites Writes the orbital elements and transponder information onto the connected device. Radio IDs Contacts Group Lists Channels Zones Scan Lists GPS/APRS Roaming Channels Roaming Zones Extensions Unsaved changes to codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. MultiChannelSelectionDialog [Selected] Select a channel: MultiGroupCallSelectionDialog Show private calls Select a group call: MultiRoamingChannelSelectionDialog Select roaming channels PositioningSystemListView Cannot delete GPS system Cannot delete GPS system: You have to select a GPS system first. Delete positioning system? Delete positioning system %1? Delete %1 positioning systems? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support GPS or APRS. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add GPS System Alt+G Add APRS System Alt+A Delete Position System Alt+- PositioningSystemListWrapper DMR APRS [None] [Selected] Type Name Destination Period [s] Channel Message PropertyDelegate False True [None] PropertyWrapper new element Property Value Description true false [None] Instance of %1 List of %1 instances QObject [None] RXGroupListDialog Create Group List Edit Group List Cannot remove group call Cannot remove group call: You have to select at least one group call first. Basic Name Add Contact Alt++ Remove Contact Alt+- Extensions RadioIDListView Cannot delete radio IDs Cannot delete radio IDs: You have to select a radio ID first. Delete radio ID? Delete radio ID %1? Delete scan lists? Delete %1 scan lists? Default Radio ID Add Radio ID Delete Radio ID RadioIdListWrapper Type Name Number RadioSelectionDialog Cannot auto-detect radio Select a specific radio ReleaseNotes Cannot download release notes from https://github.com/hmatuschek/qdmr %1 Cannot read release notes from https://github.com/hmatuschek/qdmr Release is not a JSON object! Cannot read release notes from https://github.com/hmatuschek/qdmr Release does not contain a release note. qDMR was updated to version %1 RoamingChannelDialog Name RX Frequency [MHz] TX Frequency [MHz] Time Slot Color Code Selected Edit roaming channel Create roaming channel TS 1 TS 2 RoamingChannelListView <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p><span style=" font-weight:600;">Hint:</span> You do not need to add roaming channel explicitly, just create roaming zones and add channels there. These channels are then added to this list.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Channel Delete Roaming Channel Cannot delete roaming channel Cannot delete roaming channel: You have to select a channel first. Delete roaming channel? Delete roaming channel %1? Delete %1 roaming channel? RoamingChannelListWrapper [Selected] Name RX Frequency TX Frequency TS Zones CC RoamingChannelRefListWrapper Roaming Channel RoamingListWrapper %1 (containing %2 channels) Roaming zone RoamingZoneDialog Create Roaming Zone Set Roaming Zone Cannot remove channels. Cannot remove channels. Select at least one channel first. Basic Name: Add Roaming Channel Add DMR Channel Alt++ Remove Channel Alt+- Extension RoamingZoneListView Generate roaming zone Create a roaming zone by collecting all channels with these group calls. Cannot delete roaming zone Cannot delete roaming zone: You have to select a zone first. Delete roaming zone? Delete roaming zone %1? Delete roaming zones? Delete %1 roaming zones? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Zone Alt++ Generate Roaming Zone Delete Roaming Zone Alt+- SatelliteDatabaseDialog Edit satellite database Add Delete SatelliteSelectionDialog Select a satellite ScanListDialog Edit Scan List Basic Name Primary Channel (50%) Secondary Channel (25%) Transmit Channel Add Channel Alt++ Remove Channel Alt+- Extensions Create Scan List [None] [Selected] [Last] ScanListsView Cannot delete scanlist Cannot delete scanlist: You have to select a scanlist first. Delete scan list? Delete scan list %1? Delete scan lists? Delete %1 scan lists? Add Scan List Alt++ Delete Scan List Alt+- ScanListsWrapper Scan-List SearchPopup Ctrl+F %1/%2 SelectiveCallBox None CTCSS DCS Hz Inverted SettingsDialog Warning! Settings Location System location Locator Repeater Info Sources enable Radio Programming Update codeplug <html><head/><body><p>Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.</p><p><br/></p><p>If selected, QDMR downloads the codeplug from the radio and updates only those settings specified. The remaining settings within the radio are not touched (recommended).</p></body></html> Auto-enable GPS When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically. When a roaming zone is defined and used by any channel, the automatic roaming gets enabled. Auto-enable roaming Data Sources World North America Programming Radio Interfaces disable auto-detect Ignore verification warnings <html><head/><body><p>As the communication interface to the radio is kept open after verification, time-outs may occur and the code-plug upload may fail when the verification dialog pops up. To prevent this, verification warnings can be ignored, eliminating the time-gap between verification and upload. Verification errors still prevent the upload.</p></body></html> Ignore frequency limits Do not set this option unless you know what you are doing. Call-Sign DB Limit number of DB entries When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent). Number of DB entries Specifies the number of DB entries (if enabled above). Select using my DMR ID If enabled, the entries are selected using the users DMR ID. Select using prefixes If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries. Extensions Extended Features Show commercial features Show device extensions VerifyDialog Verify Codeplug ZoneDialog Edit Zone Basic <html><head/><body><p align="justify"><span style=" font-weight:600;">Note:</span> Zones are collections of channels that are usually valid for a specific region. I.e., a collection of channels for repeaters within a certain region. </p><p align="justify">QDMR manages zones by allowing for two independent channel lists for each VFO of the radio (if it has two). Many radios however, allow one to assign zones to each VFO individually. In these cases, QDMR will split the zone into two (A &amp; B) and program them individually into the radio.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Name Channels A add remove Channels B Extension Create Zone Cannot remove channel Select at least one channel first. ZoneListView Cannot delete zone Cannot delete zone: You have to select a zone first. Delete zone? Delete zone %1? Delete zones? Delete %1 zones? Add Zone Alt++ Delete Zone Alt+- ZoneListWrapper Zone aprssystemdialog Edit APRS System Basic Name Channel Source Destination Path Icon Update period [s] Message Extensions qdmr-0.12.3/i18n/en_US.ts000066400000000000000000004627361501654372000147250ustar00rootroot00000000000000 APRSSystem [None] [None] Police station Police station Digipeater Digipeater Phone Phone DX cluster DX cluster HF gateway HF gateway Plane small Plane small Mobile Satellite station Mobile satellite station Wheel Chair Wheel chair Snowmobile Snowmobile Red cross Red cross Boy scout Boy scout Home Home X X Red dot Red dot Circle 0 Circle 0 Circle 1 Circle 1 Circle 2 Circle 2 Circle 3 Circle 3 Circle 4 Circle 4 Circle 5 Circle 5 Circle 6 Circle 6 Circle 7 Circle 7 Circle 8 Circle 8 Circle 9 Circle 9 Fire Fire Campground Campground Motorcycle Motorcycle Rail engine Rail engine Car Car File server File server HC Future HC Future Aid station Aid station BBS BBS Canoe Canoe Eyeball Eyeball Tractor Tractor Grid Square Grid Square Hotel Hotel TCP/IP TCP/IP School School Logon Logon MacOS MacOS NTS station NTS station Balloon Balloon Police car Police car TBD TBD RV RV Shuttle Shuttle SSTV SSTV Bus Bus ATV ATV Weather service Weather service Helo Helo Yacht Yacht MS Windows MS Windows Jogger Jogger Triangle Triangle PBBS PBBS Plane large Plane large Weather station Weather station Dish antenna Dish antenna Ambulance Ambulance Bike Bike ICP ICP Fire station Fire station Horse Horse Fire truck Fire truck Glider Glider Hospital Hospital IOTA IOTA Jeep Jeep Truck small Truck small Laptop Laptop Mic-E Mic-E Node Node EOC EOC Rover Rover Grid Grid Antenna Antenna Power boat Power boat Truck stop Truck stop Truck large Truck large Van Van Water Water XAPRS XAPRS Yagi Yagi Shelter Shelter APRSSystemDialog Create APRS system Create APRS system Edit APRS system Edit APRS system [Selected] [Selected] AboutDialog About qdmr About qdmr Supported Radios Supported Radios AnalogChannelDialog [None] [None] Edit Analog Channel Edit analog channel <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and CTCSS tones are filled in (if applicable).</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and CTCSS tones are filled in (if applicable).</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Basic Name Name Rx Frequency RX Frequency Tx Frequency TX Frequency Power Power Max Max High High Mid Mid Low Low Min Min Default Default Tx Timeout TX Timeout Off Off VOX Level VOX Level Rx Only RX Only Scan List Scan List Tx Admit TX Admit Always Always Channel Free Channel Free Tone Tone Squelch Squelch Rx Tone RX Tone Tx Tone TX Tone Bandwidth Bandwidth Narrow (12.5 kHz) Narrow (12.5 kHz) Wide (25 kHz) Wide (25 kHz) APRS APRS Open Open Extensions Extensions Application Unsaved changes to codeplug. Unsaved changes to codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. There are unsaved changes to the current codeplug. These changes are lost if you proceed. Open codeplug Open codeplug Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*) Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*) Cannot open file Cannot open file Cannot read codeplug from file '%1': %2 Cannot read codeplug from file '%1': %2 Cannot read codeplug. Cannot read codeplug. Save codeplug Save codeplug Codeplug Files (*.yaml *.yml) Codeplug Files (*.yaml *.yml) Please use new YAML format. Please use new YAML format. Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works. Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works. Cannot save codeplug to file '%1': %2 Cannot save codeplug to file '%1': %2 Cannot save codeplug Cannot save codeplug Cannot save codeplug to file '%1'. Cannot save codeplug to file '%1'. Export codeplug Export codeplug CHIRP CSV Files (*.csv) CHIRP CSV Files (*.csv) Cannot export codeplug Cannot export codeplug Cannot export codeplug to file '%1': %2 Cannot export codeplug to file '%1': %2 Import codeplug Import codeplug CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml) CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml) Cannot import codeplug Cannot import codeplug Cannot import codeplug from '%1': %2 Cannot import codeplug from '%1': %2 Do not know, how to handle file '%1'. Do not know, how to handle file '%1'. No matching devices found. No matching devices found. Cannot connect to radio Cannot connect to radio Cannot connect to radio: %1 Cannot connect to radio: %1 Radio found Radio found Found device '%1'. Found device '%1'. No radio found No radio found Verification success Verification success The codeplug was successfully verified with the radio '%1' The codeplug was successfully verified with the radio '%1' Read ... Read … Read error Read error Read complete Read complete Upload ... Upload … Cannot write call-sign DB. Cannot write call-sign DB. The detected radio '%1' does not support a call-sign DB. The detected radio '%1' does not support a call-sign DB. No matching device was found. No matching device was found. The detected radio '%1' does support a call-sign DB. This feature, however, is not implemented yet. The detected radio '%1' does support a call-sign DB. This feature, however, is not implemented yet. QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set. QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set. Write call-sign DB ... Write call-sign DB … Cannot write satellite config. Cannot write satellite config. The detected radio '%1' does not support satellite tracking. The detected radio '%1' does not support satellite tracking. The detected radio '%1' does support satellite tracking. This feature, however, is not implemented yet. The detected radio '%1' does support satellite tracking. This feature, however, is not implemented yet. Write satellite config ... Write satellite config … Write error Write error Write complete Write complete %1 (alias for %2 %3) %1 (alias for %2 %3) ChannelListView Select a single channel first Select a single channel first To clone a channel, please select a single channel to clone. To clone a channel, please select a single channel to clone. Cannot delete channel Cannot delete channel Cannot delete channel: You have to select a channel first. Cannot delete channel: You have to select a channel first. Delete channel? Delete channel? Delete channel %1? Delete channel '%1'? Delete %1 channels? Delete %1 channels? Add Analog Channel Add Analog Channel Alt+A Alt+A Add Digital Channel Add Digital Channel Alt+D Alt+D Clone Channel Clone Channel Alt+C Alt+C Delete Channel Delete Channel Alt+- Alt+- ChannelListWrapper FM FM DMR DMR [Default] [Default] Max Max High High Mid Mid Low Low Min Min Off Off On On Always Always Free Free Color Color Tone Tone [None] [None] Open Open Wide Wide Narrow Narrow Type Type Name Name Rx Frequency RX Frequency Tx Frequency TX Frequency Power Power Timeout Timeout Rx Only RX Only Admit Admit Scanlist Scanlist CC CC TS TS RX Group List RX Group List TX Contact TX Contact DMR ID DMR ID GPS/APRS GPS/APRS Roaming Roaming Squelch Squelch Rx Tone RX Tone Tx Tone TX Tone Zones Zones Bandwidth Bandwidth ChannelRefListWrapper Channel Channel ChannelSelectionDialog Select a channel: Select a channel: ConfigMergeDialog Merging codeplugs ... Merging codeplugs ... <html><head/><body><p><span style=" font-weight:600;">Conflict resolution strategies:</span></p><p>If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Conflict resolution strategies:</span></p><p>If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.</p></body></html> Items are all atomic objects like radio IDs, channels, contacts and roaming channels. Items are all atomic objects like radio IDs, channels, contacts and roaming channels. Items Items Ignore Ignore Override Override Duplicate Duplicate Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones. Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones. Sets Sets Merge Merge Ignores any duplicate item. Ignores any duplicate item. Replaces any duplicate item with the imported one. Replaces any duplicate item with the imported one. Imports any duplicate item with a modified name. Imports any duplicate item with a modified name. Ignores any duplicate set. Ignores any duplicate set. Replaces any duplicate set with the imported one. Replaces any duplicate set with the imported one. Imports any duplicate set with a modified name. Imports any duplicate set with a modified name. Merges duplicate sets. Merges duplicate sets. ConfigObjectListView Cannot move items. Cannot move items. Cannot move items: You have to select at least one item first. Cannot move items: You have to select at least one item first. Move selected item(s) to the top. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. Move selected item(s) to the bottom. ConfigObjectTableView Cannot move items. Cannot move items. Cannot move items: You have to select at least one item first. Cannot move items: You have to select at least one item first. Move selected item(s) to the top. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. Move selected item(s) to the bottom. ConfigObjectTypeSelectionDialog An instance of %1. An instance of '%1'. <p>%1<p><p style="margin-left:10px;">%2</p> <p>%1<p><p style="margin-left:10px;">%2</p> Create extension object Create extension object Select the class of object to create Select the class of object to create ContactListView Cannot delete contact Cannot delete contact Cannot delete contact: You have to select a contact first. Cannot delete contact: You have to select a contact first. Delete contact? Delete contact? Delete contact %1? Delete contact '%1'? Delete contacts? Delete contacts? Delete %1 contacts? Delete %1 contacts? Adds a contact to the list. Adds a contact to the list. Add Digital Contact Add Digital Contact Alt++ Alt++ Add DTMF Contact Add DTMF Contact Delete contact button Delete contact button Delete Contact Delete Contact Alt+- Alt+- ContactListWrapper DTMF DTMF On On Off Off Private Call Private Call Group Call Group Call All Call All Call Type Type Name Name Number Number RX Tone RX Tone DMRContactDialog Create DMR Contact Create DMR Contact Edit DMR Contact Edit DMR Contact Private Call Private Call Group Call Group Call All Call All Call Basic Basic Type Type Name Name Number Number Ring Ring Extensions Extensions DMRIDDialog Basic Basic Name Name DMR ID DMR ID Extensions Extensions DTMFContactDialog Create DTMF Contact Create DTMF Contact Edit DMR Contact Edit DMR Contact Basic Basic Name Name Number Number Ring Ring Extensions Extensions DeviceSelectionDialog Select a device Select a device <html><head/><body><p>There is either more than one device detected or the one found is not considered save to access. Either way, select the device to use.</p></body></html> <html><head/><body><p>There is either more than one device detected or the one found is not considered save to access. Either way, select the device to use.</p></body></html> DigitalChannelDialog [None] [None] [Default] [Default] Edit Digital Channel Edit digital channel <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and color-code are filled in.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and color-code are filled in.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Basic Name Name Rx Frequency RX Frequency Tx Frequency TX Frequency Power Power Tx Timeout TX Timeout Rx Only RX Only Scan List Scan List Max Max High High Mid Mid Low Low Min Min Default Default Specifies the transmit timeout in seconds. A value of 0 means that the transmit timeout is disabled. Specifies the transmit timeout in seconds. A value of 0 means that the transmit timeout is disabled. Off Off VOX Level VOX Level Tx Admit TX Admit Always Always Channel Free Channel Free Color Code Color Code Time Slot Time Slot TS 1 TS 1 TS 2 TS 2 Rx Group List Group List Tx Contact TX Contact Positioning System Positioning System Roaming Zone Roaming Zone DMR ID DMR ID Extended Extended Device specific settings Device specific settings ErrorMessageView Error: Unknown. Error: Unknown. Error: %1 Error: %1 Traceback: Traceback: ExtensionView Cannot create extension. Cannot create extension. Cannot create extension, consider reporting a bug. Cannot create extension, consider reporting a bug. Cannot create list element. Cannot create list element. Cannot create list element, consider reporting a bug. Cannot create list element, consider reporting a bug. Create Create Remove Remove GPSSystemDialog Create DMR APRS Sysmtem Create DMR APRS Sysmtem Edit DMR APRS Sysmtem Edit DMR APRS Sysmtem [Selected] [Selected] Edit GPS System Edit GPS System Basic Basic Name Name Destination Destination Update period Update period Revert Channel Revert Channel Extensions Extensions GeneralSettingsView DMR Radio ID DMR Radio ID DMR ID DMR ID Radio Name Radio Name Enter your name or callsign. Enter your name or call-sign. Name/Callsign Name/Call-sign Boot Settings Boot Settings Intro Line 1 Intro Line 1 First greeting line (if supported by the radio). First greeting line (if supported by the radio). Intro line 1 Intro line 1 Intro Line 2 Intro Line 2 Second greeting line (if supported by the radio). Second greeting line (if supported by the radio). Intro line 2 Intro line 2 Audio Settings Audio Settings MIC Amp. MIC Amp. Speech Synthesis Speech Synthesis Channel Default Values Channel Default Values Power Power Max Max High High Mid Mid Low Low Min Min Squelch Squelch Open Open Transmit Timeout Transmit Timeout Off Off VOX Level VOX Level Extensions Extensions GroupListWrapper Contact Contact GroupListsView Cannot delete RX group list Cannot delete group list Cannot delete RX group lists: You have to select a group list first. Cannot delete group lists: You have to select a group list first. Delete RX group list? Delete group list? Delete RX group list %1? Delete group list '%1'? Delete %1 RX group lists? Delete %1 group lists? Add RX Group Add Group List Alt++ Alt++ Delete RX Group Delete Group List Alt+- Alt+- GroupListsWrapper RX Group Lists Group Lists MainWindow File File Device Device Help Help Databases Databases Toolbar New New Creates a new Codeplug. Creates a new Codeplug. Ctrl+N Ctrl+N Open ... Open … <html><head/><body><p>Imports a codeplug from &quot;conf&quot; files.</p></body></html> <html><head/><body><p>Imports a codeplug from &quot;conf&quot; files.</p></body></html> Ctrl+O Ctrl+O Save ... Save … <html><head/><body><p>Saves the codeplug in a &quot;conf&quot; file.</p></body></html> <html><head/><body><p>Saves the codeplug in a &quot;conf&quot; file.</p></body></html> Ctrl+S Ctrl+S Quit Quit Quits the application. Quits the application. Ctrl+Q Ctrl+Q Detect Detect Detect connected radios. Detect connected radios. Verify Verify <html><head/><body><p>Verifies the current codeplug with connected radios.</p></body></html> Verifies the current codeplug with connected radios. Ctrl+R Ctrl+R Read Read Reads a codeplug from connected radios. Reads a codeplug from connected radios. Write Write Writes the codeplug to the connected radio. Writes the codeplug to the connected radio. About qdmr About qdmr Read the handbook. Read the handbook. F1 F1 Settings Settings Shows settings dialog Shows settings dialog. Write Callsign DB Write call-sign DB Writes call-sign DB to radio. Writes call-sign DB to radio. Refresh Callsign DB Refresh call-sign DB Refreshes the downloaded callsign DB Refreshes the downloaded call-sign DB Refresh Talkgroup DB Refresh talk-group DB Refreshes the downloaded talkgroup DB Refreshes the downloaded talk-group DB Export to CHIRP ... Export to CHIRP ... Exports all FM channels to CHRIP CSV. Exports all FM channels to CHRIP CSV. Import ... Import ... Imports and merges a codeplug into the current one. Imports and merges a codeplug into the current one. Refresh Orbital Elements Refresh Orbital Elements Refreshes the orbital elements. Refreshes the orbital elements. Edit Satellites ... Edit Satellites ... Opens an editor to edit your satellite database. Opens an editor to edit your satellite database. Write satellites Write satellites Writes the orbital elements and transponder information onto the connected device. Writes the orbital elements and transponder information onto the connected device. Radio IDs Radio IDs Contacts Contacts Group Lists Group Lists Channels Channels Zones Zones Scan Lists Scan Lists GPS/APRS GPS/APRS Roaming Channels Roaming Channels Roaming Zones Roaming Zones Extensions Extensions Unsaved changes to codeplug. Unsaved changes to codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. There are unsaved changes to the current codeplug. These changes are lost if you proceed. MultiChannelSelectionDialog [Selected] [Selected] Select a channel: Select a channel: MultiGroupCallSelectionDialog Show private calls Show private calls Select a group call: Select a group call: MultiRoamingChannelSelectionDialog Select roaming channels Select roaming channels PositioningSystemListView Cannot delete GPS system Cannot delete GPS system Cannot delete GPS system: You have to select a GPS system first. Cannot delete GPS system: You have to select a GPS system first. Delete positioning system? Delete positioning system? Delete positioning system %1? Delete positioning system '%1'? Delete %1 positioning systems? Delete %1 positioning systems? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support GPS or APRS. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support GPS or APRS. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add GPS System Add GPS System Alt+G Alt+G Add APRS System Add APRS System Alt+A Alt+A Delete Position System Delete Positioning System Alt+- Alt+- PositioningSystemListWrapper DMR DMR APRS APRS [None] [None] [Selected] [Selected] Type Type Name Name Destination Destination Period [s] Period [s] Channel Channel Message Message PropertyDelegate False False True True [None] [None] PropertyWrapper new element new element Property Property Value Value Description Description true true false false [None] [None] Instance of %1 Instance of '%1' List of %1 instances List of %1 instances QObject [None] [None] RXGroupListDialog Create Group List Create group list Edit Group List Edit group list Cannot remove group call Cannot remove group call Cannot remove group call: You have to select at least one group call first. Cannot remove group call: You have to select at least one group call first. Basic Basic Name Name Add Contact Add Contact Alt++ Alt++ Remove Contact Remove Contact Alt+- Alt+- Extensions Extensions RadioIDListView Cannot delete radio IDs Cannot delete radio IDs Cannot delete radio IDs: You have to select a radio ID first. Cannot delete radio IDs: You have to select a radio ID first. Delete radio ID? Delete radio ID? Delete radio ID %1? Delete radio ID '%1'? Delete scan lists? Delete scan lists? Delete %1 scan lists? Delete %1 scan lists? Default Radio ID Default Radio ID Add Radio ID Add Radio ID Delete Radio ID Delete Radio ID RadioIdListWrapper Type Type Name Name Number Number RadioSelectionDialog Cannot auto-detect radio Cannot auto-detect radio Select a specific radio Select a specific radio ReleaseNotes Cannot download release notes from https://github.com/hmatuschek/qdmr %1 Cannot download release notes from https://github.com/hmatuschek/qdmr %1 Cannot read release notes from https://github.com/hmatuschek/qdmr Release is not a JSON object! Cannot read release notes from https://github.com/hmatuschek/qdmr Release is not a JSON object! Cannot read release notes from https://github.com/hmatuschek/qdmr Release does not contain a release note. Cannot read release notes from https://github.com/hmatuschek/qdmr Release does not contain a release note. qDMR was updated to version %1 qdmr was updated to version %1 RoamingChannelDialog Name Name RX Frequency [MHz] RX Frequency [MHz] TX Frequency [MHz] TX Frequency [MHz] Time Slot Time Slot Color Code Color Code Selected Selected Edit roaming channel Edit roaming channel Create roaming channel Create roaming channel TS 1 TS 1 TS 2 TS 2 RoamingChannelListView <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p><span style=" font-weight:600;">Hint:</span> You do not need to add roaming channel explicitly, just create roaming zones and add channels there. These channels are then added to this list.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p><span style=" font-weight:600;">Hint:</span> You do not need to add roaming channel explicitly, just create roaming zones and add channels there. These channels are then added to this list.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Channel Add Roaming Channel Delete Roaming Channel Delete Roaming Channel Cannot delete roaming channel Cannot delete roaming channel Cannot delete roaming channel: You have to select a channel first. Cannot delete roaming channel: You have to select a channel first. Delete roaming channel? Delete roaming channel? Delete roaming channel %1? Delete roaming channel %1? Delete %1 roaming channel? Delete %1 roaming channel? RoamingChannelListWrapper [Selected] [Selected] Name Name RX Frequency RX Frequency TX Frequency TX Frequency TS TS Zones Zones CC CC RoamingChannelRefListWrapper Roaming Channel Roaming Channel RoamingListWrapper %1 (containing %2 channels) %1 (containing %2 channels) Roaming zone Roaming zone RoamingZoneDialog Create Roaming Zone Create roaming zone Set Roaming Zone Edit roaming zone Cannot remove channels. Cannot remove channels. Cannot remove channels. Select at least one channel first. Cannot remove channels. Select at least one channel first. Basic Basic Name: Name: Add Roaming Channel Add Roaming Channel Add DMR Channel Add DMR Channel Alt++ Alt++ Remove Channel Remove Channel Alt+- Alt+- Extension Extension RoamingZoneListView Generate roaming zone Generate roaming zone Create a roaming zone by collecting all channels with these group calls. Create a roaming zone by collecting all channels with these group calls. Cannot delete roaming zone Cannot delete roaming zone Cannot delete roaming zone: You have to select a zone first. Cannot delete roaming zone: You have to select a zone first. Delete roaming zone? Delete roaming zone? Delete roaming zone %1? Delete roaming zone '%1'? Delete roaming zones? Delete roaming zones? Delete %1 roaming zones? Delete %1 roaming zones? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Zone Add Roaming Zone Alt++ Alt++ Generate Roaming Zone Generate Roaming Zone Delete Roaming Zone Delete Roaming Zone Alt+- Alt+- SatelliteDatabaseDialog Edit satellite database Edit satellite database Add Add Delete Delete SatelliteSelectionDialog Select a satellite Select a satellite ScanListDialog Edit Scan List Edit scan list Basic Basic Name Name Primary Channel (50%) Primary Channel (50%) Secondary Channel (25%) Secondary Channel (25%) Transmit Channel Transmit Channel Add Channel Add Channel Alt++ Alt++ Remove Channel Remove Channel Alt+- Alt+- Extensions Extensions Create Scan List Create scan list [None] [None] [Selected] [Selected] [Last] [Last] ScanListsView Cannot delete scanlist Cannot delete scanlist Cannot delete scanlist: You have to select a scanlist first. Cannot delete scanlist: You have to select a scanlist first. Delete scan list? Delete scan list? Delete scan list %1? Delete scan list '%1'? Delete scan lists? Delete scan lists? Delete %1 scan lists? Delete %1 scan lists? Add Scan List Add Scan List Alt++ Alt++ Delete Scan List Delete Scan List Alt+- Alt+- ScanListsWrapper Scan-List Scanlist SearchPopup Ctrl+F Ctrl+F %1/%2 %1/%2 SelectiveCallBox None None CTCSS CTCSS DCS DCS Hz Hz Inverted Inverted SettingsDialog Warning! Warning! Settings Settings Location Location System location System location Locator Locator Repeater Info Sources Repeater Info Sources enable enable Radio Programming Radio Programming Update codeplug Update codeplug <html><head/><body><p>Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.</p><p><br/></p><p>If selected, QDMR downloads the codeplug from the radio and updates only those settings specified. The remaining settings within the radio are not touched (recommended).</p></body></html> <html><head/><body><p>Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.</p><p><br/></p><p>If selected, QDMR downloads the codeplug from the radio and updates only those settings specified. The remaining settings within the radio are not touched (recommended).</p></body></html> Auto-enable GPS Auto-enable GPS When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically. When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically. When a roaming zone is defined and used by any channel, the automatic roaming gets enabled. When a roaming zone is defined and used by any channel, the automatic roaming gets enabled. Auto-enable roaming Auto-enable roaming Data Sources Data Sources World World North America North America Programming Programming Radio Interfaces Radio Interfaces disable auto-detect disable auto-detect Ignore verification warnings Ignore verification warnings <html><head/><body><p>As the communication interface to the radio is kept open after verification, time-outs may occur and the code-plug upload may fail when the verification dialog pops up. To prevent this, verification warnings can be ignored, eliminating the time-gap between verification and upload. Verification errors still prevent the upload.</p></body></html> <html><head/><body><p>As the communication interface to the radio is kept open after verification, time-outs may occur and the code-plug upload may fail when the verification dialog pops up. To prevent this, verification warnings can be ignored, eliminating the time-gap between verification and upload. Verification errors still prevent the upload.</p></body></html> Ignore frequency limits Ignore frequency limits Do not set this option unless you know what you are doing. Do not set this option unless you know what you are doing. Call-Sign DB Call-Sign DB Limit number of DB entries Limit number of DB entries When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent). When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent). Number of DB entries Number of DB entries Specifies the number of DB entries (if enabled above). Specifies the number of DB entries (if enabled above). Select using my DMR ID Select using my DMR ID If enabled, the entries are selected using the users DMR ID. If enabled, the entries are selected using the users DMR ID. Select using prefixes Select using prefixes If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries. If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries. Extensions Extensions Extended Features Extended Features Show commercial features Show commercial features Show device extensions Show device extensions VerifyDialog Verify Codeplug Verify Codeplug ZoneDialog Edit Zone Edit zone Basic Basic <html><head/><body><p align="justify"><span style=" font-weight:600;">Note:</span> Zones are collections of channels that are usually valid for a specific region. I.e., a collection of channels for repeaters within a certain region. </p><p align="justify">QDMR manages zones by allowing for two independent channel lists for each VFO of the radio (if it has two). Many radios however, allow one to assign zones to each VFO individually. In these cases, QDMR will split the zone into two (A &amp; B) and program them individually into the radio.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> <html><head/><body><p align="justify"><span style=" font-weight:600;">Note:</span> Zones are collections of channels that are usually valid for a specific region. I.e., a collection of channels for repeaters within a certain region. </p><p align="justify">QDMR manages zones by allowing for two independent channel lists for each VFO of the radio (if it has two). Many radios however, allow one to assign zones to each VFO individually. In these cases, QDMR will split the zone into two (A &amp; B) and program them individually into the radio.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Name Name Channels A Channels A add add remove remove Channels B Channels B Extension Extension Create Zone Create zone Cannot remove channel Cannot remove channel Select at least one channel first. Select at least one channel first. ZoneListView Cannot delete zone Cannot delete zone Cannot delete zone: You have to select a zone first. Cannot delete zone: You have to select a zone first. Delete zone? Delete zone? Delete zone %1? Delete zone '%1'? Delete zones? Delete zones? Delete %1 zones? Delete %1 zones? Add Zone Add Zone Alt++ Alt++ Delete Zone Delete Zone Alt+- Alt+- ZoneListWrapper Zone Zone aprssystemdialog Edit APRS System Edit APRS System Basic Basic Name Name Channel Channel Source Source Destination Destination Path Path Icon Icon Update period [s] Update period [s] Message Message Extensions Extensions qdmr-0.12.3/i18n/fr.ts000066400000000000000000004520221501654372000143060ustar00rootroot00000000000000 APRSSystem [None] [Aucun] Police station Digipeater Phone DX cluster HF gateway Plane small Mobile Satellite station Wheel Chair Snowmobile Red cross Boy scout Home X Red dot Circle 0 Circle 1 Circle 2 Circle 3 Circle 4 Circle 5 Circle 6 Circle 7 Circle 8 Circle 9 Fire Campground Motorcycle Rail engine Car File server HC Future Aid station BBS Canoe Eyeball Tractor Grid Square Hotel TCP/IP School Logon MacOS NTS station Balloon Police car TBD RV Shuttle SSTV Bus ATV Weather service Helo Yacht MS Windows Jogger Triangle PBBS Plane large Weather station Dish antenna Ambulance Bike ICP Fire station Horse Fire truck Glider Hospital IOTA Jeep Truck small Laptop Mic-E Node EOC Rover Grid Antenna Power boat Truck stop Truck large Van Water XAPRS Yagi Shelter APRSSystemDialog Create APRS system Créer un système APRS Edit APRS system Modifier le système APRS [Selected] AboutDialog About qdmr À propos de qdrm Supported Radios Radios prises en charge AnalogChannelDialog [None] [Aucun] Edit Analog Channel Modifier le canal analogique <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and CTCSS tones are filled in (if applicable).</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Basiques Name Nom Rx Frequency Fréquence RX Tx Frequency Fréquence TX Power Énergie Max Max High Haute Mid Moyenne Low Basse Min Min Default Défaut Tx Timeout Off VOX Level Rx Only Scan List Tx Admit Always Channel Free Tone Squelch Rx Tone Tx Tone Bandwidth Narrow (12.5 kHz) Wide (25 kHz) APRS Open Extensions Application Unsaved changes to codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. Open codeplug Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*) Cannot open file Cannot read codeplug from file '%1': %2 Cannot read codeplug. Save codeplug Codeplug Files (*.yaml *.yml) Please use new YAML format. Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works. Cannot save codeplug to file '%1': %2 Cannot save codeplug Cannot save codeplug to file '%1'. Export codeplug CHIRP CSV Files (*.csv) Cannot export codeplug Cannot export codeplug to file '%1': %2 Import codeplug CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml) Cannot import codeplug Cannot import codeplug from '%1': %2 Do not know, how to handle file '%1'. No matching devices found. Cannot connect to radio Cannot connect to radio: %1 Radio found Found device '%1'. No radio found No matching device was found. Verification success The codeplug was successfully verified with the radio '%1' Read ... Read error Read complete Upload ... Cannot write call-sign DB. The detected radio '%1' does not support a call-sign DB. The detected radio '%1' does support a call-sign DB. This feature, however, is not implemented yet. QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set. Write call-sign DB ... Cannot write satellite config. The detected radio '%1' does not support satellite tracking. The detected radio '%1' does support satellite tracking. This feature, however, is not implemented yet. Write satellite config ... Write error Write complete %1 (alias for %2 %3) ChannelListView Select a single channel first To clone a channel, please select a single channel to clone. Cannot delete channel Cannot delete channel: You have to select a channel first. Delete channel? Delete channel %1? Delete %1 channels? Add Analog Channel Alt+A Add Digital Channel Alt+D Clone Channel Alt+C Delete Channel Alt+- ChannelListWrapper FM DMR [Default] Max Max High Haute Mid Moyenne Low Basse Min Min Off On Always Free Color Tone [None] [Aucun] Open Wide Narrow Type Name Nom Rx Frequency Fréquence RX Tx Frequency Fréquence TX Power Énergie Timeout Rx Only Admit Scanlist Zones CC TS RX Group List TX Contact DMR ID GPS/APRS Roaming Squelch Rx Tone Tx Tone Bandwidth ChannelRefListWrapper Channel ChannelSelectionDialog Select a channel: ConfigMergeDialog Merging codeplugs ... <html><head/><body><p><span style=" font-weight:600;">Conflict resolution strategies:</span></p><p>If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.</p></body></html> Items are all atomic objects like radio IDs, channels, contacts and roaming channels. Items Ignore Override Duplicate Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones. Sets Merge Ignores any duplicate item. Replaces any duplicate item with the imported one. Imports any duplicate item with a modified name. Ignores any duplicate set. Replaces any duplicate set with the imported one. Imports any duplicate set with a modified name. Merges duplicate sets. ConfigObjectListView Cannot move items. Cannot move items: You have to select at least one item first. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTableView Cannot move items. Cannot move items: You have to select at least one item first. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTypeSelectionDialog An instance of %1. <p>%1<p><p style="margin-left:10px;">%2</p> Create extension object Select the class of object to create ContactListView Cannot delete contact Cannot delete contact: You have to select a contact first. Delete contact? Delete contact %1? Delete contacts? Delete %1 contacts? Adds a contact to the list. Add Digital Contact Alt++ Add DTMF Contact Delete contact button Delete Contact Alt+- ContactListWrapper DTMF On Off Private Call Group Call All Call Type Name Nom Number RX Tone DMRContactDialog Create DMR Contact Edit DMR Contact Private Call Group Call All Call Basic Basiques Type Name Nom Number Ring Extensions DMRIDDialog Basic Basiques Name Nom DMR ID Extensions DTMFContactDialog Create DTMF Contact Edit DMR Contact Basic Basiques Name Nom Number Ring Extensions DeviceSelectionDialog Select a device <html><head/><body><p>There is either more than one device detected or the one found is not considered save to access. Either way, select the device to use.</p></body></html> DigitalChannelDialog [None] [Aucun] [Default] Edit Digital Channel <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and color-code are filled in.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Basiques Name Nom Rx Frequency Fréquence RX Tx Frequency Fréquence TX Power Énergie Tx Timeout Rx Only Scan List Max Max High Haute Mid Moyenne Low Basse Min Min Default Défaut Specifies the transmit timeout in seconds. A value of 0 means that the transmit timeout is disabled. Off VOX Level Tx Admit Always Channel Free Color Code Time Slot TS 1 TS 2 Rx Group List Tx Contact Positioning System Roaming Zone DMR ID Extended Device specific settings ErrorMessageView Error: Unknown. Error: %1 Traceback: ExtensionView Cannot create extension. Cannot create extension, consider reporting a bug. Cannot create list element. Cannot create list element, consider reporting a bug. Create Remove GPSSystemDialog Create DMR APRS Sysmtem Edit DMR APRS Sysmtem [Selected] Edit GPS System Basic Basiques Name Nom Destination Update period Revert Channel Extensions GeneralSettingsView DMR Radio ID DMR ID Radio Name Enter your name or callsign. Name/Callsign Boot Settings Intro Line 1 First greeting line (if supported by the radio). Intro line 1 Intro Line 2 Second greeting line (if supported by the radio). Intro line 2 Audio Settings MIC Amp. Speech Synthesis Channel Default Values Power Énergie Max Max High Haute Mid Moyenne Low Basse Min Min Squelch Open Transmit Timeout Off VOX Level Extensions GroupListWrapper Contact GroupListsView Cannot delete RX group list Cannot delete RX group lists: You have to select a group list first. Delete RX group list? Delete RX group list %1? Delete %1 RX group lists? Add RX Group Alt++ Delete RX Group Alt+- GroupListsWrapper RX Group Lists MainWindow File Device Help Databases Toolbar New Creates a new Codeplug. Ctrl+N Open ... <html><head/><body><p>Imports a codeplug from &quot;conf&quot; files.</p></body></html> Ctrl+O Save ... <html><head/><body><p>Saves the codeplug in a &quot;conf&quot; file.</p></body></html> Ctrl+S Quit Quits the application. Ctrl+Q Detect Detect connected radios. Verify <html><head/><body><p>Verifies the current codeplug with connected radios.</p></body></html> Ctrl+R Read Reads a codeplug from connected radios. Write Writes the codeplug to the connected radio. About qdmr À propos de qdrm Read the handbook. F1 Settings Shows settings dialog Write Callsign DB Writes call-sign DB to radio. Refresh Callsign DB Refreshes the downloaded callsign DB Refresh Talkgroup DB Refreshes the downloaded talkgroup DB Export to CHIRP ... Exports all FM channels to CHRIP CSV. Import ... Imports and merges a codeplug into the current one. Refresh Orbital Elements Refreshes the orbital elements. Edit Satellites ... Opens an editor to edit your satellite database. Write satellites Writes the orbital elements and transponder information onto the connected device. Radio IDs Contacts Group Lists Channels Zones Scan Lists GPS/APRS Roaming Channels Roaming Zones Extensions Unsaved changes to codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. MultiChannelSelectionDialog [Selected] Select a channel: MultiGroupCallSelectionDialog Show private calls Select a group call: MultiRoamingChannelSelectionDialog Select roaming channels PositioningSystemListView Cannot delete GPS system Cannot delete GPS system: You have to select a GPS system first. Delete positioning system? Delete positioning system %1? Delete %1 positioning systems? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support GPS or APRS. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add GPS System Alt+G Add APRS System Alt+A Delete Position System Alt+- PositioningSystemListWrapper DMR APRS [None] [Aucun] [Selected] Type Name Nom Destination Period [s] Channel Message PropertyDelegate False True [None] [Aucun] PropertyWrapper new element Property Value Description true false [None] [Aucun] Instance of %1 List of %1 instances QObject [None] [Aucun] RXGroupListDialog Create Group List Edit Group List Cannot remove group call Cannot remove group call: You have to select at least one group call first. Basic Basiques Name Nom Add Contact Alt++ Remove Contact Alt+- Extensions RadioIDListView Cannot delete radio IDs Cannot delete radio IDs: You have to select a radio ID first. Delete radio ID? Delete radio ID %1? Delete scan lists? Delete %1 scan lists? Default Radio ID Add Radio ID Delete Radio ID RadioIdListWrapper Type Name Nom Number RadioSelectionDialog Cannot auto-detect radio Select a specific radio ReleaseNotes Cannot download release notes from https://github.com/hmatuschek/qdmr %1 Cannot read release notes from https://github.com/hmatuschek/qdmr Release is not a JSON object! Cannot read release notes from https://github.com/hmatuschek/qdmr Release does not contain a release note. qDMR was updated to version %1 RoamingChannelDialog Name Nom RX Frequency [MHz] TX Frequency [MHz] Time Slot Color Code Selected Edit roaming channel Create roaming channel TS 1 TS 2 RoamingChannelListView <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p><span style=" font-weight:600;">Hint:</span> You do not need to add roaming channel explicitly, just create roaming zones and add channels there. These channels are then added to this list.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Channel Delete Roaming Channel Cannot delete roaming channel Cannot delete roaming channel: You have to select a channel first. Delete roaming channel? Delete roaming channel %1? Delete %1 roaming channel? RoamingChannelListWrapper [Selected] Name Nom RX Frequency TX Frequency TS Zones CC RoamingChannelRefListWrapper Roaming Channel RoamingListWrapper %1 (containing %2 channels) Roaming zone RoamingZoneDialog Create Roaming Zone Set Roaming Zone Cannot remove channels. Cannot remove channels. Select at least one channel first. Basic Basiques Name: Add Roaming Channel Add DMR Channel Alt++ Remove Channel Alt+- Extension RoamingZoneListView Generate roaming zone Create a roaming zone by collecting all channels with these group calls. Cannot delete roaming zone Cannot delete roaming zone: You have to select a zone first. Delete roaming zone? Delete roaming zone %1? Delete roaming zones? Delete %1 roaming zones? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Zone Alt++ Generate Roaming Zone Delete Roaming Zone Alt+- SatelliteDatabaseDialog Edit satellite database Add Delete SatelliteSelectionDialog Select a satellite ScanListDialog Edit Scan List Basic Basiques Name Nom Primary Channel (50%) Secondary Channel (25%) Transmit Channel Add Channel Alt++ Remove Channel Alt+- Extensions Create Scan List [None] [Aucun] [Selected] [Last] ScanListsView Cannot delete scanlist Cannot delete scanlist: You have to select a scanlist first. Delete scan list? Delete scan list %1? Delete scan lists? Delete %1 scan lists? Add Scan List Alt++ Delete Scan List Alt+- ScanListsWrapper Scan-List SearchPopup Ctrl+F %1/%2 SelectiveCallBox None CTCSS DCS Hz Inverted SettingsDialog Warning! Settings Location System location Locator Repeater Info Sources enable Radio Programming Update codeplug <html><head/><body><p>Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.</p><p><br/></p><p>If selected, QDMR downloads the codeplug from the radio and updates only those settings specified. The remaining settings within the radio are not touched (recommended).</p></body></html> Auto-enable GPS When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically. When a roaming zone is defined and used by any channel, the automatic roaming gets enabled. Auto-enable roaming Data Sources World North America Programming Radio Interfaces disable auto-detect Ignore verification warnings <html><head/><body><p>As the communication interface to the radio is kept open after verification, time-outs may occur and the code-plug upload may fail when the verification dialog pops up. To prevent this, verification warnings can be ignored, eliminating the time-gap between verification and upload. Verification errors still prevent the upload.</p></body></html> Ignore frequency limits Do not set this option unless you know what you are doing. Call-Sign DB Limit number of DB entries When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent). Number of DB entries Specifies the number of DB entries (if enabled above). Select using my DMR ID If enabled, the entries are selected using the users DMR ID. Select using prefixes If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries. Extensions Extended Features Show commercial features Show device extensions VerifyDialog Verify Codeplug ZoneDialog Edit Zone Basic Basiques <html><head/><body><p align="justify"><span style=" font-weight:600;">Note:</span> Zones are collections of channels that are usually valid for a specific region. I.e., a collection of channels for repeaters within a certain region. </p><p align="justify">QDMR manages zones by allowing for two independent channel lists for each VFO of the radio (if it has two). Many radios however, allow one to assign zones to each VFO individually. In these cases, QDMR will split the zone into two (A &amp; B) and program them individually into the radio.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Name Nom Channels A add remove Channels B Extension Create Zone Cannot remove channel Select at least one channel first. ZoneListView Cannot delete zone Cannot delete zone: You have to select a zone first. Delete zone? Delete zone %1? Delete zones? Delete %1 zones? Add Zone Alt++ Delete Zone Alt+- ZoneListWrapper Zone aprssystemdialog Edit APRS System Basic Basiques Name Nom Channel Source Destination Path Icon Update period [s] Message Extensions qdmr-0.12.3/i18n/it.ts000066400000000000000000004561071501654372000143230ustar00rootroot00000000000000 APRSSystem [None] Police station Stazione di polizia Digipeater Digipeater Phone Telefono DX cluster Cluster DX HF gateway Gateway HF Plane small Plane small Mobile Satellite station Stazione satellitare mobile Wheel Chair Sedia a rotelle Snowmobile Motoslitta Red cross Croce rossa Boy scout Boy scout Home Home X X Red dot Punto rosso Circle 0 Cerchio 0 Circle 1 Cerchio 1 Circle 2 Cerchio 2 Circle 3 Cerchio 3 Circle 4 Cerchio 4 Circle 5 Cerchio 5 Circle 6 Cerchio 6 Circle 7 Cerchio 7 Circle 8 Cerchio 8 Circle 9 Cerchio 9 Fire Fuoco Campground Motorcycle Rail engine Car File server HC Future Aid station BBS Canoe Eyeball Tractor Grid Square Hotel TCP/IP School Logon MacOS NTS station Balloon Police car TBD RV Shuttle SSTV Bus ATV Weather service Helo Yacht MS Windows Jogger Triangle PBBS Plane large Weather station Dish antenna Ambulance Bike ICP Fire station Horse Fire truck Glider Hospital IOTA Jeep Truck small Laptop Mic-E Node EOC Rover Grid Antenna Power boat Truck stop Truck large Van Water XAPRS Yagi Shelter APRSSystemDialog Create APRS system Crea sistema APRS Edit APRS system Modifica sistema APRS [Selected] AboutDialog About qdmr Informazioni su qdmr Supported Radios Radio supportate AnalogChannelDialog [None] [None] Edit Analog Channel Modifica canale analogico <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and CTCSS tones are filled in (if applicable).</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Base Name Nome Rx Frequency Frequenza RX Tx Frequency Frequenza TX Power Potenza Max Massima High Alta Mid Media Low Bassa Min Minima Default Default Tx Timeout Timeout TX Off Off VOX Level Livello VOX Rx Only Solo RX Scan List Lista Scan Tx Admit Admit TX Always Sempre Channel Free Canale libero Tone Tono Squelch Squelch Rx Tone Tono RX Tx Tone Tono TX Bandwidth Larghezza di banda Narrow (12.5 kHz) Narrow (12.5 kHz) Wide (25 kHz) Wide (25 kHz) APRS APRS Open Open Extensions Estensioni Application Unsaved changes to codeplug. Modifiche non salvate al codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. Ci sono modifiche non salvate al codeplug corrente. Queste modifiche andranno perse se procedi. Open codeplug Apri codeplug Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*) File Codeplug (*.yaml);;File Codeplug, vecchio formato (*.conf *.csv *.txt);;All Files (*) Cannot open file Impossibile aprire il file Cannot read codeplug from file '%1': %2 Impossibile leggere il codeplug dal file '%1': %2 Cannot read codeplug. Impossibile leggere il codeplug. Save codeplug Salva codeplug Codeplug Files (*.yaml *.yml) File Codeplug (*.yaml *.yml) Please use new YAML format. Utilizzare il nuovo formato YAML. Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works. Il salvataggio nel vecchio formato conf basato su tabelle è stato disabilitato dalla la versione 0.9.0 in poi. La lettura di questi file è ancora possibile. Cannot save codeplug to file '%1': %2 Impossibile salvare il codeplug nel file '%1': %2 Cannot save codeplug Impossibile salvare il codeplug Cannot save codeplug to file '%1'. Impossibile salvare il codeplug nel file '%1'. Export codeplug CHIRP CSV Files (*.csv) Cannot export codeplug Cannot export codeplug to file '%1': %2 Import codeplug CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml) Cannot import codeplug Cannot import codeplug from '%1': %2 Do not know, how to handle file '%1'. No matching devices found. Nessun dispositivo corrispondente trovato. Cannot connect to radio Impossibile connettersi alla radio Cannot connect to radio: %1 Impossibile connettersi alla radio: %1 Radio found Radio rilevata Found device '%1'. Rilevato dispositivo '%1'. No radio found Nessuna radio rilevata No matching device was found. Nessun dispositivo corrispondente trovato. Verification success Verifica riuscita The codeplug was successfully verified with the radio '%1' Il codeplug è stato verificato con successo con la radio '%1 Read ... Lettura … Read error Errore di lettura Read complete Lettura completata Upload ... Caricamento … Cannot write call-sign DB. Impossibile scrivere il DB dei nominativi. The detected radio '%1' does not support a call-sign DB. La radio rilevata '%1' non supporta un DB dei nominativi. The detected radio '%1' does support a call-sign DB. This feature, however, is not implemented yet. La radio rilevata '%1' supporta un DB dei nominativi. Tuttavia, questa funzione non è ancora stata implementata. QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set. QDMR seleziona i nominativi da scrivere in base all'ID DMR predefinito della radio. Non è stato impostato alcun ID predefinito. Write call-sign DB ... Scrittura del DB dei nominativi … Cannot write satellite config. The detected radio '%1' does not support satellite tracking. The detected radio '%1' does support satellite tracking. This feature, however, is not implemented yet. Write satellite config ... Write error Errore di scrittura Write complete Scrittura completata %1 (alias for %2 %3) %1 (alias for %2 %3) ChannelListView Select a single channel first Prima selezionare un canale singolo To clone a channel, please select a single channel to clone. Per clonare un canale, selezionare un canale singolo da clonare. Cannot delete channel Impossibile eliminare canale Cannot delete channel: You have to select a channel first. Impossibile eliminare canale: Selezionare prima un canale. Delete channel? Eliminare canale? Delete channel %1? Eliminare canale '%1'? Delete %1 channels? Eliminare %1 canali? Add Analog Channel Aggiungi Canale Analogico Alt+A Alt+A Add Digital Channel Aggiungi Canale Digitale Alt+D Alt+D Clone Channel Clona Canale Alt+C Alt+C Delete Channel Elimina Canale Alt+- Alt+- ChannelListWrapper FM FM DMR DMR [Default] [Predefinito] Max Massima High Alta Mid Media Low Bassa Min Minima Off Off On On Always Sempre Free Free Color Colore Tone Tono [None] [Nessuno/a] Open Open Wide Wide Narrow Narrow Type Tipo Name Nome Rx Frequency Frequenza RX Tx Frequency Frequenza TX Power Potenza Timeout Timeout Rx Only Solo RX Admit Admit Scanlist Lista scan Zones Zone CC CC TS TS RX Group List Lista Gruppo RX TX Contact Contatto TX DMR ID ID DMR GPS/APRS GPS/APRS Roaming Roaming Squelch Squelch Rx Tone Tono RX Tx Tone Tono TX Bandwidth Larghezza di banda ChannelRefListWrapper Channel Canale ChannelSelectionDialog Select a channel: Seleziona un canale: ConfigMergeDialog Merging codeplugs ... <html><head/><body><p><span style=" font-weight:600;">Conflict resolution strategies:</span></p><p>If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.</p></body></html> Items are all atomic objects like radio IDs, channels, contacts and roaming channels. Items Ignore Override Duplicate Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones. Sets Merge Ignores any duplicate item. Replaces any duplicate item with the imported one. Imports any duplicate item with a modified name. Ignores any duplicate set. Replaces any duplicate set with the imported one. Imports any duplicate set with a modified name. Merges duplicate sets. ConfigObjectListView Cannot move items. Impossibile spostare gli elementi. Cannot move items: You have to select at least one item first. Impossibile spostare gli elementi: Prima selezionare almeno un elemento. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTableView Cannot move items. Impossibile spostare gli elementi. Cannot move items: You have to select at least one item first. Impossibile spostare elementi: Prima selezionare almeno un elemento. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTypeSelectionDialog An instance of %1. An instance of '%1'. <p>%1<p><p style="margin-left:10px;">%2</p> <p>%1<p><p style="margin-left:10px;">%2</p> Create extension object Crea oggetto di estensione Select the class of object to create Selezionare la classe di oggetto da creare ContactListView Cannot delete contact Impossibile eliminare contatto Cannot delete contact: You have to select a contact first. Impossibile eliminare contatto: Prima selezionare un contatto. Delete contact? Eliminare contatto? Delete contact %1? Eliminare contatto '%1'? Delete contacts? Eliminare contatti? Delete %1 contacts? Eliminare %1 contatti? Adds a contact to the list. Aggiunge un contatto alla lista. Add Digital Contact Aggiungi Contatto Digitale Alt++ Alt++ Add DTMF Contact Aggiungi Contatto DTMF Delete contact button Pulsante Elimina contatto Delete Contact Elimina Contatto Alt+- Alt+- ContactListWrapper DTMF DTMF On On Off Off Private Call Chiamata Privata Group Call Chiamata di Gruppo All Call Chiamata Generale Type Tipo Name Nome Number Numero RX Tone Tono RX DMRContactDialog Create DMR Contact Crea Contatto DMR Edit DMR Contact Modifica Contatto DMR Private Call Chiamata Privata Group Call Chiamata di Gruppo All Call Chiamata Generale Basic Base Type Tipo Name Nome Number Numero Ring Ring Extensions Estensioni DMRIDDialog Basic Base Name Nome DMR ID ID DMR Extensions Estensioni DTMFContactDialog Create DTMF Contact Crea Contatto DTMF Edit DMR Contact Modifica Contatto DMR Basic Base Name Nome Number Numero Ring Ring Extensions Estensioni DeviceSelectionDialog Select a device Seleziona un dispositivo <html><head/><body><p>There is either more than one device detected or the one found is not considered save to access. Either way, select the device to use.</p></body></html> DigitalChannelDialog [None] [None] [Default] [Predefinito] Edit Digital Channel Modifica canale digitale <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and color-code are filled in.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Base Name Nome Rx Frequency Frequenza RX Tx Frequency Frequenza TX Power Potenza Tx Timeout Timeout TX Rx Only Solo RX Scan List Lista Scan Max Massima High Alta Mid Media Low Bassa Min Minima Default Predefinita Specifies the transmit timeout in seconds. A value of 0 means that the transmit timeout is disabled. Specifica il timeout di trasmissione in secondi. Un valore pari a 0 significa che il timeout di trasmissione è disabilitato. Off Off VOX Level Livello VOX Tx Admit Admit TX Always Sempre Channel Free Canale Libero Color Code Color Code Time Slot Time Slot TS 1 TS 1 TS 2 TS 2 Rx Group List Lista Gruppo Tx Contact Contatto TX Positioning System Sistema di Posizionamento Roaming Zone Zona Roaming DMR ID ID DMR Extended Esteso Device specific settings Impostazioni specifiche del dispositivo ErrorMessageView Error: Unknown. Errore: Sconosciuto. Error: %1 Errore: %1 Traceback: Traceback: ExtensionView Cannot create extension. Impossibile creare estensione. Cannot create extension, consider reporting a bug. Impossibile creare estensione, puoi provare a segnalare un bug. Cannot create list element. Impossibile creare un elemento nella lista. Cannot create list element, consider reporting a bug. Impossibile crere un elemento nella lista, puoi provare a segnalare un bug. Create Crea Remove Elimina GPSSystemDialog Create DMR APRS Sysmtem Crea Sistema APRS DMR Edit DMR APRS Sysmtem Modifica Sistema APRS DMR [Selected] [Selezionato] Edit GPS System Modifica Sistema GPS Basic Base Name Nome Destination Destinazione Update period Revert Channel Revert Channel Extensions Estensioni GeneralSettingsView DMR Radio ID ID DMR della Radio DMR ID DMR ID Radio Name Nome della Radio Enter your name or callsign. Inserisci il tuo nome o nominativo. Name/Callsign Nome/Nominativo Boot Settings Impostazioni di avvio Intro Line 1 Intro Riga 1 First greeting line (if supported by the radio). Prima riga di benvenuto (se supportata dalla radio). Intro line 1 Intro riga 1 Intro Line 2 Intro Riga 2 Second greeting line (if supported by the radio). Seconda riga di benvenuto (se supportata dalla radio). Intro line 2 Intro riga 2 Audio Settings Impostazioni Audio MIC Amp. Amp. Microfono Speech Synthesis Sintesi Vocale Channel Default Values Valori Predefiniti dei Canali Power Potenza Max Massima High Alta Mid Media Low Bassa Min Minima Squelch Squelch Open Open Transmit Timeout Timeout di Trasmissione Off Off VOX Level Livello VOX Extensions Estensioni GroupListWrapper Contact Contatto GroupListsView Cannot delete RX group list Impossibile eliminare lista di gruppo Cannot delete RX group lists: You have to select a group list first. Delete RX group list? Delete RX group list %1? Delete %1 RX group lists? Add RX Group Aggiungi Lista di Gruppo Alt++ Alt++ Delete RX Group Elimina Lista di Gruppo Alt+- Alt+- GroupListsWrapper RX Group Lists Liste di Gruppo MainWindow File File Device Dispositivo Help Aiuto Databases Database Toolbar New Nuovo Creates a new Codeplug. Crea un nuovo Codeplug. Ctrl+N Ctrl+N Open ... Apri … <html><head/><body><p>Imports a codeplug from &quot;conf&quot; files.</p></body></html> <html><head/><body><p>Importa un codeplug da file &quot;conf&quot;.</p></body></html> Ctrl+O Ctrl+O Save ... Salva … <html><head/><body><p>Saves the codeplug in a &quot;conf&quot; file.</p></body></html> <html><head/><body><p>Salva il codeplug in un file &quot;conf&quot;.</p></body></html> Ctrl+S Ctrl+S Quit Esci Quits the application. Esci dall'applicazione. Ctrl+Q Ctrl+Q Detect Rileva Detect connected radios. Rileva le radio collegate. Verify Verifica <html><head/><body><p>Verifies the current codeplug with connected radios.</p></body></html> Verifica il codeplug corrente con le radio collegate. Ctrl+R Ctrl+R Read Leggi Reads a codeplug from connected radios. Legge il codeplug dalle radio collegate. Write Scrivi Writes the codeplug to the connected radio. Scrive il codeplug sulla radio collegata. About qdmr Informazioni su qdmr Read the handbook. Leggi il manuale. F1 F1 Settings Impostazioni Shows settings dialog Mostra la finestra delle impostazioni. Write Callsign DB Scrivi il DB dei nominativi Writes call-sign DB to radio. Scrive il DB dei nominativi sulla radio. Refresh Callsign DB Aggiorna il DB dei nominativi Refreshes the downloaded callsign DB Aggiorna il DB dei nominativi scaricati Refresh Talkgroup DB Aggiorna DB dei talk-group Refreshes the downloaded talkgroup DB Aggiorna il DB dei talk-group scaricati Export to CHIRP ... Exports all FM channels to CHRIP CSV. Import ... Imports and merges a codeplug into the current one. Refresh Orbital Elements Refreshes the orbital elements. Edit Satellites ... Opens an editor to edit your satellite database. Write satellites Writes the orbital elements and transponder information onto the connected device. Radio IDs ID Radio Contacts Contatti Group Lists Liste Gruppi Channels Canali Zones Zone Scan Lists Liste Scan GPS/APRS GPS/APRS Roaming Channels Canali Roaming Roaming Zones Zone Roaming Extensions Estensioni Unsaved changes to codeplug. Modifiche non salvate al codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. Ci sono modifiche non salvate al codeplug corrente. Queste modifiche andranno perse se procedi. MultiChannelSelectionDialog [Selected] [Selezionato/a] Select a channel: Seleziona un canale: MultiGroupCallSelectionDialog Show private calls Mostra chiamate private Select a group call: Seleziona una chiamata di gruppo: MultiRoamingChannelSelectionDialog Select roaming channels Seleziona canali roaming PositioningSystemListView Cannot delete GPS system Impossibile eliminare sistema GPS Cannot delete GPS system: You have to select a GPS system first. Impossibile eliminare sistema GPS: Prima selezionare un sistema GPS. Delete positioning system? Eliminare sistema di posizionamento? Delete positioning system %1? Eliminare sistema di posizionamento '%1'? Delete %1 positioning systems? Eliminare %1 sistemi di posizionamento? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support GPS or APRS. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add GPS System Aggiungi Sistema GPS Alt+G Alt+G Add APRS System Aggiungi Sistema APRS Alt+A Alt+A Delete Position System Elimina Sistema di Posizionamento Alt+- Alt+- PositioningSystemListWrapper DMR DMR APRS APRS [None] [None] [Selected] [Selezionato/a] Type Tipo Name Nome Destination Destinazione Period [s] Periodo [s] Channel Canale Message Messaggio PropertyDelegate False Falso True Vero [None] [None] PropertyWrapper new element nuovo elemento Property Proprietà Value Valore Description Descrizione true vero false falso [None] [None] Instance of %1 List of %1 instances QObject [None] [None] RXGroupListDialog Create Group List Edit Group List Cannot remove group call Cannot remove group call: You have to select at least one group call first. Basic Base Name Nome Add Contact Aggiungi Contatto Alt++ Alt++ Remove Contact Rimuovi Contatto Alt+- Alt+- Extensions Estensioni RadioIDListView Cannot delete radio IDs Impossibile eliminare ID radio Cannot delete radio IDs: You have to select a radio ID first. Impossibile eliminare ID radio: Prima selezionare un ID radio. Delete radio ID? Eliminare ID radio? Delete radio ID %1? Eliminare ID radio '%1'? Delete scan lists? Eliminare liste scan? Delete %1 scan lists? Eliminare %1 liste scan? Default Radio ID ID Radio predefinito Add Radio ID Aggiungi ID Radio Delete Radio ID Elimina ID Radio RadioIdListWrapper Type Tipo Name Nome Number Numero RadioSelectionDialog Cannot auto-detect radio Impossibile rilevare automaticamente la radio Select a specific radio Seleziona una radio specifica ReleaseNotes Cannot download release notes from https://github.com/hmatuschek/qdmr %1 Impossibile scaricare le note di rilascio da https://github.com/hmatuschek/qdmr %1 Cannot read release notes from https://github.com/hmatuschek/qdmr Release is not a JSON object! Impossibile leggere le note di rilascio da https://github.com/hmatuschek/qdmr Il rilascio non è un oggetto JSON! Cannot read release notes from https://github.com/hmatuschek/qdmr Release does not contain a release note. qDMR was updated to version %1 RoamingChannelDialog Name Nome RX Frequency [MHz] TX Frequency [MHz] Time Slot Time Slot Color Code Color Code Selected Edit roaming channel Create roaming channel TS 1 TS 1 TS 2 TS 2 RoamingChannelListView <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p><span style=" font-weight:600;">Hint:</span> You do not need to add roaming channel explicitly, just create roaming zones and add channels there. These channels are then added to this list.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Channel Delete Roaming Channel Cannot delete roaming channel Cannot delete roaming channel: You have to select a channel first. Delete roaming channel? Delete roaming channel %1? Delete %1 roaming channel? RoamingChannelListWrapper [Selected] Name Nome RX Frequency TX Frequency TS TS Zones Zone CC CC RoamingChannelRefListWrapper Roaming Channel RoamingListWrapper %1 (containing %2 channels) Roaming zone RoamingZoneDialog Create Roaming Zone Set Roaming Zone Cannot remove channels. Cannot remove channels. Select at least one channel first. Basic Base Name: Add Roaming Channel Add DMR Channel Alt++ Alt++ Remove Channel Alt+- Alt+- Extension Estensione RoamingZoneListView Generate roaming zone Create a roaming zone by collecting all channels with these group calls. Cannot delete roaming zone Cannot delete roaming zone: You have to select a zone first. Delete roaming zone? Delete roaming zone %1? Delete roaming zones? Delete %1 roaming zones? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Zone Alt++ Alt++ Generate Roaming Zone Delete Roaming Zone Alt+- Alt+- SatelliteDatabaseDialog Edit satellite database Add Delete SatelliteSelectionDialog Select a satellite ScanListDialog Edit Scan List Basic Base Name Nome Primary Channel (50%) Secondary Channel (25%) Transmit Channel Add Channel Alt++ Alt++ Remove Channel Alt+- Alt+- Extensions Estensioni Create Scan List [None] [Selected] [Last] ScanListsView Cannot delete scanlist Cannot delete scanlist: You have to select a scanlist first. Delete scan list? Delete scan list %1? Delete scan lists? Eliminare liste scan? Delete %1 scan lists? Eliminare %1 liste scan? Add Scan List Alt++ Alt++ Delete Scan List Alt+- Alt+- ScanListsWrapper Scan-List SearchPopup Ctrl+F %1/%2 %1/%2 SelectiveCallBox None CTCSS DCS Hz Inverted SettingsDialog Warning! Settings Impostazioni Location System location Locator Repeater Info Sources enable Radio Programming Update codeplug <html><head/><body><p>Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.</p><p><br/></p><p>If selected, QDMR downloads the codeplug from the radio and updates only those settings specified. The remaining settings within the radio are not touched (recommended).</p></body></html> Auto-enable GPS When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically. When a roaming zone is defined and used by any channel, the automatic roaming gets enabled. Auto-enable roaming Data Sources World North America Programming Radio Interfaces disable auto-detect Ignore verification warnings <html><head/><body><p>As the communication interface to the radio is kept open after verification, time-outs may occur and the code-plug upload may fail when the verification dialog pops up. To prevent this, verification warnings can be ignored, eliminating the time-gap between verification and upload. Verification errors still prevent the upload.</p></body></html> Ignore frequency limits Do not set this option unless you know what you are doing. Call-Sign DB DB dei Nominativi Limit number of DB entries When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent). Number of DB entries Specifies the number of DB entries (if enabled above). Select using my DMR ID If enabled, the entries are selected using the users DMR ID. Select using prefixes If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries. Se abilitati, questi prefissi ID DMR separati da virgole vengono utilizzati per selezionare le voci del DB dei nominativi. Extensions Estensioni Extended Features Funzionalità Estese Show commercial features Mostra le funzionalità commerciali Show device extensions Mostra estensioni del dispositivo VerifyDialog Verify Codeplug Verifica Codeplug ZoneDialog Edit Zone Modifica zona Basic Base <html><head/><body><p align="justify"><span style=" font-weight:600;">Note:</span> Zones are collections of channels that are usually valid for a specific region. I.e., a collection of channels for repeaters within a certain region. </p><p align="justify">QDMR manages zones by allowing for two independent channel lists for each VFO of the radio (if it has two). Many radios however, allow one to assign zones to each VFO individually. In these cases, QDMR will split the zone into two (A &amp; B) and program them individually into the radio.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> <html><head/><body><p align="justify"><span style=" font-weight:600;">Note:</span> Le zone sono raccolte di canali che di solito sono validi per una regione specifica. Ad esempio, una raccolta di canali per i ripetitori di una certa regione. </p><p align="justify">QDMR gestisce le zone consentendo due elenchi di canali indipendenti per ogni VFO della radio (se ne ha due). Molte radio, tuttavia, consentono di assegnare le zone a ciascun VFO individualmente. In questi casi, il QDMR divide la zona in due (A e B) e le programma singolarmente nella radio..</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Nascondi</span></a></p></body></html> Name Nome Channels A Canali A add aggiungi remove elimina Channels B Canali B Extension Estensione Create Zone Crea zona Cannot remove channel Impossibile eliminare canale Select at least one channel first. Prima selezionare almeno un canale. ZoneListView Cannot delete zone Impossibile eliminare zona Cannot delete zone: You have to select a zone first. Impossibile eliminare zona: Prima selezionare almeno una zona. Delete zone? Eliminare zona? Delete zone %1? Eliminare zona '%1'? Delete zones? Eliminare zone? Delete %1 zones? Eliminare %1 zone? Add Zone Aggiungi Zona Alt++ Alt++ Delete Zone Elimina Zona Alt+- Alt+- ZoneListWrapper Zone aprssystemdialog Edit APRS System Basic Base Name Nome Channel Canale Source Destination Destinazione Path Icon Update period [s] Intervallo di aggiornamento [s] Message Messaggio Extensions Estensioni qdmr-0.12.3/i18n/nl.ts000066400000000000000000004462341501654372000143200ustar00rootroot00000000000000 APRSSystem [None] [Geen] Police station Digipeater Digipeater Phone Telefoon DX cluster DX cluster HF gateway Plane small Vliegtuig klein Mobile Satellite station Mobiel satelliet station Wheel Chair Rolstoel Snowmobile Red cross Boy scout Home Thuis X X Red dot Circle 0 Cirkel 0 Circle 1 Cirkel 1 Circle 2 Cirkel 2 Circle 3 Cirkel 3 Circle 4 Cirkel 4 Circle 5 Cirkel 5 Circle 6 Cirkel 6 Circle 7 Cirkel 7 Circle 8 Cirkel 8 Circle 9 Cirkel 9 Fire Vuur Campground Motorcycle Motorfiets Rail engine Car Auto File server HC Future Aid station BBS Canoe Eyeball Tractor Grid Square Hotel Hotel TCP/IP TCP/IP School School Logon MacOS MacOS NTS station Balloon Ballon Police car Politie auto TBD RV Shuttle SSTV SSTV Bus Bus ATV ATV Weather service Helo Yacht MS Windows MS Windows Jogger Jogger Triangle Driehoek PBBS Plane large Vliegtuig groot Weather station Weerstation Dish antenna Schotelantenne Ambulance Ambulance Bike Fiets ICP Fire station Horse Paard Fire truck Glider Hospital Ziekenhuis IOTA IOTA Jeep Jeep Truck small Laptop Laptop Mic-E Mic-E Node EOC Rover Grid Antenna Antenne Power boat Truck stop Truck large Van Water Water XAPRS XAPRS Yagi Yagi Shelter APRSSystemDialog Create APRS system Edit APRS system [Selected] AboutDialog About qdmr Supported Radios Ondersteunde Radios AnalogChannelDialog [None] [Geen] Edit Analog Channel <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and CTCSS tones are filled in (if applicable).</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Name Naam Rx Frequency RX Frequentie Tx Frequency TX Frequentie Power Vermogen Max Max High Hoog Mid Middel Low Laag Min Min Default Tx Timeout TX Timeout Off Uit VOX Level Rx Only Scan List Scan Lijst Tx Admit Always Altijd Channel Free Kanaal Vrij Tone Toon Squelch Squelch Rx Tone RX Toon Tx Tone TX Toon Bandwidth Bandbreedte Narrow (12.5 kHz) Smal (12.5 kHz) Wide (25 kHz) Breed (25 kHz) APRS APRS Open Open Extensions Extensies Application Unsaved changes to codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. Open codeplug Open codeplug Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*) Cannot open file Kan bestand niet openen Cannot read codeplug from file '%1': %2 Cannot read codeplug. Kan codeplug niet lezen. Save codeplug Codeplug opslaan Codeplug Files (*.yaml *.yml) Codeplug Bestanden (*.yaml *.yml) Please use new YAML format. Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works. Cannot save codeplug to file '%1': %2 Cannot save codeplug Kan codeplug niet opslaan Cannot save codeplug to file '%1'. Export codeplug CHIRP CSV Files (*.csv) CHIRP CSV Bestanden (*.csv) Cannot export codeplug Cannot export codeplug to file '%1': %2 Import codeplug CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml) Cannot import codeplug Cannot import codeplug from '%1': %2 Do not know, how to handle file '%1'. No matching devices found. Cannot connect to radio Kan niet verbinden met radio Cannot connect to radio: %1 Radio found Radio gevonden Found device '%1'. No radio found Geen radio gevonden No matching device was found. Verification success The codeplug was successfully verified with the radio '%1' Read ... Lees … Read error Leesfout Read complete Upload ... Upload … Cannot write call-sign DB. The detected radio '%1' does not support a call-sign DB. The detected radio '%1' does support a call-sign DB. This feature, however, is not implemented yet. QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set. Write call-sign DB ... Cannot write satellite config. The detected radio '%1' does not support satellite tracking. The detected radio '%1' does support satellite tracking. This feature, however, is not implemented yet. Write satellite config ... Write error Schrijffout Write complete %1 (alias for %2 %3) ChannelListView Select a single channel first To clone a channel, please select a single channel to clone. Cannot delete channel Cannot delete channel: You have to select a channel first. Delete channel? Kanaal verwijderen? Delete channel %1? Delete %1 channels? %1 kanalen verwijderen? Add Analog Channel Alt+A Alt+A Add Digital Channel Alt+D Alt+D Clone Channel Kanaal Klonen Alt+C Alt+C Delete Channel Kanaal Verwijderen Alt+- Alt+- ChannelListWrapper FM FM DMR DMR [Default] Max Max High Hoog Mid Middel Low Laag Min Min Off Uit On Aan Always Altijd Free Vrij Color Kleur Tone Toon [None] [Geen] Open Open Wide Breed Narrow Smal Type Type Name Naam Rx Frequency RX Frequentie Tx Frequency TX Frequentie Power Vermogen Timeout Timeout Rx Only Admit Scanlist Scanlijst Zones Zones CC TS RX Group List TX Contact TX Contact DMR ID DMR ID GPS/APRS GPS/APRS Roaming Roaming Squelch Squelch Rx Tone RX Toon Tx Tone TX Toon Bandwidth Bandbreedte ChannelRefListWrapper Channel Kanaal ChannelSelectionDialog Select a channel: ConfigMergeDialog Merging codeplugs ... <html><head/><body><p><span style=" font-weight:600;">Conflict resolution strategies:</span></p><p>If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.</p></body></html> Items are all atomic objects like radio IDs, channels, contacts and roaming channels. Items Ignore Override Duplicate Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones. Sets Merge Ignores any duplicate item. Replaces any duplicate item with the imported one. Imports any duplicate item with a modified name. Ignores any duplicate set. Replaces any duplicate set with the imported one. Imports any duplicate set with a modified name. Merges duplicate sets. ConfigObjectListView Cannot move items. Cannot move items: You have to select at least one item first. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTableView Cannot move items. Cannot move items: You have to select at least one item first. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTypeSelectionDialog An instance of %1. <p>%1<p><p style="margin-left:10px;">%2</p> Create extension object Select the class of object to create ContactListView Cannot delete contact Cannot delete contact: You have to select a contact first. Delete contact? Delete contact %1? Delete contacts? Delete %1 contacts? Adds a contact to the list. Add Digital Contact Alt++ Alt++ Add DTMF Contact Delete contact button Delete Contact Alt+- Alt+- ContactListWrapper DTMF DTMF On Aan Off Uit Private Call Group Call All Call Type Type Name Naam Number Nummer RX Tone RX Toon DMRContactDialog Create DMR Contact Edit DMR Contact Private Call Group Call All Call Basic Type Type Name Naam Number Nummer Ring Extensions Extensies DMRIDDialog Basic Name Naam DMR ID DMR ID Extensions Extensies DTMFContactDialog Create DTMF Contact Edit DMR Contact Basic Name Naam Number Nummer Ring Extensions Extensies DeviceSelectionDialog Select a device <html><head/><body><p>There is either more than one device detected or the one found is not considered save to access. Either way, select the device to use.</p></body></html> DigitalChannelDialog [None] [Geen] [Default] Edit Digital Channel <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and color-code are filled in.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Name Naam Rx Frequency RX Frequentie Tx Frequency TX Frequentie Power Vermogen Tx Timeout TX Timeout Rx Only Scan List Scan Lijst Max Max High Hoog Mid Mid Low Laag Min Min Default Specifies the transmit timeout in seconds. A value of 0 means that the transmit timeout is disabled. Off Uit VOX Level Tx Admit Always Altijd Channel Free Kanaal Vrij Color Code Time Slot TS 1 TS 1 TS 2 TS 2 Rx Group List Tx Contact Positioning System Roaming Zone Roaming Zone DMR ID DMR ID Extended Device specific settings ErrorMessageView Error: Unknown. Error: %1 Fout: %1 Traceback: Traceback: ExtensionView Cannot create extension. Cannot create extension, consider reporting a bug. Cannot create list element. Cannot create list element, consider reporting a bug. Create Remove GPSSystemDialog Create DMR APRS Sysmtem Edit DMR APRS Sysmtem [Selected] Edit GPS System Basic Name Naam Destination Update period Revert Channel Extensions Extensies GeneralSettingsView DMR Radio ID DMR Radio ID DMR ID DMR ID Radio Name Radionaam Enter your name or callsign. Name/Callsign Naam/Roepletters Boot Settings Intro Line 1 First greeting line (if supported by the radio). Intro line 1 Intro Line 2 Second greeting line (if supported by the radio). Intro line 2 Audio Settings MIC Amp. Speech Synthesis Channel Default Values Power Vermogen Max Max High Hoog Mid Middel Low Laag Min Min Squelch Squelch Open Open Transmit Timeout Off Uit VOX Level Extensions Extensies GroupListWrapper Contact Contact GroupListsView Cannot delete RX group list Cannot delete RX group lists: You have to select a group list first. Delete RX group list? Delete RX group list %1? Delete %1 RX group lists? Add RX Group Alt++ Alt++ Delete RX Group Alt+- Alt+- GroupListsWrapper RX Group Lists MainWindow File Bestand Device Help Databases Toolbar New Nieuw Creates a new Codeplug. Ctrl+N Ctrl+N Open ... <html><head/><body><p>Imports a codeplug from &quot;conf&quot; files.</p></body></html> Ctrl+O Ctrl+O Save ... Opslaan … <html><head/><body><p>Saves the codeplug in a &quot;conf&quot; file.</p></body></html> Ctrl+S Ctrl+S Quit Quits the application. Ctrl+Q Ctrl+Q Detect Detecteren Detect connected radios. Verify Verifiëren <html><head/><body><p>Verifies the current codeplug with connected radios.</p></body></html> Ctrl+R Ctrl+R Read Reads a codeplug from connected radios. Write Writes the codeplug to the connected radio. About qdmr Read the handbook. F1 F1 Settings Instellingen Shows settings dialog Write Callsign DB Writes call-sign DB to radio. Refresh Callsign DB Refreshes the downloaded callsign DB Refresh Talkgroup DB Refreshes the downloaded talkgroup DB Export to CHIRP ... Exports all FM channels to CHRIP CSV. Import ... Imports and merges a codeplug into the current one. Refresh Orbital Elements Refreshes the orbital elements. Edit Satellites ... Opens an editor to edit your satellite database. Write satellites Writes the orbital elements and transponder information onto the connected device. Radio IDs Radio IDs Contacts Contacten Group Lists Groepenlijst Channels Kanalen Zones Zones Scan Lists Scan Lijsten GPS/APRS GPS/APRS Roaming Channels Roaming Kanalen Roaming Zones Roaming Zones Extensions Extensies Unsaved changes to codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. MultiChannelSelectionDialog [Selected] Select a channel: MultiGroupCallSelectionDialog Show private calls Select a group call: MultiRoamingChannelSelectionDialog Select roaming channels PositioningSystemListView Cannot delete GPS system Cannot delete GPS system: You have to select a GPS system first. Delete positioning system? Delete positioning system %1? Delete %1 positioning systems? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support GPS or APRS. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add GPS System Alt+G Add APRS System Alt+A Alt+A Delete Position System Alt+- Alt+- PositioningSystemListWrapper DMR DMR APRS APRS [None] [Geen] [Selected] Type Type Name Naam Destination Period [s] Channel Kanaal Message Bericht PropertyDelegate False True [None] [Geen] PropertyWrapper new element Property Value Waarde Description Beschrijving true false [None] [Geen] Instance of %1 List of %1 instances QObject [None] [Geen] RXGroupListDialog Create Group List Edit Group List Cannot remove group call Cannot remove group call: You have to select at least one group call first. Basic Name Naam Add Contact Alt++ Alt++ Remove Contact Alt+- Alt+- Extensions Extensies RadioIDListView Cannot delete radio IDs Cannot delete radio IDs: You have to select a radio ID first. Delete radio ID? Delete radio ID %1? Delete scan lists? Delete %1 scan lists? Default Radio ID Add Radio ID Delete Radio ID RadioIdListWrapper Type Type Name Naam Number Nummer RadioSelectionDialog Cannot auto-detect radio Select a specific radio ReleaseNotes Cannot download release notes from https://github.com/hmatuschek/qdmr %1 Cannot read release notes from https://github.com/hmatuschek/qdmr Release is not a JSON object! Cannot read release notes from https://github.com/hmatuschek/qdmr Release does not contain a release note. qDMR was updated to version %1 RoamingChannelDialog Name Naam RX Frequency [MHz] TX Frequency [MHz] Time Slot Color Code Selected Edit roaming channel Create roaming channel TS 1 TS 1 TS 2 TS 2 RoamingChannelListView <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p><span style=" font-weight:600;">Hint:</span> You do not need to add roaming channel explicitly, just create roaming zones and add channels there. These channels are then added to this list.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Channel Delete Roaming Channel Cannot delete roaming channel Cannot delete roaming channel: You have to select a channel first. Delete roaming channel? Delete roaming channel %1? Delete %1 roaming channel? RoamingChannelListWrapper [Selected] Name Naam RX Frequency TX Frequency CC TS Zones Zones RoamingChannelRefListWrapper Roaming Channel RoamingListWrapper %1 (containing %2 channels) Roaming zone RoamingZoneDialog Create Roaming Zone Set Roaming Zone Cannot remove channels. Cannot remove channels. Select at least one channel first. Basic Name: Add Roaming Channel Add DMR Channel Alt++ Alt++ Remove Channel Alt+- Alt+- Extension RoamingZoneListView Generate roaming zone Create a roaming zone by collecting all channels with these group calls. Cannot delete roaming zone Cannot delete roaming zone: You have to select a zone first. Delete roaming zone? Delete roaming zone %1? Delete roaming zones? Delete %1 roaming zones? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Zone Alt++ Alt++ Generate Roaming Zone Delete Roaming Zone Alt+- Alt+- SatelliteDatabaseDialog Edit satellite database Add Delete SatelliteSelectionDialog Select a satellite ScanListDialog Edit Scan List Basic Name Naam Primary Channel (50%) Secondary Channel (25%) Transmit Channel Add Channel Alt++ Alt++ Remove Channel Alt+- Alt+- Extensions Extensies Create Scan List [None] [Geen] [Selected] [Last] ScanListsView Cannot delete scanlist Cannot delete scanlist: You have to select a scanlist first. Delete scan list? Delete scan list %1? Delete scan lists? Delete %1 scan lists? Add Scan List Alt++ Alt++ Delete Scan List Alt+- Alt+- ScanListsWrapper Scan-List SearchPopup Ctrl+F Ctrl+F %1/%2 SelectiveCallBox None CTCSS DCS Hz Inverted SettingsDialog Warning! Waarschuwing! Settings Instellingen Location Locatie System location Systeem locatie Locator Locator Repeater Info Sources enable Radio Programming Update codeplug Update codeplug <html><head/><body><p>Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.</p><p><br/></p><p>If selected, QDMR downloads the codeplug from the radio and updates only those settings specified. The remaining settings within the radio are not touched (recommended).</p></body></html> Auto-enable GPS When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically. When a roaming zone is defined and used by any channel, the automatic roaming gets enabled. Auto-enable roaming Data Sources World Wereld North America Programming Radio Interfaces disable auto-detect Ignore verification warnings <html><head/><body><p>As the communication interface to the radio is kept open after verification, time-outs may occur and the code-plug upload may fail when the verification dialog pops up. To prevent this, verification warnings can be ignored, eliminating the time-gap between verification and upload. Verification errors still prevent the upload.</p></body></html> Ignore frequency limits Negeer frequentie limieten Do not set this option unless you know what you are doing. Call-Sign DB Limit number of DB entries When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent). Number of DB entries Specifies the number of DB entries (if enabled above). Select using my DMR ID If enabled, the entries are selected using the users DMR ID. Select using prefixes If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries. Extensions Extensies Extended Features Show commercial features Show device extensions VerifyDialog Verify Codeplug ZoneDialog Edit Zone Basic <html><head/><body><p align="justify"><span style=" font-weight:600;">Note:</span> Zones are collections of channels that are usually valid for a specific region. I.e., a collection of channels for repeaters within a certain region. </p><p align="justify">QDMR manages zones by allowing for two independent channel lists for each VFO of the radio (if it has two). Many radios however, allow one to assign zones to each VFO individually. In these cases, QDMR will split the zone into two (A &amp; B) and program them individually into the radio.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Name Naam Channels A add remove Channels B Extension Create Zone Cannot remove channel Select at least one channel first. ZoneListView Cannot delete zone Cannot delete zone: You have to select a zone first. Delete zone? Delete zone %1? Delete zones? Delete %1 zones? Add Zone Alt++ Alt++ Delete Zone Alt+- Alt+- ZoneListWrapper Zone Zone aprssystemdialog Edit APRS System Basic Name Naam Channel Kanaal Source Destination Path Icon Update period [s] Updateperiode [s] Message Bericht Extensions Extensies qdmr-0.12.3/i18n/pl.ts000066400000000000000000004522761501654372000143250ustar00rootroot00000000000000 APRSSystem [None] Police station Digipeater Phone DX cluster HF gateway Plane small Mobile Satellite station Wheel Chair Snowmobile Red cross Boy scout Home X Red dot Circle 0 Circle 1 Circle 2 Circle 3 Circle 4 Circle 5 Circle 6 Circle 7 Circle 8 Circle 9 Fire Campground Motorcycle Rail engine Car File server HC Future Aid station BBS Canoe Eyeball Tractor Grid Square Hotel TCP/IP School Logon MacOS NTS station Balloon Police car TBD RV Shuttle SSTV Bus ATV Weather service Helo Yacht MS Windows Jogger Triangle PBBS Plane large Weather station Dish antenna Ambulance Bike ICP Fire station Horse Fire truck Glider Hospital IOTA Jeep Truck small Laptop Mic-E Node EOC Rover Grid Antenna Power boat Truck stop Truck large Van Water XAPRS Yagi Shelter APRSSystemDialog Create APRS system Utwórz system APRS Edit APRS system Edytuj system APRS [Selected] AboutDialog About qdmr O qdrm Supported Radios Wspierane radia AnalogChannelDialog [None] Zależy ściśle od kontekstu. Edit Analog Channel Edytuj kanał analogowy <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and CTCSS tones are filled in (if applicable).</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Podstawowe Name Nazwa Rx Frequency Częstotliwość RX Tx Frequency Częstotliwość TX Power Moc Max Maks High Wysoki Mid Średni Low Niski Min Min Default Domyślny Tx Timeout Limit czasu TX Off Wyłącz VOX Level Poziom VOX Rx Only Tylko RX Scan List Skanuj listę Tx Admit Always Zawsze Channel Free Wolny kanał Tone Ton Squelch Rx Tone Ton RX Tx Tone Ton TX Bandwidth Szerokość pasma Narrow (12.5 kHz) Wide (25 kHz) APRS APRS Open Otwórz Extensions Rozszerzenia Application Unsaved changes to codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. Open codeplug Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*) Cannot open file Cannot read codeplug from file '%1': %2 Cannot read codeplug. Save codeplug Codeplug Files (*.yaml *.yml) Please use new YAML format. Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works. Cannot save codeplug to file '%1': %2 Cannot save codeplug Cannot save codeplug to file '%1'. Export codeplug CHIRP CSV Files (*.csv) Cannot export codeplug Cannot export codeplug to file '%1': %2 Import codeplug CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml) Cannot import codeplug Cannot import codeplug from '%1': %2 Do not know, how to handle file '%1'. No matching devices found. Cannot connect to radio Cannot connect to radio: %1 Radio found Found device '%1'. No radio found No matching device was found. Verification success The codeplug was successfully verified with the radio '%1' Read ... Read error Read complete Upload ... Cannot write call-sign DB. The detected radio '%1' does not support a call-sign DB. The detected radio '%1' does support a call-sign DB. This feature, however, is not implemented yet. QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set. Write call-sign DB ... Cannot write satellite config. The detected radio '%1' does not support satellite tracking. The detected radio '%1' does support satellite tracking. This feature, however, is not implemented yet. Write satellite config ... Write error Write complete %1 (alias for %2 %3) ChannelListView Select a single channel first To clone a channel, please select a single channel to clone. Cannot delete channel Cannot delete channel: You have to select a channel first. Delete channel? Delete channel %1? Delete %1 channels? Add Analog Channel Alt+A Add Digital Channel Alt+D Clone Channel Alt+C Delete Channel Usuń kanał Alt+- ChannelListWrapper FM DMR [Default] Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy. Max Maks High Wysoki Mid Średni Low Niski Min Minimum Off Wyłącz On Włącz Always Zawsze Free Wolny Color Kolor Tone Ton [None] Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy. Open Otwórz Wide Szeroki Narrow Wąski Type Typ Name Nazwa Rx Frequency Częstotliwość RX Tx Frequency Częstotliwość TX Power Moc Timeout Koniec czasu połączenia Rx Only Tylko RX Admit Zezwól Scanlist Lista skanowania Zones Strefy CC TS RX Group List Lista grupy TX Contact Kontakt TX DMR ID Identyfikator DMR GPS/APRS GPS/APRS Roaming Squelch Rx Tone Ton RX Tx Tone Ton TX Bandwidth Szerokość pasma ChannelRefListWrapper Channel Kanał ChannelSelectionDialog Select a channel: Wybierz kanał: ConfigMergeDialog Merging codeplugs ... <html><head/><body><p><span style=" font-weight:600;">Conflict resolution strategies:</span></p><p>If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.</p></body></html> Items are all atomic objects like radio IDs, channels, contacts and roaming channels. Items Ignore Override Duplicate Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones. Sets Merge Ignores any duplicate item. Replaces any duplicate item with the imported one. Imports any duplicate item with a modified name. Ignores any duplicate set. Replaces any duplicate set with the imported one. Imports any duplicate set with a modified name. Merges duplicate sets. ConfigObjectListView Cannot move items. Nie można przenieść elementów. Cannot move items: You have to select at least one item first. Nie można przenieść elementów: Musisz wybrać conajmniej jeden z nich. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTableView Cannot move items. Nie można przenieść elementów Cannot move items: You have to select at least one item first. Nie można przenieść elementów: Musisz wybrać conajmniej jeden z nich. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTypeSelectionDialog An instance of %1. <p>%1<p><p style="margin-left:10px;">%2</p> Create extension object Select the class of object to create ContactListView Cannot delete contact Nie można usunąć kontaktu Cannot delete contact: You have to select a contact first. Delete contact? Delete contact %1? Delete contacts? Delete %1 contacts? Adds a contact to the list. Add Digital Contact Dodaj cyfrowy kontakt Alt++ Add DTMF Contact Delete contact button Delete Contact Alt+- ContactListWrapper DTMF On Włącz Off Wyłącz Private Call Group Call All Call Type Typ Name Nazwa Number Numer RX Tone DMRContactDialog Create DMR Contact Edit DMR Contact Edytuj kontakt DMR Private Call Group Call All Call Basic Podstawowe Type Typ Name Nazwa Number Numer Ring Extensions Rozszerzenia DMRIDDialog Basic Podstawowe Name Nazwa DMR ID Identyfikator DMR Extensions Rozszerzenia DTMFContactDialog Create DTMF Contact Utwórz kontakt DTMF Edit DMR Contact Edytuj kontakt DMR Basic Podstawowe Name Nazwa Number Numer Ring Extensions Rozszerzenia DeviceSelectionDialog Select a device <html><head/><body><p>There is either more than one device detected or the one found is not considered save to access. Either way, select the device to use.</p></body></html> DigitalChannelDialog [None] [Default] Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy. Edit Digital Channel <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and color-code are filled in.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Podstawowe Name Nazwa Rx Frequency Częstotliwość RX Tx Frequency Częstotliwość TX Power Moc Tx Timeout Limit czasu TX Rx Only Tylko RX Scan List Skanuj listę Max Maks High Wysoki Mid Średni Low Niski Min Minimum Default Domyślny Specifies the transmit timeout in seconds. A value of 0 means that the transmit timeout is disabled. Off Wyłącz VOX Level Poziom VOX Tx Admit Always Zawsze Channel Free Wolny kanał Color Code Time Slot TS 1 TS 2 Rx Group List Tx Contact Positioning System Roaming Zone DMR ID Identyfikator DMR Extended Device specific settings ErrorMessageView Error: Unknown. Error: %1 Traceback: ExtensionView Cannot create extension. Cannot create extension, consider reporting a bug. Cannot create list element. Cannot create list element, consider reporting a bug. Create Remove GPSSystemDialog Create DMR APRS Sysmtem Edit DMR APRS Sysmtem [Selected] Edit GPS System Basic Podstawowe Name Nazwa Destination Update period Revert Channel Extensions Rozszerzenia GeneralSettingsView DMR Radio ID DMR ID Identyfikator DMR Radio Name Enter your name or callsign. Name/Callsign Boot Settings Intro Line 1 First greeting line (if supported by the radio). Intro line 1 Intro Line 2 Second greeting line (if supported by the radio). Intro line 2 Audio Settings MIC Amp. Speech Synthesis Channel Default Values Power Moc Max Maks High Wysoki Mid Średni Low Niski Min Minimum Squelch Open Otwórz Transmit Timeout Off Wyłącz VOX Level Poziom VOX Extensions Rozszerzenia GroupListWrapper Contact GroupListsView Cannot delete RX group list Cannot delete RX group lists: You have to select a group list first. Delete RX group list? Delete RX group list %1? Delete %1 RX group lists? Add RX Group Alt++ Delete RX Group Alt+- GroupListsWrapper RX Group Lists MainWindow File Device Help Databases Toolbar New Creates a new Codeplug. Ctrl+N Open ... <html><head/><body><p>Imports a codeplug from &quot;conf&quot; files.</p></body></html> Ctrl+O Save ... <html><head/><body><p>Saves the codeplug in a &quot;conf&quot; file.</p></body></html> Ctrl+S Quit Quits the application. Ctrl+Q Detect Detect connected radios. Verify <html><head/><body><p>Verifies the current codeplug with connected radios.</p></body></html> Ctrl+R Read Reads a codeplug from connected radios. Write Writes the codeplug to the connected radio. About qdmr O qdrm Read the handbook. F1 Settings Ustawienia Shows settings dialog Write Callsign DB Writes call-sign DB to radio. Refresh Callsign DB Refreshes the downloaded callsign DB Refresh Talkgroup DB Refreshes the downloaded talkgroup DB Export to CHIRP ... Exports all FM channels to CHRIP CSV. Import ... Imports and merges a codeplug into the current one. Refresh Orbital Elements Refreshes the orbital elements. Edit Satellites ... Opens an editor to edit your satellite database. Write satellites Writes the orbital elements and transponder information onto the connected device. Radio IDs Identyfikatory radia Contacts Kontakty Group Lists Channels Kanały Zones Strefy Scan Lists Skanuj listy GPS/APRS GPS/APRS Roaming Channels Roaming Zones Extensions Rozszerzenia Unsaved changes to codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. MultiChannelSelectionDialog [Selected] Select a channel: Wybierz kanał: MultiGroupCallSelectionDialog Show private calls Select a group call: MultiRoamingChannelSelectionDialog Select roaming channels PositioningSystemListView Cannot delete GPS system Cannot delete GPS system: You have to select a GPS system first. Delete positioning system? Delete positioning system %1? Delete %1 positioning systems? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support GPS or APRS. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add GPS System Alt+G Add APRS System Alt+A Delete Position System Alt+- PositioningSystemListWrapper DMR APRS APRS [None] [Selected] Type Typ Name Nazwa Destination Period [s] Channel Kanał Message PropertyDelegate False True [None] PropertyWrapper new element Property Value Description true false [None] Instance of %1 List of %1 instances QObject [None] RXGroupListDialog Create Group List Edit Group List Cannot remove group call Cannot remove group call: You have to select at least one group call first. Basic Podstawowe Name Nazwa Add Contact Alt++ Remove Contact Alt+- Extensions Rozszerzenia RadioIDListView Cannot delete radio IDs Cannot delete radio IDs: You have to select a radio ID first. Delete radio ID? Delete radio ID %1? Delete scan lists? Delete %1 scan lists? Default Radio ID Add Radio ID Delete Radio ID RadioIdListWrapper Type Typ Name Nazwa Number Numer RadioSelectionDialog Cannot auto-detect radio Select a specific radio ReleaseNotes Cannot download release notes from https://github.com/hmatuschek/qdmr %1 Cannot read release notes from https://github.com/hmatuschek/qdmr Release is not a JSON object! Cannot read release notes from https://github.com/hmatuschek/qdmr Release does not contain a release note. qDMR was updated to version %1 RoamingChannelDialog Name Nazwa RX Frequency [MHz] TX Frequency [MHz] Time Slot Color Code Selected Edit roaming channel Create roaming channel TS 1 TS 2 RoamingChannelListView <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p><span style=" font-weight:600;">Hint:</span> You do not need to add roaming channel explicitly, just create roaming zones and add channels there. These channels are then added to this list.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Channel Delete Roaming Channel Cannot delete roaming channel Cannot delete roaming channel: You have to select a channel first. Delete roaming channel? Delete roaming channel %1? Delete %1 roaming channel? RoamingChannelListWrapper [Selected] Name Nazwa RX Frequency TX Frequency TS Zones Strefy CC RoamingChannelRefListWrapper Roaming Channel RoamingListWrapper %1 (containing %2 channels) Roaming zone RoamingZoneDialog Create Roaming Zone Set Roaming Zone Cannot remove channels. Cannot remove channels. Select at least one channel first. Basic Podstawowe Name: Add Roaming Channel Add DMR Channel Alt++ Remove Channel Alt+- Extension RoamingZoneListView Generate roaming zone Create a roaming zone by collecting all channels with these group calls. Cannot delete roaming zone Cannot delete roaming zone: You have to select a zone first. Delete roaming zone? Delete roaming zone %1? Delete roaming zones? Delete %1 roaming zones? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Zone Alt++ Generate Roaming Zone Delete Roaming Zone Alt+- SatelliteDatabaseDialog Edit satellite database Add Delete SatelliteSelectionDialog Select a satellite ScanListDialog Edit Scan List Basic Podstawowe Name Nazwa Primary Channel (50%) Secondary Channel (25%) Transmit Channel Add Channel Alt++ Remove Channel Alt+- Extensions Rozszerzenia Create Scan List [None] [Selected] [Last] ScanListsView Cannot delete scanlist Cannot delete scanlist: You have to select a scanlist first. Delete scan list? Delete scan list %1? Delete scan lists? Delete %1 scan lists? Add Scan List Alt++ Delete Scan List Alt+- ScanListsWrapper Scan-List SearchPopup Ctrl+F %1/%2 SelectiveCallBox None CTCSS DCS Hz Inverted SettingsDialog Warning! Settings Ustawienia Location System location Locator Repeater Info Sources enable Radio Programming Update codeplug <html><head/><body><p>Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.</p><p><br/></p><p>If selected, QDMR downloads the codeplug from the radio and updates only those settings specified. The remaining settings within the radio are not touched (recommended).</p></body></html> Auto-enable GPS When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically. When a roaming zone is defined and used by any channel, the automatic roaming gets enabled. Auto-enable roaming Data Sources World North America Programming Radio Interfaces disable auto-detect Ignore verification warnings <html><head/><body><p>As the communication interface to the radio is kept open after verification, time-outs may occur and the code-plug upload may fail when the verification dialog pops up. To prevent this, verification warnings can be ignored, eliminating the time-gap between verification and upload. Verification errors still prevent the upload.</p></body></html> Ignore frequency limits Do not set this option unless you know what you are doing. Call-Sign DB Limit number of DB entries When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent). Number of DB entries Specifies the number of DB entries (if enabled above). Select using my DMR ID If enabled, the entries are selected using the users DMR ID. Select using prefixes If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries. Extensions Rozszerzenia Extended Features Show commercial features Show device extensions VerifyDialog Verify Codeplug ZoneDialog Edit Zone Basic Podstawowe <html><head/><body><p align="justify"><span style=" font-weight:600;">Note:</span> Zones are collections of channels that are usually valid for a specific region. I.e., a collection of channels for repeaters within a certain region. </p><p align="justify">QDMR manages zones by allowing for two independent channel lists for each VFO of the radio (if it has two). Many radios however, allow one to assign zones to each VFO individually. In these cases, QDMR will split the zone into two (A &amp; B) and program them individually into the radio.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Name Nazwa Channels A add remove Channels B Extension Create Zone Cannot remove channel Select at least one channel first. ZoneListView Cannot delete zone Cannot delete zone: You have to select a zone first. Delete zone? Delete zone %1? Delete zones? Delete %1 zones? Add Zone Alt++ Delete Zone Alt+- ZoneListWrapper Zone aprssystemdialog Edit APRS System Basic Podstawowe Name Nazwa Channel Kanał Source Destination Path Icon Update period [s] Message Extensions Rozszerzenia qdmr-0.12.3/i18n/pt_BR.ts000066400000000000000000004514431501654372000147130ustar00rootroot00000000000000 APRSSystem [None] Police station Digipeater Phone DX cluster HF gateway Plane small Mobile Satellite station Wheel Chair Snowmobile Red cross Boy scout Home X Red dot Circle 0 Circle 1 Circle 2 Circle 3 Circle 4 Circle 5 Circle 6 Circle 7 Circle 8 Circle 9 Fire Campground Motorcycle Rail engine Car File server HC Future Aid station BBS Canoe Eyeball Tractor Grid Square Hotel TCP/IP School Logon MacOS NTS station Balloon Police car TBD RV Shuttle SSTV Bus ATV Weather service Helo Yacht MS Windows Jogger Triangle PBBS Plane large Weather station Dish antenna Ambulance Bike ICP Fire station Horse Fire truck Glider Hospital IOTA Jeep Truck small Laptop Mic-E Node EOC Rover Grid Antenna Power boat Truck stop Truck large Van Water XAPRS Yagi Shelter APRSSystemDialog Create APRS system Edit APRS system [Selected] AboutDialog About qdmr Supported Radios AnalogChannelDialog [None] Edit Analog Channel <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and CTCSS tones are filled in (if applicable).</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Name Rx Frequency Tx Frequency Power Max High Mid Low Min Default Tx Timeout Off VOX Level Rx Only Scan List Tx Admit Always Channel Free Tone Squelch Rx Tone Tx Tone Bandwidth Narrow (12.5 kHz) Wide (25 kHz) APRS Open Extensions Application Unsaved changes to codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. Open codeplug Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*) Cannot open file Cannot read codeplug from file '%1': %2 Cannot read codeplug. Save codeplug Codeplug Files (*.yaml *.yml) Please use new YAML format. Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works. Cannot save codeplug to file '%1': %2 Cannot save codeplug Cannot save codeplug to file '%1'. Export codeplug CHIRP CSV Files (*.csv) Cannot export codeplug Cannot export codeplug to file '%1': %2 Import codeplug CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml) Cannot import codeplug Cannot import codeplug from '%1': %2 Do not know, how to handle file '%1'. No matching devices found. Cannot connect to radio Cannot connect to radio: %1 Radio found Found device '%1'. No radio found No matching device was found. Verification success The codeplug was successfully verified with the radio '%1' Read ... Read error Read complete Upload ... Cannot write call-sign DB. The detected radio '%1' does not support a call-sign DB. The detected radio '%1' does support a call-sign DB. This feature, however, is not implemented yet. QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set. Write call-sign DB ... Cannot write satellite config. The detected radio '%1' does not support satellite tracking. The detected radio '%1' does support satellite tracking. This feature, however, is not implemented yet. Write satellite config ... Write error Write complete %1 (alias for %2 %3) ChannelListView Select a single channel first To clone a channel, please select a single channel to clone. Cannot delete channel Cannot delete channel: You have to select a channel first. Delete channel? Delete channel %1? Delete %1 channels? Add Analog Channel Alt+A Add Digital Channel Alt+D Clone Channel Alt+C Delete Channel Alt+- ChannelListWrapper FM DMR [Default] Max High Mid Low Min Off On Always Free Color Tone [None] Open Wide Narrow Type Name Rx Frequency Tx Frequency Power Timeout Rx Only Admit Scanlist Zones CC TS RX Group List TX Contact DMR ID GPS/APRS Roaming Squelch Rx Tone Tx Tone Bandwidth ChannelRefListWrapper Channel ChannelSelectionDialog Select a channel: ConfigMergeDialog Merging codeplugs ... <html><head/><body><p><span style=" font-weight:600;">Conflict resolution strategies:</span></p><p>If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.</p></body></html> Items are all atomic objects like radio IDs, channels, contacts and roaming channels. Items Ignore Override Duplicate Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones. Sets Merge Ignores any duplicate item. Replaces any duplicate item with the imported one. Imports any duplicate item with a modified name. Ignores any duplicate set. Replaces any duplicate set with the imported one. Imports any duplicate set with a modified name. Merges duplicate sets. ConfigObjectListView Cannot move items. Cannot move items: You have to select at least one item first. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTableView Cannot move items. Cannot move items: You have to select at least one item first. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTypeSelectionDialog An instance of %1. <p>%1<p><p style="margin-left:10px;">%2</p> Create extension object Select the class of object to create ContactListView Cannot delete contact Cannot delete contact: You have to select a contact first. Delete contact? Delete contact %1? Delete contacts? Delete %1 contacts? Adds a contact to the list. Add Digital Contact Alt++ Add DTMF Contact Delete contact button Delete Contact Alt+- ContactListWrapper DTMF On Off Private Call Group Call All Call Type Name Number RX Tone DMRContactDialog Create DMR Contact Edit DMR Contact Private Call Group Call All Call Basic Type Name Number Ring Extensions DMRIDDialog Basic Name DMR ID Extensions DTMFContactDialog Create DTMF Contact Edit DMR Contact Basic Name Number Ring Extensions DeviceSelectionDialog Select a device <html><head/><body><p>There is either more than one device detected or the one found is not considered save to access. Either way, select the device to use.</p></body></html> DigitalChannelDialog [None] [Default] Edit Digital Channel <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and color-code are filled in.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Name Rx Frequency Tx Frequency Power Tx Timeout Rx Only Scan List Max High Mid Low Min Default Specifies the transmit timeout in seconds. A value of 0 means that the transmit timeout is disabled. Off VOX Level Tx Admit Always Channel Free Color Code Time Slot TS 1 TS 2 Rx Group List Tx Contact Positioning System Roaming Zone DMR ID Extended Device specific settings ErrorMessageView Error: Unknown. Error: %1 Traceback: ExtensionView Cannot create extension. Cannot create extension, consider reporting a bug. Cannot create list element. Cannot create list element, consider reporting a bug. Create Remove GPSSystemDialog Create DMR APRS Sysmtem Edit DMR APRS Sysmtem [Selected] Edit GPS System Basic Name Destination Update period Revert Channel Extensions GeneralSettingsView DMR Radio ID DMR ID Radio Name Enter your name or callsign. Name/Callsign Boot Settings Intro Line 1 First greeting line (if supported by the radio). Intro line 1 Intro Line 2 Second greeting line (if supported by the radio). Intro line 2 Audio Settings MIC Amp. Speech Synthesis Channel Default Values Power Max High Mid Low Min Squelch Open Transmit Timeout Off VOX Level Extensions GroupListWrapper Contact GroupListsView Cannot delete RX group list Cannot delete RX group lists: You have to select a group list first. Delete RX group list? Delete RX group list %1? Delete %1 RX group lists? Add RX Group Alt++ Delete RX Group Alt+- GroupListsWrapper RX Group Lists MainWindow File Device Help Databases Toolbar New Creates a new Codeplug. Ctrl+N Open ... <html><head/><body><p>Imports a codeplug from &quot;conf&quot; files.</p></body></html> Ctrl+O Save ... <html><head/><body><p>Saves the codeplug in a &quot;conf&quot; file.</p></body></html> Ctrl+S Quit Quits the application. Ctrl+Q Detect Detect connected radios. Verify <html><head/><body><p>Verifies the current codeplug with connected radios.</p></body></html> Ctrl+R Read Reads a codeplug from connected radios. Write Writes the codeplug to the connected radio. About qdmr Read the handbook. F1 Settings Shows settings dialog Write Callsign DB Writes call-sign DB to radio. Refresh Callsign DB Refreshes the downloaded callsign DB Refresh Talkgroup DB Refreshes the downloaded talkgroup DB Export to CHIRP ... Exports all FM channels to CHRIP CSV. Import ... Imports and merges a codeplug into the current one. Refresh Orbital Elements Refreshes the orbital elements. Edit Satellites ... Opens an editor to edit your satellite database. Write satellites Writes the orbital elements and transponder information onto the connected device. Radio IDs Contacts Group Lists Channels Zones Scan Lists GPS/APRS Roaming Channels Roaming Zones Extensions Unsaved changes to codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. MultiChannelSelectionDialog [Selected] Select a channel: MultiGroupCallSelectionDialog Show private calls Select a group call: MultiRoamingChannelSelectionDialog Select roaming channels PositioningSystemListView Cannot delete GPS system Cannot delete GPS system: You have to select a GPS system first. Delete positioning system? Delete positioning system %1? Delete %1 positioning systems? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support GPS or APRS. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add GPS System Alt+G Add APRS System Alt+A Delete Position System Alt+- PositioningSystemListWrapper DMR APRS [None] [Selected] Type Name Destination Period [s] Channel Message PropertyDelegate False True [None] PropertyWrapper new element Property Value Description true false [None] Instance of %1 List of %1 instances QObject [None] RXGroupListDialog Create Group List Edit Group List Cannot remove group call Cannot remove group call: You have to select at least one group call first. Basic Name Add Contact Alt++ Remove Contact Alt+- Extensions RadioIDListView Cannot delete radio IDs Cannot delete radio IDs: You have to select a radio ID first. Delete radio ID? Delete radio ID %1? Delete scan lists? Delete %1 scan lists? Default Radio ID Add Radio ID Delete Radio ID RadioIdListWrapper Type Name Number RadioSelectionDialog Cannot auto-detect radio Select a specific radio ReleaseNotes Cannot download release notes from https://github.com/hmatuschek/qdmr %1 Cannot read release notes from https://github.com/hmatuschek/qdmr Release is not a JSON object! Cannot read release notes from https://github.com/hmatuschek/qdmr Release does not contain a release note. qDMR was updated to version %1 RoamingChannelDialog Name RX Frequency [MHz] TX Frequency [MHz] Time Slot Color Code Selected Edit roaming channel Create roaming channel TS 1 TS 2 RoamingChannelListView <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p><span style=" font-weight:600;">Hint:</span> You do not need to add roaming channel explicitly, just create roaming zones and add channels there. These channels are then added to this list.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Channel Delete Roaming Channel Cannot delete roaming channel Cannot delete roaming channel: You have to select a channel first. Delete roaming channel? Delete roaming channel %1? Delete %1 roaming channel? RoamingChannelListWrapper [Selected] Name RX Frequency TX Frequency TS Zones CC RoamingChannelRefListWrapper Roaming Channel RoamingListWrapper %1 (containing %2 channels) Roaming zone RoamingZoneDialog Create Roaming Zone Set Roaming Zone Cannot remove channels. Cannot remove channels. Select at least one channel first. Basic Name: Add Roaming Channel Add DMR Channel Alt++ Remove Channel Alt+- Extension RoamingZoneListView Generate roaming zone Create a roaming zone by collecting all channels with these group calls. Cannot delete roaming zone Cannot delete roaming zone: You have to select a zone first. Delete roaming zone? Delete roaming zone %1? Delete roaming zones? Delete %1 roaming zones? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Add Roaming Zone Alt++ Generate Roaming Zone Delete Roaming Zone Alt+- SatelliteDatabaseDialog Edit satellite database Add Delete SatelliteSelectionDialog Select a satellite ScanListDialog Edit Scan List Basic Name Primary Channel (50%) Secondary Channel (25%) Transmit Channel Add Channel Alt++ Remove Channel Alt+- Extensions Create Scan List [None] [Selected] [Last] ScanListsView Cannot delete scanlist Cannot delete scanlist: You have to select a scanlist first. Delete scan list? Delete scan list %1? Delete scan lists? Delete %1 scan lists? Add Scan List Alt++ Delete Scan List Alt+- ScanListsWrapper Scan-List SearchPopup Ctrl+F %1/%2 SelectiveCallBox None CTCSS DCS Hz Inverted SettingsDialog Warning! Settings Location System location Locator Repeater Info Sources enable Radio Programming Update codeplug <html><head/><body><p>Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.</p><p><br/></p><p>If selected, QDMR downloads the codeplug from the radio and updates only those settings specified. The remaining settings within the radio are not touched (recommended).</p></body></html> Auto-enable GPS When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically. When a roaming zone is defined and used by any channel, the automatic roaming gets enabled. Auto-enable roaming Data Sources World North America Programming Radio Interfaces disable auto-detect Ignore verification warnings <html><head/><body><p>As the communication interface to the radio is kept open after verification, time-outs may occur and the code-plug upload may fail when the verification dialog pops up. To prevent this, verification warnings can be ignored, eliminating the time-gap between verification and upload. Verification errors still prevent the upload.</p></body></html> Ignore frequency limits Do not set this option unless you know what you are doing. Call-Sign DB Limit number of DB entries When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent). Number of DB entries Specifies the number of DB entries (if enabled above). Select using my DMR ID If enabled, the entries are selected using the users DMR ID. Select using prefixes If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries. Extensions Extended Features Show commercial features Show device extensions VerifyDialog Verify Codeplug ZoneDialog Edit Zone Basic <html><head/><body><p align="justify"><span style=" font-weight:600;">Note:</span> Zones are collections of channels that are usually valid for a specific region. I.e., a collection of channels for repeaters within a certain region. </p><p align="justify">QDMR manages zones by allowing for two independent channel lists for each VFO of the radio (if it has two). Many radios however, allow one to assign zones to each VFO individually. In these cases, QDMR will split the zone into two (A &amp; B) and program them individually into the radio.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Name Channels A add remove Channels B Extension Create Zone Cannot remove channel Select at least one channel first. ZoneListView Cannot delete zone Cannot delete zone: You have to select a zone first. Delete zone? Delete zone %1? Delete zones? Delete %1 zones? Add Zone Alt++ Delete Zone Alt+- ZoneListWrapper Zone aprssystemdialog Edit APRS System Basic Name Channel Source Destination Path Icon Update period [s] Message Extensions qdmr-0.12.3/i18n/sv.ts000066400000000000000000004665321501654372000143420ustar00rootroot00000000000000 APRSSystem [None] Police station Polisstation Digipeater Digipeater Phone Telefon DX cluster DX-kluster HF gateway HF gateway Plane small Litet flygplan Mobile Satellite station Mobil satellitstation Wheel Chair Rullstol Snowmobile Snöskoter Red cross Röda Korset Boy scout Pojk Scout Home Hem X X Red dot Röd prick Circle 0 Cirkel 0 Circle 1 Cirkel 1 Circle 2 Cirkel 2 Circle 3 Cirkel 3 Circle 4 Cirkel 4 Circle 5 Cirkel 5 Circle 6 Cirkel 6 Circle 7 Cirkel 7 Circle 8 Cirkel 8 Circle 9 Cirkel 9 Fire Brand Campground Camping Motorcycle Motorcykel Rail engine Spårmotor Car Bil File server Fil server HC Future HC Future Aid station Biståndsstation BBS BBS Canoe Kanot Eyeball Ögonglob Tractor Traktor Grid Square Grid Square Hotel Hotell TCP/IP TCP/IP School Skola Logon Logga in MacOS MacOS NTS station NTS station Balloon Ballong Police car Polis bil TBD TBD RV RV Shuttle Shuttle SSTV SSTV Bus Buss ATV ATV Weather service Vädertjänst Helo Yacht Yacht MS Windows MS Windows Jogger Joggare Triangle Triangel PBBS Plane large Weather station Väderstation Dish antenna Diskantenn Ambulance Ambulans Bike Cykel ICP Fire station Brandstation Horse Häst Fire truck Brandbil Glider Segelflygplan Hospital Sjukhus IOTA IOTA Jeep Jeep Truck small Lilla lastbil Laptop Bärbar dator Mic-E Node Nod EOC Rover Rover Grid Rutnät Antenna Antenn Power boat Motorbåt Truck stop Lastbilsstopp Truck large Stor lastbil Van Skåpbil Water Vatten XAPRS Yagi Yagi Shelter Skydd APRSSystemDialog Create APRS system Skapa APRS-system Edit APRS system Redigera APRS-system [Selected] [Vald] AboutDialog About qdmr Om qdmr Supported Radios Radio som stöds AnalogChannelDialog [None] [Inget] Edit Analog Channel Redigera analog kanal <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and CTCSS tones are filled in (if applicable).</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Obs!</span> qdmr tillhandahåller viss autokomplettering för kanaler. Det vill säga, börja skriva anropssignalen för en repeater. Efter att tre tecken har angetts skickas en begäran till repeaterbook.com för att hämta matchande repeatrar. Dessa förfrågningar kan ta lite tid. Resultaten lagras lokalt i en cache.</p><p>En rullgardinslista visas där du kan välja en repeater. När en repeater har valts fylls RX/TX-frekvenserna och CTCSS-tonerna i (om tillämpligt).</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Basic Grundläggande Inställningar Name Namn Rx Frequency RX-frekvens Tx Frequency TX-frekvens Power Effekt Max max. High Hög Mid Mitten Low Lågt Min Minimalt Default Standard Tx Timeout TX Tidsgräns Off Avstängd VOX Level VOX-nivå Rx Only Bara RX Scan List Scan List Tx Admit TX Admit Always Alltid Channel Free Kanal Tillgänglig Tone Ton Squelch Squelch Rx Tone RX Ton Tx Tone TX Ton Bandwidth Bandbredd Narrow (12.5 kHz) Smal (12,5 kHz) Wide (25 kHz) Bred (25 kHz) APRS APRS Open Öppet Extensions Tillägg Application Unsaved changes to codeplug. Ej sparade ändringar i codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. Det finns osparade ändringar av den aktuella kodpluggen. Dessa ändringar går förlorade om du fortsätter. Open codeplug Öppna kodpluggen Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*) Codeplug-filer (*.yaml);;Codeplug-filer, gammalt format (*.conf *.csv *.txt);;Alla filer (*) Cannot open file Kan inte öppna filen Cannot read codeplug from file '%1': %2 Kan inte läsa codeplug från filen '%1': %2 Cannot read codeplug. Kan inte läsa kodpluggen. Save codeplug Spara kodpluggen Codeplug Files (*.yaml *.yml) Codeplug-filer (*.yaml *.yml) Please use new YAML format. Använd det nya YAML-formatet. Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works. Spara i det gamla tabellbaserade conf-formatet inaktiverades med 0.9.0. Att läsa dessa filer fungerar fortfarande. Cannot save codeplug to file '%1': %2 Det går inte att spara codeplug till filen '%1': %2 Cannot save codeplug Det går inte att spara codepluggen Cannot save codeplug to file '%1'. Det går inte att spara codeplug till filen '%1'. Export codeplug CHIRP CSV Files (*.csv) Cannot export codeplug Cannot export codeplug to file '%1': %2 Import codeplug CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml) Cannot import codeplug Cannot import codeplug from '%1': %2 Do not know, how to handle file '%1'. No matching devices found. Inga matchande enheter hittades. Cannot connect to radio Kan inte ansluta till radio Cannot connect to radio: %1 Kan inte ansluta till radio: %1 Radio found Radio hittad Found device '%1'. Hittade enheten '%1'. No radio found Ingen radio hittades No matching device was found. Ingen matchande enhet hittades. Verification success Verifieringen lyckades The codeplug was successfully verified with the radio '%1' Kodpluggen har verifierats med radion '%1'. Read ... Läs… Read error Läsfel Read complete Läs komplett Upload ... Skriv… Cannot write call-sign DB. Kan inte skriva anropssignal DB. The detected radio '%1' does not support a call-sign DB. Den upptäckta radion '%1' stöder inte en anropssignal-DB. The detected radio '%1' does support a call-sign DB. This feature, however, is not implemented yet. Den upptäckta radion '%1' stöder en anropssignal-DB. Denna funktion är dock inte implementerad ännu. QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set. QDMR väljer de anropssignaler som ska skrivas baserat på radions standard DMR-ID. Inget standard-ID inställt. Write call-sign DB ... Skriv anropssignal DB … Cannot write satellite config. The detected radio '%1' does not support satellite tracking. The detected radio '%1' does support satellite tracking. This feature, however, is not implemented yet. Write satellite config ... Write error Skrivfel Write complete Skriv komplett %1 (alias for %2 %3) %1 (alias för %2 %3) ChannelListView Select a single channel first Välj en enskild kanal först To clone a channel, please select a single channel to clone. För att klona en kanal, välj en enskild kanal att klona. Cannot delete channel Kan inte ta bort kanal Cannot delete channel: You have to select a channel first. Kan inte ta bort kanal: Du måste först välja en kanal. Delete channel? Ta bort kanal? Delete channel %1? Ta bort kanalen '%1'? Delete %1 channels? Ta bort %1 kanaler? Add Analog Channel Lägg till analog kanal Alt+A Alt+A Add Digital Channel Lägg till digital kanal Alt+D Alt+D Clone Channel Klona kanal Alt+C Alt+C Delete Channel Ta bort kanal Alt+- Alt+- ChannelListWrapper FM FM DMR DMR [Default] [Standard] Max max. High Hög Mid Mitten Low Lågt Min Minimalt Off Av On Aktiverat Always Alltid Free Fri Color Färg Tone Ton [None] [Ingen] Open Öppet Wide bred Narrow smal Type typ Name Namn Rx Frequency RX-frekvens Tx Frequency TX-frekvens Power Effekt Timeout timeouttid Rx Only Bara RX Admit Tillåta Scanlist skanningslistan Zones Zoner CC CC TS TS RX Group List Grupplistan TX Contact TX kontakt DMR ID DMR ID GPS/APRS GPS/APRS Roaming Roaming Squelch Squelch Rx Tone RX Ton Tx Tone TX Ton Bandwidth Bandbredd ChannelRefListWrapper Channel Kanal ChannelSelectionDialog Select a channel: Välj kanal: ConfigMergeDialog Merging codeplugs ... <html><head/><body><p><span style=" font-weight:600;">Conflict resolution strategies:</span></p><p>If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.</p></body></html> Items are all atomic objects like radio IDs, channels, contacts and roaming channels. Items Ignore Override Duplicate Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones. Sets Merge Ignores any duplicate item. Replaces any duplicate item with the imported one. Imports any duplicate item with a modified name. Ignores any duplicate set. Replaces any duplicate set with the imported one. Imports any duplicate set with a modified name. Merges duplicate sets. ConfigObjectListView Cannot move items. Kan inte flytta objekt. Cannot move items: You have to select at least one item first. Kan inte flytta objekt: Du måste välja minst ett objekt först. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTableView Cannot move items. Kan inte flytta objekt Cannot move items: You have to select at least one item first. Kan inte flytta objekt: Du måste välja minst ett objekt först. Move selected item(s) to the top. Move selected item(s) ten positions up. Move selected item(s) one position up. Move selected item(s) one position down. Move selected item(s) ten positions down. Move selected item(s) to the bottom. ConfigObjectTypeSelectionDialog An instance of %1. En förekomst av '%1'. <p>%1<p><p style="margin-left:10px;">%2</p> <p>%1<p><p style="margin-left:10px;">%2</p> Create extension object Skapa extension object Select the class of object to create Välj objektklassen som ska skapas ContactListView Cannot delete contact Kan inte ta bort kontakt Cannot delete contact: You have to select a contact first. Kan inte ta bort kontakt: Du måste först välja en kontakt. Delete contact? Ta bort kontakt? Delete contact %1? Ta bort kontakten '%1'? Delete contacts? Ta bort kontakter? Delete %1 contacts? Ta bort %1 kontakter? Adds a contact to the list. Lägger till en kontakt i listan. Add Digital Contact Lägg till digital kontakt Alt++ Alt++ Add DTMF Contact Lägg till DTMF-kontakt Delete contact button Ta bort kontakt-knappen Delete Contact Ta bort kontakt Alt+- Alt+- ContactListWrapper DTMF DTMF On Påslagen Off Avstängd Private Call Privat samtal Group Call Gruppsamtal All Call All Call Type Typ Name Namn Number Nummer RX Tone RX-ton DMRContactDialog Create DMR Contact Skapa DMR-kontakt Edit DMR Contact Redigera DMR-kontakt Private Call Privat samtal Group Call Gruppsamtal All Call All Call Basic Grund inställningar Type Typ Name Namn Number Nummer Ring Ringa Extensions Extensions DMRIDDialog Basic Grund inställningar Name Namn DMR ID DMR ID Extensions Extensions DTMFContactDialog Create DTMF Contact Skapa DTMF-kontakt Edit DMR Contact Redigera DMR-kontakt Basic Grund inställningar Name Namn Number Nummer Ring Ringa Extensions Extensions DeviceSelectionDialog Select a device Välj en enhet <html><head/><body><p>There is either more than one device detected or the one found is not considered save to access. Either way, select the device to use.</p></body></html> <html><head/><body><p>Antingen har mer än en enhet upptäckts eller så anses den hittade inte vara säkert för åtkomst. Hur som helst, välj vilken enhet du vill använda.</p></body></html> DigitalChannelDialog [None] [Ingen] [Default] [Standard] Edit Digital Channel Redigera digital kanal <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and color-code are filled in.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Obs!</span> qdmr tillhandahåller viss autokomplettering för kanaler. Det vill säga, börja skriva anropssignalen för en repeater. Efter att tre tecken har angetts skickas en begäran till repeaterbook.com för att hämta matchande repeatrar. Dessa förfrågningar kan ta lite tid. Resultaten lagras lokalt i en cache.</p><p>En rullgardinslista visas där du kan välja en repeater. När en repeater har valts fylls RX/TX-frekvenserna och färgkoden i.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">dölj</span></a></p></body></html> Basic Grund inställningar Name Namn Rx Frequency RX-frekvens Tx Frequency TX-frekvens Power Effekt Tx Timeout TX Timeout Rx Only Bara RX Scan List Scan List Max max. High Hög Mid Mitten Low Lågt Min Minimalt Default Standard Specifies the transmit timeout in seconds. A value of 0 means that the transmit timeout is disabled. Anger sändningstidsgränsen i sekunder. Ett värde på 0 betyder att sändningstidsgränsen är inaktiverad. Off Av VOX Level VOX-nivå Tx Admit TX Admit Always Alltid Channel Free Kanal Tillgänglig Color Code Färgkod Time Slot Time Slot TS 1 TS 1 TS 2 TS 2 Rx Group List Grupplista Tx Contact TX kontakt Positioning System Positioneringssystem Roaming Zone Roamingzon DMR ID DMR ID Extended Förlängd Device specific settings Enhetens specifika inställningar ErrorMessageView Error: Unknown. Fel: Okänt. Error: %1 Fel: %1 Traceback: Traceback: ExtensionView Cannot create extension. Det går inte att skapa tillägg. Cannot create extension, consider reporting a bug. Kan inte skapa tillägg, överväg att rapportera ett fel. Cannot create list element. Kan inte skapa listelement. Cannot create list element, consider reporting a bug. Kan inte skapa listelement, överväg att rapportera ett fel. Create Skapa Remove Ta bort GPSSystemDialog Create DMR APRS Sysmtem Skapa ett DMR APRS-system Edit DMR APRS Sysmtem Redigera DMR APRS-system [Selected] [Vald] Edit GPS System Redigera GPS-system Basic Grundläggande Name Namn Destination Destination Update period Revert Channel Återställ kanal Extensions Extensions GeneralSettingsView DMR Radio ID DMR-Radio-ID DMR ID DMR ID Radio Name Radions namn Enter your name or callsign. Ange ditt namn eller anropssignal. Name/Callsign Namn/Anropssignal Boot Settings Startinställningar Intro Line 1 Intro rad 1 First greeting line (if supported by the radio). Första hälsningsraden (om det stöds av radion). Intro line 1 Intro rad 1 Intro Line 2 Intro rad 2 Second greeting line (if supported by the radio). Andra hälsningsraden (om den stöds av radion). Intro line 2 Intro rad 2 Audio Settings Ljudinställningar MIC Amp. MIC-förstärkare Speech Synthesis Talsyntes Channel Default Values Kanalens standardvärden Power Effekt Max max. High Hög Mid Mitten Low Lågt Min Minimalt Squelch Squelch Open Öppet Transmit Timeout Timeout för sändning Off Av VOX Level VOX-nivå Extensions Extensions GroupListWrapper Contact Kontakt GroupListsView Cannot delete RX group list Kan inte radera grupplistan Cannot delete RX group lists: You have to select a group list first. Kan inte ta bort grupplistor: Du måste först välja en grupplista. Delete RX group list? Ta bort grupplistan? Delete RX group list %1? Ta bort grupplistan '%1'? Delete %1 RX group lists? Ta bort %1 grupplistor? Add RX Group Lägg till grupplista Alt++ Alt++ Delete RX Group Ta bort grupplista Alt+- Alt+- GroupListsWrapper RX Group Lists Grupplistor MainWindow File Fil Device Enhet Help Hjälp Databases Databaser Toolbar New Ny Creates a new Codeplug. Skapar en ny Codeplug. Ctrl+N Ctrl+N Open ... Öppna … <html><head/><body><p>Imports a codeplug from &quot;conf&quot; files.</p></body></html> <html><head/><body><p>Importerar en codeplug från &quot;conf&quot; filer.</p></body></html> Ctrl+O Ctrl+O Save ... Spara … <html><head/><body><p>Saves the codeplug in a &quot;conf&quot; file.</p></body></html> <html><head/><body><p>Sparar codepluggen i en &quot;conf&quot; fil.</p></body></html> Ctrl+S Ctrl+S Quit Sluta Quits the application. Avsluter applikationen. Ctrl+Q Ctrl+Q Detect Upptäck Detect connected radios. Upptäck anslutna radioapparater. Verify Verifiera <html><head/><body><p>Verifies the current codeplug with connected radios.</p></body></html> Verifierar den aktuella kodpluggen med anslutna radioapparater. Ctrl+R Ctrl+R Read Läsa Reads a codeplug from connected radios. Läser en kodplugg från anslutna radioapparater. Write Skriva Writes the codeplug to the connected radio. Skriver kodpluggen till den anslutna radion. About qdmr Om qdmr Read the handbook. Läs handboken. F1 F1 Settings Inställningar Shows settings dialog Visar inställningsdialog. Write Callsign DB Skriv anropssignal DB Writes call-sign DB to radio. Skriver anropssignal DB till radio. Refresh Callsign DB Uppdatera anropssignal DB Refreshes the downloaded callsign DB Uppdaterar den nedladdade anropssignal-DB Refresh Talkgroup DB Uppdatera samtalsgrupp DB Refreshes the downloaded talkgroup DB Uppdaterar den nedladdade samtalsgruppens DB Export to CHIRP ... Exports all FM channels to CHRIP CSV. Import ... Imports and merges a codeplug into the current one. Refresh Orbital Elements Refreshes the orbital elements. Edit Satellites ... Opens an editor to edit your satellite database. Write satellites Writes the orbital elements and transponder information onto the connected device. Radio IDs Radio ID-nummrar Contacts Kontakter Group Lists Grupplistor Channels Kanaler Zones Zoner Scan Lists Scan Lists GPS/APRS GPS/APRS Roaming Channels Roaming-kanaler Roaming Zones Roamingzoner Extensions Unsaved changes to codeplug. Ej sparade ändringar i codeplug. There are unsaved changes to the current codeplug. These changes are lost if you proceed. Det finns osparade ändringar av den aktuella kodpluggen. Dessa ändringar går förlorade om du fortsätter. MultiChannelSelectionDialog [Selected] [Vald] Select a channel: Välj kanal: MultiGroupCallSelectionDialog Show private calls Visa privata samtal Select a group call: Välj ett gruppsamtal: MultiRoamingChannelSelectionDialog Select roaming channels Välj roamingkanaler PositioningSystemListView Cannot delete GPS system Kan inte radera GPS-systemet Cannot delete GPS system: You have to select a GPS system first. Kan inte ta bort GPS-system: Du måste först välja ett GPS-system. Delete positioning system? Ta bort positioneringssystemet? Delete positioning system %1? Ta bort positioneringssystemet '%1'? Delete %1 positioning systems? Ta bort %1 positioneringssystem? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support GPS or APRS. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Obs!</span> QDMR är en enhetsoberoende CPS. Det är dock inte alla radioapparater som stöder GPS eller APRS. Därför kan dessa inställningar ignoreras när kodpluggen programmeras till enheten. </p><p align="right"><a href="#hide"> <span style=" text-decoration: underline; color:#0000ff;">Göm</span></a></p></body></html> Add GPS System Lägg till GPS-system Alt+G Alt+G Add APRS System Lägg till APRS-system Alt+A Alt+A Delete Position System Ta bort positioneringssystem Alt+- Alt+- PositioningSystemListWrapper DMR DMR APRS APRS [None] [Ingen] [Selected] [Vald] Type Typ Name Namn Destination Destination Period [s] Period [s] Channel Kanal Message Meddelande PropertyDelegate False Falsk True Sann [None] [Ingen] PropertyWrapper new element nytt element Property Egenskap Value Värde Description Beskrivning true sann false falsk [None] [Ingen] Instance of %1 Förekomst av '%1' List of %1 instances Lista över %1 instanser QObject [None] [Ingen] RXGroupListDialog Create Group List Skapa grupplista Edit Group List Redigera grupplista Cannot remove group call Kan inte ta bort gruppsamtal Cannot remove group call: You have to select at least one group call first. Kan inte ta bort gruppsamtal: Du måste först välja minst ett gruppsamtal. Basic Grundläggande Name Namn Add Contact Lägg till kontakt Alt++ Alt++ Remove Contact Ta bort kontakt Alt+- Alt+- Extensions RadioIDListView Cannot delete radio IDs Kan inte radera radio-ID:n Cannot delete radio IDs: You have to select a radio ID first. Kan inte radera radio-ID:n: Du måste först välja ett radio-ID. Delete radio ID? Ta bort radio-ID? Delete radio ID %1? Ta bort radio-ID '%1'? Delete scan lists? Ta bort skanningslistor? Delete %1 scan lists? Ta bort %1 skanningslistor? Default Radio ID Standard radio-ID Add Radio ID Lägg till radio-ID Delete Radio ID Ta bort radio-ID RadioIdListWrapper Type Typ Name Namn Number Nummer RadioSelectionDialog Cannot auto-detect radio Kan inte automatiskt upptäcka radio Select a specific radio Välj en specifik radio ReleaseNotes Cannot download release notes from https://github.com/hmatuschek/qdmr %1 Det går inte att ladda ned release notes från https://github.com/hmatuschek/qdmr %1 Cannot read release notes from https://github.com/hmatuschek/qdmr Release is not a JSON object! Kan inte läsa release notes från https://github.com/hmatuschek/qdmr Release är inte ett JSON-objekt! Cannot read release notes from https://github.com/hmatuschek/qdmr Release does not contain a release note. Kan inte läsa release notes från https://github.com/hmatuschek/qdmr Releasen innehåller ingen release note. qDMR was updated to version %1 qdmr uppdaterades till version %1 RoamingChannelDialog Name Namn RX Frequency [MHz] RX-frekvens [MHz] TX Frequency [MHz] Sändningsfrekvens [MHz] Time Slot Tidslucka Color Code Färgkod Selected Vald Edit roaming channel Redigera roamingkanal Create roaming channel Skapa roamingkanal TS 1 TS 1 TS 2 TS 2 RoamingChannelListView <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p><span style=" font-weight:600;">Hint:</span> You do not need to add roaming channel explicitly, just create roaming zones and add channels there. These channels are then added to this list.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Obs!</span> QDMR är en enhetsoberoende CPS. Det är dock inte alla radioapparater som stöder roaming. Därför kan dessa inställningar ignoreras när kodkontakten programmeras till enheten. </p><p><span style=" font-weight:600;">Tips:</span> Du behöver inte lägga till en roamingkanal uttryckligen, skapa bara roamingzoner och lägg till kanaler där. Dessa kanaler läggs sedan till i den här listan.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Göm</span></a></p></body></html> Add Roaming Channel Lägg till roamingkanal Delete Roaming Channel Ta bort roamingkanal Cannot delete roaming channel Det går inte att ta bort roamingkanal Cannot delete roaming channel: You have to select a channel first. Kan inte ta bort roamingkanal: Du måste först välja en kanal. Delete roaming channel? Delete roaming channel %1? Delete %1 roaming channel? RoamingChannelListWrapper [Selected] [Vald] Name Namn RX Frequency RX-frekvens TX Frequency Sändningsfrekvens TS TS Zones Zoner CC CC RoamingChannelRefListWrapper Roaming Channel Roaming kanal RoamingListWrapper %1 (containing %2 channels) %1 (innehåller %2 kanaler) Roaming zone Roamingzon RoamingZoneDialog Create Roaming Zone Skapa roamingzon Set Roaming Zone Redigera roamingzon Cannot remove channels. Kan inte ta bort kanaler. Cannot remove channels. Select at least one channel first. Kan inte ta bort kanaler. Välj minst en kanal först. Basic Grundläggande Name: Namn: Add Roaming Channel Lägg till roamingkanal Add DMR Channel Lägg till DMR-kanal Alt++ Alt++ Remove Channel Ta bort kanal Alt+- Alt+- Extension Förlängning RoamingZoneListView Generate roaming zone Skapa roamingzon Create a roaming zone by collecting all channels with these group calls. Skapa en roamingzon genom att samla alla kanaler med dessa gruppsamtal. Cannot delete roaming zone Det går inte att ta bort roamingzon Cannot delete roaming zone: You have to select a zone first. Det går inte att ta bort roamingzon: Du måste först välja en zon. Delete roaming zone? Ta bort roamingzon? Delete roaming zone %1? Ta bort roamingzon '%1'? Delete roaming zones? Ta bort roamingzoner? Delete %1 roaming zones? Ta bort %1 roamingzoner? <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Obs!</span> QDMR är en enhetsoberoende CPS. Det är dock inte alla radioapparater som stöder roaming. Därför kan dessa inställningar ignoreras när kodkontakten programmeras till enheten. </p> <p align="right"><a href="#hide"> <span style=" text-decoration: underline; color:#0000ff;">Göm</span></a></p></body></html> Add Roaming Zone Lägg till roamingzon Alt++ Alt++ Generate Roaming Zone Skapa roamingzon Delete Roaming Zone Ta bort roamingzon Alt+- Alt+- SatelliteDatabaseDialog Edit satellite database Add Delete SatelliteSelectionDialog Select a satellite ScanListDialog Edit Scan List Redigera skanningslista Basic Grundläggande Name Namn Primary Channel (50%) Primär kanal (50 %) Secondary Channel (25%) Sekundär kanal (25 %) Transmit Channel Add Channel Lägg till kanal Alt++ Alt++ Remove Channel Ta bort kanal Alt+- Alt+- Extensions Create Scan List Skapa skanningslista [None] [Ingen] [Selected] [Vald] [Last] [Sista] ScanListsView Cannot delete scanlist Kan inte radera skanningslistan Cannot delete scanlist: You have to select a scanlist first. Kan inte ta bort skanningslista: Du måste först välja en skanningslista. Delete scan list? Ta bort skanningslistan? Delete scan list %1? Ta bort skanningslistan '%1'? Delete scan lists? Ta bort skanningslistor? Delete %1 scan lists? Ta bort %1 skanningslistor? Add Scan List Lägg till skanningslista Alt++ Alt++ Delete Scan List Ta bort skanningslista Alt+- Alt+- ScanListsWrapper Scan-List Scanlista SearchPopup Ctrl+F Ctrl+F %1/%2 %1/%2 SelectiveCallBox None CTCSS DCS Hz Inverted SettingsDialog Warning! Varning! Settings Inställningar Location System location Locator Repeater Info Sources enable Radio Programming Update codeplug Uppdatera codeplug <html><head/><body><p>Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.</p><p><br/></p><p>If selected, QDMR downloads the codeplug from the radio and updates only those settings specified. The remaining settings within the radio are not touched (recommended).</p></body></html> <html><head/><body><p>Uppdatera kodpluggen på radion. Om det inte väljs, åsidosätts kodpluggen på radion med möjligen ofullständiga standardvärden.</p> <p><br/></p><p>Om det väljs, laddar QDMR ner kodpluggen från radion och uppdaterar endast dessa inställningar specificerad. De återstående inställningarna i radion berörs inte (rekommenderas).</p></body></html> Auto-enable GPS Autoaktivera GPS When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically. När ett GPS- eller APRS-system definieras och används för valfri kanal, aktiveras GPS-modulen automatiskt. When a roaming zone is defined and used by any channel, the automatic roaming gets enabled. När en roamingzon definieras och används av en kanal, aktiveras automatisk roaming. Auto-enable roaming Aktivera roaming automatiskt Data Sources World North America Programming Radio Interfaces disable auto-detect Ignore verification warnings Ignorera verifieringsvarningar <html><head/><body><p>As the communication interface to the radio is kept open after verification, time-outs may occur and the code-plug upload may fail when the verification dialog pops up. To prevent this, verification warnings can be ignored, eliminating the time-gap between verification and upload. Verification errors still prevent the upload.</p></body></html> <html><head/><body><p>Eftersom kommunikationsgränssnittet till radion hålls öppet efter verifiering kan time-outs inträffa och kodplugguppladdningen kan misslyckas när verifieringsdialogrutan dyker upp. För att förhindra detta kan verifieringsvarningar ignoreras, vilket eliminerar tidsskillnaden mellan verifiering och uppladdning. Verifieringsfel förhindrar fortfarande uppladdningen.</p></body></html> Ignore frequency limits Ignorera frekvensgränser Do not set this option unless you know what you are doing. Ställ inte in det här alternativet om du inte vet vad du gör. Call-Sign DB Anropssignal DB Limit number of DB entries Begränsa antalet DB-poster When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent). När det är aktiverat kommer antalet DB-poster att vara begränsat. Annars genereras det maximala antalet poster (enhetsberoende). Number of DB entries Antal DB-poster Specifies the number of DB entries (if enabled above). Anger antalet DB-poster (om aktiverat ovan). Select using my DMR ID Välj med mitt DMR-ID If enabled, the entries are selected using the users DMR ID. Om det är aktiverat väljs posterna med användarens DMR-ID. Select using prefixes Välj med prefix If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries. Om aktiverat används dessa kommaseparerade DMR ID-prefix för att välja anropssignal DB-posterna. Extensions Extended Features Utökade funktioner Show commercial features Visa kommersiella funktioner Show device extensions Visa enhetstillägg VerifyDialog Verify Codeplug Verifiera Codeplug ZoneDialog Edit Zone Redigera zon Basic Grundläggande <html><head/><body><p align="justify"><span style=" font-weight:600;">Note:</span> Zones are collections of channels that are usually valid for a specific region. I.e., a collection of channels for repeaters within a certain region. </p><p align="justify">QDMR manages zones by allowing for two independent channel lists for each VFO of the radio (if it has two). Many radios however, allow one to assign zones to each VFO individually. In these cases, QDMR will split the zone into two (A &amp; B) and program them individually into the radio.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> <html><head/><body><p align="justify"><span style=" font-weight:600;">Obs! </span> Zoner är samlingar av kanaler som vanligtvis är giltiga för en specifik region. Dvs en samling kanaler för repeatrar inom en viss region. </p> <p align="justify">QDMR hanterar zoner genom att tillåta två oberoende kanallistor för varje VFO i radion (om den har två). Många radioapparater tillåter dock en att tilldela zoner till varje VFO individuellt. I dessa fall kommer QDMR att dela upp zonen i två (A &amp; B) och programmera in dem individuellt i radion.</p><p align="right"> <a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Göm </span></a></p></body></html> Name Namn Channels A Kanaler A add Lägg till remove ta bort Channels B Kanaler B Extension Förlängning Create Zone Skapa zon Cannot remove channel Kan inte ta bort kanalen Select at least one channel first. Välj minst en kanal först. ZoneListView Cannot delete zone Kan inte ta bort zon Cannot delete zone: You have to select a zone first. Kan inte ta bort zon: Du måste först välja en zon. Delete zone? Ta bort zon? Delete zone %1? Ta bort zon '%1'? Delete zones? Ta bort zoner? Delete %1 zones? Ta bort %1 zoner? Add Zone Lägg till zon Alt++ Alt++ Delete Zone Ta bort zon Alt+- Alt+- ZoneListWrapper Zone Zon aprssystemdialog Edit APRS System Redigera APRS-system Basic Grundläggande Name Namn Channel Kanal Source Källa Destination Destination Path Väg Icon Ikon Update period [s] Uppdateringsperiod [s] Message Meddelande Extensions qdmr-0.12.3/lib/000077500000000000000000000000001501654372000133115ustar00rootroot00000000000000qdmr-0.12.3/lib/CMakeLists.txt000066400000000000000000000145021501654372000160530ustar00rootroot00000000000000IF (APPLE) SET(hid_SOURCES hid_macos.cc) SET(hid_HEADERS hid_macos.hh) #SET(hid_SOURCES hid_libusb.cc) #SET(hid_HEADERS hid_libusb.hh) ELSE (APPLE) SET(hid_SOURCES hid_libusb.cc) SET(hid_HEADERS hid_libusb.hh) ENDIF(APPLE) SET(libdmrconf_SOURCES utils.cc crc32.cc addressmap.cc radiointerface.cc errorstack.cc frequency.cc interval.cc ranges.cc dummyfilereader.cc chirpformat.cc signaling.cc radio.cc ${hid_SOURCES} dfu_libusb.cc usbserial.cc radioinfo.cc usbdevice.cc radiolimits.cc csvreader.cc dfufile.cc userdatabase.cc logger.cc melody.cc visitor.cc configlabelingvisitor.cc configcopyvisitor.cc intermediaterepresentation.cc configmergevisitor.cc configobject.cc configreference.cc config.cc radiosettings.cc contact.cc rxgrouplist.cc channel.cc zone.cc scanlist.cc gpssystem.cc codeplug.cc roamingzone.cc roamingchannel.cc callsigndb.cc talkgroupdatabase.cc radioid.cc encryptionextension.cc commercial_extension.cc smsextension.cc satellitedatabase.cc orbitalelementsdatabase.cc transponderdatabase.cc satelliteconfig.cc tyt_radio.cc tyt_interface.cc tyt_codeplug.cc tyt_callsigndb.cc tyt_extensions.cc md2017.cc md2017_codeplug.cc md2017_callsigndb.cc md2017_filereader.cc md2017_limits.cc md390.cc md390_codeplug.cc md390_filereader.cc md390_limits.cc uv390.cc uv390_codeplug.cc uv390_callsigndb.cc uv390_filereader.cc uv390_limits.cc dm1701.cc dm1701_codeplug.cc dm1701_callsigndb.cc dm1701_filereader.cc dm1701_limits.cc radioddity_radio.cc radioddity_codeplug.cc radioddity_extensions.cc radioddity_interface.cc rd5r.cc rd5r_codeplug.cc rd5r_filereader.cc rd5r_limits.cc gd77.cc gd77_codeplug.cc gd77_callsigndb.cc gd77_filereader.cc gd77_limits.cc opengd77_interface.cc opengd77base.cc opengd77base_codeplug.cc opengd77base_callsigndb.cc opengd77base_satelliteconfig.cc opengd77_extension.cc opengd77_limits.cc opengd77.cc opengd77_codeplug.cc opengd77_callsigndb.cc opengd77_satelliteconfig.cc openuv380.cc openuv380_codeplug.cc openuv380_callsigndb.cc openuv380_satelliteconfig.cc openrtx.cc openrtx_interface.cc openrtx_codeplug.cc c7000device.cc gd73.cc gd73_interface.cc gd73_codeplug.cc gd73_filereader.cc gd73_limits.cc anytone_interface.cc anytone_radio.cc anytone_codeplug.cc anytone_extension.cc anytone_limits.cc anytone_satelliteconfig.cc d868uv.cc d868uv_codeplug.cc d868uv_callsigndb.cc d868uv_limits.cc d878uv.cc d878uv_codeplug.cc d878uv_limits.cc d578uv.cc d578uv_codeplug.cc d578uv_limits.cc d878uv2.cc d878uv2_codeplug.cc d878uv2_limits.cc d878uv2_callsigndb.cc dmr6x2uv.cc dmr6x2uv_codeplug.cc dmr6x2uv_limits.cc auctus_a6_interface.cc dr1801uv.cc dr1801uv_interface.cc dr1801uv_codeplug.cc dr1801uv_filereader.cc dr1801uv_limits.cc) SET(libdmrconf_MOC_HEADERS radio.hh ${hid_HEADERS} dfu_libusb.hh usbserial.hh radiolimits.hh csvreader.hh dfufile.hh userdatabase.hh logger.hh melody.hh configobject.hh configreference.hh config.hh radiosettings.hh contact.hh rxgrouplist.hh channel.hh zone.hh scanlist.hh gpssystem.hh codeplug.hh roamingzone.hh roamingchannel.hh callsigndb.hh talkgroupdatabase.hh radioid.hh encryptionextension.hh commercial_extension.hh smsextension.hh satellitedatabase.hh orbitalelementsdatabase.hh transponderdatabase.hh satelliteconfig.hh tyt_radio.hh tyt_interface.hh tyt_codeplug.hh tyt_callsigndb.hh tyt_extensions.hh md2017.hh md2017_codeplug.hh md2017_callsigndb.hh md2017_limits.hh md390.hh md390_codeplug.hh md390_limits.hh uv390.hh uv390_codeplug.hh uv390_callsigndb.hh uv390_limits.hh dm1701.hh dm1701_codeplug.hh dm1701_callsigndb.hh dm1701_filereader.hh dm1701_limits.hh radioddity_radio.hh radioddity_codeplug.hh radioddity_extensions.hh radioddity_interface.hh rd5r.hh rd5r_codeplug.hh rd5r_limits.hh gd77.hh gd77_codeplug.hh gd77_callsigndb.hh gd77_limits.hh opengd77_interface.hh opengd77base.hh opengd77base_codeplug.hh opengd77base_callsigndb.hh opengd77base_satelliteconfig.hh opengd77_extension.hh opengd77_limits.hh opengd77.hh opengd77_codeplug.hh opengd77_callsigndb.hh opengd77_satelliteconfig.hh openuv380.hh openuv380_codeplug.hh openuv380_callsigndb.hh openuv380_satelliteconfig.hh c7000device.hh gd73.hh gd73_interface.hh gd73_codeplug.hh gd73_limits.hh openrtx.hh openrtx_interface.hh openrtx_codeplug.hh anytone_interface.hh anytone_radio.hh anytone_codeplug.hh anytone_extension.hh anytone_limits.hh anytone_satelliteconfig.hh d868uv.hh d868uv_codeplug.hh d868uv_callsigndb.hh d868uv_limits.hh d878uv.hh d878uv_codeplug.hh d878uv_limits.hh d578uv.hh d578uv_codeplug.hh d578uv_limits.hh d878uv2.hh d878uv2_codeplug.hh d878uv2_limits.hh d878uv2_callsigndb.hh dmr6x2uv.hh dmr6x2uv_codeplug.hh dmr6x2uv_limits.hh auctus_a6_interface.hh dr1801uv.hh dr1801uv_interface.hh dr1801uv_codeplug.hh dr1801uv_limits.hh) SET(libdmrconf_HEADERS libdmrconf.hh radiointerface.hh radioinfo.hh usbdevice.hh signaling.hh gd77_filereader.hh rd5r_filereader.hh uv390_filereader.hh md2017_filereader.hh gd73_filereader.hh md390_filereader.hh dr1801uv_filereader.hh dummyfilereader.hh utils.hh crc32.hh signaling.hh addressmap.hh errorstack.hh frequency.hh interval.hh ranges.hh chirpformat.hh visitor.hh configlabelingvisitor.hh configcopyvisitor.hh intermediaterepresentation.hh configmergevisitor.hh) configure_file(config.h.in ${PROJECT_BINARY_DIR}/lib/config.h) #SET(CMAKE_AUTOMOC ON) qt5_wrap_cpp(libdmrconf_MOC_SOURCES ${libdmrconf_MOC_HEADERS}) add_library(libdmrconf SHARED ${libdmrconf_SOURCES} ${libdmrconf_MOC_SOURCES}) set_target_properties(libdmrconf PROPERTIES MACOSX_RPATH TRUE OUTPUT_NAME dmrconf VERSION "${PROJECT_VERSION}" SOVERSION "${PROJECT_VERSION_MAJOR}") target_link_libraries(libdmrconf ${CORE_LIBS}) install(TARGETS libdmrconf DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}) install(FILES ${libdmrconf_HEADERS} DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/libdmrconf) install(FILES ${libdmrconf_MOC_HEADERS} DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/libdmrconf) install(FILES ${PROJECT_BINARY_DIR}/lib/config.h DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/libdmrconf) if (UNIX AND NOT APPLE AND ${INSTALL_UDEV_RULES}) install(FILES "${CMAKE_SOURCE_DIR}/dist/99-qdmr.rules" DESTINATION ${INSTALL_UDEV_PATH}) endif(UNIX AND NOT APPLE AND ${INSTALL_UDEV_RULES}) qdmr-0.12.3/lib/addressmap.cc000066400000000000000000000026041501654372000157450ustar00rootroot00000000000000#include "addressmap.hh" #include AddressMap::AddressMap() : _items() { // pass... } AddressMap::AddressMap(const AddressMap &other) : _items(other._items) { // pass... } AddressMap & AddressMap::operator =(const AddressMap &other) { _items = other._items; return *this; } void AddressMap::clear() { _items.clear(); } bool AddressMap::add(uint32_t addr, uint32_t len, int idx) { if (0 > idx) idx = _items.size(); AddrMapItem item(addr, len, idx); std::vector::iterator at = std::lower_bound(_items.begin(), _items.end(), item); if (_items.end() == at) _items.push_back(item); else _items.insert(at, item); return true; } bool AddressMap::rem(uint32_t idx) { std::vector::iterator at = _items.begin(); for (; at!=_items.end(); at++) { if (at->index == idx) break; } if (_items.end() == at) return false; _items.erase(at); return true; } bool AddressMap::contains(uint32_t addr) const { return 0 <= find(addr); } int AddressMap::find(uint32_t addr) const { std::vector::const_iterator at = std::lower_bound(_items.begin(), _items.end(), addr); if (_items.end() == at) return _items.back().contains(addr) ? _items.back().index : -1; if (at->contains(addr)) return at->index; if (_items.begin() == at) return -1; --at; return at->contains(addr) ? at->index : -1; } qdmr-0.12.3/lib/addressmap.hh000066400000000000000000000042241501654372000157570ustar00rootroot00000000000000#ifndef ADDRESSMAP_HH #define ADDRESSMAP_HH #include #include /** This class represents a memory map. * That is, it maintains a vector of memory regions (address and length) that can be searched * efficiently. This should speedup the generation of codeplugs consisting of many small memory * sections. * * @ingroup util */ class AddressMap { public: /** Empty constructor. */ AddressMap(); /** Copy constructor. */ AddressMap(const AddressMap &other); /** Copy assignment. */ AddressMap &operator=(const AddressMap &other); /** Clears the address map. */ void clear(); /** Adds an item to the address map. */ bool add(uint32_t addr, uint32_t len, int idx=-1); /** Removes an item from the address map associated with the given index. */ bool rem(uint32_t idx); /** Returns @c true if the given address is contained in any of the memory regions. */ bool contains(uint32_t addr) const; /** Finds the index of the memory region containing the given address. If no such region is found, * -1 is returned. */ int find(uint32_t addr) const; protected: /** Memory map item. * That is, a collection of address, length and associated index. */ struct AddrMapItem { uint32_t address; ///< The start address of the item. uint32_t length; ///< The size/length of the memory item. uint32_t index; ///< The associated (element) index. /** Constructor. */ inline AddrMapItem(uint32_t addr, uint32_t len, uint32_t idx) : address(addr), length(len), index(idx) { // pass... } /** Comparison operator. */ inline bool operator<(const AddrMapItem &other) const { return address < other.address; } /** Comparison operator. */ inline bool operator<(uint32_t addr) const { return address < addr; } /** Returns @c true if the given address is contained within this memory region. */ inline bool contains(uint32_t addr) const { return (address <= addr) && ((address+length) > addr); } }; protected: /** Holds the vector of memory items, the order of these items is maintained. */ std::vector _items; }; #endif // ADDRESSMAP_HH qdmr-0.12.3/lib/anytone_codeplug.cc000066400000000000000000004212721501654372000171670ustar00rootroot00000000000000#include "anytone_codeplug.hh" #include "utils.hh" #include "logger.hh" #include "anytone_extension.hh" #include "config.hh" #include "melody.hh" #include "intermediaterepresentation.hh" #include #include #define CUSTOM_CTCSS_TONE 0x33 QVector _anytone_bin_dtmf_tab = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','*','#' }; /* ******************************************************************************************** * * Implementation of AnytoneCodeplug::CTCSS * ******************************************************************************************** */ SelectiveCall AnytoneCodeplug::CTCSS::_codeTable[] = { SelectiveCall(62.5), SelectiveCall(67.0), SelectiveCall(69.3), SelectiveCall(71.9), SelectiveCall(74.4), SelectiveCall(77.0), SelectiveCall(79.7), SelectiveCall(82.5), SelectiveCall(85.4), SelectiveCall(88.5), SelectiveCall(91.5), SelectiveCall(94.8), SelectiveCall(97.4), SelectiveCall(100.0), SelectiveCall(103.5), SelectiveCall(107.2), SelectiveCall(110.9), SelectiveCall(114.8), SelectiveCall(118.8), SelectiveCall(123.0), SelectiveCall(127.3), SelectiveCall(131.8), SelectiveCall(136.5), SelectiveCall(141.3), SelectiveCall(146.2), SelectiveCall(151.4), SelectiveCall(156.7), SelectiveCall(159.8), SelectiveCall(162.2), SelectiveCall(165.5), SelectiveCall(167.9), SelectiveCall(171.3), SelectiveCall(173.8), SelectiveCall(177.3), SelectiveCall(179.9), SelectiveCall(183.5), SelectiveCall(186.2), SelectiveCall(189.9), SelectiveCall(192.8), SelectiveCall(196.6), SelectiveCall(199.5), SelectiveCall(203.5), SelectiveCall(206.5), SelectiveCall(210.7), SelectiveCall(218.1), SelectiveCall(225.7), SelectiveCall(229.1), SelectiveCall(233.6), SelectiveCall(241.8), SelectiveCall(250.3), SelectiveCall(254.1), SelectiveCall() }; uint8_t AnytoneCodeplug::CTCSS::encode(const SelectiveCall &code) { for (uint8_t i=0; i<52; i++) { if (code == _codeTable[i]) return i; } return 0; } SelectiveCall AnytoneCodeplug::CTCSS::decode(uint8_t num) { if (num >= 52) return SelectiveCall(); return _codeTable[num]; } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::BitmapElement * ********************************************************************************************* */ AnytoneCodeplug::BitmapElement::BitmapElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } void AnytoneCodeplug::BitmapElement::clear() { memset(_data, 0, _size); } bool AnytoneCodeplug::BitmapElement::isEncoded(unsigned int idx) const { unsigned int byte = idx/8, bit = idx%8; return (_data[byte] & (1 << bit)); } void AnytoneCodeplug::BitmapElement::setEncoded(unsigned int idx, bool enable) { unsigned int byte = idx/8, bit = idx%8; if (enable) _data[byte] |= (1 << bit); else _data[byte] &= ~(1 << bit); } void AnytoneCodeplug::BitmapElement::enableFirst(unsigned int n) { unsigned int byte = n/8, bit=n%8; memset(_data, 0xff, byte); for (unsigned int i=0; i= _size) return false; return 0 == _data[idx]; } void AnytoneCodeplug::InvertedBytemapElement::setEncoded(unsigned int idx, bool enable) { if (idx >= _size) return; _data[idx] = enable ? 0x00 : 0xff; } void AnytoneCodeplug::InvertedBytemapElement::enableFirst(unsigned int n) { memset(_data, 0x00, n); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::ChannelElement * ********************************************************************************************* */ AnytoneCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::ChannelElement::ChannelElement(uint8_t *ptr) : Element(ptr, ChannelElement::size()) { // pass... } AnytoneCodeplug::ChannelElement::~ChannelElement() { // pass... } void AnytoneCodeplug::ChannelElement::clear() { setRXFrequency(0); setTXOffset(0); setMode(Mode::Analog); setPower(Channel::Power::Low); setBandwidth(FMChannel::Bandwidth::Narrow); setRXTone(SelectiveCall()); setTXTone(SelectiveCall()); setBit(0x0008, 5, false); // Unused set to 0 } unsigned AnytoneCodeplug::ChannelElement::rxFrequency() const { return ((unsigned)getBCD8_be(0x0000))*10; } void AnytoneCodeplug::ChannelElement::setRXFrequency(unsigned hz) { setBCD8_be(0x0000, hz/10); } unsigned AnytoneCodeplug::ChannelElement::txOffset() const { return ((unsigned)getBCD8_be(0x0004))*10; } void AnytoneCodeplug::ChannelElement::setTXOffset(unsigned hz) { setBCD8_be(0x0004, hz/10); } unsigned AnytoneCodeplug::ChannelElement::txFrequency() const { unsigned rx = rxFrequency(), off = txOffset(); if (RepeaterMode::Simplex == repeaterMode()) return rx; else if (RepeaterMode::Positive == repeaterMode()) return rx+off; return rx-off; } void AnytoneCodeplug::ChannelElement::setTXFrequency(unsigned hz) { unsigned rx = rxFrequency(); if (rx == hz) { setTXOffset(0); setRepeaterMode(RepeaterMode::Simplex); } else if (rx < hz) { setTXOffset(hz-rx); setRepeaterMode(RepeaterMode::Positive); } else { setTXOffset(rx-hz); setRepeaterMode(RepeaterMode::Negative); } } AnytoneCodeplug::ChannelElement::Mode AnytoneCodeplug::ChannelElement::mode() const { return (Mode) getUInt2(0x0008, 0); } void AnytoneCodeplug::ChannelElement::setMode(Mode mode) { setUInt2(0x0008, 0, (unsigned)mode); } Channel::Power AnytoneCodeplug::ChannelElement::power() const { switch ((Power)getUInt2(0x0008, 2)) { case POWER_LOW: return Channel::Power::Low; case POWER_MIDDLE: return Channel::Power::Mid; case POWER_HIGH: return Channel::Power::High; case POWER_TURBO: return Channel::Power::Max; } return Channel::Power::Low; } void AnytoneCodeplug::ChannelElement::setPower(Channel::Power power) { switch (power) { case Channel::Power::Min: case Channel::Power::Low: setUInt2(0x0008, 2, (unsigned)POWER_LOW); break; case Channel::Power::Mid: setUInt2(0x0008, 2, (unsigned)POWER_MIDDLE); break; case Channel::Power::High: setUInt2(0x0008, 2, (unsigned)POWER_HIGH); break; case Channel::Power::Max: setUInt2(0x0008, 2, (unsigned)POWER_TURBO); break; } } FMChannel::Bandwidth AnytoneCodeplug::ChannelElement::bandwidth() const { if (getBit(0x0008, 4)) return FMChannel::Bandwidth::Wide; return FMChannel::Bandwidth::Narrow; } void AnytoneCodeplug::ChannelElement::setBandwidth(FMChannel::Bandwidth bw) { switch (bw) { case FMChannel::Bandwidth::Narrow: setBit(0x0008, 4, false); break; case FMChannel::Bandwidth::Wide: setBit(0x0008, 4, true); break; } } AnytoneCodeplug::ChannelElement::RepeaterMode AnytoneCodeplug::ChannelElement::repeaterMode() const { return (RepeaterMode)getUInt2(0x0008, 6); } void AnytoneCodeplug::ChannelElement::setRepeaterMode(RepeaterMode mode) { setUInt2(0x0008, 6, (unsigned)mode); } AnytoneCodeplug::ChannelElement::SignalingMode AnytoneCodeplug::ChannelElement::rxSignalingMode() const { return (SignalingMode)getUInt2(0x0009, 0); } void AnytoneCodeplug::ChannelElement::setRXSignalingMode(SignalingMode mode) { setUInt2(0x0009, 0, (unsigned)mode); } SelectiveCall AnytoneCodeplug::ChannelElement::rxTone() const { if (SignalingMode::None == rxSignalingMode()) return SelectiveCall(); else if (SignalingMode::CTCSS == rxSignalingMode()) return rxCTCSS(); else if (SignalingMode::DCS == rxSignalingMode()) return rxDCS(); return SelectiveCall(); } void AnytoneCodeplug::ChannelElement::setRXTone(const SelectiveCall &code) { if (code.isInvalid()) { setRXSignalingMode(SignalingMode::None); } else if (code.isCTCSS()) { setRXSignalingMode(SignalingMode::CTCSS); setRXCTCSS(code); } else if (code.isDCS()) { setRXSignalingMode(SignalingMode::DCS); setRXDCS(code); } } AnytoneCodeplug::ChannelElement::SignalingMode AnytoneCodeplug::ChannelElement::txSignalingMode() const { return (SignalingMode)getUInt2(0x0009, 2); } void AnytoneCodeplug::ChannelElement::setTXSignalingMode(SignalingMode mode) { setUInt2(0x0009, 2, (unsigned)mode); } SelectiveCall AnytoneCodeplug::ChannelElement::txTone() const { if (SignalingMode::None == txSignalingMode()) return SelectiveCall(); else if (SignalingMode::CTCSS == txSignalingMode()) return txCTCSS(); else if (SignalingMode::DCS == txSignalingMode()) return txDCS(); return SelectiveCall(); } void AnytoneCodeplug::ChannelElement::setTXTone(const SelectiveCall &code) { if (code.isInvalid()) { setTXSignalingMode(SignalingMode::None); } else if (code.isCTCSS()) { setTXSignalingMode(SignalingMode::CTCSS); setTXCTCSS(code); } else if (code.isDCS()) { setTXSignalingMode(SignalingMode::DCS); setTXDCS(code); } } bool AnytoneCodeplug::ChannelElement::ctcssPhaseReversal() const { return getBit(0x0009, 4); } void AnytoneCodeplug::ChannelElement::enableCTCSSPhaseReversal(bool enable) { setBit(0x0009, 4, enable); } bool AnytoneCodeplug::ChannelElement::rxOnly() const { return getBit(0x0009, 5); } void AnytoneCodeplug::ChannelElement::enableRXOnly(bool enable) { setBit(0x0009, 5, enable); } bool AnytoneCodeplug::ChannelElement::callConfirm() const { return getBit(0x0009, 6); } void AnytoneCodeplug::ChannelElement::enableCallConfirm(bool enable) { setBit(0x0009, 6, enable); } bool AnytoneCodeplug::ChannelElement::talkaround() const { return getBit(0x0009, 7); } void AnytoneCodeplug::ChannelElement::enableTalkaround(bool enable) { setBit(0x0009, 7, enable); } bool AnytoneCodeplug::ChannelElement::txCTCSSIsCustom() const { return CUSTOM_CTCSS_TONE == getUInt8(0x000a); } SelectiveCall AnytoneCodeplug::ChannelElement::txCTCSS() const { return CTCSS::decode(getUInt8(0x000a)); } void AnytoneCodeplug::ChannelElement::setTXCTCSS(const SelectiveCall &tone) { setUInt8(0x000a, CTCSS::encode(tone)); } void AnytoneCodeplug::ChannelElement::enableTXCustomCTCSS() { setUInt8(0x000a, CUSTOM_CTCSS_TONE); } bool AnytoneCodeplug::ChannelElement::rxCTCSSIsCustom() const { return CUSTOM_CTCSS_TONE == getUInt8(0x000b); } SelectiveCall AnytoneCodeplug::ChannelElement::rxCTCSS() const { return CTCSS::decode(getUInt8(0x000b)); } void AnytoneCodeplug::ChannelElement::setRXCTCSS(const SelectiveCall &tone) { setUInt8(0x000b, CTCSS::encode(tone)); } void AnytoneCodeplug::ChannelElement::enableRXCustomCTCSS() { setUInt8(0x000b, CUSTOM_CTCSS_TONE); } SelectiveCall AnytoneCodeplug::ChannelElement::txDCS() const { uint16_t code = getUInt16_le(0x000c); if (512 > code) return SelectiveCall::fromBinaryDCS(code, false); return SelectiveCall::fromBinaryDCS(code-512, true); } void AnytoneCodeplug::ChannelElement::setTXDCS(const SelectiveCall &code) { if (code.isDCS()) setUInt16_le(0x000c, code.binCode() + (code.isInverted() ? 512 : 0)); else setUInt16_le(0x000c, 0); } SelectiveCall AnytoneCodeplug::ChannelElement::rxDCS() const { uint16_t code = getUInt16_le(0x000e); if (512 > code) return SelectiveCall::fromBinaryDCS(code, false); return SelectiveCall::fromBinaryDCS(code-512, true); } void AnytoneCodeplug::ChannelElement::setRXDCS(const SelectiveCall &code) { if (code.isDCS()) setUInt16_le(0x000e, code.binCode() + (code.isInverted() ? 512 : 0)); else setUInt16_le(0x000e, 0); } double AnytoneCodeplug::ChannelElement::customCTCSSFrequency() const { return ((double) getUInt16_le(0x0010))/10; } void AnytoneCodeplug::ChannelElement::setCustomCTCSSFrequency(double hz) { setUInt16_le(0x0010, hz*10); } unsigned AnytoneCodeplug::ChannelElement::twoToneDecodeIndex() const { return getUInt16_le(0x0012); } void AnytoneCodeplug::ChannelElement::setTwoToneDecodeIndex(unsigned idx) { setUInt16_le(0x0012, idx); } unsigned AnytoneCodeplug::ChannelElement::contactIndex() const { return getUInt32_le(0x0014); } void AnytoneCodeplug::ChannelElement::setContactIndex(unsigned idx) { return setUInt32_le(0x0014, idx); } unsigned AnytoneCodeplug::ChannelElement::radioIDIndex() const { return getUInt8(0x0018); } void AnytoneCodeplug::ChannelElement::setRadioIDIndex(unsigned idx) { return setUInt8(0x0018, idx); } AnytoneFMChannelExtension::SquelchMode AnytoneCodeplug::ChannelElement::squelchMode() const { return (AnytoneFMChannelExtension::SquelchMode)getUInt3(0x0019, 4); } void AnytoneCodeplug::ChannelElement::setSquelchMode(AnytoneFMChannelExtension::SquelchMode mode) { setUInt3(0x0019, 4, (unsigned)mode); } AnytoneCodeplug::ChannelElement::Admit AnytoneCodeplug::ChannelElement::admit() const { return (Admit)getUInt2(0x001a, 0); } void AnytoneCodeplug::ChannelElement::setAdmit(Admit admit) { setUInt2(0x001a, 0, (unsigned)admit); } AnytoneCodeplug::ChannelElement::OptSignaling AnytoneCodeplug::ChannelElement::optionalSignaling() const { return (OptSignaling)getUInt2(0x001a, 4); } void AnytoneCodeplug::ChannelElement::setOptionalSignaling(OptSignaling sig) { setUInt2(0x001a, 4, (unsigned)sig); } bool AnytoneCodeplug::ChannelElement::hasScanListIndex() const { return 0xff != scanListIndex(); } unsigned AnytoneCodeplug::ChannelElement::scanListIndex() const { return getUInt8(0x001b); } void AnytoneCodeplug::ChannelElement::setScanListIndex(unsigned idx) { setUInt8(0x001b, idx); } void AnytoneCodeplug::ChannelElement::clearScanListIndex() { setScanListIndex(0xff); } bool AnytoneCodeplug::ChannelElement::hasGroupListIndex() const { return 0xff != groupListIndex(); } unsigned AnytoneCodeplug::ChannelElement::groupListIndex() const { return getUInt8(0x001c); } void AnytoneCodeplug::ChannelElement::setGroupListIndex(unsigned idx) { setUInt8(0x001c, idx); } void AnytoneCodeplug::ChannelElement::clearGroupListIndex() { setGroupListIndex(0xff); } unsigned AnytoneCodeplug::ChannelElement::twoToneIDIndex() const { return getUInt8(0x001d); } void AnytoneCodeplug::ChannelElement::setTwoToneIDIndex(unsigned idx) { setUInt8(0x001d, idx); } unsigned AnytoneCodeplug::ChannelElement::fiveToneIDIndex() const { return getUInt8(0x001e); } void AnytoneCodeplug::ChannelElement::setFiveToneIDIndex(unsigned idx) { setUInt8(0x001e, idx); } unsigned AnytoneCodeplug::ChannelElement::dtmfIDIndex() const { return getUInt8(0x001f); } void AnytoneCodeplug::ChannelElement::setDTMFIDIndex(unsigned idx) { setUInt8(0x001f, idx); } unsigned AnytoneCodeplug::ChannelElement::colorCode() const { return getUInt8(0x0020); } void AnytoneCodeplug::ChannelElement::setColorCode(unsigned code) { setUInt8(0x0020, code); } DMRChannel::TimeSlot AnytoneCodeplug::ChannelElement::timeSlot() const { if (false == getBit(0x0021, 0)) return DMRChannel::TimeSlot::TS1; return DMRChannel::TimeSlot::TS2; } void AnytoneCodeplug::ChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) { if (DMRChannel::TimeSlot::TS1 == ts) setBit(0x0021, 0, false); else setBit(0x0021, 0, true); } bool AnytoneCodeplug::ChannelElement::smsConfirm() const { return getBit(0x0021, 1); } void AnytoneCodeplug::ChannelElement::enableSMSConfirm(bool enable) { setBit(0x0021, 1, enable); } bool AnytoneCodeplug::ChannelElement::simplexTDMA() const { return getBit(0x0021, 2); } void AnytoneCodeplug::ChannelElement::enableSimplexTDMA(bool enable) { setBit(0x0021, 2, enable); } bool AnytoneCodeplug::ChannelElement::adaptiveTDMA() const { return getBit(0x0021, 4); } void AnytoneCodeplug::ChannelElement::enableAdaptiveTDMA(bool enable) { setBit(0x0021, 4, enable); } bool AnytoneCodeplug::ChannelElement::rxAPRS() const { return getBit(0x0021, 5); } void AnytoneCodeplug::ChannelElement::enableRXAPRS(bool enable) { setBit(0x0021, 5, enable); } bool AnytoneCodeplug::ChannelElement::enhancedEncryption() const { return getBit(0x0021, 6); } void AnytoneCodeplug::ChannelElement::enableEnhancedEncryption(bool enable) { setBit(0x0021, 6, enable); } bool AnytoneCodeplug::ChannelElement::loneWorker() const { return getBit(0x0021, 7); } void AnytoneCodeplug::ChannelElement::enableLoneWorker(bool enable) { setBit(0x0021, 7, enable); } bool AnytoneCodeplug::ChannelElement::hasEncryptionKeyIndex() const { return 0xff != encryptionKeyIndex(); } unsigned AnytoneCodeplug::ChannelElement::encryptionKeyIndex() const { return getUInt8(0x0022); } void AnytoneCodeplug::ChannelElement::setEncryptionKeyIndex(unsigned idx) { setUInt8(0x0022, idx); } void AnytoneCodeplug::ChannelElement::clearEncryptionKeyIndex() { setEncryptionKeyIndex(0xff); } QString AnytoneCodeplug::ChannelElement::name() const { return readASCII(0x0023, 16, 0x00); } void AnytoneCodeplug::ChannelElement::setName(const QString &name) { writeASCII(0x0023, name, 16, 0x00); } Channel * AnytoneCodeplug::ChannelElement::toChannelObj(Context &ctx) const { Q_UNUSED(ctx) Channel *ch; AnytoneChannelExtension *ch_ext = nullptr; if ((Mode::Analog == mode()) || (Mode::MixedAnalog == mode())) { if (Mode::MixedAnalog == mode()) logWarn() << "Mixed mode channels are not supported (for now). Treat ch '" << name() <<"' as analog channel."; FMChannel *ach = new FMChannel(); switch(this->admit()) { case Admit::Always: ach->setAdmit(FMChannel::Admit::Always); break; case Admit::Busy: ach->setAdmit(FMChannel::Admit::Free); break; case Admit::Tone: ach->setAdmit(FMChannel::Admit::Tone); break; default: ach->setAdmit(FMChannel::Admit::Always); break; } ach->setRXTone(rxTone()); ach->setTXTone(txTone()); ach->setBandwidth(bandwidth()); // no per channel squelch settings ach->setSquelchDefault(); // Create extension AnytoneFMChannelExtension *ext = new AnytoneFMChannelExtension(); ch_ext = ext; ach->setAnytoneChannelExtension(ext); ext->enableReverseBurst(ctcssPhaseReversal()); ext->enableRXCustomCTCSS(rxCTCSSIsCustom()); ext->enableTXCustomCTCSS(txCTCSSIsCustom()); ext->setCustomCTCSS(customCTCSSFrequency()); ext->setSquelchMode(squelchMode()); // done ch = ach; } else if ((Mode::Digital == mode()) || (Mode::MixedDigital == mode())) { if (Mode::MixedDigital == mode()) logWarn() << "Mixed mode channels are not supported (for now). Treat ch '" << name() <<"' as digital channel."; DMRChannel *dch = new DMRChannel(); switch (this->admit()) { case Admit::Always: dch->setAdmit(DMRChannel::Admit::Always); break; case Admit::Free: dch->setAdmit(DMRChannel::Admit::Free); break; case Admit::DifferentColorCode:dch->setAdmit(DMRChannel::Admit::ColorCode); break; case Admit::SameColorCode: logWarn() << "Cannot decode admit cirit. 'same CC', use 'different CC' instead."; dch->setAdmit(DMRChannel::Admit::ColorCode); break; } dch->setColorCode(colorCode()); dch->setTimeSlot(timeSlot()); // Create extension AnytoneDMRChannelExtension *ext = new AnytoneDMRChannelExtension(); ch_ext = ext; dch->setAnytoneChannelExtension(ext); ext->enableCallConfirm(callConfirm()); ext->enableSMSConfirm(smsConfirm()); ext->enableSimplexTDMA(simplexTDMA()); ext->enableAdaptiveTDMA(adaptiveTDMA()); ext->enableLoneWorker(loneWorker()); // Done ch = dch; } else { logError() << "Cannot create channel '" << name() << "': Channel type " << (unsigned)mode() << "not supported."; return nullptr; } ch->setName(name()); ch->setRXFrequency(Frequency::fromHz(rxFrequency())); ch->setTXFrequency(Frequency::fromHz(txFrequency())); ch->setPower(power()); ch->setRXOnly(rxOnly()); // No per channel vox & tot setting ch->setVOXDefault(); ch->setDefaultTimeout(); // Apply common channel extension settings if (nullptr != ch_ext) { ch_ext->enableTalkaround(talkaround()); } return ch; } bool AnytoneCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx) const { if (Mode::Digital == mode()) { // If channel is digital DMRChannel *dc = c->as(); if (nullptr == dc) return false; // Check if default contact is set, in fact a valid contact index is mandatory. if (! ctx.has(contactIndex())) { logError() << "Cannot resolve contact index " << contactIndex() << " for channel '" << c->name() << "'."; return false; } dc->setTXContactObj(ctx.get(contactIndex())); // Set if RX group list is set if (hasGroupListIndex() && ctx.has(groupListIndex())) dc->setGroupListObj(ctx.get(groupListIndex())); // Link radio ID DMRRadioID *rid = ctx.get(radioIDIndex()); if (rid == ctx.config()->settings()->defaultIdRef()->as()) dc->setRadioIdObj(DefaultRadioID::get()); else dc->setRadioIdObj(rid); } else if (Mode::Analog == mode()) { // If channel is analog FMChannel *ac = c->as(); if (nullptr == ac) return false; } // For both, analog and digital channels: // If channel has scan list if (hasScanListIndex() && ctx.has(scanListIndex())) c->setScanList(ctx.get(scanListIndex())); return true; } bool AnytoneCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) { // Clear channel element clear(); // set channel name setName(c->name()); // set rx and tx frequencies setRXFrequency(c->rxFrequency().inHz()); setTXFrequency(c->txFrequency().inHz()); // set power if (c->defaultPower()) setPower(ctx.config()->settings()->power()); else setPower(c->power()); // set tx-enable enableRXOnly(c->rxOnly()); if (nullptr == c->scanList()) clearScanListIndex(); else setScanListIndex(ctx.index(c->scanList())); // Dispatch by channel type if (c->is()) { const FMChannel *ac = c->as(); setMode(Mode::Analog); // set admit criterion switch (ac->admit()) { case FMChannel::Admit::Always: setAdmit(Admit::Always); break; case FMChannel::Admit::Free: setAdmit(Admit::Busy); break; case FMChannel::Admit::Tone: setAdmit(Admit::Tone); break; } // squelch mode setRXTone(ac->rxTone()); setTXTone(ac->txTone()); if (ac->rxTone().isValid()) setSquelchMode(AnytoneFMChannelExtension::SquelchMode::SubTone); else setSquelchMode(AnytoneFMChannelExtension::SquelchMode::Carrier); // set bandwidth setBandwidth(ac->bandwidth()); // Handle extension if (AnytoneFMChannelExtension *ext = ac->anytoneChannelExtension()) { // Apply common settings enableTalkaround(ext->talkaround()); // Apply FM settings enableCTCSSPhaseReversal(ext->reverseBurst()); setCustomCTCSSFrequency(ext->customCTCSS()); if (ext->rxCustomCTCSS()) enableRXCustomCTCSS(); if (ext->txCustomCTCSS()) enableTXCustomCTCSS(); setSquelchMode(ext->squelchMode()); } } else if (c->is()) { const DMRChannel *dc = c->as(); // pack digital channel config. setMode(Mode::Digital); // set admit criterion switch(dc->admit()) { case DMRChannel::Admit::Always: setAdmit(Admit::Always); break; case DMRChannel::Admit::Free: setAdmit(Admit::Free); break; case DMRChannel::Admit::ColorCode: setAdmit(Admit::DifferentColorCode); break; } // set color code setColorCode(dc->colorCode()); // set time-slot setTimeSlot(dc->timeSlot()); // link transmit contact if (nullptr == dc->txContactObj()) setContactIndex(0); else setContactIndex(ctx.index(dc->txContactObj())); // link RX group list if (nullptr == dc->groupListObj()) clearGroupListIndex(); else setGroupListIndex(ctx.index(dc->groupListObj())); // Set radio ID if ((nullptr == dc->radioIdObj()) || (DefaultRadioID::get() == dc->radioIdObj())) { if (nullptr == ctx.config()->settings()->defaultIdRef()->as()) { logWarn() << "No default radio ID set: using index 0."; setRadioIDIndex(0); } else { setRadioIDIndex(ctx.index(ctx.config()->settings()->defaultIdRef()->as())); } } else { setRadioIDIndex(ctx.index(dc->radioIdObj())); } // Handle extension if (AnytoneDMRChannelExtension *ext = dc->anytoneChannelExtension()) { // Apply common settings enableTalkaround(ext->talkaround()); // Apply DMR settings enableCallConfirm(ext->callConfirm()); enableSMSConfirm(ext->smsConfirm()); enableSimplexTDMA(ext->simplexTDMA()); enableAdaptiveTDMA(ext->adaptiveTDMA()); enableLoneWorker(ext->loneWorker()); } } return true; } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::ChannelBitmapElement * ********************************************************************************************* */ AnytoneCodeplug::ChannelBitmapElement::ChannelBitmapElement(uint8_t *ptr, size_t size) : BitmapElement(ptr, size) { // pass... } AnytoneCodeplug::ChannelBitmapElement::ChannelBitmapElement(uint8_t *ptr) : BitmapElement(ptr, ChannelBitmapElement::size()) { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::ContactElement * ********************************************************************************************* */ AnytoneCodeplug::ContactElement::ContactElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::ContactElement::ContactElement(uint8_t *ptr) : Element(ptr, ContactElement::size()) { // pass... } AnytoneCodeplug::ContactElement::~ContactElement() { // pass... } void AnytoneCodeplug::ContactElement::clear() { memset(_data, 0, _size); } bool AnytoneCodeplug::ContactElement::isValid() const { return !name().isEmpty(); } DMRContact::Type AnytoneCodeplug::ContactElement::type() const { switch (getUInt8(0x0000)) { case 0: return DMRContact::PrivateCall; case 1: return DMRContact::GroupCall; case 2: return DMRContact::AllCall; } return DMRContact::PrivateCall; } void AnytoneCodeplug::ContactElement::setType(DMRContact::Type type) { switch (type) { case DMRContact::PrivateCall: setUInt8(0x0000, 0); break; case DMRContact::GroupCall: setUInt8(0x0000, 1); break; case DMRContact::AllCall: setUInt8(0x0000, 2); break; } } QString AnytoneCodeplug::ContactElement::name() const { return readASCII(0x0001, 16, 0x00); } void AnytoneCodeplug::ContactElement::setName(const QString &name) { writeASCII(0x0001, name, 16, 0x00); } unsigned AnytoneCodeplug::ContactElement::number() const { return getBCD8_be(0x0023); } void AnytoneCodeplug::ContactElement::setNumber(unsigned number) { setBCD8_be(0x0023, number); } AnytoneContactExtension::AlertType AnytoneCodeplug::ContactElement::alertType() const { switch (getUInt8(0x0027)) { case (uint8_t)AnytoneContactExtension::AlertType::None: return AnytoneContactExtension::AlertType::None; case (uint8_t)AnytoneContactExtension::AlertType::Ring: return AnytoneContactExtension::AlertType::Ring; case (uint8_t)AnytoneContactExtension::AlertType::Online: return AnytoneContactExtension::AlertType::Online; default: break; } logWarn() << "Unknown value " << getUInt8(0x0027) << " for alert type of AnyTone contact element. Maybe the codeplug implementation is" " outdated. Consider reporting it at https://github.com/hmatuschek/qdmr."; return AnytoneContactExtension::AlertType::None; } void AnytoneCodeplug::ContactElement::setAlertType(AnytoneContactExtension::AlertType type) { setUInt8(0x0027, (unsigned)type); } DMRContact * AnytoneCodeplug::ContactElement::toContactObj(Context &ctx) const { Q_UNUSED(ctx); // Common settings DMRContact *cont = new DMRContact(); cont->setType(type()); cont->setName(name()); cont->setNumber(number()); cont->setRing(AnytoneContactExtension::AlertType::None != alertType()); // Create AnyTone specific extension AnytoneContactExtension *ext = new AnytoneContactExtension(); cont->setAnytoneExtension(ext); ext->setAlertType(alertType()); return cont; } bool AnytoneCodeplug::ContactElement::fromContactObj(const DMRContact *contact, Context &ctx) { Q_UNUSED(ctx) clear(); setType(contact->type()); setName(contact->name()); setNumber(contact->number()); setAlertType(contact->ring() ? AnytoneContactExtension::AlertType::Ring : AnytoneContactExtension::AlertType::None); if (AnytoneContactExtension *ext = contact->anytoneExtension()) { setAlertType(ext->alertType()); } return true; } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::ContactBitmapElement * ********************************************************************************************* */ AnytoneCodeplug::ContactBitmapElement::ContactBitmapElement(uint8_t *ptr, size_t size) : InvertedBitmapElement(ptr, size) { // pass... } AnytoneCodeplug::ContactBitmapElement::ContactBitmapElement(uint8_t *ptr) : InvertedBitmapElement(ptr, ContactBitmapElement::size()) { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::DTMFContactElement * ********************************************************************************************* */ AnytoneCodeplug::DTMFContactElement::DTMFContactElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::DTMFContactElement::DTMFContactElement(uint8_t *ptr) : Element(ptr, DTMFContactElement::size()) { // pass... } AnytoneCodeplug::DTMFContactElement::~DTMFContactElement() { // pass... } void AnytoneCodeplug::DTMFContactElement::clear() { memset(_data, 0, _size); } QString AnytoneCodeplug::DTMFContactElement::number() const { QString number; int n = getUInt8(Offset::numDigits()); for (int i=0; i>4)&0xf]); else number.append(_anytone_bin_dtmf_tab[(byte>>0)&0xf]); } return number; } void AnytoneCodeplug::DTMFContactElement::setNumber(const QString &number) { if (! validDTMFNumber(number)) return; memset(_data+Offset::digits(), 0, Limit::digitCount()/2); unsigned int n = std::min((unsigned int)number.length(), Limit::digitCount()); setUInt8(Offset::numDigits(), n); for (unsigned int i=0; inumber()); setName(contact->name()); return true; } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::DTMFContactBytemapElement * ********************************************************************************************* */ AnytoneCodeplug::DTMFContactBytemapElement::DTMFContactBytemapElement(uint8_t *ptr, size_t size) : InvertedBytemapElement(ptr, size) { // pass... } AnytoneCodeplug::DTMFContactBytemapElement::DTMFContactBytemapElement(uint8_t *ptr) : InvertedBytemapElement(ptr, DTMFContactBytemapElement::size()) { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::GroupListElement * ********************************************************************************************* */ AnytoneCodeplug::GroupListElement::GroupListElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::GroupListElement::GroupListElement(uint8_t *ptr) : Element(ptr, GroupListElement::size()) { // pass... } void AnytoneCodeplug::GroupListElement::clear() { memset(_data, 0x00, _size); // set member indices to 0xffffffff memset(_data, 0xff, 4*64); } bool AnytoneCodeplug::GroupListElement::isValid() const { return !name().isEmpty(); } QString AnytoneCodeplug::GroupListElement::name() const { return readASCII(0x0100, 16, 0x00); } void AnytoneCodeplug::GroupListElement::setName(const QString &name) { writeASCII(0x0100, name, 16, 0x00); } bool AnytoneCodeplug::GroupListElement::hasMemberIndex(unsigned n) const { return 0xffffffff != memberIndex(n); } unsigned AnytoneCodeplug::GroupListElement::memberIndex(unsigned n) const { return getUInt32_le(0x0000 + 4*n); } void AnytoneCodeplug::GroupListElement::setMemberIndex(unsigned n, unsigned idx) { setUInt32_le(0x0000 + 4*n, idx); } void AnytoneCodeplug::GroupListElement::clearMemberIndex(unsigned n) { setMemberIndex(n, 0xffffffff); } RXGroupList * AnytoneCodeplug::GroupListElement::toGroupListObj() const { return new RXGroupList(name()); } bool AnytoneCodeplug::GroupListElement::linkGroupList(RXGroupList *lst, Context &ctx) const { for (uint8_t i=0; i<64; i++) { // Disabled contact -> continue if (! hasMemberIndex(i)) continue; // Missing contact ignore. if (! ctx.has(memberIndex(i))) { logWarn() << "Cannot link contact " << memberIndex(i) << " to group list '" << this->name() << "': Invalid contact index. Ignored."; continue; } lst->addContact(ctx.get(memberIndex(i))); } return true; } bool AnytoneCodeplug::GroupListElement::fromGroupListObj(const RXGroupList *lst, Context &ctx) { clear(); // set name of group list setName(lst->name()); int j=0; // set members for (uint8_t i=0; i<64; i++) { // Skip non-private-call entries while((lst->count() > j) && (DMRContact::GroupCall != lst->contact(j)->type())) { logWarn() << "Contact '" << lst->contact(i)->name() << "' in group list '" << lst->name() << "' is not a group call. Skip entry."; j++; } if (lst->count() > j) { setMemberIndex(i, ctx.index(lst->contact(j))); j++; } else { clearMemberIndex(i); } } return true; } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::GroupListBitmapElement * ********************************************************************************************* */ AnytoneCodeplug::GroupListBitmapElement::GroupListBitmapElement(uint8_t *ptr, size_t size) : BitmapElement(ptr, size) { // pass... } AnytoneCodeplug::GroupListBitmapElement::GroupListBitmapElement(uint8_t *ptr) : BitmapElement(ptr, GroupListBitmapElement::size()) { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::ScanListElement * ********************************************************************************************* */ AnytoneCodeplug::ScanListElement::ScanListElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::ScanListElement::ScanListElement(uint8_t *ptr) : Element(ptr, ScanListElement::size()) { // pass... } void AnytoneCodeplug::ScanListElement::clear() { memset(_data, 0, _size); setPriorityChannels(PriChannel::Off); clearPrimaryChannel(); clearSecondaryChannel(); setLookBackTimeA(150); setLookBackTimeB(250); setDropOutDelay(290); setDwellTime(290); setRevertChannel(RevertChannel::Selected); // clear members memset(_data+0x0020, 0xff, 2*50); } AnytoneCodeplug::ScanListElement::PriChannel AnytoneCodeplug::ScanListElement::priorityChannels() const { return (PriChannel) getUInt8(0x0001); } void AnytoneCodeplug::ScanListElement::setPriorityChannels(PriChannel sel) { setUInt8(0x0001, (unsigned)sel); } bool AnytoneCodeplug::ScanListElement::hasPrimary() const { return 0xffff != getUInt16_le(0x0002); } bool AnytoneCodeplug::ScanListElement::primaryIsSelected() const { return 0 == getUInt16_le(0x0002); } unsigned AnytoneCodeplug::ScanListElement::primary() const { return ((unsigned)getUInt16_le(0x0002))-1; } void AnytoneCodeplug::ScanListElement::setPrimary(unsigned idx) { setUInt16_le(0x0002, idx+1); } void AnytoneCodeplug::ScanListElement::setPrimarySelected() { setUInt16_le(0x0002, 0); } void AnytoneCodeplug::ScanListElement::clearPrimaryChannel() { setUInt16_le(0x0002, 0xffff); } bool AnytoneCodeplug::ScanListElement::hasSecondary() const { return 0xffff != getUInt16_le(0x0004); } bool AnytoneCodeplug::ScanListElement::secondaryIsSelected() const { return 0 == getUInt16_le(0x0004); } unsigned AnytoneCodeplug::ScanListElement::secondary() const { return ((unsigned)getUInt16_le(0x0002))-1; } void AnytoneCodeplug::ScanListElement::setSecondary(unsigned idx) { setUInt16_le(0x0004, idx+1); } void AnytoneCodeplug::ScanListElement::setSecondarySelected() { setUInt16_le(0x0004, 0); } void AnytoneCodeplug::ScanListElement::clearSecondaryChannel() { setUInt16_le(0x0004, 0xffff); } unsigned AnytoneCodeplug::ScanListElement::lookBackTimeA() const { return ((unsigned)getUInt16_le(0x0006))*10; } void AnytoneCodeplug::ScanListElement::setLookBackTimeA(unsigned sec) { setUInt16_le(0x0006, sec/10); } unsigned AnytoneCodeplug::ScanListElement::lookBackTimeB() const { return ((unsigned)getUInt16_le(0x0008))*10; } void AnytoneCodeplug::ScanListElement::setLookBackTimeB(unsigned sec) { setUInt16_le(0x0008, sec/10); } unsigned AnytoneCodeplug::ScanListElement::dropOutDelay() const { return ((unsigned)getUInt16_le(0x000a))*10; } void AnytoneCodeplug::ScanListElement::setDropOutDelay(unsigned sec) { setUInt16_le(0x000a, sec/10); } unsigned AnytoneCodeplug::ScanListElement::dwellTime() const { return ((unsigned)getUInt16_le(0x000c))*10; } void AnytoneCodeplug::ScanListElement::setDwellTime(unsigned sec) { setUInt16_le(0x000c, sec/10); } AnytoneCodeplug::ScanListElement::RevertChannel AnytoneCodeplug::ScanListElement::revertChannel() const { return (RevertChannel)getUInt8(0x000e); } void AnytoneCodeplug::ScanListElement::setRevertChannel(RevertChannel type) { setUInt8(0x000e, (unsigned)type); } QString AnytoneCodeplug::ScanListElement::name() const { return readASCII(0x000f, 16, 0x00); } void AnytoneCodeplug::ScanListElement::setName(const QString &name) { writeASCII(0x000f, name, 16, 0x00); } bool AnytoneCodeplug::ScanListElement::hasMemberIndex(unsigned n) const { return 0xffff != memberIndex(n); } unsigned AnytoneCodeplug::ScanListElement::memberIndex(unsigned n) const { return getUInt16_le(0x0020+2*n); } void AnytoneCodeplug::ScanListElement::setMemberIndex(unsigned n, unsigned idx) { setUInt16_le(0x0020+2*n, idx); } void AnytoneCodeplug::ScanListElement::clearMemberIndex(unsigned n) { setMemberIndex(n, 0xffff); } ScanList * AnytoneCodeplug::ScanListElement::toScanListObj() const { return new ScanList(name()); } bool AnytoneCodeplug::ScanListElement::linkScanListObj(ScanList *lst, Context &ctx) const { if (((PriChannel::Both == priorityChannels()) || (PriChannel::Primary==priorityChannels())) && hasPrimary()) { if (primaryIsSelected()) lst->setPrimaryChannel(SelectedChannel::get()); else if (ctx.has(primary())) lst->setPrimaryChannel(ctx.get(primary())); } if (((PriChannel::Both == priorityChannels()) || (PriChannel::Secondary==priorityChannels())) && hasSecondary()) { if (secondaryIsSelected()) lst->setSecondaryChannel(SelectedChannel::get()); else if (ctx.has(secondary())) lst->setSecondaryChannel(ctx.get(secondary())); } for (uint16_t i=0; i<50; i++) { if (! hasMemberIndex(i)) continue; if (! ctx.has(memberIndex(i))) { logError() << "Cannot link scanlist '" << name() << "', channel index " << memberIndex(i) << " unknown."; continue; } lst->addChannel(ctx.get(memberIndex(i))); } return true; } bool AnytoneCodeplug::ScanListElement::fromScanListObj(ScanList *lst, Context &ctx) { clear(); setName(lst->name()); if (lst->primaryChannel() && lst->secondaryChannel()) { setPriorityChannels(PriChannel::Both); } else if (lst->primaryChannel()) { setPriorityChannels(PriChannel::Primary); } else if (lst->secondaryChannel()) { setPriorityChannels(PriChannel::Secondary); } if (lst->primaryChannel()) { if (SelectedChannel::get() == lst->primaryChannel()) setPrimarySelected(); else setPrimary(ctx.index(lst->primaryChannel())); } if (lst->secondaryChannel()) { if (SelectedChannel::get() == lst->secondaryChannel()) setSecondarySelected(); else setSecondary(ctx.index(lst->secondaryChannel())); } for (int i=0; icount()); i++) { if (SelectedChannel::get() == lst->channel(i)) continue; setMemberIndex(i, ctx.index(lst->channel(i))); } return false; } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::ScanListBitmapElement * ********************************************************************************************* */ AnytoneCodeplug::ScanListBitmapElement::ScanListBitmapElement(uint8_t *ptr, size_t size) : BitmapElement(ptr, size) { // pass... } AnytoneCodeplug::ScanListBitmapElement::ScanListBitmapElement(uint8_t *ptr) : BitmapElement(ptr, ScanListBitmapElement::size()) { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::RadioIDElement * ********************************************************************************************* */ AnytoneCodeplug::RadioIDElement::RadioIDElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::RadioIDElement::RadioIDElement(uint8_t *ptr) : Element(ptr, RadioIDElement::size()) { // pass... } void AnytoneCodeplug::RadioIDElement::clear() { memset(_data, 0x00, _size); } unsigned AnytoneCodeplug::RadioIDElement::number() const { return getBCD8_be(0x0000); } void AnytoneCodeplug::RadioIDElement::setNumber(unsigned number) { setBCD8_be(0x0000, number); } QString AnytoneCodeplug::RadioIDElement::name() const { return readASCII(0x0005, 16, 0x00); } void AnytoneCodeplug::RadioIDElement::setName(const QString &name) { writeASCII(0x0005, name, 16, 0x00); } DMRRadioID * AnytoneCodeplug::RadioIDElement::toRadioID() const { return new DMRRadioID(name(), number()); } bool AnytoneCodeplug::RadioIDElement::fromRadioID(DMRRadioID *id) { setName(id->name()); setNumber(id->number()); return true; } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::RadioIDBitmapElement * ********************************************************************************************* */ AnytoneCodeplug::RadioIDBitmapElement::RadioIDBitmapElement(uint8_t *ptr, size_t size) : BitmapElement(ptr, size) { // pass... } AnytoneCodeplug::RadioIDBitmapElement::RadioIDBitmapElement(uint8_t *ptr) : BitmapElement(ptr, RadioIDBitmapElement::size()) { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::GeneralSettingsElement * ********************************************************************************************* */ AnytoneCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } void AnytoneCodeplug::GeneralSettingsElement::clear() { memset(_data, 0, _size); } bool AnytoneCodeplug::GeneralSettingsElement::displayFrequency() const { return 0!=getUInt8(Offset::displayMode()); } void AnytoneCodeplug::GeneralSettingsElement::enableDisplayFrequency(bool enable) { setUInt8(Offset::displayMode(), (enable ? 0x01 : 0x00)); } bool AnytoneCodeplug::GeneralSettingsElement::autoKeyLock() const { return 0!=getUInt8(Offset::autoKeyLock()); } void AnytoneCodeplug::GeneralSettingsElement::enableAutoKeyLock(bool enable) { setUInt8(Offset::autoKeyLock(), (enable ? 0x01 : 0x00)); } Interval AnytoneCodeplug::GeneralSettingsElement::autoShutdownDelay() const { switch ((AutoShutdown) getUInt8(Offset::autoShutDown())) { case AutoShutdown::Off: return Interval::fromMinutes(0); case AutoShutdown::After10min: return Interval::fromMinutes(10); case AutoShutdown::After30min: return Interval::fromMinutes(30); case AutoShutdown::After60min: return Interval::fromMinutes(60); case AutoShutdown::After120min: return Interval::fromMinutes(120); } return Interval(); } void AnytoneCodeplug::GeneralSettingsElement::setAutoShutdownDelay(Interval intv) { if (0 == intv.minutes()) { setUInt8(Offset::autoShutDown(), (unsigned)AutoShutdown::Off); } else if (intv.minutes() <= 10) { setUInt8(Offset::autoShutDown(), (unsigned)AutoShutdown::After10min); } else if (intv.minutes() <= 30) { setUInt8(Offset::autoShutDown(), (unsigned)AutoShutdown::After30min); } else if (intv.minutes() <= 60) { setUInt8(Offset::autoShutDown(), (unsigned)AutoShutdown::After60min); } else { setUInt8(Offset::autoShutDown(), (unsigned)AutoShutdown::After120min); } } AnytoneBootSettingsExtension::BootDisplay AnytoneCodeplug::GeneralSettingsElement::bootDisplay() const { return (AnytoneBootSettingsExtension::BootDisplay) getUInt8(Offset::bootDisplay()); } void AnytoneCodeplug::GeneralSettingsElement::setBootDisplay(AnytoneBootSettingsExtension::BootDisplay mode) { setUInt8(Offset::bootDisplay(), (unsigned)mode); } bool AnytoneCodeplug::GeneralSettingsElement::bootPassword() const { return getUInt8(Offset::bootPassword()); } void AnytoneCodeplug::GeneralSettingsElement::enableBootPassword(bool enable) { setUInt8(Offset::bootPassword(), (enable ? 0x01 : 0x00)); } unsigned AnytoneCodeplug::GeneralSettingsElement::squelchLevelA() const { return getUInt8(Offset::squelchLevelA()); } void AnytoneCodeplug::GeneralSettingsElement::setSquelchLevelA(unsigned level) { setUInt8(Offset::squelchLevelA(), level); } unsigned AnytoneCodeplug::GeneralSettingsElement::squelchLevelB() const { return getUInt8(Offset::squelchLevelB()); } void AnytoneCodeplug::GeneralSettingsElement::setSquelchLevelB(unsigned level) { setUInt8(Offset::squelchLevelB(), level); } bool AnytoneCodeplug::GeneralSettingsElement::fromConfig(const Flags &flags, Context &ctx) { // Set microphone gain // For the 868UV, this setting sets both, FM and DMR mic gain. // For all other devices, there is an additional FM mic gain setting. setDMRMicGain(ctx.config()->settings()->micLevel()); // If auto-enable GPS is enabled if (flags.autoEnableGPS) { // Check if GPS is required -> enable if (ctx.config()->requiresGPS()) { enableGPS(true); // Set time zone based on system time zone. setGPSTimeZone(QTimeZone::systemTimeZone()); enableGetGPSPosition(false); } else { enableGPS(false); } } // Set default squelch level if (0 == ctx.config()->settings()->squelch()) { setSquelchLevelA(0); setSquelchLevelB(0); } else if (1 == ctx.config()->settings()->squelch()) { setSquelchLevelA(1); setSquelchLevelB(1); } else { setSquelchLevelA(ctx.config()->settings()->squelch()/2); setSquelchLevelB(ctx.config()->settings()->squelch()/2); } // Handle extensions if (AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension()) { setAutoShutdownDelay(ext->powerSaveSettings()->autoShutdown()); setVFOScanType(ext->vfoScanType()); enableVFOModeA(AnytoneSettingsExtension::VFOMode::VFO == ext->modeA()); enableVFOModeB(AnytoneSettingsExtension::VFOMode::VFO == ext->modeB()); if ((AnytoneSettingsExtension::VFOMode::VFO == ext->modeA()) || ext->zoneA()->isNull()) setMemoryZoneA(0); else setMemoryZoneA(ctx.index(ext->zoneA()->as())); if ((AnytoneSettingsExtension::VFOMode::VFO == ext->modeB()) || ext->zoneB()->isNull()) setMemoryZoneB(0); else setMemoryZoneB(ctx.index(ext->zoneB()->as())); enableActiveChannelB(AnytoneSettingsExtension::VFO::B == ext->selectedVFO()); enableSubChannel(ext->subChannelEnabled()); setMinVFOScanFrequencyUHF(ext->minVFOScanFrequencyUHF()); setMaxVFOScanFrequencyUHF(ext->maxVFOScanFrequencyUHF()); setMinVFOScanFrequencyVHF(ext->minVFOScanFrequencyVHF()); setMaxVFOScanFrequencyVHF(ext->maxVFOScanFrequencyVHF()); // Encode boot settings setBootDisplay(ext->bootSettings()->bootDisplay()); enableBootPassword(ext->bootSettings()->bootPasswordEnabled()); enableDefaultChannel( ext->bootSettings()->defaultChannelEnabled() && (! ext->bootSettings()->zoneA()->isNull()) && (! ext->bootSettings()->zoneB()->isNull())); if (defaultChannel()) { setDefaultZoneIndexA(ctx.index(ext->bootSettings()->zoneA()->as())); if (ext->bootSettings()->channelA()->isNull() || (! ext->bootSettings()->zoneA()->as()->A()->has( ext->bootSettings()->channelA()->as()))) setDefaultChannelAToVFO(); else setDefaultChannelAIndex(ext->bootSettings()->zoneA()->as()->A()->indexOf( ext->bootSettings()->channelA()->as())); setDefaultZoneIndexB(ctx.index(ext->bootSettings()->zoneB()->as())); if (ext->bootSettings()->channelB()->isNull() || (! ext->bootSettings()->zoneB()->as()->A()->has( ext->bootSettings()->channelB()->as()))) setDefaultChannelBToVFO(); else setDefaultChannelBIndex(ext->bootSettings()->zoneB()->as()->A()->indexOf( ext->bootSettings()->channelB()->as())); } // Encode key settings setFuncKeyAShort(ext->keySettings()->funcKeyAShort()); setFuncKeyALong(ext->keySettings()->funcKeyALong()); setFuncKeyBShort(ext->keySettings()->funcKeyBShort()); setFuncKeyBLong(ext->keySettings()->funcKeyBLong()); setFuncKeyCShort(ext->keySettings()->funcKeyCShort()); setFuncKeyCLong(ext->keySettings()->funcKeyCLong()); setFuncKey1Short(ext->keySettings()->funcKey1Short()); setFuncKey1Long(ext->keySettings()->funcKey1Long()); setFuncKey2Short(ext->keySettings()->funcKey2Short()); setFuncKey2Long(ext->keySettings()->funcKey2Long()); setLongPressDuration(ext->keySettings()->longPressDuration()); enableAutoKeyLock(ext->keySettings()->autoKeyLockEnabled()); // Encode tone settings enableKeyTone(ext->toneSettings()->keyToneEnabled()); enableSMSAlert(ext->toneSettings()->smsAlertEnabled()); enableCallAlert(ext->toneSettings()->callAlertEnabled()); enableDMRTalkPermit(ext->toneSettings()->talkPermitDigitalEnabled()); enableFMTalkPermit(ext->toneSettings()->talkPermitAnalogEnabled()); enableDMRResetTone(ext->toneSettings()->digitalResetToneEnabled()); enableIdleChannelTone(ext->toneSettings()->dmrIdleChannelToneEnabled()); enableStartupTone(ext->toneSettings()->startupToneEnabled()); setCallToneMelody(*(ext->toneSettings()->callMelody())); setIdleToneMelody(*(ext->toneSettings()->idleMelody())); setResetToneMelody(*(ext->toneSettings()->resetMelody())); // Encode display settings enableDisplayFrequency(ext->displaySettings()->displayFrequencyEnabled()); setBrightness(ext->displaySettings()->brightness()); enableCallEndPrompt(ext->displaySettings()->callEndPromptEnabled()); setLastCallerDisplayMode(ext->displaySettings()->lastCallerDisplay()); enableDisplayClock(ext->displaySettings()->showClockEnabled()); enableDisplayCall(ext->displaySettings()->showCallEnabled()); setCallDisplayColor(ext->displaySettings()->callColor()); enableVolumeChangePrompt(ext->displaySettings()->volumeChangePromptEnabled()); // Encode audio settings enableRecording(ext->audioSettings()->recordingEnabled()); enableEnhancedAudio(ext->audioSettings()->enhanceAudioEnabled()); setMaxSpeakerVolume(ext->audioSettings()->maxVolume()); // Encode menu settings setMenuExitTime(ext->menuSettings()->duration()); // Encode auto-repeater settings setAutoRepeaterDirectionA(ext->autoRepeaterSettings()->directionA()); setAutoRepeaterDirectionB(ext->autoRepeaterSettings()->directionB()); if (ext->autoRepeaterSettings()->vhfRef()->isNull()) clearAutoRepeaterOffsetFrequencyIndexVHF(); else setAutoRepeaterOffsetFrequenyIndexVHF( ctx.index(ext->autoRepeaterSettings()->vhfRef()->as())); if (ext->autoRepeaterSettings()->uhfRef()->isNull()) clearAutoRepeaterOffsetFrequencyIndexUHF(); else setAutoRepeaterOffsetFrequenyIndexUHF( ctx.index(ext->autoRepeaterSettings()->uhfRef()->as())); setAutoRepeaterMinFrequencyVHF(ext->autoRepeaterSettings()->vhfMin()); setAutoRepeaterMaxFrequencyVHF(ext->autoRepeaterSettings()->vhfMax()); setAutoRepeaterMinFrequencyUHF(ext->autoRepeaterSettings()->uhfMin()); setAutoRepeaterMaxFrequencyUHF(ext->autoRepeaterSettings()->uhfMax()); // Encode GPS Settings setGPSTimeZone(ext->gpsSettings()->timeZone()); enableGPSUnitsImperial(AnytoneGPSSettingsExtension::Units::Archaic == ext->gpsSettings()->units()); // Encode other settings enableKeepLastCaller(ext->keepLastCallerEnabled()); } else if (! flags.updateCodePlug) { clearAutoRepeaterOffsetFrequencyIndexVHF(); clearAutoRepeaterOffsetFrequencyIndexUHF(); } return true; } bool AnytoneCodeplug::GeneralSettingsElement::updateConfig(Context &ctx) { // get microphone gain ctx.config()->settings()->setMicLevel(dmrMicGain()); // D868UV does not support speech synthesis? ctx.config()->settings()->enableSpeech(false); ctx.config()->settings()->setSquelch(std::max(squelchLevelA(), squelchLevelB())*2); // Set extension AnytoneSettingsExtension *ext = nullptr; if (ctx.config()->settings()->anytoneExtension()) ext = ctx.config()->settings()->anytoneExtension(); else ctx.config()->settings()->setAnytoneExtension(ext = new AnytoneSettingsExtension()); ext->powerSaveSettings()->setAutoShutdown(autoShutdownDelay()); ext->setVFOScanType(vfoScanType()); ext->setModeA(vfoModeA() ? AnytoneSettingsExtension::VFOMode::VFO : AnytoneSettingsExtension::VFOMode::Memory); ext->setModeB(vfoModeB() ? AnytoneSettingsExtension::VFOMode::VFO : AnytoneSettingsExtension::VFOMode::Memory); if ((! vfoModeA()) && ctx.has(memoryZoneA())) ext->zoneA()->set(ctx.get(memoryZoneA())); if ((! vfoModeB()) && ctx.has(memoryZoneB())) ext->zoneB()->set(ctx.get(memoryZoneB())); ext->setSelectedVFO(activeChannelB() ? AnytoneSettingsExtension::VFO::B : AnytoneSettingsExtension::VFO::A); ext->enableSubChannel(subChannel()); ext->setMinVFOScanFrequencyUHF(this->minVFOScanFrequencyUHF()); ext->setMaxVFOScanFrequencyUHF(this->maxVFOScanFrequencyUHF()); ext->setMinVFOScanFrequencyVHF(this->minVFOScanFrequencyVHF()); ext->setMaxVFOScanFrequencyVHF(this->maxVFOScanFrequencyVHF()); // Store boot settings ext->bootSettings()->setBootDisplay(bootDisplay()); ext->bootSettings()->enableBootPassword(bootPassword()); ext->bootSettings()->enableDefaultChannel(this->defaultChannel()); // Store key settings ext->keySettings()->setFuncKeyAShort(funcKeyAShort()); ext->keySettings()->setFuncKeyALong(funcKeyALong()); ext->keySettings()->setFuncKeyBShort(funcKeyBShort()); ext->keySettings()->setFuncKeyBLong(funcKeyBLong()); ext->keySettings()->setFuncKeyCShort(funcKeyCShort()); ext->keySettings()->setFuncKeyCLong(funcKeyCLong()); ext->keySettings()->setFuncKey1Short(funcKey1Short()); ext->keySettings()->setFuncKey1Long(funcKey1Long()); ext->keySettings()->setFuncKey2Short(funcKey2Short()); ext->keySettings()->setFuncKey2Long(funcKey2Long()); ext->keySettings()->setLongPressDuration(longPressDuration()); ext->keySettings()->enableAutoKeyLock(autoKeyLock()); // Store tone settings ext->toneSettings()->enableKeyTone(this->keyToneEnabled()); ext->toneSettings()->enableSMSAlert(smsAlert()); ext->toneSettings()->enableCallAlert(callAlert()); ext->toneSettings()->enableTalkPermitDigital(this->dmrTalkPermit()); ext->toneSettings()->enableTalkPermitAnalog(this->fmTalkPermit()); ext->toneSettings()->enableDigitalResetTone(this->dmrResetTone()); ext->toneSettings()->enableDMRIdleChannelTone(this->idleChannelTone()); ext->toneSettings()->enableStartupTone(this->startupTone()); this->callToneMelody(*(ext->toneSettings()->callMelody())); this->idleToneMelody(*(ext->toneSettings()->idleMelody())); this->resetToneMelody(*(ext->toneSettings()->resetMelody())); // Store display settings ext->displaySettings()->enableDisplayFrequency(displayFrequency()); ext->displaySettings()->setBrightness(brightness()); ext->displaySettings()->enableVolumeChangePrompt(this->volumeChangePrompt()); ext->displaySettings()->enableCallEndPrompt(this->callEndPrompt()); ext->displaySettings()->setLastCallerDisplay(this->lastCallerDisplayMode()); ext->displaySettings()->enableShowClock(displayClock()); ext->displaySettings()->enableShowCall(displayCall()); ext->displaySettings()->setCallColor(this->callDisplayColor()); // Menu settings ext->menuSettings()->setDuration(this->menuExitTime()); // Store audio settings ext->audioSettings()->enableRecording(recording()); ext->audioSettings()->setMaxVolume(this->maxSpeakerVolume()); ext->audioSettings()->enableEnhanceAudio(this->enhanceAudio()); // Store auto-repeater settings ext->autoRepeaterSettings()->setDirectionA(this->autoRepeaterDirectionA()); ext->autoRepeaterSettings()->setDirectionB(autoRepeaterDirectionB()); ext->autoRepeaterSettings()->setVHFMin(this->autoRepeaterMinFrequencyVHF()); ext->autoRepeaterSettings()->setVHFMax(this->autoRepeaterMaxFrequencyVHF()); ext->autoRepeaterSettings()->setUHFMin(this->autoRepeaterMinFrequencyUHF()); ext->autoRepeaterSettings()->setUHFMax(this->autoRepeaterMaxFrequencyUHF()); // Store GPS settings ext->gpsSettings()->setUnits(this->gpsUnitsImperial() ? AnytoneGPSSettingsExtension::Units::Archaic : AnytoneGPSSettingsExtension::Units::Metric); ext->gpsSettings()->setTimeZone(gpsTimeZone()); // Other settings ext->enableKeepLastCaller(this->keepLastCaller()); return true; } bool AnytoneCodeplug::GeneralSettingsElement::linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err) { if (! settings->anytoneExtension()) return false; AnytoneSettingsExtension *ext = settings->anytoneExtension(); // Link boot settings if (this->defaultChannel()) { if (! ctx.has(this->defaultZoneIndexA())) { errMsg(err) << "Cannot link default zone A. Zone index " << this->defaultZoneIndexA() << " not defined."; return false; } Zone *zoneA = ctx.get(this->defaultZoneIndexA()); ext->bootSettings()->zoneA()->set(zoneA); if (this->defaultChannelAIsVFO()) { // pass... } else if (this->defaultChannelAIndex() >= (unsigned int)zoneA->A()->count()) { errMsg(err) << "Cannot link default channel A. Index " << this->defaultChannelAIndex() << " not defined."; return false; } else { ext->bootSettings()->channelA()->set( zoneA->A()->get(this->defaultChannelAIndex())->as()); } if (! ctx.has(this->defaultZoneIndexB())) { errMsg(err) << "Cannot link default zone B. Zone index " << this->defaultZoneIndexB() << " not defined."; return false; } Zone *zoneB = ctx.get(this->defaultZoneIndexB()); ext->bootSettings()->zoneB()->set(zoneB); if (this->defaultChannelBIsVFO()) { // pass... } else if (this->defaultChannelBIndex() >= (unsigned int)zoneB->A()->count()) { errMsg(err) << "Cannot link default channel B. Index " << this->defaultChannelBIndex() << " not defined."; return false; } else { ext->bootSettings()->channelB()->set( zoneB->A()->get(this->defaultChannelBIndex())->as()); } } // Link repeater offsets if (this->hasAutoRepeaterOffsetFrequencyIndexVHF()) { if (! ctx.has(this->autoRepeaterOffsetFrequencyIndexVHF())) { errMsg(err) << "Cannot link auto-repeater offset for VHF, index " << this->autoRepeaterOffsetFrequencyIndexVHF() << " not defined."; return false; } ext->autoRepeaterSettings()->vhfRef()->set( ctx.get(this->autoRepeaterOffsetFrequencyIndexVHF())); } if (this->hasAutoRepeaterOffsetFrequencyIndexUHF()) { if (! ctx.has(this->autoRepeaterOffsetFrequencyIndexUHF())) { errMsg(err) << "Cannot link auto-repeater offset for UHF, index " << this->autoRepeaterOffsetFrequencyIndexUHF() << " not defined."; return false; } ext->autoRepeaterSettings()->uhfRef()->set( ctx.get(this->autoRepeaterOffsetFrequencyIndexUHF())); } // Link auto-repeater if (hasAutoRepeaterOffsetFrequencyIndexVHF()) { if (! ctx.has(this->autoRepeaterOffsetFrequencyIndexVHF())) { errMsg(err) << "Cannot link auto-repeater offset frequency for VHF, index " << this->autoRepeaterOffsetFrequencyIndexVHF() << " not defined."; return false; } ext->autoRepeaterSettings()->vhfRef()->set( ctx.get(this->autoRepeaterOffsetFrequencyIndexVHF())); } if (hasAutoRepeaterOffsetFrequencyIndexUHF()) { if (! ctx.has(this->autoRepeaterOffsetFrequencyIndexUHF())) { errMsg(err) << "Cannot link auto-repeater offset frequency for UHF, index " << this->autoRepeaterOffsetFrequencyIndexUHF() << " not defined."; return false; } ext->autoRepeaterSettings()->uhfRef()->set( ctx.get(this->autoRepeaterOffsetFrequencyIndexUHF())); } return true; } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::ExtendedSettingsElement * ********************************************************************************************* */ AnytoneCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } bool AnytoneCodeplug::ExtendedSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(err); if (! flags.updateCodePlug) this->clear(); if (nullptr == ctx.config()->settings()->anytoneExtension()) { // If there is no extension, done return true; } // Get extension AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); // Encode DMR settings enableSendTalkerAlias(ext->dmrSettings()->sendTalkerAlias()); setTalkerAliasSource(ext->dmrSettings()->talkerAliasSource()); setTalkerAliasEncoding(ext->dmrSettings()->talkerAliasEncoding()); // Encode display settings setChannelBNameColor(ext->displaySettings()->channelBNameColor()); setZoneANameColor(ext->displaySettings()->zoneNameColor()); setZoneBNameColor(ext->displaySettings()->zoneBNameColor()); return true; } bool AnytoneCodeplug::ExtendedSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) { Q_UNUSED(err); // Get or add extension if not present AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); if (nullptr == ext) { ext = new AnytoneSettingsExtension(); ctx.config()->settings()->setAnytoneExtension(ext); } // Store DMR settings ext->dmrSettings()->enableSendTalkerAlias(sendTalkerAlias()); ext->dmrSettings()->setTalkerAliasSource(talkerAliasSource()); ext->dmrSettings()->setTalkerAliasEncoding(talkerAliasEncoding()); // Store display settings ext->displaySettings()->setChannelBNameColor(channelBNameColor()); ext->displaySettings()->setZoneNameColor(zoneANameColor()); ext->displaySettings()->setZoneBNameColor(zoneBNameColor()); return true; } bool AnytoneCodeplug::ExtendedSettingsElement::linkConfig(Context &ctx, const ErrorStack &err) { // Get or add extension if not present AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); if (nullptr == ext) { errMsg(err) << "Cannot link config extension: not set."; return false; } return true; } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::ZoneChannelListElement * ********************************************************************************************* */ AnytoneCodeplug::ZoneChannelListElement::ZoneChannelListElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pas... } AnytoneCodeplug::ZoneChannelListElement::ZoneChannelListElement(uint8_t *ptr) : Element(ptr, ZoneChannelListElement::size()) { // pass... } void AnytoneCodeplug::ZoneChannelListElement::clear() { memset(_data, 0x00, _size); memset(_data, 0xff, 2*250); memset(_data+0x200, 0xff, 2*250); } bool AnytoneCodeplug::ZoneChannelListElement::hasChannelA(unsigned n) const { return 0xffff == channelIndexA(n); } unsigned AnytoneCodeplug::ZoneChannelListElement::channelIndexA(unsigned n) const { return getUInt16_le(0x0000 + 2*n); } void AnytoneCodeplug::ZoneChannelListElement::setChannelIndexA(unsigned n, unsigned idx) { setUInt16_le(0x0000 + 2*n, idx); } void AnytoneCodeplug::ZoneChannelListElement::clearChannelIndexA(unsigned n) { setChannelIndexA(n, 0xffff); } bool AnytoneCodeplug::ZoneChannelListElement::hasChannelB(unsigned n) const { return 0xffff == channelIndexB(n); } unsigned AnytoneCodeplug::ZoneChannelListElement::channelIndexB(unsigned n) const { return getUInt16_le(0x0200 + 2*n); } void AnytoneCodeplug::ZoneChannelListElement::setChannelIndexB(unsigned n, unsigned idx) { setUInt16_le(0x0200 + 2*n, idx); } void AnytoneCodeplug::ZoneChannelListElement::clearChannelIndexB(unsigned n) { setChannelIndexB(n, 0xffff); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::ZoneBitmapElement * ********************************************************************************************* */ AnytoneCodeplug::ZoneBitmapElement::ZoneBitmapElement(uint8_t *ptr, size_t size) : BitmapElement(ptr, size) { // pass... } AnytoneCodeplug::ZoneBitmapElement::ZoneBitmapElement(uint8_t *ptr) : BitmapElement(ptr, ZoneBitmapElement::size()) { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::BootSettingsElement * ********************************************************************************************* */ AnytoneCodeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr) : Element(ptr, BootSettingsElement::size()) { // pass... } void AnytoneCodeplug::BootSettingsElement::clear() { memset(_data, 0x00, _size); } QString AnytoneCodeplug::BootSettingsElement::introLine1() const { return readASCII(0x0000, 16, 0x00); } void AnytoneCodeplug::BootSettingsElement::setIntroLine1(const QString &txt) { writeASCII(0x0000, txt, 16, 0x00); } QString AnytoneCodeplug::BootSettingsElement::introLine2() const { return readASCII(0x0010, 16, 0x00); } void AnytoneCodeplug::BootSettingsElement::setIntroLine2(const QString &txt) { writeASCII(0x0010, txt, 16, 0x00); } QString AnytoneCodeplug::BootSettingsElement::password() const { return readASCII(0x0020, 8, 0x00); } void AnytoneCodeplug::BootSettingsElement::setPassword(const QString &txt) { QRegularExpression pattern("[0-9]{0,8}"); if (pattern.match(txt).isValid()) writeASCII(0x0020, txt, 8, 0x00); } bool AnytoneCodeplug::BootSettingsElement::fromConfig(const Flags &flags, Context &ctx) { Q_UNUSED(flags) setIntroLine1(ctx.config()->settings()->introLine1()); setIntroLine2(ctx.config()->settings()->introLine2()); // Handle extensions if (ctx.config()->settings()->anytoneExtension()) { AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); setPassword(ext->bootSettings()->bootPassword()); } return true; } bool AnytoneCodeplug::BootSettingsElement::updateConfig(Context &ctx) { ctx.config()->settings()->setIntroLine1(introLine1()); ctx.config()->settings()->setIntroLine2(introLine2()); // Create/update extension AnytoneSettingsExtension *ext = nullptr; if (ctx.config()->settings()->anytoneExtension()) ext = ctx.config()->settings()->anytoneExtension(); else ctx.config()->settings()->setAnytoneExtension(ext = new AnytoneSettingsExtension()); ext->bootSettings()->setBootPassword(password()); return true; } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::DMRAPRSSettingsElement * ********************************************************************************************* */ AnytoneCodeplug::DMRAPRSSettingsElement::DMRAPRSSettingsElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::DMRAPRSSettingsElement::DMRAPRSSettingsElement(uint8_t *ptr) : Element(ptr, DMRAPRSSettingsElement::size()) { // pass... } void AnytoneCodeplug::DMRAPRSSettingsElement::clear() { memset(_data, 0x00, _size); } unsigned AnytoneCodeplug::DMRAPRSSettingsElement::manualInterval() const { return getUInt8(0x0000); } void AnytoneCodeplug::DMRAPRSSettingsElement::setManualInterval(unsigned sec) { setUInt8(0x0000, sec); } bool AnytoneCodeplug::DMRAPRSSettingsElement::automatic() const { return 0!=getUInt8(0x0001); } unsigned AnytoneCodeplug::DMRAPRSSettingsElement::automaticInterval() const { return 45 + 15*((unsigned)getUInt8(0x0001)); } void AnytoneCodeplug::DMRAPRSSettingsElement::setAutomaticInterval(unsigned sec) { if (sec<60) sec = 60; setUInt8(0x0001, (sec-45)/15); } void AnytoneCodeplug::DMRAPRSSettingsElement::disableAutomatic() { setUInt8(0x0001, 0x00); } bool AnytoneCodeplug::DMRAPRSSettingsElement::fixedLocation() const { return getUInt8(0x0002); } QGeoCoordinate AnytoneCodeplug::DMRAPRSSettingsElement::location() const { double latitude = getUInt8(0x0003) + double(getUInt8(0x0004))/60 + double(getUInt8(0x0005))/3600; if (getUInt8(0x0006)) latitude *= -1; double longitude = getUInt8(0x0007) + double(getUInt8(0x0008))/60 + double(getUInt8(0x0009))/3600; if (getUInt8(0x000a)) longitude *= -1; return QGeoCoordinate(latitude, longitude); } void AnytoneCodeplug::DMRAPRSSettingsElement::setLocation(const QGeoCoordinate &pos) { double latitude = pos.latitude(); bool south = (0 > latitude); latitude = std::abs(latitude); unsigned lat_deg = int(latitude); latitude -= lat_deg; latitude *= 60; unsigned lat_min = int(latitude); latitude -= lat_min; latitude *= 60; unsigned lat_sec = int(latitude); double longitude = pos.longitude(); bool west = (0 > longitude); longitude = std::abs(longitude); unsigned lon_deg = int(longitude); longitude -= lon_deg; longitude *= 60; unsigned lon_min = int(longitude); longitude -= lon_min; longitude *= 60; unsigned lon_sec = int(longitude); setUInt8(0x0003, lat_deg); setUInt8(0x0004, lat_min); setUInt8(0x0005, lat_sec); setUInt8(0x0006, (south ? 0x01 : 0x00)); setUInt8(0x0007, lon_deg); setUInt8(0x0008, lon_min); setUInt8(0x0009, lon_sec); setUInt8(0x000a, (west ? 0x01 : 0x00)); } void AnytoneCodeplug::DMRAPRSSettingsElement::enableFixedLocation(bool enable) { setUInt8(0x0002, (enable ? 0x01 : 0x00)); } Channel::Power AnytoneCodeplug::DMRAPRSSettingsElement::power() const { switch (getUInt8(0x000b)) { case 0x00: return Channel::Power::Low; case 0x01: return Channel::Power::Mid; case 0x02: return Channel::Power::High; case 0x03: return Channel::Power::Max; } return Channel::Power::Low; } void AnytoneCodeplug::DMRAPRSSettingsElement::setPower(Channel::Power power) { switch (power) { case Channel::Power::Min: case Channel::Power::Low: setUInt8(0x000b, 0x00); break; case Channel::Power::Mid: setUInt8(0x000b, 0x01); break; case Channel::Power::High: setUInt8(0x000b, 0x02); break; case Channel::Power::Max: setUInt8(0x000b, 0x03); break; } } bool AnytoneCodeplug::DMRAPRSSettingsElement::hasChannel(unsigned n) const { return 0xffff != channelIndex(n); } bool AnytoneCodeplug::DMRAPRSSettingsElement::channelIsVFOA(unsigned n) const { return 0x0fa0 == channelIndex(n); } bool AnytoneCodeplug::DMRAPRSSettingsElement::channelIsVFOB(unsigned n) const { return 0x0fa1 == channelIndex(n); } bool AnytoneCodeplug::DMRAPRSSettingsElement::channelIsSelected(unsigned n) const { return 0x0fa2 == channelIndex(n); } unsigned AnytoneCodeplug::DMRAPRSSettingsElement::channelIndex(unsigned n) const { return getUInt16_le(0x000c + 2*n); } void AnytoneCodeplug::DMRAPRSSettingsElement::setChannelIndex(unsigned n, unsigned idx) { setUInt16_le(0x000c + 2*n, idx); } void AnytoneCodeplug::DMRAPRSSettingsElement::setChannelVFOA(unsigned n) { setChannelIndex(n, 0x0fa0); } void AnytoneCodeplug::DMRAPRSSettingsElement::setChannelVFOB(unsigned n) { setChannelIndex(n, 0x0fa1); } void AnytoneCodeplug::DMRAPRSSettingsElement::setChannelSelected(unsigned n) { setChannelIndex(n, 0x0fa2); } void AnytoneCodeplug::DMRAPRSSettingsElement::clearChannel(unsigned n) { setChannelIndex(n, 0xffff); } unsigned AnytoneCodeplug::DMRAPRSSettingsElement::destination() const { return getBCD8_be(0x001c); } void AnytoneCodeplug::DMRAPRSSettingsElement::setDestination(unsigned id) { setBCD8_be(0x001c, id); } DMRContact::Type AnytoneCodeplug::DMRAPRSSettingsElement::callType() const { switch (getUInt8(0x0020)) { case 0x00: return DMRContact::PrivateCall; case 0x01: return DMRContact::GroupCall; case 0x02: return DMRContact::AllCall; } return DMRContact::PrivateCall; } void AnytoneCodeplug::DMRAPRSSettingsElement::setCallType(DMRContact::Type type) { switch (type) { case DMRContact::PrivateCall: setUInt8(0x0020, 0x00); break; case DMRContact::GroupCall: setUInt8(0x0020, 0x01); break; case DMRContact::AllCall: setUInt8(0x0020, 0x02); break; } } bool AnytoneCodeplug::DMRAPRSSettingsElement::timeSlotOverride() const { return 0 != getUInt8(0x0021); } DMRChannel::TimeSlot AnytoneCodeplug::DMRAPRSSettingsElement::timeslot() const { if (1 == getUInt8(0x0021)) return DMRChannel::TimeSlot::TS1; else if (2 == getUInt8(0x0021)) return DMRChannel::TimeSlot::TS2; return DMRChannel::TimeSlot::TS1; } void AnytoneCodeplug::DMRAPRSSettingsElement::overrideTimeSlot(DMRChannel::TimeSlot ts) { if (DMRChannel::TimeSlot::TS1 == ts) setUInt8(0x0021, 0x01); else setUInt8(0x0021, 0x02); } void AnytoneCodeplug::DMRAPRSSettingsElement::disableTimeSlotOverride() { setUInt8(0x0021, 0x00); } bool AnytoneCodeplug::DMRAPRSSettingsElement::fromConfig(const Flags &flags, Context &ctx) { Q_UNUSED(flags) if (1 < ctx.config()->posSystems()->gpsCount()) { logDebug() << "D868UV only supports a single independent GPS positioning system."; } else if (0 == ctx.config()->posSystems()->gpsCount()) { return true; } GPSSystem *sys = ctx.config()->posSystems()->gpsSystem(0); setDestination(sys->contactObj()->number()); setCallType(sys->contactObj()->type()); setManualInterval(sys->period()); setAutomaticInterval(sys->period()); disableTimeSlotOverride(); if (! sys->hasRevertChannel()) { setChannelSelected(0); } else if (sys->revert()->is()) { setChannelIndex(0, ctx.index(sys->revertChannel())); } else { clearChannel(0); } return true; } bool AnytoneCodeplug::DMRAPRSSettingsElement::createGPSSystem(uint8_t i, Context &ctx) { GPSSystem *sys = new GPSSystem(QString("GPS sys %1").arg(i+1), nullptr, nullptr, automaticInterval()); ctx.config()->posSystems()->add(sys); ctx.add(sys, i); return true; } bool AnytoneCodeplug::DMRAPRSSettingsElement::linkGPSSystem(uint8_t i, Context &ctx) { DMRContact *cont = nullptr; // Find matching contact, if not found -> create one. if (nullptr == (cont = ctx.config()->contacts()->findDigitalContact(destination()))) { cont = new DMRContact(callType(), QString("GPS target"), destination()); ctx.config()->contacts()->add(cont); } ctx.get(i)->setContactObj(cont); // Check if there is a revert channel set if ((! channelIsSelected(i)) && (ctx.has(channelIndex(i))) && (ctx.get(channelIndex(i)))->is()) { DMRChannel *ch = ctx.get(channelIndex(i))->as(); ctx.get(i)->setRevertChannel(ch); } else { ctx.get(i)->resetRevertChannel(); } return true; } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::DMRAPRSMessageElement * ********************************************************************************************* */ AnytoneCodeplug::DMRAPRSMessageElement::DMRAPRSMessageElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } AnytoneCodeplug::DMRAPRSMessageElement::DMRAPRSMessageElement(uint8_t *ptr) : Element(ptr, DMRAPRSMessageElement::size()) { // pass... } void AnytoneCodeplug::DMRAPRSMessageElement::clear() { memset(_data, 0x00, _size); } QString AnytoneCodeplug::DMRAPRSMessageElement::message() const { return readASCII(Offset::message(), Limit::length(), 0x00); } void AnytoneCodeplug::DMRAPRSMessageElement::setMessage(const QString &message) { writeASCII(Offset::message(), message, Limit::length(), 0x00); } bool AnytoneCodeplug::DMRAPRSMessageElement::fromConfig(Codeplug::Flags flags, Context &ctx) { Q_UNUSED(flags); Q_UNUSED(ctx) return true; } bool AnytoneCodeplug::DMRAPRSMessageElement::updateConfig(Context &ctx) const { Q_UNUSED(ctx) return true; } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::RepeaterOffsetListElement * ********************************************************************************************* */ AnytoneCodeplug::RepeaterOffsetListElement::RepeaterOffsetListElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } AnytoneCodeplug::RepeaterOffsetListElement::RepeaterOffsetListElement(uint8_t *ptr) : Element(ptr, RepeaterOffsetListElement::size()) { // pass... } void AnytoneCodeplug::RepeaterOffsetListElement::clear() { memset(_data, 0x00, _size); for (unsigned int i=0; i= Limit::numEntries()) return false; return 0 != getUInt32_le(Offset::frequencies() + n * Offset::betweenFrequencies()); } Frequency AnytoneCodeplug::RepeaterOffsetListElement::offset(unsigned int n) const { if (n >= Limit::numEntries()) return Frequency::fromHz(0); return Frequency::fromHz( ((unsigned long long)getUInt32_le(Offset::frequencies() + n * Offset::betweenFrequencies()))*10); } void AnytoneCodeplug::RepeaterOffsetListElement::setOffset(unsigned int n, Frequency freq) { if (n >= Limit::numEntries()) return; setUInt32_le(Offset::frequencies() + n*Offset::betweenFrequencies(), freq.inHz()/10); } void AnytoneCodeplug::RepeaterOffsetListElement::clearOffset(unsigned int n) { if (n >= Limit::numEntries()) return; setUInt32_le(Offset::frequencies() + n*Offset::betweenFrequencies(), 0); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::MessageListElement * ********************************************************************************************* */ AnytoneCodeplug::MessageListElement::MessageListElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::MessageListElement::MessageListElement(uint8_t *ptr) : Element(ptr, MessageListElement::size()) { // pass... } void AnytoneCodeplug::MessageListElement::clear() { memset(_data, 0x00, _size); clearNext(); clearIndex(); } bool AnytoneCodeplug::MessageListElement::hasNext() const { return 0xff != next(); } unsigned AnytoneCodeplug::MessageListElement::next() const { return getUInt8(0x0002); } void AnytoneCodeplug::MessageListElement::setNext(unsigned idx) { setUInt8(0x0002, idx); } void AnytoneCodeplug::MessageListElement::clearNext() { setNext(0xff); } bool AnytoneCodeplug::MessageListElement::hasIndex() const { return 0xff != index(); } unsigned AnytoneCodeplug::MessageListElement::index() const { return getUInt8(0x0003); } void AnytoneCodeplug::MessageListElement::setIndex(unsigned idx) { setUInt8(0x0003, idx); } void AnytoneCodeplug::MessageListElement::clearIndex() { setIndex(0xff); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::MessageElement * ********************************************************************************************* */ AnytoneCodeplug::MessageElement::MessageElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::MessageElement::MessageElement(uint8_t *ptr) : Element(ptr, MessageElement::size()) { // pass... } void AnytoneCodeplug::MessageElement::clear() { memset(_data, 0x00, _size); } QString AnytoneCodeplug::MessageElement::message() const { return readASCII(0x0000, 99, 0x00); } void AnytoneCodeplug::MessageElement::setMessage(const QString &msg) { writeASCII(0x0000, msg, 99, 0x00); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::MessageBytemapElement * ********************************************************************************************* */ AnytoneCodeplug::MessageBytemapElement::MessageBytemapElement(uint8_t *ptr, size_t size) : InvertedBytemapElement(ptr, size) { // pass... } AnytoneCodeplug::MessageBytemapElement::MessageBytemapElement(uint8_t *ptr) : InvertedBytemapElement(ptr, MessageBytemapElement::size()) { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::AnalogQuickCallElement * ********************************************************************************************* */ AnytoneCodeplug::AnalogQuickCallElement::AnalogQuickCallElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::AnalogQuickCallElement::AnalogQuickCallElement(uint8_t *ptr) : Element(ptr, AnalogQuickCallElement::size()) { // pass... } void AnytoneCodeplug::AnalogQuickCallElement::clear() { memset(_data, 0x00, _size); clearContactIndex(); } AnytoneCodeplug::AnalogQuickCallElement::Type AnytoneCodeplug::AnalogQuickCallElement::type() const { return (Type)getUInt8(0x0000); } void AnytoneCodeplug::AnalogQuickCallElement::setType(Type type) { setUInt8(0x0000, (unsigned)type); } bool AnytoneCodeplug::AnalogQuickCallElement::hasContactIndex() const { return 0xff != contactIndex(); } unsigned AnytoneCodeplug::AnalogQuickCallElement::contactIndex() const { return getUInt8(0x0001); } void AnytoneCodeplug::AnalogQuickCallElement::setContactIndex(unsigned idx) { setUInt8(0x0001, idx); } void AnytoneCodeplug::AnalogQuickCallElement::clearContactIndex() { setContactIndex(0xff); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::AnalogQuickCallsElement * ********************************************************************************************* */ AnytoneCodeplug::AnalogQuickCallsElement::AnalogQuickCallsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } AnytoneCodeplug::AnalogQuickCallsElement::AnalogQuickCallsElement(uint8_t *ptr) : Element(ptr, AnalogQuickCallsElement::size()) { // pass... } void AnytoneCodeplug::AnalogQuickCallsElement::clear() { memset(_data, 0x00, _size); for (unsigned int i=0; i= Limit::numMessages()) return; writeASCII(Offset::messages()+n*Offset::betweenMessages(), msg, Limit::messageLength(), 0x00); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::StatusMessageBitmapElement * ********************************************************************************************* */ AnytoneCodeplug::StatusMessageBitmapElement::StatusMessageBitmapElement(uint8_t *ptr, size_t size) : BitmapElement(ptr, size) { // pass... } AnytoneCodeplug::StatusMessageBitmapElement::StatusMessageBitmapElement(uint8_t *ptr) : BitmapElement(ptr, StatusMessageBitmapElement::size()) { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::HotKeyElement * ********************************************************************************************* */ AnytoneCodeplug::HotKeyElement::HotKeyElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::HotKeyElement::HotKeyElement(uint8_t *ptr) : Element(ptr, HotKeyElement::size()) { // pass... } void AnytoneCodeplug::HotKeyElement::clear() { memset(_data, 0x00, _size); clearContactIndex(); clearMessageIndex(); } AnytoneCodeplug::HotKeyElement::Type AnytoneCodeplug::HotKeyElement::type() const { return (Type)getUInt8(0x0000); } void AnytoneCodeplug::HotKeyElement::setType(Type type) { setUInt8(0x0000, (unsigned)type); } AnytoneCodeplug::HotKeyElement::MenuItem AnytoneCodeplug::HotKeyElement::menuItem() const { return (MenuItem) getUInt8(0x0001); } void AnytoneCodeplug::HotKeyElement::setMenuItem(MenuItem item) { setUInt8(0x0001, (unsigned)item); } AnytoneCodeplug::HotKeyElement::CallType AnytoneCodeplug::HotKeyElement::callType() const { return (CallType)getUInt8(0x0002); } void AnytoneCodeplug::HotKeyElement::setCallType(CallType type) { setUInt8(0x0002, (unsigned)type); } AnytoneCodeplug::HotKeyElement::DigiCallType AnytoneCodeplug::HotKeyElement::digiCallType() const { return (DigiCallType)getUInt8(0x0003); } void AnytoneCodeplug::HotKeyElement::setDigiCallType(DigiCallType type) { setUInt8(0x0003, (unsigned)type); } bool AnytoneCodeplug::HotKeyElement::hasContactIndex() const { return 0xffffffff != contactIndex(); } unsigned AnytoneCodeplug::HotKeyElement::contactIndex() const { return getUInt32_le(0x0004); } void AnytoneCodeplug::HotKeyElement::setContactIndex(unsigned idx) { setUInt32_le(0x0004, idx); } void AnytoneCodeplug::HotKeyElement::clearContactIndex() { setContactIndex(0xffffffff); } bool AnytoneCodeplug::HotKeyElement::hasMessageIndex() const { return 0xff != messageIndex(); } unsigned AnytoneCodeplug::HotKeyElement::messageIndex() const { return getUInt8(0x0008); } void AnytoneCodeplug::HotKeyElement::setMessageIndex(unsigned idx) { setUInt8(0x0008, idx); } void AnytoneCodeplug::HotKeyElement::clearMessageIndex() { setMessageIndex(0xff); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::HotKeySettingsElement * ********************************************************************************************* */ AnytoneCodeplug::HotKeySettingsElement::HotKeySettingsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } AnytoneCodeplug::HotKeySettingsElement::HotKeySettingsElement(uint8_t *ptr) : Element(ptr, HotKeySettingsElement::size()) { // pass... } void AnytoneCodeplug::HotKeySettingsElement::clear() { memset(_data, 0x00, _size); for (unsigned int i=0; i min) min = 1; setUInt8(0x0008, min-1); } unsigned AnytoneCodeplug::AlarmSettingElement::DigitalAlarm::areaBroadcastDuration() const { return getUInt8(0x0009)+1; } void AnytoneCodeplug::AlarmSettingElement::DigitalAlarm::setAreaBroadcastDuration(unsigned min) { if (1 > min) min = 1; setUInt8(0x0009, min-1); } bool AnytoneCodeplug::AlarmSettingElement::DigitalAlarm::vox() const { return getUInt8(0x000a); } void AnytoneCodeplug::AlarmSettingElement::DigitalAlarm::enableVOX(bool enable) { setUInt8(0x000a, (enable ? 0x01 : 0x00)); } bool AnytoneCodeplug::AlarmSettingElement::DigitalAlarm::rxAlarm() const { return getUInt8(0x000b); } void AnytoneCodeplug::AlarmSettingElement::DigitalAlarm::enableRXAlarm(bool enable) { setUInt8(0x000b, (enable ? 0x01 : 0x00)); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::AlarmSettingElement * ********************************************************************************************* */ AnytoneCodeplug::AlarmSettingElement::AlarmSettingElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::AlarmSettingElement::AlarmSettingElement(uint8_t *ptr) : Element(ptr, AlarmSettingElement::size()) { // pass... } void AnytoneCodeplug::AlarmSettingElement::clear() { AnalogAlarm(analog()).clear(); DigitalAlarm(digital()).clear(); } uint8_t * AnytoneCodeplug::AlarmSettingElement::analog() const { return _data + Offset::analog(); } uint8_t * AnytoneCodeplug::AlarmSettingElement::digital() const { return _data + Offset::digital(); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::DigitalAlarmExtensionElement * ********************************************************************************************* */ AnytoneCodeplug::DigitalAlarmExtensionElement::DigitalAlarmExtensionElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::DigitalAlarmExtensionElement::DigitalAlarmExtensionElement(uint8_t *ptr) : Element(ptr, DigitalAlarmExtensionElement::size()) { // pass... } void AnytoneCodeplug::DigitalAlarmExtensionElement::clear() { memset(_data, 0x00, _size); } DMRContact::Type AnytoneCodeplug::DigitalAlarmExtensionElement::callType() const { switch (getUInt8(Offset::callType())) { case 0x00: return DMRContact::PrivateCall; case 0x01: return DMRContact::GroupCall; case 0x02: return DMRContact::AllCall; } return DMRContact::PrivateCall; } void AnytoneCodeplug::DigitalAlarmExtensionElement::setCallType(DMRContact::Type type) { switch (type) { case DMRContact::PrivateCall: setUInt8(Offset::callType(), 0x00); break; case DMRContact::GroupCall: setUInt8(Offset::callType(), 0x01); break; case DMRContact::AllCall: setUInt8(Offset::callType(), 0x02); break; } } unsigned AnytoneCodeplug::DigitalAlarmExtensionElement::destination() const { return getBCD8_be(Offset::destination()); } void AnytoneCodeplug::DigitalAlarmExtensionElement::setDestination(unsigned number) { setBCD8_be(Offset::destination(), number); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::FiveToneIDElement * ********************************************************************************************* */ AnytoneCodeplug::FiveToneIDElement::FiveToneIDElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::FiveToneIDElement::FiveToneIDElement(uint8_t *ptr) : Element(ptr, FiveToneIDElement::size()) { // pass... } void AnytoneCodeplug::FiveToneIDElement::clear() { memset(_data, 0x00, _size); } AnytoneCodeplug::FiveToneIDElement::Standard AnytoneCodeplug::FiveToneIDElement::standard() const { return (Standard) getUInt8(0x0001); } void AnytoneCodeplug::FiveToneIDElement::setStandard(Standard std) { setUInt8(0x0001, (unsigned)std); } unsigned AnytoneCodeplug::FiveToneIDElement::toneDuration() const { return getUInt8(0x0003); } void AnytoneCodeplug::FiveToneIDElement::setToneDuration(unsigned ms) { setUInt8(0x0003, ms); } QString AnytoneCodeplug::FiveToneIDElement::id() const { unsigned len = getUInt8(0x0002); QString id; for (unsigned i=0; i>4)&0xf,16)); else id.append(QString::number((b>>0)&0xf,16)); } return id; } void AnytoneCodeplug::FiveToneIDElement::setID(const QString &id) { unsigned len = 0; for (int i=0; i= Limit::numEntries()) return nullptr; return _data + n*FiveToneIDElement::size(); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::FiveToneFunctionElement * ********************************************************************************************* */ AnytoneCodeplug::FiveToneFunctionElement::FiveToneFunctionElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::FiveToneFunctionElement::FiveToneFunctionElement(uint8_t *ptr) : Element(ptr, FiveToneFunctionElement::size()) { // pass... } void AnytoneCodeplug::FiveToneFunctionElement::clear() { memset(_data, 0x00, _size); } AnytoneCodeplug::FiveToneFunctionElement::Function AnytoneCodeplug::FiveToneFunctionElement::function() const { return (Function) getUInt8(0x0000); } void AnytoneCodeplug::FiveToneFunctionElement::setFunction(Function function) { setUInt8(0x0000, (unsigned)function); } AnytoneCodeplug::FiveToneFunctionElement::Response AnytoneCodeplug::FiveToneFunctionElement::response() const { return (Response) getUInt8(0x0001); } void AnytoneCodeplug::FiveToneFunctionElement::setResponse(Response response) { setUInt8(0x0001, (unsigned)response); } QString AnytoneCodeplug::FiveToneFunctionElement::id() const { unsigned len = getUInt8(0x0002); QString id; for (unsigned i=0; i>4)&0xf, 16)); else id.append(QString::number((b>>0)&0xf, 16)); } return id; } void AnytoneCodeplug::FiveToneFunctionElement::setID(const QString &id) { unsigned len = 0; for (int i=0; i= Limit::numFunctions()) return nullptr; return _data + n*FiveToneFunctionElement::size(); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::FiveToneSettingsElement * ********************************************************************************************* */ AnytoneCodeplug::FiveToneSettingsElement::FiveToneSettingsElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::FiveToneSettingsElement::FiveToneSettingsElement(uint8_t *ptr) : Element(ptr, FiveToneSettingsElement::size()) { // pass... } void AnytoneCodeplug::FiveToneSettingsElement::clear() { memset(_data, 0x00, _size); } AnytoneCodeplug::FiveToneSettingsElement::Response AnytoneCodeplug::FiveToneSettingsElement::decodingResponse() const { return (Response) getUInt8(0x0021); } void AnytoneCodeplug::FiveToneSettingsElement::setDecodingResponse(Response response) { setUInt8(0x0021, (unsigned)response); } AnytoneCodeplug::FiveToneSettingsElement::Standard AnytoneCodeplug::FiveToneSettingsElement::decodingStandard() const { return (Standard) getUInt8(0x0022); } void AnytoneCodeplug::FiveToneSettingsElement::setDecodingStandard(Standard standard) { setUInt8(0x0022, (unsigned)standard); } unsigned AnytoneCodeplug::FiveToneSettingsElement::decodingToneDuration() const { return getUInt8(0x0024); } void AnytoneCodeplug::FiveToneSettingsElement::setDecodingToneDuration(unsigned ms) { setUInt8(0x0024, ms); } QString AnytoneCodeplug::FiveToneSettingsElement::id() const { unsigned len = getUInt8(0x0023); QString id; for (unsigned i=0; i>4)&0xf, 16)); else id.append(QString::number((b>>0)&0xf, 16)); } return id; } void AnytoneCodeplug::FiveToneSettingsElement::setID(const QString &id) { unsigned len = 0; for (int i=0; i>4)&0xf, 16)); else id.append(QString::number((b>>0)&0xf, 16)); } return id; } void AnytoneCodeplug::FiveToneSettingsElement::setBOTID(const QString &id) { unsigned len = 0; for (int i=0; i>4)&0xf, 16)); else id.append(QString::number((b>>0)&0xf, 16)); } return id; } void AnytoneCodeplug::FiveToneSettingsElement::setEOTID(const QString &id) { unsigned len = 0; for (int i=0; i= Limit::numEntries()) return false; return 0xff != getUInt8(n*Limit::numberLength()); } QString AnytoneCodeplug::DTMFIDListElement::number(unsigned int n) const { if (n >= Limit::numEntries()) return ""; uint8_t *num = _data + n*Limit::numberLength(); return decode_dtmf_bin(num, Limit::numberLength(), 0xff); } void AnytoneCodeplug::DTMFIDListElement::setNumber(unsigned int n, const QString &number) { if (n >= Limit::numEntries()) return; uint8_t *num = _data + n*Limit::numberLength(); encode_dtmf_bin(number, num, Limit::numberLength(), 0xff); } void AnytoneCodeplug::DTMFIDListElement::clearNumber(unsigned int n) { if (n >= Limit::numEntries()) return; uint8_t *num = _data + n*Limit::numberLength(); memset(num, 0xff, Limit::numberLength()); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::WFMChannelListElement * ********************************************************************************************* */ AnytoneCodeplug::WFMChannelListElement::WFMChannelListElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } AnytoneCodeplug::WFMChannelListElement::WFMChannelListElement(uint8_t *ptr) : Element(ptr, WFMChannelListElement::size()) { // pass... } void AnytoneCodeplug::WFMChannelListElement::clear() { memset(_data, 0x00, _size); } bool AnytoneCodeplug::WFMChannelListElement::hasChannel(unsigned int n) const { if (n >= Limit::numEntries()) return false; return 0 != getBCD8_le(n*Offset::betweenChannels()); } Frequency AnytoneCodeplug::WFMChannelListElement::channel(unsigned int n) const { if (n >= Limit::numEntries()) return Frequency(); return Frequency::fromHz(((unsigned long long)getBCD8_le(n*Offset::betweenChannels()))*100); } void AnytoneCodeplug::WFMChannelListElement::setChannel(unsigned int n, Frequency freq) { if (n >= Limit::numEntries()) return; setBCD8_le(n*Offset::betweenChannels(), freq.inHz()/100); } void AnytoneCodeplug::WFMChannelListElement::clearChannel(unsigned int n) { if (n >= Limit::numEntries()) return; setBCD8_le(n*Offset::betweenChannels(), 0); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::WFMChannelBitmapElement * ********************************************************************************************* */ AnytoneCodeplug::WFMChannelBitmapElement::WFMChannelBitmapElement(uint8_t *ptr, size_t size) : BitmapElement(ptr, size) { // pass... } AnytoneCodeplug::WFMChannelBitmapElement::WFMChannelBitmapElement(uint8_t *ptr) : BitmapElement(ptr, WFMChannelBitmapElement::size()) { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::WFMVFOElement * ********************************************************************************************* */ AnytoneCodeplug::WFMVFOElement::WFMVFOElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } AnytoneCodeplug::WFMVFOElement::WFMVFOElement(uint8_t *ptr) : Element(ptr, WFMVFOElement::size()) { // pass... } void AnytoneCodeplug::WFMVFOElement::clear() { memset(_data, 0x00, _size); setFrequency(Frequency::fromMHz(88)); } Frequency AnytoneCodeplug::WFMVFOElement::frequency() const { return Frequency::fromHz(((unsigned long long)getBCD8_le(0))*100); } void AnytoneCodeplug::WFMVFOElement::setFrequency(Frequency freq) { setBCD8_le(0, freq.inHz()/100); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::DMREncryptionKeyIDListElement * ********************************************************************************************* */ AnytoneCodeplug::DMREncryptionKeyIDListElement::DMREncryptionKeyIDListElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } AnytoneCodeplug::DMREncryptionKeyIDListElement::DMREncryptionKeyIDListElement(uint8_t *ptr) : Element(ptr, DMREncryptionKeyIDListElement::size()) { // pass... } void AnytoneCodeplug::DMREncryptionKeyIDListElement::clear() { memset(_data, 0xff, _size); } bool AnytoneCodeplug::DMREncryptionKeyIDListElement::hasID(unsigned int n) const { if (n >= Limit::numEntries()) return false; return 0xffff == getUInt16_be(n*Offset::betweenIDs()); } uint16_t AnytoneCodeplug::DMREncryptionKeyIDListElement::id(unsigned int n) const { if (n >= Limit::numEntries()) return 0xffff; return getUInt16_be(n*Offset::betweenIDs()); } void AnytoneCodeplug::DMREncryptionKeyIDListElement::setID(unsigned int n, uint16_t id) { if (n >= Limit::numEntries()) return; setUInt16_be(n*Offset::betweenIDs(), id); } void AnytoneCodeplug::DMREncryptionKeyIDListElement::clearID(unsigned int n) { if (n >= Limit::numEntries()) return; setUInt16_be(n*Offset::betweenIDs(), 0xffff); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::DMREncryptionKeyListElement * ********************************************************************************************* */ AnytoneCodeplug::DMREncryptionKeyListElement::DMREncryptionKeyListElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } AnytoneCodeplug::DMREncryptionKeyListElement::DMREncryptionKeyListElement(uint8_t *ptr) : Element(ptr, DMREncryptionKeyListElement::size()) { // pass... } void AnytoneCodeplug::DMREncryptionKeyListElement::clear() { memset(_data, 0x00, _size); for (unsigned int i=0; i= Limit::numEntries()) return QByteArray(); return QByteArray::fromRawData((const char *)_data + Offset::keys() + n*Offset::betweenKeys(), 2); } void AnytoneCodeplug::DMREncryptionKeyListElement::setKey(unsigned int n, const QByteArray &key) { if ((n >= Limit::numEntries()) || (2 != key.size())) return; memcpy(_data + Offset::keys() + n*Offset::betweenKeys(), key.constData(), 2); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug::ContactMapElement * ********************************************************************************************* */ AnytoneCodeplug::ContactMapElement::ContactMapElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } AnytoneCodeplug::ContactMapElement::ContactMapElement(uint8_t *ptr) : Element(ptr, ContactMapElement::size()) { // pass... } void AnytoneCodeplug::ContactMapElement::clear() { memset(_data, 0xff, _size); } bool AnytoneCodeplug::ContactMapElement::isValid() const { return (0xffffffff!=getUInt32_le(0x0000)) && (0xffffffff!=getUInt32_le(0x0004)); } bool AnytoneCodeplug::ContactMapElement::isGroup() const { uint32_t tmp = getUInt32_le(0x0000); return tmp & 0x01; } unsigned AnytoneCodeplug::ContactMapElement::id() const { uint32_t tmp = getUInt32_le(0x0000); tmp = tmp>>1; return decode_dmr_id_bcd_le((uint8_t *)&tmp); } void AnytoneCodeplug::ContactMapElement::setID(unsigned id, bool group) { uint32_t tmp; encode_dmr_id_bcd_le((uint8_t *)&tmp, id); tmp = ( (tmp << 1) | (group ? 1 : 0) ); setUInt32_le(0x0000, tmp); } unsigned AnytoneCodeplug::ContactMapElement::index() const { return getUInt32_le(0x0004); } void AnytoneCodeplug::ContactMapElement::setIndex(unsigned idx) { setUInt32_le(0x0004, idx); } /* ********************************************************************************************* * * Implementation of AnytoneCodeplug * ********************************************************************************************* */ AnytoneCodeplug::AnytoneCodeplug(const QString &label, QObject *parent) : Codeplug(parent), _label(label) { // pass... } AnytoneCodeplug::~AnytoneCodeplug() { // pass... } void AnytoneCodeplug::clear() { while (this->numImages()) remImage(0); addImage(_label); // Allocate bitmaps this->allocateBitmaps(); } bool AnytoneCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) const { Q_UNUSED(err) // All indices as 0-based. That is, the first channel gets index 0 etc. // Map radio IDs for (int i=0; iradioIDs()->count(); i++) ctx.add(config->radioIDs()->getId(i), i); // Map digital and DTMF contacts for (int i=0, d=0, a=0; icontacts()->count(); i++) { if (config->contacts()->contact(i)->is()) { ctx.add(config->contacts()->contact(i)->as(), d); d++; } else if (config->contacts()->contact(i)->is()) { ctx.add(config->contacts()->contact(i)->as(), a); a++; } } // Map rx group lists for (int i=0; irxGroupLists()->count(); i++) ctx.add(config->rxGroupLists()->list(i), i); // Map channels for (int i=0; ichannelList()->count(); i++) ctx.add(config->channelList()->channel(i), i); // Map zones for (int i=0; izones()->count(); i++) ctx.add(config->zones()->zone(i), i); // Map scan lists for (int i=0; iscanlists()->count(); i++) ctx.add(config->scanlists()->scanlist(i), i); // Map DMR APRS systems for (int i=0,a=0,d=0; iposSystems()->count(); i++) { if (config->posSystems()->system(i)->is()) { ctx.add(config->posSystems()->system(i)->as(), d); d++; } else if (config->posSystems()->system(i)->is()) { auto *aprs = config->posSystems()->system(i)->as(); ctx.add(aprs, a); a++; // Index FM APRS frequencies (referenced in channel extensions). if (auto *ext = aprs->anytoneExtension()) { for (int j=0; jfrequencies()->count(); j++) { // Index 0 = default, so index is 1-based ctx.add(ext->frequencies()->get(j)->as(), j+1); } } } } // Map roaming for (int i=0; iroamingZones()->count(); i++) ctx.add(config->roamingZones()->zone(i), i); for (int i=0; iroamingChannels()->count(); i++) ctx.add(config->roamingChannels()->channel(i), i); // Map auto-repeater offsets if (config->settings()->anytoneExtension()) { auto *autoRep = config->settings()->anytoneExtension()->autoRepeaterSettings(); for (int i=0; ioffsets()->count(); i++) ctx.add(autoRep->offsets()->get(i)->as(), i); } // Map SMS templates for (int i=0; ismsExtension()->smsTemplates()->count(); i++) ctx.add(config->smsExtension()->smsTemplates()->get(i)->as(), i); return true; } Config * AnytoneCodeplug::preprocess(Config *config, const ErrorStack &err) const { Config *intermediate = Codeplug::preprocess(config, err); if (nullptr == intermediate) { errMsg(err) << "Cannot pre-process codeplug for anytone device."; return nullptr; } ZoneSplitVisitor splitter; if (! splitter.process(intermediate, err)) { errMsg(err) << "Cannot pre-process codeplug for anytone device."; delete intermediate; return nullptr; } return intermediate; } bool AnytoneCodeplug::postprocess(Config *config, const ErrorStack &err) const { if (! Codeplug::postprocess(config, err)) { errMsg(err) << "Cannot post-process codeplug for anytone device."; return false; } ZoneMergeVisitor merger; if (! merger.process(config, err)) { errMsg(err) << "Cannot post-process codeplug for anytone device."; return false; } return true; } bool AnytoneCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) { Context ctx(config); // Register table for auto-repeater offsets ctx.addTable(&AnytoneAutoRepeaterOffset::staticMetaObject); // Register table for FM APRS frequencies ctx.addTable(&AnytoneAPRSFrequency::staticMetaObject); if (! index(config, ctx, err)) { errMsg(err) << "Cannot encode anytone codeplug."; return false; } // If codeplug is generated from scratch -> clear and reallocate if (! flags.updateCodePlug) { // Clear codeplug this->clear(); // Then allocate elements this->allocateUpdated(); } // First set bitmaps this->setBitmaps(ctx); // Allocate all memory elements representing the common config this->allocateForEncoding(); // Then encode everything. return this->encodeElements(flags, ctx, err); } bool AnytoneCodeplug::decode(Config *config, const ErrorStack &err) { // Maps code-plug indices to objects Context ctx(config); // Register table for auto-repeater offsets ctx.addTable(&AnytoneAutoRepeaterOffset::staticMetaObject); // Register table for FM APRS frequencies ctx.addTable(&AnytoneAPRSFrequency::staticMetaObject); return this->decodeElements(ctx, err); } qdmr-0.12.3/lib/anytone_codeplug.hh000066400000000000000000003303721501654372000172010ustar00rootroot00000000000000#ifndef ANYTONECODEPLUG_HH #define ANYTONECODEPLUG_HH #include "codeplug.hh" #include #include "channel.hh" #include "contact.hh" class RadioSettings; /** Base class interface for all Anytone radio codeplugs. * * This class extends the generic @c CodePlug to provide an interface to the multi-step up and * download of the binary codeplug. In contrast to the majority of radios, the Anytone codeplugs * are heavily segmented and only valid sections are read from a written to the device. * * @ingroup anytone */ class AnytoneCodeplug : public Codeplug { Q_OBJECT public: /** Implements encoding of CTCSS tones. */ struct CTCSS { public: /** Encodes Signaling::Code CTCSS tones. */ static uint8_t encode(const SelectiveCall &tone); /** Decodes to Signaling::Code CTCSS tones. */ static SelectiveCall decode(uint8_t code); protected: /** Translation table. */ static SelectiveCall _codeTable[52]; }; /** Represents the base class for bitmaps in all AnyTone codeplugs. */ class BitmapElement: public Element { protected: /** Hidden constructor. */ BitmapElement(uint8_t *ptr, size_t size); public: /** Clears the bitmap, disables all channels. */ void clear(); /** Returns @c true if the given index is valid. */ virtual bool isEncoded(unsigned int idx) const ; /** Enables/disables the specified index. */ virtual void setEncoded(unsigned int idx, bool enable); /** Enables the first n elements. */ virtual void enableFirst(unsigned int n); }; /** Represents the base class for inverted bitmaps in all AnyTone codeplugs. */ class InvertedBitmapElement: public Element { protected: /** Hidden constructor. */ InvertedBitmapElement(uint8_t *ptr, size_t size); public: /** Clears the bitmap, disables all channels. */ void clear(); /** Returns @c true if the given index is valid. */ virtual bool isEncoded(unsigned int idx) const ; /** Enables/disables the specified index. */ virtual void setEncoded(unsigned int idx, bool enable); /** Enables the first n elements. */ virtual void enableFirst(unsigned int n); }; /** Represents the base class for inverted bytemaps in all AnyTone codeplugs. * This is obviously a result of a lazy firmware developer. There is already some code in the * firmware to handle bitmaps. The developer, however, copied some BS code from elsewhere. It is * inverted, because erased flash memory is usually initialized with 0xff. However, the AnyTone * memory gets erased to 0x00. So the inversion is not necessary. Someone really took pride in * his/her work and consequently, I need to implement some BS elements more. */ class InvertedBytemapElement: public Element { protected: /** Hidden constructor. */ InvertedBytemapElement(uint8_t *ptr, size_t size); public: /** Clears the bitmap, disables all channels. */ void clear(); /** Returns @c true if the given index is valid. */ virtual bool isEncoded(unsigned int idx) const ; /** Enables/disables the specified index. */ virtual void setEncoded(unsigned int idx, bool enable); /** Enables the first n elements. */ virtual void enableFirst(unsigned int n); }; /** Represents the base class for channel encodings in all AnyTone codeplugs. * * Memory layout of encoded channel (0x40 bytes): * @verbinclude anytone_channel.txt */ class ChannelElement: public Element { public: /** Defines all possible channel modes, see @c channelMode. */ enum class Mode { Analog = 0, ///< Analog channel. Digital = 1, ///< Digital (DMR) channel. MixedAnalog = 2, ///< Mixed, analog channel with digital RX. MixedDigital = 3 ///< Mixed, digital channel with analog RX. }; /** Defines all possible power settings.*/ enum Power { POWER_LOW = 0, ///< Low power, usually 1W. POWER_MIDDLE = 1, ///< Medium power, usually 2.5W. POWER_HIGH = 2, ///< High power, usually 5W. POWER_TURBO = 3 ///< Higher power, usually 7W on VHF and 6W on UHF. }; /** Defines all possible repeater modes. */ enum class RepeaterMode { Simplex = 0, ///< Simplex mode, that is TX frequency = RX frequency. @c tx_offset is ignored. Positive = 1, ///< Repeater mode with positive @c tx_offset. Negative = 2 ///< Repeater mode with negative @c tx_offset. }; /** Possible analog signaling modes. */ enum class SignalingMode { None = 0, ///< None. CTCSS = 1, ///< Use CTCSS tones DCS = 2 ///< Use DCS codes. }; /** Defines possible admit criteria. */ enum class Admit { Always = 0, ///< For both channel types. Free = 1, ///< For digital channels. DifferentColorCode = 2, ///< For digital channels. SameColorCode = 3, ///< For digital channels. Tone = 1, ///< For analog channels Busy = 2 ///< For analog channels. }; /** Defines all possible optional signalling settings. */ enum class OptSignaling { Off = 0, ///< None. DTMF = 1, ///< Use DTMF. TwoTone = 2, ///< Use 2-tone. FiveTone = 3 ///< Use 5-tone. }; protected: /** Hidden constructor. */ ChannelElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ ChannelElement(uint8_t *ptr); /** Destructor. */ virtual ~ChannelElement(); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0040; } /** Resets the channel. */ void clear(); /** Returns the RX frequency in Hz. */ virtual unsigned rxFrequency() const; /** Sets the RX frequency in Hz. */ virtual void setRXFrequency(unsigned hz); /** Returns the TX frequency offset in Hz. * This method returns an unsigned value, the sign of the offset frequency is stored in * @c repeaterMode(). */ virtual unsigned txOffset() const; /** Sets the TX frequency offset in Hz. * This method accepts unsigned values, the sign of the offset frequency is stored in * @c repeaterMode(). */ virtual void setTXOffset(unsigned hz); /** Returns the TX frequency in Hz. */ virtual unsigned txFrequency() const; /** Sets the TX frequency indirectly. That is, relative to the RX frequency which must be set * first. This method also updates the @c repeaterMode. */ virtual void setTXFrequency(unsigned hz); /** Returns the channel mode (analog, digtital, etc). */ virtual Mode mode() const; /** Sets the channel mode. */ virtual void setMode(Mode mode); /** Returns the channel power. */ virtual Channel::Power power() const; /** Sets the channel power. */ virtual void setPower(Channel::Power power); /** Returns the band width of the channel. */ virtual FMChannel::Bandwidth bandwidth() const; /** Sets the band width of the channel. */ virtual void setBandwidth(FMChannel::Bandwidth bw); /** Returns the transmit offset direction. */ virtual RepeaterMode repeaterMode() const; /** Sets the transmit offset direction. */ virtual void setRepeaterMode(RepeaterMode mode); /** Returns the RX signaling mode */ virtual SignalingMode rxSignalingMode() const; /** Sets the RX signaling mode */ virtual void setRXSignalingMode(SignalingMode mode); /** Simplified access to RX signaling (tone). */ virtual SelectiveCall rxTone() const; /** Sets the RX signaling (tone). */ virtual void setRXTone(const SelectiveCall &code); /** Returns the TX signaling mode */ virtual SignalingMode txSignalingMode() const; /** Sets the TX signaling mode */ virtual void setTXSignalingMode(SignalingMode mode); /** Simplified access to TX signaling (tone). */ virtual SelectiveCall txTone() const; /** Sets the RX signaling (tone). */ virtual void setTXTone(const SelectiveCall &code); /** Returns @c true if the CTCSS phase reversal is enabled. */ virtual bool ctcssPhaseReversal() const; /** Enables/disables CTCSS phase reversal. */ virtual void enableCTCSSPhaseReversal(bool enable); /** Returns @c true if the RX only is enabled. */ virtual bool rxOnly() const; /** Enables/disables RX only. */ virtual void enableRXOnly(bool enable); /** Returns @c true if the call confirm is enabled. */ virtual bool callConfirm() const; /** Enables/disables call confirm. */ virtual void enableCallConfirm(bool enable); /** Returns @c true if the talkaround is enabled. */ virtual bool talkaround() const; /** Enables/disables talkaround. */ virtual void enableTalkaround(bool enable); /** Returns @c true if the TX CTCSS tone frequency is custom (non standard). */ virtual bool txCTCSSIsCustom() const; /** Returns the TX CTCSS tone. */ virtual SelectiveCall txCTCSS() const; /** Sets the TX CTCSS tone. */ virtual void setTXCTCSS(const SelectiveCall &tone); /** Enables TX custom CTCSS frequency. */ virtual void enableTXCustomCTCSS(); /** Returns @c true if the RX CTCSS tone frequency is custom (non standard). */ virtual bool rxCTCSSIsCustom() const; /** Returns the RX CTCSS tone. */ virtual SelectiveCall rxCTCSS() const; /** Sets the RX CTCSS tone. */ virtual void setRXCTCSS(const SelectiveCall &tone); /** Enables RX custom CTCSS frequency. */ virtual void enableRXCustomCTCSS(); /** Returns the TX DCS code. */ virtual SelectiveCall txDCS() const; /** Sets the TX DCS code. */ virtual void setTXDCS(const SelectiveCall &code); /** Returns the RX DCS code. */ virtual SelectiveCall rxDCS() const; /** Sets the RX DCS code. */ virtual void setRXDCS(const SelectiveCall &code); /** Returns the custom CTCSS frequency in Hz. */ virtual double customCTCSSFrequency() const; /** Sets the custom CTCSS frequency in Hz. */ virtual void setCustomCTCSSFrequency(double hz); /** Returns the 2-tone decode index (0-based). */ virtual unsigned twoToneDecodeIndex() const; /** Sets the 2-tone decode index (0-based). */ virtual void setTwoToneDecodeIndex(unsigned idx); /** Returns the transmit contact index (0-based). */ virtual unsigned contactIndex() const; /** Sets the transmit contact index (0-based). */ virtual void setContactIndex(unsigned idx); /** Returns the radio ID index (0-based). */ virtual unsigned radioIDIndex() const; /** Sets the radio ID index (0-based). */ virtual void setRadioIDIndex(unsigned idx); /** Returns @c true if the sequelch is silent and @c false if open. */ virtual AnytoneFMChannelExtension::SquelchMode squelchMode() const; /** Enables/disables silent squelch. */ virtual void setSquelchMode(AnytoneFMChannelExtension::SquelchMode mode); /** Returns the admit criterion. */ virtual Admit admit() const; /** Sets the admit criterion. */ virtual void setAdmit(Admit admit); /** Returns the optional signalling type. */ virtual OptSignaling optionalSignaling() const; /** Sets the optional signaling type. */ virtual void setOptionalSignaling(OptSignaling sig); /** Returns @c true, if a scan list index is set. */ virtual bool hasScanListIndex() const; /** Returns the scan list index (0-based). */ virtual unsigned scanListIndex() const; /** Sets the scan list index (0-based). */ virtual void setScanListIndex(unsigned idx); /** Clears the scan list index. */ virtual void clearScanListIndex(); /** Returns @c true, if a group list index is set. */ virtual bool hasGroupListIndex() const; /** Returns the scan list index (0-based). */ virtual unsigned groupListIndex() const; /** Sets the group list index (0-based). */ virtual void setGroupListIndex(unsigned idx); /** Clears the group list index. */ virtual void clearGroupListIndex(); /** Returns the two-tone ID index (0-based). */ virtual unsigned twoToneIDIndex() const; /** Sets the two-tone ID index (0-based). */ virtual void setTwoToneIDIndex(unsigned idx); /** Returns the five-tone ID index (0-based). */ virtual unsigned fiveToneIDIndex() const; /** Sets the five-tone ID index (0-based). */ virtual void setFiveToneIDIndex(unsigned idx); /** Returns the DTFM ID index (0-based). */ virtual unsigned dtmfIDIndex() const; /** Sets the DTMF ID index (0-based). */ virtual void setDTMFIDIndex(unsigned idx); /** Returns the color code. */ virtual unsigned colorCode() const; /** Sets the color code. */ virtual void setColorCode(unsigned code); /** Returns the time slot. */ virtual DMRChannel::TimeSlot timeSlot() const; /** Sets the time slot. */ virtual void setTimeSlot(DMRChannel::TimeSlot ts); /** Returns @c true if SMS confirmation is enabled. */ virtual bool smsConfirm() const; /** Enables/disables SMS confirmation. */ virtual void enableSMSConfirm(bool enable); /** Returns @c true if simplex TDMA is enabled. */ virtual bool simplexTDMA() const; /** Enables/disables simplex TDMA confirmation. */ virtual void enableSimplexTDMA(bool enable); /** Returns @c true if adaptive TDMA is enabled. */ virtual bool adaptiveTDMA() const; /** Enables/disables adaptive TDMA. */ virtual void enableAdaptiveTDMA(bool enable); /** Returns @c true if RX APRS is enabled. */ virtual bool rxAPRS() const; /** Enables/disables RX APRS. */ virtual void enableRXAPRS(bool enable); /** Returns @c true if enhanced encryption is enabled. */ virtual bool enhancedEncryption() const; /** Enables/disables enhanced encryption. */ virtual void enableEnhancedEncryption(bool enable); /** Returns @c true if lone worker is enabled. */ virtual bool loneWorker() const; /** Enables/disables lone worker. */ virtual void enableLoneWorker(bool enable); /** Returns @c true if an encryption key is set. */ virtual bool hasEncryptionKeyIndex() const; /** Returns the AES (enhanced) encryption key index (0-based). */ virtual unsigned encryptionKeyIndex() const; /** Sets the AES (enahnced) encryption key index (0-based). */ virtual void setEncryptionKeyIndex(unsigned idx); /** Clears the encryption key index. */ virtual void clearEncryptionKeyIndex(); /** Returns the channel name. */ virtual QString name() const; /** Sets the channel name. */ virtual void setName(const QString &name); /** Constructs a generic @c Channel object from the codeplug channel. */ virtual Channel *toChannelObj(Context &ctx) const; /** Links a previously constructed channel to the rest of the configuration. */ virtual bool linkChannelObj(Channel *c, Context &ctx) const; /** Initializes this codeplug channel from the given generic configuration. */ virtual bool fromChannelObj(const Channel *c, Context &ctx); protected: /** Internal used offsets within the channel element. */ struct Offset { /// @todo Implement }; }; /** Represents the channel bitmaps in all AnyTone codeplugs. */ class ChannelBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ ChannelBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ChannelBitmapElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0200; } }; /** Represents the base class for conacts in all AnyTone codeplugs. * * Memory layout of encoded contact (0x64 bytes): * @verbinclude anytone_contact.txt */ class ContactElement: public Element { protected: /** Hidden constructor. */ ContactElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ContactElement(uint8_t *ptr); /** Destructor. */ virtual ~ContactElement(); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0064; } /** Resets the contact element. */ void clear(); /** Returns @c true if the element is valid. */ bool isValid() const; /** Returns the contact type. */ virtual DMRContact::Type type() const; /** Sets the contact type. */ virtual void setType(DMRContact::Type type); /** Returns the name of the contact. */ virtual QString name() const; /** Sets the name of the contact. */ virtual void setName(const QString &name); /** Returns the contact number. */ virtual unsigned number() const; /** Sets the contact number. */ virtual void setNumber(unsigned number); /** Returns the alert type. */ virtual AnytoneContactExtension::AlertType alertType() const; /** Sets the alert type. */ virtual void setAlertType(AnytoneContactExtension::AlertType type); /** Assembles a @c DigitalContact from this contact. */ virtual DMRContact *toContactObj(Context &ctx) const; /** Constructs this contact from the give @c DigitalContact. */ virtual bool fromContactObj(const DMRContact *contact, Context &ctx); }; /** Represents the contact bitmaps in all AnyTone codeplugs. */ class ContactBitmapElement: public InvertedBitmapElement { protected: /** Hidden constructor. */ ContactBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ContactBitmapElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0500; } }; /** Represents the base class for analog (DTMF) contacts in all AnyTone codeplugs. * * Encoding of the DTMF contact (0x30 bytes): * @verbinclude anytone_dtmfcontact.txt */ class DTMFContactElement: public Element { protected: /** Hidden constructor. */ DTMFContactElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit DTMFContactElement(uint8_t *ptr); /** Destructor. */ virtual ~DTMFContactElement(); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0018; } /** Resets the contact element. */ void clear(); /** Returns the number of the contact. */ virtual QString number() const; /** Sets the number of the contact. */ virtual void setNumber(const QString &number); /** Returns the name of the contact. */ virtual QString name() const; /** Sets the name of the contact. */ virtual void setName(const QString &name); /** Creates an DTMF contact from the entry. */ virtual DTMFContact *toContact() const; /** Encodes an DTMF contact from the given one. */ virtual bool fromContact(const DTMFContact *contact); public: /** Some limits for the element. */ struct Limit { static constexpr unsigned int digitCount() { return 14; } ///< The max number of digits. static constexpr unsigned int nameLength() { return 15; } ///< Maximum name length. }; protected: /** Internal used offsets within the codeplug. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int digits() { return 0x0000; } static constexpr unsigned int numDigits() { return 0x0007; } static constexpr unsigned int name() { return 0x0008; } /// @endcond }; }; /** Represents the DTMF contact byte map, indicating which contacts are valid. */ class DTMFContactBytemapElement: public InvertedBytemapElement { protected: /** Hidden constructor. */ DTMFContactBytemapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit DTMFContactBytemapElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0100; } }; /** Represents the base class for group lists in all AnyTone codeplugs. * * Encoding of a group list (0x120 bytes): * @verbinclude anytone_grouplist.txt */ class GroupListElement: public Element { protected: /** Hidden constructor. */ GroupListElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ GroupListElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0120; } /** Clears the group list. */ void clear(); /** Returns @c true if the group list is valid. */ bool isValid() const; /** Returns the name of the group list. */ virtual QString name() const; /** Sets the name of the group list. */ virtual void setName(const QString &name); /** Returns @c true if the n-th member index is valid. */ virtual bool hasMemberIndex(unsigned n) const; /** Returns the n-th member index. */ virtual unsigned memberIndex(unsigned n) const; /** Sets the n-th member index. */ virtual void setMemberIndex(unsigned n, unsigned idx); /** Clears the n-th member index. */ virtual void clearMemberIndex(unsigned n); /** Constructs a new @c RXGroupList from this group list. * None of the members are added yet. Call @c linkGroupList * to do that. */ virtual RXGroupList *toGroupListObj() const; /** Populates the @c RXGroupList from this group list. The CodeplugContext * is used to map the member indices. */ virtual bool linkGroupList(RXGroupList *lst, Context &ctx) const; /** Constructs this group list from the given @c RXGroupList. */ virtual bool fromGroupListObj(const RXGroupList *lst, Context &ctx); }; /** Represents the bitmap indicating which group list element is valid. */ class GroupListBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ GroupListBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit GroupListBitmapElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0020; } }; /** Represents the base class for scan lists in all AnyTone codeplugs. * * Memory layout of encoded scanlist (0x90 bytes): * @verbinclude anytone_scanlist.txt */ class ScanListElement: public Element { public: /** Defines all possible priority channel selections. */ enum class PriChannel { Off = 0, ///< Off. Primary = 1, ///< Priority Channel Select 1. Secondary = 2, ///< Priority Channel Select 2. Both = 3 ///< Priority Channel Select 1 + Priority Channel Select 2. }; /** Defines all possible reply channel selections. */ enum class RevertChannel { Selected = 0, ///< Selected channel. SelectedActive = 1, ///< Selected + active channel. Primary = 2, ///< Primary channel. Secondary = 3, ///< Secondary channel. LastCalled = 4, ///< Last Called. LastUsed = 5, ///< Last Used. PrimaryActive = 6, ///< Primary + active channel. SecondaryActive = 7 ///< Secondary + active channel. }; protected: /** Hidden constructor. */ ScanListElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ ScanListElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0090; } /** Resets the scan list. */ void clear(); /** Returns the priority channel selection. */ virtual PriChannel priorityChannels() const; /** Sets the priority channel selection. */ virtual void setPriorityChannels(PriChannel sel); /** Returns @c true if the primary channel is set. */ virtual bool hasPrimary() const; /** Returns @c true if the primary channel is set to the selected channel. */ virtual bool primaryIsSelected() const; /** Returns the primary channel index. */ virtual unsigned primary() const; /** Sets the primary channel index. */ virtual void setPrimary(unsigned idx); /** Sets the primary channel to be selected channel. */ virtual void setPrimarySelected(); /** Clears the primary channel index. */ virtual void clearPrimaryChannel(); /** Returns @c true if the secondary channel is set. */ virtual bool hasSecondary() const; /** Returns @c true if the secondary channel is set to the selected channel. */ virtual bool secondaryIsSelected() const; /** Returns the secondary channel index. */ virtual unsigned secondary() const; /** Sets the secondary channel index. */ virtual void setSecondary(unsigned idx); /** Sets the secondary channel to be selected channel. */ virtual void setSecondarySelected(); /** Clears the secondary channel index. */ virtual void clearSecondaryChannel(); /** Returns the look back time A in seconds. */ virtual unsigned lookBackTimeA() const; /** Sets the look back time A in seconds. */ virtual void setLookBackTimeA(unsigned sec); /** Returns the look back time B in seconds. */ virtual unsigned lookBackTimeB() const; /** Sets the look back time B in seconds. */ virtual void setLookBackTimeB(unsigned sec); /** Returns the drop out delay in seconds. */ virtual unsigned dropOutDelay() const; /** Sets the drop out delay in seconds. */ virtual void setDropOutDelay(unsigned sec); /** Returns the dwell time in seconds. */ virtual unsigned dwellTime() const; /** Sets the dwell time in seconds. */ virtual void setDwellTime(unsigned sec); /** Returns the revert channel type. */ virtual RevertChannel revertChannel() const; /** Sets the revert channel type. */ virtual void setRevertChannel(RevertChannel type); /** Returns the name of the scan list. */ virtual QString name() const; /** Sets the name of the scan list. */ virtual void setName(const QString &name); /** Returns @c true if the n-th member index is set. */ virtual bool hasMemberIndex(unsigned n) const; /** Returns the n-th member index. */ virtual unsigned memberIndex(unsigned n) const; /** Sets the n-th member index. */ virtual void setMemberIndex(unsigned n, unsigned idx); /** Clears the n-th member index. */ virtual void clearMemberIndex(unsigned n); /** Constructs a ScanList object from this definition. This only sets the properties of * the scan list. To associate all members with the scan list object, call @c linkScanListObj. */ virtual ScanList *toScanListObj() const; /** Links all channels (members and primary channels) with the given scan-list object. */ virtual bool linkScanListObj(ScanList *lst, Context &ctx) const; /** Constructs the binary representation from the give config. */ virtual bool fromScanListObj(ScanList *lst, Context &ctx); }; /** Represents the bitmap indicating which scanlist elements are valid. */ class ScanListBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ ScanListBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ScanListBitmapElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x00000020; } }; /** Represents the base class for radio IDs in all AnyTone codeplugs. * * Memory layout of encoded scanlist (0x20 bytes): * @verbinclude anytone_radioid.txt */ class RadioIDElement: public Element { protected: /** Hidden constructor. */ RadioIDElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ RadioIDElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0020; } /** Resets the radio ID. */ void clear(); /** Returns the number of the radio ID. */ virtual unsigned number() const; /** Sets the number of the radio ID. */ virtual void setNumber(unsigned number); /** Returns the name of the radio ID. */ virtual QString name() const; /** Sets the name of the radio ID. */ virtual void setName(const QString &name); /** Encodes the given RadioID. */ virtual bool fromRadioID(DMRRadioID *id); /** Constructs a new radio id. */ virtual DMRRadioID *toRadioID() const; }; /** Represents the bitmap indicating which radio IDs are valid. */ class RadioIDBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ RadioIDBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ RadioIDBitmapElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0020; } }; /** Represents the base class for the settings elements in all AnyTone codeplugs. * This class only implements those few settings, common to all devices and encoded the same way. * It also defines all common settings as interfaces. * * Memory layout of encoded general settings (0xd0 bytes): * @verbinclude anytone_generalsettings.txt */ class GeneralSettingsElement: public Element { public: /** Possible automatic shutdown delays. */ enum class AutoShutdown { Off = 0, After10min = 1, After30min = 2, After60min = 3, After120min = 4, }; protected: /** Hidden constructor. */ GeneralSettingsElement(uint8_t *ptr, unsigned size); public: /** Resets the general settings. */ void clear(); /** Returns @c true, if the key tone is enabled. */ virtual bool keyToneEnabled() const = 0; /** Enables/disables the key-tone. */ virtual void enableKeyTone(bool enable) = 0; /** Returns @c true if the radio displays frequecies instead of channels is enabled. */ virtual bool displayFrequency() const; /** Enables/disables the frequency display. */ virtual void enableDisplayFrequency(bool enable); /** Returns @c true if auto key-lock is enabled. */ virtual bool autoKeyLock() const; /** Enables/disables auto key-lock. */ virtual void enableAutoKeyLock(bool enable); /** Returns the auto-shutdown delay in minutes. */ virtual Interval autoShutdownDelay() const; /** Sets the auto-shutdown delay in minutes. */ virtual void setAutoShutdownDelay(Interval min); /** Returns the boot display mode. */ virtual AnytoneBootSettingsExtension::BootDisplay bootDisplay() const; /** Sets the boot display mode. */ virtual void setBootDisplay(AnytoneBootSettingsExtension::BootDisplay mode); /** Returns @c true if boot password is enabled. */ virtual bool bootPassword() const; /** Enables/disables boot password. */ virtual void enableBootPassword(bool enable); /** Squelch level of VFO A, (0=off). */ virtual unsigned squelchLevelA() const; /** Returns the squelch level for VFO A, (0=off). */ virtual void setSquelchLevelA(unsigned level); /** Squelch level of VFO B, (0=off). */ virtual unsigned squelchLevelB() const; /** Returns the squelch level for VFO B, (0=off). */ virtual void setSquelchLevelB(unsigned level); /** Returns the VFO scan type. */ virtual AnytoneSettingsExtension::VFOScanType vfoScanType() const = 0; /** Sets the VFO scan type. */ virtual void setVFOScanType(AnytoneSettingsExtension::VFOScanType type) = 0; /** Returns the mirophone gain. */ virtual unsigned dmrMicGain() const = 0; /** Sets the microphone gain. */ virtual void setDMRMicGain(unsigned int gain) = 0; /** Returns the key function for a short press on the function key 1/A. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKeyAShort() const = 0; /** Sets the key function for a short press on the function key 1/A. */ virtual void setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) = 0; /** Returns the key function for a short press on the function key 2/B. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKeyBShort() const = 0; /** Sets the key function for a short press on the function key 2/B. */ virtual void setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) = 0; /** Returns the key function for a short press on the function key 3/C. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKeyCShort() const = 0; /** Sets the key function for a short press on the function key 3/C. */ virtual void setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) = 0; /** Returns the key function for a short press on the function key 1. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKey1Short() const = 0; /** Sets the key function for a short press on the function key 1. */ virtual void setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) = 0; /** Returns the key function for a short press on the function key 2. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKey2Short() const = 0; /** Sets the key function for a short press on the function key 2. */ virtual void setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) = 0; /** Returns the key function for a long press on the function key 1. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKeyALong() const = 0; /** Sets the key function for a long press on the function key 1. */ virtual void setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) = 0; /** Returns the key function for a long press on the function key 2. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKeyBLong() const = 0; /** Sets the key function for a long press on the function key 2. */ virtual void setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) = 0; /** Returns the key function for a long press on the function key 3. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKeyCLong() const = 0; /** Sets the key function for a long press on the function key 3. */ virtual void setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) = 0; /** Returns the key function for a long press on the function key 1. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKey1Long() const = 0; /** Sets the key function for a long press on the function key 1. */ virtual void setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) = 0; /** Returns the key function for a long press on the function key 2. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKey2Long() const = 0; /** Sets the key function for a long press on the function key 2. */ virtual void setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) = 0; /** Returns the long-press duration in ms. */ virtual Interval longPressDuration() const = 0; /** Sets the long-press duration in ms. */ virtual void setLongPressDuration(Interval ms) = 0; /** Returns @c true if the knob is locked. */ virtual bool knobLock() const = 0; /** Enables/disables the knob lock. */ virtual void enableKnobLock(bool enable) = 0; /** Returns @c true if the keypad is locked. */ virtual bool keypadLock() const = 0; /** Enables/disables the keypad lock. */ virtual void enableKeypadLock(bool enable) = 0; /** Returns @c true if the sidekeys are locked. */ virtual bool sidekeysLock() const = 0; /** Enables/disables the sidekeys lock. */ virtual void enableSidekeysLock(bool enable) = 0; /** Returns @c true if the "professional" key is locked. */ virtual bool keyLockForced() const = 0; /** Enables/disables the "professional" key lock. */ virtual void enableKeyLockForced(bool enable) = 0; public: /** Returns @c true if the VFO A is in VFO mode. */ virtual bool vfoModeA() const = 0; /** Enables/disables VFO mode for VFO A. */ virtual void enableVFOModeA(bool enable) = 0; /** Returns @c true if the VFO B is in VFO mode. */ virtual bool vfoModeB() const = 0; /** Enables/disables VFO mode for VFO B. */ virtual void enableVFOModeB(bool enable) = 0; /** Returns the memory zone for VFO A. */ virtual unsigned memoryZoneA() const = 0; /** Sets the memory zone for VFO A. */ virtual void setMemoryZoneA(unsigned zone) = 0; /** Returns the memory zone for VFO B. */ virtual unsigned memoryZoneB() const = 0; /** Sets the memory zone for VFO B. */ virtual void setMemoryZoneB(unsigned zone) = 0; /** Returns @c true if recording is enabled. */ virtual bool recording() const = 0; /** Enables/disables recording. */ virtual void enableRecording(bool enable) = 0; /** Returns the display brightness. */ virtual unsigned brightness() const = 0; /** Sets the display brightness. */ virtual void setBrightness(unsigned level) = 0; /** Returns @c true if GPS is enabled. */ virtual bool gps() const = 0; /** Enables/disables recording. */ virtual void enableGPS(bool enable) = 0; /** Returns @c true if SMS alert is enabled. */ virtual bool smsAlert() const = 0; /** Enables/disables SMS alert. */ virtual void enableSMSAlert(bool enable) = 0; /** Returns @c true if the active channel is VFO B. */ virtual bool activeChannelB() const = 0; /** Enables/disables VFO B as the active channel. */ virtual void enableActiveChannelB(bool enable) = 0; /** Returns @c true if sub channel is enabled. */ virtual bool subChannel() const = 0; /** Enables/disables sub channel. */ virtual void enableSubChannel(bool enable) = 0; /** Returns @c true if call alert is enabled. */ virtual bool callAlert() const = 0; /** Enables/disables call alert. */ virtual void enableCallAlert(bool enable) = 0; /** Returns the GPS time zone. */ virtual QTimeZone gpsTimeZone() const = 0; /** Sets the GPS time zone. */ virtual void setGPSTimeZone(const QTimeZone &zone) = 0; /** Returns @c true if the talk permit tone is enabled for digital channels. */ virtual bool dmrTalkPermit() const = 0; /** Returns @c true if the talk permit tone is enabled for digital channels. */ virtual bool fmTalkPermit() const = 0; /** Enables/disables the talk permit tone for digital channels. */ virtual void enableDMRTalkPermit(bool enable) = 0; /** Enables/disables the talk permit tone for analog channels. */ virtual void enableFMTalkPermit(bool enable) = 0; /** Returns @c true if the reset tone is enabled for digital calls. */ virtual bool dmrResetTone() const = 0; /** Enables/disables the reset tone for digital calls. */ virtual void enableDMRResetTone(bool enable) = 0; /** Returns @c true if the idle channel tone is enabled. */ virtual bool idleChannelTone() const = 0; /** Enables/disables the idle channel tone. */ virtual void enableIdleChannelTone(bool enable) = 0; /** Returns the menu exit time in seconds. */ virtual Interval menuExitTime() const = 0; /** Sets the menu exit time in seconds. */ virtual void setMenuExitTime(Interval intv) = 0; /** Returns @c true if the startup tone is enabled. */ virtual bool startupTone() const = 0; /** Enables/disables the startup tone. */ virtual void enableStartupTone(bool enable) = 0; /** Returns @c true if the call-end prompt is enabled. */ virtual bool callEndPrompt() const = 0; /** Enables/disables the call-end prompt. */ virtual void enableCallEndPrompt(bool enable) = 0; /** Returns the maximum volume. */ virtual unsigned maxSpeakerVolume() const = 0; /** Sets the maximum volume. */ virtual void setMaxSpeakerVolume(unsigned level) = 0; /** Returns @c true if get GPS position is enabled. */ virtual bool getGPSPosition() const = 0; /** Enables/disables get GPS position. */ virtual void enableGetGPSPosition(bool enable) = 0; /** Returns @c true if the volume change prompt is enabled. */ virtual bool volumeChangePrompt() const = 0; /** Enables/disables the volume change prompt. */ virtual void enableVolumeChangePrompt(bool enable) = 0; /** Returns the auto repeater offset direction for VFO A. */ virtual AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionA() const = 0; /** Sets the auto-repeater offset direction for VFO A. */ virtual void setAutoRepeaterDirectionA(AnytoneAutoRepeaterSettingsExtension::Direction dir) = 0; /** Returns the last-caller display mode. */ virtual AnytoneDisplaySettingsExtension::LastCallerDisplayMode lastCallerDisplayMode() const = 0; /** Sets the last-caller display mode. */ virtual void setLastCallerDisplayMode(AnytoneDisplaySettingsExtension::LastCallerDisplayMode mode) = 0; /** Returns @c true if the clock is shown. */ virtual bool displayClock() const = 0; /** Enables/disables clock display. */ virtual void enableDisplayClock(bool enable) = 0; /** Returns @c true if the audio is "enhanced". */ virtual bool enhanceAudio() const = 0; /** Enables/disables "enhanced" audio. */ virtual void enableEnhancedAudio(bool enable) = 0; /** Returns the minimum VFO scan frequency for the UHF band in Hz. */ virtual Frequency minVFOScanFrequencyUHF() const = 0; /** Sets the minimum VFO scan frequency for the UHF band in Hz. */ virtual void setMinVFOScanFrequencyUHF(Frequency hz) = 0; /** Returns the maximum VFO scan frequency for the UHF band in Hz. */ virtual Frequency maxVFOScanFrequencyUHF() const = 0; /** Sets the maximum VFO scan frequency for the UHF band in Hz. */ virtual void setMaxVFOScanFrequencyUHF(Frequency hz) = 0; /** Returns the minimum VFO scan frequency for the VHF band in Hz. */ virtual Frequency minVFOScanFrequencyVHF() const = 0; /** Sets the minimum VFO scan frequency for the VHF band in Hz. */ virtual void setMinVFOScanFrequencyVHF(Frequency hz) = 0; /** Returns the maximum VFO scan frequency for the VHF band in Hz. */ virtual Frequency maxVFOScanFrequencyVHF() const = 0; /** Sets the maximum VFO scan frequency for the VHF band in Hz. */ virtual void setMaxVFOScanFrequencyVHF(Frequency hz) = 0; /** Returns @c true if the auto-repeater offset frequency for UHF is set. */ virtual bool hasAutoRepeaterOffsetFrequencyIndexUHF() const = 0; /** Returns the auto-repeater offset frequency index for UHF. */ virtual unsigned autoRepeaterOffsetFrequencyIndexUHF() const = 0; /** Sets the auto-repeater offset frequency index for UHF. */ virtual void setAutoRepeaterOffsetFrequenyIndexUHF(unsigned idx) = 0; /** Clears the auto-repeater offset frequency index for UHF. */ virtual void clearAutoRepeaterOffsetFrequencyIndexUHF() = 0; /** Returns @c true if the auto-repeater offset frequency for VHF is set. */ virtual bool hasAutoRepeaterOffsetFrequencyIndexVHF() const = 0; /** Returns the auto-repeater offset frequency index for UHF. */ virtual unsigned autoRepeaterOffsetFrequencyIndexVHF() const = 0; /** Sets the auto-repeater offset frequency index for VHF. */ virtual void setAutoRepeaterOffsetFrequenyIndexVHF(unsigned idx) = 0; /** Clears the auto-repeater offset frequency index for VHF. */ virtual void clearAutoRepeaterOffsetFrequencyIndexVHF() = 0; /** Returns @c true if the current contact is shown. */ virtual bool showCurrentContact() const = 0; /** Enables/disables display of current contact. */ virtual void enableShowCurrentContact(bool enable) = 0; /** Returns the call-tone melody. */ virtual void callToneMelody(Melody &melody) const = 0; /** Sets the call-tone melody. */ virtual void setCallToneMelody(const Melody &melody) = 0; /** Returns the idle-tone melody. */ virtual void idleToneMelody(Melody &melody) const = 0; /** Sets the idle-tone melody. */ virtual void setIdleToneMelody(const Melody &melody) = 0; /** Returns the reset-tone melody. */ virtual void resetToneMelody(Melody &melody) const = 0; /** Sets the reset-tone melody. */ virtual void setResetToneMelody(const Melody &melody) = 0; /** Returns @c true if the default boot channel is enabled. */ virtual bool defaultChannel() const = 0; /** Enables/disables default boot channel. */ virtual void enableDefaultChannel(bool enable) = 0; /** Returns the default zone index (0-based) for VFO A. */ virtual unsigned defaultZoneIndexA() const = 0; /** Sets the default zone (0-based) for VFO A. */ virtual void setDefaultZoneIndexA(unsigned idx) = 0; /** Returns the default zone index (0-based) for VFO B. */ virtual unsigned defaultZoneIndexB() const = 0; /** Sets the default zone (0-based) for VFO B. */ virtual void setDefaultZoneIndexB(unsigned idx) = 0; /** Returns @c true if the default channel for VFO A is VFO. */ virtual bool defaultChannelAIsVFO() const = 0; /** Returns the default channel index for VFO A. * Must be within default zone. If 0xff, default channel is VFO. */ virtual unsigned defaultChannelAIndex() const = 0; /** Sets the default channel index for VFO A. */ virtual void setDefaultChannelAIndex(unsigned idx) = 0; /** Sets the default channel for VFO A to be VFO. */ virtual void setDefaultChannelAToVFO() = 0; /** Returns @c true if the default channel for VFO B is VFO. */ virtual bool defaultChannelBIsVFO() const = 0; /** Returns the default channel index for VFO B. * Must be within default zone. If 0xff, default channel is VFO. */ virtual unsigned defaultChannelBIndex() const = 0; /** Sets the default channel index for VFO B. */ virtual void setDefaultChannelBIndex(unsigned idx) = 0; /** Sets the default channel for VFO B to be VFO. */ virtual void setDefaultChannelBToVFO() = 0; /** Returns @c true if the call is displayed instead of the name. */ virtual bool displayCall() const = 0; /** Enables/disables call display. */ virtual void enableDisplayCall(bool enable) = 0; /** Returns the display color for callsigns. */ virtual AnytoneDisplaySettingsExtension::Color callDisplayColor() const = 0; /** Sets the display color for callsigns. */ virtual void setCallDisplayColor(AnytoneDisplaySettingsExtension::Color color) = 0; /** Returns @c true if the GPS units are imperial. */ virtual bool gpsUnitsImperial() const = 0; /** Enables/disables imperial GPS units. */ virtual void enableGPSUnitsImperial(bool enable) = 0; /** Returns the minimum frequency in Hz for the auto-repeater range in VHF band. */ virtual Frequency autoRepeaterMinFrequencyVHF() const = 0; /** Sets the minimum frequency in Hz for the auto-repeater range in VHF band. */ virtual void setAutoRepeaterMinFrequencyVHF(Frequency Hz) = 0; /** Returns the maximum frequency in Hz for the auto-repeater range in VHF band. */ virtual Frequency autoRepeaterMaxFrequencyVHF() const = 0; /** Sets the maximum frequency in Hz for the auto-repeater range in VHF band. */ virtual void setAutoRepeaterMaxFrequencyVHF(Frequency Hz) = 0; /** Returns the minimum frequency in Hz for the auto-repeater range in UHF band. */ virtual Frequency autoRepeaterMinFrequencyUHF() const = 0; /** Sets the minimum frequency in Hz for the auto-repeater range in UHF band. */ virtual void setAutoRepeaterMinFrequencyUHF(Frequency Hz) = 0; /** Returns the maximum frequency in Hz for the auto-repeater range in UHF band. */ virtual Frequency autoRepeaterMaxFrequencyUHF() const = 0; /** Sets the maximum frequency in Hz for the auto-repeater range in UHF band. */ virtual void setAutoRepeaterMaxFrequencyUHF(Frequency Hz) = 0; /** Returns the auto-repeater direction for VFO B. */ virtual AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionB() const = 0; /** Sets the auto-repeater direction for VFO B. */ virtual void setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir) = 0; /** Returns @c true if the last heard is shown while pressing PTT. */ virtual bool showLastHeard() const = 0; /** Enables/disables showing last heard. */ virtual void enableShowLastHeard(bool enable) = 0; /** Returns @c true if the last caller is kept when changing channel. */ virtual bool keepLastCaller() const = 0; /** Enables/disables keeping the last caller when changing the channel. */ virtual void enableKeepLastCaller(bool enable) = 0; /** Encodes the general settings. */ virtual bool fromConfig(const Flags &flags, Context &ctx); /** Updates the abstract config from general settings. */ virtual bool updateConfig(Context &ctx); /** Links the general settings. */ virtual bool linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Internal used offsets within the element. */ struct Offset : public Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int displayMode() { return 0x0001; } static constexpr unsigned int autoKeyLock() { return 0x0002; } static constexpr unsigned int autoShutDown() { return 0x0003; } static constexpr unsigned int bootDisplay() { return 0x0006; } static constexpr unsigned int bootPassword() { return 0x0007; } static constexpr unsigned int squelchLevelA() { return 0x0009; } static constexpr unsigned int squelchLevelB() { return 0x000a; } /// @endcond }; }; /** Represents the base class for the extended settings element in many AnyTone codeplugs. That * is, every device after the D868UVE. It provides additional settings to the * @c AnytoneGeneralSettingsElement. * * As these elements differ heavily from device to device, there is no common encoding. This * class only defines an interface to get/set common settings. */ class ExtendedSettingsElement: public Element { protected: /** Hidden constructor. */ ExtendedSettingsElement(uint8_t *ptr, unsigned size); public: /** Returns @c true if the talker alias is sent. */ virtual bool sendTalkerAlias() const = 0; /** Enables/disables sending the talker alias. */ virtual void enableSendTalkerAlias(bool enable) = 0; /** Returns the talker alias source. */ virtual AnytoneDMRSettingsExtension::TalkerAliasSource talkerAliasSource() const = 0; /** Sets the talker alias source. */ virtual void setTalkerAliasSource(AnytoneDMRSettingsExtension::TalkerAliasSource mode) = 0; /** Returns the talker alias encoding. */ virtual AnytoneDMRSettingsExtension::TalkerAliasEncoding talkerAliasEncoding() const = 0; /** Sets the talker alias encoding. */ virtual void setTalkerAliasEncoding(AnytoneDMRSettingsExtension::TalkerAliasEncoding encoding) = 0; /** Returns the color of the channel name for VFO B. */ virtual AnytoneDisplaySettingsExtension::Color channelBNameColor() const = 0; /** Sets the channel name color for the VFO B. */ virtual void setChannelBNameColor(AnytoneDisplaySettingsExtension::Color) = 0; /** Returns the color of the zone name for VFO A. */ virtual AnytoneDisplaySettingsExtension::Color zoneANameColor() const = 0; /** Sets the zone name color for the VFO A. */ virtual void setZoneANameColor(AnytoneDisplaySettingsExtension::Color) = 0; /** Returns the color of the zone name for VFO B. */ virtual AnytoneDisplaySettingsExtension::Color zoneBNameColor() const = 0; /** Sets the zone name color for the VFO B. */ virtual void setZoneBNameColor(AnytoneDisplaySettingsExtension::Color) = 0; /** Encodes the settings from the config. */ virtual bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Update config from settings. */ virtual bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack()); /** Link config from settings extension. */ virtual bool linkConfig(Context &ctx, const ErrorStack &err=ErrorStack()); }; /** Represents the base class for zone channel list for all AnyTone codeplugs. * Zone channel lists assign a default channel to each zone for VFO A and B. * * Memory layout of ecoded zone channel lists (size 0x400 bytes): * @verbinclude anytone_zonechannellist.txt */ class ZoneChannelListElement: public Element { protected: /** Hidden constructor. */ ZoneChannelListElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ ZoneChannelListElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0400; } /** Resets the zone channel list. */ void clear(); /** Returns @c true if the channel index for VFO A is set for the n-th zone. */ virtual bool hasChannelA(unsigned n) const; /** Returns the channel index (0-based) for VFO A for the n-th zone. */ virtual unsigned channelIndexA(unsigned n) const; /** Sets the channel index (0-based) for VFO A for the n-th zone. */ virtual void setChannelIndexA(unsigned n, unsigned idx); /** Clears the channel index (0-based) for VFO A for the n-th zone. */ virtual void clearChannelIndexA(unsigned n); /** Returns @c true if the channel index for VFO B is set for the n-th zone. */ virtual bool hasChannelB(unsigned n) const; /** Returns the channel index (0-based) for VFO B for the n-th zone. */ virtual unsigned channelIndexB(unsigned n) const; /** Sets the channel index (0-based) for VFO B for the n-th zone. */ virtual void setChannelIndexB(unsigned n, unsigned idx); /** Clears the channel index (0-based) for VFO B for the n-th zone. */ virtual void clearChannelIndexB(unsigned n); }; /** Represents the bitmap indcating which zones are valid. */ class ZoneBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ ZoneBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ZoneBitmapElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0020; } }; /** Represents the base class of the boot settings for all AnyTone codeplug. * * Memory layout of encoded boot settings (size 0x0030): * @verbinclude anytone_bootsettings.txt */ class BootSettingsElement: public Element { protected: /** Hidden constructor. */ BootSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ BootSettingsElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0030; } /** Resets the boot settings. */ void clear(); /** Returns the first intro line. */ virtual QString introLine1() const; /** Sets the first intro line. */ virtual void setIntroLine1(const QString &txt); /** Returns the second intro line. */ virtual QString introLine2() const; /** Sets the second intro line. */ virtual void setIntroLine2(const QString &txt); /** Returns the password. */ virtual QString password() const; /** Sets the password. */ virtual void setPassword(const QString &txt); /** Updates the general settings from the given abstract configuration. */ virtual bool fromConfig(const Flags &flags, Context &ctx); /** Updates the abstract configuration from this general settings. */ virtual bool updateConfig(Context &ctx); }; /** Represents the base class of DMR APRS settings for all AnyTone codeplugs. * * Memory encoding of the DMR APRS settings (size 0x0030 bytes): * @verbinclude anytone_dmraprssettings.txt */ class DMRAPRSSettingsElement: public Element { protected: /** Hidden constructor. */ DMRAPRSSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit DMRAPRSSettingsElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0030; } /** Resets the APRS settings. */ void clear(); /** Returns the Manual TX interval in seconds. */ virtual unsigned manualInterval() const; /** Sets the manual TX interval in seconds. */ virtual void setManualInterval(unsigned sec); /** Returns @c true if the automatic APRS is enabled. */ virtual bool automatic() const; /** Returns the automatic transmit interval in seconds. */ virtual unsigned automaticInterval() const; /** Sets the automatic transmit interval in seconds. */ virtual void setAutomaticInterval(unsigned sec); /** Disables the automatic APRS. To enable it, set an interval. */ virtual void disableAutomatic(); /** Returns @c true if the fixed location beacon is enabled. */ virtual bool fixedLocation() const; /** Returns the location of the fixed position. */ virtual QGeoCoordinate location() const; /** Sets the location of the fixed position. */ virtual void setLocation(const QGeoCoordinate &pos); /** Enables/disables fixed location beacon. */ virtual void enableFixedLocation(bool enable); /** Returns the transmit power. */ virtual Channel::Power power() const; /** Sets the transmit power. */ virtual void setPower(Channel::Power power); /** Returns @c true if the n-th channel is set. */ virtual bool hasChannel(unsigned n) const; /** Returns @c true if the n-th channel is VFO A. */ virtual bool channelIsVFOA(unsigned n) const; /** Returns @c true if the n-th channel is VFO B. */ virtual bool channelIsVFOB(unsigned n) const; /** Returns @c true if the n-th channel is selected channel. */ virtual bool channelIsSelected(unsigned n) const; /** Returns the index of the n-th channel. */ virtual unsigned channelIndex(unsigned n) const; /** Sets the n-th channel index. */ virtual void setChannelIndex(unsigned n, unsigned idx); /** Sets the n-th channel to VFO A. */ virtual void setChannelVFOA(unsigned n); /** Sets the n-th channel to VFO B. */ virtual void setChannelVFOB(unsigned n); /** Sets the n-th channel to selected channel. */ virtual void setChannelSelected(unsigned n); /** Resets the n-th channel. */ virtual void clearChannel(unsigned n); /** Returns the destination DMR ID to send the APRS information to. */ virtual unsigned destination() const; /** Sets the destination DMR ID to send the APRS information to. */ virtual void setDestination(unsigned id); /** Returns the call type. */ virtual DMRContact::Type callType() const; /** Sets the call type. */ virtual void setCallType(DMRContact::Type type); /** Returns @c true if the timeslot of the channel is overridden. */ virtual bool timeSlotOverride() const; /** Returns the timeslot (only valid if @c timeSlotOverride returns @c true). */ virtual DMRChannel::TimeSlot timeslot() const; /** Sets the timeslot. */ virtual void overrideTimeSlot(DMRChannel::TimeSlot ts); /** Disables TS override. */ virtual void disableTimeSlotOverride(); /** Updates the GPS settings from the given config. */ virtual bool fromConfig(const Flags &flags, Context &ctx); /** Creates GPS system from this GPS settings. */ virtual bool createGPSSystem(uint8_t i, Context &ctx); /** Links GPS system from this GPS settings. */ virtual bool linkGPSSystem(uint8_t i, Context &ctx); }; /** Represents the base class of a DMR APRS message for all AnyTone codeplugs. */ class DMRAPRSMessageElement: public Element { protected: /** Hidden constructor. */ DMRAPRSMessageElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DMRAPRSMessageElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0030; } void clear(); /** Returns the message. */ virtual QString message() const; /** Sets the message. */ void setMessage(const QString &message); /** Encodes the message. */ virtual bool fromConfig(Codeplug::Flags flags, Context &ctx); /** Decodes the message. */ virtual bool updateConfig(Context &ctx) const; public: /** Some limits for the message. */ struct Limit { static constexpr unsigned int length() { return 32; } ///< Maximum message length. }; protected: /** Some internal used offset. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int message() { return 0x0000; } /// @endcond }; }; /** Represents the table of repeater offset frequencies. * * Memory representation of the offset frequency table (size 0x03F0 bytes): * @verbinclude anytone_repeateroffsetfrequencies.txt */ class RepeaterOffsetListElement: public Element { protected: /** Hidden constructor. */ RepeaterOffsetListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit RepeaterOffsetListElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x03f0; } void clear(); /** Returns @c true, if the n-th offset frequency is set. */ virtual bool isSet(unsigned int n) const; /** Returns the n-th offset frequency. */ virtual Frequency offset(unsigned int n) const; /** Sets the n-th offset frequency. */ virtual void setOffset(unsigned int n, Frequency freq); /** Clears the n-th offset frequency. */ virtual void clearOffset(unsigned int n); public: /** Some limits for the offset frequency table. */ struct Limit { static constexpr unsigned int numEntries() { return 250; } ///< Max number of entries in the table. }; protected: /** Some internal used offsets. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int frequencies() { return 0x0000; } static constexpr unsigned int betweenFrequencies() { return sizeof(uint32_t); } /// @endcond }; }; /** Represents the base class of prefabricated message linked list for all AnyTone codeplugs. * This element is some weird linked list that determines some order for the prefabricated * SMS messages. * * Memory encoding of the message list (size 0x0010 bytes): * @verbinclude anytone_messagelist.txt */ class MessageListElement: public Element { protected: /** Hidden constructor. */ MessageListElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit MessageListElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0010; } /** Clears the message list item. */ void clear(); /** Returns @c true if there is a next message (EOL otherwise). */ virtual bool hasNext() const; /** Returns the index of the next message in list. */ virtual unsigned next() const; /** Sets the index of the next message in list. */ virtual void setNext(unsigned idx); /** Clears the next message index. */ virtual void clearNext(); /** Returns @c true if there is a message index. */ virtual bool hasIndex() const; /** Returns the index of the message. */ virtual unsigned index() const; /** Sets the index of the message. */ virtual void setIndex(unsigned idx); /** Clears the index of the message. */ virtual void clearIndex(); }; /** Represents base class of a preset message for all AnyTone codeplugs. * * Memory encoding of the message (0x100 bytes): * @verbinclude anytone_message.txt */ class MessageElement: public Element { protected: /** Hidden constructor. */ MessageElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ MessageElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0100; } /** Resets the message. */ void clear(); /** Returns the message text. */ virtual QString message() const; /** Sets the message text. */ virtual void setMessage(const QString &msg); }; /** Represents the bytemap indicating which message is valid. */ class MessageBytemapElement: public InvertedBytemapElement { protected: /** Hidden constructor. */ MessageBytemapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ MessageBytemapElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0090; } }; /** Represents base class of a analog quick call entry for all AnyTone codeplugs. * * Memory encoding of the message (0x0002 bytes): * @verbinclude anytone_analogquickcall.txt */ class AnalogQuickCallElement: public Element { public: /** Analog quick-call types. */ enum class Type { None = 0, ///< None, quick-call disabled. DTMF = 1, ///< DTMF call. TwoTone = 2, ///< 2-tone call. FiveTone = 3 ///< 5-tone call }; protected: /** Hidden constructor. */ AnalogQuickCallElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit AnalogQuickCallElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0002; } /** Resets the quick call entry. */ void clear(); /** Returns the call type. */ virtual Type type() const; /** Sets the type of the quick call. */ virtual void setType(Type type); /** Returns @c true if an analog contact index is set. */ virtual bool hasContactIndex() const; /** Returns the analog contact index. */ virtual unsigned contactIndex() const; /** Sets the analog contact index. */ virtual void setContactIndex(unsigned idx); /** Clears the contact index. */ virtual void clearContactIndex(); }; /** Implements the list of analog quick-call settings for all AnyTone codeplugs. * * Memory reresentation of the quick-call settings (size 0x0100 bytes): * @verbinclude anytone_analogquickcalls.txt */ class AnalogQuickCallsElement: public Element { protected: /** Hidden constructor. */ AnalogQuickCallsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ AnalogQuickCallsElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0100; } /** Clears the quick calls. */ void clear(); /** Returns a pointer to the n-th entry. */ uint8_t *quickCall(unsigned int n) const; public: /** Some limits for the quick calls. */ struct Limit { static constexpr unsigned int numEntries() { return 4; } ///< The maximum number of quick-call entries. }; protected: /** Some offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int quickCalls() { return 0x0000; } /// @endcond }; }; /** Implements the list of status messages for all AnyTone codeplugs. * * Memory reresentation of the status messages (size 0x0400 bytes): * @verbinclude anytone_statusmessages.txt */ class StatusMessagesElement: public Element { protected: /** Hidden constructor. */ StatusMessagesElement(uint8_t *ptr, size_t size); public: /** Constructor. */ StatusMessagesElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0400; } void clear(); /** Returns the n-th status message. */ virtual QString message(unsigned int n) const; /** Sets the n-th status message. */ virtual void setMessage(unsigned int n, const QString &msg); public: /** Some limits. */ struct Limit { static constexpr unsigned int numMessages() { return 32; } ///< Maximum number of messages. static constexpr unsigned int messageLength() { return 32; } ///< Maximum length of the messages. }; protected: /** Some internal offsets. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int messages() { return 0x0000; } static constexpr unsigned int betweenMessages() { return 0x0020; } /// @endcond }; }; /** Represents the bitmap, indicating which status messages are valid. */ class StatusMessageBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ StatusMessageBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ StatusMessageBitmapElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0010; } }; /** Represents the base class of a hot-key setting entry for all AnyTone codeplugs. * * Memory encoding of a hot-key setting (size 0x0030 bytes): * @verbinclude anytone_hotkey.txt */ class HotKeyElement: public Element { public: /** Hot-key types. */ enum class Type { Call = 0, ///< Perform a call. Menu = 1 ///< Show a menu item. }; /** Possible menu items. */ enum class MenuItem { SMS = 1, ///< Show SMS menu. NewSMS = 2, ///< Create new SMS. HotText = 3, ///< Send a hot-text. Inbox = 4, ///< Show SMS inbox. Outbox = 5, ///< Show SMS outbox. Contacts = 6, ///< Show contact list. ManualDial = 7, ///< Show manual dial. CallLog = 8 ///< Show call log. }; /** Possible call types. */ enum class CallType { Analog = 0, ///< Perform an analog call. Digital = 1 ///< Perform a digital call. }; /** Possible digital call sub-types. */ enum class DigiCallType { Off = 0xff, ///< Call disabled. GroupCall = 0, ///< Perform a group call. PrivateCall= 1, ///< Perform private call. AllCall = 2, ///< Perform all call. HotText = 3, ///< Send a text message. CallTip = 4, ///< Send a call tip (?). StatusMessage = 5 ///< Send a state message. }; protected: /** Hidden constructor. */ HotKeyElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit HotKeyElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0030; } /** Resets the hot-key entry. */ void clear(); /** Returns the type of the hot-key entry. */ virtual Type type() const; /** Sets the type of the hot-key entry. */ virtual void setType(Type type); /** If @c type returns @c Type::Menu, returns the menu item. */ virtual MenuItem menuItem() const; /** Sets the menu item. For this setting to have an effect, the @c type must be set to * @c Type::Menu. */ virtual void setMenuItem(MenuItem item); /** If @c type returns @c Type::Call, returns the type of the call. */ virtual CallType callType() const; /** Sets the call type. For this settings to have an effect, the type must be set to * @c Type::Call. */ virtual void setCallType(CallType type); /** If @c type returns @c Type::Call and @c callType @c CalLType::Digital, returns the digital * call type. */ virtual DigiCallType digiCallType() const; /** Sets the digital call type. For this setting to have an effect, the @c type must be * @c Type::Call and @c callType must be @c CallType::Digital. */ virtual void setDigiCallType(DigiCallType type); /** Returns @c true if the contact index is set. */ virtual bool hasContactIndex() const; /** If @c type is @c Type::Call, returns the contact index. This is either an index of an * analog quick call if @c callType is CallType::Analog or a contact index if @c callType is * @c CallType::Digital. If set to 0xffffffff the index is invalid. */ virtual unsigned contactIndex() const; /** Sets the contact index. This can either be an index of an analog quick-call or a contact * index. */ virtual void setContactIndex(unsigned idx); /** Clears the contact index. */ virtual void clearContactIndex(); /** Returns @c true if a message index is set. */ virtual bool hasMessageIndex() const; /** Returns the message index. This can either be an index of an SMS or an index of a status * message. */ virtual unsigned messageIndex() const; /** Sets the message index. */ virtual void setMessageIndex(unsigned idx); /** Clears the message index. */ virtual void clearMessageIndex(); }; /** Represents the list of hot-key settings for all AnyTone codeplugs. * * See @c HotKeyElement for encoding of each element. * * Memory encoding of the hot-key settings (size 0x0360 bytes): * @verbinclude anytone_hotkeysettings.txt */ class HotKeySettingsElement: public Element { protected: /** Hidden constructor. */ HotKeySettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ HotKeySettingsElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0360; } void clear(); /** Returns a pointer to the n-th hot key setting. */ virtual uint8_t *hotKeySetting(unsigned int n) const; public: /** Some limits for this element. */ struct Limit { static constexpr unsigned int numEntries() { return 18; } ///< Maximum number of hot-key entries. }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int hotKeySettings() { return 0x0000; } static constexpr unsigned int betweenHotKeySettings() { return HotKeySettingsElement::size(); } /// @endcond }; }; /** Represents the base class of alarm setting entry for all AnyTone codeplugs. * * Memory encoding of an alarm setting (size 0x0020 bytes): * @verbinclude anytone_alarmsetting.txt */ class AlarmSettingElement: public Element { public: /** Represents the base class of an analog alarm setting for all AnyTone codeplugs. * * Memory representation of an analog alarm setting (size 0x000a bytes): * @verbinclude anytone_analogalarm.txt */ class AnalogAlarm: public Element { public: /** Possible analog alarm types. */ enum class Action { None = 0, ///< No alarm at all. Background = 1, ///< Transmit and background. TXAlarm = 2, ///< Transmit and alarm Both = 3, ///< Both? }; /** Possible alarm signalling types. */ enum class ENIType { None = 0, ///< No alarm code signalling. DTMF = 1, ///< Send alarm code as DTMF. FiveTone = 2 ///< Send alarm code as 5-tone. }; protected: /** Hidden constructor. */ AnalogAlarm(uint8_t *ptr, unsigned size); public: /** Constructor. */ AnalogAlarm(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x000a; } /** Resets the alarm. */ void clear(); /** Returns the alarm action. */ virtual Action action() const; /** Sets the alarm action. */ virtual void setAction(Action action); /** Returns the encoding type. */ virtual ENIType encodingType() const; /** Sets the encoding type. */ virtual void setEncodingType(ENIType type); /** Returns the emergency ID index. */ virtual unsigned emergencyIndex() const; /** Sets the emergency ID index. */ virtual void setEmergencyIndex(unsigned idx); /** Returns the alarm duration in seconds. */ virtual unsigned duration() const; /** Sets the alarm duration in seconds. */ virtual void setDuration(unsigned sec); /** Returns the TX duration in seconds. */ virtual unsigned txDuration() const; /** Sets the TX duration in seconds. */ virtual void setTXDuration(unsigned sec); /** Returns the RX duration in seconds. */ virtual unsigned rxDuration() const; /** Sets the RX duration in seconds. */ virtual void setRXDuration(unsigned sec); /** Returns @c true if the alarm channel is the selected channel. */ virtual bool channelIsSelected() const; /** Returns the channel index. */ virtual unsigned channelIndex() const; /** Sets the channel index. */ virtual void setChannelIndex(unsigned idx); /** Sets the alarm channel to the selected channel. */ virtual void setChannelSelected(); /** Returns @c true if the alarm is repeated continuously. */ virtual bool repeatContinuously() const; /** Returns the number of alarm repetitions. */ virtual unsigned repetitions() const; /** Sets the number of alarm repetitions. */ virtual void setRepetitions(unsigned num); /** Sets the alarm to be repeated continuously. */ virtual void setRepatContinuously(); }; /** Represents the base class of an digital alarm setting for all AnyTone codeplugs. * * Memory representation of a digital alarm setting (size 0x000c bytes): * @verbinclude anytone_digitalalarm.txt */ class DigitalAlarm: public Element { public: /** Possible alarm types. */ enum class Action { None = 0, ///< No alarm at all. Background = 1, ///< Transmit and background. NonLocal = 2, ///< Transmit and non-local alarm. Local = 3, ///< Transmit and local alarm. }; protected: /** Hidden constructor. */ DigitalAlarm(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit DigitalAlarm(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x000c; } /** Resets the digital alarm settings. */ void clear(); /** Returns the alarm action. */ virtual Action action() const; /** Sets the alarm action. */ virtual void setAction(Action action); /** Returns the alarm duration in seconds. */ virtual unsigned duration() const; /** Sets the alarm duration in seconds. */ virtual void setDuration(unsigned sec); /** Returns the TX duration in seconds. */ virtual unsigned txDuration() const; /** Sets the TX duration in seconds. */ virtual void setTXDuration(unsigned sec); /** Returns the RX duration in seconds. */ virtual unsigned rxDuration() const; /** Sets the RX duration in seconds. */ virtual void setRXDuration(unsigned sec); /** Returns @c true if the alarm channel is the selected channel. */ virtual bool channelIsSelected() const; /** Returns the channel index. */ virtual unsigned channelIndex() const; /** Sets the channel index. */ virtual void setChannelIndex(unsigned idx); /** Sets the alarm channel to the selected channel. */ virtual void setChannelSelected(); /** Returns @c true if the alarm is repeated continuously. */ virtual bool repeatContinuously() const; /** Returns the number of alarm repetitions. */ virtual unsigned repetitions() const; /** Sets the number of alarm repetitions. */ virtual void setRepetitions(unsigned num); /** Sets the alarm to be repeated continuously. */ virtual void setRepatContinuously(); /** Returns voice broadcast duration in minutes. */ virtual unsigned voiceBroadcastDuration() const; /** Sets voice broadcast duration in minutes. */ virtual void setVoiceBroadcastDuration(unsigned min); /** Returns area broadcast duration in minutes. */ virtual unsigned areaBroadcastDuration() const; /** Sets area broadcast duration in minutes. */ virtual void setAreaBroadcastDuration(unsigned min); /** Returns @c true if the VOX gets enabled. */ virtual bool vox() const; /** Enables/disables the VOX for alarms. */ virtual void enableVOX(bool enable); /** Returns @c true if alarms gets received enabled. */ virtual bool rxAlarm() const; /** Enables/disables the reception of alarms. */ virtual void enableRXAlarm(bool enable); }; protected: /** Hidden constructor. */ AlarmSettingElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ AlarmSettingElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0020; } /** Clears the alarm settings. */ void clear(); /** Returns a pointer to the analog alarm settings. */ virtual uint8_t *analog() const; /** Returns a pointer to the digital alarm settings. */ virtual uint8_t *digital() const; protected: /** Internal offsets within the element */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int analog() { return 0x0000; } static constexpr unsigned int digital() { return 0x000a; } /// @endcond }; }; /** Represents the base class of digital alarm setting extension for all AnyTone codeplugs. * * Memory encoding of a digital alarm setting extension (size 0x0030 bytes): * @verbinclude anytone_digitalalarmextension.txt */ class DigitalAlarmExtensionElement: public Element { protected: /** Hidden constructor. */ DigitalAlarmExtensionElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ DigitalAlarmExtensionElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0030; } /** Clears the settings. */ void clear(); /** Returns the call type. */ virtual DMRContact::Type callType() const; /** Sets the call type. */ virtual void setCallType(DMRContact::Type type); /** Returns the destination DMR number. */ virtual unsigned destination() const; /** Sets the destination DMR number. */ virtual void setDestination(unsigned number); protected: /** Internal used offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int callType() { return 0x0000; } static constexpr unsigned int destination() { return 0x0023; } /// @endcond }; }; /** Represents the base-class for 5Tone IDs for all AnyTone codeplugs. * * Memory encoding of the ID (size 0x0020 bytes): * @verbinclude anytone_5toneid.txt */ class FiveToneIDElement: public Element { public: /** Possible 5-tone encoding standards. */ enum class Standard { ZVEI1 = 0, ZVEI2, ZVEI3, PZVEI, DZVEI, PDZVEI, CCIR1, CCIR2, PCCIR, EEA, EuroSignal, NATEL, MODAT, CCITT, EIA }; protected: /** Hidden constructor. */ FiveToneIDElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ FiveToneIDElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0020; } /** Clears the ID. */ void clear(); /** Returns the 5Tone encoding standard. */ virtual Standard standard() const; /** Sets the encoding standard. */ virtual void setStandard(Standard std); /** Returns the tone duration in ms. */ virtual unsigned toneDuration() const; /** Sets the tone duration in ms. */ virtual void setToneDuration(unsigned ms); /** Returns the ID. */ virtual QString id() const; /** Sets the ID. */ virtual void setID(const QString &id); /** Returns the name. */ virtual QString name() const; /** Sets the name. */ virtual void setName(const QString &name); }; /** Represents the bitmap indicating which five-tone IDs are valid. */ class FiveToneIDBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ FiveToneIDBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ FiveToneIDBitmapElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0010; } }; /** Represents the list of five-tone IDs. * * Memory encoding of the ID list (size 0x0c80 bytes): * @verbinclude anytone_5toneidlist.txt */ class FiveToneIDListElement: public Element { protected: /** Hidden constructor. */ FiveToneIDListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ FiveToneIDListElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0c80; } void clear(); /** Returns a pointer to the n-th five-tone ID. */ virtual uint8_t *member(unsigned int n) const; public: /** Some limits for the list. */ struct Limit { static constexpr unsigned int numEntries() { return 100; } ///< Maximum number of entries. }; }; /** Represents the base-class for 5Tone function for all AnyTone codeplugs. * * Memory encoding of the function (size 0x0020 bytes): * @verbinclude anytone_5tonefunction.txt */ class FiveToneFunctionElement: public Element { public: /** Possible function being performed on 5-tone decoding. */ enum class Function { OpenSquelch=0, CallAll, EmergencyAlarm, RemoteKill, RemoteStun, RemoteWakeup, GroupCall }; /** Possible responses to 5-tone decoding. */ enum class Response { None=0, Tone, ToneRespond }; protected: /** Hidden constructor. */ FiveToneFunctionElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit FiveToneFunctionElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0020; } /** Clears the function settings. */ void clear(); /** Returns the function. */ virtual Function function() const; /** Sets the function. */ virtual void setFunction(Function function); /** Returns the response. */ virtual Response response() const; /** Sets the response. */ virtual void setResponse(Response response); /** Returns the ID. */ virtual QString id() const; /** Sets the ID. */ virtual void setID(const QString &id); /** Returns the name. */ virtual QString name() const; /** Sets the name. */ virtual void setName(const QString &name); }; /** Represents the list of five-tone functions for all AnyTone codeplugs. * * Memory representation of the function list (size 0x0200 bytes): * @verbinclude anytone_5tonefunctionlist.txt */ class FiveToneFunctionListElement: public Element { protected: /** Hidden constructor. */ FiveToneFunctionListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ FiveToneFunctionListElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0200; } void clear(); /** Returns the pointer to the n-th function setting. */ virtual uint8_t *function(unsigned int n) const; public: /** Some limits for the list. */ struct Limit { static constexpr unsigned int numFunctions() { return 16; } ///< The max number of functions. }; }; /** Represents the base-class for 5Tone settings for all AnyTone codeplugs. * * Memory encoding of the settings (size 0x0080 bytes): * @verbinclude anytone_5tonesettings.txt */ class FiveToneSettingsElement: public Element { public: /** Possible responses to decoded 5-tone codes. */ enum class Response { None = 0, Tone, ToneRespond }; /** Possible 5-tone encoding standards. */ typedef enum FiveToneIDElement::Standard Standard; protected: /** Hidden constructor. */ FiveToneSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ FiveToneSettingsElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0080; } /** Resets the 5tone settings. */ void clear(); /** Returns the decoding response. */ virtual Response decodingResponse() const; /** Sets the decoding response. */ virtual void setDecodingResponse(Response response); /** Returns the decoding standard. */ virtual Standard decodingStandard() const; /** Sets the decoding standard. */ virtual void setDecodingStandard(Standard standard); /** Returns the decoding tone duration in ms. */ virtual unsigned decodingToneDuration() const; /** Sets the decoding tone duration in ms. */ virtual void setDecodingToneDuration(unsigned ms); /** Returns the 5tone radio ID. */ virtual QString id() const; /** Sets the 5tone radio ID. */ virtual void setID(const QString &id); /** Returns the post-encode delay in ms. */ virtual unsigned postEncodeDelay() const; /** Sets the post-encode delay in ms. */ virtual void setPostEncodeDelay(unsigned ms); /** Returns @c true if the PTT ID is set. */ virtual bool hasPTTID() const; /** Returns the PTT ID. */ virtual unsigned pttID() const; /** Sets the PTT ID [5,75]. */ virtual void setPTTID(unsigned id); /** Clears the PTT ID. */ virtual void clearPTTID(); /** Returns the auto-reset time in seconds. */ virtual unsigned autoResetTime() const; /** Sets the auto-reset time in seconds. */ virtual void setAutoResetTime(unsigned s); /** Returns the first delay in ms. */ virtual unsigned firstDelay() const; /** Sets the first delay in ms. */ virtual void setFirstDelay(unsigned ms); /** Returns @c true if the sidetone is enabled. */ virtual bool sidetoneEnabled() const; /** Enables/disables side tone. */ virtual void enableSidetone(bool enable); /** Returns the stop code [0,15]. */ virtual unsigned stopCode() const; /** Sets the stop code. */ virtual void setStopCode(unsigned code); /** Returns the stop time in ms. */ virtual unsigned stopTime() const; /** Sets the stop time in ms. */ virtual void setStopTime(unsigned ms); /** Returns the decode time in ms. */ virtual unsigned decodeTime() const; /** Sets the decode time in ms. */ virtual void setDecodeTime(unsigned ms); /** Returns the delay after stop in ms. */ virtual unsigned delayAfterStop() const; /** Sets the delay after stop in ms. */ virtual void setDelayAfterStop(unsigned ms); /** Returns the pre time in ms. */ virtual unsigned preTime() const; /** Sets the pre time in ms. */ virtual void setPreTime(unsigned ms); /** Returns the BOT standard. */ virtual Standard botStandard() const; /** Sets the BOT standard. */ virtual void setBOTStandard(Standard standard); /** Returns the BOT tone duration in ms. */ virtual unsigned botToneDuration() const; /** Sets the BOT tone duration in ms. */ virtual void setBOTToneDuration(unsigned ms); /** Returns the 5tone BOT ID. */ virtual QString botID() const; /** Sets the 5tone BOT ID. */ virtual void setBOTID(const QString &id); /** Returns the EOT standard. */ virtual Standard eotStandard() const; /** Sets the EOT standard. */ virtual void setEOTStandard(Standard standard); /** Returns the EOT tone duration in ms. */ virtual unsigned eotToneDuration() const; /** Sets the EOT tone duration in ms. */ virtual void setEOTToneDuration(unsigned ms); /** Returns the 5tone EOT ID. */ virtual QString eotID() const; /** Sets the 5tone EOT ID. */ virtual void setEOTID(const QString &id); }; /** Represents the base-class for a 2-tone ID for all AnyTone codeplugs. * * Memory encoding of the ID (size 0x0020 bytes): * @verbinclude anytone_2toneid.txt */ class TwoToneIDElement: public Element { protected: /** Hidden constructor. */ TwoToneIDElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ TwoToneIDElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0010; } /** Resets the ID. */ void clear(); /** Returns the first tone of the sequence. */ virtual double firstTone() const; /** Sets the first tone of the sequence. */ virtual void setFirstTone(double f); /** Returns the second tone of the sequence. */ virtual double secondTone() const; /** Sets the second tone of the sequence. */ virtual void setSecondTone(double f); /** Returns the name of the function. */ virtual QString name() const; /** Sets the name of the function. */ virtual void setName(const QString &name); public: /** Some limits for the element. */ struct Limit { static constexpr unsigned int nameLength() { return 7; } ///< Maximum name length. }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int firstTone() { return 0x0000; } static constexpr unsigned int secondTone() { return 0x0002; } static constexpr unsigned int name() { return 0x0008; } /// @endcond }; }; /** Represents the two-tone ID bitmap, indicating the which two-tone IDs are valid. */ class TwoToneIDBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ TwoToneIDBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ TwoToneIDBitmapElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0010; } }; /** Represents the base-class for a 2-tone function for all AnyTone codeplugs. * * Memory encoding of the function (size 0x0020 bytes): * @verbinclude anytone_2tonefunction.txt */ class TwoToneFunctionElement: public Element { public: /** Possible responses to a decode. */ enum class Response { None = 0, Tone, ToneRespond }; protected: /** Hidden constructor. */ TwoToneFunctionElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ TwoToneFunctionElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0020; } /** Resets the function. */ void clear(); /** Returns the first tone of the sequence. */ virtual double firstTone() const; /** Sets the first tone of the sequence. */ virtual void setFirstTone(double f); /** Returns the second tone of the sequence. */ virtual double secondTone() const; /** Sets the second tone of the sequence. */ virtual void setSecondTone(double f); /** Returns the response. */ virtual Response response() const; /** Sets the response. */ virtual void setResponse(Response resp); /** Returns the name of the function. */ virtual QString name() const; /** Sets the name of the function. */ virtual void setName(const QString &name); public: /** Some limits of the element. */ struct Limit { static constexpr unsigned int nameLength() { return 7; } ///< Maximum name length. }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int firstTone() { return 0x0000; } static constexpr unsigned int secondTone() { return 0x0002; } static constexpr unsigned int response() { return 0x0004; } static constexpr unsigned int name() { return 0x0005; } /// @endcond }; }; /** Rerpesents the two-tone function bitmap, indicating which two-tone functions are valid. */ class TwoToneFunctionBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ TwoToneFunctionBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ TwoToneFunctionBitmapElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0010; } }; /** Represents the base class of 2-tone settings for all AnyTone codeplugs. * * Memory encoding of the settings (size 0x0010 bytes): * @verbinclude anytone_2tonesettings.txt */ class TwoToneSettingsElement : public Element { protected: /** Hidden constructor. */ TwoToneSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ TwoToneSettingsElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0010; } /** Resets the settings. */ void clear(); /** Returns the first tone duration in ms. */ virtual unsigned firstToneDuration() const; /** Sets the first tone duration in ms. */ virtual void setFirstToneDuration(unsigned ms); /** Returns the second tone duration in ms. */ virtual unsigned secondToneDuration() const; /** Sets the second tone duration in ms. */ virtual void setSecondToneDuration(unsigned ms); /** Returns the long tone duration in ms. */ virtual unsigned longToneDuration() const; /** Sets the long tone duration in ms. */ virtual void setLongToneDuration(unsigned ms); /** Returns the gap duration in ms. */ virtual unsigned gapDuration() const; /** Sets the gap duration in ms. */ virtual void setGapDuration(unsigned ms); /** Returns the auto-reset time in seconds. */ virtual unsigned autoResetTime() const; /** Sets the auto-reset time in seconds. */ virtual void setAutoResetTime(unsigned sec); /** Returns @c true if the sidetone is enabled. */ virtual bool sidetone() const; /** Enables/disables the sidetone. */ virtual void enableSidetone(bool enable); }; /** Represents the base class of DTMF settings for all AnyTone codeplugs. * * Memory representation of the settings (size 0x0050): * @verbinclude anytone_dtmfsettings.txt */ class DTMFSettingsElement: public Element { public: /** Possible responses to a DTMF decode. */ enum Response { None=0, Tone, ToneRespond }; protected: /** Hidden constructor. */ DTMFSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit DTMFSettingsElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0050; } /** Resets the settings. */ void clear(); /** Returns the interval/repeat symbol [0,15]. */ virtual unsigned intervalSymbol() const; /** Sets the interval/repeat symbol [0,15]. */ virtual void setIntervalSymbol(unsigned symb); /** Returns the group code [0,15]. */ virtual unsigned groupCode() const; /** Sets the group code [0,15]. */ virtual void setGroupCode(unsigned symb); /** Returns the response to a DMTF decode. */ virtual Response response() const; /** Sets the response to a DTMF decode. */ virtual void setResponse(Response resp); /** Returns the pre time in ms. */ virtual unsigned preTime() const; /** Sets the pre time in ms. */ virtual void setPreTime(unsigned ms); /** Returns the first digit duration in ms. */ virtual unsigned firstDigitDuration() const; /** Sets the first digit duration in ms. */ virtual void setFirstDigitDuration(unsigned ms); /** Returns the auto reset time in seconds. */ virtual unsigned autoResetTime() const; /** Sets the auto reset time in seconds. */ virtual void setAutoResetTime(unsigned sec); /** Returns the radio ID. */ virtual QString id() const; /** Sets the radio ID. */ virtual void setID(const QString &id); /** Returns the post encoding delay in ms. */ virtual unsigned postEncodingDelay() const; /** Sets the post encoding delay in ms. */ virtual void setPostEncodingDelay(unsigned ms); /** Returns the PTT ID pause in seconds. */ virtual unsigned pttIDPause() const; /** Sets the PTT ID pause in seconds. */ virtual void setPTTIDPause(unsigned sec); /** Returns @c true if the PTT ID is enabled. */ virtual bool pttIDEnabled() const; /** Enables/disables the PTT ID. */ virtual void enablePTTID(bool enable); /** Returns the D-code pause in seconds. */ virtual unsigned dCodePause() const; /** Sets the D-code pause in seconds. */ virtual void setDCodePause(unsigned sec); /** Returns @c true if the sidetone is enabled. */ virtual bool sidetone() const; /** Enables/disables the sidetone. */ virtual void enableSidetone(bool enable); /** Returns the BOT ID. */ virtual QString botID() const; /** Sets the BOT ID. */ virtual void setBOTID(const QString &id); /** Returns the EOT ID. */ virtual QString eotID() const; /** Sets the EOT ID. */ virtual void setEOTID(const QString &id); /** Returns the remote kill ID. */ virtual QString remoteKillID() const; /** Sets the remote kill ID. */ virtual void setRemoteKillID(const QString &id); /** Returns the remote stun ID. */ virtual QString remoteStunID() const; /** Sets the remote stun ID. */ virtual void setRemoteStunID(const QString &id); }; /** Represents a list of DTMF IDs to be send. * * Memory encoding of the DTMF IDs (size 0x0100 bytes): * @verbinclude anytone_dtmfidlist.txt */ class DTMFIDListElement: public Element { protected: /** Hidden constructor. */ DTMFIDListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DTMFIDListElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0100; } void clear(); /** Returns @c true, if the n-th number is set. */ virtual bool hasNumber(unsigned int n) const; /** Returns the n-th number. */ virtual QString number(unsigned int n) const; /** Sets the n-th number. */ virtual void setNumber(unsigned int n, const QString &number); /** Clears the n-th number. */ virtual void clearNumber(unsigned int n); public: /** Some limits of the list. */ struct Limit { static constexpr unsigned int numEntries() { return 16; } ///< The maximum number of entries in the list. static constexpr unsigned int numberLength() { return 16; } ///< The maximum length of the numbers. }; }; /** Represents a list of 100 FM broad cast channels. * * Memory representation of the channel list (size 0x0200 bytes): * @verbinclude anytone_wfmchannellist.txt */ class WFMChannelListElement: public Element { protected: /** Hidden constructor. */ WFMChannelListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit WFMChannelListElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0200; } void clear(); /** Returns @c true, if the n-th channel is set. */ virtual bool hasChannel(unsigned int n) const; /** Returns the n-th channel frequency. */ virtual Frequency channel(unsigned int n) const; /** Sets the n-th channel frequency. */ virtual void setChannel(unsigned int n, Frequency freq); /** Clears the n-th channel frequency. */ virtual void clearChannel(unsigned int n); public: /** Some limits for the channel list. */ struct Limit { static constexpr unsigned int numEntries() { return 100; } ///< Maximum number of channels in the list. }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int betweenChannels() { return 0x0004; } /// @endcond }; }; /** Represents the bitmap, indicating which WFM (FM broadcast) channels are valid. */ class WFMChannelBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ WFMChannelBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ WFMChannelBitmapElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0020; } }; /** Represents the WFM (FM broadcast) VFO frquency. */ class WFMVFOElement: public Element { protected: /** Hidden constructor. */ WFMVFOElement(uint8_t *ptr, size_t size); public: /** Constructor. */ WFMVFOElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0010; } void clear(); /** Returns the VFO frequency. */ virtual Frequency frequency() const; /** Sets the VFO frequency. */ virtual void setFrequency(Frequency freq); }; /** Represents a list of DMR encryption key IDs. */ class DMREncryptionKeyIDListElement: public Element { protected: /** Hidden constructor. */ DMREncryptionKeyIDListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DMREncryptionKeyIDListElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0040; } void clear(); /** Returns @c true if the n-th id is set. */ virtual bool hasID(unsigned int n) const; /** Returns the ID of the encryption key. */ virtual uint16_t id(unsigned int n) const; /** Sets the ID of the encryption key. */ virtual void setID(unsigned int n, uint16_t id); /** Clears the n-th id. */ virtual void clearID(unsigned int n); public: /** Some limits for the list. */ struct Limit { static constexpr unsigned int numEntries() { return 32; } ///< Maximum number of DMR encryption key IDs. }; protected: /** Some internal used offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int betweenIDs() { return 0x0002; } /// @endcond }; }; /** Represents a list of DMR encryption keys. */ class DMREncryptionKeyListElement: public Element { protected: /** Hidden constructor. */ DMREncryptionKeyListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DMREncryptionKeyListElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0500; } void clear(); /** Returns the n-th key. */ QByteArray key(unsigned int n) const; /** Sets the n-th key. */ void setKey(unsigned int n, const QByteArray &key); public: /** Some limits of the list. */ struct Limit { static constexpr unsigned numEntries() { return DMREncryptionKeyIDListElement::Limit::numEntries(); } ///< Maximum number of keys. }; protected: /** Some offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int keys() { return 0x0010; } static constexpr unsigned int betweenKeys() { return 0x0028; } /// @endcond }; }; /** Represents the base class for entries to the contact indices in all AnyTone codeplugs. * * Memory representation of the entry (size 0x0008): * @verbinclude anytone_contactmapentry.txt */ class ContactMapElement: public Element { protected: /** Hidden constructor. */ ContactMapElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ ContactMapElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0008; } /** Clears the entry. */ void clear(); /** Returns @c true if the contact map is valid. */ bool isValid() const; /** Returns @c true if the entry is a group call. */ virtual bool isGroup() const; /** Returns the id. */ virtual unsigned id() const; /** Encodes ID and group call flag. */ virtual void setID(unsigned id, bool group=false); /** Returns the index. */ virtual unsigned index() const; /** Sets the index. */ virtual void setIndex(unsigned idx); }; protected: /** Hidden constructor. */ AnytoneCodeplug(const QString &label, QObject *parent=nullptr); public: /** Destructor. */ virtual ~AnytoneCodeplug(); /** Clears and resets the complete codeplug to some default values. */ virtual void clear(); Config *preprocess(Config *config, const ErrorStack &err) const; bool encode(Config *config, const Flags &flags, const ErrorStack &err); bool decode(Config *config, const ErrorStack &err); bool postprocess(Config *config, const ErrorStack &err) const; protected: virtual bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Allocates the bitmaps. This is also performed during a clear. */ virtual bool allocateBitmaps() = 0; /** Sets all bitmaps for the given config. */ virtual void setBitmaps(Context &ctx) = 0; /** Allocate all code-plug elements that must be written back to the device to maintain a working * codeplug. These elements might be updated during encoding. */ virtual void allocateUpdated() = 0; /** Allocate all code-plug elements that must be downloaded for decoding. All code-plug elements * within the radio that are not represented within the common Config are omitted. */ virtual void allocateForDecoding() = 0; /** Allocate all code-plug elements that are defined through the common Config. */ virtual void allocateForEncoding() = 0; /** Encodes the given config (via context) to the binary codeplug. */ virtual bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Decodes the downloaded codeplug. */ virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; protected: /** Holds the image label. */ QString _label; // Allow access to protected allocation methods. friend class AnytoneRadio; }; #endif // ANYTONECODEPLUG_HH qdmr-0.12.3/lib/anytone_extension.cc000066400000000000000000002231411501654372000173740ustar00rootroot00000000000000#include "anytone_extension.hh" #include "signaling.hh" /* ********************************************************************************************* * * Implementation of AnytoneAPRSFrequency * ********************************************************************************************* */ AnytoneAPRSFrequency::AnytoneAPRSFrequency(QObject *parent) : ConfigObject(parent), _frequency(Frequency::fromHz(0)) { // pass... } ConfigItem * AnytoneAPRSFrequency::clone() const { AnytoneAPRSFrequency *obj = new AnytoneAPRSFrequency(); if (! obj->copy(*this)) { obj->deleteLater(); return nullptr; } return obj; } Frequency AnytoneAPRSFrequency::frequency() const { return _frequency; } void AnytoneAPRSFrequency::setFrequency(Frequency freq) { if (_frequency == freq) return; _frequency = freq; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneAPRSFrequencyRef * ********************************************************************************************* */ AnytoneAPRSFrequencyRef::AnytoneAPRSFrequencyRef(QObject *parent) : ConfigObjectReference(AnytoneAPRSFrequency::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneAPRSFrequencyList * ********************************************************************************************* */ AnytoneAPRSFrequencyList::AnytoneAPRSFrequencyList(QObject *parent) : ConfigObjectList(AnytoneAPRSFrequency::staticMetaObject, parent) { // pass... } ConfigItem * AnytoneAPRSFrequencyList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err); return new AnytoneAPRSFrequency(); } /* ********************************************************************************************* * * Implementation of AnytoneChannelExtension * ********************************************************************************************* */ AnytoneChannelExtension::AnytoneChannelExtension(QObject *parent) : ConfigExtension(parent), _talkaround(false), _frequencyCorrection(0), _handsFree(false), _fmAPRSFrequency(new AnytoneAPRSFrequencyRef(this)), _aprsPTT(APRSPTT::Off) { // pass... } bool AnytoneChannelExtension::talkaround() const { return _talkaround; } void AnytoneChannelExtension::enableTalkaround(bool enable) { if (enable == _talkaround) return; _talkaround = enable; emit modified(this); } int AnytoneChannelExtension::frequencyCorrection() const { return _frequencyCorrection; } void AnytoneChannelExtension::setFrequencyCorrection(int corr) { if (corr == _frequencyCorrection) return; _frequencyCorrection = corr; emit modified(this); } bool AnytoneChannelExtension::handsFree() const { return _handsFree; } void AnytoneChannelExtension::enableHandsFree(bool enable) { if (enable == _handsFree) return; _handsFree = enable; emit modified(this); } AnytoneAPRSFrequencyRef * AnytoneChannelExtension::fmAPRSFrequency() const { return _fmAPRSFrequency; } AnytoneChannelExtension::APRSPTT AnytoneChannelExtension::aprsPTT() const { return _aprsPTT; } void AnytoneChannelExtension::setAPRSPTT(APRSPTT mode) { if (_aprsPTT == mode) return; _aprsPTT = mode; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneAnalogChannelExtension * ********************************************************************************************* */ AnytoneFMChannelExtension::AnytoneFMChannelExtension(QObject *parent) : AnytoneChannelExtension(parent), _reverseBurst(false), _rxCustomCTCSS(false), _txCustomCTCSS(false), _customCTCSS(0), _squelchMode(SquelchMode::Carrier), _scrambler(false) { // pass... } ConfigItem * AnytoneFMChannelExtension::clone() const { AnytoneFMChannelExtension *ext = new AnytoneFMChannelExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } bool AnytoneFMChannelExtension::reverseBurst() const { return _reverseBurst; } void AnytoneFMChannelExtension::enableReverseBurst(bool enable) { if (enable == _reverseBurst) return; _reverseBurst = enable; emit modified(this); } bool AnytoneFMChannelExtension::rxCustomCTCSS() const { return _rxCustomCTCSS; } void AnytoneFMChannelExtension::enableRXCustomCTCSS(bool enable) { if (enable == _rxCustomCTCSS) return; _rxCustomCTCSS = enable; emit modified(this); } bool AnytoneFMChannelExtension::txCustomCTCSS() const { return _txCustomCTCSS; } void AnytoneFMChannelExtension::enableTXCustomCTCSS(bool enable) { if (enable == _txCustomCTCSS) return; _txCustomCTCSS = enable; emit modified(this); } double AnytoneFMChannelExtension::customCTCSS() const { return _customCTCSS; } void AnytoneFMChannelExtension::setCustomCTCSS(double freq) { if (freq == _customCTCSS) return; _customCTCSS = freq; emit modified(this); } AnytoneFMChannelExtension::SquelchMode AnytoneFMChannelExtension::squelchMode() const { return _squelchMode; } void AnytoneFMChannelExtension::setSquelchMode(SquelchMode mode) { if (mode == _squelchMode) return; _squelchMode = mode; emit modified(this); } bool AnytoneFMChannelExtension::scrambler() const { return _scrambler; } void AnytoneFMChannelExtension::enableScrambler(bool enable) { if (enable == _scrambler) return; _scrambler = enable; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneDigitalChannelExtension * ********************************************************************************************* */ AnytoneDMRChannelExtension::AnytoneDMRChannelExtension(QObject *parent) : AnytoneChannelExtension(parent), _callConfirm(false), _sms(true), _smsConfirm(false), _dataACK(true), _simplexTDMA(false), _adaptiveTDMA(false), _loneWorker(false), _throughMode(false) { // pass... } ConfigItem * AnytoneDMRChannelExtension::clone() const { AnytoneDMRChannelExtension *ext = new AnytoneDMRChannelExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } bool AnytoneDMRChannelExtension::callConfirm() const { return _callConfirm; } void AnytoneDMRChannelExtension::enableCallConfirm(bool enabled) { if (enabled == _callConfirm) return; _callConfirm = enabled; emit modified(this); } bool AnytoneDMRChannelExtension::sms() const { return _sms; } void AnytoneDMRChannelExtension::enableSMS(bool enable) { if (enable == _sms) return; _sms = enable; emit modified(this); } bool AnytoneDMRChannelExtension::smsConfirm() const { return _smsConfirm; } void AnytoneDMRChannelExtension::enableSMSConfirm(bool enabled) { if (enabled == _smsConfirm) return; _smsConfirm = enabled; emit modified(this); } bool AnytoneDMRChannelExtension::dataACK() const { return _dataACK; } void AnytoneDMRChannelExtension::enableDataACK(bool enable) { if (enable==_dataACK) return; _dataACK = enable; emit modified(this); } bool AnytoneDMRChannelExtension::simplexTDMA() const { return _simplexTDMA; } void AnytoneDMRChannelExtension::enableSimplexTDMA(bool enable) { if (enable == _simplexTDMA) return; _simplexTDMA = enable; emit modified(this); } bool AnytoneDMRChannelExtension::adaptiveTDMA() const { return _adaptiveTDMA; } void AnytoneDMRChannelExtension::enableAdaptiveTDMA(bool enable) { if (enable == _adaptiveTDMA) return; _adaptiveTDMA = enable; emit modified(this); } bool AnytoneDMRChannelExtension::loneWorker() const { return _loneWorker; } void AnytoneDMRChannelExtension::enableLoneWorker(bool enable) { if (enable == _loneWorker) return; _loneWorker = enable; emit modified(this); } bool AnytoneDMRChannelExtension::throughMode() const { return _throughMode; } void AnytoneDMRChannelExtension::enableThroughMode(bool enable) { if (enable == _throughMode) return; _throughMode = enable; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneZoneExtension * ********************************************************************************************* */ AnytoneZoneExtension::AnytoneZoneExtension(QObject *parent) : ConfigExtension(parent), _hidden(false) { // pass... } ConfigItem * AnytoneZoneExtension::clone() const { AnytoneZoneExtension *obj = new AnytoneZoneExtension(); if (! obj->copy(*this)) { obj->deleteLater(); return nullptr; } return obj; } bool AnytoneZoneExtension::hidden() const { return _hidden; } void AnytoneZoneExtension::enableHidden(bool enable) { if (_hidden == enable) return; _hidden = enable; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneContactExtension * ********************************************************************************************* */ AnytoneContactExtension::AnytoneContactExtension(QObject *parent) : ConfigExtension(parent), _alertType(AlertType::None) { // pass... } ConfigItem * AnytoneContactExtension::clone() const { AnytoneContactExtension *obj = new AnytoneContactExtension(); if (! obj->copy(*this)) { obj->deleteLater(); return nullptr; } return obj; } AnytoneContactExtension::AlertType AnytoneContactExtension::alertType() const { return _alertType; } void AnytoneContactExtension::setAlertType(AlertType type) { _alertType = type; } /* ********************************************************************************************* * * Implementation of AnytoneDMRSettingsExtension * ********************************************************************************************* */ AnytoneDMRSettingsExtension::AnytoneDMRSettingsExtension(QObject *parent) : ConfigItem(parent), _groupCallHangTime(Interval::fromSeconds(3)), _manualGroupCallHangTime(Interval::fromSeconds(5)), _privateCallHangTime(Interval::fromSeconds(5)), _manualPrivateCallHangTime(Interval::fromSeconds(7)), _preWaveDelay(Interval::fromMilliseconds(100)), _wakeHeadPeriod(Interval::fromMilliseconds(100)), _filterOwnID(true), _monitorSlotMatch(SlotMatch::Off), _monitorColorCodeMatch(false), _monitorIDMatch(false), _monitorTimeSlotHold(true), _smsFormat(SMSFormat::Motorola), _sendTalkerAlias(false), _talkerAliasSource(TalkerAliasSource::UserDB), _talkerAliasEncoding(TalkerAliasEncoding::ISO7), _encryption(EncryptionType::DMR) { // pass... } ConfigItem * AnytoneDMRSettingsExtension::clone() const { AnytoneDMRSettingsExtension *ext = new AnytoneDMRSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } Interval AnytoneDMRSettingsExtension::groupCallHangTime() const { return _groupCallHangTime; } void AnytoneDMRSettingsExtension::setGroupCallHangTime(Interval sec) { if (_groupCallHangTime == sec) return; _groupCallHangTime = sec; emit modified(this); } Interval AnytoneDMRSettingsExtension::manualGroupCallHangTime() const { return _manualGroupCallHangTime; } void AnytoneDMRSettingsExtension::setManualGroupCallHangTime(Interval sec) { if (_manualGroupCallHangTime == sec) return; _manualGroupCallHangTime = sec; emit modified(this); } Interval AnytoneDMRSettingsExtension::privateCallHangTime() const { return _privateCallHangTime; } void AnytoneDMRSettingsExtension::setPrivateCallHangTime(Interval sec) { if (_privateCallHangTime == sec) return; _privateCallHangTime = sec; emit modified(this); } Interval AnytoneDMRSettingsExtension::manualPrivateCallHangTime() const { return _manualPrivateCallHangTime; } void AnytoneDMRSettingsExtension::setManualPrivateCallHangTime(Interval sec) { if (_manualPrivateCallHangTime == sec) return; _manualPrivateCallHangTime = sec; emit modified(this); } Interval AnytoneDMRSettingsExtension::preWaveDelay() const { return _preWaveDelay; } void AnytoneDMRSettingsExtension::setPreWaveDelay(Interval ms) { if (_preWaveDelay == ms) return; _preWaveDelay = ms; emit modified(this); } Interval AnytoneDMRSettingsExtension::wakeHeadPeriod() const { return _wakeHeadPeriod; } void AnytoneDMRSettingsExtension::setWakeHeadPeriod(Interval ms) { if (_wakeHeadPeriod == ms) return; _wakeHeadPeriod = ms; emit modified(this); } bool AnytoneDMRSettingsExtension::filterOwnIDEnabled() const { return _filterOwnID; } void AnytoneDMRSettingsExtension::enableFilterOwnID(bool enable) { if (_filterOwnID == enable) return; _filterOwnID = enable; emit modified(this); } AnytoneDMRSettingsExtension::SlotMatch AnytoneDMRSettingsExtension::monitorSlotMatch() const { return _monitorSlotMatch; } void AnytoneDMRSettingsExtension::setMonitorSlotMatch(SlotMatch match) { if (_monitorSlotMatch == match) return; _monitorSlotMatch = match; emit modified(this); } bool AnytoneDMRSettingsExtension::monitorColorCodeMatchEnabled() const { return _monitorColorCodeMatch; } void AnytoneDMRSettingsExtension::enableMonitorColorCodeMatch(bool enable) { if (_monitorColorCodeMatch == enable) return; _monitorColorCodeMatch = enable; emit modified(this); } bool AnytoneDMRSettingsExtension::monitorIDMatchEnabled() const { return _monitorIDMatch; } void AnytoneDMRSettingsExtension::enableMonitorIDMatch(bool enable) { if (_monitorIDMatch == enable) return; _monitorIDMatch = enable; emit modified(this); } bool AnytoneDMRSettingsExtension::monitorTimeSlotHoldEnabled() const { return _monitorTimeSlotHold; } void AnytoneDMRSettingsExtension::enableMonitorTimeSlotHold(bool enable) { if (_monitorTimeSlotHold == enable) return; _monitorTimeSlotHold = enable; emit modified(this); } AnytoneDMRSettingsExtension::SMSFormat AnytoneDMRSettingsExtension::smsFormat() const { return _smsFormat; } void AnytoneDMRSettingsExtension::setSMSFormat(SMSFormat format) { if (_smsFormat == format) return; _smsFormat = format; emit modified(this); } bool AnytoneDMRSettingsExtension::sendTalkerAlias() const { return _sendTalkerAlias; } void AnytoneDMRSettingsExtension::enableSendTalkerAlias(bool enable) { if (_sendTalkerAlias == enable) return; _sendTalkerAlias = enable; emit modified(this); } AnytoneDMRSettingsExtension::TalkerAliasSource AnytoneDMRSettingsExtension::talkerAliasSource() const { return _talkerAliasSource; } void AnytoneDMRSettingsExtension::setTalkerAliasSource(TalkerAliasSource mode) { if (mode == _talkerAliasSource) return; _talkerAliasSource = mode; emit modified(this); } AnytoneDMRSettingsExtension::TalkerAliasEncoding AnytoneDMRSettingsExtension::talkerAliasEncoding() const { return _talkerAliasEncoding; } void AnytoneDMRSettingsExtension::setTalkerAliasEncoding(TalkerAliasEncoding encoding) { if (_talkerAliasEncoding == encoding) return; _talkerAliasEncoding = encoding; emit modified(this); } AnytoneDMRSettingsExtension::EncryptionType AnytoneDMRSettingsExtension::encryption() const { return _encryption; } void AnytoneDMRSettingsExtension::setEncryption(EncryptionType type) { if (type == _encryption) return; _encryption = type; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneGPSSettingsExtension * ********************************************************************************************* */ AnytoneGPSSettingsExtension::AnytoneGPSSettingsExtension(QObject *parent) : ConfigItem(parent), _gpsUnits(Units::Metric), _timeZone(QTimeZone::utc()), _gpsRangeReporting(false), _gpsRangingInterval(Interval::fromSeconds(300)), _mode(GPSMode::GPS) { // pass... } ConfigItem * AnytoneGPSSettingsExtension::clone() const { auto *ext = new AnytoneGPSSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } AnytoneGPSSettingsExtension::Units AnytoneGPSSettingsExtension::units() const { return _gpsUnits; } void AnytoneGPSSettingsExtension::setUnits(Units units) { if (_gpsUnits == units) return; _gpsUnits = units; emit modified(this); } QTimeZone AnytoneGPSSettingsExtension::timeZone() const { return _timeZone; } QString AnytoneGPSSettingsExtension::ianaTimeZone() const { return QString::fromLocal8Bit(_timeZone.id()); } void AnytoneGPSSettingsExtension::setTimeZone(const QTimeZone &zone) { if (_timeZone == zone) return; _timeZone = zone; emit modified(this); } void AnytoneGPSSettingsExtension::setIANATimeZone(const QString &id) { setTimeZone(QTimeZone(id.toLocal8Bit())); } bool AnytoneGPSSettingsExtension::positionReportingEnabled() const { return _gpsRangeReporting; } void AnytoneGPSSettingsExtension::enablePositionReporting(bool enable) { if (_gpsRangeReporting == enable) return; _gpsRangeReporting = enable; emit modified(this); } Interval AnytoneGPSSettingsExtension::updatePeriod() const { return _gpsRangingInterval; } void AnytoneGPSSettingsExtension::setUpdatePeriod(Interval sec) { if (_gpsRangingInterval == sec) return; _gpsRangingInterval = sec; emit modified(this); } AnytoneGPSSettingsExtension::GPSMode AnytoneGPSSettingsExtension::mode() const { return _mode; } void AnytoneGPSSettingsExtension::setMode(GPSMode mode) { if (mode == _mode) return; _mode = mode; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneRangingSettingsExtension * ********************************************************************************************* */ AnytoneRoamingSettingsExtension::AnytoneRoamingSettingsExtension(QObject *parent) : ConfigItem(parent), _autoRoam(false), _autoRoamPeriod(Interval::fromMinutes(1)), _autoRoamDelay(), _repeaterRangeCheck(false), _repeaterCheckInterval(Interval::fromSeconds(5)), _repeaterRangeCheckCount(3), _outOfRangeAlert(OutOfRangeAlert::None), _roamingStartCondition(RoamStart::Periodic), _roamingReturnCondition(RoamStart::Periodic), _notification(false), _notificationCount(1), _gpsRoaming(false), _defaultRoamingZone(new RoamingZoneReference(this)) { // pass... } ConfigItem * AnytoneRoamingSettingsExtension::clone() const { AnytoneRoamingSettingsExtension *ext = new AnytoneRoamingSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } bool AnytoneRoamingSettingsExtension::autoRoam() const { return _autoRoam; } void AnytoneRoamingSettingsExtension::enableAutoRoam(bool enable) { if (enable == _autoRoam) return; _autoRoam = enable; emit modified(this); } Interval AnytoneRoamingSettingsExtension::autoRoamPeriod() const { return _autoRoamPeriod; } void AnytoneRoamingSettingsExtension::setAutoRoamPeriod(Interval min) { if (_autoRoamPeriod == min) return; _autoRoamPeriod = min; emit modified(this); } Interval AnytoneRoamingSettingsExtension::autoRoamDelay() const { return _autoRoamDelay; } void AnytoneRoamingSettingsExtension::setAutoRoamDelay(Interval min) { if (_autoRoamDelay == min) return; _autoRoamDelay = min; emit modified(this); } bool AnytoneRoamingSettingsExtension::repeaterRangeCheckEnabled() const { return _repeaterRangeCheck; } void AnytoneRoamingSettingsExtension::enableRepeaterRangeCheck(bool enable) { if (_repeaterRangeCheck == enable) return; _repeaterRangeCheck = enable; emit modified(this); } Interval AnytoneRoamingSettingsExtension::repeaterCheckInterval() const { return _repeaterCheckInterval; } void AnytoneRoamingSettingsExtension::setRepeaterCheckInterval(Interval sec) { if (_repeaterCheckInterval == sec) return; _repeaterCheckInterval = sec; emit modified(this); } unsigned int AnytoneRoamingSettingsExtension::repeaterRangeCheckCount() const { return _repeaterRangeCheckCount; } void AnytoneRoamingSettingsExtension::setRepeaterRangeCheckCount(unsigned int sec) { if (_repeaterRangeCheckCount == sec) return; _repeaterRangeCheckCount = sec; emit modified(this); } AnytoneRoamingSettingsExtension::OutOfRangeAlert AnytoneRoamingSettingsExtension::outOfRangeAlert() const { return _outOfRangeAlert; } void AnytoneRoamingSettingsExtension::setOutOfRangeAlert(OutOfRangeAlert type) { if (type == _outOfRangeAlert) return; _outOfRangeAlert = type; emit modified(this); } AnytoneRoamingSettingsExtension::RoamStart AnytoneRoamingSettingsExtension::roamingStartCondition() const { return _roamingStartCondition; } void AnytoneRoamingSettingsExtension::setRoamingStartCondition(RoamStart start) { if (_roamingStartCondition == start) return; _roamingStartCondition = start; emit modified(this); } AnytoneRoamingSettingsExtension::RoamStart AnytoneRoamingSettingsExtension::roamingReturnCondition() const { return _roamingReturnCondition; } void AnytoneRoamingSettingsExtension::setRoamingReturnCondition(RoamStart end) { if (_roamingReturnCondition == end) return; _roamingReturnCondition = end; emit modified(this); } bool AnytoneRoamingSettingsExtension::notificationEnabled() const { return _notification; } void AnytoneRoamingSettingsExtension::enableNotification(bool enable) { if (_notification == enable) return; _notification = enable; emit modified(this); } unsigned int AnytoneRoamingSettingsExtension::notificationCount() const { return _notificationCount; } void AnytoneRoamingSettingsExtension::setNotificationCount(unsigned int n) { if (_notificationCount == n) return; _notificationCount = n; emit modified(this); } bool AnytoneRoamingSettingsExtension::gpsRoaming() const { return _gpsRoaming; } void AnytoneRoamingSettingsExtension::enableGPSRoaming(bool enable) { if (enable == _gpsRoaming) return; _gpsRoaming = enable; emit modified(this); } RoamingZoneReference * AnytoneRoamingSettingsExtension::defaultZone() const { return _defaultRoamingZone; } /* ********************************************************************************************* * * Implementation of AnytoneSettingsExtension * ********************************************************************************************* */ AnytoneSettingsExtension::AnytoneSettingsExtension(QObject *parent) : ConfigExtension(parent), _bootSettings(new AnytoneBootSettingsExtension(this)), _powerSaveSettings(new AnytonePowerSaveSettingsExtension(this)), _keySettings(new AnytoneKeySettingsExtension(this)), _toneSettings(new AnytoneToneSettingsExtension(this)), _displaySettings(new AnytoneDisplaySettingsExtension(this)), _audioSettings(new AnytoneAudioSettingsExtension(this)), _menuSettings(new AnytoneMenuSettingsExtension(this)), _autoRepeaterSettings(new AnytoneAutoRepeaterSettingsExtension(this)), _dmrSettings(new AnytoneDMRSettingsExtension(this)), _gpsSettings(new AnytoneGPSSettingsExtension(this)), _roamingSettings(new AnytoneRoamingSettingsExtension(this)), _bluetoothSettings(new AnytoneBluetoothSettingsExtension(this)), _simplexRepeaterSettings(new AnytoneSimplexRepeaterSettingsExtension(this)), _vfoScanType(VFOScanType::Time), _modeA(VFOMode::Memory), _modeB(VFOMode::Memory), _zoneA(), _zoneB(), _selectedVFO(VFO::A), _subChannel(true), _minVFOScanFrequencyUHF(Frequency::fromMHz(430)), _maxVFOScanFrequencyUHF(Frequency::fromMHz(440)), _minVFOScanFrequencyVHF(Frequency::fromMHz(144)), _maxVFOScanFrequencyVHF(Frequency::fromMHz(146)), _keepLastCaller(false), _vfoStep(Frequency::fromkHz(5)), _steType(STEType::Off), _steFrequency(0), _steDuration(Interval::fromMilliseconds(300)), _tbstFrequency(Frequency::fromHz(1750)), _proMode(false), _maintainCallChannel(false) { connect(_bootSettings, &AnytoneBootSettingsExtension::modified, this, &AnytoneSettingsExtension::modified); connect(_keySettings, &AnytoneKeySettingsExtension::modified, this, &AnytoneSettingsExtension::modified); connect(_toneSettings, &AnytoneToneSettingsExtension::modified, this, &AnytoneSettingsExtension::modified); connect(_displaySettings, &AnytoneDisplaySettingsExtension::modified, this, &AnytoneSettingsExtension::modified); connect(_audioSettings, &AnytoneAudioSettingsExtension::modified, this, &AnytoneSettingsExtension::modified); connect(_menuSettings, &AnytoneMenuSettingsExtension::modified, this, &AnytoneSettingsExtension::modified); connect(_autoRepeaterSettings, &AnytoneAutoRepeaterSettingsExtension::modified, this, &AnytoneSettingsExtension::modified); connect(_dmrSettings, &AnytoneDMRSettingsExtension::modified, this, &AnytoneSettingsExtension::modified); connect(_roamingSettings, &AnytoneRoamingSettingsExtension::modified, this, &AnytoneSettingsExtension::modified); connect(_simplexRepeaterSettings, &AnytoneSimplexRepeaterSettingsExtension::modified, this, &AnytoneSettingsExtension::modified); } ConfigItem * AnytoneSettingsExtension::clone() const { AnytoneSettingsExtension *ext = new AnytoneSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } AnytoneBootSettingsExtension * AnytoneSettingsExtension::bootSettings() const { return _bootSettings; } AnytonePowerSaveSettingsExtension * AnytoneSettingsExtension::powerSaveSettings() const { return _powerSaveSettings; } AnytoneKeySettingsExtension * AnytoneSettingsExtension::keySettings() const { return _keySettings; } AnytoneToneSettingsExtension * AnytoneSettingsExtension::toneSettings() const { return _toneSettings; } AnytoneDisplaySettingsExtension * AnytoneSettingsExtension::displaySettings() const { return _displaySettings; } AnytoneAudioSettingsExtension * AnytoneSettingsExtension::audioSettings() const { return _audioSettings; } AnytoneMenuSettingsExtension * AnytoneSettingsExtension::menuSettings() const { return _menuSettings; } AnytoneAutoRepeaterSettingsExtension * AnytoneSettingsExtension::autoRepeaterSettings() const { return _autoRepeaterSettings; } AnytoneDMRSettingsExtension * AnytoneSettingsExtension::dmrSettings() const { return _dmrSettings; } AnytoneGPSSettingsExtension * AnytoneSettingsExtension::gpsSettings() const { return _gpsSettings; } AnytoneRoamingSettingsExtension * AnytoneSettingsExtension::roamingSettings() const { return _roamingSettings; } AnytoneBluetoothSettingsExtension * AnytoneSettingsExtension::bluetoothSettings() const { return _bluetoothSettings; } AnytoneSimplexRepeaterSettingsExtension * AnytoneSettingsExtension::simplexRepeaterSettings() const { return _simplexRepeaterSettings; } AnytoneSettingsExtension::VFOScanType AnytoneSettingsExtension::vfoScanType() const { return _vfoScanType; } void AnytoneSettingsExtension::setVFOScanType(VFOScanType type) { if (_vfoScanType == type) return; _vfoScanType = type; emit modified(this); } AnytoneSettingsExtension::VFOMode AnytoneSettingsExtension::modeA() const { return _modeA; } void AnytoneSettingsExtension::setModeA(VFOMode mode) { if (_modeA == mode) return; _modeA = mode; emit modified(this); } AnytoneSettingsExtension::VFOMode AnytoneSettingsExtension::modeB() const { return _modeB; } void AnytoneSettingsExtension::setModeB(VFOMode mode) { if (_modeB == mode) return; _modeB = mode; emit modified(this); } ZoneReference * AnytoneSettingsExtension::zoneA() { return &_zoneA; } const ZoneReference * AnytoneSettingsExtension::zoneA() const { return &_zoneA; } ZoneReference * AnytoneSettingsExtension::zoneB() { return &_zoneB; } const ZoneReference * AnytoneSettingsExtension::zoneB() const { return &_zoneB; } AnytoneSettingsExtension::VFO AnytoneSettingsExtension::selectedVFO() const { return _selectedVFO; } void AnytoneSettingsExtension::setSelectedVFO(VFO vfo) { if (_selectedVFO == vfo) return; _selectedVFO = vfo; emit modified(this); } bool AnytoneSettingsExtension::subChannelEnabled() const { return _subChannel; } void AnytoneSettingsExtension::enableSubChannel(bool enable) { if (_subChannel == enable) return; _subChannel = enable; emit modified(this); } Frequency AnytoneSettingsExtension::minVFOScanFrequencyUHF() const { return _minVFOScanFrequencyUHF; } void AnytoneSettingsExtension::setMinVFOScanFrequencyUHF(Frequency hz) { if (_minVFOScanFrequencyUHF == hz) return; _minVFOScanFrequencyUHF = hz; emit modified(this); } Frequency AnytoneSettingsExtension::maxVFOScanFrequencyUHF() const { return _maxVFOScanFrequencyUHF; } void AnytoneSettingsExtension::setMaxVFOScanFrequencyUHF(Frequency hz) { if (_maxVFOScanFrequencyUHF == hz) return; _maxVFOScanFrequencyUHF = hz; emit modified(this); } Frequency AnytoneSettingsExtension::minVFOScanFrequencyVHF() const { return _minVFOScanFrequencyVHF; } void AnytoneSettingsExtension::setMinVFOScanFrequencyVHF(Frequency hz) { if (_minVFOScanFrequencyVHF == hz) return; _minVFOScanFrequencyVHF = hz; emit modified(this); } Frequency AnytoneSettingsExtension::maxVFOScanFrequencyVHF() const { return _maxVFOScanFrequencyVHF; } void AnytoneSettingsExtension::setMaxVFOScanFrequencyVHF(Frequency hz) { if (_maxVFOScanFrequencyVHF == hz) return; _maxVFOScanFrequencyVHF = hz; emit modified(this); } bool AnytoneSettingsExtension::keepLastCallerEnabled() const { return _keepLastCaller; } void AnytoneSettingsExtension::enableKeepLastCaller(bool enable) { if (_keepLastCaller == enable) return; _keepLastCaller = enable; emit modified(this); } Frequency AnytoneSettingsExtension::vfoStep() const { return _vfoStep; } void AnytoneSettingsExtension::setVFOStep(Frequency step) { if (_vfoStep == step) return; _vfoStep = step; emit modified(this); } AnytoneSettingsExtension::STEType AnytoneSettingsExtension::steType() const { return _steType; } void AnytoneSettingsExtension::setSTEType(STEType type) { if (_steType == type) return; _steType = type; emit modified(this); } double AnytoneSettingsExtension::steFrequency() const { return _steFrequency; } void AnytoneSettingsExtension::setSTEFrequency(double freq) { if (_steFrequency == freq) return; _steFrequency = freq; emit modified(this); } Interval AnytoneSettingsExtension::steDuration() const { return _steDuration; } void AnytoneSettingsExtension::setSTEDuration(Interval intv) { if (intv == _steDuration) return; _steDuration = intv; emit modified(this); } Frequency AnytoneSettingsExtension::tbstFrequency() const { return _tbstFrequency; } void AnytoneSettingsExtension::setTBSTFrequency(Frequency Hz) { if (_tbstFrequency == Hz) return; _tbstFrequency = Hz; emit modified(this); } bool AnytoneSettingsExtension::proModeEnabled() const { return _proMode; } void AnytoneSettingsExtension::enableProMode(bool enable) { if (_proMode == enable) return; _proMode = enable; emit modified(this); } bool AnytoneSettingsExtension::maintainCallChannelEnabled() const { return _maintainCallChannel; } void AnytoneSettingsExtension::enableMaintainCallChannel(bool enable) { if (_maintainCallChannel == enable) return; _maintainCallChannel = enable; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneBootSettingsExtension * ********************************************************************************************* */ AnytoneBootSettingsExtension::AnytoneBootSettingsExtension(QObject *parent) : ConfigItem(parent), _bootDisplay(BootDisplay::Default), _bootPasswordEnabled(false), _defaultChannel(false), _zoneA(new ZoneReference(this)), _channelA(new ChannelReference(this)), _zoneB(new ZoneReference(this)), _channelB(new ChannelReference(this)), _priorityZoneA(new ZoneReference(this)), _priorityZoneB(new ZoneReference(this)), _gpsCheck(false), _reset(true) { // pass... } ConfigItem * AnytoneBootSettingsExtension::clone() const { AnytoneBootSettingsExtension *ext = new AnytoneBootSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } AnytoneBootSettingsExtension::BootDisplay AnytoneBootSettingsExtension::bootDisplay() const { return _bootDisplay; } void AnytoneBootSettingsExtension::setBootDisplay(BootDisplay mode) { if (_bootDisplay == mode) return; _bootDisplay = mode; emit modified(this); } bool AnytoneBootSettingsExtension::bootPasswordEnabled() const { return _bootPasswordEnabled; } void AnytoneBootSettingsExtension::enableBootPassword(bool enable) { if (_bootPasswordEnabled == enable) return; _bootPasswordEnabled = enable; emit modified(this); } const QString & AnytoneBootSettingsExtension::bootPassword() const { return _bootPassword; } void AnytoneBootSettingsExtension::setBootPassword(const QString &pass) { if (_bootPassword == pass) return; _bootPassword = pass; emit modified(this); } bool AnytoneBootSettingsExtension::defaultChannelEnabled() const { return _defaultChannel; } void AnytoneBootSettingsExtension::enableDefaultChannel(bool enable) { if (_defaultChannel == enable) return; _defaultChannel = enable; emit modified(this); } ZoneReference * AnytoneBootSettingsExtension::zoneA() const { return _zoneA; } ChannelReference * AnytoneBootSettingsExtension::channelA() const { return _channelA; } ZoneReference * AnytoneBootSettingsExtension::zoneB() const { return _zoneB; } ChannelReference * AnytoneBootSettingsExtension::channelB() const { return _channelB; } ZoneReference * AnytoneBootSettingsExtension::priorityZoneA() const { return _priorityZoneA; } ZoneReference * AnytoneBootSettingsExtension::priorityZoneB() const { return _priorityZoneB; } bool AnytoneBootSettingsExtension::gpsCheckEnabled() const { return _gpsCheck; } void AnytoneBootSettingsExtension::enableGPSCheck(bool enable) { if (_gpsCheck == enable) return; _gpsCheck = enable; emit modified(this); } bool AnytoneBootSettingsExtension::resetEnabled() const { return _reset; } void AnytoneBootSettingsExtension::enableReset(bool enable) { if (_reset == enable) return; _reset = enable; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytonePowerSaveSettingsExtension * ********************************************************************************************* */ AnytonePowerSaveSettingsExtension::AnytonePowerSaveSettingsExtension(QObject *parent) : ConfigItem(parent), _autoShutDownDelay(), _resetAutoShutdownOnCall(true), _powerSave(PowerSave::Save50), _atpc(false) { // pass... } ConfigItem * AnytonePowerSaveSettingsExtension::clone() const { AnytonePowerSaveSettingsExtension *ext = new AnytonePowerSaveSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } Interval AnytonePowerSaveSettingsExtension::autoShutdown() const { return _autoShutDownDelay; } void AnytonePowerSaveSettingsExtension::setAutoShutdown(Interval intv) { if (_autoShutDownDelay == intv) return; _autoShutDownDelay = intv; emit modified(this); } bool AnytonePowerSaveSettingsExtension::resetAutoShutdownOnCall() const { return _resetAutoShutdownOnCall; } void AnytonePowerSaveSettingsExtension::enableResetAutoShutdownOnCall(bool enable) { if (enable == _resetAutoShutdownOnCall) return; _resetAutoShutdownOnCall = enable; emit modified(this); } AnytonePowerSaveSettingsExtension::PowerSave AnytonePowerSaveSettingsExtension::powerSave() const { return _powerSave; } void AnytonePowerSaveSettingsExtension::setPowerSave(PowerSave save) { if (_powerSave == save) return; _powerSave = save; emit modified(this); } bool AnytonePowerSaveSettingsExtension::atpc() const { return _atpc; } void AnytonePowerSaveSettingsExtension::enableATPC(bool enable) { if (enable == _atpc) return; _atpc = enable; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneKeySettingsExtension * ********************************************************************************************* */ AnytoneKeySettingsExtension::AnytoneKeySettingsExtension(QObject *parent) : ConfigItem(parent), _funcKey1Short(KeyFunction::VOX), _funcKey1Long(KeyFunction::ToggleVFO), _funcKey2Short(KeyFunction::Reverse), _funcKey2Long(KeyFunction::Off), _funcKey3Short(KeyFunction::Power), _funcKey3Long(KeyFunction::Record), _funcKey4Short(KeyFunction::Repeater), _funcKey4Long(KeyFunction::SMS), _funcKey5Short(KeyFunction::Reverse), _funcKey5Long(KeyFunction::Dial), _funcKey6Short(KeyFunction::Encryption), _funcKey6Long(KeyFunction::Off), _funcKeyAShort(KeyFunction::Off), _funcKeyALong(KeyFunction::Encryption), _funcKeyBShort(KeyFunction::Voltage), _funcKeyBLong(KeyFunction::Call), _funcKeyCShort(KeyFunction::Power), _funcKeyCLong(KeyFunction::VOX), _funcKeyDShort(KeyFunction::Off), _funcKeyDLong(KeyFunction::Off), _longPressDuration(Interval::fromSeconds(1)), _autoKeyLock(false), _knobLock(false), _keypadLock(false), _sideKeysLock(false), _forcedKeyLock(false) { // pass... } ConfigItem * AnytoneKeySettingsExtension::clone() const { AnytoneKeySettingsExtension *ext = new AnytoneKeySettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKey1Short() const { return _funcKey1Short; } void AnytoneKeySettingsExtension::setFuncKey1Short(KeyFunction func) { if (_funcKey1Short == func) return; _funcKey1Short = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKey1Long() const { return _funcKey1Long; } void AnytoneKeySettingsExtension::setFuncKey1Long(KeyFunction func) { if (_funcKey1Long == func) return; _funcKey1Long = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKey2Short() const { return _funcKey2Short; } void AnytoneKeySettingsExtension::setFuncKey2Short(KeyFunction func) { if (_funcKey2Short == func) return; _funcKey2Short = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKey2Long() const { return _funcKey2Long; } void AnytoneKeySettingsExtension::setFuncKey2Long(KeyFunction func) { if (_funcKey2Long == func) return; _funcKey2Long = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKey3Short() const { return _funcKey3Short; } void AnytoneKeySettingsExtension::setFuncKey3Short(KeyFunction func) { if (_funcKey3Short == func) return; _funcKey3Short = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKey3Long() const { return _funcKey3Long; } void AnytoneKeySettingsExtension::setFuncKey3Long(KeyFunction func) { if (_funcKey3Long == func) return; _funcKey3Long = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKey4Short() const { return _funcKey4Short; } void AnytoneKeySettingsExtension::setFuncKey4Short(KeyFunction func) { if (_funcKey4Short == func) return; _funcKey4Short = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKey4Long() const { return _funcKey4Long; } void AnytoneKeySettingsExtension::setFuncKey4Long(KeyFunction func) { if (_funcKey4Long == func) return; _funcKey4Long = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKey5Short() const { return _funcKey5Short; } void AnytoneKeySettingsExtension::setFuncKey5Short(KeyFunction func) { if (_funcKey5Short == func) return; _funcKey5Short = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKey5Long() const { return _funcKey5Long; } void AnytoneKeySettingsExtension::setFuncKey5Long(KeyFunction func) { if (_funcKey5Long == func) return; _funcKey5Long = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKey6Short() const { return _funcKey6Short; } void AnytoneKeySettingsExtension::setFuncKey6Short(KeyFunction func) { if (_funcKey6Short == func) return; _funcKey6Short = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKey6Long() const { return _funcKey6Long; } void AnytoneKeySettingsExtension::setFuncKey6Long(KeyFunction func) { if (_funcKey6Long == func) return; _funcKey6Long = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKeyAShort() const { return _funcKeyAShort; } void AnytoneKeySettingsExtension::setFuncKeyAShort(KeyFunction func) { if (_funcKeyAShort == func) return; _funcKeyAShort = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKeyALong() const { return _funcKeyALong; } void AnytoneKeySettingsExtension::setFuncKeyALong(KeyFunction func) { if (_funcKeyALong == func) return; _funcKeyALong = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKeyBShort() const { return _funcKeyBShort; } void AnytoneKeySettingsExtension::setFuncKeyBShort(KeyFunction func) { if (_funcKeyBShort == func) return; _funcKeyBShort = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKeyBLong() const { return _funcKeyBLong; } void AnytoneKeySettingsExtension::setFuncKeyBLong(KeyFunction func) { if (_funcKeyBLong == func) return; _funcKeyBLong = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKeyCShort() const { return _funcKeyCShort; } void AnytoneKeySettingsExtension::setFuncKeyCShort(KeyFunction func) { if (_funcKeyCShort == func) return; _funcKeyCShort = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKeyCLong() const { return _funcKeyCLong; } void AnytoneKeySettingsExtension::setFuncKeyCLong(KeyFunction func) { if (_funcKeyCLong == func) return; _funcKeyCLong = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKeyDShort() const { return _funcKeyDShort; } void AnytoneKeySettingsExtension::setFuncKeyDShort(KeyFunction func) { if (_funcKeyDShort == func) return; _funcKeyDShort = func; emit modified(this); } AnytoneKeySettingsExtension::KeyFunction AnytoneKeySettingsExtension::funcKeyDLong() const { return _funcKeyDLong; } void AnytoneKeySettingsExtension::setFuncKeyDLong(KeyFunction func) { if (_funcKeyDLong == func) return; _funcKeyDLong = func; emit modified(this); } Interval AnytoneKeySettingsExtension::longPressDuration() const { return _longPressDuration; } void AnytoneKeySettingsExtension::setLongPressDuration(Interval ms) { if (_longPressDuration == ms) return; _longPressDuration = ms; emit modified(this); } bool AnytoneKeySettingsExtension::autoKeyLockEnabled() const { return _autoKeyLock; } void AnytoneKeySettingsExtension::enableAutoKeyLock(bool enabled) { if (_autoKeyLock==enabled) return; _autoKeyLock = enabled; emit modified(this); } bool AnytoneKeySettingsExtension::knobLockEnabled() const { return _knobLock; } void AnytoneKeySettingsExtension::enableKnobLock(bool enable) { if (_knobLock == enable) return; _knobLock = enable; emit modified(this); } bool AnytoneKeySettingsExtension::keypadLockEnabled() const { return _keypadLock; } void AnytoneKeySettingsExtension::enableKeypadLock(bool enable) { if (_keypadLock == enable) return; _keypadLock = enable; emit modified(this); } bool AnytoneKeySettingsExtension::sideKeysLockEnabled() const { return _sideKeysLock; } void AnytoneKeySettingsExtension::enableSideKeysLock(bool enable) { if (_sideKeysLock == enable) return; _sideKeysLock = enable; emit modified(this); } bool AnytoneKeySettingsExtension::forcedKeyLockEnabled() const { return _forcedKeyLock; } void AnytoneKeySettingsExtension::enableForcedKeyLock(bool enable) { if (_forcedKeyLock == enable) return; _forcedKeyLock = enable; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneToneSettingsExtension * ********************************************************************************************* */ AnytoneToneSettingsExtension::AnytoneToneSettingsExtension(QObject *parent) : ConfigItem(parent), _keyTone(false), _smsAlert(false), _callAlert(false), _talkPermitDigital(false), _talkPermitAnalog(false), _resetToneDigital(false), _dmrIdleChannelTone(false), _fmIdleChannelTone(false), _startupTone(false), _totNotification(false), _callMelody(new Melody(100, this)), _idleMelody(new Melody(100, this)), _resetMelody(new Melody(100, this)), _callEndMelody(new Melody(100, this)), _keyToneLevel(0) { connect(_callMelody, &Melody::modified, this, &AnytoneToneSettingsExtension::modified); connect(_idleMelody, &Melody::modified, this, &AnytoneToneSettingsExtension::modified); connect(_resetMelody, &Melody::modified, this, &AnytoneToneSettingsExtension::modified); } ConfigItem * AnytoneToneSettingsExtension::clone() const { AnytoneToneSettingsExtension *ext = new AnytoneToneSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } bool AnytoneToneSettingsExtension::keyToneEnabled() const { return _keyTone; } void AnytoneToneSettingsExtension::enableKeyTone(bool enable) { if (_keyTone==enable) return; _keyTone = enable; emit modified(this); } bool AnytoneToneSettingsExtension::smsAlertEnabled() const { return _smsAlert; } void AnytoneToneSettingsExtension::enableSMSAlert(bool enable) { if (_smsAlert == enable) return; _smsAlert = enable; emit modified(this); } bool AnytoneToneSettingsExtension::callAlertEnabled() const { return _callAlert; } void AnytoneToneSettingsExtension::enableCallAlert(bool enable) { if (_callAlert == enable) return; _callAlert = enable; emit modified(this); } bool AnytoneToneSettingsExtension::talkPermitDigitalEnabled() const { return _talkPermitDigital; } void AnytoneToneSettingsExtension::enableTalkPermitDigital(bool enable) { if (_talkPermitDigital == enable) return; _talkPermitDigital = enable; emit modified(this); } bool AnytoneToneSettingsExtension::talkPermitAnalogEnabled() const { return _talkPermitAnalog; } void AnytoneToneSettingsExtension::enableTalkPermitAnalog(bool enable) { if (_talkPermitAnalog == enable) return; _talkPermitAnalog = enable; emit modified(this); } bool AnytoneToneSettingsExtension::digitalResetToneEnabled() const { return _resetToneDigital; } void AnytoneToneSettingsExtension::enableDigitalResetTone(bool enable) { if (_resetToneDigital == enable) return; _resetToneDigital = enable; emit modified(this); } bool AnytoneToneSettingsExtension::dmrIdleChannelToneEnabled() const { return _dmrIdleChannelTone; } void AnytoneToneSettingsExtension::enableDMRIdleChannelTone(bool enable) { if (_dmrIdleChannelTone == enable) return; _dmrIdleChannelTone = enable; return modified(this); } bool AnytoneToneSettingsExtension::fmIdleChannelToneEnabled() const { return _fmIdleChannelTone; } void AnytoneToneSettingsExtension::enableFMIdleChannelTone(bool enable) { if (_fmIdleChannelTone == enable) return; _fmIdleChannelTone = enable; return modified(this); } bool AnytoneToneSettingsExtension::startupToneEnabled() const { return _startupTone; } void AnytoneToneSettingsExtension::enableStartupTone(bool enable) { if (_startupTone == enable) return; _startupTone = enable; return modified(this); } bool AnytoneToneSettingsExtension::totNotification() const { return _totNotification; } void AnytoneToneSettingsExtension::enableTOTNotification(bool enable) { if (enable == _totNotification) return; _totNotification = enable; emit modified(this); } Melody * AnytoneToneSettingsExtension::callMelody() const { return _callMelody; } Melody * AnytoneToneSettingsExtension::idleMelody() const { return _idleMelody; } Melody * AnytoneToneSettingsExtension::resetMelody() const { return _resetMelody; } Melody * AnytoneToneSettingsExtension::callEndMelody() const { return _callEndMelody; } unsigned int AnytoneToneSettingsExtension::keyToneLevel() const { return _keyToneLevel; } void AnytoneToneSettingsExtension::setKeyToneLevel(unsigned int level) { if (_keyToneLevel == level) return; _keyToneLevel = level; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneDisplaySettingsExtension * ********************************************************************************************* */ AnytoneDisplaySettingsExtension::AnytoneDisplaySettingsExtension(QObject *parent) : ConfigItem(parent), _displayFrequency(false), _brightness(5), _backlightDuration(Interval::fromSeconds(10)), _volumeChangePrompt(true), _callEndPrompt(true), _lastCallerDisplay(LastCallerDisplayMode::Both), _showClock(true), _showCall(true), _callColor(Color::Orange), _language(Language::English), _dateFormat(DateFormat::DayFirst), _showChannelNumber(true), _showColorCode(true), _showTimeSlot(true), _showChannelType(true), _showContact(true), _standbyTextColor(Color::White), _standbyBackgroundColor(Color::Black), _showLastHeard(false), _backlightDurationTX(), _backlightDurationRX(), _customChannelBackground(false), _channelNameColor(Color::Orange), _channelBNameColor(Color::Orange), _zoneNameColor(Color::White), _zoneBNameColor(Color::White) { // pass... } ConfigItem * AnytoneDisplaySettingsExtension::clone() const { AnytoneDisplaySettingsExtension *ext = new AnytoneDisplaySettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } unsigned int AnytoneDisplaySettingsExtension::brightness() const { return _brightness; } void AnytoneDisplaySettingsExtension::setBrightness(unsigned int level) { if (_brightness == level) return; _brightness = level; emit modified(this); } bool AnytoneDisplaySettingsExtension::displayFrequencyEnabled() const { return _displayFrequency; } void AnytoneDisplaySettingsExtension::enableDisplayFrequency(bool enable) { if (_displayFrequency == enable) return; _displayFrequency = enable; emit modified(this); } Interval AnytoneDisplaySettingsExtension::backlightDuration() const { return _backlightDuration; } void AnytoneDisplaySettingsExtension::setBacklightDuration(Interval sec) { if (_backlightDuration == sec) return; _backlightDuration = sec; emit modified(this); } bool AnytoneDisplaySettingsExtension::volumeChangePromptEnabled() const { return _volumeChangePrompt; } void AnytoneDisplaySettingsExtension::enableVolumeChangePrompt(bool enable) { if (_volumeChangePrompt == enable) return; _volumeChangePrompt = enable; emit modified(this); } bool AnytoneDisplaySettingsExtension::callEndPromptEnabled() const { return _callEndPrompt; } void AnytoneDisplaySettingsExtension::enableCallEndPrompt(bool enable) { if (_callEndPrompt == enable) return; _callEndPrompt = enable; emit modified(this); } AnytoneDisplaySettingsExtension::LastCallerDisplayMode AnytoneDisplaySettingsExtension::lastCallerDisplay() const { return _lastCallerDisplay; } void AnytoneDisplaySettingsExtension::setLastCallerDisplay(LastCallerDisplayMode mode) { if (_lastCallerDisplay == mode) return; _lastCallerDisplay = mode; emit modified(this); } bool AnytoneDisplaySettingsExtension::showClockEnabled() const { return _showClock; } void AnytoneDisplaySettingsExtension::enableShowClock(bool enable) { if (_showClock == enable) return; _showClock = enable; emit modified(this); } bool AnytoneDisplaySettingsExtension::showCallEnabled() const { return _showCall; } void AnytoneDisplaySettingsExtension::enableShowCall(bool enable) { if (_showCall == enable) return; _showCall = enable; emit modified(this); } AnytoneDisplaySettingsExtension::Color AnytoneDisplaySettingsExtension::callColor() const { return _callColor; } void AnytoneDisplaySettingsExtension::setCallColor(Color color) { if (_callColor == color) return; _callColor = color; emit modified(this); } AnytoneDisplaySettingsExtension::Language AnytoneDisplaySettingsExtension::language() const { return _language; } void AnytoneDisplaySettingsExtension::setLanguage(Language lang) { if (_language == lang) return; _language = lang; emit modified(this); } AnytoneDisplaySettingsExtension::DateFormat AnytoneDisplaySettingsExtension::dateFormat() const { return _dateFormat; } void AnytoneDisplaySettingsExtension::setDateFormat(DateFormat format) { if (format == _dateFormat) return; _dateFormat = format; emit modified(this); } bool AnytoneDisplaySettingsExtension::showChannelNumberEnabled() const { return _showChannelNumber; } void AnytoneDisplaySettingsExtension::enableShowChannelNumber(bool enable) { if (_showChannelNumber == enable) return; _showChannelNumber = enable; emit modified(this); } bool AnytoneDisplaySettingsExtension::showColorCode() const { return _showColorCode; } void AnytoneDisplaySettingsExtension::enableShowColorCode(bool enable) { if (_showColorCode == enable) return; _showColorCode = enable; emit modified(this); } bool AnytoneDisplaySettingsExtension::showTimeSlot() const { return _showTimeSlot; } void AnytoneDisplaySettingsExtension::enableShowTimeSlot(bool enable) { if (_showTimeSlot == enable) return; _showTimeSlot = enable; emit modified(this); } bool AnytoneDisplaySettingsExtension::showChannelType() const { return _showChannelType; } void AnytoneDisplaySettingsExtension::enableShowChannelType(bool enable) { if (_showChannelType == enable) return; _showChannelType = enable; emit modified(this); } bool AnytoneDisplaySettingsExtension::showContact() const { return _showContact; } void AnytoneDisplaySettingsExtension::enableShowContact(bool enable) { if (_showContact == enable) return; _showContact = enable; emit modified(this); } bool AnytoneDisplaySettingsExtension::showLastHeardEnabled() const { return _showLastHeard; } void AnytoneDisplaySettingsExtension::enableShowLastHeard(bool enable) { if (_showLastHeard == enable) return; _showLastHeard = enable; emit modified(this); } AnytoneDisplaySettingsExtension::Color AnytoneDisplaySettingsExtension::standbyTextColor() const { return _standbyTextColor; } void AnytoneDisplaySettingsExtension::setStandbyTextColor(Color color) { if (_standbyTextColor == color) return; _standbyTextColor = color; emit modified(this); } AnytoneDisplaySettingsExtension::Color AnytoneDisplaySettingsExtension::standbyBackgroundColor() const { return _standbyBackgroundColor; } void AnytoneDisplaySettingsExtension::setStandbyBackgroundColor(Color color) { if (_standbyBackgroundColor == color) return; _standbyBackgroundColor = color; emit modified(this); } Interval AnytoneDisplaySettingsExtension::backlightDurationTX() const { return _backlightDurationTX; } void AnytoneDisplaySettingsExtension::setBacklightDurationTX(Interval sec) { if (_backlightDurationTX == sec) return; _backlightDurationTX = sec; emit modified(this); } AnytoneDisplaySettingsExtension::Color AnytoneDisplaySettingsExtension::channelNameColor() const { return _channelNameColor; } void AnytoneDisplaySettingsExtension::setChannelNameColor(Color color) { if (_channelNameColor == color) return; _channelNameColor = color; emit modified(this); } AnytoneDisplaySettingsExtension::Color AnytoneDisplaySettingsExtension::channelBNameColor() const { return _channelBNameColor; } void AnytoneDisplaySettingsExtension::setChannelBNameColor(Color color) { if (_channelBNameColor == color) return; _channelBNameColor = color; emit modified(this); } AnytoneDisplaySettingsExtension::Color AnytoneDisplaySettingsExtension::zoneNameColor() const { return _zoneNameColor; } void AnytoneDisplaySettingsExtension::setZoneNameColor(Color color) { if (_zoneNameColor == color) return; _zoneNameColor = color; emit modified(this); } AnytoneDisplaySettingsExtension::Color AnytoneDisplaySettingsExtension::zoneBNameColor() const { return _zoneBNameColor; } void AnytoneDisplaySettingsExtension::setZoneBNameColor(Color color) { if (_zoneBNameColor == color) return; _zoneBNameColor = color; emit modified(this); } Interval AnytoneDisplaySettingsExtension::backlightDurationRX() const { return _backlightDurationRX; } void AnytoneDisplaySettingsExtension::setBacklightDurationRX(Interval sec) { if (_backlightDurationRX == sec) return; _backlightDurationRX = sec; emit modified(this); } bool AnytoneDisplaySettingsExtension::customChannelBackground() const { return _customChannelBackground; } void AnytoneDisplaySettingsExtension::enableCustomChannelBackground(bool enable) { if (enable == _customChannelBackground) return; _customChannelBackground = enable; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneAudioSettingsExtension * ********************************************************************************************* */ AnytoneAudioSettingsExtension::AnytoneAudioSettingsExtension(QObject *parent) : ConfigItem(parent), _voxDelay(), _recording(false), _voxSource(VoxSource::Both), _maxVolume(3), _maxHeadPhoneVolume(3), _enhanceAudio(true), _muteDelay(Interval::fromMinutes(1)), _enableAnalogMicGain(false), _analogMicGain(1) { // pass... } ConfigItem * AnytoneAudioSettingsExtension::clone() const { AnytoneAudioSettingsExtension *ext = new AnytoneAudioSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } Interval AnytoneAudioSettingsExtension::voxDelay() const { return _voxDelay; } void AnytoneAudioSettingsExtension::setVOXDelay(Interval ms) { if (_voxDelay == ms) return; _voxDelay = ms; emit modified(this); } bool AnytoneAudioSettingsExtension::recordingEnabled() const { return _recording; } void AnytoneAudioSettingsExtension::enableRecording(bool enable) { if (_recording == enable) return; _recording = enable; emit modified(this); } AnytoneAudioSettingsExtension::VoxSource AnytoneAudioSettingsExtension::voxSource() const { return _voxSource; } void AnytoneAudioSettingsExtension::setVOXSource(VoxSource source) { if (_voxSource == source) return; _voxSource = source; emit modified(this); } unsigned int AnytoneAudioSettingsExtension::maxVolume() const { return _maxVolume; } void AnytoneAudioSettingsExtension::setMaxVolume(unsigned int vol) { if (_maxVolume == vol) return; _maxVolume = vol; emit modified(this); } unsigned int AnytoneAudioSettingsExtension::maxHeadPhoneVolume() const { return _maxHeadPhoneVolume; } void AnytoneAudioSettingsExtension::setMaxHeadPhoneVolume(unsigned int vol) { if (_maxHeadPhoneVolume == vol) return; _maxHeadPhoneVolume = vol; emit modified(this); } bool AnytoneAudioSettingsExtension::enhanceAudioEnabled() const { return _enhanceAudio; } void AnytoneAudioSettingsExtension::enableEnhanceAudio(bool enable) { if (_enhanceAudio == enable) return; _enhanceAudio = enable; emit modified(this); } Interval AnytoneAudioSettingsExtension::muteDelay() const { return _muteDelay; } void AnytoneAudioSettingsExtension::setMuteDelay(Interval intv) { if (_muteDelay == intv) return; _muteDelay = intv; emit modified(this); } bool AnytoneAudioSettingsExtension::fmMicGainEnabled() const { return _enableAnalogMicGain; } void AnytoneAudioSettingsExtension::enableFMMicGain(bool enable) { if (_enableAnalogMicGain == enable) return; _enableAnalogMicGain = enable; emit modified(this); } unsigned int AnytoneAudioSettingsExtension::fmMicGain() const { return _analogMicGain; } void AnytoneAudioSettingsExtension::setFMMicGain(unsigned int gain) { if (_analogMicGain == gain) return; _analogMicGain = gain; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneMenuSettingsExtension * ********************************************************************************************* */ AnytoneMenuSettingsExtension::AnytoneMenuSettingsExtension(QObject *parent) : ConfigItem(parent), _menuDuration(Interval::fromSeconds(15)), _showSeparator(false) { // pass... } ConfigItem * AnytoneMenuSettingsExtension::clone() const { AnytoneMenuSettingsExtension *ext = new AnytoneMenuSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } Interval AnytoneMenuSettingsExtension::duration() const { return _menuDuration; } void AnytoneMenuSettingsExtension::setDuration(Interval sec) { if (_menuDuration == sec) return; _menuDuration = sec; emit modified(this); } bool AnytoneMenuSettingsExtension::separatorEnabled() const { return _showSeparator; } void AnytoneMenuSettingsExtension::enableSeparator(bool enable) { if (_showSeparator == enable) return; _showSeparator = enable; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneAutoRepeaterSettingsExtension * ********************************************************************************************* */ AnytoneAutoRepeaterSettingsExtension::AnytoneAutoRepeaterSettingsExtension(QObject *parent) : ConfigItem(parent), _directionA(Direction::Off), _directionB(Direction::Off), _minVHF(Frequency::fromMHz(136)), _maxVHF(Frequency::fromMHz(174)), _minUHF(Frequency::fromMHz(400)), _maxUHF(Frequency::fromMHz(480)), _vhfOffset(new AnytoneAutoRepeaterOffsetRef(this)), _uhfOffset(new AnytoneAutoRepeaterOffsetRef(this)), _minVHF2(Frequency::fromMHz(136)), _maxVHF2(Frequency::fromMHz(174)), _minUHF2(Frequency::fromMHz(400)), _maxUHF2(Frequency::fromMHz(480)), _vhf2Offset(new AnytoneAutoRepeaterOffsetRef(this)), _uhf2Offset(new AnytoneAutoRepeaterOffsetRef(this)), _offsets(new AnytoneAutoRepeaterOffsetList(this)) { // pass... } ConfigItem * AnytoneAutoRepeaterSettingsExtension::clone() const { AnytoneAutoRepeaterSettingsExtension *ext = new AnytoneAutoRepeaterSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } AnytoneAutoRepeaterSettingsExtension::Direction AnytoneAutoRepeaterSettingsExtension::directionA() const { return _directionA; } void AnytoneAutoRepeaterSettingsExtension::setDirectionA(Direction dir) { if (_directionA == dir) return; _directionA = dir; emit modified(this); } AnytoneAutoRepeaterSettingsExtension::Direction AnytoneAutoRepeaterSettingsExtension::directionB() const { return _directionB; } void AnytoneAutoRepeaterSettingsExtension::setDirectionB(Direction dir) { if (_directionB == dir) return; _directionB = dir; emit modified(this); } Frequency AnytoneAutoRepeaterSettingsExtension::vhfMin() const { return _minVHF; } void AnytoneAutoRepeaterSettingsExtension::setVHFMin(Frequency Hz) { if (_minVHF == Hz) return; _minVHF = Hz; emit modified(this); } Frequency AnytoneAutoRepeaterSettingsExtension::vhfMax() const { return _maxVHF; } void AnytoneAutoRepeaterSettingsExtension::setVHFMax(Frequency Hz) { if (_maxVHF == Hz) return; _maxVHF = Hz; emit modified(this); } Frequency AnytoneAutoRepeaterSettingsExtension::uhfMin() const { return _minUHF; } void AnytoneAutoRepeaterSettingsExtension::setUHFMin(Frequency Hz) { if (_minUHF == Hz) return; _minUHF = Hz; emit modified(this); } Frequency AnytoneAutoRepeaterSettingsExtension::uhfMax() const { return _maxUHF; } void AnytoneAutoRepeaterSettingsExtension::setUHFMax(Frequency Hz) { if (_maxUHF == Hz) return; _maxUHF = Hz; emit modified(this); } AnytoneAutoRepeaterOffsetRef * AnytoneAutoRepeaterSettingsExtension::vhfRef() const { return _vhfOffset; } AnytoneAutoRepeaterOffsetRef * AnytoneAutoRepeaterSettingsExtension::uhfRef() const { return _uhfOffset; } Frequency AnytoneAutoRepeaterSettingsExtension::vhf2Min() const { return _minVHF2; } void AnytoneAutoRepeaterSettingsExtension::setVHF2Min(Frequency Hz) { if (_minVHF2 == Hz) return; _minVHF2 = Hz; emit modified(this); } Frequency AnytoneAutoRepeaterSettingsExtension::vhf2Max() const { return _maxVHF2; } void AnytoneAutoRepeaterSettingsExtension::setVHF2Max(Frequency Hz) { if (_maxVHF2 == Hz) return; _maxVHF2 = Hz; emit modified(this); } Frequency AnytoneAutoRepeaterSettingsExtension::uhf2Min() const { return _minUHF2; } void AnytoneAutoRepeaterSettingsExtension::setUHF2Min(Frequency Hz) { if (_minUHF2 == Hz) return; _minUHF2 = Hz; emit modified(this); } Frequency AnytoneAutoRepeaterSettingsExtension::uhf2Max() const { return _maxUHF2; } void AnytoneAutoRepeaterSettingsExtension::setUHF2Max(Frequency Hz) { if (_maxUHF2 == Hz) return; _maxUHF2 = Hz; emit modified(this); } AnytoneAutoRepeaterOffsetRef * AnytoneAutoRepeaterSettingsExtension::vhf2Ref() const { return _vhf2Offset; } AnytoneAutoRepeaterOffsetRef * AnytoneAutoRepeaterSettingsExtension::uhf2Ref() const { return _uhf2Offset; } AnytoneAutoRepeaterOffsetList * AnytoneAutoRepeaterSettingsExtension::offsets() const { return _offsets; } /* ********************************************************************************************* * * Implementation of AnytoneAutoRepeaterOffset * ********************************************************************************************* */ AnytoneAutoRepeaterOffset::AnytoneAutoRepeaterOffset(QObject *parent) : ConfigObject(parent), _offset() { // pass... } ConfigItem * AnytoneAutoRepeaterOffset::clone() const { AnytoneAutoRepeaterOffset *off = new AnytoneAutoRepeaterOffset(); if (! off->copy(*this)) { off->deleteLater(); return nullptr; } return off; } Frequency AnytoneAutoRepeaterOffset::offset() const { return _offset; } void AnytoneAutoRepeaterOffset::setOffset(Frequency offset) { if (_offset == offset) return; _offset = offset; emit modified(this); } ConfigItem * AnytoneAutoRepeaterOffsetList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err); return new AnytoneAutoRepeaterOffset(); } /* ********************************************************************************************* * * Implementation of AnytoneAutoRepeaterOffsetRef * ********************************************************************************************* */ AnytoneAutoRepeaterOffsetRef::AnytoneAutoRepeaterOffsetRef(QObject *parent) : ConfigObjectReference(AnytoneAutoRepeaterOffset::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneAutoRepeaterOffsetList * ********************************************************************************************* */ AnytoneAutoRepeaterOffsetList::AnytoneAutoRepeaterOffsetList(QObject *parent) : ConfigObjectList(AnytoneAutoRepeaterOffset::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneBluetoothSettingsExtension * ********************************************************************************************* */ AnytoneBluetoothSettingsExtension::AnytoneBluetoothSettingsExtension(QObject *parent) : ConfigItem(parent), _pttLatch(false), _pttSleep(Interval::fromMilliseconds(0)) { // pass... } ConfigItem * AnytoneBluetoothSettingsExtension::clone() const { AnytoneBluetoothSettingsExtension *ext = new AnytoneBluetoothSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } bool AnytoneBluetoothSettingsExtension::pttLatch() const { return _pttLatch; } void AnytoneBluetoothSettingsExtension::enablePTTLatch(bool enable) { if (enable == _pttLatch) return; _pttLatch = enable; emit modified(this); } Interval AnytoneBluetoothSettingsExtension::pttSleepTimer() const { return _pttSleep; } void AnytoneBluetoothSettingsExtension::setPTTSleepTimer(Interval intv) { if (intv == _pttSleep) return; _pttSleep = intv; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneSimplexRepeaterSettingsExtension * ********************************************************************************************* */ AnytoneSimplexRepeaterSettingsExtension::AnytoneSimplexRepeaterSettingsExtension(QObject *parent) : ConfigItem(parent), _enabled(false), _monitor(false), _timeSlot(TimeSlot::Channel) { // pass... } ConfigItem * AnytoneSimplexRepeaterSettingsExtension::clone() const { AnytoneSimplexRepeaterSettingsExtension *ext = new AnytoneSimplexRepeaterSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } bool AnytoneSimplexRepeaterSettingsExtension::enabled() const { return _enabled; } void AnytoneSimplexRepeaterSettingsExtension::enable(bool enable) { if (_enabled == enable) return; _enabled = enable; emit modified(this); } bool AnytoneSimplexRepeaterSettingsExtension::monitorEnabled() const { return _monitor; } void AnytoneSimplexRepeaterSettingsExtension::enableMonitor(bool enable) { if (_monitor == enable) return; _monitor = enable; emit modified(this); } AnytoneSimplexRepeaterSettingsExtension::TimeSlot AnytoneSimplexRepeaterSettingsExtension::timeSlot() const { return _timeSlot; } void AnytoneSimplexRepeaterSettingsExtension::setTimeSlot(TimeSlot ts) { if (_timeSlot == ts) return; _timeSlot = ts; emit modified(this); } /* ********************************************************************************************* * * Implementation of AnytoneFMAPRSSettingsExtension * ********************************************************************************************* */ AnytoneFMAPRSSettingsExtension::AnytoneFMAPRSSettingsExtension(QObject *parent) : ConfigExtension(parent), _txDelay(Interval::fromMilliseconds(60)), _preWaveDelay(Interval::fromMilliseconds(0)), _passAll(false), _reportPosition(false), _reportMicE(false), _reportObject(false), _reportItem(false), _reportMessage(false), _reportWeather(false), _reportNMEA(false), _reportStatus(false), _reportOther(false), _frequencies(new AnytoneAPRSFrequencyList(this)) { // pass... } ConfigItem * AnytoneFMAPRSSettingsExtension::clone() const { AnytoneFMAPRSSettingsExtension *ext = new AnytoneFMAPRSSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } Interval AnytoneFMAPRSSettingsExtension::txDelay() const { return _txDelay; } void AnytoneFMAPRSSettingsExtension::setTXDelay(Interval intv) { if (_txDelay == intv) return; _txDelay = intv; emit modified(this); } Interval AnytoneFMAPRSSettingsExtension::preWaveDelay() const { return _preWaveDelay; } void AnytoneFMAPRSSettingsExtension::setPreWaveDelay(Interval intv) { if (_preWaveDelay == intv) return; _preWaveDelay = intv; emit modified(this); } bool AnytoneFMAPRSSettingsExtension::passAll() const { return _passAll; } void AnytoneFMAPRSSettingsExtension::enablePassAll(bool enable) { if (_passAll == enable) return; _passAll = enable; emit modified(this); } bool AnytoneFMAPRSSettingsExtension::reportPosition() const { return _reportPosition; } void AnytoneFMAPRSSettingsExtension::enableReportPosition(bool enable) { if (_reportPosition == enable) return; _reportPosition = enable; emit modified(this); } bool AnytoneFMAPRSSettingsExtension::reportMicE() const { return _reportMicE; } void AnytoneFMAPRSSettingsExtension::enableReportMicE(bool enable) { if (_reportMicE == enable) return; _reportMicE = enable; emit modified(this); } bool AnytoneFMAPRSSettingsExtension::reportObject() const { return _reportObject; } void AnytoneFMAPRSSettingsExtension::enableReportObject(bool enable) { if (_reportObject == enable) return; _reportObject = enable; emit modified(this); } bool AnytoneFMAPRSSettingsExtension::reportItem() const { return _reportItem; } void AnytoneFMAPRSSettingsExtension::enableReportItem(bool enable) { if (_reportItem == enable) return; _reportItem = enable; emit modified(this); } bool AnytoneFMAPRSSettingsExtension::reportMessage() const { return _reportMessage; } void AnytoneFMAPRSSettingsExtension::enableReportMessage(bool enable) { if (_reportMessage == enable) return; _reportMessage = enable; emit modified(this); } bool AnytoneFMAPRSSettingsExtension::reportWeather() const { return _reportWeather; } void AnytoneFMAPRSSettingsExtension::enableReportWeather(bool enable) { if (_reportWeather == enable) return; _reportWeather = enable; emit modified(this); } bool AnytoneFMAPRSSettingsExtension::reportNMEA() const { return _reportNMEA; } void AnytoneFMAPRSSettingsExtension::enableReportNMEA(bool enable) { if (_reportNMEA == enable) return; _reportNMEA = enable; emit modified(this); } bool AnytoneFMAPRSSettingsExtension::reportStatus() const { return _reportStatus; } void AnytoneFMAPRSSettingsExtension::enableReportStatus(bool enable) { if (_reportStatus == enable) return; _reportStatus = enable; emit modified(this); } bool AnytoneFMAPRSSettingsExtension::reportOther() const { return _reportOther; } void AnytoneFMAPRSSettingsExtension::enableReportOther(bool enable) { if (_reportOther == enable) return; _reportOther = enable; emit modified(this); } AnytoneAPRSFrequencyList * AnytoneFMAPRSSettingsExtension::frequencies() const { return _frequencies; } qdmr-0.12.3/lib/anytone_extension.hh000066400000000000000000003151321501654372000174100ustar00rootroot00000000000000#ifndef ANYTONEEXTENSION_HH #define ANYTONEEXTENSION_HH #include "configobject.hh" #include "configreference.hh" #include "melody.hh" #include "frequency.hh" #include "interval.hh" #include "signaling.hh" #include /** Implements the config representation of an FM APRS frequency. * @ingroup anytone */ class AnytoneAPRSFrequency: public ConfigObject { Q_OBJECT Q_CLASSINFO("IdPrefix", "af") Q_CLASSINFO("frequencyDecription", "Transmit-frequency.") /** The frequency. */ Q_PROPERTY(Frequency frequency READ frequency WRITE setFrequency) public: /** Default constructor. */ Q_INVOKABLE explicit AnytoneAPRSFrequency(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the transmit frequency. */ Frequency frequency() const; /** Sets the transmit frequency. */ void setFrequency(Frequency freq); protected: /** The transmit frequency. */ Frequency _frequency; }; /** Represents a reference to an APRS frequency. * @ingroup anytone */ class AnytoneAPRSFrequencyRef: public ConfigObjectReference { Q_OBJECT public: /** Default constructor. */ explicit AnytoneAPRSFrequencyRef(QObject *parent=nullptr); }; /** Represents a list of APRS transmit frequencies. * @ingroup anytone */ class AnytoneAPRSFrequencyList: public ConfigObjectList { Q_OBJECT public: /** Empty constructor. */ explicit AnytoneAPRSFrequencyList(QObject *parent=nullptr); ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err); }; /** Implements the common properties for analog and digital AnyTone channels. * This class cannot be instantiated directly, use one of the derived classes. * @ingroup anytone */ class AnytoneChannelExtension: public ConfigExtension { Q_OBJECT /** If @c true, talkaround is enabled. */ Q_PROPERTY(bool talkaround READ talkaround WRITE enableTalkaround) /** Holds the frequency correction in some unknown units. */ Q_PROPERTY(int frequencyCorrection READ frequencyCorrection WRITE setFrequencyCorrection) /** If @c true, the hands-free featrue is enabled for this channel. */ Q_PROPERTY(bool handsFree READ handsFree WRITE enableHandsFree) /** A reference to the FM APRS frequency. If not set, the default will be used. */ Q_PROPERTY(AnytoneAPRSFrequencyRef *fmAPRSFrequency READ fmAPRSFrequency()) /** Specifies if and when the position is send via the associated APRS system, once the PTT is * pressed. */ Q_PROPERTY(APRSPTT aprsPTT READ aprsPTT WRITE setAPRSPTT) public: /** Possible APRS PTT modes. */ enum class APRSPTT{ Off, Start, End }; Q_ENUM(APRSPTT) protected: /** Hidden constructor. */ explicit AnytoneChannelExtension(QObject *parent=nullptr); public: /** Returns @c true, if talkaround is enabled. */ bool talkaround() const; /** Enables/disables talkaround. */ void enableTalkaround(bool enable); /** Returns the frequency correction in some unknown units. */ int frequencyCorrection() const; /** Sets the frequency correction. */ void setFrequencyCorrection(int corr); /** Returns @c true if the hands-free feature is enabled. */ bool handsFree() const; /** Enables/disables the hands-free feature for this channel. */ void enableHandsFree(bool enable); /** Holds a reference to the FM APRS frequency to be used if FM APRS is enabled on the channel. */ AnytoneAPRSFrequencyRef *fmAPRSFrequency() const; /** Holds the APRS PTT mode. That his, if and when the APRS information is send via the * associated APRS system. */ APRSPTT aprsPTT() const; /** Sets the APRS PTT mode. */ void setAPRSPTT(APRSPTT mode); protected: /** If @c true, talkaround is enabled. */ bool _talkaround; /** The frequency correction. */ int _frequencyCorrection; /** If @c true, the hands-free featrue is enabled for this channel. */ bool _handsFree; /** A reference to the FM APRS frequency. */ AnytoneAPRSFrequencyRef *_fmAPRSFrequency; /** Holds the APRS PTT mode. */ APRSPTT _aprsPTT; }; /** Implements the settings extension for FM channels on AnyTone devices. * @ingroup anytone */ class AnytoneFMChannelExtension: public AnytoneChannelExtension { Q_OBJECT /** If @c true, the CTCSS phase-reverse burst at the end of transmission is enabled. */ Q_PROPERTY(bool reverseBurst READ reverseBurst WRITE enableReverseBurst) /** If @c true, the custom CTCSS tone is used for RX (open squelch). */ Q_PROPERTY(bool rxCustomCTCSS READ rxCustomCTCSS WRITE enableRXCustomCTCSS) /** If @c true, the custom CTCSS tone is transmitted. */ Q_PROPERTY(bool txCustomCTCSS READ txCustomCTCSS WRITE enableTXCustomCTCSS) /** Holds the custom CTCSS tone frequency in Hz. Resolution is 0.1Hz */ Q_PROPERTY(double customCTCSS READ customCTCSS WRITE setCustomCTCSS) /** Holds the squelch mode. */ Q_PROPERTY(SquelchMode squelchMode READ squelchMode WRITE setSquelchMode) /** If @c true, the analog scrabler is enabled. */ Q_PROPERTY(bool scrambler READ scrambler WRITE enableScrambler) public: /** Possible squelch mode settings. */ enum class SquelchMode { Carrier = 0, SubTone = 1, OptSig = 2, SubToneAndOptSig = 3, SubToneOrOptSig = 4 }; Q_ENUM(SquelchMode) public: /** Default constructor. */ Q_INVOKABLE explicit AnytoneFMChannelExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns @c true, if the CTCSS phase-reverse burst is enabled. */ bool reverseBurst() const; /** Enables/disables the CTCSS phase-reverse burst. */ void enableReverseBurst(bool enable); /** Returns @c true, if the custom CTCSS frequency is used for RX (open squelch). */ bool rxCustomCTCSS() const; /** Enables/disables usage of custom CTCSS frequency for RX. */ void enableRXCustomCTCSS(bool enable); /** Returns @c true, if the custom CTCSS frequency is used for TX (open squelch). */ bool txCustomCTCSS() const; /** Enables/disables usage of custom CTCSS frequency for TX. */ void enableTXCustomCTCSS(bool enable); /** Returns the custom CTCSS frequency in Hz. Resolution is 0.1Hz. */ double customCTCSS() const; /** Sets the custom CTCSS frequency in Hz. Resolution is 0.1Hz. */ void setCustomCTCSS(double freq); /** Returns the squelch mode. */ SquelchMode squelchMode() const; /** Sets the squelch mode. */ void setSquelchMode(SquelchMode mode); /** Reuturns @c true, if the analog scrambler is enabled. */ bool scrambler() const; /** Enables/disables the analog scrambler. */ void enableScrambler(bool enable); protected: /** If @c true, the CTCSS phase-reverse burst at the end of transmission is enabled. */ bool _reverseBurst; /** If @c true, the custom CTCSS tone is used for RX (open squelch). */ bool _rxCustomCTCSS; /** If @c true, the custom CTCSS tone is transmitted. */ bool _txCustomCTCSS; /** Holds the custom CTCSS tone frequency in Hz. Resolution is 0.1Hz */ double _customCTCSS; /** Holds the squelch mode. */ SquelchMode _squelchMode; /** If @c true, the analog scrambler is enabled for this channel. */ bool _scrambler; }; /** Implements the settings extension for DMR channels on AnyTone devices. * @ingroup anytone */ class AnytoneDMRChannelExtension: public AnytoneChannelExtension { Q_OBJECT /** If @c true, the call confirmation is enabled. */ Q_PROPERTY(bool callConfirm READ callConfirm WRITE enableCallConfirm) /** If @c true, SMS reception is enabled. */ Q_PROPERTY(bool sms READ sms WRITE enableSMS) /** If @c true, the SMS confirmation is enabled. */ Q_PROPERTY(bool smsConfirm READ smsConfirm WRITE enableSMSConfirm) /** If @c true, the radio will response to received data packages. Should be enabled. */ Q_PROPERTY(bool dataACK READ dataACK WRITE enableDataACK) /** If @c true, the simplex TDMA mode is enabled (aka DCDM). */ Q_PROPERTY(bool simplexTDMA READ simplexTDMA WRITE enableSimplexTDMA) /** If @c true, the adaptive TDMA mode is enabled. This makes only sense, if @c simplexTDMA is * enabled too. In this case, the radio is able to receive both simplex TDMA as well as "normal" * simplex DMR on the channel. */ Q_PROPERTY(bool adaptiveTDMA READ adaptiveTDMA WRITE enableAdaptiveTDMA) /** If @c true, the lone-worker feature is enabled for this channel. */ Q_PROPERTY(bool loneWorker READ loneWorker WRITE enableLoneWorker) /** If @c true, the through mode is enabled (what ever that means). */ Q_PROPERTY(bool throughMode READ throughMode WRITE enableThroughMode) public: /** Default constructor. */ Q_INVOKABLE explicit AnytoneDMRChannelExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns @c true if the call confirmation is enabled. */ bool callConfirm() const; /** Enables/disables the call confirmation. */ void enableCallConfirm(bool enabled); /** Returns @c true if SMS reception is enabled. */ bool sms() const; /** Enables/disables SMS reception. */ void enableSMS(bool enable); /** Returns @c true if the SMS confirmation is enabled. */ bool smsConfirm() const; /** Enables/disables the SMS confirmation. */ void enableSMSConfirm(bool enabled); /** Returns @c true if the data acknowledgement is enabled. */ bool dataACK() const; /** Enables/disables the data acknowledgement. */ void enableDataACK(bool enable); /** Returns @c true if the simplex TDMA (DCDM) mode is enabled. */ bool simplexTDMA() const; /** Enables/disables the simplex TDMA (DCDM) mode. */ void enableSimplexTDMA(bool enable); /** Returns @c true if the adaptive TDMA mode is enabled. */ bool adaptiveTDMA() const; /** Enables/disables the adaptive TDMA mode. */ void enableAdaptiveTDMA(bool enable); /** Returns @c true if the lone-worker feature is enabled. */ bool loneWorker() const; /** Enables the lone-worker feature for this channel. */ void enableLoneWorker(bool enable); /** Returns @c true if the through mode is enabled. */ bool throughMode() const; /** Enables/disables the through mode. */ void enableThroughMode(bool enable); protected: /** If @c true, the call confirmation is enabled. */ bool _callConfirm; /** If @c true, the SMS reception is enabled. */ bool _sms; /** If @c true, the SMS confirmation is enabled. */ bool _smsConfirm; /** If @c true, the data acknowledgement is enabled. */ bool _dataACK; /** If @c true, the simplex TDMA mode is enabled. */ bool _simplexTDMA; /** If @c true, the adaptive TDMA mode is enabled. */ bool _adaptiveTDMA; /** If @c true the lone-worker feature is enabled. */ bool _loneWorker; /** If @c true the through mode is enabled. */ bool _throughMode; }; /** Implements the AnyTone extensions for zones. * @ingroup anytone */ class AnytoneZoneExtension : public ConfigExtension { Q_OBJECT /** If @c true, the zone is hidden in the menu. */ Q_PROPERTY(bool hidden READ hidden WRITE enableHidden) public: /** Default constructor. */ Q_INVOKABLE explicit AnytoneZoneExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns @c true if the zone is hidden. */ bool hidden() const; /** Enables/disables hidden zone. */ void enableHidden(bool enable); protected: /** If @c true, the zone is hidden in the menu. */ bool _hidden; }; /** Implements the AnyTone contact extension. * @ingroup anytone */ class AnytoneContactExtension: public ConfigExtension { Q_OBJECT /** Overrides the ring flag, allows to set None, Ring and Online. */ Q_PROPERTY(AlertType alertType READ alertType WRITE setAlertType) public: /** Possible ring-tone types. */ enum class AlertType { None = 0, ///< Alert disabled. Ring = 1, ///< Ring tone. Online = 2 ///< WTF? }; Q_ENUM(AlertType) public: /** Default constructor. */ Q_INVOKABLE explicit AnytoneContactExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the alert type for the contact. */ AlertType alertType() const; /** Sets the alert type for the contact. */ void setAlertType(AlertType type); protected: /** Holds the alert type for the contact. */ AlertType _alertType; }; /** Implements the boot settings extension of AnyTone devices. * This extension is part of the @c AnytoneSettingsExtension. * * @ingroup anytone */ class AnytoneBootSettingsExtension: public ConfigItem { Q_OBJECT /** The boot display setting. */ Q_PROPERTY(BootDisplay bootDisplay READ bootDisplay WRITE setBootDisplay) /** If @c true, the boot password is enabled. */ Q_PROPERTY(bool bootPasswordEnabled READ bootPasswordEnabled WRITE enableBootPassword) /** Holds the boot password. */ Q_PROPERTY(QString bootPassword READ bootPassword WRITE setBootPassword) Q_CLASSINFO("defaultChannelDescription", "If enabled, the default channels are selected at boot.") /** If enabled, the default channels are selected at boot. */ Q_PROPERTY(bool defaultChannel READ defaultChannelEnabled WRITE enableDefaultChannel) Q_CLASSINFO("zoneADescription", "The default zone for VFO A.") /** The default zone for VFO A. */ Q_PROPERTY(ZoneReference* zoneA READ zoneA) Q_CLASSINFO("channelADescription", "The default channel for VFO A. Must be within zone A.") /** The default channel for VFO A. */ Q_PROPERTY(ChannelReference* channelA READ channelA) Q_CLASSINFO("zoneBDescription", "The default zone for VFO B.") /** The current zone for VFO B. */ Q_PROPERTY(ZoneReference* zoneB READ zoneB) Q_CLASSINFO("channelBDescription", "The default channel for VFO B. Must be within zone B.") /** The default channel for VFO B. */ Q_PROPERTY(ChannelReference* channelB READ channelB) /** The priority zone for VFO A. */ Q_PROPERTY(ZoneReference* priorityZoneA READ priorityZoneA) /** The priority zone for VFO B. */ Q_PROPERTY(ZoneReference* priorityZoneB READ priorityZoneB) /** Enables the GPS check. */ Q_PROPERTY(bool gpsCheck READ gpsCheckEnabled WRITE enableGPSCheck) /** Enables the MCU reset on boot. */ Q_PROPERTY(bool reset READ resetEnabled WRITE enableReset) public: /** What to display during boot. */ enum class BootDisplay { Default = 0, CustomText = 1, CustomImage = 2 }; Q_ENUM(BootDisplay) public: /** Constructor. */ explicit AnytoneBootSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the boot display setting. */ BootDisplay bootDisplay() const; /** Sets the boot display. */ void setBootDisplay(BootDisplay mode); /** Returns @c true if the boot password is enabled.*/ bool bootPasswordEnabled() const; /** Enables the boot password. */ void enableBootPassword(bool enable); /** Returns the boot password. */ const QString &bootPassword() const; /** Sets the boot password. */ void setBootPassword(const QString &pass); /** If @c true, the radio switches to the default channel at boot. */ bool defaultChannelEnabled() const; /** Enables/disables boot default channel. */ void enableDefaultChannel(bool enable); /** Returns a reference to the default zone for VFO A. */ ZoneReference *zoneA() const; /** Returns a reference to the default channel for VFO A. */ ChannelReference *channelA() const; /** Returns a reference to the default zone for VFO B. */ ZoneReference *zoneB() const; /** Returns a reference to the default channel for VFO B. */ ChannelReference *channelB() const; /** Returns a reference to the priority zone for VFO A. */ ZoneReference *priorityZoneA() const; /** Returns a reference to the priority zone for VFO B. */ ZoneReference *priorityZoneB() const; /** Returns @c true if the GPS check is enabled. */ bool gpsCheckEnabled() const; /** Enables/disables the GPS check. */ void enableGPSCheck(bool enable); /** Returns @c true if the MCU is reset on boot. */ bool resetEnabled() const; /** Enables/disables MCU reset on boot. */ void enableReset(bool enable); protected: BootDisplay _bootDisplay; ///< The boot display property. bool _bootPasswordEnabled; ///< If true, the boot password is enabled. QString _bootPassword; ///< The boot password bool _defaultChannel; ///< Change to the default channel on boot. ZoneReference *_zoneA; ///< Default zone for VFO A. ChannelReference *_channelA; ///< Default channel for VFO A, must be member of zone for VFO A. ZoneReference *_zoneB; ///< Default zone for VFO B. ChannelReference *_channelB; ///< Default channel for VFO B, must be member of zone for VFO B. ZoneReference *_priorityZoneA; ///< Priority zone for VFO A. ZoneReference *_priorityZoneB; ///< Priority zone for VFO B. bool _gpsCheck; ///< Enables GPS check. bool _reset; ///< Enables MCU reset on boot. }; /** Implements the power-save settings for AnyTone devices. * This extension is part of the @c AnytoneSettingsExtension. * * @ingroup anytone */ class AnytonePowerSaveSettingsExtension: public ConfigItem { Q_OBJECT Q_CLASSINFO("autoShutDownDelayDescription", "The auto shut-down delay in minutes.") /** The auto shut-down delay in minutes. */ Q_PROPERTY(Interval autoShutdown READ autoShutdown WRITE setAutoShutdown) /** Resets the auto shut-down timer on every call. */ Q_PROPERTY(bool resetAutoShutdownOnCall READ resetAutoShutdownOnCall WRITE enableResetAutoShutdownOnCall) Q_CLASSINFO("powerSaveDescription", "Specifies the power save mode. " "D686UV, D878UV(2) and DMR-6X2UV only.") /** The power-save mode. */ Q_PROPERTY(PowerSave powerSave READ powerSave WRITE setPowerSave) /** If @c true, the adaptive transmission power control is enabled. */ Q_PROPERTY(bool atpc READ atpc WRITE enableATPC) public: /** Possible power save modes. */ enum class PowerSave { Off = 0, Save50 = 1, Save66 = 2 }; Q_ENUM(PowerSave) public: /** Default constructor. */ explicit AnytonePowerSaveSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the auto shut-down delay in minutes. */ Interval autoShutdown() const; /** Sets the auto shut-down delay. */ void setAutoShutdown(Interval min); /** Returns @c true, if the auto shut-down timer is reset on every call. */ bool resetAutoShutdownOnCall() const; /** Enables/disables reset of auto shut-down timer on every call. */ void enableResetAutoShutdownOnCall(bool enable); /** Returns the power-save mode. */ PowerSave powerSave() const; /** Sets the power-save mode. */ void setPowerSave(PowerSave mode); /** Returns @c true if the adaptive transmission power control is enabled. */ bool atpc() const; /** Enables/disables the adaptive transmission power control. */ void enableATPC(bool enable); protected: Interval _autoShutDownDelay; ///< The auto shut-down delay in minutes. bool _resetAutoShutdownOnCall; ///< Enables reset of auto shut-down timer on every call. PowerSave _powerSave; ///< Power save mode property. bool _atpc; ///< Adaptive Transmission Power Control. }; /** Implements the key settings extension of AnyTone devices. * This extension is part of the @c AnytoneSettingsExtension. * * @ingroup anytone */ class AnytoneKeySettingsExtension: public ConfigItem { Q_OBJECT /** Function key 1 (P1 below or next to screen), short press function. */ Q_PROPERTY(KeyFunction funcKey1Short READ funcKey1Short WRITE setFuncKey1Short) /** Function key 1 (P1 below or next to screen), long press function. */ Q_PROPERTY(KeyFunction funcKey1Long READ funcKey1Long WRITE setFuncKey1Long) /** Function key 2 (P2 below or next to screen), short press function. */ Q_PROPERTY(KeyFunction funcKey2Short READ funcKey2Short WRITE setFuncKey2Short) /** Function key 2 (P2 below or next to screen), long press function. */ Q_PROPERTY(KeyFunction funcKey2Long READ funcKey2Long WRITE setFuncKey2Long) /** Function key 3 (P3 next to screen, D578UV only), short press function. */ Q_PROPERTY(KeyFunction funcKey3Short READ funcKey3Short WRITE setFuncKey3Short) /** Function key 3 (P3 next to screen, D578UV only), long press function. */ Q_PROPERTY(KeyFunction funcKey3Long READ funcKey3Long WRITE setFuncKey3Long) /** Function key 4 (P4 next to screen, D578UV only), short press function. */ Q_PROPERTY(KeyFunction funcKey4Short READ funcKey4Short WRITE setFuncKey4Short) /** Function key 4 (P4 next to screen, D578UV only), long press function. */ Q_PROPERTY(KeyFunction funcKey4Long READ funcKey4Long WRITE setFuncKey4Long) /** Function key 5 (P5 next to screen, D578UV only), short press function. */ Q_PROPERTY(KeyFunction funcKey5Short READ funcKey5Short WRITE setFuncKey5Short) /** Function key 5 (P5 next to screen, D578UV only), long press function. */ Q_PROPERTY(KeyFunction funcKey5Long READ funcKey5Long WRITE setFuncKey5Long) /** Function key 6 (P6 next to screen, D578UV only), short press function. */ Q_PROPERTY(KeyFunction funcKey6Short READ funcKey6Short WRITE setFuncKey6Short) /** Function key 6 (P6 next to screen, D578UV only), long press function. */ Q_PROPERTY(KeyFunction funcKey6Long READ funcKey6Long WRITE setFuncKey6Long) /** Function key A (PF1 below PTT or A on mic), short press function. */ Q_PROPERTY(KeyFunction funcKeyAShort READ funcKeyAShort WRITE setFuncKeyAShort) /** Function key A (PF1 below PTT or A on mic), long press function. */ Q_PROPERTY(KeyFunction funcKeyALong READ funcKeyALong WRITE setFuncKeyALong) /** Function key B (PF2 second below PTT or B on mic), short press function. */ Q_PROPERTY(KeyFunction funcKeyBShort READ funcKeyBShort WRITE setFuncKeyBShort) /** Function key B (PF2 second below PTT or B on mic), long press function. */ Q_PROPERTY(KeyFunction funcKeyBLong READ funcKeyBLong WRITE setFuncKeyBLong) /** Function key C (PF3 on top or C on mic), short press function. */ Q_PROPERTY(KeyFunction funcKeyCShort READ funcKeyCShort WRITE setFuncKeyCShort) /** Function key C (PF3 on top or B on mic), long press function. */ Q_PROPERTY(KeyFunction funcKeyCLong READ funcKeyCLong WRITE setFuncKeyCLong) /** Function key D (D on mic, D578UV only), short press function. */ Q_PROPERTY(KeyFunction funcKeyDShort READ funcKeyDShort WRITE setFuncKeyDShort) /** Function key D (D on mic, D578UV only), long press function. */ Q_PROPERTY(KeyFunction funcKeyDLong READ funcKeyDLong WRITE setFuncKeyDLong) /** The long press duration in ms. */ Q_PROPERTY(Interval longPressDuration READ longPressDuration WRITE setLongPressDuration) /** The auto key-lock property. */ Q_PROPERTY(bool autoKeyLock READ autoKeyLockEnabled WRITE enableAutoKeyLock) Q_CLASSINFO("knobLockDescription", "If enabled, the knob gets locked too.") /** If @c true, the knob gets locked too. */ Q_PROPERTY(bool knobLock READ knobLockEnabled WRITE enableKnobLock) Q_CLASSINFO("keypadLockDescription", "If enabled, the key-pad gets locked.") /** If @c true, the key-pad gets locked too. */ Q_PROPERTY(bool keypadLock READ keypadLockEnabled WRITE enableKeypadLock) Q_CLASSINFO("sideKeysLockDescription", "If enabled, the side-keys get locked.") /** If @c true, the side-keys get locked too. */ Q_PROPERTY(bool sideKeysLock READ sideKeysLockEnabled WRITE enableSideKeysLock) Q_CLASSINFO("forcedKeyLockDescription", "If enabled, the key-lock is forced.") /** If @c true, the key-lock is forced. */ Q_PROPERTY(bool forcedKeyLock READ forcedKeyLockEnabled WRITE enableForcedKeyLock) public: /** All possible key functions. */ enum class KeyFunction { Off, Voltage, Power, Repeater, Reverse, Encryption, Call, VOX, ToggleVFO, SubPTT, Scan, WFM, Alarm, RecordSwitch, Record, SMS, Dial, GPSInformation, Monitor, ToggleMainChannel, HotKey1, HotKey2, HotKey3, HotKey4, HotKey5, HotKey6, WorkAlone, SkipChannel, DMRMonitor, SubChannel, PriorityZone, VFOScan, MICSoundQuality, LastCallReply, ChannelType, Ranging, Roaming, ChannelRanging, MaxVolume, Slot, APRSTypeSwitch, Zone, ZoneUp, ZoneDown, RoamingSet, APRSSet, Mute, MuteA, MuteB, CtcssDcsSet, TBSTSend, Bluetooth, GPS, ChannelName, CDTScan, APRSSend, APRSInfo, Speaker, XBandRepeater, SimplexRepeater, GPSRoaming, Squelch, NoiseReductionTX }; Q_ENUM(KeyFunction) public: /** Empty constructor. */ explicit AnytoneKeySettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the key function for a short press on the function key 1. */ KeyFunction funcKey1Short() const; /** Sets the key function for a short press on the function key 1. */ void setFuncKey1Short(KeyFunction func); /** Returns the key function for a long press on the function key 1. */ KeyFunction funcKey1Long() const; /** Sets the key function for a long press on the function key 1. */ void setFuncKey1Long(KeyFunction func); /** Returns the key function for a short press on the programmable function key 2. */ KeyFunction funcKey2Short() const; /** Sets the key function for a short press on the programmable function key 2. */ void setFuncKey2Short(KeyFunction func); /** Returns the key function for a long press on the programmable function key 2. */ KeyFunction funcKey2Long() const; /** Sets the key function for a long press on the programmable function key 2. */ void setFuncKey2Long(KeyFunction func); /** Returns the key function for a short press on the programmable function key 3. */ KeyFunction funcKey3Short() const; /** Sets the key function for a short press on the programmable function key 3. */ void setFuncKey3Short(KeyFunction func); /** Returns the key function for a long press on the programmable function key 3. */ KeyFunction funcKey3Long() const; /** Sets the key function for a long press on the programmable function key 3. */ void setFuncKey3Long(KeyFunction func); /** Returns the key function for a short press on the programmable function key 4. */ KeyFunction funcKey4Short() const; /** Sets the key function for a short press on the programmable function key 4. */ void setFuncKey4Short(KeyFunction func); /** Returns the key function for a long press on the programmable function key 4. */ KeyFunction funcKey4Long() const; /** Sets the key function for a long press on the programmable function key 4. */ void setFuncKey4Long(KeyFunction func); /** Returns the key function for a short press on the programmable function key 5. */ KeyFunction funcKey5Short() const; /** Sets the key function for a short press on the programmable function key 5. */ void setFuncKey5Short(KeyFunction func); /** Returns the key function for a long press on the programmable function key 5. */ KeyFunction funcKey5Long() const; /** Sets the key function for a long press on the programmable function key 5. */ void setFuncKey5Long(KeyFunction func); /** Returns the key function for a short press on the programmable function key 6. */ KeyFunction funcKey6Short() const; /** Sets the key function for a short press on the programmable function key 6. */ void setFuncKey6Short(KeyFunction func); /** Returns the key function for a long press on the programmable function key 6. */ KeyFunction funcKey6Long() const; /** Sets the key function for a long press on the programmable function key 6. */ void setFuncKey6Long(KeyFunction func); /** Returns the key function for a short press on the function key A. */ KeyFunction funcKeyAShort() const; /** Sets the key function for a short press on the function key A. */ void setFuncKeyAShort(KeyFunction func); /** Returns the key function for a long press on the function key A. */ KeyFunction funcKeyALong() const; /** Sets the key function for a long press on the function key A. */ void setFuncKeyALong(KeyFunction func); /** Returns the key function for a short press on the function key B. */ KeyFunction funcKeyBShort() const; /** Sets the key function for a short press on the function key B. */ void setFuncKeyBShort(KeyFunction func); /** Returns the key function for a long press on the function key B. */ KeyFunction funcKeyBLong() const; /** Sets the key function for a long press on the function key B. */ void setFuncKeyBLong(KeyFunction func); /** Returns the key function for a short press on the function key C. */ KeyFunction funcKeyCShort() const; /** Sets the key function for a short press on the function key C. */ void setFuncKeyCShort(KeyFunction func); /** Returns the key function for a long press on the function key C. */ KeyFunction funcKeyCLong() const; /** Sets the key function for a long press on the function key C. */ void setFuncKeyCLong(KeyFunction func); /** Returns the key function for a short press on the function key D. */ KeyFunction funcKeyDShort() const; /** Sets the key function for a short press on the function key D. */ void setFuncKeyDShort(KeyFunction func); /** Returns the key function for a long press on the function key D. */ KeyFunction funcKeyDLong() const; /** Sets the key function for a long press on the function key D. */ void setFuncKeyDLong(KeyFunction func); /** Returns the long-press duration in ms. */ Interval longPressDuration() const; /** Sets the long-press duration in ms. */ void setLongPressDuration(Interval ms); /** Returns @c true, if the automatic key-lock feature is enabled. */ bool autoKeyLockEnabled() const; /** Enables/disables auto key-lock. */ void enableAutoKeyLock(bool enabled); /** Returns @c true if the knob gets locked too. */ bool knobLockEnabled() const; /** Enables/disables the knob lock. */ void enableKnobLock(bool enable); /** Returns @c true if the key-pad gets locked too. */ bool keypadLockEnabled() const; /** Enables/disables the key-pad lock. */ void enableKeypadLock(bool enable); /** Returns @c true if the side-keys gets locked too. */ bool sideKeysLockEnabled() const; /** Enables/disables the side-keys lock. */ void enableSideKeysLock(bool enable); /** Returns @c true if the key-lock is forced. */ bool forcedKeyLockEnabled() const; /** Enables/disables the forced key-lock. */ void enableForcedKeyLock(bool enable); protected: KeyFunction _funcKey1Short; ///< Function of the function key 1, short press. KeyFunction _funcKey1Long; ///< Function of the function key 1, long press. KeyFunction _funcKey2Short; ///< Function of the function key 2, short press. KeyFunction _funcKey2Long; ///< Function of the function key 2, long press. KeyFunction _funcKey3Short; ///< Function of the function key 3, short press. KeyFunction _funcKey3Long; ///< Function of the function key 3, long press. KeyFunction _funcKey4Short; ///< Function of the function key 4, short press. KeyFunction _funcKey4Long; ///< Function of the function key 4, long press. KeyFunction _funcKey5Short; ///< Function of the function key 5, short press. KeyFunction _funcKey5Long; ///< Function of the function key 5, long press. KeyFunction _funcKey6Short; ///< Function of the function key 6, short press. KeyFunction _funcKey6Long; ///< Function of the function key 6, long press. KeyFunction _funcKeyAShort; ///< Function of the function key A, short press. KeyFunction _funcKeyALong; ///< Function of the function key A, long press. KeyFunction _funcKeyBShort; ///< Function of the function key B, short press. KeyFunction _funcKeyBLong; ///< Function of the function key B, long press. KeyFunction _funcKeyCShort; ///< Function of the function key C, short press. KeyFunction _funcKeyCLong; ///< Function of the function key C, long press. KeyFunction _funcKeyDShort; ///< Function of the function key D, short press. KeyFunction _funcKeyDLong; ///< Function of the function key D, long press. Interval _longPressDuration; ///< The long-press duration in ms. bool _autoKeyLock; ///< Auto key-lock property. bool _knobLock; ///< Knob locked too. bool _keypadLock; ///< Key-pad is locked. bool _sideKeysLock; ///< Side-keys are locked. bool _forcedKeyLock; ///< Forced key-lock. }; /** Implements the tone settings extension of AnyTone devices. * This extension is part of the @c AnytoneSettingsExtension. * * @ingroup anytone */ class AnytoneToneSettingsExtension: public ConfigItem { Q_OBJECT Q_CLASSINFO("description", "Tone settings for AnyTone devices.") Q_CLASSINFO("keyToneDescription", "If true, enables the key tones.") /** The key tone setting. */ Q_PROPERTY(bool keyTone READ keyToneEnabled WRITE enableKeyTone) Q_CLASSINFO("keyToneLevelDescription", "Specifies the key-tone level, 0=user adjustable.") /** The key tone level setting. */ Q_PROPERTY(unsigned int keyToneLevel READ keyToneLevel WRITE setKeyToneLevel) Q_CLASSINFO("smsAlertDescription", "Enables/disables the SMS alert tone.") /** If @c true, the SMS alert tone is enabled. */ Q_PROPERTY(bool smsAlert READ smsAlertEnabled WRITE enableSMSAlert) Q_CLASSINFO("callAlertDescription", "Enables/disables the call alert tone.") /** If @c true, the call alert tone is enabled. */ Q_PROPERTY(bool callAlert READ callAlertEnabled WRITE enableCallAlert) Q_CLASSINFO("dmrTalkPermitDescription", "Enables/disables the talk-permit tone for DMR channels.") /** If @c true, the DMR talk permit tone is enabled. */ Q_PROPERTY(bool dmrTalkPermit READ talkPermitDigitalEnabled WRITE enableTalkPermitDigital) Q_CLASSINFO("dmrResetDescription", "Enables/disables the reset tone for DMR channels.") /** If @c true, the DMR reset tone is enabled. */ Q_PROPERTY(bool dmrReset READ digitalResetToneEnabled WRITE enableDigitalResetTone) Q_CLASSINFO("fmTalkPermitDescription", "Enables/disables the talk-permit tone for FM channels.") /** If @c true, the FM talk permit tone is enabled. */ Q_PROPERTY(bool fmTalkPermit READ talkPermitAnalogEnabled WRITE enableTalkPermitAnalog) /** If @c true, the idle tone is enabled for DMR channels. */ Q_PROPERTY(bool dmrIdle READ dmrIdleChannelToneEnabled WRITE enableDMRIdleChannelTone) /** If @c true, the idle tone is enabled for FM channels. */ Q_PROPERTY(bool fmIdle READ fmIdleChannelToneEnabled WRITE enableFMIdleChannelTone) /** If @c true, the startup tone is enabled. */ Q_PROPERTY(bool startup READ startupToneEnabled WRITE enableStartupTone) /** Enables transmit timeout notification (5s before TOT). */ Q_PROPERTY(bool tot READ totNotification WRITE enableTOTNotification) /** The call melody. */ Q_PROPERTY(Melody * callMelody READ callMelody) /** The idle melody. */ Q_PROPERTY(Melody * idleMelody READ idleMelody) /** The reset melody. */ Q_PROPERTY(Melody * resetMelody READ resetMelody) /** The call-end melody. */ Q_PROPERTY(Melody * callEndMelody READ callEndMelody) public: /** Empty constructor. */ explicit AnytoneToneSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns @c true if the key tone is enabled. */ bool keyToneEnabled() const; /** Enables/disables the key tone. */ void enableKeyTone(bool enable); /** Returns @c true if SMS alert is enabled. */ bool smsAlertEnabled() const; /** Enables/disables SMS alert. */ void enableSMSAlert(bool enable); /** Returns @c true if call alert is enabled. */ bool callAlertEnabled() const; /** Enables/disables call alert. */ void enableCallAlert(bool enable); /** Returns @c true if the talk permit tone is enabled for digital channels. */ bool talkPermitDigitalEnabled() const; /** Enables/disables the talk permit tone for digital channels. */ void enableTalkPermitDigital(bool enable); /** Returns @c true if the talk permit tone is enabled for digital channels. */ bool talkPermitAnalogEnabled() const; /** Enables/disables the talk permit tone for analog channels. */ void enableTalkPermitAnalog(bool enable); /** Returns @c true if the reset tone is enabled for digital calls. */ bool digitalResetToneEnabled() const; /** Enables/disables the reset tone for digital calls. */ void enableDigitalResetTone(bool enable); /** Returns @c true if the idle channel tone is enabled for DMR channel. */ bool dmrIdleChannelToneEnabled() const; /** Enables/disables the idle DMR channel tone. */ void enableDMRIdleChannelTone(bool enable); /** Returns @c true if the idle channel tone is enabled for FM channel. */ bool fmIdleChannelToneEnabled() const; /** Enables/disables the idle FM channel tone. */ void enableFMIdleChannelTone(bool enable); /** Returns @c true if the startup tone is enabled. */ bool startupToneEnabled() const; /** Enables/disables the startup tone. */ void enableStartupTone(bool enable); /** Returns @c true if the transmit timeout notification is enabled (5s before TOT). */ bool totNotification() const; /** Enables/disables the transmit timeout notification (5s before TOT). */ void enableTOTNotification(bool enable); /** Returns a reference to the call melody. */ Melody *callMelody() const; /** Returns a reference to the idle melody. */ Melody *idleMelody() const; /** Returns a reference to the reset melody. */ Melody *resetMelody() const; /** Returns a reference to the call-end melody. */ Melody *callEndMelody() const; /** Returns the key-tone level. */ unsigned int keyToneLevel() const; /** Sets the key-tone level. */ void setKeyToneLevel(unsigned int level); protected: bool _keyTone; ///< Key tone property. bool _smsAlert; ///< SMS alert tone enabled. bool _callAlert; ///< Call alert tone enabled. bool _talkPermitDigital; ///< DMR talk permit tone. bool _talkPermitAnalog; ///< FM talk permit tone. bool _resetToneDigital; ///< DMR reset tone. bool _dmrIdleChannelTone; ///< Idle channel tone (DMR). bool _fmIdleChannelTone; ///< Idle channel tone (FM). bool _startupTone; ///< Startup tone enabled. bool _totNotification; ///< TOT notification enabled. Melody *_callMelody; ///< Call melody. Melody *_idleMelody; ///< Idle melody. Melody *_resetMelody; ///< Reset melody. Melody *_callEndMelody; ///< Call end melody. unsigned int _keyToneLevel; ///< The level of key-tones, 0=user adjustable. }; /** Implements the display settings extension of AnyTone devices. * This extension is part of the @c AnytoneSettingsExtension. * * @ingroup anytone */ class AnytoneDisplaySettingsExtension: public ConfigItem { Q_OBJECT /** The display frequency setting. */ Q_PROPERTY(bool displayFrequency READ displayFrequencyEnabled WRITE enableDisplayFrequency) /** The display brightness [1-10]. */ Q_PROPERTY(unsigned int brightness READ brightness WRITE setBrightness) /** The backlight duration in seconds. */ Q_PROPERTY(Interval backlightDuration READ backlightDuration WRITE setBacklightDuration) Q_CLASSINFO("backlightDurationTX", "The duration in seconds, the backlight is lit during TX. " "A value of 0 means off.") /** TX backlight duration. */ Q_PROPERTY(Interval backlightDurationTX READ backlightDurationTX WRITE setBacklightDurationTX) Q_CLASSINFO("backlightDurationRX", "The duration in seconds, the backlight is lit during RX. " "A value of 0 means always on.") /** RX backlight duration. */ Q_PROPERTY(Interval backlightDurationRX READ backlightDurationRX WRITE setBacklightDurationRX) /** Enables custom channel background. */ Q_PROPERTY(bool customChannelBackground READ customChannelBackground WRITE enableCustomChannelBackground) /** The volume-change prompt is shown. */ Q_PROPERTY(bool volumeChangePrompt READ volumeChangePromptEnabled WRITE enableVolumeChangePrompt) /** The call-end prompt is shown. */ Q_PROPERTY(bool callEndPrompt READ callEndPromptEnabled WRITE enableCallEndPrompt) /** If @c true, the clock is shown. */ Q_PROPERTY(bool showClock READ showClockEnabled WRITE enableShowClock) /** If @c true, the call is shown. */ Q_PROPERTY(bool showCall READ showCallEnabled WRITE enableShowCall) /** Shows the contact. */ Q_PROPERTY(bool showContact READ showContact WRITE enableShowContact) /** Shows the channel number. */ Q_PROPERTY(bool showChannelNumber READ showChannelNumberEnabled WRITE enableShowChannelNumber) /** Shows the color code. */ Q_PROPERTY(bool showColorCode READ showColorCode WRITE enableShowColorCode) /** Shows the time slot. */ Q_PROPERTY(bool showTimeSlot READ showTimeSlot WRITE enableShowTimeSlot) /** Shows the channel type. */ Q_PROPERTY(bool showChannelType READ showChannelType WRITE enableShowChannelType) /** Shows the last caller. */ Q_PROPERTY(bool showLastHeard READ showLastHeardEnabled WRITE enableShowLastHeard) /** The last-caller display mode. */ Q_PROPERTY(LastCallerDisplayMode lastCallerDisplay READ lastCallerDisplay WRITE setLastCallerDisplay) /** The color of the call. */ Q_PROPERTY(Color callColor READ callColor WRITE setCallColor) /** The standby text color. */ Q_PROPERTY(Color standbyTextColor READ standbyTextColor WRITE setStandbyTextColor) /** The standby background color. */ Q_PROPERTY(Color standbyBackgroundColor READ standbyBackgroundColor WRITE setStandbyBackgroundColor) Q_CLASSINFO("channelNameColorDescription", "Specifies the color of the channel name.") /** The channel name color. */ Q_PROPERTY(Color channelNameColor READ channelNameColor WRITE setChannelNameColor) Q_CLASSINFO("channelBNameColorDescription", "Specifies the color of the channel name for VFO B.") /** The channel name color for VFO B. */ Q_PROPERTY(Color channelBNameColor READ channelBNameColor WRITE setChannelBNameColor) Q_CLASSINFO("zoneNameColorDescription", "Specifies the color of the zone name.") /** The zone name color. */ Q_PROPERTY(Color zoneNameColor READ zoneNameColor WRITE setZoneNameColor) Q_CLASSINFO("zoneBNameColorDescription", "Specifies the color of the zone name for VFO B.") /** The zone name color for VFO B. */ Q_PROPERTY(Color zoneBNameColor READ zoneBNameColor WRITE setZoneBNameColor) /** Specifies the UI language. */ Q_PROPERTY(Language language READ language WRITE setLanguage) /** Specifies the date format. */ Q_PROPERTY(DateFormat dateFormat READ dateFormat WRITE setDateFormat) public: /** What to show from the last caller. */ enum class LastCallerDisplayMode { Off = 0, ID = 1, Call = 2, Both = 3 }; Q_ENUM(LastCallerDisplayMode) /** Possible display colors. */ enum class Color { White = 0, Black = 1, Orange=2, Red=3, Yellow=4, Green=5, Turquoise=6, Blue=7 }; Q_ENUM(Color) /** Possible UI languages. */ enum class Language { English = 0, ///< UI Language is english. German = 1 ///< UI Language is german. }; Q_ENUM(Language) /** Possible date formats. */ enum class DateFormat { YearFirst = 0, ///< yyyy/mm/dd DayFirst = 1 ///< dd/mm/yyyy }; Q_ENUM(DateFormat) public: /** Constructor. */ explicit AnytoneDisplaySettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns @c true, if the frequency is displayed instead of the channel name. */ bool displayFrequencyEnabled() const; /** Enables/disables display of frequency. */ void enableDisplayFrequency(bool enable); /** Returns the display brightness [1-10]. */ unsigned int brightness() const; /** Sets the display brightness [1-10]. */ void setBrightness(unsigned int level); /** Returns the backlight duration in seconds, 0 means permanent. */ Interval backlightDuration() const; /** Sets the backlight duration in seconds, 0 means permanent. */ void setBacklightDuration(Interval sec); /** Returns @c true if the volume-change prompt is shown. */ bool volumeChangePromptEnabled() const; /** Enables/disables the volume-change prompt. */ void enableVolumeChangePrompt(bool enable); /** Returns @c true if the call-end prompt is shown. */ bool callEndPromptEnabled() const; /** Enables/disables the call-end prompt. */ void enableCallEndPrompt(bool enable); /** Returns the last caller display mode. */ LastCallerDisplayMode lastCallerDisplay() const; /** Sets the last caller display mode. */ void setLastCallerDisplay(LastCallerDisplayMode mode); /** Returns @c true if the clock is shown. */ bool showClockEnabled() const; /** Enables/disables clock. */ void enableShowClock(bool enable); /** Returns @c true if the call is shown. */ bool showCallEnabled() const; /** Enables/disables display of call. */ void enableShowCall(bool enable); /** Returns the color of the call. */ Color callColor() const; /** Sets the color of the call. */ void setCallColor(Color color); /** Returns the UI language. */ Language language() const; /** Sets the UI language. */ void setLanguage(Language lang); /** Returns the date format */ DateFormat dateFormat() const; /** Sets the date format. */ void setDateFormat(DateFormat format); /** Returns @c true if the channel number is shown. */ bool showChannelNumberEnabled() const; /** Enables/disables the display of the channel number. */ void enableShowChannelNumber(bool enable); /** Returns @c true if the color code is shown. */ bool showColorCode() const; /** Shows/hides color code. */ void enableShowColorCode(bool enable); /** Returns @c true if the time slot is shown. */ bool showTimeSlot() const; /** Shows/hides time slot. */ void enableShowTimeSlot(bool enable); /** Returns @c true if the channel type is shown. */ bool showChannelType() const; /** Shows/hides channel type. */ void enableShowChannelType(bool enable); /** Returns @c true if the contact is shown. */ bool showContact() const; /** Enables/disables the display of calling contact. */ void enableShowContact(bool enable); /** Returns the standby text color. */ Color standbyTextColor() const; /** Sets the standby text color. */ void setStandbyTextColor(Color color); /** Returns the standby background color. */ Color standbyBackgroundColor() const; /** Sets the standby background color. */ void setStandbyBackgroundColor(Color color); /** Shows the last caller. */ bool showLastHeardEnabled() const; /** Enables/disables display of last caller. */ void enableShowLastHeard(bool enable); /** Returns backlight duration during TX. */ Interval backlightDurationTX() const; /** Sets the backlight duration during TX in seconds. */ void setBacklightDurationTX(Interval sec); /** Returns the color of the channel name. */ Color channelNameColor() const; /** Sets the color of the channel name. */ void setChannelNameColor(Color color); /** Returns the color of the channel name for VFO B. */ Color channelBNameColor() const; /** Sets the channel name color for VFO B. */ void setChannelBNameColor(Color color); /** Returns the color of the zone name. */ Color zoneNameColor() const; /** Sets the color of the zone name. */ void setZoneNameColor(Color color); /** Returns the color of the zone name for VFO B. */ Color zoneBNameColor() const; /** Sets the zone name color for VFO B. */ void setZoneBNameColor(Color color); /** Returns backlight duration during RX. */ Interval backlightDurationRX() const; /** Sets the backlight duration during RX in seconds. */ void setBacklightDurationRX(Interval sec); /** Returns @c true if the custom channel background is enabled. */ bool customChannelBackground() const; /** Enables/disables the custom channel background. */ void enableCustomChannelBackground(bool enable); protected: bool _displayFrequency; ///< Display frequency property. unsigned int _brightness; ///< The display brightness. Interval _backlightDuration; ///< Backlight duration in seconds, 0=permanent. bool _volumeChangePrompt; ///< Volume-change prompt enabled. bool _callEndPrompt; ///< Call-end prompt enabled. LastCallerDisplayMode _lastCallerDisplay; ///< Last-caller display mode. bool _showClock; ///< Display clock. bool _showCall; ///< Display call. Color _callColor; ///< Color of call. Language _language; ///< UI language. DateFormat _dateFormat; ///< The date format. bool _showChannelNumber; ///< Show channel number. bool _showColorCode; ///< Show color code. bool _showTimeSlot; ///< Show time slot. bool _showChannelType; ///< Show channel type. bool _showContact; ///< Enables showing the contact. Color _standbyTextColor; ///< Standby text color. Color _standbyBackgroundColor; ///< Standby background color. bool _showLastHeard; ///< Shows the last caller. Interval _backlightDurationTX; ///< Backlight duration in seconds during TX. Interval _backlightDurationRX; ///< Backlight duration in seconds during RX. bool _customChannelBackground; ///< Custom channel background enabled. Color _channelNameColor; ///< Color of channel name. Color _channelBNameColor; ///< Color of channel name for VFO B. Color _zoneNameColor; ///< Color of zone name. Color _zoneBNameColor; ///< Color of zone name for VFO B. }; /** Implements the audio settings extension of AnyTone devices. * This extension is part of the @c AnytoneSettingsExtension. * * @ingroup anytone */ class AnytoneAudioSettingsExtension: public ConfigItem { Q_OBJECT /** The VOX delay in ms. */ Q_PROPERTY(Interval voxDelay READ voxDelay WRITE setVOXDelay) /** The VOX source. */ Q_PROPERTY(VoxSource voxSource READ voxSource WRITE setVOXSource) /** If @c true, recording is enabled. */ Q_PROPERTY(bool recording READ recordingEnabled WRITE enableRecording) /** If @c true, the audio is "enhanced". */ Q_PROPERTY(bool enhance READ enhanceAudioEnabled WRITE enableEnhanceAudio) /** The mute delay in minutes. */ Q_PROPERTY(Interval muteDelay READ muteDelay WRITE setMuteDelay) /** The maximum volume setting [0-10]. */ Q_PROPERTY(unsigned int maxVolume READ maxVolume WRITE setMaxVolume) /** The maximum head-phone volume setting [0-10]. */ Q_PROPERTY(unsigned int maxHeadPhoneVolume READ maxHeadPhoneVolume WRITE setMaxHeadPhoneVolume) /** Enables the separate FM mic gain. If disabled, the DMR mic gain setting is used for FM too. */ Q_PROPERTY(bool enableFMMicGain READ fmMicGainEnabled WRITE enableFMMicGain) /** The FM mic gain [1,10]. */ Q_PROPERTY(unsigned int fmMicGain READ fmMicGain WRITE setFMMicGain) public: /** Source for the VOX. */ enum class VoxSource { Internal = 0, External = 1, Both = 2 }; Q_ENUM(VoxSource) public: /** Default constructor. */ explicit AnytoneAudioSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the VOX delay in ms. */ Interval voxDelay() const; /** Sets the VOX delay in ms. */ void setVOXDelay(Interval ms); /** Returns the VOX source. */ VoxSource voxSource() const; /** Sets the VOX source. */ void setVOXSource(VoxSource source); /** Returns @c true if recording is enabled. */ bool recordingEnabled() const; /** Enables/disables recording. */ void enableRecording(bool enable); /** Returns the maximum volume setting [0-10]. */ unsigned int maxVolume() const; /** Sets the maximum volume. */ void setMaxVolume(unsigned int vol); /** Returns the maximum head-phone volume setting [0-10]. */ unsigned int maxHeadPhoneVolume() const; /** Sets the maximum head-phone volume. */ void setMaxHeadPhoneVolume(unsigned int vol); /** Returns @c true if the audio is "enhanced". */ bool enhanceAudioEnabled() const; /** Enables/disables enhanced audio. */ void enableEnhanceAudio(bool enable); /** Returns the mute delay. */ Interval muteDelay() const; /** Sets the mute delay. */ void setMuteDelay(Interval intv); /** Returns @c true, if the FM mic gain is set independently. */ bool fmMicGainEnabled() const; /** Enables/disables the FM mic gain. */ void enableFMMicGain(bool enable); /** Returns the FM mic gain. */ unsigned int fmMicGain() const; /** Sets the FM mic gain. */ void setFMMicGain(unsigned int gain); protected: Interval _voxDelay; ///< VOX delay in ms. bool _recording; ///< Recording enabled. VoxSource _voxSource; ///< The VOX source. unsigned int _maxVolume; ///< The maximum volume. unsigned int _maxHeadPhoneVolume; ///< The maximum head-phone volume. bool _enhanceAudio; ///< Enhance audio. Interval _muteDelay; ///< Mute delay in minutes. bool _enableAnalogMicGain; ///< Enables separate analog mic gain. unsigned int _analogMicGain; ///< The FM mic gain. }; /** Implements the menu settings extension of AnyTone devices. * This extension is part of the @c AnytoneSettingsExtension. * * @ingroup anytone */ class AnytoneMenuSettingsExtension: public ConfigItem { Q_OBJECT Q_CLASSINFO("durationDescription", "The time in seconds, the menu is shown.") /** Menu exit time in seconds. */ Q_PROPERTY(Interval duration READ duration WRITE setDuration) Q_CLASSINFO("separatorDescription", "If enabled, the menu items are separated by a line.") /** Menu separator. */ Q_PROPERTY(bool separator READ separatorEnabled WRITE enableSeparator) public: /** Default constructor. */ explicit AnytoneMenuSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the menu duration in seconds. */ Interval duration() const; /** Sets the menu duration in seconds. */ void setDuration(Interval sec); /** Returns @c true, if the menu separator lines are shown. */ bool separatorEnabled() const; /** Enables/disables the menu separator lines. */ void enableSeparator(bool enable); protected: Interval _menuDuration; ///< Menu display duration in seconds. bool _showSeparator; ///< Show menu separator lines. }; /** Implements the config representation of a repeater offset. This is just a transmit * offset-frequency in Hz. * @ingroup anytone */ class AnytoneAutoRepeaterOffset: public ConfigObject { Q_OBJECT Q_CLASSINFO("IdPrefix", "off") Q_CLASSINFO("offsetDecription", "Transmit-frequency offset in Hz.") Q_CLASSINFO("offsetLongDecription", "The transmit-frequency offset is specified as a positive integer in Hz. The offset " "direction is specified for each VFO separately.") /** The offset frequency. */ Q_PROPERTY(Frequency offset READ offset WRITE setOffset) public: /** Default constructor. */ explicit Q_INVOKABLE AnytoneAutoRepeaterOffset(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the transmit frequency offset in Hz. */ Frequency offset() const; /** Sets the transmit frequency offset in Hz. */ void setOffset(Frequency offset); protected: /** The transmit frequency offset in Hz. */ Frequency _offset; }; /** Represents a reference to a repeater offset. * @ingroup anytone */ class AnytoneAutoRepeaterOffsetRef: public ConfigObjectReference { Q_OBJECT public: /** Default constructor. */ explicit AnytoneAutoRepeaterOffsetRef(QObject *parent=nullptr); }; /** Represents a list of auto-repeater offsets. * @ingroup anytone */ class AnytoneAutoRepeaterOffsetList: public ConfigObjectList { Q_OBJECT public: /** Empty constructor. */ explicit AnytoneAutoRepeaterOffsetList(QObject *parent=nullptr); ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err); }; /** Implements the auto-repeater settings extension of AnyTone devices. * This extension is part of the @c AnytoneSettingsExtension. * * @ingroup anytone */ class AnytoneAutoRepeaterSettingsExtension: public ConfigItem { Q_OBJECT Q_CLASSINFO("description", "Auto-repeater settings for AnyTone devices.") Q_CLASSINFO("directionADescription", "Auto-repeater transmit-frequency offset direction for VFO A.") /** Specifies the auto-repeater transmit-frequency offset direction for VFO A. */ Q_PROPERTY(Direction directionA READ directionA WRITE setDirectionA) Q_CLASSINFO("directionBDescription", "Auto-repeater transmit-frequency offset direction for VFO B.") /** Specifies the auto-repeater transmit-frequency offset direction for VFO B. */ Q_PROPERTY(Direction directionB READ directionB WRITE setDirectionB) Q_CLASSINFO("vhfMin", "The minimum frequency in Hz of the VHF auto-repeater frequency range.") /** The minimum frequency in Hz of the VHF auto-repeater frequency range. */ Q_PROPERTY(Frequency vhfMin READ vhfMin WRITE setVHFMin) Q_CLASSINFO("vhfMax", "The maximum frequency in Hz of the VHF auto-repeater frequency range.") /** The maximum frequency in Hz of the VHF auto-repeater frequency range. */ Q_PROPERTY(Frequency vhfMax READ vhfMax WRITE setVHFMax) Q_CLASSINFO("uhfMin", "The minimum frequency in Hz of the UHF auto-repeater frequency range.") /** The minimum frequency in Hz of the UHF auto-repeater frequency range. */ Q_PROPERTY(Frequency uhfMin READ uhfMin WRITE setUHFMin) Q_CLASSINFO("uhfMax", "The maximum frequency in Hz of the UHF auto-repeater frequency range.") /** The maximum frequency in Hz of the UHF auto-repeater frequency range. */ Q_PROPERTY(Frequency uhfMax READ uhfMax WRITE setUHFMax) Q_CLASSINFO("vhfDescription", "A reference to an offset frequency for the VHF band.") /** A reference to the auto-repeater frequency for VHF. */ Q_PROPERTY(AnytoneAutoRepeaterOffsetRef* vhf READ vhfRef) Q_CLASSINFO("uhfDescription", "A reference to an offset frequency for the UHF band.") /** A reference to the auto-repeater frequency for UHF. */ Q_PROPERTY(AnytoneAutoRepeaterOffsetRef* uhf READ uhfRef) Q_CLASSINFO("vhf2Min", "The minimum frequency in Hz of the second VHF auto-repeater frequency range.") /** The minimum frequency in Hz of the second VHF auto-repeater frequency range. */ Q_PROPERTY(Frequency vhf2Min READ vhf2Min WRITE setVHF2Min) Q_CLASSINFO("vhf2Max", "The maximum frequency in Hz of the second VHF auto-repeater frequency range.") /** The maximum frequency in Hz of the second VHF auto-repeater frequency range. */ Q_PROPERTY(Frequency vhf2Max READ vhf2Max WRITE setVHF2Max) Q_CLASSINFO("uhf2Min", "The minimum frequency in Hz of the second UHF auto-repeater frequency range.") /** The minimum frequency in Hz of the second UHF auto-repeater frequency range. */ Q_PROPERTY(Frequency uhf2Min READ uhf2Min WRITE setUHF2Min) Q_CLASSINFO("uhf2Max", "The maximum frequency in Hz of the second UHF auto-repeater frequency range.") /** The maximum frequency in Hz of the second UHF auto-repeater frequency range. */ Q_PROPERTY(Frequency uhf2Max READ uhf2Max WRITE setUHF2Max) Q_CLASSINFO("vhf2Description", "A reference to an offset frequency for the second VHF band.") /** A reference to the auto-repeater frequency for the second VHF band. */ Q_PROPERTY(AnytoneAutoRepeaterOffsetRef* vhf2 READ vhf2Ref) Q_CLASSINFO("uhf2Description", "A reference to an offset frequency for the second UHF band.") /** A reference to the auto-repeater frequency for the second UHF band. */ Q_PROPERTY(AnytoneAutoRepeaterOffsetRef* uhf2 READ uhf2Ref) Q_CLASSINFO("offsetDescription", "The lists of offset frequencies.") /** The repeater transmit offset frequencies. */ Q_PROPERTY(AnytoneAutoRepeaterOffsetList* offsets READ offsets) public: /** Encodes the auto-repeater offset sign. */ enum class Direction { Off = 0, ///< Disabled. Positive = 1, ///< Positive frequency offset. Negative = 2 ///< Negative frequency offset. }; Q_ENUM(Direction) public: /** Default constructor. */ explicit AnytoneAutoRepeaterSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** The auto-repeater offset direction for VFO A. */ Direction directionA() const; /** Set the auto-repeater offset direction for VFO A. */ void setDirectionA(Direction dir); /** The auto-repeater offset direction for VFO B. */ Direction directionB() const; /** Set the auto-repeater offset direction for VFO V. */ void setDirectionB(Direction dir); /** Returns the minimum frequency (in Hz) of the auto-repeater frequency range in the VHF band. */ Frequency vhfMin() const; /** Sets the minimum frequency (in Hz) of the auto-repeater frequency range in the VHF band. */ void setVHFMin(Frequency Hz); /** Returns the maximum frequency (in Hz) of the auto-repeater frequency range in the VHF band. */ Frequency vhfMax() const; /** Sets the maximum frequency (in Hz) of the auto-repeater frequency range in the VHF band. */ void setVHFMax(Frequency Hz); /** Returns the minimum frequency (in Hz) of the auto-repeater frequency range in the UHF band. */ Frequency uhfMin() const; /** Sets the minimum frequency (in Hz) of the auto-repeater frequency range in the UHF band. */ void setUHFMin(Frequency Hz); /** Returns the maximum frequency (in Hz) of the auto-repeater frequency range in the UHF band. */ Frequency uhfMax() const; /** Sets the maximum frequency (in Hz) of the auto-repeater frequency range in the UHF band. */ void setUHFMax(Frequency Hz); /** Returns the reference for the UHF offset freuqency. */ AnytoneAutoRepeaterOffsetRef *uhfRef() const; /** Returns the reference for the VHF offset freuqency. */ AnytoneAutoRepeaterOffsetRef *vhfRef() const; /** Returns the minimum frequency (in Hz) of the auto-repeater frequency range in the second VHF band. */ Frequency vhf2Min() const; /** Sets the minimum frequency (in Hz) of the auto-repeater frequency range in the second VHF band. */ void setVHF2Min(Frequency Hz); /** Returns the maximum frequency (in Hz) of the auto-repeater frequency range in the second VHF band. */ Frequency vhf2Max() const; /** Sets the maximum frequency (in Hz) of the auto-repeater frequency range in the second VHF band. */ void setVHF2Max(Frequency Hz); /** Returns the minimum frequency (in Hz) of the auto-repeater frequency range in the second UHF band. */ Frequency uhf2Min() const; /** Sets the minimum frequency (in Hz) of the auto-repeater frequency range in the second UHF band. */ void setUHF2Min(Frequency Hz); /** Returns the maximum frequency (in Hz) of the auto-repeater frequency range in the second UHF band. */ Frequency uhf2Max() const; /** Sets the maximum frequency (in Hz) of the auto-repeater frequency range in the second UHF band. */ void setUHF2Max(Frequency Hz); /** Returns the reference for the second UHF offset freuqency. */ AnytoneAutoRepeaterOffsetRef *uhf2Ref() const; /** Returns the reference for the second VHF offset freuqency. */ AnytoneAutoRepeaterOffsetRef *vhf2Ref() const; /** Returns a weak reference to the offset list. */ AnytoneAutoRepeaterOffsetList *offsets() const; protected: /** The auto-repeater direction for VFO A. */ Direction _directionA; /** The auto-repeater direction for VFO B. */ Direction _directionB; /** Minimum frequency of the VHF auto-repeater range. */ Frequency _minVHF; /** Maximum frequency of the VHF auto-repeater range. */ Frequency _maxVHF; /** Minimum frequency of the UHF auto-repeater range. */ Frequency _minUHF; /** Maximum frequency of the UHF auto-repeater range. */ Frequency _maxUHF; /** A reference to the VHF offset frequency. */ AnytoneAutoRepeaterOffsetRef *_vhfOffset; /** A reference to the UHF offset frequency. */ AnytoneAutoRepeaterOffsetRef *_uhfOffset; /** Minimum frequency of the second VHF auto-repeater range. */ Frequency _minVHF2; /** Maximum frequency of the second VHF auto-repeater range. */ Frequency _maxVHF2; /** Minimum frequency of the second UHF auto-repeater range. */ Frequency _minUHF2; /** Maximum frequency of the second UHF auto-repeater range. */ Frequency _maxUHF2; /** A reference to the second VHF offset frequency. */ AnytoneAutoRepeaterOffsetRef *_vhf2Offset; /** A reference to the second UHF offset frequency. */ AnytoneAutoRepeaterOffsetRef *_uhf2Offset; /** The list of repeater offsets. */ AnytoneAutoRepeaterOffsetList *_offsets; }; /** Implements the DMR settings extension of AnyTone devices. * This extension is part of the @c AnytoneSettingsExtension. * * @ingroup anytone */ class AnytoneDMRSettingsExtension: public ConfigItem { Q_OBJECT Q_CLASSINFO("groupCallHangTimeDescription", "Specifies the hang- or hold-time for group calls.") /** Group-call hang-time in seconds. */ Q_PROPERTY(Interval groupCallHangTime READ groupCallHangTime WRITE setGroupCallHangTime) /** Manual dialed group-call hang-time in seconds. */ Q_PROPERTY(Interval manualGroupCallHangTime READ manualGroupCallHangTime WRITE setManualGroupCallHangTime) Q_CLASSINFO("privateCallHangTimeDescription", "Specifies the hang- or hold-time for private calls.") /** Private-call hang-time in seconds. */ Q_PROPERTY(Interval privateCallHangTime READ privateCallHangTime WRITE setPrivateCallHangTime) /** Manual dialed private-call hang-time in seconds. */ Q_PROPERTY(Interval manualPrivateCallHangTime READ manualPrivateCallHangTime WRITE setManualPrivateCallHangTime) Q_CLASSINFO("preWaveDelay", "Sets the pre-wave delay in ms. Should be set to 100ms.") /** Pre-wave delay in ms. */ Q_PROPERTY(Interval preWaveDelay READ preWaveDelay WRITE setPreWaveDelay) Q_CLASSINFO("wakeHeadPeriod", "Sets the wake head-period in ms. Should be set to 100ms.") /** Wake head-period in ms. */ Q_PROPERTY(Interval wakeHeadPeriod READ wakeHeadPeriod WRITE setWakeHeadPeriod) Q_CLASSINFO("filterOwnIDDescription", "If enabled, own ID is not shown in call lists.") /** Filter own ID from call lists. */ Q_PROPERTY(bool filterOwnID READ filterOwnIDEnabled WRITE enableFilterOwnID) Q_CLASSINFO("monitorSlotMatchDescription", "Time-slot match-mode for DMR monitor.") /** Slot-match mode for DMR monitor. */ Q_PROPERTY(SlotMatch monitorSlotMatch READ monitorSlotMatch WRITE setMonitorSlotMatch) Q_CLASSINFO("monitorColorCodeMatchDescription", "If enabled, the DMR monitor will only open for " "matching color-codes.") /** Color-code match for DMR monitor. */ Q_PROPERTY(bool monitorColorCodeMatch READ monitorColorCodeMatchEnabled WRITE enableMonitorColorCodeMatch) Q_CLASSINFO("monitorIDMatchDescription", "If enabled, the DMR monitor will only open for matching IDs.") /** ID match for DMR monitor. */ Q_PROPERTY(bool monitorIDMatch READ monitorIDMatchEnabled WRITE enableMonitorIDMatch) Q_CLASSINFO("monitorTimeSlotHold", "Whether the DMR monitor holds the time-slot.") /** The DMR monitor holds the time-slot. */ Q_PROPERTY(bool monitorTimeSlotHold READ monitorTimeSlotHoldEnabled WRITE enableMonitorTimeSlotHold) Q_CLASSINFO("smsFormatDescription", "Specifies the SMS format, select Motorola here.") /** The SMS format. */ Q_PROPERTY(SMSFormat smsFormat READ smsFormat WRITE setSMSFormat) Q_CLASSINFO("sendTalkerAliasDescription", "Sends the radio name as talker alias over the air.") /** If @c true, the talker alias (name) is sent. */ Q_PROPERTY(bool sendTalkerAlias READ sendTalkerAlias WRITE enableSendTalkerAlias) /** Specifies the talker alias source. */ Q_PROPERTY(TalkerAliasSource talkerAliasSource READ talkerAliasSource WRITE setTalkerAliasSource) /** Specifies the talker alias encoding. */ Q_PROPERTY(TalkerAliasEncoding talkerAliasEncoding READ talkerAliasEncoding WRITE setTalkerAliasEncoding) /** The encryption type to be used. */ Q_PROPERTY(EncryptionType encryption READ encryption WRITE setEncryption) public: /** Possible monitor slot matches. */ enum class SlotMatch { Off = 0, Single = 1, Both = 2 }; Q_ENUM(SlotMatch) /** Possible SMS formats. */ enum class SMSFormat { Motorola = 0, Hytera = 1, DMR = 2 }; Q_ENUM(SMSFormat) /** Talker alias display preference. */ enum class TalkerAliasSource { Off = 0, UserDB = 1, Air = 2 }; Q_ENUM(TalkerAliasSource) /** Talker alias encoding. */ enum class TalkerAliasEncoding { ISO8 = 0, ISO7 = 1, Unicode = 2, }; Q_ENUM(TalkerAliasEncoding) /** Possible encryption types. */ enum class EncryptionType { AES=0, DMR=1 }; Q_ENUM(EncryptionType) public: /** Constructor. */ explicit AnytoneDMRSettingsExtension(QObject *parent = nullptr); ConfigItem *clone() const; /** Returns the group-call hang-time in seconds. */ Interval groupCallHangTime() const; /** Sets the group-call hang-time in seconds. */ void setGroupCallHangTime(Interval sec); /** Returns the manual dialed group-call hang-time in seconds. */ Interval manualGroupCallHangTime() const; /** Sets the manual dialed group-call hang-time in seconds. */ void setManualGroupCallHangTime(Interval sec); /** Returns the private-call hang-time in seconds. */ Interval privateCallHangTime() const; /** Sets the private-call hang-time in seconds. */ void setPrivateCallHangTime(Interval sec); /** Returns the manual dialed private-call hang-time in seconds. */ Interval manualPrivateCallHangTime() const; /** Sets the manual dialed private-call hang-time in seconds. */ void setManualPrivateCallHangTime(Interval sec); /** Returns the pre-wave delay in ms. */ Interval preWaveDelay() const; /** Sets the pre-wave delay in ms. */ void setPreWaveDelay(Interval ms); /** Returns the wake head-period in ms. */ Interval wakeHeadPeriod() const; /** Sets the wake head-period in ms. */ void setWakeHeadPeriod(Interval ms); /** If @c true, the own ID is not shown in call lists. */ bool filterOwnIDEnabled() const; /** Enables/disables filtering of own ID. */ void enableFilterOwnID(bool enable); /** Returns the slot-match mode for the DMR monitor. */ SlotMatch monitorSlotMatch() const; /** Sets the slot-match mode for the DMR monitor. */ void setMonitorSlotMatch(SlotMatch match); /** Returns @c true if the CC match is enabled for the DMR monitor. */ bool monitorColorCodeMatchEnabled() const; /** Enables/disables the CC match for the DMR monitor. */ void enableMonitorColorCodeMatch(bool enable); /** Returns @c true if the ID match is enabled for the DMR monitor. */ bool monitorIDMatchEnabled() const; /** Enables/disables ID match for the DMR monitor. */ void enableMonitorIDMatch(bool enable); /** Returns @c true if the time-slot is held by the DMR monitor. */ bool monitorTimeSlotHoldEnabled() const; /** Enables/disables the time-slot hold for the DMR monitor. */ void enableMonitorTimeSlotHold(bool enable); /** Returns the SMS format. */ SMSFormat smsFormat() const; /** Sets the SMS format. */ void setSMSFormat(SMSFormat format); /** Returns @c true if the talker alias is sent. */ bool sendTalkerAlias() const; /** Enables/disables sending talker alias. */ void enableSendTalkerAlias(bool enable); /** Returns the talker alias source. */ TalkerAliasSource talkerAliasSource() const; /** Sets the talker alias source. */ void setTalkerAliasSource(TalkerAliasSource mode); /** Returns the talker alias encoding. */ TalkerAliasEncoding talkerAliasEncoding() const ; /** Sets the talker alias encoding. */ void setTalkerAliasEncoding(TalkerAliasEncoding encoding); /** Returns the encryption type. */ EncryptionType encryption() const; /** Sets the encryption type. */ void setEncryption(EncryptionType type); protected: Interval _groupCallHangTime; ///< Hang-time for group-calls in seconds. Interval _manualGroupCallHangTime; ///< Hang-time for manual dialed group-calls in seconds. Interval _privateCallHangTime; ///< Hang-time for private-calls in seconds. Interval _manualPrivateCallHangTime; ///< Hang-time for manual dialed private-calls in seconds. Interval _preWaveDelay; ///< Pre-wave time in ms, should be 100ms. Interval _wakeHeadPeriod; ///< Wake head-period in ms, should be 100ms. bool _filterOwnID; ///< If enabled, the own ID is not shown in call lists. SlotMatch _monitorSlotMatch; ///< Slot-match mode for DMR monitor. bool _monitorColorCodeMatch; ///< Enables CC match for DMR monitor. bool _monitorIDMatch; ///< Enables ID match for DMR monitor. bool _monitorTimeSlotHold; ///< Enables the time-slot hold for the DMR monitor. SMSFormat _smsFormat; ///< Sets the SMS format. bool _sendTalkerAlias; ///< Enables sending talker alias. TalkerAliasSource _talkerAliasSource; ///< Source for the talker alias. TalkerAliasEncoding _talkerAliasEncoding; ///< Encoding for the talker alias. EncryptionType _encryption; ///< DMR encryption type. }; /** Implements the GPS settings extension of AnyTone devices. * This extension is part of the @c AnytoneSettingsExtension. * * @ingroup anytone */ class AnytoneGPSSettingsExtension: public ConfigItem { Q_OBJECT Q_CLASSINFO("unitsDescription", "Specifies the GPS units.") /** The GPS units used. */ Q_PROPERTY(Units units READ units WRITE setUnits) Q_CLASSINFO("timeZoneDescription", "Specifies the GPS time-zone (IANA name).") /** The time-zone IANA Id. */ Q_PROPERTY(QString timeZone READ ianaTimeZone WRITE setIANATimeZone) Q_CLASSINFO("positionReportingDescription", "Enables GPS range reporting.") /** Enables GPS range reporting. */ Q_PROPERTY(bool reportPosition READ positionReportingEnabled WRITE enablePositionReporting) Q_CLASSINFO("updatePeriodDescription", "Specifies the GPS reporting interval in seconds.") /** GPS ranging interval in seconds. */ Q_PROPERTY(Interval updatePeriod READ updatePeriod WRITE setUpdatePeriod) /** The GPS mode. */ Q_PROPERTY(GPSMode mode READ mode WRITE setMode) public: /** Possible unit systems. */ enum class Units { Metric = 0, Archaic = 1 }; Q_ENUM(Units) /** Possible GPS modes. */ enum class GPSMode { GPS=0, Beidou=1, GPS_Beidou=2, Glonass=3, GPS_Glonas=4, Beidou_Glonass=5, All=6 }; Q_ENUM(GPSMode) public: /** Constructor. */ explicit AnytoneGPSSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the GPS units used. */ Units units() const; /** Sets the GPS units. */ void setUnits(Units units); /** Returns the IANA ID of the time zone. */ QString ianaTimeZone() const; /** Returns the time-zone. */ QTimeZone timeZone() const; /** Sets the time zone by IANA ID. */ void setIANATimeZone(const QString &id); /** Sets the time zone. */ void setTimeZone(const QTimeZone &zone); /** Returns @c true if the GPS range reporting is enabled. */ bool positionReportingEnabled() const; /** Enables/disables the GPS range reporting. */ void enablePositionReporting(bool enable); /** Returns the GPS ranging interval in seconds. */ Interval updatePeriod() const; /** Sets the GPS ranging interval in seconds. */ void setUpdatePeriod(Interval sec); /** returns the GPS mode. */ GPSMode mode() const; /** Sets the GPS mode. */ void setMode(GPSMode mode); protected: Units _gpsUnits; ///< The GPS units. QTimeZone _timeZone; ///< The time zone. bool _gpsRangeReporting; ///< Enables GPS range reporting. Interval _gpsRangingInterval; ///< The GPS ranging interval in seconds. GPSMode _mode; ///< The GPS mode (GPS, Baidu, both). }; /** Implements the ranging/roaming settings extension of AnyTone devices. * This extension is part of the @c AnytoneSettingsExtension. * * @ingroup anytone */ class AnytoneRoamingSettingsExtension: public ConfigItem { Q_OBJECT Q_CLASSINFO("Description", "Collects all ranging/roaming settings for AnyTone devices.") /** Enables auto-roaming. */ Q_PROPERTY(bool autoRoam READ autoRoam WRITE enableAutoRoam) Q_CLASSINFO("autoRoamPeriodDescription", "Specifies the auto-roaming period in minutes.") /** The auto-roaming period in minutes. */ Q_PROPERTY(Interval autoRoamPeriod READ autoRoamPeriod WRITE setAutoRoamPeriod) Q_CLASSINFO("autoRoamDelayDescription", "A delay in seconds before starting the auto-roaming.") /** The auto-roam delay. */ Q_PROPERTY(Interval autoRoamDelay READ autoRoamDelay WRITE setAutoRoamDelay) Q_CLASSINFO("roamStart", "Start condition for auto-roaming.") /** Auto-roaming start condition. */ Q_PROPERTY(RoamStart roamStart READ roamingStartCondition WRITE setRoamingStartCondition) Q_CLASSINFO("roamReturn", "Condition to return to the original repeater.") /** Auto-roaming end/return condition. */ Q_PROPERTY(RoamStart roamReturn READ roamingReturnCondition WRITE setRoamingReturnCondition) Q_CLASSINFO("rangeCheckDescription", "Repeater range check.") /** Repeater range check. */ Q_PROPERTY(bool rangeCheck READ repeaterRangeCheckEnabled WRITE enableRepeaterRangeCheck) Q_CLASSINFO("checkIntervalDescription", "Repeater range check interval in seconds.") /** Repeater range check interval in seconds. */ Q_PROPERTY(Interval checkInterval READ repeaterCheckInterval WRITE setRepeaterCheckInterval) Q_CLASSINFO("retryCount", "Number of retries to connect to a repeater before giving up.") /** Retry count. */ Q_PROPERTY(unsigned int retryCount READ repeaterRangeCheckCount WRITE setRepeaterRangeCheckCount) /** Repeater out-of-range alert type. */ Q_PROPERTY(OutOfRangeAlert outOfRangeAlert READ outOfRangeAlert WRITE setOutOfRangeAlert) Q_CLASSINFO("notificationDescription", "Enables the repeater-check notification.") /** Repeater-check notification. */ Q_PROPERTY(bool notification READ notificationEnabled WRITE enableNotification) Q_CLASSINFO("notificationCountDescription", "The number of repeater-check notifications.") /** Repeater-check notification count. */ Q_PROPERTY(unsigned int notificationCount READ notificationCount WRITE setNotificationCount) /** The default roaming zone. */ Q_PROPERTY(RoamingZoneReference* defaultZone READ defaultZone) /** GPS roaming enabled. */ Q_PROPERTY(bool gpsRoaming READ gpsRoaming WRITE enableGPSRoaming) public: /** Possible roaming start conditions. */ enum class RoamStart { Periodic=0, OutOfRange=1 }; Q_ENUM(RoamStart) /** Possible repeater out-of-range alerts. */ enum class OutOfRangeAlert { None = 0x00, Bell = 0x01, Voice = 0x02 }; Q_ENUM(OutOfRangeAlert) public: /** Constructor. */ explicit AnytoneRoamingSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns @c true if auto-roaming is enabled. */ bool autoRoam() const; /** Enables/disables auto-roaming. */ void enableAutoRoam(bool enable); /** Returns the auto-roaming period in minutes. */ Interval autoRoamPeriod() const; /** Sets the auto-roam period in minutes. */ void setAutoRoamPeriod(Interval min); /** Returns the auto-roam delay in seconds. */ Interval autoRoamDelay() const; /** Sets the auto-roam delay in seconds. */ void setAutoRoamDelay(Interval sec); /** Returns @c true if the repeater range check is enabled. */ bool repeaterRangeCheckEnabled() const; /** Enables/disables repeater range check. */ void enableRepeaterRangeCheck(bool enable); /** Returns the repeater check interval in seconds. */ Interval repeaterCheckInterval() const; /** Sets the repeater check interval in seconds. */ void setRepeaterCheckInterval(Interval sec); /** Number of retries before givnig up. */ unsigned int repeaterRangeCheckCount() const; /** Sets the number of retries before giving up. */ void setRepeaterRangeCheckCount(unsigned int count); /** Returns the repeater out-of-range alert type. */ OutOfRangeAlert outOfRangeAlert() const; /** Sets the repeater out-of-range alert type. */ void setOutOfRangeAlert(OutOfRangeAlert type); /** Returns the auto-roaming start condition. */ RoamStart roamingStartCondition() const; /** Sets the auto-roaming start condition. */ void setRoamingStartCondition(RoamStart start); /** Returns the auto-roaming return condition. */ RoamStart roamingReturnCondition() const; /** Sets the auto-roaming return condition. */ void setRoamingReturnCondition(RoamStart start); /** Returns @c true, if the repeater check notification is enabled. */ bool notificationEnabled() const; /** Enables/disables the repeater-check notification. */ void enableNotification(bool enable); /** Returns the number of notifications. */ unsigned int notificationCount() const; /** Sets the number of repeater-check notifications. */ void setNotificationCount(unsigned int n); /** Returns @c true if GPS roaming is enabled. */ bool gpsRoaming() const; /** Enables/disables GPS roaming. */ void enableGPSRoaming(bool enable); /** Returns a reference to the default roaming zone. */ RoamingZoneReference *defaultZone() const; protected: bool _autoRoam; ///< Enables auto roaming. Interval _autoRoamPeriod; ///< The auto-roam period in minutes. Interval _autoRoamDelay; ///< The auto-roam delay in seconds. bool _repeaterRangeCheck; ///< Enables the repeater range-check. Interval _repeaterCheckInterval; ///< The repeater check interval in seconds. unsigned int _repeaterRangeCheckCount; ///< Number of range checks before giving up. OutOfRangeAlert _outOfRangeAlert; ///< Type of the out-out-range alert. RoamStart _roamingStartCondition; ///< Auto-roaming start condition. RoamStart _roamingReturnCondition; ///< Auto-roaming return condition. bool _notification; ///< Repeater check notification. unsigned int _notificationCount; ///< Number of notifications. bool _gpsRoaming; ///< Enables GPS roaming. RoamingZoneReference *_defaultRoamingZone; ///< The default roaming zone. }; /** Implements the bluetooth settings for some AnyTone devices. * This extension is part of the @c AnytoneSettingsExtension. * * @ingroup anytone */ class AnytoneBluetoothSettingsExtension: public ConfigItem { Q_OBJECT Q_CLASSINFO("Description", "Collects all bluetooth settings for AnyTone devices.") /** If @c true, the PTT latch is enabled. */ Q_PROPERTY(bool pttLatch READ pttLatch WRITE enablePTTLatch) /** The sleep timeout of the PTT button. If 0, sleep timer is disabled. */ Q_PROPERTY(Interval pttSleepTimer READ pttSleepTimer WRITE setPTTSleepTimer) public: /** Default constructor. */ explicit AnytoneBluetoothSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns @c true if the PTT latch is enabled. */ bool pttLatch() const; /** Enables/disables the PTT latch. */ void enablePTTLatch(bool enable); /** Returns the PTT sleep timeout, 0 means off. */ Interval pttSleepTimer() const; /** Sets the PTT sleep timeout, 0 means off. */ void setPTTSleepTimer(Interval intv); protected: bool _pttLatch; ///< PTT latch flag. Interval _pttSleep; ///< PTT sleep timer, 0 is off. }; /** Implements the simplex repeater settings for the BTECH DMR-6X2UV. * This extension is part of the @c AnytoneSettingsExtension * * @ingroup anytone */ class AnytoneSimplexRepeaterSettingsExtension: public ConfigItem { Q_OBJECT Q_CLASSINFO("enabledDescription", "If true, the simplex-repeater feature is enabled.") /** Enables/disables the simplex repeater. */ Q_PROPERTY(bool enabled READ enabled WRITE enable) Q_CLASSINFO("monitorDescription", "If true, the repeater-monitoring is enabled.") /** Enables/disables the repeater monitor. */ Q_PROPERTY(bool monitor READ monitorEnabled WRITE enableMonitor) Q_CLASSINFO("timeSlotDescription", "Specifies the time-slot of the repeater.") /** Time-slot of the repeater. */ Q_PROPERTY(TimeSlot timeSlot READ timeSlot WRITE setTimeSlot) public: /** Possible simplex repeater time-slots. */ enum class TimeSlot { TS1 = 0, TS2 = 1, Channel = 2 }; Q_ENUM(TimeSlot) public: /** Default constructor. */ explicit AnytoneSimplexRepeaterSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** If @c true, the simplex-repeater is enabled. */ bool enabled() const; /** Enables/disables the simplex repeater. */ void enable(bool enable); /** If @c true, repeater monitoring is enabled. */ bool monitorEnabled() const; /** Enables/disables repeater monitoring. */ void enableMonitor(bool enable); /** Returns the repeater time-slot. */ TimeSlot timeSlot() const; /** Sets the repeater time-slot. */ void setTimeSlot(TimeSlot ts); protected: bool _enabled; ///< If @c true, the simplex repeater is enabled. bool _monitor; ///< If enabled, the radio will monitor the channel. TimeSlot _timeSlot; ///< The repeater time-slot. }; /** Implements the device specific extension for the general settings of AnyTone devices. * * As there are a huge amount of different settings, they are split into separate extensions. * One for each topic. * * @ingroup anytone */ class AnytoneSettingsExtension: public ConfigExtension { Q_OBJECT Q_CLASSINFO("description", "Device specific settings for AnyTone devices.") Q_CLASSINFO("subChannelDescription", "Enables/disables the sub-channel.") /** If @c true, the sub-channel is enabled. */ Q_PROPERTY(bool subChannel READ subChannelEnabled WRITE enableSubChannel) Q_CLASSINFO("selectedVFODecription", "Specifies the currently selected VFO (A or B).") /** The current active VFO. */ Q_PROPERTY(VFO selectedVFO READ selectedVFO WRITE setSelectedVFO) Q_CLASSINFO("modeADescription", "Specifies the mode (memory or VFO) for VFO A.") Q_CLASSINFO("modeBDescription", "Specifies the mode (memory or VFO) for VFO B.") /** The mode of VFO A. */ Q_PROPERTY(VFOMode modeA READ modeA WRITE setModeA) /** The mode of VFO B. */ Q_PROPERTY(VFOMode modeB READ modeB WRITE setModeB) Q_CLASSINFO("zoneADescription", "Specifies the current zone for VFO A.") Q_CLASSINFO("zoneBDescription", "Specifies the current zone for VFO B.") /** The current zone for VFO A. */ Q_PROPERTY(ZoneReference* zoneA READ zoneA) /** The current zone for VFO B. */ Q_PROPERTY(ZoneReference* zoneB READ zoneB) /** The VFO scan type. */ Q_PROPERTY(VFOScanType vfoScanType READ vfoScanType WRITE setVFOScanType) /** The minimum UHF VFO-scan frequency in Hz. */ Q_PROPERTY(Frequency minVFOScanFrequencyUHF READ minVFOScanFrequencyUHF WRITE setMinVFOScanFrequencyUHF) /** The maximum UHF VFO-scan frequency in Hz. */ Q_PROPERTY(Frequency maxVFOScanFrequencyUHF READ maxVFOScanFrequencyUHF WRITE setMaxVFOScanFrequencyUHF) /** The minimum VHF VFO-scan frequency in Hz. */ Q_PROPERTY(Frequency minVFOScanFrequencyVHF READ minVFOScanFrequencyVHF WRITE setMinVFOScanFrequencyVHF) /** The maximum VHF VFO-scan frequency in Hz. */ Q_PROPERTY(Frequency maxVFOScanFrequencyVHF READ maxVFOScanFrequencyVHF WRITE setMaxVFOScanFrequencyVHF) Q_CLASSINFO("keepLastCallerDescription", "Keeps the last caller on channel switch") /** The keep-last-caller setting. */ Q_PROPERTY(bool keepLastCaller READ keepLastCallerEnabled WRITE enableKeepLastCaller) Q_CLASSINFO("vfoStepDescription", "Specifies the VFO tuning steps in kHz.") /** The VFO tuning step-size in kHz. */ Q_PROPERTY(Frequency vfoStep READ vfoStep WRITE setVFOStep) Q_CLASSINFO("steTypeDescription", "Specifies the STE (squelch tail elimination) type.") Q_CLASSINFO("steTypeLongDescription", "Can also be used to disable the STE entirely.") /** The STE type. */ Q_PROPERTY(STEType steType READ steType WRITE setSTEType) Q_CLASSINFO("steFrequencyDescription", "Specifies the STE (squelch tail elimination) frequency in Hz.") /** The STE frequency in Hz. */ Q_PROPERTY(double steFrequency READ steFrequency WRITE setSTEFrequency) Q_CLASSINFO("steDurationDescription", "Specifies the duration of the STE (squelch tail elimination) tone.") Q_CLASSINFO("steDurationLongDescription", "Valid values are usually in the range between 10 and 1000ms.") /** The STE duration in ms. */ Q_PROPERTY(Interval steDuration READ steDuration WRITE setSTEDuration) Q_CLASSINFO("tbstFrequencyDescription", "Specifies the TBST frequency in Hz.") Q_CLASSINFO("tbstFrequencyDescription", "Should be one of 1000, 1450, 1750 and 2100 Hz. " "D878UV(2), D578UV and DMR-6X2UV only.") /** The TBST frequency in Hz. */ Q_PROPERTY(Frequency tbstFrequency READ tbstFrequency WRITE setTBSTFrequency) Q_CLASSINFO("proModeDescription", "Enables 'professional' mode. This limits the option reachable via the menu.") /** If @c true, the "pro mode" is enabled. */ Q_PROPERTY(bool proMode READ proModeEnabled WRITE enableProMode) /** If @c true, the call-channel is maintained (whatever that means). */ Q_PROPERTY(bool maintainCallChannel READ maintainCallChannelEnabled WRITE enableMaintainCallChannel) /** The boot settings. */ Q_PROPERTY(AnytoneBootSettingsExtension* bootSettings READ bootSettings) /** The power-save settings. */ Q_PROPERTY(AnytonePowerSaveSettingsExtension* powerSaveSettings READ powerSaveSettings) /** The key settings. */ Q_PROPERTY(AnytoneKeySettingsExtension* keySettings READ keySettings) /** The tone settings. */ Q_PROPERTY(AnytoneToneSettingsExtension* toneSettings READ toneSettings) /** The display settings. */ Q_PROPERTY(AnytoneDisplaySettingsExtension* displaySettings READ displaySettings) /** The audio settings. */ Q_PROPERTY(AnytoneAudioSettingsExtension* audioSettings READ audioSettings) /** The menu settings. */ Q_PROPERTY(AnytoneMenuSettingsExtension* menuSettings READ menuSettings) /** The auto-repeater settings. */ Q_PROPERTY(AnytoneAutoRepeaterSettingsExtension* autoRepeaterSettings READ autoRepeaterSettings) /** The DMR settings. */ Q_PROPERTY(AnytoneDMRSettingsExtension* dmrSettings READ dmrSettings) /** The GPS settings. */ Q_PROPERTY(AnytoneGPSSettingsExtension* gpsSettings READ gpsSettings) /** The Roaming settings. */ Q_PROPERTY(AnytoneRoamingSettingsExtension* roamingSettings READ roamingSettings) /** The bluethooth settings. */ Q_PROPERTY(AnytoneBluetoothSettingsExtension* bluetoothSettings READ bluetoothSettings) Q_CLASSINFO("simplexRepeaterSettingsDescription", "Configuration for the DMR-6X2UV simplex-repeater feature.") /** The simplex-repeater settings. DMR-6X2UV only. */ Q_PROPERTY(AnytoneSimplexRepeaterSettingsExtension * simplexRepeaterSettings READ simplexRepeaterSettings) public: /** Encodes the possible VFO scan types. */ enum class VFOScanType { Time = 0, Carrier = 1, Stop = 2 }; Q_ENUM(VFOScanType) /** Possible VFO modes. */ enum class VFOMode { Memory = 0, VFO = 1 }; Q_ENUM(VFOMode) /** Possible VFOs. */ enum class VFO { A = 0, B = 1 }; Q_ENUM(VFO) /** All possible STE (squelch tail eliminate) types. */ enum class STEType { Off = 0, Silent = 1, Deg120 = 2, Deg180 = 3, Deg240 = 4 }; Q_ENUM(STEType) public: /** Constructor. */ Q_INVOKABLE explicit AnytoneSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** A reference to the boot settings. */ AnytoneBootSettingsExtension *bootSettings() const; /** A reference to the power-save settings. */ AnytonePowerSaveSettingsExtension *powerSaveSettings() const; /** A reference to the key settings. */ AnytoneKeySettingsExtension *keySettings() const; /** A reference to the tone settings. */ AnytoneToneSettingsExtension *toneSettings() const; /** A reference to the display settings. */ AnytoneDisplaySettingsExtension *displaySettings() const; /** A reference to the audio settings. */ AnytoneAudioSettingsExtension *audioSettings() const; /** A reference to the menu settings. */ AnytoneMenuSettingsExtension *menuSettings() const; /** A reference to the auto-repeater settings. */ AnytoneAutoRepeaterSettingsExtension *autoRepeaterSettings() const; /** A reference to the DMR settings. */ AnytoneDMRSettingsExtension *dmrSettings() const; /** A reference to the GPS settings. */ AnytoneGPSSettingsExtension *gpsSettings() const; /** A reference to the roaming settings. */ AnytoneRoamingSettingsExtension *roamingSettings() const; /** A reference to the bluetooth settings. */ AnytoneBluetoothSettingsExtension *bluetoothSettings() const; /** A reference to the simplex repeater settings. */ AnytoneSimplexRepeaterSettingsExtension *simplexRepeaterSettings() const; /** Returns the VFO scan type. */ VFOScanType vfoScanType() const; /** Sets the VFO scan type. */ void setVFOScanType(VFOScanType type); /** Returns mode for VFO A. */ VFOMode modeA() const; /** Sets the mode for VFO A. */ void setModeA(VFOMode mode); /** Returns mode for VFO B. */ VFOMode modeB() const; /** Sets the mode for VFO B. */ void setModeB(VFOMode mode); /** Returns a reference to the current zone for VFO A. */ ZoneReference *zoneA(); /** Returns a reference to the current zone for VFO A. */ const ZoneReference *zoneA() const; /** Returns a reference to the current zone for VFO B. */ ZoneReference *zoneB(); /** Returns a reference to the current zone for VFO B. */ const ZoneReference *zoneB() const; /** Returns the selected VFO. */ VFO selectedVFO() const; /** Sets the selected VFO. */ void setSelectedVFO(VFO vfo); /** Returns @c true if the sub-channel is enabled. */ bool subChannelEnabled() const; /** Enables/disables the sub-channel. */ void enableSubChannel(bool enable); /** Returns the minimum VFO scan frequency for the UHF band in Hz. */ Frequency minVFOScanFrequencyUHF() const; /** Sets the minimum VFO scan frequency for the UHF band in Hz. */ void setMinVFOScanFrequencyUHF(Frequency hz); /** Returns the maximum VFO scan frequency for the UHF band in Hz. */ Frequency maxVFOScanFrequencyUHF() const; /** Sets the maximum VFO scan frequency for the UHF band in Hz. */ void setMaxVFOScanFrequencyUHF(Frequency hz); /** Returns the minimum VFO scan frequency for the VHF band in Hz. */ Frequency minVFOScanFrequencyVHF() const; /** Sets the minimum VFO scan frequency for the VHF band in Hz. */ void setMinVFOScanFrequencyVHF(Frequency hz); /** Returns the maximum VFO scan frequency for the VHF band in Hz. */ Frequency maxVFOScanFrequencyVHF() const; /** Sets the maximum VFO scan frequency for the VHF band in Hz. */ void setMaxVFOScanFrequencyVHF(Frequency hz); /** Returns @c true if the last caller is kept on channel switch. */ bool keepLastCallerEnabled() const; /** Enables/disables keeping the last caller on channel switch. */ void enableKeepLastCaller(bool enable); /** Returns the VFO tuning step in kHz. */ Frequency vfoStep() const; /** Sets the VFO tuning step in kHz. */ void setVFOStep(Frequency step); /** Returns the STE (squelch tail elimination) type. */ STEType steType() const; /** Sets the STE (squelch tail elimination) type. */ void setSTEType(STEType type); /** Returns the STE (squelch tail elimination) frequency in Hz. * A frequency of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */ double steFrequency() const; /** Sets the STE (squelch tail elimination) frequency in Hz. * A frequency of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */ void setSTEFrequency(double freq); /** Returns the STE duration in ms. */ Interval steDuration() const; /** Sets the STE duration. */ void setSTEDuration(Interval intv); /** Returns the TBST frequency in Hz. */ Frequency tbstFrequency() const; /** Sets the TBST frequency in Hz. Should be one of 1000, 1450, 1750 and 2100 Hz. */ void setTBSTFrequency(Frequency Hz); /** Returns @c true, if the "pro mode" is enabled. */ bool proModeEnabled() const; /** Enables/disables the "pro mode". */ void enableProMode(bool enable); /** Returns @c true if the call-channel is maintained. */ bool maintainCallChannelEnabled() const; /** Enables/disables maintaining the call-channel. */ void enableMaintainCallChannel(bool enable); protected: /** The boot settings. */ AnytoneBootSettingsExtension *_bootSettings; /** The power-save settings. */ AnytonePowerSaveSettingsExtension *_powerSaveSettings; /** The key settings. */ AnytoneKeySettingsExtension *_keySettings; /** The tone settings. */ AnytoneToneSettingsExtension *_toneSettings; /** The display settings. */ AnytoneDisplaySettingsExtension *_displaySettings; /** The audio settings. */ AnytoneAudioSettingsExtension *_audioSettings; /** The menu settings. */ AnytoneMenuSettingsExtension *_menuSettings; /** The auto-repeater settings. */ AnytoneAutoRepeaterSettingsExtension *_autoRepeaterSettings; /** The DMR settings. */ AnytoneDMRSettingsExtension *_dmrSettings; /** The GSP settings. */ AnytoneGPSSettingsExtension *_gpsSettings; /** The roaming settings. */ AnytoneRoamingSettingsExtension *_roamingSettings; /** The bluetooth settings. */ AnytoneBluetoothSettingsExtension *_bluetoothSettings; /** The simplex-repeater settings. */ AnytoneSimplexRepeaterSettingsExtension *_simplexRepeaterSettings; VFOScanType _vfoScanType; ///< The VFO scan-type property. VFOMode _modeA; ///< Mode of VFO A. VFOMode _modeB; ///< Mode of VFO B. ZoneReference _zoneA; ///< The current zone for VFO A. ZoneReference _zoneB; ///< The current zone for VFO B. VFO _selectedVFO; ///< The current VFO. bool _subChannel; ///< If @c true, the sub-channel is enabled. Frequency _minVFOScanFrequencyUHF; ///< The minimum UHF VFO-scan frequency in Hz. Frequency _maxVFOScanFrequencyUHF; ///< The maximum UHF VFO-scan frequency in Hz. Frequency _minVFOScanFrequencyVHF; ///< The minimum VHF VFO-scan frequency in Hz. Frequency _maxVFOScanFrequencyVHF; ///< The maximum VHF VFO-scan frequency in Hz. bool _keepLastCaller; ///< If @c true, the last caller is kept on channel switch. Frequency _vfoStep; ///< The VFO tuning step in kHz. STEType _steType; ///< The STE type. double _steFrequency; ///< STE frequency in Hz. Interval _steDuration; ///< STE duration Frequency _tbstFrequency; ///< The TBST frequency in Hz. bool _proMode; ///< The "pro mode" flag. bool _maintainCallChannel; ///< Maintains the call channel. }; /** Implements some additional settings for the FM APRS system. * This extension gets attached to a @c APRSSystem instance. */ class AnytoneFMAPRSSettingsExtension: public ConfigExtension { Q_OBJECT /** The transmit delay in milliseconds. */ Q_PROPERTY(Interval txDelay READ txDelay WRITE setTXDelay) /** The transmit pre-wave delay in milliseconds. */ Q_PROPERTY(Interval preWaveDelay READ preWaveDelay WRITE setPreWaveDelay) /** If @c true, all APRS messages are processed, including those with invalid CRC. */ Q_PROPERTY(bool passAll READ passAll WRITE enablePassAll) /** If @c true, the report position flag is set. */ Q_PROPERTY(bool reportPosition READ reportPosition WRITE enableReportPosition) /** If @c true, the report Mic-E flag is set. */ Q_PROPERTY(bool reportMicE READ reportMicE WRITE enableReportMicE) /** If @c true, the report object flag is set. */ Q_PROPERTY(bool reportObject READ reportObject WRITE enableReportObject) /** If @c true, the report item flag is set. */ Q_PROPERTY(bool reportItem READ reportItem WRITE enableReportItem) /** If @c true, the report message flag is set. */ Q_PROPERTY(bool reportMessage READ reportMessage WRITE enableReportMessage) /** If @c true, the report weather flag is set. */ Q_PROPERTY(bool reportWeather READ reportWeather WRITE enableReportWeather) /** If @c true, the report NMEA flag is set. */ Q_PROPERTY(bool reportNMEA READ reportNMEA WRITE enableReportNMEA) /** If @c true, the report status flag is set. */ Q_PROPERTY(bool reportStatus READ reportStatus WRITE enableReportStatus) /** If @c true, the report other flag is set. */ Q_PROPERTY(bool reportOther READ reportOther WRITE enableReportOther) /** The list of additional APRS frequencies. */ Q_PROPERTY(AnytoneAPRSFrequencyList *frequencies READ frequencies) public: /** Possible bandwidth settings. */ enum class Bandwidth { Narrow = 0, Wide = 1 }; Q_ENUM(Bandwidth) public: /** Default constructor. */ explicit Q_INVOKABLE AnytoneFMAPRSSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the transmit delay. */ Interval txDelay() const; /** Sets the transmit delay. */ void setTXDelay(Interval intv); /** Returns the pre-wave delay in ms. */ Interval preWaveDelay() const; /** Sets the pre-wave delay in ms. */ void setPreWaveDelay(Interval ms); /** Returns @c true if all received APRS messages are processed, even those with invalid CRC. */ bool passAll() const; /** Enables processing of all received APRS messages, including those with invalid CRC. */ void enablePassAll(bool enable); /** Returns @c true if the report position flag is set. */ bool reportPosition() const; /** Enables/disables report position flag. */ void enableReportPosition(bool enable); /** Returns @c true if the report Mic-E flag is set. */ bool reportMicE() const; /** Enables/disables report Mic-E flag. */ void enableReportMicE(bool enable); /** Returns @c true if the report object flag is set. */ bool reportObject() const; /** Enables/disables report object flag. */ void enableReportObject(bool enable); /** Returns @c true if the report item flag is set. */ bool reportItem() const; /** Enables/disables report item flag. */ void enableReportItem(bool enable); /** Returns @c true if the report message flag is set. */ bool reportMessage() const; /** Enables/disables report message flag. */ void enableReportMessage(bool enable); /** Returns @c true if the report weather flag is set. */ bool reportWeather() const; /** Enables/disables report weather flag. */ void enableReportWeather(bool enable); /** Returns @c true if the report NMEA flag is set. */ bool reportNMEA() const; /** Enables/disables report NMEA flag. */ void enableReportNMEA(bool enable); /** Returns @c true if the report status flag is set. */ bool reportStatus() const; /** Enables/disables report status flag. */ void enableReportStatus(bool enable); /** Returns @c true if the report other flag is set. */ bool reportOther() const; /** Enables/disables report other flag. */ void enableReportOther(bool enable); /** Returns the list of additional FM APRS frequencies. */ AnytoneAPRSFrequencyList *frequencies() const; protected: /** The transmit delay. */ Interval _txDelay; /** The pre-wave delay. */ Interval _preWaveDelay; /** If @c true, all APRS messages are processed. */ bool _passAll; /** If @c true the report position flag is set. */ bool _reportPosition; /** The report Mic-E flag. */ bool _reportMicE; /** The report object flag. */ bool _reportObject; /** The report item flag. */ bool _reportItem; /** The report message flag. */ bool _reportMessage; /** The report weather flag. */ bool _reportWeather; /** The report NMEA flag. */ bool _reportNMEA; /** The report status flag. */ bool _reportStatus; /** The report other flag. */ bool _reportOther; /** The list of additional FM APRS frequencies. */ AnytoneAPRSFrequencyList *_frequencies; }; #endif // ANYTONEEXTENSION_HH qdmr-0.12.3/lib/anytone_filereader.cc000066400000000000000000000106261501654372000174640ustar00rootroot00000000000000#include "anytone_filereader.hh" #include #include #include #include "d868uv_filereader.hh" #include "d878uv_filereader.hh" /** Internal used struct to read a CPS file header. */ struct __attribute__((packed)) file_header { char version[5]; ///< The version number. uint32_t payload_size; ///< The content size. char modelname[7]; ///< The model name. uint8_t _unused0010[3]; ///< Unused/reserved. char hw_version[4]; ///< The hardware version. }; /* ********************************************************************************************* * * Implementation of AnytoneFileReader::Element * ********************************************************************************************* */ AnytoneFileReader::Element::Element(const uint8_t *ptr) : _data(ptr) { // pass... } AnytoneFileReader::Element::~Element() { // pass... } /* ********************************************************************************************* * * Implementation of AnytoneFileReader * ********************************************************************************************* */ AnytoneFileReader::AnytoneFileReader(Config *config, const uint8_t *data, size_t size, QString &message) : _context(config), _start(data), _data(data), _size(size), _message(message) { // pass... } AnytoneFileReader::~AnytoneFileReader() { // pass... } bool AnytoneFileReader::read() { // Set pointer to start _data = _start; if (! this->readHeader()) return false; if (! this->readChannels()) return false; if (! this->readRadioIDs()) return false; if (! this->readZones()) return false; if (! this->readScanLists()) return false; if (! this->readAnalogContacts()) return false; // 2nd pass, link. _data = _start; if (! this->linkHeader()) return false; if (! this->linkChannels()) return false; if (! this->linkRadioIDs()) return false; if (! this->linkZones()) return false; if (! this->linkScanLists()) return false; if (! this->linkAnalogContacts()) return false; return true; } bool AnytoneFileReader::read(const QString &filename, Config *config, QString &message) { QFileInfo info(filename); if (! info.exists()) { message = QObject::tr("Cannot open file '%1': File does not exist.").arg(filename); return false; } QFile file(filename); if (! file.open(QFile::ReadOnly)) { message = QObject::tr("Cannot open file '%1': %2.").arg(filename).arg(file.errorString()); return false; } file_header head; if (sizeof(file_header) != file.read((char *)&head, sizeof(file_header))) { message = QObject::tr("Cannot read header from file '%1': %2.") .arg(filename).arg(file.errorString()); file.close(); return false; } size_t size = qFromLittleEndian(head.payload_size)+14; if (size != (size_t)info.size()) { message = QObject::tr("Malformed header in file '%1': Mismatching content size. Expected %2, got %3.") .arg(filename).arg(info.size()-14).arg(size-14); file.close(); return false; } uint8_t *data = file.map(0, size); if (nullptr == data) { message = QObject::tr("Cannot mmap file '%1': %2.") .arg(filename).arg(file.errorString()); file.close(); return false; } QString cps_version = QString::fromLocal8Bit(head.version, strnlen(head.version,5)); QString model = QString::fromLocal8Bit(head.modelname, strnlen(head.modelname,7)); // Dispatch by model name AnytoneFileReader *reader = nullptr; if ("D868UVE" == model) { reader = new D868UVFileReader(config, data, size, message); } else if ("D878UV" == model) { reader = new D878UVFileReader(config, data, size, message); } else if ("D878UV2" == model) { } else if ("D578UV" == model) { } else { message = QObject::tr("Cannot read codeplug file '%1': Unknown model '%2'.") .arg(filename).arg(model); file.unmap(data); file.close(); return false; } if (nullptr == reader) { message = QObject::tr("Cannot read codeplug file '%1': Model '%2' not implemented yet.") .arg(filename).arg(model); file.unmap(data); file.close(); return false; } // Clear config config->reset(); if (! reader->read()) { message = QObject::tr("Cannot read codeplug file '%1': %2").arg(filename).arg(message); file.unmap(data); file.close(); return false; } file.unmap(data); file.close(); return true; } qdmr-0.12.3/lib/anytone_filereader.hh000066400000000000000000000041471501654372000174770ustar00rootroot00000000000000#ifndef ANYTONEFILEREADER_HH #define ANYTONEFILEREADER_HH #include #include "config.hh" /** This class implements a reader of AnyTone codeplug files. * @warning This is mostly incomplete. * @ingroup anytone */ class AnytoneFileReader { public: /** Base class for all elements in the codeplug file. */ class Element { protected: /** Hidden constructor. */ Element(const uint8_t *ptr); public: /** Destructor. */ virtual ~Element(); /** Returns the storage size of the element. */ virtual size_t size() const = 0; protected: /** Pointer to the actual element. */ const uint8_t *_data; }; protected: /** Constructs a configuration from the given codeplug file in memory (@c data, @c size). */ AnytoneFileReader(Config *config, const uint8_t *data, size_t size, QString &message); public: /** Destructor. */ virtual ~AnytoneFileReader(); protected: /** Read all elements. */ virtual bool read(); /** Read header of file. */ virtual bool readHeader() = 0; /** Link elements. */ virtual bool linkHeader() = 0; /** Read all channels. */ virtual bool readChannels() = 0; /** Link elements. */ virtual bool linkChannels() = 0; /** Read all radio IDs. */ virtual bool readRadioIDs() = 0; /** Link elements. */ virtual bool linkRadioIDs() = 0; /** Read all zones. */ virtual bool readZones() = 0; /** Link elements. */ virtual bool linkZones() = 0; /** Read all scal lists. */ virtual bool readScanLists() = 0; /** Link elements. */ virtual bool linkScanLists() = 0; /** Read all DTMF contacts. */ virtual bool readAnalogContacts() = 0; /** Link elements. */ virtual bool linkAnalogContacts() = 0; public: /** Use this static function to read a codeplug from the manufacturer CPS file. */ static bool read(const QString &filename, Config *config, QString &message); protected: /** Pointer to the start. */ const uint8_t * const _start; /** Pointer to the entire data. */ const uint8_t *_data; /** Size of the entire blob. */ size_t _size; /** Error message. */ QString &_message; }; #endif // ANYTONEFILEREADER_HH qdmr-0.12.3/lib/anytone_interface.cc000066400000000000000000000261211501654372000173170ustar00rootroot00000000000000#include "anytone_interface.hh" #include "logger.hh" #include #define USB_VID 0x28e9 #define USB_PID 0x018a /* ********************************************************************************************* * * Implementation of AnytoneInterface::ReadRequest * ********************************************************************************************* */ AnytoneInterface::ReadRequest::ReadRequest(uint32_t addr) { cmd = 'R'; this->addr = qToBigEndian(addr); size = 16; } /* ********************************************************************************************* * * Implementation of AnytoneInterface::ReadResponse * ********************************************************************************************* */ bool AnytoneInterface::ReadResponse::check(uint32_t addr, QString &msg) const { if ('W' != cmd) { msg = QObject::tr("Invalid read response: Expected command 'W' got '%1'").arg(cmd); return false; } if (qFromBigEndian(this->addr) != addr) { msg = QObject::tr("Invalid read response: Expected address '%1' got '%2'") .arg(addr, 8, 16, QChar('0')).arg(qFromLittleEndian(this->addr), 8, 16, QChar('0')); return false; } if (16 != size) { msg = QObject::tr("Invalid read response: Expected size 64 got %1").arg((int)size); return false; } // Compute checksum uint8_t crc=((const uint8_t *)this)[1]; for (uint8_t i=2; i<(size+6); i++) crc += ((const uint8_t *)this)[i]; // compare if (crc != sum) { msg = QObject::tr("Invalid read response: Expected check-sum %1 got %2") .arg((int)crc).arg((int)sum); return false; } // check ACK flag. if (6 != ack) { msg = QObject::tr("Invalid read response: Expected ACK 6 got %1").arg((int)ack); return false; } // ok return true; } /* ********************************************************************************************* * * Implementation of AnytoneInterface::WriteRequest * ********************************************************************************************* */ AnytoneInterface::WriteRequest::WriteRequest(uint32_t addr, const char *data) { cmd = 'W'; this->addr = qToBigEndian(addr); size = 16; memcpy(this->data, data, size); sum = 0; uint8_t *b=(uint8_t *)this; for (uint8_t i=1; i<(size+6); i++) sum += b[i]; ack = 6; } /* ********************************************************************************************* * * Implementation of AnytoneInterface::RadioInfo * ********************************************************************************************* */ AnytoneInterface::RadioVariant::RadioVariant() : name(""), bands(0x00), version("") { // pass... } bool AnytoneInterface::RadioVariant::isValid() const { return ! name.isEmpty(); } /* ********************************************************************************************* * * Implementation of AnytoneInterface * ********************************************************************************************* */ AnytoneInterface::AnytoneInterface(const USBDeviceDescriptor &descriptor, const ErrorStack &err, QObject *parent) : USBSerial(descriptor, QSerialPort::Baud115200, err, parent), _state(STATE_INITIALIZED), _info() { if (isOpen()) { _state = STATE_OPEN; } else { _state = STATE_ERROR; return; } // enter program mode if (! this->enter_program_mode()) return; // identify device if (! this->request_identifier(_info)) { _info = RadioVariant(); _state = STATE_ERROR; } } AnytoneInterface::~AnytoneInterface() { if (isOpen()) AnytoneInterface::close(); } USBDeviceInfo AnytoneInterface::interfaceInfo() { return USBDeviceInfo(USBDeviceInfo::Class::Serial, USB_VID, USB_PID); } QList AnytoneInterface::detect(bool saveOnly) { Q_UNUSED(saveOnly); return USBSerial::detect(USB_VID, USB_PID, true); } void AnytoneInterface::close() { switch (_state) { case STATE_INITIALIZED: case STATE_OPEN: USBSerial::close(); break; case STATE_PROGRAM: this->reboot(); break; case STATE_CLOSED: case STATE_ERROR: break; } } RadioInfo AnytoneInterface::identifier(const ErrorStack &err) { if (! _info.isValid()) return RadioInfo(); if ("D868UVE" == _info.name) { return RadioInfo::byID(RadioInfo::D868UVE); } else if ("D6X2UV" == _info.name) { return RadioInfo::byID(RadioInfo::DMR6X2UV); } else if ("D878UV" == _info.name) { return RadioInfo::byID(RadioInfo::D878UV); } else if ("D878UV2" == _info.name) { return RadioInfo::byID(RadioInfo::D878UVII); } else if ("D578UV" == _info.name) { return RadioInfo::byID(RadioInfo::D578UV); } errMsg(err) << tr("Unsupported AnyTone radio '%1', HW rev. '%2'.") .arg(_info.name).arg(_info.version); return RadioInfo(); } bool AnytoneInterface::getInfo(RadioVariant &info) { if (_info.isValid()) { info = _info; return true; } return false; } bool AnytoneInterface::write_start(uint32_t bank, uint32_t addr, const ErrorStack &err) { Q_UNUSED(bank); Q_UNUSED(addr) if ((STATE_PROGRAM != _state) && (! enter_program_mode(err))) return false; return true; } bool AnytoneInterface::write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) { if (0 != bank) { errMsg(err) << "Anytone: Cannot write to bank " << bank << ". There is only one (idx=0)."; return false; } if (STATE_PROGRAM != _state) { errMsg(err) << "Anytone: Cannot write data to device: Not in programming mode."; return false; } //logDebug() << "Anytone: Write " << nbytes << "b to addr 0x" << QString::number(addr, 16) << "..."; for (int i=0; iclose(); _state = STATE_CLOSED; } return true; } bool AnytoneInterface::enter_program_mode(const ErrorStack &err) { if (STATE_PROGRAM == _state) { logDebug() << "Already in program mode. Skip."; return true; } else if (STATE_OPEN != _state) { errMsg(err) << "Anytone: Cannot enter program mode. Device is in state " << _state << "."; return false; } char ack[3]; // send "enter program mode" command if (! send_receive("PROGRAM", 7, ack, 3, err)) { errMsg(err) << "Anytone: Cannot enter program mode."; return false; } // check response if (0 != memcmp(ack, "QX\6", 3)) { errMsg(err) << "Anytone: Cannot enter program mode: Unexpected response. " << "Expected 515806 got " << QString::number(ack[0], 16) << QString::number(ack[1], 16) << QString::number(ack[1], 16) << "."; close(); _state = STATE_ERROR; return false; } logDebug() << "Anytone: In program-mode now."; _state = STATE_PROGRAM; return true; } bool AnytoneInterface::request_identifier(RadioVariant &info, const ErrorStack &err) { if (STATE_PROGRAM != _state) { errMsg(err) << "Anytone: Cannot request identifier. Device not in program mode, is in state " << _state << "."; return false; } RadioInfoResponse resp; // send "identify" command (in program mode) if (! send_receive("\2", 1, (char *)&resp, sizeof(RadioInfoResponse))) { errMsg(err) << "Anytone: Cannot request identifier."; return false; } // check response if (('I'!=resp.prefix) || (0x06 != resp.eot)) { errMsg(err) << "Anytone: Cannot request identifier: Unexpected response."; close(); _state = STATE_ERROR; return false; } info.name = QString::fromLocal8Bit(resp.model, strnlen(resp.model, sizeof(resp.model))).simplified(); info.bands = resp.bands; info.version = QString::fromLocal8Bit(resp.version, strnlen(resp.version, sizeof(resp.version))).simplified(); logDebug() << "Found radio '" << info.name << "', version '" << info.version << "'."; return true; } bool AnytoneInterface::leave_program_mode(const ErrorStack &err) { if (STATE_OPEN == _state) { logDebug() << "Device in open mode -> no need to leave program mode."; return true; } else if (STATE_PROGRAM != _state) { errMsg(err) << "Anytone: Cannot leave program mode. Device in state " << _state << "."; return false; } char ack[1]; if (! send_receive("END", 3, ack, 1)) { errMsg(err) << "Anytone: Cannot leave program mode."; return false; } logDebug() << "Anytone: Left program-mode."; _state = STATE_OPEN; return true; } bool AnytoneInterface::send_receive(const char *cmd, int clen, char *resp, int rlen, const ErrorStack &err) { // Try to write command to device if (clen != QSerialPort::write(cmd, clen)) { errMsg(err) << "Cannot send command to device."; close(); _state = STATE_ERROR; return false; } // Read from device until complete response has been read char *p = resp; int len = rlen; while (len > 0) { if (! waitForReadyRead(1000)) { errMsg(err) << "No response from device: Timeout."; close(); _state = STATE_ERROR; return false; } int r = QSerialPort::read(p, len); if (r < 0) { errMsg(err) << "Cannot read response from device."; close(); _state = STATE_ERROR; return false; } p += r; len-=r; } // done return true; } qdmr-0.12.3/lib/anytone_interface.hh000066400000000000000000000127711501654372000173370ustar00rootroot00000000000000#ifndef ANYTONEINTERFACE_HH #define ANYTONEINTERFACE_HH #include "usbserial.hh" /** Implements the interface to Anytone D868UV, D878UV, etc radios. * * This interface uses a USB serial-port to communicate with the device. To find the corresponding * port, the device-specific VID @c 0x28e9 and PID @c 0x018a are used. Hence no udev rules are * needed to access these devices. The user, however, should be a member of the @c dialout group * to get access to the serial interfaces. * * @ingroup anytone */ class AnytoneInterface : public USBSerial { Q_OBJECT public: /** Collects information about the particular radio being accessed. */ struct RadioVariant { /** The name of the radio. */ QString name; /** A code for which bands are supported. */ char bands; /** The (firmware/hardware) version. */ QString version; /** Empty constructor. */ RadioVariant(); /** Returns @c true if the radio info is valid. */ bool isValid() const; }; public: /** Constructs a new interface to Anytone radios. If a matching device was found, @c isOpen * returns @c true. */ explicit AnytoneInterface(const USBDeviceDescriptor &descriptor, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr); /** Destructor. */ virtual ~AnytoneInterface(); /** Closes the interface to the device. */ void close(); /** Returns an identifier of the radio. */ RadioInfo identifier(const ErrorStack &err=ErrorStack()); /** Reads the radio info from the device and returns it. * The information is only read once. */ bool getInfo(RadioVariant &info); bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()); bool read_finish(const ErrorStack &err=ErrorStack()); bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()); bool write_finish(const ErrorStack &err=ErrorStack()); bool reboot(const ErrorStack &err=ErrorStack()); public: /** Returns some information about this interface. */ static USBDeviceInfo interfaceInfo(); /** Tries to find all interfaces connected AnyTone radios. */ static QList detect(bool saveOnly=true); protected: /** Send command message to radio to ender program state. */ bool enter_program_mode(const ErrorStack &err=ErrorStack()); /** Sends a request to radio to identify itself. */ bool request_identifier(RadioVariant &info, const ErrorStack &err=ErrorStack()); /** Sends a command message to radio to leave program state and reboot. */ bool leave_program_mode(const ErrorStack &err=ErrorStack()); /** Internal used method to send messages to and receive responses from radio. */ bool send_receive(const char *cmd, int clen, char *resp, int rlen, const ErrorStack &err=ErrorStack()); protected: /** Binary representation of a read request to the radio. */ struct __attribute__((packed)) ReadRequest { char cmd; ///< Fixed to 'R'. uint32_t addr; ///< Memory address in little-endian. uint8_t size; ///< Fixed to 16. /// Constructs a read request for the specified address. ReadRequest(uint32_t addr); }; /** Binary representation of a read response from the radio. */ struct __attribute__((packed)) ReadResponse { char cmd; ///< Fixed to 'W'. uint32_t addr; ///< Memory address in big-endian. uint8_t size; ///< Fixed to 16. char data[16]; ///< The actual data. uint8_t sum; ///< Sum over address, size and data. uint8_t ack; ///< Fixed to 0x06. /** Check the response, returns @c true if read request was successful. * @param addr The read address to verify. * @param msg On error, contains a message describing the issue. */ bool check(uint32_t addr, QString &msg) const; }; /** Binary representation of a write request to the radio. */ struct __attribute__((packed)) WriteRequest { char cmd; ///< Fixed to 'W' uint32_t addr; ///< Memory address in big-endian. uint8_t size; ///< Fixed to 16 char data[16]; ///< The actual data. uint8_t sum; ///< Sum over addr, size and data. uint8_t ack; ///< Fixed to 0x06; /** Assembles a write request message to the given address with the given data. * @param addr Specifies the address to write to. * @param data 16 bytes of payload. */ WriteRequest(uint32_t addr, const char *data); }; /** Structure of radio information response. */ struct __attribute__((packed)) RadioInfoResponse { char prefix; ///< Fixed prefix. Set to 'I' on success. char model[7]; ///< Model name. uint8_t bands; ///< Frequency bands supported by radio. char version[6]; ///< Version number. Either hardware or 'radio' version. uint8_t eot; ///< Fixed 0x06 on success. }; /** Possible states of the radio interface. */ enum State { STATE_INITIALIZED, ///< Initial state. STATE_OPEN, ///< Interface to radio is open. STATE_PROGRAM, ///< Radio is in program mode. STATE_CLOSED, ///< Interface to radio is closed (captive final state). STATE_ERROR ///< An error occurred (captive final state), /// use @c errorMessage() to get an error message. }; /** Holds the state of the interface. */ State _state; /** Holds the radio info. */ RadioVariant _info; }; #endif // ANYTONEINTERFACE_HH qdmr-0.12.3/lib/anytone_limits.cc000066400000000000000000000022721501654372000166610ustar00rootroot00000000000000#include "anytone_limits.hh" AnytoneLimits::AnytoneLimits(const QString &hardwareRevision, const QString &supportedRevision, bool betaWarning, QObject *parent) : RadioLimits(betaWarning, parent), _hardwareRevision(hardwareRevision), _supportedRevision(supportedRevision) { // pass... } bool AnytoneLimits::verifyConfig(const Config *config, RadioLimitContext &context) const { bool success = RadioLimits::verifyConfig(config, context); if (_hardwareRevision.isEmpty()) return success; if (_supportedRevision < _hardwareRevision) { auto &msg = context.newMessage(RadioLimitIssue::Warning); msg = tr("You are likely using a newer radio reversion (%1) than supported (%2) by qdmr. " "The codeplug might be incompatible. " "Notify the developers of qdmr about the new reversion.").arg(_hardwareRevision, _supportedRevision); } else if (_supportedRevision > _hardwareRevision) { auto &msg = context.newMessage(RadioLimitIssue::Warning); msg = tr("You are likely using an older hardware reversion (%1) than supported (%2) by qdmr. " "The codeplug might be incompatible.").arg(_hardwareRevision, _supportedRevision); } return success; } qdmr-0.12.3/lib/anytone_limits.hh000066400000000000000000000021311501654372000166650ustar00rootroot00000000000000#ifndef ANYTONELIMITS_HH #define ANYTONELIMITS_HH #include "radiolimits.hh" /** Base class of limtis for all AnyTone radios. * This class extends @c RadioLimits and implements the hardware revision check. * @ingroup anytone */ class AnytoneLimits: public RadioLimits { Q_OBJECT protected: /** Constructor. * @param hardwareRevision Specifies the hardware revision as reported by the radio. If empty, * no check is perfored. * @param supportedRevision Specifies the supported hardware revision. * @param betaWarning If @c true, a warning is issued that the radio is still under development. * @param parent Specifies the QObject parent. */ AnytoneLimits(const QString &hardwareRevision, const QString &supportedRevision, bool betaWarning, QObject *parent=nullptr); public: bool verifyConfig(const Config *config, RadioLimitContext &context) const; protected: /** Holds the hardware revision of the radio. */ QString _hardwareRevision; /** Holds the supported hardware revision of the radio. */ QString _supportedRevision; }; #endif // ANYTONELIMITS_HH qdmr-0.12.3/lib/anytone_radio.cc000066400000000000000000000240001501654372000164470ustar00rootroot00000000000000#include "anytone_radio.hh" #include "d868uv.hh" #include "config.hh" #include "logger.hh" #include "configcopyvisitor.hh" #define RBSIZE 16 #define WBSIZE 16 AnytoneRadio::AnytoneRadio(const QString &name, AnytoneInterface *device, QObject *parent) : Radio(parent), _name(name), _dev(device), _codeplugFlags(), _config(nullptr), _codeplug(nullptr), _callsigns(nullptr), _satellites(nullptr) { // Check if device is open if ((nullptr==_dev) || (! _dev->isOpen())) { _task = StatusError; return; } } AnytoneRadio::~AnytoneRadio() { if (_dev && _dev->isOpen()) { _dev->reboot(); _dev->close(); } if (_dev) { _dev->deleteLater(); _dev = nullptr; } } const QString & AnytoneRadio::name() const { return _name; } const Codeplug & AnytoneRadio::codeplug() const { return *_codeplug; } Codeplug & AnytoneRadio::codeplug() { return *_codeplug; } bool AnytoneRadio::startDownload(bool blocking, const ErrorStack &err) { if (StatusIdle != _task) return false; _task = StatusDownload; _errorStack = err; if (blocking) { run(); return (StatusIdle == _task); } // If non-blocking -> move device to this thread if (_dev && _dev->isOpen()) _dev->moveToThread(this); start(); return true; } bool AnytoneRadio::startUpload(Config *config, bool blocking, const Codeplug::Flags &flags, const ErrorStack &err) { if (StatusIdle != _task) return false; if (_config) delete _config; // Cannot upload null-pointer if (nullptr == (_config = config)) return false; _task = StatusUpload; _codeplugFlags = flags; _errorStack = err; if (blocking) { run(); return (StatusIdle == _task); } // If non-blocking -> move device to this thread if (_dev && _dev->isOpen()) _dev->moveToThread(this); // also, move config to thread _config->moveToThread(this); start(); return true; } bool AnytoneRadio::startUploadCallsignDB(UserDatabase *db, bool blocking, const CallsignDB::Selection &selection, const ErrorStack &err) { _callsigns->encode(db, selection); _task = StatusUploadCallsigns; _errorStack = err; if (blocking) { run(); return (StatusIdle == _task); } // If non-blocking -> move device to this thread if (_dev && _dev->isOpen()) _dev->moveToThread(this); start(); return true; } bool AnytoneRadio::startUploadSatelliteConfig(SatelliteDatabase *db, bool blocking, const ErrorStack &err) { if (! _satellites->encode(db, err)) { errMsg(err) << "Cannot encode satellite config for AnyTone device."; return false; } _task = StatusUploadSatellites; _errorStack = err; if (blocking) { run(); return (StatusIdle == _task); } // If non-blocking -> move device to this thread if (_dev && _dev->isOpen()) _dev->moveToThread(this); start(); return true; } void AnytoneRadio::run() { if (StatusDownload == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { _task = StatusError; emit downloadError(this); return; } emit downloadStarted(); if (! download()) { _dev->reboot(); _dev->close(); _task = StatusError; emit downloadError(this); return; } _dev->close(); _task = StatusIdle; emit downloadFinished(this, _codeplug); _config = nullptr; } else if (StatusUpload == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { _task = StatusError; emit uploadError(this); return; } emit uploadStarted(); if (! upload()) { _dev->reboot(); _dev->close(); _task = StatusError; emit uploadError(this); return; } _dev->reboot(); _dev->close(); _task = StatusIdle; emit uploadComplete(this); } else if (StatusUploadCallsigns == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { _task = StatusError; emit uploadError(this); return; } emit uploadStarted(); if (! uploadCallsigns()) { _dev->reboot(); _dev->close(); _task = StatusError; emit uploadError(this); return; } _dev->reboot(); _dev->close(); _task = StatusIdle; emit uploadComplete(this); } else if (StatusUploadSatellites == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { _task = StatusError; emit uploadError(this); return; } emit uploadStarted(); if (! uploadSatellites()) { _dev->reboot(); _dev->close(); _task = StatusError; emit uploadError(this); return; } _dev->reboot(); _dev->close(); _task = StatusIdle; emit uploadComplete(this); } } bool AnytoneRadio::download() { if (nullptr == _codeplug) { errMsg(_errorStack) << "Cannot download codeplug: Object not created yet."; return false; } logDebug() << "Download of " << _codeplug->image(0).numElements() << " bitmaps."; // Download bitmaps for (int n=0; n<_codeplug->image(0).numElements(); n++) { unsigned addr = _codeplug->image(0).element(n).address(); unsigned size = _codeplug->image(0).element(n).data().size(); if (! _dev->read(0, addr, _codeplug->data(addr), size, _errorStack)) { errMsg(_errorStack) << "Cannot download codeplug."; return false; } emit downloadProgress(float(n*100)/_codeplug->image(0).numElements()); } // Allocate remaining memory sections unsigned nstart = _codeplug->image(0).numElements(); _codeplug->allocateForDecoding(); // Check every segment in the remaining codeplug for (int n=nstart; n<_codeplug->image(0).numElements(); n++) { if (! _codeplug->image(0).element(n).isAligned(RBSIZE)) { errMsg(_errorStack) << "Cannot download codeplug: Codeplug element " << n << " (addr=" << _codeplug->image(0).element(n).address() << ", size=" << _codeplug->image(0).element(n).data().size() << ") is not aligned with blocksize " << RBSIZE << "."; return false; } } // Download remaining memory sections for (int n=nstart; n<_codeplug->image(0).numElements(); n++) { unsigned addr = _codeplug->image(0).element(n).address(); unsigned size = _codeplug->image(0).element(n).data().size(); if (! _dev->read(0, addr, _codeplug->data(addr), size, _errorStack)) { errMsg(_errorStack) << "Cannot download codeplug."; return false; } emit downloadProgress(float(n*100)/_codeplug->image(0).numElements()); } return true; } bool AnytoneRadio::upload() { if (nullptr == _codeplug) { errMsg(_errorStack) << "Cannot write codeplug: Object not created yet."; return false; } // Download bitmaps first size_t nbitmaps = _codeplug->numImages(); for (int n=0; n<_codeplug->image(0).numElements(); n++) { unsigned addr = _codeplug->image(0).element(n).address(); unsigned size = _codeplug->image(0).element(n).data().size(); if (! _dev->read(0, addr, _codeplug->data(addr), size, _errorStack)) { errMsg(_errorStack) << "Cannot read codeplug for update."; return false; } emit uploadProgress(float(n*25)/_codeplug->image(0).numElements()); } // Allocate all memory sections that must be read first // and written back to the device more or less untouched _codeplug->allocateUpdated(); // Download new memory sections for update for (int n=nbitmaps; n<_codeplug->image(0).numElements(); n++) { unsigned addr = _codeplug->image(0).element(n).address(); unsigned size = _codeplug->image(0).element(n).data().size(); if (! _dev->read(0, addr, _codeplug->data(addr), size, _errorStack)) { errMsg(_errorStack) << "Cannot read codeplug for update."; return false; } emit uploadProgress(25+float(n*25)/_codeplug->image(0).numElements()); } // Update binary codeplug from config if (! _codeplug->encode(_config, _codeplugFlags, _errorStack)) { errMsg(_errorStack) << "Cannot encode codeplug."; return false; } // Sort all elements before uploading _codeplug->image(0).sort(); // Upload all elements back to the device for (int n=0; n<_codeplug->image(0).numElements(); n++) { unsigned addr = _codeplug->image(0).element(n).address(); unsigned size = _codeplug->image(0).element(n).data().size(); if (! _dev->write(0, addr, _codeplug->data(addr), size, _errorStack)) { errMsg(_errorStack) << "Cannot write codeplug."; return false; } emit uploadProgress(50+float(n*50)/_codeplug->image(0).numElements()); } return true; } bool AnytoneRadio::uploadCallsigns() { // Sort all elements before uploading _callsigns->image(0).sort(); size_t totalBlocks = _callsigns->memSize()/WBSIZE; size_t blkWritten = 0; // Upload all elements back to the device for (int n=0; n<_callsigns->image(0).numElements(); n++) { unsigned addr = _callsigns->image(0).element(n).address(); unsigned size = _callsigns->image(0).element(n).data().size(); unsigned nblks = size/WBSIZE; for (unsigned i=0; iwrite(0, addr+i*WBSIZE, _callsigns->data(addr)+i*WBSIZE, WBSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot write callsign db."; _task = StatusError; return false; } blkWritten++; emit uploadProgress(float(blkWritten*100)/totalBlocks); } } return true; } bool AnytoneRadio::uploadSatellites() { // Sort all elements before uploading _satellites->image(0).sort(); size_t totalBlocks = _satellites->memSize()/WBSIZE; size_t blkWritten = 0; // Upload all elements back to the device for (int n=0; n<_satellites->image(0).numElements(); n++) { unsigned addr = _satellites->image(0).element(n).address(); unsigned size = _satellites->image(0).element(n).data().size(); unsigned nblks = size/WBSIZE; for (unsigned i=0; iwrite(0, addr+i*WBSIZE, _satellites->data(addr)+i*WBSIZE, WBSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot write satellite config."; _task = StatusError; return false; } blkWritten++; emit uploadProgress(float(blkWritten*100)/totalBlocks); } } return true; } qdmr-0.12.3/lib/anytone_radio.hh000066400000000000000000000100251501654372000164630ustar00rootroot00000000000000/** @defgroup anytone Anytone Radios * Base classes for all anytone radios. * * Anytone radios share a lot of common formats and interfaces. * Consequently there are base-classes for radio interfaces (@c AnytoneInterface), codeplugs * (@c AnytoneCodeplug) and radios (@c AnytoneRadio). This helps to keep the burden of implementing * the support for each radio at a minimum. * * @ingroup dsc */ #ifndef __ANYTONE_RADIO_HH__ #define __ANYTONE_RADIO_HH__ #include "radio.hh" #include "anytone_interface.hh" #include "anytone_codeplug.hh" #include "anytone_satelliteconfig.hh" /** Implements an interface to Anytone radios. * * The transfer of the codeplug to the device is performed in 4 steps. * * First only the bitmaps of all lists are downloaded from the device. Then all elements that are * not touched or only updated by the common codeplug config are downloaded. Then, the common * config gets applied to the binary codeplug. That is, all channels, contacts, zones, group-lists * and scan-lists are generated and their bitmaps gets updated accordingly. Also the general config * gets updated from the common codeplug settings. Finally, the resulting binary codeplug gets * written back to the device. * * This rather complex method of writing a codeplug to the device is needed to maintain all * settings within the radio that are not defined within the common codeplug config while keeping * the amount of data being read from and written to the device small. * * @ingroup anytone */ class AnytoneRadio: public Radio { Q_OBJECT protected: /** Do not construct this class directly. */ explicit AnytoneRadio(const QString &name, AnytoneInterface *device=nullptr, QObject *parent=nullptr); public: /** Destructor. */ virtual ~AnytoneRadio(); const QString &name() const; const Codeplug &codeplug() const; Codeplug &codeplug(); public slots: /** Starts the download of the codeplug and derives the generic configuration from it. */ bool startDownload(bool blocking=false, const ErrorStack &err=ErrorStack()); /** Derives the device-specific codeplug from the generic configuration and uploads that * codeplug to the radio. */ bool startUpload(Config *config, bool blocking=false, const Codeplug::Flags &flags = Codeplug::Flags(), const ErrorStack &err=ErrorStack()); /** Encodes the given user-database and uploades it to the device. */ bool startUploadCallsignDB(UserDatabase *db, bool blocking=false, const CallsignDB::Selection &selection=CallsignDB::Selection(), const ErrorStack &err=ErrorStack()); bool startUploadSatelliteConfig( SatelliteDatabase *db, bool blocking=false, const ErrorStack &err=ErrorStack()); protected: /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */ void run(); private: /** Downloads the codeplug from the radio. This method block until the download is complete. */ virtual bool download(); /** Uploads the encoded codeplug to the radio. This method block until the upload is complete. */ virtual bool upload(); /** Uploads the encoded callsign database to the radio. * This method block until the upload is complete. */ virtual bool uploadCallsigns(); /** Uploads the encoded satellite config to the radio. * This method block until the upload is complete. */ virtual bool uploadSatellites(); protected: /** The device identifier. */ QString _name; /** The interface to the radio. */ AnytoneInterface *_dev; /** If @c true, the codeplug on the radio gets updated upon upload. If @c false, it gets * overridden. */ Codeplug::Flags _codeplugFlags; /** Owns the generic configuration. */ Config *_config; /** A weak reference to the user-database. */ UserDatabase *_userDB; /** The actual binary codeplug representation. */ AnytoneCodeplug *_codeplug; /** The actual binary callsign database representation. */ CallsignDB *_callsigns; /** The actual binary callsign database representation. */ AnytoneSatelliteConfig *_satellites; }; #endif // __D868UV_HH__ qdmr-0.12.3/lib/anytone_satelliteconfig.cc000066400000000000000000000150221501654372000205310ustar00rootroot00000000000000#include "anytone_satelliteconfig.hh" #include "anytone_codeplug.hh" #include "satellitedatabase.hh" #include "logger.hh" /* ********************************************************************************************* * * Implementation of AnytoneSatelliteConfig::Satellite * ********************************************************************************************* */ AnytoneSatelliteConfig::SatelliteElement::SatelliteElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } AnytoneSatelliteConfig::SatelliteElement::SatelliteElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } void AnytoneSatelliteConfig::SatelliteElement::clear() { memset(_data, 0, size()); memset(_data, 0x20, 0x50); } void AnytoneSatelliteConfig::SatelliteElement::setName(const QString &name) { writeASCII(Offset::name(), name, Limit::name(), 0); } void AnytoneSatelliteConfig::SatelliteElement::setEpoch(const OrbitalElement::Epoch &epoch) { double tmp = epoch.toEpoch(); int year = epoch.year; int day = tmp; tmp -= day; tmp *= 100000000; int fday = tmp; QString fmt_year = QString("%1").arg(year%100, 2, 10, QChar('0')); QString fmt_day = QString("%1.%2") .arg(day, 3, 10, QChar('0')) .arg(fday, 8, 10, QChar('0')); writeASCII(Offset::epochYear(), fmt_year, 2); writeASCII(Offset::epochDay(), fmt_day, 12, 0x20); } void AnytoneSatelliteConfig::SatelliteElement::setMeanMotionDerivative(double dmm) { QString fmt = QString("%1.%2") .arg(dmm < 0 ? '-' : ' ') .arg((int)(std::abs(dmm) * 100000000), 8, 10, QChar('0')); writeASCII(Offset::meanMotionDerivative(), fmt, 10, 0x20); } void AnytoneSatelliteConfig::SatelliteElement::setInclination(double incl) { int ddd = incl; incl -= ddd; int ffff = incl*10000; QString fmt = QString("%1.%2") .arg(ddd, 3, 10, QChar(' ')) .arg(ffff, 4, 10, QChar('0')); writeASCII(Offset::inclination(), fmt, 8, 0x20); } void AnytoneSatelliteConfig::SatelliteElement::setAscension(double asc) { int ddd = asc; asc -= ddd; int ffff = asc*10000; QString fmt = QString("%1.%2") .arg(ddd, 3, 10, QChar(' ')) .arg(ffff, 4, 10, QChar('0')); writeASCII(Offset::ascension(), fmt, 8, 0x20); } void AnytoneSatelliteConfig::SatelliteElement::setEccentricity(double ecc) { QString fmt = QString("%1").arg((int)(ecc * 10000000), 7, 10, QChar('0')); writeASCII(Offset::eccentricity(), fmt, 7, 0x20); } void AnytoneSatelliteConfig::SatelliteElement::setPerigee(double peri) { int ddd = peri; peri -= ddd; int ffff = peri*10000; QString fmt = QString("%1.%2") .arg(ddd, 3, 10, QChar(' ')) .arg(ffff, 4, 10, QChar('0')); writeASCII(Offset::perigee(), fmt, 8, 0x20); } void AnytoneSatelliteConfig::SatelliteElement::setAnomaly(double ma) { int ddd = ma; ma -= ddd; int ffff = ma*10000; QString fmt = QString("%1.%2") .arg(ddd, 3, 10, QChar(' ')) .arg(ffff, 4, 10, QChar('0')); writeASCII(Offset::anomaly(), fmt, 8, 0x20); } void AnytoneSatelliteConfig::SatelliteElement::setMeanMotion(double mm) { int dd = mm; mm -= dd; int ffffffff = mm*100000000; QString fmt = QString("%1.%2") .arg(dd, 2, 10, QChar(' ')) .arg(ffffffff, 8, 10, QChar('0')); writeASCII(Offset::meanMotion(), fmt, 11, 0x20); } void AnytoneSatelliteConfig::SatelliteElement::setRevolution(unsigned int num) { writeASCII(Offset::revolution(), QString("%1").arg(num, 5, 10, QChar('0')), 5, 0x20); } void AnytoneSatelliteConfig::SatelliteElement::setDownlink(const Frequency &f) { setUInt32_le(Offset::downlinkFrequency(), f.inHz()/10); } void AnytoneSatelliteConfig::SatelliteElement::setDownlinkTone(const SelectiveCall &tone) { if (tone.isInvalid()) { setUInt8(Offset::downlinkToneType(), (int)ToneType::None); } else if (tone.isCTCSS()) { setUInt8(Offset::downlinkToneType(), (int)ToneType::CTCSS); setUInt8(Offset::downlinkCTCSS(), AnytoneCodeplug::CTCSS::encode(tone)); } else if (tone.isDCS()) { uint16_t val = tone.binCode(); if (tone.isInverted()) val |= (1<<9); setUInt8(Offset::downlinkToneType(), (int)ToneType::DCS); setUInt16_le(Offset::downlinkDCS(), val); } } void AnytoneSatelliteConfig::SatelliteElement::setUplink(const Frequency &f) { setUInt32_le(Offset::uplinkFrequency(), f.inHz()/10); } void AnytoneSatelliteConfig::SatelliteElement::setUplinkTone(const SelectiveCall &tone) { if (tone.isInvalid()) { setUInt8(Offset::uplinkToneType(), (int)ToneType::None); } else if (tone.isCTCSS()) { setUInt8(Offset::uplinkToneType(), (int)ToneType::CTCSS); setUInt8(Offset::uplinkCTCSS(), AnytoneCodeplug::CTCSS::encode(tone)); } else if (tone.isDCS()) { uint16_t val = tone.binCode(); if (tone.isInverted()) val |= (1<<9); setUInt8(Offset::uplinkToneType(), (int)ToneType::DCS); setUInt16_le(Offset::uplinkDCS(), val); } } bool AnytoneSatelliteConfig::SatelliteElement::encode(const Satellite &sat, const ErrorStack &err) { Q_UNUSED(err) setName(sat.name()); setEpoch(sat.epoch()); setMeanMotionDerivative(sat.meanMotionDerivative()); setInclination(sat.inclination()); setAscension(sat.ascension()); setEccentricity(sat.eccentricity()); setPerigee(sat.perigee()); setAnomaly(sat.meanAnomaly()); setMeanMotion(sat.meanMotion()); setRevolution(sat.revolutionNumber()); setDownlink(sat.fmDownlink()); setDownlinkTone(sat.fmDownlinkTone()); setUplink(sat.fmUplink()); setUplinkTone(sat.fmUplinkTone()); return true; } /* ********************************************************************************************* * * Implementation of AnytoneSatelliteConfig * ********************************************************************************************* */ AnytoneSatelliteConfig::AnytoneSatelliteConfig(QObject *parent) : SatelliteConfig{parent} { addImage("AnyTone Satellite Configuration"); } AnytoneSatelliteConfig::SatelliteElement AnytoneSatelliteConfig::satellite(unsigned int idx) { return SatelliteElement(data(Offset::satellites() + idx*Offset::betweenSatellites())); } bool AnytoneSatelliteConfig::encode(SatelliteDatabase *db, const ErrorStack &err) { unsigned int numSat = std::min(Limit::satellites(), db->count()); image(0).addElement(Offset::satellites(), numSat*SatelliteElement::size()); for (unsigned int i=0; igetAt(i).name() << "' at index " << i << "."; if (! satellite(i).encode(db->getAt(i), err)) { errMsg(err) << "Cannot encode satellite '" << db->getAt(i).name() << "at index " << i << "."; return false; } } return true; } qdmr-0.12.3/lib/anytone_satelliteconfig.hh000066400000000000000000000105461501654372000205510ustar00rootroot00000000000000#ifndef ANYTONE_SATELLITECONFIG_HH #define ANYTONE_SATELLITECONFIG_HH #include "satelliteconfig.hh" #include "frequency.hh" #include "signaling.hh" #include "codeplug.hh" #include "orbitalelementsdatabase.hh" // Forward declarations class Satellite; /** Implementation of satellite confifuration for all AnyTone devices. * @ingroup anytone */ class AnytoneSatelliteConfig : public SatelliteConfig { Q_OBJECT public: class SatelliteElement: Codeplug::Element { protected: /** Internal encoding of sub tone type. */ enum class ToneType { None = 0, CTCSS = 1, DCS = 2 }; protected: /** Hidden constructor. */ SatelliteElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit SatelliteElement(uint8_t *ptr); static constexpr unsigned int size() { return 0x200; } void clear(); /** Sets the satellite name. */ void setName(const QString &name); /** Sets the epoch of the orbital element. */ void setEpoch(const OrbitalElement::Epoch &epoch); /** Sets the derivative of the mean motion. */ void setMeanMotionDerivative(double dmm); /** Sets the inclination. */ void setInclination(double incl); /** Sets the right ascension of the ascending node. */ void setAscension(double asc); /** Sets the eccentricity */ void setEccentricity(double ecc); /** Sets the argument of perigee. */ void setPerigee(double peri); /** Sets the mean anomaly. */ void setAnomaly(double ma); /** Sets the mean motion. */ void setMeanMotion(double mm); /** Sets the revolution number. */ void setRevolution(unsigned int num); /** Sets the downlink frequency. */ void setDownlink(const Frequency &f); /** Sets the downlink sub tone. */ void setDownlinkTone(const SelectiveCall &tone); /** Sets the uplink frequency. */ void setUplink(const Frequency &f); /** Sets the uplink sub tone. */ void setUplinkTone(const SelectiveCall &tone); /** Encodes the given satellite. */ bool encode(const Satellite &sat, const ErrorStack &err=ErrorStack()); public: /** Some limits for the satellite. */ struct Limit: Element::Limit { /** Maximum size of satellite name. */ static constexpr unsigned int name() { return 8; } }; protected: /** Some internal offsets. */ struct Offset: Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int epochYear() { return 0x0008; } static constexpr unsigned int epochDay() { return 0x000a; } static constexpr unsigned int meanMotionDerivative() { return 0x0017; } static constexpr unsigned int inclination() { return 0x0021; } static constexpr unsigned int ascension() { return 0x002a; } static constexpr unsigned int eccentricity() { return 0x0033; } static constexpr unsigned int perigee() { return 0x003b; } static constexpr unsigned int anomaly() { return 0x0044; } static constexpr unsigned int meanMotion() { return 0x004d; } static constexpr unsigned int revolution() { return 0x0058; } static constexpr unsigned int downlinkFrequency() { return 0x0060; } static constexpr unsigned int uplinkFrequency() { return 0x0064; } static constexpr unsigned int uplinkToneType() { return 0x0068; } static constexpr unsigned int downlinkToneType() { return 0x0069; } static constexpr unsigned int uplinkCTCSS() { return 0x006a; } static constexpr unsigned int downlinkCTCSS() { return 0x006b; } static constexpr unsigned int uplinkDCS() { return 0x006c; } static constexpr unsigned int downlinkDCS() { return 0x006e; } /// @endcond }; }; public: /** Default constructor. */ explicit AnytoneSatelliteConfig(QObject *parent = nullptr); SatelliteElement satellite(unsigned int idx); bool encode(SatelliteDatabase *db, const ErrorStack &err=ErrorStack()); public: /** Some limits for the satellite config. */ struct Limit { static constexpr unsigned int satellites() { return 200; } }; protected: /** Some internal offsets. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int satellites() { return 0x2ec0000; } static constexpr unsigned int betweenSatellites() { return SatelliteElement::size(); } /// @endcond }; }; #endif // ANYTONE_SATELLITECONFIG_HH qdmr-0.12.3/lib/auctus_a6_interface.cc000066400000000000000000000121341501654372000175330ustar00rootroot00000000000000#include "auctus_a6_interface.hh" #include #include #include #include "logger.hh" #define TIMEOUT 5000 AuctusA6Interface::AuctusA6Interface( const USBDeviceDescriptor &descriptor, const ErrorStack &err, QObject *parent) : USBSerial(descriptor, QSerialPort::Baud9600, err, parent), _state(CLOSED) { if (isOpen()) { _state = IDLE; } else { errMsg(err) << "Cannot construct Auctus A6 interface."; _state = ERROR; return; } if (! clear(QSerialPort::AllDirections)) { logWarn() << "Cannot clear RX/TX buffer of serial interface."; } logDebug() << "Open interface to Auctus A6 based radio."; } AuctusA6Interface::State AuctusA6Interface::state() const { return _state; } bool AuctusA6Interface::send_receive(uint16_t command, const uint8_t *params, uint8_t plen, uint8_t *response, uint8_t &rlen, const ErrorStack &err) { if (! send(command, params, plen, err)) { errMsg(err) << "Cannot send command."; return false; } uint16_t rcommand=0; if (! receive(rcommand, response, rlen, err)) { errMsg(err) << "Cannot receive response."; return false; } if ((rcommand&0x7fff) != command) { errMsg(err) << "Request and response commands mismatch. Expected " << QString::number(command, 16) << " got " << QString::number(command&0x7fff, 16) << "."; return false; } return true; } bool AuctusA6Interface::send(uint16_t command, const uint8_t *params, uint8_t plen, const ErrorStack &err) { // check parameter length if (plen>249) { errMsg(err) << "Parameter length cannot exceed 250 bytes."; errMsg(err) << "Cannot send request " << QString::number(command, 16) << "h."; return false; } // assemble request uint8_t buffer[255], *ptr=buffer; uint8_t total_length = 6 + plen; memset(buffer, 0, total_length); // start of packet *ptr = 0xaa; ptr += sizeof(uint8_t); // length *ptr = 6 + plen; ptr += sizeof(uint8_t); // total length // command (*(uint16_t *)(ptr)) = qToBigEndian(command); ptr += sizeof(uint16_t); // copy parameters memcpy(ptr, params, plen); ptr += plen; // compute CRC for (int i=0; i<(3+plen); i++) (*ptr) ^= buffer[1+i]; // Skip start-of-packet byte. ptr += sizeof(uint8_t); // end of packet *ptr = 0xbb; // send request logDebug() << "Send " << QByteArray((const char *)buffer, total_length).toHex(); if (total_length != QSerialPort::write((const char*)buffer, total_length)) { errMsg(err) << "QSerialPort: " << errorString(); errMsg(err) << "Cannot send request " << QString::number(command, 16) << "h."; return false; } if (! flush()) { logWarn() << "No data writen to the device, " << bytesToWrite() << " of " << total_length << " left in buffer."; } return true; } bool AuctusA6Interface::receive(uint16_t &command, uint8_t *response, uint8_t &rlen, const ErrorStack &err) { uint8_t buffer[255]; // read start-of-packet and length bytes uint8_t total_length = 2; if (! read(buffer, total_length, TIMEOUT, err)) { errMsg(err) << "Cannot read response " << QString::number(command, 16) << "h."; return false; } // check start of packet if (0xaa != buffer[0]) { errMsg(err) << "Unexpected start-of-packet byte: Expected aah, got " << QString::number(buffer[0],16) << "h."; return false; } // get length total_length = buffer[1]; if (6 > total_length) { errMsg(err) << "Invalid packet: Expected minimum lenght of 6h, got " << QString::number(total_length, 16) << "h."; return false; } // read remaining packet if (! read(buffer+2, total_length-2, TIMEOUT, err)) { errMsg(err) << "Cannot read response " << QString::number(command, 16) << "h."; return false; } logDebug() << "Got response " << QByteArray((const char *)buffer, total_length).toHex() << "."; // unpack command command = qFromBigEndian((*(uint16_t *)(buffer+2))); // check CRC uint8_t crc = 0; for (int i=1; i<(total_length-1); i++) crc ^= buffer[i]; if (crc) { errMsg(err) << "Invalid response: Invalid CRC."; return false; } // Check if response buffer is sufficiently large if (rlen < (total_length-6)) { errMsg(err) << "Cannot store response in response buffer, buffer too small. " << "Got " << rlen << "bytes, needs " << (total_length-6) << "bytes."; return false; } // copy response payload rlen = total_length-6; memcpy(response, buffer+4, rlen); // done. return true; } bool AuctusA6Interface::read(uint8_t *data, qint64 n, unsigned int timeout_ms, const ErrorStack &err) { while (n > 0) { if (0 == bytesAvailable()) { if (! waitForReadyRead(timeout_ms)) { errMsg(err) << "QSerialPort: " << errorString(); return false; } } qint64 k = QSerialPort::read((char *)data, std::min(n, bytesAvailable())); if (0 > k) { errMsg(err) << "QSerialPort: " << errorString(); return false; } n -= k; data += k; } return true; } qdmr-0.12.3/lib/auctus_a6_interface.hh000066400000000000000000000041461501654372000175510ustar00rootroot00000000000000/** @defgroup auctus Auctus A6/A7 Based devices. * * Several devices use the radio-on-a-chip Auctus A6, which enables very cheap DMR radios. * * @ingroup dsc */ #ifndef AUCTUS_A6_INTERFACE_HH #define AUCTUS_A6_INTERFACE_HH #include #include "usbserial.hh" /** Implements the communication interface to radios using the Auctus A6 chip. * * This includes devices like Cotre A1 or the Baofeng DR-1801. This class only implements * the basic communication protocol. Specifics, like particular commands are implemented in * derived interface classes. * * @ingroup auctus */ class AuctusA6Interface : public USBSerial { Q_OBJECT public: /** Possible states of the interface. */ enum State { CLOSED, ///< Conntection to device is closed. IDLE, ///< Connection is open and device is ready. READ_THROUGH, ///< Read from memory. The device just sends some amount of data. WRITE_THROUGH, ///< Write to memory. The device just receives some amount of data. ERROR ///< An error state. }; protected: /** Hidden constructor. */ explicit AuctusA6Interface(const USBDeviceDescriptor &descriptor, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr); public: /** Returns the interface state. */ State state() const; protected: /** Internal used method to send messages to and receive responses from radio. */ bool send_receive(uint16_t command, const uint8_t *params, uint8_t plen, uint8_t *response, uint8_t &rlen, const ErrorStack &err=ErrorStack()); /** Internal used method to send a command. */ bool send(uint16_t command, const uint8_t *params, uint8_t plen, const ErrorStack &err=ErrorStack()); /** Internal used method to receive a response. */ bool receive(uint16_t &command, uint8_t *response, uint8_t &rlen, const ErrorStack &err=ErrorStack()); /** Reads exactly @c n bytes or timeouts. */ bool read(uint8_t *data, qint64 n, unsigned int timeout_ms, const ErrorStack &err=ErrorStack()); protected: /** Holds the state of the interface. */ State _state; }; #endif // AUCTUS_A6_INTERFACE_HH qdmr-0.12.3/lib/c7000device.cc000066400000000000000000000220301501654372000155260ustar00rootroot00000000000000#include "c7000device.hh" #include "logger.hh" #include #include #define C7000_VID 0x1206 #define C7000_PID 0x0227 /* ********************************************************************************************* * * Implementation of C7000Device::Packet * ********************************************************************************************* */ C7000Device::Packet::Packet() : _encoded() { // pass... } C7000Device::Packet::Packet(uint8_t command, uint8_t sub, uint8_t flags, const QByteArray &payload) { _encoded.resize(9 + payload.size()); _encoded.fill(0); _encoded[0] = 0x68; _encoded[1] = flags; _encoded[2] = command; _encoded[3] = sub; *((uint16_t *)(_encoded.data()+6)) = qToLittleEndian((uint16_t)payload.size()); memcpy(_encoded.data()+8,payload.constData(), payload.size()); _encoded[8+payload.size()] = 0x10; uint32_t crc = 0xffff; for (int i=0; i<(_encoded.size()/2); i++) { uint16_t v = qFromLittleEndian(*(uint16_t *)(_encoded.constData()+2*i)); if (crc < v) crc += 0xffff; crc -= v; } if (_encoded.size()%2) { uint16_t v = _encoded[_encoded.size()-1]; if (crc < v) crc += 0xffff; crc -= v; } *((uint16_t *)(_encoded.data()+4)) = qToLittleEndian((uint16_t)crc); } C7000Device::Packet::Packet(const QByteArray &buffer) : _encoded() { _encoded.append(buffer); if (! isValid()) _encoded.clear(); } bool C7000Device::Packet::isValid() const { if (_encoded.size() < 9) return false; if (0x68 != _encoded[0]) return false; if (0x10 != _encoded[8+payloadSize()]) return false; int size = _encoded.size(); //9 + payloadSize(); uint32_t crc = 0xffff; for (int i=0; i (num = libusb_get_device_list(_ctx, &lst))) { errMsg(err) << "Cannot obtain list of USB devices."; libusb_exit(_ctx); _ctx = nullptr; return; } logDebug() << "Try to detect USB C7000 interface " << descr.description() << "."; USBDeviceHandle addr = descr.device().value(); for (int i=0; (i libusb_get_device_descriptor(lst[i],&usb_descr)) continue; if (descr.vendorId() != usb_descr.idVendor) continue; if (descr.productId() != usb_descr.idProduct) continue; logDebug() << "Matching device found at bus " << addr.bus << ", device " << addr.device << " with vendor ID " << QString::number(usb_descr.idVendor, 16) << " and product ID " << QString::number(usb_descr.idProduct, 16) << "."; libusb_ref_device(lst[i]); dev = lst[i]; } // Unref all devices and free list, matching device was referenced earlier libusb_free_device_list(lst, 1); if (nullptr == dev) { errMsg(err) << "No matching device found: " << descr.description() << "."; libusb_exit(_ctx); _ctx = nullptr; return; } if (0 > (error = libusb_open(dev, &_dev))) { errMsg(err) << "Cannot open device " << descr.description() << ": " << libusb_strerror((enum libusb_error) error) << "."; libusb_unref_device(dev); libusb_exit(_ctx); _ctx = nullptr; return; } if (libusb_kernel_driver_active(_dev, 0) && libusb_detach_kernel_driver(_dev, 0)) { errMsg(err) << "Cannot detach kernel driver for device " << descr.description() << ". Interface claim will likely fail."; } if (0 > (error = libusb_claim_interface(_dev, 0))) { errMsg(err) << "Failed to claim USB interface " << descr.description() << ": " << libusb_strerror((enum libusb_error) error) << "."; libusb_close(_dev); _dev = nullptr; libusb_exit(_ctx); _ctx = nullptr; return; } logDebug() << "Connected to C7000 device " << descr.description() << "."; } C7000Device::~C7000Device() { close(); } USBDeviceInfo C7000Device::interfaceInfo() { return USBDeviceInfo(USBDeviceInfo::Class::C7K, C7000_VID, C7000_PID); } QList C7000Device::detect(bool saveOnly) { Q_UNUSED(saveOnly) QList res; int error, num; libusb_context *ctx; if (0 > (error = libusb_init(&ctx))) { logError() << "Libusb init failed (" << error << "): " << libusb_strerror((enum libusb_error) error) << "."; return res; } libusb_device **lst; if (0 == (num = libusb_get_device_list(ctx, &lst))) { logDebug() << "No USB devices found at all."; // unref devices and free list libusb_free_device_list(lst, 1); return res; } logDebug() << "Search for C7000 devices matching VID:PID " << QString::number(C7000_VID, 16) << ":" << QString::number(C7000_PID, 16) << "."; for (int i=0; (iusleep(1000); unsigned int retry_count = 0; retry_receive: ret = libusb_bulk_transfer(_dev, 0x81, buffer, 64, &bytes_received, 1000); if (ret) { errMsg(err) << "Cannot receive response from device: " << libusb_error_name(ret) << "."; return false; } if (0x68 != buffer[0]) { if ((++retry_count) > 10) { errMsg(err) << "Cannot receive response from device: Retry count of 10 exceeded."; return false; } goto retry_receive; } response = Packet(QByteArray((const char *)buffer, bytes_received)); if (! response.isValid()) { errMsg(err) << "Invalid response received."; return false; } return true; } qdmr-0.12.3/lib/c7000device.hh000066400000000000000000000050741501654372000155510ustar00rootroot00000000000000#ifndef C7000DEVICE_HH #define C7000DEVICE_HH #include #include #include "errorstack.hh" #include "radiointerface.hh" /** Base class for all C7000 based radios. This class implements the basic communication protocol * to these devices. * @ingroup rif */ class C7000Device : public QObject { Q_OBJECT public: /** Request/response packet. */ struct Packet { public: /** Default constructor. */ Packet(); /** Copy constructor. */ Packet(const Packet &other) = default ; /** Constructs a request/response from commands and payload. */ Packet(uint8_t command, uint8_t sub, uint8_t flags=0x0f, const QByteArray &payload=QByteArray()); /** Constructs a request/response from the given encoded packet. */ Packet(const QByteArray &buffer); /** Assignment. */ Packet &operator =(const Packet &other) = default; /** Returns @c true, if the packet is valid. */ bool isValid() const; /** Returns the flags field. */ uint8_t flags() const; /** Returns the command field. */ uint8_t command() const; /** Returns the sub-command field. */ uint8_t subcommand() const; /** Returns the payload size. */ uint16_t payloadSize() const; /** Returns the payload field. */ QByteArray payload() const; /** Returns the encoded packet. */ const QByteArray &encoded() const; protected: /** Holds the encoded packet. */ QByteArray _encoded; }; public: /** Specialization to address a DFU device uniquely. */ class Descriptor: public USBDeviceDescriptor { public: /** Constructor from interface info, bus number and device address. */ Descriptor(const USBDeviceInfo &info, uint8_t bus, uint8_t device); }; public: /** Opens a connection to the C7000 device. */ C7000Device(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr); /** Destructor. */ virtual ~C7000Device(); /** Returns @c true if the C7000 interface is open. */ bool isOpen() const; /** Closes the C7000 interface. */ void close(); public: /** Returns some information about the interface. */ static USBDeviceInfo interfaceInfo(); /** Finds all C7000 interfaces. */ static QList detect(bool saveOnly=true); protected: /** Sends the given request to the device and receives the response. */ bool sendRecv(const Packet &request, Packet &response, const ErrorStack &err=ErrorStack()); protected: /** USB context. */ libusb_context *_ctx; /** USB device object. */ libusb_device_handle *_dev; }; #endif // C7000DEVICE_HH qdmr-0.12.3/lib/callsigndb.cc000066400000000000000000000022061501654372000157220ustar00rootroot00000000000000#include "callsigndb.hh" /* ********************************************************************************************* * * Implementation of CallsignDB::Selection * ********************************************************************************************* */ CallsignDB::Selection::Selection(int64_t count) : _count(count) { // pass... } CallsignDB::Selection::Selection(const Selection &other) : _count(other._count) { // pass... } bool CallsignDB::Selection::hasCountLimit() const { return (0 <= _count); } size_t CallsignDB::Selection::countLimit() const { if (0 > _count) return std::numeric_limits::max(); return _count; } void CallsignDB::Selection::setCountLimit(size_t n) { _count = n; } void CallsignDB::Selection::clearCountLimit() { _count = -1; } /* ********************************************************************************************* * * Implementation of CallsignDB * ********************************************************************************************* */ CallsignDB::CallsignDB(QObject *parent) : DFUFile(parent) { // pass... } CallsignDB::~CallsignDB() { // pass... } qdmr-0.12.3/lib/callsigndb.hh000066400000000000000000000030451501654372000157360ustar00rootroot00000000000000#ifndef CALLSIGNDB_HH #define CALLSIGNDB_HH #include "dfufile.hh" // Forward decl. class UserDatabase; /** Abstract base class of all callsign database implementations. * This class defines the interface for all device-specific binary encodings of call sign * databases. The interface is particularly simple: reimplement the @c encode method. * @ingroup conf */ class CallsignDB : public DFUFile { Q_OBJECT public: /** Controls the selection of callsigns from the @c UserDatabase to be encoded into the * callsign db. */ class Selection { public: /** Constructor. */ Selection(int64_t count=-1); /** Copy constructor. */ Selection(const Selection &other); /** Returns @c true if the selection has a limit on the number of callsigns to encode. */ bool hasCountLimit() const; /** Returns the limit of callsigns to encode. */ size_t countLimit() const; /** Sets the count limit. */ void setCountLimit(size_t n); /** Clears the count limit. */ void clearCountLimit(); protected: /** Specifies the maximum amount of callsigns to add. If negative, the device limit should be * used. */ int64_t _count; }; protected: /** Hidden constructor. */ explicit CallsignDB(QObject *parent=nullptr); public: /** Destructor. */ virtual ~CallsignDB(); /** Encodes the given user db into the device specific callsign db. */ virtual bool encode(UserDatabase *db, const Selection &selection=Selection(), const ErrorStack &err=ErrorStack()) = 0; }; #endif // CALLSIGNDB_HH qdmr-0.12.3/lib/channel.cc000066400000000000000000000604121501654372000152330ustar00rootroot00000000000000#include "channel.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "config.hh" #include "scanlist.hh" #include "logger.hh" #include #include #include #include #include #include #include #include #include #include "utils.hh" #include "application.hh" #include #include #include #include #include "opengd77_extension.hh" /* ********************************************************************************************* * * Implementation of Channel * ********************************************************************************************* */ Channel::Channel(QObject *parent) : ConfigObject("ch", parent), _rxFreq(Frequency::fromHz(0)), _txFreq(Frequency::fromHz(0)), _defaultPower(true), _power(Power::Low), _txTimeOut(std::numeric_limits::max()), _rxOnly(false), _vox(std::numeric_limits::max()), _scanlist(), _openGD77ChannelExtension(nullptr), _tytChannelExtension(nullptr) { // Link scan list modification event (e.g., scan list gets deleted). connect(&_scanlist, SIGNAL(modified()), this, SLOT(onReferenceModified())); } Channel::Channel(const Channel &other, QObject *parent) : ConfigObject("ch", parent), _scanlist(), _openGD77ChannelExtension(nullptr), _tytChannelExtension(nullptr) { Channel::copy(other); // Link scan list modification event (e.g., scan list gets deleted). connect(&_scanlist, SIGNAL(modified()), this, SLOT(onReferenceModified())); } bool Channel::copy(const ConfigItem &other) { const Channel *c = other.as(); if ((nullptr == c) || (! ConfigObject::copy(other))) return false; if (c->defaultPower()) setDefaultPower(); if (c->defaultTimeout()) setDefaultTimeout(); if (c->defaultVOX()) setVOXDefault(); return true; } void Channel::clear() { ConfigObject::clear(); setRXFrequency(Frequency::fromHz(0)); setTXFrequency(Frequency::fromHz(0)); setDefaultPower(); setDefaultTimeout(); setRXOnly(false); setVOXDefault(); _scanlist.clear(); setOpenGD77ChannelExtension(nullptr); setTyTChannelExtension(nullptr); } Frequency Channel::rxFrequency() const { return _rxFreq; } bool Channel::setRXFrequency(Frequency freq) { if (freq == _rxFreq) return true; _rxFreq = freq; emit modified(this); return true; } Frequency Channel::txFrequency() const { return _txFreq; } bool Channel::setTXFrequency(Frequency freq) { if (freq == _txFreq) return true; _txFreq = freq; emit modified(this); return true; } bool Channel::defaultPower() const { return _defaultPower; } Channel::Power Channel::power() const { return _power; } void Channel::setPower(Power power) { if ((power == _power) && (! _defaultPower)) return; _power = power; _defaultPower = false; emit modified(this); } void Channel::setDefaultPower() { if (defaultPower()) return; _defaultPower = true; emit modified(this); } bool Channel::defaultTimeout() const { return std::numeric_limits::max() == timeout(); } bool Channel::timeoutDisabled() const { return 0 == timeout(); } unsigned Channel::timeout() const { return _txTimeOut; } bool Channel::setTimeout(unsigned dur) { if (dur == _txTimeOut) return true; _txTimeOut = dur; emit modified(this); return true; } void Channel::setDefaultTimeout() { if (defaultTimeout()) return; setTimeout(std::numeric_limits::max()); emit modified(this); } void Channel::disableTimeout() { setTimeout(0); } bool Channel::rxOnly() const { return _rxOnly; } bool Channel::setRXOnly(bool enable) { _rxOnly = enable; emit modified(this); return true; } bool Channel::voxDisabled() const { return 0==vox(); } bool Channel::defaultVOX() const { return std::numeric_limits::max() == vox(); } unsigned Channel::vox() const { return _vox; } void Channel::setVOX(unsigned level) { _vox = level; emit modified(this); } void Channel::setVOXDefault() { setVOX(std::numeric_limits::max()); } void Channel::disableVOX() { setVOX(0); } const ScanListReference * Channel::scanListRef() const { return &_scanlist; } ScanListReference * Channel::scanListRef() { return &_scanlist; } ScanList * Channel::scanList() const { return _scanlist.as(); } bool Channel::setScanList(ScanList *list) { return _scanlist.set(list); } void Channel::onReferenceModified() { emit modified(this); } OpenGD77ChannelExtension * Channel::openGD77ChannelExtension() const { return _openGD77ChannelExtension; } void Channel::setOpenGD77ChannelExtension(OpenGD77ChannelExtension *ext) { if (_openGD77ChannelExtension == ext) return; if (_openGD77ChannelExtension) _openGD77ChannelExtension->deleteLater(); _openGD77ChannelExtension = ext; if (_openGD77ChannelExtension) { _openGD77ChannelExtension->setParent(this); connect(_openGD77ChannelExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onReferenceModified())); } } TyTChannelExtension * Channel::tytChannelExtension() const { return _tytChannelExtension; } void Channel::setTyTChannelExtension(TyTChannelExtension *ext) { if (_tytChannelExtension == ext) return; if (_tytChannelExtension) _tytChannelExtension->deleteLater(); _tytChannelExtension = ext; if (_tytChannelExtension) { _tytChannelExtension->setParent(this); connect(_tytChannelExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onReferenceModified())); } } bool Channel::populate(YAML::Node &node, const Context &context, const ErrorStack &err) { if (! ConfigObject::populate(node, context, err)) return false; // Serialize freuqencies in MHz node["rxFrequency"] = _rxFreq; node["txFrequency"] = _txFreq; if (defaultPower()) { YAML::Node def = YAML::Node(YAML::NodeType::Scalar); def.SetTag("!default"); node["power"] = def; } else { QMetaEnum metaEnum = QMetaEnum::fromType(); node["power"] = metaEnum.valueToKey((unsigned)power()); } if (defaultTimeout()) { YAML::Node def = YAML::Node(YAML::NodeType::Scalar); def.SetTag("!default"); node["timeout"] = def; } else { node["timeout"] = timeout(); } if (defaultVOX()) { YAML::Node def = YAML::Node(YAML::NodeType::Scalar); def.SetTag("!default"); node["vox"] = def; } else { node["vox"] = vox(); } return true; } bool Channel::parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { if (! node) return false; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot parse channel: Expected object with one child."; return false; } YAML::Node ch = node.begin()->second; // Parse frequencies if (ch["rxFrequency"].IsNull()) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << "Cannot parse channel. No rxFreuqency specified."; return false; } setRXFrequency(ch["rxFrequency"].as()); if (ch["txFrequency"].IsNull()) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << "Cannot parse channel. No txFrequency specified."; return false; } setTXFrequency(ch["txFrequency"].as()); if ((!ch["power"]) || ("!default" == ch["power"].Tag())) { setDefaultPower(); } else if (ch["power"] && ch["power"].IsScalar()) { QMetaEnum meta = QMetaEnum::fromType(); setPower((Channel::Power)meta.keyToValue(ch["power"].as().c_str())); } if ((!ch["timeout"]) || ("!default" == ch["timeout"].Tag())) { setDefaultTimeout(); } else if (ch["timeout"] && ch["timeout"].IsScalar()) { setTimeout(ch["timeout"].as()); } if ((!ch["vox"]) || ("!default" == ch["vox"].Tag())) { setVOXDefault(); } else if (ch["vox"] && ch["vox"].IsScalar()) { setVOX(ch["vox"].as()); } return ConfigObject::parse(ch, ctx, err); } bool Channel::link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err) { if (! node) return false; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot link channel: Expected object with one child."; return false; } return ConfigObject::link(node.begin()->second, ctx, err); } /* ********************************************************************************************* * * Implementation of AnalogChannel * ********************************************************************************************* */ AnalogChannel::AnalogChannel(QObject *parent) : Channel(parent) { // pass... } AnalogChannel::AnalogChannel(const AnalogChannel &other, QObject *parent) : Channel(other, parent) { // pass... } /* ********************************************************************************************* * * Implementation of FMChannel * ********************************************************************************************* */ FMChannel::FMChannel(QObject *parent) : AnalogChannel(parent), _admit(Admit::Always), _squelch(std::numeric_limits::max()), _rxTone(), _txTone(), _bw(Bandwidth::Narrow), _aprsSystem(), _anytoneExtension(nullptr) { // Link APRS system reference connect(&_aprsSystem, SIGNAL(modified()), this, SLOT(onReferenceModified())); } FMChannel::FMChannel(const FMChannel &other, QObject *parent) : AnalogChannel(parent), _aprsSystem(), _anytoneExtension(nullptr) { copy(other); // Link APRS system reference connect(&_aprsSystem, SIGNAL(modified()), this, SLOT(onReferenceModified())); } bool FMChannel::copy(const ConfigItem &other) { const FMChannel *c = other.as(); if ((nullptr==c) || (! AnalogChannel::copy(other))) return false; setRXTone(c->rxTone()); setTXTone(c->txTone()); return true; } ConfigItem * FMChannel::clone() const { FMChannel *c = new FMChannel(); if (! c->copy(*this)) { c->deleteLater(); return nullptr; } return c; } void FMChannel::clear() { AnalogChannel::clear(); setAdmit(Admit::Always); setSquelchDefault(); setRXTone(SelectiveCall()); setTXTone(SelectiveCall()); setBandwidth(Bandwidth::Narrow); setAPRSSystem(nullptr); setAnytoneChannelExtension(nullptr); } FMChannel::Admit FMChannel::admit() const { return _admit; } void FMChannel::setAdmit(Admit admit) { _admit = admit; emit modified(this); } bool FMChannel::defaultSquelch() const { return std::numeric_limits::max()==squelch(); } bool FMChannel::squelchDisabled() const { return 0==squelch(); } unsigned FMChannel::squelch() const { return _squelch; } bool FMChannel::setSquelch(unsigned val) { _squelch = val; emit modified(this); return true; } void FMChannel::disableSquelch() { setSquelch(0); } void FMChannel::setSquelchDefault() { setSquelch(std::numeric_limits::max()); } SelectiveCall FMChannel::rxTone() const { return _rxTone; } bool FMChannel::setRXTone(SelectiveCall code) { _rxTone = code; emit modified(this); return true; } SelectiveCall FMChannel::txTone() const { return _txTone; } bool FMChannel::setTXTone(SelectiveCall code) { _txTone = code; emit modified(this); return true; } FMChannel::Bandwidth FMChannel::bandwidth() const { return _bw; } bool FMChannel::setBandwidth(Bandwidth bw) { _bw = bw; emit modified(this); return true; } const APRSSystemReference * FMChannel::aprs() const { return &_aprsSystem; } APRSSystemReference * FMChannel::aprs() { return &_aprsSystem; } void FMChannel::setAPRS(APRSSystemReference *ref) { if (nullptr == ref) return _aprsSystem.clear(); _aprsSystem.copy(ref); } APRSSystem * FMChannel::aprsSystem() const { return _aprsSystem.as(); } void FMChannel::setAPRSSystem(APRSSystem *sys) { _aprsSystem.set(sys); } AnytoneFMChannelExtension * FMChannel::anytoneChannelExtension() const { return _anytoneExtension; } void FMChannel::setAnytoneChannelExtension(AnytoneFMChannelExtension *ext) { if (_anytoneExtension == ext) return; if (_anytoneExtension) _anytoneExtension->deleteLater(); _anytoneExtension = ext; if (_anytoneExtension) { _anytoneExtension->setParent(this); connect(_anytoneExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onReferenceModified())); } } YAML::Node FMChannel::serialize(const Context &context, const ErrorStack &err) { YAML::Node node = AnalogChannel::serialize(context, err); if (node.IsNull()) return node; YAML::Node type; type["analog"] = node; return type; } bool FMChannel::populate(YAML::Node &node, const Context &context, const ErrorStack &err) { if (! AnalogChannel::populate(node, context, err)) return false; if (defaultSquelch()) { YAML::Node def = YAML::Node(YAML::NodeType::Scalar); def.SetTag("!default"); node["squelch"] = def; } else { node["squelch"] = squelch(); } return true; } bool FMChannel::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) { if (! node) return false; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot parse analog channel: Expected object with one child."; return false; } YAML::Node ch = node.begin()->second; if ((!ch["squelch"]) || ("!default" == ch["squelch"].Tag())) { setSquelchDefault(); } else if (ch["squelch"].IsDefined() && ch["squelch"].IsScalar()) { setSquelch(ch["squelch"].as()); } return AnalogChannel::parse(node, ctx, err); } /* ********************************************************************************************* * * Implementation of DigitalChannel * ********************************************************************************************* */ DigitalChannel::DigitalChannel(QObject *parent) : Channel(parent) { // pass... } DigitalChannel::DigitalChannel(const DigitalChannel &other, QObject *parent) : Channel(other, parent) { // pass... } /* ********************************************************************************************* * * Implementation of DMRChannel * ********************************************************************************************* */ DMRChannel::DMRChannel(QObject *parent) : DigitalChannel(parent), _admit(Admit::Always), _colorCode(1), _timeSlot(TimeSlot::TS1), _rxGroup(), _txContact(), _posSystem(), _roaming(), _radioId(), _commercialExtension(nullptr), _anytoneExtension(nullptr) { // Register default tags if (! ConfigItem::Context::hasTag(staticMetaObject.className(), "roaming", "!default")) ConfigItem::Context::setTag(staticMetaObject.className(), "roaming", "!default", DefaultRoamingZone::get()); if (! ConfigItem::Context::hasTag(staticMetaObject.className(), "radioId", "!default")) ConfigItem::Context::setTag(staticMetaObject.className(), "radioId", "!default", DefaultRadioID::get()); // Set default DMR Id _radioId.set(DefaultRadioID::get()); // Connect signals of references connect(&_rxGroup, SIGNAL(modified()), this, SLOT(onReferenceModified())); connect(&_txContact, SIGNAL(modified()), this, SLOT(onReferenceModified())); connect(&_posSystem, SIGNAL(modified()), this, SLOT(onReferenceModified())); connect(&_roaming, SIGNAL(modified()), this, SLOT(onReferenceModified())); connect(&_radioId, SIGNAL(modified()), this, SLOT(onReferenceModified())); } DMRChannel::DMRChannel(const DMRChannel &other, QObject *parent) : DigitalChannel(parent), _rxGroup(), _txContact(), _posSystem(), _roaming(), _radioId(), _commercialExtension(nullptr), _anytoneExtension(nullptr) { // Register default tags if (! ConfigItem::Context::hasTag(staticMetaObject.className(), "roaming", "!default")) ConfigItem::Context::setTag(staticMetaObject.className(), "roaming", "!default", DefaultRoamingZone::get()); if (! ConfigItem::Context::hasTag(staticMetaObject.className(), "radioId", "!default")) ConfigItem::Context::setTag(staticMetaObject.className(), "radioId", "!default", DefaultRadioID::get()); copy(other); // Connect signals of references connect(&_rxGroup, SIGNAL(modified()), this, SLOT(onReferenceModified())); connect(&_txContact, SIGNAL(modified()), this, SLOT(onReferenceModified())); connect(&_posSystem, SIGNAL(modified()), this, SLOT(onReferenceModified())); connect(&_roaming, SIGNAL(modified()), this, SLOT(onReferenceModified())); connect(&_radioId, SIGNAL(modified()), this, SLOT(onReferenceModified())); } void DMRChannel::clear() { DigitalChannel::clear(); setColorCode(1); setTimeSlot(TimeSlot::TS1); setGroupListObj(nullptr); setTXContactObj(nullptr); setAPRSObj(nullptr); setRoamingZone(nullptr); setRadioIdObj(DefaultRadioID::get()); setCommercialExtension(nullptr); setAnytoneChannelExtension(nullptr); } ConfigItem * DMRChannel::clone() const { DMRChannel *c = new DMRChannel(); if (! c->copy(*this)) { c->deleteLater(); return nullptr; } return c; } DMRChannel::Admit DMRChannel::admit() const { return _admit; } void DMRChannel::setAdmit(Admit admit) { _admit = admit; emit modified(this); } unsigned DMRChannel::colorCode() const { return _colorCode; } bool DMRChannel::setColorCode(unsigned cc) { if (15 < cc) { logWarn() << "Color-code " << cc << " is not in range [0,15], set to 15."; cc = 15; } _colorCode = cc; emit modified(this); return true; } DMRChannel::TimeSlot DMRChannel::timeSlot() const { return _timeSlot; } bool DMRChannel::setTimeSlot(TimeSlot slot) { _timeSlot = slot; emit modified(this); return true; } const GroupListReference * DMRChannel::groupList() const { return &_rxGroup; } GroupListReference * DMRChannel::groupList() { return &_rxGroup; } void DMRChannel::setGroupList(GroupListReference *ref) { if (nullptr == ref) _rxGroup.clear(); else _rxGroup.copy(ref); } RXGroupList * DMRChannel::groupListObj() const { return _rxGroup.as(); } bool DMRChannel::setGroupListObj(RXGroupList *g) { if(! _rxGroup.set(g)) return false; emit modified(this); return true; } const DMRContactReference * DMRChannel::contact() const { return &_txContact; } DMRContactReference * DMRChannel::contact() { return &_txContact; } void DMRChannel::setContact(DMRContactReference *ref) { if (nullptr == ref) _txContact.clear(); else _txContact.copy(ref); } DMRContact * DMRChannel::txContactObj() const { return _txContact.as(); } bool DMRChannel::setTXContactObj(DMRContact *c) { if(! _txContact.set(c)) return false; emit modified(this); return true; } const PositioningSystemReference * DMRChannel::aprs() const { return &_posSystem; } PositioningSystemReference * DMRChannel::aprs() { return &_posSystem; } void DMRChannel::setAPRS(PositioningSystemReference *ref) { if (nullptr == ref) _posSystem.clear(); else _posSystem.copy(ref); } PositioningSystem * DMRChannel::aprsObj() const { return _posSystem.as(); } bool DMRChannel::setAPRSObj(PositioningSystem *sys) { if (! _posSystem.set(sys)) return false; emit modified(this); return true; } const RoamingZoneReference * DMRChannel::roaming() const { return &_roaming; } RoamingZoneReference * DMRChannel::roaming() { return &_roaming; } void DMRChannel::setRoaming(RoamingZoneReference *ref) { if (nullptr == ref) _roaming.clear(); else _roaming.copy(ref); } RoamingZone * DMRChannel::roamingZone() const { return _roaming.as(); } bool DMRChannel::setRoamingZone(RoamingZone *zone) { _roaming.set(zone); emit modified(this); return true; } const DMRRadioIDReference * DMRChannel::radioId() const { return &_radioId; } DMRRadioIDReference * DMRChannel::radioId() { return &_radioId; } void DMRChannel::setRadioId(DMRRadioIDReference *ref) { if (nullptr == ref) _radioId.clear(); else _radioId.copy(ref); } DMRRadioID * DMRChannel::radioIdObj() const { return _radioId.as(); } bool DMRChannel::setRadioIdObj(DMRRadioID *id) { if (! _radioId.set(id)) return false; emit modified(this); return true; } CommercialChannelExtension * DMRChannel::commercialExtension() const { return _commercialExtension; } void DMRChannel::setCommercialExtension(CommercialChannelExtension *ext) { if (_commercialExtension == ext) return; if (_commercialExtension) _commercialExtension->deleteLater(); _commercialExtension = ext; if (_commercialExtension) { _commercialExtension->setParent(this); connect(_commercialExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onReferenceModified())); } } AnytoneDMRChannelExtension * DMRChannel::anytoneChannelExtension() const { return _anytoneExtension; } void DMRChannel::setAnytoneChannelExtension(AnytoneDMRChannelExtension *ext) { if (_anytoneExtension == ext) return; if (_anytoneExtension) _anytoneExtension->deleteLater(); _anytoneExtension = ext; if (_anytoneExtension) { _anytoneExtension->setParent(this); connect(_anytoneExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onReferenceModified())); } } YAML::Node DMRChannel::serialize(const Context &context, const ErrorStack &err) { YAML::Node node = DigitalChannel::serialize(context, err); if (node.IsNull()) return node; YAML::Node type; type["digital"] = node; return type; } /* ********************************************************************************************* * * Implementation of SelectedChannel * ********************************************************************************************* */ SelectedChannel *SelectedChannel::_instance = nullptr; SelectedChannel::SelectedChannel() : Channel() { setName("[Selected]"); } SelectedChannel::~SelectedChannel() { SelectedChannel::_instance = nullptr; } bool SelectedChannel::copy(const ConfigItem &other) { Q_UNUSED(other); return false; } ConfigItem * SelectedChannel::clone() const { return nullptr; } SelectedChannel * SelectedChannel::get() { if (nullptr == SelectedChannel::_instance) SelectedChannel::_instance = new SelectedChannel(); return SelectedChannel::_instance; } /* ********************************************************************************************* * * Implementation of ChannelList * ********************************************************************************************* */ ChannelList::ChannelList(QObject *parent) : ConfigObjectList(Channel::staticMetaObject, parent) { // pass... } int ChannelList::add(ConfigObject *obj, int row, bool unique) { if ((nullptr == obj) || (! obj->is())) { logError() << "Cannot add nullptr or non-channel objects to channel list."; return -1; } return ConfigObjectList::add(obj, row, unique); } Channel * ChannelList::channel(int idx) const { if (ConfigItem *obj = get(idx)) return obj->as(); return nullptr; } DMRChannel * ChannelList::findDMRChannel(Frequency rx, Frequency tx, DMRChannel::TimeSlot ts, unsigned cc) const { for (int i=0; iis()) continue; /// @bug I should certainly change the frequency handling to integer values! if ((channel(i)->txFrequency()!=tx) || (channel(i)->rxFrequency()!=rx)) continue; DMRChannel *digi = channel(i)->as(); if (digi->timeSlot() != ts) continue; if (digi->colorCode() != cc) continue; return digi; } return nullptr; } FMChannel * ChannelList::findFMChannelByTxFreq(Frequency freq) const { for (int i=0; iis()) continue; if (channel(i)->txFrequency() == freq) return channel(i)->as(); } return nullptr; } ConfigItem * ChannelList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) if (! node) return nullptr; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create channel: Expected object with one child."; return nullptr; } QString type = QString::fromStdString(node.begin()->first.as()); if (("digital" == type) || ("dmr" == type)) { return new DMRChannel(); } else if (("analog" == type) || ("fm"==type)) { return new FMChannel(); } errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create channel: Unknown type '" << type << "'."; return nullptr; } qdmr-0.12.3/lib/channel.hh000066400000000000000000000467231501654372000152560ustar00rootroot00000000000000#ifndef CHANNEL_HH #define CHANNEL_HH #include #include #include "configobject.hh" #include "configreference.hh" #include "signaling.hh" #include "opengd77_extension.hh" #include "tyt_extensions.hh" #include "opengd77_extension.hh" #include "anytone_extension.hh" #include "commercial_extension.hh" class Config; class RXGroupList; class DMRContact; class ScanList; class APRSSystem; class PositioningSystem; class RoamingZone; class DMRRadioID; /** The base class of all channels (analog and digital) of a codeplug configuration. * * This class holds the common configuration of @c AnalogChannel and @c DigitalChannel, that is * the name, RX and TX frequencies, output power, TOT and default scanlist properties. * * @ingroup conf */ class Channel: public ConfigObject { Q_OBJECT /** The receive frequency of the channel in Hz. */ Q_PROPERTY(Frequency rxFrequency READ rxFrequency WRITE setRXFrequency SCRIPTABLE false) /** The transmit frequency of the channel in Hz. */ Q_PROPERTY(Frequency txFrequency READ txFrequency WRITE setTXFrequency SCRIPTABLE false) /** The transmit power. */ Q_PROPERTY(Power power READ power WRITE setPower SCRIPTABLE false) /** The transmit timeout in seconds. */ Q_PROPERTY(unsigned timeout READ timeout WRITE setTimeout SCRIPTABLE false) /** If true, the channel is receive only. */ Q_PROPERTY(bool rxOnly READ rxOnly WRITE setRXOnly) /** The scan list. */ Q_PROPERTY(ScanListReference* scanListRef READ scanListRef) /** The VOX setting. */ Q_PROPERTY(unsigned vox READ vox WRITE setVOX SCRIPTABLE false) /** The OpenGD77 channel extension. */ Q_PROPERTY(OpenGD77ChannelExtension* openGD77 READ openGD77ChannelExtension WRITE setOpenGD77ChannelExtension) /** The TyT channel extension. */ Q_PROPERTY(TyTChannelExtension* tyt READ tytChannelExtension WRITE setTyTChannelExtension) /** Specifies the prefix for every ID assigned to every channel during serialization. */ Q_CLASSINFO("IdPrefix", "ch") public: /** Possible power settings. */ enum class Power { Max, ///< Highest power setting (e.g. > 5W, if available). High, ///< High power setting (e.g, 5W). Mid, ///< Medium power setting (e.g., 2W, if available) Low, ///< Low power setting (e.g., 1W). Min ///< Lowest power setting (e.g., <1W, if available). }; Q_ENUM(Power) protected: /** Hidden constructor. * Constructs a new empty channel. */ explicit Channel(QObject *parent=nullptr); /** Copy constructor. */ Channel(const Channel &other, QObject *parent=nullptr); public: bool copy(const ConfigItem &other); void clear(); /** Returns the RX frequency of the channel in Hz. */ Frequency rxFrequency() const; /** (Re-)Sets the RX frequency of the channel in Hz. */ bool setRXFrequency(Frequency freq); /** Returns the TX frequency of the channel in Hz. */ Frequency txFrequency() const; /** (Re-)Sets the TX frequency of the channel in Hz. */ bool setTXFrequency(Frequency freq); /** Returns @c true if the channel uses the global default power setting. */ bool defaultPower() const; /** Returns the power setting of the channel if the channel does not use the default power. */ Power power() const; /** (Re-)Sets the power setting of the channel, overrides default power. */ void setPower(Power power); /** Sets the channel to use the default power setting. */ void setDefaultPower(); /** Returns @c true if the transmit timeout is specified by the global default value. */ bool defaultTimeout() const; /** Returns @c true if the transmit timeout is disabled. */ bool timeoutDisabled() const; /** Returns the TX timeout (TOT) in seconds. */ unsigned timeout() const; /** (Re-)Sets the TX timeout (TOT) in seconds. */ bool setTimeout(unsigned dur); /** Disables the transmit timeout. */ void disableTimeout(); /** Sets the timeout to the global default timeout. */ void setDefaultTimeout(); /** Returns @c true, if the channel is RX only. */ bool rxOnly() const; /** Set, whether the channel is RX only. */ bool setRXOnly(bool enable); /** Returns @c true if the VOX is disabled. */ bool voxDisabled() const; /** Returns @c true if the VOX is specified by the global default value. */ bool defaultVOX() const; /** Returns the VOX level [0-10]. */ unsigned vox() const; /** Sets the VOX level [0-10]. */ void setVOX(unsigned level); /** Sets the VOX level to the default value. */ void setVOXDefault(); /** Disables the VOX. */ void disableVOX(); /** Returns the reference to the scan list. */ const ScanListReference *scanListRef() const; /** Returns the reference to the scan list. */ ScanListReference *scanListRef(); /** Returns the default scan list for the channel. */ ScanList *scanList() const; /** (Re-) Sets the default scan list for the channel. */ bool setScanList(ScanList *list); /** Returns the channel extension for the OpenGD77 firmware. * If this extension is not set, returns @c nullptr. */ OpenGD77ChannelExtension *openGD77ChannelExtension() const; /** Sets the OpenGD77 channel extension. */ void setOpenGD77ChannelExtension(OpenGD77ChannelExtension *ext); /** Returns the channel extension for TyT devices. * If this extension is not set, returns @c nullptr. */ TyTChannelExtension *tytChannelExtension() const; /** Sets the TyT channel extension. */ void setTyTChannelExtension(TyTChannelExtension *ext); public: bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack()); bool link(const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack()); protected: bool populate(YAML::Node &node, const Context &context, const ErrorStack &err=ErrorStack()); protected slots: /** Gets called whenever a referenced object is changed or deleted. */ void onReferenceModified(); protected: /** The RX frequency in Hz. */ Frequency _rxFreq; /** The TX frequency in Hz. */ Frequency _txFreq; /** If @c true, the channel uses the global power setting. */ bool _defaultPower; /** The transmit power setting. */ Power _power; /** Transmit timeout in seconds. */ unsigned _txTimeOut; /** RX only flag. */ bool _rxOnly; /** Holds the VOX level. */ unsigned _vox; /** Default scan list of the channel. */ ScanListReference _scanlist; /** Owns the OpenGD77 channel extension object. */ OpenGD77ChannelExtension *_openGD77ChannelExtension; /** Owns the TyT channel extension object. */ TyTChannelExtension *_tytChannelExtension; }; /** Base class for all analog channels. * * @ingroup conf */ class AnalogChannel: public Channel { Q_OBJECT protected: /** Hidden constructor. */ explicit AnalogChannel(QObject *parent=nullptr); public: /** Copy constructor. */ AnalogChannel(const AnalogChannel &other, QObject *parent=nullptr); }; /** Extension to the @c AnalogChannel class to implement an analog FM channel. * * This class implements all the properties specific to an FM channel. That is, the admit * criterion, squelch, RX and TX tones and bandwidth settings. * * @ingroup conf */ class FMChannel: public AnalogChannel { Q_OBJECT /** The admit criterion of the channel. */ Q_PROPERTY(Admit admit READ admit WRITE setAdmit) /** The squelch level of the channel [1-10]. */ Q_PROPERTY(unsigned squelch READ squelch WRITE setSquelch SCRIPTABLE false) /** The RX tone (CTCSS/DSC). */ Q_PROPERTY(SelectiveCall rxTone READ rxTone WRITE setRXTone) /** The TX tone (CTCSS/DSC). */ Q_PROPERTY(SelectiveCall txTone READ txTone WRITE setTXTone) /** The band width of the channel. */ Q_PROPERTY(Bandwidth bandwidth READ bandwidth WRITE setBandwidth) /** The APRS system. */ Q_PROPERTY(APRSSystemReference* aprs READ aprs WRITE setAPRS) /** The AnyTone FM channel extension. */ Q_PROPERTY(AnytoneFMChannelExtension* anytone READ anytoneChannelExtension WRITE setAnytoneChannelExtension) public: /** Admit criteria of analog channel. */ enum class Admit { Always, ///< Allow always. Free, ///< Allow when channel free. Tone ///< Allow when free or wrong ctcss/dcs tone is present. }; Q_ENUM(Admit) /** Possible bandwidth of an analog channel. */ enum class Bandwidth { Narrow, ///< Narrow bandwidth (12.5kHz). Wide ///< Wide bandwidth (25kHz). }; Q_ENUM(Bandwidth) public: /** Constructs a new empty analog channel. */ Q_INVOKABLE explicit FMChannel(QObject *parent=nullptr); /** Copy constructor. */ FMChannel(const FMChannel &other, QObject *parent=nullptr); bool copy(const ConfigItem &other); ConfigItem *clone() const; void clear(); /** Returns the admit criterion for the analog channel. */ Admit admit() const; /** (Re-)Sets the admit criterion for the analog channel. */ void setAdmit(Admit admit); /** Returns @c true if the global default squelch level is used. */ bool defaultSquelch() const; /** Returns @c true if the squelch is disabled. */ bool squelchDisabled() const; /** Returns the squelch level [0,10]. */ unsigned squelch() const; /** (Re-)Sets the squelch level [0,10]. 0 Disables squelch (on some radios). */ bool setSquelch(unsigned squelch); /** Disables the quelch. */ void disableSquelch(); /** Sets the squelch to the global default value. */ void setSquelchDefault(); /** Returns the CTCSS/DCS RX tone, @c SIGNALING_NONE means disabled. */ SelectiveCall rxTone() const; /** (Re-)Sets the CTCSS/DCS RX tone, @c SIGNALING_NONE disables the RX tone. */ bool setRXTone(SelectiveCall code); /** Returns the CTCSS/DCS TX tone, @c SIGNALING_NONE means disabled. */ SelectiveCall txTone() const; /** (Re-)Sets the CTCSS/DCS TX tone, @c SIGNALING_NONE disables the TX tone. */ bool setTXTone(SelectiveCall code); /** Returns the bandwidth of the analog channel. */ Bandwidth bandwidth() const; /** (Re-)Sets the bandwidth of the analog channel. */ bool setBandwidth(Bandwidth bw); /** Returns the reference to the APRS system. */ const APRSSystemReference *aprs() const; /** Returns the reference to the APRS system. */ APRSSystemReference *aprs(); /** Sets the APRS system reference. */ void setAPRS(APRSSystemReference *ref); /** Returns the APRS system used for this channel or @c nullptr if disabled. */ APRSSystem *aprsSystem() const; /** Sets the APRS system. */ void setAPRSSystem(APRSSystem *sys); /** Returns the FM channel extension for AnyTone devices. * If this extension is not set, returns @c nullptr. */ AnytoneFMChannelExtension *anytoneChannelExtension() const; /** Sets the AnyTone FM channel extension. */ void setAnytoneChannelExtension(AnytoneFMChannelExtension *ext); public: YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack()); bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack()); protected: bool populate(YAML::Node &node, const Context &context, const ErrorStack &err=ErrorStack()); protected: /** Holds the admit criterion. */ Admit _admit; /** Holds the squelch level [0,10]. */ unsigned _squelch; /** The RX CTCSS/DCS setting. */ SelectiveCall _rxTone; /** The TX CTCSS/DCS setting. */ SelectiveCall _txTone; /** The channel bandwidth. */ Bandwidth _bw; /** A reference to the APRS system used on the channel or @c nullptr if disabled. */ APRSSystemReference _aprsSystem; /** Owns the AnyTone FM channel extension. */ AnytoneFMChannelExtension *_anytoneExtension; }; /** Base class of all digital channels. * * @ingroup conf */ class DigitalChannel: public Channel { Q_OBJECT protected: /** Hidden constructor. */ explicit DigitalChannel(QObject *parent=nullptr); public: /** Copy constructor. */ DigitalChannel(const DigitalChannel &other, QObject *parent=nullptr); }; /** Extension to the @c DigitalChannel class to implement an DMR channel. * * That is, the admit criterion, color code, time slot, RX group list and TX contact. * * @ingroup conf */ class DMRChannel: public DigitalChannel { Q_OBJECT /** The admit criterion of the channel. */ Q_PROPERTY(Admit admit READ admit WRITE setAdmit) /** The color code of the channel. */ Q_PROPERTY(unsigned colorCode READ colorCode WRITE setColorCode) /** The time slot of the channel. */ Q_PROPERTY(TimeSlot timeSlot READ timeSlot WRITE setTimeSlot) /** The radio ID. */ Q_PROPERTY(DMRRadioIDReference* radioId READ radioId WRITE setRadioId) /** The rx group list. */ Q_PROPERTY(GroupListReference* groupList READ groupList WRITE setGroupList) /** The tx contact. */ Q_PROPERTY(DMRContactReference* contact READ contact WRITE setContact) /** The positioning system. */ Q_PROPERTY(PositioningSystemReference* aprs READ aprs WRITE setAPRS) /** The roaming zone. */ Q_PROPERTY(RoamingZoneReference* roaming READ roaming WRITE setRoaming) /** The commercial channel extension. */ Q_PROPERTY(CommercialChannelExtension* commercial READ commercialExtension WRITE setCommercialExtension) /** The AnyTone DMR channel extension. */ Q_PROPERTY(AnytoneDMRChannelExtension* anytone READ anytoneChannelExtension WRITE setAnytoneChannelExtension) public: /** Possible admit criteria of digital channels. */ enum class Admit { Always, ///< No admit criteria, allows one to transmit any time. Free, ///< Transmit only if channel is free. ColorCode ///< Transmit if channel is free or differs given color code. }; Q_ENUM(Admit) /** Possible timeslots for digital channels. */ enum class TimeSlot { TS1, ///< Time/repeater slot 1 TS2 ///< Time/repeater slot 2 }; Q_ENUM(TimeSlot) public: /** Constructs a new empty digital (DMR) channel. */ Q_INVOKABLE explicit DMRChannel(QObject *parent=nullptr); /** Copy constructor. */ DMRChannel(const DMRChannel &other, QObject *parent=nullptr); ConfigItem *clone() const; void clear(); /** Returns the admit criterion for the channel. */ Admit admit() const; /** (Re-)Sets the admit criterion for the channel. */ void setAdmit(Admit admit); /** Returns the color code for the channel. */ unsigned colorCode() const; /** (Re-)Sets the color code for the channel. */ bool setColorCode(unsigned cc); /** Returns the time slot for the channel. */ TimeSlot timeSlot() const; /** (Re-)Sets the time slot for the channel. */ bool setTimeSlot(TimeSlot ts); /** Returns a reference to the group list. */ const GroupListReference *groupList() const; /** Returns a reference to the group list. */ GroupListReference *groupList(); /** Sets the reference to the group list. */ void setGroupList(GroupListReference *ref); /** Returns the RX group list for the channel. */ RXGroupList *groupListObj() const; /** (Re-)Sets the RX group list for the channel. */ bool setGroupListObj(RXGroupList *rxg); /** Returns a reference to the transmit contact. */ const DMRContactReference *contact() const; /** Returns a reference to the transmit contact. */ DMRContactReference *contact(); /** Sets the reference to the transmit contact. */ void setContact(DMRContactReference *ref); /** Returns the default TX contact to call on this channel. */ DMRContact *txContactObj() const; /** (Re-) Sets the default TX contact for this channel. */ bool setTXContactObj(DMRContact *c); /** Returns a reference to the positioning system. */ const PositioningSystemReference *aprs() const; /** Returns a reference to the positioning system. */ PositioningSystemReference *aprs(); /** Sets the reference to the positioning system. */ void setAPRS(PositioningSystemReference *ref); /** Returns the GPS system associated with this channel or @c nullptr if not set. */ PositioningSystem *aprsObj() const; /** Associates the GPS System with this channel. */ bool setAPRSObj(PositioningSystem *sys); /** Returns a reference to the roaming zone. */ const RoamingZoneReference *roaming() const; /** Returns a reference to the roaming zone. */ RoamingZoneReference *roaming(); /** Sets the reference to the roaming zone. */ void setRoaming(RoamingZoneReference *ref); /** Returns the roaming zone associated with this channel or @c nullptr if not set. */ RoamingZone *roamingZone() const; /** Associates the given roaming zone with this channel. */ bool setRoamingZone(RoamingZone *zone); /** Returns the reference to the radio ID. */ const DMRRadioIDReference *radioId() const; /** Returns the reference to the radio ID. */ DMRRadioIDReference *radioId(); /** Sets the reference to the radio ID. */ void setRadioId(DMRRadioIDReference *ref); /** Returns the radio ID associated with this channel. */ DMRRadioID *radioIdObj() const; /** Associates the given radio ID with this channel. */ bool setRadioIdObj(DMRRadioID *id); /** Returns the extension for commercial features. */ CommercialChannelExtension *commercialExtension() const; /** Sets the commercial channel extension. */ void setCommercialExtension(CommercialChannelExtension *ext); /** Returns the DMR channel extension for AnyTone devices. * If this extension is not set, returns @c nullptr. */ AnytoneDMRChannelExtension *anytoneChannelExtension() const; /** Sets the AnyTone DMR channel extension. */ void setAnytoneChannelExtension(AnytoneDMRChannelExtension *ext); public: YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack()); protected: /** The admit criterion. */ Admit _admit; /** The channel color code. */ unsigned _colorCode; /** The time slot for the channel. */ TimeSlot _timeSlot; /** The RX group list for this channel. */ GroupListReference _rxGroup; /** The default TX contact. */ DMRContactReference _txContact; /** The GPS system. */ PositioningSystemReference _posSystem; /** Roaming zone for the channel. */ RoamingZoneReference _roaming; /** Radio ID to use on this channel. */ DMRRadioIDReference _radioId; /** Owns the commercial channel extension. */ CommercialChannelExtension *_commercialExtension; /** Owns the AnyTone DMR channel extension. */ AnytoneDMRChannelExtension *_anytoneExtension; }; /** Internal singleton class representing the "currently selected" channel. * @ingroup conf */ class SelectedChannel: public Channel { Q_OBJECT protected: /** Constructs the "selected" channel. * @warning Do not use this class directly, call @c SelectedChannel::get() instead. */ explicit SelectedChannel(); public: /** Destructor. */ virtual ~SelectedChannel(); bool copy(const ConfigItem &other); ConfigItem *clone() const; /** Constructs/gets the singleton instance. */ static SelectedChannel *get(); protected: /** Holds the channel singleton instance. */ static SelectedChannel *_instance; }; /** Container class holding all channels (analog and digital) for a specific configuration * (@c Config). * * This class also implements the QAbstractTableModel and can therefore be displayed using a * default QTableView instance. * * @ingroup conf */ class ChannelList: public ConfigObjectList { Q_OBJECT public: /** Constructs an empty channel list. */ explicit ChannelList(QObject *parent=nullptr); int add(ConfigObject *obj, int row=-1, bool unique=true); /** Gets the channel at the specified index. */ Channel *channel(int idx) const; /** Finds a digital channel with the given frequencies, time slot and color code. */ DMRChannel *findDMRChannel(Frequency rx, Frequency tx, DMRChannel::TimeSlot ts, unsigned cc) const; /** Finds an analog channel with the given frequency. */ FMChannel *findFMChannelByTxFreq(Frequency freq) const; public: ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); }; #endif // CHANNEL_HH qdmr-0.12.3/lib/chirpformat.cc000066400000000000000000000446451501654372000161530ustar00rootroot00000000000000#include "chirpformat.hh" #include "logger.hh" #include "frequency.hh" #include "signaling.hh" #include "channel.hh" #include "config.hh" #include /* ********************************************************************************************* * * Implementation of ChirpFormat * ********************************************************************************************* */ QSet const ChirpFormat::_mandatoryHeaders = { "Location", "Name", "Frequency", "Duplex", "Offset", "Mode" }; QSet const ChirpFormat::_knownHeaders = { "Location", "Name", "Frequency", "Duplex", "Offset", "Mode" "Tone", "rToneFreq", "cToneFreq", "DtcsCode", "DtcsPolarity", "RxDtcsCode", "CrossMode", "TStep", "Skip", "Power", "Comment", "URCALL", "RPT1CALL", "RPT2CALL", "DVCODE" }; QHash const ChirpFormat::_duplexCodes = { {"", ChirpFormat::Duplex::None}, {"+", ChirpFormat::Duplex::Positive}, {"-", ChirpFormat::Duplex::Negative}, {"Off", ChirpFormat::Duplex::Off}, }; QHash const ChirpFormat::_modeCodes = { {"FM", ChirpReader::Mode::FM}, {"NFM", ChirpReader::Mode::NFM}, {"WFM", ChirpReader::Mode::WFM}, {"AM", ChirpReader::Mode::AM}, {"DV", ChirpReader::Mode::DV}, {"DN", ChirpReader::Mode::DN}, }; QHash const ChirpFormat::_toneModeCodes = { {"", ChirpFormat::ToneMode::None}, {"Tone", ChirpFormat::ToneMode::Tone}, {"TSQL", ChirpFormat::ToneMode::TSQL}, {"TSQL-R", ChirpFormat::ToneMode::TSQL_R}, {"DTCS", ChirpFormat::ToneMode::DTCS}, {"DTCS-R", ChirpFormat::ToneMode::DTCS_R}, {"Cross", ChirpFormat::ToneMode::Cross}, }; QHash const ChirpFormat::_crossModes = { {"->Tone", ChirpFormat::CrossMode::NoneTone}, {"->DTCS", ChirpFormat::CrossMode::NoneDTCS}, {"Tone->", ChirpFormat::CrossMode::ToneNone}, {"Tone->Tone", ChirpFormat::CrossMode::ToneTone}, {"Tone->DTCS", ChirpFormat::CrossMode::ToneDTCS}, {"DTCS->", ChirpFormat::CrossMode::DTCSNone}, {"DTCS->Tone", ChirpFormat::CrossMode::DTCSTone}, {"DTCS->DTCS", ChirpFormat::CrossMode::DTCSDTCS}, }; /* ********************************************************************************************* * * Implementation of ChirpReader * ********************************************************************************************* */ bool ChirpReader::read(QTextStream &stream, Config *config, const ErrorStack &err) { // First read header QStringList header; if (! readLine(stream, header, err)) { errMsg(err) << "Cannot read CSV header."; return false; } // Some trivial sanity checks for the header if (0 == header.size()) { errMsg(err) << "Invalid CSV file header: Got empty header."; return false; } if ("Location" != header.at(0)) { errMsg(err) << "Invalid CSV file header: 'Location' is not first column!"; return false; } // check fields foreach (QString field, _mandatoryHeaders) { if (! header.contains(field)) { errMsg(err) << "Mandatory column '" << field << "' is missing."; return false; } } foreach(QString field, header) { if (! _knownHeaders.contains(field)) { logInfo() << "Unknown header field '" << field << "'."; } } int line=2; while (! stream.atEnd()) { QStringList entry; if (! readLine(stream, entry, err)) { errMsg(err) << "In CSV file line " << line << ": Cannot read line."; return false; } if (! processLine(header, entry, config, err)) { errMsg(err) << "In CSV file line " << line << ": Cannot read line."; return false; } line++; } return true; } bool ChirpReader::readLine(QTextStream &stream, QStringList &list, const ErrorStack &err) { Q_UNUSED(err) list.clear(); if (stream.atEnd()) return true; QString token; QChar ch; stream >> ch; bool string = false; while ((! stream.atEnd()) && (QChar('\n') != ch)) { if ((!string) && (QChar(',') == ch)) { list.append(token); token.clear(); } else if ((! string) && (QChar('"') == ch)) { string = true; } else if (string && (QChar('"') == ch)) { string = false; } else { token.append(ch); } stream >> ch; } list.append(token); return true; } bool ChirpReader::processLine(const QStringList &header, const QStringList &line, Config *config, const ErrorStack &err) { if (header.size() != line.size()) { errMsg(err) << "Malformed line. Expected " << header.size() << " entries, got " << line.size() << "."; return false; } bool ok; QString name; Frequency rxFrequency, txFrequency; Duplex duplex = Duplex::None; Mode mode = Mode::FM; ToneMode toneMode = ToneMode::None; CrossMode crossMode; double txTone = 67.0, rxTone = 67.0; int txDTCSCode = 000, rxDTCSCode = 000; Polarity txPol = Polarity::Normal, rxPol = Polarity::Normal; for (int i=1; isetBandwidth(Mode::FM == mode ? FMChannel::Bandwidth::Wide : FMChannel::Bandwidth::Narrow); fm->setName(name); fm->setRXFrequency(rxFrequency); switch (duplex) { case Duplex::None: fm->setTXFrequency(fm->rxFrequency()); break; case Duplex::Off: fm->setTXFrequency(fm->rxFrequency()); fm->setRXOnly(true); break; case Duplex::Split: fm->setTXFrequency(txFrequency); break; case Duplex::Negative: fm->setTXFrequency(Frequency::fromHz(rxFrequency.inHz()-txFrequency.inHz())); break; case Duplex::Positive: fm->setTXFrequency(Frequency::fromHz(rxFrequency.inHz()+txFrequency.inHz())); break; } switch (toneMode) { case ToneMode::None: fm->setTXTone(SelectiveCall()); fm->setRXTone(SelectiveCall()); break; case ToneMode::Tone: fm->setTXTone(SelectiveCall(txTone)); fm->setRXTone(SelectiveCall()); break; case ToneMode::TSQL: fm->setTXTone(SelectiveCall(rxTone)); fm->setRXTone(SelectiveCall(rxTone)); break; case ToneMode::TSQL_R: errMsg(err) << "Reversed CTCSS not supported."; return false; case ToneMode::DTCS: fm->setTXTone(SelectiveCall(txDTCSCode, Polarity::Reversed == txPol)); fm->setRXTone(SelectiveCall(txDTCSCode, Polarity::Reversed == rxPol)); break; case ToneMode::DTCS_R: errMsg(err) << "Reversed DCS not supported."; return false; case ToneMode::Cross: switch (crossMode) { case CrossMode::NoneTone: fm->setTXTone(SelectiveCall()); fm->setRXTone(SelectiveCall(rxTone)); break; case CrossMode::NoneDTCS: fm->setTXTone(SelectiveCall()); fm->setRXTone(SelectiveCall(rxDTCSCode, Polarity::Reversed == rxPol)); break; case CrossMode::ToneNone: fm->setTXTone(SelectiveCall(txTone)); fm->setRXTone(SelectiveCall()); break; case CrossMode::ToneTone: fm->setTXTone(SelectiveCall(txTone)); fm->setRXTone(SelectiveCall(rxTone)); break; case CrossMode::ToneDTCS: fm->setTXTone(SelectiveCall(txTone)); fm->setRXTone(SelectiveCall(rxDTCSCode, Polarity::Reversed == rxPol)); break; case CrossMode::DTCSNone: fm->setTXTone(SelectiveCall(txDTCSCode, Polarity::Reversed == txPol)); fm->setRXTone(SelectiveCall()); break; case CrossMode::DTCSTone: fm->setTXTone(SelectiveCall(txDTCSCode, Polarity::Reversed == txPol)); fm->setRXTone(SelectiveCall(rxTone)); break; case CrossMode::DTCSDTCS: fm->setTXTone(SelectiveCall(txDTCSCode, Polarity::Reversed == txPol)); fm->setRXTone(SelectiveCall(rxDTCSCode, Polarity::Reversed == rxPol)); break; } } config->channelList()->add(fm); return true; } errMsg(err) << "Unhandled channel format."; return false; } bool ChirpReader::processDuplex(const QString &code, Duplex &duplex, const ErrorStack &err) { if (! _duplexCodes.contains(code.simplified())) { errMsg(err) << "Cannot decode duplex '" << code << "': Unknown setting."; return false; } duplex = _duplexCodes[code.simplified()]; return true; } bool ChirpReader::processMode(const QString &code, Mode &mode, const ErrorStack &err) { if (! _modeCodes.contains(code.simplified())) { errMsg(err) << "Cannot decode mode '" << code << "': Unknown setting."; return false; } mode = _modeCodes[code.simplified()]; return true; } bool ChirpReader::processToneMode(const QString &code, ToneMode &mode, const ErrorStack &err) { if (! _toneModeCodes.contains(code.simplified())) { errMsg(err) << "Cannot decode tone mode '" << code << "': Unknown setting."; return false; } mode = _toneModeCodes[code.simplified()]; return true; } bool ChirpReader::processPolarity(const QString &code, Polarity &txPol, Polarity &rxPol, const ErrorStack &err) { if (2 != code.simplified().size()) { errMsg(err) << "Cannot parse polarity code '" << code << "': invalid format."; return false; } QChar tx = code.simplified().at(0), rx = code.simplified().at(1); if ('N' == tx) { txPol = Polarity::Normal; } else if ('R' == tx) { txPol = Polarity::Reversed; } else { errMsg(err) << "Invalid polarity code: '" << tx << "': expected 'N' or 'R'."; return false; } if ('N' == rx) { rxPol = Polarity::Normal; } else if ('R' == rx) { rxPol = Polarity::Reversed; } else { errMsg(err) << "Invalid polarity code: '" << rx << "': expected 'N' or 'R'."; return false; } return true; } bool ChirpReader::processCrossMode(const QString &code, CrossMode &crossMode, const ErrorStack &err) { if (! _crossModes.contains(code.simplified())) { errMsg(err) << "Cannot decode cross-mode '" << code << "': unknown mode."; return false; } crossMode = _crossModes[code.simplified()]; return true; } /* ********************************************************************************************* * * Implementation of ChirpWriter * ********************************************************************************************* */ bool ChirpWriter::write(QTextStream &stream, Config *config, const ErrorStack &err) { if (! writeHeader(stream, err)) { errMsg(err) << "Cannot write CHIRP CSV file."; return false; } for (int i=0, j=0; ichannelList()->count(); i++) { if (! config->channelList()->channel(i)->is()) continue; if (! writeChannel(stream, j, config->channelList()->channel(i)->as(), err)) { errMsg(err) << "Cannot encode FM channel '" << config->channelList()->channel(i)->name() << "'."; return false; } j++; } return true; } bool ChirpWriter::writeHeader(QTextStream &stream, const ErrorStack &err) { Q_UNUSED(err) stream << "Location" << "," << "Name" << "," << "Frequency" << "," << "Duplex" << "," << "Offset" << "," << "Tone" << "," << "rToneFreq" << "," << "cToneFreq" << "," << "DtcsCode" << "," "RxDtcsCode" << "," << "DtcsPolarity" << "," << "CrossMode" << "," << "Mode" << "\n"; return true; } bool ChirpWriter::writeChannel(QTextStream &stream, int i, FMChannel *channel, const ErrorStack &err) { stream << i << "," << '"' << channel->name() << '"'; if (! encodeFrequency(stream, channel, err)) { errMsg(err) << "Cannot encode frequencies of channel '" << channel->name() << "'."; return false; } if (! encodeSubTone(stream, channel, err)) { errMsg(err) << "Cannot encode sub-tone setting for channel '" << channel->name() << "'."; return false; } if (! encodeBandwidth(stream, channel, err)) { errMsg(err) << "Cannot encode sub-tone setting for channel '" << channel->name() << "'."; return false; } stream << "\n"; return true; } bool ChirpWriter::encodeFrequency(QTextStream &stream, FMChannel *channel, const ErrorStack &err) { Q_UNUSED(err) stream << "," << channel->rxFrequency().inMHz(); if (channel->rxOnly()) stream << "," << "Off" << "," << 0.0; else if (channel->rxFrequency() == channel->txFrequency()) stream << "," << "" << "," << 0.0; else if (channel->rxFrequency() > channel->txFrequency()) stream << "," << "-" << "," << channel->rxFrequency().inMHz()-channel->txFrequency().inMHz(); else stream << "," << "+" << "," << channel->txFrequency().inMHz()-channel->rxFrequency().inMHz(); return true; } bool ChirpWriter::encodeSubTone(QTextStream &stream, FMChannel *channel, const ErrorStack &err) { Q_UNUSED(err) // Serializes ", Tone, rToneFreq, cToneFreq, DtcsCode, RxDtcsCode, Polarity, CrossMode" if (channel->txTone().isInvalid()) stream << "," << "" << "," << 67.0 << "," << 67.0 << "," << "023" << "," << "023" << "," << "NN" << "," << "Tone->Tone" ; else if (channel->txTone().isCTCSS() && channel->rxTone().isInvalid()) stream << "," << "Tone" << "," << QString::number(channel->txTone().Hz(), 'f', 1) << "," << 67.0 << "," << "023" << "," << "023" << "," << "NN" << "," << "Tone->Tone"; else if (channel->txTone().isCTCSS() && (channel->txTone() == channel->rxTone())) stream << "," << "TSQL" << "," << 67.0 << "," << QString::number(channel->txTone().Hz(), 'f', 1) << "," << "023" << "," << "023" << "," << "NN" << "," << "Tone->Tone"; else if (channel->txTone().isInvalid() && channel->rxTone().isCTCSS()) stream << "," << "Cross" << "," << 67.0 << "," << QString::number(channel->rxTone().Hz(), 'f', 1) << "," << "023" << "," << "023" << "," << "NN" << "," << "->Tone"; else if (channel->txTone().isInvalid() && channel->rxTone().isDCS()) stream << "," << "Cross" << "," << 67.0 << "," << 67.0 << "," << "023" << "," << channel->rxTone().octalCode() << "," << (channel->rxTone().isInverted() ? "NR" : "NN") << "," << "->DTCS"; else if (channel->txTone().isCTCSS() && channel->rxTone().isCTCSS() && (channel->txTone() != channel->rxTone())) stream << "," << "Cross" << "," << QString::number(channel->txTone().Hz(), 'f', 1) << "," << QString::number(channel->rxTone().Hz(), 'f', 1) << "," << "023" << "," << "023" << "," << "NN" << "," << "Tone->Tone"; else if (channel->txTone().isCTCSS() && channel->rxTone().isDCS()) stream << "," << "Cross" << "," << QString::number(channel->txTone().Hz(), 'f', 1) << "," << 67.0 << "," << "023" << "," << channel->rxTone().octalCode() << "," << (channel->rxTone().isInverted() ? "NR" : "NN") << "," << "Tone->DTCS"; else if (channel->txTone().isDCS() && channel->rxTone().isCTCSS()) stream << "," << "Cross" << "," << 67.0 << "," << QString::number(channel->rxTone().Hz(), 'f', 1) << "," << channel->txTone().octalCode() << "," << "023" << "," << (channel->txTone().isInverted() ? "RN" : "NN") << "," << "DTCS->Tone"; else if (channel->txTone().isDCS() && channel->rxTone().isDCS() && (channel->txTone().binCode() == channel->rxTone().binCode())) stream << "," << "DTCS" << "," << 67.0 << "," << 67.0 << "," << channel->txTone().octalCode() << "," << channel->rxTone().octalCode() << "," << (channel->txTone().isInverted() ? 'R' : 'N') << (channel->rxTone().isInverted() ? 'R' : 'N') << "," << "Tone->Tone"; else if (channel->txTone().isDCS() && channel->rxTone().isDCS()) stream << "," << "Cross" << "," << 67.0 << "," << 67.0 << "," << channel->txTone().octalCode() << "," << channel->rxTone().octalCode() << "," << (channel->txTone().isInverted() ? 'R' : 'N') << (channel->rxTone().isInverted() ? 'R' : 'N') << "," << "DTCS->DTCS"; else stream << "," << "" << "," << 67.0 << "," << 67.0 << "," << "023" << "," << "023" << "," << "NN" << "," << "Tone->Tone" ; return true; } bool ChirpWriter::encodeBandwidth(QTextStream &stream, FMChannel *channel, const ErrorStack &err) { Q_UNUSED(err) if (FMChannel::Bandwidth::Narrow == channel->bandwidth()) stream << "," << "NFM"; else stream << "," << "FM"; return true; } qdmr-0.12.3/lib/chirpformat.hh000066400000000000000000000134001501654372000161460ustar00rootroot00000000000000/** @defgroup chrip Import/Export from/to CHIRP CSV format * @ingroup util * * These classes implement the (partial) import and export of FM channels from and to the CHIRP * CSV format. */ #ifndef CHIRPFORMAT_HH #define CHIRPFORMAT_HH #include "errorstack.hh" #include class QTextStream; class Config; class QStringList; class FMChannel; /** Some common constants for the CIRP reader/writer. * @ingroup chirp */ class ChirpFormat { protected: /** Possible values for the "duplex" column. Specifies the frequency offset direction for the * transmit frequency wrt to the receive frequency. */ enum class Duplex { None, ///< No offset at all. That is, the TX and RX frequencies are equal. Positive, ///< Positive frequency offset, TX above RX frequency. Negative, ///< Negative frequency offset, TX below RX frequency. Split, ///< Explicit transmit frequency. Off ///< No transmit frequency specified. Channel is RX only. }; /** Possible CHIRP channel modes. */ enum class Mode { FM, ///< 25kHz FM (still NBFM). NFM, ///< 12.5kHz FM (also NBFM). WFM, ///< 100kHz FM (WBFM, broadcast). AM, ///< AM, usually airband (not supported by qdmr yet). DV, ///< D-STAR (not supported by qdmr yet). DN ///< SystemFusion (not supported by qdmr yet). }; /** Possible modes for transmitting and processing sub tones. */ enum class ToneMode { None, ///< No transmission of subtones. Tone, ///< Transmission of a CTCSS tone. TSQL, ///< CTCSS tone squelch. TSQL_R, ///< Inverted, CTCSS tone squelch. That is, the squelch opens if a specific CTCSS tone is not received. DTCS, ///< Transmission of a DCS code and also DCS squelch. DTCS_R, ///< Transmission of a DCS code and also DCS squelch. Cross ///< More complex setting (see @c CrossMode). }; /** Polarity of DCS codes. */ enum class Polarity { Normal, ///< Normal DCS encoding. Reversed ///< Reversed DCS encoding. }; /** Generic mode for sub tones. There is no reason to use any other mode. This one covers * everything. */ enum class CrossMode { NoneTone, ///< No TX, RX CTCSS NoneDTCS, ///< No TX, RX DCS ToneNone, ///< TX CTCSS, no RX ToneTone, ///< TX CTCSS, RX CTCSS ToneDTCS, ///< TX CTCSS, RX DCS DTCSNone, ///< TX DCS, no RX DTCSTone, ///< TX DCS, RX CTCSS DTCSDTCS ///< TX DCS, RX DCS }; protected: /** Internal set of mandatory headers. */ static const QSet _mandatoryHeaders; /** Internal used set of known headers. */ static const QSet _knownHeaders; /** Mapping of duplex codes. */ static const QHash _duplexCodes; /** Mapping of mode codes. */ static const QHash _modeCodes; /** Mapping of tone mode codes. */ static const QHash _toneModeCodes; /** Mapping of cross mode codes. */ static const QHash _crossModes; }; /** Implements the CHIRP CSV reader. * @ingroup chirp */ class ChirpReader: public ChirpFormat { public: /** Reads a CHIRP CSV file from the given stream and updates the given configuration. * Please note, that the CHRIP generic CSV does not contain a functional DMR codeplug. */ static bool read(QTextStream &stream, Config *config, const ErrorStack &err=ErrorStack()); protected: /** Internal used method to read a line from the given stream. * This method also implements the proper quotation parsing of strings. */ static bool readLine(QTextStream &stream, QStringList &list, const ErrorStack &err=ErrorStack()); /** Line parser, the header must be read before and passed to this method. The parsed channel is * added to the configuration. */ static bool processLine(const QStringList &header, const QStringList &line, Config *config, const ErrorStack &err=ErrorStack()); /** Helper function to parse a duplex column. */ static bool processDuplex(const QString &code, Duplex &duplex, const ErrorStack &err=ErrorStack()); /** Helper function to parse a mode column. */ static bool processMode(const QString &code, Mode &mode, const ErrorStack &err=ErrorStack()); /** Helper function to parse a tone mode column. */ static bool processToneMode(const QString &code, ToneMode &mode, const ErrorStack &err=ErrorStack()); /** Helper function to parse a polarity column. */ static bool processPolarity(const QString &code, Polarity &txPol, Polarity &rxPol, const ErrorStack &err=ErrorStack()); /** Helper function to parse a cross mode column. */ static bool processCrossMode(const QString &code, CrossMode &crossMode, const ErrorStack &err = ErrorStack()); }; /** Implements the chirp writer. * @ingroup chirp */ class ChirpWriter: public ChirpFormat { public: /** Writes the (FM channels) of the given codeplug as a CHIRP CSV file into the given stream. */ static bool write(QTextStream &stream, Config *config, const ErrorStack &err=ErrorStack()); protected: /** Writes the header into the given stream. */ static bool writeHeader(QTextStream &stream, const ErrorStack &err = ErrorStack()); /** Writes a channel into the given stream. */ static bool writeChannel(QTextStream &stream, int i, FMChannel *channel, const ErrorStack &err=ErrorStack()); /** Writes frequency related columns to the given stream. */ static bool encodeFrequency(QTextStream &stream, FMChannel *channel, const ErrorStack &err = ErrorStack()); /** Wirtes sub tone related columns to the given stream. */ static bool encodeSubTone(QTextStream &stream, FMChannel *channel, const ErrorStack &err = ErrorStack()); /** Writes the bandwidth column to the given stream */ static bool encodeBandwidth(QTextStream &stream, FMChannel *channel, const ErrorStack &err = ErrorStack()); }; #endif // CHIRPFORMAT_HH qdmr-0.12.3/lib/codeplug.cc000066400000000000000000000516651501654372000154370ustar00rootroot00000000000000#include "codeplug.hh" #include "config.hh" #include #include "logger.hh" #include "roamingchannel.hh" #include "configcopyvisitor.hh" /* ********************************************************************************************* * * Implementation of CodePlug::Flags * ********************************************************************************************* */ Codeplug::Flags::Flags() : updateCodePlug(true), autoEnableGPS(false), autoEnableRoaming(false) { // pass... } /* ********************************************************************************************* * * Implementation of CodePlug::Element * ********************************************************************************************* */ Codeplug::Element::Element(uint8_t *ptr, size_t size) : _data(ptr), _size(size) { // pass... } Codeplug::Element::Element(const Element &other) : _data(other._data), _size(other._size) { // pass... } Codeplug::Element::~Element() { // pass... } Codeplug::Element & Codeplug::Element::operator =(const Element &other) { this->_data = other._data; this->_size = other._size; return *this; } bool Codeplug::Element::isValid() const { return nullptr != _data; } void Codeplug::Element::clear() { // pass... } bool Codeplug::Element::fill(uint8_t value, unsigned offset, int size) { if (0 > size) size = _size-offset; if ((offset+size) > _size) { logFatal() << "Cannot fill codeplug element from " << QString::number(offset,16) << " size " << QString::number(size, 16) << ": overflow."; return false; } memset(_data+offset, value, size); return true; } bool Codeplug::Element::getBit(const Offset::Bit &offset) const { return getBit(offset.byte, offset.bit); } bool Codeplug::Element::getBit(unsigned offset, unsigned bit) const { if (offset >= _size) { logFatal() << "Cannot get bit at " << QString::number(offset, 16) << " bit " << bit << ": Overflow."; return false; } uint8_t *ptr = (_data+offset); return (1<= _size) { logFatal() << "Cannot set bit at " << QString::number(offset, 16) << " bit " << bit << ": Overflow."; return; } uint8_t *ptr = (_data+offset); if (value) (*ptr) |= (1<= _size) { logFatal() << "Cannot clear bit at " << QString::number(offset, 16) << " bit " << bit << ": Overflow."; return; } uint8_t *ptr = (_data+offset); (*ptr) &= ~(1<= _size) { logFatal() << "Cannot get uint2 at " << QString::number(offset, 16) << " bit " << bit << ": Overflow."; return 0; } return (((*(_data+offset)) >> bit) & 0b11); } void Codeplug::Element::setUInt2(unsigned offset, unsigned bit, uint8_t value) { if (offset >= _size) { logFatal() << "Cannot set uint2 at " << QString::number(offset, 16) << " bit " << bit << ": Overflow."; return; } *(_data+offset) &= ~(0b11 << bit); *(_data+offset) |= ((value & 0b11)<= _size) { logFatal() << "Cannot get uint3 at " << QString::number(offset, 16) << " bit " << bit << ": Overflow."; return 0; } return (((*(_data+offset)) >> bit) & 0b111); } void Codeplug::Element::setUInt3(unsigned offset, unsigned bit, uint8_t value) { if (offset >= _size) { logFatal() << "Cannot set uint3 at " << QString::number(offset, 16) << " bit " << bit << ": Overflow."; return; } *(_data+offset) &= ~(0b111 << bit); *(_data+offset) |= ((value & 0b111)<= _size) { logFatal() << "Cannot get uint4 at " << QString::number(offset, 16) << " bit " << bit << ": Overflow."; return 0; } return (((*(_data+offset)) >> bit) & 0b1111); } void Codeplug::Element::setUInt4(unsigned offset, unsigned bit, uint8_t value) { if (offset >= _size) { logFatal() << "Cannot set uint4 at " << QString::number(offset, 16) << " bit " << bit << ": Overflow."; return; } *(_data+offset) &= ~(0b1111 << bit); *(_data+offset) |= ((value & 0b1111)<= _size) { logFatal() << "Cannot get uint5 at " << QString::number(offset, 16) << " bit " << bit << ": Overflow."; return 0; } return (((*(_data+offset)) >> bit) & 0b11111); } void Codeplug::Element::setUInt5(unsigned offset, unsigned bit, uint8_t value) { if (offset >= _size) { logFatal() << "Cannot get uint5 at " << QString::number(offset, 16) << " bit " << bit << ": Overflow."; return; } *(_data+offset) &= ~(0b11111 << bit); *(_data+offset) |= ((value & 0b11111)<= _size) { logFatal() << "Cannot get uint6 at " << QString::number(offset, 16) << " bit " << bit << ": Overflow."; return 0; } return (((*(_data+offset)) >> bit) & 0b111111); } void Codeplug::Element::setUInt6(unsigned offset, unsigned bit, uint8_t value) { if (offset >= _size) { logFatal() << "Cannot set uint6 at " << QString::number(offset, 16) << " bit " << bit << ": Overflow."; return; } *(_data+offset) &= ~(0b111111 << bit); *(_data+offset) |= ((value & 0b111111)<= _size) { logFatal() << "Cannot get uint8 at " << QString::number(offset, 16) << ": Overflow."; return 0; } return _data[offset]; } void Codeplug::Element::setUInt8(unsigned offset, uint8_t value) { if (offset >= _size) { logFatal() << "Cannot set uint8 at " << QString::number(offset, 16) << ": Overflow."; return; } _data[offset] = value; } int8_t Codeplug::Element::getInt8(unsigned offset) const { if (offset >= _size) { logFatal() << "Cannot get int8 at " << QString::number(offset, 16) << ": Overflow."; return 0; } return ((int8_t *)_data)[offset]; } void Codeplug::Element::setInt8(unsigned offset, int8_t value) { if (offset >= _size) { logFatal() << "Cannot set int8 at " << QString::number(offset, 16) << ": Overflow."; return; } ((int8_t *)_data)[offset] = value; } uint16_t Codeplug::Element::getUInt16_be(unsigned offset) const { if ((offset+2) > _size) { logFatal() << "Cannot get int16 (be) at " << QString::number(offset, 16) << ": Overflow."; return 0; } uint16_t *ptr = (uint16_t *)(_data+offset); return qFromBigEndian(*ptr); } uint16_t Codeplug::Element::getUInt16_le(unsigned offset) const { if ((offset+2) > _size) { logFatal() << "Cannot get int16 (le) at " << QString::number(offset, 16) << ": Overflow."; return 0; } quint16 *ptr = (quint16 *)(_data+offset); return qFromLittleEndian(*ptr); } void Codeplug::Element::setUInt16_be(unsigned offset, uint16_t value) { if ((offset+2) > _size) { logFatal() << "Cannot set int16 (be) at " << QString::number(offset, 16) << ": Overflow."; return; } quint16 *ptr = (quint16 *)(_data+offset); (*ptr) = qToBigEndian((quint16)value); } void Codeplug::Element::setUInt16_le(unsigned offset, uint16_t value) { if ((offset+2) > _size) { logFatal() << "Cannot set int16 (le) at " << QString::number(offset, 16) << ": Overflow."; return; } quint16 *ptr = (quint16 *)(_data+offset); (*ptr) = qToLittleEndian(value); } uint32_t Codeplug::Element::getUInt24_be(unsigned offset) const { if ((offset+3) > _size) { logFatal() << "Cannot get int24 (be) at " << QString::number(offset, 16) << ": Overflow."; return 0; } uint8_t *ptr = _data+offset; return uint32_t(ptr[2]) + (uint32_t(ptr[1])<<8) + (uint32_t(ptr[0])<<16); } uint32_t Codeplug::Element::getUInt24_le(unsigned offset) const { if ((offset+3) > _size) { logFatal() << "Cannot get int24 (le) at " << QString::number(offset, 16) << ": Overflow."; return 0; } uint8_t *ptr = _data+offset; return uint32_t(ptr[0]) + (uint32_t(ptr[1])<<8) + (uint32_t(ptr[2])<<16); } void Codeplug::Element::setUInt24_be(unsigned offset, uint32_t value) { if ((offset+3) > _size) { logFatal() << "Cannot set int24 (be) at " << QString::number(offset, 16) << ": Overflow."; return; } uint8_t *ptr = _data+offset; ptr[0] = ((value >> 16) & 0xff); ptr[1] = ((value >> 8) & 0xff); ptr[2] = ((value >> 0) & 0xff); } void Codeplug::Element::setUInt24_le(unsigned offset, uint32_t value) { if ((offset+3) > _size) { logFatal() << "Cannot set int24 (le) at " << QString::number(offset, 16) << ": Overflow."; return; } uint8_t *ptr = _data+offset; ptr[0] = ((value >> 0) & 0xff); ptr[1] = ((value >> 8) & 0xff); ptr[2] = ((value >> 16) & 0xff); } uint32_t Codeplug::Element::getUInt32_be(unsigned offset) const { if ((offset+4) > _size) { logFatal() << "Cannot get int32 (be) at " << QString::number(offset, 16) << ": Overflow."; return 0; } uint32_t *ptr = (uint32_t *)(_data+offset); return qFromBigEndian(*ptr); } uint32_t Codeplug::Element::getUInt32_le(unsigned offset) const { if ((offset+4) > _size) { logFatal() << "Cannot get int32 (le) at " << QString::number(offset, 16) << ": Overflow."; return 0; } uint32_t *ptr = (uint32_t *)(_data+offset); return qFromLittleEndian(*ptr); } void Codeplug::Element::setUInt32_be(unsigned offset, uint32_t value) { if ((offset+4) > _size) { logFatal() << "Cannot set int32 (be) at " << QString::number(offset, 16) << ": Overflow."; return; } uint32_t *ptr = (uint32_t *)(_data+offset); (*ptr) = qToBigEndian(value); } void Codeplug::Element::setUInt32_le(unsigned offset, uint32_t value) { if ((offset+4) > _size) { logFatal() << "Cannot set int32 (le) at " << QString::number(offset, 16) << ": Overflow."; return; } uint32_t *ptr = (uint32_t *)(_data+offset); (*ptr) = qToLittleEndian(value); } uint64_t Codeplug::Element::getUInt64_be(unsigned offset) const { if ((offset+8) > _size) { logFatal() << "Cannot get int64 (be) at " << QString::number(offset, 16) << ": Overflow."; return 0; } quint64 *ptr = (quint64 *)(_data+offset); return qFromBigEndian(*ptr); } uint64_t Codeplug::Element::getUInt64_le(unsigned offset) const { if ((offset+8) > _size) { logFatal() << "Cannot get int64 (le) at " << QString::number(offset, 16) << ": Overflow."; return 0; } quint64 *ptr = (quint64 *)(_data+offset); return qFromLittleEndian(*ptr); } void Codeplug::Element::setUInt64_be(unsigned offset, uint64_t value) { if ((offset+8) > _size) { logFatal() << "Cannot set int64 (be) at " << QString::number(offset, 16) << ": Overflow."; return; } quint64 *ptr = (quint64 *)(_data+offset); (*ptr) = qToBigEndian(value); } void Codeplug::Element::setUInt64_le(unsigned offset, uint64_t value) { if ((offset+8) > _size) { logFatal() << "Cannot set int64 (le) at " << QString::number(offset, 16) << ": Overflow."; return; } quint64 *ptr = (quint64 *)(_data+offset); (*ptr) = qToLittleEndian(value); } uint8_t Codeplug::Element::getBCD2(unsigned offset) const { if ((offset+1) > _size) { logFatal() << "Cannot get BCD2 at " << QString::number(offset, 16) << ": Overflow."; return 0; } uint8_t val = getUInt8(offset); return (val & 0xf) + ((val>>4) & 0xf)*10; } void Codeplug::Element::setBCD2(unsigned offset, uint8_t val) { if ((offset+1) > _size) { logFatal() << "Cannot get BCD2 at " << QString::number(offset, 16) << ": Overflow."; return; } uint8_t a = (val / 10) % 10; uint8_t b = (val / 1) % 10; setUInt8(offset, (a << 4) + b); } uint16_t Codeplug::Element::getBCD4_be(unsigned offset) const { if ((offset+2) > _size) { logFatal() << "Cannot get BCD4 (be) at " << QString::number(offset, 16) << ": Overflow."; return 0; } uint32_t val = getUInt16_be(offset); return (val & 0xf) + ((val>>4) & 0xf)*10 + ((val>>8) & 0xf)*100 + ((val>>12) & 0xf)*1000; } void Codeplug::Element::setBCD4_be(unsigned offset, uint16_t val) { if ((offset+2) > _size) { logFatal() << "Cannot set BCD4 (be) at " << QString::number(offset, 16) << ": Overflow."; return; } uint32_t a = (val / 1000) % 10; uint32_t b = (val / 100) % 10; uint32_t c = (val / 10) % 10; uint32_t d = (val / 1) % 10; setUInt16_be(offset, (a << 12) + (b << 8) + (c << 4) + d); } uint16_t Codeplug::Element::getBCD4_le(unsigned offset) const { if ((offset+2) > _size) { logFatal() << "Cannot get BCD4 (le) at " << QString::number(offset, 16) << ": Overflow."; return 0; } uint32_t val = getUInt16_le(offset); return (val & 0xf) + ((val>>4) & 0xf)*10 + ((val>>8) & 0xf)*100 + ((val>>12) & 0xf)*1000; } void Codeplug::Element::setBCD4_le(unsigned offset, uint16_t val) { if ((offset+2) > _size) { logFatal() << "Cannot set BCD4 (le) at " << QString::number(offset, 16) << ": Overflow."; return; } uint32_t a = (val / 1000) % 10; uint32_t b = (val / 100) % 10; uint32_t c = (val / 10) % 10; uint32_t d = (val / 1) % 10; setUInt16_le(offset, (a << 12) + (b << 8) + (c << 4) + d); } uint32_t Codeplug::Element::getBCD8_be(unsigned offset) const { if ((offset+4) > _size) { logFatal() << "Cannot get BCD8 (be) at " << QString::number(offset, 16) << ": Overflow."; return 0; } uint32_t val = getUInt32_be(offset); return (val & 0xf) + ((val>>4) & 0xf)*10 + ((val>>8) & 0xf)*100 + ((val>>12) & 0xf)*1000 + ((val>>16) & 0xf)*10000 + ((val>>20) & 0xf)*100000 + ((val>>24) & 0xf)*1000000 + ((val>>28) & 0xf)*10000000; } void Codeplug::Element::setBCD8_be(unsigned offset, uint32_t val) { if ((offset+4) > _size) { logFatal() << "Cannot set BCD8 (be) at " << QString::number(offset, 16) << ": Overflow."; return; } uint32_t a = (val / 10000000) % 10; uint32_t b = (val / 1000000) % 10; uint32_t c = (val / 100000) % 10; uint32_t d = (val / 10000) % 10; uint32_t e = (val / 1000) % 10; uint32_t f = (val / 100) % 10; uint32_t g = (val / 10) % 10; uint32_t h = (val / 1) % 10; setUInt32_be(offset, (a << 28) + (b << 24) + (c << 20) + (d << 16) + (e << 12) + (f << 8) + (g << 4) + h); } uint32_t Codeplug::Element::getBCD8_le(unsigned offset) const { if ((offset+4) > _size) { logFatal() << "Cannot get BCD8 (le) at " << QString::number(offset, 16) << ": Overflow."; return 0; } uint32_t val = getUInt32_le(offset); return (val & 0xf) + ((val>>4) & 0xf)*10 + ((val>>8) & 0xf)*100 + ((val>>12) & 0xf)*1000 + ((val>>16) & 0xf)*10000 + ((val>>20) & 0xf)*100000 + ((val>>24) & 0xf)*1000000 + ((val>>28) & 0xf)*10000000; } void Codeplug::Element::setBCD8_le(unsigned offset, uint32_t val) { if ((offset+4) > _size) { logFatal() << "Cannot set BCD8 (le) at " << QString::number(offset, 16) << ": Overflow."; return; } uint32_t a = (val / 10000000) % 10; uint32_t b = (val / 1000000) % 10; uint32_t c = (val / 100000) % 10; uint32_t d = (val / 10000) % 10; uint32_t e = (val / 1000) % 10; uint32_t f = (val / 100) % 10; uint32_t g = (val / 10) % 10; uint32_t h = (val / 1) % 10; setUInt32_le(offset, (a << 28) + (b << 24) + (c << 20) + (d << 16) + (e << 12) + (f << 8) + (g << 4) + h); } QString Codeplug::Element::readASCII(unsigned offset, unsigned maxlen, uint8_t eos) const { QString txt; uint8_t *ptr = (uint8_t *)(_data+offset); for (unsigned i=0; (iclassName())) return true; else if (obj->superClass()) return hasTable(obj->superClass()); return false; } Codeplug::Context::Table & Codeplug::Context::getTable(const QMetaObject *obj) { if (_tables.contains(obj->className())) return _tables[obj->className()]; return getTable(obj->superClass()); } bool Codeplug::Context::addTable(const QMetaObject *obj) { if (hasTable(obj)) return false; _tables.insert(obj->className(), Table()); return true; } ConfigItem * Codeplug::Context::obj(const QMetaObject *elementType, unsigned idx) { if (! hasTable(elementType)) return nullptr; return getTable(elementType).objects.value(idx, nullptr); } int Codeplug::Context::index(ConfigItem *obj) { if (nullptr == obj) return -1; if (! hasTable(obj->metaObject())) return -1; return getTable(obj->metaObject()).indices.value(obj, -1); } bool Codeplug::Context::add(ConfigItem *obj, unsigned idx) { if (!hasTable(obj->metaObject())) return false; if (! getTable(obj->metaObject()).indices.contains(obj)) getTable(obj->metaObject()).indices.insert(obj, idx); if (! getTable(obj->metaObject()).objects.contains(idx)) getTable(obj->metaObject()).objects.insert(idx, obj); return true; } /* ********************************************************************************************* * * Implementation of CodePlug * ********************************************************************************************* */ Codeplug::Codeplug(QObject *parent) : DFUFile(parent) { // pass... } Codeplug::~Codeplug() { // pass... } Config * Codeplug::preprocess(Config *config, const ErrorStack &err) const { return ConfigCopy::copy(config, err)->as(); } bool Codeplug::postprocess(Config *config, const ErrorStack &err) const { Q_UNUSED(config); Q_UNUSED(err); return true; } qdmr-0.12.3/lib/codeplug.hh000066400000000000000000000404101501654372000154330ustar00rootroot00000000000000#ifndef CODEPLUG_HH #define CODEPLUG_HH #include #include #include "dfufile.hh" //#include "userdatabase.hh" //#include "config.hh" class Config; class ConfigItem; class SatelliteDatabase; /** This class defines the interface all device-specific code-plugs must implement. * Device-specific codeplugs are derived from the common configuration and implement the * construction/parsing of the device specific binary configuration. */ class Codeplug: public DFUFile { Q_OBJECT public: /** Certain flags passed to CodePlug::encode to control the transfer and encoding of the * codeplug. */ class Flags { public: /** If @c true, the codeplug will first be downloaded from the device, updated from the * abstract config and then written back to the device. This maintains the user-settings * made within the device or manufacturer CPS. If @c false, the code-plug gets overridden * entirely using some default settings. Default @c true. */ bool updateCodePlug; /** If @c true enables GPS when there is a GPS or APRS system defined that is used by any * channel. This may cause automatic transmissions, hence the default is @c false. */ bool autoEnableGPS; /** If @c true enables automatic roaming when there is a roaming zone defined that is used by any * channel. This may cause automatic transmissions, hence the default is @c false. */ bool autoEnableRoaming; /** Default constructor, enables code-plug update and disables automatic GPS/APRS and roaming. */ Flags(); }; /** Represents the abstract base class of all codeplug elements. * * That is a memory region within the codeplug that encodes a specific element. E.g., channels, * contacts, zones, etc. This class provides some helper methods to access specific members of * the element. * * @since 0.9.0 */ class Element { protected: /** Base class for Offsets. */ struct Offset { /** Some type to specify a bit offset. That is the byte-offset and bit of that byte. */ struct Bit { /** The byte offset. */ const unsigned int byte; /** The bit within the byte. */ const unsigned int bit; /** Implements a simple increment. */ inline Bit operator+ (unsigned int bits) const { unsigned int tmp = 8 * byte + (7-bit) + bits; return {tmp/8, (7 - (tmp % 8))}; } /** Implements a simple increment. */ inline Bit operator- (unsigned int bits) const { unsigned int tmp = 8 * byte + (7-bit) - bits; return {tmp/8, (7 - (tmp % 8))}; } }; }; public: /** Base class for Limits. */ struct Limit { /** Holds a range of values [min, max]. */ template struct Range { /// Lower bound. const T min; /// Upper bound. const T max; /// Limits the value to the range. inline T limit(const T &value) const { return std::min(max, std::max(min, value)); } /// Checks if value is in range inline bool in(const T &value) const { return (value <= max) && (value >= min); } }; }; protected: /** Hidden constructor. * @param ptr Specifies the pointer to the element within the codeplug. * @param size Specifies the size of the element in bytes. */ Element(uint8_t *ptr, size_t size); public: /** Copy constructor. */ Element(const Element &other); /** Destructor. */ virtual ~Element(); /** Copy assignment. */ Element &operator=(const Element &other); /** Returns @c true if the pointer is not null. */ virtual bool isValid() const; /** Abstract method to reset the element within the codeplug. Any device specific element * should implement this method. */ virtual void clear(); /** Fills the memsets the entire element to the given value. */ bool fill(uint8_t value, unsigned offset=0, int size=-1); /** Reads a specific bit at the given byte-offset. */ bool getBit(const Offset::Bit &offset) const; /** Reads a specific bit at the given byte-offset. */ bool getBit(unsigned offset, unsigned bit) const; /** Sets a specific bit at the given byte-offset. */ void setBit(const Offset::Bit &offset, bool value=true); /** Sets a specific bit at the given byte-offset. */ void setBit(unsigned offset, unsigned bit, bool value=true); /** Clears a specific bit at the given byte-offset. */ void clearBit(unsigned offset, unsigned bit); /** Clears a specific bit. */ void clearBit(const Offset::Bit &offset); /** Reads a 2bit unsigned integer at the given bit-offset. */ uint8_t getUInt2(const Offset::Bit &offset) const; /** Reads a 2bit unsigned integer at the given byte- and bit-offset. */ uint8_t getUInt2(unsigned offset, unsigned bit) const; /** Stores a 2bit unsigned integer at the given bit-offset. */ void setUInt2(const Offset::Bit &offset, uint8_t value); /** Stores a 2bit unsigned integer at the given byte- and bit-offset. */ void setUInt2(unsigned offset, unsigned bit, uint8_t value); /** Reads a 3bit unsigned integer at the given bit-offset. */ uint8_t getUInt3(const Offset::Bit &offset) const; /** Reads a 3bit unsigned integer at the given byte- and bit-offset. */ uint8_t getUInt3(unsigned offset, unsigned bit) const; /** Stores a 3bit unsigned integer at the given bit-offset. */ void setUInt3(const Offset::Bit &offset, uint8_t value); /** Stores a 3bit unsigned integer at the given byte- and bit-offset. */ void setUInt3(unsigned offset, unsigned bit, uint8_t value); /** Reads a 4bit unsigned integer at the given bit-offset. */ uint8_t getUInt4(const Offset::Bit &offset) const; /** Reads a 4bit unsigned integer at the given byte- and bit-offset. */ uint8_t getUInt4(unsigned offset, unsigned bit) const; /** Stores a 4bit unsigned integer at the given bit-offset. */ void setUInt4(const Offset::Bit &offset, uint8_t value); /** Stores a 4bit unsigned integer at the given byte- and bit-offset. */ void setUInt4(unsigned offset, unsigned bit, uint8_t value); /** Reads a 5bit unsigned integer at the given byte- and bit-offset. */ uint8_t getUInt5(const Offset::Bit &offset) const; /** Reads a 5bit unsigned integer at the given byte- and bit-offset. */ uint8_t getUInt5(unsigned offset, unsigned bit) const; /** Stores a 5bit iunsinged nteger at the given byte- and bit-offset. */ void setUInt5(const Offset::Bit &offset, uint8_t value); /** Stores a 5bit iunsinged nteger at the given byte- and bit-offset. */ void setUInt5(unsigned offset, unsigned bit, uint8_t value); /** Reads a 6bit unsigned integer at the given byte- and bit-offset. */ uint8_t getUInt6(const Offset::Bit &offset) const; /** Reads a 6bit unsigned integer at the given byte- and bit-offset. */ uint8_t getUInt6(unsigned offset, unsigned bit) const; /** Stores a 6bit unsigned integer at the given byte- and bit-offset. */ void setUInt6(const Offset::Bit &offset, uint8_t value); /** Stores a 6bit unsigned integer at the given byte- and bit-offset. */ void setUInt6(unsigned offset, unsigned bit, uint8_t value); /** Reads a 8bit unsigned integer at the given byte- and bit-offset. */ uint8_t getUInt8(unsigned offset) const; /** Reads a 8bit unsigned integer at the given byte- and bit-offset. */ void setUInt8(unsigned offset, uint8_t value); /** Reads a 8bit signed integer at the given byte- and bit-offset. */ int8_t getInt8(unsigned offset) const; /** Reads a 8bit signed integer at the given byte- and bit-offset. */ void setInt8(unsigned offset, int8_t value); /** Reads a 16bit big-endian unsigned integer at the given byte-offset. */ uint16_t getUInt16_be(unsigned offset) const; /** Reads a 16bit little-endian unsigned integer at the given byte-offset. */ uint16_t getUInt16_le(unsigned offset) const; /** Stores a 16bit big-endian unsigned integer at the given byte-offset. */ void setUInt16_be(unsigned offset, uint16_t value); /** Stores a 16bit little-endian unsigned integer at the given byte-offset. */ void setUInt16_le(unsigned offset, uint16_t value); /** Reads a 24bit big-endian unsigned integer at the given byte-offset. */ uint32_t getUInt24_be(unsigned offset) const; /** Reads a 24bit little-endian unsigned integer at the given byte-offset. */ uint32_t getUInt24_le(unsigned offset) const; /** Stores a 24bit big-endian unsigned integer at the given byte-offset. */ void setUInt24_be(unsigned offset, uint32_t value); /** Stores a 24bit little-endian unsigned integer at the given byte-offset. */ void setUInt24_le(unsigned offset, uint32_t value); /** Reads a 32bit big-endian unsigned integer at the given byte-offset. */ uint32_t getUInt32_be(unsigned offset) const; /** Reads a 32bit little-endian unsigned integer at the given byte-offset. */ uint32_t getUInt32_le(unsigned offset) const; /** Stores a 32bit big-endian unsigned integer at the given byte-offset. */ void setUInt32_be(unsigned offset, uint32_t value); /** Stores a 32bit little-endian unsigned integer at the given byte-offset. */ void setUInt32_le(unsigned offset, uint32_t value); /** Reads a 64bit big-endian unsigned integer at the given byte-offset. */ uint64_t getUInt64_be(unsigned offset) const; /** Reads a 64bit little-endian unsigned integer at the given byte-offset. */ uint64_t getUInt64_le(unsigned offset) const; /** Stores a 64bit big-endian unsigned integer at the given byte-offset. */ void setUInt64_be(unsigned offset, uint64_t value); /** Stores a 64bit little-endian unsigned integer at the given byte-offset. */ void setUInt64_le(unsigned offset, uint64_t value); /** Reads a 2-digit (1-byte/8bit) BDC value in big-endian at the given byte-offset. */ uint8_t getBCD2(unsigned offset) const; /** Stores a 2-digit (1-byte/8bit) BDC value in big-endian at the given byte-offset. */ void setBCD2(unsigned offset, uint8_t value); /** Reads a 4-digit (2-byte/16bit) BDC value in big-endian at the given byte-offset. */ uint16_t getBCD4_be(unsigned offset) const; /** Stores a 4-digit (2-byte/16bit) BDC value in big-endian at the given byte-offset. */ void setBCD4_be(unsigned offset, uint16_t value); /** Reads a 4-digit (2-byte/16bit) BDC value in little-endian at the given byte-offset. */ uint16_t getBCD4_le(unsigned offset) const; /** Stores a 4-digit (1-byte/16bit) BDC value in little-endian at the given byte-offset. */ void setBCD4_le(unsigned offset, uint16_t value); /** Reads a 8-digit (4-byte/32bit) BDC value in big-endian at the given byte-offset. */ uint32_t getBCD8_be(unsigned offset) const; /** Stores a 8-digit (4-byte/32bit) BDC value in big-endian at the given byte-offset. */ void setBCD8_be(unsigned offset, uint32_t value); /** Reads a 8-digit (4-byte/32bit) BDC value in little-endian at the given byte-offset. */ uint32_t getBCD8_le(unsigned offset) const; /** Stores a 8-digit (4-byte/32bit) BDC value in little-endian at the given byte-offset. */ void setBCD8_le(unsigned offset, uint32_t value); /** Reads up to @c maxlen ASCII chars at the given byte-offset using @c eos as the string termination char. */ QString readASCII(unsigned offset, unsigned maxlen, uint8_t eos=0x00) const; /** Stores up to @c maxlen ASCII chars at the given byte-offset using @c eos as the string termination char. * The stored string gets padded with @c eos to @c maxlen. */ void writeASCII(unsigned offset, const QString &txt, unsigned maxlen, uint8_t eos=0x00); /** Reads up to @c maxlen unicode chars at the given byte-offset using @c eos as the string termination char. */ QString readUnicode(unsigned offset, unsigned maxlen, uint16_t eos=0x0000) const; /** Stores up to @c maxlen unicode chars at the given byte-offset using @c eos as the string termination char. * The stored string gets padded with @c eos to @c maxlen. */ void writeUnicode(unsigned offset, const QString &txt, unsigned maxlen, uint16_t eos=0x0000); protected: /** Holds the pointer to the element. */ uint8_t *_data; /** Holds the size of the element. */ size_t _size; }; /** Base class for all codeplug contexts. * Each device specific codeplug may extend this class to allow for device specific elements to * be indexed in a separate index. By default tables for @c DigitalContact, @c RXGroupList, * @c Channel, @c Zone and @c ScanList are defined. For any other type, an additional table must * be defined first using @c addTable. * @since 0.9.0 */ class Context { public: /** Empty constructor. */ explicit Context(Config *config); /** Returns the reference to the config object. */ Config *config() const; /** Returns a reference to the satellite database. */ SatelliteDatabase *satellites() const; /** Resolves the given index for the specifies element type. * @returns @c nullptr if the index is not defined or the type is unknown. */ ConfigItem *obj(const QMetaObject *elementType, unsigned idx); /** Returns the index for the given object. * @returns -1 if no index is associated with the object or its type is unknown. */ int index(ConfigItem *obj); /** Associates the given object with the given index. */ bool add(ConfigItem *obj, unsigned idx); /** Adds a table for the given type. */ bool addTable(const QMetaObject *obj); /** Returns @c true if a table is defined for the given type. */ bool hasTable(const QMetaObject *obj) const; /** Returns the object associated by the given index and type. */ template T* get(unsigned idx) { return this->obj(&(T::staticMetaObject), idx)->template as(); } /** Returns @c true, if the given index is defined for the specified type. */ template bool has(unsigned idx) { return nullptr != this->obj(&(T::staticMetaObject), idx)->template as(); } /** Returns the number of elements for the specified type. */ template unsigned int count() { return getTable(&T::staticMetaObject).indices.size(); } protected: /** Internal used table type to associate objects and indices. */ class Table { public: /** The index->object map. */ QHash objects; /** The object->index map. */ QHash indices; }; protected: /** Returns a reference to the table for the given type. */ Table &getTable(const QMetaObject *obj); protected: /** A weak reference to the config object. */ Config *_config; /** A weak reference to the satellite database. */ SatelliteDatabase *_satellites; /** Table of tables. */ QHash _tables; }; protected: /** Hidden default constructor. */ explicit Codeplug(QObject *parent=nullptr); public: /** Destructor. */ virtual ~Codeplug(); /** Indexes all elements of the codeplug. * This method must be implemented by any device or vendor specific codeplug to map config * objects to indices used within the binary codeplug to address each element (e.g., channels, * contacts etc.). */ virtual bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const = 0; /** Decodes a binary codeplug to the given abstract configuration @c config. * This must be implemented by the device-specific codeplug. */ virtual bool decode(Config *config, const ErrorStack &err=ErrorStack()) = 0; /** Retruns a post-processed configuration of the decoded config. By default, the passed * config is returned. */ virtual bool postprocess(Config *config, const ErrorStack &err=ErrorStack()) const; /** Retruns a prepared configuration for this particular radio. All unsupported featrues are * removed from the copy. The default implementation only copies the config. */ virtual Config *preprocess(Config *config, const ErrorStack &err=ErrorStack()) const; /** Encodes a given abstract configuration (@c config) to the device specific binary code-plug. * This must be implemented by the device-specific codeplug. */ virtual bool encode(Config *config, const Flags &flags=Flags(), const ErrorStack &err=ErrorStack()) = 0; }; #endif // CODEPLUG_HH qdmr-0.12.3/lib/commercial_extension.cc000066400000000000000000000036311501654372000200320ustar00rootroot00000000000000#include "commercial_extension.hh" #include "encryptionextension.hh" /* ********************************************************************************************* * * Implementation of CommercialExtension * ********************************************************************************************* */ CommercialExtension::CommercialExtension(QObject *parent) : ConfigExtension(parent), _encryptionKeys(new EncryptionKeys(this)) { // pass... } ConfigItem * CommercialExtension::clone() const { CommercialExtension *ext = new CommercialExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } EncryptionKeys * CommercialExtension::encryptionKeys() const { return _encryptionKeys; } /* ********************************************************************************************* * * Implementation of CommercialChannelExtension * ********************************************************************************************* */ CommercialChannelExtension::CommercialChannelExtension(QObject *parent) : ConfigExtension(parent), _encryptionKey() { // pass... } ConfigItem * CommercialChannelExtension::clone() const { CommercialChannelExtension *ex = new CommercialChannelExtension(); if (! ex->copy(*this)) { ex->deleteLater(); return nullptr; } return ex; } EncryptionKeyReference * CommercialChannelExtension::encryptionKeyRef() { return &_encryptionKey; } void CommercialChannelExtension::setEncryptionKeyRef(EncryptionKeyReference *ref) { if (nullptr == ref) setEncryptionKey(nullptr); else setEncryptionKey(ref->as()); } EncryptionKey * CommercialChannelExtension::encryptionKey() { return _encryptionKey.as(); } void CommercialChannelExtension::setEncryptionKey(EncryptionKey *key) { if (_encryptionKey.as() == key) return; _encryptionKey.set(key); emit modified(this); } qdmr-0.12.3/lib/commercial_extension.hh000066400000000000000000000034511501654372000200440ustar00rootroot00000000000000#ifndef COMMERCIALEXTENSION_HH #define COMMERCIALEXTENSION_HH #include "configobject.hh" #include "configreference.hh" #include "encryptionextension.hh" /** Implements the generic extension for the codeplug to represent some commercial features of DMR. * @ingroup conf */ class CommercialExtension: public ConfigExtension { Q_OBJECT /** Read only property returning holding the list of encryption keys. */ Q_PROPERTY(EncryptionKeys* encryptionKeys READ encryptionKeys) public: /** Default constructor. */ Q_INVOKABLE explicit CommercialExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the list of encryption keys. */ EncryptionKeys *encryptionKeys() const; protected: /** Owns the instance of the encryption key list. */ EncryptionKeys *_encryptionKeys; }; /** Implements the generic extension for all channels configuring commercial features of DMR. * @ingroup conf */ class CommercialChannelExtension: public ConfigExtension { Q_OBJECT /** Holds a reference to the associated encryption key. */ Q_PROPERTY(EncryptionKeyReference* encryptionKey READ encryptionKeyRef WRITE setEncryptionKeyRef) public: /** Empty constructor. */ Q_INVOKABLE explicit CommercialChannelExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the reference to the encryption key. */ EncryptionKeyReference *encryptionKeyRef(); /** Sets the reference to the encryption key. */ void setEncryptionKeyRef(EncryptionKeyReference *ref); /** Returns the referenced encryption key. */ EncryptionKey *encryptionKey(); /** References the given encryption key. */ void setEncryptionKey(EncryptionKey *key); protected: /** The actual reference to the the encryption key. */ EncryptionKeyReference _encryptionKey; }; #endif // COMMERCIALEXTENSION_HH qdmr-0.12.3/lib/config.cc000066400000000000000000000325331501654372000150730ustar00rootroot00000000000000#include "config.hh" #include "config.h" #include "rxgrouplist.hh" #include "channel.hh" #include "csvreader.hh" #include "userdatabase.hh" #include "logger.hh" #include #include #include #include #include /* ********************************************************************************************* * * Implementation of Config * ********************************************************************************************* */ Config::Config(QObject *parent) : ConfigItem(parent), _modified(false), _settings(new RadioSettings(this)), _radioIDs(new RadioIDList(this)), _contacts(new ContactList(this)), _rxGroupLists(new RXGroupLists(this)), _channels(new ChannelList(this)), _zones(new ZoneList(this)), _scanlists(new ScanLists(this)), _gpsSystems(new PositioningSystems(this)), _roamingChannels(new RoamingChannelList(this)), _roamingZones(new RoamingZoneList(this)), _tytExtension(nullptr), _commercialExtension(new CommercialExtension(this)), _smsExtension(new SMSExtension(this)) { connect(_settings, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModified())); connect(_radioIDs, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified())); connect(_radioIDs, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified())); connect(_radioIDs, SIGNAL(elementModified(int)), this, SLOT(onConfigModified())); connect(_contacts, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified())); connect(_contacts, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified())); connect(_contacts, SIGNAL(elementModified(int)), this, SLOT(onConfigModified())); connect(_rxGroupLists, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified())); connect(_rxGroupLists, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified())); connect(_rxGroupLists, SIGNAL(elementModified(int)), this, SLOT(onConfigModified())); connect(_channels, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified())); connect(_channels, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified())); connect(_channels, SIGNAL(elementModified(int)), this, SLOT(onConfigModified())); connect(_zones, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified())); connect(_zones, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified())); connect(_zones, SIGNAL(elementModified(int)), this, SLOT(onConfigModified())); connect(_scanlists, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified())); connect(_scanlists, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified())); connect(_scanlists, SIGNAL(elementModified(int)), this, SLOT(onConfigModified())); connect(_gpsSystems, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified())); connect(_gpsSystems, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified())); connect(_gpsSystems, SIGNAL(elementModified(int)), this, SLOT(onConfigModified())); connect(_roamingChannels, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified())); connect(_roamingChannels, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified())); connect(_roamingChannels, SIGNAL(elementModified(int)), this, SLOT(onConfigModified())); connect(_roamingZones, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified())); connect(_roamingZones, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified())); connect(_roamingZones, SIGNAL(elementModified(int)), this, SLOT(onConfigModified())); connect(_commercialExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModified())); connect(_smsExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModified())); } bool Config::copy(const ConfigItem &other) { const Config *conf = other.as(); if ((nullptr==conf) || (! ConfigItem::copy(other))) return false; _settings->copy(*conf->settings()); _radioIDs->copy(*conf->radioIDs()); _contacts->copy(*conf->contacts()); _rxGroupLists->copy(*conf->rxGroupLists()); _channels->copy(*conf->channelList()); _zones->copy(*conf->zones()); _scanlists->copy(*conf->scanlists()); _gpsSystems->copy(*conf->posSystems()); _roamingChannels->copy(*conf->roamingChannels()); _roamingZones->copy(*conf->roamingZones()); return true; } ConfigItem * Config::clone() const { Config *conf = new Config(); if (! conf->copy(*this)) { conf->deleteLater(); return nullptr; } return conf; } bool Config::isModified() const { return _modified; } void Config::setModified(bool modified) { _modified = modified; } bool Config::toYAML(QTextStream &stream, const ErrorStack &err) { ConfigItem::Context context; // Label all codeplug elements if (! this->label(context, err)) return false; // Serialize into YAML YAML::Node doc = serialize(context, err); if (doc.IsNull()) return false; // Print YAML YAML::Emitter emitter; emitter << YAML::BeginDoc << doc << YAML::EndDoc; stream << QString(emitter.c_str()); return true; } bool Config::populate(YAML::Node &node, const Context &context, const ErrorStack &err) { node["version"] = VERSION_STRING; if ((node["settings"]= _settings->serialize(context, err)).IsNull()) return false; if ((node["radioIDs"] = _radioIDs->serialize(context, err)).IsNull()) return false; if ((node["contacts"] = _contacts->serialize(context, err)).IsNull()) return false; if ((node["groupLists"] = _rxGroupLists->serialize(context, err)).IsNull()) return false; if ((node["channels"] = _channels->serialize(context, err)).IsNull()) return false; if ((node["zones"] = _zones->serialize(context, err)).IsNull()) return false; if (_scanlists->count()) { if ((node["scanLists"] = _scanlists->serialize(context, err)).IsNull()) return false; } if (_gpsSystems->count()) { if ((node["positioning"] = _gpsSystems->serialize(context, err)).IsNull()) return false; } if (_roamingChannels->count()) { if ((node["roamingChannels"] = _roamingChannels->serialize(context, err)).IsNull()) return false; } if (_roamingZones->count()) { if ((node["roamingZones"] = _roamingZones->serialize(context, err)).IsNull()) return false; } if (! ConfigItem::populate(node, context, err)) return false; return true; } RadioSettings * Config::settings() const { return _settings; } RadioIDList * Config::radioIDs() const { return _radioIDs; } ContactList * Config::contacts() const { return _contacts; } RXGroupLists * Config::rxGroupLists() const { return _rxGroupLists; } ChannelList * Config::channelList() const { return _channels; } ZoneList * Config::zones() const { return _zones; } ScanLists * Config::scanlists() const { return _scanlists; } PositioningSystems * Config::posSystems() const { return _gpsSystems; } RoamingChannelList * Config::roamingChannels() const { return _roamingChannels; } RoamingZoneList * Config::roamingZones() const { return _roamingZones; } bool Config::requiresRoaming() const { // Check is roaming should be enabled bool chHasRoaming = false; for (int i=0; icount(); i++) { const DMRChannel *digi = channelList()->channel(i)->as(); if (nullptr == digi) continue; if (nullptr != digi->roamingZone()) { chHasRoaming = true; break; } } return chHasRoaming; } bool Config::requiresGPS() const { // Check is GPS should be enabled bool chHasGPS = false; for (int i=0; icount(); i++) { Channel *ch = channelList()->channel(i); // For analog channels if APRS system is set or // for digital channels if any positioning system is set if ( (ch->is() && ch->as()->aprsSystem()) || (ch->is() && ch->as()->aprsObj()) ) { chHasGPS = true; break; } } return chHasGPS; } void Config::clear() { ConfigItem::clear(); // Reset settings _settings->clear(); // Reset lists _radioIDs->clear(); _contacts->clear(); _rxGroupLists->clear(); _channels->clear(); _zones->clear(); _scanlists->clear(); _gpsSystems->clear(); _roamingChannels->clear(); _roamingZones->clear(); // Clear extensions commercialExtension()->clear(); smsExtension()->clear(); setTyTExtension(nullptr); emit modified(this); } const Config * Config::config() const { return this; } CommercialExtension * Config::commercialExtension() const { return _commercialExtension; } SMSExtension * Config::smsExtension() const { return _smsExtension; } TyTConfigExtension * Config::tytExtension() const { return _tytExtension; } void Config::setTyTExtension(TyTConfigExtension *ext) { if (_tytExtension == ext) return; if (_tytExtension) _tytExtension->deleteLater(); _tytExtension = ext; if (_tytExtension) { _tytExtension->setParent(this); connect(_tytExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModified())); } } void Config::onConfigModified() { _modified = true; emit modified(this); } bool Config::readCSV(const QString &filename, QString &errorMessage) { QFile file(filename); if (! file.open(QIODevice::ReadOnly)) return false; QTextStream stream(&file); return readCSV(stream, errorMessage); } bool Config::readCSV(QTextStream &stream, QString &errorMessage) { if (CSVReader::read(this, stream, errorMessage)) _modified = false; else return false; return true; } bool Config::readYAML(const QString &filename, const ErrorStack &err) { YAML::Node node; try { QFile file(filename); if (! file.open(QIODevice::ReadOnly)) { errMsg(err) << "Cannot open file '" << filename << "': " << file.errorString() << "."; errMsg(err) << "Cannot read YAML codeplug from file '" << filename << "'."; return false; } QByteArray content = file.readAll(); node = YAML::Load(content.constData()); } catch (const YAML::Exception &exc) { errMsg(err) << "Cannot read YAML codeplug from file '"<< filename << "': " << QString::fromStdString(exc.msg) << "."; return false; } if (! node) { errMsg(err) << "Cannot read YAML codeplug from file '" << filename << "'."; return false; } clear(); ConfigItem::Context context; if (! parse(node, context, err)) return false; if (! link(node, context, err)) return false; setModified(false); return true; } bool Config::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) { if (! node.IsMap()) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot read configuration" << ": Element is not a map."; return false; } if (node["version"] && node["version"].IsScalar()) { ctx.setVersion(QString::fromStdString(node["version"].as())); logDebug() << "Using format version " << ctx.version() << "."; } else { logWarn() << "No version string set, assuming " << VERSION_STRING << "."; ctx.setVersion(VERSION_STRING); } if (node["settings"] && (! _settings->parse(node["settings"], ctx, err))) return false; if (node["radioIDs"] && (! _radioIDs->parse(node["radioIDs"], ctx, err))) return false; if (node["contacts"] && (! _contacts->parse(node["contacts"], ctx, err))) return false; if (node["groupLists"] && (! _rxGroupLists->parse(node["groupLists"], ctx, err))) return false; if (node["channels"] && (! _channels->parse(node["channels"], ctx, err))) return false; if (node["zones"] && (! _zones->parse(node["zones"], ctx, err))) return false; if (node["scanLists"] && (! _scanlists->parse(node["scanLists"], ctx, err))) return false; if (node["positioning"] && (! _gpsSystems->parse(node["positioning"], ctx, err))) return false; if (node["roamingChannels"] && (! _roamingChannels->parse(node["roamingChannels"], ctx, err))) return false; if (node["roamingZones"] && (! _roamingZones->parse(node["roamingZones"], ctx, err))) return false; /** @todo Implemented for backward compatibility with version 0.10.0, remove for 1.0.0.*/ else if (node["roaming"] && (! _roamingZones->parse(node["roaming"], ctx, err))) return false; // also parses extensions if (! ConfigItem::parse(node, ctx, err)) return false; return true; } bool Config::link(const YAML::Node &node, const Context &ctx, const ErrorStack &err) { // radio IDs must be linked before settings, as they may refer to the default DMR ID if (node["radioIDs"] && (! _radioIDs->link(node["radioIDs"], ctx, err))) return false; if (node["settings"]) if (!_settings->link(node["settings"], ctx, err)) return false; if (node["contacts"] && (! _contacts->link(node["contacts"], ctx, err))) return false; if (node["groupLists"] && (! _rxGroupLists->link(node["groupLists"], ctx, err))) return false; if (node["channels"] && (! _channels->link(node["channels"], ctx, err))) return false; if (node["zones"] && (! _zones->link(node["zones"], ctx, err))) return false; if (node["scanLists"] && (! _scanlists->link(node["scanLists"], ctx, err))) return false; if (node["positioning"] && (! _gpsSystems->link(node["positioning"], ctx, err))) return false; if (node["roamingZones"] && (! _roamingZones->link(node["roamingZones"], ctx, err))) return false; /** @todo Implemented for backward compatibility with version 0.10.0, remove for 1.0.0.*/ else if (node["roaming"] && (! _roamingZones->link(node["roaming"], ctx, err))) return false; // also links extensions if (! ConfigItem::link(node, ctx, err)) return false; return true; } qdmr-0.12.3/lib/config.h.in000066400000000000000000000003501501654372000153320ustar00rootroot00000000000000#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@ #define VERSION_MINOR @PROJECT_VERSION_MINOR@ #define VERSION_PATCH @PROJECT_VERSION_PATCH@ #define VERSION_STRING @PROJECT_VERSION_STRING@ #define LOCALE_DIRECTORY "@LOCALE_DIRECTORY@" qdmr-0.12.3/lib/config.hh000066400000000000000000000204531501654372000151030ustar00rootroot00000000000000/** @defgroup conf Common codeplug configuration * This module collects all classes that represent the general configuration for all DMR codeplugs. * * To this end, it aims at covering the important features for ham radio applications. * All features that are more related to "professional" applications and are specific to each radio, * are implemented by so-called extensions, see @c ConfigExtension. The central class for the * abstract configuration is @c Config, this class represents a complete configuration a.k.a. * codeplug of a radio. It contains all the information being programmed into the radio irrespective * of the model and manufacturer. * * The entire configuration (abstract, device independent codeplug) consists of a tree of * @c ConfigItem instances. This class forms the base-class of all elements in the configuration * (excluding lists etc.). Each @c ConfigItem may have a set of properties. These properties are * used to implement the majority of the common functionality concerning the abstract codeplug. * These are * - Copying & cloning of elements of the configuration. * - Labeling of codeplug objects (eveything that has an ID for cross referencing). * - Serialzation into YAML (all properties are serialized into YAML automatically if not * prevented by marking the property as not @c SCRIPTABLE). * - Parsing of YAML codeplugs (automatic property parsing can be disabled on a per-property * bassis by marking it as not @c SCRIPTABLE). * - Generic editing of the properties in the GUI. * . * To this end, the creation of codeplug extensions is pretty easy, as only the properties for the * extension must be defined. The rest is taken care of by the default implementation of the * @c ConfigItem::copy, @c ConfigItem::clone, @c ConfigItem::label, @c ConfigItem::serialize, * @c ConfigItem::parse and @c ConfigItem::link methods. It is not necessary to override any of * these methods if there is a one-to-one mapping between the property and its YAML representation. * * Frequently, however, it is necessary to represent a property in a different way in YAML. This * is usually true if a property may hold different specializations of a common type. For example * the channel list may hold analog and digital channels. In YAML, the type is specified explicitly * as an enclosing map. This structure is not a one-to-one representation of the actual property * (the channel list) to the YAML format. In these cases, the @c ConfigItem::parse and * @c ConfigItem::link method might be overridden to implement this. */ #ifndef CONFIG_HH #define CONFIG_HH #include #include "configobject.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "channel.hh" #include "zone.hh" #include "scanlist.hh" #include "gpssystem.hh" #include "roamingchannel.hh" #include "roamingzone.hh" #include "radioid.hh" #include "radiosettings.hh" #include "commercial_extension.hh" #include "smsextension.hh" #include "tyt_extensions.hh" // Forward declaration class UserDatabase; /** The config class, representing the codeplug configuration. * * It contains the description of the contacts, channels, zones, etc. of the codeplug * configuration. * * @ingroup conf */ class Config : public ConfigItem { Q_OBJECT /** The global radio settings. */ Q_PROPERTY(RadioSettings* settings READ settings SCRIPTABLE false) /** The list of radio IDs. */ Q_PROPERTY(RadioIDList* radioIDs READ radioIDs SCRIPTABLE false) /** The list of contacts. */ Q_PROPERTY(ContactList* contacts READ contacts SCRIPTABLE false) /** The list of group lists. */ Q_PROPERTY(RXGroupLists* groupLists READ rxGroupLists SCRIPTABLE false) /** The list of channels. */ Q_PROPERTY(ChannelList* channels READ channelList SCRIPTABLE false) /** The list of zones. */ Q_PROPERTY(ZoneList* zones READ zones SCRIPTABLE false) /** The list of scan lists. */ Q_PROPERTY(ScanLists* scanLists READ scanlists SCRIPTABLE false) /** The list of positioning systems. */ Q_PROPERTY(PositioningSystems* positioning READ posSystems SCRIPTABLE false) /** The list of roaming channels. */ Q_PROPERTY(RoamingChannelList* roamingChannels READ roamingChannels SCRIPTABLE false) /** The list of roaming zones. */ Q_PROPERTY(RoamingZoneList* roamingZones READ roamingZones SCRIPTABLE false) /** Represents the config extension for encryption keys. */ Q_PROPERTY(CommercialExtension* commercial READ commercialExtension) /** Represents the extended SMS settings. */ Q_PROPERTY(SMSExtension *sms READ smsExtension) /** Represents the config extension for TyT devices. */ Q_PROPERTY(TyTConfigExtension* tytExtension READ tytExtension WRITE setTyTExtension) public: /** Constructs an empty configuration. */ Q_INVOKABLE explicit Config(QObject *parent = nullptr); bool copy(const ConfigItem &other); ConfigItem *clone() const; /** Returns @c true if the config was modified, @see modified. */ bool isModified() const; /** Sets the modified flag. */ void setModified(bool modified); /** Returns the radio wide settings. */ RadioSettings *settings() const; /** Returns the list of radio IDs. */ RadioIDList *radioIDs() const; /** Returns the list of contacts. */ ContactList *contacts() const; /** Returns the list of RX group lists. */ RXGroupLists *rxGroupLists() const; /** Returns the list of channels. */ ChannelList *channelList() const; /** Returns the list of zones. */ ZoneList *zones() const; /** Returns the list of scanlists. */ ScanLists *scanlists() const; /** Returns the list of positioning systems. */ PositioningSystems *posSystems() const; /** Returns the list of roaming channels. */ RoamingChannelList *roamingChannels() const; /** Returns the list of roaming zones. */ RoamingZoneList *roamingZones() const; /** Returns @c true if one of the digital channels has a roaming zone assigned. */ bool requiresRoaming() const; /** Returns @c true if one of the channels has a GPS or APRS system assigned. */ bool requiresGPS() const; /** Clears the complete configuration. */ void clear(); const Config *config() const; /** Returns the commercial extension. */ CommercialExtension *commercialExtension() const; /** Returns the SMS settings extension. */ SMSExtension *smsExtension() const; /** Returns the TyT settings extension. * If this extension is not set, returns @c nullptr. */ TyTConfigExtension *tytExtension() const; /** Sets the TyT settings extension. */ void setTyTExtension(TyTConfigExtension *ext); public: /** Imports a configuration from the given file. */ bool readCSV(const QString &filename, QString &errorMessage); /** Imports a configuration from the given text stream in text format. */ bool readCSV(QTextStream &stream, QString &errorMessage); /** Imports a configuration from the given YAML file. */ bool readYAML(const QString &filename, const ErrorStack &err=ErrorStack()); bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack()); bool link(const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Serializes the configuration into the given stream as text. */ bool toYAML(QTextStream &stream, const ErrorStack &err=ErrorStack()); protected: bool populate(YAML::Node &node, const Context &context, const ErrorStack &err=ErrorStack()); protected slots: /** Iternal callback. */ void onConfigModified(); protected: /** If @c true, the configuration was modified. */ bool _modified; /** Radio wide settings. */ RadioSettings *_settings; /** The list of radio IDs. */ RadioIDList *_radioIDs; /** The list of contacts. */ ContactList *_contacts; /** The list of RX group lists. */ RXGroupLists *_rxGroupLists; /** The list of channels. */ ChannelList *_channels; /** The list of zones. */ ZoneList *_zones; /** The list of scan lists. */ ScanLists *_scanlists; /** The list of GPS Systems. */ PositioningSystems *_gpsSystems; /** The list of roaming channels. */ RoamingChannelList *_roamingChannels; /** The list of roaming zones. */ RoamingZoneList *_roamingZones; /** Owns the TyT settings extension. */ TyTConfigExtension *_tytExtension; /** Owns the commercial extension. */ CommercialExtension *_commercialExtension; /** Owns the SMS settings extension. */ SMSExtension *_smsExtension; }; #endif // CONFIG_HH qdmr-0.12.3/lib/configcopyvisitor.cc000066400000000000000000000326351501654372000174110ustar00rootroot00000000000000#include "configcopyvisitor.hh" #include "configobject.hh" #include "configreference.hh" #include "channel.hh" #include "radioid.hh" #include "roamingzone.hh" #include "logger.hh" /* ********************************************************************************************* * * Implementation of ConfigCloneVisitor * ********************************************************************************************* */ ConfigCloneVisitor::ConfigCloneVisitor(QHash &map) : Visitor(), _map(map) { // pass... } bool ConfigCloneVisitor::processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err) { if (prop.isEnumType() || (QString("bool") == prop.typeName()) || (QString("int") == prop.typeName()) || (QString("uint") == prop.typeName()) || (QString("double") == prop.typeName()) || (QString("QString") == prop.typeName()) || (QString("Frequency") == prop.typeName()) || (QString("Interval") == prop.typeName()) || (QString("SelectiveCall") == prop.typeName())) { if ((! prop.isReadable()) && (!prop.isWritable())) { logDebug() << "Skip property " << prop.name() << " of item " << item->metaObject()->className() << ": Not readable or writable."; return true; } if (! Visitor::processProperty(item, prop, err)) return false; // Get clone ConfigItem *clone = qobject_cast(_stack.back()); if (nullptr == clone) { errMsg(err) << "Unexpected element on the stack. Found object of type " << _stack.back()->metaObject()->className() << ", expected ConfigItem."; return false; } // Find the property int pidx = clone->metaObject()->indexOfProperty(prop.name()); if (0 > pidx) { errMsg(err) << "Cannot set property " << prop.name() << " on element on stack."; return false; } // Set it if (! clone->metaObject()->property(pidx).write(clone, prop.read(item))) { errMsg(err) << "Cannot set property " << prop.name() << " on element on stack."; return false; } return true; } else if (propIsInstance(prop)) { // Get clone ConfigItem *clone = qobject_cast(_stack.back()); if (nullptr == clone) { errMsg(err) << "Unexpected element on the stack. Found object of type " << _stack.back()->metaObject()->className() << ", expected ConfigItem."; return false; } // Set reference to same object, will be replaced later. ConfigObjectReference *ref = prop.read(item).value(); ConfigObjectReference *cloneRef = prop.read(clone).value(); cloneRef->set(ref->as()); return true; } else if (propIsInstance(prop)) { if (nullptr == prop.read(item).value()) return true; // If writeable, simply traverse config item if (prop.isWritable()) { // Clone item in property recursively if(! this->processItem(prop.read(item).value(), err)) return false; // Get cloned item from stack ConfigItem *newItem = dynamic_cast(_stack.back()); _stack.pop_back(); // Also, get item from stack, who owns the clones item ConfigItem *clone = dynamic_cast(_stack.back()); int pidx = clone->metaObject()->indexOfProperty(prop.name()); // Find the property if (0 > pidx) { errMsg(err) << "Cannot set property " << prop.name() << " on element on stack."; return false; } // Set it if (! clone->metaObject()->property(pidx).write(clone, QVariant::fromValue(newItem))) { errMsg(err) << "Cannot set property " << prop.name() << " on element on stack."; return false; } return true; } // If property is not writeable, the parent element (top of stack), owns and creates the item // -> needs to be put on the stack ConfigItem *clone = dynamic_cast(_stack.back()); if (nullptr == clone) { errMsg(err) << "Unexpected element on the stack. Found object of type " << _stack.back()->metaObject()->className() << ", expected ConfigItem."; return false; } int pidx = clone->metaObject()->indexOfProperty(prop.name()); if (0 > pidx) { errMsg(err) << "Cannot read property " << prop.name() << " on element on stack."; return false; } QVariant value = clone->metaObject()->property(pidx).read(clone); if (!value.isValid()) { errMsg(err) << "Cannot read property " << prop.name() << " on element on stack."; return false; } _stack.push_back(value.value()); if (! Visitor::processItem(prop.read(item).value(), err)) { errMsg(err) << "Cannot process property " << prop.name() << "."; return false; } _stack.pop_back(); return true; } else if (ConfigObjectList *lst = prop.read(item).value()) { // Lists are always owned by the item. So dig up the list and put it on the stack ConfigItem *clone = dynamic_cast(_stack.back()); if (nullptr == clone) { errMsg(err) << "Unexpected element on the stack. Found object of type " << _stack.back()->metaObject()->className() << ", expected ConfigItem."; return false; } int pidx = clone->metaObject()->indexOfProperty(prop.name()); if (0 > pidx) { errMsg(err) << "Cannot read property " << prop.name() << " on element on stack."; return false; } QVariant value = clone->metaObject()->property(pidx).read(clone); if (!value.isValid()) { errMsg(err) << "Cannot read property " << prop.name() << " on element on stack."; return false; } _stack.push_back(value.value()); if (! processList(lst, err)) { errMsg(err) << "Cannot process object list in property " << prop.name() << "."; return false; } _stack.pop_back(); return true; } else if (ConfigObjectRefList *refs = prop.read(item).value()) { // Lists are always owned by the item. So dig up the parent and get the list ConfigItem *clone = dynamic_cast(_stack.back()); if (nullptr == clone) { errMsg(err) << "Unexpected element on the stack. Found object of type " << _stack.back()->metaObject()->className() << ", expected ConfigItem."; return false; } int pidx = clone->metaObject()->indexOfProperty(prop.name()); if (0 > pidx) { errMsg(err) << "Cannot read property " << prop.name() << " on element on stack."; return false; } QVariant value = clone->metaObject()->property(pidx).read(clone); if (!value.isValid()) { errMsg(err) << "Cannot read property " << prop.name() << " on element on stack."; return false; } _stack.push_back(value.value()); if (! processList(refs, err)) { errMsg(err) << "Cannot process reference list in property " << prop.name() << "."; return false; } _stack.pop_back(); return true; } else { if (! this->processUnknownType(item, prop, err)) { errMsg(err) << "While processing property '" << prop.name() << "' of '" << item->metaObject()->className() << "' of unknown type."; return false; } } return true; } bool ConfigCloneVisitor::processItem(ConfigItem *item, const ErrorStack &err) { // Call default constructor. QObject *obj = item->metaObject()->newInstance(); if (nullptr == obj) { errMsg(err) << "Cannot construct new instance for item of type " << item->metaObject()->className() << "."; return false; } // Put new item on stack _stack.push_back(obj); // If item is an object -> store in map if (item->is()) _map[item->as()] = dynamic_cast(obj); // then traverse by type ... if (item->is()) return processChannel(item->as(), err); // .. or use default. return Visitor::processItem(item, err); } bool ConfigCloneVisitor::processChannel(Channel *item, const ErrorStack &err) { if (! Visitor::processItem(item, err)) return false; if (item->defaultPower()) qobject_cast(_stack.back())->setDefaultPower(); return true; } bool ConfigCloneVisitor::processList(AbstractConfigObjectList *list, const ErrorStack &err) { if (ConfigObjectRefList *refs = dynamic_cast(list)) { ConfigObjectRefList *clone = dynamic_cast(_stack.back()); if (nullptr == clone) { errMsg(err) << "Cannot clone reference list, no ref list found on the stack. Got " << _stack.back()->metaObject()->className() << "."; return false; } // Just copy references, they will be replaced later for (int i=0; icount(); i++) { clone->add(refs->get(i)); } return true; } if (ConfigObjectList *olist = dynamic_cast(list)) { // First, get object list from stack ConfigObjectList *clone = dynamic_cast(_stack.back()); if (nullptr == clone) { errMsg(err) << "Cannot clone list, no list found on the stack. Got " << _stack.back()->metaObject()->className() << "."; return false; } // just traverse list if (! Visitor::processList(olist, err)) return false; // Take generated items from stack for (int i=0; icount(); i++) { clone->add(dynamic_cast(_stack.back()),0); _stack.pop_back(); } return true; } errMsg(err) << "Unexpected list type " << list->metaObject()->className() << "."; return false; } ConfigItem * ConfigCloneVisitor::takeResult(const ErrorStack &err) { if (1 != _stack.size()) { errMsg(err) << "Cannot take result, result stack does not contain one element, got " << _stack.size() << "."; return nullptr; } ConfigItem *item = dynamic_cast(_stack.back()); if (nullptr == item) { errMsg(err) << "Cannot take result, last element on stack is not a ConfigItem, got " << _stack.back()->metaObject()->className() << "."; return nullptr; } _stack.pop_back(); return item; } /* ********************************************************************************************* * * Implementation of FixReferencesVisitor * ********************************************************************************************* */ FixReferencesVisistor::FixReferencesVisistor(QHash &map, bool keepUnknown) : Visitor(), _map(map), _keepUnknown(keepUnknown) { // Populate with default singleton instances. map[SelectedChannel::get()] = SelectedChannel::get(); map[DefaultRadioID::get()] = DefaultRadioID::get(); map[DefaultRoamingZone::get()] = DefaultRoamingZone::get(); } bool FixReferencesVisistor::processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err) { // First, traverse normally if (! Visitor::processProperty(item, prop, err)) return false; if (ConfigObjectReference *ref = prop.read(item).value()) { if (ref->isNull()) return true; ConfigObject *obj = ref->as(); if ((! _keepUnknown) && (! _map.contains(obj))) { errMsg(err) << "Cannot fix refrence to object '" << obj->name() << "' of type " << obj->metaObject()->className() << ": Not mapped/cloned yet."; return false; } if (_map.contains(obj)) ref->set(_map[obj]); return true; } return true; } bool FixReferencesVisistor::processList(AbstractConfigObjectList *list, const ErrorStack &err) { // First traverse list if (! Visitor::processList(list, err)) return false; if (ConfigObjectRefList *rlist = dynamic_cast(list)) { QSet del; // Resolve all references. for (int i=0; icount(); i++) { if ((! _keepUnknown) && (! _map.contains(rlist->get(i)))) { errMsg(err) << "Cannot fix refrence to object '" << rlist->get(i)->name() << "' of type " << rlist->get(i)->metaObject()->className() << ": Not mapped/cloned yet."; return false; } // If the reference cannot be replaced (i.e., the replacement is already in the list): if (_map.contains(rlist->get(i)) && (0 > rlist->replace(_map[rlist->get(i)], i))) del.insert(rlist->get(i)); } // Now remove all references that could not be replaced foreach (ConfigObject *obj, del) { rlist->del(obj); } } return true; } /* ********************************************************************************************* * * Implementation of ConfigCopy * ********************************************************************************************* */ ConfigItem * ConfigCopy::copy(ConfigItem *original, const ErrorStack &err) { QHash map; ConfigCloneVisitor cloner(map); if (! cloner.processItem(original, err)) { errMsg(err) << "Cannot clone item of type " << original->metaObject()->className() << "."; return nullptr; } ConfigItem *clone = cloner.takeResult(); FixReferencesVisistor fixer(map, true); if (! fixer.processItem(clone, err)) { errMsg(err) << "Cannot fix references in item of type " << clone->metaObject()->className() << "."; delete clone; return nullptr; } return clone; } qdmr-0.12.3/lib/configcopyvisitor.hh000066400000000000000000000043041501654372000174130ustar00rootroot00000000000000#ifndef CONFIGCOPYVISITOR_HH #define CONFIGCOPYVISITOR_HH #include "visitor.hh" class ConfigObject; class Channel; /** This visitor traverses the the given configuration and clones it. All references are still * pointing to the originals. * @ingroup conf */ class ConfigCloneVisitor : public Visitor { public: /** Constructor. * @param map Specifies the mapping table to be filled with the created clones. */ ConfigCloneVisitor(QHash &map); bool processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack()); bool processItem(ConfigItem *item, const ErrorStack &err=ErrorStack()); bool processList(AbstractConfigObjectList *list, const ErrorStack &err=ErrorStack()); /** Specialized handler for channels, must traverse the channel object. */ virtual bool processChannel(Channel *item, const ErrorStack &err=ErrorStack()); /** Extracts the cloned item. */ ConfigItem *takeResult(const ErrorStack &err=ErrorStack()); protected: /** Stack of the current object. */ QList _stack; /** Reference to the translation table origial -> cloned object. */ QHash &_map; }; /** Replaces references using a specified map. * This can be considered a second step in copying an entire codeplug, first all objects are cloned * and in a second step, all references are fixed using this class. * @ingroup conf */ class FixReferencesVisistor: public Visitor { public: /** Constructor. */ FixReferencesVisistor(QHash &map, bool keepUnknown=false); bool processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack()); bool processList(AbstractConfigObjectList *list, const ErrorStack &err=ErrorStack()); protected: /** Reference to the translation table origial -> cloned object. */ QHash &_map; /** If false, an unmapped reference is an error. */ bool _keepUnknown; }; /** Just a name space to hold the copy function. */ class ConfigCopy { public: /** Copies the given item. */ static ConfigItem *copy(ConfigItem *original, const ErrorStack &err=ErrorStack()); }; #endif // CONFIGCOPYVISITOR_HH qdmr-0.12.3/lib/configlabelingvisitor.cc000066400000000000000000000020001501654372000201730ustar00rootroot00000000000000#include "configlabelingvisitor.hh" ConfigLabelingVisitor::ConfigLabelingVisitor(ConfigItem::Context &context) : Visitor(), _context(context) { // pass.. } bool ConfigLabelingVisitor::processItem(ConfigItem *item, const ErrorStack &err) { // First, check if item is an ConfigObject if (item->is()) { ConfigObject *obj = item->as(); QString prefix = obj->idPrefix(); // If no prefix is set -> skip. if (prefix.isEmpty()) return Visitor::processItem(item, err); // Find unused ID unsigned n=1; QString id=QString("%1%2").arg(prefix).arg(n); while (_context.contains(id)) id=QString("%1%2").arg(prefix).arg(++n); // Add to context if (! _context.add(id, obj)) { if (_context.contains(obj)) errMsg(err) << "Object already in context with id '" << _context.getId(obj) << "'."; errMsg(err) << "Cannot add element '" << id << "' to context."; return false; } } return Visitor::processItem(item, err); } qdmr-0.12.3/lib/configlabelingvisitor.hh000066400000000000000000000015311501654372000202150ustar00rootroot00000000000000#ifndef CONFIGLABELINGVISITOR_HH #define CONFIGLABELINGVISITOR_HH #include "visitor.hh" #include "configobject.hh" /** A visitor to label the entire configuration. * That is, assigning unique labels to each @c ConfigObject within the configuration. * @ingroup conf */ class ConfigLabelingVisitor: protected Visitor { public: /** Use the static method @c label to label the configuration. */ ConfigLabelingVisitor(ConfigItem::Context &context); bool processItem(ConfigItem *item, const ErrorStack &err=ErrorStack()); /** Labels the configuration and stores the labels in the given context. */ static bool label(Config *config, ConfigItem::Context &context); protected: /** Holds a weak reference to the parser/serializer context. * That is, the id<->obj table. */ ConfigItem::Context &_context; }; #endif // CONFIGLABELINGVISITOR_HH qdmr-0.12.3/lib/configmergevisitor.cc000066400000000000000000000304531501654372000175320ustar00rootroot00000000000000#include "configmergevisitor.hh" #include "configcopyvisitor.hh" #include "configobject.hh" #include "configreference.hh" #include "config.hh" #include "channel.hh" #include "logger.hh" /* ********************************************************************************************* * * Implementation of ConfigMergeVisitor * ********************************************************************************************* */ ConfigMergeVisitor::ConfigMergeVisitor(Config* destination, QHash& translation, ItemStrategy itemStrategy, SetStrategy setStrategy) : Visitor(), _destination(destination), _translation(translation), _itemStrategy(itemStrategy), _setStrategy(setStrategy) { // pass... } bool ConfigMergeVisitor::processItem(ConfigItem *item, const ErrorStack &err) { // Dispatch by type if (item->is()) return processRadioID(item->as(), err); else if (item->is()) return processContact(item->as(), err); else if (item->is()) return processGroupList(item->as(), err); else if (item->is()) return processChannel(item->as(), err); else if (item->is()) return processZone(item->as(), err); else if (item->is()) return processScanList(item->as(), err); else if (item->is()) return processPositioningSystem(item->as(), err); else if (item->is()) return processRoamingChannel(item->as(), err); else if (item->is()) return processRoamingZone(item->as(), err); return Visitor::processItem(item, err); } bool ConfigMergeVisitor::processRadioID(RadioID *item, const ErrorStack &err) { if (0 == _destination->radioIDs()->findItemsByName(item->name()).count()) return addObject(_destination->radioIDs(), nullptr, item, err); RadioID *present = _destination->radioIDs()->findItemsByName(item->name()) .first()->as(); if (ItemStrategy::Ignore == _itemStrategy) return ignoreObject(_destination->radioIDs(), present, item, err); if (ItemStrategy::Override == _itemStrategy) return replaceObject(_destination->radioIDs(), present, item, err); if (ItemStrategy::Duplicate == _itemStrategy) return duplicateObject(_destination->radioIDs(), present, item, err); return true; } bool ConfigMergeVisitor::processChannel(Channel *item, const ErrorStack &err) { if (0 == _destination->channelList()->findItemsByName(item->name()).count()) return addObject(_destination->channelList(), nullptr, item, err); Channel *present = _destination->channelList()->findItemsByName(item->name()) .first()->as(); if (ItemStrategy::Ignore == _itemStrategy) return ignoreObject(_destination->channelList(), present, item, err); if (ItemStrategy::Override == _itemStrategy) return replaceObject(_destination->channelList(), present, item, err); if (ItemStrategy::Duplicate == _itemStrategy) return duplicateObject(_destination->channelList(), present, item, err); return true; } bool ConfigMergeVisitor::processContact(Contact *item, const ErrorStack &err) { if (0 == _destination->contacts()->findItemsByName(item->name()).count()) return addObject(_destination->contacts(), nullptr, item, err); Contact *present = _destination->contacts()->findItemsByName(item->name()) .first()->as(); if (ItemStrategy::Ignore == _itemStrategy) return ignoreObject(_destination->contacts(), present, item, err); if (ItemStrategy::Override == _itemStrategy) return replaceObject(_destination->contacts(), present, item, err); if (ItemStrategy::Duplicate == _itemStrategy) return duplicateObject(_destination->contacts(), present, item, err); return true; } bool ConfigMergeVisitor::processPositioningSystem(PositioningSystem *item, const ErrorStack &err) { if (0 == _destination->posSystems()->findItemsByName(item->name()).count()) return addObject(_destination->posSystems(), nullptr, item, err); PositioningSystem *present = _destination->posSystems()->findItemsByName(item->name()) .first()->as(); if (ItemStrategy::Ignore == _itemStrategy) return ignoreObject(_destination->posSystems(), present, item, err); if (ItemStrategy::Override == _itemStrategy) return replaceObject(_destination->posSystems(), present, item, err); if (ItemStrategy::Duplicate == _itemStrategy) return duplicateObject(_destination->posSystems(), present, item, err); return true; } bool ConfigMergeVisitor::processRoamingChannel(RoamingChannel *item, const ErrorStack &err) { if (0 == _destination->roamingChannels()->findItemsByName(item->name()).count()) return addObject(_destination->roamingChannels(), nullptr, item, err); RoamingChannel *present = _destination->roamingChannels()->findItemsByName(item->name()) .first()->as(); if (ItemStrategy::Ignore == _itemStrategy) return ignoreObject(_destination->roamingChannels(), present, item, err); if (ItemStrategy::Override == _itemStrategy) return replaceObject(_destination->roamingChannels(), present, item, err); if (ItemStrategy::Duplicate == _itemStrategy) return duplicateObject(_destination->roamingChannels(), present, item, err); return true; } bool ConfigMergeVisitor::processGroupList(RXGroupList *item, const ErrorStack &err) { if (0 == _destination->rxGroupLists()->findItemsByName(item->name()).count()) return addObject(_destination->rxGroupLists(), nullptr, item, err); RXGroupList *present = _destination->rxGroupLists()->findItemsByName(item->name()) .first()->as(); if (SetStrategy::Ignore == _setStrategy) return ignoreObject(_destination->rxGroupLists(), present, item, err); if (SetStrategy::Override == _setStrategy) return replaceObject(_destination->rxGroupLists(), present, item, err); if (SetStrategy::Duplicate == _setStrategy) return duplicateObject(_destination->rxGroupLists(), present, item, err); if (SetStrategy::Merge == _setStrategy) return mergeList(present->contacts(), item->contacts(), err); return true; } bool ConfigMergeVisitor::processZone(Zone *item, const ErrorStack &err) { if (0 == _destination->zones()->findItemsByName(item->name()).count()) return addObject(_destination->zones(), nullptr, item, err); Zone *present = _destination->zones()->findItemsByName(item->name()) .first()->as(); if (SetStrategy::Ignore == _setStrategy) return ignoreObject(_destination->zones(), present, item, err); if (SetStrategy::Override == _setStrategy) return replaceObject(_destination->zones(), present, item, err); if (SetStrategy::Duplicate == _setStrategy) return duplicateObject(_destination->zones(), present, item, err); if (SetStrategy::Merge == _setStrategy) return mergeList(present->A(), item->A(), err) && mergeList(present->B(), item->B(), err); return true; } bool ConfigMergeVisitor::processScanList(ScanList *item, const ErrorStack &err) { if (0 == _destination->scanlists()->findItemsByName(item->name()).count()) return addObject(_destination->scanlists(), nullptr, item, err); ScanList *present = _destination->scanlists()->findItemsByName(item->name()) .first()->as(); if (SetStrategy::Ignore == _setStrategy) return ignoreObject(_destination->scanlists(), present, item, err); if (SetStrategy::Override == _setStrategy) return replaceObject(_destination->scanlists(), present, item, err); if (SetStrategy::Duplicate == _setStrategy) return duplicateObject(_destination->scanlists(), present, item, err); if (SetStrategy::Merge == _setStrategy) return mergeList(present->channels(), item->channels(), err); return true; } bool ConfigMergeVisitor::processRoamingZone(RoamingZone *item, const ErrorStack &err) { if (0 == _destination->roamingZones()->findItemsByName(item->name()).count()) return addObject(_destination->roamingZones(), nullptr, item, err); RoamingZone *present = _destination->roamingZones()->findItemsByName(item->name()) .first()->as(); if (SetStrategy::Ignore == _setStrategy) return ignoreObject(_destination->roamingZones(), present, item, err); if (SetStrategy::Override == _setStrategy) return replaceObject(_destination->roamingZones(), present, item, err); if (SetStrategy::Duplicate == _setStrategy) return duplicateObject(_destination->roamingZones(), present, item, err); if (SetStrategy::Merge == _setStrategy) return mergeList(present->channels(), item->channels(), err); return true; } bool ConfigMergeVisitor::addObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err) { Q_UNUSED(present) ConfigObject *newObject = ConfigCopy::copy(merging, err)->as(); if (nullptr == newObject) { errMsg(err) << "Cannot copy item '" << merging->name() << "'."; return false; } logDebug() << "Add object '" << merging->name() << "'."; list->add(newObject); _translation[merging] = newObject; return true; } bool ConfigMergeVisitor::ignoreObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err) { Q_UNUSED(list); Q_UNUSED(err) logDebug() << "Ignore object '" << merging->name() << "'."; _translation[merging] = present; return true; } bool ConfigMergeVisitor::replaceObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err) { ConfigObject *newObject = ConfigCopy::copy(merging, err)->as(); if (nullptr == newObject) { errMsg(err) << "Cannot copy item '" << merging->name() << "'."; return false; } logDebug() << "Replace object '" << present->name() << "'."; list->replace(newObject, list->indexOf(present)); _translation[merging] = newObject; _translation[present] = newObject; return true; } bool ConfigMergeVisitor::duplicateObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err) { Q_UNUSED(present) ConfigObject *newItem = ConfigCopy::copy(merging, err)->as(); if (nullptr == newItem) { errMsg(err) << "Cannot copy item '" << merging->name() << "'."; return false; } logDebug() << "Copy object '" << merging->name() << "'."; newItem->setName(newItem->name()+" (copy)"); list->add(newItem); _translation[merging] = newItem; return true; } bool ConfigMergeVisitor::mergeList(ConfigObjectRefList *present, ConfigObjectRefList *merging, const ErrorStack &err) { Q_UNUSED(err); for (int i=0; icount(); i++) { ConfigObject *mergingItem = merging->get(i); mergingItem = _translation.value(mergingItem, mergingItem)->as(); if (! present->has(mergingItem)) { present->add(mergingItem); } } return true; } /* ********************************************************************************************* * * Implementation of ConfigMerge * ********************************************************************************************* */ bool ConfigMerge::mergeInto(Config *destination, Config *source, ConfigMergeVisitor::ItemStrategy itemStrategy, ConfigMergeVisitor::SetStrategy setStrategy, const ErrorStack &err) { QHash referenceTable; ConfigMergeVisitor mergeVisitor(destination, referenceTable, itemStrategy, setStrategy); if (! mergeVisitor.process(source, err)) { errMsg(err) << "Cannot merge configurations."; return false; } FixReferencesVisistor linkVisitor(referenceTable, true); if (! linkVisitor.process(destination, err)) { errMsg(err) << "Cannot fix references in merged configuration."; return false; } return true; } Config * ConfigMerge::merge(Config *destination, Config *source, ConfigMergeVisitor::ItemStrategy itemStrategy, ConfigMergeVisitor::SetStrategy setStrategy, const ErrorStack &err) { Config *copy = ConfigCopy::copy(destination, err)->as(); if (nullptr == copy) { errMsg(err) << "Cannot merge configurations."; return nullptr; } if (! mergeInto(copy, source, itemStrategy, setStrategy, err)) { delete copy; return nullptr; } return copy; } qdmr-0.12.3/lib/configmergevisitor.hh000066400000000000000000000140431501654372000175410ustar00rootroot00000000000000#ifndef CONFIGMERGEVISITOR_HH #define CONFIGMERGEVISITOR_HH #include "visitor.hh" class ConfigObject; class RadioID; class Channel; class Contact; class PositioningSystem; class RoamingChannel; class ConfigObjectRefList; class RXGroupList; class Zone; class ScanList; class RoamingZone; /** Implements a visitor to merge the visited config into a given config. * The destination configuration object is passed to the constructor. This allows to merge several * configurations into one. * @ingroup conf */ class ConfigMergeVisitor : public Visitor { public: /** Possible strategies to merge atomic items. That is, Channels, Contacts & PositioningSystems, * if there are two items with the same name. */ enum class ItemStrategy { Ignore, ///< Ignore the source item. Override, ///< Override the destination item. Duplicate ///< Add a new item with a modified name. }; /** Possible strategies to merge sets of references. That is, GroupLists, Zones & ScanLists, * if tehre are two sets with the same name. */ enum class SetStrategy { Ignore, ///< Ignore the source set. Override, ///< Override the destination item. Duplicate, ///< Add a new set with a modified name. Merge ///< Merge sets. }; public: /** Constructor of the merge visitor. * @param destination Specifies the destination configuration, the additional configurations are * merged into. * @param translation Specifies the translation table to fill of objects being replaced. * @param itemStrategy Specifies the item merge strategy. * @param setStrategy Specifies the set merge strategy. */ ConfigMergeVisitor(Config *destination, QHash &translation, ItemStrategy itemStrategy=ItemStrategy::Ignore, SetStrategy setStrategy=SetStrategy::Ignore); bool processItem(ConfigItem *item, const ErrorStack &err=ErrorStack()); protected: /** Handles a RadioID object of the source configuration. */ bool processRadioID(RadioID *item, const ErrorStack &err = ErrorStack()); /** Handles a Channel object of the source configuration. */ bool processChannel(Channel *item, const ErrorStack &err = ErrorStack()); /** Handles a Contact object of the source configuration. */ bool processContact(Contact *item, const ErrorStack &err = ErrorStack()); /** Handles a PositioningSystem object of the source configuration. */ bool processPositioningSystem(PositioningSystem *item, const ErrorStack &err = ErrorStack()); /** Handles a RoamingChannel object of the source configuration. */ bool processRoamingChannel(RoamingChannel *item, const ErrorStack &err = ErrorStack()); /** Handles a RXGroupList object of the source configuration. */ bool processGroupList(RXGroupList *item, const ErrorStack &err = ErrorStack()); /** Handles a Zone object of the source configuration. */ bool processZone(Zone *item, const ErrorStack &err = ErrorStack()); /** Handles a ScanList object of the source configuration. */ bool processScanList(ScanList *item, const ErrorStack &err = ErrorStack()); /** Handles a RoamingZone object of the source configuration. */ bool processRoamingZone(RoamingZone *item, const ErrorStack &err = ErrorStack()); /** Adds a copy of the @c merging object to the given list, containing the colliding @c present * object. Also updates the translation table to bend references to the merging object to that copy. */ bool addObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err = ErrorStack()); /** Does not add the @c merging object, but fixes the translation table to bend references to the merging object. */ bool ignoreObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err = ErrorStack()); /** Replaces the @c present object in the given list by a copy of @c merging object. Also fixes * the translation table to bend references to both the removed @c present object as well as the * @c merging object. */ bool replaceObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err = ErrorStack()); /** Adds a copy of the @c merging object to the given list. */ bool duplicateObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err = ErrorStack()); /** Merges two reference lists. That is, @c merging gets merged into the @c present list. */ bool mergeList(ConfigObjectRefList *present, ConfigObjectRefList *merging, const ErrorStack &err = ErrorStack()); protected: /** The destination configuration. */ Config *_destination; /** Translation table for fixing references. */ QHash &_translation; /** The item merge strategy. */ ItemStrategy _itemStrategy; /** The set merge strategy. */ SetStrategy _setStrategy; }; /** Just a namespace to provide merging functions. * @ingroup conf */ class ConfigMerge { public: /** Merges the given @c source into the given @c destination using the specified strategies to * handle conflicts. Here the @c destination codeplug gets modified, even on errors. */ static bool mergeInto(Config *destination, Config *source, ConfigMergeVisitor::ItemStrategy itemStrategy=ConfigMergeVisitor::ItemStrategy::Ignore, ConfigMergeVisitor::SetStrategy setStrategy=ConfigMergeVisitor::SetStrategy::Ignore, const ErrorStack &err = ErrorStack()); /** Merges the given @c source into a copy of the given @c destination, using the specified * strategies to handle conflicts. Here the @c destination codeplug does not get modified at * all. */ static Config *merge(Config *destination, Config *source, ConfigMergeVisitor::ItemStrategy itemStrategy=ConfigMergeVisitor::ItemStrategy::Ignore, ConfigMergeVisitor::SetStrategy setStrategy=ConfigMergeVisitor::SetStrategy::Ignore, const ErrorStack &err = ErrorStack()); }; #endif // CONFIGMERGEVISITOR_HH qdmr-0.12.3/lib/configobject.cc000066400000000000000000001521051501654372000162600ustar00rootroot00000000000000#include "configobject.hh" #include "configreference.hh" #include "logger.hh" #include "frequency.hh" #include "interval.hh" #include "signaling.hh" #include "commercial_extension.hh" #include #include // Helper function to extract key names for a QMetaEnum inline QStringList enumKeys(const QMetaEnum &e) { QStringList lst; for (int i=0; imetaObject(); while (type) { if (typeNames.contains(type->className())) return true; type = type->superClass(); } return false; } /* ********************************************************************************************* * * Implementation of ConfigObject::Context * ********************************************************************************************* */ QHash> ConfigObject::Context::_tagObjects = QHash>(); QHash> ConfigObject::Context::_tagNames = QHash>(); ConfigItem::Context::Context() : _version(), _objects(), _ids() { // pass... } ConfigItem::Context::~Context() { // pass... } const QString & ConfigItem::Context::version() const { return _version; } void ConfigItem::Context::setVersion(const QString &ver) { _version = ver; } bool ConfigItem::Context::contains(ConfigObject *obj) const { return _ids.contains(obj); } bool ConfigItem::Context::contains(const QString &id) const { return _objects.contains(id); } QString ConfigItem::Context::getId(ConfigObject *obj) const { return _ids.value(obj, ""); } ConfigObject * ConfigItem::Context::getObj(const QString &id) const { return _objects.value(id, nullptr); } bool ConfigItem::Context::add(const QString &id, ConfigObject *obj) { if (_objects.contains(id) || _ids.contains(obj)) return false; _objects.insert(id, obj); _ids.insert(obj, id); return true; } bool ConfigItem::Context::hasTag(const QString &className, const QString &property, const QString &tag) { QString qname = className+"::"+property; return _tagObjects.contains(qname) && _tagObjects[qname].contains(tag); } bool ConfigItem::Context::hasTag(const QString &className, const QString &property, ConfigObject *obj) { QString qname = className+"::"+property; return _tagNames.contains(qname) && _tagNames[qname].contains(obj); } ConfigObject * ConfigItem::Context::getTag(const QString &className, const QString &property, const QString &tag) { //logDebug() << "Request " << tag << " for " << property << " in " << className << "."; QString qname = className+"::"+property; if (! _tagObjects.contains(qname)) return nullptr; return _tagObjects[qname].value(tag, nullptr); } QString ConfigItem::Context::getTag(const QString &className, const QString &property, ConfigObject *obj) { //logDebug() << "Request tag for " << property << " in " << className << "."; QString qname = className+"::"+property; if (! _tagNames.contains(qname)) return nullptr; return _tagNames[qname].value(obj); } void ConfigItem::Context::setTag(const QString &className, const QString &property, const QString &tag, ConfigObject *obj) { //logDebug() << "Register tag " << tag << " for " << property << " in " << className << "."; QString qname = className+"::"+property; if (! _tagObjects.contains(qname)) _tagObjects[qname] = QHash(); _tagObjects[qname].insert(tag, obj); if (! _tagNames.contains(qname)) _tagNames[qname] = QHash(); _tagNames[qname].insert(obj, tag); } /* ********************************************************************************************* * * Implementation of ConfigItem * ********************************************************************************************* */ ConfigItem::ConfigItem(QObject *parent) : QObject(parent) { // pass... } bool ConfigItem::copy(const ConfigItem &other) { // check if other has the same type if (strcmp(other.metaObject()->className(), metaObject()->className())) { logError() << metaObject()->className() << " cannot copy from " << other.metaObject()->className(); return false; } // clear this instance this->clear(); // Iterate over all properties const QMetaObject *meta = metaObject(); for (int p=QObject::staticMetaObject.propertyCount(); ppropertyCount(); p++) { // This property QMetaProperty prop = meta->property(p); // the same property over at other QMetaProperty oprop = other.metaObject()->property(p); // Should never happen if (! prop.isValid()) { logWarn() << "Invalid property " << prop.name() << ". This should not happen."; continue; } // true if the property is a basic type bool isBasicType = ( prop.isEnumType() || (QVariant::Bool==prop.type()) || (QVariant::Int==prop.type()) || (QVariant::UInt==prop.type()) || (QVariant::Double==prop.type()) || (QVariant::String==prop.type()) || (QString("Frequency")==prop.typeName()) || (QString("Interval")==prop.typeName()) ); // If a basic type -> simply copy value if (isBasicType && prop.isWritable() && (prop.type()==oprop.type())) { if (! prop.write(this, oprop.read(&other))) { logError() << "Cannot set property '" << prop.name() << "' of " << this->metaObject()->className() << "."; return false; } } else if (ConfigObjectReference *ref = prop.read(this).value()) { if (! ref->copy(oprop.read(&other).value())) { logError() << "Cannot copy object reference '" << prop.name() << "' of " << this->metaObject()->className() << "."; return false; } } else if (ConfigObjectList *lst = prop.read(this).value()) { if (! lst->copy(*oprop.read(&other).value())) { logError() << "Cannot copy object list '" << prop.name() << "' of " << this->metaObject()->className() << "."; return false; } } else if (ConfigObjectRefList *lst = prop.read(this).value()) { if (! lst->copy(*oprop.read(&other).value())) { logError() << "Cannot copy reference list '" << prop.name() << "' of " << this->metaObject()->className() << "."; return false; } } else if (propIsInstance(prop)) { // If the item is owned by this item if (prop.isWritable()) { // If the owned item is writeable -> clone if set in other if (oprop.read(&other).isNull()) { if (! prop.write(this, QVariant::fromValue(nullptr))) { logError() << "Cannot delete item '" << prop.name() << "' of " << this->metaObject()->className() << "."; return false; } } else { // Clone element form other item ConfigItem *cl = oprop.read(&other).value()->clone(); if (nullptr == cl) { logError() << "Cannot clone item '" << oprop.name() << "' of " << other.metaObject()->className() << "."; return false; } // Write clone if (! prop.write(this, QVariant::fromValue(cl))) { logError() << "Cannot replace item '" << prop.name() << "' of " << this->metaObject()->className() << "."; cl->deleteLater(); return false; } } } else if (! oprop.read(&other).isNull()) { // If the owned item is not writable (must be present) -> copy from other if set if (! prop.read(this).value()->copy(*oprop.read(&other).value())) { logError() << "Cannot copy fixed item '" << prop.name() << "' of " << this->metaObject()->className() << "."; return false; } } } } emit modified(this); return true; } int ConfigItem::compare(const ConfigItem &other) const { // Check if other has the same type if (strcmp(other.metaObject()->className(), metaObject()->className())) return strcmp(metaObject()->className(), other.metaObject()->className()); // Compare by properties const QMetaObject *meta = metaObject(); for (int p=QObject::staticMetaObject.propertyCount(); ppropertyCount(); p++) { // This property QMetaProperty prop = meta->property(p); // the same property over at other QMetaProperty oprop = other.metaObject()->property(p); // Should never happen if (! prop.isValid()) continue; // Handle comparison of basic types if ((prop.isEnumType()) || (QVariant::Bool == prop.type()) || (QVariant::Int == prop.type()) || (QVariant::UInt == prop.type())) { int a=prop.read(this).toInt(), b=oprop.read(&other).toInt(); if (ab) return 1; continue; } if (QVariant::Double == prop.type()) { double a=prop.read(this).toDouble(), b=oprop.read(&other).toDouble(); if (ab) return 1; continue; } if (QVariant::String == prop.type()) { int cmp = QString::compare(prop.read(this).toString(), oprop.read(&other).toString()); if (cmp) return cmp; continue; } if (QString("Frequency") == prop.typeName()) { Frequency a = prop.read(this).value(), b = oprop.read(&other).value(); if (a(), b = oprop.read(&other).value(); if (a()) { int cmp = ref->compare(*oprop.read(&other).value()); if (cmp) return cmp; continue; } if (ConfigObjectList *lst = prop.read(this).value()) { int cmp = lst->compare(*oprop.read(&other).value()); if (cmp) return cmp; continue; } if (propIsInstance(prop)) { ConfigObjectRefList *lst = prop.read(this).value(); int cmp = lst->compare(*oprop.read(&other).value()); if (cmp) return cmp; continue; } if (propIsInstance(prop)) { // If the owned item is writeable -> clone if set in other if (prop.read(this).isNull() && !oprop.read(&other).isNull()) return -1; if (!prop.read(this).isNull() && oprop.read(&other).isNull()) return 1; if (prop.read(this).isNull() && oprop.read(&other).isNull()) continue; int cmp = prop.read(this).value()->compare(*oprop.read(&other).value()); if (cmp) return cmp; continue; } } return 0; } bool ConfigItem::label(ConfigObject::Context &context, const ErrorStack &err) { // Label properties owning config objects, that is of type ConfigObject or ConfigObjectList const QMetaObject *meta = metaObject(); for (int p=QObject::staticMetaObject.propertyCount(); ppropertyCount(); p++) { QMetaProperty prop = meta->property(p); if (! prop.isValid()) continue; if (prop.read(this).value()) { ConfigObjectList *lst = prop.read(this).value(); if (! lst->label(context, err)) return false; } else if (prop.read(this).value()) { ConfigItem *obj = prop.read(this).value(); if (! obj->label(context, err)) return false; } } return true; } YAML::Node ConfigItem::serialize(const Context &context, const ErrorStack &err) { YAML::Node node; if (! populate(node, context, err)) return YAML::Node(); return node; } void ConfigItem::clear() { emit beginClear(); // Delete or clear all object owned by properties, that is ConfigObjectList and ConfigObject const QMetaObject *meta = metaObject(); for (int p=QObject::staticMetaObject.propertyCount(); ppropertyCount(); p++) { QMetaProperty prop = meta->property(p); if (! prop.isValid()) continue; if (propIsInstance(prop) && prop.isWritable()) { if (ConfigItem *item = prop.read(this).value()) item->deleteLater(); prop.write(this, QVariant::fromValue(nullptr)); } else if (ConfigObjectList *lst = prop.read(this).value()) { lst->clear(); } } emit endClear(); } bool ConfigItem::populate(YAML::Node &node, const Context &context, const ErrorStack &err){ // Serialize all properties const QMetaObject *meta = metaObject(); for (int p=QObject::staticMetaObject.propertyCount(); ppropertyCount(); p++) { QMetaProperty prop = meta->property(p); if (! prop.isValid()) continue; if (! prop.isScriptable()) { /*logDebug() << "Do not serialize property '" << prop.name() << "': Marked as not scriptable.";*/ continue; } if (prop.isEnumType()) { QMetaEnum e = prop.enumerator(); QVariant value = prop.read(this); const char *key = e.valueToKey(value.toInt()); if (nullptr == key) { errMsg(err) << "Cannot map value " << value.toUInt() << " to enum " << e.name() << ". Ignore attribute '" << prop.name() << "' but this points to an incompatibility in some codeplug. " << "Consider reporting it to https://github.com/hmatuschek/qdmr/issues."; continue; } node[prop.name()] = key; } else if (QString("bool") == prop.typeName()) { node[prop.name()] = this->property(prop.name()).toBool(); } else if (QString("int") == prop.typeName()) { node[prop.name()] = this->property(prop.name()).toInt(); } else if (QString("uint") == prop.typeName()) { node[prop.name()] = this->property(prop.name()).toUInt(); } else if (QString("double") == prop.typeName()) { node[prop.name()] = this->property(prop.name()).toDouble(); } else if (QString("QString") == prop.typeName()) { node[prop.name()] = this->property(prop.name()).toString().toStdString(); } else if (QString("Frequency") == prop.typeName()) { node[prop.name()] = this->property(prop.name()).value(); } else if (QString("Interval") == prop.typeName()) { node[prop.name()] = this->property(prop.name()).value(); } else if (QString("SelectiveCall") == prop.typeName()) { node[prop.name()] = this->property(prop.name()).value(); } else if (ConfigObjectReference *ref = prop.read(this).value()) { ConfigObject *obj = ref->as(); if (nullptr == obj) continue; if (context.hasTag(prop.enclosingMetaObject()->className(), prop.name(), obj)) { YAML::Node tag(YAML::NodeType::Scalar); tag.SetTag(context.getTag(prop.enclosingMetaObject()->className(), prop.name(), obj).toStdString()); node[prop.name()] = tag; continue; } else if (! context.contains(obj)) { errMsg(err) << "Cannot reference object of type " << obj->metaObject()->className() << " object not labeled."; return false; } node[prop.name()] = context.getId(obj).toStdString(); } else if (ConfigObjectRefList *refs = prop.read(this).value()) { //logDebug() << "Serialize obj ref list w/ " << refs->count() << " elements." ; YAML::Node list = YAML::Node(YAML::NodeType::Sequence); list.SetStyle(YAML::EmitterStyle::Flow); for (int i=0; icount(); i++) { ConfigObject *obj = refs->get(i); if (context.hasTag(prop.enclosingMetaObject()->className(), prop.name(), obj)) { YAML::Node tag(YAML::NodeType::Scalar); tag.SetTag(context.getTag(prop.enclosingMetaObject()->className(), prop.name(), obj).toStdString()); //tag = tag.Tag().substr(1); list.push_back(tag); continue; } else if (! context.contains(obj)) { errMsg(err) << "Cannot reference object of type " << obj->metaObject()->className() << " object not labeled."; return false; } list.push_back(context.getId(obj).toStdString()); } node[prop.name()] = list; } else if (propIsInstance(prop)) { ConfigItem *obj = prop.read(this).value(); // Serialize config objects in-place. if (obj) node[prop.name()] = obj->serialize(context); } else if (ConfigObjectList *lst = prop.read(this).value()) { // Serialize config object lists in-place. node[prop.name()] = lst->serialize(context); } else { logDebug() << "Unhandled property " << prop.name() << " of unknown type " << prop.typeName() << "."; } } return true; } ConfigItem * ConfigItem::allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err) { Q_UNUSED(node); Q_UNUSED(ctx); if (QMetaType::UnknownType == prop.userType()) { errMsg(err) << "Cannot instantiate child of unregistered type " << prop.typeName() << "."; return nullptr; } QMetaType type(prop.userType()); if (! (QMetaType::PointerToQObject & type.flags())) { errMsg(err) << "Cannot instantiate child of non-qobject pointer type " << prop.typeName() << "."; return nullptr; } const QMetaObject *propType = type.metaObject(); /*logDebug() << "Try to instantiate child of type " << prop.typeName() << " for element " << prop.name() << " in " << this->metaObject()->className() << ".";*/ ConfigItem *item = qobject_cast( propType->newInstance(Q_ARG(QObject *,this))); if (! item) { errMsg(err) << "Could not instantiate " << propType->className() << "."; } return item; } bool ConfigItem::parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) if (! node) return false; if (! node.IsMap()) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot parse element: Expected object."; return false; } const QMetaObject *meta = this->metaObject(); for (int p=QObject::staticMetaObject.propertyOffset(); ppropertyCount(); p++) { QMetaProperty prop = meta->property(p); if (! prop.isValid()) continue; // If marked as non-scriptable, skip that property. // It is handled separately or not at all. if (! prop.isScriptable()) continue; /// @todo With Qt 5.15, we can use the REQUIRED flag to check for mandatory properties. /// However, Ubuntu 20.04 (Focal) comes with Qt 5.12. if (prop.isEnumType()) { // If property is not set -> skip if (! node[prop.name()]) continue; // parse & check enum key if (! node[prop.name()].IsScalar()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot parse " << prop.name() << " of " << meta->className() << ": Expected enum key."; return false; } QMetaEnum e = prop.enumerator(); std::string key = node[prop.name()].as(); bool ok=true; int value = e.keyToValue(key.c_str(), &ok); if (! ok) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Unknown key '" << key.c_str() << "' for enum '" << prop.name() << "'. Expected one of " << enumKeys(e).join(", ") << "."; return false; } // finally set property prop.write(this, value); } else if (QString("bool") == prop.typeName()) { // If property is not set -> skip if (! node[prop.name()]) continue; // parse & check type if (! node[prop.name()].IsScalar()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot parse " << prop.name() << " of " << meta->className() << ": Expected boolean value."; return false; } prop.write(this, node[prop.name()].as()); } else if (QString("int") == prop.typeName()) { // If property is not set -> skip if (! node[prop.name()]) continue; // parse & check type if (! node[prop.name()].IsScalar()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot parse " << prop.name() << " of " << meta->className() << ": Expected integer value."; return false; } prop.write(this, node[prop.name()].as()); } else if (QString("uint") == prop.typeName()) { // If property is not set -> skip if (! node[prop.name()]) continue; // parse & check type if (! node[prop.name()].IsScalar()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot parse " << prop.name() << " of " << meta->className() << ": Expected unsigned integer value."; return false; } prop.write(this, node[prop.name()].as()); } else if (QString("double") == prop.typeName()) { // If property is not set -> skip if (! node[prop.name()]) continue; // parse & check type if (! node[prop.name()].IsScalar()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot parse " << prop.name() << " of " << meta->className() << ": Expected floating point value."; return false; } prop.write(this, node[prop.name()].as()); } else if (QString("QString") == prop.typeName()) { // If property is not set -> skip if (! node[prop.name()]) continue; // parse & check type if (! node[prop.name()].IsScalar()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot parse " << prop.name() << " of " << meta->className() << ": Expected string."; return false; } prop.write(this, QString::fromStdString(node[prop.name()].as())); } else if (QString("Frequency") == prop.typeName()) { // If property is not set -> skip if (! node[prop.name()]) continue; // parse & check type if (! node[prop.name()].IsScalar()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot parse " << prop.name() << " of " << meta->className() << ": Expected frequency."; return false; } Frequency f = node[prop.name()].as(); prop.write(this, QVariant::fromValue(f)); } else if (QString("Interval") == prop.typeName()) { // If property is not set -> skip if (! node[prop.name()]) continue; // parse & check type if (! node[prop.name()].IsScalar()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot parse " << prop.name() << " of " << meta->className() << ": Expected interval."; return false; } prop.write(this, QVariant::fromValue(node[prop.name()].as())); } else if (QString("SelectiveCall") == prop.typeName()) { // If property is not set -> skip if ((! node[prop.name()]) || (node[prop.name()].IsNull())) { prop.write(this, QVariant::fromValue(SelectiveCall())); continue; } // parse & check type if ((! node[prop.name()].IsMap()) || (1 != node[prop.name()].size())) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot parse " << prop.name() << " of " << meta->className() << ": Expected selective call."; return false; } prop.write(this, QVariant::fromValue(node[prop.name()].as())); } else if (prop.read(this).value()) { // references are linked later continue; } else if (prop.read(this).value()) { // reference lists are linked later continue; } else if (propIsInstance(prop)) { if (! node[prop.name()]) continue; // check type if (! node[prop.name()].IsMap()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot parse '" << prop.name() << "' of '" << meta->className() << "': Expected instance of '" << QMetaType::metaObjectForType(prop.userType())->className() << "'."; return false; } // Get object ConfigItem *obj = prop.read(this).value(); // If not set and writable -> allocate and set if ((nullptr == obj) && prop.isWritable()) { if (nullptr == (obj = this->allocateChild(prop, node[prop.name()], ctx))) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot allocate " << prop.name() << " of " << meta->className() << "."; return false; } // Set property if (! prop.write(this, QVariant::fromValue(obj))) { if (nullptr == obj->parent()) obj->deleteLater(); errMsg(err) << "Cannot set writable property '" << prop.name() << "' in " << this->metaObject()->className() << "."; return false; } } // parse instance YAML::Node propNode = node[prop.name()]; if (obj && (! obj->parse(propNode, ctx))) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot parse " << prop.name() << " of " << meta->className() << "."; if (nullptr == obj->parent()) obj->deleteLater(); return false; } } else if (prop.read(this).value()) { if (! node[prop.name()]) continue; // check type if (! node[prop.name()].IsSequence()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot parse " << prop.name() << " of " << meta->className() << ": Expected instance of '" << QMetaType::metaObjectForType(prop.userType())->className() << "'."; return false; } // Get list ConfigObjectList *lst = prop.read(this).value(); // If not set and writable -> allocate and set if ((nullptr == lst) && prop.isWritable()) { if (nullptr == (lst = this->allocateChild(prop, node[prop.name()], ctx)->as())) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot allocate list " << prop.name() << " of " << meta->className() << "."; return false; } // Set property if (! prop.write(this, QVariant::fromValue(lst))) { if (nullptr == lst->parent()) lst->deleteLater(); errMsg(err) << "Cannot set writable property '" << prop.name() << "' in " << this->metaObject()->className() << "."; return false; } } // Allocate elements ConfigObject *obj = nullptr; for (YAML::const_iterator it=node[prop.name()].begin(); it!=node[prop.name()].end(); it++) { // allocate element if (nullptr == (obj = lst->allocateChild(*it, ctx, err)->as())) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot allocate child of list."; return false; } // parse element if (obj && (! obj->parse(*it, ctx))) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot parse object of type " << obj->metaObject()->className(); if (nullptr == obj->parent()) obj->deleteLater(); return false; } // add element to list if (-1 == lst->add(obj)) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot add element '" << obj->name() << "' of type " << obj->metaObject()->className() << "' to list."; if (nullptr == obj->parent()) obj->deleteLater(); return false; } } } } return true; } bool ConfigItem::link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) const QMetaObject *meta = this->metaObject(); for (int p=QObject::staticMetaObject.propertyOffset(); ppropertyCount(); p++) { QMetaProperty prop = meta->property(p); if (! prop.isValid()) continue; if (! prop.isScriptable()) { //logDebug() << "Do not link property '" << prop.name() << "': Marked as not scriptable."; continue; } if ((prop.isEnumType()) || (QString("bool") == prop.typeName()) || (QString("int") == prop.typeName()) || (QString("uint") == prop.typeName()) || (QString("double") == prop.typeName()) || (QString("QString") == prop.typeName()) ) { continue; } else if (ConfigObjectReference *ref = prop.read(this).value()) { // If not set -> skip if (! node[prop.name()]) continue; // check type if (! node[prop.name()].IsScalar()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot link " << prop.name() << " of " << meta->className() << ": Expected id."; return false; } // handle tags QString tag = QString::fromStdString(node[prop.name()].Tag()); if ((!node[prop.name()].Scalar().size()) && (!tag.isEmpty())) { if (! ref->set(ctx.getTag(prop.enclosingMetaObject()->className(), prop.name(), tag))) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot link " << prop.name() << " of " << meta->className() << ": Unknown tag " << tag << "."; return false; } continue; } // set reference QString id = QString::fromStdString(node[prop.name()].as()); if (! ctx.contains(id)) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot link reference to '" << id << "', element not defined."; return false; } if (! ref->set(ctx.getObj(id))) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot link " << prop.name() << " of " << meta->className() << ": Cannot set reference."; return false; } /*logDebug() << "Linked reference " << prop.name() << "='" << id << "' to " << ctx.getObj(id)->metaObject()->className() << " '" << ctx.getObj(id)->name() << "'.";*/ } else if (ConfigObjectRefList *lst = prop.read(this).value()) { // If not set -> skip if (! node[prop.name()]) continue; // check type if (! node[prop.name()].IsSequence()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot link " << prop.name() << " of " << meta->className() << ": Expected sequence."; return false; } for (YAML::const_iterator it=node[prop.name()].begin(); it!=node[prop.name()].end(); it++) { if (! it->IsScalar()) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot link " << prop.name() << " of " << meta->className() << ": Expected ID string."; return false; } // check for tags QString tag = QString::fromStdString(it->Tag()); if ((!it->Scalar().size()) && (!tag.isEmpty())) { if (0 > lst->add(ctx.getTag(prop.enclosingMetaObject()->className(), prop.name(), tag))) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot link " << prop.name() << " of " << meta->className() << ": Cannot add reference for tag '" << tag << "'."; return false; } continue; } QString id = QString::fromStdString(it->as()); if (! ctx.contains(id)) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot link " << prop.name() << " of " << meta->className() << ": Reference '" << id << "' not defined."; return false; } if (0 > lst->add(ctx.getObj(id))) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot link " << prop.name() << " of " << meta->className() << ": Cannot add reference to '" << id << "' to list."; return false; } } } else if (ConfigItem *obj = prop.read(this).value()) { // If not set -> skip if (! node[prop.name()]) continue; // check type if (! node[prop.name()].IsMap()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot link " << prop.name() << " of " << meta->className() << ": Expected object."; return false; } if (! obj->link(node[prop.name()], ctx, err)) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot link " << prop.name() << " of " << meta->className() << "."; return false; } } else if (ConfigObjectList *lst = prop.read(this).value()) { // If not set -> skip if (! node[prop.name()]) continue; // check type if (! node[prop.name()].IsSequence()) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot link " << prop.name() << " of " << meta->className() << ": Expected sequence."; return false; } if (! lst->link(node[prop.name()], ctx, err)) { errMsg(err) << node[prop.name()].Mark().line << ":" << node[prop.name()].Mark().column << ": Cannot link " << prop.name() << " of " << meta->className() << "."; return false; } } } return true; } const Config * ConfigItem::config() const { if (nullptr == parent()) return nullptr; if (ConfigItem *item = qobject_cast(parent())) return item->config(); if (ConfigObjectList *lst = qobject_cast(parent())) return lst->config(); return nullptr; } void ConfigItem::findItemsOfTypes(const QStringList &typeNames, QSet &items) const { // Do not check yourself const QMetaObject *meta = metaObject(); // Visit all properties for (int p=QObject::staticMetaObject.propertyCount(); ppropertyCount(); p++) { QMetaProperty prop = meta->property(p); if (! prop.isValid()) continue; if (! prop.isReadable()) continue; if (ConfigItem *obj = prop.read(this).value()) { if (isInstanceOf(obj, typeNames)) items.insert(obj); obj->findItemsOfTypes(typeNames, items); } else if (ConfigObjectList *lst = prop.read(this).value()) { lst->findItemsOfTypes(typeNames, items); } } } bool ConfigItem::hasDescription() const { const QMetaObject *meta = metaObject(); return (0 <= meta->indexOfClassInfo("description")); } bool ConfigItem::hasLongDescription() const { const QMetaObject *meta = metaObject(); return (0 <= meta->indexOfClassInfo("longDescription")); } bool ConfigItem::hasDescription(const QMetaProperty &prop) const { if (! prop.isValid()) return false; QString infoName = QString("%1Description").arg(prop.name()); const QMetaObject *meta = metaObject(); return (0 <= meta->indexOfClassInfo(infoName.toLocal8Bit().constData())); } bool ConfigItem::hasLongDescription(const QMetaProperty &prop) const { if (! prop.isValid()) return false; QString infoName = QString("%1LongDescription").arg(prop.name()); const QMetaObject *meta = metaObject(); return (0 <= meta->indexOfClassInfo(infoName.toLocal8Bit().constData())); } QString ConfigItem::description() const { if (! hasDescription()) return metaObject()->className(); const QMetaObject *meta = metaObject(); return meta->classInfo(meta->indexOfClassInfo("description")).value(); } QString ConfigItem::longDescription() const { if (! hasLongDescription()) return QString(); const QMetaObject *meta = metaObject(); return meta->classInfo(meta->indexOfClassInfo("longDescription")).value(); } QString ConfigItem::description(const QMetaProperty &prop) const { if (! hasDescription(prop)) return QString(); QString infoName = QString("%1Description").arg(prop.name()); const QMetaObject *meta = metaObject(); return meta->classInfo(meta->indexOfClassInfo(infoName.toLocal8Bit().constData())).value(); } QString ConfigItem::longDescription(const QMetaProperty &prop) const { if (! hasLongDescription(prop)) return QString(); QString infoName = QString("%1LongDescription").arg(prop.name()); const QMetaObject *meta = metaObject(); return meta->classInfo(meta->indexOfClassInfo(infoName.toLocal8Bit().constData())).value(); } /* ********************************************************************************************* * * Implementation of ConfigObject * ********************************************************************************************* */ ConfigObject::ConfigObject(QObject *parent) : ConfigItem(parent), _name() { // pass... } ConfigObject::ConfigObject(const QString &name, QObject *parent) : ConfigItem(parent), _name(name) { // pass... } const QString & ConfigObject::name() const { return _name; } void ConfigObject::setName(const QString &name) { if (name.simplified().isEmpty() || (_name == name.simplified())) return; _name = name; emit modified(this); } QString ConfigObject::idPrefix() const { return findIdPrefix(this->metaObject()); } bool ConfigObject::label(ConfigObject::Context &context, const ErrorStack &err) { unsigned n=1; QString prefix = this->idPrefix(); QString id=QString("%1%2").arg(prefix).arg(n); while (context.contains(id)) { id=QString("%1%2").arg(prefix).arg(++n); } if (! context.add(id, this)) { if (context.contains(this)) errMsg(err) << "Object already in context with id '" << context.getId(this) << "'."; errMsg(err) << "Cannot add element '" << id << "' to context."; return false; } if (! ConfigItem::label(context, err)) return false; return true; } bool ConfigObject::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) { if (! node["id"]) { logWarn() << node.Mark().line << ":" << node.Mark().column << ": No id specified for " << metaObject()->className() << ". Object cannot be referenced later."; } else { QString id = QString::fromStdString(node["id"].as()); if (! ctx.add(id, this)) { errMsg(err) << node["id"].Mark().line << ":" << node["id"].Mark().column << ": Cannot register ID '" << id << "'."; return false; } } return ConfigItem::parse(node, ctx); } bool ConfigObject::populate(YAML::Node &node, const Context &context, const ErrorStack &err) { if (context.contains(this)) node["id"] = context.getId(this).toStdString(); return ConfigItem::populate(node, context, err); } QString ConfigObject::findIdPrefix(const QMetaObject *meta) { for (int i=meta->classInfoOffset(); iclassInfoCount(); i++) { if (0 == strcmp("IdPrefix", meta->classInfo(i).name())) { return meta->classInfo(i).value(); } } if (meta->superClass()) return findIdPrefix(meta->superClass()); return ""; } /* ********************************************************************************************* * * Implementation of ConfigExtension * ********************************************************************************************* */ ConfigExtension::ConfigExtension(QObject *parent) : ConfigItem(parent) { // pass... } /* ********************************************************************************************* * * Implementation of AbstractConfigObjectList * ********************************************************************************************* */ AbstractConfigObjectList::AbstractConfigObjectList(const QMetaObject &elementType, QObject *parent) : QObject(parent), _elementTypes(), _items() { _elementTypes.append(elementType); } AbstractConfigObjectList::AbstractConfigObjectList(const std::initializer_list &elementTypes, QObject *parent) : QObject(parent), _elementTypes(elementTypes), _items() { // pass... } bool AbstractConfigObjectList::copy(const AbstractConfigObjectList &other) { this->clear(); _elementTypes = other._elementTypes; foreach (ConfigObject *item, other._items) add(item); return true; } int AbstractConfigObjectList::count() const { return _items.count(); } int AbstractConfigObjectList::indexOf(ConfigObject *obj) const { return _items.indexOf(obj); } void AbstractConfigObjectList::clear() { for (int i=(count()-1); i>=0; i--) { _items.pop_back(); emit elementRemoved(i); } } const Config * AbstractConfigObjectList::config() const { if (nullptr == parent()) return nullptr; if (ConfigItem *item = qobject_cast(parent())) return item->config(); return nullptr; } void AbstractConfigObjectList::findItemsOfTypes(const QStringList &typeNames, QSet &items) const { foreach (ConfigObject *obj, _items) { if (isInstanceOf(obj, typeNames)) items.insert(obj); obj->findItemsOfTypes(typeNames, items); } } QList AbstractConfigObjectList::findItemsByName(const QString name) const { QList items; foreach (ConfigObject *obj, _items) { if (obj->name() == name) items.append(obj); } return items; } bool AbstractConfigObjectList::has(ConfigObject *obj) const { return 0 <= indexOf(obj); } ConfigObject * AbstractConfigObjectList::get(int idx) const { return _items.value(idx, nullptr); } int AbstractConfigObjectList::add(ConfigObject *obj, int row, bool unique) { // Ignore nullptr if (nullptr == obj) return -1; // If already in list -> ignore if (unique && (0 <= indexOf(obj))) return -1; if (-1 == row) row = _items.size(); // Check type bool matchesType = false; foreach (const QMetaObject &type, _elementTypes) { if (obj->inherits(type.className())) { matchesType = true; break; } } if (! matchesType) { logError() << "Cannot add element of type " << obj->metaObject()->className() << " to list, expected instances of " << classNames().join(", "); return -1; } _items.insert(row, obj); // Otherwise connect to object connect(obj, SIGNAL(destroyed(QObject*)), this, SLOT(onElementDeleted(QObject*))); connect(obj, SIGNAL(modified(ConfigItem*)), this, SLOT(onElementModified(ConfigItem*))); emit elementAdded(row); return row; } int AbstractConfigObjectList::replace(ConfigObject *obj, int row, bool unique) { // Ignore nullptr if (nullptr == obj) return -1; // Check index if (row >= count()) return -1; // Check if self-replacement if (row == indexOf(obj)) return indexOf(obj); // If already in list -> ignore if (unique && (0 <= indexOf(obj))) return -1; // Check type bool matchesType = false; foreach (const QMetaObject &type, _elementTypes) { if (obj->inherits(type.className())) { matchesType = true; break; } } if (! matchesType) { logError() << "Cannot add element of type " << obj->metaObject()->className() << " to list, expected instances of " << classNames().join(", "); return -1; } // Remove present element ConfigObject *oldobj = _items.at(row); _items.remove(row, 1); emit elementRemoved(row); disconnect(oldobj, nullptr, this, nullptr); _items.insert(row, obj); // connect to object connect(obj, SIGNAL(destroyed(QObject*)), this, SLOT(onElementDeleted(QObject*))); connect(obj, SIGNAL(modified(ConfigItem*)), this, SLOT(onElementModified(ConfigItem*))); emit elementAdded(row); return row; } bool AbstractConfigObjectList::take(ConfigObject *obj) { // Ignore nullptr if (nullptr == obj) return false; int idx = indexOf(obj); if (0 > idx) return false; _items.remove(idx, 1); emit elementRemoved(idx); // Otherwise disconnect from disconnect(obj, nullptr, this, nullptr); return true; } bool AbstractConfigObjectList::del(ConfigObject *obj) { return take(obj); } bool AbstractConfigObjectList::moveUp(int row) { if ((row <= 0) || (row>=count())) return false; std::swap(_items[row-1], _items[row]); return true; } bool AbstractConfigObjectList::moveUp(int first, int last) { if ((first <= 0) || (last>=count())) return false; for (int row=first; row<=last; row++) std::swap(_items[row-1], _items[row]); return true; } bool AbstractConfigObjectList::moveDown(int row) { if ((row >= (count()-1)) || (0 > row)) return false; std::swap(_items[row+1], _items[row]); return true; } bool AbstractConfigObjectList::moveDown(int first, int last) { if ((last >= (count()-1)) || (0 > first)) return false; for (int row=last; row>=first; row--) std::swap(_items[row+1], _items[row]); return true; } bool AbstractConfigObjectList::move(int source, int count, int destination) { if ((0 == count) || (source == destination)) return true; if ((source+count)>_items.size()) return false; if (source > destination) { // move up for (int take=source, put=destination, i=0; i & AbstractConfigObjectList::elementTypes() const { return _elementTypes; } QStringList AbstractConfigObjectList::classNames() const { QStringList cls; foreach (const QMetaObject &type, _elementTypes) { cls.append(type.className()); } return cls; } void AbstractConfigObjectList::onElementModified(ConfigItem *obj) { int idx = indexOf(obj->as()); if (0 <= idx) emit elementModified(idx); } void AbstractConfigObjectList::onElementDeleted(QObject *obj) { // Use reinterpret cast here as the obj may already be destroyed and this all RTTI freed. // We just use the pointer address to remove the element here. int idx = indexOf(reinterpret_cast(obj)); if (0 <= idx) { _items.remove(idx); emit elementRemoved(idx); } } /* ********************************************************************************************* * * Implementation of ConfigObjectList * ********************************************************************************************* */ ConfigObjectList::ConfigObjectList(const QMetaObject &elementType, QObject *parent) : AbstractConfigObjectList(elementType, parent) { // pass... } ConfigObjectList::ConfigObjectList(const std::initializer_list &elementTypes, QObject *parent) : AbstractConfigObjectList(elementTypes, parent) { // pass... } bool ConfigObjectList::label(ConfigItem::Context &context, const ErrorStack &err) { foreach (ConfigItem *obj, _items) { if (! obj->label(context, err)) return false; } return true; } YAML::Node ConfigObjectList::serialize(const ConfigItem::Context &context, const ErrorStack &err) { YAML::Node list(YAML::NodeType::Sequence); foreach (ConfigItem *obj, _items) { YAML::Node node = obj->serialize(context, err); if (node.IsNull()) return node; list.push_back(node); } return list; } bool ConfigObjectList::parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { if (! node) return false; if (!node.IsSequence()) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot parse list: Expected list."; return false; } for (YAML::Node::const_iterator it=node.begin(); it!=node.end(); it++) { // Create object for node ConfigItem *element = allocateChild(*it, ctx); if ((nullptr == element) || (!element->is())) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot parse list."; return false; } if (! element->parse(*it, ctx, err)) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot parse list."; element->deleteLater(); return false; } if (0 > add(element->as())) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot add element to list."; element->deleteLater(); return false; } } return true; } bool ConfigObjectList::link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err) { if (! node) return false; if (!node.IsSequence()) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot parse list: Expected list."; return false; } int i = 0; YAML::Node::const_iterator it=node.begin(); for (; it!=node.end(); it++, i++) { // Create object for node ConfigItem *element = get(i); if (nullptr == element) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot link list, " << i << "-th element not present."; return false; } if (! element->link(*it, ctx, err)) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot link list."; return false; } } return true; } int ConfigObjectList::add(ConfigObject *obj, int row, bool unique) { if (0 <= (row = AbstractConfigObjectList::add(obj, row, unique))) obj->setParent(this); return row; } bool ConfigObjectList::take(ConfigObject *obj) { if (AbstractConfigObjectList::take(obj)) obj->setParent(nullptr); return true; } bool ConfigObjectList::del(ConfigObject *obj) { if (AbstractConfigObjectList::del(obj)) delete obj; return true; } void ConfigObjectList::clear() { QVector items = _items; AbstractConfigObjectList::clear(); for (int i=0; ideleteLater(); } bool ConfigObjectList::copy(const AbstractConfigObjectList &other) { clear(); _elementTypes = other.elementTypes(); for (int i=0; iclone()->as()); return true; } int ConfigObjectList::compare(const ConfigObjectList &other) const { if (count() < other.count()) return -1; if (count() > other.count()) return 1; for (int i=0; iget(i)->compare(*other.get(i)); if (cmp) return cmp; } return 0; } /* ********************************************************************************************* * * Implementation of ConfigObjectRefList * ********************************************************************************************* */ ConfigObjectRefList::ConfigObjectRefList(const QMetaObject &elementType, QObject *parent) : AbstractConfigObjectList(elementType, parent) { // pass... } ConfigObjectRefList::ConfigObjectRefList(const std::initializer_list &elementTypes, QObject *parent) : AbstractConfigObjectList(elementTypes, parent) { // pass... } bool ConfigObjectRefList::label(ConfigItem::Context &context, const ErrorStack &err) { Q_UNUSED(context); Q_UNUSED(err); // pass... return true; } YAML::Node ConfigObjectRefList::serialize(const ConfigItem::Context &context, const ErrorStack &err) { YAML::Node list(YAML::NodeType::Sequence); foreach (ConfigObject *obj, _items) { if (! context.contains(obj)) { errMsg(err) << "Cannot serialized ref list: Object '" << obj->name() << "' not in context!"; return YAML::Node(); } list.push_back(context.getId(obj).toStdString()); } return list; } int ConfigObjectRefList::compare(const ConfigObjectRefList &other) const { if (count() < other.count()) return -1; if (count() > other.count()) return 1; for (int i=0; icompare(*other.get(i)); if (cmp) return cmp; } return 0; } qdmr-0.12.3/lib/configobject.hh000066400000000000000000000373621501654372000163010ustar00rootroot00000000000000#ifndef CONFIGOBJECT_HH #define CONFIGOBJECT_HH #include #include #include #include #include #include #include "errorstack.hh" // Forward declaration class Config; class ConfigObject; class ConfigExtension; /** Helper function to test property type. */ template bool propIsInstance(const QMetaProperty &prop) { if (QMetaType::UnknownType == prop.userType()) return false; QMetaType type(prop.userType()); if (! (QMetaType::PointerToQObject & type.flags())) return false; const QMetaObject *propType = type.metaObject(); for (; nullptr != propType; propType = propType->superClass()) { if (0==strcmp(T::staticMetaObject.className(), propType->className())) return true; } return false; } /** Base class for all configuration objects (channels, zones, contacts, etc). * * @ingroup conf */ class ConfigItem : public QObject { Q_OBJECT public: /** Parse context for config objects. During serialization, each config object gets an * ID assigned. When reading the config, these IDs must be matched back to the corresponding * objects. This is done using this context. */ class Context { public: /** Empty constructor. */ Context(); /** Destructor. */ virtual ~Context(); /** Returns the read version string. */ const QString &version() const; /** Sets the version string. */ void setVersion(const QString &ver); /** Returns @c true, if the context contains the given object. */ virtual bool contains(ConfigObject *obj) const; /** Returns @c true, if the context contains the given ID. */ virtual bool contains(const QString &id) const; /** Returns ID of the given object. */ virtual QString getId(ConfigObject *obj) const; /** Returns the object for the given ID. */ virtual ConfigObject *getObj(const QString &id) const; /** Associates the given object with the given ID. */ virtual bool add(const QString &id, ConfigObject *); /** Returns @c true if the property of the class has the specified tag associated. */ static bool hasTag(const QString &className, const QString &property, const QString &tag); /** Returns @c true if the property of the class has the specified object as a tag associated. */ static bool hasTag(const QString &className, const QString &property, ConfigObject *obj); /** Returns the object associated with the tag for the property of the class. */ static ConfigObject *getTag(const QString &className, const QString &property, const QString &tag); /** Returns the tag associated with the object for the property of the class. */ static QString getTag(const QString &className, const QString &property, ConfigObject *obj); /** Associates the given object with the tag for the property of the given class. */ static void setTag(const QString &className, const QString &property, const QString &tag, ConfigObject *obj); protected: /** The version string. */ QString _version; /** ID->OBJ look-up table. */ QHash _objects; /** OBJ->ID look-up table. */ QHash _ids; /** Maps tags to singleton objects. */ static QHash> _tagObjects; /** Maps singleton objects to tags. */ static QHash> _tagNames; }; protected: /** Hidden constructor. * @param parent Specifies the QObject parent. */ explicit ConfigItem(QObject *parent = nullptr); public: /** Copies the given item into this one. * @returns @c true if copying was successful and false otherwise. The two items must be of the * same type (obviously). */ virtual bool copy(const ConfigItem &other); /** Clones this item. */ virtual ConfigItem *clone() const = 0; /** Compares the items. * * This method returns 0 if the two items are equivalent and -1, 1 otherwise. The established * order is somewhat arbitrary. * * @returns 0 if the two items are equivalent, -1 or 1 otherwise.*/ virtual int compare(const ConfigItem &other) const; public: /** Recursively labels the config object. * Does not assign a label if the @c idBase passed to the constructor is empty. */ virtual bool label(Context &context, const ErrorStack &err=ErrorStack()); /** Recursively serializes the configuration to YAML nodes. * The complete configuration must be labeled first. */ virtual YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack()); /** Allocates an instance for the given property on the given YAML node. * This is usually done automatically based on the meta-type of the property. To be able to * instantiate the item, its default constructor must be Q_INVOKABLE. */ virtual ConfigItem *allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack()); /** Parses the given YAML node, updates the given object and updates the given context (IDs). */ virtual bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack()); /** Links the given object to the rest of the codeplug using the given context. */ virtual bool link(const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack()); /** Clears the config object. */ virtual void clear(); /** Returns the config, the item belongs to or @c nullptr if not part of a config. */ virtual const Config *config() const; /** Searches the config tree to find all instances of the given type names. */ virtual void findItemsOfTypes(const QStringList &typeNames, QSet &items) const; /** Returns @c true if this object is of class @c Object. */ template bool is() const { return nullptr != qobject_cast(this); } /** Casts this object to the given type. */ template const Object *as() const { return qobject_cast(this); } /** Casts this object to the given type. */ template Object *as() { return qobject_cast(this); } /** Returns @c true if there is a class info "description" for this instance. */ bool hasDescription() const; /** Returns @c true if there is a class info "longDescription" for this instance. */ bool hasLongDescription() const; /** Returns @c true if there is a class info "[PropertyName]Description" for the given property. */ bool hasDescription(const QMetaProperty &prop) const; /** Returns @c true if there is a class info "[PropertyName]LongDescription" for the given property. */ bool hasLongDescription(const QMetaProperty &prop) const; /** Returns the description of this instance if set by a class info. */ QString description() const; /** Returns the long description of this instance if set by a class info. */ QString longDescription() const; /** Returns the description of property if set by a class info. */ QString description(const QMetaProperty &prop) const; /** Returns the long description of property if set by a class info. */ QString longDescription(const QMetaProperty &prop) const; protected: /** Recursively serializes the configuration to YAML nodes. * The complete configuration must be labeled first. */ virtual bool populate(YAML::Node &node, const Context &context, const ErrorStack &err=ErrorStack()); signals: /** Gets emitted once the config object is modified. * The instance passed is the modified item, this event is passed up the config tree. */ void modified(ConfigItem *obj); /** Gets emitted before clearing the item. */ void beginClear(); /** Gets emitted after clearing the item. */ void endClear(); }; /** Base class of all labeled and named objects. * @ingroup config */ class ConfigObject: public ConfigItem { Q_OBJECT /** The name of the object. */ Q_PROPERTY(QString name READ name WRITE setName) /** Specifies the prefix for every ID assigned to every object during serialization. */ Q_CLASSINFO("IdPrefix", "obj") protected: /** Hidden constructor. * @param parent Specifies the QObject parent. */ ConfigObject(QObject *parent = nullptr); /** Hidden constructor. * @param name Name of the object. * @param parent Specifies the QObject parent. */ ConfigObject(const QString &name, QObject *parent = nullptr); public: /** Returns the name of the object. */ virtual const QString &name() const; /** Sets the name of the object. */ virtual void setName(const QString &name); public: /** Returns the ID prefix for this object. */ QString idPrefix() const; bool label(Context &context, const ErrorStack &err=ErrorStack()); bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack()); protected: virtual bool populate(YAML::Node &node, const Context &context, const ErrorStack &err=ErrorStack()); /** Helper to find the @c IdPrefix class info in the class hierarchy. */ static QString findIdPrefix(const QMetaObject* meta); protected: /** Holds the name of the object. */ QString _name; }; /** Base class of all device/vendor specific confiuration extensions. * This class already implements the serialization of all @c QMetaObject * properties. */ class ConfigExtension : public ConfigItem { Q_OBJECT protected: /** Hidden constructor. */ explicit ConfigExtension(QObject *parent=nullptr); }; /** Generic list class for config objects. * @ingroup config */ class AbstractConfigObjectList: public QObject { Q_OBJECT protected: /** Hidden constructor. */ explicit AbstractConfigObjectList(const QMetaObject &elementTypes=ConfigObject::staticMetaObject, QObject *parent = nullptr); /** Hidden constructor from initializer list. */ AbstractConfigObjectList(const std::initializer_list &elementTypes, QObject *parent=nullptr); public: /** Copies all elements from @c other to this list. */ virtual bool copy(const AbstractConfigObjectList &other); /** Recursively labels the config object. */ virtual bool label(ConfigItem::Context &context, const ErrorStack &err=ErrorStack()) = 0; /** Recursively serializes the configuration to YAML nodes. * The complete configuration must be labeled first. */ virtual YAML::Node serialize(const ConfigItem::Context &context, const ErrorStack &err=ErrorStack()) = 0; /** Returns the number of elements in the list. */ virtual int count() const; /** Returns the index of the given object within the list. */ virtual int indexOf(ConfigObject *obj) const; /** Clears the list. */ virtual void clear(); /** Returns the config object, this list belongs to. */ virtual const Config *config() const; /** Searches the config tree to find all instances of the given type names. */ virtual void findItemsOfTypes(const QStringList &typeNames, QSet &items) const; /** Searches the list for objects with the given name. */ virtual QList findItemsByName(const QString name) const; /** Returns @c true, if the list contains the given object. */ virtual bool has(ConfigObject *obj) const; /** Returns the list element at the given index or @c nullptr if out of bounds. */ virtual ConfigObject *get(int idx) const; /** Adds an element to the list. */ virtual int add(ConfigObject *obj, int row=-1, bool unique=true); /** Replaces an element in the list. */ virtual int replace(ConfigObject *obj, int row, bool unique=true); /** Removes an element from the list. */ virtual bool take(ConfigObject *obj); /** Removes an element from the list (and deletes it if owned). */ virtual bool del(ConfigObject *obj); /** Moves an object at index @c idx one step up. */ virtual bool moveUp(int idx); /** Moves objects at [first, last] one step up. */ virtual bool moveUp(int first, int last); /** Moves an object at index @c idx one step down. */ virtual bool moveDown(int idx); /** Moves objects [first, last] one step down. */ virtual bool moveDown(int first, int last); /** Moves the given source range to the destination index. * The destination index is given before the movement. That is, if elements 0 & 1 are moved to * indices 1 & 2, call @c move(0,2, 2) */ virtual bool move(int source, int count, int destination); /** Returns the element type for this list. */ const QList &elementTypes() const; /** Returns a list of all class names. */ QStringList classNames() const; signals: /** Gets emitted if an element was added to the list. */ void elementAdded(int idx); /** Gets emitted if one of the lists elements gets modified. */ void elementModified(int idx); /** Gets emitted if one of the lists elements gets deleted. */ void elementRemoved(int idx); private slots: /** Internal used callback to handle modified elements. */ void onElementModified(ConfigItem *obj); /** Internal used callback to handle deleted elements. */ void onElementDeleted(QObject *obj); protected: /** Holds the static QMetaObject of the element type. */ QList _elementTypes; /** Holds the list items. */ QVector _items; }; /** List class for config objects. * This list owns the config objects it contains. See @c ConfigObjectRefList for a list of weak * references. * @ingroup config */ class ConfigObjectList: public AbstractConfigObjectList { Q_OBJECT protected: /** Hidden constructor. */ explicit ConfigObjectList(const QMetaObject &elementTypes=ConfigItem::staticMetaObject, QObject *parent = nullptr); /** Hidden constructor from initializer list. */ ConfigObjectList(const std::initializer_list &elementTypes, QObject *parent=nullptr); public: int add(ConfigObject *obj, int row=-1, bool unique=true); bool take(ConfigObject *obj); bool del(ConfigObject *obj); void clear(); bool copy(const AbstractConfigObjectList &other); /** Compares the object lists. * * This method returns 0 if the two lists are equivalent and -1, 1 otherwise. The established * order is somewhat arbitrary. * * @returns 0 if the two lists are equivalent, -1 or 1 otherwise.*/ virtual int compare(const ConfigObjectList &other) const; /** Allocates a member objects for the given YAML node. */ virtual ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Parses the list from the YAML node. */ virtual bool parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); /** Links the list from the given YAML node. */ virtual bool link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); bool label(ConfigItem::Context &context, const ErrorStack &err=ErrorStack()); YAML::Node serialize(const ConfigItem::Context &context, const ErrorStack &err=ErrorStack()); }; /** List class for config objects. * This list only references the config objects, see @c ConfigObjectList for a list that owns the * config objects. * @ingroup config */ class ConfigObjectRefList: public AbstractConfigObjectList { Q_OBJECT protected: /** Hidden constructor. */ explicit ConfigObjectRefList(const QMetaObject &elementTypes=ConfigObject::staticMetaObject, QObject *parent = nullptr); /** Hidden constructor from initializer list. */ ConfigObjectRefList(const std::initializer_list &elementTypes, QObject *parent=nullptr); public: bool label(ConfigItem::Context &context, const ErrorStack &err=ErrorStack()); YAML::Node serialize(const ConfigItem::Context &context, const ErrorStack &err=ErrorStack()); /** Compares the object ref lists. * * This method returns 0 if the two lists are equivalent and -1, 1 otherwise. The established * order is somewhat arbitrary. * * @returns 0 if the two lists are equivalent, -1 or 1 otherwise.*/ virtual int compare(const ConfigObjectRefList &other) const; }; #endif // CONFIGOBJECT_HH qdmr-0.12.3/lib/configreference.cc000066400000000000000000000237321501654372000167530ustar00rootroot00000000000000#include "configreference.hh" #include "logger.hh" #include "contact.hh" #include "channel.hh" #include "scanlist.hh" #include "gpssystem.hh" #include "radioid.hh" #include "rxgrouplist.hh" #include "roamingzone.hh" #include "zone.hh" #include "encryptionextension.hh" /* ********************************************************************************************* * * Implementation of ConfigObjectReference * ********************************************************************************************* */ ConfigObjectReference::ConfigObjectReference(const QMetaObject &elementType, QObject *parent) : QObject(parent), _elementTypes(), _object(nullptr) { _elementTypes.append(elementType.className()); } bool ConfigObjectReference::isNull() const { return nullptr == _object; } void ConfigObjectReference::clear() { if (_object) { disconnect(_object, SIGNAL(destroyed(QObject*)), this, SLOT(onReferenceDeleted(QObject*))); emit modified(); } _object = nullptr; } bool ConfigObjectReference::set(ConfigObject *object) { if (_object) disconnect(_object, SIGNAL(destroyed(QObject*)), this, SLOT(onReferenceDeleted(QObject*))); if (nullptr == object) { _object = nullptr; return true; } // Check type bool typeCheck = false; foreach (const QString &cname, _elementTypes) { if (object->inherits(cname.toLatin1().constData())) { typeCheck = true; break; } } if (! typeCheck) { logError() << "Cannot reference element of type " << object->metaObject()->className() << ", expected instance of " << _elementTypes.join(", "); return false; } _object = object; if (_object) connect(_object, &QObject::destroyed, this, &ConfigObjectReference::onReferenceDeleted, Qt::DirectConnection); emit modified(); return true; } bool ConfigObjectReference::copy(const ConfigObjectReference *ref) { clear(); if (nullptr == ref) return true; return set(ref->_object); } int ConfigObjectReference::compare(const ConfigObjectReference &other) const { if (isNull() && other.isNull()) return 0; if (!isNull() && other.isNull()) return 1; if (isNull() && !other.isNull()) return -1; return this->_object->compare(*other._object); } bool ConfigObjectReference::allow(const QMetaObject *elementType) { if (! _elementTypes.contains(elementType->className())) _elementTypes.append(elementType->className()); return true; } const QStringList & ConfigObjectReference::elementTypeNames() const { return _elementTypes; } void ConfigObjectReference::onReferenceDeleted(QObject *obj) { // Check if destroyed obj is referenced one. if (_object != reinterpret_cast(obj)) return; // If it is _object = nullptr; emit modified(); } /* ********************************************************************************************* * * Implementation of ContactReference * ********************************************************************************************* */ ContactReference::ContactReference(const QMetaObject &elementType, QObject *parent) : ConfigObjectReference(elementType, parent) { // pass... } ContactReference::ContactReference(QObject *parent) : ConfigObjectReference(Contact::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of DMRContactReference * ********************************************************************************************* */ DMRContactReference::DMRContactReference(QObject *parent) : ContactReference(DMRContact::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of DMRContactRefList * ********************************************************************************************* */ DMRContactRefList::DMRContactRefList(QObject *parent) : ConfigObjectRefList(DMRContact::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of ChannelReference * ********************************************************************************************* */ ChannelReference::ChannelReference(const QMetaObject &elementType, QObject *parent) : ConfigObjectReference(elementType, parent) { // pass... } ChannelReference::ChannelReference(QObject *parent) : ConfigObjectReference(Channel::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of DMRChannelReference * ********************************************************************************************* */ DMRChannelReference::DMRChannelReference(QObject *parent) : ChannelReference(DMRChannel::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of FMChannelReference * ********************************************************************************************* */ FMChannelReference::FMChannelReference(QObject *parent) : ChannelReference(FMChannel::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of ChannelRefList * ********************************************************************************************* */ ChannelRefList::ChannelRefList(const QMetaObject &elementType, QObject *parent) : ConfigObjectRefList(elementType, parent) { // pass... } ChannelRefList::ChannelRefList(QObject *parent) : ConfigObjectRefList(Channel::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of DMRChannelRefList * ********************************************************************************************* */ DMRChannelRefList::DMRChannelRefList(QObject *parent) : ChannelRefList(DMRChannel::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of RoamingChannelRefList * ********************************************************************************************* */ RoamingChannelRefList::RoamingChannelRefList(QObject *parent) : ConfigObjectRefList({RoamingChannel::staticMetaObject}, parent) { // pass... } /* ********************************************************************************************* * * Implementation of ScanListReference * ********************************************************************************************* */ ScanListReference::ScanListReference(QObject *parent) : ConfigObjectReference(ScanList::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of PositioningSystemReference * ********************************************************************************************* */ PositioningSystemReference::PositioningSystemReference(const QMetaObject &elementType, QObject *parent) : ConfigObjectReference(elementType, parent) { // pass... } PositioningSystemReference::PositioningSystemReference(QObject *parent) : ConfigObjectReference(PositioningSystem::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of APRSSystemReference * ********************************************************************************************* */ APRSSystemReference::APRSSystemReference(QObject *parent) : PositioningSystemReference(APRSSystem::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of GPSSystemReference * ********************************************************************************************* */ GPSSystemReference::GPSSystemReference(QObject *parent) : PositioningSystemReference(APRSSystem::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of RadioIDReference * ********************************************************************************************* */ DMRRadioIDReference::DMRRadioIDReference(QObject *parent) : ConfigObjectReference(DMRRadioID::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of GroupListReference * ********************************************************************************************* */ GroupListReference::GroupListReference(QObject *parent) : ConfigObjectReference(RXGroupList::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of RoamingZoneReference * ********************************************************************************************* */ RoamingZoneReference::RoamingZoneReference(QObject *parent) : ConfigObjectReference(RoamingZone::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of EncryptionKeyReference * ********************************************************************************************* */ EncryptionKeyReference::EncryptionKeyReference(QObject *parent) : ConfigObjectReference(EncryptionKey::staticMetaObject, parent) { // pass... } /* ********************************************************************************************* * * Implementation of ZoneReference * ********************************************************************************************* */ ZoneReference::ZoneReference(QObject *parent) : ConfigObjectReference(Zone::staticMetaObject, parent) { // pass... } qdmr-0.12.3/lib/configreference.hh000066400000000000000000000154611501654372000167650ustar00rootroot00000000000000#ifndef CONFIGREFERENCE_HH #define CONFIGREFERENCE_HH #include "configobject.hh" #include class Channel; class DMRChannel; class FMChannel; class ScanList; class EncryptionKey; /** Implements a reference to a config object. * This class is only used to implement the automatic generation/parsing of the YAML codeplug files. * @ingroup conf */ class ConfigObjectReference: public QObject { Q_OBJECT protected: /** Hidden constructor. */ ConfigObjectReference(const QMetaObject &elementType=ConfigObject::staticMetaObject, QObject *parent = nullptr); public: /** Returns @c true if the reference is null. * That is, if there is no object referenced. */ bool isNull() const; /** Resets the reference. * Same as @c set(nullptr). */ virtual void clear(); /** Sets the reference. * If set to @c nullptr, the reference gets cleared. */ virtual bool set(ConfigObject *object); /** Copies the reference from another reference. */ virtual bool copy(const ConfigObjectReference *ref); /** Adds a possible type to this reference. */ virtual bool allow(const QMetaObject *elementType); /** Returns the type names of allowed objects. */ const QStringList &elementTypeNames() const; /** Returns the reference as the specified type. */ template Type *as() const { if (nullptr == _object) return nullptr; return _object->as(); } /** Returns @c true if the reference is of the specified type. */ template bool is() const { if (nullptr == _object) return false; return _object->is(); } /** Compares the references. */ int compare(const ConfigObjectReference &other) const; signals: /** Gets emitted if the reference is changed. * This signal is not emitted if the referenced object is modified. */ void modified(); protected slots: /** Internal call back whenever the referenced object gets deleted. */ void onReferenceDeleted(QObject *obj); protected: /** Holds the static QMetaObject of the possible element types. */ QStringList _elementTypes; /** The reference to the object. */ ConfigObject *_object; }; /** Represents a reference to a contact. * This class is only used to automate the parsing and generation of the YAML codeplug file. * @ingroup config */ class ContactReference: public ConfigObjectReference { Q_OBJECT protected: /** Constructor. */ ContactReference(const QMetaObject &elementType, QObject *parent = nullptr); public: /** Constructor. */ explicit ContactReference(QObject *parent=nullptr); }; /** Represents a reference to a DMR contact. * @ingroup conf*/ class DMRContactReference: public ContactReference { Q_OBJECT public: /** Constructor. */ explicit DMRContactReference(QObject *parent=nullptr); }; /** List of references to DMR contacts. */ class DMRContactRefList: public ConfigObjectRefList { Q_OBJECT public: /** Constructor. */ explicit DMRContactRefList(QObject *parent=nullptr); }; /** Represents a reference to a channel. * This class is only used to automate the parsing and generation of the YAML codeplug file. * @ingroup config */ class ChannelReference: public ConfigObjectReference { Q_OBJECT protected: /** Hidden constructor. */ ChannelReference(const QMetaObject &elementType, QObject *parent = nullptr); public: /** Constructor. */ explicit ChannelReference(QObject *parent=nullptr); }; /** Implements a reference to a DMR channel. * @ingroup conf */ class DMRChannelReference: public ChannelReference { Q_OBJECT public: /** Constructor. */ explicit DMRChannelReference(QObject *parent=nullptr); }; /** Implements a reference to a FM channel. * @ingroup conf */ class FMChannelReference: public ChannelReference { Q_OBJECT public: /** Constructor. */ explicit FMChannelReference(QObject *parent=nullptr); }; /** Represents a list of weak references to channels (analog and digital). * @ingroup config */ class ChannelRefList: public ConfigObjectRefList { Q_OBJECT protected: /** Hidden constructor. */ explicit ChannelRefList(const QMetaObject &elementTypes, QObject *parent = nullptr); public: /** Empty constructor. */ explicit ChannelRefList(QObject *parent=nullptr); }; /** Represents a list of references to some DMR channels. * @ingroup config */ class DMRChannelRefList: public ChannelRefList { Q_OBJECT public: /** Empty constructor. */ explicit DMRChannelRefList(QObject *parent=nullptr); }; /** Represents a list of references to some roaming channels. * @ingroup config */ class RoamingChannelRefList: public ConfigObjectRefList { Q_OBJECT public: /** Empty constructor. */ explicit RoamingChannelRefList(QObject *parent=nullptr); }; /** Implements a reference to a scan list. * @ingroup conf */ class ScanListReference: public ConfigObjectReference { Q_OBJECT public: /** Constructor. */ explicit ScanListReference(QObject *parent=nullptr); }; /** Implements a reference to a positioning system. * @ingroup conf */ class PositioningSystemReference: public ConfigObjectReference { Q_OBJECT protected: /** Hidden constructor. */ PositioningSystemReference(const QMetaObject &elementType, QObject *parent = nullptr); public: /** Constructor. */ explicit PositioningSystemReference(QObject *parent=nullptr); }; /** Implements a reference to an APRS system. * @ingroup conf */ class APRSSystemReference: public PositioningSystemReference { Q_OBJECT public: /** Constructor. */ explicit APRSSystemReference(QObject *parent=nullptr); }; /** Implements a reference to a GPS system. * @ingroup conf */ class GPSSystemReference: public PositioningSystemReference { Q_OBJECT public: /** Constructor. */ explicit GPSSystemReference(QObject *parent=nullptr); }; /** Implements a reference to a radio ID. * @ingroup conf */ class DMRRadioIDReference: public ConfigObjectReference { Q_OBJECT public: /** Constructor. */ explicit DMRRadioIDReference(QObject *parent=nullptr); }; /** Implements a reference to a group list. * @ingroup conf */ class GroupListReference: public ConfigObjectReference { Q_OBJECT public: /** Constructor. */ explicit GroupListReference(QObject *parent=nullptr); }; /** Implements a reference to a roaming zone. * @ingroup conf */ class RoamingZoneReference: public ConfigObjectReference { Q_OBJECT public: /** Constructor. */ explicit RoamingZoneReference(QObject *parent=nullptr); }; /** Implements a reference to an encryption key. * @ingroup conf */ class EncryptionKeyReference: public ConfigObjectReference { Q_OBJECT public: /** Constructor. */ explicit EncryptionKeyReference(QObject *parent=nullptr); }; /** Implements a reference to a zone. * @ingroup conf */ class ZoneReference: public ConfigObjectReference { Q_OBJECT public: /** Constructor. */ explicit ZoneReference(QObject *parent=nullptr); }; #endif // CONFIGREFERENCE_HH qdmr-0.12.3/lib/contact.cc000066400000000000000000000203711501654372000152560ustar00rootroot00000000000000#include "contact.hh" #include "config.hh" #include "utils.hh" #include "logger.hh" /* ********************************************************************************************* * * Implementation of Contact * ********************************************************************************************* */ Contact::Contact(QObject *parent) : ConfigObject(parent), _ring(false) { // pass... } Contact::Contact(const QString &name, bool rxTone, QObject *parent) : ConfigObject(name, parent), _ring(rxTone) { // pass... } void Contact::clear() { ConfigObject::clear(); _ring = false; } bool Contact::ring() const { return _ring; } void Contact::setRing(bool enable) { _ring = enable; emit modified(this); } bool Contact::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) { if (! node) return false; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot parse contact: Expected object with one child."; return false; } YAML::Node cnt = node.begin()->second; return ConfigObject::parse(cnt, ctx, err); } bool Contact::link(const YAML::Node &node, const Context &ctx, const ErrorStack &err) { return ConfigObject::link(node.begin()->second, ctx, err); } /* ********************************************************************************************* * * Implementation of AnalogContact * ********************************************************************************************* */ AnalogContact::AnalogContact(QObject *parent) : Contact(parent) { // pass... } AnalogContact::AnalogContact(const QString &name, bool ring, QObject *parent) : Contact(name, ring, parent) { // pass... } /* ********************************************************************************************* * * Implementation of DTMFContact * ********************************************************************************************* */ DTMFContact::DTMFContact(QObject *parent) : AnalogContact(parent), _number() { // pass... } DTMFContact::DTMFContact(const QString &name, const QString &number, bool rxTone, QObject *parent) : AnalogContact(name, rxTone, parent), _number(number.simplified()) { // pass... } ConfigItem * DTMFContact::clone() const { DTMFContact *c = new DTMFContact(); if (! c->copy(*this)) { c->deleteLater(); return nullptr; } return c; } void DTMFContact::clear() { Contact::clear(); _number.clear(); } const QString & DTMFContact::number() const { return _number; } bool DTMFContact::setNumber(const QString &number) { if (! validDTMFNumber(number)) return false; _number = number.simplified(); emit modified(this); return true; } YAML::Node DTMFContact::serialize(const Context &context, const ErrorStack &err) { YAML::Node node = AnalogContact::serialize(context, err); if (node.IsNull()) return node; node.SetStyle(YAML::EmitterStyle::Flow); YAML::Node type; type["dtmf"] = node; return type; } /* ********************************************************************************************* * * Implementation of DigitalContact * ********************************************************************************************* */ DigitalContact::DigitalContact(QObject *parent) : Contact(parent) { // pass... } DigitalContact::DigitalContact(const QString &name, bool ring, QObject *parent) : Contact(name, ring, parent) { // pass... } /* ********************************************************************************************* * * Implementation of DMRContact * ********************************************************************************************* */ DMRContact::DMRContact(QObject *parent) : DigitalContact(parent), _type(PrivateCall), _number(0), _anytone(nullptr), _openGD77(nullptr) { // pass... } DMRContact::DMRContact(Type type, const QString &name, unsigned number, bool rxTone, QObject *parent) : DigitalContact(name, rxTone, parent), _type(type), _number(number), _anytone(nullptr), _openGD77(nullptr) { // pass... } ConfigItem * DMRContact::clone() const { DMRContact *c = new DMRContact(); if (! c->copy(*this)) { c->deleteLater(); return nullptr; } return c; } void DMRContact::clear() { DigitalContact::clear(); _type = PrivateCall; _number = 0; if (_openGD77) _openGD77->deleteLater(); _openGD77 = nullptr; if (_anytone) _anytone->deleteLater(); _anytone = nullptr; } DMRContact::Type DMRContact::type() const { return _type; } void DMRContact::setType(DMRContact::Type type) { _type = type; } unsigned DMRContact::number() const { return _number; } bool DMRContact::setNumber(unsigned number) { _number = number; emit modified(this); return true; } YAML::Node DMRContact::serialize(const Context &context, const ErrorStack &err) { YAML::Node node = DigitalContact::serialize(context, err); if (node.IsNull()) return node; node.SetStyle(YAML::EmitterStyle::Flow); YAML::Node type; type["dmr"] = node; return type; } OpenGD77ContactExtension * DMRContact::openGD77ContactExtension() const { return _openGD77; } void DMRContact::setOpenGD77ContactExtension(OpenGD77ContactExtension *ext) { if (_openGD77) _openGD77->deleteLater(); _openGD77 = ext; if (_openGD77) { _openGD77->setParent(this); connect(_openGD77, &OpenGD77ContactExtension::modified, [this](ConfigItem*){ emit modified(this); }); } } AnytoneContactExtension * DMRContact::anytoneExtension() const { return _anytone; } void DMRContact::setAnytoneExtension(AnytoneContactExtension *ext) { if (_anytone) _anytone->deleteLater(); _anytone = ext; if (_anytone) { _anytone->setParent(this); connect(_anytone, &OpenGD77ContactExtension::modified, [this](ConfigItem*){ emit modified(this); }); } } /* ********************************************************************************************* * * Implementation of ContactList * ********************************************************************************************* */ ContactList::ContactList(QObject *parent) : ConfigObjectList(Contact::staticMetaObject, parent) { // pass... } int ContactList::add(ConfigObject *obj, int row, bool unique) { if ((nullptr == obj) || (! obj->is())) return -1; return ConfigObjectList::add(obj, row, unique); } int ContactList::digitalCount() const { int c=0; for (int i=0; i<_items.size(); i++) if (_items.at(i)->is()) c++; return c; } int ContactList::dtmfCount() const { int c=0; for (int i=0; i<_items.size(); i++) if (_items.at(i)->is()) c++; return c; } Contact * ContactList::contact(int idx) const { if ((0>idx) || (idx >= count())) return nullptr; return _items[idx]->as(); } DMRContact * ContactList::digitalContact(int idx) const { for (int i=0; i<_items.size(); i++) { if (_items.at(i)->is()) { if (0 == idx) return _items.at(i)->as(); else idx--; } } return nullptr; } DMRContact * ContactList::findDigitalContact(unsigned number) const { for (int i=0; i<_items.size(); i++) { if (! _items.at(i)->is()) continue; if (_items.at(i)->as()->number() == number) return _items.at(i)->as(); } return nullptr; } DTMFContact * ContactList::dtmfContact(int idx) const { for (int i=0; i<_items.size(); i++) { if (_items.at(i)->is()) { if (0 == idx) return _items.at(i)->as(); else idx--; } } return nullptr; } ConfigItem * ContactList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) if (! node) return nullptr; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create contact: Expected object with one child."; return nullptr; } QString type = QString::fromStdString(node.begin()->first.as()); if ("dmr" == type) { return new DMRContact(); } else if ("dtmf" == type) { return new DTMFContact(); } errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create contact: Unknown type '" << type << "'."; return nullptr; } qdmr-0.12.3/lib/contact.hh000066400000000000000000000166231501654372000152750ustar00rootroot00000000000000#ifndef CONTACT_HH #define CONTACT_HH #include "configobject.hh" #include #include #include "anytone_extension.hh" #include "opengd77_extension.hh" class Config; /** Represents the base-class for all contact types, analog (DTMF) or digital (DMR, M17). * * @ingroup conf */ class Contact: public ConfigObject { Q_OBJECT Q_CLASSINFO("IdPrefix", "cont") /** If @c true and supported by radio, ring on call from this contact. */ Q_PROPERTY(bool ring READ ring WRITE setRing) protected: /** Default constructor. */ explicit Contact(QObject *parent=nullptr); /** Hidden constructor. * @param name Specifies the name of the contact. * @param ring Specifies whether a ring-tone for this contact is used. * @param parent Specifies the QObject parent. */ Contact(const QString &name, bool ring=true, QObject *parent=nullptr); public: void clear(); public: /** Returns @c true if the ring-tone is enabled for this contact. */ bool ring() const; /** Enables/disables the ring-tone for this contact. */ void setRing(bool enable); /** Typecheck contact. * For example, @c contact->is() returns @c true if @c contact is a * @c DigitalContact. */ template bool is() const { return 0 != dynamic_cast(this); } /** Typecast contact. */ template T *as() { return dynamic_cast(this); } /** Typecast contact. */ template const T *as() const { return dynamic_cast(this); } public: bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack()); bool link(const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Ringtone enabled? */ bool _ring; }; /** Base class for all analog contacts. * @ingroup conf */ class AnalogContact: public Contact { Q_OBJECT protected: /** Hidden constructor. */ explicit AnalogContact(QObject *parent=nullptr); /** Constructor. */ AnalogContact(const QString &name, bool rxTone, QObject *parent=nullptr); }; /** Represents an analog contact, that is a DTMF number. * @ingroup conf */ class DTMFContact: public AnalogContact { Q_OBJECT /** The contact number. */ Q_PROPERTY(QString number READ number WRITE setNumber) public: /** Default constructor. */ Q_INVOKABLE explicit DTMFContact(QObject *parent=nullptr); /** Constructs a DTMF (analog) contact. * @param name Specifies the contact name. * @param number Specifies the DTMF number (0-9,A,B,C,D,*,#). * @param ring Specifies whether the ring-tone is enabled for this contact. * @param parent Specifies the QObject parent. */ DTMFContact(const QString &name, const QString &number, bool ring=false, QObject *parent=nullptr); ConfigItem *clone() const; void clear(); /** Returns the DTMF number of this contact. * The number must consist of 0-9, a-f, * or #. */ const QString &number() const; /** (Re-)Sets the DTMF number of this contact. */ bool setNumber(const QString &number); public: YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack()); protected: /** The DTMF number. */ QString _number; }; /** Base class for all digital contacts. * @ingroup conf */ class DigitalContact: public Contact { Q_OBJECT protected: /** Hidden constructor. */ explicit DigitalContact(QObject *parent=nullptr); /** Hidden constructor. */ DigitalContact(const QString &name, bool ring, QObject *parent=nullptr); }; /** Represents a digital contact, that is a DMR number. * @ingroup conf */ class DMRContact: public DigitalContact { Q_OBJECT /** The type of the contact. */ Q_PROPERTY(Type type READ type WRITE setType) /** The number of the contact. */ Q_PROPERTY(unsigned number READ number WRITE setNumber) /** The AnyTone extension to the digital contact. */ Q_PROPERTY(AnytoneContactExtension* anytone READ anytoneExtension WRITE setAnytoneExtension) /** The OpenGD77 extension to the digital contact. */ Q_PROPERTY(OpenGD77ContactExtension* openGD77 READ openGD77ContactExtension WRITE setOpenGD77ContactExtension) public: /** Possible call types for a contact. */ typedef enum { PrivateCall, ///< A private call. GroupCall, ///< A group call. AllCall ///< An all-call. } Type; Q_ENUM(Type) public: /** Default constructor. */ Q_INVOKABLE explicit DMRContact(QObject *parent=nullptr); /** Constructs a DMR (digital) contact. * @param type Specifies the call type (private, group, all-call). * @param name Specifies the contact name. * @param number Specifies the DMR number for this contact. * @param ring Specifies whether the ring-tone is enabled for this contact. * @param parent Specifies the QObject parent. */ DMRContact(Type type, const QString &name, unsigned number, bool ring=false, QObject *parent=nullptr); ConfigItem *clone() const; void clear(); /** Returns the call-type. */ Type type() const; /** (Re-)Sets the call-type. */ void setType(Type type); /** Returns the DMR number. */ unsigned number() const; /** (Re-)Sets the DMR number of the contact. */ bool setNumber(unsigned number); /** Returns the OpenGD77 extension, or @c nullptr if not set. */ OpenGD77ContactExtension *openGD77ContactExtension() const; /** Sets the OpenGD77 extension. */ void setOpenGD77ContactExtension(OpenGD77ContactExtension *ext); /** Returns the AnyTone extension, or @c nullptr if not set. */ AnytoneContactExtension *anytoneExtension() const; /** Sets the AnyTone extension. */ void setAnytoneExtension(AnytoneContactExtension *ext); public: YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack()); protected: /** The call type. */ Type _type; /** The DMR number of the contact. */ unsigned _number; /** Owns the AnytoneContactextension to the digital contacts. */ AnytoneContactExtension *_anytone; /** Owns the OpenGD77 extensions to the digital contacts. */ OpenGD77ContactExtension *_openGD77; }; /** Represents the list of contacts within the abstract radio configuration. * * A special feature of this list, is that DTMF and digital contacts can be accessed by their own * unique index although they are held within this single list. Most radios manage digital and * DTMF contacts in separate lists, hence a means to iterate over and get indices of digital and * DTMF contacts only is needed. * * This class implements the @c QAbstractTableModel, such that the list can be shown with the * @c QTableView widget. * * @ingroup conf */ class ContactList: public ConfigObjectList { Q_OBJECT public: /** Constructs an empty contact list. */ explicit ContactList(QObject *parent=nullptr); int add(ConfigObject *obj, int row=-1, bool unique=true); /** Returns the number of digital contacts. */ int digitalCount() const; /** Returns the number of DTMF contacts. */ int dtmfCount() const; /** Returns the contact at index @c idx. */ Contact *contact(int idx) const; /** Returns the digital contact at index @c idx among digital contacts. */ DMRContact *digitalContact(int idx) const; /** Searches for a digital contact with the given number. */ DMRContact *findDigitalContact(unsigned number) const; /** Returns the DTMF contact at index @c idx among DTMF contacts. */ DTMFContact *dtmfContact(int idx) const; public: ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); }; #endif // CONTACT_HH qdmr-0.12.3/lib/crc32.cc000066400000000000000000000071061501654372000145400ustar00rootroot00000000000000#include "crc32.hh" static const uint32_t _crc_table[256] = { /* CRC polynomial 0xedb88320 */ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; CRC32::CRC32() : _crc(0xFFFFFFFF) { // pass... } void CRC32::update(uint8_t c) { _crc = ( _crc_table[(_crc ^ c) & 0xFF] ^ (_crc >> 8) ); } void CRC32::update(const uint8_t *buf, size_t n) { for (size_t i=0; i> 8) ); } void CRC32::update(const QByteArray &buf) { update((const uint8_t *)buf.constData(), buf.size()); } qdmr-0.12.3/lib/crc32.hh000066400000000000000000000010631501654372000145460ustar00rootroot00000000000000#ifndef CRC32_HH #define CRC32_HH #include /** Implements the CRC32 checksum as used in DFU files. * * @ingroup util */ class CRC32 { public: /** Default constructor. */ CRC32(); /** Update CRC with given byte. */ void update(uint8_t c); /** Update CRC with given data. */ void update(const uint8_t *c, size_t n); /** Update CRC with given data. */ void update(const QByteArray &data); /** Returns the current CRC. */ inline uint32_t get() { return _crc; } protected: /** Current CRC. */ uint32_t _crc; }; #endif // CRC32_HH qdmr-0.12.3/lib/csvreader.cc000066400000000000000000002336121501654372000156050ustar00rootroot00000000000000#include "csvreader.hh" #include "config.hh" #include "utils.hh" #include "logger.hh" #include #include QVector< QPair > CSVLexer::_pattern = { { QRegExp("^n([0-9]{3})"), CSVLexer::Token::T_DCS_N }, { QRegExp("^i([0-9]{3})"), CSVLexer::Token::T_DCS_I }, { QRegExp("^([a-zA-Z0-9]{1,6}-[0-9]{1,2})"), CSVLexer::Token::T_APRSCALL }, { QRegExp("^([a-zA-Z_][a-zA-Z0-9_]*)"), CSVLexer::Token::T_KEYWORD }, { QRegExp("^\"([^\"\r\n]*)\""), CSVLexer::Token::T_STRING }, { QRegExp("^([+-]?[0-9]+(\\.[0-9]*)?)"), CSVLexer::Token::T_NUMBER }, { QRegExp("^(:)"), CSVLexer::Token::T_COLON }, { QRegExp("^(-)"), CSVLexer::Token::T_NOT_SET }, { QRegExp("^(\\+)"), CSVLexer::Token::T_ENABLED }, { QRegExp("^(,)"), CSVLexer::Token::T_COMMA }, { QRegExp("^([ \t]+)"), CSVLexer::Token::T_WHITESPACE }, { QRegExp("^(\r?\n)"), CSVLexer::Token::T_NEWLINE}, { QRegExp("^(#[^\n\r]*)"), CSVLexer::Token::T_COMMENT}, }; /* ********************************************************************************************* * * Implementation of CSVLexer * ********************************************************************************************* */ CSVLexer::CSVLexer(QTextStream &stream, QObject *parent) : QObject(parent), _errorMessage(), _stream(stream), _stack(), _currentLine() { _stream.seek(0); _stack.reserve(10); _stack.push_back({0, 1, 1}); _currentLine = _stream.readLine(); } const QString & CSVLexer::errorMessage() const { return _errorMessage; } CSVLexer::Token CSVLexer::next() { Token token = lex(); while ((Token::T_WHITESPACE == token.type) || (Token::T_COMMENT == token.type)) token = lex(); return token; } CSVLexer::Token CSVLexer::lex() { if (_currentLine.isEmpty() && _stream.atEnd()) { return {Token::T_END_OF_STREAM, "", _stack.back().line, _stack.back().column }; } else if (_currentLine.isEmpty()) { Token token = {Token::T_NEWLINE, "", _stack.back().line, _stack.back().column }; _stack.back().offset = _stream.pos(); _currentLine = _stream.readLine(); _stack.back().line++; _stack.back().column = 1; return token; } foreach (auto pattern, _pattern) { if (0 == pattern.first.indexIn(_currentLine)) { Token token = {pattern.second, pattern.first.cap(1), _stack.back().line, _stack.back().column}; _stack.back().offset += pattern.first.matchedLength(); _stack.back().column += token.value.size(); _currentLine = _currentLine.mid(pattern.first.matchedLength()); return token; } } _errorMessage = tr("Lexer error %1,%2: Unexpected char '%3'.").arg(_stack.back().line) .arg(_stack.back().column).arg(_currentLine.at(0)); return {Token::T_ERROR, _errorMessage, _stack.back().line, _stack.back().column}; } void CSVLexer::push() { _stack.push_back(_stack.back()); } void CSVLexer::pop() { if (_stack.size() < 2) return; _stack.pop_back(); _stream.seek(_stack.back().offset); _currentLine = QString(); } /* ********************************************************************************************* * * Implementation of CSVHandler * ********************************************************************************************* */ CSVHandler::CSVHandler(QObject *parent) : QObject(parent) { // pass... } CSVHandler::~CSVHandler() { // pass... } bool CSVHandler::handleRadioId(const QList &ids, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(ids); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage) return true; } bool CSVHandler::handleRadioName(const QString &name, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(name); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleIntroLine1(const QString &text, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(text); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleIntroLine2(const QString &text, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(text); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleMicLevel(unsigned level, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(level); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleSpeech(bool speech, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(speech); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleDTMFContact(qint64 idx, const QString &name, const QString &num, bool rxTone, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(idx); Q_UNUSED(name); Q_UNUSED(num); Q_UNUSED(rxTone); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleDigitalContact(qint64 idx, const QString &name, DMRContact::Type type, qint64 id, bool rxTone, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(idx); Q_UNUSED(name); Q_UNUSED(type); Q_UNUSED(id); Q_UNUSED(rxTone); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleGroupList(qint64 idx, const QString &name, const QList &contacts, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(idx); Q_UNUSED(name); Q_UNUSED(contacts); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleDigitalChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan, qint64 tot, bool ro, DMRChannel::Admit admit, qint64 color, DMRChannel::TimeSlot slot, qint64 gl, qint64 contact, qint64 gps, qint64 roam, qint64 radioID, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(idx); Q_UNUSED(name); Q_UNUSED(rx); Q_UNUSED(tx); Q_UNUSED(power); Q_UNUSED(scan); Q_UNUSED(tot); Q_UNUSED(ro); Q_UNUSED(admit); Q_UNUSED(color); Q_UNUSED(slot); Q_UNUSED(gl); Q_UNUSED(contact); Q_UNUSED(gps); Q_UNUSED(roam); Q_UNUSED(radioID); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleAnalogChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan, qint64 aprs, qint64 tot, bool ro, FMChannel::Admit admit, qint64 squelch, const SelectiveCall &rxTone, const SelectiveCall &txTone, FMChannel::Bandwidth bw, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(idx); Q_UNUSED(name); Q_UNUSED(rx); Q_UNUSED(tx); Q_UNUSED(power); Q_UNUSED(scan); Q_UNUSED(aprs); Q_UNUSED(tot); Q_UNUSED(ro); Q_UNUSED(admit); Q_UNUSED(squelch); Q_UNUSED(rxTone); Q_UNUSED(txTone); Q_UNUSED(bw); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleZone(qint64 idx, const QString &name, bool a, const QList &channels, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(idx); Q_UNUSED(name); Q_UNUSED(a); Q_UNUSED(channels); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleGPSSystem( qint64 idx, const QString &name, qint64 contactIdx, qint64 period, qint64 revertChannelIdx, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(idx); Q_UNUSED(name); Q_UNUSED(contactIdx); Q_UNUSED(period); Q_UNUSED(revertChannelIdx); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleAPRSSystem(qint64 idx, const QString &name, qint64 channelIdx, qint64 period, const QString &src, unsigned srcSSID, const QString &dest, unsigned destSSID, const QString &path, const QString &icon, const QString &message, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(idx); Q_UNUSED(name); Q_UNUSED(channelIdx); Q_UNUSED(period); Q_UNUSED(src); Q_UNUSED(srcSSID); Q_UNUSED(dest); Q_UNUSED(destSSID); Q_UNUSED(path); Q_UNUSED(icon); Q_UNUSED(message); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleScanList(qint64 idx, const QString &name, qint64 pch1, qint64 pch2, qint64 txch, const QList &channels, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(idx); Q_UNUSED(name); Q_UNUSED(pch1); Q_UNUSED(pch2); Q_UNUSED(txch); Q_UNUSED(channels); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } bool CSVHandler::handleRoamingZone(qint64 idx, const QString &name, const QList &channels, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(idx); Q_UNUSED(name); Q_UNUSED(channels); Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); return true; } /* ********************************************************************************************* * * Implementation of CSVParser * ********************************************************************************************* */ CSVParser::CSVParser(CSVHandler *handler, QObject *parent) : QObject(parent), _errorMessage(), _handler(handler) { // pass... } const QString & CSVParser::errorMessage() const { return _errorMessage; } bool CSVParser::parse(QTextStream &stream) { if (! stream.seek(0)) return false; CSVLexer lexer(stream); for (CSVLexer::Token token=lexer.next(); CSVLexer::Token::T_END_OF_STREAM != token.type; token = lexer.next()) { if (CSVLexer::Token::T_NEWLINE == token.type) continue; if ((CSVLexer::Token::T_KEYWORD == token.type) && ("id" == token.value.toLower())) { if (! _parse_radio_id(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("name" == token.value.toLower())) { if (! _parse_radio_name(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("introline1" == token.value.toLower())) { if (! _parse_introline1(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("introline2" == token.value.toLower())) { if (! _parse_introline2(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("miclevel" == token.value.toLower())) { if (! _parse_mic_level(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("speech" == token.value.toLower())) { if (! _parse_speech(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("userdb" == token.value.toLower())) { if (! _parse_userdb(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("contact" == token.value.toLower())) { if (! _parse_contacts(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("grouplist" == token.value.toLower())) { if (! _parse_rx_groups(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("digital" == token.value.toLower())) { if (! _parse_digital_channels(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("analog" == token.value.toLower())) { if (! _parse_analog_channels(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("zone" == token.value.toLower())) { if (! _parse_zones(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("gps" == token.value.toLower())) { if (! _parse_gps_systems(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("aprs" == token.value.toLower())) { if (! _parse_aprs_systems(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("scanlist" == token.value.toLower())) { if (! _parse_scanlists(lexer)) return false; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("roaming" == token.value.toLower())) { if (! _parse_roaming_zones(lexer)) return false; } else if (CSVLexer::Token::T_ERROR == token.type) { _errorMessage = QString("Lexer error @ %1,%2 '%3': %4").arg(token.line).arg(token.column) .arg(token.value).arg(lexer.errorMessage()); return false; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4'.").arg(token.line) .arg(token.column).arg(token.type).arg(token.value); return false; } } return true; } bool CSVParser::_parse_radio_id(CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); if (CSVLexer::Token::T_COLON != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QList ids; token = lexer.next(); while (CSVLexer::Token::T_NUMBER == token.type) { ids.append(token.value.toInt()); token = lexer.next(); if (CSVLexer::Token::T_COMMA == token.type) { token = lexer.next(); continue; } } if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } if (0 == ids.size()) { _errorMessage = QString("Parse error @ %1,%2: At least one radio ID must be specified.") .arg(token.line).arg(token.column); return false; } return _handler->handleRadioId(ids, token.line, token.column, _errorMessage); } bool CSVParser::_parse_radio_name(CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); if (CSVLexer::Token::T_COLON != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if (CSVLexer::Token::T_STRING != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QString name = token.value; qint64 line=token.line, column=token.column; token = lexer.next(); if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } return _handler->handleRadioName(name, line, column, _errorMessage); } bool CSVParser::_parse_introline1(CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); if (CSVLexer::Token::T_COLON != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if (CSVLexer::Token::T_STRING != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QString text = token.value; qint64 line=token.line, column=token.column; token = lexer.next(); if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } return _handler->handleIntroLine1(text, line, column, _errorMessage); } bool CSVParser::_parse_introline2(CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); if (CSVLexer::Token::T_COLON != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if (CSVLexer::Token::T_STRING != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QString text = token.value; qint64 line=token.line, column=token.column; token = lexer.next(); if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } return _handler->handleIntroLine2(text, line, column, _errorMessage); } bool CSVParser::_parse_mic_level(CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); if (CSVLexer::Token::T_COLON != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if (CSVLexer::Token::T_NUMBER != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } qint64 level = token.value.toInt(); qint64 line=token.line, column=token.column; token = lexer.next(); if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } return _handler->handleMicLevel(level, line, column, _errorMessage); } bool CSVParser::_parse_speech(CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); if (CSVLexer::Token::T_COLON != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if ((CSVLexer::Token::T_KEYWORD != token.type) || (("on" != token.value.toLower()) && ("off" != token.value.toLower()))) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected 'On' or 'Off'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } qint64 line=token.line, column=token.column; bool speech = ("on" == token.value.toLower()); token = lexer.next(); if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } return _handler->handleSpeech(speech, line, column, _errorMessage); } bool CSVParser::_parse_userdb(CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); if (CSVLexer::Token::T_COLON != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if ((CSVLexer::Token::T_KEYWORD != token.type) || (("on" != token.value.toLower()) && ("off" != token.value.toLower()))) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected 'On' or 'Off'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } qint64 line=token.line, column=token.column; token = lexer.next(); if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } logWarn() << line << "," << column << ": The 'UserDB' setting is obsolete. " << "It will be removed in future releases. Just delete this line."; // Ignore user DB setting return true; } bool CSVParser::_parse_contacts(CSVLexer &lexer) { // skip rest of header CSVLexer::Token token = lexer.next(); for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) { // skip } if (CSVLexer::Token::T_NEWLINE != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) { qint64 idx = token.value.toInt(); if (! _parse_contact(idx, lexer)) return false; } if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) return true; _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } bool CSVParser::_parse_contact(qint64 idx, CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); qint64 line=token.line, column=token.column; if (CSVLexer::Token::T_STRING != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QString name = token.value; token = lexer.next(); if (CSVLexer::Token::T_KEYWORD != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected keyword.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } bool dtmf = false; DMRContact::Type type = DMRContact::PrivateCall; if ("group" == token.value.toLower()) { type = DMRContact::GroupCall; } else if ("private" == token.value.toLower()) { type = DMRContact::PrivateCall; } else if ("all" == token.value.toLower()) { type = DMRContact::AllCall; } else if ("dtmf" == token.value.toLower()) { dtmf = true; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected 'Group', 'Private', 'All' or 'DTMF'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if ( (!dtmf) && (CSVLexer::Token::T_NUMBER != token.type) ) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } else if (dtmf && (CSVLexer::Token::T_NUMBER != token.type) && (CSVLexer::Token::T_STRING != token.type)) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QString dtmf_num = token.value; if (dtmf && (! validDTMFNumber(dtmf_num))) { _errorMessage = QString("Parse error @ %1,%2: Invalid DTMF number '%3'.") .arg(token.line).arg(token.column).arg(token.value); } qint64 id = token.value.toInt(); bool rxToneEnabled; token = lexer.next(); if (CSVLexer::Token::T_NOT_SET == token.type) rxToneEnabled = false; else if (CSVLexer::Token::T_ENABLED == token.type) rxToneEnabled = true; else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected '+' or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } if (dtmf) return _handler->handleDTMFContact(idx, name, dtmf_num, rxToneEnabled, line, column, _errorMessage); return _handler->handleDigitalContact(idx, name, type, id, rxToneEnabled, line, column, _errorMessage); } bool CSVParser::_parse_rx_groups(CSVLexer &lexer) { // skip rest of header CSVLexer::Token token = lexer.next(); while (CSVLexer::Token::T_KEYWORD==token.type) token = lexer.next(); if (CSVLexer::Token::T_NEWLINE != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) { qint64 idx = token.value.toInt(); if (! _parse_rx_group(idx, lexer)) return false; } if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) return true; _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } bool CSVParser::_parse_rx_group(qint64 idx, CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); qint64 line=token.line, column=token.column; if (CSVLexer::Token::T_STRING != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QString name = token.value; QList lst; token = lexer.next(); while (CSVLexer::Token::T_NUMBER == token.type) { lst.append(token.value.toInt()); token = lexer.next(); if (CSVLexer::Token::T_COMMA == token.type) token = lexer.next(); } if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } return _handler->handleGroupList(idx, name, lst, line, column, _errorMessage); } bool CSVParser::_parse_digital_channels(CSVLexer &lexer) { // skip rest of header CSVLexer::Token token = lexer.next(); for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) { // skip } if (CSVLexer::Token::T_NEWLINE != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) { qint64 idx = token.value.toInt(); if (! _parse_digital_channel(idx, lexer)) return false; } if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) return true; _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } bool CSVParser::_parse_digital_channel(qint64 idx, CSVLexer &lexer) { bool ok=false; CSVLexer::Token token = lexer.next(); qint64 line=token.line, column=token.column; if (CSVLexer::Token::T_STRING != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QString name = token.value; token = lexer.next(); if (CSVLexer::Token::T_NUMBER != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } double rx = token.value.toDouble(&ok); if (! ok) { _errorMessage = QString("Parse error @ %1,%2: Cannot convert '%3' to double.") .arg(token.line).arg(token.column).arg(token.value); return false; } token = lexer.next(); if (CSVLexer::Token::T_NUMBER != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } double tx = token.value.toDouble(&ok); if (! ok) { _errorMessage = QString("Parse error @ %1,%2: Cannot convert '%3' to double.") .arg(token.line).arg(token.column).arg(token.value); return false; } if (token.value.startsWith('+') || token.value.startsWith('-')) tx = rx + tx; token = lexer.next(); if (CSVLexer::Token::T_KEYWORD != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected keyword.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } Channel::Power pwr; if ("max" == token.value.toLower()) { pwr = Channel::Power::Max; } else if ("high" == token.value.toLower()) { pwr = Channel::Power::High; } else if ("mid" == token.value.toLower()) { pwr = Channel::Power::Mid; } else if ("low" == token.value.toLower()) { pwr = Channel::Power::Low; } else if ("min" == token.value.toLower()) { pwr = Channel::Power::Min; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected 'High' or 'Low'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); qint64 scanlist; if (CSVLexer::Token::T_NOT_SET == token.type) { scanlist = 0; } else if (CSVLexer::Token::T_NUMBER == token.type) { scanlist = token.value.toInt(); } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if ((CSVLexer::Token::T_NUMBER != token.type) && (CSVLexer::Token::T_NOT_SET != token.type)) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } qint64 tot = (CSVLexer::Token::T_NOT_SET == token.type) ? 0 : token.value.toInt(); bool rxOnly; token = lexer.next(); if (CSVLexer::Token::T_NOT_SET == token.type) rxOnly = false; else if (CSVLexer::Token::T_ENABLED == token.type) rxOnly = true; else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected '+' or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); DMRChannel::Admit admit; if (CSVLexer::Token::T_NOT_SET == token.type) { admit = DMRChannel::Admit::Always; } else if (CSVLexer::Token::T_KEYWORD == token.type) { if ("free" == token.value.toLower()) admit = DMRChannel::Admit::Free; else if ("color" == token.value.toLower()) admit = DMRChannel::Admit::ColorCode; else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected 'Free' or 'Color'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected 'Free', 'Color' or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if (CSVLexer::Token::T_NUMBER != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } qint64 color = token.value.toInt(); token = lexer.next(); DMRChannel::TimeSlot slot; if (CSVLexer::Token::T_NUMBER == token.type) { if (1 == token.value.toInt()) { slot = DMRChannel::TimeSlot::TS1; } else if (2 == token.value.toInt()) { slot = DMRChannel::TimeSlot::TS2; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected '1' or '2'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); qint64 rxGroupList; if (CSVLexer::Token::T_NOT_SET == token.type) { rxGroupList = 0; } else if (CSVLexer::Token::T_NUMBER == token.type) { rxGroupList = token.value.toInt(); } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); qint64 txContact; if (CSVLexer::Token::T_NOT_SET == token.type) { txContact = 0; } else if (CSVLexer::Token::T_NUMBER == token.type) { txContact = token.value.toInt(); } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); qint64 gps; if (CSVLexer::Token::T_NOT_SET == token.type) { gps = 0; } else if (CSVLexer::Token::T_NUMBER == token.type) { gps = token.value.toInt(&ok); } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); qint64 roam = -1, radioID = -1; if (CSVLexer::Token::T_NOT_SET == token.type) { roam = -1; } else if (CSVLexer::Token::T_ENABLED == token.type) { roam = 0; } else if (CSVLexer::Token::T_NUMBER == token.type) { roam = token.value.toInt(&ok); } else if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) { // This entry is optional for backward compatibility. goto done; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number, '-' or '+'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if (CSVLexer::Token::T_NOT_SET == token.type) { radioID = -1; } else if (CSVLexer::Token::T_NUMBER == token.type) { radioID = token.value.toInt(&ok); } else if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) { // This entry is optional for backward compatibility. goto done; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } done: return _handler->handleDigitalChannel(idx, name, rx, tx, pwr, scanlist, tot, rxOnly, admit, color, slot, rxGroupList, txContact, gps, roam, radioID, line, column, _errorMessage); } bool CSVParser::_parse_analog_channels(CSVLexer &lexer) { // skip rest of header CSVLexer::Token token = lexer.next(); for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) { // skip } if (CSVLexer::Token::T_NEWLINE != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) { qint64 idx = token.value.toInt(); if (! _parse_analog_channel(idx, lexer)) return false; } if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) return true; _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } bool CSVParser::_parse_analog_channel(qint64 idx, CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); qint64 line=token.line, column=token.column; if (CSVLexer::Token::T_STRING != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QString name = token.value; token = lexer.next(); if (CSVLexer::Token::T_NUMBER != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } double rx = token.value.toDouble(); token = lexer.next(); if (CSVLexer::Token::T_NUMBER != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } double tx = token.value.toDouble(); if (token.value.startsWith('+') || token.value.startsWith('-')) tx = rx + tx; token = lexer.next(); if (CSVLexer::Token::T_KEYWORD != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected keyword.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } Channel::Power pwr; if ("max" == token.value.toLower()) { pwr = Channel::Power::Max; } else if ("high" == token.value.toLower()) { pwr = Channel::Power::High; } else if ("mid" == token.value.toLower()) { pwr = Channel::Power::Mid; } else if ("low" == token.value.toLower()) { pwr = Channel::Power::Low; } else if ("min" == token.value.toLower()) { pwr = Channel::Power::Min; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected 'High' or 'Low'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); qint64 scanlist; if (CSVLexer::Token::T_NOT_SET == token.type) { scanlist = 0; } else if (CSVLexer::Token::T_NUMBER == token.type) { scanlist = token.value.toInt(); } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if ((CSVLexer::Token::T_NUMBER != token.type) && (CSVLexer::Token::T_NOT_SET != token.type)) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } qint64 tot = (CSVLexer::Token::T_NOT_SET == token.type) ? 0 : token.value.toInt(); bool rxOnly; token = lexer.next(); if (CSVLexer::Token::T_NOT_SET == token.type) rxOnly = false; else if (CSVLexer::Token::T_ENABLED == token.type) rxOnly = true; else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected '+' or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); FMChannel::Admit admit; if (CSVLexer::Token::T_NOT_SET == token.type) { admit = FMChannel::Admit::Always; } else if (CSVLexer::Token::T_KEYWORD == token.type) { if ("free" == token.value.toLower()) admit = FMChannel::Admit::Free; else if ("tone" == token.value.toLower()) admit = FMChannel::Admit::Tone; else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected 'Free', 'Tone'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected 'Free', 'Tone' or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if (CSVLexer::Token::T_NUMBER != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } qint64 squelch = token.value.toInt(); token = lexer.next(); SelectiveCall rxTone; if (CSVLexer::Token::T_NOT_SET == token.type) { rxTone = SelectiveCall(); } else if (CSVLexer::Token::T_NUMBER == token.type) { rxTone = SelectiveCall(token.value.toFloat()); } else if (CSVLexer::Token::T_DCS_N == token.type) { rxTone = SelectiveCall(token.value.toUInt(), false); } else if (CSVLexer::Token::T_DCS_I == token.type) { rxTone = SelectiveCall(token.value.toUInt(), true); } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); SelectiveCall txTone; if (CSVLexer::Token::T_NOT_SET == token.type) { txTone = SelectiveCall(); } else if (CSVLexer::Token::T_NUMBER == token.type) { txTone = SelectiveCall(token.value.toFloat()); } else if (CSVLexer::Token::T_DCS_N == token.type) { txTone = SelectiveCall(token.value.toUInt(), false); } else if (CSVLexer::Token::T_DCS_I == token.type) { txTone = SelectiveCall(token.value.toUInt(), true); } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if (CSVLexer::Token::T_NUMBER != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } FMChannel::Bandwidth bw; if (25 == token.value.toFloat()) { bw = FMChannel::Bandwidth::Wide; } else if (12.5 == token.value.toFloat()) { bw = FMChannel::Bandwidth::Narrow; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected '12.5' or '25'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); qint64 aprs = 0; if (CSVLexer::Token::T_NOT_SET == token.type) { aprs = 0; } else if (CSVLexer::Token::T_NUMBER == token.type) { aprs = token.value.toInt(); } else if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) { // This entry is optional for backward compatibility. goto done; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } done: return _handler->handleAnalogChannel(idx, name, rx, tx, pwr, scanlist, aprs, tot, rxOnly, admit, squelch, rxTone, txTone, bw, line, column, _errorMessage); } bool CSVParser::_parse_zones(CSVLexer &lexer) { // skip rest of header CSVLexer::Token token = lexer.next(); for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) { // skip } if (CSVLexer::Token::T_NEWLINE != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) { qint64 idx = token.value.toInt(); if (! _parse_zone(idx, lexer)) return false; } if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) return true; _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } bool CSVParser::_parse_zone(qint64 idx, CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); qint64 line=token.line, column=token.column; if (CSVLexer::Token::T_STRING != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QString name = token.value; token = lexer.next(); if ((CSVLexer::Token::T_KEYWORD != token.type) || (("a" != token.value.toLower()) && ("b" != token.value.toLower())) ) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected 'A or 'B'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } bool a = ("a" == token.value.toLower()); QList lst; token = lexer.next(); while (CSVLexer::Token::T_NUMBER == token.type) { lst.append(token.value.toInt()); token = lexer.next(); if (CSVLexer::Token::T_COMMA == token.type) token = lexer.next(); } if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } return _handler->handleZone(idx, name, a, lst, line, column, _errorMessage); } bool CSVParser::_parse_gps_systems(CSVLexer &lexer) { // skip rest of header CSVLexer::Token token = lexer.next(); for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) { // skip } if (CSVLexer::Token::T_NEWLINE != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) { qint64 idx = token.value.toInt(); if (! _parse_gps_system(idx, lexer)) return false; } if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) return true; _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } bool CSVParser::_parse_gps_system(qint64 id, CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); qint64 line=token.line, column=token.column; if (CSVLexer::Token::T_STRING != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QString name = token.value; qint64 contact; token = lexer.next(); if (CSVLexer::Token::T_NUMBER == token.type) { contact = token.value.toInt(); } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } qint64 period; token = lexer.next(); if (CSVLexer::Token::T_NUMBER == token.type) { period = token.value.toInt(); } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } qint64 chan; token = lexer.next(); if (CSVLexer::Token::T_NOT_SET == token.type) { chan = 0; } else if (CSVLexer::Token::T_NUMBER == token.type) { chan = token.value.toInt(); } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } return _handler->handleGPSSystem(id, name, contact, period, chan, line, column, _errorMessage); } bool CSVParser::_parse_aprs_systems(CSVLexer &lexer) { // skip rest of header CSVLexer::Token token = lexer.next(); for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) { // skip } if (CSVLexer::Token::T_NEWLINE != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) { qint64 idx = token.value.toInt(); if (! _parse_aprs_system(idx, lexer)) return false; } if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) return true; _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } bool CSVParser::_parse_aprs_system(qint64 id, CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); qint64 line=token.line, column=token.column; if (CSVLexer::Token::T_STRING != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QString name = token.value; qint64 channel; token = lexer.next(); if (CSVLexer::Token::T_NUMBER == token.type) { channel = token.value.toInt(); } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } qint64 period; token = lexer.next(); if (CSVLexer::Token::T_NUMBER == token.type) { period = token.value.toInt(); } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); QString src=""; unsigned srcSSID=0; if (CSVLexer::Token::T_APRSCALL == token.type) { QStringList lst = token.value.split('-'); src = lst.first(); srcSSID = lst.last().toInt(); } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (token.value.size()<=6)) { src = token.value; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected APRS call.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); QString dest=""; unsigned destSSID=0; if (CSVLexer::Token::T_APRSCALL == token.type) { QStringList lst = token.value.split('-'); dest = lst.first(); destSSID = lst.last().toInt(); } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (token.value.size()<=6)) { dest = token.value; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected APRS call.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); QString path=""; if (CSVLexer::Token::T_NOT_SET == token.type) { // pass.. } else if (CSVLexer::Token::T_STRING == token.type) { path = token.value; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); QString iconname; if ((CSVLexer::Token::T_KEYWORD == token.type) || (CSVLexer::Token::T_STRING == token.type)) { iconname = token.value; } else if (CSVLexer::Token::T_NOT_SET == token.type) { iconname = ""; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected keyword or string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); QString message; if (CSVLexer::Token::T_STRING == token.type) { message = token.value; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } return _handler->handleAPRSSystem(id, name, channel, period, src, srcSSID, dest, destSSID, path, iconname, message, line, column, _errorMessage); } bool CSVParser::_parse_scanlists(CSVLexer &lexer) { // skip rest of header CSVLexer::Token token = lexer.next(); for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) { // skip } if (CSVLexer::Token::T_NEWLINE != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) { qint64 idx = token.value.toInt(); if (! _parse_scanlist(idx, lexer)) return false; } if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) return true; _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } bool CSVParser::_parse_scanlist(qint64 idx, CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); qint64 line=token.line, column=token.column; if (CSVLexer::Token::T_STRING != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QString name = token.value; qint64 pch1; token = lexer.next(); if (CSVLexer::Token::T_NOT_SET == token.type) { pch1 = 0; } else if (CSVLexer::Token::T_NUMBER == token.type) { pch1 = token.value.toInt() + 1; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("sel" == token.value.toLower())) { pch1 = -1; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } qint64 pch2; token = lexer.next(); if (CSVLexer::Token::T_NOT_SET == token.type) { pch2 = 0; } else if (CSVLexer::Token::T_NUMBER == token.type) { pch2 = token.value.toInt() + 1; } else if ((CSVLexer::Token::T_KEYWORD == token.type) && ("sel" == token.value.toLower())) { pch2 = -1; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } qint64 txch; token = lexer.next(); if (CSVLexer::Token::T_NOT_SET == token.type) { txch = 0; } else if (CSVLexer::Token::T_KEYWORD == token.type) { if ("sel" == token.value.toLower()) { txch = -1; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number, 'Sel' or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } } else if (CSVLexer::Token::T_NUMBER == token.type) { txch = token.value.toInt() + 1; } else { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected number, 'Sel' or '-'.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QList lst; token = lexer.next(); while ((CSVLexer::Token::T_NUMBER == token.type) || (CSVLexer::Token::T_KEYWORD == token.type)) { if (CSVLexer::Token::T_NUMBER == token.type) { lst.append(token.value.toInt()); } else if (CSVLexer::Token::T_KEYWORD == token.type) { if ("sel" == token.value.toLower()) lst.append(-1); else { _errorMessage = QString("Parse error @ %1,%2: Unexpected keyword '%3' expected number or 'Sel'.") .arg(token.line).arg(token.column).arg(token.value); return false; } } token = lexer.next(); if (CSVLexer::Token::T_COMMA == token.type) token = lexer.next(); } if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } return _handler->handleScanList(idx, name, pch1, pch2, txch, lst, line, column, _errorMessage); } bool CSVParser::_parse_roaming_zones(CSVLexer &lexer) { // skip rest of header CSVLexer::Token token = lexer.next(); for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) { // skip } if (CSVLexer::Token::T_NEWLINE != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } token = lexer.next(); for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) { qint64 idx = token.value.toInt(); if (! _parse_roaming_zone(idx, lexer)) return false; } if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) return true; _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } bool CSVParser::_parse_roaming_zone(qint64 idx, CSVLexer &lexer) { CSVLexer::Token token = lexer.next(); qint64 line=token.line, column=token.column; if (CSVLexer::Token::T_STRING != token.type) { _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected string.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } QString name = token.value; QList lst; token = lexer.next(); while (CSVLexer::Token::T_NUMBER == token.type) { lst.append(token.value.toInt()); token = lexer.next(); if (CSVLexer::Token::T_COMMA == token.type) token = lexer.next(); } if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){ _errorMessage = QString("Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.") .arg(token.line).arg(token.column).arg(token.type).arg(token.value); return false; } return _handler->handleRoamingZone(idx, name, lst, line, column, _errorMessage); } /* ********************************************************************************************* * * Implementation of CSVReader * ********************************************************************************************* */ CSVReader::CSVReader(Config *config, QObject *parent) : CSVHandler(parent), _link(false), _config(config) { // pass... } bool CSVReader::read(Config *config, QTextStream &stream, QString &errorMessage) { // Seek to start-of-stream if (! stream.seek(0)) { errorMessage = "Cannot read CSV codeplug: Cannot seek within text stream. Abort import." ; return false; } config->clear(); CSVReader reader(config); CSVParser parser(&reader); if (! parser.parse(stream)) { errorMessage = parser.errorMessage(); errorMessage.append(tr("\nThe generic code-plug format might be changed with a new release of qdmr." "\nVisit https://github.com/hmatuschek/qdmr/releases for further information.")); return false; } reader._link = true; if (! parser.parse(stream)) { errorMessage = parser.errorMessage(); return false; } return true; } bool CSVReader::handleRadioId(const QList &ids, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); if (_link) return true; logDebug() << "Got " << ids.count() << " IDs..."; for (int i=0; iradioIDs()->add(id); _radioIDs[i+1] = id; } return true; } bool CSVReader::handleRadioName(const QString &name, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); if (_link && (!_config->settings()->defaultIdRef()->isNull())) { _config->settings()->defaultIdRef()->as()->setName(name); } return true; } bool CSVReader::handleIntroLine1(const QString &text, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); if (_link) { _config->settings()->setIntroLine1(text); } return true; } bool CSVReader::handleIntroLine2(const QString &text, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); if (_link) { _config->settings()->setIntroLine2(text); } return true; } bool CSVReader::handleMicLevel(unsigned level, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); if (_link) { _config->settings()->setMicLevel(level); } return true; } bool CSVReader::handleSpeech(bool speech, qint64 line, qint64 column, QString &errorMessage) { Q_UNUSED(line); Q_UNUSED(column); Q_UNUSED(errorMessage); if (_link) { _config->settings()->enableSpeech(speech); } return true; } bool CSVReader::handleDTMFContact(qint64 idx, const QString &name, const QString &num, bool rxTone, qint64 line, qint64 column, QString &errorMessage) { if (_link) { // pass... return true; } if (_digital_contacts.contains(idx)) { errorMessage = QString("Parse error @ %1,%2: Cannot create contact '%3' with index %4, index already taken.") .arg(line).arg(column).arg(name).arg(idx); return false; } DTMFContact *contact = new DTMFContact(name, num, rxTone); //_contacts[idx] = contact; _config->contacts()->add(contact); return true; } bool CSVReader::handleDigitalContact(qint64 idx, const QString &name, DMRContact::Type type, qint64 id, bool rxTone, qint64 line, qint64 column, QString &errorMessage) { if (_link) { // pass... return true; } if (_digital_contacts.contains(idx)) { errorMessage = QString("Parse error @ %1,%2: Cannot create contact '%3' with index %4, index already taken.") .arg(line).arg(column).arg(name).arg(idx); return false; } DMRContact *contact = new DMRContact(type, name, id, rxTone); _digital_contacts[idx] = contact; _config->contacts()->add(contact); return true; } bool CSVReader::handleGroupList(qint64 idx, const QString &name, const QList &contacts, qint64 line, qint64 column, QString &errorMessage) { if (_link) { foreach(qint64 i, contacts) { // Check contacts if (! _digital_contacts.contains(i)) { errorMessage = QString("Parse error @ %1,%2: Cannot create contact list '%3' with index %4, unknown index.") .arg(line).arg(column).arg(name).arg(i); return false; } // link contacts _rxgroups[idx]->addContact(_digital_contacts[i]); } // done return true; } // check index if (_rxgroups.contains(idx)) { errorMessage = QString("Parse error @ %1,%2: Cannot create RX-group list '%3' with index %4, index already taken.") .arg(line).arg(column).arg(name).arg(idx); return false; } // Create group list RXGroupList *lst = new RXGroupList(name); _config->rxGroupLists()->add(lst); _rxgroups[idx] = lst; return true; } bool CSVReader::handleDigitalChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan, qint64 tot, bool ro, DMRChannel::Admit admit, qint64 color, DMRChannel::TimeSlot slot, qint64 gl, qint64 contact, qint64 gps, qint64 roam, qint64 radioID, qint64 line, qint64 column, QString &errorMessage) { if (_link) { // Check RX Grouplist if (0 < gl) { if (! _rxgroups.contains(gl)) { errorMessage = QString("Parse error @ %1,%2: Cannot link digital channel '%3', unknown RX-group list %4.") .arg(line).arg(column).arg(name).arg(gl); return false; } _channels[idx]->as()->setGroupListObj(_rxgroups[gl]); } // Check TX Contact if (0 < contact) { if (! _digital_contacts.contains(contact)) { errorMessage = QString("Parse error @ %1,%2: Cannot link digital channel '%3', unknown contact index %4.") .arg(line).arg(column).arg(name).arg(contact); return false; } _channels[idx]->as()->setTXContactObj(_digital_contacts[contact]); } // Check scanlist if (0 < scan) { if (! _scanlists.contains(scan)) { errorMessage = QString("Parse error @ %1,%2: Cannot link digital channel '%3', unknown scan-list index %4.") .arg(line).arg(column).arg(name).arg(scan); return false; } _channels[idx]->as()->setScanList(_scanlists[scan]); } // Check GPS System if (0 < gps) { if (! _posSystems.contains(gps)) { errorMessage = QString("Parse error @ %1,%2: Cannot link digital channel '%3', unknown GPS system index %4.") .arg(line).arg(column).arg(name).arg(gps); return false; } _channels[idx]->as()->setAPRSObj(_posSystems[gps]); } // Check roaming zone if (0 == roam) { // index 0 mean default zone -> just set it _channels[idx]->as()->setRoamingZone(DefaultRoamingZone::get()); } else if (0 < roam) { // positive index means reference to roaming specific roaming zone if (! _roamingZones.contains(roam)) { errorMessage = QString("Parse error @ %1,%2: Cannot link digital channel '%3', unknown roaming zone index %4.") .arg(line).arg(column).arg(name).arg(roam); return false; } _channels[idx]->as()->setRoamingZone(_roamingZones[roam]); } // check radio ID if (-1 == radioID) { _channels[idx]->as()->setRadioIdObj(DefaultRadioID::get()); } else if ((0 < radioID) && (_radioIDs.contains(radioID))) { _channels[idx]->as()->setRadioIdObj(_radioIDs[radioID]); } else { errorMessage = QString("Parse error @ %1,%2: Cannot link digital channel '%3', unknown radio ID index %4.") .arg(line).arg(column).arg(name).arg(radioID); return false; } return true; } // check index if (_channels.contains(idx)) { errorMessage = QString("Parse error @ %1,%2: Cannot create digital channel '%3' with index %4, index already taken.") .arg(line).arg(column).arg(name).arg(idx); return false; } DMRChannel *chan = new DMRChannel(); chan->setName(name); chan->setRXFrequency(Frequency::fromMHz(rx)); chan->setTXFrequency(Frequency::fromMHz(tx)); chan->setPower(power); chan->setTimeout(tot); chan->setRXOnly(ro); chan->setAdmit(admit); chan->setColorCode(color); chan->setTimeSlot(slot); _config->channelList()->add(chan); _channels[idx] = chan; return true; } bool CSVReader::handleAnalogChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan, qint64 aprs, qint64 tot, bool ro, FMChannel::Admit admit, qint64 squelch, const SelectiveCall &rxTone, const SelectiveCall &txTone, FMChannel::Bandwidth bw, qint64 line, qint64 column, QString &errorMessage) { if (_link) { // Check scanlist if (0 < scan) { if (! _scanlists.contains(scan)) { errorMessage = QString("Parse error @ %1,%2: Cannot link analog channel '%3', unknown scan-list index %4.") .arg(line).arg(column).arg(name).arg(scan); return false; } _channels[idx]->as()->setScanList(_scanlists[scan]); } // Check APRS system if (0 < aprs) { if (! _posSystems.contains(aprs)) { errorMessage = QString("Parse error @ %1,%2: Cannot link analog channel '%3', unknown APRS system index %4.") .arg(line).arg(column).arg(name).arg(aprs); return false; } if (! _posSystems[aprs]->is()) { errorMessage = QString("Parse error @ %1,%2: Cannot link analog channel '%3', positioning system %4 ('%5') is not an APRS system!.") .arg(line).arg(column).arg(name).arg(aprs).arg(_posSystems[aprs]->name()); return false; } _channels[idx]->as()->setAPRSSystem(_posSystems[aprs]->as()); } return true; } // check index if (_channels.contains(idx)) { errorMessage = QString("Parse error @ %1,%2: Cannot create analog channel '%3' with index %4, index already taken.") .arg(line).arg(column).arg(name).arg(idx); return false; } FMChannel *chan = new FMChannel(); chan->setName(name); chan->setRXFrequency(Frequency::fromMHz(rx)); chan->setTXFrequency(Frequency::fromMHz(tx)); chan->setPower(power); chan->setTimeout(tot); chan->setRXOnly(ro); chan->setAdmit(admit); chan->setSquelch(squelch); chan->setRXTone(rxTone); chan->setTXTone(txTone); chan->setBandwidth(bw); _config->channelList()->add(chan); _channels[idx] = chan; return true; } bool CSVReader::handleZone(qint64 idx, const QString &name, bool a, const QList &channels, qint64 line, qint64 column, QString &errorMessage) { if (_link) { foreach(qint64 i, channels) { // Check channels if (! _channels.contains(i)) { errorMessage = QString("Parse error @ %1,%2: Cannot create zone '%3', unknown channel index %4.") .arg(line).arg(column).arg(name).arg(i); return false; } // link channels if (a) _zones[idx]->A()->add(_channels[i]); else _zones[idx]->B()->add(_channels[i]); } // done return true; } // check index if (! _zones.contains(idx)) { Zone *zone = new Zone(name); _zones[idx] = zone; _config->zones()->add(zone); } return true; } bool CSVReader::handleGPSSystem( qint64 idx, const QString &name, qint64 contactIdx, qint64 period, qint64 revertChannelIdx, qint64 line, qint64 column, QString &errorMessage) { if (_link) { GPSSystem *gps = _posSystems[idx]->as(); // Check contact ID if (! _digital_contacts.contains(contactIdx)) { errorMessage = QString("Parse error @ %1,%2: Cannot create GPS system '%3', unknown destination contact ID %4.") .arg(line).arg(column).arg(name).arg(contactIdx); logError() << errorMessage; return false; } gps->setContactObj(_digital_contacts[contactIdx]); if (revertChannelIdx) { if (! _channels.contains(revertChannelIdx)) { errorMessage = QString("Parse error @ %1,%2: Cannot create GPS system '%3', unknown revert channel ID %4.") .arg(line).arg(column).arg(name).arg(revertChannelIdx); return false; } if (! _channels[revertChannelIdx]->is()) { errorMessage = QString("Parse error @ %1,%2: Cannot create GPS system '%3', revert channel %4 is not a digital channel.") .arg(line).arg(column).arg(name).arg(revertChannelIdx); return false; } gps->setRevertChannel(_channels[revertChannelIdx]->as()); } return true; } // check index if (_posSystems.contains(idx)) { errorMessage = QString("Parse error @ %1,%2: Cannot create GPS system '%3' with index %4, index already taken.") .arg(line).arg(column).arg(name).arg(idx); return false; } GPSSystem *gps = new GPSSystem(name, nullptr, nullptr, period); _posSystems[idx] = gps; _config->posSystems()->add(gps); return true; } bool CSVReader::handleAPRSSystem( qint64 idx, const QString &name, qint64 channelIdx, qint64 period, const QString &src, unsigned srcSSID, const QString &dest, unsigned destSSID, const QString &path, const QString &iconname, const QString &message, qint64 line, qint64 column, QString &errorMessage) { if (_link) { APRSSystem *aprs = _posSystems[idx]->as(); if (! _channels.contains(channelIdx)) { errorMessage = QString("Parse error @ %1,%2: Cannot create APRS system '%3', unknown channel ID %4.") .arg(line).arg(column).arg(name).arg(channelIdx); return false; } if (! _channels[channelIdx]->is()) { errorMessage = QString("Parse error @ %1,%2: Cannot create APRS system '%3', transmit channel %4 is not an analog channel.") .arg(line).arg(column).arg(name).arg(channelIdx); return false; } aprs->setRevertChannel(_channels[channelIdx]->as()); return true; } // check index if (_posSystems.contains(idx)) { errorMessage = QString("Parse error @ %1,%2: Cannot create GPS system '%3' with index %4, index already taken.") .arg(line).arg(column).arg(name).arg(idx); return false; } APRSSystem::Icon icon = name2aprsicon(iconname); APRSSystem *aprs = new APRSSystem(name, nullptr, dest, destSSID, src, srcSSID, path, icon, message, period); _posSystems[idx] = aprs; _config->posSystems()->add(aprs); return true; } bool CSVReader::handleScanList(qint64 idx, const QString &name, qint64 pch1, qint64 pch2, qint64 txch, const QList &channels, qint64 line, qint64 column, QString &errorMessage) { if (_link) { // Check PriChan 1 if ((pch1>0) && (! _channels.contains(pch1))) { errorMessage = QString("Parse error @ %1,%2: Cannot create scanlist '%3', unknown priority channel 1 index %4.") .arg(line).arg(column).arg(name).arg(pch1); return false; } if (pch1 < 0) { _scanlists[idx]->setPrimaryChannel(SelectedChannel::get()); } else if (pch2 > 0) { _scanlists[idx]->setPrimaryChannel(_channels[pch1-1]); } // Check PriChan 2 if ((pch2>0) && (! _channels.contains(pch2))) { errorMessage = QString("Parse error @ %1,%2: Cannot create scanlist '%3', unknown priority channel 2 index %4.") .arg(line).arg(column).arg(name).arg(pch2); return false; } if (pch2 < 0) { _scanlists[idx]->setSecondaryChannel(SelectedChannel::get()); } else if (pch2 > 0){ _scanlists[idx]->setSecondaryChannel(_channels[pch2-1]); } // Check Tx channel if ((txch>0) && (! _channels.contains(txch-1))) { errorMessage = QString("Parse error @ %1,%2: Cannot create scanlist '%3', unknown TX channel index %4.") .arg(line).arg(column).arg(name).arg(txch-1); return false; } if (txch<0) { _scanlists[idx]->setRevertChannel(SelectedChannel::get()); } else if (txch>0) { _scanlists[idx]->setRevertChannel(_channels[txch-2]); } // Check channels foreach(qint64 i, channels) { if ((i>=0) && (!_channels.contains(i))) { errorMessage = QString("Parse error @ %1,%2: Cannot create scanlist '%3', unknown channel index %4.") .arg(line).arg(column).arg(name).arg(i); return false; } // link channels if (i<0) _scanlists[idx]->addChannel(SelectedChannel::get()); else _scanlists[idx]->addChannel(_channels[i]); } // done return true; } // check index if (_scanlists.contains(idx)) { errorMessage = QString("Parse error @ %1,%2: Cannot create scan list '%3' with index %4, index already taken.") .arg(line).arg(column).arg(name).arg(idx); return false; } ScanList *lst = new ScanList(name); _scanlists[idx] = lst; _config->scanlists()->add(lst); return true; } bool CSVReader::handleRoamingZone(qint64 idx, const QString &name, const QList &channels, qint64 line, qint64 column, QString &errorMessage) { if (_link) { foreach(qint64 i, channels) { // Check channels if (! _channels.contains(i)) { errorMessage = QString("Parse error @ %1,%2: Cannot create zone '%3', unknown channel index %4.") .arg(line).arg(column).arg(name).arg(i); return false; } else if (_channels[i]->is()) { errorMessage = QString("Parse error @ %1,%2: Cannot add channel '%3' (idx %4) " "to roaming zone, only digital channels can be used.") .arg(line).arg(column).arg(name).arg(i); return false; } RoamingChannel *rch = RoamingChannel::fromDMRChannel(_channels[i]->as()); _config->roamingChannels()->add(rch); _roamingZones[idx]->addChannel(rch); } // done return true; } // check index if (! _roamingZones.contains(idx)) { RoamingZone *zone = new RoamingZone(name); _roamingZones[idx] = zone; _config->roamingZones()->add(zone); } return true; } qdmr-0.12.3/lib/csvreader.hh000066400000000000000000000341171501654372000156160ustar00rootroot00000000000000#ifndef CSVREADER_HH #define CSVREADER_HH #include #include #include #include #include "channel.hh" #include "contact.hh" class Config; class DMRContact; class RXGroupList; class Zone; class PositioningSystem; class ScanList; class RoamingZone; /** The lexer class divides a text stream into tokens. */ class CSVLexer: public QObject { Q_OBJECT public: /** The token. */ struct Token { public: /** Possible token types. */ enum TokenType { T_KEYWORD, ///< A Keyword/Identifier. T_APRSCALL, ///< A APRS call of form CALL-SSID. T_STRING, ///< A quoted string. T_NUMBER, ///< An integer or floating point number. T_DCS_N, ///< A normal DCS code number. T_DCS_I, ///< An inverted DCS code number. T_COLON, ///< A colon. T_NOT_SET, ///< A dash, being used as "not-set". T_ENABLED, ///< A plus sign, being used as "enabled" T_COMMA, ///< A comma T_WHITESPACE, ///< Any whitespace character excluding newline. T_NEWLINE, ///< A new line. T_COMMENT, ///< A comment starts with # end ends at the line-end. T_END_OF_STREAM, ///< Indicates the end-of-input. T_ERROR ///< Indicates a lexer error. }; /// The token type. TokenType type; /// The token value. QString value; /// Line number. qint64 line; /// Column number. qint64 column; }; /// Current state of lexer. struct State { /// The current stream offset. qint64 offset; /// The current line count. qint64 line; /// The current column number. qint64 column; }; public: /** Constructs a lexer for the given stream. */ CSVLexer(QTextStream &stream, QObject *parent=nullptr); /** Saves the current lexer state. */ void push(); /** Restores the last lexer state. */ void pop(); /** Reads the next token from the stream. */ Token next(); /** Returns the last error message. */ const QString &errorMessage() const; protected: /** Internal used function to get the next token. Also returns ignored tokens like whitespace * and comment. */ Token lex(); protected: /// The error message. QString _errorMessage; /// The text stream to read from. QTextStream &_stream; /// The stack of saved lexer states QVector _stack; /// The current line count QString _currentLine; /// The list of patterns to match static QVector< QPair > _pattern; }; /** Basic parse-handler interface. * * That is, a set of callbacks getting called by the parser on the occurrence of a particular * statement in the config file. * @ingroup conf */ class CSVHandler: public QObject { Q_OBJECT protected: /** Hidden constructor. */ explicit CSVHandler(QObject *parent=nullptr); public: /** Destructor. */ virtual ~CSVHandler(); /** Gets called once the DMR IDs has been parsed. */ virtual bool handleRadioId(const QList &ids, qint64 line, qint64 column, QString &errorMessage); /** Gets called once the radio name has been parsed. */ virtual bool handleRadioName(const QString &name, qint64 line, qint64 column, QString &errorMessage); /** Gets called once the first intro line has been parsed. */ virtual bool handleIntroLine1(const QString &text, qint64 line, qint64 column, QString &errorMessage); /** Gets called once the second intro line has been parsed. */ virtual bool handleIntroLine2(const QString &text, qint64 line, qint64 column, QString &errorMessage); /** Gets called once the MIC level has been parsed. */ virtual bool handleMicLevel(unsigned level, qint64 line, qint64 column, QString &errorMessage); /** Gets called once the Speech flag has been parsed. */ virtual bool handleSpeech(bool speech, qint64 line, qint64 column, QString &errorMessage); /** Gets called once a DTMF contact has been parsed. */ virtual bool handleDTMFContact(qint64 idx, const QString &name, const QString &num, bool rxTone, qint64 line, qint64 column, QString &errorMessage); /** Gets called once a digital contact has been parsed. */ virtual bool handleDigitalContact(qint64 idx, const QString &name, DMRContact::Type type, qint64 id, bool rxTone, qint64 line, qint64 column, QString &errorMessage); /** Gets called once an RX group list has been parsed. */ virtual bool handleGroupList(qint64 idx, const QString &name, const QList &contacts, qint64 line, qint64 column, QString &errorMessage); /** Gets called once a digital channel has been parsed. */ virtual bool handleDigitalChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan, qint64 tot, bool ro, DMRChannel::Admit admit, qint64 color, DMRChannel::TimeSlot slot, qint64 gl, qint64 contact, qint64 gps, qint64 roam, qint64 radioID, qint64 line, qint64 column, QString &errorMessage); /** Gets called once a analog channel has been parsed. */ virtual bool handleAnalogChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan, qint64 aprs, qint64 tot, bool ro, FMChannel::Admit admit, qint64 squelch, const SelectiveCall &rxTone, const SelectiveCall &txTone, FMChannel::Bandwidth bw, qint64 line, qint64 column, QString &errorMessage); /** Gets called once a zone list has been parsed. */ virtual bool handleZone(qint64 idx, const QString &name, bool a, const QList &channels, qint64 line, qint64 column, QString &errorMessage); /** Gets called once a GPS system has been parsed. */ virtual bool handleGPSSystem(qint64 idx, const QString &name, qint64 contactIdx, qint64 period, qint64 revertChannelIdx, qint64 line, qint64 column, QString &errorMessage); /** Gets called once a APRS system has been parsed. */ virtual bool handleAPRSSystem(qint64 idx, const QString &name, qint64 channelIdx, qint64 period, const QString &src, unsigned srcSSID, const QString &dest, unsigned destSSID, const QString &path, const QString &icon, const QString &message, qint64 line, qint64 column, QString &errorMessage); /** Gets called once a scan list has been parsed. */ virtual bool handleScanList(qint64 idx, const QString &name, qint64 pch1, qint64 pch2, qint64 txch, const QList &channels, qint64 line, qint64 column, QString &errorMessage); /** Gets called once a roaming zone list has been parsed. */ virtual bool handleRoamingZone(qint64 idx, const QString &name, const QList &channels, qint64 line, qint64 column, QString &errorMessage); }; /** The actual config file parser. * * This class parses the config file and calls the associated callback functions of a handler * instance that is responsible to assemble the final @c Config instance. */ class CSVParser: public QObject { Q_OBJECT public: /** Constructs a parser using the given handler instance. */ explicit CSVParser(CSVHandler *handler, QObject *parent=nullptr); /** Parses the given text stream. */ bool parse(QTextStream &stream); /** Returns the current error message, for example if @c parse returns @c false. */ const QString &errorMessage() const; protected: /** Internal function to parse DMR IDs. */ bool _parse_radio_id(CSVLexer &lexer); /** Internal function to parse radio names. */ bool _parse_radio_name(CSVLexer &lexer); /** Internal function to parse intro line 1. */ bool _parse_introline1(CSVLexer &lexer); /** Internal function to parse intro line 2. */ bool _parse_introline2(CSVLexer &lexer); /** Internal function to parse MIC level. */ bool _parse_mic_level(CSVLexer &lexer); /** Internal function to parse Speech flag. */ bool _parse_speech(CSVLexer &lexer); /** Internal function to parse UserDB flag. */ bool _parse_userdb(CSVLexer &lexer); /** Internal function to parse a digital contact list. */ bool _parse_contacts(CSVLexer &lexer); /** Internal function to parse digital contact. */ bool _parse_contact(qint64 id, CSVLexer &lexer); /** Internal function to parse an RX group list. */ bool _parse_rx_groups(CSVLexer &lexer); /** Internal function to parse an RX group. */ bool _parse_rx_group(qint64 id, CSVLexer &lexer); /** Internal function to parse a digital channel list. */ bool _parse_digital_channels(CSVLexer &lexer); /** Internal function to parse a digital channel. */ bool _parse_digital_channel(qint64 id, CSVLexer &lexer); /** Internal function to parse an analog channel list. */ bool _parse_analog_channels(CSVLexer &lexer); /** Internal function to parse an analog channel. */ bool _parse_analog_channel(qint64 id, CSVLexer &lexer); /** Internal function to parse a zone list. */ bool _parse_zones(CSVLexer &lexer); /** Internal function to parse a zone. */ bool _parse_zone(qint64 id, CSVLexer &lexer); /** Internal function to parse a GPS system list. */ bool _parse_gps_systems(CSVLexer &lexer); /** Internal function to parse a GPS system. */ bool _parse_gps_system(qint64 id, CSVLexer &lexer); /** Internal function to parse a APRS system list. */ bool _parse_aprs_systems(CSVLexer &lexer); /** Internal function to parse a APRS system. */ bool _parse_aprs_system(qint64 id, CSVLexer &lexer); /** Internal function to parse a scanlist list. */ bool _parse_scanlists(CSVLexer &lexer); /** Internal function to parse a scanlist. */ bool _parse_scanlist(qint64 id, CSVLexer &lexer); /** Internal function to parse a roaming zone list. */ bool _parse_roaming_zones(CSVLexer &lexer); /** Internal function to parse a zone. */ bool _parse_roaming_zone(qint64 id, CSVLexer &lexer); protected: /** Holds the current error message. */ QString _errorMessage; /** The handler instance. */ CSVHandler *_handler; }; /** Implements the text-file codeplug reader. * @ingroup conf */ class CSVReader : public CSVHandler { Q_OBJECT protected: /** Hidden constructor. Consider using the static @c read method. */ CSVReader(Config *config, QObject *parent=nullptr); public: /** Reads a config file from @c stream and stores the read configuration into @c config. * @returns @c true on success. */ static bool read(Config *config, QTextStream &stream, QString &errorMessage); virtual bool handleRadioId(const QList &ids, qint64 line, qint64 column, QString &errorMessage); virtual bool handleRadioName(const QString &name, qint64 line, qint64 column, QString &errorMessage); virtual bool handleIntroLine1(const QString &text, qint64 line, qint64 column, QString &errorMessage); virtual bool handleIntroLine2(const QString &text, qint64 line, qint64 column, QString &errorMessage); virtual bool handleMicLevel(unsigned level, qint64 line, qint64 column, QString &errorMessage); virtual bool handleSpeech(bool speech, qint64 line, qint64 column, QString &errorMessage); virtual bool handleDTMFContact(qint64 idx, const QString &name, const QString &num, bool rxTone, qint64 line, qint64 column, QString &errorMessage); virtual bool handleDigitalContact( qint64 idx, const QString &name, DMRContact::Type type, qint64 id, bool rxTone, qint64 line, qint64 column, QString &errorMessage); virtual bool handleGroupList(qint64 idx, const QString &name, const QList &contacts, qint64 line, qint64 column, QString &errorMessage); virtual bool handleDigitalChannel( qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan, qint64 tot, bool ro, DMRChannel::Admit admit, qint64 color, DMRChannel::TimeSlot slot, qint64 gl, qint64 contact, qint64 gps, qint64 roam, qint64 radioID, qint64 line, qint64 column, QString &errorMessage); virtual bool handleAnalogChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan, qint64 aprs, qint64 tot, bool ro, FMChannel::Admit admit, qint64 squelch, const SelectiveCall &rxTone, const SelectiveCall &txTone, FMChannel::Bandwidth bw, qint64 line, qint64 column, QString &errorMessage); virtual bool handleZone(qint64 idx, const QString &name, bool a, const QList &channels, qint64 line, qint64 column, QString &errorMessage); virtual bool handleGPSSystem(qint64 idx, const QString &name, qint64 contactIdx, qint64 period, qint64 revertChannelIdx, qint64 line, qint64 column, QString &errorMessage); virtual bool handleAPRSSystem(qint64 idx, const QString &name, qint64 channelIdx, qint64 period, const QString &src, unsigned srcSSID, const QString &dest, unsigned destSSID, const QString &path, const QString &icon, const QString &message, qint64 line, qint64 column, QString &errorMessage); virtual bool handleScanList(qint64 idx, const QString &name, qint64 pch1, qint64 pch2, qint64 txch, const QList &channels, qint64 line, qint64 column, QString &errorMessage); virtual bool handleRoamingZone(qint64 idx, const QString &name, const QList &channels, qint64 line, qint64 column, QString &errorMessage); protected: /** If @c true, the reader is in "link" mode. */ bool _link; /** The configuration to read. */ Config *_config; /** Index <-> Channel map. */ QMap _channels; /** Index <-> Digital contact map. */ QMap _digital_contacts; /** Index <-> RX group list map. */ QMap _rxgroups; /** Index <-> Zone map. */ QMap _zones; /** Index <-> GPS System map. */ QMap _posSystems; /** Index <-> Scan list map. */ QMap _scanlists; /** Index <-> RoamingZone map */ QMap _roamingZones; /** Index <-> radio ID map */ QMap _radioIDs; }; #endif // CSVREADER_HH qdmr-0.12.3/lib/d578uv.cc000066400000000000000000000205201501654372000146610ustar00rootroot00000000000000#include "userdatabase.hh" #include "d578uv.hh" #include "config.hh" #include "logger.hh" #include "d578uv_limits.hh" #include "d578uv_codeplug.hh" // uses same callsign db as 878 #include "d878uv2_callsigndb.hh" #define RBSIZE 16 #define WBSIZE 16 D578UV::D578UV(AnytoneInterface *device, QObject *parent) : AnytoneRadio("Anytone AT-D578UV", device, parent), _limits(nullptr) { _codeplug = new D578UVCodeplug(this); _codeplug->clear(); _callsigns = new D878UV2CallsignDB(this); // Get device info and determine supported TX frequency bands AnytoneInterface::RadioVariant info; if (_dev) _dev->getInfo(info); switch (info.bands) { case 0x00: case 0x01: _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, info.version, this); break; case 0x02: _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x03: _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x04: _limits = new D578UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} }, info.version, this); break; case 0x05: _limits = new D578UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(434.), Frequency::fromMHz(447.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(434.), Frequency::fromMHz(447.)} }, info.version, this); break; case 0x06: _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this); break; case 0x07: _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this); break; case 0x08: _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, info.version, this); break; case 0x09: _limits = new D578UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, info.version, this); break; case 0x0a: _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(450.)} }, info.version, this); break; case 0x0b: _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this); break; case 0x0c: _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} }, info.version, this); break; case 0x0d: _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} },info.version, this); break; case 0x0e: _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(220.),Frequency::fromMHz(225.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(220.),Frequency::fromMHz(225.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this); break; case 0x0f: _limits = new D578UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} }, info.version, this); break; case 0x10: _limits = new D578UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(147.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(147.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x11: _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)} }, info.version, this); break; case 0x12: _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(220.),Frequency::fromMHz(225.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(222.),Frequency::fromMHz(225.)}, {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this); break; default: logInfo() << "Unknown band-code 0x" << QString::number(int(info.bands), 16) << ": Do not perform a frequency range check."; _limits = new D578UVLimits({}, {}, info.version, this); break; } } const RadioLimits & D578UV::limits() const { return *_limits; } RadioInfo D578UV::defaultRadioInfo() { return RadioInfo( RadioInfo::D578UV, "d578uv", "AT-D578UV", "AnyTone", AnytoneInterface::interfaceInfo()); } qdmr-0.12.3/lib/d578uv.hh000066400000000000000000000017461501654372000147040ustar00rootroot00000000000000/** @defgroup d578uv Anytone AT-D578UV * Device specific classes for Anytone AT-D578UV. * * \image html d578uv.jpg "AT-D578UV" width=200px * \image latex d578uv.jpg "AT-D578UV" width=200px * * @ingroup anytone */ #ifndef __D578UV_HH__ #define __D578UV_HH__ #include "anytone_radio.hh" #include "anytone_interface.hh" #include "d878uv2_callsigndb.hh" /** Implements an interface to Anytone AT-D578UV VHF/UHF 50W DMR (Tier I & II) radios. * * @ingroup d578uv */ class D578UV: public AnytoneRadio { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit D578UV(AnytoneInterface *device=nullptr, QObject *parent=nullptr); const RadioLimits &limits() const; /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); private: /** Holds the limits for the radio. */ RadioLimits *_limits; }; #endif // __D878UV_HH__ qdmr-0.12.3/lib/d578uv_codeplug.cc000066400000000000000000002345301501654372000165530ustar00rootroot00000000000000#include "gpssystem.hh" #include "userdatabase.hh" #include "roamingchannel.hh" #include "d578uv_codeplug.hh" #include "config.hh" #include "utils.hh" #include "channel.hh" #include "config.h" #include "logger.hh" #include "roamingchannel.hh" #include #include /* ******************************************************************************************** * * Implementation of D578UVCodeplug::ChannelElement * ******************************************************************************************** */ D578UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, unsigned size) : D878UVCodeplug::ChannelElement(ptr, size) { // pass... } D578UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr) : D878UVCodeplug::ChannelElement(ptr) { // pass... } bool D578UVCodeplug::ChannelElement::handsFree() const { return getBit(0x0034, 2); } void D578UVCodeplug::ChannelElement::enableHandsFree(bool enable) { setBit(0x0034, 2, enable); } bool D578UVCodeplug::ChannelElement::roamingEnabled() const { // inverted! return !getBit(0x0034,3); } void D578UVCodeplug::ChannelElement::enableRoaming(bool enable) { // inverted! setBit(0x0034, 3, !enable); } bool D578UVCodeplug::ChannelElement::dataACK() const { // inverted! return !getBit(0x003d,3); } void D578UVCodeplug::ChannelElement::enableDataACK(bool enable) { // inverted! setBit(0x003d, 3, !enable); } unsigned D578UVCodeplug::ChannelElement::dmrEncryptionKeyIndex() const { return 0; } void D578UVCodeplug::ChannelElement::setDMREncryptionKeyIndex(unsigned idx) { Q_UNUSED(idx) } bool D578UVCodeplug::ChannelElement::analogScambler() const { return getUInt8(0x003a); } void D578UVCodeplug::ChannelElement::enableAnalogScamber(bool enable) { setUInt8(0x003a, (enable ? 0x01 : 0x00)); } Channel * D578UVCodeplug::ChannelElement::toChannelObj(Context &ctx) const { Channel *ch = D878UVCodeplug::ChannelElement::toChannelObj(ctx); if (nullptr == ch) return nullptr; // Apply extensions if (FMChannel *fch = ch->as()) { if (AnytoneFMChannelExtension *ext = fch->anytoneChannelExtension()) { // Common settings ext->enableHandsFree(handsFree()); // FM specific settings ext->enableScrambler(analogScambler()); } } else if (DMRChannel *dch = ch->as()) { if (AnytoneDMRChannelExtension *ext = dch->anytoneChannelExtension()) { // Common settings ext->enableHandsFree(handsFree()); // DMR specific extensions } } // Done. return ch; } /* ******************************************************************************************** * * Implementation of D578UVCodeplug::GeneralSettingsElement::KeyFunction * ******************************************************************************************** */ uint8_t D578UVCodeplug::GeneralSettingsElement::KeyFunction::encode(AnytoneKeySettingsExtension::KeyFunction func) { switch (func) { case AnytoneKeySettingsExtension::KeyFunction::Off: return (uint8_t)KeyFunction::Off; case AnytoneKeySettingsExtension::KeyFunction::Voltage: return (uint8_t)KeyFunction::Voltage; case AnytoneKeySettingsExtension::KeyFunction::Power: return (uint8_t)KeyFunction::Power; case AnytoneKeySettingsExtension::KeyFunction::Repeater: return (uint8_t)KeyFunction::Repeater; case AnytoneKeySettingsExtension::KeyFunction::Reverse: return (uint8_t)KeyFunction::Reverse; case AnytoneKeySettingsExtension::KeyFunction::Encryption: return (uint8_t)KeyFunction::Encryption; case AnytoneKeySettingsExtension::KeyFunction::Call: return (uint8_t)KeyFunction::Call; case AnytoneKeySettingsExtension::KeyFunction::ToggleVFO: return (uint8_t)KeyFunction::ToggleVFO; case AnytoneKeySettingsExtension::KeyFunction::Scan: return (uint8_t)KeyFunction::Scan; case AnytoneKeySettingsExtension::KeyFunction::WFM: return (uint8_t)KeyFunction::WFM; case AnytoneKeySettingsExtension::KeyFunction::Alarm: return (uint8_t)KeyFunction::Alarm; case AnytoneKeySettingsExtension::KeyFunction::RecordSwitch: return (uint8_t)KeyFunction::RecordSwitch; case AnytoneKeySettingsExtension::KeyFunction::Record: return (uint8_t)KeyFunction::Record; case AnytoneKeySettingsExtension::KeyFunction::SMS: return (uint8_t)KeyFunction::SMS; case AnytoneKeySettingsExtension::KeyFunction::Dial: return (uint8_t)KeyFunction::Dial; case AnytoneKeySettingsExtension::KeyFunction::GPSInformation: return (uint8_t)KeyFunction::GPSInformation; case AnytoneKeySettingsExtension::KeyFunction::Monitor: return (uint8_t)KeyFunction::Monitor; case AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel: return (uint8_t)KeyFunction::ToggleMainChannel; case AnytoneKeySettingsExtension::KeyFunction::HotKey1: return (uint8_t)KeyFunction::HotKey1; case AnytoneKeySettingsExtension::KeyFunction::HotKey2: return (uint8_t)KeyFunction::HotKey2; case AnytoneKeySettingsExtension::KeyFunction::HotKey3: return (uint8_t)KeyFunction::HotKey3; case AnytoneKeySettingsExtension::KeyFunction::HotKey4: return (uint8_t)KeyFunction::HotKey4; case AnytoneKeySettingsExtension::KeyFunction::HotKey5: return (uint8_t)KeyFunction::HotKey5; case AnytoneKeySettingsExtension::KeyFunction::HotKey6: return (uint8_t)KeyFunction::HotKey6; case AnytoneKeySettingsExtension::KeyFunction::WorkAlone: return (uint8_t)KeyFunction::WorkAlone; case AnytoneKeySettingsExtension::KeyFunction::SkipChannel: return (uint8_t)KeyFunction::SkipChannel; case AnytoneKeySettingsExtension::KeyFunction::DMRMonitor: return (uint8_t)KeyFunction::DMRMonitor; case AnytoneKeySettingsExtension::KeyFunction::SubChannel: return (uint8_t)KeyFunction::SubChannel; case AnytoneKeySettingsExtension::KeyFunction::PriorityZone: return (uint8_t)KeyFunction::PriorityZone; case AnytoneKeySettingsExtension::KeyFunction::VFOScan: return (uint8_t)KeyFunction::VFOScan; case AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality: return (uint8_t)KeyFunction::MICSoundQuality; case AnytoneKeySettingsExtension::KeyFunction::LastCallReply: return (uint8_t)KeyFunction::LastCallReply; case AnytoneKeySettingsExtension::KeyFunction::ChannelType: return (uint8_t)KeyFunction::ChannelType; case AnytoneKeySettingsExtension::KeyFunction::Ranging: return (uint8_t)KeyFunction::Ranging; case AnytoneKeySettingsExtension::KeyFunction::Roaming: return (uint8_t)KeyFunction::Roaming; case AnytoneKeySettingsExtension::KeyFunction::ChannelRanging: return (uint8_t)KeyFunction::ChannelRanging; case AnytoneKeySettingsExtension::KeyFunction::MaxVolume: return (uint8_t)KeyFunction::MaxVolume; case AnytoneKeySettingsExtension::KeyFunction::Slot: return (uint8_t)KeyFunction::Slot; case AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch: return (uint8_t)KeyFunction::APRSTypeSwitch; case AnytoneKeySettingsExtension::KeyFunction::Zone: return (uint8_t)KeyFunction::Zone; case AnytoneKeySettingsExtension::KeyFunction::MuteA: return (uint8_t)KeyFunction::MuteA; case AnytoneKeySettingsExtension::KeyFunction::MuteB: return (uint8_t)KeyFunction::MuteB; case AnytoneKeySettingsExtension::KeyFunction::RoamingSet: return (uint8_t)KeyFunction::RoamingSet; case AnytoneKeySettingsExtension::KeyFunction::APRSSet: return (uint8_t)KeyFunction::APRSSet; case AnytoneKeySettingsExtension::KeyFunction::ZoneUp: return (uint8_t)KeyFunction::ZoneUp; case AnytoneKeySettingsExtension::KeyFunction::ZoneDown: return (uint8_t)KeyFunction::ZoneDown; case AnytoneKeySettingsExtension::KeyFunction::XBandRepeater: return (uint8_t)KeyFunction::XBandRepeater; case AnytoneKeySettingsExtension::KeyFunction::Speaker: return (uint8_t)KeyFunction::Speaker; case AnytoneKeySettingsExtension::KeyFunction::ChannelName: return (uint8_t)KeyFunction::ChannelName; case AnytoneKeySettingsExtension::KeyFunction::Bluetooth: return (uint8_t)KeyFunction::Bluetooth; case AnytoneKeySettingsExtension::KeyFunction::GPS: return (uint8_t)KeyFunction::GPS; case AnytoneKeySettingsExtension::KeyFunction::CDTScan: return (uint8_t)KeyFunction::CDTScan; case AnytoneKeySettingsExtension::KeyFunction::TBSTSend: return (uint8_t)KeyFunction::TBSTSend; case AnytoneKeySettingsExtension::KeyFunction::APRSSend: return (uint8_t)KeyFunction::APRSSend; case AnytoneKeySettingsExtension::KeyFunction::APRSInfo: return (uint8_t)KeyFunction::APRSInfo; case AnytoneKeySettingsExtension::KeyFunction::GPSRoaming: return (uint8_t)KeyFunction::GPSRoaming; case AnytoneKeySettingsExtension::KeyFunction::Squelch: return (uint8_t)KeyFunction::Squelch; case AnytoneKeySettingsExtension::KeyFunction::NoiseReductionTX: return (uint8_t)KeyFunction::NoiseReductionTX; default: return (uint8_t)KeyFunction::Off; } } AnytoneKeySettingsExtension::KeyFunction D578UVCodeplug::GeneralSettingsElement::KeyFunction::decode(uint8_t code) { switch ((KeyFunctionCode)code) { case KeyFunction::Off: return AnytoneKeySettingsExtension::KeyFunction::Off; case KeyFunction::Voltage: return AnytoneKeySettingsExtension::KeyFunction::Voltage; case KeyFunction::Power: return AnytoneKeySettingsExtension::KeyFunction::Power; case KeyFunction::Repeater: return AnytoneKeySettingsExtension::KeyFunction::Repeater; case KeyFunction::Reverse: return AnytoneKeySettingsExtension::KeyFunction::Reverse; case KeyFunction::Encryption: return AnytoneKeySettingsExtension::KeyFunction::Encryption; case KeyFunction::Call: return AnytoneKeySettingsExtension::KeyFunction::Call; case KeyFunction::ToggleVFO: return AnytoneKeySettingsExtension::KeyFunction::ToggleVFO; case KeyFunction::Scan: return AnytoneKeySettingsExtension::KeyFunction::Scan; case KeyFunction::WFM: return AnytoneKeySettingsExtension::KeyFunction::WFM; case KeyFunction::Alarm: return AnytoneKeySettingsExtension::KeyFunction::Alarm; case KeyFunction::RecordSwitch: return AnytoneKeySettingsExtension::KeyFunction::RecordSwitch; case KeyFunction::Record: return AnytoneKeySettingsExtension::KeyFunction::Record; case KeyFunction::SMS: return AnytoneKeySettingsExtension::KeyFunction::SMS; case KeyFunction::Dial: return AnytoneKeySettingsExtension::KeyFunction::Dial; case KeyFunction::GPSInformation: return AnytoneKeySettingsExtension::KeyFunction::GPSInformation; case KeyFunction::Monitor: return AnytoneKeySettingsExtension::KeyFunction::Monitor; case KeyFunction::ToggleMainChannel: return AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel; case KeyFunction::HotKey1: return AnytoneKeySettingsExtension::KeyFunction::HotKey1; case KeyFunction::HotKey2: return AnytoneKeySettingsExtension::KeyFunction::HotKey2; case KeyFunction::HotKey3: return AnytoneKeySettingsExtension::KeyFunction::HotKey3; case KeyFunction::HotKey4: return AnytoneKeySettingsExtension::KeyFunction::HotKey4; case KeyFunction::HotKey5: return AnytoneKeySettingsExtension::KeyFunction::HotKey5; case KeyFunction::HotKey6: return AnytoneKeySettingsExtension::KeyFunction::HotKey6; case KeyFunction::WorkAlone: return AnytoneKeySettingsExtension::KeyFunction::WorkAlone; case KeyFunction::SkipChannel: return AnytoneKeySettingsExtension::KeyFunction::SkipChannel; case KeyFunction::DMRMonitor: return AnytoneKeySettingsExtension::KeyFunction::DMRMonitor; case KeyFunction::SubChannel: return AnytoneKeySettingsExtension::KeyFunction::SubChannel; case KeyFunction::PriorityZone: return AnytoneKeySettingsExtension::KeyFunction::PriorityZone; case KeyFunction::VFOScan: return AnytoneKeySettingsExtension::KeyFunction::VFOScan; case KeyFunction::MICSoundQuality: return AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality; case KeyFunction::LastCallReply: return AnytoneKeySettingsExtension::KeyFunction::LastCallReply; case KeyFunction::ChannelType: return AnytoneKeySettingsExtension::KeyFunction::ChannelType; case KeyFunction::Ranging: return AnytoneKeySettingsExtension::KeyFunction::Ranging; case KeyFunction::Roaming: return AnytoneKeySettingsExtension::KeyFunction::Roaming; case KeyFunction::ChannelRanging: return AnytoneKeySettingsExtension::KeyFunction::ChannelRanging; case KeyFunction::MaxVolume: return AnytoneKeySettingsExtension::KeyFunction::MaxVolume; case KeyFunction::Slot: return AnytoneKeySettingsExtension::KeyFunction::Slot; case KeyFunction::APRSTypeSwitch: return AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch; case KeyFunction::Zone: return AnytoneKeySettingsExtension::KeyFunction::Zone; case KeyFunction::MuteA: return AnytoneKeySettingsExtension::KeyFunction::MuteA; case KeyFunction::MuteB: return AnytoneKeySettingsExtension::KeyFunction::MuteB; case KeyFunction::RoamingSet: return AnytoneKeySettingsExtension::KeyFunction::RoamingSet; case KeyFunction::APRSSet: return AnytoneKeySettingsExtension::KeyFunction::APRSSet; case KeyFunction::ZoneUp: return AnytoneKeySettingsExtension::KeyFunction::ZoneUp; case KeyFunction::ZoneDown: return AnytoneKeySettingsExtension::KeyFunction::ZoneDown; case KeyFunction::XBandRepeater: return AnytoneKeySettingsExtension::KeyFunction::XBandRepeater; case KeyFunction::Speaker: return AnytoneKeySettingsExtension::KeyFunction::Speaker; case KeyFunction::ChannelName: return AnytoneKeySettingsExtension::KeyFunction::ChannelName; case KeyFunction::Bluetooth: return AnytoneKeySettingsExtension::KeyFunction::Bluetooth; case KeyFunction::GPS: return AnytoneKeySettingsExtension::KeyFunction::GPS; case KeyFunction::CDTScan: return AnytoneKeySettingsExtension::KeyFunction::CDTScan; case KeyFunction::TBSTSend: return AnytoneKeySettingsExtension::KeyFunction::TBSTSend; case KeyFunction::APRSSend: return AnytoneKeySettingsExtension::KeyFunction::APRSSend; case KeyFunction::APRSInfo: return AnytoneKeySettingsExtension::KeyFunction::APRSInfo; case KeyFunction::GPSRoaming: return AnytoneKeySettingsExtension::KeyFunction::GPSRoaming; case KeyFunction::Squelch: return AnytoneKeySettingsExtension::KeyFunction::Squelch; case KeyFunction::NoiseReductionTX: return AnytoneKeySettingsExtension::KeyFunction::NoiseReductionTX; default: return AnytoneKeySettingsExtension::KeyFunction::Off; } } /* ******************************************************************************************** * * Implementation of D578UVCodeplug::GeneralSettingsElement::TimeZone * ******************************************************************************************** */ QVector D578UVCodeplug::GeneralSettingsElement::TimeZone::_timeZones = { QTimeZone(-43200), QTimeZone(-39600), QTimeZone(-36000), QTimeZone(-32400), QTimeZone(-28800), QTimeZone(-25200), QTimeZone(-21600), QTimeZone(-18000), QTimeZone(-14400), QTimeZone(-12600), QTimeZone(-10800), QTimeZone(- 7200), QTimeZone(- 3600), QTimeZone( 0), QTimeZone( 3600), QTimeZone( 7200), QTimeZone( 10800), QTimeZone( 12600), QTimeZone(-28800), QTimeZone( 14400), QTimeZone( 16200), QTimeZone( 18000), QTimeZone( 19800), QTimeZone( 20700), QTimeZone( 21600), QTimeZone( 25200), QTimeZone( 28600), QTimeZone( 30600), QTimeZone( 32400), QTimeZone( 36000), QTimeZone( 39600), QTimeZone( 43200), QTimeZone( 46800) }; QTimeZone D578UVCodeplug::GeneralSettingsElement::TimeZone::decode(uint8_t code) { if (code >= _timeZones.size()) return _timeZones.back(); return _timeZones.at(code); } uint8_t D578UVCodeplug::GeneralSettingsElement::TimeZone::encode(const QTimeZone &zone) { if (! _timeZones.contains(zone)) return 13; //<- UTC return _timeZones.indexOf(zone); } /* ******************************************************************************************** * * Implementation of D578UVCodeplug::GeneralSettingsElement * ******************************************************************************************** */ D578UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size) : AnytoneCodeplug::GeneralSettingsElement(ptr, size) { // pass... } D578UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr) : AnytoneCodeplug::GeneralSettingsElement(ptr, GeneralSettingsElement::size()) { // pass... } bool D578UVCodeplug::GeneralSettingsElement::keyToneEnabled() const { return 0x00 != getUInt8(Offset::enableKeyTone()); } void D578UVCodeplug::GeneralSettingsElement::enableKeyTone(bool enable) { setUInt8(Offset::enableKeyTone(), enable ? 0x01 : 0x00); } unsigned D578UVCodeplug::GeneralSettingsElement::transmitTimeout() const { return ((unsigned)getUInt8(Offset::transmitTimeout()))*30; } void D578UVCodeplug::GeneralSettingsElement::setTransmitTimeout(unsigned tot) { setUInt8(Offset::transmitTimeout(), tot/30); } AnytoneDisplaySettingsExtension::Language D578UVCodeplug::GeneralSettingsElement::language() const { return (AnytoneDisplaySettingsExtension::Language)getUInt8(Offset::language()); } void D578UVCodeplug::GeneralSettingsElement::setLanguage(AnytoneDisplaySettingsExtension::Language lang) { setUInt8(Offset::language(), (unsigned)lang); } AnytoneSettingsExtension::VFOScanType D578UVCodeplug::GeneralSettingsElement::vfoScanType() const { return (AnytoneSettingsExtension::VFOScanType)getUInt8(Offset::vfoScanType()); } void D578UVCodeplug::GeneralSettingsElement::setVFOScanType(AnytoneSettingsExtension::VFOScanType type) { setUInt8(Offset::vfoScanType(), (unsigned int) type); } unsigned D578UVCodeplug::GeneralSettingsElement::dmrMicGain() const { return (((unsigned)getUInt8(Offset::dmrMicGain())+1)*10)/4; } void D578UVCodeplug::GeneralSettingsElement::setDMRMicGain(unsigned gain) { gain = std::min(1U, std::min(10U, gain)); setUInt8(Offset::dmrMicGain(), (gain*4)/10); } bool D578UVCodeplug::GeneralSettingsElement::vfoModeA() const { return getUInt8(Offset::vfoModeA()); } void D578UVCodeplug::GeneralSettingsElement::enableVFOModeA(bool enable) { setUInt8(Offset::vfoModeA(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::vfoModeB() const { return getUInt8(Offset::vfoModeB()); } void D578UVCodeplug::GeneralSettingsElement::enableVFOModeB(bool enable) { setUInt8(Offset::vfoModeB(), (enable ? 0x01 : 0x00)); } AnytoneSettingsExtension::STEType D578UVCodeplug::GeneralSettingsElement::steType() const { return (AnytoneSettingsExtension::STEType)getUInt8(Offset::steType()); } void D578UVCodeplug::GeneralSettingsElement::setSTEType(AnytoneSettingsExtension::STEType type) { setUInt8(Offset::steType(), (unsigned)type); } double D578UVCodeplug::GeneralSettingsElement::steFrequency() const { switch ((STEFrequency)getUInt8(Offset::steFrequency())) { case STEFrequency::Off: return 0; case STEFrequency::Hz55_2: return 55.2; case STEFrequency::Hz259_2: return 259.2; } return 0; } void D578UVCodeplug::GeneralSettingsElement::setSTEFrequency(double freq) { if (0 >= freq) { setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Off); } else if (100 > freq) { setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz55_2); } else { setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz259_2); } } Interval D578UVCodeplug::GeneralSettingsElement::groupCallHangTime() const { return Interval::fromSeconds(getUInt8(Offset::groupCallHangTime())); } void D578UVCodeplug::GeneralSettingsElement::setGroupCallHangTime(Interval intv) { setUInt8(Offset::groupCallHangTime(), intv.seconds()); } Interval D578UVCodeplug::GeneralSettingsElement::privateCallHangTime() const { return Interval::fromSeconds(getUInt8(Offset::privateCallHangTime())); } void D578UVCodeplug::GeneralSettingsElement::setPrivateCallHangTime(Interval intv) { setUInt8(Offset::privateCallHangTime(), intv.seconds()); } Interval D578UVCodeplug::GeneralSettingsElement::preWaveDelay() const { return Interval::fromMilliseconds((unsigned)getUInt8(Offset::preWaveDelay())*20); } void D578UVCodeplug::GeneralSettingsElement::setPreWaveDelay(Interval intv) { setUInt8(Offset::preWaveDelay(), intv.milliseconds()/20); } Interval D578UVCodeplug::GeneralSettingsElement::wakeHeadPeriod() const { return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::wakeHeadPeriod()))*20); } void D578UVCodeplug::GeneralSettingsElement::setWakeHeadPeriod(Interval intv) { setUInt8(Offset::wakeHeadPeriod(), intv.milliseconds()/20); } unsigned D578UVCodeplug::GeneralSettingsElement::wfmChannelIndex() const { return getUInt8(Offset::wfmChannelIndex()); } void D578UVCodeplug::GeneralSettingsElement::setWFMChannelIndex(unsigned idx) { setUInt8(Offset::wfmChannelIndex(), idx); } bool D578UVCodeplug::GeneralSettingsElement::wfmVFOEnabled() const { return getUInt8(Offset::wfmVFOEnabled()); } void D578UVCodeplug::GeneralSettingsElement::enableWFMVFO(bool enable) { setUInt8(Offset::wfmVFOEnabled(), (enable ? 0x01 : 0x00)); } unsigned D578UVCodeplug::GeneralSettingsElement::memoryZoneA() const { return getUInt8(Offset::memZoneA()); } void D578UVCodeplug::GeneralSettingsElement::setMemoryZoneA(unsigned zone) { setUInt8(Offset::memZoneA(), zone); } unsigned D578UVCodeplug::GeneralSettingsElement::memoryZoneB() const { return getUInt8(Offset::memZoneB()); } void D578UVCodeplug::GeneralSettingsElement::setMemoryZoneB(unsigned zone) { setUInt8(Offset::memZoneB(), zone); } bool D578UVCodeplug::GeneralSettingsElement::wfmEnabled() const { return 0x00 != getUInt8(Offset::wfmEnable()); } void D578UVCodeplug::GeneralSettingsElement::enableWFM(bool enable) { setUInt8(Offset::wfmEnable(), enable ? 0x01 : 0x00); } bool D578UVCodeplug::GeneralSettingsElement::recording() const { return getUInt8(Offset::enableRecoding()); } void D578UVCodeplug::GeneralSettingsElement::enableRecording(bool enable) { setUInt8(Offset::enableRecoding(), (enable ? 0x01 : 0x00)); } unsigned D578UVCodeplug::GeneralSettingsElement::brightness() const { return (getUInt8(Offset::displayBrightness())*10)/4; } void D578UVCodeplug::GeneralSettingsElement::setBrightness(unsigned level) { setUInt8(Offset::displayBrightness(), (level*4)/10); } bool D578UVCodeplug::GeneralSettingsElement::gps() const { return getUInt8(Offset::gpsEnable()); } void D578UVCodeplug::GeneralSettingsElement::enableGPS(bool enable) { setUInt8(Offset::gpsEnable(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::smsAlert() const { return getUInt8(Offset::smsAlert()); } void D578UVCodeplug::GeneralSettingsElement::enableSMSAlert(bool enable) { setUInt8(Offset::smsAlert(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::wfmMonitor() const { return getUInt8(Offset::wfmMonitor()); } void D578UVCodeplug::GeneralSettingsElement::enableWFMMonitor(bool enable) { setUInt8(Offset::wfmMonitor(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::activeChannelB() const { return getUInt8(Offset::activeChannelB()); } void D578UVCodeplug::GeneralSettingsElement::enableActiveChannelB(bool enable) { setUInt8(Offset::activeChannelB(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::subChannel() const { return getUInt8(Offset::subChannel()); } void D578UVCodeplug::GeneralSettingsElement::enableSubChannel(bool enable) { setUInt8(Offset::subChannel(), (enable ? 0x01 : 0x00)); } Frequency D578UVCodeplug::GeneralSettingsElement::tbstFrequency() const { switch ((TBSTFrequency)getUInt8(Offset::tbstFrequency())) { case TBSTFrequency::Hz1000: return Frequency::fromHz(1000); case TBSTFrequency::Hz1450: return Frequency::fromHz(1450); case TBSTFrequency::Hz1750: return Frequency::fromHz(1750); case TBSTFrequency::Hz2100: return Frequency::fromHz(2100); } return Frequency::fromHz(1750); } void D578UVCodeplug::GeneralSettingsElement::setTBSTFrequency(Frequency freq) { if (1000 == freq.inHz()) { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1000); } else if (1450 == freq.inHz()) { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1450); } else if (1750 == freq.inHz()) { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750); } else if (2100 == freq.inHz()) { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz2100); } else { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750); } } bool D578UVCodeplug::GeneralSettingsElement::callAlert() const { return getUInt8(Offset::callAlert()); } void D578UVCodeplug::GeneralSettingsElement::enableCallAlert(bool enable) { setUInt8(Offset::callAlert(), (enable ? 0x01 : 0x00)); } QTimeZone D578UVCodeplug::GeneralSettingsElement::gpsTimeZone() const { return TimeZone::decode(getUInt8(Offset::gpsTimeZone())); } void D578UVCodeplug::GeneralSettingsElement::setGPSTimeZone(const QTimeZone &zone) { setUInt8(Offset::gpsTimeZone(), TimeZone::encode(zone)); } bool D578UVCodeplug::GeneralSettingsElement::dmrTalkPermit() const { return getBit(Offset::talkPermit(), 0); } void D578UVCodeplug::GeneralSettingsElement::enableDMRTalkPermit(bool enable) { return setBit(Offset::talkPermit(), 0, enable); } bool D578UVCodeplug::GeneralSettingsElement::fmTalkPermit() const { return getBit(Offset::talkPermit(), 1); } void D578UVCodeplug::GeneralSettingsElement::enableFMTalkPermit(bool enable) { return setBit(Offset::talkPermit(), 1, enable); } bool D578UVCodeplug::GeneralSettingsElement::dmrResetTone() const { return getUInt8(Offset::dmrResetTone()); } void D578UVCodeplug::GeneralSettingsElement::enableDMRResetTone(bool enable) { return setUInt8(Offset::dmrResetTone(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::idleChannelTone() const { return getUInt8(Offset::idleChannelTone()); } void D578UVCodeplug::GeneralSettingsElement::enableIdleChannelTone(bool enable) { return setUInt8(Offset::idleChannelTone(), (enable ? 0x01 : 0x00)); } Interval D578UVCodeplug::GeneralSettingsElement::menuExitTime() const { return Interval::fromSeconds(5 + 5*((unsigned) getUInt8(Offset::menuExitTime()))); } void D578UVCodeplug::GeneralSettingsElement::setMenuExitTime(Interval intv) { setUInt8(Offset::menuExitTime(), (std::max(5ULL, intv.seconds())-5)/5); } bool D578UVCodeplug::GeneralSettingsElement::filterOwnID() const { return getUInt8(Offset::filterOwnID()); } void D578UVCodeplug::GeneralSettingsElement::enableFilterOwnID(bool enable) { setUInt8(Offset::filterOwnID(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::startupTone() const { return getUInt8(Offset::startupTone()); } void D578UVCodeplug::GeneralSettingsElement::enableStartupTone(bool enable) { return setUInt8(Offset::startupTone(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::callEndPrompt() const { return getUInt8(Offset::callEndPrompt()); } void D578UVCodeplug::GeneralSettingsElement::enableCallEndPrompt(bool enable) { return setUInt8(Offset::callEndPrompt(), (enable ? 0x01 : 0x00)); } unsigned D578UVCodeplug::GeneralSettingsElement::maxSpeakerVolume() const { return (((unsigned)getUInt8(Offset::maxSpeakerVolume()))*10)/8; } void D578UVCodeplug::GeneralSettingsElement::setMaxSpeakerVolume(unsigned level) { setUInt8(Offset::maxSpeakerVolume(), (level*8)/10); } bool D578UVCodeplug::GeneralSettingsElement::remoteStunKill() const { return getUInt8(Offset::remoteStunKill()); } void D578UVCodeplug::GeneralSettingsElement::enableRemoteStunKill(bool enable) { setUInt8(Offset::remoteStunKill(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::remoteMonitor() const { return getUInt8(Offset::remoteMonitor()); } void D578UVCodeplug::GeneralSettingsElement::enableRemoteMonitor(bool enable) { setUInt8(Offset::remoteMonitor(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::getGPSPosition() const { return getUInt8(Offset::getGPSPosition()); } void D578UVCodeplug::GeneralSettingsElement::enableGetGPSPosition(bool enable) { return setUInt8(Offset::getGPSPosition(), (enable ? 0x01 : 0x00)); } Interval D578UVCodeplug::GeneralSettingsElement::longPressDuration() const { return Interval::fromSeconds(((unsigned)getUInt8(Offset::longPressDuration()))+1); } void D578UVCodeplug::GeneralSettingsElement::setLongPressDuration(Interval ms) { setUInt8(Offset::longPressDuration(), std::max(1ULL,ms.seconds())-1); } bool D578UVCodeplug::GeneralSettingsElement::volumeChangePrompt() const { return getUInt8(Offset::volumeChangePrompt()); } void D578UVCodeplug::GeneralSettingsElement::enableVolumeChangePrompt(bool enable) { setUInt8(Offset::volumeChangePrompt(), (enable ? 0x01 : 0x01)); } AnytoneAutoRepeaterSettingsExtension::Direction D578UVCodeplug::GeneralSettingsElement::autoRepeaterDirectionA() const { return (AnytoneAutoRepeaterSettingsExtension::Direction) getUInt8(Offset::autoRepeaterDirA()); } void D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterDirectionA(AnytoneAutoRepeaterSettingsExtension::Direction dir) { setUInt8(Offset::autoRepeaterDirA(), (unsigned)dir); } AnytoneDMRSettingsExtension::SlotMatch D578UVCodeplug::GeneralSettingsElement::monitorSlotMatch() const { return (AnytoneDMRSettingsExtension::SlotMatch)getUInt8(Offset::monSlotMatch()); } void D578UVCodeplug::GeneralSettingsElement::setMonitorSlotMatch(AnytoneDMRSettingsExtension::SlotMatch match) { setUInt8(Offset::monSlotMatch(), (unsigned)match); } bool D578UVCodeplug::GeneralSettingsElement::monitorColorCodeMatch() const { return getUInt8(Offset::monColorCodeMatch()); } void D578UVCodeplug::GeneralSettingsElement::enableMonitorColorCodeMatch(bool enable) { setUInt8(Offset::monColorCodeMatch(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::monitorIDMatch() const { return getUInt8(Offset::monIDMatch()); } void D578UVCodeplug::GeneralSettingsElement::enableMonitorIDMatch(bool enable) { setUInt8(Offset::monIDMatch(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::monitorTimeSlotHold() const { return getUInt8(Offset::monTimeSlotHold()); } void D578UVCodeplug::GeneralSettingsElement::enableMonitorTimeSlotHold(bool enable) { setUInt8(Offset::monTimeSlotHold(), (enable ? 0x01 : 0x00)); } AnytoneDisplaySettingsExtension::LastCallerDisplayMode D578UVCodeplug::GeneralSettingsElement::lastCallerDisplayMode() const { return (AnytoneDisplaySettingsExtension::LastCallerDisplayMode)getUInt8(Offset::lastCallerDisplay()); } void D578UVCodeplug::GeneralSettingsElement::setLastCallerDisplayMode(AnytoneDisplaySettingsExtension::LastCallerDisplayMode mode) { setUInt8(Offset::lastCallerDisplay(), (unsigned)mode); } unsigned D578UVCodeplug::GeneralSettingsElement::fmCallHold() const { return getUInt8(Offset::fmCallHold()); } void D578UVCodeplug::GeneralSettingsElement::setFMCallHold(unsigned sec) { setUInt8(Offset::fmCallHold(), sec); } bool D578UVCodeplug::GeneralSettingsElement::displayClock() const { return getUInt8(Offset::showClock()); } void D578UVCodeplug::GeneralSettingsElement::enableDisplayClock(bool enable) { setUInt8(Offset::showClock(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::gpsMessageEnabled() const { return getUInt8(Offset::enableGPSMessage()); } void D578UVCodeplug::GeneralSettingsElement::enableGPSMessage(bool enable) { setUInt8(Offset::enableGPSMessage(), (enable ? 0x01 : 0x00)); } bool D578UVCodeplug::GeneralSettingsElement::enhanceAudio() const { return getUInt8(Offset::enhanceAudio()); } void D578UVCodeplug::GeneralSettingsElement::enableEnhancedAudio(bool enable) { setUInt8(Offset::enhanceAudio(), (enable ? 0x01 : 0x00)); } Frequency D578UVCodeplug::GeneralSettingsElement::minVFOScanFrequencyUHF() const { return Frequency::fromHz(((unsigned)getUInt32_le(Offset::minVFOScanUHF()))*10); } void D578UVCodeplug::GeneralSettingsElement::setMinVFOScanFrequencyUHF(Frequency freq) { setUInt32_le(Offset::minVFOScanUHF(), freq.inHz()/10); } Frequency D578UVCodeplug::GeneralSettingsElement::maxVFOScanFrequencyUHF() const { return Frequency::fromHz(((unsigned)getUInt32_le(Offset::maxVFOScanUHF()))*10); } void D578UVCodeplug::GeneralSettingsElement::setMaxVFOScanFrequencyUHF(Frequency freq) { setUInt32_le(Offset::maxVFOScanUHF(), freq.inHz()/10); } Frequency D578UVCodeplug::GeneralSettingsElement::minVFOScanFrequencyVHF() const { return Frequency::fromHz(((unsigned)getUInt32_le(Offset::minVFOScanVHF()))*10); } void D578UVCodeplug::GeneralSettingsElement::setMinVFOScanFrequencyVHF(Frequency freq) { setUInt32_le(Offset::minVFOScanVHF(), freq.inHz()/10); } Frequency D578UVCodeplug::GeneralSettingsElement::maxVFOScanFrequencyVHF() const { return Frequency::fromHz(((unsigned)getUInt32_le(Offset::maxVFOScanVHF()))*10); } void D578UVCodeplug::GeneralSettingsElement::setMaxVFOScanFrequencyVHF(Frequency freq) { setUInt32_le(Offset::maxVFOScanVHF(), freq.inHz()/10); } bool D578UVCodeplug::GeneralSettingsElement::hasAutoRepeaterOffsetFrequencyIndexUHF() const { return 0xff != autoRepeaterOffsetFrequencyIndexUHF(); } unsigned D578UVCodeplug::GeneralSettingsElement::autoRepeaterOffsetFrequencyIndexUHF() const { return getUInt8(Offset::autoRepOffsetUHF()); } void D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterOffsetFrequenyIndexUHF(unsigned idx) { setUInt8(Offset::autoRepOffsetUHF(), idx); } void D578UVCodeplug::GeneralSettingsElement::clearAutoRepeaterOffsetFrequencyIndexUHF() { setAutoRepeaterOffsetFrequenyIndexUHF(0xff); } bool D578UVCodeplug::GeneralSettingsElement::hasAutoRepeaterOffsetFrequencyIndexVHF() const { return 0xff != autoRepeaterOffsetFrequencyIndexVHF(); } unsigned D578UVCodeplug::GeneralSettingsElement::autoRepeaterOffsetFrequencyIndexVHF() const { return getUInt8(Offset::autoRepOffsetVHF()); } void D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterOffsetFrequenyIndexVHF(unsigned idx) { setUInt8(Offset::autoRepOffsetVHF(), idx); } void D578UVCodeplug::GeneralSettingsElement::clearAutoRepeaterOffsetFrequencyIndexVHF() { setAutoRepeaterOffsetFrequenyIndexVHF(0xff); } Frequency D578UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyVHF() const { return Frequency::fromHz(getUInt32_le(Offset::autoRepMinVHF())*10); } void D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyVHF(Frequency freq) { setUInt32_le(Offset::autoRepMinVHF(), freq.inHz()/10); } Frequency D578UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyVHF() const { return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxVHF())*10); } void D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyVHF(Frequency freq) { setUInt32_le(Offset::autoRepMaxVHF(), freq.inHz()/10); } Frequency D578UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyUHF() const { return Frequency::fromHz(getUInt32_le(Offset::autoRepMinUHF())*10); } void D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyUHF(Frequency freq) { setUInt32_le(Offset::autoRepMinUHF(), freq.inHz()/10); } Frequency D578UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyUHF() const { return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxUHF())*10); } void D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyUHF(Frequency freq) { setUInt32_le(Offset::autoRepMaxUHF(), freq.inHz()/10); } void D578UVCodeplug::GeneralSettingsElement::callToneMelody(Melody &melody) const { QVector> tones; tones.reserve(5); for (int i=0; i<5; i++) { double freq = getUInt16_le(Offset::callToneTones()+2*i); unsigned int duration = getUInt16_le(Offset::callToneDurations()+2*i); if (duration) tones.append({freq, duration}); } melody.infer(tones); } void D578UVCodeplug::GeneralSettingsElement::setCallToneMelody(const Melody &melody) { unsigned int n=std::min(5U, (unsigned int)melody.count()); QVector> tones = melody.toTones(); for (unsigned int i=0; i> tones; tones.reserve(5); for (int i=0; i<5; i++) { double frequency = getUInt16_le(Offset::idleToneTones()+2*i); unsigned int duration = getUInt16_le(Offset::idleToneDurations()+2*i); if (duration) tones.append({frequency, duration}); } melody.infer(tones); } void D578UVCodeplug::GeneralSettingsElement::setIdleToneMelody(const Melody &melody) { unsigned int n=std::min(5U, (unsigned int)melody.count()); QVector> tones = melody.toTones(); for (unsigned int i=0; i> tones; tones.reserve(5); for (int i=0; i<5; i++) { double frequency = getUInt16_le(Offset::resetToneTones()+2*i); unsigned int duration = getUInt16_le(Offset::resetToneDurations()+2*i); if (duration) tones.append({frequency, duration}); } melody.infer(tones); } void D578UVCodeplug::GeneralSettingsElement::setResetToneMelody(const Melody &melody) { unsigned int n=std::min(5U, (unsigned int)melody.count()); QVector> tones = melody.toTones(); for (unsigned int i=0; i= delay.milliseconds()) { setUInt8(Offset::btRXDelay(), 0); } else { unsigned int millis = std::min(5500ULL, std::max(500ULL, delay.milliseconds())); setUInt8(Offset::btRXDelay(), (millis-500)/500); } } bool D578UVCodeplug::GeneralSettingsElement::fromConfig(const Flags &flags, Context &ctx) { if (! AnytoneCodeplug::GeneralSettingsElement::fromConfig(flags, ctx)) return false; // Set measurement system based on system locale (0x00==Metric) enableGPSUnitsImperial(QLocale::ImperialSystem == QLocale::system().measurementSystem()); // Set transmit timeout setTransmitTimeout(ctx.config()->settings()->tot()); // Handle D578UV specific settings AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); if (nullptr == ext) { // <- if extension is not set setPriorityZoneAIndex(0xff); setPriorityZoneBIndex(0xff); return true; } // Encode boot settings if (ext->bootSettings()->priorityZoneA()->isNull()) setPriorityZoneAIndex(0xff); else setPriorityZoneAIndex(ctx.index(ext->bootSettings()->priorityZoneA()->as())); if (ext->bootSettings()->priorityZoneB()->isNull()) setPriorityZoneBIndex(0xff); else setPriorityZoneBIndex(ctx.index(ext->bootSettings()->priorityZoneB()->as())); if (! ext->roamingSettings()->defaultZone()->isNull()) setDefaultRoamingZoneIndex(ctx.index(ext->roamingSettings()->defaultZone()->as())); // Encode key settings enableKnobLock(ext->keySettings()->knobLockEnabled()); enableKeypadLock(ext->keySettings()->keypadLockEnabled()); enableSidekeysLock(ext->keySettings()->sideKeysLockEnabled()); enableKeyLockForced(ext->keySettings()->forcedKeyLockEnabled()); setFuncKey3Short(ext->keySettings()->funcKey3Short()); setFuncKey3Long(ext->keySettings()->funcKey3Long()); setFuncKey4Short(ext->keySettings()->funcKey4Short()); setFuncKey4Long(ext->keySettings()->funcKey4Long()); setFuncKey5Short(ext->keySettings()->funcKey5Short()); setFuncKey5Long(ext->keySettings()->funcKey5Long()); setFuncKey6Short(ext->keySettings()->funcKey6Short()); setFuncKey6Long(ext->keySettings()->funcKey6Long()); setFuncKeyDShort(ext->keySettings()->funcKeyDShort()); setFuncKeyDLong(ext->keySettings()->funcKeyDLong()); // Encode display settings setCallDisplayColor(ext->displaySettings()->callColor()); setLanguage(ext->displaySettings()->language()); enableDisplayChannelNumber(ext->displaySettings()->showChannelNumberEnabled()); enableShowCurrentContact(ext->displaySettings()->showContact()); setStandbyTextColor(ext->displaySettings()->standbyTextColor()); enableShowLastHeard(ext->displaySettings()->showLastHeardEnabled()); setChannelNameColor(ext->displaySettings()->callColor()); enableShowCurrentContact(ext->displaySettings()->showContact()); // Encode menu settings enableSeparateDisplay(ext->menuSettings()->separatorEnabled()); // Encode auto-repeater settings setAutoRepeaterDirectionB(ext->autoRepeaterSettings()->directionB()); setAutoRepeaterMinFrequencyVHF(ext->autoRepeaterSettings()->vhfMin()); setAutoRepeaterMaxFrequencyVHF(ext->autoRepeaterSettings()->vhfMax()); setAutoRepeaterMinFrequencyUHF(ext->autoRepeaterSettings()->uhfMin()); setAutoRepeaterMaxFrequencyUHF(ext->autoRepeaterSettings()->uhfMax()); // Encode DMR settings setGroupCallHangTime(ext->dmrSettings()->groupCallHangTime()); setPrivateCallHangTime(ext->dmrSettings()->privateCallHangTime()); setPreWaveDelay(ext->dmrSettings()->preWaveDelay()); setWakeHeadPeriod(ext->dmrSettings()->wakeHeadPeriod()); enableFilterOwnID(ext->dmrSettings()->filterOwnIDEnabled()); setMonitorSlotMatch(ext->dmrSettings()->monitorSlotMatch()); enableMonitorColorCodeMatch(ext->dmrSettings()->monitorColorCodeMatchEnabled()); enableMonitorIDMatch(ext->dmrSettings()->monitorIDMatchEnabled()); enableMonitorTimeSlotHold(ext->dmrSettings()->monitorTimeSlotHoldEnabled()); setSMSFormat(ext->dmrSettings()->smsFormat()); // Encode GPS settings enableGPSUnitsImperial(AnytoneGPSSettingsExtension::Units::Archaic == ext->gpsSettings()->units()); setGPSTimeZone(ext->gpsSettings()->timeZone()); enableGPSMessage(ext->gpsSettings()->positionReportingEnabled()); setGPSUpdatePeriod(ext->gpsSettings()->updatePeriod()); // Encode ranging/roaming settings. setAutoRoamPeriod(ext->roamingSettings()->autoRoamPeriod()); setAutoRoamDelay(ext->roamingSettings()->autoRoamDelay()); enableRepeaterRangeCheck(ext->roamingSettings()->repeaterRangeCheckEnabled()); setRepeaterRangeCheckInterval(ext->roamingSettings()->repeaterCheckInterval()); setRepeaterRangeCheckCount(ext->roamingSettings()->repeaterRangeCheckCount()); setRoamingStartCondition(ext->roamingSettings()->roamingStartCondition()); enableRepeaterCheckNotification(ext->roamingSettings()->notificationEnabled()); setRepeaterCheckNumNotifications(ext->roamingSettings()->notificationCount()); // Encode other settings enableKeepLastCaller(ext->keepLastCallerEnabled()); setSTEType(ext->steType()); setSTEFrequency(ext->steFrequency()); setTBSTFrequency(ext->tbstFrequency()); return true; } bool D578UVCodeplug::GeneralSettingsElement::updateConfig(Context &ctx) { if (! AnytoneCodeplug::GeneralSettingsElement::updateConfig(ctx)) return false; ctx.config()->settings()->setTOT(transmitTimeout()); // Handle D578UV specific extension AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); if (nullptr == ext) { ext = new AnytoneSettingsExtension(); ctx.config()->settings()->setAnytoneExtension(ext); } // Decode key settings ext->keySettings()->enableKnobLock(this->knobLock()); ext->keySettings()->enableKeypadLock(this->keypadLock()); ext->keySettings()->enableSideKeysLock(this->sidekeysLock()); ext->keySettings()->enableForcedKeyLock(this->keyLockForced()); ext->keySettings()->setFuncKey3Short(funcKey3Short()); ext->keySettings()->setFuncKey3Long(funcKey3Long()); ext->keySettings()->setFuncKey4Short(funcKey4Short()); ext->keySettings()->setFuncKey4Long(funcKey4Long()); ext->keySettings()->setFuncKey5Short(funcKey5Short()); ext->keySettings()->setFuncKey5Long(funcKey5Long()); ext->keySettings()->setFuncKey6Short(funcKey6Short()); ext->keySettings()->setFuncKey6Long(funcKey6Long()); ext->keySettings()->setFuncKeyDShort(funcKeyDShort()); ext->keySettings()->setFuncKeyDLong(funcKeyDLong()); // Decode display settings ext->displaySettings()->setCallColor(this->callDisplayColor()); ext->displaySettings()->setLanguage(this->language()); ext->displaySettings()->enableShowChannelNumber(this->displayChannelNumber()); ext->displaySettings()->enableShowContact(this->showCurrentContact()); ext->displaySettings()->setStandbyTextColor(this->standbyTextColor()); ext->displaySettings()->enableShowLastHeard(this->showLastHeard()); ext->displaySettings()->setChannelNameColor(this->channelNameColor()); ext->displaySettings()->enableShowContact(this->showCurrentContact()); // Decode menu settings ext->menuSettings()->enableSeparator(this->separateDisplay()); // Decode auto-repeater settings ext->autoRepeaterSettings()->setDirectionB(autoRepeaterDirectionB()); ext->autoRepeaterSettings()->setVHFMin(this->autoRepeaterMinFrequencyVHF()); ext->autoRepeaterSettings()->setVHFMax(this->autoRepeaterMaxFrequencyVHF()); ext->autoRepeaterSettings()->setUHFMin(this->autoRepeaterMinFrequencyUHF()); ext->autoRepeaterSettings()->setUHFMax(this->autoRepeaterMaxFrequencyUHF()); // Encode dmr settings ext->dmrSettings()->setGroupCallHangTime(this->groupCallHangTime()); ext->dmrSettings()->setPrivateCallHangTime(this->privateCallHangTime()); ext->dmrSettings()->setPreWaveDelay(this->preWaveDelay()); ext->dmrSettings()->setWakeHeadPeriod(this->wakeHeadPeriod()); ext->dmrSettings()->enableFilterOwnID(this->filterOwnID()); ext->dmrSettings()->setMonitorSlotMatch(this->monitorSlotMatch()); ext->dmrSettings()->enableMonitorColorCodeMatch(this->monitorColorCodeMatch()); ext->dmrSettings()->enableMonitorIDMatch(this->monitorIDMatch()); ext->dmrSettings()->enableMonitorTimeSlotHold(this->monitorTimeSlotHold()); ext->dmrSettings()->setSMSFormat(this->smsFormat()); // Encode GPS settings ext->gpsSettings()->setUnits(this->gpsUnitsImperial() ? AnytoneGPSSettingsExtension::Units::Archaic : AnytoneGPSSettingsExtension::Units::Metric); ext->gpsSettings()->setTimeZone(this->gpsTimeZone()); ext->gpsSettings()->enablePositionReporting(this->gpsMessageEnabled()); ext->gpsSettings()->setUpdatePeriod(this->gpsUpdatePeriod()); // Encode ranging/roaming settings ext->roamingSettings()->setAutoRoamPeriod(this->autoRoamPeriod()); ext->roamingSettings()->setAutoRoamDelay(this->autoRoamDelay()); ext->roamingSettings()->enableRepeaterRangeCheck(this->repeaterRangeCheck()); ext->roamingSettings()->setRepeaterCheckInterval(this->repeaterRangeCheckInterval()); ext->roamingSettings()->setRepeaterRangeCheckCount(this->repeaterRangeCheckCount()); ext->roamingSettings()->setRoamingStartCondition(this->roamingStartCondition()); ext->roamingSettings()->enableNotification(this->repeaterCheckNotification()); ext->roamingSettings()->setNotificationCount(this->repeaterCheckNumNotifications()); // Decode other settings ext->enableKeepLastCaller(this->keepLastCaller()); ext->setSTEType(this->steType()); ext->setSTEFrequency(this->steFrequency()); ext->setTBSTFrequency(this->tbstFrequency()); return true; } bool D578UVCodeplug::GeneralSettingsElement::linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err) { if (! AnytoneCodeplug::GeneralSettingsElement::linkSettings(settings, ctx, err)) return false; AnytoneSettingsExtension *ext = settings->anytoneExtension(); if (0xff != priorityZoneAIndex()) { if (! ctx.has(priorityZoneAIndex())) { errMsg(err) << "Cannot link priority zone A index " << priorityZoneAIndex() << ": Zone with that index not defined."; return false; } ext->bootSettings()->priorityZoneA()->set(ctx.get(priorityZoneAIndex())); } if (0xff != priorityZoneBIndex()) { if (! ctx.has(priorityZoneBIndex())) { errMsg(err) << "Cannot link priority zone B index " << priorityZoneBIndex() << ": Zone with that index not defined."; return false; } ext->bootSettings()->priorityZoneB()->set(ctx.get(priorityZoneBIndex())); } if (ctx.has(defaultRoamingZoneIndex())) { ext->roamingSettings()->defaultZone()->set(ctx.get(this->defaultRoamingZoneIndex())); } return true; } /* ******************************************************************************************** * * Implementation of D578UVCodeplug::HotKeySettingsElement * ******************************************************************************************** */ D578UVCodeplug::HotKeySettingsElement::HotKeySettingsElement(uint8_t *ptr, size_t size) : AnytoneCodeplug::HotKeySettingsElement(ptr, size) { // pass... } D578UVCodeplug::HotKeySettingsElement::HotKeySettingsElement(uint8_t *ptr) : AnytoneCodeplug::HotKeySettingsElement(ptr, HotKeySettingsElement::size()) { // pass... } uint8_t * D578UVCodeplug::HotKeySettingsElement::hotKeySetting(unsigned int n) const { if (n >= Limit::numEntries()) return nullptr; return _data + Offset::hotKeySettings() + n*Offset::betweenHotKeySettings(); } /* ******************************************************************************************** * * Implementation of D578UVCodeplug::AirBandChannelElement * ******************************************************************************************** */ D578UVCodeplug::AirBandChannelElement::AirBandChannelElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } D578UVCodeplug::AirBandChannelElement::AirBandChannelElement(uint8_t *ptr) : Element(ptr, AirBandChannelElement::size()) { // pass... } void D578UVCodeplug::AirBandChannelElement::clear() { memset(_data, 0, _size); } Frequency D578UVCodeplug::AirBandChannelElement::frequency() const { return Frequency::fromHz(((unsigned long long)getBCD8_be(Offset::frequency()))*10); } void D578UVCodeplug::AirBandChannelElement::setFrequency(Frequency freq) { setBCD8_be(Offset::frequency(), freq.inHz()/10); } QString D578UVCodeplug::AirBandChannelElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0x00); } void D578UVCodeplug::AirBandChannelElement::setName(const QString &name) { writeASCII(Offset::name(), name, Limit::nameLength(), 0x00); } /* ******************************************************************************************** * * Implementation of D578UVCodeplug::AirBandBitmapElement * ******************************************************************************************** */ D578UVCodeplug::AirBandBitmapElement::AirBandBitmapElement(uint8_t *ptr, size_t size) : BitmapElement(ptr, size) { // pass... } D578UVCodeplug::AirBandBitmapElement::AirBandBitmapElement(uint8_t *ptr) : BitmapElement(ptr, AirBandBitmapElement::size()) { // pass... } /* ******************************************************************************************** * * Implementation of D578UVCodeplug * ******************************************************************************************** */ D578UVCodeplug::D578UVCodeplug(const QString &label, QObject *parent) : D878UVCodeplug(label, parent) { // pass... } D578UVCodeplug::D578UVCodeplug(QObject *parent) : D878UVCodeplug("AnyTone AT-D578UV Codeplug", parent) { // pass... } bool D578UVCodeplug::allocateBitmaps() { if (! D878UVCodeplug::allocateBitmaps()) return false; image(0).addElement(Offset::airBandChannelBitmap(), AirBandBitmapElement::size()); image(0).addElement(Offset::airBandScanBitmap(), AirBandBitmapElement::size()); return true; } void D578UVCodeplug::allocateUpdated() { D878UVCodeplug::allocateUpdated(); this->allocateAirBand(); } void D578UVCodeplug::allocateHotKeySettings() { image(0).addElement(Offset::hotKeySettings(), HotKeySettingsElement::size()); } bool D578UVCodeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err) // Encode channels for (int i=0; ichannelList()->count(); i++) { // enable channel uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank(); ChannelElement ch(data(Offset::channelBanks() + bank*Offset::betweenChannelBanks() + idx*ChannelElement::size())); ch.fromChannelObj(ctx.config()->channelList()->channel(i), ctx); } return true; } bool D578UVCodeplug::createChannels(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap())); // Create channels for (uint16_t i=0; ichannelList()->add(obj); ctx.add(obj, i); } } return true; } bool D578UVCodeplug::linkChannels(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap())); // Link channel objects for (uint16_t i=0; i(i)) ch.linkChannelObj(ctx.get(i), ctx); } return true; } void D578UVCodeplug::allocateContacts() { /* Allocate contacts */ ContactBitmapElement contact_bitmap(data(Offset::contactBitmap())); unsigned contactCount=0; for (uint16_t i=0; i contacts; // Encode contacts and also collect id<->index map for (int i=0; icontacts()->digitalCount(); i++) { uint32_t bank_addr = Offset::contactBanks() + (i/Limit::contactsPerBank())*Offset::betweenContactBanks(); uint32_t addr = bank_addr + (i%Limit::contactsPerBank())*ContactElement::size(); ContactElement con(data(addr)); DMRContact *contact = ctx.config()->contacts()->digitalContact(i); if(! con.fromContactObj(contact, ctx)) return false; ((uint32_t *)data(Offset::contactIndex()))[i] = qToLittleEndian(i); contacts.append(contact); } // encode index map for contacts std::sort(contacts.begin(), contacts.end(), [](DMRContact *a, DMRContact *b) { return a->number() < b->number(); }); for (int i=0; inumber(), (DMRContact::GroupCall==contacts[i]->type())); el.setIndex(ctx.index(contacts[i])); } return true; } void D578UVCodeplug::allocateGeneralSettings() { // override allocation of general settings for D878UV code-plug. General settings are larger! image(0).addElement(Offset::settings(), GeneralSettingsElement::size()); image(0).addElement(Offset::gpsMessages(), DMRAPRSMessageElement::size()); image(0).addElement(Offset::settingsExtension(), ExtendedSettingsElement::size()); } bool D578UVCodeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) GeneralSettingsElement(data(Offset::settings())).fromConfig(flags, ctx); DMRAPRSMessageElement(data(Offset::gpsMessages())).fromConfig(flags, ctx); ExtendedSettingsElement(data(Offset::settingsExtension())).fromConfig(flags, ctx); return true; } bool D578UVCodeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx); DMRAPRSMessageElement(data(Offset::gpsMessages())).updateConfig(ctx); ExtendedSettingsElement(data(Offset::settingsExtension())).updateConfig(ctx); return true; } bool D578UVCodeplug::linkGeneralSettings(Context &ctx, const ErrorStack &err) { if (! GeneralSettingsElement(data(Offset::settings())).linkSettings(ctx.config()->settings(), ctx, err)) { errMsg(err) << "Cannot link general settings extension."; return false; } if (! ExtendedSettingsElement(data(Offset::settingsExtension())).linkConfig(ctx, err)) { errMsg(err) << "Cannot link general settings extension."; return false; } return true; } void D578UVCodeplug::allocateAirBand() { // Allocate valid air-band channels AirBandBitmapElement bitmap(data(Offset::airBandChannelBitmap())); for (unsigned int i=0; i #include "d878uv_codeplug.hh" #include "signaling.hh" class Channel; class DMRContact; class Zone; class RXGroupList; class ScanList; class GPSSystem; /** Represents the device specific binary codeplug for Anytone AT-D578UV radios. * * Fits firmware/CPS version 1.17. * * @section d578uvcpl Codeplug structure within radio * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Channels
      Start Size Content
      024C1500 000200 Bitmap of 4000 channels, default 0x00, 0x00 padded.
      00800000 max. 002000 Channel bank 0 of up to 128 channels, see @c D578UVCodeplug::ChannelElement 64 b each.
      00802000 max, 002000 Unknown data, Maybe extended channel information for channel bank 0? * It is of exactly the same size as the channel bank 0. Mostly 0x00, a few 0xff.
      00840000 max. 002000 Channel bank 1 of up to 128 channels.
      00842000 max. 002000 Unknown data, related to CH bank 1?
      ... ... ...
      00FC0000 max. 000800 Channel bank 32, up to 32 channels.
      00FC2000 max. 000800 Unknown data, related to CH bank 32.
      00FC0800 000040 VFO A settings, see @c D578UVCodeplug::ChannelElement.
      00FC0840 000040 VFO B settings, see @c D578UVCodeplug::ChannelElement.
      00FC2800 000080 Unknown data, related to VFO A+B? * It is of exactly the same size as the two VFO channels. Mostly 0x00, a few 0xff. Same pattern as * the unknown data associated with channel banks.
      Zones
      Start Size Content
      024C1300 000020 Bitmap of 250 zones.
      024C1360 000020 Hidden zone bitmap of 250 zones.
      01000000 max. 01f400 250 zones channel lists of 250 16bit indices each. * 0-based, little endian, default/padded=0xffff. Offset between channel lists 0x200, size of each list 0x1f4.
      02540000 max. 001f40 250 Zone names. * Each zone name is up to 16 ASCII chars long and gets 0-padded to 32b.
      Roaming
      Start Size Content
      01042000 000020 Roaming channel bitmask, up to 250 bits, 0-padded, default 0.
      01040000 max. 0x1f40 Optional up to 250 roaming channels, of 32b each. * See @c D878UVCodeplug::RoamingChannelElement for details.
      01042080 000010 Roaming zone bitmask, up to 64 bits, 0-padded, default 0.
      01043000 max. 0x2000 Optional up to 64 roaming zones, of 128b each. * See @c D878UVCodeplug::RoamingZoneElement for details.
      Contacts
      Start Size Content
      02600000 max. 009C40 Index list of valid contacts. * 10000 32bit indices, little endian, default 0xffffffff
      02640000 000500 Contact bitmap, 10000 bit, inverted, default 0xff, 0x00 padded.
      02680000 max. 0f4240 10000 contacts, see @c D868UVCodeplug::ContactElement. * As each contact is 100b, they do not align with the 16b blocks being transferred to the device. * Hence contacts are organized internally in groups of 4 contacts forming a "bank".
      04800000 max. 013880 DMR ID to contact index map, see @c AnytoneCodeplug::ContactMapElement. * Sorted by ID, empty entries set to 0xffffffffffffffff.
      Analog Contacts
      Start Size Content
      02900000 000080 Index list of valid analog contacts.
      02900100 000080 Bytemap for 128 analog contacts.
      02940000 max. 000180 128 analog contacts. See @c AnytoneCodeplug::DTMFContactElement. * As each analog contact is 24b, they do not align with the 16b transfer block-size. Hence * analog contacts are internally organized in groups of 2.
      RX Group Lists
      Start Size Content
      025C0B10 000020 Bitmap of 250 RX group lists, default/padding 0x00.
      02980000 max. 000120 Grouplist 0, see @c AnytoneCodeplug::GroupListElement.
      02980200 max. 000120 Grouplist 1
      ... ... ...
      0299f200 max. 000120 Grouplist 250
      Scan lists
      Start Size Content
      024C1340 000020 Bitmap of 250 scan lists.
      01080000 000090 Bank 0, Scanlist 1, see @c AnytoneCodeplug::ScanListElement.
      01080200 000090 Bank 0, Scanlist 2
      ... ... ...
      01081E00 000090 Bank 0, Scanlist 16
      010C0000 000090 Bank 1, Scanlist 17
      ... ... ...
      01440000 000090 Bank 15, Scanlist 241
      ... ... ...
      01441400 000090 Bank 15, Scanlist 250
      Radio IDs
      Start Size Content
      024C1320 000020 Bitmap of 250 radio IDs.
      02580000 max. 001f40 250 Radio IDs. See @c AnytoneCodeplug::RadioIDElement.
      GPS/APRS
      Start Size Content
      02501000 000100 APRS settings, see @c D878UVCodeplug::APRSSettingsElement.
      02501200 000040 APRS Text, up to 60 chars ASCII, 0-padded.
      02501800 000100 APRS-RX settings list up to 32 entries, 8b each. * See @c D878UVCodeplug::AnalogAPRSRXEntryElement.
      02502000 000080 FM APRS frequency names, * see @c FMAPRSFrequencyNamesElement. This element is not part of the manufacturer codeplug. * QDMR uses this memory section to store additional information.
      General Settings
      Start Size Content
      02500000 0000f0 General settings, see @c D578UVCodeplug::GeneralSettingsElement.
      02500100 000400 Zone A & B channel list.
      02500500 000100 DTMF list
      02500600 000030 Power on settings, see @c AnytoneCodeplug::BootSettingsElement.
      02501280 000030 DMR ARPS (GPS) messages, see @c D878UVCodeplug::GPSMessageElement.
      02501400 000200 General settings extension 2, see @c D578UVCodeplug::GeneralSettingsExtensionElement.
      024C2000 0003F0 List of 250 auto-repeater offset frequencies. * 32bit little endian frequency in 10Hz. I.e., 600kHz = 60000. Default 0x00000000, 0x00 padded.
      Messages
      Start Size Content
      01640000 max. 000100 Some kind of linked list of messages. * See @c AnytoneCodeplug::MessageListElement. Each entry has a size of 0x10.
      01640800 000090 Bytemap of up to 100 valid messages. * 0x00=valid, 0xff=invalid, remaining 46b set to 0x00.
      02140000 max. 000800 Bank 0, Messages 1-8. * Each message consumes 0x100b. See @c AnytoneCodeplug::MessageElement.
      02180000 max. 000800 Bank 1, Messages 9-16
      ... ... ...
      02440000 max. 000800 Bank 12, Messages 97-100
      Hot Keys
      Start Size Content
      025C0000 000100 4 analog quick-call settings. * See @c AnytoneCodeplug::AnalogQuickCallElement.
      025C0B00 000010 Status message bitmap.
      025C0100 000400 Up to 32 status messages. * Length unknown, offset 0x20. ASCII 0x00 terminated and padded.
      025C0500 000470 24 hot-key settings, * see @c AnytoneCodeplug::HotKeyElement
      Encryption keys
      Start Size Content
      024C1700 000040 32 Encryption IDs, 0-based, 16bit big-endian.
      024C1800 000500 32 DMR-Encryption keys, * see @c D868UVCodeplug::dmr_encryption_key_t, * 40b each.
      024C4000 004000 Up to 256 AES encryption keys. * See @c D878UVCodeplug::AESEncryptionKeyElement.
      Misc
      Start Size Content
      024C1400 000020 Alarm setting, * see @c AnytoneCodeplug::AlarmSettingElement.
      024C1440 000030 Digital alarm settings extension, * see @c AnytoneCodeplug::DigitalAlarmExtensionElement.
      FM Broadcast
      Start Size Content
      02480210 000020 Bitmap of 100 FM broadcast channels.
      02480000 max. 000200 100 FM broadcast channels. Encoded * as 8-digit BCD little-endian in 100Hz. Filled with 0x00.
      02480200 000010 FM broadcast VFO frequency. Encoded * as 8-digit BCD little-endian in 100Hz. Filled with 0x00.
      DTMF, 2-tone & 5-tone signaling.
      Start Size Content
      024C0C80 000010 5-tone encoding bitmap.
      024C0000 000020 5-tone encoding.
      024C0D00 000200 5-tone ID list.
      024C1000 000080 5-tone settings.
      024C1080 000050 DTMF settings.
      024C1280 000010 2-tone encoding bitmap.
      024C1100 000010 2-tone encoding.
      024C1290 000010 2-tone settings.
      024C2600 000010 2-tone decoding bitmap.
      024C2400 000030 2-tone decoding.
      Air-band settings
      Start Size Content
      02BC0000 000c80 Up to 100 air band channels, 0x20 bytes each. See * @c AirBandChannelElement.
      02BC1000 000020 Air band VFO channel, see * @c AirBandChannelElement.
      02BC1020 000020 Air band channel bitmap
      02BC1040 000020 Air band scan enable bitmap
      Still unknown
      Start Size Content
      024C1090 000040 Unknown, set to 0xff
      024C1440 000030 Unknown data.
      02504000 000400 Unknown data.
      * * @ingroup d578uv */ class D578UVCodeplug : public D878UVCodeplug { Q_OBJECT public: /** Represents the actual channel encoded within the binary code-plug. * * Memory layout of encoded channel (size 0x40 bytes): * @verbinclude d578uv_channel.txt */ class ChannelElement: public D878UVCodeplug::ChannelElement { protected: /** Hidden constructor. */ ChannelElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ ChannelElement(uint8_t *ptr); /** Returns @c true if bluetooth hands-free is enabled. */ virtual bool handsFree() const; /** Enables/disables hands-free. */ virtual void enableHandsFree(bool enable); // moved to a different bit bool roamingEnabled() const; void enableRoaming(bool enable); // moved to a different bit /** Returns @c true if the data ACK is enabled. */ bool dataACK() const; /** Enables data ACK. */ void enableDataACK(bool enable); // Replaced by analog scrambler settings /** The D578UV does not support the weak DMR encryption, hence this function returns always 0. */ unsigned dmrEncryptionKeyIndex() const; /** The D578UV does not support the weak DMR encryption, hence this function has not effect. */ void setDMREncryptionKeyIndex(unsigned idx); /** Returns @c true if the analog scambler is enabled. */ virtual bool analogScambler() const; /** Enables/disables the analog scambler. */ virtual void enableAnalogScamber(bool enable); Channel *toChannelObj(Context &ctx) const; }; /** Represents the general config of the radio within the D578UV binary codeplug. * * This class implements only the differences to the D878UV general settings * @c D878UVCodeplug::GeneralSettingsElement * * Binary encoding of the general settings (size 0x00f0 bytes): * @verbinclude d578uv_generalsettings.txt */ class GeneralSettingsElement: public AnytoneCodeplug::GeneralSettingsElement { protected: /** Device specific key functions. */ struct KeyFunction { public: /** Encodes key function. */ static uint8_t encode(AnytoneKeySettingsExtension::KeyFunction tone); /** Decodes key function. */ static AnytoneKeySettingsExtension::KeyFunction decode(uint8_t code); protected: /** Encoded key functions. */ typedef enum { Off = 0x00, Voltage = 0x01, Power = 0x02, Repeater = 0x03, Reverse = 0x04, Encryption = 0x05, Call = 0x06, ToggleVFO = 0x07, Scan = 0x08, WFM = 0x09, Alarm = 0x0a, RecordSwitch = 0x0b, Record = 0x0c, SMS = 0x0d, Dial = 0x0e, GPSInformation=0x0f, Monitor = 0x10, ToggleMainChannel = 0x11, HotKey1 = 0x12, HotKey2 = 0x13, HotKey3 = 0x14, HotKey4 = 0x15, HotKey5 = 0x16, HotKey6 = 0x17, WorkAlone = 0x18, SkipChannel = 0x19, DMRMonitor = 0x1a, SubChannel = 0x1b, PriorityZone = 0x1c, VFOScan = 0x1d, MICSoundQuality = 0x1e, LastCallReply = 0x1f, ChannelType = 0x20, Ranging=0x21, Roaming = 0x22, ChannelRanging = 0x23, MaxVolume = 0x24, Slot = 0x25, APRSTypeSwitch=0x26, Zone = 0x27, MuteA = 0x28, MuteB = 0x29, RoamingSet = 0x2a, APRSSet = 0x2b, ZoneUp = 0x2c, ZoneDown = 0x2d, XBandRepeater = 0x30, Speaker = 0x31, ChannelName = 0x32, Bluetooth = 0x33, GPS = 0x34, CDTScan = 0x35, TBSTSend = 0x36, APRSSend = 0x37, APRSInfo = 0x38, GPSRoaming = 0x39, Squelch=0x3a, NoiseReductionTX=0x3b } KeyFunctionCode; }; /** Device specific time zones. */ struct TimeZone { public: /** Encodes time zone. */ static uint8_t encode(const QTimeZone& zone); /** Decodes time zone. */ static QTimeZone decode(uint8_t code); protected: /** Vector of possible time-zones. */ static QVector _timeZones; }; /** TBST (open repeater) frequencies. */ enum class TBSTFrequency { Hz1000 = 0, Hz1450 = 1, Hz1750 = 2, Hz2100 = 3 }; /** All possible STE (squelch tail eliminate) frequencies. */ enum class STEFrequency { Off = 0, Hz55_2 = 1, Hz259_2 = 2 }; protected: /** Hidden constructor. */ GeneralSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ GeneralSettingsElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x00f0; } bool keyToneEnabled() const; void enableKeyTone(bool enable); /** Returns the transmit timeout in seconds. */ virtual unsigned transmitTimeout() const; /** Sets the transmit timeout in seconds. */ virtual void setTransmitTimeout(unsigned tot); /** Returns the UI language. */ virtual AnytoneDisplaySettingsExtension::Language language() const; /** Sets the UI language. */ virtual void setLanguage(AnytoneDisplaySettingsExtension::Language lang); AnytoneSettingsExtension::VFOScanType vfoScanType() const; void setVFOScanType(AnytoneSettingsExtension::VFOScanType type); unsigned int dmrMicGain() const; void setDMRMicGain(unsigned int gain); bool vfoModeA() const; void enableVFOModeA(bool enable); bool vfoModeB() const; void enableVFOModeB(bool enable); /** Returns the STE (squelch tail eliminate) type. */ virtual AnytoneSettingsExtension::STEType steType() const; /** Sets the STE (squelch tail eliminate) type. */ virtual void setSTEType(AnytoneSettingsExtension::STEType type); /** Returns the STE (squelch tail eliminate) frequency setting in Hz. * A value of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */ virtual double steFrequency() const; /** Sets the STE (squelch tail eliminate) frequency setting. * A value of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */ virtual void setSTEFrequency(double freq); /** Returns the group call hang time in seconds. */ virtual Interval groupCallHangTime() const; /** Sets the group call hang time in seconds. */ virtual void setGroupCallHangTime(Interval sec); /** Returns the private call hang time in seconds. */ virtual Interval privateCallHangTime() const; /** Sets the private call hang time in seconds. */ virtual void setPrivateCallHangTime(Interval sec); /** Returns the pre-wave time in ms. */ virtual Interval preWaveDelay() const; /** Sets the pre-wave time in ms. */ virtual void setPreWaveDelay(Interval ms); /** Returns the wake head-period in ms. */ virtual Interval wakeHeadPeriod() const; /** Sets the wake head-period in ms. */ virtual void setWakeHeadPeriod(Interval ms); /** Returns the wide-FM (broadcast) channel index. */ virtual unsigned wfmChannelIndex() const; /** Sets the wide-FM (broadcast) channel index. */ virtual void setWFMChannelIndex(unsigned idx); /** Returns @c true if the WFM RX is in VFO mode. */ virtual bool wfmVFOEnabled() const; /** Enables/disables VFO mode for WFM RX. */ virtual void enableWFMVFO(bool enable); unsigned memoryZoneA() const; void setMemoryZoneA(unsigned zone); unsigned memoryZoneB() const; void setMemoryZoneB(unsigned zone); /** Returns @c true, if the WFM/Airband receiver is enabled. */ virtual bool wfmEnabled() const; /** Enables/disables WFM/Airband receiver. */ virtual void enableWFM(bool enable); bool recording() const; void enableRecording(bool enable); unsigned brightness() const; void setBrightness(unsigned level); bool gps() const; void enableGPS(bool enable); bool smsAlert() const; void enableSMSAlert(bool enable); /** Returns @c true if WFM monitor is enabled. */ virtual bool wfmMonitor() const; /** Enables/disables WFM monitor. */ virtual void enableWFMMonitor(bool enable); bool activeChannelB() const; void enableActiveChannelB(bool enable); bool subChannel() const; void enableSubChannel(bool enable); /** Returns the TBST frequency. */ virtual Frequency tbstFrequency() const; /** Sets the TBST frequency. */ virtual void setTBSTFrequency(Frequency freq); bool callAlert() const; void enableCallAlert(bool enable); QTimeZone gpsTimeZone() const; void setGPSTimeZone(const QTimeZone &zone); bool dmrTalkPermit() const; void enableDMRTalkPermit(bool enable); bool fmTalkPermit() const; void enableFMTalkPermit(bool enable); bool dmrResetTone() const; void enableDMRResetTone(bool enable); bool idleChannelTone() const; void enableIdleChannelTone(bool enable); Interval menuExitTime() const; void setMenuExitTime(Interval intv); /** Returns @c true if the own ID is filtered in call lists. */ virtual bool filterOwnID() const; /** Enables/disables filter of own ID in call lists. */ virtual void enableFilterOwnID(bool enable); bool startupTone() const; void enableStartupTone(bool enable); bool callEndPrompt() const; void enableCallEndPrompt(bool enable); unsigned maxSpeakerVolume() const; void setMaxSpeakerVolume(unsigned level); /** Returns @c true remote stun/kill is enabled. */ virtual bool remoteStunKill() const; /** Enables/disables remote stun/kill. */ virtual void enableRemoteStunKill(bool enable); /** Returns @c true remote monitor is enabled. */ virtual bool remoteMonitor() const; /** Enables/disables remote monitor. */ virtual void enableRemoteMonitor(bool enable); bool getGPSPosition() const; void enableGetGPSPosition(bool enable); Interval longPressDuration() const; void setLongPressDuration(Interval ms); bool volumeChangePrompt() const; void enableVolumeChangePrompt(bool enable); AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionA() const; void setAutoRepeaterDirectionA(AnytoneAutoRepeaterSettingsExtension::Direction dir); /** Returns the monitor slot match. */ virtual AnytoneDMRSettingsExtension::SlotMatch monitorSlotMatch() const; /** Sets the monitor slot match. */ virtual void setMonitorSlotMatch(AnytoneDMRSettingsExtension::SlotMatch match); /** Returns @c true if the monitor matches color code. */ virtual bool monitorColorCodeMatch() const; /** Enables/disables monitor color code match. */ virtual void enableMonitorColorCodeMatch(bool enable); /** Returns @c true if the monitor matches ID. */ virtual bool monitorIDMatch() const; /** Enables/disables monitor ID match. */ virtual void enableMonitorIDMatch(bool enable); /** Returns @c true if the monitor holds the time slot. */ virtual bool monitorTimeSlotHold() const; /** Enables/disables monitor time slot hold. */ virtual void enableMonitorTimeSlotHold(bool enable); AnytoneDisplaySettingsExtension::LastCallerDisplayMode lastCallerDisplayMode() const; void setLastCallerDisplayMode(AnytoneDisplaySettingsExtension::LastCallerDisplayMode mode); /** Returns the analog call hold in seconds. */ virtual unsigned fmCallHold() const; /** Sets the analog call hold in seconds. */ virtual void setFMCallHold(unsigned sec); bool displayClock() const; void enableDisplayClock(bool enable); /** Returns @c true if the GPS range reporting is enabled. */ virtual bool gpsMessageEnabled() const; /** Enables/disables GPS range reporting. */ virtual void enableGPSMessage(bool enable); bool enhanceAudio() const; void enableEnhancedAudio(bool enable); Frequency minVFOScanFrequencyUHF() const; void setMinVFOScanFrequencyUHF(Frequency hz); Frequency maxVFOScanFrequencyUHF() const; void setMaxVFOScanFrequencyUHF(Frequency hz); Frequency minVFOScanFrequencyVHF() const; void setMinVFOScanFrequencyVHF(Frequency hz); Frequency maxVFOScanFrequencyVHF() const; void setMaxVFOScanFrequencyVHF(Frequency hz); bool hasAutoRepeaterOffsetFrequencyIndexUHF() const; unsigned autoRepeaterOffsetFrequencyIndexUHF() const; void setAutoRepeaterOffsetFrequenyIndexUHF(unsigned idx); void clearAutoRepeaterOffsetFrequencyIndexUHF(); bool hasAutoRepeaterOffsetFrequencyIndexVHF() const; unsigned autoRepeaterOffsetFrequencyIndexVHF() const; void setAutoRepeaterOffsetFrequenyIndexVHF(unsigned idx); void clearAutoRepeaterOffsetFrequencyIndexVHF(); Frequency autoRepeaterMinFrequencyVHF() const; void setAutoRepeaterMinFrequencyVHF(Frequency Hz); Frequency autoRepeaterMaxFrequencyVHF() const; void setAutoRepeaterMaxFrequencyVHF(Frequency Hz); Frequency autoRepeaterMinFrequencyUHF() const; void setAutoRepeaterMinFrequencyUHF(Frequency Hz); Frequency autoRepeaterMaxFrequencyUHF() const; void setAutoRepeaterMaxFrequencyUHF(Frequency Hz); void callToneMelody(Melody &melody) const; void setCallToneMelody(const Melody &melody); void idleToneMelody(Melody &melody) const; void setIdleToneMelody(const Melody &melody); void resetToneMelody(Melody &melody) const; void setResetToneMelody(const Melody &melody); /** Returns the priority Zone A index. */ virtual unsigned priorityZoneAIndex() const; /** Sets the priority zone A index. */ virtual void setPriorityZoneAIndex(unsigned idx); /** Returns the priority Zone B index. */ virtual unsigned priorityZoneBIndex() const; /** Sets the priority zone B index. */ virtual void setPriorityZoneBIndex(unsigned idx); bool displayCall() const; void enableDisplayCall(bool enable); /** Returns @c true if bluetooth is enabled. */ virtual bool bluetooth() const; /** Enables/disables bluetooth. */ virtual void enableBluetooth(bool enable); /** Returns @c true if the internal mic is additionally active when BT is active. */ virtual bool btAndInternalMic() const; /** Enables/disables the internal mic when BT is active. */ virtual void enableBTAndInternalMic(bool enable); /** Returns @c true if the internal speaker is additionally active when BT is active. */ virtual bool btAndInternalSpeaker() const; /** Enables/disables the internal speaker when BT is active. */ virtual void enableBTAndInternalSpeaker(bool enable); /** Returns @c true if the plug-in record tone is enabled. */ virtual bool pluginRecTone() const; /** Enables/disables the plug-in record tone. */ virtual void enablePluginRecTone(bool enable); /** Returns the GPS ranging interval in seconds. */ virtual Interval gpsUpdatePeriod() const; /** Sets the GPS ranging interval in seconds. */ virtual void setGPSUpdatePeriod(Interval sec); /** Returns the bluetooth microphone gain [1,10]. */ virtual unsigned int btMicGain() const; /** Sets the bluetooth microphone gain [1,10]. */ virtual void setBTMicGain(unsigned int gain); /** Returns the bluetooth speaker gain [1,10]. */ virtual unsigned int btSpeakerGain() const; /** Sets the bluetooth speaker gain [1,10]. */ virtual void setBTSpeakerGain(unsigned int gain); /** Returns @c true if the channel number is displayed. */ virtual bool displayChannelNumber() const; /** Enables/disables display of channel number. */ virtual void enableDisplayChannelNumber(bool enable); bool showCurrentContact() const; void enableShowCurrentContact(bool enable); /** Returns the auto roaming period in minutes. */ virtual Interval autoRoamPeriod() const; /** Sets the auto roaming period in minutes. */ virtual void setAutoRoamPeriod(Interval min); AnytoneDisplaySettingsExtension::Color callDisplayColor() const; void setCallDisplayColor(AnytoneDisplaySettingsExtension::Color color); bool gpsUnitsImperial() const; void enableGPSUnitsImperial(bool enable); bool knobLock() const; void enableKnobLock(bool enable); bool keypadLock() const; void enableKeypadLock(bool enable); bool sidekeysLock() const; void enableSidekeysLock(bool enable); bool keyLockForced() const; void enableKeyLockForced(bool enable); /** Returns the auto-roam delay in seconds. */ virtual Interval autoRoamDelay() const; /** Sets the auto-roam delay in seconds. */ virtual void setAutoRoamDelay(Interval sec); /** Returns the standby text color. */ virtual AnytoneDisplaySettingsExtension::Color standbyTextColor() const; /** Sets the standby text color. */ virtual void setStandbyTextColor(AnytoneDisplaySettingsExtension::Color color); /** Returns the standby image color. */ virtual AnytoneDisplaySettingsExtension::Color standbyBackgroundColor() const; /** Sets the standby image color. */ virtual void setStandbyBackgroundColor(AnytoneDisplaySettingsExtension::Color color); bool showLastHeard() const; void enableShowLastHeard(bool enable); /** Returns the SMS format. */ virtual AnytoneDMRSettingsExtension::SMSFormat smsFormat() const; /** Sets the SMS format. */ virtual void setSMSFormat(AnytoneDMRSettingsExtension::SMSFormat fmt); AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionB() const; void setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir); /** If enabled, the FM ID is sent together with selected contact. */ virtual bool fmSendIDAndContact() const; /** Enables/disables sending contact with FM ID. */ virtual void enableFMSendIDAndContact(bool enable); bool defaultChannel() const; void enableDefaultChannel(bool enable); unsigned defaultZoneIndexA() const; void setDefaultZoneIndexA(unsigned idx); unsigned defaultZoneIndexB() const; void setDefaultZoneIndexB(unsigned idx); bool defaultChannelAIsVFO() const; unsigned defaultChannelAIndex() const; void setDefaultChannelAIndex(unsigned idx); void setDefaultChannelAToVFO(); bool defaultChannelBIsVFO() const; unsigned defaultChannelBIndex() const; void setDefaultChannelBIndex(unsigned idx); void setDefaultChannelBToVFO(); /** Returns the default roaming zone index. */ virtual unsigned defaultRoamingZoneIndex() const; /** Sets the default roaming zone index. */ virtual void setDefaultRoamingZoneIndex(unsigned idx); /** Returns @c true if repeater range check is enabled. */ virtual bool repeaterRangeCheck() const; /** Enables/disables repeater range check. */ virtual void enableRepeaterRangeCheck(bool enable); /** Returns the repeater range check period in seconds. */ virtual Interval repeaterRangeCheckInterval() const; /** Sets the repeater range check interval in seconds. */ virtual void setRepeaterRangeCheckInterval(Interval sec); /** Returns the number of repeater range checks. */ virtual unsigned repeaterRangeCheckCount() const; /** Sets the number of repeater range checks. */ virtual void setRepeaterRangeCheckCount(unsigned n); /** Returns the roaming start condition. */ virtual AnytoneRoamingSettingsExtension::RoamStart roamingStartCondition() const; /** Sets the roaming start condition. */ virtual void setRoamingStartCondition(AnytoneRoamingSettingsExtension::RoamStart cond); /** Returns @c true if the "separate display" is enabled. */ virtual bool separateDisplay() const; /** Enables/disables "separate display. */ virtual void enableSeparateDisplay(bool enable); bool keepLastCaller() const; void enableKeepLastCaller(bool enable); /** Returns the channel name color. */ virtual AnytoneDisplaySettingsExtension::Color channelNameColor() const; /** Sets the channel name color. */ virtual void setChannelNameColor(AnytoneDisplaySettingsExtension::Color color); /** Returns @c true if repeater check notification is enabled. */ virtual bool repeaterCheckNotification() const; /** Enables/disables repeater check notification. */ virtual void enableRepeaterCheckNotification(bool enable); /** Returns @c true if roaming is enabled. */ virtual bool roaming() const; /** Enables/disables repeater check notification. */ virtual void enableRoaming(bool enable); AnytoneKeySettingsExtension::KeyFunction funcKey1Short() const; void setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey2Short() const; void setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func); /** Returns the function for programmable function key 3 short press. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKey3Short() const; /** Sets the function for programmable function key 3 short press. */ virtual void setFuncKey3Short(AnytoneKeySettingsExtension::KeyFunction func); /** Returns the function for programmable function key 4 short press. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKey4Short() const; /** Sets the function for programmable function key 4 short press. */ virtual void setFuncKey4Short(AnytoneKeySettingsExtension::KeyFunction func); /** Returns the function for programmable function key 5 short press. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKey5Short() const; /** Sets the function for programmable function key 5 short press. */ virtual void setFuncKey5Short(AnytoneKeySettingsExtension::KeyFunction func); /** Returns the function for programmable function key 6 short press. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKey6Short() const; /** Sets the function for programmable function key 6 short press. */ virtual void setFuncKey6Short(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyAShort() const; void setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyBShort() const; void setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyCShort() const; void setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func); /** Returns the function for programmable function key D short press. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKeyDShort() const; /** Sets the function for programmable function key D short press. */ virtual void setFuncKeyDShort(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey1Long() const; void setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey2Long() const; void setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func); /** Returns the function for programmable function key 3 long press. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKey3Long() const; /** Sets the function for programmable function key 3 long press. */ virtual void setFuncKey3Long(AnytoneKeySettingsExtension::KeyFunction func); /** Returns the function for programmable function key 4 long press. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKey4Long() const; /** Sets the function for programmable function key 4 long press. */ virtual void setFuncKey4Long(AnytoneKeySettingsExtension::KeyFunction func); /** Returns the function for programmable function key 5 long press. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKey5Long() const; /** Sets the function for programmable function key 5 long press. */ virtual void setFuncKey5Long(AnytoneKeySettingsExtension::KeyFunction func); /** Returns the function for programmable function key 6 long press. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKey6Long() const; /** Sets the function for programmable function key 6 long press. */ virtual void setFuncKey6Long(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyALong() const; void setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyBLong() const; void setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyCLong() const; void setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func); /** Returns the function for programmable function key D long press. */ virtual AnytoneKeySettingsExtension::KeyFunction funcKeyDLong() const; /** Sets the function for programmable function key D long press. */ virtual void setFuncKeyDLong(AnytoneKeySettingsExtension::KeyFunction func); /** Returns the number of repeater check notifications. */ virtual unsigned repeaterCheckNumNotifications() const; /** Sets the number of repeater check notifications. */ virtual void setRepeaterCheckNumNotifications(unsigned num); /** Returns the transmit timeout rekey interval in seconds [0,255], 0=Off. */ virtual Interval transmitTimeoutRekey() const; /** Sets the transmit timeout rekey interval in seconds [0,255], 0=Off. */ virtual void setTransmitTimeoutRekey(Interval dt); /** Returns @c true, if the bluetooth hold time is enabled. */ virtual bool btHoldTimeEnabled() const; /** Returns @c true, if the bluetooth hold time is infinite. */ virtual bool btHoldTimeInfinite() const; /** Returns the bluetooth hold time. */ virtual Interval btHoldTime() const; /** Sets the bluethooth hold time (1-120s). */ virtual void setBTHoldTime(Interval interval); /** Sets the bluethooth hold time to infinite. */ virtual void setBTHoldTimeInfinite(); /** Sets the bluethooth hold time to infinite. */ virtual void disableBTHoldTime(); /** Returns the bluetooth RX delay in ms. */ virtual Interval btRXDelay() const; /** Sets the bluetooth RX delay in ms. */ virtual void setBTRXDelay(Interval delay); bool fromConfig(const Flags &flags, Context &ctx); bool updateConfig(Context &ctx); bool linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err); protected: /** Some internal offsets. */ struct Offset: AnytoneCodeplug::GeneralSettingsElement::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int enableKeyTone() { return 0x0000; } static constexpr unsigned int transmitTimeout() { return 0x0004; } static constexpr unsigned int language() { return 0x0005; } static constexpr unsigned int vfoScanType() { return 0x000b; } static constexpr unsigned int dmrMicGain() { return 0x000c; } static constexpr unsigned int vfoModeA() { return 0x000d; } static constexpr unsigned int vfoModeB() { return 0x000e; } static constexpr unsigned int steType() { return 0x000f; } static constexpr unsigned int steFrequency() { return 0x0010; } static constexpr unsigned int groupCallHangTime() { return 0x0011; } static constexpr unsigned int privateCallHangTime() { return 0x0012; } static constexpr unsigned int preWaveDelay() { return 0x0013; } static constexpr unsigned int wakeHeadPeriod() { return 0x0014; } static constexpr unsigned int wfmChannelIndex() { return 0x0015; } static constexpr unsigned int wfmVFOEnabled() { return 0x0016; } static constexpr unsigned int memZoneA() { return 0x0017; } static constexpr unsigned int memZoneB() { return 0x0018; } static constexpr unsigned int wfmEnable() { return 0x0019; } // new static constexpr unsigned int enableRecoding() { return 0x001a; } static constexpr unsigned int displayBrightness() { return 0x001d; } static constexpr unsigned int gpsEnable() { return 0x001f; } static constexpr unsigned int smsAlert() { return 0x0020; } static constexpr unsigned int wfmMonitor() { return 0x0021;} static constexpr unsigned int activeChannelB() { return 0x0023; } static constexpr unsigned int subChannel() { return 0x0024; } static constexpr unsigned int tbstFrequency() { return 0x0025; } static constexpr unsigned int callAlert() { return 0x0026; } static constexpr unsigned int gpsTimeZone() { return 0x0027; } static constexpr unsigned int talkPermit() { return 0x0028; } static constexpr unsigned int dmrResetTone() { return 0x0029; } static constexpr unsigned int idleChannelTone() { return 0x002a; } static constexpr unsigned int menuExitTime() { return 0x002b; } static constexpr unsigned int filterOwnID() { return 0x002c; } static constexpr unsigned int startupTone() { return 0x002d; } static constexpr unsigned int callEndPrompt() { return 0x002e; } static constexpr unsigned int maxSpeakerVolume() { return 0x002f; } static constexpr unsigned int remoteStunKill() { return 0x0030; } static constexpr unsigned int remoteMonitor() { return 0x0031; } static constexpr unsigned int getGPSPosition() { return 0x0032; } static constexpr unsigned int longPressDuration() { return 0x0033; } static constexpr unsigned int volumeChangePrompt() { return 0x0034; } static constexpr unsigned int autoRepeaterDirA() { return 0x0035; } static constexpr unsigned int monSlotMatch() { return 0x0036; } static constexpr unsigned int monColorCodeMatch() { return 0x0037; } static constexpr unsigned int monIDMatch() { return 0x0038; } static constexpr unsigned int monTimeSlotHold() { return 0x0039; } static constexpr unsigned int lastCallerDisplay() { return 0x003a; } static constexpr unsigned int fmCallHold() { return 0x003c; } static constexpr unsigned int showClock() { return 0x003d; } static constexpr unsigned int enableGPSMessage() { return 0x003e; } static constexpr unsigned int enhanceAudio() { return 0x003f; } static constexpr unsigned int minVFOScanUHF() { return 0x0040; } static constexpr unsigned int maxVFOScanUHF() { return 0x0044; } static constexpr unsigned int minVFOScanVHF() { return 0x0048; } static constexpr unsigned int maxVFOScanVHF() { return 0x004c; } static constexpr unsigned int autoRepMinVHF() { return 0x0050; } static constexpr unsigned int autoRepMaxVHF() { return 0x0054; } static constexpr unsigned int autoRepMinUHF() { return 0x0058; } static constexpr unsigned int autoRepMaxUHF() { return 0x005c; } static constexpr unsigned int callToneTones() { return 0x0060; } static constexpr unsigned int callToneDurations() { return 0x006a; } static constexpr unsigned int idleToneTones() { return 0x0074; } static constexpr unsigned int idleToneDurations() { return 0x007e; } static constexpr unsigned int resetToneTones() { return 0x0088; } static constexpr unsigned int resetToneDurations() { return 0x0092; } static constexpr unsigned int autoRepOffsetUHF() { return 0x009c; } static constexpr unsigned int autoRepOffsetVHF() { return 0x009d; } static constexpr unsigned int priorityZoneA() { return 0x009f; } static constexpr unsigned int priorityZoneB() { return 0x00a0; } static constexpr unsigned int callDisplayMode() { return 0x00a2; } static constexpr unsigned int bluetooth() { return 0x00a4; } static constexpr unsigned int btAndInternalMic() { return 0x00a5; } static constexpr unsigned int btAndInternalSpeaker(){ return 0x00a6; } static constexpr unsigned int pluginRecTone() { return 0x00a7; } static constexpr unsigned int gpsRangingInterval() { return 0x00a8; } static constexpr unsigned int btMicGain() { return 0x00a9; } static constexpr unsigned int btSpeakerGain() { return 0x00aa; } static constexpr unsigned int showChannelNumber() { return 0x00ab; } static constexpr unsigned int showCurrentContact() { return 0x00ac; } static constexpr unsigned int autoRoamPeriod() { return 0x00ad; } static constexpr unsigned int callColor() { return 0x00ae; } static constexpr unsigned int gpsUnits() { return 0x00af; } static constexpr unsigned int knobLock() { return 0x00b0; } static constexpr unsigned int keypadLock() { return 0x00b0; } static constexpr unsigned int sideKeyLock() { return 0x00b0; } static constexpr unsigned int forceKeyLock() { return 0x00b0; } static constexpr unsigned int autoRoamDelay() { return 0x00b1; } static constexpr unsigned int standbyTextColor() { return 0x00b2; } static constexpr unsigned int standbyBackground() { return 0x00b3; } static constexpr unsigned int showLastHeard() { return 0x00b4; } static constexpr unsigned int smsFormat() { return 0x00b5; } static constexpr unsigned int autoRepeaterDirB() { return 0x00b6; } static constexpr unsigned int fmSendIDAndContact() { return 0x00b7; } static constexpr unsigned int defaultChannels() { return 0x00b8; } static constexpr unsigned int defaultZoneA() { return 0x00b9; } static constexpr unsigned int defaultZoneB() { return 0x00ba; } static constexpr unsigned int defaultChannelA() { return 0x00bb; } static constexpr unsigned int defaultChannelB() { return 0x00bc; } static constexpr unsigned int defaultRoamingZone() { return 0x00bd; } static constexpr unsigned int repRangeCheck() { return 0x00be; } static constexpr unsigned int rangeCheckInterval() { return 0x00bf; } static constexpr unsigned int rangeCheckCount() { return 0x00c0; } static constexpr unsigned int roamStartCondition() { return 0x00c1; } static constexpr unsigned int txBacklightDuration() { return 0x00c2; } static constexpr unsigned int displaySeparator() { return 0x00c3; } static constexpr unsigned int keepLastCaller() { return 0x00c4; } static constexpr unsigned int channelNameColor() { return 0x00c5; } static constexpr unsigned int repCheckNotify() { return 0x00c6; } static constexpr unsigned int rxBacklightDuration() { return 0x00c7; } static constexpr unsigned int roaming() { return 0x00c8; } static constexpr unsigned int progFuncKey1Short() { return 0x00c9; } static constexpr unsigned int progFuncKey2Short() { return 0x00ca; } static constexpr unsigned int progFuncKey3Short() { return 0x00cb; } static constexpr unsigned int progFuncKey4Short() { return 0x00cc; } static constexpr unsigned int progFuncKey5Short() { return 0x00cd; } static constexpr unsigned int progFuncKey6Short() { return 0x00ce; } static constexpr unsigned int progFuncKeyAShort() { return 0x00cf; } static constexpr unsigned int progFuncKeyBShort() { return 0x00d0; } static constexpr unsigned int progFuncKeyCShort() { return 0x00d1; } static constexpr unsigned int progFuncKeyDShort() { return 0x00d2; } static constexpr unsigned int progFuncKey1Long() { return 0x00d3; } static constexpr unsigned int progFuncKey2Long() { return 0x00d4; } static constexpr unsigned int progFuncKey3Long() { return 0x00d5; } static constexpr unsigned int progFuncKey4Long() { return 0x00d6; } static constexpr unsigned int progFuncKey5Long() { return 0x00d7; } static constexpr unsigned int progFuncKey6Long() { return 0x00d8; } static constexpr unsigned int progFuncKeyALong() { return 0x00d9; } static constexpr unsigned int progFuncKeyBLong() { return 0x00da; } static constexpr unsigned int progFuncKeyCLong() { return 0x00db; } static constexpr unsigned int progFuncKeyDLong() { return 0x00dc; } static constexpr unsigned int repCheckNumNotify() { return 0x00de; } static constexpr unsigned int totRekey() { return 0x00df; } static constexpr unsigned int btHoldTime() { return 0x00e1; } static constexpr unsigned int btRXDelay() { return 0x00e2; } /// @endcond }; }; /** Represents the hot-key settings of the radio within the D578UV binary codeplug. * * This class extends the common @c AnytoneCodeplug::HotKeySettings element, encoding 24 instead * of 17 @c HotKeySettingsElement. * * Memory layout of the hot-key settings (size 0x0370 bytes): * @verbinclude d578uv_hotkeysettings.txt */ class HotKeySettingsElement: public AnytoneCodeplug::HotKeySettingsElement { protected: /** Hidden constructor. */ HotKeySettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ HotKeySettingsElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0970; } uint8_t *hotKeySetting(unsigned int n) const; public: /** Some limits for this element. */ struct Limit { static constexpr unsigned int numEntries() { return 24; } ///< Maximum number of hot-key entries. }; }; /** Implements the air-band receiver channel. * * Memory layout of the air-band channel list (size 0x0020 bytes): * @verbinclude d578uv_airbandchannel.txt */ class AirBandChannelElement: public Element { protected: /** Hidden constructor. */ AirBandChannelElement(uint8_t *ptr, size_t size); public: /** Constructor. */ AirBandChannelElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0020; } /** Resets the element. */ void clear(); /** The channel frequency. */ virtual Frequency frequency() const; /** Sets the channel frequency. */ virtual void setFrequency(Frequency freq); /** The name of the channel. */ virtual QString name() const; /** Sets the name of the channel. */ virtual void setName(const QString &name); public: /** Some limits of the channel. */ struct Limit { static constexpr unsigned int nameLength() { return 16; } ///< Maximum name length. }; public: /** Internal used offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int frequency() { return 0x0000; } static constexpr unsigned int name() { return 0x0004; } /// @endcond }; }; /** Represents the bitmap indicating which channels are valid and which are included in the * air-band scan. */ class AirBandBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ AirBandBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ AirBandBitmapElement(uint8_t *ptr); /** The element size. */ static constexpr unsigned int size() { return 0x0020; } }; protected: /** Hidden constructor. */ explicit D578UVCodeplug(const QString &label, QObject *parent = nullptr); public: /** Empty constructor. */ explicit D578UVCodeplug(QObject *parent = nullptr); protected: bool allocateBitmaps(); void allocateUpdated(); void allocateHotKeySettings(); bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()); void allocateContacts(); bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); void allocateGeneralSettings(); bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocates the air-band channels und VFO settings. */ virtual void allocateAirBand(); public: /** Some limtis for the codeplug. */ struct Limit: D878UVCodeplug::Limit { /// Maximum number of air-band channels. static constexpr unsigned int airBandChannels() { return 100; } }; protected: /** Internal used offsets within the codeplug. */ struct Offset: D878UVCodeplug::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int contactIdTable() { return 0x04800000; } static constexpr unsigned int settings() { return 0x02500000; } static constexpr unsigned int gpsMessages() { return 0x02501280; } static constexpr unsigned int settingsExtension() { return 0x02501400; } static constexpr unsigned int airBandChannels() { return 0x02BC0000; } static constexpr unsigned int airBandVFO() { return 0x02BC1000; } static constexpr unsigned int airBandChannelBitmap() { return 0x02BC1020; } static constexpr unsigned int airBandScanBitmap() { return 0x02BC1040; } /// @endcond }; }; #endif // D578UV_CODEPLUG_HH qdmr-0.12.3/lib/d578uv_limits.cc000066400000000000000000000206241501654372000162470ustar00rootroot00000000000000#include "d578uv_limits.hh" #include "channel.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "zone.hh" #include "scanlist.hh" #include "gpssystem.hh" #include "roamingzone.hh" D578UVLimits::D578UVLimits(const std::initializer_list > &rxFreqRanges, const std::initializer_list > &txFreqRanges, const QString &hardwareRevision, QObject *parent) : AnytoneLimits(hardwareRevision, "V110", true, parent) { // Define limits for call-sign DB _hasCallSignDB = true; _callSignDBImplemented = true; _numCallSignDBEntries = 500000; // Define limits for satellite config _hasSatelliteConfig = false; _satelliteConfigImplemented = false; _numSatellites = 0; /* Define limits for the general settings. */ add("settings", new RadioLimitItem{ { "introLine1", new RadioLimitString(-1, 14, RadioLimitString::ASCII) }, { "introLine2", new RadioLimitString(-1, 14, RadioLimitString::ASCII) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, -1) } }); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList { { DMRRadioID::staticMetaObject, 1, 250, new RadioLimitObject { {"name", new RadioLimitString(1,8, RadioLimitString::ASCII) }, {"id", new RadioLimitUInt(0, 16777215)} } } }); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum{ (unsigned)DMRContact::PrivateCall, (unsigned)DMRContact::GroupCall, (unsigned)DMRContact::AllCall }}, { "number", new RadioLimitUInt(0, 16777215) } } }, { DTMFContact::staticMetaObject, 0, 128, new RadioLimitObject { { "name", new RadioLimitString(1, 15, RadioLimitString::ASCII) }, { "number", new RadioLimitString(1, 14, RadioLimitString::DTMF) } } } }); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "contacts", new RadioLimitGroupCallRefList(1, 64) } })); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, 4000, new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies(rxFreqRanges, true)}, {"txFrequency", new RadioLimitTransmitFrequencies(txFreqRanges)}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRef(APRSSystem::staticMetaObject)}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1,16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies(rxFreqRanges, true)}, {"txFrequency", new RadioLimitTransmitFrequencies(txFreqRanges)}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false)}, {"aprs", new RadioLimitObjRef(PositioningSystem::staticMetaObject, true)}, {"roaming", new RadioLimitObjRef(RoamingZone::staticMetaObject, true) }, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } )); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone( 250, { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions }) ) ); /* Define limits for scan lists. */ add("scanlists", new RadioLimitList( ScanList::staticMetaObject, 0, 250, new RadioLimitObject{ { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "primary", new RadioLimitObjRef(Channel::staticMetaObject, false) }, { "secondary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "revert", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "channels", new RadioLimitRefList(0, 31, Channel::staticMetaObject) } })); /* Handle positioning systems. */ add("positioning", new RadioLimitList{ { GPSSystem::staticMetaObject, 0, 8, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "period", new RadioLimitUInt(0, 7650) }, { "contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false) }, { "revert", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } }, { APRSSystem::staticMetaObject, 0, 1, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "period", new RadioLimitUInt(0, 7650) }, { "revert", new RadioLimitObjRef({SelectedChannel::staticMetaObject, FMChannel::staticMetaObject}, false) }, { "icon", new RadioLimitEnum{} }, { "message", new RadioLimitString(0, 60, RadioLimitString::ASCII) } ///@todo extend APRSSystem to expose other settings as properties. }} } ); /* Handle roaming zones. */ add("roaming", new RadioLimitList(RoamingZone::staticMetaObject, 0, 64, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "channels", new RadioLimitRefList(0, 64, DMRChannel::staticMetaObject) } } ) ); } qdmr-0.12.3/lib/d578uv_limits.hh000066400000000000000000000010151501654372000162520ustar00rootroot00000000000000#ifndef D578UVLIMITS_HH #define D578UVLIMITS_HH #include "anytone_limits.hh" /** Implements the limits for the AnyTone AT-D878UV. * @ingroup d578 */ class D578UVLimits: public AnytoneLimits { Q_OBJECT public: /** Constructor. */ D578UVLimits(const std::initializer_list > &rxFreqRanges, const std::initializer_list > &txFreqRanges, const QString &hardwareRevision, QObject *parent=nullptr); }; #endif // D578UVLIMITS_HH qdmr-0.12.3/lib/d868uv.cc000066400000000000000000000143441501654372000146720ustar00rootroot00000000000000#include "d868uv.hh" #include "userdatabase.hh" #include "d868uv_codeplug.hh" #include "d868uv_callsigndb.hh" #include "d868uv_limits.hh" #include "config.hh" #include "logger.hh" #define RBSIZE 16 #define WBSIZE 16 D868UV::D868UV(AnytoneInterface *device, QObject *parent) : AnytoneRadio("Anytone AT-D868UV", device, parent), _limits(nullptr) { _codeplug = new D868UVCodeplug(this); _codeplug->clear(); _callsigns = new D868UVCallsignDB(this); // Get device info and determine supported TX frequency bands AnytoneInterface::RadioVariant info; if (_dev) _dev->getInfo(info); switch (info.bands) { case 0x00: _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, info.version, this); break; case 0x01: _limits = new D868UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this); break; case 0x02: _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x03: _limits = new D868UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x04: _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, info.version, this); break; case 0x05: _limits = new D868UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, info.version, this); break; case 0x06: _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this); break; case 0x07: _limits = new D868UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this); break; case 0x08: _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(440.), Frequency::fromMHz(470.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(440.), Frequency::fromMHz(470.)} }, info.version, this); break; case 0x09: _limits = new D868UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, info.version, this); break; case 0x0a: _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(450.)} }, info.version, this); break; case 0x0b: _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x0c: _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} }, info.version, this); break; default: logInfo() << "Unknown band-code" << QString::number(int(info.bands), 16) << ": Ignore frequency limits."; _limits = new D868UVLimits({}, {}, info.version, this); break; } } const RadioLimits & D868UV::limits() const { return *_limits; } RadioInfo D868UV::defaultRadioInfo() { return RadioInfo( RadioInfo::D868UVE, "d868uve", "AT-D868UVE", "AnyTone", AnytoneInterface::interfaceInfo(), QList{ RadioInfo(RadioInfo::D868UV, "d868uv", "AT-D868UV", "AnyTone", AnytoneInterface::interfaceInfo()) }); } qdmr-0.12.3/lib/d868uv.hh000066400000000000000000000041501501654372000146760ustar00rootroot00000000000000/** @defgroup d868uv Anytone AT-D868UV * Device specific classes for Anytone AT-D868UV. * * \image html d878uv.jpg "AT-D868UV" width=200px * \image latex d878uv.jpg "AT-D868UV" width=200px * * @ingroup anytone */ #ifndef __D868UV_HH__ #define __D868UV_HH__ #include "radio.hh" #include "anytone_radio.hh" /** Implements an interface to Anytone AT-D868UV VHF/UHF 7W DMR (Tier I & II) radios. * * The reverse-engineering of the D868UVCodeplug was quiet hard as it is huge and the radio * provides a lot of bells and whistles. Moreover, the binary code-plug file created by the * windows CPS does not directly relate to the data being written to the device. These two issues * (a lot of features and a huge codeplug) require that the transfer of the codeplug to the * device is performed in 4 steps. * * First only the bitmaps of all lists are downloaded from the device. Then all elements that are * not touched or only updated by the common code-plug config are downloaded. Then, the common * config gets applied to the binary codeplug. That is, all channels, contacts, zones, group-lists * and scan-lists are generated and their bitmaps gets updated accordingly. Also the general config * gets updated from the common codeplug settings. Finally, the resulting binary codeplug gets * written back to the device. * * This rather complex method of writing a codeplug to the device is needed to maintain all * settings within the radio that are not defined within the common codeplug config while keeping * the amount of data being read from and written to the device small. * * @ingroup d868uv */ class D868UV: public AnytoneRadio { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit D868UV(AnytoneInterface *device=nullptr, QObject *parent=nullptr); const RadioLimits &limits() const; /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); protected: /** Holds the limits for this radio.*/ RadioLimits *_limits; }; #endif // __D868UV_HH__ qdmr-0.12.3/lib/d868uv_callsigndb.cc000066400000000000000000000173751501654372000170630ustar00rootroot00000000000000#include "userdatabase.hh" #include "d868uv_callsigndb.hh" #include "utils.hh" #include /* ********************************************************************************************* * * Implementation of D868UVCallsignDB::EntryElement * ********************************************************************************************* */ D868UVCallsignDB::EntryElement::EntryElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } D868UVCallsignDB::EntryElement::EntryElement(uint8_t *ptr) : Element(ptr, 0x0064) { // pass... } void D868UVCallsignDB::EntryElement::clear() { // Clear header and all strings. memset(_data, 0x00, 12); } void D868UVCallsignDB::EntryElement::setCallType(DMRContact::Type type) { switch (type) { case DMRContact::PrivateCall: setUInt8(0x0000, 0); break; case DMRContact::GroupCall: setUInt8(0x0000, 1); break; case DMRContact::AllCall: setUInt8(0x0000, 2); break; } } void D868UVCallsignDB::EntryElement::setNumber(unsigned num) { setBCD8_be(0x0001, num); } void D868UVCallsignDB::EntryElement::setFriendFlag(bool set) { setBit(0x0005, 4, set); } void D868UVCallsignDB::EntryElement::setRingTone(RingTone tone) { setUInt2(0x0005, 0, (unsigned)tone); } void D868UVCallsignDB::EntryElement::setContent( const QString &name, const QString &city, const QString &call, const QString &state, const QString &country, const QString &comment) { unsigned addr = 0x0006; writeASCII(addr, name, 16, 0x00); addr += std::min(16, name.size()); setUInt8(addr, 0); addr++; writeASCII(addr, city, 15, 0x00); addr += std::min(15, city.size()); setUInt8(addr, 0); addr++; writeASCII(addr, call, 8, 0x00); addr += std::min(8, call.size()); setUInt8(addr, 0); addr++; writeASCII(addr, state, 16, 0x00); addr += std::min(16, state.size()); setUInt8(addr, 0); addr++; writeASCII(addr, country, 16, 0x00); addr += std::min(16, country.size()); setUInt8(addr, 0); addr++; writeASCII(addr, comment, 16, 0x00); addr += std::min(16, comment.size()); setUInt8(addr, 0); addr++; } unsigned D868UVCallsignDB::EntryElement::fromUser(const UserDatabase::User &user) { clear(); setCallType(DMRContact::PrivateCall); setNumber(user.id); setRingTone(RingTone::Off); setContent(user.name, user.city, user.call, user.state, user.country, ""); return size(user); } unsigned D868UVCallsignDB::EntryElement::size(const UserDatabase::User &user) { return 6 // header + std::min(16, user.name.size())+1 // name + std::min(15, user.city.size())+1 // city + std::min( 8, user.call.size())+1 // call + std::min(16, user.state.size())+1 // state + std::min(16, user.country.size())+1 // country + 1; // no comment but 0x00 terminator } /* ********************************************************************************************* * * Implementation of D868UVCallsignDB::LimitsElement * ********************************************************************************************* */ D868UVCallsignDB::LimitsElement::LimitsElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } D868UVCallsignDB::LimitsElement::LimitsElement(uint8_t *ptr) : Element(ptr, 0x0010) { // pass... } void D868UVCallsignDB::LimitsElement::clear() { memset(_data, 0x00, _size); setTotalSize(0); } unsigned D868UVCallsignDB::LimitsElement::count() const { return getUInt32_le(0x0000); } void D868UVCallsignDB::LimitsElement::setCount(unsigned count) { setUInt32_le(0x0000, count); } unsigned D868UVCallsignDB::LimitsElement::endOfDB() const { return getUInt32_le(0x0004); } void D868UVCallsignDB::LimitsElement::setEndOfDB(unsigned addr) { setUInt32_le(0x0004, addr); } void D868UVCallsignDB::LimitsElement::setTotalSize(unsigned size) { setEndOfDB(D868UVCallsignDB::Offset::callsigns() + size); } unsigned D868UVCallsignDB::LimitsElement::size() { return 0x0010; } /* ********************************************************************************************* * * Implementation of D868UVCallsignDB * ********************************************************************************************* */ D868UVCallsignDB::D868UVCallsignDB(QObject *parent) : CallsignDB(parent) { // allocate and clear DB memory addImage("AnyTone AT-D878UV Callsign database."); } bool D868UVCallsignDB::encode(UserDatabase *db, const Selection &selection, const ErrorStack &err) { Q_UNUSED(err) // Determine size of call-sign DB in memory qint64 n = std::min(db->count(), qint64(Limit::entries())); // If DB size is limited by settings if (selection.hasCountLimit()) n = std::min(n, (qint64)selection.countLimit()); // Select n users and sort them in ascending order of their IDs QVector users; users.reserve(n); for (unsigned i=0; iuser(i)); std::sort(users.begin(), users.end(), [](const UserDatabase::User &a, const UserDatabase::User &b) { return a.id < b.id; }); // Compute total size of callsign db entries size_t dbSize = 0; size_t indexSize = n*IndexEntryElement::size(); for (qint64 i=0; i * Callsign database * Start Size Content * 04000000 max. 186a00 Index of callsign entries. Follows the same * weird format as @c D868UVCodeplug::contact_map_t. Sorted by ID. Empty entries set to * 0xffffffffffffffff. * 044c0000 unknown Database limits, see @c limits_t. * 04500000 unknown The actual DB entries, each entry is of * variable size but shares the same header, see @c entry_t. Order arbitrary. * Filled with 0x00. * * * @ingroup d868uv */ class D868UVCallsignDB : public CallsignDB { Q_OBJECT public: /** Represents the header of an entry in the callsign database. * Each entry is of variable size. That is, every entry has a common header containing the * number, call-type etc. All strings, that is contact name, city, callsign, state, country and * comment are 0x00 terminated strings in a lists. * * Max length for name is 16, city is 15, callsign is 8, state is 16, country is 16 and * comment is 16, excluding terminating 0x00. * * Memory layout of encoded Callsign/User database entry (variable size, min 0x000c-0x0064): * @verbinclude d868uv_callsigndbentry.txt */ class EntryElement: public Codeplug::Element { public: /** Notification tones for callsign entry. */ enum class RingTone { Off = 0, Tone = 1, Online = 2 }; protected: /** Hidden constructor. */ EntryElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit EntryElement(uint8_t *ptr); void clear(); /** Sets the call type. */ virtual void setCallType(DMRContact::Type type); /** Sets the DMR ID number. */ virtual void setNumber(unsigned num); /** Set/clear friend flag. */ virtual void setFriendFlag(bool set); /** Sets the ring tone. */ virtual void setRingTone(RingTone tone); /** Sets the entry content. */ virtual void setContent(const QString &name, const QString &city, const QString &call, const QString &state, const QString &country, const QString &comment); /** Constructs a database entry from the given user. * @returns The size of the entry. */ virtual unsigned fromUser(const UserDatabase::User &user); /** Computes the size of the database entry for the given user. */ static unsigned size(const UserDatabase::User &user); }; /** Represents a bank of call-sign DB entries. */ class EntryBankElement: public Codeplug::Element { protected: /** Hidden constructor. */ EntryBankElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit EntryBankElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x000186a0; } void clear(); /** Returns the i-th element of the bank. */ uint8_t *entry(unsigned int i) const; }; /** Same index entry used by the codeplug to map normal digital contacts to an contact index. Here * it maps to the byte offset within the database entries. */ typedef D868UVCodeplug::ContactMapElement IndexEntryElement; /** Represents a bank of index entries. */ class IndexBankElement: public Codeplug::Element { protected: /** Hidden constructor. */ IndexBankElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit IndexBankElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0001f400; } void clear(); /** Returns the i-th element of the bank. */ uint8_t *entry(unsigned int i) const; }; /** Stores some basic limits of the callsign db. * * Memory layout of encoded Callsign/User database entry (size 0x0010 bytes): * @verbinclude d868uv_callsigndblimit.txt */ class LimitsElement: public Codeplug::Element { protected: /** Hidden constructor. */ LimitsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ LimitsElement(uint8_t *ptr); /** Resets the limits. */ void clear(); /** Returns the number of entries in the DB. */ virtual unsigned count() const; /** Sets the number of entries. */ virtual void setCount(unsigned count); /** Returns the end-of-db address. */ virtual unsigned endOfDB() const; /** Sets the end-of-db address. */ virtual void setEndOfDB(unsigned addr); /** Sets the total size of the DB (updated end-of-db address). */ virtual void setTotalSize(unsigned size); public: /** Returns the size of the encoded element. */ static unsigned size(); }; public: /** Constructor, does not allocate any memory yet. */ explicit D868UVCallsignDB(QObject *parent=nullptr); /** Tries to encode as many entries of the given user-database. */ bool encode(UserDatabase *db, const Selection &selection=Selection(), const ErrorStack &err=ErrorStack()); public: /** Some limits for the call-sign DB. */ struct Limit { /// Specifies the max number of entries in the DB. static constexpr unsigned int entries() { return 200000; } }; protected: /** Some internal used offsets within the DB. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int index() { return 0x04000000; } static constexpr unsigned int betweenIndexBanks() { return 0x00040000; } static constexpr unsigned int callsigns() { return 0x04500000; } static constexpr unsigned int betweenCallsignBanks() { return 0x00040000; } static constexpr unsigned int limits() { return 0x044C0000; } /// @endcond }; }; #endif // D868UVCALLSIGNDB_HH qdmr-0.12.3/lib/d868uv_codeplug.cc000066400000000000000000002326111501654372000165530ustar00rootroot00000000000000#include "d868uv_codeplug.hh" #include "config.hh" #include "utils.hh" #include "channel.hh" #include "gpssystem.hh" #include "smsextension.hh" #include "userdatabase.hh" #include "config.h" #include "logger.hh" #include "utils.hh" #include #include #include #include /* ******************************************************************************************** * * Implementation of D868UVCodeplug::Color * ******************************************************************************************** */ AnytoneDisplaySettingsExtension::Color D868UVCodeplug::Color::decode(uint8_t code) { switch((CodedColor) code) { case White: return AnytoneDisplaySettingsExtension::Color::White; case Red: return AnytoneDisplaySettingsExtension::Color::Red; default: break; } return AnytoneDisplaySettingsExtension::Color::White; } uint8_t D868UVCodeplug::Color::encode(AnytoneDisplaySettingsExtension::Color color) { switch(color) { case AnytoneDisplaySettingsExtension::Color::White: return (uint8_t) White; case AnytoneDisplaySettingsExtension::Color::Black: return (uint8_t) Red; case AnytoneDisplaySettingsExtension::Color::Orange: return (uint8_t) Red; case AnytoneDisplaySettingsExtension::Color::Red: return (uint8_t) Red; case AnytoneDisplaySettingsExtension::Color::Yellow: return (uint8_t) White; case AnytoneDisplaySettingsExtension::Color::Green: return (uint8_t) White; case AnytoneDisplaySettingsExtension::Color::Turquoise: return (uint8_t) White; case AnytoneDisplaySettingsExtension::Color::Blue: return (uint8_t) Red; default: break; } return (uint8_t) White; } /* ******************************************************************************************** * * Implementation of D868UVCodeplug::ChannelElement * ******************************************************************************************** */ D868UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, unsigned size) : AnytoneCodeplug::ChannelElement(ptr, size) { // pass... } D868UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr) : AnytoneCodeplug::ChannelElement(ptr) { // pass... } bool D868UVCodeplug::ChannelElement::ranging() const { return getBit(0x0034, 0); } void D868UVCodeplug::ChannelElement::enableRanging(bool enable) { setBit(0x0034, 0, enable); } bool D868UVCodeplug::ChannelElement::throughMode() const { return getBit(0x0034, 1); } void D868UVCodeplug::ChannelElement::enableThroughMode(bool enable) { setBit(0x0034, 1, enable); } bool D868UVCodeplug::ChannelElement::dataACK() const { return !getBit(0x0034, 2); } void D868UVCodeplug::ChannelElement::enableDataACK(bool enable) { setBit(0x0034, 2, !enable); } bool D868UVCodeplug::ChannelElement::txDigitalAPRS() const { return getBit(0x0035, 0); } void D868UVCodeplug::ChannelElement::enableTXDigitalAPRS(bool enable) { setBit(0x0035, 0, enable); } unsigned D868UVCodeplug::ChannelElement::digitalAPRSSystemIndex() const { return getUInt8(0x0036); } void D868UVCodeplug::ChannelElement::setDigitalAPRSSystemIndex(unsigned idx) { setUInt8(0x0036, idx); } unsigned D868UVCodeplug::ChannelElement::dmrEncryptionKeyIndex() const { return getUInt8(0x003a); } void D868UVCodeplug::ChannelElement::setDMREncryptionKeyIndex(unsigned idx) { setUInt8(0x003a, idx); } bool D868UVCodeplug::ChannelElement::multipleKeyEncryption() const { return getBit(0x003b, 0); } void D868UVCodeplug::ChannelElement::enableMultipleKeyEncryption(bool enable) { setBit(0x003b, 0, enable); } bool D868UVCodeplug::ChannelElement::randomKey() const { return getBit(0x003b, 1); } void D868UVCodeplug::ChannelElement::enableRandomKey(bool enable) { setBit(0x003b, 1, enable); } bool D868UVCodeplug::ChannelElement::sms() const { return !getBit(0x003b, 2); } void D868UVCodeplug::ChannelElement::enableSMS(bool enable) { setBit(0x003b, 0, !enable); } Channel * D868UVCodeplug::ChannelElement::toChannelObj(Context &ctx) const { Channel *ch = AnytoneCodeplug::ChannelElement::toChannelObj(ctx); if (nullptr == ch) return nullptr; if (ch->is()) { DMRChannel *dch = ch->as(); if (AnytoneDMRChannelExtension *ext = dch->anytoneChannelExtension()) { ext->enableSMS(sms()); ext->enableDataACK(dataACK()); ext->enableThroughMode(throughMode()); } } return ch; } bool D868UVCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx) const { if (! AnytoneCodeplug::ChannelElement::linkChannelObj(c, ctx)) return false; if (c->is()) { DMRChannel *dc = c->as(); // Link to GPS system if (txDigitalAPRS() && (! ctx.has(digitalAPRSSystemIndex()))) logWarn() << "Cannot link to DMR APRS system index " << digitalAPRSSystemIndex() << ": undefined DMR APRS system."; else if (ctx.has(digitalAPRSSystemIndex())) dc->setAPRSObj(ctx.get(digitalAPRSSystemIndex())); } return true; } bool D868UVCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) { if (! AnytoneCodeplug::ChannelElement::fromChannelObj(c, ctx)) return false; if (c->is()) { const DMRChannel *dc = c->as(); // Set GPS system index if (dc->aprsObj() && dc->aprsObj()->is()) { setDigitalAPRSSystemIndex(ctx.index(dc->aprsObj()->as())); enableTXDigitalAPRS(true); enableRXAPRS(false); } else { enableTXDigitalAPRS(false); enableRXAPRS(false); } // Handle extension if (AnytoneDMRChannelExtension *ext = dc->anytoneChannelExtension()) { enableSMS(ext->sms()); enableDataACK(ext->dataACK()); enableThroughMode(ext->throughMode()); } } return true; } /* ******************************************************************************************** * * Implementation of D868UVCodeplug::GeneralSettingsElement::KeyFunction * ******************************************************************************************** */ uint8_t D868UVCodeplug::GeneralSettingsElement::KeyFunction::encode(AnytoneKeySettingsExtension::KeyFunction func) { switch (func) { case AnytoneKeySettingsExtension::KeyFunction::Off: return (uint8_t)KeyFunction::Off; case AnytoneKeySettingsExtension::KeyFunction::Voltage: return (uint8_t)KeyFunction::Voltage; case AnytoneKeySettingsExtension::KeyFunction::Power: return (uint8_t)KeyFunction::Power; case AnytoneKeySettingsExtension::KeyFunction::Repeater: return (uint8_t)KeyFunction::Repeater; case AnytoneKeySettingsExtension::KeyFunction::Reverse: return (uint8_t)KeyFunction::Reverse; case AnytoneKeySettingsExtension::KeyFunction::Encryption: return (uint8_t)KeyFunction::Encryption; case AnytoneKeySettingsExtension::KeyFunction::Call: return (uint8_t)KeyFunction::Call; case AnytoneKeySettingsExtension::KeyFunction::VOX: return (uint8_t)KeyFunction::VOX; case AnytoneKeySettingsExtension::KeyFunction::ToggleVFO: return (uint8_t)KeyFunction::ToggleVFO; case AnytoneKeySettingsExtension::KeyFunction::SubPTT: return (uint8_t)KeyFunction::SubPTT; case AnytoneKeySettingsExtension::KeyFunction::Scan: return (uint8_t)KeyFunction::Scan; case AnytoneKeySettingsExtension::KeyFunction::WFM: return (uint8_t)KeyFunction::WFM; case AnytoneKeySettingsExtension::KeyFunction::Alarm: return (uint8_t)KeyFunction::Alarm; case AnytoneKeySettingsExtension::KeyFunction::RecordSwitch: return (uint8_t)KeyFunction::RecordSwitch; case AnytoneKeySettingsExtension::KeyFunction::Record: return (uint8_t)KeyFunction::Record; case AnytoneKeySettingsExtension::KeyFunction::SMS: return (uint8_t)KeyFunction::SMS; case AnytoneKeySettingsExtension::KeyFunction::Dial: return (uint8_t)KeyFunction::Dial; case AnytoneKeySettingsExtension::KeyFunction::GPSInformation: return (uint8_t)KeyFunction::GPSInformation; case AnytoneKeySettingsExtension::KeyFunction::Monitor: return (uint8_t)KeyFunction::Monitor; case AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel: return (uint8_t)KeyFunction::ToggleMainChannel; case AnytoneKeySettingsExtension::KeyFunction::HotKey1: return (uint8_t)KeyFunction::HotKey1; case AnytoneKeySettingsExtension::KeyFunction::HotKey2: return (uint8_t)KeyFunction::HotKey2; case AnytoneKeySettingsExtension::KeyFunction::HotKey3: return (uint8_t)KeyFunction::HotKey3; case AnytoneKeySettingsExtension::KeyFunction::HotKey4: return (uint8_t)KeyFunction::HotKey4; case AnytoneKeySettingsExtension::KeyFunction::HotKey5: return (uint8_t)KeyFunction::HotKey5; case AnytoneKeySettingsExtension::KeyFunction::HotKey6: return (uint8_t)KeyFunction::HotKey6; case AnytoneKeySettingsExtension::KeyFunction::WorkAlone: return (uint8_t)KeyFunction::WorkAlone; case AnytoneKeySettingsExtension::KeyFunction::SkipChannel: return (uint8_t)KeyFunction::SkipChannel; case AnytoneKeySettingsExtension::KeyFunction::DMRMonitor: return (uint8_t)KeyFunction::DMRMonitor; case AnytoneKeySettingsExtension::KeyFunction::SubChannel: return (uint8_t)KeyFunction::SubChannel; case AnytoneKeySettingsExtension::KeyFunction::PriorityZone: return (uint8_t)KeyFunction::PriorityZone; case AnytoneKeySettingsExtension::KeyFunction::VFOScan: return (uint8_t)KeyFunction::VFOScan; case AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality: return (uint8_t)KeyFunction::MICSoundQuality; case AnytoneKeySettingsExtension::KeyFunction::LastCallReply: return (uint8_t)KeyFunction::LastCallReply; case AnytoneKeySettingsExtension::KeyFunction::ChannelType: return (uint8_t)KeyFunction::ChannelType; case AnytoneKeySettingsExtension::KeyFunction::Ranging: return (uint8_t)KeyFunction::Ranging; case AnytoneKeySettingsExtension::KeyFunction::ChannelRanging: return (uint8_t)KeyFunction::ChannelRanging; case AnytoneKeySettingsExtension::KeyFunction::MaxVolume: return (uint8_t)KeyFunction::MaxVolume; case AnytoneKeySettingsExtension::KeyFunction::Slot: return (uint8_t)KeyFunction::Slot; default: return (uint8_t)KeyFunction::Off; } } AnytoneKeySettingsExtension::KeyFunction D868UVCodeplug::GeneralSettingsElement::KeyFunction::decode(uint8_t code) { switch ((KeyFunction::KeyFunctionCode)code) { case KeyFunction::Off: return AnytoneKeySettingsExtension::KeyFunction::Off; case KeyFunction::Voltage: return AnytoneKeySettingsExtension::KeyFunction::Voltage; case KeyFunction::Power: return AnytoneKeySettingsExtension::KeyFunction::Power; case KeyFunction::Repeater: return AnytoneKeySettingsExtension::KeyFunction::Repeater; case KeyFunction::Reverse: return AnytoneKeySettingsExtension::KeyFunction::Reverse; case KeyFunction::Encryption: return AnytoneKeySettingsExtension::KeyFunction::Encryption; case KeyFunction::Call: return AnytoneKeySettingsExtension::KeyFunction::Call; case KeyFunction::VOX: return AnytoneKeySettingsExtension::KeyFunction::VOX; case KeyFunction::ToggleVFO: return AnytoneKeySettingsExtension::KeyFunction::ToggleVFO; case KeyFunction::SubPTT: return AnytoneKeySettingsExtension::KeyFunction::SubPTT; case KeyFunction::Scan: return AnytoneKeySettingsExtension::KeyFunction::Scan; case KeyFunction::WFM: return AnytoneKeySettingsExtension::KeyFunction::WFM; case KeyFunction::Alarm: return AnytoneKeySettingsExtension::KeyFunction::Alarm; case KeyFunction::RecordSwitch: return AnytoneKeySettingsExtension::KeyFunction::RecordSwitch; case KeyFunction::Record: return AnytoneKeySettingsExtension::KeyFunction::Record; case KeyFunction::SMS: return AnytoneKeySettingsExtension::KeyFunction::SMS; case KeyFunction::Dial: return AnytoneKeySettingsExtension::KeyFunction::Dial; case KeyFunction::GPSInformation: return AnytoneKeySettingsExtension::KeyFunction::GPSInformation; case KeyFunction::Monitor: return AnytoneKeySettingsExtension::KeyFunction::Monitor; case KeyFunction::ToggleMainChannel: return AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel; case KeyFunction::HotKey1: return AnytoneKeySettingsExtension::KeyFunction::HotKey1; case KeyFunction::HotKey2: return AnytoneKeySettingsExtension::KeyFunction::HotKey2; case KeyFunction::HotKey3: return AnytoneKeySettingsExtension::KeyFunction::HotKey3; case KeyFunction::HotKey4: return AnytoneKeySettingsExtension::KeyFunction::HotKey4; case KeyFunction::HotKey5: return AnytoneKeySettingsExtension::KeyFunction::HotKey5; case KeyFunction::HotKey6: return AnytoneKeySettingsExtension::KeyFunction::HotKey6; case KeyFunction::WorkAlone: return AnytoneKeySettingsExtension::KeyFunction::WorkAlone; case KeyFunction::SkipChannel: return AnytoneKeySettingsExtension::KeyFunction::SkipChannel; case KeyFunction::DMRMonitor: return AnytoneKeySettingsExtension::KeyFunction::DMRMonitor; case KeyFunction::SubChannel: return AnytoneKeySettingsExtension::KeyFunction::SubChannel; case KeyFunction::PriorityZone: return AnytoneKeySettingsExtension::KeyFunction::PriorityZone; case KeyFunction::VFOScan: return AnytoneKeySettingsExtension::KeyFunction::VFOScan; case KeyFunction::MICSoundQuality: return AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality; case KeyFunction::LastCallReply: return AnytoneKeySettingsExtension::KeyFunction::LastCallReply; case KeyFunction::ChannelType: return AnytoneKeySettingsExtension::KeyFunction::ChannelType; case KeyFunction::Ranging: return AnytoneKeySettingsExtension::KeyFunction::Ranging; case KeyFunction::ChannelRanging: return AnytoneKeySettingsExtension::KeyFunction::ChannelRanging; case KeyFunction::MaxVolume: return AnytoneKeySettingsExtension::KeyFunction::MaxVolume; case KeyFunction::Slot: return AnytoneKeySettingsExtension::KeyFunction::Slot; default: return AnytoneKeySettingsExtension::KeyFunction::Off; } } /* ******************************************************************************************** * * Implementation of D868UVCodeplug::GeneralSettingsElement * ******************************************************************************************** */ D868UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size) : AnytoneCodeplug::GeneralSettingsElement(ptr, size) { // pass.... } D868UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr) : D868UVCodeplug::GeneralSettingsElement(ptr, GeneralSettingsElement::size()) { // pass... } void D868UVCodeplug::GeneralSettingsElement::clear() { AnytoneCodeplug::GeneralSettingsElement::clear(); } bool D868UVCodeplug::GeneralSettingsElement::keyToneEnabled() const { return 0x00 != getUInt8(Offset::enableKeyTone()); } void D868UVCodeplug::GeneralSettingsElement::enableKeyTone(bool enable) { setUInt8(Offset::enableKeyTone(), enable ? 0x01 : 0x00); } AnytonePowerSaveSettingsExtension::PowerSave D868UVCodeplug::GeneralSettingsElement::powerSave() const { return (AnytonePowerSaveSettingsExtension::PowerSave)getUInt8(Offset::powerSaveMode()); } void D868UVCodeplug::GeneralSettingsElement::setPowerSave(AnytonePowerSaveSettingsExtension::PowerSave mode) { setUInt8(Offset::powerSaveMode(), (unsigned int)mode); } unsigned D868UVCodeplug::GeneralSettingsElement::voxLevel() const { return ((unsigned)getUInt8(Offset::voxLevel()))*3; } void D868UVCodeplug::GeneralSettingsElement::setVOXLevel(unsigned level) { setUInt8(Offset::voxLevel(), level/3); } Interval D868UVCodeplug::GeneralSettingsElement::voxDelay() const { return Interval::fromMilliseconds(100 + 500*((unsigned)getUInt8(Offset::voxDelay()))); } void D868UVCodeplug::GeneralSettingsElement::setVOXDelay(Interval intv) { setUInt8(Offset::voxDelay(), (std::max(100ULL, intv.milliseconds())-100)/500); } AnytoneSettingsExtension::VFOScanType D868UVCodeplug::GeneralSettingsElement::vfoScanType() const { return (AnytoneSettingsExtension::VFOScanType) getUInt8(Offset::vfoScanType()); } void D868UVCodeplug::GeneralSettingsElement::setVFOScanType(AnytoneSettingsExtension::VFOScanType type) { setUInt8(Offset::vfoScanType(), (unsigned)type); } unsigned D868UVCodeplug::GeneralSettingsElement::dmrMicGain() const { return (((unsigned)getUInt8(Offset::dmrMicGain())+1)*10)/4; } void D868UVCodeplug::GeneralSettingsElement::setDMRMicGain(unsigned gain) { gain = std::max(1U, std::min(10U, gain)); setUInt8(Offset::dmrMicGain(), (gain*4)/10); } AnytoneKeySettingsExtension::KeyFunction D868UVCodeplug::GeneralSettingsElement::funcKeyAShort() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyAShort())); } void D868UVCodeplug::GeneralSettingsElement::setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyAShort(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D868UVCodeplug::GeneralSettingsElement::funcKeyBShort() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyBShort())); } void D868UVCodeplug::GeneralSettingsElement::setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyBShort(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D868UVCodeplug::GeneralSettingsElement::funcKeyCShort() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyCShort())); } void D868UVCodeplug::GeneralSettingsElement::setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyCShort(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D868UVCodeplug::GeneralSettingsElement::funcKey1Short() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey1Short())); } void D868UVCodeplug::GeneralSettingsElement::setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKey1Short(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D868UVCodeplug::GeneralSettingsElement::funcKey2Short() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey2Short())); } void D868UVCodeplug::GeneralSettingsElement::setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKey2Short(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D868UVCodeplug::GeneralSettingsElement::funcKeyALong() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyALong())); } void D868UVCodeplug::GeneralSettingsElement::setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyALong(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D868UVCodeplug::GeneralSettingsElement::funcKeyBLong() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyBLong())); } void D868UVCodeplug::GeneralSettingsElement::setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyBLong(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D868UVCodeplug::GeneralSettingsElement::funcKeyCLong() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyCLong())); } void D868UVCodeplug::GeneralSettingsElement::setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyCLong(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D868UVCodeplug::GeneralSettingsElement::funcKey1Long() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey1Long())); } void D868UVCodeplug::GeneralSettingsElement::setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKey1Long(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D868UVCodeplug::GeneralSettingsElement::funcKey2Long() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey2Long())); } void D868UVCodeplug::GeneralSettingsElement::setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKey2Long(), KeyFunction::encode(func)); } Interval D868UVCodeplug::GeneralSettingsElement::longPressDuration() const { return Interval::fromSeconds(((unsigned)getUInt8(Offset::longPressDuration()))+1); } void D868UVCodeplug::GeneralSettingsElement::setLongPressDuration(Interval ms) { setUInt8(Offset::longPressDuration(), std::max(1ULL,ms.seconds())-1); } bool D868UVCodeplug::GeneralSettingsElement::vfoModeA() const { return getUInt8(Offset::vfoModeA()); } void D868UVCodeplug::GeneralSettingsElement::enableVFOModeA(bool enable) { setUInt8(Offset::vfoModeA(), (enable ? 0x01 : 0x00)); } bool D868UVCodeplug::GeneralSettingsElement::vfoModeB() const { return getUInt8(Offset::vfoModeB()); } void D868UVCodeplug::GeneralSettingsElement::enableVFOModeB(bool enable) { setUInt8(Offset::vfoModeB(), (enable ? 0x01 : 0x00)); } unsigned D868UVCodeplug::GeneralSettingsElement::memoryZoneA() const { return getUInt8(Offset::memZoneA()); } void D868UVCodeplug::GeneralSettingsElement::setMemoryZoneA(unsigned zone) { setUInt8(Offset::memZoneA(), zone); } unsigned D868UVCodeplug::GeneralSettingsElement::memoryZoneB() const { return getUInt8(Offset::memZoneB()); } void D868UVCodeplug::GeneralSettingsElement::setMemoryZoneB(unsigned zone) { setUInt8(Offset::memZoneB(), zone); } bool D868UVCodeplug::GeneralSettingsElement::recording() const { return getUInt8(Offset::enableRecoding()); } void D868UVCodeplug::GeneralSettingsElement::enableRecording(bool enable) { setUInt8(Offset::enableRecoding(), (enable ? 0x01 : 0x00)); } unsigned D868UVCodeplug::GeneralSettingsElement::brightness() const { return (getUInt8(Offset::displayBrightness())*10)/4; } void D868UVCodeplug::GeneralSettingsElement::setBrightness(unsigned level) { setUInt8(Offset::displayBrightness(), (level*4)/10); } bool D868UVCodeplug::GeneralSettingsElement::backlightPermanent() const { return rxBacklightDuration().isNull(); } Interval D868UVCodeplug::GeneralSettingsElement::rxBacklightDuration() const { return Interval::fromSeconds(5*((unsigned)getUInt8(Offset::backlightDuration()))); } void D868UVCodeplug::GeneralSettingsElement::setRXBacklightDuration(Interval intv) { setUInt8(Offset::backlightDuration(), intv.seconds()/5); } void D868UVCodeplug::GeneralSettingsElement::enableBacklightPermanent() { setRXBacklightDuration(Interval()); } bool D868UVCodeplug::GeneralSettingsElement::gps() const { return getUInt8(Offset::gpsEnable()); } void D868UVCodeplug::GeneralSettingsElement::enableGPS(bool enable) { setUInt8(Offset::gpsEnable(), (enable ? 0x01 : 0x00)); } bool D868UVCodeplug::GeneralSettingsElement::smsAlert() const { return getUInt8(Offset::smsAlert()); } void D868UVCodeplug::GeneralSettingsElement::enableSMSAlert(bool enable) { setUInt8(Offset::smsAlert(), (enable ? 0x01 : 0x00)); } bool D868UVCodeplug::GeneralSettingsElement::activeChannelB() const { return getUInt8(Offset::activeChannelB()); } void D868UVCodeplug::GeneralSettingsElement::enableActiveChannelB(bool enable) { setUInt8(Offset::activeChannelB(), (enable ? 0x01 : 0x00)); } bool D868UVCodeplug::GeneralSettingsElement::subChannel() const { return getUInt8(Offset::subChannel()); } void D868UVCodeplug::GeneralSettingsElement::enableSubChannel(bool enable) { setUInt8(Offset::subChannel(), (enable ? 0x01 : 0x00)); } bool D868UVCodeplug::GeneralSettingsElement::callAlert() const { return getUInt8(Offset::callAlert()); } void D868UVCodeplug::GeneralSettingsElement::enableCallAlert(bool enable) { setUInt8(Offset::callAlert(), (enable ? 0x01 : 0x00)); } QTimeZone D868UVCodeplug::GeneralSettingsElement::gpsTimeZone() const { return QTimeZone((((int)getUInt8(Offset::gpsTimeZone()))-12)*3600); } void D868UVCodeplug::GeneralSettingsElement::setGPSTimeZone(const QTimeZone &zone) { int offset = zone.offsetFromUtc(QDateTime::currentDateTime()); setUInt8(Offset::gpsTimeZone(), (12 + offset/3600)); } bool D868UVCodeplug::GeneralSettingsElement::dmrTalkPermit() const { return getBit(Offset::talkPermit(), 0); } void D868UVCodeplug::GeneralSettingsElement::enableDMRTalkPermit(bool enable) { return setBit(Offset::talkPermit(), 0, enable); } bool D868UVCodeplug::GeneralSettingsElement::fmTalkPermit() const { return getBit(Offset::talkPermit(), 1); } void D868UVCodeplug::GeneralSettingsElement::enableFMTalkPermit(bool enable) { return setBit(Offset::talkPermit(), 1, enable); } bool D868UVCodeplug::GeneralSettingsElement::dmrResetTone() const { return getUInt8(Offset::dmrResetTone()); } void D868UVCodeplug::GeneralSettingsElement::enableDMRResetTone(bool enable) { return setUInt8(Offset::dmrResetTone(), (enable ? 0x01 : 0x00)); } AnytoneAudioSettingsExtension::VoxSource D868UVCodeplug::GeneralSettingsElement::voxSource() const { return (AnytoneAudioSettingsExtension::VoxSource)getUInt8(Offset::voxSource()); } void D868UVCodeplug::GeneralSettingsElement::setVOXSource(AnytoneAudioSettingsExtension::VoxSource source) { setUInt8(Offset::voxSource(), (unsigned)source); } bool D868UVCodeplug::GeneralSettingsElement::idleChannelTone() const { return getUInt8(Offset::idleChannelTone()); } void D868UVCodeplug::GeneralSettingsElement::enableIdleChannelTone(bool enable) { return setUInt8(Offset::idleChannelTone(), (enable ? 0x01 : 0x00)); } Interval D868UVCodeplug::GeneralSettingsElement::menuExitTime() const { return Interval::fromSeconds(5 + 5*((unsigned) getUInt8(Offset::menuExitTime()))); } void D868UVCodeplug::GeneralSettingsElement::setMenuExitTime(Interval intv) { setUInt8(Offset::menuExitTime(), (std::max(5ULL, intv.seconds())-5)/5); } bool D868UVCodeplug::GeneralSettingsElement::startupTone() const { return getUInt8(Offset::startupTone()); } void D868UVCodeplug::GeneralSettingsElement::enableStartupTone(bool enable) { return setUInt8(Offset::startupTone(), (enable ? 0x01 : 0x00)); } bool D868UVCodeplug::GeneralSettingsElement::callEndPrompt() const { return getUInt8(Offset::callEndPrompt()); } void D868UVCodeplug::GeneralSettingsElement::enableCallEndPrompt(bool enable) { return setUInt8(Offset::callEndPrompt(), (enable ? 0x01 : 0x00)); } unsigned D868UVCodeplug::GeneralSettingsElement::maxSpeakerVolume() const { return (((unsigned)getUInt8(Offset::maxSpeakerVolume()))*10)/8; } void D868UVCodeplug::GeneralSettingsElement::setMaxSpeakerVolume(unsigned level) { setUInt8(Offset::maxSpeakerVolume(), (level*8)/10); } bool D868UVCodeplug::GeneralSettingsElement::getGPSPosition() const { return getUInt8(Offset::getGPSPosition()); } void D868UVCodeplug::GeneralSettingsElement::enableGetGPSPosition(bool enable) { return setUInt8(Offset::getGPSPosition(), (enable ? 0x01 : 0x00)); } bool D868UVCodeplug::GeneralSettingsElement::volumeChangePrompt() const { return getUInt8(Offset::volumeChangePrompt()); } void D868UVCodeplug::GeneralSettingsElement::enableVolumeChangePrompt(bool enable) { setUInt8(Offset::volumeChangePrompt(), (enable ? 0x01 : 0x01)); } AnytoneAutoRepeaterSettingsExtension::Direction D868UVCodeplug::GeneralSettingsElement::autoRepeaterDirectionA() const { return (AnytoneAutoRepeaterSettingsExtension::Direction) getUInt8(Offset::autoRepeaterDirA()); } void D868UVCodeplug::GeneralSettingsElement::setAutoRepeaterDirectionA(AnytoneAutoRepeaterSettingsExtension::Direction dir) { setUInt8(Offset::autoRepeaterDirA(), (unsigned)dir); } AnytoneDisplaySettingsExtension::LastCallerDisplayMode D868UVCodeplug::GeneralSettingsElement::lastCallerDisplayMode() const { return (AnytoneDisplaySettingsExtension::LastCallerDisplayMode)getUInt8(Offset::lastCallerDisplay()); } void D868UVCodeplug::GeneralSettingsElement::setLastCallerDisplayMode(AnytoneDisplaySettingsExtension::LastCallerDisplayMode mode) { setUInt8(Offset::lastCallerDisplay(), (unsigned)mode); } bool D868UVCodeplug::GeneralSettingsElement::displayClock() const { return getUInt8(Offset::showClock()); } void D868UVCodeplug::GeneralSettingsElement::enableDisplayClock(bool enable) { setUInt8(Offset::showClock(), (enable ? 0x01 : 0x00)); } unsigned D868UVCodeplug::GeneralSettingsElement::maxHeadPhoneVolume() const { return (((unsigned)getUInt8(Offset::maxHeadPhoneVolume()))*10)/8; } void D868UVCodeplug::GeneralSettingsElement::setMaxHeadPhoneVolume(unsigned max) { setUInt8(Offset::maxHeadPhoneVolume(), (max*8)/10); } bool D868UVCodeplug::GeneralSettingsElement::enhanceAudio() const { return getUInt8(Offset::enhanceAudio()); } void D868UVCodeplug::GeneralSettingsElement::enableEnhancedAudio(bool enable) { setUInt8(Offset::enhanceAudio(), (enable ? 0x01 : 0x00)); } Frequency D868UVCodeplug::GeneralSettingsElement::minVFOScanFrequencyUHF() const { return Frequency::fromHz(((unsigned)getUInt32_le(Offset::minVFOScanUHF()))*10); } void D868UVCodeplug::GeneralSettingsElement::setMinVFOScanFrequencyUHF(Frequency freq) { setUInt32_le(Offset::minVFOScanUHF(), freq.inHz()/10); } Frequency D868UVCodeplug::GeneralSettingsElement::maxVFOScanFrequencyUHF() const { return Frequency::fromHz(((unsigned)getUInt32_le(Offset::maxVFOScanUHF()))*10); } void D868UVCodeplug::GeneralSettingsElement::setMaxVFOScanFrequencyUHF(Frequency freq) { setUInt32_le(Offset::maxVFOScanUHF(), freq.inHz()/10); } Frequency D868UVCodeplug::GeneralSettingsElement::minVFOScanFrequencyVHF() const { return Frequency::fromHz(((unsigned)getUInt32_le(Offset::minVFOScanVHF()))*10); } void D868UVCodeplug::GeneralSettingsElement::setMinVFOScanFrequencyVHF(Frequency freq) { setUInt32_le(Offset::minVFOScanVHF(), freq.inHz()/10); } Frequency D868UVCodeplug::GeneralSettingsElement::maxVFOScanFrequencyVHF() const { return Frequency::fromHz(((unsigned)getUInt32_le(Offset::maxVFOScanVHF()))*10); } void D868UVCodeplug::GeneralSettingsElement::setMaxVFOScanFrequencyVHF(Frequency freq) { setUInt32_le(Offset::maxVFOScanVHF(), freq.inHz()/10); } bool D868UVCodeplug::GeneralSettingsElement::hasAutoRepeaterOffsetFrequencyIndexUHF() const { return 0xff != autoRepeaterOffsetFrequencyIndexUHF(); } unsigned D868UVCodeplug::GeneralSettingsElement::autoRepeaterOffsetFrequencyIndexUHF() const { return getUInt8(Offset::autoRepOffsetUHF()); } void D868UVCodeplug::GeneralSettingsElement::setAutoRepeaterOffsetFrequenyIndexUHF(unsigned idx) { setUInt8(Offset::autoRepOffsetUHF(), idx); } void D868UVCodeplug::GeneralSettingsElement::clearAutoRepeaterOffsetFrequencyIndexUHF() { setAutoRepeaterOffsetFrequenyIndexUHF(0xff); } bool D868UVCodeplug::GeneralSettingsElement::hasAutoRepeaterOffsetFrequencyIndexVHF() const { return 0xff != autoRepeaterOffsetFrequencyIndexVHF(); } unsigned D868UVCodeplug::GeneralSettingsElement::autoRepeaterOffsetFrequencyIndexVHF() const { return getUInt8(Offset::autoRepOffsetVHF()); } void D868UVCodeplug::GeneralSettingsElement::setAutoRepeaterOffsetFrequenyIndexVHF(unsigned idx) { setUInt8(Offset::autoRepOffsetVHF(), idx); } void D868UVCodeplug::GeneralSettingsElement::clearAutoRepeaterOffsetFrequencyIndexVHF() { setAutoRepeaterOffsetFrequenyIndexVHF(0xff); } void D868UVCodeplug::GeneralSettingsElement::callToneMelody(Melody &melody) const { QVector> tones; tones.reserve(5); for (int i=0; i<5; i++) { double freq = getUInt16_le(Offset::callToneTones()+2*i); unsigned int duration = getUInt16_le(Offset::callToneDurations()+2*i); if (duration) tones.append({freq, duration}); } melody.infer(tones); } void D868UVCodeplug::GeneralSettingsElement::setCallToneMelody(const Melody &melody) { unsigned int n=std::min(5U, (unsigned int)melody.count()); QVector> tones = melody.toTones(); for (unsigned int i=0; i> tones; tones.reserve(5); for (int i=0; i<5; i++) { double frequency = getUInt16_le(Offset::idleToneTones()+2*i); unsigned int duration = getUInt16_le(Offset::idleToneDurations()+2*i); if (duration) tones.append({frequency, duration}); } melody.infer(tones); } void D868UVCodeplug::GeneralSettingsElement::setIdleToneMelody(const Melody &melody) { unsigned int n=std::min(5U, (unsigned int)melody.count()); QVector> tones = melody.toTones(); for (unsigned int i=0; i> tones; tones.reserve(5); for (int i=0; i<5; i++) { double frequency = getUInt16_le(Offset::resetToneTones()+2*i); unsigned int duration = getUInt16_le(Offset::resetToneDurations()+2*i); if (duration) tones.append({frequency, duration}); } melody.infer(tones); } void D868UVCodeplug::GeneralSettingsElement::setResetToneMelody(const Melody &melody) { unsigned int n=std::min(5U, (unsigned int)melody.count()); QVector> tones = melody.toTones(); for (unsigned int i=0; isettings()->vox()); AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); if (nullptr == ext) return true; // Power save settings setPowerSave(ext->powerSaveSettings()->powerSave()); // Encode key settings enableKnobLock(ext->keySettings()->knobLockEnabled()); enableKeypadLock(ext->keySettings()->keypadLockEnabled()); enableSidekeysLock(ext->keySettings()->sideKeysLockEnabled()); enableKeyLockForced(ext->keySettings()->forcedKeyLockEnabled()); // Encode tone settings setKeyToneLevel(ext->toneSettings()->keyToneLevel()); // Encode audio settings setVOXDelay(ext->audioSettings()->voxDelay()); setVOXSource(ext->audioSettings()->voxSource()); setMaxHeadPhoneVolume(ext->audioSettings()->maxHeadPhoneVolume()); // Encode display settings setRXBacklightDuration(ext->displaySettings()->backlightDuration()); enableShowCurrentContact(ext->displaySettings()->showContact()); return true; } bool D868UVCodeplug::GeneralSettingsElement::updateConfig(Context &ctx) { if (! AnytoneCodeplug::GeneralSettingsElement::updateConfig(ctx)) return false; ctx.config()->settings()->setVOX(voxLevel()); // Get or add settings extension AnytoneSettingsExtension *ext = nullptr; if (ctx.config()->settings()->anytoneExtension()) { ext = ctx.config()->settings()->anytoneExtension(); } else { ext = new AnytoneSettingsExtension(); ctx.config()->settings()->setAnytoneExtension(ext); } // Decode power save settings: ext->powerSaveSettings()->setPowerSave(powerSave()); // Decode key settings ext->keySettings()->enableKnobLock(this->knobLock()); ext->keySettings()->enableKeypadLock(this->keypadLock()); ext->keySettings()->enableSideKeysLock(this->sidekeysLock()); ext->keySettings()->enableForcedKeyLock(this->keyLockForced()); // Decode tone settings ext->toneSettings()->setKeyToneLevel(keyToneLevel()); // Decode audio settings ext->audioSettings()->setVOXDelay(voxDelay()); ext->audioSettings()->setVOXSource(voxSource()); ext->audioSettings()->setMaxHeadPhoneVolume(this->maxHeadPhoneVolume()); // Decode display settings ext->displaySettings()->enableShowContact(this->showCurrentContact()); ext->displaySettings()->setBacklightDuration(rxBacklightDuration()); return true; } bool D868UVCodeplug::GeneralSettingsElement::linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err) { if (! AnytoneCodeplug::GeneralSettingsElement::linkSettings(settings, ctx, err)) return false; // Get or add settings extension AnytoneSettingsExtension *ext = settings->anytoneExtension(); if (nullptr == ext) { ext = new AnytoneSettingsExtension(); settings->setAnytoneExtension(ext); } // Nothing to link return true; } /* ******************************************************************************************** * * Implementation of D868UVCodeplug * ******************************************************************************************** */ D868UVCodeplug::D868UVCodeplug(const QString &label, QObject *parent) : AnytoneCodeplug(label, parent) { // pass... } D868UVCodeplug::D868UVCodeplug(QObject *parent) : AnytoneCodeplug("AnyTone AT-D868UV Codeplug", parent) { // pass... } void D868UVCodeplug::allocateUpdated() { this->allocateVFOSettings(); // General config this->allocateGeneralSettings(); this->allocateZoneChannelList(); this->allocateDTMFNumbers(); this->allocateBootSettings(); this->allocateRepeaterOffsetFrequencies(); this->allocateGPSSystems(); this->allocateHotKeySettings(); this->allocateAlarmSettings(); this->allocateFMBroadcastSettings(); this->allocate5ToneIDs(); this->allocate5ToneFunctions(); this->allocate5ToneSettings(); this->allocate2ToneIDs(); this->allocate2ToneFunctions(); this->allocate2ToneSettings(); this->allocateDTMFSettings(); image(0).addElement(Offset::dmrEncryptionIDs(), DMREncryptionKeyIDListElement::size()); image(0).addElement(Offset::dmrEncryptionKeys(), DMREncryptionKeyListElement::size()); } void D868UVCodeplug::allocateForEncoding() { this->allocateChannels(); this->allocateZones(); this->allocateContacts(); this->allocateAnalogContacts(); this->allocateRXGroupLists(); this->allocateScanLists(); this->allocateRadioIDs(); this->allocateSMSMessages(); } void D868UVCodeplug::allocateForDecoding() { this->allocateRadioIDs(); this->allocateChannels(); this->allocateZones(); this->allocateContacts(); this->allocateAnalogContacts(); this->allocateRXGroupLists(); this->allocateScanLists(); // General config this->allocateGeneralSettings(); this->allocateZoneChannelList(); this->allocateBootSettings(); this->allocateRepeaterOffsetFrequencies(); this->allocateSMSMessages(); // GPS settings this->allocateGPSSystems(); } bool D868UVCodeplug::allocateBitmaps() { // Channel bitmap image(0).addElement(Offset::channelBitmap(), ChannelBitmapElement::size()); // Zone bitmap image(0).addElement(Offset::zoneBitmap(), ZoneBitmapElement::size()); // Contacts bitmap image(0).addElement(Offset::contactBitmap(), ContactBitmapElement::size()); // Analog contacts bytemap image(0).addElement(Offset::dtmfContactBytemap(), DTMFContactBytemapElement::size()); // RX group list bitmaps image(0).addElement(Offset::groupListBitmap(), GroupListBitmapElement::size()); // Scan list bitmaps image(0).addElement(Offset::scanListBitmap(), ScanListBitmapElement::size()); // Radio IDs bitmaps image(0).addElement(Offset::radioIDBitmap(), RadioIDBitmapElement::size()); // Message bitmaps image(0).addElement(Offset::messageBytemap(), MessageBytemapElement::size()); // Status messages image(0).addElement(Offset::statusMessageBitmap(), StatusMessageBitmapElement::size()); // FM Broadcast bitmaps image(0).addElement(Offset::wfmChannelBitmap(), WFMChannelBitmapElement::size()); // 5-Tone function bitmaps image(0).addElement(Offset::fiveToneIdBitmap(), FiveToneIDBitmapElement::size()); // 2-Tone function bitmaps image(0).addElement(Offset::twoToneIdBitmap(), TwoToneIDBitmapElement::size()); image(0).addElement(Offset::twoToneFunctionBitmap(), TwoToneFunctionBitmapElement::size()); return true; } void D868UVCodeplug::setBitmaps(Context& ctx) { // Mark first radio ID as valid RadioIDBitmapElement radioid_bitmap(data(Offset::radioIDBitmap())); unsigned int num_radio_ids = std::min(Limit::numRadioIDs(), ctx.count()); radioid_bitmap.clear(); radioid_bitmap.enableFirst(num_radio_ids); // Mark valid channels (set bit) ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap())); unsigned int num_channels = std::min(Limit::numChannels(), ctx.count()); channel_bitmap.clear(); channel_bitmap.enableFirst(num_channels); // Mark valid contacts (clear bit) ContactBitmapElement contact_bitmap(data(Offset::contactBitmap())); unsigned int num_contacts = std::min(Limit::numContacts(), ctx.count()); contact_bitmap.clear(); contact_bitmap.enableFirst(num_contacts); // Mark valid analog contacts (clear bytes) DTMFContactBytemapElement analog_contact_bitmap(data(Offset::dtmfContactBytemap())); unsigned int num_dtmf_contacts = std::min(Limit::numDTMFContacts(), ctx.count()); analog_contact_bitmap.clear(); analog_contact_bitmap.enableFirst(num_dtmf_contacts); // Mark valid zones (set bits) ZoneBitmapElement zone_bitmap(data(Offset::zoneBitmap())); unsigned int num_zones = std::min(Limit::numZones(), ctx.count()); zone_bitmap.clear(); zone_bitmap.enableFirst(num_zones); // Mark group lists GroupListBitmapElement group_bitmap(data(Offset::groupListBitmap())); unsigned int num_group_lists = std::min(Limit::numGroupLists(), ctx.count()); group_bitmap.clear(); group_bitmap.enableFirst(num_group_lists); // Mark scan lists ScanListBitmapElement scan_bitmap(data(Offset::scanListBitmap())); unsigned int num_scan_lists = std::min(Limit::numScanLists(), ctx.count()); scan_bitmap.clear(); scan_bitmap.enableFirst(num_scan_lists); // Mark SMS messages MessageBytemapElement sms_bytemap(data(Offset::messageBytemap())); unsigned int num_messages = std::min(Limit::numMessages(), ctx.count()); sms_bytemap.clear(); sms_bytemap.enableFirst(num_messages); } bool D868UVCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) { if (! this->encodeRadioID(flags, ctx, err)) return false; if (! this->encodeGeneralSettings(flags, ctx, err)) return false; if (! this->encodeSMSMessages(flags, ctx, err)) return false; if (! this->encodeRepeaterOffsetFrequencies(flags, ctx, err)) return false; if (! this->encodeBootSettings(flags, ctx, err)) return false; if (! this->encodeChannels(flags, ctx, err)) return false; if (! this->encodeContacts(flags, ctx, err)) return false; if (! this->encodeAnalogContacts(flags, ctx, err)) return false; if (! this->encodeRXGroupLists(flags, ctx, err)) return false; if (! this->encodeZones(flags, ctx, err)) return false; if (! this->encodeScanLists(flags, ctx, err)) return false; if (! this->encodeGPSSystems(flags, ctx, err)) return false; return true; } bool D868UVCodeplug::decodeElements(Context &ctx, const ErrorStack &err) { if (! this->setRadioID(ctx, err)) return false; if (! this->decodeGeneralSettings(ctx, err)) return false; if (! this->createSMSMessages(ctx, err)) return false; if (! this->decodeRepeaterOffsetFrequencies(ctx, err)) return false; if (! this->decodeBootSettings(ctx, err)) return false; if (! this->createChannels(ctx, err)) return false; if (! this->createContacts(ctx, err)) return false; if (! this->createAnalogContacts(ctx, err)) return false; if (! this->createRXGroupLists(ctx, err)) return false; if (! this->linkRXGroupLists(ctx, err)) return false; if (! this->createZones(ctx, err)) return false; if (! this->linkZones(ctx, err)) return false; if (! this->createScanLists(ctx, err)) return false; if (! this->linkScanLists(ctx, err)) return false; if (! this->createGPSSystems(ctx, err)) return false; if (! this->linkChannels(ctx, err)) return false; if (! this->linkGPSSystems(ctx, err)) return false; if (! this->linkGeneralSettings(ctx, err)) { return false; } return true; } void D868UVCodeplug::allocateChannels() { /* Allocate channels */ ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap())); for (uint16_t i=0; i skip if (! channel_bitmap.isEncoded(i)) continue; // compute address for channel uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank(); uint32_t addr = Offset::channelBanks() + + bank * Offset::betweenChannelBanks() + idx * ChannelElement::size(); if (!isAllocated(addr, 0)) { image(0).addElement(addr, ChannelElement::size()); } } } bool D868UVCodeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err) // Encode channels for (int i=0; ichannelList()->count(); i++) { // enable channel uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank(); ChannelElement ch(data(Offset::channelBanks() + bank * Offset::betweenChannelBanks() + idx * ChannelElement::size())); if (! ch.fromChannelObj(ctx.config()->channelList()->channel(i), ctx)) return false; } return true; } bool D868UVCodeplug::createChannels(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap())); // Create channels for (uint16_t i=0; ichannelList()->add(obj); ctx.add(obj, i); } } return true; } bool D868UVCodeplug::linkChannels(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap())); // Link channel objects for (uint16_t i=0; i(i)) ch.linkChannelObj(ctx.get(i), ctx); } return true; } void D868UVCodeplug::allocateVFOSettings() { // Allocate VFO channels image(0).addElement(Offset::vfoA(), ChannelElement::size()); image(0).addElement(Offset::vfoA()+0x2000, ChannelElement::size()); image(0).addElement(Offset::vfoB(), ChannelElement::size()); image(0).addElement(Offset::vfoB()+0x2000, ChannelElement::size()); } void D868UVCodeplug::allocateContacts() { /* Allocate contacts */ ContactBitmapElement contact_bitmap(data(Offset::contactBitmap())); unsigned contactCount=0; for (uint16_t i=0; i contacts; // Encode contacts and also collect id<->index map for (int i=0; icontacts()->digitalCount(); i++) { uint32_t bank_addr = Offset::contactBanks() + (i/Limit::contactsPerBank())*Offset::betweenContactBanks(); uint32_t addr = bank_addr + (i%Limit::contactsPerBank())*ContactElement::size(); ContactElement con(data(addr)); DMRContact *contact = ctx.config()->contacts()->digitalContact(i); if(! con.fromContactObj(contact, ctx)) return false; ((uint32_t *)data(Offset::contactIndex()))[i] = qToLittleEndian(i); contacts.append(contact); } // encode index map for contacts std::sort(contacts.begin(), contacts.end(), [](DMRContact *a, DMRContact *b) { return a->number() < b->number(); }); for (int i=0; inumber(), (DMRContact::GroupCall==contacts[i]->type())); el.setIndex(ctx.index(contacts[i])); } return true; } bool D868UVCodeplug::createContacts(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Create digital contacts ContactBitmapElement contact_bitmap(data(Offset::contactBitmap())); for (uint16_t i=0; icontacts()->add(obj); ctx.add(obj, i); } } return true; } void D868UVCodeplug::allocateAnalogContacts() { /* Allocate analog contacts */ DTMFContactBytemapElement analog_contact_bytemap(data(Offset::dtmfContactBytemap())); for (uint8_t i=0; i skip if (! analog_contact_bytemap.isEncoded(i)) continue; uint32_t bank_addr = Offset::dtmfContacts() + (i/2)*(2*DTMFContactElement::size()); if (! isAllocated(bank_addr, 0)) { image(0).addElement(bank_addr, 2*DTMFContactElement::size()); } } image(0).addElement(Offset::dtmfIndex(), 1*Limit::numDTMFContacts()); } bool D868UVCodeplug::encodeAnalogContacts(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err) uint8_t *idxlst = data(Offset::dtmfIndex()); memset(idxlst, 0xff, 1*Limit::numDTMFContacts()); for (unsigned int i=0; i(); i++) { DTMFContactElement cont(data(Offset::dtmfContacts() + i*DTMFContactElement::size())); cont.fromContact(ctx.config()->contacts()->dtmfContact(i)); idxlst[i] = i; } return true; } bool D868UVCodeplug::createAnalogContacts(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) DTMFContactBytemapElement analog_contact_bytemap(data(Offset::dtmfContactBytemap())); for (unsigned int i=0; i skip if (! analog_contact_bytemap.isEncoded(i)) continue; DTMFContactElement cont(data(Offset::dtmfContacts() + i*DTMFContactElement::size())); if (DTMFContact *dtmf = cont.toContact()) { ctx.config()->contacts()->add(dtmf); ctx.add(dtmf, i); } } return true; } void D868UVCodeplug::allocateRadioIDs() { /* Allocate radio IDs */ RadioIDBitmapElement radioid_bitmap(data(Offset::radioIDBitmap())); for (uint8_t i=0; i skip if (! radioid_bitmap.isEncoded(i)) continue; // Allocate radio IDs individually uint32_t addr = Offset::radioIDs() + i*RadioIDElement::size(); if (! isAllocated(addr, 0)) { image(0).addElement(addr, RadioIDElement::size()); } } } bool D868UVCodeplug::encodeRadioID(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err) // Encode radio IDs for (unsigned int i=0; i(); i++) { RadioIDElement(data(Offset::radioIDs() + i*RadioIDElement::size())).fromRadioID( ctx.config()->radioIDs()->getId(i)); } return true; } bool D868UVCodeplug::setRadioID(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Find a valid RadioID RadioIDBitmapElement radio_id_bitmap(data(Offset::radioIDBitmap())); for (uint16_t i=0; iradioIDs()->add(rid); ctx.add(rid, i); } } return true; } void D868UVCodeplug::allocateRXGroupLists() { /* * Allocate group lists */ GroupListBitmapElement grouplist_bitmap(data(Offset::groupListBitmap())); for (uint16_t i=0; i skip if (! grouplist_bitmap.isEncoded(i)) continue; // Allocate RX group lists indivitually uint32_t addr = Offset::groupLists() + i*Offset::betweenGroupLists(); if (! isAllocated(addr, 0)) { image(0).addElement(addr, GroupListElement::size()); GroupListElement(data(addr)).clear(); } } } bool D868UVCodeplug::encodeRXGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err) // Encode RX group-lists for (int i=0; irxGroupLists()->count(); i++) { GroupListElement grp(data(Offset::groupLists() + i*Offset::betweenGroupLists())); grp.fromGroupListObj(ctx.config()->rxGroupLists()->list(i), ctx); } return true; } bool D868UVCodeplug::createRXGroupLists(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Create RX group lists GroupListBitmapElement grouplist_bitmap(data(Offset::groupListBitmap())); for (uint16_t i=0; irxGroupLists()->add(obj); ctx.add(obj, i); } } return true; } bool D868UVCodeplug::linkRXGroupLists(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) GroupListBitmapElement grouplist_bitmap(data(Offset::groupListBitmap())); for (uint16_t i=0; i(i), ctx)) { logError() << "Cannot link RX group list idx=" << i; return false; } } return true; } void D868UVCodeplug::allocateZones() { ZoneBitmapElement zone_bitmap(data(Offset::zoneBitmap())); for (uint16_t i=0; i skip if (! zone_bitmap.isEncoded(i)) continue; // Allocate zone itself image(0).addElement(Offset::zoneChannels()+i*Offset::betweenZoneChannels(), Size::zoneChannels()); image(0).addElement(Offset::zoneNames()+i*Offset::betweenZoneNames(), Size::zoneName()); } } bool D868UVCodeplug::encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err); // Encode zones for (unsigned int i=0; i(); i++) { Zone *zone = ctx.get(i); // Clear name and channel list uint8_t *name = (uint8_t *)data(Offset::zoneNames() + i*Offset::betweenZoneNames()); uint16_t *channels = (uint16_t *)data(Offset::zoneChannels() + i*Offset::betweenZoneChannels()); encode_ascii(name, zone->name(), Limit::zoneNameLength(), 0); memset(channels, 0xff, Size::zoneChannels()); // Handle list A for (int j=0; jA()->count(); j++) { channels[j] = qToLittleEndian(ctx.index(zone->A()->get(j)->as())); } if (! encodeZone(i, zone, flags, ctx, err)) return false; } return true; } bool D868UVCodeplug::encodeZone(int i, Zone *zone, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(i); Q_UNUSED(zone); Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err) return true; } bool D868UVCodeplug::createZones(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Create zones ZoneBitmapElement zone_bitmap(data(Offset::zoneBitmap())); for (uint16_t i=0; ideleteLater(); return false; } // add to config ctx.config()->zones()->add(zone); ctx.add(zone, i); } return true; } bool D868UVCodeplug::decodeZone(int i, Zone *zone, Context &ctx, const ErrorStack &err) { Q_UNUSED(i); Q_UNUSED(zone); Q_UNUSED(ctx); Q_UNUSED(err) return true; } bool D868UVCodeplug::linkZones(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Create zones ZoneBitmapElement zone_bitmap(data(Offset::zoneBitmap())); for (uint16_t i=0; i(i); // link zone uint16_t *channels = (uint16_t *)data(Offset::zoneChannels() + i*Offset::betweenZoneChannels()); for (uint8_t j=0; j continue if (0xffff == *channels) continue; // Get channel index and check if defined uint16_t cidx = qFromLittleEndian(*channels); if (! ctx.has(cidx)) continue; zone->A()->add(ctx.get(cidx)); } if (! linkZone(i, zone, false, ctx, err)) return false; } return true; } bool D868UVCodeplug::linkZone(int i, Zone *zone, bool isB, Context &ctx, const ErrorStack &err) { Q_UNUSED(i); Q_UNUSED(zone); Q_UNUSED(isB); Q_UNUSED(ctx); Q_UNUSED(err) return true; } void D868UVCodeplug::allocateScanLists() { ScanListBitmapElement scanlist_bitmap(data(Offset::scanListBitmap())); for (uint8_t i=0; i skip if (! scanlist_bitmap.isEncoded(i)) continue; // Allocate scan lists indivitually uint8_t bank = (i/Limit::numScanListsPerBank()), bank_idx = (i%Limit::numScanListsPerBank()); uint32_t addr = Offset::scanListBanks() + bank*Offset::betweenScanListBanks() + bank_idx*Offset::betweenScanLists(); if (!isAllocated(addr, 0)) { image(0).addElement(addr, ScanListElement::size()); ScanListElement(data(addr)).clear(); } } } bool D868UVCodeplug::encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err); // Encode scan lists unsigned int num_scan_lists = std::min(Limit::numScanLists(), ctx.count()); for (unsigned int i=0; iscanlists()->scanlist(i), ctx); } return true; } bool D868UVCodeplug::createScanLists(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Create scan lists ScanListBitmapElement scanlist_bitmap(data(Offset::scanListBitmap())); for (unsigned int i=0; iscanlists()->add(obj); ctx.add(obj, i); } return true; } bool D868UVCodeplug::linkScanLists(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) ScanListBitmapElement scanlist_bitmap(data(Offset::scanListBitmap())); for (unsigned i=0; i(i); // Link scanlists immediately, all channels are defined already ctx.config()->scanlists()->add(obj); scanl.linkScanListObj(obj, ctx); } return true; } void D868UVCodeplug::allocateGeneralSettings() { image(0).addElement(Offset::settings(), GeneralSettingsElement::size()); } bool D868UVCodeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) return GeneralSettingsElement(data(Offset::settings())).fromConfig(flags, ctx); } bool D868UVCodeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) return GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx); } bool D868UVCodeplug::linkGeneralSettings(Context &ctx, const ErrorStack &err) { return GeneralSettingsElement(data(Offset::settings())).linkSettings(ctx.config()->settings(), ctx, err); } void D868UVCodeplug::allocateZoneChannelList() { image(0).addElement(Offset::zoneChannelList(), ZoneChannelListElement::size()); } void D868UVCodeplug::allocateDTMFNumbers() { image(0).addElement(Offset::dtmfIDList(), DTMFIDListElement::size()); } void D868UVCodeplug::allocateBootSettings() { image(0).addElement(Offset::bootSettings(), BootSettingsElement::size()); } bool D868UVCodeplug::encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) return BootSettingsElement(data(Offset::bootSettings())).fromConfig(flags, ctx); } bool D868UVCodeplug::decodeBootSettings(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) return BootSettingsElement(data(Offset::bootSettings())).updateConfig(ctx); } void D868UVCodeplug::allocateGPSSystems() { image(0).addElement(Offset::aprsSettings(), DMRAPRSSettingsElement::size()); image(0).addElement(Offset::dmrAPRSMessage(), DMRAPRSMessageElement::size()); } bool D868UVCodeplug::encodeGPSSystems(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) DMRAPRSSettingsElement gps(data(Offset::aprsSettings())); return gps.fromConfig(flags, ctx); } bool D868UVCodeplug::createGPSSystems(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) QSet systems; // First find all GPS systems linked, that is referenced by any channel // Create channels ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap())); for (uint16_t i=0; i(i)->is()) continue; ChannelElement ch(data(Offset::channelBanks() + bank*Offset::betweenChannelBanks() + idx*ChannelElement::size())); if (ch.txDigitalAPRS()) systems.insert(ch.digitalAPRSSystemIndex()); } // Then create all referenced GPS systems DMRAPRSSettingsElement gps(data(Offset::aprsSettings())); for (QSet::iterator idx=systems.begin(); idx!=systems.end(); idx++) gps.createGPSSystem(*idx, ctx); return true; } bool D868UVCodeplug::linkGPSSystems(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) DMRAPRSSettingsElement gps(data(Offset::aprsSettings())); // Then link all referenced GPS systems for (uint8_t i=0; i(i)) continue; gps.linkGPSSystem(i, ctx); } return true; } void D868UVCodeplug::allocateSMSMessages() { // Prefab. SMS messages MessageBytemapElement messages_bytemap(data(Offset::messageBytemap())); unsigned message_count = 0; for (uint8_t i=0; i()); for (unsigned int i=0; i(i)->message()); MessageListElement listElement(data(Offset::messageIndex() + i*Size::messageIndex())); listElement.setIndex(i); if (i > 0) { MessageListElement prevElement(data(Offset::messageIndex() + (i-1)*Size::messageIndex())); prevElement.setIndex(i); } } return true; } bool D868UVCodeplug::createSMSMessages(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) MessageBytemapElement messages_bytemap(data(Offset::messageBytemap())); for (unsigned int i=0; isetName(QString("SMS %1").arg(i+1)); temp->setMessage(message.message()); ctx.config()->smsExtension()->smsTemplates()->add(temp); ctx.add(temp, i); } return true; } bool D868UVCodeplug::linkSMSMessages(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); // nothing to do return true; } void D868UVCodeplug::allocateHotKeySettings() { // Allocate Hot Keys image(0).addElement(Offset::analogQuickCall(), AnalogQuickCallsElement::size()); image(0).addElement(Offset::statusMessages(), StatusMessagesElement::size()); image(0).addElement(Offset::hotKeySettings(), HotKeySettingsElement::size()); } void D868UVCodeplug::allocateRepeaterOffsetFrequencies() { // Offset frequencies image(0).addElement(Offset::offsetFrequencies(), RepeaterOffsetListElement::size()); } bool D868UVCodeplug::encodeRepeaterOffsetFrequencies(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err); // If no AnyTone extension is present -> leave untouched. if (! ctx.config()->settings()->anytoneExtension()) return true; RepeaterOffsetListElement offsets(data(Offset::offsetFrequencies())); offsets.clear(); for (unsigned int i=0; i(); i++) { offsets.setOffset(i, ctx.get(i)->offset()); } return true; } bool D868UVCodeplug::decodeRepeaterOffsetFrequencies(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Allocate extension, if not present. AnytoneSettingsExtension *ext = nullptr; if (ctx.config()->settings()->anytoneExtension()) { ext = ctx.config()->settings()->anytoneExtension(); } else { ext = new AnytoneSettingsExtension(); ctx.config()->settings()->setAnytoneExtension(ext); } // Decode offsets. RepeaterOffsetListElement offsets(data(Offset::offsetFrequencies())); for (unsigned int i=0; isetOffset(offsets.offset(i)); offset->setName(QString("%1 offset").arg(offsets.offset(i).format())); ext->autoRepeaterSettings()->offsets()->add(offset); ctx.add(offset, i); } } return true; } void D868UVCodeplug::allocateAlarmSettings() { // Alarm settings image(0).addElement(Offset::alarmSettings(), AlarmSettingElement::size()); image(0).addElement(Offset::alarmSettingsExtension(), DigitalAlarmExtensionElement::size()); } void D868UVCodeplug::allocateFMBroadcastSettings() { // FM broad-cast settings image(0).addElement(Offset::wfmChannels(), WFMChannelListElement::size()); image(0).addElement(Offset::wfmVFO(), WFMVFOElement::size()); } void D868UVCodeplug::allocate5ToneIDs() { // Allocate 5-tone functions FiveToneIDBitmapElement bitmap(data(Offset::fiveToneIdBitmap())); for (uint8_t i=0; i #include "anytone_codeplug.hh" #include "signaling.hh" class Channel; class DMRContact; class Zone; class RXGroupList; class ScanList; class GPSSystem; /** Represents the device specific binary codeplug for Anytone AT-D868UV radios. * * In contrast to many other code-plugs, the code-plug for Anytone radios are spread over a large * memory area. The amount of fragmentation of the codeplug is overwhelming. * For example, while channels are organized more or less nicely in continuous banks, zones are * distributed throughout the entire code-plug. That is, the names of zones are located in a * different memory section that the channel lists. Some lists are defined though bit-masks others * by byte-masks. All bit-masks are positive, that is 1 indicates an enabled item while the * bit-mask for contacts is inverted. * * In general the code-plug is huge and complex. Moreover, the radio provides a huge amount of * options and features. To this end, reverse-engineering this code-plug was a nightmare. * * More over, the binary code-plug file generate by the windows CPS does not directly relate to * the data being written to the radio. To this end the code-plug has been reverse-engineered * using wireshark to monitor the USB communication between the windows CPS (running in a virtual * box) and the device. The latter makes the reverse-engineering particularly cumbersome. * * @section d868uvcpl Codeplug structure within radio * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Channels
      Start Size Content
      024C1500 000200 Bitmap of 4000 channels, default 0x00, 0x00 padded.
      00800000 max. 002000 Channel bank 0 of up to 128 channels, * see @c AnytoneCodeplug::ChannelElement 64 b each.
      00840000 max. 002000 Channel bank 1 of up to 128 channels.
      ... ... ...
      00FC0000 max. 000800 Channel bank 32, up to 32 channels.
      00FC0800 000040 VFO A settings, * see @c AnytoneCodeplug::ChannelElement.
      00FC0840 000040 VFO B settings, * see @c AnytoneCodeplug::ChannelElement.
      Zones
      Start Size Content
      024C1300 000020 Bitmap of 250 zones.
      01000000 max. 01f400 250 zones channel lists of 250 16bit indices each. * 0-based, little endian, default/padded=0xffff. Offset between channel lists 0x200, size of each list 0x1f4.
      02540000 max. 001f40 250 Zone names. * Each zone name is up to 16 ASCII chars long and gets 0-padded to 32b.
      Contacts
      Start Size Content
      02600000 max. 009C40 Index list of valid contacts. * 10000 32bit indices, little endian, default 0xffffffff
      02640000 000500 Contact bitmap, 10000 bit, inverted, default 0xff, 0x00 padded.
      02680000 max. 0186a0 Bank 1 of 1000 contacts, * see @c AnytoneCodeplug::ContactElement. As each contact is 100b, they do not align with the * 16b blocks being transferred to the device. Hence contacts are organized internally in groups * of 4 contacts. There are 10 banks, each containing 1000 contacts. The offset between banks is 0x40000.
      04340000 max. 013880 DMR ID to contact index map, * see @c AnytoneCodeplug::ContactMapElement. Sorted by ID, empty entries set to * @c 0xffffffffffffffff.
      Analog Contacts
      Start Size Content
      02900000 000080 Index list of valid analog contacts.
      02900100 000080 Bytemap for 128 analog contacts.
      02940000 max. 000180 128 analog contacts. * See @c AnytoneCodeplug::DTMFContactElement. As each analog contact is 24b, they do not align * with the 16b transfer block-size. Hence analog contacts are internally organized in groups * of 2.
      RX Group Lists
      Start Size Content
      025C0B10 000020 Bitmap of 250 RX group lists, default/padding 0x00.
      02980000 max. 000120 Grouplist 0, * see @c AnytoneCodeplug::GroupListElement.
      02980200 max. 000120 Grouplist 1
      ... ... ...
      0299f200 max. 000120 Grouplist 250
      Scan lists
      Start Size Content
      024C1340 000020 Bitmap of 250 scan lists.
      01080000 000090 Bank 0, Scanlist 1, * see @c AnytoneCodeplug::ScanListElement.
      01080200 000090 Bank 0, Scanlist 2
      ... ... ...
      01081E00 000090 Bank 0, Scanlist 16
      010C0000 000090 Bank 1, Scanlist 17
      ... ... ...
      01440000 000090 Bank 15, Scanlist 241
      ... ... ...
      01441400 000090 Bank 15, Scanlist 250
      Radio IDs
      Start Size Content
      024C1320 000020 Bitmap of 250 radio IDs.
      02580000 max. 001f40 250 Radio IDs. * See @c AnytoneCodeplug::RadioIDElement.
      GPS
      Start Size Content
      02501000 000030 GPS settings, * see @c AnytoneCodeplug::DMRAPRSSettingsElement.
      02501100 000030 GPS message.
      General Settings
      Start Size Content
      02500000 0000D0 General settings, * see @c D868UVCodeplug::GeneralSettingsElement.
      02500100 000400 Zone A & B channel list.
      02500500 000100 DTMF list
      02500600 000030 Power on settings, * see @c AnytoneCodeplug::BootSettingsElement.
      024C2000 0003F0 List of 250 auto-repeater offset frequencies. * 32bit little endian frequency in 10Hz. I.e., 600kHz = 60000. Default 0x00000000, 0x00 padded.
      Messages
      Start Size Content
      01640000 max. 000100 Some kind of linked list of messages. * See @c AnytoneCodeplug::MessageListElement. Each entry has a size of 0x10.
      01640800 000090 Bytemap of up to 100 valid messages. * 0x00=valid, 0xff=invalid, remaining 46b set to 0x00.
      02140000 max. 000800 Bank 0, Messages 1-8. * Each message consumes 0x100b. See @c AnytoneCodeplug::MessageElement.
      02180000 max. 000800 Bank 1, Messages 9-16
      ... ... ...
      02440000 max. 000800 Bank 12, Messages 97-100
      Hot Keys
      Start Size Content
      025C0000 000100 4 analog quick-call settings. * See @c AnytoneCodeplug::AnalogQuickCallElement.
      025C0B00 000010 Status message bitmap.
      025C0100 000400 Up to 32 status messages. * Length unknown, offset 0x20. ASCII 0x00 terminated and padded.
      025C0500 000360 18 hot-key settings, * see @c AnytoneCodeplug::HotKeyElement
      Misc
      Start Size Content
      024C1400 000020 Alarm setting, * see @c AnytoneCodeplug::AlarmSettingElement.
      024C1440 000030 Digital alarm settings extension, * see @c AnytoneCodeplug::DigitalAlarmExtensionElement.
      FM Broadcast
      Start Size Content
      02480210 000020 Bitmap of 100 FM broadcast channels.
      02480000 max. 000200 100 FM broadcast channels. Encoded * as 8-digit BCD little-endian in 100Hz. Filled with 0x00.
      02480200 000010 FM broadcast VFO frequency. Encoded * as 8-digit BCD little-endian in 100Hz. Filled with 0x00.
      DTMF, 2-tone & 5-tone signaling.
      Start Size Content
      024C0C80 000010 5-tone encoding bitmap for 100 IDs.
      024C0000 000020 List of 100 5-tone IDs, * see @c AnytoneCodeplug::FiveToneIDElement
      024C0D00 000200 List of 16 5-tone functions, * see @c AnytoneCodeplug::FiveToneFunctionElement.
      024C1000 000080 5-tone settings, * see @c AnytoneCodeplug::FiveToneSettingsElement.
      024C1080 000050 DTMF settings, * see @c AnytoneCodeplug::DTMFSettingsElement.
      024C1280 000010 2-tone encoding bitmap.
      024C1100 000010 2-tone encoding.
      024C1290 000010 2-tone settings.
      024C2600 000010 2-tone decoding bitmap.
      024C2400 000030 2-tone decoding.
      Encryption
      Start Size Content
      024C1700 000040 32 Encryption IDs, 0-based, 16bit big-endian.
      024C1800 000500 32 DMR-Encryption keys, see @c dmr_encryption_key_t, * 40b each.
      * * @ingroup d868uv */ class D868UVCodeplug : public AnytoneCodeplug { Q_OBJECT protected: /** Colors supported by the D868UVE. */ struct Color { public: /** Maps code -> color. */ static AnytoneDisplaySettingsExtension::Color decode(uint8_t code); /** Maps color -> code. */ static uint8_t encode(AnytoneDisplaySettingsExtension::Color color); protected: /** Encoding of the supported colors. */ typedef enum { White = 0, Red=1 } CodedColor; }; public: /** Represents the channel element for AnyTone D868UV devices. * This class derives from @c AnytoneCodeplug::ChannelElement and implements the device-specific * encoding of channels for the AnyTone D868UV. * * Memory layout of the encoded channel element (size 0x0040 bytes): * @verbinclude d868uv_channel.txt */ class ChannelElement: public AnytoneCodeplug::ChannelElement { protected: /** Hidden constructor. */ ChannelElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ ChannelElement(uint8_t *ptr); /** Returns @c true if ranging is enabled. */ virtual bool ranging() const; /** Enables/disables ranging. */ virtual void enableRanging(bool enable); /** Returns @c true if through mode is enabled. */ virtual bool throughMode() const; /** Enables/disables though mode. */ virtual void enableThroughMode(bool enable); /** Returns @c true if data ACK is enabled. */ virtual bool dataACK() const; /** Enables/disables data ACK. */ virtual void enableDataACK(bool enable); /** Returns @c true if TX APRS is enabled. */ virtual bool txDigitalAPRS() const; /** Enables/disables TX APRS. */ virtual void enableTXDigitalAPRS(bool enable); /** Returns the DMR APRS system index. */ virtual unsigned digitalAPRSSystemIndex() const; /** Sets the DMR APRS system index. */ virtual void setDigitalAPRSSystemIndex(unsigned idx); /** Returns the DMR encryption key index (+1), 0=Off. */ virtual unsigned dmrEncryptionKeyIndex() const; /** Sets the DMR encryption key index (+1), 0=Off. */ virtual void setDMREncryptionKeyIndex(unsigned idx); /** Returns @c true if multiple key encryption is enabled. */ virtual bool multipleKeyEncryption() const; /** Enables/disables multiple key encryption. */ virtual void enableMultipleKeyEncryption(bool enable); /** Returns @c true if random key is enabled. */ virtual bool randomKey() const; /** Enables/disables random key. */ virtual void enableRandomKey(bool enable); /** Returns @c true if SMS is enabled. */ virtual bool sms() const; /** Enables/disables SMS. */ virtual void enableSMS(bool enable); /** Constructs a generic @c Channel object from the codeplug channel. */ virtual Channel *toChannelObj(Context &ctx) const; /** Links a previously constructed channel to the rest of the configuration. */ virtual bool linkChannelObj(Channel *c, Context &ctx) const; /** Initializes this codeplug channel from the given generic configuration. */ virtual bool fromChannelObj(const Channel *c, Context &ctx); protected: /** Internal used offsets within the channel element. */ struct Offset: public AnytoneCodeplug::ChannelElement::Offset { /// @todo Implement }; }; /** Represents the general config of the radio within the D868UV binary codeplug. * * This class only implements the differences to the generic * @c AnytoneCodeplug::GeneralSettingsElement. * * Memory layout of encoded general settings (size 0x00d0 bytes): * @verbinclude d868uv_generalsettings.txt */ class GeneralSettingsElement: public AnytoneCodeplug::GeneralSettingsElement { protected: /** Device specific key functions. */ struct KeyFunction { public: /** Encodes key function. */ static uint8_t encode(AnytoneKeySettingsExtension::KeyFunction tone); /** Decodes key function. */ static AnytoneKeySettingsExtension::KeyFunction decode(uint8_t code); protected: /** Encoded key functions. */ typedef enum { Off = 0x00, Voltage = 0x01, Power = 0x02, Repeater = 0x03, Reverse = 0x04, Encryption = 0x05, Call = 0x06, VOX = 0x07, ToggleVFO = 0x08, SubPTT = 0x09, Scan = 0x0a, WFM = 0x0b, Alarm = 0x0c, RecordSwitch = 0x0d, Record = 0x0e, SMS = 0x0f, Dial = 0x10, GPSInformation = 0x11, Monitor = 0x12, ToggleMainChannel = 0x13, HotKey1 = 0x14, HotKey2 = 0x15, HotKey3 = 0x16, HotKey4 = 0x17, HotKey5 = 0x18, HotKey6 = 0x19, WorkAlone = 0x1a, SkipChannel = 0x1b, DMRMonitor = 0x1c, SubChannel = 0x1d, PriorityZone = 0x1e, VFOScan = 0x1f, MICSoundQuality = 0x20, LastCallReply = 0x21, ChannelType = 0x22, Ranging = 0x23, ChannelRanging = 0x24, MaxVolume = 0x25, Slot = 0x26 } KeyFunctionCode; }; protected: /** Hidden constructor. */ GeneralSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ GeneralSettingsElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x00d0; } /** Resets the general settings. */ void clear(); /** Returns the power-save mode. */ virtual AnytonePowerSaveSettingsExtension::PowerSave powerSave() const; /** Sets the power-save mode. */ virtual void setPowerSave(AnytonePowerSaveSettingsExtension::PowerSave mode); /** Returns the VOX level. */ virtual unsigned voxLevel() const; /** Sets the VOX level. */ virtual void setVOXLevel(unsigned level); /** Returns the VOX delay in ms. */ virtual Interval voxDelay() const; /** Sets the VOX delay in ms. */ virtual void setVOXDelay(Interval ms); /** Returns the VOX source. */ virtual AnytoneAudioSettingsExtension::VoxSource voxSource() const; /** Sets the VOX source. */ virtual void setVOXSource(AnytoneAudioSettingsExtension::VoxSource source); unsigned int dmrMicGain() const; void setDMRMicGain(unsigned int gain); unsigned maxSpeakerVolume() const; void setMaxSpeakerVolume(unsigned level); /** Returns the maximum headphone volume. */ virtual unsigned maxHeadPhoneVolume() const; /** Sets the maximum headphone volume. */ virtual void setMaxHeadPhoneVolume(unsigned max); bool enhanceAudio() const; void enableEnhancedAudio(bool enable); bool recording() const; void enableRecording(bool enable); /** Returns the recording delay in ms. */ virtual unsigned recordingDelay() const; /** Sets the recording delay in ms. */ virtual void setRecodringDelay(unsigned ms); AnytoneSettingsExtension::VFOScanType vfoScanType() const; void setVFOScanType(AnytoneSettingsExtension::VFOScanType type); Frequency minVFOScanFrequencyUHF() const; void setMinVFOScanFrequencyUHF(Frequency hz); Frequency maxVFOScanFrequencyUHF() const; void setMaxVFOScanFrequencyUHF(Frequency hz); Frequency minVFOScanFrequencyVHF() const; void setMinVFOScanFrequencyVHF(Frequency hz); Frequency maxVFOScanFrequencyVHF() const; void setMaxVFOScanFrequencyVHF(Frequency hz); AnytoneKeySettingsExtension::KeyFunction funcKeyAShort() const; void setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyBShort() const; void setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyCShort() const; void setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey1Short() const; void setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey2Short() const; void setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyALong() const; void setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyBLong() const; void setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyCLong() const; void setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey1Long() const; void setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey2Long() const; void setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func); Interval longPressDuration() const; void setLongPressDuration(Interval ms); bool vfoModeA() const; void enableVFOModeA(bool enable); bool vfoModeB() const; void enableVFOModeB(bool enable); unsigned memoryZoneA() const; void setMemoryZoneA(unsigned zone); unsigned memoryZoneB() const; void setMemoryZoneB(unsigned zone); bool gps() const; void enableGPS(bool enable); QTimeZone gpsTimeZone() const; void setGPSTimeZone(const QTimeZone &zone); bool getGPSPosition() const; void enableGetGPSPosition(bool enable); bool gpsUnitsImperial() const ; void enableGPSUnitsImperial(bool enable); /** Returns the GPS update period in seconds. */ virtual Interval gpsUpdatePeriod() const; /** Sets the GPS update period in seconds. */ virtual void setGPSUpdatePeriod(Interval sec); bool keyToneEnabled() const; void enableKeyTone(bool enable); bool smsAlert() const; void enableSMSAlert(bool enable); bool callAlert() const; void enableCallAlert(bool enable); bool dmrTalkPermit() const; void enableDMRTalkPermit(bool enable); bool fmTalkPermit() const; void enableFMTalkPermit(bool enable); bool dmrResetTone() const; void enableDMRResetTone(bool enable); bool idleChannelTone() const; void enableIdleChannelTone(bool enable); bool startupTone() const; void enableStartupTone(bool enable); void callToneMelody(Melody &melody) const; void setCallToneMelody(const Melody &melody); void idleToneMelody(Melody &melody) const; void setIdleToneMelody(const Melody &melody); void resetToneMelody(Melody &melody) const; void setResetToneMelody(const Melody &melody); bool activeChannelB() const; void enableActiveChannelB(bool enable); bool subChannel() const; void enableSubChannel(bool enable); Interval menuExitTime() const; void setMenuExitTime(Interval intv); bool callEndPrompt() const; void enableCallEndPrompt(bool enable); bool volumeChangePrompt() const; void enableVolumeChangePrompt(bool enable); AnytoneDisplaySettingsExtension::LastCallerDisplayMode lastCallerDisplayMode() const; void setLastCallerDisplayMode(AnytoneDisplaySettingsExtension::LastCallerDisplayMode mode); bool displayClock() const; void enableDisplayClock(bool enable); bool displayCall() const; void enableDisplayCall(bool enable); AnytoneDisplaySettingsExtension::Color callDisplayColor() const; void setCallDisplayColor(AnytoneDisplaySettingsExtension::Color color); bool showLastHeard() const; void enableShowLastHeard(bool enable); unsigned brightness() const; void setBrightness(unsigned level); /** Returns @c true if the backlight is always on. */ virtual bool backlightPermanent() const; /** Returns the backlight duration in seconds. */ virtual Interval rxBacklightDuration() const; /** Sets the backlight duration in seconds. */ virtual void setRXBacklightDuration(Interval sec); /** Sets the backlight to permanent (always on). */ virtual void enableBacklightPermanent(); AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionA() const; void setAutoRepeaterDirectionA(AnytoneAutoRepeaterSettingsExtension::Direction dir); AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionB() const; void setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir); bool hasAutoRepeaterOffsetFrequencyIndexUHF() const; unsigned autoRepeaterOffsetFrequencyIndexUHF() const; void setAutoRepeaterOffsetFrequenyIndexUHF(unsigned idx); void clearAutoRepeaterOffsetFrequencyIndexUHF(); bool hasAutoRepeaterOffsetFrequencyIndexVHF() const; unsigned autoRepeaterOffsetFrequencyIndexVHF() const; void setAutoRepeaterOffsetFrequenyIndexVHF(unsigned idx); void clearAutoRepeaterOffsetFrequencyIndexVHF(); Frequency autoRepeaterMinFrequencyVHF() const; void setAutoRepeaterMinFrequencyVHF(Frequency Hz); Frequency autoRepeaterMaxFrequencyVHF() const; void setAutoRepeaterMaxFrequencyVHF(Frequency Hz); Frequency autoRepeaterMinFrequencyUHF() const; void setAutoRepeaterMinFrequencyUHF(Frequency Hz); Frequency autoRepeaterMaxFrequencyUHF() const; void setAutoRepeaterMaxFrequencyUHF(Frequency Hz); bool showCurrentContact() const; void enableShowCurrentContact(bool enable); /** Returns @c true if the key-tone level is adjustable. */ virtual bool keyToneLevelAdjustable() const; /** Returns the key-tone level (0=adjustable). */ virtual unsigned keyToneLevel() const; /** Sets the key-tone level. */ virtual void setKeyToneLevel(unsigned level); /** Sets the key-tone level adjustable. */ virtual void setKeyToneLevelAdjustable(); bool knobLock() const; void enableKnobLock(bool enable); bool keypadLock() const; void enableKeypadLock(bool enable); bool sidekeysLock() const; void enableSidekeysLock(bool enable); bool keyLockForced() const; void enableKeyLockForced(bool enable); bool defaultChannel() const; void enableDefaultChannel(bool enable); unsigned defaultZoneIndexA() const; void setDefaultZoneIndexA(unsigned idx); unsigned defaultZoneIndexB() const; void setDefaultZoneIndexB(unsigned idx); bool defaultChannelAIsVFO() const; unsigned defaultChannelAIndex() const; void setDefaultChannelAIndex(unsigned idx); void setDefaultChannelAToVFO(); bool defaultChannelBIsVFO() const; unsigned defaultChannelBIndex() const; void setDefaultChannelBIndex(unsigned idx); void setDefaultChannelBToVFO(); bool keepLastCaller() const; void enableKeepLastCaller(bool enable); bool fromConfig(const Flags &flags, Context &ctx); bool updateConfig(Context &ctx); bool linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Some internal used offsets within the element. */ struct Offset: public AnytoneCodeplug::GeneralSettingsElement::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int enableKeyTone() { return 0x0000; } static constexpr unsigned int powerSaveMode() { return 0x000b; } static constexpr unsigned int voxLevel() { return 0x000c; } static constexpr unsigned int voxDelay() { return 0x000d; } static constexpr unsigned int vfoScanType() { return 0x000e; } static constexpr unsigned int dmrMicGain() { return 0x000f; } static constexpr unsigned int progFuncKeyAShort() { return 0x0010; } static constexpr unsigned int progFuncKeyBShort() { return 0x0011; } static constexpr unsigned int progFuncKeyCShort() { return 0x0012; } static constexpr unsigned int progFuncKey1Short() { return 0x0013; } static constexpr unsigned int progFuncKey2Short() { return 0x0014; } static constexpr unsigned int vfoModeA() { return 0x0015; } static constexpr unsigned int vfoModeB() { return 0x0016; } static constexpr unsigned int memZoneA() { return 0x001f; } static constexpr unsigned int memZoneB() { return 0x0020; } static constexpr unsigned int enableRecoding() { return 0x0022; } static constexpr unsigned int displayBrightness() { return 0x0026; } static constexpr unsigned int backlightDuration() { return 0x0027; } static constexpr unsigned int gpsEnable() { return 0x0028; } static constexpr unsigned int smsAlert() { return 0x0029; } static constexpr unsigned int activeChannelB() { return 0x002c; } static constexpr unsigned int subChannel() { return 0x002d; } static constexpr unsigned int callAlert() { return 0x002f; } static constexpr unsigned int gpsTimeZone() { return 0x0030; } static constexpr unsigned int talkPermit() { return 0x0031; } static constexpr unsigned int dmrResetTone() { return 0x0032; } static constexpr unsigned int voxSource() { return 0x0033; } static constexpr unsigned int idleChannelTone() { return 0x0036; } static constexpr unsigned int menuExitTime() { return 0x0037; } static constexpr unsigned int startupTone() { return 0x0039; } static constexpr unsigned int callEndPrompt() { return 0x003a; } static constexpr unsigned int maxSpeakerVolume() { return 0x003b; } static constexpr unsigned int getGPSPosition() { return 0x003f; } static constexpr unsigned int progFuncKeyALong() { return 0x0041; } static constexpr unsigned int progFuncKeyBLong() { return 0x0042; } static constexpr unsigned int progFuncKeyCLong() { return 0x0043; } static constexpr unsigned int progFuncKey1Long() { return 0x0044; } static constexpr unsigned int progFuncKey2Long() { return 0x0045; } static constexpr unsigned int longPressDuration() { return 0x0046; } static constexpr unsigned int volumeChangePrompt(){ return 0x0047; } static constexpr unsigned int autoRepeaterDirA() { return 0x0048; } static constexpr unsigned int lastCallerDisplay() { return 0x004d; } static constexpr unsigned int showClock() { return 0x0051; } static constexpr unsigned int maxHeadPhoneVolume(){ return 0x0052; } static constexpr unsigned int enhanceAudio() { return 0x0057; } static constexpr unsigned int minVFOScanUHF() { return 0x0058; } static constexpr unsigned int maxVFOScanUHF() { return 0x005c; } static constexpr unsigned int minVFOScanVHF() { return 0x0060; } static constexpr unsigned int maxVFOScanVHF() { return 0x0064; } static constexpr unsigned int autoRepOffsetUHF() { return 0x0068; } static constexpr unsigned int autoRepOffsetVHF() { return 0x0069; } static constexpr unsigned int callToneTones() { return 0x0072; } static constexpr unsigned int callToneDurations() { return 0x007c; } static constexpr unsigned int idleToneTones() { return 0x0086; } static constexpr unsigned int idleToneDurations() { return 0x0090; } static constexpr unsigned int resetToneTones() { return 0x009a; } static constexpr unsigned int resetToneDurations(){ return 0x00a4; } static constexpr unsigned int recordingDelay() { return 0x00ae; } static constexpr unsigned int callDisplayMode() { return 0x00af; } static constexpr unsigned int callColor() { return 0x00b0; } static constexpr unsigned int gpsPeriod() { return 0x00b1; } static constexpr unsigned int showCurrentContact(){ return 0x00b2; } static constexpr unsigned int keyToneLevel() { return 0x00b3; } static constexpr unsigned int gpsUnits() { return 0x00b4; } static constexpr unsigned int knobLock() { return 0x00b5; } static constexpr unsigned int keypadLock() { return 0x00b5; } static constexpr unsigned int sideKeyLock() { return 0x00b5; } static constexpr unsigned int forceKeyLock() { return 0x00b5; } static constexpr unsigned int showLastHeard() { return 0x00b6; } static constexpr unsigned int autoRepMinVHF() { return 0x00b8; } static constexpr unsigned int autoRepMaxVHF() { return 0x00bc; } static constexpr unsigned int autoRepMinUHF() { return 0x00c0; } static constexpr unsigned int autoRepMaxUHF() { return 0x00c4; } static constexpr unsigned int autoRepeaterDirB() { return 0x00c8; } static constexpr unsigned int defaultChannels() { return 0x00ca; } static constexpr unsigned int defaultZoneA() { return 0x00cb; } static constexpr unsigned int defaultZoneB() { return 0x00cc; } static constexpr unsigned int defaultChannelA() { return 0x00cd; } static constexpr unsigned int defaultChannelB() { return 0x00ce; } static constexpr unsigned int keepLastCaller() { return 0x00cf; } /// @endcond }; }; protected: /** Hidden constructor constructor. */ explicit D868UVCodeplug(const QString &label, QObject *parent = nullptr); public: /** Empty constructor. */ explicit D868UVCodeplug(QObject *parent = nullptr); protected: bool allocateBitmaps(); virtual void setBitmaps(Context &ctx); virtual void allocateUpdated(); virtual void allocateForDecoding(); virtual void allocateForEncoding(); virtual bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocate channels from bitmap. */ virtual void allocateChannels(); /** Encode channels into codeplug. */ virtual bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Create channels from codeplug. */ virtual bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()); /** Link channels. */ virtual bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocate VFO settings. */ virtual void allocateVFOSettings(); /** Allocate contacts from bitmaps. */ virtual void allocateContacts(); /** Encode contacts into codeplug. */ virtual bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Create contacts from codeplug. */ virtual bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocate analog contacts from bitmaps. */ virtual void allocateAnalogContacts(); /** Encode analog contacts into codeplug. */ virtual bool encodeAnalogContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Create analog contacts from codeplug. */ virtual bool createAnalogContacts(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocate radio IDs from bitmaps. */ virtual void allocateRadioIDs(); /** Encode radio ID into codeplug. */ virtual bool encodeRadioID(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Set radio ID from codeplug. */ virtual bool setRadioID(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocates RX group lists from bitmaps. */ virtual void allocateRXGroupLists(); /** Encode RX group lists into codeplug. */ virtual bool encodeRXGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Create RX group lists from codeplug. */ virtual bool createRXGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); /** Link RX group lists. */ virtual bool linkRXGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocate zones from bitmaps. */ virtual void allocateZones(); /** Encode zones into codeplug. */ virtual bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Function to encode a single zone. */ virtual bool encodeZone(int i, Zone *zone, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Create zones from codeplug. */ virtual bool createZones(Context &ctx, const ErrorStack &err=ErrorStack()); /** Function to decode a single zone. */ virtual bool decodeZone(int i, Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack()); /** Link zones. */ virtual bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()); /** Function to link a single zone. */ virtual bool linkZone(int i, Zone *zone, bool isB, Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocate scanlists from bitmaps. */ virtual void allocateScanLists(); /** Encode scan lists into codeplug. */ virtual bool encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Create scan lists from codeplug. */ virtual bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack()); /** Link scan lists. */ virtual bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocates general settings memory section. */ virtual void allocateGeneralSettings(); /** Encodes the general settings section. */ virtual bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Decodes the general settings section. */ virtual bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()); /** Link the general settings. */ virtual bool linkGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocates zone channel list memory section. */ virtual void allocateZoneChannelList(); /** Allocates DTMF number list memory section. */ virtual void allocateDTMFNumbers(); /** Allocates boot settings memory section. */ virtual void allocateBootSettings(); /** Encodes the boot settings section. */ virtual bool encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Decodes the boot settings section. */ virtual bool decodeBootSettings(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocates GPS settings memory section. */ virtual void allocateGPSSystems(); /** Encodes the GPS settings section. */ virtual bool encodeGPSSystems(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Create GPS systems from codeplug. */ virtual bool createGPSSystems(Context &ctx, const ErrorStack &err=ErrorStack()); /** Link GPS systems. */ virtual bool linkGPSSystems(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocate prefab SMS messages. */ virtual void allocateSMSMessages(); /** Encodes prefab SMS messages. */ virtual bool encodeSMSMessages(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Create SMS messages from codeplug. */ virtual bool createSMSMessages(Context &ctx, const ErrorStack &err=ErrorStack()); /** Link SMS messages. */ virtual bool linkSMSMessages(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocates hot key settings memory section. */ virtual void allocateHotKeySettings(); /** Allocates repeater offset settings memory section. */ virtual void allocateRepeaterOffsetFrequencies(); /** Encodes auto-repeater offset frequencies. */ virtual bool encodeRepeaterOffsetFrequencies(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Decodes auto-repeater offset frequencies. */ virtual bool decodeRepeaterOffsetFrequencies(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocates alarm settings memory section. */ virtual void allocateAlarmSettings(); /** Allocates FM broadcast settings memory section. */ virtual void allocateFMBroadcastSettings(); /** Allocates all 5-Tone IDs used. */ virtual void allocate5ToneIDs(); /** Allocates 5-Tone functions. */ virtual void allocate5ToneFunctions(); /** Allocates 5-Tone settings. */ virtual void allocate5ToneSettings(); /** Allocates all 2-Tone IDs used. */ virtual void allocate2ToneIDs(); /** Allocates 2-Tone functions. */ virtual void allocate2ToneFunctions(); /** Allocates 2-Tone settings. */ virtual void allocate2ToneSettings(); /** Allocates DTMF settings. */ virtual void allocateDTMFSettings(); public: /** Some limits for the codeplug. */ struct Limit { static constexpr unsigned int channelsPerBank() { return 128; } ///< Max number of channels per bank. static constexpr unsigned int numChannels() { return 4000; } ///< Max number of channels. static constexpr unsigned int contactsPerBank() { return 1000; } ///< Max number of contacts per bank. static constexpr unsigned int contactsPerBlock() { return 4; } ///< Max number of contacts per block. static constexpr unsigned int numContacts() { return 10000; } ///< Max number of contacts. static constexpr unsigned int numDTMFContacts() { return 128; } ///< Max number of DTMF contacts. static constexpr unsigned int numGroupLists() { return 250; } ///< Max number of group lists. static constexpr unsigned int numScanLists() { return 250; } ///< Max number of scan lists. static constexpr unsigned int numScanListsPerBank() { return 16; } ///< Max number of scan lists per bank. static constexpr unsigned int numRadioIDs() { return 250; } ///< Max number of radio IDs. // There is no zone element -> hence all attributes must be defied within the codeplug. static constexpr unsigned int numZones() { return 250; } ///< Max number of zones. static constexpr unsigned int numChannelsPerZone() { return 250; } ///< Max number of channels per zone. static constexpr unsigned int zoneNameLength() { return 16; } ///< Max zone name length. static constexpr unsigned int dmrAPRSSystems() { return 8; } ///< Max number of DMR APRS systems. static constexpr unsigned int numMessages() { return 100; } ///< Max number of preset SMS. static constexpr unsigned int numMessagePerBank() { return 8; } ///< Max number of SMS per bank. static constexpr unsigned int numTwoToneIDs() { return 24; } ///< Max number of two-tone IDs. static constexpr unsigned int numTwoToneFunctions() { return 16; } ///< Max number of two-tone functions. }; protected: /** Some internal used offsets within the codeplug. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int channelBitmap() { return 0x024c1500; } static constexpr unsigned int channelBanks() { return 0x00800000; } static constexpr unsigned int betweenChannelBanks() { return 0x00040000; } static constexpr unsigned int vfoA() { return 0x00fc0800; } static constexpr unsigned int vfoB() { return 0x00fc0840; } static constexpr unsigned int contactBitmap() { return 0x02640000; } static constexpr unsigned int contactBanks() { return 0x02680000; } static constexpr unsigned int betweenContactBanks() { return 0x00040000; } static constexpr unsigned int betweenContactBlocks() { return 0x00000190; } static constexpr unsigned int contactIndex() { return 0x02600000; } static constexpr unsigned int contactIdTable() { return 0x04340000; } static constexpr unsigned int dtmfContactBytemap() { return 0x02900100; } static constexpr unsigned int dtmfContacts() { return 0x02940000; } static constexpr unsigned int dtmfIndex() { return 0x02900000; } static constexpr unsigned int dtmfSettings() { return 0x024C1080; } static constexpr unsigned int dtmfIDList() { return 0x02500500; } static constexpr unsigned int groupListBitmap() { return 0x025C0B10; } static constexpr unsigned int groupLists() { return 0x02980000; } static constexpr unsigned int betweenGroupLists() { return 0x00000200; } static constexpr unsigned int scanListBitmap() { return 0x024c1340; } static constexpr unsigned int scanListBanks() { return 0x01080000; } static constexpr unsigned int betweenScanLists() { return 0x00000200; } static constexpr unsigned int betweenScanListBanks() { return 0x00040000; } static constexpr unsigned int radioIDBitmap() { return 0x024c1320; } static constexpr unsigned int radioIDs() { return 0x02580000; } static constexpr unsigned int settings() { return 0x02500000; } static constexpr unsigned int zoneChannelList() { return 0x02500100; } static constexpr unsigned int bootSettings() { return 0x02500600; } static constexpr unsigned int aprsSettings() { return 0x02501000; } static constexpr unsigned int dmrAPRSMessage() { return 0x02501100; } static constexpr unsigned int offsetFrequencies() { return 0x024C2000; } static constexpr unsigned int zoneBitmap() { return 0x024c1300; } static constexpr unsigned int zoneChannels() { return 0x01000000; } static constexpr unsigned int betweenZoneChannels() { return 0x00000200; } static constexpr unsigned int zoneNames() { return 0x02540000; } static constexpr unsigned int betweenZoneNames() { return 0x00000020; } static constexpr unsigned int messageBytemap() { return 0x01640800; } static constexpr unsigned int messageBanks() { return 0x02140000; } static constexpr unsigned int betweenMessageBanks() { return 0x00040000; } static constexpr unsigned int messageIndex() { return 0x01640000; } static constexpr unsigned int analogQuickCall() { return 0x025C0000; } static constexpr unsigned int statusMessageBitmap() { return 0x025C0B00; } static constexpr unsigned int statusMessages() { return 0x025C0100; } static constexpr unsigned int hotKeySettings() { return 0x025C0500; } static constexpr unsigned int alarmSettings() { return 0x024C1400; } static constexpr unsigned int alarmSettingsExtension() { return 0x024c1440; } static constexpr unsigned int fiveToneIdBitmap() { return 0x024C0C80; } static constexpr unsigned int fiveToneIdList() { return 0x024C0000; } static constexpr unsigned int fiveToneFunctions() { return 0x024C0D00; } static constexpr unsigned int fiveToneSettings() { return 0x024C1000; } static constexpr unsigned int twoToneIdBitmap() { return 0x024C1280; } static constexpr unsigned int twoToneIdList() { return 0x024C1100; } static constexpr unsigned int twoToneFunctionBitmap(){ return 0x024c2600; } static constexpr unsigned int twoToneFunctionList() { return 0x024c2400; } static constexpr unsigned int twoToneSettings() { return 0x024C1290; } static constexpr unsigned int wfmChannelBitmap() { return 0x02480210; } static constexpr unsigned int wfmChannels() { return 0x02480000; } static constexpr unsigned int wfmVFO() { return 0x02480200; } static constexpr unsigned int dmrEncryptionIDs() { return 0x024C1700; } static constexpr unsigned int dmrEncryptionKeys() { return 0x024C1800; } /// @endcond }; /** Internal used sizes. Usually sizes are specified by static methods of the element, however, * some stuff is not represented by Elements in the AnyTone codeplug, hence this table is needed. */ struct Size { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int zoneName() { return 0x0020; } static constexpr unsigned int zoneChannels() { return 0x0200; } static constexpr unsigned int messageBank() { return 0x0800; } static constexpr unsigned int messageIndex() { return 0x0010; } /// @endcond }; }; #endif // D868UVCODEPLUG_HH qdmr-0.12.3/lib/d868uv_filereader.cc000066400000000000000000000422171501654372000170540ustar00rootroot00000000000000#include "d868uv_filereader.hh" #include #include "logger.hh" #include "utils.hh" #define HEADER_SIZE 0x000000e2 /* ********************************************************************************************* * * Implementation of D868UVFileReader::ChannelElement * ********************************************************************************************* */ D868UVFileReader::ChannelElement::ChannelElement(const uint8_t *ptr) : Element(ptr), _nameLength(0) { _nameLength = strnlen((char *)_data+0x30,16); } uint16_t D868UVFileReader::ChannelElement::index() const { return qFromLittleEndian(*(uint16_t *)_data + 0x00); } D868UVCodeplug::channel_t::Mode D868UVFileReader::ChannelElement::mode() const { return (D868UVCodeplug::channel_t::Mode) *(_data + 0x0b); } double D868UVFileReader::ChannelElement::rxFrequency() const { return double(qFromLittleEndian(*(uint32_t *)(_data + 0x02)))/1e5; } double D868UVFileReader::ChannelElement::txFrequency() const { double rx = double(qFromLittleEndian(*(uint32_t *)(_data + 0x02)))/1e5; double off = double(qFromLittleEndian(*(uint32_t *)(_data + 0x07)))/1e5; D868UVCodeplug::channel_t::RepeaterMode repeater_type = (D868UVCodeplug::channel_t::RepeaterMode)*(_data+0x06); switch(repeater_type) { case D868UVCodeplug::channel_t::RM_TXPOS: return rx+off; case D868UVCodeplug::channel_t::RM_TXNEG: return rx-off; case D868UVCodeplug::channel_t::RM_SIMPLEX: default: break; } return rx; } Channel::Power D868UVFileReader::ChannelElement::power() const { D868UVCodeplug::channel_t::Power pwr = (D868UVCodeplug::channel_t::Power) * (_data + 0x0c); switch(pwr) { case D868UVCodeplug::channel_t::POWER_LOW: return Channel::LowPower; case D868UVCodeplug::channel_t::POWER_MIDDLE: return Channel::MidPower; case D868UVCodeplug::channel_t::POWER_HIGH: return Channel::HighPower; case D868UVCodeplug::channel_t::POWER_TURBO: return Channel::MaxPower; default: break; } return Channel::MinPower; } AnalogChannel::Bandwidth D868UVFileReader::ChannelElement::bandwidth() const { D868UVCodeplug::channel_t::Bandwidth bw = (D868UVCodeplug::channel_t::Bandwidth) * (_data + 0x0d); return (D868UVCodeplug::channel_t::BW_25_KHZ == bw) ? AnalogChannel::BWWide : AnalogChannel::BWNarrow; } bool D868UVFileReader::ChannelElement::rxOnly() const { return *(_data + 0x0f); } Signaling::Code D868UVFileReader::ChannelElement::rxSignaling() const { ///@todo Implement. return Signaling::SIGNALING_NONE; } Signaling::Code D868UVFileReader::ChannelElement::txSignaling() const { ///@todo Implement. return Signaling::SIGNALING_NONE; } DigitalChannel::Admit D868UVFileReader::ChannelElement::admitDigital() const { D868UVCodeplug::channel_t::Admit admit = (D868UVCodeplug::channel_t::Admit) * (_data + 0x20); switch (admit) { case D868UVCodeplug::channel_t::ADMIT_CH_FREE: return DigitalChannel::AdmitFree; case D868UVCodeplug::channel_t::ADMIT_COLORCODE: return DigitalChannel::AdmitColorCode; case D868UVCodeplug::channel_t::ADMIT_ALWAYS: default: break; } return DigitalChannel::AdmitNone; } AnalogChannel::Admit D868UVFileReader::ChannelElement::admitAnalog() const { D868UVCodeplug::channel_t::Admit admit = (D868UVCodeplug::channel_t::Admit) * (_data + 0x20); switch (admit) { case D868UVCodeplug::channel_t::ADMIT_CH_FREE: return AnalogChannel::AdmitFree; case D868UVCodeplug::channel_t::ADMIT_ALWAYS: default: break; } return AnalogChannel::AdmitNone; } uint8_t D868UVFileReader::ChannelElement::colorCode() const { return *(_data + 0x28); } DigitalChannel::TimeSlot D868UVFileReader::ChannelElement::timeSlot() const { return (0==(*(_data + 0x2a))) ? DigitalChannel::TimeSlot1 : DigitalChannel::TimeSlot2; } QString D868UVFileReader::ChannelElement::name() const { return QString::fromLocal8Bit((char *)(_data + 0x30), _nameLength); } size_t D868UVFileReader::ChannelElement::size() const { return 0x30 + _nameLength+1 + 0x14; } /* ********************************************************************************************* * * Implementation of D868UVFileReader::RadioIDElement * ********************************************************************************************* */ D868UVFileReader::RadioIDElement::RadioIDElement(const uint8_t *ptr) : AnytoneFileReader::Element(ptr) { _nameLength = strnlen((char *)(_data+0x04), 16); } uint8_t D868UVFileReader::RadioIDElement::index() const { return *(_data + 0x00); } uint32_t D868UVFileReader::RadioIDElement::id() const { uint32_t a=_data[1], b=_data[2], c=_data[3]; return ((c<<16) | (b<<8) | a); } QString D868UVFileReader::RadioIDElement::name() const { return QString::fromLocal8Bit((char *)(_data+4), _nameLength); } size_t D868UVFileReader::RadioIDElement::size() const { return 4 + _nameLength+1; } /* ********************************************************************************************* * * Implementation of D868UVFileReader::ZoneElement * ********************************************************************************************* */ D868UVFileReader::ZoneElement::ZoneElement(const uint8_t *ptr) : AnytoneFileReader::Element(ptr) { _numChannels = *(_data + 0x01); _nameLength = strnlen((char *)(_data+0x02 + 2*_numChannels + 2*2), 16); } uint8_t D868UVFileReader::ZoneElement::index() const { return *(_data+0x00); } uint8_t D868UVFileReader::ZoneElement::numChannels() const { return _numChannels; } uint16_t D868UVFileReader::ZoneElement::channel(uint8_t index) const { return qFromLittleEndian(*(uint16_t *)(_data + 0x02 + 2*index)); } QString D868UVFileReader::ZoneElement::name() const { return QString::fromLocal8Bit((char *)(_data+0x02+2*_numChannels+2*2), _nameLength); } size_t D868UVFileReader::ZoneElement::size() const { return 0x02 + 2*_numChannels + 2*2 + _nameLength+1; } /* ********************************************************************************************* * * Implementation of D868UVFileReader::ScanListElement * ********************************************************************************************* */ D868UVFileReader::ScanListElement::ScanListElement(const uint8_t *ptr) : AnytoneFileReader::Element(ptr) { _nameLength = strnlen((char *)(_data+0x01), 16); _numChannels = *(_data + 0x01 + _nameLength+1 + 0x0b); } uint8_t D868UVFileReader::ScanListElement::index() const { return *(_data+0x00); } uint8_t D868UVFileReader::ScanListElement::numChannels() const { return _numChannels; } D868UVCodeplug::scanlist_t::PriChannel D868UVFileReader::ScanListElement::prioChannelSelect() const { return (D868UVCodeplug::scanlist_t::PriChannel)(* (_data + 0x01 + _nameLength+1 + 0x01)); } uint16_t D868UVFileReader::ScanListElement::prioChannel1() const { return qFromLittleEndian((uint16_t)(* (_data + 0x01 + _nameLength+1 + 0x02))); } uint16_t D868UVFileReader::ScanListElement::prioChannel2() const { return qFromLittleEndian((uint16_t)(* (_data + 0x01 + _nameLength+1 + 0x04))); } uint16_t D868UVFileReader::ScanListElement::channel(uint8_t index) const { return qFromLittleEndian(*(uint16_t *)(_data + 0x01 + _nameLength+1 + 0x0b + 0x03 + 2*index)); } QString D868UVFileReader::ScanListElement::name() const { return QString::fromLocal8Bit((char *)(_data+1), _nameLength); } size_t D868UVFileReader::ScanListElement::size() const { return 0x0f + 2*_numChannels + _nameLength+1; } /* ********************************************************************************************* * * Implementation of D868UVFileReader::AnalogContactElement * ********************************************************************************************* */ D868UVFileReader::AnalogContactElement::AnalogContactElement(const uint8_t *ptr) : AnytoneFileReader::Element(ptr) { _numberLength = *(_data+1); _nameLength = strnlen((char *)(_data+0x02+_numberLength), 16); } uint8_t D868UVFileReader::AnalogContactElement::index() const { return *(_data + 0x00); } QString D868UVFileReader::AnalogContactElement::number() const { return QString::fromLocal8Bit((char *)(_data+0x02), _numberLength); } QString D868UVFileReader::AnalogContactElement::name() const { return QString::fromLocal8Bit((char *)(_data+0x02+_numberLength), _nameLength); } size_t D868UVFileReader::AnalogContactElement::size() const { return 0x02 + _numberLength + _nameLength+1; } /* ********************************************************************************************* * * Implementation of D868UVFileReader * ********************************************************************************************* */ D868UVFileReader::D868UVFileReader(Config *config, const uint8_t *data, size_t size, QString &message) : AnytoneFileReader(config, data, size, message) { // pass... } bool D868UVFileReader::readHeader() { // Header content is ignored, only advance pointer to end of header _data += HEADER_SIZE; return true; } bool D868UVFileReader::linkHeader() { // Header content is ignored, only advance pointer to end of header _data += HEADER_SIZE; return true; } bool D868UVFileReader::readChannels() { // Read number of channels and advance pointer uint16_t numChannels = qFromLittleEndian(*(uint16_t *)_data); _data += 0x02; // Read each channel for (uint16_t i=0; ireadChannel()) return false; } return true; } bool D868UVFileReader::readChannel() { ChannelElement channel(_data); // Assemble channel Channel *ch = nullptr; if (D868UVCodeplug::channel_t::MODE_ANALOG == channel.mode()) { ch = new AnalogChannel( channel.name(), channel.rxFrequency(), channel.txFrequency(), channel.power(), 0, channel.rxOnly(), channel.admitAnalog(), 2, channel.rxSignaling(), channel.txSignaling(), channel.bandwidth(), nullptr, nullptr, nullptr); } else if (D868UVCodeplug::channel_t::MODE_DIGITAL == channel.mode()) { ch = new DigitalChannel( channel.name(), channel.rxFrequency(), channel.txFrequency(), channel.power(), 0, channel.rxOnly(), channel.admitDigital(), channel.colorCode(), channel.timeSlot(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); } // Store channel in context & config if not VFO channel if ((4000 != channel.index()) && (4001 != channel.index())) { logDebug() << QString("0x%1").arg(uint(_data-_start), 8,16,QChar('0')) << ": Add channel '" << channel.name() << "'."; _context.addChannel(ch, channel.index()); } // Advance pointer _data += channel.size(); return true; } bool D868UVFileReader::linkChannels() { // Get number of channels and advance pointer uint16_t numChannels = qFromLittleEndian(*(uint16_t *)_data); _data += 0x02; // Link each channel for (uint16_t i=0; ilinkChannel()) return false; } return true; } bool D868UVFileReader::linkChannel() { ChannelElement channel(_data); // Advance pointer _data += channel.size(); return true; } bool D868UVFileReader::readRadioIDs() { // Read number of radio IDs and advance pointer uint8_t numIDs = *_data; _data += 1; // Read each radio ID for (uint8_t i=0; ireadRadioID()) return false; } return true; } bool D868UVFileReader::readRadioID() { RadioIDElement radioid(_data); // Assemble radio ID & add to context/config if (0 == radioid.index()) { _context.setDefaultRadioId(radioid.id(), radioid.index()); _context.config()->setName(radioid.name()); logDebug() << QString("0x%1").arg(uint(_data-_start), 8,16,QChar('0')) << ": Set default radio ID " << radioid.id() << " ('" << radioid.name() << "')."; } else { _context.addRadioId(radioid.id(), radioid.index()); logDebug() << QString("0x%1").arg(uint(_data-_start), 8,16,QChar('0')) << ": Add radio ID " << radioid.id() << " ('" << radioid.name() << "')."; } // Advance pointer _data += radioid.size(); return true; } bool D868UVFileReader::linkRadioIDs() { // Read number of radio IDs and advance pointer uint8_t numIDs = *_data; _data += 1; // skip each radio ID for (uint8_t i=0; ireadZone()) return false; } return true; } bool D868UVFileReader::readZone() { ZoneElement zone(_data); // Assemble Zone & add to context/config Zone *z = new Zone(zone.name()); if (! _context.config()->zones()->addZone(z)) { _message = QObject::tr("0x%1: Cannot add zone to config.").arg(uint(_data-_start), 8,16,QChar('0')); return false; } for (uint8_t i=0; iA()->addChannel(_context.getChannel(zone.channel(i))); logDebug() << "Add channel '" << _context.getChannel(zone.channel(i))->name() << "' to zone '" << zone.name() << "'."; } logDebug() << QString("0x%1").arg(uint(_data-_start), 8,16,QChar('0')) << ": Add zone '" << zone.name() << "'."; // Advance pointer _data += zone.size(); return true; } bool D868UVFileReader::linkZones() { // Read number of zones and advance pointer uint8_t numZones = *_data; _data += 1; // Skip each zone, zones are linked during creation for (uint8_t i=0; ireadScanList()) return false; } return true; } bool D868UVFileReader::readScanList() { ScanListElement sl(_data); ScanList *scanlist = new ScanList(sl.name()); _context.addScanList(scanlist, sl.index()); logDebug() << QString("0x%1").arg(uint(_data-_start), 8,16,QChar('0')) << ": Add scan list '" << sl.name() << "'."; for (uint8_t i=0; iaddChannel(_context.getChannel(sl.channel(i))); logDebug() << "Add channel '" << _context.getChannel(sl.channel(i))->name() << "' to zone '" << sl.name() << "'."; if ((D868UVCodeplug::scanlist_t::PRIO_CHAN_SEL1 & sl.prioChannelSelect()) && (0 != sl.prioChannel1())) { if (1 == sl.prioChannel1()) scanlist->setPriorityChannel(SelectedChannel::get()); else if (_context.hasChannel(sl.prioChannel1()-2)) scanlist->setPriorityChannel(_context.getChannel(sl.prioChannel1()-2)); else logWarn() << QString("0x%1").arg(uint(_data-_start), 8,16,QChar('0')) << "Cannot link priority channel index " << (sl.prioChannel1()-2) << " in scanlist '" << sl.name() << "'."; } if ((D868UVCodeplug::scanlist_t::PRIO_CHAN_SEL2 & sl.prioChannelSelect()) && (0 != sl.prioChannel2())) { if (1 == sl.prioChannel2()) scanlist->setSecPriorityChannel(SelectedChannel::get()); else if (_context.hasChannel(sl.prioChannel2()-2)) scanlist->setSecPriorityChannel(_context.getChannel(sl.prioChannel2()-2)); else logWarn() << QString("0x%1").arg(uint(_data-_start), 8,16,QChar('0')) << "Cannot link sec. priority channel index " << (sl.prioChannel2()-2) << " in scanlist '" << sl.name() << "'."; } } return true; } bool D868UVFileReader::linkScanLists() { // Read number of scan lists and advance pointer uint8_t numScanlists = *_data; _data += 1; // Skip each scan list, scan lists are linked during creation for (uint8_t i=0; ireadAnalogContact(); } return true; } bool D868UVFileReader::readAnalogContact() { AnalogContactElement ac(_data); if (validDTMFNumber(ac.number())) { DTMFContact *contact = new DTMFContact(ac.name(), ac.number()); _context.addAnalogContact(contact, ac.index()); } else { logWarn() << QString("0x%1").arg(uint(_data-_start), 8,16,QChar('0')) << ": Skip analog contact '" << ac.name() << ". Invalid DTMF number '" << ac.number() << "'."; } _data += ac.size(); return true; } bool D868UVFileReader::linkAnalogContacts() { // Read number of scan lists and advance pointer uint8_t numContacts = *_data; _data += 1; // Skip each analog contact, analog contacts need no linking for (uint8_t i=0; i > &rxFreqRanges, const std::initializer_list > &txFreqRanges, const QString &hardwareRevision, QObject *parent) : AnytoneLimits(hardwareRevision, "V102", true, parent) { // Define limits for call-sign DB _hasCallSignDB = true; _callSignDBImplemented = true; _numCallSignDBEntries = 200000; // Define limits for satellite config _hasSatelliteConfig = false; _satelliteConfigImplemented = false; _numSatellites = 0; /* Define limits for the general settings. */ add("settings", new RadioLimitItem{ { "introLine1", new RadioLimitString(-1, 14, RadioLimitString::ASCII) }, { "introLine2", new RadioLimitString(-1, 14, RadioLimitString::ASCII) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, -1) } }); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList { { DMRRadioID::staticMetaObject, 1, 250, new RadioLimitObject { {"name", new RadioLimitString(1,8, RadioLimitString::ASCII) }, {"id", new RadioLimitUInt(0, 16777215)} } } }); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum { (unsigned)DMRContact::PrivateCall, (unsigned)DMRContact::GroupCall, (unsigned)DMRContact::AllCall }}, { "number", new RadioLimitUInt(0, 16777215) } } }, { DTMFContact::staticMetaObject, 0, 128, new RadioLimitObject { { "name", new RadioLimitString(1, 15, RadioLimitString::ASCII) }, { "number", new RadioLimitString(1, 14, RadioLimitString::DTMF) } } } }); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject { { "name", new RadioLimitString(1,16, RadioLimitString::ASCII) }, { "contacts", new RadioLimitGroupCallRefList(1, 64) } })); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, 4000, new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies(rxFreqRanges, true)}, {"txFrequency", new RadioLimitTransmitFrequencies(txFreqRanges)}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRefIgnored()} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1,16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies(rxFreqRanges, true)}, {"txFrequency", new RadioLimitTransmitFrequencies(txFreqRanges)}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false)}, {"aprs", new RadioLimitObjRef(GPSSystem::staticMetaObject, true)}, {"roaming", new RadioLimitObjRefIgnored(DefaultRoamingZone::get()) }, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } )); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone( 250, { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions }) ) ); /* Define limits for scan lists. */ add("scanlists", new RadioLimitList( ScanList::staticMetaObject, 0, 250, new RadioLimitObject{ { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "primary", new RadioLimitObjRef(Channel::staticMetaObject, false) }, { "secondary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "revert", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "channels", new RadioLimitRefList(0, 31, Channel::staticMetaObject) } })); /* Ignore positioning systems. */ add("positioning", new RadioLimitList({ { GPSSystem::staticMetaObject, 0, 1, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "period", new RadioLimitUInt(0, 7650) }, { "contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false) }, { "revert", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } }, { APRSSystem::staticMetaObject, 0, -1, new RadioLimitIgnored() } } ) ); /* Ignore roaming zones. */ add("roaming", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint) ) ); } qdmr-0.12.3/lib/d868uv_limits.hh000066400000000000000000000010171501654372000162560ustar00rootroot00000000000000#ifndef D868UVLIMITS_HH #define D868UVLIMITS_HH #include "anytone_limits.hh" /** Implements the limits for the AnyTone AT-D878UV. * @ingroup d868uv */ class D868UVLimits: public AnytoneLimits { Q_OBJECT public: /** Constructor. */ D868UVLimits(const std::initializer_list > &rxFreqRanges, const std::initializer_list > &txFreqRanges, const QString &hardwareRevision, QObject *parent=nullptr); }; #endif // D868UVLIMITS_HH qdmr-0.12.3/lib/d878uv.cc000066400000000000000000000174731501654372000147010ustar00rootroot00000000000000#include "userdatabase.hh" #include "d878uv.hh" #include "config.hh" #include "logger.hh" #include "d878uv_codeplug.hh" #include "d878uv_limits.hh" // uses same callsign db as 878 #include "d868uv_callsigndb.hh" #define RBSIZE 16 #define WBSIZE 16 D878UV::D878UV(AnytoneInterface *device, QObject *parent) : AnytoneRadio("Anytone AT-D878UV", device, parent), _limits(nullptr) { _codeplug = new D878UVCodeplug(this); _codeplug->clear(); _callsigns = new D868UVCallsignDB(this); _satellites = new AnytoneSatelliteConfig(this); // Get device info and determine supported TX frequency bands AnytoneInterface::RadioVariant info; if (_dev) _dev->getInfo(info); switch (info.bands) { case 0x00: case 0x01: _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, info.version, this); break; case 0x02: _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x03: _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x04: _limits = new D878UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} }, info.version, this); break; case 0x05: _limits = new D878UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(434.), Frequency::fromMHz(437.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(434.), Frequency::fromMHz(437.)} }, info.version, this); break; case 0x06: _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this); break; case 0x07: _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this); break; case 0x08: _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, info.version, this); break; case 0x09: _limits = new D878UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, info.version, this); break; case 0x0a: _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(450.)} }, info.version, this); break; case 0x0b: _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this); break; case 0x0c: _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} }, info.version, this); break; case 0x0d: _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} }, info.version, this); break; case 0x0e: _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(220.),Frequency::fromMHz(225.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(220.),Frequency::fromMHz(225.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this); break; case 0x0f: _limits = new D878UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} }, info.version, this); break; case 0x10: _limits = new D878UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(147.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(147.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x11: _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)} }, info.version, this); break; default: logInfo() << "Unknown band-code" << QString::number(int(info.bands), 16) << ": Do not check frequency range."; _limits = new D878UVLimits({}, {}, info.version, this); break; } } const RadioLimits & D878UV::limits() const { return *_limits; } RadioInfo D878UV::defaultRadioInfo() { return RadioInfo( RadioInfo::D878UV, "d878uv", "AT-D878UV", "AnyTone", AnytoneInterface::interfaceInfo()); } qdmr-0.12.3/lib/d878uv.hh000066400000000000000000000041201501654372000146740ustar00rootroot00000000000000/** @defgroup d878uv Anytone AT-D878UV * Device specific classes for Anytone AT-D878UV. * * \image html d878uv.jpg "AT-D878UV" width=200px * \image latex d878uv.jpg "AT-D878UV" width=200px * * @ingroup anytone */ #ifndef __D878UV_HH__ #define __D878UV_HH__ #include "anytone_radio.hh" #include "anytone_interface.hh" /** Implements an interface to Anytone AT-D878UV VHF/UHF 7W DMR (Tier I & II) radios. * * The reverse-engineering of the D878UVCodeplug was quiet hard as it is huge and the radio * provides a lot of bells and whistles. Moreover, the binary code-plug file created by the * windows CPS does not directly relate to the data being written to the device. These two issues * (a lot of features and a huge code-plug) require that the transfer of the code-plug to the * device is performed in 4 steps. * * First only the bitmaps of all lists are downloaded from the device. Then all elements that are * not touched or only updated by the common code-plug config are downloaded. Then, the common * config gets applied to the binary code-plug. That is, all channels, contacts, zones, group-lists * and scan-lists are generated and their bitmaps gets updated accordingly. Also the general config * gets updated from the common code-plug settings. Finally, the resulting binary code-plug gets * written back to the device. * * This rather complex method of writing a code-plug to the device is needed to maintain all * settings within the radio that are not defined within the common code-plug config while keeping * the amount of data being read from and written to the device small. * * @ingroup d878uv */ class D878UV: public AnytoneRadio { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit D878UV(AnytoneInterface *device=nullptr, QObject *parent=nullptr); const RadioLimits &limits() const; /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); private: RadioLimits *_limits; }; #endif // __D878UV_HH__ qdmr-0.12.3/lib/d878uv2.cc000066400000000000000000000175621501654372000147620ustar00rootroot00000000000000#include "userdatabase.hh" #include "d878uv2.hh" #include "config.hh" #include "logger.hh" #include "d878uv2_limits.hh" #include "d878uv2_codeplug.hh" #include "d878uv2_callsigndb.hh" #define RBSIZE 16 #define WBSIZE 16 D878UV2::D878UV2(AnytoneInterface *device, QObject *parent) : AnytoneRadio("Anytone AT-D878UVII", device, parent), _limits(nullptr) { _codeplug = new D878UV2Codeplug(this); _codeplug->clear(); _callsigns = new D878UV2CallsignDB(this); _satellites = new AnytoneSatelliteConfig(this); // Get device info and determine supported TX frequency bands AnytoneInterface::RadioVariant info; if (_dev) _dev->getInfo(info); switch (info.bands) { case 0x00: case 0x01: _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, info.version, this); break; case 0x02: _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x03: _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x04: _limits = new D878UV2Limits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} }, info.version, this); break; case 0x05: _limits = new D878UV2Limits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(434.), Frequency::fromMHz(437.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(434.), Frequency::fromMHz(437.)} }, info.version, this); break; case 0x06: _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this); break; case 0x07: _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this); break; case 0x08: _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, info.version, this); break; case 0x09: _limits = new D878UV2Limits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, info.version, this); break; case 0x0a: _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(450.)} }, info.version, this); break; case 0x0b: _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this); break; case 0x0c: _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} }, info.version, this); break; case 0x0d: _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} }, info.version, this); break; case 0x0e: _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(220.),Frequency::fromMHz(225.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(220.),Frequency::fromMHz(225.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this); break; case 0x0f: _limits = new D878UV2Limits({ {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} }, info.version, this); break; case 0x10: _limits = new D878UV2Limits({ {Frequency::fromMHz(144.), Frequency::fromMHz(147.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(147.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x11: _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)} }, info.version, this); break; default: logInfo() << "Unknown band-code" << QString::number(int(info.bands), 16) << ": Do not check frequency range."; _limits = new D878UV2Limits({}, {}, info.version, this); break; } } const RadioLimits & D878UV2::limits() const { return *_limits; } RadioInfo D878UV2::defaultRadioInfo() { return RadioInfo( RadioInfo::D878UVII, "d878uv2", "AT-D878UVII", "AnyTone", AnytoneInterface::interfaceInfo()); } qdmr-0.12.3/lib/d878uv2.hh000066400000000000000000000042401501654372000147610ustar00rootroot00000000000000/** @defgroup d878uv2 Anytone AT-D878UVII * Device specific classes for Anytone AT-D878UVII. * * \image html d878uv.jpg "AT-D878UV" width=200px * \image latex d878uv.jpg "AT-D878UV" width=200px * * @ingroup anytone */ #ifndef __D878UV2_HH__ #define __D878UV2_HH__ #include "anytone_radio.hh" #include "anytone_interface.hh" #include "d878uv2_callsigndb.hh" /** Implements an interface to Anytone AT-D878UVII VHF/UHF 7W DMR (Tier I & II) radios. * * The reverse-engineering of the D878UVCodeplug was quiet hard as it is huge and the radio * provides a lot of bells and whistles. Moreover, the binary code-plug file created by the * windows CPS does not directly relate to the data being written to the device. These two issues * (a lot of features and a huge code-plug) require that the transfer of the code-plug to the * device is performed in 4 steps. * * First only the bitmaps of all lists are downloaded from the device. Then all elements that are * not touched or only updated by the common code-plug config are downloaded. Then, the common * config gets applied to the binary code-plug. That is, all channels, contacts, zones, group-lists * and scan-lists are generated and their bitmaps gets updated accordingly. Also the general config * gets updated from the common code-plug settings. Finally, the resulting binary code-plug gets * written back to the device. * * This rather complex method of writing a code-plug to the device is needed to maintain all * settings within the radio that are not defined within the common code-plug config while keeping * the amount of data being read from and written to the device small. * * @ingroup d878uv2 */ class D878UV2: public AnytoneRadio { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit D878UV2(AnytoneInterface *device=nullptr, QObject *parent=nullptr); const RadioLimits &limits() const; /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); private: /** The limits for the radio. */ RadioLimits *_limits; }; #endif // __D878UV2_HH__ qdmr-0.12.3/lib/d878uv2_callsigndb.cc000066400000000000000000000077551501654372000171470ustar00rootroot00000000000000#include "gpssystem.hh" #include "userdatabase.hh" #include "d878uv2_callsigndb.hh" #include "utils.hh" #include /* ********************************************************************************************* * * Implementation of D878UVCallsignDB * ********************************************************************************************* */ D878UV2CallsignDB::D878UV2CallsignDB(QObject *parent) : D868UVCallsignDB(parent) { // pass... } bool D878UV2CallsignDB::encode(UserDatabase *db, const Selection &selection, const ErrorStack &err) { Q_UNUSED(err) // Determine size of call-sign DB in memory qint64 n = std::min(db->count(), qint64(Limit::entries())); // If DB size is limited by settings if (selection.hasCountLimit()) n = std::min(n, (qint64)selection.countLimit()); // Select n users and sort them in ascending order of their IDs QVector users; users.reserve(n); for (unsigned i=0; iuser(i)); std::sort(users.begin(), users.end(), [](const UserDatabase::User &a, const UserDatabase::User &b) { return a.id < b.id; }); // Compute total size of callsign db entries size_t dbSize = 0; size_t indexSize = n*IndexEntryElement::size(); for (qint64 i=0; i * Callsign database * Start Size Content * 04000000 variable Index of callsign entries. Follows the same * weird format as @c D868UVCodeplug::contact_map_t. Sorted by ID. Empty entries set to * 0xffffffffffffffff. * 04840000 000010 Database limits, see @c limits_t. * 05500000 variable The actual DB entries, each entry is of * variable size but shares the same header, see @c entry_t. Order arbitrary. * Filled with 0x00. * * * @ingroup d878uv2 */ class D878UV2CallsignDB : public D868UVCallsignDB { Q_OBJECT public: /** Same index entry used by the codeplug to map normal digital contacts to an contact index. Here * it maps to the byte offset within the database entries. */ typedef D868UVCodeplug::ContactMapElement IndexEntryElement; public: /** Constructor, does not allocate any memory yet. */ explicit D878UV2CallsignDB(QObject *parent=nullptr); /** Tries to encode as many entries of the given user-database. */ bool encode(UserDatabase *db, const Selection &selection=Selection(), const ErrorStack &err=ErrorStack()); public: /** Some limits of the call-sign DB. */ struct Limit : public D868UVCallsignDB::Limit { /// Specifies the max number of entries in the call-sign DB. */ static constexpr unsigned int entries() { return 500000; } }; protected: /** Some internal offsets within the call-sign DB. */ struct Offset : public D868UVCallsignDB::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int callsigns() { return 0x05500000; } static constexpr unsigned int limits() { return 0x04840000; } /// @endcond }; }; #endif // D868UVCALLSIGNDB_HH qdmr-0.12.3/lib/d878uv2_codeplug.cc000066400000000000000000000062731501654372000166410ustar00rootroot00000000000000#include "gpssystem.hh" #include "userdatabase.hh" #include "roamingchannel.hh" #include "d878uv2_codeplug.hh" #include "config.hh" #include "utils.hh" #include "channel.hh" #include "config.h" #include "logger.hh" #include #include /* ******************************************************************************************** * * Implementation of D878UV2Codeplug * ******************************************************************************************** */ D878UV2Codeplug::D878UV2Codeplug(const QString &label, QObject *parent) : D878UVCodeplug(label, parent) { // pass... } D878UV2Codeplug::D878UV2Codeplug(QObject *parent) : D878UVCodeplug("AnyTone AT-D868UVII Codeplug", parent) { // pass... } /* The address of the contact ID<->Index table has changed hence allocation and encoding must * be reimplemented. Otherwise, everything remains the same. */ void D878UV2Codeplug::allocateContacts() { /* Allocate contacts */ ContactBitmapElement contact_bitmap(data(Offset::contactBitmap())); unsigned contactCount=0; for (uint16_t i=0; i contacts; // Encode contacts and also collect id<->index map for (int i=0; icontacts()->digitalCount(); i++) { uint32_t bank_addr = Offset::contactBanks() + (i/Limit::contactsPerBank())*Offset::betweenContactBanks(); uint32_t addr = bank_addr + (i%Limit::contactsPerBank())*ContactElement::size(); ContactElement con(data(addr)); DMRContact *contact = ctx.config()->contacts()->digitalContact(i); if(! con.fromContactObj(contact, ctx)) return false; ((uint32_t *)data(Offset::contactIndex()))[i] = qToLittleEndian(i); contacts.append(contact); } // encode index map for contacts std::sort(contacts.begin(), contacts.end(), [](DMRContact *a, DMRContact *b) { return a->number() < b->number(); }); for (int i=0; inumber(), (DMRContact::GroupCall==contacts[i]->type())); el.setIndex(ctx.index(contacts[i])); } return true; } qdmr-0.12.3/lib/d878uv2_codeplug.hh000066400000000000000000000311471501654372000166510ustar00rootroot00000000000000#ifndef D878UV2_CODEPLUG_HH #define D878UV2_CODEPLUG_HH #include #include "d878uv_codeplug.hh" #include "signaling.hh" class Channel; class DMRContact; class Zone; class RXGroupList; class ScanList; class GPSSystem; /** Represents the device specific binary codeplug for Anytone AT-D878UVII radios. * * This class only implements the difference to the AT-D878UV codeplug. In fact there is only a * difference in the address of the contact ID<->Index map. * * @section d878uv2cpl Codeplug structure within radio * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Channels
      Start Size Content
      024C1500 000200 Bitmap of 4000 channels, default 0x00, 0x00 padded.
      00800000 max. 002000 Channel bank 0 of up to 128 channels, * see @c D878UVCodeplug::ChannelElement 64 b each.
      00802000 max, 002000 Unknown data, Maybe extended channel information for channel bank 0? * It is of exactly the same size as the channel bank 0. Mostly 0x00, a few 0xff.
      00840000 max. 002000 Channel bank 1 of up to 128 channels.
      00842000 max. 002000 Unknown data, related to CH bank 1?
      ... ... ...
      00FC0000 max. 000800 Channel bank 32, up to 32 channels.
      00FC2000 max. 000800 Unknown data, related to CH bank 32.
      00FC0800 000040 VFO A settings, see @c D878UVCodeplug::ChanneElement.
      00FC0840 000040 VFO B settings, see @c D878UVCodeplug::ChanneElement.
      00FC2800 000080 Unknown data, related to VFO A+B? * It is of exactly the same size as the two VFO channels. Mostly 0x00, a few 0xff. Same pattern as * the unknown data associated with channel banks.
      Zones
      Start Size Content
      024C1300 000020 Bitmap of 250 zones.
      024C1360 000020 Hidden zone bitmap of 250 zones.
      01000000 max. 01f400 250 zones channel lists of 250 16bit indices each. * 0-based, little endian, default/padded=0xffff. Offset between channel lists 0x200, size of each list 0x1f4.
      02540000 max. 001f40 250 Zone names. * Each zone name is up to 16 ASCII chars long and gets 0-padded to 32b.
      Roaming
      Start Size Content
      01042000 000020 Roaming channel bitmask, up to 250 bits, 0-padded, default 0.
      01040000 max. 0x1f40 Optional up to 250 roaming channels, of 32b each. * See @c D878UVCodeplug::RoamingChannelElement for details.
      01042080 000010 Roaming zone bitmask, up to 64 bits, 0-padded, default 0.
      01043000 max. 0x2000 Optional up to 64 roaming zones, of 128b each. * See @c D878UVCodeplug::RoamingZoneElement for details.
      Contacts
      Start Size Content
      02600000 max. 009C40 Index list of valid contacts. * 10000 32bit indices, little endian, default 0xffffffff
      02640000 000500 Contact bitmap, 10000 bit, inverted, default 0xff, 0x00 padded.
      02680000 max. 0f4240 10000 contacts, see @c AnytoneCodeplug::ContactElement. * As each contact is 100b, they do not align with the 16b blocks being transferred to the device. * Hence contacts are organized internally in groups of 4 contacts forming a "bank".
      04800000 max. 013880 DMR ID to contact index map, see @c AnytoneCodeplug::ContactMapElement. * Sorted by ID, empty entries set to 0xffffffffffffffff.
      Analog Contacts
      Start Size Content
      02900000 000080 Index list of valid analog contacts.
      02900100 000080 Bytemap for 128 analog contacts.
      02940000 max. 000180 128 analog contacts. * See @c AnytoneCodeplug::AnalogContactElement. As each analog contact is 24b, they do not * align with the 16b transfer block-size. Hence analog contacts are internally organized in * groups of 2.
      RX Group Lists
      Start Size Content
      025C0B10 000020 Bitmap of 250 RX group lists, default/padding 0x00.
      02980000 max. 000120 Grouplist 0, see @c AnytoneCodeplug::GroupListElement.
      02980200 max. 000120 Grouplist 1
      ... ... ...
      0299f200 max. 000120 Grouplist 250
      Scan lists
      Start Size Content
      024C1340 000020 Bitmap of 250 scan lists.
      01080000 000090 Bank 0, Scanlist 1, * see @c AnytoneCodeplug::ScanListElement.
      01080200 000090 Bank 0, Scanlist 2
      ... ... ...
      01081E00 000090 Bank 0, Scanlist 16
      010C0000 000090 Bank 1, Scanlist 17
      ... ... ...
      01440000 000090 Bank 15, Scanlist 241
      ... ... ...
      01441400 000090 Bank 15, Scanlist 250
      Radio IDs
      Start Size Content
      024C1320 000020 Bitmap of 250 radio IDs.
      02580000 max. 001f40 250 Radio IDs. * See @c AnytoneCodeplug::RadioIDElement.
      GPS/APRS
      Start Size Content
      02501000 000040 APRS settings, * see @c D878UVCodeplug::AnalogAPRSSettingsElement.
      02501040 000060 APRS settings, * see @c D878UVCodeplug::DMRAPRSSystemsElement.
      025010A0 000060 Extended APRS settings, * see @c D878UVCodeplug::AnalogAPRSSettingsExtensionElement.
      02501200 000040 APRS Text, up to 60 chars ASCII, 0-padded.
      02501800 000100 APRS-RX settings list up to 32 entries, 8b each. * See @c D878UVCodeplug::AnalogAPRSRXEntryElement.
      General Settings
      Start Size Content
      02500000 000100 General settings, * see @c D878UVCodeplug::GeneralSettingsElement.
      02500100 000400 Zone A & B channel list.
      02500500 000100 DTMF list
      02500600 000030 Power on settings, * see @c AnytoneCodeplug::BootSettingsElement.
      02501280 000030 General settings extension 1, * see @c D878UVCodeplug::GPSMessageElement.
      02501400 000100 General settings extension 2, * see @c D878UVCodeplug::GeneralSettingsExtensionElement.
      024C2000 0003F0 List of 250 auto-repeater offset frequencies. * 32bit little endian frequency in 10Hz. I.e., 600kHz = 60000. Default 0x00000000, 0x00 padded.
      Messages
      Start Size Content
      01640000 max. 000100 Some kind of linked list of messages. * See @c AnytoneCodeplug::MessageListElement. Each entry has a size of 0x10.
      01640800 000090 Bytemap of up to 100 valid messages. * 0x00=valid, 0xff=invalid, remaining 46b set to 0x00.
      02140000 max. 000800 Bank 0, Messages 1-8. * Each message consumes 0x100b. See @c AnytoneCodeplug::MessageElement.
      02180000 max. 000800 Bank 1, Messages 9-16
      ... ... ...
      02440000 max. 000800 Bank 12, Messages 97-100
      Hot Keys
      Start Size Content
      025C0000 000100 4 analog quick-call settings. * See @c AnytoneCodeplug::AnalogQuickCallElement.
      025C0B00 000010 Status message bitmap.
      025C0100 000400 Up to 32 status messages. * Length unknown, offset 0x20. ASCII 0x00 terminated and padded.
      025C0500 000360 18 hot-key settings, * see @c AnytoneCodeplug::HotKeyElement.
      Encryption keys
      Start Size Content
      024C1700 000040 32 Encryption IDs, 0-based, 16bit big-endian.
      024C1800 000500 32 DMR-Encryption keys, * see @c D868UVCodeplug::dmr_encryption_key_t, * 40b each.
      024C4000 004000 Up to 256 AES encryption keys. * See @c D878UVCodeplug::AESEncryptionKeyElement.
      Misc
      Start Size Content
      024C1400 000020 Alarm setting, * see @c AnytoneCodeplug::AlarmSettingElement.
      024C1440 000030 Digital alarm settings extension, * see @c AnytoneCodeplug::DigitalAlarmExtensionElement.
      FM Broadcast
      Start Size Content
      02480210 000020 Bitmap of 100 FM broadcast channels.
      02480000 max. 000200 100 FM broadcast channels. Encoded * as 8-digit BCD little-endian in 100Hz. Filled with 0x00.
      02480200 000010 FM broadcast VFO frequency. Encoded * as 8-digit BCD little-endian in 100Hz. Filled with 0x00.
      DTMF, 2-tone & 5-tone signaling.
      Start Size Content
      024C0C80 000010 5-tone encoding bitmap.
      024C0000 000020 5-tone encoding.
      024C0D00 000200 5-tone ID list.
      024C1000 000080 5-tone settings.
      024C1080 000050 DTMF settings.
      024C1280 000010 2-tone encoding bitmap.
      024C1100 000010 2-tone encoding.
      024C1290 000010 2-tone settings.
      024C2600 000010 2-tone decoding bitmap.
      024C2400 000030 2-tone decoding.
      * * @ingroup d878uv2 */ class D878UV2Codeplug : public D878UVCodeplug { Q_OBJECT protected: /** Hidden constructor. */ explicit D878UV2Codeplug(const QString &label, QObject *parent = nullptr); public: /** Empty constructor. */ explicit D878UV2Codeplug(QObject *parent = nullptr); protected: void allocateContacts(); bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Internal used offsets within the codeplug. */ struct Offset: public D878UVCodeplug::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int contactIdTable() { return 0x04800000; } /// @endcond }; }; #endif // D878UVCODEPLUG_HH qdmr-0.12.3/lib/d878uv2_limits.cc000066400000000000000000000206331501654372000163340ustar00rootroot00000000000000#include "d878uv2_limits.hh" #include "channel.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "zone.hh" #include "scanlist.hh" #include "gpssystem.hh" #include "roamingzone.hh" D878UV2Limits::D878UV2Limits(const std::initializer_list > &rxFreqRanges, const std::initializer_list > &txFreqRanges, const QString &hardwareRevision, QObject *parent) : AnytoneLimits(hardwareRevision, "V100", true, parent) { // Define limits for call-sign DB _hasCallSignDB = true; _callSignDBImplemented = true; _numCallSignDBEntries = 500000; // Define limits for satellite config _hasSatelliteConfig = true; _satelliteConfigImplemented = true; _numSatellites = 200; /* Define limits for the general settings. */ add("settings", new RadioLimitItem{ { "introLine1", new RadioLimitString(-1, 14, RadioLimitString::ASCII) }, { "introLine2", new RadioLimitString(-1, 14, RadioLimitString::ASCII) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, -1) } }); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList { { DMRRadioID::staticMetaObject, 1, 250, new RadioLimitObject { {"name", new RadioLimitString(1,8, RadioLimitString::ASCII) }, {"id", new RadioLimitUInt(0, 16777215)} } } }); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum{ (unsigned)DMRContact::PrivateCall, (unsigned)DMRContact::GroupCall, (unsigned)DMRContact::AllCall }}, { "number", new RadioLimitUInt(0, 16777215) } } }, { DTMFContact::staticMetaObject, 0, 128, new RadioLimitObject { { "name", new RadioLimitString(1, 15, RadioLimitString::ASCII) }, { "number", new RadioLimitString(1, 14, RadioLimitString::DTMF) } } } }); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "contacts", new RadioLimitGroupCallRefList(1, 64) } })); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, 4000, new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies(rxFreqRanges, true)}, {"txFrequency", new RadioLimitTransmitFrequencies(txFreqRanges)}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRef(APRSSystem::staticMetaObject)}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1,16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies(rxFreqRanges, true)}, {"txFrequency", new RadioLimitTransmitFrequencies(txFreqRanges)}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false)}, {"aprs", new RadioLimitObjRef(PositioningSystem::staticMetaObject, true)}, {"roaming", new RadioLimitObjRef(RoamingZone::staticMetaObject, true) }, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } )); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone( 250, { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions }) ) ); /* Define limits for scan lists. */ add("scanlists", new RadioLimitList( ScanList::staticMetaObject, 0, 250, new RadioLimitObject{ { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "primary", new RadioLimitObjRef(Channel::staticMetaObject, false) }, { "secondary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "revert", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "channels", new RadioLimitRefList(0, 31, Channel::staticMetaObject) } })); /* Handle positioning systems. */ add("positioning", new RadioLimitList{ { GPSSystem::staticMetaObject, 0, 8, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "period", new RadioLimitUInt(0, 7650) }, { "contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false) }, { "revert", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } }, { APRSSystem::staticMetaObject, 0, 1, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "period", new RadioLimitUInt(0, 7650) }, { "revert", new RadioLimitObjRef({SelectedChannel::staticMetaObject, FMChannel::staticMetaObject}, false) }, { "icon", new RadioLimitEnum{} }, { "message", new RadioLimitString(0, 60, RadioLimitString::ASCII) } ///@todo extend APRSSystem to expose other settings as properties. }} } ); /* Handle roaming zones. */ add("roaming", new RadioLimitList(RoamingZone::staticMetaObject, 0, 64, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "channels", new RadioLimitRefList(0, 64, DMRChannel::staticMetaObject) } } ) ); } qdmr-0.12.3/lib/d878uv2_limits.hh000066400000000000000000000010301501654372000163340ustar00rootroot00000000000000#ifndef D878UV2LIMITS_HH #define D878UV2LIMITS_HH #include "anytone_limits.hh" /** Implements the limits for the AnyTone AT-D878UV2. * @ingroup d878uv2 */ class D878UV2Limits: public AnytoneLimits { Q_OBJECT public: /** Constructor. */ D878UV2Limits(const std::initializer_list > &rxFreqRanges, const std::initializer_list > &txFreqRanges, const QString &hardwareRevision, QObject *parent=nullptr); }; #endif // D878UV2LIMITS_HH qdmr-0.12.3/lib/d878uv_codeplug.cc000066400000000000000000004252341501654372000165610ustar00rootroot00000000000000#include "gpssystem.hh" #include "userdatabase.hh" #include "roamingchannel.hh" #include "d878uv_codeplug.hh" #include "config.hh" #include "utils.hh" #include "channel.hh" #include "config.h" #include "logger.hh" #include "channel.hh" #include #include /* ******************************************************************************************** * * Implementation of D878UVCodeplug::NameColor * ******************************************************************************************** */ AnytoneDisplaySettingsExtension::Color D878UVCodeplug::NameColor::decode(uint8_t code) { switch((CodedColor) code) { case White: return AnytoneDisplaySettingsExtension::Color::White; case Orange: return AnytoneDisplaySettingsExtension::Color::Orange; case Red: return AnytoneDisplaySettingsExtension::Color::Red; case Yellow: return AnytoneDisplaySettingsExtension::Color::Yellow; case Green: return AnytoneDisplaySettingsExtension::Color::Green; case Turquoise: return AnytoneDisplaySettingsExtension::Color::Turquoise; case Blue: return AnytoneDisplaySettingsExtension::Color::Blue; default: break; } return AnytoneDisplaySettingsExtension::Color::White; } uint8_t D878UVCodeplug::NameColor::encode(AnytoneDisplaySettingsExtension::Color color) { switch(color) { case AnytoneDisplaySettingsExtension::Color::White: return (uint8_t) White; case AnytoneDisplaySettingsExtension::Color::Orange: return (uint8_t) Orange; case AnytoneDisplaySettingsExtension::Color::Red: return (uint8_t) Red; case AnytoneDisplaySettingsExtension::Color::Yellow: return (uint8_t) Yellow; case AnytoneDisplaySettingsExtension::Color::Green: return (uint8_t) Green; case AnytoneDisplaySettingsExtension::Color::Turquoise: return (uint8_t) Turquoise; case AnytoneDisplaySettingsExtension::Color::Blue: return (uint8_t) Blue; default: break; } return (uint8_t) White; } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::TextColor * ******************************************************************************************** */ AnytoneDisplaySettingsExtension::Color D878UVCodeplug::TextColor::decode(uint8_t code) { switch((CodedColor) code) { case White: return AnytoneDisplaySettingsExtension::Color::White; case Orange: return AnytoneDisplaySettingsExtension::Color::Orange; case Red: return AnytoneDisplaySettingsExtension::Color::Red; case Yellow: return AnytoneDisplaySettingsExtension::Color::Yellow; case Green: return AnytoneDisplaySettingsExtension::Color::Green; case Turquoise: return AnytoneDisplaySettingsExtension::Color::Turquoise; case Blue: return AnytoneDisplaySettingsExtension::Color::Blue; default: break; } return AnytoneDisplaySettingsExtension::Color::White; } uint8_t D878UVCodeplug::TextColor::encode(AnytoneDisplaySettingsExtension::Color color) { switch(color) { case AnytoneDisplaySettingsExtension::Color::White: return (uint8_t) White; case AnytoneDisplaySettingsExtension::Color::Orange: return (uint8_t) Orange; case AnytoneDisplaySettingsExtension::Color::Red: return (uint8_t) Red; case AnytoneDisplaySettingsExtension::Color::Yellow: return (uint8_t) Yellow; case AnytoneDisplaySettingsExtension::Color::Green: return (uint8_t) Green; case AnytoneDisplaySettingsExtension::Color::Turquoise: return (uint8_t) Turquoise; case AnytoneDisplaySettingsExtension::Color::Blue: return (uint8_t) Blue; default: break; } return (uint8_t) White; } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::ChannelElement * ******************************************************************************************** */ D878UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, unsigned size) : D868UVCodeplug::ChannelElement(ptr, size) { // pass... } D878UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr) : D868UVCodeplug::ChannelElement(ptr, ChannelElement::size()) { // pass... } void D878UVCodeplug::ChannelElement::clear() { D868UVCodeplug::ChannelElement::clear(); setPTTIDSetting(PTTId::Off); } D878UVCodeplug::ChannelElement::PTTId D878UVCodeplug::ChannelElement::pttIDSetting() const { return (PTTId)getUInt2(Offset::pttIDSetting(), 0); } void D878UVCodeplug::ChannelElement::setPTTIDSetting(PTTId ptt) { setUInt2(Offset::pttIDSetting(), 0, (unsigned)ptt); } bool D878UVCodeplug::ChannelElement::roamingEnabled() const { // inverted return !getBit(Offset::roamingEnabled(), 2); } void D878UVCodeplug::ChannelElement::enableRoaming(bool enable) { // inverted setBit(Offset::roamingEnabled(), 2, !enable); } bool D878UVCodeplug::ChannelElement::dataACK() const { // inverted return !getBit(Offset::dataACK(), 3); } void D878UVCodeplug::ChannelElement::enableDataACK(bool enable) { // inverted setBit(Offset::dataACK(), 3, !enable); } bool D878UVCodeplug::ChannelElement::txDigitalAPRS() const { return 2 == getUInt2(Offset::txDMRAPRS(), 0); } void D878UVCodeplug::ChannelElement::enableTXDigitalAPRS(bool enable) { setUInt2(Offset::txDMRAPRS(), 0, (enable ? 0x02 : 0x00)); } bool D878UVCodeplug::ChannelElement::txAnalogAPRS() const { return 1 == getUInt2(Offset::txDMRAPRS(), 0); } void D878UVCodeplug::ChannelElement::enableTXAnalogAPRS(bool enable) { setUInt2(Offset::txDMRAPRS(), 0, (enable ? 0x01 : 0x00)); } D878UVCodeplug::ChannelElement::APRSPTT D878UVCodeplug::ChannelElement::analogAPRSPTTSetting() const { return (APRSPTT)getUInt8(Offset::fmAPRSPTTSetting()); } void D878UVCodeplug::ChannelElement::setAnalogAPRSPTTSetting(APRSPTT ptt) { setUInt8(Offset::fmAPRSPTTSetting(), (unsigned)ptt); } D878UVCodeplug::ChannelElement::APRSPTT D878UVCodeplug::ChannelElement::digitalAPRSPTTSetting() const { return (APRSPTT)getUInt8(Offset::dmrAPRSPTTSetting()); } void D878UVCodeplug::ChannelElement::setDigitalAPRSPTTSetting(APRSPTT ptt) { setUInt8(Offset::dmrAPRSPTTSetting(), (unsigned)ptt); } unsigned D878UVCodeplug::ChannelElement::digitalAPRSSystemIndex() const { return getUInt8(Offset::dmrAPRSSystemIndex()); } void D878UVCodeplug::ChannelElement::setDigitalAPRSSystemIndex(unsigned idx) { setUInt8(Offset::dmrAPRSSystemIndex(), idx); } int D878UVCodeplug::ChannelElement::frequenyCorrection() const { return ((int)getInt8(Offset::frequenyCorrection()))*10; } void D878UVCodeplug::ChannelElement::setFrequencyCorrection(int corr) { setInt8(Offset::frequenyCorrection(), corr/10); } unsigned int D878UVCodeplug::ChannelElement::fmAPRSFrequencyIndex() const { return getUInt8(Offset::fmAPRSFrequencyIndex()); } void D878UVCodeplug::ChannelElement::setFMAPRSFrequencyIndex(unsigned int idx) { setUInt8(Offset::fmAPRSFrequencyIndex(), std::min(7U, idx)); } Channel * D878UVCodeplug::ChannelElement::toChannelObj(Context &ctx) const { Channel *ch = D868UVCodeplug::ChannelElement::toChannelObj(ctx); if (nullptr == ch) return nullptr; // Get extensions AnytoneChannelExtension *ext = nullptr; if (DMRChannel *dch = ch->as()) { ext = dch->anytoneChannelExtension(); } else if (FMChannel *fch = ch->as()){ ext = fch->anytoneChannelExtension(); } // If extension is present, update if (nullptr != ext) { ext->setFrequencyCorrection(frequenyCorrection()); // Decode APRS PTT setting. if (txAnalogAPRS()) { switch(analogAPRSPTTSetting()) { case APRSPTT::Off: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Off); break; case APRSPTT::Start: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Start); break; case APRSPTT::End: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::End); break; } } else if (txDigitalAPRS()) { switch(digitalAPRSPTTSetting()) { case APRSPTT::Off: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Off); break; case APRSPTT::Start: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Start); break; case APRSPTT::End: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::End); break; } } } return ch; } bool D878UVCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx) const { if (! D868UVCodeplug::ChannelElement::linkChannelObj(c, ctx)) return false; if (c->is()) { DMRChannel *dc = c->as(); // Link to GPS system if (txDigitalAPRS() && ctx.has(digitalAPRSSystemIndex())) dc->setAPRSObj(ctx.get(digitalAPRSSystemIndex())); // Link APRS system if one is defined // There can only be one active APRS system, hence the index is fixed to one. if (txAnalogAPRS() && ctx.has(0)) dc->setAPRSObj(ctx.get(0)); // If roaming is not disabled -> link to default roaming zone if (roamingEnabled()) dc->setRoamingZone(DefaultRoamingZone::get()); if (auto *ext = dc->anytoneChannelExtension()) { // If not default FM APRS frequency if (0 != fmAPRSFrequencyIndex()) { if (ctx.has(fmAPRSFrequencyIndex())) ext->fmAPRSFrequency()->set(ctx.get(fmAPRSFrequencyIndex())); } } } else if (c->is()) { FMChannel *ac = c->as(); // Link APRS system if one is defined // There can only be one active APRS system, hence the index is fixed to one. if (txAnalogAPRS() && ctx.has(0)) ac->setAPRSSystem(ctx.get(0)); if (auto *ext = ac->anytoneChannelExtension()) { // If not default FM APRS frequency if (0 != fmAPRSFrequencyIndex()) { if (ctx.has(fmAPRSFrequencyIndex())) ext->fmAPRSFrequency()->set(ctx.get(fmAPRSFrequencyIndex())); } } } return true; } bool D878UVCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) { if (! D868UVCodeplug::ChannelElement::fromChannelObj(c, ctx)) return false; AnytoneChannelExtension *ch_ext = nullptr; if (const DMRChannel *dc = c->as()) { // Set GPS system index enableRXAPRS(false); if (dc->aprsObj() && dc->aprsObj()->is()) { enableRXAPRS(true); enableTXDigitalAPRS(true); setDigitalAPRSSystemIndex(ctx.index(dc->aprsObj()->as())); } else if (dc->aprsObj() && dc->aprsObj()->is()) { enableTXAnalogAPRS(true); } // Enable roaming if (dc->roaming()) enableRoaming(true); // Apply extension settings, if present if (AnytoneDMRChannelExtension *ext = dc->anytoneChannelExtension()) { ch_ext = ext; /// Handles bug in AnyTone firmware. /// @todo Remove once fixed by AnyTone. enableRXAPRS(! ext->sms()); } } else if (const FMChannel *ac = c->as()) { // Set APRS system enableRXAPRS(false); if (nullptr != ac->aprsSystem()) { enableTXAnalogAPRS(true); if (ac == ac->aprsSystem()->revertChannel()) { enableRXAPRS(true); } } // Apply extension settings if (AnytoneFMChannelExtension *ext = ac->anytoneChannelExtension()) { ch_ext = ext; if (! ext->fmAPRSFrequency()->isNull()) { int idx = ctx.index(ext->fmAPRSFrequency()->as()); if ((0 <= idx) && (7 >= idx)) setFMAPRSFrequencyIndex(idx); else setFMAPRSFrequencyIndex(0); } else { // Use default setFMAPRSFrequencyIndex(0); } } } // Apply common channel extension if (nullptr != ch_ext) { setFrequencyCorrection(ch_ext->frequencyCorrection()); if (txDigitalAPRS()) { switch(ch_ext->aprsPTT()) { case AnytoneChannelExtension::APRSPTT::Off: setDigitalAPRSPTTSetting(APRSPTT::Off); break; case AnytoneChannelExtension::APRSPTT::Start: setDigitalAPRSPTTSetting(APRSPTT::Start); break; case AnytoneChannelExtension::APRSPTT::End: setDigitalAPRSPTTSetting(APRSPTT::End); break; } } else if (txAnalogAPRS()) { switch(ch_ext->aprsPTT()) { case AnytoneChannelExtension::APRSPTT::Off: setAnalogAPRSPTTSetting(APRSPTT::Off); break; case AnytoneChannelExtension::APRSPTT::Start: setAnalogAPRSPTTSetting(APRSPTT::Start); break; case AnytoneChannelExtension::APRSPTT::End: setAnalogAPRSPTTSetting(APRSPTT::End); break; } } } return true; } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::ChannelExtensionElement * ******************************************************************************************** */ D878UVCodeplug::ChannelExtensionElement::ChannelExtensionElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } D878UVCodeplug::ChannelExtensionElement::ChannelExtensionElement(uint8_t *ptr) : Element(ptr, size()) { /// pass... } void D878UVCodeplug::ChannelExtensionElement::clear() { Element::clear(); memset(_data, 0, size()); } unsigned int D878UVCodeplug::ChannelExtensionElement::bot5ToneIDIndex() const { return getUInt8(Offset::bot5ToneIDIndex()); } void D878UVCodeplug::ChannelExtensionElement::setBOT5ToneIDIndex(unsigned int idx) { setUInt8(Offset::bot5ToneIDIndex(), idx); } unsigned int D878UVCodeplug::ChannelExtensionElement::eot5ToneIDIndex() const { return getUInt8(Offset::eot5ToneIDIndex()); } void D878UVCodeplug::ChannelExtensionElement::setEOT5ToneIDIndex(unsigned int idx) { setUInt8(Offset::eot5ToneIDIndex(), idx); } unsigned int D878UVCodeplug::ChannelExtensionElement::txColorCode() const { return getUInt8(Offset::txColorCode()); } void D878UVCodeplug::ChannelExtensionElement::setTXColorCode(unsigned int cc) { setUInt8(Offset::txColorCode(), cc); } bool D878UVCodeplug::ChannelExtensionElement::updateChannelObj(Channel *c, Context &ctx) const { Q_UNUSED(c); Q_UNUSED(ctx); return true; } bool D878UVCodeplug::ChannelExtensionElement::linkChannelObj(Channel *c, Context &ctx) const { Q_UNUSED(c); Q_UNUSED(ctx); return true; } bool D878UVCodeplug::ChannelExtensionElement::fromChannelObj(const Channel *c, Context &ctx) { Q_UNUSED(ctx); if (c->is()) { auto dmr = c->as(); setTXColorCode(dmr->colorCode()); } return true; } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::FMAPRSFrequencyNamesElement * ******************************************************************************************** */ D878UVCodeplug::FMAPRSFrequencyNamesElement::FMAPRSFrequencyNamesElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } D878UVCodeplug::FMAPRSFrequencyNamesElement::FMAPRSFrequencyNamesElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } void D878UVCodeplug::FMAPRSFrequencyNamesElement::clear() { memset(_data, 0xff, size()); } QString D878UVCodeplug::FMAPRSFrequencyNamesElement::name(unsigned int n) const { n = std::min(n, 7U); return readASCII(n*Offset::betweenNames(), Limit::nameLength(), 0xff); } void D878UVCodeplug::FMAPRSFrequencyNamesElement::setName(unsigned int n, const QString &name) { n = std::min(n, 7U); writeASCII(n*Offset::betweenNames(), name, Limit::nameLength(), 0xff); } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::RoamingChannelElement * ******************************************************************************************** */ D878UVCodeplug::RoamingChannelElement::RoamingChannelElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } D878UVCodeplug::RoamingChannelElement::RoamingChannelElement(uint8_t *ptr) : Element(ptr, RoamingChannelElement::size()) { // pass... } void D878UVCodeplug::RoamingChannelElement::clear() { memset(_data, 0x00, _size); } unsigned D878UVCodeplug::RoamingChannelElement::rxFrequency() const { return getBCD8_be(Offset::rxFrequency())*10; } void D878UVCodeplug::RoamingChannelElement::setRXFrequency(unsigned hz) { setBCD8_be(Offset::rxFrequency(), hz/10); } unsigned D878UVCodeplug::RoamingChannelElement::txFrequency() const { return getBCD8_be(Offset::txFrequency())*10; } void D878UVCodeplug::RoamingChannelElement::setTXFrequency(unsigned hz) { setBCD8_be(Offset::txFrequency(), hz/10); } bool D878UVCodeplug::RoamingChannelElement::hasColorCode() const { return ColorCodeValue::Disabled == getUInt8(Offset::colorCode()); } unsigned D878UVCodeplug::RoamingChannelElement::colorCode() const { return std::min(15u, (unsigned)getUInt8(Offset::colorCode())); } void D878UVCodeplug::RoamingChannelElement::setColorCode(unsigned cc) { setUInt8(Offset::colorCode(), cc); } void D878UVCodeplug::RoamingChannelElement::disableColorCode() { setUInt8(Offset::colorCode(), ColorCodeValue::Disabled); } DMRChannel::TimeSlot D878UVCodeplug::RoamingChannelElement::timeSlot() const { switch (getUInt8(Offset::timeSlot())) { case TimeSlotValue::TS1: return DMRChannel::TimeSlot::TS1; case TimeSlotValue::TS2: return DMRChannel::TimeSlot::TS2; } return DMRChannel::TimeSlot::TS1; } void D878UVCodeplug::RoamingChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) { switch (ts) { case DMRChannel::TimeSlot::TS1: setUInt8(Offset::timeSlot(), TimeSlotValue::TS1); break; case DMRChannel::TimeSlot::TS2: setUInt8(Offset::timeSlot(), TimeSlotValue::TS2); break; } } QString D878UVCodeplug::RoamingChannelElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0x00); } void D878UVCodeplug::RoamingChannelElement::setName(const QString &name) { writeASCII(Offset::name(), name, Limit::nameLength(), 0x00); } bool D878UVCodeplug::RoamingChannelElement::fromChannel(const RoamingChannel* ch) { setName(ch->name()); setRXFrequency(ch->rxFrequency().inHz()); setTXFrequency(ch->txFrequency().inHz()); if (ch->colorCodeOverridden()) setColorCode(ch->colorCode()); else disableColorCode(); setTimeSlot(ch->timeSlot()); return true; } RoamingChannel * D878UVCodeplug::RoamingChannelElement::toChannel(Context &ctx) { RoamingChannel *roam = new RoamingChannel(); roam->setName(name()); roam->setRXFrequency(Frequency::fromHz(rxFrequency())); roam->setTXFrequency(Frequency::fromHz(txFrequency())); if (hasColorCode()) roam->setColorCode(colorCode()); else roam->overrideColorCode(false); roam->overrideTimeSlot(true); roam->setTimeSlot(timeSlot()); ctx.config()->roamingChannels()->add(roam); return roam; } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::RoamingChannelBitmapElement * ******************************************************************************************** */ D878UVCodeplug::RoamingChannelBitmapElement::RoamingChannelBitmapElement(uint8_t *ptr, size_t size) : BitmapElement(ptr, size) { // pass... } D878UVCodeplug::RoamingChannelBitmapElement::RoamingChannelBitmapElement(uint8_t *ptr) : BitmapElement(ptr, RoamingChannelBitmapElement::size()) { // pass... } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::RoamingZoneElement * ******************************************************************************************** */ D878UVCodeplug::RoamingZoneElement::RoamingZoneElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } D878UVCodeplug::RoamingZoneElement::RoamingZoneElement(uint8_t *ptr) : Element(ptr, RoamingZoneElement::size()) { // pass... } void D878UVCodeplug::RoamingZoneElement::clear() { memset(_data, 0x00, _size); memset(_data+Offset::members(), 0xff, Limit::numMembers()); } bool D878UVCodeplug::RoamingZoneElement::hasMember(unsigned n) const { return (0xff != member(n)); } unsigned D878UVCodeplug::RoamingZoneElement::member(unsigned n) const { return getUInt8(Offset::members() + n*Offset::betweenMembers()); } void D878UVCodeplug::RoamingZoneElement::setMember(unsigned n, unsigned idx) { if (n >= Limit::numMembers()) return; setUInt8(Offset::members() + n*Offset::betweenMembers(), idx); } void D878UVCodeplug::RoamingZoneElement::clearMember(unsigned n) { if (n >= Limit::numMembers()) return; setMember(Offset::members() + n*Offset::betweenMembers(), 0xff); } QString D878UVCodeplug::RoamingZoneElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0x00); } void D878UVCodeplug::RoamingZoneElement::setName(const QString &name) { writeASCII(Offset::name(), name, Limit::nameLength(), 0x00); } bool D878UVCodeplug::RoamingZoneElement::fromRoamingZone(RoamingZone *zone, Context &ctx, const ErrorStack& err) { Q_UNUSED(err) clear(); setName(zone->name()); for (unsigned int i=0; icount()); i++) { setMember(i, ctx.index(zone->channel(i))); } return true; } RoamingZone * D878UVCodeplug::RoamingZoneElement::toRoamingZone(Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx); Q_UNUSED(err); return new RoamingZone(name()); } bool D878UVCodeplug::RoamingZoneElement::linkRoamingZone(RoamingZone *zone, Context &ctx, const ErrorStack &err) { for (uint8_t i=0; (i(member(i))) { zone->addChannel(ctx.get(member(i))); } else { errMsg(err) << "Cannot link roaming zone '" << zone->name() << "': Roaming channel index " << member(i) << " is not defined."; return false; } } return true; } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::RoamingZoneBitmapElement * ******************************************************************************************** */ D878UVCodeplug::RoamingZoneBitmapElement::RoamingZoneBitmapElement(uint8_t *ptr, size_t size) : BitmapElement(ptr, size) { // pass... } D878UVCodeplug::RoamingZoneBitmapElement::RoamingZoneBitmapElement(uint8_t *ptr) : BitmapElement(ptr, RoamingZoneBitmapElement::size()) { // pass... } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::GeneralSettingsElement::KeyFunction * ******************************************************************************************** */ uint8_t D878UVCodeplug::GeneralSettingsElement::KeyFunction::encode(AnytoneKeySettingsExtension::KeyFunction func) { switch (func) { case AnytoneKeySettingsExtension::KeyFunction::Off: return (uint8_t)KeyFunction::Off; case AnytoneKeySettingsExtension::KeyFunction::Voltage: return (uint8_t)KeyFunction::Voltage; case AnytoneKeySettingsExtension::KeyFunction::Power: return (uint8_t)KeyFunction::Power; case AnytoneKeySettingsExtension::KeyFunction::Repeater: return (uint8_t)KeyFunction::Repeater; case AnytoneKeySettingsExtension::KeyFunction::Reverse: return (uint8_t)KeyFunction::Reverse; case AnytoneKeySettingsExtension::KeyFunction::Encryption: return (uint8_t)KeyFunction::Encryption; case AnytoneKeySettingsExtension::KeyFunction::Call: return (uint8_t)KeyFunction::Call; case AnytoneKeySettingsExtension::KeyFunction::VOX: return (uint8_t)KeyFunction::VOX; case AnytoneKeySettingsExtension::KeyFunction::ToggleVFO: return (uint8_t)KeyFunction::ToggleVFO; case AnytoneKeySettingsExtension::KeyFunction::SubPTT: return (uint8_t)KeyFunction::SubPTT; case AnytoneKeySettingsExtension::KeyFunction::Scan: return (uint8_t)KeyFunction::Scan; case AnytoneKeySettingsExtension::KeyFunction::WFM: return (uint8_t)KeyFunction::WFM; case AnytoneKeySettingsExtension::KeyFunction::Alarm: return (uint8_t)KeyFunction::Alarm; case AnytoneKeySettingsExtension::KeyFunction::RecordSwitch: return (uint8_t)KeyFunction::RecordSwitch; case AnytoneKeySettingsExtension::KeyFunction::Record: return (uint8_t)KeyFunction::Record; case AnytoneKeySettingsExtension::KeyFunction::SMS: return (uint8_t)KeyFunction::SMS; case AnytoneKeySettingsExtension::KeyFunction::Dial: return (uint8_t)KeyFunction::Dial; case AnytoneKeySettingsExtension::KeyFunction::Monitor: return (uint8_t)KeyFunction::Monitor; case AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel: return (uint8_t)KeyFunction::ToggleMainChannel; case AnytoneKeySettingsExtension::KeyFunction::HotKey1: return (uint8_t)KeyFunction::HotKey1; case AnytoneKeySettingsExtension::KeyFunction::HotKey2: return (uint8_t)KeyFunction::HotKey2; case AnytoneKeySettingsExtension::KeyFunction::HotKey3: return (uint8_t)KeyFunction::HotKey3; case AnytoneKeySettingsExtension::KeyFunction::HotKey4: return (uint8_t)KeyFunction::HotKey4; case AnytoneKeySettingsExtension::KeyFunction::HotKey5: return (uint8_t)KeyFunction::HotKey5; case AnytoneKeySettingsExtension::KeyFunction::HotKey6: return (uint8_t)KeyFunction::HotKey6; case AnytoneKeySettingsExtension::KeyFunction::WorkAlone: return (uint8_t)KeyFunction::WorkAlone; case AnytoneKeySettingsExtension::KeyFunction::SkipChannel: return (uint8_t)KeyFunction::SkipChannel; case AnytoneKeySettingsExtension::KeyFunction::DMRMonitor: return (uint8_t)KeyFunction::DMRMonitor; case AnytoneKeySettingsExtension::KeyFunction::SubChannel: return (uint8_t)KeyFunction::SubChannel; case AnytoneKeySettingsExtension::KeyFunction::PriorityZone: return (uint8_t)KeyFunction::PriorityZone; case AnytoneKeySettingsExtension::KeyFunction::VFOScan: return (uint8_t)KeyFunction::VFOScan; case AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality: return (uint8_t)KeyFunction::MICSoundQuality; case AnytoneKeySettingsExtension::KeyFunction::LastCallReply: return (uint8_t)KeyFunction::LastCallReply; case AnytoneKeySettingsExtension::KeyFunction::ChannelType: return (uint8_t)KeyFunction::ChannelType; case AnytoneKeySettingsExtension::KeyFunction::Roaming: return (uint8_t)KeyFunction::Roaming; case AnytoneKeySettingsExtension::KeyFunction::ChannelRanging: return (uint8_t)KeyFunction::ChannelRanging; case AnytoneKeySettingsExtension::KeyFunction::MaxVolume: return (uint8_t)KeyFunction::MaxVolume; case AnytoneKeySettingsExtension::KeyFunction::Slot: return (uint8_t)KeyFunction::Slot; case AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch: return (uint8_t)KeyFunction::APRSType; case AnytoneKeySettingsExtension::KeyFunction::Zone: return (uint8_t)KeyFunction::Zone; case AnytoneKeySettingsExtension::KeyFunction::RoamingSet: return (uint8_t)KeyFunction::RoamingSet; case AnytoneKeySettingsExtension::KeyFunction::APRSSet: return (uint8_t)KeyFunction::APRSSet; case AnytoneKeySettingsExtension::KeyFunction::Mute: return (uint8_t)KeyFunction::Mute; case AnytoneKeySettingsExtension::KeyFunction::CtcssDcsSet: return (uint8_t)KeyFunction::CtcssDcsSet; case AnytoneKeySettingsExtension::KeyFunction::TBSTSend: return (uint8_t)KeyFunction::TBSTSend; case AnytoneKeySettingsExtension::KeyFunction::Bluetooth: return (uint8_t)KeyFunction::Bluetooth; case AnytoneKeySettingsExtension::KeyFunction::ChannelName: return (uint8_t)KeyFunction::ChannelName; case AnytoneKeySettingsExtension::KeyFunction::CDTScan: return (uint8_t)KeyFunction::CDTScan; case AnytoneKeySettingsExtension::KeyFunction::APRSSend: return (uint8_t)KeyFunction::APRSSend; case AnytoneKeySettingsExtension::KeyFunction::APRSInfo: return (uint8_t)KeyFunction::APRSInfo; default: return (uint8_t)KeyFunction::Off; } } AnytoneKeySettingsExtension::KeyFunction D878UVCodeplug::GeneralSettingsElement::KeyFunction::decode(uint8_t code) { switch ((KeyFunctionCode)code) { case KeyFunction::Off: return AnytoneKeySettingsExtension::KeyFunction::Off; case KeyFunction::Voltage: return AnytoneKeySettingsExtension::KeyFunction::Voltage; case KeyFunction::Power: return AnytoneKeySettingsExtension::KeyFunction::Power; case KeyFunction::Repeater: return AnytoneKeySettingsExtension::KeyFunction::Repeater; case KeyFunction::Reverse: return AnytoneKeySettingsExtension::KeyFunction::Reverse; case KeyFunction::Encryption: return AnytoneKeySettingsExtension::KeyFunction::Encryption; case KeyFunction::Call: return AnytoneKeySettingsExtension::KeyFunction::Call; case KeyFunction::VOX: return AnytoneKeySettingsExtension::KeyFunction::VOX; case KeyFunction::ToggleVFO: return AnytoneKeySettingsExtension::KeyFunction::ToggleVFO; case KeyFunction::SubPTT: return AnytoneKeySettingsExtension::KeyFunction::SubPTT; case KeyFunction::Scan: return AnytoneKeySettingsExtension::KeyFunction::Scan; case KeyFunction::WFM: return AnytoneKeySettingsExtension::KeyFunction::WFM; case KeyFunction::Alarm: return AnytoneKeySettingsExtension::KeyFunction::Alarm; case KeyFunction::RecordSwitch: return AnytoneKeySettingsExtension::KeyFunction::RecordSwitch; case KeyFunction::Record: return AnytoneKeySettingsExtension::KeyFunction::Record; case KeyFunction::SMS: return AnytoneKeySettingsExtension::KeyFunction::SMS; case KeyFunction::Dial: return AnytoneKeySettingsExtension::KeyFunction::Dial; case KeyFunction::Monitor: return AnytoneKeySettingsExtension::KeyFunction::Monitor; case KeyFunction::ToggleMainChannel: return AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel; case KeyFunction::HotKey1: return AnytoneKeySettingsExtension::KeyFunction::HotKey1; case KeyFunction::HotKey2: return AnytoneKeySettingsExtension::KeyFunction::HotKey2; case KeyFunction::HotKey3: return AnytoneKeySettingsExtension::KeyFunction::HotKey3; case KeyFunction::HotKey4: return AnytoneKeySettingsExtension::KeyFunction::HotKey4; case KeyFunction::HotKey5: return AnytoneKeySettingsExtension::KeyFunction::HotKey5; case KeyFunction::HotKey6: return AnytoneKeySettingsExtension::KeyFunction::HotKey6; case KeyFunction::WorkAlone: return AnytoneKeySettingsExtension::KeyFunction::WorkAlone; case KeyFunction::SkipChannel: return AnytoneKeySettingsExtension::KeyFunction::SkipChannel; case KeyFunction::DMRMonitor: return AnytoneKeySettingsExtension::KeyFunction::DMRMonitor; case KeyFunction::SubChannel: return AnytoneKeySettingsExtension::KeyFunction::SubChannel; case KeyFunction::PriorityZone: return AnytoneKeySettingsExtension::KeyFunction::PriorityZone; case KeyFunction::VFOScan: return AnytoneKeySettingsExtension::KeyFunction::VFOScan; case KeyFunction::MICSoundQuality: return AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality; case KeyFunction::LastCallReply: return AnytoneKeySettingsExtension::KeyFunction::LastCallReply; case KeyFunction::ChannelType: return AnytoneKeySettingsExtension::KeyFunction::ChannelType; case KeyFunction::Roaming: return AnytoneKeySettingsExtension::KeyFunction::Roaming; case KeyFunction::ChannelRanging: return AnytoneKeySettingsExtension::KeyFunction::ChannelRanging; case KeyFunction::MaxVolume: return AnytoneKeySettingsExtension::KeyFunction::MaxVolume; case KeyFunction::Slot: return AnytoneKeySettingsExtension::KeyFunction::Slot; case KeyFunction::APRSType: return AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch; case KeyFunction::Zone: return AnytoneKeySettingsExtension::KeyFunction::Zone; case KeyFunction::RoamingSet: return AnytoneKeySettingsExtension::KeyFunction::RoamingSet; case KeyFunction::APRSSet: return AnytoneKeySettingsExtension::KeyFunction::APRSSet; case KeyFunction::Mute: return AnytoneKeySettingsExtension::KeyFunction::Mute; case KeyFunction::CtcssDcsSet: return AnytoneKeySettingsExtension::KeyFunction::CtcssDcsSet; case KeyFunction::TBSTSend: return AnytoneKeySettingsExtension::KeyFunction::TBSTSend; case KeyFunction::Bluetooth: return AnytoneKeySettingsExtension::KeyFunction::Bluetooth; case KeyFunction::GPS: return AnytoneKeySettingsExtension::KeyFunction::GPS; case KeyFunction::ChannelName: return AnytoneKeySettingsExtension::KeyFunction::ChannelName; case KeyFunction::CDTScan: return AnytoneKeySettingsExtension::KeyFunction::CDTScan; case KeyFunction::APRSSend: return AnytoneKeySettingsExtension::KeyFunction::APRSSend; case KeyFunction::APRSInfo: return AnytoneKeySettingsExtension::KeyFunction::APRSInfo; default: return AnytoneKeySettingsExtension::KeyFunction::Off; } } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::GeneralSettingsElement::TimeZone * ******************************************************************************************** */ QVector D878UVCodeplug::GeneralSettingsElement::TimeZone::_timeZones = { QTimeZone(-43200), QTimeZone(-39600), QTimeZone(-36000), QTimeZone(-32400), QTimeZone(-28800), QTimeZone(-25200), QTimeZone(-21600), QTimeZone(-18000), QTimeZone(-14400), QTimeZone(-12600), QTimeZone(-10800), QTimeZone(- 7200), QTimeZone(- 3600), QTimeZone( 0), QTimeZone( 3600), QTimeZone( 7200), QTimeZone( 10800), QTimeZone( 12600), QTimeZone(-28800), QTimeZone( 14400), QTimeZone( 16200), QTimeZone( 18000), QTimeZone( 19800), QTimeZone( 20700), QTimeZone( 21600), QTimeZone( 25200), QTimeZone( 28600), QTimeZone( 30600), QTimeZone( 32400), QTimeZone( 36000), QTimeZone( 39600), QTimeZone( 43200), QTimeZone( 46800) }; QTimeZone D878UVCodeplug::GeneralSettingsElement::TimeZone::decode(uint8_t code) { if (code >= _timeZones.size()) return _timeZones.back(); return _timeZones.at(code); } uint8_t D878UVCodeplug::GeneralSettingsElement::TimeZone::encode(const QTimeZone &zone) { if (! _timeZones.contains(zone)) return 13; //<- UTC return _timeZones.indexOf(zone); } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::GeneralSettingsElement * ******************************************************************************************** */ D878UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size) : D868UVCodeplug::GeneralSettingsElement(ptr, size) { // pass... } D878UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr) : D868UVCodeplug::GeneralSettingsElement(ptr, GeneralSettingsElement::size()) { // pass... } void D878UVCodeplug::GeneralSettingsElement::clear() { AnytoneCodeplug::GeneralSettingsElement::clear(); } QTimeZone D878UVCodeplug::GeneralSettingsElement::gpsTimeZone() const { return TimeZone::decode(getUInt8(Offset::gpsTimeZone())); } void D878UVCodeplug::GeneralSettingsElement::setGPSTimeZone(const QTimeZone &zone) { setUInt8(Offset::gpsTimeZone(), TimeZone::encode(zone)); // <- Set to UTC } unsigned D878UVCodeplug::GeneralSettingsElement::transmitTimeout() const { return ((unsigned)getUInt8(Offset::transmitTimeout()))*30; } void D878UVCodeplug::GeneralSettingsElement::setTransmitTimeout(unsigned tot) { setUInt8(Offset::transmitTimeout(), tot/30); } AnytoneDisplaySettingsExtension::Language D878UVCodeplug::GeneralSettingsElement::language() const { return (AnytoneDisplaySettingsExtension::Language)getUInt8(Offset::language()); } void D878UVCodeplug::GeneralSettingsElement::setLanguage(AnytoneDisplaySettingsExtension::Language lang) { setUInt8(Offset::language(), (unsigned)lang); } Frequency D878UVCodeplug::GeneralSettingsElement::vfoFrequencyStep() const { switch (getUInt8(Offset::vfoFrequencyStep())) { case FREQ_STEP_2_5kHz: return Frequency::fromkHz(2.5); case FREQ_STEP_5kHz: return Frequency::fromkHz(5); case FREQ_STEP_6_25kHz: return Frequency::fromkHz(6.25); case FREQ_STEP_10kHz: return Frequency::fromkHz(10); case FREQ_STEP_12_5kHz: return Frequency::fromkHz(12.5); case FREQ_STEP_20kHz: return Frequency::fromkHz(20); case FREQ_STEP_25kHz: return Frequency::fromkHz(25); case FREQ_STEP_50kHz: return Frequency::fromkHz(50); } return Frequency::fromkHz(2.5); } void D878UVCodeplug::GeneralSettingsElement::setVFOFrequencyStep(Frequency freq) { if (freq.inkHz() <= 2.5) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_2_5kHz); else if (freq.inkHz() <= 5) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_5kHz); else if (freq.inkHz() <= 6.25) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_6_25kHz); else if (freq.inkHz() <= 10) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_10kHz); else if (freq.inkHz() <= 12.5) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_12_5kHz); else if (freq.inkHz() <= 20) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_20kHz); else if (freq.inkHz() <= 25) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_25kHz); else setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_50kHz); } AnytoneKeySettingsExtension::KeyFunction D878UVCodeplug::GeneralSettingsElement::funcKeyAShort() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyAShort())); } void D878UVCodeplug::GeneralSettingsElement::setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyAShort(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D878UVCodeplug::GeneralSettingsElement::funcKeyBShort() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyBShort())); } void D878UVCodeplug::GeneralSettingsElement::setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyBShort(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D878UVCodeplug::GeneralSettingsElement::funcKeyCShort() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyCShort())); } void D878UVCodeplug::GeneralSettingsElement::setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyCShort(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D878UVCodeplug::GeneralSettingsElement::funcKey1Short() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey1Short())); } void D878UVCodeplug::GeneralSettingsElement::setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKey1Short(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D878UVCodeplug::GeneralSettingsElement::funcKey2Short() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey2Short())); } void D878UVCodeplug::GeneralSettingsElement::setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKey2Short(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D878UVCodeplug::GeneralSettingsElement::funcKeyALong() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyALong())); } void D878UVCodeplug::GeneralSettingsElement::setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyALong(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D878UVCodeplug::GeneralSettingsElement::funcKeyBLong() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyBLong())); } void D878UVCodeplug::GeneralSettingsElement::setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyBLong(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D878UVCodeplug::GeneralSettingsElement::funcKeyCLong() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyCLong())); } void D878UVCodeplug::GeneralSettingsElement::setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyCLong(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D878UVCodeplug::GeneralSettingsElement::funcKey1Long() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey1Long())); } void D878UVCodeplug::GeneralSettingsElement::setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKey1Long(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction D878UVCodeplug::GeneralSettingsElement::funcKey2Long() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey2Long())); } void D878UVCodeplug::GeneralSettingsElement::setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKey2Long(), KeyFunction::encode(func)); } AnytoneSettingsExtension::STEType D878UVCodeplug::GeneralSettingsElement::steType() const { return (AnytoneSettingsExtension::STEType)getUInt8(Offset::steType()); } void D878UVCodeplug::GeneralSettingsElement::setSTEType(AnytoneSettingsExtension::STEType type) { setUInt8(Offset::steType(), (unsigned)type); } double D878UVCodeplug::GeneralSettingsElement::steFrequency() const { switch ((STEFrequency)getUInt8(Offset::steFrequency())) { case STEFrequency::Off: return 0; case STEFrequency::Hz55_2: return 55.2; case STEFrequency::Hz259_2: return 259.2; } return 0; } void D878UVCodeplug::GeneralSettingsElement::setSTEFrequency(double freq) { if (0 >= freq) { setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Off); } else if (100 > freq) { setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz55_2); } else { setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz259_2); } } Interval D878UVCodeplug::GeneralSettingsElement::groupCallHangTime() const { return Interval::fromSeconds(getUInt8(Offset::groupCallHangTime())); } void D878UVCodeplug::GeneralSettingsElement::setGroupCallHangTime(Interval intv) { setUInt8(Offset::groupCallHangTime(), intv.seconds()); } Interval D878UVCodeplug::GeneralSettingsElement::privateCallHangTime() const { return Interval::fromSeconds(getUInt8(Offset::privateCallHangTime())); } void D878UVCodeplug::GeneralSettingsElement::setPrivateCallHangTime(Interval intv) { setUInt8(Offset::privateCallHangTime(), intv.seconds()); } Interval D878UVCodeplug::GeneralSettingsElement::preWaveDelay() const { return Interval::fromMilliseconds((unsigned)getUInt8(Offset::preWaveDelay())*20); } void D878UVCodeplug::GeneralSettingsElement::setPreWaveDelay(Interval intv) { setUInt8(Offset::preWaveDelay(), intv.milliseconds()/20); } Interval D878UVCodeplug::GeneralSettingsElement::wakeHeadPeriod() const { return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::wakeHeadPeriod()))*20); } void D878UVCodeplug::GeneralSettingsElement::setWakeHeadPeriod(Interval intv) { setUInt8(Offset::wakeHeadPeriod(), intv.milliseconds()/20); } unsigned D878UVCodeplug::GeneralSettingsElement::wfmChannelIndex() const { return getUInt8(Offset::wfmChannelIndex()); } void D878UVCodeplug::GeneralSettingsElement::setWFMChannelIndex(unsigned idx) { setUInt8(Offset::wfmChannelIndex(), idx); } bool D878UVCodeplug::GeneralSettingsElement::wfmVFOEnabled() const { return getUInt8(Offset::wfmVFOEnabled()); } void D878UVCodeplug::GeneralSettingsElement::enableWFMVFO(bool enable) { setUInt8(Offset::wfmVFOEnabled(), (enable ? 0x01 : 0x00)); } unsigned D878UVCodeplug::GeneralSettingsElement::dtmfToneDuration() const { switch (getUInt8(Offset::dtmfToneDuration())) { case DTMF_DUR_50ms: return 50; case DTMF_DUR_100ms: return 100; case DTMF_DUR_200ms: return 200; case DTMF_DUR_300ms: return 300; case DTMF_DUR_500ms: return 500; } return 50; } void D878UVCodeplug::GeneralSettingsElement::setDTMFToneDuration(unsigned ms) { if (ms<=50) { setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_50ms); } else if (ms<=100) { setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_100ms); } else if (ms<=200) { setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_200ms); } else if (ms<=300) { setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_300ms); } else { setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_500ms); } } bool D878UVCodeplug::GeneralSettingsElement::manDown() const { return getUInt8(Offset::manDown()); } void D878UVCodeplug::GeneralSettingsElement::enableManDown(bool enable) { setUInt8(Offset::manDown(), (enable ? 0x01 : 0x00)); } bool D878UVCodeplug::GeneralSettingsElement::wfmMonitor() const { return getUInt8(Offset::wfmMonitor()); } void D878UVCodeplug::GeneralSettingsElement::enableWFMMonitor(bool enable) { setUInt8(Offset::wfmMonitor(), (enable ? 0x01 : 0x00)); } Frequency D878UVCodeplug::GeneralSettingsElement::tbstFrequency() const { switch ((TBSTFrequency)getUInt8(Offset::tbstFrequency())) { case TBSTFrequency::Hz1000: return Frequency::fromHz(1000); case TBSTFrequency::Hz1450: return Frequency::fromHz(1450); case TBSTFrequency::Hz1750: return Frequency::fromHz(1750); case TBSTFrequency::Hz2100: return Frequency::fromHz(2100); } return Frequency::fromHz(1750); } void D878UVCodeplug::GeneralSettingsElement::setTBSTFrequency(Frequency freq) { if (1000 == freq.inHz()) { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1000); } else if (1450 == freq.inHz()) { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1450); } else if (1750 == freq.inHz()) { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750); } else if (2100 == freq.inHz()) { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz2100); } else { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750); } } bool D878UVCodeplug::GeneralSettingsElement::proMode() const { return getUInt8(Offset::proMode()); } void D878UVCodeplug::GeneralSettingsElement::enableProMode(bool enable) { setUInt8(Offset::proMode(), (enable ? 0x01 : 0x00)); } bool D878UVCodeplug::GeneralSettingsElement::filterOwnID() const { return getUInt8(Offset::filterOwnID()); } void D878UVCodeplug::GeneralSettingsElement::enableFilterOwnID(bool enable) { setUInt8(Offset::filterOwnID(), (enable ? 0x01 : 0x00)); } bool D878UVCodeplug::GeneralSettingsElement::remoteStunKill() const { return getUInt8(Offset::remoteStunKill()); } void D878UVCodeplug::GeneralSettingsElement::enableRemoteStunKill(bool enable) { setUInt8(Offset::remoteStunKill(), (enable ? 0x01 : 0x00)); } bool D878UVCodeplug::GeneralSettingsElement::remoteMonitor() const { return getUInt8(Offset::remoteMonitor()); } void D878UVCodeplug::GeneralSettingsElement::enableRemoteMonitor(bool enable) { setUInt8(Offset::remoteMonitor(), (enable ? 0x01 : 0x00)); } AnytoneDMRSettingsExtension::SlotMatch D878UVCodeplug::GeneralSettingsElement::monitorSlotMatch() const { return (AnytoneDMRSettingsExtension::SlotMatch)getUInt8(Offset::monSlotMatch()); } void D878UVCodeplug::GeneralSettingsElement::setMonitorSlotMatch(AnytoneDMRSettingsExtension::SlotMatch match) { setUInt8(Offset::monSlotMatch(), (unsigned)match); } bool D878UVCodeplug::GeneralSettingsElement::monitorColorCodeMatch() const { return getUInt8(Offset::monColorCodeMatch()); } void D878UVCodeplug::GeneralSettingsElement::enableMonitorColorCodeMatch(bool enable) { setUInt8(Offset::monColorCodeMatch(), (enable ? 0x01 : 0x00)); } bool D878UVCodeplug::GeneralSettingsElement::monitorIDMatch() const { return getUInt8(Offset::monIDMatch()); } void D878UVCodeplug::GeneralSettingsElement::enableMonitorIDMatch(bool enable) { setUInt8(Offset::monIDMatch(), (enable ? 0x01 : 0x00)); } bool D878UVCodeplug::GeneralSettingsElement::monitorTimeSlotHold() const { return getUInt8(Offset::monTimeSlotHold()); } void D878UVCodeplug::GeneralSettingsElement::enableMonitorTimeSlotHold(bool enable) { setUInt8(Offset::monTimeSlotHold(), (enable ? 0x01 : 0x00)); } Interval D878UVCodeplug::GeneralSettingsElement::manDownDelay() const { return Interval::fromSeconds(getUInt8(Offset::manDownDelay())); } void D878UVCodeplug::GeneralSettingsElement::setManDownDelay(Interval sec) { setUInt8(Offset::manDownDelay(), sec.seconds()); } unsigned D878UVCodeplug::GeneralSettingsElement::fmCallHold() const { return getUInt8(Offset::fmCallHold()); } void D878UVCodeplug::GeneralSettingsElement::setFMCallHold(unsigned sec) { setUInt8(Offset::fmCallHold(), sec); } bool D878UVCodeplug::GeneralSettingsElement::gpsMessageEnabled() const { return getUInt8(Offset::enableGPSMessage()); } void D878UVCodeplug::GeneralSettingsElement::enableGPSMessage(bool enable) { setUInt8(Offset::enableGPSMessage(), (enable ? 0x01 : 0x00)); } bool D878UVCodeplug::GeneralSettingsElement::maintainCallChannel() const { return getUInt8(Offset::maintainCallChannel()); } void D878UVCodeplug::GeneralSettingsElement::enableMaintainCallChannel(bool enable) { setUInt8(Offset::maintainCallChannel(), (enable ? 0x01 : 0x00)); } unsigned D878UVCodeplug::GeneralSettingsElement::priorityZoneAIndex() const { return getUInt8(Offset::priorityZoneA()); } void D878UVCodeplug::GeneralSettingsElement::setPriorityZoneAIndex(unsigned idx) { setUInt8(Offset::priorityZoneA(), idx); } unsigned D878UVCodeplug::GeneralSettingsElement::priorityZoneBIndex() const { return getUInt8(Offset::priorityZoneB()); } void D878UVCodeplug::GeneralSettingsElement::setPriorityZoneBIndex(unsigned idx) { setUInt8(Offset::priorityZoneB(), idx); } bool D878UVCodeplug::GeneralSettingsElement::bluetooth() const { return 0 != getUInt8(Offset::bluetooth()); } void D878UVCodeplug::GeneralSettingsElement::enableBluetooth(bool enable) { setUInt8(Offset::bluetooth(), enable ? 0x01 : 0x00); } bool D878UVCodeplug::GeneralSettingsElement::btAndInternalMic() const { return 0 != getUInt8(Offset::btAndInternalMic()); } void D878UVCodeplug::GeneralSettingsElement::enableBTAndInternalMic(bool enable) { setUInt8(Offset::btAndInternalMic(), enable ? 0x01 : 0x00); } bool D878UVCodeplug::GeneralSettingsElement::btAndInternalSpeaker() const { return 0 != getUInt8(Offset::btAndInternalSpeaker()); } void D878UVCodeplug::GeneralSettingsElement::enableBTAndInternalSpeaker(bool enable) { setUInt8(Offset::btAndInternalSpeaker(), enable ? 0x01 : 0x00); } bool D878UVCodeplug::GeneralSettingsElement::pluginRecTone() const { return 0 != getUInt8(Offset::pluginRecTone()); } void D878UVCodeplug::GeneralSettingsElement::enablePluginRecTone(bool enable) { setUInt8(Offset::pluginRecTone(), enable ? 0x01 : 0x00); } Interval D878UVCodeplug::GeneralSettingsElement::gpsUpdatePeriod() const { return Interval::fromSeconds(getUInt8(Offset::gpsRangingInterval())); } void D878UVCodeplug::GeneralSettingsElement::setGPSUpdatePeriod(Interval intv) { setUInt8(Offset::gpsRangingInterval(), intv.seconds()); } unsigned int D878UVCodeplug::GeneralSettingsElement::btMicGain() const { return (getUInt8(Offset::btMicGain())+1)*2; } void D878UVCodeplug::GeneralSettingsElement::setBTMicGain(unsigned int gain) { gain = std::min(10U, std::max(2U, gain)); setUInt8(Offset::btMicGain(), gain/2-1); } unsigned int D878UVCodeplug::GeneralSettingsElement::btSpeakerGain() const { return (getUInt8(Offset::btSpeakerGain())+1)*2; } void D878UVCodeplug::GeneralSettingsElement::setBTSpeakerGain(unsigned int gain) { gain = std::min(10U, std::max(2U, gain)); setUInt8(Offset::btSpeakerGain(), gain/2-1); } bool D878UVCodeplug::GeneralSettingsElement::displayChannelNumber() const { return getUInt8(Offset::showChannelNumber()); } void D878UVCodeplug::GeneralSettingsElement::enableDisplayChannelNumber(bool enable) { setUInt8(Offset::showChannelNumber(), (enable ? 0x01 : 0x00)); } bool D878UVCodeplug::GeneralSettingsElement::showCurrentContact() const { return getUInt8(Offset::showCurrentContact()); } void D878UVCodeplug::GeneralSettingsElement::enableShowCurrentContact(bool enable) { setUInt8(Offset::showCurrentContact(), (enable ? 0x01 : 0x00)); } Interval D878UVCodeplug::GeneralSettingsElement::autoRoamPeriod() const { return Interval::fromMinutes(getUInt8(Offset::autoRoamPeriod())); } void D878UVCodeplug::GeneralSettingsElement::setAutoRoamPeriod(Interval intv) { setUInt8(Offset::autoRoamPeriod(), intv.minutes()); } bool D878UVCodeplug::GeneralSettingsElement::keyToneLevelAdjustable() const { return 0 == keyToneLevel(); } unsigned D878UVCodeplug::GeneralSettingsElement::keyToneLevel() const { return ((unsigned)getUInt8(Offset::keyToneLevel()))*10/15; } void D878UVCodeplug::GeneralSettingsElement::setKeyToneLevel(unsigned level) { setUInt8(Offset::keyToneLevel(), level*10/15); } void D878UVCodeplug::GeneralSettingsElement::setKeyToneLevelAdjustable() { setUInt8(Offset::keyToneLevel(), 0); } AnytoneDisplaySettingsExtension::Color D878UVCodeplug::GeneralSettingsElement::callDisplayColor() const { return NameColor::decode(getUInt8(Offset::callColor())); } void D878UVCodeplug::GeneralSettingsElement::setCallDisplayColor(AnytoneDisplaySettingsExtension::Color color) { setUInt8(Offset::callColor(), NameColor::encode(color)); } bool D878UVCodeplug::GeneralSettingsElement::gpsUnitsImperial() const { return getUInt8(Offset::gpsUnits()); } void D878UVCodeplug::GeneralSettingsElement::enableGPSUnitsImperial(bool enable) { setUInt8(Offset::gpsUnits(), (enable ? 0x01 : 0x00)); } bool D878UVCodeplug::GeneralSettingsElement::knobLock() const { return getBit(Offset::knobLock(), 0); } void D878UVCodeplug::GeneralSettingsElement::enableKnobLock(bool enable) { setBit(Offset::knobLock(), 0, enable); } bool D878UVCodeplug::GeneralSettingsElement::keypadLock() const { return getBit(Offset::keypadLock(), 1); } void D878UVCodeplug::GeneralSettingsElement::enableKeypadLock(bool enable) { setBit(Offset::keypadLock(), 1, enable); } bool D878UVCodeplug::GeneralSettingsElement::sidekeysLock() const { return getBit(Offset::sideKeyLock(), 3); } void D878UVCodeplug::GeneralSettingsElement::enableSidekeysLock(bool enable) { setBit(Offset::sideKeyLock(), 3, enable); } bool D878UVCodeplug::GeneralSettingsElement::keyLockForced() const { return getBit(Offset::forceKeyLock(), 4); } void D878UVCodeplug::GeneralSettingsElement::enableKeyLockForced(bool enable) { setBit(Offset::forceKeyLock(), 4, enable); } Interval D878UVCodeplug::GeneralSettingsElement::autoRoamDelay() const { return Interval::fromSeconds(getUInt8(Offset::autoRoamDelay())); } void D878UVCodeplug::GeneralSettingsElement::setAutoRoamDelay(Interval intv) { setUInt8(Offset::autoRoamDelay(), intv.seconds()); } AnytoneDisplaySettingsExtension::Color D878UVCodeplug::GeneralSettingsElement::standbyTextColor() const { return TextColor::decode(getUInt8(Offset::standbyTextColor())); } void D878UVCodeplug::GeneralSettingsElement::setStandbyTextColor(AnytoneDisplaySettingsExtension::Color color) { setUInt8(Offset::standbyTextColor(), TextColor::encode(color)); } D878UVCodeplug::GeneralSettingsElement::BackgroundImage D878UVCodeplug::GeneralSettingsElement::standbyBackgroundImage() const { return (D878UVCodeplug::GeneralSettingsElement::BackgroundImage)getUInt8(Offset::standbyBackground()); } void D878UVCodeplug::GeneralSettingsElement::setStandbyBackgroundImage(D878UVCodeplug::GeneralSettingsElement::BackgroundImage img) { setUInt8(Offset::standbyBackground(), (unsigned)img); } bool D878UVCodeplug::GeneralSettingsElement::showLastHeard() const { return getUInt8(Offset::showLastHeard()); } void D878UVCodeplug::GeneralSettingsElement::enableShowLastHeard(bool enable) { setUInt8(Offset::showLastHeard(), (enable ? 0x01 : 0x00)); } AnytoneDMRSettingsExtension::SMSFormat D878UVCodeplug::GeneralSettingsElement::smsFormat() const { return (AnytoneDMRSettingsExtension::SMSFormat) getUInt8(Offset::smsFormat()); } void D878UVCodeplug::GeneralSettingsElement::setSMSFormat(AnytoneDMRSettingsExtension::SMSFormat fmt) { setUInt8(Offset::smsFormat(), (unsigned)fmt); } Frequency D878UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyVHF() const { return Frequency::fromHz(getUInt32_le(Offset::autoRepMinVHF())*10); } void D878UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyVHF(Frequency freq) { setUInt32_le(Offset::autoRepMinVHF(), freq.inHz()/10); } Frequency D878UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyVHF() const { return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxVHF())*10); } void D878UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyVHF(Frequency freq) { setUInt32_le(Offset::autoRepMaxVHF(), freq.inHz()/10); } Frequency D878UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyUHF() const { return Frequency::fromHz(getUInt32_le(Offset::autoRepMinUHF())*10); } void D878UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyUHF(Frequency freq) { setUInt32_le(Offset::autoRepMinUHF(), freq.inHz()/10); } Frequency D878UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyUHF() const { return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxUHF())*10); } void D878UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyUHF(Frequency freq) { setUInt32_le(Offset::autoRepMaxUHF(), freq.inHz()/10); } AnytoneAutoRepeaterSettingsExtension::Direction D878UVCodeplug::GeneralSettingsElement::autoRepeaterDirectionB() const { return (AnytoneAutoRepeaterSettingsExtension::Direction)getUInt8(Offset::autoRepeaterDirB()); } void D878UVCodeplug::GeneralSettingsElement::setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir) { setUInt8(Offset::autoRepeaterDirB(), (unsigned)dir); } bool D878UVCodeplug::GeneralSettingsElement::fmSendIDAndContact() const { return 0 != getUInt8(Offset::fmSendIDAndContact()); } void D878UVCodeplug::GeneralSettingsElement::enableFMSendIDAndContact(bool enable) { setUInt8(Offset::fmSendIDAndContact(), enable ? 0x01 : 0x00); } bool D878UVCodeplug::GeneralSettingsElement::defaultChannel() const { return getUInt8(Offset::defaultChannels()); } void D878UVCodeplug::GeneralSettingsElement::enableDefaultChannel(bool enable) { setUInt8(Offset::defaultChannels(), (enable ? 0x01 : 0x00)); } unsigned D878UVCodeplug::GeneralSettingsElement::defaultZoneIndexA() const { return getUInt8(Offset::defaultZoneA()); } void D878UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexA(unsigned idx) { setUInt8(Offset::defaultZoneA(), idx); } unsigned D878UVCodeplug::GeneralSettingsElement::defaultZoneIndexB() const { return getUInt8(Offset::defaultZoneB()); } void D878UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexB(unsigned idx) { setUInt8(Offset::defaultZoneB(), idx); } bool D878UVCodeplug::GeneralSettingsElement::defaultChannelAIsVFO() const { return 0xff == defaultChannelAIndex(); } unsigned D878UVCodeplug::GeneralSettingsElement::defaultChannelAIndex() const { return getUInt8(Offset::defaultChannelA()); } void D878UVCodeplug::GeneralSettingsElement::setDefaultChannelAIndex(unsigned idx) { setUInt8(Offset::defaultChannelA(), idx); } void D878UVCodeplug::GeneralSettingsElement::setDefaultChannelAToVFO() { setDefaultChannelAIndex(0xff); } bool D878UVCodeplug::GeneralSettingsElement::defaultChannelBIsVFO() const { return 0xff == defaultChannelBIndex(); } unsigned D878UVCodeplug::GeneralSettingsElement::defaultChannelBIndex() const { return getUInt8(Offset::defaultChannelB()); } void D878UVCodeplug::GeneralSettingsElement::setDefaultChannelBIndex(unsigned idx) { setUInt8(Offset::defaultChannelB(), idx); } void D878UVCodeplug::GeneralSettingsElement::setDefaultChannelBToVFO() { setDefaultChannelBIndex(0xff); } unsigned D878UVCodeplug::GeneralSettingsElement::defaultRoamingZoneIndex() const { return getUInt8(Offset::defaultRoamingZone()); } void D878UVCodeplug::GeneralSettingsElement::setDefaultRoamingZoneIndex(unsigned idx) { setUInt8(Offset::defaultRoamingZone(), idx); } bool D878UVCodeplug::GeneralSettingsElement::repeaterRangeCheck() const { return getUInt8(Offset::repRangeCheck()); } void D878UVCodeplug::GeneralSettingsElement::enableRepeaterRangeCheck(bool enable) { setUInt8(Offset::repRangeCheck(), (enable ? 0x01 : 0x00)); } Interval D878UVCodeplug::GeneralSettingsElement::repeaterRangeCheckInterval() const { return Interval::fromSeconds(((unsigned)getUInt8(Offset::rangeCheckInterval()))*5); } void D878UVCodeplug::GeneralSettingsElement::setRepeaterRangeCheckInterval(Interval intv) { setUInt8(Offset::rangeCheckInterval(), intv.seconds()/5); } unsigned D878UVCodeplug::GeneralSettingsElement::repeaterRangeCheckCount() const { return getUInt8(Offset::rangeCheckCount()); } void D878UVCodeplug::GeneralSettingsElement::setRepeaterRangeCheckCount(unsigned n) { setUInt8(Offset::rangeCheckCount(), n); } AnytoneRoamingSettingsExtension::RoamStart D878UVCodeplug::GeneralSettingsElement::roamingStartCondition() const { return (AnytoneRoamingSettingsExtension::RoamStart)getUInt8(Offset::roamStartCondition()); } void D878UVCodeplug::GeneralSettingsElement::setRoamingStartCondition(AnytoneRoamingSettingsExtension::RoamStart cond) { setUInt8(Offset::roamStartCondition(), (unsigned)cond); } Interval D878UVCodeplug::GeneralSettingsElement::txBacklightDuration() const { return Interval::fromSeconds(getUInt8(Offset::txBacklightDuration())); } void D878UVCodeplug::GeneralSettingsElement::setTXBacklightDuration(Interval intv) { setUInt8(Offset::txBacklightDuration(), intv.seconds()); } bool D878UVCodeplug::GeneralSettingsElement::separateDisplay() const { return getUInt8(Offset::displaySeparator()); } void D878UVCodeplug::GeneralSettingsElement::enableSeparateDisplay(bool enable) { setUInt8(Offset::displaySeparator(), (enable ? 0x01 : 0x00)); } bool D878UVCodeplug::GeneralSettingsElement::keepLastCaller() const { return getUInt8(Offset::keepLastCaller()); } void D878UVCodeplug::GeneralSettingsElement::enableKeepLastCaller(bool enable) { setUInt8(Offset::keepLastCaller(), (enable ? 0x01 : 0x00)); } AnytoneDisplaySettingsExtension::Color D878UVCodeplug::GeneralSettingsElement::channelNameColor() const { return NameColor::decode(getUInt8(Offset::channelNameColor())); } void D878UVCodeplug::GeneralSettingsElement::setChannelNameColor(AnytoneDisplaySettingsExtension::Color color) { setUInt8(Offset::channelNameColor(), NameColor::encode(color)); } bool D878UVCodeplug::GeneralSettingsElement::repeaterCheckNotification() const { return getUInt8(Offset::repCheckNotify()); } void D878UVCodeplug::GeneralSettingsElement::enableRepeaterCheckNotification(bool enable) { setUInt8(Offset::repCheckNotify(), (enable ? 0x01 : 0x00)); } Interval D878UVCodeplug::GeneralSettingsElement::rxBacklightDuration() const { return Interval::fromSeconds(getUInt8(Offset::rxBacklightDuration())); } void D878UVCodeplug::GeneralSettingsElement::setRXBacklightDuration(Interval intv) { setUInt8(Offset::rxBacklightDuration(), intv.seconds()); } bool D878UVCodeplug::GeneralSettingsElement::roaming() const { return getUInt8(Offset::roaming()); } void D878UVCodeplug::GeneralSettingsElement::enableRoaming(bool enable) { setUInt8(Offset::roaming(), (enable ? 0x01 : 0x00)); } Interval D878UVCodeplug::GeneralSettingsElement::muteDelay() const { return Interval::fromMinutes(getUInt8(Offset::muteDelay())+1); } void D878UVCodeplug::GeneralSettingsElement::setMuteDelay(Interval min) { setUInt8(Offset::muteDelay(), std::max(1ULL, min.minutes())-1); } unsigned D878UVCodeplug::GeneralSettingsElement::repeaterCheckNumNotifications() const { return getUInt8(Offset::repCheckNumNotify()); } void D878UVCodeplug::GeneralSettingsElement::setRepeaterCheckNumNotifications(unsigned num) { setUInt8(Offset::repCheckNumNotify(), num); } bool D878UVCodeplug::GeneralSettingsElement::bootGPSCheck() const { return getUInt8(Offset::bootGPSCheck()); } void D878UVCodeplug::GeneralSettingsElement::enableBootGPSCheck(bool enable) { setUInt8(Offset::bootGPSCheck(), (enable ? 0x01 : 0x00)); } bool D878UVCodeplug::GeneralSettingsElement::bootReset() const { return getUInt8(Offset::bootReset()); } void D878UVCodeplug::GeneralSettingsElement::enableBootReset(bool enable) { setUInt8(Offset::bootReset(), (enable ? 0x01 : 0x00)); } bool D878UVCodeplug::GeneralSettingsElement::btHoldTimeEnabled() const { return 0x00 != getUInt8(Offset::btHoldTime()); } bool D878UVCodeplug::GeneralSettingsElement::btHoldTimeInfinite() const { return 121U == getUInt8(Offset::btHoldTime()); } Interval D878UVCodeplug::GeneralSettingsElement::btHoldTime() const { return Interval::fromSeconds(getUInt8(Offset::btHoldTime())); } void D878UVCodeplug::GeneralSettingsElement::setBTHoldTime(Interval interval) { unsigned int seconds = std::min(120ULL, std::max(1ULL, interval.seconds())); setUInt8(Offset::btHoldTime(), seconds); } void D878UVCodeplug::GeneralSettingsElement::setBTHoldTimeInfinite() { setUInt8(Offset::btHoldTime(), 121); } void D878UVCodeplug::GeneralSettingsElement::disableBTHoldTime() { setUInt8(Offset::btHoldTime(), 0); } Interval D878UVCodeplug::GeneralSettingsElement::btRXDelay() const { if (0 == getUInt8(Offset::btRXDelay())) return Interval::fromMilliseconds(30); return Interval::fromMilliseconds(((unsigned int)getUInt8(Offset::btRXDelay())+1)*500); } void D878UVCodeplug::GeneralSettingsElement::setBTRXDelay(Interval delay) { if (500 >= delay.milliseconds()) { setUInt8(Offset::btRXDelay(), 0); } else { unsigned int millis = std::min(5500ULL, std::max(500ULL, delay.milliseconds())); setUInt8(Offset::btRXDelay(), (millis-500)/500); } } bool D878UVCodeplug::GeneralSettingsElement::fromConfig(const Flags &flags, Context &ctx) { if (! AnytoneCodeplug::GeneralSettingsElement::fromConfig(flags, ctx)) return false; // Set measurement system based on system locale (0x00==Metric) enableGPSUnitsImperial(QLocale::ImperialSystem == QLocale::system().measurementSystem()); // Set transmit timeout setTransmitTimeout(ctx.config()->settings()->tot()); AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); if (nullptr == ext) return true; // Encode boot settings if (ext->bootSettings()->priorityZoneA()->isNull()) setPriorityZoneAIndex(0xff); else setPriorityZoneAIndex(ctx.index(ext->bootSettings()->priorityZoneA()->as())); if (ext->bootSettings()->priorityZoneB()->isNull()) setPriorityZoneBIndex(0xff); else setPriorityZoneBIndex(ctx.index(ext->bootSettings()->priorityZoneB()->as())); if (! ext->roamingSettings()->defaultZone()->isNull()) setDefaultRoamingZoneIndex(ctx.index(ext->roamingSettings()->defaultZone()->as())); enableBootGPSCheck(ext->bootSettings()->gpsCheckEnabled()); enableBootReset(ext->bootSettings()->resetEnabled()); // Encode key settings enableKnobLock(ext->keySettings()->knobLockEnabled()); enableKeypadLock(ext->keySettings()->keypadLockEnabled()); enableSidekeysLock(ext->keySettings()->sideKeysLockEnabled()); enableKeyLockForced(ext->keySettings()->forcedKeyLockEnabled()); // Encode tone settings setKeyToneLevel(ext->toneSettings()->keyToneLevel()); // Encode audio settings setMuteDelay(ext->audioSettings()->muteDelay()); // Encode display settings setCallDisplayColor(ext->displaySettings()->callColor()); setLanguage(ext->displaySettings()->language()); enableDisplayChannelNumber(ext->displaySettings()->showChannelNumberEnabled()); enableShowCurrentContact(ext->displaySettings()->showContact()); setStandbyTextColor(ext->displaySettings()->standbyTextColor()); //setStandbyBackgroundImage(ext->displaySettings()->standbyBackgroundColor()); enableShowLastHeard(ext->displaySettings()->showLastHeardEnabled()); setChannelNameColor(ext->displaySettings()->channelNameColor()); setRXBacklightDuration(ext->displaySettings()->backlightDurationRX()); setTXBacklightDuration(ext->displaySettings()->backlightDurationTX()); // Encode menu settings enableSeparateDisplay(ext->menuSettings()->separatorEnabled()); // Encode auto-repeater settings setAutoRepeaterDirectionB(ext->autoRepeaterSettings()->directionB()); setAutoRepeaterMinFrequencyVHF(ext->autoRepeaterSettings()->vhfMin()); setAutoRepeaterMaxFrequencyVHF(ext->autoRepeaterSettings()->vhfMax()); setAutoRepeaterMinFrequencyUHF(ext->autoRepeaterSettings()->uhfMin()); setAutoRepeaterMaxFrequencyUHF(ext->autoRepeaterSettings()->uhfMax()); // Encode DMR settings setGroupCallHangTime(ext->dmrSettings()->groupCallHangTime()); setPrivateCallHangTime(ext->dmrSettings()->privateCallHangTime()); setPreWaveDelay(ext->dmrSettings()->preWaveDelay()); setWakeHeadPeriod(ext->dmrSettings()->wakeHeadPeriod()); enableFilterOwnID(ext->dmrSettings()->filterOwnIDEnabled()); setMonitorSlotMatch(ext->dmrSettings()->monitorSlotMatch()); enableMonitorColorCodeMatch(ext->dmrSettings()->monitorColorCodeMatchEnabled()); enableMonitorIDMatch(ext->dmrSettings()->monitorIDMatchEnabled()); enableMonitorTimeSlotHold(ext->dmrSettings()->monitorTimeSlotHoldEnabled()); setSMSFormat(ext->dmrSettings()->smsFormat()); // Encode GPS settings enableGPSUnitsImperial(AnytoneGPSSettingsExtension::Units::Archaic == ext->gpsSettings()->units()); setGPSTimeZone(ext->gpsSettings()->timeZone()); enableGPSMessage(ext->gpsSettings()->positionReportingEnabled()); setGPSUpdatePeriod(ext->gpsSettings()->updatePeriod()); // Encode ranging/roaming settings. setAutoRoamPeriod(ext->roamingSettings()->autoRoamPeriod()); setAutoRoamDelay(ext->roamingSettings()->autoRoamDelay()); enableRepeaterRangeCheck(ext->roamingSettings()->repeaterRangeCheckEnabled()); setRepeaterRangeCheckInterval(ext->roamingSettings()->repeaterCheckInterval()); setRepeaterRangeCheckCount(ext->roamingSettings()->repeaterRangeCheckCount()); setRoamingStartCondition(ext->roamingSettings()->roamingStartCondition()); enableRepeaterCheckNotification(ext->roamingSettings()->notificationEnabled()); setRepeaterCheckNumNotifications(ext->roamingSettings()->notificationCount()); // Encode other settings enableKeepLastCaller(ext->keepLastCallerEnabled()); setVFOFrequencyStep(ext->vfoStep()); setSTEType(ext->steType()); setSTEFrequency(ext->steFrequency()); setTBSTFrequency(ext->tbstFrequency()); enableProMode(ext->proModeEnabled()); enableMaintainCallChannel(ext->maintainCallChannelEnabled()); return true; } bool D878UVCodeplug::GeneralSettingsElement::updateConfig(Context &ctx) { if (! AnytoneCodeplug::GeneralSettingsElement::updateConfig(ctx)) return false; ctx.config()->settings()->setTOT(transmitTimeout()); // Get or add settings extension AnytoneSettingsExtension *ext = nullptr; if (ctx.config()->settings()->anytoneExtension()) { ext = ctx.config()->settings()->anytoneExtension(); } else { ext = new AnytoneSettingsExtension(); ctx.config()->settings()->setAnytoneExtension(ext); } // Decode boot settings ext->bootSettings()->enableGPSCheck(this->bootGPSCheck()); ext->bootSettings()->enableReset(this->bootReset()); // Decode key settings ext->keySettings()->enableKnobLock(this->knobLock()); ext->keySettings()->enableKeypadLock(this->keypadLock()); ext->keySettings()->enableSideKeysLock(this->sidekeysLock()); ext->keySettings()->enableForcedKeyLock(this->keyLockForced()); // Decode tone settings ext->toneSettings()->setKeyToneLevel(keyToneLevel()); // Store audio settings ext->audioSettings()->setMuteDelay(this->muteDelay()); // Decode display settings ext->displaySettings()->setCallColor(this->callDisplayColor()); ext->displaySettings()->setLanguage(this->language()); ext->displaySettings()->enableShowChannelNumber(this->displayChannelNumber()); ext->displaySettings()->enableShowContact(this->showCurrentContact()); ext->displaySettings()->setStandbyTextColor(this->standbyTextColor()); //ext->displaySettings()->setStandbyBackgroundColor(this->standbyBackgroundImage()); ext->displaySettings()->enableShowLastHeard(this->showLastHeard()); ext->displaySettings()->setBacklightDurationTX(this->txBacklightDuration()); ext->displaySettings()->setChannelNameColor(this->channelNameColor()); ext->displaySettings()->setBacklightDurationRX(this->rxBacklightDuration()); // Decode menu settings ext->menuSettings()->enableSeparator(this->separateDisplay()); // Decode auto-repeater settings ext->autoRepeaterSettings()->setDirectionB(autoRepeaterDirectionB()); ext->autoRepeaterSettings()->setVHFMin(this->autoRepeaterMinFrequencyVHF()); ext->autoRepeaterSettings()->setVHFMax(this->autoRepeaterMaxFrequencyVHF()); ext->autoRepeaterSettings()->setUHFMin(this->autoRepeaterMinFrequencyUHF()); ext->autoRepeaterSettings()->setUHFMax(this->autoRepeaterMaxFrequencyUHF()); // Encode dmr settings ext->dmrSettings()->setGroupCallHangTime(this->groupCallHangTime()); ext->dmrSettings()->setPrivateCallHangTime(this->privateCallHangTime()); ext->dmrSettings()->setPreWaveDelay(this->preWaveDelay()); ext->dmrSettings()->setWakeHeadPeriod(this->wakeHeadPeriod()); ext->dmrSettings()->enableFilterOwnID(this->filterOwnID()); ext->dmrSettings()->setMonitorSlotMatch(this->monitorSlotMatch()); ext->dmrSettings()->enableMonitorColorCodeMatch(this->monitorColorCodeMatch()); ext->dmrSettings()->enableMonitorIDMatch(this->monitorIDMatch()); ext->dmrSettings()->enableMonitorTimeSlotHold(this->monitorTimeSlotHold()); ext->dmrSettings()->setSMSFormat(this->smsFormat()); // Encode GPS settings ext->gpsSettings()->setUnits( this->gpsUnitsImperial() ? AnytoneGPSSettingsExtension::Units::Archaic : AnytoneGPSSettingsExtension::Units::Metric); ext->gpsSettings()->enablePositionReporting(this->gpsMessageEnabled()); ext->gpsSettings()->setUpdatePeriod(this->gpsUpdatePeriod()); // Encode ranging/roaming settings ext->roamingSettings()->setAutoRoamPeriod(this->autoRoamPeriod()); ext->roamingSettings()->setAutoRoamDelay(this->autoRoamDelay()); ext->roamingSettings()->enableRepeaterRangeCheck(this->repeaterRangeCheck()); ext->roamingSettings()->setRepeaterCheckInterval(this->repeaterRangeCheckInterval()); ext->roamingSettings()->setRepeaterRangeCheckCount(this->repeaterRangeCheckCount()); ext->roamingSettings()->setRoamingStartCondition(this->roamingStartCondition()); ext->roamingSettings()->enableNotification(this->repeaterCheckNotification()); ext->roamingSettings()->setNotificationCount(this->repeaterCheckNumNotifications()); // Decode other settings ext->enableKeepLastCaller(this->keepLastCaller()); ext->setVFOStep(this->vfoFrequencyStep()); ext->setSTEType(this->steType()); ext->setSTEFrequency(this->steFrequency()); ext->setTBSTFrequency(this->tbstFrequency()); ext->enableProMode(this->proMode()); ext->enableMaintainCallChannel(this->maintainCallChannel()); return true; } bool D878UVCodeplug::GeneralSettingsElement::linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err) { if (! AnytoneCodeplug::GeneralSettingsElement::linkSettings(settings, ctx, err)) return false; AnytoneSettingsExtension *ext = settings->anytoneExtension(); if (0xff != priorityZoneAIndex()) { if (! ctx.has(priorityZoneAIndex())) { errMsg(err) << "Cannot link priority zone A index " << priorityZoneAIndex() << ": Zone with that index not defined."; return false; } ext->bootSettings()->priorityZoneA()->set(ctx.get(priorityZoneAIndex())); } if (0xff != priorityZoneBIndex()) { if (! ctx.has(priorityZoneBIndex())) { errMsg(err) << "Cannot link priority zone B index " << priorityZoneBIndex() << ": Zone with that index not defined."; return false; } ext->bootSettings()->priorityZoneB()->set(ctx.get(priorityZoneBIndex())); } if (ctx.has(defaultRoamingZoneIndex())) { ext->roamingSettings()->defaultZone()->set(ctx.get(this->defaultRoamingZoneIndex())); } return true; } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::GeneralSettingsExtensionElement * ******************************************************************************************** */ D878UVCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr, unsigned size) : AnytoneCodeplug::ExtendedSettingsElement(ptr, size) { // pass... } D878UVCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr) : AnytoneCodeplug::ExtendedSettingsElement(ptr, ExtendedSettingsElement::size()) { // pass... } void D878UVCodeplug::ExtendedSettingsElement::clear() { memset(_data, 0x00, _size); clearAutoRepeaterVHF2OffsetIndex(); clearAutoRepeaterUHF2OffsetIndex(); } bool D878UVCodeplug::ExtendedSettingsElement::sendTalkerAlias() const { return getUInt8(Offset::sendTalkerAlias()); } void D878UVCodeplug::ExtendedSettingsElement::enableSendTalkerAlias(bool enable) { setUInt8(Offset::sendTalkerAlias(), (enable ? 0x01 : 0x00)); } AnytoneDMRSettingsExtension::TalkerAliasSource D878UVCodeplug::ExtendedSettingsElement::talkerAliasSource() const { return (AnytoneDMRSettingsExtension::TalkerAliasSource)getUInt8(Offset::talkerAliasDisplay()); } void D878UVCodeplug::ExtendedSettingsElement::setTalkerAliasSource(AnytoneDMRSettingsExtension::TalkerAliasSource mode) { setUInt8(Offset::talkerAliasDisplay(), (unsigned)mode); } AnytoneDMRSettingsExtension::TalkerAliasEncoding D878UVCodeplug::ExtendedSettingsElement::talkerAliasEncoding() const { return (AnytoneDMRSettingsExtension::TalkerAliasEncoding)getUInt8(Offset::talkerAliasEncoding()); } void D878UVCodeplug::ExtendedSettingsElement::setTalkerAliasEncoding(AnytoneDMRSettingsExtension::TalkerAliasEncoding enc) { setUInt8(Offset::talkerAliasEncoding(), (unsigned)enc); } bool D878UVCodeplug::ExtendedSettingsElement::bluetoothPTTLatch() const { return getUInt8(Offset::btPTTLatch()); } void D878UVCodeplug::ExtendedSettingsElement::enableBluetoothPTTLatch(bool enable) { setUInt8(Offset::btPTTLatch(), enable ? 0x01 : 0x00); } bool D878UVCodeplug::ExtendedSettingsElement::infiniteBluetoothPTTSleepDelay() const { return bluetoothPTTSleepDelay().isNull(); } Interval D878UVCodeplug::ExtendedSettingsElement::bluetoothPTTSleepDelay() const { return Interval::fromMinutes(getUInt8(Offset::btPTTSleepDelay())); } void D878UVCodeplug::ExtendedSettingsElement::setBluetoothPTTSleepDelay(Interval delay) { unsigned int t = std::min(Limit::maxBluetoothPTTSleepDelay(), (unsigned int)delay.minutes()); setUInt8(Offset::btPTTSleepDelay(), t); } void D878UVCodeplug::ExtendedSettingsElement::setInfiniteBluetoothPTTSleepDelay() { setBluetoothPTTSleepDelay(Interval::fromMinutes(0)); } bool D878UVCodeplug::ExtendedSettingsElement::hasAutoRepeaterUHF2OffsetIndex() const { return 0xff != getUInt8(Offset::autoRepeaterUHF2OffsetIndex()); } unsigned D878UVCodeplug::ExtendedSettingsElement::autoRepeaterUHF2OffsetIndex() const { return getUInt8(Offset::autoRepeaterUHF2OffsetIndex()); } void D878UVCodeplug::ExtendedSettingsElement::setAutoRepeaterUHF2OffsetIndex(unsigned idx) { setUInt8(Offset::autoRepeaterUHF2OffsetIndex(), idx); } void D878UVCodeplug::ExtendedSettingsElement::clearAutoRepeaterUHF2OffsetIndex() { setUInt8(Offset::autoRepeaterUHF2OffsetIndex(), 0xff); } bool D878UVCodeplug::ExtendedSettingsElement::hasAutoRepeaterVHF2OffsetIndex() const { return 0xff != getUInt8(Offset::autoRepeaterVHF2OffsetIndex()); } unsigned D878UVCodeplug::ExtendedSettingsElement::autoRepeaterVHF2OffsetIndex() const { return getUInt8(Offset::autoRepeaterVHF2OffsetIndex()); } void D878UVCodeplug::ExtendedSettingsElement::setAutoRepeaterVHF2OffsetIndex(unsigned idx) { setUInt8(Offset::autoRepeaterVHF2OffsetIndex(), idx); } void D878UVCodeplug::ExtendedSettingsElement::clearAutoRepeaterVHF2OffsetIndex() { setUInt8(Offset::autoRepeaterVHF2OffsetIndex(), 0xff); } Frequency D878UVCodeplug::ExtendedSettingsElement::autoRepeaterVHF2MinFrequency() const { return Frequency::fromHz(((unsigned long long)getUInt32_le(Offset::autoRepeaterVHF2MinFrequency()))*10); } void D878UVCodeplug::ExtendedSettingsElement::setAutoRepeaterVHF2MinFrequency(Frequency hz) { setUInt32_le(Offset::autoRepeaterVHF2MinFrequency(), hz.inHz()/10); } Frequency D878UVCodeplug::ExtendedSettingsElement::autoRepeaterVHF2MaxFrequency() const { return Frequency::fromHz(((unsigned long long)getUInt32_le(Offset::autoRepeaterVHF2MaxFrequency()))*10); } void D878UVCodeplug::ExtendedSettingsElement::setAutoRepeaterVHF2MaxFrequency(Frequency hz) { setUInt32_le(Offset::autoRepeaterVHF2MaxFrequency(), hz.inHz()/10); } Frequency D878UVCodeplug::ExtendedSettingsElement::autoRepeaterUHF2MinFrequency() const { return Frequency::fromHz(((unsigned long long)getUInt32_le(Offset::autoRepeaterUHF2MinFrequency()))*10); } void D878UVCodeplug::ExtendedSettingsElement::setAutoRepeaterUHF2MinFrequency(Frequency hz) { setUInt32_le(Offset::autoRepeaterUHF2MinFrequency(), hz.inHz()/10); } Frequency D878UVCodeplug::ExtendedSettingsElement::autoRepeaterUHF2MaxFrequency() const { return Frequency::fromHz(((unsigned long long)getUInt32_le(Offset::autoRepeaterUHF2MaxFrequency()))*10); } void D878UVCodeplug::ExtendedSettingsElement::setAutoRepeaterUHF2MaxFrequency(Frequency hz) { setUInt32_le(Offset::autoRepeaterUHF2MaxFrequency(), hz.inHz()/10); } AnytoneGPSSettingsExtension::GPSMode D878UVCodeplug::ExtendedSettingsElement::gpsMode() const { return (AnytoneGPSSettingsExtension::GPSMode)getUInt8(Offset::gpsMode()); } void D878UVCodeplug::ExtendedSettingsElement::setGPSMode(AnytoneGPSSettingsExtension::GPSMode mode) { setUInt8(Offset::gpsMode(), (unsigned)mode); } Interval D878UVCodeplug::ExtendedSettingsElement::steDuration() const { return Interval::fromMilliseconds((getUInt8(Offset::steDuration())+1)*10); } void D878UVCodeplug::ExtendedSettingsElement::setSTEDuration(Interval dur) { unsigned int t = std::max(10U, std::min(1000U, (unsigned int)dur.milliseconds())); setUInt8(Offset::steDuration(), t/10-1); } bool D878UVCodeplug::ExtendedSettingsElement::infiniteManDialGroupCallHangTime() const { return manDialGroupCallHangTime().isNull(); } Interval D878UVCodeplug::ExtendedSettingsElement::manDialGroupCallHangTime() const { unsigned int t = getUInt8(Offset::manGrpCallHangTime())+1; if (t<=30) return Interval::fromSeconds(t); else if (31==t) return Interval::fromMinutes(30); return Interval(); } void D878UVCodeplug::ExtendedSettingsElement::setManDialGroupCallHangTime(Interval dur) { unsigned int t = dur.seconds(); if (t > 30) t = 31; // = 30min else if (0 == t) t = 32; // = infinite setUInt8(Offset::manGrpCallHangTime(), t-1); } void D878UVCodeplug::ExtendedSettingsElement::setManDialGroupCallHangTimeInfinite() { setManDialGroupCallHangTime(Interval::fromSeconds(0)); } bool D878UVCodeplug::ExtendedSettingsElement::infiniteManDialPrivateCallHangTime() const { return manDialPrivateCallHangTime().isNull(); } Interval D878UVCodeplug::ExtendedSettingsElement::manDialPrivateCallHangTime() const { unsigned int t = getUInt8(Offset::manPrivCallHangTime())+1; if (t<=30) return Interval::fromSeconds(t); else if (31==t) return Interval::fromMinutes(30); return Interval(); } void D878UVCodeplug::ExtendedSettingsElement::setManDialPrivateCallHangTime(Interval dur) { unsigned int t = dur.seconds(); if (t > 30) t = 31; // = 30min else if (0 == t) t = 32; // = infinite setUInt8(Offset::manPrivCallHangTime(), t-1); } void D878UVCodeplug::ExtendedSettingsElement::setManDialPrivateCallHangTimeInfinite() { setManDialPrivateCallHangTime(Interval::fromSeconds(0)); } AnytoneDisplaySettingsExtension::Color D878UVCodeplug::ExtendedSettingsElement::channelBNameColor() const { return NameColor::decode(getUInt8(Offset::channelBNameColor())); } void D878UVCodeplug::ExtendedSettingsElement::setChannelBNameColor(AnytoneDisplaySettingsExtension::Color color) { setUInt8(Offset::channelBNameColor(), NameColor::encode(color)); } AnytoneDMRSettingsExtension::EncryptionType D878UVCodeplug::ExtendedSettingsElement::encryption() const { return (AnytoneDMRSettingsExtension::EncryptionType)getUInt8(Offset::encryptionType()); } void D878UVCodeplug::ExtendedSettingsElement::setEncryption(AnytoneDMRSettingsExtension::EncryptionType mode) { setUInt8(Offset::encryptionType(), (unsigned int)mode); } bool D878UVCodeplug::ExtendedSettingsElement::totNotification() const { return 0x00 != getUInt8(Offset::totNotification()); } void D878UVCodeplug::ExtendedSettingsElement::enableTOTNotification(bool enable) { setUInt8(Offset::totNotification(), enable ? 0x01 : 0x00); } bool D878UVCodeplug::ExtendedSettingsElement::atpc() const { return 0x00 != getUInt8(Offset::atpc()); } void D878UVCodeplug::ExtendedSettingsElement::enableATPC(bool enable) { setUInt8(Offset::atpc(), enable ? 0x01 : 0x00); } AnytoneDisplaySettingsExtension::Color D878UVCodeplug::ExtendedSettingsElement::zoneANameColor() const { return NameColor::decode(getUInt8(Offset::zoneANameColor())); } void D878UVCodeplug::ExtendedSettingsElement::setZoneANameColor(AnytoneDisplaySettingsExtension::Color color) { setUInt8(Offset::zoneANameColor(), NameColor::encode(color)); } AnytoneDisplaySettingsExtension::Color D878UVCodeplug::ExtendedSettingsElement::zoneBNameColor() const { return NameColor::decode(getUInt8(Offset::zoneBNameColor())); } void D878UVCodeplug::ExtendedSettingsElement::setZoneBNameColor(AnytoneDisplaySettingsExtension::Color color) { setUInt8(Offset::zoneBNameColor(), NameColor::encode(color)); } bool D878UVCodeplug::ExtendedSettingsElement::resetAutoShutdownOnCall() const { return 0x00 != getUInt8(Offset::autoShutdownMode()); } void D878UVCodeplug::ExtendedSettingsElement::enableResetAutoShutdownOnCall(bool enable) { setUInt8(Offset::autoShutdownMode(), enable ? 0x01 : 0x00); } bool D878UVCodeplug::ExtendedSettingsElement::showColorCode() const { return getBit(Offset::displayColorCode(), 2); } void D878UVCodeplug::ExtendedSettingsElement::enableShowColorCode(bool enable) { setBit(Offset::displayColorCode(), 2, enable); } bool D878UVCodeplug::ExtendedSettingsElement::showTimeSlot() const { return getBit(Offset::displayTimeSlot(), 1); } void D878UVCodeplug::ExtendedSettingsElement::enableShowTimeSlot(bool enable) { setBit(Offset::displayTimeSlot(), 1, enable); } bool D878UVCodeplug::ExtendedSettingsElement::showChannelType() const { return getBit(Offset::displayChannelType(), 0); } void D878UVCodeplug::ExtendedSettingsElement::enableShowChannelType(bool enable) { setBit(Offset::displayChannelType(), 0, enable); } bool D878UVCodeplug::ExtendedSettingsElement::fmIdleTone() const { return 0x00 != getUInt8(Offset::fmIdleTone()); } void D878UVCodeplug::ExtendedSettingsElement::enableFMIdleTone(bool enable) { setUInt8(Offset::fmIdleTone(), enable ? 0x01 : 0x00); } AnytoneDisplaySettingsExtension::DateFormat D878UVCodeplug::ExtendedSettingsElement::dateFormat() const { return (AnytoneDisplaySettingsExtension::DateFormat)getUInt8(Offset::dateFormat()); } void D878UVCodeplug::ExtendedSettingsElement::setDateFormat(AnytoneDisplaySettingsExtension::DateFormat format) { setUInt8(Offset::dateFormat(), (unsigned int)format); } unsigned int D878UVCodeplug::ExtendedSettingsElement::fmMicGain() const { return (getUInt8(Offset::analogMicGain())+1)*10/5; } void D878UVCodeplug::ExtendedSettingsElement::setFMMicGain(unsigned int gain) { gain = std::min(10U, std::max(1U, gain)); setUInt8(Offset::analogMicGain(), gain*4/10); } bool D878UVCodeplug::ExtendedSettingsElement::gpsRoaming() const { return 0x00 != getUInt8(Offset::gpsRoaming()); } void D878UVCodeplug::ExtendedSettingsElement::enableGPSRoaming(bool enable) { setUInt8(Offset::gpsRoaming(), enable ? 0x01 : 0x00); } void D878UVCodeplug::ExtendedSettingsElement::callEndToneMelody(Melody &melody) const { QVector> tones; tones.reserve(5); for (int i=0; i<5; i++) { double freq = getUInt16_le(Offset::callEndTones()+2*i); unsigned int duration = getUInt16_le(Offset::callEndDurations()+2*i); if (duration) tones.append({freq, duration}); } melody.infer(tones); } void D878UVCodeplug::ExtendedSettingsElement::setCallEndToneMelody(const Melody &melody) { unsigned int n=std::min(5U, (unsigned int)melody.count()); QVector> tones = melody.toTones(); for (unsigned int i=0; i> tones; tones.reserve(5); for (int i=0; i<5; i++) { double freq = getUInt16_le(Offset::allCallTones()+2*i); unsigned int duration = getUInt16_le(Offset::allCallDurations()+2*i); if (duration) tones.append({freq, duration}); } melody.infer(tones); } void D878UVCodeplug::ExtendedSettingsElement::setAllCallToneMelody(const Melody &melody) { unsigned int n=std::min(5U, (unsigned int)melody.count()); QVector> tones = melody.toTones(); for (unsigned int i=0; iclear(); if (! AnytoneCodeplug::ExtendedSettingsElement::fromConfig(flags, ctx, err)) return false; if (nullptr == ctx.config()->settings()->anytoneExtension()) { // If there is no extension, reuse DMR mic gain setting setFMMicGain(ctx.config()->settings()->micLevel()); return true; } // Get extension AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); // Some general settings setSTEDuration(ext->steDuration()); // Power save settings enableATPC(ext->powerSaveSettings()->atpc()); enableResetAutoShutdownOnCall(ext->powerSaveSettings()->resetAutoShutdownOnCall()); // Encode tone settings enableTOTNotification(ext->toneSettings()->totNotification()); enableFMIdleTone(ext->toneSettings()->fmIdleChannelToneEnabled()); setCallEndToneMelody(*ext->toneSettings()->callEndMelody()); // Encode audio settings if (ext->audioSettings()->fmMicGainEnabled()) setFMMicGain(ext->audioSettings()->fmMicGain()); else setFMMicGain(ctx.config()->settings()->micLevel()); // Encode DMR settings setManDialGroupCallHangTime(ext->dmrSettings()->manualGroupCallHangTime()); setManDialPrivateCallHangTime(ext->dmrSettings()->manualPrivateCallHangTime()); setEncryption(ext->dmrSettings()->encryption()); // Encode display settings enableShowColorCode(ext->displaySettings()->showColorCode()); enableShowTimeSlot(ext->displaySettings()->showTimeSlot()); enableShowChannelType(ext->displaySettings()->showChannelType()); setDateFormat(ext->displaySettings()->dateFormat()); // Encode auto-repeater frequency ranges setAutoRepeaterVHF2MinFrequency(ext->autoRepeaterSettings()->vhf2Min()); setAutoRepeaterVHF2MaxFrequency(ext->autoRepeaterSettings()->vhf2Max()); setAutoRepeaterUHF2MinFrequency(ext->autoRepeaterSettings()->uhf2Min()); setAutoRepeaterUHF2MaxFrequency(ext->autoRepeaterSettings()->uhf2Max()); // Encode auto-repeater offset indices clearAutoRepeaterVHF2OffsetIndex(); if (! ext->autoRepeaterSettings()->vhf2Ref()->isNull()) { setAutoRepeaterVHF2OffsetIndex( ctx.index( ext->autoRepeaterSettings()->vhf2Ref()->as())); } clearAutoRepeaterUHF2OffsetIndex(); if (! ext->autoRepeaterSettings()->uhf2Ref()->isNull()) { setAutoRepeaterUHF2OffsetIndex( ctx.index( ext->autoRepeaterSettings()->uhf2Ref()->as())); } // Encode GPS settings setGPSMode(ext->gpsSettings()->mode()); // Encode roaming settings enableGPSRoaming(ext->roamingSettings()->gpsRoaming()); // Encode bluetooth settings enableBluetoothPTTLatch(ext->bluetoothSettings()->pttLatch()); setBluetoothPTTSleepDelay(ext->bluetoothSettings()->pttSleepTimer()); return true; } bool D878UVCodeplug::ExtendedSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); if (! AnytoneCodeplug::ExtendedSettingsElement::updateConfig(ctx, err)) return false; // Get or add extension if not present AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); if (nullptr == ext) { ext = new AnytoneSettingsExtension(); ctx.config()->settings()->setAnytoneExtension(ext); } // Some general settings ext->setSTEDuration(this->steDuration()); // Some power-save settings ext->powerSaveSettings()->enableATPC(this->atpc()); ext->powerSaveSettings()->enableResetAutoShutdownOnCall(this->resetAutoShutdownOnCall()); // Store tone settings ext->toneSettings()->enableTOTNotification(this->totNotification()); ext->toneSettings()->enableFMIdleChannelTone(this->fmIdleTone()); this->callEndToneMelody(*ext->toneSettings()->callEndMelody()); // Store FM mic gain separately ext->audioSettings()->setFMMicGain(fmMicGain()); // Enable separate mic gain, if it differs from the DMR mic gain: ext->audioSettings()->enableFMMicGain( ctx.config()->settings()->micLevel() != fmMicGain()); // Store display settings ext->displaySettings()->enableShowColorCode(this->showColorCode()); ext->displaySettings()->enableShowTimeSlot(this->showTimeSlot()); ext->displaySettings()->enableShowChannelType(this->showChannelType()); ext->displaySettings()->setDateFormat(this->dateFormat()); // Store some DMR settings ext->dmrSettings()->setManualGroupCallHangTime(this->manDialGroupCallHangTime()); ext->dmrSettings()->setManualPrivateCallHangTime(this->manDialPrivateCallHangTime()); ext->dmrSettings()->setEncryption(this->encryption()); // Store auto-repeater frequency ranges ext->autoRepeaterSettings()->setVHF2Min(this->autoRepeaterVHF2MinFrequency()); ext->autoRepeaterSettings()->setVHF2Max(this->autoRepeaterVHF2MaxFrequency()); ext->autoRepeaterSettings()->setUHF2Min(this->autoRepeaterUHF2MinFrequency()); ext->autoRepeaterSettings()->setUHF2Max(this->autoRepeaterUHF2MaxFrequency()); // Store GPS settings ext->gpsSettings()->setMode(this->gpsMode()); // Store roaming settings ext->roamingSettings()->enableGPSRoaming(this->gpsRoaming()); // Store bluetooth settings ext->bluetoothSettings()->enablePTTLatch(this->bluetoothPTTLatch()); ext->bluetoothSettings()->setPTTSleepTimer(this->bluetoothPTTSleepDelay()); return true; } bool D878UVCodeplug::ExtendedSettingsElement::linkConfig(Context &ctx, const ErrorStack &err) { if (! AnytoneCodeplug::ExtendedSettingsElement::linkConfig(ctx, err)) return false; // Get or add extension if not present AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); if (nullptr == ext) { errMsg(err) << "Cannot link config extension: not set."; return false; } if (hasAutoRepeaterVHF2OffsetIndex()) { if (! ctx.has(this->autoRepeaterVHF2OffsetIndex())) { errMsg(err) << "Cannot link AnyTone settings extension: " "second auto-repeater VHF offset index " << this->autoRepeaterVHF2OffsetIndex() << " not defined."; return false; } ext->autoRepeaterSettings()->vhf2Ref()->set( ctx.get( this->autoRepeaterVHF2OffsetIndex())); } if (hasAutoRepeaterUHF2OffsetIndex()) { if (! ctx.has(this->autoRepeaterUHF2OffsetIndex())) { errMsg(err) << "Cannot link AnyTone settings extension: " "second auto-repeater UHF offset index " << this->autoRepeaterUHF2OffsetIndex() << " not defined."; return false; } ext->autoRepeaterSettings()->uhf2Ref()->set( ctx.get( this->autoRepeaterUHF2OffsetIndex())); } return true; } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::AnalogAPRSSettingsElement * ******************************************************************************************** */ D878UVCodeplug::APRSSettingsElement::APRSSettingsElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } D878UVCodeplug::APRSSettingsElement::APRSSettingsElement(uint8_t *ptr) : Element(ptr, APRSSettingsElement::size()) { // pass... } void D878UVCodeplug::APRSSettingsElement::clear() { memset(_data, 0x00, _size); setUInt8(0x0000, 0xff); setFMTXDelay(Interval::fromMilliseconds(60)); setUInt8(0x003d, 0x01); setUInt8(0x003e, 0x03); setUInt8(0x003f, 0xff); } bool D878UVCodeplug::APRSSettingsElement::isValid() const { if (! Codeplug::Element::isValid()) return false; return (! destination().simplified().isEmpty()) && (! source().simplified().isEmpty()); } Interval D878UVCodeplug::APRSSettingsElement::fmTXDelay() const { return Interval::fromMilliseconds( ((unsigned)getUInt8(Offset::fmTXDelay())) * 20); } void D878UVCodeplug::APRSSettingsElement::setFMTXDelay(Interval ms) { setUInt8(Offset::fmTXDelay(), ms.milliseconds()/20); } SelectiveCall D878UVCodeplug::APRSSettingsElement::txTone() const { if ((uint8_t)SignalingType::Off ==getUInt8(Offset::fmSigType())) { // none return SelectiveCall(); } else if ((uint8_t)SignalingType::CTCSS == getUInt8(Offset::fmSigType())) { // CTCSS return CTCSS::decode(getUInt8(Offset::fmCTCSS())); } else if ((uint8_t)SignalingType::DCS == getUInt8(Offset::fmSigType())) { // DCS uint16_t code = getUInt16_le(Offset::fmDCS()); if (512 < code) return SelectiveCall::fromBinaryDCS(code, false); return SelectiveCall::fromBinaryDCS(code-512, true); } return SelectiveCall(); } void D878UVCodeplug::APRSSettingsElement::setTXTone(const SelectiveCall &code) { if (code.isInvalid()) { setUInt8(Offset::fmSigType(), (uint8_t)SignalingType::Off); } else if (code.isCTCSS()) { setUInt8(Offset::fmSigType(), (uint8_t)SignalingType::CTCSS); setUInt8(Offset::fmCTCSS(), CTCSS::encode(code)); } else if (code.isDCS()) { setUInt8(Offset::fmSigType(), (uint8_t)SignalingType::DCS); setUInt16_le(Offset::fmDCS(), code.binCode() + (code.isInverted() ? 512 : 0)); } } Interval D878UVCodeplug::APRSSettingsElement::manualTXInterval() const { return Interval::fromSeconds(getUInt8(Offset::manualTXInterval())); } void D878UVCodeplug::APRSSettingsElement::setManualTXInterval(Interval sec) { setUInt8(Offset::manualTXInterval(), sec.seconds()); } bool D878UVCodeplug::APRSSettingsElement::autoTX() const { return ! autoTXInterval().isNull(); } Interval D878UVCodeplug::APRSSettingsElement::autoTXInterval() const { return Interval::fromSeconds( ((unsigned)getUInt8(Offset::autoTXInterval()))*30 ); } void D878UVCodeplug::APRSSettingsElement::setAutoTXInterval(Interval sec) { setUInt8(Offset::autoTXInterval(), sec.seconds()/30); } void D878UVCodeplug::APRSSettingsElement::disableAutoTX() { setAutoTXInterval(Interval::fromMilliseconds(0)); } bool D878UVCodeplug::APRSSettingsElement::fixedLocationEnabled() const { return getUInt8(Offset::fixedLocation()); } QGeoCoordinate D878UVCodeplug::APRSSettingsElement::fixedLocation() const { double latitude = getUInt8(Offset::fixedLatDeg()) + double(getUInt8(Offset::fixedLatMin()))/60 + double(getUInt8(Offset::fixedLatSec()))/3600; if (getUInt8(Offset::fixedLatSouth())) latitude *= -1; double longitude = getUInt8(Offset::fixedLonDeg()) + double(getUInt8(Offset::fixedLonMin()))/60 + double(getUInt8(Offset::fixedLonSec()))/3600; if (getUInt8(Offset::fixedLonWest())) longitude *= -1; double height_ft = getUInt16_le(Offset::fixedHeight()); return QGeoCoordinate(latitude, longitude, height_ft/3.281); } void D878UVCodeplug::APRSSettingsElement::setFixedLocation(QGeoCoordinate &loc) { double latitude = loc.latitude(); bool south = (0 > latitude); latitude = std::abs(latitude); unsigned lat_deg = int(latitude); latitude -= lat_deg; latitude *= 60; unsigned lat_min = int(latitude); latitude -= lat_min; latitude *= 60; unsigned lat_sec = int(latitude); double longitude = loc.longitude(); bool west = (0 > longitude); longitude = std::abs(longitude); unsigned lon_deg = int(longitude); longitude -= lon_deg; longitude *= 60; unsigned lon_min = int(longitude); longitude -= lon_min; longitude *= 60; unsigned lon_sec = int(longitude); unsigned height_ft = int(loc.altitude()*3.281); setUInt8(Offset::fixedLatDeg(), lat_deg); setUInt8(Offset::fixedLatMin(), lat_min); setUInt8(Offset::fixedLatSec(), lat_sec); setUInt8(Offset::fixedLatSouth(), (south ? 0x01 : 0x00)); setUInt8(Offset::fixedLonDeg(), lon_deg); setUInt8(Offset::fixedLonMin(), lon_min); setUInt8(Offset::fixedLonSec(), lon_sec); setUInt8(Offset::fixedLonWest(), (west ? 0x01 : 0x00)); setUInt16_le(Offset::fixedHeight(), height_ft); // enable fixed location. setUInt8(Offset::fixedLocation(), 0x01); } void D878UVCodeplug::APRSSettingsElement::disableFixedLocation() { setUInt8(Offset::fixedLocation(), 0x00); } QString D878UVCodeplug::APRSSettingsElement::destination() const { // Terminated/padded with space return readASCII(Offset::destinationCall(), 6, ' '); } unsigned D878UVCodeplug::APRSSettingsElement::destinationSSID() const { return getUInt8(Offset::destinationSSID()); } void D878UVCodeplug::APRSSettingsElement::setDestination(const QString &call, unsigned ssid) { // Terminated/padded with space writeASCII(Offset::destinationCall(), call, 6, ' '); setUInt8(Offset::destinationSSID(), ssid); } QString D878UVCodeplug::APRSSettingsElement::source() const { // Terminated/padded with space return readASCII(Offset::sourceCall(), 6, ' '); } unsigned D878UVCodeplug::APRSSettingsElement::sourceSSID() const { return getUInt8(Offset::sourceSSID()); } void D878UVCodeplug::APRSSettingsElement::setSource(const QString &call, unsigned ssid) { // Terminated/padded with space writeASCII(Offset::sourceCall(), call, 6, ' '); setUInt8(Offset::sourceSSID(), ssid); } QString D878UVCodeplug::APRSSettingsElement::path() const { return readASCII(Offset::path(), 20, 0x00); } void D878UVCodeplug::APRSSettingsElement::setPath(const QString &path) { writeASCII(Offset::path(), path, 20, 0x00); } APRSSystem::Icon D878UVCodeplug::APRSSettingsElement::icon() const { return code2aprsicon(getUInt8(Offset::symbolTable()), getUInt8(Offset::symbol())); } void D878UVCodeplug::APRSSettingsElement::setIcon(APRSSystem::Icon icon) { setUInt8(Offset::symbolTable(), aprsicon2tablecode(icon)); setUInt8(Offset::symbol(), aprsicon2iconcode(icon)); } Channel::Power D878UVCodeplug::APRSSettingsElement::power() const { switch (getUInt8(Offset::fmPower())) { case 0: return Channel::Power::Low; case 1: return Channel::Power::Mid; case 2: return Channel::Power::High; case 3: return Channel::Power::Max; } return Channel::Power::Low; } void D878UVCodeplug::APRSSettingsElement::setPower(Channel::Power power) { switch (power) { case Channel::Power::Min: case Channel::Power::Low: setUInt8(Offset::fmPower(), 0x00); break; case Channel::Power::Mid: setUInt8(Offset::fmPower(), 0x01); break; case Channel::Power::High: setUInt8(Offset::fmPower(), 0x02); break; case Channel::Power::Max: setUInt8(Offset::fmPower(), 0x03); break; } } Interval D878UVCodeplug::APRSSettingsElement::fmPreWaveDelay() const { return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::fmPrewaveDelay()))*10); } void D878UVCodeplug::APRSSettingsElement::setFMPreWaveDelay(Interval ms) { setUInt8(Offset::fmPrewaveDelay(), ms.milliseconds()/10); } bool D878UVCodeplug::APRSSettingsElement::dmrChannelIsSelected(unsigned n) const { return 0xfa2 == dmrChannelIndex(n); } unsigned D878UVCodeplug::APRSSettingsElement::dmrChannelIndex(unsigned n) const { return getUInt16_le(Offset::dmrChannelIndices() + n*Offset::betweenDMRChannelIndices()); } void D878UVCodeplug::APRSSettingsElement::setDMRChannelIndex(unsigned n, unsigned idx) { setUInt16_le(Offset::dmrChannelIndices() + n*Offset::betweenDMRChannelIndices(), idx); } void D878UVCodeplug::APRSSettingsElement::setDMRChannelSelected(unsigned n) { setDMRChannelIndex(n, 0xfa2); } unsigned D878UVCodeplug::APRSSettingsElement::dmrDestination(unsigned n) const { return getBCD8_be(Offset::dmrDestinations() + n*Offset::betweenDMRDestinations()); } void D878UVCodeplug::APRSSettingsElement::setDMRDestination(unsigned n, unsigned idx) { setBCD8_be(Offset::dmrDestinations() + n*Offset::betweenDMRDestinations(), idx); } DMRContact::Type D878UVCodeplug::APRSSettingsElement::dmrCallType(unsigned n) const { switch(getUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes())) { case 0: return DMRContact::PrivateCall; case 1: return DMRContact::GroupCall; case 2: return DMRContact::AllCall; } return DMRContact::PrivateCall; } void D878UVCodeplug::APRSSettingsElement::setDMRCallType(unsigned n, DMRContact::Type type) { switch(type) { case DMRContact::PrivateCall: setUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes(), 0x00); break; case DMRContact::GroupCall: setUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes(), 0x01); break; case DMRContact::AllCall: setUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes(), 0x02); break; } } bool D878UVCodeplug::APRSSettingsElement::dmrTimeSlotOverride(unsigned n) { return 0x00 != getUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots()); } DMRChannel::TimeSlot D878UVCodeplug::APRSSettingsElement::dmrTimeSlot(unsigned n) const { switch (getUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots())) { case 1: return DMRChannel::TimeSlot::TS1; case 2: return DMRChannel::TimeSlot::TS2; } return DMRChannel::TimeSlot::TS1; } void D878UVCodeplug::APRSSettingsElement::setDMRTimeSlot(unsigned n, DMRChannel::TimeSlot ts) { switch (ts) { case DMRChannel::TimeSlot::TS1: setUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots(), 0x01); break; case DMRChannel::TimeSlot::TS2: setUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots(), 0x02); break; } } void D878UVCodeplug::APRSSettingsElement::clearDMRTimeSlotOverride(unsigned n) { setUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots(), 0); } bool D878UVCodeplug::APRSSettingsElement::dmrRoaming() const { return getUInt8(Offset::roamingSupport()); } void D878UVCodeplug::APRSSettingsElement::enableDMRRoaming(bool enable) { setUInt8(Offset::roamingSupport(), (enable ? 0x01 : 0x00)); } Interval D878UVCodeplug::APRSSettingsElement::dmrPreWaveDelay() const { return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::dmrPrewaveDelay()))*100); } void D878UVCodeplug::APRSSettingsElement::setDMRPreWaveDelay(Interval ms) { setUInt8(Offset::dmrPrewaveDelay(), ms.milliseconds()/100); } bool D878UVCodeplug::APRSSettingsElement::infiniteDisplayTime() const { return 13 == getUInt8(Offset::displayInterval()); } Interval D878UVCodeplug::APRSSettingsElement::displayTime() const { return Interval::fromSeconds(3 + getUInt8(Offset::displayInterval())); } void D878UVCodeplug::APRSSettingsElement::setDisplayTime(Interval dur) { unsigned int sec = dur.seconds(); sec = std::min(16U, std::max(3U, sec)); setUInt8(Offset::displayInterval(), sec-3); } void D878UVCodeplug::APRSSettingsElement::setDisplayTimeInifinite() { setUInt8(Offset::displayInterval(), 13); } bool D878UVCodeplug::APRSSettingsElement::reportPosition() const { return getBit(Offset::reportPosition(), 0); } void D878UVCodeplug::APRSSettingsElement::enableReportPosition(bool enable) { setBit(Offset::reportPosition(), 0, enable); } bool D878UVCodeplug::APRSSettingsElement::reportMicE() const { return getBit(Offset::reportMicE(), 1); } void D878UVCodeplug::APRSSettingsElement::enableReportMicE(bool enable) { setBit(Offset::reportMicE(), 1, enable); } bool D878UVCodeplug::APRSSettingsElement::reportObject() const { return getBit(Offset::reportObject(), 2); } void D878UVCodeplug::APRSSettingsElement::enableReportObject(bool enable) { setBit(Offset::reportObject(), 2, enable); } bool D878UVCodeplug::APRSSettingsElement::reportItem() const { return getBit(Offset::reportItem(), 3); } void D878UVCodeplug::APRSSettingsElement::enableReportItem(bool enable) { setBit(Offset::reportItem(), 3, enable); } bool D878UVCodeplug::APRSSettingsElement::reportMessage() const { return getBit(Offset::reportMessage(), 4); } void D878UVCodeplug::APRSSettingsElement::enableReportMessage(bool enable) { setBit(Offset::reportMessage(), 4, enable); } bool D878UVCodeplug::APRSSettingsElement::reportWeather() const { return getBit(Offset::reportWeather(), 5); } void D878UVCodeplug::APRSSettingsElement::enableReportWeather(bool enable) { setBit(Offset::reportWeather(), 5, enable); } bool D878UVCodeplug::APRSSettingsElement::reportNMEA() const { return getBit(Offset::reportNMEA(), 6); } void D878UVCodeplug::APRSSettingsElement::enableReportNMEA(bool enable) { setBit(Offset::reportNMEA(), 6, enable); } bool D878UVCodeplug::APRSSettingsElement::reportStatus() const { return getBit(Offset::reportStatus(), 7); } void D878UVCodeplug::APRSSettingsElement::enableReportStatus(bool enable) { setBit(Offset::reportStatus(), 7, enable); } bool D878UVCodeplug::APRSSettingsElement::reportOther() const { return getBit(Offset::reportOther(), 0); } void D878UVCodeplug::APRSSettingsElement::enableReportOther(bool enable) { setBit(Offset::reportOther(), 0, enable); } AnytoneFMAPRSSettingsExtension::Bandwidth D878UVCodeplug::APRSSettingsElement::fmChannelWidth() const { return (AnytoneFMAPRSSettingsExtension::Bandwidth)getUInt8(Offset::fmWidth()); } void D878UVCodeplug::APRSSettingsElement::setFMChannelWidth(AnytoneFMAPRSSettingsExtension::Bandwidth width) { setUInt8(Offset::fmWidth(), (uint8_t)width); } bool D878UVCodeplug::APRSSettingsElement::fmPassAll() const { return 0x00 != getUInt8(Offset::passAll()); } void D878UVCodeplug::APRSSettingsElement::enableFMPassAll(bool enable) { setUInt8(Offset::passAll(), enable ? 0x01 : 0x00); } bool D878UVCodeplug::APRSSettingsElement::fmFrequencySet(unsigned int n) const { n = std::min(Limit::fmFrequencies()-1, n); return 0 != getBCD8_be(Offset::fmFrequencies() + n*Offset::betweenFMFrequencies()); } Frequency D878UVCodeplug::APRSSettingsElement::fmFrequency(unsigned int n) const { n = std::min(Limit::fmFrequencies()-1, n); return Frequency::fromHz(getBCD8_be(Offset::fmFrequencies() + n*Offset::betweenFMFrequencies())*10); } void D878UVCodeplug::APRSSettingsElement::setFMFrequency(unsigned int n, Frequency f) { n = std::min(Limit::fmFrequencies()-1, n); setBCD8_be(Offset::fmFrequencies() + n*Offset::betweenFMFrequencies(), f.inHz()/10); } void D878UVCodeplug::APRSSettingsElement::clearFMFrequency(unsigned int n) { n = std::min(Limit::fmFrequencies()-1, n); setBCD8_be(Offset::fmFrequencies() + n*Offset::betweenFMFrequencies(), 0); } bool D878UVCodeplug::APRSSettingsElement::fromFMAPRSSystem( const APRSSystem *sys, Context &ctx, FMAPRSFrequencyNamesElement &names, const ErrorStack &err) { Q_UNUSED(ctx) clear(); if (! sys->hasRevertChannel()) { errMsg(err) << "Cannot encode APRS settings: " << "No revert channel defined for APRS system '" << sys->name() <<"'."; return false; } names.setName(0, sys->name()); setFMFrequency(0, sys->revertChannel()->txFrequency()); setTXTone(sys->revertChannel()->txTone()); setPower(sys->revertChannel()->power()); setFMChannelWidth(FMChannel::Bandwidth::Wide == sys->revertChannel()->bandwidth() ? AnytoneFMAPRSSettingsExtension::Bandwidth::Wide : AnytoneFMAPRSSettingsExtension::Bandwidth::Narrow); setManualTXInterval(Interval::fromSeconds(sys->period())); setAutoTXInterval(Interval::fromSeconds(sys->period())); setDestination(sys->destination(), sys->destSSID()); setSource(sys->source(), sys->srcSSID()); setPath(sys->path()); setIcon(sys->icon()); setFMPreWaveDelay(Interval()); // Handle FM APRS Settings if set AnytoneFMAPRSSettingsExtension *ext = sys->anytoneExtension(); if (nullptr == ext) return true; setFMTXDelay(ext->txDelay()); setFMPreWaveDelay(ext->preWaveDelay()); enableFMPassAll(ext->passAll()); enableReportPosition(ext->reportPosition()); enableReportMicE(ext->reportMicE()); enableReportObject(ext->reportObject()); enableReportItem(ext->reportItem()); enableReportMessage(ext->reportMessage()); enableReportWeather(ext->reportWeather()); enableReportNMEA(ext->reportNMEA()); enableReportStatus(ext->reportStatus()); enableReportOther(ext->reportOther()); // Encode additional FM APRS frequencies for (int i=0; ifrequencies()->count(); i++) { setFMFrequency(ctx.index(ext->frequencies()->get(i)), ext->frequencies()->get(i)->as()->frequency()); names.setName(ctx.index(ext->frequencies()->get(i)), ext->frequencies()->get(i)->name()); } return true; } APRSSystem * D878UVCodeplug::APRSSettingsElement::toFMAPRSSystem(Context &ctx, const FMAPRSFrequencyNamesElement &names, const ErrorStack &err) { Q_UNUSED(err) QString name = QString("APRS %1").arg(destination()); if (names.isValid() && (! names.name(0).isEmpty())) name = names.name(0); APRSSystem *sys = new APRSSystem( name, nullptr, destination(), destinationSSID(), source(), sourceSSID(), path(), icon(), "", autoTXInterval().seconds()); // Decode extension AnytoneFMAPRSSettingsExtension *ext = new AnytoneFMAPRSSettingsExtension(); sys->setAnytoneExtension(ext); ext->setTXDelay(fmTXDelay()); ext->setPreWaveDelay(fmPreWaveDelay()); ext->enablePassAll(fmPassAll()); ext->enableReportPosition(reportPosition()); ext->enableReportMicE(reportMicE()); ext->enableReportObject(reportObject()); ext->enableReportItem(reportItem()); ext->enableReportMessage(reportMessage()); ext->enableReportWeather(reportWeather()); ext->enableReportNMEA(reportNMEA()); ext->enableReportStatus(reportStatus()); ext->enableReportOther(reportOther()); for (unsigned int i=1; isetFrequency(fmFrequency(i)); QString name = QString("APRS %1").arg(i); if (names.isValid() && (! names.name(i).isEmpty())) name = names.name(i); f->setName(name); ext->frequencies()->add(f); ctx.add(f, i); } return sys; } bool D878UVCodeplug::APRSSettingsElement::linkFMAPRSSystem(APRSSystem *sys, Context &ctx) { // First, try to find a matching analog channel in list FMChannel *ch = ctx.config()->channelList()->findFMChannelByTxFreq(fmFrequency(0)); if (! ch) { // If no channel is found, create one with the settings from APRS channel: ch = new FMChannel(); ch->setName("APRS Channel"); ch->setRXFrequency(fmFrequency(0)); ch->setTXFrequency(fmFrequency(0)); ch->setPower(power()); ch->setTXTone(txTone()); ch->setBandwidth(AnytoneFMAPRSSettingsExtension::Bandwidth::Wide == fmChannelWidth() ? FMChannel::Bandwidth::Wide : FMChannel::Bandwidth::Narrow); logInfo() << "No matching APRS channel found for TX frequency " << double(fmFrequency(0).inHz())/1e6 << "MHz, create one as 'APRS Channel'"; ctx.config()->channelList()->add(ch); } sys->setRevertChannel(ch); return true; } bool D878UVCodeplug::APRSSettingsElement::fromDMRAPRSSystems(Context &ctx) { unsigned int n = std::min(ctx.count(), Limit::dmrSystems()); for (unsigned int idx=0; idx(idx), ctx); return true; } bool D878UVCodeplug::APRSSettingsElement::fromDMRAPRSSystemObj(unsigned int idx, GPSSystem *sys, Context &ctx) { if (sys->hasContact()) { setDMRDestination(idx, sys->contactObj()->number()); setDMRCallType(idx, sys->contactObj()->type()); } if (sys->hasRevertChannel()) { setDMRChannelIndex(idx, ctx.index(sys->revertChannel())); clearDMRTimeSlotOverride(idx); } else { // no revert channel specified or "selected channel": setDMRChannelSelected(idx); } return true; } GPSSystem * D878UVCodeplug::APRSSettingsElement::toDMRAPRSSystemObj(int idx) const { if (0 == dmrDestination(idx)) return nullptr; return new GPSSystem(tr("GPS Sys #%1").arg(idx+1)); } bool D878UVCodeplug::APRSSettingsElement::linkDMRAPRSSystem(int idx, GPSSystem *sys, Context &ctx) const { // if a revert channel is defined -> link to it if (dmrChannelIsSelected(idx)) sys->resetRevertChannel(); else if (ctx.has(dmrChannelIndex(idx)) && ctx.get(dmrChannelIndex(idx))->is()) sys->setRevertChannel(ctx.get(dmrChannelIndex(idx))->as()); // Search for a matching contact in contacts DMRContact *cont = ctx.config()->contacts()->findDigitalContact(dmrDestination(idx)); // If no matching contact is found, create one if (nullptr == cont) { cont = new DMRContact(dmrCallType(idx), tr("GPS #%1 Contact").arg(idx+1), dmrDestination(idx), false); ctx.config()->contacts()->add(cont); } // link contact to GPS system. sys->setContactObj(cont); return true; } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::AnalogAPRSMessageElement * ******************************************************************************************** */ D878UVCodeplug::AnalogAPRSMessageElement::AnalogAPRSMessageElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } D878UVCodeplug::AnalogAPRSMessageElement::AnalogAPRSMessageElement(uint8_t *ptr) : Element(ptr, AnalogAPRSMessageElement::size()) { // pass... } void D878UVCodeplug::AnalogAPRSMessageElement::clear() { memset(_data, 0x00, _size); } QString D878UVCodeplug::AnalogAPRSMessageElement::message() const { return readASCII(0, Limit::length(), 0x00); } void D878UVCodeplug::AnalogAPRSMessageElement::setMessage(const QString &msg) { writeASCII(0, msg, Limit::length(), 0x00); } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::AnalogAPRSRXEntryElement * ******************************************************************************************** */ D878UVCodeplug::AnalogAPRSRXEntryElement::AnalogAPRSRXEntryElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } D878UVCodeplug::AnalogAPRSRXEntryElement::AnalogAPRSRXEntryElement(uint8_t *ptr) : Element(ptr, 0x0008) { // pass... } void D878UVCodeplug::AnalogAPRSRXEntryElement::clear() { memset(_data, 0x00, _size); } bool D878UVCodeplug::AnalogAPRSRXEntryElement::isValid() const { return Element::isValid() && (0 != getUInt8(0x0000)); } QString D878UVCodeplug::AnalogAPRSRXEntryElement::call() const { return readASCII(0x0001, 6, 0x00); } unsigned D878UVCodeplug::AnalogAPRSRXEntryElement::ssid() const { return getUInt8(0x0007); } void D878UVCodeplug::AnalogAPRSRXEntryElement::setCall(const QString &call, unsigned ssid) { writeASCII(0x0001, call, 6, 0x00); // Store call setUInt8(0x0007, ssid); // Store SSID setUInt8(0x0000, 0x01); // Enable entry. } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::AESEncryptionKeyElement * ******************************************************************************************** */ D878UVCodeplug::AESEncryptionKeyElement::AESEncryptionKeyElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } D878UVCodeplug::AESEncryptionKeyElement::AESEncryptionKeyElement(uint8_t *ptr) : Element(ptr, AESEncryptionKeyElement::size()) { // pass... } void D878UVCodeplug::AESEncryptionKeyElement::clear() { memset(_data, 0x00, _size); setIndex(0xff); setUInt8(0x0022, 0x40); } bool D878UVCodeplug::AESEncryptionKeyElement::isValid() const { return Element::isValid() && (0xff != index()); } unsigned D878UVCodeplug::AESEncryptionKeyElement::index() const { return getUInt8(0x0000); } void D878UVCodeplug::AESEncryptionKeyElement::setIndex(unsigned idx) { setUInt8(0x0000, idx); } QByteArray D878UVCodeplug::AESEncryptionKeyElement::key() const { QByteArray ar(32, 0); memcpy(ar.data(), _data+0x0001, 32); return ar; } void D878UVCodeplug::AESEncryptionKeyElement::setKey(const QByteArray &key) { if (32 != key.size()) return; memcpy(_data+0x0001, key.constData(), 32); } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::HiddenZoneBitmapElement * ******************************************************************************************** */ D878UVCodeplug::HiddenZoneBitmapElement::HiddenZoneBitmapElement(uint8_t *ptr, size_t size) : BitmapElement(ptr, size) { // pass... } D878UVCodeplug::HiddenZoneBitmapElement::HiddenZoneBitmapElement(uint8_t *ptr) : BitmapElement(ptr, HiddenZoneBitmapElement::size()) { // pass... } /* ******************************************************************************************** * * Implementation of D878UVCodeplug::RadioInfoElement * ******************************************************************************************** */ D878UVCodeplug::RadioInfoElement::RadioInfoElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } D878UVCodeplug::RadioInfoElement::RadioInfoElement(uint8_t *ptr) : Element(ptr, 0x0100) { // pass... } void D878UVCodeplug::RadioInfoElement::clear() { setBandSelectPassword(""); setProgramPassword(""); } bool D878UVCodeplug::RadioInfoElement::fullTest() const { return getUInt8(0x0002); } D878UVCodeplug::RadioInfoElement::FrequencyRange D878UVCodeplug::RadioInfoElement::frequencyRange() const { return (FrequencyRange)getUInt8(0x0003); } void D878UVCodeplug::RadioInfoElement::setFrequencyRange(FrequencyRange range) { setUInt8(0x0003, (unsigned)range); } bool D878UVCodeplug::RadioInfoElement::international() const { return getUInt8(0x0004); } void D878UVCodeplug::RadioInfoElement::enableInternational(bool enable) { setUInt8(0x0004, (enable ? 0x01 : 0x00)); } bool D878UVCodeplug::RadioInfoElement::bandSelect() const { return getUInt8(0x0006); } void D878UVCodeplug::RadioInfoElement::enableBandSelect(bool enable) { setUInt8(0x0006, (enable ? 0x01 : 0x00)); } QString D878UVCodeplug::RadioInfoElement::bandSelectPassword() const { return readASCII(0x000b, 4, 0x00); } void D878UVCodeplug::RadioInfoElement::setBandSelectPassword(const QString &passwd) { writeASCII(0x000b, passwd, 4, 0x00); } QString D878UVCodeplug::RadioInfoElement::radioType() const { return readASCII(0x0010, 7, 0x00); } QString D878UVCodeplug::RadioInfoElement::programPassword() const { return readASCII(0x0028, 4, 0x00); } void D878UVCodeplug::RadioInfoElement::setProgramPassword(const QString &passwd) { writeASCII(0x0028, passwd, 4, 0x00); } QString D878UVCodeplug::RadioInfoElement::areaCode() const { return readASCII(0x002c, 4, 0x00); } QString D878UVCodeplug::RadioInfoElement::serialNumber() const { return readASCII(0x0030, 16, 0x00); } QString D878UVCodeplug::RadioInfoElement::productionDate() const { return readASCII(0x0040, 10, 0x00); } QString D878UVCodeplug::RadioInfoElement::manufacturerCode() const { return readASCII(0x0050, 8, 0x00); } QString D878UVCodeplug::RadioInfoElement::maintainedDate() const { return readASCII(0x0060, 16, 0x00); } QString D878UVCodeplug::RadioInfoElement::dealerCode() const { return readASCII(0x0070, 16, 0x00); } QString D878UVCodeplug::RadioInfoElement::stockDate() const { return readASCII(0x0080, 16, 0x00); } QString D878UVCodeplug::RadioInfoElement::sellDate() const { return readASCII(0x0090, 16, 0x00); } QString D878UVCodeplug::RadioInfoElement::seller() const { return readASCII(0x00a0, 16, 0x00); } QString D878UVCodeplug::RadioInfoElement::maintainerNote() const { return readASCII(0x00b0, 128, 0x00); } /* ******************************************************************************************** * * Implementation of D878UVCodeplug * ******************************************************************************************** */ D878UVCodeplug::D878UVCodeplug(const QString &label, QObject *parent) : D868UVCodeplug(label, parent) { // pass... } D878UVCodeplug::D878UVCodeplug(QObject *parent) : D868UVCodeplug("Anytone AT-D878UV Codeplug", parent) { // pass... } bool D878UVCodeplug::allocateBitmaps() { if (! D868UVCodeplug::allocateBitmaps()) return false; // Roaming channel bitmaps image(0).addElement(Offset::roamingChannelBitmap(), RoamingChannelBitmapElement::size()); // Roaming zone bitmaps image(0).addElement(Offset::roamingZoneBitmap(), RoamingZoneBitmapElement::size()); return true; } void D878UVCodeplug::allocateUpdated() { // First allocate everything common between D868UV and D878UV codeplugs. D868UVCodeplug::allocateUpdated(); // Encryption keys image(0).addElement(Offset::aesKeys(), Limit::aesKeys()*AESEncryptionKeyElement::size()); // allocate APRS RX list image(0).addElement(Offset::analogAPRSRXEntries(), Limit::analogAPRSRXEntries()*AnalogAPRSRXEntryElement::size()); // allocate FM APRS frequency names image(0).addElement(Offset::fmAPRSFrequencyNames(), FMAPRSFrequencyNamesElement::size()); } void D878UVCodeplug::allocateForEncoding() { // First allocate everything common between D868UV and D878UV codeplugs. D868UVCodeplug::allocateForEncoding(); this->allocateRoaming(); } void D878UVCodeplug::allocateForDecoding() { // First allocate everything common between D868UV and D878UV codeplugs. D868UVCodeplug::allocateForDecoding(); this->allocateRoaming(); // allocate FM APRS frequency names image(0).addElement(Offset::fmAPRSFrequencyNames(), FMAPRSFrequencyNamesElement::size()); } void D878UVCodeplug::setBitmaps(Context& ctx) { // First set everything common between D868UV and D878UV codeplugs. D868UVCodeplug::setBitmaps(ctx); // Mark roaming zones RoamingZoneBitmapElement roaming_zone_bitmap(data(Offset::roamingZoneBitmap())); unsigned int num_roaming_zones = std::min(Limit::roamingZones(), ctx.count()); roaming_zone_bitmap.clear(); roaming_zone_bitmap.enableFirst(num_roaming_zones); // Mark roaming channels RoamingChannelBitmapElement roaming_ch_bitmap(data(Offset::roamingChannelBitmap())); unsigned int num_roaming_channel = std::min(Limit::roamingChannels(), ctx.count()); roaming_ch_bitmap.clear(); roaming_ch_bitmap.enableFirst(num_roaming_channel); } bool D878UVCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) { // Encode everything common between d868uv and d878uv radios. if (! D868UVCodeplug::encodeElements(flags, ctx, err)) return false; if (! this->encodeRoaming(flags, ctx, err)) return false; return true; } bool D878UVCodeplug::decodeElements(Context &ctx, const ErrorStack &err) { // Decode everything commong between d868uv and d878uv codeplugs. if (! D868UVCodeplug::decodeElements(ctx, err)) return false; if (! this->createRoaming(ctx, err)) return false; if (! this->linkRoaming(ctx, err)) return false; return true; } void D878UVCodeplug::allocateChannels() { /* Allocate channels */ ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap())); for (uint16_t i=0; i skip if (! channel_bitmap.isEncoded(i)) continue; // compute address for channel uint16_t bank = i/Limit::channelsPerBank(), idx=i%Limit::channelsPerBank(); uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks() + idx * ChannelElement::size(); if (!isAllocated(addr, 0)) { image(0).addElement(addr, ChannelElement::size()); } if (!isAllocated(addr+Offset::toChannelExtension(), 0)) { image(0).addElement(addr+Offset::toChannelExtension(), ChannelElement::size()); memset(data(addr+Offset::toChannelExtension()), 0x00, ChannelElement::size()); } } } bool D878UVCodeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err) // Encode channels for (int i=0; ichannelList()->count(); i++) { // enable channel uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank(); uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks() + idx*ChannelElement::size(); ChannelElement ch(data(addr)); ch.fromChannelObj(ctx.config()->channelList()->channel(i), ctx); ChannelExtensionElement ext(data(addr + Offset::toChannelExtension())); ext.fromChannelObj(ctx.config()->channelList()->channel(i), ctx); } return true; } bool D878UVCodeplug::createChannels(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Create channels ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap())); for (uint16_t i=0; ichannelList()->add(obj); ctx.add(obj, i); ext.updateChannelObj(obj, ctx); } } return true; } bool D878UVCodeplug::linkChannels(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap())); // Link channel objects for (uint16_t i=0; i(i)) { ch.linkChannelObj(ctx.get(i), ctx); ext.linkChannelObj(ctx.get(i), ctx); } } return true; } void D878UVCodeplug::allocateZones() { D868UVCodeplug::allocateZones(); // Hidden zone map image(0).addElement(Offset::hiddenZoneBitmap(), HiddenZoneBitmapElement::size()); } bool D878UVCodeplug::encodeZone(int i, Zone *zone, const Flags &flags, Context &ctx, const ErrorStack &err) { if (! D868UVCodeplug::encodeZone(i, zone, flags, ctx, err)) return false; AnytoneZoneExtension *ext = zone->anytoneExtension(); if (nullptr == ext) return true; HiddenZoneBitmapElement(data(Offset::hiddenZoneBitmap())).setEncoded(i, ext->hidden()); return true; } bool D878UVCodeplug::decodeZone(int i, Zone *zone, Context &ctx, const ErrorStack &err) { if (! D868UVCodeplug::decodeZone(i, zone, ctx, err)) return false; AnytoneZoneExtension *ext = zone->anytoneExtension(); if (nullptr == ext) { ext = new AnytoneZoneExtension(); zone->setAnytoneExtension(ext); } HiddenZoneBitmapElement bitmap(data(Offset::hiddenZoneBitmap())); ext->enableHidden(bitmap.isEncoded(i)); return true; } void D878UVCodeplug::allocateGeneralSettings() { // override allocation of general settings for D878UV code-plug. General settings are larger! image(0).addElement(Offset::settings(), GeneralSettingsElement::size()); image(0).addElement(Offset::dmrAPRSMessage(), DMRAPRSMessageElement::size()); image(0).addElement(Offset::settingsExtension(), ExtendedSettingsElement::size()); } bool D878UVCodeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) GeneralSettingsElement(data(Offset::settings())).fromConfig(flags, ctx); DMRAPRSMessageElement(data(Offset::dmrAPRSMessage())).fromConfig(flags, ctx); ExtendedSettingsElement(data(Offset::settingsExtension())).fromConfig(flags, ctx); return true; } bool D878UVCodeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx); DMRAPRSMessageElement(data(Offset::dmrAPRSMessage())).updateConfig(ctx); ExtendedSettingsElement(data(Offset::settingsExtension())).updateConfig(ctx); return true; } bool D878UVCodeplug::linkGeneralSettings(Context &ctx, const ErrorStack &err) { if (! GeneralSettingsElement(data(Offset::settings())).linkSettings(ctx.config()->settings(), ctx, err)) { errMsg(err) << "Cannot link general settings extension."; return false; } if (! ExtendedSettingsElement(data(Offset::settingsExtension())).linkConfig(ctx, err)) { errMsg(err) << "Cannot link general settings extension."; return false; } return true; } void D878UVCodeplug::allocateGPSSystems() { // replaces D868UVCodeplug::allocateGPSSystems // APRS settings image(0).addElement(Offset::aprsSettings(), APRSSettingsElement::size()); image(0).addElement(Offset::analogAPRSMessage(), AnalogAPRSMessageElement::size()); } bool D878UVCodeplug::encodeGPSSystems(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err) // replaces D868UVCodeplug::encodeGPSSystems APRSSettingsElement aprs(data(Offset::aprsSettings())); FMAPRSFrequencyNamesElement aprsNames(data(Offset::fmAPRSFrequencyNames())); // Encode APRS system (there can only be one) if (0 < ctx.config()->posSystems()->aprsCount()) { aprs.fromFMAPRSSystem(ctx.config()->posSystems()->aprsSystem(0), ctx, aprsNames, err); AnalogAPRSMessageElement(data(Offset::analogAPRSMessage())) .setMessage(ctx.config()->posSystems()->aprsSystem(0)->message()); } // Encode GPS systems if (! aprs.fromDMRAPRSSystems(ctx)) return false; if (0 < ctx.config()->posSystems()->gpsCount()) { // If there is at least one GPS system defined -> set auto TX interval. // This setting might be overridden by any analog APRS system below APRSSettingsElement aprs(data(Offset::aprsSettings())); aprs.setAutoTXInterval(Interval::fromSeconds(ctx.config()->posSystems()->gpsSystem(0)->period())); aprs.setManualTXInterval(Interval::fromSeconds(ctx.config()->posSystems()->gpsSystem(0)->period())); } return true; } bool D878UVCodeplug::createGPSSystems(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // replaces D868UVCodeplug::createGPSSystems // Before creating any GPS/APRS systems, get global auto TX interval APRSSettingsElement aprs(data(Offset::aprsSettings())); FMAPRSFrequencyNamesElement aprsNames(isAllocated(Offset::fmAPRSFrequencyNames()) ? data(Offset::fmAPRSFrequencyNames()): nullptr); AnalogAPRSMessageElement aprsMessage(data(Offset::analogAPRSMessage())); unsigned pos_intervall = aprs.autoTXInterval().seconds(); // Create APRS system (if enabled) if (aprs.isValid()) { APRSSystem *sys = aprs.toFMAPRSSystem(ctx, aprsNames, err); if (nullptr == sys) { errMsg(err) << "Cannot decode positioning systems."; return false; } sys->setPeriod(pos_intervall); sys->setMessage(aprsMessage.message()); ctx.config()->posSystems()->add(sys); ctx.add(sys,0); } // Create GPS systems for (unsigned int i=0; iname() << "' at idx " << i << "."; sys->setPeriod(pos_intervall); ctx.config()->posSystems()->add(sys); ctx.add(sys, i); } else { return false; } } return true; } bool D878UVCodeplug::linkGPSSystems(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // replaces D868UVCodeplug::linkGPSSystems // Link APRS system APRSSettingsElement aprs(data(Offset::aprsSettings())); if (aprs.isValid()) { aprs.linkFMAPRSSystem(ctx.config()->posSystems()->aprsSystem(0), ctx); } // Link GPS systems for (unsigned int i=0; i(i), ctx); } return true; } void D878UVCodeplug::allocateRoaming() { /* Allocate roaming channels */ RoamingChannelBitmapElement roaming_channel_bitmap(data(Offset::roamingChannelBitmap())); for (uint8_t i=0; i skip if (! roaming_channel_bitmap.isEncoded(i)) continue; // Allocate roaming channel uint32_t addr = Offset::roamingChannels() + i*RoamingChannelElement::size(); if (!isAllocated(addr, 0)) { //logDebug() << "Allocate roaming channel at " << QString::number(addr, 16) << "h."; image(0).addElement(addr, RoamingChannelElement::size()); } } /* Allocate roaming zones. */ RoamingZoneBitmapElement roaming_zone_bitmap(data(Offset::roamingZoneBitmap())); for (uint8_t i=0; i skip if (! roaming_zone_bitmap.isEncoded(i)) continue; // Allocate roaming zone uint32_t addr = Offset::roamingZones() + i*RoamingZoneElement::size(); if (!isAllocated(addr, 0)) { logDebug() << "Allocate roaming zone at " << QString::number(addr, 16); image(0).addElement(addr, RoamingZoneElement::size()); } } } bool D878UVCodeplug::encodeRoaming(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err); // Encode roaming channels unsigned int num_roaming_channel = std::min( Limit::roamingChannels(), ctx.count()); for (uint8_t i=0; iroamingChannels()->get(i)->as(); rch_elm.clear(); rch_elm.fromChannel(rch); if (! ctx.add(rch, i)) { errMsg(err) << "Cannot add index " << i << " for roaming channel '" << rch->name() << "' to codeplug context."; return false; } } // Encode roaming zones for (unsigned int i=0; i(); i++){ uint32_t addr = Offset::roamingZones() + i*RoamingZoneElement::size(); RoamingZoneElement zone(data(addr)); logDebug() << "Encode roaming zone " << ctx.config()->roamingZones()->zone(i)->name() << " (" << (i+1) << ") at " << QString::number(addr, 16) << " with " << ctx.config()->roamingZones()->zone(i)->count() << " elements."; zone.fromRoamingZone(ctx.config()->roamingZones()->zone(i), ctx); } return true; } bool D878UVCodeplug::createRoaming(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Create or find roaming channels RoamingChannelBitmapElement roaming_channel_bitmap(data(Offset::roamingChannelBitmap())); for (unsigned int i=0; iroamingZones()->add(zone); ctx.add(zone, i); z.linkRoamingZone(zone, ctx, err); } return true; } bool D878UVCodeplug::linkRoaming(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err) // Pass, no need to link roaming channels. return true; } qdmr-0.12.3/lib/d878uv_codeplug.hh000066400000000000000000002661131501654372000165720ustar00rootroot00000000000000#ifndef D878UV_CODEPLUG_HH #define D878UV_CODEPLUG_HH #include #include "d868uv_codeplug.hh" #include "signaling.hh" #include "gpssystem.hh" class Channel; class DMRContact; class Zone; class RXGroupList; class ScanList; class GPSSystem; class RoamingChannel; /** Represents the device specific binary codeplug for Anytone AT-D878UV radios. * * In contrast to many other code-plugs, the code-plug for Anytone radios are spread over a large * memory area. In principle, this is a good idea, as it allows one to upload only the portion of the * codeplug that is actually configured. For example, if only a small portion of the available * contacts and channels are used, the amount of data that is written to the device can be * reduced. * * However, the implementation of this idea in this device is utter shit. The amount of * fragmentation of the codeplug is overwhelming. For example, while channels are organized more or * less nicely in continuous banks, zones are distributed throughout the entire code-plug. That is, * the names of zones are located in a different memory section that the channel lists. Some lists * are defined though bit-masks others by byte-masks. All bit-masks are positive, that is 1 * indicates an enabled item while the bit-mask for contacts is inverted. * * In general the code-plug is huge and complex. Moreover, the radio provides a huge amount of * options and features. To this end, reverse-engineering this code-plug was a nightmare. * * More over, the binary code-plug file generate by the windows CPS does not directly relate to * the data being written to the radio. To this end the code-plug has been reverse-engineered * using wireshark to monitor the USB communication between the windows CPS (running in a virtual * box) and the device. The latter makes the reverse-engineering particularly cumbersome. * * @section d878uvcpl Codeplug structure within radio * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Channels
      Start Size Content
      024C1500 000200 Bitmap of 4000 channels, default 0x00, * 0x00 padded.
      00800000 max. 002000 Channel bank 0 of up to 128 channels, * see @c D878UVCodeplug::ChannelElement 64 b each.
      00802000 max, 002000 Unknown data, Maybe extended channel information * for channel bank 0? It is of exactly the same size as the channel bank 0. Mostly 0x00, a * few 0xff.
      00840000 max. 002000 Channel bank 1 of up to 128 channels.
      00842000 max. 002000 Unknown data, related to CH bank 1?
      ... ... ...
      00FC0000 max. 000800 Channel bank 32, up to 32 channels.
      00FC2000 max. 000800 Unknown data, related to CH bank 32.
      00FC0800 000040 VFO A settings, * see @c D878UVCodeplug::ChannelElement.
      00FC0840 000040 VFO B settings, * see @c D878UVCodeplug::ChannelElement.
      00FC2800 000080 Unknown data, related to VFO A+B? * It is of exactly the same size as the two VFO channels. Mostly 0x00, a few 0xff. Same pattern * as the unknown data associated with channel banks.
      Zones
      Start Size Content
      024C1300 000020 Bitmap of 250 zones.
      024C1360 000020 Hidden zone bitmap of 250 zones.
      01000000 max. 01f400 250 zones channel lists of 250 16bit indices each. * 0-based, little endian, default/padded=0xffff. Offset between channel lists 0x200, * size of each list 0x1f4.
      02540000 max. 001f40 250 Zone names. * Each zone name is up to 16 ASCII chars long and gets 0-padded to 32b.
      Roaming
      Start Size Content
      01042000 000020 Roaming channel bitmask, up to 250 bits, * 0-padded, default 0.
      01040000 max. 0x1f40 Optional up to 250 roaming channels, of 32b each. * See @c D878UVCodeplug::RoamingChannelElement for details.
      01042080 000010 Roaming zone bitmask, up to 64 bits, 0-padded, * default 0.
      01043000 max. 0x2000 Optional up to 64 roaming zones, of 128b each. * See @c D878UVCodeplug::RoamingZoneElement for details.
      Contacts
      Start Size Content
      02600000 max. 009C40 Index list of valid contacts. * 10000 32bit indices, little endian, default 0xffffffff
      02640000 000500 Contact bitmap, 10000 bit, inverted, * default 0xff, 0x00 padded.
      02680000 max. 0f4240 10000 contacts, * see @c AnytoneCodeplug::ContactElement. As each contact is 100b, they do not align with the * 16b blocks being transferred to the device. Hence contacts are organized internally in groups * of 4 contacts forming a "bank".
      04340000 max. 013880 DMR ID to contact index map, * see @c AnytoneCodeplug::ContactMapElement. Sorted by ID, empty entries set to * @c 0xffffffffffffffff.
      Analog Contacts
      Start Size Content
      02900000 000080 Index list of valid analog contacts.
      02900100 000080 Bytemap for 128 analog contacts.
      02940000 max. 000180 128 analog contacts. * See @c AnytoneCodeplug::DTMFContactElement. As each analog contact is 24b, they do not align with * the 16b transfer block-size. Hence analog contacts are internally organized in groups of 2.
      RX Group Lists
      Start Size Content
      025C0B10 000020 Bitmap of 250 RX group lists, * default/padding 0x00.
      02980000 max. 000120 Grouplist 0, * see @c AnytoneCodeplug::GroupListElement.
      02980200 max. 000120 Grouplist 1
      ... ... ...
      0299f200 max. 000120 Grouplist 250
      Scan lists
      Start Size Content
      024C1340 000020 Bitmap of 250 scan lists.
      01080000 000090 Bank 0, Scanlist 1, * see @c AnytoneCodeplug::ScanListElement.
      01080200 000090 Bank 0, Scanlist 2
      ... ... ...
      01081E00 000090 Bank 0, Scanlist 16
      010C0000 000090 Bank 1, Scanlist 17
      ... ... ...
      01440000 000090 Bank 15, Scanlist 241
      ... ... ...
      01441400 000090 Bank 15, Scanlist 250
      Radio IDs
      Start Size Content
      024C1320 000020 Bitmap of 250 radio IDs.
      02580000 max. 001f40 250 Radio IDs. * See @c AnytoneCodeplug::RadioIDElement.
      GPS/APRS
      Start Size Content
      02501000 000100 APRS settings, * see @c D878UVCodeplug::APRSSettingsElement.
      02501200 000040 APRS Text, up to 60 chars ASCII, 0-padded.
      02501280 000030 APRS template text (optional settings).
      02501800 000100 APRS-RX settings list up to 32 entries, 8b each. * See @c D878UVCodeplug::AnalogAPRSRXEntryElement.
      02502000 000080 FM APRS frequency names, * see @c FMAPRSFrequencyNamesElement. This element is not part of the manufacturer codeplug. * QDMR uses this memory section to store additional information.
      General Settings
      Start Size Content
      02500000 0000f0 General settings, * see @c D878UVCodeplug::GeneralSettingsElement.
      02500100 000400 Zone A & B channel list.
      02500500 000100 DTMF list
      02500600 000030 Power on settings, * see @c AnytoneCodeplug::BootSettingsElement.
      02501400 000200 General settings extension, * see @c D878UVCodeplug::GeneralSettingsExtensionElement.
      024C2000 0003F0 List of 250 auto-repeater offset frequencies. * 32bit little endian frequency in 10Hz. I.e., 600kHz = 60000. * Default 0x00000000, 0x00 padded.
      Messages
      Start Size Content
      01640000 max. 000100 Some kind of linked list of messages. * See @c AnytoneCodeplug::MessageListElement. Each entry has a size of 0x10.
      01640800 000090 Bytemap of up to 100 valid messages. * 0x00=valid, 0xff=invalid, remaining 46b set to 0x00.
      02140000 max. 000800 Bank 0, Messages 1-8. * Each message consumes 0x100b. See @c AnytoneCodeplug::MessageElement.
      02180000 max. 000800 Bank 1, Messages 9-16
      ... ... ...
      02440000 max. 000800 Bank 12, Messages 97-100
      Hot Keys
      Start Size Content
      025C0000 000100 4 analog quick-call settings. * See @c AnytoneCodeplug::AnalogQuickCallElement.
      025C0B00 000010 Status message bitmap.
      025C0100 000400 Up to 32 status messages. * Length unknown, offset 0x20. ASCII 0x00 terminated and padded.
      025C0500 000360 18 hot-key settings, see * @c AnytoneCodeplug::HotKeyElement
      Encryption
      Start Size Content
      024C1700 000040 32 Encryption IDs, 0-based, 16bit big-endian.
      024C1800 000500 32 DMR-Encryption keys, * see @c D868UVCodeplug::dmr_encryption_key_t, * 40b each.
      024C4000 004000 Up to 256 AES encryption keys. * See @c D878UVCodeplug::AESEncryptionKeyElement.
      Misc
      Start Size Content
      024C1400 000020 Alarm setting, * see @c AnytoneCodeplug::AlarmSettingElement.
      024C1440 000030 Digital alarm settings extension, * see @c AnytoneCodeplug::DigitalAlarmExtensionElement.
      FM Broadcast
      Start Size Content
      02480210 000020 Bitmap of 100 FM broadcast channels.
      02480000 max. 000200 100 FM broadcast channels. Encoded * as 8-digit BCD little-endian in 100Hz. Filled with 0x00.
      02480200 000010 FM broadcast VFO frequency. Encoded * as 8-digit BCD little-endian in 100Hz. Filled with 0x00.
      DTMF, 2-tone & 5-tone signaling.
      Start Size Content
      024C0C80 000010 5-tone encoding bitmap.
      024C0000 000020 5-tone encoding.
      024C0D00 000200 5-tone ID list.
      024C1000 000080 5-tone settings.
      024C1080 000050 DTMF settings.
      024C1280 000010 2-tone encoding bitmap.
      024C1100 000010 2-tone encoding.
      024C1290 000010 2-tone settings.
      024C2600 000010 2-tone decoding bitmap.
      024C2400 000030 2-tone decoding.
      Unknown settings.
      Start Size Content
      024C1090 000040 Unknown, filled with 0xff.
      02504000 000400 Unknown, filled with 0xff.
      * * @ingroup d878uv */ class D878UVCodeplug : public D868UVCodeplug { Q_OBJECT protected: /** Channel name and call-sign colors supported by the D878UV. */ struct NameColor { public: /** Maps code -> color. */ static AnytoneDisplaySettingsExtension::Color decode(uint8_t code); /** Maps color -> code. */ static uint8_t encode(AnytoneDisplaySettingsExtension::Color color); protected: /** Encoding of the supported colors. */ typedef enum { Orange=0, Red=1, Yellow=2, Green=3, Turquoise=4, Blue=5, White = 6 } CodedColor; }; /** Text colors supported by the D878UV. */ struct TextColor { public: /** Maps code -> color. */ static AnytoneDisplaySettingsExtension::Color decode(uint8_t code); /** Maps color -> code. */ static uint8_t encode(AnytoneDisplaySettingsExtension::Color color); protected: /** Encoding of the supported colors. */ typedef enum { White=0, Black=1, Orange=2, Red=3, Yellow=4, Green=5, Turquoise=6, Blue=7 } CodedColor; }; public: /** Represents the actual channel encoded within the binary D878UV codeplug. * * This class implements only the differences to the generic @c AnytoneCodeplug::ChannelElement * (i.e. D868UVII). * * Memory layout of encoded channel (size 0x40 bytes): * @verbinclude d878uv_channel.txt */ class ChannelElement: public D868UVCodeplug::ChannelElement { public: /** Possible PTT ID settings. */ enum class PTTId { Off = 0, ///< Never send PTT-ID. Start = 1, ///< Send PTT-ID at start. End = 2, ///< Send PTT-ID at end. Both = 3 ///< Send PTT-ID at start and end. }; /** Defines all possible APRS PTT settings. */ enum class APRSPTT { Off = 0, ///< Do not send APRS on PTT. Start = 1, ///< Send APRS at start of transmission. End = 2 ///< Send APRS at end of transmission. }; protected: /** Hidden constructor. */ ChannelElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ChannelElement(uint8_t *ptr); /** Resets the channel. */ void clear(); /** Returns the PTT ID settings. */ virtual PTTId pttIDSetting() const; /** Sets the PTT ID setting. */ virtual void setPTTIDSetting(PTTId ptt); /** Returns @c true if roaming is enabled. */ virtual bool roamingEnabled() const; /** Enables/disables roaming. */ virtual void enableRoaming(bool enable); // Moved /** Returns @c true if the data ACK is enabled. */ bool dataACK() const; /** Enables/disables data ACK. */ void enableDataACK(bool enable); /** Returns @c true if digital APRS transmission is enabled. */ bool txDigitalAPRS() const; /** Enables/disables digital APRS transmission. */ void enableTXDigitalAPRS(bool enable); /** Returns @c true if the analog APRS reporting (TX) is enabled. */ virtual bool txAnalogAPRS() const; /** Enables/disables analog APRS reporting. */ virtual void enableTXAnalogAPRS(bool enable); /** Returns the analog APRS PTT setting. */ virtual APRSPTT analogAPRSPTTSetting() const; /** Sets the analog APRS PTT setting. */ virtual void setAnalogAPRSPTTSetting(APRSPTT ptt); /** Returns the digital APRS PTT setting. */ virtual APRSPTT digitalAPRSPTTSetting() const; /** Sets the digital APRS PTT setting. */ virtual void setDigitalAPRSPTTSetting(APRSPTT ptt); /** Returns the DMR APRS system index. */ virtual unsigned digitalAPRSSystemIndex() const; /** Sets the DMR APRS system index. */ virtual void setDigitalAPRSSystemIndex(unsigned idx); /** Returns the frequency correction in ???. */ virtual int frequenyCorrection() const; /** Sets the frequency correction in ???. */ virtual void setFrequencyCorrection(int corr); /** Returns the index of the FM APRS frequency [0,7]. */ virtual unsigned int fmAPRSFrequencyIndex() const; /** Sets the FM APRS frequency index [0,7]. */ virtual void setFMAPRSFrequencyIndex(unsigned int idx); /** Constructs a Channel object from this element. */ Channel *toChannelObj(Context &ctx) const; /** Links a previously created channel object. */ bool linkChannelObj(Channel *c, Context &ctx) const; /** Encodes the given channel object. */ bool fromChannelObj(const Channel *c, Context &ctx); protected: /** Internal used offsets within the channel element. */ struct Offset : public D868UVCodeplug::ChannelElement::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int pttIDSetting() { return 0x0019; } static constexpr unsigned int roamingEnabled() { return 0x0034; } static constexpr unsigned int dataACK() { return 0x0034; } static constexpr unsigned int txDMRAPRS() { return 0x0035; } static constexpr unsigned int fmAPRSPTTSetting() { return 0x0036; } static constexpr unsigned int dmrAPRSPTTSetting() { return 0x0037; } static constexpr unsigned int dmrAPRSSystemIndex() { return 0x0038; } static constexpr unsigned int frequenyCorrection() { return 0x0039; } static constexpr unsigned int fmAPRSFrequencyIndex() { return 0x003c; } /// @endcond }; }; /** Starting from AT-D878UV, all AnyTone devices encode an channel settings extension element * at an offset 0x2000 to the original channel. Also the size of the extension element is * identical to the channel element itself. This is weird. Anyway. This class encodes/decodes * these settings. * * This implementation is compatible with firmware version 3.06 */ class ChannelExtensionElement: public Codeplug::Element { protected: /** Hidden constructor. */ ChannelExtensionElement(uint8_t *ptr, size_t size); public: /** Default constructor. */ ChannelExtensionElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return ChannelElement::size(); } /** Resets the channel extension. */ void clear(); /** Returns the BOT 5-tone ID index. */ virtual unsigned int bot5ToneIDIndex() const; /** Sets the BOT 5-tone ID index. */ virtual void setBOT5ToneIDIndex(unsigned int idx); /** Returns the EOT 5-tone ID index. */ virtual unsigned int eot5ToneIDIndex() const; /** Sets the EOT 5-tone ID index. */ virtual void setEOT5ToneIDIndex(unsigned int idx); /** Returns the transmit color code. */ virtual unsigned int txColorCode() const; /** Sets the transmit color code. */ virtual void setTXColorCode(unsigned int cc); /** Constructs a Channel object from this element. */ virtual bool updateChannelObj(Channel *c, Context &ctx) const; /** Links a previously created channel object. */ virtual bool linkChannelObj(Channel *c, Context &ctx) const; /** Encodes the given channel object. */ virtual bool fromChannelObj(const Channel *c, Context &ctx); protected: /** Some internal used offsets. */ struct Offset: public Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int bot5ToneIDIndex() { return 0x0000; } static constexpr unsigned int eot5ToneIDIndex() { return 0x0001; } static constexpr unsigned int txColorCode() { return 0x0003; } /// @endcond }; }; /** Represents the general config of the radio within the D878UV binary codeplug. * * This class implements only the differences to the generic * @c AnytonCodeplug::GeneralSettingsElement. * * Binary encoding of the general settings (size 0x00f0 bytes): * @verbinclude d878uv_generalsettings.txt */ class GeneralSettingsElement: public D868UVCodeplug::GeneralSettingsElement { protected: /** Device specific key functions. */ struct KeyFunction { public: /** Encodes key function. */ static uint8_t encode(AnytoneKeySettingsExtension::KeyFunction tone); /** Decodes key function. */ static AnytoneKeySettingsExtension::KeyFunction decode(uint8_t code); protected: /** Device specific key functions. */ typedef enum { Off = 0x00, Voltage = 0x01, Power = 0x02, Repeater = 0x03, Reverse = 0x04, Encryption = 0x05, Call = 0x06, VOX = 0x07, ToggleVFO = 0x08, SubPTT = 0x09, Scan = 0x0a, WFM = 0x0b, Alarm = 0x0c, RecordSwitch = 0x0d, Record = 0x0e, SMS = 0x0f, Dial = 0x10, Monitor = 0x12, ToggleMainChannel = 0x13, HotKey1 = 0x14, HotKey2 = 0x15, HotKey3 = 0x16, HotKey4 = 0x17, HotKey5 = 0x18, HotKey6 = 0x19, WorkAlone = 0x1a, SkipChannel = 0x1b, DMRMonitor = 0x1c, SubChannel = 0x1d, PriorityZone = 0x1e, VFOScan = 0x1f, MICSoundQuality = 0x20, LastCallReply = 0x21, ChannelType = 0x22, Roaming = 0x24, ChannelRanging = 0x25, MaxVolume = 0x26, Slot = 0x27, APRSType = 0x28, Zone = 0x29, RoamingSet = 0x2a, APRSSet = 0x2b, Mute=0x2c, CtcssDcsSet=0x2d, TBSTSend = 0x2e, Bluetooth = 0x2f, GPS = 0x30, ChannelName = 0x31, CDTScan = 0x32, APRSSend = 0x33, APRSInfo = 0x34 } KeyFunctionCode; }; /** Device specific time zones. */ struct TimeZone { public: /** Encodes time zone. */ static uint8_t encode(const QTimeZone& zone); /** Decodes time zone. */ static QTimeZone decode(uint8_t code); protected: /** Vector of possible time-zones. */ static QVector _timeZones; }; protected: /** Possible VFO frequency steps. */ enum FreqStep { FREQ_STEP_2_5kHz = 0, ///< 2.5kHz FREQ_STEP_5kHz = 1, ///< 5kHz FREQ_STEP_6_25kHz = 2, ///< 6.25kHz FREQ_STEP_10kHz = 3, ///< 10kHz FREQ_STEP_12_5kHz = 4, ///< 12.5kHz FREQ_STEP_20kHz = 5, ///< 20kHz FREQ_STEP_25kHz = 6, ///< 25kHz FREQ_STEP_50kHz = 7 ///< 50kHz }; /** DTMF signalling durations. */ enum DTMFDuration { DTMF_DUR_50ms = 0, DTMF_DUR_100ms = 1, DTMF_DUR_200ms = 2, DTMF_DUR_300ms = 3, DTMF_DUR_500ms = 4 }; /** TBST (open repeater) frequencies. */ enum class TBSTFrequency { Hz1000 = 0, Hz1450 = 1, Hz1750 = 2, Hz2100 = 3 }; /** All possible STE (squelch tail eliminate) frequencies. */ enum class STEFrequency { Off = 0, Hz55_2 = 1, Hz259_2 = 2 }; /** Possible background images. */ enum class BackgroundImage { Default=0, Custom1=1, Custom2=2 }; protected: /** Hidden constructor. */ GeneralSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ GeneralSettingsElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x00f0; } /** Resets the general settings. */ void clear(); /** Returns the transmit timeout in seconds. */ virtual unsigned transmitTimeout() const; /** Sets the transmit timeout in seconds. */ virtual void setTransmitTimeout(unsigned tot); /** Returns the UI language. */ virtual AnytoneDisplaySettingsExtension::Language language() const; /** Sets the UI language. */ virtual void setLanguage(AnytoneDisplaySettingsExtension::Language lang); QTimeZone gpsTimeZone() const; void setGPSTimeZone(const QTimeZone &zone); /** Returns the VFO frequency step in kHz. */ virtual Frequency vfoFrequencyStep() const; /** Sets the VFO frequency step in kHz. */ virtual void setVFOFrequencyStep(Frequency kHz); AnytoneKeySettingsExtension::KeyFunction funcKeyAShort() const; void setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyBShort() const; void setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyCShort() const; void setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey1Short() const; void setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey2Short() const; void setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyALong() const; void setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyBLong() const; void setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyCLong() const; void setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey1Long() const; void setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey2Long() const; void setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func); /** Returns the STE (squelch tail eliminate) type. */ virtual AnytoneSettingsExtension::STEType steType() const; /** Sets the STE (squelch tail eliminate) type. */ virtual void setSTEType(AnytoneSettingsExtension::STEType type); /** Returns the STE (squelch tail eliminate) frequency setting in Hz. * A value of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */ virtual double steFrequency() const; /** Sets the STE (squelch tail eliminate) frequency setting. * A value of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */ virtual void setSTEFrequency(double freq); /** Returns the group call hang time in seconds. */ virtual Interval groupCallHangTime() const; /** Sets the group call hang time in seconds. */ virtual void setGroupCallHangTime(Interval sec); /** Returns the private call hang time in seconds. */ virtual Interval privateCallHangTime() const; /** Sets the private call hang time in seconds. */ virtual void setPrivateCallHangTime(Interval sec); /** Returns the pre-wave time in ms. */ virtual Interval preWaveDelay() const; /** Sets the pre-wave time in ms. */ virtual void setPreWaveDelay(Interval ms); /** Returns the wake head-period in ms. */ virtual Interval wakeHeadPeriod() const; /** Sets the wake head-period in ms. */ virtual void setWakeHeadPeriod(Interval ms); /** Returns the wide-FM (broadcast) channel index. */ virtual unsigned wfmChannelIndex() const; /** Sets the wide-FM (broadcast) channel index. */ virtual void setWFMChannelIndex(unsigned idx); /** Returns @c true if the WFM RX is in VFO mode. */ virtual bool wfmVFOEnabled() const; /** Enables/disables VFO mode for WFM RX. */ virtual void enableWFMVFO(bool enable); /** Returns the DTMF tone duration in ms. */ virtual unsigned dtmfToneDuration() const; /** Sets the DTMF tone duration in ms. */ virtual void setDTMFToneDuration(unsigned ms); /** Returns @c true if "man down" is enabled. */ virtual bool manDown() const; /** Enables/disables "man down". */ virtual void enableManDown(bool enable); /** Returns @c true if WFM monitor is enabled. */ virtual bool wfmMonitor() const; /** Enables/disables WFM monitor. */ virtual void enableWFMMonitor(bool enable); /** Returns the TBST frequency. */ virtual Frequency tbstFrequency() const; /** Sets the TBST frequency. */ virtual void setTBSTFrequency(Frequency freq); /** Returns @c true if the "pro mode" is enabled. */ virtual bool proMode() const; /** Enables/disables the "pro mode". */ virtual void enableProMode(bool enable); /** Returns @c true if the own ID is filtered in call lists. */ virtual bool filterOwnID() const; /** Enables/disables filter of own ID in call lists. */ virtual void enableFilterOwnID(bool enable); /** Returns @c true remote stun/kill is enabled. */ virtual bool remoteStunKill() const; /** Enables/disables remote stun/kill. */ virtual void enableRemoteStunKill(bool enable); /** Returns @c true remote monitor is enabled. */ virtual bool remoteMonitor() const; /** Enables/disables remote monitor. */ virtual void enableRemoteMonitor(bool enable); /** Returns the monitor slot match. */ virtual AnytoneDMRSettingsExtension::SlotMatch monitorSlotMatch() const; /** Sets the monitor slot match. */ virtual void setMonitorSlotMatch(AnytoneDMRSettingsExtension::SlotMatch match); /** Returns @c true if the monitor matches color code. */ virtual bool monitorColorCodeMatch() const; /** Enables/disables monitor color code match. */ virtual void enableMonitorColorCodeMatch(bool enable); /** Returns @c true if the monitor matches ID. */ virtual bool monitorIDMatch() const; /** Enables/disables monitor ID match. */ virtual void enableMonitorIDMatch(bool enable); /** Returns @c true if the monitor holds the time slot. */ virtual bool monitorTimeSlotHold() const; /** Enables/disables monitor time slot hold. */ virtual void enableMonitorTimeSlotHold(bool enable); /** Returns the "man down" delay in seconds. */ virtual Interval manDownDelay() const; /** Sets the "man down" delay in seconds. */ virtual void setManDownDelay(Interval sec); /** Returns the analog call hold in seconds. */ virtual unsigned fmCallHold() const; /** Sets the analog call hold in seconds. */ virtual void setFMCallHold(unsigned sec); /** Returns @c true if the GPS range reporting is enabled. */ virtual bool gpsMessageEnabled() const; /** Enables/disables GPS range reporting. */ virtual void enableGPSMessage(bool enable); /** Returns @c true if the call channel is maintained. */ virtual bool maintainCallChannel() const; /** Enables/disables maintaining the call channel. */ virtual void enableMaintainCallChannel(bool enable); /** Returns the priority Zone A index. */ virtual unsigned priorityZoneAIndex() const; /** Sets the priority zone A index. */ virtual void setPriorityZoneAIndex(unsigned idx); /** Returns the priority Zone B index. */ virtual unsigned priorityZoneBIndex() const; /** Sets the priority zone B index. */ virtual void setPriorityZoneBIndex(unsigned idx); /** Returns @c true if bluetooth is enabled. */ virtual bool bluetooth() const; /** Enables/disables bluetooth. */ virtual void enableBluetooth(bool enable); /** Returns @c true if the internal mic is additionally active when BT is active. */ virtual bool btAndInternalMic() const; /** Enables/disables the internal mic when BT is active. */ virtual void enableBTAndInternalMic(bool enable); /** Returns @c true if the internal speaker is additionally active when BT is active. */ virtual bool btAndInternalSpeaker() const; /** Enables/disables the internal speaker when BT is active. */ virtual void enableBTAndInternalSpeaker(bool enable); /** Returns @c true if the plug-in record tone is enabled. */ virtual bool pluginRecTone() const; /** Enables/disables the plug-in record tone. */ virtual void enablePluginRecTone(bool enable); /** Returns the GPS ranging interval in seconds. */ virtual Interval gpsUpdatePeriod() const; /** Sets the GPS ranging interval in seconds. */ virtual void setGPSUpdatePeriod(Interval sec); /** Returns the bluetooth microphone gain [1,10]. */ virtual unsigned int btMicGain() const; /** Sets the bluetooth microphone gain [1,10]. */ virtual void setBTMicGain(unsigned int gain); /** Returns the bluetooth speaker gain [1,10]. */ virtual unsigned int btSpeakerGain() const; /** Sets the bluetooth speaker gain [1,10]. */ virtual void setBTSpeakerGain(unsigned int gain); /** Returns @c true if the channel number is displayed. */ virtual bool displayChannelNumber() const; /** Enables/disables display of channel number. */ virtual void enableDisplayChannelNumber(bool enable); bool showCurrentContact() const; void enableShowCurrentContact(bool enable); /** Returns the auto roaming period in minutes. */ virtual Interval autoRoamPeriod() const; /** Sets the auto roaming period in minutes. */ virtual void setAutoRoamPeriod(Interval min); bool keyToneLevelAdjustable() const; unsigned keyToneLevel() const; void setKeyToneLevel(unsigned level); void setKeyToneLevelAdjustable(); AnytoneDisplaySettingsExtension::Color callDisplayColor() const; void setCallDisplayColor(AnytoneDisplaySettingsExtension::Color color); bool gpsUnitsImperial() const; void enableGPSUnitsImperial(bool enable); bool knobLock() const; void enableKnobLock(bool enable); bool keypadLock() const; void enableKeypadLock(bool enable); bool sidekeysLock() const; void enableSidekeysLock(bool enable); bool keyLockForced() const; void enableKeyLockForced(bool enable); /** Returns the auto-roam delay in seconds. */ virtual Interval autoRoamDelay() const; /** Sets the auto-roam delay in seconds. */ virtual void setAutoRoamDelay(Interval sec); /** Returns the standby text color. */ virtual AnytoneDisplaySettingsExtension::Color standbyTextColor() const; /** Sets the standby text color. */ virtual void setStandbyTextColor(AnytoneDisplaySettingsExtension::Color color); /** Returns the standby background image. */ virtual BackgroundImage standbyBackgroundImage() const; /** Sets the standby background image. */ virtual void setStandbyBackgroundImage(D878UVCodeplug::GeneralSettingsElement::BackgroundImage img); bool showLastHeard() const; void enableShowLastHeard(bool enable); /** Returns the SMS format. */ virtual AnytoneDMRSettingsExtension::SMSFormat smsFormat() const; /** Sets the SMS format. */ virtual void setSMSFormat(AnytoneDMRSettingsExtension::SMSFormat fmt); /** Returns the minimum frequency in Hz for the auto-repeater range in VHF band. */ virtual Frequency autoRepeaterMinFrequencyVHF() const; /** Sets the minimum frequency in Hz for the auto-repeater range in VHF band. */ virtual void setAutoRepeaterMinFrequencyVHF(Frequency Hz); /** Returns the maximum frequency in Hz for the auto-repeater range in VHF band. */ virtual Frequency autoRepeaterMaxFrequencyVHF() const; /** Sets the maximum frequency in Hz for the auto-repeater range in VHF band. */ virtual void setAutoRepeaterMaxFrequencyVHF(Frequency Hz); /** Returns the minimum frequency in Hz for the auto-repeater range in UHF band. */ virtual Frequency autoRepeaterMinFrequencyUHF() const; /** Sets the minimum frequency in Hz for the auto-repeater range in UHF band. */ virtual void setAutoRepeaterMinFrequencyUHF(Frequency Hz); /** Returns the maximum frequency in Hz for the auto-repeater range in UHF band. */ virtual Frequency autoRepeaterMaxFrequencyUHF() const; /** Sets the maximum frequency in Hz for the auto-repeater range in UHF band. */ virtual void setAutoRepeaterMaxFrequencyUHF(Frequency Hz); /** Returns the auto-repeater direction for VFO B. */ virtual AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionB() const; /** Sets the auto-repeater direction for VFO B. */ virtual void setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir); /** If enabled, the FM ID is sent together with selected contact. */ virtual bool fmSendIDAndContact() const; /** Enables/disables sending contact with FM ID. */ virtual void enableFMSendIDAndContact(bool enable); bool defaultChannel() const; void enableDefaultChannel(bool enable); unsigned defaultZoneIndexA() const; void setDefaultZoneIndexA(unsigned idx); unsigned defaultZoneIndexB() const; void setDefaultZoneIndexB(unsigned idx); bool defaultChannelAIsVFO() const; unsigned defaultChannelAIndex() const; void setDefaultChannelAIndex(unsigned idx); void setDefaultChannelAToVFO(); bool defaultChannelBIsVFO() const; unsigned defaultChannelBIndex() const; void setDefaultChannelBIndex(unsigned idx); void setDefaultChannelBToVFO(); /** Returns the default roaming zone index. */ virtual unsigned defaultRoamingZoneIndex() const; /** Sets the default roaming zone index. */ virtual void setDefaultRoamingZoneIndex(unsigned idx); /** Returns @c true if repeater range check is enabled. */ virtual bool repeaterRangeCheck() const; /** Enables/disables repeater range check. */ virtual void enableRepeaterRangeCheck(bool enable); /** Returns the repeater range check period in seconds. */ virtual Interval repeaterRangeCheckInterval() const; /** Sets the repeater range check interval in seconds. */ virtual void setRepeaterRangeCheckInterval(Interval sec); /** Returns the number of repeater range checks. */ virtual unsigned repeaterRangeCheckCount() const; /** Sets the number of repeater range checks. */ virtual void setRepeaterRangeCheckCount(unsigned n); /** Returns the roaming start condition. */ virtual AnytoneRoamingSettingsExtension::RoamStart roamingStartCondition() const; /** Sets the roaming start condition. */ virtual void setRoamingStartCondition(AnytoneRoamingSettingsExtension::RoamStart cond); /** Returns the backlight duration during TX in seconds. */ virtual Interval txBacklightDuration() const; /** Sets the backlight duration during TX in seconds. */ virtual void setTXBacklightDuration(Interval sec); /** Returns @c true if the "separate display" is enabled. */ virtual bool separateDisplay() const; /** Enables/disables "separate display. */ virtual void enableSeparateDisplay(bool enable); bool keepLastCaller() const; void enableKeepLastCaller(bool enable); /** Returns the channel name color. */ virtual AnytoneDisplaySettingsExtension::Color channelNameColor() const; /** Sets the channel name color. */ virtual void setChannelNameColor(AnytoneDisplaySettingsExtension::Color color); /** Returns @c true if repeater check notification is enabled. */ virtual bool repeaterCheckNotification() const; /** Enables/disables repeater check notification. */ virtual void enableRepeaterCheckNotification(bool enable); /** Returns the backlight duration during RX in seconds. */ Interval rxBacklightDuration() const; /** Sets the backlight duration during RX in seconds. */ void setRXBacklightDuration(Interval sec); /** Returns @c true if roaming is enabled. */ virtual bool roaming() const; /** Enables/disables repeater check notification. */ virtual void enableRoaming(bool enable); /** Returns the mute delay in minutes. */ virtual Interval muteDelay() const; /** Sets the mute delay in minutes. */ virtual void setMuteDelay(Interval min); /** Returns the number of repeater check notifications. */ virtual unsigned repeaterCheckNumNotifications() const; /** Sets the number of repeater check notifications. */ virtual void setRepeaterCheckNumNotifications(unsigned num); /** Returns @c true if boot GPS check is enabled. */ virtual bool bootGPSCheck() const; /** Enables/disables boot GPS check. */ virtual void enableBootGPSCheck(bool enable); /** Returns @c true if boot reset is enabled. */ virtual bool bootReset() const; /** Enables/disables boot reset. */ virtual void enableBootReset(bool enable); /** Returns @c true, if the bluetooth hold time is enabled. */ virtual bool btHoldTimeEnabled() const; /** Returns @c true, if the bluetooth hold time is infinite. */ virtual bool btHoldTimeInfinite() const; /** Returns the bluetooth hold time. */ virtual Interval btHoldTime() const; /** Sets the bluethooth hold time (1-120s). */ virtual void setBTHoldTime(Interval interval); /** Sets the bluethooth hold time to infinite. */ virtual void setBTHoldTimeInfinite(); /** Sets the bluethooth hold time to infinite. */ virtual void disableBTHoldTime(); /** Returns the bluetooth RX delay in ms. */ virtual Interval btRXDelay() const; /** Sets the bluetooth RX delay in ms. */ virtual void setBTRXDelay(Interval delay); bool fromConfig(const Flags &flags, Context &ctx); bool updateConfig(Context &ctx); bool linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err); protected: /** Some internal used offsets within the element. */ struct Offset: public D868UVCodeplug::GeneralSettingsElement::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int transmitTimeout() { return 0x0004; } static constexpr unsigned int language() { return 0x0005; } static constexpr unsigned int vfoFrequencyStep() { return 0x0008; } static constexpr unsigned int steType() { return 0x0017; } static constexpr unsigned int steFrequency() { return 0x0018; } static constexpr unsigned int groupCallHangTime() { return 0x0019; } static constexpr unsigned int privateCallHangTime() { return 0x001a; } static constexpr unsigned int preWaveDelay() { return 0x001b; } static constexpr unsigned int wakeHeadPeriod() { return 0x001c; } static constexpr unsigned int wfmChannelIndex() { return 0x001d; } static constexpr unsigned int wfmVFOEnabled() { return 0x001e; } static constexpr unsigned int dtmfToneDuration() { return 0x0023; } static constexpr unsigned int manDown() { return 0x0024; } static constexpr unsigned int wfmMonitor() { return 0x002b; } static constexpr unsigned int tbstFrequency() { return 0x002e; } static constexpr unsigned int proMode() { return 0x0034; } static constexpr unsigned int filterOwnID() { return 0x0038; } static constexpr unsigned int remoteStunKill() { return 0x003c; } static constexpr unsigned int remoteMonitor() { return 0x003e; } static constexpr unsigned int monSlotMatch() { return 0x0049; } static constexpr unsigned int monColorCodeMatch() { return 0x004a; } static constexpr unsigned int monIDMatch() { return 0x004b; } static constexpr unsigned int monTimeSlotHold() { return 0x004c; } static constexpr unsigned int manDownDelay() { return 0x004f; } static constexpr unsigned int fmCallHold() { return 0x0050; } static constexpr unsigned int enableGPSMessage() { return 0x0053; } static constexpr unsigned int maintainCallChannel() { return 0x006e; } static constexpr unsigned int priorityZoneA() { return 0x006f; } static constexpr unsigned int priorityZoneB() { return 0x0070; } static constexpr unsigned int bluetooth() { return 0x00b1; } static constexpr unsigned int btAndInternalMic() { return 0x00b2; } static constexpr unsigned int btAndInternalSpeaker(){ return 0x00b3; } static constexpr unsigned int pluginRecTone() { return 0x00b4; } static constexpr unsigned int gpsRangingInterval() { return 0x00b5; } static constexpr unsigned int btMicGain() { return 0x00b6; } static constexpr unsigned int btSpeakerGain() { return 0x00b7; } static constexpr unsigned int showChannelNumber() { return 0x00b8; } static constexpr unsigned int showCurrentContact() { return 0x00b9; } static constexpr unsigned int autoRoamPeriod() { return 0x00ba; } static constexpr unsigned int keyToneLevel() { return 0x00bb; } static constexpr unsigned int callColor() { return 0x00bc; } static constexpr unsigned int gpsUnits() { return 0x00bd; } static constexpr unsigned int knobLock() { return 0x00be; } static constexpr unsigned int keypadLock() { return 0x00be; } static constexpr unsigned int sideKeyLock() { return 0x00be; } static constexpr unsigned int forceKeyLock() { return 0x00be; } static constexpr unsigned int autoRoamDelay() { return 0x00bf; } static constexpr unsigned int standbyTextColor() { return 0x00c0; } static constexpr unsigned int standbyBackground() { return 0x00c1; } static constexpr unsigned int showLastHeard() { return 0x00c2; } static constexpr unsigned int smsFormat() { return 0x00c3; } static constexpr unsigned int autoRepMinVHF() { return 0x00c4; } static constexpr unsigned int autoRepMaxVHF() { return 0x00c8; } static constexpr unsigned int autoRepMinUHF() { return 0x00cc; } static constexpr unsigned int autoRepMaxUHF() { return 0x00d0; } static constexpr unsigned int autoRepeaterDirB() { return 0x00d4; } static constexpr unsigned int fmSendIDAndContact() { return 0x00d5; } static constexpr unsigned int defaultChannels() { return 0x00d7; } static constexpr unsigned int defaultZoneA() { return 0x00d8; } static constexpr unsigned int defaultZoneB() { return 0x00d9; } static constexpr unsigned int defaultChannelA() { return 0x00da; } static constexpr unsigned int defaultChannelB() { return 0x00db; } static constexpr unsigned int defaultRoamingZone() { return 0x00dc; } static constexpr unsigned int repRangeCheck() { return 0x00dd; } static constexpr unsigned int rangeCheckInterval() { return 0x00de; } static constexpr unsigned int rangeCheckCount() { return 0x00df; } static constexpr unsigned int roamStartCondition() { return 0x00e0; } static constexpr unsigned int txBacklightDuration() { return 0x00e1; } static constexpr unsigned int displaySeparator() { return 0x00e2; } static constexpr unsigned int keepLastCaller() { return 0x00e3; } static constexpr unsigned int channelNameColor() { return 0x00e4; } static constexpr unsigned int repCheckNotify() { return 0x00e5; } static constexpr unsigned int rxBacklightDuration() { return 0x00e6; } static constexpr unsigned int roaming() { return 0x00e7; } static constexpr unsigned int muteDelay() { return 0x00e9; } static constexpr unsigned int repCheckNumNotify() { return 0x00ea; } static constexpr unsigned int bootGPSCheck() { return 0x00eb; } static constexpr unsigned int bootReset() { return 0x00ec; } static constexpr unsigned int btHoldTime() { return 0x00ed; } static constexpr unsigned int btRXDelay() { return 0x00ee; } /// @endcond }; }; /** General settings extension element for the D878UV. * * Memory representation of the encoded settings element (size 0x200 bytes): * @verbinclude d878uv_generalsettingsextension.txt */ class ExtendedSettingsElement: public AnytoneCodeplug::ExtendedSettingsElement { protected: /** Hidden Constructor. */ ExtendedSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ExtendedSettingsElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x00000200; } /** Resets the settings. */ void clear(); bool sendTalkerAlias() const; void enableSendTalkerAlias(bool enable); AnytoneDMRSettingsExtension::TalkerAliasSource talkerAliasSource() const; void setTalkerAliasSource(AnytoneDMRSettingsExtension::TalkerAliasSource mode); AnytoneDMRSettingsExtension::TalkerAliasEncoding talkerAliasEncoding() const; void setTalkerAliasEncoding(AnytoneDMRSettingsExtension::TalkerAliasEncoding encoding); /** Returns @c true if the BT PTT latch is enabled. */ virtual bool bluetoothPTTLatch() const; /** Enables/disables bluetooth PTT latch. */ virtual void enableBluetoothPTTLatch(bool enable); /** Returns @c true if the bluetooth PTT sleep delay is disabled (infinite). */ virtual bool infiniteBluetoothPTTSleepDelay() const; /** Returns the bluetooth PTT sleep delay in minutes, 0=off. */ virtual Interval bluetoothPTTSleepDelay() const; /** Sets the bluetooth PTT sleep delay in minutes. */ virtual void setBluetoothPTTSleepDelay(Interval delay); /** Sets the bluetooth PTT sleep delay to infinite/disabled. */ virtual void setInfiniteBluetoothPTTSleepDelay(); /** Returns @c true if the auto repeater UHF 2 offset index is set. */ virtual bool hasAutoRepeaterUHF2OffsetIndex() const; /** Returns the index of the UHF 2 offset frequency. */ virtual unsigned autoRepeaterUHF2OffsetIndex() const; /** Sets the index of the UHF 2 offset frequency. */ virtual void setAutoRepeaterUHF2OffsetIndex(unsigned idx); /** Clears the auto repeater UHF 2 offset frequency index. */ virtual void clearAutoRepeaterUHF2OffsetIndex(); /** Returns @c true if the auto repeater VHF 2 offset index is set. */ virtual bool hasAutoRepeaterVHF2OffsetIndex() const; /** Returns the index of the VHF 2 offset frequency. */ virtual unsigned autoRepeaterVHF2OffsetIndex() const; /** Sets the index of the VHF 2 offset frequency. */ virtual void setAutoRepeaterVHF2OffsetIndex(unsigned idx); /** Clears the auto repeater VHF 2 offset frequency index. */ virtual void clearAutoRepeaterVHF2OffsetIndex(); /** Returns the minimum frequency in Hz for the auto-repeater VHF 2 band. */ virtual Frequency autoRepeaterVHF2MinFrequency() const; /** Sets the minimum frequency in Hz for the auto-repeater VHF 2 band. */ virtual void setAutoRepeaterVHF2MinFrequency(Frequency hz); /** Returns the maximum frequency in Hz for the auto-repeater VHF 2 band. */ virtual Frequency autoRepeaterVHF2MaxFrequency() const; /** Sets the maximum frequency in Hz for the auto-repeater VHF 2 band. */ virtual void setAutoRepeaterVHF2MaxFrequency(Frequency hz); /** Returns the minimum frequency in Hz for the auto-repeater UHF 2 band. */ virtual Frequency autoRepeaterUHF2MinFrequency() const; /** Sets the minimum frequency in Hz for the auto-repeater UHF 2 band. */ virtual void setAutoRepeaterUHF2MinFrequency(Frequency hz); /** Returns the maximum frequency in Hz for the auto-repeater UHF 2 band. */ virtual Frequency autoRepeaterUHF2MaxFrequency() const; /** Sets the maximum frequency in Hz for the auto-repeater UHF 2 band. */ virtual void setAutoRepeaterUHF2MaxFrequency(Frequency hz); /** Returns the GPS mode. */ virtual AnytoneGPSSettingsExtension::GPSMode gpsMode() const; /** Sets the GPS mode. */ virtual void setGPSMode(AnytoneGPSSettingsExtension::GPSMode mode); /** Returns the STE (squelch tail elimination) duration. */ virtual Interval steDuration() const; /** Sets the STE (squelch tail elimination) duration. */ virtual void setSTEDuration(Interval dur); /** Returns @c true if the manual dialed group call hang time is infinite. */ virtual bool infiniteManDialGroupCallHangTime() const; /** Returns the manual dial group call hang time. */ virtual Interval manDialGroupCallHangTime() const; /** Sets the manual dial group call hang time. */ virtual void setManDialGroupCallHangTime(Interval dur); /** Sets the manual dial group call hang time to infinite. */ virtual void setManDialGroupCallHangTimeInfinite(); /** Returns @c true if the manual dialed private call hang time is infinite. */ virtual bool infiniteManDialPrivateCallHangTime() const; /** Returns the manual dial private call hang time. */ virtual Interval manDialPrivateCallHangTime() const; /** Sets the manual dial private call hang time. */ virtual void setManDialPrivateCallHangTime(Interval dur); /** Sets the manual dial private call hang time to infinite. */ virtual void setManDialPrivateCallHangTimeInfinite(); AnytoneDisplaySettingsExtension::Color channelBNameColor() const; void setChannelBNameColor(AnytoneDisplaySettingsExtension::Color color); /** Returns the encryption mode. */ virtual AnytoneDMRSettingsExtension::EncryptionType encryption() const; /** Sets the encryption mode. */ virtual void setEncryption(AnytoneDMRSettingsExtension::EncryptionType mode); /** Returns @c true if the transmit timeout notification is enabled. */ virtual bool totNotification() const; /** Enables/disables transmit timeout notification. */ virtual void enableTOTNotification(bool enable); /** Returns @c true if the ATPC (Adaptiv Transmission Power Control) is enabled. */ virtual bool atpc() const; /** Enables/disables the ATPC (Adaptiv Transmission Power Control). */ virtual void enableATPC(bool enable); AnytoneDisplaySettingsExtension::Color zoneANameColor() const; void setZoneANameColor(AnytoneDisplaySettingsExtension::Color color); AnytoneDisplaySettingsExtension::Color zoneBNameColor() const; void setZoneBNameColor(AnytoneDisplaySettingsExtension::Color color); /** Returns @c true if the auto-shutdown timer is reset on a call. */ virtual bool resetAutoShutdownOnCall() const; /** Enables/disables reset on call of the auto-shutdown timer. */ virtual void enableResetAutoShutdownOnCall(bool enable); /** Returns @c true if the color code is shown. */ virtual bool showColorCode() const; /** Enables/disables display of color code. */ virtual void enableShowColorCode(bool enable); /** Returns @c true if the time slot is shown. */ virtual bool showTimeSlot() const; /** Enables/disables display of time slot. */ virtual void enableShowTimeSlot(bool enable); /** Returns @c true if the channel type is shown. */ virtual bool showChannelType() const; /** Enables/disables display of channel type. */ virtual void enableShowChannelType(bool enable); /** Returns @c true if the FM idle channel tone is enabled. */ virtual bool fmIdleTone() const; /** Enables/disables FM idle channel tone. */ virtual void enableFMIdleTone(bool enable); /** Returns the date format. */ virtual AnytoneDisplaySettingsExtension::DateFormat dateFormat() const; /** Sets the date format. */ virtual void setDateFormat(AnytoneDisplaySettingsExtension::DateFormat format); /** Returns the FM Mic gain [1,10]. */ virtual unsigned int fmMicGain() const; /** Sets the analog mic gain [1,10]. */ virtual void setFMMicGain(unsigned int gain); /** Returns @c true if the GPS roaming is enabled. */ virtual bool gpsRoaming() const; /** Enables/disables GPS roaming. */ virtual void enableGPSRoaming(bool enable); /** Returns the call-end tone melody. */ virtual void callEndToneMelody(Melody &melody) const; /** Sets the call-end tone melody. */ virtual void setCallEndToneMelody(const Melody &melody); /** Returns the all-call tone melody. */ virtual void allCallToneMelody(Melody &melody) const; /** Sets the all-call tone melody. */ virtual void setAllCallToneMelody(const Melody &melody); /** Encodes the settings from the config. */ virtual bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Update config from settings. */ virtual bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack()); /** Link config from settings extension. */ virtual bool linkConfig(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for the settings. */ struct Limit { static constexpr unsigned int maxBluetoothPTTSleepDelay() { return 4; } ///< Maximum delay in minutes. }; protected: /** Internal used offset within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int sendTalkerAlias() { return 0x0000; } static constexpr unsigned int talkerAliasDisplay() { return 0x001e; } static constexpr unsigned int talkerAliasEncoding() { return 0x001f; } static constexpr unsigned int btPTTLatch() { return 0x0020; } static constexpr unsigned int autoRepeaterUHF2OffsetIndex() { return 0x0022; } static constexpr unsigned int autoRepeaterVHF2OffsetIndex() { return 0x0023; } static constexpr unsigned int autoRepeaterVHF2MinFrequency() { return 0x0024; } static constexpr unsigned int autoRepeaterVHF2MaxFrequency() { return 0x0028; } static constexpr unsigned int autoRepeaterUHF2MinFrequency() { return 0x002c; } static constexpr unsigned int autoRepeaterUHF2MaxFrequency() { return 0x0030; } static constexpr unsigned int btPTTSleepDelay() { return 0x0034; } static constexpr unsigned int gpsMode() { return 0x0035; } static constexpr unsigned int steDuration() { return 0x0036; } static constexpr unsigned int manGrpCallHangTime() { return 0x0037; } static constexpr unsigned int manPrivCallHangTime() { return 0x0038; } static constexpr unsigned int channelBNameColor() { return 0x0039; } static constexpr unsigned int encryptionType() { return 0x003a; } static constexpr unsigned int totNotification() { return 0x003b; } static constexpr unsigned int atpc() { return 0x003c; } static constexpr unsigned int zoneANameColor() { return 0x003d; } static constexpr unsigned int zoneBNameColor() { return 0x003e; } static constexpr unsigned int autoShutdownMode() { return 0x003f; } static constexpr unsigned int displayColorCode() { return 0x0040; } // bit 2 static constexpr unsigned int displayTimeSlot() { return 0x0040; } // bit 1 static constexpr unsigned int displayChannelType() { return 0x0040; } // bit 0 static constexpr unsigned int fmIdleTone() { return 0x0041; } static constexpr unsigned int dateFormat() { return 0x0042; } static constexpr unsigned int analogMicGain() { return 0x0043; } static constexpr unsigned int gpsRoaming() { return 0x0044; } static constexpr unsigned int callEndTones() { return 0x0046; } static constexpr unsigned int callEndDurations() { return 0x0050; } static constexpr unsigned int allCallTones() { return 0x005a; } static constexpr unsigned int allCallDurations() { return 0x0064; } /// @endcond }; }; /** Implements some storage to hold the names for the FM APRS frequencies. */ class FMAPRSFrequencyNamesElement: public Element { protected: /** Hidden constructor. */ FMAPRSFrequencyNamesElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit FMAPRSFrequencyNamesElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0080; } void clear(); /** Returns the n-th name. The 0-th name, is the name of the FM APRS system. */ virtual QString name(unsigned int n) const; /** Sets the n-th name. The 0-th name, is the name of the FM APRS system. */ virtual void setName(unsigned int n, const QString &name); public: /** Some limits for the element. */ struct Limit { static constexpr unsigned int nameLength() { return 16; } ///< Maximum name length. }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int betweenNames() { return 0x0010; } /// @endcond }; }; /** Represents the APRS settings within the binary D878UV codeplug. * * Memory layout of APRS settings (size 0x00f0 bytes): * @verbinclude d878uv_aprssetting.txt */ class APRSSettingsElement: public Element { protected: /** Hidden constructor. */ APRSSettingsElement(uint8_t *ptr, unsigned size); /** Possible settings for the FM APRS subtone type. */ enum class SignalingType { Off=0, CTCSS=1, DCS=2 }; public: /** Constructor. */ explicit APRSSettingsElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0100; } /** Resets the settings. */ void clear(); bool isValid() const; /** Returns the TX delay in ms. */ virtual Interval fmTXDelay() const; /** Sets the TX delay in ms. */ virtual void setFMTXDelay(Interval ms); /** Returns the sub tone settings. */ virtual SelectiveCall txTone() const; /** Sets the sub tone settings. */ virtual void setTXTone(const SelectiveCall &code); /** Returns the manual TX interval in seconds. */ virtual Interval manualTXInterval() const; /** Sets the manual TX interval in seconds. */ virtual void setManualTXInterval(Interval sec); /** Returns @c true if the auto transmit is enabled. */ virtual bool autoTX() const; /** Returns the auto TX interval in seconds. */ virtual Interval autoTXInterval() const; /** Sets the auto TX interval in seconds. */ virtual void setAutoTXInterval(Interval sec); /** Disables auto tx. */ virtual void disableAutoTX(); /** Returns @c true if a fixed location is sent. */ virtual bool fixedLocationEnabled() const; /** Returns the fixed location send. */ virtual QGeoCoordinate fixedLocation() const; /** Sets the fixed location to send. */ virtual void setFixedLocation(QGeoCoordinate &loc); /** Disables sending a fixed location. */ virtual void disableFixedLocation(); /** Returns the destination call. */ virtual QString destination() const; /** Returns the destination SSID. */ virtual unsigned destinationSSID() const; /** Sets the destination call & SSID. */ virtual void setDestination(const QString &call, unsigned ssid); /** Returns the source call. */ virtual QString source() const; /** Returns the source SSID. */ virtual unsigned sourceSSID() const; /** Sets the source call & SSID. */ virtual void setSource(const QString &call, unsigned ssid); /** Returns the path string. */ virtual QString path() const; /** Sets the path string. */ virtual void setPath(const QString &path); /** Returns the APRS icon. */ virtual APRSSystem::Icon icon() const; /** Sets the APRS icon. */ virtual void setIcon(APRSSystem::Icon icon); /** Returns the transmit power. */ virtual Channel::Power power() const; /** Sets the transmit power. */ virtual void setPower(Channel::Power power); /** Returns the pre-wave delay in ms. */ virtual Interval fmPreWaveDelay() const; /** Sets the pre-wave delay in ms. */ virtual void setFMPreWaveDelay(Interval ms); /** Returns @c true if the channel points to the current/selected channel. */ virtual bool dmrChannelIsSelected(unsigned n) const; /** Returns the digital channel index for the n-th system. */ virtual unsigned dmrChannelIndex(unsigned n) const; /** Sets the digital channel index for the n-th system. */ virtual void setDMRChannelIndex(unsigned n, unsigned idx); /** Sets the channel to the current/selected channel. */ virtual void setDMRChannelSelected(unsigned n); /** Returns the destination contact for the n-th system. */ virtual unsigned dmrDestination(unsigned n) const; /** Sets the destination contact for the n-th system. */ virtual void setDMRDestination(unsigned n, unsigned idx); /** Returns the call type for the n-th system. */ virtual DMRContact::Type dmrCallType(unsigned n) const; /** Sets the call type for the n-th system. */ virtual void setDMRCallType(unsigned n, DMRContact::Type type); /** Returns @c true if the n-th system overrides the channel time-slot. */ virtual bool dmrTimeSlotOverride(unsigned n); /** Returns the time slot if overridden (only valid if @c timeSlot returns true). */ virtual DMRChannel::TimeSlot dmrTimeSlot(unsigned n) const; /** Overrides the time slot of the n-th selected channel. */ virtual void setDMRTimeSlot(unsigned n, DMRChannel::TimeSlot ts); /** Clears the time-slot override. */ virtual void clearDMRTimeSlotOverride(unsigned n); /** Returns @c true if the roaming is enabled. */ virtual bool dmrRoaming() const; /** Enables/disables roaming. */ virtual void enableDMRRoaming(bool enable); /** Returns the the repeater activation delay in ms. */ virtual Interval dmrPreWaveDelay() const; /** Sets the repeater activation delay in ms. */ virtual void setDMRPreWaveDelay(Interval ms); /** Returns @c true if a received APRS message is shown indefinitely. */ virtual bool infiniteDisplayTime() const; /** Returns the time, a received APRS message is shown. */ virtual Interval displayTime() const; /** Sets the time, a received APRS is shown. */ virtual void setDisplayTime(Interval dur); /** Sets the APRS display time to infinite. */ virtual void setDisplayTimeInifinite(); /** Returns the FM APRS channel width. */ virtual AnytoneFMAPRSSettingsExtension::Bandwidth fmChannelWidth() const; /** Sets the FM APRS channel width. */ virtual void setFMChannelWidth(AnytoneFMAPRSSettingsExtension::Bandwidth width); /** Retruns @c true if the CRC check on received FM APRS messages is disabled. */ virtual bool fmPassAll() const; /** Enables/disables "pass all", that is the CRC check on FM APRS messages is disabled. */ virtual void enableFMPassAll(bool enable); /** Retruns @c true if the n-th of 8 FM APRS frequencies is set. */ virtual bool fmFrequencySet(unsigned int n) const; /** Returns the n-th of 8 FM APRS frequencies. */ virtual Frequency fmFrequency(unsigned int n) const; /** Sets the n-th of 8 FM APRS frequencies. */ virtual void setFMFrequency(unsigned int n, Frequency f); /** Clears the n-th of 8 FM APRS frequencies. */ virtual void clearFMFrequency(unsigned int n); /** Returns @c true if the report position flag is set. */ virtual bool reportPosition() const; /** Enables/disables report position flag. */ virtual void enableReportPosition(bool enable); /** Returns @c true if the report Mic-E flag is set. */ virtual bool reportMicE() const; /** Enables/disables report Mic-E flag. */ virtual void enableReportMicE(bool enable); /** Returns @c true if the report object flag is set. */ virtual bool reportObject() const; /** Enables/disables report object flag. */ virtual void enableReportObject(bool enable); /** Returns @c true if the report item flag is set. */ virtual bool reportItem() const; /** Enables/disables report item flag. */ virtual void enableReportItem(bool enable); /** Returns @c true if the report message flag is set. */ virtual bool reportMessage() const; /** Enables/disables report message flag. */ virtual void enableReportMessage(bool enable); /** Returns @c true if the report weather flag is set. */ virtual bool reportWeather() const; /** Enables/disables report weather flag. */ virtual void enableReportWeather(bool enable); /** Returns @c true if the report NMEA flag is set. */ virtual bool reportNMEA() const; /** Enables/disables report NMEA flag. */ virtual void enableReportNMEA(bool enable); /** Returns @c true if the report status flag is set. */ virtual bool reportStatus() const; /** Enables/disables report status flag. */ virtual void enableReportStatus(bool enable); /** Returns @c true if the report other flag is set. */ virtual bool reportOther() const; /** Enables/disables report other flag. */ virtual void enableReportOther(bool enable); /** Configures this APRS system from the given generic config. */ virtual bool fromFMAPRSSystem(const APRSSystem *sys, Context &ctx, FMAPRSFrequencyNamesElement &names, const ErrorStack &err=ErrorStack()); /** Constructs a generic APRS system configuration from this APRS system. */ virtual APRSSystem *toFMAPRSSystem( Context &ctx, const FMAPRSFrequencyNamesElement &names, const ErrorStack &err=ErrorStack()); /** Links the transmit channel within the generic APRS system based on the transmit frequency * defined within this APRS system. */ virtual bool linkFMAPRSSystem(APRSSystem *sys, Context &ctx); /** Constructs all GPS system from the generic configuration. */ virtual bool fromDMRAPRSSystems(Context &ctx); /** Encodes the given GPS system. */ virtual bool fromDMRAPRSSystemObj(unsigned int idx, GPSSystem *sys, Context &ctx); /** Constructs a generic GPS system from the idx-th encoded GPS system. */ virtual GPSSystem *toDMRAPRSSystemObj(int idx) const; /** Links the specified generic GPS system. */ virtual bool linkDMRAPRSSystem(int idx, GPSSystem *sys, Context &ctx) const; public: /** Some static limits for this element. */ struct Limit { /// Maximum length of call signs. static constexpr unsigned int callLength() { return 0x0006; } /// Maximum length of the repeater path string. static constexpr unsigned int pathLength() { return 0x0020; } /// Maximum number of DMR APRS systems. static constexpr unsigned int dmrSystems() { return 0x0008; } /// Maximum number of FM APRS frequencies. static constexpr unsigned int fmFrequencies() { return 0x0008; } }; protected: /** Internal used offsets within the codeplug element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int fmTXDelay() { return 0x0005; } static constexpr unsigned int fmSigType() { return 0x0006; } static constexpr unsigned int fmCTCSS() { return 0x0007; } static constexpr unsigned int fmDCS() { return 0x0008; } static constexpr unsigned int manualTXInterval() { return 0x000a; } static constexpr unsigned int autoTXInterval() { return 0x000b; } static constexpr unsigned int fmTXMonitor() { return 0x000c; } static constexpr unsigned int fixedLocation() { return 0x000d; } static constexpr unsigned int fixedLatDeg() { return 0x000e; } static constexpr unsigned int fixedLatMin() { return 0x000f; } static constexpr unsigned int fixedLatSec() { return 0x0010; } static constexpr unsigned int fixedLatSouth() { return 0x0011; } static constexpr unsigned int fixedLonDeg() { return 0x0012; } static constexpr unsigned int fixedLonMin() { return 0x0013; } static constexpr unsigned int fixedLonSec() { return 0x0014; } static constexpr unsigned int fixedLonWest() { return 0x0015; } static constexpr unsigned int destinationCall() { return 0x0016; } static constexpr unsigned int destinationSSID() { return 0x001c; } static constexpr unsigned int sourceCall() { return 0x001d; } static constexpr unsigned int sourceSSID() { return 0x0023; } static constexpr unsigned int path() { return 0x0024; } static constexpr unsigned int symbolTable() { return 0x0039; } static constexpr unsigned int symbol() { return 0x003a; } static constexpr unsigned int fmPower() { return 0x003b; } static constexpr unsigned int fmPrewaveDelay() { return 0x003c; } static constexpr unsigned int dmrChannelIndices() { return 0x0040; } static constexpr unsigned int betweenDMRChannelIndices() { return 0x0002; } static constexpr unsigned int dmrDestinations() { return 0x0050; } static constexpr unsigned int betweenDMRDestinations() { return 0x0004; } static constexpr unsigned int dmrCallTypes() { return 0x0070; } static constexpr unsigned int betweenDMRCallTypes() { return 0x0001; } static constexpr unsigned int roamingSupport() { return 0x0078; } static constexpr unsigned int dmrTimeSlots() { return 0x0079; } static constexpr unsigned int betweenDMRTimeSlots() { return 0x0001; } static constexpr unsigned int dmrPrewaveDelay() { return 0x0081; } static constexpr unsigned int displayInterval() { return 0x0082; } static constexpr unsigned int fixedHeight() { return 0x00a6; } static constexpr unsigned int reportPosition() { return 0x00a8; } static constexpr unsigned int reportMicE() { return 0x00a8; } static constexpr unsigned int reportObject() { return 0x00a8; } static constexpr unsigned int reportItem() { return 0x00a8; } static constexpr unsigned int reportMessage() { return 0x00a8; } static constexpr unsigned int reportWeather() { return 0x00a8; } static constexpr unsigned int reportNMEA() { return 0x00a8; } static constexpr unsigned int reportStatus() { return 0x00a8; } static constexpr unsigned int reportOther() { return 0x00a9; } static constexpr unsigned int fmWidth() { return 0x00aa; } static constexpr unsigned int passAll() { return 0x00ab; } static constexpr unsigned int fmFrequencies() { return 0x00ac; } static constexpr unsigned int betweenFMFrequencies() { return 0x0004; } /// @endcond }; }; /** Represents an (analog/FM) APRS message. */ class AnalogAPRSMessageElement: public Element { protected: /** Hidden constructor. */ AnalogAPRSMessageElement(uint8_t *ptr, size_t size); public: /** Constructor. */ AnalogAPRSMessageElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0040; } void clear(); /** Returns the message. */ virtual QString message() const; /** Sets the message. */ virtual void setMessage(const QString &msg); public: /** Some limits. */ struct Limit { static constexpr unsigned int length() { return 60; } ///< Maximum message length. }; }; /** Represents an analog APRS RX entry. * * Memory layout of analog APRS RX entry (size 0x0008 bytes): * @verbinclude d878uv_aprsrxentry.txt */ class AnalogAPRSRXEntryElement: public Element { protected: /** Hidden constructor. */ AnalogAPRSRXEntryElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ AnalogAPRSRXEntryElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0008; } /** Resets the entry. */ void clear(); /** Returns @c true if the APRS RX entry is valid. */ bool isValid() const; /** Returns the call sign. */ virtual QString call() const; /** Returns the SSID. */ virtual unsigned ssid() const; /** Sets the call, SSID and enables the entry. */ virtual void setCall(const QString &call, unsigned ssid); }; /** Implements the binary representation of a roaming channel within the codeplug. * * Memory layout of roaming channel (size 0x0020 bytes): * @verbinclude d878uv_roamingchannel.txt */ class RoamingChannelElement: public Element { protected: /** Hidden constructor. */ RoamingChannelElement(uint8_t *ptr, unsigned size); protected: /** Special values for the color code. */ enum ColorCodeValue { Disabled = 16 }; /** Encoded values for the time slot. */ enum TimeSlotValue { TS1 = 0, TS2 = 1 }; public: /** Constructor. */ RoamingChannelElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0020; } /** Resets the roaming channel. */ void clear(); /** Returns the RX frequency in Hz. */ virtual unsigned rxFrequency() const; /** Sets the RX frequency in Hz. */ virtual void setRXFrequency(unsigned hz); /** Returns the TX frequency in Hz. */ virtual unsigned txFrequency() const; /** Sets the TX frequency in Hz. */ virtual void setTXFrequency(unsigned hz); /** Returns @c true if the color code is set. */ virtual bool hasColorCode() const; /** Returns the color code. */ virtual unsigned colorCode() const; /** Sets the color code. */ virtual void setColorCode(unsigned cc); /** Disables the color code for the roaming channel. */ virtual void disableColorCode(); /** Returns the time slot. */ virtual DMRChannel::TimeSlot timeSlot() const; /** Sets the time slot. */ virtual void setTimeSlot(DMRChannel::TimeSlot ts); /** Returns the name of the channel. */ virtual QString name() const; /** Sets the name of the channel. */ virtual void setName(const QString &name); /** Constructs a roaming channel from the given digital channel. */ virtual bool fromChannel(const RoamingChannel *ch); /** Constructs a @c RoamingChannel instance for this roaming channel. */ virtual RoamingChannel *toChannel(Context &ctx); public: /** Some limits. */ struct Limit { static constexpr unsigned int nameLength() { return 16; } ///< Maximum name length. }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int rxFrequency() { return 0x0000; } static constexpr unsigned int txFrequency() { return 0x0004; } static constexpr unsigned int colorCode() { return 0x0008; } static constexpr unsigned int timeSlot() { return 0x0009; } static constexpr unsigned int name() { return 0x000a; } /// @endcond }; }; /** Represents the bitmap, indicating which roaming channel is valid. */ class RoamingChannelBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ RoamingChannelBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ RoamingChannelBitmapElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0020; } }; /** Represents a roaming zone within the binary codeplug. * * Memory layout of roaming zone (0x80byte): * @verbinclude d878uv_roamingzone.txt */ class RoamingZoneElement: public Element { protected: /** Hidden constructor. */ RoamingZoneElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ RoamingZoneElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0080; } /** Clears the roaming zone. */ void clear(); /** Returns @c true if the n-th member is set. */ virtual bool hasMember(unsigned n) const; /** Returns the n-th member index. */ virtual unsigned member(unsigned n) const; /** Sets the n-th member index. */ virtual void setMember(unsigned n, unsigned idx); /** Clears the n-th member. */ virtual void clearMember(unsigned n); /** Returns the name of the zone. */ virtual QString name() const; /** Sets the name of the zone. */ virtual void setName(const QString &name); /** Assembles a binary representation of the given RoamingZone instance.*/ virtual bool fromRoamingZone(RoamingZone *zone, Context& ctx, const ErrorStack &err=ErrorStack()); /** Constructs a @c RoamingZone instance from this configuration. */ virtual RoamingZone *toRoamingZone(Context& ctx, const ErrorStack &err=ErrorStack()) const; /** Links the given RoamingZone. */ virtual bool linkRoamingZone(RoamingZone *zone, Context& ctx, const ErrorStack& err=ErrorStack()); public: /** Some limits. */ struct Limit { static constexpr unsigned int nameLength() { return 16; } ///< Maximum name length. static constexpr unsigned int numMembers() { return 64; } ///< Maximum number of roaming channel in zone. }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int members() { return 0x0000; } static constexpr unsigned int betweenMembers() { return 0x0001; } static constexpr unsigned int name() { return 0x0040; } /// @endcond }; }; /** Represents the bitmap, indicating which roaming zone is valid. */ class RoamingZoneBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ RoamingZoneBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ RoamingZoneBitmapElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0010; } }; /** Represents an AES encryption key. * * Binary representation of the key (size 0x0040 bytes): * @verbinclude d878uv_aeskey.txt */ class AESEncryptionKeyElement: public Element { protected: /** Hidden constructor. */ AESEncryptionKeyElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ AESEncryptionKeyElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0040; } /** Resets the key. */ void clear(); /** Returns @c true if the key is set. */ bool isValid() const; /** Returns the key index. */ virtual unsigned index() const; /** Sets the key index. */ virtual void setIndex(unsigned idx); /** Returns the actual key. */ virtual QByteArray key() const; /** Sets the key. */ virtual void setKey(const QByteArray &key); }; /** Encodes the bitmap, indicating which zone is hidden. */ class HiddenZoneBitmapElement: public BitmapElement { protected: /** Hidden constructor. */ HiddenZoneBitmapElement(uint8_t *ptr, size_t size); public: /** Constructor. */ HiddenZoneBitmapElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0020; } }; /** Encodes some information about the radio and firmware. * * Binary encoding of the info, size 0x0100 bytes: * @verbinclude d878uv_radioinfo.txt */ class RadioInfoElement: public Element { public: /** Possible frequency ranges for the AT-D878UV. */ enum FrequencyRange { RX_400_480_136_174_TX_400_480_136_174 = 0, RX_400_480_136_174_TX_400_480_136_174_STEP_12_5kHz = 1, RX_430_440_136_174_TX_430_440_136_174 = 2, RX_400_480_136_174_TX_430_440_144_146 = 3, RX_440_480_136_174_TX_440_480_136_174 = 4, RX_440_480_144_146_TX_440_480_144_146 = 5, RX_446_447_136_174_TX_446_447_136_174 = 6, RX_400_480_136_174_TX_420_450_136_174 = 7, RX_400_470_136_174_TX_400_470_136_174 = 8, RX_430_432_144_146_TX_430_432_144_146 = 9, RX_400_480_136_174_TX_430_450_144_148 = 10, RX_400_520_136_174_TX_400_520_136_174 = 11, RX_400_490_136_174_TX_400_490_136_174 = 12, RX_400_480_136_174_TX_403_470_136_174 = 13, RX_400_520_220_225_136_174_TX_400_520_220_225_136_174 = 14, RX_420_520_144_148_TX_420_520_144_148 = 15, RX_430_440_144_147_TX_430_440_144_147 = 16, RX_430_440_136_174_TX_136_174 = 17 }; protected: /** Hidden constructor. */ RadioInfoElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit RadioInfoElement(uint8_t *ptr); /** Resets the info. */ void clear(); /** Returns @c true if full test is enabled. * @warning Do not enable, may brick device! */ virtual bool fullTest() const; /** Returns the frequency range. */ virtual FrequencyRange frequencyRange() const; /** Sets the frequency range. */ virtual void setFrequencyRange(FrequencyRange range); /** Returns @c true if "international" is enabled. */ virtual bool international() const; /** Enables/disables "international". */ virtual void enableInternational(bool enable); /** Returns @c true if band select is enabled. */ virtual bool bandSelect() const; /** Enables/disables band select. */ virtual void enableBandSelect(bool enable); /** Returns the band-select password. */ virtual QString bandSelectPassword() const; /** Sets the band-select password. */ virtual void setBandSelectPassword(const QString &passwd); /** Returns the radio type. */ virtual QString radioType() const; /** Returns the program password. */ virtual QString programPassword() const; /** Sets the program password. */ virtual void setProgramPassword(const QString &passwd); /** Returns the area code. */ virtual QString areaCode() const; /** Returns the serial number. */ virtual QString serialNumber() const; /** Returns the production date. */ virtual QString productionDate() const; /** Returns the manufacturer code. */ virtual QString manufacturerCode() const; /** Returns the maintained date. */ virtual QString maintainedDate() const; /** Returns the dealer code. */ virtual QString dealerCode() const; /** Returns the stock date. */ virtual QString stockDate() const; /** Returns the sell date. */ virtual QString sellDate() const; /** Returns the seller. */ virtual QString seller() const; /** Returns the maintainer note. */ virtual QString maintainerNote() const; }; protected: /** Hidden constructor. */ explicit D878UVCodeplug(const QString &label, QObject *parent = nullptr); public: /** Empty constructor. */ explicit D878UVCodeplug(QObject *parent = nullptr); protected: bool allocateBitmaps(); void setBitmaps(Context &ctx); void allocateForDecoding(); void allocateUpdated(); void allocateForEncoding(); bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack()); bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); void allocateChannels(); bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()); virtual void allocateZones(); virtual bool encodeZone(int i, Zone *zone, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); virtual bool decodeZone(int i, Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack()); void allocateGeneralSettings(); bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()); void allocateGPSSystems(); bool encodeGPSSystems(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createGPSSystems(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkGPSSystems(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocates memory to store all roaming channels and zones. */ virtual void allocateRoaming(); /** Encodes the roaming channels and zones. */ virtual bool encodeRoaming(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Creates roaming channels and zones from codeplug. */ virtual bool createRoaming(Context &ctx, const ErrorStack &err=ErrorStack()); /** Links roaming channels and zones. */ virtual bool linkRoaming(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit: public D868UVCodeplug::Limit { static constexpr unsigned int analogAPRSRXEntries() { return 32; } ///< Maximum number of analog APRS RX entries. static constexpr unsigned int roamingChannels() { return 250; } ///< Maximum number of roaming channels. static constexpr unsigned int roamingZones() { return 64; } ///< Maximum number of roaming zones. static constexpr unsigned int aesKeys() { return 256; } ///< Maximum number of AES keys. }; protected: /** Internal offsets within the codeplug. */ struct Offset: public D868UVCodeplug::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int toChannelExtension() { return 0x00002000; } static constexpr unsigned int settingsExtension() { return 0x02501400; } static constexpr unsigned int aprsSettings() { return 0x02501000; } static constexpr unsigned int analogAPRSMessage() { return 0x02501200; } static constexpr unsigned int analogAPRSRXEntries() { return 0x02501800; } static constexpr unsigned int fmAPRSFrequencyNames() { return 0x02502000; } static constexpr unsigned int hiddenZoneBitmap() { return 0x024c1360; } static constexpr unsigned int roamingChannelBitmap() { return 0x01042000; } static constexpr unsigned int roamingChannels() { return 0x01040000; } static constexpr unsigned int roamingZoneBitmap() { return 0x01042080; } static constexpr unsigned int roamingZones() { return 0x01043000; } static constexpr unsigned int aesKeys() { return 0x024C4000; } /// @endcond }; }; #endif // D878UVCODEPLUG_HH qdmr-0.12.3/lib/d878uv_filereader.cc000066400000000000000000000003141501654372000170450ustar00rootroot00000000000000#include "d878uv_filereader.hh" D878UVFileReader::D878UVFileReader(Config *config, const uint8_t *data, size_t size, QString &message) :D868UVFileReader(config, data, size, message) { // pass... } qdmr-0.12.3/lib/d878uv_filereader.hh000066400000000000000000000005601501654372000170620ustar00rootroot00000000000000#ifndef D878UVFILEREADER_HH #define D878UVFILEREADER_HH #include "d868uv_filereader.hh" /** Implements a file read for the AnyTone D878UV manufacturer CPS file. */ class D878UVFileReader: public D868UVFileReader { public: /** Constructor. */ D878UVFileReader(Config *config, const uint8_t *data, size_t size, QString &message); }; #endif // D878UVFILEREADER_HH qdmr-0.12.3/lib/d878uv_limits.cc000066400000000000000000000207411501654372000162520ustar00rootroot00000000000000#include "d878uv_limits.hh" #include "channel.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "zone.hh" #include "scanlist.hh" #include "gpssystem.hh" #include "roamingzone.hh" #include "anytone_satelliteconfig.hh" D878UVLimits::D878UVLimits(const std::initializer_list > &rxFreqRanges, const std::initializer_list > &txFreqRanges, const QString &hardwareRevision, QObject *parent) : AnytoneLimits(hardwareRevision, "V100", true, parent) { // Define limits for call-sign DB _hasCallSignDB = true; _callSignDBImplemented = true; _numCallSignDBEntries = 200000; // Define limits for satellite config _hasSatelliteConfig = true; _satelliteConfigImplemented = true; _numSatellites = AnytoneSatelliteConfig::Limit::satellites(); /* Define limits for the general settings. */ add("settings", new RadioLimitItem{ { "introLine1", new RadioLimitString(-1, 14, RadioLimitString::ASCII) }, { "introLine2", new RadioLimitString(-1, 14, RadioLimitString::ASCII) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, -1) } }); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList { { DMRRadioID::staticMetaObject, 1, 250, new RadioLimitObject { {"name", new RadioLimitString(1,8, RadioLimitString::ASCII) }, {"id", new RadioLimitUInt(0, 16777215)} } } }); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum{ (unsigned)DMRContact::PrivateCall, (unsigned)DMRContact::GroupCall, (unsigned)DMRContact::AllCall }}, { "number", new RadioLimitUInt(0, 16777215) } } }, { DTMFContact::staticMetaObject, 0, 128, new RadioLimitObject { { "name", new RadioLimitString(1, 15, RadioLimitString::ASCII) }, { "number", new RadioLimitString(1, 14, RadioLimitString::DTMF) } } } }); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "contacts", new RadioLimitGroupCallRefList(1, 64) } })); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, 4000, new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies(rxFreqRanges, true)}, {"txFrequency", new RadioLimitTransmitFrequencies(txFreqRanges)}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRef(APRSSystem::staticMetaObject)}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1,16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies(rxFreqRanges, true)}, {"txFrequency", new RadioLimitTransmitFrequencies(txFreqRanges)}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false)}, {"aprs", new RadioLimitObjRef(PositioningSystem::staticMetaObject, true)}, {"roaming", new RadioLimitObjRef(RoamingZone::staticMetaObject, true) }, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } )); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone( 250, { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions }) ) ); /* Define limits for scan lists. */ add("scanlists", new RadioLimitList( ScanList::staticMetaObject, 0, 250, new RadioLimitObject{ { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "primary", new RadioLimitObjRef(Channel::staticMetaObject, false) }, { "secondary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "revert", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "channels", new RadioLimitRefList(0, 31, Channel::staticMetaObject) } })); /* Handle positioning systems. */ add("positioning", new RadioLimitList{ { GPSSystem::staticMetaObject, 0, 8, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "period", new RadioLimitUInt(0, 7650) }, { "contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false) }, { "revert", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } }, { APRSSystem::staticMetaObject, 0, 1, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "period", new RadioLimitUInt(0, 7650) }, { "revert", new RadioLimitObjRef({SelectedChannel::staticMetaObject, FMChannel::staticMetaObject}, false) }, { "icon", new RadioLimitEnum{} }, { "message", new RadioLimitString(0, 60, RadioLimitString::ASCII) } ///@todo extend APRSSystem to expose other settings as properties. }} } ); /* Handle roaming zones. */ add("roaming", new RadioLimitList(RoamingZone::staticMetaObject, 0, 64, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "channels", new RadioLimitRefList(0, 64, DMRChannel::staticMetaObject) } } ) ); } qdmr-0.12.3/lib/d878uv_limits.hh000066400000000000000000000010171501654372000162570ustar00rootroot00000000000000#ifndef D878UVLIMITS_HH #define D878UVLIMITS_HH #include "anytone_limits.hh" /** Implements the limits for the AnyTone AT-D878UV. * @ingroup d878uv */ class D878UVLimits: public AnytoneLimits { Q_OBJECT public: /** Constructor. */ D878UVLimits(const std::initializer_list > &rxFreqRanges, const std::initializer_list > &txFreqRanges, const QString &hardwareRevision, QObject *parent=nullptr); }; #endif // D878UVLIMITS_HH qdmr-0.12.3/lib/dfu_libusb.cc000066400000000000000000000266741501654372000157550ustar00rootroot00000000000000#include "dfu_libusb.hh" #include #include "logger.hh" #include "utils.hh" // USB request types. #define REQUEST_TYPE_TO_HOST 0xA1 #define REQUEST_TYPE_TO_DEVICE 0x21 enum { REQUEST_DETACH = 0, REQUEST_DNLOAD = 1, REQUEST_UPLOAD = 2, REQUEST_GETSTATUS = 3, REQUEST_CLRSTATUS = 4, REQUEST_GETSTATE = 5, REQUEST_ABORT = 6, }; enum { appIDLE = 0, appDETACH = 1, dfuIDLE = 2, dfuDNLOAD_SYNC = 3, dfuDNBUSY = 4, dfuDNLOAD_IDLE = 5, dfuMANIFEST_SYNC = 6, dfuMANIFEST = 7, dfuMANIFEST_WAIT_RESET = 8, dfuUPLOAD_IDLE = 9, dfuERROR = 10, }; /* ********************************************************************************************* * * Implementation of DFUDevice::Descriptor * ********************************************************************************************* */ DFUDevice::Descriptor::Descriptor(const USBDeviceInfo &info, uint8_t bus, uint8_t device) : USBDeviceDescriptor(info, USBDeviceHandle(bus, device)) { // pass... } /* ********************************************************************************************* * * Implementation of DFUDevice * ********************************************************************************************* */ DFUDevice::DFUDevice(const USBDeviceDescriptor &descr, const ErrorStack &err, QObject *parent) : QObject(parent), _ctx(nullptr), _dev(nullptr) { if (USBDeviceInfo::Class::DFU != descr.interfaceClass()) { errMsg(err) << "Cannot connect to DFU device using a non DFU descriptor: " << descr.description() << "."; return; } int error = libusb_init(&_ctx); if (error < 0) { errMsg(err) << "Libusb init failed (" << error << "): " << libusb_strerror((enum libusb_error) error) << "."; return; } int num=0; libusb_device **lst; libusb_device *dev=nullptr; if (0 > (num = libusb_get_device_list(_ctx, &lst))) { errMsg(err) << "Cannot obtain list of USB devices."; libusb_exit(_ctx); _ctx = nullptr; return; } logDebug() << "Try to detect USB DFU interface " << descr.description() << "."; USBDeviceHandle addr = descr.device().value(); for (int i=0; (i libusb_get_device_descriptor(lst[i],&usb_descr)) continue; if (descr.vendorId() != usb_descr.idVendor) continue; if (descr.productId() != usb_descr.idProduct) continue; logDebug() << "Matching device found at bus " << addr.bus << ", device " << addr.device << " with vendor ID " << QString::number(usb_descr.idVendor, 16) << " and product ID " << QString::number(usb_descr.idProduct, 16) << "."; libusb_ref_device(lst[i]); dev = lst[i]; } // Unref all devices and free list, matching device was referenced earlier libusb_free_device_list(lst, 1); if (nullptr == dev) { errMsg(err) << "No matching device found: " << descr.description() << "."; libusb_exit(_ctx); _ctx = nullptr; return; } if (0 > (error = libusb_open(dev, &_dev))) { errMsg(err) << "Cannot open device " << descr.description() << ": " << libusb_strerror((enum libusb_error) error) << "."; libusb_unref_device(dev); libusb_exit(_ctx); _ctx = nullptr; return; } if (libusb_kernel_driver_active(_dev, 0) && libusb_detach_kernel_driver(_dev, 0)) { errMsg(err) << "Cannot detach kernel driver for device " << descr.description() << ". Interface claim will likely fail."; } if (0 > (error = libusb_claim_interface(_dev, 0))) { errMsg(err) << "Failed to claim USB interface " << descr.description() << ": " << libusb_strerror((enum libusb_error) error) << "."; libusb_close(_dev); _dev = nullptr; libusb_exit(_ctx); _ctx = nullptr; return; } logDebug() << "Connected to DFU device " << descr.description() << "."; } DFUDevice::~DFUDevice() { close(); } QList DFUDevice::detect(uint16_t vid, uint16_t pid) { QList res; int error, num; libusb_context *ctx; if (0 > (error = libusb_init(&ctx))) { logError() << "Libusb init failed (" << error << "): " << libusb_strerror((enum libusb_error) error) << "."; return res; } libusb_device **lst; if (0 == (num = libusb_get_device_list(ctx, &lst))) { logDebug() << "No USB devices found at all."; // unref devices and free list libusb_free_device_list(lst, 1); return res; } logDebug() << "Search for DFU devices matching VID:PID " << QString::number(vid, 16) << ":" << QString::number(pid, 16) << "."; for (int i=0; (i error) { errMsg(err) << "Cannot detach device: " << libusb_strerror((enum libusb_error) error) << "."; return error; } return 0; } int DFUDevice::get_status(const ErrorStack &err) { int error = libusb_control_transfer( _dev, REQUEST_TYPE_TO_HOST, REQUEST_GETSTATUS, 0, 0, (unsigned char*)&_status, 6, 0); if (0 > error) { errMsg(err) << "Cannot get status: " << libusb_strerror((enum libusb_error) error) << "."; return error; } return 0; } int DFUDevice::clear_status(const ErrorStack &err) { int error = libusb_control_transfer( _dev, REQUEST_TYPE_TO_DEVICE, REQUEST_CLRSTATUS, 0, 0, NULL, 0, 0); if (0 > error) { errMsg(err) << "Cannot clear status: " << libusb_strerror((enum libusb_error) error) << "."; return error; } return 0; } int DFUDevice::get_state(int &pstate, const ErrorStack &err) { unsigned char state; int error = libusb_control_transfer( _dev, REQUEST_TYPE_TO_HOST, REQUEST_GETSTATE, 0, 0, &state, 1, 0); pstate = state; if (error < 0) { errMsg(err) << "Cannot get state: " << libusb_strerror((enum libusb_error) error) << "."; return error; } return 0; } int DFUDevice::abort(const ErrorStack &err) { int error = libusb_control_transfer( _dev, REQUEST_TYPE_TO_DEVICE, REQUEST_ABORT, 0, 0, NULL, 0, 0); if (error < 0) { errMsg(err) << "Cannot abort: " << libusb_strerror((enum libusb_error) error) << "."; return error; } return 0; } int DFUDevice::wait_idle(const ErrorStack &err) { int state, error; for (;;) { if (0 > (error = get_state(state, err))) return 1; switch (state) { case dfuIDLE: return 0; case appIDLE: error = detach(1000, err); break; case dfuERROR: error = clear_status(err); break; case appDETACH: case dfuDNBUSY: case dfuMANIFEST_WAIT_RESET: usleep(100000); continue; default: error = abort(err); break; } if (error < 0) return 1; } } /* ********************************************************************************************* * * Implementation of DFUSEDevice * ********************************************************************************************* */ DFUSEDevice::DFUSEDevice(const USBDeviceDescriptor &descr, const ErrorStack &err, uint16_t blocksize, QObject *parent) : DFUDevice(descr, err, parent), _blocksize(blocksize) { // pass... } void DFUSEDevice::close() { leaveDFU(); DFUDevice::close(); } uint16_t DFUSEDevice::blocksize() const { return _blocksize; } bool DFUSEDevice::setAddress(uint32_t address, const ErrorStack &err) { uint8_t cmd[5] ={ 0x21, (uint8_t)address, (uint8_t)(address >> 8), (uint8_t)(address >> 16), (uint8_t)(address >> 24) }; if (int error = download(0, cmd, 5, err)) { errMsg(err) << "Cannot set address to " << QString::number(address, 16) << "."; return error; } if (wait_idle(err)) { errMsg(err) << "Set address command failed."; return false; } return true; } bool DFUSEDevice::readBlock(unsigned block, uint8_t *data, const ErrorStack &err) { return 0 == upload(block+2, data, _blocksize, err); } bool DFUSEDevice::writeBlock(unsigned block, const uint8_t *data, const ErrorStack &err) { if (download(block+2, (uint8_t *)data, _blocksize, err)) { return false; } if (wait_idle(err)) { return false; } return true; } bool DFUSEDevice::erasePage(uint32_t address, const ErrorStack &err) { uint8_t cmd[5] ={ 0x41, (uint8_t)address, (uint8_t)(address >> 8), (uint8_t)(address >> 16), (uint8_t)(address >> 24) }; if (int error = download(0, cmd, 5, err)) { errMsg(err) << "Cannot erase page at address " << QString::number(address, 16) << "."; return error; } if (wait_idle(err)) { errMsg(err) << "Erase page command failed."; return false; } return true; } bool DFUSEDevice::eraseAll(const ErrorStack &err) { uint8_t cmd[1] ={0x41}; if (int error = download(0, cmd, 1, err)) { errMsg(err) << "Cannot erase entire memory."; return error; } if (wait_idle(err)) { errMsg(err) << "Erase memory command failed."; return false; } return true; } bool DFUSEDevice::releaseReadLock(const ErrorStack &err) { uint8_t cmd[1] ={0x92}; if (int error = download(0, cmd, 1, err)) { errMsg(err) << "Cannot unlock memory."; return error; } if (wait_idle(err)) { errMsg(err) << "Unlock memory command failed."; return false; } return true; } bool DFUSEDevice::leaveDFU(const ErrorStack &err) { if (int error = download(0, nullptr, 0)) { errMsg(err) << "Cannot leave DFU mode."; return error; } return true; } qdmr-0.12.3/lib/dfu_libusb.hh000066400000000000000000000112241501654372000157500ustar00rootroot00000000000000#ifndef DFU_LIBUSB_HH #define DFU_LIBUSB_HH #include #include #include "errorstack.hh" #include "radiointerface.hh" /** This class implements DFU protocol to access radios. * * Many manufactures use the standardized DFU protocol to program codeplugs and update the * firmware of their radios. This class implements this protocol, see * https://www.usb.org/sites/default/files/DFU_1.1.pdf for details. * * @ingroup rif */ class DFUDevice: public QObject { Q_OBJECT private: /** Status message from device. */ struct __attribute__((packed)) status_t { unsigned status : 8; unsigned poll_timeout : 24; unsigned state : 8; unsigned string_index : 8; }; public: /** Specialization to address a DFU device uniquely. */ class Descriptor: public USBDeviceDescriptor { public: /** Constructor from interface info, bus number and device address. */ Descriptor(const USBDeviceInfo &info, uint8_t bus, uint8_t device); }; public: /** Opens a connection to the USB-DFU device at vendor @c vid and product @c pid. */ DFUDevice(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr); /** Destructor. */ virtual ~DFUDevice(); /** Returns @c true if the DFU device interface is open. */ bool isOpen() const; /** Closes the DFU interface. */ void close(); /** Downloads some data to the device. */ int download(unsigned block, uint8_t *data, unsigned len, const ErrorStack &err=ErrorStack()); /** Uploads some data from the device. */ int upload(unsigned block, uint8_t *data, unsigned len, const ErrorStack &err=ErrorStack()); public: /** Finds all DFU interfaces with the specified VID/PID combination. */ static QList detect(uint16_t vid, uint16_t pid); protected: /** Internal used function to detach the device. */ int detach(int timeout, const ErrorStack &err=ErrorStack()); /** Internal used function to read the current status. */ int get_status(const ErrorStack &err=ErrorStack()); /** Internal used function to clear the status. */ int clear_status(const ErrorStack &err=ErrorStack()); /** Internal used function to read the state. */ int get_state(int &pstate, const ErrorStack &err=ErrorStack()); /** Internal used function to abort the current operation. */ int abort(const ErrorStack &err=ErrorStack()); /** Internal used function to busy-wait for a response from the device. */ int wait_idle(const ErrorStack &err=ErrorStack()); protected: /** USB context. */ libusb_context *_ctx; /** USB device object. */ libusb_device_handle *_dev; /** Device status. */ status_t _status; }; /** Implements the ST MCU extensions for the DFU protocol, aka DfuSe. * This class implements the extensions to the DFU protocaol used by ST for their MCUs. This is * also known as DfuSe. * * @ingroup rif */ class DFUSEDevice: public DFUDevice { public: /** Constructor, also connects to the specified VID/PID device found first. The @c blocksize * specifies the blocksize for every read and write operation. */ DFUSEDevice(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), uint16_t blocksize=32, QObject *parent=nullptr); /** Closes the connection. */ void close(); /** Returns the blocksize in bytes. */ uint16_t blocksize() const; /** Sets the read/write reference address. By default this is @c 0x08000000 (flash program * memory address on ST devices). */ bool setAddress(uint32_t address, const ErrorStack &err=ErrorStack()); /** Reads a block of data from the device. The address is computed as base address + * block*blocksize, where the base address is set using the @c setAddress method. */ bool readBlock(unsigned block, uint8_t *data, const ErrorStack &err=ErrorStack()); /** Writes a block of data to the device. The address is computed as base address + * block*blocksize, where the base address is set using the @c setAddress method. */ bool writeBlock(unsigned block, const uint8_t *data, const ErrorStack &err=ErrorStack()); /** Erases an entire page of memory at the specified address. A page is usually 0x10000 bytes * large. */ bool erasePage(uint32_t address, const ErrorStack &err=ErrorStack()); /** Erases the entire memory. Not reconmended. */ bool eraseAll(const ErrorStack &err=ErrorStack()); /** Releases the read lock. This usually also erases the entire memory. Not reconmended. */ bool releaseReadLock(const ErrorStack &err=ErrorStack()); /** Leaves the DFU mode, may boot into the application code. */ bool leaveDFU(const ErrorStack &err=ErrorStack()); protected: /** Holds the block size in bytes. */ uint16_t _blocksize; }; #endif // DFU_LIBUSB_HH qdmr-0.12.3/lib/dfufile.cc000066400000000000000000000436021501654372000152430ustar00rootroot00000000000000#include "dfufile.hh" #include #include #include "crc32.hh" #include "logger.hh" typedef struct __attribute((packed)) { uint8_t signature[5]; ///< File signature = "DfuSe" uint8_t version; ///< File version = 0x01 uint32_t image_size; ///< Total file size in big-endian uint8_t n_targets; ///< Number of images. } file_prefix_t; typedef struct __attribute((packed)) { uint16_t device_id; ///< Device id in little endian uint16_t product_id; ///< Product id in little endian uint16_t vendor_id; ///< Vendor id in little endian uint8_t DFUlo; ///< Fixed 0x1A uint8_t DFUhi; ///< Fixed 0x01 uint8_t signature[3]; ///< Fixed "UFD", {0x44, 0x46, 0x55} uint8_t size; ///< Suffix size, fixed 16 uint32_t crc; ///< CRC over compltete file excluding the CRC in little endian. } file_suffix_t; typedef struct __attribute((packed)) { uint8_t signature[6]; ///< Target signature, fixed "Target" uint8_t alternate_setting; ///< Alternate setting for image. uint32_t is_named; ///< Bool, if target is named; uint8_t name[255]; ///< Target name (0-padded?). uint32_t size; ///< Size of complete image excl. prefix in big endian. uint32_t n_elements; ///< Number of elements in image in big endian. } image_prefix_t; typedef struct __attribute((packed)) { uint32_t address; ///< Target address of element in big endian. uint32_t size; ///< Element size in big endian; } element_prefix_t; /* ********************************************************************************************* * * Implementation of DFUFile * ********************************************************************************************* */ DFUFile::DFUFile(QObject *parent) : QObject(parent) { // pass... } uint32_t DFUFile::size() const { uint32_t size = sizeof(file_prefix_t); foreach (const Image &i, _images) { size += i.size(); } return size+sizeof(file_suffix_t); } uint32_t DFUFile::memSize() const { uint32_t size = 0; foreach (const Image &i, _images) { size += i.memSize(); } return size; } int DFUFile::numImages() const { return _images.size(); } const DFUFile::Image & DFUFile::image(int i) const { return _images[i]; } DFUFile::Image & DFUFile::image(int i) { return _images[i]; } void DFUFile::addImage(const QString &name, uint8_t altSettings) { _images.append(Image(name, altSettings)); } void DFUFile::addImage(const Image &img) { _images.append(img); } void DFUFile::remImage(int i) { _images.remove(i); } bool DFUFile::isAligned(unsigned blocksize) const { for (int i=0; i<_images.size(); i++) if (! _images.at(i).isAligned(blocksize)) return false; return true; } bool DFUFile::read(const QString &filename, const ErrorStack &err) { QFile file(filename); if (! file.open(QIODevice::ReadOnly)) { errMsg(err) << "Cannot read DFU file '" << filename << "': " << file.errorString() << "."; return false; } if (! read(file, err)) { file.close(); return false; } return true; } bool DFUFile::read(QFile &file, const ErrorStack &err) { CRC32 crc; _images.clear(); file_prefix_t prefix; if (sizeof(file_prefix_t) != file.read((char *)&prefix, sizeof(file_prefix_t))) { errMsg(err) << "Cannot read suffix: " << file.errorString() << "."; errMsg(err) << "Cannot read DFU file '" << file.fileName() << "'."; return false; } // update crc crc.update((const uint8_t *)&prefix, sizeof(file_prefix_t)); if (memcmp(prefix.signature, "DfuSe", 5)) { errMsg(err) << "Invalid DFU file signature. Not a DFU file?"; errMsg(err) << "Cannot read DFU file '" << file.fileName() << "'."; return false; } uint32_t filesize = qFromLittleEndian(prefix.image_size); uint8_t n_images = prefix.n_targets; for (uint8_t i=0; i= _images.size()) return false; return image(img).isAllocated(offset); } unsigned char * DFUFile::data(uint32_t offset, uint32_t img) { if (int(img) >= _images.size()) return nullptr; return image(img).data(offset); } const unsigned char * DFUFile::data(uint32_t offset, uint32_t img) const { if (int(img) >= _images.size()) return nullptr; return image(img).data(offset); } void DFUFile::dump(QTextStream &stream) const { stream << "DFU file with " << _images.size() << " images:\n"; foreach (const Image &i, _images) { i.dump(stream); } } /* ********************************************************************************************* * * Implementation of DFUFile::Element * ********************************************************************************************* */ DFUFile::Element::Element() : _address(0), _data() { // pass... } DFUFile::Element::Element(uint32_t addr, uint32_t size) : _address(addr), _data(size, 0x00) { // pass... } DFUFile::Element::Element(const Element &other) : _address(other._address), _data(other._data) { // pass... } DFUFile::Element & DFUFile::Element::operator=(const Element &other) { _address = other._address; _data = other._data; return *this; } uint32_t DFUFile::Element::size() const { return sizeof(element_prefix_t) + _data.size(); } uint32_t DFUFile::Element::memSize() const { return _data.size(); } uint32_t DFUFile::Element::address() const { return _address; } void DFUFile::Element::setAddress(uint32_t addr) { _address = addr; } bool DFUFile::Element::isAligned(unsigned blocksize) const { return (0 == (_address % blocksize)) && (0 == (_data.size() % blocksize)); } const QByteArray & DFUFile::Element::data() const { return _data; } QByteArray & DFUFile::Element::data() { return _data; } bool DFUFile::Element::read(QFile &file, CRC32 &crc, QString &errorMessage) { // Read Element prefix: element_prefix_t prefix; if (sizeof(element_prefix_t) != file.read((char *)&prefix, sizeof(element_prefix_t))) { errorMessage = tr("Cannot read DFU file '%1': Cannot read element prefix: %2").arg(file.fileName()).arg(file.errorString()); return false; } crc.update((const uint8_t *) &prefix, sizeof(element_prefix_t)); _address = qFromLittleEndian(prefix.address); uint32_t size = qFromLittleEndian(prefix.size); _data.clear(); _data = file.read(size); if (size != uint32_t(_data.size())) { errorMessage = tr("Cannot read DFU file '%1': Cannot read element data: %2").arg(file.fileName()).arg(file.errorString()); return false; } crc.update(_data); return true; } bool DFUFile::Element::write(QFile &file, CRC32 &crc, QString &errorMessage) const { element_prefix_t prefix; prefix.address = qToLittleEndian(_address); prefix.size = qToLittleEndian(uint32_t(_data.size())); crc.update((uint8_t *) &prefix, sizeof(element_prefix_t)); if (sizeof(element_prefix_t) != file.write((const char *)&prefix, sizeof(element_prefix_t))) { errorMessage = tr("Cannot write element prefix to file '%1': %2") .arg(file.fileName()).arg(file.errorString()); return false; } crc.update(_data); if (_data.size() != file.write(_data)) { errorMessage = tr("Cannot write element data to file '%1': %2") .arg(file.fileName()).arg(file.errorString()); return false; } return true; } void DFUFile::Element::dump(QTextStream &stream) const { stream.setIntegerBase(16); stream << " Element @ 0x" << _address << ", size=0x" << _data.size() << "\n"; int nrow = _data.size()/16; uint8_t last_line[16]; memset(last_line, 0, 16); bool skipping = false; for (int i=0; i0) && (0==memcmp(last_line, _data.constData()+i*16, 16)) && skipping) continue; if ((i>0) && (0==memcmp(last_line, _data.constData()+i*16, 16))) { skipping = true; stream.setFieldAlignment(QTextStream::AlignRight); stream << qSetFieldWidth(8) << "*" << qSetFieldWidth(1) << "\n"; continue; } memcpy(last_line, _data.constData()+i*16, 16); skipping = false; stream << qSetFieldWidth(8) << (_address+i*16) << qSetFieldWidth(1) << " "; for (int j=(i*16); j<(i*16+8); j++) { stream << QString("%1").arg(uint8_t(_data.at(j)), 2, 16, QChar('0')) << qSetFieldWidth(1) << " "; } stream << " "; for (int j=(i*16+8); j<(i*16+16); j++) { stream << QString("%1").arg(uint8_t(_data.at(j)), 2, 16, QChar('0')) << qSetFieldWidth(1) << " "; } stream << " |"; for (int j=(i*16); j<(i*16+16); j++) { char c = _data.at(j); if ((c>=32) && (c<127)) stream << c; else stream << "."; } stream << "|\n"; } } /* ********************************************************************************************* * * Implementation of DFUFile::Image * ********************************************************************************************* */ DFUFile::Image::Image() : _alternate_settings(0), _name(), _elements(), _addressmap() { // pass... } DFUFile::Image::Image(const QString &name, uint8_t altSettings) : _alternate_settings(altSettings), _name(name), _elements(), _addressmap() { // pass... } DFUFile::Image::Image(const Image &other) : _alternate_settings(other._alternate_settings), _name(other._name), _elements(other._elements), _addressmap(other._addressmap) { // pass... } DFUFile::Image::~Image() { // pass... } DFUFile::Image & DFUFile::Image::operator=(const Image &other) { _alternate_settings = other._alternate_settings; _name = other._name; _elements = other._elements; _addressmap = other._addressmap; return *this; } uint32_t DFUFile::Image::size() const { uint32_t size = sizeof(image_prefix_t); foreach (const Element &e, _elements) size += e.size(); return size; } uint32_t DFUFile::Image::memSize() const { uint32_t size = 0; foreach (const Element &e, _elements) size += e.memSize(); return size; } uint8_t DFUFile::Image::alternateSettings() const { return _alternate_settings; } void DFUFile::Image::setAlternateSettings(uint8_t s) { _alternate_settings = s; } bool DFUFile::Image::isNamed() const { return ! _name.isEmpty(); } const QString & DFUFile::Image::name() const { return _name; } void DFUFile::Image::setName(const QString &name) { _name = name; } int DFUFile::Image::numElements() const { return _elements.size(); } const DFUFile::Element & DFUFile::Image::element(int i) const { return _elements[i]; } DFUFile::Element & DFUFile::Image::element(int i) { return _elements[i]; } void DFUFile::Image::addElement(uint32_t addr, uint32_t size, int index) { if ((0 > index) || (_elements.size() <= index)) { _elements.append(Element(addr, size)); _addressmap.add(addr, size); } else { _elements.insert(index, Element(addr, size)); _addressmap.add(addr, size, index); } } void DFUFile::Image::addElement(const Element &element) { _elements.append(element); _addressmap.add(element.address(), element.size()); } void DFUFile::Image::remElement(int i) { _elements.remove(i); _addressmap.rem(i); } bool DFUFile::Image::isAligned(unsigned blocksize) const { for (int i=0; i<_elements.count(); i++) if (! _elements.at(i).isAligned(blocksize)) return false; return true; } bool DFUFile::Image::read(QFile &file, CRC32 &crc, QString &errorMessage) { image_prefix_t prefix; if (sizeof(image_prefix_t) != file.read((char *)&prefix, sizeof(image_prefix_t))) { errorMessage = tr("Cannot read DFU file '%1': Cannot read image: %2").arg(file.fileName()).arg(file.errorString()); return false; } crc.update((const uint8_t *) &prefix, sizeof(image_prefix_t)); if (memcmp(prefix.signature, "Target", 6)) { errorMessage = tr("Cannot read DFU file '%1': Invalid image signature value.").arg(file.fileName()); return false; } _alternate_settings = prefix.alternate_setting; if (0x01 ==qFromLittleEndian(prefix.is_named)) { char tmp[256]; tmp[255]=0; memcpy(tmp, prefix.name, 255); _name = tmp; } uint32_t size = qFromLittleEndian(prefix.size); uint32_t n_elements = qFromLittleEndian(prefix.n_elements); for (uint32_t i=0; iaddElement(element); } // verify size: if (size != (this->size()-sizeof(image_prefix_t))) { errorMessage = tr("Cannot read DFU file '%1': Invalid image size %2b specified, expected %3b.") .arg(file.fileName()).arg(size).arg(this->size()-sizeof(image_prefix_t)); return false; } return true; } bool DFUFile::Image::write(QFile &file, CRC32 &crc, QString &errorMessage) const { image_prefix_t prefix; memcpy(prefix.signature, "Target", 6); prefix.alternate_setting = _alternate_settings; prefix.is_named = qToLittleEndian(uint32_t(_name.isEmpty() ? 0 : 1)); memset(prefix.name, 0, 255); if (! _name.isEmpty()) memcpy(prefix.name, _name.toLocal8Bit().constData(), std::min(255, _name.size())); prefix.size = qToLittleEndian(uint32_t(size()-sizeof(image_prefix_t))); prefix.n_elements = qToLittleEndian(uint32_t(_elements.size())); crc.update((uint8_t *)&prefix, sizeof(image_prefix_t)); if (sizeof(image_prefix_t) != file.write((char *)&prefix, sizeof(image_prefix_t))) { errorMessage = tr("Cannot write image prefix to '%1': %2.") .arg(file.fileName()).arg(file.errorString()); return false; } foreach (const Element &e, _elements) { if (! e.write(file, crc, errorMessage)) return false; } return true; } void DFUFile::Image::sort() { std::stable_sort(_elements.begin(), _elements.end(), [](const Element &first, const Element &second) { return first.address() idx) { logFatal() << "Cannot resolve offset " << QString::number(offset, 16) << "h."; return nullptr; } return (unsigned char *)(element(idx).data().data()+ (offset-element(idx).address())); } const unsigned char * DFUFile::Image::data(uint32_t offset) const { int idx = _addressmap.find(offset); if (0 > idx) { logFatal() << "Cannot resolve offset " << QString::number(offset, 16) << "h."; return nullptr; } return (unsigned char *)(element(idx).data().data()+ (offset-element(idx).address())); } qdmr-0.12.3/lib/dfufile.hh000066400000000000000000000232501501654372000152520ustar00rootroot00000000000000#ifndef DFUFILE_HH #define DFUFILE_HH #include #include #include #include #include #include "addressmap.hh" #include "errorstack.hh" class CRC32; /** A collection of images, each consisting of one or more memory sections. * * This class forms the base of all binary encoded codeplugs and call-sign DBs. To this end, it * represents the codeplug or call-sign DB memory as written and stored inside the radio. It also * implements the DFU file format (hence the name) and thus allows to store and load binary * codeplugs or call-sign DBs in files. Please note, that binary codeplugs and call-sign DBs are * highly vendor and device specific. Consequently, they should be be used to exchange codeplugs. * * DFU File consists of a file prefix followed by several images and a final file suffix. The file * prefix consists of a file signature of 5 bytes just consisting of the ASCII string "DfuSe", * followed by a single byte indicating the version number (V), here 0x01. The next field is a * uint32_t containing the file-size excluding the suffix in little endian. Finally, there is a * single byte (N) holding the number of images. * * @code * +---+---+---+---+---+---+---+---+---+---+---+ * | "DfuSe" | V | file size | N | * +---+---+---+---+---+---+---+---+---+---+---+ * @endcode * * The file suffix consists of the device, product and vendor IDs as uint16_t followed by a * fixed signature uin16_t 0x011a (little endian) followed by another fixed signature containing the * ASCII string "UFD". The next field (S) contains the size of the suffix, that is 16. Finally there * is a CRC32 field computed over the entire file excluding the CRC itself and stored in little * endian. * * @code * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ * | dev | prod | vend | Sig | UFD | S | CRC32 | * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ * @endcode * * Each image section consists of a image prefix followed by several element sections. The image * prefix consists of a 6byte signature containing the ASCII string "Target" followed by a * single byte indicating the so-called "alternate settings" field, usually 0x01. The 32bit field * "is named" just indicates that the 255 byte name field is set (i.e., 0x01 in little endian). * The next field contains the 32 bit size of the image excluding the image prefix in little endian. * Finally the last 32bit field contains the number of elements, the image consists of in little * endian too. * * @code * +---+---+---+---+---+---+---+---+---+---+---+---+...+---+---+---+---+---+---+---+---+---+ * | "Target" | A | is named | 255b name | size | N Elements | * +---+---+---+---+---+---+---+---+---+---+---+---+...+---+---+---+---+---+---+---+---+---+ * @endcode * * Finally, each element of an image is prefixed by a header containing the target address and size * of the element in little endian. * * @code * +---+---+---+---+---+---+---+---+---+...+---+ * | address | size | el. data | * +---+---+---+---+---+---+---+---+---+...+---+ * @endcode * * @ingroup util */ class DFUFile: public QObject { Q_OBJECT public: /** Represents a single element within a @c Image. */ class Element { public: /** Empty constructor. */ Element(); /** Constructs an element for the given address and of the given size. */ Element(uint32_t addr, uint32_t size); /** Copy constructor. */ Element(const Element &other); /** Copying assignment. */ Element &operator= (const Element &other); /** Returns the address of the element. */ uint32_t address() const; /** Sets the address of the element. */ void setAddress(uint32_t addr); /** Returns the size of the element (including headers). */ uint32_t size() const; /** Returns the memory size of the element. */ uint32_t memSize() const; /** Checks if the element address and size is aligned with the given block size. */ bool isAligned(unsigned blocksize) const; /** Returns a reference to the data. */ const QByteArray &data() const; /** Returns a reference to the data. */ QByteArray &data(); /** Reads an element from the given file and updates the CRC. */ bool read(QFile &file, CRC32 &crc, QString &errorMessage); /** Writes an element to the given file and updates the CRC. */ bool write(QFile &file, CRC32 &crc, QString &errorMessage) const; /** Dumps a textual representation of the element. */ void dump(QTextStream &stream) const; protected: /** The address of the element. */ uint32_t _address; /** The data of the element. */ QByteArray _data; }; /** Represents a single image within a @c DFUFile. */ class Image { public: /** Default constructor. * Constructs an empty image. */ Image(); /** Constructs an image with the given name and optional "alternative settings". */ Image(const QString &name, uint8_t altSettings=0); /** Copy constructor. */ Image(const Image &other); /** Destructor. */ virtual ~Image(); /** Copying assignment. */ Image &operator=(const Image &other); /** Returns the alternate settings byte. */ uint8_t alternateSettings() const; /** Sets the alternate settings byte. */ void setAlternateSettings(uint8_t s); /** Returns @c true if the image is named. */ bool isNamed() const; /** Returns the name of the image. */ const QString &name() const; /** Sets the name of the image. */ void setName(const QString &name); /** Returns the total size of the image (including headers). */ uint32_t size() const; /** Returns the memory size stored in the image. */ uint32_t memSize() const; /** Returns the number of elements of this image. */ int numElements() const; /** Returns a reference to the i-th element of the image. */ const Element &element(int i) const; /** Returns a reference to the i-th element of the image. */ Element &element(int i); /** Adds an element to the image with the given address and size at the specified index. * If the index is negative, the element gets appended. */ void addElement(uint32_t addr, uint32_t size, int index=-1); /** Adds an element to the image. */ void addElement(const Element &element); /** Removes the i-th element from this image. */ void remElement(int i); /** Checks if all element addresses and sizes is aligned with the given block size. */ bool isAligned(unsigned blocksize) const; /** Reads an image from the given file and updates the CRC. */ bool read(QFile &file, CRC32 &crc, QString &errorMessage); /** Writes this image to the given file and updates the CRC. */ bool write(QFile &file, CRC32 &crc, QString &errorMessage) const; /** Prints a textual representation of the image into the given stream. */ void dump(QTextStream &stream) const; /** Returns @c true if the specified address is allocated. */ virtual bool isAllocated(uint32_t offset) const; /** Returns a pointer to the encoded raw data at the specified offset. */ virtual unsigned char *data(uint32_t offset); /** Returns a const pointer to the encoded raw data at the specified offset. */ virtual const unsigned char *data(uint32_t offset) const; /** Sorts all elements with respect to their addresses. */ void sort(); protected: /** Alternate settings byte. */ uint8_t _alternate_settings; /** Optional image name. */ QString _name; /** The elements of the image. */ QVector _elements; /** Maps an address range to element index. */ AddressMap _addressmap; }; public: /** Constructs an empty DFU file object. */ DFUFile(QObject *parent=nullptr); /** Returns the total size of the DFU file. */ uint32_t size() const; /** Returns the total memory size stored in the DFU file. */ uint32_t memSize() const; /** Returns the number of images within the DFU file. */ int numImages() const; /** Returns a reference to the @c i-th image of the file. */ const Image &image(int i) const; /** Returns a reference to the @c i-th image of the file. */ Image &image(int i); /** Adds a new image to the file. */ void addImage(const QString &name, uint8_t altSettings=1); /** Adds an image to the file. */ void addImage(const Image &img); /** Deletes the @c i-th image from the file. */ void remImage(int i); /** Checks if all image addresses and sizes is aligned with the given block size. */ bool isAligned(unsigned blocksize) const; /** Reads the specified DFU file. * @return @c false on error. */ bool read(const QString &filename, const ErrorStack &err=ErrorStack()); /** Reads the specified DFU file. * @returns @c false on error. */ bool read(QFile &file, const ErrorStack &err=ErrorStack()); /** Writes to the specified file. * @returns @c false on error. */ bool write(const QString &filename, const ErrorStack &err=ErrorStack()); /** Writes to the specified file. * @returns @c false on error. */ bool write(QFile &file, const ErrorStack &err=ErrorStack()); /** Dumps a text representation of the DFU file structure to the specified text stream. */ void dump(QTextStream &stream) const; /** Returns @c true if the specified address (and image) is allocated. */ virtual bool isAllocated(uint32_t offset, uint32_t img=0) const; /** Returns a pointer to the encoded raw data at the specified offset. */ virtual unsigned char *data(uint32_t offset, uint32_t img=0); /** Returns a const pointer to the encoded raw data at the specified offset. */ virtual const unsigned char *data(uint32_t offset, uint32_t img=0) const; protected: /// The list of images. QVector _images; }; #endif // DFUFILE_HH qdmr-0.12.3/lib/dm1701.cc000066400000000000000000000017771501654372000145450ustar00rootroot00000000000000#include "dm1701.hh" #include "dm1701_limits.hh" #include "config.hh" #include "logger.hh" #include "utils.hh" RadioLimits *DM1701::_limits = nullptr; DM1701::DM1701(TyTInterface *device, QObject *parent) : TyTRadio(device, parent), _name("Baofeng DM-1701"), _codeplug() { // pass... } const QString & DM1701::name() const { return _name; } const RadioLimits & DM1701::limits() const { if (nullptr == _limits) _limits = new DM1701Limits(); return *_limits; } RadioInfo DM1701::defaultRadioInfo() { return RadioInfo( RadioInfo::DM1701, "dm1701", "DM-1701", "Baofeng", TyTInterface::interfaceInfo(), QList{ RadioInfo(RadioInfo::RT84, "rt84", "RT84", "Retevis", TyTInterface::interfaceInfo()) }); } const Codeplug & DM1701::codeplug() const { return _codeplug; } Codeplug & DM1701::codeplug() { return _codeplug; } const CallsignDB * DM1701::callsignDB() const { return &_callsigndb; } CallsignDB * DM1701::callsignDB() { return &_callsigndb; } qdmr-0.12.3/lib/dm1701.hh000066400000000000000000000040711501654372000145450ustar00rootroot00000000000000/** @defgroup dm1701 Baofeng DM-1701, Retevis RT84 * Device specific classes for Baofeng DM-1701 and Retevis RT84. * * \image html dm1701.png "DM-1701" width=200px * \image latex dm1701.png "DM-1701" width=200px * * The Baofeng DM-1701 and the identical Retevis RT84. This implementation supports CPS version 1.5 * and firmware version 2.3. * * Features: * - VHF/UHF, 136-174 MHz and 400-480 MHz * - Pout = 1, 3 and 5W * - 3000 channels * - 10000 contacts * - 250 RX group lists with ?? contacts each * - 250 zones, 64 channels each (A & B) * - 250 scan lists, 31 channels each * - ... * @ingroup tyt */ #ifndef DM1701_HH #define DM1701_HH #include "radio.hh" #include "tyt_radio.hh" #include "tyt_codeplug.hh" #include "tyt_callsigndb.hh" #include "dm1701_codeplug.hh" #include "dm1701_callsigndb.hh" /** Implements an USB interface to the Baofeng DM-1701 and Retevis RT84 VHF/UHF 5W DMR (Tier I&II) radios. * * The Baofeng DM-1701 and Retevis RT84 radios use a DFU-style communication protocol to read and write * codeplugs onto the radio (see @c DFUDevice). This class implements the communication details * using DFU protocol. * * @ingroup dm1701 */ class DM1701: public TyTRadio { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit DM1701(TyTInterface *device=nullptr, QObject *parent=nullptr); const QString &name() const; const RadioLimits &limits() const; const Codeplug &codeplug() const; Codeplug &codeplug(); const CallsignDB *callsignDB() const; CallsignDB *callsignDB(); /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); protected: /** The device identifier. */ QString _name; /** The actual binary codeplug representation. */ DM1701Codeplug _codeplug; /** The callsign DB object. */ DM1701CallsignDB _callsigndb; private: /** Holds the singleton instance of the radio limits. */ static RadioLimits *_limits; }; #endif // DM1701_HH qdmr-0.12.3/lib/dm1701_callsigndb.cc000066400000000000000000000003451501654372000167150ustar00rootroot00000000000000#include "dm1701_callsigndb.hh" DM1701CallsignDB::DM1701CallsignDB(QObject *parent) : TyTCallsignDB(parent) { image(0).setName("BTECH DM-1701 Callsign database."); } DM1701CallsignDB::~DM1701CallsignDB() { // pass... } qdmr-0.12.3/lib/dm1701_callsigndb.hh000066400000000000000000000010041501654372000167200ustar00rootroot00000000000000#ifndef DM1701_CALLSIGNDB_HH #define DM1701_CALLSIGNDB_HH #include "tyt_callsigndb.hh" /** Device specific implementation of the call-sign DB for the BTECH DM1701 and Retevis RT84. * * In fact this callsign DB is identical to the generic @c TyTCallsignDB. * * @ingroup dm1701 */ class DM1701CallsignDB : public TyTCallsignDB { Q_OBJECT public: /** Constructor. */ explicit DM1701CallsignDB(QObject *parent=nullptr); /** Destructor. */ virtual ~DM1701CallsignDB(); }; #endif // DM1701_CALLSIGNDB_HH qdmr-0.12.3/lib/dm1701_codeplug.cc000066400000000000000000000631521501654372000164220ustar00rootroot00000000000000#include "dm1701_codeplug.hh" #include "logger.hh" #include "config.hh" #include #define NUM_CHANNELS 3000 #define ADDR_CHANNELS 0x110000 #define CHANNEL_SIZE 0x000040 #define NUM_CONTACTS 10000 #define ADDR_CONTACTS 0x140000 #define CONTACT_SIZE 0x000024 #define NUM_ZONES 250 #define ADDR_ZONES 0x0149e0 #define ZONE_SIZE 0x000040 #define ADDR_ZONEEXTS 0x031000 #define ZONEEXT_SIZE 0x0000e0 #define NUM_GROUPLISTS 250 #define ADDR_GROUPLISTS 0x00ec20 #define GROUPLIST_SIZE 0x000060 #define NUM_SCANLISTS 250 #define ADDR_SCANLISTS 0x018860 #define SCANLIST_SIZE 0x000068 #define ADDR_TIMESTAMP 0x002000 #define ADDR_SETTINGS 0x002040 #define SETTINGS_SIZE 0x0000b0 #define ADDR_BOOTSETTINGS 0x02f000 #define ADDR_MENUSETTINGS 0x0020f0 #define ADDR_BUTTONSETTINGS 0x002100 #define BUTTONSETTINGS_SIZE 0x000014 #define ADDR_PRIVACY_KEYS 0x0059c0 #define NUM_GPSSYSTEMS 16 #define ADDR_GPSSYSTEMS 0x03ec40 #define GPSSYSTEM_SIZE 0x000010 #define ADDR_EMERGENCY_SETTINGS 0x005a50 #define NUM_EMERGENCY_SYSTEMS 32 #define ADDR_EMERGENCY_SYSTEMS 0x005a60 #define EMERGENCY_SYSTEM_SIZE 0x000028 #define ADDR_VFO_CHANNEL_A 0x02ef00 #define ADDR_VFO_CHANNEL_B 0x02ef40 /* ********************************************************************************************* * * Implementation of DM1701Codeplug::ChannelElement * ********************************************************************************************* */ DM1701Codeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size) : TyTCodeplug::ChannelElement::ChannelElement(ptr, size) { // pass... } DM1701Codeplug::ChannelElement::ChannelElement(uint8_t *ptr) : TyTCodeplug::ChannelElement(ptr, CHANNEL_SIZE) { // pass... } void DM1701Codeplug::ChannelElement::clear() { TyTCodeplug::ChannelElement::clear(); enableTightSquelch(false); enableReverseBurst(true); setPower(Channel::Power::High); setBit(0x0003, 6, true); setUInt8(0x0005, 0xc3); setUInt8(0x000f, 0xff); } bool DM1701Codeplug::ChannelElement::tightSquelchEnabled() const { return !getBit(0x0000, 5); } void DM1701Codeplug::ChannelElement::enableTightSquelch(bool enable) { setBit(0x0000, 5, !enable); } bool DM1701Codeplug::ChannelElement::reverseBurst() const { return getBit(0x0004, 2); } void DM1701Codeplug::ChannelElement::enableReverseBurst(bool enable) { setBit(0x0004, 2, enable); } Channel::Power DM1701Codeplug::ChannelElement::power() const { if (getBit(0x0004, 5)) return Channel::Power::High; return Channel::Power::Low; } void DM1701Codeplug::ChannelElement::setPower(Channel::Power pwr) { switch (pwr) { case Channel::Power::Min: case Channel::Power::Low: case Channel::Power::Mid: setBit(0x0004, 5, false); break; case Channel::Power::High: case Channel::Power::Max: setBit(0x0004, 5, true); } } Channel * DM1701Codeplug::ChannelElement::toChannelObj(const ErrorStack &err) const { Channel *ch = TyTCodeplug::ChannelElement::toChannelObj(err); if (nullptr == ch) return ch; ch->setPower(power()); // Apply extension if (ch->tytChannelExtension()) { ch->tytChannelExtension()->enableTightSquelch(tightSquelchEnabled()); ch->tytChannelExtension()->enableReverseBurst(reverseBurst()); } return ch; } void DM1701Codeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) { TyTCodeplug::ChannelElement::fromChannelObj(c, ctx); setPower(c->power()); // apply extensions (extension will be created in TyTCodeplug::ChannelElement::fromChannelObj) if (TyTChannelExtension *ex = c->tytChannelExtension()) { enableTightSquelch(ex->tightSquelch()); enableReverseBurst(ex->reverseBurst()); } } /* ********************************************************************************************* * * Implementation of DM1701Codeplug::VFOChannelElement * ********************************************************************************************* */ DM1701Codeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr, size_t size) : ChannelElement(ptr, size) { // pass... } DM1701Codeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr) : ChannelElement(ptr, CHANNEL_SIZE) { // pass... } DM1701Codeplug::VFOChannelElement::~VFOChannelElement() { // pass... } QString DM1701Codeplug::VFOChannelElement::name() const { return ""; } void DM1701Codeplug::VFOChannelElement::setName(const QString &txt) { Q_UNUSED(txt) // pass... } unsigned DM1701Codeplug::VFOChannelElement::stepSize() const { return (getUInt8(32)+1)*2500; } void DM1701Codeplug::VFOChannelElement::setStepSize(unsigned ss_Hz) { ss_Hz = std::min(50000U, std::max(ss_Hz, 2500U)); setUInt8(32, ss_Hz/2500-1); setUInt8(33, 0xff); } /* ********************************************************************************************* * * Implementation of DM1701Codeplug::GeneralSettingsElement * ********************************************************************************************* */ DM1701Codeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, size_t size) : TyTCodeplug::GeneralSettingsElement(ptr, size) { // pass... } DM1701Codeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr) : TyTCodeplug::GeneralSettingsElement(ptr, SETTINGS_SIZE) { // pass... } void DM1701Codeplug::GeneralSettingsElement::clear() { TyTCodeplug::GeneralSettingsElement::clear(); enableChannelModeA(true); enableChannelModeB(true); enableChannelMode(true); enableGroupCallMatch(true); enablePrivateCallMatch(true); setTimeZone(QTimeZone::systemTimeZone()); setChannelHangTime(3000); memset(_data+0x91, 0xff, 0x1f); } bool DM1701Codeplug::GeneralSettingsElement::channelModeA() const { return getBit(0x43,3); } void DM1701Codeplug::GeneralSettingsElement::enableChannelModeA(bool enable) { setBit(0x43,3, enable); } bool DM1701Codeplug::GeneralSettingsElement::channelModeB() const { return getBit(0x43,7); } void DM1701Codeplug::GeneralSettingsElement::enableChannelModeB(bool enable) { setBit(0x43,7, enable); } bool DM1701Codeplug::GeneralSettingsElement::channelMode() const { return 0xff == getUInt8(0x57); } void DM1701Codeplug::GeneralSettingsElement::enableChannelMode(bool enable) { setUInt8(0x57, enable ? 0xff : 0x00); } bool DM1701Codeplug::GeneralSettingsElement::groupCallMatch() const { return getBit(0x6b, 0); } void DM1701Codeplug::GeneralSettingsElement::enableGroupCallMatch(bool enable) { setBit(0x6b, 0, enable); } bool DM1701Codeplug::GeneralSettingsElement::privateCallMatch() const { return getBit(0x6b, 1); } void DM1701Codeplug::GeneralSettingsElement::enablePrivateCallMatch(bool enable) { setBit(0x6b, 1, enable); } QTimeZone DM1701Codeplug::GeneralSettingsElement::timeZone() const { return QTimeZone((int(getUInt5(0x6b, 3))-12)*3600); } void DM1701Codeplug::GeneralSettingsElement::setTimeZone(const QTimeZone &zone) { int idx = (zone.standardTimeOffset(QDateTime::currentDateTime())/3600)+12; setUInt5(0x6b, 3, uint8_t(idx)); } unsigned DM1701Codeplug::GeneralSettingsElement::channelHangTime() const { return unsigned(getUInt8(0x90))*100; } void DM1701Codeplug::GeneralSettingsElement::setChannelHangTime(unsigned dur) { setUInt8(0x90, dur/100); } bool DM1701Codeplug::GeneralSettingsElement::fromConfig(const Config *config) { if (! TyTCodeplug::GeneralSettingsElement::fromConfig(config)) return false; setTimeZone(QTimeZone::systemTimeZone()); // apply extension if (TyTSettingsExtension *ex = config->settings()->tytExtension()) { enableChannelMode(ex->channelMode()); enableChannelModeA(ex->channelModeA()); enableChannelModeB(ex->channelModeB()); enableGroupCallMatch(ex->groupCallMatch()); enablePrivateCallMatch(ex->privateCallMatch()); setChannelHangTime(ex->channelHangTime()); } return true; } bool DM1701Codeplug::GeneralSettingsElement::updateConfig(Config *config) { if (! TyTCodeplug::GeneralSettingsElement::updateConfig(config)) return false; // Update extension if set. if (TyTSettingsExtension *ex = config->settings()->tytExtension()) { ex->enableChannelMode(channelMode()); ex->enableChannelModeA(channelModeA()); ex->enableChannelModeB(channelModeB()); ex->enableGroupCallMatch(groupCallMatch()); ex->enablePrivateCallMatch(privateCallMatch()); ex->setChannelHangTime(channelHangTime()); } return true; } /* ********************************************************************************************* * * Implementation of DM1701Codeplug::ButtonSettingsElement * ********************************************************************************************* */ DM1701Codeplug::ButtonSettingsElement::ButtonSettingsElement(uint8_t *ptr, size_t size) : TyTCodeplug::ButtonSettingsElement(ptr, size) { // pass... } DM1701Codeplug::ButtonSettingsElement::ButtonSettingsElement(uint8_t *ptr) : TyTCodeplug::ButtonSettingsElement(ptr, BUTTONSETTINGS_SIZE) { // pass... } void DM1701Codeplug::ButtonSettingsElement::clear() { setSideButton3Short(ButtonAction::Disabled); setSideButton3Long(ButtonAction::Disabled); setProgButton1Short(ButtonAction::Disabled); setProgButton1Long(ButtonAction::Disabled); setProgButton2Short(ButtonAction::Disabled); setProgButton2Long(ButtonAction::Disabled); } TyTCodeplug::ButtonSettingsElement::ButtonAction DM1701Codeplug::ButtonSettingsElement::sideButton3Short() const { return ButtonAction(getUInt8(0x06)); } void DM1701Codeplug::ButtonSettingsElement::setSideButton3Short(ButtonAction action) { setUInt8(0x06, action); } TyTCodeplug::ButtonSettingsElement::ButtonAction DM1701Codeplug::ButtonSettingsElement::sideButton3Long() const { return ButtonAction(getUInt8(0x07)); } void DM1701Codeplug::ButtonSettingsElement::setSideButton3Long(ButtonAction action) { setUInt8(0x07, action); } TyTCodeplug::ButtonSettingsElement::ButtonAction DM1701Codeplug::ButtonSettingsElement::progButton1Short() const { return ButtonAction(getUInt8(0x08)); } void DM1701Codeplug::ButtonSettingsElement::setProgButton1Short(ButtonAction action) { setUInt8(0x08, action); } TyTCodeplug::ButtonSettingsElement::ButtonAction DM1701Codeplug::ButtonSettingsElement::progButton1Long() const { return ButtonAction(getUInt8(0x09)); } void DM1701Codeplug::ButtonSettingsElement::setProgButton1Long(ButtonAction action) { setUInt8(0x09, action); } TyTCodeplug::ButtonSettingsElement::ButtonAction DM1701Codeplug::ButtonSettingsElement::progButton2Short() const { return ButtonAction(getUInt8(0x0a)); } void DM1701Codeplug::ButtonSettingsElement::setProgButton2Short(ButtonAction action) { setUInt8(0x0a, action); } TyTCodeplug::ButtonSettingsElement::ButtonAction DM1701Codeplug::ButtonSettingsElement::progButton2Long() const { return ButtonAction(getUInt8(0x0b)); } void DM1701Codeplug::ButtonSettingsElement::setProgButton2Long(ButtonAction action) { setUInt8(0x0b, action); } bool DM1701Codeplug::ButtonSettingsElement::fromConfig(const Config *config) { if (! TyTCodeplug::ButtonSettingsElement::fromConfig(config)) return false; if (config->tytExtension()) { TyTButtonSettings *ex = config->tytExtension()->buttonSettings(); setSideButton3Short(ex->sideButton3Short()); setSideButton3Long(ex->sideButton3Long()); setProgButton1Short(ex->progButton1Short()); setProgButton1Long(ex->progButton1Long()); setProgButton2Short(ex->progButton2Short()); setProgButton2Long(ex->progButton2Long()); } return true; } bool DM1701Codeplug::ButtonSettingsElement::updateConfig(Config *config) { if (! TyTCodeplug::ButtonSettingsElement::updateConfig(config)) return false; if (config->tytExtension()) { TyTButtonSettings *ex = config->tytExtension()->buttonSettings(); ex->setSideButton3Short(sideButton3Short()); ex->setSideButton3Long(sideButton3Long()); ex->setProgButton1Short(progButton1Short()); ex->setProgButton1Long(progButton1Long()); ex->setProgButton2Short(progButton2Short()); ex->setProgButton2Long(progButton2Long()); } return true; } /* ******************************************************************************************** * * Implementation of UV390Codeplug::ZoneElement * ******************************************************************************************** */ DM1701Codeplug::ZoneExtElement::ZoneExtElement(uint8_t *ptr, size_t size) : Codeplug::Element(ptr, size) { // pass... } DM1701Codeplug::ZoneExtElement::ZoneExtElement(uint8_t *ptr) : Codeplug::Element(ptr, ZONEEXT_SIZE) { // pass... } DM1701Codeplug::ZoneExtElement::~ZoneExtElement() { // pass... } void DM1701Codeplug::ZoneExtElement::clear() { memset(_data, 0x00, 0xe0); } uint16_t DM1701Codeplug::ZoneExtElement::memberIndexA(unsigned n) const { return getUInt16_le(0x00 + 2*n); } void DM1701Codeplug::ZoneExtElement::setMemberIndexA(unsigned n, uint16_t idx) { setUInt16_le(0x00 + 2*n, idx); } uint16_t DM1701Codeplug::ZoneExtElement::memberIndexB(unsigned n) const { return getUInt16_le(0x60 + 2*n); } void DM1701Codeplug::ZoneExtElement::setMemberIndexB(unsigned n, uint16_t idx) { setUInt16_le(0x60 + 2*n, idx); } bool DM1701Codeplug::ZoneExtElement::fromZoneObj(const Zone *zone, Context &ctx) { // Store remaining channels from list A for (int i=16; i<64; i++) { if (i < zone->A()->count()) setMemberIndexA(i-16, ctx.index(zone->A()->get(i))); else setMemberIndexA(i-16, 0); } // Store channel from list B for (int i=0; i<64; i++) { if (i < zone->B()->count()) setMemberIndexB(i, ctx.index(zone->B()->get(i))); else setMemberIndexB(i, 0); } return true; } bool DM1701Codeplug::ZoneExtElement::linkZoneObj(Zone *zone, Context &ctx) { for (int i=0; (i<48) && memberIndexA(i); i++) { if (! ctx.has(memberIndexA(i))) { logError() << "Cannot link zone extension: Channel index " << memberIndexA(i) << " not defined."; return false; } zone->A()->add(ctx.get(memberIndexA(i))); } for (int i=0; (i<64) && memberIndexB(i); i++) { if (! ctx.has(memberIndexB(i))) { logWarn() << "Cannot link zone extension: Channel index " << memberIndexB(i) << " not defined."; return false; } zone->B()->add(ctx.get(memberIndexB(i))); } return true; } /* ********************************************************************************************* * * Implementation of DM1701Codeplug * ********************************************************************************************* */ DM1701Codeplug::DM1701Codeplug(QObject *parent) : TyTCodeplug(parent) { addImage("Baofeng DM-1701 Codeplug"); image(0).addElement(0x002000, 0x3e000); image(0).addElement(0x110000, 0x90000); // Clear entire codeplug clear(); } DM1701Codeplug::~DM1701Codeplug() { // pass... } void DM1701Codeplug::clearTimestamp() { TimestampElement(data(ADDR_TIMESTAMP)).clear(); } bool DM1701Codeplug::encodeTimestamp() { TimestampElement ts(data(ADDR_TIMESTAMP)); ts.setTimestamp(QDateTime::currentDateTime()); return true; } void DM1701Codeplug::clearGeneralSettings() { GeneralSettingsElement(data(ADDR_SETTINGS)).clear(); } bool DM1701Codeplug::encodeGeneralSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err) return GeneralSettingsElement(data(ADDR_SETTINGS)).fromConfig(config); } bool DM1701Codeplug::decodeGeneralSettings(Config *config, const ErrorStack &err) { Q_UNUSED(err) return GeneralSettingsElement(data(ADDR_SETTINGS)).updateConfig(config); } void DM1701Codeplug::clearChannels() { // Clear channels for (int i=0; ichannelList()->count()) { chan.fromChannelObj(config->channelList()->channel(i), ctx); } else { chan.clear(); } } return true; } bool DM1701Codeplug::createChannels(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; ichannelList()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid channel at index %" << i << "."; return false; } } return true; } bool DM1701Codeplug::linkChannels(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link channel at index " << i << "."; return false; } } return true; } void DM1701Codeplug::clearContacts() { // Clear contacts for (int i=0; icontacts()->digitalCount()) cont.fromContactObj(config->contacts()->digitalContact(i)); else cont.clear(); } return true; } bool DM1701Codeplug::createContacts(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; icontacts()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid contact at index " << i << "."; return false; } } return true; } void DM1701Codeplug::clearZones() { // Clear zones & zone extensions for (int i=0; izones()->count()) { zone.fromZoneObj(config->zones()->zone(i), ctx); if (config->zones()->zone(i)->B()->count() || (16 < config->zones()->zone(i)->A()->count())) ext.fromZoneObj(config->zones()->zone(i), ctx); } } return true; } bool DM1701Codeplug::createZones(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; izones()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid zone at index " << i << "."; return false; } } return true; } bool DM1701Codeplug::linkZones(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link zone at index " << i << "."; return false; } ZoneExtElement zoneext(data(ADDR_ZONEEXTS + i*ZONEEXT_SIZE)); if (! zoneext.linkZoneObj(ctx.get(i+1), ctx)) { errMsg(err) << "Cannot link zone extension at index " << i << "."; return false; } } return true; } void DM1701Codeplug::clearGroupLists() { for (int i=0; irxGroupLists()->count()) glist.fromGroupListObj(config->rxGroupLists()->list(i), ctx); else glist.clear(); } return true; } bool DM1701Codeplug::createGroupLists(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; irxGroupLists()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid group list at index " << i << "."; return false; } } return true; } bool DM1701Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link group list at index " << i << "."; return false; } } return true; } void DM1701Codeplug::clearScanLists() { // Clear scan lists for (int i=0; iscanlists()->count()) scan.fromScanListObj(config->scanlists()->scanlist(i), ctx); else scan.clear(); } return true; } bool DM1701Codeplug::createScanLists(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; iscanlists()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid scanlist at index " << i << "."; return false; } } return true; } bool DM1701Codeplug::linkScanLists(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link scan list at index" << i << "."; return false; } } return true; } void DM1701Codeplug::clearMenuSettings() { MenuSettingsElement(data(ADDR_MENUSETTINGS)).clear(); } void DM1701Codeplug::clearButtonSettings() { ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).clear(); } bool DM1701Codeplug::encodeButtonSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err) // Encode settings return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).fromConfig(config); } bool DM1701Codeplug::decodeButtonSetttings(Config *config, const ErrorStack &err) { Q_UNUSED(err) return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).updateConfig(config); } void DM1701Codeplug::clearPrivacyKeys() { EncryptionElement(data(ADDR_PRIVACY_KEYS)).clear(); } bool DM1701Codeplug::encodePrivacyKeys(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err); // First, reset keys clearPrivacyKeys(); // Get keys EncryptionElement keys(data(ADDR_PRIVACY_KEYS)); return keys.fromCommercialExt(config->commercialExtension(), ctx); } bool DM1701Codeplug::decodePrivacyKeys(Config *config, Context &ctx, const ErrorStack &err) { Q_UNUSED(config) // Get keys EncryptionElement keys(data(ADDR_PRIVACY_KEYS)); // Decode element if (! keys.updateCommercialExt(ctx)) { errMsg(err) << "Cannot create encryption extension."; return false; } return true; } void DM1701Codeplug::clearTextMessages() { MessageBankElement(data(Offset::messages())).clear(); } bool DM1701Codeplug::encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err) { return MessageBankElement(data(Offset::messages())).encode(ctx, flags, err); } bool DM1701Codeplug::decodeTextMessages(Context &ctx, const ErrorStack &err) { return MessageBankElement(data(Offset::messages())).decode(ctx, err); } void DM1701Codeplug::clearEmergencySystems() { EmergencySettingsElement(data(ADDR_EMERGENCY_SETTINGS)).clear(); for (int i=0; i * Start End Size Content * First segment 0x002000-0x040000 * 0x002000 0x00200c 0x0000c Timestamp see @c TyTCodeplug::TimestampElement. * 0x00200c 0x002040 0x00034 Reserved, filled with 0xff. * 0x002040 0x0020f0 0x000b0 General settings see @c DM1701Codeplug::GeneralSettingsElement. * 0x0020f0 0x002100 0x00010 Menu settings, see @c TyTCodeplug::MenuSettingsElement * 0x002100 0x002140 0x00040 Button config, see @c DM1701Codeplug::ButtonSettingsElement. * 0x002140 0x002180 0x00040 Reserved, filled with 0xff. * 0x002180 0x0059c0 0x03840 50 Text messages @ 0x120 bytes each. * 0x0059c0 0x005a70 0x000b0 Privacy keys, see @c TyTCodeplug::EncryptionElement. * 0x005a70 0x005a80 0x00010 Emergency system settings, see @c TyTCodeplug::EmergencySettingsElement. * 0x005a80 0x005f80 0x00500 Emergency systems, see @c TyTCodeplug::EmergencySystemElement. * 0x005f80 0x00ec20 0x08ca0 Reserved, filled with 0xff. * 0x00ec20 0x0149e0 0x05dc0 250 RX Group lists @ 0x60 bytes each, see @c TyTCodeplug::GroupListElement. * 0x0149e0 0x018860 0x03e80 250 Zones @ 0x40 bytes each, see @c TyTCodeplug::ZoneElement. * 0x018860 0x01edf0 0x06590 250 Scanlists @ 0x68 bytes each, see @c TyTCodeplug::ScanListElement. * 0x01edf0 0x02ef00 0x10110 Reserved, filled with @c 0xff. * 0x02ef00 0x02ef40 0x00040 VFO A channel, see @c DM1701Codeplug::VFOChannelElement. * 0x02ef40 0x02ef80 0x00040 VFO B channel, see @c DM1701Codeplug::VFOChannelElement. * 0x02ef80 0x031000 0x02080 Reserved, filled with @c 0xff. * 0x031000 0x03eac0 0x0dac0 250 Zone-extensions @ 0xe0 bytes each, see @c DM1701Codeplug::ZoneExtElement. * 0x03eac0 0x03ec40 0x00180 Reserved, filled with @c 0xff. * 0x03ec40 0x03ed40 0x00100 16 GPS systems @ 0x10 bytes each, see @c TyTCodeplug::GPSSystemElement. * 0x03ed40 0x040000 0x012c0 Reserved, filled with @c 0xff. * Second segment 0x110000-0x1a0000 * 0x110000 0x13ee00 0x2ee00 3000 Channels @ 0x40 bytes each, see @c DM1701Codeplug::ChannelElement. * 0x13ee00 0x140000 0x01200 Reserved, filled with @c 0xff. * 0x140000 0x197e40 0x57e40 10000 Contacts @ 0x24 bytes each, see @c TyTCodeplug::ContactElement. * 0x197e40 0x1a0000 0x081c0 Reserved, filled with @c 0xff. * * * @ingroup dm1701 */ class DM1701Codeplug : public TyTCodeplug { Q_OBJECT public: /** Extends the common @c TyTCodeplug::ChannelElement to implement the DM-1701 specific settings. * * Memory layout of the channel (size 0x0040 bytes): * @verbinclude dm1701_channel.txt */ class ChannelElement: public TyTCodeplug::ChannelElement { protected: /** Hidden constructor. */ ChannelElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit ChannelElement(uint8_t *ptr); void clear(); /** Returns @c true if the squelch is 'tight'. */ virtual bool tightSquelchEnabled() const; /** Enables/disables tight squelch. */ virtual void enableTightSquelch(bool enable); /** Returns @c true if the reversed burst is enabled. */ virtual bool reverseBurst() const; /** Enables/disables reverse burst. */ virtual void enableReverseBurst(bool enable); /** Returns the power of this channel. */ virtual Channel::Power power() const; /** Sets the power of this channel. */ virtual void setPower(Channel::Power pwr); /** Constructs a generic @c Channel object from the codeplug channel. */ Channel *toChannelObj(const ErrorStack &err=ErrorStack()) const; /** Initializes this codeplug channel from the given generic configuration. */ void fromChannelObj(const Channel *c, Context &ctx); }; /** Extends the @c ChannelElement to implement the VFO channel settings for the DM-1701. * This class is an extension of the normal @c ChannelElement that only implements the step-size * feature and encodes it where the name used to be. Thus the memory layout and size is identical * to the normal channel. */ class VFOChannelElement: public ChannelElement { protected: /** Constructor from pointer to memory. */ VFOChannelElement(uint8_t *ptr, size_t size); public: /** Constructor from pointer to memory. */ VFOChannelElement(uint8_t *ptr); /** Destructor. */ virtual ~VFOChannelElement(); QString name() const; void setName(const QString &txt); /** Returns the step-size for the VFO channel. */ virtual unsigned stepSize() const; /** Sets the step-size for the VFO channel in Hz. */ virtual void setStepSize(unsigned ss_hz); }; /** Extends the common @c TyTCodeplug::GeneralSettingsElement to implement the DM-1701 specific * settings. * * Memory layout of the settings (size 0x00b0 bytes): * @verbinclude dm1701_settings.txt */ class GeneralSettingsElement: public TyTCodeplug::GeneralSettingsElement { protected: /** Hidden constructor. */ GeneralSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ GeneralSettingsElement(uint8_t *ptr); void clear(); /** Returns @c true, if VFO A is in channel mode. */ virtual bool channelModeA() const; /** Enables/disables the channel mode for VFO A. */ virtual void enableChannelModeA(bool enable); /** Returns @c true, if VFO B is in channel mode. */ virtual bool channelModeB() const; /** Enables/disables the channel mode for VFO B. */ virtual void enableChannelModeB(bool enable); /** Returns @c true, if the radio is in channel (and not VFO) mode. */ virtual bool channelMode() const; /** Enable/disable channel mode. */ virtual void enableChannelMode(bool enable); /** Returns @c true if group-call match is enabled. */ virtual bool groupCallMatch() const; /** Enables/disables group-call match. */ virtual void enableGroupCallMatch(bool enable); /** Returns @c true if private-call match is enabled. */ virtual bool privateCallMatch() const; /** Enables/disables private-call match. */ virtual void enablePrivateCallMatch(bool enable); /** Returns the time-zone. */ virtual QTimeZone timeZone() const; /** Sets the time-zone. */ virtual void setTimeZone(const QTimeZone &zone); /** Returns the channel hang time in ms. */ virtual unsigned channelHangTime() const; /** Sets the channel hang time in ms. */ virtual void setChannelHangTime(unsigned dur); /** Encodes the general settings. */ virtual bool fromConfig(const Config *config); /** Updates config from general settings. */ virtual bool updateConfig(Config *config); }; /** Extens the common @c TyTCodeplug::ButtonSettingsElement to implement the DM-1701 specific * settings. * * Memory layout of the setting (size 0x0014 bytes): * @verbinclude dm1701_buttonsettings.txt */ class ButtonSettingsElement: public TyTCodeplug::ButtonSettingsElement { protected: /** Hidden constructor. */ ButtonSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit ButtonSettingsElement(uint8_t *ptr); void clear(); /** Returns the action for a short press on side button 3. */ virtual ButtonAction sideButton3Short() const; /** Sets the action for a short press on side button 3. */ virtual void setSideButton3Short(ButtonAction action); /** Returns the action for a long press on side button 3. */ virtual ButtonAction sideButton3Long() const; /** Sets the action for a long press on side button 3. */ virtual void setSideButton3Long(ButtonAction action); /** Returns the action for a short press on programmable button 1. */ virtual ButtonAction progButton1Short() const; /** Sets the action for a short press on programmable button 1. */ virtual void setProgButton1Short(ButtonAction action); /** Returns the action for a long press on programmable button 1. */ virtual ButtonAction progButton1Long() const; /** Sets the action for a long press on programmable button 1. */ virtual void setProgButton1Long(ButtonAction action); /** Returns the action for a short press on programmable button 2. */ virtual ButtonAction progButton2Short() const; /** Sets the action for a short press on programmable button 2. */ virtual void setProgButton2Short(ButtonAction action); /** Returns the action for a long press on programmable button 2. */ virtual ButtonAction progButton2Long() const; /** Sets the action for a long press on programmable button 2. */ virtual void setProgButton2Long(ButtonAction action); /** Encodes the button settings. */ virtual bool fromConfig(const Config *config); /** Updates config from button settings. */ virtual bool updateConfig(Config *config); }; /** Extended zone data for the DM-1701. * The zone definition @c ZoneElement contains only a single set of 16 channels. For each zone * definition, there is a zone extension which extends a zone to zwo sets of 64 channels each. * * Memory layout of encoded zone extension: * @verbinclude dm1701_zoneext.txt */ class ZoneExtElement: public Codeplug::Element { protected: /** Constructor. */ ZoneExtElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ZoneExtElement(uint8_t *ptr); /** Destructor. */ virtual ~ZoneExtElement(); void clear(); /** Returns the n-th member index of the channel list for A. */ virtual uint16_t memberIndexA(unsigned n) const; /** Sets the n-th member index of the channel list for A. */ virtual void setMemberIndexA(unsigned n, uint16_t idx); /** Returns the n-th member index of the channel list for B. */ virtual uint16_t memberIndexB(unsigned n) const; /** Returns the n-th member index of the channel list for B. */ virtual void setMemberIndexB(unsigned n, uint16_t idx); /** Encodes the given zone. */ virtual bool fromZoneObj(const Zone *zone, Context &ctx); /** Links the given zone object. * That is, extends channel list A and populates channel list B. */ virtual bool linkZoneObj(Zone *zone, Context &ctx); }; public: /** Constructor. */ explicit DM1701Codeplug(QObject *parent = nullptr); /** Destructor. */ virtual ~DM1701Codeplug(); public: void clearTimestamp(); bool encodeTimestamp(); void clearGeneralSettings(); bool encodeGeneralSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeGeneralSettings(Config *config, const ErrorStack &err=ErrorStack()); void clearChannels(); bool encodeChannels(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createChannels(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()); void clearContacts(); bool encodeContacts(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createContacts(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); void clearZones(); bool encodeZones(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createZones(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()); void clearGroupLists(); bool encodeGroupLists(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createGroupLists(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); void clearScanLists(); bool encodeScanLists(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createScanLists(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack()); void clearButtonSettings(); bool encodeButtonSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeButtonSetttings(Config *config, const ErrorStack &err=ErrorStack()); void clearPositioningSystems(); bool encodePositioningSystems(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err); bool createPositioningSystems(Config *config, Context &ctx, const ErrorStack &err); bool linkPositioningSystems(Context &ctx, const ErrorStack &err); void clearPrivacyKeys(); bool encodePrivacyKeys(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err); bool decodePrivacyKeys(Config *config, Context &ctx, const ErrorStack &err); void clearTextMessages(); bool encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err); bool decodeTextMessages(Context &ctx, const ErrorStack &err); void clearMenuSettings(); void clearEmergencySystems(); /** Resets VFO settings. */ virtual void clearVFOSettings(); protected: /** Some internal offsets within the codeplug. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int messages() { return 0x002180; } /// @endcond }; }; #endif // DM1701_CODEPLUG_HH qdmr-0.12.3/lib/dm1701_filereader.cc000066400000000000000000000041521501654372000167150ustar00rootroot00000000000000#include "dm1701_filereader.hh" #include #include #define SEGMENT0_FILE_ADDR 0x00002225 #define SEGMENT0_TARGET_ADDR 0x00002000 #define SEGMENT0_SIZE 0x0003e000 #define SEGMENT1_FILE_ADDR 0x00040235 #define SEGMENT1_TARGET_ADDR 0x00110000 #define SEGMENT1_SIZE 0x00090000 bool DM1701FileReader::read(const QString &filename, DM1701Codeplug *codeplug, const ErrorStack &err) { // Check file properties QFileInfo info(filename); if (! info.exists()) { errMsg(err) << "Cannot open file '" << filename << "': File does not exisist."; return false; } if (852533 != info.size()) { errMsg(err) << "Cannot read codeplug file '" << filename << "': File size is not 852533 bytes."; return false; } // Open file QFile file(filename); if (! file.open(QFile::ReadOnly)) { errMsg(err) << "Cannot open file '" << filename << "': " << file.errorString() << "."; return false; } // Read file content if (! file.seek(SEGMENT0_FILE_ADDR)) { errMsg(err) << "Cannot read codeplug file '" << filename << "': Cannot seek within file: " << file.errorString() << "."; file.close(); return false; } char *ptr = (char *)codeplug->data(SEGMENT0_TARGET_ADDR); size_t n = SEGMENT0_SIZE; while (0 < n) { int nread = file.read(ptr, n); if (0 > nread) { errMsg(err) << "Cannot read codeplug file '" << filename << "': " << file.errorString() << "."; file.close(); return false; } n -= nread; ptr += nread; } if (! file.seek(SEGMENT1_FILE_ADDR)) { errMsg(err) << "Cannot read codeplug file '" << filename << "': Cannot seek within file: " << file.errorString() << "."; file.close(); return false; } ptr = (char *)codeplug->data(SEGMENT1_TARGET_ADDR); n = SEGMENT1_SIZE; while (0 < n) { int nread = file.read(ptr, n); if (0 > nread) { errMsg(err) << "Cannot read codeplug file '" << filename << "': " << file.errorString() << "."; file.close(); return false; } n -= nread; ptr += nread; } return true; } qdmr-0.12.3/lib/dm1701_filereader.hh000066400000000000000000000023271501654372000167310ustar00rootroot00000000000000#ifndef DM1701_FILEREADER_HH #define DM1701_FILEREADER_HH #include "dm1701_codeplug.hh" /** Methods to read manufacturer codeplug files. * * The file format of the stock CPS is still pretty simple. The first part of the file consists of * a mal-formed DFU file. This contains a single image with a single element containing the * first section of the memory written to the device. The second section is then added as-is * to the end of the file. Due to the DFU header/footer, the file and memory offsets differ. * * * * * *
      File Start Memory Start Size
      0x002225 0x002000 0x03e000
      0x040235 0x110000 0x090000
      * * @ingroup dm1701 */ class DM1701FileReader { public: /** Reads manufacturer codeplug file into given codeplug object. * @param filename Specifies the file to read. * @param codeplug Specifies the codeplug object to store read codeplug. * @param err Error stack. * @returns @c true on success and @c false on error. */ static bool read(const QString &filename, DM1701Codeplug *codeplug, const ErrorStack &err=ErrorStack()); }; #endif // DM1701_FILEREADER_HH qdmr-0.12.3/lib/dm1701_limits.cc000066400000000000000000000216411501654372000161160ustar00rootroot00000000000000#include "dm1701_limits.hh" #include "dm1701_codeplug.hh" #include "channel.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "scanlist.hh" #include "zone.hh" #include "gpssystem.hh" #include "roamingzone.hh" DM1701Limits::DM1701Limits(QObject *parent) : RadioLimits(true, parent) { // Define limits for call-sign DB _hasCallSignDB = true; _callSignDBImplemented = true; _numCallSignDBEntries = 122197; // Define limits for satellite config _hasSatelliteConfig = false; _satelliteConfigImplemented = false; _numSatellites = 0; add("settings", new RadioLimitItem { { "introLine1", new RadioLimitString(-1, 10, RadioLimitString::Unicode) }, { "introLine2", new RadioLimitString(-1, 10, RadioLimitString::Unicode) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum { unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid), unsigned(Channel::Power::High) } }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, -1) } /// @todo check default radio ID. } ); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList{ { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, {"id", new RadioLimitUInt(0, 16777215)} } } } ); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum { (unsigned) DMRContact::PrivateCall, (unsigned) DMRContact::GroupCall, (unsigned) DMRContact::AllCall }}, { "number", new RadioLimitUInt(0, 16777215) } } }, { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() } } ); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, { "contacts", new RadioLimitGroupCallRefList(1, 32) } }) ); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, 3000, new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::Unicode)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef({ScanList::staticMetaObject})}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRefIgnored(nullptr, RadioLimitIssue::Hint)} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::Unicode)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})}, {"power", new RadioLimitEnum { unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid), unsigned(Channel::Power::High), }}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, true)}, {"aprs", new RadioLimitObjRefIgnored()}, {"roaming", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } ) ); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 1, 250, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, // 16 ASCII chars in name { "A", new RadioLimitRefList(0, 64, Channel::staticMetaObject) }, { "B", new RadioLimitRefList(0, 64, Channel::staticMetaObject) }, { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions } ) ); /* Define limits for scan lists. */ add("scanlists", new RadioLimitList( ScanList::staticMetaObject, 0, 250, new RadioLimitObject{ { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, { "primary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "secondary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "revert", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "channels", new RadioLimitRefList(0, 31, Channel::staticMetaObject) } }) ); /* Define limits for positioning systems. */ add("positioning", new RadioLimitList({ { GPSSystem::staticMetaObject, 0, 16, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "period", new RadioLimitUInt(0, 7650) }, { "contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false) }, { "revert", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } }, { APRSSystem::staticMetaObject, 0, -1, new RadioLimitIgnored() } } ) ); /* Check encryption keys. */ add("commercial", new RadioLimitItem { {"encryptionKeys", new RadioLimitList { {BasicEncryptionKey::staticMetaObject, 0, TyTCodeplug::EncryptionElement::Limit::basicKeys(), new RadioLimitObject { {"name", new RadioLimitIgnored()}, {"key", new RadioLimitStringRegEx("[0-9a-fA-F]{4}")} }}, {AESEncryptionKey::staticMetaObject, 0, TyTCodeplug::EncryptionElement::Limit::advancedKeys(), new RadioLimitObject { {"name", new RadioLimitIgnored()}, {"key", new RadioLimitStringRegEx("[0-9a-fA-F]{32}")} }} } } }); /* Ignore roaming zones. */ add("roaming", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint) ) ); } qdmr-0.12.3/lib/dm1701_limits.hh000066400000000000000000000005061501654372000161250ustar00rootroot00000000000000#ifndef DM1701LIMITS_HH #define DM1701LIMITS_HH #include "radiolimits.hh" /** Implements the configuration limits for the Baofeng DM-1701. * @ingroup dm1701 */ class DM1701Limits : public RadioLimits { Q_OBJECT public: /** Constructor. */ explicit DM1701Limits(QObject *parent=nullptr); }; #endif // UV390LIMITS_HH qdmr-0.12.3/lib/dmr6x2uv.cc000066400000000000000000000142521501654372000153210ustar00rootroot00000000000000#include "gpssystem.hh" #include "userdatabase.hh" #include "dmr6x2uv.hh" #include "dmr6x2uv_codeplug.hh" #include "dmr6x2uv_limits.hh" #include "d868uv_callsigndb.hh" #include "logger.hh" DMR6X2UV::DMR6X2UV(AnytoneInterface *device, QObject *parent) : AnytoneRadio("BTECH DMR-6X2UV", device, parent), _limits(nullptr) { _codeplug = new DMR6X2UVCodeplug(this); _codeplug->clear(); _callsigns = new D868UVCallsignDB(this); // Get device info and determine supported TX frequency bands AnytoneInterface::RadioVariant info; if (_dev) _dev->getInfo(info); switch (info.bands) { case 0x00: _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, info.version, this); break; case 0x01: _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this); break; case 0x02: _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x03: _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x04: _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, info.version, this); break; case 0x05: _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, info.version, this); break; case 0x06: _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this); break; case 0x07: _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this); break; case 0x08: _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, info.version, this); break; case 0x09: _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, info.version, this); break; case 0x0a: _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(450.)} }, info.version, this); break; case 0x0b: _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)}, {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this); break; case 0x0c: _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} }, { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} }, info.version, this); break; default: logInfo() << "Unknown band-code" << QString::number(int(info.bands), 16) << ": Do not check frequency range."; _limits = new DMR6X2UVLimits({}, {}, info.version, this); break; } } const RadioLimits & DMR6X2UV::limits() const { return *_limits; } RadioInfo DMR6X2UV::defaultRadioInfo() { return RadioInfo( RadioInfo::DMR6X2UV, "dmr6x2uv", "DMR-6X2UV", "BTECH", AnytoneInterface::interfaceInfo()); } qdmr-0.12.3/lib/dmr6x2uv.hh000066400000000000000000000020771501654372000153350ustar00rootroot00000000000000/** @defgroup dmr6x2uv BTECH DMR-6X2UV * Device specific classes for BTECH DMR-6X2UV. * * Although labeled BTECH (Baofeng USA), this device is basically a relabeled AnyTone AT-D868UV. * However, there are some minor differences in the codeplug format, hence it needs a separate * implementation. * * \image html dmr6x2uv.jpg "DMR-6X2UV" width=200px * \image latex dmr6x2uv.jpg "DMR-6X2UV" width=200px * * @ingroup anytone */ #ifndef DMR6X2UV_HH #define DMR6X2UV_HH #include "anytone_radio.hh" #include "anytone_interface.hh" /** Represents a BTECH DMR-6X2UV. * @ingroup dmr6x2uv */ class DMR6X2UV: public AnytoneRadio { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit DMR6X2UV(AnytoneInterface *device=nullptr, QObject *parent=nullptr); const RadioLimits &limits() const; /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); private: RadioLimits *_limits; }; #endif // DMR6X2UV_HH qdmr-0.12.3/lib/dmr6x2uv_codeplug.cc000066400000000000000000002732401501654372000172070ustar00rootroot00000000000000#include "gpssystem.hh" #include "roamingchannel.hh" #include "config.hh" #include "dmr6x2uv_codeplug.hh" #include "utils.hh" #include "logger.hh" /* ******************************************************************************************** * * Implementation of DMR6X2UVCodeplug::Color * ******************************************************************************************** */ AnytoneDisplaySettingsExtension::Color DMR6X2UVCodeplug::Color::decode(uint8_t code) { switch((CodedColor) code) { case Orange: return AnytoneDisplaySettingsExtension::Color::Orange; case Red: return AnytoneDisplaySettingsExtension::Color::Red; case Yellow: return AnytoneDisplaySettingsExtension::Color::Yellow; case Green: return AnytoneDisplaySettingsExtension::Color::Green; case Turquoise: return AnytoneDisplaySettingsExtension::Color::Turquoise; case Blue: return AnytoneDisplaySettingsExtension::Color::Blue; case White: return AnytoneDisplaySettingsExtension::Color::White; case Black: return AnytoneDisplaySettingsExtension::Color::Black; default: break; } return AnytoneDisplaySettingsExtension::Color::White; } uint8_t DMR6X2UVCodeplug::Color::encode(AnytoneDisplaySettingsExtension::Color color) { switch(color) { case AnytoneDisplaySettingsExtension::Color::Orange: return (uint8_t) Orange; case AnytoneDisplaySettingsExtension::Color::Red: return (uint8_t) Red; case AnytoneDisplaySettingsExtension::Color::Yellow: return (uint8_t) Yellow; case AnytoneDisplaySettingsExtension::Color::Green: return (uint8_t) Green; case AnytoneDisplaySettingsExtension::Color::Turquoise: return (uint8_t) Turquoise; case AnytoneDisplaySettingsExtension::Color::Blue: return (uint8_t) Blue; case AnytoneDisplaySettingsExtension::Color::White: return (uint8_t) White; case AnytoneDisplaySettingsExtension::Color::Black: return (uint8_t) Black; default: break; } return (uint8_t) White; } /* ******************************************************************************************** * * Implementation of DMR6X2UVCodeplug::BackgroundColor * ******************************************************************************************** */ AnytoneDisplaySettingsExtension::Color DMR6X2UVCodeplug::BackgroundColor::decode(uint8_t code) { switch((CodedColor) code) { case Black: return AnytoneDisplaySettingsExtension::Color::Black; case Blue: return AnytoneDisplaySettingsExtension::Color::Blue; default: break; } return AnytoneDisplaySettingsExtension::Color::Black; } uint8_t DMR6X2UVCodeplug::BackgroundColor::encode(AnytoneDisplaySettingsExtension::Color color) { switch(color) { case AnytoneDisplaySettingsExtension::Color::Black: return (uint8_t) Black; case AnytoneDisplaySettingsExtension::Color::Blue: return (uint8_t) Blue; default: break; } return (uint8_t) Black; } /* ******************************************************************************************** * * Implementation of DMR6X2UVCodeplug::FontColor * ******************************************************************************************** */ AnytoneDisplaySettingsExtension::Color DMR6X2UVCodeplug::FontColor::decode(uint8_t code) { switch((CodedColor) code) { case Orange: return AnytoneDisplaySettingsExtension::Color::Orange; case Red: return AnytoneDisplaySettingsExtension::Color::Red; case Yellow: return AnytoneDisplaySettingsExtension::Color::Yellow; case Green: return AnytoneDisplaySettingsExtension::Color::Green; case Turquoise: return AnytoneDisplaySettingsExtension::Color::Turquoise; case Blue: return AnytoneDisplaySettingsExtension::Color::Blue; case White: return AnytoneDisplaySettingsExtension::Color::White; case Black: return AnytoneDisplaySettingsExtension::Color::Black; default: break; } return AnytoneDisplaySettingsExtension::Color::White; } uint8_t DMR6X2UVCodeplug::FontColor::encode(AnytoneDisplaySettingsExtension::Color color) { switch(color) { case AnytoneDisplaySettingsExtension::Color::Orange: return (uint8_t) Orange; case AnytoneDisplaySettingsExtension::Color::Red: return (uint8_t) Red; case AnytoneDisplaySettingsExtension::Color::Yellow: return (uint8_t) Yellow; case AnytoneDisplaySettingsExtension::Color::Green: return (uint8_t) Green; case AnytoneDisplaySettingsExtension::Color::Turquoise: return (uint8_t) Turquoise; case AnytoneDisplaySettingsExtension::Color::Blue: return (uint8_t) Blue; case AnytoneDisplaySettingsExtension::Color::White: return (uint8_t) White; case AnytoneDisplaySettingsExtension::Color::Black: return (uint8_t) Black; default: break; } return (uint8_t) White; } /* ********************************************************************************************* * * Implementation of DMR6X2UVCodeplug::GeneralSettingsElement::KeyFunction * ********************************************************************************************* */ uint8_t DMR6X2UVCodeplug::GeneralSettingsElement::KeyFunction::encode(AnytoneKeySettingsExtension::KeyFunction func) { switch (func) { case AnytoneKeySettingsExtension::KeyFunction::Off: return (uint8_t)KeyFunction::Off; case AnytoneKeySettingsExtension::KeyFunction::Voltage: return (uint8_t)KeyFunction::Voltage; case AnytoneKeySettingsExtension::KeyFunction::Power: return (uint8_t)KeyFunction::Power; case AnytoneKeySettingsExtension::KeyFunction::Repeater: return (uint8_t)KeyFunction::Repeater; case AnytoneKeySettingsExtension::KeyFunction::Reverse: return (uint8_t)KeyFunction::Reverse; case AnytoneKeySettingsExtension::KeyFunction::Encryption: return (uint8_t)KeyFunction::Encryption; case AnytoneKeySettingsExtension::KeyFunction::Call: return (uint8_t)KeyFunction::Call; case AnytoneKeySettingsExtension::KeyFunction::VOX: return (uint8_t)KeyFunction::VOX; case AnytoneKeySettingsExtension::KeyFunction::ToggleVFO: return (uint8_t)KeyFunction::ToggleVFO; case AnytoneKeySettingsExtension::KeyFunction::SubPTT: return (uint8_t)KeyFunction::SubPTT; case AnytoneKeySettingsExtension::KeyFunction::Scan: return (uint8_t)KeyFunction::Scan; case AnytoneKeySettingsExtension::KeyFunction::WFM: return (uint8_t)KeyFunction::WFM; case AnytoneKeySettingsExtension::KeyFunction::Alarm: return (uint8_t)KeyFunction::Alarm; case AnytoneKeySettingsExtension::KeyFunction::RecordSwitch: return (uint8_t)KeyFunction::RecordSwitch; case AnytoneKeySettingsExtension::KeyFunction::Record: return (uint8_t)KeyFunction::Record; case AnytoneKeySettingsExtension::KeyFunction::SMS: return (uint8_t)KeyFunction::SMS; case AnytoneKeySettingsExtension::KeyFunction::Dial: return (uint8_t)KeyFunction::Dial; case AnytoneKeySettingsExtension::KeyFunction::GPSInformation: return (uint8_t)KeyFunction::GPSInformation; case AnytoneKeySettingsExtension::KeyFunction::Monitor: return (uint8_t)KeyFunction::Monitor; case AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel: return (uint8_t)KeyFunction::ToggleMainChannel; case AnytoneKeySettingsExtension::KeyFunction::HotKey1: return (uint8_t)KeyFunction::HotKey1; case AnytoneKeySettingsExtension::KeyFunction::HotKey2: return (uint8_t)KeyFunction::HotKey2; case AnytoneKeySettingsExtension::KeyFunction::HotKey3: return (uint8_t)KeyFunction::HotKey3; case AnytoneKeySettingsExtension::KeyFunction::HotKey4: return (uint8_t)KeyFunction::HotKey4; case AnytoneKeySettingsExtension::KeyFunction::HotKey5: return (uint8_t)KeyFunction::HotKey5; case AnytoneKeySettingsExtension::KeyFunction::HotKey6: return (uint8_t)KeyFunction::HotKey6; case AnytoneKeySettingsExtension::KeyFunction::WorkAlone: return (uint8_t)KeyFunction::WorkAlone; case AnytoneKeySettingsExtension::KeyFunction::SkipChannel: return (uint8_t)KeyFunction::SkipChannel; case AnytoneKeySettingsExtension::KeyFunction::DMRMonitor: return (uint8_t)KeyFunction::DMRMonitor; case AnytoneKeySettingsExtension::KeyFunction::SubChannel: return (uint8_t)KeyFunction::SubChannel; case AnytoneKeySettingsExtension::KeyFunction::PriorityZone: return (uint8_t)KeyFunction::PriorityZone; case AnytoneKeySettingsExtension::KeyFunction::VFOScan: return (uint8_t)KeyFunction::VFOScan; case AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality: return (uint8_t)KeyFunction::MICSoundQuality; case AnytoneKeySettingsExtension::KeyFunction::LastCallReply: return (uint8_t)KeyFunction::LastCallReply; case AnytoneKeySettingsExtension::KeyFunction::ChannelType: return (uint8_t)KeyFunction::ChannelType; case AnytoneKeySettingsExtension::KeyFunction::SimplexRepeater: return (uint8_t)KeyFunction::SimplexRepeater; case AnytoneKeySettingsExtension::KeyFunction::Ranging: return (uint8_t)KeyFunction::Ranging; case AnytoneKeySettingsExtension::KeyFunction::ChannelRanging: return (uint8_t)KeyFunction::ChannelRanging; case AnytoneKeySettingsExtension::KeyFunction::MaxVolume: return (uint8_t)KeyFunction::MaxVolume; case AnytoneKeySettingsExtension::KeyFunction::Slot: return (uint8_t)KeyFunction::Slot; case AnytoneKeySettingsExtension::KeyFunction::Squelch: return (uint8_t)KeyFunction::Squelch; case AnytoneKeySettingsExtension::KeyFunction::Roaming: return (uint8_t)KeyFunction::Roaming; case AnytoneKeySettingsExtension::KeyFunction::Zone: return (uint8_t)KeyFunction::Zone; case AnytoneKeySettingsExtension::KeyFunction::RoamingSet: return (uint8_t)KeyFunction::RoamingSet; case AnytoneKeySettingsExtension::KeyFunction::Mute: return (uint8_t)KeyFunction::Mute; case AnytoneKeySettingsExtension::KeyFunction::CtcssDcsSet: return (uint8_t)KeyFunction::CtcssDcsSet; case AnytoneKeySettingsExtension::KeyFunction::APRSSet: return (uint8_t)KeyFunction::APRSSet; case AnytoneKeySettingsExtension::KeyFunction::APRSSend: return (uint8_t)KeyFunction::APRSSend; default: return (uint8_t)KeyFunction::Off; } } AnytoneKeySettingsExtension::KeyFunction DMR6X2UVCodeplug::GeneralSettingsElement::KeyFunction::decode(uint8_t code) { switch ((KeyFunctionCode)code) { case KeyFunction::Off: return AnytoneKeySettingsExtension::KeyFunction::Off; case KeyFunction::Voltage: return AnytoneKeySettingsExtension::KeyFunction::Voltage; case KeyFunction::Power: return AnytoneKeySettingsExtension::KeyFunction::Power; case KeyFunction::Repeater: return AnytoneKeySettingsExtension::KeyFunction::Repeater; case KeyFunction::Reverse: return AnytoneKeySettingsExtension::KeyFunction::Reverse; case KeyFunction::Encryption: return AnytoneKeySettingsExtension::KeyFunction::Encryption; case KeyFunction::Call: return AnytoneKeySettingsExtension::KeyFunction::Call; case KeyFunction::VOX: return AnytoneKeySettingsExtension::KeyFunction::VOX; case KeyFunction::ToggleVFO: return AnytoneKeySettingsExtension::KeyFunction::ToggleVFO; case KeyFunction::SubPTT: return AnytoneKeySettingsExtension::KeyFunction::SubPTT; case KeyFunction::Scan: return AnytoneKeySettingsExtension::KeyFunction::Scan; case KeyFunction::WFM: return AnytoneKeySettingsExtension::KeyFunction::WFM; case KeyFunction::Alarm: return AnytoneKeySettingsExtension::KeyFunction::Alarm; case KeyFunction::RecordSwitch: return AnytoneKeySettingsExtension::KeyFunction::RecordSwitch; case KeyFunction::Record: return AnytoneKeySettingsExtension::KeyFunction::Record; case KeyFunction::SMS: return AnytoneKeySettingsExtension::KeyFunction::SMS; case KeyFunction::Dial: return AnytoneKeySettingsExtension::KeyFunction::Dial; case KeyFunction::GPSInformation: return AnytoneKeySettingsExtension::KeyFunction::GPSInformation; case KeyFunction::Monitor: return AnytoneKeySettingsExtension::KeyFunction::Monitor; case KeyFunction::ToggleMainChannel: return AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel; case KeyFunction::HotKey1: return AnytoneKeySettingsExtension::KeyFunction::HotKey1; case KeyFunction::HotKey2: return AnytoneKeySettingsExtension::KeyFunction::HotKey2; case KeyFunction::HotKey3: return AnytoneKeySettingsExtension::KeyFunction::HotKey3; case KeyFunction::HotKey4: return AnytoneKeySettingsExtension::KeyFunction::HotKey4; case KeyFunction::HotKey5: return AnytoneKeySettingsExtension::KeyFunction::HotKey5; case KeyFunction::HotKey6: return AnytoneKeySettingsExtension::KeyFunction::HotKey6; case KeyFunction::WorkAlone: return AnytoneKeySettingsExtension::KeyFunction::WorkAlone; case KeyFunction::SkipChannel: return AnytoneKeySettingsExtension::KeyFunction::SkipChannel; case KeyFunction::DMRMonitor: return AnytoneKeySettingsExtension::KeyFunction::DMRMonitor; case KeyFunction::SubChannel: return AnytoneKeySettingsExtension::KeyFunction::SubChannel; case KeyFunction::PriorityZone: return AnytoneKeySettingsExtension::KeyFunction::PriorityZone; case KeyFunction::VFOScan: return AnytoneKeySettingsExtension::KeyFunction::VFOScan; case KeyFunction::MICSoundQuality: return AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality; case KeyFunction::LastCallReply: return AnytoneKeySettingsExtension::KeyFunction::LastCallReply; case KeyFunction::ChannelType: return AnytoneKeySettingsExtension::KeyFunction::ChannelType; case KeyFunction::SimplexRepeater: return AnytoneKeySettingsExtension::KeyFunction::SimplexRepeater; case KeyFunction::Ranging: return AnytoneKeySettingsExtension::KeyFunction::Ranging; case KeyFunction::ChannelRanging: return AnytoneKeySettingsExtension::KeyFunction::ChannelRanging; case KeyFunction::MaxVolume: return AnytoneKeySettingsExtension::KeyFunction::MaxVolume; case KeyFunction::Slot: return AnytoneKeySettingsExtension::KeyFunction::Slot; case KeyFunction::Squelch: return AnytoneKeySettingsExtension::KeyFunction::Squelch; case KeyFunction::Roaming: return AnytoneKeySettingsExtension::KeyFunction::Roaming; case KeyFunction::Zone: return AnytoneKeySettingsExtension::KeyFunction::Zone; case KeyFunction::RoamingSet: return AnytoneKeySettingsExtension::KeyFunction::RoamingSet; case KeyFunction::Mute: return AnytoneKeySettingsExtension::KeyFunction::Mute; case KeyFunction::CtcssDcsSet: return AnytoneKeySettingsExtension::KeyFunction::CtcssDcsSet; case KeyFunction::APRSSet: return AnytoneKeySettingsExtension::KeyFunction::APRSSet; case KeyFunction::APRSSend: return AnytoneKeySettingsExtension::KeyFunction::APRSSend; default: return AnytoneKeySettingsExtension::KeyFunction::Off; } } /* ********************************************************************************************* * * Implementation of DMR6X2UVCodeplug::GeneralSettingsElement * ********************************************************************************************* */ DMR6X2UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size) : D868UVCodeplug::GeneralSettingsElement(ptr, size) { // pass... } DMR6X2UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr) : D868UVCodeplug::GeneralSettingsElement(ptr, GeneralSettingsElement::size()) { // pass... } bool DMR6X2UVCodeplug::GeneralSettingsElement::idleChannelTone() const { return getUInt8(Offset::idleChannelTone()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableIdleChannelTone(bool enable) { return setUInt8(Offset::idleChannelTone(), (enable ? 0x01 : 0x00)); } unsigned DMR6X2UVCodeplug::GeneralSettingsElement::transmitTimeout() const { return ((unsigned)getUInt8(Offset::transmitTimeout()))*30; } void DMR6X2UVCodeplug::GeneralSettingsElement::setTransmitTimeout(unsigned tot) { setUInt8(Offset::transmitTimeout(), tot/30); } AnytoneDisplaySettingsExtension::Language DMR6X2UVCodeplug::GeneralSettingsElement::language() const { return (AnytoneDisplaySettingsExtension::Language)getUInt8(Offset::language()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setLanguage(AnytoneDisplaySettingsExtension::Language lang) { setUInt8(Offset::language(), (unsigned)lang); } Frequency DMR6X2UVCodeplug::GeneralSettingsElement::vfoFrequencyStep() const { switch (getUInt8(Offset::vfoFrequencyStep())) { case FREQ_STEP_2_5kHz: return Frequency::fromkHz(2.5); case FREQ_STEP_5kHz: return Frequency::fromkHz(5); case FREQ_STEP_6_25kHz: return Frequency::fromkHz(6.25); case FREQ_STEP_10kHz: return Frequency::fromkHz(10); case FREQ_STEP_12_5kHz: return Frequency::fromkHz(12.5); case FREQ_STEP_20kHz: return Frequency::fromkHz(20); case FREQ_STEP_25kHz: return Frequency::fromkHz(25); case FREQ_STEP_50kHz: return Frequency::fromkHz(50); } return Frequency::fromkHz(2.5); } void DMR6X2UVCodeplug::GeneralSettingsElement::setVFOFrequencyStep(Frequency freq) { if (freq.inkHz() <= 2.5) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_2_5kHz); else if (freq.inkHz() <= 5) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_5kHz); else if (freq.inkHz() <= 6.25) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_6_25kHz); else if (freq.inkHz() <= 10) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_10kHz); else if (freq.inkHz() <= 12.5) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_12_5kHz); else if (freq.inkHz() <= 20) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_20kHz); else if (freq.inkHz() <= 25) setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_25kHz); else setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_50kHz); } AnytoneKeySettingsExtension::KeyFunction DMR6X2UVCodeplug::GeneralSettingsElement::funcKeyAShort() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyAShort())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyAShort(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction DMR6X2UVCodeplug::GeneralSettingsElement::funcKeyBShort() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyBShort())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyBShort(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction DMR6X2UVCodeplug::GeneralSettingsElement::funcKeyCShort() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyCShort())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyCShort(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction DMR6X2UVCodeplug::GeneralSettingsElement::funcKey1Short() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey1Short())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKey1Short(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction DMR6X2UVCodeplug::GeneralSettingsElement::funcKey2Short() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey2Short())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKey2Short(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction DMR6X2UVCodeplug::GeneralSettingsElement::funcKeyALong() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyALong())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyALong(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction DMR6X2UVCodeplug::GeneralSettingsElement::funcKeyBLong() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyBLong())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyBLong(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction DMR6X2UVCodeplug::GeneralSettingsElement::funcKeyCLong() const { return KeyFunction::decode(getUInt8(Offset::progFuncKeyCLong())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKeyCLong(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction DMR6X2UVCodeplug::GeneralSettingsElement::funcKey1Long() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey1Long())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKey1Long(), KeyFunction::encode(func)); } AnytoneKeySettingsExtension::KeyFunction DMR6X2UVCodeplug::GeneralSettingsElement::funcKey2Long() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey2Long())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) { setUInt8(Offset::progFuncKey2Long(), KeyFunction::encode(func)); } bool DMR6X2UVCodeplug::GeneralSettingsElement::vfoModeA() const { return getUInt8(Offset::vfoModeA()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableVFOModeA(bool enable) { setUInt8(Offset::vfoModeA(), (enable ? 0x01 : 0x00)); } bool DMR6X2UVCodeplug::GeneralSettingsElement::vfoModeB() const { return getUInt8(Offset::vfoModeB()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableVFOModeB(bool enable) { setUInt8(Offset::vfoModeB(), (enable ? 0x01 : 0x00)); } AnytoneSettingsExtension::STEType DMR6X2UVCodeplug::GeneralSettingsElement::steType() const { return (AnytoneSettingsExtension::STEType)getUInt8(Offset::steType()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setSTEType(AnytoneSettingsExtension::STEType type) { setUInt8(Offset::steType(), (unsigned)type); } double DMR6X2UVCodeplug::GeneralSettingsElement::steFrequency() const { switch ((STEFrequency)getUInt8(Offset::steFrequency())) { case STEFrequency::Off: return 0; case STEFrequency::Hz55_2: return 55.2; case STEFrequency::Hz259_2: return 259.2; } return 0; } void DMR6X2UVCodeplug::GeneralSettingsElement::setSTEFrequency(double freq) { if (0 >= freq) { setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Off); } else if (100 > freq) { setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz55_2); } else { setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz259_2); } } Interval DMR6X2UVCodeplug::GeneralSettingsElement::groupCallHangTime() const { return Interval::fromSeconds(getUInt8(Offset::groupCallHangTime())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setGroupCallHangTime(Interval intv) { setUInt8(Offset::groupCallHangTime(), intv.seconds()); } Interval DMR6X2UVCodeplug::GeneralSettingsElement::privateCallHangTime() const { return Interval::fromSeconds(getUInt8(Offset::privateCallHangTime())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setPrivateCallHangTime(Interval intv) { setUInt8(Offset::privateCallHangTime(), intv.seconds()); } Interval DMR6X2UVCodeplug::GeneralSettingsElement::preWaveDelay() const { return Interval::fromMilliseconds((unsigned)getUInt8(Offset::preWaveDelay())*20); } void DMR6X2UVCodeplug::GeneralSettingsElement::setPreWaveDelay(Interval intv) { setUInt8(Offset::preWaveDelay(), intv.milliseconds()/20); } Interval DMR6X2UVCodeplug::GeneralSettingsElement::wakeHeadPeriod() const { return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::wakeHeadPeriod()))*20); } void DMR6X2UVCodeplug::GeneralSettingsElement::setWakeHeadPeriod(Interval intv) { setUInt8(Offset::wakeHeadPeriod(), intv.milliseconds()/20); } unsigned DMR6X2UVCodeplug::GeneralSettingsElement::wfmChannelIndex() const { return getUInt8(Offset::wfmChannelIndex()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setWFMChannelIndex(unsigned idx) { setUInt8(Offset::wfmChannelIndex(), idx); } bool DMR6X2UVCodeplug::GeneralSettingsElement::wfmVFOEnabled() const { return getUInt8(Offset::wfmVFOEnabled()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableWFMVFO(bool enable) { setUInt8(Offset::wfmVFOEnabled(), (enable ? 0x01 : 0x00)); } unsigned DMR6X2UVCodeplug::GeneralSettingsElement::dtmfToneDuration() const { switch (getUInt8(Offset::dtmfToneDuration())) { case DTMF_DUR_50ms: return 50; case DTMF_DUR_100ms: return 100; case DTMF_DUR_200ms: return 200; case DTMF_DUR_300ms: return 300; case DTMF_DUR_500ms: return 500; } return 50; } void DMR6X2UVCodeplug::GeneralSettingsElement::setDTMFToneDuration(unsigned ms) { if (ms<=50) { setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_50ms); } else if (ms<=100) { setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_100ms); } else if (ms<=200) { setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_200ms); } else if (ms<=300) { setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_300ms); } else { setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_500ms); } } bool DMR6X2UVCodeplug::GeneralSettingsElement::manDown() const { return getUInt8(Offset::manDown()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableManDown(bool enable) { setUInt8(Offset::manDown(), (enable ? 0x01 : 0x00)); } bool DMR6X2UVCodeplug::GeneralSettingsElement::wfmMonitor() const { return getUInt8(Offset::wfmMonitor()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableWFMMonitor(bool enable) { setUInt8(Offset::wfmMonitor(), (enable ? 0x01 : 0x00)); } Frequency DMR6X2UVCodeplug::GeneralSettingsElement::tbstFrequency() const { switch ((TBSTFrequency)getUInt8(Offset::tbstFrequency())) { case TBSTFrequency::Hz1000: return Frequency::fromHz(1000); case TBSTFrequency::Hz1450: return Frequency::fromHz(1450); case TBSTFrequency::Hz1750: return Frequency::fromHz(1750); case TBSTFrequency::Hz2100: return Frequency::fromHz(2100); } return Frequency::fromHz(1750); } void DMR6X2UVCodeplug::GeneralSettingsElement::setTBSTFrequency(Frequency freq) { if (1000 == freq.inHz()) { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1000); } else if (1450 == freq.inHz()) { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1450); } else if (1750 == freq.inHz()) { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750); } else if (2100 == freq.inHz()) { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz2100); } else { setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750); } } bool DMR6X2UVCodeplug::GeneralSettingsElement::proMode() const { return getUInt8(Offset::proMode()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableProMode(bool enable) { setUInt8(Offset::proMode(), (enable ? 0x01 : 0x00)); } bool DMR6X2UVCodeplug::GeneralSettingsElement::filterOwnID() const { return getUInt8(Offset::filterOwnID()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableFilterOwnID(bool enable) { setUInt8(Offset::filterOwnID(), (enable ? 0x01 : 0x00)); } bool DMR6X2UVCodeplug::GeneralSettingsElement::keyToneEnabled() const { return 0x00 != getUInt8(Offset::enableKeyTone()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableKeyTone(bool enable) { setUInt8(Offset::enableKeyTone(), enable ? 0x01 : 0x00); } bool DMR6X2UVCodeplug::GeneralSettingsElement::remoteStunKill() const { return getUInt8(Offset::remoteStunKill()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableRemoteStunKill(bool enable) { setUInt8(Offset::remoteStunKill(), (enable ? 0x01 : 0x00)); } bool DMR6X2UVCodeplug::GeneralSettingsElement::remoteMonitor() const { return getUInt8(Offset::remoteMonitor()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableRemoteMonitor(bool enable) { setUInt8(Offset::remoteMonitor(), (enable ? 0x01 : 0x00)); } bool DMR6X2UVCodeplug::GeneralSettingsElement::selectTXContactEnabled() const { return 0x01 == getUInt8(Offset::selectTXContact()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableSelectTXContact(bool enable) { setUInt8(Offset::selectTXContact(), enable ? 0x01 : 0x00); } AnytoneDMRSettingsExtension::SlotMatch DMR6X2UVCodeplug::GeneralSettingsElement::monitorSlotMatch() const { return (AnytoneDMRSettingsExtension::SlotMatch)getUInt8(Offset::monSlotMatch()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setMonitorSlotMatch(AnytoneDMRSettingsExtension::SlotMatch match) { setUInt8(Offset::monSlotMatch(), (unsigned)match); } bool DMR6X2UVCodeplug::GeneralSettingsElement::monitorColorCodeMatch() const { return getUInt8(Offset::monColorCodeMatch()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableMonitorColorCodeMatch(bool enable) { setUInt8(Offset::monColorCodeMatch(), (enable ? 0x01 : 0x00)); } bool DMR6X2UVCodeplug::GeneralSettingsElement::monitorIDMatch() const { return getUInt8(Offset::monIDMatch()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableMonitorIDMatch(bool enable) { setUInt8(Offset::monIDMatch(), (enable ? 0x01 : 0x00)); } bool DMR6X2UVCodeplug::GeneralSettingsElement::monitorTimeSlotHold() const { return getUInt8(Offset::monTimeSlotHold()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableMonitorTimeSlotHold(bool enable) { setUInt8(Offset::monTimeSlotHold(), (enable ? 0x01 : 0x00)); } Interval DMR6X2UVCodeplug::GeneralSettingsElement::manDownDelay() const { return Interval::fromSeconds(getUInt8(Offset::manDownDelay())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setManDownDelay(Interval sec) { setUInt8(Offset::manDownDelay(), sec.seconds()); } unsigned DMR6X2UVCodeplug::GeneralSettingsElement::fmCallHold() const { return getUInt8(Offset::fmCallHold()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setFMCallHold(unsigned sec) { setUInt8(Offset::fmCallHold(), sec); } bool DMR6X2UVCodeplug::GeneralSettingsElement::gpsMessageEnabled() const { return getUInt8(Offset::enableGPSMessage()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableGPSMessage(bool enable) { setUInt8(Offset::enableGPSMessage(), (enable ? 0x01 : 0x00)); } bool DMR6X2UVCodeplug::GeneralSettingsElement::maintainCallChannel() const { return getUInt8(Offset::maintainCallChannel()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableMaintainCallChannel(bool enable) { setUInt8(Offset::maintainCallChannel(), (enable ? 0x01 : 0x00)); } unsigned DMR6X2UVCodeplug::GeneralSettingsElement::priorityZoneAIndex() const { return getUInt8(Offset::priorityZoneA()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setPriorityZoneAIndex(unsigned idx) { setUInt8(Offset::priorityZoneA(), idx); } unsigned DMR6X2UVCodeplug::GeneralSettingsElement::priorityZoneBIndex() const { return getUInt8(Offset::priorityZoneB()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setPriorityZoneBIndex(unsigned idx) { setUInt8(Offset::priorityZoneB(), idx); } bool DMR6X2UVCodeplug::GeneralSettingsElement::smsConfirmEnabled() const { return 0x01 == getUInt8(Offset::smsConfirm()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableSMSConfirm(bool enable) { setUInt8(Offset::smsConfirm(), enable ? 0x01 : 0x00); } bool DMR6X2UVCodeplug::GeneralSettingsElement::simplexRepeaterEnabled() const { return 0x01 == getUInt8(Offset::simplexRepEnable()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableSimplexRepeater(bool enable) { setUInt8(Offset::simplexRepEnable(), enable ? 0x01 : 0x00); } Interval DMR6X2UVCodeplug::GeneralSettingsElement::gpsUpdatePeriod() const { return Interval::fromSeconds(getUInt8(Offset::gpsUpdatePeriod())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setGPSUpdatePeriod(Interval intv) { setUInt8(Offset::gpsUpdatePeriod(), intv.seconds()); } bool DMR6X2UVCodeplug::GeneralSettingsElement::monitorSimplexRepeaterEnabled() const { return 0x01 == getUInt8(Offset::simplxRepSpeaker()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableMonitorSimplexRepeater(bool enable) { setUInt8(Offset::simplxRepSpeaker(), enable ? 0x01 : 0x00); } bool DMR6X2UVCodeplug::GeneralSettingsElement::showCurrentContact() const { return getUInt8(Offset::showContact()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableShowCurrentContact(bool enable) { setUInt8(Offset::showContact(), (enable ? 0x01 : 0x00)); } bool DMR6X2UVCodeplug::GeneralSettingsElement::keyToneLevelAdjustable() const { return 0 == keyToneLevel(); } unsigned DMR6X2UVCodeplug::GeneralSettingsElement::keyToneLevel() const { return ((unsigned)getUInt8(Offset::keyToneLevel()))*10/15; } void DMR6X2UVCodeplug::GeneralSettingsElement::setKeyToneLevel(unsigned level) { setUInt8(Offset::keyToneLevel(), level*10/15); } void DMR6X2UVCodeplug::GeneralSettingsElement::setKeyToneLevelAdjustable() { setUInt8(Offset::keyToneLevel(), 0); } bool DMR6X2UVCodeplug::GeneralSettingsElement::knobLock() const { return getBit(Offset::knobLock()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableKnobLock(bool enable) { setBit(Offset::knobLock(), enable); } bool DMR6X2UVCodeplug::GeneralSettingsElement::keypadLock() const { return getBit(Offset::keypadLock()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableKeypadLock(bool enable) { setBit(Offset::keypadLock(), enable); } bool DMR6X2UVCodeplug::GeneralSettingsElement::sidekeysLock() const { return getBit(Offset::sideKeyLock()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableSidekeysLock(bool enable) { setBit(Offset::sideKeyLock(), enable); } bool DMR6X2UVCodeplug::GeneralSettingsElement::keyLockForced() const { return getBit(Offset::forceKeyLock()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableKeyLockForced(bool enable) { setBit(Offset::forceKeyLock(), enable); } AnytoneSimplexRepeaterSettingsExtension::TimeSlot DMR6X2UVCodeplug::GeneralSettingsElement::simplexRepeaterTimeslot() const { return (AnytoneSimplexRepeaterSettingsExtension::TimeSlot)getUInt8(Offset::simplxRepSlot()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setSimplexRepeaterTimeslot(AnytoneSimplexRepeaterSettingsExtension::TimeSlot slot) { setUInt8(Offset::simplxRepSlot(), (unsigned int) slot); } bool DMR6X2UVCodeplug::GeneralSettingsElement::showLastHeard() const { return getUInt8(Offset::showLastHeard()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableShowLastHeard(bool enable) { setUInt8(Offset::showLastHeard(), (enable ? 0x01 : 0x00)); } AnytoneDMRSettingsExtension::SMSFormat DMR6X2UVCodeplug::GeneralSettingsElement::smsFormat() const { return (AnytoneDMRSettingsExtension::SMSFormat) getUInt8(Offset::smsFormat()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setSMSFormat(AnytoneDMRSettingsExtension::SMSFormat format) { setUInt8(Offset::smsFormat(), (unsigned int)format); } bool DMR6X2UVCodeplug::GeneralSettingsElement::gpsUnitsImperial() const { return getUInt8(Offset::gpsUnits()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableGPSUnitsImperial(bool enable) { setUInt8(Offset::gpsUnits(), (enable ? 0x01 : 0x00)); } Frequency DMR6X2UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyVHF() const { return Frequency::fromHz(getUInt32_le(Offset::autoRepMinVHF())*10); } void DMR6X2UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyVHF(Frequency freq) { setUInt32_le(Offset::autoRepMinVHF(), freq.inHz()/10); } Frequency DMR6X2UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyVHF() const { return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxVHF())*10); } void DMR6X2UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyVHF(Frequency freq) { setUInt32_le(Offset::autoRepMaxVHF(), freq.inHz()/10); } Frequency DMR6X2UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyUHF() const { return Frequency::fromHz(getUInt32_le(Offset::autoRepMinUHF())*10); } void DMR6X2UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyUHF(Frequency freq) { setUInt32_le(Offset::autoRepMinUHF(), freq.inHz()/10); } Frequency DMR6X2UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyUHF() const { return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxUHF())*10); } void DMR6X2UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyUHF(Frequency freq) { setUInt32_le(Offset::autoRepMaxUHF(), freq.inHz()/10); } AnytoneAutoRepeaterSettingsExtension::Direction DMR6X2UVCodeplug::GeneralSettingsElement::autoRepeaterDirectionB() const { return (AnytoneAutoRepeaterSettingsExtension::Direction)getUInt8(Offset::autoRepeaterDirB()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir) { setUInt8(Offset::autoRepeaterDirB(), (uint8_t)dir); } bool DMR6X2UVCodeplug::GeneralSettingsElement::fmSendIDAndContact() const { return 0 != getUInt8(Offset::fmSendIDAndContact()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableFMSendIDAndContact(bool enable) { setUInt8(Offset::fmSendIDAndContact(), enable ? 0x01 : 0x00); } bool DMR6X2UVCodeplug::GeneralSettingsElement::defaultChannel() const { return 0x01 == getUInt8(Offset::defaultChannels()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableDefaultChannel(bool enable) { setUInt8(Offset::defaultChannels(), enable ? 0x01 : 0x00); } unsigned int DMR6X2UVCodeplug::GeneralSettingsElement::defaultZoneIndexA() const { return getUInt8(Offset::defaultZoneA()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexA(unsigned int index) { setUInt8(Offset::defaultZoneA(), index); } bool DMR6X2UVCodeplug::GeneralSettingsElement::defaultChannelAIsVFO() const { return 0xff == defaultChannelAIndex(); } unsigned int DMR6X2UVCodeplug::GeneralSettingsElement::defaultChannelAIndex() const { return getUInt8(Offset::defaultChannelA()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setDefaultChannelAIndex(unsigned int index) { return setUInt8(Offset::defaultChannelA(), index); } void DMR6X2UVCodeplug::GeneralSettingsElement::setDefaultChannelAToVFO() { setDefaultChannelAIndex(0xff); } unsigned int DMR6X2UVCodeplug::GeneralSettingsElement::defaultZoneIndexB() const { return getUInt8(Offset::defaultZoneB()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexB(unsigned int index) { setUInt8(Offset::defaultZoneB(), index); } bool DMR6X2UVCodeplug::GeneralSettingsElement::defaultChannelBIsVFO() const { return 0xff == defaultChannelBIndex(); } unsigned int DMR6X2UVCodeplug::GeneralSettingsElement::defaultChannelBIndex() const { return getUInt8(Offset::defaultChannelB()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setDefaultChannelBIndex(unsigned int index) { return setUInt8(Offset::defaultChannelB(), index); } void DMR6X2UVCodeplug::GeneralSettingsElement::setDefaultChannelBToVFO() { setDefaultChannelBIndex(0xff); } bool DMR6X2UVCodeplug::GeneralSettingsElement::keepLastCaller() const { return getUInt8(Offset::keepLastCaller()); } void DMR6X2UVCodeplug::GeneralSettingsElement::enableKeepLastCaller(bool enable) { setUInt8(Offset::keepLastCaller(), (enable ? 0x01 : 0x00)); } Interval DMR6X2UVCodeplug::GeneralSettingsElement::rxBacklightDuration() const { return Interval::fromSeconds(getUInt8(Offset::rxBacklightDuration())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setRXBacklightDuration(Interval dur) { unsigned int secs = std::max(30ULL, dur.seconds()); setUInt8(Offset::rxBacklightDuration(), secs); } AnytoneDisplaySettingsExtension::Color DMR6X2UVCodeplug::GeneralSettingsElement::standbyBackgroundColor() const { return BackgroundColor::decode(getUInt8(Offset::standbyBackground())); } void DMR6X2UVCodeplug::GeneralSettingsElement::setStandbyBackgroundColor(AnytoneDisplaySettingsExtension::Color color) { setUInt8(Offset::standbyBackground(), BackgroundColor::encode(color)); } unsigned int DMR6X2UVCodeplug::GeneralSettingsElement::manualDialedGroupCallHangTime() const { return getUInt8(Offset::manGrpCallHangTime()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setManualDialedGroupCallHangTime(unsigned int dur) { setUInt8(Offset::manGrpCallHangTime(), dur); } unsigned int DMR6X2UVCodeplug::GeneralSettingsElement::manualDialedPrivateCallHangTime() const { return getUInt8(Offset::manPrvCallHangTime()); } void DMR6X2UVCodeplug::GeneralSettingsElement::setManualDialedPrivateCallHangTime(unsigned int dur) { setUInt8(Offset::manPrvCallHangTime(), dur); } bool DMR6X2UVCodeplug::GeneralSettingsElement::fromConfig(const Flags &flags, Context &ctx) { if (! D868UVCodeplug::GeneralSettingsElement::fromConfig(flags, ctx)) return false; // apply DMR-6X2UV specific settings. AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); if (nullptr == ext) return true; // Encode boot settings if (ext->bootSettings()->priorityZoneA()->isNull()) setPriorityZoneAIndex(0xff); else setPriorityZoneAIndex(ctx.index(ext->bootSettings()->priorityZoneA()->as())); if (ext->bootSettings()->priorityZoneB()->isNull()) setPriorityZoneBIndex(0xff); else setPriorityZoneBIndex(ctx.index(ext->bootSettings()->priorityZoneB()->as())); // Encode key settings enableKnobLock(ext->keySettings()->knobLockEnabled()); enableKeypadLock(ext->keySettings()->keypadLockEnabled()); enableSidekeysLock(ext->keySettings()->sideKeysLockEnabled()); enableKeyLockForced(ext->keySettings()->forcedKeyLockEnabled()); // Encode tone settings setKeyToneLevel(ext->toneSettings()->keyToneLevel()); // Encode display settings setCallDisplayColor(ext->displaySettings()->callColor()); setStandbyBackgroundColor(ext->displaySettings()->standbyBackgroundColor()); setLanguage(ext->displaySettings()->language()); enableShowCurrentContact(ext->displaySettings()->showContact()); enableShowLastHeard(ext->displaySettings()->showLastHeardEnabled()); setRXBacklightDuration(ext->displaySettings()->backlightDurationRX()); // Encode auto-repeater settings setAutoRepeaterDirectionB(ext->autoRepeaterSettings()->directionB()); setAutoRepeaterMinFrequencyVHF(ext->autoRepeaterSettings()->vhfMin()); setAutoRepeaterMaxFrequencyVHF(ext->autoRepeaterSettings()->vhfMax()); setAutoRepeaterMinFrequencyUHF(ext->autoRepeaterSettings()->uhfMin()); setAutoRepeaterMaxFrequencyUHF(ext->autoRepeaterSettings()->uhfMax()); // Encode DMR settings setGroupCallHangTime(ext->dmrSettings()->groupCallHangTime()); setPrivateCallHangTime(ext->dmrSettings()->privateCallHangTime()); setPreWaveDelay(ext->dmrSettings()->preWaveDelay()); setWakeHeadPeriod(ext->dmrSettings()->wakeHeadPeriod()); enableFilterOwnID(ext->dmrSettings()->filterOwnIDEnabled()); setMonitorSlotMatch(ext->dmrSettings()->monitorSlotMatch()); enableMonitorColorCodeMatch(ext->dmrSettings()->monitorColorCodeMatchEnabled()); enableMonitorIDMatch(ext->dmrSettings()->monitorIDMatchEnabled()); enableMonitorTimeSlotHold(ext->dmrSettings()->monitorTimeSlotHoldEnabled()); setSMSFormat(ext->dmrSettings()->smsFormat()); // Encode GPS settings. enableGPSUnitsImperial(AnytoneGPSSettingsExtension::Units::Archaic == ext->gpsSettings()->units()); setGPSTimeZone(ext->gpsSettings()->timeZone()); enableGPSMessage(ext->gpsSettings()->positionReportingEnabled()); setGPSUpdatePeriod(ext->gpsSettings()->updatePeriod()); // Encode other settings enableKeepLastCaller(ext->keepLastCallerEnabled()); setVFOFrequencyStep(ext->vfoStep()); setSTEType(ext->steType()); setSTEFrequency(ext->steFrequency()); setTBSTFrequency(ext->tbstFrequency()); enableProMode(ext->proModeEnabled()); enableMaintainCallChannel(ext->maintainCallChannelEnabled()); // Apply simplex repeater settings enableSimplexRepeater(ext->simplexRepeaterSettings()->enabled()); enableMonitorSimplexRepeater(ext->simplexRepeaterSettings()->monitorEnabled()); setSimplexRepeaterTimeslot(ext->simplexRepeaterSettings()->timeSlot()); return true; } bool DMR6X2UVCodeplug::GeneralSettingsElement::updateConfig(Context &ctx) { if (! D868UVCodeplug::GeneralSettingsElement::updateConfig(ctx)) return false; // Get or add settings extension AnytoneSettingsExtension *ext = nullptr; if (ctx.config()->settings()->anytoneExtension()) { ext = ctx.config()->settings()->anytoneExtension(); } else { ext = new AnytoneSettingsExtension(); ctx.config()->settings()->setAnytoneExtension(ext); } // Decode key settings ext->keySettings()->enableKnobLock(this->knobLock()); ext->keySettings()->enableKeypadLock(this->keypadLock()); ext->keySettings()->enableSideKeysLock(this->sidekeysLock()); ext->keySettings()->enableForcedKeyLock(this->keyLockForced()); // Decode tone settings ext->toneSettings()->setKeyToneLevel(keyToneLevel()); // Decode display settings ext->displaySettings()->setCallColor(this->callDisplayColor()); ext->displaySettings()->setLanguage(this->language()); ext->displaySettings()->enableShowContact(this->showCurrentContact()); ext->displaySettings()->enableShowLastHeard(this->showLastHeard()); ext->displaySettings()->setBacklightDurationRX(this->rxBacklightDuration()); ext->displaySettings()->setStandbyBackgroundColor(this->standbyBackgroundColor()); // Decode auto-repeater settings ext->autoRepeaterSettings()->setDirectionB(autoRepeaterDirectionB()); ext->autoRepeaterSettings()->setVHFMin(this->autoRepeaterMinFrequencyVHF()); ext->autoRepeaterSettings()->setVHFMax(this->autoRepeaterMaxFrequencyVHF()); ext->autoRepeaterSettings()->setUHFMin(this->autoRepeaterMinFrequencyUHF()); ext->autoRepeaterSettings()->setUHFMax(this->autoRepeaterMaxFrequencyUHF()); // Encode dmr settings ext->dmrSettings()->setGroupCallHangTime(this->groupCallHangTime()); ext->dmrSettings()->setPrivateCallHangTime(this->privateCallHangTime()); ext->dmrSettings()->setPreWaveDelay(this->preWaveDelay()); ext->dmrSettings()->setWakeHeadPeriod(this->wakeHeadPeriod()); ext->dmrSettings()->enableFilterOwnID(this->filterOwnID()); ext->dmrSettings()->setMonitorSlotMatch(this->monitorSlotMatch()); ext->dmrSettings()->enableMonitorColorCodeMatch(this->monitorColorCodeMatch()); ext->dmrSettings()->enableMonitorIDMatch(this->monitorIDMatch()); ext->dmrSettings()->enableMonitorTimeSlotHold(this->monitorTimeSlotHold()); ext->dmrSettings()->setSMSFormat(this->smsFormat()); // Encode GPS settings ext->gpsSettings()->setUnits(this->gpsUnitsImperial() ? AnytoneGPSSettingsExtension::Units::Archaic : AnytoneGPSSettingsExtension::Units::Metric); ext->gpsSettings()->setTimeZone(this->gpsTimeZone()); ext->gpsSettings()->enablePositionReporting(this->gpsMessageEnabled()); ext->gpsSettings()->setUpdatePeriod(this->gpsUpdatePeriod()); // Decode other settings ext->enableKeepLastCaller(this->keepLastCaller()); ext->setVFOStep(this->vfoFrequencyStep()); ext->setSTEType(this->steType()); ext->setSTEFrequency(this->steFrequency()); ext->setTBSTFrequency(this->tbstFrequency()); ext->enableProMode(this->proMode()); ext->enableMaintainCallChannel(this->maintainCallChannel()); // Decode simplex-repeater feature. ext->simplexRepeaterSettings()->enable(this->simplexRepeaterEnabled()); ext->simplexRepeaterSettings()->enableMonitor(this->monitorSimplexRepeaterEnabled()); ext->simplexRepeaterSettings()->setTimeSlot(this->simplexRepeaterTimeslot()); return true; } /* ********************************************************************************************* * * Implementation of DMR6X2UVCodeplug::ExtendedSettingsElement * ********************************************************************************************* */ DMR6X2UVCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr, unsigned size) : AnytoneCodeplug::ExtendedSettingsElement(ptr, size) { // pass... } DMR6X2UVCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr) : AnytoneCodeplug::ExtendedSettingsElement(ptr, ExtendedSettingsElement::size()) { // pass... } void DMR6X2UVCodeplug::ExtendedSettingsElement::clear() { Codeplug::Element::clear(); } bool DMR6X2UVCodeplug::ExtendedSettingsElement::sendTalkerAlias() const { return 0x01 == getUInt8(Offset::sendTalkerAlias()); } void DMR6X2UVCodeplug::ExtendedSettingsElement::enableSendTalkerAlias(bool enable) { setUInt8(Offset::sendTalkerAlias(), enable ? 0x01 : 0x00); } AnytoneDMRSettingsExtension::TalkerAliasSource DMR6X2UVCodeplug::ExtendedSettingsElement::talkerAliasSource() const { return (AnytoneDMRSettingsExtension::TalkerAliasSource) getUInt8(Offset::talkerAliasDisplay()); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setTalkerAliasSource(AnytoneDMRSettingsExtension::TalkerAliasSource source) { setUInt8(Offset::talkerAliasDisplay(), (uint8_t)source); } AnytoneDMRSettingsExtension::TalkerAliasEncoding DMR6X2UVCodeplug::ExtendedSettingsElement::talkerAliasEncoding() const { return (AnytoneDMRSettingsExtension::TalkerAliasEncoding) getUInt8(Offset::talkerAliasEncoding()); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setTalkerAliasEncoding(AnytoneDMRSettingsExtension::TalkerAliasEncoding encoding) { setUInt8(Offset::talkerAliasEncoding(), (uint8_t)encoding); } AnytoneDisplaySettingsExtension::Color DMR6X2UVCodeplug::ExtendedSettingsElement::fontColor() const { return FontColor::decode(getUInt8(Offset::fontColor())); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setFontColor(AnytoneDisplaySettingsExtension::Color color) { setUInt8(Offset::fontColor(), FontColor::encode(color)); } bool DMR6X2UVCodeplug::ExtendedSettingsElement::customChannelBackgroundEnabled() const { return 0x01 == getUInt8(Offset::customChannelBackground()); } void DMR6X2UVCodeplug::ExtendedSettingsElement::enableCustomChannelBackground(bool enable) { setUInt8(Offset::customChannelBackground(), enable ? 0x01 : 0x00); } unsigned int DMR6X2UVCodeplug::ExtendedSettingsElement::defaultRoamingZoneIndex() const { return getUInt8(Offset::defaultRoamingZone()); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setDefaultRoamingZoneIndex(unsigned int index) { setUInt8(Offset::defaultRoamingZone(), index); } bool DMR6X2UVCodeplug::ExtendedSettingsElement::autoRoamingEnabled() const { return 0x01 == getUInt8(Offset::roaming()); } void DMR6X2UVCodeplug::ExtendedSettingsElement::enableAutoRoaming(bool enable) { setUInt8(Offset::roaming(), enable ? 0x01 : 0x00); } bool DMR6X2UVCodeplug::ExtendedSettingsElement::repeaterRangeCheckEnabled() const { return 0x01 == getUInt8(Offset::repRangeCheck()); } void DMR6X2UVCodeplug::ExtendedSettingsElement::enableRepeaterRangeCheck(bool enable) { setUInt8(Offset::repRangeCheck(), enable ? 0x01 : 0x00); } AnytoneRoamingSettingsExtension::OutOfRangeAlert DMR6X2UVCodeplug::ExtendedSettingsElement::repeaterOutOfRangeAlert() const { return (AnytoneRoamingSettingsExtension::OutOfRangeAlert) getUInt8(Offset::repRangeAlert()); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setRepeaterOutOfRangeAlert(AnytoneRoamingSettingsExtension::OutOfRangeAlert alert) { setUInt8(Offset::repRangeAlert(), (uint8_t)alert); } unsigned int DMR6X2UVCodeplug::ExtendedSettingsElement::repeaterCheckNumNotifications() const { return getUInt8(Offset::repRangeReminder())+1; } void DMR6X2UVCodeplug::ExtendedSettingsElement::setRepeaterCheckNumNotifications(unsigned int n) { n = Limit::repRangeReminder().map(n); setUInt8(Offset::repRangeReminder(), n-1); } Interval DMR6X2UVCodeplug::ExtendedSettingsElement::repeaterRangeCheckInterval() const { return Interval::fromSeconds((1+getUInt8(Offset::rangeCheckInterval()))*5); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setRepeaterRangeCheckInterval(Interval intv) { intv = Limit::rangeCheckInterval().map(intv); setUInt8(Offset::rangeCheckInterval(), intv.seconds()/5-1); } unsigned int DMR6X2UVCodeplug::ExtendedSettingsElement::repeaterRangeCheckCount() const { return getUInt8(Offset::rangeCheckCount())+3; } void DMR6X2UVCodeplug::ExtendedSettingsElement::setRepeaterRangeCheckCount(unsigned int n) { n = Limit::repeaterReconnections().map(n); setUInt8(Offset::rangeCheckCount(), n-3); } AnytoneRoamingSettingsExtension::RoamStart DMR6X2UVCodeplug::ExtendedSettingsElement::roamingStartCondition() const { return (AnytoneRoamingSettingsExtension::RoamStart) getUInt8(Offset::roamStartCondition()); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setRoamingStartCondition(AnytoneRoamingSettingsExtension::RoamStart cond) { setUInt8(Offset::roamStartCondition(), (uint8_t)cond); } Interval DMR6X2UVCodeplug::ExtendedSettingsElement::autoRoamPeriod() const { return Interval::fromMinutes(getUInt8(Offset::autoRoamPeriod()) + 1); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setAutoRoamPeriod(Interval minutes) { minutes = Limit::autoRoamingInterval().map(minutes); setUInt8(Offset::autoRoamPeriod(), minutes.minutes()-1); } Interval DMR6X2UVCodeplug::ExtendedSettingsElement::autoRoamDelay() const { return Interval::fromSeconds(getUInt8(Offset::autoRoamDelay())); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setAutoRoamDelay(Interval sec) { sec = Limit::autoRoamDelay().map(sec); setUInt8(Offset::autoRoamDelay(), sec.seconds()); } AnytoneRoamingSettingsExtension::RoamStart DMR6X2UVCodeplug::ExtendedSettingsElement::roamingReturnCondition() const { return (AnytoneRoamingSettingsExtension::RoamStart) getUInt8(Offset::roamReturnCondition()); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setRoamingReturnCondition(AnytoneRoamingSettingsExtension::RoamStart cond) { setUInt8(Offset::roamReturnCondition(), (uint8_t)cond); } Interval DMR6X2UVCodeplug::ExtendedSettingsElement::muteTimer() const { return Interval::fromMinutes(getUInt8(Offset::muteDelay()) + 1); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setMuteTimer(Interval minutes) { minutes = Limit::muteTimer().map(minutes); setUInt8(Offset::muteDelay(), minutes.minutes()-1); } AnytoneDMRSettingsExtension::EncryptionType DMR6X2UVCodeplug::ExtendedSettingsElement::encryptionType() const { return (0 == getUInt8(0x0011)) ? AnytoneDMRSettingsExtension::EncryptionType::DMR : AnytoneDMRSettingsExtension::EncryptionType::AES; } void DMR6X2UVCodeplug::ExtendedSettingsElement::setEncryptionType(AnytoneDMRSettingsExtension::EncryptionType type) { switch (type) { case AnytoneDMRSettingsExtension::EncryptionType::DMR: setUInt8(Offset::encryptionType(), 0x00); break; case AnytoneDMRSettingsExtension::EncryptionType::AES: setUInt8(Offset::encryptionType(), 0x01); break; } } AnytoneDisplaySettingsExtension::Color DMR6X2UVCodeplug::ExtendedSettingsElement::zoneANameColor() const { return Color::decode(getUInt8(Offset::zoneANameColor())); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setZoneANameColor(AnytoneDisplaySettingsExtension::Color color) { setUInt8(Offset::zoneANameColor(), Color::encode(color)); } AnytoneDisplaySettingsExtension::Color DMR6X2UVCodeplug::ExtendedSettingsElement::zoneBNameColor() const { return Color::decode(getUInt8(Offset::zoneBNameColor())); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setZoneBNameColor(AnytoneDisplaySettingsExtension::Color color) { setUInt8(Offset::zoneBNameColor(), Color::encode(color)); } AnytoneDisplaySettingsExtension::Color DMR6X2UVCodeplug::ExtendedSettingsElement::channelANameColor() const { return Color::decode(getUInt8(Offset::channelANameColor())); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setChannelANameColor(AnytoneDisplaySettingsExtension::Color color) { setUInt8(Offset::channelANameColor(), Color::encode(color)); } AnytoneDisplaySettingsExtension::Color DMR6X2UVCodeplug::ExtendedSettingsElement::channelBNameColor() const { return Color::decode(getUInt8(Offset::channelBNameColor())); } void DMR6X2UVCodeplug::ExtendedSettingsElement::setChannelBNameColor(AnytoneDisplaySettingsExtension::Color color) { setUInt8(Offset::channelBNameColor(), Color::encode(color)); } bool DMR6X2UVCodeplug::ExtendedSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) { if (! AnytoneCodeplug::ExtendedSettingsElement::fromConfig(flags, ctx, err)) { errMsg(err) << "Cannot encode extended settings for DMR-6X2UV codeplug."; return false; } // Encode device specific settings AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); if (nullptr == ext) return true; // Encode audio settings setMuteTimer(ext->audioSettings()->muteDelay()); // Encode display settings setChannelANameColor(ext->displaySettings()->channelNameColor()); setFontColor(ext->displaySettings()->standbyTextColor()); enableCustomChannelBackground(ext->displaySettings()->customChannelBackground()); // Encode DMR settings setEncryptionType(ext->dmrSettings()->encryption()); // Encode ranging/roaming settings. enableAutoRoaming(ext->roamingSettings()->autoRoam()); setAutoRoamPeriod(ext->roamingSettings()->autoRoamPeriod()); setAutoRoamDelay(ext->roamingSettings()->autoRoamDelay()); enableRepeaterRangeCheck(ext->roamingSettings()->repeaterRangeCheckEnabled()); setRepeaterRangeCheckInterval(ext->roamingSettings()->repeaterCheckInterval()); setRepeaterRangeCheckCount(ext->roamingSettings()->repeaterRangeCheckCount()); setRepeaterOutOfRangeAlert(ext->roamingSettings()->outOfRangeAlert()); setRoamingStartCondition(ext->roamingSettings()->roamingStartCondition()); setRoamingReturnCondition(ext->roamingSettings()->roamingReturnCondition()); setRepeaterCheckNumNotifications(ext->roamingSettings()->notificationCount()); if (! ext->roamingSettings()->defaultZone()->isNull()) setDefaultRoamingZoneIndex( ctx.index(ext->roamingSettings()->defaultZone()->as())); return true; } bool DMR6X2UVCodeplug::ExtendedSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) { if (! AnytoneCodeplug::ExtendedSettingsElement::updateConfig(ctx, err)) { errMsg(err) << "Cannot decode extended settings for DMR-6X2UV codeplug"; return false; } AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); if (nullptr == ext) { ext = new AnytoneSettingsExtension(); ctx.config()->settings()->setAnytoneExtension(ext); } // Decode audio settings ext->audioSettings()->setMuteDelay(this->muteTimer()); // Decode display settings ext->displaySettings()->setChannelNameColor(this->channelANameColor()); ext->displaySettings()->setStandbyTextColor(this->fontColor()); ext->displaySettings()->enableCustomChannelBackground(this->customChannelBackgroundEnabled()); // Decode DMR settings ext->dmrSettings()->setEncryption(this->encryptionType()); // Decode ranging/roaming settings ext->roamingSettings()->enableAutoRoam(this->autoRoamingEnabled()); ext->roamingSettings()->setAutoRoamPeriod(this->autoRoamPeriod()); ext->roamingSettings()->setAutoRoamDelay(this->autoRoamDelay()); ext->roamingSettings()->enableRepeaterRangeCheck(this->repeaterRangeCheckEnabled()); ext->roamingSettings()->setRepeaterCheckInterval(this->repeaterRangeCheckInterval()); ext->roamingSettings()->setRepeaterRangeCheckCount(this->repeaterRangeCheckCount()); ext->roamingSettings()->setOutOfRangeAlert(this->repeaterOutOfRangeAlert()); ext->roamingSettings()->setRoamingStartCondition(this->roamingStartCondition()); ext->roamingSettings()->setRoamingReturnCondition(this->roamingReturnCondition()); ext->roamingSettings()->setNotificationCount(this->repeaterCheckNumNotifications()); return true; } bool DMR6X2UVCodeplug::ExtendedSettingsElement::linkConfig(Context &ctx, const ErrorStack &err) { if (! AnytoneCodeplug::ExtendedSettingsElement::linkConfig(ctx, err)) { errMsg(err) << "Cannot link extended settings for DMR-6X2UV codeplug"; return false; } AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension(); if (nullptr == ext) return false; if (ctx.has(defaultRoamingZoneIndex())) { if (! ctx.has(this->defaultRoamingZoneIndex())) { errMsg(err) << "Cannot link roaming zone, index " << this->defaultRoamingZoneIndex() << " not defined."; return false; } ext->roamingSettings()->defaultZone()->set(ctx.get(this->defaultRoamingZoneIndex())); } return true; } /* ********************************************************************************************* * * Implementation of DMR6X2UVCodeplug::ChannelElement * ********************************************************************************************* */ DMR6X2UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, unsigned size) : AnytoneCodeplug::ChannelElement(ptr, size) { // pass... } DMR6X2UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr) : AnytoneCodeplug::ChannelElement(ptr) { // pass... } bool DMR6X2UVCodeplug::ChannelElement::hasScanListIndex() const { return hasScanListIndex(0); } unsigned DMR6X2UVCodeplug::ChannelElement::scanListIndex() const { return scanListIndex(0); } void DMR6X2UVCodeplug::ChannelElement::setScanListIndex(unsigned idx) { setScanListIndex(0, idx); } void DMR6X2UVCodeplug::ChannelElement::clearScanListIndex() { clearScanListIndex(0); } bool DMR6X2UVCodeplug::ChannelElement::hasScanListIndex(unsigned int n) const { return 0xff != scanListIndex(n); } unsigned int DMR6X2UVCodeplug::ChannelElement::scanListIndex(unsigned int n) const { if (n >= Limit::scanListIndices()) return 0xff; return getUInt8(Offset::scanListIndices() + n*Offset::betweenScanListIndices()); } void DMR6X2UVCodeplug::ChannelElement::setScanListIndex(unsigned int n, unsigned idx) { if (n >= Limit::scanListIndices()) return; setUInt8(Offset::scanListIndices() + n*Offset::betweenScanListIndices(), idx); } void DMR6X2UVCodeplug::ChannelElement::clearScanListIndex(unsigned int n) { setScanListIndex(n, 0xff); } bool DMR6X2UVCodeplug::ChannelElement::roamingEnabled() const { // inverted return ! getBit(Offset::roaming()); } void DMR6X2UVCodeplug::ChannelElement::enableRoaming(bool enable) { // inverted setBit(Offset::roaming(), !enable); } bool DMR6X2UVCodeplug::ChannelElement::ranging() const { return getBit(Offset::ranging()); } void DMR6X2UVCodeplug::ChannelElement::enableRanging(bool enable) { return setBit(Offset::ranging(), enable); } unsigned int DMR6X2UVCodeplug::ChannelElement::dmrAPRSChannelIndex() const { return getUInt8(Offset::dmrAPRSChannelIndex()); } void DMR6X2UVCodeplug::ChannelElement::setDMRAPRSChannelIndex(unsigned int idx) { setUInt8(Offset::dmrAPRSChannelIndex(), std::min(APRSSettingsElement::Limit::dmrSystems(), idx)); } bool DMR6X2UVCodeplug::ChannelElement::dmrAPRSRXEnabled() const { return getBit(Offset::dmrAPRSRXEnable()); } void DMR6X2UVCodeplug::ChannelElement::enableDMRARPSRX(bool enable) { setBit(Offset::dmrAPRSRXEnable(), enable); } bool DMR6X2UVCodeplug::ChannelElement::dmrAPRSPTTEnabled() const { return getBit(Offset::dmrAPRSPTTEnable()); } void DMR6X2UVCodeplug::ChannelElement::enableDMRAPRSPTT(bool enable) { setBit(Offset::dmrAPRSPTTEnable(), enable); } DMR6X2UVCodeplug::ChannelElement::FMAPRSPTTMode DMR6X2UVCodeplug::ChannelElement::fmAPRSPTTMode() const { return (FMAPRSPTTMode)getUInt2(Offset::fmAPRSPTTMode()); } void DMR6X2UVCodeplug::ChannelElement::setFMAPRSPTTMode(FMAPRSPTTMode mode) { setUInt2(Offset::fmAPRSPTTMode(), (unsigned int)mode); } DMR6X2UVCodeplug::ChannelElement::APRSType DMR6X2UVCodeplug::ChannelElement::aprsType() const { return (APRSType) getUInt2(Offset::aprsType()); } void DMR6X2UVCodeplug::ChannelElement::setAPRSType(APRSType aprstype) { setUInt2(Offset::aprsType(), (unsigned int)aprstype); } bool DMR6X2UVCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) { if (! AnytoneCodeplug::ChannelElement::fromChannelObj(c, ctx)) return false; if (const FMChannel *fm = c->as()) { if (fm->aprsSystem()) { setAPRSType(APRSType::FM); if (auto ext = fm->anytoneChannelExtension()) { switch (ext->aprsPTT()) { case AnytoneChannelExtension::APRSPTT::Off: setFMAPRSPTTMode(FMAPRSPTTMode::Off); break; case AnytoneChannelExtension::APRSPTT::Start: setFMAPRSPTTMode(FMAPRSPTTMode::Start); break; case AnytoneChannelExtension::APRSPTT::End: setFMAPRSPTTMode(FMAPRSPTTMode::End); break; } } } } else if (const DMRChannel *dmr = c->as()) { if (dmr->aprs()) { if (dmr->aprs()->is()) { setAPRSType(APRSType::FM); if (auto ext = dmr->anytoneChannelExtension()) { switch (ext->aprsPTT()) { case AnytoneChannelExtension::APRSPTT::Off: setFMAPRSPTTMode(FMAPRSPTTMode::Off); break; case AnytoneChannelExtension::APRSPTT::Start: setFMAPRSPTTMode(FMAPRSPTTMode::Start); break; case AnytoneChannelExtension::APRSPTT::End: setFMAPRSPTTMode(FMAPRSPTTMode::End); break; } } } else if (GPSSystem *sys = dmr->aprs()->as()){ if (0 <= ctx.index(sys)) { setAPRSType(APRSType::DMR); setDMRAPRSChannelIndex(ctx.index(sys)); if (auto ext = dmr->anytoneChannelExtension()) enableDMRAPRSPTT(AnytoneChannelExtension::APRSPTT::Off != ext->aprsPTT()); } } } } return true; } bool DMR6X2UVCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx) const { if (! AnytoneCodeplug::ChannelElement::linkChannelObj(c, ctx)) return false; if (FMChannel *fm = c->as()) { auto ext = fm->anytoneChannelExtension(); if (nullptr == ext) fm->setAnytoneChannelExtension(ext = new AnytoneFMChannelExtension()); if ((APRSType::FM == aprsType()) && ctx.count()) { switch (fmAPRSPTTMode()) { case FMAPRSPTTMode::Off: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Off); break; case FMAPRSPTTMode::Start: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Start); break; case FMAPRSPTTMode::End: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::End); break; } fm->setAPRSSystem(ctx.get(0)); } } else if (DMRChannel *dmr = c->as()) { auto ext = dmr->anytoneChannelExtension(); if (nullptr == ext) dmr->setAnytoneChannelExtension(ext = new AnytoneDMRChannelExtension()); if ((APRSType::FM == aprsType()) && ctx.count()) { switch (fmAPRSPTTMode()) { case FMAPRSPTTMode::Off: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Off); break; case FMAPRSPTTMode::Start: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Start); break; case FMAPRSPTTMode::End: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::End); break; } dmr->setAPRSObj(ctx.get(0)); } else if ((APRSType::DMR == aprsType()) && ctx.has(dmrAPRSChannelIndex())) { ext->setAPRSPTT(dmrAPRSPTTEnabled() ? AnytoneChannelExtension::APRSPTT::Start : AnytoneChannelExtension::APRSPTT::Off); dmr->setAPRSObj(ctx.get(dmrAPRSChannelIndex())); } } return true; } /* ******************************************************************************************** * * Implementation of DMR6X2UVCodeplug::AnalogAPRSSettingsElement * ******************************************************************************************** */ DMR6X2UVCodeplug::APRSSettingsElement::APRSSettingsElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } DMR6X2UVCodeplug::APRSSettingsElement::APRSSettingsElement(uint8_t *ptr) : Element(ptr, APRSSettingsElement::size()) { // pass... } void DMR6X2UVCodeplug::APRSSettingsElement::clear() { memset(_data, 0x00, _size); setUInt8(0x0000, 0xff); setFMTXDelay(Interval::fromMilliseconds(60)); setUInt8(0x003d, 0x01); setUInt8(0x003e, 0x03); setUInt8(0x003f, 0xff); } bool DMR6X2UVCodeplug::APRSSettingsElement::isValid() const { if (! Codeplug::Element::isValid()) return false; return (! destination().simplified().isEmpty()) && (! source().simplified().isEmpty()); } Frequency DMR6X2UVCodeplug::APRSSettingsElement::fmFrequency() const { return Frequency::fromHz(getBCD8_be(Offset::fmFrequency())*10); } void DMR6X2UVCodeplug::APRSSettingsElement::setFMFrequency(Frequency f) { setBCD8_be(Offset::fmFrequency(), f.inHz()/10); } Interval DMR6X2UVCodeplug::APRSSettingsElement::fmTXDelay() const { return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::fmTXDelay())*20)); } void DMR6X2UVCodeplug::APRSSettingsElement::setFMTXDelay(const Interval intv) { setUInt8(Offset::fmTXDelay(), intv.milliseconds()/20); } SelectiveCall DMR6X2UVCodeplug::APRSSettingsElement::txTone() const { if ((uint8_t)SignalingType::Off ==getUInt8(Offset::fmSigType())) { // none return SelectiveCall(); } else if ((uint8_t)SignalingType::CTCSS == getUInt8(Offset::fmSigType())) { // CTCSS return CTCSS::decode(getUInt8(Offset::fmCTCSS())); } else if ((uint8_t)SignalingType::DCS == getUInt8(Offset::fmSigType())) { // DCS uint16_t code = getUInt16_le(Offset::fmDCS()); if (512 < code) return SelectiveCall::fromBinaryDCS(code, false); return SelectiveCall::fromBinaryDCS(code-512, true); } return SelectiveCall(); } void DMR6X2UVCodeplug::APRSSettingsElement::setTXTone(const SelectiveCall &code) { if (code.isInvalid()) { setUInt8(Offset::fmSigType(), (uint8_t)SignalingType::Off); } else if (code.isCTCSS()) { setUInt8(Offset::fmSigType(), (uint8_t)SignalingType::CTCSS); setUInt8(Offset::fmCTCSS(), CTCSS::encode(code)); } else if (code.isDCS()) { setUInt8(Offset::fmSigType(), (uint8_t)SignalingType::DCS); setUInt16_le(Offset::fmDCS(), code.binCode() + (code.isInverted() ? 512 : 0)); } } Interval DMR6X2UVCodeplug::APRSSettingsElement::manualTXInterval() const { return Interval::fromSeconds(getUInt8(Offset::manualTXInterval())); } void DMR6X2UVCodeplug::APRSSettingsElement::setManualTXInterval(Interval sec) { setUInt8(Offset::manualTXInterval(), sec.seconds()); } bool DMR6X2UVCodeplug::APRSSettingsElement::autoTX() const { return ! autoTXInterval().isNull(); } Interval DMR6X2UVCodeplug::APRSSettingsElement::autoTXInterval() const { return Interval::fromSeconds( ((unsigned)getUInt8(Offset::autoTXInterval()))*30 ); } void DMR6X2UVCodeplug::APRSSettingsElement::setAutoTXInterval(Interval sec) { setUInt8(Offset::autoTXInterval(), sec.seconds()/30); } void DMR6X2UVCodeplug::APRSSettingsElement::disableAutoTX() { setAutoTXInterval(Interval::fromMilliseconds(0)); } bool DMR6X2UVCodeplug::APRSSettingsElement::fixedLocationEnabled() const { return getUInt8(Offset::fixedLocation()); } QGeoCoordinate DMR6X2UVCodeplug::APRSSettingsElement::fixedLocation() const { double latitude = getUInt8(Offset::fixedLatDeg()) + double(getUInt8(Offset::fixedLatMin()))/60 + double(getUInt8(Offset::fixedLatSec()))/3600; if (getUInt8(Offset::fixedLatSouth())) latitude *= -1; double longitude = getUInt8(Offset::fixedLonDeg()) + double(getUInt8(Offset::fixedLonMin()))/60 + double(getUInt8(Offset::fixedLonSec()))/3600; if (getUInt8(Offset::fixedLonWest())) longitude *= -1; return QGeoCoordinate(latitude, longitude); } void DMR6X2UVCodeplug::APRSSettingsElement::setFixedLocation(QGeoCoordinate &loc) { double latitude = loc.latitude(); bool south = (0 > latitude); latitude = std::abs(latitude); unsigned lat_deg = int(latitude); latitude -= lat_deg; latitude *= 60; unsigned lat_min = int(latitude); latitude -= lat_min; latitude *= 60; unsigned lat_sec = int(latitude); double longitude = loc.longitude(); bool west = (0 > longitude); longitude = std::abs(longitude); unsigned lon_deg = int(longitude); longitude -= lon_deg; longitude *= 60; unsigned lon_min = int(longitude); longitude -= lon_min; longitude *= 60; unsigned lon_sec = int(longitude); setUInt8(Offset::fixedLatDeg(), lat_deg); setUInt8(Offset::fixedLatMin(), lat_min); setUInt8(Offset::fixedLatSec(), lat_sec); setUInt8(Offset::fixedLatSouth(), (south ? 0x01 : 0x00)); setUInt8(Offset::fixedLonDeg(), lon_deg); setUInt8(Offset::fixedLonMin(), lon_min); setUInt8(Offset::fixedLonSec(), lon_sec); setUInt8(Offset::fixedLonWest(), (west ? 0x01 : 0x00)); // enable fixed location. setUInt8(Offset::fixedLocation(), 0x01); } void DMR6X2UVCodeplug::APRSSettingsElement::disableFixedLocation() { setUInt8(Offset::fixedLocation(), 0x00); } QString DMR6X2UVCodeplug::APRSSettingsElement::destination() const { // Terminated/padded with space return readASCII(Offset::destinationCall(), 6, ' '); } unsigned DMR6X2UVCodeplug::APRSSettingsElement::destinationSSID() const { return getUInt8(Offset::destinationSSID()); } void DMR6X2UVCodeplug::APRSSettingsElement::setDestination(const QString &call, unsigned ssid) { // Terminated/padded with space writeASCII(Offset::destinationCall(), call, 6, ' '); setUInt8(Offset::destinationSSID(), ssid); } QString DMR6X2UVCodeplug::APRSSettingsElement::source() const { // Terminated/padded with space return readASCII(Offset::sourceCall(), 6, ' '); } unsigned DMR6X2UVCodeplug::APRSSettingsElement::sourceSSID() const { return getUInt8(Offset::sourceSSID()); } void DMR6X2UVCodeplug::APRSSettingsElement::setSource(const QString &call, unsigned ssid) { // Terminated/padded with space writeASCII(Offset::sourceCall(), call, 6, ' '); setUInt8(Offset::sourceSSID(), ssid); } QString DMR6X2UVCodeplug::APRSSettingsElement::path() const { return readASCII(Offset::path(), 20, 0x00); } void DMR6X2UVCodeplug::APRSSettingsElement::setPath(const QString &path) { writeASCII(Offset::path(), path, 20, 0x00); } APRSSystem::Icon DMR6X2UVCodeplug::APRSSettingsElement::icon() const { return code2aprsicon(getUInt8(Offset::symbolTable()), getUInt8(Offset::symbol())); } void DMR6X2UVCodeplug::APRSSettingsElement::setIcon(APRSSystem::Icon icon) { setUInt8(Offset::symbolTable(), aprsicon2tablecode(icon)); setUInt8(Offset::symbol(), aprsicon2iconcode(icon)); } Channel::Power DMR6X2UVCodeplug::APRSSettingsElement::power() const { switch (getUInt8(Offset::fmPower())) { case 0: return Channel::Power::Low; case 1: return Channel::Power::Mid; case 2: return Channel::Power::High; case 3: return Channel::Power::Max; } return Channel::Power::Low; } void DMR6X2UVCodeplug::APRSSettingsElement::setPower(Channel::Power power) { switch (power) { case Channel::Power::Min: case Channel::Power::Low: setUInt8(Offset::fmPower(), 0x00); break; case Channel::Power::Mid: setUInt8(Offset::fmPower(), 0x01); break; case Channel::Power::High: setUInt8(Offset::fmPower(), 0x02); break; case Channel::Power::Max: setUInt8(Offset::fmPower(), 0x03); break; } } Interval DMR6X2UVCodeplug::APRSSettingsElement::fmPreWaveDelay() const { return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::fmPrewaveDelay()))*10); } void DMR6X2UVCodeplug::APRSSettingsElement::setFMPreWaveDelay(Interval ms) { setUInt8(Offset::fmPrewaveDelay(), ms.milliseconds()/10); } bool DMR6X2UVCodeplug::APRSSettingsElement::dmrChannelIsSelected(unsigned n) const { return 0xfa2 == dmrChannelIndex(n); } unsigned DMR6X2UVCodeplug::APRSSettingsElement::dmrChannelIndex(unsigned n) const { return getUInt16_le(Offset::dmrChannelIndices() + n*Offset::betweenDMRChannelIndices()); } void DMR6X2UVCodeplug::APRSSettingsElement::setDMRChannelIndex(unsigned n, unsigned idx) { setUInt16_le(Offset::dmrChannelIndices() + n*Offset::betweenDMRChannelIndices(), idx); } void DMR6X2UVCodeplug::APRSSettingsElement::setDMRChannelSelected(unsigned n) { setDMRChannelIndex(n, 0xfa2); } unsigned DMR6X2UVCodeplug::APRSSettingsElement::dmrDestination(unsigned n) const { return getBCD8_be(Offset::dmrDestinations() + n*Offset::betweenDMRDestinations()); } void DMR6X2UVCodeplug::APRSSettingsElement::setDMRDestination(unsigned n, unsigned idx) { setBCD8_be(Offset::dmrDestinations() + n*Offset::betweenDMRDestinations(), idx); } DMRContact::Type DMR6X2UVCodeplug::APRSSettingsElement::dmrCallType(unsigned n) const { switch(getUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes())) { case 0: return DMRContact::PrivateCall; case 1: return DMRContact::GroupCall; case 2: return DMRContact::AllCall; } return DMRContact::PrivateCall; } void DMR6X2UVCodeplug::APRSSettingsElement::setDMRCallType(unsigned n, DMRContact::Type type) { switch(type) { case DMRContact::PrivateCall: setUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes(), 0x00); break; case DMRContact::GroupCall: setUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes(), 0x01); break; case DMRContact::AllCall: setUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes(), 0x02); break; } } bool DMR6X2UVCodeplug::APRSSettingsElement::dmrTimeSlotOverride(unsigned n) { return 0x00 != getUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots()); } DMRChannel::TimeSlot DMR6X2UVCodeplug::APRSSettingsElement::dmrTimeSlot(unsigned n) const { switch (getUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots())) { case 1: return DMRChannel::TimeSlot::TS1; case 2: return DMRChannel::TimeSlot::TS2; } return DMRChannel::TimeSlot::TS1; } void DMR6X2UVCodeplug::APRSSettingsElement::setDMRTimeSlot(unsigned n, DMRChannel::TimeSlot ts) { switch (ts) { case DMRChannel::TimeSlot::TS1: setUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots(), 0x01); break; case DMRChannel::TimeSlot::TS2: setUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots(), 0x02); break; } } void DMR6X2UVCodeplug::APRSSettingsElement::clearDMRTimeSlotOverride(unsigned n) { setUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots(), 0); } bool DMR6X2UVCodeplug::APRSSettingsElement::dmrRoaming() const { return getUInt8(Offset::roamingSupport()); } void DMR6X2UVCodeplug::APRSSettingsElement::enableDMRRoaming(bool enable) { setUInt8(Offset::roamingSupport(), (enable ? 0x01 : 0x00)); } Interval DMR6X2UVCodeplug::APRSSettingsElement::dmrPreWaveDelay() const { return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::dmrPrewaveDelay()))*100); } void DMR6X2UVCodeplug::APRSSettingsElement::setDMRPreWaveDelay(Interval ms) { setUInt8(Offset::dmrPrewaveDelay(), ms.milliseconds()/100); } bool DMR6X2UVCodeplug::APRSSettingsElement::fromFMAPRSSystem(const APRSSystem *sys, Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) clear(); if (! sys->hasRevertChannel()) { errMsg(err) << "Cannot encode APRS settings: " << "No revert channel defined for APRS system '" << sys->name() <<"'."; return false; } setFMFrequency(sys->revertChannel()->txFrequency()); setTXTone(sys->revertChannel()->txTone()); setPower(sys->revertChannel()->power()); setManualTXInterval(Interval::fromSeconds(sys->period())); setAutoTXInterval(Interval::fromSeconds(sys->period())); setDestination(sys->destination(), sys->destSSID()); setSource(sys->source(), sys->srcSSID()); setPath(sys->path()); setIcon(sys->icon()); AnytoneFMAPRSSettingsExtension *ext = sys->anytoneExtension(); if (nullptr == ext) return true; setFMPreWaveDelay(ext->preWaveDelay()); setFMTXDelay(ext->txDelay()); return true; } APRSSystem * DMR6X2UVCodeplug::APRSSettingsElement::toFMAPRSSystem() { APRSSystem *sys = new APRSSystem( tr("APRS %1").arg(destination()), nullptr, destination(), destinationSSID(), source(), sourceSSID(), path(), icon(), "", autoTXInterval().seconds()); AnytoneFMAPRSSettingsExtension *ext = new AnytoneFMAPRSSettingsExtension(); ext->setPreWaveDelay(fmPreWaveDelay()); ext->setTXDelay(fmTXDelay()); sys->setAnytoneExtension(ext); return sys; } bool DMR6X2UVCodeplug::APRSSettingsElement::linkFMAPRSSystem(APRSSystem *sys, Context &ctx) { // First, try to find a matching analog channel in list Frequency f = fmFrequency(); FMChannel *ch = ctx.config()->channelList()->findFMChannelByTxFreq(f); if (! ch) { // If no channel is found, create one with the settings from APRS channel: ch = new FMChannel(); ch->setName("APRS Channel"); ch->setRXFrequency(fmFrequency()); ch->setTXFrequency(fmFrequency()); ch->setPower(power()); ch->setTXTone(txTone()); logInfo() << "No matching APRS channel found for TX frequency " << fmFrequency().format() << "MHz, create one as 'APRS Channel'"; ctx.config()->channelList()->add(ch); } sys->setRevertChannel(ch); return true; } bool DMR6X2UVCodeplug::APRSSettingsElement::fromDMRAPRSSystems(Context &ctx) { unsigned int n = std::min(ctx.count(), Limit::dmrSystems()); for (unsigned int idx=0; idx(idx), ctx); return true; } bool DMR6X2UVCodeplug::APRSSettingsElement::fromDMRAPRSSystemObj(unsigned int idx, GPSSystem *sys, Context &ctx) { if (sys->hasContact()) { setDMRDestination(idx, sys->contactObj()->number()); setDMRCallType(idx, sys->contactObj()->type()); } if (sys->hasRevertChannel()) { setDMRChannelIndex(idx, ctx.index(sys->revertChannel())); clearDMRTimeSlotOverride(idx); } else { // no revert channel specified or "selected channel": setDMRChannelSelected(idx); } return true; } GPSSystem * DMR6X2UVCodeplug::APRSSettingsElement::toDMRAPRSSystemObj(int idx) const { if (0 == dmrDestination(idx)) return nullptr; return new GPSSystem(tr("GPS Sys #%1").arg(idx+1)); } bool DMR6X2UVCodeplug::APRSSettingsElement::linkDMRAPRSSystem(int idx, GPSSystem *sys, Context &ctx) const { // if a revert channel is defined -> link to it if (dmrChannelIsSelected(idx)) sys->resetRevertChannel(); else if (ctx.has(dmrChannelIndex(idx)) && ctx.get(dmrChannelIndex(idx))->is()) sys->setRevertChannel(ctx.get(dmrChannelIndex(idx))->as()); // Search for a matching contact in contacts DMRContact *cont = ctx.config()->contacts()->findDigitalContact(dmrDestination(idx)); // If no matching contact is found, create one if (nullptr == cont) { cont = new DMRContact(dmrCallType(idx), tr("GPS #%1 Contact").arg(idx+1), dmrDestination(idx), false); ctx.config()->contacts()->add(cont); } // link contact to GPS system. sys->setContactObj(cont); return true; } /* ********************************************************************************************* * * Implementation of DMR6X2UVCodeplug * ********************************************************************************************* */ DMR6X2UVCodeplug::DMR6X2UVCodeplug(const QString &label, QObject *parent) : D868UVCodeplug(label, parent) { // pass... } DMR6X2UVCodeplug::DMR6X2UVCodeplug(QObject *parent) : D868UVCodeplug("BTECH DMR-6X2UV", parent) { // pass... } bool DMR6X2UVCodeplug::allocateBitmaps() { if (! D868UVCodeplug::allocateBitmaps()) return false; // Roaming channel bitmaps image(0).addElement(Offset::roamingChannelBitmap(), RoamingChannelBitmapElement::size()); // Roaming zone bitmaps image(0).addElement(Offset::roamingZoneBitmap(), RoamingZoneBitmapElement::size()); return true; } void DMR6X2UVCodeplug::setBitmaps(Context& ctx) { // First set everything common between D868UV and D878UV codeplugs. D868UVCodeplug::setBitmaps(ctx); // Mark roaming zones RoamingZoneBitmapElement roaming_zone_bitmap(data(Offset::roamingZoneBitmap())); unsigned int num_roaming_zones = std::min(Limit::roamingZones(), ctx.count()); roaming_zone_bitmap.clear(); roaming_zone_bitmap.enableFirst(num_roaming_zones); // Mark roaming channels RoamingChannelBitmapElement roaming_ch_bitmap(data(Offset::roamingChannelBitmap())); unsigned int num_roaming_channel = std::min(Limit::roamingChannels(), ctx.count()); roaming_ch_bitmap.clear(); roaming_ch_bitmap.enableFirst(num_roaming_channel); } void DMR6X2UVCodeplug::allocateForEncoding() { // First allocate everything common between D868UV and D878UV codeplugs. D868UVCodeplug::allocateForEncoding(); this->allocateRoaming(); } void DMR6X2UVCodeplug::allocateForDecoding() { // First allocate everything common between D868UV and D878UV codeplugs. D868UVCodeplug::allocateForDecoding(); this->allocateRoaming(); } bool DMR6X2UVCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) { // Encode everything common between d868uv and d878uv radios. if (! D868UVCodeplug::encodeElements(flags, ctx, err)) return false; if (! this->encodeRoaming(flags, ctx, err)) return false; return true; } bool DMR6X2UVCodeplug::decodeElements(Context &ctx, const ErrorStack &err) { // Decode everything commong between d868uv and d878uv codeplugs. if (! D868UVCodeplug::decodeElements(ctx, err)) return false; if (! this->createRoaming(ctx, err)) return false; if (! this->linkRoaming(ctx, err)) return false; return true; } void DMR6X2UVCodeplug::allocateGeneralSettings() { image(0).addElement(Offset::settings(), GeneralSettingsElement::size()); image(0).addElement(Offset::settingsExtension(), ExtendedSettingsElement::size()); } bool DMR6X2UVCodeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { if (! GeneralSettingsElement(data(Offset::settings())).fromConfig(flags, ctx)) { errMsg(err) << "Cannot encode general settings element."; return false; } if (! ExtendedSettingsElement(data(Offset::settingsExtension())).fromConfig(flags, ctx)) { errMsg(err) << "Cannot encode extended settings element."; return false; } return true; } bool DMR6X2UVCodeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) { if (! GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx)) { errMsg(err) << "Cannot decode general settings element."; return false; } if (! ExtendedSettingsElement(data(Offset::settingsExtension())).updateConfig(ctx)) { errMsg(err) << "Cannot decode extended settings element."; return false; } return true; } bool DMR6X2UVCodeplug::linkGeneralSettings(Context &ctx, const ErrorStack &err) { return GeneralSettingsElement(data(Offset::settings())).linkSettings(ctx.config()->settings(), ctx, err); } bool DMR6X2UVCodeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err) // Encode channels for (int i=0; ichannelList()->count(); i++) { // enable channel uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank(); ChannelElement ch(data(Offset::channelBanks() + bank * Offset::betweenChannelBanks() + idx * ChannelElement::size())); if (! ch.fromChannelObj(ctx.config()->channelList()->channel(i), ctx)) return false; } return true; } bool DMR6X2UVCodeplug::createChannels(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Create channels ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap())); for (uint16_t i=0; ichannelList()->add(obj); ctx.add(obj, i); } } return true; } bool DMR6X2UVCodeplug::linkChannels(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap())); // Link channel objects for (uint16_t i=0; i(i)) ch.linkChannelObj(ctx.get(i), ctx); } return true; } void DMR6X2UVCodeplug::allocateGPSSystems() { // replaces D868UVCodeplug::allocateGPSSystems // APRS settings image(0).addElement(Offset::aprsSettings(), APRSSettingsElement::size()); image(0).addElement(Offset::fmAPRSMessage(), Size::fmAPRSMessage()); } bool DMR6X2UVCodeplug::encodeGPSSystems(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err) // replaces D868UVCodeplug::encodeGPSSystems APRSSettingsElement aprs(data(Offset::aprsSettings())); aprs.clear(); // Encode APRS system (there can only be one) if (0 < ctx.count()) { aprs.fromFMAPRSSystem(ctx.get(0), ctx, err); uint8_t *aprsmsg = (uint8_t *)data(Offset::fmAPRSMessage()); encode_ascii(aprsmsg, ctx.get(0)->message(), Limit::fmAPRSMessage(), 0x00); } // Encode GPS systems if (! aprs.fromDMRAPRSSystems(ctx)) return false; if (0 < ctx.count()) { // If there is at least one GPS system defined -> set auto TX interval. // This setting might be overridden by any analog APRS system below aprs.setAutoTXInterval(Interval::fromSeconds(ctx.get(0)->period())); aprs.setManualTXInterval(Interval::fromSeconds(ctx.get(0)->period())); } return true; } bool DMR6X2UVCodeplug::createGPSSystems(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // replaces D868UVCodeplug::createGPSSystems // Before creating any GPS/APRS systems, get global auto TX interval APRSSettingsElement aprs(data(Offset::aprsSettings())); unsigned pos_interval = aprs.autoTXInterval().seconds(); // Create APRS system (if enabled) uint8_t *aprsmsg = (uint8_t *)data(Offset::fmAPRSMessage()); if (aprs.isValid()) { APRSSystem *sys = aprs.toFMAPRSSystem(); if (nullptr == sys) { errMsg(err) << "Cannot decode positioning systems."; return false; } sys->setPeriod(pos_interval); sys->setMessage(decode_ascii(aprsmsg, Limit::fmAPRSMessage(), 0x00)); ctx.config()->posSystems()->add(sys); ctx.add(sys,0); } // Create GPS systems for (unsigned int i=0; isetPeriod(pos_interval); ctx.config()->posSystems()->add(sys); ctx.add(sys, i); } else { return false; } } return true; } bool DMR6X2UVCodeplug::linkGPSSystems(Context &ctx, const ErrorStack &err) { Q_UNUSED(err); // replaces D868UVCodeplug::linkGPSSystems // Link APRS system APRSSettingsElement aprs(data(Offset::aprsSettings())); if (aprs.isValid()) { aprs.linkFMAPRSSystem(ctx.config()->posSystems()->aprsSystem(0), ctx); } // Link GPS systems for (unsigned int i=0; i(i), ctx); } return true; } void DMR6X2UVCodeplug::allocateRoaming() { /* Allocate roaming channels */ RoamingChannelBitmapElement roaming_channel_bitmap(data(Offset::roamingChannelBitmap())); for (uint8_t i=0; i skip if (! roaming_channel_bitmap.isEncoded(i)) continue; // Allocate roaming channel uint32_t addr = Offset::roamingChannels() + i*D878UVCodeplug::RoamingChannelElement::size(); if (!isAllocated(addr, 0)) image(0).addElement(addr, D878UVCodeplug::RoamingChannelElement::size()); } /* Allocate roaming zones. */ RoamingZoneBitmapElement roaming_zone_bitmap(data(Offset::roamingZoneBitmap())); for (uint8_t i=0; i skip if (! roaming_zone_bitmap.isEncoded(i)) continue; // Allocate roaming zone uint32_t addr = Offset::roamingZones() + i*D878UVCodeplug::RoamingZoneElement::size(); if (!isAllocated(addr, 0)) { logDebug() << "Allocate roaming zone at " << QString::number(addr, 16); image(0).addElement(addr, D878UVCodeplug::RoamingZoneElement::size()); } } } bool DMR6X2UVCodeplug::encodeRoaming(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err); // Encode roaming channels unsigned int num_roaming_channel = std::min( Limit::roamingChannels(), ctx.count()); for (uint8_t i=0; iroamingChannels()->get(i)->as(); rch_elm.clear(); rch_elm.fromChannel(rch); if (! ctx.add(rch, i)) { errMsg(err) << "Cannot add index " << i << " for roaming channel '" << rch->name() << "' to codeplug context."; return false; } } // Encode roaming zones for (unsigned int i=0; i(); i++){ uint32_t addr = Offset::roamingZones() + i*RoamingZoneElement::size(); RoamingZoneElement zone(data(addr)); logDebug() << "Encode roaming zone " << ctx.config()->roamingZones()->zone(i)->name() << " (" << (i+1) << ") at " << QString::number(addr, 16) << " with " << ctx.config()->roamingZones()->zone(i)->count() << " elements."; zone.fromRoamingZone(ctx.config()->roamingZones()->zone(i), ctx); } return true; } bool DMR6X2UVCodeplug::createRoaming(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Create or find roaming channels RoamingChannelBitmapElement roaming_channel_bitmap(data(Offset::roamingChannelBitmap())); for (unsigned int i=0; iroamingZones()->add(zone); ctx.add(zone, i); z.linkRoamingZone(zone, ctx, err); } return true; } bool DMR6X2UVCodeplug::linkRoaming(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err) // Pass, no need to link roaming channels. return true; } qdmr-0.12.3/lib/dmr6x2uv_codeplug.hh000066400000000000000000001665231501654372000172260ustar00rootroot00000000000000#ifndef DMR6X2UVCODEPLUG_HH #define DMR6X2UVCODEPLUG_HH #include "d878uv_codeplug.hh" #include "ranges.hh" /** Represents the device specific binary codeplug for BTECH DMR-6X2UV radios. * * This codeplug implementation is compatible with firmware revision 2.04. * * @section dmr6x2uvcpl Codeplug structure within radio * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Channels
      Start Size Content
      024C1500 000200 Bitmap of 4000 channels, default 0x00, * 0x00 padded.
      00800000 max. 002000 Channel bank 0 of up to 128 channels, * see @c AnytoneCodeplug::ChannelElement 64 b each.
      00840000 max. 002000 Channel bank 1 of up to 128 channels.
      00FC0000 max. 000800 Channel bank 32, up to 32 channels.
      00FC0800 000040 VFO A settings, * see @c AnytoneCodeplug::ChannelElement.
      00FC0840 000040 VFO B settings, * see @c AnytoneCodeplug::ChannelElement.
      Zones
      Start Size Content
      024C1300 000020 Bitmap of 250 zones.
      01000000 max. 01f400 250 zones channel lists of 250 16bit indices each. * 0-based, little endian, default/padded=0xffff. Offset between channel lists 0x200, * size of each list 0x1f4.
      02540000 max. 001f40 250 Zone names. * Each zone name is up to 16 ASCII chars long and gets 0-padded to 32b.
      02500100 000400 Zone A & B channel list.
      Roaming
      Start Size Content
      01042000 000020 Roaming channel bitmask, up to 250 bits, * 0-padded, default 0.
      01040000 max. 0x1f40 Optional up to 250 roaming channels, of 32b each. * See @c D868UVCodeplug::RoamingChannelElement for details.
      01042080 000010 Roaming zone bitmask, up to 64 bits, 0-padded, * default 0.
      01043000 max. 0x2000 Optional up to 64 roaming zones, of 128b each. * See @c D868UVCodeplug::RoamingZoneElement for details.
      Contacts
      Start Size Content
      02600000 max. 009C40 Index list of valid contacts. * 10000 32bit indices, little endian, default 0xffffffff
      02640000 000500 Contact bitmap, 10000 bit, inverted, * default 0xff, 0x00 padded.
      02680000 max. 0f4240 10000 contacts, * see @c AnytoneCodeplug::ContactElement. As each contact is 100b, they do not align with the * 16b blocks being transferred to the device. Hence contacts are organized internally in groups * of 4 contacts forming a "bank".
      04340000 max. 013880 DMR ID to contact index map, * see @c AnytoneCodeplug::ContactMapElement. Sorted by ID, empty entries set to * @c 0xffffffffffffffff.
      Analog Contacts
      Start Size Content
      02900000 000080 Index list of valid analog contacts.
      02900100 000080 Bytemap for 128 analog contacts.
      02940000 max. 000180 128 analog contacts. * See @c AnytoneCodeplug::DTMFContactElement. As each analog contact is 24b, they do not align with * the 16b transfer block-size. Hence analog contacts are internally organized in groups of 2.
      RX Group Lists
      Start Size Content
      025C0B10 000020 Bitmap of 250 RX group lists, * default/padding 0x00.
      02980000 max. 000120 Grouplist 0, * see @c AnytoneCodeplug::GroupListElement.
      02980200 max. 000120 Grouplist 1
      ... ... ...
      0299f200 max. 000120 Grouplist 250
      Scan lists
      Start Size Content
      024C1340 000020 Bitmap of 250 scan lists.
      01080000 000090 Bank 0, Scanlist 1, * see @c AnytoneCodeplug::ScanListElement.
      01080200 000090 Bank 0, Scanlist 2
      ... ... ...
      01081E00 000090 Bank 0, Scanlist 16
      010C0000 000090 Bank 1, Scanlist 17
      ... ... ...
      01440000 000090 Bank 15, Scanlist 241
      ... ... ...
      01441400 000090 Bank 15, Scanlist 250
      Radio IDs
      Start Size Content
      024C1320 000020 Bitmap of 250 radio IDs.
      02580000 max. 001f40 250 Radio IDs. * See @c AnytoneCodeplug::RadioIDElement.
      GPS/APRS
      Start Size Content
      02501000 0000A0 APRS settings (DMR + FM), * see @c DMR6X2UVCodeplug::APRSSettingsElement.
      02501200 000040 APRS Text, up to 60 chars ASCII, 0-padded.
      02501280 000030 GPS template message, ASCII, 0-padded.
      02502000 000080 FM APRS frequency names, * see @c FMAPRSFrequencyNamesElement. This element is not part of the manufacturer codeplug. * QDMR uses this memory section to store additional information.
      General Settings
      Start Size Content
      02500000 0000e0 General settings, * see @c DMR6X2UVCodeplug::GeneralSettingsElement.
      02500500 000100 DTMF list
      02500600 000030 Power on settings, * see @c AnytoneCodeplug::BootSettingsElement.
      02501400 000030 Settings extension, see @c DMR6X2UVCodeplug::ExtendedSettingsElement.
      024C2000 0003F0 List of 250 auto-repeater offset frequencies. * 32bit little endian frequency in 10Hz. I.e., 600kHz = 60000. * Default 0x00000000, 0x00 padded.
      Messages
      Start Size Content
      01640000 max. 000100 Some kind of linked list of messages. * See @c AnytoneCodeplug::MessageListElement. Each entry has a size of 0x10.
      01640800 000090 Bytemap of up to 100 valid messages. * 0x00=valid, 0xff=invalid, remaining 46b set to 0x00.
      02140000 max. 000800 Bank 0, Messages 1-8. * Each message consumes 0x100b. See @c AnytoneCodeplug::MessageElement.
      02180000 max. 000800 Bank 1, Messages 9-16
      ... ... ...
      02440000 max. 000800 Bank 12, Messages 97-100
      Hot Keys
      Start Size Content
      025C0000 000100 4 analog quick-call settings. * See @c AnytoneCodeplug::AnalogQuickCallElement.
      025C0B00 000010 Status message bitmap.
      025C0100 000400 Up to 32 status messages. * Length unknown, offset 0x20. ASCII 0x00 terminated and padded.
      025C0500 000360 18 hot-key settings, see * @c AnytoneCodeplug::HotKeyElement
      Encryption
      Start Size Content
      024C1700 000040 32 Encryption IDs, 0-based, 16bit big-endian.
      024C1800 000500 32 DMR-Encryption keys, * see @c D868UVCodeplug::dmr_encryption_key_t, * 40b each.
      025C1000 004000 Up to 256 AES encryption keys. * See @c D878UVCodeplug::AESEncryptionKeyElement.
      Misc
      Start Size Content
      024C1400 000020 Alarm setting, * see @c AnytoneCodeplug::AlarmSettingElement.
      024C1440 000030 Digital alarm settings extension, * see @c AnytoneCodeplug::DigitalAlarmExtensionElement.
      FM Broadcast
      Start Size Content
      02480210 000020 Bitmap of 100 FM broadcast channels.
      02480000 max. 000200 100 FM broadcast channels. Encoded * as 8-digit BCD little-endian in 100Hz. Filled with 0x00.
      02480200 000010 FM broadcast VFO frequency. Encoded * as 8-digit BCD little-endian in 100Hz. Filled with 0x00.
      DTMF, 2-tone & 5-tone signaling.
      Start Size Content
      024C0C80 000010 5-tone encoding bitmap.
      024C0000 000020 5-tone encoding.
      024C0D00 000200 5-tone ID list.
      024C1000 000080 5-tone settings.
      024C1080 000050 DTMF settings.
      024C1280 000010 2-tone encoding bitmap.
      024C1100 000010 2-tone encoding.
      024C1290 000010 2-tone settings.
      024C2600 000010 2-tone decoding bitmap.
      024C2400 000030 2-tone decoding.
      Unknown settings.
      Start Size Content
      024C2610 000020 Unknown bitmap.
      024C2630 000020 Unknown bitmap.
      024C3000 000020 Unknown settings.
      024C5000 000020 Unknown settings.
      * * @ingroup dmr6x2uv */ class DMR6X2UVCodeplug : public D868UVCodeplug { Q_OBJECT protected: /** Colors supported by the DMR-6X2UV. */ struct Color { public: /** Maps code -> color. */ static AnytoneDisplaySettingsExtension::Color decode(uint8_t code); /** Maps color -> code. */ static uint8_t encode(AnytoneDisplaySettingsExtension::Color color); protected: /** Encoding of the supported colors. */ typedef enum { Orange=0, Red=1, Yellow=2, Green=3, Turquoise=4, Blue=5, White = 6, Black = 7 } CodedColor; }; /** Background colors supported by the DMR-6X2UV. */ struct BackgroundColor { public: /** Maps code -> color. */ static AnytoneDisplaySettingsExtension::Color decode(uint8_t code); /** Maps color -> code. */ static uint8_t encode(AnytoneDisplaySettingsExtension::Color color); protected: /** Encoding of the supported colors. */ typedef enum { Black = 0, Blue = 1 } CodedColor; }; /** Font colors supported by the DMR-6X2UV. */ struct FontColor { public: /** Maps code -> color. */ static AnytoneDisplaySettingsExtension::Color decode(uint8_t code); /** Maps color -> code. */ static uint8_t encode(AnytoneDisplaySettingsExtension::Color color); protected: /** Encoding of the supported colors. */ typedef enum { White = 0, Black = 1, Orange=2, Red=3, Yellow=4, Green=5, Turquoise=6, Blue=7 } CodedColor; }; public: /** General settings element for the DMR-6X2UV. * * Extends the @c AnytoneCodeplug::GeneralSettingsElement by the device specific settings for * the BTECH DMR-6X2UV. * * Memory representation of the encoded settings element (size 0x0e0 bytes): * @verbinclude dmr6x2uv_generalsettings.txt */ class GeneralSettingsElement: public D868UVCodeplug::GeneralSettingsElement { protected: /** Device specific encoding of the key functions. */ struct KeyFunction { public: /** Encodes key function. */ static uint8_t encode(AnytoneKeySettingsExtension::KeyFunction tone); /** Decodes key function. */ static AnytoneKeySettingsExtension::KeyFunction decode(uint8_t code); protected: /** Device specific key functions. */ typedef enum { Off = 0x00, Voltage = 0x01, Power = 0x02, Repeater = 0x03, Reverse = 0x04, Encryption = 0x05, Call = 0x06, VOX = 0x07, ToggleVFO = 0x08, SubPTT = 0x09, Scan = 0x0a, WFM = 0x0b, Alarm = 0x0c, RecordSwitch = 0x0d, Record = 0x0e, SMS = 0x0f, Dial = 0x10, GPSInformation = 0x11, Monitor = 0x12, ToggleMainChannel = 0x13, HotKey1 = 0x14, HotKey2 = 0x15, HotKey3 = 0x16, HotKey4 = 0x17, HotKey5 = 0x18, HotKey6 = 0x19, WorkAlone = 0x1a, SkipChannel = 0x1b, DMRMonitor = 0x1c, SubChannel = 0x1d, PriorityZone = 0x1e, VFOScan = 0x1f, MICSoundQuality = 0x20, LastCallReply = 0x21, ChannelType = 0x22, SimplexRepeater = 0x23, Ranging = 0x24, ChannelRanging = 0x25, MaxVolume = 0x26, Slot = 0x27, Squelch = 0x28, Roaming = 0x29, Zone = 0x2a, RoamingSet = 0x2b, Mute = 0x02c, CtcssDcsSet=0x2d, APRSSet = 0x2e, APRSSend = 0x2f } KeyFunctionCode; }; /** Possible VFO frequency steps. */ enum FreqStep { FREQ_STEP_2_5kHz = 0, ///< 2.5kHz FREQ_STEP_5kHz = 1, ///< 5kHz FREQ_STEP_6_25kHz = 2, ///< 6.25kHz FREQ_STEP_10kHz = 3, ///< 10kHz FREQ_STEP_12_5kHz = 4, ///< 12.5kHz FREQ_STEP_20kHz = 5, ///< 20kHz FREQ_STEP_25kHz = 6, ///< 25kHz FREQ_STEP_50kHz = 7 ///< 50kHz }; /** DTMF signalling durations. */ enum DTMFDuration { DTMF_DUR_50ms = 0, DTMF_DUR_100ms = 1, DTMF_DUR_200ms = 2, DTMF_DUR_300ms = 3, DTMF_DUR_500ms = 4 }; /** TBST (open repeater) frequencies. */ enum class TBSTFrequency { Hz1000 = 0, Hz1450 = 1, Hz1750 = 2, Hz2100 = 3 }; /** All possible STE (squelch tail eliminate) frequencies. */ enum class STEFrequency { Off = 0, Hz55_2 = 1, Hz259_2 = 2 }; protected: /** Hidden Constructor. */ GeneralSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit GeneralSettingsElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x00e0; } bool idleChannelTone() const; void enableIdleChannelTone(bool enable); /** Returns the transmit timeout in seconds. */ virtual unsigned transmitTimeout() const; /** Sets the transmit timeout in seconds. */ virtual void setTransmitTimeout(unsigned tot); /** Returns the UI language. */ virtual AnytoneDisplaySettingsExtension::Language language() const; /** Sets the UI language. */ virtual void setLanguage(AnytoneDisplaySettingsExtension::Language lang); /** Returns the VFO frequency step in kHz. */ virtual Frequency vfoFrequencyStep() const; /** Sets the VFO frequency step in kHz. */ virtual void setVFOFrequencyStep(Frequency kHz); AnytoneKeySettingsExtension::KeyFunction funcKeyAShort() const; void setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyBShort() const; void setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyCShort() const; void setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey1Short() const; void setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey2Short() const; void setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyALong() const; void setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyBLong() const; void setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKeyCLong() const; void setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey1Long() const; void setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func); AnytoneKeySettingsExtension::KeyFunction funcKey2Long() const; void setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func); bool vfoModeA() const; void enableVFOModeA(bool enable); bool vfoModeB() const; void enableVFOModeB(bool enable); /** Returns the STE (squelch tail eliminate) type. */ virtual AnytoneSettingsExtension::STEType steType() const; /** Sets the STE (squelch tail eliminate) type. */ virtual void setSTEType(AnytoneSettingsExtension::STEType type); /** Returns the STE (squelch tail eliminate) frequency setting in Hz. * A value of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */ virtual double steFrequency() const; /** Sets the STE (squelch tail eliminate) frequency setting. * A value of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */ virtual void setSTEFrequency(double freq); /** Returns the group call hang time in seconds. */ virtual Interval groupCallHangTime() const; /** Sets the group call hang time in seconds. */ virtual void setGroupCallHangTime(Interval sec); /** Returns the private call hang time in seconds. */ virtual Interval privateCallHangTime() const; /** Sets the private call hang time in seconds. */ virtual void setPrivateCallHangTime(Interval sec); /** Returns the pre-wave time in ms. */ virtual Interval preWaveDelay() const; /** Sets the pre-wave time in ms. */ virtual void setPreWaveDelay(Interval ms); /** Returns the wake head-period in ms. */ virtual Interval wakeHeadPeriod() const; /** Sets the wake head-period in ms. */ virtual void setWakeHeadPeriod(Interval ms); /** Returns the wide-FM (broadcast) channel index. */ virtual unsigned wfmChannelIndex() const; /** Sets the wide-FM (broadcast) channel index. */ virtual void setWFMChannelIndex(unsigned idx); /** Returns @c true if the WFM RX is in VFO mode. */ virtual bool wfmVFOEnabled() const; /** Enables/disables VFO mode for WFM RX. */ virtual void enableWFMVFO(bool enable); /** Returns the DTMF tone duration in ms. */ virtual unsigned dtmfToneDuration() const; /** Sets the DTMF tone duration in ms. */ virtual void setDTMFToneDuration(unsigned ms); /** Returns @c true if "man down" is enabled. */ virtual bool manDown() const; /** Enables/disables "man down". */ virtual void enableManDown(bool enable); /** Returns @c true if WFM monitor is enabled. */ virtual bool wfmMonitor() const; /** Enables/disables WFM monitor. */ virtual void enableWFMMonitor(bool enable); /** Returns the TBST frequency. */ virtual Frequency tbstFrequency() const; /** Sets the TBST frequency. */ virtual void setTBSTFrequency(Frequency freq); /** Returns @c true if the "pro mode" is enabled. */ virtual bool proMode() const; /** Enables/disables the "pro mode". */ virtual void enableProMode(bool enable); bool keyToneEnabled() const; void enableKeyTone(bool enable); /** Returns @c true if the own ID is filtered in call lists. */ virtual bool filterOwnID() const; /** Enables/disables filter of own ID in call lists. */ virtual void enableFilterOwnID(bool enable); /** Returns @c true remote stun/kill is enabled. */ virtual bool remoteStunKill() const; /** Enables/disables remote stun/kill. */ virtual void enableRemoteStunKill(bool enable); /** Returns @c true remote monitor is enabled. */ virtual bool remoteMonitor() const; /** Enables/disables remote monitor. */ virtual void enableRemoteMonitor(bool enable); /** Returns @c true, if the selection of a TX contact is enabled. */ virtual bool selectTXContactEnabled() const; /** Enables/disables selection of the TX contact. */ virtual void enableSelectTXContact(bool enable); /** Returns the monitor slot match. */ virtual AnytoneDMRSettingsExtension::SlotMatch monitorSlotMatch() const; /** Sets the monitor slot match. */ virtual void setMonitorSlotMatch(AnytoneDMRSettingsExtension::SlotMatch match); /** Returns @c true if the monitor matches color code. */ virtual bool monitorColorCodeMatch() const; /** Enables/disables monitor color code match. */ virtual void enableMonitorColorCodeMatch(bool enable); /** Returns @c true if the monitor matches ID. */ virtual bool monitorIDMatch() const; /** Enables/disables monitor ID match. */ virtual void enableMonitorIDMatch(bool enable); /** Returns @c true if the monitor holds the time slot. */ virtual bool monitorTimeSlotHold() const; /** Enables/disables monitor time slot hold. */ virtual void enableMonitorTimeSlotHold(bool enable); /** Returns the "man down" delay in seconds. */ virtual Interval manDownDelay() const; /** Sets the "man down" delay in seconds. */ virtual void setManDownDelay(Interval sec); /** Returns the analog call hold in seconds. */ virtual unsigned fmCallHold() const; /** Sets the analog call hold in seconds. */ virtual void setFMCallHold(unsigned sec); /** Returns @c true if the GPS range reporting is enabled. */ virtual bool gpsMessageEnabled() const; /** Enables/disables GPS range reporting. */ virtual void enableGPSMessage(bool enable); /** Returns @c true if the call channel is maintained. */ virtual bool maintainCallChannel() const; /** Enables/disables maintaining the call channel. */ virtual void enableMaintainCallChannel(bool enable); /** Returns the priority Zone A index. */ virtual unsigned priorityZoneAIndex() const; /** Sets the priority zone A index. */ virtual void setPriorityZoneAIndex(unsigned idx); /** Returns the priority Zone B index. */ virtual unsigned priorityZoneBIndex() const; /** Sets the priority zone B index. */ virtual void setPriorityZoneBIndex(unsigned idx); /** Returns @c true, if a SMS confirmation is sent. */ virtual bool smsConfirmEnabled() const; /** Enables/disables SMS confirmation. */ virtual void enableSMSConfirm(bool enable); /** Returns @c true if the simplex repeater feature is enabled. */ virtual bool simplexRepeaterEnabled() const; /** Enables disables the simplex repeater feature. */ virtual void enableSimplexRepeater(bool enable); Interval gpsUpdatePeriod() const; void setGPSUpdatePeriod(Interval sec); /** Returns @c true if the speaker is switched on during RX in simplex repeater mode, * see @c simplexRepeaterEnabled. */ virtual bool monitorSimplexRepeaterEnabled() const; /** Enables/disables the speaker during RX in simplex repeater mode. */ virtual void enableMonitorSimplexRepeater(bool enable); bool showCurrentContact() const; void enableShowCurrentContact(bool enable); bool keyToneLevelAdjustable() const; unsigned keyToneLevel() const; void setKeyToneLevel(unsigned level); void setKeyToneLevelAdjustable(); bool knobLock() const; void enableKnobLock(bool enable); bool keypadLock() const; void enableKeypadLock(bool enable); bool sidekeysLock() const; void enableSidekeysLock(bool enable); bool keyLockForced() const; void enableKeyLockForced(bool enable); /** Returns the time-slot in simplex repeater mode. */ virtual AnytoneSimplexRepeaterSettingsExtension::TimeSlot simplexRepeaterTimeslot() const; /** Sets the time-slot in simplex repeater mode. */ virtual void setSimplexRepeaterTimeslot(AnytoneSimplexRepeaterSettingsExtension::TimeSlot slot); bool showLastHeard() const; void enableShowLastHeard(bool enable); /** Returns the SMS format. */ virtual AnytoneDMRSettingsExtension::SMSFormat smsFormat() const; /** Sets the SMS format. */ virtual void setSMSFormat(AnytoneDMRSettingsExtension::SMSFormat fmt); bool gpsUnitsImperial() const; void enableGPSUnitsImperial(bool enable); Frequency autoRepeaterMinFrequencyVHF() const; void setAutoRepeaterMinFrequencyVHF(Frequency Hz); Frequency autoRepeaterMaxFrequencyVHF() const; void setAutoRepeaterMaxFrequencyVHF(Frequency Hz); Frequency autoRepeaterMinFrequencyUHF() const; void setAutoRepeaterMinFrequencyUHF(Frequency Hz); Frequency autoRepeaterMaxFrequencyUHF() const; void setAutoRepeaterMaxFrequencyUHF(Frequency Hz); AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionB() const; void setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir); /** If enabled, the FM ID is sent together with selected contact. */ virtual bool fmSendIDAndContact() const; /** Enables/disables sending contact with FM ID. */ virtual void enableFMSendIDAndContact(bool enable); bool defaultChannel() const; void enableDefaultChannel(bool enable); unsigned defaultZoneIndexA() const; void setDefaultZoneIndexA(unsigned idx); unsigned defaultZoneIndexB() const; void setDefaultZoneIndexB(unsigned idx); bool defaultChannelAIsVFO() const; unsigned defaultChannelAIndex() const; void setDefaultChannelAIndex(unsigned idx); void setDefaultChannelAToVFO(); bool defaultChannelBIsVFO() const; unsigned defaultChannelBIndex() const; void setDefaultChannelBIndex(unsigned idx); void setDefaultChannelBToVFO(); bool keepLastCaller() const; void enableKeepLastCaller(bool enable); Interval rxBacklightDuration() const; void setRXBacklightDuration(Interval sec); /** Returns the stand-by background color. */ virtual AnytoneDisplaySettingsExtension::Color standbyBackgroundColor() const; /** Sets the stand-by background color. */ virtual void setStandbyBackgroundColor(AnytoneDisplaySettingsExtension::Color color); /** Returns the group-call hang time, if group call was dialed manually. */ virtual unsigned int manualDialedGroupCallHangTime() const; /** Sets the group-call hang time, if the group call was dialed maually. */ virtual void setManualDialedGroupCallHangTime(unsigned int dur); /** Returns the private-call hang time, if private call was dialed manually. */ virtual unsigned int manualDialedPrivateCallHangTime() const; /** Sets the private-call hang time, if the private call was dialed maually. */ virtual void setManualDialedPrivateCallHangTime(unsigned int dur); bool fromConfig(const Flags &flags, Context &ctx); bool updateConfig(Context &ctx); protected: /** Some internal used offsets within the element. */ struct Offset: public D868UVCodeplug::GeneralSettingsElement::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int idleChannelTone() { return 0x0000; } static constexpr unsigned int transmitTimeout() { return 0x0004; } static constexpr unsigned int language() { return 0x0005; } static constexpr unsigned int vfoFrequencyStep() { return 0x0008; } static constexpr unsigned int vfoModeB() { return 0x0015; } static constexpr unsigned int vfoModeA() { return 0x0016; } static constexpr unsigned int steType() { return 0x0017; } static constexpr unsigned int steFrequency() { return 0x0018; } static constexpr unsigned int groupCallHangTime() { return 0x0019; } static constexpr unsigned int privateCallHangTime() { return 0x001a; } static constexpr unsigned int preWaveDelay() { return 0x001b; } static constexpr unsigned int wakeHeadPeriod() { return 0x001c; } static constexpr unsigned int wfmChannelIndex() { return 0x001d; } static constexpr unsigned int wfmVFOEnabled() { return 0x001e; } static constexpr unsigned int dtmfToneDuration() { return 0x0023; } static constexpr unsigned int manDown() { return 0x0024; } static constexpr unsigned int wfmMonitor() { return 0x002b; } static constexpr unsigned int tbstFrequency() { return 0x002e; } static constexpr unsigned int proMode() { return 0x0034; } static constexpr unsigned int enableKeyTone() { return 0x0036; } static constexpr unsigned int filterOwnID() { return 0x0038; } static constexpr unsigned int remoteStunKill() { return 0x003c; } static constexpr unsigned int remoteMonitor() { return 0x003e; } static constexpr unsigned int selectTXContact() { return 0x0040; } static constexpr unsigned int monSlotMatch() { return 0x0049; } static constexpr unsigned int monColorCodeMatch() { return 0x004a; } static constexpr unsigned int monIDMatch() { return 0x004b; } static constexpr unsigned int monTimeSlotHold() { return 0x004c; } static constexpr unsigned int manDownDelay() { return 0x004f; } static constexpr unsigned int fmCallHold() { return 0x0050; } static constexpr unsigned int enableGPSMessage() { return 0x0053; } static constexpr unsigned int maintainCallChannel() { return 0x006e; } static constexpr unsigned int priorityZoneA() { return 0x006f; } static constexpr unsigned int priorityZoneB() { return 0x0070; } static constexpr unsigned int smsConfirm() { return 0x0071; } static constexpr unsigned int simplexRepEnable() { return 0x00b1; } static constexpr unsigned int gpsUpdatePeriod() { return 0x00b2; } static constexpr unsigned int simplxRepSpeaker() { return 0x00b3; } static constexpr unsigned int showContact() { return 0x00b4; } static constexpr unsigned int keyToneLevel() { return 0x00b5; } static constexpr Bit knobLock() { return {0x00b6, 0}; } static constexpr Bit keypadLock() { return {0x00b6, 1}; } static constexpr Bit sideKeyLock() { return {0x00b6, 3}; } static constexpr Bit forceKeyLock() { return {0x00b6, 4}; } static constexpr unsigned int simplxRepSlot() { return 0x00b7; } static constexpr unsigned int showLastHeard() { return 0x00b8; } static constexpr unsigned int smsFormat() { return 0x00b9; } static constexpr unsigned int gpsUnits() { return 0x00ba; } static constexpr unsigned int autoRepMinVHF() { return 0x00bc; } static constexpr unsigned int autoRepMaxVHF() { return 0x00c0; } static constexpr unsigned int autoRepMinUHF() { return 0x00c4; } static constexpr unsigned int autoRepMaxUHF() { return 0x00c8; } static constexpr unsigned int autoRepeaterDirB() { return 0x00cc; } static constexpr unsigned int fmSendIDAndContact() { return 0x00cd; } static constexpr unsigned int defaultChannels() { return 0x00ce; } static constexpr unsigned int defaultZoneA() { return 0x00cf; } static constexpr unsigned int defaultZoneB() { return 0x00d0; } static constexpr unsigned int defaultChannelA() { return 0x00d1; } static constexpr unsigned int defaultChannelB() { return 0x00d2; } static constexpr unsigned int keepLastCaller() { return 0x00d3; } static constexpr unsigned int rxBacklightDuration() { return 0x00d4; } static constexpr unsigned int standbyBackground() { return 0x00d5; } static constexpr unsigned int manGrpCallHangTime() { return 0x00d6; } static constexpr unsigned int manPrvCallHangTime() { return 0x00d7; } /// @endcond }; }; /** Implements some settings extension for the BTECH DMR-6X2UV. * * Memory representation of the encoded settings element (size 0x0e0 bytes): * @verbinclude dmr6x2uv_settingsextension.txt */ class ExtendedSettingsElement: public AnytoneCodeplug::ExtendedSettingsElement { protected: /** Hidden Constructor. */ ExtendedSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ExtendedSettingsElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0030; } /** Resets the general settings. */ void clear(); bool sendTalkerAlias() const; void enableSendTalkerAlias(bool enable); AnytoneDMRSettingsExtension::TalkerAliasSource talkerAliasSource() const; void setTalkerAliasSource(AnytoneDMRSettingsExtension::TalkerAliasSource source); AnytoneDMRSettingsExtension::TalkerAliasEncoding talkerAliasEncoding() const; void setTalkerAliasEncoding(AnytoneDMRSettingsExtension::TalkerAliasEncoding encoding); /** Returns the font color. */ virtual AnytoneDisplaySettingsExtension::Color fontColor() const; /** Sets the font color. */ virtual void setFontColor(AnytoneDisplaySettingsExtension::Color color); /** Returns @c true if the custom channel background is enabled. */ virtual bool customChannelBackgroundEnabled() const; /** Enables/disables the custom channel background. */ virtual void enableCustomChannelBackground(bool enable); /** Returns @c true if auto roaming is enabled. */ virtual bool autoRoamingEnabled() const; /** Enables/disables auto roaming. */ virtual void enableAutoRoaming(bool enable); /** Returns @c true if repeater check is enabled. */ virtual bool repeaterRangeCheckEnabled() const; /** Enables/disables repeater check. */ virtual void enableRepeaterRangeCheck(bool enable); /** Returns the number of times, the repeater out-of-range reminder is shown (1-10). */ virtual unsigned int repeaterCheckNumNotifications() const; /** Sets the number of times, the repeater out-of-range reminder is shown (1-10). */ virtual void setRepeaterCheckNumNotifications(unsigned int n); /** Returns the repeater check interval in seconds (5-50s). */ virtual Interval repeaterRangeCheckInterval() const; /** Sets the repeater check interval in seconds (5-50s). */ virtual void setRepeaterRangeCheckInterval(Interval intv); /** Returns the repeater out-of-range alert type. */ virtual AnytoneRoamingSettingsExtension::OutOfRangeAlert repeaterOutOfRangeAlert() const; /** Sets the repeater out-of-range alert type. */ virtual void setRepeaterOutOfRangeAlert(AnytoneRoamingSettingsExtension::OutOfRangeAlert alert); /** Returns the number of times, a repeater reconnection is tried (3-5). */ virtual unsigned int repeaterRangeCheckCount() const; /** Sets the number of times, a repeater reconnection is tried (3-5). */ virtual void setRepeaterRangeCheckCount(unsigned int n); /** Returns the roaming zone index. */ virtual unsigned int defaultRoamingZoneIndex() const; /** Sets the roaming zone index. */ virtual void setDefaultRoamingZoneIndex(unsigned int index); /** Returns the condition to start roaming. */ virtual AnytoneRoamingSettingsExtension::RoamStart roamingStartCondition() const; /** Sets the condition to start roaming. */ virtual void setRoamingStartCondition(AnytoneRoamingSettingsExtension::RoamStart cond); /** Returns the auto-roaming interval in minutes (1-256). */ virtual Interval autoRoamPeriod() const; /** Sets the auto-roaming interval in minutes (1-256). */ virtual void setAutoRoamPeriod(Interval minutes); /** Returns the effective roaming waiting time in seconds (0-30s). */ virtual Interval autoRoamDelay() const; /** Sets the effective roaming waiting time in seconds (0-30s). */ virtual void setAutoRoamDelay(Interval sec); /** Returns the roaming return condition. */ virtual AnytoneRoamingSettingsExtension::RoamStart roamingReturnCondition() const; /** Sets the roaming return condition. */ virtual void setRoamingReturnCondition(AnytoneRoamingSettingsExtension::RoamStart cond); /** Returns the mute timer in minutes. */ virtual Interval muteTimer() const; /** Sets the mute timer in minutes. */ virtual void setMuteTimer(Interval minutes); /** Returns the encryption type. */ virtual AnytoneDMRSettingsExtension::EncryptionType encryptionType() const; /** Sets the encryption type. */ virtual void setEncryptionType(AnytoneDMRSettingsExtension::EncryptionType type); AnytoneDisplaySettingsExtension::Color zoneANameColor() const; void setZoneANameColor(AnytoneDisplaySettingsExtension::Color color); AnytoneDisplaySettingsExtension::Color zoneBNameColor() const; void setZoneBNameColor(AnytoneDisplaySettingsExtension::Color color); /** Returns the name color for channel A. */ virtual AnytoneDisplaySettingsExtension::Color channelANameColor() const; /** Sets the name color for channel A. */ virtual void setChannelANameColor(AnytoneDisplaySettingsExtension::Color color); AnytoneDisplaySettingsExtension::Color channelBNameColor() const; void setChannelBNameColor(AnytoneDisplaySettingsExtension::Color color); bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkConfig(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for entries. */ struct Limit { /// Out of range reminder count limits. static constexpr IntRange repRangeReminder() { return {1, 10}; } /// Repeater range check interval limits. static constexpr TimeRange rangeCheckInterval() { return TimeRange{Interval::fromSeconds(1), Interval::fromSeconds(50)}; } /// Repeater reconnection count limits. static constexpr IntRange repeaterReconnections() { return {3,5}; } /// Auto-roaming interval limits. static constexpr TimeRange autoRoamingInterval() { return TimeRange{Interval::fromMinutes(1), Interval::fromMinutes(256)}; } /// Auto-roaming delay limits. static constexpr TimeRange autoRoamDelay() { return TimeRange{Interval::fromSeconds(0), Interval::fromSeconds(30)}; } /// Mute-timer limits. static constexpr TimeRange muteTimer() { return TimeRange{Interval::fromMinutes(1), Interval::fromMinutes(256)}; } }; protected: /** Some internal offset within the codeplug element. */ struct Offset { /// @cond DO_NOT_DOCUEMNT static constexpr unsigned int sendTalkerAlias() { return 0x0000; } static constexpr unsigned int talkerAliasDisplay() { return 0x0001; } static constexpr unsigned int talkerAliasEncoding() { return 0x0002; } static constexpr unsigned int fontColor() { return 0x0003; } static constexpr unsigned int customChannelBackground() { return 0x0004; } static constexpr unsigned int defaultRoamingZone() { return 0x0005; } static constexpr unsigned int roaming() { return 0x0006; } static constexpr unsigned int repRangeCheck() { return 0x0007; } static constexpr unsigned int repRangeAlert() { return 0x0008; } static constexpr unsigned int repRangeReminder() { return 0x0009; } static constexpr unsigned int rangeCheckInterval() { return 0x000a; } static constexpr unsigned int rangeCheckCount() { return 0x000b; } static constexpr unsigned int roamStartCondition() { return 0x000c; } static constexpr unsigned int autoRoamPeriod() { return 0x000d; } static constexpr unsigned int autoRoamDelay() { return 0x000e; } static constexpr unsigned int roamReturnCondition() { return 0x000f; } static constexpr unsigned int muteDelay() { return 0x0010; } static constexpr unsigned int encryptionType() { return 0x0011; } static constexpr unsigned int zoneANameColor() { return 0x0012; } static constexpr unsigned int zoneBNameColor() { return 0x0013; } static constexpr unsigned int channelANameColor() { return 0x0014; } static constexpr unsigned int channelBNameColor() { return 0x0015; } /// @endcond }; }; /** Implements the channel element for the BTECH DMR-6X2UV. * Extends the AnytoneCodeplug::ChannelElement by the device specific features, like multiple * scan lists associated with the channel. * * Memory representation of the encoded channel element (size 0x040 bytes): * @verbinclude dmr6x2uv_channel.txt */ class ChannelElement: public AnytoneCodeplug::ChannelElement { public: /** Possible PTT modes for FM APRS. */ enum class FMAPRSPTTMode { Off = 0, Start = 1, End = 2 }; /** Possible APRS report types. */ enum class APRSType{ Off = 0, FM = 1, DMR = 2 }; protected: /** Hidden constructor. */ ChannelElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ ChannelElement(uint8_t *ptr); /** Returns @c true, if the first scan list index is set. */ bool hasScanListIndex() const; /** Returns the first scan list index (0-based). */ unsigned scanListIndex() const; /** Sets the first scan list index (0-based). */ void setScanListIndex(unsigned idx); /** Clears the first scan list index. */ void clearScanListIndex(); /** Returns @c true, if the n-th scan list index is set (n=0,...,7). */ virtual bool hasScanListIndex(unsigned int n) const; /** Returns the n-th scan list index (0-based, n=0,...,7). */ virtual unsigned scanListIndex(unsigned int n) const; /** Sets the n-th scan list index (0-based, n=0,...,7). */ virtual void setScanListIndex(unsigned int n, unsigned idx); /** Clears the n-th scan list index (n=0,...,7). */ virtual void clearScanListIndex(unsigned int n); /** Returns @c true if roaming is enabled for this channel. */ virtual bool roamingEnabled() const; /** Enables/disables roaming. */ virtual void enableRoaming(bool enable); /** Returns @c true, if ranging is enabled. */ virtual bool ranging() const; /** Enables/disables ranging. */ virtual void enableRanging(bool enable); /** Returns the DMR APRS report channel index. */ virtual unsigned int dmrAPRSChannelIndex() const; /** Sets the DMR APRS report channel index. */ virtual void setDMRAPRSChannelIndex(unsigned int idx); /** Returns @c true, if the reception of DMR APRS messages is enabled. */ virtual bool dmrAPRSRXEnabled() const; /** Enables/disables the reception of DMR APRS messages. */ virtual void enableDMRARPSRX(bool enable); /** Returns true, if the position is reported via DMR APRS on PTT. */ virtual bool dmrAPRSPTTEnabled() const; /** Enables/disables reporting the position via DMR APRS on PTT. */ virtual void enableDMRAPRSPTT(bool enable); /** Returns teh FM APRS PTT mode. */ virtual FMAPRSPTTMode fmAPRSPTTMode() const; /** Sets the FM APRS PTT mode. */ virtual void setFMAPRSPTTMode(FMAPRSPTTMode mode); /** Returns the APRS type. */ virtual APRSType aprsType() const; /** Sets the APRS type. */ virtual void setAPRSType(APRSType aprstype); bool linkChannelObj(Channel *c, Context &ctx) const; bool fromChannelObj(const Channel *c, Context &ctx); public: /** Some limits of this element. */ struct Limit { /// Maximum number of scan list indices. static constexpr unsigned int scanListIndices() { return 8; } }; protected: /// @cond DO_NOT_DOCUMENT struct Offset: public Element::Offset { static constexpr Bit roaming() { return {0x001b, 2}; } static constexpr Bit ranging() { return {0x001b, 0}; } static constexpr unsigned int scanListIndices() { return 0x0036; } static constexpr unsigned int betweenScanListIndices() { return 0x0001; } static constexpr unsigned int dmrAPRSChannelIndex() { return 0x003e; } static constexpr Bit dmrAPRSRXEnable() { return {0x003f, 5}; } static constexpr Bit dmrAPRSPTTEnable() { return {0x003f, 4}; } static constexpr Bit fmAPRSPTTMode() { return {0x003f, 2}; } static constexpr Bit aprsType() { return {0x003f, 0}; } }; /// @endcond }; /** Represents the APRS settings within the binary DMR-6X2UV codeplug. * * Memory layout of APRS settings (size 0x00a0 bytes): * @verbinclude dmr6x2uv_aprssetting.txt */ class APRSSettingsElement: public Element { protected: /** Hidden constructor. */ APRSSettingsElement(uint8_t *ptr, unsigned size); /** Possible settings for the FM APRS subtone type. */ enum class SignalingType { Off=0, CTCSS=1, DCS=2 }; public: /** Constructor. */ explicit APRSSettingsElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x00a0; } /** Resets the settings. */ void clear(); bool isValid() const; /** Returns the FM APRS frequency. */ virtual Frequency fmFrequency() const; /** Sets the FM APRS frequency. */ virtual void setFMFrequency(Frequency f); /** Returns the TX delay in ms. */ virtual Interval fmTXDelay() const; /** Sets the TX delay in ms. */ virtual void setFMTXDelay(const Interval intv); /** Returns the sub tone settings. */ virtual SelectiveCall txTone() const; /** Sets the sub tone settings. */ virtual void setTXTone(const SelectiveCall &code); /** Returns the manual TX interval in seconds. */ virtual Interval manualTXInterval() const; /** Sets the manual TX interval in seconds. */ virtual void setManualTXInterval(Interval sec); /** Returns @c true if the auto transmit is enabled. */ virtual bool autoTX() const; /** Returns the auto TX interval in seconds. */ virtual Interval autoTXInterval() const; /** Sets the auto TX interval in seconds. */ virtual void setAutoTXInterval(Interval sec); /** Disables auto tx. */ virtual void disableAutoTX(); /** Returns @c true if a fixed location is send. */ virtual bool fixedLocationEnabled() const; /** Returns the fixed location send. */ virtual QGeoCoordinate fixedLocation() const; /** Sets the fixed location to send. */ virtual void setFixedLocation(QGeoCoordinate &loc); /** Disables sending a fixed location. */ virtual void disableFixedLocation(); /** Returns the destination call. */ virtual QString destination() const; /** Returns the destination SSID. */ virtual unsigned destinationSSID() const; /** Sets the destination call & SSID. */ virtual void setDestination(const QString &call, unsigned ssid); /** Returns the source call. */ virtual QString source() const; /** Returns the source SSID. */ virtual unsigned sourceSSID() const; /** Sets the source call & SSID. */ virtual void setSource(const QString &call, unsigned ssid); /** Returns the path string. */ virtual QString path() const; /** Sets the path string. */ virtual void setPath(const QString &path); /** Returns the APRS icon. */ virtual APRSSystem::Icon icon() const; /** Sets the APRS icon. */ virtual void setIcon(APRSSystem::Icon icon); /** Returns the transmit power. */ virtual Channel::Power power() const; /** Sets the transmit power. */ virtual void setPower(Channel::Power power); /** Returns the pre-wave delay in ms. */ virtual Interval fmPreWaveDelay() const; /** Sets the pre-wave delay in ms. */ virtual void setFMPreWaveDelay(Interval ms); /** Returns @c true if the channel points to the current/selected channel. */ virtual bool dmrChannelIsSelected(unsigned n) const; /** Returns the digital channel index for the n-th system. */ virtual unsigned dmrChannelIndex(unsigned n) const; /** Sets the digital channel index for the n-th system. */ virtual void setDMRChannelIndex(unsigned n, unsigned idx); /** Sets the channel to the current/selected channel. */ virtual void setDMRChannelSelected(unsigned n); /** Returns the destination contact for the n-th system. */ virtual unsigned dmrDestination(unsigned n) const; /** Sets the destination contact for the n-th system. */ virtual void setDMRDestination(unsigned n, unsigned idx); /** Returns the call type for the n-th system. */ virtual DMRContact::Type dmrCallType(unsigned n) const; /** Sets the call type for the n-th system. */ virtual void setDMRCallType(unsigned n, DMRContact::Type type); /** Returns @c true if the n-th system overrides the channel time-slot. */ virtual bool dmrTimeSlotOverride(unsigned n); /** Returns the time slot if overridden (only valid if @c timeSlot returns true). */ virtual DMRChannel::TimeSlot dmrTimeSlot(unsigned n) const; /** Overrides the time slot of the n-th selected channel. */ virtual void setDMRTimeSlot(unsigned n, DMRChannel::TimeSlot ts); /** Clears the time-slot override. */ virtual void clearDMRTimeSlotOverride(unsigned n); /** Returns @c true if the roaming is enabled. */ virtual bool dmrRoaming() const; /** Enables/disables roaming. */ virtual void enableDMRRoaming(bool enable); /** Returns the the repeater activation delay in ms. */ virtual Interval dmrPreWaveDelay() const; /** Sets the repeater activation delay in ms. */ virtual void setDMRPreWaveDelay(Interval ms); /** Configures this APRS system from the given generic config. */ virtual bool fromFMAPRSSystem(const APRSSystem *sys, Context &ctx, const ErrorStack &err=ErrorStack()); /** Constructs a generic APRS system configuration from this APRS system. */ virtual APRSSystem *toFMAPRSSystem(); /** Links the transmit channel within the generic APRS system based on the transmit frequency * defined within this APRS system. */ virtual bool linkFMAPRSSystem(APRSSystem *sys, Context &ctx); /** Constructs all GPS system from the generic configuration. */ virtual bool fromDMRAPRSSystems(Context &ctx); /** Encodes the given GPS system. */ virtual bool fromDMRAPRSSystemObj(unsigned int idx, GPSSystem *sys, Context &ctx); /** Constructs a generic GPS system from the idx-th encoded GPS system. */ virtual GPSSystem *toDMRAPRSSystemObj(int idx) const; /** Links the specified generic GPS system. */ virtual bool linkDMRAPRSSystem(int idx, GPSSystem *sys, Context &ctx) const; public: /** Some static limits for this element. */ struct Limit { /// Maximum length of call signs. static constexpr unsigned int callLength() { return 0x0006; } /// Maximum length of the repeater path string. static constexpr unsigned int pathLength() { return 0x0020; } /// Maximum number of DMR APRS systems. static constexpr unsigned int dmrSystems() { return 0x0008; } }; protected: /** Internal used offsets within the codeplug element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int fmFrequency() { return 0x0001; } static constexpr unsigned int fmTXDelay() { return 0x0005; } static constexpr unsigned int fmSigType() { return 0x0006; } static constexpr unsigned int fmCTCSS() { return 0x0007; } static constexpr unsigned int fmDCS() { return 0x0008; } static constexpr unsigned int manualTXInterval() { return 0x000a; } static constexpr unsigned int autoTXInterval() { return 0x000b; } static constexpr unsigned int fmTXMonitor() { return 0x000c; } static constexpr unsigned int fixedLocation() { return 0x000d; } static constexpr unsigned int fixedLatDeg() { return 0x000e; } static constexpr unsigned int fixedLatMin() { return 0x000f; } static constexpr unsigned int fixedLatSec() { return 0x0010; } static constexpr unsigned int fixedLatSouth() { return 0x0011; } static constexpr unsigned int fixedLonDeg() { return 0x0012; } static constexpr unsigned int fixedLonMin() { return 0x0013; } static constexpr unsigned int fixedLonSec() { return 0x0014; } static constexpr unsigned int fixedLonWest() { return 0x0015; } static constexpr unsigned int destinationCall() { return 0x0016; } static constexpr unsigned int destinationSSID() { return 0x001c; } static constexpr unsigned int sourceCall() { return 0x001d; } static constexpr unsigned int sourceSSID() { return 0x0023; } static constexpr unsigned int path() { return 0x0024; } static constexpr unsigned int symbolTable() { return 0x0039; } static constexpr unsigned int symbol() { return 0x003a; } static constexpr unsigned int fmPower() { return 0x003b; } static constexpr unsigned int fmPrewaveDelay() { return 0x003c; } static constexpr unsigned int dmrChannelIndices() { return 0x0040; } static constexpr unsigned int betweenDMRChannelIndices() { return 0x0002; } static constexpr unsigned int dmrDestinations() { return 0x0050; } static constexpr unsigned int betweenDMRDestinations() { return 0x0004; } static constexpr unsigned int dmrCallTypes() { return 0x0070; } static constexpr unsigned int betweenDMRCallTypes() { return 0x0001; } static constexpr unsigned int roamingSupport() { return 0x0078; } static constexpr unsigned int dmrTimeSlots() { return 0x0079; } static constexpr unsigned int betweenDMRTimeSlots() { return 0x0001; } static constexpr unsigned int dmrPrewaveDelay() { return 0x0081; } /// @endcond }; }; /** Reuse roaming channel bitmap from D878UV. */ typedef D878UVCodeplug::RoamingChannelBitmapElement RoamingChannelBitmapElement ; /** Reuse roaming channel from D878UV. */ typedef D878UVCodeplug::RoamingChannelElement RoamingChannelElement; /** Reuse roaming zone bitmap from D878UV. */ typedef D878UVCodeplug::RoamingZoneBitmapElement RoamingZoneBitmapElement; /** Reuse roaming zone from D878UV. */ typedef D878UVCodeplug::RoamingZoneElement RoamingZoneElement; public: /** Hidden constructor. */ explicit DMR6X2UVCodeplug(const QString &label, QObject *parent=nullptr); public: /** Empty constructor. */ explicit DMR6X2UVCodeplug(QObject *parent=nullptr); protected: bool allocateBitmaps(); void setBitmaps(Context &ctx); void allocateForDecoding(); void allocateForEncoding(); bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack()); bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); void allocateGeneralSettings(); bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()); virtual bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); virtual bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()); virtual bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()); void allocateGPSSystems(); bool encodeGPSSystems(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createGPSSystems(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkGPSSystems(Context &ctx, const ErrorStack &err=ErrorStack()); /** Allocates memory to store all roaming channels and zones. */ virtual void allocateRoaming(); /** Encodes the roaming channels and zones. */ virtual bool encodeRoaming(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Creates roaming channels and zones from codeplug. */ virtual bool createRoaming(Context &ctx, const ErrorStack &err=ErrorStack()); /** Links roaming channels and zones. */ virtual bool linkRoaming(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for the codeplug. */ struct Limit : public D868UVCodeplug::Limit { /// Maximum length of the FM APRS message static constexpr unsigned int fmAPRSMessage() { return 60; } /// Maximum number of roaming channels. static constexpr unsigned int roamingChannels() { return 250; } /// Maximum number of roaming zones. static constexpr unsigned int roamingZones() { return 64; } }; protected: /** Some internal used offsets within the codeplug. */ struct Offset: public D868UVCodeplug::Offset { ///@cond DO_NOT_DOCUMENT static constexpr unsigned int roamingChannelBitmap() { return 0x01042000; } static constexpr unsigned int roamingChannels() { return 0x01040000; } static constexpr unsigned int roamingZoneBitmap() { return 0x01042080; } static constexpr unsigned int roamingZones() { return 0x01043000; } static constexpr unsigned int fmAPRSMessage() { return 0x02501200; } static constexpr unsigned int fmAPRSFrequencyNames() { return 0x02502000; } static constexpr unsigned int settingsExtension() { return 0x02501400; } /// @endcond }; /** Some internal used sizes. */ struct Size: public D868UVCodeplug::Size { ///@cond DO_NOT_DOCUMENT static constexpr unsigned int fmAPRSMessage() { return 0x00000040; } /// @endcond }; }; #endif // DMR6X2UVCODEPLUG_HH qdmr-0.12.3/lib/dmr6x2uv_limits.cc000066400000000000000000000203471501654372000167040ustar00rootroot00000000000000#include "dmr6x2uv_limits.hh" #include "channel.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "zone.hh" #include "scanlist.hh" #include "gpssystem.hh" #include "roamingzone.hh" DMR6X2UVLimits::DMR6X2UVLimits(const std::initializer_list > &rxFreqRanges, const std::initializer_list > &txFreqRanges, const QString &hardwareRevision, QObject *parent) : AnytoneLimits(hardwareRevision, "V102", true, parent) { // Define limits for call-sign DB _hasCallSignDB = true; _callSignDBImplemented = true; _numCallSignDBEntries = 200000; // Define limits for satellite config _hasSatelliteConfig = false; _satelliteConfigImplemented = false; _numSatellites = 0; /* Define limits for the general settings. */ add("settings", new RadioLimitItem{ { "introLine1", new RadioLimitString(-1, 14, RadioLimitString::ASCII) }, { "introLine2", new RadioLimitString(-1, 14, RadioLimitString::ASCII) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, -1) } }); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList { { DMRRadioID::staticMetaObject, 1, 250, new RadioLimitObject { {"name", new RadioLimitString(1,8, RadioLimitString::ASCII) }, {"id", new RadioLimitDMRId()} } } }); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum{ (unsigned)DMRContact::PrivateCall, (unsigned)DMRContact::GroupCall, (unsigned)DMRContact::AllCall }}, { "number", new RadioLimitDMRId() } } }, { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() } }); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "contacts", new RadioLimitGroupCallRefList(1, 64) } })); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, 4000, new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies(rxFreqRanges, true)}, {"txFrequency", new RadioLimitTransmitFrequencies(txFreqRanges)}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRef(APRSSystem::staticMetaObject)}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1,16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies(rxFreqRanges, true)}, {"txFrequency", new RadioLimitTransmitFrequencies(txFreqRanges)}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false)}, {"aprs", new RadioLimitObjRef(PositioningSystem::staticMetaObject, true)}, {"roaming", new RadioLimitObjRef(RoamingZone::staticMetaObject, true) }, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } )); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone( 250, { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions }) ) ); /* Define limits for scan lists. */ add("scanlists", new RadioLimitList( ScanList::staticMetaObject, 0, 250, new RadioLimitObject{ { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "primary", new RadioLimitObjRef(Channel::staticMetaObject, false) }, { "secondary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "revert", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "channels", new RadioLimitRefList(0, 31, Channel::staticMetaObject) } })); /* Handle positioning systems. */ add("positioning", new RadioLimitList{ { GPSSystem::staticMetaObject, 0, 8, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "period", new RadioLimitUInt(0, 7650) }, { "contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false) }, { "revert", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } }, { APRSSystem::staticMetaObject, 0, 1, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "period", new RadioLimitUInt(0, 7650) }, { "revert", new RadioLimitObjRef({SelectedChannel::staticMetaObject, FMChannel::staticMetaObject}, false) }, { "icon", new RadioLimitEnum{} }, { "message", new RadioLimitString(0, 60, RadioLimitString::ASCII) } ///@todo extend APRSSystem to expose other settings as properties. }} } ); /* Handle roaming zones. */ add("roaming", new RadioLimitList(RoamingZone::staticMetaObject, 0, 64, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "channels", new RadioLimitRefList(0, 64, DMRChannel::staticMetaObject) } } ) ); } qdmr-0.12.3/lib/dmr6x2uv_limits.hh000066400000000000000000000010331501654372000167050ustar00rootroot00000000000000#ifndef DMR6X2UVLIMITS_HH #define DMR6X2UVLIMITS_HH #include "anytone_limits.hh" /** Implements the limits for the BTECH DMR-6X2UV. * @ingroup dmr6x2uv */ class DMR6X2UVLimits: public AnytoneLimits { Q_OBJECT public: /** Constructor. */ DMR6X2UVLimits(const std::initializer_list > &rxFreqRanges, const std::initializer_list > &txFreqRanges, const QString &hardwareRevision, QObject *parent=nullptr); }; #endif // D878UVLIMITS_HH qdmr-0.12.3/lib/dr1801uv.cc000066400000000000000000000105271501654372000151170ustar00rootroot00000000000000#include "dr1801uv.hh" #include "dr1801uv_interface.hh" #include "logger.hh" RadioLimits * DR1801UV::_limits = nullptr; DR1801UV::DR1801UV(DR1801UVInterface *device, QObject *parent) : Radio(parent), _device(device), _name("Baofeng DR-1801UV") { // Check if device is open if ((nullptr==_device) || (! _device->isOpen())) { _task = StatusError; return; } } RadioInfo DR1801UV::defaultRadioInfo() { return RadioInfo(RadioInfo::DR1801UV, "dr1801uv", "DR-1801UV", "Baofeng", DR1801UVInterface::interfaceInfo()); } const QString & DR1801UV::name() const { return _name; } const Codeplug & DR1801UV::codeplug() const { return _codeplug; } Codeplug & DR1801UV::codeplug() { return _codeplug; } const RadioLimits & DR1801UV::limits() const { if (nullptr == _limits) _limits = new DR1801UVLimits(); return *_limits; } bool DR1801UV::startDownload(bool blocking, const ErrorStack &err) { if (StatusIdle != _task) return false; _task = StatusDownload; _errorStack = err; if (blocking) { run(); return (StatusIdle == _task); } // If non-blocking -> move device to this thread if (_device && _device->isOpen()) _device->moveToThread(this); start(); return true; } bool DR1801UV::startUpload(Config *config, bool blocking, const Codeplug::Flags &flags, const ErrorStack &err) { if (StatusIdle != _task) return false; if (! (_config = config)) return false; _codeplugFlags = flags; _task = StatusUpload; _errorStack = err; if (blocking) { run(); return (StatusIdle == _task); } // If non-blocking -> move device to this thread if (_device && _device->isOpen()) _device->moveToThread(this); start(); return true; } bool DR1801UV::startUploadCallsignDB(UserDatabase *db, bool blocking, const CallsignDB::Selection &selection, const ErrorStack &err) { Q_UNUSED(db); Q_UNUSED(blocking); Q_UNUSED(selection); errMsg(err) << "This device does not support a call-sign DB."; return false; } bool DR1801UV::startUploadSatelliteConfig(SatelliteDatabase *db, bool blocking, const ErrorStack &err) { Q_UNUSED(db); Q_UNUSED(blocking); errMsg(err) << "Satellite config upload is not implemented yet."; return false; } void DR1801UV::run() { if (StatusDownload == _task) { if ((nullptr==_device) || (! _device->isOpen())) { emit downloadError(this); return; } emit downloadStarted(); if (! download()) { _device->read_finish(); _device->reboot(); _device->close(); _task = StatusError; emit downloadError(this); return; } _task = StatusIdle; _device->reboot(); _device->close(); emit downloadFinished(this, &codeplug()); //_config = nullptr; } else if (StatusUpload == _task) { if ((nullptr==_device) || (! _device->isOpen())) { emit uploadError(this); return; } emit uploadStarted(); if (! upload()) { _device->write_finish(); _device->reboot(); _device->close(); _task = StatusError; emit uploadError(this); return; } _device->write_finish(); _device->reboot(); _device->close(); _task = StatusIdle; emit uploadComplete(this); } else if (StatusUploadCallsigns == _task) { // Not implemented. emit uploadError(this); return; } } bool DR1801UV::download() { if (! _device->readCodeplug(_codeplug, [this](unsigned int n, unsigned int total){ emit downloadProgress(float(n*100)/total); }, _errorStack)) { errMsg(_errorStack) << "Cannot read codeplug from device."; return false; } return true; } bool DR1801UV::upload() { // First, read codeplug from the device if (! _device->readCodeplug(_codeplug, [this](unsigned int n, unsigned int total) { emit uploadProgress(float(n*50)/total); }, _errorStack)) { errMsg(_errorStack) << "Cannot read codeplug."; return false; } // Encode config into codeplug _codeplug.encode(_config, _codeplugFlags); _codeplug.data(0x304)[0] = 0; // Write codeplug back to the device if (! _device->writeCodeplug(_codeplug, [this](unsigned int n, unsigned int total) { emit uploadProgress(50+float(n*50)/total); }, _errorStack)) { errMsg(_errorStack) << "Cannot write codeplug to the device."; } return true; } qdmr-0.12.3/lib/dr1801uv.hh000066400000000000000000000037451501654372000151350ustar00rootroot00000000000000/** @defgroup dr1801uv BTECH DR-1801 UV * * This module collects all classes implementing the codeplug and communication protocol * for the BTECH DR-1801UV. This device is also known as BF-1801 A6 and is a completely different * device than the well known DM-1801. The former uses the Auctu A6 radio-on-a-chip. * * @ingroup auctus */ #ifndef DR1801UV_HH #define DR1801UV_HH #include "radio.hh" #include "dr1801uv_interface.hh" #include "dr1801uv_codeplug.hh" #include "dr1801uv_limits.hh" /** Implements the BTECH DR-1801UV (BF-1801 A6). * * @ingroup dr1801uv */ class DR1801UV : public Radio { Q_OBJECT public: /** Constructs a new instance representig a DR-1801UV. */ explicit DR1801UV(DR1801UVInterface *device=nullptr, QObject *parent=nullptr); public: /** Retunrs the default radio info. */ static RadioInfo defaultRadioInfo(); const QString &name() const; const RadioLimits &limits() const; const Codeplug &codeplug() const; Codeplug &codeplug(); bool startDownload(bool blocking, const ErrorStack &err); bool startUpload(Config *config, bool blocking, const Codeplug::Flags &flags, const ErrorStack &err); bool startUploadCallsignDB(UserDatabase *db, bool blocking, const CallsignDB::Selection &selection, const ErrorStack &err); bool startUploadSatelliteConfig(SatelliteDatabase *db, bool blocking, const ErrorStack &err); protected: /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */ void run(); private: virtual bool download(); virtual bool upload(); protected: /** Owns the interface to the device. */ DR1801UVInterface *_device; /** Holds the device name, once it got identified. */ QString _name; /** The binary codeplug. */ DR1801UVCodeplug _codeplug; /** The generic configuration. */ Config *_config; /** Some codeplug flags. */ Codeplug::Flags _codeplugFlags; private: /** Holds the singleton instance of the radio limits. */ static RadioLimits *_limits; }; #endif // DR1801UV_HH qdmr-0.12.3/lib/dr1801uv_codeplug.cc000066400000000000000000003121521501654372000170000ustar00rootroot00000000000000#include "dr1801uv_codeplug.hh" #include "logger.hh" #include "utils.hh" #include "zone.hh" #include "config.hh" #include "intermediaterepresentation.hh" #include /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::ChannelBankElement * ******************************************************************************************** */ DR1801UVCodeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr) : Element(ptr, ChannelBankElement::size()) { // pass... } void DR1801UVCodeplug::ChannelBankElement::clear() { memset(_data, 0, ChannelBankElement::size()); } unsigned int DR1801UVCodeplug::ChannelBankElement::channelCount() const { return getUInt16_le(Offset::channelCount()); } void DR1801UVCodeplug::ChannelBankElement::setChannelCount(unsigned int count) { count = std::min(Limit::channelCount(), count); setUInt16_le(Offset::channelCount(), count); } DR1801UVCodeplug::ChannelElement DR1801UVCodeplug::ChannelBankElement::channel(unsigned int index) const { return ChannelElement(_data + Offset::channel() + index*ChannelElement::size()); } QString DR1801UVCodeplug::ChannelBankElement::channelName(unsigned int index) const { return readASCII(Offset::channelName() + index*Limit::channelNameLength(), Limit::channelNameLength(), 0x00); } void DR1801UVCodeplug::ChannelBankElement::setChannelName(unsigned int index, const QString &name) { writeASCII(Offset::channelName() + index*Limit::channelNameLength(), name, Limit::channelNameLength(), 0x00); } bool DR1801UVCodeplug::ChannelBankElement::decode(Context &ctx, const ErrorStack &err) const { for (unsigned int i=0,j=0; isetName(channelName(i)); // Add channel to context and config ctx.add(obj, ch.index()); ctx.config()->channelList()->add(obj); } return true; } bool DR1801UVCodeplug::ChannelBankElement::link(Context &ctx, const ErrorStack &err) const { for (unsigned int i=0,j=0; i(ch.index())) { errMsg(err) << "Cannot link channel at index " << i << ". Channel not defined."; return false; } Channel *obj = ctx.get(ch.index()); if (! ch.linkChannelObj(obj, ctx, err)) { errMsg(err) << "Cannot link channel at index " << i << "."; return false; } } return true; } bool DR1801UVCodeplug::ChannelBankElement::encode(Context &ctx, const ErrorStack &err) { unsigned int n = std::min(Limit::channelCount(), ctx.count()); setChannelCount(n); for (unsigned int i=0; i(i), ctx, err)) { errMsg(err) << "Cannot encode channel '" << ctx.get(i) << "' at index " << i << "."; return false; } ch.setIndex(i); setChannelName(i, ctx.get(i)->name()); } return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::ChannelElement * ******************************************************************************************** */ DR1801UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr) : Element(ptr, ChannelElement::size()) { // pass... } bool DR1801UVCodeplug::ChannelElement::isValid() const { return 0xffff != index(); } void DR1801UVCodeplug::ChannelElement::clear() { memset(_data, 0, _size); setIndex(0xffff); } unsigned int DR1801UVCodeplug::ChannelElement::index() const { return getUInt16_le(Offset::index()); } void DR1801UVCodeplug::ChannelElement::setIndex(unsigned int idx) { setUInt16_le(Offset::index(), idx); } DR1801UVCodeplug::ChannelElement::Type DR1801UVCodeplug::ChannelElement::channelType() const { return (Type)getUInt8(Offset::channelType()); } void DR1801UVCodeplug::ChannelElement::setChannelType(Type type) { setUInt8(Offset::channelType(), (uint8_t)type); } Channel::Power DR1801UVCodeplug::ChannelElement::power() const { switch ((Power)getUInt8(Offset::power())) { case Power::Low: return Channel::Power::Low; case Power::High: return Channel::Power::High; } return Channel::Power::Low; } void DR1801UVCodeplug::ChannelElement::setPower(Channel::Power pwr) { switch (pwr) { case Channel::Power::Min: case Channel::Power::Low: setUInt8(Offset::power(), (uint8_t)Power::Low); break; case Channel::Power::Mid: case Channel::Power::High: case Channel::Power::Max: setUInt8(Offset::power(), (uint8_t)Power::High); break; } } Frequency DR1801UVCodeplug::ChannelElement::rxFrequency() const { return Frequency::fromHz(getUInt32_le(Offset::rxFrequency())); } void DR1801UVCodeplug::ChannelElement::setRXFrequency(Frequency MHz) { setUInt32_le(Offset::rxFrequency(), MHz.inHz()); } Frequency DR1801UVCodeplug::ChannelElement::txFrequency() const { return Frequency::fromHz(getUInt32_le(Offset::txFrequency())); } void DR1801UVCodeplug::ChannelElement::setTXFrequency(Frequency MHz) { setUInt32_le(Offset::txFrequency(), MHz.inHz()); } bool DR1801UVCodeplug::ChannelElement::hasTransmitContact() const { return 0 != getUInt16_le(Offset::transmitContactIndex()); } unsigned int DR1801UVCodeplug::ChannelElement::transmitContactIndex() const { return getUInt16_le(Offset::transmitContactIndex())-1; } void DR1801UVCodeplug::ChannelElement::setTransmitContactIndex(unsigned int index) { setUInt16_le(Offset::transmitContactIndex(), index+1); } void DR1801UVCodeplug::ChannelElement::clearTransmitContactIndex() { setUInt16_le(Offset::transmitContactIndex(), 0); } DR1801UVCodeplug::ChannelElement::Admit DR1801UVCodeplug::ChannelElement::admitCriterion() const { return (Admit) getUInt8(Offset::admitCriterion()); } void DR1801UVCodeplug::ChannelElement::setAdmitCriterion(Admit admit) { setUInt8(Offset::admitCriterion(), (uint8_t)admit); } unsigned int DR1801UVCodeplug::ChannelElement::colorCode() const { return getUInt8(Offset::colorCode()); } void DR1801UVCodeplug::ChannelElement::setColorCode(unsigned int cc) { setUInt8(Offset::colorCode(), cc); } DMRChannel::TimeSlot DR1801UVCodeplug::ChannelElement::timeSlot() const { switch ((TimeSlot)getUInt8(Offset::timeSlot())) { case TimeSlot::TS1: return DMRChannel::TimeSlot::TS1; case TimeSlot::TS2: return DMRChannel::TimeSlot::TS2; } return DMRChannel::TimeSlot::TS1; } void DR1801UVCodeplug::ChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) { switch (ts) { case DMRChannel::TimeSlot::TS1: setUInt8(Offset::timeSlot(), (uint8_t)TimeSlot::TS1); break; case DMRChannel::TimeSlot::TS2: setUInt8(Offset::timeSlot(), (uint8_t)TimeSlot::TS2); break; } } bool DR1801UVCodeplug::ChannelElement::hasEncryptionKey() const { return 0 != getUInt8(Offset::encryptionKeyIndex()); } unsigned int DR1801UVCodeplug::ChannelElement::encryptionKeyIndex() const { return getUInt8(Offset::encryptionKeyIndex())-1; } void DR1801UVCodeplug::ChannelElement::setEncryptionKeyIndex(unsigned int index) { setUInt8(Offset::encryptionKeyIndex(), index+1); } void DR1801UVCodeplug::ChannelElement::clearEncryptionKeyIndex() { setUInt8(Offset::encryptionKeyIndex(), 0); } bool DR1801UVCodeplug::ChannelElement::dcdm() const { return getBit(Offset::dcdm().byte, Offset::dcdm().bit); } void DR1801UVCodeplug::ChannelElement::enableDCDM(bool enable) { setBit(Offset::dcdm().byte, Offset::dcdm().bit, enable); } bool DR1801UVCodeplug::ChannelElement::confirmPrivateCall() const { return getBit(Offset::confirmPivateCall().byte, Offset::confirmPivateCall().bit); } void DR1801UVCodeplug::ChannelElement::enablePrivateCallConfirmation(bool enable) { setBit(Offset::confirmPivateCall().byte, Offset::confirmPivateCall().bit, enable); } DR1801UVCodeplug::ChannelElement::SignalingMode DR1801UVCodeplug::ChannelElement::signalingMode() const { return (SignalingMode) getUInt8(Offset::signalingMode()); } void DR1801UVCodeplug::ChannelElement::setSignalingMode(SignalingMode mode) { setUInt8(Offset::signalingMode(), (uint8_t)mode); } bool DR1801UVCodeplug::ChannelElement::hasAlarmSystem() const { return 0 != getUInt8(Offset::alarmSystemIndex()); } unsigned int DR1801UVCodeplug::ChannelElement::alarmSystemIndex() const { return getUInt8(Offset::alarmSystemIndex())-1; } void DR1801UVCodeplug::ChannelElement::setAlarmSystemIndex(unsigned int index) { setUInt8(Offset::alarmSystemIndex(), index+1); } void DR1801UVCodeplug::ChannelElement::clearAlarmSystemIndex() { setUInt8(Offset::alarmSystemIndex(), 0); } FMChannel::Bandwidth DR1801UVCodeplug::ChannelElement::bandwidth() const { switch ((Bandwidth)getUInt8(Offset::bandwidth())) { case Bandwidth::Narrow: return FMChannel::Bandwidth::Narrow; case Bandwidth::Wide: return FMChannel::Bandwidth::Wide; } return FMChannel::Bandwidth::Narrow; } void DR1801UVCodeplug::ChannelElement::setBandwidth(FMChannel::Bandwidth bw) { switch (bw){ case FMChannel::Bandwidth::Narrow: setUInt8(Offset::bandwidth(), (uint8_t)Bandwidth::Narrow); break; case FMChannel::Bandwidth::Wide: setUInt8(Offset::bandwidth(), (uint8_t)Bandwidth::Wide); break; } } bool DR1801UVCodeplug::ChannelElement::autoScanEnabled() const { return 0x01 == getUInt8(Offset::autoScan()); } void DR1801UVCodeplug::ChannelElement::enableAutoScan(bool enable) { setUInt8(Offset::autoScan(), enable ? 0x01 : 0x00); } bool DR1801UVCodeplug::ChannelElement::hasScanList() const { return 0x00 != getUInt8(Offset::scanListIndex()); } unsigned int DR1801UVCodeplug::ChannelElement::scanListIndex() const { return getUInt8(Offset::scanListIndex())-1; } void DR1801UVCodeplug::ChannelElement::setScanListIndex(unsigned int index) { setUInt8(Offset::scanListIndex(), index+1); } void DR1801UVCodeplug::ChannelElement::clearScanListIndex() { setUInt8(Offset::scanListIndex(), 0); } SelectiveCall DR1801UVCodeplug::ChannelElement::rxTone() const { uint16_t ctcss_dcs = getUInt16_le(Offset::rxSubtoneCode()); SubToneType type = (SubToneType)getUInt8(Offset::rxSubtoneType()); DCSMode dcsMode = (DCSMode)getUInt8(Offset::rxDCSMode()); switch (type) { case SubToneType::None: return SelectiveCall(); case SubToneType::CTCSS: return SelectiveCall(float(ctcss_dcs)/10); case SubToneType::DCS: return SelectiveCall(ctcss_dcs, DCSMode::Inverted == dcsMode); } return SelectiveCall(); } void DR1801UVCodeplug::ChannelElement::setRXTone(const SelectiveCall &code) { uint16_t ctcss_dcs = 0; SubToneType type = SubToneType::None; DCSMode dcsMode = DCSMode::Normal; if (code.isCTCSS()) { type = SubToneType::CTCSS; ctcss_dcs = code.Hz()*10; } else if (code.isDCS()) { type = SubToneType::DCS; ctcss_dcs = code.octalCode(); dcsMode = code.isInverted() ? DCSMode::Inverted : DCSMode::Normal; } setUInt16_le(Offset::rxSubtoneCode(), ctcss_dcs); setUInt8(Offset::rxSubtoneType(), (uint8_t)type); setUInt8(Offset::rxDCSMode(), (uint8_t)dcsMode); } SelectiveCall DR1801UVCodeplug::ChannelElement::txTone() const { uint16_t ctcss_dcs = getUInt16_le(Offset::txSubtoneCode()); SubToneType type = (SubToneType)getUInt8(Offset::txSubtoneType()); DCSMode dcsMode = (DCSMode)getUInt8(Offset::txDCSMode()); switch (type) { case SubToneType::None: return SelectiveCall(); case SubToneType::CTCSS: return SelectiveCall(float(ctcss_dcs)/10); case SubToneType::DCS: return SelectiveCall(ctcss_dcs, DCSMode::Inverted == dcsMode); } return SelectiveCall(); } void DR1801UVCodeplug::ChannelElement::setTXTone(const SelectiveCall &code) { uint16_t ctcss_dcs = 0; SubToneType type = SubToneType::None; DCSMode dcsMode = DCSMode::Normal; if (code.isCTCSS()) { type = SubToneType::CTCSS; ctcss_dcs = code.Hz()*10; } else if (code.isDCS()) { type = SubToneType::DCS; ctcss_dcs = code.octalCode(); dcsMode = code.isInverted() ? DCSMode::Inverted : DCSMode::Normal; } setUInt16_le(Offset::txSubtoneCode(), ctcss_dcs); setUInt8(Offset::txSubtoneType(), (uint8_t)type); setUInt8(Offset::txDCSMode(), (uint8_t)dcsMode); } bool DR1801UVCodeplug::ChannelElement::talkaround() const { return getBit(Offset::talkaround(), 7); } void DR1801UVCodeplug::ChannelElement::enableTalkaround(bool enable) { setBit(Offset::talkaround(), 7, enable); } bool DR1801UVCodeplug::ChannelElement::hasPTTID() const { return 0 != getUInt8(Offset::pttIDIndex()); } unsigned int DR1801UVCodeplug::ChannelElement::pttIDIndex() const { return getUInt8(Offset::pttIDIndex())-1; } void DR1801UVCodeplug::ChannelElement::setPTTIDIndex(unsigned int idx) { setUInt8(Offset::pttIDIndex(), idx+1); } void DR1801UVCodeplug::ChannelElement::clearPTTID() { setUInt8(Offset::pttIDIndex(), 0); } bool DR1801UVCodeplug::ChannelElement::hasGroupList() const { return 0 != getUInt8(Offset::groupListIndex()); } unsigned int DR1801UVCodeplug::ChannelElement::groupListIndex() const { return getUInt8(Offset::groupListIndex())-1; } void DR1801UVCodeplug::ChannelElement::setGroupListIndex(unsigned int index) { setUInt8(Offset::groupListIndex(), index+1); } void DR1801UVCodeplug::ChannelElement::clearGroupListIndex() { setUInt8(Offset::groupListIndex(), 0); } bool DR1801UVCodeplug::ChannelElement::loneWorker() const { return 0x01 == getUInt8(Offset::loneWorker()); } void DR1801UVCodeplug::ChannelElement::enableLoneWorker(bool enable) { setUInt8(Offset::loneWorker(), enable ? 0x01 : 0x00); } Channel * DR1801UVCodeplug::ChannelElement::toChannelObj(Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx); Channel *ch = nullptr; if (Type::FM == channelType()) { FMChannel *fm = new FMChannel(); ch = fm; switch (admitCriterion()) { case Admit::Always: fm->setAdmit(FMChannel::Admit::Always); break; case Admit::ColorCode_or_Tone: fm->setAdmit(FMChannel::Admit::Tone); break; case Admit::ChannelFree: fm->setAdmit(FMChannel::Admit::Free); break; } fm->setBandwidth(bandwidth()); fm->setRXTone(rxTone()); fm->setTXTone(txTone()); } else if (Type::DMR == channelType()) { DMRChannel *dmr = new DMRChannel(); ch = dmr; switch (admitCriterion()) { case Admit::Always: dmr->setAdmit(DMRChannel::Admit::Always); break; case Admit::ColorCode_or_Tone: dmr->setAdmit(DMRChannel::Admit::ColorCode); break; case Admit::ChannelFree: dmr->setAdmit(DMRChannel::Admit::Free); break; } dmr->setColorCode(colorCode()); dmr->setTimeSlot(timeSlot()); } else { errMsg(err) << "Unknonwn channel type " << (uint8_t)channelType() << "."; return nullptr; } ch->setPower(power()); ch->setRXFrequency(rxFrequency()); ch->setTXFrequency(txFrequency()); return ch; } bool DR1801UVCodeplug::ChannelElement::linkChannelObj(Channel *channel, Context &ctx, const ErrorStack &err) const { // Common references if (hasScanList()) { if (! ctx.has(scanListIndex())) { errMsg(err) << "Scanlist with index " << scanListIndex() << " not known."; return false; } channel->setScanList(ctx.get(scanListIndex())); } // Handle DMR specifc references if (DMRChannel *dmr = channel->as()) { if (hasTransmitContact()) { if (! ctx.has(transmitContactIndex())) { errMsg(err) << "DMR contact with index " << transmitContactIndex() << " not known."; return false; } dmr->setTXContactObj(ctx.get(transmitContactIndex())); } if (hasGroupList()) { if (! ctx.has(groupListIndex())) { errMsg(err) << "Group list with index " << groupListIndex() << " not known."; return false; } dmr->setGroupListObj(ctx.get(groupListIndex())); } } return true; } bool DR1801UVCodeplug::ChannelElement::encode(Channel *channel, Context &ctx, const ErrorStack &err) { Q_UNUSED(err); // Encode common properties setPower(channel->power()); setRXFrequency(channel->rxFrequency()); setTXFrequency(channel->txFrequency()); if (channel->scanList()) setScanListIndex(ctx.index(channel->scanList())); else clearScanListIndex(); // Encode type specific settings if (channel->is()) { FMChannel *fm = channel->as(); setChannelType(Type::FM); setBandwidth(fm->bandwidth()); setRXTone(fm->rxTone()); setTXTone(fm->txTone()); switch (fm->admit()) { case FMChannel::Admit::Always: setAdmitCriterion(Admit::Always); break; case FMChannel::Admit::Free: setAdmitCriterion(Admit::ChannelFree); break; case FMChannel::Admit::Tone: setAdmitCriterion(Admit::ColorCode_or_Tone); break; } } else if (channel->is()) { DMRChannel *dmr = channel->as(); setChannelType(Type::DMR); setBandwidth(FMChannel::Bandwidth::Narrow); if (dmr->txContactObj()) setTransmitContactIndex(ctx.index(dmr->txContactObj())); else clearTransmitContactIndex(); switch (dmr->admit()) { case DMRChannel::Admit::Always: setAdmitCriterion(Admit::Always); break; case DMRChannel::Admit::Free: setAdmitCriterion(Admit::ChannelFree); break; case DMRChannel::Admit::ColorCode: setAdmitCriterion(Admit::ColorCode_or_Tone); break; } setColorCode(dmr->colorCode()); setTimeSlot(dmr->timeSlot()); if (dmr->commercialExtension() && dmr->commercialExtension()->encryptionKey()) setEncryptionKeyIndex(ctx.index(dmr->commercialExtension()->encryptionKey())); else clearEncryptionKeyIndex(); if (dmr->groupListObj()) setGroupListIndex(ctx.index(dmr->groupListObj())); else clearGroupListIndex(); } return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::ContactBankElement * ******************************************************************************************** */ DR1801UVCodeplug::ContactBankElement::ContactBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::ContactBankElement::ContactBankElement(uint8_t *ptr) : Element(ptr, ContactBankElement::size()) { // pass... } void DR1801UVCodeplug::ContactBankElement::clear() { memset(_data, 0, ContactBankElement::size()); } unsigned int DR1801UVCodeplug::ContactBankElement::contactCount() const { return getUInt16_le(Offset::contactCount()); } void DR1801UVCodeplug::ContactBankElement::setContactCount(unsigned int count) { count = std::min(Limit::contactCount(), count); setUInt16_le(Offset::contactCount(), count); } unsigned int DR1801UVCodeplug::ContactBankElement::firstIndex() const { return getUInt16_le(Offset::firstIndex())-1; } void DR1801UVCodeplug::ContactBankElement::setFirstIndex(unsigned int index) { setUInt16_le(Offset::firstIndex(), index+1); } DR1801UVCodeplug::ContactElement DR1801UVCodeplug::ContactBankElement::contact(unsigned int index) const { return ContactElement(_data + Offset::contacts() + index*ContactElement::size()); } bool DR1801UVCodeplug::ContactBankElement::decode(Context &ctx, const ErrorStack &err) const { // Get first element in list unsigned int currentIndex = firstIndex(); ContactElement currentContact = contact(currentIndex); for (unsigned int i=0; icontacts()->add(obj); // continue with successor if (currentContact.hasSuccessor()) { currentIndex = currentContact.successorIndex(); currentContact = contact(currentIndex); } } return true; } bool DR1801UVCodeplug::ContactBankElement::link(Context &ctx, const ErrorStack &err) const { if (0 == contactCount()) return true; unsigned int currentIndex = firstIndex(); for (unsigned int i=0; i(currentIndex)) { errMsg(err) << "Cannot link contact at index " << currentIndex << ", not defined."; } DMRContact *obj = ctx.get(currentIndex); if (! currentContact.linkContactObj(obj, ctx, err)) { errMsg(err) << "Cannot link contact element at index " << currentIndex; return false; } currentIndex = currentContact.successorIndex(); } return true; } bool DR1801UVCodeplug::ContactBankElement::encode(Context &ctx, const ErrorStack &err) { unsigned int n = std::min(Limit::contactCount(), ctx.count()); setContactCount(n); setFirstIndex(0); for (unsigned int i=0; icontact(i); if (! contact.encode(ctx.get(i), ctx, err)) { errMsg(err) << "Cannot encode contact '" << ctx.get(i)->name() << "' at index " << i << "."; return false; } if ((i+1) < n) contact.setSuccessorIndex(i+1); else contact.clearSuccessorIndex(); } return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::ContactElement * ******************************************************************************************** */ DR1801UVCodeplug::ContactElement::ContactElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::ContactElement::ContactElement(uint8_t *ptr) : Element(ptr, ContactElement::size()) { // pass... } bool DR1801UVCodeplug::ContactElement::isValid() const { return 0 != getUInt8(Offset::nameLength()) && // has name 0 != getUInt24_le(0x0004) && // has DMR ID 0 != getUInt8(0x0007); // has call type } void DR1801UVCodeplug::ContactElement::clear() { memset(_data, 0, _size); } bool DR1801UVCodeplug::ContactElement::hasSuccessor() const { return 0x0000 != getUInt16_le(Offset::successorIndex()); } uint16_t DR1801UVCodeplug::ContactElement::successorIndex() const { return getUInt16_le(Offset::successorIndex())-1; } void DR1801UVCodeplug::ContactElement::setSuccessorIndex(uint16_t index) { setUInt16_le(Offset::successorIndex(), index+1); } void DR1801UVCodeplug::ContactElement::clearSuccessorIndex() { setUInt16_le(Offset::successorIndex(), 0); } uint32_t DR1801UVCodeplug::ContactElement::dmrID() const { if (DMRContact::AllCall == type()) return 0xffffff; return getUInt24_le(Offset::dmrID()); } void DR1801UVCodeplug::ContactElement::setDMRID(uint32_t id) { setUInt24_le(Offset::dmrID(), id); } DMRContact::Type DR1801UVCodeplug::ContactElement::type() const { switch ((CallType)getUInt8(Offset::callType())) { case CallType::AllCall: return DMRContact::AllCall; case CallType::PrivateCall: return DMRContact::PrivateCall; case CallType::GroupCall: return DMRContact::GroupCall; } return DMRContact::PrivateCall; } void DR1801UVCodeplug::ContactElement::setCallType(DMRContact::Type type) { switch (type) { case DMRContact::AllCall: setUInt8(Offset::callType(), (uint8_t)CallType::AllCall); setDMRID(0xffffff); break; case DMRContact::PrivateCall: setUInt8(Offset::callType(), (uint8_t)CallType::PrivateCall); break; case DMRContact::GroupCall: setUInt8(Offset::callType(), (uint8_t)CallType::GroupCall); break; } } QString DR1801UVCodeplug::ContactElement::name() const { return readASCII(Offset::name(), getUInt8(Offset::nameLength()), 0x00); } void DR1801UVCodeplug::ContactElement::setName(const QString &name) { uint8_t len = std::min(Limit::nameLength(), (unsigned int)name.size()); setUInt8(Offset::nameLength(), len); writeASCII(Offset::name(), name, len, 0x00); } DMRContact * DR1801UVCodeplug::ContactElement::toContactObj(Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx); Q_UNUSED(err) return new DMRContact(type(), name(), dmrID()); } bool DR1801UVCodeplug::ContactElement::linkContactObj(DMRContact *contact, Context &ctx, const ErrorStack &err) { Q_UNUSED(contact); Q_UNUSED(ctx); Q_UNUSED(err); return true; } bool DR1801UVCodeplug::ContactElement::encode(DMRContact *contact, Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); setName(contact->name()); setCallType(contact->type()); setDMRID(contact->number()); return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::GroupListBankElement * ******************************************************************************************** */ DR1801UVCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr) : Element(ptr, GroupListBankElement::size()) { // pass... } void DR1801UVCodeplug::GroupListBankElement::clear() { memset(_data, 0, _size); } unsigned int DR1801UVCodeplug::GroupListBankElement::groupListCount() const { return getUInt8(Offset::groupListCount()); } void DR1801UVCodeplug::GroupListBankElement::setGroupListCount(unsigned int count) { count = std::min(Limit::groupListCount(), count); setUInt8(Offset::groupListCount(), count); } DR1801UVCodeplug::GroupListElement DR1801UVCodeplug::GroupListBankElement::groupList(unsigned int index) const { return GroupListElement(_data + Offset::groupLists() + index*GroupListElement::size()); } bool DR1801UVCodeplug::GroupListBankElement::decode(Context &ctx, const ErrorStack &err) const { for (unsigned int i=0,j=0; irxGroupLists()->add(obj); } return true; } bool DR1801UVCodeplug::GroupListBankElement::link(Context &ctx, const ErrorStack &err) const { for (unsigned int i=0,j=0; i(gl.index())) { errMsg(err) << "Cannot link group list at index " << i << ". Group list not defined."; return false; } if (! gl.linkGroupListObj(ctx.get(gl.index()), ctx, err)) { errMsg(err) << "Cannot link group list at index " << i << "."; return false; } } return true; } bool DR1801UVCodeplug::GroupListBankElement::encode(Context &ctx, const ErrorStack &err) { unsigned int n = std::min(Limit::groupListCount(), ctx.count()); setGroupListCount(n); for (unsigned int i=0; i(i), ctx, err)) { errMsg(err) << "Cannot encode group list '" << ctx.get(i)->name() << "' at index " << i << "."; return false; } lst.setIndex(i); } return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::GroupListElement * ******************************************************************************************** */ DR1801UVCodeplug::GroupListElement::GroupListElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::GroupListElement::GroupListElement(uint8_t *ptr) : Element(ptr, GroupListElement::size()) { // pass... } void DR1801UVCodeplug::GroupListElement::clear() { memset(_data, 0, _size); } bool DR1801UVCodeplug::GroupListElement::isValid() const { return 0 != getUInt16_le(Offset::index()); } unsigned int DR1801UVCodeplug::GroupListElement::index() const { return getUInt16_le(Offset::index())-1; } void DR1801UVCodeplug::GroupListElement::setIndex(unsigned int index) { setUInt16_le(Offset::index(), index+1); } unsigned int DR1801UVCodeplug::GroupListElement::count() const { return getUInt16_le(Offset::count()); } void DR1801UVCodeplug::GroupListElement::setCount(unsigned int n) { n = std::min(Limit::members(), n); setUInt16_le(Offset::count(), n); } bool DR1801UVCodeplug::GroupListElement::hasMemberIndex(unsigned int n) const { return 0 != getUInt16_le(Offset::members() + n*0x02); } unsigned int DR1801UVCodeplug::GroupListElement::memberIndex(unsigned int n) const { return getUInt16_le(Offset::members() + n*0x02) - 1; } void DR1801UVCodeplug::GroupListElement::setMemberIndex(unsigned int n, unsigned int index) { setUInt16_le(Offset::members() + n*0x02, index+1); } void DR1801UVCodeplug::GroupListElement::clearMemberIndex(unsigned int n) { setUInt16_le(Offset::members() + n*0x02, 0); } RXGroupList * DR1801UVCodeplug::GroupListElement::toGroupListObj(Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx); Q_UNUSED(err); // Simply derive name from index return new RXGroupList(QString("Group List %1").arg(index()+1)); } bool DR1801UVCodeplug::GroupListElement::linkGroupListObj(RXGroupList *list, Context &ctx, const ErrorStack &err) const { if (! isValid()) return false; for (unsigned int i=0; i(memberIndex(i))) { errMsg(err) << "Member index " << memberIndex(i) << " is not known."; return false; } list->addContact(ctx.get(memberIndex(i))); } return true; } bool DR1801UVCodeplug::GroupListElement::encode(RXGroupList *list, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) unsigned int n = std::min(Limit::members(), (unsigned int)list->count()); setCount(n); for (unsigned int i=0; icontact(i))); } return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::ZoneBankElement * ******************************************************************************************** */ DR1801UVCodeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr) : Element(ptr, ZoneBankElement::size()) { // pass... } void DR1801UVCodeplug::ZoneBankElement::clear() { memset(_data, 0, _size); } unsigned int DR1801UVCodeplug::ZoneBankElement::zoneCount() const { return getUInt8(Offset::zoneCount()); } void DR1801UVCodeplug::ZoneBankElement::setZoneCount(unsigned int count) { count = std::min(Limit::zoneCount(), count); setUInt8(Offset::zoneCount(), count); } unsigned int DR1801UVCodeplug::ZoneBankElement::upZoneIndex() const { return getUInt16_le(Offset::upZoneIndex()); } void DR1801UVCodeplug::ZoneBankElement::setUpZoneIndex(unsigned int index) { setUInt16_le(Offset::upZoneIndex(), index); } unsigned int DR1801UVCodeplug::ZoneBankElement::downZoneIndex() const { return getUInt16_le(Offset::downZoneIndex()); } void DR1801UVCodeplug::ZoneBankElement::setDownZoneIndex(unsigned int index) { setUInt16_le(Offset::downZoneIndex(), index); } DR1801UVCodeplug::ZoneElement DR1801UVCodeplug::ZoneBankElement::zone(unsigned int index) const { return ZoneElement(_data + Offset::zones() + index*ZoneElement::size()); } bool DR1801UVCodeplug::ZoneBankElement::decode(Context &ctx, const ErrorStack &err) const { for (unsigned int i=0,j=0; izone(i)); if (! zone.isValid()) continue; j++; Zone *obj = zone.toZoneObj(ctx, err); if (nullptr == obj) { errMsg(err) << "Cannot create zone at index " << i << "."; return false; } // Store zone in context ctx.add(obj, zone.index()); // Store zone in config ctx.config()->zones()->add(obj); } return true; } bool DR1801UVCodeplug::ZoneBankElement::link(Context &ctx, const ErrorStack &err) const { for (unsigned int i=0, j=0; izone(i)); if (! zone.isValid()) continue; j++; if (! ctx.has(zone.index())) { errMsg(err) << "Cannot link zone at index " << i << ", not defined."; return false; } if (! zone.linkZoneObj(ctx.get(zone.index()), ctx, err)) { errMsg(err) << "Cannot link zone at index " << i << "."; return false; } } return true; } bool DR1801UVCodeplug::ZoneBankElement::encode(Context &ctx, const ErrorStack &err) { setZoneCount(ctx.count()); for (unsigned int i=0; i(); i++) { ZoneElement zone = this->zone(i); if (! zone.encode(ctx.get(i), ctx, err)) { errMsg(err) << "Cannot encode zone bank."; return false; } zone.setIndex(i); } return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::ZoneElement * ******************************************************************************************** */ DR1801UVCodeplug::ZoneElement::ZoneElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::ZoneElement::ZoneElement(uint8_t *ptr) : Element(ptr, ZoneElement::size()) { // pass... } void DR1801UVCodeplug::ZoneElement::clear() { memset(_data, 0, _size); } bool DR1801UVCodeplug::ZoneElement::isValid() const { // Read name-length and channel count return (0 != getUInt8(Offset::nameLength())) && (0 != getUInt8(Offset::numEntries())); } QString DR1801UVCodeplug::ZoneElement::name() const { uint8_t n = getUInt8(Offset::nameLength()); return readASCII(Offset::name(), n, 0x00); } void DR1801UVCodeplug::ZoneElement::setName(const QString &name) { uint8_t n = std::min(32, name.length()); setUInt8(Offset::nameLength(), n); writeASCII(Offset::name(), name, Limit::nameLength(), 0x00); } unsigned int DR1801UVCodeplug::ZoneElement::index() const { return getUInt16_le(Offset::index()); } void DR1801UVCodeplug::ZoneElement::setIndex(unsigned int index) { setUInt16_le(Offset::index(), index); } unsigned int DR1801UVCodeplug::ZoneElement::numEntries() const { return getUInt8(Offset::numEntries()); } unsigned int DR1801UVCodeplug::ZoneElement::entryIndex(unsigned int n) { n = std::min(Limit::memberCount(), n); return getUInt16_le(Offset::members()+2*n); } void DR1801UVCodeplug::ZoneElement::setEntryIndex(unsigned int n, unsigned int index) { n = std::min(Limit::memberCount(), n); setUInt16_le(Offset::members() + 2*n, index); } Zone * DR1801UVCodeplug::ZoneElement::toZoneObj(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) if (! isValid()) { errMsg(err) << "Cannot create zone from invalid zone element."; return nullptr; } return new Zone(name()); } bool DR1801UVCodeplug::ZoneElement::linkZoneObj(Zone *obj, Context &ctx, const ErrorStack &err) { if (! isValid()) { errMsg(err) << "Cannot link zone using invalid zone element."; return false; } for (unsigned int i=0; i(entryIndex(i))) { errMsg(err) << "Channel with index " << entryIndex(i) << " not defined."; return false; } obj->A()->add(ctx.get(entryIndex(i))); } return true; } bool DR1801UVCodeplug::ZoneElement::encode(Zone *zone, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) setName(zone->name()); unsigned int n = std::min(Limit::memberCount(), (unsigned int)zone->A()->count()); setUInt8(Offset::numEntries(), n); for (unsigned int i=0; iA()->get(i)->as())); } return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::SettingsElement * ******************************************************************************************** */ DR1801UVCodeplug::SettingsElement::SettingsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::SettingsElement::SettingsElement(uint8_t *ptr) : Element(ptr, SettingsElement::size()) { // psas... } void DR1801UVCodeplug::SettingsElement::clear() { memset(_data, 0, _size); } unsigned int DR1801UVCodeplug::SettingsElement::dmrID() const { return getUInt24_le(Offset::dmrID()); } void DR1801UVCodeplug::SettingsElement::setDMRID(unsigned int id) { setUInt24_le(Offset::dmrID(), id); } DR1801UVCodeplug::SettingsElement::PowerSaveMode DR1801UVCodeplug::SettingsElement::powerSaveMode() const { if (0x00 == getUInt8(Offset::powerSaveEnabled())) { // Power save disabled return PowerSaveMode::Off; } return (PowerSaveMode)getUInt8(Offset::powerSaveMode()); } void DR1801UVCodeplug::SettingsElement::setPowerSaveMode(PowerSaveMode mode) { setUInt8(Offset::powerSaveEnabled(), PowerSaveMode::Off != mode ? 0x01 : 0x00); setUInt8(Offset::powerSaveMode(), (uint8_t) mode); } unsigned int DR1801UVCodeplug::SettingsElement::voxSensitivity() const { if (0x00 == getUInt8(Offset::voxEnabled())) { return 0; } return getUInt8(Offset::voxSensitivity())*10/3; } void DR1801UVCodeplug::SettingsElement::setVOXSensitivity(unsigned int sens) { if (0 == sens) { setUInt8(Offset::voxEnabled(), 0x00); } else { setUInt8(Offset::voxEnabled(), 0x01); setUInt8(Offset::voxSensitivity(), 1+sens*2/10); } } unsigned int DR1801UVCodeplug::SettingsElement::voxDelay() const { return getUInt8(Offset::voxDelay())*500; } void DR1801UVCodeplug::SettingsElement::setVOXDelay(unsigned int ms) { setUInt8(Offset::voxDelay(), ms/500); } bool DR1801UVCodeplug::SettingsElement::encryptionEnabled() const { return 0x01 == getUInt8(Offset::encryptionEnabled()); } void DR1801UVCodeplug::SettingsElement::enableEncryption(bool enable) { setUInt8(Offset::encryptionEnabled(), enable ? 0x01 : 0x00); } bool DR1801UVCodeplug::SettingsElement::keyLockEnabled() const { return 0x01 == getUInt8(Offset::keyLockEnabled()); } void DR1801UVCodeplug::SettingsElement::enableKeyLock(bool enable) { setUInt8(Offset::keyLockEnabled(), enable ? 0x01 : 0x00); } unsigned int DR1801UVCodeplug::SettingsElement::keyLockDelay() const { return getUInt8(Offset::keyLockDelay()); } void DR1801UVCodeplug::SettingsElement::setKeyLockDelay(unsigned int sec) { setUInt8(Offset::keyLockDelay(), sec); } bool DR1801UVCodeplug::SettingsElement::lockSideKey1() const { return getBit(Offset::lockSideKey1().byte, Offset::lockSideKey1().bit); } void DR1801UVCodeplug::SettingsElement::enableLockSideKey1(bool enable) { setBit(Offset::lockSideKey1().byte, Offset::lockSideKey1().bit, enable); } bool DR1801UVCodeplug::SettingsElement::lockSideKey2() const { return getBit(Offset::lockSideKey2().byte, Offset::lockSideKey2().bit); } void DR1801UVCodeplug::SettingsElement::enableLockSideKey2(bool enable) { setBit(Offset::lockSideKey2().byte, Offset::lockSideKey2().bit, enable); } bool DR1801UVCodeplug::SettingsElement::lockPTT() const { return getBit(Offset::lockPTT().byte, Offset::lockPTT().bit); } void DR1801UVCodeplug::SettingsElement::enableLockPTT(bool enable) { setBit(Offset::lockPTT().byte, Offset::lockPTT().bit, enable); } DR1801UVCodeplug::SettingsElement::Language DR1801UVCodeplug::SettingsElement::language() const { return (Language) getUInt8(Offset::language()); } void DR1801UVCodeplug::SettingsElement::setLanguage(Language lang) { setUInt8(Offset::language(), (uint8_t)lang); } DR1801UVCodeplug::SettingsElement::SquelchMode DR1801UVCodeplug::SettingsElement::squelchMode() const { return (SquelchMode) getUInt8(Offset::squelchMode()); } void DR1801UVCodeplug::SettingsElement::setSquelchMode(SquelchMode mode) { setUInt8(Offset::squelchMode(), (uint8_t)mode); } bool DR1801UVCodeplug::SettingsElement::rogerTonesEnabled() const { return 0x01 == getUInt8(Offset::rogerTonesEnabled()); } void DR1801UVCodeplug::SettingsElement::enableRogerTones(bool enable) { setUInt8(Offset::rogerTonesEnabled(), enable ? 0x01 : 0x00); } bool DR1801UVCodeplug::SettingsElement::dmrCallOutToneEnabled() const { return getBit(Offset::dmrCallOutToneEnabled()); } void DR1801UVCodeplug::SettingsElement::enableDMRCallOutTone(bool enable) { setBit(Offset::dmrCallOutToneEnabled(), enable); } bool DR1801UVCodeplug::SettingsElement::fmCallOutToneEnabled() const { return getBit(Offset::fmCallOutToneEnabled()); } void DR1801UVCodeplug::SettingsElement::enableFMCallOutTone(bool enable) { setBit(Offset::fmCallOutToneEnabled(), enable); } bool DR1801UVCodeplug::SettingsElement::dmrVoiceEndToneEnabled() const { return getBit(Offset::dmrVoiceEndToneEnabled()); } void DR1801UVCodeplug::SettingsElement::enableDMRVoiceEndTone(bool enable) { setBit(Offset::dmrVoiceEndToneEnabled(), enable); } bool DR1801UVCodeplug::SettingsElement::fmVoiceEndToneEnabled() const { return getBit(Offset::fmVoiceEndToneEnabled()); } void DR1801UVCodeplug::SettingsElement::enableFMVoiceEndTone(bool enable) { setBit(Offset::fmVoiceEndToneEnabled(), enable); } bool DR1801UVCodeplug::SettingsElement::dmrCallEndToneEnabled() const { return getBit(Offset::dmrCallEndToneEnabled()); } void DR1801UVCodeplug::SettingsElement::enableDMRCallEndTone(bool enable) { setBit(Offset::dmrCallEndToneEnabled(), enable); } bool DR1801UVCodeplug::SettingsElement::messageToneEnabled() const { return getBit(Offset::messageToneEnabled()); } void DR1801UVCodeplug::SettingsElement::enableMessageTone(bool enable) { setBit(Offset::messageToneEnabled(), enable); } DR1801UVCodeplug::SettingsElement::RingTone DR1801UVCodeplug::SettingsElement::ringTone() const { return (RingTone) getUInt8(Offset::ringTone()); } void DR1801UVCodeplug::SettingsElement::setRingTone(RingTone tone) { setUInt8(Offset::ringTone(), (uint8_t)tone); } QString DR1801UVCodeplug::SettingsElement::radioName() const { return readASCII(Offset::radioName(), Limit::radioNameLength(), 0x00); } void DR1801UVCodeplug::SettingsElement::setRadioName(const QString &name) { writeASCII(Offset::radioName(), name, Limit::radioNameLength(), 0x00); } float DR1801UVCodeplug::SettingsElement::reverseBurstFrequency() const { return float(getUInt16_le(Offset::reverseBurstFrequency()))/10; } void DR1801UVCodeplug::SettingsElement::setReverseBurstFrequency(float Hz) { setUInt16_le(Offset::reverseBurstFrequency(), Hz*10); } DR1801UVCodeplug::SettingsElement::BacklightTime DR1801UVCodeplug::SettingsElement::backlightTime() const { return (BacklightTime) getUInt8(Offset::backlightTime()); } void DR1801UVCodeplug::SettingsElement::setBacklightTime(BacklightTime time) { setUInt8(Offset::backlightTime(), (uint8_t)time); } bool DR1801UVCodeplug::SettingsElement::campandingEnabled() const { return 0x01 == getUInt8(Offset::campandingEnabled()); } void DR1801UVCodeplug::SettingsElement::enableCampanding(bool enable) { setUInt8(Offset::campandingEnabled(), enable ? 0x01 : 0x00); } DR1801UVCodeplug::SettingsElement::TuningMode DR1801UVCodeplug::SettingsElement::tunigModeUp() const { return (TuningMode) getUInt8(Offset::tuningModeUp()); } void DR1801UVCodeplug::SettingsElement::setTuningModeUp(TuningMode mode) { setUInt8(Offset::tuningModeUp(), (uint8_t)mode); } DR1801UVCodeplug::SettingsElement::TuningMode DR1801UVCodeplug::SettingsElement::tunigModeDown() const { return (TuningMode) getUInt8(Offset::tunigModeDown()); } void DR1801UVCodeplug::SettingsElement::setTuningModeDown(TuningMode mode) { setUInt8(Offset::tunigModeDown(), (uint8_t)mode); } DR1801UVCodeplug::SettingsElement::DisplayMode DR1801UVCodeplug::SettingsElement::displayMode() const { return (DisplayMode)getUInt8(Offset::displayMode()); } void DR1801UVCodeplug::SettingsElement::setDisplayMode(DisplayMode mode) { setUInt8(Offset::displayMode(), (uint8_t)mode); } DR1801UVCodeplug::SettingsElement::DualWatchMode DR1801UVCodeplug::SettingsElement::dualWatchMode() const { return (DualWatchMode) getUInt8(Offset::dualWatchMode()); } void DR1801UVCodeplug::SettingsElement::setDualWatchMode(DualWatchMode mode) { setUInt8(Offset::dualWatchMode(), (uint8_t)mode); } DR1801UVCodeplug::SettingsElement::ScanMode DR1801UVCodeplug::SettingsElement::scanMode() const { return (ScanMode) getUInt8(Offset::scanMode()); } void DR1801UVCodeplug::SettingsElement::setScanMode(ScanMode mode) { setUInt8(Offset::scanMode(), (uint8_t)mode); } DR1801UVCodeplug::SettingsElement::BootScreen DR1801UVCodeplug::SettingsElement::bootScreen() const { return (BootScreen) getUInt8(Offset::bootScreen()); } void DR1801UVCodeplug::SettingsElement::setBootScreen(BootScreen mode) { setUInt8(Offset::bootScreen(), (uint8_t) mode); } QString DR1801UVCodeplug::SettingsElement::bootLine1() const { return readASCII(Offset::bootLine1(), Limit::bootLineLength(), 0x00); } void DR1801UVCodeplug::SettingsElement::setBootLine1(const QString &line) { writeASCII(Offset::bootLine1(), line, Limit::bootLineLength(), 0x00); } QString DR1801UVCodeplug::SettingsElement::bootLine2() const { return readASCII(Offset::bootLine2(), Limit::bootLineLength(), 0x00); } void DR1801UVCodeplug::SettingsElement::setBootLine2(const QString &line) { writeASCII(Offset::bootLine2(), line, Limit::bootLineLength(), 0x00); } bool DR1801UVCodeplug::SettingsElement::ledEnabled() const { return 0x01 == getUInt8(Offset::ledEnabled()); } void DR1801UVCodeplug::SettingsElement::enableLED(bool enabled) { setUInt8(Offset::ledEnabled(), enabled ? 0x01 : 0x00); } unsigned int DR1801UVCodeplug::SettingsElement::loneWorkerResponseTime() const { return getUInt8(Offset::loneWorkerResponseTime()); } void DR1801UVCodeplug::SettingsElement::setLoneWorkerResponseTime(unsigned int sec) { setUInt8(Offset::loneWorkerResponseTime(), sec); } unsigned int DR1801UVCodeplug::SettingsElement::loneWorkerReminderTime() const { return getUInt8(Offset::loneWorkerReminderTime()); } void DR1801UVCodeplug::SettingsElement::setLoneWorkerReminderTime(unsigned int sec) { setUInt8(Offset::loneWorkerReminderTime(), sec); } bool DR1801UVCodeplug::SettingsElement::bootPasswordEnabled() const { return getBit(Offset::bootPasswordEnabled()); } QString DR1801UVCodeplug::SettingsElement::bootPassword() const { return readASCII(Offset::bootPassword(), Limit::bootPasswordLength(), 0x00); } void DR1801UVCodeplug::SettingsElement::setBootPassword(const QString &passwd) { setBit(Offset::bootPasswordEnabled(), true); setUInt8(Offset::boolPasswordLength(), std::min(Limit::bootPasswordLength(), (unsigned int)passwd.length())); writeASCII(Offset::bootPassword(), passwd, Limit::bootPasswordLength(), 0x00); } void DR1801UVCodeplug::SettingsElement::clearBootPassword() { setBit(Offset::bootPasswordEnabled(), false); setUInt8(Offset::boolPasswordLength(), 0); writeASCII(Offset::bootPassword(), "", Limit::bootPasswordLength(), 0x00); } bool DR1801UVCodeplug::SettingsElement::progPasswordEnabled() const { return getBit(Offset::progPasswordEnabled()); } QString DR1801UVCodeplug::SettingsElement::progPassword() const { return readASCII(Offset::progPassword(), Limit::progPasswordLength(), 0x00); } void DR1801UVCodeplug::SettingsElement::setProgPassword(const QString &passwd) { setBit(Offset::progPasswordEnabled(), true); setUInt8(Offset::progPasswordLength(), std::min(Limit::progPasswordLength(), (unsigned int)passwd.length())); writeASCII(Offset::progPassword(), passwd, Limit::progPasswordLength(), 0x00); } void DR1801UVCodeplug::SettingsElement::clearProgPassword() { setBit(Offset::progPasswordEnabled(), false); setUInt8(Offset::progPasswordLength(), 0); writeASCII(Offset::progPassword(), "", Limit::progPasswordLength(), 0x00); } bool DR1801UVCodeplug::SettingsElement::updateConfig(Config *config, const ErrorStack &err) { Q_UNUSED(err); // Store radio ID config->radioIDs()->add(new DMRRadioID(radioName(), dmrID())); config->settings()->setDefaultId(config->radioIDs()->getId(0)); // Handle VOX settings. config->settings()->setVOX(voxSensitivity()); // Handle intro lines config->settings()->setIntroLine1(bootLine1()); config->settings()->setIntroLine2(bootLine2()); return true; } bool DR1801UVCodeplug::SettingsElement::fromConfig(Config *config, const ErrorStack &err) { // Store radio ID DMRRadioID *id = config->settings()->defaultId(); if (nullptr == id) { errMsg(err) << "Cannot encode radio ID and name. No default DMR radio ID."; return false; } setRadioName(id->name()); setDMRID(id->number()); setVOXSensitivity(config->settings()->vox()); setBootLine1(config->settings()->introLine1()); setBootLine2(config->settings()->introLine2()); return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::ScanListBankElement * ******************************************************************************************** */ DR1801UVCodeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr) : Element(ptr, ScanListBankElement::size()) { // pass... } void DR1801UVCodeplug::ScanListBankElement::clear() { memset(_data, 0, _size); } unsigned int DR1801UVCodeplug::ScanListBankElement::scanListCount() const { return getUInt8(Offset::scanListCount()); } void DR1801UVCodeplug::ScanListBankElement::setScanListCount(unsigned int count) { count = std::min(Limit::scanListCount(), count); setUInt8(Offset::scanListCount(), count); } DR1801UVCodeplug::ScanListElement DR1801UVCodeplug::ScanListBankElement::scanList(unsigned int index) const { return ScanListElement(_data + Offset::scanLists() + index*ScanListElement::size()); } bool DR1801UVCodeplug::ScanListBankElement::decode(Context &ctx, const ErrorStack &err) const { for (unsigned int i=0, j=0; iscanlists()->add(obj); } return true; } bool DR1801UVCodeplug::ScanListBankElement::link(Context &ctx, const ErrorStack &err) const { for (unsigned int i=0,j=0; i(sl.index())) { errMsg(err) << "Cannot link scan list at index " << i << ". Scan list not defined."; return false; } if (! sl.linkScanListObj(ctx.get(sl.index()), ctx, err)) { errMsg(err) << "Cannot link scan list at index " << i << "."; return false; } } return true; } bool DR1801UVCodeplug::ScanListBankElement::encode(Context &ctx, const ErrorStack &err) { unsigned int n = std::min(Limit::scanListCount(), ctx.count()); setScanListCount(n); for (unsigned int i=0; iscanList(i); if (! scan.encode(ctx.get(i), ctx, err)) { errMsg(err) << "Cannot encode scan list '" << ctx.get(i)->name() << " at index " << i << "."; return false; } scan.setIndex(i); } return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::ScanListElement * ******************************************************************************************** */ DR1801UVCodeplug::ScanListElement::ScanListElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::ScanListElement::ScanListElement(uint8_t *ptr) : Element(ptr, ScanListElement::size()) { // pass... } void DR1801UVCodeplug::ScanListElement::clear() { memset(_data, 0, _size); } bool DR1801UVCodeplug::ScanListElement::isValid() const { return 0 != getUInt8(Offset::index()); // Check index } unsigned int DR1801UVCodeplug::ScanListElement::index() const { return getUInt8(Offset::index())-1; } void DR1801UVCodeplug::ScanListElement::setIndex(unsigned int idx) { setUInt8(Offset::index(), idx+1); } unsigned int DR1801UVCodeplug::ScanListElement::entryCount() const { return getUInt8(Offset::memberCount()); } void DR1801UVCodeplug::ScanListElement::setEntryCount(unsigned int num) { setUInt8(Offset::memberCount(), num); } DR1801UVCodeplug::ScanListElement::PriorityChannel DR1801UVCodeplug::ScanListElement::priorityChannel1() const { return (PriorityChannel) getUInt8(Offset::priorityChannel1()); } void DR1801UVCodeplug::ScanListElement::setPriorityChannel1(PriorityChannel mode) { setUInt8(Offset::priorityChannel1(), (uint8_t) mode); } unsigned int DR1801UVCodeplug::ScanListElement::priorityChannel1Index() const { return getUInt16_le(Offset::priorityChannel1Index()); } void DR1801UVCodeplug::ScanListElement::setPriorityChannel1Index(unsigned int index) { setUInt16_le(Offset::priorityChannel1Index(), index); } DR1801UVCodeplug::ScanListElement::PriorityChannel DR1801UVCodeplug::ScanListElement::priorityChannel2() const { return (PriorityChannel) getUInt8(Offset::priorityChannel2()); } void DR1801UVCodeplug::ScanListElement::setPriorityChannel2(PriorityChannel mode) { setUInt8(Offset::priorityChannel2(), (uint8_t) mode); } unsigned int DR1801UVCodeplug::ScanListElement::priorityChannel2Index() const { return getUInt16_le(Offset::priorityChannel2Index()); } void DR1801UVCodeplug::ScanListElement::setPriorityChannel2Index(unsigned int index) { setUInt16_le(Offset::priorityChannel2Index(), index); } DR1801UVCodeplug::ScanListElement::RevertChannel DR1801UVCodeplug::ScanListElement::revertChannel() const { return (RevertChannel) getUInt8(Offset::revertChannel()); } void DR1801UVCodeplug::ScanListElement::setRevertChannel(RevertChannel mode) { setUInt8(Offset::revertChannel(), (uint8_t) mode); } unsigned int DR1801UVCodeplug::ScanListElement::revertChannelIndex() const { return getUInt16_le(Offset::revertChannelIndex()); } void DR1801UVCodeplug::ScanListElement::setRevertChannelIndex(unsigned int index) { setUInt16_le(Offset::revertChannelIndex(), index); } QString DR1801UVCodeplug::ScanListElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0x00); } void DR1801UVCodeplug::ScanListElement::setName(const QString &name) { writeASCII(Offset::name(), name, Limit::nameLength(), 0x00); } unsigned int DR1801UVCodeplug::ScanListElement::entryIndex(unsigned int n) { return getUInt16_le(Offset::memberIndices() + 2*n); } void DR1801UVCodeplug::ScanListElement::setEntryIndex(unsigned int n, unsigned int index) { setUInt16_le(Offset::memberIndices()+2*n, index); } ScanList * DR1801UVCodeplug::ScanListElement::toScanListObj(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err) return new ScanList(name()); } bool DR1801UVCodeplug::ScanListElement::linkScanListObj(ScanList *obj, Context &ctx, const ErrorStack &err) { // Link priority channels. switch (priorityChannel1()) { case PriorityChannel::Selected: obj->setPrimaryChannel(SelectedChannel::get()); break; case PriorityChannel::Fixed: if (! ctx.has(priorityChannel1Index())) { errMsg(err) << "Cannot link to priority channel 1: Channel with index " << priorityChannel1Index() << " not defined."; return false; } obj->setPrimaryChannel(ctx.get(priorityChannel1Index())); break; case PriorityChannel::None: break; } switch (priorityChannel2()) { case PriorityChannel::Selected: obj->setSecondaryChannel(SelectedChannel::get()); break; case PriorityChannel::Fixed: if (! ctx.has(priorityChannel2Index())) { errMsg(err) << "Cannot link to priority channel 2: Channel with index " << priorityChannel2Index() << " not defined."; return false; } obj->setSecondaryChannel(ctx.get(priorityChannel2Index())); break; case PriorityChannel::None: break; } for (unsigned int i=0; i(entryIndex(i))) { errMsg(err) << "Cannot link scan-list entry " << i << ": Channel with index " << entryIndex(i) << " not defined."; return false; } obj->addChannel(ctx.get(entryIndex(i))); } return true; } bool DR1801UVCodeplug::ScanListElement::encode(ScanList *obj, Context &ctx, const ErrorStack &err) { Q_UNUSED(err); setName(obj->name()); if (nullptr == obj->primaryChannel()) setPriorityChannel1(PriorityChannel::None); else if (obj->primaryChannel() && (SelectedChannel::get() == obj->primaryChannel())) { setPriorityChannel1(PriorityChannel::Selected); } else { setPriorityChannel1(PriorityChannel::Fixed); setPriorityChannel1Index(ctx.index(obj->primaryChannel())); } if (nullptr == obj->secondaryChannel()) setPriorityChannel2(PriorityChannel::None); else if (obj->secondaryChannel() && (SelectedChannel::get() == obj->secondaryChannel())) { setPriorityChannel2(PriorityChannel::Selected); } else { setPriorityChannel2(PriorityChannel::Fixed); setPriorityChannel2Index(ctx.index(obj->secondaryChannel())); } if (nullptr == obj->revertChannel()) setRevertChannel(RevertChannel::LastActive); else if (obj->revertChannel() && (SelectedChannel::get() == obj->revertChannel())) { setRevertChannel(RevertChannel::Selected); } else { setRevertChannel(RevertChannel::Fixed); setRevertChannelIndex(ctx.index(obj->revertChannel())); } unsigned int n = std::min(Limit::memberCount(), (unsigned int)obj->count()); for (unsigned int i=0; ichannel(i))); } return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::MessageBankElement * ******************************************************************************************** */ DR1801UVCodeplug::MessageBankElement::MessageBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::MessageBankElement::MessageBankElement(uint8_t *ptr) : Element(ptr, MessageBankElement::size()) { // pass... } void DR1801UVCodeplug::MessageBankElement::clear() { memset(_data, 0, _size); } unsigned int DR1801UVCodeplug::MessageBankElement::messageCount() const { return getUInt8(Offset::messageCount()); } void DR1801UVCodeplug::MessageBankElement::setMessageCount(unsigned int count) { setUInt8(Offset::messageCount(), count); } DR1801UVCodeplug::MessageElement DR1801UVCodeplug::MessageBankElement::message(unsigned int n) const { return MessageElement(_data + Offset::messages() + n*MessageElement::size()); } bool DR1801UVCodeplug::MessageBankElement::decode(Context &ctx, const ErrorStack &err) const { Q_UNUSED(err); ctx.config()->smsExtension()->smsTemplates()->clear(); for (unsigned int i=0,j=0; isetName(QString("Message %1").arg(msg.index())); sms->setMessage(msg.text()); ctx.config()->smsExtension()->smsTemplates()->add(sms); } return true; } bool DR1801UVCodeplug::MessageBankElement::encode(Context &ctx, const ErrorStack &err) { Q_UNUSED(err); setMessageCount(0); SMSExtension *smsExt = ctx.config()->smsExtension(); unsigned int count = std::min(Limit::messageCount(), (unsigned int)smsExt->smsTemplates()->count()); for (unsigned int i=0; ismsTemplates()->message(i)->message()); msg.setIndex(i+1); } setMessageCount(count); return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::MessageElement * ******************************************************************************************** */ DR1801UVCodeplug::MessageElement::MessageElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::MessageElement::MessageElement(uint8_t *ptr) : Element(ptr, MessageElement::size()) { // pass... } void DR1801UVCodeplug::MessageElement::clear() { memset(_data, 0, _size); } bool DR1801UVCodeplug::MessageElement::isValid() const { return 0x00 != getUInt8(Offset::index()); } unsigned int DR1801UVCodeplug::MessageElement::index() const { return getUInt8(Offset::index())-1; } void DR1801UVCodeplug::MessageElement::setIndex(unsigned int index) { setUInt8(Offset::index(), index+1); } QString DR1801UVCodeplug::MessageElement::text() const { return readASCII(Offset::text(), Limit::textLength(), 0x00); } void DR1801UVCodeplug::MessageElement::setText(const QString &text) { setUInt8(Offset::textLength(), std::min(Limit::textLength(), (unsigned int)text.length())); writeASCII(Offset::text(), text, Limit::textLength(), 0x00); } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::KeySettingsElement * ******************************************************************************************** */ DR1801UVCodeplug::KeySettingsElement::KeySettingsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::KeySettingsElement::KeySettingsElement(uint8_t *ptr) : Element(ptr, KeySettingsElement::size()) { // pass... } void DR1801UVCodeplug::KeySettingsElement::clear() { memset(_data, 0, _size); } DR1801UVCodeplug::KeySettingsElement::Function DR1801UVCodeplug::KeySettingsElement::sideKey1Short() const { return (Function) getUInt8(Offset::sideKey1Short()); } void DR1801UVCodeplug::KeySettingsElement::setSideKey1Short(Function func) { setUInt8(Offset::sideKey1Short(), (uint8_t) func); } DR1801UVCodeplug::KeySettingsElement::Function DR1801UVCodeplug::KeySettingsElement::sideKey1Long() const { return (Function) getUInt8(Offset::sideKey1Long()); } void DR1801UVCodeplug::KeySettingsElement::setSideKey1Long(Function func) { setUInt8(Offset::sideKey1Long(), (uint8_t) func); } DR1801UVCodeplug::KeySettingsElement::Function DR1801UVCodeplug::KeySettingsElement::sideKey2Short() const { return (Function) getUInt8(Offset::sideKey2Short()); } void DR1801UVCodeplug::KeySettingsElement::setSideKey2Short(Function func) { setUInt8(Offset::sideKey2Short(), (uint8_t) func); } DR1801UVCodeplug::KeySettingsElement::Function DR1801UVCodeplug::KeySettingsElement::sideKey2Long() const { return (Function) getUInt8(Offset::sideKey2Long()); } void DR1801UVCodeplug::KeySettingsElement::setSideKey2Long(Function func) { setUInt8(Offset::sideKey2Long(), (uint8_t) func); } DR1801UVCodeplug::KeySettingsElement::Function DR1801UVCodeplug::KeySettingsElement::topKeyShort() const { return (Function) getUInt8(Offset::topKeyShort()); } void DR1801UVCodeplug::KeySettingsElement::setTopKeyShort(Function func) { setUInt8(Offset::topKeyShort(), (uint8_t) func); } DR1801UVCodeplug::KeySettingsElement::Function DR1801UVCodeplug::KeySettingsElement::topKeyLong() const { return (Function) getUInt8(Offset::topKeyLong()); } void DR1801UVCodeplug::KeySettingsElement::setTopKeyLong(Function func) { setUInt8(Offset::topKeyLong(), (uint8_t) func); } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::VFOBankElement * ******************************************************************************************** */ DR1801UVCodeplug::VFOBankElement::VFOBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::VFOBankElement::VFOBankElement(uint8_t *ptr) : Element(ptr, VFOBankElement::size()) { // pass... } void DR1801UVCodeplug::VFOBankElement::clear() { memset(_data, 0, _size); } DR1801UVCodeplug::ChannelElement DR1801UVCodeplug::VFOBankElement::vfoA() const { return ChannelElement(_data + Offset::vfoA()); } DR1801UVCodeplug::ChannelElement DR1801UVCodeplug::VFOBankElement::vfoB() const { return ChannelElement(_data + Offset::vfoB()); } QString DR1801UVCodeplug::VFOBankElement::nameA() const { return readASCII(Offset::nameA(), Limit::nameLength(), 0x00); } void DR1801UVCodeplug::VFOBankElement::setNameA(const QString &name) { writeASCII(Offset::nameB(), name, Limit::nameLength(), 0x00); } QString DR1801UVCodeplug::VFOBankElement::nameB() const { return readASCII(Offset::nameB(), Limit::nameLength(), 0x00); } void DR1801UVCodeplug::VFOBankElement::setNameB(const QString &name) { writeASCII(Offset::nameB(), name, Limit::nameLength(), 0x00); } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::EncryptionKeyBankElement * ******************************************************************************************** */ DR1801UVCodeplug::EncryptionKeyBankElement::EncryptionKeyBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::EncryptionKeyBankElement::EncryptionKeyBankElement(uint8_t *ptr) : Element(ptr, EncryptionKeyBankElement::size()) { // pass... } void DR1801UVCodeplug::EncryptionKeyBankElement::clear() { memset(_data, 0, _size); } DR1801UVCodeplug::EncryptionKeyElement DR1801UVCodeplug::EncryptionKeyBankElement::key(unsigned int index) const { return EncryptionKeyElement(_data + index*EncryptionKeyElement::size()); } bool DR1801UVCodeplug::EncryptionKeyBankElement::decode(Context &ctx, const ErrorStack &err) const { for (unsigned int i=0; icommercialExtension()->encryptionKeys()->add(obj); } return true; } bool DR1801UVCodeplug::EncryptionKeyBankElement::link(Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx); Q_UNUSED(err); // Nothing to do return true; } bool DR1801UVCodeplug::EncryptionKeyBankElement::encode(Context &ctx, const ErrorStack &err) { unsigned int n = std::min(Limit::keyCount(), ctx.count()); for (unsigned int i=0; ikey(i); if (i>=n) { key.clear(); continue; } if (! key.encode(ctx.get(i), ctx, err)) { errMsg(err) << "Cannot encode DMR encryption key '" << ctx.get(i)->name() << "' at index " << i << "."; return false; } key.setIndex(i); } return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::EncryptionKeyElement * ******************************************************************************************** */ DR1801UVCodeplug::EncryptionKeyElement::EncryptionKeyElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::EncryptionKeyElement::EncryptionKeyElement(uint8_t *ptr) : Element(ptr, EncryptionKeyElement::size()) { // pass... } void DR1801UVCodeplug::EncryptionKeyElement::clear() { memset(_data, 0, _size); } bool DR1801UVCodeplug::EncryptionKeyElement::isValid() const { return 0x00 != getUInt8(Offset::index()); } unsigned int DR1801UVCodeplug::EncryptionKeyElement::index() const { return getUInt8(Offset::index())-1; } void DR1801UVCodeplug::EncryptionKeyElement::setIndex(unsigned int index) { setUInt8(Offset::index(), index+1); } unsigned int DR1801UVCodeplug::EncryptionKeyElement::keyLength() const { return getUInt16_le(Offset::length()); } QString DR1801UVCodeplug::EncryptionKeyElement::key() const { return readASCII(Offset::key(), Limit::keyLength(), 0x00); } void DR1801UVCodeplug::EncryptionKeyElement::setKey(const QString &key) { setUInt8(Offset::length(), 8); writeASCII(Offset::key(), key, Limit::keyLength(), 0x00); } EncryptionKey * DR1801UVCodeplug::EncryptionKeyElement::toKeyObj(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); if (! isValid()) { errMsg(err) << "Cannot decode an invalid encryption key."; return nullptr; } BasicEncryptionKey *obj = new BasicEncryptionKey(); if (! obj->fromHex(key(), err)) { errMsg(err) << "Cannot decode key '" << key() << "'."; delete obj; return nullptr; } return obj; } bool DR1801UVCodeplug::EncryptionKeyElement::linkKeyObj(EncryptionKey *obj, Context &ctx, const ErrorStack &err) { Q_UNUSED(obj); Q_UNUSED(ctx); Q_UNUSED(err); // There is nothing to do. return true; } bool DR1801UVCodeplug::EncryptionKeyElement::encode(EncryptionKey *obj, Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); if (!obj->is()) { errMsg(err) << "Cannot encode AES encryption key. Not supported by the device."; return false; } BasicEncryptionKey *key = obj->as(); setKey(key->key().toHex()); return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::DTMFSettingsElement * ******************************************************************************************** */ DR1801UVCodeplug::DTMFSettingsElement::DTMFSettingsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::DTMFSettingsElement::DTMFSettingsElement(uint8_t *ptr) : Element(ptr, DR1801UVCodeplug::DTMFSettingsElement::size()) { // pass... } void DR1801UVCodeplug::DTMFSettingsElement::clear() { memset(_data, 0, _size); } QString DR1801UVCodeplug::DTMFSettingsElement::radioID() const { return readASCII(Offset::radioID(), Limit::radioIDLength(), 0x00); } void DR1801UVCodeplug::DTMFSettingsElement::setRadioID(const QString &id) { setUInt8(Offset::radioIDLength(), std::min(Limit::radioIDLength(), (unsigned int)id.length())); writeASCII(Offset::radioIDLength(), id, Limit::radioIDLength(), 0x00); } QString DR1801UVCodeplug::DTMFSettingsElement::killCode() const { return readASCII(Offset::killCode(), Limit::killCodeLength(), 0x00); } void DR1801UVCodeplug::DTMFSettingsElement::setKillCode(const QString &code) { setUInt8(Offset::killCodeLength(), std::min(Limit::killCodeLength(), (unsigned int)code.length())); writeASCII(Offset::killCode(), code, Limit::killCodeLength(), 0x00); } QString DR1801UVCodeplug::DTMFSettingsElement::wakeCode() const { return readASCII(Offset::wakeCode(), Limit::wakeCodeLength(), 0x00); } void DR1801UVCodeplug::DTMFSettingsElement::setWakeCode(const QString &code) { setUInt8(Offset::wakeCodeLength(), std::min(Limit::wakeCodeLength(), (unsigned int)code.length())); writeASCII(Offset::wakeCode(), code, Limit::wakeCodeLength(), 0x00); } DR1801UVCodeplug::DTMFSettingsElement::NonNumber DR1801UVCodeplug::DTMFSettingsElement::delimiter() const { return (NonNumber) getUInt8(Offset::delimiter()); } void DR1801UVCodeplug::DTMFSettingsElement::setDelimiter(NonNumber code) { setUInt8(Offset::delimiter(), (uint8_t)code); } DR1801UVCodeplug::DTMFSettingsElement::NonNumber DR1801UVCodeplug::DTMFSettingsElement::groupCode() const { return (NonNumber) getUInt8(Offset::groupCode()); } void DR1801UVCodeplug::DTMFSettingsElement::setGroupCode(NonNumber code) { setUInt8(Offset::groupCode(), (uint8_t)code); } DR1801UVCodeplug::DTMFSettingsElement::Response DR1801UVCodeplug::DTMFSettingsElement::response() const { return (Response) getUInt8(Offset::response()); } void DR1801UVCodeplug::DTMFSettingsElement::setResponse(Response resp) { setUInt8(Offset::response(), (uint8_t)resp); } unsigned int DR1801UVCodeplug::DTMFSettingsElement::autoResetTime() const { return getUInt8(Offset::autoResetTime()); } void DR1801UVCodeplug::DTMFSettingsElement::setAutoResetTime(unsigned int sec) { setUInt8(Offset::autoResetTime(), sec); } bool DR1801UVCodeplug::DTMFSettingsElement::killWakeEnabled() const { return 0x01 == getUInt8(Offset::killWake()); } void DR1801UVCodeplug::DTMFSettingsElement::enableKillWake(bool enable) { setUInt8(Offset::killWake(), enable ? 0x01 : 0x00); } DR1801UVCodeplug::DTMFSettingsElement::Kill DR1801UVCodeplug::DTMFSettingsElement::killType() const { return (Kill) getUInt8(Offset::killType()); } void DR1801UVCodeplug::DTMFSettingsElement::setKillType(Kill type) { setUInt8(Offset::killType(), (uint8_t)type); } DR1801UVCodeplug::DTMFSystemBankElement DR1801UVCodeplug::DTMFSettingsElement::dtmfSystems() const { return DTMFSystemBankElement(_data+Offset::dtmfSystems()); } DR1801UVCodeplug::DTMFIDBankElement DR1801UVCodeplug::DTMFSettingsElement::dtmfIDs() const { return DTMFIDBankElement(_data + Offset::dtmfIDs()); } DR1801UVCodeplug::PTTIDBankElement DR1801UVCodeplug::DTMFSettingsElement::pttIDs() const { return PTTIDBankElement(_data + Offset::pttIDs()); } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::DTMFSystemBankElement * ******************************************************************************************** */ DR1801UVCodeplug::DTMFSystemBankElement::DTMFSystemBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::DTMFSystemBankElement::DTMFSystemBankElement(uint8_t *ptr) : Element(ptr, DTMFSystemBankElement::size()) { // pass... } void DR1801UVCodeplug::DTMFSystemBankElement::clear() { memset(_data, 0, _size); } unsigned int DR1801UVCodeplug::DTMFSystemBankElement::systemCount() const { return getUInt8(Offset::systemCount()); } void DR1801UVCodeplug::DTMFSystemBankElement::setSystemCount(unsigned int count) { setUInt8(Offset::systemCount(), count); } DR1801UVCodeplug::DTMFSystemElement DR1801UVCodeplug::DTMFSystemBankElement::system(unsigned int n) const { return DTMFSystemElement(_data + Offset::systems() + n*DTMFSystemElement::size()); } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::DTMFSystemElement * ******************************************************************************************** */ DR1801UVCodeplug::DTMFSystemElement::DTMFSystemElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::DTMFSystemElement::DTMFSystemElement(uint8_t *ptr) : Element(ptr, DTMFSystemElement::size()) { // pass... } void DR1801UVCodeplug::DTMFSystemElement::clear() { memset(_data, 0, _size); } bool DR1801UVCodeplug::DTMFSystemElement::sideToneEnabled() const { return 0x01 == getUInt8(Offset::sideTone()); } void DR1801UVCodeplug::DTMFSystemElement::enableSideTone(bool enable) { setUInt8(Offset::sideTone(), enable ? 0x01 : 0x00); } unsigned int DR1801UVCodeplug::DTMFSystemElement::preTime() const { return getUInt16_le(Offset::preTime()); } void DR1801UVCodeplug::DTMFSystemElement::setPreTime(unsigned int ms) { setUInt16_le(Offset::preTime(), ms); } unsigned int DR1801UVCodeplug::DTMFSystemElement::codeDuration() const { return getUInt16_le(Offset::codeDuration()); } void DR1801UVCodeplug::DTMFSystemElement::setCodeDuration(unsigned int ms) { setUInt16_le(Offset::codeDuration(), ms); } unsigned int DR1801UVCodeplug::DTMFSystemElement::codeItervall() const { return getUInt16_le(Offset::codeIntervall()); } void DR1801UVCodeplug::DTMFSystemElement::setCodeItervall(unsigned int ms) { setUInt16_le(Offset::codeIntervall(), ms); } unsigned int DR1801UVCodeplug::DTMFSystemElement::resetTime() const { return getUInt16_le(Offset::resetTime()); } void DR1801UVCodeplug::DTMFSystemElement::setResetTime(unsigned int ms) { setUInt16_le(Offset::resetTime(), ms); } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::DTMFIDBankElement * ******************************************************************************************** */ DR1801UVCodeplug::DTMFIDBankElement::DTMFIDBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::DTMFIDBankElement::DTMFIDBankElement(uint8_t *ptr) : Element(ptr, DTMFIDBankElement::size()) { // pass... } void DR1801UVCodeplug::DTMFIDBankElement::clear() { memset(_data, 0, _size); } unsigned int DR1801UVCodeplug::DTMFIDBankElement::idCount() const { return getUInt8(Offset::idCount()); } void DR1801UVCodeplug::DTMFIDBankElement::setIDCount(unsigned int n) { setUInt8(Offset::idCount(), n); } DR1801UVCodeplug::DTMFIDElement DR1801UVCodeplug::DTMFIDBankElement::id(unsigned int n) const { return DTMFIDElement(_data + Offset::ids() + n*DTMFIDElement::size()); } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::DTMFIDElement * ******************************************************************************************** */ QVector DR1801UVCodeplug::DTMFIDElement::_bin_dtmf_tab = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','*','#' }; DR1801UVCodeplug::DTMFIDElement::DTMFIDElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::DTMFIDElement::DTMFIDElement(uint8_t *ptr) : Element(ptr, DR1801UVCodeplug::DTMFIDElement::size()) { // pass... } void DR1801UVCodeplug::DTMFIDElement::clear() { memset(_data, 0, _size); } unsigned int DR1801UVCodeplug::DTMFIDElement::numberLength() const { return getUInt8(Offset::numberLength()); } void DR1801UVCodeplug::DTMFIDElement::setNumberLength(unsigned int len) { setUInt8(Offset::numberLength(), len); } QString DR1801UVCodeplug::DTMFIDElement::number() const { QString number; number.reserve(16); for (unsigned int i=0; ismsExtension()->setFormat(SMSExtension::Format::Motorola); break; case SMSFormat::CompressedIP: ctx.config()->smsExtension()->setFormat(SMSExtension::Format::Hytera); break; case SMSFormat::DefinedData: ctx.config()->smsExtension()->setFormat(SMSExtension::Format::DMR); break; } return true; } bool DR1801UVCodeplug::DMRSettingsElement::encode(Context &ctx, const ErrorStack &err) { Q_UNUSED(err); switch(ctx.config()->smsExtension()->format()) { case SMSExtension::Format::Motorola: setSMSFormat(SMSFormat::IPData); break; case SMSExtension::Format::Hytera: setSMSFormat(SMSFormat::CompressedIP); break; case SMSExtension::Format::DMR: setSMSFormat(SMSFormat::DefinedData); break; } return true; } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::OneTouchSettingsElement * ******************************************************************************************** */ DR1801UVCodeplug::OneTouchSettingsElement::OneTouchSettingsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::OneTouchSettingsElement::OneTouchSettingsElement(uint8_t *ptr) : Element(ptr, OneTouchSettingsElement::size()) { // pass... } void DR1801UVCodeplug::OneTouchSettingsElement::clear() { memset(_data, 0, _size); } unsigned int DR1801UVCodeplug::OneTouchSettingsElement::settingsCount() const { return Limit::settingsCount(); } DR1801UVCodeplug::OneTouchSettingElement DR1801UVCodeplug::OneTouchSettingsElement::setting(unsigned int n) const { return OneTouchSettingElement(_data + Offset::settings() + n*OneTouchSettingElement::size()); } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug::OneTouchSettingElement * ******************************************************************************************** */ DR1801UVCodeplug::OneTouchSettingElement::OneTouchSettingElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } DR1801UVCodeplug::OneTouchSettingElement::OneTouchSettingElement(uint8_t *ptr) : Element(ptr, OneTouchSettingElement::size()) { // pass... } void DR1801UVCodeplug::OneTouchSettingElement::clear() { memset(_data, 0, _size); } bool DR1801UVCodeplug::OneTouchSettingElement::isValid() const { return Element::isValid() && (Type::Disabled != type()); } bool DR1801UVCodeplug::OneTouchSettingElement::hasContact() const { return 0 != getUInt16_le(Offset::contactIndex()); } unsigned int DR1801UVCodeplug::OneTouchSettingElement::contactIndex() const { return getUInt16_le(Offset::contactIndex())-1; } void DR1801UVCodeplug::OneTouchSettingElement::setContactIndex(unsigned int index) { setUInt16_le(Offset::contactIndex(), index+1); } void DR1801UVCodeplug::OneTouchSettingElement::clearContact() { setUInt16_le(Offset::contactIndex(), 0); } DR1801UVCodeplug::OneTouchSettingElement::Action DR1801UVCodeplug::OneTouchSettingElement::action() const { return (Action) getUInt8(Offset::action()); } void DR1801UVCodeplug::OneTouchSettingElement::setAction(Action action) { setUInt8(Offset::action(), (uint8_t) action); } bool DR1801UVCodeplug::OneTouchSettingElement::hasMessage() const { return 0 != getUInt8(Offset::messageIndex()); } unsigned int DR1801UVCodeplug::OneTouchSettingElement::messageIndex() const { return getUInt8(Offset::messageIndex())-1; } void DR1801UVCodeplug::OneTouchSettingElement::setMessageIndex(unsigned int index) { setUInt8(Offset::messageIndex(), index+1); } void DR1801UVCodeplug::OneTouchSettingElement::clearMessage() { setUInt8(Offset::messageIndex(), 0); } DR1801UVCodeplug::OneTouchSettingElement::Type DR1801UVCodeplug::OneTouchSettingElement::type() const { return (Type) getUInt8(Offset::type()); } void DR1801UVCodeplug::OneTouchSettingElement::setType(Type type) { setUInt8(Offset::type(), (uint8_t)type); } bool DR1801UVCodeplug::OneTouchSettingElement::hasDTMFID() const { return 0 != getUInt8(Offset::dtmfIDIndex()); } unsigned int DR1801UVCodeplug::OneTouchSettingElement::dtmfIDIndex() const { return getUInt8(Offset::dtmfIDIndex())-1; } void DR1801UVCodeplug::OneTouchSettingElement::setDTMFIDIndex(unsigned int index) { setUInt8(Offset::dtmfIDIndex(), index+1); } void DR1801UVCodeplug::OneTouchSettingElement::clearDTMFIDIndex() { setUInt8(Offset::dtmfIDIndex(), 0); } /* ******************************************************************************************** * * Implementation of DR1801UVCodeplug * ******************************************************************************************** */ DR1801UVCodeplug::DR1801UVCodeplug(QObject *parent) : Codeplug(parent) { addImage("BTECH DR-1801UV Codeplug"); image(0).addElement(0, Offset::size()); } Config * DR1801UVCodeplug::preprocess(Config *config, const ErrorStack &err) const { Config *copy = Codeplug::preprocess(config, err); if (nullptr == copy) { errMsg(err) << "Cannot pre-process DR1801A6 codeplug."; return nullptr; } // Split dual-zones into two. ZoneSplitVisitor splitter; if (! splitter.process(copy, err)) { errMsg(err) << "Cannot pre-process DR1801A6 codeplug."; delete copy; return nullptr; } return copy; } bool DR1801UVCodeplug::postprocess(Config *config, const ErrorStack &err) const { if (! Codeplug::postprocess(config, err)) { errMsg(err) << "Cannot post-process DR1801A6 codeplug."; return false; } // Merge splitted zones into one. ZoneMergeVisitor merger; if (! merger.process(config, err)) { errMsg(err) << "Cannot post-process DR1801A6 codeplug."; return false; } return true; } bool DR1801UVCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) const { Q_UNUSED(err) // All indices as 0-based. That is, the first channel gets index 0 etc. // There must be a default DMR radio ID. if (nullptr == ctx.config()->settings()->defaultId()) { errMsg(err) << "No default DMR radio ID specified."; errMsg(err) << "Cannot index codplug for encoding for the BTECH DR-1801UV."; return false; } // Map radio IDs for (int i=0; iradioIDs()->count(); i++) ctx.add(ctx.config()->radioIDs()->getId(i), i); // Map digital and DTMF contacts for (int i=0, d=0; icontacts()->count(); i++) { if (config->contacts()->contact(i)->is()) { ctx.add(config->contacts()->contact(i)->as(), d); d++; } } // Map rx group lists for (int i=0; irxGroupLists()->count(); i++) ctx.add(config->rxGroupLists()->list(i), i); // Map channels for (int i=0; ichannelList()->count(); i++) ctx.add(config->channelList()->channel(i), i); // Map zones for (int i=0; izones()->count(); i++) ctx.add(config->zones()->zone(i), i); // Map scan lists for (int i=0; iscanlists()->count(); i++) ctx.add(config->scanlists()->scanlist(i), i); return true; } bool DR1801UVCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) { Q_UNUSED(flags); Context ctx(config); ctx.addTable(&BasicEncryptionKey::staticMetaObject); if (! index(config, ctx, err)) { errMsg(err) << "Cannot encode codeplug."; return false; } if (! encodeElements(ctx, err)) { errMsg(err) << "Cannot encode codeplug."; return false; } return true; } bool DR1801UVCodeplug::decode(Config *config, const ErrorStack &err) { Context ctx(config); if (! decodeElements(ctx, err)) { errMsg(err) << "Cannot decode elements."; return false; } if (! linkElements(ctx, err)) { errMsg(err) << "Cannot decode elements."; return false; } return true; } bool DR1801UVCodeplug::decodeElements(Context &ctx, const ErrorStack &err) { if (! ChannelBankElement(data(Offset::channelBank())).decode(ctx, err)) { errMsg(err) << "Cannot decode channel elements."; return false; } if (! ContactBankElement(data(Offset::contactBank())).decode(ctx, err)) { errMsg(err) << "Cannot decode contact elements."; return false; } if (! GroupListBankElement(data(Offset::groupListBank())).decode(ctx, err)) { errMsg(err) << "Cannot decode group list elements."; return false; } if (! ZoneBankElement(data(Offset::zoneBank())).decode(ctx, err)) { errMsg(err) << "Cannot decode zone elements."; return false; } if (! MessageBankElement(data(Offset::messageBank())).decode(ctx, err)) { errMsg(err) << "Cannot decode preset messages."; return false; } if (! SettingsElement(data(Offset::settings())).updateConfig(ctx.config(), err)) { errMsg(err) << "Cannot decode settings element."; return false; } if (! DMRSettingsElement(data(Offset::dmrSettings())).decode(ctx, err)) { errMsg(err) << "Cannot decode DMR settings element."; return false; } if (! ScanListBankElement(data(Offset::scanListBank())).decode(ctx, err)) { errMsg(err) << "Cannot decode scan list elements."; return false; } if (! EncryptionKeyBankElement(data(Offset::encryptionKeyBank())).decode(ctx, err)) { logWarn() << "Cannot decode encryption keys:\n" << err.format(" "); //errMsg(err) << "Cannot decode encryption keys."; //return false; } return true; } bool DR1801UVCodeplug::linkElements(Context &ctx, const ErrorStack &err) { if (! ChannelBankElement(data(Offset::channelBank())).link(ctx, err)) { errMsg(err) << "Cannot link channels."; return false; } if (! ContactBankElement(data(Offset::contactBank())).link(ctx, err)) { errMsg(err) << "Cannot link contacts."; return false; } if (! GroupListBankElement(data(Offset::groupListBank())).link(ctx, err)) { errMsg(err) << "Cannot link group lists."; return false; } if (! ZoneBankElement(data(Offset::zoneBank())).link(ctx, err)) { errMsg(err) << "Cannot link zones."; return false; } if (! ScanListBankElement(data(Offset::scanListBank())).link(ctx, err)) { errMsg(err) << "Cannot link scan lists."; return false; } return true; } bool DR1801UVCodeplug::encodeElements(Context &ctx, const ErrorStack &err) { if (! SettingsElement(data(Offset::settings())).fromConfig(ctx.config(), err)) { errMsg(err) << "Cannot encode settings element."; return false; } if (! DMRSettingsElement(data(Offset::dmrSettings())).encode(ctx, err)) { errMsg(err) << "Cannot encode DMR settings element."; return false; } if (! ZoneBankElement(data(Offset::zoneBank())).encode(ctx, err)) { errMsg(err) << "Cannot encode zones."; return false; } if (! MessageBankElement(data(Offset::messageBank())).encode(ctx, err)) { errMsg(err) << "Cannot encode messages."; return false; } if (! ContactBankElement(data(Offset::contactBank())).encode(ctx, err)) { errMsg(err) << "Cannot encode contacts."; return false; } if (! ScanListBankElement(data(Offset::scanListBank())).encode(ctx, err)) { errMsg(err) << "Cannot encode scan lists."; return false; } if (! ChannelBankElement(data(Offset::channelBank())).encode(ctx, err)) { errMsg(err) << "Cannot encode channels."; return false; } if (! GroupListBankElement(data(Offset::groupListBank())).encode(ctx, err)) { errMsg(err) << "Cannot encode group lists."; return false; } if (! EncryptionKeyBankElement(data(Offset::encryptionKeyBank())).encode(ctx, err)) { errMsg(err) << "Cannot encode encryption keys."; return false; } return true; } qdmr-0.12.3/lib/dr1801uv_codeplug.hh000066400000000000000000002534451501654372000170230ustar00rootroot00000000000000#ifndef DR1801UVCODEPLUG_HH #define DR1801UVCODEPLUG_HH #include "channel.hh" #include "codeplug.hh" #include "contact.hh" class Zone; /** Implements the binary codeplug representation of a BTECH DR-1801UV (a.k.a, BF-1801-A6). * * This device is kind of a re-release of the BTECH DM-1801. However, the hardware and firmware * is completely different. It is just the chassis that remained the same. This version uses the * Auctus A6 radio-on-a-chip as its main component. * * @section dr1801ver Matching firmware versions * This class implements the codeplug for the firmware version @c 1.10. The codeplug format usually * does not change much with firmware revisions, in particular not with older radios. * * @section dr1801cpl Codeplug structure within radio * The memory representation of the codeplug within the radio is simply a single large block of * data, read and written entirely. The total size of the codeplug is 01dd90h bytes. * * * * * * * * * * * * * * * * * * * * *
      Start End Size Content
      0x00000 0x00300 0x00300 Some unkown settings, not * configurable through the CPS. Likely some sort of calibration data. Must be conserved.
      0x00300 0x003bc 0x000bc Some information about the radio. * Like serial number, firmware version, etc and timestamp.
      0x003b4 0x00418 0x00064 General settings, see * @c DR1801UVCodeplug::SettingsElement.
      0x00418 0x04110 0x00000 Zone bank, see * @c DR1801UVCodeplug::ZoneBankElement.
      0x04110 0x04274 0x00164 Message bank, see * @c DR1801UVCodeplug::MessageBankElement.
      0x04334 0x0a338 0x06008 Contact bank, see * @c DR1801UVCodeplug::ContactBankElement.
      0x0a338 0x0a65c 0x00324 Scan-list bank, see * @c DR1801UVCodeplug::ScanListBankElement.
      0x0a65c 0x1c660 0x12004 Channel bank, see * @c DR1801UVCodeplug::ChannelBankElement.
      0x1c6c4 0x1c6dc 0x0018 Key settings, see * @c DR1801UVCodeplug::KeySettingsElement.
      0x1c6dc 0x1d7e0 0x01104 Group list bank, see * @c DR1801UVCodeplug::GroupListBankElement.
      0x1d7e0 0x1d858 0x00078 Encryption keys, see * @c DR1801UVCodeplug::EncryptionKeyBankElement.
      0x1d858 0x1daf4 0x0029c DTMF signaling settings, see * @c DR1801UVCodeplug::DTMFSettingsElement.
      0x1daf4 0x1dbb8 0x00c4 Alarm settings, see * @c DR1801UVCodeplug::AlarmSettingsBankElement.
      0x1dbb8 0x1dbc8 0x0010 DMR settings, see * @c DR1801UVCodeplug::DMRSettingsElement.
      0x1dbc8 0x1dbf0 0x00028 One-touch settings, see * @c DR1801UVCodeplug::OneTouchSettingsElement.
      0x1dbf0 0x1dd00 0x00110 Some unknown settings.
      0x1dd00 0x1dd90 0x00090 VFO channels, see * @c DR1801UVCodeplug::VFOBankElement.
      * * @ingroup dr1801uv */ class DR1801UVCodeplug : public Codeplug { Q_OBJECT public: /** Implements the binary encoding of the channels settings. * * Memory representation of the channel settings (0034h bytes): * @verbinclude dr1801uv_channelelement.txt */ class ChannelElement: public Element { public: /** Possible channel types. */ enum class Type { FM = 1, DMR = 3 }; /** Possible power settings. */ enum class Power { Low = 0, High = 1 }; /** Possible values for the admid criterion. */ enum class Admit { Always = 0, ColorCode_or_Tone = 1, ChannelFree = 2 }; /** Possible time-slot values. */ enum class TimeSlot { TS1 = 1, TS2 = 2 }; /** Possible FM signaling modes. */ enum class SignalingMode { None = 0, DTMF = 1 }; /** Possible band width settings. */ enum class Bandwidth { Narrow = 1, Wide = 2 }; /** Possible subtone types. */ enum class SubToneType { None = 0, CTCSS = 1, DCS = 2 }; /** Possible DCS modes. */ enum class DCSMode { Normal = 0, Inverted = 1 }; protected: /** Hidden constructor. */ ChannelElement(uint8_t *ptr, size_t size); public: /** Constructor from pointer. */ ChannelElement(uint8_t *ptr); bool isValid() const; void clear(); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x00034; } /** Retunrs the 0-based index of the channel. */ virtual unsigned int index() const; /** Sets the index. */ virtual void setIndex(unsigned int idx); /** Returns the channel type. */ virtual Type channelType() const; /** Sets the channel type. */ virtual void setChannelType(Type type); /** Returns the power settings of the channel. */ virtual Channel::Power power() const; /** Sets the power for the channel. */ virtual void setPower(Channel::Power pwr); /** Retunrs the RX frequency in MHz. */ virtual Frequency rxFrequency() const; /** Sets the RX frequency in MHz. */ virtual void setRXFrequency(Frequency MHz); /** Retunrs the TX frequency in MHz. */ virtual Frequency txFrequency() const; /** Sets the TX frequency in MHz. */ virtual void setTXFrequency(Frequency MHz); /** Returns @c true if a contact index is set. */ virtual bool hasTransmitContact() const; /** Retunrs the contact index. */ virtual unsigned int transmitContactIndex() const; /** Sets the transmit contact index. */ virtual void setTransmitContactIndex(unsigned int index); /** Clears the contact index. */ virtual void clearTransmitContactIndex(); /** Returns the admit criterion. */ virtual Admit admitCriterion() const; /** Sets the admit criterion. */ virtual void setAdmitCriterion(Admit admit); /** Returns the color code (0-15). */ virtual unsigned int colorCode() const; /** Sets the color code (0-15). */ virtual void setColorCode(unsigned int cc); /** Returns the time slot. */ virtual DMRChannel::TimeSlot timeSlot() const; /** Sets the time slot. */ virtual void setTimeSlot(DMRChannel::TimeSlot ts); /** Returns @c true if an ecryption key index is set. */ virtual bool hasEncryptionKey() const; /** Returns the encryption key index. */ virtual unsigned int encryptionKeyIndex() const; /** Sets the encryption key index. */ virtual void setEncryptionKeyIndex(unsigned int index); /** Clears the encryption key index. */ virtual void clearEncryptionKeyIndex(); /** Returns @c true if dual-capacity direct mode is enabled. */ virtual bool dcdm() const; /** Enables/disables dual-capacity direct mode. */ virtual void enableDCDM(bool enable); /** Returns @c true if private-call confirmation is enabled. */ virtual bool confirmPrivateCall() const; /** Enables/disables private-call confirmation. */ virtual void enablePrivateCallConfirmation(bool enable); /** Returns the FM signaling mode. */ virtual SignalingMode signalingMode() const; /** Sets the FM signaling mode. */ virtual void setSignalingMode(SignalingMode mode); /** Returns @c true if the alarm system index is set. */ virtual bool hasAlarmSystem() const; /** Returns the index of the alarm system. */ virtual unsigned int alarmSystemIndex() const; /** Sets the alarm system index. */ virtual void setAlarmSystemIndex(unsigned int index); /** Clears the alarm system index. */ virtual void clearAlarmSystemIndex(); /** Retunrs the band width for FM channels. */ virtual FMChannel::Bandwidth bandwidth() const; /** Sets the band width for FM channels. */ virtual void setBandwidth(FMChannel::Bandwidth bw); /** Retunrs @c true, if the auto-scan is enabled. */ virtual bool autoScanEnabled() const; /** Enables/disables auto-scan. */ virtual void enableAutoScan(bool enable); /** Retunrs @c true if a scan list is assigned. */ virtual bool hasScanList() const; /** Returns the scan list index. */ virtual unsigned int scanListIndex() const; /** Sets the scan list index. */ virtual void setScanListIndex(unsigned int index); /** Clears the scan list index. */ virtual void clearScanListIndex(); /** Retunrs the RX CTCSS/DCS signaling. */ virtual SelectiveCall rxTone() const; /** Sets the RX CTCSS/DCS signaling. */ virtual void setRXTone(const SelectiveCall &code); /** Retunrs the TX CTCSS/DCS signaling. */ virtual SelectiveCall txTone() const; /** Sets the TX CTCSS/DCS signaling. */ virtual void setTXTone(const SelectiveCall &code); /** Returns @c true if talkaround is enabled. */ virtual bool talkaround() const; /** Enables/disables talkaround. */ virtual void enableTalkaround(bool enable); /** Returns @c true if a PTT ID is set. */ virtual bool hasPTTID() const; /** Returns the PTT-ID index. */ virtual unsigned int pttIDIndex() const; /** Sets the PTT-ID index. */ virtual void setPTTIDIndex(unsigned int idx); /** Clears the PTT ID. */ virtual void clearPTTID(); /** Retruns @c true if a group list is assigned. */ virtual bool hasGroupList() const; /** Retunrs the group-list index. */ virtual unsigned int groupListIndex() const; /** Sets the group-list index. */ virtual void setGroupListIndex(unsigned int index); /** Clears the group list index. */ virtual void clearGroupListIndex(); /** Returns @c true if lone-worker is enabled. */ virtual bool loneWorker() const; /** Enables/disables lone-worker. */ virtual void enableLoneWorker(bool enable); /** Constructs a channel object. */ virtual Channel *toChannelObj(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links the channel object. */ virtual bool linkChannelObj(Channel *channel, Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Encodes the given channel. */ virtual bool encode(Channel *channel, Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Some offsets within the codeplug. */ struct Offset: Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int index() { return 0x0000; } static constexpr unsigned int channelType() { return 0x0002; } static constexpr unsigned int power() { return 0x0003; } static constexpr unsigned int rxFrequency() { return 0x0004; } static constexpr unsigned int txFrequency() { return 0x0008; } static constexpr unsigned int transmitContactIndex() { return 0x000c; } static constexpr unsigned int admitCriterion() { return 0x000e; } static constexpr unsigned int colorCode() { return 0x0010; } static constexpr unsigned int timeSlot() { return 0x0011; } static constexpr unsigned int encryptionKeyIndex() { return 0x0014; } static constexpr Bit dcdm() { return {0x0015, 1} ; } static constexpr Bit confirmPivateCall() { return {0x0015, 0}; } static constexpr unsigned int signalingMode() { return 0x0016; } static constexpr unsigned int alarmSystemIndex() { return 0x0018; } static constexpr unsigned int bandwidth() { return 0x0019; } static constexpr unsigned int autoScan() { return 0x001a; } static constexpr unsigned int scanListIndex() { return 0x001b; } static constexpr unsigned int rxSubtoneCode() { return 0x001c; } static constexpr unsigned int rxSubtoneType() { return 0x001e; } static constexpr unsigned int rxDCSMode() { return 0x001f; } static constexpr unsigned int txSubtoneCode() { return 0x0020; } static constexpr unsigned int txSubtoneType() { return 0x0022; } static constexpr unsigned int txDCSMode() { return 0x0023; } static constexpr unsigned int talkaround() { return 0x0025; } static constexpr unsigned int pttIDIndex() { return 0x0028; } static constexpr unsigned int groupListIndex() { return 0x002a; } static constexpr unsigned int loneWorker() { return 0x002f; } /// @endcond }; }; /** Implements the binary encoding of the channel bank. * * Holds up to 1024 @c DR1801UVCodeplug::ChannelElement. * * Memory representation of the channel bank (12004h bytes): * @verbinclude dr1801uv_channelbankelement.txt */ class ChannelBankElement: public Element { protected: /** Hidden constructor. */ ChannelBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ChannelBankElement(uint8_t *ptr); void clear(); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x12004; } /** Returns the number of channels. */ virtual unsigned int channelCount() const; /** Sets the number of channels. */ virtual void setChannelCount(unsigned int count); /** Returns a reference to the channel element that the given index. */ virtual ChannelElement channel(unsigned int index) const; /** Returns the name of the channel at the given index. */ virtual QString channelName(unsigned int index) const; /** Sets the name of the channel at the given index. */ virtual void setChannelName(unsigned int index, const QString &name); /** Decodes all defined channels. */ virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links channels. */ virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Encodes all channles. */ virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Limits of some elements. */ struct Limit { /** Returns the maximum number of channels. */ static constexpr unsigned int channelCount() { return 1024; } /** Returns the maximum length of a channel name. */ static constexpr unsigned int channelNameLength() { return 0x00014; } }; protected: /** Offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int channelCount() { return 0x00000; } static constexpr unsigned int channel() { return 0x00004; } static constexpr unsigned int channelName() { return 0x0d004; } /// @endcond }; }; /** Implements the binary encoding of a contact. * * Memory representation of contact (0018h bytes): * @verbinclude dr1801uv_contactelement.txt */ class ContactElement: public Element { public: /** Possible call types. */ enum class CallType { AllCall = 0x20, PrivateCall = 0x40, GroupCall = 0x80 }; protected: /** Hidden constructor. */ ContactElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ContactElement(uint8_t *ptr); bool isValid() const; void clear(); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x00018; } /** Returns @c true if the contact has a successor. */ virtual bool hasSuccessor() const; /** Returns the index of the next element. */ virtual uint16_t successorIndex() const; /** Sets successor index. */ virtual void setSuccessorIndex(uint16_t index); /** Clears the successor index. */ virtual void clearSuccessorIndex(); /** Returns the number. */ virtual uint32_t dmrID() const; /** Sets the number. */ virtual void setDMRID(uint32_t id); /** Returns the call type. */ virtual DMRContact::Type type() const; /** Sets the call type. */ virtual void setCallType(DMRContact::Type type); /** Returns the name of the contact. */ virtual QString name() const; /** Sets the name of the contact. */ virtual void setName(const QString &name); /** Constructs a DMR contact object from this contact elmeent. */ virtual DMRContact *toContactObj(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links the DMR contact object. */ virtual bool linkContactObj(DMRContact *contact, Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the contact. */ virtual bool encode(DMRContact *contact, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit { /** Maximum length of the contact name. */ static constexpr unsigned int nameLength() { return 16; } }; protected: /** Defines offsets within the element. */ struct Offset : public Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int successorIndex() { return 0x0000; } static constexpr unsigned int nameLength() { return 0x0002; } static constexpr unsigned int dmrID() { return 0x0004; } static constexpr unsigned int callType() { return 0x0007; } static constexpr unsigned int name() { return 0x0008; } /// @endcond }; }; /** Implements the binary encoding of the contact bank. * * The bank holds the list of all contacts defined. See @c DR1801UVCodeplug::ContactElement for * details. * * Memory representation of the contact bank (6008h bytes): * @verbinclude dr1801uv_contactbankelement.txt */ class ContactBankElement: public Element { protected: /** Hidden constructor. */ ContactBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ContactBankElement(uint8_t *ptr); void clear(); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x06004; } /** Returns the number of contacts. */ virtual unsigned int contactCount() const; /** Sets the number of contacts. */ virtual void setContactCount(unsigned int count); /** Returns the index of the first contact. */ virtual unsigned int firstIndex() const; /** Sets the index of the first element. */ virtual void setFirstIndex(unsigned int index); /** Returns a reference to the n-th contact. */ virtual ContactElement contact(unsigned int index) const; /** Decodes all contacts and stores them into the given context and config. */ virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links all contacts. */ virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Encodes all contacts. */ virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some Limits.*/ struct Limit { /** The maximum number of contacts. */ static constexpr unsigned int contactCount() { return 1024; } }; protected: /** Offsets within the element. */ struct Offset : public Element::Offset{ /// @cond DO_NOT_DOCUMENT static constexpr unsigned int contactCount() { return 0x0000; } static constexpr unsigned int firstIndex() { return 0x0002; } static constexpr unsigned int contacts() { return 0x0004; } /// @endcond }; }; /** Implements the binary encoding of a group list. * * Memory representation of group list (44h bytes): * @verbinclude dr1801uv_grouplistelement.txt */ class GroupListElement: public Element { protected: /** Hidden constructor. */ GroupListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ GroupListElement(uint8_t *ptr); bool isValid() const; void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x00044; } /** Returns the index of the group list. */ virtual unsigned int index() const; /** Sets the index of the group list. */ virtual void setIndex(unsigned int index); /** Returns the number of elements in the list. */ virtual unsigned int count() const; /** Sets the number of elements in the list. */ virtual void setCount(unsigned int n); /** Returns @c true if the n-th member index is set. */ virtual bool hasMemberIndex(unsigned int n) const; /** Retuns the n-th member index. */ virtual unsigned int memberIndex(unsigned int n) const; /** Sets the n-th member index. */ virtual void setMemberIndex(unsigned int n, unsigned int index); /** Clears the n-th member index. */ virtual void clearMemberIndex(unsigned int n); /** Constructs a group list object from this elmeent. */ virtual RXGroupList *toGroupListObj(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links the group list object. */ virtual bool linkGroupListObj(RXGroupList *list, Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links the group list object. */ virtual bool encode(RXGroupList *list, Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Some offset within the codeplug. */ struct Offset: public Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int count() { return 0x0000; } static constexpr unsigned int index() { return 0x0002; } static constexpr unsigned int members() { return 0x0004; } /// @endcond }; /** Some limits. */ struct Limit { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int members() { return 10; } /// @endcond }; }; /** Implements the binary encoding of the group-list bank. * * Memory representation of the group-list bank (??h bytes): * @verbinclude dr1801uv_grouplistbankelement.txt */ class GroupListBankElement: public Element { protected: /** Hidden constructor. */ GroupListBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ GroupListBankElement(uint8_t *ptr); void clear(); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x01104; } /** Returns the number of group lists defined. */ virtual unsigned int groupListCount() const; /** Sets the number of group lists. */ virtual void setGroupListCount(unsigned int count); /** Returns a reference to the group list at the given index. */ virtual GroupListElement groupList(unsigned int index) const; /** Decodes all group lists. */ virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links all group lists. */ virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Encodes all group lists. */ virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit { /** The maximum number of group lists. */ static constexpr unsigned int groupListCount() { return 64; } }; protected: /** Some offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int groupListCount() { return 0x0000; } static constexpr unsigned int groupLists() { return 0x00004; } /// @endcond }; }; /** Implements the binary encoding of a zone. * * Memory representation of zone (68h bytes): * @verbinclude dr1801uv_zoneelement.txt */ class ZoneElement: public Element { protected: /** Hidden constructor. */ ZoneElement(uint8_t *ptr, size_t size); public: /** Construtor. */ ZoneElement(uint8_t *ptr); bool isValid() const; void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x00068; } /** Returns the name of the zone. */ virtual QString name() const; /** Sets the name of the zone. */ virtual void setName(const QString &name); /** Retunrs the number of entries. */ virtual unsigned int numEntries() const; /** Returns the channel index of the n-th entry. */ virtual unsigned int entryIndex(unsigned int n); /** Sets the n-th entry index. */ virtual void setEntryIndex(unsigned int n, unsigned int index); /** Returns the index of the zone. */ virtual unsigned int index() const; /** Sets the index of the zone. */ virtual void setIndex(unsigned int index); /** Constructs a zone object from this element. */ virtual Zone *toZoneObj(Context &ctx, const ErrorStack &err=ErrorStack()); /** Links the zone object. */ virtual bool linkZoneObj(Zone *obj, Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the zone. */ virtual bool encode(Zone *obj, Context &ctx, const ErrorStack &err=ErrorStack()); /** Some limits. */ struct Limit { /** The maximum name length. */ static constexpr unsigned int nameLength() { return 32; } /** The maximum number of channels in the zone. */ static constexpr unsigned int memberCount() { return 32; } }; protected: /** Some offset within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int nameLength() { return 0x0020; } static constexpr unsigned int numEntries() { return 0x0022; } static constexpr unsigned int index() { return 0x0024; } static constexpr unsigned int members() { return 0x0028; } /// @endcond }; }; /** Implements the binary encoding of the zone bank. * * Memory representation of the zone bank (3cf8h bytes): * @verbinclude dr1801uv_zonebankelement.txt */ class ZoneBankElement: public Element { protected: /** Hidden constructor. */ ZoneBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ZoneBankElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x03cf8; } /** Returns the number of zones. */ virtual unsigned int zoneCount() const; /** Sets the number of zones. */ virtual void setZoneCount(unsigned int count); /** Returns the so-called up-zone index. */ virtual unsigned int upZoneIndex() const; /** Sets the so-called up-zone index. */ virtual void setUpZoneIndex(unsigned int index); /** Returns the so-called down-zone index. */ virtual unsigned int downZoneIndex() const; /** Sets the so-called down-zone index. */ virtual void setDownZoneIndex(unsigned int index); /** Returns a reference to the n-th zone. */ virtual ZoneElement zone(unsigned int index) const; /** Decodes all zones. */ virtual bool decode(Context &ctx, const ErrorStack &err = ErrorStack()) const; /** Links all zones. */ virtual bool link(Context &ctx, const ErrorStack &err = ErrorStack()) const; /** Encodes all zones. */ virtual bool encode(Context &ctx, const ErrorStack &err = ErrorStack()); public: /** Some limits. */ struct Limit { /** The maximum number of zones. */ static constexpr unsigned int zoneCount() { return 150; } }; protected: /** Some offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int zoneCount() { return 0x0000; } static constexpr unsigned int upZoneIndex() { return 0x0002; } static constexpr unsigned int downZoneIndex() { return 0x0004; } static constexpr unsigned int zones() { return 0x0008; } /// @endcond }; }; /** Implements the binary encoding of the settings element. * * Memory representation of settings element (64h bytes): * @verbinclude dr1801uv_settingselement.txt */ class SettingsElement: public Element { public: /** Possible power-save modes. */ enum class PowerSaveMode { Off = 0, Save50 = 1, Save25 = 2, Save12 = 3 }; /** Possible UI languages. */ enum class Language { SimplifiedChinese = 0, English = 1 }; /** Possible squelch modes. */ enum class SquelchMode { Normal = 0, Silent = 1 }; /** Possible ring tone variants. */ enum class RingTone { Off = 0, RingTone1 = 1, RingTone2 = 2, RingTone3 = 3, RingTone4 = 4, RingTone5 = 5, RingTone6 = 6, RingTone7 = 7, RingTone8 = 8, RingTone9 = 9, RingTone10 = 10, RingTone11 = 11, RingTone12 = 12, RingTone13 = 13, RingTone14 = 14, RingTone15 = 15, RingTone16 = 16, RingTone17 = 17, RingTone18 = 18, RingTone19 = 19, RingTone20 = 20 }; /** Possible backlight time settings. */ enum class BacklightTime { Infinite = 0, Off = 1, On5s = 2, On10s = 3 }; /** Possible tuning modes. */ enum class TuningMode { Channel = 0, VFO = 1 }; /** Possible display modes. */ enum class DisplayMode { Number = 0, Name = 1, Frequency = 2 }; /** Possible dual-watch modes. */ enum class DualWatchMode { Off = 0, DoubleDouble = 1, DoubleSingle = 2 }; /** Possible scan modes. */ enum class ScanMode { Time = 0, Carrier = 1, Search = 2 }; /** Possible boot screen modes. */ enum class BootScreen { Picture = 0, Text = 1 }; protected: /** Hidden constructor. */ SettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ SettingsElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x00064; } /** Returns the radios DMR ID. */ virtual unsigned int dmrID() const; /** Sets the radios DMR ID. */ virtual void setDMRID(unsigned int id); /** Retunrs the the power-save mode. */ virtual PowerSaveMode powerSaveMode() const; /** Sets the power-save mode. */ virtual void setPowerSaveMode(PowerSaveMode mode); /** Returns the VOX sensitivity [0,10]. * 0 means VOX off. */ virtual unsigned int voxSensitivity() const; /** Sets the VOX sensitivity [0,10]. * 0 means VOX off. */ virtual void setVOXSensitivity(unsigned int sens); /** Returns the VOX delay in ms. */ virtual unsigned int voxDelay() const; /** Sets the VOX delay in ms. */ virtual void setVOXDelay(unsigned int ms); /** Returns @c true if encryption is enabled. */ virtual bool encryptionEnabled() const; /** Enables/disables encryption globally. */ virtual void enableEncryption(bool enable); /** Returns @c true if the key-lock is enabled. */ virtual bool keyLockEnabled() const; /** Enable/disable key-lock. */ virtual void enableKeyLock(bool enable); /** Returns the key-lock delay in seconds. */ virtual unsigned int keyLockDelay() const; /** Sets the key-lock delay in seconds. */ virtual void setKeyLockDelay(unsigned int sec); /** Retunrs @c true if the side-key 1 gets locked too. */ virtual bool lockSideKey1() const; /** Enables/disables locking the side-key 1. */ virtual void enableLockSideKey1(bool enable); /** Retunrs @c true if the side-key 2 gets locked too. */ virtual bool lockSideKey2() const; /** Enables/disables locking the side-key 2. */ virtual void enableLockSideKey2(bool enable); /** Retunrs @c true if the PTT gets locked too. */ virtual bool lockPTT() const; /** Enables/disables locking the PTT. */ virtual void enableLockPTT(bool enable); /** Returns the UI language. */ virtual Language language() const; /** Sets the UI language. */ virtual void setLanguage(Language lang); /** Returns the squelch mode. */ virtual SquelchMode squelchMode() const; /** Sets the squelch mode. */ virtual void setSquelchMode(SquelchMode mode); /** Returns @c true, if the roger tones are enabled. */ virtual bool rogerTonesEnabled() const; /** Enables/disables roger tones. */ virtual void enableRogerTones(bool enable); /** Returns @c true if the DMR call out roger tone is enabled. */ virtual bool dmrCallOutToneEnabled() const; /** Enables/disables the DMR call out roger tone. */ virtual void enableDMRCallOutTone(bool enable); /** Returns @c true if the DMR voice end roger tone is enabled. */ virtual bool dmrVoiceEndToneEnabled() const; /** Enables/disables the DMR voice end roger tone. */ virtual void enableDMRVoiceEndTone(bool enable); /** Returns @c true if the DMR call end roger tone is enabled. */ virtual bool dmrCallEndToneEnabled() const; /** Enables/disables the DMR call end roger tone. */ virtual void enableDMRCallEndTone(bool enable); /** Returns @c true if the FM voice end roger tone is enabled. */ virtual bool fmVoiceEndToneEnabled() const; /** Enables/disables the FM voice end roger tone. */ virtual void enableFMVoiceEndTone(bool enable); /** Returns @c true if the FM call out roger tone is enabled. */ virtual bool fmCallOutToneEnabled() const; /** Enables/disables the FM call out roger tone. */ virtual void enableFMCallOutTone(bool enable); /** Returns @c true if the message tone is enabled. */ virtual bool messageToneEnabled() const; /** Enables/disables message tone. */ virtual void enableMessageTone(bool enable); /** Retuns the ring tone. */ virtual RingTone ringTone() const; /** Sets the ring tone. */ virtual void setRingTone(RingTone tone); /** Retuns the radio name. */ virtual QString radioName() const; /** Sets the radio name. */ virtual void setRadioName(const QString &name); /** Returns the reverse burst frequency in Hz. */ virtual float reverseBurstFrequency() const; /** Sets the reverse burst frequency in Hz. */ virtual void setReverseBurstFrequency(float Hz); /** Returns the backlight time settings. */ virtual BacklightTime backlightTime() const; /** Sets the backlight time. */ virtual void setBacklightTime(BacklightTime time); /** Returns @c true, if campanding is enabled. */ virtual bool campandingEnabled() const; /** Enables/disables campanding. */ virtual void enableCampanding(bool enable); /** Retunrs the tuning mode up-direction. */ virtual TuningMode tunigModeUp() const; /** Sets the tuning mode up-direction. */ virtual void setTuningModeUp(TuningMode mode); /** Retunrs the tuning mode down-direction. */ virtual TuningMode tunigModeDown() const; /** Sets the tuning mode down-direction. */ virtual void setTuningModeDown(TuningMode mode); /** Returns the display mode. */ virtual DisplayMode displayMode() const; /** Sets the display mode. */ virtual void setDisplayMode(DisplayMode mode); /** Returns the dual-watch mode. */ virtual DualWatchMode dualWatchMode() const; /** Sets the dual-watch mode. */ virtual void setDualWatchMode(DualWatchMode mode); /** Returns the scan mode. */ virtual ScanMode scanMode() const; /** Sets the scan mode. */ virtual void setScanMode(ScanMode mode); /** Returns the boot-screen mode. */ virtual BootScreen bootScreen() const; /** Sets the boot-screen mode. */ virtual void setBootScreen(BootScreen mode); /** Returns the boot-screen line 1. */ virtual QString bootLine1() const; /** Sets the boot-screen line 1. */ virtual void setBootLine1(const QString &line); /** Returns the boot-screen line 2. */ virtual QString bootLine2() const; /** Sets the boot-screen line 2. */ virtual void setBootLine2(const QString &line); /** Returns @c true if the LED is enabled. */ virtual bool ledEnabled() const; /** Enables/disables the LED. */ virtual void enableLED(bool enabled); /** Returns the lone-worker response time in seconds. */ virtual unsigned int loneWorkerResponseTime() const; /** Sets the lone-worker response time in seconds. */ virtual void setLoneWorkerResponseTime(unsigned int sec); /** Returns the lone-worker reminder time in seconds. */ virtual unsigned int loneWorkerReminderTime() const; /** Sets the lone-worker resminder time in seconds. */ virtual void setLoneWorkerReminderTime(unsigned int sec); /** Returns @c true if the boot password is enabled. */ virtual bool bootPasswordEnabled() const; /** Returns the boot password. */ virtual QString bootPassword() const; /** Sets and enables boot password. */ virtual void setBootPassword(const QString &passwd); /** Clears and disables boot password. */ virtual void clearBootPassword(); /** Returns @c true if the programming password is enabled. */ virtual bool progPasswordEnabled() const; /** Returns the programming password. */ virtual QString progPassword() const; /** Sets and enables programming password. */ virtual void setProgPassword(const QString &passwd); /** Clears and disables programming password. */ virtual void clearProgPassword(); /** Updates configuration. */ virtual bool updateConfig(Config *config, const ErrorStack &err=ErrorStack()); /** Encode from config. */ virtual bool fromConfig(Config *config, const ErrorStack &err=ErrorStack()); protected: /** Some offsets within the element. */ struct Offset: Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int dmrID() { return 0x0000; } static constexpr unsigned int powerSaveEnabled() { return 0x0008; } static constexpr unsigned int powerSaveMode() { return 0x0009; } static constexpr unsigned int voxSensitivity() { return 0x000a; } static constexpr unsigned int voxDelay() { return 0x000c; } static constexpr unsigned int encryptionEnabled() { return 0x000d; } static constexpr unsigned int keyLockDelay() { return 0x000e; } static constexpr Bit lockPTT() { return {0x000f, 0}; } static constexpr Bit lockSideKey1() { return {0x000f, 1}; } static constexpr Bit lockSideKey2() { return {0x000f, 2}; } static constexpr unsigned int language() { return 0x0010; } static constexpr unsigned int squelchMode() { return 0x0011; } static constexpr unsigned int rogerTonesEnabled() { return 0x0013; } static constexpr unsigned int keyLockEnabled() { return 0x0017; } static constexpr unsigned int ringTone() { return 0x0016; } static constexpr unsigned int radioName() { return 0x0018; } static constexpr Bit dmrCallOutToneEnabled() { return {0x0028, 1}; } static constexpr Bit fmCallOutToneEnabled() { return {0x0028, 2}; } static constexpr Bit dmrVoiceEndToneEnabled() { return {0x0028, 3}; } static constexpr Bit fmVoiceEndToneEnabled() { return {0x0028, 4}; } static constexpr Bit dmrCallEndToneEnabled() { return {0x0028, 5}; } static constexpr Bit messageToneEnabled() { return {0x0028, 6}; } static constexpr unsigned int reverseBurstFrequency() { return 0x002c; } static constexpr unsigned int backlightTime() { return 0x002f; } static constexpr unsigned int voxEnabled() { return 0x0030; } static constexpr unsigned int campandingEnabled() { return 0x0032; } static constexpr unsigned int tuningModeUp() { return 0x0036; } static constexpr unsigned int tunigModeDown() { return 0x0037; } static constexpr unsigned int displayMode() { return 0x003c; } static constexpr unsigned int dualWatchMode() { return 0x003d; } static constexpr unsigned int scanMode() { return 0x003e; } static constexpr unsigned int bootScreen() { return 0x003f; } static constexpr unsigned int bootLine1() { return 0x0040; } static constexpr unsigned int bootLine2() { return 0x0048; } static constexpr unsigned int ledEnabled() { return 0x0050; } static constexpr unsigned int loneWorkerResponseTime() { return 0x0051; } static constexpr unsigned int loneWorkerReminderTime() { return 0x005c; } static constexpr Bit progPasswordEnabled() { return {0x0052, 0}; } static constexpr Bit bootPasswordEnabled() { return {0x0052, 1}; } static constexpr unsigned int progPasswordLength() { return 0x0053; } static constexpr unsigned int progPassword() { return 0x0054; } static constexpr unsigned int boolPasswordLength() { return 0x005d; } static constexpr unsigned int bootPassword() { return 0x005e; } /// @endcond }; public: /** Some limits. */ struct Limit { /** Maximum radio name length. */ static constexpr unsigned int radioNameLength() { return 16; } /** Maximum boot-text lines length. */ static constexpr unsigned int bootLineLength() { return 8; } /** Maximum boot password length. */ static constexpr unsigned int bootPasswordLength() { return 6; } /** Maximum programming password length. */ static constexpr unsigned int progPasswordLength() { return 6; } }; }; /** Implements the binary encoding of a scan list element. * * Memory representation of a scan list element (50h bytes): * @verbinclude dr1801uv_scanlistelement.txt */ class ScanListElement: public Element { public: /** Possible priority channel modes. */ enum class PriorityChannel { None = 0, Fixed = 1, Selected = 2 }; /** Possible revert channel modes. */ enum class RevertChannel { LastActive = 0, Fixed = 1, Selected = 2 }; protected: /** Hidden constructor. */ ScanListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ScanListElement(uint8_t *ptr); bool isValid() const; void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x00050; } /** Retunrs the index of the scan list. */ virtual unsigned int index() const; /** Sets the index of the scan list. */ virtual void setIndex(unsigned int idx); /** Returns the number of entries. */ virtual unsigned int entryCount() const; /** Sets the number of entries. */ virtual void setEntryCount(unsigned int num); /** Retunrs the priority channel 1 setting. */ virtual PriorityChannel priorityChannel1() const; /** Sets the priority channel 1 setting. */ virtual void setPriorityChannel1(PriorityChannel mode); /** Returns the priority channel 1 index. */ virtual unsigned int priorityChannel1Index() const; /** Sets the priority channel 1 index. */ virtual void setPriorityChannel1Index(unsigned int index); /** Retunrs the priority channel 2 setting. */ virtual PriorityChannel priorityChannel2() const; /** Sets the priority channel 2 setting. */ virtual void setPriorityChannel2(PriorityChannel mode); /** Returns the priority channel 2 index. */ virtual unsigned int priorityChannel2Index() const; /** Sets the priority channel 2 index. */ virtual void setPriorityChannel2Index(unsigned int index); /** Retunrs the revert channel setting. */ virtual RevertChannel revertChannel() const; /** Sets the revert channel setting. */ virtual void setRevertChannel(RevertChannel mode); /** Returns the revert channel index. */ virtual unsigned int revertChannelIndex() const; /** Sets the revert channel index. */ virtual void setRevertChannelIndex(unsigned int index); /** Returns the name of the scan list. */ virtual QString name() const; /** Sets the name of the scan list. */ virtual void setName(const QString &name); /** Returns the n-th entry index. */ virtual unsigned int entryIndex(unsigned int n); /** Sets the n-th entry index. */ virtual void setEntryIndex(unsigned int n, unsigned int index); /** Constructs a scan-list object from this element. */ virtual ScanList *toScanListObj(Context &ctx, const ErrorStack &err=ErrorStack()); /** Links the scan-list object. */ virtual bool linkScanListObj(ScanList *obj, Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the scan list. */ virtual bool encode(ScanList *obj, Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Some offsets within the element. */ struct Offset { /// @cond DO_NO_DOCUMENT static constexpr unsigned int index() { return 0x0000; } static constexpr unsigned int name() { return 0x0010; } static constexpr unsigned int priorityChannel1() { return 0x0002; } static constexpr unsigned int priorityChannel1Index() { return 0x0004; } static constexpr unsigned int priorityChannel2() { return 0x0003; } static constexpr unsigned int priorityChannel2Index() { return 0x0006; } static constexpr unsigned int revertChannel() { return 0x0008; } static constexpr unsigned int revertChannelIndex() { return 0x000a; } static constexpr unsigned int memberCount() { return 0x0001; } static constexpr unsigned int memberIndices() { return 0x0030; } /// @endcond }; public: /** Some limits. */ struct Limit { /** Maximum length of the name. */ static constexpr unsigned int nameLength() { return 32; } /** Maximum number of channels in scan list. */ static constexpr unsigned int memberCount() { return 16; } }; }; /** Implements the binary encoding of the scan-list bank. * * Holds up to 10 @c DR1801UVCodeplug::ScanListElement. * * Memory representation of the scan list bank (324h bytes): * @verbinclude dr1801uv_scanlistbankelement.txt */ class ScanListBankElement: public Element { protected: /** Hidden constructor. */ ScanListBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ScanListBankElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x00324; } /** Returns the number of scan lists. */ virtual unsigned int scanListCount() const; /** Sets the number of scan lists. */ virtual void setScanListCount(unsigned int count); /** Returns a reference to the n-th scan list. */ virtual ScanListElement scanList(unsigned int index) const; /** Decodes all scan lists. */ virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links the scan lists. */ virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Encodes all scan lists. */ virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Some offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int scanListCount() { return 0x0000; } static constexpr unsigned int scanLists() { return 0x0004; } /// @endcond }; public: /** Some limits. */ struct Limit { /** Maximum number of scan lists. */ static constexpr unsigned int scanListCount() { return 10; } }; }; /** Implements the binary representation of a single message. * * Memory representation of the message element (44h bytes): * @verbinclude dr1801uv_messageelement.txt */ class MessageElement: public Element { protected: /** Hidden constructor. */ MessageElement(uint8_t *ptr, size_t size); public: /** Constructor. */ MessageElement(uint8_t *ptr); void clear(); bool isValid() const; /** The size of the element. */ static constexpr unsigned int size() { return 0x0044; } /** Returns the index of the message. */ virtual unsigned int index() const; /** Sets the index of the message. */ virtual void setIndex(unsigned int index); /** Returns the message text. */ virtual QString text() const; /** Sets the message text. */ virtual void setText(const QString &text); protected: /** Some offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int index() { return 0x0000; } static constexpr unsigned int textLength() { return 0x0001; } static constexpr unsigned int text() { return 0x0004; } /// @endcond }; public: /** Some limits. */ struct Limit { /** The maximum message length. */ static constexpr unsigned int textLength() { return 64; } }; }; /** Implements the binary encoding of the preset message bank element. * * The message bank contains all messages defined. See @c DR1801UVCodeplug::MessageElement for * details. * * Memory representation of the message bank element (164h bytes): * @verbinclude dr1801uv_messagebankelement.txt */ class MessageBankElement: public Element { protected: /** Hidden constructor. */ MessageBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ MessageBankElement(uint8_t *ptr); void clear(); /** Size of the element. */ static constexpr unsigned int size() { return 0x00164; } /** Retunrs the number of elements in the bank. */ virtual unsigned int messageCount() const; /** Sets the number of messages. */ virtual void setMessageCount(unsigned int count); /** Returns a reference to the n-th message. */ virtual MessageElement message(unsigned int n) const; /** Decodes all scan lists. */ virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Encodes all scan lists. */ virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit { static constexpr unsigned int messageCount() { return 8; } ///< Maximum number of messages. }; protected: /** Offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int messageCount() { return 0x0000; } static constexpr unsigned int messages() { return 0x0004; } /// @endcond }; }; /** Implements the binary encoding of the key settings. * * Memory representation of the message bank element (0018h bytes): * @verbinclude dr1801uv_keysettingselement.txt */ class KeySettingsElement: public Element { public: /** Possible key functions. */ enum class Function { None = 0, ToggleAlertTones = 1, EmergencyOn = 2, EmergencyOff = 3, TogglePower = 4, Monitor = 5, DeleteNuisance = 6, OneTouch1 = 7, OneTouch2 = 8, OneTouch3 = 9, OneTouch4 = 10, OneTouch5 = 11, ToggleTalkaround = 13, ToggleScan = 14, ToggleEncryption = 15, ToggleVOX = 16, ZoneSelect = 17, ToggleLoneWorker = 19, PhoneExit = 20 }; protected: /** Hidden constructor. */ KeySettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ KeySettingsElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x00018; } /** Returns the function for the side-key 1, short press. */ virtual Function sideKey1Short() const; /** Sets the function for the side-key 1, short press. */ virtual void setSideKey1Short(Function func); /** Returns the function for the side-key 1, long press. */ virtual Function sideKey1Long() const; /** Sets the function for the side-key 1, long press. */ virtual void setSideKey1Long(Function func); /** Returns the function for the side-key 2, short press. */ virtual Function sideKey2Short() const; /** Sets the function for the side-key 2, short press. */ virtual void setSideKey2Short(Function func); /** Returns the function for the side-key 2, long press. */ virtual Function sideKey2Long() const; /** Sets the function for the side-key 2, long press. */ virtual void setSideKey2Long(Function func); /** Returns the function for the top-key, short press. */ virtual Function topKeyShort() const; /** Sets the function for the top-key, short press. */ virtual void setTopKeyShort(Function func); /** Returns the function for the top-key, long press. */ virtual Function topKeyLong() const; /** Sets the function for the side-key, long press. */ virtual void setTopKeyLong(Function func); protected: /** Internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int sideKey1Short() { return 0x0001; } static constexpr unsigned int sideKey1Long() { return 0x0002; } static constexpr unsigned int sideKey2Short() { return 0x0005; } static constexpr unsigned int sideKey2Long() { return 0x0006; } static constexpr unsigned int topKeyShort() { return 0x0009; } static constexpr unsigned int topKeyLong() { return 0x000a; } /// @endcond }; }; /** Implements the binary encoding of the VFO settings. * * Memory representation of the VFO settings (0090h bytes): * @verbinclude dr1801uv_vfobankelement.txt */ class VFOBankElement: public Element { protected: /** Hidden constructor. */ VFOBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ VFOBankElement(uint8_t *ptr); void clear(); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0090; } /** Returns a reference to the VFO A settings. */ virtual ChannelElement vfoA() const; /** Returns a reference to the VFO A settings. */ virtual ChannelElement vfoB() const; /** Retunrs the name of the first VFO. */ virtual QString nameA() const; /** Sets the name for the first VFO. */ virtual void setNameA(const QString &name); /** Retunrs the name of the second VFO. */ virtual QString nameB() const; /** Sets the name for the second VFO. */ virtual void setNameB(const QString &name); public: /** Some limits for the element. */ struct Limit { /** The maximum length of the VFO names (not set). */ static constexpr unsigned int nameLength() { return 20; } }; protected: /** Some offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int vfoA() { return 0x0000; } static constexpr unsigned int vfoB() { return 0x0034; } static constexpr unsigned int nameA() { return 0x0068; } static constexpr unsigned int nameB() { return 0x007c; } /// @endcond }; }; /** Implements the binary encoding of an encryption key. * * Memory representation of the encryption key bank (000ch bytes): * @verbinclude dr1801uv_encryptionkeyelement.txt */ class EncryptionKeyElement: public Element { protected: /** Hidden constructor. */ EncryptionKeyElement(uint8_t *ptr, size_t size); public: /** Constructor. */ EncryptionKeyElement(uint8_t *ptr); void clear(); bool isValid() const; /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x000c; } /** Returns the index of the key. */ virtual unsigned int index() const; /** Sets the index of the key. */ virtual void setIndex(unsigned int index); /** Retunrs the length of the key. */ virtual unsigned int keyLength() const; /** Returns the key as a string. */ virtual QString key() const; /** Sets the key. */ virtual void setKey(const QString &key); /** Creates a key object for this element. */ virtual EncryptionKey *toKeyObj(Context &ctx, const ErrorStack &err=ErrorStack()); /** Links the key object. */ virtual bool linkKeyObj(EncryptionKey *obj, Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the key. */ virtual bool encode(EncryptionKey *obj, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for the element. */ struct Limit { /** The maximum length of the key. */ static constexpr unsigned int keyLength() { return 8; } }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int index() { return 0x0000; } static constexpr unsigned int length() { return 0x0001; } static constexpr unsigned int key() { return 0x0004; } /// @endcond }; }; /** Implements the binary encoding of the encryption keys. * * Memory representation of the encryption key bank (0078h bytes): * @verbinclude dr1801uv_encryptionkeybankelement.txt */ class EncryptionKeyBankElement: public Element { protected: /** Hidden constructor. */ EncryptionKeyBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ EncryptionKeyBankElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x0078;} /** Returns a reference to the encryption key. */ virtual EncryptionKeyElement key(unsigned int index) const; /** Decodes the all encryption keys defined. */ virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links all encryption keys. */ virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Encodes all keys. */ virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for the key bank. */ struct Limit { /** The number of keys. */ static constexpr unsigned int keyCount() { return 10; } }; }; /** Implements the DTMF system. * * Memory representation of the DTMF system (000ch bytes): * @verbinclude dr1801uv_dtmfsystemelement.txt */ class DTMFSystemElement: public Element { protected: /** Hidden constructor. */ DTMFSystemElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DTMFSystemElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x0c; } /** Returns @c true if the side-tone is enabled. */ virtual bool sideToneEnabled() const; /** Enables/disable side-tone. */ virtual void enableSideTone(bool enable); /** Returns the pre-time in milliseconds. */ virtual unsigned int preTime() const; /** Sets the pre-time in milliseconds. */ virtual void setPreTime(unsigned int ms); /** Returns the code duration in milliseconds. */ virtual unsigned int codeDuration() const; /** Sets the code duration in milliseconds. */ virtual void setCodeDuration(unsigned int ms); /** Returns the code intervall in milliseconds. */ virtual unsigned int codeItervall() const; /** Sets the code intervall in milliseconds. */ virtual void setCodeItervall(unsigned int ms); /** Returns the reset time in milliseconds. */ virtual unsigned int resetTime() const; /** Sets the reset time in milliseconds. */ virtual void setResetTime(unsigned int ms); protected: /** Some offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int sideTone() { return 0x0000; } static constexpr unsigned int preTime() { return 0x0002; } static constexpr unsigned int codeDuration() { return 0x0004; } static constexpr unsigned int codeIntervall() { return 0x0006; } static constexpr unsigned int resetTime() { return 0x0008; } /// @endcond }; }; /** Implements the DTMF systems bank. Holding all defined DTMF systems * (see @c DTMFSystemElement). * * Memory representation of the DTMF system bank (0034h bytes): * @verbinclude dr1801uv_dtmfsystembankelement.txt */ class DTMFSystemBankElement: public Element { protected: /** Hidden constructor. */ DTMFSystemBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DTMFSystemBankElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x34; } /** Returns the number of DTMF systems. */ virtual unsigned int systemCount() const; /** Sets the number of DTMF systems. */ virtual void setSystemCount(unsigned int count); /** Returns a reference to the n-th system. */ virtual DTMFSystemElement system(unsigned int n) const; public: /** Some limits. */ struct Limit { /** The maximum number of DTMF systems. */ static constexpr unsigned int systemCount() { return 4; } }; protected: /** Some offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int systemCount() { return 0x0000; } static constexpr unsigned int systems() { return 0x0004; } /// @endcond }; }; /** Implements the DTMF ID. * * Memory representation of the DTMF ID (014h bytes): * @verbinclude dr1801uv_dtmfidelement.txt */ class DTMFIDElement: public Element { protected: /** Hidden constructor. */ DTMFIDElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DTMFIDElement(uint8_t *ptr); void clear(); /** Size of the element. */ static constexpr unsigned int size() { return 0x0014; } public: /** Returns the DTMF code/number. */ virtual QString number() const; /** Sets the DTMF code/number. */ virtual void setNumber(const QString &number); /** Some limits. */ struct Limit { /** The maximum number of digits of the number. */ static constexpr unsigned int numberLength() { return 16; } }; protected: /** Returns the length of the number. */ virtual unsigned int numberLength() const; /** Sets the number length. */ virtual void setNumberLength(unsigned int len); /** Some internal offset within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int numberLength() { return 0x0000; } static constexpr unsigned int number() { return 0x0004; } /// @endcond }; /** Translation table. */ static QVector _bin_dtmf_tab; }; /** Implements the DTMF ID bank. Holding all defined DTMF IDS * (@c see DTMFIDElement). * * Memory representation of the DTMF ID bank (0144h bytes): * @verbinclude dr1801uv_dtmfidbankelement.txt */ class DTMFIDBankElement: public Element { protected: /** Hidden constructor. */ DTMFIDBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DTMFIDBankElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x0144; } /** Returns the number of IDs encoded. */ virtual unsigned int idCount() const; /** Sets the ID count. */ virtual void setIDCount(unsigned int n); /** Returns a reference to the n-th ID. */ virtual DTMFIDElement id(unsigned int n) const; public: /** Some limits. */ struct Limit { /** The maximum number of IDs. */ static constexpr unsigned int idCount() { return 16; } }; protected: /** Some offsets within the codeplug. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int idCount() { return 0x0000; } static constexpr unsigned int ids() { return 0x0004; } /// @endcond }; }; /** Implements the PTT ID. * * Memory representation of the PTT ID (014h bytes): * @verbinclude dr1801uv_pttidelement.txt */ class PTTIDElement: public Element { public: /** Possible modes of transmission. */ enum class Transmit { None = 0, Start = 1, End = 2, Both = 3 }; /** Possible ID modes. */ enum class IDMode { Forbidden = 0, Each = 1, Once = 2 }; protected: /** Hidden constructor. */ PTTIDElement(uint8_t *ptr, size_t size); public: /** Constructor. */ PTTIDElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x14; } /** Returns @c true, if the DTMF system is set. */ virtual bool hasDTMFSystem() const; /** Returns the DTMF system index. */ virtual unsigned int dtmfSystemIndex() const; /** Sets the DTMF system index. */ virtual void setDTMFSystemIndex(unsigned int index); /** Clears the DTMF system. */ virtual void clearDTMFSystem(); /** Returns the ID transmission mode. */ virtual Transmit transmitMode() const; /** Sets the ID transmission mode. */ virtual void setTransmitMode(Transmit mode); /** Returns the ID mode. */ virtual IDMode idMode() const; /** Sets the ID mode. */ virtual void setIDMode(IDMode mode); /** Returns @c true if the BOT DTMF ID is set. */ virtual bool hasBOTID() const; /** Returns the BOT DTMF ID index. */ virtual unsigned int botIDIndex() const; /** Sets the BOT DTMF ID index. */ virtual void setBOTIDIndex(unsigned int index); /** Clears the BOT DTMF ID index. */ virtual void clearBOTID(); /** Returns @c true if the EOT DTMF ID is set. */ virtual bool hasEOTID() const; /** Returns the EOT DTMF ID index. */ virtual unsigned int eotIDIndex() const; /** Sets the EOT DTMF ID index. */ virtual void setEOTIDIndex(unsigned int index); /** Clears the EOT DTMF ID index. */ virtual void clearEOTID(); protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int dtmfSystemIndex() { return 0x0000; } static constexpr unsigned int transmitMode() { return 0x0001; } static constexpr unsigned int idMode() { return 0x0002; } static constexpr unsigned int botIDIndex() { return 0x0003; } static constexpr unsigned int eotIDIndex() { return 0x0004; } /// @endcond }; }; /** Implements the encoding of the DTMF PTT IDs. Holding all PTT IDs * (see @c PTTIDElement). * * Memory representation of the PTT ID bank (0104h bytes): * @verbinclude dr1801uv_pttidbankelement.txt */ class PTTIDBankElement: public Element { protected: /** Hidden constructor. */ PTTIDBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ PTTIDBankElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x0104; } /** Returns the number of PTT IDs defined. */ virtual unsigned int idCount() const; /** Set the numbe of PTT IDs. */ virtual void setPTTIDCount(unsigned int n); /** Returns a reference to the n-th PTT ID. */ virtual PTTIDElement pttID(unsigned int n); public: /** Some limits. */ struct Limit { /** The maximum number of PTT IDs. */ static constexpr unsigned int idCount() { return 16; } }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int idCount() { return 0x0000; } static constexpr unsigned int ids() { return 0x0004; } /// @endcond }; }; /** Implements the binary encoding of the DTMF signaling settings. * * Memory representation of the settings (029ch bytes): * @verbinclude dr1801uv_dtmfsettingselement.txt */ class DTMFSettingsElement: public Element { public: /** Possible DTMF non-number codes. Usually used as separator and group codes. */ enum class NonNumber { None = 0, A=0xa, B=0xb, C=0xc, D=0xd, Asterisk=0xe, Gate=0xf }; /** Possible responses. */ enum class Response { None=0, Reminder=1, Reply=2, Both=3 }; /** Possible kill actions. */ enum class Kill { DisableTX = 0, DisableRXandTX = 1 }; protected: /** Hidden constructor. */ DTMFSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DTMFSettingsElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x029c; } /** Retunrs the DTMF radio ID as a string. */ virtual QString radioID() const; /** Sets the DTMF radio ID as a string. */ virtual void setRadioID(const QString &id); /** Retunrs the DTMF kill code as a string. */ virtual QString killCode() const; /** Sets the DTMF kill code as a string. */ virtual void setKillCode(const QString &code); /** Retunrs the DTMF wake code as a string. */ virtual QString wakeCode() const; /** Sets the DTMF wake code as a string. */ virtual void setWakeCode(const QString &code); /** Returns the delimiter code. */ virtual NonNumber delimiter() const; /** Sets the delimiter code. */ virtual void setDelimiter(NonNumber code); /** Returns the group code. */ virtual NonNumber groupCode() const; /** Sets the group code. */ virtual void setGroupCode(NonNumber code); /** Returns the decode response. */ virtual Response response() const; /** Sets the decode response. */ virtual void setResponse(Response resp); /** Returns the auto-reset time in seconds [5,60s]. */ virtual unsigned int autoResetTime() const; /** Set the auto-reset time in seconds. */ virtual void setAutoResetTime(unsigned int sec); /** Returns @c true if the kill/wake decoding is endabled. */ virtual bool killWakeEnabled() const; /** Enables/disables the kill/wake decoding. */ virtual void enableKillWake(bool enable); /** Returns the kill type. */ virtual Kill killType() const; /** Sets the kill type. */ virtual void setKillType(Kill type); /** Returns a reference to the DTMF systems. */ virtual DTMFSystemBankElement dtmfSystems() const; /** Returns a reference to the DTMF IDs. */ virtual DTMFIDBankElement dtmfIDs() const; /** Retunrs a reference to the PTT ID bank. */ virtual PTTIDBankElement pttIDs() const; public: /** Some limits. */ struct Limit { /** The maximum length of the radio ID. */ static constexpr unsigned int radioIDLength() { return 5; } /** The maximum length of the kill code. */ static constexpr unsigned int killCodeLength() { return 6; } /** The maximum length of the wake code. */ static constexpr unsigned int wakeCodeLength() { return 6; } }; protected: /** Internal offsets within the settings element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int radioID() { return 0x0000; } static constexpr unsigned int radioIDLength() { return 0x0005; } static constexpr unsigned int killCode() { return 0x0008; } static constexpr unsigned int killCodeLength() { return 0x000e; } static constexpr unsigned int wakeCode() { return 0x0010; } static constexpr unsigned int wakeCodeLength() { return 0x0016; } static constexpr unsigned int delimiter() { return 0x0018; } static constexpr unsigned int groupCode() { return 0x0019; } static constexpr unsigned int response() { return 0x001a; } static constexpr unsigned int autoResetTime() { return 0x001b; } static constexpr unsigned int killWake() { return 0x001c; } static constexpr unsigned int killType() { return 0x001d; } static constexpr unsigned int dtmfSystems() { return 0x0020; } static constexpr unsigned int dtmfIDs() { return 0x0054; } static constexpr unsigned int pttIDs() { return 0x0198; } /// @endcond }; }; /** Implements the binary encoding of the alarm system. * * Memory representation of the alarm system (0018h bytes): * @verbinclude dr1801uv_alarmsystembankelement.txt */ class AlarmSystemElement: public Element { protected: /** Hidden constructor. */ AlarmSystemElement(uint8_t *ptr, size_t size); public: /** Constructor. */ AlarmSystemElement(uint8_t *ptr); void clear(); bool isValid() const; /** The size of the alarm system element. */ static constexpr unsigned int size() { return 0x0018; } /** Returns the index of the system. */ virtual unsigned int index() const; /** Sets the index of the element. */ virtual void setIndex(unsigned int index); /** Clears the index. */ virtual void clearIndex(); /** Returns @c true if the alarm is enabled. */ virtual bool alarmEnabled() const; /** Enables/disables the alarm. */ virtual void enableAlarm(bool enable); /** Returns @c true if no alarm channel is specified. */ virtual bool noAlarmChannel() const; /** Returns @c true if the alarm channel is the current channel. */ virtual bool alarmChannelIsSelected() const; /** Returns the index of the alarm channel. */ virtual unsigned int alarmChannelIndex() const; /** Sets the alarm channel index. */ virtual void setAlarmChannelIndex(unsigned int index); /** Sets the alarm channel to the selected chanel. */ virtual void setAlarmChannelSelected(); /** Clears the alarm channel. */ virtual void clearAlarmChannel(); /** Returns the name. */ virtual QString name() const; /** Sets the system name. */ virtual void setName(const QString &name); public: /** Some limits. */ struct Limit { /** The maximum name length. */ static constexpr unsigned int nameLength() { return 16; } }; protected: /** Some internal offsets within the codeplug. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int index() { return 0x0000; } static constexpr unsigned int alarmEnabled() { return 0x0001; } static constexpr unsigned int alarmChannelIndex() { return 0x0004; } static constexpr unsigned int name() { return 0x0008; } /// @endcond }; }; /** Implements the binary encoding of the alarm system bank. * * Holding all alarm systems, see @c AlarmSystemElement. * * Memory representation of the alarm system bank (00c4h bytes): * @verbinclude dr1801uv_alarmsystembankelement.txt */ class AlarmSystemBankElement: public Element { protected: /** Hidden constructor. */ AlarmSystemBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ AlarmSystemBankElement(uint8_t *ptr); void clear(); /** The size of the alarm system bank. */ static constexpr unsigned int size() { return 0x00c4; } /** Returns the number of alarm systems defined. */ virtual unsigned int alarmSystemCount() const; /** Sets the number of alarm systems. */ virtual void setAlarmSystemCount(unsigned int n); /** Returns a reference to the n-th alarm system. */ virtual AlarmSystemElement alarmSystem(unsigned int n) const; public: /** Some limits. */ struct Limit { /** The maximum number of alarm systems. */ static constexpr unsigned int alarmSystemCount() { return 8; } }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int alarmSystemCount() { return 0x0000; } static constexpr unsigned int alarmSystems() { return 0x0004; } /// @endcond }; }; /** Implements the binary encoding of the DMR settings. * * Memory representation of the DMR settings (0010h bytes): * @verbinclude dr1801uv_dmrsettingselement.txt */ class DMRSettingsElement: public Element { public: /** Possible SMS encodings. */ enum class SMSFormat { CompressedIP = 0, DefinedData = 1, IPData = 2 }; protected: /** Hidden constructor. */ DMRSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DMRSettingsElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x0010; } /** Returns the hold-time in seconds. */ virtual unsigned int holdTime() const; /** Sets the hold-time in seconds. */ virtual void setHoldTime(unsigned int sec); /** Returns the remote-listen duration in seconds. */ virtual unsigned int remoteListen() const; /** Sets the remote-listen duration in seconds. */ virtual void setRemoteListen(unsigned int sec); /** Returns the active wait period in ms. */ virtual unsigned int activeWait() const; /** Sets the active wait period in ms. */ virtual void setActiveWait(unsigned int ms); /** Returns the active resend count. */ virtual unsigned int activeResend() const; /** Sets the active resend cound. */ virtual void setActiveResend(unsigned int count); /** Returns the pre-send count. */ virtual unsigned int preSend() const; /** Sets the pre-send count. */ virtual void setPreSend(unsigned int count); /** Returns the voice head count. */ virtual unsigned int voiceHead() const; /** Sets the voice head count. */ virtual void setVoiceHead(unsigned int count); /** Returns the SMS format. */ virtual SMSFormat smsFormat() const; /** Sets the SMS format. */ virtual void setSMSFormat(SMSFormat format); /** Returns @c true if the kill encoding is enabled. */ virtual bool killEnc() const; /** Enables kill encoding. */ virtual void enableKillEnc(bool enable); /** Returns @c true if the kill decoding is enabled. */ virtual bool killDec() const; /** Enables kill decoding. */ virtual void enableKillDec(bool enable); /** Returns @c true if the active encoding is enabled. */ virtual bool activeEnc() const; /** Enables active encoding. */ virtual void enableActiveEnc(bool enable); /** Returns @c true if the active decoding is enabled. */ virtual bool activeDec() const; /** Enables active decoding. */ virtual void enableActiveDec(bool enable); /** Returns @c true if the check encoding is enabled. */ virtual bool checkEnc() const; /** Enables check encoding. */ virtual void enableCheckEnc(bool enable); /** Returns @c true if the check decoding is enabled. */ virtual bool checkDec() const; /** Enables check decoding. */ virtual void enableCheckDec(bool enable); /** Returns @c true if the call alter encoding is enabled. */ virtual bool callAlterEnc() const; /** Enables call alter encoding. */ virtual void enableCallAlterEnc(bool enable); /** Returns @c true if the call alter decoding is enabled. */ virtual bool callAlterDec() const; /** Enables call alter decoding. */ virtual void enableCallAlterDec(bool enable); /** Returns @c true if the remote monitor encoding is enabled. */ virtual bool remoteMonitorEnc() const; /** Enables remote monitor encoding. */ virtual void enableRemoteMonitorEnc(bool enable); /** Returns @c true if the remote monitor decoding is enabled. */ virtual bool remoteMonitorDec() const; /** Enables remote monitor decoding. */ virtual void enableRemoteMonitorDec(bool enable); /** Decodes the DMR settings. */ virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Encodes all keys. */ virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit: public Element::Limit { /** The range of hold time. */ static constexpr Range holdTime() { return {1, 90}; } /** The range of remote listen duration. */ static constexpr Range remoteListen() { return {10, 120}; } /** The range of active wait period. */ static constexpr Range activeWait() { return {120, 600}; } /** The range of active resend count. */ static constexpr Range activeResend() { return {1, 10}; } /** The range of pre-send count. */ static constexpr Range preSend() { return {4, 15}; } /** The range of voice head count. */ static constexpr Range voiceHead() { return {0, 20}; } }; protected: /** Internal offsets within the element. */ struct Offset: public Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int holdTime() { return 0x0000; } static constexpr unsigned int remoteListen() { return 0x0001; } static constexpr unsigned int activeWait() { return 0x0002; } static constexpr unsigned int activeResend() { return 0x0003; } static constexpr unsigned int preSend() { return 0x0004; } static constexpr unsigned int killEnc() { return 0x0005; } static constexpr unsigned int activeEnc() { return 0x0006; } static constexpr unsigned int checkEnc() { return 0x0007; } static constexpr unsigned int killDec() { return 0x0008; } static constexpr unsigned int activeDec() { return 0x0009; } static constexpr unsigned int checkDec() { return 0x000a; } static constexpr unsigned int smsFormat() { return 0x000b; } static constexpr unsigned int voiceHead() { return 0x000c; } static constexpr Bit callAlterEnc() { return {0x000d, 0}; } static constexpr Bit callAlterDec() { return {0x000d, 1}; } static constexpr Bit remoteMonitorEnc() { return {0x000d, 2}; } static constexpr Bit remoteMonitorDec() { return {0x000d, 3}; } /// @endcond }; }; /** Implements the binary encoding of a one-touch setting. * * Memory representation of the one-touch setting (0008h bytes): * @verbinclude dr1801uv_onetouchsettingelement.txt */ class OneTouchSettingElement: public Element { public: /** Possible actions to perform. */ enum class Action { Call = 0, Message = 1 }; /** Possible one-touch types. */ enum class Type { Disabled = 0, DMR=1, FM=2 }; protected: /** Hidden constructor. */ OneTouchSettingElement(uint8_t *ptr, size_t size); public: /** Constructor. */ OneTouchSettingElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x0008; } /** Returns @c true if the setting is enabled. */ bool isValid() const; /** Returns @c true if a contact is set. */ virtual bool hasContact() const; /** Returns the DMR contact index. */ virtual unsigned int contactIndex() const; /** Sets the contact index. */ virtual void setContactIndex(unsigned int index); /** Clears the contact. */ virtual void clearContact(); /** Returns the one-touch action. */ virtual Action action() const; /** Sets the one-touch action. */ virtual void setAction(Action action); /** Returns @c true, if a message is set. */ virtual bool hasMessage() const; /** Returns the message index. */ virtual unsigned int messageIndex() const; /** Sets the message index. */ virtual void setMessageIndex(unsigned int index); /** Clears the message. */ virtual void clearMessage(); /** Retuns the type of the one-touch setting. */ virtual Type type() const; /** Sets the type of the one-touch setting. */ virtual void setType(Type type); /** Returns @c true if a DTMF ID is set. */ virtual bool hasDTMFID() const; /** Returns the DTMF ID index. */ virtual unsigned int dtmfIDIndex() const; /** Sets the DTMF ID index. */ virtual void setDTMFIDIndex(unsigned int index); /** Clears the DTMF ID index. */ virtual void clearDTMFIDIndex(); protected: /** Some internal offsets. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int contactIndex() { return 0x0000; } static constexpr unsigned int action() { return 0x0002; } static constexpr unsigned int messageIndex() { return 0x0003; } static constexpr unsigned int type() { return 0x0004; } static constexpr unsigned int dtmfIDIndex() { return 0x0005; } /// @endcond }; }; /** Implements the binary encoding of the one-touch settings. * * Holding all one-touch settings, see @c OneTouchSettingElement. * * Memory representation of the one-touch settings (0028h bytes): * @verbinclude dr1801uv_onetouchsettingselement.txt */ class OneTouchSettingsElement: public Element { protected: /** Hidden constructor. */ OneTouchSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ OneTouchSettingsElement(uint8_t *ptr); void clear(); /** The size of the element. */ static constexpr unsigned int size() { return 0x0028; } /** Returns the number of one-touch settings. */ virtual unsigned int settingsCount() const; /** Returns a reference to the n-th one-touch setting. */ virtual OneTouchSettingElement setting(unsigned int n) const; public: /** Some limits. */ struct Limit { /** Returns the maximum number of one-touch settings. */ static constexpr unsigned int settingsCount() { return 5; } }; protected: /** Some internal offset. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int settings() { return 0x0000; } /// @endcond }; }; public: /** Default constructor. */ explicit DR1801UVCodeplug(QObject *parent = nullptr); Config * preprocess(Config *config, const ErrorStack &err) const; bool postprocess(Config *config, const ErrorStack &err) const; bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const; bool encode(Config *config, const Flags &flags, const ErrorStack &err=ErrorStack()); bool decode(Config *config, const ErrorStack &err=ErrorStack()); protected: /** Decode codeplug elements. */ virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack()); /** Link decoded elements. */ virtual bool linkElements(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encode all elements. */ virtual bool encodeElements(Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Defines the offsets within the codeplug. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int size() { return 0x1dd90; } static constexpr unsigned int settings() { return 0x003b4; } static constexpr unsigned int zoneBank() { return 0x00418; } static constexpr unsigned int messageBank() { return 0x04110; } static constexpr unsigned int contactBank() { return 0x04334; } static constexpr unsigned int scanListBank() { return 0x0a338; } static constexpr unsigned int channelBank() { return 0x0a65c; } static constexpr unsigned int keySettings() { return 0x1c6c4; } static constexpr unsigned int groupListBank() { return 0x1c6dc; } static constexpr unsigned int encryptionKeyBank() { return 0x1d7e0; } static constexpr unsigned int dtmfSettings() { return 0x1d858; } static constexpr unsigned int alarmSettings() { return 0x1daf4; } static constexpr unsigned int dmrSettings() { return 0x1dbb8; } static constexpr unsigned int vfoBank() { return 0x1dd00; } /// @endcond }; }; #endif // DR1801UVCODEPLUG_HH qdmr-0.12.3/lib/dr1801uv_filereader.cc000066400000000000000000000023151501654372000172750ustar00rootroot00000000000000#include "dr1801uv_filereader.hh" #include #include #define SEGMENT0_ADDR 0x00000000 #define SEGMENT0_SIZE 0x0001dd90 bool DR1801UVFileReader::read(const QString &filename, DR1801UVCodeplug *codeplug, const ErrorStack &err) { // Check file properties QFileInfo info(filename); if (! info.exists()) { errMsg(err) << "Cannot open file '" << filename << "': File does not exisist."; return false; } if (0x1dd90 != info.size()) { errMsg(err) << "Cannot read codeplug file '" << filename << "': File size is not 1dd90h bytes."; return false; } // Open file QFile file(filename); if (! file.open(QFile::ReadOnly)) { errMsg(err) << "Cannot open file '" << filename << "': " << file.errorString() << "."; return false; } // Read file content char *ptr = (char *)codeplug->data(SEGMENT0_ADDR); size_t n = SEGMENT0_SIZE; while (0 < n) { int nread = file.read(ptr, n); if (0 > nread) { errMsg(err) << "Cannot read codeplug file '" << filename << "': " << file.errorString() << "."; file.close(); return false; } n -= nread; ptr += nread; } return true; } qdmr-0.12.3/lib/dr1801uv_filereader.hh000066400000000000000000000015251501654372000173110ustar00rootroot00000000000000#ifndef DR1801UVFILEREADER_HH #define DR1801UVFILEREADER_HH #include "dr1801uv_codeplug.hh" /** Methods to read manufacturer codeplug files. * * The file format of the stock CPS is pretty simple. It is a one-to-one dump of the codeplug * data as written to the device. This makes the decoding of the manufacturer codeplug files very * easy. * * @ingroup dr1801uv */ class DR1801UVFileReader { public: /** Reads manufacturer codeplug file into given codeplug object. * @param filename Specifies the file to read. * @param codeplug Specifies the codeplug object to store read codeplug. * @param err On error, contains an error message. * @returns @c true on success and @c false on error. */ static bool read(const QString &filename, DR1801UVCodeplug *codeplug, const ErrorStack &err=ErrorStack()); }; #endif // GD77FILEREADER_HH qdmr-0.12.3/lib/dr1801uv_interface.cc000066400000000000000000000340501501654372000171340ustar00rootroot00000000000000#include "dr1801uv_interface.hh" #include #include "logger.hh" #include "dr1801uv.hh" // Identifies itself as "Prolific USB-serial controller" (chip in cable). #define USB_VID 0x067b #define USB_PID 0x23a3 #define WRITE_CODEPLUG_OFFSET 0x00000304 #define WRITE_CODEPLUG_SIZE 0x0001da8c /* ********************************************************************************************* * * Implementation of DR1801UVInterface::PrepareReadRequest * ********************************************************************************************* */ DR1801UVInterface::PrepareReadRequest::PrepareReadRequest(uint32_t baudrate) : baudrate(qToBigEndian(baudrate)) { // pass... } void DR1801UVInterface::PrepareReadRequest::setBaudrate(uint32_t baudrate) { this->baudrate = qToBigEndian(baudrate); } /* ********************************************************************************************* * * Implementation of DR1801UVInterface::PrepareReadResponse * ********************************************************************************************* */ bool DR1801UVInterface::PrepareReadResponse::isSuccessful() const { return 0x01 == success; } uint32_t DR1801UVInterface::PrepareReadResponse::getSize() const { return qFromBigEndian(size); } /* ********************************************************************************************* * * Implementation of DR1801UVInterface::PrepareWriteRequest * ********************************************************************************************* */ DR1801UVInterface::PrepareWriteRequest::PrepareWriteRequest(uint32_t s, uint32_t speed, uint16_t crc=0) : _unknown0(qToBigEndian((uint16_t)0x0001)), size(qToBigEndian(s)), checksum(crc), baudRate(qToBigEndian(speed)) { // pass... } void DR1801UVInterface::PrepareWriteRequest::updateCRC(const uint8_t *data, size_t length) { uint16_t *ptr = (uint16_t*)data; for (unsigned int i=0; i progress, const ErrorStack &err) { if (! isOpen()) { errMsg(err) << "Cannot read codeplug from device: Interface not open."; return false; } if (IDLE != _state) { errMsg(err) << "Cannot read codeplug from device: Interface not in idle state. " << "State=" << _state << "."; return false; } PrepareReadResponse resp; if (! prepareReading(ReadSpeed, resp, err)) { errMsg(err) << "Cannot start reading the codeplug from " << _identifier << "."; _state = ERROR; return false; } unsigned int bytesToTransfer = resp.getSize(), total = bytesToTransfer; if (! startReading(err)) { errMsg(err) << "Cannot start reading the codeplug form " << _identifier << "."; _state = ERROR; return false; } if (codeplug.image(0).element(0).memSize() != bytesToTransfer) { errMsg(err) << "Codeplug size mismatch! Expected " << codeplug.image(0).element(0).memSize() << " radio sends " << bytesToTransfer << "."; _state = ERROR; return false; } if (progress) progress(0, total); logDebug() << "Start reading " << bytesToTransfer << "b of codeplug memory."; unsigned int offset = 0; while (bytesToTransfer) { unsigned n = std::min(256U, bytesToTransfer); if (! AuctusA6Interface::read(codeplug.image(0).data(offset), n, 2000, err)) { errMsg(err) << "Cannot read from device '" << portName() << "'."; _state = ERROR; return false; } offset += n; bytesToTransfer -= n; if (progress) progress(offset, total); } // Set the baud-rate back to 9600 logDebug() << "Reset baudrate to " << DefaultSpeed << "."; if (! this->setBaudRate(DefaultSpeed)) { errMsg(err) << "Cannot set baud-rate of serial port '" << portName() << "'."; return false; } QThread::msleep(250); _state = IDLE; return true; } bool DR1801UVInterface::writeCodeplug( const Codeplug &codeplug, std::function progress, const ErrorStack &err) { if (! isOpen()) { errMsg(err) << "Cannot write codeplug to device: Interface not open."; return false; } if (IDLE != _state) { errMsg(err) << "Cannot write codeplug to device: Interface not in idle state. " << "State=" << _state << "."; return false; } if (! enterProgrammingMode(err)) { errMsg(err) << "Cannot write codeplug."; return false; } // Compute CRC over codeplug uint16_t crc = 0; unsigned int offset = WRITE_CODEPLUG_OFFSET, bytesToTransfer = WRITE_CODEPLUG_SIZE, total = bytesToTransfer; for (unsigned int i=0; i DR1801UVInterface::detect(bool saveOnly) { // if non-save devices are allowed, search for all USB AMC-CDC devices. if (! saveOnly) return USBSerial::detect(); // otherwise, return none return QList(); } bool DR1801UVInterface::getDeviceInfo(QString &info, const ErrorStack &err) { uint8_t response[255], respLen = 255; if (! send_receive(REQUEST_INFO, nullptr, 0, response, respLen, err)) { errMsg(err) << "Cannot request device information."; _state = ERROR; return false; } // check status byte if (1 > respLen) { errMsg(err) << "Invalid response lenght."; errMsg(err) << "Cannot request device information."; _state = ERROR; return false; } if (0x01 != response[0]) { errMsg(err) << "Device returned no success. Expected 01h, got " << QString::number(response[0],16) << "h."; errMsg(err) << "Cannot request device information."; _state = ERROR; return false; } if (1 < respLen) { logDebug() << QByteArray((const char *)response+1, respLen-1); info = QString::fromLatin1((char *)(response+1), respLen-1); } return true; } /* ********************************************************************************************* * * Internal methods communicating with the device. * ********************************************************************************************* */ bool DR1801UVInterface::enterProgrammingMode(const ErrorStack &err) { uint8_t response[255], respLen=255; if (! send_receive(ENTER_PROGRAMMING_MODE, nullptr, 0, response, respLen, err)) { errMsg(err) << "Cannot enter programming mode."; _state = ERROR; return false; } if ((1 != respLen) || (0x01 != response[0])) { errMsg(err) << "Unexpected response from radio. Expected 01h, got " << QString::number(response[0], 16) << "h."; errMsg(err) << "Cannot enter programming mode."; return false; } return true; } bool DR1801UVInterface::checkProgrammingPassword(const ErrorStack &err) { // No argument -> no password uint8_t request[] = {0x00}, reqLen=sizeof(request), response[255], respLen=255; if (! send_receive(CHECK_PROG_PASSWORD, request, reqLen, response, respLen, err)) { errMsg(err) << "Cannot enter programming mode."; _state = ERROR; return false; } if ((1 != respLen) || (0x02 != response[0])) { errMsg(err) << "Unexpected response from radio. Expected 02h, got " << QString::number(response[0], 16) << "h."; errMsg(err) << "Cannot enter programming mode."; return false; } return true; } bool DR1801UVInterface::prepareReading(uint32_t baudrate, PrepareReadResponse &response, const ErrorStack &err) { PrepareReadRequest request(baudrate); uint8_t respSize = sizeof(response); if (! send_receive(PREPARE_CODEPLUG_READ, (uint8_t *)&request, sizeof(request), (uint8_t *)&response, respSize, err)) { errMsg(err) << "Cannot prepare reading of codeplug."; return false; } if ((sizeof(PrepareReadResponse) != respSize) || (! response.isSuccessful())) { errMsg(err) << "Prepare reading of codeplug failed."; return false; } logDebug() << "Set baudrate to " << baudrate << "."; if (! this->setBaudRate(baudrate)) { errMsg(err) << "Cannot set baud-rate of serial port '" << portName() << "'."; return false; } QThread::msleep(1000); return true; } bool DR1801UVInterface::prepareWriting(uint32_t size, uint32_t baudrate, uint16_t crc, const ErrorStack &err) { PrepareWriteRequest request(size, baudrate, crc); PrepareReadResponse response; uint8_t respSize = sizeof(request); if (! send_receive(PREPARE_CODEPLUG_WRITE, (uint8_t *)&request, sizeof(request), (uint8_t *)&response, respSize, err)) { errMsg(err) << "Cannot prepare writing of codeplug."; return false; } if ((sizeof(PrepareWriteResponse) != respSize) || (! response.isSuccessful())) { errMsg(err) << "Prepare writing of codeplug failed."; return false; } logDebug() << "Set baudrate to " << baudrate << "."; if (! this->setBaudRate(baudrate)) { errMsg(err) << "Cannot set baud-rate of serial port '" << portName() << "'."; return false; } QThread::msleep(1000); _state = WRITE_THROUGH; return true; } bool DR1801UVInterface::receiveWriteACK(const ErrorStack &err) { // Wait for device response uint16_t rcode = CODEPLUG_WRITTEN; CodeplugWriteResponse response; uint8_t size = sizeof(CodeplugWriteResponse); if (! receive(rcode, (uint8_t*) &response, size, err)) { errMsg(err) << "Cannot complete codeplug write."; return false; } if (((rcode & 0x7fff) != CODEPLUG_WRITTEN) || (! response.isSuccessful())) { errMsg(err) << "Cannot complete codeplug write."; return false; } // Set the baud-rate back to default speed logDebug() << "Reset baudrate to " << DefaultSpeed; if (! this->setBaudRate(DefaultSpeed)) { errMsg(err) << "Cannot set baud-rate of serial port '" << portName() << "'."; return false; } QThread::msleep(1000); _state = IDLE; return true; } bool DR1801UVInterface::startReading(const ErrorStack &err) { if (! send(START_READ_DATA, 0, 0, err)) { errMsg(err) << "Cannot initalize codeplug read."; return false; } flush(); _state = READ_THROUGH; return true; } qdmr-0.12.3/lib/dr1801uv_interface.hh000066400000000000000000000135321501654372000171500ustar00rootroot00000000000000#ifndef DR1801UVINTERFACE_HH #define DR1801UVINTERFACE_HH #include "auctus_a6_interface.hh" #include // Forward declarations class Codeplug; /** Implements the actual interface to the DR-1801UV, which builds upon the @c AuctusA6Interface. * * @ingroup dr1801uv */ class DR1801UVInterface : public AuctusA6Interface { Q_OBJECT public: /** Constructs an interface to the BTECH DR-1801UV from the specifeid USB descriptor. */ DR1801UVInterface(const USBDeviceDescriptor &descriptor, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr); RadioInfo identifier(const ErrorStack &err); /** Reads the codeplug from the device blocking. */ bool readCodeplug(Codeplug &codeplug, std::function progress, const ErrorStack &err=ErrorStack()); /** Writes the codeplug to the device blocking. */ bool writeCodeplug(const Codeplug &codeplug, std::function progress, const ErrorStack &err=ErrorStack()); bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()); bool read_finish(const ErrorStack &err=ErrorStack()); bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()); bool write_finish(const ErrorStack &err=ErrorStack()); public: /** Returns some information about this interface. */ static USBDeviceInfo interfaceInfo(); /** Tries to find all interfaces connected AnyTone radios. */ static QList detect(bool saveOnly=true); protected: /** Some default speeds. */ enum DefaultTransferSpeed { /** Initial speed, used to send commands. */ DefaultSpeed = QSerialPort::Baud9600, /** Speed for reading the codeplug. */ ReadSpeed = QSerialPort::Baud115200, /** Speed for writing the codeplug. */ WriteSpeed = QSerialPort::Baud9600 }; /** Implemented commands. */ enum Command { REQUEST_INFO = 0x0000, ///< Returns some information about the device. ENTER_PROGRAMMING_MODE = 0x0104, ///< Puts the device into the programming mode. CHECK_PROG_PASSWORD = 0x002b, ///< Checks the programming password. PREPARE_CODEPLUG_READ = 0x0100, ///< Sets baud rate and prepares codeplug read. START_READ_DATA = 0x0101, ///< Actually starts reading the codeplug. PREPARE_CODEPLUG_WRITE = 0x0102, ///< Prepares writing the codeplug. CODEPLUG_WRITTEN = 0x0103 ///< Send by the device once the codeplug was written. }; /** Request to set transfer speed and load codeplug into RAM for transfer. */ struct Q_PACKED PrepareReadRequest { /** The transfer speed. */ uint32_t baudrate; /** Constructor. */ PrepareReadRequest(uint32_t baudrate); /** Sets the baudrate. */ void setBaudrate(uint32_t baudrate); }; /** Response to a @c PREPARE_CODEPLUG_READ command. * Contains some information about the codeplug to read. */ struct Q_PACKED PrepareReadResponse { uint8_t success; ///< If successful, set to 0x01. uint32_t size; ///< Contains the size of the codeplug in big-endian. uint8_t _unknown[10]; ///< Some additional information. /** Returns @c true, if the operation was successful. */ bool isSuccessful() const; /** Returns the codeplug size in bytes. */ uint32_t getSize() const; }; /** Request to prepare a codeplug write. * Contains some information about the codeplug to write and transfer speed to use. */ struct Q_PACKED PrepareWriteRequest { uint16_t _unknown0; ///< Just set to 0x0001 uint32_t size; ///< Contains the size of the codeplug in big-endian. uint16_t checksum; ///< A checksum over the codeplug to be written. uint32_t baudRate; ///< The baud rate for the transfer. /** Constructor. */ PrepareWriteRequest(uint32_t size, uint32_t speed, uint16_t crc); /** Updates the crc with the data. */ void updateCRC(const uint8_t *data, size_t length); }; /** Response to a prepare-write request. Just contains a status word. */ struct Q_PACKED PrepareWriteResponse { uint16_t responseCode; ///< Response code. /** Returns @c true, if the operation was successful. */ bool isSuccessful() const; }; /** Response to a codeplug write. Just contains a status word. */ struct Q_PACKED CodeplugWriteResponse { uint8_t success; ///< Response code. uint16_t unknown; ///< Some unkown data. /** Returns @c true, if the operation was successful. */ bool isSuccessful() const; }; /** Puts the device into programming mode. */ bool enterProgrammingMode(const ErrorStack &err=ErrorStack()); /** Reads some information about the device. */ bool getDeviceInfo(QString &info, const ErrorStack &err=ErrorStack()); /** Checks the if a programming password is set. */ bool checkProgrammingPassword(const ErrorStack &err=ErrorStack()); /** Prepares reading the codeplug. */ bool prepareReading(uint32_t baudrate, PrepareReadResponse &response, const ErrorStack &err=ErrorStack()); /** Starts the read operation. Once the operation is complete, the device will close the * connection. */ bool startReading(const ErrorStack &err=ErrorStack()); /** Repares the codeplug write. */ bool prepareWriting(uint32_t size, uint32_t baudrate, uint16_t crc, const ErrorStack &err=ErrorStack()); /** Receives the ACK for writing the codeplug. */ bool receiveWriteACK(const ErrorStack &err=ErrorStack()); protected: /** Holds the device identifier, once read. */ QString _identifier; }; #endif // DR1801UVINTERFACE_HH qdmr-0.12.3/lib/dr1801uv_limits.cc000066400000000000000000000213651501654372000165020ustar00rootroot00000000000000#include "dr1801uv_limits.hh" #include "channel.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "scanlist.hh" #include "zone.hh" #include "gpssystem.hh" #include "roamingzone.hh" #include "dr1801uv_codeplug.hh" DR1801UVLimits::DR1801UVLimits(QObject *parent) : RadioLimits(true, parent) { // Define limits for call-sign DB _hasCallSignDB = false; _callSignDBImplemented = false; _numCallSignDBEntries = 0; // Define limits for satellite config _hasSatelliteConfig = false; _satelliteConfigImplemented = false; _numSatellites = 0; add("settings", new RadioLimitItem { { "introLine1", new RadioLimitString( -1, DR1801UVCodeplug::SettingsElement::Limit::bootLineLength(), RadioLimitString::ASCII) }, { "introLine2", new RadioLimitString( -1, DR1801UVCodeplug::SettingsElement::Limit::bootLineLength(), RadioLimitString::ASCII) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum { unsigned(Channel::Power::Low), unsigned(Channel::Power::High) } }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, -1) } /// @todo check default radio ID. } ); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList{ { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject { {"name", new RadioLimitString( 1, DR1801UVCodeplug::SettingsElement::Limit::radioNameLength(), RadioLimitString::ASCII) }, {"id", new RadioLimitUInt(0, 16777215)} } } } ); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, DR1801UVCodeplug::ContactBankElement::Limit::contactCount(), new RadioLimitObject { { "name", new RadioLimitString( 1, DR1801UVCodeplug::ContactElement::Limit::nameLength(), RadioLimitString::ASCII) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum { (unsigned) DMRContact::PrivateCall, (unsigned) DMRContact::GroupCall, (unsigned) DMRContact::AllCall }}, { "number", new RadioLimitUInt(0, 16777215) } } }, { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() } } ); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 1, DR1801UVCodeplug::GroupListBankElement::Limit::groupListCount(), new RadioLimitObject { { "name", new RadioLimitString(1, -1, RadioLimitString::ASCII) }, // Name is not encoded. { "contacts", new RadioLimitGroupCallRefList(1, 32) } }) ); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, DR1801UVCodeplug::ChannelBankElement::Limit::channelCount(), new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString( 1, DR1801UVCodeplug::ChannelBankElement::Limit::channelNameLength(), RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromHz(136.), Frequency::fromHz(174.)}, {Frequency::fromHz(400.), Frequency::fromHz(480.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromHz(136.), Frequency::fromHz(174.)}, {Frequency::fromHz(400.), Frequency::fromHz(480.)}})}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRefIgnored(nullptr, RadioLimitIssue::Hint)} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString( 1, DR1801UVCodeplug::ChannelBankElement::Limit::channelNameLength(), RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromHz(136.), Frequency::fromHz(174.)}, {Frequency::fromHz(400.), Frequency::fromHz(480.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromHz(136.), Frequency::fromHz(174.)}, {Frequency::fromHz(400.), Frequency::fromHz(480.)}})}, {"power", new RadioLimitEnum { unsigned(Channel::Power::Low), unsigned(Channel::Power::High), }}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, true)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, true)}, {"aprs", new RadioLimitObjRefIgnored()}, {"roaming", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } ) ); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 1, DR1801UVCodeplug::ZoneBankElement::Limit::zoneCount(), new RadioLimitObject { { "name", new RadioLimitString( 1, DR1801UVCodeplug::ZoneElement::Limit::nameLength(), RadioLimitString::Unicode) }, { "A", new RadioLimitRefList( 0, DR1801UVCodeplug::ZoneElement::Limit::memberCount(), Channel::staticMetaObject) }, { "B", new RadioLimitRefList(0, 0, Channel::staticMetaObject) }, { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions } ) ); /* Define limits for scan lists. */ add("scanlists", new RadioLimitList( ScanList::staticMetaObject, 0, DR1801UVCodeplug::ScanListBankElement::Limit::scanListCount(), new RadioLimitObject{ { "name", new RadioLimitString( 1, DR1801UVCodeplug::ScanListElement::Limit::nameLength(), RadioLimitString::Unicode) }, { "primary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "secondary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "revert", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "channels", new RadioLimitRefList( 0, DR1801UVCodeplug::ScanListBankElement::Limit::scanListCount(), Channel::staticMetaObject) } }) ); /* Define limits for positioning systems. */ add("positioning", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint) ) ); /* Ignore roaming zones. */ add("roaming", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint) ) ); } qdmr-0.12.3/lib/dr1801uv_limits.hh000066400000000000000000000005121501654372000165030ustar00rootroot00000000000000#ifndef DR1801UVLIMITS_HH #define DR1801UVLIMITS_HH #include "radiolimits.hh" /** Implements the configuration limits for the BTECH DR-1801UV. * @ingroup dr1801uv */ class DR1801UVLimits : public RadioLimits { Q_OBJECT public: /** Constructor. */ explicit DR1801UVLimits(QObject *parent=nullptr); }; #endif // DR1801UV qdmr-0.12.3/lib/dummyfilereader.cc000066400000000000000000000004151501654372000167760ustar00rootroot00000000000000#include "dummyfilereader.hh" bool DummyFileReader::read(const QString &filename, Codeplug *codeplug, const ErrorStack &err) { Q_UNUSED(codeplug); Q_UNUSED(filename); errMsg(err) << "Cannot read manufacturer codeplug file: not implemented yet."; return false; } qdmr-0.12.3/lib/dummyfilereader.hh000066400000000000000000000006221501654372000170100ustar00rootroot00000000000000#ifndef DUMMYFILEREADER_HH #define DUMMYFILEREADER_HH #include "errorstack.hh" class Codeplug; /** Just a class implementing the filereader "interface" that fails. */ class DummyFileReader { public: /** Does not read the specified file. Just returns an error. */ static bool read(const QString &filename, Codeplug *codeplug, const ErrorStack &err=ErrorStack()); }; #endif // DUMMYFILEREADER_HH qdmr-0.12.3/lib/encryptionextension.cc000066400000000000000000000160351501654372000177540ustar00rootroot00000000000000#include "encryptionextension.hh" #include "logger.hh" /* ********************************************************************************************* * * Implementation of EncryptionKey * ********************************************************************************************* */ EncryptionKey::EncryptionKey(QObject *parent) : ConfigObject(parent) { // pass... } void EncryptionKey::clear() { _key.clear(); } bool EncryptionKey::fromHex(const QString &hex, const ErrorStack &err) { return setKey(QByteArray::fromHex(hex.toLocal8Bit()), err); } QString EncryptionKey::toHex() const { return _key.toHex(); } const QByteArray & EncryptionKey::key() const { return _key; } bool EncryptionKey::setKey(const QByteArray &key, const ErrorStack &err) { if (key.isEmpty()) { errMsg(err) << "Cannot set empty encryption key."; return false; } if (_key == key) return true; _key = key; emit modified(this); return true; } /* ********************************************************************************************* * * Implementation of BasicEncryptionKey * ********************************************************************************************* */ BasicEncryptionKey::BasicEncryptionKey(QObject *parent) : EncryptionKey(parent) { // pass... } ConfigItem * BasicEncryptionKey::clone() const { BasicEncryptionKey *key = new BasicEncryptionKey(); if (! key->copy(*this)) { key->deleteLater(); return nullptr; } return key; } YAML::Node BasicEncryptionKey::serialize(const Context &context, const ErrorStack &err) { YAML::Node node = EncryptionKey::serialize(context, err); if (node.IsNull()) return node; YAML::Node type; type["dmr"] = node; return type; } bool BasicEncryptionKey::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) { if (! node) return false; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot parse basic encryption key: Expected object with one child."; return false; } YAML::Node key = node.begin()->second; return EncryptionKey::parse(key, ctx, err); } /* ********************************************************************************************* * * Implementation of EnhancedEncryptionKey * ********************************************************************************************* */ EnhancedEncryptionKey::EnhancedEncryptionKey(QObject *parent) : EncryptionKey(parent) { // pass... } ConfigItem * EnhancedEncryptionKey::clone() const { EnhancedEncryptionKey *key = new EnhancedEncryptionKey(); if (! key->copy(*this)) { key->deleteLater(); return nullptr; } return key; } bool EnhancedEncryptionKey::fromHex(const QString &hex, const ErrorStack &err) { if (10 != hex.size()) { errMsg(err) << "Cannot set RC4 (enhanced) ecryption key to '" << hex << "': Not a 40bit key."; return false; } return EncryptionKey::fromHex(hex); } bool EnhancedEncryptionKey::setKey(const QByteArray &key, const ErrorStack &err) { if (5 != key.size()) { errMsg(err) << "Cannot set RC4 (enhanced) ecryption key: Not a 40bit key."; return false; } return EncryptionKey::setKey(key, err); } YAML::Node EnhancedEncryptionKey::serialize(const Context &context, const ErrorStack &err) { YAML::Node node = EncryptionKey::serialize(context, err); if (node.IsNull()) return node; YAML::Node type; type["rc4"] = node; return type; } bool EnhancedEncryptionKey::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) { if (! node) return false; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot parse basic encryption key: Expected object with one child."; return false; } YAML::Node key = node.begin()->second; return EncryptionKey::parse(key, ctx, err); } /* ********************************************************************************************* * * Implementation of AESEncryptionKey * ********************************************************************************************* */ AESEncryptionKey::AESEncryptionKey(QObject *parent) : EncryptionKey(parent) { // pass... } ConfigItem * AESEncryptionKey::clone() const { AESEncryptionKey *key = new AESEncryptionKey(); if (! key->copy(*this)) { key->deleteLater(); return nullptr; } return key; } bool AESEncryptionKey::setKey(const QByteArray &key, const ErrorStack &err) { if (16 > key.size()) { errMsg(err) << "Cannot set AES ecryption key to '" << key.toHex() << "': Key smaller than 128bit."; return false; } return EncryptionKey::setKey(key, err); } YAML::Node AESEncryptionKey::serialize(const Context &context, const ErrorStack &err) { YAML::Node node = EncryptionKey::serialize(context, err); if (node.IsNull()) return node; YAML::Node type; type["aes"] = node; return type; } bool AESEncryptionKey::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) { if (! node) return false; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot parse enhanced encryption key: Expected object with one child."; return false; } YAML::Node key = node.begin()->second; return EncryptionKey::parse(key, ctx, err); } /* ********************************************************************************************* * * Implementation of EncryptionKeys * ********************************************************************************************* */ EncryptionKeys::EncryptionKeys(QObject *parent) : ConfigObjectList({BasicEncryptionKey::staticMetaObject, EnhancedEncryptionKey::staticMetaObject, AESEncryptionKey::staticMetaObject}, parent) { // pass... } int EncryptionKeys::add(ConfigObject *obj, int row, bool unique) { if ((nullptr == obj) || (! obj->is())) { logError() << "Cannot add nullptr or non-encryption key objects to key list."; return -1; } return ConfigObjectList::add(obj, row, unique); } EncryptionKey * EncryptionKeys::key(int index) const { if (index >= count()) return nullptr; return get(index)->as(); } ConfigItem * EncryptionKeys::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) if (! node) return nullptr; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create encryption key: Expected object with one child."; return nullptr; } QString type = QString::fromStdString(node.begin()->first.as()); if (("basic" == type) || ("dmr" == type)) { return new BasicEncryptionKey(); } else if ("rc4" == type) { return new EnhancedEncryptionKey(); } else if ("aes" == type) { return new AESEncryptionKey(); } errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create encryption key: Unknown type '" << type << "'."; return nullptr; } qdmr-0.12.3/lib/encryptionextension.hh000066400000000000000000000103141501654372000177600ustar00rootroot00000000000000#ifndef ENCRYPTIONEXTENSION_HH #define ENCRYPTIONEXTENSION_HH #include "configobject.hh" /** Base class of all encryption keys. * @ingroup conf */ class EncryptionKey: public ConfigObject { Q_OBJECT Q_CLASSINFO("IdPrefix", "key") /** The key representation as a hex string. */ Q_PROPERTY(QString key READ toHex WRITE fromHex) protected: /** Hidden constructor. */ explicit EncryptionKey(QObject *parent=nullptr); public: void clear(); /** Creates a key from the given hex-string. */ virtual bool fromHex(const QString &hex, const ErrorStack &err=ErrorStack()); /** Converts a key to a hex string. */ virtual QString toHex() const; /** Returns the binary key. */ const QByteArray &key() const; /** Sets the binary key. */ virtual bool setKey(const QByteArray &key, const ErrorStack &err=ErrorStack()); protected: /** Holds the key data. * The size depends on the key type. */ QByteArray _key; }; /** Represents a DMR (basic) encryption key. * * This is a variable sized key used for the DMR basic encryption method. * * @ingroup conf */ class BasicEncryptionKey: public EncryptionKey { Q_OBJECT Q_CLASSINFO("description", "A basic DMR encryption key.") Q_CLASSINFO("longDescription", "This is a variable sized pre-shared key that can be used to encrypt/decrypt traffic " "on DMR channels. Encryption is forbidden in HAM radio context!") public: /** Empty constructor. */ Q_INVOKABLE explicit BasicEncryptionKey(QObject *parent=nullptr); ConfigItem *clone() const; public: YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack()); bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack()); }; /** Represents an (enhanced) RC4 encryption key. * * This is a 40bit key used for the DMR enhanced encryption method. * * @ingroup conf */ class EnhancedEncryptionKey: public EncryptionKey { Q_OBJECT Q_CLASSINFO("description", "An enhanced DMR encryption key.") Q_CLASSINFO("longDescription", "This is a 40bit pre-shared RC4 key, that can be used to encrypt/decrypt traffic on " "DMR channels. Encryption is forbidden in HAM radio context!") public: /** Empty constructor. */ Q_INVOKABLE explicit EnhancedEncryptionKey(QObject *parent=nullptr); ConfigItem *clone() const; bool fromHex(const QString &hex, const ErrorStack &err=ErrorStack()); bool setKey(const QByteArray &key, const ErrorStack &err); public: YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack()); bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack()); }; /** Represents an AES (enhanced) encryption key. * * This is a 128bit key used for the DMR enhanced encryption method. * * @ingroup conf */ class AESEncryptionKey: public EncryptionKey { Q_OBJECT Q_CLASSINFO("description", "An AES (advanced) DMR encryption key.") Q_CLASSINFO("longDescription", "This is a variable sized (usually 128-256bit) pre-shared key that can be used to " "encrypt/decrypt traffic on DMR channels. Encryption is forbidden in HAM radio " "context!") public: /** Empty constructor. */ Q_INVOKABLE explicit AESEncryptionKey(QObject *parent=nullptr); ConfigItem *clone() const; bool setKey(const QByteArray &key, const ErrorStack &err); public: YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack()); bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack()); }; Q_DECLARE_OPAQUE_POINTER(AESEncryptionKey*) /** The list of encryption keys. * * This list holds all encryption keys defined within the codeplug. * * @warning Please note that this is a commercial feature and forbidden for HAM radio use. * * @ingroup conf */ class EncryptionKeys: public ConfigObjectList { Q_OBJECT public: /** Empty constructor. */ explicit EncryptionKeys(QObject *parent=nullptr); int add(ConfigObject *obj, int row=-1, bool unique=true); /** Returns the key the given index. */ EncryptionKey *key(int index) const; ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); }; #endif // ENCRYPTIONEXTENSION_HH qdmr-0.12.3/lib/errorstack.cc000066400000000000000000000076731501654372000160140ustar00rootroot00000000000000#include "errorstack.hh" #include #include /* ********************************************************************************************* * * Implementation of ErrorStack::Message * ********************************************************************************************* */ ErrorStack::Message::Message() : _file(), _line(0), _message() { // pass... } ErrorStack::Message::Message(const QString &file, unsigned line, const QString &message) : _file(file), _line(line), _message(message) { // pass... } const QString & ErrorStack::Message::file() const { return _file; } unsigned ErrorStack::Message::line() const { return _line; } const QString & ErrorStack::Message::message() const { return _message; } QString ErrorStack::Message::format() const { return QString("In %1:%2: %3").arg(QFileInfo(file()).fileName()).arg(line()).arg(message()); } /* ********************************************************************************************* * * Implementation of ErrorStack::MessageStream * ********************************************************************************************* */ ErrorStack::MessageStream::MessageStream(const ErrorStack &stack, const QString &file, unsigned line) : QTextStream(&_message), _stack(stack), _file(file), _line(line), _message() { // pass... } ErrorStack::MessageStream::~MessageStream() { _stack.push(Message(_file, _line, _message)); LogMessage(LogMessage::ERROR, _file, _line) << _message; } /* ********************************************************************************************* * * Implementation of ErrorStack::Stack * ********************************************************************************************* */ ErrorStack::Stack::Stack() noexcept : _refcount(1), _errorMessageStack() { // pass... } void ErrorStack::Stack::push(const Message &msg) { _errorMessageStack.push_front(msg); } void ErrorStack::Stack::push(const Stack &other) { if (other.isEmpty()) return; for (int i=(other.count()-1); i>=0; i--) { push(other.message(i)); } } bool ErrorStack::Stack::isEmpty() const { return 0 == count(); } unsigned ErrorStack::Stack::count() const { return _errorMessageStack.count(); } const ErrorStack::Message & ErrorStack::Stack::message(unsigned i) const { return _errorMessageStack.at(i); } QString ErrorStack::Stack::format(const QString &indent) const { QString res; if (isEmpty()) return res; res += indent + message(0).format(); for (unsigned i=1; iref()) { // pass... } ErrorStack::~ErrorStack() { _stack->unref(); _stack = nullptr; } ErrorStack & ErrorStack::operator =(const ErrorStack &other) { _stack->unref(); _stack = other._stack->ref(); return *this; } bool ErrorStack::isEmpty() const { return _stack->isEmpty(); } unsigned ErrorStack::count() const { return _stack->count(); } const ErrorStack::Message & ErrorStack::message(unsigned i) const { return _stack->message(i); } void ErrorStack::push(const Message &msg) const { _stack->push(msg); } void ErrorStack::take(const ErrorStack &other) const { _stack->push(*other._stack); other._stack->clear(); } QString ErrorStack::format(const QString &indent) const { return _stack->format(indent); } qdmr-0.12.3/lib/errorstack.hh000066400000000000000000000102461501654372000160140ustar00rootroot00000000000000#ifndef ERRORSTACK_HH #define ERRORSTACK_HH #include /** Implements a stack of error messages to provide a pretty formatted error traceback. * * This class is intended to be used like: * @code * class MyClass: public ErrorStack * { * * // [...] * * bool someMethod(const ErrorStack &err=ErrorStack()) { * // [...] * if (someError) { * errMsg() << "Some error happened!"; * return false; * } * // [...] * } * * // [...] * } * @endcode * * The error message can then be obtained using the public methods. E.g., * @code * // [...] * MyClass instance; * ErrorStack err; * * if (! instance.someMethod(err)) { * QString msg = err.formatErrorMessages(); * // [] * } * @endcode * @ingroup log */ class ErrorStack { public: class Stack; /** Represents a single error message. That is, a tuple of file, line and message. */ class Message { public: /** Empty constructor. */ Message(); /** Constructor from file, line and message. */ Message(const QString &file, unsigned line, const QString &message); /** Returns the file name. */ const QString &file() const; /** Returns the line within the file. */ unsigned line() const; /** Returns the error message. */ const QString &message() const; /** Formats the error messaege. */ QString format() const; protected: /** Holds the file path. */ QString _file; /** Holds the line. */ unsigned _line; /** Holds the error message. */ QString _message; }; /** A helper class to assemble error messages as streams. Use the @c errorMessage macro. */ class MessageStream: public QTextStream { public: /** Constructor. */ MessageStream(const ErrorStack &stack, const QString &file, unsigned line); /** Destructor, puts the message on the stack. */ virtual ~MessageStream(); protected: /** Holds a weak reference to the error stack to put the message on. */ const ErrorStack &_stack; /** The file path. */ QString _file; /** The line number. */ unsigned _line; /** The message buffer. */ QString _message; }; /** The actual error message stack. */ class Stack { public: /** Empty constructor. */ Stack() noexcept; public: /** Returns @c true if there are any error messages. */ bool isEmpty() const; /** Returns the number of error messages. */ unsigned count() const; /** Returns a specific error message. */ const Message &message(unsigned i) const; /** Returns a formatted string of error messages. */ QString format(const QString &indent=" ") const; /** Adds an error message to the stack. */ void push(const Message &msg); /** Adds the error messages from another stack. */ void push(const Stack &other); /** Clears the error stack. */ void clear(); /** Returns a new reference to the stack. */ Stack *ref(); /** Dereferences a stack, this decreases the ref count. When 0 is reached, the stack is * destroyed. */ void unref(); private: /** Reference counter. */ unsigned _refcount; /** Holds the stack of error messages. */ QList _errorMessageStack; }; public: /** Default constructor. */ ErrorStack() noexcept; /** Copy constructor. */ ErrorStack(const ErrorStack &other); /** Destructor. */ ~ErrorStack(); /** Copy assignment. */ ErrorStack &operator= (const ErrorStack &other); /** Returns @c true, if the stack is empty. */ bool isEmpty() const; /** Returns the number of elements on the stack. */ unsigned count() const; /** Returns the i-th message from the stack. */ const Message &message(unsigned i) const; /** Pushes a message on the stack. */ void push(const Message &msg) const; /** Takes all messages from the other stack. */ void take(const ErrorStack &other) const; /** Returns a formatted string of error messages. */ QString format(const QString &indent=" ") const; protected: /** A reference to the actual message stack. */ Stack *_stack; }; /** Utility macro to assemble a message stream. */ #define errMsg(stack) (ErrorStack::MessageStream(stack, __FILE__, __LINE__)) #endif // ERRORSTACK_HH qdmr-0.12.3/lib/frequency.cc000066400000000000000000000053021501654372000156210ustar00rootroot00000000000000#include "frequency.hh" #include "logger.hh" #include Frequency::Frequency(unsigned long long Hz) : _frequency(Hz) { // pass... } Frequency::Frequency() : _frequency(0) { // pass... } Frequency::Frequency(const Frequency &other) : _frequency(other._frequency) { // pass... } Frequency & Frequency::operator =(const Frequency &other) { _frequency = other._frequency; return *this; } QString Frequency::format(Format f) const { switch (f) { case Format::Automatic: if (10000ULL > _frequency) return format(Format::Hz); else if (10000000ULL > _frequency) return format(Format::kHz); else if (10000000000ULL > _frequency) return format(Format::MHz); return format(Format::GHz); case Format::Hz: return QString("%1 Hz").arg(inHz()); case Format::kHz: return QString("%1 kHz").arg(inkHz(), 0, 'g', 6); case Format::MHz: return QString("%1 MHz").arg(inMHz(), 0, 'g', 9); case Format::GHz: return QString("%1 GHz").arg(inGHz(), 0, 'g', 12); } return ""; } bool Frequency::parse(const QString &value) { QRegularExpression re(R"(\s*([0-9]+)(?:\.([0-9]*)|)\s*([kMG]?Hz|)\s*)"); QRegularExpressionMatch match = re.match(value); if (! match.isValid()) return false; bool hasUnit = match.capturedLength(3); QString unit = match.captured(3); QString decimals = match.captured(2); QString leading = match.captured(1); _frequency = leading.toUInt(); if ("Hz" == unit) return true; if ("kHz" == unit) { _frequency *= 1000ULL; unsigned long long factor = 100ULL; for (int i=0; i3) && (decimals[3].digitValue()>=5)) _frequency+=1; } else if (("MHz"==unit) || (!hasUnit)) { _frequency *= 1000000ULL; unsigned long long factor = 100000ULL; for (int i=0; i6) && (decimals[6].digitValue()>=5)) _frequency+=1; } else if ("GHz"==unit) { _frequency *= 1000000000ULL; unsigned long long factor = 100000000; for (int i=0; i9) && (decimals[9].digitValue()>=5)) _frequency+=1; } return true; } Frequency Frequency::fromString(const QString &freq) { Frequency f; if (! f.parse(freq)) { logWarn() << "Cannot parse frequency '" << freq << "'."; } return f; } qdmr-0.12.3/lib/frequency.hh000066400000000000000000000056611501654372000156430ustar00rootroot00000000000000#ifndef FREQUENCY_HH #define FREQUENCY_HH #include #include #include /** Helper type to encode frequencies without any rounding error. * @ingroup utils */ struct Frequency { public: /** Possible formatting hints. */ enum class Format { Automatic, Hz, kHz, MHz, GHz }; protected: /** Hidden constructor from frequency in Hz. */ Frequency(unsigned long long hz); public: /** Default constructor. */ Frequency(); /** Copy constructor. */ Frequency(const Frequency &other); /** Assignment. */ Frequency &operator = (const Frequency &other); inline bool operator< (const Frequency &other) const { ///< Comparison. return _frequency < other._frequency; } inline bool operator <=(const Frequency &other) const { ///< Comparison. return _frequency <= other._frequency; } inline bool operator== (const Frequency &other) const { ///< Comparison. return _frequency == other._frequency; } inline bool operator != (const Frequency &other) const { ///< Comparison. return _frequency != other._frequency; } inline bool operator> (const Frequency &other) const { ///< Comparison. return _frequency > other._frequency; } inline bool operator >=(const Frequency &other) const { ///< Comparison. return _frequency >= other._frequency; } /** Format the frequency. */ QString format(Format f=Format::Automatic) const; /** Parses a frequency. */ bool parse(const QString &value); /** Pareses a frequency. */ static Frequency fromString(const QString &freq); inline unsigned long long inHz() const { return _frequency; } ///< Unit conversion. inline double inkHz() const { return double(_frequency)/1e3; } ///< Unit conversion. inline double inMHz() const { return double(_frequency)/1e6; } ///< Unit conversion. inline double inGHz() const { return double(_frequency)/1e9; } ///< Unit conversion. static inline Frequency fromHz(unsigned long long Hz) { return Frequency(Hz); } ///< Unit conversion. static inline Frequency fromkHz(double kHz) { return Frequency(kHz*1e3); } ///< Unit conversion. static inline Frequency fromMHz(double MHz) { return Frequency(MHz*1e6); } ///< Unit conversion. static inline Frequency fromGHz(double GHz) { return Frequency(GHz*1e6); } ///< Unit conversion. protected: /** The actual frequency in Hz. */ unsigned long long _frequency; }; Q_DECLARE_METATYPE(Frequency) namespace YAML { /** Implements the conversion to and from YAML::Node. */ template<> struct convert { /** Serializes the frequency. */ static Node encode(const Frequency& rhs) { return Node(rhs.format().toStdString()); } /** Parses the frequency. */ static bool decode(const Node& node, Frequency& rhs) { if (! node.IsScalar()) return false; return rhs.parse(QString::fromStdString(node.as())); } }; } #endif // FREQUENCY_HH qdmr-0.12.3/lib/gd73.cc000066400000000000000000000135171501654372000143730ustar00rootroot00000000000000#include "gd73.hh" #include "gd73_limits.hh" #include "logger.hh" #include "config.hh" #define BSIZE 0x35 RadioLimits * GD73::_limits = nullptr; GD73::GD73(GD73Interface *device, QObject *parent) : Radio(parent), _name("Radioddity GD-73"), _dev(device), _codeplugFlags(), _config(nullptr), _codeplug() { if (_dev) _dev->setParent(this); } const QString & GD73::name() const { return _name; } const RadioLimits & GD73::limits() const { if (nullptr == _limits) _limits = new GD73Limits(); return *_limits; } const Codeplug & GD73::codeplug() const { return _codeplug; } Codeplug & GD73::codeplug() { return _codeplug; } RadioInfo GD73::defaultRadioInfo() { return RadioInfo( RadioInfo::GD73, "gd73", "GD-73", "Radioddity", GD73Interface::interfaceInfo()); } bool GD73::startDownload(bool blocking, const ErrorStack &err) { if (StatusIdle != _task) return false; _task = StatusDownload; _errorStack = err; if (blocking) { run(); return (StatusIdle == _task); } start(); return true; } bool GD73::startUpload(Config *config, bool blocking, const Codeplug::Flags &flags, const ErrorStack &err) { if (StatusIdle != _task) return false; if (! (_config = config)) return false; _task = StatusUpload; _codeplugFlags = flags; if (blocking) { this->run(); return (StatusIdle == _task); } _errorStack = err; this->start(); return true; } bool GD73::startUploadCallsignDB(UserDatabase *db, bool blocking, const CallsignDB::Selection &selection, const ErrorStack &err) { Q_UNUSED(db); Q_UNUSED(blocking); Q_UNUSED(selection); errMsg(err) << "Radio does not support a callsign DB."; return false; } bool GD73::startUploadSatelliteConfig(SatelliteDatabase *db, bool blocking, const ErrorStack &err) { Q_UNUSED(db); Q_UNUSED(blocking); errMsg(err) << "Satellite config upload is not implemented yet."; return false; } void GD73::run() { if (StatusDownload == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { emit downloadError(this); return; } if (! download()) { _dev->read_finish(); _dev->reboot(); _dev->close(); _task = StatusError; emit downloadError(this); return; } _task = StatusIdle; _dev->reboot(); _dev->close(); emit downloadFinished(this, &codeplug()); _config = nullptr; } else if (StatusUpload == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { emit uploadError(this); return; } if (! upload()) { _dev->write_finish(); _dev->reboot(); _dev->close(); _task = StatusError; emit uploadError(this); return; } _dev->write_finish(); _dev->reboot(); _dev->close(); _task = StatusIdle; emit uploadComplete(this); } else if (StatusUploadCallsigns == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { emit uploadError(this); return; } if(! uploadCallsigns()) { _dev->reboot(); _dev->close(); _task = StatusError; emit uploadError(this); return; } _task = StatusIdle; _dev->reboot(); _dev->close(); emit uploadComplete(this); } } bool GD73::download() { emit downloadStarted(); unsigned btot = 0; for (int n=0; nread_start(0,0,_errorStack)) return false; unsigned bcount = 0; for (int n=0; nread(0, (b0+i)*BSIZE, codeplug().data((b0+i)*BSIZE), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot download codeplug."; return false; } emit downloadProgress(float(bcount*100)/btot); } } _dev->read_finish(_errorStack); return true; } bool GD73::upload() { emit uploadStarted(); unsigned btot = 0; for (int n=0; nread_start(0,0,_errorStack)) return false; // If codeplug gets updated, download codeplug from device first: for (int n=0; nread(0, addr, codeplug().data(addr), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot upload codeplug."; return false; } emit uploadProgress(float(bcount*50)/btot); } } _dev->read_finish(_errorStack); } // Encode config into codeplug if (! codeplug().encode(_config, _codeplugFlags, _errorStack)) { errMsg(_errorStack) << "Codeplug upload failed."; return false; } if (! _dev->write_start(0, 0, _errorStack)) return false; // then, upload modified codeplug bcount = 0; for (int n=0; nwrite(0, addr, codeplug().data(addr), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot upload codeplug."; return false; } emit uploadProgress(50+float(bcount*50)/btot); } } _dev->write_finish(_errorStack); return true; } bool GD73::uploadCallsigns() { return false; } qdmr-0.12.3/lib/gd73.hh000066400000000000000000000046521501654372000144050ustar00rootroot00000000000000/** @defgroup gd73 Radioddity GD-73 * Device specific classes for Radioddity GD-73A and GD-73E. * * \image html gd73.webp "GD-73" width=200px * \image latex gd73.webp "GD-73" width=200px * * @ingroup radioddity */ #ifndef GD73_HH #define GD73_HH #include "radio.hh" #include "gd73_interface.hh" #include "gd73_codeplug.hh" /** Implements an USB interface to the Radioddity GD-73 UHF 2W DMR (Tier I&II) radio. * * @ingroup gd73 */ class GD73 : public Radio { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit GD73(GD73Interface *device=nullptr, QObject *parent=nullptr); const QString &name() const; const RadioLimits &limits() const; const Codeplug &codeplug() const; Codeplug &codeplug(); /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); public slots: /** Starts the download of the codeplug and derives the generic configuration from it. */ bool startDownload(bool blocking=false, const ErrorStack &err=ErrorStack()); /** Derives the device-specific codeplug from the generic configuration and uploads that * codeplug to the radio. */ bool startUpload(Config *config, bool blocking=false, const Codeplug::Flags &flags = Codeplug::Flags(), const ErrorStack &err=ErrorStack()); /** Encodes the given user-database and uploades it to the device. */ bool startUploadCallsignDB(UserDatabase *db, bool blocking=false, const CallsignDB::Selection &selection=CallsignDB::Selection(), const ErrorStack &err=ErrorStack()); bool startUploadSatelliteConfig(SatelliteDatabase *db, bool blocking, const ErrorStack &err); protected: /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */ void run(); private: virtual bool download(); virtual bool upload(); virtual bool uploadCallsigns(); protected: /** The device identifier. */ QString _name; /** The interface to the radio. */ GD73Interface *_dev; /** Holds the flags to control assembly and upload of code-plugs. */ Codeplug::Flags _codeplugFlags; /** The generic configuration. */ Config *_config; /** The codeplug. */ GD73Codeplug _codeplug; private: /** Holds the signleton instance of the radio limits for this radio. */ static RadioLimits *_limits; }; #endif // GD77_HH qdmr-0.12.3/lib/gd73_codeplug.cc000066400000000000000000002504311501654372000162530ustar00rootroot00000000000000#include "gd73_codeplug.hh" #include "config.hh" #include "intermediaterepresentation.hh" #include "logger.hh" QVector _ctcss_codes = { SelectiveCall(62.5), SelectiveCall(67.0), SelectiveCall(69.3), SelectiveCall(71.9), SelectiveCall(74.4), SelectiveCall(77.0), SelectiveCall(79.7), SelectiveCall(82.5), SelectiveCall(85.4), SelectiveCall(88.5), SelectiveCall(91.5), SelectiveCall(94.8), SelectiveCall(97.4), SelectiveCall(100.0), SelectiveCall(103.5), SelectiveCall(107.2), SelectiveCall(110.9), SelectiveCall(114.8), SelectiveCall(118.8), SelectiveCall(123.0), SelectiveCall(127.3), SelectiveCall(131.8), SelectiveCall(136.5), SelectiveCall(141.3), SelectiveCall(146.2), SelectiveCall(151.4), SelectiveCall(156.7), SelectiveCall(159.8), SelectiveCall(162.2), SelectiveCall(165.5), SelectiveCall(167.9), SelectiveCall(171.3), SelectiveCall(173.8), SelectiveCall(177.3), SelectiveCall(179.9), SelectiveCall(183.5), SelectiveCall(186.2), SelectiveCall(189.9), SelectiveCall(192.8), SelectiveCall(196.6), SelectiveCall(199.5), SelectiveCall(203.5), SelectiveCall(206.5), SelectiveCall(210.7), SelectiveCall(218.1), SelectiveCall(225.7), SelectiveCall(229.1), SelectiveCall(233.6), SelectiveCall(241.8), SelectiveCall(250.3), SelectiveCall(254.1) }; QVector _dcs_codes = { 23, 25, 26, 31, 32, 36, 43, 47, 51, 53, 54, 65, 71, 72, 73, 74, 114, 115, 116, 122, 125, 131, 132, 134, 143, 145, 152, 155, 156, 162, 165, 172, 174, 205, 212, 223, 225, 226, 243, 244, 245, 246, 251, 252, 255, 261, 263, 265, 266, 271, 274, 306, 311, 315, 325, 331, 332, 343, 346, 351, 356, 364, 365, 371, 411, 412, 413, 423, 431, 432, 445, 446, 452, 454, 455, 462, 464, 465, 466, 503, 506, 516, 523, 526, 532, 546, 565, 606, 612, 624, 627, 631, 632, 645, 654, 662, 703, 712, 723, 731, 732, 734, 743, 754 }; /* ********************************************************************************************* * * Implementation of GD73Codeplug::InformationElement * ********************************************************************************************* */ GD73Codeplug::InformationElement::InformationElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::InformationElement::InformationElement(uint8_t *ptr) : Element(ptr, InformationElement::size()) { // pass... } FrequencyRange GD73Codeplug::InformationElement::frequencyRange() const { switch (getUInt8(Offset::frequencyRange())) { case 0x00: return FrequencyRange {Frequency::fromMHz(406.1), Frequency::fromMHz(470.0)}; break; case 0x01: return FrequencyRange {Frequency::fromMHz(446.0), Frequency::fromMHz(446.995)}; break; case 0x02: return FrequencyRange {Frequency::fromMHz(400.0), Frequency::fromMHz(470.0)}; break; } return FrequencyRange(); } void GD73Codeplug::InformationElement::setFrequencyRange(const FrequencyRange &range) { if ((Frequency::fromMHz(446.0)<=range.lower) && (range.upper <=Frequency::fromMHz(446.995))) setUInt8(Offset::frequencyRange(), 0x01); else if ((range.lower>=Frequency::fromMHz(406.1)) && (range.upper <=Frequency::fromMHz(470.0))) setUInt8(Offset::frequencyRange(), 0x00); else setUInt8(Offset::frequencyRange(), 0x02); } QDateTime GD73Codeplug::InformationElement::timestamp() const { int year = ((int)getUInt8(Offset::dateCentury()))*100 + getUInt8(Offset::dateYear()); return QDateTime(QDate(year, getUInt8(Offset::dateMonth()), getUInt8(Offset::dateDay())), QTime(getUInt8(Offset::dateHour()), getUInt8(Offset::dateMinute()), getUInt8(Offset::dateSecond()))); } void GD73Codeplug::InformationElement::setTimestamp(const QDateTime ×tamp) { setUInt8(Offset::dateCentury(), timestamp.date().year()/100); setUInt8(Offset::dateYear(), timestamp.date().year()%100); setUInt8(Offset::dateMonth(), timestamp.date().month()); setUInt8(Offset::dateDay(), timestamp.date().day()); setUInt8(Offset::dateHour(), timestamp.time().hour()); setUInt8(Offset::dateMinute(), timestamp.time().minute()); setUInt8(Offset::dateSecond(), timestamp.time().second()); } QString GD73Codeplug::InformationElement::serial() const { return readASCII(Offset::serial(), Limit::serial(), 0x00); } QString GD73Codeplug::InformationElement::modelName() const { return readASCII(Offset::modelName(), Limit::modelName(), 0x00); } QString GD73Codeplug::InformationElement::deviceID() const { return readASCII(Offset::deviceID(), Limit::deviceID(), 0x00); } QString GD73Codeplug::InformationElement::modelNumber() const { return readASCII(Offset::modelNumber(), Limit::modelNumber(), 0x00); } QString GD73Codeplug::InformationElement::softwareVersion() const { return readASCII(Offset::softwareVersion(), Limit::softwareVersion(), 0x00); } /* ********************************************************************************************* * * Implementation of GD73Codeplug::SettingsElement::KeyFunction * ********************************************************************************************* */ uint8_t GD73Codeplug::SettingsElement::KeyFunction::encode(RadioddityButtonSettingsExtension::Function func) { switch (func) { case RadioddityButtonSettingsExtension::Function::None: return None; case RadioddityButtonSettingsExtension::Function::RadioEnable: return RadioEnable; case RadioddityButtonSettingsExtension::Function::RadioCheck: return RadioCheck; case RadioddityButtonSettingsExtension::Function::RadioDisable: return RadioDisable; case RadioddityButtonSettingsExtension::Function::PowerLevel: return PowerLevel; case RadioddityButtonSettingsExtension::Function::ToggleMonitor: return Monitor; case RadioddityButtonSettingsExtension::Function::EmergencyOn: return EmergencyOn; case RadioddityButtonSettingsExtension::Function::EmergencyOff: return EmergencyOff; case RadioddityButtonSettingsExtension::Function::ZoneSelect: return ZoneSwitch; case RadioddityButtonSettingsExtension::Function::ToggleScan: return ToggleScan; case RadioddityButtonSettingsExtension::Function::ToggleVox: return ToggleVOX; case RadioddityButtonSettingsExtension::Function::OneTouch1: return OneTouch1; case RadioddityButtonSettingsExtension::Function::OneTouch2: return OneTouch2; case RadioddityButtonSettingsExtension::Function::OneTouch3: return OneTouch3; case RadioddityButtonSettingsExtension::Function::OneTouch4: return OneTouch4; case RadioddityButtonSettingsExtension::Function::OneTouch5: return OneTouch5; case RadioddityButtonSettingsExtension::Function::ToggleTalkaround: return ToggleTalkaround; case RadioddityButtonSettingsExtension::Function::ToggleLoneWorker: return LoneWorker; case RadioddityButtonSettingsExtension::Function::TBST: return TBST; case RadioddityButtonSettingsExtension::Function::CallSwell: return CallSwell; default: break; } return None; } RadioddityButtonSettingsExtension::Function GD73Codeplug::SettingsElement::KeyFunction::decode(uint8_t code) { switch((Code) code) { case None: return RadioddityButtonSettingsExtension::Function::None; case RadioEnable: return RadioddityButtonSettingsExtension::Function::RadioEnable; case RadioCheck: return RadioddityButtonSettingsExtension::Function::RadioCheck; case RadioDisable: return RadioddityButtonSettingsExtension::Function::RadioDisable; case PowerLevel: return RadioddityButtonSettingsExtension::Function::PowerLevel; case Monitor: return RadioddityButtonSettingsExtension::Function::ToggleMonitor; case EmergencyOn: return RadioddityButtonSettingsExtension::Function::EmergencyOn; case EmergencyOff: return RadioddityButtonSettingsExtension::Function::EmergencyOff; case ZoneSwitch: return RadioddityButtonSettingsExtension::Function::ZoneSelect; case ToggleScan: return RadioddityButtonSettingsExtension::Function::ToggleScan; case ToggleVOX: return RadioddityButtonSettingsExtension::Function::ToggleVox; case OneTouch1: return RadioddityButtonSettingsExtension::Function::OneTouch1; case OneTouch2: return RadioddityButtonSettingsExtension::Function::OneTouch2; case OneTouch3: return RadioddityButtonSettingsExtension::Function::OneTouch3; case OneTouch4: return RadioddityButtonSettingsExtension::Function::OneTouch4; case OneTouch5: return RadioddityButtonSettingsExtension::Function::OneTouch5; case ToggleTalkaround: return RadioddityButtonSettingsExtension::Function::ToggleTalkaround; case LoneWorker: return RadioddityButtonSettingsExtension::Function::ToggleLoneWorker; case TBST: return RadioddityButtonSettingsExtension::Function::TBST; case CallSwell: return RadioddityButtonSettingsExtension::Function::CallSwell; default: break; } return RadioddityButtonSettingsExtension::Function::None; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::SettingsElement * ********************************************************************************************* */ GD73Codeplug::SettingsElement::SettingsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::SettingsElement::SettingsElement(uint8_t *ptr) : Element(ptr, SettingsElement::size()) { // pass... } QString GD73Codeplug::SettingsElement::name() const { return readUnicode(Offset::name(), Limit::name(), 0x0000); } void GD73Codeplug::SettingsElement::setName(const QString &name) { writeUnicode(Offset::name(), name, Limit::name(), 0x0000); } unsigned int GD73Codeplug::SettingsElement::dmrID() const { return getUInt32_le(Offset::dmrId()); } void GD73Codeplug::SettingsElement::setDMRID(unsigned int id) { setUInt32_le(Offset::dmrId(), id); } GD73Codeplug::SettingsElement::Language GD73Codeplug::SettingsElement::language() const { return (Language)getUInt8(Offset::language()); } void GD73Codeplug::SettingsElement::setLanguage(Language lang) { setUInt8(Offset::language(), (unsigned int)lang); } unsigned int GD73Codeplug::SettingsElement::vox() const { return getUInt8(Offset::voxLevel())*10/4; } void GD73Codeplug::SettingsElement::setVOX(unsigned int level) { setUInt8(Offset::voxLevel(), level*4/10); } unsigned int GD73Codeplug::SettingsElement::squelch() const { return getUInt8(Offset::squelchLevel())*10/9; } void GD73Codeplug::SettingsElement::setSquelch(unsigned int level) { setUInt8(Offset::squelchLevel(), level*9/10); } bool GD73Codeplug::SettingsElement::totIsSet() const { return 0 != getUInt8(Offset::tot()); } Interval GD73Codeplug::SettingsElement::tot() const { unsigned int n = getUInt8(Offset::tot()); if (0 == n) return Interval::fromSeconds(0); return Interval::fromSeconds(n*10+20); } void GD73Codeplug::SettingsElement::setTOT(const Interval &interval) { Interval intv = Limit::tot().map(interval); if (intv.seconds()<20) setUInt8(Offset::tot(), 0); setUInt8(Offset::tot(), (intv.seconds()-20)/10); } void GD73Codeplug::SettingsElement::clearTOT() { setUInt8(Offset::tot(), 0); } bool GD73Codeplug::SettingsElement::txInterruptedEnabled() const { return 0 != getUInt8(Offset::txInterrupt()); } void GD73Codeplug::SettingsElement::enableTXInterrupt(bool enable) { setUInt8(Offset::txInterrupt(), enable ? 0x01 : 0x00); } bool GD73Codeplug::SettingsElement::powerSaveEnabled() const { return 0x00 != getUInt8(Offset::powerSave()); } void GD73Codeplug::SettingsElement::enablePowerSave(bool enable) { setUInt8(Offset::powerSave(), enable ? 0x01 : 0x00); } Interval GD73Codeplug::SettingsElement::powerSaveTimeout() const { return Interval::fromSeconds(getUInt8(Offset::powerSaveTimeout())); } void GD73Codeplug::SettingsElement::setPowerSaveTimeout(const Interval &interval) { Interval intv = Limit::powerSaveTimeout().map(interval); setUInt8(Offset::powerSaveTimeout(), intv.seconds()); } bool GD73Codeplug::SettingsElement::readLockEnabled() const { return 0x00 != getUInt8(Offset::readLockEnable()); } void GD73Codeplug::SettingsElement::enableReadLock(bool enable) { setUInt8(Offset::readLockEnable(), enable ? 0x01 : 0x00); } QString GD73Codeplug::SettingsElement::readLockPin() const { return readASCII(Offset::readLockPin(), Limit::pin(), 0x00); } void GD73Codeplug::SettingsElement::setReadLockPin(const QString &pin) { writeASCII(Offset::readLockPin(), pin, Limit::pin(), 0x00); } bool GD73Codeplug::SettingsElement::writeLockEnabled() const { return 0x00 != getUInt8(Offset::writeLockEnable()); } void GD73Codeplug::SettingsElement::enableWriteLock(bool enable) { setUInt8(Offset::writeLockEnable(), enable ? 0x01 : 0x00); } QString GD73Codeplug::SettingsElement::writeLockPin() const { return readASCII(Offset::writeLockPin(), Limit::pin(), 0x00); } void GD73Codeplug::SettingsElement::setWriteLockPin(const QString &pin) { writeASCII(Offset::writeLockPin(), pin, Limit::pin(), 0x00); } GD73Codeplug::SettingsElement::ChannelDisplayMode GD73Codeplug::SettingsElement::channelDisplayMode() const { return (ChannelDisplayMode)getUInt8(Offset::channelDisplayMode()); } void GD73Codeplug::SettingsElement::setChannelDisplayMode(ChannelDisplayMode mode) { setUInt8(Offset::channelDisplayMode(), (unsigned int)mode); } unsigned int GD73Codeplug::SettingsElement::dmrMicGain() const { return getUInt8(Offset::dmrMicGain())*9/5+1; } void GD73Codeplug::SettingsElement::setDMRMicGain(unsigned int gain) { setUInt8(Offset::dmrMicGain(), (gain-1)*5/9); } unsigned int GD73Codeplug::SettingsElement::fmMicGain() const { return getUInt8(Offset::fmMicGain())*9/5+1; } void GD73Codeplug::SettingsElement::setFMMicGain(unsigned int gain) { setUInt8(Offset::fmMicGain(), (gain-1)*5/9); } Interval GD73Codeplug::SettingsElement::loneWorkerResponseTimeout() const { return Interval::fromMinutes(getUInt16_le(Offset::loneWorkerResponseTimeout())); } void GD73Codeplug::SettingsElement::setLoneWorkerResponseTimeout(const Interval &interval) { Interval intv = Limit::loneWorkerResponse().map(interval); setUInt16_le(Offset::loneWorkerResponseTimeout(), intv.minutes()); } Interval GD73Codeplug::SettingsElement::loneWorkerRemindPeriod() const { return Interval::fromSeconds(getUInt16_le(Offset::loneWorkerReminderPeriod())); } void GD73Codeplug::SettingsElement::setLoneWorkerRemindPeriod(const Interval &interval) { Interval intv = Limit::loneWorkerRemindPeriod().map(interval); setUInt16_le(Offset::loneWorkerReminderPeriod(), intv.seconds()); } GD73Codeplug::SettingsElement::BootDisplayMode GD73Codeplug::SettingsElement::bootDisplayMode() const { return (BootDisplayMode)getUInt8(Offset::bootDisplayMode()); } void GD73Codeplug::SettingsElement::setBootDisplayMode(BootDisplayMode mode) { setUInt8(Offset::bootDisplayMode(), (unsigned int)mode); } QString GD73Codeplug::SettingsElement::bootTextLine1() const { return readUnicode(Offset::bootTextLine1(), Limit::bootTextLine(), 0x0000); } void GD73Codeplug::SettingsElement::setBootTextLine1(const QString &line) { writeUnicode(Offset::bootTextLine1(), line, Limit::bootTextLine(), 0x0000); } QString GD73Codeplug::SettingsElement::bootTextLine2() const { return readUnicode(Offset::bootTextLine2(), Limit::bootTextLine(), 0x0000); } void GD73Codeplug::SettingsElement::setBootTextLine2(const QString &line) { writeUnicode(Offset::bootTextLine2(), line, Limit::bootTextLine(), 0x0000); } bool GD73Codeplug::SettingsElement::keyToneEnabled() const { return 0x00 != getUInt8(Offset::keyToneEnable()); } void GD73Codeplug::SettingsElement::enableKeyTone(bool enable) { setUInt8(Offset::keyToneEnable(), enable ? 0x01 : 0x00); } unsigned int GD73Codeplug::SettingsElement::keyToneVolume() const { return getUInt8(Offset::keyToneVolume()); } void GD73Codeplug::SettingsElement::setKeyToneVolume(unsigned int vol) { setUInt8(Offset::keyToneVolume(), Limit::toneVolume().map(vol)); } bool GD73Codeplug::SettingsElement::lowBatteryToneEnabled() const { return 0x00 != getUInt8(Offset::lowBatToneEnable()); } void GD73Codeplug::SettingsElement::enableLowBatteryTone(bool enable) { setUInt8(Offset::lowBatToneEnable(), enable ? 0x01 : 0x00); } unsigned int GD73Codeplug::SettingsElement::lowBatteryToneVolume() const { return getUInt8(Offset::lowBatToneVolume()); } void GD73Codeplug::SettingsElement::setLowBatteryToneVolume(unsigned int vol) { setUInt8(Offset::lowBatToneVolume(), Limit::toneVolume().map(vol)); } Interval GD73Codeplug::SettingsElement::longPressDuration() const { return Interval::fromMilliseconds(500*((unsigned int)getUInt8(Offset::longPressDuration()))); } void GD73Codeplug::SettingsElement::setLongPressDuration(const Interval &interval) { Interval intv = Limit::longPressDuration().map(interval); setUInt8(Offset::longPressDuration(), intv.milliseconds()/500); } RadioddityButtonSettingsExtension::Function GD73Codeplug::SettingsElement::keyFunctionLongPressP1() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey1LongPress())); } void GD73Codeplug::SettingsElement::setKeyFunctionLongPressP1(RadioddityButtonSettingsExtension::Function function) { setUInt8(Offset::progFuncKey1LongPress(), KeyFunction::encode(function)); } RadioddityButtonSettingsExtension::Function GD73Codeplug::SettingsElement::keyFunctionShortPressP1() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey1ShortPress())); } void GD73Codeplug::SettingsElement::setKeyFunctionShortPressP1(RadioddityButtonSettingsExtension::Function function) { setUInt8(Offset::progFuncKey1ShortPress(), KeyFunction::encode(function)); } RadioddityButtonSettingsExtension::Function GD73Codeplug::SettingsElement::keyFunctionLongPressP2() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey2LongPress())); } void GD73Codeplug::SettingsElement::setKeyFunctionLongPressP2(RadioddityButtonSettingsExtension::Function function) { setUInt8(Offset::progFuncKey2LongPress(), KeyFunction::encode(function)); } RadioddityButtonSettingsExtension::Function GD73Codeplug::SettingsElement::keyFunctionShortPressP2() const { return KeyFunction::decode(getUInt8(Offset::progFuncKey2ShortPress())); } void GD73Codeplug::SettingsElement::setKeyFunctionShortPressP2(RadioddityButtonSettingsExtension::Function function) { setUInt8(Offset::progFuncKey2ShortPress(), KeyFunction::encode(function)); } GD73Codeplug::OneTouchSettingElement GD73Codeplug::SettingsElement::oneTouch(unsigned int n) { return OneTouchSettingElement( _data+Offset::oneTouchSettings() + n*Offset::betweenOneTouchSettings()); } bool GD73Codeplug::SettingsElement::updateConfig(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Create radio ID DMRRadioID *radioID = new DMRRadioID(name(), dmrID()); ctx.config()->radioIDs()->add(radioID); ctx.config()->settings()->setDefaultId(radioID); ctx.add(radioID, 0); // Apply settings ctx.config()->settings()->setIntroLine1(bootTextLine1()); ctx.config()->settings()->setIntroLine2(bootTextLine2()); ctx.config()->settings()->setMicLevel(dmrMicGain()); ctx.config()->settings()->setSquelch(squelch()); ctx.config()->settings()->setVOX(vox()); if (! totIsSet()) ctx.config()->settings()->setTOT(0); else ctx.config()->settings()->setTOT(tot().seconds()); // Get/add radioddity settings extension RadiodditySettingsExtension *ext = ctx.config()->settings()->radioddityExtension(); if (nullptr == ext) ctx.config()->settings()->setRadioddityExtension(ext = new RadiodditySettingsExtension()); ext->setLoneWorkerResponseTime(loneWorkerResponseTimeout()); ext->setLoneWorkerReminderPeriod(loneWorkerRemindPeriod()); ext->enableTXInterrupt(txInterruptedEnabled()); switch(language()) { case Language::Chinese: ext->setLanguage(RadiodditySettingsExtension::Language::Chinese); break; case Language::English: ext->setLanguage(RadiodditySettingsExtension::Language::English); break; } ext->enablePowerSaveMode(powerSaveEnabled()); ext->setPowerSaveDelay(powerSaveTimeout()); ext->buttons()->setLongPressDuration(longPressDuration()); ext->buttons()->setFuncKey1Short(keyFunctionShortPressP1()); ext->buttons()->setFuncKey1Long(keyFunctionLongPressP1()); ext->buttons()->setFuncKey2Short(keyFunctionShortPressP2()); ext->buttons()->setFuncKey2Long(keyFunctionLongPressP2()); ext->tone()->setFMMicGain(fmMicGain()); ext->tone()->enableKeyTone(keyToneEnabled()); ext->tone()->setKeyToneVolume(keyToneVolume()); ext->tone()->enableLowBatteryWarn(lowBatteryToneEnabled()); ext->tone()->setLowBatteryWarnVolume(lowBatteryToneVolume()); switch (bootDisplayMode()) { case BootDisplayMode::Off: ext->boot()->setDisplay(RadioddityBootSettingsExtension::DisplayMode::None); break; case BootDisplayMode::Text: ext->boot()->setDisplay(RadioddityBootSettingsExtension::DisplayMode::Text); break; case BootDisplayMode::Image: case BootDisplayMode::Both: ext->boot()->setDisplay(RadioddityBootSettingsExtension::DisplayMode::Image); break; } if (readLockEnabled()) ext->boot()->setBootPassword(readLockPin()); else ext->boot()->setBootPassword(""); if (writeLockEnabled()) ext->boot()->setProgPassword(writeLockPin()); else ext->boot()->setProgPassword(""); return true; } bool GD73Codeplug::SettingsElement::encode(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Get default radio ID if (nullptr == ctx.config()->settings()->defaultId()) { errMsg(err) << "Cannot encode default radio DMR ID, none is set."; return false; } setDMRID(ctx.config()->settings()->defaultId()->number()); setName(ctx.config()->settings()->defaultId()->name()); // Apply settings setBootTextLine1(ctx.config()->settings()->introLine1()); setBootTextLine2(ctx.config()->settings()->introLine2()); setDMRMicGain(ctx.config()->settings()->micLevel()); setFMMicGain(ctx.config()->settings()->micLevel()); setSquelch(ctx.config()->settings()->squelch()); setVOX(ctx.config()->settings()->vox()); if (ctx.config()->settings()->totDisabled()) clearTOT(); else setTOT(Interval::fromSeconds(ctx.config()->settings()->tot())); setLanguage(Language::English); setUInt8(0x003c, 0x01); setUInt8(0x003e, 0x01); // Get/add radioddity settings extension RadiodditySettingsExtension *ext = ctx.config()->settings()->radioddityExtension(); if (nullptr == ext) return true; setLoneWorkerResponseTimeout(ext->loneWorkerResponseTime()); setLoneWorkerRemindPeriod(ext->loneWorkerReminderPeriod()); enableTXInterrupt(ext->txInterrupt()); switch(ext->language()) { case RadiodditySettingsExtension::Language::Chinese: setLanguage(Language::Chinese); break; case RadiodditySettingsExtension::Language::English: setLanguage(Language::English); break; } enablePowerSave(ext->powerSaveMode()); setPowerSaveTimeout(ext->powerSaveDelay()); setLongPressDuration(ext->buttons()->longPressDuration()); setKeyFunctionShortPressP1(ext->buttons()->funcKey1Short()); setKeyFunctionLongPressP1(ext->buttons()->funcKey1Long()); setKeyFunctionShortPressP2(ext->buttons()->funcKey2Short()); setKeyFunctionLongPressP2(ext->buttons()->funcKey2Long()); setFMMicGain(ext->tone()->fmMicGain()); enableKeyTone(ext->tone()->keyTone()); setKeyToneVolume(ext->tone()->keyToneVolume()); enableLowBatteryTone(ext->tone()->lowBatteryWarn()); setLowBatteryToneVolume(ext->tone()->lowBatteryWarnVolume()); switch(ext->boot()->display()) { case RadioddityBootSettingsExtension::DisplayMode::None: setBootDisplayMode(BootDisplayMode::Off); break; case RadioddityBootSettingsExtension::DisplayMode::Text: setBootDisplayMode(BootDisplayMode::Text); break; case RadioddityBootSettingsExtension::DisplayMode::Image: setBootDisplayMode(BootDisplayMode::Image); break; } if (! ext->boot()->bootPassword().isEmpty()) { setReadLockPin(ext->boot()->bootPassword()); enableReadLock(true); } else { enableReadLock(false); } if (! ext->boot()->progPassword().isEmpty()) { setWriteLockPin(ext->boot()->bootPassword()); enableWriteLock(true); } else { enableWriteLock(false); } return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::OneTouchSettingElement * ********************************************************************************************* */ GD73Codeplug::OneTouchSettingElement::OneTouchSettingElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::OneTouchSettingElement::OneTouchSettingElement(uint8_t *ptr) : Element(ptr, OneTouchSettingElement::size()) { // pass... } /* ********************************************************************************************* * * Implementation of GD73Codeplug::DMRSettingsElement * ********************************************************************************************* */ GD73Codeplug::DMRSettingsElement::DMRSettingsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::DMRSettingsElement::DMRSettingsElement(uint8_t *ptr) : Element(ptr, DMRSettingsElement::size()) { // pass... } Interval GD73Codeplug::DMRSettingsElement::callHangTime() const { return Interval::fromSeconds(getUInt8(Offset::callHangTime())+1); } void GD73Codeplug::DMRSettingsElement::setCallHangTime(const Interval &intv) { setUInt8(Offset::callHangTime(), Limit::callHangTime().map(intv).seconds()-1); } Interval GD73Codeplug::DMRSettingsElement::activeWaitTime() const { return Interval::fromMilliseconds(120 + ((unsigned int)getUInt8(Offset::activeWaitTime()))*5); } void GD73Codeplug::DMRSettingsElement::setActiveWaitTime(const Interval &intv) { setUInt8(Offset::activeWaitTime(), (Limit::activeWaitTime().map(intv).milliseconds()-120)/5); } unsigned int GD73Codeplug::DMRSettingsElement::activeRetries() const { return getUInt8(Offset::activeRetries()); } void GD73Codeplug::DMRSettingsElement::setActiveRetries(unsigned int count) { setUInt8(Offset::activeRetries(), Limit::activeRetires().map(count)); } unsigned int GD73Codeplug::DMRSettingsElement::txPreambles() const { return getUInt8(Offset::txPreambles()); } void GD73Codeplug::DMRSettingsElement::setTXPreambles(unsigned int count) { setUInt8(Offset::txPreambles(), Limit::txPreambles().map(count)); } bool GD73Codeplug::DMRSettingsElement::decodeDisableRadioEnabled() const { return 0x00 != getUInt8(Offset::decodeDisableRadio()); } void GD73Codeplug::DMRSettingsElement::enableDecodeDisableRadio(bool enable) { setUInt8(Offset::decodeDisableRadio(), enable ? 0x01 : 0x00); } bool GD73Codeplug::DMRSettingsElement::decodeRadioCheckEnabled() const { return 0x00 != getUInt8(Offset::decodeCheckRadio()); } void GD73Codeplug::DMRSettingsElement::enableDecodeRadioCheck(bool enable) { setUInt8(Offset::decodeCheckRadio(), enable ? 0x01 : 0x00); } bool GD73Codeplug::DMRSettingsElement::decodeEnableRadioEnabled() const { return 0x00 != getUInt8(Offset::decodeEnableRadio()); } void GD73Codeplug::DMRSettingsElement::enableDecodeEnableRadio(bool enable) { setUInt8(Offset::decodeEnableRadio(), enable ? 0x01 : 0x00); } bool GD73Codeplug::DMRSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) { Q_UNUSED(err); if (nullptr == ctx.config()->settings()->radioddityExtension()) { ctx.config()->settings()->setRadioddityExtension(new RadiodditySettingsExtension()); } auto ext = ctx.config()->settings()->radioddityExtension(); ext->setPrivateCallHangTime(callHangTime()); ext->setGroupCallHangTime(callHangTime()); return true; } bool GD73Codeplug::DMRSettingsElement::encode(Context &ctx, const ErrorStack &err) { Q_UNUSED(err); if (nullptr == ctx.config()->settings()->radioddityExtension()) return true; auto ext = ctx.config()->settings()->radioddityExtension(); setCallHangTime(std::max(ext->privateCallHangTime(), ext->groupCallHangTime())); return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::EncryptionKeyElement * ********************************************************************************************* */ GD73Codeplug::EncryptionKeyElement::EncryptionKeyElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::EncryptionKeyElement::EncryptionKeyElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } void GD73Codeplug::EncryptionKeyElement::clear() { setKeySize(0); } bool GD73Codeplug::EncryptionKeyElement::isValid() const { return Element::isValid() && (0 != keySize()); } unsigned int GD73Codeplug::EncryptionKeyElement::keySize() const { return getUInt8(Offset::size())*4; } void GD73Codeplug::EncryptionKeyElement::setKeySize(unsigned int size) { setUInt8(Offset::size(), size/4); } BasicEncryptionKey * GD73Codeplug::EncryptionKeyElement::createEncryptionKey(const ErrorStack &err) const { if (! isValid()) return nullptr; BasicEncryptionKey *key = new BasicEncryptionKey(); if (! key->setKey(QByteArray((const char*)_data+Offset::key(), keySize()/8))) { errMsg(err) << "Cannot decode encryption key of size " << keySize() << "."; delete key; return nullptr; } return key; } bool GD73Codeplug::EncryptionKeyElement::encodeEncryptionKey(BasicEncryptionKey *key, const ErrorStack &err) { unsigned int size = key->key().size()*8; if (size > 32) { errMsg(err) << "Key size of " << size << " exceeds 32bit."; return false; } setKeySize(size); memcpy(_data+Offset::key(), key->key().constData(), key->key().size()); return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::EncryptionKeyBankElement * ********************************************************************************************* */ GD73Codeplug::EncryptionKeyBankElement::EncryptionKeyBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::EncryptionKeyBankElement::EncryptionKeyBankElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } bool GD73Codeplug::EncryptionKeyBankElement::createEncryptionKeys(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; isetName(QString("Basic Key %1").arg(i+1)); ctx.add(key, i); } return true; } bool GD73Codeplug::EncryptionKeyBankElement::encodeEncryptionKeys(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; i= ctx.count()) continue; if (! keyElement.encodeEncryptionKey(ctx.get(i))) { errMsg(err) << "Cannot encode " << i+1 << "-th encryption key."; return false; } } return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::MessageElement * ********************************************************************************************* */ GD73Codeplug::MessageElement::MessageElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::MessageElement::MessageElement(uint8_t *ptr) : Element(ptr, GD73Codeplug::MessageElement::size()) { // pass... } QString GD73Codeplug::MessageElement::text() const { unsigned int length = std::min(Limit::messageLength(), (unsigned int)getUInt8(Offset::size())); return readUnicode(Offset::text(), length, 0x0000); } void GD73Codeplug::MessageElement::setText(const QString &message) { unsigned int length = std::min(Limit::messageLength(), (unsigned int)message.length()); writeUnicode(Offset::text(), message, length, 0x0000); setUInt8(Offset::size(), length); } bool GD73Codeplug::MessageElement::encode(SMSTemplate *message, const ErrorStack &err) { Q_UNUSED(err); setText(message->message()); return true; } SMSTemplate * GD73Codeplug::MessageElement::decode(const ErrorStack &err) { Q_UNUSED(err); SMSTemplate *sms = new SMSTemplate(); sms->setName("Message"); sms->setMessage(text()); return sms; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::MessageBankElement * ********************************************************************************************* */ GD73Codeplug::MessageBankElement::MessageBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::MessageBankElement::MessageBankElement(uint8_t *ptr) : Element(ptr, MessageBankElement::size()) { // pass... } unsigned int GD73Codeplug::MessageBankElement::memberCount() const { return getUInt8(Offset::memberCount()); } void GD73Codeplug::MessageBankElement::setMemberCount(unsigned int count) { setUInt8(Offset::memberCount(), std::min(Limit::memberCount(), count)); } GD73Codeplug::MessageElement GD73Codeplug::MessageBankElement::message(unsigned int i) { i = std::min(i, memberCount()-1); return MessageElement(_data + Offset::members() + i*Offset::betweenMembers()); } bool GD73Codeplug::MessageBankElement::decode(SMSExtension *ext, const ErrorStack &err) { ext->smsTemplates()->clear(); for (unsigned int i=0; isetName(QString("Message %1").arg(i+1)); ext->smsTemplates()->add(sms); } return true; } bool GD73Codeplug::MessageBankElement::encode(const SMSExtension *ext, const ErrorStack &err) { setMemberCount(0); unsigned int count = std::min((unsigned int) ext->smsTemplates()->count(), Limit::memberCount()); for (unsigned int i=0; ismsTemplates()->get(i)->as(), err)) { errMsg(err) << "Cannot encode " << i+1 << "-th preset message."; return false; } } setMemberCount(count); return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::ContactBankElement * ********************************************************************************************* */ GD73Codeplug::ContactBankElement::ContactBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::ContactBankElement::ContactBankElement(uint8_t *ptr) : Element(ptr, ContactBankElement::size()) { // pass... } bool GD73Codeplug::ContactBankElement::createContacts(Context &ctx, const ErrorStack &err) { unsigned int count = std::min((unsigned int)getUInt16_le(Offset::contactCount()), Limit::contactCount()); for (unsigned int i=0; icontacts()->add(contactObj); ctx.add(contactObj, i); } return true; } bool GD73Codeplug::ContactBankElement::encode(Context &ctx, const ErrorStack &err) { unsigned int count = std::min(ctx.count(), Limit::contactCount()); setUInt16_le(Offset::contactCount(), count); for (unsigned int i=0; i(i)) { errMsg(err) << "Contact at index " << i << " not indexed."; errMsg(err) << "Cannot encode contact bank."; return false; } DMRContact *contactObj = ctx.get(i); if (! contact.encode(contactObj, ctx, err)) { errMsg(err) << "Cannot encode contact at index " << i << "."; errMsg(err) << "Cannot encode contact bank."; return false; } } return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::ContactElement * ********************************************************************************************* */ GD73Codeplug::ContactElement::ContactElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::ContactElement::ContactElement(uint8_t *ptr) : Element(ptr, ContactElement::size()) { // pass... } QString GD73Codeplug::ContactElement::name() const { return readUnicode(Offset::name(), Limit::nameLength(), 0x0000); } void GD73Codeplug::ContactElement::setName(const QString &name) { writeUnicode(Offset::name(), name, Limit::nameLength(), 0x0000); } DMRContact::Type GD73Codeplug::ContactElement::type() const { switch (getUInt8(Offset::type())) { case 0: return DMRContact::GroupCall; case 1: return DMRContact::PrivateCall; case 2: return DMRContact::AllCall; } return DMRContact::PrivateCall; } void GD73Codeplug::ContactElement::setType(DMRContact::Type type) { switch (type) { case DMRContact::GroupCall: setUInt8(Offset::type(), 0); break; case DMRContact::PrivateCall: setUInt8(Offset::type(), 1); break; case DMRContact::AllCall: setUInt8(Offset::type(), 2); break; } } unsigned int GD73Codeplug::ContactElement::id() const { return getUInt32_le(Offset::id()); } void GD73Codeplug::ContactElement::setID(unsigned int id) { setUInt32_le(Offset::id(), id); } DMRContact * GD73Codeplug::ContactElement::toContact(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); return new DMRContact(type(), name(), id()); } bool GD73Codeplug::ContactElement::encode(const DMRContact *contact, Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); setType(contact->type()); setName(contact->name()); setID(contact->number()); return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::GroupListBankElement * ********************************************************************************************* */ GD73Codeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr) : Element(ptr, GroupListBankElement::size()) { // pass... } bool GD73Codeplug::GroupListBankElement::createGroupLists(Context &ctx, const ErrorStack &err) { unsigned int count = std::min((unsigned int)getUInt8(Offset::memberCount()), Limit::memberCount()); for (unsigned int i=0; irxGroupLists()->add(glObj); ctx.add(glObj, i); } return true; } bool GD73Codeplug::GroupListBankElement::linkGroupLists(Context &ctx, const ErrorStack &err) { unsigned int count = std::min(ctx.count(), Limit::memberCount()); for (unsigned int i=0; i(i); if (! gl.linkGroupList(glObj, ctx, err)) { errMsg(err) << "Cannot link " << i << "-th group list."; return false; } } return true; } bool GD73Codeplug::GroupListBankElement::encode(Context &ctx, const ErrorStack &err) { unsigned int count = std::min(ctx.count(), Limit::memberCount()); setUInt8(Offset::memberCount(), count); for (unsigned int i=0; i(i)) { errMsg(err) << "Group list at index " << i << " is not indexed."; return false; } if (!gl.encode(ctx.get(i), ctx, err)) { errMsg(err) << "Cannot encode group list at index " << i << "."; return false; } } return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::GroupListElement * ********************************************************************************************* */ GD73Codeplug::GroupListElement::GroupListElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::GroupListElement::GroupListElement(uint8_t *ptr) : Element(ptr, GroupListElement::size()) { // pass... } QString GD73Codeplug::GroupListElement::name() const { return readUnicode(Offset::name(), Limit::nameLength(), 0x0000); } void GD73Codeplug::GroupListElement::setName(const QString &name) { writeUnicode(Offset::name(), name, Limit::nameLength(), 0x0000); } unsigned int GD73Codeplug::GroupListElement::members() const { return getUInt8(Offset::memberCount()); } bool GD73Codeplug::GroupListElement::hasMember(unsigned int i) const { return 0 != getUInt16_le(Offset::members() + i*Offset::betweenMembers()); } unsigned int GD73Codeplug::GroupListElement::memberIndex(unsigned int i) const { return getUInt16_le(Offset::members() + i*Offset::betweenMembers())-1; } RXGroupList * GD73Codeplug::GroupListElement::toGroupList(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); return new RXGroupList(name()); } bool GD73Codeplug::GroupListElement::linkGroupList(RXGroupList *lst, Context &ctx, const ErrorStack &err) { Q_UNUSED(err); unsigned int count = std::min(members(), Limit::memberCount()); for (unsigned int i=0; i(idx)) lst->addContact(ctx.get(idx)); else logWarn() << "Cannot link group list '" << lst->name() << "': Cannot resolve contact at index " << idx << "."; } return true; } bool GD73Codeplug::GroupListElement::encode(RXGroupList *lst, Context &ctx, const ErrorStack &err) { unsigned int count = std::min((unsigned int)lst->count(), Limit::memberCount()); setName(lst->name()); setUInt8(Offset::memberCount(), count); for (unsigned int i=0; i ctx.index(lst->contact(i))) { errMsg(err) << "Cannot resolve index of contact '" << lst->contact(i)->name() << "'."; return false; } setUInt16_le(Offset::members() + i*Offset::betweenMembers(), ctx.index(lst->contact(i))+1); } return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::ChannelBankElement * ********************************************************************************************* */ GD73Codeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr) : Element(ptr, ChannelBankElement::size()) { // pass... } bool GD73Codeplug::ChannelBankElement::createChannels(Context &ctx, const ErrorStack &err) { unsigned int count = std::min((unsigned int)getUInt8(Offset::channelCount()), Limit::channelCount()); for (unsigned int i=0; ichannelList()->add(chObj); ctx.add(chObj, i); } return true; } bool GD73Codeplug::ChannelBankElement::linkChannels(Context &ctx, const ErrorStack &err) { unsigned int count = std::min(ctx.count(), Limit::channelCount()); for (unsigned int i=0; i(i); if (! ch.linkChannel(chObj, ctx, err)) { errMsg(err) << "Cannot link channel at index " << i << "."; return false; } } return true; } bool GD73Codeplug::ChannelBankElement::encode(Context &ctx, const ErrorStack &err) { unsigned int count = std::min(ctx.count(), Limit::channelCount()); setUInt16_le(Offset::channelCount(), count); for (unsigned int i=0; i(i); if (! ch.encode(chObj, ctx, err)) { errMsg(err) << "Cannot encode channel at index " << i << "."; return false; } } return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::ChannelElement * ********************************************************************************************* */ GD73Codeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::ChannelElement::ChannelElement(uint8_t *ptr) : Element(ptr, ChannelElement::size()) { // pass... } QString GD73Codeplug::ChannelElement::name() const { return readUnicode(Offset::name(), Limit::nameLength(), 0x0000); } void GD73Codeplug::ChannelElement::setName(const QString &name) { writeUnicode(Offset::name(), name, Limit::nameLength(), 0x0000); } FMChannel::Bandwidth GD73Codeplug::ChannelElement::bandwidth() const { switch (getUInt8(Offset::bandwidth())) { case 0: return FMChannel::Bandwidth::Narrow; case 1: return FMChannel::Bandwidth::Wide; } return FMChannel::Bandwidth::Narrow; } void GD73Codeplug::ChannelElement::setBandwidth(FMChannel::Bandwidth bandwidth) { switch (bandwidth) { case FMChannel::Bandwidth::Narrow: setUInt8(Offset::bandwidth(), 0); break; case FMChannel::Bandwidth::Wide: setUInt8(Offset::bandwidth(), 1); break; } } bool GD73Codeplug::ChannelElement::hasScanListIndex() const { return 0 != getUInt8(Offset::scanList()); } unsigned int GD73Codeplug::ChannelElement::scanListIndex() const { return getUInt8(Offset::scanList())-1; } void GD73Codeplug::ChannelElement::setScanListIndex(unsigned int idx) { setUInt8(Offset::scanList(), idx+1); } void GD73Codeplug::ChannelElement::clearScanListIndex() { setUInt8(Offset::scanList(), 0); } GD73Codeplug::ChannelElement::Type GD73Codeplug::ChannelElement::type() const { return (Type) getUInt8(Offset::channelType()); } void GD73Codeplug::ChannelElement::setType(Type type) { setUInt8(Offset::channelType(), (unsigned int)type); } bool GD73Codeplug::ChannelElement::talkaroundEnabled() const { return 0x00 != getUInt8(Offset::talkaround()); } void GD73Codeplug::ChannelElement::enableTalkaround(bool enable) { setUInt8(Offset::talkaround(), enable ? 0x01 : 0x00); } bool GD73Codeplug::ChannelElement::rxOnly() const { return 0x00 != getUInt8(Offset::rxOnly()); } void GD73Codeplug::ChannelElement::enableRXOnly(bool enable) { setUInt8(Offset::rxOnly(), enable ? 0x01 : 0x00); } bool GD73Codeplug::ChannelElement::scanAutoStartEnabled() const { return 0x00 != getUInt8(Offset::scanAutoStart()); } void GD73Codeplug::ChannelElement::enableScanAutoStart(bool enable) { setUInt8(Offset::scanAutoStart(), enable ? 0x01 : 0x00); } Frequency GD73Codeplug::ChannelElement::rxFrequency() const { return Frequency::fromHz(getUInt32_le(Offset::rxFrequency())); } void GD73Codeplug::ChannelElement::setRXFrequency(const Frequency &f) { setUInt32_le(Offset::rxFrequency(), f.inHz()); } Frequency GD73Codeplug::ChannelElement::txFrequency() const { return Frequency::fromHz(getUInt32_le(Offset::txFrequency())); } void GD73Codeplug::ChannelElement::setTXFrequency(const Frequency &f) { setUInt32_le(Offset::txFrequency(), f.inHz()); } bool GD73Codeplug::ChannelElement::hasDTMFPTTSettingsIndex() const { return 0x00 != getUInt8(Offset::dtmfPTTSettingsIndex()); } unsigned int GD73Codeplug::ChannelElement::dtmfPTTSettingsIndex() const { return getUInt8(Offset::dtmfPTTSettingsIndex())-1; } void GD73Codeplug::ChannelElement::setDTMFPTTSettingsIndex(unsigned int idx) { setUInt8(Offset::dtmfPTTSettingsIndex(), idx+1); } void GD73Codeplug::ChannelElement::clearDTMFPTTSettingsIndex() { setUInt8(Offset::dtmfPTTSettingsIndex(), 0); } Channel::Power GD73Codeplug::ChannelElement::power() const { switch (getUInt8(Offset::power())) { case 0: return Channel::Power::Low; case 1: return Channel::Power::High; } return Channel::Power::Low; } void GD73Codeplug::ChannelElement::setPower(Channel::Power power) { switch (power) { case Channel::Power::Min: case Channel::Power::Low: case Channel::Power::Mid: setUInt8(Offset::power(), 0); break; case Channel::Power::High: case Channel::Power::Max: setUInt8(Offset::power(), 1); break; } } GD73Codeplug::ChannelElement::Admit GD73Codeplug::ChannelElement::admit() const { return (Admit) getUInt8(Offset::admid()); } void GD73Codeplug::ChannelElement::setAdmit(Admit admit) { setUInt8(Offset::admid(), (unsigned int) admit); } SelectiveCall GD73Codeplug::ChannelElement::rxTone() const { int mode = getUInt8(Offset::rxToneMode()); int ctcss_code = getUInt8(Offset::rxCTCSS()); int dcs_code = getUInt8(Offset::rxDCS()); if (0 == mode) return SelectiveCall(); if (1 == mode) { if (ctcss_code >= _ctcss_codes.size()) return SelectiveCall(); return _ctcss_codes[ctcss_code]; } if (dcs_code >= _dcs_codes.size()) return SelectiveCall(); return SelectiveCall(_dcs_codes[dcs_code], 3 == mode); } void GD73Codeplug::ChannelElement::setRXTone(const SelectiveCall &code) { int mode = 0, ctcss_code = 0, dcs_code = 0; if (code.isCTCSS()) { mode = 1; ctcss_code = _ctcss_codes.indexOf(code); } else if (code.isDCS()) { if (code.isInverted()) mode = 3; else mode = 2; dcs_code = _dcs_codes.indexOf(code.octalCode()); } setUInt8(Offset::rxToneMode(), mode); setUInt8(Offset::rxCTCSS(), ctcss_code); setUInt8(Offset::rxDCS(), dcs_code); } SelectiveCall GD73Codeplug::ChannelElement::txTone() const { int mode = getUInt8(Offset::txToneMode()); int ctcss_code = getUInt8(Offset::txCTCSS()); int dcs_code = getUInt8(Offset::txDCS()); if (0 == mode) return SelectiveCall(); if (1 == mode) { if (ctcss_code >= _ctcss_codes.size()) return SelectiveCall(); return _ctcss_codes[ctcss_code]; } if (dcs_code >= _dcs_codes.size()) return SelectiveCall(); return SelectiveCall(_dcs_codes[dcs_code], 3 == mode); } void GD73Codeplug::ChannelElement::setTXTone(const SelectiveCall &code) { int mode = 0, ctcss_code = 0, dcs_code = 0; if (code.isCTCSS()) { mode = 1; ctcss_code = _ctcss_codes.indexOf(code); } else if (code.isDCS()) { if (code.isInverted()) mode = 3; else mode = 2; dcs_code = _dcs_codes.indexOf(code.octalCode()); } setUInt8(Offset::txToneMode(), mode); setUInt8(Offset::txCTCSS(), ctcss_code); setUInt8(Offset::txDCS(), dcs_code); } DMRChannel::TimeSlot GD73Codeplug::ChannelElement::timeSlot() const { switch (getUInt8(Offset::timeslot())) { case 0: case 2: return DMRChannel::TimeSlot::TS1; case 1: case 3: return DMRChannel::TimeSlot::TS2; } return DMRChannel::TimeSlot::TS1; } void GD73Codeplug::ChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) { switch (ts) { case DMRChannel::TimeSlot::TS1: setUInt8(Offset::timeslot(), 0); break; case DMRChannel::TimeSlot::TS2: setUInt8(Offset::timeslot(), 1); break; } } unsigned int GD73Codeplug::ChannelElement::colorCode() const { return getUInt8(Offset::colorcode()); } void GD73Codeplug::ChannelElement::setColorCode(unsigned int cc) { setUInt8(Offset::colorcode(), std::min(15u, cc)); } bool GD73Codeplug::ChannelElement::groupListMatchesContact() const { return 0 == getUInt8(Offset::groupListIndex()); } bool GD73Codeplug::ChannelElement::groupListAllMatch() const { return 1 == getUInt8(Offset::groupListIndex()); } unsigned int GD73Codeplug::ChannelElement::groupListIndex() const { return getUInt8(Offset::groupListIndex())-2; } void GD73Codeplug::ChannelElement::setGroupListIndex(unsigned int idx) { setUInt8(Offset::groupListIndex(), idx+2); } void GD73Codeplug::ChannelElement::setGroupListAllMatch() { setUInt8(Offset::groupListIndex(), 1); } void GD73Codeplug::ChannelElement::setGroupListMatchesContact() { setUInt8(Offset::groupListIndex(), 0); } bool GD73Codeplug::ChannelElement::hasTXContact() const { return 0 != getUInt16_le(Offset::contactIndex()); } unsigned int GD73Codeplug::ChannelElement::txContactIndex() const { return getUInt16_le(Offset::contactIndex())-1; } void GD73Codeplug::ChannelElement::setTXContactIndex(unsigned int idx) { setUInt16_le(Offset::contactIndex(), idx+1); } void GD73Codeplug::ChannelElement::clearTXContactIndex() { setUInt16_le(Offset::contactIndex(), 0); } bool GD73Codeplug::ChannelElement::hasEmergencySystemIndex() const { return 0 != getUInt8(Offset::emergencySystemIndex()); } unsigned int GD73Codeplug::ChannelElement::emergencySystemIndex() const { return getUInt8(Offset::emergencySystemIndex())-1; } void GD73Codeplug::ChannelElement::setEmergencySystemIndex(unsigned int idx) { setUInt8(Offset::emergencySystemIndex(), idx+1); } void GD73Codeplug::ChannelElement::clearEmergencySystemIndex() { setUInt8(Offset::emergencySystemIndex(), 0); } bool GD73Codeplug::ChannelElement::hasEncryptionKeyIndex() const { return 0 != getUInt8(Offset::encryptionKeyIndex()); } unsigned int GD73Codeplug::ChannelElement::encryptionKeyIndex() const { return getUInt8(Offset::encryptionKeyIndex())-1; } void GD73Codeplug::ChannelElement::setEncryptionKeyIndex(unsigned int idx) { setUInt8(Offset::encryptionKeyIndex(), idx+1); } void GD73Codeplug::ChannelElement::clearEncryptionKeyIndex() { setUInt8(Offset::encryptionKeyIndex(), 0); } Channel * GD73Codeplug::ChannelElement::toChannel(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err) Channel *ch = nullptr; if (Type::FM == type()) { FMChannel *fm = new FMChannel(); ch = fm; switch (admit()) { case Admit::Always: fm->setAdmit(FMChannel::Admit::Always); break; case Admit::CC_CTCSS: fm->setAdmit(FMChannel::Admit::Tone); break; case Admit::Free: fm->setAdmit(FMChannel::Admit::Free); break; } fm->setBandwidth(bandwidth()); fm->setSquelchDefault(); fm->setRXTone(rxTone()); fm->setTXTone(txTone()); } else if (Type::DMR == type()) { DMRChannel *dmr = new DMRChannel(); ch = dmr; switch (admit()) { case Admit::Always: dmr->setAdmit(DMRChannel::Admit::Always); break; case Admit::CC_CTCSS: dmr->setAdmit(DMRChannel::Admit::ColorCode); break; case Admit::Free: dmr->setAdmit(DMRChannel::Admit::Free); break; } dmr->setColorCode(colorCode()); dmr->setTimeSlot(timeSlot()); dmr->setRadioIdObj(DefaultRadioID::get()); } ch->setName(name()); ch->setRXFrequency(rxFrequency()); ch->setTXFrequency(txFrequency()); ch->setRXOnly(rxOnly()); return ch; } bool GD73Codeplug::ChannelElement::linkChannel(Channel *ch, Context &ctx, const ErrorStack &err) { Q_UNUSED(err); if (Type::DMR == type()) { DMRChannel *dmr = ch->as(); if (hasTXContact()) { if (ctx.has(txContactIndex())) dmr->setTXContactObj(ctx.get(txContactIndex())); else logWarn() << "Cannot link channel '" << name() << "', cannot resolve contact index " << txContactIndex() << "."; } if ((! groupListAllMatch()) && (! groupListMatchesContact())) { if (ctx.has(groupListIndex())) dmr->setGroupListObj(ctx.get(groupListIndex())); else logWarn() << "Cannot link channel '" << name() << "', cannot resolve group list index " << groupListIndex() << "."; } if (hasEncryptionKeyIndex()) { // Check if already defined if (! ctx.has(encryptionKeyIndex())) { errMsg(err) << "Cannot link channel '" << name() << "', cannot resolve encryption key index " << encryptionKeyIndex() << "."; return false; } // set... if (nullptr == dmr->commercialExtension()) dmr->setCommercialExtension(new CommercialChannelExtension()); dmr->commercialExtension()->setEncryptionKey( ctx.get(encryptionKeyIndex())); } } if (hasScanListIndex()) { if (ctx.has(scanListIndex())) ch->setScanList(ctx.get(scanListIndex())); else logWarn() << "Cannot link channel '" << name() << "', cannot resolve scanlist index " << scanListIndex() << "."; } return true; } bool GD73Codeplug::ChannelElement::encode(Channel *ch, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) // Encode common stuff setName(ch->name()); setRXFrequency(ch->rxFrequency()); setTXFrequency(ch->txFrequency()); enableRXOnly(ch->rxOnly()); if (! ch->scanListRef()->isNull()) setScanListIndex(ctx.index(ch->scanList())); // Dispatch by type if (ch->is()) { DMRChannel *dmr = ch->as(); setType(ChannelElement::Type::DMR); if (! dmr->contact()->isNull()) setTXContactIndex(ctx.index(dmr->txContactObj())); if (dmr->groupList()->isNull()) setGroupListAllMatch(); else setGroupListIndex(ctx.index(dmr->groupListObj())); switch (dmr->admit()) { case DMRChannel::Admit::Always: setAdmit(Admit::Always); break; case DMRChannel::Admit::ColorCode: setAdmit(Admit::CC_CTCSS); break; case DMRChannel::Admit::Free: setAdmit(Admit::Free); break; } setColorCode(dmr->colorCode()); setTimeSlot(dmr->timeSlot()); if (CommercialChannelExtension *ext = dmr->commercialExtension()) if ((! ext->encryptionKeyRef()->isNull()) && (0 <= ctx.index(ext->encryptionKey())) ) setEncryptionKeyIndex(ctx.index(ext->encryptionKey())); } else if (ch->is()) { FMChannel *fm = ch->as(); switch (fm->admit()) { case FMChannel::Admit::Always: setAdmit(Admit::Always); break; case FMChannel::Admit::Tone: setAdmit(Admit::CC_CTCSS); break; case FMChannel::Admit::Free: setAdmit(Admit::Free); break; } setBandwidth(fm->bandwidth()); setRXTone(fm->rxTone()); setTXTone(fm->txTone()); } return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::ZoneBankElement * ********************************************************************************************* */ GD73Codeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr) : Element(ptr, ZoneBankElement::size()) { // pass... } bool GD73Codeplug::ZoneBankElement::createZones(Context &ctx, const ErrorStack &err) { unsigned int count = std::min((unsigned int)getUInt8(Offset::zoneCount()), Limit::zoneCount()); for (unsigned int i=0; izones()->add(zoneObj); ctx.add(zoneObj, i); } return true; } bool GD73Codeplug::ZoneBankElement::linkZones(Context &ctx, const ErrorStack &err) { unsigned int count = std::min(ctx.count(), Limit::zoneCount()); for (unsigned int i=0; i(i); if (! zone.linkZone(zoneObj, ctx, err)) { errMsg(err) << "Cannot link zone at index " << i << "."; return false; } } return true; } bool GD73Codeplug::ZoneBankElement::encode(Context &ctx, const ErrorStack &err) { unsigned int count = std::min(ctx.count(), Limit::zoneCount()); setUInt8(Offset::zoneCount(), count); for (unsigned int i=0; i(i); if (! zone.encode(zoneObj, ctx, err)) { errMsg(err) << "Cannot encode zone at index " << i << "."; return false; } } return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::ZoneElement * ********************************************************************************************* */ GD73Codeplug::ZoneElement::ZoneElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::ZoneElement::ZoneElement(uint8_t *ptr) : Element(ptr, ZoneElement::size()) { // pass... } QString GD73Codeplug::ZoneElement::name() const { return readUnicode(Offset::name(), Limit::nameLength(), 0x0000); } void GD73Codeplug::ZoneElement::setName(const QString &name) { writeUnicode(Offset::name(), name, Limit::nameLength(), 0x0000); } Zone * GD73Codeplug::ZoneElement::toZone(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); return new Zone(name()); } bool GD73Codeplug::ZoneElement::linkZone(Zone *zone, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) unsigned int count = std::min((unsigned int)getUInt8(Offset::channeCount()), Limit::channelCount()); for (unsigned int i=0; i(index-1)) zone->A()->add(ctx.get(index-1)); else logWarn() << "Cannot link zone '" << zone->name() << "': Channel at index " << (index-1) << " not known."; } return true; } bool GD73Codeplug::ZoneElement::encode(Zone *zone, Context &ctx, const ErrorStack &err) { unsigned int count = std::min((unsigned int)zone->A()->count(), Limit::channelCount()); setUInt8(Offset::channeCount(), count); setName(zone->name()); for (unsigned int i=0; i ctx.index(zone->A()->get(i)->as())) { errMsg(err) << "Cannot find index for channel " << zone->A()->get(i)->name() << " in zone " << zone->name() << "."; return false; } setUInt16_le(Offset::channelIndices() + i*Offset::betweenChannelIndices(), ctx.index(zone->A()->get(i)->as())+1); } return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::ScanListBankElement * ********************************************************************************************* */ GD73Codeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr) : Element(ptr, ScanListBankElement::size()) { // pass... } bool GD73Codeplug::ScanListBankElement::createScanLists(Context &ctx, const ErrorStack &err) { unsigned int count = std::min((unsigned int)getUInt8(Offset::memberCount()), Limit::memberCount()); for (unsigned int i=0; iscanlists()->add(lstObj); ctx.add(lstObj, i); } return true; } bool GD73Codeplug::ScanListBankElement::linkScanLists(Context &ctx, const ErrorStack &err) { unsigned int count = std::min(ctx.count(), Limit::memberCount()); for (unsigned int i=0; i(i); if (! lst.linkScanList(lstObj, ctx, err)) { errMsg(err) << "Cannot link scan list at index " << i << "."; return false; } } return true; } bool GD73Codeplug::ScanListBankElement::encode(Context &ctx, const ErrorStack &err) { unsigned int count = std::min(ctx.count(), Limit::memberCount()); setUInt8(Offset::memberCount(), count); for (unsigned int i=0; i(i); if (! lst.encode(lstObj, ctx, err)) { errMsg(err) << "Cannot encode scan list at index " << i << "."; return false; } } return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug::ScanListElement * ********************************************************************************************* */ GD73Codeplug::ScanListElement::ScanListElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } GD73Codeplug::ScanListElement::ScanListElement(uint8_t *ptr) : Element(ptr, ScanListElement::size()) { // pass... } QString GD73Codeplug::ScanListElement::name() const { return readUnicode(Offset::name(), Limit::nameLength(), 0x0000); } void GD73Codeplug::ScanListElement::setName(const QString &name) { writeUnicode(Offset::name(), name, Limit::nameLength(), 0x0000); } GD73Codeplug::ScanListElement::ChannelMode GD73Codeplug::ScanListElement::primaryChannelMode() const { return (ChannelMode)getUInt8(Offset::priChannel1Mode()); } void GD73Codeplug::ScanListElement::setPrimaryChannelMode(ChannelMode mode) { setUInt8(Offset::priChannel1Mode(), (unsigned int)mode); } bool GD73Codeplug::ScanListElement::hasPrimaryZoneIndex() const { return 0 != getUInt8(Offset::priChannel1Zone()); } unsigned int GD73Codeplug::ScanListElement::primaryZoneIndex() const { return getUInt8(Offset::priChannel1Zone())-1; } void GD73Codeplug::ScanListElement::setPrimaryZoneIndex(unsigned int idx) { setUInt8(Offset::priChannel1Zone(), idx+1); } void GD73Codeplug::ScanListElement::clearPrimaryZoneIndex() { setUInt8(Offset::priChannel1Zone(), 0); } bool GD73Codeplug::ScanListElement::hasPrimaryChannelIndex() const { return 0 != getUInt8(Offset::priChannel1Channel()); } unsigned int GD73Codeplug::ScanListElement::primaryChannelIndex() const { return getUInt8(Offset::priChannel1Channel())-1; } void GD73Codeplug::ScanListElement::setPrimaryChannelIndex(unsigned int idx) { setUInt8(Offset::priChannel1Channel(), idx+1); } void GD73Codeplug::ScanListElement::clearPrimaryChannelIndex() { setUInt8(Offset::priChannel1Channel(), 0); } GD73Codeplug::ScanListElement::ChannelMode GD73Codeplug::ScanListElement::secondaryChannelMode() const { return (ChannelMode)getUInt8(Offset::priChannel2Mode()); } void GD73Codeplug::ScanListElement::setSecondaryChannelMode(ChannelMode mode) { setUInt8(Offset::priChannel2Mode(), (unsigned int)mode); } bool GD73Codeplug::ScanListElement::hasSecondaryZoneIndex() const { return 0 != getUInt8(Offset::priChannel2Zone()); } unsigned int GD73Codeplug::ScanListElement::secondaryZoneIndex() const { return getUInt8(Offset::priChannel2Zone())-1; } void GD73Codeplug::ScanListElement::setSecondaryZoneIndex(unsigned int idx) { setUInt8(Offset::priChannel2Zone(), idx+1); } void GD73Codeplug::ScanListElement::clearSecondaryZoneIndex() { setUInt8(Offset::priChannel2Zone(), 0); } bool GD73Codeplug::ScanListElement::hasSecondaryChannelIndex() const { return 0 != getUInt8(Offset::priChannel2Channel()); } unsigned int GD73Codeplug::ScanListElement::secondaryChannelIndex() const { return getUInt8(Offset::priChannel2Channel())-1; } void GD73Codeplug::ScanListElement::setSecondaryChannelIndex(unsigned int idx) { setUInt8(Offset::priChannel2Channel(), idx+1); } void GD73Codeplug::ScanListElement::clearSecondaryChannelIndex() { setUInt8(Offset::priChannel2Channel(), 0); } GD73Codeplug::ScanListElement::ChannelMode GD73Codeplug::ScanListElement::revertChannelMode() const { return (ChannelMode)getUInt8(Offset::txChannelMode()); } void GD73Codeplug::ScanListElement::setRevertChannelMode(ChannelMode mode) { setUInt8(Offset::txChannelMode(), (unsigned int)mode); } bool GD73Codeplug::ScanListElement::hasRevertZoneIndex() const { return 0 != getUInt8(Offset::txChannelZone()); } unsigned int GD73Codeplug::ScanListElement::revertZoneIndex() const { return getUInt8(Offset::txChannelZone())-1; } void GD73Codeplug::ScanListElement::setRevertZoneIndex(unsigned int idx) { setUInt8(Offset::txChannelZone(), idx+1); } void GD73Codeplug::ScanListElement::clearRevertZoneIndex() { setUInt8(Offset::txChannelZone(), 0); } bool GD73Codeplug::ScanListElement::hasRevertChannelIndex() const { return 0 != getUInt8(Offset::txChannelChannel()); } unsigned GD73Codeplug::ScanListElement::revertChannelIndex() const { return getUInt8(Offset::txChannelChannel())-1; } void GD73Codeplug::ScanListElement::setRevertChannelIndex(unsigned int idx) { setUInt8(Offset::txChannelChannel(), idx+1); } void GD73Codeplug::ScanListElement::clearRevertChannelIndex() { setUInt8(Offset::txChannelChannel(), 0); } Interval GD73Codeplug::ScanListElement::rxHoldTime() const { return Interval::fromMilliseconds(500*getUInt8(Offset::holdTime())); } void GD73Codeplug::ScanListElement::setRXHoldTime(const Interval &interval) { setUInt8(Offset::holdTime(), Limit::holdTime().map(interval).milliseconds()/500); } Interval GD73Codeplug::ScanListElement::txHoldTime() const { return Interval::fromMilliseconds(500*getUInt8(Offset::txHoldTime())); } void GD73Codeplug::ScanListElement::setTXHoldTime(const Interval &interval) { setUInt8(Offset::txHoldTime(), Limit::holdTime().map(interval).milliseconds()/500); } ScanList * GD73Codeplug::ScanListElement::toScanList(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); return new ScanList(name()); } bool GD73Codeplug::ScanListElement::linkScanList(ScanList *lst, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) if ((ChannelMode::Fixed == primaryChannelMode()) && hasPrimaryChannelIndex()) { if (ctx.has(primaryChannelIndex())) lst->setPrimaryChannel(ctx.get(primaryChannelIndex())); else logWarn() << "Cannot link scan list '" << lst->name() << "': Cannot resolve primary channel index " << primaryChannelIndex() << "."; } else if (ChannelMode::Selected == primaryChannelMode()) { lst->setPrimaryChannel(SelectedChannel::get()); } if ((ChannelMode::Fixed == secondaryChannelMode()) && hasSecondaryChannelIndex()) { if (ctx.has(secondaryChannelIndex())) lst->setSecondaryChannel(ctx.get(secondaryChannelIndex())); else logWarn() << "Cannot link scan list '" << lst->name() << "': Cannot resolve secondary channel index " << secondaryChannelIndex() << "."; } else if (ChannelMode::Selected == secondaryChannelMode()) { lst->setSecondaryChannel(SelectedChannel::get()); } if ((ChannelMode::Fixed == revertChannelMode()) && hasRevertChannelIndex()) { if (ctx.has(revertChannelIndex())) lst->setRevertChannel(ctx.get(revertChannelIndex())); else logWarn() << "Cannot link scan list '" << lst->name() << "': Cannot resolve revert channel index " << revertChannelIndex() << "."; } else if (ChannelMode::Selected == revertChannelMode()) { lst->setRevertChannel(SelectedChannel::get()); } unsigned int count = std::min((unsigned int)getUInt8(Offset::memberCount()), Limit::memberCount()); for (unsigned int i=0; i(index-1)) lst->addChannel(ctx.get(index-1)); else logWarn() << "Cannot link scan list '" << lst->name() << "': Cannot resolve member index" << index-1 << "."; } return true; } bool GD73Codeplug::ScanListElement::encode(ScanList *lst, Context &ctx, const ErrorStack &err) { Q_UNUSED(err); setName(lst->name()); if (! lst->primary()->isNull()) { if (SelectedChannel::get() == lst->primaryChannel()) { setPrimaryChannelMode(ChannelMode::Selected); } else { setPrimaryChannelMode(ChannelMode::Fixed); setPrimaryChannelIndex(ctx.index(lst->primaryChannel())); } } if (! lst->secondary()->isNull()) { if (SelectedChannel::get() == lst->secondaryChannel()) { setSecondaryChannelMode(ChannelMode::Selected); } else { setSecondaryChannelMode(ChannelMode::Fixed); setSecondaryChannelIndex(ctx.index(lst->secondaryChannel())); } } if (! lst->revert()->isNull()) { if (SelectedChannel::get() == lst->revertChannel()) { setRevertChannelMode(ChannelMode::Selected); } else { setRevertChannelMode(ChannelMode::Fixed); setRevertChannelIndex(ctx.index(lst->revertChannel())); } } unsigned int count = std::min((unsigned int)lst->count(), Limit::memberCount()); setUInt8(Offset::memberCount(), count); for (unsigned int i=0; ichannel(i))+1); } return true; } /* ********************************************************************************************* * * Implementation of GD73Codeplug * ********************************************************************************************* */ GD73Codeplug::GD73Codeplug(QObject *parent) : Codeplug{parent} { addImage("Radioddity GD-73A/E codeplug"); image(0).addElement(0x000000, 0x22014); } Config * GD73Codeplug::preprocess(Config *config, const ErrorStack &err) const { Config *copy = Codeplug::preprocess(config, err); if (nullptr == copy) { errMsg(err) << "Cannot pre-process codeplug for GD73A/E."; return nullptr; } ZoneSplitVisitor splitter; if (! splitter.process(copy, err)) { errMsg(err) << "Cannot pre-process codeplug for GD73A/E."; return nullptr; } return copy; } bool GD73Codeplug::postprocess(Config *config, const ErrorStack &err) const { if (! Codeplug::postprocess(config, err)) { errMsg(err) << "Cannot post-process codeplug for GD73A/E."; return false; } ZoneMergeVisitor merger; if (! merger.process(config, err)) { errMsg(err) << "Cannot post-process codeplug for GD73A/E."; return false; } return true; } bool GD73Codeplug::index(Config *config, Context &ctx, const ErrorStack &err) const { // There must be a default DMR radio ID. if (nullptr == ctx.config()->settings()->defaultId()) { errMsg(err) << "No default DMR radio ID specified."; errMsg(err) << "Cannot index codplug for encoding for the Radioddity GD73."; return false; } // Map radio IDs for (int i=0; iradioIDs()->count(); i++) ctx.add(ctx.config()->radioIDs()->getId(i), i); // Map digital and DTMF contacts for (int i=0, d=0, a=0; icontacts()->count(); i++) { if (ctx.config()->contacts()->contact(i)->is()) { ctx.add(ctx.config()->contacts()->contact(i)->as(), d); d++; } else if (ctx.config()->contacts()->contact(i)->is()) { ctx.add(ctx.config()->contacts()->contact(i)->as(), a); a++; } } // Map rx group lists for (int i=0; irxGroupLists()->count(); i++) ctx.add(ctx.config()->rxGroupLists()->list(i), i); // Map channels for (int i=0; ichannelList()->count(); i++) { if (ctx.config()->channelList()->get(i)->is() || ctx.config()->channelList()->get(i)->is()) ctx.add(ctx.config()->channelList()->channel(i), i); } // Map zones for (int i=0; izones()->count(); i++) ctx.add(config->zones()->zone(i), i); // Map scan lists for (int i=0; iscanlists()->count(); i++) ctx.add(config->scanlists()->scanlist(i), i); // Handle encryption keys) if (nullptr != config->commercialExtension()) { for (int i=0, j=0; icommercialExtension()->encryptionKeys()->count(); i++) { EncryptionKey *key = config->commercialExtension()->encryptionKeys()->key(i); // Can only encode basic encryption keys if (! key->is()) continue; ctx.add(key->as(), j++); } } return true; } bool GD73Codeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) { Q_UNUSED(flags); Context ctx(config); ctx.addTable(&BasicEncryptionKey::staticMetaObject); if (! index(config, ctx, err)) { errMsg(err) << "Cannot encode codeplug for Radioddity GD73."; return false; } if (! encodeTimestamp(ctx, err)) { errMsg(err) << "Cannot encode codeplug for Radioddity GD73."; return false; } if (! encodeMessages(ctx, err)) { errMsg(err) << "Cannot encode codeplug for Radioddity GD73."; return false; } if (! encodeSettings(ctx, err)) { errMsg(err) << "Cannot encode codeplug for Radioddity GD73."; return false; } if (! encodeContacts(ctx, err)) { errMsg(err) << "Cannot encode codeplug for Radioddity GD73."; return false; } if (! encodeGroupLists(ctx, err)) { errMsg(err) << "Cannot encode codeplug for Radioddity GD73."; return false; } if (! encodeEncryptionKeys(ctx, err)) { errMsg(err) << "Cannot encode encryption keys for Radioddity GD73."; return false; } if (! encodeChannels(ctx, err)) { errMsg(err) << "Cannot encode codeplug for Radioddity GD73."; return false; } if (! encodeZones(ctx, err)) { errMsg(err) << "Cannot encode codeplug for Radioddity GD73."; return false; } if (! encodeScanLists(ctx, err)) { errMsg(err) << "Cannot encode codeplug for Radioddity GD73."; return false; } return true; } bool GD73Codeplug::decode(Config *config, const ErrorStack &err) { Context ctx(config); ctx.addTable(&BasicEncryptionKey::staticMetaObject); if (! decodeTimestamp(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } if (! createMessages(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } if (! decodeSettings(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } if (! createContacts(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } if (! createDTMFContacts(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } if (! createGroupLists(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } if (! createEncryptionKeys(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } if (! createChannels(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } if (! createZones(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } if (! createScanLists(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } if (! linkGroupLists(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } if (! linkChannels(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } if (! linkZones(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } if (! linkScanLists(ctx, err)) { errMsg(err) << "Cannot decode codeplug."; return false; } return true; } bool GD73Codeplug::decodeTimestamp(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) InformationElement info(data(Offset::timestamp())); if (! info.isValid()) { errMsg(err) << "Cannot parse info element @" << Qt::hex << Offset::timestamp() << "."; return false; } // Nothing to do here. return true; } bool GD73Codeplug::encodeTimestamp(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err) InformationElement info(data(Offset::timestamp())); info.setTimestamp(QDateTime::currentDateTimeUtc()); info.setFrequencyRange(FrequencyRange {Frequency::fromMHz(400.0), Frequency::fromMHz(470.0)}); return true; } bool GD73Codeplug::createMessages(Context &ctx, const ErrorStack &err) { if (! MessageBankElement(data(Offset::messages())).decode(ctx.config()->smsExtension())) { errMsg(err) << "Cannot decode preset text messages."; return false; } return true; } bool GD73Codeplug::encodeMessages(Context &ctx, const ErrorStack &err) { if (! MessageBankElement(data(Offset::messages())).encode(ctx.config()->smsExtension())) { errMsg(err) << "Cannot encode preset text messages."; return false; } return true; } bool GD73Codeplug::decodeSettings(Context &ctx, const ErrorStack &err) { SettingsElement settings(data(Offset::settings())); if (! settings.updateConfig(ctx, err)) { errMsg(err) << "Cannot decode settings element."; return false; } DMRSettingsElement dmrSettings(data(Offset::dmrSettings())); if (! dmrSettings.updateConfig(ctx, err)) { errMsg(err) << "Cannot decode DMR settings element."; return false; } return true; } bool GD73Codeplug::encodeSettings(Context &ctx, const ErrorStack &err) { SettingsElement settings(data(Offset::settings())); if (! settings.encode(ctx, err)) { errMsg(err) << "Cannot encode settings element."; return false; } DMRSettingsElement dmrSettings(data(Offset::dmrSettings())); if (! dmrSettings.encode(ctx, err)) { errMsg(err) << "Cannot encode DMR settings element."; return false; } return true; } bool GD73Codeplug::createContacts(Context &ctx, const ErrorStack &err) { ContactBankElement bank(data(Offset::contacts())); if (! bank.createContacts(ctx, err)) { errMsg(err) << "Cannot create contacts."; return false; } return true; } bool GD73Codeplug::encodeContacts(Context &ctx, const ErrorStack &err) { ContactBankElement bank(data(Offset::contacts())); if (! bank.encode(ctx, err)) { errMsg(err) << "Cannot encode contacts."; return false; } return true; } bool GD73Codeplug::createDTMFContacts(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); return true; } bool GD73Codeplug::createGroupLists(Context &ctx, const ErrorStack &err) { if (! GroupListBankElement(data(Offset::groupLists())).createGroupLists(ctx, err)) { errMsg(err) << "Cannot decode group lists."; return false; } return true; } bool GD73Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) { if (! GroupListBankElement(data(Offset::groupLists())).linkGroupLists(ctx, err)) { errMsg(err) << "Cannot link group lists."; return false; } return true; } bool GD73Codeplug::encodeGroupLists(Context &ctx, const ErrorStack &err) { if (! GroupListBankElement(data(Offset::groupLists())).encode(ctx, err)) { errMsg(err) << "Cannot encode group lists."; return false; } return true; } bool GD73Codeplug::createEncryptionKeys(Context &ctx, const ErrorStack &err) { if (! EncryptionKeyBankElement(data(Offset::encryptionKeys())).createEncryptionKeys(ctx, err)) { errMsg(err) << "Cannot create encryption keys."; return false; } return true; } bool GD73Codeplug::encodeEncryptionKeys(Context &ctx, const ErrorStack &err) { if (! EncryptionKeyBankElement(data(Offset::encryptionKeys())).encodeEncryptionKeys(ctx, err)) { errMsg(err) << "Cannot encode encryption keys."; return false; } return true; } bool GD73Codeplug::createChannels(Context &ctx, const ErrorStack &err) { if (! ChannelBankElement(data(Offset::channels())).createChannels(ctx, err)) { errMsg(err) << "Cannot create channels."; return false; } return true; } bool GD73Codeplug::linkChannels(Context &ctx, const ErrorStack &err) { if (! ChannelBankElement(data(Offset::channels())).linkChannels(ctx, err)) { errMsg(err) << "Cannot link channels."; return false; } return true; } bool GD73Codeplug::encodeChannels(Context &ctx, const ErrorStack &err) { if (! ChannelBankElement(data(Offset::channels())).encode(ctx, err)) { errMsg(err) << "Cannot encode channels."; return false; } return true; } bool GD73Codeplug::createZones(Context &ctx, const ErrorStack &err) { if (! ZoneBankElement(data(Offset::zones())).createZones(ctx, err)) { errMsg(err) << "Cannot create zones."; return false; } return true; } bool GD73Codeplug::linkZones(Context &ctx, const ErrorStack &err) { if (! ZoneBankElement(data(Offset::zones())).linkZones(ctx, err)) { errMsg(err) << "Cannot link zones."; return false; } return true; } bool GD73Codeplug::encodeZones(Context &ctx, const ErrorStack &err) { if (! ZoneBankElement(data(Offset::zones())).encode(ctx, err)) { errMsg(err) << "Cannot encode zones."; return false; } return true; } bool GD73Codeplug::createScanLists(Context &ctx, const ErrorStack &err) { if (! ScanListBankElement(data(Offset::scanLists())).createScanLists(ctx, err)) { errMsg(err) << "Cannot create scan-lists."; return false; } return true; } bool GD73Codeplug::linkScanLists(Context &ctx, const ErrorStack &err) { if (! ScanListBankElement(data(Offset::scanLists())).linkScanLists(ctx, err)) { errMsg(err) << "Cannot link scan-lists."; return false; } return true; } bool GD73Codeplug::encodeScanLists(Context &ctx, const ErrorStack &err) { if (! ScanListBankElement(data(Offset::scanLists())).encode(ctx, err)) { errMsg(err) << "Cannot encode scan-lists."; return false; } return true; } qdmr-0.12.3/lib/gd73_codeplug.hh000066400000000000000000002035671501654372000162750ustar00rootroot00000000000000#ifndef GD73CODEPLUG_HH #define GD73CODEPLUG_HH #include "codeplug.hh" #include "interval.hh" #include "ranges.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "channel.hh" #include "zone.hh" #include "radioddity_extensions.hh" class SMSTemplate; class SMSExtension; /** Represents, encodes and decodes the device specific codeplug for a Radioddity GD-73. * * * * * * * * * * * * * * * * * * *
      Start End Size Content
      First segment 0x00000-0x22014
      0x00000 0x00061 0x0061 Basic info, see * @c GD73Codeplug::InformationElement
      0x00061 0x0010b 0x00aa Radio settings, see * @c GD73Codeplug::SettingsElement
      0x0010b 0x00d4c 0x0c41 Zone bank, see * @c GD73Codeplug::ZoneBankElement
      0x00d4c 0x1254e 0x11802 Channel bank, see * @c GD73Codeplug::ChannelBankElement
      0x125ff 0x1c201 0x9c02 Contact bank, see * @c GD73Codeplug::ContactBankElement
      0x1c201 0x21310 0x510f Group list bank, see * @c GD73Codeplug::GroupListBankElement
      0x21310 0x21911 0x0601 Scan list bank, see * @c GD73Codeplug::ScanListBankElement
      0x21911 0x2191f 0x000e DMR settings, see * @c GD73Codeplug::DMRSettingsElement
      0x2191f 0x2196f 0x0050 16 encryption keys, see * @c GD73Codeplug::EncryptionKeyBankElement
      0x2196f 0x21e80 0x0511 Message bank, see * @c GD73Codeplug::MessageBankElement
      0x21e80 0x21e94 0x0014 4 DTMF systems, see * @c GD73Codeplug::DTMFSystemBankElement
      0x21e94 0x21f24 0x0090 16 DTMF numbers, see * @c GD73Codeplug::DTMFNumberBankElement
      0x21f24 0x21fc4 0x00a0 32 DTMF PTT settings, see * @c GD73Codeplug::DTMFPTTSettingBankElement
      0x21fc4 0x22014 0x0050 Unused, filled with 0x00
      * * @ingroup gd73 */ class GD73Codeplug : public Codeplug { Q_OBJECT public: /** Implements the information element. * * Memory representation of the element (size 000h bytes): * @verbinclude gd73_timestamp.txt */ class InformationElement: public Element { protected: /** Hidden constructor. */ InformationElement(uint8_t *ptr, size_t size); public: /** Constructor. */ InformationElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0061; } /** Returns the frequency range, supported by the radio. */ FrequencyRange frequencyRange() const; /** Overrides the frequency range settings. */ void setFrequencyRange(const FrequencyRange &range); /** Returns the timestamp of the last programming. */ QDateTime timestamp() const; /** Sets the timestamp of the last programming. */ void setTimestamp(const QDateTime ×tamp); /** Returns the serial number as a string. */ QString serial() const; /** Returns the model name. */ QString modelName() const; /** Returns the device id. */ QString deviceID() const; /** Returns the model number as a string. */ QString modelNumber() const; /** Returns the software version as a string. */ QString softwareVersion() const; public: /** Some limits. */ struct Limit { /** Maximum length of serial number. */ static constexpr unsigned int serial() { return 16; } /** Maximum length of model name. */ static constexpr unsigned int modelName() { return 16; } /** Maximum length of device id. */ static constexpr unsigned int deviceID() { return 16; } /** Maximum length of model number. */ static constexpr unsigned int modelNumber() { return 16; } /** Maximum length of software version. */ static constexpr unsigned int softwareVersion() { return 16; } }; protected: /** Internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int frequencyRange() { return 0x0000; } static constexpr unsigned int dateCentury() { return 0x0001; } static constexpr unsigned int dateYear() { return 0x0002; } static constexpr unsigned int dateMonth() { return 0x0003; } static constexpr unsigned int dateDay() { return 0x0004; } static constexpr unsigned int dateHour() { return 0x0005; } static constexpr unsigned int dateMinute() { return 0x0006; } static constexpr unsigned int dateSecond() { return 0x0007; } static constexpr unsigned int serial() { return 0x0011; } static constexpr unsigned int modelName() { return 0x0021; } static constexpr unsigned int deviceID() { return 0x0031; } static constexpr unsigned int modelNumber() { return 0x0041; } static constexpr unsigned int softwareVersion() { return 0x0051; } /// @endcond }; }; /** Implements one of the 5 one-touch settings elements. * * Memory representation of the element (size 0005h bytes): * @verbinclude gd73_one_touch_element.txt */ class OneTouchSettingElement: public Element { public: /** Possible one-touch actions. */ enum class Action { Call = 0, Message = 1 }; protected: /** Hidden constructor. */ OneTouchSettingElement(uint8_t *ptr, size_t size); public: /** Constructor. */ OneTouchSettingElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0005; } protected: /** Internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int contact() { return 0x0001; } static constexpr unsigned int action() { return 0x0003; } static constexpr unsigned int message() { return 0x0001; } /// @endcond }; }; /** Implements the radio settings. * * Memory representation within the binary codeplug (size: 00aah): * @verbinclude gd73_settings_element.txt. */ class SettingsElement: public Element { public: /** Possible channel display modes. */ enum class ChannelDisplayMode { Name = 0, Frequency = 1 }; /** Possible boot display modes. */ enum class BootDisplayMode { Off = 0, Text = 1, Image = 2, Both = 3 }; /** Possible programmable key function. */ struct KeyFunction { public: /** Encodes the given function. */ static uint8_t encode(RadioddityButtonSettingsExtension::Function func); /** Decodes the given function code. */ static RadioddityButtonSettingsExtension::Function decode(uint8_t code); protected: /** Possible function codes. */ enum Code { None=0, RadioEnable=1, RadioCheck=2, RadioDisable=3, PowerLevel=4, Monitor=5, EmergencyOn=6, EmergencyOff=7, ZoneSwitch=8, ToggleScan=9, ToggleVOX=10, OneTouch1=11, OneTouch2=12, OneTouch3=13, OneTouch4=14, OneTouch5=15, ToggleTalkaround=16, LoneWorker=17, TBST=18, CallSwell=19 }; }; /** Possible languages. */ enum class Language { Chinese=0, English=1 }; protected: /** Hidden constructor. */ SettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ SettingsElement(uint8_t *ptr); /** Returns the size of the settings element. */ static constexpr unsigned int size() { return 0x00aa; } /** Returns the radio name. */ QString name() const; /** Sets the radio name. */ void setName(const QString &name); /** Returns the radio ID. */ unsigned int dmrID() const; /** Sets the radio ID. */ void setDMRID(unsigned int id); /** Retruns the menu language. */ Language language() const; /** Sets the menu language. */ void setLanguage(Language lang); /** Returns the VOX level [0,10]. */ unsigned int vox() const; /** Sets the VOX level [0,10]. */ void setVOX(unsigned int level); /** Returns the squelch level [0,10]. */ unsigned int squelch() const; /** Sets the squelch level [0,10]. */ void setSquelch(unsigned int level); /** Returns @c true, if a transmit time-out is set. */ bool totIsSet() const; /** Returns the transmit time-out. */ Interval tot() const; /** Sets the transmit time-out. */ void setTOT(const Interval &interval); /** Disables transmit time-out. */ void clearTOT(); /** Returns @c true if the TX interrupt is enabled. */ bool txInterruptedEnabled() const; /** Enables/disables the TX interrupt. */ void enableTXInterrupt(bool enable); /** Returns @c true if power save is enabled. */ bool powerSaveEnabled() const; /** Enables/disables power save. */ void enablePowerSave(bool enable); /** Returns the power-save time-out. */ Interval powerSaveTimeout() const; /** Sets the power-save time-out. */ void setPowerSaveTimeout(const Interval &interval); /** Returns @c true, if the read lock is enabled. */ bool readLockEnabled() const; /** Enables/disables read lock. */ void enableReadLock(bool enable); /** Returns the read-lock pin (1-6 digits as ASCII). */ QString readLockPin() const; /** Sets the read-lock pin (1-6 digits as ASCII). */ void setReadLockPin(const QString &pin); /** Returns @c true, if the write lock is enabled. */ bool writeLockEnabled() const; /** Enables/disables write lock. */ void enableWriteLock(bool enable); /** Returns the write-lock pin (1-6 digits as ASCII). */ QString writeLockPin() const; /** Sets the write-lock pin (1-6 digits as ASCII). */ void setWriteLockPin(const QString &pin); /** Returns the channel display mode. */ ChannelDisplayMode channelDisplayMode() const; /** Sets the channel display mode. */ void setChannelDisplayMode(ChannelDisplayMode mode); /** Returns the DMR microphone gain [1,10]. */ unsigned int dmrMicGain() const; /** Sets the DMR microphone gain [1,10]. */ void setDMRMicGain(unsigned int gain); /** Returns the FM microphone gain [1,10]. */ unsigned int fmMicGain() const; /** Sets the FM microphone gain [1,10]. */ void setFMMicGain(unsigned int gain); /** Returns the lone-worker response time-out. */ Interval loneWorkerResponseTimeout() const; /** Sets the lone-worker response time-out. */ void setLoneWorkerResponseTimeout(const Interval &interval); /** Returns the lone-worker remind period. */ Interval loneWorkerRemindPeriod() const; /** Sets the lone-worker remind period. */ void setLoneWorkerRemindPeriod(const Interval &interval); /** Returns the boot display mode. */ BootDisplayMode bootDisplayMode() const; /** Sets the boot display mode. */ void setBootDisplayMode(BootDisplayMode mode); /** Returns the first line of the boot text. */ QString bootTextLine1() const; /** Sets the first line of the boot text. */ void setBootTextLine1(const QString &line); /** Returns the second line of the boot text. */ QString bootTextLine2() const; /** Sets the second line of the boot text. */ void setBootTextLine2(const QString &line); /** Returns @c true if the key tones are enabled. */ bool keyToneEnabled() const; /** Enables/disables the key tones. */ void enableKeyTone(bool enable); /** Returns the key-tone volume [0-13]. */ unsigned int keyToneVolume() const; /** Sets the key-tone volume. */ void setKeyToneVolume(unsigned int vol); /** Returns @c true if the low-battery warn tone is enabled. */ bool lowBatteryToneEnabled() const; /** Enables/disables the low-battery warn tone. */ void enableLowBatteryTone(bool enable); /** Returns the low-battery warn-tone volume [0-13]. */ unsigned int lowBatteryToneVolume() const; /** Sets the low-battery warn-tone volume. */ void setLowBatteryToneVolume(unsigned int vol); /** Returns the long-press duration. */ Interval longPressDuration() const; /** Sets the long-press duration. */ void setLongPressDuration(const Interval &interval); /** Long-press function of programmable key 1. */ RadioddityButtonSettingsExtension::Function keyFunctionLongPressP1() const; /** Sets the long-press function of the programmable key 1. */ void setKeyFunctionLongPressP1(RadioddityButtonSettingsExtension::Function function); /** Short-press function of programmable key 1. */ RadioddityButtonSettingsExtension::Function keyFunctionShortPressP1() const; /** Sets the short-press function of the programmable key 1. */ void setKeyFunctionShortPressP1(RadioddityButtonSettingsExtension::Function function); /** Long-press function of programmable key 2. */ RadioddityButtonSettingsExtension::Function keyFunctionLongPressP2() const; /** Sets the long-press function of the programmable key 2. */ void setKeyFunctionLongPressP2(RadioddityButtonSettingsExtension::Function function); /** Short-press function of programmable key 2. */ RadioddityButtonSettingsExtension::Function keyFunctionShortPressP2() const; /** Sets the short-press function of the programmable key 2. */ void setKeyFunctionShortPressP2(RadioddityButtonSettingsExtension::Function function); /** Returns the n-th one-touch setting. */ OneTouchSettingElement oneTouch(unsigned int n); /** Updates the given config. */ bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the settings from the given config */ bool encode(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits of the settings. */ struct Limit { /** Maximum name length. */ static constexpr unsigned int name() { return 16; } /** Transmit time-out range. */ static constexpr TimeRange tot() { return TimeRange{Interval::fromSeconds(20), Interval::fromSeconds(500)}; } /** Power-save timeout. */ static constexpr TimeRange powerSaveTimeout() { return TimeRange{Interval::fromSeconds(10), Interval::fromSeconds(60)}; } /** Maximum read/write lock pin size. */ static constexpr unsigned int pin() { return 6; } /** Lone-worker response time-out range. */ static constexpr TimeRange loneWorkerResponse() { return TimeRange{Interval::fromMinutes(1), Interval::fromMinutes(480)}; } /** Lone-worker remind period range. */ static constexpr TimeRange loneWorkerRemindPeriod() { return TimeRange{Interval::fromSeconds(10), Interval::fromSeconds(200)}; } /** Maximum length of the boot text lines. */ static constexpr unsigned int bootTextLine() { return 16; } /** Value range for tone-volumes. */ static constexpr IntRange toneVolume() { return IntRange{0,13}; } /** Long-press duration range. */ static constexpr TimeRange longPressDuration() { return TimeRange{Interval::fromSeconds(0), Interval::fromSeconds(31)}; } }; protected: /** Internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int dmrId() { return 0x0020; } static constexpr unsigned int language() { return 0x0024; } static constexpr unsigned int voxLevel() { return 0x0026; } static constexpr unsigned int squelchLevel() { return 0x0027; } static constexpr unsigned int tot() { return 0x0028; } static constexpr unsigned int txInterrupt() { return 0x0029; } static constexpr unsigned int powerSave() { return 0x002a; } static constexpr unsigned int powerSaveTimeout() { return 0x002b; } static constexpr unsigned int readLockEnable() { return 0x002c; } static constexpr unsigned int writeLockEnable() { return 0x002d; } static constexpr unsigned int channelDisplayMode() { return 0x002f; } static constexpr unsigned int readLockPin() { return 0x0030; } static constexpr unsigned int writeLockPin() { return 0x0036; } static constexpr unsigned int dmrMicGain() { return 0x003d; } static constexpr unsigned int fmMicGain() { return 0x003f; } static constexpr unsigned int loneWorkerResponseTimeout() { return 0x0040; } static constexpr unsigned int loneWorkerReminderPeriod() { return 0x0042; } static constexpr unsigned int bootDisplayMode() { return 0x0043; } static constexpr unsigned int bootTextLine1() { return 0x0044; } static constexpr unsigned int bootTextLine2() { return 0x0064; } static constexpr unsigned int keyToneEnable() { return 0x0084; } static constexpr unsigned int keyToneVolume() { return 0x0085; } static constexpr unsigned int lowBatToneEnable() { return 0x0086; } static constexpr unsigned int lowBatToneVolume() { return 0x0087; } static constexpr unsigned int longPressDuration() { return 0x0088; } static constexpr unsigned int progFuncKey1ShortPress() { return 0x008b; } static constexpr unsigned int progFuncKey1LongPress() { return 0x008c; } static constexpr unsigned int progFuncKey2ShortPress() { return 0x008d; } static constexpr unsigned int progFuncKey2LongPress() { return 0x008e; } static constexpr unsigned int oneTouchSettings() { return 0x0090; } static constexpr unsigned int betweenOneTouchSettings() { return OneTouchSettingElement::size(); } /// @endcond }; }; /** Implements a single zone within the binary codeplug. * * Memory representation of the zone (size 0031h): * @verbinclude gd73_zone_element.txt */ class ZoneElement: public Element { protected: /** Hidden constructor. */ ZoneElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ZoneElement(uint8_t *ptr); /** Returns the size of the zone element. */ static constexpr unsigned int size() { return 0x0031; } /** Returns the name of the zone. */ QString name() const; /** Sets the name of the zone. */ void setName(const QString &name); /** Decodes the zone element. */ Zone *toZone(Context &ctx, const ErrorStack &err=ErrorStack()); /** Links the decoded zone */ bool linkZone(Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the given zone. */ bool encode(Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for the element. */ struct Limit { /** Maximum name length. */ static constexpr unsigned int nameLength() { return 8; } /** Maximum number of channels per zone. */ static constexpr unsigned int channelCount() { return 16; } }; protected: /** Internal offsets within the zone element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int channeCount() { return 0x0010; } static constexpr unsigned int channelIndices() { return 0x0011; } static constexpr unsigned int betweenChannelIndices() { return 0x0002; } /// @endcond }; }; /** Implements the bank of zones. * * See also @c GD73Codeplug::ZoneElement. * * Memory representation of the zone bank (size ????h bytes): * @verbinclude gd73_zone_bank.txt */ class ZoneBankElement: public Element { protected: /** Hidden constructor. */ ZoneBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ZoneBankElement(uint8_t *ptr); /** Returns the size of the zone bank element. */ static constexpr unsigned int size() { return 0x0c41; } /** Creates all encoded zones, also updates the context. */ bool createZones(Context &ctx, const ErrorStack &err); /** Links all decoded zones. */ bool linkZones(Context &ctx, const ErrorStack &err); /** Encodess all zones. */ bool encode(Context &ctx, const ErrorStack &err); public: /** Some limits for the zone bank. */ struct Limit { /** Maximum number of zones. */ static constexpr unsigned int zoneCount() { return 64; } }; protected: /** Internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int zoneCount() { return 0x0000; } static constexpr unsigned int zones() { return 0x0001; } static constexpr unsigned int betweenZones() { return ZoneElement::size(); } /// @endcond }; }; /** Implements an FM/DMR channel. * * Memory representaion of the channel (size ): * @verbinclude gd73_channel_element.txt */ class ChannelElement: public Element { protected: /** Hidden constructor. */ ChannelElement(uint8_t *ptr, size_t size); public: /** Possible channel types. */ enum class Type { FM = 0, DMR = 1 }; /** Possible admit criteria. */ enum class Admit { Always = 0, CC_CTCSS = 1, Free=2 }; public: /** Constructor. */ ChannelElement(uint8_t *ptr); /** Returns the size of the channel element. */ static constexpr unsigned int size() { return 0x0046; } /** Returns the name of the channel. */ QString name() const; /** Sets the channel name. */ void setName(const QString &name); /** Returns the bandwidth of the channel. */ FMChannel::Bandwidth bandwidth() const; /** Sets the bandwidth. */ void setBandwidth(FMChannel::Bandwidth bandwidth); /** Returns @c true, if a scan list index is set. */ bool hasScanListIndex() const; /** Returns the index of the scan list. */ unsigned int scanListIndex() const; /** Sets the scan list index. */ void setScanListIndex(unsigned int idx); /** Clears the scan list index. */ void clearScanListIndex(); /** Returns the channel type. */ Type type() const; /** Sets the channel type. */ void setType(Type type); /** Returns @c true if talkaround is enabled. */ bool talkaroundEnabled() const; /** Enable/disable talkaround. */ void enableTalkaround(bool enable); /** Returns @c true if RX only is enabled. */ bool rxOnly() const; /** Enables/disables RX only. */ void enableRXOnly(bool enable); /** Returns @c true if scan auto-start is enabled. */ bool scanAutoStartEnabled() const; /** Enables/disables scan auto-start. */ void enableScanAutoStart(bool enable); /** Returns the RX frequency. */ Frequency rxFrequency() const; /** Sets the RX frequency. */ void setRXFrequency(const Frequency &f); /** Returns the TX frequency. */ Frequency txFrequency() const; /** Sets the TX frequency. */ void setTXFrequency(const Frequency &f); /** Returns @c true if channel has DTMF PTT settings index. */ bool hasDTMFPTTSettingsIndex() const; /** Returns the DTMF PTT settings index. */ unsigned int dtmfPTTSettingsIndex() const; /** Sets the DTMF PTT settings index. */ void setDTMFPTTSettingsIndex(unsigned int idx); /** Resets the DTMF PTT settings index. */ void clearDTMFPTTSettingsIndex(); /** Returns the power setting. */ Channel::Power power() const; /** Sets the power. */ void setPower(Channel::Power power); /** Returns the admit criterion. */ Admit admit() const; /** Sets the admit criterion. */ void setAdmit(Admit admit); /** Returns the RX tone. */ SelectiveCall rxTone() const; /** Sets the RX tone. */ void setRXTone(const SelectiveCall &code); /** Returns the TX tone. */ SelectiveCall txTone() const; /** Sets the TX tone. */ void setTXTone(const SelectiveCall &code); /** Returns the time slot. */ DMRChannel::TimeSlot timeSlot() const; /** Sets the time slot. */ void setTimeSlot(DMRChannel::TimeSlot ts); /** Returns the color code. */ unsigned int colorCode() const; /** Sets the color code. */ void setColorCode(unsigned int cc); /** Returns @c true, if group list matches current TX contact. */ bool groupListMatchesContact() const; /** Returns @c true, if no group list match is needed (monitor). */ bool groupListAllMatch() const; /** Returns the group list index. */ unsigned int groupListIndex() const; /** Sets the group list index. */ void setGroupListIndex(unsigned int idx); /** Enables, that no group list match is needed (monitor). */ void setGroupListAllMatch(); /** Enables, that the group list matches the current TX contact. */ void setGroupListMatchesContact(); /** Returns @c true, if the transmit contact is set. */ bool hasTXContact() const; /** Returns the tx contact index. */ unsigned int txContactIndex() const; /** Sets the transmit contact index. */ void setTXContactIndex(unsigned int idx); /** Clears the transmit contact index. */ void clearTXContactIndex(); /** Returns @c true if an emergency system index is set. */ bool hasEmergencySystemIndex() const; /** Returns the emergency system index. */ unsigned int emergencySystemIndex() const; /** Sets the emergency system index. */ void setEmergencySystemIndex(unsigned int idx); /** Clears the emergency system index. */ void clearEmergencySystemIndex(); /** Returns @c true if an encryption key index is set. */ bool hasEncryptionKeyIndex() const; /** Returns the encryption key index. */ unsigned int encryptionKeyIndex() const; /** Sets the encryption key index. */ void setEncryptionKeyIndex(unsigned int idx); /** Clears the encryption key index. */ void clearEncryptionKeyIndex(); /** Decodes the channel. */ Channel *toChannel(Context &ctx, const ErrorStack &err=ErrorStack()); /** Links decoded channel. */ bool linkChannel(Channel *ch, Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the given channel. */ bool encode(Channel *ch, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for the channel. */ struct Limit { /** Maximum name length. */ static constexpr unsigned int nameLength() { return 16; } }; protected: /** Internal used offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int bandwidth() { return 0x0020; } static constexpr unsigned int scanList() { return 0x0021; } static constexpr unsigned int channelType() { return 0x0022; } static constexpr unsigned int talkaround() { return 0x0023; } static constexpr unsigned int rxOnly() { return 0x0024; } static constexpr unsigned int scanAutoStart() { return 0x0026; } static constexpr unsigned int rxFrequency() { return 0x0027; } static constexpr unsigned int txFrequency() { return 0x002b; } static constexpr unsigned int dtmfPTTSettingsIndex() { return 0x002f; } static constexpr unsigned int power() { return 0x0030; } static constexpr unsigned int admid() { return 0x0031; } static constexpr unsigned int rxToneMode() { return 0x0034; } static constexpr unsigned int rxCTCSS() { return 0x0035; } static constexpr unsigned int rxDCS() { return 0x0036; } static constexpr unsigned int txToneMode() { return 0x0037; } static constexpr unsigned int txCTCSS() { return 0x0038; } static constexpr unsigned int txDCS() { return 0x0039; } static constexpr unsigned int timeslot() { return 0x003c; } static constexpr unsigned int colorcode() { return 0x003d; } static constexpr unsigned int groupListIndex() { return 0x003e; } static constexpr unsigned int contactIndex() { return 0x0040; } static constexpr unsigned int emergencySystemIndex() { return 0x0042; } static constexpr unsigned int encryptionKeyIndex() { return 0x0044; } /// @endcond }; }; /** Implements the bank of channels within the binary codeplug. * * See @c GD73Codeplug::ChannelElement for details on how the channels are encoded. * * Memory representation of the channel bank (size 11802h bytes): * @verbinclude gd73_channel_bank.txt */ class ChannelBankElement: public Element { protected: /** Hidden constructor. */ ChannelBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ChannelBankElement(uint8_t *ptr); /** Returns the size of the channel bank. */ static constexpr unsigned int size() { return 0x11802; } /** Creates the encoded channels, also updates context. */ bool createChannels(Context &ctx, const ErrorStack &err); /** Link all decoded channels. */ bool linkChannels(Context &ctx, const ErrorStack &err); /** Encodes all indexed channels. */ bool encode(Context &ctx, const ErrorStack &err); public: /** Some limits for the channel bank. */ struct Limit { /** Maximum number of channels. */ static constexpr unsigned int channelCount() { return 1024; } }; protected: /** Internal offsets within the bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int channelCount() { return 0x0000; } static constexpr unsigned int channels() { return 0x0002; } static constexpr unsigned int betweenChannels() { return ChannelElement::size(); } /// @endcond }; }; /** Implements the contact element. * * Memory representation of the contact (size 9c02h bytes): * @verbinclude gd73_contact_bank.txt */ class ContactElement: public Element { protected: /** Hidden constructor. */ ContactElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ContactElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x025; } /** Returns the name of the contact. */ QString name() const; /** Sets the name of the contact. */ void setName(const QString &name); /** Returns the contact type. */ DMRContact::Type type() const; /** Sets the contact type. */ void setType(DMRContact::Type type); /** Returns the DMR ID. */ unsigned int id() const; /** Sets the DMR ID. */ void setID(unsigned int id); /** Decodes the contact. */ DMRContact *toContact(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the given contact. */ bool encode(const DMRContact *contact, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit { /** The maximum name length. */ static constexpr unsigned int nameLength() { return 16; } }; protected: /** Some internal offsets within the contact. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int type() { return 0x0020; } static constexpr unsigned int id() { return 0x0021; } /// @endcond }; }; /** Implements the contact bank within the codeplug. * * See @c GD73Codeplug::ContactElement for contact encoding. * * Memory representation of the contact bank (size 9c02h bytes): * @verbinclude gd73_contact_bank.txt */ class ContactBankElement: public Element { protected: /** Hidden constructor. */ ContactBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ContactBankElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x9c02; } /** Adds all encoded contacts, also updates the context */ bool createContacts(Context &ctx, const ErrorStack &err); /** Encodes all defined contacts. */ bool encode(Context &ctx, const ErrorStack &err); public: /** Some limits. */ struct Limit { /** The maximum number of contacts. */ static constexpr unsigned int contactCount() { return 1024; } }; protected: /** Some internal offsets within the contact bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int contactCount() { return 0x0000; } static constexpr unsigned int contacts() { return 0x0802; } static constexpr unsigned int betweenContacts() { return ContactElement::size(); } /// @endcond }; }; /** Encodes a group list. * * Memory representation of the group list (size 0053h bytes): * @verbinclude gd73_group_list_element.txt */ class GroupListElement: public Element { protected: /** Hidden constructor. */ GroupListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ GroupListElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0053; } /** Returns the name of the group list. */ QString name() const; /** Sets the name of the group list. */ void setName(const QString &name); /** Returns the number of entries in the group list. */ unsigned int members() const; /** Returns @c true, if the i-th member is set. */ bool hasMember(unsigned int i) const; /** Returns the i-th member index. */ unsigned int memberIndex(unsigned int i) const; /** Decodes the group list. */ RXGroupList *toGroupList(Context &ctx, const ErrorStack &err=ErrorStack()); /** Links the given RX group list. */ bool linkGroupList(RXGroupList *lst, Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the group list. */ bool encode(RXGroupList *lst, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit { /** The maximum name length. */ static constexpr unsigned int nameLength() { return 8; } /** The maximum number of members. */ static constexpr unsigned int memberCount() { return 33; } }; protected: /** Some internal offsets within the group list. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int memberCount() { return 0x0010; } static constexpr unsigned int members() { return 0x0011; } static constexpr unsigned int betweenMembers() { return 0x0002; } /// @endcond }; }; /** Encodes the bank of group lists. * * See @c GD73Codeplug::GroupListElement for group list encoding. * * Memory representation of group list bank (size 510fh bytes): * @verbinclude gd73_group_list_bank.txt */ class GroupListBankElement: public Element { protected: /** Hidden constructor. */ GroupListBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ GroupListBankElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x510f; } /** Create all encoded group lists, also update context. */ bool createGroupLists(Context &ctx, const ErrorStack &err); /** Link all decoded group lists. */ bool linkGroupLists(Context &ctx, const ErrorStack &err); /** Encode group lists. */ bool encode(Context &ctx, const ErrorStack &err); public: /** Some limits. */ struct Limit { /** The maximum number of members. */ static constexpr unsigned int memberCount() { return 250; } }; protected: /** Some internal offsets within the group list bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int memberCount() { return 0x0000; } static constexpr unsigned int members() { return 0x0001; } static constexpr unsigned int betweenMembers() { return GroupListElement::size(); } /// @endcond }; }; /** Implements a scan list. * * Memory representation of the scan list (size 005fh bytes): * @verbinclude gd73_scan_list_element.txt */ class ScanListElement: public Element { public: /** Possible priority/revert channel modes. */ enum class ChannelMode { None=0, Fixed=1, Selected=2 }; protected: /** Hidden constructor. */ ScanListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ScanListElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x005f; } /** Returns the name of the scan list. */ QString name() const; /** Sets the name of the scan list. */ void setName(const QString &name); /** Returns the primary channel mode. */ ChannelMode primaryChannelMode() const; /** Sets the primary channel mode. */ void setPrimaryChannelMode(ChannelMode mode); /** Returns @c true, if a primary zone is set. */ bool hasPrimaryZoneIndex() const; /** Returns the primary zone index. */ unsigned int primaryZoneIndex() const; /** Sets the primary zone index. */ void setPrimaryZoneIndex(unsigned int idx); /** Clears the primary zone index. */ void clearPrimaryZoneIndex(); /** Returns @c true, if a primary channel is set. */ bool hasPrimaryChannelIndex() const; /** Returns the primary channel index. */ unsigned int primaryChannelIndex() const; /** Sets the primary channel index. */ void setPrimaryChannelIndex(unsigned int idx); /** Clears the primary channel index. */ void clearPrimaryChannelIndex(); /** Returns the secondary channel mode. */ ChannelMode secondaryChannelMode() const; /** Sets the secondary channel mode. */ void setSecondaryChannelMode(ChannelMode mode); /** Returns @c true, if a secondary zone is set. */ bool hasSecondaryZoneIndex() const; /** Returns the secondary zone index. */ unsigned int secondaryZoneIndex() const; /** Sets the secondary zone index. */ void setSecondaryZoneIndex(unsigned int idx); /** Clears the secondary zone index. */ void clearSecondaryZoneIndex(); /** Returns @c true, if a secondary channel is set. */ bool hasSecondaryChannelIndex() const; /** Returns the secondary channel index. */ unsigned int secondaryChannelIndex() const; /** Sets the secondary channel index. */ void setSecondaryChannelIndex(unsigned int idx); /** Clears the secondary channel index. */ void clearSecondaryChannelIndex(); /** Returns the revert channel mode. */ ChannelMode revertChannelMode() const; /** Sets the revert channel mode. */ void setRevertChannelMode(ChannelMode mode); /** Returns @c true, if a revert zone is set. */ bool hasRevertZoneIndex() const; /** Returns the revert zone index. */ unsigned int revertZoneIndex() const; /** Sets the revert zone index. */ void setRevertZoneIndex(unsigned int idx); /** Clears the revert zone index. */ void clearRevertZoneIndex(); /** Returns @c true, if a revert channel is set. */ bool hasRevertChannelIndex() const; /** Returns the revert channel index. */ unsigned int revertChannelIndex() const; /** Sets the revert channel index. */ void setRevertChannelIndex(unsigned int idx); /** Clears the revert channel index. */ void clearRevertChannelIndex(); /** Returns the RX hold time. */ Interval rxHoldTime() const; /** Sets the RX hold time. */ void setRXHoldTime(const Interval &interval); /** Returns the TX hold time. */ Interval txHoldTime() const; /** Sets the TX hold time. */ void setTXHoldTime(const Interval &interval); /** Constructs a ScanList from this elemet. */ ScanList *toScanList(Context &ctx, const ErrorStack &err=ErrorStack()); /** Links a decoded scan list. */ bool linkScanList(ScanList *lst, Context&ctx, const ErrorStack &err=ErrorStack()); /** Encodes the scan list. */ bool encode(ScanList *lst, Context&ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit { /** The maximum name length. */ static constexpr unsigned int nameLength() { return 8; } /** The maximum number of members. */ static constexpr unsigned int memberCount() { return 32; } /** The range of hold times. */ static TimeRange holdTime() { return TimeRange{Interval::fromSeconds(0), Interval::fromSeconds(10)}; } }; protected: /** Some internal offsets within the scan list bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int memberCount() { return 0x0010; } static constexpr unsigned int members() { return 0x0011; } static constexpr unsigned int betweenMembers() { return 0x0002; } static constexpr unsigned int priChannel1Mode() { return 0x0051; } static constexpr unsigned int priChannel2Mode() { return 0x0052; } static constexpr unsigned int priChannel1Zone() { return 0x0053; } static constexpr unsigned int priChannel2Zone() { return 0x0054; } static constexpr unsigned int priChannel1Channel() { return 0x0055; } static constexpr unsigned int priChannel2Channel() { return 0x0057; } static constexpr unsigned int txChannelMode() { return 0x0059; } static constexpr unsigned int txChannelZone() { return 0x005a; } static constexpr unsigned int txChannelChannel() { return 0x005b; } static constexpr unsigned int holdTime() { return 0x005d; } static constexpr unsigned int txHoldTime() { return 0x005e; } /// @endcond }; }; /** Implements the bank of scan lists. * * See @c GD73Codeplug::ScanListElement for the encoding of the single scan lists. * * Memory representation of the scan list bank (size 0601h bytes): * @verbinclude gd73_scan_list_bank.txt */ class ScanListBankElement: public Element { protected: /** Hidden constructor. */ ScanListBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ScanListBankElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x601; } /** Creates all encoded scan lists, also updates context. */ bool createScanLists(Context &ctx, const ErrorStack &err); /** Links all decoded scan lists. */ bool linkScanLists(Context &ctx, const ErrorStack &err); /** Encodes all scan lists. */ bool encode(Context &ctx, const ErrorStack &err); public: /** Some limits. */ struct Limit { /** The maximum number of members. */ static constexpr unsigned int memberCount() { return 16; } }; protected: /** Some internal offsets within the scan list bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int memberCount() { return 0x0000; } static constexpr unsigned int members() { return 0x0011; } static constexpr unsigned int betweenMembers() { return ScanListElement::size(); } /// @endcond }; }; /** Implements the DMR settings element. * * Memory representation of the settings (size 000eh bytes): * @verbinclude gd73_dmr_settings_element.txt */ class DMRSettingsElement: public Element { protected: /** Hidden constructor. */ DMRSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DMRSettingsElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x000e; } /** Returns the call hang time (private & group). */ Interval callHangTime() const; /** Sets the call hang time (private & group). */ void setCallHangTime(const Interval &intv); /** Returns the active wait time. */ Interval activeWaitTime() const; /** Sets the active wait time. */ void setActiveWaitTime(const Interval &interval); /** Retruns the number of active reties. */ unsigned int activeRetries() const; /** Sets the number of active retries. */ void setActiveRetries(unsigned int count); /** Retruns the number of TX preambles. */ unsigned int txPreambles() const; /** Sets the number of TX preambles. */ void setTXPreambles(unsigned int count); /** Returns @c true, if decoding of 'disable radio' is enabled. */ bool decodeDisableRadioEnabled() const; /** Enables/disables decoding of 'disable radio'. */ void enableDecodeDisableRadio(bool enable); /** Returns @c true, if decoding of 'radio check' is enabled. */ bool decodeRadioCheckEnabled() const; /** Enables/disables decoding of 'radio check'. */ void enableDecodeRadioCheck(bool enable); /** Returns @c true, if decoding of 'enable radio' is enabled. */ bool decodeEnableRadioEnabled() const; /** Enables/disables decoding of 'enable radio'. */ void enableDecodeEnableRadio(bool enable); /** Updates the settings within the config. */ bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the settings from the given config */ bool encode(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit { /** The range of call hang times. */ static constexpr TimeRange callHangTime() { return TimeRange{ Interval::fromSeconds(1), Interval::fromSeconds(90) }; } /** The range of active wait times. */ static constexpr TimeRange activeWaitTime() { return TimeRange{ Interval::fromMilliseconds(120), Interval::fromMilliseconds(600) }; } /** The range of active retries. */ static constexpr IntRange activeRetires() { return IntRange{ 1, 10}; } /** The maximum number of TX preambles. */ static constexpr IntRange txPreambles() { return IntRange{ 0, 63}; } }; protected: /** Some internal offsets within the scan list bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int callHangTime() { return 0x0000; } static constexpr unsigned int activeWaitTime() { return 0x0001; } static constexpr unsigned int activeRetries() { return 0x0002; } static constexpr unsigned int txPreambles() { return 0x0003; } static constexpr unsigned int decodeDisableRadio() { return 0x0004; } static constexpr unsigned int decodeCheckRadio() { return 0x0005; } static constexpr unsigned int decodeEnableRadio() { return 0x0006; } /// @endcond }; }; /** Implements the encryption key element. * * Memory representation (size 0005h bytes): * @verbinclude gd73_encryption_key_element.txt */ class EncryptionKeyElement: public Element { protected: /** Hidden constructor. */ EncryptionKeyElement(uint8_t *ptr, size_t size); public: /** Constructor. */ EncryptionKeyElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x005; } void clear(); bool isValid() const; /** Returns the key size in bits. */ unsigned int keySize() const; /** Sets the key size in bits. */ void setKeySize(unsigned int size); /** Decodes the encryption key. */ BasicEncryptionKey *createEncryptionKey(const ErrorStack &err=ErrorStack()) const; /** Encodes the encryption key. */ bool encodeEncryptionKey(BasicEncryptionKey *key, const ErrorStack &err=ErrorStack()); protected: /** Internal used offsets within the bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int size() { return 0x0000; } static constexpr unsigned int key() { return 0x0001; } /// @endcond }; }; /** Implements the encryption key-bank. * * Memory representation (size 0050h bytes): * @verbinclude gd73_encryption_key_bank.txt */ class EncryptionKeyBankElement: public Element { protected: /** Hidden constructor. */ EncryptionKeyBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ EncryptionKeyBankElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0050; } /** Decodes and create encryption keys. */ bool createEncryptionKeys(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes encryption keys. */ bool encodeEncryptionKeys(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit { /** The maximum number of keys. */ static constexpr unsigned int keys() { return 16; } }; protected: /** Internal used offsets within the bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int keys() { return 0x0000; } static constexpr unsigned int betweenKeys() { return EncryptionKeyElement::size(); } /// @endcond }; }; /** Implements a message. * Memory representation (size 0051h bytes): * @verbinclude gd73_message_element.txt */ class MessageElement: public Element { protected: /** Hidden constructor. */ MessageElement(uint8_t *ptr, size_t size); public: /** Constructor. */ MessageElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x051; } /** Returns the message text. */ QString text() const; /** Set message text. */ void setText(const QString &message); /** Sets a message element from an SMS message. */ bool encode(SMSTemplate *message, const ErrorStack &err=ErrorStack()); /** Creates a SMS template from this message. */ SMSTemplate *decode(const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit { /** The maximum message length. */ static constexpr unsigned int messageLength() { return 40; } }; protected: /** Internal used offsets within the bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int size() { return 0x0000; } static constexpr unsigned int text() { return 0x0001; } /// @endcond }; }; /** Implements the message bank element. * * See @c GD73Codeplug::MessageElement for encoding of the single messages. * * Memory representation of the bank (size 0511h bytes): * @verbinclude gd73_message_bank.txt */ class MessageBankElement: public Element { protected: /** Hidden constructor. */ MessageBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ MessageBankElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x511; } /** Returns the member count. */ unsigned int memberCount() const; /** Sets the member count. */ void setMemberCount(unsigned int count); /** Returns the i-th message. */ MessageElement message(unsigned int i); /** Updates the SMS extension by decoding all defined messages. */ bool decode(SMSExtension *ext, const ErrorStack &err=ErrorStack()); /** Encodes all messages defined within the SMS extension. */ bool encode(const SMSExtension *ext, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit { /** The maximum number of members. */ static constexpr unsigned int memberCount() { return 16; } }; protected: /** Some internal offsets within the message bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int memberCount() { return 0x0000; } static constexpr unsigned int members() { return 0x0001; } static constexpr unsigned int betweenMembers() { return MessageElement::size(); } /// @endcond }; }; /** Implements a single DTMF system. * Memory representation of the system (size 0005h bytes): * @verbinclude gd73_dtmf_system_element.txt */ class DTMFSystemElement: public Element { protected: /** Hidden constructor. */ DTMFSystemElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DTMFSystemElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x005; } public: /** Some limits. */ struct Limit { /** Maximum preamble duration in ms. */ static constexpr Interval preambleDuration() { return Interval::fromMilliseconds(1000); } /** Range for tone duration. */ static constexpr TimeRange toneDuration() { return TimeRange{ Interval::fromMilliseconds(30), Interval::fromMilliseconds(1900) }; } /** Range for pause duration. */ static constexpr TimeRange pauseDuration() { return TimeRange{ Interval::fromMilliseconds(30), Interval::fromMilliseconds(1900) }; } /** Rang of dead time. */ static constexpr TimeRange deadTime() { return TimeRange{ Interval::fromMilliseconds(200), Interval::fromSeconds(33) }; } }; protected: /** Internal used offsets within the bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int sidetone() { return 0x0000; } static constexpr unsigned int preambleDuration() { return 0x0001; } static constexpr unsigned int toneDuration() { return 0x0002; } static constexpr unsigned int pauseDuration() { return 0x0003; } static constexpr unsigned int deadTime() { return 0x0004; } /// @endcond }; }; /** Implements the bank of 4 DTMF systems. * @c GD73Codeplug::DTMFSystemElement for encoding of each system. */ class DTMFSystemBankElement: public Element { protected: /** Hidden constructor. */ DTMFSystemBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DTMFSystemBankElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0014; } public: /** Some limits. */ struct Limit { /** The number of members. */ static constexpr unsigned int memberCount() { return 4; } }; protected: /** Some internal offsets within the message bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int members() { return 0x0000; } static constexpr unsigned int betweenMembers() { return DTMFSystemElement::size(); } /// @endcond }; }; /** Implements a single DTMF number. * Memory representation of the DTMF number (size 000ah bytes): * @verbinclude gd73_dtmf_code_element.txt */ class DTMFNumberElement: public Element { protected: /** Hidden constructor. */ DTMFNumberElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DTMFNumberElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x000a; } public: /** Some limits. */ struct Limit { /** Maximum number of digita. */ static constexpr unsigned int digits() { return 16; } }; protected: /** Internal used offsets within the bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int count() { return 0x0000; } static constexpr unsigned int digits() { return 0x0001; } /// @endcond }; }; /** Implements the bank of 16 DTMF numbers. * @c GD73Codeplug::DTMFNumberElement for encoding of each system. */ class DTMFNumberBankElement: public Element { protected: /** Hidden constructor. */ DTMFNumberBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DTMFNumberBankElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0090; } public: /** Some limits. */ struct Limit { /** The number of members. */ static constexpr unsigned int memberCount() { return 16; } }; protected: /** Some internal offsets within the number bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int members() { return 0x0000; } static constexpr unsigned int betweenMembers() { return DTMFNumberElement::size(); } /// @endcond }; }; /** Implements a single DTMF PTT setting. * Memory representation of the DTMF PTT setting (size 0005h bytes): * @verbinclude gd73_dtmf_ptt_settings.txt */ class DTMFPTTSettingElement: public Element { protected: /** Hidden constructor. */ DTMFPTTSettingElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DTMFPTTSettingElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0005; } protected: /** Internal used offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int systemIndex() { return 0x0000; } static constexpr unsigned int pttIDType() { return 0x0001; } static constexpr unsigned int pttIDMode() { return 0x0002; } static constexpr unsigned int connectIDIndex() { return 0x0003; } static constexpr unsigned int disconnectIDIndex() { return 0x0004; } /// @endcond }; }; /** Implements the bank of 32 DTMF PTT settings. * @c GD73Codeplug::DTMFPTTSettingElement for encoding of each element. */ class DTMFPTTSettingBankElement: public Element { protected: /** Hidden constructor. */ DTMFPTTSettingBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DTMFPTTSettingBankElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x00a0; } public: /** Some limits. */ struct Limit { /** The number of members. */ static constexpr unsigned int memberCount() { return 32; } }; protected: /** Some internal offsets within the number bank. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int members() { return 0x0000; } static constexpr unsigned int betweenMembers() { return DTMFPTTSettingElement::size(); } /// @endcond }; }; public: /** Default constructor. */ explicit GD73Codeplug(QObject *parent = nullptr); Config *preprocess(Config *config, const ErrorStack &err=ErrorStack()) const; bool postprocess(Config *config, const ErrorStack &err=ErrorStack()) const; bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const; bool decode(Config *config, const ErrorStack &err=ErrorStack()); bool encode(Config *config, const Flags &flags=Flags(), const ErrorStack &err=ErrorStack()); protected: /** Decodes the time-stamp field. */ virtual bool decodeTimestamp(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the time-stamp field. */ virtual bool encodeTimestamp(Context &ctx, const ErrorStack &err=ErrorStack()); /** Creates messages. */ virtual bool createMessages(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encode messages. */ virtual bool encodeMessages(Context &ctx, const ErrorStack &err=ErrorStack()); /** Decodes the settings fields (generic & DMR). */ virtual bool decodeSettings(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encode settings fields (generic & DMR settings). */ virtual bool encodeSettings(Context &ctx, const ErrorStack &err=ErrorStack()); /** Creates contacts. */ virtual bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encode contacts. */ virtual bool encodeContacts(Context &ctx, const ErrorStack &err=ErrorStack()); /** Creates DTMF contacts. */ virtual bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack()); /** Create group lists. */ virtual bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); /** Link group lists. */ virtual bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encode group lists. */ virtual bool encodeGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); /** Create encryption keys. */ virtual bool createEncryptionKeys(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encode encryption keys. */ virtual bool encodeEncryptionKeys(Context &ctx, const ErrorStack &err=ErrorStack()); /** Create channels. */ virtual bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()); /** Link channels. */ virtual bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encode channels. */ virtual bool encodeChannels(Context &ctx, const ErrorStack &err=ErrorStack()); /** Create zones. */ virtual bool createZones(Context &ctx, const ErrorStack &err=ErrorStack()); /** Link zones. */ virtual bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encode zones. */ virtual bool encodeZones(Context &ctx, const ErrorStack &err=ErrorStack()); /** Create scan lists. */ virtual bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack()); /** Link zones. */ virtual bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encode zones. */ virtual bool encodeScanLists(Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Internal used offsets within the codeplug. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int timestamp() { return 0x00000; } static constexpr unsigned int settings() { return 0x00061; } static constexpr unsigned int zones() { return 0x0010b; } static constexpr unsigned int channels() { return 0x00d4c; } static constexpr unsigned int contacts() { return 0x125ff; } static constexpr unsigned int groupLists() { return 0x1c201; } static constexpr unsigned int scanLists() { return 0x21310; } static constexpr unsigned int dmrSettings() { return 0x21911; } static constexpr unsigned int encryptionKeys() { return 0x2191f; } static constexpr unsigned int messages() { return 0x2196f; } static constexpr unsigned int dtmfSystems() { return 0x21e80; } static constexpr unsigned int dtmfNumbers() { return 0x21e94; } static constexpr unsigned int dtmfPTTSettings() { return 0x21f24; } /// @endcond }; }; #endif // GD73CODEPLUG_HH qdmr-0.12.3/lib/gd73_filereader.cc000066400000000000000000000026021501654372000165460ustar00rootroot00000000000000#include "gd73_filereader.hh" #include #include #define SEGMENT0_ADDR 0x00000000 #define SEGMENT0_SIZE 0x00022014 bool GD73FileReader::read(const QString &filename, GD73Codeplug *codeplug, const ErrorStack &err) { // Check file properties QFileInfo info(filename); if (! info.exists()) { errMsg(err) << "Cannot open file '" << filename << "': File does not exisist."; return false; } if (SEGMENT0_SIZE != info.size()) { errMsg(err) << "Cannot read codeplug file '" << filename << "': File size is not " << SEGMENT0_SIZE << " bytes."; return false; } // Open file QFile file(filename); if (! file.open(QFile::ReadOnly)) { errMsg(err) << "Cannot open file '" << filename << "': " << file.errorString() << "."; return false; } // Read file content if (! file.seek(SEGMENT0_ADDR)) { errMsg(err) << "Cannot read codeplug file '" << filename << "': Cannot seek within file: " << file.errorString() << "."; file.close(); return false; } char *ptr = (char *)codeplug->data(SEGMENT0_ADDR); size_t n = SEGMENT0_SIZE; while (0 < n) { int nread = file.read(ptr, n); if (0 > nread) { errMsg(err) << "Cannot read codeplug file '" << filename << "': " << file.errorString() << "."; file.close(); return false; } n -= nread; ptr += nread; } return true; } qdmr-0.12.3/lib/gd73_filereader.hh000066400000000000000000000020621501654372000165600ustar00rootroot00000000000000#ifndef GD73FILEREADER_HH #define GD73FILEREADER_HH #include "gd73_codeplug.hh" /** Methods to read manufacturer codeplug files. * * The file format of the stock CPS is pretty simple. It is a one-to-one dump of the codeplug * data as written to the device. This makes the decoding of the manufacturer codeplug files very * easy. Some memory regions, however, are not written to the deivice although they are present in * the codeplug file. * * * * *
      Start End Size
      0x00000 0x22149 0x22149
      * * @ingroup gd73 */ class GD73FileReader { public: /** Reads manufacturer codeplug file into given codeplug object. * @param filename Specifies the file to read. * @param codeplug Specifies the codeplug object to store read codeplug. * @param err The error stack. * @returns @c true on success and @c false on error. */ static bool read(const QString &filename, GD73Codeplug *codeplug, const ErrorStack &err=ErrorStack()); }; #endif // GD73FILEREADER_HH qdmr-0.12.3/lib/gd73_interface.cc000066400000000000000000000065221501654372000164110ustar00rootroot00000000000000#include "gd73_interface.hh" #include "logger.hh" #include #define BLOCK_SIZE 0x35 GD73Interface::GD73Interface(const USBDeviceDescriptor &descriptor, const ErrorStack &err, QObject *parent) : C7000Device(descriptor, err, parent), RadioInterface() { Packet request, response; if (nullptr == _dev) { errMsg(err) << "Cannot initialize GD73 interface: C7000 interface not open."; return; } request = Packet(0x01, 0x04); if (! sendRecv(request, response, err)) { errMsg(err) << "Cannot enter programming mode."; C7000Device::close(); } logDebug() << "Entered prog mode. Response: " << response.payload().toHex() << "."; } bool GD73Interface::isOpen() const { return C7000Device::isOpen(); } RadioInfo GD73Interface::identifier(const ErrorStack &err) { Q_UNUSED(err); return RadioInfo::byID(RadioInfo::GD73); } void GD73Interface::close() { C7000Device::close(); } bool GD73Interface::write_start(uint32_t bank, uint32_t addr, const ErrorStack &err) { Q_UNUSED(bank); Q_UNUSED(addr); Q_UNUSED(err); return true; } bool GD73Interface::write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) { Q_UNUSED(bank); if ((addr%BLOCK_SIZE) || (nbytes!=BLOCK_SIZE)) { errMsg(err) << "Address and size must align with block size of 35h"; return false; } C7000Device::Packet request, response; QByteArray payload; payload.resize(2); *((uint16_t *)payload.data()) = qToLittleEndian((uint16_t)(addr/BLOCK_SIZE)); payload.append((char *)data, nbytes); request = C7000Device::Packet(0x01, 0x00, 0x0f, payload); if (! sendRecv(request, response, err)) { errMsg(err) << "Cannot send write command."; return false; } return true; } bool GD73Interface::write_finish(const ErrorStack &err) { Q_UNUSED(err); return true; } bool GD73Interface::read_start(uint32_t bank, uint32_t addr, const ErrorStack &err) { Q_UNUSED(bank); Q_UNUSED(addr); Q_UNUSED(err); _lastSequence = 0xffff; logDebug() << "Start codeplug read, seqnr=" << Qt::hex << _lastSequence << "h."; return true; } bool GD73Interface::read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) { Q_UNUSED(bank); if ((addr%BLOCK_SIZE) || (nbytes!=BLOCK_SIZE)) { errMsg(err) << "Address and size must align with block size of 35h"; return false; } //logDebug() << "Read " << nbytes << "bytes from address " << Qt::hex << addr << "h."; uint16_t seqNum = addr/BLOCK_SIZE; if (uint16_t(_lastSequence+1) != seqNum) { errMsg(err) << "Out-of-sequence read: Expected seqnr. " << uint16_t(_lastSequence+1) << " got " << seqNum << "."; return false; } C7000Device::Packet request, response; if (0xffff == _lastSequence) { // Request start-of-read request = C7000Device::Packet(0x01, 0x02); } else { QByteArray payload(2,0); *((uint16_t *)payload.data()) = qToLittleEndian(_lastSequence); request = C7000Device::Packet(0x04, 0x01, 0x0f, payload); } if (! sendRecv(request, response, err)) { errMsg(err) << "Cannot read codeplug from device."; return false; } _lastSequence = qFromLittleEndian(*(uint16_t *)response.payload().data()); memcpy(data, response.payload().data()+2, nbytes); return true; } bool GD73Interface::read_finish(const ErrorStack &err) { Q_UNUSED(err); _lastSequence = 0xffff; return true; } qdmr-0.12.3/lib/gd73_interface.hh000066400000000000000000000024071501654372000164210ustar00rootroot00000000000000#ifndef GD73INTERFACE_HH #define GD73INTERFACE_HH #include "c7000device.hh" #include "radiointerface.hh" /** Implements the communication interface to the GD-73. * @ingroup gd73 */ class GD73Interface: public C7000Device, public RadioInterface { Q_OBJECT public: /** Constructs a new interface to GD73A/E radios. If a matching device was found, @c isOpen * returns @c true. */ GD73Interface(const USBDeviceDescriptor &descriptor, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr); bool isOpen() const; RadioInfo identifier(const ErrorStack &err=ErrorStack()); bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()); bool write_finish(const ErrorStack &err=ErrorStack()); bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()); bool read_finish(const ErrorStack &err=ErrorStack()); void close(); protected: /** Name of the radio. */ QString _identifier; /** Last received/send sequence number. */ uint16_t _lastSequence; }; #endif // GD73INTERFACE_HH qdmr-0.12.3/lib/gd73_limits.cc000066400000000000000000000202131501654372000157430ustar00rootroot00000000000000#include "gd73_limits.hh" #include "gd73_codeplug.hh" #include "channel.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "zone.hh" #include "scanlist.hh" #include "roamingzone.hh" GD73Limits::GD73Limits(QObject *parent) : RadioLimits(true, parent) { // Define limits for call-sign DB _hasCallSignDB = false; _callSignDBImplemented = false; _numCallSignDBEntries = 0; // Define limits for satellite config _hasSatelliteConfig = false; _satelliteConfigImplemented = false; _numSatellites = 0; /* Define limits for the general settings. */ add("settings", new RadioLimitItem{ { "introLine1", new RadioLimitString(-1, GD73Codeplug::SettingsElement::Limit::bootTextLine(), RadioLimitString::Unicode) }, { "introLine2", new RadioLimitString(-1, GD73Codeplug::SettingsElement::Limit::bootTextLine(), RadioLimitString::Unicode) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, -1) } }); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList { { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject { {"name", new RadioLimitString(1, GD73Codeplug::SettingsElement::Limit::name(), RadioLimitString::Unicode) }, {"id", new RadioLimitUInt(0, 16777215)} } } /// @todo check default radio ID. }); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, GD73Codeplug::ContactBankElement::Limit::contactCount(), new RadioLimitObject { { "name", new RadioLimitString(1, GD73Codeplug::ContactElement::Limit::nameLength(), RadioLimitString::Unicode) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum{ (unsigned)DMRContact::PrivateCall, (unsigned)DMRContact::GroupCall, (unsigned)DMRContact::AllCall }}, { "number", new RadioLimitUInt(0, 16777215) } } } }); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 1, GD73Codeplug::GroupListBankElement::Limit::memberCount(), new RadioLimitObject { { "name", new RadioLimitString(1, GD73Codeplug::GroupListElement::Limit::nameLength(), RadioLimitString::Unicode) }, { "contacts", new RadioLimitGroupCallRefList(1, GD73Codeplug::GroupListElement::Limit::memberCount()) } })); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, GD73Codeplug::ChannelBankElement::Limit::channelCount(), new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, GD73Codeplug::ChannelElement::Limit::nameLength(), RadioLimitString::Unicode)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, 3825, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRefIgnored()}, {"openGD77", new RadioLimitIgnored()}, {"tyt", new RadioLimitIgnored()} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, GD73Codeplug::ChannelElement::Limit::nameLength(), RadioLimitString::Unicode)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, 3825, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, true)}, {"aprs", new RadioLimitObjRefIgnored()}, {"roaming", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } )); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 1, GD73Codeplug::ZoneBankElement::Limit::zoneCount(), new RadioLimitSingleZone( GD73Codeplug::ZoneElement::Limit::channelCount(), { { "name", new RadioLimitString(1, GD73Codeplug::ZoneElement::Limit::nameLength(), RadioLimitString::Unicode) }, { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions }) ) ); /* Check scan lists. */ add("scanlists", new RadioLimitList( ScanList::staticMetaObject, 0, GD73Codeplug::ScanListBankElement::Limit::memberCount(), new RadioLimitObject{ { "name", new RadioLimitString(1, GD73Codeplug::ScanListElement::Limit::nameLength(), RadioLimitString::Unicode) }, { "primary", new RadioLimitObjRef(Channel::staticMetaObject, false) }, { "secondary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "revert", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "channels", new RadioLimitRefList(0, GD73Codeplug::ScanListElement::Limit::memberCount(), Channel::staticMetaObject) } })); /* Check encryption keys. */ add("commercial", new RadioLimitItem { {"encryptionKeys", new RadioLimitList( BasicEncryptionKey::staticMetaObject, 0, GD73Codeplug::EncryptionKeyBankElement::Limit::keys(), new RadioLimitObject { {"name", new RadioLimitIgnored()}, {"key", new RadioLimitStringRegEx("[0-9a-fA-F]{2,8}")} })} }); /* Ignore positioning systems. */ add("positioning", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored()) ); /* Ignore roaming zones. */ add("roaming", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored()) ); } qdmr-0.12.3/lib/gd73_limits.hh000066400000000000000000000004541501654372000157620ustar00rootroot00000000000000#ifndef GD73LIMITS_HH #define GD73LIMITS_HH #include "radiolimits.hh" /** Implements the limits for the Radioddity GD77. * @ingroup gd73 */ class GD73Limits: public RadioLimits { Q_OBJECT public: /** Constructor. */ explicit GD73Limits(QObject *parent=nullptr); }; #endif // GD73LIMITS_HH qdmr-0.12.3/lib/gd77.cc000066400000000000000000000053271501654372000143770ustar00rootroot00000000000000#include "gd77.hh" #include "gd77_limits.hh" #include "logger.hh" #include "config.hh" #define BSIZE 32 RadioLimits * GD77::_limits = nullptr; GD77::GD77(RadioddityInterface *device, QObject *parent) : RadioddityRadio(device, parent), _name("Radioddity GD-77"), _codeplug(), _callsigns() { // pass... } const QString & GD77::name() const { return _name; } const RadioLimits & GD77::limits() const { if (nullptr == _limits) _limits = new GD77Limits(); return *_limits; } const Codeplug & GD77::codeplug() const { return _codeplug; } Codeplug & GD77::codeplug() { return _codeplug; } RadioInfo GD77::defaultRadioInfo() { return RadioInfo( RadioInfo::GD77, "gd77", "GD-77", "Radioddity", RadioddityInterface::interfaceInfo()); } bool GD77::startUploadCallsignDB(UserDatabase *db, bool blocking, const CallsignDB::Selection &selection, const ErrorStack &err) { logDebug() << "Start call-sign DB upload to " << name() << "..."; _errorStack = err; if (StatusIdle != _task) { errMsg(err) << "Cannot upload to radio, radio is not idle."; return false; } // Assemble call-sign db from user DB logDebug() << "Encode call-signs into db."; _callsigns.encode(db, selection); _task = StatusUploadCallsigns; if (blocking) { logDebug() << "Upload call-sign DB in this thread (blocking)."; run(); return (StatusIdle == _task); } //if (_dev && _dev->isOpen()) // _dev->moveToThread(this); // start thread for upload logDebug() << "Upload call-sign DB in separate thread."; start(); return true; } bool GD77::uploadCallsigns() { emit uploadStarted(); // Check every segment in the codeplug if (! _callsigns.isAligned(BSIZE)) { errMsg(_errorStack) << "Cannot upload call-sign DB: Not aligned with block-size " << BSIZE << "."; return false; } logDebug() << "Call-sign DB upload started..."; size_t totb = _callsigns.memSize(); unsigned bcount = 0; for (int n=0; n<_callsigns.image(0).numElements(); n++) { unsigned addr = _callsigns.image(0).element(n).address(); unsigned size = _callsigns.image(0).element(n).data().size(); unsigned b0 = addr/BSIZE, nb = size/BSIZE; for (unsigned b=0; b (b0+b)*BSIZE) ? RadioddityInterface::MEMBANK_CALLSIGN_LOWER : RadioddityInterface::MEMBANK_CALLSIGN_UPPER ); if (! _dev->write(bank, ((b0+b)*BSIZE)&0xffff, _callsigns.data((b0+b)*BSIZE, 0), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot write block " << (b0+b) << "."; return false; } emit uploadProgress(float(bcount*100)/totb); } } _dev->write_finish(); return true; } qdmr-0.12.3/lib/gd77.hh000066400000000000000000000033301501654372000144010ustar00rootroot00000000000000/** @defgroup gd77 Radioddity GD-77 * Device specific classes for Radioddity GD-77 and GD-77S. * * \image html gd77.jpg "GD-77" width=200px * \image latex gd77.jpg "GD-77" width=200px * * @ingroup radioddity */ #ifndef GD77_HH #define GD77_HH #include "radioddity_radio.hh" #include "radioddity_interface.hh" #include "gd77_codeplug.hh" #include "gd77_callsigndb.hh" /** Implements an USB interface to the Radioddity GD-77(S) VHF/UHF 5W DMR (Tier I&II) radios. * * @ingroup gd77 */ class GD77 : public RadioddityRadio { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit GD77(RadioddityInterface *device=nullptr, QObject *parent=nullptr); const QString &name() const; const RadioLimits &limits() const; const Codeplug &codeplug() const; Codeplug &codeplug(); /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); public slots: /** Encodes the given user-database and uploades it to the device. */ bool startUploadCallsignDB(UserDatabase *db, bool blocking=false, const CallsignDB::Selection &selection=CallsignDB::Selection(), const ErrorStack &err=ErrorStack()); protected: /** Implements the actual callsign DB upload process. */ bool uploadCallsigns(); protected: /** The device identifier. */ QString _name; /** The codeplug. */ GD77Codeplug _codeplug; /** The actual binary callsign DB representation. */ GD77CallsignDB _callsigns; private: /** Holds the signleton instance of the radio limits for this radio. */ static RadioLimits *_limits; }; #endif // GD77_HH qdmr-0.12.3/lib/gd77_callsigndb.cc000066400000000000000000000070621501654372000165570ustar00rootroot00000000000000#include "gd77_callsigndb.hh" #include "utils.hh" #include "userdatabase.hh" #include "logger.hh" #include #define OFFSET_USERDB 0x00000 #define USERDB_MAX_ENTRIES 10920 #define USERDB_MAX_ENTRIES_PER_BANK 5460 #define BLOCK_SIZE 32 /* ******************************************************************************************** * * Implementation of GD77CallsignDB::userdb_entry_t * ******************************************************************************************** */ GD77CallsignDB::userdb_entry_t::userdb_entry_t() { clear(); } void GD77CallsignDB::userdb_entry_t::clear() { memset(this, 0, sizeof(userdb_entry_t)); } uint32_t GD77CallsignDB::userdb_entry_t::getNumber() const { return decode_dmr_id_bcd_le((uint8_t *)&number); } void GD77CallsignDB::userdb_entry_t::setNumber(uint32_t number) { encode_dmr_id_bcd_le((uint8_t *)&(this->number), number); } QString GD77CallsignDB::userdb_entry_t::getName() const { return decode_ascii((const uint8_t *)name, 7, 0x00); } void GD77CallsignDB::userdb_entry_t::setName(const QString &name) { encode_ascii((uint8_t *)(this->name), name, 7, 0x00); } void GD77CallsignDB::userdb_entry_t::fromEntry(const UserDatabase::User &user) { clear(); setNumber(user.id); setName(user.call); } /* ******************************************************************************************** * * Implementation of GD77CallsignDB::userdb_t * ******************************************************************************************** */ GD77CallsignDB::userdb_t::userdb_t() { clear(); } void GD77CallsignDB::userdb_t::clear() { memcpy(magic, "ID-V001\0", 8); count = 0; } void GD77CallsignDB::userdb_t::setSize(uint32_t n) { count = qToLittleEndian(std::min(n, uint32_t(USERDB_MAX_ENTRIES))); } /* ******************************************************************************************** * * Implementation of OpenGD77CallsignDB * ******************************************************************************************** */ GD77CallsignDB::GD77CallsignDB(QObject *parent) : CallsignDB(parent) { addImage("GD77 call-sign database"); } GD77CallsignDB::~GD77CallsignDB() { // pass... } bool GD77CallsignDB::encode(UserDatabase *calldb, const Selection &selection, const ErrorStack &err) { Q_UNUSED(err) // Limit entries to USERDB_NUM_ENTRIES qint64 n = std::min(calldb->count(), qint64(USERDB_MAX_ENTRIES)); if (selection.hasCountLimit()) n = std::min(n, (qint64)selection.countLimit()); // If there are no entries -> done. if (0 == n) return true; // Select first n entries and sort them in ascending order of their IDs logDebug() << "Select first " << n << " entries out off " << calldb->count() << "."; QVector users; for (unsigned i=0; iuser(i)); logDebug() << "Sort selected w.r.t their ID in ascending order."; std::sort(users.begin(), users.end(), [](const UserDatabase::User &a, const UserDatabase::User &b) { return a.id < b.id; }); // Allocate segment for user db if requested size_t size = align_size(sizeof(userdb_t)+n*sizeof(userdb_entry_t), BLOCK_SIZE); logDebug() << "Allocate 0x" << QString::number(size,16) << " bytes for call-sign DB."; this->image(0).addElement(OFFSET_USERDB, size); // Encode user DB userdb_t *userdb = (userdb_t *)this->data(OFFSET_USERDB); userdb->clear(); userdb->setSize(n); userdb_entry_t *db = (userdb_entry_t *)this->data(OFFSET_USERDB+sizeof(userdb_t), 0); for (unsigned i=0; i /* ******************************************************************************************** * * Implementation of GD77Codeplug::ChannelElement * ******************************************************************************************** */ GD77Codeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size) : RadioddityCodeplug::ChannelElement(ptr, size) { // pass... } GD77Codeplug::ChannelElement::ChannelElement(uint8_t *ptr) : RadioddityCodeplug::ChannelElement(ptr) { // pass... } void GD77Codeplug::ChannelElement::clear() { RadioddityCodeplug::ChannelElement::clear(); setUInt8(0x0028, 0x00); setARTSMode(ARTS_OFF); setSTEAngle(STE_FREQUENCY); } GD77Codeplug::ChannelElement::ARTSMode GD77Codeplug::ChannelElement::artsMode() const { return ARTSMode(getUInt2(0x0030,0)); } void GD77Codeplug::ChannelElement::setARTSMode(ARTSMode mode) { setUInt2(0x0030, 0, (unsigned)mode); } GD77Codeplug::ChannelElement::STEAngle GD77Codeplug::ChannelElement::steAngle() const { return STEAngle(getUInt2(0x0032,6)); } void GD77Codeplug::ChannelElement::setSTEAngle(STEAngle angle) { setUInt2(0x0032, 6, (unsigned)angle); } GD77Codeplug::ChannelElement::PTTId GD77Codeplug::ChannelElement::pttIDMode() const { return PTTId(getUInt2(0x0032, 2)); } void GD77Codeplug::ChannelElement::setPTTIDMode(PTTId mode) { setUInt2(0x0032, 2, (unsigned)mode); } bool GD77Codeplug::ChannelElement::squelchIsTight() const { return getBit(0x0033, 0); } void GD77Codeplug::ChannelElement::enableTightSquelch(bool enable) { setBit(0x0033, 0, enable); } bool GD77Codeplug::ChannelElement::loneWorker() const { return getBit(0x0033, 4); } void GD77Codeplug::ChannelElement::enableLoneWorker(bool enable) { setBit(0x0033, 4, enable); } bool GD77Codeplug::ChannelElement::autoscan() const { return getBit(0x0033, 5); } void GD77Codeplug::ChannelElement::enableAutoscan(bool enable) { setBit(0x0033, 5, enable); } /* ******************************************************************************************** * * Implementation of GD77Codeplug::ContactElement * ******************************************************************************************** */ GD77Codeplug::ContactElement::ContactElement(uint8_t *ptr, unsigned size) : RadioddityCodeplug::ContactElement(ptr, size) { // pass... } GD77Codeplug::ContactElement::ContactElement(uint8_t *ptr) : RadioddityCodeplug::ContactElement(ptr) { // pass... } void GD77Codeplug::ContactElement::clear() { markValid(false); } bool GD77Codeplug::ContactElement::isValid() const { uint8_t validFlag = getUInt8(Offset::validFlag()); return RadioddityCodeplug::ContactElement::isValid() && (0x00 != validFlag); } void GD77Codeplug::ContactElement::markValid(bool valid) { setUInt8(Offset::validFlag(), valid ? 0xff : 0x00); } bool GD77Codeplug::ContactElement::fromContactObj(const DMRContact *obj, Context &ctx, const ErrorStack &err) { if (! RadioddityCodeplug::ContactElement::fromContactObj(obj, ctx, err)) return false; markValid(); return true; } /* ******************************************************************************************** * * Implementation of GD77Codeplug::ScanListElement * ******************************************************************************************** */ GD77Codeplug::ScanListElement::ScanListElement(uint8_t *ptr, unsigned size) : RadioddityCodeplug::ScanListElement(ptr, size) { // pass... } GD77Codeplug::ScanListElement::ScanListElement(uint8_t *ptr) : RadioddityCodeplug::ScanListElement(ptr) { // pass... } void GD77Codeplug::ScanListElement::clear() { RadioddityCodeplug::ScanListElement::clear(); setBit(0x0f, 0, true); } /* ******************************************************************************************** * * Implementation of GD77Codeplug::ScanListBankElement * ******************************************************************************************** */ GD77Codeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr, unsigned size) : RadioddityCodeplug::ScanListBankElement(ptr, size) { // pass... } GD77Codeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr) : RadioddityCodeplug::ScanListBankElement(ptr, size()) { // pass... } void GD77Codeplug::ScanListBankElement::clear() { memset(_data, 0, Limit::scanListCount()); } uint8_t * GD77Codeplug::ScanListBankElement::get(unsigned n) const { return _data+Offset::scanLists() + n*ScanListElement::size(); } /* ******************************************************************************************** * * Implementation of GD77Codeplug::GroupListElement * ******************************************************************************************** */ GD77Codeplug::GroupListElement::GroupListElement(uint8_t *ptr, unsigned size) : RadioddityCodeplug::GroupListElement(ptr, size) { // pass... } GD77Codeplug::GroupListElement::GroupListElement(uint8_t *ptr) : RadioddityCodeplug::GroupListElement(ptr, size()) { // pass... } bool GD77Codeplug::GroupListElement::linkRXGroupListObj(unsigned int ncnt, RXGroupList *lst, Context &ctx, const ErrorStack &err) const { for (unsigned int i=0; (i(member(i))) { lst->addContact(ctx.get(member(i))); } else { errMsg(err) << "Cannot link group list '" << lst->name() << "': Member index " << member(i) << " does not refer to a digital contact."; return false; } } return true; } bool GD77Codeplug::GroupListElement::fromRXGroupListObj(const RXGroupList *lst, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) setName(lst->name()); int j = 0; // Iterate over all entries in the codeplug for (unsigned int i=0; icount() > j) { // Skip non-private-call entries while((lst->count() > j) && (DMRContact::GroupCall != lst->contact(j)->type())) { logWarn() << "Contact '" << lst->contact(i)->name() << "' in group list '" << lst->name() << "' is not a group call. Skip entry."; j++; } setMember(i, ctx.index(lst->contact(j))); j++; } else { // Clear entry. clearMember(i); } } return false; } /* ******************************************************************************************** * * Implementation of GD77Codeplug::GroupListBankElement * ******************************************************************************************** */ GD77Codeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr, unsigned size) : RadioddityCodeplug::GroupListBankElement(ptr, size) { // pass... } GD77Codeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr) : RadioddityCodeplug::GroupListBankElement(ptr, size()) { // pass... } uint8_t * GD77Codeplug::GroupListBankElement::get(unsigned n) const { if ((Offset::groupLists() + (n+1)*GroupListElement::size())>_size) { logFatal() << "Cannot resolve group list at index " << n << ": Overflow."; return nullptr; } return _data + Offset::groupLists() + n*GroupListElement::size(); } /* ******************************************************************************************** * * Implementation of GD77Codeplug * ******************************************************************************************** */ GD77Codeplug::GD77Codeplug(QObject *parent) : RadioddityCodeplug(parent) { addImage("Radioddity GD77 Codeplug"); image(0).addElement(0x00080, 0x07b80); image(0).addElement(0x08000, 0x16b00); } void GD77Codeplug::clearGeneralSettings() { GeneralSettingsElement(data(Offset::settings())).clear(); } bool GD77Codeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { GeneralSettingsElement el(data(Offset::settings())); if (! flags.updateCodePlug) el.clear(); return el.fromConfig(ctx, err); } bool GD77Codeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) { return GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx, err); } void GD77Codeplug::clearButtonSettings() { ButtonSettingsElement(data(Offset::buttons())).clear(); } bool GD77Codeplug::encodeButtonSettings(Context &ctx, const Flags &flags, const ErrorStack &err) { Q_UNUSED(flags); return ButtonSettingsElement(data(Offset::buttons())).encode(ctx, err); } bool GD77Codeplug::decodeButtonSettings(Context &ctx, const ErrorStack &err) { return ButtonSettingsElement(data(Offset::buttons())).decode(ctx, err); } void GD77Codeplug::clearMessages() { MessageBankElement(data(Offset::messages())).clear(); } bool GD77Codeplug::encodeMessages(Context &ctx, const Flags &flags, const ErrorStack &err) { return MessageBankElement(data(Offset::messages())).encode(ctx, flags, err); } bool GD77Codeplug::decodeMessages(Context &ctx, const ErrorStack &err) { return MessageBankElement(data(Offset::messages())).decode(ctx, err); } void GD77Codeplug::clearScanLists() { ScanListBankElement bank(data(Offset::scanListBank())); bank.clear(); for (unsigned int i=0; i= ctx.count()) { bank.enable(i, false); continue; } if (! ScanListElement(bank.get(i)).fromScanListObj(ctx.get(i+1), ctx, err)) return false; bank.enable(i, true); } return true; } bool GD77Codeplug::createScanLists(Context &ctx, const ErrorStack &err) { ScanListBankElement bank(data(Offset::scanListBank())); for (unsigned int i=0; iscanlists()->add(scan); ctx.add(scan, i+1); } return true; } bool GD77Codeplug::linkScanLists(Context &ctx, const ErrorStack &err) { ScanListBankElement bank(data(Offset::scanListBank())); for (unsigned int i=0; i(i+1), ctx, err)) return false; } return true; } void GD77Codeplug::clearChannels() { for (unsigned int b=0,c=0; b()) { if (! el.fromChannelObj(ctx.get(c+1), ctx, err)) { errMsg(err) << "Cannot encode channel " << c << " (" << i << " of bank " << b <<")."; return false; } bank.enable(i,true); } else { el.clear(); bank.enable(i, false); } } } return true; } bool GD77Codeplug::createChannels(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) for (unsigned int b=0,c=0; bchannelList()->add(ch); ctx.add(ch, c+1); } } return true; } bool GD77Codeplug::linkChannels(Context &ctx, const ErrorStack &err) { for (unsigned int b=0,c=0; b(c+1), ctx, err)) { errMsg(err) << "Cannot link channel '" << ctx.get(c+1)->name() << "' at index " << i << "."; return false; } } } return true; } void GD77Codeplug::clearBootSettings() { BootSettingsElement(data(Offset::bootSettings())).clear(); } void GD77Codeplug::clearMenuSettings() { MenuSettingsElement(data(Offset::menuSettings())).clear(); } void GD77Codeplug::clearBootText() { BootTextElement(data(Offset::bootText())).clear(); } bool GD77Codeplug::encodeBootText(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); BootTextElement(data(Offset::bootText())).fromConfig(ctx, err); return true; } bool GD77Codeplug::decodeBootText(Context &ctx, const ErrorStack &err) { BootTextElement(data(Offset::bootText())).updateConfig(ctx, err); return true; } void GD77Codeplug::clearVFOSettings() { VFOChannelElement(data(Offset::vfoA())).clear(); VFOChannelElement(data(Offset::vfoB())).clear(); } void GD77Codeplug::clearZones() { ZoneBankElement bank(data(Offset::zoneBank())); bank.clear(); for (unsigned int i=0; i(i+1)) { bank.enable(i, false); continue; } // Construct from Zone obj Zone *zone = ctx.get(i+1); z.fromZoneObjA(zone, ctx); bank.enable(i, true); } return true; } bool GD77Codeplug::createZones(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) ZoneBankElement bank(data(Offset::zoneBank())); for (unsigned int i=0; izones()->add(zone); ctx.add(zone, i+1); } return true; } bool GD77Codeplug::linkZones(Context &ctx, const ErrorStack &err) { ZoneBankElement bank(data(Offset::zoneBank())); for (unsigned int i=0; i(i+1); if (! z.linkZoneObj(zone, ctx)) { errMsg(err) << "Cannot link zone at index " << i << "."; return false; } } return true; } void GD77Codeplug::clearContacts() { for (unsigned int i=0; i= ctx.count()) continue; if (! el.fromContactObj(ctx.get(i+1), ctx, err)) { errMsg(err) << "Cannot encode contact at index " << i << "."; return false; } } return true; } bool GD77Codeplug::createContacts(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; icontacts()->add(cont); } return true; } void GD77Codeplug::clearDTMFContacts() { for (unsigned int i=0; i= ctx.count()) continue; el.fromContactObj(ctx.get(i), ctx, err); } return true; } bool GD77Codeplug::createDTMFContacts(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; icontacts()->add(cont); } return true; } void GD77Codeplug::clearGroupLists() { GroupListBankElement bank(data(Offset::groupListBank())); bank.clear(); for (unsigned int i=0; i= ctx.count()) continue; GroupListElement el(bank.get(i)); el.fromRXGroupListObj(ctx.get(i+1), ctx, err); bank.setContactCount(i, ctx.get(i+1)->count()); } return true; } bool GD77Codeplug::createGroupLists(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) GroupListBankElement bank(data(Offset::groupListBank())); for (unsigned int i=0; irxGroupLists()->add(list); ctx.add(list, i+1); } return true; } bool GD77Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) { GroupListBankElement bank(data(Offset::groupListBank())); for (unsigned int i=0; i(i+1), ctx, err)) { errMsg(err) << "Cannot link group list '" << ctx.get(i+1)->name() << "'."; return false; } } return true; } void GD77Codeplug::clearEncryption() { EncryptionElement enc(data(Offset::encryption())); enc.clear(); } bool GD77Codeplug::encodeEncryption(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); clearEncryption(); EncryptionElement enc(data(Offset::encryption())); return enc.fromCommercialExt(ctx.config()->commercialExtension(), ctx, err); } bool GD77Codeplug::createEncryption(Context &ctx, const ErrorStack &err) { EncryptionElement enc(data(Offset::encryption())); if (EncryptionElement::PrivacyType::None == enc.privacyType()) return true; return enc.updateCommercialExt(ctx, err); } bool GD77Codeplug::linkEncryption(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); return true; } qdmr-0.12.3/lib/gd77_codeplug.hh000066400000000000000000000375561501654372000163040ustar00rootroot00000000000000#ifndef GD77_CODEPLUG_HH #define GD77_CODEPLUG_HH #include "radioddity_codeplug.hh" #include "signaling.hh" /** Represents, encodes and decodes the device specific codeplug for a Radioddity GD-77. * * The GD-77 & GD-77S codeplugs are almost identical to the Radioddity/Baofeng @c RD5RCodeplug, in fact * the memory layout (see below) and almost all of the single components of the codeplug are encoded in * exactly the same way. Obviously, when Baofeng and Radioddity joint to create the RD5R, * Radioddity provided the firmware. However, there are some small subtile differences between * these two codeplug formats, requiring a separate class for the GD-77. For example, the contacts * and scan-lists swapped the addresses and the @c channel_t encoding analog and digital channels * for the codeplugs are identical except for the squelch settings. Thanks for that! * * @section gd77ver Matching firmware versions * This class implements the codeplug for the firmware version @b 4.03.06. The codeplug format usually * does not change much with firmware revisions, in particular not with older radios. Unfortunately, * it is not possible to detect the firmware version running on the device. Consequenly, only the * newest firmware version is supported. However, older revisions may still work. * * @section gd77cpl Codeplug structure within radio * The memory representation of the codeplug within the radio is divided into two segments. * The first segment starts at the address 0x00080 and ends at 0x07c00 while the second section * starts at 0x08000 and ends at 0x1e300. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Start End Size Content
      First segment 0x00080-0x07c00
      0x00080 0x000e0 0x0070 ??? Unknown ???
      0x000e0 0x000ec 0x000c General settings, see @c RadioddityCodeplug::GeneralSettingsElement.
      0x000ec 0x00108 0x0028 ??? Unknown ???
      0x00108 0x00128 0x0020 Button settings, see @c RadioddityCodeplug::ButtonSettingsElement.
      0x00128 0x01370 0x1248 32 preset message texts, see @c RadioddityCodeplug::MessageBankElement.
      0x01370 0x01790 0x0420 ??? Unknown ???
      0x01790 0x02dd0 0x1640 64 scan lists, see @c GD77Codeplug::ScanListBankElement and @c GD77Codeplug::ScanListElement
      0x02dd0 0x02f88 0x01b8 ??? Unknown ???
      0x02f88 0x03388 0x0400 DTMF contacts, see RadioddityCodeplug::DTMFContactElement.
      0x03388 0x03780 0x03f8 ??? Unknown ???
      0x03780 0x05390 0x1c10 First 128 channels (bank 0), see @c RadioddityCodeplug::ChannelBankElement and GD77Codeplug::ChannelElement
      0x05390 0x07518 0x2188 ??? Unknown ???
      0x07518 0x07538 0x0020 Boot settings, see @c RadioddityCodeplug::BootSettingsElement.
      0x07538 0x07540 0x0008 Menu settings, see @c RadioddityCodeplug::MenuSettingsElement.
      0x07540 0x07560 0x0020 2 intro lines, @c RadioddityCodeplug::BootTextElement.
      0x07560 0x07590 0x0030 ??? Unknown ???
      0x07590 0x075c8 0x0038 VFO A settings @c RadioddityCodeplug::VFOChannelElement
      0x075c8 0x07600 0x0038 VFO B settings @c RadioddityCodeplug::VFOChannelElement
      0x07600 0x07c00 0x0600 ??? Unknown ???
      Second segment 0x08000-0x1e300
      0x08000 0x08010 0x0010 ??? Unknown ???
      0x08010 0x0af10 0x2f00 68 zones of 80 channels each, see @c RadioddityCodeplug::ZoneBankElement @c RadioddityCodeplug::ZoneElement.
      0x0af10 0x0b1b0 0x02a0 ??? Unknown ???
      0x0b1b0 0x17620 0xc470 Remaining 896 channels (bank 1-7), see @c RadioddityCodeplug::ChannelBankElement, @c GD77Codeplug::ChannelElement.
      0x17620 0x1d620 0x6000 1024 contacts, see @c GD77Codeplug::ContactElement.
      0x1d620 0x1eaa0 0x1480 64 RX group lists, see @c GD77Codeplug::GroupListBankElement, @c GD77Codeplug::GroupListElement.
      0x1eaa0 0x1eb00 0x0060 ??? Unknown ???
      * @ingroup gd77 */ class GD77Codeplug: public RadioddityCodeplug { Q_OBJECT public: /** Channel representation within the binary codeplug. * * Each channel requires 0x38b: * @verbinclude gd77_channel.txt */ class ChannelElement: public RadioddityCodeplug::ChannelElement { public: /** ARTS send. */ enum ARTSMode { ARTS_OFF = 0, ARTS_TX = 1, ARTS_RX = 2, ARTS_BOTH = 3 }; /** STE angle. */ enum STEAngle { STE_FREQUENCY = 0, ///< STE Frequency. STE_120DEG = 1, ///< 120 degree. STE_180DEG = 2, ///< 180 degree. STE_240DEG = 3 ///< 240 degree. }; /** PTT ID send. */ enum PTTId { PTTID_OFF = 0, PTTID_START = 1, PTTID_END = 2, PTTID_BOTH = 3 }; protected: /** Hidden Constructor. */ ChannelElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit ChannelElement(uint8_t *ptr); void clear(); /** Returns the ARTS mode. */ virtual ARTSMode artsMode() const; /** Sets the ARTS mode. */ virtual void setARTSMode(ARTSMode mode); /** Returns the STE angle. */ virtual STEAngle steAngle() const; /** Sets the STE angle. */ virtual void setSTEAngle(STEAngle angle); /** Returns the PTT ID mode. */ virtual PTTId pttIDMode() const; /** Sets the PTT ID mode. */ virtual void setPTTIDMode(PTTId mode); /** Returns @c true if the squech type is tight. */ virtual bool squelchIsTight() const; /** Enables/disables tight squelch. */ virtual void enableTightSquelch(bool enable); /** Returns @c true if lone worker is enabled. */ virtual bool loneWorker() const; /** Enables/disables lone worker. */ virtual void enableLoneWorker(bool enable); /** Returns @c true if auto scan is enabled. */ virtual bool autoscan() const; /** Enables/disables auto scan. */ virtual void enableAutoscan(bool enable); }; /** Specific codeplug representation of a DMR contact for the GD77. * * Memory layout of the contact (0x18b): * @verbinclude gd77_contact.txt */ class ContactElement: public RadioddityCodeplug::ContactElement { protected: /** Hidden constructor. */ ContactElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ ContactElement(uint8_t *ptr); /** Resets the contact. */ void clear(); /** Returns @c true if the contact is valid. */ bool isValid() const; /** Marks the entry as valid/invalid. */ virtual void markValid(bool valid=true); bool fromContactObj(const DMRContact *obj, Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Some internal offsets. */ struct Offset: RadioddityCodeplug::ContactElement::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int validFlag() { return 0x0017; } /// @endcond }; }; /** Represents an RX group list within the codeplug. * * The group list is encoded as (size 0x50b): * @verbinclude gd77_grouplist.txt */ class GroupListElement: public RadioddityCodeplug::GroupListElement { protected: /** Hidden constructor. */ GroupListElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ GroupListElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0050; } virtual bool linkRXGroupListObj(unsigned int ncnt, RXGroupList *lst, Context &ctx, const ErrorStack &err = ErrorStack()) const; virtual bool fromRXGroupListObj(const RXGroupList *lst, Context &ctx, const ErrorStack &err = ErrorStack()); public: /** Some limits for the group list. */ struct Limit: public RadioddityCodeplug::GroupListElement { static constexpr unsigned int memberCount() { return 32; } ///< Maximum number of entries. }; }; /** Table of RX group lists. * * The RX group list table constsis of a table of number of members per group list and the actual * list of RX group lists. The former also acts as a byte map for valid RX group lists. If 0, the * group list is disabled, if 1 the group list is empty, etc. So the entry is N+1, where N is the * number of entries per group list. * * Encoding of group list table: * @verbinclude gd77_grouplistbank.txt*/ class GroupListBankElement: public RadioddityCodeplug::GroupListBankElement { protected: /** Hidden constructor. */ GroupListBankElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ GroupListBankElement(uint8_t *ptr); /** The size of the group list bank. */ static constexpr unsigned int size() { return 0x1480; } uint8_t *get(unsigned n) const; public: /** Some limits for the group list bank. */ struct Limit: public RadioddityCodeplug::GroupListBankElement::Limit { static constexpr unsigned int groupListCount() { return 64; } ///< Number of group lists. }; }; /** Represents a single scan list within the GD77 codeplug. * * Encoding of scan list (size: 0x58b): * @verbinclude gd77_scanlist.txt */ class ScanListElement: public RadioddityCodeplug::ScanListElement { protected: /** Hidden constructor. */ ScanListElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ ScanListElement(uint8_t *ptr); /** Resets the scan list. */ void clear(); }; /** Bank of scan lists for the GD77. * * Encoding of scan list table (size 0x1640b): * @verbinclude gd77_scanlistbank.txt */ class ScanListBankElement: public RadioddityCodeplug::ScanListBankElement { protected: /** Hidden constructor. */ ScanListBankElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ ScanListBankElement(uint8_t *ptr); /** The size of the scan list bank. */ static constexpr unsigned int size() { return 0x1640; } /** Clears the scan list bank. */ void clear(); uint8_t *get(unsigned n) const; public: /** Some limits for the scan list bank. */ struct Limit: public RadioddityCodeplug::ScanListBankElement::Limit { static constexpr unsigned int scanListCount() { return 64; } ///< Maximum number of scan lists. }; protected: /** Internal offsets within the element. */ struct Offset: public RadioddityCodeplug::ScanListBankElement::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int bytemap() { return 0x0000; } static constexpr unsigned int scanLists() { return 0x0040; } /// @endcond }; }; public: /** Constructs an empty codeplug for the GD-77. */ explicit GD77Codeplug(QObject *parent=nullptr); public: void clearGeneralSettings(); bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()); void clearButtonSettings(); bool encodeButtonSettings(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack()); bool decodeButtonSettings(Context &ctx, const ErrorStack &err=ErrorStack()); void clearMessages(); bool encodeMessages(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack()); bool decodeMessages(Context &ctx, const ErrorStack &err=ErrorStack()); void clearScanLists(); bool encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack()); void clearContacts(); bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack()); void clearDTMFContacts(); bool encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack()); void clearChannels(); bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()); void clearBootSettings(); void clearMenuSettings(); void clearBootText(); bool encodeBootText(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeBootText(Context &ctx, const ErrorStack &err=ErrorStack()); void clearVFOSettings(); void clearZones(); bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createZones(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()); void clearGroupLists(); bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); void clearEncryption(); bool encodeEncryption(const Flags &flags, Context &ctx, const ErrorStack &err); bool createEncryption(Context &ctx, const ErrorStack &err); bool linkEncryption(Context &ctx, const ErrorStack &err); public: /** Some limits for the GD77 codeplug. */ struct Limit { static constexpr unsigned int dtmfContactCount() { return 32; } ///< Maximum number of DTMF contacts. static constexpr unsigned int channelBankCount() { return 8; } ///< Number of channel banks. static constexpr unsigned int channelCount() { return 1024; } ///< Maximum number of channels. static constexpr unsigned int contactCount() { return 1024; } ///< Maximum number of contacts. }; protected: /** Some internal offsets within the codeplug. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int settings() { return 0x0000e0; } static constexpr unsigned int buttons() { return 0x000108; } static constexpr unsigned int messages() { return 0x000128; } static constexpr unsigned int encryption() { return 0x001370; } static constexpr unsigned int scanListBank() { return 0x001790; } static constexpr unsigned int dtmfContacts() { return 0x002f88; } static constexpr unsigned int channelBank0() { return 0x003780; } // Channels 1-128 static constexpr unsigned int bootSettings() { return 0x007518; } static constexpr unsigned int bootText() { return 0x007540; } static constexpr unsigned int menuSettings() { return 0x007538; } static constexpr unsigned int vfoA() { return 0x007590; } static constexpr unsigned int vfoB() { return 0x0075c8; } static constexpr unsigned int zoneBank() { return 0x008010; } static constexpr unsigned int channelBank1() { return 0x00b1b0; } // Channels 129-1024 static constexpr unsigned int contacts() { return 0x017620; } static constexpr unsigned int groupListBank() { return 0x01d620; } /// @endcond }; }; #endif // GD77_CODEPLUG_HH qdmr-0.12.3/lib/gd77_filereader.cc000066400000000000000000000037401501654372000165560ustar00rootroot00000000000000#include "gd77_filereader.hh" #include #include #define SEGMENT0_ADDR 0x00000080 #define SEGMENT0_SIZE 0x00007b80 #define SEGMENT1_ADDR 0x00008000 #define SEGMENT1_SIZE 0x00016300 bool GD77FileReader::read(const QString &filename, GD77Codeplug *codeplug, const ErrorStack &err) { // Check file properties QFileInfo info(filename); if (! info.exists()) { errMsg(err) << "Cannot open file '" << filename << "': File does not exisist."; return false; } if (131072 != info.size()) { errMsg(err) << "Cannot read codeplug file '" << filename << "': File size is not 131072 bytes."; return false; } // Open file QFile file(filename); if (! file.open(QFile::ReadOnly)) { errMsg(err) << "Cannot open file '" << filename << "': " << file.errorString() << "."; return false; } // Read file content if (! file.seek(SEGMENT0_ADDR)) { errMsg(err) << "Cannot read codeplug file '" << filename << "': Cannot seek within file: " << file.errorString() << "."; file.close(); return false; } char *ptr = (char *)codeplug->data(SEGMENT0_ADDR); size_t n = SEGMENT0_SIZE; while (0 < n) { int nread = file.read(ptr, n); if (0 > nread) { errMsg(err) << "Cannot read codeplug file '" << filename << "': " << file.errorString() << "."; file.close(); return false; } n -= nread; ptr += nread; } if (! file.seek(SEGMENT1_ADDR)) { errMsg(err) << "Cannot read codeplug file '" << filename << "': Cannot seek within file: " << file.errorString() << "."; file.close(); return false; } ptr = (char *)codeplug->data(SEGMENT1_ADDR); n = SEGMENT1_SIZE; while (0 < n) { int nread = file.read(ptr, n); if (0 > nread) { errMsg(err) << "Cannot read codeplug file '" << filename << "': " << file.errorString() << "."; file.close(); return false; } n -= nread; ptr += nread; } return true; } qdmr-0.12.3/lib/gd77_filereader.hh000066400000000000000000000021631501654372000165660ustar00rootroot00000000000000#ifndef GD77FILEREADER_HH #define GD77FILEREADER_HH #include "gd77_codeplug.hh" /** Methods to read manufacturer codeplug files. * * The file format of the stock CPS is pretty simple. It is a one-to-one dump of the codeplug * data as written to the device. This makes the decoding of the manufacturer codeplug files very * easy. Some memory regions, however, are not written to the deivice although they are present in * the codeplug file. * * * * * *
      Start End Size
      0x00080 0x07c00 0x07b80
      0x08000 0x1e300 0x16300
      * * @ingroup gd77 */ class GD77FileReader { public: /** Reads manufacturer codeplug file into given codeplug object. * @param filename Specifies the file to read. * @param codeplug Specifies the codeplug object to store read codeplug. * @param err The error stack. * @returns @c true on success and @c false on error. */ static bool read(const QString &filename, GD77Codeplug *codeplug, const ErrorStack &err=ErrorStack()); }; #endif // GD77FILEREADER_HH qdmr-0.12.3/lib/gd77_limits.cc000066400000000000000000000201301501654372000157450ustar00rootroot00000000000000#include "gd77_limits.hh" #include "gd77_codeplug.hh" #include "channel.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "zone.hh" #include "scanlist.hh" #include "roamingzone.hh" GD77Limits::GD77Limits(QObject *parent) : RadioLimits(true, parent) { // Define limits for call-sign DB _hasCallSignDB = true; _callSignDBImplemented = true; _numCallSignDBEntries = 10920; // Define limits for satellite config _hasSatelliteConfig = false; _satelliteConfigImplemented = false; _numSatellites = 0; /* Define limits for the general settings. */ add("settings", new RadioLimitItem{ { "introLine1", new RadioLimitString(-1, 16, RadioLimitString::ASCII) }, { "introLine2", new RadioLimitString(-1, 16, RadioLimitString::ASCII) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, -1) } }); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList { { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject { {"name", new RadioLimitString(1,8, RadioLimitString::ASCII) }, {"id", new RadioLimitUInt(0, 16777215)} } } /// @todo check default radio ID. }); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, 1024, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum{ (unsigned)DMRContact::PrivateCall, (unsigned)DMRContact::GroupCall, (unsigned)DMRContact::AllCall }}, { "number", new RadioLimitUInt(0, 16777215) } } }, { DTMFContact::staticMetaObject, 0, 32, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "ring", new RadioLimitBool() }, { "number", new RadioLimitStringRegEx("^[0-9A-Fa-f]+$") } } } }); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 1, 76, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "contacts", new RadioLimitGroupCallRefList(1, 32) } })); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, 1024, // < up to 1024 channels new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, 3825, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRefIgnored()}, {"openGD77", new RadioLimitIgnored()}, {"tyt", new RadioLimitIgnored()} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1,16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, 3825, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, true)}, {"aprs", new RadioLimitObjRefIgnored()}, {"roaming", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } )); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone( 16, { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions }) ) ); /* Ignore scan lists. */ add("scanlists", new RadioLimitList( ScanList::staticMetaObject, 0, 64, new RadioLimitObject{ { "name", new RadioLimitString(1, 15, RadioLimitString::ASCII) }, { "primary", new RadioLimitObjRef(Channel::staticMetaObject, false) }, { "secondary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "revert", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "channels", new RadioLimitRefList(0, 32, Channel::staticMetaObject) } })); /* Check encryption keys. */ add("commercial", new RadioLimitItem { {"encryptionKeys", new RadioLimitList( BasicEncryptionKey::staticMetaObject, 0, RadioddityCodeplug::EncryptionElement::Limit::keyCount(), new RadioLimitObject { {"name", new RadioLimitIgnored()}, {"key", new RadioLimitStringRegEx("[0-9a-fA-F]{8}")} })} }); /* Ignore positioning systems. */ add("positioning", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored()) ); /* Ignore roaming zones. */ add("roaming", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored()) ); } qdmr-0.12.3/lib/gd77_limits.hh000066400000000000000000000004541501654372000157660ustar00rootroot00000000000000#ifndef GD77LIMITS_HH #define GD77LIMITS_HH #include "radiolimits.hh" /** Implements the limits for the Radioddity GD77. * @ingroup gd77 */ class GD77Limits: public RadioLimits { Q_OBJECT public: /** Constructor. */ explicit GD77Limits(QObject *parent=nullptr); }; #endif // GD77LIMITS_HH qdmr-0.12.3/lib/gpssystem.cc000066400000000000000000000373071501654372000156700ustar00rootroot00000000000000#include "gpssystem.hh" #include "contact.hh" #include "channel.hh" #include "logger.hh" #include "utils.hh" /* ********************************************************************************************* * * Implementation of PositioningSystem * ********************************************************************************************* */ PositioningSystem::PositioningSystem(QObject *parent) : ConfigObject(parent), _period(0) { // pass... } PositioningSystem::PositioningSystem(const QString &name, unsigned period, QObject *parent) : ConfigObject(name, parent), _period(period) { // pass... } PositioningSystem::~PositioningSystem() { // pass... } unsigned PositioningSystem::period() const { return _period; } void PositioningSystem::setPeriod(unsigned period) { _period = period; emit modified(this); } bool PositioningSystem::populate(YAML::Node &node, const ConfigItem::Context &context, const ErrorStack &err) { if (! ConfigObject::populate(node, context, err)) return false; return true; } bool PositioningSystem::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) { if (! node) return false; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot parse positioning system: Expected object with one child."; return false; } YAML::Node pos = node.begin()->second; if (pos && (!pos["period"])) { logWarn() << pos.Mark().line << ":" << pos.Mark().column << ": Positioning system has no period."; } return ConfigObject::parse(pos, ctx, err); } bool PositioningSystem::link(const YAML::Node &node, const Context &ctx, const ErrorStack &err) { return ConfigObject::link(node.begin()->second, ctx, err); } void PositioningSystem::onReferenceModified() { emit modified(this); } /* ********************************************************************************************* * * Implementation of GPSSystem * ********************************************************************************************* */ GPSSystem::GPSSystem(QObject *parent) : PositioningSystem(parent), _contact(), _revertChannel() { // Allow revert channel to take a reference to the SelectedChannel singleton _revertChannel.allow(SelectedChannel::get()->metaObject()); // Register '!selected' tag for revert channel Context::setTag(staticMetaObject.className(), "revert", "!selected", SelectedChannel::get()); // By default, selected channel is revert channel resetRevertChannel(); // Connect signals connect(&_contact, SIGNAL(modified()), this, SLOT(onReferenceModified())); connect(&_revertChannel, SIGNAL(modified()), this, SLOT(onReferenceModified())); } GPSSystem::GPSSystem(const QString &name, DMRContact *contact, DMRChannel *revertChannel, unsigned period, QObject *parent) : PositioningSystem(name, period, parent), _contact(), _revertChannel() { // Allow revert channel to take a reference to the SelectedChannel singleton _revertChannel.allow(SelectedChannel::get()->metaObject()); // Register '!selected' tag for revert channel Context::setTag(staticMetaObject.className(), "revert", "!selected", SelectedChannel::get()); // Set references. _contact.set(contact); setRevertChannel(revertChannel); // Connect signals connect(&_contact, SIGNAL(modified()), this, SLOT(onReferenceModified())); connect(&_revertChannel, SIGNAL(modified()), this, SLOT(onReferenceModified())); } ConfigItem * GPSSystem::clone() const { GPSSystem *sys = new GPSSystem(); if (! sys->copy(*this)) { sys->deleteLater(); return nullptr; } return sys; } bool GPSSystem::hasContact() const { return ! _contact.isNull(); } DMRContact * GPSSystem::contactObj() const { return _contact.as(); } void GPSSystem::setContactObj(DMRContact *contact) { _contact.set(contact); } void GPSSystem::setContact(DMRContactReference *contact) { _contact.copy(contact); } const DMRContactReference * GPSSystem::contact() const { return &_contact; } DMRContactReference * GPSSystem::contact() { return &_contact; } bool GPSSystem::hasRevertChannel() const { return _revertChannel.is(); } DMRChannel * GPSSystem::revertChannel() const { return _revertChannel.as(); } void GPSSystem::setRevertChannel(DMRChannel *channel) { if (nullptr == channel) resetRevertChannel(); else _revertChannel.set(channel); } void GPSSystem::resetRevertChannel() { _revertChannel.set(SelectedChannel::get()); } const DMRChannelReference* GPSSystem::revert() const { return &_revertChannel; } DMRChannelReference* GPSSystem::revert() { return &_revertChannel; } YAML::Node GPSSystem::serialize(const Context &context, const ErrorStack &err) { YAML::Node node = PositioningSystem::serialize(context, err); if (node.IsNull()) return node; YAML::Node type; type["dmr"] = node; return type; } /* ********************************************************************************************* * * Implementation of APRSSystem * ********************************************************************************************* */ APRSSystem::APRSSystem(QObject *parent) : PositioningSystem(parent), _channel(), _destination(), _destSSID(0), _source(), _srcSSID(0), _path(), _icon(Icon::None), _message(), _anytone(nullptr), _openGD77(nullptr) { // Allow revert channel to take a reference to the SelectedChannel singleton _channel.allow(SelectedChannel::get()->metaObject()); // Register '!selected' tag for revert channel Context::setTag(staticMetaObject.className(), "revert", "!selected", SelectedChannel::get()); // By default, selected channel is revert channel resetRevertChannel(); // Connect to channel reference connect(&_channel, SIGNAL(modified()), this, SLOT(onReferenceModified())); } APRSSystem::APRSSystem(const QString &name, FMChannel *channel, const QString &dest, unsigned destSSID, const QString &src, unsigned srcSSID, const QString &path, Icon icon, const QString &message, unsigned period, QObject *parent) : PositioningSystem(name, period, parent), _channel(), _destination(dest), _destSSID(destSSID), _source(src), _srcSSID(srcSSID), _path(path), _icon(icon), _message(message), _anytone(nullptr), _openGD77(nullptr) { // Allow revert channel to take a reference to the SelectedChannel singleton _channel.allow(SelectedChannel::get()->metaObject()); // Register '!selected' tag for revert channel Context::setTag(staticMetaObject.className(), "revert", "!selected", SelectedChannel::get()); // Set revert channel setRevertChannel(channel); // Connect to channel reference connect(&_channel, SIGNAL(modified()), this, SLOT(onReferenceModified())); } bool APRSSystem::copy(const ConfigItem &other) { const APRSSystem *sys = other.as(); if ((nullptr == sys) || (! PositioningSystem::copy(other))) return false; _destination = sys->destination(); _destSSID = sys->_destSSID; _source = sys->_source; _srcSSID = sys->_srcSSID; _path = sys->_path; return true; } ConfigItem * APRSSystem::clone() const { APRSSystem *sys = new APRSSystem(); if (! sys->copy(*this)) { sys->deleteLater(); return nullptr; } return sys; } bool APRSSystem::hasRevertChannel() const { return _channel.is(); } FMChannel * APRSSystem::revertChannel() const { return _channel.as(); } void APRSSystem::setRevertChannel(FMChannel *channel) { if (nullptr == channel) resetRevertChannel(); else _channel.set(channel); } void APRSSystem::resetRevertChannel() { _channel.set(SelectedChannel::get()); } const FMChannelReference * APRSSystem::revert() const { return &_channel; } FMChannelReference * APRSSystem::revert() { return &_channel; } const QString & APRSSystem::destination() const { return _destination; } unsigned APRSSystem::destSSID() const { return _destSSID; } void APRSSystem::setDestination(const QString &call, unsigned ssid) { _destination = call; _destSSID = ssid; } void APRSSystem::setDestination(const QString &call) { _destination = call; } void APRSSystem::setDestSSID(unsigned int ssid) { _destSSID = ssid; } const QString & APRSSystem::source() const { return _source; } unsigned APRSSystem::srcSSID() const { return _srcSSID; } void APRSSystem::setSource(const QString &call, unsigned ssid) { _source = call; _srcSSID = ssid; } void APRSSystem::setSource(const QString &call) { _source = call; } void APRSSystem::setSrcSSID(unsigned ssid) { _srcSSID = ssid; } const QString & APRSSystem::path() const { return _path; } void APRSSystem::setPath(const QString &path) { _path = path.toUpper(); _path.replace(" ",""); } APRSSystem::Icon APRSSystem::icon() const { return _icon; } void APRSSystem::setIcon(Icon icon) { _icon = icon; } const QString & APRSSystem::message() const { return _message; } void APRSSystem::setMessage(const QString &msg) { _message = msg; emit modified(this); } AnytoneFMAPRSSettingsExtension * APRSSystem::anytoneExtension() const { return _anytone; } void APRSSystem::setAnytoneExtension(AnytoneFMAPRSSettingsExtension *ext) { if (_anytone) { _anytone->deleteLater(); _anytone = nullptr; } if (ext) { _anytone = ext; ext->setParent(this); connect(ext, SIGNAL(modified(ConfigItem *)), this, SIGNAL(modified(ConfigItem *))); } } OpenGD77APRSSystemExtension * APRSSystem::openGD77Extension() const { return _openGD77; } void APRSSystem::setOpenGD77Extension(OpenGD77APRSSystemExtension *ext) { if (_openGD77) { _openGD77->deleteLater(); _openGD77 = nullptr; } if (ext) { _openGD77 = ext; ext->setParent(this); connect(ext, SIGNAL(modified(ConfigItem *)), this, SIGNAL(modified(ConfigItem *))); } } YAML::Node APRSSystem::serialize(const Context &context, const ErrorStack &err) { YAML::Node node = PositioningSystem::serialize(context, err); if (node.IsNull()) return node; YAML::Node type; type["aprs"] = node; return type; } bool APRSSystem::populate(YAML::Node &node, const Context &context, const ErrorStack &err) { if (! PositioningSystem::populate(node, context, err)) return false; node["destination"] = QString("%1-%2").arg(_destination).arg(_destSSID).toStdString(); node["source"] = QString("%1-%2").arg(_source).arg(_srcSSID).toStdString(); QStringList path; QRegExp pattern("([A-Za-z0-9]+-[0-9]+)"); int idx = 0; while (0 <= (idx = pattern.indexIn(_path, idx))) { path.append(pattern.cap(1)); idx += pattern.matchedLength(); } if (path.count()) { YAML::Node list = YAML::Node(YAML::NodeType::Sequence); list.SetStyle(YAML::EmitterStyle::Flow); foreach (QString call, path) { list.push_back(call.toStdString()); } node["path"] = list; } return true; } bool APRSSystem::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) { if (! node) return false; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot parse APRS system: Expected object with one child."; return false; } YAML::Node sys = node.begin()->second; if (sys["source"] && sys["source"].IsScalar()) { QString source = QString::fromStdString(sys["source"].as()); QRegExp pattern("^([A-Z0-9]+)-(1?[0-9])$"); if (! pattern.exactMatch(source)) { errMsg(err) << sys.Mark().line << ":" << sys.Mark().column << ": Cannot parse APRS system: '" << source << "' not a valid source call and SSID."; return false; } setSource(pattern.cap(1), pattern.cap(2).toUInt()); } else { errMsg(err) << sys.Mark().line << ":" << sys.Mark().column << ": Cannot parse APRS system: No source call+SSID specified."; return false; } if (sys["destination"] && sys["destination"].IsScalar()) { QString dest = QString::fromStdString(sys["destination"].as()); QRegExp pattern("^([A-Z0-9]+)-(1?[0-9])$"); if (! pattern.exactMatch(dest)) { errMsg(err) << sys.Mark().line << ":" << sys.Mark().column << ": Cannot parse APRS system: '" << dest << "' not a valid destination call and SSID."; return false; } setDestination(pattern.cap(1), pattern.cap(2).toUInt()); } else { errMsg(err) << sys.Mark().line << ":" << sys.Mark().column << ": Cannot parse APRS system: No destination call+SSID specified."; return false; } if (sys["path"] && sys["path"].IsSequence()) { QStringList path; for (YAML::const_iterator it=sys["path"].begin(); it!=sys["path"].end(); it++) { if (it->IsScalar()) path.append(QString::fromStdString(it->as())); } setPath(path.join(",")); } return PositioningSystem::parse(node, ctx, err); } /* ********************************************************************************************* * * Implementation of GPSSystems table * ********************************************************************************************* */ PositioningSystems::PositioningSystems(QObject *parent) : ConfigObjectList(PositioningSystem::staticMetaObject, parent) { // pass... } PositioningSystem * PositioningSystems::system(int idx) const { if (ConfigItem *obj = get(idx)) return obj->as(); return nullptr; } int PositioningSystems::add(ConfigObject *obj, int row, bool unique) { if (obj && obj->is()) return ConfigObjectList::add(obj, row, unique); return -1; } int PositioningSystems::gpsCount() const { int c=0; for (int i=0; i<_items.size(); i++) if (_items.at(i)->is()) c++; return c; } int PositioningSystems::indexOfGPSSys(const GPSSystem *gps) const { if (! _items.contains((GPSSystem *)gps)) return -1; int idx=0; for (int i=0; iis()) idx++; } return -1; } GPSSystem * PositioningSystems::gpsSystem(int idx) const { if ((0>idx) || (idx >= _items.size())) return nullptr; for (int i=0; i<_items.size(); i++) { if (_items.at(i)->is()) { if (0==idx) return _items.at(i)->as(); else idx--; } } return nullptr; } int PositioningSystems::aprsCount() const { int c=0; for (int i=0; iis()) c++; } return c; } int PositioningSystems::indexOfAPRSSys(APRSSystem *aprs) const { if (! _items.contains(aprs)) return -1; int idx=0; for (int i=0; iis()) idx++; } return -1; } APRSSystem * PositioningSystems::aprsSystem(int idx) const { if ((0>idx) || (idx >= _items.size())) return nullptr; for (int i=0; i<_items.size(); i++) { if (_items.at(i)->is()) { if (0==idx) return _items.at(i)->as(); else idx--; } } return nullptr; } ConfigItem * PositioningSystems::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) if (! node) return nullptr; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create positioning system: Expected object with one child."; return nullptr; } QString type = QString::fromStdString(node.begin()->first.as()); if ("dmr" == type) { return new GPSSystem(); } else if ("aprs"==type) { return new APRSSystem(); } errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create positioning system: Unknown type '" << type << "'."; return nullptr; } qdmr-0.12.3/lib/gpssystem.hh000066400000000000000000000313701501654372000156740ustar00rootroot00000000000000#ifndef GPSSYSTEM_H #define GPSSYSTEM_H #include "configreference.hh" #include #include "anytone_extension.hh" #include "opengd77_extension.hh" class Config; class DMRContact; class DMRChannel; class FMChannel; /** Base class of the positioning systems, that is APRS and DMR position reporting system. * @ingroup conf */ class PositioningSystem: public ConfigObject { Q_OBJECT Q_CLASSINFO("IdPrefix", "aprs") /** The update period in seconds. */ Q_PROPERTY(unsigned period READ period WRITE setPeriod) protected: /** Default constructor. */ explicit PositioningSystem(QObject *parent=nullptr); /** Hidden constructor. * The PositioningSystem class is not instantiated directly, use either @c GPSSystem or * @c APRSSystem instead. * @param name Specifies the name of the system. * @param period Specifies the auto-update period in seconds. * @param parent Specified the QObject parent object. */ PositioningSystem(const QString &name, unsigned period=300, QObject *parent=nullptr); public: /** Destructor. */ virtual ~PositioningSystem(); /** Returns the update period in seconds. */ unsigned period() const; /** Sets the update period in seconds. */ void setPeriod(unsigned period); public: bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack()); bool link(const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack()); protected: bool populate(YAML::Node &node, const ConfigItem::Context &context, const ErrorStack &err=ErrorStack()); protected slots: /** Gets called, whenever a reference is modified. */ void onReferenceModified(); protected: /** Holds the update period in seconds. */ unsigned _period; }; /** This class represents a GPS signalling system within the codeplug. * @ingroup conf */ class GPSSystem : public PositioningSystem { Q_OBJECT /** References the destination contact. */ Q_PROPERTY(DMRContactReference* contact READ contact WRITE setContact) /** References the revert channel. */ Q_PROPERTY(DMRChannelReference* revert READ revert) public: /** Default constructor. */ Q_INVOKABLE explicit GPSSystem(QObject *parent=nullptr); /** Constructor. * * Please note, that a contact needs to be set in order for the GPS system to work properly. * * @param name Specifies the name of the GPS system. * @param contact Specifies the contact, the GPS position is sent to. * @param revertChannel Specifies the channel on which the GPS is sent on. If @c nullptr, the GPS * data is sent on the current channel. * @param period Specifies the update period in seconds. * @param parent Specifies the QObject parent object. */ GPSSystem(const QString &name, DMRContact *contact=nullptr, DMRChannel *revertChannel = nullptr, unsigned period=300, QObject *parent = nullptr); ConfigItem *clone() const; /** Returns @c true if a contact is set for the GPS system. */ bool hasContact() const; /** Returns the destination contact for the GPS information or @c nullptr if not set. */ DMRContact *contactObj() const; /** Sets the destination contact for the GPS information. */ void setContactObj(DMRContact *contactObj); /** Returns the reference to the destination contact. */ const DMRContactReference *contact() const; /** Returns the reference to the destination contact. */ DMRContactReference *contact(); /** Sets the reference to the destination contact for the GPS information. */ void setContact(DMRContactReference *contactObj); /** Returns @c true if the GPS system has a revert channel set. If not, the GPS information will * be sent on the current channel. */ bool hasRevertChannel() const; /** Returns the revert channel for the GPS information or @c nullptr if not set. */ DMRChannel *revertChannel() const; /** Sets the revert channel for the GPS information to be sent on. */ void setRevertChannel(DMRChannel *channel); /** Resets the revert channel to the current channel. */ void resetRevertChannel(); /** Returns a reference to the revert channel. */ const DMRChannelReference *revert() const; /** Returns a reference to the revert channel. */ DMRChannelReference *revert(); public: YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack()); protected: /** Holds the destination contact for the GPS information. */ DMRContactReference _contact; /** Holds the revert channel on which the GPS information is sent on. */ DMRChannelReference _revertChannel; }; /** Represents an APRS system within the generic config. * @ingroup conf */ class APRSSystem: public PositioningSystem { Q_OBJECT /** The transmit channel. */ Q_PROPERTY(FMChannelReference* revert READ revert) /** The destination call. */ Q_PROPERTY(QString destination READ destination WRITE setDestination SCRIPTABLE false) /** The destination SSID. */ Q_PROPERTY(unsigned int destSSID READ destSSID WRITE setDestSSID SCRIPTABLE false) /** The source call. */ Q_PROPERTY(QString source READ source WRITE setSource SCRIPTABLE false) /** The source SSID. */ Q_PROPERTY(unsigned int srcSSID READ srcSSID WRITE setSrcSSID SCRIPTABLE false) /** The APRS path as a string. */ Q_PROPERTY(QString path READ path WRITE setPath SCRIPTABLE false) /** The APRS icon. */ Q_PROPERTY(Icon icon READ icon WRITE setIcon) /** An optional text message. */ Q_PROPERTY(QString message READ message WRITE setMessage) /** Anytone sepecific settings. */ Q_PROPERTY(AnytoneFMAPRSSettingsExtension *anytone READ anytoneExtension WRITE setAnytoneExtension) /** OpenGD77 sepecific settings. */ Q_PROPERTY(OpenGD77APRSSystemExtension *opengd77 READ openGD77Extension WRITE setOpenGD77Extension) public: static const unsigned PRIMARY_TABLE = (0<<8); ///< Primary icon table flag. static const unsigned SECONDARY_TABLE = (1<<8); ///< Secondary icon table flag. static const unsigned TABLE_MASK = (3<<8); ///< Bitmask for icon table flags. static const unsigned ICON_MASK = 0x7f; ///< Bitmask for the icon table entry. /** All implemented APRS icons. */ enum class Icon { PoliceStation = (PRIMARY_TABLE | 0), None, Digipeater, Phone, DXCluster, HFGateway, SmallPlane, MobileSatelliteStation, WheelChair, Snowmobile, RedCross, BoyScout, Home, X, RedDot, Circle0, Circle1, Circle2, Circle3, Circle4, Circle5, Circle6, Circle7, Circle8, Circle9, Fire, Campground, Motorcycle, RailEngine, Car, FileServer, HCFuture, AidStation, BBS, Canoe, Eyeball = (PRIMARY_TABLE | 36), Tractor, GridSquare, Hotel, TCPIP, School = (PRIMARY_TABLE | 42), Logon, MacOS, NTSStation, Balloon, PoliceCar, TBD, RV, Shuttle, SSTV, Bus, ATV, WXService, Helo, Yacht, Windows, Jogger, Triangle, PBBS, LargePlane, WXStation, DishAntenna, Ambulance, Bike, ICP, FireStation, Horse, FireTruck, Glider, Hospital, IOTA, Jeep, SmallTruck, Laptop, MicE, Node, EOC, Rover, Grid, Antenna, PowerBoat, TruckStop, TruckLarge, Van, Water, XAPRS, Yagi, Shelter }; Q_ENUM(Icon) public: /** Default constructor. */ Q_INVOKABLE explicit APRSSystem(QObject *parent=nullptr); /** Constructor for a APRS system. * @param name Specifies the name of the APRS system. This property is just a name, it does not * affect the radio configuration. * @param channel Specifies the transmit channel. This property is not optional. A transmit * channel must be specified to obtain a working APRS system. * @param dest Specifies the destination call, APRS messages are sent to. Usually 'WIDE3' is a * reasonable setting. * @param destSSID Specifies the destination SSID. Usually 3 is a reasonable choice. * @param src Specifies the source call, usually you call has to be entered here. * @param srcSSID The source SSID, usually 7 is a reasonable choice of handhelds. * @param path Specifies the APRS path string. * @param icon Specifies the map icon to send. * @param message An optional message to send. * @param period Specifies the auto-update period in seconds. * @param parent Specifies the QObject parent object. */ APRSSystem(const QString &name, FMChannel *channel, const QString &dest, unsigned destSSID, const QString &src, unsigned srcSSID, const QString &path="", Icon icon=Icon::Jogger, const QString &message="", unsigned period=300, QObject *parent=nullptr); bool copy(const ConfigItem &other); ConfigItem *clone() const; /** Returns @c true if a revert channel is set. If false, the APRS information is send on the * current channel. */ bool hasRevertChannel() const; /** Returns the transmit channel of the APRS system. */ FMChannel *revertChannel() const; /** Sets the transmit channel of the APRS system. */ void setRevertChannel(FMChannel *revertChannel); /** Resets the revert channel to the current one */ void resetRevertChannel(); /** Returns a reference to the revert channel. */ const FMChannelReference *revert() const; /** Returns a reference to the revert channel. */ FMChannelReference *revert(); /** Returns the destination call. */ const QString &destination() const; /** Returns the destination SSID. */ unsigned destSSID() const; /** Sets the destination call and SSID. */ void setDestination(const QString &call, unsigned ssid); /** Sets the destination call. */ void setDestination(const QString &call); /** Sets the destination SSID. */ void setDestSSID(unsigned ssid); /** Returns the source call. */ const QString &source() const; /** Returns the source SSID. */ unsigned srcSSID() const; /** Sets the source call and SSID. */ void setSource(const QString &call, unsigned ssid); /** Sets the source call. */ void setSource(const QString &call); /** Sets the source SSID. */ void setSrcSSID(unsigned ssid); /** Returns the APRS path. */ const QString &path() const; /** Sets the APRS path. */ void setPath(const QString &path); /** Returns the map icon. */ Icon icon() const; /** Sets the map icon. */ void setIcon(Icon icon); /** Returns the optional message. */ const QString &message() const; /** Sets the optional APRS message text. */ void setMessage(const QString &msg); /** Returns the Anytone settings extension, if set. */ AnytoneFMAPRSSettingsExtension *anytoneExtension() const; /** Sets the Anytone settings extension. */ void setAnytoneExtension(AnytoneFMAPRSSettingsExtension *ext); /** Returns the OpenGD77 settings extension, if set. */ OpenGD77APRSSystemExtension *openGD77Extension() const; /** Sets the OpenGD77 settings extension. */ void setOpenGD77Extension(OpenGD77APRSSystemExtension *ext); public: YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack()); bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack()); protected: bool populate(YAML::Node &node, const Context &context, const ErrorStack &err=ErrorStack()); protected: /** A weak reference to the transmit channel. */ FMChannelReference _channel; /** Holds the destination call. */ QString _destination; /** Holds the destination SSID. */ unsigned _destSSID; /** Holds the source call. */ QString _source; /** Holds the source SSID. */ unsigned _srcSSID; /** Holds the APRS path string. */ QString _path; /** Holds the map icon. */ Icon _icon; /** Holds the optional message. */ QString _message; /** Owns the Anytone settings extension. */ AnytoneFMAPRSSettingsExtension *_anytone; /** Owns the OpenGD77 settings extension. */ OpenGD77APRSSystemExtension *_openGD77; }; /** The list of positioning systems. * @ingroup conf */ class PositioningSystems: public ConfigObjectList { Q_OBJECT public: /** Constructs an empty list of GPS systems. */ explicit PositioningSystems(QObject *parent=nullptr); /** Returns the positioning system at the specified index. */ PositioningSystem *system(int idx) const; int add(ConfigObject *obj, int row=-1, bool unique=true); /** Returns the number of defined GPS systems. */ int gpsCount() const; /** Returns the index of the GPS System. * This index in only within all defined GPS systems. */ int indexOfGPSSys(const GPSSystem *gps) const; /** Gets the GPS system at the specified index. * This index is only within all defined GPS systems. */ GPSSystem *gpsSystem(int idx) const; /** Returns the number of defined APRS system. */ int aprsCount() const; /** Returns the index of the specified APRS system. That index is only within all * defined APRS systems. */ int indexOfAPRSSys(APRSSystem *gps) const; /** Returns the APRS system at index @c idx. * That index is only within all defined APRS systems. */ APRSSystem *aprsSystem(int idx) const; public: ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); }; #endif // GPSSYSTEM_H qdmr-0.12.3/lib/hid_libusb.cc000066400000000000000000000226551501654372000157360ustar00rootroot00000000000000#include "hid_libusb.hh" #include "logger.hh" #define HID_INTERFACE 0 // interface index #define TIMEOUT_MSEC 500 // receive timeout #define MAX_RETRY 20 // Number of retries /* ********************************************************************************************* * * Implementation of HIDevice::Descriptor * ********************************************************************************************* */ HIDevice::Descriptor::Descriptor(const USBDeviceInfo &info, uint8_t bus, uint8_t device) : USBDeviceDescriptor(info, USBDeviceHandle(bus, device)) { // pass... } /* ********************************************************************************************* * * Implementation of HIDevice * ********************************************************************************************* */ HIDevice::HIDevice(const USBDeviceDescriptor &descr, const ErrorStack &err, QObject *parent) : QObject(parent), _ctx(nullptr), _dev(nullptr), _transfer(nullptr) { if (USBDeviceInfo::Class::HID != descr.interfaceClass()) { errMsg(err) << "Cannot connect to HID device using a non HID descriptor: " << descr.description() << "."; return; } int error = libusb_init(&_ctx); if (error < 0) { errMsg(err) << "Libusb init failed (" << error << "): " << libusb_strerror((enum libusb_error) error) << "."; return; } int num=0; libusb_device **lst; libusb_device *dev=nullptr; if (0 > (num = libusb_get_device_list(_ctx, &lst))) { errMsg(err) << "Cannot obtain list of USB devices."; libusb_exit(_ctx); _ctx = nullptr; return; } logDebug() << "Try to detect USB HID interface " << descr.description() << "."; USBDeviceHandle addr = descr.device().value(); for (int i=0; (i libusb_get_device_descriptor(lst[i],&usb_descr)) continue; if (descr.vendorId() != usb_descr.idVendor) continue; if (descr.productId() != usb_descr.idProduct) continue; logDebug() << "Matching device found at bus " << addr.bus << ", device " << addr.device << " with vendor ID " << QString::number(usb_descr.idVendor, 16) << " and product ID " << QString::number(usb_descr.idProduct, 16) << "."; libusb_ref_device(lst[i]); dev = lst[i]; } // Unref all devices and free list, matching device was referenced earlier libusb_free_device_list(lst, 1); if (nullptr == dev) { errMsg(err) << "No matching device found: " << descr.description() << "."; libusb_exit(_ctx); _ctx = nullptr; return; } if (0 > (error = libusb_open(dev, &_dev))) { errMsg(err) << "Cannot open device " << descr.description() << ": " << libusb_strerror((enum libusb_error) error) << "."; libusb_unref_device(dev); libusb_exit(_ctx); _ctx = nullptr; } if (libusb_kernel_driver_active(_dev, 0)) { error = libusb_detach_kernel_driver(_dev, 0); if (error < 0) { logWarn() << "Failed to detach kernel from HID interface (" << error << "): " << libusb_strerror((enum libusb_error) error) << ". Device claim will likely fail."; } } error = libusb_claim_interface(_dev, HID_INTERFACE); if (error < 0) { errMsg(err) << "Failed to claim HID interface (" << error << "): " << libusb_strerror((enum libusb_error) error) << "."; libusb_close(_dev); libusb_exit(_ctx); _dev = nullptr; _ctx = nullptr; } } HIDevice::~HIDevice() { close(); } QList HIDevice::detect(uint16_t vid, uint16_t pid) { QList res; int error, num; libusb_context *ctx; if (0 > (error = libusb_init(&ctx))) { logError() << "Libusb init failed (" << error << "): " << libusb_strerror((enum libusb_error) error) << "."; return res; } libusb_device **lst; if (0 == (num = libusb_get_device_list(ctx, &lst))) { logDebug() << "No USB devices found at all."; // unref devices and free list libusb_free_device_list(lst, 1); return res; } logDebug() << "Search for HID interfaces matching VID:PID " << QString::number(vid, 16) << ":" << QString::number(pid, 16) << "."; for (int i=0; (i> 8; if (nbytes > 0) memcpy(buf+4, data, nbytes); nbytes += 4; reply_len = write_read(buf, sizeof(buf), reply, sizeof(reply)); if (reply_len < 0) { err.take(_cbError); return false; } if (reply_len != sizeof(reply)) { errMsg(err) << "Short read: " << reply_len << " bytes instead of " << (int)sizeof(reply) << "!"; return false; } if (reply[0] != 3 || reply[1] != 0 || reply[3] != 0) { errMsg(err) << "Incorrect reply!"; return false; } if (reply[2] != rlength) { errMsg(err) << "Incorrect reply length " << reply[2] << ", expected " << rlength << "."; return false; } memcpy(rdata, reply+4, rlength); return true; } int HIDevice::write_read(const unsigned char *data, unsigned length, unsigned char *reply, unsigned rlength, const ErrorStack &err) { if (! _transfer) { // Allocate transfer descriptor on first invocation. _transfer = libusb_alloc_transfer(0); } libusb_fill_interrupt_transfer( _transfer, _dev, LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN, reply, rlength, read_callback, this, TIMEOUT_MSEC); size_t nretry = 0; again: _nbytes_received = 0; libusb_submit_transfer(_transfer); int result = libusb_control_transfer( _dev, LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, 0x09/*HID Set_Report*/, (2/*HID output*/ << 8) | 0, HID_INTERFACE, (unsigned char*)data, length, TIMEOUT_MSEC); if (result < 0) { err.take(_cbError); errMsg(err) << "Error " << result << " transmitting data via control transfer: " << libusb_strerror((enum libusb_error) result) << "."; _transfer = nullptr; return -1; } while (_nbytes_received == 0) { result = libusb_handle_events(_ctx); if (result < 0) { /* Break out of this loop only on fatal error.*/ if (result != LIBUSB_ERROR_BUSY && result != LIBUSB_ERROR_TIMEOUT && result != LIBUSB_ERROR_OVERFLOW && result != LIBUSB_ERROR_INTERRUPTED) { err.take(_cbError); errMsg(err) << "Error " <= MAX_RETRY) { logError() << "HID (libusb): Retry limit of " << MAX_RETRY << " exceeded."; } return _nbytes_received; } void HIDevice::read_callback(struct libusb_transfer *t) { HIDevice *self = (HIDevice *)t->user_data; switch (t->status) { case LIBUSB_TRANSFER_COMPLETED: memcpy(self->_receive_buf, t->buffer, t->actual_length); self->_nbytes_received = t->actual_length; break; case LIBUSB_TRANSFER_CANCELLED: self->_nbytes_received = LIBUSB_ERROR_INTERRUPTED; errMsg(self->_cbError) << libusb_error_name(LIBUSB_ERROR_INTERRUPTED); break; case LIBUSB_TRANSFER_NO_DEVICE: self->_nbytes_received = LIBUSB_ERROR_NO_DEVICE; errMsg(self->_cbError) << libusb_error_name(LIBUSB_ERROR_NO_DEVICE); break; case LIBUSB_TRANSFER_TIMED_OUT: self->_nbytes_received = LIBUSB_ERROR_TIMEOUT; errMsg(self->_cbError) << libusb_error_name(LIBUSB_ERROR_TIMEOUT); break; default: self->_nbytes_received = LIBUSB_ERROR_IO; errMsg(self->_cbError) << libusb_error_name(LIBUSB_ERROR_IO); break; } } qdmr-0.12.3/lib/hid_libusb.hh000066400000000000000000000043451501654372000157440ustar00rootroot00000000000000#ifndef HID_MACOS_HH #define HID_MACOS_HH #include #include #include "errorstack.hh" #include "radiointerface.hh" /** Implements the HID radio interface using libusb. * @ingroup rif */ class HIDevice: public QObject { Q_OBJECT public: /** Specialization to address a HI device uniquely. */ class Descriptor: public USBDeviceDescriptor { public: /** Constructor from interface info, bus number and device address. */ Descriptor(const USBDeviceInfo &info, uint8_t bus, uint8_t device); }; public: /** Connects to the device with given vendor and product ID. */ HIDevice(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr); /** Destructor. */ virtual ~HIDevice(); /** Returns @c true if the connection is established. */ bool isOpen() const; /** Send command/data to the device and store response in @c rdata. * @param data Pointer to the command/data to send. * @param nbytes The number of bytes to send. * @param rdata Pointer to receive buffer. * @param rlength Size of receive buffer. * @param err Passes an error stack to put error messages on. */ bool hid_send_recv(const unsigned char *data, unsigned nbytes, unsigned char *rdata, unsigned rlength, const ErrorStack &err=ErrorStack()); /** Close connection to device. */ void close(); public: /** Finds all HID interfaces with the specified VID/PID combination. */ static QList detect(uint16_t vid, uint16_t pid); protected: /** Internal used implementation of send_recv(). */ int write_read(const unsigned char *data, unsigned length, unsigned char *reply, unsigned rlength, const ErrorStack &err=ErrorStack()); /** Callback for response data. */ static void read_callback(struct libusb_transfer *t); protected: /** libusb context. */ libusb_context *_ctx; /** libusb device. */ libusb_device_handle *_dev; /** libusb async transfer descriptor. */ struct libusb_transfer *_transfer; /** Receive buffer. */ unsigned char _receive_buf[42]; /** Receive result. */ volatile int _nbytes_received; /** Internal used error stack for the static callback function. */ ErrorStack _cbError; }; #endif // HID_MACOS_HH qdmr-0.12.3/lib/hid_macos.cc000066400000000000000000000233241501654372000155520ustar00rootroot00000000000000#include "hid_macos.hh" #include #include #include #include #include #include #include /* ********************************************************************************************* * * Implementation of HIDevice::Descriptor * ********************************************************************************************* */ HIDevice::Descriptor::Descriptor(const USBDeviceInfo &info, uint32_t locationId, uint16_t device) : USBDeviceDescriptor(info, USBDeviceHandle((locationId>>24), device, locationId)) { // pass... } /* ********************************************************************************************* * * Implementation of HIDevice * ********************************************************************************************* */ HIDevice::HIDevice(const USBDeviceDescriptor &desc, const ErrorStack &err, QObject *parent) : QObject(parent), _dev(nullptr) { // Create the USB HID Manager. _HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); // Create an empty matching dictionary for filtering USB devices in our HID manager. CFMutableDictionaryRef matchDict = CFDictionaryCreateMutable( kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); // Specify the USB device manufacturer and product in our matching dictionary. int locationId = desc.device().value().locationId; CFDictionarySetValue(matchDict, CFSTR(kIOHIDLocationIDKey), CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &locationId)); // Apply the matching to our HID manager. IOHIDManagerSetDeviceMatching(_HIDManager, matchDict); CFRelease(matchDict); // The HID manager will use callbacks when specified USB devices are connected/disconnected. IOHIDManagerRegisterDeviceMatchingCallback(_HIDManager, &callback_open, this); IOHIDManagerRegisterDeviceRemovalCallback(_HIDManager, &callback_close, this); // Add the HID manager to the main run loop IOHIDManagerScheduleWithRunLoop(_HIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode); // Open the HID manager IOReturn IOReturn = IOHIDManagerOpen(_HIDManager, kIOHIDOptionsTypeNone); if (IOReturn != kIOReturnSuccess) { errMsg(err) << "Cannot open HID manager for USB device " << QString::number(locationId,16) << "."; IOHIDManagerUnscheduleFromRunLoop(_HIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode); _HIDManager = nullptr; return; } // Run loop until device found. for (int k=0; k<4; k++) { CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 0); if (_dev) return; usleep(10000); } errMsg(err) << "Cannot open USB device " << QString::number(locationId,16) << "."; IOHIDManagerUnscheduleFromRunLoop(_HIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode); IOHIDManagerClose(_HIDManager, kIOHIDOptionsTypeNone); _HIDManager = nullptr; } HIDevice::~HIDevice() { if (_dev) close(); if (_HIDManager) { IOHIDManagerUnscheduleFromRunLoop(_HIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode); IOHIDManagerClose(_HIDManager, kIOHIDOptionsTypeNone); } } QList HIDevice::detect(uint16_t vid, uint16_t pid) { /* Get the IO registry which has the system information for connected hardware. */ io_registry_entry_t entry = IORegistryGetRootEntry(kIOMasterPortDefault); if (entry == 0) return QList(); /* Get an iterator for the USB plane. */ io_iterator_t iter = 0; kern_return_t kret = IORegistryEntryCreateIterator(entry, kIOUSBPlane, kIORegistryIterateRecursively, &iter); if (kret != KERN_SUCCESS || iter == 0) return QList(); QList devices; io_service_t service = 0; while ((service = IOIteratorNext(iter))) { IOCFPlugInInterface **plug = NULL; IOUSBDeviceInterface **dev = NULL; io_string_t path; SInt32 score = 0; IOReturn ioret; /* Pull out IO Plugins for each iterator. */ kret = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plug, &score); IOObjectRelease(service); if (kret != KERN_SUCCESS || plug == NULL) continue; /* Get an IOUSBDeviceInterface for each USB device from the IO Plugin object. */ ioret = (*plug)->QueryInterface(plug, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (void **)&dev); (*plug)->Release(plug); if (ioret != kIOReturnSuccess || dev == NULL) continue; /* Print out the path in the IO Plane the device is at. */ if (IORegistryEntryGetPath(service, kIOServicePlane, path) != KERN_SUCCESS) { (*dev)->Release(dev); continue; } UInt16 dev_vendor, dev_product, dev_addr; UInt32 dev_location; if ( ((*dev)->GetDeviceVendor(dev, &dev_vendor) != kIOReturnSuccess) || (dev_vendor != vid) ) continue; if ( ((*dev)->GetDeviceProduct(dev, &dev_product) != kIOReturnSuccess) || (dev_product != pid) ) continue; if ((*dev)->GetLocationID(dev, &dev_location) != kIOReturnSuccess) continue; if ((*dev)->GetDeviceAddress(dev, &dev_addr) != kIOReturnSuccess) continue; logDebug() << "Found device with vid:pid " << QString::number(vid, 16) << ":" << QString::number(pid, 16) << " at " << QString::number(dev_location, 16) << "."; devices.append(HIDevice::Descriptor( USBDeviceInfo(USBDeviceInfo::Class::HID, vid, pid), dev_location, dev_addr)); /* All done with this device. */ (*dev)->Release(dev); } IOObjectRelease(iter); return devices; } bool HIDevice::isOpen() const { return nullptr != _dev; } // // Send a request to the device. // Store the reply into the rdata[] array. // Terminate in case of errors. // bool HIDevice::hid_send_recv(const unsigned char *data, unsigned nbytes, unsigned char *rdata, unsigned rlength, const ErrorStack &err) { unsigned char buf[42]; unsigned k; IOReturn result; if (! isOpen()) return false; memset(buf, 0, sizeof(buf)); buf[0] = 1; buf[1] = 0; buf[2] = nbytes; buf[3] = nbytes >> 8; if (nbytes > 0) memcpy(buf+4, data, nbytes); nbytes += 4; _nbytes_received = 0; memset(_receive_buf, 0, sizeof(_receive_buf)); uint retrycount = 0; again: // Write to HID device. result = IOHIDDeviceSetReport(_dev, kIOHIDReportTypeOutput, 0, buf, sizeof(buf)); if (result != kIOReturnSuccess) { errMsg(err) << "HID output error: " << result << "!"; return false; } // Run application loop until reply received. CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 0); for (k = 0; _nbytes_received <= 0; k++) { usleep(100); CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 0); if (k >= 1000) { retrycount++; if (retrycount<100) goto again; errMsg(err) << "HID IO error: Exceeded max. retry count."; return false; } } usleep(100); if (_nbytes_received != sizeof(_receive_buf)) { errMsg(err) << "Short read: " << _nbytes_received << " bytes instead of " << (int)sizeof(_receive_buf) << "!"; return false; } if ((_receive_buf[0] != 3) || (_receive_buf[1] != 0) || (_receive_buf[3] != 0)) { errMsg(err) << "Incorrect reply. Expected {3,0,0}, got {" << int(_receive_buf[0]) << "," << int(_receive_buf[1]) << "," << int(_receive_buf[3]) << "}."; return false; } if (_receive_buf[2] != rlength) { errMsg(err) << "Incorrect reply length " << (int)_receive_buf[2] << ", expected " << rlength << "!"; return false; } memcpy(rdata, _receive_buf+4, rlength); return true; } // // Callback: data is received from the HID device // void HIDevice::callback_input(void *context, IOReturn result, void *sender, IOHIDReportType type, uint32_t reportID, uint8_t *data, CFIndex nbytes) { Q_UNUSED(sender); Q_UNUSED(type); Q_UNUSED(reportID); HIDevice *self = reinterpret_cast(context); if (result != kIOReturnSuccess) { logError() << "HID input error: " << result << "."; self->close(); return; } if (nbytes > CFIndex(sizeof(self->_receive_buf))) { logError() << "Too large HID input: " << (int)nbytes << " bytes!"; self->close(); return; } self->_nbytes_received = nbytes; if (nbytes > 0) memcpy(self->_receive_buf, data, nbytes); } // // Callback: device specified in the matching dictionary has been added // void HIDevice::callback_open(void *context, IOReturn result, void *sender, IOHIDDeviceRef deviceRef) { Q_UNUSED(result); Q_UNUSED(sender); HIDevice *self = reinterpret_cast(context); IOReturn o = IOHIDDeviceOpen(deviceRef, kIOHIDOptionsTypeSeizeDevice); if (o != kIOReturnSuccess) { logError() << "Cannot open HID device!"; return; } // Register input callback. IOHIDDeviceRegisterInputReportCallback(deviceRef, self->_transfer_buf, sizeof(self->_transfer_buf), callback_input, self); self->_dev = deviceRef; } // // Callback: device specified in the matching dictionary has been removed // void HIDevice::callback_close(void *context, IOReturn result, void *sender, IOHIDDeviceRef deviceRef) { Q_UNUSED(result); Q_UNUSED(sender); HIDevice *self = reinterpret_cast(context); // De-register input callback. IOHIDDeviceRegisterInputReportCallback( deviceRef, self->_transfer_buf, sizeof(self->_transfer_buf), NULL, NULL); } // // Close HID device. // void HIDevice::close() { if (! _dev) return; IOHIDDeviceClose(_dev, kIOHIDOptionsTypeNone); _dev = nullptr; } qdmr-0.12.3/lib/hid_macos.hh000066400000000000000000000045731501654372000155710ustar00rootroot00000000000000#ifndef HID_MACOS_HH #define HID_MACOS_HH #include #include #include "errorstack.hh" #include "radiointerface.hh" /** Implements the HID radio interface MacOS X API. * @ingroup rif */ class HIDevice: public QObject { Q_OBJECT public: /** Specialization to address a HI device uniquely. */ class Descriptor: public USBDeviceDescriptor { public: /** Constructor from interface info, bus number and device address. */ Descriptor(const USBDeviceInfo &info, uint32_t locationId, uint16_t device); }; public: /** Opens a connection to the device with given vendor and product ID. */ HIDevice(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr); /** Destructor. */ virtual ~HIDevice(); /** Returns @c true if the connection was established. */ bool isOpen() const; /** Send command/data to the device and store response in @c rdata. * @param data Pointer to the command/data to send. * @param nbytes The number of bytes to send. * @param rdata Pointer to receive buffer. * @param rlength Size of receive buffer. * @param err The stack to put error messages on. */ bool hid_send_recv(const unsigned char *data, unsigned nbytes, unsigned char *rdata, unsigned rlength, const ErrorStack &err=ErrorStack()); /** Close connection to device. */ void close(); public: /** Finds all HID interfaces with the specified VID/PID combination. */ static QList detect(uint16_t vid, uint16_t pid); protected: /** Internal callback for response data. */ static void callback_input(void *context, IOReturn result, void *sender, IOHIDReportType type, uint32_t reportID, uint8_t *data, CFIndex nbytes); /** Internal callback for device opend. */ static void callback_open(void *context, IOReturn result, void *sender, IOHIDDeviceRef deviceRef); /** Internal callback for device closed. */ static void callback_close(void *ontext, IOReturn result, void *sender, IOHIDDeviceRef deviceRef); protected: /** Device manager. */ IOHIDManagerRef _HIDManager; /** Device handle. */ volatile IOHIDDeviceRef _dev; /** Device buffer. */ unsigned char _transfer_buf[42]; /** Receive buffer. */ unsigned char _receive_buf[42]; /** Receive result. */ volatile int _nbytes_received = 0; }; #endif // HID_MACOS_HH qdmr-0.12.3/lib/intermediaterepresentation.cc000066400000000000000000000113221501654372000212540ustar00rootroot00000000000000#include "intermediaterepresentation.hh" #include "configobject.hh" #include "zone.hh" /* ********************************************************************************************* * * Implementation of ZoneSplitVisitor * ********************************************************************************************* */ ZoneSplitVisitor::ZoneSplitVisitor() : Visitor() { // pass... } bool ZoneSplitVisitor::processItem(ConfigItem *item, const ErrorStack &err) { // Skip non-zones if (! item->is()) return Visitor::processItem(item, err); Zone *zone = item->as(); // skip zones with empty B list if (0 == zone->B()->count()) return Visitor::processItem(item, err); // create new zone with B list as A list, clear B list of "old" zone Zone *newZone = new Zone(); while (zone->B()->count()) { newZone->A()->add(zone->B()->get(0)); zone->B()->take(zone->B()->get(0)); } // set names newZone->setName(QString("%1 B").arg(zone->name())); zone->setName(QString("%1 A").arg(zone->name())); // add zone to list of zones ConfigObjectList *lst = qobject_cast(item->parent()); int idx = lst->indexOf(zone); lst->add(newZone, idx+1); return true; } /* ********************************************************************************************* * * Implementation of ZoneMergeVisitor * ********************************************************************************************* */ ZoneMergeVisitor::ZoneMergeVisitor() : Visitor(), _lastZone(nullptr), _mergedZones() { // pass... } bool ZoneMergeVisitor::processList(AbstractConfigObjectList *list, const ErrorStack &err) { if (nullptr == qobject_cast(list)) return Visitor::processList(list, err); if (2 > list->count()) return Visitor::processList(list, err); _mergedZones.clear(); _lastZone = nullptr; if (! processItem(list->get(0), err)) return false; _lastZone = list->get(0)->as(); for (int i=1; icount(); i++) { if (! processItem(list->get(i), err)) return false; _lastZone = list->get(i)->as(); } foreach(Zone *zone, _mergedZones) { list->del(zone); } _mergedZones.clear(); return true; } bool ZoneMergeVisitor::processItem(ConfigItem *item, const ErrorStack &err) { if (! item->is()) return Visitor::processItem(item, err); Zone *currentZone = item->as(); if ((! _lastZone) || (!_lastZone->name().endsWith(" A")) || (0 != _lastZone->B()->count())) return Visitor::processItem(item, err); if ((!currentZone->name().endsWith(" B")) || (0 != currentZone->B()->count())) return Visitor::processItem(item, err); while (currentZone->A()->count()) { ConfigObject *ch = currentZone->A()->get(0); _lastZone->B()->add(ch); currentZone->A()->del(ch); } _lastZone->setName(_lastZone->name().chopped(2)); _mergedZones.append(currentZone); return Visitor::processItem(item); } /* ********************************************************************************************* * * Implementation of ObjectFilterVisitor * ********************************************************************************************* */ ObjectFilterVisitor::ObjectFilterVisitor(const std::initializer_list &types) : Visitor(), _filter(types) { // pass... } bool ObjectFilterVisitor::processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err) { if (! propIsInstance(prop)) return Visitor::processProperty(item, prop, err); if (prop.read(item).isNull()) return Visitor::processProperty(item, prop, err); if (! prop.isWritable()) return Visitor::processProperty(item, prop, err); foreach (const QMetaObject &meta, _filter) { ConfigItem *propItem = prop.read(item).value(); const char *classname = meta.className(); if (propItem->inherits(classname)) { prop.write(item, QVariant::fromValue(nullptr)); delete propItem; return true; } } return Visitor::processProperty(item, prop, err); } bool ObjectFilterVisitor::processList(AbstractConfigObjectList *list, const ErrorStack &err) { if (qobject_cast(list)) return Visitor::processList(list, err); ConfigObjectList *objList = qobject_cast(list); if (nullptr == objList) return Visitor::processList(list, err);; QList filtered; for (int i=0; icount(); i++) { foreach (const QMetaObject &meta, _filter) { const char *classname = meta.className(); if (objList->get(i)->inherits(classname)) filtered.append(objList->get(i)); } } foreach (ConfigObject *item, filtered) objList->del(item); return Visitor::processList(list, err); } qdmr-0.12.3/lib/intermediaterepresentation.hh000066400000000000000000000042131501654372000212670ustar00rootroot00000000000000/** @defgroup ir Intermediate representation. * This module collects visitors applied to a codeplug before the actual encoding of the device * specific binary codeplug. This preprocessing step eases the encoding a lot. * * @ingroup config */ #ifndef INTERMEDIATEREPRESENTATION_HH #define INTERMEDIATEREPRESENTATION_HH #include "visitor.hh" #include class Zone; /** Simple visitor that splits Zones having A and B channels into two zones with A-lists only. * This is a pre-processing step for many radios, where zones consists of a single list of channels * and a zone is selected for each VFO separately. * @ingroup ir */ class ZoneSplitVisitor: public Visitor { public: /** Constructor. */ explicit ZoneSplitVisitor(); bool processItem(ConfigItem *item, const ErrorStack &err); }; /** Simple visitor that merges zones. This is the reverse step of the @c ZoneSplitVisitor. * Two subsequent zones are only merged into one, if both zones have empty B lists, the name of the * first zone ends on "... A" and the name of the second ends on "... B". That is, if the two zones * where likely split by qdmr. */ class ZoneMergeVisitor: public Visitor { public: /** Constructor. */ explicit ZoneMergeVisitor(); bool processList(AbstractConfigObjectList *list, const ErrorStack &err); bool processItem(ConfigItem *item, const ErrorStack &err); protected: /** The last zone visited, @c nullptr if the first zone is processed. */ Zone *_lastZone; /** Zones to be removed. */ QList _mergedZones; }; /** Fitlers instance by meta object. * This visitor can be used to remove elements from the abstract codeplug, not supported by the * target device. */ class ObjectFilterVisitor: public Visitor { public: /** Constructor from initializer list of Qt meta objects. */ explicit ObjectFilterVisitor(const std::initializer_list &types); bool processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err); bool processList(AbstractConfigObjectList *list, const ErrorStack &err); protected: /** The list of filtered types. */ QList _filter; }; #endif // INTERMEDIATEREPRESENTATION_HH qdmr-0.12.3/lib/interval.cc000066400000000000000000000022161501654372000154450ustar00rootroot00000000000000#include "interval.hh" #include QString Interval::format(Format f) const { if (0 == _duration) return "0"; switch (f) { case Format::Automatic: if (0 == _duration % 60000UL) return format(Format::Minutes); else if (0 == _duration % 1000UL) return format(Format::Seconds); return format(Format::Milliseconds); case Format::Minutes: return QString("%1 min").arg(_duration/60000UL); case Format::Seconds: return QString("%1 s").arg(_duration/1000UL); case Format::Milliseconds: return QString("%1 ms").arg(_duration); } return QString("%1 ms").arg(_duration); } bool Interval::parse(const QString &value) { QRegularExpression ex(R"(\s*([0-9]+)\s*(min|s|ms|)\s*)"); QRegularExpressionMatch match = ex.match(value); if (! match.isValid()) return false; bool hasUnit = match.capturedLength(2); QString unit = match.captured(2); QString dur = match.captured(1); if (hasUnit && ("s"==unit)) _duration = dur.toULongLong()*1000ULL; else if (hasUnit && ("min"==unit)) _duration = dur.toULongLong()*60000ULL; else _duration = dur.toULongLong(); return true; } qdmr-0.12.3/lib/interval.hh000066400000000000000000000050311501654372000154550ustar00rootroot00000000000000#ifndef INTERVAL_HH #define INTERVAL_HH #include #include #include /** Represents a time interval. * @ingroup utils */ class Interval { public: /** Possible formats. */ enum class Format { Automatic, Milliseconds, Seconds, Minutes }; protected: /** Constructor from milliseconds. */ constexpr explicit Interval(unsigned long long ms) : _duration(ms) { // pass... } public: /** Default constructor. */ Interval() : _duration(0) { // pass... } /** Copy constructor. */ constexpr Interval(const Interval &other) : _duration(other._duration) { // pass... } inline Interval &operator =(const Interval &other) { ///< Assignment. _duration = other._duration; return *this; } inline bool isNull() const { return 0 == _duration; } ///< Test for 0. inline bool operator ==(const Interval &other) const { ///< Comparison. return _duration == other._duration; } inline bool operator< (const Interval &other) const {///< Comparison. return _duration < other._duration; } inline unsigned long long milliseconds() const { return _duration; } ///< Unit conversion. inline unsigned long long seconds() const { return _duration/1000ULL; } ///< Unit conversion. inline unsigned long long minutes() const { return _duration/60000ULL; } ///< Unit conversion. static inline constexpr Interval fromMilliseconds(unsigned long long ms) { ///< Unit conversion. return Interval(ms); } static inline constexpr Interval fromSeconds(unsigned long long s) { ///< Unit conversion. return Interval(s*1000ULL); } static inline constexpr Interval fromMinutes(unsigned long long min) { ///< Unit conversion. return Interval(min*60000ULL); } /** Format the frequency. */ QString format(Format f=Format::Automatic) const; /** Parses a frequency. */ bool parse(const QString &value); private: /** An interval duration in ms. */ unsigned long long _duration; }; Q_DECLARE_METATYPE(Interval) namespace YAML { /** Implements the conversion to and from YAML::Node. */ template<> struct convert { /** Serializes the interval. */ static Node encode(const Interval& rhs) { return Node(rhs.format().toStdString()); } /** Parses the interval. */ static bool decode(const Node& node, Interval& rhs) { if (!node.IsScalar()) return false; return rhs.parse(QString::fromStdString(node.as())); } }; } #endif // INTERVAL_HH qdmr-0.12.3/lib/libdmrconf.hh000066400000000000000000000131251501654372000157530ustar00rootroot00000000000000/** @mainpage libdmrconf -- A Library to program DMR radios. * Libdmrconf is a Qt based library, providing methods to assemble, read and program codeplugs for * cheap Chinese DMR radios. To this end, it aims at providing a common way to describe codeplugs * irrespective of the actual radio being used. * * The aim of this project is, to define a minimal but sufficient generic configuration for all * radios, such that all relevant ham-radio features of the radios can be used. This is a challenge, * as there are many manufacturers, each selling several different models, each having * a variety of different features. In fact, manufacturers provide a separate application to program * each model. The resulting codeplugs are usually stored in a binary format, * similar to the actual codeplug being loaded onto the radio. * * This makes absolutely sense for commercial applications, like radios used by staff at large * events. In these cases, the organizer of the event would buy large quantities of identical radios and * assemble a single codeplug for all radios. * * For ham-radio purposes, however, this incompatibility between models and manufactures is an * issue. Assembling a decent codeplug for a region with several repeaters is cumbersome. As these * applications cannot share codeplugs between models let alone between manufactures, this * cumbersome work has to be repeated for each model, although the basic configuration of these * models like contact, channels & zones remains identical. In fact, many features of these radios * are specific to the commercial application and are not used in ham-radio context. * * A common minimalistic configuration of these radios for ham-radio use would allow for sharing * codeplugs between different models and even manufactures. Hence, the hard work to * assemble a decent codeplug for a particular region only needs to be done once. * * To this end, @c libdmrconf defines a configuration language in text format, that represents the * configuration of the radio and from which the actual binary code-plug for each radio is derived. * If a radio does not support a particular feature within the configuration, it simply gets * ignored. The configuration language as well as classes for reading and writing config files are * documented in the manual. * * @section usage Using libdmrconf * - Using cmake to find libdmrconf * - setting paths * - linking * - include files * * @section interface Interfacing radios * In a first step, detect all USB devices that could be connected radios. Some radios use generic * USB CDC-AMC (USB-to-serial) chips. In these cases be careful, that you do not confuse them with * other hardware connected to the host. So, to detect USB interfaces use the device specific * interfaces, e.g. @c AnytoneInterface::detect(), @c RadioddityInterface::detect() or * @c TyTInterface::detect(). For example * @code * #include * * int main(void) { * // Find any supported devices connected * QList devices = AnytoneInterface::detect() + * TyTInterface::detect() + * RadioddityInterface::detect() + * OpenGD77Interface::detect(); * if (0 == devices.size()) { * std::cerr << "No matching device found."; * return -1; * } * * // As the connected radio may use a generic USB CDC-ACM chip, check if the interface is safe. * if (! devices.first().isSave()) { * std::cerr << "Sorry, the detected device is not save to talk to automatically, " * "check if the chosen device is the correct one."; * return -1; * } * * // Then try to connect to the first found. * // The radio interface is common to all devices. So the remaining code is device independent. * Radio *radio = Radio::detect(devices.first(), RadioInfo(), err); * if (nullptr == radio) { * std::cerr << "Oops:" << err.format(); * return -1; * } * * // Download codeplug * if (! radio->startDownload(true, err)) { * std::cerr << "Error while downloading codeplug:" << err.format(); * delete radio; * return -1; * } * * // Decode codeplug to device independent config * Config config; * if (! radio->codeplug().decode(&config, err)) { * std::cerr << "Cannot decode codeplug:" << err.format(); * delete radio; * return -1; * } * * // Now, manipulate the generic codeplug in config. * * // Verify codeplug with radio * RadioLimitContext issues; * if (! radio->limits().verifyConfig(config, issues)) { * // Dump issues... * delete radio; * return -1; * } * * // Encode and upload codeplug back to the device * if (! radio->startUpload(config, true, Codeplug::Flags(), err)) { * std::err << "Cannot write codeplug: " << err.format(); * delete radio; * return -1; * } * * // Done. * delete radio; * return 0; * } * @endcode * * @section codeplug Reading and writing codeplug files * - Using YAML for reading and writing codeplug files * * @section config Manipulating codeplugs * - Some examples for basic manipulations of the @c Config object. * */ #ifndef __LIBDMRCONF_HH__ #define __LIBDMRCONF_HH__ #include "config.h" #include "config.hh" #include "csvreader.hh" #include "radio.hh" #include "anytone_interface.hh" #include "tyt_interface.hh" #include "opengd77_interface.hh" #include "radioddity_interface.hh" #endif // __LIBDMRCONF_HH__ qdmr-0.12.3/lib/logger.cc000066400000000000000000000140441501654372000151020ustar00rootroot00000000000000#include "logger.hh" #include #include #include /* ********************************************************************************************* * * Implementation of LogMessage * ********************************************************************************************* */ LogMessage::LogMessage(Level level, const QString &file, int line, const QString &message) : QTextStream(), _level(level), _file(file), _line(line), _message(message) { this->setString(&_message); this->seek(_message.size()); } LogMessage::LogMessage(const LogMessage &other) : QTextStream(), _level(other._level), _file(other._file), _line(other._line), _message(other._message) { this->setString(&_message); this->seek(_message.size()); } LogMessage::~LogMessage() { Logger::get().log(*this); } LogMessage::Level LogMessage::level() const { return _level; } const QString & LogMessage::file() const { return _file; } int LogMessage::line() const { return _line; } const QString & LogMessage::message() const { return _message; } /* ********************************************************************************************* * * Implementation of LogHandler * ********************************************************************************************* */ LogHandler::LogHandler(QObject *parent) : QObject(parent) { // pass... } LogHandler::~LogHandler() { // pass... } /* ********************************************************************************************* * * Implementation of Logger * ********************************************************************************************* */ Logger *Logger::_instance = nullptr; Logger::Logger() : QObject(nullptr), _handler() { // pass... } Logger::~Logger() { _handler.clear(); } void Logger::log(const LogMessage &msg) { foreach (LogHandler *handler, _handler) { handler->handle(msg); } } void Logger::addHandler(LogHandler *handler) { if (nullptr == handler) return; if (_handler.contains(handler)) return; handler->setParent(this); _handler.append(handler); connect(handler, SIGNAL(destroyed(QObject*)), this, SLOT(onHandlerDeleted(QObject*))); } void Logger::remHandler(LogHandler *handler) { if (_handler.contains(handler)) { handler->setParent(nullptr); disconnect(handler, SIGNAL(destroyed(QObject*)), this, SLOT(onHandlerDeleted(QObject*))); } _handler.removeAll(handler); } void Logger::onHandlerDeleted(QObject *obj) { _handler.removeAll(dynamic_cast(obj)); } Logger & Logger::get() { if (nullptr == _instance) { _instance = new Logger(); } return *_instance; } /* ********************************************************************************************* * * Implementation of StreamLogHandler * ********************************************************************************************* */ StreamLogHandler::StreamLogHandler(QTextStream &stream, LogMessage::Level minLevel, bool color, QObject *parent) : LogHandler(parent), _stream(stream), _minLevel(minLevel), _color(color) { // pass... } LogMessage::Level StreamLogHandler::minLevel() const { return _minLevel; } void StreamLogHandler::setMinLevel(LogMessage::Level minLevel) { _minLevel = minLevel; } void StreamLogHandler::handle(const LogMessage &message) { if (message.level() < _minLevel) return; switch (message.level()) { case LogMessage::DEBUG: if (_color) _stream << "\033[37m"; _stream << "Debug "; break; case LogMessage::INFO: if (_color) _stream << "\033[39m"; _stream << "Info "; break; case LogMessage::WARNING: if (_color) _stream << "\033[33m"; _stream << "Warning "; break; case LogMessage::ERROR: if (_color) _stream << "\033[31m"; _stream << "ERROR "; break; case LogMessage::FATAL: if (_color) _stream << "\033[30m\033[101m"; _stream << "FATAL "; break; } QFileInfo finfo(message.file()); _stream << "in " << finfo.dir().dirName() << "/" << finfo.fileName() << "@" << message.line() << ": " << message.message() << "\n"; if (_color) _stream << "\033[39m"; _stream.flush(); } /* ********************************************************************************************* * * Implementation of FileLogHandler * ********************************************************************************************* */ FileLogHandler::FileLogHandler(const QString &filename, LogMessage::Level minLevel, QObject *parent) : LogHandler(parent), _file(filename), _stream(), _minLevel(minLevel) { QFileInfo info(filename); // Check if logfile exists if (! info.exists()) { // check and create path to logfile (if needed) if (! info.absoluteDir().mkpath(info.absoluteDir().absolutePath())) { logError() << "Cannot create log-file directory '" << info.absoluteDir().absolutePath() << "'."; return; } // Create logfile _file.open(QFile::WriteOnly); } else { // Open logfile _file.open(QFile::Append); } if (_file.isOpen()) { // Set stream desitnation to logfile _stream.setDevice(&_file); } } FileLogHandler::~FileLogHandler() { if (_file.isOpen()) { _file.flush(); _file.close(); } } LogMessage::Level FileLogHandler::minLevel() const { return _minLevel; } void FileLogHandler::setMinLevel(LogMessage::Level minLevel) { _minLevel = minLevel; } void FileLogHandler::handle(const LogMessage &message) { if (!_file.isOpen()) return; if (message.level() < _minLevel) return; _stream << QDateTime::currentDateTime().toString(Qt::ISODateWithMs) << ": "; switch (message.level()) { case LogMessage::DEBUG: _stream << "Debug "; break; case LogMessage::INFO: _stream << "Info "; break; case LogMessage::WARNING: _stream << "Warning "; break; case LogMessage::ERROR: _stream << "ERROR "; break; case LogMessage::FATAL: _stream << "FATAL "; break; } QFileInfo finfo(message.file()); _stream << "in " << finfo.dir().dirName() << "/" << finfo.fileName() << "@" << message.line() << ": " << message.message() << "\n"; _stream.flush(); } qdmr-0.12.3/lib/logger.hh000066400000000000000000000127631501654372000151220ustar00rootroot00000000000000/** @defgroup log Log Message Handling * @ingroup util */ #ifndef LOGGER_HH #define LOGGER_HH #include #include #include /** Constructs a debug message. */ #define logDebug() LogMessage(LogMessage::DEBUG, __FILE__, __LINE__) /** Constructs an info message. */ #define logInfo() LogMessage(LogMessage::INFO, __FILE__, __LINE__) /** Constructs a warning message. */ #define logWarn() LogMessage(LogMessage::WARNING, __FILE__, __LINE__) /** Constructs an error message. */ #define logError() LogMessage(LogMessage::ERROR, __FILE__, __LINE__) /** Constructs a fatal error message. */ #ifdef __cpp_lib_stacktrace #include #define logFatal() LogMessage(LogMessage::FATAL, __FILE__, __LINE__) << \ QString::fromStdString(std::to_string(std::stacktrace::current())) #else #define logFatal() LogMessage(LogMessage::FATAL, __FILE__, __LINE__) #endif /** Implements a log-message. * Instances of this class will forward the content of this message automatically to the @c Logger * instance upon destruction. That means, you do not need to forward log messages explicitly. * @ingroup log */ class LogMessage: public QTextStream { public: /** Possible log-levels. */ typedef enum { DEBUG, ///< Level for debug messages. Will not be shown to the user unless requested. INFO, ///< Level for informative messages. Will not be shown to the user unless requested. WARNING, ///< Level for warning messages. ERROR, ///< Level for error messages. FATAL ///< Level for fatal error messages. } Level; public: /** Constructor. * @param level Specifies the level of the log message. * @param file Specifies the source file. * @param line Specifies the source line. * @param message Specifies the log message content. */ LogMessage(Level level, const QString &file, int line, const QString &message=""); /** Copy constructor. */ LogMessage(const LogMessage &other); /** Destructor. */ virtual ~LogMessage(); /** Returns the level of the log message. */ Level level() const; /** Returns the source file. */ const QString &file() const; /** Returns the source line. */ int line() const; /** Returns the log message content. */ const QString &message() const; protected: /** The log level. */ Level _level; /** The source file. */ QString _file; /** The source line. */ int _line; /** The log message content. */ QString _message; }; /** Interface for all log message handler. * @ingroup log */ class LogHandler: public QObject { Q_OBJECT public: /** Constructor. */ explicit LogHandler(QObject *parent=nullptr); /** Destructor. */ virtual ~LogHandler(); /** Callback to handle log messages. */ virtual void handle(const LogMessage &message) = 0; }; /** Singleton class to process log messages. * @ingroup log */ class Logger: public QObject { Q_OBJECT protected: /** Hidden constructor. Use @c get method to obtain an instance. */ Logger(); public: /** Destructor. */ virtual ~Logger(); /** Logs a message. */ void log(const LogMessage &msg); /** Adds a log-handler to the logger. The ownership is transferred to the logger. */ void addHandler(LogHandler *handler); /** Removes a log-handler from the logger. The ownership is transferred back to the caller. */ void remHandler(LogHandler *handler); protected slots: /** Internal callback to handle deleted handler objects. */ void onHandlerDeleted(QObject *obj); public: /** Factory method to get the singleton instance. */ static Logger &get(); protected: /** The singleton instance. */ static Logger *_instance; /** The list of registered log-handler. */ QList _handler; }; /** A log-handler that dumps log-messages into a @c QTextStream. * @ingroup log */ class StreamLogHandler: public LogHandler { Q_OBJECT public: /** Constructor. * @param stream Specifies the text stream to log into. * @param minLevel Specifies the minimum log-level to log. * @param color If @c true, the output will be colored. * @param parent Specifies the parent object. */ StreamLogHandler(QTextStream &stream, LogMessage::Level minLevel=LogMessage::DEBUG, bool color=false, QObject *parent=nullptr); /** Returns the minimum log level. */ LogMessage::Level minLevel() const; /** Resets the minimum log level. */ void setMinLevel(LogMessage::Level minLevel); void handle(const LogMessage &message); protected: /** A reference to the text stream to log into. */ QTextStream &_stream; /** The minimum log level. */ LogMessage::Level _minLevel; /** If true, write messages using console colors. */ bool _color; }; /** A log-handler that dumps log-messages into files. * @ingroup log */ class FileLogHandler: public LogHandler { Q_OBJECT public: /** Constructor. * @param file Specifies the filename to log to. * @param minLevel Specifies the minimum log-level to log. * @param parent Specifies the parent object. */ FileLogHandler(const QString &file, LogMessage::Level minLevel=LogMessage::DEBUG, QObject *parent=nullptr); /** Destructor, closes log file. */ virtual ~FileLogHandler(); /** Returns the minimum log level. */ LogMessage::Level minLevel() const; /** Resets the minimum log level. */ void setMinLevel(LogMessage::Level minLevel); void handle(const LogMessage &message); protected: /** The file to log into. */ QFile _file; /** A reference to the text stream to log into. */ QTextStream _stream; /** The minimum log level. */ LogMessage::Level _minLevel; }; #endif // LOGGER_HH qdmr-0.12.3/lib/md2017.cc000066400000000000000000000017471501654372000145430ustar00rootroot00000000000000#include "md2017.hh" #include "md2017_limits.hh" RadioLimits *MD2017::_limits = nullptr; MD2017::MD2017(TyTInterface *device, QObject *parent) : TyTRadio(device, parent), _name("TyT DM-2017") { //_codeplug(nullptr), _callsigndb(nullptr); } MD2017::~MD2017() { // pass... } const QString & MD2017::name() const { return _name; } const RadioLimits & MD2017::limits() const { if (nullptr == _limits) _limits = new MD2017Limits(); return *_limits; } const Codeplug & MD2017::codeplug() const { return _codeplug; } Codeplug & MD2017::codeplug() { return _codeplug; } const CallsignDB * MD2017::callsignDB() const { return &_callsigndb; } CallsignDB * MD2017::callsignDB() { return &_callsigndb; } RadioInfo MD2017::defaultRadioInfo() { return RadioInfo( RadioInfo::MD2017, "md2017", "MD-2017", "TyT", TyTInterface::interfaceInfo(), QList{ RadioInfo(RadioInfo::RT82, "RT82", "Retevis", TyTInterface::interfaceInfo()) }); } qdmr-0.12.3/lib/md2017.hh000066400000000000000000000050451501654372000145500ustar00rootroot00000000000000/** @defgroup md2017 TYT MD-2017, Retevis RT82 * Device specific classes for TYT MD-2017 and Retevis RT82. * * \image html md2017.jpg "MD-2017" width=200px * \image latex md2017.jpg "MD-2017" width=200px * * * The TYT MD-2017 and the identical Retevis RT-82 are decent VHF/UHF FM and DMR handheld radios. * Both radios are available with and without an GPS option. @c libdmrconf will support that * feature. Non-GPS variants of that radio will simply ignore any GPS system settings. * * These radios support up to 3000 channels organized in 250 zones. Each zone may hold up to 64 * channels for each VFO (64 for VFO A and 64 for VFO B). There are also up to 250 scanlists * holding up to 31(?) channels each. * * The radio can hold up to 3000 contacts (DMR contacts) and 250 RX group lists as well as up to 50 * pre-programmed messages. Depending on the firmware programmed on the radio, it may also hold a * callsign database of up to 100000 entries. This can be used to resolve amlost any DMR ID assigned * (at the time of this writing, there are about 140k IDs assigned) to name and callsign. * * @ingroup tyt */ #ifndef MD2017_HH #define MD2017_HH #include "tyt_radio.hh" #include "md2017_codeplug.hh" #include "md2017_callsigndb.hh" /** Implements an USB interface to the TYT MD-2017 & Retevis RT82 VHF/UHF 5W DMR (Tier I&II) radios. * * The TYT MD-2017 and Retevis RT82 radios use the TyT typical DFU-style communication protocol * to read and write codeplugs onto the radio (see @c TyTRadio). * * @ingroup md2017 */ class MD2017 : public TyTRadio { Q_OBJECT public: /** Constructor. * @param device Specifies the DFU device to use for communication with the device. * @param parent The QObject parent. */ MD2017(TyTInterface *device=nullptr, QObject *parent=nullptr); /** Desturctor. */ virtual ~MD2017(); const QString &name() const; const RadioLimits &limits() const; const Codeplug &codeplug() const; Codeplug &codeplug(); const CallsignDB *callsignDB() const; CallsignDB *callsignDB(); /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); private: /** Holds the name of the device. */ QString _name; /** The codeplug object. */ MD2017Codeplug _codeplug; /** The callsign DB object. */ MD2017CallsignDB _callsigndb; private: /** Singleton instance of the limits for this radio. */ static RadioLimits *_limits; }; #endif // MD2017_HH qdmr-0.12.3/lib/md2017_callsigndb.cc000066400000000000000000000003431501654372000167140ustar00rootroot00000000000000#include "md2017_callsigndb.hh" MD2017CallsignDB::MD2017CallsignDB(QObject *parent) : TyTCallsignDB(parent) { image(0).setName("TYT MD-2017 Callsign database."); } MD2017CallsignDB::~MD2017CallsignDB() { // pass... } qdmr-0.12.3/lib/md2017_callsigndb.hh000066400000000000000000000007621501654372000167330ustar00rootroot00000000000000#ifndef MD2017_CALLSIGNDB_HH #define MD2017_CALLSIGNDB_HH #include "tyt_callsigndb.hh" /** Device specific implementation of the call-sign DB for the TyT MD-2017. * * In fact this callsign DB is identical to the generic @c TyTCallsignDB. * * @ingroup md2017 */ class MD2017CallsignDB : public TyTCallsignDB { Q_OBJECT public: /** Constructor. */ explicit MD2017CallsignDB(QObject *parent=nullptr); /** Destructor. */ virtual ~MD2017CallsignDB(); }; #endif // MD2017_CALLSIGNDB_HH qdmr-0.12.3/lib/md2017_codeplug.cc000066400000000000000000000343631501654372000164250ustar00rootroot00000000000000#include "md2017_codeplug.hh" #include "config.hh" #include "logger.hh" #define NUM_CHANNELS 3000 #define ADDR_CHANNELS 0x110000 #define CHANNEL_SIZE 0x000040 #define NUM_CONTACTS 10000 #define ADDR_CONTACTS 0x140000 #define CONTACT_SIZE 0x000024 #define NUM_ZONES 250 #define ADDR_ZONES 0x0149e0 #define ZONE_SIZE 0x000040 #define ADDR_ZONEEXTS 0x031000 #define ZONEEXT_SIZE 0x0000e0 #define NUM_GROUPLISTS 250 #define ADDR_GROUPLISTS 0x00ec20 #define GROUPLIST_SIZE 0x000060 #define NUM_SCANLISTS 250 #define ADDR_SCANLISTS 0x018860 #define SCANLIST_SIZE 0x000068 #define ADDR_TIMESTAMP 0x002000 #define ADDR_SETTINGS 0x002040 #define ADDR_BOOTSETTINGS 0x02f000 #define ADDR_MENUSETTINGS 0x0020f0 #define ADDR_BUTTONSETTINGS 0x002100 #define ADDR_PRIVACY_KEYS 0x0059c0 #define NUM_GPSSYSTEMS 16 #define ADDR_GPSSYSTEMS 0x03ec40 #define GPSSYSTEM_SIZE 0x000010 #define ADDR_EMERGENCY_SETTINGS 0x005a50 #define NUM_EMERGENCY_SYSTEMS 32 #define ADDR_EMERGENCY_SYSTEMS 0x005a60 #define EMERGENCY_SYSTEM_SIZE 0x000028 #define ADDR_VFO_CHANNEL_A 0x02ef00 #define ADDR_VFO_CHANNEL_B 0x02ef40 /* ********************************************************************************************* * * MD2017Codeplug::ContactElement * ********************************************************************************************* */ MD2017Codeplug::ContactElement::ContactElement(uint8_t *ptr, size_t size) : TyTCodeplug::ContactElement(ptr, size) { // pass... } MD2017Codeplug::ContactElement::ContactElement(uint8_t *ptr) : TyTCodeplug::ContactElement(ptr) { // pass... } bool MD2017Codeplug::ContactElement::isValid() const { return Codeplug::Element::isValid() && (! name().isEmpty()); } MD2017Codeplug::MD2017Codeplug(QObject *parent) : TyTCodeplug(parent) { addImage("TYT MD-2017 Codeplug"); image(0).addElement(0x002000, 0x3e000); image(0).addElement(0x110000, 0x90000); // Clear entire codeplug clear(); } MD2017Codeplug::~MD2017Codeplug() { // pass... } void MD2017Codeplug::clearTimestamp() { TimestampElement(data(ADDR_TIMESTAMP)).clear(); } bool MD2017Codeplug::encodeTimestamp() { TimestampElement ts(data(ADDR_TIMESTAMP)); ts.setTimestamp(QDateTime::currentDateTime()); return true; } void MD2017Codeplug::clearGeneralSettings() { GeneralSettingsElement(data(ADDR_SETTINGS)).clear(); } bool MD2017Codeplug::encodeGeneralSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err) return GeneralSettingsElement(data(ADDR_SETTINGS)).fromConfig(config); } bool MD2017Codeplug::decodeGeneralSettings(Config *config, const ErrorStack &err) { Q_UNUSED(err) return GeneralSettingsElement(data(ADDR_SETTINGS)).updateConfig(config); } void MD2017Codeplug::clearChannels() { // Clear channels for (int i=0; ichannelList()->count()) { chan.fromChannelObj(config->channelList()->channel(i), ctx); } else { chan.clear(); } } return true; } bool MD2017Codeplug::createChannels(Config *config, Context &ctx, const ErrorStack &err) { Q_UNUSED(err); for (int i=0; ichannelList()->add(obj); ctx.add(obj, i+1); } else { logWarn() << "Cannot decode channel at index " << i << ":\n" << err.format(" "); continue; } } return true; } bool MD2017Codeplug::linkChannels(Context &ctx, const ErrorStack &err) { Q_UNUSED(err); for (int i=0; i(i+1))) continue; ErrorStack err; if (! chan.linkChannelObj(ctx.get(i+1), ctx, err)) { logWarn() << "Cannot link channel at index " << i << ":\n" << err.format(" "); continue; } } return true; } void MD2017Codeplug::clearContacts() { // Clear contacts for (int i=0; icontacts()->digitalCount()) cont.fromContactObj(config->contacts()->digitalContact(i)); else cont.clear(); } return true; } bool MD2017Codeplug::createContacts(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; icontacts()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid contact at index " << i << "."; return false; } } return true; } void MD2017Codeplug::clearZones() { // Clear zones & zone extensions for (int i=0; izones()->count()) { zone.fromZoneObj(config->zones()->zone(i), ctx); if (config->zones()->zone(i)->B()->count() || (16 < config->zones()->zone(i)->A()->count())) ext.fromZoneObj(config->zones()->zone(i), ctx); } } return true; } bool MD2017Codeplug::createZones(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; izones()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid zone at index " << i << "."; return false; } } return true; } bool MD2017Codeplug::linkZones(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link zone at index " << i << "."; return false; } ZoneExtElement zoneext(data(ADDR_ZONEEXTS + i*ZONEEXT_SIZE)); if (! zoneext.linkZoneObj(ctx.get(i+1), ctx)) { errMsg(err) << "Cannot link zone extension at index " << i << "."; return false; } } return true; } void MD2017Codeplug::clearGroupLists() { for (int i=0; irxGroupLists()->count()) glist.fromGroupListObj(config->rxGroupLists()->list(i), ctx); else glist.clear(); } return true; } bool MD2017Codeplug::createGroupLists(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; irxGroupLists()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid group list at index " << i << "."; return false; } } return true; } bool MD2017Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link group list at index " << i << "."; return false; } } return true; } void MD2017Codeplug::clearScanLists() { // Clear scan lists for (int i=0; iscanlists()->count()) scan.fromScanListObj(config->scanlists()->scanlist(i), ctx); else scan.clear(); } return true; } bool MD2017Codeplug::createScanLists(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; iscanlists()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid scanlist at index " << i << "."; return false; } } return true; } bool MD2017Codeplug::linkScanLists(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link scan list at index" << i << "."; return false; } } return true; } void MD2017Codeplug::clearPositioningSystems() { // Clear GPS systems for (int i=0; iposSystems()->gpsCount()) gps.fromGPSSystemObj(config->posSystems()->gpsSystem(i), ctx); else gps.clear(); } return true; } bool MD2017Codeplug::createPositioningSystems(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; iposSystems()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid GPS system at index " << i << "."; return false; } } return true; } bool MD2017Codeplug::linkPositioningSystems(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link GPS system at index " << i << "."; return false; } } return true; } void MD2017Codeplug::clearBootSettings() { BootSettingsElement(data(ADDR_BOOTSETTINGS)).clear(); } void MD2017Codeplug::clearMenuSettings() { MenuSettingsElement(data(ADDR_MENUSETTINGS)).clear(); } void MD2017Codeplug::clearButtonSettings() { ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).clear(); } bool MD2017Codeplug::encodeButtonSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err) // Encode settings return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).fromConfig(config); } bool MD2017Codeplug::decodeButtonSetttings(Config *config, const ErrorStack &err) { Q_UNUSED(err) return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).updateConfig(config); } void MD2017Codeplug::clearPrivacyKeys() { EncryptionElement(data(ADDR_PRIVACY_KEYS)).clear(); } bool MD2017Codeplug::encodePrivacyKeys(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err); // First, reset keys clearPrivacyKeys(); // Get keys EncryptionElement keys(data(ADDR_PRIVACY_KEYS)); // If there is no encryption extension -> done. if (! config->commercialExtension()) return true; // Otherwise encode return keys.fromCommercialExt(config->commercialExtension(), ctx); } bool MD2017Codeplug::decodePrivacyKeys(Config *config, Context &ctx, const ErrorStack &err) { Q_UNUSED(config) // Get keys EncryptionElement keys(data(ADDR_PRIVACY_KEYS)); // Decode element if(! keys.updateCommercialExt(ctx)) { errMsg(err) << "Cannot create encryption keys."; return false; } return true; } void MD2017Codeplug::clearTextMessages() { MessageBankElement(data(Offset::messages())).clear(); } bool MD2017Codeplug::encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err) { return MessageBankElement(data(Offset::messages())).encode(ctx, flags, err); } bool MD2017Codeplug::decodeTextMessages(Context &ctx, const ErrorStack &err) { return MessageBankElement(data(Offset::messages())).decode(ctx, err); } void MD2017Codeplug::clearEmergencySystems() { EmergencySettingsElement(data(ADDR_EMERGENCY_SETTINGS)).clear(); for (int i=0; i * Start End Size Content * First segment 0x002000-0x040000 * 0x002000 0x00200c 0x0000c Timestamp see @c TyTCodeplug::TimestampElement. * 0x00200c 0x002040 0x00034 Reserved, filled with 0xff. * 0x002040 0x0020f0 0x000b0 General settings see @c TyTCodeplug::GeneralSettingsElement. * 0x0020f0 0x002100 0x00010 Menu settings, see @c UV390Codeplug::menu_t * 0x002100 0x002140 0x00040 Button config, see @c UV390Codeplug::buttons_t. * 0x002140 0x002180 0x00040 Reserved, filled with 0xff. * 0x002180 0x0059c0 0x03840 50 Text messages @ 0x120 bytes each, see @c UV390Codeplug::message_t. * 0x0059c0 0x005a70 0x000b0 Privacy keys, see @c UV390Codeplug::privacy_t. * 0x005a70 0x005a80 0x00010 Emergency system settings, see @c TyTCodeplug::EmergencySettingsElement. * 0x005a80 0x005f80 0x00500 Emergency systems, see @c TyTCodeplug::EmergencySystemElement. * 0x005f80 0x00ec20 0x08ca0 Reserved, filled with 0xff. * 0x00ec20 0x0149e0 0x05dc0 250 RX Group lists @ 0x60 bytes each, see @c TyTCodeplug::GroupListElement. * 0x0149e0 0x018860 0x03e80 250 Zones @ 0x40 bytes each, see @c TyTCodeplug::ZoneElement. * 0x018860 0x01edf0 0x06590 250 Scanlists @ 0x68 bytes each, see @c TyTCodeplug::ScanListElement. * 0x01edf0 0x02ef00 0x10110 Reserved, filled with @c 0xff. * 0x02ef00 0x02ef40 0x00040 VFO A channel, see @c TyTCodeplug::VFOChannelElement. * 0x02ef40 0x02ef80 0x00040 VFO B channel, see @c TyTCodeplug::VFOChannelElement. * 0x02ef80 0x02f000 0x00080 Reserved, filled with @c 0xff. * 0x02f000 0x02f010 0x00010 Some unknown settings like current channel, see @c TyTCodeplug::BootSettingsElement. * 0x02f010 0x031000 0x01ff0 Reserved, filled with @c 0xff. * 0x031000 0x03eac0 0x0dac0 250 Zone-extensions @ 0xe0 bytes each, see @c TyTCodeplug::ZoneExtElement. * 0x03eac0 0x03ec40 0x00180 Reserved, filled with @c 0xff. * 0x03ec40 0x03ed40 0x00100 16 GPS systems @ 0x10 bytes each, see @c TyTCodeplug::GPSSystemElement. * 0x03ed40 0x040000 0x012c0 Reserved, filled with @c 0xff. * Second segment 0x110000-0x1a0000 * 0x110000 0x13ee00 0x2ee00 3000 Channels @ 0x40 bytes each, see @c TyTCodeplug::ChannelElement. * 0x13ee00 0x140000 0x01200 Reserved, filled with @c 0xff. * 0x140000 0x197e40 0x57e40 10000 Contacts @ 0x24 bytes each, see @c TyTCodeplug::ContactElement. * 0x197e40 0x1a0000 0x081c0 Reserved, filled with @c 0xff. * * * @ingroup md2017 */ class MD2017Codeplug : public TyTCodeplug { Q_OBJECT public: /** Reuse TyT MD-UV390 channel element. */ typedef UV390Codeplug::ChannelElement ChannelElement; /** Reuse TyT MD-UV390 VFO channel element. */ typedef UV390Codeplug::VFOChannelElement VFOChannelElement; /** Reuse TyT MD-UV390 zone extension element. */ typedef UV390Codeplug::ZoneExtElement ZoneExtElement; /** Reuse TyT MD-UV390 boot settings element. */ typedef UV390Codeplug::BootSettingsElement BootSettingsElement; /** Reuse TyT MD-UV390 menu settings element. */ typedef UV390Codeplug::MenuSettingsElement MenuSettingsElement; /** Contact element for MD2017 codeplugs. * * This class implements the same memory layout as the base TyTCodeplug::ContactElement. It just * overrides the @c isValid method. * * Memory layout of encoded contact: * @verbinclude tyt_contact.txt */ class ContactElement: public TyTCodeplug::ContactElement { protected: /** Hidden constructor. */ ContactElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ContactElement(uint8_t *ptr); bool isValid() const; }; public: /** Constructor. */ explicit MD2017Codeplug(QObject *parent = nullptr); /** Destructor. */ virtual ~MD2017Codeplug(); public: void clearTimestamp(); bool encodeTimestamp(); void clearGeneralSettings(); bool encodeGeneralSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeGeneralSettings(Config *config, const ErrorStack &err=ErrorStack()); void clearChannels(); bool encodeChannels(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createChannels(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()); void clearContacts(); bool encodeContacts(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createContacts(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); void clearZones(); bool encodeZones(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createZones(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()); void clearGroupLists(); bool encodeGroupLists(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createGroupLists(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); void clearScanLists(); bool encodeScanLists(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createScanLists(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack()); void clearPositioningSystems(); bool encodePositioningSystems(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createPositioningSystems(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkPositioningSystems(Context &ctx, const ErrorStack &err=ErrorStack()); void clearButtonSettings(); bool encodeButtonSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeButtonSetttings(Config *config, const ErrorStack &err=ErrorStack()); void clearPrivacyKeys(); bool encodePrivacyKeys(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err); bool decodePrivacyKeys(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); void clearTextMessages(); bool encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err); bool decodeTextMessages(Context &ctx, const ErrorStack &err); /** Resets/clears the boot settings. */ virtual void clearBootSettings(); void clearMenuSettings(); void clearEmergencySystems(); /** Resets VFO settings. */ virtual void clearVFOSettings(); protected: /** Some internal offsets within the codeplug. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int messages() { return 0x002180; } /// @endcond }; }; #endif // MD2017_CODEPLUG_HH qdmr-0.12.3/lib/md2017_filereader.cc000066400000000000000000000041521501654372000167160ustar00rootroot00000000000000#include "md2017_filereader.hh" #include #include #define SEGMENT0_FILE_ADDR 0x00002225 #define SEGMENT0_TARGET_ADDR 0x00002000 #define SEGMENT0_SIZE 0x0003e000 #define SEGMENT1_FILE_ADDR 0x00040235 #define SEGMENT1_TARGET_ADDR 0x00110000 #define SEGMENT1_SIZE 0x00090000 bool MD2017FileReader::read(const QString &filename, MD2017Codeplug *codeplug, const ErrorStack &err) { // Check file properties QFileInfo info(filename); if (! info.exists()) { errMsg(err) << "Cannot open file '" << filename << "': File does not exisist."; return false; } if (852533 != info.size()) { errMsg(err) << "Cannot read codeplug file '" << filename << "': File size is not 852533 bytes."; return false; } // Open file QFile file(filename); if (! file.open(QFile::ReadOnly)) { errMsg(err) << "Cannot open file '" << filename << "': " << file.errorString() << "."; return false; } // Read file content if (! file.seek(SEGMENT0_FILE_ADDR)) { errMsg(err) << "Cannot read codeplug file '" << filename << "': Cannot seek within file: " << file.errorString() << "."; file.close(); return false; } char *ptr = (char *)codeplug->data(SEGMENT0_TARGET_ADDR); size_t n = SEGMENT0_SIZE; while (0 < n) { int nread = file.read(ptr, n); if (0 > nread) { errMsg(err) << "Cannot read codeplug file '" << filename << "': " << file.errorString() << "."; file.close(); return false; } n -= nread; ptr += nread; } if (! file.seek(SEGMENT1_FILE_ADDR)) { errMsg(err) << "Cannot read codeplug file '" << filename << "': Cannot seek within file: " << file.errorString() << "."; file.close(); return false; } ptr = (char *)codeplug->data(SEGMENT1_TARGET_ADDR); n = SEGMENT1_SIZE; while (0 < n) { int nread = file.read(ptr, n); if (0 > nread) { errMsg(err) << "Cannot read codeplug file '" << filename << "': " << file.errorString() << "."; file.close(); return false; } n -= nread; ptr += nread; } return true; } qdmr-0.12.3/lib/md2017_filereader.hh000066400000000000000000000023271501654372000167320ustar00rootroot00000000000000#ifndef MD2017_FILEREADER_HH #define MD2017_FILEREADER_HH #include "md2017_codeplug.hh" /** Methods to read manufacturer codeplug files. * * The file format of the stock CPS is still pretty simple. The first part of the file consists of * a mal-formed DFU file. This contains a single image with a single element containing the * first section of the memory written to the device. The second section is then added as-is * to the end of the file. Due to the DFU header/footer, the file and memory offsets differ. * * * * * *
      File Start Memory Start Size
      0x002225 0x002000 0x03e000
      0x040235 0x110000 0x090000
      * * @ingroup md2017 */ class MD2017FileReader { public: /** Reads manufacturer codeplug file into given codeplug object. * @param filename Specifies the file to read. * @param codeplug Specifies the codeplug object to store read codeplug. * @param err Error stack. * @returns @c true on success and @c false on error. */ static bool read(const QString &filename, MD2017Codeplug *codeplug, const ErrorStack &err=ErrorStack()); }; #endif // MD2017_FILEREADER_HH qdmr-0.12.3/lib/md2017_limits.cc000066400000000000000000000216401501654372000161160ustar00rootroot00000000000000#include "md2017_limits.hh" #include "md2017_codeplug.hh" #include "channel.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "zone.hh" #include "scanlist.hh" #include "gpssystem.hh" #include "roamingzone.hh" MD2017Limits::MD2017Limits(QObject *parent) : RadioLimits(true, parent) { // Define limits for call-sign DB _hasCallSignDB = true; _callSignDBImplemented = true; _numCallSignDBEntries = 122197; // Define limits for satellite config _hasSatelliteConfig = false; _satelliteConfigImplemented = false; _numSatellites = 0; add("settings", new RadioLimitItem { { "introLine1", new RadioLimitString(-1, 10, RadioLimitString::Unicode) }, { "introLine2", new RadioLimitString(-1, 10, RadioLimitString::Unicode) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum { unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid), unsigned(Channel::Power::High) } }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, -1) } /// @todo check default radio ID. } ); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList{ { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, {"id", new RadioLimitUInt(0, 16777215)} } } } ); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum { (unsigned) DMRContact::PrivateCall, (unsigned) DMRContact::GroupCall, (unsigned) DMRContact::AllCall }}, { "number", new RadioLimitUInt(0, 16777215) } } }, { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() } } ); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, { "contacts", new RadioLimitGroupCallRefList(1, 32) } }) ); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, 10000, new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::Unicode)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRefIgnored(nullptr, RadioLimitIssue::Hint)} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::Unicode)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})}, {"power", new RadioLimitEnum { unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid), unsigned(Channel::Power::High), }}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, true)}, {"aprs", new RadioLimitObjRefIgnored()}, {"roaming", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } ) ); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 1, 250, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, // 16 ASCII chars in name { "A", new RadioLimitRefList(0, 64, Channel::staticMetaObject) }, { "B", new RadioLimitRefList(0, 64, Channel::staticMetaObject) }, { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions } ) ); /* Define limits for scan lists. */ add("scanlists", new RadioLimitList( ScanList::staticMetaObject, 0, 250, new RadioLimitObject{ { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, { "primary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "secondary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "revert", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "channels", new RadioLimitRefList(0, 31, Channel::staticMetaObject) } }) ); /* Define limits for positioning systems. */ add("positioning", new RadioLimitList({ { GPSSystem::staticMetaObject, 0, 16, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "period", new RadioLimitUInt(0, 7650) }, { "contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false) }, { "revert", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } }, { APRSSystem::staticMetaObject, 0, -1, new RadioLimitIgnored() } } ) ); /* Check encryption keys. */ add("commercial", new RadioLimitItem { {"encryptionKeys", new RadioLimitList { {BasicEncryptionKey::staticMetaObject, 0, TyTCodeplug::EncryptionElement::Limit::basicKeys(), new RadioLimitObject { {"name", new RadioLimitIgnored()}, {"key", new RadioLimitStringRegEx("[0-9a-fA-F]{4}")} }}, {AESEncryptionKey::staticMetaObject, 0, TyTCodeplug::EncryptionElement::Limit::advancedKeys(), new RadioLimitObject { {"name", new RadioLimitIgnored()}, {"key", new RadioLimitStringRegEx("[0-9a-fA-F]{32}")} }} } } }); /* Ignore roaming zones. */ add("roaming", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint) ) ); } qdmr-0.12.3/lib/md2017_limits.hh000066400000000000000000000004641501654372000161310ustar00rootroot00000000000000#ifndef MD2017LIMITS_HH #define MD2017LIMITS_HH #include "radiolimits.hh" /** Implements the limits for the TyT MD-2017. * @ingroup md2017 */ class MD2017Limits: public RadioLimits { Q_OBJECT public: /** Constructor. */ explicit MD2017Limits(QObject *parent=nullptr); }; #endif // MD2017LIMITS_HH qdmr-0.12.3/lib/md390.cc000066400000000000000000000073501501654372000144610ustar00rootroot00000000000000#include "md390.hh" #include "md390_limits.hh" #include "logger.hh" #include "utils.hh" #define NUM_CHANNELS 1000 #define ADDR_CHANNELS 0x01ee00 #define CHANNEL_SIZE 0x000040 #define BLOCK_SIZE 1024 MD390::MD390(TyTInterface *device, const ErrorStack &err, QObject *parent) : TyTRadio(device, parent), _name("TyT MD-390"), _limits(nullptr) { // Read channels and identify device variance based on the channel frequencies. This may block // the GUI for some time. unsigned addr_start = align_addr(ADDR_CHANNELS, BLOCK_SIZE); unsigned channel_offset = ADDR_CHANNELS-addr_start; uint total_size = align_size(addr_start+NUM_CHANNELS*CHANNEL_SIZE, BLOCK_SIZE)-addr_start; QByteArray buffer(total_size, 0xff); if (! _dev->read_start(0, addr_start, err)) { errMsg(err) << "Cannot read channels from MD-390, cannot determine variant."; return; } for (unsigned i=0; i<(total_size/BLOCK_SIZE); i++){ if (! _dev->read(0, ADDR_CHANNELS, (uint8_t *)buffer.data()+i*BLOCK_SIZE, BLOCK_SIZE, err)) { errMsg(err) << "Cannot read channels from MD-390, cannot determine variant."; return; } } _dev->read_finish(err); // Determine frequency range of channels unsigned channelCount = 0; QPair range(std::numeric_limits::max(),0); for (unsigned i=0; i=range.second)) { _limits = new MD390Limits({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}}, this); _name += "V"; } else if ((350<=range.first) && (400>=range.second)) { _limits = new MD390Limits({{Frequency::fromMHz(350.), Frequency::fromMHz(400.)}}, this); _name += "U"; } else if ((400<=range.first) && (450>=range.second)) { _limits = new MD390Limits({{Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, this); _name += "U"; } else if ((450<=range.first) && (520>=range.second)) { _limits = new MD390Limits({{Frequency::fromMHz(450.), Frequency::fromMHz(520.)}}, this); _name += "U"; } else { // Invalid frequency range, needs "Ignore frequency limits" in settings to write any codeplug. _limits = new MD390Limits({}, this); errMsg(err) << "Cannot determine frequency range from channel frequencies between " << range.first << "MHz and " << range.second << "MHz. Will not check frequency ranges."; } } const QString & MD390::name() const { return _name; } const RadioLimits & MD390::limits() const { return *_limits; } const Codeplug & MD390::codeplug() const { return _codeplug; } Codeplug & MD390::codeplug() { return _codeplug; } const CallsignDB * MD390::callsignDB() const { return nullptr; } CallsignDB * MD390::callsignDB() { return nullptr; } RadioInfo MD390::defaultRadioInfo() { return RadioInfo(RadioInfo::MD390, "md390", "MD-390", "TyT", TyTInterface::interfaceInfo(), QList{ RadioInfo(RadioInfo::RT8, "rt8", "RT8", "Retevis", TyTInterface::interfaceInfo()) }); } qdmr-0.12.3/lib/md390.hh000066400000000000000000000041741501654372000144740ustar00rootroot00000000000000/** @defgroup md390 TYT MD-390 (U/V) * Device specific classes for TyT MD-390, both the VHF and UHF version. * * \image html md390.jpg "MD-390" width=200px * \image latex md390.jpg "MD-390" width=200px * * The TYT MD-390 is a decent VHF or UHF FM and DMR handheld radio. * The radio is available with and without an GPS option. @c libdmrconf will support that * feature. Non-GPS variants of that radio will simply ignore any GPS system settings. * * These radios support up to 1000 channels organized in 250 zones. Each zone may hold up to 16 * channels. There are also up to 250 scanlists * holding up to 31(?) channels each. * * The radio can hold up to 1000 contacts (DMR contacts) and 250 RX group lists as well as up to 50 * pre-programmed messages. * * @ingroup tyt */ #ifndef MD390_HH #define MD390_HH #include "tyt_radio.hh" #include "md390_codeplug.hh" /** Implements an USB interface to the TYT MD-390 VHF/UHF 5W DMR (Tier I&II) radio. * * The TYT MD-390 radio use the TyT typical DFU-style communication protocol * to read and write codeplugs onto the radio (see @c TyTRadio). * * @ingroup md390 */ class MD390 : public TyTRadio { Q_OBJECT public: /** Constructor. * @param device Specifies the DFU device to use for communication with the device. * @param err Passes an error stack to put error messages on. * @param parent The QObject parent. */ MD390(TyTInterface *device=nullptr, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr); const QString &name() const; const RadioLimits &limits() const; const Codeplug &codeplug() const; Codeplug &codeplug(); const CallsignDB *callsignDB() const; CallsignDB *callsignDB(); /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); private: /** Holds the name of the device. */ QString _name; /** The codeplug object. */ MD390Codeplug _codeplug; private: /** Instance of limits for this radio. Gets intantiated for every radio as it depends on the * radio variant. */ RadioLimits *_limits; }; #endif // MD2017_HH qdmr-0.12.3/lib/md390_codeplug.cc000066400000000000000000000424241501654372000163440ustar00rootroot00000000000000#include "md390_codeplug.hh" #include "config.hh" #include "logger.hh" #include "intermediaterepresentation.hh" #define NUM_CHANNELS 1000 #define ADDR_CHANNELS 0x01ee00 #define CHANNEL_SIZE 0x000040 #define NUM_CONTACTS 1000 #define ADDR_CONTACTS 0x005f80 #define CONTACT_SIZE 0x000024 #define NUM_ZONES 250 #define ADDR_ZONES 0x0149e0 #define ZONE_SIZE 0x000040 #define NUM_GROUPLISTS 250 #define ADDR_GROUPLISTS 0x00ec20 #define GROUPLIST_SIZE 0x000060 #define NUM_SCANLISTS 250 #define ADDR_SCANLISTS 0x018860 #define SCANLIST_SIZE 0x000068 #define ADDR_TIMESTAMP 0x002000 #define ADDR_SETTINGS 0x002040 #define ADDR_BOOTSETTINGS 0x02f000 #define ADDR_MENUSETTINGS 0x0020f0 #define MENUSETTINGS_SIZE 0x000010 #define ADDR_BUTTONSETTINGS 0x002100 #define ADDR_PRIVACY_KEYS 0x0059c0 #define NUM_GPSSYSTEMS 16 #define ADDR_GPSSYSTEMS 0x03ec40 #define GPSSYSTEM_SIZE 0x000010 #define ADDR_EMERGENCY_SETTINGS 0x005a50 #define NUM_EMERGENCY_SYSTEMS 32 #define ADDR_EMERGENCY_SYSTEMS 0x005a60 #define EMERGENCY_SYSTEM_SIZE 0x000028 /* ********************************************************************************************* * * Implementation of MD390Codeplug::ChannelElement * ********************************************************************************************* */ MD390Codeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size) : DM1701Codeplug::ChannelElement::ChannelElement(ptr, size) { // pass... } MD390Codeplug::ChannelElement::ChannelElement(uint8_t *ptr) : DM1701Codeplug::ChannelElement(ptr, CHANNEL_SIZE) { // pass... } void MD390Codeplug::ChannelElement::clear() { DM1701Codeplug::ChannelElement::clear(); enableCompressedUDPHeader(false); } bool MD390Codeplug::ChannelElement::compressedUDPHeader() const { return !getBit(0x0003, 6); } void MD390Codeplug::ChannelElement::enableCompressedUDPHeader(bool enable) { setBit(0x0003, 6, !enable); } Channel * MD390Codeplug::ChannelElement::toChannelObj(const ErrorStack &err) const { Channel *ch = DM1701Codeplug::ChannelElement::toChannelObj(err); if (nullptr == ch) return ch; // Apply extension if (ch->tytChannelExtension()) { ch->tytChannelExtension()->enableCompressedUDPHeader(compressedUDPHeader()); } return ch; } void MD390Codeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) { DM1701Codeplug::ChannelElement::fromChannelObj(c, ctx); // apply extensions (extension will be created in TyTCodeplug::ChannelElement::fromChannelObj) if (TyTChannelExtension *ex = c->tytChannelExtension()) { enableCompressedUDPHeader(ex->compressedUDPHeader()); } } /* ******************************************************************************************** * * Implementation of MD390Codeplug::MenuSettingsElement * ******************************************************************************************** */ MD390Codeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr, size_t size) : TyTCodeplug::MenuSettingsElement(ptr, size) { // pass... } MD390Codeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr) : TyTCodeplug::MenuSettingsElement(ptr, MENUSETTINGS_SIZE) { // pass... } void MD390Codeplug::MenuSettingsElement::clear() { TyTCodeplug::MenuSettingsElement::clear(); enableGPSInformation(true); } bool MD390Codeplug::MenuSettingsElement::gpsInformation() const { return !getBit(0x04, 4); } void MD390Codeplug::MenuSettingsElement::enableGPSInformation(bool enable) { setBit(0x04, 4, !enable); } bool MD390Codeplug::MenuSettingsElement::fromConfig(const Config *config) { if (! TyTCodeplug::MenuSettingsElement::fromConfig(config)) return false; if (TyTConfigExtension *ex = config->tytExtension()) { enableGPSInformation(ex->menuSettings()->gpsInformation()); } return true; } bool MD390Codeplug::MenuSettingsElement::updateConfig(Config *config) { if (! TyTCodeplug::MenuSettingsElement::updateConfig(config)) return false; if (TyTConfigExtension *ex = config->tytExtension()) { ex->menuSettings()->enableGPSInformation(gpsInformation()); } return true; } /* ********************************************************************************************* * * Implementation of MD390Codeplug * ********************************************************************************************* */ MD390Codeplug::MD390Codeplug(QObject *parent) : TyTCodeplug(parent) { addImage("TYT MD-390 Codeplug"); image(0).addElement(0x002000, 0x3e000); // Clear entire codeplug clear(); } Config * MD390Codeplug::preprocess(Config *config, const ErrorStack &err) const { Config *intermediate = TyTCodeplug::preprocess(config, err); if (nullptr == intermediate) { errMsg(err) << "Cannot prepare codeplug for MD-390."; return nullptr; } ZoneSplitVisitor splitter; if (! splitter.process(intermediate, err)) { errMsg(err) << "Cannot split zones in codeplug for MD-390."; delete intermediate; return nullptr; } return intermediate; } bool MD390Codeplug::postprocess(Config *config, const ErrorStack &err) const { if (! TyTCodeplug::postprocess(config, err)) { errMsg(err) << "Cannot post-process MD-390 codeplug."; return false; } ZoneMergeVisitor merger; if (! merger.process(config, err)) { errMsg(err) << "Cannot merge zones from decoded MD-390 codeplug."; return false; } return true; } bool MD390Codeplug::decodeElements(Context &ctx, const ErrorStack &err) { logDebug() << "Decode MD390 codeplug, programmed with CPS version " << TimestampElement(data(ADDR_TIMESTAMP)).cpsVersion() << "."; return TyTCodeplug::decodeElements(ctx, err); } void MD390Codeplug::clearTimestamp() { TimestampElement(data(ADDR_TIMESTAMP)).clear(); } bool MD390Codeplug::encodeTimestamp() { TimestampElement ts(data(ADDR_TIMESTAMP)); ts.setTimestamp(QDateTime::currentDateTime()); return true; } void MD390Codeplug::clearGeneralSettings() { GeneralSettingsElement(data(ADDR_SETTINGS)).clear(); } bool MD390Codeplug::encodeGeneralSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err) return GeneralSettingsElement(data(ADDR_SETTINGS)).fromConfig(config); } bool MD390Codeplug::decodeGeneralSettings(Config *config, const ErrorStack &err) { Q_UNUSED(err) return GeneralSettingsElement(data(ADDR_SETTINGS)).updateConfig(config); } void MD390Codeplug::clearChannels() { // Clear channels for (int i=0; ichannelList()->count()) { chan.fromChannelObj(config->channelList()->channel(i), ctx); } else { chan.clear(); } } return true; } bool MD390Codeplug::createChannels(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; ichannelList()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid channel at index " << i << "."; return false; } } return true; } bool MD390Codeplug::linkChannels(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link channel at index " << i << "."; return false; } } return true; } void MD390Codeplug::clearContacts() { // Clear contacts for (int i=0; icontacts()->digitalCount()) cont.fromContactObj(config->contacts()->digitalContact(i)); else cont.clear(); } return true; } bool MD390Codeplug::createContacts(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; icontacts()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid contact at index " << i << "."; return false; } } return true; } void MD390Codeplug::clearZones() { // Clear zones & zone extensions for (int i=0; i(i+1)) continue; Zone *obj = ctx.get(i+1); zone.setName(obj->name()); // fill channels for (int c=0; c<16; c++) { if (c < obj->A()->count()) zone.setMemberIndex(c, ctx.index(obj->A()->get(c))); } } return true; } bool MD390Codeplug::createZones(Config *config, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) for (int i=0; isetName(zone.name()); config->zones()->add(obj); ctx.add(obj, i+1); } return true; } bool MD390Codeplug::linkZones(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) for (int i=0; i(i+1)) continue; Zone *obj = ctx.get(i+1); for (int j=0; ((j<16) && zone.memberIndex(j)); j++) { if (! ctx.has(zone.memberIndex(j))) { logWarn() << "Cannot link channel with index " << zone.memberIndex(j) << " channel not defined."; continue; } obj->A()->add(ctx.get(zone.memberIndex(j))); } } return true; } void MD390Codeplug::clearGroupLists() { for (int i=0; irxGroupLists()->count()) glist.fromGroupListObj(config->rxGroupLists()->list(i), ctx); else glist.clear(); } return true; } bool MD390Codeplug::createGroupLists(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; irxGroupLists()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid group list at index " << i << "."; return false; } } return true; } bool MD390Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link group list at index " << i << "."; return false; } } return true; } void MD390Codeplug::clearScanLists() { // Clear scan lists for (int i=0; iscanlists()->count()) scan.fromScanListObj(config->scanlists()->scanlist(i), ctx); else scan.clear(); } return true; } bool MD390Codeplug::createScanLists(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; iscanlists()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid scanlist at index " << i << "."; return false; } } return true; } bool MD390Codeplug::linkScanLists(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link scan list at index " << i << "."; return false; } } return true; } void MD390Codeplug::clearPositioningSystems() { // Clear GPS systems for (int i=0; iposSystems()->gpsCount()) { logDebug() << "Encode GPS system #" << i << " '" << config->posSystems()->gpsSystem(i)->name() << "'."; gps.fromGPSSystemObj(config->posSystems()->gpsSystem(i), ctx); } else { gps.clear(); } } return true; } bool MD390Codeplug::createPositioningSystems(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; iposSystems()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid GPS system at index " << i << "."; return false; } } return true; } bool MD390Codeplug::linkPositioningSystems(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link GPS system at index " << i << "."; return false; } } return true; } void MD390Codeplug::clearButtonSettings() { ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).clear(); } bool MD390Codeplug::encodeButtonSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(flags); Q_UNUSED(err) // Encode settings return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).fromConfig(config); } bool MD390Codeplug::decodeButtonSetttings(Config *config, const ErrorStack &err) { Q_UNUSED(err) return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).updateConfig(config); } void MD390Codeplug::clearPrivacyKeys() { EncryptionElement(data(ADDR_PRIVACY_KEYS)).clear(); } bool MD390Codeplug::encodePrivacyKeys(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err); // First, reset keys clearPrivacyKeys(); // Get keys EncryptionElement keys(data(ADDR_PRIVACY_KEYS)); // Encode keys return keys.fromCommercialExt(config->commercialExtension(), ctx); } bool MD390Codeplug::decodePrivacyKeys(Config *config, Context &ctx, const ErrorStack &err) { Q_UNUSED(config) // Get keys EncryptionElement keys(data(ADDR_PRIVACY_KEYS)); // Decode element if (! keys.updateCommercialExt(ctx)) { errMsg(err) << "Cannot create encryption extension."; return false; } return true; } void MD390Codeplug::clearTextMessages() { MessageBankElement(data(Offset::messages())).clear(); } bool MD390Codeplug::encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err) { return MessageBankElement(data(Offset::messages())).encode(ctx, flags, err); } bool MD390Codeplug::decodeTextMessages(Context &ctx, const ErrorStack &err) { return MessageBankElement(data(Offset::messages())).decode(ctx, err); } void MD390Codeplug::clearMenuSettings() { MenuSettingsElement(data(ADDR_MENUSETTINGS)).clear(); } void MD390Codeplug::clearEmergencySystems() { EmergencySettingsElement(data(ADDR_EMERGENCY_SETTINGS)).clear(); for (int i=0; i * Start End Size Content * Segment 0x002000-0x040000 * 0x002000 0x00200c 0x0000c Timestamp see @c TyTCodeplug::TimestampElement. * 0x00200c 0x002040 0x00034 Reserved, filled with 0xff. * 0x002100 0x002140 0x00040 Button config, see @c TyTCodeplug::ButtonSettingsElement. * 0x002140 0x002180 0x00040 Reserved, filled with 0xff. * 0x002040 0x0020f0 0x000b0 General settings see @c TyTCodeplug::GeneralSettingsElement. * 0x002180 0x0059c0 0x03840 50 Text messages @ 0x120 bytes each, see @c TyTCodeplug::MessageElement. * 0x0059c0 0x005a70 0x000b0 ??? Privacy keys, see @c TyTCodeplug::EncryptionElement. * 0x005a70 0x005a80 0x00010 Emergency system settings, see @c TyTCodeplug::EmergencySettingsElement. * 0x005a80 0x005f80 0x00500 Emergency systems, see @c TyTCodeplug::EmergencySystemElement. * 0x005f80 0x00ec20 0x008ca 1000 contacts, see @c TyTCodeplug::ContactElement. * 0x00ec20 0x0149e0 0x05dc0 250 RX Group lists @ 0x60 bytes each, see @c TyTCodeplug::GroupListElement. * 0x0149e0 0x018860 0x03e80 250 Zones @ 0x40 bytes each, see @c TyTCodeplug::ZoneElement. * 0x018860 0x01edf0 0x06590 250 Scanlists @ 0x68 bytes each, see @c TyTCodeplug::ScanListElement. * 0x01ee00 0x02e800 0x0fa00 1000 channels, see @c MD390Codeplug::ChannelElement. * 0x03ec40 0x03ed40 0x00100 16 GPS systems @ 0x10 bytes each, see @c TyTCodeplug::GPSSystemElement. * * * @ingroup md390 */ class MD390Codeplug : public TyTCodeplug { Q_OBJECT public: /** Extends the common @c TyTCodeplug::ChannelElement to implement the MD-390 specific settings. * * Memory layout of the channel (size 0x0040 bytes): * @verbinclude md390_channel.txt */ class ChannelElement: public DM1701Codeplug::ChannelElement { protected: /** Hidden constructor. */ ChannelElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit ChannelElement(uint8_t *ptr); void clear(); /** Returns @c true if the 'compressed UDP data header' is enabled. */ virtual bool compressedUDPHeader() const; /** Enables/disables 'compressed UDP data header'. */ virtual void enableCompressedUDPHeader(bool enable); /** Constructs a generic @c Channel object from the codeplug channel. */ virtual Channel *toChannelObj(const ErrorStack &err=ErrorStack()) const; /** Initializes this codeplug channel from the given generic configuration. */ virtual void fromChannelObj(const Channel *c, Context &ctx); }; /** Extends the @c TyTCodeplug::MenuSettingsElement to implement the MD-390 specific menu settings. * * Memory layout of the settings (size 0x0010 bytes): * @verbinclude md390_menusettings.txt */ class MenuSettingsElement: public TyTCodeplug::MenuSettingsElement { protected: /** Hidden constructor. */ MenuSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit MenuSettingsElement(uint8_t *ptr); void clear(); /** Returns @c true if GPS information is enabled. */ virtual bool gpsInformation() const; /** Enables/disables GPS information menu. */ virtual void enableGPSInformation(bool enable); bool fromConfig(const Config *config); bool updateConfig(Config *config); }; public: /** Empty constructor. */ explicit MD390Codeplug(QObject *parent=nullptr); Config *preprocess(Config *config, const ErrorStack &err) const; bool postprocess(Config *config, const ErrorStack &err) const; virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack()); void clearTimestamp(); bool encodeTimestamp(); void clearGeneralSettings(); bool encodeGeneralSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeGeneralSettings(Config *config, const ErrorStack &err=ErrorStack()); void clearChannels(); bool encodeChannels(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createChannels(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()); void clearContacts(); bool encodeContacts(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createContacts(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); void clearZones(); bool encodeZones(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createZones(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()); void clearGroupLists(); bool encodeGroupLists(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createGroupLists(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); void clearScanLists(); bool encodeScanLists(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createScanLists(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack()); void clearPositioningSystems(); bool encodePositioningSystems(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createPositioningSystems(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkPositioningSystems(Context &ctx, const ErrorStack &err=ErrorStack()); void clearButtonSettings(); bool encodeButtonSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeButtonSetttings(Config *config, const ErrorStack &err=ErrorStack()); void clearPrivacyKeys(); bool encodePrivacyKeys(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err); bool decodePrivacyKeys(Config *config, Context &ctx, const ErrorStack &err); void clearTextMessages(); bool encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err); bool decodeTextMessages(Context &ctx, const ErrorStack &err); void clearMenuSettings(); void clearEmergencySystems(); protected: /** Some internal offsets within the codeplug. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int messages() { return 0x002180; } /// @endcond }; }; #endif // MD390CODEPLUG_HH qdmr-0.12.3/lib/md390_filereader.cc000066400000000000000000000026611501654372000166430ustar00rootroot00000000000000#include "md390_filereader.hh" #include #include #define SEGMENT0_FILE_ADDR 0x00002225 #define SEGMENT0_TARGET_ADDR 0x00002000 #define SEGMENT0_SIZE 0x0003e000 bool MD390FileReader::read(const QString &filename, MD390Codeplug *codeplug, const ErrorStack &err) { // Check file properties QFileInfo info(filename); if (! info.exists()) { errMsg(err) << "Cannot open file '" << filename << "': File does not exisist."; return false; } if (262709 != info.size()) { errMsg(err) << "Cannot read codeplug file '" << filename << "': File size is not 262709 bytes."; return false; } // Open file QFile file(filename); if (! file.open(QFile::ReadOnly)) { errMsg(err) << "Cannot open file '" << filename << "': " << file.errorString() << "."; return false; } // Read file content if (! file.seek(SEGMENT0_FILE_ADDR)) { errMsg(err) << "Cannot read codeplug file '" << filename << "': Cannot seek within file: " << file.errorString() << "."; file.close(); return false; } char *ptr = (char *)codeplug->data(SEGMENT0_TARGET_ADDR); size_t n = SEGMENT0_SIZE; while (0 < n) { int nread = file.read(ptr, n); if (0 > nread) { errMsg(err) << "Cannot read codeplug file '" << filename << "': " << file.errorString() << "."; file.close(); return false; } n -= nread; ptr += nread; } return true; } qdmr-0.12.3/lib/md390_filereader.hh000066400000000000000000000022061501654372000166500ustar00rootroot00000000000000#ifndef MD390FILEREADER_HH #define MD390FILEREADER_HH #include "md390_codeplug.hh" /** Methods to read manufacturer codeplug files. * * The file format of the stock CPS is still pretty simple. The first part of the file consists of * a mal-formed DFU file. This contains a single image with a single element containing the * first section of the memory written to the device. The second section is then added as-is * to the end of the file. Due to the DFU header/footer, the file and memory offsets differ. * * * * *
      File Start Memory Start Size
      0x002225 0x002000 0x03e000
      * * @ingroup md390 */ class MD390FileReader { public: /** Reads manufacturer codeplug file into given codeplug object. * @param filename Specifies the file to read. * @param codeplug Specifies the codeplug object to store read codeplug. * @param err Error stack. * @returns @c true on success and @c false on error. */ static bool read(const QString &filename, MD390Codeplug *codeplug, const ErrorStack &err = ErrorStack()); }; #endif // MD390FILEREADER_HH qdmr-0.12.3/lib/md390_limits.cc000066400000000000000000000205461501654372000160440ustar00rootroot00000000000000#include "md390_limits.hh" #include "md390_codeplug.hh" #include "channel.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "zone.hh" #include "scanlist.hh" #include "gpssystem.hh" #include "roamingzone.hh" MD390Limits::MD390Limits(const std::initializer_list> &freqRanges, QObject *parent) : RadioLimits(true, parent) { // Define limits for call-sign DB _hasCallSignDB = false; _callSignDBImplemented = false; _numCallSignDBEntries = 0; // Define limits for satellite config _hasSatelliteConfig = false; _satelliteConfigImplemented = false; _numSatellites = 0; add("settings", new RadioLimitItem { { "introLine1", new RadioLimitString(-1, 10, RadioLimitString::Unicode) }, { "introLine2", new RadioLimitString(-1, 10, RadioLimitString::Unicode) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum { unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid), unsigned(Channel::Power::High) } }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, -1) } /// @todo check default radio ID. } ); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList{ { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, {"id", new RadioLimitUInt(0, 16777215)} } } } ); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum { (unsigned) DMRContact::PrivateCall, (unsigned) DMRContact::GroupCall, (unsigned) DMRContact::AllCall }}, { "number", new RadioLimitUInt(0, 16777215) } } }, { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() } } ); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, { "contacts", new RadioLimitGroupCallRefList(1, 32) } }) ); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, 1000, new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::Unicode)}, {"rxFrequency", new RadioLimitFrequencies(freqRanges, true)}, {"txFrequency", new RadioLimitTransmitFrequencies(freqRanges)}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRefIgnored(nullptr, RadioLimitIssue::Hint)} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::Unicode)}, {"rxFrequency", new RadioLimitFrequencies(freqRanges, true)}, {"txFrequency", new RadioLimitTransmitFrequencies(freqRanges)}, {"power", new RadioLimitEnum { unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid), unsigned(Channel::Power::High), }}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, true)}, {"aprs", new RadioLimitObjRefIgnored()}, {"roaming", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } ) ); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 1, 250, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, // 16 ASCII chars in name { "A", new RadioLimitRefList(0, 64, Channel::staticMetaObject) }, { "B", new RadioLimitRefList(0, 64, Channel::staticMetaObject) }, { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions } ) ); /* Define limits for scan lists. */ add("scanlists", new RadioLimitList( ScanList::staticMetaObject, 0, 250, new RadioLimitObject{ { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, { "primary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "secondary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "revert", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "channels", new RadioLimitRefList(0, 31, Channel::staticMetaObject) } }) ); /* Define limits for positioning systems. */ add("positioning", new RadioLimitList({ { GPSSystem::staticMetaObject, 0, 16, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "period", new RadioLimitUInt(0, 7650) }, { "contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false) }, { "revert", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } }, { APRSSystem::staticMetaObject, 0, -1, new RadioLimitIgnored() } } ) ); /* Check encryption keys. */ add("commercial", new RadioLimitItem { {"encryptionKeys", new RadioLimitList { {BasicEncryptionKey::staticMetaObject, 0, TyTCodeplug::EncryptionElement::Limit::basicKeys(), new RadioLimitObject { {"name", new RadioLimitIgnored()}, {"key", new RadioLimitStringRegEx("[0-9a-fA-F]{4}")} }}, {AESEncryptionKey::staticMetaObject, 0, TyTCodeplug::EncryptionElement::Limit::advancedKeys(), new RadioLimitObject { {"name", new RadioLimitIgnored()}, {"key", new RadioLimitStringRegEx("[0-9a-fA-F]{32}")} }} } } }); /* Ignore roaming zones. */ add("roaming", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint) ) ); } qdmr-0.12.3/lib/md390_limits.hh000066400000000000000000000006161501654372000160520ustar00rootroot00000000000000#ifndef MD390LIMITS_HH #define MD390LIMITS_HH #include "radiolimits.hh" /** Implements the radio limits for TyT MD-390 radios. * @ingroup md390 */ class MD390Limits: public RadioLimits { Q_OBJECT public: /** Constructor from frequency ranges. */ MD390Limits(const std::initializer_list > &freqRanges, QObject *parent=nullptr); }; #endif // MD390LIMITS_HH qdmr-0.12.3/lib/melody.cc000066400000000000000000000255361501654372000151240ustar00rootroot00000000000000#include "melody.hh" #include "logger.hh" #include /* ********************************************************************************************* * * Implementation of Melody * ********************************************************************************************* */ Melody::Melody(unsigned int bpm, QObject *parent) : ConfigItem(parent), _bpm(bpm), _melody() { // pass... } ConfigItem * Melody::clone() const { Melody *melody = new Melody(); if (! melody->copy(*this)) { melody->deleteLater(); return nullptr; } return melody; } bool Melody::copy(const ConfigItem &other) { if (! ConfigItem::copy(other)) return false; const Melody *omelody = other.as(); _bpm = omelody->_bpm; _melody = omelody->_melody; return true; } Melody::iterator Melody::begin() { return _melody.begin(); } Melody::iterator Melody::end() { return _melody.end(); } Melody::const_iterator Melody::begin() const { return _melody.begin(); } Melody::const_iterator Melody::end() const { return _melody.end(); } size_t Melody::count() const { return _melody.count(); } const Melody::Note & Melody::operator [](size_t index) const { return _melody[index]; } Melody::Note & Melody::operator [](size_t index) { return _melody[index]; } unsigned int Melody::bpm() const { return _bpm; } void Melody::setBPM(unsigned int bpm) { if (_bpm == bpm) return; _bpm = bpm; emit modified(this); } bool Melody::fromLilypond(const QString &melody) { _melody.clear(); Note::Duration currentDuration = Note::Duration::Whole; for (QString note : melody.split(" ", Qt::SkipEmptyParts)) { Note n; if (! n.fromLilypond(note, currentDuration)) return false; currentDuration = n.duration; _melody.append(n); } emit modified(this); return true; } QString Melody::toLilypond() const { QStringList res; Note::Duration currentDuration = Note::Duration::Whole; for (Note note : _melody) { res.append(note.toLilypond(currentDuration)); currentDuration = note.duration; } return res.join(" "); } QVector> Melody::toTones() const { QVector> res; for (const Note ¬e: _melody) { res.append(note.toTone(_bpm)); } return res; } bool Melody::infer(const QVector > &tones) { // Find minimum quantization error -> infer also BPM unsigned int best_bpm = 30; unsigned int best_cost = quantizationTimingError(tones, best_bpm) + std::abs(100-(int)best_bpm); for (unsigned int bpm=31; bpm<200; bpm++) { unsigned int cost = quantizationTimingError(tones, best_bpm); if (cost < best_cost) { best_bpm = bpm; best_cost = cost + std::abs(100 - (int)bpm); } } _bpm = best_bpm; _melody.clear(); for(const QPair &tone: tones) { Note note; note.infer(tone.first, tone.second, _bpm); _melody.append(note); } emit modified(this); return true; } unsigned int Melody::quantizationTimingError(const QVector > &tones, unsigned int bpm) { if (0 == tones.count()) return 0; unsigned int error=0; for (const QPair &tone: tones) { error += Note::quantizationTimingError(tone.second, bpm); } return error / tones.count(); } /* ********************************************************************************************* * * Implementation of Melody::Note * ********************************************************************************************* */ Melody::Note::Note() : tone(Tone::C), duration(Duration::Quarter), dotted(false), octave(0) { // pass... } bool Melody::Note::fromLilypond(const QString ¬e, Duration currentDuration) { QRegularExpression note_pattern("^(c|cis|des|d|dis|ees|e|f|fis|ges|g|gis|aes|a|ais|bes|b)([,]+|[']+|)(1|2|4|8|16|)(\\.|)$"); QRegularExpression rest_pattern("^r(1|2|4|8|16|)(\\.|)$"); QRegularExpressionMatch note_match = note_pattern.match(note); if (note_match.isValid()) { if ("c" == note_match.captured(1)) tone=Tone::C; else if (("cis" == note_match.captured(1)) || ("des" == note_match.captured(1))) tone=Tone::Cis; else if ("d" == note_match.captured(1)) tone=Tone::D; else if (("dis" == note_match.captured(1)) || ("ees" == note_match.captured(1))) tone=Tone::Dis; else if ("e" == note_match.captured(1)) tone=Tone::E; else if ("f" == note_match.captured(1)) tone=Tone::F; else if (("fis" == note_match.captured(1)) || ("ges" == note_match.captured(1))) tone=Tone::Fis; else if ("g" == note_match.captured(1)) tone=Tone::G; else if (("gis" == note_match.captured(1)) || ("aes" == note_match.captured(1))) tone=Tone::Gis; else if ("a" == note_match.captured(1)) tone=Tone::A; else if (("ais" == note_match.captured(1)) || ("bes" == note_match.captured(1))) tone=Tone::Ais; else if ("b" == note_match.captured(1)) tone=Tone::B; else return false; if (0 == note_match.capturedLength(2)) octave = 0; else if ('\'' == note_match.captured(2).at(0)) octave = note_match.capturedLength(3); else if (',' == note_match.captured(2).at(0)) octave = -note_match.capturedLength(3); else return false; if (0 == note_match.capturedLength(3)) duration = currentDuration; else if ("1" == note_match.captured(3)) duration = Duration::Whole; else if ("2" == note_match.captured(3)) duration = Duration::Half; else if ("4" == note_match.captured(3)) duration = Duration::Quarter; else if ("8" == note_match.captured(3)) duration = Duration::Eighth; else if ("16" == note_match.captured(3)) duration = Duration::Sixteenth; else return false; dotted = (1 == note_match.capturedLength(4)); return true; } QRegularExpressionMatch rest_match = rest_pattern.match(note); if (rest_match.isValid()) { tone = Tone::Rest; if (0 == note_match.capturedLength(1)) duration = currentDuration; else if ("1" == note_match.captured(1)) duration = Duration::Whole; else if ("2" == note_match.captured(1)) duration = Duration::Half; else if ("4" == note_match.captured(1)) duration = Duration::Quarter; else if ("8" == note_match.captured(1)) duration = Duration::Eighth; else if ("16" == note_match.captured(1)) duration = Duration::Sixteenth; else return false; dotted = (1 == note_match.capturedLength(2)); return true; } return false; } QString Melody::Note::toLilypond(Duration currentDuration) const { QString res; res.reserve(10); switch (tone) { case Tone::C: res.append("c"); break; case Tone::Cis: res.append("cis"); break; case Tone::D: res.append("d"); break; case Tone::Dis: res.append("dis"); break; case Tone::E: res.append("e"); break; case Tone::F: res.append("f"); break; case Tone::Fis: res.append("fis"); break; case Tone::G: res.append("g"); break; case Tone::Gis: res.append("gis"); break; case Tone::A: res.append("a"); break; case Tone::Ais: res.append("ais"); break; case Tone::B: res.append("b"); break; case Tone::Rest: res.append("r"); break; } if (Tone::Rest != tone) { if (0 > octave) { for (int i=0; i<(-octave); i++) res.append(","); } else if (0 < octave) { for (int i=0; i Melody::Note::toTone(unsigned int bpm) const { static const unsigned int bar = (4*60000)/bpm; unsigned int dur = bar; if (Duration::Whole == duration) { dur = bar; if (dotted) dur += dur/2; } else if (Duration::Half == duration) { dur = bar/2; if (dotted) dur += dur/2; } else if (Duration::Quarter == duration) { dur = bar/4; if (dotted) dur += dur/2; } else if (Duration::Eighth == duration) { dur = bar/8; if (dotted) dur += dur/2; } else if (Duration::Sixteenth == duration) { dur = bar/16; if (dotted) dur += dur/2; } if (Tone::Rest == tone) { return QPair(0.0, dur); } int halfTones = 12*octave; switch (tone) { case Tone::C: halfTones -= 9; break; case Tone::Cis: halfTones -= 8; break; case Tone::D: halfTones -= 7; break; case Tone::Dis: halfTones -= 6; break; case Tone::E: halfTones -= 5; break; case Tone::F: halfTones -= 4; break; case Tone::Fis: halfTones -= 3; break; case Tone::G: halfTones -= 2; break; case Tone::Gis: halfTones -= 1; break; case Tone::A: break; case Tone::Ais: halfTones += 1; break; case Tone::B: halfTones += 2; break; case Tone::Rest: break; } double freq = 440.*std::pow(2.0,halfTones/12.0); return QPair(freq, dur); } unsigned int Melody::Note::infer(double frequency, unsigned int ms, unsigned int bpm) { if (frequency) { // Try to infer half-tones from A4 int halfTones = std::round(std::log2(frequency/440.)*12.0); int halfTonesC4 = halfTones+9; octave = halfTonesC4/12; halfTonesC4 = std::abs(halfTonesC4)%12; switch(halfTonesC4) { case 0: tone = Tone::C; break; case 1: tone = Tone::Cis; break; case 2: tone = Tone::D; break; case 3: tone = Tone::Dis; break; case 4: tone = Tone::E; break; case 5: tone = Tone::F; break; case 6: tone = Tone::Fis; break; case 7: tone = Tone::G; break; case 8: tone = Tone::Gis; break; case 9: tone = Tone::A; break; case 10: tone = Tone::Ais; break; case 11: tone = Tone::B; break; } } else if (ms) { // Obviously a rest octave = 0; tone = Tone::Rest; } else { logWarn() << "Cannot infer a note of frequency and duration 0."; octave = 0; tone = Tone::Rest; duration = Duration::Quarter; dotted=false; } // Try to infer note duration from duration and BPM static const unsigned int bar = (4*60000)/bpm; int fraction = std::round(std::log2(bar)-std::log2(ms)); int diff = 0; if (1 > fraction) { duration = Duration::Whole; diff = std::abs((int)ms-(int)bar); } else if (2 > fraction) { duration = Duration::Half; diff = std::abs((int)ms-(int)bar/2); } else if (3 > fraction) { duration = Duration::Quarter; diff = std::abs((int)ms-(int)bar/4); } else if (4 > fraction) { duration = Duration::Eighth; diff = std::abs((int)ms-(int)bar/8); } else { duration = Duration::Sixteenth; diff = std::abs((int)ms-(int)bar/16); } return diff; } unsigned int Melody::Note::quantizationTimingError(unsigned int ms, unsigned int bpm) { static const unsigned int bar = (4*60000)/bpm; int fraction = std::round(std::log2(bar)-std::log2(ms)); // Limit onto [2^0, 2^4] fractions == [1/1, 1/16] fraction = std::max(0, std::min(4, fraction)); return std::abs(ms - bar*std::pow(2, fraction)); } qdmr-0.12.3/lib/melody.hh000066400000000000000000000107361501654372000151320ustar00rootroot00000000000000#ifndef MELODY_HH #define MELODY_HH #include "configobject.hh" #include /** A config item that encodes a melody. This can be used to configure several melodies like * ring tones and boot-up melodies. Have fun with it. In contrast to the common manufacturer CPSs, * qdmr uses a proper musical notation for that. * * This however, comes with some difficulties. While the translation from musical notation to * tone frequencies and durations is easy, the reverse direction is not. The note duration is the * most difficult. In musical notation, durations are expressed in terms of fractions of a bar, * implicitly defined by the beat frequency. This additional information must be inferred. To achieve * this, this class searches for the BPM, that minimizes the quantization error in the duration * while keeping the BPM as close as possible to 100 BPM. Yes, this is utterly over-engineered. * * @ingroup config */ class Melody : public ConfigItem { Q_OBJECT /** The BPM of the melody. */ Q_PROPERTY(unsigned int bpm READ bpm WRITE setBPM) /** The melody in LilyPond notation. */ Q_PROPERTY(QString melody READ toLilypond WRITE fromLilypond) public: /** Encodes a note, that is tone and duration. */ struct Note { /** Possible tone values. */ enum class Tone { Rest, C, Cis, D, Dis, E, F, Fis, G, Gis, A, Ais, B }; /** Note durations as fractions of a bar. */ enum class Duration { Whole, Half, Quarter, Eighth, Sixteenth }; /** The note tone. */ Tone tone; /** The note duration. */ Duration duration; /** If @c true, the note/rest is dottet. */ bool dotted; /** The octave of the note, 0 means middle. */ int octave; /** Default constructor. A middle C quarter note. */ Note(); /** Reads a note in Lilypond notation. */ bool fromLilypond(const QString ¬e, Duration currentDuration); /** Serializes the note in Lilypond notation. */ QString toLilypond(Duration currentDuration) const; /** Converts the note to a frequency in Hz and duration in seconds. */ QPair toTone(unsigned int bpm) const; /** Infers the note from the given frequency and duration in ms. * This is guesswork, consequently there will be some issues. The function updates the note and * returns the timing error in ms. */ unsigned int infer(double frequency, unsigned int ms, unsigned int bpm); /** Computes the quantization timing error for the given duration and BPM. */ static unsigned int quantizationTimingError(unsigned int ms, unsigned int bpm); }; /** Iterator over notes. */ typedef QVector::iterator iterator; /** Const iterator over notes. */ typedef QVector::const_iterator const_iterator; public: /** Empty constructor. */ Melody(unsigned int bpm=100, QObject *parent = nullptr); ConfigItem *clone() const; bool copy(const ConfigItem &other); /** Returns an iterator pointing at the first note. */ iterator begin(); /** Returns an iterator pointing right after the last note. */ iterator end(); /** Returns a const-iterator pointing at the first note. */ const_iterator begin() const; /** Returns a const-iterator pointing right after the last note. */ const_iterator end() const; /** Returns the number of notes (and rests) of this melody. */ size_t count() const; /** Element access. */ const Note &operator [](size_t index) const; /** Element access. */ Note &operator [](size_t index); /** Returns the BPM of the melody. */ unsigned int bpm() const; /** Sets the BPM of the melody. */ void setBPM(unsigned int bpm); /** Parses the Lilypond notation of the melody. * For example, * @code * a8 b e2 des4 d * @endcode */ bool fromLilypond(const QString &melody); /** Serializes the melody into Lilypond notation. */ QString toLilypond() const; /** Converts the melody to a series of tones in terms of frequency and duration in ms. */ QVector> toTones() const; /** Infer melody from a vector of frequeny-duration pairs. */ bool infer(const QVector> &tones); protected: /** Computes the absolute quantization timing error over the given melody for the given BPM. */ static unsigned int quantizationTimingError( const QVector> &tones, unsigned int bpm); protected: /** Holds the beats per minute. */ unsigned int _bpm; /** The actual melody. */ QVector _melody; }; #endif // MELODY_HH qdmr-0.12.3/lib/opengd77.cc000066400000000000000000000014061501654372000152530ustar00rootroot00000000000000#include "opengd77.hh" #include "logger.hh" #include "config.hh" OpenGD77::OpenGD77(OpenGD77Interface *device, QObject *parent) : OpenGD77Base(device, parent), _name("Open GD-77"), _codeplug(), _callsigns() { _satelliteConfig = new OpenGD77SatelliteConfig(this); } const QString & OpenGD77::name() const { return _name; } const Codeplug & OpenGD77::codeplug() const { return _codeplug; } Codeplug & OpenGD77::codeplug() { return _codeplug; } const CallsignDB * OpenGD77::callsignDB() const { return &_callsigns; } CallsignDB * OpenGD77::callsignDB() { return &_callsigns; } RadioInfo OpenGD77::defaultRadioInfo() { return RadioInfo( RadioInfo::OpenGD77, "opengd77", "OpenGD77", "OpenGD77 Project", OpenGD77Interface::interfaceInfo()); } qdmr-0.12.3/lib/opengd77.hh000066400000000000000000000022601501654372000152640ustar00rootroot00000000000000/** @defgroup ogd77 Open GD-77 Firmware * Implements a radio running the Open GD77 firmware. * @ingroup dsc */ #ifndef OPENGD77_HH #define OPENGD77_HH #include "opengd77base.hh" #include "opengd77_interface.hh" #include "opengd77_codeplug.hh" #include "opengd77_callsigndb.hh" /** Implements an USB interface to Open GD-77(S) VHF/UHF 5W DMR (Tier I&II) radios. * * @ingroup ogd77 */ class OpenGD77 : public OpenGD77Base { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit OpenGD77(OpenGD77Interface *device=nullptr, QObject *parent=nullptr); const QString &name() const; const Codeplug &codeplug() const; Codeplug &codeplug(); const CallsignDB *callsignDB() const; CallsignDB *callsignDB(); /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); protected: /** The device identifier. */ QString _name; /** The actual binary codeplug representation. */ OpenGD77Codeplug _codeplug; /** The actual binary callsign DB representation. */ OpenGD77CallsignDB _callsigns; }; #endif // OPENGD77_HH qdmr-0.12.3/lib/opengd77_callsigndb.cc000066400000000000000000000034171501654372000174410ustar00rootroot00000000000000#include "opengd77_callsigndb.hh" #include "utils.hh" #include "userdatabase.hh" #include #define USERDB_SIZE 0x40000 #define USERDB_NUM_ENTRIES (USERDB_SIZE-sizeof(userdb_t))/sizeof(userdb_entry_t) /* ******************************************************************************************** * * Implementation of OpenGD77CallsignDB * ******************************************************************************************** */ OpenGD77CallsignDB::OpenGD77CallsignDB(QObject *parent) : OpenGD77BaseCallsignDB(parent) { addImage("OpenGD77 call-sign database"); } bool OpenGD77CallsignDB::encode(UserDatabase *calldb, const Selection &selection, const ErrorStack &err) { Q_UNUSED(err) // Limit entries to USERDB_NUM_ENTRIES qint64 n = std::min(calldb->count(), qint64(USERDB_NUM_ENTRIES)); if (selection.hasCountLimit()) n = std::min(n, (qint64)selection.countLimit()); // If there are no entries -> done. if (0 == n) return true; // Select first n entries and sort them in ascending order of their IDs QVector users; for (unsigned i=0; iuser(i)); std::sort(users.begin(), users.end(), [](const UserDatabase::User &a, const UserDatabase::User &b) { return a.id < b.id; }); // Allocate segment for user db if requested unsigned size = align_size(sizeof(userdb_t)+n*sizeof(userdb_entry_t), Limit::blockSize()); this->image(0).addElement(Offset::callsignDB(), size); // Encode user DB userdb_t *userdb = (userdb_t *)this->data(Offset::callsignDB()); userdb->clear(); userdb->setSize(n); userdb_entry_t *db = (userdb_entry_t *)this->data(Offset::callsignDB()+sizeof(userdb_t)); for (unsigned i=0; i #include /* ******************************************************************************************** * * Implementation of OpenGD77Codeplug * ******************************************************************************************** */ OpenGD77Codeplug::OpenGD77Codeplug(QObject *parent) : OpenGD77BaseCodeplug(parent) { // Delete allocated image by GD77 codeplug addImage("OpenGD77 Codeplug EEPROM"); image(EEPROM).addElement(0x00080, 0x05fe0); image(EEPROM).addElement(0x07500, 0x03b00); addImage("OpenGD77 Codeplug FLASH"); image(FLASH).addElement(0x00000, 0x011a0); image(FLASH).addElement(0x7b000, 0x13e60); } void OpenGD77Codeplug::clearGeneralSettings() { GeneralSettingsElement(data(Offset::settings(), ImageIndex::settings())).clear(); } bool OpenGD77Codeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { GeneralSettingsElement el(data(Offset::settings(), ImageIndex::settings())); if (! flags.updateCodePlug) el.clear(); return el.encode(ctx, err); } bool OpenGD77Codeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) { return GeneralSettingsElement(data(Offset::settings(), ImageIndex::settings())).decode(ctx, err); } void OpenGD77Codeplug::clearDTMFSettings() { //DTMFSettingsElement(data(Offset::settings(), ImageIndex::settings())).clear(); } bool OpenGD77Codeplug::encodeDTMFSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err); return true; } bool OpenGD77Codeplug::decodeDTMFSettings(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); return true; } void OpenGD77Codeplug::clearAPRSSettings() { APRSSettingsBankElement(data(Offset::aprsSettings(), ImageIndex::aprsSettings())).clear(); } bool OpenGD77Codeplug::encodeAPRSSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { APRSSettingsBankElement el(data(Offset::aprsSettings(), ImageIndex::aprsSettings())); if (! flags.updateCodePlug) el.clear(); return el.encode(ctx, err); } bool OpenGD77Codeplug::decodeAPRSSettings(Context &ctx, const ErrorStack &err) { return APRSSettingsBankElement(data(Offset::aprsSettings(), ImageIndex::aprsSettings())) .decode(ctx, err); } bool OpenGD77Codeplug::linkAPRSSettings(Context &ctx, const ErrorStack &err) { return APRSSettingsBankElement(data(Offset::aprsSettings(), ImageIndex::aprsSettings())) .link(ctx, err); } bool OpenGD77Codeplug::encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); return BootSettingsElement(data(Offset::bootSettings(), ImageIndex::bootSettings())) .encode(ctx, err); } bool OpenGD77Codeplug::decodeBootSettings(Context &ctx, const ErrorStack &err) { return BootSettingsElement(data(Offset::bootSettings(), ImageIndex::bootSettings())) .decode(ctx, err); } void OpenGD77Codeplug::clearContacts() { ContactBankElement(data(Offset::contacts(), ImageIndex::contacts())).clear(); } bool OpenGD77Codeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); return ContactBankElement(data(Offset::contacts(), ImageIndex::contacts())).encode(ctx, err); } bool OpenGD77Codeplug::createContacts(Context &ctx, const ErrorStack &err) { return ContactBankElement(data(Offset::contacts(), ImageIndex::contacts())).decode(ctx, err); } void OpenGD77Codeplug::clearDTMFContacts() { DTMFContactBankElement(data(Offset::dtmfContacts(), ImageIndex::dtmfContacts())).clear(); } bool OpenGD77Codeplug::encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); return DTMFContactBankElement(data(Offset::dtmfContacts(), ImageIndex::dtmfContacts())) .encode(ctx, err); } bool OpenGD77Codeplug::createDTMFContacts(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) return DTMFContactBankElement(data(Offset::dtmfContacts(), ImageIndex::dtmfContacts())) .decode(ctx, err); } void OpenGD77Codeplug::clearChannels() { for (unsigned int b=0; b(c)) { if (! bank.channel(i).encode(ctx.get(c), ctx, err)) { errMsg(err) << "Cannot encode channel '" << ctx.get(c)->name() << "' at index " << i << " of bank " << b << "."; return false; } bank.enable(i, true); } else { bank.enable(i, false); } } } return true; } bool OpenGD77Codeplug::createChannels(Context &ctx, const ErrorStack &err) { for (unsigned int b=0,c=0; bchannelList()->add(obj); ctx.add(obj, c++); } } return true; } bool OpenGD77Codeplug::linkChannels(Context &ctx, const ErrorStack &err) { for (unsigned int b=0,c=0; b(c); if (! bank.channel(i).link(obj, ctx, err)) { errMsg(err) << "Cannot link channel '" << obj->name() << "' from index " << i << " in bank " << b << "."; return false; } c++; } } return true; } void OpenGD77Codeplug::clearBootSettings() { BootSettingsElement(data(Offset::bootSettings(), ImageIndex::bootSettings())).clear(); } void OpenGD77Codeplug::clearVFOSettings() { VFOChannelElement(data(Offset::vfoA(), ImageIndex::vfoA())).clear(); VFOChannelElement(data(Offset::vfoB(), ImageIndex::vfoB())).clear(); } void OpenGD77Codeplug::clearZones() { ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).clear(); } bool OpenGD77Codeplug::encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); return ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).encode(ctx, err); } bool OpenGD77Codeplug::createZones(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) return ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).decode(ctx, err); } bool OpenGD77Codeplug::linkZones(Context &ctx, const ErrorStack &err) { return ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).link(ctx, err); } void OpenGD77Codeplug::clearGroupLists() { GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).clear(); } bool OpenGD77Codeplug::encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); return GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).encode(ctx, err); } bool OpenGD77Codeplug::createGroupLists(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) return GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).decode(ctx, err); } bool OpenGD77Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) { return GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).link(ctx, err); } qdmr-0.12.3/lib/opengd77_codeplug.hh000066400000000000000000000211151501654372000171460ustar00rootroot00000000000000#ifndef OPENGD77_CODEPLUG_HH #define OPENGD77_CODEPLUG_HH #include "opengd77base_codeplug.hh" #include "opengd77_extension.hh" /** Represents, encodes and decodes the device specific codeplug for Open GD-77 firmware. * This codeplug is almost identical to the original GD77 codeplug. * * @section ogd77cpl Codeplug structure within radio * The memory representation of the codeplug within the radio is divided into two images * (EEPROM and Flash) and each image again into two sections. * The first segment of the EEPROM image starts at the address 0x000e0 and ends at 0x06000, while * the second EEPROM section starts at 0x07500 and ends at 0x0b000. * * The first segment of the Flash image starts at the address 0x00000 and ends at 0x011a0, while the * second Flash section starts at 0x7b000 and ends at 0x8ee60. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Start End Size Content
      First EEPROM segment 0x000e0-0x06000
      0x000e0 0x000ec 0x000c General settings, see @c RadioddityCodeplug::GeneralSettingsElement.
      0x000ec 0x00128 0x003c ??? Unknown ???
      0x00128 0x01370 0x1248 32 message texts, see @c RadioddityCodeplug::MessageBankElement.
      0x01370 0x01790 0x0420 ??? Unknown ???
      0x01790 0x02dd0 0x1640 64 scan lists, see @c GD77Codeplug::ScanListBankElement, GD77Codeplug::ScanListElement.
      0x02dd0 0x02f88 0x01b8 ??? Unknown ???
      0x02f88 0x03388 0x0400 DTMF contacts, see RadioddityCodeplug::DTMFContactElement.
      0x03388 0x03780 0x03f8 ??? Unknown ???
      0x03780 0x05390 0x1c10 First 128 channels (bank 0), see @c RadioddityCodeplug::ChannelBankElement, @c OpenGD77Codeplug::ChannelElement.
      0x05390 0x06000 0x0c70 ??? Unknown ???
      Second EEPROM segment 0x07500-0x13000
      0x07500 0x07518 0x0018 ??? Unknown ???
      0x07518 0x07538 0x0020 Boot settings, see @c RadioddityCodeplug::BootSettingsElement.
      0x07538 0x07540 0x0008 Menu settings, see @c RadioddityCodeplug::MenuSettingsElement.
      0x07540 0x07560 0x0020 2 intro lines, @c RadioddityCodeplug::BootTextElement.
      0x07560 0x07590 0x0030 ??? Unknown ???
      0x07590 0x075c8 0x0038 VFO A settings @c OpenGD77Codeplug::VFOChannelElement
      0x075c8 0x07600 0x0038 VFO B settings @c OpenGD77Codeplug::VFOChannelElement
      0x07600 0x08010 0x0a10 ??? Unknown ???
      0x08010 0x12c10 0xac00 250 zones, see @c OpenGD77Codeplug::ZoneBankElement, @c OpenGD77Codeplug::ZoneElement.
      0x12c10 0x13000 0x03f0 ??? Unknown ???
      First Flash segment 0x00000-0x011a0
      0x00000 0x011a0 0x11a0 ??? Unknown ???
      Second Flash segment 0x7b000-0x8ee60
      0x7b000 0x7b1b0 0x01b0 ??? Unknown ???
      0x7b1b0 0x87620 0xc470 Remaining 896 channels (bank 1-7), see @c RadioddityCodeplug::ChannelBankElement and @c OpenGD77Codeplug::ChannelElement.
      0x87620 0x8d620 0x6000 1024 contacts, see @c OpenGD77Codeplug::ContactElement.
      0x8d620 0x8e2a0 0x0c80 76 RX group lists, see @c GD77Codeplug::GroupListBankElement, @c GD77Codeplug::GroupListElement.
      0x8e2a0 0x8ee60 0x0bc0 ??? Unknown ???
      * @ingroup ogd77 */ class OpenGD77Codeplug: public OpenGD77BaseCodeplug { Q_OBJECT public: /** Constructs an empty codeplug for the GD-77. */ explicit OpenGD77Codeplug(QObject *parent=nullptr); public: void clearGeneralSettings(); bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()); void clearDTMFSettings(); bool encodeDTMFSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeDTMFSettings(Context &ctx, const ErrorStack &err=ErrorStack()); void clearAPRSSettings(); bool encodeAPRSSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeAPRSSettings(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkAPRSSettings(Context &ctx, const ErrorStack &err=ErrorStack()); void clearContacts(); bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack()); void clearDTMFContacts(); bool encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack()); void clearChannels(); bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()); void clearBootSettings(); bool encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeBootSettings(Context &ctx, const ErrorStack &err=ErrorStack()); void clearVFOSettings(); void clearZones(); bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createZones(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()); void clearGroupLists(); bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some Limits for this codeplug. */ struct Limit: public Element::Limit { /** Number of channel banks. */ static constexpr unsigned int channelBanks() { return 8; } }; protected: /** Internal used image indices. */ struct ImageIndex { /// @cond DO_NOT_DOCUEMNT static constexpr unsigned int settings() { return EEPROM; } static constexpr unsigned int dtmfSettings() { return EEPROM; } static constexpr unsigned int aprsSettings() { return EEPROM; } static constexpr unsigned int dtmfContacts() { return EEPROM; } static constexpr unsigned int channelBank0() { return EEPROM; } static constexpr unsigned int bootSettings() { return EEPROM; } static constexpr unsigned int vfoA() { return EEPROM; } static constexpr unsigned int vfoB() { return EEPROM; } static constexpr unsigned int zoneBank() { return EEPROM; } static constexpr unsigned int channelBank1() { return FLASH; } static constexpr unsigned int contacts() { return FLASH; } static constexpr unsigned int groupLists() { return FLASH; } /// @endcond }; /** Some offsets. */ struct Offset { /// @cond DO_NOT_DOCUEMNT static constexpr unsigned int settings() { return 0x000080; } static constexpr unsigned int dtmfSettings() { return 0x001470; } static constexpr unsigned int aprsSettings() { return 0x001588; } static constexpr unsigned int dtmfContacts() { return 0x002f88; } static constexpr unsigned int channelBank0() { return 0x003780; } // Channels 1-128 static constexpr unsigned int bootSettings() { return 0x007518; } static constexpr unsigned int vfoA() { return 0x007590; } static constexpr unsigned int vfoB() { return 0x0075c8; } static constexpr unsigned int zoneBank() { return 0x008010; } static constexpr unsigned int channelBank1() { return 0x07b1b0; } // Channels 129-1024 static constexpr unsigned int contacts() { return 0x087620; } static constexpr unsigned int groupLists() { return 0x08d620; } /// @endcond }; }; #endif // OPENGD77_CODEPLUG_HH qdmr-0.12.3/lib/opengd77_extension.cc000066400000000000000000000100101501654372000173360ustar00rootroot00000000000000#include "opengd77_extension.hh" #include "utils.hh" /* ******************************************************************************************** * * Implementation of OpenGD77ChannelExtension * ******************************************************************************************** */ OpenGD77ChannelExtension::OpenGD77ChannelExtension(QObject *parent) : ConfigExtension(parent), _zoneSkip(false), _allSkip(false), _beep(true), _powerSave(true), _location(), _txTalkerAliasTS1(TalkerAlias::None), _txTalkerAliasTS2(TalkerAlias::None) { // pass... } ConfigItem * OpenGD77ChannelExtension::clone() const { OpenGD77ChannelExtension *ex = new OpenGD77ChannelExtension(); if (! ex->copy(*this)) { ex->deleteLater(); return nullptr; } return ex; } bool OpenGD77ChannelExtension::scanZoneSkip() const { return _zoneSkip; } void OpenGD77ChannelExtension::enableScanZoneSkip(bool enable) { _zoneSkip = enable; } bool OpenGD77ChannelExtension::scanAllSkip() const { return _allSkip; } void OpenGD77ChannelExtension::enableScanAllSkip(bool enable) { _allSkip = enable; } bool OpenGD77ChannelExtension::beep() const { return _beep; } void OpenGD77ChannelExtension::enableBeep(bool enable) { _beep = enable; } bool OpenGD77ChannelExtension::powerSave() const { return _powerSave; } void OpenGD77ChannelExtension::enablePowerSave(bool enable) { _powerSave = enable; } const QGeoCoordinate & OpenGD77ChannelExtension::location() const { return _location; } void OpenGD77ChannelExtension::setLocation(const QGeoCoordinate &loc) { _location = loc; } QString OpenGD77ChannelExtension::locator() const { if (_location.isValid()) return deg2loc(location(), 8); return ""; } void OpenGD77ChannelExtension::setLocator(const QString &loc) { _location = loc2deg(loc); } OpenGD77ChannelExtension::TalkerAlias OpenGD77ChannelExtension::talkerAliasTS1() const { return _txTalkerAliasTS1; } void OpenGD77ChannelExtension::setTalkerAliasTS1(TalkerAlias ta) { _txTalkerAliasTS1 = ta; } OpenGD77ChannelExtension::TalkerAlias OpenGD77ChannelExtension::talkerAliasTS2() const { return _txTalkerAliasTS2; } void OpenGD77ChannelExtension::setTalkerAliasTS2(TalkerAlias ta) { _txTalkerAliasTS2 = ta; } /* ******************************************************************************************** * * Implementation of OpenGD77ContactExtension * ******************************************************************************************** */ OpenGD77ContactExtension::OpenGD77ContactExtension(QObject *parent) : ConfigExtension(parent), _timeSlotOverride(TimeSlotOverride::None) { // pass... } ConfigItem * OpenGD77ContactExtension::clone() const { OpenGD77ContactExtension *ex = new OpenGD77ContactExtension(); if (! ex->copy(*this)) { ex->deleteLater(); return nullptr; } return ex; } OpenGD77ContactExtension::TimeSlotOverride OpenGD77ContactExtension::timeSlotOverride() const { return _timeSlotOverride; } void OpenGD77ContactExtension::setTimeSlotOverride(TimeSlotOverride ts) { _timeSlotOverride = ts; } /* ******************************************************************************************** * * Implementation of OpenGD77APRSSystemExtension * ******************************************************************************************** */ OpenGD77APRSSystemExtension::OpenGD77APRSSystemExtension(QObject *parent) : ConfigExtension(parent), _location() { // pass... } ConfigItem * OpenGD77APRSSystemExtension::clone() const { auto ex = new OpenGD77APRSSystemExtension(); if (! ex->copy(*this)) { ex->deleteLater(); return nullptr; } return ex; } const QGeoCoordinate & OpenGD77APRSSystemExtension::location() const { return _location; } void OpenGD77APRSSystemExtension::setLocation(const QGeoCoordinate &loc) { _location = loc; } QString OpenGD77APRSSystemExtension::locator() const { if (_location.isValid()) return deg2loc(location(), 8); return ""; } void OpenGD77APRSSystemExtension::setLocator(const QString &loc) { _location = loc2deg(loc); } qdmr-0.12.3/lib/opengd77_extension.hh000066400000000000000000000145361501654372000173710ustar00rootroot00000000000000/** @defgroup ogd77ex OpenGD77 Extensions * This module collects classes that implement the firmware specific extensions to the common * codeplug configuration for radios running the OpenGD77 firmware. * * @ingroup ogd77 */ #ifndef OPENGD77EXTENSION_HH #define OPENGD77EXTENSION_HH #include #include "configobject.hh" #include "opengd77_extension.hh" /** Implements the channel extensions for the OpenGD77 radios. * @since 0.9.0 * @ingroup ogd77ex */ class OpenGD77ChannelExtension: public ConfigExtension { Q_OBJECT Q_CLASSINFO("description", "Channel settings for OpenGD77 radios.") Q_CLASSINFO("longDescription", "This extension implements all channel settings specific to radios " "running the OpenGD77 firmware.") /** The zone skip flag. */ Q_PROPERTY(bool scanZoneSkip READ scanZoneSkip WRITE enableScanZoneSkip) /** The all skip flag. */ Q_PROPERTY(bool scanAllSkip READ scanAllSkip WRITE enableScanAllSkip) /** The beep enable flag. */ Q_PROPERTY(bool beep READ beep WRITE enableBeep) /** The power save enable flag. */ Q_PROPERTY(bool powerSave READ powerSave WRITE enablePowerSave) /** Sets a fixed location for the APRS report. */ Q_PROPERTY(QString location READ locator WRITE setLocator) /** Sets the talker alias for timeslot 1. */ Q_PROPERTY(TalkerAlias talkerAliasTS1 READ talkerAliasTS1 WRITE setTalkerAliasTS1) /** Sets the talker alias for timeslot 2. */ Q_PROPERTY(TalkerAlias talkerAliasTS2 READ talkerAliasTS2 WRITE setTalkerAliasTS2) public: enum class TalkerAlias { None, APRS, Text, Both }; Q_ENUM(TalkerAlias) public: /** Constructor. */ Q_INVOKABLE explicit OpenGD77ChannelExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns @c true if the zone skip flag is set. */ bool scanZoneSkip() const; /** Enables/disables zone skip. */ void enableScanZoneSkip(bool enable); /** Returns @c true if the all-skip flag is set. */ bool scanAllSkip() const; /** Enables/disables all skip. */ void enableScanAllSkip(bool enable); /** Returns @c true if the beep tone is enabled for this channel. */ bool beep() const; /** Enable beep tone for this channel. */ void enableBeep(bool enable); /** Returns @c true, if power save is enabled for this channel (default: true). */ bool powerSave() const; /** Enables power save for this channel. */ void enablePowerSave(bool enable); /** Returns the fixed location for this channel. */ const QGeoCoordinate &location() const; /** Returns the fixed location for this channel. */ QString locator() const; /** Sets the fixed location for this channel. */ void setLocation(const QGeoCoordinate &loc); /** Sets the fixed location for this channel. */ void setLocator(const QString &locator); /** Returns the talker alias setting for timeslot 1. */ TalkerAlias talkerAliasTS1() const; /** Sets the talker alias setting for timeslot 1. */ void setTalkerAliasTS1(TalkerAlias ta); /** Returns the talker alias setting for timeslot 2. */ TalkerAlias talkerAliasTS2() const; /** Sets the talker alias setting for timeslot 2. */ void setTalkerAliasTS2(TalkerAlias ta); protected: /** Holds the zone skip flag. */ bool _zoneSkip; /** Holds the all skip flag. */ bool _allSkip; /** Holds the beep enable flag. */ bool _beep; /** Holds the power-save flag. */ bool _powerSave; /** Holds the fixed location. */ QGeoCoordinate _location; /** Holds the talker alias setting for timeslot 1. */ TalkerAlias _txTalkerAliasTS1; /** Holds the talker alias setting for timeslot 2. */ TalkerAlias _txTalkerAliasTS2; }; /** Implements the contact extensions for the OpenGD77 radios. * @since 0.9.0 * @ingroup ogd77ex */ class OpenGD77ContactExtension: public ConfigExtension { Q_OBJECT Q_CLASSINFO("description", "DMR contact settings for OpenGD77 radios.") Q_CLASSINFO("longDescription", "This extension implements all contact settings specific to radios " "running the OpenGD77 firmware. As the OpenGD77 codeplug is derived from the " "Radioddity GD77 codeplug, all Radioddity extension also apply.") /** If set, overrides the channel time slot if this contact is selected as the transmit contact. */ Q_PROPERTY(TimeSlotOverride timeSlotOverride READ timeSlotOverride WRITE setTimeSlotOverride) Q_CLASSINFO("timeSlotOverrideDescription", "If set, overrides the channels timeslot.") Q_CLASSINFO("timeSlotOverrideLongDescription", "The OpenGD77 firmware allows contacts to override the channel time slot if the " "contact is selected as the current destination contact for that channel. This allows " "to assign a specific time slot to a contact, rather than creating a particular " "channel for that contact that only differs in the time slot.") public: /** Possible modes of time slot override. */ enum class TimeSlotOverride { None, ///< Do not override time slot. TS1, ///< Override with time slot 1. TS2 ///< Override with time slot 2. }; Q_ENUM(TimeSlotOverride) public: /** Constructor. */ Q_INVOKABLE explicit OpenGD77ContactExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the time slot override. */ TimeSlotOverride timeSlotOverride() const; /** Sets the time slot override. */ void setTimeSlotOverride(TimeSlotOverride ts); protected: /** Holds the time slot override. */ TimeSlotOverride _timeSlotOverride; }; class OpenGD77APRSSystemExtension: public ConfigExtension { Q_OBJECT Q_CLASSINFO("description", "OpenGD77 specific APRS settings.") /** Sets a fixed location for the APRS report. */ Q_PROPERTY(QString location READ locator WRITE setLocator) Q_CLASSINFO("locationDescription", "Allows to set a fixed location being transmitted.") public: Q_INVOKABLE explicit OpenGD77APRSSystemExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the fixed location for this APRS system. */ const QGeoCoordinate &location() const; /** Returns the fixed location for this system. */ QString locator() const; /** Sets the fixed location for this system. */ void setLocation(const QGeoCoordinate &loc); /** Sets the fixed location for this system. */ void setLocator(const QString &locator); protected: /** Holds the fixed location, if set. */ QGeoCoordinate _location; }; #endif // OPENGD77EXTENSION_HH qdmr-0.12.3/lib/opengd77_interface.cc000066400000000000000000000572361501654372000173070ustar00rootroot00000000000000#include "opengd77_interface.hh" #include "logger.hh" #include "radioinfo.hh" #include #define USB_VID 0x1fc9 #define USB_PID 0x0094 #define BLOCK_SIZE 32 #define SECTOR_SIZE 4096 #define ALIGN_BLOCK_SIZE(n) ((0==((n)%BLOCK_SIZE)) ? (n) : (n)+(BLOCK_SIZE-((n)%BLOCK_SIZE))) /* ********************************************************************************************* * * Implementation of OpenGD77Interface::ReadRequest * ********************************************************************************************* */ bool OpenGD77Interface::ReadRequest::initReadEEPROM(uint32_t addr, uint16_t length) { this->type = 'R'; this->command = READ_EEPROM; this->address = qToBigEndian(addr); this->length = qToBigEndian(length); return true; } bool OpenGD77Interface::ReadRequest::initReadFlash(uint32_t addr, uint16_t length) { this->type = 'R'; this->command = READ_FLASH; this->address = qToBigEndian(addr); this->length = qToBigEndian(length); return true; } bool OpenGD77Interface::ReadRequest::initReadFirmwareInfo() { this->type = 'R'; this->command = READ_FIRMWARE_INFO; this->address = 0; this->length = qToBigEndian((uint16_t)sizeof(FirmwareInfo)); return true; } /* ********************************************************************************************* * * Implementation of OpenGD77Interface::WriteRequest * ********************************************************************************************* */ bool OpenGD77Interface::WriteRequest::initWriteEEPROM(Variant variant, uint32_t addr, const uint8_t *data, uint16_t size) { if (size > 32) size = 32; this->type = (Variant::GD77 == variant) ? 'W' : 'X'; this->command = WRITE_EEPROM; this->payload.address = qToBigEndian(addr); this->payload.length = qToBigEndian(size); memcpy(this->payload.data, data, size); return true; } bool OpenGD77Interface::WriteRequest::initSetFlashSector(Variant variant, uint32_t addr) { uint32_t sec = addr/SECTOR_SIZE; this->type = (Variant::GD77 == variant) ? 'W' : 'X'; this->command = SET_FLASH_SECTOR; this->sector[0] = ((sec>>16) & 0xff); this->sector[1] = ((sec>>8) & 0xff); this->sector[2] = (sec & 0xff); return true; } bool OpenGD77Interface::WriteRequest::initWriteFlash(Variant variant, uint32_t addr, const uint8_t *data, uint16_t size) { if (size > 32) size = 32; this->type = (Variant::GD77 == variant) ? 'W' : 'X'; this->command = WRITE_SECTOR_BUFFER; this->payload.address = qToBigEndian(addr); this->payload.length = qToBigEndian(size); memcpy(this->payload.data, data, size); return true; } bool OpenGD77Interface::WriteRequest::initFinishWriteFlash(Variant variant) { this->type = (Variant::GD77 == variant) ? 'W' : 'X'; this->command = WRITE_FLASH_SECTOR; return true; } /* ********************************************************************************************* * * Implementation of OpenGD77Interface::CommandRequest * ********************************************************************************************* */ void OpenGD77Interface::CommandRequest::initShowCPSScreen() { this->type = 'C'; this->command = SHOW_CPS_SCREEN; this->x = 0; this->y = 0; this->font = 0; this->alignment = 0; this->inverted = 0; memset(this->message, 0, sizeof(this->message)); } void OpenGD77Interface::CommandRequest::initClearScreen() { this->type = 'C'; this->command = CLEAR_SCREEN; this->x = 0; this->y = 0; this->font = 0; this->alignment = 0; this->inverted = 0; memset(this->message, 0, sizeof(this->message)); } void OpenGD77Interface::CommandRequest::initDisplay(uint8_t x, uint8_t y, const char *message, unsigned int iSize, uint8_t font, uint8_t alignment, uint8_t inverted) { this->type = 'C'; this->command = DISPLAY; this->x = x; this->y = y; this->font = font; this->alignment = alignment; this->inverted = inverted; memset(this->message, 0, 16); strncpy(this->message, message, std::min(16u, iSize)); } void OpenGD77Interface::CommandRequest::initRenderCPS() { this->type = 'C'; this->command = RENDER_CPS; this->x = 0; this->y = 0; this->font = 0; this->alignment = 0; this->inverted = 0; memset(this->message, 0, sizeof(this->message)); } void OpenGD77Interface::CommandRequest::initCloseScreen() { this->type = 'C'; this->command = CLOSE_CPS_SCREEN; this->x = 0; this->y = 0; this->font = 0; this->alignment = 0; this->inverted = 0; memset(this->message, 0, sizeof(this->message)); } void OpenGD77Interface::CommandRequest::initCommand(Option option) { this->type = 'C'; this->command = COMMAND; this->option = option; this->y = 0; this->font = 0; this->alignment = 0; this->inverted = 0; memset(this->message, 0, sizeof(this->message)); } /* ********************************************************************************************* * * Implementation of OpenGD77Interface * ********************************************************************************************* */ OpenGD77Interface::OpenGD77Interface(const USBDeviceDescriptor &descr, const ErrorStack &err, QObject *parent) : USBSerial(descr, QSerialPort::Baud115200, err, parent), _sector(-1) { // pass... } OpenGD77Interface::~OpenGD77Interface() { // pass... } USBDeviceInfo OpenGD77Interface::interfaceInfo() { return USBDeviceInfo(USBDeviceInfo::Class::Serial, USB_VID, USB_PID); } QList OpenGD77Interface::detect(bool saveOnly) { Q_UNUSED(saveOnly) return USBSerial::detect(USB_VID, USB_PID, true); } void OpenGD77Interface::close() { if (isOpen()) USBSerial::close(); } RadioInfo OpenGD77Interface::identifier(const ErrorStack &err) { Q_UNUSED(err); if (! isOpen()) return RadioInfo(); FirmwareInfo info; if (! readFirmwareInfo(info, err)) { errMsg(err) << "Cannot identify OpenGD77 variant."; return RadioInfo(); } logDebug() << "Got type=" << info.radioType << "."; switch ((FirmwareInfo::RadioType)info.radioType) { case FirmwareInfo::RadioType::GD77: case FirmwareInfo::RadioType::GD77S: case FirmwareInfo::RadioType::RD5R: case FirmwareInfo::RadioType::DM1801: case FirmwareInfo::RadioType::DM1801A: _protocolVariant = Variant::GD77; return RadioInfo::byID(RadioInfo::OpenGD77); case FirmwareInfo::RadioType::MDUV380: case FirmwareInfo::RadioType::MD380: case FirmwareInfo::RadioType::DM1701: case FirmwareInfo::RadioType::DM1701RGB: _protocolVariant = Variant::UV380; return RadioInfo::byID(RadioInfo::OpenUV380); case FirmwareInfo::RadioType::MD9600: logInfo() << "OpenGD77 variant MD9600 not supported (yet)."; return RadioInfo(); case FirmwareInfo::RadioType::MD2017: logInfo() << "OpenGD77 variant MD2017 not supported (yet)."; return RadioInfo(); } errMsg(err) << "Unknown OpenGD77 variant " << info.radioType << "."; return RadioInfo(); } bool OpenGD77Interface::write_start(uint32_t bank, uint32_t addr, const ErrorStack &err) { logDebug() << "Send enter prog mode ..."; if (! sendShowCPSScreen(err)) return false; //logDebug() << "Send clear screen ..."; if (! sendClearScreen(err)) return false; //logDebug() << "Send display text ..."; if (! sendDisplay(0, 0, "qDMR", 4, 1, 0, err)) return false; if (! sendDisplay(0, 16, "Writing", 7, 1, 0, err)) return false; if (! sendDisplay(0, 32, "Codeplug", 8, 1, 0, err)) return false; //logDebug() << "Send 'render CPS' ..."; if (! sendRenderCPS(err)) return false; //logDebug() << "Send 'flash red LED' ..."; if (! sendCommand(CommandRequest::FLASH_RED_LED, err)) return false; //logDebug() << "Send save settings and VFOs ..."; if (! sendCommand(CommandRequest::SAVE_SETTINGS_AND_VFOS, err)) return false; if (EEPROM == bank) { if (_sector >= 0) { if (! finishWriteFlash(err)) return false; } _sector = -1; } else if (FLASH == bank) { int32_t sector = addr/SECTOR_SIZE; if ((-1 != _sector) && (_sector != sector)) { if (! finishWriteFlash(err)) return false; } } return true; } bool OpenGD77Interface::write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) { if (EEPROM == bank) { if ((0 <= _sector) && (! finishWriteFlash(err))) return false; for (int i=0; i _sector) { if (! setFlashSector(addr)) return false; _sector = sector; } if (sector == _sector) { for (int i=0; i _sector) return true; _sector = -1; if (! finishWriteFlash(err)) return false; if (! sendCloseScreen(err)) return false; return true; } bool OpenGD77Interface::read_start(uint32_t bank, uint32_t addr, const ErrorStack &err) { Q_UNUSED(bank); Q_UNUSED(addr) if (! sendShowCPSScreen(err)) return false; if (! sendClearScreen(err)) return false; if (! sendDisplay(0, 0, "qDMR", 4, 1, 0, err)) return false; if (! sendDisplay(0, 16, "Reading", 7, 1, 0, err)) return false; if (! sendDisplay(0, 32, "Codeplug", 8, 1, 0, err)) return false; if (! sendRenderCPS(err)) return false; if (! sendCommand(CommandRequest::FLASH_GREEN_LED, err)) return false; if (! sendCommand(CommandRequest::SAVE_SETTINGS_AND_VFOS, err)) return false; return true; } bool OpenGD77Interface::read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) { if (! isOpen()) { errMsg(err) << "Cannot read block: Device not open!"; return false; } for (int i=0; i retlen) { errMsg(err) << "Cannot read from serial port"; return false; } else if (0 == retlen) { errMsg(err) << "Cannot read from serial port: Device returned empty message."; return false; } if ('R' != resp.type) { errMsg(err) << "Cannot read from device: Device returned error '" << resp.type << "'."; return false; } if (qFromBigEndian(req.length) != qFromBigEndian(resp.length)) { errMsg(err) << "Cannot read from device: Device returned invalid length " << qFromBigEndian(resp.length) << "."; return false; } memcpy(data, resp.data, qFromBigEndian(resp.length)); return true; } bool OpenGD77Interface::writeEEPROM(uint32_t addr, const uint8_t *data, uint16_t len, const ErrorStack &err) { WriteRequest req; req.initWriteEEPROM(_protocolVariant, addr, data, len); WriteResponse resp; if ((8+len) != QSerialPort::write((const char *)&req, 8+len)) { errMsg(err) << "Cannot write to serial port."; return false; } if (! waitForReadyRead(1000)) { errMsg(err) << "Cannot read from serial port: Timeout!"; return false; } int retlen = QSerialPort::read((char *)&resp, sizeof(WriteResponse)); if (0 > retlen) { errMsg(err) << "Cannot read from serial port."; return false; } else if (0 == retlen) { errMsg(err) << "Cannot write EEPROM: Device returned empty message."; return false; } if ((req.type != resp.type) || (req.command != resp.command)) { errMsg(err) << "Cannot write EEPROM at " << QString::number(addr, 16) << ": Device returned error " << resp.type << "."; return false; } return true; } bool OpenGD77Interface::readFlash(uint32_t addr, uint8_t *data, uint16_t len, const ErrorStack &err) { Q_UNUSED(len) if (! isOpen()) { errMsg(err) << "Cannot read block: Device not open!"; return false; } ReadRequest req; req.initReadFlash(addr, BLOCK_SIZE); if (sizeof(ReadRequest) != QSerialPort::write((const char *)&req, sizeof(ReadRequest))) { errMsg(err) << QSerialPort::errorString(); errMsg(err) << "Cannot write to serial port."; return false; } if (! waitForReadyRead(1000)) { errMsg(err) << QSerialPort::errorString(); errMsg(err) << "Cannot read from serial port: Timeout!"; return false; } ReadResponse resp; int retlen = QSerialPort::read((char *)&resp, sizeof(ReadResponse)); if (0 > retlen) { errMsg(err) << QSerialPort::errorString(); errMsg(err) << "Cannot read from serial port."; return false; } else if (0 == retlen) { errMsg(err) << "Cannot read from serial port: Device returned empty message."; return false; } if ('R' != resp.type) { errMsg(err) << "Cannot read from device: Device returned error " << resp.type << "."; return false; } if (qFromBigEndian(req.length) != qFromBigEndian(resp.length)) { errMsg(err) << "Cannot read from device: Device returned invalid length " << qFromBigEndian(resp.length) << "."; return false; } memcpy(data, resp.data, qFromBigEndian(resp.length)); return true; } bool OpenGD77Interface::setFlashSector(uint32_t addr, const ErrorStack &err) { WriteRequest req; req.initSetFlashSector(_protocolVariant, addr); WriteResponse resp; if (5 != QSerialPort::write((const char *)&req, 5)) { errMsg(err) << QSerialPort::errorString(); errMsg(err) << "Cannot write to serial port."; return false; } if (! waitForReadyRead(1000)) { errMsg(err) << QSerialPort::errorString(); errMsg(err) << "Cannot read from serial port: Timeout!"; return false; } int retlen = QSerialPort::read((char *)&resp, sizeof(WriteResponse)); if (0 > retlen) { errMsg(err) << QSerialPort::errorString(); errMsg(err) << "Cannot read from serial port."; return false; } else if (0 == retlen) { errMsg(err) << "Cannot set flash sector: Device returned empty message."; return false; } if ((req.type != resp.type) || (req.command != resp.command)) { errMsg(err) << "Cannot set flash sector: Device returned error " << resp.type << "."; return false; } return true; } bool OpenGD77Interface::writeFlash(uint32_t addr, const uint8_t *data, uint16_t len, const ErrorStack &err) { WriteRequest req; req.initWriteFlash(_protocolVariant, addr, data, len); WriteResponse resp; if ((8+len) != QSerialPort::write((const char *)&req, 8+len)) { errMsg(err) << QSerialPort::errorString(); errMsg(err) << "Cannot write to serial port."; return false; } if (! waitForReadyRead(1000)) { errMsg(err) << QSerialPort::errorString(); errMsg(err) << "Cannot read from serial port: Timeout!"; return false; } int retlen = QSerialPort::read((char *)&resp, sizeof(WriteResponse)); if (0 > retlen) { errMsg(err) << QSerialPort::errorString(); errMsg(err) << "Cannot read from serial port."; return false; } else if (0 == retlen) { errMsg(err) << "Cannot write to buffer: Device returned empty message."; return false; } if ((req.type != resp.type) || (req.command != resp.command)) { errMsg(err) << "Cannot write to buffer at " << QString::number(addr,16) << ": Device returned error " << resp.type << "."; return false; } return true; } bool OpenGD77Interface::finishWriteFlash(const ErrorStack &err) { //logDebug() << "Send finish write flash command ..."; WriteRequest req; req.initFinishWriteFlash(_protocolVariant); WriteResponse resp; if ((2) != QSerialPort::write((const char *)&req, 2)) { errMsg(err) << "Cannot write to serial port."; return false; } if (! waitForReadyRead(1000)) { errMsg(err) << "Cannot read from serial port: Timeout!"; return false; } int retlen = QSerialPort::read((char *)&resp, sizeof(WriteResponse)); if (0 > retlen) { errMsg(err) << "Cannot read from serial port."; return false; } else if (0 == retlen) { errMsg(err) << "Cannot write to flash: Device returned empty message."; return false; } if ((req.type != resp.type) || (req.command != resp.command)) { errMsg(err) << "Cannot write to flash: Device returned error " << resp.type << "."; return false; } return true; } bool OpenGD77Interface::readFirmwareInfo(OpenGD77Interface::FirmwareInfo &radioInfo, const ErrorStack &err) { logDebug() << "Request radio info."; ReadRequest req; req.initReadFirmwareInfo(); if (sizeof(ReadRequest) != QSerialPort::write((const char *)&req, sizeof(ReadRequest))) { errMsg(err) << "Serial port error: " << QSerialPort::errorString(); errMsg(err) << "Cannot send read request."; return false; } if (! waitForReadyRead(1000)) { errMsg(err) << "Cannot read from serial port: Timeout!"; return false; } ReadResponse resp; int retlen = QSerialPort::read((char *)&resp, sizeof(ReadResponse)); if (0 > retlen) { errMsg(err) << "Cannot read from serial port."; return false; } else if (0 == retlen) { errMsg(err) << "Cannot read radio info: Device returned empty message."; return false; } if (req.type != resp.type) { errMsg(err) << "Cannot read radio info: Device returned error " << resp.type << ", expected 'R'."; return false; } memcpy(&radioInfo, &(resp.info), sizeof(FirmwareInfo)); return true; } bool OpenGD77Interface::sendShowCPSScreen(const ErrorStack &err) { CommandRequest req; uint8_t resp; req.initShowCPSScreen(); if (sizeof(CommandRequest) != QSerialPort::write((const char *) &req, sizeof(CommandRequest))) { errMsg(err) << "Cannot write to serial port."; return false; } if (! waitForReadyRead(1000)) { errMsg(err) << "Cannot read from serial port: Timeout!"; return false; } int retlen = QSerialPort::read((char *)&resp, 1); if (0 > retlen) { errMsg(err) << "Cannot read from serial port."; return false; } else if (0 == retlen) { errMsg(err) << "Cannot send command: Device returned empty message."; return false; } else if ('-' != resp) { errMsg(err) << "Cannot send command: Device returned unexpected response '" << (char)resp << "'."; return false; } return true; } bool OpenGD77Interface::sendClearScreen(const ErrorStack &err) { CommandRequest req; req.initClearScreen(); uint8_t resp; if (sizeof(CommandRequest) != QSerialPort::write((const char *) &req, sizeof(CommandRequest))) { errMsg(err) << QSerialPort::errorString(); errMsg(err) << "Cannot write to serial port."; return false; } if (! waitForReadyRead(1000)) { errMsg(err) << "Cannot read from serial port: Timeout!"; return false; } int retlen = QSerialPort::read((char *)&resp, 1); if (0 > retlen) { errMsg(err) << QSerialPort::errorString(); errMsg(err) << "Cannot read from serial port."; return false; } else if (0 == retlen) { errMsg(err) << "Cannot send command: Device returned empty message."; return false; } else if ('-' != resp) { errMsg(err) << "Cannot send command: Device returned unexpected response '" << (char)resp << "'."; return false; } return true; } bool OpenGD77Interface::sendDisplay(uint8_t x, uint8_t y, const char *message, uint8_t iSize, uint8_t alignment, uint8_t inverted, const ErrorStack &err) { CommandRequest req; req.initDisplay(x,y, message, iSize, 3, alignment, inverted); uint8_t resp; if (sizeof(CommandRequest) != QSerialPort::write((const char *) &req, sizeof(CommandRequest))) { errMsg(err) << "Cannot write to serial port."; return false; } if (! waitForReadyRead(1000)) { errMsg(err) << "Cannot read from serial port: Timeout!"; return false; } int retlen = QSerialPort::read((char *)&resp, 1); if (0 > retlen) { errMsg(err) << "Cannot read from serial port."; return false; } else if (0 == retlen) { errMsg(err) << "Cannot send command: Device returned empty message."; return false; } else if ('-' != resp) { errMsg(err) << "Cannot send command: Device returned unexpected response '" << (char)resp << "'."; return false; } return true; } bool OpenGD77Interface::sendRenderCPS(const ErrorStack &err) { CommandRequest req; req.initRenderCPS(); if (sizeof(CommandRequest) != QSerialPort::write((const char *) &req, sizeof(CommandRequest))) { errMsg(err) << "Cannot write to serial port."; return false; } if (! waitForReadyRead(1000)) { errMsg(err) << "Cannot read from serial port: Timeout!"; return false; } uint8_t resp; int retlen = QSerialPort::read((char *)&resp, 1); if (0 > retlen) { errMsg(err) << "Cannot read from serial port."; return false; } else if (0 == retlen) { errMsg(err) << "Cannot send command: Device returned empty message."; return false; } else if ('-' != resp) { errMsg(err) << "Cannot send command: Device returned unexpected response '" << (char)resp << "'."; return false; } return true; } bool OpenGD77Interface::sendCloseScreen(const ErrorStack &err) { CommandRequest req; req.initCloseScreen(); uint8_t resp; if (sizeof(CommandRequest) != QSerialPort::write((const char *) &req, sizeof(CommandRequest))) { errMsg(err) << "Cannot write to serial port."; return false; } if (! waitForReadyRead(1000)) { errMsg(err) << "Cannot read from serial port: Timeout!"; return false; } int retlen = QSerialPort::read((char *)&resp, 1); if (0 > retlen) { errMsg(err) << "Cannot read from serial port."; return false; } else if (0 == retlen) { errMsg(err) << "Cannot send command: Device returned empty message."; return false; } else if ('-' != resp) { errMsg(err) << "Cannot send command: Device returned unexpected response '" << (char)resp << "'."; return false; } return true; } bool OpenGD77Interface::sendCommand(CommandRequest::Option option, const ErrorStack &err) { CommandRequest req; req.initCommand(option); uint8_t resp; if (sizeof(CommandRequest) != QSerialPort::write((const char *) &req, sizeof(CommandRequest))) { errMsg(err) << "Cannot write to serial port."; return false; } if (! waitForReadyRead(1000)) { errMsg(err) << "Cannot read from serial port: Timeout!"; return false; } int retlen = QSerialPort::read((char *)&resp, 1); if (0 > retlen) { errMsg(err) << "Cannot read from serial port."; return false; } else if (0 == retlen) { errMsg(err) << "Cannot send command: Device returned empty message."; return false; } else if ('-' != resp) { errMsg(err) << "Cannot send command: Device returned unexpected response '" << (char)resp << "'."; return false; } return true; } qdmr-0.12.3/lib/opengd77_interface.hh000066400000000000000000000362171501654372000173150ustar00rootroot00000000000000#ifndef OPENGD77INTERFACE_HH #define OPENGD77INTERFACE_HH #include "usbserial.hh" #include "errorstack.hh" /** Implements the interfact to a radio running the Open GD77 firmware. * * This interface uses a USB serial-port to communicate with the device. To find the corresponding * port, the device-specific VID @c 0x1fc9 and PID @c 0x0094 are used. Hence no udev rules are * needed to access these devices. The user, however, should be a member of the @c dialout group * to get access to the serial interfaces. * * * @section ogd77cmd Command requests * The overall command requets structure is * * @verbinclude opengd77_protocol_command_request.txt * * where the optional and variable length payload field is determined by the command flag. The * request starts with the command prefix 'C' (43h) followed by the command flag. Following * command flags are known. * * * * * * * * * * * *
      FlagCommand
      00h Show CPS screen.
      01h Clear screen.
      02h Display text.
      03h Render screen.
      05h Close CPS screen.
      06h Control radio.
      07h Start GPS logging.
      feh Ping request.
      * * @subsection ogd77cmd_cps_screen Show CPS Screen (00h) * Reserves the screen for the CPS. The content is not cleared. * * The command is quiet simple * @verbinclude opengd77_protocol_command_show_cps_screen_request.txt * as is the response * @verbinclude opengd77_protocol_command_okay_response.txt * * @subsection ogd77cmd_clear_screen Clear Screen (01h) * Once the screen has been reserved, this command clears it. * * Also this command is quiet simple * @verbinclude opengd77_protocol_command_clear_screen_request.txt * as is the response * @verbinclude opengd77_protocol_command_okay_response.txt * * @subsection ogd77cmd_set_text Set text (02h) * This command has a variable payload size. * @verbinclude opengd77_protocol_command_display_text_request.txt * * * * * * * * *
      FieldMeaning
      Column Address Specifies the column index.
      Row Address Specifies the row index as multiple of 10h.
      Size Text size (?). Actually, always observed 3.
      Alignment Specifies text alignment on row. 0=left, 1=center, 2=right(?)
      Inverted Inverts text, 0=off, 1=inverted (?).
      Payload Variable size, up to 16bytes ASCII text.
      * * If there are no errors, the radio responds with * @verbinclude opengd77_protocol_command_okay_response.txt * * @subsection ogd77cmd_render_screen Render Screen (03h) * Randers the transmitted screen. This command is quiet simple again, with no payload. * @verbinclude opengd77_protocol_command_render_screen_request.txt * * as is the response: * @verbinclude opengd77_protocol_command_okay_response.txt * * @subsection ogd77cmd_ctrl Control Radio (06h) * This command request is used to control the radio. The specific action is transmitted as a * single payload byte. * @verbinclude opengd77_protocol_command_control_request.txt * * * * * * * * * * *
      Action CodeMeaning
      00h Save settings and rebbot.
      01h Reboot
      02h Save settings and VFOs, no reboot.
      03h Flash LED green.
      04h Flash LED red.
      05h Re-init internal buffers.
      06h Re-init sound buffers.
      07h Update date-time from GPS.
      * * If there are no errors, the radio responds with * @verbinclude opengd77_protocol_command_okay_response.txt * * @subsection ogd77cmd_gps Start GPS Logging (07h) * A simple request without any payload. * @verbinclude opengd77_protocol_command_start_gps_request.txt * * If there is no error, the response should be. * @verbinclude opengd77_protocol_command_okay_response.txt * After that, the radio will send GPS information via the serial port until any other command * is send to the radio. * * @subsection ogd77cmd_ping Ping Request (06h) * A simple request without any payload * @verbinclude opengd77_protocol_command_ping_request.txt * and the radio should respond with * @verbinclude opengd77_protocol_command_okay_response.txt * * * @section ogd77read Read requests * The read command is used to obtain different stuff. Not only the code plug. In general, all read * share the same form * @verbinclude opengd77_protocol_read_request.txt * * * * * * * * * * * *
      Code Memory region
      01h Flash
      02h EEPROM
      05h MCU ROM
      06h Display buffer
      07h WAV buffer
      08h AMBE buffer
      09h Radio info
      0ah FLASH security registers
      * * Whenever the read request returns some data, it is tranmitted with the read response * @verbinclude opengd77_protocol_read_response.txt * * If not, a simple ACK response is send * @verbinclude opengd77_protocol_command_okay_response.txt * * @subsection ogd77info Radio info struct * When reading the radio inforamtion, the information is returned in a binary struct: * @verbinclude opengd77_radio_info.txt * * * * * * * * * * * * * *
      Code Radio Variant
      00h Radioddity GD-77
      01h Radioddity GD-77S
      02h Baofeng DM-1801
      03h Radioddity RD-5R
      04h Baofeng DM-1801A
      05h TyT MD-9600
      06h TyT MD-UV390
      07h TyT MD-380
      08h Baofeng DM-1701
      09h TyT MD-2017
      0ah Baofeng DM-1701 RGB
      * * @section ogd77write Write requests * * @ingroup ogd77 */ class OpenGD77Interface : public USBSerial { Q_OBJECT public: /** The EEPROM memory bank. */ static const uint32_t EEPROM = 0; /** The Flash memory bank. */ static const uint32_t FLASH = 1; /** Specifies the detected model variant. */ enum class Variant { GD77, UV380 }; public: /** Constructs a new interface to a specific OpenGD77 device. */ explicit OpenGD77Interface(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr); /** Destructor. */ virtual ~OpenGD77Interface(); /** Closes the interface to the device. */ void close(); /** Returns an identifier of the radio. */ RadioInfo identifier(const ErrorStack &err=ErrorStack()); bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()); bool read_finish(const ErrorStack &err=ErrorStack()); bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()); bool write_finish(const ErrorStack &err=ErrorStack()); bool reboot(const ErrorStack &err=ErrorStack()); public: /** Returns some information about this interface. */ static USBDeviceInfo interfaceInfo(); /** Tries to find all interfaces connected AnyTone radios. */ static QList detect(bool saveOnly=true); protected: /** Represents a read message. */ struct __attribute__((packed)) ReadRequest { /** Possible read sources. */ enum Command { READ_FLASH = 1, READ_EEPROM = 2, READ_MCU_ROM = 5, READ_DISPLAY_BUFFER = 6, READ_WAV_BUFFER = 7, READ_AMBE_BUFFER = 8, READ_FIRMWARE_INFO = 9 }; /// 'R' read block, 'W' write block, 'C' command. char type; /// Memory to read from, FLASH, EEPROM, ROM, etc. @see OpenGD77Internface::ReadReqest::Command. uint8_t command; /// Memory address to read from in big endian. uint32_t address; /// Amount of data to read in big endian. uint16_t length; /** Constructs a FLASH read message. */ bool initReadFlash(uint32_t address, uint16_t length); /** Constructs an EEPROM read message. */ bool initReadEEPROM(uint32_t address, uint16_t length); /** Constructs a firmware-info read message. */ bool initReadFirmwareInfo(); }; /** Radio inforation struct. */ struct __attribute__((packed)) FirmwareInfo { /** Possible radio types, returned by the radio_info struct.*/ enum class RadioType { GD77=0, GD77S=1, DM1801=2, RD5R=3, DM1801A=4, MD9600=5, MDUV380=6, MD380=7, DM1701=8, MD2017=9, DM1701RGB=10 }; uint32_t structVersion; ///< Struct version number (currently 3). uint32_t radioType; ///< Device variant (see @c RadioType). char fw_revision[16]; ///< Firmware revision ASCII, 0-padded. char build_date[16]; ///< Firmware build time, YYYYMMDDhhmmss, 0-padded. uint32_t flashChipSerial; ///< Serial number of the flash chip. uint16_t features; ///< Some flags, signaling the presence of some features. }; /** Represents a read response message. */ struct __attribute__((packed)) ReadResponse { /// Same code as request. That is 'R' read block, 'W' write block, 'C' command. char type; /// Length of paylod. uint16_t length; /// Payload union { uint8_t data[32]; ///< Data payload. FirmwareInfo info; ///< Firmware information struct. }; }; /** Represents a write message. */ struct __attribute__((packed)) WriteRequest { /** Possible write destinations. */ enum Command { SET_FLASH_SECTOR = 1, WRITE_SECTOR_BUFFER = 2, WRITE_FLASH_SECTOR = 3, WRITE_EEPROM = 4, WRITE_WAV_BUFFER = 7 }; /// 'R' read block, 'W' write block or 'C' command. char type; /// Command, @see OpenGD77Internface::WriteReqest::Command. uint8_t command; union { /** 24 bit sector number. */ uint8_t sector[3]; /** Payload data. */ struct __attribute__((packed)) { /** Target address. */ uint32_t address; /** Payload length. */ uint16_t length; /** Payload data. */ uint8_t data[32]; } payload; }; /** Constructs a write-to-eeprom message. */ bool initWriteEEPROM(Variant variant, uint32_t addr, const uint8_t *data, uint16_t size); /** Constructs a set-flash-sector message. */ bool initSetFlashSector(Variant variant, uint32_t addr); /** Constructs a write-to-flash message. */ bool initWriteFlash(Variant variant, uint32_t addr, const uint8_t *data, uint16_t size); /** Constructs a finish-write-to-flash message. */ bool initFinishWriteFlash(Variant variant); }; /** Represents a write-response message. */ struct __attribute__((packed)) WriteResponse { /// Same code as request. That is 'R' read block, 'W' write block, 'C' command or '-' on Error. char type; /// Same code as request if OK. uint8_t command; }; /** Represents a command message. */ struct __attribute__((packed)) CommandRequest { /** Possible commands. */ enum Command { SHOW_CPS_SCREEN = 0, CLEAR_SCREEN = 1, DISPLAY = 2, RENDER_CPS = 3, CLOSE_CPS_SCREEN = 5, COMMAND = 6 }; /** Possible options. */ enum Option { SAVE_SETTINGS_NOT_VFOS = 0, REBOOT = 1, SAVE_SETTINGS_AND_VFOS = 2, FLASH_GREEN_LED = 3, FLASH_RED_LED = 4 }; /** Message type, here 'C' for command. */ char type; /** The command. */ uint8_t command; /** Either a command option or the x position on screen. */ union { /** The x-position on the screen. */ uint8_t x; /** The command option. */ uint8_t option; }; /** The y-position on the screen. */ uint8_t y; /** The font size. */ uint8_t font; /** The text alignment. */ uint8_t alignment; /** Is text inverted? */ uint8_t inverted; /** Some text message. */ char message[16]; /** Construct "show CPS screen" command message. */ void initShowCPSScreen(); /** Construct a clear-screen command message. */ void initClearScreen(); /** Construct a "show text on screen" message. */ void initDisplay(uint8_t x, uint8_t y, const char *message, unsigned int iSize, uint8_t font, uint8_t alignment, uint8_t inverted); /** Construct a "render CPS" message. */ void initRenderCPS(); /** Construct a "close screen" command message. */ void initCloseScreen(); /** Construct a command message with the given option. */ void initCommand(Option option); }; protected: /** Read some data from EEPROM at the given address. */ bool readEEPROM(uint32_t addr, uint8_t *data, uint16_t len, const ErrorStack &err=ErrorStack()); /** Write some data to EEPROM at the given address. */ bool writeEEPROM(uint32_t addr, const uint8_t *data, uint16_t len, const ErrorStack &err=ErrorStack()); /** Read some data from Flash at the given address. */ bool readFlash(uint32_t addr, uint8_t *data, uint16_t len, const ErrorStack &err=ErrorStack()); /** Select the correct Flash sector for the given address. * This command must be sent before writing to the flash memory. */ bool setFlashSector(uint32_t addr, const ErrorStack &err=ErrorStack()); /** Write some data to the given Flash memory. */ bool writeFlash(uint32_t addr, const uint8_t *data, uint16_t len, const ErrorStack &err=ErrorStack()); /** Finalize writing to the Flash memory. If not send after writing to a sector, * the changes are lost. */ bool finishWriteFlash(const ErrorStack &err=ErrorStack()); /** Read radio info struct. */ bool readFirmwareInfo(FirmwareInfo &radioInfo, const ErrorStack &err=ErrorStack()); /** Send a "show CPS screen" message. */ bool sendShowCPSScreen(const ErrorStack &err=ErrorStack()); /** Send a "clear screen" message. */ bool sendClearScreen(const ErrorStack &err=ErrorStack()); /** Send a "display some text" message. */ bool sendDisplay(uint8_t x, uint8_t y, const char *message, uint8_t iSize, uint8_t alignment, uint8_t inverted, const ErrorStack &err=ErrorStack()); /** Send a "render CPS screen" message. */ bool sendRenderCPS(const ErrorStack &err=ErrorStack()); /** Send a "close screen" message. */ bool sendCloseScreen(const ErrorStack &err=ErrorStack()); /** Sends some command message with the given options. */ bool sendCommand(CommandRequest::Option option, const ErrorStack &err=ErrorStack()); protected: /** The protocol variant determined by the device type obtained by the firmware info. */ Variant _protocolVariant; /** The current Flash sector, set to -1 if none is currently selected. */ int32_t _sector; }; #endif // OPENGD77INTERFACE_HH qdmr-0.12.3/lib/opengd77_limits.cc000066400000000000000000000170471501654372000166440ustar00rootroot00000000000000#include "opengd77_limits.hh" #include "channel.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "zone.hh" #include "scanlist.hh" #include "roamingzone.hh" #include "opengd77_satelliteconfig.hh" OpenGD77Limits::OpenGD77Limits(QObject *parent) : RadioLimits(false, parent) { // Define limits for call-sign DB _hasCallSignDB = true; _callSignDBImplemented = true; _numCallSignDBEntries = 15796; // Define limits for satellite config _hasSatelliteConfig = true; _satelliteConfigImplemented = true; _numSatellites = OpenGD77SatelliteConfig::Limit::satellites(); /* Define limits for the general settings. */ add("settings", new RadioLimitItem{ { "introLine1", new RadioLimitString(-1, 16, RadioLimitString::ASCII) }, { "introLine2", new RadioLimitString(-1, 16, RadioLimitString::ASCII) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, -1) } }); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList { { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject { {"name", new RadioLimitString(1,8, RadioLimitString::ASCII) }, {"id", new RadioLimitUInt(0, 16777215)} } } /// @todo check default radio ID. }); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, 1024, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum{ (unsigned)DMRContact::PrivateCall, (unsigned)DMRContact::GroupCall, (unsigned)DMRContact::AllCall }}, { "number", new RadioLimitUInt(0, 16777215) } } }, { DTMFContact::staticMetaObject, 0, 32, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "ring", new RadioLimitBool() }, { "number", new RadioLimitStringRegEx("^[0-9A-Fa-f]+$") } } } }); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 0, 76, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // allow for any digital contact reference { "contacts", new RadioLimitRefList(1, 32, DMRContact::staticMetaObject) } })); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, 1024, // < up to 1024 channels new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, 3825, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRefIgnored()}, /// @todo handle OpenGD77 extension {"openGD77", new RadioLimitIgnored()}, {"tyt", new RadioLimitIgnored()} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1,16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, 3825, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, true)}, {"aprs", new RadioLimitObjRefIgnored()}, {"roaming", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())}, /// @todo handle OpenGD77 extension {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } )); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 0, 68, new RadioLimitSingleZone( 80, { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions }) ) ); /* Ignore scan lists. */ add("scanlists", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored()) ); /* Ignore positioning systems. */ add("positioning", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored()) ); /* Ignore roaming zones. */ add("roaming", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored()) ); } qdmr-0.12.3/lib/opengd77_limits.hh000066400000000000000000000005041501654372000166440ustar00rootroot00000000000000#ifndef OPENGD77LIMITS_HH #define OPENGD77LIMITS_HH #include "radiolimits.hh" /** Implements the limits for the OpenGD77 firmware. * @ingroup @ogd77 */ class OpenGD77Limits: public RadioLimits { Q_OBJECT public: /** Constructor. */ explicit OpenGD77Limits(QObject *parent=nullptr); }; #endif // OPENGD77LIMITS_HH qdmr-0.12.3/lib/opengd77_satelliteconfig.cc000066400000000000000000000010121501654372000205000ustar00rootroot00000000000000#include "opengd77_satelliteconfig.hh" #include "errorstack.hh" OpenGD77SatelliteConfig::OpenGD77SatelliteConfig(QObject *parent) : OpenGD77BaseSatelliteConfig(parent) { image(FLASH).addElement(Offset::satellites(), 0x11a0); } bool OpenGD77SatelliteConfig::encode(SatelliteDatabase *db, const ErrorStack &err) { SatelliteBankElement bank(data(Offset::satellites(), FLASH)); if (! bank.encode(db, err)) { errMsg(err) << "Cannot encode satellite config for OpenGD77."; return false; } return true; } qdmr-0.12.3/lib/opengd77_satelliteconfig.hh000066400000000000000000000016341501654372000205240ustar00rootroot00000000000000#ifndef OPENGD77_SATELLITECONFIG_HH #define OPENGD77_SATELLITECONFIG_HH #include "opengd77base_satelliteconfig.hh" class OpenGD77SatelliteConfig : public OpenGD77BaseSatelliteConfig { Q_OBJECT public: /** Default constructor. */ explicit OpenGD77SatelliteConfig(QObject *parent = nullptr); /** Encodes the given satellite database. */ virtual bool encode(SatelliteDatabase *db, const ErrorStack &err=ErrorStack()); public: /** Some limits for the satellite config. */ struct Limit { /** The maximum number of satellites. */ static constexpr unsigned int satellites() { return OpenGD77BaseSatelliteConfig::SatelliteBankElement::Limit::satellites(); } }; protected: /** Some internal offsets. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int satellites() { return 0x000000; } /// @endcond }; }; #endif // OPENGD77_SATELLITECONFIG_HH qdmr-0.12.3/lib/opengd77base.cc000066400000000000000000000271061501654372000161130ustar00rootroot00000000000000#include "opengd77base.hh" #include "opengd77base_codeplug.hh" #include "opengd77_limits.hh" #include "logger.hh" #include "config.hh" #define BSIZE 32 RadioLimits *OpenGD77Base::_limits = nullptr; OpenGD77Base::OpenGD77Base(OpenGD77Interface *device, QObject *parent) : Radio(parent), _dev(device), _config(nullptr), _satelliteConfig(nullptr) { // pass... } OpenGD77Base::~OpenGD77Base() { if (_dev && _dev->isOpen()) { logDebug() << "Closing device."; _dev->reboot(); _dev->close(); } if (_dev) { logDebug() << "Deleting device."; _dev->deleteLater(); _dev = nullptr; } } const RadioLimits & OpenGD77Base::limits() const { if (nullptr == _limits) _limits = new OpenGD77Limits(); return *_limits; } bool OpenGD77Base::startDownload(bool blocking, const ErrorStack &err) { if (StatusIdle != _task) { logError() << "Cannot download from radio, radio is not idle."; return false; } _task = StatusDownload; _errorStack = err; if (blocking) { run(); return (StatusIdle == _task); } // If non-blocking -> move device to this thread if (_dev && _dev->isOpen()) _dev->moveToThread(this); // start thread for download start(); return true; } bool OpenGD77Base::startUpload(Config *config, bool blocking, const Codeplug::Flags &flags, const ErrorStack &err) { Q_UNUSED(flags) logDebug() << "Start upload to " << name() << "..."; if (StatusIdle != _task) { logError() << "Cannot upload to radio, radio is not idle."; return false; } if (_config) delete _config; if (! (_config = config)) { logError() << "Cannot upload to radio, no config given."; return false; } _config->setParent(this); _task = StatusUpload; _errorStack = err; if (blocking) { run(); return (StatusIdle == _task); } // If non-blocking -> move device to this thread if (_dev && _dev->isOpen()) _dev->moveToThread(this); // start thread for upload start(); return true; } bool OpenGD77Base::startUploadCallsignDB(UserDatabase *db, bool blocking, const CallsignDB::Selection &selection, const ErrorStack &err) { logDebug() << "Start upload to " << name() << "..."; if (StatusIdle != _task) { logError() << "Cannot upload to radio, radio is not idle."; return false; } // Assemble call-sign db from user DB logDebug() << "Encode call-signs into db."; callsignDB()->encode(db, selection); _task = StatusUploadCallsigns; _errorStack = err; if (blocking) { run(); return (StatusIdle == _task); } // If non-blocking -> move device to this thread if (_dev && _dev->isOpen()) _dev->moveToThread(this); // start thread for upload start(); return true; } bool OpenGD77Base::startUploadSatelliteConfig(SatelliteDatabase *db, bool blocking, const ErrorStack &err) { logDebug() << "Start upload to " << name() << "..."; if (StatusIdle != _task) { logError() << "Cannot upload to radio, radio is not idle."; return false; } // Assemble call-sign db from user DB logDebug() << "Encode satellite config.."; if (! _satelliteConfig->encode(db, err)) return false; _task = StatusUploadSatellites; _errorStack = err; if (blocking) { run(); return (StatusIdle == _task); } // If non-blocking -> move device to this thread if (_dev && _dev->isOpen()) _dev->moveToThread(this); // start thread for upload start(); return true; } void OpenGD77Base::run() { if (StatusDownload == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { emit downloadError(this); return; } if (! download()) { _task = StatusError; _dev->read_finish(); _dev->reboot(); _dev->close(); emit downloadError(this); return; } _dev->read_finish(); _dev->reboot(); _dev->close(); _task = StatusIdle; emit downloadFinished(this, &codeplug()); _config = nullptr; } else if (StatusUpload == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { emit uploadError(this); return; } if (! upload()) { _task = StatusError; _dev->write_finish(); _dev->reboot(); _dev->close(); emit uploadError(this); return; } _dev->write_finish(); _dev->reboot(); _dev->close(); _task = StatusIdle; emit uploadComplete(this); } else if (StatusUploadCallsigns == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { emit uploadError(this); return; } if (! uploadCallsigns()) { _task = StatusError; _dev->write_finish(); _dev->reboot(); _dev->close(); emit uploadError(this); return; } _dev->write_finish(); _dev->reboot(); _dev->close(); _task = StatusIdle; emit uploadComplete(this); } else if (StatusUploadSatellites == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { emit uploadError(this); return; } if (! uploadSatellites()) { _task = StatusError; _dev->write_finish(); _dev->reboot(); _dev->close(); emit uploadError(this); return; } _dev->write_finish(); _dev->reboot(); _dev->close(); _task = StatusIdle; emit uploadComplete(this); } } bool OpenGD77Base::download() { emit downloadStarted(); if (codeplug().numImages() != 2) { errMsg(_errorStack) << "Cannot download codeplug: Codeplug does not contain two images."; return false; } // Check every segment in the codeplug if (! codeplug().isAligned(BSIZE)) { errMsg(_errorStack) << "Cannot download codeplug: Codeplug is not aligned with blocksize " << BSIZE << "."; return false; } size_t totb = codeplug().memSize(); if (! _dev->read_start(0, 0, _errorStack)) { errMsg(_errorStack) << "Cannot start codeplug download."; _dev->close(); return false; } // Then download codeplug size_t bcount = 0; for (int image=0; imageread(bank, (b0+b)*BSIZE, codeplug().data((b0+b)*BSIZE, image), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot read block " << (b0+b) << "."; return false; } QThread::usleep(100); emit downloadProgress(float(bcount*100)/totb); } } _dev->read_finish(_errorStack); } return true; } bool OpenGD77Base::upload() { emit uploadStarted(); if (codeplug().numImages() != 2) { errMsg(_errorStack) << "Cannot download codeplug: Codeplug does not contain two images."; return false; } // Check every segment in the codeplug if (! codeplug().isAligned(BSIZE)) { errMsg(_errorStack) << "Cannot upload code-plug: Codeplug is not aligned with blocksize " << BSIZE << "."; return false; } size_t totb = codeplug().memSize(); if (! _dev->read_start(0, 0, _errorStack)) { errMsg(_errorStack) << "Cannot start codeplug download."; return false; } // Then download codeplug size_t bcount = 0; for (int image=0; imageread(bank, (b0+b)*BSIZE, codeplug().data((b0+b)*BSIZE, image), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot read block " << (b0+b) << "."; return false; } QThread::usleep(100); emit uploadProgress(float(bcount*50)/totb); } } _dev->read_finish(); } // Encode config into codeplug codeplug().encode(_config); if (! _dev->write_start(0,0, _errorStack)) { errMsg(_errorStack) << "Cannot start codeplug upload."; return false; } // Then upload codeplug for (int image=0; imagewrite(bank, (b0+b)*BSIZE, codeplug().data((b0+b)*BSIZE, image), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot write block " << (b0+b) << "."; return false; } QThread::usleep(100); emit uploadProgress(float(bcount*50)/totb); } } _dev->write_finish(); } return true; } bool OpenGD77Base::uploadCallsigns() { emit uploadStarted(); // Check every segment in the codeplug if (! callsignDB()->isAligned(BSIZE)) { errMsg(_errorStack) << "Cannot upload call-sign DB: Not aligned with block-size " << BSIZE << "!"; return false; } size_t totb = callsignDB()->memSize(); if (! _dev->write_start(OpenGD77BaseCodeplug::FLASH, 0, _errorStack)) { errMsg(_errorStack) << "Cannot start callsign DB upload."; return false; } unsigned bcount = 0; // Then upload callsign DB for (int n=0; nimage(0).numElements(); n++) { unsigned addr = callsignDB()->image(0).element(n).address(); unsigned size = callsignDB()->image(0).element(n).data().size(); unsigned b0 = addr/BSIZE, nb = size/BSIZE; for (unsigned b=0; bwrite(OpenGD77BaseCodeplug::FLASH, (b0+b)*BSIZE, callsignDB()->data((b0+b)*BSIZE, 0), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot write block " << (b0+b) << "."; return false; } emit uploadProgress(float(bcount*100)/totb); } } _dev->write_finish(); return true; } bool OpenGD77Base::uploadSatellites() { emit uploadStarted(); // Check every segment in the codeplug if (! _satelliteConfig->isAligned(BSIZE)) { errMsg(_errorStack) << "Cannot upload satellite config: Not aligned with block-size " << BSIZE << "!"; return false; } size_t totb = _satelliteConfig->memSize(); if (! _dev->write_start(OpenGD77BaseSatelliteConfig::FLASH, 0, _errorStack)) { errMsg(_errorStack) << "Cannot start satellite config upload."; return false; } unsigned bcount = 0; // Then upload config for (int n=0; n<_satelliteConfig->image(OpenGD77BaseSatelliteConfig::FLASH).numElements(); n++) { unsigned addr = _satelliteConfig->image(OpenGD77BaseSatelliteConfig::FLASH).element(n).address(); unsigned size = _satelliteConfig->image(OpenGD77BaseSatelliteConfig::FLASH).element(n).data().size(); unsigned b0 = addr/BSIZE, nb = size/BSIZE; for (unsigned b=0; bwrite(OpenGD77BaseCodeplug::FLASH, (b0+b)*BSIZE, _satelliteConfig->data((b0+b)*BSIZE, OpenGD77BaseSatelliteConfig::FLASH), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot write block " << (b0+b) << "."; return false; } emit uploadProgress(float(bcount*100)/totb); } } _dev->write_finish(); return true; } qdmr-0.12.3/lib/opengd77base.hh000066400000000000000000000045651501654372000161310ustar00rootroot00000000000000/** @defgroup ogd77 Open GD-77 Firmware * Implements a radio running the Open GD77 firmware. * @ingroup dsc */ #ifndef OPENGD77BASE_HH #define OPENGD77BASE_HH #include "radio.hh" #include "opengd77_interface.hh" #include "opengd77_satelliteconfig.hh" /** Implements an common USB interface to Open GD-77(S) type devices. * * @ingroup ogd77 */ class OpenGD77Base : public Radio { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit OpenGD77Base(OpenGD77Interface *device=nullptr, QObject *parent=nullptr); virtual ~OpenGD77Base(); const RadioLimits &limits() const; /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); public slots: /** Starts the download of the codeplug and derives the generic configuration from it. */ bool startDownload(bool blocking=false, const ErrorStack &err=ErrorStack()); /** Derives the device-specific codeplug from the generic configuration and uploads that * codeplug to the radio. */ bool startUpload(Config *config, bool blocking=false, const Codeplug::Flags &flags = Codeplug::Flags(), const ErrorStack &err=ErrorStack()); /** Encodes the given user-database and uploades it to the device. */ bool startUploadCallsignDB(UserDatabase *db, bool blocking=false, const CallsignDB::Selection &selection=CallsignDB::Selection(), const ErrorStack &err=ErrorStack()); bool startUploadSatelliteConfig(SatelliteDatabase *db, bool blocking, const ErrorStack &err); protected: /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */ void run(); /** Implements the actual download process. */ bool download(); /** Implements the actual codeplug upload process. */ bool upload(); /** Implements the actual callsign DB upload process. */ bool uploadCallsigns(); /** Implements the actual satellite config upload process. */ bool uploadSatellites(); protected: /** The interface to the radio. */ OpenGD77Interface *_dev; /** The generic configuration. */ Config *_config; /** The encoded eatellite configuraiton. */ OpenGD77BaseSatelliteConfig *_satelliteConfig; private: /** Holds the singleton instance. */ static RadioLimits *_limits; }; #endif // OPENGD77BASE_HH qdmr-0.12.3/lib/opengd77base_callsigndb.cc000066400000000000000000000043761501654372000203010ustar00rootroot00000000000000#include "opengd77base_callsigndb.hh" #include "utils.hh" #include "userdatabase.hh" #include /* ******************************************************************************************** * * Implementation of OpenGD77BaseCallsignDB::userdb_entry_t * ******************************************************************************************** */ OpenGD77BaseCallsignDB::userdb_entry_t::userdb_entry_t() { clear(); } void OpenGD77BaseCallsignDB::userdb_entry_t::clear() { memset(this, 0, sizeof(userdb_entry_t)); } uint32_t OpenGD77BaseCallsignDB::userdb_entry_t::getNumber() const { return decode_dmr_id_bcd_le((uint8_t *)&number); } void OpenGD77BaseCallsignDB::userdb_entry_t::setNumber(uint32_t number) { encode_dmr_id_bcd_le((uint8_t *)&(this->number), number); } QString OpenGD77BaseCallsignDB::userdb_entry_t::getName() const { return decode_ascii((const uint8_t *)name, 15, 0x00); } void OpenGD77BaseCallsignDB::userdb_entry_t::setName(const QString &name) { encode_ascii((uint8_t *)(this->name), name, 15, 0x00); } void OpenGD77BaseCallsignDB::userdb_entry_t::fromEntry(const UserDatabase::User &user) { setNumber(user.id); QString tmp = user.call; if (!user.name.isEmpty()) tmp = tmp + " " + user.name; setName(tmp); } /* ******************************************************************************************** * * Implementation of OpenGD77BaseCallsignDB::userdb_t * ******************************************************************************************** */ OpenGD77BaseCallsignDB::userdb_t::userdb_t() { clear(); } void OpenGD77BaseCallsignDB::userdb_t::clear() { memset(this, 0, sizeof(userdb_t)); memcpy(magic, "ID-", 3); size = 0x5d; // <- 19 byte entries, 15byte name memcpy(version, "001", 3); count = 0; unused6 = 0; } void OpenGD77BaseCallsignDB::userdb_t::setSize(unsigned n) { count = n; } /* ******************************************************************************************** * * Implementation of OpenGD77BaseCallsignDB * ******************************************************************************************** */ OpenGD77BaseCallsignDB::OpenGD77BaseCallsignDB(QObject *parent) : CallsignDB(parent) { // pass... } OpenGD77BaseCallsignDB::~OpenGD77BaseCallsignDB() { // pass... } qdmr-0.12.3/lib/opengd77base_callsigndb.hh000066400000000000000000000051521501654372000203040ustar00rootroot00000000000000#ifndef OPENGD77BASECALLSIGNDB_HH #define OPENGD77BASECALLSIGNDB_HH #include "callsigndb.hh" #include "userdatabase.hh" /** Represents and encodes the binary format for all call-sign databases within OpenGD77 radios. * @ingroup ogd77 */ class OpenGD77BaseCallsignDB : public CallsignDB { Q_OBJECT public: /** Represents a user-db entry within the binary codeplug. * * Memory representation of the call-sign DB entry (size 0x13 bytes): * @verbinclude opengd77_callsign_db_entry.txt */ struct __attribute__((packed)) userdb_entry_t { uint32_t number; ///< DMR ID stored in BCD little-endian. char name[15]; ///< Call or name, up to 15 ASCII chars, 0x00 padded. /** Constructor. */ userdb_entry_t(); /** Resets the entry. */ void clear(); /** Returns the DMR ID number. */ uint32_t getNumber() const; /** Sets the DMR ID number. */ void setNumber(uint32_t number); /** Returns the name of the entry. */ QString getName() const; /** Sets the name of the entry, 15b max. * The name gets truncated if longer than 15b. */ void setName(const QString &name); /** Encodes the given user. */ void fromEntry(const UserDatabase::User &user); }; /** Represents the binary call-sign database header. * * Memory representation of the call-sign DB header (size: 0x0c bytes): * @verbinclude opengd77_callsign_db_header.txt **/ struct __attribute__((packed)) userdb_t { char magic[3]; ///< Fixed string 'ID-' uint8_t size; ///< Fixed to 0x5d for 15 byte names. char version[3]; ///< Version string? Fixed to '001' uint8_t unused6; ///< Unused, set to 0x00. uint32_t count; ///< Number of contacts in DB, 32bit little-endian. /** Constructor. */ userdb_t(); /** Resets the header. */ void clear(); /** Sets the number of DB entries. This number is limited to USERDB_NUM_ENTRIES.*/ void setSize(unsigned n); }; public: /** Constructor. */ explicit OpenGD77BaseCallsignDB(QObject *parent=nullptr); /** Destructor. */ virtual ~OpenGD77BaseCallsignDB(); /** Encodes as many entries as possible of the given user-database. */ virtual bool encode(UserDatabase *calldb, const Selection &selection=Selection(), const ErrorStack &err=ErrorStack()) = 0; public: /** Some limits for this callsign DB. */ struct Limit { /** Maximum block size. */ static constexpr unsigned int blockSize() { return 32; } }; }; #endif // OPENGD77BASECALLSIGNDB_HH qdmr-0.12.3/lib/opengd77base_codeplug.cc000066400000000000000000002041661501654372000200000ustar00rootroot00000000000000#include "opengd77base_codeplug.hh" #include "opengd77_extension.hh" #include "radioid.hh" #include "config.hh" #include "logger.hh" #include "intermediaterepresentation.hh" #include "satellitedatabase.hh" /* ********************************************************************************************* * * Implementation of some helper functions * ********************************************************************************************* */ uint32_t OpenGD77BaseCodeplug::encodeAngle(double angle) { uint32_t sign = (angle < 0) ? 1 : 0; uint32_t decimals = std::abs(int(angle * 10000)); uint32_t deg = decimals/10000; decimals = decimals % 10000; return (sign << 23) | (deg << 15) | decimals; } double OpenGD77BaseCodeplug::decodeAngle(uint32_t code) { return (((code >> 23) & 1) ? -1 : 1) * ( ((code >> 15) & 0xff) + double(code & 0x7fff)/10000 ); } uint16_t OpenGD77BaseCodeplug::encodeSelectiveCall(const SelectiveCall &call) { if (call.isInvalid()) return 0xffff; uint16_t dcs = 0, inverted = 0, toneCode = 0; if (call.isDCS()) { dcs = 1; inverted = call.isInverted() ? 1 : 0; toneCode = call.octalCode(); } else { dcs = inverted = 0; toneCode = call.mHz()/100; } uint16_t bcd = (((toneCode/1000) % 10 ) << 12) | (((toneCode/100) % 10 ) << 8) | (((toneCode/10) % 10 ) << 4) | (((toneCode/1) % 10 ) << 0); return (dcs<<15) | (inverted << 14) | (bcd & 0x3fff); } SelectiveCall OpenGD77BaseCodeplug::decodeSelectiveCall(uint16_t code) { if (0xffff == code) return SelectiveCall(); bool dcs = ((code >> 15) & 1), inverted = ((code >> 14) & 1); uint16_t bcd = (code & 0x3fff); code = 1000 * ((bcd >> 12) & 0xf) + 100 * ((bcd >> 8) & 0xf) + 10 * ((bcd >> 4) & 0xf) + 1 * ((bcd >> 0) & 0xf); if (! dcs) return SelectiveCall(double(code)/10); return SelectiveCall(code, inverted); } /* ********************************************************************************************* * * Implementation of OpenGD77BaseCodeplug::ChannelElement * ********************************************************************************************* */ OpenGD77BaseCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::ChannelElement::ChannelElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } OpenGD77BaseCodeplug::ChannelElement::~ChannelElement() { // pass... } void OpenGD77BaseCodeplug::ChannelElement::clear() { setName(""); setRXFrequency(Frequency()); setTXFrequency(Frequency()); setMode(MODE_ANALOG); setPower(Channel::Power::High); clearFixedPosition(); setRXTone(SelectiveCall()); setTXTone(SelectiveCall()); enableSimplex(false); enablePowerSave(false); enableBeep(false); clearDMRId(); clearGroupListIndex(); setColorCode(0); clearAPRSIndex(); clearTXContact(); setAliasTimeSlot1(OpenGD77ChannelExtension::TalkerAlias::None); setAliasTimeSlot2(OpenGD77ChannelExtension::TalkerAlias::None); setTimeSlot(DMRChannel::TimeSlot::TS1); setBandwidth(FMChannel::Bandwidth::Narrow); enableRXOnly(false); enableSkipScan(false); enableSkipZoneScan(false); enableVOX(false); setSquelch(SquelchMode::Global, 0); } QString OpenGD77BaseCodeplug::ChannelElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0xff); } void OpenGD77BaseCodeplug::ChannelElement::setName(const QString &n) { writeASCII(Offset::name(), n, Limit::nameLength(), 0xff); } Frequency OpenGD77BaseCodeplug::ChannelElement::rxFrequency() const { return Frequency::fromHz(((unsigned long long)getBCD8_le(Offset::rxFrequency()))*10); } void OpenGD77BaseCodeplug::ChannelElement::setRXFrequency(const Frequency &freq) { setBCD8_le(Offset::rxFrequency(), freq.inHz()/10); } Frequency OpenGD77BaseCodeplug::ChannelElement::txFrequency() const { return Frequency::fromHz(((unsigned long long)getBCD8_le(Offset::txFrequency()))*10); } void OpenGD77BaseCodeplug::ChannelElement::setTXFrequency(const Frequency &freq) { setBCD8_le(Offset::txFrequency(), freq.inHz()/10); } OpenGD77BaseCodeplug::ChannelElement::Mode OpenGD77BaseCodeplug::ChannelElement::mode() const { return (Mode)getUInt8(Offset::mode()); } void OpenGD77BaseCodeplug::ChannelElement::setMode(Mode mode) { setUInt8(Offset::mode(), (unsigned)mode); } bool OpenGD77BaseCodeplug::ChannelElement::globalPower() const { return 0 == getUInt8(Offset::power()); } Channel::Power OpenGD77BaseCodeplug::ChannelElement::power() const { switch (getUInt8(Offset::power())) { case 0: case 1: return Channel::Power::Min; case 2: case 3: return Channel::Power::Low; case 4: case 5: case 6: return Channel::Power::Mid; case 7: case 8: case 9: return Channel::Power::High; case 10: return Channel::Power::Max; default: break; } return Channel::Power::Min; } void OpenGD77BaseCodeplug::ChannelElement::setPower(Channel::Power pwr) { switch (pwr) { case Channel::Power::Min: setUInt8(Offset::power(), 1); break; case Channel::Power::Low: setUInt8(Offset::power(), 3); break; case Channel::Power::Mid: setUInt8(Offset::power(), 6); break; case Channel::Power::High: setUInt8(Offset::power(), 9); break; case Channel::Power::Max: setUInt8(Offset::power(), 10); break; } } void OpenGD77BaseCodeplug::ChannelElement::clearPower() { setUInt8(Offset::power(), 0); } bool OpenGD77BaseCodeplug::ChannelElement::hasFixedPosition() const { return getBit(Offset::useFixedLocation()); } QGeoCoordinate OpenGD77BaseCodeplug::ChannelElement::fixedPosition() const { uint32_t latCode = (((uint32_t)getUInt8(Offset::latitude2())) << 16) + (((uint32_t)getUInt8(Offset::latitude1())) << 8) + ((uint32_t)getUInt8(Offset::latitude0())); uint32_t lonCode = (((uint32_t)getUInt8(Offset::longitude2())) << 16) + (((uint32_t)getUInt8(Offset::longitude1())) << 8) + ((uint32_t)getUInt8(Offset::longitude0())); return QGeoCoordinate(decodeAngle(latCode), decodeAngle(lonCode)); } void OpenGD77BaseCodeplug::ChannelElement::setFixedPosition(const QGeoCoordinate &coordinate) { if (! coordinate.isValid()) { clearFixedPosition(); return; } uint32_t latCode = encodeAngle(coordinate.latitude()); uint32_t lonCode = encodeAngle(coordinate.longitude()); setBit(Offset::useFixedLocation(), true); setUInt8(Offset::latitude0(), ((latCode >> 0) & 0xff)); setUInt8(Offset::latitude1(), ((latCode >> 8) & 0xff)); setUInt8(Offset::latitude2(), ((latCode >> 16) & 0xff)); setUInt8(Offset::longitude0(), ((lonCode >> 0) & 0xff)); setUInt8(Offset::longitude1(), ((lonCode >> 8) & 0xff)); setUInt8(Offset::longitude2(), ((lonCode >> 16) & 0xff)); } void OpenGD77BaseCodeplug::ChannelElement::clearFixedPosition() { setBit(Offset::useFixedLocation(), false); } SelectiveCall OpenGD77BaseCodeplug::ChannelElement::rxTone() const { return decodeSelectiveCall(getUInt16_le(Offset::rxTone())); } void OpenGD77BaseCodeplug::ChannelElement::setRXTone(const SelectiveCall &code) { setUInt16_le(Offset::rxTone(), encodeSelectiveCall(code)); } SelectiveCall OpenGD77BaseCodeplug::ChannelElement::txTone() const { return decodeSelectiveCall(getUInt16_le(Offset::txTone())); } void OpenGD77BaseCodeplug::ChannelElement::setTXTone(const SelectiveCall &code) { setUInt16_le(Offset::txTone(), encodeSelectiveCall(code)); } bool OpenGD77BaseCodeplug::ChannelElement::isSimplex() const { return getBit(Offset::simplex()); } void OpenGD77BaseCodeplug::ChannelElement::enableSimplex(bool enable) { setBit(Offset::simplex(), enable); } bool OpenGD77BaseCodeplug::ChannelElement::powerSave() const { return ! getBit(Offset::disablePowerSave()); } void OpenGD77BaseCodeplug::ChannelElement::enablePowerSave(bool enable) { setBit(Offset::disablePowerSave(), !enable); } bool OpenGD77BaseCodeplug::ChannelElement::beep() const { return ! getBit(Offset::disableBeep()); } void OpenGD77BaseCodeplug::ChannelElement::enableBeep(bool enable) { setBit(Offset::disableBeep(), !enable); } bool OpenGD77BaseCodeplug::ChannelElement::hasDMRId() const { return getBit(Offset::overrideDMRID()); } unsigned int OpenGD77BaseCodeplug::ChannelElement::dmrId() const { return getUInt24_be(Offset::dmrId()); } void OpenGD77BaseCodeplug::ChannelElement::setDMRId(unsigned int dmrId) { setBit(Offset::overrideDMRID()); setUInt24_be(Offset::dmrId(), dmrId); } void OpenGD77BaseCodeplug::ChannelElement::clearDMRId() { setUInt24_be(Offset::dmrId(), 0x001600); clearBit(Offset::overrideDMRID()); } bool OpenGD77BaseCodeplug::ChannelElement::hasGroupList() const { return 0 != getUInt8(Offset::groupList()); } unsigned OpenGD77BaseCodeplug::ChannelElement::groupListIndex() const { return getUInt8(Offset::groupList())-1; } void OpenGD77BaseCodeplug::ChannelElement::setGroupListIndex(unsigned index) { setUInt8(Offset::groupList(), index+1); } void OpenGD77BaseCodeplug::ChannelElement::clearGroupListIndex() { setUInt8(Offset::groupList(), 0); } unsigned OpenGD77BaseCodeplug::ChannelElement::colorCode() const { return getUInt8(Offset::colorCode()); } void OpenGD77BaseCodeplug::ChannelElement::setColorCode(unsigned cc) { setUInt8(Offset::colorCode(), cc); } bool OpenGD77BaseCodeplug::ChannelElement::hasAPRSIndex() const { return 0 != getUInt8(Offset::aprsIndex()); } unsigned int OpenGD77BaseCodeplug::ChannelElement::aprsIndex() const { return getUInt8(Offset::aprsIndex())-1; } void OpenGD77BaseCodeplug::ChannelElement::setAPRSIndex(unsigned int index) { setUInt8(Offset::aprsIndex(), index+1); } void OpenGD77BaseCodeplug::ChannelElement::clearAPRSIndex() { setUInt8(Offset::aprsIndex(), 0); } bool OpenGD77BaseCodeplug::ChannelElement::hasTXContact() const { return 0 != getUInt16_le(Offset::txContact()); } unsigned int OpenGD77BaseCodeplug::ChannelElement::txContactIndex() const { return getUInt16_le(Offset::txContact()) - 1; } void OpenGD77BaseCodeplug::ChannelElement::setTXContactIndex(unsigned int index) { setUInt16_le(Offset::txContact(), index+1); } void OpenGD77BaseCodeplug::ChannelElement::clearTXContact() { setUInt16_le(Offset::txContact(), 0); } OpenGD77ChannelExtension::TalkerAlias OpenGD77BaseCodeplug::ChannelElement::aliasTimeSlot1() const { switch ((Alias) getUInt2(Offset::aliasTimeSlot1())) { case Alias::None: return OpenGD77ChannelExtension::TalkerAlias::None; case Alias::APRS: return OpenGD77ChannelExtension::TalkerAlias::APRS; case Alias::Text: return OpenGD77ChannelExtension::TalkerAlias::Text; case Alias::Both: return OpenGD77ChannelExtension::TalkerAlias::Both; } return OpenGD77ChannelExtension::TalkerAlias::None; } void OpenGD77BaseCodeplug::ChannelElement::setAliasTimeSlot1(OpenGD77ChannelExtension::TalkerAlias alias) { switch (alias) { case OpenGD77ChannelExtension::TalkerAlias::None: setUInt2(Offset::aliasTimeSlot1(), (unsigned int)Alias::None); break; case OpenGD77ChannelExtension::TalkerAlias::APRS: setUInt2(Offset::aliasTimeSlot1(), (unsigned int)Alias::APRS); break; case OpenGD77ChannelExtension::TalkerAlias::Text: setUInt2(Offset::aliasTimeSlot1(), (unsigned int)Alias::Text); break; case OpenGD77ChannelExtension::TalkerAlias::Both: setUInt2(Offset::aliasTimeSlot1(), (unsigned int)Alias::Both); break; } } OpenGD77ChannelExtension::TalkerAlias OpenGD77BaseCodeplug::ChannelElement::aliasTimeSlot2() const { switch ((Alias) getUInt2(Offset::aliasTimeSlot2())) { case Alias::None: return OpenGD77ChannelExtension::TalkerAlias::None; case Alias::APRS: return OpenGD77ChannelExtension::TalkerAlias::APRS; case Alias::Text: return OpenGD77ChannelExtension::TalkerAlias::Text; case Alias::Both: return OpenGD77ChannelExtension::TalkerAlias::Both; } return OpenGD77ChannelExtension::TalkerAlias::None; } void OpenGD77BaseCodeplug::ChannelElement::setAliasTimeSlot2(OpenGD77ChannelExtension::TalkerAlias alias) { switch (alias) { case OpenGD77ChannelExtension::TalkerAlias::None: setUInt2(Offset::aliasTimeSlot2(), (unsigned int)Alias::None); break; case OpenGD77ChannelExtension::TalkerAlias::APRS: setUInt2(Offset::aliasTimeSlot2(), (unsigned int)Alias::APRS); break; case OpenGD77ChannelExtension::TalkerAlias::Text: setUInt2(Offset::aliasTimeSlot2(), (unsigned int)Alias::Text); break; case OpenGD77ChannelExtension::TalkerAlias::Both: setUInt2(Offset::aliasTimeSlot2(), (unsigned int)Alias::Both); break; } } DMRChannel::TimeSlot OpenGD77BaseCodeplug::ChannelElement::timeSlot() const { return (getBit(Offset::timeSlot()) ? DMRChannel::TimeSlot::TS2 : DMRChannel::TimeSlot::TS1); } void OpenGD77BaseCodeplug::ChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) { setBit(Offset::timeSlot(), DMRChannel::TimeSlot::TS2 == ts); } FMChannel::Bandwidth OpenGD77BaseCodeplug::ChannelElement::bandwidth() const { return (getBit(Offset::bandwidth()) ? FMChannel::Bandwidth::Wide : FMChannel::Bandwidth::Narrow); } void OpenGD77BaseCodeplug::ChannelElement::setBandwidth(FMChannel::Bandwidth bw) { setBit(Offset::bandwidth(), FMChannel::Bandwidth::Wide == bw); } bool OpenGD77BaseCodeplug::ChannelElement::rxOnly() const { return getBit(Offset::rxOnly()); } void OpenGD77BaseCodeplug::ChannelElement::enableRXOnly(bool enable) { setBit(Offset::rxOnly(), enable); } bool OpenGD77BaseCodeplug::ChannelElement::skipScan() const { return getBit(Offset::skipScan()); } void OpenGD77BaseCodeplug::ChannelElement::enableSkipScan(bool enable) { setBit(Offset::skipScan(), enable); } bool OpenGD77BaseCodeplug::ChannelElement::skipZoneScan() const { return getBit(Offset::skipZoneScan()); } void OpenGD77BaseCodeplug::ChannelElement::enableSkipZoneScan(bool enable) { setBit(Offset::skipZoneScan(), enable); } bool OpenGD77BaseCodeplug::ChannelElement::vox() const { return getBit(Offset::vox()); } void OpenGD77BaseCodeplug::ChannelElement::enableVOX(bool enable) { setBit(Offset::vox(), enable); } OpenGD77BaseCodeplug::ChannelElement::SquelchMode OpenGD77BaseCodeplug::ChannelElement::squelchMode() const { switch (getUInt8(Offset::squelch())) { case 0: return SquelchMode::Global; case 1: return SquelchMode::Open; case 15: return SquelchMode::Closed; default: return SquelchMode::Normal; } } unsigned int OpenGD77BaseCodeplug::ChannelElement::squelchLevel() const { int level = (10*(getUInt8(Offset::squelch())-1))/14; return std::min(level, 10); } void OpenGD77BaseCodeplug::ChannelElement::setSquelch(SquelchMode mode, unsigned int level) { level = std::min(level, 10U); switch (mode) { case SquelchMode::Global: level = 0; break; case SquelchMode::Open: level = 1; break; case SquelchMode::Closed: level = 15; break; case SquelchMode::Normal: level = 1 + (14*level)/10; break; } setUInt8(Offset::squelch(), level); } Channel * OpenGD77BaseCodeplug::ChannelElement::decode(Codeplug::Context &ctx, const ErrorStack& err) const { Q_UNUSED(err); Q_UNUSED(ctx); Channel *ch = nullptr; if (MODE_ANALOG == mode()) { FMChannel *ach = new FMChannel(); ch = ach; ach->setBandwidth(bandwidth()); ach->setRXTone(rxTone()); ach->setTXTone(txTone()); ach->setSquelchDefault(); // There is no per-channel squelch setting } else { DMRChannel *dch = new DMRChannel(); ch = dch; dch->setTimeSlot(timeSlot()); dch->setColorCode(colorCode()); } // Apply common settings ch->setName(name()); ch->setRXFrequency(rxFrequency()); if (isSimplex()) ch->setTXFrequency(rxFrequency()); else ch->setTXFrequency(txFrequency()); if (globalPower()) ch->setDefaultPower(); else ch->setPower(power()); ch->setRXOnly(rxOnly()); if (vox()) ch->setVOXDefault(); else ch->disableVOX(); ch->setOpenGD77ChannelExtension(new OpenGD77ChannelExtension()); ch->openGD77ChannelExtension()->enableScanZoneSkip(skipZoneScan()); ch->openGD77ChannelExtension()->enableScanAllSkip(skipScan()); ch->openGD77ChannelExtension()->enableBeep(beep()); ch->openGD77ChannelExtension()->enablePowerSave(powerSave()); if (hasFixedPosition()) ch->openGD77ChannelExtension()->setLocation(fixedPosition()); else ch->openGD77ChannelExtension()->setLocation(QGeoCoordinate()); ch->openGD77ChannelExtension()->setTalkerAliasTS1(aliasTimeSlot1()); ch->openGD77ChannelExtension()->setTalkerAliasTS2(aliasTimeSlot2()); // done. return ch; } bool OpenGD77BaseCodeplug::ChannelElement::link(Channel *c, Context &ctx, const ErrorStack& err) const { Q_UNUSED(err) // Link common if (c->is()) { // Link DMR channel DMRChannel *dc = c->as(); if (hasGroupList() && ctx.has(groupListIndex())) dc->setGroupListObj(ctx.get(groupListIndex())); if (hasTXContact() && ctx.has(txContactIndex())) dc->setTXContactObj(ctx.get(txContactIndex())); // Testing dmrId() == 0 fixes a bug in the OpenGD77 firmware. May change in future. if (hasDMRId() && (0 != dmrId())) { logDebug() << "Channel '" << c->name() << "' overrides default DMR id with " << dmrId() << "."; auto id = ctx.config()->radioIDs()->find(dmrId()); if (nullptr == id) { logDebug() << "DMR Id " << dmrId() << " is not defined yet, create one as 'Unknown ID'."; id = new DMRRadioID(QString("Unknown ID"), dmrId()); ctx.config()->radioIDs()->add(id); } dc->setRadioIdObj(id); } } else if (c->is()) { // Link FM channel auto fm = c->as(); if (hasAPRSIndex()) { if (! ctx.has(aprsIndex())) { logWarn() << "Cannot link APRS system index " << aprsIndex() << ": Unkown index. (ignored)"; } else { fm->setAPRSSystem(ctx.get(aprsIndex())); } } } return true; } bool OpenGD77BaseCodeplug::ChannelElement::encode(const Channel *c, Context &ctx, const ErrorStack& err) { clear(); setName(c->name()); setRXFrequency(c->rxFrequency()); setTXFrequency(c->txFrequency()); enableSimplex(false); clearPower(); if (! c->defaultPower()) setPower(c->power()); enableRXOnly(c->rxOnly()); // Enable vox bool defaultVOXEnabled = (c->defaultVOX() && (!ctx.config()->settings()->voxDisabled())); bool channelVOXEnabled = (! (c->voxDisabled()||c->defaultVOX())); enableVOX(defaultVOXEnabled || channelVOXEnabled); if (c->is()) { const FMChannel *ac = c->as(); setMode(MODE_ANALOG); setBandwidth(ac->bandwidth()); setRXTone(ac->rxTone()); setTXTone(ac->txTone()); // no per channel squelch setting if (ac->aprsSystem() && (0<=ctx.index(ac->aprsSystem()))) setAPRSIndex(ctx.index(ac->aprsSystem())); } else if (c->is()) { const DMRChannel *dc = c->as(); setMode(MODE_DIGITAL); setTimeSlot(dc->timeSlot()); setColorCode(dc->colorCode()); // OpenGD77 does not allow for both TX contact and group list, select one, prefer group list if (dc->groupListObj()) setGroupListIndex(ctx.index(dc->groupListObj())); else if (dc->txContactObj()) setTXContactIndex(ctx.index(dc->txContactObj())); if (dc->radioIdObj() != ctx.config()->settings()->defaultId()) setDMRId(dc->radioIdObj()->number()); } else { errMsg(err) << "Cannot encode channel of type '" << c->metaObject()->className() << "': Not supported by the radio."; return false; } if (nullptr == c->openGD77ChannelExtension()) return true; // apply extension enableSkipZoneScan(c->openGD77ChannelExtension()->scanZoneSkip()); enableSkipScan(c->openGD77ChannelExtension()->scanAllSkip()); enableBeep(c->openGD77ChannelExtension()->beep()); enablePowerSave(c->openGD77ChannelExtension()->powerSave()); if (c->openGD77ChannelExtension()->location().isValid()) setFixedPosition(c->openGD77ChannelExtension()->location()); else clearFixedPosition(); setAliasTimeSlot1(c->openGD77ChannelExtension()->talkerAliasTS1()); setAliasTimeSlot2(c->openGD77ChannelExtension()->talkerAliasTS2()); return true; } /* ********************************************************************************************* * * Implementation of OpenGD77BaseCodeplug::ChannelBankElement * ********************************************************************************************* */ OpenGD77BaseCodeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } OpenGD77BaseCodeplug::ChannelBankElement::~ChannelBankElement() { // pass... } void OpenGD77BaseCodeplug::ChannelBankElement::clear() { memset(_data, 0, size()); } bool OpenGD77BaseCodeplug::ChannelBankElement::isEnabled(unsigned idx) const { unsigned byte = Offset::bitmask() + idx/8, bit = idx%8; return getBit(byte, bit); } void OpenGD77BaseCodeplug::ChannelBankElement::enable(unsigned idx, bool enabled) { unsigned byte = Offset::bitmask() + idx/8, bit = idx%8; return setBit(byte, bit, enabled); } uint8_t * OpenGD77BaseCodeplug::ChannelBankElement::get(unsigned idx) const { return (_data+Offset::channels())+idx*ChannelElement::size(); } OpenGD77BaseCodeplug::ChannelElement OpenGD77BaseCodeplug::ChannelBankElement::channel(unsigned int n) { return ChannelElement((_data+Offset::channels())+n*ChannelElement::size()); } /* ******************************************************************************************** * * Implementation of OpenGD77BaseCodeplug::VFOChannelElement * ******************************************************************************************** */ OpenGD77BaseCodeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr, unsigned size) : ChannelElement(ptr, size) { // pass... } OpenGD77BaseCodeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr) : ChannelElement(ptr) { // pass... } void OpenGD77BaseCodeplug::VFOChannelElement::clear() { ChannelElement::clear(); setStepSize(12.5); setOffsetMode(OffsetMode::Off); setTXOffset(10.0); } QString OpenGD77BaseCodeplug::VFOChannelElement::name() const { return QString(); } void OpenGD77BaseCodeplug::VFOChannelElement::setName(const QString &name) { Q_UNUSED(name); ChannelElement::setName(""); } double OpenGD77BaseCodeplug::VFOChannelElement::stepSize() const { switch (StepSize(getUInt4(Offset::stepSize()))) { case StepSize::SS2_5kHz: return 2.5; case StepSize::SS5kHz: return 5; case StepSize::SS6_25kHz: return 6.25; case StepSize::SS10kHz: return 10.0; case StepSize::SS12_5kHz: return 12.5; case StepSize::SS20kHz: return 20; case StepSize::SS30kHz: return 30; case StepSize::SS50kHz: return 50; } return 12.5; } void OpenGD77BaseCodeplug::VFOChannelElement::setStepSize(double kHz) { if (2.5 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS2_5kHz); else if (5.0 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS5kHz); else if (6.25 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS6_25kHz); else if (10.0 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS10kHz); else if (12.5 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS12_5kHz); else if (20.0 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS20kHz); else if (30.0 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS30kHz); else setUInt4(Offset::stepSize(), (unsigned)StepSize::SS50kHz); } OpenGD77BaseCodeplug::VFOChannelElement::OffsetMode OpenGD77BaseCodeplug::VFOChannelElement::offsetMode() const { return (OffsetMode)getUInt2(Offset::offsetMode()); } void OpenGD77BaseCodeplug::VFOChannelElement::setOffsetMode(OffsetMode mode) { setUInt2(Offset::offsetMode(), (unsigned)mode); } double OpenGD77BaseCodeplug::VFOChannelElement::txOffset() const { return ((double)getBCD4_le(Offset::txOffset()))/100; } void OpenGD77BaseCodeplug::VFOChannelElement::setTXOffset(double f) { setBCD4_le(Offset::txOffset(), (f*100)); } /* ******************************************************************************************** * * Implementation of OpenGD77BaseCodeplug::GeneralSettingsElement * ******************************************************************************************** */ OpenGD77BaseCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } void OpenGD77BaseCodeplug::GeneralSettingsElement::clear() { setCall(""); setRadioId(0); } Frequency OpenGD77BaseCodeplug::GeneralSettingsElement::uhfMinFrequency() const { return Frequency::fromMHz(getBCD4_le(Offset::uhfMinFrequency())); } void OpenGD77BaseCodeplug::GeneralSettingsElement::setUHFMinFrequency(const Frequency &f) { setBCD4_le(Offset::uhfMinFrequency(), f.inMHz()); } Frequency OpenGD77BaseCodeplug::GeneralSettingsElement::uhfMaxFrequency() const { return Frequency::fromMHz(getBCD4_le(Offset::uhfMaxFrequency())); } void OpenGD77BaseCodeplug::GeneralSettingsElement::setUHFMaxFrequency(const Frequency &f) { setBCD4_le(Offset::uhfMaxFrequency(), f.inMHz()); } Frequency OpenGD77BaseCodeplug::GeneralSettingsElement::vhfMinFrequency() const { return Frequency::fromMHz(getBCD4_le(Offset::vhfMinFrequency())); } void OpenGD77BaseCodeplug::GeneralSettingsElement::setVHFMinFrequency(const Frequency &f) { setBCD4_le(Offset::vhfMinFrequency(), f.inMHz()); } Frequency OpenGD77BaseCodeplug::GeneralSettingsElement::vhfMaxFrequency() const { return Frequency::fromMHz(getBCD4_le(Offset::vhfMaxFrequency())); } void OpenGD77BaseCodeplug::GeneralSettingsElement::setVHFMaxFrequency(const Frequency &f) { setBCD4_le(Offset::vhfMaxFrequency(), f.inMHz()); } QString OpenGD77BaseCodeplug::GeneralSettingsElement::call() const { return readASCII(Offset::call(), Limit::callLength(), 0xff); } void OpenGD77BaseCodeplug::GeneralSettingsElement::setCall(const QString &call) { writeASCII(Offset::call(), call, Limit::callLength(), 0xff); } unsigned int OpenGD77BaseCodeplug::GeneralSettingsElement::radioId() const { return getBCD8_be(Offset::dmrId()); } void OpenGD77BaseCodeplug::GeneralSettingsElement::setRadioId(unsigned int id) { setBCD8_be(Offset::dmrId(), id); } bool OpenGD77BaseCodeplug::GeneralSettingsElement::encode(const Context &ctx, const ErrorStack &err) { DMRRadioID *id = ctx.config()->settings()->defaultId(); if (nullptr == id) { errMsg(err) << "Cannot encode DMR ID. No default ID defined."; return false; } setCall(id->name()); setRadioId(id->number()); return true; } bool OpenGD77BaseCodeplug::GeneralSettingsElement::decode(const Context &ctx, const ErrorStack &err) { Q_UNUSED(err) DMRRadioID *id = new DMRRadioID(call(), radioId()); ctx.config()->radioIDs()->add(id); ctx.config()->settings()->setDefaultId(id); return true; } /* ******************************************************************************************** * * Implementation of OpenGD77BaseCodeplug::APRSSettingsElement * ******************************************************************************************** */ OpenGD77BaseCodeplug::APRSSettingsElement::APRSSettingsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::APRSSettingsElement::APRSSettingsElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } void OpenGD77BaseCodeplug::APRSSettingsElement::clear() { Element::clear(); setName(""); clearFixedPosition(); setUInt32_le(Offset::fmFrequency(), 0); // Some random data, appears to be important writeASCII(Offset::unknownBytes(), "RA", 2); } bool OpenGD77BaseCodeplug::APRSSettingsElement::isValid() const { return ! name().isEmpty(); } QString OpenGD77BaseCodeplug::APRSSettingsElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0xff); } void OpenGD77BaseCodeplug::APRSSettingsElement::setName(const QString &name) { writeASCII(Offset::name(), name, Limit::nameLength(), 0xff); } unsigned int OpenGD77BaseCodeplug::APRSSettingsElement::sourceSSID() const { return getUInt8(Offset::sourceSSID()); } void OpenGD77BaseCodeplug::APRSSettingsElement::setSourceSSID(unsigned int ssid) { setUInt8(Offset::sourceSSID(), ssid); } bool OpenGD77BaseCodeplug::APRSSettingsElement::hasFixedPosition() const { return getBit(Offset::useFixedPosition()); } QGeoCoordinate OpenGD77BaseCodeplug::APRSSettingsElement::fixedPosition() const { uint32_t latCode = getUInt24_le(Offset::latitude()); uint32_t lonCode = getUInt24_le(Offset::longitude()); return QGeoCoordinate(decodeAngle(latCode), decodeAngle(lonCode)); } void OpenGD77BaseCodeplug::APRSSettingsElement::setFixedPosition(const QGeoCoordinate &coor) { setUInt24_le(Offset::latitude(), encodeAngle(coor.latitude())); setUInt24_le(Offset::longitude(), encodeAngle(coor.longitude())); setBit(Offset::useFixedPosition()); } void OpenGD77BaseCodeplug::APRSSettingsElement::clearFixedPosition() { clearBit(Offset::useFixedPosition()); } OpenGD77BaseCodeplug::APRSSettingsElement::PositionPrecision OpenGD77BaseCodeplug::APRSSettingsElement::positionPrecision() const { return (PositionPrecision)getUInt4(Offset::positionPrecision()); } void OpenGD77BaseCodeplug::APRSSettingsElement::setPositionPrecision(PositionPrecision prec) { setUInt4(Offset::positionPrecision(), (unsigned int) prec); } bool OpenGD77BaseCodeplug::APRSSettingsElement::hasVia1() const { return ! via1Call().isEmpty(); } QString OpenGD77BaseCodeplug::APRSSettingsElement::via1Call() const { return readASCII(Offset::via1Call(), 6, 0x00); } unsigned int OpenGD77BaseCodeplug::APRSSettingsElement::via1SSID() const { return getUInt8(Offset::via1SSID()); } void OpenGD77BaseCodeplug::APRSSettingsElement::setVia1(const QString &call, unsigned int ssid) { writeASCII(Offset::via1Call(), call, 6, 0x00); setUInt8(Offset::via1SSID(), ssid); } void OpenGD77BaseCodeplug::APRSSettingsElement::clearVia1() { setVia1("", 0); } bool OpenGD77BaseCodeplug::APRSSettingsElement::hasVia2() const { return ! via2Call().isEmpty(); } QString OpenGD77BaseCodeplug::APRSSettingsElement::via2Call() const { return readASCII(Offset::via2Call(), 6, 0x00); } unsigned int OpenGD77BaseCodeplug::APRSSettingsElement::via2SSID() const { return getUInt8(Offset::via2SSID()); } void OpenGD77BaseCodeplug::APRSSettingsElement::setVia2(const QString &call, unsigned int ssid) { writeASCII(Offset::via2Call(), call, 6, 0x00); setUInt8(Offset::via2SSID(), ssid); } void OpenGD77BaseCodeplug::APRSSettingsElement::clearVia2() { setVia2("", 0); } APRSSystem::Icon OpenGD77BaseCodeplug::APRSSettingsElement::icon() const { return (APRSSystem::Icon)getUInt8(Offset::iconIndex()); } void OpenGD77BaseCodeplug::APRSSettingsElement::setIcon(APRSSystem::Icon icon) { setUInt8(Offset::iconTable(), (APRSSystem::SECONDARY_TABLE & (unsigned int)icon) ? 1 : 0); setUInt8(Offset::iconIndex(), APRSSystem::ICON_MASK & (unsigned int)icon); } QString OpenGD77BaseCodeplug::APRSSettingsElement::comment() const { return readASCII(Offset::comment(), Limit::commentLength(), 0x00); } void OpenGD77BaseCodeplug::APRSSettingsElement::setComment(const QString &comment) { writeASCII(Offset::comment(), comment, Limit::commentLength(), 0x00); } OpenGD77BaseCodeplug::APRSSettingsElement::BaudRate OpenGD77BaseCodeplug::APRSSettingsElement::baudRate() const { return getBit(Offset::baudRate()) ? BaudRate::Baud300 : BaudRate::Baud1200; } void OpenGD77BaseCodeplug::APRSSettingsElement::setBaudRate(BaudRate rate) { setBit(Offset::baudRate(), BaudRate::Baud300 == rate); } Frequency OpenGD77BaseCodeplug::APRSSettingsElement::fmFrequency() const { return Frequency::fromHz(getUInt32_le(Offset::fmFrequency())*10); } void OpenGD77BaseCodeplug::APRSSettingsElement::setFMFrequency(Frequency f) { setUInt32_le(Offset::fmFrequency(), f.inHz()/10); } bool OpenGD77BaseCodeplug::APRSSettingsElement::encode(const APRSSystem *sys, const Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); clear(); setName(sys->name()); setSourceSSID(sys->srcSSID()); QStringList vias = sys->path().split(","); unsigned int viaCount = 0; for (auto via: vias) { QRegExp pattern("^([A-Z0-9]+)-(1?[0-9])$"); if (! pattern.exactMatch(via)) continue; if (0 == viaCount) setVia1(pattern.cap(1), pattern.cap(2).toUInt()); else if (1 == viaCount) setVia2(pattern.cap(1), pattern.cap(2).toUInt()); else break; viaCount++; } setIcon(sys->icon()); setComment(sys->message()); clearFixedPosition(); setBaudRate(BaudRate::Baud1200); setPositionPrecision(PositionPrecision::Max); if(sys->hasRevertChannel()) { setFMFrequency(sys->revertChannel()->txFrequency()); } if (nullptr == sys->openGD77Extension()) return true; if (sys->openGD77Extension()->location().isValid()) setFixedPosition(sys->openGD77Extension()->location()); return true; } APRSSystem * OpenGD77BaseCodeplug::APRSSettingsElement::decode(const Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx); Q_UNUSED(err); if (! isValid()) { errMsg(err) << "Cannot decode invalid APRS settings."; return nullptr; } APRSSystem *sys = new APRSSystem(); sys->setName(name()); sys->setDestination("APN000", 0); sys->setSrcSSID(sourceSSID()); QStringList path; if (hasVia1()) path.append(QString("%1-%2").arg(via1Call()).arg(via1SSID())); if (hasVia2()) path.append(QString("%1-%2").arg(via2Call()).arg(via2SSID())); sys->setPath(path.join(",")); sys->setIcon(icon()); sys->setMessage(comment()); auto ext = new OpenGD77APRSSystemExtension(); sys->setOpenGD77Extension(ext); if (hasFixedPosition()) ext->setLocation(fixedPosition()); return sys; } bool OpenGD77BaseCodeplug::APRSSettingsElement::link(APRSSystem *sys, const Context &ctx, const ErrorStack &err) { Q_UNUSED(err); if(fmFrequency().inHz() == 0) { sys->resetRevertChannel(); } else { // First, try to find a matching analog channel in list FMChannel *ch = ctx.config()->channelList()->findFMChannelByTxFreq(fmFrequency()); if (! ch) { // If no channel is found, create one with the settings from APRS channel: ch = new FMChannel(); ch->setName("APRS Channel"); ch->setRXFrequency(fmFrequency()); ch->setTXFrequency(fmFrequency()); ch->setBandwidth(FMChannel::Bandwidth::Narrow); logInfo() << "No matching APRS channel found for TX frequency " << double(fmFrequency().inHz())/1e6 << "MHz, create one as 'APRS Channel'"; ctx.config()->channelList()->add(ch); } sys->setRevertChannel(ch); } if (ctx.config()->settings()->defaultId()) sys->setSource(ctx.config()->settings()->defaultId()->name()); return true; } /* ******************************************************************************************** * * Implementation of OpenGD77BaseCodeplug::APRSSettingsBank * ******************************************************************************************** */ OpenGD77BaseCodeplug::APRSSettingsBankElement::APRSSettingsBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::APRSSettingsBankElement::APRSSettingsBankElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } void OpenGD77BaseCodeplug::APRSSettingsBankElement::clear() { for (unsigned int i=0; i(i)) { if (! system(i).encode(ctx.get(i), ctx, err)) { errMsg(err) << "Cannot encode APRS system '" << ctx.get(i)->name() << " at index " << i << "."; return false; } } else { system(i).clear(); } } return true; } bool OpenGD77BaseCodeplug::APRSSettingsBankElement::decode(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; iposSystems()->add(sys); ctx.add(sys, i); } } return true; } bool OpenGD77BaseCodeplug::APRSSettingsBankElement::link(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; i(i)) { errMsg(err) << "Cannot link APRS system at index " << i << ": Not found in context."; return false; } if (! system(i).link(ctx.get(i), ctx, err)) { errMsg(err) << "Cannot link APRS system '" << ctx.get(i)->name() << "' at index " << i << "."; return false; } } } return true; } /* ******************************************************************************************** * * Implementation of OpenGD77BaseCodeplug::DTMFContactElement * ******************************************************************************************** */ OpenGD77BaseCodeplug::DTMFContactElement::DTMFContactElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::DTMFContactElement::DTMFContactElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } bool OpenGD77BaseCodeplug::DTMFContactElement::isValid() const { return !name().isEmpty(); } void OpenGD77BaseCodeplug::DTMFContactElement::clear() { setName(""); setNumber(""); } QString OpenGD77BaseCodeplug::DTMFContactElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0xff); } void OpenGD77BaseCodeplug::DTMFContactElement::setName(const QString &name) { writeASCII(Offset::name(), name, Limit::nameLength(), 0xff); } QString OpenGD77BaseCodeplug::DTMFContactElement::number() const { QString number; uint8_t *ptr = _data + Offset::number(); const QVector lut = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','*','#'}; for (unsigned int i=0; (i lut = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','*','#'}; for (unsigned int i=0; (iname()); setNumber(contact->number()); return true; } DTMFContact * OpenGD77BaseCodeplug::DTMFContactElement::decode(const Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); if (! isValid()) { errMsg(err) << "Cannot decode invalid DTMF contact."; return nullptr; } return new DTMFContact(name(), number()); } /* ******************************************************************************************** * * Implementation of OpenGD77BaseCodeplug::DTMFContactBankElement * ******************************************************************************************** */ OpenGD77BaseCodeplug::DTMFContactBankElement::DTMFContactBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::DTMFContactBankElement::DTMFContactBankElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } void OpenGD77BaseCodeplug::DTMFContactBankElement::clear() { for (unsigned int i=0; i(i)) { if (! contact(i).encode(ctx.get(i), ctx, err)) { errMsg(err) << "Cannot encode DTMF contact " << ctx.get(i)->name() << " at index " << i << "."; return false; } } else { contact(i).clear(); } } return true; } bool OpenGD77BaseCodeplug::DTMFContactBankElement::decode(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; icontacts()->add(cnt); ctx.add(cnt, i); } return true; } /* ********************************************************************************************* * * Implementation of OpenGD77BaseCodeplug::BootSettingsElement * ********************************************************************************************* */ OpenGD77BaseCodeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr) : Element(ptr, 0x20) { // pass... } OpenGD77BaseCodeplug::BootSettingsElement::~BootSettingsElement() { // pass... } void OpenGD77BaseCodeplug::BootSettingsElement::clear() { enableBootText(true); clearBootPassword(); setLine1(""); setLine2(""); } bool OpenGD77BaseCodeplug::BootSettingsElement::bootText() const { return (1 == getUInt8(Offset::bootText())); } void OpenGD77BaseCodeplug::BootSettingsElement::enableBootText(bool enable) { setUInt8(Offset::bootText(), (enable ? 1 :0)); } bool OpenGD77BaseCodeplug::BootSettingsElement::bootPasswordEnabled() const { return (1 == getUInt8(Offset::bootPasswdEnable())); } unsigned OpenGD77BaseCodeplug::BootSettingsElement::bootPassword() const { return getBCD8_be(Offset::bootPasswd()); } void OpenGD77BaseCodeplug::BootSettingsElement::setBootPassword(unsigned passwd) { setBCD8_be(Offset::bootPasswd(), passwd); setUInt8(Offset::bootPasswdEnable(), 1); } void OpenGD77BaseCodeplug::BootSettingsElement::clearBootPassword() { setUInt8(Offset::bootPasswdEnable(), 0); } QString OpenGD77BaseCodeplug::BootSettingsElement::line1() const { return readASCII(Offset::line1(), Limit::lineLength(), 0xff); } void OpenGD77BaseCodeplug::BootSettingsElement::setLine1(const QString &text) { writeASCII(Offset::line1(), text, Limit::lineLength(), 0xff); } QString OpenGD77BaseCodeplug::BootSettingsElement::line2() const { return readASCII(Offset::line2(), Limit::lineLength(), 0xff); } void OpenGD77BaseCodeplug::BootSettingsElement::setLine2(const QString &text) { writeASCII(Offset::line2(), text, Limit::lineLength(), 0xff); } bool OpenGD77BaseCodeplug::BootSettingsElement::encode(const Context &ctx, const ErrorStack &err) { Q_UNUSED(err) setLine1(ctx.config()->settings()->introLine1()); setLine2(ctx.config()->settings()->introLine2()); return true; } bool OpenGD77BaseCodeplug::BootSettingsElement::decode(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) ctx.config()->settings()->setIntroLine1(line1()); ctx.config()->settings()->setIntroLine2(line2()); return true; } /* ********************************************************************************************* * * Implementation of OpenGD77BaseCodeplug::ZoneElement * ********************************************************************************************* */ OpenGD77BaseCodeplug::ZoneElement::ZoneElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::ZoneElement::ZoneElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } OpenGD77BaseCodeplug::ZoneElement::~ZoneElement() { // pass... } void OpenGD77BaseCodeplug::ZoneElement::clear() { memset(_data+Offset::name(), 0xff, Limit::nameLength()); memset(_data+Offset::channels(), 0x00, Offset::betweenChannels()*Limit::memberCount()); } bool OpenGD77BaseCodeplug::ZoneElement::isValid() const { return (! name().isEmpty()); } QString OpenGD77BaseCodeplug::ZoneElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0xff); } void OpenGD77BaseCodeplug::ZoneElement::setName(const QString &name) { writeASCII(Offset::name(), name, Limit::nameLength(), 0xff); } bool OpenGD77BaseCodeplug::ZoneElement::hasMember(unsigned n) const { if (n >= Limit::memberCount()) return false; return 0 != getUInt16_le(Offset::channels() + Offset::betweenChannels()*n); } unsigned OpenGD77BaseCodeplug::ZoneElement::member(unsigned n) const { if (n >= Limit::memberCount()) return 0; return getUInt16_le(Offset::channels() + Offset::betweenChannels()*n)-1; } void OpenGD77BaseCodeplug::ZoneElement::setMember(unsigned n, unsigned idx) { if (n >= Limit::memberCount()) return; setUInt16_le(Offset::channels() + Offset::betweenChannels()*n, idx+1); } void OpenGD77BaseCodeplug::ZoneElement::clearMember(unsigned n) { setUInt16_le(Offset::channels() + Offset::betweenChannels()*n, 0); } Zone * OpenGD77BaseCodeplug::ZoneElement::decode(const Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx) if (! isValid()) { errMsg(err) << "Cannot decode an invalid zone."; return nullptr; } return new Zone(name()); } bool OpenGD77BaseCodeplug::ZoneElement::link(Zone *zone, Context &ctx, const ErrorStack &err) const { if (! isValid()) { errMsg(err) << "Cannot link invalid zone."; return false; } for (unsigned int i=0; (i(member(i))) { zone->A()->add(ctx.get(member(i))); } else { logWarn() << "While linking zone '" << zone->name() << "': " << i <<"-th channel index " << member(i) << " out of bounds."; } } return true; } bool OpenGD77BaseCodeplug::ZoneElement::encode(const Zone *zone, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) if (zone->A()->count() && zone->B()->count()) setName(zone->name() + " A"); else setName(zone->name()); for (unsigned int i=0; iA()->count()) setMember(i, ctx.index(zone->A()->get(i))); else clearMember(i); } return true; } /* ********************************************************************************************* * * Implementation of OpenGD77BaseCodeplug::ZoneBankElement * ********************************************************************************************* */ OpenGD77BaseCodeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } OpenGD77BaseCodeplug::ZoneBankElement::~ZoneBankElement() { // pass... } void OpenGD77BaseCodeplug::ZoneBankElement::clear() { memset(_data, 0, size()); } bool OpenGD77BaseCodeplug::ZoneBankElement::isEnabled(unsigned idx) const { unsigned byte= Offset::bitmap() + idx/8, bit = idx%8; return getBit(byte, bit); } void OpenGD77BaseCodeplug::ZoneBankElement::enable(unsigned idx, bool enabled) { unsigned byte=Offset::bitmap() + idx/8, bit = idx%8; setBit(byte, bit, enabled); } OpenGD77BaseCodeplug::ZoneElement OpenGD77BaseCodeplug::ZoneBankElement::zone(unsigned int idx) { return ZoneElement(_data + Offset::zones() + idx*Offset::betweenZones()); } bool OpenGD77BaseCodeplug::ZoneBankElement::encode(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; i(i)) { if (! zone(i).encode(ctx.get(i), ctx, err)) { errMsg(err) << "Cannot encode zone '" << ctx.get(i)->name() << "' at index " << i << "."; return false; } enable(i, true); } else { zone(i).clear(); enable(i, false); } } return true; } bool OpenGD77BaseCodeplug::ZoneBankElement::decode(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; izones()->add(obj); ctx.add(obj, i); } return true; } bool OpenGD77BaseCodeplug::ZoneBankElement::link(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; i(i); if (! zone(i).link(obj, ctx, err)) { errMsg(err) << "Cannot link zone '" << obj->name() << "' at index " << i << "."; return false; } } return true; } /* ********************************************************************************************* * * Implementation of OpenGD77BaseCodeplug::ContactElement * ********************************************************************************************* */ OpenGD77BaseCodeplug::ContactElement::ContactElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::ContactElement::ContactElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } OpenGD77BaseCodeplug::ContactElement::~ContactElement() { // pass... } void OpenGD77BaseCodeplug::ContactElement::clear() { setName(""); setNumber(0); setType(DMRContact::GroupCall); setTimeSlotOverride(TimeSlotOverride::None); } bool OpenGD77BaseCodeplug::ContactElement::isValid() const { return (! name().isEmpty()); } QString OpenGD77BaseCodeplug::ContactElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0xff); } void OpenGD77BaseCodeplug::ContactElement::setName(const QString name) { writeASCII(Offset::name(), name, Limit::nameLength(), 0xff); } unsigned OpenGD77BaseCodeplug::ContactElement::number() const { return getBCD8_be(Offset::number()); } void OpenGD77BaseCodeplug::ContactElement::setNumber(unsigned id) { setBCD8_be(Offset::number(), id); } DMRContact::Type OpenGD77BaseCodeplug::ContactElement::type() const { switch (getUInt8(Offset::type())) { case 0: return DMRContact::GroupCall; case 1: return DMRContact::PrivateCall; case 2: return DMRContact::AllCall; default: break; } return DMRContact::PrivateCall; } void OpenGD77BaseCodeplug::ContactElement::setType(DMRContact::Type type) { switch (type) { case DMRContact::GroupCall: setUInt8(Offset::type(), 0); break; case DMRContact::PrivateCall: setUInt8(Offset::type(), 1); break; case DMRContact::AllCall: setUInt8(Offset::type(), 2); break; } } OpenGD77BaseCodeplug::ContactElement::TimeSlotOverride OpenGD77BaseCodeplug::ContactElement::timeSlotOverride() const { return (TimeSlotOverride)getUInt8(Offset::timeSlotOverride()); } void OpenGD77BaseCodeplug::ContactElement::setTimeSlotOverride(TimeSlotOverride ts) { setUInt8(Offset::timeSlotOverride(), (unsigned int) ts); } DMRContact * OpenGD77BaseCodeplug::ContactElement::decode(Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx) if (! isValid()) { errMsg(err) << "Cannot create contact from an invalid element."; return nullptr; } auto contact = new DMRContact(type(), name(), number(), false); contact->setOpenGD77ContactExtension(new OpenGD77ContactExtension()); switch (timeSlotOverride()) { case TimeSlotOverride::None: contact->openGD77ContactExtension()->setTimeSlotOverride( OpenGD77ContactExtension::TimeSlotOverride::None); break; case TimeSlotOverride::TS1: contact->openGD77ContactExtension()->setTimeSlotOverride( OpenGD77ContactExtension::TimeSlotOverride::TS1); break; case TimeSlotOverride::TS2: contact->openGD77ContactExtension()->setTimeSlotOverride( OpenGD77ContactExtension::TimeSlotOverride::TS2); break; } return contact; } bool OpenGD77BaseCodeplug::ContactElement::encode(const DMRContact *cont, Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err) setName(cont->name()); setNumber(cont->number()); setType(cont->type()); if (nullptr == cont->openGD77ContactExtension()) return true; switch (cont->openGD77ContactExtension()->timeSlotOverride()) { case OpenGD77ContactExtension::TimeSlotOverride::None: setTimeSlotOverride(TimeSlotOverride::None); break; case OpenGD77ContactExtension::TimeSlotOverride::TS1: setTimeSlotOverride(TimeSlotOverride::TS1); break; case OpenGD77ContactExtension::TimeSlotOverride::TS2: setTimeSlotOverride(TimeSlotOverride::TS2); break; } return true; } /* ********************************************************************************************* * * Implementation of OpenGD77BaseCodeplug::ContactBankElement * ********************************************************************************************* */ OpenGD77BaseCodeplug::ContactBankElement::ContactBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::ContactBankElement::ContactBankElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } void OpenGD77BaseCodeplug::ContactBankElement::clear() { for (unsigned int i=0; i(i)) { if (! contact(i).encode(ctx.get(i), ctx, err)) { errMsg(err) << "Cannot encode DMR contact " << ctx.get(i)->name() << " at index " << i << "."; return false; } } else { contact(i).clear(); } } return true; } bool OpenGD77BaseCodeplug::ContactBankElement::decode(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; icontacts()->add(cnt); ctx.add(cnt, i); } return true; } /* ********************************************************************************************* * * Implementation of OpenGD77BaseCodeplug::GroupListElement * ********************************************************************************************* */ OpenGD77BaseCodeplug::GroupListElement::GroupListElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::GroupListElement::GroupListElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } void OpenGD77BaseCodeplug::GroupListElement::clear() { memset(_data + Offset::name(), 0xff, Limit::nameLength()+1); memset(_data + Offset::contacts(), 0, Limit::contactCount()*2); } QString OpenGD77BaseCodeplug::GroupListElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0xff); } void OpenGD77BaseCodeplug::GroupListElement::setName(const QString &name) { writeASCII(Offset::name(), name, Limit::nameLength(), 0xff); } bool OpenGD77BaseCodeplug::GroupListElement::hasContactIndex(unsigned int i) const { return 0 != getUInt16_le(Offset::contacts() + i*Offset::betweenContacts()); } unsigned int OpenGD77BaseCodeplug::GroupListElement::contactIndex(unsigned int i) const { return getUInt16_le(Offset::contacts() + i*Offset::betweenContacts()) - 1; } void OpenGD77BaseCodeplug::GroupListElement::setContactIndex(unsigned int i, unsigned int contactIdx) { setUInt16_le(Offset::contacts() + i*Offset::betweenContacts(), contactIdx + 1); } void OpenGD77BaseCodeplug::GroupListElement::clearContactIndex(unsigned int i) { setUInt16_le(Offset::contacts() + i*Offset::betweenContacts(), 0); } bool OpenGD77BaseCodeplug::GroupListElement::encode(RXGroupList *lst, Context &ctx, const ErrorStack &err) { setName(lst->name()); for (unsigned int i=0; icount()) { int idx = ctx.index(lst->contact(i)); if (0 > idx) { errMsg(err) << "Cannot encode group list '" << lst->name() << "', contact '" << lst->contact(i)->name() << "' not indexed."; return false; } setContactIndex(i, idx); } else { clearContactIndex(i); } } return true; } RXGroupList * OpenGD77BaseCodeplug::GroupListElement::decode(Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx); Q_UNUSED(err); return new RXGroupList(name()); } bool OpenGD77BaseCodeplug::GroupListElement::link(RXGroupList *lst, Context &ctx, const ErrorStack &err) const { for (unsigned int i=0; i(contactIndex(i))) { errMsg(err) << "Cannot resolve contact index " << contactIndex(i) << "."; return false; } lst->addContact(ctx.get(contactIndex(i))); } return true; } /* ********************************************************************************************* * * Implementation of OpenGD77BaseCodeplug::GroupListBankElement * ********************************************************************************************* */ OpenGD77BaseCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } OpenGD77BaseCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } void OpenGD77BaseCodeplug::GroupListBankElement::clear() { for (unsigned int i=0; i(i)) { auto obj = ctx.get(i); setGroupListContactCount(i, obj->count()); if (! groupList(i).encode(obj, ctx, err)) { clearGroupList(i); errMsg(err) << "Cannot encode group list '" << obj->name() << "' at index " << i << "."; return false; } } else { clearGroupList(i); } } return true; } bool OpenGD77BaseCodeplug::GroupListBankElement::decode(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; irxGroupLists()->add(obj); ctx.add(obj, i); } return true; } bool OpenGD77BaseCodeplug::GroupListBankElement::link(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; i(i); if (! groupList(i).link(obj, ctx, err)) { errMsg(err) << "Cannot link group list '" << obj->name() << "' at index " << i << "."; return false; } } return true; } /* ********************************************************************************************* * * Implementation of OpenGD77BaseCodeplug * ********************************************************************************************* */ OpenGD77BaseCodeplug::OpenGD77BaseCodeplug(QObject *parent) : Codeplug{parent} { // pass... } void OpenGD77BaseCodeplug::clear() { // Clear general config clearGeneralSettings(); clearDTMFSettings(); clearAPRSSettings(); // clear DTMF contacts clearDTMFContacts(); // clear channel clearChannels(); // clear boot settings clearBootSettings(); // clear VFO settings clearVFOSettings(); // clear zones clearZones(); // Clear contacts clearContacts(); // clear group lists clearGroupLists(); } bool OpenGD77BaseCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) const { Q_UNUSED(err) // Map radio IDs for (int i=0; iradioIDs()->count(); i++) ctx.add(config->radioIDs()->getId(i), i+1); // Map digital and DTMF contacts for (int i=0, d=0, a=0; icontacts()->count(); i++) { if (config->contacts()->contact(i)->is()) { ctx.add(config->contacts()->contact(i)->as(), d); d++; } else if (config->contacts()->contact(i)->is()) { ctx.add(config->contacts()->contact(i)->as(), a); a++; } } // Map rx group lists for (int i=0; irxGroupLists()->count(); i++) ctx.add(config->rxGroupLists()->list(i), i); // Map channels for (int i=0; ichannelList()->count(); i++) ctx.add(config->channelList()->channel(i), i); // Map zones for (int i=0; izones()->count(); i++) ctx.add(config->zones()->zone(i), i); // Map FM APRS systems for (int i=0,a=0; iposSystems()->count(); i++) { if (config->posSystems()->system(i)->is()) { ctx.add(config->posSystems()->system(i)->as(), a); a++; } } return true; } Config * OpenGD77BaseCodeplug::preprocess(Config *config, const ErrorStack &err) const { Config *intermediate = Codeplug::preprocess(config, err); if (nullptr == intermediate) { errMsg(err) << "Cannot pre-process OpenGD77 codeplug."; return nullptr; } ZoneSplitVisitor splitter; if (! splitter.process(intermediate, err)) { errMsg(err) << "Cannot split zone for OpenGD77 codeplug."; delete intermediate; return nullptr; } return intermediate; } bool OpenGD77BaseCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) { // Check if default DMR id is set. if (config->settings()->defaultIdRef()->isNull()) { errMsg(err) << "No default radio ID specified."; return false; } // Create index<->object table. Context ctx(config); if (! index(config, ctx, err)) { errMsg(err) << "Cannot index configuration objects."; return false; } return this->encodeElements(flags, ctx); } bool OpenGD77BaseCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) { // General config if (! this->encodeGeneralSettings(flags, ctx, err)) { errMsg(err) << "Cannot encode general settings."; return false; } if (! this->encodeDTMFSettings(flags, ctx, err)) { errMsg(err) << "Cannot encode DTMF settings."; return false; } if (! this->encodeAPRSSettings(flags, ctx, err)) { errMsg(err) << "Cannot encode APRS settings."; return false; } if (! this->encodeDTMFContacts(flags, ctx, err)) { errMsg(err) << "Cannot encode DTMF contacts."; return false; } if (! this->encodeChannels(flags, ctx, err)) { errMsg(err) << "Cannot encode channels"; return false; } if (! this->encodeBootSettings(flags, ctx, err)) { errMsg(err) << "Cannot encode boot text."; return false; } if (! this->encodeZones(flags, ctx, err)) { errMsg(err) << "Cannot encode zones."; return false; } // Define Contacts if (! this->encodeContacts(flags, ctx, err)) { errMsg(err) << "Cannot encode contacts."; return false; } if (! this->encodeGroupLists(flags, ctx, err)) { errMsg(err) << "Cannot encode group lists."; return false; } return true; } bool OpenGD77BaseCodeplug::decode(Config *config, const ErrorStack &err) { // Clear config object config->clear(); // Create index<->object table. Context ctx(config); return this->decodeElements(ctx, err); } bool OpenGD77BaseCodeplug::postprocess(Config *config, const ErrorStack &err) const { if (! Codeplug::postprocess(config, err)) { errMsg(err) << "Cannot post-process Radioddy codeplug."; return false; } ZoneMergeVisitor merger; if (! merger.process(config, err)) { errMsg(err) << "Cannot merg zones in decoded Radioddity codeplug."; return false; } return true; } bool OpenGD77BaseCodeplug::decodeElements(Context &ctx, const ErrorStack &err) { if (! this->decodeGeneralSettings(ctx, err)) { errMsg(err) << "Cannot decode general settings."; return false; } if (! this->decodeDTMFSettings(ctx, err)) { errMsg(err) << "Cannot decode DTMF settings."; return false; } if (! this->decodeAPRSSettings(ctx, err)) { errMsg(err) << "Cannot decode APRS settings."; return false; } if (! this->decodeBootSettings(ctx, err)) { errMsg(err) << "Cannot decode boot settings."; return false; } if (! this->createContacts(ctx, err)) { errMsg(err) << "Cannot create contacts."; return false; } if (! this->createDTMFContacts(ctx, err)) { errMsg(err) << "Cannot create DTMF contacts"; return false; } if (! this->createChannels(ctx, err)) { errMsg(err) << "Cannot create channels."; return false; } if (! this->createZones(ctx, err)) { errMsg(err) << "Cannot create zones."; return false; } if (! this->createGroupLists(ctx, err)) { errMsg(err) << "Cannot create group lists."; return false; } if (! this->linkChannels(ctx, err)) { errMsg(err) << "Cannot link channels."; return false; } if (! this->linkZones(ctx, err)) { errMsg(err) << "Cannot link zones."; return false; } if (! this->linkGroupLists(ctx, err)) { errMsg(err) << "Cannot link group lists."; return false; } if (! this->linkAPRSSettings(ctx, err)) { errMsg(err) << "Cannot decode APRS settings."; return false; } return true; } qdmr-0.12.3/lib/opengd77base_codeplug.hh000066400000000000000000001301711501654372000200040ustar00rootroot00000000000000#ifndef OPENGD77BASE_CODEPLUG_HH #define OPENGD77BASE_CODEPLUG_HH #include "channel.hh" #include "codeplug.hh" #include "gpssystem.hh" #include "contact.hh" #include "zone.hh" #include "satellitedatabase.hh" #include /** Base codeplug for all OpenGD77 based firmware variants. * @ingroup ogd77 */ class OpenGD77BaseCodeplug : public Codeplug { Q_OBJECT public: /** Possible image types. */ enum ImageType { EEPROM = 0, FLASH = 1 }; public: /** Encodes an angle used to store locations. */ static uint32_t encodeAngle(double degee); /** Decodes an angle used to store locations. */ static double decodeAngle(uint32_t code); /** Encodes a selective call (tx/rx tone). */ static uint16_t encodeSelectiveCall(const SelectiveCall &call); /** Decodes a selective call (tx/rx tone). */ static SelectiveCall decodeSelectiveCall(uint16_t code); public: /** Implements the base for all OpenGD77 channel encodings. */ class ChannelElement: public Codeplug::Element { public: /** Possible channel types. */ enum Mode { MODE_ANALOG = 0, ///< Analog channel, aka FM. MODE_DIGITAL = 1 ///< Digital channel, aka DMR. }; /** Alias to transmit. */ enum class Alias { None = 0, APRS = 1, Text = 2, Both = 3 }; /** Possible squelch modes. */ enum class SquelchMode { Global, Open, Normal, Closed }; protected: /** Constructs a channel from the given memory. */ ChannelElement(uint8_t *ptr, size_t size); public: /** Constructs a channel from the given memory. */ explicit ChannelElement(uint8_t *ptr); /** Destructor. */ virtual ~ChannelElement(); /** The size of the channel. */ static constexpr unsigned int size() { return 0x0038; } /** Resets the channel. */ virtual void clear(); /** Returns the name of the channel. */ virtual QString name() const; /** Sets the name of the channel. */ virtual void setName(const QString &n); /** Returns the RX frequency of the channel. */ virtual Frequency rxFrequency() const; /** Sets the RX frequency of the channel. */ virtual void setRXFrequency(const Frequency &freq); /** Returns the TX frequency of the channel. */ virtual Frequency txFrequency() const; /** Sets the TX frequency of the channel. */ virtual void setTXFrequency(const Frequency &freq); /** Returns the channel mode. */ virtual Mode mode() const; /** Sets the channel mode. */ virtual void setMode(Mode mode); /** Returns @c true if the power-setting is global. */ virtual bool globalPower() const; /** Returns the power setting of the channel. */ virtual Channel::Power power() const; /** Sets the power setting of the channel. */ virtual void setPower(Channel::Power pwr); /** Clears the power setting. The global power setting is used. */ virtual void clearPower(); /** Returns @c true, if a fixed position is set for the channel. */ virtual bool hasFixedPosition() const; /** Returns the fixed position. */ virtual QGeoCoordinate fixedPosition() const; /** Sets the fixed position for this channel. */ virtual void setFixedPosition(const QGeoCoordinate &coordinate); /** Resets the fixed position. */ virtual void clearFixedPosition(); /** Returns the RX subtone. */ virtual SelectiveCall rxTone() const; /** Sets the RX subtone. */ virtual void setRXTone(const SelectiveCall &code); /** Returns the TX subtone. */ virtual SelectiveCall txTone() const; /** Sets the TX subtone. */ virtual void setTXTone(const SelectiveCall &code); /** Returns @c true if the channel is set to simplex. */ virtual bool isSimplex() const; /** Sets the channel to simplex. */ virtual void enableSimplex(bool enable); /** Returns @c true, if the power-save feature is enabled. */ virtual bool powerSave() const; /** Enables/disables power-save. */ virtual void enablePowerSave(bool enable); /** Returns @c true, if the "beep" is enabled. */ virtual bool beep() const; /** Enables/disables "the beep". */ virtual void enableBeep(bool enable); /** Returns @c true if the global DMR ID is overridden. */ virtual bool hasDMRId() const; /** Retruns the DMR ID for this channel. */ virtual unsigned int dmrId() const; /** Sets the DMR ID for this channel. */ virtual void setDMRId(unsigned int id); /** Resets the DMR ID for this channel to the global one. */ virtual void clearDMRId(); /** Returns @c true if a group list is set. */ virtual bool hasGroupList() const; /** Returns the group-list index. */ virtual unsigned groupListIndex() const; /** Sets the group-list index. */ virtual void setGroupListIndex(unsigned index); /** Clears the group list index. */ virtual void clearGroupListIndex(); /** Returns the color code. */ virtual unsigned colorCode() const; /** Sets thecolor code. */ virtual void setColorCode(unsigned cc); /** Returns @c true, if the APRS system index is set. */ virtual bool hasAPRSIndex() const; /** Returns the APRS system index. */ virtual unsigned int aprsIndex() const; /** Sets the APRS system index. */ virtual void setAPRSIndex(unsigned int index); /** Resets the APRS system index. */ virtual void clearAPRSIndex(); /** Returns @c true, if the TX contact is set. */ virtual bool hasTXContact() const; /** Returns the TX contact index. */ virtual unsigned int txContactIndex() const; /** Sets the TX contact index. */ virtual void setTXContactIndex(unsigned int index); /** Clears the TX contact index. */ virtual void clearTXContact(); /** Returns the alias transmitted on time slot 1. */ virtual OpenGD77ChannelExtension::TalkerAlias aliasTimeSlot1() const; /** Sets the alias transmitted on time slot 1. */ virtual void setAliasTimeSlot1(OpenGD77ChannelExtension::TalkerAlias alias); /** Returns the alias transmitted on time slot 2. */ virtual OpenGD77ChannelExtension::TalkerAlias aliasTimeSlot2() const; /** Sets the alias transmitted on time slot 2. */ virtual void setAliasTimeSlot2(OpenGD77ChannelExtension::TalkerAlias alias); /** Returns the time slot of the channel. */ virtual DMRChannel::TimeSlot timeSlot() const; /** Sets the time slot of the channel. */ virtual void setTimeSlot(DMRChannel::TimeSlot ts); /** Returns the bandwidth. */ virtual FMChannel::Bandwidth bandwidth() const; /** Sets the bandwidth. */ virtual void setBandwidth(FMChannel::Bandwidth bw); /** Returns @c true if RX only is enabled. */ virtual bool rxOnly() const; /** Enables/disables RX only. */ virtual void enableRXOnly(bool enable); /** Returns @c true if channel is skipped in a scan. */ virtual bool skipScan() const; /** Enables/disables skipping in scan. */ virtual void enableSkipScan(bool enable); /** Returns @c true if channel is skipped in zone scan. */ virtual bool skipZoneScan() const; /** Enables/disables skipping in zone scan. */ virtual void enableSkipZoneScan(bool enable); /** Returns @c true if VOX is enabled. */ virtual bool vox() const; /** Enables/disables VOX. */ virtual void enableVOX(bool enable); /** Returns the squelch mode*/ virtual SquelchMode squelchMode() const; /** Retunrs the squelch level. */ virtual unsigned int squelchLevel() const; /** Set the squelch level and mode. Level is ignored, if mode is not normal.*/ virtual void setSquelch(SquelchMode mode, unsigned int level); /** Constructs a generic @c Channel object from the codeplug channel. */ virtual Channel *decode(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links a previously constructed channel to the rest of the configuration. */ virtual bool link(Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Initializes this codeplug channel from the given generic configuration. */ virtual bool encode(const Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for this element. */ struct Limit { /** The maximum length of the name. */ static constexpr unsigned int nameLength() { return 16; } }; protected: /** Some internal offsets within the channel element. */ struct Offset: public Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int rxFrequency() { return 0x0010; } static constexpr unsigned int txFrequency() { return 0x0014; } static constexpr unsigned int mode() { return 0x0018; } static constexpr unsigned int power() { return 0x0019; } static constexpr unsigned int latitude0() { return 0x001a; } static constexpr unsigned int latitude1() { return 0x001c; } static constexpr unsigned int latitude2() { return 0x001d; } static constexpr unsigned int longitude0() { return 0x001e; } static constexpr unsigned int longitude1() { return 0x001f; } static constexpr unsigned int rxTone() { return 0x0020; } static constexpr unsigned int txTone() { return 0x0022; } static constexpr unsigned int longitude2() { return 0x0024; } static constexpr Bit simplex() { return {0x0026, 2}; } static constexpr Bit useFixedLocation() { return {0x0026, 3}; } static constexpr Bit disablePowerSave() { return {0x0026, 5}; } static constexpr Bit disableBeep() { return {0x0026, 6}; } static constexpr Bit overrideDMRID() { return {0x0026, 7}; } static constexpr unsigned int dmrId() { return 0x0027; } static constexpr unsigned int groupList() { return 0x002b; } static constexpr unsigned int colorCode() { return 0x002c; } static constexpr unsigned int aprsIndex() { return 0x002d; } static constexpr unsigned int txContact() { return 0x002e; } static constexpr Bit aliasTimeSlot2() { return { 0x030, 2}; } static constexpr Bit aliasTimeSlot1() { return { 0x030, 0}; } static constexpr Bit timeSlot() { return {0x0031, 6}; } static constexpr Bit bandwidth() { return {0x0033, 1}; } static constexpr Bit rxOnly() { return {0x0033, 2}; } static constexpr Bit skipScan() { return {0x0033, 4}; } static constexpr Bit skipZoneScan() { return {0x0033, 5}; } static constexpr Bit vox() { return {0x0033, 6}; } static constexpr unsigned int squelch() { return 0x0037; } /// @endcond }; }; /** Implements the base for channel banks in Radioddity codeplugs. * * Memory layout of a channel bank: * @verbinclude radioddity_channelbank.txt */ class ChannelBankElement: public Element { protected: /** Hidden constructor. */ ChannelBankElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ChannelBankElement(uint8_t *ptr); /** Destructor. */ virtual ~ChannelBankElement(); /** The size of the channel bank. */ static constexpr unsigned int size() { return 0x1c10; } /** Clears the bank. */ void clear(); /** Returns @c true if the channel is enabled. */ virtual bool isEnabled(unsigned idx) const ; /** Enable/disable a channel in the bank. */ virtual void enable(unsigned idx, bool enabled); /** Returns a pointer to the channel at the given index. */ virtual uint8_t *get(unsigned idx) const; /** Retunrs the n-th channel. */ ChannelElement channel(unsigned int n); public: /** Some limits for the channel bank. */ struct Limit { /** The maximum number of channels. */ static constexpr unsigned int channelCount() { return 128; } }; protected: /** Some internal offset within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int bitmask() { return 0x0000; } static constexpr unsigned int channels() { return 0x0010; } /// @endcond }; }; /** VFO Channel representation within the binary codeplug. * * Each channel requires 0x38b: * @verbinclude radioddity_vfochannel.txt */ class VFOChannelElement: public ChannelElement { public: /** Possible offset frequency modes. */ enum class OffsetMode { Off = 0, ///< Disables transmit frequency offset. Positive = 1, ///< Transmit offset frequency is positive (TX above RX). Negative = 2 ///< Transmit offset frequency is negative (TX below RX). }; /** Possible tuning step sizes. */ enum class StepSize { SS2_5kHz = 0, ///< 2.5kHz SS5kHz = 1, ///< 5kHz SS6_25kHz = 2, ///< 6.25kHz SS10kHz = 3, ///< 10kHz SS12_5kHz = 4, ///< 12.5kHz SS20kHz = 5, ///< 20kHz SS30kHz = 6, ///< 30kHz SS50kHz = 7 ///< 50kHz }; protected: /** Hidden constructor. */ VFOChannelElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit VFOChannelElement(uint8_t *ptr); void clear(); /** The VFO channel has no name. */ QString name() const; /** The VFO channel has no name. */ void setName(const QString &name); /** Returns the tuning step-size in kHz. */ virtual double stepSize() const; /** Sets the tuning step-size in kHz. */ virtual void setStepSize(double kHz); /** Returns the transmit frequency offset mode. */ virtual OffsetMode offsetMode() const; /** Returns the transmit frequency offset. */ virtual double txOffset() const; /** Sets the transmit frequency offset in MHz. */ virtual void setTXOffset(double f); /** Sets the transmit frequency offset mode. */ virtual void setOffsetMode(OffsetMode mode); protected: /// @cond DO_NOT_DOCUMENT struct Offset: public ChannelElement::Offset { static constexpr Bit stepSize() { return {0x0036, 4} ; } static constexpr Bit offsetMode() { return {0x0036, 2} ; } static constexpr unsigned int txOffset() { return 0x0034; } }; /// @endcond }; /** Encodes the settings element for all OpenGD77 codeplugs. */ class GeneralSettingsElement: public Element { protected: /** Hidden constructor. */ GeneralSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ GeneralSettingsElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0090; } void clear(); /** Returns the UHF minimum frequency. */ virtual Frequency uhfMinFrequency() const; /** Sets the UHF minimum frequency. */ virtual void setUHFMinFrequency(const Frequency &f); /** Returns the UHF maximum frequency. */ virtual Frequency uhfMaxFrequency() const; /** Sets the UHF maximum frequency. */ virtual void setUHFMaxFrequency(const Frequency &f); /** Returns the VHF minimum frequency. */ virtual Frequency vhfMinFrequency() const; /** Sets the VHF minimum frequency. */ virtual void setVHFMinFrequency(const Frequency &f); /** Returns the VHF maximum frequency. */ virtual Frequency vhfMaxFrequency() const; /** Sets the VHF maximum frequency. */ virtual void setVHFMaxFrequency(const Frequency &f); /** Returns the radio callsign. */ virtual QString call() const; /** Sets the radio callsign. */ virtual void setCall(const QString &call); /** Returns the DMR ID. */ virtual unsigned int radioId() const; /** Sets the DMR ID. */ virtual void setRadioId(unsigned int id); /** Encodes the settings. */ virtual bool encode(const Context &ctx, const ErrorStack &err = ErrorStack()); /** Decodes the settings. */ virtual bool decode(const Context &ctx, const ErrorStack &err = ErrorStack()); public: /** Some limits. */ struct Limit: public Element::Limit { /** The maximum call length. */ static constexpr unsigned int callLength() { return 8; } }; protected: /** Some internal offset within the element. */ struct Offset: public Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int uhfMinFrequency() { return 0x0000; } static constexpr unsigned int uhfMaxFrequency() { return 0x0002; } static constexpr unsigned int vhfMinFrequency() { return 0x0004; } static constexpr unsigned int vhfMaxFrequency() { return 0x0006; } static constexpr unsigned int call() { return 0x0060; } static constexpr unsigned int dmrId() { return 0x0068; } /// @endcond }; }; /** APRS system for OpenGD77 devices. */ class APRSSettingsElement: public Element { public: /** Possible APRS baud rates. */ enum class BaudRate { Baud300 = 1, Baud1200 = 0 }; /** Possible position precisions. */ enum class PositionPrecision { Max = 0, Mask1_8sec = 1, Mask3_6sec = 2, Mask18sec = 3, Mask36sec = 4, Mask3min = 5, Mask6min = 6, Mask30min = 7 }; public: /** Constructor from pointer. */ explicit APRSSettingsElement(uint8_t *ptr); protected: /** Hidden constructor. */ APRSSettingsElement(uint8_t *ptr, size_t size); public: /** The size of the channel bank. */ static constexpr unsigned int size() { return 0x40; } /** Clears the bank. */ void clear(); /** Returns @c true, if the system is valid. */ virtual bool isValid() const; /** Returns the name of the system. */ virtual QString name() const; /** Sets the name of the system. */ virtual void setName(const QString &name); /** Returns the source SSID. */ virtual unsigned int sourceSSID() const; /** Sets the source SSID. */ virtual void setSourceSSID(unsigned int ssid); /** Returns @c true, if a fixed position is send. */ virtual bool hasFixedPosition() const; /** Returns the fixed position. */ virtual QGeoCoordinate fixedPosition() const; /** Sets the fixed position. */ virtual void setFixedPosition(const QGeoCoordinate &coor); /** Resets the fixed position. */ virtual void clearFixedPosition(); /** Retunrs the posiiton reporting precision. */ virtual PositionPrecision positionPrecision() const; /** Sets the position reporting precision in degrees. */ virtual void setPositionPrecision(PositionPrecision prec); /** Returns @c true, if the first via node is set. */ virtual bool hasVia1() const; /** Returns the first via node call. */ virtual QString via1Call() const; /** Returns the first via node ssid. */ virtual unsigned int via1SSID() const; /** Sets the first via node. */ virtual void setVia1(const QString &call, unsigned int ssid); /** Clears the first via node. */ virtual void clearVia1(); /** Returns @c true, if the second via node is set. */ virtual bool hasVia2() const; /** Returns the second via node call. */ virtual QString via2Call() const; /** Returns the second via node ssid. */ virtual unsigned int via2SSID() const; /** Sets the second via node. */ virtual void setVia2(const QString &call, unsigned int ssid); /** Clears the second via node. */ virtual void clearVia2(); /** Retunrs the icon. */ virtual APRSSystem::Icon icon() const; /** Sets the icon. */ virtual void setIcon(APRSSystem::Icon icon); /** Retunrs the comment text. */ virtual QString comment() const; /** Sets the comment text. */ virtual void setComment(const QString &text); /** Retunrs the baud-rate. */ virtual BaudRate baudRate() const; /** Sets the baud rate. */ virtual void setBaudRate(BaudRate rate); Frequency fmFrequency() const; void setFMFrequency(Frequency f); /** Encodes the APRS settings. */ virtual bool encode(const APRSSystem *system, const Context &ctx, const ErrorStack &err=ErrorStack()); /** Decodes some APRS settings. */ virtual APRSSystem *decode(const Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links the ARPS settings. */ virtual bool link(APRSSystem *system, const Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit: public Element::Limit { /** The maximum name length in chars. */ static constexpr unsigned int nameLength() { return 8; } /** The maximum comment length in chars. */ static constexpr unsigned int commentLength() { return 23; } }; protected: /** Some internal offsets within the element. */ struct Offset: public Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int sourceSSID() { return 0x0008; } static constexpr unsigned int latitude() { return 0x0009; } static constexpr unsigned int longitude() { return 0x000c; } static constexpr unsigned int via1Call() { return 0x000f; } static constexpr unsigned int via1SSID() { return 0x0015; } static constexpr unsigned int via2Call() { return 0x0016; } static constexpr unsigned int via2SSID() { return 0x001c; } static constexpr unsigned int iconTable() { return 0x001d; } static constexpr unsigned int iconIndex() { return 0x001e; } static constexpr unsigned int comment() { return 0x001f; } static constexpr unsigned int fmFrequency() { return 0x0037; } static constexpr Bit positionPrecision() { return { 0x003d, 4}; } static constexpr Bit useFixedPosition() { return { 0x003d, 1}; } static constexpr Bit baudRate() { return { 0x003d, 0}; } static constexpr unsigned int unknownBytes() { return 0x003e; } /// @endcond }; }; /** APRS System bank. */ class APRSSettingsBankElement: public Element { public: /** Constructor from pointer. */ explicit APRSSettingsBankElement(uint8_t *ptr); protected: /** Hidden constructor. */ APRSSettingsBankElement(uint8_t *ptr, size_t size); public: /** The size of the channel bank. */ static constexpr unsigned int size() { return 0x40; } /** Clears the bank. */ void clear(); /** Retunrs the n-th APRS system. */ APRSSettingsElement system(unsigned int idx) const; /** Encodes all FM APRS systems. */ virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack()); /** Decodes all FM APRS systems. */ virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()); /** Links all FM APRS systems. */ virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for the bank. */ struct Limit: public Element::Limit { /** The total number of APRS systems. */ static constexpr unsigned int systems() { return 8; } }; public: /// @cond DO_NOT_DOCUMENT struct Offset: public Element::Offset { static constexpr unsigned int systems() { return 0x0000; } static constexpr unsigned int betweenSystems() { return APRSSettingsElement::size(); } }; /// @endcond }; /** DTMF contact element. * Just a name and DTMF number. */ class DTMFContactElement: public Element { protected: /** Hidden constructor. */ DTMFContactElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit DTMFContactElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0020; } void clear(); bool isValid() const; /** Returns the name. */ virtual QString name() const; /** Sets the name. */ virtual void setName(const QString &name); /** Returns the DTMF number. */ virtual QString number() const; /** Sets the DTMF number. */ virtual void setNumber(const QString &number); /** Encodes a number. */ virtual bool encode(const DTMFContact *contact, const Context &ctx, const ErrorStack &err=ErrorStack()); /** Decodes a number. */ virtual DTMFContact *decode(const Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. Ü*/ struct Limit: public Element::Limit { /** The maximum name length. */ static constexpr unsigned int nameLength() { return 16; } /** The maximum length of the number. */ static constexpr unsigned int numberLength() { return 16; } }; protected: /// @cond DO_NOT_DOCUMENT struct Offset: public Element::Offset { static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int number() { return 0x0010; } }; /// @endcond }; /** DTMF contact bank. */ class DTMFContactBankElement: public Element { protected: /** Hidden constructor. */ DTMFContactBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ DTMFContactBankElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return Limit::contacts()*DTMFContactElement::size(); } void clear(); /** Returns the n-th DTMF contact. */ DTMFContactElement contact(unsigned int n) const; /** Encodes all DTMF contacts. */ virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack()); /** Decodes all DTMF contacts. */ virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for the bank. */ struct Limit: public Element::Limit { /** The total number of contacts. */ static constexpr unsigned int contacts() { return 64; } }; public: /// @cond DO_NOT_DOCUMENT struct Offset: public Element::Offset { static constexpr unsigned int contacts() { return 0x0000; } static constexpr unsigned int betweenContacts() { return DTMFContactElement::size(); } }; /// @endcond }; /** Implements the base class of boot settings for all OpenGD77 codeplugs. */ class BootSettingsElement: public Element { protected: /** Hidden constructor. */ BootSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit BootSettingsElement(uint8_t *ptr); /** Destructor. */ virtual ~BootSettingsElement(); /** Resets the settings. */ void clear(); /** Returns @c true if the text is shown on boot, other wise an image is shown. */ virtual bool bootText() const; /** Enables/disables boot text. */ virtual void enableBootText(bool enable); /** Returns @c true if the boot password is enabled. */ virtual bool bootPasswordEnabled() const; /** Returns the boot password (6 digit). */ virtual unsigned bootPassword() const; /** Sets the boot password (6 digit). */ virtual void setBootPassword(unsigned passwd); /** Clear boot password. */ virtual void clearBootPassword(); /** Returns the first line. */ virtual QString line1() const; /** Sets the first line. */ virtual void setLine1(const QString &text); /** Returns the Second line. */ virtual QString line2() const; /** Sets the second line. */ virtual void setLine2(const QString &text); /** Encodes boot text settings from configuration. */ virtual bool encode(const Context &ctx, const ErrorStack &err = ErrorStack()); /** Updates the configuration with the boot text settings. */ virtual bool decode(Context &ctx, const ErrorStack &err = ErrorStack()); public: /** Some limits for the settings. */ struct Limit: public Element::Limit { /** The total number of contacts. */ static constexpr unsigned int lineLength() { return 16; } }; public: /// @cond DO_NOT_DOCUMENT struct Offset: public Element::Offset { static constexpr unsigned int bootText() { return 0x0000; } static constexpr unsigned int bootPasswdEnable() { return 0x0001; } static constexpr unsigned int bootPasswd() { return 0x000c; } static constexpr unsigned int line1() { return 0x0028; } static constexpr unsigned int line2() { return 0x0038; } }; /// @endcond }; /** Represents a zone within OpenGD77 codeplugs. */ class ZoneElement: public Element { protected: /** Hidden constructor. */ ZoneElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ZoneElement(uint8_t *ptr); virtual ~ZoneElement(); /** The size of the zone element. */ static constexpr unsigned int size() { return 0x00b0; } /** Resets the zone. */ void clear(); /** Returns @c true if the zone is valid. */ bool isValid() const; /** Returns the name of the zone. */ virtual QString name() const; /** Sets the name of the zone. */ virtual void setName(const QString &name); /** Returns @c true if a member is stored at the given index. */ virtual bool hasMember(unsigned n) const; /** Returns the n-th member index. */ virtual unsigned member(unsigned n) const; /** Sets the n-th member index. */ virtual void setMember(unsigned n, unsigned idx); /** Clears the n-th member index. */ virtual void clearMember(unsigned n); /** Resets this codeplug zone representation from the given generic @c Zone object. */ virtual bool encode(const Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack()); /** Constructs a generic @c Zone object from this codeplug zone. */ virtual Zone *decode(const Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links a previously constructed @c Zone object to the rest of the configuration. That is * linking to the referred channels. */ virtual bool link(Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack()) const; public: /** Some limits for zone elements. */ struct Limit: public Element::Limit { /** The maximum length of the zone name. */ static constexpr unsigned int nameLength() { return 16; } /** The maximum number of members. */ static constexpr unsigned int memberCount() { return 80; } }; protected: /** Some internal offsets within the element. */ struct Offset: public Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int channels() { return 0x0010; } static constexpr unsigned int betweenChannels() { return 0x0002; } /// @endcond }; }; /** Implements the base class for all zone banks of OpenGD77 codeplugs. */ class ZoneBankElement: public Element { protected: /** Hidden constructor. */ ZoneBankElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ZoneBankElement(uint8_t *ptr); /** Destructor. */ ~ZoneBankElement(); /** The size of the zone element. */ static constexpr unsigned int size() { return 0x0020 + Limit::zoneCount()*ZoneElement::size(); } /** Resets the bank. */ void clear(); /** Returns @c true if the zone is enabled. */ virtual bool isEnabled(unsigned idx) const ; /** Enable/disable a zone in the bank. */ virtual void enable(unsigned idx, bool enabled); /** Retunrs the n-th zone. */ ZoneElement zone(unsigned int n); /** Encodes all zones. */ virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack()); /** Decodes all zones. */ virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()); /** Links all zones. */ virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for the zone bank. */ struct Limit: public Element::Limit { /** The maximum number of zones in this bank. */ static constexpr unsigned int zoneCount() { return 68; } }; protected: /** Some internal offsets within the element. */ struct Offset: public Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int bitmap() { return 0x0000; } static constexpr unsigned int zones() { return 0x0020; } static constexpr unsigned int betweenZones() { return ZoneElement::size(); } /// @endcond }; }; /** Implements digital contacts in OpenGD77 codeplugs. */ class ContactElement: public Element { public: /** Possible values for the time-slot override option. * Encoded values are correct for firmware 2022-02-28 (0118581D) to 2025-03-23 (1bd23ea). */ enum class TimeSlotOverride { None = 0x01, ///< Do not override time-slot of channel. TS1 = 0x00, ///< Force time-slot to TS1. TS2 = 0x02 ///< Force time-slot to TS2. }; protected: /** Hidden constructor. */ ContactElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ContactElement(uint8_t *ptr); /** Destructor. */ virtual ~ContactElement(); /** The size of the contact element. */ static constexpr unsigned int size() { return 0x0018; } /** Resets the contact. */ void clear(); /** Returns @c true if the contact is valid. */ bool isValid() const; /** Returns the name of the contact. */ virtual QString name() const; /** Sets the name of the contact. */ virtual void setName(const QString name); /** Returns the DMR number of the contact. */ virtual unsigned number() const; /** Sets the DMR number of the contact. */ virtual void setNumber(unsigned id); /** Returns the call type. */ virtual DMRContact::Type type() const; /** Sets the call type. */ virtual void setType(DMRContact::Type type); /** Returns the time slot override of the contact. */ virtual TimeSlotOverride timeSlotOverride() const; /** Sets the time slot override. */ virtual void setTimeSlotOverride(TimeSlotOverride ts); /** Constructs a @c DigitalContact instance from this codeplug contact. */ virtual DMRContact *decode(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Resets this codeplug contact from the given @c DigitalContact. */ virtual bool encode(const DMRContact *obj, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for the contact. */ struct Limit { /** Maximum name length. */ static constexpr unsigned int nameLength() { return 16; } }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int number() { return 0x0010; } static constexpr unsigned int type() { return 0x0014; } static constexpr unsigned int timeSlotOverride() { return 0x0017; } /// @endcond }; }; /** Encodes the contact bank. */ class ContactBankElement: public Element { protected: /** Hidden constructor. */ ContactBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ContactBankElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return Limit::contactCount() * ContactElement::size(); } /** Resets the contact. */ void clear(); /** Returns the i-th contact element. */ ContactElement contact(unsigned int idx) const; /** Encodes all DMR contacts. */ virtual bool encode(Context &ctx, const ErrorStack &err = ErrorStack()); /** Decodes all DMR contacts. */ virtual bool decode(Context &ctx, const ErrorStack &err = ErrorStack()); public: /** Some limits for the element. */ struct Limit { /** Maximum number of contacts. */ static constexpr unsigned int contactCount() { return 1024; } }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int contacts() { return 0x0000; } static constexpr unsigned int betweenContacts() { return ContactElement::size(); } // @endcond }; }; /** Encodes a group list for all OpenGD77 codeplugs. */ class GroupListElement: public Element { protected: /** Hidden constructor. */ GroupListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ GroupListElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0050; } /** Clears the group list. */ void clear(); /** Returns the name of the group list. */ virtual QString name() const; /** Sets the name of the group list. */ virtual void setName(const QString &name); /** Returns @c true, if the i-th contact is set. */ virtual bool hasContactIndex(unsigned int i) const; /** Returns the i-th contact index. */ virtual unsigned int contactIndex(unsigned int i) const; /** Sets the i-th contact index. */ virtual void setContactIndex(unsigned int i, unsigned int contactIdx); /** Clears the i-th contact index. */ virtual void clearContactIndex(unsigned int i); /** Encodes group list element. */ virtual bool encode(RXGroupList *lst, Context &ctx, const ErrorStack &err=ErrorStack()); /** Decodes group list element. */ virtual RXGroupList *decode(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links the group list element. */ virtual bool link(RXGroupList *lst, Context &ctx, const ErrorStack &err=ErrorStack()) const; public: /** Some limits for the element. */ struct Limit { /** Maximum name length. */ static constexpr unsigned int nameLength() { return 15; } /** Maximum number of contacts. */ static constexpr unsigned int contactCount() { return 32; } }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int contacts() { return 0x0010; } static constexpr unsigned int betweenContacts() { return 0x0002; } // @endcond }; }; /** Encodes a group list bank for all OpenGD77 codeplugs. */ class GroupListBankElement: public Element { protected: /** Hidden constructor. */ GroupListBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ GroupListBankElement(uint8_t *ptr); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x1840; } /** Clears the group list bank. */ void clear(); /** Returns @c true, if the i-th group list is encoded. */ virtual bool hasGroupList(unsigned int i) const; /** Returns the number of contacts in the given group list. */ virtual unsigned int groupListContactCount(unsigned int i) const; /** Sets the number of contacts in the given group list. */ virtual void setGroupListContactCount(unsigned int i, unsigned int count); /** Returns the i-th group list. */ virtual GroupListElement groupList(unsigned int i) const; /** Clears the i-th group list. */ virtual void clearGroupList(unsigned int i); /** Encodes all group lists. */ virtual bool encode(Context &ctx, const ErrorStack &err = ErrorStack()); /** Decodes all group lists. */ virtual bool decode(Context &ctx, const ErrorStack &err = ErrorStack()); /** Links all group lists. */ virtual bool link(Context &ctx, const ErrorStack &err = ErrorStack()); public: /** Some limits for the element. */ struct Limit { /** Maximum number of group lists. */ static constexpr unsigned int groupListCount() { return 76; } }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int length() { return 0x0000; } static constexpr unsigned int groupLists() { return 0x0080; } static constexpr unsigned int betweenGroupLists() { return GroupListElement::size(); } // @endcond }; }; protected: /** Default hidden constructor. */ explicit OpenGD77BaseCodeplug(QObject *parent = nullptr); public: /** Clears and resets the complete codeplug to some default values. */ virtual void clear(); bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const; bool decode(Config *config, const ErrorStack &err=ErrorStack()); bool postprocess(Config *config, const ErrorStack &err=ErrorStack()) const; Config *preprocess(Config *config, const ErrorStack &err=ErrorStack()) const; bool encode(Config *config, const Flags &flags = Flags(), const ErrorStack &err=ErrorStack()); public: /** Decodes the binary codeplug and stores its content in the given generic configuration using * the given context. */ virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the given generic configuration as a binary codeplug using the given context. */ virtual bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Clears the general settings in the codeplug. */ virtual void clearGeneralSettings() = 0; /** Updates the general settings from the given configuration. */ virtual bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Updates the given configuration from the general settings. */ virtual bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears the DTMF settings. */ virtual void clearDTMFSettings() = 0; /** Encodes DTMF settings. */ virtual bool encodeDTMFSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Decodes the DTMF settings. */ virtual bool decodeDTMFSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears the APRS settings. */ virtual void clearAPRSSettings() = 0; /** Encodes APRS settings. */ virtual bool encodeAPRSSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Decodes the APRS settings. */ virtual bool decodeAPRSSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links the APRS settings. */ virtual bool linkAPRSSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all DTMF contacts in the codeplug. */ virtual void clearDTMFContacts() = 0; /** Encodes all DTMF contacts. */ virtual bool encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds all DTMF contacts to the configuration. */ virtual bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clear all channels. */ virtual void clearChannels() = 0; /** Encode all channels. */ virtual bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds all defined channels to the configuration. */ virtual bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links all channels. */ virtual bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clear boot settings. */ virtual void clearBootSettings() = 0; /** Encodes boot settings. */ virtual bool encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Decodes the boot settings. */ virtual bool decodeBootSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears the VFO settings. */ virtual void clearVFOSettings() = 0; /** Clears all zones. */ virtual void clearZones() = 0; /** Encodes zones. */ virtual bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds zones to the configuration. */ virtual bool createZones(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links all zones within the configuration. */ virtual bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all contacts in the codeplug. */ virtual void clearContacts() = 0; /** Encodes all digital contacts in the configuration into the codeplug. */ virtual bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds a digital contact to the configuration for each one in the codeplug. */ virtual bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all group lists. */ virtual void clearGroupLists() = 0; /** Encodes all group lists. */ virtual bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Creates all group lists. */ virtual bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links all group lists. */ virtual bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; }; #endif // OPENGD77BASE_CODEPLUG_HH qdmr-0.12.3/lib/opengd77base_satelliteconfig.cc000066400000000000000000000171451501654372000213510ustar00rootroot00000000000000#include "opengd77base_satelliteconfig.hh" #include "satellitedatabase.hh" /* ********************************************************************************************* * * Implementation of OpenGD77BaseSatelliteConfig::SatelliteElement * ********************************************************************************************* */ OpenGD77BaseSatelliteConfig::SatelliteElement::SatelliteElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } OpenGD77BaseSatelliteConfig::SatelliteElement::SatelliteElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } void OpenGD77BaseSatelliteConfig::SatelliteElement::clear() { memset(_data, 0, size()); } void OpenGD77BaseSatelliteConfig::SatelliteElement::writeDigit(const Offset::Bit &offset, uint8_t digit) { // Must be bit 0 or 3 (BCD) if (offset.bit % 4) return; uint8_t val = getUInt8(offset.byte); val &= ~(0xf << offset.bit); val |= ((digit & 0xf) << offset.bit); setUInt8(offset.byte, val); } void OpenGD77BaseSatelliteConfig::SatelliteElement::writeInteger(const Offset::Bit &offset, int value, bool sign, unsigned int dec) { unsigned int o = 0; // Must be bit 0 or 4 (BCD) if (offset.bit % 4) return; if (0 == dec) return; if (sign && 0 > value) writeDigit(offset + o, 0xc); // '-' ?!? else writeDigit(offset + o, 0xb); // blank o += 4*(dec-1); for (int i=dec; i>0; i--, o = o - 4) { writeDigit(offset + o, value % 10); value /= 10; } } void OpenGD77BaseSatelliteConfig::SatelliteElement::writeFractional(const Offset::Bit &offset, double value, bool sign, unsigned int frac) { unsigned int o = 0; if (offset.bit % 4) return; if (0 == frac) return; if (sign) { if (0 > value) writeDigit(offset + o, 0xc); else writeDigit(offset + o, 0xb); o += 4; } value -= int(value); for (unsigned int i=0; icount() <= i) continue; if (! el.encode(db->getAt(i), err)) { errMsg(err) << "Cannot encode satellite '" << db->getAt(i).name() << "' at index " << i << "."; return false; } } return true; } /* ********************************************************************************************* * * Implementation of OpenGD77BaseSatelliteConfig * ********************************************************************************************* */ OpenGD77BaseSatelliteConfig::OpenGD77BaseSatelliteConfig(QObject *parent) : SatelliteConfig{parent} { addImage("OpenGD77 satellite configuration EEPROM"); addImage("OpenGD77 satellite configuration FLASH"); } qdmr-0.12.3/lib/opengd77base_satelliteconfig.hh000066400000000000000000000163061501654372000213610ustar00rootroot00000000000000#ifndef OPENGD77BASE_SATELLITECONFIG_HH #define OPENGD77BASE_SATELLITECONFIG_HH #include #include #include #include #include #include /** Implements the satellite tracking configuration for the OpenGD77 type radios. * @ingroup ogd77 */ class OpenGD77BaseSatelliteConfig : public SatelliteConfig { Q_OBJECT public: /** Possible image types. */ enum ImageType { EEPROM = 0, FLASH = 1 }; public: /** Encodes a satellite for the OpenGD77 devices. * That is a set of orbital elements and transponder information. */ class SatelliteElement: public Codeplug::Element { protected: /** Hidden constructor. */ SatelliteElement(uint8_t *ptr, size_t size); public: /** Constructor. */ SatelliteElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x0064; } void clear(); /** Sets the name of the element. */ virtual void setName(const QString &name); /** Sets the epoch. */ virtual void setEpoch(const ::OrbitalElement::Epoch &epoch); /** Sets the first derivative of mean motion. */ virtual void setMeanMotion(double mm); /** Sets the first derivative of mean motion. */ virtual void setMeanMotionDerivative(double dmm); /** Sets the inclination. */ virtual void setInclination(double incl); /** Right ascension of the ascending node. */ virtual void setAscension(double asc); /** Sets eccentricity. */ virtual void setEccentricity(double ecc); /** Sets argument of perigee. */ virtual void setPerigee(double arg); /** Set the mean anomaly. */ virtual void setMeanAnomaly(double ma); /** Sets the revolution number at epoch. */ virtual void setRevolutionNumber(unsigned int num); /** Sets the downlink frequency. */ void setFMDownlink(const Frequency &f); /** Sets the uplink frequency. */ void setFMUplink(const Frequency &f); /** Sets the CTCSS tone. */ void setCTCSS(const SelectiveCall &call); /** Sets the APRS downlink frequency. */ void setAPRSDownlink(const Frequency &f); /** Sets the APRS uplink frequency. */ void setAPRSUplink(const Frequency &f); /** Sets the beacon frequency. */ void setBeacon(const Frequency &f); /** Sets the APRS path. */ void setAPRSPath(const QString &path); /** Encodes a satellite. */ virtual bool encode(const Satellite &sat, const ErrorStack &err = ErrorStack()); protected: /** Writes a fixed point value as a BCD number. Using 0-9 as digits, ah as decimal dot and bh * as blank. * @param offset Specifies, where to write the fixed point value. * @param value The value to write. * @param sign If @c true, a sign is written. * @param dec The number of digits in the integer part. * @param frac The number of digits in the fractional part. */ void writeFixedPoint(const Offset::Bit &offset, double value, bool sign, unsigned int dec, unsigned int frac); /** Writes a fixed point value as a BCD number. Using 0-9 as digits and bh as blank. In contrast * to @c writeFixedPoint, this function expects no integer part. * @param offset Specifies, where to write the fixed point value. * @param value The value to write. * @param sign If @c true, a sign is written. * @param frac The number of digits in the fractional part. */ void writeFractional(const Offset::Bit &offset, double value, bool sign, unsigned int frac); /** Write a fixed digit integer value. */ void writeInteger(const Offset::Bit &offset, int value, bool sign, unsigned dec); /** Writes a single digit at the given offset. */ void writeDigit(const Offset::Bit &offset, uint8_t digit); public: /** Some limits for the zone bank. */ struct Limit: public Element::Limit { /** The maximum name length. */ static constexpr unsigned int nameLength() { return 8; } /** Maximum length of the APRS path. */ static constexpr unsigned int pathLength() { return 24; } }; protected: /** Some internal offsets within the element. */ struct Offset: public Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr Bit epochYear() { return {0x0008, 4}; } static constexpr Bit epochJulienDay() { return {0x0009, 4}; } static constexpr Bit meanMotionDerivative() { return {0x000f, 4}; } static constexpr Bit inclination() { return {0x0014, 4}; } static constexpr Bit ascension() { return {0x0018, 4}; } static constexpr Bit eccentricity() { return {0x001c, 4}; } static constexpr Bit perigee() { return {0x001f, 0}; } static constexpr Bit meanAnomaly() { return {0x0023, 0}; } static constexpr Bit meanMotion() { return {0x0027, 0}; } static constexpr Bit revolutionNumber() { return {0x002d, 4}; } static constexpr unsigned int fmDownlink() { return 0x0030; } static constexpr unsigned int fmUplink() { return 0x0034; } static constexpr unsigned int ctcss() { return 0x0038; } static constexpr unsigned int aprsDownlink() { return 0x003c; } static constexpr unsigned int aprsUplink() { return 0x0040; } static constexpr unsigned int beacon() { return 0x0044; } static constexpr unsigned int aprsPath() { return 0x004c; } /// @endcond }; }; /** Implements the satellite config bank. Holding all satellites to track. */ class SatelliteBankElement: Codeplug::Element { protected: /** Hidden constructor. */ SatelliteBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ SatelliteBankElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x09d8; } void clear(); /** Returns the i-th satellite. */ SatelliteElement satellite(unsigned int idx); /** Encodes the given satellite database. */ bool encode(SatelliteDatabase *db, const ErrorStack &err=ErrorStack()); public: /** Some limits for the satellite config. */ struct Limit { /** The maximum number of satellites. */ static constexpr unsigned int satellites() { return 25; } }; protected: /** Some internal offsets. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int label() { return 0x0000; } static constexpr unsigned int unknownInteger0() { return 0x0008; } static constexpr unsigned int unknownInteger1() { return 0x000c; } static constexpr unsigned int segmentSize() { return 0x0010; } static constexpr unsigned int satellites() { return 0x0014; } static constexpr unsigned int betweenSatellites() { return SatelliteElement::size(); } /// @endcond }; }; public: /** Default constructor. */ explicit OpenGD77BaseSatelliteConfig(QObject *parent = nullptr); /** Encodes the given satellite database. */ virtual bool encode(SatelliteDatabase *db, const ErrorStack &err=ErrorStack()) = 0; }; #endif // OPENGD77BASE_SATELLITECONFIG_HH qdmr-0.12.3/lib/openrtx.cc000066400000000000000000000164341501654372000153270ustar00rootroot00000000000000#include "openrtx.hh" #include "openrtx_interface.hh" #include "logger.hh" #include "config.hh" #define BSIZE 32 OpenRTX::OpenRTX(OpenRTXInterface *device, QObject *parent) : Radio(parent), _name("Open RTX"), _dev(device), _config(nullptr), _codeplug() { if (! connect()) return; logDebug() << "Connected to radio '" << _name << "'."; } OpenRTX::~OpenRTX() { if (_dev && _dev->isOpen()) { logDebug() << "Closing device."; _dev->reboot(); _dev->close(); } if (_dev) { logDebug() << "Deleting device."; _dev->deleteLater(); _dev = nullptr; } } const QString & OpenRTX::name() const { return _name; } const Codeplug & OpenRTX::codeplug() const { return _codeplug; } Codeplug & OpenRTX::codeplug() { return _codeplug; } RadioInfo OpenRTX::defaultRadioInfo() { return RadioInfo( RadioInfo::OpenRTX, "openrtx", "OpenRTX", USBDeviceInfo()); } bool OpenRTX::startDownload(bool blocking, const ErrorStack &err) { if (StatusIdle != _task) { errMsg(err) << "Cannot download from radio, radio is not idle."; return false; } _task = StatusDownload; if (blocking) { run(); return (StatusIdle == _task); } // If non-blocking -> move device to this thread if (_dev && _dev->isOpen()) _dev->moveToThread(this); // start thread for download start(); return true; } bool OpenRTX::startUpload(Config *config, bool blocking, const Codeplug::Flags &flags, const ErrorStack &err) { Q_UNUSED(flags) logDebug() << "Start upload to " << name() << "..."; if (StatusIdle != _task) { errMsg(err) << "Cannot upload to radio, radio is not idle."; return false; } if (_config) delete _config; if (! (_config = config)) { errMsg(err) << "Cannot upload to radio, no config given."; return false; } _config->setParent(this); _task = StatusUpload; if (blocking) { run(); return (StatusIdle == _task); } // If non-blocking -> move device to this thread if (_dev && _dev->isOpen()) _dev->moveToThread(this); // start thread for upload start(); return true; } bool OpenRTX::startUploadCallsignDB(UserDatabase *db, bool blocking, const CallsignDB::Selection &selection,const ErrorStack &err) { Q_UNUSED(db); Q_UNUSED(blocking); Q_UNUSED(selection) errMsg(err) << "OpenRTX has no call-sign DB implemented."; return false; } void OpenRTX::run() { if (StatusDownload == _task) { if (! connect()) { emit downloadError(this); return; } if (! download()) { _task = StatusError; _dev->read_finish(); _dev->reboot(); _dev->close(); emit downloadError(this); return; } _dev->read_finish(); _dev->reboot(); _dev->close(); _task = StatusIdle; emit downloadFinished(this, &_codeplug); _config = nullptr; } else if (StatusUpload == _task) { if (! connect()) { emit uploadError(this); return; } if (! upload()) { _task = StatusError; _dev->write_finish(); _dev->reboot(); _dev->close(); emit uploadError(this); return; } _dev->write_finish(); _dev->reboot(); _dev->close(); _task = StatusIdle; emit uploadComplete(this); } else if (StatusUploadCallsigns == _task) { emit uploadError(this); return; } } bool OpenRTX::connect(const ErrorStack &err) { if (_dev && _dev->isOpen()) return true; if (_dev) _dev->deleteLater(); _dev = new OpenRTXInterface(USBDeviceDescriptor()); if (! _dev->isOpen()) { _task = StatusError; errMsg(err) << "Cannot connect to radio."; _dev->deleteLater(); _dev = nullptr; return false; } return true; } bool OpenRTX::download(const ErrorStack &err) { emit downloadStarted(); if (_codeplug.numImages() != 2) { errMsg(err) << "Cannot download codeplug: Codeplug does not contain two images."; return false; } // Check every segment in the codeplug if (! _codeplug.isAligned(BSIZE)) { errMsg(err) << "Cannot download codeplug: Codeplug is not aligned with blocksize " << BSIZE << "."; return false; } size_t totb = _codeplug.memSize(); if (! _dev->read_start(0, 0, err)) { errMsg(err) << "Cannot start codeplug download."; _dev->close(); return false; } // Then download codeplug size_t bcount = 0; for (int image=0; image<_codeplug.numImages(); image++) { uint32_t bank = 0; for (int n=0; n<_codeplug.image(image).numElements(); n++) { unsigned addr = _codeplug.image(image).element(n).address(); unsigned size = _codeplug.image(image).element(n).data().size(); unsigned b0 = addr/BSIZE, nb = size/BSIZE; for (unsigned b=0; bread(bank, (b0+b)*BSIZE, _codeplug.data((b0+b)*BSIZE, image), BSIZE, err)) { errMsg(err) << "Cannot read block "<< (b0+b) <<"."; return false; } QThread::usleep(100); emit downloadProgress(float(bcount*100)/totb); } } _dev->read_finish(err); } return true; } bool OpenRTX::upload(const ErrorStack &err) { emit uploadStarted(); if (_codeplug.numImages() != 2) { errMsg(err) << "Cannot download codeplug: Codeplug does not contain two images."; return false; } // Check every segment in the codeplug if (! _codeplug.isAligned(BSIZE)) { errMsg(err) << "Cannot upload code-plug: Codeplug is not aligned with blocksize " << BSIZE << "."; return false; } size_t totb = _codeplug.memSize(); if (! _dev->read_start(0, 0, err)) { errMsg(err) << "Cannot start codeplug download."; return false; } // Then download codeplug size_t bcount = 0; for (int image=0; image<_codeplug.numImages(); image++) { uint32_t bank = 0; for (int n=0; n<_codeplug.image(image).numElements(); n++) { unsigned addr = _codeplug.image(image).element(n).address(); unsigned size = _codeplug.image(image).element(n).data().size(); unsigned b0 = addr/BSIZE, nb = size/BSIZE; for (unsigned b=0; bread(bank, (b0+b)*BSIZE, _codeplug.data((b0+b)*BSIZE, image), BSIZE, err)) { errMsg(err) << "Cannot read block " << (b0+b) << "."; return false; } QThread::usleep(100); emit uploadProgress(float(bcount*50)/totb); } } _dev->read_finish(err); } // Encode config into codeplug _codeplug.encode(_config, Codeplug::Flags(), err); if (! _dev->write_start(0,0, err)) { errMsg(err) << "Cannot start codeplug upload."; return false; } // Then upload codeplug for (int image=0; image<_codeplug.numImages(); image++) { uint32_t bank = 0; for (int n=0; n<_codeplug.image(image).numElements(); n++) { unsigned addr = _codeplug.image(image).element(n).address(); unsigned size = _codeplug.image(image).element(n).data().size(); unsigned b0 = addr/BSIZE, nb = size/BSIZE; for (unsigned b=0; bwrite(bank, (b0+b)*BSIZE, _codeplug.data((b0+b)*BSIZE, image), BSIZE, err)) { errMsg(err) << "Cannot write block " << (b0+b) << "."; return false; } QThread::usleep(100); emit uploadProgress(float(bcount*50)/totb); } } _dev->write_finish(err); } return true; } qdmr-0.12.3/lib/openrtx.hh000066400000000000000000000045751501654372000153440ustar00rootroot00000000000000/** @defgroup ortx Open RTX Firmware * Implements a radio running the Open RTX firmware. * @ingroup dsc */ #ifndef OPENGRTX_HH #define OPENGRTX_HH #include "radio.hh" //#include "openrtx_interface.hh" #include "openrtx_codeplug.hh" class OpenRTXInterface; /** Implements an USB interface to radios running the Open RTX firmware. * * @ingroup ortx */ class OpenRTX: public Radio { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit OpenRTX(OpenRTXInterface *device=nullptr, QObject *parent=nullptr); virtual ~OpenRTX(); const QString &name() const; const Codeplug &codeplug() const; Codeplug &codeplug(); /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); public slots: /** Starts the download of the codeplug and derives the generic configuration from it. */ bool startDownload(bool blocking=false, const ErrorStack &err=ErrorStack()); /** Derives the device-specific codeplug from the generic configuration and uploads that * codeplug to the radio. */ bool startUpload(Config *config, bool blocking=false, const Codeplug::Flags &flags = Codeplug::Flags(), const ErrorStack &err=ErrorStack()); /** Encodes the given user-database and uploades it to the device. */ bool startUploadCallsignDB(UserDatabase *db, bool blocking=false, const CallsignDB::Selection &selection=CallsignDB::Selection(), const ErrorStack &err=ErrorStack()); protected: /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */ void run(); /** Connects to the radio, if a radio interface is passed to the constructor, this interface * instance is used. */ bool connect(const ErrorStack &err=ErrorStack()); /** Implements the actual download process. */ bool download(const ErrorStack &err=ErrorStack()); /** Implements the actual codeplug upload process. */ bool upload(const ErrorStack &err=ErrorStack()); protected: /** The device identifier. */ QString _name; /** The interface to the radio. */ OpenRTXInterface *_dev; /** The generic configuration. */ Config *_config; /** The actual binary codeplug representation. */ OpenRTXCodeplug _codeplug; }; #endif // OPENGD77_HH qdmr-0.12.3/lib/openrtx_codeplug.cc000066400000000000000000001040321501654372000172010ustar00rootroot00000000000000#include "openrtx_codeplug.hh" #include "utils.hh" #include "logger.hh" #include "scanlist.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "zone.hh" #include "config.hh" #include "config.h" #include QVector _openrtx_ctcss_tone_table{ 670, 693, 719, 744, 770, 797, 825, 854, 885, 915, 948, 974, 1000, 1034, 1072, 1109, 1148, 1188, 1230, 1273, 1318, 1365, 1413, 1462, 1514, 1567, 1598, 1622, 1655, 1679, 1713, 1738, 1773, 1799, 1835, 1862, 1899, 1928, 1966, 1995, 2035, 2065, 2107, 2181, 2257, 2291, 2336, 2418, 2503, 2541 }; /* ********************************************************************************************* * * Implementation of OpenRTXCodeplug::HeaderElement * ********************************************************************************************* */ OpenRTXCodeplug::HeaderElement::HeaderElement(uint8_t *ptr, size_t size) : Codeplug::Element(ptr, size) { // pass... } OpenRTXCodeplug::HeaderElement::HeaderElement(uint8_t *ptr) : Codeplug::Element(ptr, 0x0058) { // pass... } void OpenRTXCodeplug::HeaderElement::clear() { memset(_data, 0, _size); setUInt64_le(OffsetMagic, MagicNumber); setVersion(); setTimestamp(); } bool OpenRTXCodeplug::HeaderElement::isValid() const { return Codeplug::Element::isValid() && (MagicNumber == getUInt64_le(OffsetMagic)); } uint16_t OpenRTXCodeplug::HeaderElement::version() const { return getUInt16_le(OffsetVersion); } void OpenRTXCodeplug::HeaderElement::setVersion() { setUInt16_le(OffsetVersion, SupportedVersion); } QString OpenRTXCodeplug::HeaderElement::author() const { return readASCII(OffsetAuthor, StringLength, 0); } void OpenRTXCodeplug::HeaderElement::setAuthor(const QString &name) { writeASCII(OffsetAuthor, name, StringLength, 0); } QString OpenRTXCodeplug::HeaderElement::description() const { return readASCII(OffsetDescription, StringLength, 0); } void OpenRTXCodeplug::HeaderElement::setDescription(const QString description) { writeASCII(OffsetDescription, description, StringLength, 0); } QDateTime OpenRTXCodeplug::HeaderElement::timestamp() const { return QDateTime::fromSecsSinceEpoch(getUInt64_le(OffsetTimestamp), Qt::UTC); } void OpenRTXCodeplug::HeaderElement::setTimestamp(const QDateTime timestamp) { setUInt64_le(OffsetTimestamp, timestamp.toUTC().toSecsSinceEpoch()); } unsigned int OpenRTXCodeplug::HeaderElement::contactCount() const { return getUInt16_le(OffsetContactCount); } void OpenRTXCodeplug::HeaderElement::setContactCount(unsigned int n) { setUInt16_le(OffsetContactCount, n); } unsigned int OpenRTXCodeplug::HeaderElement::channelCount() const { return getUInt16_le(OffsetChannelCount); } void OpenRTXCodeplug::HeaderElement::setChannelCount(unsigned int n) { setUInt16_le(OffsetChannelCount, n); } unsigned int OpenRTXCodeplug::HeaderElement::zoneCount() const { return getUInt16_le(OffsetZoneCount); } void OpenRTXCodeplug::HeaderElement::setZoneCount(unsigned int n) { return setUInt16_le(OffsetZoneCount, n); } /* ********************************************************************************************* * * Implementation of OpenRTXCodeplug::ChannelElement * ********************************************************************************************* */ OpenRTXCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } OpenRTXCodeplug::ChannelElement::ChannelElement(uint8_t *ptr) : Element(ptr, 0x5a) { // pass... } OpenRTXCodeplug::ChannelElement::~ChannelElement() { // pass... } bool OpenRTXCodeplug::ChannelElement::isValid() const { return Codeplug::Element::isValid() && (Mode_None != mode()); } void OpenRTXCodeplug::ChannelElement::clear() { memset(_data, 0, _size); } OpenRTXCodeplug::Mode OpenRTXCodeplug::ChannelElement::mode() const { return (OpenRTXCodeplug::Mode)getUInt8(OffsetMode); } void OpenRTXCodeplug::ChannelElement::setMode(Mode mode) { setUInt8(Offsets::OffsetMode, (uint8_t)mode); } bool OpenRTXCodeplug::ChannelElement::rxOnly() const { return getBit(OffsetRXOnly, BitRXOnly); } void OpenRTXCodeplug::ChannelElement::setRXOnly(bool enable) { setBit(OffsetRXOnly, BitRXOnly, enable); } OpenRTXCodeplug::ChannelElement::Bandwidth OpenRTXCodeplug::ChannelElement::bandwidth() const { return (Bandwidth)getUInt2(OffsetBandwidth, BitBandwidth); } void OpenRTXCodeplug::ChannelElement::setBandwidth(Bandwidth bw) { setUInt2(OffsetBandwidth, BitBandwidth, bw); } float OpenRTXCodeplug::ChannelElement::power() const { return 10.+0.2*getUInt8(OffsetPower); } void OpenRTXCodeplug::ChannelElement::setPower(float dBm) { if (dBm < 10) dBm = 10; setUInt8(OffsetPower, (uint8_t)((dBm-10)*5)); } Frequency OpenRTXCodeplug::ChannelElement::rxFrequency() const { return Frequency::fromHz(getUInt32_le(OffsetRXFrequency)); } void OpenRTXCodeplug::ChannelElement::setRXFrequency(Frequency MHz) { setUInt32_le(OffsetRXFrequency, MHz.inHz()); } Frequency OpenRTXCodeplug::ChannelElement::txFrequency() const { return Frequency::fromHz(getUInt32_le(OffsetTXFrequency)); } void OpenRTXCodeplug::ChannelElement::setTXFrequency(Frequency MHz) { setUInt32_le(OffsetTXFrequency, MHz.inHz()); } bool OpenRTXCodeplug::ChannelElement::hasScanListIndex() const { return 0 != scanListIndex(); } unsigned int OpenRTXCodeplug::ChannelElement::scanListIndex() const { return getUInt8(OffsetScanList); } void OpenRTXCodeplug::ChannelElement::setScanListIndex(unsigned int index) { setUInt8(OffsetScanList, index); } void OpenRTXCodeplug::ChannelElement::clearScanListIndex() { setScanListIndex(0); } bool OpenRTXCodeplug::ChannelElement::hasGroupListIndex() const { return 0 != groupListIndex(); } unsigned int OpenRTXCodeplug::ChannelElement::groupListIndex() const { return getUInt8(OffsetGroupList); } void OpenRTXCodeplug::ChannelElement::setGroupListIndex(unsigned int index) { setUInt8(OffsetGroupList, index); } void OpenRTXCodeplug::ChannelElement::clearGroupListIndex() { setGroupListIndex(0); } QString OpenRTXCodeplug::ChannelElement::name() const { return readASCII(OffsetName, StringLength, 0x00); } void OpenRTXCodeplug::ChannelElement::setName(const QString &name) { writeASCII(OffsetName, name, StringLength, 0x00); } QString OpenRTXCodeplug::ChannelElement::description() const { return readASCII(OffsetDescription, StringLength, 0x00); } void OpenRTXCodeplug::ChannelElement::setDescription(const QString &description) { writeASCII(OffsetDescription, description, StringLength, 0x00); } float OpenRTXCodeplug::ChannelElement::latitude() const { return getInt8(OffsetChLatInt) + ((float)getUInt16_le(OffsetChLatDec))/65536; } void OpenRTXCodeplug::ChannelElement::setLatitude(float lat) { setInt8(OffsetChLatInt, (int)lat); setUInt16_le(OffsetChLatDec, std::abs(lat-((int)lat))*65536); } float OpenRTXCodeplug::ChannelElement::longitude() const { return getInt8(OffsetChLonInt) + ((float)getUInt16_le(OffsetChLonDec))/65536; } void OpenRTXCodeplug::ChannelElement::setLongitude(float lon) { setInt8(OffsetChLonInt, (int)lon); setUInt16_le(OffsetChLonDec, std::abs(lon-((int)lon))*65536); } unsigned int OpenRTXCodeplug::ChannelElement::altitude() const { return getUInt16_le(OffsetChAltitude); } void OpenRTXCodeplug::ChannelElement::setAltitude(unsigned int alt) { setUInt16_le(OffsetChAltitude, alt); } SelectiveCall OpenRTXCodeplug::ChannelElement::rxTone() const { if (! getBit(OffsetRXTone, 0)) return SelectiveCall(); int idx = getUInt8(OffsetRXTone)>>1; if (idx >= _openrtx_ctcss_tone_table.size()) return SelectiveCall(); return SelectiveCall(double(_openrtx_ctcss_tone_table[idx])/10); } void OpenRTXCodeplug::ChannelElement::setRXTone(const SelectiveCall &code, const ErrorStack &err) { if (code.isInvalid()) { setBit(OffsetRXTone, 0, false); return; } if (! code.isCTCSS()) { errMsg(err) << "Can only encode CTCSS tones."; setBit(OffsetRXTone, 0, false); return; } if (! _openrtx_ctcss_tone_table.contains((unsigned int)(code.Hz()*10))) { errMsg(err) << "Cannot encode CTCSS frequency " << code.Hz() << "Hz: " << "Not supported."; setBit(OffsetRXTone, 0, false); return; } uint8_t index = _openrtx_ctcss_tone_table.indexOf( (unsigned int)(code.Hz()*10)); setUInt8(OffsetRXTone, (index<<1)|1); } SelectiveCall OpenRTXCodeplug::ChannelElement::txTone() const { if (! getBit(OffsetTXTone, 0)) return SelectiveCall(); int idx = getUInt8(OffsetTXTone)>>1; if (idx >= _openrtx_ctcss_tone_table.size()) return SelectiveCall(); return SelectiveCall(float(_openrtx_ctcss_tone_table[idx])/10); } void OpenRTXCodeplug::ChannelElement::setTXTone(const SelectiveCall &code, const ErrorStack &err) { if (code.isInvalid()) { setBit(OffsetRXTone, 0, false); return; } if (! code.isCTCSS()) { errMsg(err) << "Can only encode CTCSS tones."; setBit(OffsetRXTone, 0, false); return; } if (! _openrtx_ctcss_tone_table.contains((unsigned int)(code.Hz()*10))) { errMsg(err) << "Cannot encode CTCSS frequency " << code.Hz() << "Hz: " << "Not supported."; setBit(OffsetRXTone, 0, false); return; } uint8_t index = _openrtx_ctcss_tone_table.indexOf( (unsigned int)(code.Hz()*10)); setUInt8(OffsetTXTone, (index<<1)|1); } unsigned int OpenRTXCodeplug::ChannelElement::rxColorCode() const { return getUInt4(OffsetRXColorCode, BitRXColorCode); } void OpenRTXCodeplug::ChannelElement::setRXColorCode(unsigned int cc) { setUInt4(OffsetRXColorCode, BitRXColorCode, cc); } unsigned int OpenRTXCodeplug::ChannelElement::txColorCode() const { return getUInt4(OffsetTXColorCode, BitTXColorCode); } void OpenRTXCodeplug::ChannelElement::setTXColorCode(unsigned int cc) { setUInt4(OffsetTXColorCode, BitTXColorCode, cc); } DMRChannel::TimeSlot OpenRTXCodeplug::ChannelElement::timeslot() const { if (1 == getUInt8(OffsetTimeSlot)) return DMRChannel::TimeSlot::TS1; return DMRChannel::TimeSlot::TS2; } void OpenRTXCodeplug::ChannelElement::setTimeslot(DMRChannel::TimeSlot ts) { if (DMRChannel::TimeSlot::TS1 == ts) setUInt8(OffsetTimeSlot, 1); else setUInt8(OffsetTimeSlot, 2); } bool OpenRTXCodeplug::ChannelElement::hasDMRContactIndex() const { return 0 != dmrContactIndex(); } unsigned int OpenRTXCodeplug::ChannelElement::dmrContactIndex() const { return getUInt16_le(OffsetDMRContact); } void OpenRTXCodeplug::ChannelElement::setDMRContactIndex(unsigned int idx) { setUInt16_le(OffsetDMRContact, idx); } void OpenRTXCodeplug::ChannelElement::clearDMRContactIndex() { setDMRContactIndex(0); } unsigned int OpenRTXCodeplug::ChannelElement::rxChannelAccessNumber() const { return getUInt4(OffsetRXCAN, BitRXCAN); } void OpenRTXCodeplug::ChannelElement::setRXChannelAccessNumber(unsigned int cc) { setUInt4(OffsetRXCAN, BitRXCAN, cc); } unsigned int OpenRTXCodeplug::ChannelElement::txChannelAccessNumber() const { return getUInt4(OffsetTXCAN, BitTXCAN); } void OpenRTXCodeplug::ChannelElement::setTXChannelAccessNumber(unsigned int cc) { setUInt4(OffsetTXCAN, BitTXCAN, cc); } OpenRTXCodeplug::ChannelElement::EncryptionMode OpenRTXCodeplug::ChannelElement::encryptionMode() const { return (EncryptionMode)getUInt4(OffsetEncrMode, BitEncrMode); } void OpenRTXCodeplug::ChannelElement::setEncryptionMode(EncryptionMode mode) { return setUInt4(OffsetEncrMode, BitEncrMode, mode); } OpenRTXCodeplug::ChannelElement::ChannelMode OpenRTXCodeplug::ChannelElement::channelMode() const { return (ChannelMode) getUInt4(OffsetM17ChMode, BitM17ChMode); } void OpenRTXCodeplug::ChannelElement::setChannelMode(ChannelMode mode) { setUInt4(OffsetM17ChMode, BitM17ChMode, mode); } bool OpenRTXCodeplug::ChannelElement::gpsDataEnabled() const { return 1 == getUInt8(OffsetM17GPSMode); } void OpenRTXCodeplug::ChannelElement::enableGPSData(bool enable) { if (enable) setUInt8(OffsetM17GPSMode, 1); else setUInt8(OffsetM17GPSMode, 0); } bool OpenRTXCodeplug::ChannelElement::hasM17ContactIndex() const { return 0 != m17ContactIndex(); } unsigned int OpenRTXCodeplug::ChannelElement::m17ContactIndex() const { return getUInt16_le(OffsetM17Contact); } void OpenRTXCodeplug::ChannelElement::setM17ContactIndex(unsigned int idx) { setUInt16_le(OffsetM17Contact, idx); } void OpenRTXCodeplug::ChannelElement::clearM17ContactIndex() { setM17ContactIndex(0); } Channel * OpenRTXCodeplug::ChannelElement::toChannelObj(Codeplug::Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx) if (! isValid()) { errMsg(err) << "Cannot decode invalid channel."; return nullptr; } if (Mode_M17 == mode()) { errMsg(err) << "Cannot decode M17 channel. Not implemented yet."; return nullptr; } Channel *ch = nullptr; if (Mode_FM == mode()) { FMChannel *an = new FMChannel(); ch = an; switch(bandwidth()) { case BW_12_5kHz: an->setBandwidth(FMChannel::Bandwidth::Narrow); break; case BW_20kHz: case BW_25kHz: an->setBandwidth(FMChannel::Bandwidth::Wide); break; } an->setRXTone(rxTone()); an->setTXTone(txTone()); } else if (Mode_DMR == mode()) { DMRChannel *dmr = new DMRChannel(); ch = dmr; dmr->setAdmit(DMRChannel::Admit::ColorCode); dmr->setColorCode(rxColorCode()); dmr->setTimeSlot(timeslot()); } // Common settings ch->setName(name()); ch->setRXOnly(rxOnly()); if (30 > power()) { // less than 30dBm (1W) min ch->setPower(Channel::Power::Min); } else if (34 > power()) { ch->setPower(Channel::Power::Low); } else if (37 > power()) { ch->setPower(Channel::Power::Mid); } else if (38 > power()) { ch->setPower(Channel::Power::High); } else { ch->setPower(Channel::Power::Max); } ch->setRXFrequency(rxFrequency()); ch->setTXFrequency(txFrequency()); return ch; } bool OpenRTXCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err) const { if (! isValid()) { errMsg(err) << "Cannot link invalid channel '" << c->name() << "'."; return false; } if (Mode_M17 == mode()) { errMsg(err) << "Cannot link M17 channel '" << c->name() << "', not implemented yet."; return false; } else if (Mode_DMR == mode()) { DMRChannel *dmr = c->as(); // Link group list, if set /*if (hasGroupListIndex()) { if (! ctx.has(groupListIndex())) { errMsg(err) << "Cannot link group list index " << groupListIndex() << " for channel '" << c->name() << "': Index not found."; return false; } dmr->setGroupListObj(ctx.get(groupListIndex())); }*/ // Link contact, if set if (hasDMRContactIndex()) { if (! ctx.has(dmrContactIndex())) { errMsg(err) << "Cannot link DMR contact index " << dmrContactIndex() << " for channel '" << c->name() << "': Index not found."; return false; } dmr->setTXContactObj(ctx.get(dmrContactIndex())); } } // Link scan list, if set /*if (hasScanListIndex()) { if (! ctx.has(scanListIndex())) { errMsg(err) << "Cannot link scan list index " << scanListIndex() << " for channel '" << c->name() << "': Index not found."; return false; } c->setScanList(ctx.get(scanListIndex())); }*/ return true; } bool OpenRTXCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx, const ErrorStack &err) { clear(); setName(c->name()); setRXOnly(c->rxOnly()); switch (c->power()) { case Channel::Power::Min: setPower(27); break; case Channel::Power::Low: setPower(30); break; case Channel::Power::Mid: setPower(34); break; case Channel::Power::High: setPower(37); break; case Channel::Power::Max: setPower(38.5); break; } setRXFrequency(c->rxFrequency()); setTXFrequency(c->txFrequency()); if (c->scanListRef()->isNull()) clearScanListIndex(); else setScanListIndex(ctx.index(c->scanList())); clearGroupListIndex(); if (c->is()) { const FMChannel *fm = c->as(); setMode(Mode_FM); setRXTone(fm->rxTone(), err); setTXTone(fm->txTone(), err); } else if (c->is()) { const DMRChannel *dmr = c->as(); setMode(Mode_DMR); if (! dmr->groupList()->isNull()) setGroupListIndex(ctx.index(dmr->groupListObj())); setTXColorCode(dmr->colorCode()); setRXColorCode(dmr->colorCode()); setTimeslot(dmr->timeSlot()); if (! dmr->contact()->isNull()) setDMRContactIndex(ctx.index(dmr->txContactObj())); } return true; } /* ********************************************************************************************* * * Implementation of OpenRTXCodeplug::ContactElement * ********************************************************************************************* */ OpenRTXCodeplug::ContactElement::ContactElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } OpenRTXCodeplug::ContactElement::ContactElement(uint8_t *ptr) : Element(ptr, 0x0027) { // pass... } OpenRTXCodeplug::ContactElement::~ContactElement() { // pass... } void OpenRTXCodeplug::ContactElement::clear() { memset(_data, 0, _size); } bool OpenRTXCodeplug::ContactElement::isValid() const { return Codeplug::Element::isValid() && ( (Mode_M17 == mode()) || (Mode_DMR == mode()) ); } QString OpenRTXCodeplug::ContactElement::name() const { return readASCII(OffsetName, StringLength, 0); } void OpenRTXCodeplug::ContactElement::setName(const QString &name) { writeASCII(OffsetName, name, StringLength, 0); } OpenRTXCodeplug::Mode OpenRTXCodeplug::ContactElement::mode() const { return (Mode) getUInt8(OffsetMode); } void OpenRTXCodeplug::ContactElement::setMode(Mode mode) { setUInt8(OffsetMode, mode); } unsigned int OpenRTXCodeplug::ContactElement::dmrId() const { return getUInt32_le(OffsetDMRId); } void OpenRTXCodeplug::ContactElement::setDMRId(unsigned int id) { setUInt32_le(OffsetDMRId, id); } bool OpenRTXCodeplug::ContactElement::dmrRing() const { return getBit(OffsetDMRRing, BitDMRRing); } void OpenRTXCodeplug::ContactElement::enableDMRRing(bool enable) { return setBit(OffsetDMRRing, BitDMRRing, enable); } DMRContact::Type OpenRTXCodeplug::ContactElement::dmrContactType() const { // This is not specified yet?!? return (DMRContact::Type)getUInt2(OffsetDMRCallType, BitDMRCallType); } void OpenRTXCodeplug::ContactElement::setDMRContactType(DMRContact::Type type) { setUInt2(OffsetDMRCallType, BitDMRCallType, type); } QString OpenRTXCodeplug::ContactElement::m17Call() const { static const char charMap[] = "xABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/."; uint64_t encoded; memcpy(((uint8_t *)&encoded)+2, _data+OffsetM17Address, 6); QString result; while (encoded) { result.append(charMap[encoded%40]); encoded /= 40; } return result; } bool OpenRTXCodeplug::ContactElement::setM17Call(const QString &call, const ErrorStack &err) { if (call.size() > 9) { errMsg(err) << "Cannot encode calls longer than 9 chars."; return false; } QString C = call.toUpper(); uint64_t encoded = 0; for (QString::const_reverse_iterator it=C.rbegin(); it!=C.rend(); it++) { encoded *= 40; char c = QChar(*it).toLatin1(); if (('A' <= c) && ('Z' >= c)) encoded += (c-'A')+1; else if (('0' <= c) && ('9' >= c)) encoded += (c-'0')+27; else if ('-' == c) encoded += 37; else if ('/' == c) encoded += 38; else if ('.' == c) encoded += 39; else { errMsg(err) << "Invalid char '" << *it << "' for an M17 call [A-Z,0-9,-,/,.]."; return false; } } memcpy(_data+OffsetM17Address, ((uint8_t*)&encoded)+2, 6); return true; } DMRContact * OpenRTXCodeplug::ContactElement::toContactObj(Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx) if (! isValid()) { errMsg(err) << "Cannot decode invalid contacts."; return nullptr; } if (Mode_DMR != mode()) { errMsg(err) << "Only DMR contacts are implemented."; return nullptr; } DMRContact *contact = new DMRContact(); contact->setName(name()); contact->setNumber(dmrId()); contact->setType(dmrContactType()); contact->setRing(dmrRing()); return contact; } void OpenRTXCodeplug::ContactElement::fromContactObj(const DMRContact *cont, Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err) setMode(Mode_DMR); setName(cont->name()); setDMRId(cont->number()); setDMRContactType(cont->type()); enableDMRRing(cont->ring()); } /* ********************************************************************************************* * * Implementation of OpenRTXCodeplug::ZoneElement * ********************************************************************************************* */ OpenRTXCodeplug::ZoneElement::ZoneElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } OpenRTXCodeplug::ZoneElement::ZoneElement(uint8_t *ptr) : Element(ptr, 0x0022) { _size = 0x22 + channelCount()*4; } OpenRTXCodeplug::ZoneElement::~ZoneElement() { // pass... } void OpenRTXCodeplug::ZoneElement::clear() { memset(_data, 0, 0x22); setChannelCount(0); } bool OpenRTXCodeplug::ZoneElement::isValid() const { return Codeplug::Element::isValid(); } QString OpenRTXCodeplug::ZoneElement::name() const { return readASCII(OffsetName, StringLength, 0); } void OpenRTXCodeplug::ZoneElement::setName(const QString &name) { writeASCII(OffsetName, name, StringLength, 0); } unsigned int OpenRTXCodeplug::ZoneElement::channelCount() const { return getUInt16_le(OffsetCount); } void OpenRTXCodeplug::ZoneElement::setChannelCount(unsigned int n) { setUInt16_le(OffsetCount, n); _size = 0x22 + sizeof(uint32_t)*n; } unsigned int OpenRTXCodeplug::ZoneElement::channelIndex(unsigned int n) const { return getUInt32_le(OffsetChannel + sizeof(uint32_t)*n); } void OpenRTXCodeplug::ZoneElement::setChannelIndex(unsigned int n, unsigned int idx) { setUInt32_le(OffsetChannel + sizeof(uint32_t)*n, idx); } Zone * OpenRTXCodeplug::ZoneElement::toZoneObj(Context &ctx) const { Q_UNUSED(ctx) Zone *zone = new Zone(); zone->setName(name()); return zone; } bool OpenRTXCodeplug::ZoneElement::linkZoneObj(Zone *zone, Context &ctx, bool putInB, const ErrorStack &err) const { for (unsigned int i=0; i(channelIndex(i))) { errMsg(err) << "Cannot link zone '" << zone->name() << "': Channel index " << channelIndex(i) << " not known."; return false; } if (putInB) zone->B()->add(ctx.get(channelIndex(i))); else zone->A()->add(ctx.get(channelIndex(i))); } return true; } void OpenRTXCodeplug::ZoneElement::fromZoneObjA(const Zone *zone, Context &ctx) { if (zone->B()->count()) setName(zone->name()+" A"); else setName(zone->name()); setChannelCount(zone->A()->count()); for (int i=0; iA()->count(); i++) { setChannelIndex(i, ctx.index(zone->A()->get(i))); } } void OpenRTXCodeplug::ZoneElement::fromZoneObjB(const Zone *zone, Context &ctx) { setName(zone->name() + " B"); setChannelCount(zone->B()->count()); for (int i=0; iB()->count(); i++) { setChannelIndex(i, ctx.index(zone->B()->get(i))); } } /* ********************************************************************************************* * * Implementation of OpenRTXCodeplug * ********************************************************************************************* */ OpenRTXCodeplug::OpenRTXCodeplug(QObject *parent) : Codeplug(parent) { addImage("OpenRTX codeplug v0.1"); image(0).addElement(0x0000, HeaderSize); } OpenRTXCodeplug::~OpenRTXCodeplug() { // pass... } void OpenRTXCodeplug::clear() { remImage(0); addImage("OpenRTX codeplug v0.1"); image(0).addElement(0x0000, HeaderSize); } bool OpenRTXCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) const { Q_UNUSED(err) // All indices as 1-based. That is, the first channel gets index 1 etc. // Map DMR contacts for (int i=0, d=0; icontacts()->count(); i++) { if (config->contacts()->contact(i)->is()) { ctx.add(config->contacts()->contact(i)->as(), d+1); d++; } } // Map channels for (int i=0; ichannelList()->count(); i++) ctx.add(config->channelList()->channel(i), i+1); // Map zones for (int i=0; izones()->count(); i++) ctx.add(config->zones()->zone(i), i+1); return true; } bool OpenRTXCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) { // Check if default DMR id is set. if (nullptr == config->settings()->defaultIdRef()) { errMsg(err) << "Cannot encode TyT codeplug: No default radio ID specified."; return false; } // Create index<->object table. Context ctx(config); if (! index(config, ctx, err)) return false; return this->encodeElements(flags, ctx, err); } bool OpenRTXCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) { HeaderElement header(data(0)); header.clear(); header.setAuthor(ctx.config()->settings()->defaultId()->name()); header.setDescription("Encoded by qdmr v" VERSION_STRING); // Define Contacts if (! this->encodeContacts(ctx.config(), flags, ctx, err)) { errMsg(err) << "Cannot encode contacts."; return false; } if (! this->encodeChannels(ctx.config(), flags, ctx, err)) { errMsg(err) << "Cannot encode channels."; return false; } if (! this->encodeZones(ctx.config(), flags, ctx, err)) { errMsg(err) << "Cannot encode zones."; return false; } return true; } bool OpenRTXCodeplug::decode(Config *config, const ErrorStack &err) { // Clear config object config->clear(); // Create index<->object table. Context ctx(config); return this->decodeElements(ctx, err); } bool OpenRTXCodeplug::decodeElements(Context &ctx, const ErrorStack &err) { if (! this->createContacts(ctx.config(), ctx, err)) { errMsg(err) << "Cannot create contacts."; return false; } if (! this->createChannels(ctx.config(), ctx, err)) { errMsg(err) << "Cannot create channels."; return false; } if (! this->createZones(ctx.config(), ctx, err)) { errMsg(err) << "Cannot create zones."; return false; } if (! this->linkChannels(ctx.config(), ctx, err)) { errMsg(err) << "Cannot link channels."; return false; } if (! this->linkZones(ctx.config(), ctx, err)) { errMsg(err) << "Cannot link zones."; return false; } return true; } unsigned int OpenRTXCodeplug::numContacts() { return HeaderElement(data(0x0000)).contactCount(); } unsigned int OpenRTXCodeplug::offsetContact(unsigned int n) { return HeaderSize + n*ContactSize; } bool OpenRTXCodeplug::encodeContacts(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags) /// @todo Limit number of contacts. unsigned int numContacts = ctx.count(); HeaderElement(data(0x0000)).setContactCount(numContacts); image(0).addElement(offsetContact(0), numContacts*ContactSize); for (int i=0,c=0; icontacts()->count(); i++) { if (! config->contacts()->contact(i)->is()) continue; ContactElement contact(data(offsetContact(c))); contact.fromContactObj( config->contacts()->contact(i)->as(), ctx, err); c++; } return true; } bool OpenRTXCodeplug::createContacts(Config *config, Context &ctx, const ErrorStack &err) { unsigned int numContacts = HeaderElement(data(0x0000)).contactCount(); for (unsigned int i=0; icontacts()->add(contact); ctx.add(contact, i+1); } return true; } unsigned int OpenRTXCodeplug::numChannels() { return HeaderElement(data(0x0000)).channelCount(); } unsigned int OpenRTXCodeplug::offsetChannel(unsigned int n) { unsigned int numContacts = HeaderElement(data(0x0000)).contactCount(); return HeaderSize + numContacts*ContactSize + n*ChannelSize; } bool OpenRTXCodeplug::encodeChannels(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags) /// @todo Limit number of channels. unsigned int numChannels = config->channelList()->count(); HeaderElement(data(0x0000)).setChannelCount(numChannels); image(0).addElement(offsetChannel(0), numChannels*ChannelSize); for (int i=0; ichannelList()->count(); i++) { ChannelElement ch(data(offsetChannel(i))); if (! ch.fromChannelObj(config->channelList()->channel(i), ctx, err)) { errMsg(err) << "Cannot encode " << (i+1) << "-th channel '" << config->channelList()->channel(i)->name() << "'."; return false; } ctx.add(config->channelList()->channel(i), i+1); } return true; } bool OpenRTXCodeplug::createChannels(Config *config, Context &ctx, const ErrorStack &err) { unsigned int numChannels = HeaderElement(data(0x0000)).channelCount(); unsigned int offsetChannels = offsetChannel(0); for (unsigned int i=0; ichannelList()->add(chObj); ctx.add(chObj, i+1); } return true; } bool OpenRTXCodeplug::linkChannels(Config *config, Context &ctx, const ErrorStack &err) { unsigned int numChannels = HeaderElement(data(0x0000)).channelCount(); unsigned int offsetChannels = offsetChannel(0); for (unsigned int i=0; ichannelList()->channel(i); if (! ch.linkChannelObj(chObj, ctx, err)) { errMsg(err) << "Cannot link " << (i+1) << "-th channel " << chObj->name() << "."; return false; } } return true; } unsigned int OpenRTXCodeplug::numZones() { return HeaderElement(data(0x0000)).zoneCount(); } unsigned int OpenRTXCodeplug::offsetZoneOffsets() { HeaderElement header(data(0x0000)); unsigned int numContacts = header.contactCount(); unsigned int numChannels = header.channelCount(); return HeaderSize + numContacts*ContactSize + numChannels*ChannelSize; } unsigned int OpenRTXCodeplug::offsetZone(unsigned int n) { HeaderElement header(data(0x0000)); unsigned int numContacts = header.contactCount(); unsigned int numChannels = header.channelCount(); uint32_t *ptr = (uint32_t *)data(HeaderSize + numContacts*ContactSize + numChannels*ChannelSize + n*sizeof(uint32_t)); return qFromLittleEndian(*ptr); } bool OpenRTXCodeplug::encodeZones(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err) // Count zones (A + B) unsigned int zoneCount=0; for (int i=0; izones()->count(); i++) { zoneCount++; // Check if B contains channels if (config->zones()->zone(i)->B()->count()) zoneCount++; } // Allocate zone offsets HeaderElement(data(0x0000)).setZoneCount(zoneCount); image(0).addElement(offsetZoneOffsets(), zoneCount*sizeof(uint32_t)); uint32_t *offsets = (uint32_t *)data(offsetZoneOffsets()); // Allocate and encode zones uint32_t currentOffset = offsetZoneOffsets() + zoneCount*sizeof(uint32_t); for (unsigned int z=0, i=0; izones()->zone(z)->A()->count()*sizeof(uint32_t); image(0).addElement(currentOffset, zoneSize); offsets[i] = qToLittleEndian(currentOffset); ZoneElement(data(currentOffset)).fromZoneObjA(config->zones()->zone(z), ctx); currentOffset += zoneSize; // Allocate & encode zone B, if not empty if (ZoneHeaderSize+config->zones()->zone(z)->B()->count()) { i++; unsigned int zoneSize = ZoneHeaderSize+config->zones()->zone(z)->B()->count()*sizeof(uint32_t); image(0).addElement(currentOffset, zoneSize); offsets[i] = qToLittleEndian(currentOffset); ZoneElement(data(currentOffset)).fromZoneObjB(config->zones()->zone(z), ctx); currentOffset += zoneSize; } } return true; } bool OpenRTXCodeplug::createZones(Config *config, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) unsigned int zoneCount = numZones(); uint32_t *zoneOffsets = (uint32_t *) data(offsetZoneOffsets()); Zone *last_zone = nullptr; for (unsigned int i=0; iname())); Zone *obj = last_zone; if (! is_ext) { last_zone = obj = new Zone(zone.name()); if (zone.name().endsWith(" A")) obj->setName(zone.name().chopped(2)); config->zones()->add(obj); ctx.add(obj, i+1); } } return true; } bool OpenRTXCodeplug::linkZones(Config *config, Context &ctx, const ErrorStack &err) { Q_UNUSED(config); Q_UNUSED(err) unsigned int zoneCount = numZones(); uint32_t *zoneOffsets = (uint32_t *) data(offsetZoneOffsets()); Zone *last_zone = nullptr; for (unsigned int i=0, z=0; i(i+1)) { Zone *obj = last_zone = ctx.get(i+1); for (unsigned int i=0; i(zone.channelIndex(i))) { logWarn() << "Cannot link channel with index " << zone.channelIndex(i) << " channel not defined."; continue; } obj->A()->add(ctx.get(zone.channelIndex(i))); } } else { Zone *obj = last_zone; last_zone = nullptr; for (unsigned int i=0; i(zone.channelIndex(i))) { logWarn() << "Cannot link channel with index " << zone.channelIndex(i) << " channel not defined."; continue; } obj->B()->add(ctx.get(zone.channelIndex(i))); } } } return true; } qdmr-0.12.3/lib/openrtx_codeplug.hh000066400000000000000000000460241501654372000172210ustar00rootroot00000000000000#ifndef OPENRTX_CODEPLUG_HH #define OPENRTX_CODEPLUG_HH #include "codeplug.hh" #include "signaling.hh" #include "channel.hh" #include "contact.hh" class DMRContact; class Zone; class RXGroupList; class ScanList; /** Implements the binary encoding and decoding of the OpenRTX codeplug. * * @section ortxcpl Codeplug structure within radio * The binary codeplug does not use fixed offsets. It is just a concatenation arrays of codeplug * elements. This codeplug implements the revision 0.1. * * * * * * * *
      Content
      Header see @c HeaderElement.
      Max. 65536 contacts as specified in the header. See @c ContactElement.
      Max. 65536 channels as specified in the header. See @c ChannelElement.
      Max. 65536 bank (zone) offsets as specified in the header. Each offset is stored as a * ??? and specifies the offset to the bank w.r.t. ??? in ???.
      Max. 65536 banks (zones). See @c ZoneElement.
      * * @ingroup ortx */ class OpenRTXCodeplug : public Codeplug { Q_OBJECT public: /** Possible modes for a channel or contact. */ enum Mode { Mode_None = 0, ///< Disabled? Mode_FM = 1, ///< FM Channel. Mode_DMR = 2, ///< DMR Channel. Mode_M17 = 3 ///< M17 Channel. }; public: /** Implements the codeplug header element. * * Binary representation of the header (size 0058h bytes): * @verbinclude openrtx_header.txt */ class HeaderElement: public Codeplug::Element { protected: /** Hidden constructor. */ HeaderElement(uint8_t *ptr, size_t size); public: /** Constructor. */ HeaderElement(uint8_t *ptr); void clear(); bool isValid() const; /** Returns the version number (MAJOR<<8)|MINOR. */ virtual uint16_t version() const; /** Sets the version number. This number is fixed to 0.1, the supported version. */ virtual void setVersion(); /** Returns the author name. */ virtual QString author() const; /** Sets the author name. */ virtual void setAuthor(const QString &name); /** Returns the description. */ virtual QString description() const; /** Sets the description. */ virtual void setDescription(const QString description); /** Returns the timestamp. */ virtual QDateTime timestamp() const; /** Sets the timestamp. */ virtual void setTimestamp(const QDateTime timestamp=QDateTime::currentDateTime()); /** Returns the contact count. */ virtual unsigned int contactCount() const; /** Sets the contact count. */ virtual void setContactCount(unsigned int n); /** Returns the channel count. */ virtual unsigned int channelCount() const; /** Sets the channel count. */ virtual void setChannelCount(unsigned int n); /** Returns the zone count. */ virtual unsigned int zoneCount() const; /** Sets the zone count. */ virtual void setZoneCount(unsigned int n); protected: /** Just holds the offsets within the header and other constants. */ enum Offsets { OffsetMagic = 0x00, MagicNumber = 0x43585452, OffsetVersion = 0x08, OffsetAuthor = 0x0a, OffsetDescription = 0x2a, OffsetTimestamp = 0x4a, OffsetContactCount = 0x52, OffsetChannelCount = 0x54, OffsetZoneCount = 0x56, StringLength = 0x20, SupportedVersion = ((0<<8)|1) }; }; /** Implements the binary representation of a channel. * * Binary representation (size 005ah bytes): * @verbinclude openrtx_channel.txt */ class ChannelElement: public Codeplug::Element { public: /** Specifies the possible bandwidth settings. */ enum Bandwidth { BW_12_5kHz = 0, BW_20kHz = 1, BW_25kHz = 2 }; /** Specifies the DMR time slot settings. */ enum Timeslot { Timeslot1 = 1, Timeslot2 = 2 }; /** Specifies the M17 channel mode. */ enum ChannelMode { M17Voice = 1, M17Data = 2, M17VoiceData = 3 }; /** Specifies the M17 encryption modes. */ enum EncryptionMode { EncrNone = 0, EncrAES256 = 1, EncrScrambler = 2 }; protected: /** Constructs a channel from the given memory. */ ChannelElement(uint8_t *ptr, size_t size); public: /** Constructs a channel from the given memory. */ explicit ChannelElement(uint8_t *ptr); /** Destructor. */ virtual ~ChannelElement(); bool isValid() const; /** Resets the channel. */ virtual void clear(); /** Returns the channel mode. */ virtual Mode mode() const; /** Sets the channel mode. */ virtual void setMode(Mode mode); /** Returns @c true, if the channel is RX only. */ virtual bool rxOnly() const; /** Enables/disables RX only for the channel. */ virtual void setRXOnly(bool enable); /** Returns the bandwidth of the channel. */ virtual Bandwidth bandwidth() const; /** Sets the bandwidth of the channel. */ virtual void setBandwidth(Bandwidth bw); /** Power in dBm. */ virtual float power() const; /** Set power in dBm. */ virtual void setPower(float dBm); /** Returns the RX frequency in MHz. */ virtual Frequency rxFrequency() const; /** Sets the RX frequency in MHz. */ virtual void setRXFrequency(Frequency MHz); /** Returns the TX frequency in MHz. */ virtual Frequency txFrequency() const; /** Sets the TX frequency in MHz. */ virtual void setTXFrequency(Frequency MHz); /** Retrusn @c true if the scan list is set. */ virtual bool hasScanListIndex() const; /** Returns the scan list index. */ virtual unsigned int scanListIndex() const; /** Sets the scan list index. */ virtual void setScanListIndex(unsigned int index); /** Clears the scan list index. */ virtual void clearScanListIndex(); /** Retrusn @c true if the group list is set. */ virtual bool hasGroupListIndex() const; /** Returns the group list index. */ virtual unsigned int groupListIndex() const; /** Sets the group list index. */ virtual void setGroupListIndex(unsigned int index); /** Clears the group list index. */ virtual void clearGroupListIndex(); /** Returns the channel name. */ virtual QString name() const; /** Sets the channel name. */ virtual void setName(const QString &name); /** Returns the channel description. */ virtual QString description() const; /** Sets the channel description. */ virtual void setDescription(const QString &description); /** Returns the channel latitude. */ virtual float latitude() const; /** Sets the latitude. */ virtual void setLatitude(float lat); /** Returns the channel longitude. */ virtual float longitude() const; /** Sets the longitude. */ virtual void setLongitude(float lat); /** Returns the height in meters. */ virtual unsigned int altitude() const; /** Sets the height in meters. */ virtual void setAltitude(unsigned int alt); /** Returns the CTCSS RX sub-tone. Only valid for FM channels. */ virtual SelectiveCall rxTone() const; /** Sets the CTCSS RX sub-tone. Only valid for FM channels. */ virtual void setRXTone(const SelectiveCall &code, const ErrorStack &err=ErrorStack()); /** Returns the CTCSS TX sub-tone. Only valid for FM channels. */ virtual SelectiveCall txTone() const; /** Sets the CTCSS TX sub-tone. Only valid for FM channels. */ virtual void setTXTone(const SelectiveCall &code, const ErrorStack &err=ErrorStack()); /** Returns the RX color code. Only valid for DMR channels. */ virtual unsigned int rxColorCode() const; /** Sets the RX color code. Only valid for DMR channels. */ virtual void setRXColorCode(unsigned int cc); /** Returns the TX color code. Only valid for DMR channels. */ virtual unsigned int txColorCode() const; /** Sets the TX color code. Only valid for DMR channels. */ virtual void setTXColorCode(unsigned int cc); /** Returns the times slot for the channel. Only valid for DMR channels. */ virtual DMRChannel::TimeSlot timeslot() const; /** Sets the timeslot for the channel. Only valid for DMR channels. */ virtual void setTimeslot(DMRChannel::TimeSlot ts); /** Returns @c true if the DMR contact index is set. */ virtual bool hasDMRContactIndex() const; /** Returns the contact index for the DMR contact. Only valid for DMR channels. */ virtual unsigned int dmrContactIndex() const; /** Sets the DMR contact index. Only valid for DMR channels. */ virtual void setDMRContactIndex(unsigned int idx); /** Clears the DMR contact index. */ virtual void clearDMRContactIndex(); /** Returns the RX channel access number. Only valid for M17 channels. */ virtual unsigned int rxChannelAccessNumber() const; /** Sets the RX channel access number. Only valid for M17 channels. */ virtual void setRXChannelAccessNumber(unsigned int cc); /** Returns the TX color code. Only valid for M17 channels. */ virtual unsigned int txChannelAccessNumber() const; /** Sets the TX color code. Only valid for M17 channels. */ virtual void setTXChannelAccessNumber(unsigned int cc); /** Returns the encryption mode of the channel. Only valid for M17 channels. */ virtual EncryptionMode encryptionMode() const; /** Sets the encryption mode for the channel. Only valid for M17 channels. */ virtual void setEncryptionMode(EncryptionMode mode); /** Returns the channel mode. Only valid for M17 channels. */ virtual ChannelMode channelMode() const; /** Sets the channel mode. Only valid for M17 channels. */ virtual void setChannelMode(ChannelMode mode); /** Returns @c true if GPS position is sent as meta-data. Only valid for M17 channels. */ virtual bool gpsDataEnabled() const; /** Enables/disables sending of GPS position as meta-data. Only valid for M17 channels. */ virtual void enableGPSData(bool enable); /** Returns @c true if the M17 contact index is set. */ virtual bool hasM17ContactIndex() const; /** Returns the M17 contact index. Only valid for M17 channels. */ virtual unsigned int m17ContactIndex() const; /** Sets the M17 contact index. Only valid for M17 channels. */ virtual void setM17ContactIndex(unsigned int idx); /** Clears the M17 contact index. */ virtual void clearM17ContactIndex(); /** Constructs a generic @c Channel object from the codeplug channel. */ virtual Channel *toChannelObj(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links a previously constructed channel to the rest of the configuration. */ virtual bool linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Initializes this codeplug channel from the given generic configuration. */ virtual bool fromChannelObj(const Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Just contains the offsets within the channel element. */ enum Offsets { OffsetMode = 0x00, OffsetBandwidth = 0x01, BitBandwidth = 0x00, OffsetRXOnly = 0x01, BitRXOnly = 0x02, OffsetPower = 0x02, OffsetRXFrequency = 0x03, OffsetTXFrequency = 0x07, OffsetScanList = 0x0b, OffsetGroupList = 0x0c, OffsetName = 0x0d, OffsetDescription = 0x2d, OffsetChLatInt = 0x4d, OffsetChLatDec = 0x4e, OffsetChLonInt = 0x50, OffsetChLonDec = 0x51, OffsetChAltitude = 0x53, OffsetRXTone = 0x55, OffsetTXTone = 0x56, OffsetRXColorCode = 0x55, BitRXColorCode = 0x00, OffsetTXColorCode = 0x55, BitTXColorCode = 0x04, OffsetTimeSlot = 0x56, OffsetDMRContact = 0x57, OffsetRXCAN = 0x55, BitRXCAN = 0x00, OffsetTXCAN = 0x55, BitTXCAN = 0x04, OffsetEncrMode = 0x56, BitEncrMode = 0x04, OffsetM17ChMode = 0x56, BitM17ChMode = 0x00, OffsetM17GPSMode = 0x57, OffsetM17Contact = 0x58, StringLength = 0x20 }; }; /** Implements the digital contact for the OpenRTX firmware. * * Binary representation (size 0027h bytes): * @verbinclude openrtx_contact.txt */ class ContactElement: public Element { protected: /** Hidden constructor. */ ContactElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ContactElement(uint8_t *ptr); /** Destructor. */ virtual ~ContactElement(); /** Resets the contact. */ void clear(); /** Returns @c true if the contact is valid. */ bool isValid() const; /** Returns the name of the contact. */ virtual QString name() const; /** Sets the name of the contact. */ virtual void setName(const QString &name); /** Returns the mode of the contact (either DMR or M17). */ virtual Mode mode() const; /** Sets the mode of the contact. */ virtual void setMode(Mode mode); /** Returns the DMR ID. Only valid for DMR contacts. */ virtual unsigned int dmrId() const; /** Sets the DMR ID. Only valid for DMR contacts. */ virtual void setDMRId(unsigned int id); /** Returns @c true if the RX tone is enabled (ring). Only valid for DMR contacts. */ virtual bool dmrRing() const; /** Enables/disables RX tone (ring). Only valid for DMR contacts. */ virtual void enableDMRRing(bool enable); /** Returns the contact type. Only valid for DMR contacts. */ virtual DMRContact::Type dmrContactType() const; /** Sets the contact type. */ virtual void setDMRContactType(DMRContact::Type type); /** Returns the contact call. */ virtual QString m17Call() const; /** Sets the M17 call. */ virtual bool setM17Call(const QString &call, const ErrorStack &err=ErrorStack()); /** Constructs a @c DigitalContact instance from this codeplug contact. */ virtual DMRContact *toContactObj(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Resets this codeplug contact from the given @c DigitalContact. */ virtual void fromContactObj(const DMRContact *obj, Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Just holds the offsets within the codeplug. */ enum Offsets { OffsetName = 0x00, OffsetMode = 0x20, OffsetDMRId = 0x21, OffsetDMRCallType = 0x25, BitDMRCallType = 0, OffsetDMRRing = 0x25, BitDMRRing = 0x02, OffsetM17Address = 0x21, StringLength = 0x20 }; }; /** The binary encoding of a zone. * * Binary representation (variable size): * @verbinclude openrtx_zone.txt */ class ZoneElement: Element { protected: /** Hidden constructor. */ ZoneElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ZoneElement(uint8_t *ptr); virtual ~ZoneElement(); /** Resets the zone. */ void clear(); /** Returns @c true if the zone is valid. */ bool isValid() const; /** Returns the zone name. */ virtual QString name() const; /** Sets the name of the zone. */ virtual void setName(const QString &name); /** Returns the number of channels in zone. */ virtual unsigned int channelCount() const; /** Sets the number of channels in zone. */ virtual void setChannelCount(unsigned int n); /** Returns the n-th channel index. */ virtual unsigned int channelIndex(unsigned int n) const; /** Sets the n-th channel index. */ virtual void setChannelIndex(unsigned int n, unsigned int idx); /** Constructs a generic @c Zone object from this codeplug zone. */ virtual Zone *toZoneObj(Context &ctx) const; /** Links a previously constructed @c Zone object to the rest of the configuration. That is * linking to the referred channels. */ virtual bool linkZoneObj(Zone *zone, Context &ctx, bool putInB, const ErrorStack &err=ErrorStack()) const; /** Resets this codeplug zone representation from the given generic @c Zone object. */ virtual void fromZoneObjA(const Zone *zone, Context &ctx); /** Resets this codeplug zone representation from the given generic @c Zone object. */ virtual void fromZoneObjB(const Zone *zone, Context &ctx); protected: /** Just defines the offsets with the element. */ enum Offsets { OffsetName = 0x00, OffsetCount = 0x20, OffsetChannel = 0x22, StringLength = 0x20 }; }; public: /** Hidden constructor, use a device specific class to instantiate. */ explicit OpenRTXCodeplug(QObject *parent=nullptr); /** Destructor. */ virtual ~OpenRTXCodeplug(); /** Clears and resets the complete codeplug to some default values. */ virtual void clear(); bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Decodes the binary codeplug and stores its content in the given generic configuration. */ bool decode(Config *config, const ErrorStack &err=ErrorStack()); /** Encodes the given generic configuration as a binary codeplug. */ bool encode(Config *config, const Flags &flags = Flags(), const ErrorStack &err=ErrorStack()); public: /** Decodes the binary codeplug and stores its content in the given generic configuration using * the given context. */ virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the given generic configuration as a binary codeplug using the given context. */ virtual bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Returns the number of stored contacts. */ virtual unsigned int numContacts(); /** Returns the offset to the n-th contact element. */ virtual unsigned int offsetContact(unsigned int n); /** Encodes all digital contacts in the configuration into the codeplug. */ virtual bool encodeContacts(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Adds a digital contact to the configuration for each one in the codeplug. */ virtual bool createContacts(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); /** Returns the number of stored channels. */ virtual unsigned int numChannels(); /** Returns the offset to the n-th channel element. */ virtual unsigned int offsetChannel(unsigned int n); /** Encode all channels. */ virtual bool encodeChannels(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Adds all defined channels to the configuration. */ virtual bool createChannels(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); /** Links all channels. */ virtual bool linkChannels(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); /** Returns the number of stored zones. */ virtual unsigned int numZones(); /** Returns the offset to the zone offset array. */ virtual unsigned int offsetZoneOffsets(); /** Returns the offset to the n-th zone element. */ virtual unsigned int offsetZone(unsigned int n); /** Encodes zones. */ virtual bool encodeZones(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Adds zones to the configuration. */ virtual bool createZones(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); /** Links all zones within the configuration. */ virtual bool linkZones(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Just stores some sizes. */ enum Offsets { HeaderSize = 0x58, ChannelSize = 0x5a, ContactSize = 0x27, ZoneHeaderSize=0x22 }; }; #endif // OPENRTX_CODEPLUG_HH qdmr-0.12.3/lib/openrtx_interface.cc000066400000000000000000000022101501654372000173320ustar00rootroot00000000000000#include "openrtx_interface.hh" OpenRTXInterface::OpenRTXInterface(const USBDeviceDescriptor &descr, const ErrorStack &err, QObject *parent) : DFUDevice(descr, err, parent), RadioInterface() { // pass... } bool OpenRTXInterface::isOpen() const { return DFUDevice::isOpen(); } void OpenRTXInterface::close() { DFUDevice::close(); } RadioInfo OpenRTXInterface::identifier(const ErrorStack &err) { Q_UNUSED(err) return RadioInfo(); } bool OpenRTXInterface::read_start(uint32_t bank, uint32_t addr, const ErrorStack &err) { return false; } bool OpenRTXInterface::read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) { return false; } bool OpenRTXInterface::read_finish(const ErrorStack &err) { return false; } bool OpenRTXInterface::write_start(uint32_t bank, uint32_t addr, const ErrorStack &err) { return false; } bool OpenRTXInterface::write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) { return false; } bool OpenRTXInterface::write_finish(const ErrorStack &err) { return false; } bool OpenRTXInterface::reboot(const ErrorStack &err) { return false; } qdmr-0.12.3/lib/openrtx_interface.hh000066400000000000000000000021371501654372000173540ustar00rootroot00000000000000#ifndef OPENRTXINTERFACE_HH #define OPENRTXINTERFACE_HH #include "radiointerface.hh" #include "dfu_libusb.hh" /** Implements the communication interface to radios running the OpenRTX firmware. * * @ingroup ortx */ class OpenRTXInterface : public DFUDevice, public RadioInterface { Q_OBJECT public: explicit OpenRTXInterface(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent = nullptr); bool isOpen() const; void close(); RadioInfo identifier(const ErrorStack &err=ErrorStack()); bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()); bool read_finish(const ErrorStack &err=ErrorStack()); bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()); bool write_finish(const ErrorStack &err=ErrorStack()); bool reboot(const ErrorStack &err=ErrorStack()); }; #endif // OPENRTXINTERFACE_HH qdmr-0.12.3/lib/openuv380.cc000066400000000000000000000015471501654372000153760ustar00rootroot00000000000000#include "openuv380.hh" #include "opengd77_limits.hh" #include "openuv380_satelliteconfig.hh" #include "logger.hh" #include "config.hh" OpenUV380::OpenUV380(OpenGD77Interface *device, QObject *parent) : OpenGD77Base(device, parent), _name("Open MD-UV380"), _codeplug(), _callsigns() { _satelliteConfig = new OpenUV380SatelliteConfig(this); } const QString & OpenUV380::name() const { return _name; } const Codeplug & OpenUV380::codeplug() const { return _codeplug; } Codeplug & OpenUV380::codeplug() { return _codeplug; } const CallsignDB * OpenUV380::callsignDB() const { return &_callsigns; } CallsignDB * OpenUV380::callsignDB() { return &_callsigns; } RadioInfo OpenUV380::defaultRadioInfo() { return RadioInfo( RadioInfo::OpenUV380, "openuv380", "OpenMDUV380", "OpenGD77 Project", OpenGD77Interface::interfaceInfo()); } qdmr-0.12.3/lib/openuv380.hh000066400000000000000000000022301501654372000153760ustar00rootroot00000000000000/** @defgroup ogd77 Open GD-77 Firmware * Implements a radio running the Open MD-UV380 firmware. * @ingroup dsc */ #ifndef OPENUV380_HH #define OPENUV380_HH #include "opengd77base.hh" #include "openuv380_codeplug.hh" #include "openuv380_callsigndb.hh" /** Implements an USB interface to Open UV380 VHF/UHF 5W DMR (Tier I&II) radios. * * @ingroup ogd77 */ class OpenUV380 : public OpenGD77Base { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit OpenUV380(OpenGD77Interface *device=nullptr, QObject *parent=nullptr); const QString &name() const; const Codeplug &codeplug() const; Codeplug &codeplug(); const CallsignDB *callsignDB() const; CallsignDB *callsignDB(); /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); protected: /** The device identifier. */ QString _name; /** The actual binary codeplug representation. */ OpenUV380Codeplug _codeplug; /** The actual binary callsign DB representation. */ OpenUV380CallsignDB _callsigns; }; #endif // OPENGD77_HH qdmr-0.12.3/lib/openuv380_callsigndb.cc000066400000000000000000000034251501654372000175550ustar00rootroot00000000000000#include "openuv380_callsigndb.hh" #include "utils.hh" #include "userdatabase.hh" #include #define USERDB_SIZE 0x40000 #define USERDB_NUM_ENTRIES (USERDB_SIZE-sizeof(userdb_t))/sizeof(userdb_entry_t) /* ******************************************************************************************** * * Implementation of OpenUV380CallsignDB * ******************************************************************************************** */ OpenUV380CallsignDB::OpenUV380CallsignDB(QObject *parent) : OpenGD77BaseCallsignDB(parent) { addImage("OpenUV380 call-sign database"); } bool OpenUV380CallsignDB::encode(UserDatabase *calldb, const Selection &selection, const ErrorStack &err) { Q_UNUSED(err) // Limit entries to USERDB_NUM_ENTRIES qint64 n = std::min(calldb->count(), qint64(USERDB_NUM_ENTRIES)); if (selection.hasCountLimit()) n = std::min(n, (qint64)selection.countLimit()); // If there are no entries -> done. if (0 == n) return true; // Select first n entries and sort them in ascending order of their IDs QVector users; for (unsigned i=0; iuser(i)); std::sort(users.begin(), users.end(), [](const UserDatabase::User &a, const UserDatabase::User &b) { return a.id < b.id; }); // Allocate segment for user db if requested unsigned size = align_size(sizeof(userdb_t)+n*sizeof(userdb_entry_t), Limit::blockSize()); this->image(0).addElement(Offset::callsignDB(), size); // Encode user DB userdb_t *userdb = (userdb_t *)this->data(Offset::callsignDB()); userdb->clear(); userdb->setSize(n); userdb_entry_t *db = (userdb_entry_t *)this->data(Offset::callsignDB()+sizeof(userdb_t)); for (unsigned i=0; i #include #include "opengd77_extension.hh" /* ******************************************************************************************** * * Implementation of OpenUV380Codeplug * ******************************************************************************************** */ OpenUV380Codeplug::OpenUV380Codeplug(QObject *parent) : OpenGD77BaseCodeplug(parent) { addImage("OpenGD77 Codeplug EEPROM"); addImage("OpenGD77 Codeplug FLASH"); image(FLASH).addElement(0x00000080, 0x00005fe0); image(FLASH).addElement(0x00007500, 0x00003b00); image(FLASH).addElement(0x00020000, 0x000011a0); image(FLASH).addElement(0x0009b000, 0x00013e60); } void OpenUV380Codeplug::clearGeneralSettings() { GeneralSettingsElement(data(Offset::settings(), ImageIndex::settings())).clear(); } bool OpenUV380Codeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { GeneralSettingsElement el(data(Offset::settings(), ImageIndex::settings())); if (! flags.updateCodePlug) el.clear(); return el.encode(ctx, err); } bool OpenUV380Codeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) { return GeneralSettingsElement(data(Offset::settings(), ImageIndex::settings())).decode(ctx, err); } void OpenUV380Codeplug::clearDTMFSettings() { //DTMFSettingsElement(data(Offset::settings(), ImageIndex::settings())).clear(); } bool OpenUV380Codeplug::encodeDTMFSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err); return true; } bool OpenUV380Codeplug::decodeDTMFSettings(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); return true; } void OpenUV380Codeplug::clearAPRSSettings() { APRSSettingsBankElement(data(Offset::aprsSettings(), ImageIndex::aprsSettings())).clear(); } bool OpenUV380Codeplug::encodeAPRSSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { APRSSettingsBankElement el(data(Offset::aprsSettings(), ImageIndex::aprsSettings())); if (! flags.updateCodePlug) el.clear(); return el.encode(ctx, err); } bool OpenUV380Codeplug::decodeAPRSSettings(Context &ctx, const ErrorStack &err) { return APRSSettingsBankElement(data(Offset::aprsSettings(), ImageIndex::aprsSettings())) .decode(ctx, err); } bool OpenUV380Codeplug::linkAPRSSettings(Context &ctx, const ErrorStack &err) { return APRSSettingsBankElement(data(Offset::aprsSettings(), ImageIndex::aprsSettings())) .link(ctx, err); } bool OpenUV380Codeplug::encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); return BootSettingsElement(data(Offset::bootSettings(), ImageIndex::bootSettings())) .encode(ctx, err); } bool OpenUV380Codeplug::decodeBootSettings(Context &ctx, const ErrorStack &err) { return BootSettingsElement(data(Offset::bootSettings(), ImageIndex::bootSettings())) .decode(ctx, err); } void OpenUV380Codeplug::clearContacts() { ContactBankElement(data(Offset::contacts(), ImageIndex::contacts())).clear(); } bool OpenUV380Codeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); return ContactBankElement(data(Offset::contacts(), ImageIndex::contacts())).encode(ctx, err); } bool OpenUV380Codeplug::createContacts(Context &ctx, const ErrorStack &err) { return ContactBankElement(data(Offset::contacts(), ImageIndex::contacts())).decode(ctx, err); } void OpenUV380Codeplug::clearDTMFContacts() { DTMFContactBankElement(data(Offset::dtmfContacts(), ImageIndex::dtmfContacts())).clear(); } bool OpenUV380Codeplug::encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); return DTMFContactBankElement(data(Offset::dtmfContacts(), ImageIndex::dtmfContacts())) .encode(ctx, err); } bool OpenUV380Codeplug::createDTMFContacts(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) return DTMFContactBankElement(data(Offset::dtmfContacts(), ImageIndex::dtmfContacts())) .decode(ctx, err); } void OpenUV380Codeplug::clearChannels() { for (unsigned int b=0; b(c)) { if (! bank.channel(i).encode(ctx.get(c), ctx, err)) { errMsg(err) << "Cannot encode channel '" << ctx.get(c)->name() << "' at index " << i << " of bank " << b << "."; return false; } bank.enable(i, true); } else { bank.enable(i, false); } } } return true; } bool OpenUV380Codeplug::createChannels(Context &ctx, const ErrorStack &err) { for (unsigned int b=0,c=0; bchannelList()->add(obj); ctx.add(obj, c++); } } return true; } bool OpenUV380Codeplug::linkChannels(Context &ctx, const ErrorStack &err) { for (unsigned int b=0,c=0; b(c); ChannelElement element = bank.channel(i); assert(obj->name() == element.name()); if (! element.link(obj, ctx, err)) { errMsg(err) << "Cannot link channel '" << obj->name() << "' from index " << i << " in bank " << b << "."; return false; } c++; } } return true; } void OpenUV380Codeplug::clearBootSettings() { BootSettingsElement(data(Offset::bootSettings(), ImageIndex::bootSettings())).clear(); } void OpenUV380Codeplug::clearVFOSettings() { VFOChannelElement(data(Offset::vfoA(), ImageIndex::vfoA())).clear(); VFOChannelElement(data(Offset::vfoB(), ImageIndex::vfoB())).clear(); } void OpenUV380Codeplug::clearZones() { ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).clear(); } bool OpenUV380Codeplug::encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); return ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).encode(ctx, err); } bool OpenUV380Codeplug::createZones(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) return ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).decode(ctx, err); } bool OpenUV380Codeplug::linkZones(Context &ctx, const ErrorStack &err) { return ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).link(ctx, err); } void OpenUV380Codeplug::clearGroupLists() { GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).clear(); } bool OpenUV380Codeplug::encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); return GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).encode(ctx, err); } bool OpenUV380Codeplug::createGroupLists(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) return GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).decode(ctx, err); } bool OpenUV380Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) { return GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).link(ctx, err); } qdmr-0.12.3/lib/openuv380_codeplug.hh000066400000000000000000000112451501654372000172660ustar00rootroot00000000000000#ifndef OPENUV380_CODEPLUG_HH #define OPENUV380_CODEPLUG_HH #include "opengd77base_codeplug.hh" #include "opengd77_extension.hh" /** Represents, encodes and decodes the device specific codeplug for Open MD-UV380 firmware. * * This codeplug is almost identical to the original GD77 codeplug. * * @ingroup ogd77 */ class OpenUV380Codeplug: public OpenGD77BaseCodeplug { Q_OBJECT public: /** Constructs an empty codeplug for the Open MD-UV380. */ explicit OpenUV380Codeplug(QObject *parent=nullptr); public: void clearGeneralSettings(); bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()); void clearDTMFSettings(); bool encodeDTMFSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeDTMFSettings(Context &ctx, const ErrorStack &err=ErrorStack()); void clearAPRSSettings(); bool encodeAPRSSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeAPRSSettings(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkAPRSSettings(Context &ctx, const ErrorStack &err=ErrorStack()); void clearContacts(); bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack()); void clearDTMFContacts(); bool encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack()); void clearChannels(); bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()); void clearBootSettings(); bool encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeBootSettings(Context &ctx, const ErrorStack &err=ErrorStack()); void clearVFOSettings(); void clearZones(); bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createZones(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()); void clearGroupLists(); bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some Limits for this codeplug. */ struct Limit: public Element::Limit { /** Number of channel banks. */ static constexpr unsigned int channelBanks() { return 8; } }; protected: /** Internal used image indices. */ struct ImageIndex { /// @cond DO_NOT_DOCUEMNT static constexpr unsigned int settings() { return FLASH; } static constexpr unsigned int dtmfSettings() { return FLASH; } static constexpr unsigned int aprsSettings() { return FLASH; } static constexpr unsigned int dtmfContacts() { return FLASH; } static constexpr unsigned int channelBank0() { return FLASH; } static constexpr unsigned int bootSettings() { return FLASH; } static constexpr unsigned int vfoA() { return FLASH; } static constexpr unsigned int vfoB() { return FLASH; } static constexpr unsigned int zoneBank() { return FLASH; } static constexpr unsigned int channelBank1() { return FLASH; } static constexpr unsigned int contacts() { return FLASH; } static constexpr unsigned int groupLists() { return FLASH; } /// @endcond }; /** Some offsets. */ struct Offset { /// @cond DO_NOT_DOCUEMNT static constexpr unsigned int settings() { return 0x00000080; } static constexpr unsigned int dtmfSettings() { return 0x00001470; } static constexpr unsigned int aprsSettings() { return 0x00001588; } static constexpr unsigned int dtmfContacts() { return 0x00002f88; } static constexpr unsigned int channelBank0() { return 0x00003780; } // Channels 1-128 static constexpr unsigned int bootSettings() { return 0x00007518; } static constexpr unsigned int vfoA() { return 0x00007590; } static constexpr unsigned int vfoB() { return 0x000075c8; } static constexpr unsigned int zoneBank() { return 0x00008010; } static constexpr unsigned int channelBank1() { return 0x0009b1b0; } // Channels 129-1024 static constexpr unsigned int contacts() { return 0x000a7620; } static constexpr unsigned int groupLists() { return 0x000ad620; } /// @endcond }; }; #endif // OPENUV380_CODEPLUG_HH qdmr-0.12.3/lib/openuv380_satelliteconfig.cc000066400000000000000000000010201501654372000206140ustar00rootroot00000000000000#include "openuv380_satelliteconfig.hh" #include "errorstack.hh" OpenUV380SatelliteConfig::OpenUV380SatelliteConfig(QObject *parent) : OpenGD77BaseSatelliteConfig(parent) { image(FLASH).addElement(Offset::satellites(), 0x11a0); } bool OpenUV380SatelliteConfig::encode(SatelliteDatabase *db, const ErrorStack &err) { SatelliteBankElement bank(data(Offset::satellites(), FLASH)); if (! bank.encode(db, err)) { errMsg(err) << "Cannot encode satellite config for OpenUV380."; return false; } return true; } qdmr-0.12.3/lib/openuv380_satelliteconfig.hh000066400000000000000000000016411501654372000206370ustar00rootroot00000000000000#ifndef OPENUV380_SATELLITECONFIG_HH #define OPENUV380_SATELLITECONFIG_HH #include "opengd77base_satelliteconfig.hh" class OpenUV380SatelliteConfig : public OpenGD77BaseSatelliteConfig { Q_OBJECT public: /** Default constructor. */ explicit OpenUV380SatelliteConfig(QObject *parent = nullptr); /** Encodes the given satellite database. */ virtual bool encode(SatelliteDatabase *db, const ErrorStack &err=ErrorStack()); public: /** Some limits for the satellite config. */ struct Limit { /** The maximum number of satellites. */ static constexpr unsigned int satellites() { return OpenGD77BaseSatelliteConfig::SatelliteBankElement::Limit::satellites(); } }; protected: /** Some internal offsets. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int satellites() { return 0x020000; } /// @endcond }; }; #endif // OPENUV380_SATELLITECONFIG_HH qdmr-0.12.3/lib/orbitalelementsdatabase.cc000066400000000000000000000230751501654372000205050ustar00rootroot00000000000000#include "orbitalelementsdatabase.hh" #include #include #include #include #include #include #include #include "logger.hh" /* ********************************************************************************************* * * Implementation of OrbitalElement::Epoch * ********************************************************************************************* */ OrbitalElement::Epoch::Epoch() : year(0), month(0), day(0), hour(0), minute(0), second(0), microsecond(0) { // pass... } OrbitalElement::Epoch::Epoch(unsigned int pyear, unsigned int pmonth, unsigned int pday, unsigned int phour, unsigned int pminute, unsigned int psecond, unsigned int pmicrosecond) : year(pyear), month(pmonth), day(pday), hour(phour), minute(pminute), second(psecond), microsecond(pmicrosecond) { // pass... } OrbitalElement::Epoch OrbitalElement::Epoch::parse(const QString &datetime) { QRegularExpression pattern("([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2}).([0-9]{6})"); QRegularExpressionMatch match = pattern.match(datetime); if (! match.isValid()) return Epoch(); return Epoch{ match.captured(1).toUInt(), match.captured(2).toUInt(), match.captured(3).toUInt(), match.captured(4).toUInt(), match.captured(5).toUInt(), match.captured(6).toUInt(), match.captured(7).toUInt() }; } double OrbitalElement::Epoch::toEpoch() const { QDate date(year, month, day); double res = microsecond; res = (res / 1e6) + second; res = (res / 60) + minute; res = (res / 60) + hour; res = (res / 24) + date.dayOfYear(); return res; } QString OrbitalElement::Epoch::toString() const { return QString("%1-%2-%3T%4:%5:%6.%7") .arg(year, 4, 10, QChar('0')) .arg(month, 2, 10, QChar('0')) .arg(day, 2, 10, QChar('0')) .arg(hour, 2, 10, QChar('0')) .arg(minute, 2, 10, QChar('0')) .arg(second, 2, 10, QChar('0')) .arg(microsecond, 6, 10, QChar('0')); } /* ********************************************************************************************* * * Implementation of OrbitalElement * ********************************************************************************************* */ OrbitalElement::OrbitalElement() : _id(0), _name(), _epoch(), _meanMotion(0.0), _meanMotionDerivative(0.0), _inclination(0.0), _ascension(0.0), _eccentricity(0.0), _perigee(0.0), _meanAnomaly(0.0), _revolutionNumber(0) { // pass... } OrbitalElement::OrbitalElement(unsigned int id) : _id(id), _name(), _epoch(), _meanMotion(0.0), _meanMotionDerivative(0.0), _inclination(0.0), _ascension(0.0), _eccentricity(0.0), _perigee(0.0), _meanAnomaly(0.0), _revolutionNumber(0) { // pass... } bool OrbitalElement::isValid() const { return 0 != _id; } unsigned int OrbitalElement::id() const { return _id; } const QString & OrbitalElement::name() const { return _name; } const OrbitalElement::Epoch & OrbitalElement::epoch() const { return _epoch; } double OrbitalElement::meanMotion() const { return _meanMotion; } double OrbitalElement::meanMotionDerivative() const { return _meanMotionDerivative; } double OrbitalElement::inclination() const { return _inclination; } double OrbitalElement::ascension() const { return _ascension; } double OrbitalElement::eccentricity() const { return _eccentricity; } double OrbitalElement::perigee() const { return _perigee; } double OrbitalElement::meanAnomaly() const { return _meanAnomaly; } unsigned int OrbitalElement::revolutionNumber() const { return _revolutionNumber; } OrbitalElement OrbitalElement::fromCelesTrak(const QJsonObject &obj) { OrbitalElement el; el._name = obj.value("OBJECT_NAME").toString(); el._epoch = Epoch::parse(obj.value("EPOCH").toString()); el._meanMotion = obj.value("MEAN_MOTION").toDouble(); el._meanMotionDerivative = obj.value("MEAN_MOTION_DOT").toDouble(); el._inclination = obj.value("INCLINATION").toDouble(); el._ascension = obj.value("RA_OF_ASC_NODE").toDouble(); el._eccentricity = obj.value("ECCENTRICITY").toDouble(); el._perigee = obj.value("ARG_OF_PERICENTER").toDouble(); el._meanAnomaly = obj.value("MEAN_ANOMALY").toDouble(); el._revolutionNumber = obj.value("REV_AT_EPOCH").toInt(); el._id = obj.value("NORAD_CAT_ID").toInt(); return el; } /* ********************************************************************************************* * * Implementation of OrbitalElementsDatabase * ********************************************************************************************* */ OrbitalElementsDatabase::OrbitalElementsDatabase(bool autoLoad, unsigned int updatePeriod, QObject *parent) : QAbstractTableModel{parent}, _updatePeriod(updatePeriod), _elements(), _network() { connect(&_network, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadFinished(QNetworkReply*))); if (autoLoad) load(); } bool OrbitalElementsDatabase::contains(unsigned int id) const { return _idIndexMap.contains(id); } OrbitalElement OrbitalElementsDatabase::getById(unsigned int id) const { return _elements.at(_idIndexMap.value(id)); } const OrbitalElement & OrbitalElementsDatabase::getAt(unsigned int idx) const { return _elements[idx]; } OrbitalElement & OrbitalElementsDatabase::getAt(unsigned int idx) { return _elements[idx]; } int OrbitalElementsDatabase::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return _elements.size(); } int OrbitalElementsDatabase::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3; } QVariant OrbitalElementsDatabase::data(const QModelIndex &index, int role) const { if (index.row() >= _elements.size()) return QVariant(); if (Qt::DisplayRole == role) { if (0 == index.column()) return _elements.at(index.row()).id(); if (1 == index.column()) return _elements.at(index.row()).name(); if (2 == index.column()) return _elements.at(index.row()).epoch().toString(); } return QVariant(); } QVariant OrbitalElementsDatabase::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); switch (section) { case 0: return QStringLiteral("NORAD"); case 1: return QStringLiteral("Name"); case 2: return QStringLiteral("Epoch"); } return QVariant(); } unsigned OrbitalElementsDatabase::dbAge() const { QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/elements.json"; QFileInfo info(path); if (! info.exists()) return -1; return info.lastModified().daysTo(QDateTime::currentDateTime()); } void OrbitalElementsDatabase::load() { QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/elements.json"; if ((! load(path)) || (_updatePeriod < dbAge())) download(); } bool OrbitalElementsDatabase::load(const QString &filename) { QFile file(filename); if (! file.open(QIODevice::ReadOnly)) { QString msg = QString("Cannot open orbital elements '%1': %2").arg(filename).arg(file.errorString()); logError() << msg; emit error(msg); return false; } QByteArray data = file.readAll(); file.close(); QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (doc.isEmpty()) { QString msg = "Failed to load orbital elements: " + err.errorString(); logError() << msg; emit error(msg); return false; } if (! doc.isArray()) { QString msg = "Failed to load orbital elements: JSON document is not an array!"; logError() << msg; emit error(msg); return false; } beginResetModel(); QJsonArray array = doc.array(); _elements.clear(); _elements.reserve(array.size()); for (int i=0; ierror()) { QString msg = QString("Cannot download orbital elements: %1").arg(reply->errorString()); logError() << msg; emit error(msg); return; } QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QFile file(path+"/elements.json"); QDir directory; if ((! directory.exists(path)) && (!directory.mkpath(path))) { QString msg = QString("Cannot create path '%1'.").arg(path); logError() << msg; emit error(msg); return; } if (! file.open(QIODevice::WriteOnly)) { QString msg = QString("Cannot save orbital elements at '%1'.").arg(file.fileName()); logError() << msg; emit error(msg); return; } file.write(reply->readAll()); file.flush(); file.close(); load(); reply->deleteLater(); } qdmr-0.12.3/lib/orbitalelementsdatabase.hh000066400000000000000000000136371501654372000205220ustar00rootroot00000000000000/** @defgroup sat Satellite tracking settings. * @ingroup conf */ #ifndef ORBITALELEMENTSDATABASE_HH #define ORBITALELEMENTSDATABASE_HH #include #include /** Defines a single orbital element, enabling the tracking of a single satellite. This dataset * does not contain any transponder information. * @ingroup sat */ class OrbitalElement { public: /** Represents a Julien day epoch since a specified year. */ struct Epoch { /** The year of the epoch. */ unsigned int year; /** The month. */ unsigned int month; /** The day. */ unsigned int day; /** The hour. */ unsigned int hour; /** The minute. */ unsigned int minute; /** The second. */ unsigned int second; /** The microsecond. */ unsigned int microsecond; /** Default constructor. */ Epoch(); /** Constructor */ Epoch(unsigned int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int minute, unsigned int second, unsigned int microsecond); /** Copy constructor. */ Epoch(const Epoch &other) = default; /** Copy assignment. */ Epoch &operator =(const Epoch &other) = default; /** Parses a date-time string into the epoch. */ static Epoch parse(const QString &datetime); /** Computes the decimal epoch as the day of year a */ double toEpoch() const; /** Encodes the Epoch as YYYY-MM-DDThh:mm:ss.uuuuuu. */ QString toString() const; }; public: /** Default constructor. */ OrbitalElement(); /** Constructor from ID. */ OrbitalElement(unsigned int id); /** Copy constructor. */ OrbitalElement(const OrbitalElement &other) = default; /** Copy assignemnt. */ OrbitalElement &operator=(const OrbitalElement &other) = default; /** Returns @c true, if this represents a valid satellite information. */ bool isValid() const; /** Returns the NORAD catalog id. */ unsigned int id() const; /** Retunrs the name of the satellite. */ const QString &name() const; /** Epoch of the orbital elements. */ const Epoch &epoch() const; /** Returns the mean motion. */ double meanMotion() const; /** Returns the first derivative of the mean motion. */ double meanMotionDerivative() const; /** Returns the inclination. */ double inclination() const; /** Returns the right ascension of the ascending node. */ double ascension() const; /** Returns the eccentricity. */ double eccentricity() const; /** Returns the argument of perigee. */ double perigee() const; /** Returns the mean anomaly. */ double meanAnomaly() const; /** Returns the revolution number. */ unsigned int revolutionNumber() const; public: /** Constructs a orbital element from a CelesTrak JSON object. */ static OrbitalElement fromCelesTrak(const QJsonObject &obj); protected: /** NORAD id of the satellite. */ unsigned int _id; /** Desriptive name of the satellite. */ QString _name; /** The epoch. */ Epoch _epoch; /** Mean motion. */ double _meanMotion; /** First derivative of the mean motion. */ double _meanMotionDerivative; /** Inclination. */ double _inclination; /** Right ascension of the ascending node. */ double _ascension; /** Eccentricity. */ double _eccentricity; /** Argument of perigee. */ double _perigee; /** Mean anomaly. */ double _meanAnomaly; /** The revolution number. */ unsigned int _revolutionNumber; }; /** Downloads and updates a database of orbital elements from CelesTrak. * @ingroup sat */ class OrbitalElementsDatabase: public QAbstractTableModel { Q_OBJECT public: /** Constructs a orbital element database. * @param autoLoad [in] If @c true, the database gets downloaded and loaded automatically. * @param updatePeriodDays [in] Specifies the max age of the local database cache in days. * @param parent [in] Specifies the QObject parent. */ explicit OrbitalElementsDatabase(bool autoLoad, unsigned int updatePeriodDays=7, QObject *parent=nullptr); /** @c returns @c true if the database contains a satellite with the given NORAD id. */ bool contains(unsigned int id) const; /** Retunrs the orbital elements for the satellite with the given NORAD id. */ OrbitalElement getById(unsigned int id) const; /** Returns the i-th orbital element. */ const OrbitalElement &getAt(unsigned int idx) const; /** Returns the i-th orbital element. */ OrbitalElement &getAt(unsigned int idx); /** Returns the current age of the cache. */ unsigned int dbAge() const; /** If needed, downloads the database and loads all received orbital elements. */ void load(); /** Returns the number of elements in the database. */ int rowCount(const QModelIndex &parent = QModelIndex()) const; /** Returns the number of columns of the database table. */ int columnCount(const QModelIndex &parent = QModelIndex()) const; /** Returns a single cell of the database table. */ QVariant data(const QModelIndex &index, int role) const; /** Returns a single header of the database table. */ QVariant headerData(int section, Qt::Orientation orientation, int role) const; signals: /** Gets emitted once the satellite orbitals has been loaded. */ void loaded(); /** Gets emitted if the loading one of the sources fails. */ void error(const QString &msg); public slots: /** Starts the download of the orbital elements. */ void download(); private slots: /** Gets called whenever the orbital elements download is complete. */ void downloadFinished(QNetworkReply *reply); protected: /** Loads a database from the given filename. */ bool load(const QString &filename); private: /** Update period in days. */ unsigned int _updatePeriod; /** Holds all sattellites sorted by their catalog number. */ QVector _elements; /** Maps NORAD id to element. */ QHash _idIndexMap; /** The network access used for downloading. */ QNetworkAccessManager _network; }; #endif // ORBITALELEMENTSDATABASE_HH qdmr-0.12.3/lib/radio.cc000066400000000000000000000157521501654372000147300ustar00rootroot00000000000000#include "radio.hh" #include "anytone_interface.hh" #include "radioddity_interface.hh" #include "tyt_interface.hh" #include "dr1801uv_interface.hh" #include "gd73_interface.hh" #include "rd5r.hh" #include "gd73.hh" #include "gd77.hh" #include "md390.hh" #include "uv390.hh" #include "md2017.hh" #include "dm1701.hh" #include "dr1801uv.hh" #include "opengd77.hh" #include "openuv380.hh" #include "d868uv.hh" #include "d878uv.hh" #include "d878uv2.hh" #include "d578uv.hh" #include "dmr6x2uv.hh" #include "config.hh" #include "configcopyvisitor.hh" #include "logger.hh" #include /* ******************************************************************************************** * * Implementation of Radio * ******************************************************************************************** */ Radio::Radio(QObject *parent) : QThread(parent), _task(StatusIdle) { // pass... } Radio::~Radio() { // pass... } const CallsignDB * Radio::callsignDB() const { return nullptr; } CallsignDB * Radio::callsignDB() { return nullptr; } Radio * Radio::detect(const USBDeviceDescriptor &descr, const RadioInfo &force, const ErrorStack &err) { if (! descr.isValid()) { errMsg(err) << "Cannot detect radio: Invalid interface descriptor."; return nullptr; } logDebug() << "Try to detect radio at " << descr.description() << "."; if (AnytoneInterface::interfaceInfo() == descr) { AnytoneInterface *anytone = new AnytoneInterface(descr, err); if (anytone->isOpen()) { RadioInfo id = anytone->identifier(err); if ((id.isValid() && (RadioInfo::D868UVE == id.id())) || (force.isValid() && (RadioInfo::D868UVE == force.id()))) { return new D868UV(anytone); } else if ((id.isValid() && (RadioInfo::D878UV == id.id())) || (force.isValid() && (RadioInfo::D878UV == force.id()))) { return new D878UV(anytone); } else if ((id.isValid() && (RadioInfo::D878UVII == id.id())) || (force.isValid() && (RadioInfo::D878UVII == force.id()))) { return new D878UV2(anytone); } else if ((id.isValid() && (RadioInfo::D578UV == id.id())) || (force.isValid() && (RadioInfo::D578UV == force.id()))) { return new D578UV(anytone); } else if ((id.isValid() && (RadioInfo::DMR6X2UV == id.id())) || (force.isValid() && (RadioInfo::DMR6X2UV == force.id()))) { return new DMR6X2UV(anytone); } else if (id.isValid()) { errMsg(err) << tr("Unhandled device %1 '%2'. Device known but not implemented yet.") .arg(id.manufacturer()) .arg(id.name()); } else { errMsg(err) << tr("Unknown AnyTone (or similar) device."); } anytone->close(); anytone->deleteLater(); return nullptr; } anytone->deleteLater(); } else if (OpenGD77Interface::interfaceInfo() == descr) { OpenGD77Interface *ogd77 = new OpenGD77Interface(descr, err); if (ogd77->isOpen()) { RadioInfo id = ogd77->identifier(); if ((id.isValid() && (RadioInfo::OpenGD77 == id.id())) || (force.isValid() && (RadioInfo::OpenGD77 == force.id()))) { return new OpenGD77(ogd77); } else if ((id.isValid() && (RadioInfo::OpenUV380 == id.id())) || (force.isValid() && (RadioInfo::OpenUV380 == force.id()))) { return new OpenUV380(ogd77); } else { errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name() << ". Device known but not implemented yet."; } ogd77->close(); ogd77->deleteLater(); return nullptr; } ogd77->deleteLater(); } else if (TyTInterface::interfaceInfo() == descr) { TyTInterface *dfu = new TyTInterface(descr, err); if (dfu->isOpen()) { RadioInfo id = dfu->identifier(); if ((id.isValid() && (RadioInfo::MD390 == id.id())) || (force.isValid() && (RadioInfo::MD390 == force.id()))) { return new MD390(dfu); } else if ((id.isValid() && (RadioInfo::UV390 == id.id())) || (force.isValid() && (RadioInfo::UV390 == force.id()))) { return new UV390(dfu); } else if ((id.isValid() && (RadioInfo::MD2017 == id.id())) || (force.isValid() && (RadioInfo::MD2017 == force.id()))) { return new MD2017(dfu); } else if ((id.isValid() && (RadioInfo::DM1701 == id.id())) || (force.isValid() && (RadioInfo::DM1701 == force.id()))) { logDebug() << "Create DM-1701 radio object."; return new DM1701(dfu); } else { errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name() << ". Device known but not implemented yet."; } dfu->close(); dfu->deleteLater(); return nullptr; } dfu->deleteLater(); } else if (RadioddityInterface::interfaceInfo() == descr) { RadioddityInterface *hid = new RadioddityInterface(descr, err); if (hid->isOpen()) { RadioInfo id = hid->identifier(); if ((id.isValid() && (RadioInfo::RD5R == id.id())) || (force.isValid() && (RadioInfo::RD5R == force.id()))) { return new RD5R(hid); } else if ((id.isValid() && (RadioInfo::GD77 == id.id())) || (force.isValid() && (RadioInfo::GD77 == force.id()))) { return new GD77(hid); } else if (id.isValid()) { errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name() << ". Device known but not implemented yet."; } else { errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name() << ". Device not known."; } hid->close(); hid->deleteLater(); return nullptr; } hid->deleteLater(); } else if (DR1801UVInterface::interfaceInfo() == descr) { DR1801UVInterface *dif = new DR1801UVInterface(descr, err); if (dif->isOpen()) { RadioInfo id = dif->identifier(err); if (((id.isValid()) && (RadioInfo::DR1801UV == id.id())) || (force.isValid() && (RadioInfo::DR1801UV==force.id()))) { return new DR1801UV(dif); } else if (id.isValid()) { errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name() << ". Device known but not implemented yet."; } else { errMsg(err) << "Unknown device or failed connection to the device."; } dif->close(); dif->deleteLater(); return nullptr; } dif->deleteLater(); } else if (C7000Device::interfaceInfo() == descr) { GD73Interface *gdif = new GD73Interface(descr, err); if (gdif->isOpen()) { RadioInfo id = gdif->identifier(); if ((id.isValid() && (RadioInfo::GD73 == id.id())) || (force.isValid() && (RadioInfo::GD73 == force.id()))) { return new GD73(gdif); } else { errMsg(err) << "Unhandled device " << id.manufacturer() << " " << id.name() << ". Device known but not implemented yet."; } gdif->close(); gdif->deleteLater(); return nullptr; } gdif->deleteLater(); } return nullptr; } Radio::Status Radio::status() const { return _task; } const ErrorStack & Radio::errorStack() const { return _errorStack; } qdmr-0.12.3/lib/radio.hh000066400000000000000000000107571501654372000147420ustar00rootroot00000000000000/** @defgroup dsc Supported devices * This module collects all classes are device specific. * * That is, implementing device specific configurations, aka codeplugs as well as the specific * communication with these radios. */ #ifndef RADIO_HH #define RADIO_HH #include #include "radioinfo.hh" #include "radiointerface.hh" #include "codeplug.hh" #include "userdatabase.hh" #include "callsigndb.hh" #include "errorstack.hh" #include "config.hh" class RadioLimits; /** Base class for all Radio objects. * * The radio objects represents a connected radio. This class controlles the communication * with the device as well as the conversion between device specific code-plugs and generic * configurations. * * @ingroup rif */ class Radio : public QThread { Q_OBJECT public: /** Possible states of the radio object. */ typedef enum { StatusIdle, ///< Idle, nothing to do. StatusDownload, ///< Downloading codeplug. StatusUpload, ///< Uploading codeplug. StatusUploadCallsigns, ///< Uploading codeplug. StatusUploadSatellites, ///< Uploading satellite config. StatusError ///< An error occurred. } Status; public: /** Default constructor. */ explicit Radio(QObject *parent = nullptr); virtual ~Radio(); /** Returns the name of the radio (e.g., device identifier). */ virtual const QString &name() const = 0; /** Returns the limits for this radio. * * Call @c RadioLimits::verifyConfig to verify a codeplug with respect to a radio. * * @since Version 0.10.2 */ virtual const RadioLimits &limits() const = 0; /** Returns the codeplug instance. */ virtual const Codeplug &codeplug() const = 0; /** Returns the codeplug instance. */ virtual Codeplug &codeplug() = 0; /** Returns the call-sign DB instance. */ virtual const CallsignDB *callsignDB() const; /** Returns the call-sign DB instance. */ virtual CallsignDB *callsignDB(); /** Returns the current status. */ Status status() const; /** Returns the error stack, passed to @c startDownload, @c startUpload or * @c startUploadCallsignDB. It contains the error messages from the upload/download process. */ const ErrorStack &errorStack() const; public: /** Tries to detect the radio connected to the specified interface or constructs the specified * radio using the @c RadioInfo passed by @c force. */ static Radio *detect(const USBDeviceDescriptor &descr, const RadioInfo &force=RadioInfo(), const ErrorStack &err=ErrorStack()); public slots: /** Starts the download of the codeplug. * Once the download finished, the codeplug can be accessed and decoded using * the @c codeplug() method. */ virtual bool startDownload(bool blocking=false, const ErrorStack &err=ErrorStack()) = 0; /** Derives the device-specific codeplug from the generic configuration and uploads that * codeplug to the radio. */ virtual bool startUpload( Config *config, bool blocking=false, const Codeplug::Flags &flags = Codeplug::Flags(), const ErrorStack &err=ErrorStack()) = 0; /** Assembles the callsign DB from the given one and uploads it to the device. */ virtual bool startUploadCallsignDB( UserDatabase *db, bool blocking=false, const CallsignDB::Selection &selection=CallsignDB::Selection(), const ErrorStack &err=ErrorStack()) = 0; /** Assembles the satellite config and writes it to the device. */ virtual bool startUploadSatelliteConfig( SatelliteDatabase *db, bool blocking=false, const ErrorStack &err=ErrorStack()) = 0; signals: /** Gets emitted once the codeplug download has been started. */ void downloadStarted(); /** Gets emitted on download progress (e.g., for progress bars). */ void downloadProgress(int percent); /** Gets emitted once the codeplug download has been finished. */ void downloadFinished(Radio *radio, Codeplug *codeplug); /** Gets emitted if there was an error during the codeplug download. */ void downloadError(Radio *radio); /** Gets emitted once the codeplug upload has been started. */ void uploadStarted(); /** Gets emitted on upload progress (e.g., for progress bars). */ void uploadProgress(int percent); /** Gets emitted if there was an error during the upload. */ void uploadError(Radio *radio); /** Gets emitted once the codeplug upload has been completed successfully. */ void uploadComplete(Radio *radio); protected: /** The current state/task. */ Status _task; /** The error stack. */ ErrorStack _errorStack; }; #endif // RADIO_HH qdmr-0.12.3/lib/radioddity_codeplug.cc000066400000000000000000002675561501654372000176630ustar00rootroot00000000000000#include "radioddity_codeplug.hh" #include "utils.hh" #include "logger.hh" #include "scanlist.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "zone.hh" #include "config.hh" #include "commercial_extension.hh" #include "intermediaterepresentation.hh" /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::ChannelElement * ********************************************************************************************* */ RadioddityCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } RadioddityCodeplug::ChannelElement::ChannelElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RadioddityCodeplug::ChannelElement::~ChannelElement() { // pass... } void RadioddityCodeplug::ChannelElement::clear() { setName(""); setRXFrequency(0); setTXFrequency(0); setMode(MODE_ANALOG); setUInt8(0x0019, 0x00); setUInt8(0x001a, 0x00); setTXTimeOut(0); setTXTimeOutRekeyDelay(0); setAdmitCriterion(ADMIT_ALWAYS); setUInt8(0x001e, 0x50); setScanListIndex(0x00); setRXTone(SelectiveCall()); setTXTone(SelectiveCall()); setUInt8(0x0024, 0x00); setTXSignalingIndex(0); setUInt8(0x0026, 0x00); setRXSignalingIndex(0); setUInt8(0x0028, 0x16); setPrivacyGroup(PRIVGR_NONE); setTXColorCode(0); setGroupListIndex(0); setRXColorCode(0); setEmergencySystemIndex(0); setContactIndex(0); setUInt32_be(0x0030, 0); // clear all bitfields at once. setUInt8(0x0034, 0); setUInt8(0x0035, 0); setUInt8(0x0036, 0); } QString RadioddityCodeplug::ChannelElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0xff); } void RadioddityCodeplug::ChannelElement::setName(const QString &n) { writeASCII(Offset::name(), n, Limit::nameLength(), 0xff); } uint32_t RadioddityCodeplug::ChannelElement::rxFrequency() const { return getBCD8_le(Offset::rxFrequency())*10; } void RadioddityCodeplug::ChannelElement::setRXFrequency(uint32_t freq) { setBCD8_le(Offset::rxFrequency(), freq/10); } uint32_t RadioddityCodeplug::ChannelElement::txFrequency() const { return getBCD8_le(Offset::txFrequency())*10; } void RadioddityCodeplug::ChannelElement::setTXFrequency(uint32_t freq) { setBCD8_le(Offset::txFrequency(), freq/10); } RadioddityCodeplug::ChannelElement::Mode RadioddityCodeplug::ChannelElement::mode() const { return (Mode)getUInt8(Offset::mode()); } void RadioddityCodeplug::ChannelElement::setMode(Mode mode) { setUInt8(Offset::mode(), (unsigned)mode); } unsigned RadioddityCodeplug::ChannelElement::txTimeOut() const { return getUInt8(Offset::txTimeout())*15; } void RadioddityCodeplug::ChannelElement::setTXTimeOut(unsigned tot) { setUInt8(Offset::txTimeout(), tot/15); } unsigned RadioddityCodeplug::ChannelElement::txTimeOutRekeyDelay() const { return getUInt8(Offset::txTimeoutRekeyDelay()); } void RadioddityCodeplug::ChannelElement::setTXTimeOutRekeyDelay(unsigned delay) { setUInt8(Offset::txTimeoutRekeyDelay(), delay); } RadioddityCodeplug::ChannelElement::Admit RadioddityCodeplug::ChannelElement::admitCriterion() const { return (Admit) getUInt8(Offset::admitCriterion()); } void RadioddityCodeplug::ChannelElement::setAdmitCriterion(Admit admit) { setUInt8(Offset::admitCriterion(), (unsigned)admit); } bool RadioddityCodeplug::ChannelElement::hasScanList() const { return 0 != scanListIndex(); } unsigned RadioddityCodeplug::ChannelElement::scanListIndex() const { return getUInt8(Offset::scanList()); } void RadioddityCodeplug::ChannelElement::setScanListIndex(unsigned index) { setUInt8(Offset::scanList(), index); } SelectiveCall RadioddityCodeplug::ChannelElement::rxTone() const { return decode_ctcss_tone_table(getUInt16_le(Offset::rxTone())); } void RadioddityCodeplug::ChannelElement::setRXTone(const SelectiveCall &code) { setUInt16_le(Offset::rxTone(), encode_ctcss_tone_table(code)); } SelectiveCall RadioddityCodeplug::ChannelElement::txTone() const { return decode_ctcss_tone_table(getUInt16_le(Offset::txTone())); } void RadioddityCodeplug::ChannelElement::setTXTone(const SelectiveCall &code) { setUInt16_le(Offset::txTone(), encode_ctcss_tone_table(code)); } unsigned RadioddityCodeplug::ChannelElement::txSignalingIndex() const { return getUInt8(Offset::txSignaling()); } void RadioddityCodeplug::ChannelElement::setTXSignalingIndex(unsigned index) { setUInt8(Offset::txSignaling(), index); } unsigned RadioddityCodeplug::ChannelElement::rxSignalingIndex() const { return getUInt8(Offset::rxSignaling()); } void RadioddityCodeplug::ChannelElement::setRXSignalingIndex(unsigned index) { setUInt8(Offset::rxSignaling(), index); } RadioddityCodeplug::ChannelElement::PrivacyGroup RadioddityCodeplug::ChannelElement::privacyGroup() const { return (PrivacyGroup) getUInt8(Offset::privacyGroup()); } void RadioddityCodeplug::ChannelElement::setPrivacyGroup(PrivacyGroup grp) { setUInt8(Offset::privacyGroup(), (unsigned)grp); } unsigned RadioddityCodeplug::ChannelElement::txColorCode() const { return getUInt8(Offset::txColorCode()); } void RadioddityCodeplug::ChannelElement::setTXColorCode(unsigned cc) { setUInt8(Offset::txColorCode(), cc); } bool RadioddityCodeplug::ChannelElement::hasGroupList() const { return 0 != groupListIndex(); } unsigned RadioddityCodeplug::ChannelElement::groupListIndex() const { return getUInt8(Offset::groupList()); } void RadioddityCodeplug::ChannelElement::setGroupListIndex(unsigned index) { setUInt8(Offset::groupList(), index); } unsigned RadioddityCodeplug::ChannelElement::rxColorCode() const { return getUInt8(Offset::rxColorCode()); } void RadioddityCodeplug::ChannelElement::setRXColorCode(unsigned cc) { setUInt8(Offset::rxColorCode(), cc); } bool RadioddityCodeplug::ChannelElement::hasEmergencySystem() const { return 0 != emergencySystemIndex(); } unsigned RadioddityCodeplug::ChannelElement::emergencySystemIndex() const { return getUInt8(Offset::emergencySystem()); } void RadioddityCodeplug::ChannelElement::setEmergencySystemIndex(unsigned index) { setUInt8(Offset::emergencySystem(), index); } bool RadioddityCodeplug::ChannelElement::hasContact() const { return 0!=contactIndex(); } unsigned RadioddityCodeplug::ChannelElement::contactIndex() const { return getUInt16_le(Offset::transmitContact()); } void RadioddityCodeplug::ChannelElement::setContactIndex(unsigned index) { setUInt16_le(Offset::transmitContact(), index); } bool RadioddityCodeplug::ChannelElement::dataCallConfirm() const { return getBit(Offset::dataCallConfirm()); } void RadioddityCodeplug::ChannelElement::enableDataCallConfirm(bool enable) { setBit(Offset::dataCallConfirm(), enable); } bool RadioddityCodeplug::ChannelElement::emergencyAlarmACK() const { return getBit(Offset::emergencyAlarmACK()); } void RadioddityCodeplug::ChannelElement::enableEmergencyAlarmACK(bool enable) { setBit(Offset::emergencyAlarmACK(), enable); } bool RadioddityCodeplug::ChannelElement::privateCallConfirm() const { return getBit(Offset::privateCallConfirm()); } void RadioddityCodeplug::ChannelElement::enablePrivateCallConfirm(bool enable) { setBit(Offset::privateCallConfirm(), enable); } bool RadioddityCodeplug::ChannelElement::privacyEnabled() const { return getBit(Offset::privacyEnabled()); } void RadioddityCodeplug::ChannelElement::enablePrivacy(bool enable) { setBit(Offset::privacyEnabled(), enable); } DMRChannel::TimeSlot RadioddityCodeplug::ChannelElement::timeSlot() const { return (getBit(Offset::timeSlot()) ? DMRChannel::TimeSlot::TS2 : DMRChannel::TimeSlot::TS1); } void RadioddityCodeplug::ChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) { setBit(Offset::timeSlot(), DMRChannel::TimeSlot::TS2 == ts); } bool RadioddityCodeplug::ChannelElement::dualCapacityDirectMode() const { return getBit(Offset::dualCapacityDirectMode()); } void RadioddityCodeplug::ChannelElement::enableDualCapacityDirectMode(bool enable) { setBit(Offset::dualCapacityDirectMode(), enable); } bool RadioddityCodeplug::ChannelElement::nonSTEFrequency() const { return getBit(Offset::nonSTEFrequency()); } void RadioddityCodeplug::ChannelElement::enableNonSTEFrequency(bool enable) { setBit(Offset::nonSTEFrequency(), enable); } FMChannel::Bandwidth RadioddityCodeplug::ChannelElement::bandwidth() const { return (getBit(Offset::bandwidth()) ? FMChannel::Bandwidth::Wide : FMChannel::Bandwidth::Narrow); } void RadioddityCodeplug::ChannelElement::setBandwidth(FMChannel::Bandwidth bw) { setBit(Offset::bandwidth(), FMChannel::Bandwidth::Wide == bw); } bool RadioddityCodeplug::ChannelElement::rxOnly() const { return getBit(Offset::rxOnly()); } void RadioddityCodeplug::ChannelElement::enableRXOnly(bool enable) { setBit(Offset::rxOnly(), enable); } bool RadioddityCodeplug::ChannelElement::talkaround() const { return getBit(Offset::talkaround()); } void RadioddityCodeplug::ChannelElement::enableTalkaround(bool enable) { setBit(Offset::talkaround(), enable); } bool RadioddityCodeplug::ChannelElement::vox() const { return getBit(Offset::vox()); } void RadioddityCodeplug::ChannelElement::enableVOX(bool enable) { setBit(Offset::vox(), enable); } Channel::Power RadioddityCodeplug::ChannelElement::power() const { return (getBit(Offset::power()) ? Channel::Power::High : Channel::Power::Low); } void RadioddityCodeplug::ChannelElement::setPower(Channel::Power pwr) { switch (pwr) { case Channel::Power::Min: case Channel::Power::Low: clearBit(Offset::power()); break; case Channel::Power::Mid: case Channel::Power::High: case Channel::Power::Max: setBit(Offset::power()); break; } } Channel * RadioddityCodeplug::ChannelElement::toChannelObj(Codeplug::Context &ctx, const ErrorStack& err) const { Q_UNUSED(ctx); Q_UNUSED(err) Channel *ch = nullptr; if (MODE_ANALOG == mode()) { FMChannel *ach = new FMChannel(); ch = ach; switch (admitCriterion()) { case ADMIT_ALWAYS: ach->setAdmit(FMChannel::Admit::Always); break; case ADMIT_CH_FREE: ach->setAdmit(FMChannel::Admit::Free); break; default: ach->setAdmit(FMChannel::Admit::Always); break; } ach->setBandwidth(bandwidth()); ach->setRXTone(rxTone()); ach->setTXTone(txTone()); ach->setSquelchDefault(); // There is no per-channel squelch setting } else { DMRChannel *dch = new DMRChannel(); ch = dch; switch (admitCriterion()) { case ADMIT_ALWAYS: dch->setAdmit(DMRChannel::Admit::Always); break; case ADMIT_CH_FREE: dch->setAdmit(DMRChannel::Admit::Free); break; case ADMIT_COLOR: dch->setAdmit(DMRChannel::Admit::ColorCode); break; default: dch->setAdmit(DMRChannel::Admit::Always); break; } dch->setTimeSlot(timeSlot()); dch->setColorCode(txColorCode()); } // Apply common settings ch->setName(name()); ch->setRXFrequency(Frequency::fromHz(rxFrequency())); ch->setTXFrequency(Frequency::fromHz(txFrequency())); ch->setPower(power()); ch->setTimeout(txTimeOut()); ch->setRXOnly(rxOnly()); if (vox()) ch->setVOXDefault(); else ch->disableVOX(); // done. return ch; } bool RadioddityCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx, const ErrorStack& err) const { Q_UNUSED(err) // Link common if (hasScanList() && ctx.has(scanListIndex())) c->setScanList(ctx.get(scanListIndex())); // Link digital channel if (c->is()) { DMRChannel *dc = c->as(); if (hasGroupList() && ctx.has(groupListIndex())) dc->setGroupListObj(ctx.get(groupListIndex())); if (hasContact() && ctx.has(contactIndex())) dc->setTXContactObj(ctx.get(contactIndex())); } return true; } bool RadioddityCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx, const ErrorStack& err) { clear(); setName(c->name()); setRXFrequency(c->rxFrequency().inHz()); setTXFrequency(c->txFrequency().inHz()); if (c->defaultPower()) setPower(ctx.config()->settings()->power()); else setPower(c->power()); if (c->defaultTimeout()) setTXTimeOut(ctx.config()->settings()->tot()); else setTXTimeOut(c->timeout()); enableRXOnly(c->rxOnly()); // Enable vox bool defaultVOXEnabled = (c->defaultVOX() && (!ctx.config()->settings()->voxDisabled())); bool channelVOXEnabled = (! (c->voxDisabled()||c->defaultVOX())); enableVOX(defaultVOXEnabled || channelVOXEnabled); if (c->scanList()) setScanListIndex(ctx.index(c->scanList())); if (c->is()) { const FMChannel *ac = c->as(); setMode(MODE_ANALOG); switch (ac->admit()) { case FMChannel::Admit::Always: setAdmitCriterion(ADMIT_ALWAYS); break; case FMChannel::Admit::Free: setAdmitCriterion(ADMIT_CH_FREE); break; default: setAdmitCriterion(ADMIT_ALWAYS); } setBandwidth(ac->bandwidth()); setRXTone(ac->rxTone()); setTXTone(ac->txTone()); // no per channel squelch setting } else if (c->is()) { const DMRChannel *dc = c->as(); setMode(MODE_DIGITAL); switch (dc->admit()) { case DMRChannel::Admit::Always: setAdmitCriterion(ADMIT_ALWAYS); break; case DMRChannel::Admit::Free: setAdmitCriterion(ADMIT_CH_FREE); break; case DMRChannel::Admit::ColorCode: setAdmitCriterion(ADMIT_COLOR); break; } setTimeSlot(dc->timeSlot()); setRXColorCode(dc->colorCode()); setTXColorCode(dc->colorCode()); if (dc->groupListObj()) setGroupListIndex(ctx.index(dc->groupListObj())); if (dc->txContactObj()) setContactIndex(ctx.index(dc->txContactObj())); } else { errMsg(err) << "Cannot encode channel of type '" << c->metaObject()->className() << "': Not supported by the radio."; return false; } return true; } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::ChannelBankElement * ********************************************************************************************* */ RadioddityCodeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RadioddityCodeplug::ChannelBankElement::~ChannelBankElement() { // pass... } void RadioddityCodeplug::ChannelBankElement::clear() { memset(_data, 0, size()); } bool RadioddityCodeplug::ChannelBankElement::isEnabled(unsigned idx) const { unsigned byte = Offset::bitmask() + idx/8, bit = idx%8; return getBit(byte, bit); } void RadioddityCodeplug::ChannelBankElement::enable(unsigned idx, bool enabled) { unsigned byte = Offset::bitmask() + idx/8, bit = idx%8; return setBit(byte, bit, enabled); } uint8_t * RadioddityCodeplug::ChannelBankElement::get(unsigned idx) const { return (_data+Offset::channels())+idx*ChannelElement::size(); } RadioddityCodeplug::ChannelElement RadioddityCodeplug::ChannelBankElement::channel(unsigned int n) { return ChannelElement((_data+Offset::channels())+n*ChannelElement::size()); } /* ******************************************************************************************** * * Implementation of RadioddityCodeplug::VFOChannelElement * ******************************************************************************************** */ RadioddityCodeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr, unsigned size) : ChannelElement(ptr, size) { // pass... } RadioddityCodeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr) : ChannelElement(ptr) { // pass... } void RadioddityCodeplug::VFOChannelElement::clear() { ChannelElement::clear(); setStepSize(12.5); setOffsetMode(OffsetMode::Off); setTXOffset(10.0); } QString RadioddityCodeplug::VFOChannelElement::name() const { return QString(); } void RadioddityCodeplug::VFOChannelElement::setName(const QString &name) { Q_UNUSED(name); ChannelElement::setName(""); } double RadioddityCodeplug::VFOChannelElement::stepSize() const { switch (StepSize(getUInt4(Offset::stepSize()))) { case StepSize::SS2_5kHz: return 2.5; case StepSize::SS5kHz: return 5; case StepSize::SS6_25kHz: return 6.25; case StepSize::SS10kHz: return 10.0; case StepSize::SS12_5kHz: return 12.5; case StepSize::SS20kHz: return 20; case StepSize::SS30kHz: return 30; case StepSize::SS50kHz: return 50; } return 12.5; } void RadioddityCodeplug::VFOChannelElement::setStepSize(double kHz) { if (2.5 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS2_5kHz); else if (5.0 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS5kHz); else if (6.25 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS6_25kHz); else if (10.0 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS10kHz); else if (12.5 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS12_5kHz); else if (20.0 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS20kHz); else if (30.0 >= kHz) setUInt4(Offset::stepSize(), (unsigned)StepSize::SS30kHz); else setUInt4(Offset::stepSize(), (unsigned)StepSize::SS50kHz); } RadioddityCodeplug::VFOChannelElement::OffsetMode RadioddityCodeplug::VFOChannelElement::offsetMode() const { return (OffsetMode)getUInt2(Offset::offsetMode()); } void RadioddityCodeplug::VFOChannelElement::setOffsetMode(OffsetMode mode) { setUInt2(Offset::offsetMode(), (unsigned)mode); } double RadioddityCodeplug::VFOChannelElement::txOffset() const { return ((double)getBCD4_le(Offset::txOffset()))/100; } void RadioddityCodeplug::VFOChannelElement::setTXOffset(double f) { setBCD4_le(Offset::txOffset(), (f*100)); } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::ContactElement * ********************************************************************************************* */ RadioddityCodeplug::ContactElement::ContactElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::ContactElement::ContactElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RadioddityCodeplug::ContactElement::~ContactElement() { // pass... } void RadioddityCodeplug::ContactElement::clear() { setName(""); setNumber(0); setType(DMRContact::GroupCall); enableRing(0); setRingStyle(0); setUInt8(0x017, 0x00); } bool RadioddityCodeplug::ContactElement::isValid() const { return (! name().isEmpty()); } QString RadioddityCodeplug::ContactElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0xff); } void RadioddityCodeplug::ContactElement::setName(const QString name) { writeASCII(Offset::name(), name, Limit::nameLength(), 0xff); } unsigned RadioddityCodeplug::ContactElement::number() const { return getBCD8_be(Offset::number()); } void RadioddityCodeplug::ContactElement::setNumber(unsigned id) { setBCD8_be(Offset::number(), id); } DMRContact::Type RadioddityCodeplug::ContactElement::type() const { switch (getUInt8(Offset::type())) { case 0: return DMRContact::GroupCall; case 1: return DMRContact::PrivateCall; case 2: return DMRContact::AllCall; default: break; } return DMRContact::PrivateCall; } void RadioddityCodeplug::ContactElement::setType(DMRContact::Type type) { switch (type) { case DMRContact::GroupCall: setUInt8(Offset::type(), 0); break; case DMRContact::PrivateCall: setUInt8(Offset::type(), 1); break; case DMRContact::AllCall: setUInt8(Offset::type(), 2); break; } } bool RadioddityCodeplug::ContactElement::ring() const { return 0x00 != getUInt8(Offset::ring()); } void RadioddityCodeplug::ContactElement::enableRing(bool enable) { setUInt8(Offset::ring(), enable ? 0x01 : 0x00); } unsigned RadioddityCodeplug::ContactElement::ringStyle() const { return getUInt8(Offset::ringStyle()); } void RadioddityCodeplug::ContactElement::setRingStyle(unsigned style) { style = std::min(style, Limit::ringStyle()); setUInt8(Offset::ringStyle(), style); } DMRContact * RadioddityCodeplug::ContactElement::toContactObj(Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx) if (! isValid()) { errMsg(err) << "Cannot create contact from an invalid element."; return nullptr; } return new DMRContact(type(), name(), number(), ring()); } bool RadioddityCodeplug::ContactElement::fromContactObj(const DMRContact *cont, Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err) setName(cont->name()); setNumber(cont->number()); setType(cont->type()); if (cont->ring()) { enableRing(true); setRingStyle(1); } else { enableRing(false); } return true; } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::DTMFContactElement * ********************************************************************************************* */ RadioddityCodeplug::DTMFContactElement::DTMFContactElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::DTMFContactElement::DTMFContactElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RadioddityCodeplug::DTMFContactElement::~DTMFContactElement() { // pass... } void RadioddityCodeplug::DTMFContactElement::clear() { memset(_data, 0xff, Limit::nameLength()); } bool RadioddityCodeplug::DTMFContactElement::isValid() const { return (! name().isEmpty()); } QString RadioddityCodeplug::DTMFContactElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0xff); } void RadioddityCodeplug::DTMFContactElement::setName(const QString &name) { writeASCII(Offset::name(), name, Limit::nameLength(), 0xff); } QString RadioddityCodeplug::DTMFContactElement::number() const { return readASCII(Offset::number(), Limit::numberLength(), 0xff); } void RadioddityCodeplug::DTMFContactElement::setNumber(const QString &number) { writeASCII(Offset::number(), number, Limit::numberLength(), 0xff); } DTMFContact * RadioddityCodeplug::DTMFContactElement::toContactObj(Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx) if (! isValid()) { errMsg(err) << "Cannot create a DTMF contact from an invalid element."; return nullptr; } return new DTMFContact(name(), number()); } bool RadioddityCodeplug::DTMFContactElement::fromContactObj(const DTMFContact *cont, Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err) setName(cont->name()); setNumber(cont->number()); return true; } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::ZoneElement * ********************************************************************************************* */ RadioddityCodeplug::ZoneElement::ZoneElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::ZoneElement::ZoneElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RadioddityCodeplug::ZoneElement::~ZoneElement() { // pass... } void RadioddityCodeplug::ZoneElement::clear() { memset(_data+Offset::name(), 0xff, Limit::nameLength()); memset(_data+Offset::channels(), 0x00, sizeof(uint16_t)*Limit::memberCount()); } bool RadioddityCodeplug::ZoneElement::isValid() const { return (! name().isEmpty()); } QString RadioddityCodeplug::ZoneElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0xff); } void RadioddityCodeplug::ZoneElement::setName(const QString &name) { writeASCII(Offset::name(), name, Limit::nameLength(), 0xff); } bool RadioddityCodeplug::ZoneElement::hasMember(unsigned n) const { if (n >= Limit::memberCount()) return false; return (0 != member(n)); } unsigned RadioddityCodeplug::ZoneElement::member(unsigned n) const { if (n >= Limit::memberCount()) return 0; return getUInt16_le(Offset::channels()+Offset::betweenChannels()*n); } void RadioddityCodeplug::ZoneElement::setMember(unsigned n, unsigned idx) { if (n >= Limit::memberCount()) return; setUInt16_le(Offset::channels()+Offset::betweenChannels()*n, idx); } void RadioddityCodeplug::ZoneElement::clearMember(unsigned n) { setMember(n, 0); } Zone * RadioddityCodeplug::ZoneElement::toZoneObj(Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx) if (! isValid()) { errMsg(err) << "Cannot decode an invalid zone."; return nullptr; } return new Zone(name()); } bool RadioddityCodeplug::ZoneElement::linkZoneObj(Zone *zone, Context &ctx, const ErrorStack &err) const { if (! isValid()) { errMsg(err) << "Cannot link invalid zone."; return false; } for (unsigned int i=0; (i(member(i))) { zone->A()->add(ctx.get(member(i))); } else { logWarn() << "While linking zone '" << zone->name() << "': " << i <<"-th channel index " << member(i) << " out of bounds."; } } return true; } bool RadioddityCodeplug::ZoneElement::fromZoneObjA(const Zone *zone, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) if (zone->A()->count() && zone->B()->count()) setName(zone->name() + " A"); else setName(zone->name()); for (unsigned int i=0; iA()->count()) setMember(i, ctx.index(zone->A()->get(i))); else clearMember(i); } return true; } bool RadioddityCodeplug::ZoneElement::fromZoneObjB(const Zone *zone, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) if (zone->A()->count() && zone->B()->count()) setName(zone->name() + " B"); else setName(zone->name()); for (unsigned int i=0; iB()->count()) setMember(i, ctx.index(zone->B()->get(i))); else clearMember(i); } return true; } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::ZoneBankElement * ********************************************************************************************* */ RadioddityCodeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RadioddityCodeplug::ZoneBankElement::~ZoneBankElement() { // pass... } void RadioddityCodeplug::ZoneBankElement::clear() { memset(_data, 0, size()); } bool RadioddityCodeplug::ZoneBankElement::isEnabled(unsigned idx) const { unsigned byte=Offset::bitmap() + idx/8, bit = idx%8; return getBit(byte, bit); } void RadioddityCodeplug::ZoneBankElement::enable(unsigned idx, bool enabled) { unsigned byte=Offset::bitmap() + idx/8, bit = idx%8; setBit(byte, bit, enabled); } uint8_t * RadioddityCodeplug::ZoneBankElement::get(unsigned idx) const { return _data + Offset::zones() + idx*ZoneElement::size(); } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::GroupListElement * ********************************************************************************************* */ RadioddityCodeplug::GroupListElement::GroupListElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::GroupListElement::GroupListElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RadioddityCodeplug::GroupListElement::~GroupListElement() { // pass... } void RadioddityCodeplug::GroupListElement::clear() { setName(""); if ((Offset::members() + Offset::betweenMembers()*Limit::memberCount()) > _size) { logFatal() << "Cannot clear group list: Overflow."; return; } memset(_data+Offset::members(), 0, Offset::betweenMembers()*Limit::memberCount()); } QString RadioddityCodeplug::GroupListElement::name() const { return readASCII(Offset::name(), Limit::nameLength(), 0xff); } void RadioddityCodeplug::GroupListElement::setName(const QString &name) { writeASCII(Offset::name(), name, Limit::nameLength(), 0xff); } bool RadioddityCodeplug::GroupListElement::hasMember(unsigned n) const { if (n >= Limit::memberCount()) return false; return 0 != member(n); } unsigned RadioddityCodeplug::GroupListElement::member(unsigned n) const { return getUInt16_le(Offset::members() + sizeof(uint16_t)*n); } void RadioddityCodeplug::GroupListElement::setMember(unsigned n, unsigned idx) { return setUInt16_le(Offset::members() + sizeof(uint16_t)*n, idx); } void RadioddityCodeplug::GroupListElement::clearMember(unsigned n) { setMember(n,0); } RXGroupList * RadioddityCodeplug::GroupListElement::toRXGroupListObj(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err) return new RXGroupList(name()); } bool RadioddityCodeplug::GroupListElement::linkRXGroupListObj(unsigned int ncnt, RXGroupList *lst, Context &ctx, const ErrorStack &err) const { for (unsigned int i=0; (i(member(i))) { lst->addContact(ctx.get(member(i))); } else { errMsg(err) << "Cannot link group list '" << lst->name() << "': Member index " << member(i) << " does not refer to a digital contact."; return false; } } return true; } bool RadioddityCodeplug::GroupListElement::fromRXGroupListObj(const RXGroupList *lst, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) setName(lst->name()); int j = 0; // Iterate over all entries in the codeplug for (unsigned int i=0; icount() > j) { // Skip non-group-call entries while((lst->count() > j) && (DMRContact::GroupCall != lst->contact(j)->type())) { logWarn() << "Contact '" << lst->contact(i)->name() << "' in group list '" << lst->name() << "' is not a group call. Skip entry."; j++; } setMember(i, ctx.index(lst->contact(j))); j++; } else { // Clear entry. clearMember(i); } } return false; } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::GroupListBankElement * ********************************************************************************************* */ RadioddityCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RadioddityCodeplug::GroupListBankElement::~GroupListBankElement() { // pass... } void RadioddityCodeplug::GroupListBankElement::clear() { memset(_data, 0, Limit::groupListCount()); } bool RadioddityCodeplug::GroupListBankElement::isEnabled(unsigned n) const { return 0 != getUInt8(Offset::contactCounts() + n); } unsigned RadioddityCodeplug::GroupListBankElement::contactCount(unsigned n) const { return getUInt8(Offset::contactCounts() + n) - 1; } void RadioddityCodeplug::GroupListBankElement::setContactCount(unsigned n, unsigned size) { setUInt8(Offset::contactCounts() + n, size+1); } void RadioddityCodeplug::GroupListBankElement::disable(unsigned n) { setUInt8(Offset::contactCounts() + n, 0); } uint8_t * RadioddityCodeplug::GroupListBankElement::get(unsigned n) const { if ((Offset::groupLists() + (n+1)*GroupListElement::size())>_size) { logFatal() << "Cannot resolve group list at index " << n << ": Overflow."; return nullptr; } return _data + Offset::groupLists() + n*GroupListElement::size(); } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::ScanListElement * ********************************************************************************************* */ RadioddityCodeplug::ScanListElement::ScanListElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::ScanListElement::ScanListElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RadioddityCodeplug::ScanListElement::~ScanListElement() { // pass... } void RadioddityCodeplug::ScanListElement::clear() { setName(""); setUInt8(0x000f, 0xff); // Clear member memset(_data+0x0010, 0x00, 2*32); clearPrimary(); clearSecondary(); clearRevert(); setHoldTime(1000); setPrioritySampleTime(2000); } QString RadioddityCodeplug::ScanListElement::name() const { return readASCII(Offset::name(), Limit::name(), 0xff); } void RadioddityCodeplug::ScanListElement::setName(const QString &name) { writeASCII(Offset::name(), name, Limit::name(), 0xff); } bool RadioddityCodeplug::ScanListElement::channelMark() const { return getBit(Offset::channelMark()); } void RadioddityCodeplug::ScanListElement::enableChannelMark(bool enable) { setBit(Offset::channelMark(), enable); } RadioddityCodeplug::ScanListElement::Mode RadioddityCodeplug::ScanListElement::mode() const { return (Mode) getUInt2(Offset::mode()); } void RadioddityCodeplug::ScanListElement::setMode(Mode mode) { setUInt2(Offset::mode(), (unsigned)mode); } bool RadioddityCodeplug::ScanListElement::talkback() const { return getBit(Offset::talkback()); } void RadioddityCodeplug::ScanListElement::enableTalkback(bool enable) { setBit(Offset::talkback(), enable); } bool RadioddityCodeplug::ScanListElement::hasMember(unsigned n) const { return 0 != getUInt16_le(Offset::members()+Offset::betweenMembers()*n); } bool RadioddityCodeplug::ScanListElement::isSelected(unsigned n) const { return 1 == getUInt16_le(Offset::members()+Offset::betweenMembers()*n); } unsigned RadioddityCodeplug::ScanListElement::member(unsigned n) const { return getUInt16_le(Offset::members() + Offset::betweenMembers()*n)-1; } void RadioddityCodeplug::ScanListElement::setMember(unsigned n, unsigned idx) { setUInt16_le(Offset::members()+Offset::betweenMembers()*n, idx+1); } void RadioddityCodeplug::ScanListElement::setSelected(unsigned n) { setUInt16_le(Offset::members() + Offset::betweenMembers()*n, 1); } void RadioddityCodeplug::ScanListElement::clearMember(unsigned n) { setUInt16_le(Offset::members() + Offset::betweenMembers()*n, 0); } bool RadioddityCodeplug::ScanListElement::hasPrimary() const { return 0 != getUInt16_le(Offset::primary()); } bool RadioddityCodeplug::ScanListElement::primaryIsSelected() const { return 1 == getUInt16_le(Offset::primary()); } unsigned RadioddityCodeplug::ScanListElement::primary() const { return getUInt16_le(Offset::primary())-1; } void RadioddityCodeplug::ScanListElement::setPrimary(unsigned idx) { setUInt16_le(Offset::primary(), idx+1); } void RadioddityCodeplug::ScanListElement::setPrimarySelected() { setUInt16_le(Offset::primary(), 1); } void RadioddityCodeplug::ScanListElement::clearPrimary() { setUInt16_le(Offset::primary(), 0); } bool RadioddityCodeplug::ScanListElement::hasSecondary() const { return 0 != getUInt16_le(Offset::secondary()); } bool RadioddityCodeplug::ScanListElement::secondaryIsSelected() const { return 1 == getUInt16_le(Offset::secondary()); } unsigned RadioddityCodeplug::ScanListElement::secondary() const { return getUInt16_le(Offset::secondary())-1; } void RadioddityCodeplug::ScanListElement::setSecondary(unsigned idx) { setUInt16_le(Offset::secondary(), idx+1); } void RadioddityCodeplug::ScanListElement::setSecondarySelected() { setUInt16_le(Offset::secondary(), 1); } void RadioddityCodeplug::ScanListElement::clearSecondary() { setUInt16_le(Offset::secondary(), 0); } bool RadioddityCodeplug::ScanListElement::hasRevert() const { return 0 != getUInt16_le(Offset::revert()); } bool RadioddityCodeplug::ScanListElement::revertIsSelected() const { return 1 == getUInt16_le(Offset::revert()); } unsigned RadioddityCodeplug::ScanListElement::revert() const { return getUInt16_le(Offset::revert())-1; } void RadioddityCodeplug::ScanListElement::setRevert(unsigned idx) { setUInt16_le(Offset::revert(), idx+1); } void RadioddityCodeplug::ScanListElement::setRevertSelected() { setUInt16_le(Offset::revert(), 1); } void RadioddityCodeplug::ScanListElement::clearRevert() { setUInt16_le(Offset::revert(), 0); } unsigned RadioddityCodeplug::ScanListElement::holdTime() const { return unsigned(getUInt8(Offset::holdTime()))*25; } void RadioddityCodeplug::ScanListElement::setHoldTime(unsigned ms) { setUInt8(Offset::holdTime(), ms/25); } unsigned RadioddityCodeplug::ScanListElement::prioritySampleTime() const { return unsigned(getUInt8(Offset::primaryHoldTime()))*250; } void RadioddityCodeplug::ScanListElement::setPrioritySampleTime(unsigned ms) { setUInt8(Offset::primaryHoldTime(), ms/250); } ScanList * RadioddityCodeplug::ScanListElement::toScanListObj(Context &ctx, const ErrorStack &err) const { Q_UNUSED(ctx); Q_UNUSED(err) return new ScanList(name()); } bool RadioddityCodeplug::ScanListElement::linkScanListObj(ScanList *lst, Context &ctx, const ErrorStack &err) const { if (primaryIsSelected()) { lst->setPrimaryChannel(SelectedChannel::get()); } else if (hasPrimary()) { if (! ctx.has(primary())) { errMsg(err) << "Cannot link scan list '" << lst->name() << "', primary priority channel index " << primary() << " not defined."; return false; } lst->setPrimaryChannel(ctx.get(primary())); } if (secondaryIsSelected()) { lst->setSecondaryChannel(SelectedChannel::get()); } else if (hasSecondary()) { if (! ctx.has(secondary())) { errMsg(err) << "Cannot link scan list '" << lst->name() << "', secondary priority channel index " << secondary() << " not defined."; return false; } lst->setSecondaryChannel(ctx.get(secondary())); } if (revertIsSelected()) { lst->setRevertChannel(SelectedChannel::get()); } else if (hasRevert()) { if (! ctx.has(revert())) { errMsg(err) << "Cannot link scan list '" << lst->name() << "', revert channel index " << revert() << " not defined."; return false; } lst->setRevertChannel(ctx.get(revert())); } for (unsigned int i=0; (iaddChannel(SelectedChannel::get()); else if (hasMember(i)) { if (! ctx.has(member(i))) { errMsg(err) << "Cannot link scan list '" << lst->name() << "', " << (i+1) << "-th member index " << member(i) << " not defined."; return false; } lst->addChannel(ctx.get(member(i))); } } return true; } bool RadioddityCodeplug::ScanListElement::fromScanListObj(const ScanList *lst, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) clear(); setName(lst->name()); if (lst->primaryChannel() && (SelectedChannel::get() == lst->primaryChannel())) setPrimarySelected(); else if (lst->primaryChannel()) setPrimary(ctx.index(lst->primaryChannel())); if (lst->secondaryChannel() && (SelectedChannel::get() == lst->secondaryChannel())) setSecondarySelected(); else if (lst->secondaryChannel()) setSecondary(ctx.index(lst->secondaryChannel())); if (lst->revertChannel() && (SelectedChannel::get() == lst->revertChannel())) setRevertSelected(); else if (lst->revertChannel()) setRevert(ctx.index(lst->revertChannel())); for (unsigned int i=0; i= (unsigned int)lst->count()) clearMember(i); else if (SelectedChannel::get() == lst->channel(i)) setSelected(i); else setMember(i, ctx.index(lst->channel(i))); } return true; } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::ScanListBankElement * ********************************************************************************************* */ RadioddityCodeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RadioddityCodeplug::ScanListBankElement::~ScanListBankElement() { // pass... } void RadioddityCodeplug::ScanListBankElement::clear() { memset(_data, 0, Limit::scanListCount()); } bool RadioddityCodeplug::ScanListBankElement::isEnabled(unsigned n) const { return 0x00 != getUInt8(Offset::bytemap() + n); } void RadioddityCodeplug::ScanListBankElement::enable(unsigned n, bool enabled) { if (enabled) setUInt8(Offset::bytemap() + n, 0x01); else setUInt8(Offset::bytemap() + n, 0x00); } uint8_t * RadioddityCodeplug::ScanListBankElement::get(unsigned n) const { return _data+Offset::scanLists() + n*ScanListElement::size(); } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::GeneralSettingsElement * ********************************************************************************************* */ RadioddityCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pas... } RadioddityCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RadioddityCodeplug::GeneralSettingsElement::~GeneralSettingsElement() { // pass... } void RadioddityCodeplug::GeneralSettingsElement::clear() { memset(_data+0x0000, 0xff, 8); memset(_data+0x0008, 0x00, 4); setUInt32_be(0x000c, 0); setUInt8(0x0010, 0); setPreambleDuration(360); setMonitorType(MonitorType::Silent); setVOXSensitivity(3); setLowBatteryWarnInterval(30); setCallAlertDuration(120); setLoneWorkerResponsePeriod(1); setLoneWorkerReminderPeriod(10); setGroupCallHangTime(3000); setPrivateCallHangTime(3000); enableDownChannelModeVFO(false); enableUpChannelModeVFO(false); enableResetTone(false); enableUnknownNumberTone(false); setARTSToneMode(ARTSTone::Once); enableDigitalTalkPermitTone(false); enableAnalogTalkPermitTone(false); enableSelftestTone(true); enableChannelFreeIndicationTone(false); setBit(0x001b, 4, false); disableAllTones(false); enableBatsaveRX(true); enableBatsavePreamble(true); setUInt5(0x001c, 0, 0); disableAllLEDs(false); inhibitQuickKeyOverride(false); setBit(0x001c, 7, true); setUInt3(0x001d, 0, 0); enableTXExitTone(false); enableTXOnActiveChannel(true); enableAnimation(false); setScanMode(ScanMode::Time); setRepeaterEndDelay(0); setRepeaterSTE(0); setUInt8(0x001f, 0); clearProgPassword(); } QString RadioddityCodeplug::GeneralSettingsElement::name() const { return readASCII(0x0000, 8, 0xff); } void RadioddityCodeplug::GeneralSettingsElement::setName(const QString &name) { writeASCII(0x0000, name, 8, 0xff); } unsigned RadioddityCodeplug::GeneralSettingsElement::radioID() const { return getBCD8_be(0x0008); } void RadioddityCodeplug::GeneralSettingsElement::setRadioID(unsigned id) { setBCD8_be(0x0008, id); } unsigned RadioddityCodeplug::GeneralSettingsElement::preambleDuration() const { return unsigned(getUInt8(0x0011)*60); } void RadioddityCodeplug::GeneralSettingsElement::setPreambleDuration(unsigned ms) { setUInt8(0x0011, ms/60); } RadioddityCodeplug::GeneralSettingsElement::MonitorType RadioddityCodeplug::GeneralSettingsElement::monitorType() const { return MonitorType(getUInt8(0x0012)); } void RadioddityCodeplug::GeneralSettingsElement::setMonitorType(MonitorType type) { setUInt8(0x0012, (unsigned)type); } unsigned RadioddityCodeplug::GeneralSettingsElement::voxSensitivity() const { return getUInt8(0x0013); } void RadioddityCodeplug::GeneralSettingsElement::setVOXSensitivity(unsigned value) { value = std::min(10u, std::max(1u, value)); setUInt8(0x0013, value); } unsigned RadioddityCodeplug::GeneralSettingsElement::lowBatteryWarnInterval() const { return unsigned(getUInt8(0x0014))*5; } void RadioddityCodeplug::GeneralSettingsElement::setLowBatteryWarnInterval(unsigned sec) { setUInt8(0x0014, sec/5); } unsigned RadioddityCodeplug::GeneralSettingsElement::callAlertDuration() const { return unsigned(getUInt8(0x0015))*5; } void RadioddityCodeplug::GeneralSettingsElement::setCallAlertDuration(unsigned sec) { setUInt8(0x0015, sec/5); } unsigned RadioddityCodeplug::GeneralSettingsElement::loneWorkerResponsePeriod() const { return getUInt8(0x0016); } void RadioddityCodeplug::GeneralSettingsElement::setLoneWorkerResponsePeriod(unsigned min) { setUInt8(0x0016, min); } unsigned RadioddityCodeplug::GeneralSettingsElement::loneWorkerReminderPeriod() const { return getUInt8(0x0017); } void RadioddityCodeplug::GeneralSettingsElement::setLoneWorkerReminderPeriod(unsigned sec) { setUInt8(0x0017, sec); } unsigned RadioddityCodeplug::GeneralSettingsElement::groupCallHangTime() const { return unsigned(getUInt8(0x0018))*500; } void RadioddityCodeplug::GeneralSettingsElement::setGroupCallHangTime(unsigned ms) { setUInt8(0x0018, ms/500); } unsigned RadioddityCodeplug::GeneralSettingsElement::privateCallHangTime() const { return unsigned(getUInt8(0x0019))*500; } void RadioddityCodeplug::GeneralSettingsElement::setPrivateCallHangTime(unsigned ms) { setUInt8(0x0019, ms/500); } bool RadioddityCodeplug::GeneralSettingsElement::downChannelModeVFO() const { return getBit(0x001a, 0); } void RadioddityCodeplug::GeneralSettingsElement::enableDownChannelModeVFO(bool enable) { setBit(0x001a, 0, enable); } bool RadioddityCodeplug::GeneralSettingsElement::upChannelModeVFO() const { return getBit(0x001a, 1); } void RadioddityCodeplug::GeneralSettingsElement::enableUpChannelModeVFO(bool enable) { setBit(0x001a, 1, enable); } bool RadioddityCodeplug::GeneralSettingsElement::resetTone() const { return getBit(0x001a, 2); } void RadioddityCodeplug::GeneralSettingsElement::enableResetTone(bool enable) { setBit(0x001a, 2, enable); } bool RadioddityCodeplug::GeneralSettingsElement::unknownNumberTone() const { return getBit(0x001a, 3); } void RadioddityCodeplug::GeneralSettingsElement::enableUnknownNumberTone(bool enable) { setBit(0x001a, 3, enable); } RadioddityCodeplug::GeneralSettingsElement::ARTSTone RadioddityCodeplug::GeneralSettingsElement::artsToneMode() const { return ARTSTone(getUInt4(0x001a, 4)); } void RadioddityCodeplug::GeneralSettingsElement::setARTSToneMode(ARTSTone mode) { setUInt4(0x001a, 4, (unsigned) mode); } bool RadioddityCodeplug::GeneralSettingsElement::digitalTalkPermitTone() const { return getBit(0x001b, 0); } void RadioddityCodeplug::GeneralSettingsElement::enableDigitalTalkPermitTone(bool enable) { setBit(0x001b, 0, enable); } bool RadioddityCodeplug::GeneralSettingsElement::analogTalkPermitTone() const { return getBit(0x001b, 1); } void RadioddityCodeplug::GeneralSettingsElement::enableAnalogTalkPermitTone(bool enable) { setBit(0x001b, 1, enable); } bool RadioddityCodeplug::GeneralSettingsElement::selftestTone() const { return getBit(0x001b, 2); } void RadioddityCodeplug::GeneralSettingsElement::enableSelftestTone(bool enable) { setBit(0x001b, 2, enable); } bool RadioddityCodeplug::GeneralSettingsElement::channelFreeIndicationTone() const { return getBit(0x001b, 3); } void RadioddityCodeplug::GeneralSettingsElement::enableChannelFreeIndicationTone(bool enable) { setBit(0x001b, 3, enable); } bool RadioddityCodeplug::GeneralSettingsElement::allTonesDisabled() const { return getBit(0x001b, 5); } void RadioddityCodeplug::GeneralSettingsElement::disableAllTones(bool disable) { setBit(0x001b, 5, disable); } bool RadioddityCodeplug::GeneralSettingsElement::batsaveRX() const { return getBit(0x001b, 6); } void RadioddityCodeplug::GeneralSettingsElement::enableBatsaveRX(bool enable) { setBit(0x001b, 6, enable); } bool RadioddityCodeplug::GeneralSettingsElement::batsavePreamble() const { return getBit(0x001b, 7); } void RadioddityCodeplug::GeneralSettingsElement::enableBatsavePreamble(bool enable) { setBit(0x001b, 7, enable); } bool RadioddityCodeplug::GeneralSettingsElement::allLEDsDisabled() const { return getBit(0x001c, 5); } void RadioddityCodeplug::GeneralSettingsElement::disableAllLEDs(bool disable) { setBit(0x001c, 5, disable); } bool RadioddityCodeplug::GeneralSettingsElement::quickKeyOverrideInhibited() const { return getBit(0x001c, 6); } void RadioddityCodeplug::GeneralSettingsElement::inhibitQuickKeyOverride(bool inhibit) { setBit(0x001c, 6, inhibit); } bool RadioddityCodeplug::GeneralSettingsElement::txExitTone() const { return getBit(0x001d, 3); } void RadioddityCodeplug::GeneralSettingsElement::enableTXExitTone(bool enable) { setBit(0x001d, 3, enable); } bool RadioddityCodeplug::GeneralSettingsElement::txOnActiveChannel() const { return getBit(0x001d, 4); } void RadioddityCodeplug::GeneralSettingsElement::enableTXOnActiveChannel(bool enable) { setBit(0x001d, 4, enable); } bool RadioddityCodeplug::GeneralSettingsElement::animation() const { return getBit(0x001d, 5); } void RadioddityCodeplug::GeneralSettingsElement::enableAnimation(bool enable) { setBit(0x001d, 5, enable); } RadioddityCodeplug::GeneralSettingsElement::ScanMode RadioddityCodeplug::GeneralSettingsElement::scanMode() const { return ScanMode(getUInt2(0x001d, 6)); } void RadioddityCodeplug::GeneralSettingsElement::setScanMode(ScanMode mode) { setUInt2(0x001d, 6, unsigned(mode)); } unsigned RadioddityCodeplug::GeneralSettingsElement::repeaterEndDelay() const { return getUInt4(0x001e, 0); } void RadioddityCodeplug::GeneralSettingsElement::setRepeaterEndDelay(unsigned delay) { setUInt4(0x001e, 0, delay); } unsigned RadioddityCodeplug::GeneralSettingsElement::repeaterSTE() const { return getUInt4(0x001e, 4); } void RadioddityCodeplug::GeneralSettingsElement::setRepeaterSTE(unsigned ste) { setUInt4(0x001e, 4, ste); } bool RadioddityCodeplug::GeneralSettingsElement::hasProgPassword() const { return (0xff != _data[0x0020]) && (0x00 != _data[0x0020]); } QString RadioddityCodeplug::GeneralSettingsElement::progPassword() const { return readASCII(0x0020, 8, 0xff); } void RadioddityCodeplug::GeneralSettingsElement::setProgPassword(const QString &pwd) { writeASCII(0x0020, pwd, 8, 0xff); } void RadioddityCodeplug::GeneralSettingsElement::clearProgPassword() { memset(_data+0x0020, 0xff, 8); } bool RadioddityCodeplug::GeneralSettingsElement::fromConfig(Context &ctx, const ErrorStack &err) { if (! ctx.config()->settings()->defaultIdRef()->isNull()) { setName(ctx.config()->settings()->defaultIdRef()->as()->name()); setRadioID(ctx.config()->settings()->defaultIdRef()->as()->number()); } else if (ctx.config()->radioIDs()->count()) { setName(ctx.config()->radioIDs()->getId(0)->name()); setRadioID(ctx.config()->radioIDs()->getId(0)->number()); } else { errMsg(err) << "Cannot encode radioddity codeplug: No radio ID defined."; return false; } setVOXSensitivity(ctx.config()->settings()->vox()); // There is no global squelch settings either // Handle Radioddity extension if (RadiodditySettingsExtension *ext = ctx.config()->settings()->radioddityExtension()) { setPreambleDuration(ext->preambleDuration().milliseconds()); setMonitorType(ext->monitorType()); setLowBatteryWarnInterval(ext->tone()->lowBatteryWarnInterval().seconds()); setCallAlertDuration(ext->tone()->callAlertDuration().seconds()); setLoneWorkerResponsePeriod(ext->loneWorkerResponseTime().minutes()); setLoneWorkerReminderPeriod(ext->loneWorkerReminderPeriod().seconds()); setGroupCallHangTime(ext->groupCallHangTime().milliseconds()); setPrivateCallHangTime(ext->privateCallHangTime().milliseconds()); enableDownChannelModeVFO(ext->downChannelModeVFO()); enableUpChannelModeVFO(ext->upChannelModeVFO()); enableResetTone(ext->tone()->resetTone()); enableUnknownNumberTone(ext->tone()->unknownNumberTone()); setARTSToneMode(ext->tone()->artsToneMode()); enableDigitalTalkPermitTone(ext->tone()->digitalTalkPermitTone()); enableAnalogTalkPermitTone(ext->tone()->analogTalkPermitTone()); enableSelftestTone(ext->tone()->selftestTone()); enableChannelFreeIndicationTone(ext->tone()->channelFreeIndicationTone()); disableAllTones(ext->tone()->allTonesDisabled()); enableBatsaveRX(ext->powerSaveMode()); enableBatsavePreamble(ext->wakeupPreamble()); disableAllLEDs(ext->allLEDsDisabled()); inhibitQuickKeyOverride(ext->quickKeyOverrideInhibited()); enableTXExitTone(ext->tone()->txExitTone()); enableTXOnActiveChannel(ext->txOnActiveChannel()); enableAnimation(RadioddityBootSettingsExtension::DisplayMode::Image == ext->boot()->display()); setScanMode(ext->scanMode()); setRepeaterEndDelay(ext->repeaterEndDelay().seconds()); setRepeaterSTE(ext->repeaterSTE().seconds()); if (ext->boot()->progPassword().isEmpty()) clearProgPassword(); else setProgPassword(ext->boot()->progPassword()); } return true; } bool RadioddityCodeplug::GeneralSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) if (ctx.config()->settings()->defaultIdRef()->isNull()) { int idx = ctx.config()->radioIDs()->add(new DMRRadioID(name(), radioID())); ctx.config()->settings()->defaultIdRef()->set(ctx.config()->radioIDs()->getId(idx)); } else { ctx.config()->settings()->defaultIdRef()->as()->setName(name()); ctx.config()->settings()->defaultIdRef()->as()->setNumber(radioID()); } ctx.config()->settings()->setVOX(voxSensitivity()); // There is no global squelch settings either, so set it to 1 ctx.config()->settings()->setSquelch(1); // Allocate Radioddity extension if needed RadiodditySettingsExtension *ext = ctx.config()->settings()->radioddityExtension(); if (nullptr == ext) { ext = new RadiodditySettingsExtension(); ctx.config()->settings()->setRadioddityExtension(ext); } // Update settings extension ext->setPreambleDuration(Interval::fromMilliseconds(preambleDuration())); ext->setMonitorType(monitorType()); ext->tone()->setLowBatteryWarnInterval(Interval::fromSeconds(lowBatteryWarnInterval())); ext->tone()->setCallAlertDuration(Interval::fromSeconds(callAlertDuration())); ext->setLoneWorkerResponseTime(Interval::fromMinutes(loneWorkerResponsePeriod())); ext->setLoneWorkerReminderPeriod(Interval::fromSeconds(loneWorkerReminderPeriod())); ext->setGroupCallHangTime(Interval::fromMilliseconds(groupCallHangTime())); ext->setPrivateCallHangTime(Interval::fromMilliseconds(privateCallHangTime())); ext->enableDownChannelModeVFO(downChannelModeVFO()); ext->enableUpChannelModeVFO(upChannelModeVFO()); ext->tone()->enableResetTone(resetTone()); ext->tone()->enableUnknownNumberTone(unknownNumberTone()); ext->tone()->setARTSToneMode(artsToneMode()); ext->tone()->enableDigitalTalkPermitTone(digitalTalkPermitTone()); ext->tone()->enableAnalogTalkPermitTone(analogTalkPermitTone()); ext->tone()->enableSelftestTone(selftestTone()); ext->tone()->enableChannelFreeIndicationTone(channelFreeIndicationTone()); ext->tone()->disableAllTones(allTonesDisabled()); ext->enablePowerSaveMode(batsaveRX()); ext->enableWakeupPreamble(batsavePreamble()); ext->disableAllLEDs(allLEDsDisabled()); ext->inhibitQuickKeyOverride(quickKeyOverrideInhibited()); ext->tone()->enableTXExitTone(txExitTone()); ext->enableTXOnActiveChannel(txOnActiveChannel()); ext->boot()->setDisplay(animation() ? RadioddityBootSettingsExtension::DisplayMode::Image: RadioddityBootSettingsExtension::DisplayMode::Text); ext->setScanMode(scanMode()); ext->setRepeaterEndDelay(Interval::fromSeconds(repeaterEndDelay())); ext->setRepeaterSTE(Interval::fromSeconds(repeaterSTE())); if (hasProgPassword()) ext->boot()->setProgPassword(progPassword()); else ext->boot()->setProgPassword(""); return true; } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::ButtonSettingsElement * ********************************************************************************************* */ uint8_t RadioddityCodeplug::ButtonSettingsElement::KeyFunction::encode(RadioddityButtonSettingsExtension::Function func) { switch (func) { case RadioddityButtonSettingsExtension::Function::None: return None; case RadioddityButtonSettingsExtension::Function::ToggleAllAlertTones: return ToggleAllAlertTones; case RadioddityButtonSettingsExtension::Function::EmergencyOn: return EmergencyOn; case RadioddityButtonSettingsExtension::Function::EmergencyOff: return EmergencyOff; case RadioddityButtonSettingsExtension::Function::ToggleMonitor: return ToggleMonitor; case RadioddityButtonSettingsExtension::Function::RadioDisable: return NuiaceDelete; case RadioddityButtonSettingsExtension::Function::OneTouch1: return OneTouch1; case RadioddityButtonSettingsExtension::Function::OneTouch2: return OneTouch2; case RadioddityButtonSettingsExtension::Function::OneTouch3: return OneTouch3; case RadioddityButtonSettingsExtension::Function::OneTouch4: return OneTouch4; case RadioddityButtonSettingsExtension::Function::OneTouch5: return OneTouch5; case RadioddityButtonSettingsExtension::Function::OneTouch6: return OneTouch6; case RadioddityButtonSettingsExtension::Function::ToggleTalkaround: return ToggleRepeatTalkaround; case RadioddityButtonSettingsExtension::Function::ToggleScan: return ToggleScan; case RadioddityButtonSettingsExtension::Function::ToggleEncryption: return TogglePrivacy; case RadioddityButtonSettingsExtension::Function::ToggleVox: return ToggleVox; case RadioddityButtonSettingsExtension::Function::ZoneSelect: return ZoneSelect; case RadioddityButtonSettingsExtension::Function::BatteryIndicator: return BatteryIndicator; case RadioddityButtonSettingsExtension::Function::ToggleLoneWorker: return ToggleLoneWorker; case RadioddityButtonSettingsExtension::Function::PhoneExit: return PhoneExit; case RadioddityButtonSettingsExtension::Function::ToggleFlashLight: return ToggleFlashLight; case RadioddityButtonSettingsExtension::Function::ToggleFMRadio: return ToggleFMRadio; default: break; } return Action::None; } RadioddityButtonSettingsExtension::Function RadioddityCodeplug::ButtonSettingsElement::KeyFunction::decode(uint8_t action) { switch ((Action) action) { case None: return RadioddityButtonSettingsExtension::Function::None; case ToggleAllAlertTones: return RadioddityButtonSettingsExtension::Function::ToggleAllAlertTones; case EmergencyOn: return RadioddityButtonSettingsExtension::Function::EmergencyOn; case EmergencyOff: return RadioddityButtonSettingsExtension::Function::EmergencyOff; case ToggleMonitor: return RadioddityButtonSettingsExtension::Function::ToggleMonitor; case NuiaceDelete: return RadioddityButtonSettingsExtension::Function::RadioDisable; case OneTouch1: return RadioddityButtonSettingsExtension::Function::OneTouch1; case OneTouch2: return RadioddityButtonSettingsExtension::Function::OneTouch2; case OneTouch3: return RadioddityButtonSettingsExtension::Function::OneTouch3; case OneTouch4: return RadioddityButtonSettingsExtension::Function::OneTouch4; case OneTouch5: return RadioddityButtonSettingsExtension::Function::OneTouch5; case OneTouch6: return RadioddityButtonSettingsExtension::Function::OneTouch6; case ToggleRepeatTalkaround: return RadioddityButtonSettingsExtension::Function::ToggleTalkaround; case ToggleScan: return RadioddityButtonSettingsExtension::Function::ToggleScan; case TogglePrivacy: return RadioddityButtonSettingsExtension::Function::ToggleEncryption; case ToggleVox: return RadioddityButtonSettingsExtension::Function::ToggleVox; case ZoneSelect: return RadioddityButtonSettingsExtension::Function::ZoneSelect; case BatteryIndicator: return RadioddityButtonSettingsExtension::Function::BatteryIndicator; case ToggleLoneWorker: return RadioddityButtonSettingsExtension::Function::ToggleLoneWorker; case PhoneExit: return RadioddityButtonSettingsExtension::Function::PhoneExit; case ToggleFlashLight: return RadioddityButtonSettingsExtension::Function::ToggleFlashLight; case ToggleFMRadio: return RadioddityButtonSettingsExtension::Function::ToggleFMRadio; } return RadioddityButtonSettingsExtension::Function::None; } RadioddityCodeplug::ButtonSettingsElement::ButtonSettingsElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::ButtonSettingsElement::ButtonSettingsElement(uint8_t *ptr) : Element(ptr, 0x0020) { // pass... } RadioddityCodeplug::ButtonSettingsElement::~ButtonSettingsElement() { // pass... } void RadioddityCodeplug::ButtonSettingsElement::clear() { setUInt8(0x0000, 0x01); setLongPressDuration(Interval::fromMilliseconds(1500)); setSK1ShortPress(RadioddityButtonSettingsExtension::Function::ZoneSelect); setSK1LongPress(RadioddityButtonSettingsExtension::Function::ToggleFMRadio); setSK2ShortPress(RadioddityButtonSettingsExtension::Function::ToggleMonitor); setSK2LongPress(RadioddityButtonSettingsExtension::Function::ToggleFlashLight); setTKShortPress(RadioddityButtonSettingsExtension::Function::BatteryIndicator); setTKLongPress(RadioddityButtonSettingsExtension::Function::ToggleVox); memset(_data+0x0008, 0xff, 6*4); } Interval RadioddityCodeplug::ButtonSettingsElement::longPressDuration() const { return Interval::fromMilliseconds(getUInt8(Offset::longPressDuration())*250); } void RadioddityCodeplug::ButtonSettingsElement::setLongPressDuration(Interval ms) { setUInt8(Offset::longPressDuration(), ms.milliseconds()/250); } RadioddityButtonSettingsExtension::Function RadioddityCodeplug::ButtonSettingsElement::sk1ShortPress() const { return KeyFunction::decode(getUInt8(Offset::sk1ShortPress())); } void RadioddityCodeplug::ButtonSettingsElement::setSK1ShortPress(RadioddityButtonSettingsExtension::Function action) { setUInt8(Offset::sk1ShortPress(), KeyFunction::encode(action)); } RadioddityButtonSettingsExtension::Function RadioddityCodeplug::ButtonSettingsElement::sk1LongPress() const { return KeyFunction::decode(getUInt8(0x0003)); } void RadioddityCodeplug::ButtonSettingsElement::setSK1LongPress(RadioddityButtonSettingsExtension::Function action) { setUInt8(Offset::sk1LongPress(), KeyFunction::encode(action)); } RadioddityButtonSettingsExtension::Function RadioddityCodeplug::ButtonSettingsElement::sk2ShortPress() const { return KeyFunction::decode(getUInt8(Offset::sk2ShortPress())); } void RadioddityCodeplug::ButtonSettingsElement::setSK2ShortPress(RadioddityButtonSettingsExtension::Function action) { setUInt8(Offset::sk2ShortPress(), KeyFunction::encode(action)); } RadioddityButtonSettingsExtension::Function RadioddityCodeplug::ButtonSettingsElement::sk2LongPress() const { return KeyFunction::decode(getUInt8(Offset::sk2LongPress())); } void RadioddityCodeplug::ButtonSettingsElement::setSK2LongPress(RadioddityButtonSettingsExtension::Function action) { setUInt8(Offset::sk2LongPress(), KeyFunction::encode(action)); } RadioddityButtonSettingsExtension::Function RadioddityCodeplug::ButtonSettingsElement::tkShortPress() const { return KeyFunction::decode(getUInt8(Offset::tkShortPress())); } void RadioddityCodeplug::ButtonSettingsElement::setTKShortPress(RadioddityButtonSettingsExtension::Function action) { setUInt8(Offset::tkShortPress(), KeyFunction::encode(action)); } RadioddityButtonSettingsExtension::Function RadioddityCodeplug::ButtonSettingsElement::tkLongPress() const { return KeyFunction::decode(getUInt8(Offset::tkLongPress())); } void RadioddityCodeplug::ButtonSettingsElement::setTKLongPress(RadioddityButtonSettingsExtension::Function action) { setUInt8(Offset::tkLongPress(), KeyFunction::encode(action)); } RadioddityCodeplug::ButtonSettingsElement::OneTouchAction RadioddityCodeplug::ButtonSettingsElement::oneTouchAction(unsigned n) const { return OneTouchAction(getUInt8(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 0)); } unsigned RadioddityCodeplug::ButtonSettingsElement::oneTouchContact(unsigned n) const { return getUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 1); } unsigned RadioddityCodeplug::ButtonSettingsElement::oneTouchMessage(unsigned n) const { return getUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 3); } void RadioddityCodeplug::ButtonSettingsElement::disableOneTouch(unsigned n) { setUInt8(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 0, (unsigned)OneTouchAction::None); } void RadioddityCodeplug::ButtonSettingsElement::setOneTouchDigitalCall(unsigned n, unsigned index) { setUInt8(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 0, (unsigned)OneTouchAction::DigitalCall); setUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 1, index); setUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 3, 0); } void RadioddityCodeplug::ButtonSettingsElement::setOneTouchDigitalMessage(unsigned n, unsigned index) { setUInt8(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 0, (unsigned)OneTouchAction::DigitalMessage); setUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 1, 0); setUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 3, index); } void RadioddityCodeplug::ButtonSettingsElement::setOneTouchAnalogCall(unsigned n) { setUInt8(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 0, (unsigned)OneTouchAction::AnalogCall); setUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 1, 0); setUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 3, 0); } bool RadioddityCodeplug::ButtonSettingsElement::encode(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) if (! ctx.config()->settings()->radioddityExtension()) return true; RadiodditySettingsExtension *ext = ctx.config()->settings()->radioddityExtension(); setLongPressDuration(ext->buttons()->longPressDuration()); setSK1ShortPress(ext->buttons()->funcKey1Short()); setSK1LongPress(ext->buttons()->funcKey1Long()); setSK2ShortPress(ext->buttons()->funcKey2Short()); setSK2LongPress(ext->buttons()->funcKey2Long()); setTKShortPress(ext->buttons()->funcKey3Short()); setTKLongPress(ext->buttons()->funcKey3Long()); return true; } bool RadioddityCodeplug::ButtonSettingsElement::decode(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) if (! ctx.config()->settings()->radioddityExtension()) ctx.config()->settings()->setRadioddityExtension(new RadiodditySettingsExtension()); RadiodditySettingsExtension *ext = ctx.config()->settings()->radioddityExtension(); ext->buttons()->setLongPressDuration(longPressDuration()); ext->buttons()->setFuncKey1Short(sk1ShortPress()); ext->buttons()->setFuncKey1Long(sk1LongPress()); ext->buttons()->setFuncKey2Short(sk2ShortPress()); ext->buttons()->setFuncKey2Long(sk2LongPress()); ext->buttons()->setFuncKey3Short(tkShortPress()); ext->buttons()->setFuncKey3Long(tkLongPress()); return true; } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::MenuSettingsElement * ********************************************************************************************* */ RadioddityCodeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr) : Element(ptr, 0x0008) { // pass... } RadioddityCodeplug::MenuSettingsElement::~MenuSettingsElement() { // pass... } void RadioddityCodeplug::MenuSettingsElement::clear() { setMenuHangTime(10); enableMessage(true); enableScanStart(true); enableCallAlert(true); enableEditContact(true); enableManualDial(true); enableRadioCheck(true); enableRemoteMonitor(true); enableRadioEnable(true); enableRadioDisable(true); enableProgPassword(true); enableTalkaround(true); enableTone(true); enablePower(true); enableBacklight(true); enableIntroScreen(true); enableKeypadLock(true); enableLEDIndicator(true); enableSquelch(true); enablePrivacy(true); enableVOX(true); enablePasswordLock(true); enableMissedCalls(true); enableAnsweredCalls(true); enableOutgoingCalls(true); enableChannelDisplay(true); enableDualWatch(true); setBit(0x0004, 3, 0); setBit(0x0004, 4, 0); setBit(0x0004, 5, 1); setBit(0x0004, 6, 1); setBit(0x0004, 7, 1); setUInt8(0x0005, 0xff); setKeypadLockTime(0); setBacklightTime(15); setUInt2(0x0006, 4, 0); setChannelDisplayMode(ChannelDisplayMode::Name); setUInt4(0x0007, 0, 0); setBit(0x0007, 4, 1); enableKeyTone(true); setDualWatchMode(DualWatchMode::DualDual); } unsigned RadioddityCodeplug::MenuSettingsElement::menuHangTime() const { return getUInt8(0x0000); } void RadioddityCodeplug::MenuSettingsElement::setMenuHangTime(unsigned sec) { sec = std::min(10u, sec); setUInt8(0x0000, sec); } bool RadioddityCodeplug::MenuSettingsElement::message() const { return getBit(0x0001, 0); } void RadioddityCodeplug::MenuSettingsElement::enableMessage(bool enable) { setBit(0x0001, 0, enable); } bool RadioddityCodeplug::MenuSettingsElement::scanStart() const { return getBit(0x0001, 1); } void RadioddityCodeplug::MenuSettingsElement::enableScanStart(bool enable) { setBit(0x0001, 1, enable); } bool RadioddityCodeplug::MenuSettingsElement::editScanList() const { return getBit(0x0001, 2); } void RadioddityCodeplug::MenuSettingsElement::enableEditScanList(bool enable) { setBit(0x0001, 2, enable); } bool RadioddityCodeplug::MenuSettingsElement::callAlert() const { return getBit(0x0001, 3); } void RadioddityCodeplug::MenuSettingsElement::enableCallAlert(bool enable) { setBit(0x0001, 3, enable); } bool RadioddityCodeplug::MenuSettingsElement::editContact() const { return getBit(0x0001, 4); } void RadioddityCodeplug::MenuSettingsElement::enableEditContact(bool enable) { setBit(0x0001, 4, enable); } bool RadioddityCodeplug::MenuSettingsElement::manualDial() const { return getBit(0x0001, 5); } void RadioddityCodeplug::MenuSettingsElement::enableManualDial(bool enable) { setBit(0x0001, 5, enable); } bool RadioddityCodeplug::MenuSettingsElement::radioCheck() const { return getBit(0x0001, 6); } void RadioddityCodeplug::MenuSettingsElement::enableRadioCheck(bool enable) { setBit(0x0001, 6, enable); } bool RadioddityCodeplug::MenuSettingsElement::remoteMonitor() const { return getBit(0x0001, 7); } void RadioddityCodeplug::MenuSettingsElement::enableRemoteMonitor(bool enable) { setBit(0x0001, 7, enable); } bool RadioddityCodeplug::MenuSettingsElement::radioEnable() const { return getBit(0x0002, 0); } void RadioddityCodeplug::MenuSettingsElement::enableRadioEnable(bool enable) { setBit(0x0002, 0, enable); } bool RadioddityCodeplug::MenuSettingsElement::radioDisable() const { return getBit(0x0002, 1); } void RadioddityCodeplug::MenuSettingsElement::enableRadioDisable(bool enable) { setBit(0x0002, 1, enable); } bool RadioddityCodeplug::MenuSettingsElement::progPassword() const { return getBit(0x0002, 2); } void RadioddityCodeplug::MenuSettingsElement::enableProgPassword(bool enable) { setBit(0x0002, 2, enable); } bool RadioddityCodeplug::MenuSettingsElement::talkaround() const { return getBit(0x0002, 3); } void RadioddityCodeplug::MenuSettingsElement::enableTalkaround(bool enable) { setBit(0x0002, 3, enable); } bool RadioddityCodeplug::MenuSettingsElement::tone() const { return getBit(0x0002, 4); } void RadioddityCodeplug::MenuSettingsElement::enableTone(bool enable) { setBit(0x0002, 4, enable); } bool RadioddityCodeplug::MenuSettingsElement::power() const { return getBit(0x0002, 5); } void RadioddityCodeplug::MenuSettingsElement::enablePower(bool enable) { setBit(0x0002, 5, enable); } bool RadioddityCodeplug::MenuSettingsElement::backlight() const { return getBit(0x0002, 6); } void RadioddityCodeplug::MenuSettingsElement::enableBacklight(bool enable) { setBit(0x0002, 6, enable); } bool RadioddityCodeplug::MenuSettingsElement::introScreen() const { return getBit(0x0002, 7); } void RadioddityCodeplug::MenuSettingsElement::enableIntroScreen(bool enable) { setBit(0x0002, 7, enable); } bool RadioddityCodeplug::MenuSettingsElement::keypadLock() const { return getBit(0x0003, 0); } void RadioddityCodeplug::MenuSettingsElement::enableKeypadLock(bool enable) { setBit(0x0003, 0, enable); } bool RadioddityCodeplug::MenuSettingsElement::ledIndicator() const { return getBit(0x0003, 1); } void RadioddityCodeplug::MenuSettingsElement::enableLEDIndicator(bool enable) { setBit(0x0003, 1, enable); } bool RadioddityCodeplug::MenuSettingsElement::squelch() const { return getBit(0x0003, 2); } void RadioddityCodeplug::MenuSettingsElement::enableSquelch(bool enable) { setBit(0x0003, 2, enable); } bool RadioddityCodeplug::MenuSettingsElement::privacy() const { return getBit(0x0003, 3); } void RadioddityCodeplug::MenuSettingsElement::enablePrivacy(bool enable) { setBit(0x0003, 3, enable); } bool RadioddityCodeplug::MenuSettingsElement::vox() const { return getBit(0x0003, 4); } void RadioddityCodeplug::MenuSettingsElement::enableVOX(bool enable) { setBit(0x0003, 4, enable); } bool RadioddityCodeplug::MenuSettingsElement::passwordLock() const { return getBit(0x0003, 5); } void RadioddityCodeplug::MenuSettingsElement::enablePasswordLock(bool enable) { setBit(0x0003, 5, enable); } bool RadioddityCodeplug::MenuSettingsElement::missedCalls() const { return getBit(0x0003, 6); } void RadioddityCodeplug::MenuSettingsElement::enableMissedCalls(bool enable) { setBit(0x0003, 6, enable); } bool RadioddityCodeplug::MenuSettingsElement::answeredCalls() const { return getBit(0x0003, 7); } void RadioddityCodeplug::MenuSettingsElement::enableAnsweredCalls(bool enable) { setBit(0x0003, 7, enable); } bool RadioddityCodeplug::MenuSettingsElement::outgoingCalls() const { return getBit(0x0004, 0); } void RadioddityCodeplug::MenuSettingsElement::enableOutgoingCalls(bool enable) { setBit(0x0004, 0, enable); } bool RadioddityCodeplug::MenuSettingsElement::channelDisplay() const { return getBit(0x0004, 1); } void RadioddityCodeplug::MenuSettingsElement::enableChannelDisplay(bool enable) { setBit(0x0004, 1, enable); } bool RadioddityCodeplug::MenuSettingsElement::dualWatch() const { return getBit(0x0004, 2); } void RadioddityCodeplug::MenuSettingsElement::enableDualWatch(bool enable) { setBit(0x0004, 2, enable); } unsigned RadioddityCodeplug::MenuSettingsElement::keypadLockTime() const { return getUInt2(0x0006, 0)*5; } void RadioddityCodeplug::MenuSettingsElement::setKeypadLockTime(unsigned sec) { setUInt2(0x0006, 0, sec/5); } unsigned RadioddityCodeplug::MenuSettingsElement::backlightTime() const { return getUInt2(0x0006, 2)*5; } void RadioddityCodeplug::MenuSettingsElement::setBacklightTime(unsigned sec) { setUInt2(0x0006, 2, sec/5); } RadioddityCodeplug::MenuSettingsElement::ChannelDisplayMode RadioddityCodeplug::MenuSettingsElement::channelDisplayMode() const { return (ChannelDisplayMode)getUInt2(0x0006, 6); } void RadioddityCodeplug::MenuSettingsElement::setChannelDisplayMode(ChannelDisplayMode mode) { setUInt2(0x0006, 6, (unsigned)mode); } bool RadioddityCodeplug::MenuSettingsElement::keyTone() const { return getBit(0x0007, 5); } void RadioddityCodeplug::MenuSettingsElement::enableKeyTone(bool enable) { setBit(0x0007, 5, enable); } RadioddityCodeplug::MenuSettingsElement::DualWatchMode RadioddityCodeplug::MenuSettingsElement::dualWatchMode() const { return DualWatchMode(getUInt2(0x0007, 6)); } void RadioddityCodeplug::MenuSettingsElement::setDualWatchMode(DualWatchMode mode) { setUInt2(0x0007, 6, (unsigned)mode); } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::BootSettingsElement * ********************************************************************************************* */ RadioddityCodeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr) : Element(ptr, 0x20) { // pass... } RadioddityCodeplug::BootSettingsElement::~BootSettingsElement() { // pass... } void RadioddityCodeplug::BootSettingsElement::clear() { enableBootText(true); enableBootPassword(false); setBCD8_be(0x0002, 0); setUInt8(0x0007, 0); memset(_data+0x0008, 0, 24); } bool RadioddityCodeplug::BootSettingsElement::bootText() const { return (1 == getUInt8(0x0000)); } void RadioddityCodeplug::BootSettingsElement::enableBootText(bool enable) { setUInt8(0x0000, (enable ? 1 :0)); } bool RadioddityCodeplug::BootSettingsElement::bootPasswordEnabled() const { return (1 == getUInt8(0x0001)); } void RadioddityCodeplug::BootSettingsElement::enableBootPassword(bool enable) { setUInt8(0x0001, (enable ? 1 : 0)); } unsigned RadioddityCodeplug::BootSettingsElement::bootPassword() const { return getBCD8_be(0x0002); } void RadioddityCodeplug::BootSettingsElement::setBootPassword(unsigned passwd) { setBCD8_be(0x0002, passwd); } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::BootTextElement * ********************************************************************************************* */ RadioddityCodeplug::BootTextElement::BootTextElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::BootTextElement::BootTextElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RadioddityCodeplug::BootTextElement::~BootTextElement() { // pass... } void RadioddityCodeplug::BootTextElement::clear() { setLine1(""); setLine2(""); } QString RadioddityCodeplug::BootTextElement::line1() const { return readASCII(Offset::line1(), Limit::lineLength(), 0xff); } void RadioddityCodeplug::BootTextElement::setLine1(const QString &text) { writeASCII(Offset::line1(), text, Limit::lineLength(), 0xff); } QString RadioddityCodeplug::BootTextElement::line2() const { return readASCII(Offset::line2(), Limit::lineLength(), 0xff); } void RadioddityCodeplug::BootTextElement::setLine2(const QString &text) { writeASCII(Offset::line2(), text, Limit::lineLength(), 0xff); } bool RadioddityCodeplug::BootTextElement::fromConfig(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) setLine1(ctx.config()->settings()->introLine1()); setLine2(ctx.config()->settings()->introLine2()); return true; } bool RadioddityCodeplug::BootTextElement::updateConfig(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) ctx.config()->settings()->setIntroLine1(line1()); ctx.config()->settings()->setIntroLine2(line2()); return true; } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::MessageBankElement * ********************************************************************************************* */ RadioddityCodeplug::MessageBankElement::MessageBankElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RadioddityCodeplug::MessageBankElement::MessageBankElement(uint8_t *ptr) : Element(ptr, 0x1248) { // pass... } RadioddityCodeplug::MessageBankElement::~MessageBankElement() { // pass... } void RadioddityCodeplug::MessageBankElement::clear() { setUInt8(Offset::messageConut(), 0); // set count to 0 memset(_data+0x0001, 0x00, 7); // Fill unused memset(_data+Offset::messageLengths(), 0x00, Limit::messages()); // Set message lengths to 0 memset(_data+0x0028, 0x00, 32); // Fill unused memset(_data+Offset::messages(), 0xff, Limit::messages()*Limit::messageLength()); // Clear all messages } unsigned RadioddityCodeplug::MessageBankElement::numMessages() const { return getUInt8(Offset::messageConut()); } QString RadioddityCodeplug::MessageBankElement::message(unsigned n) const { if (n >= numMessages()) return QString(); return readASCII(Offset::messages()+n*Offset::betweenMessages(), Limit::messageLength(), 0xff); } void RadioddityCodeplug::MessageBankElement::appendMessage(const QString msg) { unsigned idx = numMessages(); if (idx >= Limit::messages()) return; unsigned int len = std::min((unsigned int)msg.size(), Limit::messageLength()); // increment counter setUInt8(Offset::messageConut(), idx+1); // store length setUInt8(Offset::messageLengths()+idx, len); // store string writeASCII(Offset::messages()+Offset::betweenMessages()*idx, msg, Limit::messageLength(), 0xff); } bool RadioddityCodeplug::MessageBankElement::encode(Context &ctx, const Flags &flags, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err) clear(); unsigned int count = std::min( Limit::messages(), (unsigned int)ctx.config()->smsExtension()->smsTemplates()->count()); for (unsigned int i=0; ismsExtension()->smsTemplates()->message(i)->message()); return true; } bool RadioddityCodeplug::MessageBankElement::decode(Context &ctx, const ErrorStack &err) { Q_UNUSED(err); for (unsigned int i=0; isetName(QString("Message %1").arg(i+1)); sms->setMessage(message(i)); ctx.config()->smsExtension()->smsTemplates()->add(sms); } return true; } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug::EncryptionElement * ********************************************************************************************* */ RadioddityCodeplug::EncryptionElement::EncryptionElement(uint8_t *ptr, size_t size) : Codeplug::Element(ptr, size) { // pass... } RadioddityCodeplug::EncryptionElement::EncryptionElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RadioddityCodeplug::EncryptionElement::~EncryptionElement() { // pass... } void RadioddityCodeplug::EncryptionElement::clear() { setPrivacyType(PrivacyType::None); for (unsigned int i=0; i= Limit::keyCount()) return false; unsigned byte=n/8, bit =n%8; return getBit(Offset::bitmap()+byte, bit); } QByteArray RadioddityCodeplug::EncryptionElement::basicKey(unsigned n) const { if (n >= Limit::keyCount()) return QByteArray(); return QByteArray((const char *)_data+Offset::keys()+Offset::key()*n, Limit::keySize()); } void RadioddityCodeplug::EncryptionElement::setBasicKey(unsigned n, const QByteArray &key) { if ((n >= Limit::keyCount()) || (Limit::keySize() != key.size())) return; unsigned byte=n/8, bit =n%8; // Store key (twice?) memcpy(_data+Offset::keys() + Offset::key()*n, key.data(), Limit::keySize()); memcpy(_data+Offset::keys() + Offset::key()*n + Limit::keySize(), key.data(), Limit::keySize()); // Update bitmap setBit(Offset::bitmap()+byte, bit); setPrivacyType(PrivacyType::Basic); } void RadioddityCodeplug::EncryptionElement::clearBasicKey(unsigned n) { if (n >= Limit::keyCount()) return; unsigned byte=n/8, bit =n%8; memset(_data+Offset::keys() + Offset::key()*n, 0xff, Limit::keySize()); memset(_data+Offset::keys() + Offset::key()*n + Limit::keySize(), 0xff, Limit::keySize()); clearBit(Offset::bitmap()+byte, bit); } bool RadioddityCodeplug::EncryptionElement::fromCommercialExt(CommercialExtension *ext, Context &ctx, const ErrorStack &err) { clear(); if ((unsigned int)ext->encryptionKeys()->count() > Limit::keyCount()) { errMsg(err) << "Cannot encode encryption extension. Can only encode " << Limit::keyCount() << " keys."; return false; } for (int i=0; iencryptionKeys()->count(); i++) { if (! ext->encryptionKeys()->get(i)->is()) { errMsg(err) << "Can only encode basic encryption keys."; return false; } BasicEncryptionKey *key = ext->encryptionKeys()->get(i)->as(); if (key->key().size() != Limit::keySize()) { errMsg(err) << "Can only encode " << 8*Limit::keySize() << "bit basic encryption keys."; return false; } setBasicKey(i, key->key()); ctx.add(key, i+1); } return true; } bool RadioddityCodeplug::EncryptionElement::updateCommercialExt(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) if (PrivacyType::None == privacyType()) return false; CommercialExtension *ext = ctx.config()->commercialExtension(); for (unsigned int i=0; isetName(QString("Basic Key %1").arg(i+1)); key->fromHex(basicKey(i).toHex()); // add key to extension ext->encryptionKeys()->add(key); // register key index ctx.add(key, i+1); } return ext; } bool RadioddityCodeplug::EncryptionElement::linkCommercialExt(CommercialExtension *ext, Context &ctx, const ErrorStack &err) { Q_UNUSED(ext); Q_UNUSED(ctx); Q_UNUSED(err) // Keys do not need any linking step return true; } /* ********************************************************************************************* * * Implementation of RadioddityCodeplug * ********************************************************************************************* */ RadioddityCodeplug::RadioddityCodeplug(QObject *parent) : Codeplug(parent) { // pass... } RadioddityCodeplug::~RadioddityCodeplug() { // pass... } void RadioddityCodeplug::clear() { // Clear general config clearGeneralSettings(); // Clear button settings clearButtonSettings(); // Clear messages clearMessages(); // Clear contacts clearContacts(); // clear DTMF contacts clearDTMFContacts(); // clear boot settings clearBootSettings(); // clear menu settings clearMenuSettings(); // clear boot text clearBootText(); // clear VFO settings clearVFOSettings(); // clear zones clearZones(); // clear scan lists clearScanLists(); // clear group lists clearGroupLists(); // clear encryption keys clearEncryption(); } bool RadioddityCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) const { Q_UNUSED(err) // All indices as 1-based. That is, the first channel gets index 1. // Map radio IDs for (int i=0; iradioIDs()->count(); i++) ctx.add(config->radioIDs()->getId(i), i+1); // Map digital and DTMF contacts for (int i=0, d=0, a=0; icontacts()->count(); i++) { if (config->contacts()->contact(i)->is()) { ctx.add(config->contacts()->contact(i)->as(), d+1); d++; } else if (config->contacts()->contact(i)->is()) { ctx.add(config->contacts()->contact(i)->as(), a+1); a++; } } // Map rx group lists for (int i=0; irxGroupLists()->count(); i++) ctx.add(config->rxGroupLists()->list(i), i+1); // Map channels for (int i=0; ichannelList()->count(); i++) ctx.add(config->channelList()->channel(i), i+1); // Map zones for (int i=0; izones()->count(); i++) ctx.add(config->zones()->zone(i), i+1); // Map scan lists for (int i=0; iscanlists()->count(); i++) ctx.add(config->scanlists()->scanlist(i), i+1); // Map DMR APRS systems for (int i=0,a=0,d=0; iposSystems()->count(); i++) { if (config->posSystems()->system(i)->is()) { ctx.add(config->posSystems()->system(i)->as(), d+1); d++; } else if (config->posSystems()->system(i)->is()) { ctx.add(config->posSystems()->system(i)->as(), a+1); a++; } } // Map roaming for (int i=0; iroamingZones()->count(); i++) ctx.add(config->roamingZones()->zone(i), i+1); return true; } Config * RadioddityCodeplug::preprocess(Config *config, const ErrorStack &err) const { Config *intermediate = Codeplug::preprocess(config, err); if (nullptr == intermediate) { errMsg(err) << "Cannot pre-process Radioddity codeplug."; return nullptr; } ZoneSplitVisitor splitter; if (! splitter.process(intermediate, err)) { errMsg(err) << "Cannot split zone for Radioddity codeplug."; delete intermediate; return nullptr; } return intermediate; } bool RadioddityCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) { // Check if default DMR id is set. if (config->settings()->defaultIdRef()->isNull()) { errMsg(err) << "No default radio ID specified."; return false; } // Create index<->object table. Context ctx(config); if (! index(config, ctx, err)) { errMsg(err) << "Cannot index configuration objects."; return false; } return this->encodeElements(flags, ctx); } bool RadioddityCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) { // General config if (! this->encodeGeneralSettings(flags, ctx, err)) { errMsg(err) << "Cannot encode general settings."; return false; } if (! this->encodeButtonSettings(ctx, flags, err)) { errMsg(err) << "Cannot encode button settings."; return false; } if (! this->encodeMessages(ctx, flags, err)) { errMsg(err) << "Cannot encode preset messages."; return false; } // Define Contacts if (! this->encodeContacts(flags, ctx, err)) { errMsg(err) << "Cannot encode contacts."; return false; } if (! this->encodeDTMFContacts(flags, ctx, err)) { errMsg(err) << "Cannot encode DTMF contacts."; return false; } if (! this->encodeChannels(flags, ctx, err)) { errMsg(err) << "Cannot encode channels"; return false; } if (! this->encodeBootText(flags, ctx, err)) { errMsg(err) << "Cannot encode boot text."; return false; } if (! this->encodeZones(flags, ctx, err)) { errMsg(err) << "Cannot encode zones."; return false; } if (! this->encodeScanLists(flags, ctx, err)) { errMsg(err) << "Cannot encode scan lists."; return false; } if (! this->encodeGroupLists(flags, ctx, err)) { errMsg(err) << "Cannot encode group lists."; return false; } if (! this->encodeEncryption(flags, ctx, err)) { errMsg(err) << "Cannot encode encryption keys."; return true; } return true; } bool RadioddityCodeplug::decode(Config *config, const ErrorStack &err) { // Clear config object config->clear(); // Create index<->object table. Context ctx(config); return this->decodeElements(ctx, err); } bool RadioddityCodeplug::postprocess(Config *config, const ErrorStack &err) const { if (! Codeplug::postprocess(config, err)) { errMsg(err) << "Cannot post-process Radioddy codeplug."; return false; } ZoneMergeVisitor merger; if (! merger.process(config, err)) { errMsg(err) << "Cannot merg zones in decoded Radioddity codeplug."; return false; } return true; } bool RadioddityCodeplug::decodeElements(Context &ctx, const ErrorStack &err) { if (! this->decodeGeneralSettings(ctx, err)) { errMsg(err) << "Cannot decode general settings."; return false; } if (! this->decodeButtonSettings(ctx, err)) { errMsg(err) << "Cannot decode button settings."; return false; } if (! this->decodeMessages(ctx, err)) { errMsg(err) << "Cannot decode preset messages."; return false; } if (! this->createContacts(ctx, err)) { errMsg(err) << "Cannot create contacts."; return false; } if (! this->createDTMFContacts(ctx, err)) { errMsg(err) << "Cannot create DTMF contacts"; return false; } if (! this->createChannels(ctx, err)) { errMsg(err) << "Cannot create channels."; return false; } if (! this->decodeBootText(ctx, err)) { errMsg(err) << "Cannot decode boot text."; return false; } if (! this->createEncryption(ctx, err)) { errMsg(err) << "Cannot decode encryption keys."; return false; } if (! this->createZones(ctx, err)) { errMsg(err) << "Cannot create zones."; return false; } if (! this->createScanLists(ctx, err)) { errMsg(err) << "Cannot create scan lists."; return false; } if (! this->createGroupLists(ctx, err)) { errMsg(err) << "Cannot create group lists."; return false; } if (! this->linkChannels(ctx, err)) { errMsg(err) << "Cannot link channels."; return false; } if (! this->linkZones(ctx, err)) { errMsg(err) << "Cannot link zones."; return false; } if (! this->linkScanLists(ctx, err)) { errMsg(err) << "Cannot link scan lists."; return false; } if (! this->linkGroupLists(ctx, err)) { errMsg(err) << "Cannot link group lists."; return false; } if (! this->linkEncryption(ctx, err)) { errMsg(err) << "Cannot link encryption keys."; return false; } return true; } qdmr-0.12.3/lib/radioddity_codeplug.hh000066400000000000000000002112611501654372000176530ustar00rootroot00000000000000#ifndef RADIODDITYCODEPLUG_HH #define RADIODDITYCODEPLUG_HH #include "codeplug.hh" #include "signaling.hh" #include "channel.hh" #include "contact.hh" #include "radioddity_extensions.hh" #include "ranges.hh" class DMRContact; class Zone; class RXGroupList; class ScanList; /** Base class of all Radioddity codeplugs. This class implements the majority of all codeplug * elements present in all Radioddity codeplugs (also some derivatives like OpenGD77). This eases * the support of several Radioddity radios, as only the differences in the codeplug to this base * class must be implemented. * * @ingroup radioddity */ class RadioddityCodeplug : public Codeplug { Q_OBJECT public: /** Implements the base for all Radioddity channel encodings. * * Memory layout of encoded channel: * @verbinclude radioddity_channel.txt */ class ChannelElement: public Codeplug::Element { public: /** Possible channel types. */ enum Mode { MODE_ANALOG = 0, ///< Analog channel, aka FM. MODE_DIGITAL = 1 ///< Digital channel, aka DMR. }; /** Possible admit criteria. */ enum Admit { ADMIT_ALWAYS = 0, ///< Allow always. ADMIT_CH_FREE = 1, ///< Allow TX on channel free. ADMIT_COLOR = 2 ///< Allow TX on matching color-code. }; /** Possible privacy groups, not used in ham radio. */ enum PrivacyGroup { PRIVGR_NONE = 0, ///< No privacy group, default. PRIVGR_53474C39 = 1 ///< Privacy group 53474C39 (wtf?). }; protected: /** Constructs a channel from the given memory. */ ChannelElement(uint8_t *ptr, size_t size); public: /** Constructs a channel from the given memory. */ explicit ChannelElement(uint8_t *ptr); /** Destructor. */ virtual ~ChannelElement(); /** The size of the channel. */ static constexpr unsigned int size() { return 0x0038; } /** Resets the channel. */ virtual void clear(); /** Returns the name of the channel. */ virtual QString name() const; /** Sets the name of the channel. */ virtual void setName(const QString &n); /** Returns the RX frequency of the channel. */ virtual uint32_t rxFrequency() const; /** Sets the RX frequency of the channel. */ virtual void setRXFrequency(uint32_t freq); /** Returns the TX frequency of the channel. */ virtual uint32_t txFrequency() const; /** Sets the TX frequency of the channel. */ virtual void setTXFrequency(uint32_t freq); /** Returns the channel mode. */ virtual Mode mode() const; /** Sets the channel mode. */ virtual void setMode(Mode mode); /** Returns the TX timeout in seconds. A value of 0 means disabled. */ virtual unsigned txTimeOut() const; /** Sets the TX timeout in seconds. Setting it to 0 disables the timeout. */ virtual void setTXTimeOut(unsigned tot); /** Returns the transmit time-out re-key delay in seconds. */ virtual unsigned txTimeOutRekeyDelay() const; /** Sets the transmit time-out re-key delay in seconds. */ virtual void setTXTimeOutRekeyDelay(unsigned delay); /** Returns the admit criterion. */ virtual Admit admitCriterion() const; /** Sets the admit criterion. */ virtual void setAdmitCriterion(Admit admit); /** Returns @c true if a scan list is set. */ virtual bool hasScanList() const; /** Returns the scan list index (+1). */ virtual unsigned scanListIndex() const; /** Sets the scan list index (+1). */ virtual void setScanListIndex(unsigned index); /** Returns the RX subtone. */ virtual SelectiveCall rxTone() const; /** Sets the RX subtone. */ virtual void setRXTone(const SelectiveCall &code); /** Returns the TX subtone. */ virtual SelectiveCall txTone() const; /** Sets the TX subtone. */ virtual void setTXTone(const SelectiveCall &code); /** Returns TX signaling index (+1). */ virtual unsigned txSignalingIndex() const; /** Sets TX signaling index (+1). */ virtual void setTXSignalingIndex(unsigned index); /** Returns RX signaling index (+1). */ virtual unsigned rxSignalingIndex() const; /** Sets RX signaling index (+1). */ virtual void setRXSignalingIndex(unsigned index); /** Returns the privacy group. */ virtual PrivacyGroup privacyGroup() const; /** Sets the privacy group. */ virtual void setPrivacyGroup(PrivacyGroup grp); /** Returns the TX color code. */ virtual unsigned txColorCode() const; /** Sets the TX color code. */ virtual void setTXColorCode(unsigned cc); /** Returns @c true if a group list is set. */ virtual bool hasGroupList() const; /** Returns the group-list index (+1). */ virtual unsigned groupListIndex() const; /** Sets the group-list index (+1). */ virtual void setGroupListIndex(unsigned index); /** Returns the RX color code. */ virtual unsigned rxColorCode() const; /** Sets the RX color code. */ virtual void setRXColorCode(unsigned cc); /** Returns @c true if an emergency system is set. */ virtual bool hasEmergencySystem() const; /** Returns the emergency system index (+1). */ virtual unsigned emergencySystemIndex() const; /** Sets the emergency system index (+1). */ virtual void setEmergencySystemIndex(unsigned index); /** Returns @c true if a TX contact is set. */ virtual bool hasContact() const; /** Returns the transmit contact index (+1). */ virtual unsigned contactIndex() const; /** Sets the transmit contact index (+1). */ virtual void setContactIndex(unsigned index); /** Returns @c true if data-call-confirm is enabled. */ virtual bool dataCallConfirm() const; /** Enables/disables data-call-confirm. */ virtual void enableDataCallConfirm(bool enable); /** Returns @c true if emergency alarm ACK is enabled. */ virtual bool emergencyAlarmACK() const; /** Enables/disables emergency alarm ACK. */ virtual void enableEmergencyAlarmACK(bool enable); /** Returns @c true if private-call-confirm is enabled. */ virtual bool privateCallConfirm() const; /** Enables/disables private-call-confirm. */ virtual void enablePrivateCallConfirm(bool enable); /** Returns @c true if privacy is enabled. */ virtual bool privacyEnabled() const; /** Enables/disables privacy. */ virtual void enablePrivacy(bool enable); /** Returns the time slot of the channel. */ virtual DMRChannel::TimeSlot timeSlot() const; /** Sets the time slot of the channel. */ virtual void setTimeSlot(DMRChannel::TimeSlot ts); /** Returns @c true if the dual-capacity direct mode is enabled. */ virtual bool dualCapacityDirectMode() const; /** Enables/disables the dual-capacity direct mode. */ virtual void enableDualCapacityDirectMode(bool enable); /** Returns @c true if non-STE is frequency (?!). */ virtual bool nonSTEFrequency() const; /** Enables/disables non-STE is frequency (?!). */ virtual void enableNonSTEFrequency(bool enable); /** Returns the bandwidth. */ virtual FMChannel::Bandwidth bandwidth() const; /** Sets the bandwidth. */ virtual void setBandwidth(FMChannel::Bandwidth bw); /** Returns @c true if RX only is enabled. */ virtual bool rxOnly() const; /** Enables/disables RX only. */ virtual void enableRXOnly(bool enable); /** Returns @c true if talkaround is enabled. */ virtual bool talkaround() const; /** Enables/disables talkaround. */ virtual void enableTalkaround(bool enable); /** Returns @c true if VOX is enabled. */ virtual bool vox() const; /** Enables/disables VOX. */ virtual void enableVOX(bool enable); /** Returns the power setting of the channel. */ virtual Channel::Power power() const; /** Sets the power setting of the channel. */ virtual void setPower(Channel::Power pwr); /** Constructs a generic @c Channel object from the codeplug channel. */ virtual Channel *toChannelObj(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links a previously constructed channel to the rest of the configuration. */ virtual bool linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Initializes this codeplug channel from the given generic configuration. */ virtual bool fromChannelObj(const Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for this element. */ struct Limit { /** The maximum length of the name. */ static constexpr unsigned int nameLength() { return 16; } }; protected: /** Some internal offsets within the channel element. */ struct Offset: public Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int rxFrequency() { return 0x0010; } static constexpr unsigned int txFrequency() { return 0x0014; } static constexpr unsigned int mode() { return 0x0018; } static constexpr unsigned int txTimeout() { return 0x001b; } static constexpr unsigned int txTimeoutRekeyDelay() { return 0x001c; } static constexpr unsigned int admitCriterion() { return 0x001d; } static constexpr unsigned int scanList() { return 0x001f; } static constexpr unsigned int rxTone() { return 0x0020; } static constexpr unsigned int txTone() { return 0x0022; } static constexpr unsigned int txSignaling() { return 0x0025; } static constexpr unsigned int rxSignaling() { return 0x0027; } static constexpr unsigned int privacyGroup() { return 0x0029; } static constexpr unsigned int txColorCode() { return 0x002a; } static constexpr unsigned int groupList() { return 0x002b; } static constexpr unsigned int rxColorCode() { return 0x002c; } static constexpr unsigned int emergencySystem() { return 0x002d; } static constexpr unsigned int transmitContact() { return 0x002e; } static constexpr Bit dataCallConfirm() { return {0x0030, 7}; } static constexpr Bit emergencyAlarmACK() { return {0x0030, 6}; } static constexpr Bit privateCallConfirm() { return {0x0031, 0}; } static constexpr Bit privacyEnabled() { return {0x0031, 4}; } static constexpr Bit timeSlot() { return {0x0031, 6}; } static constexpr Bit dualCapacityDirectMode() { return {0x0032, 0}; } static constexpr Bit nonSTEFrequency() { return {0x0032, 5}; } static constexpr Bit bandwidth() { return {0x0033, 1}; } static constexpr Bit rxOnly() { return {0x0033, 2}; } static constexpr Bit talkaround() { return {0x0033, 3}; } static constexpr Bit vox() { return {0x0033, 6}; } static constexpr Bit power() { return {0x0033, 7}; } /// @endcond }; }; /** Implements the base for channel banks in Radioddity codeplugs. * * Memory layout of a channel bank: * @verbinclude radioddity_channelbank.txt */ class ChannelBankElement: public Element { protected: /** Hidden constructor. */ ChannelBankElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ChannelBankElement(uint8_t *ptr); /** Destructor. */ virtual ~ChannelBankElement(); /** The size of the channel bank. */ static constexpr unsigned int size() { return 0x1c10; } /** Clears the bank. */ void clear(); /** Returns @c true if the channel is enabled. */ virtual bool isEnabled(unsigned idx) const ; /** Enable/disable a channel in the bank. */ virtual void enable(unsigned idx, bool enabled); /** Returns a pointer to the channel at the given index. */ virtual uint8_t *get(unsigned idx) const; /** Retunrs the n-th channel. */ ChannelElement channel(unsigned int n); public: /** Some limits for the channel bank. */ struct Limit { /** The maximum number of channels. */ static constexpr unsigned int channelCount() { return 128; } }; protected: /** Some internal offset within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int bitmask() { return 0x0000; } static constexpr unsigned int channels() { return 0x0010; } /// @endcond }; }; /** VFO Channel representation within the binary codeplug. * * Each channel requires 0x38b: * @verbinclude radioddity_vfochannel.txt */ class VFOChannelElement: public ChannelElement { public: /** Possible offset frequency modes. */ enum class OffsetMode { Off = 0, ///< Disables transmit frequency offset. Positive = 1, ///< Transmit offset frequency is positive (TX above RX). Negative = 2 ///< Transmit offset frequency is negative (TX below RX). }; /** Possible tuning step sizes. */ enum class StepSize { SS2_5kHz = 0, ///< 2.5kHz SS5kHz = 1, ///< 5kHz SS6_25kHz = 2, ///< 6.25kHz SS10kHz = 3, ///< 10kHz SS12_5kHz = 4, ///< 12.5kHz SS20kHz = 5, ///< 20kHz SS30kHz = 6, ///< 30kHz SS50kHz = 7 ///< 50kHz }; protected: /** Hidden constructor. */ VFOChannelElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit VFOChannelElement(uint8_t *ptr); void clear(); /** The VFO channel has no name. */ QString name() const; /** The VFO channel has no name. */ void setName(const QString &name); /** Returns the tuning step-size in kHz. */ virtual double stepSize() const; /** Sets the tuning step-size in kHz. */ virtual void setStepSize(double kHz); /** Returns the transmit frequency offset mode. */ virtual OffsetMode offsetMode() const; /** Returns the transmit frequency offset. */ virtual double txOffset() const; /** Sets the transmit frequency offset in MHz. */ virtual void setTXOffset(double f); /** Sets the transmit frequency offset mode. */ virtual void setOffsetMode(OffsetMode mode); protected: /// @cond DO_NOT_DOCUMENT struct Offset: public ChannelElement::Offset { static constexpr Bit stepSize() { return {0x0036, 4} ; } static constexpr Bit offsetMode() { return {0x0036, 2} ; } static constexpr unsigned int txOffset() { return 0x0034; } }; /// @endcond }; /** Implements the base for digital contacts in Radioddity codeplugs. * * Memory layout of a digital contact: * @verbinclude radioddity_contact.txt */ class ContactElement: public Element { protected: /** Hidden constructor. */ ContactElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ContactElement(uint8_t *ptr); /** Destructor. */ virtual ~ContactElement(); /** The size of the contact element. */ static constexpr unsigned int size() { return 0x0018; } /** Resets the contact. */ void clear(); /** Returns @c true if the contact is valid. */ bool isValid() const; /** Returns the name of the contact. */ virtual QString name() const; /** Sets the name of the contact. */ virtual void setName(const QString name); /** Returns the DMR number of the contact. */ virtual unsigned number() const; /** Sets the DMR number of the contact. */ virtual void setNumber(unsigned id); /** Returns the call type. */ virtual DMRContact::Type type() const; /** Sets the call type. */ virtual void setType(DMRContact::Type type); /** Returns @c true if the ring tone is enabled for this contact. */ virtual bool ring() const; /** Enables/disables ring tone for this contact. */ virtual void enableRing(bool enable); /** Returns the ring tone style for this contact [0-10]. */ virtual unsigned ringStyle() const; /** Sets the ring tone style for this contact [0-10]. */ virtual void setRingStyle(unsigned style); /** Constructs a @c DigitalContact instance from this codeplug contact. */ virtual DMRContact *toContactObj(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Resets this codeplug contact from the given @c DigitalContact. */ virtual bool fromContactObj(const DMRContact *obj, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for the contact. */ struct Limit { /** Maximum name length. */ static constexpr unsigned int nameLength() { return 16; } /** Number of possible ring-styles [0,10]. */ static constexpr unsigned int ringStyle() { return 10; } }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int number() { return 0x0010; } static constexpr unsigned int type() { return 0x0014; } static constexpr unsigned int ring() { return 0x0015; } static constexpr unsigned int ringStyle() { return 0x0016; } /// @endcond }; }; /** Implements a base DTMF (analog) contact for Radioddity codeplugs. * * Memory layout of the DTMF contact: * @verbinclude radioddity_dtmfcontact.txt */ class DTMFContactElement: public Element { protected: /** Hidden constructor. */ DTMFContactElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit DTMFContactElement(uint8_t *ptr); /** Destructor. */ virtual ~DTMFContactElement(); /** The size of the contact element. */ static constexpr unsigned int size() { return 0x0020; } /** Resets the contact. */ void clear(); /** Returns @c true if the contact is valid. */ bool isValid() const; /** Returns the name of the contact. */ virtual QString name() const; /** Sets the name of the conact. */ virtual void setName(const QString &name); /** Returns the number of the contact. */ virtual QString number() const; /** Sets the number of the contact. */ virtual void setNumber(const QString &number); /** Constructs a @c DTMFContact instance from this codeplug contact. */ virtual DTMFContact *toContactObj(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Resets this codeplug contact from the given @c DTMFContact. */ virtual bool fromContactObj(const DTMFContact *obj, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit { /** The maximum name length. */ static constexpr unsigned int nameLength() { return 16; } /** The maximum number length. */ static constexpr unsigned int numberLength() { return 16; } }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int number() { return 0x0010; } /// @endcond }; }; /** Represents a zone within Radioddity codeplugs. * * Memory layout of the zone: * @verbinclude radioddity_zone.txt */ class ZoneElement: public Element { protected: /** Hidden constructor. */ ZoneElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ZoneElement(uint8_t *ptr); virtual ~ZoneElement(); /** The size of the zone element. */ static constexpr unsigned int size() { return 0x0030; } /** Resets the zone. */ void clear(); /** Returns @c true if the zone is valid. */ bool isValid() const; /** Returns the name of the zone. */ virtual QString name() const; /** Sets the name of the zone. */ virtual void setName(const QString &name); /** Returns @c true if a member is stored at the given index. * That is, if the index is not 0. */ virtual bool hasMember(unsigned n) const; /** Returns the n-th member index (+1). */ virtual unsigned member(unsigned n) const; /** Sets the n-th member index (+1). */ virtual void setMember(unsigned n, unsigned idx); /** Clears the n-th member index. */ virtual void clearMember(unsigned n); /** Constructs a generic @c Zone object from this codeplug zone. */ virtual Zone *toZoneObj(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links a previously constructed @c Zone object to the rest of the configuration. That is * linking to the referred channels. */ virtual bool linkZoneObj(Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Resets this codeplug zone representation from the given generic @c Zone object. */ virtual bool fromZoneObjA(const Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack()); /** Resets this codeplug zone representation from the given generic @c Zone object. */ virtual bool fromZoneObjB(const Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for zone elements. */ struct Limit { /** The maximum length of the zone name. */ static constexpr unsigned int nameLength() { return 16; } /** The maximum number of members. */ static constexpr unsigned int memberCount() { return 16; } }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int channels() { return 0x0010; } static constexpr unsigned int betweenChannels() { return 0x0002; } /// @endcond }; }; /** Implements the base class for all zone banks of Radioddity codeplugs. * * Memory layout of the zone table/bank: * @verbinclude radioddity_zonebank.txt */ class ZoneBankElement: public Element { protected: /** Hidden constructor. */ ZoneBankElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ZoneBankElement(uint8_t *ptr); /** Destructor. */ ~ZoneBankElement(); /** The size of the zone element. */ static constexpr unsigned int size() { return 0x2f00; } /** Resets the bank. */ void clear(); /** Returns @c true if the channel is enabled. */ virtual bool isEnabled(unsigned idx) const ; /** Enable/disable a channel in the bank. */ virtual void enable(unsigned idx, bool enabled); /** Returns a pointer to the channel at the given index. */ virtual uint8_t *get(unsigned idx) const; /** Retunrs the n-th zone. */ ZoneElement zone(unsigned int n); public: /** Some limits for the zone bank. */ struct Limit { /** The maximum number of zones in this bank. */ static constexpr unsigned int zoneCount() { return 250; } }; protected: /** Some internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int bitmap() { return 0x0000; } static constexpr unsigned int zones() { return 0x0020; } /// @endcond }; }; /** Represents a base class for all group lists within Radioddity codeplugs. * * Memory layout of the RX group list: * @verbinclude radioddity_grouplist.txt */ class GroupListElement: public Element { protected: /** Hidden constructor. */ GroupListElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit GroupListElement(uint8_t *ptr); /** Destructor. */ virtual ~GroupListElement(); /** Size of the group list element. */ static constexpr unsigned int size() { return 0x0030; } /** Resets the group list. */ void clear(); /** Returns the name of the group list. */ virtual QString name() const; /** Sets the name of the group list. */ virtual void setName(const QString &name); /** Returns @c true if the group list has an n-th member. * That is if the n-th index is not 0. */ virtual bool hasMember(unsigned n) const; /** Returns the n-th member index (+1). */ virtual unsigned member(unsigned n) const; /** Sets the n-th member index (+1). */ virtual void setMember(unsigned n, unsigned idx); /** Clears the n-th member index. */ virtual void clearMember(unsigned n); /** Constructs a @c RXGroupList object from the codeplug representation. */ virtual RXGroupList *toRXGroupListObj(Context &ctx, const ErrorStack &err = ErrorStack()); /** Links a previously constructed @c RXGroupList to the rest of the generic configuration. */ virtual bool linkRXGroupListObj(unsigned int ncnt, RXGroupList *lst, Context &ctx, const ErrorStack &err = ErrorStack()) const; /** Reset this codeplug representation from a @c RXGroupList object. */ virtual bool fromRXGroupListObj(const RXGroupList *lst, Context &ctx, const ErrorStack &err = ErrorStack()); public: /** Some limits for group lists. */ struct Limit { static constexpr unsigned int nameLength() { return 16; } ///< Maximum name length. static constexpr unsigned int memberCount() { return 16; } ///< Maximum number of entries. }; protected: /** Internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int name() { return 0x0000; } static constexpr unsigned int members() { return 0x0010; } static constexpr unsigned int betweenMembers() { return 0x0002; } /// @endcond }; }; /** Implements a base class of group list memory banks for all Radioddity codeplugs. * * Memory layout of the group list table: * @verbinclude radioddity_grouplistbank.txt */ class GroupListBankElement: public Element { protected: /** Hidden constructor. */ GroupListBankElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit GroupListBankElement(uint8_t *ptr); /** Destructor. */ virtual ~GroupListBankElement(); /** The size of the group list bank element. */ static constexpr unsigned int size() { return 0x0c80; } /** Resets the bank. */ void clear(); /** Returns @c true if the n-th group list is enabled. */ virtual bool isEnabled(unsigned n) const; /** Returns the number of contacts in the n-th group list. */ virtual unsigned contactCount(unsigned n) const; /** Sets the number of contacts in the n-th group list. * This also enables the n-th group list. */ virtual void setContactCount(unsigned n, unsigned size); /** Disables the n-th group list. */ virtual void disable(unsigned n); /** Returns a pointer to the n-th group list. */ virtual uint8_t *get(unsigned n) const; public: /** Some limits for the group list bank. */ struct Limit { static constexpr unsigned int groupListCount() { return 64; } ///< Maximum number of group lists. }; protected: /** Internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int contactCounts() { return 0x0000; } static constexpr unsigned int groupLists() { return 0x0080; } /// @endcond }; }; /** Implements the base class for scan lists of all Radioddity codeplugs. * * Memory layout of the scan list. * @verbinclude radioddity_scanlist.txt */ class ScanListElement: public Element { public: /** Possible priority channel types. */ enum Mode { PL_NONPRI = 0, ///< Only non-priority channels. PL_DISABLE = 1, ///< Disable priority channels. PL_PRI = 2, ///< Only priority channels. PL_PRI_NONPRI = 3 ///< Priority and non-priority channels. }; protected: /** Hidden constructor. */ ScanListElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ScanListElement(uint8_t *ptr); /** Destructor. */ virtual ~ScanListElement(); /** The size of the scan list. */ static constexpr unsigned int size() { return 0x0058; } /** Resets the scan list. */ void clear(); /** Returns the name of the scan list. */ virtual QString name() const; /** Sets the name of the scan list. */ virtual void setName(const QString &name); /** Returns @c true if channel mark is enabled. */ virtual bool channelMark() const; /** Enables/disables channel mark. */ virtual void enableChannelMark(bool enable); /** Returns the scan mode. */ virtual Mode mode() const; /** Sets the scan mode. */ virtual void setMode(Mode mode); /** Returns @c true if talk back is enabled. */ virtual bool talkback() const; /** Enables/disables talk back. */ virtual void enableTalkback(bool enable); /** Returns @c true if the n-th member is set. */ virtual bool hasMember(unsigned n) const; /** Returns @c true if the n-th member is selected channel. */ virtual bool isSelected(unsigned n) const; /** Returns the n-th member index. */ virtual unsigned member(unsigned n) const; /** Sets the n-th member index. */ virtual void setMember(unsigned n, unsigned idx); /** Sets the n-th member to be the selected channel. */ virtual void setSelected(unsigned n); /** Clears the n-th member. */ virtual void clearMember(unsigned n); /** Returns @c true if the primary priority channel is set. */ virtual bool hasPrimary() const; /** Returns @c true if the primary priority channel is the selected channel. */ virtual bool primaryIsSelected() const; /** Return the channel index for the primary priority channel. */ virtual unsigned primary() const; /** Sets the primary priority channel index. */ virtual void setPrimary(unsigned idx); /** Sets the primary priority channel to be the selected channel. */ virtual void setPrimarySelected(); /** Clears the primary priority channel. */ virtual void clearPrimary(); /** Returns @c true if the secondary priority channel is set. */ virtual bool hasSecondary() const; /** Returns @c true if the secondary priority channel is the selected channel. */ virtual bool secondaryIsSelected() const; /** Return the channel index for the secondary priority channel. */ virtual unsigned secondary() const; /** Sets the secondary priority channel index. */ virtual void setSecondary(unsigned idx); /** Sets the secondary priority channel to be the selected channel. */ virtual void setSecondarySelected(); /** Clears the secondary priority channel. */ virtual void clearSecondary(); /** Returns @c true if the revert channel is set, if @c false the radio will transmit on the * last active channel during scan. */ virtual bool hasRevert() const; /** Returns @c true if the revert channel is the selected channel. */ virtual bool revertIsSelected() const; /** Return the channel index for the revert channel. */ virtual unsigned revert() const; /** Sets the revert channel index. */ virtual void setRevert(unsigned idx); /** Sets the revert channel to be the selected one. */ virtual void setRevertSelected(); /** Clears the revert channel, sets it to last active. */ virtual void clearRevert(); /** Returns the hold time in ms. */ virtual unsigned holdTime() const; /** Sets the hold time in ms. */ virtual void setHoldTime(unsigned ms); /** Returns the priority sample time in ms. */ virtual unsigned prioritySampleTime() const; /** Sets the priority sample time in ms. */ virtual void setPrioritySampleTime(unsigned ms); /** Constructs a @c ScanList object from this codeplug representation. */ virtual ScanList *toScanListObj(Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Links a previously constructed @c ScanList object to the rest of the generic configuration. */ virtual bool linkScanListObj(ScanList *lst, Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Initializes this codeplug representation from the given @c ScanList object. */ virtual bool fromScanListObj(const ScanList *lst, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for the scan list. */ struct Limit: public Element::Limit { /// Maximum name length. static constexpr unsigned int name() { return 15; } /// Maximum number of members. static constexpr unsigned int members() { return 32; } }; protected: /// @cond DO_NOT_DOCUMENT struct Offset: public Element::Offset { static constexpr unsigned int name() { return 0x0000; } static constexpr Bit channelMark() { return {0x000f, 4}; } static constexpr Bit mode() { return {0x000f, 6}; } static constexpr Bit talkback() { return {0x000f, 7}; } static constexpr unsigned int members() { return 0x0010; } static constexpr unsigned int betweenMembers() { return 0x0002; } static constexpr unsigned int primary() { return 0x0050; } static constexpr unsigned int secondary() { return 0x0052; } static constexpr unsigned int revert() { return 0x0054; } static constexpr unsigned int holdTime() { return 0x0056; } static constexpr unsigned int primaryHoldTime() { return 0x0057; } }; /// @endcond }; /** Implements the base class of scan lists banks for all Radioddity codeplugs. * * Memory layout of the scan list table. * @verbinclude radioddity_scanlistbank.txt */ class ScanListBankElement: public Element { protected: /** Hidden constructor. */ ScanListBankElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ScanListBankElement(uint8_t *ptr); /** Destructor. */ virtual ~ScanListBankElement(); /** The size of the scan list bank. */ static constexpr unsigned int size() { return 0x56f0; } /** Resets the scan list bank. */ void clear(); /** Returns @c true if the n-th scan list is enabled. */ virtual bool isEnabled(unsigned n) const; /** Enable/disable n-th scan list. */ virtual void enable(unsigned n, bool enabled); /** Returns a pointer to the n-th scan list. */ virtual uint8_t *get(unsigned n) const; public: /** Some limits for the scan list bank. */ struct Limit { static constexpr unsigned int scanListCount() { return 250; } ///< Maximum number of scan lists. }; protected: /** Internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int bytemap() { return 0x0000; } static constexpr unsigned int scanLists() { return 0x0040; } /// @endcond }; }; /** Implements the base class of general settings for all Radioddity codeplugs. * * Memory layout of the general settings * @verbinclude radioddity_generalsettings.txt */ class GeneralSettingsElement: public Element { public: /** Use monitor type from extension. */ typedef RadiodditySettingsExtension::MonitorType MonitorType; /** Use ARTS tone mode from extension. */ typedef RadioddityToneSettingsExtension::ARTSTone ARTSTone; /** Use scan mode from extension. */ typedef RadiodditySettingsExtension::ScanMode ScanMode; protected: /** Hidden constructor. */ GeneralSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit GeneralSettingsElement(uint8_t *ptr); /** Destructor. */ virtual ~GeneralSettingsElement(); /** The size of the element. */ static constexpr unsigned int size() { return 0x0028; } /** Resets the general settings. */ void clear(); /** Returns the radio name. */ virtual QString name() const; /** Sets the radio name. */ virtual void setName(const QString &name); /** Returns the DMR radio ID. */ virtual unsigned radioID() const; /** Sets the DMR radio ID. */ virtual void setRadioID(unsigned id); /** Returns the preamble duration in ms. */ virtual unsigned preambleDuration() const; /** Sets the preamble duration in ms. */ virtual void setPreambleDuration(unsigned ms); /** Returns the monitor type. */ virtual MonitorType monitorType() const; /** Sets the monitor type. */ virtual void setMonitorType(MonitorType type); /** Returns the VOX sensitivity [1-10], 0=disabled. */ virtual unsigned voxSensitivity() const; /** Sets the VOX sensitivity. */ virtual void setVOXSensitivity(unsigned value); /** Returns the low-battery warn interval in seconds. */ virtual unsigned lowBatteryWarnInterval() const; /** Sets the low-battery warn interval in seconds. */ virtual void setLowBatteryWarnInterval(unsigned sec); /** Returns the call-alert duration in seconds. */ virtual unsigned callAlertDuration() const; /** Sets the call-allert duration in seconds. */ virtual void setCallAlertDuration(unsigned sec); /** Returns the lone-worker response period in minutes. */ virtual unsigned loneWorkerResponsePeriod() const; /** Sets the lone-worker response period in minutes. */ virtual void setLoneWorkerResponsePeriod(unsigned min); /** Returns the lone-worker reminder period in seconds. */ virtual unsigned loneWorkerReminderPeriod() const; /** Sets the lone-worker reminder period in seconds. */ virtual void setLoneWorkerReminderPeriod(unsigned sec); /** Returns the group call hang time in ms. */ virtual unsigned groupCallHangTime() const; /** Sets the group call hang time in ms. */ virtual void setGroupCallHangTime(unsigned ms); /** Returns the private call hang time in ms. */ virtual unsigned privateCallHangTime() const; /** Sets the private call hang time in ms. */ virtual void setPrivateCallHangTime(unsigned ms); /** Returns @c true if the down-channel mode is VFO. */ virtual bool downChannelModeVFO() const; /** Enables/disables down-channel mode is VFO. */ virtual void enableDownChannelModeVFO(bool enable); /** Returns @c true if the up-channel mode is VFO. */ virtual bool upChannelModeVFO() const; /** Enables/disables up-channel mode is VFO. */ virtual void enableUpChannelModeVFO(bool enable); /** Returns @c true if the reset tone is enabled. */ virtual bool resetTone() const; /** Enables/disables reset tone. */ virtual void enableResetTone(bool enable); /** Returns @c true if the unknown number tone is enabled. */ virtual bool unknownNumberTone() const; /** Enables/disables reset tone. */ virtual void enableUnknownNumberTone(bool enable); /** Returns the ARTS tone mode. */ virtual ARTSTone artsToneMode() const; /** Sets the ARTS tone mode. */ virtual void setARTSToneMode(ARTSTone mode); /** Returns @c true if the digital channel talk permit tone is enabled. */ virtual bool digitalTalkPermitTone() const; /** Enables/disables digital channel talk permit tone. */ virtual void enableDigitalTalkPermitTone(bool enable); /** Returns @c true if the analog channel talk permit tone is enabled. */ virtual bool analogTalkPermitTone() const; /** Enables/disables analog channel talk permit tone. */ virtual void enableAnalogTalkPermitTone(bool enable); /** Returns @c true if the reset tone is enabled. */ virtual bool selftestTone() const; /** Enables/disables reset tone. */ virtual void enableSelftestTone(bool enable); /** Returns @c true if the channel free indication tone is enabled. */ virtual bool channelFreeIndicationTone() const; /** Enables/disables channel free indication tone. */ virtual void enableChannelFreeIndicationTone(bool enable); /** Returns @c true if all tones are disabled. */ virtual bool allTonesDisabled() const; /** Disables/enables all tones. */ virtual void disableAllTones(bool disable); /** Returns @c true if reception is disabled for battery saving. */ virtual bool batsaveRX() const; /** Enables/disables battery saving by disabling RX. */ virtual void enableBatsaveRX(bool enable); /** Returns @c true if preable is disabled for battery saving. */ virtual bool batsavePreamble() const; /** Enables/disables battery saving by disabling preamble. */ virtual void enableBatsavePreamble(bool enable); /** Returns @c true if all LEDs are disabled. */ virtual bool allLEDsDisabled() const; /** Disables/enables all LEDs. */ virtual void disableAllLEDs(bool disable); /** Returns true if quick-key override is inhibited. */ virtual bool quickKeyOverrideInhibited() const; /** Inhibits quick-key override. */ virtual void inhibitQuickKeyOverride(bool inhibit); /** Returns @c true if the TX exit tone is enabled. */ virtual bool txExitTone() const; /** Enables/disables TX exit tone. */ virtual void enableTXExitTone(bool enable); /** Returns @c true if the radio transmits on the active channel on double monitor. */ virtual bool txOnActiveChannel() const; /** Enables/disables transmission on active channel on double monitor. */ virtual void enableTXOnActiveChannel(bool enable); /** Returns @c true if animation is enabled. */ virtual bool animation() const; /** Enables/disables animation. */ virtual void enableAnimation(bool enable); /** Returns the scan mode. */ virtual ScanMode scanMode() const; /** Sets the scan mode. */ virtual void setScanMode(ScanMode mode); /** Returns the repeater end delay in [0-10]. */ virtual unsigned repeaterEndDelay() const; /** Sets the repeater end delay in [0-10]. */ virtual void setRepeaterEndDelay(unsigned delay); /** Returns the repeater STE in [0-10]. */ virtual unsigned repeaterSTE() const; /** Sets the repeater STE in [0-10]. */ virtual void setRepeaterSTE(unsigned ste); /** Returns @c true if a programming password is set. */ virtual bool hasProgPassword() const; /** Returns the programming password. */ virtual QString progPassword() const; /** Sets the programming password. */ virtual void setProgPassword(const QString &pwd); /** Resets the programming password. */ virtual void clearProgPassword(); /** Encodes the general setting from the given config. */ virtual bool fromConfig(Context &ctx, const ErrorStack &err=ErrorStack()); /** Updates the given config from this settings. */ virtual bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack()); }; /** Implements the base class of button settings for all Radioddity codeplugs. * * Encoding of button settings (size 0x20b): * @verbinclude radioddity_buttonsettings.txt */ class ButtonSettingsElement: public Element { public: /** Encoding/decoding of function key actions. */ struct KeyFunction { public: /** Encodes the given function. */ static uint8_t encode(RadioddityButtonSettingsExtension::Function func); /** Decodes the action. */ static RadioddityButtonSettingsExtension::Function decode(uint8_t action); protected: /** Possible function key actions. */ enum Action { None = 0x00, ///< Disables button. ToggleAllAlertTones = 0x01, EmergencyOn = 0x02, EmergencyOff = 0x03, ToggleMonitor = 0x05, ///< Toggle monitor on channel. NuiaceDelete = 0x06, OneTouch1 = 0x07, ///< Performs the first of 6 user-programmable actions (call, message). OneTouch2 = 0x08, ///< Performs the second of 6 user-programmable actions (call, message). OneTouch3 = 0x09, ///< Performs the third of 6 user-programmable actions (call, message). OneTouch4 = 0x0a, ///< Performs the fourth of 6 user-programmable actions (call, message). OneTouch5 = 0x0b, ///< Performs the fifth of 6 user-programmable actions (call, message). OneTouch6 = 0x0c, ///< Performs the sixt of 6 user-programmable actions (call, message). ToggleRepeatTalkaround = 0x0d, ToggleScan = 0x0e, TogglePrivacy = 0x10, ToggleVox = 0x11, ZoneSelect = 0x12, BatteryIndicator = 0x13, ToggleLoneWorker = 0x14, PhoneExit = 0x16, ToggleFlashLight = 0x1a, ToggleFMRadio = 0x1b }; }; /** Possible one-touch actions. */ enum class OneTouchAction { None = 0x00, ///< Disabled. DigitalCall = 0x10, ///< Calls a digital contact. DigitalMessage = 0x11, ///< Sends a SMS. AnalogCall = 0x20 ///< Calls an analog contact. }; protected: /** Hidden constructor */ ButtonSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit ButtonSettingsElement(uint8_t *ptr); /** Destructor. */ virtual ~ButtonSettingsElement(); /** Clears the button settings. */ void clear(); /** Returns the long-press duration in ms. */ virtual Interval longPressDuration() const; /** Sets the long-press duration in ms. */ virtual void setLongPressDuration(Interval ms); /** Returns the side-key 1 short-press action. */ virtual RadioddityButtonSettingsExtension::Function sk1ShortPress() const; /** Sets the side-key 1 short-press action. */ virtual void setSK1ShortPress(RadioddityButtonSettingsExtension::Function action); /** Returns the side-key 1 long-press action. */ virtual RadioddityButtonSettingsExtension::Function sk1LongPress() const; /** Sets the side-key 1 long-press action. */ virtual void setSK1LongPress(RadioddityButtonSettingsExtension::Function action); /** Returns the side-key 2 short-press action. */ virtual RadioddityButtonSettingsExtension::Function sk2ShortPress() const; /** Sets the side-key 2 short-press action. */ virtual void setSK2ShortPress(RadioddityButtonSettingsExtension::Function action); /** Returns the side-key 2 long-press action. */ virtual RadioddityButtonSettingsExtension::Function sk2LongPress() const; /** Sets the side-key 2 long-press action. */ virtual void setSK2LongPress(RadioddityButtonSettingsExtension::Function action); /** Returns the top-key short-press action. */ virtual RadioddityButtonSettingsExtension::Function tkShortPress() const; /** Sets the top-key short-press action. */ virtual void setTKShortPress(RadioddityButtonSettingsExtension::Function action); /** Returns the top-key long-press action. */ virtual RadioddityButtonSettingsExtension::Function tkLongPress() const; /** Sets the top-key long-press action. */ virtual void setTKLongPress(RadioddityButtonSettingsExtension::Function action); /** Returns the n-th one-touch action. */ virtual OneTouchAction oneTouchAction(unsigned n) const; /** Returns the n-th one-touch contact index (if action is @c OneTouchAction::DigitalCall). */ virtual unsigned oneTouchContact(unsigned n) const; /** Returns the n-th one-touch message index (if action is @c OneTouchAction::DigitalMessage). */ virtual unsigned oneTouchMessage(unsigned n) const; /** Disables the n-th one-touch action. */ virtual void disableOneTouch(unsigned n); /** Configures n-th one-touch action as a digital call to contact index. */ virtual void setOneTouchDigitalCall(unsigned n, unsigned index); /** Configures n-th one-touch action as a digital message using given index. */ virtual void setOneTouchDigitalMessage(unsigned n, unsigned index); /** Configures n-th one-touch action as a analog call. */ virtual void setOneTouchAnalogCall(unsigned n); /** Encodes the button settings (if set). */ bool encode(Context &ctx, const ErrorStack &err=ErrorStack()); /** Decodes the button settings. */ bool decode(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limits { /** Range of valid long-press durations. */ static constexpr TimeRange longPressDuration() { return TimeRange{Interval::fromMilliseconds(0), Interval::fromMilliseconds(255*250)}; } /** Number of one-touch actions. */ static constexpr unsigned int oneTouchActions() { return 6; } }; protected: /** Internal used offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int longPressDuration() { return 0x0001; } static constexpr unsigned int sk1ShortPress() { return 0x0002; } static constexpr unsigned int sk1LongPress() { return 0x0003; } static constexpr unsigned int sk2ShortPress() { return 0x0004; } static constexpr unsigned int sk2LongPress() { return 0x0005; } static constexpr unsigned int tkShortPress() { return 0x0006; } static constexpr unsigned int tkLongPress() { return 0x0007; } static constexpr unsigned int oneTouchActions() { return 0x0008; } static constexpr unsigned int betweenOneTouchActions() { return 0x0004; } /// @endcond }; }; /** Implements the base class of menu settings for all Radioddity codeplugs. * * Encoding of Menu settings (size 0x08b): * @verbinclude radioddity_menusettings.txt */ class MenuSettingsElement: public Element { public: /** Possible channel display modes. */ enum class ChannelDisplayMode { Number = 0, ///< Show channel number. Name = 1, ///< Show channel name. Frequency = 2 ///< Show channel frequency. }; /** Possible dual-watch modes. */ enum class DualWatchMode { DualDual = 1, DualSingle = 2 }; protected: /** Hidden constructor. */ MenuSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit MenuSettingsElement(uint8_t *ptr); /** Destructor. */ virtual ~MenuSettingsElement(); /** Resets the menu settings. */ void clear(); /** Returns the menu hang-time in seconds. */ virtual unsigned menuHangTime() const; /** Sets the menu hang time in seconds. */ virtual void setMenuHangTime(unsigned sec); /** Returns @c true if the message menu is shown. */ virtual bool message() const; /** Enables/disables the message menu. */ virtual void enableMessage(bool enable); /** Returns @c true if the scan-start menu is shown. */ virtual bool scanStart() const; /** Enables/disables the scan-start menu. */ virtual void enableScanStart(bool enable); /** Returns @c true if the edit scan-list menu is shown. */ virtual bool editScanList() const; /** Enables/disables the edit scan-list menu. */ virtual void enableEditScanList(bool enable); /** Returns @c true if the call-alert menu is shown. */ virtual bool callAlert() const; /** Enables/disables the call-alert menu. */ virtual void enableCallAlert(bool enable); /** Returns @c true if the edit-contact menu is shown. */ virtual bool editContact() const; /** Enables/disables the edit-contact menu. */ virtual void enableEditContact(bool enable); /** Returns @c true if the manual-dial menu is shown. */ virtual bool manualDial() const; /** Enables/disables the manual-dial menu. */ virtual void enableManualDial(bool enable); /** Returns @c true if the radio-check menu is shown. */ virtual bool radioCheck() const; /** Enables/disables the radioCheck menu. */ virtual void enableRadioCheck(bool enable); /** Returns @c true if the remote-monitor menu is shown. */ virtual bool remoteMonitor() const; /** Enables/disables the message menu. */ virtual void enableRemoteMonitor(bool enable); /** Returns @c true if the radio-enable menu is shown. */ virtual bool radioEnable() const; /** Enables/disables the radio-enable menu. */ virtual void enableRadioEnable(bool enable); /** Returns @c true if the radio-disable menu is shown. */ virtual bool radioDisable() const; /** Enables/disables the radio-disable menu. */ virtual void enableRadioDisable(bool enable); /** Returns @c true if the programming-password menu is shown. */ virtual bool progPassword() const; /** Enables/disables the programming-password menu. */ virtual void enableProgPassword(bool enable); /** Returns @c true if the talkaround menu is shown. */ virtual bool talkaround() const; /** Enables/disables the talkaround menu. */ virtual void enableTalkaround(bool enable); /** Returns @c true if the tone menu is shown. */ virtual bool tone() const; /** Enables/disables the tone menu. */ virtual void enableTone(bool enable); /** Returns @c true if the power menu is shown. */ virtual bool power() const; /** Enables/disables the power menu. */ virtual void enablePower(bool enable); /** Returns @c true if the backlight menu is shown. */ virtual bool backlight() const; /** Enables/disables the backlight menu. */ virtual void enableBacklight(bool enable); /** Returns @c true if the intro-screen menu is shown. */ virtual bool introScreen() const; /** Enables/disables the message menu. */ virtual void enableIntroScreen(bool enable); /** Returns @c true if the keypad-lock menu is shown. */ virtual bool keypadLock() const; /** Enables/disables the keypad-lock menu. */ virtual void enableKeypadLock(bool enable); /** Returns @c true if the LED-indicator menu is shown. */ virtual bool ledIndicator() const; /** Enables/disables the LED-indicator menu. */ virtual void enableLEDIndicator(bool enable); /** Returns @c true if the squelch menu is shown. */ virtual bool squelch() const; /** Enables/disables the squelch menu. */ virtual void enableSquelch(bool enable); /** Returns @c true if the privacy menu is shown. */ virtual bool privacy() const; /** Enables/disables the privacy menu. */ virtual void enablePrivacy(bool enable); /** Returns @c true if the VOX menu is shown. */ virtual bool vox() const; /** Enables/disables the VOX menu. */ virtual void enableVOX(bool enable); /** Returns @c true if the password-lock menu is shown. */ virtual bool passwordLock() const; /** Enables/disables the password-lock menu. */ virtual void enablePasswordLock(bool enable); /** Returns @c true if the missed-calls menu is shown. */ virtual bool missedCalls() const; /** Enables/disables the missed-calls menu. */ virtual void enableMissedCalls(bool enable); /** Returns @c true if the answered-calls menu is shown. */ virtual bool answeredCalls() const; /** Enables/disables the answered-calls menu. */ virtual void enableAnsweredCalls(bool enable); /** Returns @c true if the outgoing-calls menu is shown. */ virtual bool outgoingCalls() const; /** Enables/disables the outgoing-calls menu. */ virtual void enableOutgoingCalls(bool enable); /** Returns @c true if the channel display-mode menu is shown. */ virtual bool channelDisplay() const; /** Enables/disables the channel display mode menu. */ virtual void enableChannelDisplay(bool enable); /** Returns @c true if the dual-watch menu is shown. */ virtual bool dualWatch() const; /** Enables/disables the dual-watch menu. */ virtual void enableDualWatch(bool enable); /** Returns the keypad lock time in seconds. */ virtual unsigned keypadLockTime() const; /** Sets the keypad lock time in seconds. */ virtual void setKeypadLockTime(unsigned sec); /** Retunrns the backlight time in seconds. */ virtual unsigned backlightTime() const; /** Sets the backlight time in seconds. */ virtual void setBacklightTime(unsigned sec); /** Returns the channel display mode. */ virtual ChannelDisplayMode channelDisplayMode() const; /** Sets the channel display mode. */ virtual void setChannelDisplayMode(ChannelDisplayMode mode); /** Returns @c true if the keytone is enabled. */ virtual bool keyTone() const; /** Enables/disables the keytone. */ virtual void enableKeyTone(bool enable); /** Returns the dual-watch mode. */ virtual DualWatchMode dualWatchMode() const; /** Sets the dual-watch mode. */ virtual void setDualWatchMode(DualWatchMode mode); }; /** Implements the base class of boot settings for all Radioddity codeplugs. * * Encoding of boot settings (size 0x20b): * @verbinclude radioddity_bootsettings.txt */ class BootSettingsElement: public Element { protected: /** Hidden constructor. */ BootSettingsElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit BootSettingsElement(uint8_t *ptr); /** Destructor. */ virtual ~BootSettingsElement(); /** Resets the settings. */ void clear(); /** Returns @c true if the text is shown on boot, other wise an image is shown. */ virtual bool bootText() const; /** Enables/disables boot text. */ virtual void enableBootText(bool enable); /** Returns @c true if the boot password is enabled. */ virtual bool bootPasswordEnabled() const; /** Enables/disables the boot password. */ virtual void enableBootPassword(bool enable); /** Returns the boot password (6 digit). */ virtual unsigned bootPassword() const; /** Sets the boot password (6 digit). */ virtual void setBootPassword(unsigned passwd); }; /** Implements the base class of boot messages for all Radioddity codeplugs. * * Encoding of boot messages (size 0x20b): * @verbinclude radioddity_boottext.txt */ class BootTextElement: public Element { protected: /** Hidden constructor. */ BootTextElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit BootTextElement(uint8_t *ptr); /** Destructor. */ virtual ~BootTextElement(); /** The size of the boot text element. */ static constexpr unsigned int size() { return 0x0020; } /** Resets the intro text. */ void clear(); /** Returns the first line. */ virtual QString line1() const; /** Sets the first line. */ virtual void setLine1(const QString &text); /** Returns the Second line. */ virtual QString line2() const; /** Sets the second line. */ virtual void setLine2(const QString &text); /** Encodes boot text settings from configuration. */ virtual bool fromConfig(Context &ctx, const ErrorStack &err = ErrorStack()); /** Updates the configuration with the boot text settings. */ virtual bool updateConfig(Context &ctx, const ErrorStack &err = ErrorStack()); public: /** Some limits for this element. */ struct Limit { static constexpr unsigned int lineLength() { return 16; } ///< The maximum length of the boot text line. }; protected: /** Internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int line1() { return 0x0000;} static constexpr unsigned int line2() { return 0x0010;} /// @endcond }; }; /** Implements the base class of a message bank for all Radioddity message banks. * * Encoding of messages (size: 0x1248b): * @verbinclude radioddity_messagebank.txt */ class MessageBankElement: public Element { protected: /** Hidden constructor. */ MessageBankElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit MessageBankElement(uint8_t *ptr); /** Destructor. */ virtual ~MessageBankElement(); /** Returns the size of the message bank. */ static constexpr unsigned int size() { return 0x1248; } /** Resets all messages. */ void clear(); /** Returns the number of messages. */ virtual unsigned numMessages() const; /** Returns the n-th message. */ virtual QString message(unsigned n) const; /** Appends a message to the list. */ virtual void appendMessage(const QString msg); /** Encodes all preset messages. */ virtual bool encode(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack()); /** Decodes all preset messages. */ virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit { static constexpr unsigned int messages() { return 32; } ///< Maximum number of messages. static constexpr unsigned int messageLength() { return 144; } ///< Maximum length of each message. }; protected: /** Some internal used offset. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int messageConut() { return 0x0000; } static constexpr unsigned int messageLengths() { return 0x0008; } static constexpr unsigned int messages() { return 0x0048; } static constexpr unsigned int betweenMessages() { return Limit::messageLength(); } /// @endcond }; }; /** Represents all encryption keys and settings within the codeplug on the device. * * Memory representation of encryption settings: * @verbinclude radioddity_privacy.txt */ class EncryptionElement: public Codeplug::Element { public: /** Encodes possible privacy types. For now, only none (encryption disabled) and basic are * supported. */ enum class PrivacyType { None, ///< No encryption at all. Basic ///< Use basic DMR encryption. }; protected: /** Hidden constructor. */ EncryptionElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit EncryptionElement(uint8_t *ptr); /** Destructor. */ virtual ~EncryptionElement(); /** The size of the element. */ static constexpr unsigned int size() { return 0x0088; } void clear(); /** Returns the privacy type set. */ virtual PrivacyType privacyType() const; /** Sets the privacy type. */ virtual void setPrivacyType(PrivacyType type); /** Returns @c true if the n-th "basic" key (32bit) is set. * That is, if it is not filled with 0xff. */ virtual bool isBasicKeySet(unsigned n) const; /** Returns the n-th "basic" key (32bit). */ virtual QByteArray basicKey(unsigned n) const; /** Sets the n-th "basic" key (32bit). */ virtual void setBasicKey(unsigned n, const QByteArray &key); /** Resets the n-th basic key. */ virtual void clearBasicKey(unsigned n); /** Encodes given encryption extension. */ virtual bool fromCommercialExt(CommercialExtension *ext, Context &ctx, const ErrorStack &err=ErrorStack()); /** Constructs the encryption extension. */ virtual bool updateCommercialExt(Context &ctx, const ErrorStack &err=ErrorStack()); /** Links the given encryption extension. */ virtual bool linkCommercialExt(CommercialExtension *ext, Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some limits for this element. */ struct Limit { /** The maximum number of keys. */ static constexpr unsigned int keyCount() { return 16; } /** The required key size. */ static constexpr unsigned int keySize() { return 4; } }; protected: /** Internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int privacyType() { return 0x0000; } static constexpr unsigned int bitmap() { return 0x0002; } static constexpr unsigned int keys() { return 0x0008; } ///< Offset of the first key. static constexpr unsigned int key() { return 0x0008;} ///< Offset between keys. /// @endcond }; }; protected: /** Hidden constructor, use a device specific class to instantiate. */ explicit RadioddityCodeplug(QObject *parent=nullptr); public: /** Destructor. */ virtual ~RadioddityCodeplug(); /** Clears and resets the complete codeplug to some default values. */ virtual void clear(); bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const; bool decode(Config *config, const ErrorStack &err=ErrorStack()); bool postprocess(Config *config, const ErrorStack &err) const; Config *preprocess(Config *config, const ErrorStack &err) const; bool encode(Config *config, const Flags &flags = Flags(), const ErrorStack &err=ErrorStack()); public: /** Decodes the binary codeplug and stores its content in the given generic configuration using * the given context. */ virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the given generic configuration as a binary codeplug using the given context. */ virtual bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Clears the general settings in the codeplug. */ virtual void clearGeneralSettings() = 0; /** Updates the general settings from the given configuration. */ virtual bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Updates the given configuration from the general settings. */ virtual bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears the button settings. */ virtual void clearButtonSettings() = 0; /** Encodes button settings. */ virtual bool encodeButtonSettings(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack()) = 0; /** Decodes the button settings. */ virtual bool decodeButtonSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears the messages. */ virtual void clearMessages() = 0; /** Encodes preset messages. */ virtual bool encodeMessages(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack()) = 0; /** Decodes preset messages. */ virtual bool decodeMessages(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all contacts in the codeplug. */ virtual void clearContacts() = 0; /** Encodes all digital contacts in the configuration into the codeplug. */ virtual bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds a digital contact to the configuration for each one in the codeplug. */ virtual bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all DTMF contacts in the codeplug. */ virtual void clearDTMFContacts() = 0; /** Encodes all DTMF contacts. */ virtual bool encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds all DTMF contacts to the configuration. */ virtual bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clear all channels. */ virtual void clearChannels() = 0; /** Encode all channels. */ virtual bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds all defined channels to the configuration. */ virtual bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links all channels. */ virtual bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clear boot settings. */ virtual void clearBootSettings() = 0; /** Clears menu settings. */ virtual void clearMenuSettings() = 0; /** Clears boot text. */ virtual void clearBootText() = 0; /** Encodes boot text. */ virtual bool encodeBootText(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Updates the given configuration from the boot text settings. */ virtual bool decodeBootText(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears the VFO settings. */ virtual void clearVFOSettings() = 0; /** Clears all zones. */ virtual void clearZones() = 0; /** Encodes zones. */ virtual bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds zones to the configuration. */ virtual bool createZones(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links all zones within the configuration. */ virtual bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all scan lists. */ virtual void clearScanLists() = 0; /** Encodes all scan lists. */ virtual bool encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Creates all scan lists. */ virtual bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links all scan lists. */ virtual bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all group lists. */ virtual void clearGroupLists() = 0; /** Encodes all group lists. */ virtual bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Creates all group lists. */ virtual bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links all group lists. */ virtual bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all encryption keys. */ virtual void clearEncryption() = 0; /** Encodes all encryption keys defined. */ virtual bool encodeEncryption(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Creates all encryption keys. */ virtual bool createEncryption(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links all encryption keys. */ virtual bool linkEncryption(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; }; #endif // RADIODDITYCODEPLUG_HH qdmr-0.12.3/lib/radioddity_extensions.cc000066400000000000000000000407441501654372000202440ustar00rootroot00000000000000#include "radioddity_extensions.hh" /* ********************************************************************************************* * * Implementation of RadioddityButtonSettingsExtension * ********************************************************************************************* */ RadioddityButtonSettingsExtension::RadioddityButtonSettingsExtension(QObject *parent) : ConfigItem(parent), _longPressDuration(Interval::fromMilliseconds(1000)), _funcKey1Short(Function::ZoneSelect), _funcKey1Long(Function::ToggleFMRadio), _funcKey2Short(Function::ToggleMonitor), _funcKey2Long(Function::ToggleFlashLight), _funcKey3Short(Function::BatteryIndicator), _funcKey3Long(Function::ToggleVox) { // pass... } ConfigItem * RadioddityButtonSettingsExtension::clone() const { ConfigItem *clone = new RadioddityButtonSettingsExtension(); if (! clone->copy(*this)) { delete clone; return nullptr; } return clone; } Interval RadioddityButtonSettingsExtension::longPressDuration() const { return _longPressDuration; } void RadioddityButtonSettingsExtension::setLongPressDuration(Interval interval) { if (interval == _longPressDuration) return; _longPressDuration = interval; emit modified(this); } RadioddityButtonSettingsExtension::Function RadioddityButtonSettingsExtension::funcKey1Short() const { return _funcKey1Short; } void RadioddityButtonSettingsExtension::setFuncKey1Short(Function func) { if (func == _funcKey1Short) return; _funcKey1Short = func; emit modified(this); } RadioddityButtonSettingsExtension::Function RadioddityButtonSettingsExtension::funcKey1Long() const { return _funcKey1Long; } void RadioddityButtonSettingsExtension::setFuncKey1Long(Function func) { if (func == _funcKey1Long) return; _funcKey1Long = func; emit modified(this); } RadioddityButtonSettingsExtension::Function RadioddityButtonSettingsExtension::funcKey2Short() const { return _funcKey2Short; } void RadioddityButtonSettingsExtension::setFuncKey2Short(Function func) { if (func == _funcKey2Short) return; _funcKey2Short = func; emit modified(this); } RadioddityButtonSettingsExtension::Function RadioddityButtonSettingsExtension::funcKey2Long() const { return _funcKey2Long; } void RadioddityButtonSettingsExtension::setFuncKey2Long(Function func) { if (func == _funcKey2Long) return; _funcKey2Long = func; emit modified(this); } RadioddityButtonSettingsExtension::Function RadioddityButtonSettingsExtension::funcKey3Short() const { return _funcKey3Short; } void RadioddityButtonSettingsExtension::setFuncKey3Short(Function func) { if (func == _funcKey3Short) return; _funcKey3Short = func; emit modified(this); } RadioddityButtonSettingsExtension::Function RadioddityButtonSettingsExtension::funcKey3Long() const { return _funcKey3Long; } void RadioddityButtonSettingsExtension::setFuncKey3Long(Function func) { if (func == _funcKey3Long) return; _funcKey3Long = func; emit modified(this); } /* ********************************************************************************************* * * Implementation of RadioddityToneSettingsExtension * ********************************************************************************************* */ RadioddityToneSettingsExtension::RadioddityToneSettingsExtension(QObject *parent) : ConfigItem(parent), _lowBatteryWarn(true), _lowBatteryWarnInterval(Interval::fromSeconds(30)), _lowBatteryWarnVolume(5), _callAlertDuration(Interval::fromSeconds(120)), _resetTone(false), _unknownNumberTone(false), _artsToneMode(ARTSTone::Once), _digitalTalkPermitTone(false), _analogTalkPermitTone(false), _selftestTone(true), _channelFreeIndicationTone(false), _disableAllTones(false), _txExitTone(false), _keyTone(false), _keyToneVolume(5), _fmMicGain(5) { // pass... } ConfigItem * RadioddityToneSettingsExtension::clone() const { RadioddityToneSettingsExtension *ext = new RadioddityToneSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } bool RadioddityToneSettingsExtension::lowBatteryWarn() const { return _lowBatteryWarn; } void RadioddityToneSettingsExtension::enableLowBatteryWarn(bool enable) { if (enable == _lowBatteryWarn) return; _lowBatteryWarn = enable; emit modified(this); } Interval RadioddityToneSettingsExtension::lowBatteryWarnInterval() const { return _lowBatteryWarnInterval; } void RadioddityToneSettingsExtension::setLowBatteryWarnInterval(Interval sec) { if (_lowBatteryWarnInterval == sec) return; _lowBatteryWarnInterval = sec; emit modified(this); } unsigned int RadioddityToneSettingsExtension::lowBatteryWarnVolume() const { return _lowBatteryWarnVolume; } void RadioddityToneSettingsExtension::setLowBatteryWarnVolume(unsigned int volume) { volume = std::min(10U, std::max(1U, volume)); if (volume == _lowBatteryWarnVolume) return; _lowBatteryWarnVolume = volume; emit modified(this); } Interval RadioddityToneSettingsExtension::callAlertDuration() const { return _callAlertDuration; } void RadioddityToneSettingsExtension::setCallAlertDuration(Interval sec) { if (_callAlertDuration == sec) return; _callAlertDuration = sec; emit modified(this); } bool RadioddityToneSettingsExtension::resetTone() const { return _resetTone; } void RadioddityToneSettingsExtension::enableResetTone(bool enable) { if (_resetTone == enable) return; _resetTone = enable; emit modified(this); } bool RadioddityToneSettingsExtension::unknownNumberTone() const { return _unknownNumberTone; } void RadioddityToneSettingsExtension::enableUnknownNumberTone(bool enable) { if (_unknownNumberTone == enable) return; _unknownNumberTone = enable; emit modified(this); } RadioddityToneSettingsExtension::ARTSTone RadioddityToneSettingsExtension::artsToneMode() const { return _artsToneMode; } void RadioddityToneSettingsExtension::setARTSToneMode(ARTSTone mode) { if (_artsToneMode == mode) return; _artsToneMode = mode; emit modified(this); } bool RadioddityToneSettingsExtension::digitalTalkPermitTone() const { return _digitalTalkPermitTone; } void RadioddityToneSettingsExtension::enableDigitalTalkPermitTone(bool enable) { if (_digitalTalkPermitTone == enable) return; _digitalTalkPermitTone = enable; emit modified(this); } bool RadioddityToneSettingsExtension::analogTalkPermitTone() const { return _analogTalkPermitTone; } void RadioddityToneSettingsExtension::enableAnalogTalkPermitTone(bool enable) { if (_analogTalkPermitTone == enable) return; _analogTalkPermitTone = enable; emit modified(this); } bool RadioddityToneSettingsExtension::selftestTone() const { return _selftestTone; } void RadioddityToneSettingsExtension::enableSelftestTone(bool enable) { if (_selftestTone == enable) return; _selftestTone = enable; emit modified(this); } bool RadioddityToneSettingsExtension::channelFreeIndicationTone() const { return _channelFreeIndicationTone; } void RadioddityToneSettingsExtension::enableChannelFreeIndicationTone(bool enable) { if (_channelFreeIndicationTone == enable) return; _channelFreeIndicationTone = enable; emit modified(this); } bool RadioddityToneSettingsExtension::allTonesDisabled() const { return _disableAllTones; } void RadioddityToneSettingsExtension::disableAllTones(bool disable) { if (_disableAllTones == disable) return; _disableAllTones = disable; emit modified(this); } bool RadioddityToneSettingsExtension::txExitTone() const { return _txExitTone; } void RadioddityToneSettingsExtension::enableTXExitTone(bool enable) { if (_txExitTone == enable) return; _txExitTone = enable; emit modified(this); } bool RadioddityToneSettingsExtension::keyTone() const { return _keyTone; } void RadioddityToneSettingsExtension::enableKeyTone(bool enable) { if (enable == _keyTone) return; _keyTone = enable; emit modified(this); } unsigned int RadioddityToneSettingsExtension::keyToneVolume() const { return _keyToneVolume; } void RadioddityToneSettingsExtension::setKeyToneVolume(unsigned int volume) { volume = std::min(10U, std::max(1U, volume)); if (volume == _keyToneVolume) return; _keyToneVolume = volume; emit modified(this); } unsigned int RadioddityToneSettingsExtension::fmMicGain() const { return _fmMicGain; } void RadioddityToneSettingsExtension::setFMMicGain(unsigned int gain) { gain = std::min(10U, std::max(1U, gain)); if (gain == _fmMicGain) return; _fmMicGain = gain; emit modified(this); } /* ********************************************************************************************* * * Implementation of RadioddityBootSettingsExtension * ********************************************************************************************* */ RadioddityBootSettingsExtension::RadioddityBootSettingsExtension(QObject *parent) : ConfigItem(parent), _displayMode(DisplayMode::Text), _bootPasswd(), _progPasswd() { // pass... } ConfigItem * RadioddityBootSettingsExtension::clone() const { auto *ext = new RadioddityBootSettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } RadioddityBootSettingsExtension::DisplayMode RadioddityBootSettingsExtension::display() const { return _displayMode; } void RadioddityBootSettingsExtension::setDisplay(DisplayMode mode) { if (_displayMode == mode) return; _displayMode = mode; emit modified(this); } const QString & RadioddityBootSettingsExtension::bootPassword() const { return _bootPasswd; } void RadioddityBootSettingsExtension::setBootPassword(const QString &pwd) { if (_bootPasswd == pwd) return; _bootPasswd = pwd; emit modified(this); } const QString & RadioddityBootSettingsExtension::progPassword() const { return _progPasswd; } void RadioddityBootSettingsExtension::setProgPassword(const QString &pwd) { if (_progPasswd == pwd) return; _progPasswd = pwd; emit modified(this); } /* ********************************************************************************************* * * Implementation of RadiodditySettingsExtension * ********************************************************************************************* */ RadiodditySettingsExtension::RadiodditySettingsExtension(QObject *parent) : ConfigExtension(parent), _monitorType(MonitorType::Silent), _loneWorkerResponseTime(Interval::fromMinutes(1)), _loneWorkerReminderPeriod(Interval::fromSeconds(10)), _groupCallHangTime(Interval::fromMilliseconds(3000)), _privateCallHangTime(Interval::fromMilliseconds(3000)), _downChannelModeVFO(false), _upChannelModeVFO(false), _powerSaveMode(true), _wakeupPreamble(true), _preambleDuration(Interval::fromMilliseconds(360)), _powerSaveDelay(Interval::fromSeconds(10)), _disableAllLEDs(false), _quickKeyOverrideInhibited(false), _txOnActiveChannel(true), _scanMode(ScanMode::Time), _repeaterEndDelay(), _repeaterSTE(), _txInterrupt(false), _language(Language::English), _buttonSettings(new RadioddityButtonSettingsExtension(this)), _toneSettings(new RadioddityToneSettingsExtension(this)), _bootSettings(new RadioddityBootSettingsExtension(this)) { // pass... } ConfigItem * RadiodditySettingsExtension::clone() const { RadiodditySettingsExtension *ext = new RadiodditySettingsExtension(); if (! ext->copy(*this)) { ext->deleteLater(); return nullptr; } return ext; } RadiodditySettingsExtension::MonitorType RadiodditySettingsExtension::monitorType() const { return _monitorType; } void RadiodditySettingsExtension::setMonitorType(MonitorType type) { if (_monitorType == type) return; _monitorType = type; emit modified(this); } Interval RadiodditySettingsExtension::loneWorkerResponseTime() const { return _loneWorkerResponseTime; } void RadiodditySettingsExtension::setLoneWorkerResponseTime(Interval min) { if (_loneWorkerResponseTime == min) return; _loneWorkerResponseTime = min; emit modified(this); } Interval RadiodditySettingsExtension::loneWorkerReminderPeriod() const { return _loneWorkerReminderPeriod; } void RadiodditySettingsExtension::setLoneWorkerReminderPeriod(Interval sec) { if (_loneWorkerReminderPeriod == sec) return; _loneWorkerReminderPeriod = sec; emit modified(this); } Interval RadiodditySettingsExtension::groupCallHangTime() const { return _groupCallHangTime; } void RadiodditySettingsExtension::setGroupCallHangTime(Interval ms) { if (_groupCallHangTime == ms) return; _groupCallHangTime = ms; emit modified(this); } Interval RadiodditySettingsExtension::privateCallHangTime() const { return _privateCallHangTime; } void RadiodditySettingsExtension::setPrivateCallHangTime(Interval ms) { if (_privateCallHangTime == ms) return; _privateCallHangTime = ms; emit modified(this); } bool RadiodditySettingsExtension::downChannelModeVFO() const { return _downChannelModeVFO; } void RadiodditySettingsExtension::enableDownChannelModeVFO(bool enable) { if (_downChannelModeVFO == enable) return; _downChannelModeVFO = enable; emit modified(this); } bool RadiodditySettingsExtension::upChannelModeVFO() const { return _upChannelModeVFO; } void RadiodditySettingsExtension::enableUpChannelModeVFO(bool enable) { if (_upChannelModeVFO == enable) return; _upChannelModeVFO = enable; emit modified(this); } bool RadiodditySettingsExtension::powerSaveMode() const { return _powerSaveMode; } void RadiodditySettingsExtension::enablePowerSaveMode(bool enable) { if (_powerSaveMode == enable) return; _powerSaveMode = enable; emit modified(this); } bool RadiodditySettingsExtension::wakeupPreamble() const { return _wakeupPreamble; } void RadiodditySettingsExtension::enableWakeupPreamble(bool enable) { if (_wakeupPreamble == enable) return; _wakeupPreamble = enable; emit modified(this); } Interval RadiodditySettingsExtension::preambleDuration() const { return _preambleDuration; } void RadiodditySettingsExtension::setPreambleDuration(Interval ms) { if (_preambleDuration == ms) return; _preambleDuration = ms; emit modified(this); } Interval RadiodditySettingsExtension::powerSaveDelay() const { return _powerSaveDelay; } void RadiodditySettingsExtension::setPowerSaveDelay(Interval interv) { if (interv == _powerSaveDelay) return; _powerSaveDelay = interv; emit modified(this); } bool RadiodditySettingsExtension::allLEDsDisabled() const { return _disableAllLEDs; } void RadiodditySettingsExtension::disableAllLEDs(bool disable) { if (_disableAllLEDs == disable) return; _disableAllLEDs = disable; emit modified(this); } bool RadiodditySettingsExtension::quickKeyOverrideInhibited() const { return _quickKeyOverrideInhibited; } void RadiodditySettingsExtension::inhibitQuickKeyOverride(bool inhibit) { if (_quickKeyOverrideInhibited == inhibit) return; _quickKeyOverrideInhibited = inhibit; emit modified(this); } bool RadiodditySettingsExtension::txOnActiveChannel() const { return _txOnActiveChannel; } void RadiodditySettingsExtension::enableTXOnActiveChannel(bool enable) { if (_txOnActiveChannel == enable) return; _txOnActiveChannel = enable; emit modified(this); } RadiodditySettingsExtension::ScanMode RadiodditySettingsExtension::scanMode() const { return _scanMode; } void RadiodditySettingsExtension::setScanMode(ScanMode mode) { if (_scanMode == mode) return; _scanMode = mode; emit modified(this); } Interval RadiodditySettingsExtension::repeaterEndDelay() const { return _repeaterEndDelay; } void RadiodditySettingsExtension::setRepeaterEndDelay(Interval delay) { if (_repeaterEndDelay == delay) return; _repeaterEndDelay = delay; emit modified(this); } Interval RadiodditySettingsExtension::repeaterSTE() const { return _repeaterSTE; } void RadiodditySettingsExtension::setRepeaterSTE(Interval ste) { if (_repeaterSTE == ste) return; _repeaterSTE = ste; emit modified(this); } bool RadiodditySettingsExtension::txInterrupt() const { return _txInterrupt; } void RadiodditySettingsExtension::enableTXInterrupt(bool enable) { if (enable == _txInterrupt) return; _txInterrupt = enable; emit modified(this); } RadiodditySettingsExtension::Language RadiodditySettingsExtension::language() const { return _language; } void RadiodditySettingsExtension::setLanguage(Language lang) { if (lang == _language) return; _language = lang; emit modified(this); } RadioddityButtonSettingsExtension * RadiodditySettingsExtension::buttons() const { return _buttonSettings; } RadioddityToneSettingsExtension * RadiodditySettingsExtension::tone() const { return _toneSettings; } RadioddityBootSettingsExtension * RadiodditySettingsExtension::boot() const { return _bootSettings; } qdmr-0.12.3/lib/radioddity_extensions.hh000066400000000000000000000537611501654372000202610ustar00rootroot00000000000000#ifndef RADIODDITYEXTENSIONS_HH #define RADIODDITYEXTENSIONS_HH #include "configobject.hh" #include "interval.hh" /** Represents the button settings extension for all radioddity devices. * This object is part of the RadiodditySettingsExtension instance. */ class RadioddityButtonSettingsExtension: public ConfigItem { Q_OBJECT /** The long-press duration. */ Q_PROPERTY(Interval longPressDuration READ longPressDuration WRITE setLongPressDuration) /** The short-press action for the programmable function key 1 (SK1, P1). */ Q_PROPERTY(Function funcKey1Short READ funcKey1Short WRITE setFuncKey1Short) /** The long-press action for the programmable function key 1 (SK1, P1). */ Q_PROPERTY(Function funcKey1Long READ funcKey1Long WRITE setFuncKey1Long) /** The short-press action for the programmable function key 2 (SK2, P2). */ Q_PROPERTY(Function funcKey2Short READ funcKey2Short WRITE setFuncKey2Short) /** The long-press action for the programmable function key 2 (SK2, P2). */ Q_PROPERTY(Function funcKey2Long READ funcKey2Long WRITE setFuncKey2Long) /** The short-press action for the programmable function key 3 (TK). */ Q_PROPERTY(Function funcKey3Short READ funcKey3Short WRITE setFuncKey3Short) /** The long-press action for the programmable function key 3 (TK). */ Q_PROPERTY(Function funcKey3Long READ funcKey3Long WRITE setFuncKey3Long) public: /** Possible function key actions. Not all functions are present on all devices. */ enum class Function { None, ToggleAllAlertTones, EmergencyOn, EmergencyOff, ToggleMonitor, OneTouch1, OneTouch2, OneTouch3, OneTouch4, OneTouch5, OneTouch6, ToggleTalkaround, ToggleScan, ToggleEncryption, ToggleVox, ZoneSelect, BatteryIndicator, ToggleLoneWorker, PhoneExit, ToggleFlashLight, ToggleFMRadio, RadioEnable, RadioCheck, RadioDisable, PowerLevel, TBST, CallSwell }; Q_ENUM(Function) public: /** Default constructor. */ explicit RadioddityButtonSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the long-press duration, usually specified in ms. */ Interval longPressDuration() const; /** Sets the long-press duration, usually specified in ms. */ void setLongPressDuration(Interval interval); /** Returns the short-press function of the progammable function key 1 (SK1, P1). */ Function funcKey1Short() const; /** Sets the short-press function of the progammable function key 1 (SK1, P1). */ void setFuncKey1Short(Function func); /** Returns the long-press function of the progammable function key 1 (SK1, P1). */ Function funcKey1Long() const; /** Sets the long-press function of the progammable function key 1 (SK1, P1). */ void setFuncKey1Long(Function func); /** Returns the short-press function of the progammable function key 2 (SK2, P2). */ Function funcKey2Short() const; /** Sets the short-press function of the progammable function key 2 (SK2, P2). */ void setFuncKey2Short(Function func); /** Returns the long-press function of the progammable function key 2 (SK2, P2). */ Function funcKey2Long() const; /** Sets the long-press function of the progammable function key 2 (SK2, P2). */ void setFuncKey2Long(Function func); /** Returns the short-press function of the progammable function key 3 (TK). */ Function funcKey3Short() const; /** Sets the short-press function of the progammable function key 3 (TK). */ void setFuncKey3Short(Function func); /** Returns the long-press function of the progammable function key 3 (TK). */ Function funcKey3Long() const; /** Sets the long-press function of the progammable function key 3 (TK). */ void setFuncKey3Long(Function func); protected: /** The long-press duration. */ Interval _longPressDuration; /** The short-press action for the programmable function key 1 (SK1, P1). */ Function _funcKey1Short; /** The long-press action for the programmable function key 1 (SK1, P1). */ Function _funcKey1Long; /** The short-press action for the programmable function key 2 (SK2, P2). */ Function _funcKey2Short; /** The long-press action for the programmable function key 2 (SK2, P2). */ Function _funcKey2Long; /** The short-press action for the programmable function key 3 (TK). */ Function _funcKey3Short; /** The long-press action for the programmable function key 3 (TK). */ Function _funcKey3Long; }; /** Tone settings for Radioddity devices. */ class RadioddityToneSettingsExtension: public ConfigItem { Q_OBJECT /** If @c true, the low battery warning is enabled. (GD-73 only) */ Q_PROPERTY(bool lowBatteryWarn READ lowBatteryWarn WRITE enableLowBatteryWarn) /** The low-battery warn interval in seconds. */ Q_PROPERTY(Interval lowBatteryWarnInterval READ lowBatteryWarnInterval WRITE setLowBatteryWarnInterval) /** Returns the low-battery warning volume [1,10]. (GD-73 only)*/ Q_PROPERTY(bool lowBatteryWarnVolume READ lowBatteryWarnVolume WRITE setLowBatteryWarnVolume) /** If @c true, the key tones are enabled. */ Q_PROPERTY(bool keyTone READ keyTone WRITE enableKeyTone) /** Returns the key-tone volume. */ Q_PROPERTY(bool keyToneVolume READ keyToneVolume WRITE setKeyToneVolume) /** The call-alert duration in seconds. */ Q_PROPERTY(Interval callAlertDuration READ callAlertDuration WRITE setCallAlertDuration) /** If @c true the reset tone is enabled. */ Q_PROPERTY(bool resetTone READ resetTone WRITE enableResetTone) /** @c true, the unknown number tone is enabled. */ Q_PROPERTY(bool unknownNumberTone READ unknownNumberTone WRITE enableUnknownNumberTone) /** The ARTS tone mode. */ Q_PROPERTY(ARTSTone artsToneMode READ artsToneMode WRITE setARTSToneMode) /** If @c true, the talk permit tone is enabled for digital channels. */ Q_PROPERTY(bool digitalTalkPermitTone READ digitalTalkPermitTone WRITE enableDigitalTalkPermitTone) /** If @c true, the talk permit tone is enabled for analog channels. */ Q_PROPERTY(bool analogTalkPermitTone READ analogTalkPermitTone WRITE enableAnalogTalkPermitTone) /** If @c true, the self-test tone is enabled. */ Q_PROPERTY(bool selftestTone READ selftestTone WRITE enableSelftestTone) /** If @c true, the frequency indication tone is enabled. */ Q_PROPERTY(bool channelFreeIndicationTone READ channelFreeIndicationTone WRITE enableChannelFreeIndicationTone) /** If @c true, all tones are disabled. */ Q_PROPERTY(bool allTonesDisabled READ allTonesDisabled WRITE disableAllTones) /** If @c true, the TX exit tone is enabled. */ Q_PROPERTY(bool txExitTone READ txExitTone WRITE enableTXExitTone) /** Sets the FM mic gain (GD-73 only). */ Q_PROPERTY(unsigned int fmMicGain READ fmMicGain WRITE setFMMicGain); public: /** Possible ARTS tone settings. */ enum class ARTSTone { Disabled = 0, ///< ARTS tone is disabled. Once = 4, ///< ARTS tone once. Always = 8 ///< ARTS tone always. }; Q_ENUM(ARTSTone) public: /** Default constructor. */ explicit RadioddityToneSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns @c true if a low battery charge is indicated by a warning. */ bool lowBatteryWarn() const; /** Enables/disables low-battery warning. */ void enableLowBatteryWarn(bool enable); /** Returns the low-battery warn interval in seconds. */ Interval lowBatteryWarnInterval() const; /** Sets the low-battery warn interval in seconds. */ void setLowBatteryWarnInterval(Interval sec); /** Returns the volume of the low-battery warning tone [1,10]. */ unsigned int lowBatteryWarnVolume() const; /** Sets the volume of the low-battery warning tone [1,10]. */ void setLowBatteryWarnVolume(unsigned int); /** Retruns @c true, if the key tones are enabled. */ bool keyTone() const; /** Enables/disables key tones. */ void enableKeyTone(bool enable); /** Retruns the key-tone volume. */ unsigned int keyToneVolume() const; /** Sets the key-tone volume. */ void setKeyToneVolume(unsigned int volume); /** Returns the call-alert duration in seconds. */ Interval callAlertDuration() const; /** Sets the call-allert duration in seconds. */ void setCallAlertDuration(Interval sec); /** Returns @c true if the reset tone is enabled. */ bool resetTone() const; /** Enables/disables reset tone. */ void enableResetTone(bool enable); /** Returns @c true if the unknown number tone is enabled. */ bool unknownNumberTone() const; /** Enables/disables reset tone. */ void enableUnknownNumberTone(bool enable); /** Returns the ARTS tone mode. */ ARTSTone artsToneMode() const; /** Sets the ARTS tone mode. */ void setARTSToneMode(ARTSTone mode); /** Returns @c true if the digital channel talk permit tone is enabled. */ bool digitalTalkPermitTone() const; /** Enables/disables digital channel talk permit tone. */ void enableDigitalTalkPermitTone(bool enable); /** Returns @c true if the analog channel talk permit tone is enabled. */ bool analogTalkPermitTone() const; /** Enables/disables analog channel talk permit tone. */ void enableAnalogTalkPermitTone(bool enable); /** Returns @c true if the self-test tone is enabled. */ bool selftestTone() const; /** Enables/disables self-test tone. */ void enableSelftestTone(bool enable); /** Returns @c true if the channel-free indication tone is enabled. */ bool channelFreeIndicationTone() const; /** Enables/disables the channel free indication tone. */ void enableChannelFreeIndicationTone(bool enable); /** Returns @c true if all tones are disabled. */ bool allTonesDisabled() const; /** Disables/enables all tones. */ void disableAllTones(bool disable); /** Returns @c true if the TX exit tone is enabled. */ bool txExitTone() const; /** Enables/disables TX exit tone. */ void enableTXExitTone(bool enable); /** Returns the FM mic gain [1,10]. */ unsigned int fmMicGain() const; /** Sets the FM mic gain [1,10]. */ void setFMMicGain(unsigned int gain); protected: /** If @c true, a low-battery charge is indicated by a warning. */ bool _lowBatteryWarn; /** Holds the low-battery warn interval in seconds. */ Interval _lowBatteryWarnInterval; /** Holds the volume of the low-battery warning tone. */ unsigned int _lowBatteryWarnVolume; /** Holds the call alert duration in seconds. */ Interval _callAlertDuration; /** If @c true the reset tone is enabled. */ bool _resetTone; /** If @c true, the unknown number tone is enabled. */ bool _unknownNumberTone; /** Holds the ARTS tone mode. */ ARTSTone _artsToneMode; /** If @c true, the talk permit tone is enabled for digital channels. */ bool _digitalTalkPermitTone; /** If @c true, the talk permit tone is enabled for analog channels. */ bool _analogTalkPermitTone; /** If @c true, the self-test tone is enabled. */ bool _selftestTone; /** If @c true, the channel free indication tone is enabled. */ bool _channelFreeIndicationTone; /** If @c true, all tones are disabled. */ bool _disableAllTones; /** If @c true, the TX exit tone is enabled. */ bool _txExitTone; /** If @c true, the key-tones are enabled. */ bool _keyTone; /** Holds the key-tone volume [1,10]. */ unsigned int _keyToneVolume; /** The FM mic gain. */ unsigned int _fmMicGain; }; /** Represents the boot settings for Radioddity devices. * This settings extension is part of the RadiodditySettingsExtension. */ class RadioddityBootSettingsExtension: public ConfigItem { Q_OBJECT /** The boot display mode. */ Q_PROPERTY(DisplayMode display READ display WRITE setDisplay) /** The programming password, disabled if empty. */ Q_PROPERTY(QString bootPassword READ bootPassword WRITE setBootPassword) /** The programming password, disabled if empty. */ Q_PROPERTY(QString progPassword READ progPassword WRITE setProgPassword) public: /** Possible boot display modes. */ enum class DisplayMode { None, Text, Image }; Q_ENUM(DisplayMode) public: /** Default constructor. */ explicit RadioddityBootSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the boot display mode. */ DisplayMode display() const; /** Sets the boot display mode. */ void setDisplay(DisplayMode mode); /** Returns the boot password. */ const QString &bootPassword() const; /** Sets the boot password. */ void setBootPassword(const QString &pwd); /** Returns the programming password. */ const QString &progPassword() const; /** Sets the programming password. */ void setProgPassword(const QString &pwd); protected: /** The boot display mode. */ DisplayMode _displayMode; /** Holds the boot password, disabled if empty. */ QString _bootPasswd; /** Holds the programming password, disabled if empty. */ QString _progPasswd; }; /** Represents the general settings extension for Radioddity devices. * @ingroup radioddity */ class RadiodditySettingsExtension: public ConfigExtension { Q_OBJECT /** The monitor type. */ Q_PROPERTY(MonitorType monitorType READ monitorType WRITE setMonitorType) /** The lone-worker response time in minutes. */ Q_PROPERTY(Interval loneWorkerResponseTime READ loneWorkerResponseTime WRITE setLoneWorkerResponseTime) /** The lonw-worker reminder period in seconds. */ Q_PROPERTY(Interval loneWorkerReminderPeriod READ loneWorkerReminderPeriod WRITE setLoneWorkerReminderPeriod) /** The group-call hang-time in ms. */ Q_PROPERTY(Interval groupCallHangTime READ groupCallHangTime WRITE setGroupCallHangTime) /** The private-call hang-time in ms. */ Q_PROPERTY(Interval privateCallHangTime READ privateCallHangTime WRITE setPrivateCallHangTime) /** If @c true the down-channel mode is VFO. */ Q_PROPERTY(bool downChannelModeVFO READ downChannelModeVFO WRITE enableDownChannelModeVFO) /** If @c true the up-channel mode is VFO. */ Q_PROPERTY(bool upChannelModeVFO READ upChannelModeVFO WRITE enableUpChannelModeVFO) /** If @c true, the power save mode is enabled. */ Q_PROPERTY(bool powerSaveMode READ powerSaveMode WRITE enablePowerSaveMode) Q_CLASSINFO("powerSaveModeDescription", "Puts the radio into sleep-mode when idle.") Q_CLASSINFO("powerSaveModeLongDescription", "When enabled, the radio enters a sleep mode when idle. That is, when on receive and " "there is no activity on the current channel. However, the radio may need some time " "to wake up from this mode. Hence, the 'wakeupPreamble' need to be enabled by all " "radios in the network to provide this wake-up delay.") /** If @c true, a wakeup preamble is sent. */ Q_PROPERTY(bool wakeupPreamble READ wakeupPreamble WRITE enableWakeupPreamble) /** The preamble duration in ms. */ Q_PROPERTY(Interval preambleDuration READ preambleDuration WRITE setPreambleDuration) Q_CLASSINFO("wakeupPreambleDescription", "If enabled, the radio will transmit a short wake-up " "preamble before each call.") /** The delay, before the idle radio enters power save mode (if enabled). */ Q_PROPERTY(Interval powerSaveDelay READ powerSaveDelay WRITE setPowerSaveDelay) /** If @c true, all LEDs are disabled. */ Q_PROPERTY(bool allLEDsDisabled READ allLEDsDisabled WRITE disableAllLEDs) /** If @c true, the quick-key override is inhibited. */ Q_PROPERTY(bool quickKeyOverrideInhibited READ quickKeyOverrideInhibited WRITE inhibitQuickKeyOverride) /** If @c true, the radio will transmit on the active channel when double-wait is enabled. */ Q_PROPERTY(bool txOnActiveChannel READ txOnActiveChannel WRITE enableTXOnActiveChannel) /** The scan mode. */ Q_PROPERTY(ScanMode scanMode READ scanMode WRITE setScanMode) /** The repeater end delay in seconds. */ Q_PROPERTY(Interval repeaterEndDelay READ repeaterEndDelay WRITE setRepeaterEndDelay) /** The repeater STE in seconds. */ Q_PROPERTY(Interval repeaterSTE READ repeaterSTE WRITE setRepeaterSTE) /** Returns @c true, if the TX interrupt is enabled. */ Q_PROPERTY(bool txInterrupt READ txInterrupt WRITE enableTXInterrupt) /** UI language. */ Q_PROPERTY(Language language READ language WRITE setLanguage) /** The button settings. */ Q_PROPERTY(RadioddityButtonSettingsExtension *buttons READ buttons) /** The tone settings. */ Q_PROPERTY(RadioddityToneSettingsExtension *tone READ tone) /** The boot settings. */ Q_PROPERTY(RadioddityBootSettingsExtension *boot READ boot) public: /** Possible monitor types. */ enum class MonitorType { Open = 0, ///< Monitoring by opening the squelch. Silent = 1 ///< Silent monitoring. }; Q_ENUM(MonitorType) /** Possible scan modes. */ enum class ScanMode { Time = 0, Carrier = 1, Search = 2 }; Q_ENUM(ScanMode) /** Possible UI languages. */ enum class Language { Chinese, English }; Q_ENUM(Language) public: /** Default constructor. */ Q_INVOKABLE explicit RadiodditySettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the monitor type. */ MonitorType monitorType() const; /** Sets the monitor type. */ void setMonitorType(MonitorType type); /** Returns the lone-worker response time in minutes. */ Interval loneWorkerResponseTime() const; /** Sets the lone-worker response time in minutes. */ void setLoneWorkerResponseTime(Interval min); /** Returns the lone-worker reminder period in seconds. */ Interval loneWorkerReminderPeriod() const; /** Sets the lone-worker reminder period in seconds. */ void setLoneWorkerReminderPeriod(Interval sec); /** Returns the group call hang time in ms. */ Interval groupCallHangTime() const; /** Sets the group call hang time in ms. */ void setGroupCallHangTime(Interval ms); /** Returns the private call hang time in ms. */ Interval privateCallHangTime() const; /** Sets the private call hang time in ms. */ void setPrivateCallHangTime(Interval ms); /** Returns @c true if the down-channel mode is VFO. */ bool downChannelModeVFO() const; /** Enables/disables down-channel mode is VFO. */ void enableDownChannelModeVFO(bool enable); /** Returns @c true if the up-channel mode is VFO. */ bool upChannelModeVFO() const; /** Enables/disables up-channel mode is VFO. */ void enableUpChannelModeVFO(bool enable); /** Returns @c true if the power save mode is enabled. */ bool powerSaveMode() const; /** Enables the power save mode. */ void enablePowerSaveMode(bool enable); /** Returns @c true if the wake-up preamble is sent. */ bool wakeupPreamble() const; /** Enables transmission of wakeup preamble. */ void enableWakeupPreamble(bool enable); /** Returns the preamble duration in ms. */ Interval preambleDuration() const; /** Sets the preamble duration in ms. */ void setPreambleDuration(Interval ms); /** Returns the delay, before an idle radio enters power save mode. */ Interval powerSaveDelay() const; /** Sets the delay before an idle radio enters power save mode. */ void setPowerSaveDelay(Interval interv); /** Returns @c true if all LEDs are disabled. */ bool allLEDsDisabled() const; /** Disables/enables all LEDs. */ void disableAllLEDs(bool disable); /** Returns true if quick-key override is inhibited. */ bool quickKeyOverrideInhibited() const; /** Inhibits quick-key override. */ void inhibitQuickKeyOverride(bool inhibit); /** Returns @c true if the radio transmits on the active channel on double monitor. */ bool txOnActiveChannel() const; /** Enables/disables transmission on active channel on double monitor. */ void enableTXOnActiveChannel(bool enable); /** Returns the scan mode. */ ScanMode scanMode() const; /** Sets the scan mode. */ void setScanMode(ScanMode mode); /** Returns the repeater end delay in seconds. */ Interval repeaterEndDelay() const; /** Sets the repeater end delay in seconds. */ void setRepeaterEndDelay(Interval delay); /** Returns the repeater STE in seconds. */ Interval repeaterSTE() const; /** Sets the repeater STE in seconds. */ void setRepeaterSTE(Interval ste); /** Returns @c true if the TX iterrupt is enabled. */ bool txInterrupt() const; /** Enables TX iterrupt. */ void enableTXInterrupt(bool enable); /** Returns the UI language. */ Language language() const; /** Sets the language. */ void setLanguage(Language lang); /** Returns a weak reference to the button settings. */ RadioddityButtonSettingsExtension *buttons() const; /** Returns a weak reference to the tone settings. */ RadioddityToneSettingsExtension *tone() const; /** Returns a weak reference to the boot settings. */ RadioddityBootSettingsExtension *boot() const; protected: /** Holds the monitor type. */ MonitorType _monitorType; /** Holds the lone-worker response time in minutes. */ Interval _loneWorkerResponseTime; /** Holds the lone-worker reminder period in seconds. */ Interval _loneWorkerReminderPeriod; /** The group-call hang-time in ms. */ Interval _groupCallHangTime; /** The private-call hang-time in ms. */ Interval _privateCallHangTime; /** If @c true down-channel mode is VFO. */ bool _downChannelModeVFO; /** If @c true the up-channel mode is VFO. */ bool _upChannelModeVFO; /** If @c true, the power save mode is enabled. */ bool _powerSaveMode; /** If @c true, the wake-up preamble is sent. */ bool _wakeupPreamble; /** Holds the preamble duration in ms. */ Interval _preambleDuration; /** Delay before an idle radio enters the power save mode. */ Interval _powerSaveDelay; /** If @c true, all LEDs are disabled. */ bool _disableAllLEDs; /** If @c true, the quick-key override is inhibited. */ bool _quickKeyOverrideInhibited; /** If @c true, the radio will transmit on the active channel when double-wait is enabled. */ bool _txOnActiveChannel; /** Holds the scan mode. */ ScanMode _scanMode; /** Holds the repeater end delay in seconds. */ Interval _repeaterEndDelay; /** Holds the repeater STE in seconds. */ Interval _repeaterSTE; /** If @c true, TX interrupt is enabled. */ bool _txInterrupt; /** UI language. */ Language _language; /** Button settings. */ RadioddityButtonSettingsExtension *_buttonSettings; /** Tone settings. */ RadioddityToneSettingsExtension *_toneSettings; /** Boot settings. */ RadioddityBootSettingsExtension *_bootSettings; }; #endif // RADIODDITYEXTENSIONS_HH qdmr-0.12.3/lib/radioddity_interface.cc000066400000000000000000000161561501654372000200050ustar00rootroot00000000000000#include "radioddity_interface.hh" #include #include #include #include #include "logger.hh" #define USB_VID 0x15a2 #define USB_PID 0x0073 #define MAX_RETRY 10 static const unsigned char CMD_PRG[] = "\2PROGRA"; static const unsigned char CMD_PRG2[] = "M\2"; static const unsigned char CMD_ACK[] = "A"; static const unsigned char CMD_READ[] = "Raan"; static const unsigned char CMD_WRITE[] = "Waan..."; static const unsigned char CMD_ENDR[] = "ENDR"; static const unsigned char CMD_ENDW[] = "ENDW"; static const unsigned char CMD_CWB0[] = "CWB\4\0\0\0\0"; static const unsigned char CMD_CWB1[] = "CWB\4\0\1\0\0"; static const unsigned char CMD_CWB3[] = "CWB\4\0\3\0\0"; static const unsigned char CMD_CWB4[] = "CWB\4\0\4\0\0"; RadioddityInterface::RadioddityInterface(const USBDeviceDescriptor &descr, const ErrorStack &err, QObject *parent) : HIDevice(descr, err, parent), _current_bank(MEMBANK_NONE), _identifier() { if (isOpen()) identifier(); } RadioddityInterface::~RadioddityInterface() { if (isOpen()) close(); } USBDeviceInfo RadioddityInterface::interfaceInfo() { return USBDeviceInfo(USBDeviceInfo::Class::HID, USB_VID, USB_PID); } QList RadioddityInterface::detect(bool saveOnly) { Q_UNUSED(saveOnly) return HIDevice::detect(USB_VID, USB_PID); } bool RadioddityInterface::isOpen() const { return HIDevice::isOpen(); } void RadioddityInterface::close() { logDebug() << "Close HID connection."; _identifier = RadioInfo(); HIDevice::close(); } RadioInfo RadioddityInterface::identifier(const ErrorStack &err) { static unsigned char reply[38]; unsigned char ack; if (_identifier.isValid()) return _identifier; logDebug() << "Radioddity HID interface: Enter program mode."; if (! hid_send_recv(CMD_PRG, 7, &ack, 1, err)) { errMsg(err) << "Cannot identify radio."; return RadioInfo(); } if (ack != CMD_ACK[0]) { errMsg(err) << "Cannot identify radio: Wrong PRD acknowledge " << (int)ack << ", expected "<< int(CMD_ACK[0]) << "."; return RadioInfo(); } if (! hid_send_recv(CMD_PRG2, 2, reply, 16, err)) { errMsg(err) << "Cannot identify radio."; return RadioInfo(); } if (! hid_send_recv(CMD_ACK, 1, &ack, 1, err)) { errMsg(err) << "Cannot identify radio."; return RadioInfo(); } if (ack != CMD_ACK[0]) { errMsg(err) << "Cannot identify radio: Wrong PRG2 acknowledge " << (int) ack << ", expected " << (int)CMD_ACK[0] << "."; return RadioInfo(); } // Reply: // 42 46 2d 35 52 ff ff ff 56 32 31 30 00 04 80 04 // B F - 5 R V 2 1 0 // Terminate the string. char *p = (char *)memchr(reply, 0xff, sizeof(reply)); if (p) *p = 0; if (0 == strcmp((char*)reply, "BF-5R")) { _identifier = RadioInfo::byID(RadioInfo::RD5R); } else if (0 == strcmp((char*)reply, "MD-760P")) { _identifier = RadioInfo::byID(RadioInfo::GD77); } else { errMsg(err) << "Unknown Radioddity device '" << (char*)reply << "'."; return RadioInfo(); } logDebug() << "Got device '" << _identifier.name() << "'."; return _identifier; } bool RadioddityInterface::read_start(uint32_t bank, uint32_t addr, const ErrorStack &err) { Q_UNUSED(addr) if (! selectMemoryBank(MemoryBank(bank), err)) { errMsg(err) << "Cannot select memory bank " << bank << "."; return false; } return true; } bool RadioddityInterface::read(uint32_t bank, uint32_t addr, unsigned char *data, int nbytes, const ErrorStack &err) { unsigned char cmd[4], reply[32+4]; int n; if (! selectMemoryBank(MemoryBank(bank), err)) { errMsg(err) << "Cannot select memory bank " << bank << "."; return false; } // send data for (n=0; n> 8; cmd[2] = addr + n; cmd[3] = 32; if (! hid_send_recv(cmd, 4, reply, sizeof(reply), err)) return false; else memcpy(data + n, reply + 4, 32); } return true; } bool RadioddityInterface::read_finish(const ErrorStack &err) { unsigned char ack; if (! hid_send_recv(CMD_ENDR, 4, &ack, 1, err)) { errMsg(err) << "Cannot finish read()."; return false; } if (ack != CMD_ACK[0]) { errMsg(err) << "Cannot finish read(): Wrong acknowledge " << (int)ack << ", expected " << (int)CMD_ACK[0] << "."; return false; } logDebug() << "Left program mode."; _identifier = RadioInfo(); return true; } bool RadioddityInterface::write_start(uint32_t bank, uint32_t addr, const ErrorStack &err) { Q_UNUSED(addr) if (! selectMemoryBank(MemoryBank(bank), err)) { errMsg(err) << "Cannot select memory bank " << bank << "."; return false; } return true; } bool RadioddityInterface::write(uint32_t bank, uint32_t addr, unsigned char *data, int nbytes, const ErrorStack &err) { unsigned char ack, cmd[4+32]; if (! selectMemoryBank(MemoryBank(bank), err)) { errMsg(err) << "Cannot select memory bank " << bank << "."; return false; } // send data unsigned int count=0; for (int n=0; n> 8; cmd[2] = addr + n; cmd[3] = 32; memcpy(cmd + 4, data + n, 32); if (! hid_send_recv(cmd, 4+32, &ack, 1, err)) return false; else if (ack != CMD_ACK[0]) { errMsg(err) << "Cannot write block: Wrong acknowledge " << (int)ack << ", expected " << (int)CMD_ACK[0] << "."; n-=32; if ((++count) > MAX_RETRY) { errMsg(err) << "Maximum retry count reached. Abort."; return false; } } else { count = 0; } } return true; } bool RadioddityInterface::write_finish(const ErrorStack &err) { unsigned char ack; if (! hid_send_recv(CMD_ENDW, 4, &ack, 1, err)) { errMsg(err) << "Cannot finish write()."; return false; } if (ack != CMD_ACK[0]) { errMsg(err) << "Cannot finish write(): Wrong acknowledge " << (int)ack << ", expected " << (int)CMD_ACK[0] << "."; return false; } logDebug() << "Left program mode."; _identifier = RadioInfo(); return true; } bool RadioddityInterface::selectMemoryBank(MemoryBank bank, const ErrorStack &err) { unsigned char ack; const uint8_t *cmd = nullptr; if (_current_bank == bank) return true; // Select command by memory bank switch (bank) { case MEMBANK_CODEPLUG_LOWER : cmd = CMD_CWB0; break; case MEMBANK_CODEPLUG_UPPER : cmd = CMD_CWB1; break; case MEMBANK_CALLSIGN_LOWER : cmd = CMD_CWB3; break; case MEMBANK_CALLSIGN_UPPER : cmd = CMD_CWB4; break; default: errMsg(err) << "Cannot set memory bank: Unknown bank " << bank << "."; return false; } logDebug() << "Selecting memory bank " << bank << "..."; // select memory bank if (! hid_send_recv(cmd, 8, &ack, 1, err)) { errMsg(err) << "Cannot send memory bank select command."; return false; } if (ack != CMD_ACK[0]) { errMsg(err) << "Cannot select memory bank: Wrong acknowledge " << (int)ack << ", expected " << (int)CMD_ACK[0] << "."; return false; } logDebug() << "Memory bank " << bank << " selected."; _current_bank = bank; return true; } qdmr-0.12.3/lib/radioddity_interface.hh000066400000000000000000000061701501654372000200120ustar00rootroot00000000000000#ifndef RADIODDITY_INTERFACE_HH #define RADIODDITY_INTERFACE_HH #include #include #include "radiointerface.hh" #ifdef Q_OS_MACOS #include "hid_macos.hh" #else #include "hid_libusb.hh" #endif /** Implements a radio interface for radios using the HID USB schema (i.e. Radioddity devices). * * @ingroup radioddity */ class RadioddityInterface: public HIDevice, public RadioInterface { Q_OBJECT public: /** Possible memory banks to select. */ enum MemoryBank { MEMBANK_NONE = -1, ///< No bank selected. MEMBANK_CODEPLUG_LOWER = 0, ///< Lower memory bank (EEPROM). MEMBANK_CODEPLUG_UPPER = 1, ///< Upper memory bank (FLASH). MEMBANK_CALLSIGN_LOWER = 3, ///< Callsign DB memory lower bank (also FLASH). MEMBANK_CALLSIGN_UPPER = 4 ///< Callsign DB memory upper bank (also FLASH). }; public: /** Connects to the radio via the given descriptor. */ explicit RadioddityInterface(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent = nullptr); /** Destructor. */ virtual ~RadioddityInterface(); /** Returns @c true if the connection was established. */ bool isOpen() const; void close(); /** Returns radio identifier string. */ RadioInfo identifier(const ErrorStack &err=ErrorStack()); bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); /** Reads a block of data from the device at the given block number. * @param bank The memory bank to read from. * @param addr The address to read from within the memory bank. * @param data Pointer to memory where the read data is stored. * @param nbytes The number of bytes to read. * @param err The error stack, messages are put onto. * @returns @c true on success. */ bool read(uint32_t bank, uint32_t addr, unsigned char *data, int nbytes, const ErrorStack &err=ErrorStack()); bool read_finish(const ErrorStack &err=ErrorStack()); bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); /** Writes a block of data to the device at the given block number. * @param bank The memory bank to read from. * @param addr The address to read from within the memory bank. * @param data Pointer to memory where the read data is stored. * @param nbytes The number of bytes to read. * @param err The error stack, messages are put onto. * @returns @c true on success. */ bool write(uint32_t bank, uint32_t addr, unsigned char *data, int nbytes, const ErrorStack &err=ErrorStack()); bool write_finish(const ErrorStack &err=ErrorStack()); public: /** Returns some information about the interface. */ static USBDeviceInfo interfaceInfo(); /** Tries to find all interfaces connected AnyTone radios. */ static QList detect(bool saveOnly=true); protected: /** Internal used function to select a memory bank. */ bool selectMemoryBank(MemoryBank bank, const ErrorStack &err=ErrorStack()); private: /** The currently selected memory bank. */ MemoryBank _current_bank; /** Identifier received when entering the prog mode. */ RadioInfo _identifier; }; #endif // RADIODDITY_INTERFACE_HH qdmr-0.12.3/lib/radioddity_radio.cc000066400000000000000000000140561501654372000171400ustar00rootroot00000000000000#include "radioddity_radio.hh" #include "config.hh" #include "logger.hh" #include "utils.hh" #define BSIZE 32 RadioddityRadio::RadioddityRadio(RadioddityInterface *device, QObject *parent) : Radio(parent), _dev(device), _codeplugFlags(), _config(nullptr) { // pass... } RadioddityRadio::~RadioddityRadio() { if (_dev && _dev->isOpen()) { logDebug() << "Reboot and close connection to radio."; _dev->reboot(); _dev->close(); } if (_dev) { _dev->deleteLater(); _dev = nullptr; } } bool RadioddityRadio::startDownload(bool blocking, const ErrorStack &err) { if (StatusIdle != _task) return false; _task = StatusDownload; _errorStack = err; if (blocking) { run(); return (StatusIdle == _task); } start(); return true; } bool RadioddityRadio::startUpload(Config *config, bool blocking, const Codeplug::Flags &flags, const ErrorStack &err) { if (StatusIdle != _task) return false; if (_config) delete _config; if (! (_config = config)) return false; _config->setParent(this); _task = StatusUpload; _codeplugFlags = flags; if (blocking) { this->run(); return (StatusIdle == _task); } _errorStack = err; this->start(); return true; } bool RadioddityRadio::startUploadCallsignDB(UserDatabase *db, bool blocking, const CallsignDB::Selection &selection, const ErrorStack &err) { Q_UNUSED(db); Q_UNUSED(blocking); Q_UNUSED(selection); errMsg(err) << "Radio does not support a callsign DB."; return false; } bool RadioddityRadio::startUploadSatelliteConfig(SatelliteDatabase *db, bool blocking, const ErrorStack &err) { Q_UNUSED(db); Q_UNUSED(blocking); errMsg(err) << "Satellite config upload is not implemented yet."; return false; } void RadioddityRadio::run() { if (StatusDownload == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { emit downloadError(this); return; } if (! download()) { _dev->read_finish(); _dev->reboot(); _dev->close(); _task = StatusError; emit downloadError(this); return; } _task = StatusIdle; _dev->reboot(); _dev->close(); emit downloadFinished(this, &codeplug()); _config = nullptr; } else if (StatusUpload == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { emit uploadError(this); return; } if (! upload()) { _dev->write_finish(); _dev->reboot(); _dev->close(); _task = StatusError; emit uploadError(this); return; } _dev->write_finish(); _dev->reboot(); _dev->close(); _task = StatusIdle; emit uploadComplete(this); } else if (StatusUploadCallsigns == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { emit uploadError(this); return; } if(! uploadCallsigns()) { _dev->reboot(); _dev->close(); _task = StatusError; emit uploadError(this); return; } _task = StatusIdle; _dev->reboot(); _dev->close(); emit uploadComplete(this); } } bool RadioddityRadio::download() { emit downloadStarted(); unsigned btot = 0; for (int n=0; n addr) ? RadioddityInterface::MEMBANK_CODEPLUG_LOWER : RadioddityInterface::MEMBANK_CODEPLUG_UPPER ); // read if (! _dev->read(bank, (b0+i)*BSIZE, codeplug().data((b0+i)*BSIZE), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot download codeplug."; return false; } emit downloadProgress(float(bcount*100)/btot); } } _dev->read_finish(_errorStack); return true; } bool RadioddityRadio::upload() { emit uploadStarted(); unsigned btot = 0; for (int n=0; n addr) ? RadioddityInterface::MEMBANK_CODEPLUG_LOWER : RadioddityInterface::MEMBANK_CODEPLUG_UPPER ); // read if (! _dev->read(bank, addr, codeplug().data(addr), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot upload codeplug."; return false; } emit uploadProgress(float(bcount*50)/btot); } } } // Encode config into codeplug if (! codeplug().encode(_config, _codeplugFlags, _errorStack)) { errMsg(_errorStack) << "Codeplug upload failed."; return false; } // then, upload modified codeplug bcount = 0; for (int n=0; n addr) ? RadioddityInterface::MEMBANK_CODEPLUG_LOWER : RadioddityInterface::MEMBANK_CODEPLUG_UPPER ); // write block if (! _dev->write(bank, addr, codeplug().data(addr), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot upload codeplug."; return false; } emit uploadProgress(50+float(bcount*50)/btot); } } return true; } bool RadioddityRadio::uploadCallsigns() { return false; } qdmr-0.12.3/lib/radioddity_radio.hh000066400000000000000000000036311501654372000171470ustar00rootroot00000000000000/** @defgroup radioddity Radioddity radios * Abstract classes for Radioddity radios. * * @ingroup dsc */ #ifndef RADIODDITY_RADIO_HH #define RADIODDITY_RADIO_HH #include "radio.hh" #include "radioddity_interface.hh" /** Base class for all Radioddity radios. * * @ingroup radioddity */ class RadioddityRadio: public Radio { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit RadioddityRadio(RadioddityInterface *device=nullptr, QObject *parent=nullptr); virtual ~RadioddityRadio(); public slots: /** Starts the download of the codeplug and derives the generic configuration from it. */ bool startDownload(bool blocking=false, const ErrorStack &err=ErrorStack()); /** Derives the device-specific codeplug from the generic configuration and uploads that * codeplug to the radio. */ bool startUpload(Config *config, bool blocking=false, const Codeplug::Flags &flags = Codeplug::Flags(), const ErrorStack &err=ErrorStack()); /** Encodes the given user-database and uploades it to the device. */ bool startUploadCallsignDB(UserDatabase *db, bool blocking=false, const CallsignDB::Selection &selection=CallsignDB::Selection(), const ErrorStack &err=ErrorStack()); bool startUploadSatelliteConfig(SatelliteDatabase *db, bool blocking, const ErrorStack &err); protected: /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */ void run(); private: virtual bool download(); virtual bool upload(); virtual bool uploadCallsigns(); protected: /** The interface to the radio. */ RadioddityInterface *_dev; /** Holds the flags to control assembly and upload of code-plugs. */ Codeplug::Flags _codeplugFlags; /** The generic configuration. */ Config *_config; /** A weak reference to the user-database. */ UserDatabase *_userDB; }; #endif // RADIODDITY_RADIO_HH qdmr-0.12.3/lib/radioid.cc000066400000000000000000000132761501654372000152440ustar00rootroot00000000000000#include "radioid.hh" #include "logger.hh" #include "utils.hh" #include "config.hh" #include "radiosettings.hh" /* ********************************************************************************************* * * Implementation of RadioID * ********************************************************************************************* */ RadioID::RadioID(QObject *parent) : ConfigObject(parent) { // pass... } RadioID::RadioID(const QString &name, QObject *parent) : ConfigObject(name, parent) { // pass... } /* ********************************************************************************************* * * Implementation of DMRRadioID * ********************************************************************************************* */ DMRRadioID::DMRRadioID(QObject *parent) : RadioID(parent), _number(0) { // pass... } DMRRadioID::DMRRadioID(const QString &name, uint32_t id, QObject *parent) : RadioID(name, parent), _number(id) { // pass... } ConfigItem * DMRRadioID::clone() const { DMRRadioID *id = new DMRRadioID(); if (! id->copy(*this)) { id->deleteLater(); return nullptr; } return id; } uint32_t DMRRadioID::number() const { return _number; } void DMRRadioID::setNumber(uint32_t id) { if (id == _number) return; _number = id; emit modified(this); } YAML::Node DMRRadioID::serialize(const Context &context, const ErrorStack &err) { YAML::Node node = RadioID::serialize(context, err); if (node.IsNull()) return node; YAML::Node type; node.SetStyle(YAML::EmitterStyle::Flow); type["dmr"] = node; return type; } bool DMRRadioID::parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { if (! node) return false; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot parse radio id: Expected object with one child."; return false; } return ConfigObject::parse(node.begin()->second, ctx, err); } bool DMRRadioID::link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err) { if (! node) return false; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot link radio id: Expected object with one child."; return false; } return ConfigObject::link(node.begin()->second, ctx, err); } /* ********************************************************************************************* * * Implementation of DefaultRadioID * ********************************************************************************************* */ DefaultRadioID *DefaultRadioID::_instance = nullptr; DefaultRadioID::DefaultRadioID(QObject *parent) : DMRRadioID(tr("[Default]"),0,parent) { // pass... } DefaultRadioID * DefaultRadioID::get() { if (nullptr == _instance) _instance = new DefaultRadioID(); return _instance; } /* ********************************************************************************************* * * Implementation of DTMFRadioID * ********************************************************************************************* */ DTMFRadioID::DTMFRadioID(QObject *parent) : RadioID(parent) { // pass... } DTMFRadioID::DTMFRadioID(const QString &name, const QString &number, QObject *parent) : RadioID(name, parent), _number() { setNumber(number.simplified()); } ConfigItem * DTMFRadioID::clone() const { DTMFRadioID *newId = new DTMFRadioID(); if (! newId->copy(*this)) { newId->deleteLater(); return nullptr; } return newId; } const QString & DTMFRadioID::number() const { return _number; } void DTMFRadioID::setNumber(const QString &number) { if (! validDTMFNumber(number)) return; _number = number.simplified(); emit modified(this); return; } /* ********************************************************************************************* * * Implementation of RadioIDList * ********************************************************************************************* */ RadioIDList::RadioIDList(QObject *parent) : ConfigObjectList(DMRRadioID::staticMetaObject, parent) { // pass... } void RadioIDList::clear() { ConfigObjectList::clear(); } DMRRadioID * RadioIDList::getId(int idx) const { if (ConfigItem *obj = get(idx)) return obj->as(); return nullptr; } DMRRadioID * RadioIDList::find(uint32_t id) const { for (int i=0; inumber()) return getId(i); } return nullptr; } int RadioIDList::add(ConfigObject *obj, int row, bool unique) { if ((nullptr == obj) || (! obj->is())) return -1; int idx = ConfigObjectList::add(obj, row, unique); if (parent() && obj->is() && qobject_cast(parent())->settings()->defaultIdRef()->isNull()) qobject_cast(parent())->settings()->setDefaultId(obj->as()); return idx; } int RadioIDList::addId(const QString &name, uint32_t id) { return add(new DMRRadioID(name, id, this)); } bool RadioIDList::delId(uint32_t id) { return del(find(id)); } ConfigItem * RadioIDList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) if (! node) return nullptr; if ((! node.IsMap()) || (1 != node.size())) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create radio id: Expected object with one child."; return nullptr; } QString type = QString::fromStdString(node.begin()->first.as()); if ("dmr" == type) { return new DMRRadioID(); } errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create radio id: Unknown type '" << type << "'."; return nullptr; } qdmr-0.12.3/lib/radioid.hh000066400000000000000000000077211501654372000152540ustar00rootroot00000000000000#ifndef RADIOID_HH #define RADIOID_HH #include "configobject.hh" class DMRRadioIDReference; /** Abstract base class for all radio IDs. * * That is, DMR radio IDs as well as M17, DTMF, ZVEI, 5-tone etc PTT-IDs. * * @ingroup conf */ class RadioID: public ConfigObject { Q_OBJECT protected: /** Hidden default constructor. * Use one of the derived classes to instantiate radio IDs. */ explicit RadioID(QObject *parent=nullptr); /** Hidden constructor with name. */ RadioID(const QString &name, QObject *parent=nullptr); }; /** Represents a DMR radio ID within the abstract config. * * This class is used to store the DMR ID(s) of the radio. * * @ingroup conf */ class DMRRadioID : public RadioID { Q_OBJECT Q_CLASSINFO("IdPrefix", "id") /** The number of the radio ID. */ Q_PROPERTY(unsigned number READ number WRITE setNumber) public: /** Default constructor. */ Q_INVOKABLE explicit DMRRadioID(QObject *parent=nullptr); /** Constructor. * @param name Specifies the name of the ID. * @param number Specifies the DMR ID. * @param parent Specifies the parent QObject owning this object. */ DMRRadioID(const QString &name, uint32_t number, QObject *parent = nullptr); ConfigItem *clone() const; /** Returns the DMR ID. */ uint32_t number() const; /** Sets the DMR ID. */ void setNumber(uint32_t number); YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack()); bool parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); bool link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); protected: /** Holds the DMR ID. */ uint32_t _number; }; /** A singleton radio ID representing the default DMR radio ID within the abstract config. * @ingroup conf */ class DefaultRadioID: public DMRRadioID { Q_OBJECT protected: /** Constructor. */ explicit DefaultRadioID(QObject *parent=nullptr); public: /** Factory method returning the singleton instance. */ static DefaultRadioID *get(); private: /** The singleton instance. */ static DefaultRadioID *_instance; }; /** Represents a DTMF radio ID as used for PTT-ID on analog channels. * * This class just holds the name and DTMF number of the ID. * @ingroup conf */ class DTMFRadioID: public RadioID { Q_OBJECT Q_CLASSINFO("IdPrefix", "dtmf") /** The DTMF number of the radio ID. */ Q_PROPERTY(QString number READ number WRITE setNumber) public: /** Default constructor. */ Q_INVOKABLE explicit DTMFRadioID(QObject *parent=nullptr); /** Constructor from name and number. * @param name Specifies the name of the DTMF radio ID. * @param number Specifies the DTMF number of the radio ID. * @param parent Specifies the QObject parent, the object that owns this one. */ explicit DTMFRadioID(const QString &name, const QString &number, QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the DTMF number of the radio ID. */ const QString &number() const; /** Sets the DTMF number of the radio ID. */ void setNumber(const QString &number); protected: /** Holds the DTMF number of the radio ID. */ QString _number; }; /** Represents the list of configured DMR IDs (radio IDs) within the abstract config. * @ingroup conf */ class RadioIDList: public ConfigObjectList { Q_OBJECT public: /** Constructor. */ explicit RadioIDList(QObject *parent=nullptr); void clear(); /** Returns the radio ID at the given index. */ DMRRadioID *getId(int idx) const; /** Searches the DMR ID object associated with the given DMR ID. */ DMRRadioID *find(uint32_t id) const; int add(ConfigObject *obj, int row=-1, bool unique=true); /** Adds the given DMR ID. */ virtual int addId(const QString &name, uint32_t id); /** Deletes and removes the given DMR ID. */ virtual bool delId(uint32_t id); public: ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); }; #endif // RADIOID_HH qdmr-0.12.3/lib/radioinfo.cc000066400000000000000000000114751501654372000156020ustar00rootroot00000000000000#include "radioinfo.hh" #include "opengd77.hh" #include "openuv380.hh" #include "openrtx.hh" #include "gd73.hh" #include "gd77.hh" #include "rd5r.hh" #include "md390.hh" #include "uv390.hh" #include "md2017.hh" #include "d868uv.hh" #include "d878uv.hh" #include "d878uv2.hh" #include "d578uv.hh" #include "dmr6x2uv.hh" #include "dm1701.hh" #include "dr1801uv.hh" QHash RadioInfo::_radiosByName = QHash{ {"opengd77", RadioInfo::OpenGD77}, {"openuv380", RadioInfo::OpenUV380}, {"openrtx", RadioInfo::OpenRTX}, {"rd5r", RadioInfo::RD5R}, {"gd73", RadioInfo::GD73}, {"gd77", RadioInfo::GD77}, {"md380", RadioInfo::MD380}, {"md390", RadioInfo::MD390}, {"rt8", RadioInfo::RT8}, {"uv380", RadioInfo::UV380}, {"uv390", RadioInfo::UV390}, {"rt3s", RadioInfo::RT3S}, {"md2017", RadioInfo::MD2017}, {"rt82", RadioInfo::RT82}, {"dm1701", RadioInfo::DM1701}, {"rt84", RadioInfo::RT84}, {"d868uv", RadioInfo::D868UV}, {"d868uve", RadioInfo::D868UVE}, {"dmr6x2uv", RadioInfo::DMR6X2UV}, {"d878uv", RadioInfo::D878UV}, {"d878uv2", RadioInfo::D878UVII}, {"d578uv", RadioInfo::D578UV}, {"dr1801uv", RadioInfo::DR1801UV} }; QHash RadioInfo::_radiosById = QHash{ {RadioInfo::OpenGD77, OpenGD77::defaultRadioInfo()}, {RadioInfo::OpenUV380, OpenUV380::defaultRadioInfo()}, {RadioInfo::OpenRTX, OpenRTX::defaultRadioInfo()}, {RadioInfo::RD5R, RD5R::defaultRadioInfo()}, {RadioInfo::GD73, GD73::defaultRadioInfo()}, {RadioInfo::GD77, GD77::defaultRadioInfo()}, {RadioInfo::MD390, MD390::defaultRadioInfo()}, {RadioInfo::UV390, UV390::defaultRadioInfo()}, {RadioInfo::MD2017, MD2017::defaultRadioInfo()}, {RadioInfo::DM1701, DM1701::defaultRadioInfo()}, {RadioInfo::D868UVE, D868UV::defaultRadioInfo()}, {RadioInfo::D878UV, D878UV::defaultRadioInfo()}, {RadioInfo::D878UVII, D878UV2::defaultRadioInfo()}, {RadioInfo::D578UV, D578UV::defaultRadioInfo()}, {RadioInfo::DMR6X2UV, DMR6X2UV::defaultRadioInfo()}, {RadioInfo::DR1801UV, DR1801UV::defaultRadioInfo()} }; /* ********************************************************************************************* * * Implementation of RadioInfo * ********************************************************************************************* */ RadioInfo::RadioInfo(Radio radio, const QString &name, const QString manufacturer, const USBDeviceInfo &interface, const QList &alias) : _radio(radio), _key(name.toLower()), _name(name), _manufacturer(manufacturer), _alias(alias), _interface(interface) { // pass... } RadioInfo::RadioInfo(Radio radio, const QString &key, const QString &name, const QString manufacturer, const USBDeviceInfo &interface, const QList &alias) : _radio(radio), _key(key), _name(name), _manufacturer(manufacturer), _alias(alias), _interface(interface) { // pass... } RadioInfo::RadioInfo() : _key("") { // pass... } bool RadioInfo::isValid() const { return ! _key.isEmpty(); } const QString & RadioInfo::key() const { return _key; } const QString & RadioInfo::name() const { return _name; } const QString & RadioInfo::manufacturer() const { return _manufacturer; } const USBDeviceInfo & RadioInfo::interface() const { return _interface; } bool RadioInfo::hasAlias() const { return 0 != _alias.count(); } const QList & RadioInfo::alias() const { return _alias; } RadioInfo::Radio RadioInfo::id() const { return _radio; } bool RadioInfo::hasRadioKey(const QString &key) { return _radiosByName.contains(key); } RadioInfo RadioInfo::byKey(const QString &key) { if (! hasRadioKey(key)) return RadioInfo(); return byID(_radiosByName[key]); } RadioInfo RadioInfo::byID(Radio radio) { return _radiosById[radio]; } QList RadioInfo::allRadios(bool flat) { QList radios; QHash::const_iterator it = _radiosById.constBegin(); for (; it!=_radiosById.constEnd(); it++) { radios.push_back(*it); if (flat) radios.append(it->_alias); } std::sort(radios.begin(), radios.end(), [](const RadioInfo &a, const RadioInfo &b) { return a.id() RadioInfo::allRadios(const USBDeviceInfo &interface, bool flat) { QList radios; QHash::const_iterator it = _radiosById.constBegin(); for (; it!=_radiosById.constEnd(); it++) { if (it->interface() != interface) continue; radios.push_back(*it); if (flat) radios.append(it->_alias); } std::sort(radios.begin(), radios.end(), [](const RadioInfo &a, const RadioInfo &b) { return a.id() #include #include #include "usbdevice.hh" /** Provides some information about a radio model. * * This class is used to unify radio enumeration and detection. * * @since 0.9.0 */ class RadioInfo { public: /** Known radios. */ enum Radio { // Open source firmware OpenGD77, OpenUV380, OpenRTX, // Radioddity devices RD5R, GD73, GD77, // TyT devices MD390, MD380 = MD390, RT8 = MD390, UV390, UV380 = UV390, RT3S = UV390, MD2017, RT82 = MD2017, // Anytone devices D868UVE, D868UV = D868UVE, // Actually a different device. Implement! DMR6X2UV, D878UV, D878UVII, D578UV, // Baofeng/BTECH DM1701, RT84 = DM1701, DR1801UV }; public: /** Use static methods the access radio info or call @c Radio::defaultRadioInfo. */ RadioInfo(Radio radio, const QString &name, const QString manufacturer, const USBDeviceInfo &interface, const QList &alias=QList()); /** Use static methods the access radio info or call @c Radio::defaultRadioInfo. */ RadioInfo(Radio radio, const QString &key, const QString &name, const QString manufacturer, const USBDeviceInfo &interface, const QList &alias=QList()); /** Empty constructor. */ RadioInfo(); /** Returns @c true if the info is valid. */ bool isValid() const; /** Returns the radio key (used to identify radios in the command line). */ const QString &key() const; /** Returns the radio name. */ const QString &name() const; /** Returns the manufacturer name. */ const QString &manufacturer() const; /** Returns some information about the interface to the radio. */ const USBDeviceInfo &interface() const; /** Returns @c true if the radio has aliases. * That is other radios that are identical. */ bool hasAlias() const; /** Returns the list of alias radios. */ const QList &alias() const; /** Returns the unique device ID (alias radios share ID). */ Radio id() const; public: /** Returns @c true if the given key is known. */ static bool hasRadioKey(const QString &key); /** Returns the radio info by key. */ static RadioInfo byKey(const QString &key); /** Returns the radio info by id. */ static RadioInfo byID(Radio radio); /** Returns the list of all known radios. */ static QList allRadios(bool flat=true); /** Returns a list of all known radios for the specified interface. */ static QList allRadios(const USBDeviceInfo &interface, bool flat=true); protected: /** Holds the radio id. */ Radio _radio; /** Holds the key of the radio. */ QString _key; /** Holds the name of the radio. */ QString _name; /** Holds the name of the manufacturer. */ QString _manufacturer; /** Holds possible identical radios from other manufacturers. */ QList _alias; /** Holds some information about the interface to the radio. */ USBDeviceInfo _interface; protected: /** Key->ID map. */ static QHash _radiosByName; /** ID->Info map. */ static QHash _radiosById; }; #endif // RADIOINFO_HH qdmr-0.12.3/lib/radiointerface.cc000066400000000000000000000012431501654372000165770ustar00rootroot00000000000000#include "radiointerface.hh" #include "anytone_interface.hh" #include "opengd77_interface.hh" #include "radioddity_interface.hh" #include "tyt_interface.hh" /* ********************************************************************************************* * * Implementation of RadioInterface * ********************************************************************************************* */ RadioInterface::RadioInterface() { // pass... } RadioInterface::~RadioInterface() { // pass... } bool RadioInterface::write_finish(const ErrorStack &err) { Q_UNUSED(err) return true; } bool RadioInterface::reboot(const ErrorStack &err) { Q_UNUSED(err) return true; } qdmr-0.12.3/lib/radiointerface.hh000066400000000000000000000112131501654372000166070ustar00rootroot00000000000000/** @defgroup rif Radio interfaces * Depending on the manufacturer or model, different radios have different computer-radio * interfaces. This module collects all classes that provide these interfaces. */ #ifndef RADIOINFERFACE_HH #define RADIOINFERFACE_HH #include #include "usbdevice.hh" #include "radioinfo.hh" #include "errorstack.hh" /** Abstract radio interface. * A radion interface must provide means to communicate with the device. That is, open a connection * to the device, allow for reading and writing specific memory blocks. * * This class defines the common interface for all radio-interface classes, irrespective of the * actual communication protocol being used by the device. * * @ingroup rif */ class RadioInterface { protected: /** Hidden constructor. */ explicit RadioInterface(); public: /** Destructor. */ virtual ~RadioInterface(); /** Return @c true if a connection to the device has been established. */ virtual bool isOpen() const = 0; /** Closes the connection to the device. */ virtual void close() = 0; /** Returns a device identifier. */ virtual RadioInfo identifier(const ErrorStack &err=ErrorStack()) = 0; /** Starts the write process into the specified bank and at the given address. * @param bank Specifies the memory bank to write to. Usually there is only one bank. Some radios, * however, to have several memory banks to hold the codeplug. For example the Open GD77 has * EEPROM and Flash memory banks with independent addresses. * @param addr Specifies the address to write to. * @param err Passes an error stack to put error messages on. */ virtual bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack())=0; /** Writes a chunk of @c data at the address @c addr. * @param bank Specifies the memory bank to write to. Usually there is only one bank. Some radios, * however, to have several memory banks to hold the codeplug. For example the Open GD77 has * EEPROM and Flash memory banks with independent addresses. * @param addr Specifies the address to write to. * @param data Pointer to the actual data to be written. * @param nbytes Specifies the number of bytes to write. * @param err Passes an error stack to put error messages on. * @returns @c true on success. */ virtual bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()) = 0; /** This function ends a series of @c write operations. * This function will be implemented by certain interfaces that need completion of write * operations (e.g., HID). * @param err Passes an error stack to put error messages on. */ virtual bool write_finish(const ErrorStack &err=ErrorStack()) = 0; /** Starts the read process from the specified bank and at the given address. * @param bank Specifies the memory bank to read from. Usually there is only one bank. Some radios, * however, to have several memory banks to hold the codeplug. For example the Open GD77 has * EEPROM and Flash memory banks with independent addresses. * @param addr Specifies the address to read from. * @param err Passes an error stack to put error messages on. */ virtual bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()) = 0; /** Reads a chunk of data from the block-address @c bno (block number). * @param bank Specifies the memory bank to read from. Usually there is only one bank. Some radios, * however, to have several memory banks to hold the codeplug. For example the Open GD77 has * EEPROM and Flash memory banks with independent addresses. * @param addr Specifies the address to read from. * @param data Pointer where to store the read data. * @param nbytes Specifies the number of bytes to read. * @param err Passes an error stack to put error messages on. * @returns @c true on success. */ virtual bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()) = 0; /** This function ends a series of @c read operations. * This function will be re-implemented by certain interfaces that need completion of read * operations (e.g., HID). * @param err Passes an error stack to put error messages on. */ virtual bool read_finish(const ErrorStack &err=ErrorStack()) = 0; /** Some radios need to be rebooted after being read or programmed. This function * will be re-implemented by some interfaces (e.g., DFUDevice) to reboot the radio. By default * this function does nothing. * @param err Passes an error stack to put error messages on. */ virtual bool reboot(const ErrorStack &err=ErrorStack()); }; #endif // RADIOINFERFACE_HH qdmr-0.12.3/lib/radiolimits.cc000066400000000000000000001010631501654372000161410ustar00rootroot00000000000000#include "radiolimits.hh" #include "configobject.hh" #include "logger.hh" #include "config.hh" #include #include #include // Utility function to check string content for ASCII encoding inline bool qstring_is_ascii(const QString &text) { foreach (QChar c, text) { if ((c<0x1f) && (0x7f != c)) return false; } return true; } // Utility function to check string content for DTMF encoding inline bool qstring_is_dtmf(const QString &text) { return QRegularExpression("^[0-9A-Da-d*#]*$").match(text).isValid(); } /* ********************************************************************************************* * * Implementation of RadioLimitIssue * ********************************************************************************************* */ RadioLimitIssue::RadioLimitIssue(Severity severity, const QStringList &stack) : QTextStream(), _severity(severity), _stack(stack), _message() { setString(&_message); } RadioLimitIssue::RadioLimitIssue(const RadioLimitIssue &other) : QTextStream(), _severity(other._severity), _stack(other._stack), _message(other._message) { setString(&_message); } RadioLimitIssue & RadioLimitIssue::operator =(const RadioLimitIssue &other) { QTextStream::flush(); _severity = other._severity; _stack = other._stack; _message = other._message; return *this; } RadioLimitIssue & RadioLimitIssue::operator =(const QString &message) { _message = message; return *this; } RadioLimitIssue::Severity RadioLimitIssue::severity() const { return _severity; } const QString & RadioLimitIssue::message() const { return _message; } QString RadioLimitIssue::format() const { QString res; QTextStream stream(&res); switch (_severity) { case Silent: stream << "Silent: "; break; case Hint: stream << "Hint: "; break; case Warning: stream << "Warn: "; break; case Critical: stream << "Crit: "; break; } stream << "In " << _stack.join(", ") << ": " << _message; stream.flush(); return res; } /* ********************************************************************************************* * * Implementation of RadioLimitContext * ********************************************************************************************* */ RadioLimitContext::RadioLimitContext(bool ignoreFrequencyLimits) : _stack(), _ignoreFrequencyLimits(ignoreFrequencyLimits), _maxSeverity(RadioLimitIssue::Silent) { // pass... } RadioLimitIssue & RadioLimitContext::newMessage(RadioLimitIssue::Severity severity) { _messages.push_back(RadioLimitIssue(severity, _stack)); if (severity > _maxSeverity) _maxSeverity = severity; return _messages.back(); } int RadioLimitContext::count() const { return _messages.count(); } const RadioLimitIssue & RadioLimitContext::message(int n) const { return _messages.at(n); } void RadioLimitContext::push(const QString &element) { _stack.append(element); } void RadioLimitContext::pop() { _stack.pop_back(); } bool RadioLimitContext::ignoreFrequencyLimits() const { return _ignoreFrequencyLimits; } void RadioLimitContext::enableIgnoreFrequencyLimits(bool enable) { _ignoreFrequencyLimits = enable; } RadioLimitIssue::Severity RadioLimitContext::maxSeverity() const { return _maxSeverity; } /* ********************************************************************************************* * * Implementation of RadioLimitElement * ********************************************************************************************* */ RadioLimitElement::RadioLimitElement(QObject *parent) : QObject(parent) { // pass... } RadioLimitElement::~RadioLimitElement() { // pass ... } /* ********************************************************************************************* * * Implementation of RadioLimitIgnored * ********************************************************************************************* */ RadioLimitIgnored::RadioLimitIgnored(RadioLimitIssue::Severity notify, QObject *parent) : RadioLimitObject(parent), _notification(notify) { // pass... } bool RadioLimitIgnored::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { ConfigObject *obj = prop.read(item).value(); if (nullptr != obj) return verifyObject(obj, context); return true; } bool RadioLimitIgnored::verifyObject(const ConfigObject *item, RadioLimitContext &context) const { if (nullptr == item) return true; auto &msg = context.newMessage(_notification); msg = tr("Ignore %1 '%2'. Not applicable/supported by this radio.") .arg(item->metaObject()->className()).arg(item->name()); return true; } /* ********************************************************************************************* * * Implementation of RadioLimitValue * ********************************************************************************************* */ RadioLimitValue::RadioLimitValue(QObject *parent) : RadioLimitElement(parent) { // pass... } /* ********************************************************************************************* * * Implementation of RadioLimitString * ********************************************************************************************* */ RadioLimitString::RadioLimitString(int minLen, int maxLen, Encoding enc, QObject *parent) : RadioLimitValue(parent), _minLen(minLen), _maxLen(maxLen), _encoding(enc) { // pass... } bool RadioLimitString::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { if (QVariant::String != prop.type()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Expected string."; return false; } QString value = prop.read(item).toString(); if ((0<_maxLen) && (value.size() > int(_maxLen))) { auto &msg = context.newMessage(); msg << "String length of " << prop.name() << " ('" << value << "', " << value.size() << ") exceeds maximum length " << _maxLen << "."; } if ((0<_minLen) && (value.size() < int(_minLen))) { auto &msg = context.newMessage(); msg << "String length of " << prop.name() << " ('" << value << "', " << value.size() << ") is shorter than minimum size " << _minLen << "."; } if ((ASCII == _encoding) && (! qstring_is_ascii(value))) { auto &msg = context.newMessage(); msg << "Cannot encode string '" << value << "' in ASCII."; } else if ((DTMF == _encoding) && (! qstring_is_dtmf(value))) { auto &msg = context.newMessage(); msg << "Cannot encode string '" << value << "' in DTMF."; } return true; } /* ********************************************************************************************* * * Implementation of RadioLimitStringRegEx * ********************************************************************************************* */ RadioLimitStringRegEx::RadioLimitStringRegEx(const QString &pattern, QObject *parent) : RadioLimitValue(parent), _pattern(pattern) { // pass... } bool RadioLimitStringRegEx::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { if (QVariant::String != prop.type()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Expected string."; return false; } QString value = prop.read(item).toString(); if (! _pattern.exactMatch(value)) { auto &msg = context.newMessage(RadioLimitIssue::Warning); msg << "Value '" << value << "' of property " << prop.name() << " does not match pattern '" << _pattern.pattern() << "'."; } return true; } /* ********************************************************************************************* * * Implementation of RadioLimitStringIgnored * ********************************************************************************************* */ RadioLimitStringIgnored::RadioLimitStringIgnored(RadioLimitIssue::Severity severity, QObject *parent) : RadioLimitValue(parent), _severity(severity) { // pass... } bool RadioLimitStringIgnored::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { if (QVariant::String != prop.type()) { auto &msg = context.newMessage(RadioLimitIssue::Warning); msg = tr("Expected value of '%1' to be string.").arg(prop.name()); return true; } QVariant value = prop.read(item); if (! value.toString().isEmpty()) { auto &msg = context.newMessage(_severity); msg = tr("Value of '%1' is ignored. Not applicable/supported by the radio.").arg(prop.name()); } return true; } /* ********************************************************************************************* * * Implementation of RadioLimitBool * ********************************************************************************************* */ RadioLimitBool::RadioLimitBool(QObject *parent) : RadioLimitValue(parent) { // pass... } bool RadioLimitBool::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { Q_UNUSED(item) if (QVariant::Bool != prop.type()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Expected bool."; return false; } return true; } /* ********************************************************************************************* * * Implementation of RadioLimitIgnoredBool * ********************************************************************************************* */ RadioLimitIgnoredBool::RadioLimitIgnoredBool(RadioLimitIssue::Severity notify, QObject *parent) : RadioLimitBool(parent), _severity(notify) { // pass... } bool RadioLimitIgnoredBool::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { if (QVariant::Bool != prop.type()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Expected bool."; return false; } bool value = prop.read(item).toBool(); if (value) { auto &msg = context.newMessage(_severity); msg << "Setting " << prop.name() << " is ignored."; } return true; } /* ********************************************************************************************* * * Implementation of RadioLimitUInt * ********************************************************************************************* */ RadioLimitUInt::RadioLimitUInt(qint64 minValue, qint64 maxValue, qint64 defValue, QObject *parent) : RadioLimitValue(parent), _minValue(minValue), _maxValue(maxValue), _defValue(defValue) { // pass... } bool RadioLimitUInt::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { if (QVariant::UInt != prop.type()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Expected uint."; return false; } unsigned value = prop.read(item).toUInt(); if ((0<_maxValue) && (value > qint64(_maxValue)) && ((0>_defValue) || (value!=_defValue))) { auto &msg = context.newMessage(); msg << "Value " << value << " of " << prop.name() << " exceeds maximum " << _maxValue << "."; } if ((0<_minValue) && (value < qint64(_minValue)) && ((0>_defValue) || (value!=_defValue))) { auto &msg = context.newMessage(); msg << "Value " << value << " of " << prop.name() << " is smaller than minimum " << _minValue << "."; } return true; } /* ********************************************************************************************* * * Implementation of RadioLimitDMRId * ********************************************************************************************* */ RadioLimitDMRId::RadioLimitDMRId(QObject *parent) : RadioLimitUInt(1, 16777215, -1, parent) { // pass... } /* ********************************************************************************************* * * Implementation of RadioLimitEnum * ********************************************************************************************* */ RadioLimitEnum::RadioLimitEnum(const std::initializer_list &values, QObject *parent) : RadioLimitValue(parent), _values(values) { // pass... } bool RadioLimitEnum::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { if (! prop.isEnumType()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Expected enum type."; return false; } unsigned value = prop.read(item).toUInt(); if (! _values.contains(value)) { QMetaEnum e = prop.enumerator(); QStringList possible; foreach (unsigned val, _values) possible.append(e.valueToKey(val)); auto &msg = context.newMessage(RadioLimitIssue::Warning); msg << "The enum value '" << e.valueToKey(value) << "' cannot be encoded. " << "Valid values are " << possible.join(", ") << ". " << "Another value might be chosen automatically."; } return true; } /* ********************************************************************************************* * * Implementation of RadioLimitFrequencies * ********************************************************************************************* */ RadioLimitFrequencies::RadioLimitFrequencies(QObject *parent) : RadioLimitValue(parent), _frequencyRanges() { // pass... } RadioLimitFrequencies::RadioLimitFrequencies(const RangeList &ranges, bool warnOnly, QObject *parent) : RadioLimitValue(parent), _frequencyRanges(), _warnOnly(warnOnly) { for (auto range=ranges.begin(); range!=ranges.end(); range++) { _frequencyRanges.append({range->first, range->second}); } } bool RadioLimitFrequencies::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { if (qMetaTypeId() != prop.userType()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Expected frequency."; return false; } if (0 == _frequencyRanges.size()) return true; Frequency value = prop.read(item).value(); foreach (const FrequencyRange &range, _frequencyRanges) { if (range.contains(value)) return true; } if (context.ignoreFrequencyLimits()) return true; auto &msg = context.newMessage(RadioLimitIssue::Warning); msg << "Frequency " << value.inMHz() << "MHz is outside of allowed frequency ranges."; if(_warnOnly) return true; return false; } /* ********************************************************************************************* * * Implementation of RadioLimitTransmitFrequencies * ********************************************************************************************* */ RadioLimitTransmitFrequencies::RadioLimitTransmitFrequencies(QObject *parent) : RadioLimitFrequencies(parent) { // pass... } RadioLimitTransmitFrequencies::RadioLimitTransmitFrequencies(const RangeList &ranges, QObject *parent) : RadioLimitFrequencies(ranges, false, parent) { // pass... } bool RadioLimitTransmitFrequencies::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { if (qMetaTypeId() != prop.userType()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Expected frequency in MHz."; return false; } if (item->is() && (! item->as()->rxOnly())) { return RadioLimitFrequencies::verify(item, prop, context); } return true; } /* ********************************************************************************************* * * Implementation of RadioLimitItem * ********************************************************************************************* */ RadioLimitItem::RadioLimitItem(QObject *parent) : RadioLimitElement(parent), _elements() { // pass... } RadioLimitItem::RadioLimitItem(const PropList &list, QObject *parent) : RadioLimitElement(parent), _elements(list) { for (QHash::iterator item=_elements.begin(); item != _elements.end(); item++) { item.value()->setParent(this); } } bool RadioLimitItem::add(const QString &prop, RadioLimitElement *structure) { if (_elements.contains(prop) || (nullptr == structure)) return false; _elements.insert(prop, structure); structure->setParent(this); return true; } bool RadioLimitItem::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { if (! prop.isReadable()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Not readable."; return false; } if (! propIsInstance(prop)) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Not an instance of ConfigItem."; return false; } if (prop.read(item).isNull()) return true; context.push(QString("Property '%1'").arg(prop.name())); bool success = verifyItem(prop.read(item).value(), context); context.pop(); return success; } bool RadioLimitItem::verifyItem(const ConfigItem *item, RadioLimitContext &context) const { const QMetaObject *meta = item->metaObject(); for (int p=QObject::staticMetaObject.propertyCount(); ppropertyCount(); p++) { // This property QMetaProperty prop = meta->property(p); // Should never happen if (! prop.isValid()) continue; // Verify property if (_elements.contains(prop.name())) { if (! _elements[prop.name()]->verify(item, prop, context)) return false; } } return true; } /* ********************************************************************************************* * * Implementation of RadioLimitObject * ********************************************************************************************* */ RadioLimitObject::RadioLimitObject(QObject *parent) : RadioLimitItem(parent) { // pass... } RadioLimitObject::RadioLimitObject(const PropList &list, QObject *parent) : RadioLimitItem(list, parent) { // pass... } bool RadioLimitObject::verifyObject(const ConfigObject *item, RadioLimitContext &context) const { //context.push(QString("Object '%1'").arg(item->name())); bool success = verifyItem(item, context); //context.pop(); return success; } /* ********************************************************************************************* * * Implementation of RadioLimitObjects * ********************************************************************************************* */ RadioLimitObjects::RadioLimitObjects(const TypeList &list, QObject *parent) : RadioLimitObject(parent), _types() { for (auto type=list.begin(); type!=list.end(); type++) { _types[type->first.className()] = type->second; type->second->setParent(this); } } bool RadioLimitObjects::verifyItem(const ConfigItem *item, RadioLimitContext &context) const { if (! _types.contains(item->metaObject()->className())) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check item of type " << item->metaObject()->className() << ". Unexpected type. Expected one of " << QStringList(_types.keys()).join(", ") << "."; return false; } return _types[item->metaObject()->className()]->verifyItem(item, context); } /* ********************************************************************************************* * * Implementation of RadioLimitObjRef * ********************************************************************************************* */ RadioLimitObjRef::RadioLimitObjRef(const QMetaObject &type, bool allowNull, QObject *parent) : RadioLimitElement(parent), _allowNull(allowNull), _types() { _types.insert(type.className()); } RadioLimitObjRef::RadioLimitObjRef(const MetaObjectList &types, bool allowNull, QObject *parent) : RadioLimitElement(parent), _allowNull(allowNull), _types() { for (auto type=types.begin(); type!=types.end(); type++) _types.insert(type->className()); } bool RadioLimitObjRef::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { ConfigObjectReference *ref = prop.read(item).value(); if (nullptr == ref) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check type of property '" << prop.name() << "'. Exprected ConfigObjectReference."; return false; } if (ref->isNull()) { if (_allowNull) return true; auto &msg = context.newMessage(RadioLimitIssue::Warning); msg << "Property '" << prop.name() << "' must refer to an instances of " << QStringList(_types.begin(), _types.end()).join(", ") << "."; return true; } if (! validType(ref->as()->metaObject())) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Property '" << prop.name() << "' must refer to an instances of " << QStringList(_types.begin(), _types.end()).join(", ") << "."; return false; } return true; } bool RadioLimitObjRef::validType(const QMetaObject *type) const { if (_types.contains(type->className())) return true; if (type->superClass()) return validType(type->superClass()); return false; } /* ********************************************************************************************* * * Implementation of RadioLimitObjRefIgnored * ********************************************************************************************* */ RadioLimitObjRefIgnored::RadioLimitObjRefIgnored( ConfigObject *defObj, RadioLimitIssue::Severity notify, QObject *parent) : RadioLimitObjRef(ConfigObject::staticMetaObject, true, parent), _severity(notify), _default(defObj) { // pass... } bool RadioLimitObjRefIgnored::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { // Get referenced object ConfigObjectReference *ref = prop.read(item).value(); // If reference is set and not default reference if ((! ref->isNull()) && (_default != ref->as())) { auto &msg = context.newMessage(_severity); msg << "The reference '" << prop.name() << "' is ignored. Not applicable/supported by this radio."; } return true; } /* ********************************************************************************************* * * Implementation of RadioLimitList * ********************************************************************************************* */ RadioLimitList::RadioLimitList(const QMetaObject &type, int minSize, int maxSize, RadioLimitObject *element, QObject *parent) : RadioLimitElement(parent), _elements(), _minCount(), _maxCount() { _elements.insert(type.className(), element); _minCount.insert(type.className(), minSize); _maxCount.insert(type.className(), maxSize); element->setParent(this); } RadioLimitList::RadioLimitList(const std::initializer_list &elements, QObject *parent) : RadioLimitElement(parent), _elements(), _minCount(), _maxCount() { for (auto el=elements.begin(); el!=elements.end(); el++) { QString className = el->type.className(); _elements.insert(className, el->structure); el->structure->setParent(this); _minCount.insert(className, el->minCount); _maxCount.insert(className, el->maxCount); } } bool RadioLimitList::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { if (! prop.isReadable()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Not readable."; return false; } if (nullptr == prop.read(item).value()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Not an instance of ConfigObjectList."; return false; } const ConfigObjectList *plist = prop.read(item).value(); QHash counts; foreach (QString type, _elements.keys()) counts.insert(type,0); context.push(QString("List '%1'").arg(prop.name())); // Check type and structure for (int i=0; icount(); i++) { // Check type ConfigObject *obj = plist->get(i); QString className = findClassName(*(obj->metaObject())); if (className.isEmpty()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Unexpected element type '" << obj->metaObject()->className() << "'. Expected one of " << _elements.keys().join(", ") << "."; context.pop(); return false; } counts[className]++; context.push(QString("Element %1 ('%2')").arg(i).arg(obj->name())); if (! _elements[className]->verifyObject(obj, context)) { context.pop(); context.pop(); return false; } context.pop(); } // Check counts foreach (QString className, _elements.keys()) { if ((0 <= _minCount[className]) && (counts[className]<_minCount[className])) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "The number of elements of type '" << className << "' " << counts[className] << " is less than the required count " << _minCount[className] << "."; } if ((0 <= _maxCount[className]) && (counts[className]>_maxCount[className])) { auto &msg = context.newMessage(RadioLimitIssue::Warning); msg << "The number of elements of type '" << className << "' " << counts[className] << " is greater than the maximum count " << _maxCount[className] << "."; } } context.pop(); return true; } QString RadioLimitList::findClassName(const QMetaObject &type) const { if (_elements.contains(type.className())) return type.className(); if (const QMetaObject *super = type.superClass()) return findClassName(*super); return ""; } /* ********************************************************************************************* * * Implementation of RadioLimitRefList * ********************************************************************************************* */ RadioLimitRefList::RadioLimitRefList(int minSize, int maxSize, const QMetaObject &type, QObject *parent) : RadioLimitElement(parent), _minSize(minSize), _maxSize(maxSize), _types() { _types.insert(type.className()); } bool RadioLimitRefList::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { if (! prop.isReadable()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Not readable."; return false; } if (nullptr == prop.read(item).value()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Not an instance of ConfigObjectRefList."; return false; } const ConfigObjectRefList *plist = prop.read(item).value(); if ((0 <= _minSize) && (_minSize > plist->count())) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "List '" << prop.name() << "' requires at least " << _minSize << " elements, " << plist->count() << " elements found."; return false; } if ((0 <= _maxSize) && (_maxSize < plist->count())) { auto &msg = context.newMessage(RadioLimitIssue::Warning); msg << "List '" << prop.name() << "' takes at most " << _maxSize << " elements, " << plist->count() << " elements found."; return false; } for (int i=0; icount(); i++) { if (! validType(plist->get(i)->metaObject())) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Reference to " << plist->get(i)->metaObject()->className() << " is not allowed here. " << "Must be one of " << QStringList(_types.begin(), _types.end()).join(", ") << "."; return false; } } return true; } bool RadioLimitRefList::validType(const QMetaObject *type) const { if (_types.contains(type->className())) return true; if (type->superClass()) return validType(type->superClass()); return false; } /* ********************************************************************************************* * * Implementation of RadioLimitPrivateCallRefList * ********************************************************************************************* */ RadioLimitGroupCallRefList::RadioLimitGroupCallRefList(int minSize, int maxSize, QObject *parent) : RadioLimitElement(parent), _minSize(minSize), _maxSize(maxSize) { // pass... } bool RadioLimitGroupCallRefList::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const { if (! prop.isReadable()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Not readable."; return false; } if (nullptr == prop.read(item).value()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Cannot check property " << prop.name() << ": Not an instance of ConfigObjectRefList."; return false; } const ConfigObjectRefList *plist = prop.read(item).value(); if ((0 <= _minSize) && (_minSize > plist->count())) { auto &msg = context.newMessage(RadioLimitIssue::Warning); msg << "List '" << prop.name() << "' requires at least " << _minSize << " elements, " << plist->count() << " elements found."; return false; } if ((0 <= _maxSize) && (_maxSize < plist->count())) { auto &msg = context.newMessage(RadioLimitIssue::Warning); msg << "List '" << prop.name() << "' takes at most " << _maxSize << " elements, " << plist->count() << " elements found."; return false; } for (int i=0; icount(); i++) { if (! plist->get(i)->is()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Reference to " << plist->get(i)->metaObject()->className() << " is not allowed here. " << "Must be DigtialContact."; return false; } if (DMRContact::GroupCall != plist->get(i)->as()->type()) { auto &msg = context.newMessage(RadioLimitIssue::Critical); msg << "Expected reference to a group call digital contact."; return false; } } return true; } /* ********************************************************************************************* * * Implementation of RadioLimitSingleZone * ********************************************************************************************* */ RadioLimitSingleZone::RadioLimitSingleZone(qint64 maxSize, const PropList &list, QObject *parent) : RadioLimitObject(list, parent) { // A and B may hold up to maxSize references to channels _elements["A"] = new RadioLimitRefList(1, maxSize, Channel::staticMetaObject, this); _elements["B"] = new RadioLimitRefList(0, maxSize, Channel::staticMetaObject, this); } bool RadioLimitSingleZone::verifyItem(const ConfigItem *item, RadioLimitContext &context) const { if (! RadioLimitObject::verifyItem(item, context)) return false; const Zone *zone = item->as(); if (zone && zone->B()->count()) { auto &msg = context.newMessage(); msg << "This radio does not support dual channel zones. The zone '" << zone->name() << "' will be split into two."; } return true; } /* ********************************************************************************************* * * Implementation of RadioLimits * ********************************************************************************************* */ RadioLimits::RadioLimits(bool betaWarning, QObject *parent) : RadioLimitItem(parent), _betaWarning(betaWarning), _hasCallSignDB(false), _callSignDBImplemented(false), _numCallSignDBEntries(0), _hasSatelliteConfig(false), _satelliteConfigImplemented(false), _numSatellites(0) { // pass... } RadioLimits::RadioLimits(const std::initializer_list > &list, QObject *parent) : RadioLimitItem(list, parent), _hasCallSignDB(false), _callSignDBImplemented(false), _numCallSignDBEntries(0), _hasSatelliteConfig(false), _satelliteConfigImplemented(false), _numSatellites(0) { // pass... } bool RadioLimits::hasCallSignDB() const { return _hasCallSignDB; } bool RadioLimits::callSignDBImplemented() const { return _callSignDBImplemented; } unsigned RadioLimits::numCallSignDBEntries() const { return _numCallSignDBEntries; } bool RadioLimits::hasSatelliteConfig() const { return _hasSatelliteConfig; } bool RadioLimits::satelliteConfigImplemented() const { return _satelliteConfigImplemented; } unsigned RadioLimits::numSatellites() const { return _numSatellites; } bool RadioLimits::verifyConfig(const Config *config, RadioLimitContext &context) const { if (_betaWarning) { auto &msg = context.newMessage(RadioLimitIssue::Warning); msg = tr("The support for this radio is still under development. Some features may sill be " "missing or are not well tested."); } return verifyItem(config, context); } qdmr-0.12.3/lib/radiolimits.hh000066400000000000000000000576061501654372000161700ustar00rootroot00000000000000/** @defgroup limits Radio Limits * This module collects all classes used to define the limits for each supported radio. That is, * a tree of objects that hold the limitations like string length, number of elements in a list etc. * for the various settings of a radio including their extensions. * * This system will replace the static @c Radio::Features struct. The associated limits for each * radio can be obtained using the @c Radio::limits method. * * Many classes in this module provide an initializer list constructor. This allows for an easy * construction of radio limits programmatically like * @code * new RadioLimitItem { // < Describes an ConfigItem * { "radioIDs", // < with an 'radioIDs' property * new RadioLimitList( // < that is a list, * RadioId::staticMetaObject, // < holding instances of RadioId, * 1, 10, // of at least one but max 10 elements * new RadioLimitObject { // < of objects, with * { "name", // < a name * new RadioLimitString(1, 16) }, // being a string between 1 and 10 chars * { "id", // < and an ID * new RadioLimitUInt(0, 16777216) } // being an unsigned integer between 0 and 16777216 * } * ) * } * }; * @endcode * * @ingroup conf */ #ifndef RADIOLIMITS_HH #define RADIOLIMITS_HH #include #include #include #include #include "frequency.hh" #include "ranges.hh" // Forward declaration class Config; class ConfigItem; class ConfigObject; class RadioLimits; /** Represents a single issue found during verification. * @ingroup limits */ class RadioLimitIssue: public QTextStream { public: /** Defines the possible severity levels. */ enum Severity { Silent, ///< The user will not be notified. Hint, ///< Just a hint, a working codplug will be assembled. Warning, ///< The codeplug gets changed but a working codeplug might be assembled. Critical ///< Assembly of the codeplug will fail or a non-functional codeplug might be created. }; public: /** Constructs an empty message for the specified severity at the specified point of the * verification. */ RadioLimitIssue(Severity severity, const QStringList &stack); /** Copy constructor. */ RadioLimitIssue(const RadioLimitIssue &other); /** Copy assignment. */ RadioLimitIssue &operator =(const RadioLimitIssue &other); /** Set message. */ RadioLimitIssue &operator =(const QString &message); /** Returns the severity of the issue. */ Severity severity() const; /** Returns the text message. */ const QString &message() const; /** Formats the message. */ QString format() const; protected: /** Holds the severity of the issue. */ Severity _severity; /** Holds the item-stack (where the issue occurred). */ QStringList _stack; /** Holds the text message. */ QString _message; }; /** Collects the issues found during verification. * This class also tracks where the issues arise. * * @ingroup limits */ class RadioLimitContext { public: /** Empty constructor. */ explicit RadioLimitContext(bool ignoreFrequencyLimits=false); /** Constructs a new message and puts it into the list of issues. */ RadioLimitIssue &newMessage(RadioLimitIssue::Severity severity = RadioLimitIssue::Hint); /** Returns the number of issues. */ int count() const; /** Returns the n-th issue. */ const RadioLimitIssue &message(int n) const; /** Push a property name/element index onto the stack. * This method is used to track the origin of an issue. */ void push(const QString &element); /** Pops the top-most property name/element index from the stack. */ void pop(); /** If @c true, frequency limit voilations are warnings. */ bool ignoreFrequencyLimits() const; /** Enables/disables that frequency range voilations are handled as warnings. */ void enableIgnoreFrequencyLimits(bool enable=true); /** Returns the highest severity of the messages. */ RadioLimitIssue::Severity maxSeverity() const; protected: /** The current item stack. */ QStringList _stack; /** The list of issues found. */ QList _messages; /** If @c true, any frequency range voilation is a warning. */ bool _ignoreFrequencyLimits; /** Holds the highest severity of all messages. */ RadioLimitIssue::Severity _maxSeverity; }; /** Abstract base class for all radio limits. * * @ingroup limits */ class RadioLimitElement: public QObject { Q_OBJECT public: /** Initializer lists of ConfigItem properties. */ typedef std::initializer_list< std::pair > PropList; protected: /** Hidden constructor. */ explicit RadioLimitElement(QObject *parent=nullptr); public: /** Verifies the given property of the specified item. * This method gets implemented by the specialized classes to implement the actual verification. */ virtual bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const = 0; public: /** Destructor. */ virtual ~RadioLimitElement(); }; /** Base class to verify values. * * That is, the verification of strings, integers, floats, etc. * @ingroup limits */ class RadioLimitValue: public RadioLimitElement { Q_OBJECT protected: /** Hidden constructor. */ explicit RadioLimitValue(QObject *parent=nullptr); }; /** Checks a string valued property. * * Instances of this class can be used to verify string values. That is, checking the length of the * string and its encoding. * * @ingroup limits */ class RadioLimitString: public RadioLimitValue { Q_OBJECT public: /** Possible encoding of strings. */ enum Encoding { DTMF, ///< Just DTMF symbols are allowed (0-9, A-D, *, #). ASCII, ///< Just ASCII is allowed. Unicode ///< Any Unicode character is allowed. }; public: /** Constructor. * @param minLen Specifies the minimum length of the string. If -1, check is disabled. * @param maxLen Specifies the maximum length of the string. If -1, check is disabled. * @param enc Specifies the allowed string encoding. * @param parent Specifies the QObject parent object. */ RadioLimitString(int minLen, int maxLen, Encoding enc, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; protected: /** Holds the minimum length of the string. If -1, the check is disabled. */ int _minLen; /** Holds the maximum length of the string. If -1, the check is disabled. */ int _maxLen; /** Holds the allowed character encoding. */ Encoding _encoding; }; /** Verifies that a string matches a regular expression. * @ingroup limits */ class RadioLimitStringRegEx: public RadioLimitValue { Q_OBJECT public: /** Constructor. * @param pattern Specifies the regular expression pattern, the string must match. * @param parent Specifies the QObject parent. */ RadioLimitStringRegEx(const QString &pattern, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; protected: /** Holds the regular expression pattern. */ QRegExp _pattern; }; /** Notifies the user that a string gets ignored. * This is usually the case, when named elements are referenced within the codeplug by index. * @ingroup limits */ class RadioLimitStringIgnored: public RadioLimitValue { Q_OBJECT public: /** Constructor. */ RadioLimitStringIgnored(RadioLimitIssue::Severity severity=RadioLimitIssue::Hint, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; protected: /** Holds the severity of the message. */ RadioLimitIssue::Severity _severity; }; /** Checks if a property is a boolean value. * @ingroup limits */ class RadioLimitBool: public RadioLimitValue { Q_OBJECT public: /** Constructor. */ explicit RadioLimitBool(QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; }; /** Specifies an boolean value as ignored. * If the boolean value is @c true, a message is generated indicating that this property is ignored. * If the value is @c false, nothing happens. */ class RadioLimitIgnoredBool: public RadioLimitBool { Q_OBJECT public: /** Constructor. * @param notify Specifies the severity of the generated message. * @param parent Specifies the QObject parent. */ explicit RadioLimitIgnoredBool(RadioLimitIssue::Severity notify=RadioLimitIssue::Hint, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; protected: /** The severity of the issue generated. */ RadioLimitIssue::Severity _severity; }; /** Represents a limit for an unsigned integer value. * @ingroup limits */ class RadioLimitUInt: public RadioLimitValue { Q_OBJECT public: /** Constructor. * @param minValue Specifies the minimum value. If -1, no check is performed. * @param maxValue Specifies the maximum value. If -1, no check is performed. * @param defValue Specifies the default value. If -1, no default value is set. * @param parent Specifies the QObject parent. */ RadioLimitUInt(qint64 minValue=-1, qint64 maxValue=-1, qint64 defValue=-1, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; protected: /** Holds the minimum value. If -1, the check is disabled. */ qint64 _minValue; /** Holds the maximum value. If -1, the check is disabled. */ qint64 _maxValue; /** Holds the default value. If -1, no default value is set. */ qint64 _defValue; }; /** Represents a DMR ID. * That is an uint between 1 and 16777215 without any default value. * @ingroup limits */ class RadioLimitDMRId: public RadioLimitUInt { Q_OBJECT public: /** Constructor. * @param parent Specifies the QObject parent. */ explicit RadioLimitDMRId(QObject *parent=nullptr); }; /** Represents a limit for a set of enum values. * @ingroup limits */ class RadioLimitEnum: public RadioLimitValue { Q_OBJECT public: /** Constructor from initializer list of possible enum values. */ RadioLimitEnum(const std::initializer_list &values, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; protected: /** Holds the set of valid values. */ QSet _values; }; /** Represents a limit on frequencies in MHz. * @ingroup limits */ class RadioLimitFrequencies: public RadioLimitValue { Q_OBJECT public: /** Typedef for the initializer list. */ typedef std::initializer_list> RangeList; public: /** Empty constructor. */ explicit RadioLimitFrequencies(QObject *parent=nullptr); /** Constructor from initializer list. */ RadioLimitFrequencies(const RangeList &ranges, bool warnOnly=false, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; protected: /** Holds the frequency ranges for the device. */ QList _frequencyRanges; /** If @c true, throw only a warning. */ bool _warnOnly; }; /** Specialization for transmit frequency limits. * The verification is only performed if the channel is not "RX Only". * @ingroup limits */ class RadioLimitTransmitFrequencies: public RadioLimitFrequencies { Q_OBJECT public: /** Empty constructor. */ explicit RadioLimitTransmitFrequencies(QObject *parent=nullptr); /** Constructor from initializer list. */ RadioLimitTransmitFrequencies(const RangeList &ranges, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; }; /** Represents the limits for a @c ConfigItem instance. * * That is, it holds the limits for every property of the @c ConfigItem instance. This class * provides a initializer list constructor for easy programmatic construction of limits. * * @ingroup limits */ class RadioLimitItem: public RadioLimitElement { Q_OBJECT public: /** Empty constructor. */ explicit RadioLimitItem(QObject *parent=nullptr); /** Constructor from initializer list. * The ownership of all passed elements are taken. */ RadioLimitItem(const PropList &list, QObject *parent=nullptr); /** Adds a property declaration. * * The item takes the ownership of the structure declaration. If a property is already defined * with the same name, @c false is returned. * * @param prop Specifies the name of the property. * @param structure Specifies the structure declaration of the property value. * @returns @c false If a property with the same name is already defined. */ bool add(const QString &prop, RadioLimitElement *structure); virtual bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; /** Verifies the properties of the given item. */ virtual bool verifyItem(const ConfigItem *item, RadioLimitContext &context) const; protected: /** Holds the property <-> limits map. */ QHash _elements; }; /** Represents the limits for all properties of a @c ConfigObject instance. * @ingroup limits */ class RadioLimitObject: public RadioLimitItem { Q_OBJECT public: /** Empty constructor. */ explicit RadioLimitObject(QObject *parent=nullptr); /** Constructor from initializer list. * The ownership of all passed elements are taken. */ RadioLimitObject(const PropList &list, QObject *parent=nullptr); /** Verifies the properties of the given object. */ virtual bool verifyObject(const ConfigObject *item, RadioLimitContext &context) const; }; /** Represents an ignored element in the codeplug. * * Instances of this class might be used to inform the user about a configured feature not present * in the particular radio. * * @ingroup limits */ class RadioLimitIgnored: public RadioLimitObject { Q_OBJECT public: /** Constructor for a ignored setting verification element. */ RadioLimitIgnored(RadioLimitIssue::Severity notify=RadioLimitIssue::Hint, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; bool verifyObject(const ConfigObject *item, RadioLimitContext &context) const; protected: /** Holds the level of the notification. */ RadioLimitIssue::Severity _notification; }; /** Dispatch by class. * * Sometimes, a property may hold objects of different type. In these cases, a dispatcher is needed * to specify which limits to apply based on the type of the object. This class implements this * dispatcher. * * @ingroup limits */ class RadioLimitObjects: public RadioLimitObject { Q_OBJECT public: /** Initializer lists of type properties. */ typedef std::initializer_list > TypeList; public: /** Constructor from initializer list. * * A list of pairs of a @c QMetaObject and a @c RadioLimitObject must be given. The meta object * specifies the type of the @c ConfigObject and the associated @c RadioLimitObject the limits * for this type. * * A dispatch for Analog and DigitalChannel may look like * @code * new RadioLimitObjects{ * { AnalogChannel::staticMetaObject, new RadioLimitObject{ * // Limits for analog channel objects * } }, * {DigialChannel::staticMetaObject, new RadioLimitObject{ * // Limits for digital channel objects * } } * }; * @endcode */ RadioLimitObjects(const TypeList &list, QObject *parent=nullptr); bool verifyItem(const ConfigItem *item, RadioLimitContext &context) const; protected: /** Maps class-names to object limits. */ QHash _types; }; /** Limits the possible classes a @c ConfigObjectReference may refer to. * @ingroup limits */ class RadioLimitObjRef: public RadioLimitElement { Q_OBJECT public: /** Initializer lists of allowed classes. */ typedef std::initializer_list MetaObjectList; public: /** Constructor. * @param type Specifies the type that might be referenced. * @param allowNull If @c true, the reference may be a nullptr. * @param parent Specifies the QObject parent. */ RadioLimitObjRef(const QMetaObject &type, bool allowNull=true, QObject *parent=nullptr); /** Constructor. * @param types Specifies the types that might be referenced. * @param allowNull If @c true, the reference may be a nullptr. * @param parent Specifies the QObject parent. */ RadioLimitObjRef(const MetaObjectList &types, bool allowNull=true, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; protected: /** Checks if the given type is one of the valid ones in @c _types. */ bool validType(const QMetaObject *type) const; protected: /** If @c true, a null reference is allowed. */ bool _allowNull; /** Possible classes of instances, the reference may point to. */ QSet _types; }; /** Issues a notification if a reference is set. * @ingroup limits */ class RadioLimitObjRefIgnored: public RadioLimitObjRef { Q_OBJECT public: /** Constructor. * @param defObj Specifies a weak reference to a default object that gets silently ignored. * @param notify Specifies the issue severity. * @param parent Specifies the QObject parent. */ RadioLimitObjRefIgnored( ConfigObject *defObj=nullptr, RadioLimitIssue::Severity notify=RadioLimitIssue::Hint, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; protected: /** The severity of the issue. */ RadioLimitIssue::Severity _severity; /** A weak reference to a default value, that gets silently ignored. */ ConfigObject *_default; }; /** Specifies the limits for a list of @c ConfigObject instances. * @ingroup limits */ class RadioLimitList: public RadioLimitElement { Q_OBJECT public: /** Helper struct to pass list entry definitions */ struct ElementLimits { const QMetaObject &type; ///< The type of the object int minCount; ///< Minimum count of elements. int maxCount; ///< Maximum count of elements. RadioLimitObject *structure; ///< The structure of the elements. }; public: /** Constructor. * @param type Specifies the type of the elements. * @param minSize Specifies the minimum size of the list. If -1, no check is performed. * @param maxSize Specifies the maximum size of the list. If -1, no check is performed. * @param element Specifies the limits for all objects in the list. If the list contains instances * of different ConfigObject types, use @c RadioLimitObjects dispatcher. * @param parent Specifies the QObject parent. */ RadioLimitList(const QMetaObject &type, int minSize, int maxSize, RadioLimitObject *element, QObject *parent=nullptr); /** Constructor from initializer list. */ RadioLimitList(const std::initializer_list &elements, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; protected: /** Searches for the specified type or one of its super-clsases in the set of allowed types. */ QString findClassName(const QMetaObject &type) const; protected: /** Maps typename to element definition. */ QHash _elements; /** Maps typename to minimum count. */ QHash _minCount; /** Maps typename to maximum count. */ QHash _maxCount; }; /** Implements the limits for reference lists. * @ingroup limits */ class RadioLimitRefList: public RadioLimitElement { Q_OBJECT public: /** Constructor. * @param minSize Specifies the minimum size of the list. If -1, no check is performed. * @param maxSize Specifies the maximum size of the list. If -1, no check is performed. * @param type Specifies the type, the references must be instances of. * @param parent Specifies the QObject parent. */ RadioLimitRefList(int minSize, int maxSize, const QMetaObject &type, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; protected: /** Checks if the given type is one of the valid ones in @c _types. */ bool validType(const QMetaObject *type) const; protected: /** Holds the minimum size of the list. */ qint64 _minSize; /** Holds the maximum size of the list. */ qint64 _maxSize; /** Possible classes of instances, the references may point to. */ QSet _types; }; /** Implements the limits for a list of references to group call contacts. * This is used to restrict the elements of group lists to group calls. * @ingroup limits */ class RadioLimitGroupCallRefList: public RadioLimitElement { Q_OBJECT public: /** Constructor * @param minSize Specifies the minimum size of the list. If -1, no check is performed. * @param maxSize Specifies the maximum size of the list. If -1, no check is performed. * @param parent Specifies the QObject parent. */ RadioLimitGroupCallRefList(int minSize, int maxSize, QObject *parent=nullptr); bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const; protected: /** Holds the minimum size of the list. */ qint64 _minSize; /** Holds the maximum size of the list. */ qint64 _maxSize; }; /** Specialized RadioLimitObject handling a zone for radio supporting only a single channel list * per zone. * * Checks if a zone contains two sets of channel lists and notifies the user that the zone gets * split. * * @ingroup limits */ class RadioLimitSingleZone: public RadioLimitObject { Q_OBJECT public: /** Constructor. * @param maxSize Specifies the maximum size of the zone. If -1, no check is performed. * @param list Initializer list for further zone properties. * @param parent Specifies the QObject parent. */ RadioLimitSingleZone(qint64 maxSize, const PropList &list, QObject *parent=nullptr); bool verifyItem(const ConfigItem *item, RadioLimitContext &context) const; }; /** Represents the limits or the entire codeplug. * * Use @c Radio::limits to obtain an instance. * @ingroup limits */ class RadioLimits : public RadioLimitItem { Q_OBJECT public: /** Empty constructor. */ explicit RadioLimits(bool betaWarning, QObject *parent = nullptr); /** Constructor from initializer list. */ RadioLimits(const std::initializer_list > &list, QObject *parent=nullptr); /** Verifies the given configuration. */ virtual bool verifyConfig(const Config *config, RadioLimitContext &context) const; /** Returns @c true if the radio supports a call-sign DB. */ bool hasCallSignDB() const; /** Returns @c true if the call-sign DB is implemented. */ bool callSignDBImplemented() const; /** Returns the maximum number of entries in the call-sign DB. */ unsigned numCallSignDBEntries() const; /** Returns @c true if the radio supports satellite config. */ bool hasSatelliteConfig() const; /** Returns @c true if satellite config is implemented. */ bool satelliteConfigImplemented() const; /** Returns the maximum number of satellites. */ unsigned numSatellites() const; protected: /** If @c true, a warning is issued that the radio is still under development and not well * tested yet. */ bool _betaWarning; /** If @c true, the radio supports a call-sign DB. */ bool _hasCallSignDB; /** If @c true, the call-sign is implemented. */ bool _callSignDBImplemented; /** Holds the number of possible call-sign DB entries. */ unsigned _numCallSignDBEntries; /** If @c true, the radio supports satellite config. */ bool _hasSatelliteConfig; /** If @c true, satellite config is implemented. */ bool _satelliteConfigImplemented; /** Holds the number of possible satellites. */ unsigned _numSatellites; }; #endif // RADIOLIMITS_HH qdmr-0.12.3/lib/radiosettings.cc000066400000000000000000000113771501654372000165100ustar00rootroot00000000000000#include "radiosettings.hh" #include "radioid.hh" RadioSettings::RadioSettings(QObject *parent) : ConfigItem(parent), _introLine1(""), _introLine2(""), _micLevel(3), _speech(false), _squelch(1), _power(Channel::Power::High), _vox(0), _transmitTimeOut(0), _defaultId(new DMRRadioIDReference(this)), _tytExtension(nullptr), _radioddityExtension(nullptr), _anytoneExtension(nullptr) { // pass } bool RadioSettings::copy(const ConfigItem &other) { const RadioSettings *set = other.as(); if ((nullptr==set) || (!ConfigItem::copy(other))) return false; if (set->voxDisabled()) disableVOX(); if (set->totDisabled()) disableTOT(); return true; } ConfigItem * RadioSettings::clone() const { RadioSettings *set = new RadioSettings(); if (! set->copy(*this)) { set->deleteLater(); return nullptr; } return set; } void RadioSettings::clear() { ConfigItem::clear(); _introLine1.clear(); _introLine2.clear(); _micLevel = 3; _speech = false; _squelch = 1; _power = Channel::Power::High; disableVOX(); disableTOT(); defaultIdRef()->clear(); // delete extensions setTyTExtension(nullptr); setRadioddityExtension(nullptr); setAnytoneExtension(nullptr); } const QString & RadioSettings::introLine1() const { return _introLine1; } void RadioSettings::setIntroLine1(const QString &line) { _introLine1 = line; emit modified(this); } const QString & RadioSettings::introLine2() const { return _introLine2; } void RadioSettings::setIntroLine2(const QString &line) { _introLine2 = line; emit modified(this); } unsigned RadioSettings::micLevel() const { return _micLevel; } void RadioSettings::setMicLevel(unsigned value) { _micLevel = value; emit modified(this); } bool RadioSettings::speech() const { return _speech; } void RadioSettings::enableSpeech(bool enabled) { _speech = enabled; emit modified(this); } unsigned RadioSettings::squelch() const { return _squelch; } void RadioSettings::setSquelch(unsigned squelch) { squelch = std::min(10u, squelch); _squelch = squelch; emit modified(this); } Channel::Power RadioSettings::power() const { return _power; } void RadioSettings::setPower(Channel::Power power) { _power = power; emit modified(this); } bool RadioSettings::voxDisabled() const { return 0 == vox(); } unsigned RadioSettings::vox() const { return _vox; } void RadioSettings::setVOX(unsigned level) { _vox = level; emit modified(this); } void RadioSettings::disableVOX() { setVOX(0); } bool RadioSettings::totDisabled() const { return 0==tot(); } unsigned RadioSettings::tot() const { return _transmitTimeOut; } void RadioSettings::setTOT(unsigned sec) { _transmitTimeOut = sec; emit modified(this); } void RadioSettings::disableTOT() { setTOT(0); } DMRRadioIDReference * RadioSettings::defaultIdRef() const { return _defaultId; } DMRRadioID * RadioSettings::defaultId() const { if (_defaultId->isNull()) return nullptr; return _defaultId->as(); } void RadioSettings::setDefaultId(DMRRadioID *id) { _defaultId->set(id); } TyTSettingsExtension * RadioSettings::tytExtension() const { return _tytExtension; } void RadioSettings::setTyTExtension(TyTSettingsExtension *ext) { if (_tytExtension) { disconnect(_tytExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onExtensionModified())); _tytExtension->deleteLater(); } _tytExtension = ext; if (_tytExtension) { _tytExtension->setParent(this); connect(_tytExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onExtensionModified())); } emit modified(this); } RadiodditySettingsExtension * RadioSettings::radioddityExtension() const { return _radioddityExtension; } void RadioSettings::setRadioddityExtension(RadiodditySettingsExtension *ext) { if (_radioddityExtension) { disconnect(_radioddityExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onExtensionModified())); _radioddityExtension->deleteLater(); } _radioddityExtension = ext; if (_radioddityExtension) { _radioddityExtension->setParent(this); connect(_radioddityExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onExtensionModified())); } emit modified(this); } AnytoneSettingsExtension * RadioSettings::anytoneExtension() const { return _anytoneExtension; } void RadioSettings::setAnytoneExtension(AnytoneSettingsExtension *ext) { if (_anytoneExtension) { disconnect(_anytoneExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onExtensionModified())); _anytoneExtension->deleteLater(); } _anytoneExtension = ext; if (_anytoneExtension) { _anytoneExtension->setParent(this); connect(_anytoneExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onExtensionModified())); } emit modified(this); } void RadioSettings::onExtensionModified() { emit modified(this); } qdmr-0.12.3/lib/radiosettings.hh000066400000000000000000000125721501654372000165200ustar00rootroot00000000000000#ifndef RADIOSETTINGS_HH #define RADIOSETTINGS_HH #include "configobject.hh" #include "channel.hh" #include "radioddity_extensions.hh" #include "anytone_extension.hh" #include "tyt_extensions.hh" /** Represents the common radio-global settings. * @ingroup conf */ class RadioSettings : public ConfigItem { Q_OBJECT /** The first intro line. */ Q_PROPERTY(QString introLine1 READ introLine1 WRITE setIntroLine1) /** The scond intro line. */ Q_PROPERTY(QString introLine2 READ introLine2 WRITE setIntroLine2) /** The mic amplification level. */ Q_PROPERTY(unsigned micLevel READ micLevel WRITE setMicLevel) /** Speech synthesis flag. */ Q_PROPERTY(bool speech READ speech WRITE enableSpeech) /** The default channel power */ Q_PROPERTY(Channel::Power power READ power WRITE setPower) /** The squelch level. */ Q_PROPERTY(unsigned squelch READ squelch WRITE setSquelch) /** The default vox sensitivity */ Q_PROPERTY(unsigned vox READ vox WRITE setVOX) /** The default transmit timeout */ Q_PROPERTY(unsigned tot READ tot WRITE setTOT) /** The default DMR radio ID. */ Q_PROPERTY(DMRRadioIDReference *defaultID READ defaultIdRef) /** The settings extension for TyT devices. */ Q_PROPERTY(TyTSettingsExtension* tyt READ tytExtension WRITE setTyTExtension) /** The settings extension for Radioddity devices. */ Q_PROPERTY(RadiodditySettingsExtension * radioddity READ radioddityExtension WRITE setRadioddityExtension) /** Settings for AnyTone devices. */ Q_PROPERTY(AnytoneSettingsExtension *anytone READ anytoneExtension WRITE setAnytoneExtension) public: /** Default constructor. */ explicit RadioSettings(QObject *parent=nullptr); bool copy(const ConfigItem &other); ConfigItem *clone() const; /** Resets the settings. */ void clear(); /** Returns the first intro line. */ const QString &introLine1() const; /** (Re-)Sets the first intro line. */ void setIntroLine1(const QString &line); /** Returns the second intro line. */ const QString &introLine2() const; /** (Re-)Sets the second intro line. */ void setIntroLine2(const QString &line); /** Returns the MIC amplification level [1,10]. */ unsigned micLevel() const; /** (Re-)Sets the MIC amplification level [1,10]. */ void setMicLevel(unsigned value); /** Returns @c true if the speech synthesis is enabled. */ bool speech() const; /** Enables/disables the speech synthesis. */ void enableSpeech(bool enabled); /** Returns the default squelch level [0-10]. */ unsigned squelch() const; /** Sets the default squelch level. */ void setSquelch(unsigned squelch); /** Returns the default channel power. */ Channel::Power power() const; /** Sets the default channel power. */ void setPower(Channel::Power power); /** Returns @c true if VOX is disabled by default. */ bool voxDisabled() const; /** Returns the default VOX level [0-10], 0=disabled. */ unsigned vox() const; /** Sets the default VOX level [0-10], 0=disabled. */ void setVOX(unsigned level); /** Disables VOX by default. */ void disableVOX(); /** Returns @c true if the transmit timeout (TOT) is disabled. */ bool totDisabled() const; /** Returns the default transmit timeout (TOT) in seconds, 0=disabled. */ unsigned tot() const; /** Sets the default transmit timeout (TOT) in seconds, 0=disabled. */ void setTOT(unsigned sec); /** Disables the transmit timeout (TOT). */ void disableTOT(); /** Retruns a reference to the default DMR radio Id. */ DMRRadioIDReference *defaultIdRef() const; /** Returns the default DMR ID or nullptr, if non is set. */ DMRRadioID *defaultId() const; /** Sets the default DMR ID. */ void setDefaultId(DMRRadioID *id); /** Returns the TyT device specific radio settings. */ TyTSettingsExtension *tytExtension() const; /** Sets the TyT device specific radio settings. */ void setTyTExtension(TyTSettingsExtension *ext); /** Returns the Radioddity device specific radio settings. */ RadiodditySettingsExtension *radioddityExtension() const; /** Sets the Radioddity device specific radio settings. */ void setRadioddityExtension(RadiodditySettingsExtension *ext); /** Returns the AnyTone device specific radio settings. */ AnytoneSettingsExtension *anytoneExtension() const; /** Sets the AnyTone device specific radio settings. */ void setAnytoneExtension(AnytoneSettingsExtension *ext); protected slots: /** Internal used callback whenever an extension is modified. */ void onExtensionModified(); protected: /** Holds the first intro line. */ QString _introLine1; /** Holds the second intro line. */ QString _introLine2; /** Holds the mic amplification level. */ unsigned _micLevel; /** Holds the speech synthesis flag. */ bool _speech; /** Holds the global squelch setting. */ unsigned _squelch; /** Holds the global power setting. */ Channel::Power _power; /** Holds the global VOX level. */ unsigned _vox; /** Holds the global transmit timeout. */ unsigned _transmitTimeOut; /** Reference to the default DMR radio ID. */ DMRRadioIDReference *_defaultId; /** Device specific settings extension for TyT devices. */ TyTSettingsExtension *_tytExtension; /** Device specific settings extension for Radioddity devices. */ RadiodditySettingsExtension *_radioddityExtension; /** Device specific settings extension for AnyTone devices. */ AnytoneSettingsExtension *_anytoneExtension; }; #endif // RADIOCONFIG_HH qdmr-0.12.3/lib/ranges.cc000066400000000000000000000000251501654372000150740ustar00rootroot00000000000000#include "ranges.hh" qdmr-0.12.3/lib/ranges.hh000066400000000000000000000017111501654372000151110ustar00rootroot00000000000000#ifndef RANGES_HH #define RANGES_HH #include #include "interval.hh" #include "frequency.hh" /** Simple range class representing some range in some data type. Provides some methods to limit * values to these ranges. * * @ingroup utils */ template class Range { public: /** Maps a given value onto the range. */ inline T map(const T &n) const { if (n < lower) return lower; if (upper < n) return upper; return n; } /** Checks, if the given value lays within the range. */ inline bool contains(const T &n) const { return ((lower IntRange; /** A time range. */ typedef Range TimeRange; /** A frequency range. */ typedef Range FrequencyRange; #endif // RANGES_HH qdmr-0.12.3/lib/rd5r.cc000066400000000000000000000014321501654372000144740ustar00rootroot00000000000000#include "rd5r.hh" #include "config.hh" #include "radiolimits.hh" #include "rd5r_limits.hh" #define BSIZE 128 RadioLimits *RD5R::_limits = nullptr; RD5R::RD5R(RadioddityInterface *device, QObject *parent) : RadioddityRadio(device, parent), _name("Baofeng/Radioddity RD-5R"), _codeplug() { // pass... } RD5R::~RD5R() { // pass... } const QString & RD5R::name() const { return _name; } const RadioLimits & RD5R::limits() const { if (nullptr == _limits) _limits = new RD5RLimits(); return *_limits; } const Codeplug & RD5R::codeplug() const { return _codeplug; } Codeplug & RD5R::codeplug() { return _codeplug; } RadioInfo RD5R::defaultRadioInfo() { return RadioInfo( RadioInfo::RD5R, "rd5r", "RD-5R", "Radioddity", RadioddityInterface::interfaceInfo()); } qdmr-0.12.3/lib/rd5r.hh000066400000000000000000000047331501654372000145150ustar00rootroot00000000000000/** @defgroup rd5r Baofeng/Radioddity RD-5R * Device specific classes for Baofeng/Radioddity RD-5R. * * \image html rd5r.jpg "RD-5R" width=200px * \image latex rd5r.jpg "RD-5R" width=200px * * The Baofeng/Radioddity RD-5R radio is likely the cheapest fully DMR compatiple (Tier I&II) VHF/UHF * radio on the market. Consequently, it is quiet popular and is usually the first DMR radio * many operators may buy. In my opinion it is a decent radio with reasonable sensitivity and audio * quality (for a handheld). However, the receiver frontend seems to be identical to the analog * Baofeng handhelds and thus suffers from the same well known issue of blocking whenever a strong * signal is nearby (even across bands). But you get a lot for a $70 radio. * * It features up to 1024 channels organized in 250 zones, where each zone may contain up to * 16 channels. The radio is a dual VFO and each VFO might be assigned to a different zone. Hence, a * zone is just a single list of up to 16 channels (in contrast to many other radios where a zone * contains two lists of channels for each VFO). * * The radio can also hold up to 255 contacts (actually 256, but due to a bug in the firmware RX is * disabled whenever all 256 contacts are set), 64 RX group lists and 250 scanlists. * * @ingroup radioddity */ #ifndef RD5R_HH #define RD5R_HH #include "radioddity_radio.hh" #include "rd5r_codeplug.hh" /** Implements an interface to the Baofeng/Radioddity RD-5R VHF/UHF 5W DMR (Tier I/II) radio. * * The Baofeng/Radioddity RD-5R radio uses a weird HID (human-interface device, see @c HID and * @c HIDevice) protocol for communication. This class implements the communication details with * the radio to read and write codeplugs on the device. * * @ingroup rd5r */ class RD5R: public RadioddityRadio { Q_OBJECT public: /** Constructor. * Do not call this constructor directly. Consider using the factory method * @c Radio::detect. */ RD5R(RadioddityInterface *device=nullptr, QObject *parent=nullptr); virtual ~RD5R(); const QString &name() const; const RadioLimits &limits() const; const Codeplug &codeplug() const; Codeplug &codeplug(); /** Returns the default info about the radio. */ static RadioInfo defaultRadioInfo(); private: /** Device identifier string. */ QString _name; /** Current device specific codeplug. */ RD5RCodeplug _codeplug; private: /** Holds the singleton instance of the radio limits. */ static RadioLimits *_limits; }; #endif // RD5R_HH qdmr-0.12.3/lib/rd5r_codeplug.cc000066400000000000000000000464361501654372000163730ustar00rootroot00000000000000#include "rd5r_codeplug.hh" #include "config.hh" #include "channel.hh" #include "utils.hh" #include "logger.hh" #include /* ******************************************************************************************** * * Implementation of RD5RCodeplug::ChannelElement * ******************************************************************************************** */ RD5RCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size) : RadioddityCodeplug::ChannelElement(ptr, size) { // pass... } RD5RCodeplug::ChannelElement::ChannelElement(uint8_t *ptr) : RadioddityCodeplug::ChannelElement(ptr) { // pass... } void RD5RCodeplug::ChannelElement::clear() { RadioddityCodeplug::ChannelElement::clear(); setSquelch(0); } unsigned RD5RCodeplug::ChannelElement::squelch() const { return getUInt8(Offset::squelch()); } void RD5RCodeplug::ChannelElement::setSquelch(unsigned level) { level = std::min(9u, level); setUInt8(Offset::squelch(), level); } bool RD5RCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx, const ErrorStack& err) { if (! RadioddityCodeplug::ChannelElement::fromChannelObj(c, ctx, err)) return false; if (c->is()) { const FMChannel *ac = c->as(); if (ac->defaultSquelch()) setSquelch(ctx.config()->settings()->squelch()); else if (ac->squelchDisabled()) setSquelch(0); else setSquelch(ac->squelch()); } else { // If digital channel, reuse global quelch setting setSquelch(ctx.config()->settings()->squelch()); } return true; } Channel * RD5RCodeplug::ChannelElement::toChannelObj(Context &ctx, const ErrorStack& err) const { Channel *ch = RadioddityCodeplug::ChannelElement::toChannelObj(ctx, err); if (nullptr == ch) return nullptr; if (ch->is()) { FMChannel *ac = ch->as(); ac->setSquelch(squelch()); } return ch; } bool RD5RCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx, const ErrorStack& err) const { if (! RadioddityCodeplug::ChannelElement::linkChannelObj(c, ctx, err)) return false; /* if (c->is()) { AnalogChannel *ac = c->as(); if (ctx.config()->settings()->squelch() == ac->squelch()) { ac->setSquelchDefault(); } } */ return true; } /* ********************************************************************************************* * * Implementation of RD5RCodeplug::TimestampElement * ********************************************************************************************* */ RD5RCodeplug::TimestampElement::TimestampElement(uint8_t *ptr, unsigned size) : Element(ptr, size) { // pass... } RD5RCodeplug::TimestampElement::TimestampElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } RD5RCodeplug::TimestampElement::~TimestampElement() { // pass... } void RD5RCodeplug::TimestampElement::clear() { set(); } QDateTime RD5RCodeplug::TimestampElement::get() const { return QDateTime(QDate(getBCD4_be(Offset::year()), getBCD2(Offset::month()), getBCD2(Offset::day())), QTime(getBCD2(Offset::hour()), getBCD2(Offset::minute()))); } void RD5RCodeplug::TimestampElement::set(const QDateTime &ts) { setBCD4_be(Offset::year(), ts.date().year()); setBCD2(Offset::month(), ts.date().month()); setBCD2(Offset::day(), ts.date().day()); setBCD2(Offset::hour(), ts.time().hour()); setBCD2(Offset::minute(), ts.time().minute()); } /* ******************************************************************************************** * * Implementation of RD5RCodeplug::EncryptionElement * ******************************************************************************************** */ RD5RCodeplug::EncryptionElement::EncryptionElement(uint8_t *ptr) : RadioddityCodeplug::EncryptionElement(ptr) { // pass... } bool RD5RCodeplug::EncryptionElement::isBasicKeySet(unsigned n) const { if (n>0) return false; return RadioddityCodeplug::EncryptionElement::isBasicKeySet(n); } QByteArray RD5RCodeplug::EncryptionElement::basicKey(unsigned n) const { if (n>0) return QByteArray(); return QByteArray("\x53\x47\x4c\x39"); } void RD5RCodeplug::EncryptionElement::setBasicKey(unsigned n, const QByteArray &key) { if ((0 != n) || (key != "\x53\x47\x4c\x39")){ logError() << "The RD5R only supports a single fixed DMR basic key '53474c39'."; return; } RD5RCodeplug::EncryptionElement::setBasicKey(n, key); } /* ******************************************************************************************** * * Implementation of RD5RCodeplug * ******************************************************************************************** */ RD5RCodeplug::RD5RCodeplug(QObject *parent) : RadioddityCodeplug(parent) { addImage("Radioddity RD5R Codeplug"); image(0).addElement(0x00080, 0x07b80); image(0).addElement(0x08000, 0x16300); } void RD5RCodeplug::clear() { RadioddityCodeplug::clear(); this->clearTimestamp(); } bool RD5RCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) { if (! RadioddityCodeplug::encodeElements(flags, ctx, err)) return false; // Set timestamp if (! this->encodeTimestamp(err)) { errMsg(err) << "Cannot encode time-stamp."; return false; } return true; } bool RD5RCodeplug::decodeElements(Context &ctx, const ErrorStack &err) { if (! RadioddityCodeplug::decodeElements(ctx, err)) return false; return true; } void RD5RCodeplug::clearTimestamp() { encodeTimestamp(); } bool RD5RCodeplug::encodeTimestamp(const ErrorStack &err) { Q_UNUSED(err) TimestampElement(data(Offset::timestamp())).set(); return true; } void RD5RCodeplug::clearGeneralSettings() { TimestampElement(data(Offset::settings())).clear(); } bool RD5RCodeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(err) GeneralSettingsElement el(data(Offset::settings())); if (! flags.updateCodePlug) el.clear(); return el.fromConfig(ctx, err); } bool RD5RCodeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) { return GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx, err); } void RD5RCodeplug::clearButtonSettings() { ButtonSettingsElement(data(Offset::buttons())).clear(); } bool RD5RCodeplug::encodeButtonSettings(Context &ctx, const Flags &flags, const ErrorStack &err) { Q_UNUSED(flags); return ButtonSettingsElement(data(Offset::buttons())).encode(ctx, err); } bool RD5RCodeplug::decodeButtonSettings(Context &ctx, const ErrorStack &err) { return ButtonSettingsElement(data(Offset::buttons())).decode(ctx, err); } void RD5RCodeplug::clearMessages() { MessageBankElement(data(Offset::messages())).clear(); } bool RD5RCodeplug::encodeMessages(Context &ctx, const Flags &flags, const ErrorStack &err) { if (! MessageBankElement(data(Offset::messages())).encode(ctx, flags, err)) { errMsg(err) << "Cannot encode preset messages."; return false; } return true; } bool RD5RCodeplug::decodeMessages(Context &ctx, const ErrorStack &err) { if (! MessageBankElement(data(Offset::messages())).decode(ctx, err)) { errMsg(err) << "Cannot decode preset messages."; return false; } return true; } void RD5RCodeplug::clearContacts() { for (unsigned int i=0; i= (unsigned int) ctx.count()) continue; if (! el.fromContactObj(ctx.get(i+1), ctx, err)) { errMsg(err) << "Cannot encode contact '" << ctx.get(i+1)->name() << "' at index " << i+1 << "."; return false; } } return true; } bool RD5RCodeplug::createContacts(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) /* Unpack Contacts */ for (unsigned int i=0; icontacts()->add(cont); } return true; } void RD5RCodeplug::clearDTMFContacts() { for (unsigned int i=0; i= (unsigned int) ctx.count()) continue; if (! el.fromContactObj(ctx.get(i+1), ctx, err)) { errMsg(err) << "Cannot encode DTMF contact '" << ctx.get(i+1)->name() << "' at index " << i+1 << "."; return false; } } return true; } bool RD5RCodeplug::createDTMFContacts(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) for (unsigned int i=0; icontacts()->add(cont); } return true; } void RD5RCodeplug::clearChannels() { for (unsigned int b=0,c=0; b()) { if (! el.fromChannelObj(ctx.get(c+1), ctx, err)) { errMsg(err) << "Cannot encode channel " << c+1 << " (" << i << " of bank " << b <<")."; return false; } bank.enable(i,true); } else { el.clear(); bank.enable(i, false); } } } return true; } bool RD5RCodeplug::createChannels(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) for (unsigned int b=0,c=0; bchannelList()->add(ch); ctx.add(ch, c+1); } } return true; } bool RD5RCodeplug::linkChannels(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) for (unsigned int b=0,c=0; b(c+1), ctx, err)) return false; } } return true; } void RD5RCodeplug::clearBootSettings() { BootSettingsElement(data(Offset::bootSettings())).clear(); } void RD5RCodeplug::clearMenuSettings() { MenuSettingsElement(data(Offset::menuSettings())).clear(); } void RD5RCodeplug::clearBootText() { BootTextElement(data(Offset::bootText())).clear(); } bool RD5RCodeplug::encodeBootText(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); BootTextElement(data(Offset::bootText())).fromConfig(ctx, err); return true; } bool RD5RCodeplug::decodeBootText(Context &ctx, const ErrorStack &err) { BootTextElement(data(Offset::bootText())).updateConfig(ctx, err); return true; } void RD5RCodeplug::clearVFOSettings() { ChannelElement(data(Offset::vfoA())).clear(); ChannelElement(data(Offset::vfoB())).clear(); } void RD5RCodeplug::clearZones() { ZoneBankElement bank(data(Offset::zoneBank())); bank.clear(); for (unsigned int i=0; i(i+1)) { bank.enable(i, false); continue; } // Construct from Zone obj z.fromZoneObjA(ctx.get(i+1), ctx, err); bank.enable(i, true); } return true; } bool RD5RCodeplug::createZones(Context &ctx, const ErrorStack &err) { Q_UNUSED(err) ZoneBankElement bank(data(Offset::zoneBank())); for (unsigned int i=0; izones()->add(zone); ctx.add(zone, i+1); } return true; } bool RD5RCodeplug::linkZones(Context &ctx, const ErrorStack &err) { ZoneBankElement bank(data(Offset::zoneBank())); for (unsigned int i=0; i(i+1); if (! z.linkZoneObj(zone, ctx)) { errMsg(err) << "Cannot link zone at index " << i << "."; return false; } } return true; } void RD5RCodeplug::clearScanLists() { ScanListBankElement bank(data(Offset::scanListBank())); bank.clear(); for (unsigned int i=0; i= ctx.count()) { bank.enable(i, false); continue; } if (! ScanListElement(bank.get(i)).fromScanListObj(ctx.get(i+1), ctx, err)) { errMsg(err) << "Cannot encode scan list at index " << i << "."; return false; } bank.enable(i, true); } return true; } bool RD5RCodeplug::createScanLists(Context &ctx, const ErrorStack &err) { ScanListBankElement bank(data(Offset::scanListBank())); for (unsigned int i=0; iscanlists()->add(scan); ctx.add(scan, i+1); } return true; } bool RD5RCodeplug::linkScanLists(Context &ctx, const ErrorStack &err) { ScanListBankElement bank(data(Offset::scanListBank())); for (unsigned int i=0; i(i+1), ctx, err)) { errMsg(err) << "Cannot link scan list '" << ctx.get(i+1) << "' at index " << i+1 << "."; return false; } } return true; } void RD5RCodeplug::clearGroupLists() { GroupListBankElement bank(data(Offset::groupListBank())); bank.clear(); for (unsigned int i=0; i= ctx.count()) continue; GroupListElement el(bank.get(i)); el.fromRXGroupListObj(ctx.get(i+1), ctx, err); // Only group calls are encoded int count = 0; for (int j=0; j(i+1)->count(); j++) if (DMRContact::GroupCall == ctx.get(i+1)->contact(j)->type()) count++; bank.setContactCount(i, count); } return true; } bool RD5RCodeplug::createGroupLists(Context &ctx, const ErrorStack &err) { GroupListBankElement bank(data(Offset::groupListBank())); for (unsigned int i=0; irxGroupLists()->add(list); ctx.add(list, i+1); } return true; } bool RD5RCodeplug::linkGroupLists(Context &ctx, const ErrorStack &err) { GroupListBankElement bank(data(Offset::groupListBank())); for (unsigned int i=0; i(i+1)->name() << "'.";*/ if (! el.linkRXGroupListObj(bank.contactCount(i), ctx.get(i+1), ctx, err)) { errMsg(err) << "Cannot link group list '" << ctx.get(i+1)->name() << "'."; return false; } } return true; } void RD5RCodeplug::clearEncryption() { EncryptionElement enc(data(Offset::encryption())); enc.clear(); } bool RD5RCodeplug::encodeEncryption(const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); clearEncryption(); EncryptionElement enc(data(Offset::encryption())); return enc.fromCommercialExt(ctx.config()->commercialExtension(), ctx, err); } bool RD5RCodeplug::createEncryption(Context &ctx, const ErrorStack &err) { Q_UNUSED(err); EncryptionElement enc(data(Offset::encryption())); if (EncryptionElement::PrivacyType::None == enc.privacyType()) return true; return enc.updateCommercialExt(ctx, err); } bool RD5RCodeplug::linkEncryption(Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); Q_UNUSED(err); return true; } qdmr-0.12.3/lib/rd5r_codeplug.hh000066400000000000000000000325611501654372000163770ustar00rootroot00000000000000#ifndef RD5R_CODEPLUG_HH #define RD5R_CODEPLUG_HH #include #include "radioddity_codeplug.hh" #include "signaling.hh" class Channel; /** Represents, encodes and decodes the device specific codeplug for a Baofeng/Radioddity RD-5R. * * This codeplug format is quiet funny. It reveals some history of this device. First of all, the * channels are organizes in two blocks. The first block contains only a single bank of 128 channels, * while the second block contains 7 banks with a total of 896 channels. I would guess there was a * previous firmware or even hardware version with only 128 channels. * * Moreover, channels, zones, rx group lists and scan lists are organized in tables or banks, with * some preceding bitfield indicating which channel is enabled/valid. Contacts, however, are just * organized in a list, where each entry has a field, indicating whether that contact is valid. * * This difference looks like, as if the firmware code for the contacts stems from a different * device or was developed by a different engineer. Moreover, the message list again, uses yet * another method. Here a simple counter precedes the messages, indicating how many valid messages * there are. All in all, a rather inconsistent way of representing variable length lists in the * codeplug. I would guess, that over time, different people/teams worked on different revisions * of the firmware. It must have been a real nightmare to Serge Vakulenko reverse-engineering this * codeplug. * * @section rd5rcpl Codeplug structure within radio * This implementation targets firmware version 2.1.6. * * The memory representation of the codeplug within the radio is divided into two segments. * The first segment starts at the address 0x00080 and ends at 0x07c00 while the second section * starts at 0x08000 and ends at 0x1e300. * * Please note, that the codeplug is not yet fully understood and a full codeplug cannot be build * from scratch. That is, it is necessary to update an existing codeplug on the radio. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Start End Size Content
      First segment 0x00080-0x07c00
      0x00080 0x00088 0x0008 ??? Unknown ???
      0x00088 0x0008e 0x0006 Timestamp, see @c RadioddityCodeplug::TimestampElement.
      0x0008e 0x000e0 0x0052 CPS, firmware, DSP version numbers (not touched).
      0x000e0 0x00108 0x0028 General settings, see @c RadioddityCodeplug::GeneralSettingsElement.
      0x00108 0x00128 0x0020 Button settings, see @c RadioddityCodeplug::ButtonSettingsElement.
      0x00128 0x01370 0x1248 32 preset message texts, see @c RadioddityCodeplug::MessageBankElement.
      0x01370 0x01588 0x0218 ??? Unknown ???
      0x01588 0x01788 0x0200 ??? 32 Emergency systems ???
      0x01788 0x02f88 0x1800 256 contacts, see @c RadioddityCodeplug::ContactElement.
      0x02f88 0x03388 0x0400 32 DTMF contacts, see @c RadioddityCodeplug::DTMFContactElement.
      0x03388 0x03780 0x03f8 ??? Unknown ???
      0x03780 0x05390 0x1c10 First 128 channels (bank 0), see @c RadioddityCodeplug::ChannelBankElement * and @c RD5RCodeplug::ChannelElement.
      0x05390 0x07518 0x2188 ??? Unknown ???
      0x07518 0x07538 0x0020 Boot settings, see @c RadioddityCodeplug::BootSettingsElement.
      0x07538 0x07540 0x0008 Menu settings, see @c RadioddityCodeplug::MenuSettingsElement.
      0x07540 0x07560 0x0020 2 intro lines, @c RadioddityCodeplug::BootTextElement.
      0x07560 0x07590 0x0030 ??? Unknown ???
      0x07590 0x075c8 0x0038 VFO A settings @c RadioddityCodeplug::ChannelElement
      0x075c8 0x07600 0x0038 VFO B settings @c RadioddityCodeplug::ChannelElement
      0x07600 0x07c00 0x0600 ??? Unknown ???
      Second segment 0x08000-0x1e300
      0x08000 0x08010 0x0010 ??? Unknown ???
      0x08010 0x0af10 0x2f00 250 zones, see @c RadioddityCodeplug::ZoneBankElement.
      0x0af10 0x0b1b0 0x02a0 ??? Unknown ???
      0x0b1b0 0x17620 0xc470 Remaining 896 channels (bank 1-7), see @c RadioddityCodeplug::ChannelBankElement * and @c RD5RCodeplug::ChannelElement.
      0x17620 0x1cd10 0x56f0 250 scan lists, see @c RadioddityCodeplug::ScanListBankElement
      0x1cd10 0x1d620 0x0910 ??? Unknown ???
      0x1d620 0x1e2a0 0x0c80 64 RX group lists, see @c RadioddityCodeplug::GroupListBankElement
      0x1e2a0 0x1e300 0x0060 ??? Unknown ???
      * * @ingroup rd5r */ class RD5RCodeplug : public RadioddityCodeplug { Q_OBJECT public: /** Implements the specialization of the Radioddity channel for the RD5R radio. * * Memory layout of encoded channel: * @verbinclude rd5r_channel.txt */ class ChannelElement: public RadioddityCodeplug::ChannelElement { protected: /** Hidden constructor. */ ChannelElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit ChannelElement(uint8_t *ptr); void clear(); /** Returns the squelch level. */ virtual unsigned squelch() const; /** Sets the squelch level. */ virtual void setSquelch(unsigned level); bool fromChannelObj(const Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()); Channel *toChannelObj(Context &ctx, const ErrorStack &err=ErrorStack()) const; bool linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()) const; protected: /** Internal offsets within the channel element. */ struct Offset: RadioddityCodeplug::ChannelElement::Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int squelch() { return 0x0037; } /// @endcond }; }; /** Implements the timestamp for RD-5R codeplugs. * * Encoding of messages (size: 0x0006b): * @verbinclude rd5r_timestamp.txt */ class TimestampElement: Element { protected: /** Hidden constructor. */ TimestampElement(uint8_t *ptr, unsigned size); public: /** Constructor. */ explicit TimestampElement(uint8_t *ptr); /** Destructor. */ virtual ~TimestampElement(); /** The size of the element. */ static constexpr unsigned int size() { return 0x0006; } /** Resets the timestamp. */ void clear(); /** Returns the time stamp. */ virtual QDateTime get() const; /** Sets the time stamp. */ virtual void set(const QDateTime &ts=QDateTime::currentDateTime()); protected: /** Internal offsets within the element. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int year() { return 0x0000; } static constexpr unsigned int month() { return 0x0002; } static constexpr unsigned int day() { return 0x0003; } static constexpr unsigned int hour() { return 0x0004; } static constexpr unsigned int minute() { return 0x0005; } /// @endcond }; }; /** Implements the encoding/decoding of encryption keys for the RD-5R radio. * @note The RD5R only supports a single basic DMR encryption key with a fixed value! * * Encoding of encryption keys (size: 0x00088): * @verbinclude radioddity_privacy.txt */ class EncryptionElement: public RadioddityCodeplug::EncryptionElement { public: /** Constructor. */ EncryptionElement(uint8_t *ptr); bool isBasicKeySet(unsigned n) const; QByteArray basicKey(unsigned n) const; void setBasicKey(unsigned n, const QByteArray &key); }; public: /** Empty constructor. */ RD5RCodeplug(QObject *parent=0); void clear(); public: bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack()); /** Clears the time-stamp in the codeplug. */ virtual void clearTimestamp(); /** Sets the time-stamp. */ virtual bool encodeTimestamp(const ErrorStack &err=ErrorStack()); void clearGeneralSettings(); bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()); void clearButtonSettings(); bool encodeButtonSettings(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack()); bool decodeButtonSettings(Context &ctx, const ErrorStack &err=ErrorStack()); void clearMessages(); bool encodeMessages(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack()); bool decodeMessages(Context &ctx, const ErrorStack &err=ErrorStack()); void clearContacts(); bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack()); void clearDTMFContacts(); bool encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack()); void clearChannels(); bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()); void clearBootSettings(); void clearMenuSettings(); void clearBootText(); bool encodeBootText(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeBootText(Context &ctx, const ErrorStack &err=ErrorStack()); void clearVFOSettings(); void clearZones(); bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createZones(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()); void clearScanLists(); bool encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack()); void clearGroupLists(); bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); void clearEncryption(); bool encodeEncryption(const Flags &flags, Context &ctx, const ErrorStack &err); bool createEncryption(Context &ctx, const ErrorStack &err); bool linkEncryption(Context &ctx, const ErrorStack &err); public: /** Some limits for the codeplug. */ struct Limit { static constexpr unsigned int channelBankCount() { return 8; } ///< The number of channel banks. static constexpr unsigned int channelCount() { return 1024; } ///< Maximum number of channels in the codeplug. static constexpr unsigned int contactCount() { return 256; } ///< Maximum number of DMR contacts. static constexpr unsigned int dtmfContactCount() { return 32; } ///< Maximum number of DTMF contacts. static constexpr unsigned int zoneCount() { return 250; } ///< Maximum number of zones. }; protected: /** Some internal offsets within the codeplug. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int timestamp() { return 0x000088; } static constexpr unsigned int settings() { return 0x0000e0; } static constexpr unsigned int buttons() { return 0x000108; } static constexpr unsigned int messages() { return 0x000128; } static constexpr unsigned int encryption() { return 0x001370; } static constexpr unsigned int contacts() { return 0x001788; } static constexpr unsigned int dtmfContacts() { return 0x002f88; } static constexpr unsigned int channelBank0() { return 0x003780; } static constexpr unsigned int bootSettings() { return 0x007518; } static constexpr unsigned int menuSettings() { return 0x007538; } static constexpr unsigned int bootText() { return 0x007540; } static constexpr unsigned int vfoA() { return 0x007590; } static constexpr unsigned int vfoB() { return 0x0075c8; } static constexpr unsigned int zoneBank() { return 0x008010; } static constexpr unsigned int channelBank1() { return 0x00b1b0; } static constexpr unsigned int scanListBank() { return 0x017620; } static constexpr unsigned int groupListBank() { return 0x01d620; } /// @endcond }; }; #endif // RD5R_CODEPLUG_HH qdmr-0.12.3/lib/rd5r_filereader.cc000066400000000000000000000037401501654372000166620ustar00rootroot00000000000000#include "rd5r_filereader.hh" #include #include #define SEGMENT0_ADDR 0x00000080 #define SEGMENT0_SIZE 0x00007b80 #define SEGMENT1_ADDR 0x00008000 #define SEGMENT1_SIZE 0x00016300 bool RD5RFileReader::read(const QString &filename, RD5RCodeplug *codeplug, const ErrorStack &err) { // Check file properties QFileInfo info(filename); if (! info.exists()) { errMsg(err) << "Cannot open file '" << filename << "': File does not exisist."; return false; } if (131072 != info.size()) { errMsg(err) << "Cannot read codeplug file '" << filename << "': File size is not 131072 bytes."; return false; } // Open file QFile file(filename); if (! file.open(QFile::ReadOnly)) { errMsg(err) << "Cannot open file '" << filename << "': " << file.errorString() << "."; return false; } // Read file content if (! file.seek(SEGMENT0_ADDR)) { errMsg(err) << "Cannot read codeplug file '" << filename << "': Cannot seek within file: " << file.errorString() << "."; file.close(); return false; } char *ptr = (char *)codeplug->data(SEGMENT0_ADDR); size_t n = SEGMENT0_SIZE; while (0 < n) { int nread = file.read(ptr, n); if (0 > nread) { errMsg(err) << "Cannot read codeplug file '" << filename << "': " << file.errorString() << "."; file.close(); return false; } n -= nread; ptr += nread; } if (! file.seek(SEGMENT1_ADDR)) { errMsg(err) << "Cannot read codeplug file '" << filename << "': Cannot seek within file: " << file.errorString() << "."; file.close(); return false; } ptr = (char *)codeplug->data(SEGMENT1_ADDR); n = SEGMENT1_SIZE; while (0 < n) { int nread = file.read(ptr, n); if (0 > nread) { errMsg(err) << "Cannot read codeplug file '" << filename << "': " << file.errorString() << "."; file.close(); return false; } n -= nread; ptr += nread; } return true; } qdmr-0.12.3/lib/rd5r_filereader.hh000066400000000000000000000021571501654372000166750ustar00rootroot00000000000000#ifndef RD5RFILEREADER_HH #define RD5RFILEREADER_HH #include "rd5r_codeplug.hh" /** Methods to read manufacturer codeplug files. * * The file format of the stock CPS is pretty simple. It is a one-to-one dump of the codeplug * data as written to the device. This makes the decoding of the manufacturer codeplug files very * easy. Some memory regions, however, are not written to the deivice although they are present in * the codeplug file. * * * * * *
      Start End Size
      0x00080 0x07c00 0x07b80
      0x08000 0x1e300 0x16300
      * * @ingroup rd5r */ class RD5RFileReader { public: /** Reads manufacturer codeplug file into given codeplug object. * @param filename Specifies the file to read. * @param codeplug Specifies the codeplug object to store read codeplug. * @param err Error stack. * @returns @c true on success and @c false on error. */ static bool read(const QString &filename, RD5RCodeplug *codeplug, const ErrorStack &err=ErrorStack()); }; #endif // RD5RFILEREADER_HH qdmr-0.12.3/lib/rd5r_limits.cc000066400000000000000000000203371501654372000160620ustar00rootroot00000000000000#include "rd5r_limits.hh" #include "rd5r_codeplug.hh" #include "radioid.hh" #include "channel.hh" #include "scanlist.hh" #include "zone.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "roamingzone.hh" RD5RLimits::RD5RLimits(QObject *parent) : RadioLimits(false, parent) { // Define limits for call-sign DB _hasCallSignDB = false; _callSignDBImplemented = false; _numCallSignDBEntries = 0; // Define limits for satellite config _hasSatelliteConfig = false; _satelliteConfigImplemented = false; _numSatellites = 0; /* Define limits for the general settings. */ add("settings", new RadioLimitItem{ { "introLine1", new RadioLimitString(-1, 16, RadioLimitString::ASCII) }, { "introLine2", new RadioLimitString(-1, 16, RadioLimitString::ASCII) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, 3825) } }); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList { { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject { {"name", new RadioLimitString(1,8, RadioLimitString::ASCII) }, {"id", new RadioLimitUInt(0, 16777215)} } } /// @todo check default radio ID. }); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, 256, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum{ (unsigned)DMRContact::PrivateCall, (unsigned)DMRContact::GroupCall, (unsigned)DMRContact::AllCall }}, { "number", new RadioLimitUInt(0, 16777215) } } }, { DTMFContact::staticMetaObject, 0, 8, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "ring", new RadioLimitBool() }, { "number", new RadioLimitStringRegEx("^[0-9A-Fa-f]+$") } } } }); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 1, 64, new RadioLimitObject { { "name", new RadioLimitString(1,16, RadioLimitString::ASCII) }, { "contacts", new RadioLimitGroupCallRefList(1,16) } })); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, 1024, // < up to 1024 channels new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, 3825, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRefIgnored()} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1,16, RadioLimitString::ASCII)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, 3825, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, true)}, {"aprs", new RadioLimitObjRefIgnored() }, {"roaming", new RadioLimitObjRefIgnored(DefaultRoamingZone::get()) }, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } )); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone( 16, { { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions }) ) ); /* Define limits for scan lists. */ add("scanlists", new RadioLimitList( ScanList::staticMetaObject, 0, 250, new RadioLimitObject{ { "name", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, { "primary", new RadioLimitObjRef(Channel::staticMetaObject, false) }, { "secondary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "revert", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "channels", new RadioLimitRefList(0, 31, Channel::staticMetaObject) } })); /* Check encryption keys. */ add("commercial", new RadioLimitItem { {"encryptionKeys", new RadioLimitList( BasicEncryptionKey::staticMetaObject, 0, RadioddityCodeplug::EncryptionElement::Limit::keyCount(), new RadioLimitObject { {"name", new RadioLimitIgnored()}, {"key", new RadioLimitStringRegEx("[0-9a-fA-F]{8}")} })} }); /* Ignore positioning systems. */ add("positioning", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint) ) ); /* Ignore roaming zones. */ add("roaming", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint) ) ); } qdmr-0.12.3/lib/rd5r_limits.hh000066400000000000000000000004741501654372000160740ustar00rootroot00000000000000#ifndef RD5RLIMITS_HH #define RD5RLIMITS_HH #include "radiolimits.hh" /** Implements the configuration limits for the Radioddity RD-5R. * @ingroup rd5r */ class RD5RLimits : public RadioLimits { Q_OBJECT public: /** Constructor. */ explicit RD5RLimits(QObject *parent=nullptr); }; #endif // RD5RLIMITS_HH qdmr-0.12.3/lib/roamingchannel.cc000066400000000000000000000150141501654372000166060ustar00rootroot00000000000000#include "roamingchannel.hh" #include "utils.hh" /* ********************************************************************************************* * * Implementation of RoamingChannel * ********************************************************************************************* */ RoamingChannel::RoamingChannel(QObject *parent) : ConfigObject(parent), _rxFrequency(Frequency::fromHz(0)), _txFrequency(Frequency::fromHz(0)), _overrideColorCode(false), _colorCode(0), _overrideTimeSlot(false), _timeSlot(DMRChannel::TimeSlot::TS1) { // pass... } RoamingChannel::RoamingChannel(const RoamingChannel &other, QObject *parent) : ConfigObject(parent) { copy(other); } void RoamingChannel::clear() { ConfigObject::clear(); _rxFrequency = _txFrequency = Frequency::fromHz(0); _overrideColorCode = false; _colorCode = 0; _overrideTimeSlot = false; _timeSlot = DMRChannel::TimeSlot::TS1; } ConfigItem * RoamingChannel::clone() const { RoamingChannel *c = new RoamingChannel(); if (! c->copy(*this)) { c->deleteLater(); return nullptr; } return c; } Frequency RoamingChannel::rxFrequency() const { return _rxFrequency; } void RoamingChannel::setRXFrequency(Frequency f) { if (f == _rxFrequency) return; _rxFrequency = f; emit modified(this); } Frequency RoamingChannel::txFrequency() const { return _txFrequency; } void RoamingChannel::setTXFrequency(Frequency f) { if (f == _txFrequency) return; _txFrequency = f; emit modified(this); } bool RoamingChannel::colorCodeOverridden() const { return _overrideColorCode; } void RoamingChannel::overrideColorCode(bool override) { if (override == _overrideColorCode) return; _overrideColorCode = override; emit modified(this); } unsigned int RoamingChannel::colorCode() const { return _colorCode; } void RoamingChannel::setColorCode(unsigned int cc) { cc = std::min(15U, cc); if (_colorCode == cc) return; _colorCode = cc; emit modified(this); } bool RoamingChannel::timeSlotOverridden() const { return _overrideTimeSlot; } void RoamingChannel::overrideTimeSlot(bool override) { if (override == _overrideTimeSlot) return; _overrideTimeSlot = override; emit modified(this); } DMRChannel::TimeSlot RoamingChannel::timeSlot() const { return _timeSlot; } void RoamingChannel::setTimeSlot(DMRChannel::TimeSlot ts) { if (_timeSlot == ts) return; _timeSlot = ts; emit modified(this); } RoamingChannel * RoamingChannel::fromDMRChannel(DMRChannel *ch, DMRChannel* ref) { RoamingChannel *rch = new RoamingChannel(); rch->setName(QString("R %1").arg(ch->name())); rch->setRXFrequency(ch->rxFrequency()); rch->setTXFrequency(ch->txFrequency()); rch->overrideColorCode(true); rch->setColorCode(ch->colorCode()); rch->overrideTimeSlot(true); rch->setTimeSlot(ch->timeSlot()); if (nullptr != ref) { if (ch->colorCode() == ref->colorCode()) rch->overrideColorCode(false); if (ch->timeSlot() == ref->timeSlot()) rch->overrideTimeSlot(false); } return rch; } bool RoamingChannel::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) { if (! ConfigObject::parse(node, ctx, err)) return false; setRXFrequency(node["rxFrequency"].as()); if (node["txFrequency"].IsNull()) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << "Cannot parse roaming channel. No txFrequency specified."; return false; } setTXFrequency(node["txFrequency"].as()); if (! node["timeSlot"]) { this->overrideTimeSlot(false); } else { if (! node["timeSlot"].IsScalar()) { errMsg(err) << node["timeSlot"].Mark().line << ":" << node["timeSlot"].Mark().column << "Cannot parse 'timeSlot' of RoamingChannel: time slot is not scalar."; return false; } QMetaEnum e = QMetaEnum::fromType(); std::string key = node["timeSlot"].as(); bool ok=true; int value = e.keyToValue(key.c_str(), &ok); if (! ok) { QStringList lst; for (int i=0; ioverrideTimeSlot(true); this->setTimeSlot((DMRChannel::TimeSlot)value); } if (! node["colorCode"]) { this->overrideTimeSlot(false); } else { if (! node["colorCode"].IsScalar()) { errMsg(err) << node["colorCode"].Mark().line << ":" << node["colorCode"].Mark().column << "Cannot parse 'colorCode' of RoamingChannel: color code is not scalar."; return false; } // finally set property this->overrideColorCode(true); this->setColorCode(node["colorCode"].as()); } return true; } bool RoamingChannel::populate(YAML::Node &node, const Context &context, const ErrorStack &err) { // First, populate scriptable properties if (! ConfigObject::populate(node, context, err)) return false; // Serialize frequency in MHz node["rxFrequency"] = _rxFrequency; node["txFrequency"] = _txFrequency; if (timeSlotOverridden()) { QMetaEnum e = QMetaEnum::fromType(); node["timeSlot"] = e.valueToKey((int)timeSlot()); } if (colorCodeOverridden()) { node["colorCode"] = colorCode(); } return true; } /* ********************************************************************************************* * * Implementation of RoamingChannelList * ********************************************************************************************* */ RoamingChannelList::RoamingChannelList(QObject *parent) : ConfigObjectList(RoamingChannel::staticMetaObject, parent) { // pass... } RoamingChannel * RoamingChannelList::channel(int idx) const { if (ConfigItem *obj = get(idx)) return obj->as(); return nullptr; } int RoamingChannelList::add(ConfigObject *obj, int row, bool unique) { if (obj && obj->is()) return ConfigObjectList::add(obj, row, unique); return -1; } ConfigItem * RoamingChannelList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) if (! node) return nullptr; if (! node.IsMap()) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create roaming zone: Expected object."; return nullptr; } return new RoamingChannel(); } qdmr-0.12.3/lib/roamingchannel.hh000066400000000000000000000106461501654372000166260ustar00rootroot00000000000000#ifndef ROAMINGCHANNEL_HH #define ROAMINGCHANNEL_HH #include "channel.hh" /** Represents a roaming channel. * * That is, an incomplete DMR channel, that overrides some channel * settings to allow for roaming between repeaters. To this end, the roaming channel may override * only those channel settings, that are repeater specific like frequencies and color-codes but * keeps DMR contact settings. Some of these properties are overridden optionally (time slot and * color code) while the RX/TX frequencies are overridden always. * * @ingroup config */ class RoamingChannel : public ConfigObject { Q_OBJECT Q_CLASSINFO("IdPrefix", "rch") /** Holds the RX frequency in Hz. */ Q_PROPERTY(Frequency rxFrequency READ rxFrequency WRITE setRXFrequency SCRIPTABLE false) /** Holds the TX frequency in Hz. */ Q_PROPERTY(Frequency txFrequency READ txFrequency WRITE setTXFrequency SCRIPTABLE false) /** If @c true, the color code of the channel gets overridden by the one specified in @c colorCode. */ Q_PROPERTY(bool overrideColorCode READ colorCodeOverridden WRITE overrideColorCode SCRIPTABLE false) /** If @c overrideColorCode is @c true, specifies the color code. */ Q_PROPERTY(unsigned int colorCode READ colorCode WRITE setColorCode SCRIPTABLE false) /** If @c true, the time slot of the channel gets overridden by the one specified in @c timeSlot. */ Q_PROPERTY(bool overrideTimeSlot READ timeSlotOverridden WRITE overrideTimeSlot SCRIPTABLE false) /** If @c overrideTimeSlot is @c true, specifies the time slot. */ Q_PROPERTY(DMRChannel::TimeSlot timeSlot READ timeSlot WRITE setTimeSlot SCRIPTABLE false) public: /** Default constructor for a roaming channel. */ Q_INVOKABLE explicit RoamingChannel(QObject *parent = nullptr); /** Copy constructor. */ RoamingChannel(const RoamingChannel &other, QObject *parent=nullptr); ConfigItem *clone() const; void clear(); /** Returns the RX frequency in Hz. */ Frequency rxFrequency() const; /** Sets the RX frquency in Hz. */ void setRXFrequency(Frequency f); /** Returns the TX frequency in Hz. */ Frequency txFrequency() const; /** Sets the TX frquency in Hz. */ void setTXFrequency(Frequency f); /** Returns @c true, if the color code of the channel gets overridden. */ bool colorCodeOverridden() const; /** Enables/disables overriding the color code of the channel. */ void overrideColorCode(bool override); /** Returns the color code. */ unsigned int colorCode() const; /** Sets the color code. */ void setColorCode(unsigned int cc); /** Returns @c true, if the time slot of the channel gets overridden. */ bool timeSlotOverridden() const; /** Enables/disables overriding the time slot of the channel. */ void overrideTimeSlot(bool override); /** Returns the time slot. */ DMRChannel::TimeSlot timeSlot() const; /** Sets the time slot. */ void setTimeSlot(DMRChannel::TimeSlot ts); bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err); public: /** Helper method to construct a Roaming channel from a given DMR channel. Optionally with * reference to a third one. */ static RoamingChannel *fromDMRChannel(DMRChannel *ch, DMRChannel *ref=nullptr); protected: bool populate(YAML::Node &node, const Context &context, const ErrorStack &err); protected: /** Holds the RX frequency in Hz. */ Frequency _rxFrequency; /** Holds the TX frequency in Hz. */ Frequency _txFrequency; /** If @c true, the color code of the channel gets overridden by the one specified in @c _colorCode. */ bool _overrideColorCode; /** If @c _overrideColorCode is @c true, specifies the color code. */ unsigned int _colorCode; /** If @c true, the time slot of the channel gets overridden by the one specified in @c _timeSlot. */ bool _overrideTimeSlot; /** If @c _overrideTimeSlot is @c true, specifies the time slot. */ DMRChannel::TimeSlot _timeSlot; }; /** Represents the list of roaming channels within the abstract device configuration. * * @ingroup config */ class RoamingChannelList: public ConfigObjectList { Q_OBJECT public: /** Constructor. */ explicit RoamingChannelList(QObject *parent=nullptr); /** Returns the roaming channel at the given index. */ RoamingChannel *channel(int idx) const; int add(ConfigObject *obj, int row=-1, bool unique=true); public: ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); }; #endif // ROAMINGCHANNEL_HH qdmr-0.12.3/lib/roamingzone.cc000066400000000000000000000131461501654372000161550ustar00rootroot00000000000000#include "roamingzone.hh" #include "channel.hh" #include #include "config.hh" /* ********************************************************************************************* * * Implementation of RoamingZone * ********************************************************************************************* */ RoamingZone::RoamingZone(QObject *parent) : ConfigObject("roam", parent), _channel() { // pass... } RoamingZone::RoamingZone(const QString &name, QObject *parent) : ConfigObject(name, parent), _channel() { // pass... } RoamingZone & RoamingZone::operator =(const RoamingZone &other) { copy(other); return *this; } ConfigItem * RoamingZone::clone() const { RoamingZone *z = new RoamingZone(); if (! z->copy(*this)) { z->deleteLater(); return nullptr; } return z; } int RoamingZone::count() const { return _channel.count(); } void RoamingZone::clear() { _channel.clear(); } bool RoamingZone::contains(const RoamingChannel *ch) const { for (int i=0; i= count())) return nullptr; return _channel.get(idx)->as(); } int RoamingZone::addChannel(RoamingChannel* ch, int row) { row = _channel.add(ch, row); if (0 > row) return row; emit modified(this); return row; } bool RoamingZone::remChannel(int row) { return _channel.del(_channel.get(row)); } bool RoamingZone::remChannel(RoamingChannel* ch) { return _channel.del(ch); } const RoamingChannelRefList * RoamingZone::channels() const { return &_channel; } RoamingChannelRefList* RoamingZone::channels() { return &_channel; } bool RoamingZone::link(const YAML::Node &node, const Context &ctx, const ErrorStack &err) { // First, run default link if (! ConfigObject::link(node, ctx, err)) return false; // Handle channel references separately if (! node["channels"]) return true; // check type if (! node["channels"].IsSequence()) { errMsg(err) << node["channels"].Mark().line << ":" << node["channels"].Mark().column << ": Cannot link 'channels' of 'RoamingZone': Expected sequence."; return false; } YAML::Node lst = node["channels"]; for (YAML::const_iterator it=lst.begin(); it!=lst.end(); it++) { if (! it->IsScalar()) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot link 'channels' of 'RoamingZone': Expected ID string."; return false; } QString id = QString::fromStdString(it->as()); if (! ctx.contains(id)) { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot link 'channels' of 'RoamingZone': Reference '" << id << "' not defined."; return false; } // Handle referenced object (either DMR channel or roaming channel) ConfigObject *obj = ctx.getObj(id); if (obj->is()) { RoamingChannel *rch = RoamingChannel::fromDMRChannel(obj->as()); config()->roamingChannels()->add(rch); addChannel(rch); } else if (obj->is()) { addChannel(obj->as()); } else { errMsg(err) << it->Mark().line << ":" << it->Mark().column << ": Cannot link 'channels' of 'RoamingZone': " << "Cannot add reference to '" << id << "' to list. " << "Not a roaming channel."; return false; } } return true; } bool RoamingZone::populate(YAML::Node &node, const Context &context, const ErrorStack &err) { if (! ConfigObject::populate(node, context, err)) return false; // Serialize list of channel references. for (int i=0; iname() << "': No ID assigned."; return false; } node["channels"].push_back(context.getId(channel(i)).toStdString()); } return true; } /* ********************************************************************************************* * * Implementation of DefaultRoamingZone * ********************************************************************************************* */ DefaultRoamingZone *DefaultRoamingZone::_instance = nullptr; DefaultRoamingZone::DefaultRoamingZone(QObject *parent) : RoamingZone(tr("[Default]"), parent) { // pass... } DefaultRoamingZone * DefaultRoamingZone::get() { if (nullptr == _instance) _instance = new DefaultRoamingZone(); return _instance; } /* ********************************************************************************************* * * Implementation of RoamingZoneList * ********************************************************************************************* */ RoamingZoneList::RoamingZoneList(QObject *parent) : ConfigObjectList(RoamingZone::staticMetaObject, parent) { // pass... } RoamingZone * RoamingZoneList::zone(int idx) const { if (ConfigItem *obj = get(idx)) return obj->as(); return nullptr; } int RoamingZoneList::add(ConfigObject *obj, int row, bool unique) { if (obj && obj->is()) return ConfigObjectList::add(obj, row, unique); return -1; } ConfigItem * RoamingZoneList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) if (! node) return nullptr; if (! node.IsMap()) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create roaming zone: Expected object."; return nullptr; } return new RoamingZone(); } qdmr-0.12.3/lib/roamingzone.hh000066400000000000000000000076611501654372000161740ustar00rootroot00000000000000#ifndef ROAMINGZONE_HH #define ROAMINGZONE_HH #include #include "configreference.hh" #include "roamingchannel.hh" /** Represents a RoamingZone within the abstract device configuration. * * A roaming zone collects a set of repeaters that act as alternatives to each other. When a selected * repeater gets out of range, another one might be found automatically from within the roaming zone. * * @ingroup config */ class RoamingZone : public ConfigObject { Q_OBJECT Q_CLASSINFO("IdPrefix", "roam") /** The channels in the roaming zone. * @todo This property is marked non-scriptable to handle references to DMR channels before * version 0.11.0. Remove in future. */ Q_PROPERTY(RoamingChannelRefList * channels READ channels SCRIPTABLE false) public: /** Default constructor. */ Q_INVOKABLE explicit RoamingZone(QObject *parent=nullptr); /** Constructor. * @param name Specifies the name of the roaming zone. * @param parent Specifies the QObject parent of this zone. */ RoamingZone(const QString &name, QObject *parent = nullptr); /** Copies the given zone. */ RoamingZone &operator =(const RoamingZone &other); ConfigItem *clone() const; /** Returns the number of zones. */ int count() const; /** Clears the zone list. */ void clear(); /** Returns @c true, if the given roaming channel is member of this zone. */ bool contains(const RoamingChannel *ch) const; /** Returns the roaming channel, which is the member at index @c idx (0-based). * @param idx Specifies the index of the member channel. */ RoamingChannel *channel(int idx) const; /** Adds a channel to the roaming zone. * @param ch Specifies the channel to add. * @param row Speicifies the index where to insert the channel * (optional, default insert at end). */ int addChannel(RoamingChannel *ch, int row=-1); /** Removes the channel from the roaming zone at index @c row. */ bool remChannel(int row); /** Removes the given channel from the roaming zone. */ bool remChannel(RoamingChannel *ch); /** Returns the list of digital channels in this roaming zone. */ const RoamingChannelRefList *channels() const; /** Returns the list of digital channels in this roaming zone. */ RoamingChannelRefList *channels(); /** Links the channel reference list. * @todo Implemented for backward compatibility with version 0.10.0, remove for 1.0.0. */ bool link(const YAML::Node &node, const Context &ctx, const ErrorStack &err); /** Serializes the channel reference list. * @todo Implemented for backward compatibility with version 0.10.0, remove for 1.0.0. */ bool populate(YAML::Node &node, const Context &context, const ErrorStack &err); protected: /** Holds the actual channels of the roaming zone. */ RoamingChannelRefList _channel; }; /** Dummy roaming zone class that represents the default roaming zone. * * This is a singleton class. That is, there can only be one instance of this class. * @ingroup config */ class DefaultRoamingZone: public RoamingZone { Q_OBJECT protected: /** Hidden constructor. * Use @c DefaultRoamingZone::get() to obtain an instance. */ explicit DefaultRoamingZone(QObject *parent=nullptr); public: /** Returns the singleton instance of this class. */ static DefaultRoamingZone *get(); protected: /** Holds a reference to the singleton instance of this class. */ static DefaultRoamingZone *_instance; }; /** Represents the list of roaming zones within the abstract device configuration. * * @ingroup config */ class RoamingZoneList: public ConfigObjectList { Q_OBJECT public: /** Constructor. */ explicit RoamingZoneList(QObject *parent=nullptr); /** Returns the roaming zone at the given index. */ RoamingZone *zone(int idx) const; int add(ConfigObject *obj, int row=-1, bool unique=true); public: ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); }; #endif // ROAMINGZONE_HH qdmr-0.12.3/lib/rxgrouplist.cc000066400000000000000000000065171501654372000162330ustar00rootroot00000000000000#include "rxgrouplist.hh" #include #include #include #include #include #include #include #include #include #include #include "contact.hh" #include "config.hh" /* ********************************************************************************************* * * Implementation of RXGroupList * ********************************************************************************************* */ RXGroupList::RXGroupList(QObject *parent) : ConfigObject(parent), _contacts() { connect(&_contacts, SIGNAL(elementModified(int)), this, SLOT(onModified())); connect(&_contacts, SIGNAL(elementRemoved(int)), this, SLOT(onModified())); connect(&_contacts, SIGNAL(elementAdded(int)), this, SLOT(onModified())); } RXGroupList::RXGroupList(const QString &name, QObject *parent) : ConfigObject(name, parent), _contacts() { connect(&_contacts, SIGNAL(elementModified(int)), this, SLOT(onModified())); connect(&_contacts, SIGNAL(elementRemoved(int)), this, SLOT(onModified())); connect(&_contacts, SIGNAL(elementAdded(int)), this, SLOT(onModified())); } RXGroupList & RXGroupList::operator =(const RXGroupList &other) { copy(other); return *this; } ConfigItem * RXGroupList::clone() const { RXGroupList *lst = new RXGroupList(); if (! lst->copy(*this)) { lst->deleteLater(); return nullptr; } return lst; } int RXGroupList::count() const { return _contacts.count(); } void RXGroupList::clear() { _contacts.clear(); emit modified(this); } DMRContact * RXGroupList::contact(int idx) const { if (idx >= _contacts.count()) return nullptr; return _contacts.get(idx)->as(); } int RXGroupList::addContact(DMRContact *contact, int idx) { return _contacts.add(contact, idx); } bool RXGroupList::remContact(int idx) { return _contacts.del(_contacts.get(idx)); } const DMRContactRefList * RXGroupList::contacts() const { return &_contacts; } DMRContactRefList * RXGroupList::contacts() { return &_contacts; } YAML::Node RXGroupList::serialize(const Context &context, const ErrorStack &err) { YAML::Node node = ConfigObject::serialize(context, err); node.SetStyle(YAML::EmitterStyle::Flow); return node; } void RXGroupList::onModified() { emit modified(this); } /* ********************************************************************************************* * * Implementation of RXGroupLists * ********************************************************************************************* */ RXGroupLists::RXGroupLists(QObject *parent) : ConfigObjectList(RXGroupList::staticMetaObject, parent) { // pass... } RXGroupList * RXGroupLists::list(int idx) const { if (ConfigItem *obj = get(idx)) return obj->as(); return nullptr; } int RXGroupLists::add(ConfigObject *obj, int row, bool unique) { if (obj && obj->is()) return ConfigObjectList::add(obj, row, unique); return -1; } ConfigItem * RXGroupLists::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) if (! node) return nullptr; if (! node.IsMap()) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create group list: Expected object."; return nullptr; } return new RXGroupList(); } qdmr-0.12.3/lib/rxgrouplist.hh000066400000000000000000000042751501654372000162440ustar00rootroot00000000000000#ifndef RXGROUPLIST_HH #define RXGROUPLIST_HH #include #include "configreference.hh" class Config; class DMRContact; /** Generic representation of a RX group list. * @ingroup conf */ class RXGroupList: public ConfigObject { Q_OBJECT Q_CLASSINFO("IdPrefix", "grp") /** The list of contacts. */ Q_PROPERTY(DMRContactRefList* contacts READ contacts) public: /** Default constructor. */ Q_INVOKABLE explicit RXGroupList(QObject *parent=nullptr); /** Constructor. * @param name Specifies the name of the group list. * @param parent @c QObject parent instance. */ RXGroupList(const QString &name, QObject *parent=nullptr); /** Copy from other group list. */ RXGroupList &operator =(const RXGroupList &other); ConfigItem *clone() const; /** Returns the number of contacts within the group list. */ int count() const; /** Resets & clears this group list. */ void clear(); /** Returns the contact at the given list index. */ DMRContact *contact(int idx) const; /** Adds a contact to the list. */ int addContact(DMRContact *contact, int idx=-1); /** Removes the given contact from the list. */ bool remContact(DMRContact *contact); /** Removes the contact from the list at the given position. */ bool remContact(int idx); /** Returns the contact list. */ const DMRContactRefList *contacts() const; /** Returns the contact list. */ DMRContactRefList *contacts(); public: YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack()); protected slots: /** Internal used callback to handle list modifications. */ void onModified(); protected: /** The list of contacts. */ DMRContactRefList _contacts; }; /** Represents the list of RX group lists within the generic configuration. * @ingroup conf */ class RXGroupLists: public ConfigObjectList { Q_OBJECT public: /** Constructor. */ explicit RXGroupLists(QObject *parent=nullptr); /** Returns the group list at the given index. */ RXGroupList *list(int idx) const; int add(ConfigObject *obj, int row=-1, bool unique=true); public: ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); }; #endif // RXGROUPLIST_HH qdmr-0.12.3/lib/satelliteconfig.cc000066400000000000000000000001671501654372000170000ustar00rootroot00000000000000#include "satelliteconfig.hh" SatelliteConfig::SatelliteConfig(QObject *parent) : DFUFile{parent} { // pass... } qdmr-0.12.3/lib/satelliteconfig.hh000066400000000000000000000012341501654372000170060ustar00rootroot00000000000000#ifndef SATELLITECONFIG_HH #define SATELLITECONFIG_HH #include "dfufile.hh" // Forward declarations class SatelliteDatabase; /** Baseclass of all satellite database configurations. * That is, the device specific encoding of the satellite orbitals and transponder information. * @ingroup conf */ class SatelliteConfig : public DFUFile { Q_OBJECT protected: /** Hidden constructor. */ explicit SatelliteConfig(QObject *parent = nullptr); public: /** Encodes the given satellite db into the device specific satellite configuration. */ virtual bool encode(SatelliteDatabase *db, const ErrorStack &err=ErrorStack()) = 0; }; #endif // SATELLITECONFIG_HH qdmr-0.12.3/lib/satellitedatabase.cc000066400000000000000000000331711501654372000173000ustar00rootroot00000000000000#include "satellitedatabase.hh" #include #include #include #include #include #include #include #include #include "logger.hh" /* ********************************************************************************************* * * Implementation of Satellite * ********************************************************************************************* */ Satellite::Satellite() : OrbitalElement(), _name(), _fmUplink(), _fmDownlink(), _fmUplinkTone(), _fmDownlinkTone(), _aprsUplink(), _aprsDownlink(), _aprsUplinkTone(), _aprsDownlinkTone(), _beacon() { // pass... } Satellite::Satellite(const OrbitalElement &orbit) : OrbitalElement(orbit), _name(orbit.name()), _fmUplink(), _fmDownlink(), _fmUplinkTone(), _fmDownlinkTone(), _aprsUplink(), _aprsDownlink(), _aprsUplinkTone(), _aprsDownlinkTone(), _beacon() { // pass... } const QString & Satellite::name() const { return _name; } void Satellite::setName(const QString &name) { _name = name.simplified(); } const Frequency & Satellite::fmUplink() const { return _fmUplink; } void Satellite::setFMUplink(const Frequency &f) { _fmUplink = f; } const Frequency & Satellite::fmDownlink() const { return _fmDownlink; } void Satellite::setFMDownlink(const Frequency &f) { _fmDownlink = f; } const SelectiveCall & Satellite::fmUplinkTone() const { return _fmUplinkTone; } void Satellite::setFMUplinkTone(const SelectiveCall &tone) { _fmUplinkTone = tone; } const SelectiveCall & Satellite::fmDownlinkTone() const { return _fmDownlinkTone; } void Satellite::setFMDownlinkTone(const SelectiveCall &tone) { _fmDownlinkTone = tone; } const Frequency & Satellite::aprsUplink() const { return _aprsUplink; } void Satellite::setAPRSUplink(const Frequency &f) { _aprsUplink = f; } const Frequency & Satellite::aprsDownlink() const { return _aprsDownlink; } void Satellite::setAPRSDownlink(const Frequency &f) { _aprsDownlink = f; } const SelectiveCall & Satellite::aprsUplinkTone() const { return _aprsUplinkTone; } void Satellite::setAPRSUplinkTone(const SelectiveCall &tone) { _aprsUplinkTone = tone; } const SelectiveCall & Satellite::aprsDownlinkTone() const { return _aprsDownlinkTone; } void Satellite::setAPRSDownlinkTone(const SelectiveCall &tone) { _aprsDownlinkTone = tone; } const Frequency & Satellite::beacon() const { return _beacon; } void Satellite::setBeacon(const Frequency &f) { _beacon = f; } QJsonObject Satellite::toJson() const { if (! isValid()) return QJsonObject(); QJsonObject o = QJsonObject(); o.insert("norad", QJsonValue(qint64(id()))); o.insert("name", name()); if (0 != fmUplink().inHz()) o.insert("fm_uplink", fmUplink().format()); if (fmUplinkTone().isValid()) o.insert("fm_uplink_tone", fmUplinkTone().format()); if (0 != fmDownlink().inHz()) o.insert("fm_downlink", fmDownlink().format()); if (fmDownlinkTone().isValid()) o.insert("fm_downlink_tone", fmDownlinkTone().format()); if (0 != aprsUplink().inHz()) o.insert("aprs_uplink", aprsUplink().format()); if (aprsUplinkTone().isValid()) o.insert("aprs_uplink_tone", aprsUplinkTone().format()); if (0 != aprsDownlink().inHz()) o.insert("aprs_downlink", aprsDownlink().format()); if (aprsDownlinkTone().isValid()) o.insert("aprs_downlink_tone", aprsDownlinkTone().format()); if (0 != beacon().inHz()) o.insert("beacon", beacon().format()); return o; } Satellite Satellite::fromJson(const QJsonObject &obj, const OrbitalElementsDatabase &db) { unsigned int id = obj.value("norad").toInt(); QString name = obj.value("name").toString(); if (! db.contains(id)) return Satellite(); Satellite sat(db.getById(id)); sat._name = name; if (obj.contains("fm_uplink")) sat._fmUplink.parse(obj.value("fm_uplink").toString()); if (obj.contains("fm_uplink_tone")) sat._fmUplinkTone = SelectiveCall::parseCTCSS(obj.value("fm_uplink_tone").toString()); if (obj.contains("fm_downlink")) sat._fmDownlink.parse(obj.value("fm_downlink").toString()); if (obj.contains("fm_downlink_tone")) sat._fmDownlinkTone = SelectiveCall::parseCTCSS(obj.value("fm_downlink_tone").toString()); if (obj.contains("aprs_uplink")) sat._aprsUplink.parse(obj.value("aprs_uplink").toString()); if (obj.contains("aprs_uplink_tone")) sat._aprsUplinkTone = SelectiveCall::parseCTCSS(obj.value("aprs_uplink_tone").toString()); if (obj.contains("aprs_downlink")) sat._aprsDownlink.parse(obj.value("aprs_downlink").toString()); if (obj.contains("aprs_downlink_tone")) sat._aprsDownlinkTone = SelectiveCall::parseCTCSS(obj.value("aprs_downlink_tone").toString()); if (obj.contains("beacon")) sat._beacon.parse(obj.value("beacon").toString()); return sat; } /* ********************************************************************************************* * * Implementation of SatelliteDatabase * ********************************************************************************************* */ SatelliteDatabase::SatelliteDatabase(unsigned int updatePeriod, QObject *parent) : QAbstractTableModel{parent}, _satellites(), _orbitalElements(false, updatePeriod), _transponders(false, updatePeriod) { connect(&_orbitalElements, &OrbitalElementsDatabase::loaded, this, &SatelliteDatabase::load); _orbitalElements.load(); _transponders.load(); } const OrbitalElementsDatabase & SatelliteDatabase::orbitalElements() const { return _orbitalElements; } const TransponderDatabase & SatelliteDatabase::transponders() const { return _transponders; } OrbitalElementsDatabase & SatelliteDatabase::orbitalElements() { return _orbitalElements; } TransponderDatabase & SatelliteDatabase::transponders() { return _transponders; } unsigned int SatelliteDatabase::count() const { return _satellites.count(); } const Satellite & SatelliteDatabase::getAt(unsigned int idx) const { return _satellites[idx]; } void SatelliteDatabase::add(const Satellite &sat) { if (! sat.isValid()) return; beginInsertRows(QModelIndex(), _satellites.count(), _satellites.count()); _satellites.append(sat); endInsertRows(); } bool SatelliteDatabase::removeRows(int row, int count, const QModelIndex &parent) { if ((row >= _satellites.count()) || ((row+count) > _satellites.count())) return false; if (0 == count) return true; beginRemoveRows(parent, row, row+count-1); _satellites.remove(row, count); endRemoveRows(); return true; } int SatelliteDatabase::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return _satellites.size(); } int SatelliteDatabase::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 11; } QVariant SatelliteDatabase::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::Horizontal != orientation) || (Qt::DisplayRole != role)) return QVariant(); switch (section) { case 0: return tr("NORAD"); case 1: return tr("Name"); case 2: return tr("FM Downlink Frequency"); case 3: return tr("FM Uplink Frequency"); case 4: return tr("FM Downlink Tone"); case 5: return tr("FM Uplink Tone"); case 6: return tr("APRS Downlink Frequency"); case 7: return tr("APRS Uplink Frequency"); case 8: return tr("APRS Downlink Tone"); case 9: return tr("APRS Uplink Tone"); case 10: return tr("Beacon Frequency"); } return QVariant(); } Qt::ItemFlags SatelliteDatabase::flags(const QModelIndex &index) const { Qt::ItemFlags f = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; // Name if (1 == index.column()) f |= Qt::ItemIsEditable; // FM up/downlink frequencies if ((2 == index.column()) || (3 == index.column())) f |= Qt::ItemIsEditable; // FM up/downlink sub tones if ((4 == index.column()) || (5 == index.column())) f |= Qt::ItemIsEditable; // APRS up/downlink frequencies if ((6 == index.column()) || (7 == index.column())) f |= Qt::ItemIsEditable; // APRS up/downlink sub tones if ((8 == index.column()) || (9 == index.column())) f |= Qt::ItemIsEditable; // Beacon if (10 == index.column()) f |= Qt::ItemIsEditable; return f; } QVariant SatelliteDatabase::data(const QModelIndex &index, int role) const { if (index.row() >= _satellites.count()) return QVariant(); if (Qt::DisplayRole == role) { switch (index.column()) { case 0: return _satellites.at(index.row()).id(); case 1: return _satellites.at(index.row()).name(); case 2: return (0 == _satellites.at(index.row()).fmDownlink().inHz()) ? tr("None") : _satellites.at(index.row()).fmDownlink().format(); case 3: return (0 == _satellites.at(index.row()).fmUplink().inHz()) ? tr("None") : _satellites.at(index.row()).fmUplink().format(); case 4: return _satellites.at(index.row()).fmDownlinkTone().format(); case 5: return _satellites.at(index.row()).fmUplinkTone().format(); case 6: return (0 == _satellites.at(index.row()).aprsDownlink().inHz()) ? tr("None") : _satellites.at(index.row()).aprsDownlink().format(); case 7: return (0 == _satellites.at(index.row()).aprsUplink().inHz()) ? tr("None") : _satellites.at(index.row()).aprsUplink().format(); case 8: return _satellites.at(index.row()).aprsDownlinkTone().format(); case 9: return _satellites.at(index.row()).aprsUplinkTone().format(); case 10: return (0 == _satellites.at(index.row()).beacon().inHz()) ? tr("None") : _satellites.at(index.row()).beacon().format(); } } else if (Qt::EditRole == role) { switch (index.column()) { case 0: return _satellites.at(index.row()).id(); case 1: return _satellites.at(index.row()).name(); case 2: return QVariant::fromValue(_satellites.at(index.row()).fmDownlink()); case 3: return QVariant::fromValue(_satellites.at(index.row()).fmUplink()); case 4: return QVariant::fromValue(_satellites.at(index.row()).fmDownlinkTone()); case 5: return QVariant::fromValue(_satellites.at(index.row()).fmUplinkTone()); case 6: return QVariant::fromValue(_satellites.at(index.row()).aprsDownlink()); case 7: return QVariant::fromValue(_satellites.at(index.row()).aprsUplink()); case 8: return QVariant::fromValue(_satellites.at(index.row()).aprsDownlinkTone()); case 9: return QVariant::fromValue(_satellites.at(index.row()).aprsUplinkTone()); case 10: return QVariant::fromValue(_satellites.at(index.row()).beacon()); } } return QVariant(); } bool SatelliteDatabase::setData(const QModelIndex &index, const QVariant &value, int role) { if (Qt::EditRole != role) return false; if (index.row() >= _satellites.count()) return false; switch (index.column()) { case 1: _satellites[index.row()].setName(value.toString().simplified()); return true; case 2: _satellites[index.row()].setFMDownlink(value.value()); return true; case 3: _satellites[index.row()].setFMUplink(value.value()); return true; case 4: _satellites[index.row()].setFMDownlinkTone(value.value()); return true; case 5: _satellites[index.row()].setFMUplinkTone(value.value()); return true; case 6: _satellites[index.row()].setAPRSDownlink(value.value()); return true; case 7: _satellites[index.row()].setAPRSUplink(value.value()); return true; case 8: _satellites[index.row()].setAPRSDownlinkTone(value.value()); return true; case 9: _satellites[index.row()].setAPRSUplinkTone(value.value()); return true; case 10: _satellites[index.row()].setBeacon(value.value()); return true; } return false; } void SatelliteDatabase::update() { _orbitalElements.download(); _transponders.download(); } void SatelliteDatabase::load() { QString filename = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/satellites.json"; QFile file(filename); if (! file.open(QIODevice::ReadOnly)) { QString msg = QString("Cannot open satellites '%1': %2").arg(filename).arg(file.errorString()); logError() << msg; emit error(msg); return; } QByteArray data = file.readAll(); file.close(); QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (doc.isEmpty()) { QString msg = "Failed to load satellites: " + err.errorString(); logError() << msg; emit error(msg); return; } if (! doc.isArray()) { QString msg = "Failed to load satellites: JSON document is not an array!"; logError() << msg; emit error(msg); return; } beginResetModel(); QJsonArray array = doc.array(); _satellites.clear(); _satellites.reserve(array.size()); for (int i=0; i _satellites; /** Holds the orbital element database. */ OrbitalElementsDatabase _orbitalElements; /** Holds the transponder database. */ TransponderDatabase _transponders; }; #endif // SATELLITEDATABASE_HH qdmr-0.12.3/lib/scanlist.cc000066400000000000000000000120311501654372000154350ustar00rootroot00000000000000#include "scanlist.hh" #include "channel.hh" #include "config.hh" #include #include #include #include #include #include #include #include /* ********************************************************************************************* * * Implementation of ScanList * ********************************************************************************************* */ ScanList::ScanList(QObject *parent) : ConfigObject(parent), _channels(), _primary(), _secondary(), _revert(), _tyt(nullptr) { // Register "selected" channel tags for primary, secondary, revert and the channel list. Context::setTag(staticMetaObject.className(), "primary", "!selected", SelectedChannel::get()); Context::setTag(staticMetaObject.className(), "secondary", "!selected", SelectedChannel::get()); Context::setTag(staticMetaObject.className(), "revert", "!selected", SelectedChannel::get()); Context::setTag(staticMetaObject.className(), "channels", "!selected", SelectedChannel::get()); } ScanList::ScanList(const QString &name, QObject *parent) : ConfigObject(name, parent), _channels(), _primary(), _secondary(), _revert(), _tyt(nullptr) { // Register "selected" channel tags for primary, secondary, revert and the channel list. Context::setTag(staticMetaObject.className(), "primary", "!selected", SelectedChannel::get()); Context::setTag(staticMetaObject.className(), "secondary", "!selected", SelectedChannel::get()); Context::setTag(staticMetaObject.className(), "revert", "!selected", SelectedChannel::get()); Context::setTag(staticMetaObject.className(), "channels", "!selected", SelectedChannel::get()); } ScanList & ScanList::operator =(const ScanList &other) { copy(other); return *this; } ConfigItem * ScanList::clone() const { ScanList *list = new ScanList(); if (! list->copy(*this)) { list->deleteLater(); return nullptr; } return list; } void ScanList::clear() { _name.clear(); _primary.clear(); _secondary.clear(); _revert.clear(); _channels.clear(); emit modified(this); } const ChannelRefList * ScanList::channels() const { return &_channels; } ChannelRefList * ScanList::channels() { return &_channels; } int ScanList::count() const { return _channels.count(); } bool ScanList::contains(Channel *channel) const { return (0 <= _channels.indexOf(channel)); } Channel * ScanList::channel(int idx) const { return _channels.get(idx)->as(); } int ScanList::addChannel(Channel *channel, int idx) { idx = _channels.add(channel, idx); if (0 > idx) return idx; return idx; } bool ScanList::remChannel(int idx) { return _channels.del(_channels.get(idx)); emit modified(this); return true; } bool ScanList::remChannel(Channel *channel) { return _channels.del(channel); } const ChannelReference * ScanList::primary() const { return &_primary; } ChannelReference * ScanList::primary() { return &_primary; } Channel * ScanList::primaryChannel() const { return _primary.as(); } void ScanList::setPrimaryChannel(Channel *channel) { _primary.set(channel); emit modified(this); } const ChannelReference * ScanList::secondary() const { return &_secondary; } ChannelReference * ScanList::secondary() { return &_secondary; } Channel * ScanList::secondaryChannel() const { return _secondary.as(); } void ScanList::setSecondaryChannel(Channel *channel) { _secondary.set(channel); emit modified(this); } const ChannelReference * ScanList::revert() const { return &_revert; } ChannelReference * ScanList::revert() { return &_revert; } Channel * ScanList::revertChannel() const { return _revert.as(); } void ScanList::setRevertChannel(Channel *channel) { _revert.set(channel); emit modified(this); } TyTScanListExtension * ScanList::tytScanListExtension() const { return _tyt; } void ScanList::setTyTScanListExtension(TyTScanListExtension *tyt) { if (_tyt) { _tyt->deleteLater(); _tyt = nullptr; } _tyt = tyt; if (_tyt) _tyt->setParent(this); } /* ********************************************************************************************* * * Implementation of ScanLists * ********************************************************************************************* */ ScanLists::ScanLists(QObject *parent) : ConfigObjectList(ScanList::staticMetaObject, parent) { // pass... } ScanList * ScanLists::scanlist(int idx) const { if (ConfigItem *obj = get(idx)) return obj->as(); return nullptr; } int ScanLists::add(ConfigObject *obj, int row, bool unique) { if (obj && obj->is()) return ConfigObjectList::add(obj, row, unique); return -1; } ConfigItem * ScanLists::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx); if (! node) return nullptr; if (! node.IsMap()) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create scan list: Expected object."; return nullptr; } return new ScanList(); } qdmr-0.12.3/lib/scanlist.hh000066400000000000000000000074351501654372000154630ustar00rootroot00000000000000#ifndef SCANLIST_HH #define SCANLIST_HH #include "configobject.hh" #include "configreference.hh" #include "tyt_extensions.hh" class Channel; /** Generic representation of a scan list. * @ingroup conf */ class ScanList : public ConfigObject { Q_OBJECT Q_CLASSINFO("IdPrefix", "scan") /** The primary channel. */ Q_PROPERTY(ChannelReference* primary READ primary) /** The secondary channel. */ Q_PROPERTY(ChannelReference* secondary READ secondary) /** The revert channel. */ Q_PROPERTY(ChannelReference* revert READ revert) /** The list of channels. */ Q_PROPERTY(ChannelRefList * channels READ channels) /** The TyT scan-list extension. */ Q_PROPERTY(TyTScanListExtension* tyt READ tytScanListExtension WRITE setTyTScanListExtension) public: /** Default constructor. */ Q_INVOKABLE explicit ScanList(QObject *parent=nullptr); /** Constructs a scan list with the given name. */ ScanList(const QString &name, QObject *parent=nullptr); /** Copies the given scan list. */ ScanList &operator= (const ScanList &other); ConfigItem *clone() const; /** Returns the number of channels within the scanlist. */ int count() const; /** Clears the scan list. */ void clear(); /** Returns @c true if the given channel is part of this scanlist. */ bool contains(Channel *channel) const; /** Returns the channel at the given index. */ Channel *channel(int idx) const; /** Adds a channel to the scan list. */ int addChannel(Channel *channel, int idx=-1); /** Removes the channel at the given index. */ bool remChannel(int idx); /** Removes the given channel. */ bool remChannel(Channel *channel); /** Returns the channels of the scan list. */ const ChannelRefList *channels() const; /** Returns the channels of the scan list. */ ChannelRefList *channels(); /** Returns the primary channel reference. */ const ChannelReference *primary() const; /** Returns the primary channel reference. */ ChannelReference *primary(); /** Returns the priority channel. */ Channel *primaryChannel() const; /** Sets the priority channel. */ void setPrimaryChannel(Channel *channel); /** Returns the secondary channel reference. */ const ChannelReference *secondary() const; /** Returns the secondary channel reference. */ ChannelReference *secondary(); /** Returns the secondary priority channel. */ Channel *secondaryChannel() const; /** Sets the secondary priority channel. */ void setSecondaryChannel(Channel *channel); /** Returns the revert channel reference. */ const ChannelReference *revert() const; /** Returns the revert channel reference. */ ChannelReference *revert(); /** Returns the TX channel. */ Channel *revertChannel() const; /** Sets the TX channel. */ void setRevertChannel(Channel *channel); /** Returns the TyT scan-list extension instance (if set). */ TyTScanListExtension *tytScanListExtension() const; /** Sets the TyT scan-list extension. */ void setTyTScanListExtension(TyTScanListExtension *tyt); protected: /** The channel list. */ ChannelRefList _channels; /** The priority channel. */ ChannelReference _primary; /** The secondary priority channel. */ ChannelReference _secondary; /** The transmit channel. */ ChannelReference _revert; /** TyT scan-list settings extension. */ TyTScanListExtension *_tyt; }; /** Represents the list of scan lists. * @ingroup conf */ class ScanLists: public ConfigObjectList { Q_OBJECT public: /** Constructs an empty list. */ explicit ScanLists(QObject *parent = nullptr); /** Returns the scanlist at the given index. */ ScanList *scanlist(int idx) const; int add(ConfigObject *obj, int row=-1, bool unique=true); public: ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); }; #endif // SCANLIST_HH qdmr-0.12.3/lib/signaling.cc000066400000000000000000000145111501654372000155750ustar00rootroot00000000000000#include "signaling.hh" #include #include #include #include #include /* ********************************************************************************************* * * Implementation of SelectiveCall * ********************************************************************************************* */ QVector SelectiveCall::_standard = { SelectiveCall(), 67.0, 71.9, 74.4, 77.0, 79.7, 82.5, 85.4, 88.5, 91.5, 94.8, 97.4, 100.0, 103.5, 107.2, 110.9, 114.8, 118.8, 123.0, 127.3, 131.8, 136.5, 141.3, 146.2, 151.4, 156.7, 162.2, 167.9, 173.8, 179.9, 186.2, 192.8, 203.5, 210.7, 218.1, 225.7, 233.6, 241.8, 250.3, { 23, false}, { 25, false}, { 26, false}, { 31, false}, { 32, false}, { 36, false}, { 43, false}, { 47, false}, { 51, false}, { 53, false}, { 54, false}, { 71, false}, { 72, false}, { 73, false}, { 74, false}, {114, false}, {115, false}, {116, false}, {122, false}, {125, false}, {131, false}, {132, false}, {134, false}, {143, false}, {145, false}, {152, false}, {155, false}, {156, false}, {162, false}, {165, false}, {172, false}, {174, false}, {205, false}, {212, false}, {223, false}, {225, false}, {226, false}, {243, false}, {244, false}, {245, false}, {246, false}, {251, false}, {252, false}, {255, false}, {261, false}, {263, false}, {265, false}, {266, false}, {267, false}, {271, false}, {274, false}, {306, false}, {311, false}, {315, false}, {325, false}, {331, false}, {332, false}, {343, false}, {346, false}, {351, false}, {356, false}, {364, false}, {365, false}, {371, false}, {411, false}, {412, false}, {413, false}, {423, false}, {431, false}, {432, false}, {445, false}, {446, false}, {452, false}, {454, false}, {455, false}, {462, false}, {464, false}, {465, false}, {466, false}, {503, false}, {506, false}, {516, false}, {523, false}, {526, false}, {532, false}, {546, false}, {565, false}, {606, false}, {612, false}, {624, false}, {627, false}, {631, false}, {632, false}, {654, false}, {662, false}, {664, false}, {703, false}, {712, false}, {723, false}, {731, false}, {732, false}, {734, false}, {743, false}, {754, false}, { 23, true}, { 25, true}, { 26, true}, { 31, true}, { 32, true}, { 36, true}, { 43, true}, { 47, true}, { 51, true}, { 53, true}, { 54, true}, { 71, true}, { 72, true}, { 73, true}, { 74, true}, {114, true}, {115, true}, {116, true}, {122, true}, {125, true}, {131, true}, {132, true}, {134, true}, {143, true}, {145, true}, {152, true}, {155, true}, {156, true}, {162, true}, {165, true}, {172, true}, {174, true}, {205, true}, {212, true}, {223, true}, {225, true}, {226, true}, {243, true}, {244, true}, {245, true}, {246, true}, {251, true}, {252, true}, {255, true}, {261, true}, {263, true}, {265, true}, {266, true}, {267, true}, {271, true}, {274, true}, {306, true}, {311, true}, {315, true}, {325, true}, {331, true}, {332, true}, {343, true}, {346, true}, {351, true}, {356, true}, {364, true}, {365, true}, {371, true}, {411, true}, {412, true}, {413, true}, {423, true}, {431, true}, {432, true}, {445, true}, {446, true}, {452, true}, {454, true}, {455, true}, {462, true}, {464, true}, {465, true}, {466, true}, {503, true}, {506, true}, {516, true}, {523, true}, {526, true}, {532, true}, {546, true}, {565, true}, {606, true}, {612, true}, {624, true}, {627, true}, {631, true}, {632, true}, {654, true}, {662, true}, {664, true}, {703, true}, {712, true}, {723, true}, {731, true}, {732, true}, {734, true}, {743, true}, {754, true} }; SelectiveCall::SelectiveCall() : type(Type::None), dcs{0,false} { // Pass... } SelectiveCall::SelectiveCall(double ctcssFreq) : type(Type::CTCSS), ctcss(ctcssFreq*10) { // pass... } SelectiveCall::SelectiveCall(unsigned int octalDSCCode, bool inverted) : type(Type::DCS), dcs{0, inverted} { unsigned int e = 1; while (octalDSCCode) { dcs.code += std::min(7U, (octalDSCCode % 10)) * e; e *= 8; octalDSCCode /= 10; } } bool SelectiveCall::operator==(const SelectiveCall &other) const { if (type != other.type) return false; if (Type::CTCSS == type) return ctcss == other.ctcss; return (dcs.code == other.dcs.code) && (dcs.inverted == other.dcs.inverted); } bool SelectiveCall::operator !=(const SelectiveCall &other) const { return !(*this == other); } bool SelectiveCall::isInvalid() const { return Type::None == type; } bool SelectiveCall::isValid() const { return Type::None != type; } bool SelectiveCall::isCTCSS() const { return Type::CTCSS == type; } bool SelectiveCall::isDCS() const { return Type::DCS == type; } double SelectiveCall::Hz() const { return double(ctcss)/10; } unsigned int SelectiveCall::mHz() const { return ((unsigned int)ctcss)*100; } unsigned int SelectiveCall::binCode() const { return dcs.code; } unsigned int SelectiveCall::octalCode() const { unsigned int o=0, e=1, c=dcs.code; while (c) { o += (c%8)*e; e *= 10; c/= 8; } return o; } bool SelectiveCall::isInverted() const { return dcs.inverted; } QString SelectiveCall::format() const { if (! isValid()) return QString(); if (isCTCSS()) return QString("%1 Hz").arg(Hz(), 0, 'f', 1); return QString("%1%2") .arg(isInverted() ? "i" : "n") .arg(binCode(), 3, 8, QChar('0')); } SelectiveCall SelectiveCall::parseCTCSS(const QString &text) { QRegularExpression re(R"(([0-9]+(?:\.[0-9]|))\s*(?:Hz|))"); QRegularExpressionMatch match = re.match(text); if (! match.isValid()) return SelectiveCall(); return SelectiveCall(match.captured(1).toDouble()); } SelectiveCall SelectiveCall::parseDCS(const QString &text) { QRegularExpression re(R"(([\-iInN]?)([0-7]{1,3}))"); QRegularExpressionMatch match = re.match(text); if (! match.isValid()) return SelectiveCall(); bool inverted = false; if (("-" == match.captured(1)) || ("i" == match.captured(1)) || ("I" == match.captured(1))) inverted = true; return SelectiveCall(match.captured(2).toUInt(), inverted); } SelectiveCall SelectiveCall::fromBinaryDCS(unsigned int code, bool inverted) { unsigned int o=0, e=1; while (code) { o += (code % 8) * e; e *= 10; code /= 10; } return SelectiveCall(o, inverted); } const QVector & SelectiveCall::standard() { return _standard; } qdmr-0.12.3/lib/signaling.hh000066400000000000000000000065631501654372000156170ustar00rootroot00000000000000#ifndef SIGNALING_HH #define SIGNALING_HH #include #include #include /** Encodes a selective call. * This can be CTCSS sub tones or DSC codes. * @ingroup conf */ struct SelectiveCall { protected: /** Type of the subtone. */ enum class Type { None, CTCSS, DCS }; public: /** Empty constructor, no selective call defined. */ SelectiveCall(); /** Constructs a CTCSS sub tone for the specified frequency in Hz. */ SelectiveCall(double ctcssFreq); /** Constructs a DCS code for the specified ocal code and inversion. */ SelectiveCall(unsigned int octalDSCCode, bool inverted); /** Comparison operator. */ bool operator==(const SelectiveCall &other) const; /** Comparison operator. */ bool operator!=(const SelectiveCall &other) const; /** Returns @c false, if a selective call is set. */ bool isInvalid() const; /** Returns @c true, if a selective call is set. */ bool isValid() const; /** Returns @c true, if a CTCSS sub tone is set. */ bool isCTCSS() const; /** Returns @c true, if a DCS code is set. */ bool isDCS() const; /** If a CTCSS sub tone is set, returns the frequency in Hz (floating point). */ double Hz() const; /** If a CTCSS sub tone is set, returns the frequency in mHz (integer). */ unsigned int mHz() const; /** If a DCS code is set, returns the binary code. */ unsigned int binCode() const; /** If a DCS code is set, returns the octal code. */ unsigned int octalCode() const; /** If a DCS code is set, returns the inversion flag. */ bool isInverted() const; /** Formats the selective call. */ QString format() const; public: /** Parses a CTCSS frequency. */ static SelectiveCall parseCTCSS(const QString &text); /** Parses a DCS code. */ static SelectiveCall parseDCS(const QString &text); /** Construct from binary DCS code. */ static SelectiveCall fromBinaryDCS(unsigned int code, bool inverted); /** Returns a vector of standard selective calls. */ static const QVector &standard(); protected: /// Specifies the selective call type. Type type; union { /// CTCSS frequency in 0.1Hz uint16_t ctcss; struct { /// Binary DCS code uint16_t code; /// If @c true, the code is inverted. bool inverted; } dcs; }; protected: /** Fixed table of standard values. */ static QVector _standard; }; Q_DECLARE_METATYPE(SelectiveCall) namespace YAML { /** Implements the conversion to and from YAML::Node. */ template<> struct convert { /** Serializes the selective call. */ static Node encode(const SelectiveCall& rhs) { Node node; if (rhs.isCTCSS()) node["ctcss"] = rhs.format().toStdString(); else if (rhs.isDCS()) node["dcs"] = rhs.format().toStdString(); return node; } /** Parses the selective call. */ static bool decode(const Node& node, SelectiveCall& rhs) { if (node.IsNull()) { rhs = SelectiveCall(); return true; } if ((! node.IsMap()) || (1 != node.size())) return false; if (node["ctcss"]) rhs = SelectiveCall::parseCTCSS(QString::fromStdString(node["ctcss"].as())); if (node["dcs"]) rhs = SelectiveCall::parseDCS(QString::fromStdString(node["dcs"].as())); return rhs.isValid(); } }; } #endif // SIGNALING_HH qdmr-0.12.3/lib/smsextension.cc000066400000000000000000000042741501654372000163660ustar00rootroot00000000000000#include "smsextension.hh" /* ********************************************************************************************* * * Implementation of SMSTemplate * ********************************************************************************************* */ SMSTemplate::SMSTemplate(QObject *parent) : ConfigObject{parent}, _message() { // pass... } ConfigItem * SMSTemplate::clone() const { ConfigItem *item = new SMSTemplate(); if (! item->copy(*this)) { delete item; return nullptr; } return item; } const QString & SMSTemplate::message() const { return _message; } void SMSTemplate::setMessage(const QString message) { if (_message == message) return; _message = message; emit modified(this); } /* ********************************************************************************************* * * Implementation of SMSTemplates * ********************************************************************************************* */ SMSTemplates::SMSTemplates(QObject *parent) : ConfigObjectList(SMSTemplate::staticMetaObject, parent) { // pass... } ConfigItem * SMSTemplates::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err); return new SMSTemplate(); } SMSTemplate * SMSTemplates::message(unsigned int i) const { return get(i)->as(); } /* ********************************************************************************************* * * Implementation of RadioSettingsExtension * ********************************************************************************************* */ SMSExtension::SMSExtension(QObject *parent) : ConfigExtension{parent}, _format(Format::DMR), _smsTemplates(new SMSTemplates(this)) { // pass... } SMSExtension::Format SMSExtension::format() const { return _format; } void SMSExtension::setFormat(Format format) { if (_format == format) return; _format = format; emit modified(this); } ConfigItem * SMSExtension::clone() const { ConfigItem *item = new SMSExtension(); if (! item->copy(*this)) { delete item; return nullptr; } return item; } SMSTemplates * SMSExtension::smsTemplates() const { return _smsTemplates; } qdmr-0.12.3/lib/smsextension.hh000066400000000000000000000042651501654372000164000ustar00rootroot00000000000000#ifndef SMSEXTENSION_HH #define SMSEXTENSION_HH #include "configobject.hh" #include "interval.hh" /** Represents a SMS message template (pre defined message). * Instances of this class are held in the @c SMSExtension. */ class SMSTemplate: public ConfigObject { Q_OBJECT /** Specifies the prefix for every ID assigned to every message during serialization. */ Q_CLASSINFO("IdPrefix", "sms") /** The message text. */ Q_PROPERTY(QString message READ message WRITE setMessage) public: /** Default constructor. */ Q_INVOKABLE explicit SMSTemplate(QObject *parent = nullptr); ConfigItem *clone() const; /** Returns the message text. */ const QString &message() const; /** Sets the message text. */ void setMessage(const QString message); protected: /** Holds the message text. */ QString _message; }; /** Just a list, holding the SMS templates. */ class SMSTemplates: public ConfigObjectList { Q_OBJECT public: /** Default constructor. */ Q_INVOKABLE SMSTemplates(QObject *parent=nullptr); ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); /** Returns the i-th message. */ SMSTemplate *message(unsigned int i) const; }; /** Extension collecting all settings associated with SMS messages. */ class SMSExtension : public ConfigExtension { Q_OBJECT /** The format for the SMS. */ Q_PROPERTY(Format format READ format WRITE setFormat) /** All predefined SMS messages. */ Q_PROPERTY(SMSTemplates *templates READ smsTemplates) public: /** Possible SMS formats, DMR is usually a good idea. */ enum class Format { Motorola, Hytera, DMR }; Q_ENUM(Format) public: /** Default constructor. */ Q_INVOKABLE explicit SMSExtension(QObject *parent = nullptr); ConfigItem *clone() const; /** Returns the SMS format setting. */ Format format() const; /** Sets the SMS format. */ void setFormat(Format format); /** Retunrs a weak reference to the list of SMS templates. */ SMSTemplates *smsTemplates() const; protected: /** Holds the SMS format. */ Format _format; /** Owns a reference to the list of SMS templates. */ SMSTemplates *_smsTemplates; }; #endif // SMSEXTENSION_HH qdmr-0.12.3/lib/talkgroupdatabase.cc000066400000000000000000000115251501654372000173210ustar00rootroot00000000000000#include "talkgroupdatabase.hh" #include #include #include "logger.hh" #include #include #include #include /* ********************************************************************************************* * * Implementation of TalkGroupDatabase::TalkGroup * ********************************************************************************************* */ TalkGroupDatabase::TalkGroup::TalkGroup() : id(0), name() { // pass... } TalkGroupDatabase::TalkGroup::TalkGroup(const QString &name, unsigned number) { this->id = number; this->name = name; } /* ********************************************************************************************* * * Implementation of TalkGroupDatabase * ********************************************************************************************* */ TalkGroupDatabase::TalkGroupDatabase(unsigned updatePeriodDays, QObject *parent) : QAbstractTableModel(parent), _talkgroups(), _network() { connect(&_network, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadFinished(QNetworkReply*))); if ((! load()) || (updatePeriodDays < dbAge())) download(); } qint64 TalkGroupDatabase::count() const { return _talkgroups.count(); } unsigned TalkGroupDatabase::dbAge() const { QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/talkgroups.json"; QFileInfo info(path); if (! info.exists()) return -1; return info.lastModified().daysTo(QDateTime::currentDateTime()); } TalkGroupDatabase::TalkGroup TalkGroupDatabase::talkgroup(int index) const { if ((0 > index) || (index >= count())) return TalkGroup(); return _talkgroups[index]; } void TalkGroupDatabase::download() { QUrl url("https://api.brandmeister.network/v2/talkgroup/"); QNetworkRequest request(url); _network.get(request); } void TalkGroupDatabase::downloadFinished(QNetworkReply *reply) { if (reply->error()) { QString msg = QString("Cannot download user database: %1").arg(reply->errorString()); logError() << msg; emit error(msg); return; } QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QFile file(path+"/talkgroups.json"); QDir directory; if ((! directory.exists(path)) && (!directory.mkpath(path))) { QString msg = QString("Cannot create path '%1'.").arg(path); logError() << msg; emit error(msg); return; } if (! file.open(QIODevice::WriteOnly)) { QString msg = QString("Cannot save user database at '%1'.").arg(path+"/talkgroups.json"); logError() << msg; emit error(msg); return; } file.write(reply->readAll()); file.flush(); file.close(); load(); reply->deleteLater(); } bool TalkGroupDatabase::load() { QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); return load(path+"/talkgroups.json"); } bool TalkGroupDatabase::load(const QString &filename) { QFile file(filename); if (! file.open(QIODevice::ReadOnly)) { QString msg = QString("Cannot open talk group list '%1': ").arg(filename).arg(file.errorString()); logError() << msg; emit error(msg); return false; } QByteArray data = file.readAll(); file.close(); QJsonDocument doc = QJsonDocument::fromJson(data); if (! doc.isObject()) { QString msg = "Failed to load talk groups: JSON document is not an object!"; logError() << msg; emit error(msg); return false; } beginResetModel(); QJsonObject tgs = doc.object(); _talkgroups.clear(); _talkgroups.reserve(tgs.count()); for (QJsonObject::const_iterator tg = tgs.begin(); tg!=tgs.end(); tg++) { _talkgroups.append(TalkGroup(tg.value().toString(), tg.key().toUInt())); } // Sort repeater w.r.t. their IDs std::stable_sort(_talkgroups.begin(), _talkgroups.end(), [](const TalkGroup &a, const TalkGroup &b){ return a.id < b.id; }); // Done. endResetModel(); logDebug() << "Loaded talk group database with " << _talkgroups.size() << " entries from " << filename << "."; emit loaded(); return true; } int TalkGroupDatabase::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return _talkgroups.count(); } int TalkGroupDatabase::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return 2; } QVariant TalkGroupDatabase::data(const QModelIndex &index, int role) const { if ((Qt::EditRole != role) && ((Qt::DisplayRole != role))) return QVariant(); if (index.row() >= _talkgroups.size()) return QVariant(); if (0 == index.column()) { // Call if (Qt::DisplayRole == role) { return tr("%1 (%2)").arg(_talkgroups[index.row()].name).arg(_talkgroups[index.row()].name); } else { return _talkgroups[index.row()].name; } } else if (1 == index.column()) { // ID return _talkgroups[index.row()].id; } return QVariant(); } qdmr-0.12.3/lib/talkgroupdatabase.hh000066400000000000000000000046041501654372000173330ustar00rootroot00000000000000#ifndef TALKGROUPDATABASE_HH #define TALKGROUPDATABASE_HH #include #include /** Downloads, periodically updates and provides a list of talk group IDs and their names. * * @ingroup utils */ class TalkGroupDatabase : public QAbstractTableModel { Q_OBJECT /** A talk group entry in the database. */ class TalkGroup { public: /** Empty constructor. */ TalkGroup(); /** Constructor form name and DMR ID. */ TalkGroup(const QString &name, unsigned number); /** The DMR ID of the talk group. */ unsigned id; /** The Name of the talk group. */ QString name; }; public: /** Constructs a talk group database. * @param updatePeriodDays Specifies the update period of the DB in days. * @param parent Specifies the QObject parent. */ TalkGroupDatabase(unsigned updatePeriodDays=30, QObject *parent=nullptr); /** Returns the number of talk groups. */ qint64 count() const; /** Returns the age of the database in days. */ unsigned dbAge() const; /** Returns the talk group entry at the given index. */ TalkGroup talkgroup(int index) const; /** Loads all entries from the downloaded talk group db. */ bool load(); /** Loads all entries from the talk group db at the specified location. */ bool load(const QString &filename); /** Implements the QAbstractTableModel interface, returns the number of rows (number of entries). */ int rowCount(const QModelIndex &parent=QModelIndex()) const; /** Implements the QAbstractTableModel interface, returns the number of columns. */ int columnCount(const QModelIndex &parent=QModelIndex()) const; /** Implements the QAbstractTableModel interface, return the entry data. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; signals: /** Gets emitted once the talk group database has been loaded. */ void loaded(); /** Gets emitted if the loading of the talk group database fails. */ void error(const QString &msg); public slots: /** Starts the download of the talk group database. */ void download(); private slots: /** Gets called whenever the download is complete. */ void downloadFinished(QNetworkReply *reply); protected: /** Holds all talk groups as id->name table. */ QVector _talkgroups; /** The network access used for downloading. */ QNetworkAccessManager _network; }; #endif // TALKGROUPDATABASE_HH qdmr-0.12.3/lib/transponderdatabase.cc000066400000000000000000000173511501654372000176530ustar00rootroot00000000000000#include "transponderdatabase.hh" #include #include #include #include #include #include #include #include #include "logger.hh" /* ********************************************************************************************* * * Implementation of Transponder * ********************************************************************************************* */ Transponder::Transponder() : _satellite(0), _type(Type::Transmitter), _mode(Mode::CW), _name(), _downlink(), _uplink() { // pass... } unsigned int Transponder::satellite() const { return _satellite; } bool Transponder::isValid() const { return 0 != _satellite; } Transponder::Type Transponder::type() const { return _type; } Transponder::Mode Transponder::mode() const { return _mode; } const QString & Transponder::name() const { return _name; } const Frequency & Transponder::uplink() const { return _uplink; } const Frequency & Transponder::downlink() const { return _downlink; } Transponder Transponder::fromSATNOGS(const QJsonObject &obj) { Transponder t; t._satellite = obj.value("norad_cat_id").toInt(); t._name = obj.value("description").toString(); if ((! obj.contains("alive")) || (! obj.value("alive").toBool())) { //logInfo() << "Skip transponder '" << t._name << "': no alive flag set or false."; return Transponder(); } if ((! obj.contains("mode")) || obj.value("mode").isNull()) { //logInfo() << "Skip transponder '" << t._name << "': no mode set."; return Transponder(); } if ((! obj.contains("downlink_low")) || (obj.value("downlink_low").isNull())) { //logInfo() << "Skip transponder '" << t._name << "': no downlink frequency given."; return Transponder(); } QString tn = obj.value("type").toString(); if (("Transmitter" == tn) || ("Beacon" == tn)) { t._type = Type::Transmitter; } else if ("Transceiver" == tn) { t._type = Type::Transponder; } else { //logInfo() << "Skip transponder '" << t._name << "': unknown type '" << tn << "'."; return Transponder(); } QString mode = obj.value("mode").toString(); if ("FM" == mode) { t._mode = Mode::FM; } else if ("AFSK" == mode) { t._mode = Mode::APRS; } else if ("CW" == mode) { t._mode = Mode::CW; } else if ("BPSK" == mode) { t._mode = Mode::BPSK; } else { //logInfo() << "Skip transponder '" << t._name << "': unknown mode '" << mode << "'."; return Transponder(); } t._downlink = Frequency::fromHz(obj.value("downlink_low").toInt()); if (obj.contains("uplink_low") && (! obj.value("uplink_low").isNull())) t._uplink = Frequency::fromHz(obj.value("uplink_low").toInt()); return t; } /* ********************************************************************************************* * * Implementation of TransponderDatabase * ********************************************************************************************* */ TransponderDatabase::TransponderDatabase(bool autoLoad, unsigned int updatePeriod, QObject *parent) : QAbstractTableModel{parent}, _updatePeriod(updatePeriod), _transponders(), _network() { connect(&_network, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadFinished(QNetworkReply*))); if (autoLoad) load(); } const Transponder & TransponderDatabase::getAt(unsigned int idx) const { return _transponders[idx]; } int TransponderDatabase::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return _transponders.size(); } int TransponderDatabase::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 5; } QVariant TransponderDatabase::data(const QModelIndex &index, int role) const { if (index.row() >= _transponders.size()) return QVariant(); if (Qt::DisplayRole == role) { if (0 == index.column()) { switch (_transponders.at(index.row()).type()) { case Transponder::Type::Transmitter: return "Transmitter"; case Transponder::Type::Transponder: return "Transponder"; } } else if (1 == index.column()) { switch (_transponders.at(index.row()).mode()) { case Transponder::Mode::CW: return "CW"; case Transponder::Mode::BPSK: return "BPSK"; case Transponder::Mode::FM: return "FM"; case Transponder::Mode::APRS: return "APRS"; } } else if (2 == index.column()) { return _transponders.at(index.row()).name(); } else if (3 == index.column()) { return _transponders.at(index.row()).uplink().format(); } else if (4 == index.column()) { return _transponders.at(index.row()).downlink().format(); } } return QVariant(); } TransponderDatabase::const_iterator TransponderDatabase::begin() const { return _transponders.begin(); } TransponderDatabase::const_iterator TransponderDatabase::end() const { return _transponders.end(); } unsigned TransponderDatabase::dbAge() const { QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/transponders.json"; QFileInfo info(path); if (! info.exists()) return -1; return info.lastModified().daysTo(QDateTime::currentDateTime()); } void TransponderDatabase::load() { QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/transponders.json"; if ((! load(path)) || (_updatePeriod < dbAge())) download(); } bool TransponderDatabase::load(const QString &filename) { QFile file(filename); if (! file.open(QIODevice::ReadOnly)) { QString msg = QString("Cannot open transponders '%1': %2").arg(filename).arg(file.errorString()); logError() << msg; emit error(msg); return false; } QByteArray data = file.readAll(); file.close(); QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (doc.isEmpty()) { QString msg = "Failed to load transponders: " + err.errorString(); logError() << msg; emit error(msg); return false; } if (! doc.isArray()) { QString msg = "Failed to load transponders: JSON document is not an array!"; logError() << msg; emit error(msg); return false; } beginResetModel(); QJsonArray array = doc.array(); _transponders.clear(); _transponders.reserve(array.size()); for (int i=0; ierror()) { QString msg = QString("Cannot download transponders: %1").arg(reply->errorString()); logError() << msg; emit error(msg); return; } QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QFile file(path+"/transponders.json"); QDir directory; if ((! directory.exists(path)) && (!directory.mkpath(path))) { QString msg = QString("Cannot create path '%1'.").arg(path); logError() << msg; emit error(msg); return; } if (! file.open(QIODevice::WriteOnly)) { QString msg = QString("Cannot save transponders at '%1'.").arg(file.fileName()); logError() << msg; emit error(msg); return; } file.write(reply->readAll()); file.flush(); file.close(); load(); reply->deleteLater(); } qdmr-0.12.3/lib/transponderdatabase.hh000066400000000000000000000101471501654372000176610ustar00rootroot00000000000000#ifndef TRANSPONDERDATABASE_HH #define TRANSPONDERDATABASE_HH #include #include #include "frequency.hh" /** Represents a single transponder of a satellite. */ struct Transponder { public: /** Possible transponder types. */ enum class Type { Transponder, ///< Proper transponder (repeater). Transmitter ///< Just a transmitter (beacon). }; /** Possible transponder modes. */ enum class Mode { FM, ///< Plain FM. CW, ///< Simple CW. APRS, ///< AFSK APRS. BPSK ///< BPSK. }; public: /** Default constructor. */ Transponder(); /** Returns @c true, if the transponder is valid. * I.e., it is associated with a satellite. */ bool isValid() const; /** Returns the NORAD id of the associated satellite. */ unsigned int satellite() const; /** Returns the transponder type. */ Type type() const; /** Returns the transponder mode. */ Mode mode() const; /** Returns a descriptive name of the transponder. */ const QString &name() const; /** Returns the uplink frequency, if there is one. * An upload frequency is usually missing, if the type is @c Type::Transmitter. */ const Frequency &uplink() const; /** Retruns the downlink frequency. */ const Frequency &downlink() const; public: /** Parses a transponder from the given SatNOGS JSON object. */ static Transponder fromSATNOGS(const QJsonObject &obj); protected: /** Holds the NORAD id of the satellite. */ unsigned int _satellite; /** Holds the transponder type. */ Type _type; /** Holds the transponder mode. */ Mode _mode; /** Holds the name. */ QString _name; /** Holds the downlink frequency. */ Frequency _downlink; /** Holds the uplink frequency. */ Frequency _uplink; }; /** Implements the database of all known transponder. * @ingroup sat */ class TransponderDatabase : public QAbstractTableModel { Q_OBJECT public: /** Just a const iterator over all transponder. */ typedef QVector::const_iterator const_iterator; public: /** Constructor. * @param autoLoad If @c true, the transponder information gets downloaded and loaded * automatically. * @param updatePeriod Specifies the maximum age of the cache in days. * @param parent The QObject parent. */ explicit TransponderDatabase(bool autoLoad, unsigned int updatePeriod = 7, QObject *parent = nullptr); /** The current age of the cache. */ unsigned int dbAge() const; /** Returns the i-th transponder. */ const Transponder &getAt(unsigned int idx) const; /** Implements the QAbstractTableModel interface. * Returns the number of rows in the table. That is, the number of transponder. */ int rowCount(const QModelIndex &parent) const; /** Implements the QAbstractTableModel interface. Returns the number of columns in the table. */ int columnCount(const QModelIndex &parent) const; /** Implements the QAbstractTableModel interface. Returns the data for the cell. */ QVariant data(const QModelIndex &index, int role) const; /** Returns an iterator, pointing at the first transponder. */ const_iterator begin() const; /** Returns an iterator, pointing right after the last transponder. */ const_iterator end() const; public slots: /** Downloads and loads all transponder information. */ void load(); signals: /** Gets emitted once the transponder has been loaded. */ void loaded(); /** Gets emitted if the loading one of the sources fails. */ void error(const QString &msg); public slots: /** Starts the download of the transponder. */ void download(); private slots: /** Gets called whenever the transponder download is complete. */ void downloadFinished(QNetworkReply *reply); protected: /** Loads the transponder information from the given file. */ bool load(const QString &filename); private: /** The update period of the transponders in days. */ unsigned int _updatePeriod; /** Holds all transponder sorted by the catalog number of their sats. */ QVector _transponders; /** The network access used for downloading. */ QNetworkAccessManager _network; }; #endif // TRANSPONDERDATABASE_HH qdmr-0.12.3/lib/tyt_callsigndb.cc000066400000000000000000000142731501654372000166310ustar00rootroot00000000000000#include "tyt_callsigndb.hh" #include #include "utils.hh" #define MAX_CALLSIGNS 122197LL // Maximum number of callsings in DB #define ADDR_CALLSIGN_INDEX 0x00200000 // Start of callsign database #define NUM_INDEX_ENTRIES 4096 #define INDEX_ENTRY_SIZE 0x00000004 // Size of a index entry #define ADDR_CALLSIGNS 0x00204003 // Start of callsign entries #define CALLSIGN_ENTRY_SIZE 0x00000078 // Size of a call-sign entry /* ********************************************************************************************* * * Implementation of TyTCallsignDB::IndexElement * ********************************************************************************************* */ TyTCallsignDB::IndexElement::IndexElement(uint8_t *ptr, size_t size) : Codeplug::Element(ptr, size) { // pass... } TyTCallsignDB::IndexElement::IndexElement(uint8_t *ptr) : Codeplug::Element(ptr, 0x0003 + NUM_INDEX_ENTRIES*INDEX_ENTRY_SIZE) { // pass... } TyTCallsignDB::IndexElement::~IndexElement() { // pass... } void TyTCallsignDB::IndexElement::clear() { setNumEntries(0); for (int i=0; i>16) & 0xff); ptr[1] = ((n>> 8) & 0xff); ptr[2] = ((n>> 0) & 0xff); } void TyTCallsignDB::IndexElement::setIndexEntry(unsigned n, unsigned id, unsigned index) { Entry(_data+0x03 + n*INDEX_ENTRY_SIZE).set(id, index); } /* ********************************************************************************************* * * Implementation of TyTCallsignDB::IndexElement::Entry * ********************************************************************************************* */ TyTCallsignDB::IndexElement::Entry::Entry(uint8_t *ptr, size_t size) : Codeplug::Element(ptr, size) { // pass... } TyTCallsignDB::IndexElement::Entry::Entry(uint8_t *ptr) : Codeplug::Element(ptr, INDEX_ENTRY_SIZE) { // pass... } TyTCallsignDB::IndexElement::Entry::~Entry() { // pass... } void TyTCallsignDB::IndexElement::Entry::clear() { memset(_data, 0xff, INDEX_ENTRY_SIZE); } void TyTCallsignDB::IndexElement::Entry::set(unsigned id, unsigned index) { _data[0] = id>>16; _data[1] = ((id>>8)&0xf0) | ((index>>16) & 0xf); _data[2] = index>>8; _data[3] = index; } /* ********************************************************************************************* * * Implementation of TyTCallsignDB::EntryElement * ********************************************************************************************* */ TyTCallsignDB::EntryElement::EntryElement(uint8_t *ptr, size_t size) : Codeplug::Element(ptr, size) { // pass... } TyTCallsignDB::EntryElement::EntryElement(uint8_t *ptr) : Codeplug::Element(ptr, CALLSIGN_ENTRY_SIZE) { // pass... } TyTCallsignDB::EntryElement::~EntryElement() { // pass... } void TyTCallsignDB::EntryElement::clear() { memset(_data, 0xff, CALLSIGN_ENTRY_SIZE); } void TyTCallsignDB::EntryElement::set(const UserDatabase::User &user) { // Set id *((uint32_t *)(_data + 0x0000)) = qToLittleEndian(user.id); _data[3] = 0xff; // Set call encode_ascii(_data + 0x0004, user.call, 16); // Set name QString name = user.name; if ((! user.surname.isEmpty()) && (100 >= name.length() + 1 + user.surname.size())) name += " " + user.surname; if ((! user.city.isEmpty()) && (100 >= name.length() + 2 + user.city.size())) name += ", " + user.city; if ((! user.state.isEmpty()) && (100 >= name.length() + 2 + user.state.size())) name += ", " + user.state; if ((! user.country.isEmpty()) && (100 >= name.length() + 2 + user.country.size())) name += ", " + user.country; if ((! user.comment.isEmpty()) && (100 >= name.length() + 2 + user.comment.size())) name += ". " + user.comment; encode_ascii(_data + 0x0014, name, 100); } /* ********************************************************************************************* * * Implementation of TyTCallsignDB * ********************************************************************************************* */ TyTCallsignDB::TyTCallsignDB(QObject *parent) : CallsignDB(parent) { // allocate and clear DB memory addImage("TYT Callsign database."); } TyTCallsignDB::~TyTCallsignDB() { // pass... } bool TyTCallsignDB::encode(UserDatabase *db, const Selection &selection, const ErrorStack &err) { Q_UNUSED(err) // Allocate space for callsign db size_t n = std::min(MAX_CALLSIGNS, db->count()); if (selection.hasCountLimit()) n = std::min(n, selection.countLimit()); allocate(n); // Clear DB index clearIndex(); // Select n users and sort them in ascending order of their IDs QVector users; for (unsigned i=0; iuser(i)); std::sort(users.begin(), users.end(), [](const UserDatabase::User &a, const UserDatabase::User &b) { return a.id < b.id; }); // Store number of entries setNumEntries(n); // First index entry int j = 0; setIndexEntry(j++, users[0].id, 1); unsigned cidh = (users[0].id >> 12); // Store users and update index for (unsigned i=0; i> 12); if (idh != cidh) { setIndexEntry(j++,users[i].id, i+1); cidh = idh; } } return true; } void TyTCallsignDB::allocate(unsigned n) { n = std::min(n, unsigned(MAX_CALLSIGNS)); qint64 size = align_size(0x0003 + INDEX_ENTRY_SIZE*NUM_INDEX_ENTRIES + CALLSIGN_ENTRY_SIZE*n, 1024); // allocate & clear memory if (0 == image(0).numElements()) this->image(0).addElement(ADDR_CALLSIGN_INDEX, size); memset(data(ADDR_CALLSIGN_INDEX), 0xff, size); } void TyTCallsignDB::clearIndex() { IndexElement(data(ADDR_CALLSIGN_INDEX)).clear(); } void TyTCallsignDB::setNumEntries(unsigned n) { IndexElement(data(ADDR_CALLSIGN_INDEX)).setNumEntries(n); } void TyTCallsignDB::setIndexEntry(unsigned n, unsigned id, unsigned index) { IndexElement(data(ADDR_CALLSIGN_INDEX)).setIndexEntry(n, id, index); } void TyTCallsignDB::setEntry(unsigned n, const UserDatabase::User &user) { // Get pointer to entry EntryElement(data(ADDR_CALLSIGNS + n*CALLSIGN_ENTRY_SIZE)).set(user); } qdmr-0.12.3/lib/tyt_callsigndb.hh000066400000000000000000000070401501654372000166350ustar00rootroot00000000000000#ifndef TYTCALLSIGNDB_HH #define TYTCALLSIGNDB_HH #include "codeplug.hh" #include "callsigndb.hh" #include "userdatabase.hh" /** Base class for all call-sign DBs of TyT/Retevis devices. * * @section tytcdb Callsign database structure * * * * * * *
      Start End Size Content
      Callsign database 0x0200000-0x1000000
      0x200000 0x204004 0x04004 Callsign database index table, see @c TyTCallsignDB::IndexEntryElement
      0x204004 0xffffdc 0xdfbfd8 max 122197 callsign database entries, see @c TyTCallsignDB::EntryElement.
      0xffffdc 0x1000000 0x00025 Padding, filled with @c 0xff.
      * @ingroup tyt */ class TyTCallsignDB : public CallsignDB { Q_OBJECT public: /** Represents a search index over the complete call-sign database. * * Memory layout of encoded call-sign/user database: * @verbinclude tytcallsigndbindex.txt */ class IndexElement: public Codeplug::Element { public: /** Represents an index entry, a pair of DMR ID and call-sign DB index. * * Memory layout of encoded call-sign/user database index entry: * @verbinclude tytcallsigndbindexentry.txt */ class Entry: public Codeplug::Element { protected: /** Hidden constructor. */ Entry(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit Entry(uint8_t *ptr); /** Destructor. */ virtual ~Entry(); void clear(); /** Sets the index entry. */ virtual void set(unsigned id, unsigned index); }; protected: /** Hidden constructor. */ IndexElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit IndexElement(uint8_t *ptr); /** Destructor. */ virtual ~IndexElement(); void clear(); /** Sets the number of entries in the DB. */ virtual void setNumEntries(unsigned n); /** Sets the given index entry. */ virtual void setIndexEntry(unsigned n, unsigned id, unsigned index); }; /** Represents an entry within the call-sign database. * The call-sign DB entries must be ordered by their DMR IDs. * * Memory layout of encoded call-sign/user database index entry: * @verbinclude tytcallsigndbentry.txt */ class EntryElement: public Codeplug::Element { protected: /** Hidden constructor. */ EntryElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit EntryElement(uint8_t *ptr); /** Destructor. */ virtual ~EntryElement(); void clear(); /** Encodes the given user. */ virtual void set(const UserDatabase::User &user); }; protected: /** Hidden constructor. Use one of the device specific call-sign DB classes. */ explicit TyTCallsignDB(QObject *parent=nullptr); public: /** Destructor. */ virtual ~TyTCallsignDB(); bool encode(UserDatabase *db, const Selection &selection,const ErrorStack &err=ErrorStack()); protected: /** Allocates required space for index and @c n call-signs. */ virtual void allocate(unsigned n); /** Clears the call-sign DB index. */ virtual void clearIndex(); /** Sets the number of entries in the DB. */ virtual void setNumEntries(unsigned n); /** Sets the given index entry. */ virtual void setIndexEntry(unsigned n, unsigned id, unsigned index); /** Sets a given call-sign entry. */ virtual void setEntry(unsigned n, const UserDatabase::User &user); }; #endif // TYTCALLSIGNDB_HH qdmr-0.12.3/lib/tyt_codeplug.cc000066400000000000000000002527301501654372000163330ustar00rootroot00000000000000#include "tyt_codeplug.hh" #include "config.hh" #include "utils.hh" #include "channel.hh" #include "gpssystem.hh" #include "config.h" #include "logger.hh" #include "tyt_extensions.hh" #include "encryptionextension.hh" #include "commercial_extension.hh" #include #include #include #define SETTINGS_SIZE 0x000090 #define CONTACT_SIZE 0x000024 #define MENUSETTINGS_SIZE 0x000010 /* ******************************************************************************************** * * Implementation of TyTCodeplug::ChannelElement * ******************************************************************************************** */ TyTCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size) : Codeplug::Element(ptr, size) { // pass... } TyTCodeplug::ChannelElement::ChannelElement(uint8_t *ptr) : Codeplug::Element(ptr, size()) { // pass... } TyTCodeplug::ChannelElement::~ChannelElement() { // pass... } bool TyTCodeplug::ChannelElement::isValid() const { return Element::isValid() && QChar::isPrint(getUInt16_le(Offset::name())); } void TyTCodeplug::ChannelElement::clear() { Element::clear(); setMode(MODE_ANALOG); setBandwidth(FMChannel::Bandwidth::Narrow); enableAutoScan(0); setBit(0, 1); setBit(0,2); enableLoneWorker(false); enableTalkaround(false); enableRXOnly(false); setTimeSlot(DMRChannel::TimeSlot::TS1); setColorCode(1); setPrivacyIndex(0); setPrivacyType(PRIV_NONE); enablePrivateCallConfirm(false); enableDataCallConfirm(false); setRXRefFrequency(TyTChannelExtension::RefFrequency::Low); clearBit(3,2); enableEmergencyAlarmACK(false); clearBit(3,4); setBit(3,5); setBit(3,6); enableDisplayPTTId(true); setTXRefFrequency(TyTChannelExtension::RefFrequency::Low); setBit(4,2); clearBit(4,3); enableVOX(false); setBit(4,5); setAdmitCriterion(ADMIT_ALWAYS); clearBit(5,0); setContactIndex(0); resetTXTimeOut(); clearBit(8,6); setTXTimeOutRekeyDelay(0); setEmergencySystemIndex(0); setScanListIndex(0); setGroupListIndex(0); setPositioningSystemIndex(0); for (uint8_t i=0; i<8; i++) setDTMFDecode(i, false); setRXFrequency(Frequency::fromMHz(400)); setTXFrequency(Frequency::fromMHz(400)); setRXSignaling(SelectiveCall()); setTXSignaling(SelectiveCall()); setRXSignalingSystemIndex(0); setTXSignalingSystemIndex(0); setBit(30,2); setBit(30,3); setBit(30,4); setBit(30,5); setBit(30,6); setBit(30,7); enableTXGPSInfo(true); enableRXGPSInfo(true); setBit(31, 5); setBit(31, 6); setBit(31, 7); memset((_data+32), 0x00, sizeof(32)); } TyTCodeplug::ChannelElement::Mode TyTCodeplug::ChannelElement::mode() const { return TyTCodeplug::ChannelElement::Mode(getUInt2(Offset::mode())); } void TyTCodeplug::ChannelElement::setMode(Mode mode) { setUInt2(Offset::mode(), uint8_t(mode)); } FMChannel::Bandwidth TyTCodeplug::ChannelElement::bandwidth() const { if (0 == getUInt2(Offset::bandwidth())) return FMChannel::Bandwidth::Narrow; return FMChannel::Bandwidth::Wide; } void TyTCodeplug::ChannelElement::setBandwidth(FMChannel::Bandwidth bw) { if (FMChannel::Bandwidth::Narrow == bw) setUInt2(Offset::bandwidth(), BW_12_5_KHZ); else setUInt2(Offset::bandwidth(), BW_25_KHZ); } bool TyTCodeplug::ChannelElement::autoScan() const { return getBit(Offset::autoscan()); } void TyTCodeplug::ChannelElement::enableAutoScan(bool enable) { setBit(Offset::autoscan(), enable); } bool TyTCodeplug::ChannelElement::loneWorker() const { return getBit(Offset::loneworker()); } void TyTCodeplug::ChannelElement::enableLoneWorker(bool enable) { setBit(Offset::loneworker(), enable); } bool TyTCodeplug::ChannelElement::talkaround() const { return ! getBit(Offset::talkaround()); } void TyTCodeplug::ChannelElement::enableTalkaround(bool enable) { setBit(Offset::talkaround(), !enable); } bool TyTCodeplug::ChannelElement::rxOnly() const { return getBit(Offset::rxonly()); } void TyTCodeplug::ChannelElement::enableRXOnly(bool enable) { setBit(Offset::rxonly(), enable); } DMRChannel::TimeSlot TyTCodeplug::ChannelElement::timeSlot() const { if (2 == getUInt2(Offset::timeslot())) return DMRChannel::TimeSlot::TS2; return DMRChannel::TimeSlot::TS1; } void TyTCodeplug::ChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) { if (DMRChannel::TimeSlot::TS1 == ts) setUInt2(Offset::timeslot(), 1); else setUInt2(Offset::timeslot(),2); } uint8_t TyTCodeplug::ChannelElement::colorCode() const { return getUInt4(Offset::colorcode()); } void TyTCodeplug::ChannelElement::setColorCode(uint8_t cc) { cc = std::min(uint8_t(16), cc); setUInt4(Offset::colorcode(), cc); } uint8_t TyTCodeplug::ChannelElement::privacyIndex() const { return getUInt4(Offset::privacyIndex()); } void TyTCodeplug::ChannelElement::setPrivacyIndex(uint8_t idx) { setUInt4(Offset::privacyIndex(), idx); } TyTCodeplug::ChannelElement::PrivacyType TyTCodeplug::ChannelElement::privacyType() const { return TyTCodeplug::ChannelElement::PrivacyType(getUInt2(Offset::privacyType())); } void TyTCodeplug::ChannelElement::setPrivacyType(TyTCodeplug::ChannelElement::PrivacyType type) { setUInt2(Offset::privacyType(), uint8_t(type)); } bool TyTCodeplug::ChannelElement::privateCallConfirm() const { return getBit(Offset::privateCallConfirm()); } void TyTCodeplug::ChannelElement::enablePrivateCallConfirm(bool enable) { setBit(Offset::privateCallConfirm(), enable); } bool TyTCodeplug::ChannelElement::dataCallConfirm() const { return getBit(Offset::dataCallConfirm()); } void TyTCodeplug::ChannelElement::enableDataCallConfirm(bool enable) { setBit(Offset::dataCallConfirm(), enable); } TyTChannelExtension::RefFrequency TyTCodeplug::ChannelElement::rxRefFrequency() const { return TyTChannelExtension::RefFrequency(getUInt2(Offset::rxRefFrequency())); } void TyTCodeplug::ChannelElement::setRXRefFrequency(TyTChannelExtension::RefFrequency ref) { setUInt2(Offset::rxRefFrequency(), uint8_t(ref)); } bool TyTCodeplug::ChannelElement::emergencyAlarmACK() const { return getBit(Offset::emergencyAlarmACK()); } void TyTCodeplug::ChannelElement::enableEmergencyAlarmACK(bool enable) { setBit(Offset::emergencyAlarmACK(), enable); } bool TyTCodeplug::ChannelElement::displayPTTId() const { return ! getBit(Offset::displayPTTId()); } void TyTCodeplug::ChannelElement::enableDisplayPTTId(bool enable) { setBit(Offset::displayPTTId(), !enable); } TyTChannelExtension::RefFrequency TyTCodeplug::ChannelElement::txRefFrequency() const { return TyTChannelExtension::RefFrequency(getUInt2(Offset::txRefFrequency())); } void TyTCodeplug::ChannelElement::setTXRefFrequency(TyTChannelExtension::RefFrequency ref) { setUInt2(Offset::txRefFrequency(), uint8_t(ref)); } bool TyTCodeplug::ChannelElement::vox() const { return getBit(Offset::vox()); } void TyTCodeplug::ChannelElement::enableVOX(bool enable) { setBit(Offset::vox(), enable); } TyTCodeplug::ChannelElement::Admit TyTCodeplug::ChannelElement::admitCriterion() const { return TyTCodeplug::ChannelElement::Admit(getUInt2(Offset::admitCriterion())); } void TyTCodeplug::ChannelElement::setAdmitCriterion(TyTCodeplug::ChannelElement::Admit admit) { setUInt2(Offset::admitCriterion(), uint8_t(admit)); } uint16_t TyTCodeplug::ChannelElement::contactIndex() const { return getUInt16_le(Offset::contactIndex()); } void TyTCodeplug::ChannelElement::setContactIndex(uint16_t idx) { setUInt16_le(Offset::contactIndex(), idx); } bool TyTCodeplug::ChannelElement::txTimeOutDisabled() const { return 0 == getUInt6(Offset::txTimeOut()); } Interval TyTCodeplug::ChannelElement::txTimeOut() const { return Interval::fromSeconds(getUInt6(Offset::txTimeOut())*15); } void TyTCodeplug::ChannelElement::setTXTimeOut(const Interval &tot) { return setUInt6(Offset::txTimeOut(), tot.seconds()/15); } void TyTCodeplug::ChannelElement::resetTXTimeOut() { setUInt6(Offset::txTimeOut(), 0); } uint8_t TyTCodeplug::ChannelElement::txTimeOutRekeyDelay() const { return getUInt8(Offset::txTimeOutRekeyDelay()); } void TyTCodeplug::ChannelElement::setTXTimeOutRekeyDelay(uint8_t delay) { return setUInt8(Offset::txTimeOutRekeyDelay(), delay); } uint8_t TyTCodeplug::ChannelElement::emergencySystemIndex() const { return getUInt8(Offset::emergencySystemIndex()); } void TyTCodeplug::ChannelElement::setEmergencySystemIndex(uint8_t delay) { return setUInt8(Offset::emergencySystemIndex(), delay); } uint8_t TyTCodeplug::ChannelElement::scanListIndex() const { return getUInt8(Offset::scanListIndex()); } void TyTCodeplug::ChannelElement::setScanListIndex(uint8_t idx) { return setUInt8(Offset::scanListIndex(), idx); } uint8_t TyTCodeplug::ChannelElement::groupListIndex() const { return getUInt8(Offset::groupListIndex()); } void TyTCodeplug::ChannelElement::setGroupListIndex(uint8_t idx) { return setUInt8(Offset::groupListIndex(), idx); } uint8_t TyTCodeplug::ChannelElement::positioningSystemIndex() const { return getUInt8(Offset::positioningSystemIndex()); } void TyTCodeplug::ChannelElement::setPositioningSystemIndex(uint8_t idx) { return setUInt8(Offset::positioningSystemIndex(), idx); } bool TyTCodeplug::ChannelElement::dtmfDecode(uint8_t idx) const { return getBit(Offset::dtmfDecode(), idx); } void TyTCodeplug::ChannelElement::setDTMFDecode(uint8_t idx, bool enable) { setBit(Offset::dtmfDecode(), idx, enable); } Frequency TyTCodeplug::ChannelElement::rxFrequency() const { return Frequency::fromHz(getBCD8_le(Offset::rxFrequency())*10); } void TyTCodeplug::ChannelElement::setRXFrequency(const Frequency &freq_Hz) { return setBCD8_le(Offset::rxFrequency(), freq_Hz.inHz()/10); } Frequency TyTCodeplug::ChannelElement::txFrequency() const { return Frequency::fromHz(getBCD8_le(Offset::txFrequency())*10); } void TyTCodeplug::ChannelElement::setTXFrequency(const Frequency &freq_Hz) { return setBCD8_le(Offset::txFrequency(), freq_Hz.inHz()/10); } SelectiveCall TyTCodeplug::ChannelElement::rxSignaling() const { return decode_ctcss_tone_table(getUInt16_le(24)); } void TyTCodeplug::ChannelElement::setRXSignaling(const SelectiveCall &code) { setUInt16_le(24, encode_ctcss_tone_table(code)); } SelectiveCall TyTCodeplug::ChannelElement::txSignaling() const { return decode_ctcss_tone_table(getUInt16_le(26)); } void TyTCodeplug::ChannelElement::setTXSignaling(const SelectiveCall &code) { setUInt16_le(26, encode_ctcss_tone_table(code)); } uint8_t TyTCodeplug::ChannelElement::rxSignalingSystemIndex() const { return getUInt8(Offset::rxSignalingSystemIndex()); } void TyTCodeplug::ChannelElement::setRXSignalingSystemIndex(uint8_t idx) { setUInt8(Offset::rxSignalingSystemIndex(), idx); } uint8_t TyTCodeplug::ChannelElement::txSignalingSystemIndex() const { return getUInt8(Offset::txSignalingSystemIndex()); } void TyTCodeplug::ChannelElement::setTXSignalingSystemIndex(uint8_t idx) { setUInt8(Offset::txSignalingSystemIndex(), idx); } bool TyTCodeplug::ChannelElement::txGPSInfo() const { return ! getBit(Offset::txGPSInfo()); } void TyTCodeplug::ChannelElement::enableTXGPSInfo(bool enable) { setBit(Offset::txGPSInfo(), !enable); } bool TyTCodeplug::ChannelElement::rxGPSInfo() const { return !getBit(Offset::rxGPSInfo()); } void TyTCodeplug::ChannelElement::enableRXGPSInfo(bool enable) { setBit(Offset::rxGPSInfo(), !enable); } QString TyTCodeplug::ChannelElement::name() const { return readUnicode(Offset::name(), Limit::nameLength(), 0x0000); } void TyTCodeplug::ChannelElement::setName(const QString &name) { return writeUnicode(Offset::name(), name, Limit::nameLength(), 0x0000); } Channel * TyTCodeplug::ChannelElement::toChannelObj(const ErrorStack &err) const { if (! isValid()) { errMsg(err) << "Cannot decode invalid channel."; return nullptr; } Channel *ch = nullptr; TyTChannelExtension *ex = new TyTChannelExtension(); // decode power setting if (MODE_ANALOG == mode()) { FMChannel::Admit admit_crit; switch(admitCriterion()) { case ADMIT_ALWAYS: admit_crit = FMChannel::Admit::Always; break; case ADMIT_TONE: admit_crit = FMChannel::Admit::Tone; break; case ADMIT_CH_FREE: admit_crit = FMChannel::Admit::Free; break; default: admit_crit = FMChannel::Admit::Free; break; } FMChannel *ach = new FMChannel(); ach->setAdmit(admit_crit); ach->setSquelchDefault(); ach->setRXTone(rxSignaling()); ach->setTXTone(txSignaling()); ach->setBandwidth(bandwidth()); // Apply analog channel extension settings ex->enableDisplayPTTId(displayPTTId()); ch = ach; } else if (MODE_DIGITAL == mode()) { DMRChannel::Admit admit_crit; switch(admitCriterion()) { case ADMIT_ALWAYS: admit_crit = DMRChannel::Admit::Always; break; case ADMIT_CH_FREE: admit_crit = DMRChannel::Admit::Free; break; case ADMIT_COLOR: admit_crit = DMRChannel::Admit::ColorCode; break; default: admit_crit = DMRChannel::Admit::Free; break; } DMRChannel *dch = new DMRChannel(); dch->setAdmit(admit_crit); dch->setColorCode(colorCode()); dch->setTimeSlot(timeSlot()); // Apply digital channel extension settings ex->enablePrivateCallConfirmed(privateCallConfirm()); ex->enableDataCallConfirmed(dataCallConfirm()); ex->enableEmergencyAlarmConfirmed(emergencyAlarmACK()); // If encryption is enabled, Add commercial extension to channel if needed // the key will be linked later if ((PRIV_NONE != privacyType()) && (nullptr == dch->commercialExtension())) dch->setCommercialExtension(new CommercialChannelExtension()); // done ch = dch; } else { errMsg(err) << "Cannot decode channel. Channel type " << mode() << " unknown!"; delete ex; return nullptr; } // Common settings ch->setName(name()); ch->setRXFrequency(rxFrequency()); ch->setTXFrequency(txFrequency()); ch->setTimeout(txTimeOut().seconds()); ch->setRXOnly(rxOnly()); // Power setting must be overridden by specialized class ch->setDefaultPower(); if (vox()) ch->setVOXDefault(); else ch->disableVOX(); // Apply common channel settings ex->enableLoneWorker(loneWorker()); ex->enableAutoScan(autoScan()); ex->enableTalkaround(talkaround()); ex->setRXRefFrequency(rxRefFrequency()); ex->setTXRefFrequency(txRefFrequency()); ch->setTyTChannelExtension(ex); return ch; } bool TyTCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err) const { if (! isValid()) { errMsg(err) << "Cannot link an invalid channel."; return false; } if (scanListIndex() && ctx.has(scanListIndex())) { c->setScanList(ctx.get(scanListIndex())); } if (MODE_ANALOG == mode()) { // Nothing further to link for analog channels. return true; } else if ((MODE_DIGITAL == mode()) && (c->is())){ DMRChannel *dc = c->as(); if (contactIndex() && ctx.has(contactIndex())) { dc->setTXContactObj(ctx.get(contactIndex())); } if (groupListIndex() && ctx.has(groupListIndex())) { dc->setGroupListObj(ctx.get(groupListIndex())); } if (positioningSystemIndex() && ctx.has(positioningSystemIndex())) { dc->setAPRSObj(ctx.get(positioningSystemIndex())); } // Link encryption key if defined if (PRIV_NONE != privacyType()) { if (nullptr == dc->commercialExtension()) { errMsg(err) << "Cannot link encryption key: No commercial extension set."; return false; } if (PRIV_BASIC == privacyType()) { if (! ctx.has(privacyIndex())) { errMsg(err) << "Cannot link encryption key: No basic key with index " << privacyIndex() << " defined."; return false; } dc->commercialExtension()->setEncryptionKey(ctx.get(privacyIndex())); } else if (PRIV_ENHANCED == privacyType()) { if (! ctx.has(privacyIndex())) { errMsg(err) << "Cannot link encryption key: No AES (enhances) key with index " << privacyIndex() << " defined."; return false; } dc->commercialExtension()->setEncryptionKey(ctx.get(privacyIndex())); } else { errMsg(err) << "Unknown encryption key type " << privacyType() << "."; return false; } } return true; } errMsg(err) << "Cannot link channel '" << c->name() << "' invalid channel type " << mode() << "."; return false; } void TyTCodeplug::ChannelElement::fromChannelObj(const Channel *chan, Context &ctx) { setName(chan->name()); setRXFrequency(chan->rxFrequency()); setTXFrequency(chan->txFrequency()); enableRXOnly(chan->rxOnly()); if (chan->defaultTimeout()) setTXTimeOut(Interval::fromSeconds(ctx.config()->settings()->tot())); else setTXTimeOut(Interval::fromSeconds(chan->timeout())); if (chan->scanList()) setScanListIndex(ctx.index(chan->scanList())); else setScanListIndex(0); // Enable vox bool defaultVOXEnabled = (chan->defaultVOX() && (!ctx.config()->settings()->voxDisabled())); bool channelVOXEnabled = (! (chan->voxDisabled()||chan->defaultVOX())); enableVOX(defaultVOXEnabled || channelVOXEnabled); // power setting must be set by specialized element if (chan->is()) { const DMRChannel *dchan = chan->as(); setMode(MODE_DIGITAL); switch (dchan->admit()) { case DMRChannel::Admit::Always: setAdmitCriterion(ADMIT_ALWAYS); break; case DMRChannel::Admit::Free: setAdmitCriterion(ADMIT_CH_FREE); break; case DMRChannel::Admit::ColorCode: setAdmitCriterion(ADMIT_COLOR); break; } setColorCode(dchan->colorCode()); setTimeSlot(dchan->timeSlot()); if (dchan->groupListObj()) setGroupListIndex(ctx.index(dchan->groupListObj())); else setGroupListIndex(0); if (dchan->txContactObj()) setContactIndex(ctx.index(dchan->txContactObj())); setBandwidth(FMChannel::Bandwidth::Narrow); setRXSignaling(SelectiveCall()); setTXSignaling(SelectiveCall()); if (dchan->aprsObj() && dchan->aprsObj()->is()) { setPositioningSystemIndex(ctx.index(dchan->aprsObj()->as())); enableTXGPSInfo(true); enableRXGPSInfo(false); } if (chan->tytChannelExtension()) { enablePrivateCallConfirm(chan->tytChannelExtension()->privateCallConfirmed()); enableDataCallConfirm(chan->tytChannelExtension()->dataCallConfirmed()); enableEmergencyAlarmACK(chan->tytChannelExtension()->emergencyAlarmConfirmed()); } // Link encryption key if set if (dchan->commercialExtension() && dchan->commercialExtension()->encryptionKey()) { // Check for index if (0 > ctx.index(dchan->commercialExtension()->encryptionKey())) { logError() << "Cannot encode encryption key '" << dchan->commercialExtension()->encryptionKey()->name() << "': Not indexed."; } else if (dchan->commercialExtension()->encryptionKey()->is()) { setPrivacyType(PRIV_BASIC); setPrivacyIndex(ctx.index(dchan->commercialExtension()->encryptionKey())); } else if (dchan->commercialExtension()->encryptionKey()->is()) { setPrivacyType(PRIV_ENHANCED); setPrivacyIndex(ctx.index(dchan->commercialExtension()->encryptionKey())); } else { logInfo() << "Ignore unknown encryption key type " << dchan->commercialExtension()->encryptionKey()->metaObject()->className() << " for DMR channel."; } } } else if (chan->is()) { const FMChannel *achan = chan->as(); setMode(MODE_ANALOG); setBandwidth(achan->bandwidth()); // Squelch must be set by specialized element switch (achan->admit()) { case FMChannel::Admit::Always: setAdmitCriterion(ADMIT_ALWAYS); break; case FMChannel::Admit::Free: setAdmitCriterion(ADMIT_CH_FREE); break; case FMChannel::Admit::Tone: setAdmitCriterion(ADMIT_TONE); break; } setRXSignaling(achan->rxTone()); setTXSignaling(achan->txTone()); setGroupListIndex(0); setContactIndex(0); if (chan->tytChannelExtension()) { enableDisplayPTTId(chan->tytChannelExtension()->displayPTTId()); } } // Apply channel extension (if set) if (chan->tytChannelExtension()) { enableLoneWorker(chan->tytChannelExtension()->loneWorker()); enableAutoScan(chan->tytChannelExtension()->autoScan()); enableTalkaround(chan->tytChannelExtension()->talkaround()); setRXRefFrequency(chan->tytChannelExtension()->rxRefFrequency()); setTXRefFrequency(chan->tytChannelExtension()->txRefFrequency()); } } /* ******************************************************************************************** * * Implementation of TyTCodeplug::ContactElement * ******************************************************************************************** */ TyTCodeplug::ContactElement::ContactElement(uint8_t *ptr, size_t size) : Codeplug::Element(ptr, size) { // pass... } TyTCodeplug::ContactElement::ContactElement(uint8_t *ptr) : Codeplug::Element(ptr, CONTACT_SIZE) { // pass... } TyTCodeplug::ContactElement::~ContactElement() { // pass... } bool TyTCodeplug::ContactElement::isValid() const { return Element::isValid() && (0 != getUInt2(3, 0)) && (0x0000 != getUInt16_be(4)) && (0xffff != getUInt16_be(4)); } void TyTCodeplug::ContactElement::clear() { memset(_data, 0xff, 3); // clear DMR ID setUInt2(3, 0, 0); // type=0 setBit(3,2, 0); setBit(3,3, 0); setBit(3,4, 0); // unused = 0 enableRingTone(false); setBit(3,6, 1); setBit(3,7, 1); // unknown = 1 memset(_data+0x04, 0x00, 2*16); } uint32_t TyTCodeplug::ContactElement::dmrId() const { return getUInt24_le(0); } void TyTCodeplug::ContactElement::setDMRId(uint32_t id) { setUInt24_le(0, id); } bool TyTCodeplug::ContactElement::ringTone() const { return getBit(3, 5); } void TyTCodeplug::ContactElement::enableRingTone(bool enable) { setBit(3, 5, enable); } DMRContact::Type TyTCodeplug::ContactElement::callType() const { switch(getUInt2(3,0)) { case 1: return DMRContact::GroupCall; case 2: return DMRContact::PrivateCall; case 3: return DMRContact::AllCall; default: break; } return DMRContact::PrivateCall; } void TyTCodeplug::ContactElement::setCallType(DMRContact::Type type) { switch (type) { case DMRContact::GroupCall: setUInt2(3,0, 1); break; case DMRContact::PrivateCall: setUInt2(3,0, 2); break; case DMRContact::AllCall: setUInt2(3,0, 3); break; } } QString TyTCodeplug::ContactElement::name() const { return readUnicode(4, 16, 0x0000); } void TyTCodeplug::ContactElement::setName(const QString &nm) { writeUnicode(4, nm, 16, 0x0000); } DMRContact * TyTCodeplug::ContactElement::toContactObj() const { return new DMRContact(callType(), name(), dmrId(), ringTone()); } bool TyTCodeplug::ContactElement::fromContactObj(const DMRContact *cont) { if (nullptr == cont) return false; setDMRId(cont->number()); setName(cont->name()); setCallType(cont->type()); enableRingTone(cont->ring()); return true; } /* ******************************************************************************************** * * Implementation of TyTCodeplug::ZoneElement * ******************************************************************************************** */ TyTCodeplug::ZoneElement::ZoneElement(uint8_t *ptr, size_t size) : Codeplug::Element(ptr, size) { // pass... } TyTCodeplug::ZoneElement::ZoneElement(uint8_t *ptr) : Codeplug::Element(ptr, 0x0040) { // pass... } TyTCodeplug::ZoneElement::~ZoneElement() { // pass... } bool TyTCodeplug::ZoneElement::isValid() const { return Element::isValid() && (0x0000 != getUInt16_be(0)) && (0xffff != getUInt16_be(0)); } void TyTCodeplug::ZoneElement::clear() { memset(_data, 0x00, 0x40); } QString TyTCodeplug::ZoneElement::name() const { return readUnicode(0, 16); } void TyTCodeplug::ZoneElement::setName(const QString &name) { writeUnicode(0, name, 16); } uint16_t TyTCodeplug::ZoneElement::memberIndex(unsigned n) const { return getUInt16_le(0x20 + n*2); } void TyTCodeplug::ZoneElement::setMemberIndex(unsigned n, uint16_t idx) { setUInt16_le(0x20 + n*2, idx); } bool TyTCodeplug::ZoneElement::fromZoneObj(const Zone *zone, Context &ctx) { setName(zone->name()); for (int i=0; i<16; i++) { if (i < zone->A()->count()) setMemberIndex(i, ctx.index(zone->A()->get(i))); else setMemberIndex(i, 0); } return true; } Zone * TyTCodeplug::ZoneElement::toZoneObj() const { if (!isValid()) return nullptr; return new Zone(name()); } bool TyTCodeplug::ZoneElement::linkZone(Zone *zone, Context &ctx) const { if (! isValid()) return false; for (int i=0; ((i<16) && memberIndex(i)); i++) { if (! ctx.has(memberIndex(i))) { logWarn() << "Cannot link channel with index " << memberIndex(i) << " to zone '" << zone->name() << "': channel not defined."; continue; } zone->A()->add(ctx.get(memberIndex(i))); } return true; } /* ******************************************************************************************** * * Implementation of TyTCodeplug::GroupListElement * ******************************************************************************************** */ TyTCodeplug::GroupListElement::GroupListElement(uint8_t *ptr, size_t size) : Codeplug::Element(ptr, size) { // pass... } TyTCodeplug::GroupListElement::GroupListElement(uint8_t *ptr) : Codeplug::Element(ptr, 0x0060) { // pass... } TyTCodeplug::GroupListElement::~GroupListElement() { // pass... } bool TyTCodeplug::GroupListElement::isValid() const { return Element::isValid() && (0x0000 != getUInt16_be(0)); } void TyTCodeplug::GroupListElement::clear() { memset(_data, 0x00, 0x60); } QString TyTCodeplug::GroupListElement::name() const { return readUnicode(0, 16); } void TyTCodeplug::GroupListElement::setName(const QString &nm) { writeUnicode(0, nm, 16); } uint16_t TyTCodeplug::GroupListElement::memberIndex(unsigned n) const { return getUInt16_le(0x20 + n*2); } void TyTCodeplug::GroupListElement::setMemberIndex(unsigned n, uint16_t idx) { setUInt16_le(0x20 + 2*n, idx); } bool TyTCodeplug::GroupListElement::fromGroupListObj(const RXGroupList *lst, Context &ctx) { setName(lst->name()); int j=0; // Iterate over all 32 entries in the codeplug for (int i=0; i<32; i++) { // Skip non-private-call entries while((lst->count() > j) && (DMRContact::GroupCall != lst->contact(j)->type())) { logWarn() << "Contact '" << lst->contact(i)->name() << "' in group list '" << lst->name() << "' is not a group call. Skip entry."; j++; } if (lst->count() > j) { setMemberIndex(i, ctx.index(lst->contact(j))); j++; } else { // clear entry setMemberIndex(i, 0); } } return true; } RXGroupList * TyTCodeplug::GroupListElement::toGroupListObj(Context &ctx) { Q_UNUSED(ctx) if (! isValid()) return nullptr; return new RXGroupList(name()); } bool TyTCodeplug::GroupListElement::linkGroupListObj(RXGroupList *lst, Context &ctx) { if (! isValid()) return false; for (int i=0; (i<32) && memberIndex(i); i++) { if (! ctx.has(memberIndex(i))) { logWarn() << "Cannot link contact " << memberIndex(i) << " to group list '" << name() << "': Invalid contact index. Ignored."; continue; } //logDebug() << "Add contact idx=" << memberIndex(i) << " to group list " << lst->name() << "."; if (0 > lst->addContact(ctx.get(memberIndex(i)))) { logWarn() << "Cannot add contact '" << ctx.get(memberIndex(i))->name() << "' at idx=" << memberIndex(i) << "."; continue; } } return true; } /* ******************************************************************************************** * * Implementation of TyTCodeplug::ScanListElement * ******************************************************************************************** */ TyTCodeplug::ScanListElement::ScanListElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } TyTCodeplug::ScanListElement::ScanListElement(uint8_t *ptr) : Element(ptr, 0x0068) { // pass... } TyTCodeplug::ScanListElement::~ScanListElement() { // pass... } bool TyTCodeplug::ScanListElement::isValid() const { return Element::isValid() && (0x0000 != getUInt16_be(0)); } void TyTCodeplug::ScanListElement::clear() { memset(_data, 0, 2*16); setPriorityChannel1Index(0xffff); setPriorityChannel2Index(0xffff); setTXChannelIndex(0xffff); setUInt8(0x26, 0xf1); setHoldTime(500); setPrioritySampleTime(2000); setUInt8(0x29, 0xff); memset(_data+0x2a, 0, 2*31); } QString TyTCodeplug::ScanListElement::name() const { return readUnicode(0, 16); } void TyTCodeplug::ScanListElement::setName(const QString &name) { writeUnicode(0, name, 16); } uint16_t TyTCodeplug::ScanListElement::priorityChannel1Index() const { return getUInt16_le(0x20); } void TyTCodeplug::ScanListElement::setPriorityChannel1Index(uint16_t idx) { setUInt16_le(0x20, idx); } uint16_t TyTCodeplug::ScanListElement::priorityChannel2Index() const { return getUInt16_le(0x22); } void TyTCodeplug::ScanListElement::setPriorityChannel2Index(uint16_t idx) { setUInt16_le(0x22, idx); } uint16_t TyTCodeplug::ScanListElement::txChannelIndex() const { return getUInt16_le(0x24); } void TyTCodeplug::ScanListElement::setTXChannelIndex(uint16_t idx) { setUInt16_le(0x24, idx); } unsigned TyTCodeplug::ScanListElement::holdTime() const { return unsigned(getUInt8(0x27))*25; } void TyTCodeplug::ScanListElement::setHoldTime(unsigned time) { setUInt8(0x27, time/25); } unsigned TyTCodeplug::ScanListElement::prioritySampleTime() const { return unsigned(getUInt8(0x28))*250; } void TyTCodeplug::ScanListElement::setPrioritySampleTime(unsigned time) { setUInt8(0x28, time/250); } uint16_t TyTCodeplug::ScanListElement::memberIndex(unsigned n) const { return getUInt16_le(0x2a + 2*n); } void TyTCodeplug::ScanListElement::setMemberIndex(unsigned n, uint16_t idx) { setUInt16_le(0x2a + 2*n, idx); } bool TyTCodeplug::ScanListElement::fromScanListObj(const ScanList *lst, Context &ctx) { // Set name setName(lst->name()); // Set priority channel 1 if (lst->primaryChannel() && (SelectedChannel::get() == lst->primaryChannel())) setPriorityChannel1Index(0); else if (lst->primaryChannel()) setPriorityChannel1Index(ctx.index(lst->primaryChannel())); else setPriorityChannel1Index(0xffff); // Set priority channel 2 if (lst->secondaryChannel() && (SelectedChannel::get() == lst->secondaryChannel())) setPriorityChannel2Index(0); else if (lst->secondaryChannel()) setPriorityChannel2Index(ctx.index(lst->secondaryChannel())); else setPriorityChannel2Index(0xffff); // Set transmit channel if (lst->revertChannel() && (SelectedChannel::get() == lst->revertChannel())) setTXChannelIndex(0); else if (lst->revertChannel()) setTXChannelIndex(ctx.index(lst->revertChannel())); else setTXChannelIndex(0xffff); for (int i=0, j=0; i<31;) { if (j >= lst->count()) { setMemberIndex(i++, 0); } else if (SelectedChannel::get() == lst->channel(j)) { logInfo() << "Cannot encode '" << lst->channel(j) << "' for UV390: skip."; j++; } else { setMemberIndex(i++, ctx.index(lst->channel(j++))); } } if (TyTScanListExtension *ex = lst->tytScanListExtension()) { setHoldTime(ex->holdTime()); setPrioritySampleTime(ex->prioritySampleTime()); } return true; } ScanList * TyTCodeplug::ScanListElement::toScanListObj(Context &ctx) { Q_UNUSED(ctx) if (! isValid()) return nullptr; ScanList *lst = new ScanList(name()); TyTScanListExtension *ex = new TyTScanListExtension(); lst->setTyTScanListExtension(ex); ex->setHoldTime(holdTime()); ex->setPrioritySampleTime(prioritySampleTime()); return lst; } bool TyTCodeplug::ScanListElement::linkScanListObj(ScanList *lst, Context &ctx, const ErrorStack &err) { if (! isValid()) { errMsg(err) << "Cannot link invalid scanlist."; return false; } if (0 == priorityChannel1Index()) lst->setPrimaryChannel(SelectedChannel::get()); else if (ctx.has(priorityChannel1Index())) lst->setPrimaryChannel(ctx.get(priorityChannel1Index())); else if (0xffff == priorityChannel1Index()) lst->setPrimaryChannel(nullptr); else logWarn() << "Cannot decode reference to priority channel index " << priorityChannel1Index() << " in scan list '" << name() << "'."; if (0 == priorityChannel2Index()) lst->setSecondaryChannel(SelectedChannel::get()); else if (ctx.has(priorityChannel2Index())) lst->setSecondaryChannel(ctx.get(priorityChannel2Index())); else if (0xffff == priorityChannel2Index()) lst->setSecondaryChannel(nullptr); else logWarn() << "Cannot decode reference to secondary priority channel index " << priorityChannel2Index() << " in scan list '" << name() << "'."; if (0 == txChannelIndex()) lst->setRevertChannel(SelectedChannel::get()); else if (ctx.has(txChannelIndex())) lst->setRevertChannel(ctx.get(txChannelIndex())); else if (0xffff == txChannelIndex()) lst->setRevertChannel(nullptr); else logWarn() << "Cannot decode reference to transmit channel index " << txChannelIndex() << " in scan list '" << name() << "'."; for (int i=0; ((i<31) && memberIndex(i)); i++) { if (! ctx.has(memberIndex(i))) { errMsg(err) << "Cannot link scanlist to channel idx " << memberIndex(i) << ". Unknown channel index."; return false; } lst->addChannel(ctx.get(memberIndex(i))); } return true; } /* ******************************************************************************************** * * Implementation of TyTCodeplug::GeneralSettingsElement * ******************************************************************************************** */ TyTCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } TyTCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr) : Element(ptr, SETTINGS_SIZE) { // pass... } TyTCodeplug::GeneralSettingsElement::~GeneralSettingsElement() { // pass... } void TyTCodeplug::GeneralSettingsElement::clear() { setIntroLine1(""); setIntroLine2(""); memset(_data+0x26, 0xff, 0x1a); setBit(0x40,0, 0); setBit(0,1, 1); disableAllLEDs(false); setBit(0x40,3, 1); setMonitorType(TyTSettingsExtension::MonitorType::Open); setUInt3(0x40, 5, 7); setSavePreamble(true); setSaveModeRX(true); disableAllTones(false); setBit(0x41,3, 1); setChFreeIndicationTone(true); enablePasswdAndLock(false); enableTalkPermitToneDigital(false); enableTalkPermitToneAnalog(false); setBit(0x42,0, 0); setBit(0x41,1, 1); setBit(0x42,2, 0); setBit(0x42,3, 1); enableIntroPicture(true); setBit(0x42,5, 1); setBit(0x42,6, 1); setBit(0x42,7, 1); setUInt8(0x43, 0xff); setDMRId(0); setUInt8(0x47,0); setTXPreambleDuration(600); setGroupCallHangTime(3000); setPrivateCallHangTime(4000); setVOXSesitivity(3); setUInt8(0x4c, 0x00); setUInt8(0x4d, 0x00); setLowBatteryInterval(120); setCallAlertToneDuration(0); setLoneWorkerResponseTime(1); setLoneWorkerReminderTime(10); setUInt8(0x52, 0x00); setScanDigitalHangTime(1000); setScanAnalogHangTime(1000); setBacklightTime(10); setUInt6(0x55, 2, 0); keypadLockTimeSetManual(); setUInt8(0x57, 0xff); setPowerOnPassword(0); radioProgPasswordDisable(); pcProgPasswordDisable(); setUInt32_le(0x68, 0xffffffff); setUInt32_le(0x6c, 0xffffffff); setRadioName(""); } QString TyTCodeplug::GeneralSettingsElement::introLine1() const { return readUnicode(0, 10); } void TyTCodeplug::GeneralSettingsElement::setIntroLine1(const QString line) { writeUnicode(0, line, 10); } QString TyTCodeplug::GeneralSettingsElement::introLine2() const { return readUnicode(0x14, 10); } void TyTCodeplug::GeneralSettingsElement::setIntroLine2(const QString line) { writeUnicode(0x14, line, 10); } TyTSettingsExtension::MonitorType TyTCodeplug::GeneralSettingsElement::monitorType() const { return getBit(0x40,4) ? TyTSettingsExtension::MonitorType::Open : TyTSettingsExtension::MonitorType::Silent; } void TyTCodeplug::GeneralSettingsElement::setMonitorType(TyTSettingsExtension::MonitorType type) { setBit(0x40,4, TyTSettingsExtension::MonitorType::Open==type); } bool TyTCodeplug::GeneralSettingsElement::allLEDsDisabled() const { return ! getBit(0x40,2); } void TyTCodeplug::GeneralSettingsElement::disableAllLEDs(bool disable) { setBit(0x40,2, !disable); } bool TyTCodeplug::GeneralSettingsElement::savePreamble() const { return getBit(0x41,0); } void TyTCodeplug::GeneralSettingsElement::setSavePreamble(bool enable) { setBit(0x41,0, enable); } bool TyTCodeplug::GeneralSettingsElement::saveModeRX() const { return getBit(0x41,1); } void TyTCodeplug::GeneralSettingsElement::setSaveModeRX(bool enable) { setBit(0x41,1, enable); } bool TyTCodeplug::GeneralSettingsElement::allTonesDisabled() const { return ! getBit(0x41,2); } void TyTCodeplug::GeneralSettingsElement::disableAllTones(bool disable) { setBit(0x41,2, !disable); } bool TyTCodeplug::GeneralSettingsElement::chFreeIndicationTone() const { return !getBit(0x41,4); } void TyTCodeplug::GeneralSettingsElement::setChFreeIndicationTone(bool enable) { setBit(0x41,4, !enable); } bool TyTCodeplug::GeneralSettingsElement::passwdAndLock() const { return ! getBit(0x41,5); } void TyTCodeplug::GeneralSettingsElement::enablePasswdAndLock(bool enable) { setBit(0x41,5, !enable); } bool TyTCodeplug::GeneralSettingsElement::talkPermitToneDigital() const { return getBit(0x41,6); } void TyTCodeplug::GeneralSettingsElement::enableTalkPermitToneDigital(bool enable) { setBit(0x41,6, enable); } bool TyTCodeplug::GeneralSettingsElement::talkPermitToneAnalog() const { return getBit(0x41,7); } void TyTCodeplug::GeneralSettingsElement::enableTalkPermitToneAnalog(bool enable) { setBit(0x41,7, enable); } bool TyTCodeplug::GeneralSettingsElement::introPicture() const { return getBit(0x42,4); } void TyTCodeplug::GeneralSettingsElement::enableIntroPicture(bool enable) { setBit(0x42,4, enable); } uint32_t TyTCodeplug::GeneralSettingsElement::dmrId() const { return getUInt24_le(0x44); } void TyTCodeplug::GeneralSettingsElement::setDMRId(uint32_t id) { setUInt24_le(0x44, id); } unsigned TyTCodeplug::GeneralSettingsElement::txPreambleDuration() const { return unsigned(getUInt8(0x48))*60; } void TyTCodeplug::GeneralSettingsElement::setTXPreambleDuration(unsigned dur) { dur = std::min(8640U, dur); setUInt8(0x48, dur/60); } unsigned TyTCodeplug::GeneralSettingsElement::groupCallHangTime() const { return unsigned(getUInt8(0x49))*100; } void TyTCodeplug::GeneralSettingsElement::setGroupCallHangTime(unsigned dur) { dur = std::min(7000U, dur); setUInt8(0x49, dur/100); } unsigned TyTCodeplug::GeneralSettingsElement::privateCallHangTime() const { return unsigned(getUInt8(0x4a))*100; } void TyTCodeplug::GeneralSettingsElement::setPrivateCallHangTime(unsigned dur) { dur = std::min(7000U, dur); setUInt8(0x4a, dur/100); } unsigned TyTCodeplug::GeneralSettingsElement::voxSesitivity() const { return getUInt8(0x4b); } void TyTCodeplug::GeneralSettingsElement::setVOXSesitivity(unsigned level) { level = std::min(10U, std::max(1U, level)); setUInt8(0x4b, level); } unsigned TyTCodeplug::GeneralSettingsElement::lowBatteryInterval() const { return unsigned(getUInt8(0x4e))*5; } void TyTCodeplug::GeneralSettingsElement::setLowBatteryInterval(unsigned dur) { dur = std::min(635U, dur); setUInt8(0x4e, dur/5); } bool TyTCodeplug::GeneralSettingsElement::callAlertToneIsContinuous() const { return 0==getUInt8(0x4f); } unsigned TyTCodeplug::GeneralSettingsElement::callAlertToneDuration() const { return unsigned(getUInt8(0x4f))*5; } void TyTCodeplug::GeneralSettingsElement::setCallAlertToneDuration(unsigned dur) { dur = std::min(1200U, dur); setUInt8(0x4f, dur/5); } void TyTCodeplug::GeneralSettingsElement::setCallAlertToneContinuous() { setUInt8(0x4f, 0); } unsigned TyTCodeplug::GeneralSettingsElement::loneWorkerResponseTime() const { return getUInt8(0x50); } void TyTCodeplug::GeneralSettingsElement::setLoneWorkerResponseTime(unsigned dur) { setUInt8(0x50, dur); } unsigned TyTCodeplug::GeneralSettingsElement::loneWorkerReminderTime() const { return getUInt8(0x51); } void TyTCodeplug::GeneralSettingsElement::setLoneWorkerReminderTime(unsigned dur) { setUInt8(0x51, dur); } unsigned TyTCodeplug::GeneralSettingsElement::scanDigitalHangTime() const { return unsigned(getUInt8(0x53))*100; } void TyTCodeplug::GeneralSettingsElement::setScanDigitalHangTime(unsigned dur) { dur = std::min(10000U, dur); setUInt8(0x53, dur/100); } unsigned TyTCodeplug::GeneralSettingsElement::scanAnalogHangTime() const { return unsigned(getUInt8(0x54))*100; } void TyTCodeplug::GeneralSettingsElement::setScanAnalogHangTime(unsigned dur) { dur = std::min(10000U, dur); setUInt8(0x54, dur/100); } bool TyTCodeplug::GeneralSettingsElement::backlightIsAlways() const { return 0 == getUInt2(0x55, 0); } unsigned TyTCodeplug::GeneralSettingsElement::backlightTime() const { return getUInt2(0x55, 0)*5; } void TyTCodeplug::GeneralSettingsElement::setBacklightTime(unsigned sec) { sec = std::min(15U, sec); setUInt2(0x55, 0, sec/5); } void TyTCodeplug::GeneralSettingsElement::backlightTimeSetAlways() { setUInt2(0x55, 0, 0); } bool TyTCodeplug::GeneralSettingsElement::keypadLockIsManual() const { return 0xff == getUInt8(0x56); } unsigned TyTCodeplug::GeneralSettingsElement::keypadLockTime() const { return unsigned(getUInt8(0x56))*5; } void TyTCodeplug::GeneralSettingsElement::setKeypadLockTime(unsigned sec) { sec = std::max(1U, std::min(15U, sec)); setUInt8(0x56, sec/5); } void TyTCodeplug::GeneralSettingsElement::keypadLockTimeSetManual() { setUInt8(0x56, 0xff); } uint32_t TyTCodeplug::GeneralSettingsElement::powerOnPassword() const { return getBCD8_le(0x58); } void TyTCodeplug::GeneralSettingsElement::setPowerOnPassword(uint32_t pass) { setBCD8_le(0x58, pass); } bool TyTCodeplug::GeneralSettingsElement::radioProgPasswordEnabled() const { return 0xffffffff != getUInt32_le(0x5c); } uint32_t TyTCodeplug::GeneralSettingsElement::radioProgPassword() const { return getBCD8_le(0x5c); } void TyTCodeplug::GeneralSettingsElement::setRadioProgPassword(uint32_t passwd) { setBCD8_le(0x5c, passwd); } void TyTCodeplug::GeneralSettingsElement::radioProgPasswordDisable() { setUInt32_le(0x5c, 0xffffffff); } bool TyTCodeplug::GeneralSettingsElement::pcProgPasswordEnabled() const { return (0xff != getUInt8(0x60)); } QString TyTCodeplug::GeneralSettingsElement::pcProgPassword() const { return readASCII(0x60, 8, 0xff); } void TyTCodeplug::GeneralSettingsElement::setPCProgPassword(const QString &pass) { writeASCII(0x60, pass, 8, 0xff); } void TyTCodeplug::GeneralSettingsElement::pcProgPasswordDisable() { memset(_data+0x60, 0xff, 8); } QString TyTCodeplug::GeneralSettingsElement::radioName() const { return readUnicode(0x70, 16); } void TyTCodeplug::GeneralSettingsElement::setRadioName(const QString &name) { writeUnicode(0x70, name, 16); } bool TyTCodeplug::GeneralSettingsElement::fromConfig(const Config *config) { if (config->settings()->defaultIdRef()->isNull()) return false; setRadioName(config->settings()->defaultIdRef()->as()->name()); setDMRId(config->settings()->defaultIdRef()->as()->number()); setIntroLine1(config->settings()->introLine1()); setIntroLine2(config->settings()->introLine2()); setVOXSesitivity(config->settings()->vox()); if (TyTSettingsExtension *ex = config->settings()->tytExtension()) { setMonitorType(ex->monitorType()); disableAllLEDs(ex->allLEDsDisabled()); enableTalkPermitToneDigital(ex->talkPermitToneDigital()); enableTalkPermitToneAnalog(ex->talkPermitToneAnalog()); enablePasswdAndLock(ex->passwordAndLock()); setChFreeIndicationTone(ex->channelFreeIndicationTone()); disableAllTones(ex->allTonesDisabled()); setSaveModeRX(ex->powerSaveMode()); setSavePreamble(ex->wakeupPreamble()); enableIntroPicture(ex->bootPicture()); setTXPreambleDuration(ex->txPreambleDuration()); setGroupCallHangTime(ex->groupCallHangTime()); setPrivateCallHangTime(ex->privateCallHangTime()); setLowBatteryInterval(ex->lowBatteryWarnInterval()); if (ex->callAlertToneContinuous()) setCallAlertToneContinuous(); else setCallAlertToneDuration(ex->callAlertToneDuration()); setLoneWorkerResponseTime(ex->loneWorkerResponseTime()); setLoneWorkerReminderTime(ex->loneWorkerReminderTime()); setScanDigitalHangTime(ex->digitalScanHangTime()); setScanAnalogHangTime(ex->analogScanHangTime()); if (ex->backlightAlwaysOn()) backlightTimeSetAlways(); else setBacklightTime(ex->backlightDuration()); if (ex->keypadLockManual()) keypadLockTimeSetManual(); else setKeypadLockTime(ex->keypadLockTime()); if (! ex->powerOnPasswordEnabled()) setPowerOnPassword(0); else setPowerOnPassword(ex->powerOnPassword()); if (! ex->radioProgPasswordEnabled()) radioProgPasswordDisable(); else setRadioProgPassword(ex->radioProgPassword()); setPCProgPassword(ex->pcProgPassword()); } return true; } bool TyTCodeplug::GeneralSettingsElement::updateConfig(Config *config) { int idx = config->radioIDs()->addId(radioName(),dmrId()); if (0 <= idx) { logDebug() << "Explicitly set default ID to index=" << idx << "."; config->settings()->defaultIdRef()->set(config->radioIDs()->getId(idx)); } else { logError() << "Cannot add radio DMR ID & cannot set default ID."; return false; } config->settings()->setIntroLine1(introLine1()); config->settings()->setIntroLine2(introLine2()); config->settings()->setVOX(voxSesitivity()); // apply extension TyTSettingsExtension *ex = new TyTSettingsExtension(); config->settings()->setTyTExtension(ex); ex->setMonitorType(monitorType()); ex->disableAllLEDs(allLEDsDisabled()); ex->enableTalkPermitToneDigital(talkPermitToneDigital()); ex->enableTalkPermitToneAnalog(talkPermitToneAnalog()); ex->enablePasswordAndLock(passwdAndLock()); ex->enableChannelFreeIndicationTone(chFreeIndicationTone()); ex->disableAllTones(allTonesDisabled()); ex->enablePowerSaveMode(saveModeRX()); ex->enableWakeupPreamble(savePreamble()); ex->enableBootPicture(introPicture()); ex->setTXPreambleDuration(txPreambleDuration()); ex->setGroupCallHangTime(groupCallHangTime()); ex->setPrivateCallHangTime(privateCallHangTime()); ex->setLowBatteryWarnInterval(lowBatteryInterval()); ex->enableCallAlertToneContinuous(callAlertToneIsContinuous()); if (! callAlertToneIsContinuous()) ex->setCallAlertToneDuration(callAlertToneDuration()); ex->setLoneWorkerResponseTime(loneWorkerResponseTime()); ex->setLoneWorkerReminderTime(loneWorkerReminderTime()); ex->setDigitalScanHangTime(scanDigitalHangTime()); ex->setAnalogScanHangTime(scanAnalogHangTime()); ex->enableBacklightAlwaysOn(backlightIsAlways()); if (! backlightIsAlways()) ex->setBacklightDuration(backlightTime()); ex->enableKeypadLockManual(keypadLockIsManual()); if (! keypadLockIsManual()) ex->setKeypadLockTime(keypadLockTime()); ex->enableRadioProgPassword(radioProgPasswordEnabled()); if (radioProgPasswordEnabled()) ex->setRadioProgPassword(ex->radioProgPassword()); ex->setPCProgPassword(pcProgPassword()); return true; } /* ******************************************************************************************** * * Implementation of TyTCodeplug::TimestampElement * ******************************************************************************************** */ TyTCodeplug::TimestampElement::TimestampElement(uint8_t *ptr, size_t size) : Codeplug::Element(ptr, size) { // pass... } TyTCodeplug::TimestampElement::TimestampElement(uint8_t *ptr) : Codeplug::Element(ptr, 0x0c) { // pass... } TyTCodeplug::TimestampElement::~TimestampElement() { // pass... } void TyTCodeplug::TimestampElement::clear() { setUInt8(0x00, 0); setTimestamp(QDateTime::currentDateTime()); setUInt32_be(0x08, 0x00010300); } QDateTime TyTCodeplug::TimestampElement::timestamp() const { return QDateTime(QDate(getBCD4_le(0x01), getBCD2(0x03), getBCD2(0x04)), QTime(getBCD2(0x05), getBCD2(0x06), getBCD2(0x07))); } void TyTCodeplug::TimestampElement::setTimestamp(const QDateTime &ts) { setBCD4_le(0x01, ts.date().year()); setBCD2(0x03, ts.date().month()); setBCD2(0x04, ts.date().day()); setBCD2(0x05, ts.time().hour()); setBCD2(0x06, ts.time().minute()); setBCD2(0x07, ts.time().second()); } QString TyTCodeplug::TimestampElement::cpsVersion() const { const char table[] = "0123456789:;<=>?"; QString v; v.append(table[std::min(uint8_t(15), getUInt8(0x08))]); v.append(table[std::min(uint8_t(15), getUInt8(0x09))]); v.append("."); v.append(table[std::min(uint8_t(15), getUInt8(0x0a))]); v.append(table[std::min(uint8_t(15), getUInt8(0x0b))]); return v; } /* ******************************************************************************************** * * Implementation of TyTCodeplug::GPSSystemElement * ******************************************************************************************** */ TyTCodeplug::GPSSystemElement::GPSSystemElement(uint8_t *ptr, size_t size) : Codeplug::Element(ptr, size) { // pass... } TyTCodeplug::GPSSystemElement::GPSSystemElement(uint8_t *ptr) : Codeplug::Element(ptr, 0x10) { // pass... } TyTCodeplug::GPSSystemElement::~GPSSystemElement() { // pass... } bool TyTCodeplug::GPSSystemElement::isValid() const { return Element::isValid() && (! repeatIntervalDisabled()) && (!destinationContactDisabled()); } void TyTCodeplug::GPSSystemElement::clear() { setRevertChannelIndex(0xffff); disableRepeatInterval(); setUInt8(0x03, 0xff); disableDestinationContact(); memset(_data + 0x06, 0xff, 10); } bool TyTCodeplug::GPSSystemElement::revertChannelIsSelected() const { return 0 == getUInt16_le(0x00); } uint16_t TyTCodeplug::GPSSystemElement::revertChannelIndex() const { return getUInt16_le(0x00); } void TyTCodeplug::GPSSystemElement::setRevertChannelIndex(uint16_t idx) { setUInt16_le(0x00, idx); } void TyTCodeplug::GPSSystemElement::setRevertChannelSelected() { setUInt16_le(0x00, 0); } bool TyTCodeplug::GPSSystemElement::repeatIntervalDisabled() const { return 0 == getUInt8(0x02); } unsigned TyTCodeplug::GPSSystemElement::repeatInterval() const { return unsigned(getUInt8(0x02))*30; } void TyTCodeplug::GPSSystemElement::setRepeatInterval(unsigned dur) { setUInt8(0x02, dur/30); } void TyTCodeplug::GPSSystemElement::disableRepeatInterval() { setUInt8(0x02, 0); } bool TyTCodeplug::GPSSystemElement::destinationContactDisabled() const { return 0 == getUInt16_le(0x04); } uint16_t TyTCodeplug::GPSSystemElement::destinationContactIndex() const { return getUInt16_le(0x04); } void TyTCodeplug::GPSSystemElement::setDestinationContactIndex(uint16_t idx) { setUInt16_le(0x04, idx); } void TyTCodeplug::GPSSystemElement::disableDestinationContact() { setUInt16_le(0x04, 0); } bool TyTCodeplug::GPSSystemElement::fromGPSSystemObj(GPSSystem *sys, Context &ctx) { clear(); if (sys->hasContact()) setDestinationContactIndex(ctx.index(sys->contactObj())); if (sys->hasRevertChannel()) setRevertChannelIndex(ctx.index(sys->revertChannel())); else setRevertChannelSelected(); setRepeatInterval(sys->period()); return true; } GPSSystem * TyTCodeplug::GPSSystemElement::toGPSSystemObj() { return new GPSSystem("GPS System", nullptr, nullptr, repeatInterval()); } bool TyTCodeplug::GPSSystemElement::linkGPSSystemObj(GPSSystem *sys, Context &ctx) { if (! isValid()) return false; if ((! destinationContactDisabled()) && (ctx.has(destinationContactIndex()))) sys->setContactObj(ctx.get(destinationContactIndex())); if (revertChannelIsSelected()) sys->resetRevertChannel(); else if (ctx.has(revertChannelIndex()) && ctx.get(revertChannelIndex())->is()) sys->setRevertChannel(ctx.get(revertChannelIndex())->as()); return true; } /* ******************************************************************************************** * * Implementation of TyTCodeplug::MenuSettingsElement * ******************************************************************************************** */ TyTCodeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } TyTCodeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr) : Element(ptr, MENUSETTINGS_SIZE) { // pass... } TyTCodeplug::MenuSettingsElement::~MenuSettingsElement() { // pass... } void TyTCodeplug::MenuSettingsElement::clear() { setMenuHangtime(0); enableTextMessage(true); enableCallAlert(true); enableContactEditing(true); enableManualDial(true); enableRemoteRadioCheck(false); enableRemoteMonitor(false); enableRemoteRadioEnable(false); enableRemoteRadioDisable(false); setBit(0x02, 0, false); enableScan(true); enableScanListEditing(true); enableCallLogMissed(true); enableCallLogAnswered(true); enableCallLogOutgoing(true); enableTalkaround(false); enableAlertTone(true); enablePower(true); enableBacklight(true); enableBootScreen(true); enableKeypadLock(true); enableLEDIndicator(true); enableSquelch(true); setBit(0x03, 6, false); enableVOX(true); enablePassword(false); enableDisplayMode(true); enableRadioProgramming(true); setBit(0x04, 3, true); setBit(0x04, 1, true); setBit(0x04, 5, true); setBit(0x04, 6, true); setBit(0x04, 7, true); setUInt8(0x05, 0xfb); setUInt8(0x06, 0xff); memset(_data+0x07, 0xff, 9); } bool TyTCodeplug::MenuSettingsElement::menuHangtimeIsInfinite() const { return 0 == menuHangtime(); } unsigned TyTCodeplug::MenuSettingsElement::menuHangtime() const { return getUInt8(0x00); } void TyTCodeplug::MenuSettingsElement::setMenuHangtime(unsigned sec) { setUInt8(0x00, sec); } void TyTCodeplug::MenuSettingsElement::infiniteMenuHangtime() { setUInt8(0x00, 0x00); } bool TyTCodeplug::MenuSettingsElement::textMessage() const { return getBit(0x01, 0); } void TyTCodeplug::MenuSettingsElement::enableTextMessage(bool enable) { setBit(0x01, 0, enable); } bool TyTCodeplug::MenuSettingsElement::callAlert() const { return getBit(0x01, 1); } void TyTCodeplug::MenuSettingsElement::enableCallAlert(bool enable) { setBit(0x01, 1, enable); } bool TyTCodeplug::MenuSettingsElement::contactEditing() const { return getBit(0x01, 2); } void TyTCodeplug::MenuSettingsElement::enableContactEditing(bool enable) { setBit(0x01, 2, enable); } bool TyTCodeplug::MenuSettingsElement::manualDial() const { return getBit(0x01, 3); } void TyTCodeplug::MenuSettingsElement::enableManualDial(bool enable) { setBit(0x01, 3, enable); } bool TyTCodeplug::MenuSettingsElement::remoteRadioCheck() const { return getBit(0x01, 4); } void TyTCodeplug::MenuSettingsElement::enableRemoteRadioCheck(bool enable) { setBit(0x01, 4, enable); } bool TyTCodeplug::MenuSettingsElement::remoteMonitor() const { return getBit(0x01, 5); } void TyTCodeplug::MenuSettingsElement::enableRemoteMonitor(bool enable) { setBit(0x01, 5, enable); } bool TyTCodeplug::MenuSettingsElement::remoteRadioEnable() const { return getBit(0x01, 6); } void TyTCodeplug::MenuSettingsElement::enableRemoteRadioEnable(bool enable) { setBit(0x01, 6, enable); } bool TyTCodeplug::MenuSettingsElement::remoteRadioDisable() const { return getBit(0x01, 7); } void TyTCodeplug::MenuSettingsElement::enableRemoteRadioDisable(bool enable) { setBit(0x01, 7, enable); } bool TyTCodeplug::MenuSettingsElement::scan() const { return getBit(0x02, 1); } void TyTCodeplug::MenuSettingsElement::enableScan(bool enable) { setBit(0x02, 1, enable); } bool TyTCodeplug::MenuSettingsElement::scanListEditing() const { return getBit(0x02, 2); } void TyTCodeplug::MenuSettingsElement::enableScanListEditing(bool enable) { setBit(0x02, 2, enable); } bool TyTCodeplug::MenuSettingsElement::callLogMissed() const { return getBit(0x02, 3); } void TyTCodeplug::MenuSettingsElement::enableCallLogMissed(bool enable) { setBit(0x02, 3, enable); } bool TyTCodeplug::MenuSettingsElement::callLogAnswered() const { return getBit(0x02, 4); } void TyTCodeplug::MenuSettingsElement::enableCallLogAnswered(bool enable) { setBit(0x02, 4, enable); } bool TyTCodeplug::MenuSettingsElement::callLogOutgoing() const { return getBit(0x02, 5); } void TyTCodeplug::MenuSettingsElement::enableCallLogOutgoing(bool enable) { setBit(0x02, 5, enable); } bool TyTCodeplug::MenuSettingsElement::talkaround() const { return getBit(0x02, 6); } void TyTCodeplug::MenuSettingsElement::enableTalkaround(bool enable) { setBit(0x02, 6, enable); } bool TyTCodeplug::MenuSettingsElement::alertTone() const { return getBit(0x02, 7); } void TyTCodeplug::MenuSettingsElement::enableAlertTone(bool enable) { setBit(0x02, 7, enable); } bool TyTCodeplug::MenuSettingsElement::power() const { return getBit(0x03, 0); } void TyTCodeplug::MenuSettingsElement::enablePower(bool enable) { setBit(0x03, 0, enable); } bool TyTCodeplug::MenuSettingsElement::backlight() const { return getBit(0x03, 1); } void TyTCodeplug::MenuSettingsElement::enableBacklight(bool enable) { setBit(0x03, 1, enable); } bool TyTCodeplug::MenuSettingsElement::bootScreen() const { return getBit(0x03, 2); } void TyTCodeplug::MenuSettingsElement::enableBootScreen(bool enable) { setBit(0x03, 2, enable); } bool TyTCodeplug::MenuSettingsElement::keypadLock() const { return getBit(0x03, 3); } void TyTCodeplug::MenuSettingsElement::enableKeypadLock(bool enable) { setBit(0x03, 3, enable); } bool TyTCodeplug::MenuSettingsElement::ledIndicator() const { return getBit(0x03, 4); } void TyTCodeplug::MenuSettingsElement::enableLEDIndicator(bool enable) { setBit(0x03, 4, enable); } bool TyTCodeplug::MenuSettingsElement::squelch() const { return getBit(0x03, 5); } void TyTCodeplug::MenuSettingsElement::enableSquelch(bool enable) { setBit(0x03, 5, enable); } bool TyTCodeplug::MenuSettingsElement::vox() const { return getBit(0x03, 7); } void TyTCodeplug::MenuSettingsElement::enableVOX(bool enable) { setBit(0x03, 7, enable); } bool TyTCodeplug::MenuSettingsElement::password() const { return getBit(0x04, 0); } void TyTCodeplug::MenuSettingsElement::enablePassword(bool enable) { setBit(0x04, 0, enable); } bool TyTCodeplug::MenuSettingsElement::displayMode() const { return getBit(0x04, 1); } void TyTCodeplug::MenuSettingsElement::enableDisplayMode(bool enable) { setBit(0x04, 1, enable); } bool TyTCodeplug::MenuSettingsElement::radioProgramming() const { return ! getBit(0x04, 2); } void TyTCodeplug::MenuSettingsElement::enableRadioProgramming(bool enable) { setBit(0x04, 2, !enable); } bool TyTCodeplug::MenuSettingsElement::fromConfig(const Config *config) { if (nullptr == config->tytExtension()) return true; TyTMenuSettings *ex = config->tytExtension()->menuSettings(); if (ex->hangtimeIsInfinite()) infiniteMenuHangtime(); else setMenuHangtime(ex->hangTime()); enableTextMessage(ex->textMessage()); enableCallAlert(ex->callAlert()); enableContactEditing(ex->contactEditing()); enableManualDial(ex->manualDial()); enableRemoteRadioCheck(ex->remoteRadioCheck()); enableRemoteMonitor(ex->remoteMonitor()); enableRemoteRadioEnable(ex->remoteRadioEnable()); enableRemoteRadioDisable(ex->remoteRadioDisable()); enableScan(ex->scan()); enableScanListEditing(ex->scanListEditing()); enableCallLogMissed(ex->callLogMissed()); enableCallLogAnswered(ex->callLogAnswered()); enableCallLogOutgoing(ex->callLogOutgoing()); enableTalkaround(ex->talkaround()); enableAlertTone(ex->alertTone()); enablePower(ex->power()); enableBacklight(ex->backlight()); enableBootScreen(ex->bootScreen()); enableKeypadLock(ex->keypadLock()); enableLEDIndicator(ex->ledIndicator()); enableSquelch(ex->squelch()); enableVOX(ex->vox()); enablePassword(ex->password()); enableDisplayMode(ex->displayMode()); enableRadioProgramming(ex->radioProgramming()); return true; } bool TyTCodeplug::MenuSettingsElement::updateConfig(Config *config) { TyTConfigExtension *ext = config->tytExtension(); if (nullptr == ext) { ext = new TyTConfigExtension(config); config->setTyTExtension(ext); } TyTMenuSettings *ex = ext->menuSettings(); if (menuHangtimeIsInfinite()) ex->setHangtimeInfinite(true); else ex->setHangTime(menuHangtime()); ex->enableTextMessage(textMessage()); ex->enableCallAlert(callAlert()); ex->enableContactEditing(contactEditing()); ex->enableManualDial(manualDial()); ex->enableRemoteRadioCheck(remoteRadioCheck()); ex->enableRemoteMonitor(remoteMonitor()); ex->enableRemoteRadioEnable(remoteRadioEnable()); ex->enableRemoteRadioDisable(remoteRadioDisable()); ex->enableScan(scan()); ex->enableScanListEditing(scanListEditing()); ex->enableCallLogMissed(callLogMissed()); ex->enableCallLogAnswered(callLogAnswered()); ex->enableCallLogOutgoing(callLogOutgoing()); ex->enableTalkaround(talkaround()); ex->enableAlertTone(alertTone()); ex->enablePower(power()); ex->enableBacklight(backlight()); ex->enableBootScreen(bootScreen()); ex->enableKeypadLock(keypadLock()); ex->enableLEDIndicator(ledIndicator()); ex->enableSquelch(squelch()); ex->enableVOX(vox()); ex->enablePassword(password()); ex->enableDisplayMode(displayMode()); ex->enableRadioProgramming(radioProgramming()); return true; } /* ******************************************************************************************** * * Implementation of TyTCodeplug::ButtonSettingsElement * ******************************************************************************************** */ TyTCodeplug::ButtonSettingsElement::ButtonSettingsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } TyTCodeplug::ButtonSettingsElement::ButtonSettingsElement(uint8_t *ptr) : Element(ptr, 0x14) // <- size excluding one-touch settings { // pass... } TyTCodeplug::ButtonSettingsElement::~ButtonSettingsElement() { // pass... } void TyTCodeplug::ButtonSettingsElement::clear() { setUInt16_le(0x00, 0); setSideButton1Short(ButtonAction::Disabled); setSideButton1Long(ButtonAction::Tone1750Hz); setSideButton2Short(ButtonAction::MonitorToggle); setSideButton2Long(ButtonAction::Disabled); memset(_data+0x06, 0x00, 10); setUInt8(0x10, 1); setLongPressDuration(1000); setUInt16_le(0x12, 0xffff); } TyTCodeplug::ButtonSettingsElement::ButtonAction TyTCodeplug::ButtonSettingsElement::sideButton1Short() const { return ButtonAction(getUInt8(0x02)); } void TyTCodeplug::ButtonSettingsElement::setSideButton1Short(ButtonAction action) { setUInt8(0x02, action); } TyTCodeplug::ButtonSettingsElement::ButtonAction TyTCodeplug::ButtonSettingsElement::sideButton1Long() const { return ButtonAction(getUInt8(0x03)); } void TyTCodeplug::ButtonSettingsElement::setSideButton1Long(ButtonAction action) { setUInt8(0x03, action); } TyTCodeplug::ButtonSettingsElement::ButtonAction TyTCodeplug::ButtonSettingsElement::sideButton2Short() const { return ButtonAction(getUInt8(0x04)); } void TyTCodeplug::ButtonSettingsElement::setSideButton2Short(ButtonAction action) { setUInt8(0x04, action); } TyTCodeplug::ButtonSettingsElement::ButtonAction TyTCodeplug::ButtonSettingsElement::sideButton2Long() const { return ButtonAction(getUInt8(0x05)); } void TyTCodeplug::ButtonSettingsElement::setSideButton2Long(ButtonAction action) { setUInt8(0x05, action); } unsigned TyTCodeplug::ButtonSettingsElement::longPressDuration() const { return unsigned(getUInt8(0x11))*250; } void TyTCodeplug::ButtonSettingsElement::setLongPressDuration(unsigned ms) { setUInt8(0x11, ms/250); } bool TyTCodeplug::ButtonSettingsElement::fromConfig(const Config *config) { // Skip if not defined if (nullptr == config->tytExtension()) return true; setSideButton1Short(config->tytExtension()->buttonSettings()->sideButton1Short()); setSideButton1Long(config->tytExtension()->buttonSettings()->sideButton1Long()); setSideButton2Short(config->tytExtension()->buttonSettings()->sideButton2Short()); setSideButton2Long(config->tytExtension()->buttonSettings()->sideButton2Long()); setLongPressDuration(config->tytExtension()->buttonSettings()->longPressDuration()); return true; } bool TyTCodeplug::ButtonSettingsElement::updateConfig(Config *config) { TyTConfigExtension *ext = config->tytExtension(); if (nullptr == ext) { ext = new TyTConfigExtension(config); config->setTyTExtension(ext); } ext->buttonSettings()->setSideButton1Short(sideButton1Short()); ext->buttonSettings()->setSideButton1Long(sideButton1Long()); ext->buttonSettings()->setSideButton2Short(sideButton2Short()); ext->buttonSettings()->setSideButton2Long(sideButton2Long()); ext->buttonSettings()->setLongPressDuration(longPressDuration()); return true; } /* ******************************************************************************************** * * Implementation of TyTCodeplug::OneTouchSettingElement * ******************************************************************************************** */ TyTCodeplug::OneTouchSettingElement::OneTouchSettingElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } TyTCodeplug::OneTouchSettingElement::OneTouchSettingElement(uint8_t *ptr) : Element(ptr, 0x04) { // pass.... } TyTCodeplug::OneTouchSettingElement::~OneTouchSettingElement() { // pass... } bool TyTCodeplug::OneTouchSettingElement::isValid() const { return Element::isValid() && (Disabled != actionType()); } void TyTCodeplug::OneTouchSettingElement::clear() { setAction(CALL); setActionType(Disabled); setUInt2(0x00, 6, 3); setMessageIndex(0); setContactIndex(0); } TyTCodeplug::OneTouchSettingElement::Action TyTCodeplug::OneTouchSettingElement::action() const { return Action(getUInt4(0x00, 0)); } void TyTCodeplug::OneTouchSettingElement::setAction(Action action) { setUInt4(0x00, 0, action); } TyTCodeplug::OneTouchSettingElement::Type TyTCodeplug::OneTouchSettingElement::actionType() const { return Type(getUInt2(0x00, 4)); } void TyTCodeplug::OneTouchSettingElement::setActionType(Type type) { setUInt2(0x00, 4, type); } uint8_t TyTCodeplug::OneTouchSettingElement::messageIndex() const { return getUInt8(0x01); } void TyTCodeplug::OneTouchSettingElement::setMessageIndex(uint8_t idx) { setUInt8(0x01, idx); } uint16_t TyTCodeplug::OneTouchSettingElement::contactIndex() const { return getUInt16_le(0x02); } void TyTCodeplug::OneTouchSettingElement::setContactIndex(uint16_t idx) { setUInt16_le(0x02, idx); } /* ******************************************************************************************** * * Implementation of TyTCodeplug::EmergencySettingsElement * ******************************************************************************************** */ TyTCodeplug::EmergencySettingsElement::EmergencySettingsElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } TyTCodeplug::EmergencySettingsElement::EmergencySettingsElement(uint8_t *ptr) : Element(ptr, 0x10) { // pass... } TyTCodeplug::EmergencySettingsElement::~EmergencySettingsElement() { // pass... } void TyTCodeplug::EmergencySettingsElement::clear() { setUInt5(0x00, 3, 0b11111); enableRadioDisable(true); enableRemoteMonitor(false); enableEmergencyRemoteMonitor(true); setRemoteMonitorDuration(10); setTXTimeOut(125); setMessageLimit(2); memset(_data+0x04, 0xff, 12); } bool TyTCodeplug::EmergencySettingsElement::emergencyRemoteMonitor() const { return getBit(0x00, 2); } void TyTCodeplug::EmergencySettingsElement::enableEmergencyRemoteMonitor(bool enable) { setBit(0x00, 2, enable); } bool TyTCodeplug::EmergencySettingsElement::remoteMonitor() const { return getBit(0x00, 1); } void TyTCodeplug::EmergencySettingsElement::enableRemoteMonitor(bool enable) { setBit(0x00, 1, enable); } bool TyTCodeplug::EmergencySettingsElement::radioDisable() const { return getBit(0x00, 0); } void TyTCodeplug::EmergencySettingsElement::enableRadioDisable(bool enable) { setBit(0x00, 0, enable); } unsigned TyTCodeplug::EmergencySettingsElement::remoteMonitorDuration() const { return unsigned(getUInt8(0x01))*10; } void TyTCodeplug::EmergencySettingsElement::setRemoteMonitorDuration(unsigned sec) { setUInt8(0x01, sec/10); } unsigned TyTCodeplug::EmergencySettingsElement::txTimeOut() const { return unsigned(getUInt8(0x02))*25; } void TyTCodeplug::EmergencySettingsElement::setTXTimeOut(unsigned ms) { setUInt8(0x02, ms/25); } unsigned TyTCodeplug::EmergencySettingsElement::messageLimit() const { return unsigned(getUInt8(0x03)); } void TyTCodeplug::EmergencySettingsElement::setMessageLimit(unsigned limit) { setUInt8(0x03, limit); } /* ******************************************************************************************** * * Implementation of TyTCodeplug::EmergencySystemElement * ******************************************************************************************** */ TyTCodeplug::EmergencySystemElement::EmergencySystemElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } TyTCodeplug::EmergencySystemElement::EmergencySystemElement(uint8_t *ptr) : Element(ptr, 0x28) { // pass... } TyTCodeplug::EmergencySystemElement::~EmergencySystemElement() { // pass... } bool TyTCodeplug::EmergencySystemElement::isValid() const { return Element::isValid() && (0 != revertChannelIndex()); } void TyTCodeplug::EmergencySystemElement::clear() { setName(""); setAlarmType(DISABLED); setUInt2(0x00, 2, 3); setAlarmMode(ALARM); setUInt2(0x00, 6, 1); setImpoliteRetries(15); setPoliteRetries(5); setHotMICDuration(100); setRevertChannelIndex(0); setUInt16_le(0x26, 0xffff); } QString TyTCodeplug::EmergencySystemElement::name() const { return readUnicode(0x00, 16); } void TyTCodeplug::EmergencySystemElement::setName(const QString &nm) { writeUnicode(0x00, nm, 16); } TyTCodeplug::EmergencySystemElement::AlarmType TyTCodeplug::EmergencySystemElement::alarmType() const { return AlarmType(getUInt2(0x20, 0)); } void TyTCodeplug::EmergencySystemElement::setAlarmType(AlarmType type) { setUInt2(0x20, 0, type); } TyTCodeplug::EmergencySystemElement::AlarmMode TyTCodeplug::EmergencySystemElement::alarmMode() const { return AlarmMode(getUInt2(0x20, 4)); } void TyTCodeplug::EmergencySystemElement::setAlarmMode(AlarmMode mode) { setUInt2(0x20, 4, mode); } unsigned TyTCodeplug::EmergencySystemElement::impoliteRetries() const { return getUInt8(0x21); } void TyTCodeplug::EmergencySystemElement::setImpoliteRetries(unsigned num) { setUInt8(0x21, num); } unsigned TyTCodeplug::EmergencySystemElement::politeRetries() const { return getUInt8(0x22); } void TyTCodeplug::EmergencySystemElement::setPoliteRetries(unsigned num) { setUInt8(0x22, num); } unsigned TyTCodeplug::EmergencySystemElement::hotMICDuration() const { return unsigned(getUInt8(0x23))*10; } void TyTCodeplug::EmergencySystemElement::setHotMICDuration(unsigned dur) { setUInt8(0x23, dur/10); } bool TyTCodeplug::EmergencySystemElement::revertChannelIsSelected() const { return 0xffff == getUInt16_le(0x24); } uint16_t TyTCodeplug::EmergencySystemElement::revertChannelIndex() const { return getUInt16_le(0x24); } void TyTCodeplug::EmergencySystemElement::setRevertChannelIndex(uint16_t idx) { setUInt16_le(0x24, idx); } void TyTCodeplug::EmergencySystemElement::revertChannelSelected() { setUInt16_le(0x24, 0xffff); } /* ******************************************************************************************** * * Implementation of TyTCodeplug::EncryptionElement * ******************************************************************************************** */ TyTCodeplug::EncryptionElement::EncryptionElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } TyTCodeplug::EncryptionElement::EncryptionElement(uint8_t *ptr) : Element(ptr, size()) { // pass... } TyTCodeplug::EncryptionElement::~EncryptionElement() { // pass... } void TyTCodeplug::EncryptionElement::clear() { memset(_data, 0xff, size()); } bool TyTCodeplug::EncryptionElement::isEnhancedKeySet(unsigned n) const { QByteArray key = enhancedKey(n); for (int i=0; i<16; i++) { if (key[0] != key[i]) return true; } return ('\xff' != key[0]) && ('\x00' != key[0]); } QByteArray TyTCodeplug::EncryptionElement::enhancedKey(unsigned n) const { return QByteArray((char *)(_data+Offset::advancedKeys()+n*Offset::betweenAdvancedKeys()), Offset::betweenAdvancedKeys()); } void TyTCodeplug::EncryptionElement::setEnhancedKey(unsigned n, const QByteArray &key) { if (Offset::betweenAdvancedKeys() != key.size()) return; memcpy(_data+Offset::advancedKeys()+n*Offset::betweenAdvancedKeys(), key.constData(), Offset::betweenAdvancedKeys()); } bool TyTCodeplug::EncryptionElement::isBasicKeySet(unsigned n) const { QByteArray key = basicKey(n); for (int i=0; i<2; i++) if (key[0] != key[i]) return true; return ('\xff'!=key[0]) && ('\x00' != key[0]); } QByteArray TyTCodeplug::EncryptionElement::basicKey(unsigned n) const { return QByteArray((char *)(_data+Offset::basicKeys()+n*Offset::betweenBasicKeys()), Offset::betweenBasicKeys()); } void TyTCodeplug::EncryptionElement::setBasicKey(unsigned n, const QByteArray &key) { if (Offset::betweenBasicKeys() != key.size()) return; memcpy(_data+Offset::basicKeys()+n*Offset::betweenBasicKeys(), key.constData(), Offset::betweenBasicKeys()); } bool TyTCodeplug::EncryptionElement::fromCommercialExt(CommercialExtension *encr, Context &ctx) { Q_UNUSED(encr) // Clear all keys clear(); // Encode each key type separately for (unsigned int i=0; i() && i(i+1)->key()); for (unsigned int i=0; i() && i(i+1)->key()); return true; } bool TyTCodeplug::EncryptionElement::updateCommercialExt(Context &ctx) { CommercialExtension *ext = ctx.config()->commercialExtension(); for (unsigned i=0; isetName(QString("Enhanced Key %1").arg(i+1)); ctx.add(key,i+1); key->fromHex(enhancedKey(i).toHex()); ext->encryptionKeys()->add(key); } for (unsigned i=0; isetName(QString("Basic Key %1").arg(i+1)); ctx.add(key,i+1); key->fromHex(basicKey(i).toHex()); ext->encryptionKeys()->add(key); } return ext; } bool TyTCodeplug::EncryptionElement::linkCommercialExt(CommercialExtension *ext, Context &ctx) { Q_UNUSED(ext); Q_UNUSED(ctx); return true; } /* ******************************************************************************************** * * Implementation of TyTCodeplug::MessageElement * ******************************************************************************************** */ TyTCodeplug::MessageElement::MessageElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } TyTCodeplug::MessageElement::MessageElement(uint8_t *ptr) : Element(ptr, MessageElement::size()) { // pass... } void TyTCodeplug::MessageElement::clear() { memset(_data, 0xffff, size()); } bool TyTCodeplug::MessageElement::isValid() const { return Element::isValid() && (0xffff != getUInt16_le(0)); } QString TyTCodeplug::MessageElement::text() const { return readUnicode(0, Limit::length(), 0x0000); } void TyTCodeplug::MessageElement::setText(const QString &text) { writeUnicode(0, text, Limit::length(), 0x0000); } bool TyTCodeplug::MessageElement::encode(SMSTemplate *sms, const ErrorStack &err) { Q_UNUSED(err); setText(sms->message()); return true; } SMSTemplate * TyTCodeplug::MessageElement::decode(const ErrorStack &err) { if (! isValid()) { errMsg(err) << "Cannot decode invalid SMS message."; return nullptr; } SMSTemplate *sms = new SMSTemplate(); sms->setName("Message"); sms->setMessage(text()); return sms; } /* ******************************************************************************************** * * Implementation of TyTCodeplug::MessageBankElement * ******************************************************************************************** */ TyTCodeplug::MessageBankElement::MessageBankElement(uint8_t *ptr, size_t size) : Element(ptr, size) { // pass... } TyTCodeplug::MessageBankElement::MessageBankElement(uint8_t *ptr) : Element(ptr, MessageBankElement::size()) { // pass... } void TyTCodeplug::MessageBankElement::clear() { for (unsigned int i=0; ismsExtension()->smsTemplates()->count(); count = std::min(count, Limit::messages()); for (unsigned int i=0; ismsExtension()->smsTemplates()->message(i), err)) { errMsg(err) << "Cannot encode" << i+1 << "-th preset message."; return false; } } return true; } bool TyTCodeplug::MessageBankElement::decode(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; ismsExtension()->smsTemplates()->add(sms); } return true; } /* ******************************************************************************************** * * Implementation of TyTCodeplug * ******************************************************************************************** */ TyTCodeplug::TyTCodeplug(QObject *parent) : Codeplug(parent) { // pass... } TyTCodeplug::~TyTCodeplug() { // pass... } bool TyTCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) const { Q_UNUSED(err) // All indices as 1-based. That is, the first channel gets index 1. // Map radio IDs for (int i=0; iradioIDs()->count(); i++) ctx.add(config->radioIDs()->getId(i), i+1); // Map digital and DTMF contacts for (int i=0, d=0, a=0; icontacts()->count(); i++) { if (config->contacts()->contact(i)->is()) { ctx.add(config->contacts()->contact(i)->as(), d+1); d++; } else if (config->contacts()->contact(i)->is()) { ctx.add(config->contacts()->contact(i)->as(), a+1); a++; } } // Map rx group lists for (int i=0; irxGroupLists()->count(); i++) ctx.add(config->rxGroupLists()->list(i), i+1); // Map channels for (int i=0; ichannelList()->count(); i++) ctx.add(config->channelList()->channel(i), i+1); // Map zones for (int i=0; izones()->count(); i++) ctx.add(config->zones()->zone(i), i+1); // Map scan lists for (int i=0; iscanlists()->count(); i++) ctx.add(config->scanlists()->scanlist(i), i+1); // Map DMR APRS systems for (int i=0,a=0,d=0; iposSystems()->count(); i++) { if (config->posSystems()->system(i)->is()) { ctx.add(config->posSystems()->system(i)->as(), d+1); d++; } else if (config->posSystems()->system(i)->is()) { ctx.add(config->posSystems()->system(i)->as(), a+1); a++; } } // Map roaming for (int i=0; iroamingZones()->count(); i++) ctx.add(config->roamingZones()->zone(i), i+1); // Index basic (DMR) and AES keys if (CommercialExtension *ext = config->commercialExtension()) { unsigned int basicIndex = 1, aesIndex = 1; for (int i=0; iencryptionKeys()->count(); i++) { if (ext->encryptionKeys()->key(i)->is()) { ctx.add(ext->encryptionKeys()->key(i)->as(), basicIndex++); } else if (ext->encryptionKeys()->key(i)->is()) { ctx.add(ext->encryptionKeys()->key(i)->as(), aesIndex++); } } } return true; } void TyTCodeplug::clear() { // Clear timestamp this->clearTimestamp(); // Clear general config this->clearGeneralSettings(); // Clear menu settings this->clearMenuSettings(); // Clear button settings this->clearButtonSettings(); // Clear text messages this->clearTextMessages(); // Clear privacy keys this->clearPrivacyKeys(); // Clear emergency systems this->clearEmergencySystems(); // Clear RX group lists this->clearGroupLists(); // Clear zones & zone extensions this->clearZones(); // Clear scan lists; this->clearScanLists(); // Clear GPS systems this->clearPositioningSystems(); // Clear channels this->clearChannels(); // Clear contacts this->clearContacts(); } bool TyTCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) { // Check if default DMR id is set. if (config->settings()->defaultIdRef()->isNull()) { errMsg(err) << "Cannot encode TyT codeplug: No default radio ID specified."; return false; } // Create index<->object table. Context ctx(config); ctx.addTable(&BasicEncryptionKey::staticMetaObject); ctx.addTable(&AESEncryptionKey::staticMetaObject); if (! index(config, ctx)) return false; return this->encodeElements(flags, ctx); } bool TyTCodeplug::decode(Config *config, const ErrorStack &err) { // Create index<->object table. Context ctx(config); ctx.addTable(&BasicEncryptionKey::staticMetaObject); ctx.addTable(&AESEncryptionKey::staticMetaObject); // Clear config object config->clear(); return this->decodeElements(ctx, err); } bool TyTCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) { // Set timestamp if (! this->encodeTimestamp()) { errMsg(err) << "Cannot encode time-stamp."; return false; } // General config if (! this->encodeGeneralSettings(ctx.config(), flags, ctx)) { errMsg(err) << "Cannot encode general settings."; return false; } // Define Contacts if (! this->encodeContacts(ctx.config(), flags, ctx)) { errMsg(err) << "Cannot encode contacts."; return false; } // Define RX GroupLists if (! this->encodeGroupLists(ctx.config(), flags, ctx)) { errMsg(err) << "Cannot encode group lists."; return false; } // Define encryption keys if (! this->encodePrivacyKeys(ctx.config(), flags, ctx)) { errMsg(err) << "Cannot encode encryption keys."; return false; } // Define Channels if (! this->encodeChannels(ctx.config(), flags, ctx)) { errMsg(err) << "Cannot encode channels."; return false; } // Define Zones if (! this->encodeZones(ctx.config(), flags, ctx)) { errMsg(err) << "Cannot encode zones."; return false; } // Define Scanlists if (! this->encodeScanLists(ctx.config(), flags, ctx)) { errMsg(err) << "Cannot encode scan lists."; return false; } // Define GPS systems if (! this->encodePositioningSystems(ctx.config(), flags, ctx)) { errMsg(err) << "Cannot encode positioning systems."; return false; } // Encode button settings if (! this->encodeButtonSettings(ctx.config(), flags, ctx)) { errMsg(err) << "Cannot encode button settings."; return false; } // Encode text messages if (! this->encodeTextMessages(ctx, flags, err)) { errMsg(err) << "Cannot encode text messages."; return false; } return true; } bool TyTCodeplug::decodeElements(Context &ctx, const ErrorStack &err) { // General config if (! this->decodeGeneralSettings(ctx.config(), err)) { errMsg(err) << "Cannot decode general settings."; return false; } // Define Contacts if (! this->createContacts(ctx.config(), ctx, err)) { errMsg(err) << "Cannot create contacts."; return false; } // Define RX GroupLists if (! this->createGroupLists(ctx.config(), ctx, err)) { errMsg(err) << "Cannot create group lists."; return false; } // Define Channels if (! this->createChannels(ctx.config(), ctx, err)) { errMsg(err) << "Cannot create channels."; return false; } // Define Zones if (! this->createZones(ctx.config(), ctx, err)) { errMsg(err) << "Cannot create zones."; return false; } // Define Scanlists if (! this->createScanLists(ctx.config(), ctx, err)) { errMsg(err) << "Cannot create scan lists."; return false; } // Define GPS systems if (! this->createPositioningSystems(ctx.config(), ctx, err)) { errMsg(err) << "Cannot create positioning systems."; return false; } // Decode button settings if (! this->decodeButtonSetttings(ctx.config(), err)) { errMsg(err) << "Cannot decode button settings."; return false; } // Decode text messages if (! this->decodeTextMessages(ctx, err)) { errMsg(err) << "Cannot decode text messages."; return false; } // Decode encryption settings if (! this->decodePrivacyKeys(ctx.config(), ctx, err)) { errMsg(err) << "Cannot decode encryption settings."; return false; } // Link RX GroupLists if (! this->linkGroupLists(ctx, err)) { errMsg(err) << "Cannot link group lists."; return false; } // Link Channels if (! this->linkChannels(ctx, err)) { errMsg(err) << "Cannot link channels."; return false; } // Link Zones if (! this->linkZones(ctx, err)) { errMsg(err) << "Cannot link zones."; return false; } // Link Scanlists if (! this->linkScanLists(ctx, err)) { errMsg(err) << "Cannot link scan lists."; return false; } // Link GPS systems if (! this->linkPositioningSystems(ctx, err)) { errMsg(err) << "Cannot link positioning systems."; return false; } return true; } qdmr-0.12.3/lib/tyt_codeplug.hh000066400000000000000000001531621501654372000163440ustar00rootroot00000000000000#ifndef TYT_CODEPLUG_HH #define TYT_CODEPLUG_HH #include #include "codeplug.hh" #include "signaling.hh" #include "channel.hh" #include "contact.hh" #include "tyt_extensions.hh" class DMRContact; class Zone; class RXGroupList; class ScanList; class GPSSystem; class SMSExtension; class SMSTemplate; /** Base class of all TyT codeplugs. This class implements the majority of all codeplug elements * present in all TyT codeplugs. This eases the support of several TyT radios, as only the * differences in the codeplug to this base class must be implemented. * * @ingroup tyt */ class TyTCodeplug : public Codeplug { Q_OBJECT public: /** Represents a single channel (analog or digital) within the TyT codeplug. * * Memory layout of encoded channel: * @verbinclude tyt_channel.txt */ class ChannelElement: public Codeplug::Element { public: /** Possible modes for the channel, i.e. analog and digital. */ enum Mode { MODE_ANALOG = 1, ///< Analog channel. MODE_DIGITAL = 2 ///< Digital channel. }; /** Bandwidth of the channel. */ enum Bandwidth { BW_12_5_KHZ = 0, ///< 12.5 kHz narrow, (default for binary channels). BW_20_KHZ = 1, ///< 20 kHz (really?) BW_25_KHZ = 2 ///< 25kHz wide. }; /** Possible privacy types. */ enum PrivacyType { PRIV_NONE = 0, ///< No privacy. PRIV_BASIC = 1, ///< Basic privacy. PRIV_ENHANCED = 2 ///< Enhanced privacy. }; /** TX Admit criterion. */ enum Admit { ADMIT_ALWAYS = 0, ///< Always allow TX. ADMIT_CH_FREE = 1, ///< Allow TX if channel is free. ADMIT_TONE = 2, ///< Allow TX if CTCSS tone matches. ADMIT_COLOR = 3, ///< Allow TX if color-code matches. }; protected: /** Constructs a channel from the given memory. */ ChannelElement(uint8_t *ptr, size_t size); public: /** Constructs a channel from the given memory. */ ChannelElement(uint8_t *ptr); /** Destructor. */ virtual ~ChannelElement(); /** Returns @c true if channel is valid/enabled. */ bool isValid() const; /** Clears/resets the channel and therefore disables it. */ void clear(); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x0040; } /** Returns the mode of the channel. */ virtual Mode mode() const; /** Sets the mode of the channel. */ virtual void setMode(Mode setMode); /** Returns the bandwidth of the (analog) channel. */ virtual FMChannel::Bandwidth bandwidth() const; /** Sets the bandwidth of the (analog) channel. */ virtual void setBandwidth(FMChannel::Bandwidth bw); /** Returns @c true if the channel has auto scan enabled. */ virtual bool autoScan() const; /** Enables/disables auto scan for this channel. */ virtual void enableAutoScan(bool enable); /** Returns @c true if the channel has lone worker enabled. */ virtual bool loneWorker() const; /** Enables/disables lone worker for this channel. */ virtual void enableLoneWorker(bool enable); /** Returns @c true if the channel has talkaround enabled. */ virtual bool talkaround() const; /** Enables/disables talkaround for this channel. */ virtual void enableTalkaround(bool enable); /** Returns @c true if the channel has rx only enabled. */ virtual bool rxOnly() const; /** Enables/disables rx only for this channel. */ virtual void enableRXOnly(bool enable); /** Returns the time slot of this channel. */ virtual DMRChannel::TimeSlot timeSlot() const; /** Sets the time slot of this channel. */ virtual void setTimeSlot(DMRChannel::TimeSlot ts); /** Returns the color code of this channel. */ virtual uint8_t colorCode() const; /** Sets the color code of this channel. */ virtual void setColorCode(uint8_t ts); /** Returns the index of the privacy system (key). */ virtual uint8_t privacyIndex() const; /** Sets the index of the privacy system (key). */ virtual void setPrivacyIndex(uint8_t ts); /** Returns the type of the privacy system. */ virtual PrivacyType privacyType() const; /** Sets the type of the privacy system. */ virtual void setPrivacyType(PrivacyType type); /** Returns @c true if the channel has private call confirmation enabled. */ virtual bool privateCallConfirm() const; /** Enables/disables private call confirmation for this channel. */ virtual void enablePrivateCallConfirm(bool enable); /** Returns @c true if the channel has data call confirmation enabled. */ virtual bool dataCallConfirm() const; /** Enables/disables data call confirmation for this channel. */ virtual void enableDataCallConfirm(bool enable); /** Returns some weird reference frequency setting for reception. */ virtual TyTChannelExtension::RefFrequency rxRefFrequency() const; /** Sets some weird reference frequency setting for reception. */ virtual void setRXRefFrequency(TyTChannelExtension::RefFrequency ref); /** Returns some weird reference frequency setting for transmission. */ virtual TyTChannelExtension::RefFrequency txRefFrequency() const; /** Sets some weird reference frequency setting for transmission. */ virtual void setTXRefFrequency(TyTChannelExtension::RefFrequency ref); /** Returns @c true if the channel has alarm confirmation enabled. */ virtual bool emergencyAlarmACK() const; /** Enables/disables alarm confirmation for this channel. */ virtual void enableEmergencyAlarmACK(bool enable); /** Returns @c true if the channel has display PTT ID enabled. */ virtual bool displayPTTId() const; /** Enables/disables PTT ID display for this channel. */ virtual void enableDisplayPTTId(bool enable); /** Returns @c true if the channel has VOX enabled. */ virtual bool vox() const; /** Enables/disables VOX for this channel. */ virtual void enableVOX(bool enable); /** Returns the admit criterion for this channel. */ virtual Admit admitCriterion() const; /** Sets the admit criterion for this channel. */ virtual void setAdmitCriterion(Admit admit); /** Returns the transmit contact index (+1) for this channel. */ virtual uint16_t contactIndex() const; /** Sets the transmit contact index (+1) for this channel. */ virtual void setContactIndex(uint16_t idx); /** Returns @c true, if the transmit time out is disabled. */ virtual bool txTimeOutDisabled() const; /** Returns the transmit time-out in seconds. */ virtual Interval txTimeOut() const; /** Sets the transmit time-out in seconds. */ virtual void setTXTimeOut(const Interval &tot); /** Disables the transmit timeout. */ virtual void resetTXTimeOut(); /** Returns the transmit time-out re-key delay in seconds. */ virtual uint8_t txTimeOutRekeyDelay() const; /** Sets the transmit time-out re-key delay in seconds. */ virtual void setTXTimeOutRekeyDelay(uint8_t delay); /** Returns the emergency system index (+1) for this channel. */ virtual uint8_t emergencySystemIndex() const; /** Sets the emergency system index (+1) for this channel. */ virtual void setEmergencySystemIndex(uint8_t idx); /** Returns the scan-list index (+1) for this channel. */ virtual uint8_t scanListIndex() const; /** Sets the scan-list index (+1) for this channel. */ virtual void setScanListIndex(uint8_t idx); /** Returns the RX group list index (+1) for this channel. */ virtual uint8_t groupListIndex() const; /** Sets the RX group list index (+1) for this channel. */ virtual void setGroupListIndex(uint8_t idx); /** Returns the positioning system index (+1) for this channel. */ virtual uint8_t positioningSystemIndex() const; /** Sets the positioning system index (+1) for this channel. */ virtual void setPositioningSystemIndex(uint8_t idx); /** Returns @c true if the channel has DTMF decoding enabled. */ virtual bool dtmfDecode(uint8_t idx) const; /** Enables/disables DTMF decoding this channel. */ virtual void setDTMFDecode(uint8_t idx, bool enable); /** Returns the RX frequency in Hz. */ virtual Frequency rxFrequency() const; /** Sets the RX frequency in Hz. */ virtual void setRXFrequency(const Frequency &Hz); /** Returns the TX frequency in Hz. */ virtual Frequency txFrequency() const; /** Sets the TX frequency in Hz. */ virtual void setTXFrequency(const Frequency &Hz); /** Returns the CTCSS/DSC signaling for RX. */ virtual SelectiveCall rxSignaling() const; /** Sets the CTCSS/DSC signaling for RX. */ virtual void setRXSignaling(const SelectiveCall &code); /** Returns the CTCSS/DSC signaling for TX. */ virtual SelectiveCall txSignaling() const; /** Sets the CTCSS/DSC signaling for TX. */ virtual void setTXSignaling(const SelectiveCall &code); /** Returns the signaling system index (+1) for RX. */ virtual uint8_t rxSignalingSystemIndex() const; /** Sets the signaling system index (+1) for RX. */ virtual void setRXSignalingSystemIndex(uint8_t idx); /** Returns the signaling system index (+1) for TX. */ virtual uint8_t txSignalingSystemIndex() const; /** Sets the signaling system index (+1) for TX. */ virtual void setTXSignalingSystemIndex(uint8_t idx); /** Returns @c true if the channel transmits GPS information enabled. */ virtual bool txGPSInfo() const; /** Enables/disables transmission of GPS information for this channel. */ virtual void enableTXGPSInfo(bool enable); /** Returns @c true if the channel receives GPS information enabled. */ virtual bool rxGPSInfo() const; /** Enables/disables reception of GPS information for this channel. */ virtual void enableRXGPSInfo(bool enable); /** Returns the name of this channel. */ virtual QString name() const; /** Sets the name of this channel. */ virtual void setName(const QString &setName); /** Constructs a generic @c Channel object from the codeplug channel. */ virtual Channel *toChannelObj(const ErrorStack &err=ErrorStack()) const; /** Links a previously constructed channel to the rest of the configuration. */ virtual bool linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Initializes this codeplug channel from the given generic configuration. */ virtual void fromChannelObj(const Channel *c, Context &ctx); public: /** Some limits of the element. */ struct Limit: public Element::Limit { /** Maximum length of the name. */ static constexpr unsigned int nameLength() { return 16; } }; protected: /** Some internal offsets within the element. */ struct Offset: public Element::Offset { /// @cond DO_NOT_DOCUMENT static constexpr Offset::Bit mode() { return {0x0000, 0}; } static constexpr Offset::Bit bandwidth() { return {0x0000, 2}; } static constexpr Offset::Bit autoscan() { return {0x0000, 4}; } static constexpr Offset::Bit loneworker() { return {0x0000, 8}; } static constexpr Offset::Bit talkaround() { return {0x0001, 0}; } static constexpr Offset::Bit rxonly() { return {0x0001, 1}; } static constexpr Offset::Bit timeslot() { return {0x0001, 2}; } static constexpr Offset::Bit colorcode() { return {0x0001, 4}; } static constexpr Offset::Bit privacyIndex() { return {0x0002, 0}; } static constexpr Offset::Bit privacyType() { return {0x0002, 4}; } static constexpr Offset::Bit privateCallConfirm() { return {0x0002, 6}; } static constexpr Offset::Bit dataCallConfirm() { return {0x0002, 7}; } static constexpr Offset::Bit rxRefFrequency() { return {0x0003, 0}; } static constexpr Offset::Bit emergencyAlarmACK() { return {0x0003, 3}; } static constexpr Offset::Bit displayPTTId() { return {0x0003, 7}; } static constexpr Offset::Bit txRefFrequency() { return {0x0004, 0}; } static constexpr Offset::Bit vox() { return {0x0004, 4}; } static constexpr Offset::Bit admitCriterion() { return {0x0004, 6}; } static constexpr unsigned int contactIndex() { return 0x0006; } static constexpr Offset::Bit txTimeOut() { return {0x0008, 0}; } static constexpr unsigned int txTimeOutRekeyDelay() { return 0x0009; } static constexpr unsigned int emergencySystemIndex() { return 0x000a; } static constexpr unsigned int scanListIndex() { return 0x000b; } static constexpr unsigned int groupListIndex() { return 0x000c; } static constexpr unsigned int positioningSystemIndex() { return 0x000d; } static constexpr unsigned int dtmfDecode() { return 0x000e; } static constexpr unsigned int rxFrequency() { return 0x0010; } static constexpr unsigned int txFrequency() { return 0x0014; } static constexpr unsigned int rxSignaling() { return 0x018; } static constexpr unsigned int txSignaling() { return 0x01a; } static constexpr unsigned int rxSignalingSystemIndex() { return 0x01c; } static constexpr unsigned int txSignalingSystemIndex() { return 0x01d; } static constexpr Offset::Bit txGPSInfo() { return {0x001f, 0}; } static constexpr Offset::Bit rxGPSInfo() { return {0x001f, 1}; } static constexpr unsigned int name() { return 0x0020; } /// @endcond }; }; /** Represents a digital (DMR) contact within the codeplug. * * Memory layout of encoded contact: * @verbinclude tyt_contact.txt */ class ContactElement: public Codeplug::Element { protected: /** Constructor. */ ContactElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ContactElement(uint8_t *ptr); /** Destructor. */ virtual ~ContactElement(); void clear(); bool isValid() const; /** Returns the DMR ID of the contact. */ virtual uint32_t dmrId() const; /** Sets the DMR ID of the contact. */ virtual void setDMRId(uint32_t id); /** Returns the call-type of the contact. */ virtual DMRContact::Type callType() const; /** Sets the call-type of the contact. */ virtual void setCallType(DMRContact::Type type); /** Returns @c true if the ring-tone is enabled for this contact. */ virtual bool ringTone() const; /** Enables/disables the ring-tone for this contact. */ virtual void enableRingTone(bool enable); /** Returns the name of the contact. */ virtual QString name() const; /** Sets the name of the contact. */ virtual void setName(const QString &nm); /** Encodes the give contact. */ virtual bool fromContactObj(const DMRContact *contact); /** Creates a contact. */ virtual DMRContact *toContactObj() const; }; /** Represents a zone within the codeplug. * Please note that a zone consists of two elements the @c ZoneElement and the @c ZoneExtElement. * The latter adds additional channels for VFO A and the channels for VFO B. * * Memory layout of encoded zone: * @verbinclude tyt_zone.txt */ class ZoneElement: public Codeplug::Element { protected: /** Constructor. */ ZoneElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ZoneElement(uint8_t *ptr); /** Desturctor. */ virtual ~ZoneElement(); void clear(); bool isValid() const; /** Returns the name of the zone. */ virtual QString name() const; /** Sets the name of the zone. */ virtual void setName(const QString &setName); /** Returns the index (+1) of the @c n-th member. */ virtual uint16_t memberIndex(unsigned n) const; /** Sets the index (+1) of the @c n-th member. */ virtual void setMemberIndex(unsigned n, uint16_t idx); /** Encodes a given zone object. */ virtual bool fromZoneObj(const Zone *zone, Context &ctx); /** Creates a zone. */ virtual Zone *toZoneObj() const; /** Links the created zone to channels. */ virtual bool linkZone(Zone *zone, Context &ctx) const; }; /** Representation of an RX group list within the codeplug. * * Memory layout of encoded RX group list: * @verbinclude tyt_grouplist.txt */ class GroupListElement: public Codeplug::Element { protected: /** Constructor. */ GroupListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ GroupListElement(uint8_t *ptr); /** Destructor. */ virtual ~GroupListElement(); void clear(); bool isValid() const; /** Returns the name of the group list. */ virtual QString name() const; /** Sets the name of the group list. */ virtual void setName(const QString &nm); /** Returns the n-th member index. */ virtual uint16_t memberIndex(unsigned n) const; /** Sets the n-th member index. */ virtual void setMemberIndex(unsigned n, uint16_t idx); /** Encodes the given group list. */ virtual bool fromGroupListObj(const RXGroupList *lst, Context &ctx); /** Creates a group list object. */ virtual RXGroupList *toGroupListObj(Context &ctx); /** Links the given group list. */ virtual bool linkGroupListObj(RXGroupList *lst, Context &ctx); }; /** Represents a scan list within the codeplug. * * Memory layout of encoded scan list (0x0068 bytes): * @verbinclude tyt_scanlist.txt */ class ScanListElement: public Codeplug::Element { protected: /** Constructor. */ ScanListElement(uint8_t *ptr, size_t size); public: /** Constructor. */ ScanListElement(uint8_t *ptr); /** Destructor. */ virtual ~ScanListElement(); bool isValid() const; void clear(); /** Returns the name of the scan list. */ virtual QString name() const; /** Sets the name of the scan list. */ virtual void setName(const QString &nm); /** Returns the index (+1) of the first priority channel. */ virtual uint16_t priorityChannel1Index() const; /** Set the index (+1) of the first priority channel. */ virtual void setPriorityChannel1Index(uint16_t idx); /** Returns the index (+1) of the second priority channel. */ virtual uint16_t priorityChannel2Index() const; /** Set the index (+1) of the second priority channel. */ virtual void setPriorityChannel2Index(uint16_t idx); /** Returns the index (+1) of the TX channel. 0=current, 0xffff=none. */ virtual uint16_t txChannelIndex() const; /** Sets the index (+1) of the TX channel. 0=current, 0xffff=none. */ virtual void setTXChannelIndex(uint16_t idx); /** Returns the hold time in ms. */ virtual unsigned holdTime() const; /** Sets the hold time in ms. */ virtual void setHoldTime(unsigned time); /** Returns the priority sample time in ms. */ virtual unsigned prioritySampleTime() const; /** Sets the priority sample time in ms. */ virtual void setPrioritySampleTime(unsigned time); /** Returns the n-th member index. */ virtual uint16_t memberIndex(unsigned n) const; /** Sets the n-th member index. */ virtual void setMemberIndex(unsigned n, uint16_t idx); /** Encodes the given scan list. */ virtual bool fromScanListObj(const ScanList *lst, Context &ctx); /** Creates a scan list. */ virtual ScanList *toScanListObj(Context &ctx); /** Links the scan list object. */ virtual bool linkScanListObj(ScanList *lst, Context &ctx, const ErrorStack &err=ErrorStack()); }; /** Codeplug representation of the general settings. * * Memory layout of encoded settings: * @verbinclude tyt_settings.txt */ class GeneralSettingsElement: public Codeplug::Element { protected: /** Hidden constructor. */ GeneralSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit GeneralSettingsElement(uint8_t *ptr); /** Destructor. */ virtual ~GeneralSettingsElement(); void clear(); /** Returns the first intro line. */ virtual QString introLine1() const; /** Sets the first intro line. */ virtual void setIntroLine1(const QString line); /** Returns the second intro line. */ virtual QString introLine2() const; /** Sets the second intro line. */ virtual void setIntroLine2(const QString line); /** Returns the monitor type. */ virtual TyTSettingsExtension::MonitorType monitorType() const; /** Sets the monitor type. */ virtual void setMonitorType(TyTSettingsExtension::MonitorType type); /** Returns @c true if all LEDs are disabled. */ virtual bool allLEDsDisabled() const; /** Enables/disables all LEDs. */ virtual void disableAllLEDs(bool disable); /** Returns @c true, if save preamble is enabled. */ virtual bool savePreamble() const; /** Enables/disables save preamble. */ virtual void setSavePreamble(bool enable); /** Returns @c true, if save RX mode is enabled. */ virtual bool saveModeRX() const; /** Enables/disables save mode RX. */ virtual void setSaveModeRX(bool enable); /** Returns @c true, if all tones are disabled. */ virtual bool allTonesDisabled() const; /** Enables/disables all tones. */ virtual void disableAllTones(bool disable); /** Returns @c true, if the channel free indication tone is enabled. */ virtual bool chFreeIndicationTone() const; /** Enables/disables the channel free indication tone. */ virtual void setChFreeIndicationTone(bool enable); /** Returns @c true, if password and lock is enabled. */ virtual bool passwdAndLock() const; /** Enables/disables password and lock. */ virtual void enablePasswdAndLock(bool enable); /** Returns @c true, if the talk permit tone is enabled for DMR channels. */ virtual bool talkPermitToneDigital() const; /** Enables/disables talk permit tone for DMR channels. */ virtual void enableTalkPermitToneDigital(bool enable); /** Returns @c true, if the talk permit tone is enabled for analog channels. */ virtual bool talkPermitToneAnalog() const; /** Enables/disables talk permit tone for analog channels. */ virtual void enableTalkPermitToneAnalog(bool enable); /** Returns @c true, if intro picture is enabled. */ virtual bool introPicture() const; /** Enables/disables the intro picture. */ virtual void enableIntroPicture(bool enable); /** Returns the default DMR ID of the radio. */ virtual uint32_t dmrId() const; /** Sets the default DMR ID of the radio. */ virtual void setDMRId(uint32_t id); /** Returns the TX preamble duration. */ virtual unsigned txPreambleDuration() const; /** Sets the TX preamble duration. */ virtual void setTXPreambleDuration(unsigned ms); /** Returns the group call hang time. */ virtual unsigned groupCallHangTime() const; /** Sets the group call hang time. */ virtual void setGroupCallHangTime(unsigned ms); /** Returns the private call hang time. */ virtual unsigned privateCallHangTime() const; /** Sets the private call hang time. */ virtual void setPrivateCallHangTime(unsigned ms); /** Returns the VOX sensitivity. */ virtual unsigned voxSesitivity() const; /** Sets the group call hang time. */ virtual void setVOXSesitivity(unsigned ms); /** Returns the low-battery warning interval. */ virtual unsigned lowBatteryInterval() const; /** Sets the low-battery warning interval. */ virtual void setLowBatteryInterval(unsigned sec); /** Returns @c true if the call-alert is continuous. */ virtual bool callAlertToneIsContinuous() const; /** Returns the call-alert tone duration. */ virtual unsigned callAlertToneDuration() const; /** Sets the call-alert tone duration. */ virtual void setCallAlertToneDuration(unsigned sec); /** Sets the call-alert tone continuous. */ virtual void setCallAlertToneContinuous(); /** Returns the lone-worker response time. */ virtual unsigned loneWorkerResponseTime() const; /** Sets the lone-worker response time. */ virtual void setLoneWorkerResponseTime(unsigned min); /** Returns the lone-worker reminder time. */ virtual unsigned loneWorkerReminderTime() const; /** Sets the lone-worker reminder time. */ virtual void setLoneWorkerReminderTime(unsigned min); /** Returns the scan digital hang time. */ virtual unsigned scanDigitalHangTime() const; /** Sets the scan digital hang time. */ virtual void setScanDigitalHangTime(unsigned ms); /** Returns the scan analog hang time. */ virtual unsigned scanAnalogHangTime() const; /** Sets the scan analog hang time. */ virtual void setScanAnalogHangTime(unsigned ms); /** Returns @c true if the backlight is always on. */ virtual bool backlightIsAlways() const; /** Returns the backlight time. */ virtual unsigned backlightTime() const; /** Sets the backlight time. */ virtual void setBacklightTime(unsigned sec); /** Turns the backlight always on. */ virtual void backlightTimeSetAlways(); /** Returns @c true if the keypad lock is manual. */ virtual bool keypadLockIsManual() const; /** Returns the keypad lock time. */ virtual unsigned keypadLockTime() const; /** Sets the keypad lock time. */ virtual void setKeypadLockTime(unsigned sec); /** Set keypad lock to manual. */ virtual void keypadLockTimeSetManual(); /** Returns the 8-digit power-on password. */ virtual uint32_t powerOnPassword() const; /** Sets the 8-digit power-on password. */ virtual void setPowerOnPassword(uint32_t passwd); /** Returns @c true, if the radio programming password is enabled. */ virtual bool radioProgPasswordEnabled() const; /** Returns the 8-digit radio programming password. */ virtual uint32_t radioProgPassword() const; /** Sets the 8-digit radio programming password. */ virtual void setRadioProgPassword(uint32_t passwd); /** Disables the radio programming password. */ virtual void radioProgPasswordDisable(); /** Returns @c true, if the PC programming password is enabled. */ virtual bool pcProgPasswordEnabled() const; /** Returns the PC programming password. */ virtual QString pcProgPassword() const; /** Sets the PC programming password. */ virtual void setPCProgPassword(const QString &pass); /** Disables the PC programming password. */ virtual void pcProgPasswordDisable(); /** Returns the radio name. */ virtual QString radioName() const; /** Sets the radio name. */ virtual void setRadioName(const QString &name); /** Encodes the general settings. */ virtual bool fromConfig(const Config *config); /** Updates config from general settings. */ virtual bool updateConfig(Config *config); }; /** Codeplug representation of programming time-stamp and CPS version. * * Memory layout of encoded timestamp: * @verbinclude tyt_timestamp.txt */ class TimestampElement: public Codeplug::Element { protected: /** Hidden constructor. */ TimestampElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit TimestampElement(uint8_t *ptr); /** Destructor. */ virtual ~TimestampElement(); void clear(); /** Returns the time stamp. */ virtual QDateTime timestamp() const; /** Sets the time stamp. */ virtual void setTimestamp(const QDateTime &ts); /** Returns the CPS version. */ virtual QString cpsVersion() const; }; /** Represents a single GPS system within the codeplug. * * Memory layout of encoded GPS system (size 0x0010 bytes): * @verbinclude tyt_gpssystem.txt */ class GPSSystemElement: public Codeplug::Element { protected: /** Hidden constructor. */ GPSSystemElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit GPSSystemElement(uint8_t *ptr); /** Destructor. */ virtual ~GPSSystemElement(); bool isValid() const; void clear(); /** Returns @c true if the revert channel is the current one. */ virtual bool revertChannelIsSelected() const; /** Returns the revert channel index (+1). */ virtual uint16_t revertChannelIndex() const; /** Sets the revert channel index (+1). */ virtual void setRevertChannelIndex(uint16_t idx); /** Sets the revert channel to the current one. */ virtual void setRevertChannelSelected(); /** Returns @c true if the repeat interval is disabled. */ virtual bool repeatIntervalDisabled() const; /** Returns the repeat interval. */ virtual unsigned repeatInterval() const; /** Sets the repeat interval in seconds. */ virtual void setRepeatInterval(unsigned sec); /** Disables the GPS repeat interval. */ virtual void disableRepeatInterval(); /** Returns @c true if the destination contact is disabled. */ virtual bool destinationContactDisabled() const; /** Returns the destination contact index (+1). */ virtual uint16_t destinationContactIndex() const; /** Sets the destination contact index (+1). */ virtual void setDestinationContactIndex(uint16_t idx); /** Disables the destination contact. */ virtual void disableDestinationContact(); /** Encodes the given GPS system. */ virtual bool fromGPSSystemObj(GPSSystem *sys, Context &ctx); /** Constructs a GPS system. */ virtual GPSSystem *toGPSSystemObj(); /** Links the given GPS system. */ virtual bool linkGPSSystemObj(GPSSystem *sys, Context &ctx); }; /** Represents all menu settings within the codeplug on the radio. * * Memory representation of the menu settings: * @verbinclude tyt_menusettings.txt */ class MenuSettingsElement: public Codeplug::Element { protected: /** Hidden constructor. */ MenuSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit MenuSettingsElement(uint8_t *ptr); /** Destructor. */ virtual ~MenuSettingsElement(); void clear(); /** Returns @c true if the menu hang time is infinite. */ virtual bool menuHangtimeIsInfinite() const; /** Returns the menu hang time in seconds. */ virtual unsigned menuHangtime() const; /** Sets the menu hang time in seconds. */ virtual void setMenuHangtime(unsigned sec); /** Sets the menu hang time to be infinite. */ virtual void infiniteMenuHangtime(); /** Returns @c true if text message menu is enabled. */ virtual bool textMessage() const; /** Enables/disables text message menu. */ virtual void enableTextMessage(bool enable); /** Returns @c true if call alert menu is enabled. */ virtual bool callAlert() const; /** Enables/disables call alert menu. */ virtual void enableCallAlert(bool enable); /** Returns @c true if contact editing is enabled. */ virtual bool contactEditing() const; /** Enables/disables contact editing. */ virtual void enableContactEditing(bool enable); /** Returns @c true if manual dial is enabled. */ virtual bool manualDial() const; /** Enables/disables manual dial. */ virtual void enableManualDial(bool enable); /** Returns @c true if contact radio-check menu is enabled. */ virtual bool remoteRadioCheck() const; /** Enables/disables contact radio-check menu. */ virtual void enableRemoteRadioCheck(bool enable); /** Returns @c true if remote monitor menu is enabled. */ virtual bool remoteMonitor() const; /** Enables/disables remote monitor menu. */ virtual void enableRemoteMonitor(bool enable); /** Returns @c true if radio enable menu is enabled. */ virtual bool remoteRadioEnable() const; /** Enables/disables radio enable menu. */ virtual void enableRemoteRadioEnable(bool enable); /** Returns @c true if radio disable menu is enabled. */ virtual bool remoteRadioDisable() const; /** Enables/disables radio disable menu. */ virtual void enableRemoteRadioDisable(bool enable); /** Returns @c true if scan menu is enabled. */ virtual bool scan() const; /** Enables/disables scan menu. */ virtual void enableScan(bool enable); /** Returns @c true if edit scan-list menu is enabled. */ virtual bool scanListEditing() const; /** Enables/disables edit scan-list menu. */ virtual void enableScanListEditing(bool enable); /** Returns @c true if call-log missed menu is enabled. */ virtual bool callLogMissed() const; /** Enables/disables call-log missed menu. */ virtual void enableCallLogMissed(bool enable); /** Returns @c true if call-log answered menu is enabled. */ virtual bool callLogAnswered() const; /** Enables/disables call-log answered menu. */ virtual void enableCallLogAnswered(bool enable); /** Returns @c true if call-log outgoing menu is enabled. */ virtual bool callLogOutgoing() const; /** Enables/disables call-log outgoing menu. */ virtual void enableCallLogOutgoing(bool enable); /** Returns @c true if talkaround menu is enabled. */ virtual bool talkaround() const; /** Enables/disables talkaround menu. */ virtual void enableTalkaround(bool enable); /** Returns @c true if tone/alert menu is enabled. */ virtual bool alertTone() const; /** Enables/disables tone/alert menu. */ virtual void enableAlertTone(bool enable); /** Returns @c true if power menu is enabled. */ virtual bool power() const; /** Enables/disables power menu. */ virtual void enablePower(bool enable); /** Returns @c true if backlight menu is enabled. */ virtual bool backlight() const; /** Enables/disables backlight menu. */ virtual void enableBacklight(bool enable); /** Returns @c true if intro screen menu is enabled. */ virtual bool bootScreen() const; /** Enables/disables intro screen menu. */ virtual void enableBootScreen(bool enable); /** Returns @c true if keypad lock menu is enabled. */ virtual bool keypadLock() const; /** Enables/disables keypad lock menu. */ virtual void enableKeypadLock(bool enable); /** Returns @c true if LED indicator menu is enabled. */ virtual bool ledIndicator() const; /** Enables/disables LED indicator menu. */ virtual void enableLEDIndicator(bool enable); /** Returns @c true if squelch menu is enabled. */ virtual bool squelch() const; /** Enables/disables squelch menu. */ virtual void enableSquelch(bool enable); /** Returns @c true if VOX menu is enabled. */ virtual bool vox() const; /** Enables/disables VOX menu. */ virtual void enableVOX(bool enable); /** Returns @c true if password menu is enabled. */ virtual bool password() const; /** Enables/disables password menu. */ virtual void enablePassword(bool enable); /** Returns @c true if display mode menu is enabled. */ virtual bool displayMode() const; /** Enables/disables display mode menu. */ virtual void enableDisplayMode(bool enable); /** Returns @c true if program radio menu is enabled. */ virtual bool radioProgramming() const; /** Enables/disables program radio menu. */ virtual void enableRadioProgramming(bool enable); /** Encodes the menu settings. */ virtual bool fromConfig(const Config *config); /** Updates config from menu settings. */ virtual bool updateConfig(Config *config); }; /** Represents all button settings within the codeplug on the radio. * * Memory representation of the button settings: * @verbinclude tyt_buttonsettings.txt */ class ButtonSettingsElement: public Codeplug::Element { public: /** The possible button actions. */ typedef enum TyTButtonSettings::ButtonAction ButtonAction; protected: /** Hidden constructor. */ ButtonSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit ButtonSettingsElement(uint8_t *ptr); /** Destructor. */ virtual ~ButtonSettingsElement(); void clear(); /** Returns the action for a short press on side button 1. */ virtual ButtonAction sideButton1Short() const; /** Sets the action for a short press on side button 1. */ virtual void setSideButton1Short(ButtonAction action); /** Returns the action for a long press on side button 1. */ virtual ButtonAction sideButton1Long() const; /** Sets the action for a short press on side button 1. */ virtual void setSideButton1Long(ButtonAction action); /** Returns the action for a short press on side button 2. */ virtual ButtonAction sideButton2Short() const; /** Sets the action for a short press on side button 2. */ virtual void setSideButton2Short(ButtonAction action); /** Returns the action for a long press on side button 2. */ virtual ButtonAction sideButton2Long() const; /** Sets the action for a short press on side button 2. */ virtual void setSideButton2Long(ButtonAction action); /** Returns the long-press duration in ms. */ virtual unsigned longPressDuration() const; /** Sets the long-press duration in ms. */ virtual void setLongPressDuration(unsigned ms); /** Encodes the button settings. */ virtual bool fromConfig(const Config *config); /** Updates config from button settings. */ virtual bool updateConfig(Config *config); }; /** Represents a single one-touch setting within the codeplug on the radio. * * Memory representation of a one-touch setting: * @verbinclude tyt_onetouchsettings.txt */ class OneTouchSettingElement: public Codeplug::Element { public: /** Possible one-touch actions. */ enum Action { CALL = 0b0000, ///< Call someone, see @c contact. MESSAGE = 0b0001, ///< Send a message, see @c message. DTMF1 = 0b1000, ///< Analog call DTMF system 1. DTMF2 = 0b1001, ///< Analog call DTMF system 2. DTMF3 = 0b1010, ///< Analog call DTMF system 3. DTMF4 = 0b1011 ///< Analog call DTMF system 4. }; /** Possible one-touch action types. */ enum Type { Disabled = 0b00, ///< Disabled one-touch. Digital = 0b01, ///< Digital call/message. Analog = 0b10 ///< Analog call. }; protected: /** Hidden constructor. */ OneTouchSettingElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit OneTouchSettingElement(uint8_t *ptr); /** Destructor. */ virtual ~OneTouchSettingElement(); bool isValid() const; void clear(); /** Returns the action to perform. */ virtual Action action() const; /** Sets the action to perform. */ virtual void setAction(Action action); /** Returns the type of the action. */ virtual Type actionType() const; /** Sets the type of the action. */ virtual void setActionType(Type action); /** Returns the message index +1. */ virtual uint8_t messageIndex() const; /** Sets the message index +1. */ virtual void setMessageIndex(uint8_t idx); /** Returns the contact index +1. */ virtual uint16_t contactIndex() const; /** Sets the contact index +1. */ virtual void setContactIndex(uint16_t idx); }; /** Represents the emergency settings within the codeplug on the radio. * * Memory representation of the emergency settings (size 0x0010 bytes): * @verbinclude tyt_emergencysettings.txt */ class EmergencySettingsElement: public Codeplug::Element { protected: /** Hidden constructor. */ EmergencySettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit EmergencySettingsElement(uint8_t *ptr); /** Destructor. */ virtual ~EmergencySettingsElement(); virtual void clear(); /** Returns @c true if emergency remote monitor is enabled. */ virtual bool emergencyRemoteMonitor() const; /** Enables/disables emergency remote monitor. */ virtual void enableEmergencyRemoteMonitor(bool enable); /** Returns @c true if remote monitor is enabled. */ virtual bool remoteMonitor() const; /** Enables/disables remote monitor. */ virtual void enableRemoteMonitor(bool enable); /** Returns @c true if radio disable is enabled. */ virtual bool radioDisable() const; /** Enables/disables radio disable. */ virtual void enableRadioDisable(bool enable); /** Returns the remote monitor duration in seconds. */ virtual unsigned remoteMonitorDuration() const; /** Sets the remote monitor duration in seconds. */ virtual void setRemoteMonitorDuration(unsigned sec); /** Returns the TX time-out in ms. */ virtual unsigned txTimeOut() const; /** Sets the TX time-out in ms. */ virtual void setTXTimeOut(unsigned ms); /** Returns the message limit. */ virtual unsigned messageLimit() const; /** Sets the message limit. */ virtual void setMessageLimit(unsigned limit); }; /** Represents a single emergency system within the radio. * * Memory representation of emergency system (size 0x0028 bytes): * @verbinclude tyt_emergencysystem.txt */ class EmergencySystemElement: public Codeplug::Element { public: /** Possible alarm type for the system. */ enum AlarmType { DISABLED = 0, ///< No alarm at all REGULAR = 1, ///< Regular alarm sound. SILENT = 2, ///< Silent alarm. SILENT_W_VOICE = 3 ///< silent alarm with voice. }; /** Possible alarm modes for the system. */ enum AlarmMode { ALARM = 0, ///< Just alarm. ALARM_W_CALL = 1, ///< Alarm + call. ALARM_W_VOICE = 2 ///< Alarm + call + voice? }; protected: /** Hidden constructor. */ EmergencySystemElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit EmergencySystemElement(uint8_t *ptr); /** Destructor. */ virtual ~EmergencySystemElement(); bool isValid() const; void clear(); /** Returns the name of the system. */ virtual QString name() const; /** Sets the name of the system. */ virtual void setName(const QString &name); /** Returns the alarm type of the system. */ virtual AlarmType alarmType() const; /** Sets the alarm type of the system. */ virtual void setAlarmType(AlarmType type); /** Returns the alarm mode of the system. */ virtual AlarmMode alarmMode() const; /** Sets the alarm mode of the system. */ virtual void setAlarmMode(AlarmMode mode); /** Returns the number of impolite retries. */ virtual unsigned impoliteRetries() const; /** Sets the number of impolite retries. */ virtual void setImpoliteRetries(unsigned num); /** Returns the number of polite retries. */ virtual unsigned politeRetries() const; /** Sets the number of polite retries. */ virtual void setPoliteRetries(unsigned num); /** Returns the hot MIC duration in seconds. */ virtual unsigned hotMICDuration() const; /** Sets the hot MIC duration in seconds. */ virtual void setHotMICDuration(unsigned sec); /** Returns @c true if the revert channel is the selected one. */ virtual bool revertChannelIsSelected() const; /** Returns the index of the revert channel. */ virtual uint16_t revertChannelIndex() const; /** Sets the revert channel index. */ virtual void setRevertChannelIndex(uint16_t idx); /** Sets revert channel to selected channel. */ virtual void revertChannelSelected(); }; /** Represents all encryption keys and settings within the codeplug on the device. * * Memory representation of encryption settings: * @verbinclude tyt_privacy.txt */ class EncryptionElement: public Codeplug::Element { protected: /** Hidden constructor. */ EncryptionElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit EncryptionElement(uint8_t *ptr); /** Destructor. */ virtual ~EncryptionElement(); void clear(); /** Returns the size of the element. */ static constexpr unsigned int size() { return 0x00b0; } /** Returns @c true if the n-th "enhanced" key (128bit) is set. * That is, if it is not filled with 0xff. */ virtual bool isEnhancedKeySet(unsigned n) const; /** Returns the n-th "enhanced" key (128bit). */ virtual QByteArray enhancedKey(unsigned n) const; /** Sets the n-th "enhanced" key (128bit). */ virtual void setEnhancedKey(unsigned n, const QByteArray &key); /** Returns @c true if the n-th "basic" key (16bit) is set. * That is, if it is not filled with 0xff. */ virtual bool isBasicKeySet(unsigned n) const; /** Returns the n-th "basic" key (16bit). */ virtual QByteArray basicKey(unsigned n) const; /** Sets the n-th "basic" key (16bit). */ virtual void setBasicKey(unsigned n, const QByteArray &key); /** Encodes given commercial extension. */ virtual bool fromCommercialExt(CommercialExtension *encr, Context &ctx); /** Updates the commercial extension. */ virtual bool updateCommercialExt(Context &ctx); /** Links the given encryption extension. */ virtual bool linkCommercialExt(CommercialExtension *ext, Context &ctx); public: /** Some limits for the element. */ struct Limit { /** Specifies the maxumum number of basic (DMR) encryption keys (16bit). */ static constexpr unsigned int basicKeys() { return 16; } /** Specifies the maximum number of advanced (AES) encryption keys (128bit). */ static constexpr unsigned int advancedKeys() { return 8; } }; protected: /** Some internal offsets. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int advancedKeys() { return 0x0000; } static constexpr unsigned int betweenAdvancedKeys() { return 0x0010; } static constexpr unsigned int basicKeys() { return 0x0090; } static constexpr unsigned int betweenBasicKeys() { return 0x0002; } /// @endcond }; }; /** Basic pre-defined SMS text message. */ class MessageElement: public Element { protected: /** Hidden constructor. */ MessageElement(uint8_t *ptr, size_t size); public: /** Constructor. */ MessageElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return 0x00120; } void clear(); bool isValid() const; /** Retunrs the text of the message. */ virtual QString text() const; /** Sets the text of the message. */ virtual void setText(const QString &text); /** Encodes the given SMS template. */ virtual bool encode(SMSTemplate *sms, const ErrorStack &err=ErrorStack()); /** Decodes the given SMS template. */ virtual SMSTemplate *decode(const ErrorStack &err=ErrorStack()); public: /** Some limits. */ struct Limit { static constexpr unsigned int length() { return 144; } ///< Maximum message length. }; }; /** Bank of pre-defined SMS text messages. */ class MessageBankElement: public Element { protected: /** Hidden constructor. */ MessageBankElement(uint8_t *ptr, size_t size); public: /** Constructor. */ MessageBankElement(uint8_t *ptr); /** The size of the element. */ static constexpr unsigned int size() { return MessageElement::size()*Limit::messages(); } void clear(); /** Returns the i-th message. */ virtual MessageElement message(unsigned int i) const; /** Encodes all messages. */ virtual bool encode(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack()); /** Decodes all messages. */ virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()); public: /** Some Limits. */ struct Limit { /** The maximum number of messages in a bank. */ static constexpr unsigned int messages() { return 50; } }; }; protected: /** Empty constructor. */ explicit TyTCodeplug(QObject *parent = nullptr); public: /** Destructor. */ virtual ~TyTCodeplug(); /** Clears and resets the complete codeplug to some default values. */ virtual void clear(); bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const; /** Decodes the binary codeplug and stores its content in the given generic configuration. */ bool decode(Config *config, const ErrorStack &err=ErrorStack()); /** Encodes the given generic configuration as a binary codeplug. */ bool encode(Config *config, const Flags &flags = Flags(), const ErrorStack &err=ErrorStack()); public: /** Decodes the binary codeplug and stores its content in the given generic configuration using * the given context. */ virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack()); /** Encodes the given generic configuration as a binary codeplug using the given context. */ virtual bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); /** Clears the time-stamp in the codeplug. */ virtual void clearTimestamp() = 0; /** Sets the time-stamp. */ virtual bool encodeTimestamp() = 0; /** Clears the general settings in the codeplug. */ virtual void clearGeneralSettings() = 0; /** Updates the general settings from the given configuration. */ virtual bool encodeGeneralSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Updates the given configuration from the general settings. */ virtual bool decodeGeneralSettings(Config *config, const ErrorStack &err=ErrorStack()) = 0; /** Clears all contacts in the codeplug. */ virtual void clearContacts() = 0; /** Encodes all digital contacts in the configuration into the codeplug. */ virtual bool encodeContacts(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds a digital contact to the configuration for each one in the codeplug. */ virtual bool createContacts(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all RX group lists in the codeplug. */ virtual void clearGroupLists() = 0; /** Encodes all group lists in the configuration into the codeplug. */ virtual bool encodeGroupLists(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds a RX group list to the configuration for each one in the codeplug. */ virtual bool createGroupLists(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links all added RX group lists within the configuration. */ virtual bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all channels in the codeplug. */ virtual void clearChannels() = 0; /** Encodes all channels in the configuration into the codeplug. */ virtual bool encodeChannels(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds a channel to the configuration for each one in the codeplug. */ virtual bool createChannels(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links all added channels within the configuration. */ virtual bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all zones in the codeplug. */ virtual void clearZones() = 0; /** Encodes all zones in the configuration into the codeplug. */ virtual bool encodeZones(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds a zone to the configuration for each one in the codeplug. */ virtual bool createZones(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links all added zones within the configuration. */ virtual bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all scan lists in the codeplug. */ virtual void clearScanLists() = 0; /** Encodes all scan lists in the configuration into the codeplug. */ virtual bool encodeScanLists(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds a scan list to the configuration for each one in the codeplug. */ virtual bool createScanLists(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links all added scan lists within the configuration. */ virtual bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all positioning systems in the codeplug. */ virtual void clearPositioningSystems() = 0; /** Encodes all DMR positioning systems in the configuration into the codeplug. */ virtual bool encodePositioningSystems(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Adds a GPS positioning system to the configuration for each one in the codeplug. */ virtual bool createPositioningSystems(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Links all added positioning systems within the configuration. */ virtual bool linkPositioningSystems(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears the button settings in the codeplug. */ virtual void clearButtonSettings() = 0; /** Encodes the button settings. */ virtual bool encodeButtonSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Decodes the button settings. */ virtual bool decodeButtonSetttings(Config *config, const ErrorStack &err=ErrorStack()) = 0; /** Clears all encryption keys in the codeplug. */ virtual void clearPrivacyKeys() = 0; /** Encodes the encryption keys. */ virtual bool encodePrivacyKeys(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Decodes the encryption keys. */ virtual bool decodePrivacyKeys(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears the menu settings in the codeplug. */ virtual void clearMenuSettings() = 0; /** Clears all text messages in the codeplug. */ virtual void clearTextMessages() = 0; /** Encodes text messages. */ virtual bool encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack()) = 0; /** Decodes text messages. */ virtual bool decodeTextMessages(Context &ctx, const ErrorStack &err=ErrorStack()) = 0; /** Clears all emergency systems in the codeplug. */ virtual void clearEmergencySystems() = 0; }; #endif // TYT_CODEPLUG_HH qdmr-0.12.3/lib/tyt_extensions.cc000066400000000000000000000732361501654372000167320ustar00rootroot00000000000000#include "tyt_extensions.hh" #include "logger.hh" /* ******************************************************************************************** * * Implementation of TyTChannelExtension * ******************************************************************************************** */ TyTChannelExtension::TyTChannelExtension(QObject *parent) : ConfigExtension(parent), _loneWorker(false), _autoScan(false), _talkaround(false), _dataCallConfirmed(false), _privateCallConfirmed(false), _emergencyAlarmConfirmed(false), _displayPTTId(true), _rxRefFrequency(RefFrequency::Low), _txRefFrequency(RefFrequency::Low), _tightSquelch(false), _compressedUDPHeader(false), _reverseBurst(true), _killTone(KillTone::Off), _inCallCriterion(InCallCriterion::Always), _allowInterrupt(false), _dcdm(false), _dcdmLeader(false), _dmrSquelch(1) { // pass... } ConfigItem * TyTChannelExtension::clone() const { TyTChannelExtension *ex = new TyTChannelExtension(); if (! ex->copy(*this)) { ex->deleteLater(); return nullptr; } return ex; } bool TyTChannelExtension::loneWorker() const { return _loneWorker; } void TyTChannelExtension::enableLoneWorker(bool enable) { if (_loneWorker == enable) return; _loneWorker = enable; emit modified(this); } bool TyTChannelExtension::autoScan() const { return _autoScan; } void TyTChannelExtension::enableAutoScan(bool enable) { if (_autoScan == enable) return; _autoScan = enable; emit modified(this); } bool TyTChannelExtension::talkaround() const { return _talkaround; } void TyTChannelExtension::enableTalkaround(bool enable) { if (_talkaround == enable) return; _talkaround = enable; emit modified(this); } bool TyTChannelExtension::dataCallConfirmed() const { return _dataCallConfirmed; } void TyTChannelExtension::enableDataCallConfirmed(bool enable) { if (_dataCallConfirmed == enable) return; _dataCallConfirmed = enable; emit modified(this); } bool TyTChannelExtension::privateCallConfirmed() const { return _privateCallConfirmed; } void TyTChannelExtension::enablePrivateCallConfirmed(bool enable) { if (_privateCallConfirmed == enable) return; _privateCallConfirmed = enable; emit modified(this); } bool TyTChannelExtension::emergencyAlarmConfirmed() const { return _emergencyAlarmConfirmed; } void TyTChannelExtension::enableEmergencyAlarmConfirmed(bool enable) { if (_emergencyAlarmConfirmed == enable) return; _emergencyAlarmConfirmed = enable; emit modified(this); } bool TyTChannelExtension::displayPTTId() const { return _displayPTTId; } void TyTChannelExtension::enableDisplayPTTId(bool enable) { if (_displayPTTId == enable) return; _displayPTTId = enable; emit modified(this); } TyTChannelExtension::RefFrequency TyTChannelExtension::rxRefFrequency() const { return _rxRefFrequency; } void TyTChannelExtension::setRXRefFrequency(RefFrequency ref) { if (_rxRefFrequency == ref) return; _rxRefFrequency = ref; emit modified(this); } TyTChannelExtension::RefFrequency TyTChannelExtension::txRefFrequency() const { return _txRefFrequency; } void TyTChannelExtension::setTXRefFrequency(RefFrequency ref) { if (_txRefFrequency == ref) return; _txRefFrequency = ref; emit modified(this); } unsigned int TyTChannelExtension::dmrSquelch() const { return _dmrSquelch; } void TyTChannelExtension::setDMRSquelch(unsigned int sq) { if (_dmrSquelch == sq) return; _dmrSquelch = sq; emit modified(this); } bool TyTChannelExtension::tightSquelch() const { return _tightSquelch; } void TyTChannelExtension::enableTightSquelch(bool enable) { if (_tightSquelch == enable) return; _tightSquelch = enable; emit modified(this); } bool TyTChannelExtension::compressedUDPHeader() const { return _compressedUDPHeader; } void TyTChannelExtension::enableCompressedUDPHeader(bool enable) { if (_compressedUDPHeader == enable) return; _compressedUDPHeader = enable; emit modified(this); } bool TyTChannelExtension::reverseBurst() const { return _reverseBurst; } void TyTChannelExtension::enableReverseBurst(bool enable) { if (_reverseBurst == enable) return; _reverseBurst = enable; emit modified(this); } TyTChannelExtension::KillTone TyTChannelExtension::killTone() const { return _killTone; } void TyTChannelExtension::setKillTone(KillTone tone) { if (_killTone == tone) return; _killTone = tone; emit modified(this); } TyTChannelExtension::InCallCriterion TyTChannelExtension::inCallCriterion() const { return _inCallCriterion; } void TyTChannelExtension::setInCallCriterion(InCallCriterion crit) { if (_inCallCriterion == crit) return; _inCallCriterion = crit; emit modified(this); } bool TyTChannelExtension::allowInterrupt() const { return _allowInterrupt; } void TyTChannelExtension::enableAllowInterrupt(bool enable) { if (_allowInterrupt == enable) return; _allowInterrupt = enable; emit modified(this); } bool TyTChannelExtension::dcdm() const { return _dcdm; } void TyTChannelExtension::enableDCDM(bool enable) { if (_dcdm == enable) return; _dcdm = enable; emit modified(this); } bool TyTChannelExtension::dcdmLeader() const { return _dcdmLeader; } void TyTChannelExtension::enableDCDMLeader(bool enable) { if (_dcdmLeader == enable) return; _dcdmLeader = enable; emit modified(this); } /*ConfigItem * TyTChannelExtension::allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err) { Q_UNUSED(prop); Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err) // There are no further extension/children to TyTChannelExtension. return nullptr; }*/ /* ******************************************************************************************** * * Implementation of TyTScanListExtension * ******************************************************************************************** */ TyTScanListExtension::TyTScanListExtension(QObject *parent) : ConfigExtension(parent), _holdTime(500), _prioritySampleTime(2000) { // pass... } ConfigItem * TyTScanListExtension::clone() const { TyTScanListExtension *ex = new TyTScanListExtension(); if (! ex->copy(*this)) { ex->deleteLater(); return nullptr; } return ex; } unsigned TyTScanListExtension::holdTime() const { return _holdTime; } void TyTScanListExtension::setHoldTime(unsigned ms) { if (_holdTime == ms) return; _holdTime = ms; emit modified(this); } unsigned TyTScanListExtension::prioritySampleTime() const { return _prioritySampleTime; } void TyTScanListExtension::setPrioritySampleTime(unsigned ms) { if (_prioritySampleTime == ms) return; _prioritySampleTime = ms; emit modified(this); } /*ConfigItem * TyTScanListExtension::allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err) { Q_UNUSED(prop); Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err) // There are no further extension/children to TyTScanListExtension. return nullptr; }*/ /* ******************************************************************************************** * * Implementation of TyTButtonSettings * ******************************************************************************************** */ TyTButtonSettings::TyTButtonSettings(QObject *parent) : ConfigExtension(parent) { _sideButton1Short = Disabled; _sideButton1Long = Tone1750Hz; _sideButton2Short = MonitorToggle; _sideButton2Long = Disabled; _sideButton3Short = Disabled; _sideButton3Long = Disabled; _progButton1Short = Disabled; _progButton1Long = Disabled; _progButton2Short = Disabled; _progButton2Long = Disabled; _longPressDuration = 1000; } ConfigItem * TyTButtonSettings::clone() const { TyTButtonSettings *ex = new TyTButtonSettings(); if (! ex->copy(*this)) { ex->deleteLater(); return nullptr; } return ex; } TyTButtonSettings::ButtonAction TyTButtonSettings::sideButton1Short() const { return _sideButton1Short; } void TyTButtonSettings::setSideButton1Short(ButtonAction action) { if (_sideButton1Short == action) return; _sideButton1Short = action; emit modified(this); } TyTButtonSettings::ButtonAction TyTButtonSettings::sideButton1Long() const { return _sideButton1Long; } void TyTButtonSettings::setSideButton1Long(ButtonAction action) { if (_sideButton1Long == action) return; _sideButton1Long = action; emit modified(this); } TyTButtonSettings::ButtonAction TyTButtonSettings::sideButton2Short() const { return _sideButton2Short; } void TyTButtonSettings::setSideButton2Short(ButtonAction action) { if (_sideButton2Short == action) return; _sideButton2Short = action; emit modified(this); } TyTButtonSettings::ButtonAction TyTButtonSettings::sideButton2Long() const { return _sideButton2Long; } void TyTButtonSettings::setSideButton2Long(ButtonAction action) { if (_sideButton2Long == action) return; _sideButton2Long = action; emit modified(this); } TyTButtonSettings::ButtonAction TyTButtonSettings::sideButton3Short() const { return _sideButton3Short; } void TyTButtonSettings::setSideButton3Short(ButtonAction action) { if (_sideButton3Short == action) return; _sideButton3Short = action; emit modified(this); } TyTButtonSettings::ButtonAction TyTButtonSettings::sideButton3Long() const { return _sideButton3Long; } void TyTButtonSettings::setSideButton3Long(ButtonAction action) { if (_sideButton3Long == action) return; _sideButton3Long = action; emit modified(this); } TyTButtonSettings::ButtonAction TyTButtonSettings::progButton1Short() const { return _progButton1Short; } void TyTButtonSettings::setProgButton1Short(ButtonAction action) { if (_progButton1Short == action) return; _progButton1Short = action; emit modified(this); } TyTButtonSettings::ButtonAction TyTButtonSettings::progButton1Long() const { return _progButton1Long; } void TyTButtonSettings::setProgButton1Long(ButtonAction action) { if (_progButton1Long == action) return; _progButton1Long = action; emit modified(this); } TyTButtonSettings::ButtonAction TyTButtonSettings::progButton2Short() const { return _progButton2Short; } void TyTButtonSettings::setProgButton2Short(ButtonAction action) { if (_progButton2Short == action) return; _progButton2Short = action; emit modified(this); } TyTButtonSettings::ButtonAction TyTButtonSettings::progButton2Long() const { return _progButton2Long; } void TyTButtonSettings::setProgButton2Long(ButtonAction action) { if (_progButton2Long == action) return; _progButton2Long = action; emit modified(this); } unsigned TyTButtonSettings::longPressDuration() const { return _longPressDuration; } void TyTButtonSettings::setLongPressDuration(unsigned dur) { _longPressDuration = dur; } /* ******************************************************************************************** * * Implementation of TyTMenuSettings * ******************************************************************************************** */ TyTMenuSettings::TyTMenuSettings(QObject *parent) : ConfigExtension(parent), _inifiniteHangTime(false), _hangTime(10), _textMessage(true), _callAlert(true), _contactEditing(true), _manualDial(true), _remoteRadioCheck(true), _remoteMonitor(true), _remoteRadioEnable(true), _remoteRadioDisable(true), _scan(true), _scanListEditing(true), _callLogMissed(true), _callLogAnswered(true), _callLogOutgoing(true), _talkaround(true), _alertTone(true), _power(true), _backlight(true), _bootScreen(true), _keypadLock(true), _ledIndicator(true), _squelch(true), _vox(true), _password(true), _displayMode(true), _radioProgramming(true), _gpsInformation(true) { // pass... } ConfigItem * TyTMenuSettings::clone() const { TyTMenuSettings *ex = new TyTMenuSettings(); if (! ex->copy(*this)) { ex->deleteLater(); return nullptr; } return ex; } /*ConfigItem * TyTMenuSettings::allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err) { Q_UNUSED(prop); Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err) // There are no further extension/children to TyTButtonSettings. return nullptr; }*/ bool TyTMenuSettings::hangtimeIsInfinite() const { return _inifiniteHangTime; } void TyTMenuSettings::setHangtimeInfinite(bool infinite) { if (_inifiniteHangTime == infinite) return; _inifiniteHangTime = infinite; if (_inifiniteHangTime) _hangTime = 0; emit modified(this); } unsigned TyTMenuSettings::hangTime() const { return _hangTime; } void TyTMenuSettings::setHangTime(unsigned sec) { if (_hangTime == sec) return; _hangTime = sec; _inifiniteHangTime = (0 == _hangTime); emit modified(this); } bool TyTMenuSettings::textMessage() const { return _textMessage; } void TyTMenuSettings::enableTextMessage(bool enable) { if (_textMessage == enable) return; _textMessage = enable; emit modified(this); } bool TyTMenuSettings::callAlert() const { return _callAlert; } void TyTMenuSettings::enableCallAlert(bool enable) { if (_callAlert == enable) return; _callAlert = enable; emit modified(this); } bool TyTMenuSettings::contactEditing() const { return _contactEditing; } void TyTMenuSettings::enableContactEditing(bool enable) { if (_contactEditing == enable) return; _contactEditing = enable; emit modified(this); } bool TyTMenuSettings::manualDial() const { return _manualDial; } void TyTMenuSettings::enableManualDial(bool enable) { if (_manualDial == enable) return; _manualDial = enable; emit modified(this); } bool TyTMenuSettings::remoteRadioCheck() const { return _remoteRadioCheck; } void TyTMenuSettings::enableRemoteRadioCheck(bool enable) { if (_remoteRadioCheck == enable) return; _remoteRadioCheck = enable; emit modified(this); } bool TyTMenuSettings::remoteMonitor() const { return _remoteMonitor; } void TyTMenuSettings::enableRemoteMonitor(bool enable) { if (_remoteMonitor == enable) return; _remoteMonitor = enable; emit modified(this); } bool TyTMenuSettings::remoteRadioEnable() const { return _remoteRadioEnable; } void TyTMenuSettings::enableRemoteRadioEnable(bool enable) { if (_remoteRadioEnable == enable) return; _remoteRadioEnable = enable; emit modified(this); } bool TyTMenuSettings::remoteRadioDisable() const { return _remoteRadioDisable; } void TyTMenuSettings::enableRemoteRadioDisable(bool enable) { if (_remoteRadioDisable == enable) return; _remoteRadioDisable = enable; emit modified(this); } bool TyTMenuSettings::scan() const { return _scan; } void TyTMenuSettings::enableScan(bool enable) { if (_scan == enable) return; _scan = enable; emit modified(this); } bool TyTMenuSettings::scanListEditing() const { return _scanListEditing; } void TyTMenuSettings::enableScanListEditing(bool enable) { if (_scanListEditing == enable) return; _scanListEditing = enable; emit modified(this); } bool TyTMenuSettings::callLogMissed() const { return _callLogMissed; } void TyTMenuSettings::enableCallLogMissed(bool enable) { if (_callLogMissed == enable) return; _callLogMissed = enable; emit modified(this); } bool TyTMenuSettings::callLogAnswered() const { return _callLogAnswered; } void TyTMenuSettings::enableCallLogAnswered(bool enable) { if (_callLogAnswered == enable) return; _callLogAnswered = enable; emit modified(this); } bool TyTMenuSettings::callLogOutgoing() const { return _callLogOutgoing; } void TyTMenuSettings::enableCallLogOutgoing(bool enable) { if (_callLogOutgoing == enable) return; _callLogOutgoing = enable; emit modified(this); } bool TyTMenuSettings::talkaround() const { return _talkaround; } void TyTMenuSettings::enableTalkaround(bool enable) { if (_talkaround == enable) return; _talkaround = enable; emit modified(this); } bool TyTMenuSettings::alertTone() const { return _alertTone; } void TyTMenuSettings::enableAlertTone(bool enable) { if (_alertTone == enable) return; _alertTone = enable; emit modified(this); } bool TyTMenuSettings::power() const { return _power; } void TyTMenuSettings::enablePower(bool enable) { if (_power == enable) return; _power = enable; emit modified(this); } bool TyTMenuSettings::backlight() const { return _backlight; } void TyTMenuSettings::enableBacklight(bool enable) { if (_backlight == enable) return; _backlight = enable; emit modified(this); } bool TyTMenuSettings::bootScreen() const { return _bootScreen; } void TyTMenuSettings::enableBootScreen(bool enable) { if (_bootScreen == enable) return; _bootScreen = enable; emit modified(this); } bool TyTMenuSettings::keypadLock() const { return _keypadLock; } void TyTMenuSettings::enableKeypadLock(bool enable) { if (_keypadLock == enable) return; _keypadLock = enable; emit modified(this); } bool TyTMenuSettings::ledIndicator() const { return _ledIndicator; } void TyTMenuSettings::enableLEDIndicator(bool enable) { if (_ledIndicator == enable) return; _ledIndicator = enable; emit modified(this); } bool TyTMenuSettings::squelch() const { return _squelch; } void TyTMenuSettings::enableSquelch(bool enable) { if (_squelch == enable) return; _squelch = enable; emit modified(this); } bool TyTMenuSettings::vox() const { return _vox; } void TyTMenuSettings::enableVOX(bool enable) { if (_vox == enable) return; _vox = enable; emit modified(this); } bool TyTMenuSettings::password() const { return _password; } void TyTMenuSettings::enablePassword(bool enable) { if (_password == enable) return; _password = enable; emit modified(this); } bool TyTMenuSettings::displayMode() const { return _displayMode; } void TyTMenuSettings::enableDisplayMode(bool enable) { if (_displayMode == enable) return; _displayMode = enable; emit modified(this); } bool TyTMenuSettings::radioProgramming() const { return _radioProgramming; } void TyTMenuSettings::enableRadioProgramming(bool enable) { if (_radioProgramming == enable) return; _radioProgramming = enable; emit modified(this); } bool TyTMenuSettings::gpsInformation() const { return _gpsInformation; } void TyTMenuSettings::enableGPSInformation(bool enable) { if (_gpsInformation == enable) return; _gpsInformation = enable; emit modified(this); } /* ******************************************************************************************** * * Implementation of TyTSettingsExtension * ******************************************************************************************** */ TyTSettingsExtension::TyTSettingsExtension(QObject *parent) : ConfigExtension(parent), _monitorType(MonitorType::Open), _allLEDsDisabled(false), _talkPermitToneDigital(false), _talkPermitToneAnalog(false), _passwdAndLock(false), _channelFreeIndicationTone(true), _allTonesDisabled(false), _powerSaveMode(true), _wakeupPreamble(true), _bootPicture(true), _channelModeA(true), _channelModeB(true), _channelMode(true), _txPreambleDuration(600), _groupCallHangTime(3000), _privateCallHangTime(3000), _lowBatteryWarnInterval(120), _callAlertToneContinuous(false), _callAlertToneDuration(0), _loneWorkerResponseTime(1), _loneWorkerReminderTime(10), _digitalScanHangTime(1000), _analogScanHangTime(1000), _backlightAlwaysOn(false), _backlightDuration(10), _keypadLockManual(true), _keypadLockTime(5*0xff), _powerOnPasswordEnabled(false), _powerOnPassword(0), _radioProgPasswordEnabled(false), _radioProgPassword(0), _pcProgPassword(""), _privateCallMatch(true), _groupCallMatch(true), _channelHangTime(3000) { // pass... } ConfigItem * TyTSettingsExtension::clone() const { TyTSettingsExtension *ex = new TyTSettingsExtension(); if (! ex->copy(*this)) { ex->deleteLater(); return nullptr; } return ex; } TyTSettingsExtension::MonitorType TyTSettingsExtension::monitorType() const { return _monitorType; } void TyTSettingsExtension::setMonitorType(MonitorType type) { if (_monitorType == type) return; _monitorType = type; emit modified(this); } bool TyTSettingsExtension::allLEDsDisabled() const { return _allLEDsDisabled; } void TyTSettingsExtension::disableAllLEDs(bool disable) { if (_allLEDsDisabled == disable) return; _allLEDsDisabled = disable; emit modified(this); } bool TyTSettingsExtension::talkPermitToneDigital() const { return _talkPermitToneDigital; } void TyTSettingsExtension::enableTalkPermitToneDigital(bool enable) { if (_talkPermitToneDigital == enable) return; _talkPermitToneDigital = enable; emit modified(this); } bool TyTSettingsExtension::talkPermitToneAnalog() const { return _talkPermitToneAnalog; } void TyTSettingsExtension::enableTalkPermitToneAnalog(bool enable) { if (_talkPermitToneAnalog == enable) return; _talkPermitToneAnalog = enable; emit modified(this); } bool TyTSettingsExtension::passwordAndLock() const { return _passwdAndLock; } void TyTSettingsExtension::enablePasswordAndLock(bool enable) { if (_passwdAndLock == enable) return; _passwdAndLock = enable; emit modified(this); } bool TyTSettingsExtension::channelFreeIndicationTone() const { return _channelFreeIndicationTone; } void TyTSettingsExtension::enableChannelFreeIndicationTone(bool enable) { if (_channelFreeIndicationTone == enable) return; _channelFreeIndicationTone = enable; emit modified(this); } bool TyTSettingsExtension::allTonesDisabled() const { return _allTonesDisabled; } void TyTSettingsExtension::disableAllTones(bool disable) { if (_allTonesDisabled == disable) return; _allTonesDisabled = disable; emit modified(this); } bool TyTSettingsExtension::powerSaveMode() const { return _powerSaveMode; } void TyTSettingsExtension::enablePowerSaveMode(bool enable) { if (_powerSaveMode == enable) return; _powerSaveMode = enable; emit modified(this); } bool TyTSettingsExtension::wakeupPreamble() const { return _wakeupPreamble; } void TyTSettingsExtension::enableWakeupPreamble(bool enable) { if (_wakeupPreamble == enable) return; _wakeupPreamble = enable; emit modified(this); } bool TyTSettingsExtension::bootPicture() const { return _bootPicture; } void TyTSettingsExtension::enableBootPicture(bool enable) { if (_bootPicture == enable) return; _bootPicture = enable; emit modified(this); } bool TyTSettingsExtension::channelMode() const { return _channelMode; } void TyTSettingsExtension::enableChannelMode(bool enable) { if (_channelMode == enable) return; _channelMode = enable; emit modified(this); } bool TyTSettingsExtension::channelModeA() const { return _channelModeA; } void TyTSettingsExtension::enableChannelModeA(bool enable) { if (_channelModeA == enable) return; _channelModeA = enable; emit modified(this); } bool TyTSettingsExtension::channelModeB() const { return _channelModeB; } void TyTSettingsExtension::enableChannelModeB(bool enable) { if (_channelModeB == enable) return; _channelModeB = enable; emit modified(this); } unsigned TyTSettingsExtension::txPreambleDuration() const { return _txPreambleDuration; } void TyTSettingsExtension::setTXPreambleDuration(unsigned ms) { if (_txPreambleDuration == ms) return; _txPreambleDuration = ms; emit modified(this); } unsigned TyTSettingsExtension::groupCallHangTime() const { return _groupCallHangTime; } void TyTSettingsExtension::setGroupCallHangTime(unsigned ms) { if (_groupCallHangTime == ms) return; _groupCallHangTime = ms; emit modified(this); } unsigned TyTSettingsExtension::privateCallHangTime() const { return _privateCallHangTime; } void TyTSettingsExtension::setPrivateCallHangTime(unsigned ms) { if (_privateCallHangTime == ms) return; _privateCallHangTime = ms; emit modified(this); } unsigned TyTSettingsExtension::lowBatteryWarnInterval() const { return _lowBatteryWarnInterval; } void TyTSettingsExtension::setLowBatteryWarnInterval(unsigned sec) { if (_lowBatteryWarnInterval == sec) return; _lowBatteryWarnInterval = sec; emit modified(this); } bool TyTSettingsExtension::callAlertToneContinuous() const { return _callAlertToneContinuous; } void TyTSettingsExtension::enableCallAlertToneContinuous(bool enable) { if (_callAlertToneContinuous == enable) return; _callAlertToneContinuous = enable; emit modified(this); } unsigned TyTSettingsExtension::callAlertToneDuration() const { return _callAlertToneDuration; } void TyTSettingsExtension::setCallAlertToneDuration(unsigned sec) { if (_callAlertToneDuration == sec) return; _callAlertToneDuration = sec; emit modified(this); } unsigned TyTSettingsExtension::loneWorkerResponseTime() const { return _loneWorkerResponseTime; } void TyTSettingsExtension::setLoneWorkerResponseTime(unsigned min) { if (_loneWorkerResponseTime == min) return; _loneWorkerResponseTime = min; emit modified(this); } unsigned TyTSettingsExtension::loneWorkerReminderTime() const { return _loneWorkerReminderTime; } void TyTSettingsExtension::setLoneWorkerReminderTime(unsigned sec) { if (_loneWorkerReminderTime == sec) return; _loneWorkerReminderTime = sec; emit modified(this); } unsigned TyTSettingsExtension::digitalScanHangTime() const { return _digitalScanHangTime; } void TyTSettingsExtension::setDigitalScanHangTime(unsigned ms) { if (_digitalScanHangTime == ms) return; _digitalScanHangTime = ms; emit modified(this); } unsigned TyTSettingsExtension::analogScanHangTime() const { return _analogScanHangTime; } void TyTSettingsExtension::setAnalogScanHangTime(unsigned ms) { if (_analogScanHangTime == ms) return; _analogScanHangTime = ms; emit modified(this); } bool TyTSettingsExtension::backlightAlwaysOn() const { return _backlightAlwaysOn; } void TyTSettingsExtension::enableBacklightAlwaysOn(bool enable) { if (_backlightAlwaysOn == enable) return; _backlightAlwaysOn = enable; emit modified(this); } unsigned TyTSettingsExtension::backlightDuration() const { return _backlightDuration; } void TyTSettingsExtension::setBacklightDuration(unsigned sec) { if (_backlightDuration == sec) return; _backlightDuration = sec; emit modified(this); } bool TyTSettingsExtension::keypadLockManual() const { return _keypadLockManual; } void TyTSettingsExtension::enableKeypadLockManual(bool enable) { if (_keypadLockManual == enable) return; _keypadLockManual = enable; emit modified(this); } unsigned TyTSettingsExtension::keypadLockTime() const { return _keypadLockTime; } void TyTSettingsExtension::setKeypadLockTime(unsigned sec) { if (_keypadLockTime == sec) return; _keypadLockTime = sec; emit modified(this); } bool TyTSettingsExtension::powerOnPasswordEnabled() const { return _powerOnPasswordEnabled; } void TyTSettingsExtension::enablePowerOnPassword(bool enable) { if (_powerOnPasswordEnabled == enable) return; _powerOnPasswordEnabled = enable; emit modified(this); } unsigned TyTSettingsExtension::powerOnPassword() const { return _powerOnPassword; } void TyTSettingsExtension::setPowerOnPassword(unsigned passwd) { if (_powerOnPassword == passwd) return; _powerOnPassword = passwd; emit modified(this); } bool TyTSettingsExtension::radioProgPasswordEnabled() const { return _radioProgPassword; } void TyTSettingsExtension::enableRadioProgPassword(bool enable) { if (_radioProgPasswordEnabled == enable) return; _radioProgPasswordEnabled = enable; emit modified(this); } unsigned TyTSettingsExtension::radioProgPassword() const { return _radioProgPassword; } void TyTSettingsExtension::setRadioProgPassword(unsigned passwd) { if (_radioProgPassword == passwd) return; _radioProgPassword = passwd; emit modified(this); } const QString & TyTSettingsExtension::pcProgPassword() const { return _pcProgPassword; } void TyTSettingsExtension::setPCProgPassword(const QString &passwd) { if (_pcProgPassword == passwd) return; _pcProgPassword = passwd; emit modified(this); } bool TyTSettingsExtension::privateCallMatch() const { return _privateCallMatch; } void TyTSettingsExtension::enablePrivateCallMatch(bool enable) { if (_privateCallMatch == enable) return; _privateCallMatch = enable; emit modified(this); } bool TyTSettingsExtension::groupCallMatch() const { return _groupCallMatch; } void TyTSettingsExtension::enableGroupCallMatch(bool enable) { if (_groupCallMatch == enable) return; _groupCallMatch = enable; emit modified(this); } unsigned TyTSettingsExtension::channelHangTime() const { return _channelHangTime; } void TyTSettingsExtension::setChannelHangTime(unsigned ms) { if (_channelHangTime == ms) return; _channelHangTime = ms; emit modified(this); } /*ConfigItem * TyTSettingsExtension::allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err) { Q_UNUSED(prop); Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err) // No extensions to this extension. return nullptr; }*/ /* ******************************************************************************************** * * Implementation of TyTButtonSettings * ******************************************************************************************** */ TyTConfigExtension::TyTConfigExtension(QObject *parent) : ConfigExtension(parent), _buttonSettings(new TyTButtonSettings(this)), _menuSettings(new TyTMenuSettings(this)) { // Pass... } ConfigItem * TyTConfigExtension::clone() const { TyTConfigExtension *ex = new TyTConfigExtension(); if (! ex->copy(*this)) { ex->deleteLater(); return nullptr; } return ex; } TyTButtonSettings * TyTConfigExtension::buttonSettings() const { return _buttonSettings; } TyTMenuSettings * TyTConfigExtension::menuSettings() const { return _menuSettings; } /*ConfigItem * TyTConfigExtension::allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err) { Q_UNUSED(prop); Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err) // All extensions are pre-allocated. So nothing to do here. return nullptr; }*/ qdmr-0.12.3/lib/tyt_extensions.hh000066400000000000000000001325411501654372000167370ustar00rootroot00000000000000#ifndef TYTEXTENSION_HH #define TYTEXTENSION_HH #include "configobject.hh" /** Represents the TyT channel extension. * * That is, all device specific settings for TyT devices, that are not represented though the common * codeplug. * * @ingroup tyt */ class TyTChannelExtension: public ConfigExtension { Q_OBJECT Q_CLASSINFO("description", "Settings for MD-390, RT8, MD-UV390, RT3S, MD-2017, RT82, DM-1701, RT84.") Q_CLASSINFO("longDescription", "Device specific channel settings for TyT and Retevis devices." "Including TyT MD-390, MD-UV390, MD-2017, Retevis RT8, RT3S and RT82" " as well as Baofeng DM-1701.") /** The lone worker feature. */ Q_PROPERTY(bool loneWorker READ loneWorker WRITE enableLoneWorker) /** The auto scan feature. */ Q_PROPERTY(bool autoScan READ autoScan WRITE enableAutoScan) /** The talk around feature. */ Q_PROPERTY(bool talkaround READ talkaround WRITE enableTalkaround) /** If @c true, data call confirmation is enabled. */ Q_PROPERTY(bool dataCallConfirmed READ dataCallConfirmed WRITE enableDataCallConfirmed) /** If @c true, private call confirmation is enabled. */ Q_PROPERTY(bool privateCallConfirmed READ privateCallConfirmed WRITE enablePrivateCallConfirmed) /** If @c true, emergency calls are confirmed. */ Q_PROPERTY(bool emergencyAlarmConfirmed READ emergencyAlarmConfirmed WRITE enableEmergencyAlarmConfirmed) /** If @c true, displays analog PTT IDs. */ Q_PROPERTY(bool displayPTTId READ displayPTTId WRITE enableDisplayPTTId) /** Holds the reference frequency setting for RX. */ Q_PROPERTY(RefFrequency rxRefFrequency READ rxRefFrequency WRITE setRXRefFrequency) /** Holds the reference frequency setting for TX. */ Q_PROPERTY(RefFrequency txRefFrequency READ txRefFrequency WRITE setTXRefFrequency) /** The tight-squelch feature. */ Q_PROPERTY(bool tightSquelch READ tightSquelch WRITE enableTightSquelch) /** The compressed UDP header feature. */ Q_PROPERTY(bool compressedUDPHeader READ compressedUDPHeader WRITE enableCompressedUDPHeader) /** The reverse-burst feature. */ Q_PROPERTY(bool reverseBurst READ reverseBurst WRITE enableReverseBurst) /** Holds the kill tone frequency. */ Q_PROPERTY(KillTone killTone READ killTone WRITE setKillTone) /** Holds the in-call criterion. */ Q_PROPERTY(InCallCriterion inCallCriterion READ inCallCriterion WRITE setInCallCriterion) /** Holds the allow-interrupt flag. */ Q_PROPERTY(bool allowInterrupt READ allowInterrupt WRITE enableAllowInterrupt) /** If @c true, enables Dual-Capacity Direct Mode (DCDM, i.e., time-slots for simplex). */ Q_PROPERTY(bool dcdm READ dcdm WRITE enableDCDM) /** If @c true, and dcdm is enabled, this radio is the leader, specifying the clock. */ Q_PROPERTY(bool dcdmLeader READ dcdmLeader WRITE enableDCDMLeader) /** The squelch level for DMR channels. */ Q_PROPERTY(unsigned int dmrSquelch READ dmrSquelch WRITE setDMRSquelch) Q_CLASSINFO("dmrSquelchDescription", "Sets the squelch level for DMR channels. " "Only applicable for MD-UV390 and MD-2017") public: /** Possible reference frequency settings for RX & TX. */ enum class RefFrequency { Low=0, Medium=1, High=2 }; Q_ENUM(RefFrequency) /** Possible kill-tone settings. */ enum class KillTone { Tone259_2Hz=0, Tone55_2Hz=1, Off=3 }; Q_ENUM(KillTone) /** Possible in-call criterions. */ enum class InCallCriterion { Always = 0, AdmitCriterion=1, TXInterrupt=2 }; Q_ENUM(InCallCriterion) public: /** Default constructor. */ Q_INVOKABLE explicit TyTChannelExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns @c true if the lone worker feature is enabled. */ bool loneWorker() const; /** Enables/disables the lone-worker feature. */ void enableLoneWorker(bool enable); /** Returns @c true if the auto scan feature is enabled. */ bool autoScan() const; /** Enables/disables the auto-scan feature. */ void enableAutoScan(bool enable); /** Returns @c true if the talk around feature is enabled. */ bool talkaround() const; /** Enables/disables the talk-around feature. */ void enableTalkaround(bool enable); /** Returns @c true if data call confirmation is enabled. */ bool dataCallConfirmed() const; /** Enables/disables data-call confirmation. */ void enableDataCallConfirmed(bool enable); /** Returns @c true if private call confirmation is enabled. */ bool privateCallConfirmed() const; /** Enables/disables private-call confirmation. */ void enablePrivateCallConfirmed(bool enable); /** Returns @c true if emergency calls are confirmed. */ bool emergencyAlarmConfirmed() const; /** Enables/disables emergency-call confirmation. */ void enableEmergencyAlarmConfirmed(bool enable); /** Returns @c true if analog PTT IDs are shown. */ bool displayPTTId() const; /** Enables/disables analog PTT ID display. */ void enableDisplayPTTId(bool enable); /** Returns the reference frequency setting for RX. */ RefFrequency rxRefFrequency() const; /** Sets the reference frequency setting for RX. */ void setRXRefFrequency(RefFrequency ref); /** Returns the reference frequency setting for TX. */ RefFrequency txRefFrequency() const; /** Sets the reference frequency setting for TX. */ void setTXRefFrequency(RefFrequency ref); /** Returns @c true if the tight squelch is enabled. */ bool tightSquelch() const; /** Enables/disables the tight squelch. */ void enableTightSquelch(bool enable); /** Returns @c true if the compressed UDP header is enabled. */ bool compressedUDPHeader() const; /** Enables/disables the compressed UDP header. */ void enableCompressedUDPHeader(bool enable); /** Returns @c true if the reverse burst is enabled. */ bool reverseBurst() const; /** Enables/disables reverse burst. */ void enableReverseBurst(bool enable); /** Returns the kill tone frequency. */ KillTone killTone() const; /** Sets the kill-tone frequency. */ void setKillTone(KillTone tone); /** Returns the in-call criterion. */ InCallCriterion inCallCriterion() const; /** Sets the in-call criterion. */ void setInCallCriterion(InCallCriterion crit); /** Returns @c true if interrupt is allowed. */ bool allowInterrupt() const; /** Enables/disables interrupt. */ void enableAllowInterrupt(bool enable); /** Returns @c true if the DCDM mode is enabled. */ bool dcdm() const; /** Enables/disables the DCDM mode. */ void enableDCDM(bool enable); /** Returns @c true if this radio is the leader for a DCDM simplex channel. */ bool dcdmLeader() const; /** Enables/disables this radio to be the leader on a DCDM simplex channel. */ void enableDCDMLeader(bool enable); /** Squelch level for DMR channels. */ unsigned int dmrSquelch() const; /** Sets the squelch-level for DMR channels. */ void setDMRSquelch(unsigned int sq); public: /*ConfigItem *allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack());*/ protected: // Common properties /** Holds the lone-worker flag. */ bool _loneWorker; /** Holds the auto-scan flag. */ bool _autoScan; /** Holds the talk around flag. */ bool _talkaround; /** Holds the data-call confirmation flag. */ bool _dataCallConfirmed; /** Holds the private-call confirmation flag. */ bool _privateCallConfirmed; /** Holds the emergency-call confirmation flag. */ bool _emergencyAlarmConfirmed; /** Holds the display PTT ID flag. */ bool _displayPTTId; /** Holds the reference frequency setting for RX. */ RefFrequency _rxRefFrequency; /** Holds the reference frequency setting for TX. */ RefFrequency _txRefFrequency; // MD-390 properties /** Holds the tightSquelch flag. */ bool _tightSquelch; /** Holds the compressed UDP header flag. */ bool _compressedUDPHeader; /** Holds the reverse burst flag. */ bool _reverseBurst; // MD-UV390, MD-2017 properties /** Holds the kill tone setting. */ KillTone _killTone; /** Holds the in-call criterion. */ InCallCriterion _inCallCriterion; /** Holds the interrupt flag. */ bool _allowInterrupt; /** Holds the DCDM flag. */ bool _dcdm; /** Holds the DCDM-leader flag. */ bool _dcdmLeader; /** The squelch level [0-10] for DMR channels. */ unsigned int _dmrSquelch; }; /** Represents device specific scan-list settings for TyT devices. * @ingroup tyt */ class TyTScanListExtension: public ConfigExtension { Q_OBJECT /** Holds the hold time in ms. */ Q_PROPERTY(unsigned holdTime READ holdTime WRITE setHoldTime) /** Holds the sample time in ms for priority channels. */ Q_PROPERTY(unsigned prioritySampleTime READ prioritySampleTime WRITE setPrioritySampleTime) public: /** Default constructor. */ Q_INVOKABLE explicit TyTScanListExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the hold time in ms. */ unsigned holdTime() const; /** Sets the hold time im ms. */ void setHoldTime(unsigned ms); /** Returns the sample time for priority channels in ms. */ unsigned prioritySampleTime() const; /** Sets the sample time for priority channels in ms. */ void setPrioritySampleTime(unsigned ms); public: /*ConfigItem *allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack());*/ protected: /** The hold time in ms. */ unsigned _holdTime; /** The sample time for priority channels in ms. */ unsigned _prioritySampleTime; }; /** Represents the TyT button settings extension. * @ingroup tyt */ class TyTButtonSettings : public ConfigExtension { Q_OBJECT /** The action to perform on a short press on side button 1. */ Q_PROPERTY(ButtonAction sideButton1Short READ sideButton1Short WRITE setSideButton1Short) /** The action to perform on a long press on side button 1. */ Q_PROPERTY(ButtonAction sideButton1Long READ sideButton1Long WRITE setSideButton1Long) /** The action to perform on a short press on side button 2. */ Q_PROPERTY(ButtonAction sideButton2Short READ sideButton2Short WRITE setSideButton2Short) /** The action to perform on a long press on side button 2. */ Q_PROPERTY(ButtonAction sideButton2Long READ sideButton2Long WRITE setSideButton2Long) /** The action to perform on a short press on side button 3. */ Q_PROPERTY(ButtonAction sideButton3Short READ sideButton3Short WRITE setSideButton3Short) /** The action to perform on a long press on side button 3. */ Q_PROPERTY(ButtonAction sideButton3Long READ sideButton3Long WRITE setSideButton3Long) /** The action to perform on a short press on programmable button 1. */ Q_PROPERTY(ButtonAction progButton1Short READ progButton1Short WRITE setProgButton1Short) /** The action to perform on a long press on programmable button 1. */ Q_PROPERTY(ButtonAction progButton1Long READ progButton1Long WRITE setProgButton1Long) /** The action to perform on a short press on programmable button 2. */ Q_PROPERTY(ButtonAction progButton2Short READ progButton2Short WRITE setProgButton2Short) /** The action to perform on a long press on programmable button 2. */ Q_PROPERTY(ButtonAction progButton2Long READ progButton2Long WRITE setProgButton2Long) /** The duration of a long press in msec. */ Q_PROPERTY(unsigned longPressDuration READ longPressDuration WRITE setLongPressDuration) public: /** Possible actions for the side-buttons. */ enum ButtonAction { Disabled = 0, ///< Disabled side-button action. ToggleAllAlertTones = 1, ///< Toggle all alert tones. EmergencyOn = 2, ///< Enable emergency. EmergencyOff = 3, ///< Disable emergency. PowerSelect = 4, ///< Select TX power. MonitorToggle = 5, ///< Toggle monitor (promiscuous mode on digital channel, open squelch on analog channel). NuisanceDelete = 6, ///< Nuisance delete. OneTouch1 = 7, ///< Perform one-touch action 1. OneTouch2 = 8, ///< Perform one-touch action 2. OneTouch3 = 9, ///< Perform one-touch action 3. OneTouch4 = 10, ///< Perform one-touch action 4. OneTouch5 = 11, ///< Perform one-touch action 5. OneTouch6 = 12, ///< Perform one-touch action 6. RepeaterTalkaroundToggle = 13, ///< Toggle repater mode / talkaround. ScanToggle = 14, ///< Start/stop scan. SquelchToggle = 21, ///< Enable/disable squelch. PrivacyToggle = 22, ///< Enable/disable privacy system. VoxToggle = 23, ///< Enable/disable VOX. ZoneIncrement = 24, ///< Switch to next zone. BatteryIndicator = 26, ///< Show battery charge. ManualDialForPrivate = 30, ///< Manual dial for private. LoneWorkerToggle = 31, ///< Toggle lone-worker. RecordToggle = 34, ///< Enable/disable recording (dep. on firmware). RecordPlayback = 35, ///< Start/stop playback. RecordDeleteAll = 36, ///< Delete all recordings. Tone1750Hz = 38, ///< Send 1750Hz tone. SwitchUpDown = 47, ///< Switch Channel A/B. RightKey = 48, ///< Who knows? LeftKey = 49, ///< Who knows? ZoneDecrement = 55, ///< Switch to previous zone. SetTalkgroup = 81, ///< md380tools: set temp. TG PromiscuousToggle = 86 ///< md380tools: enable/disable promiscuous mode }; Q_ENUM(ButtonAction) public: /** Constructor. */ Q_INVOKABLE explicit TyTButtonSettings(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the action for the side button 1 short-press. */ ButtonAction sideButton1Short() const; /** Sets the action for the side button 1 short-press. */ void setSideButton1Short(ButtonAction action); /** Returns the action for the side button 1 long-press. */ ButtonAction sideButton1Long() const; /** Sets the action for the side button 1 long-press. */ void setSideButton1Long(ButtonAction action); /** Returns the action for the side button 2 short-press. */ ButtonAction sideButton2Short() const; /** Sets the action for the side button 2 short-press. */ void setSideButton2Short(ButtonAction action); /** Returns the action for the side button 2 long-press. */ ButtonAction sideButton2Long() const; /** Sets the action for the side button 2 long-press. */ void setSideButton2Long(ButtonAction action); /** Returns the action for the side button 3 short-press (Baofeng DM-1701). */ ButtonAction sideButton3Short() const; /** Sets the action for the side button 3 short-press (Baofeng DM-1701). */ void setSideButton3Short(ButtonAction action); /** Returns the action for the side button 3 long-press (Baofeng DM-1701). */ ButtonAction sideButton3Long() const; /** Sets the action for the side button 3 long-press (Baofeng DM-1701). */ void setSideButton3Long(ButtonAction action); /** Returns the action for the programmable button 1 short-press (Baofeng DM-1701). */ ButtonAction progButton1Short() const; /** Sets the action for the programmable button 1 short-press (Baofeng DM-1701). */ void setProgButton1Short(ButtonAction action); /** Returns the action for the programmable button 1 long-press (Baofeng DM-1701). */ ButtonAction progButton1Long() const; /** Sets the action for the programmable button 1 long-press (Baofeng DM-1701). */ void setProgButton1Long(ButtonAction action); /** Returns the action for the programmable button 2 short-press (Baofeng DM-1701). */ ButtonAction progButton2Short() const; /** Sets the action for the programmable button 2 short-press (Baofeng DM-1701). */ void setProgButton2Short(ButtonAction action); /** Returns the action for the programmable button 2 long-press (Baofeng DM-1701). */ ButtonAction progButton2Long() const; /** Sets the action for the programmable button 2 long-press (Baofeng DM-1701). */ void setProgButton2Long(ButtonAction action); /** Returns the long-press duration in msec. */ unsigned longPressDuration() const; /** Sets the long-press duration in msec. */ void setLongPressDuration(unsigned dur); protected: /** Holds the side button 1 short-press action. */ ButtonAction _sideButton1Short; /** Holds the side button 1 long-press action. */ ButtonAction _sideButton1Long; /** Holds the side button 2 short-press action. */ ButtonAction _sideButton2Short; /** Holds the side button 2 long-press action. */ ButtonAction _sideButton2Long; /** Holds the side button 3 short-press action. */ ButtonAction _sideButton3Short; /** Holds the side button 3 long-press action. */ ButtonAction _sideButton3Long; /** Holds the prog button 1 short-press action. */ ButtonAction _progButton1Short; /** Holds the prog button 1 long-press action. */ ButtonAction _progButton1Long; /** Holds the prog button 2 short-press action. */ ButtonAction _progButton2Short; /** Holds the prog button 2 long-press action. */ ButtonAction _progButton2Long; /** Holds the long-press duration in ms. */ unsigned _longPressDuration; }; /** Represents the TyT menu settings extension. * @ingroup tyt */ class TyTMenuSettings : public ConfigExtension { Q_OBJECT /** If @c true, the menu hang time is infinite. */ Q_PROPERTY(bool hangtimeIsInfinite READ hangtimeIsInfinite WRITE setHangtimeInfinite) /** The menu hang time in seconds. */ Q_PROPERTY(unsigned hangTime READ hangTime WRITE setHangTime) /** If @c true, the text message menu is shown. */ Q_PROPERTY(bool textMessage READ textMessage WRITE enableTextMessage) /** If @c true, the call-alert menu item is shown. */ Q_PROPERTY(bool callAlert READ callAlert WRITE enableCallAlert) /** If @c true, the contact editing menu is shown. */ Q_PROPERTY(bool contactEditing READ contactEditing WRITE enableContactEditing) /** If @c true, the manual dial menu item is shown. */ Q_PROPERTY(bool manualDial READ manualDial WRITE enableManualDial) /** If @c true, the remote radio check menu item is shown. */ Q_PROPERTY(bool remoteRadioCheck READ remoteRadioCheck WRITE enableRemoteRadioCheck) /** If @c true, the remote monitor menu item is shown. */ Q_PROPERTY(bool remoteMonitor READ remoteMonitor WRITE enableRemoteMonitor) /** If @c true, the remote radio enable menu item is shown. */ Q_PROPERTY(bool remoteRadioEnable READ remoteRadioEnable WRITE enableRemoteRadioEnable) /** If @c true, the remote radio disable menu item is shown. */ Q_PROPERTY(bool remoteRadioDisable READ remoteRadioDisable WRITE enableRemoteRadioDisable) /** If @c true, the scan menu item is shown. */ Q_PROPERTY(bool scan READ scan WRITE enableScan) /** If @c true, the scan list editing is enabled. */ Q_PROPERTY(bool scanListEditing READ scanListEditing WRITE enableScanListEditing) /** If @c true, the list of missed calls is shown. */ Q_PROPERTY(bool callLogMissed READ callLogMissed WRITE enableCallLogMissed) /** If @c true, the list of answered calls is shown. */ Q_PROPERTY(bool callLogAnswered READ callLogAnswered WRITE enableCallLogAnswered) /** If @c true, the list of outgoing calls is shown. */ Q_PROPERTY(bool callLogOutgoing READ callLogOutgoing WRITE enableCallLogOutgoing) /** If @c true, the talkaround menu item is shown. */ Q_PROPERTY(bool talkaround READ talkaround WRITE enableTalkaround) /** If @c true, the alert-tone menu item is shown. */ Q_PROPERTY(bool alertTone READ alertTone WRITE enableAlertTone) /** If @c true, the power settings menu item is shown. */ Q_PROPERTY(bool power READ power WRITE enablePower) /** If @c true, the backlight menu item is shown. */ Q_PROPERTY(bool backlight READ backlight WRITE enableBacklight) /** If @c true, the boot-screen settings menu item is shown. */ Q_PROPERTY(bool bootScreen READ bootScreen WRITE enableBootScreen) /** If @c true, the keypad-lock settings menu item is shown. */ Q_PROPERTY(bool keypadLock READ keypadLock WRITE enableKeypadLock) /** If @c true, the LED indicator settings menu item is shown. */ Q_PROPERTY(bool ledIndicator READ ledIndicator WRITE enableLEDIndicator) /** If @c true, the squelch settings menu item is shown. */ Q_PROPERTY(bool squelch READ squelch WRITE enableSquelch) /** If @c true, the VOX settings menu item is shown. */ Q_PROPERTY(bool vox READ vox WRITE enableVOX) /** If @c true, the password menu item is shown. */ Q_PROPERTY(bool password READ password WRITE enablePassword) /** If @c true, the display-mode settings menu item is shown. */ Q_PROPERTY(bool displayMode READ displayMode WRITE enableDisplayMode) /** If @c true, radio programming on the radio is enabled. */ Q_PROPERTY(bool radioProgramming READ radioProgramming WRITE enableRadioProgramming) /** If @c true, the positioning settings menu item is shown. */ Q_PROPERTY(bool gpsInformation READ gpsInformation WRITE enableGPSInformation) public: /** Constructor. */ Q_INVOKABLE explicit TyTMenuSettings(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns @c true if the hang time is infinite. */ bool hangtimeIsInfinite() const; /** Enables/disables infinite hang time. */ void setHangtimeInfinite(bool infinite); /** Returns the menu hang time in seconds. */ unsigned hangTime() const; /** Sets the menu hang time in seconds. */ void setHangTime(unsigned sec); /** Returns @c true if the text message menu item is enabled. */ bool textMessage() const; /** Enables/disables the text message menu item. */ void enableTextMessage(bool enable); /** Returns @c true if the call alert menu item is enabled. */ bool callAlert() const; /** Enables/disables the call alert menu item. */ void enableCallAlert(bool enable); /** Returns @c true if contact editing is enabled. */ bool contactEditing() const; /** Enables/disables contact editing. */ void enableContactEditing(bool enable); /** Returns @c true if the manual dial menu item is enabled. */ bool manualDial() const; /** Enables/disables the manual dial menu item. */ void enableManualDial(bool enable); /** Returns @c true if the remote radio check menu item is enabled. */ bool remoteRadioCheck() const; /** Enables/disables the remote radio check menu item. */ void enableRemoteRadioCheck(bool enable); /** Returns @c true if the remote montior menu item is enabled. */ bool remoteMonitor() const; /** Enables/disables the remote monitor menu item. */ void enableRemoteMonitor(bool enable); /** Returns @c true if the remote radio enable menu item is enabled. */ bool remoteRadioEnable() const; /** Enables/disables the remote radio enable menu item. */ void enableRemoteRadioEnable(bool enable); /** Returns @c true if the remote radio disable menu item is enabled. */ bool remoteRadioDisable() const; /** Enables/disables the remote radio disable menu item. */ void enableRemoteRadioDisable(bool enable); /** Returns @c true if the scan menu item is enabled. */ bool scan() const; /** Enables/disables the scan menu item. */ void enableScan(bool enable); /** Returns @c true if the scan list editing menu item is enabled. */ bool scanListEditing() const; /** Enables/disables the scan list editing menu item. */ void enableScanListEditing(bool enable); /** Returns @c true if the list of missed calls menu item is enabled. */ bool callLogMissed() const; /** Enables/disables the list of missed calls menu item. */ void enableCallLogMissed(bool enable); /** Returns @c true if the list of answered calls menu item is enabled. */ bool callLogAnswered() const; /** Enables/disables the list of answered calls menu item. */ void enableCallLogAnswered(bool enable); /** Returns @c true if the list of outgoing calls menu item is enabled. */ bool callLogOutgoing() const; /** Enables/disables the list of outgoing calls menu item. */ void enableCallLogOutgoing(bool enable); /** Returns @c true if the talkaround menu item is enabled. */ bool talkaround() const; /** Enables/disables the talkaround menu item. */ void enableTalkaround(bool enable); /** Returns @c true if the alert tone menu item is enabled. */ bool alertTone() const; /** Enables/disables the alert tone menu item. */ void enableAlertTone(bool enable); /** Returns @c true if the power menu item is enabled. */ bool power() const; /** Enables/disables the power menu item. */ void enablePower(bool enable); /** Returns @c true if the backlight menu item is enabled. */ bool backlight() const; /** Enables/disables the backlight menu item. */ void enableBacklight(bool enable); /** Returns @c true if the boot screen menu item is enabled. */ bool bootScreen() const; /** Enables/disables the boot screen menu item. */ void enableBootScreen(bool enable); /** Returns @c true if the keypad lock menu item is enabled. */ bool keypadLock() const; /** Enables/disables the keypad lock menu item. */ void enableKeypadLock(bool enable); /** Returns @c true if the LED indicator menu item is enabled. */ bool ledIndicator() const; /** Enables/disables the LED indicator menu item. */ void enableLEDIndicator(bool enable); /** Returns @c true if the squelch menu item is enabled. */ bool squelch() const; /** Enables/disables the squelch menu item. */ void enableSquelch(bool enable); /** Returns @c true if the VOX menu item is enabled. */ bool vox() const; /** Enables/disables the VOX menu item. */ void enableVOX(bool enable); /** Returns @c true if the password menu item is enabled. */ bool password() const; /** Enables/disables the password menu item. */ void enablePassword(bool enable); /** Returns @c true if the display mode menu item is enabled. */ bool displayMode() const; /** Enables/disables the display mode menu item. */ void enableDisplayMode(bool enable); /** Returns @c true if the radio programming menu item is enabled. */ bool radioProgramming() const; /** Enables/disables the radio programming menu item. */ void enableRadioProgramming(bool enable); /** Returns @c true if the GPS information menu item is enabled. */ bool gpsInformation() const; /** Enables/disables the GPS information menu item. */ void enableGPSInformation(bool enable); public: /*ConfigItem *allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack());*/ protected: /** If @c true, the menu hang time is infinite. */ bool _inifiniteHangTime; /** The menu hang time in seconds. */ unsigned _hangTime; /** If @c true, the text message menu is shown. */ bool _textMessage; /** If @c true, the call-alert menu item is shown. */ bool _callAlert; /** If @c true, the contact editing menu is shown. */ bool _contactEditing; /** If @c true, the manual dial menu item is shown. */ bool _manualDial; /** If @c true, the remote radio check menu item is shown. */ bool _remoteRadioCheck; /** If @c true, the remote monitor menu item is shown. */ bool _remoteMonitor; /** If @c true, the remote radio enable menu item is shown. */ bool _remoteRadioEnable; /** If @c true, the remote radio disable menu item is shown. */ bool _remoteRadioDisable; /** If @c true, the scan menu item is shown. */ bool _scan; /** If @c true, the scan list editing is enabled. */ bool _scanListEditing; /** If @c true, the list of missed calls is shown. */ bool _callLogMissed; /** If @c true, the list of answered calls is shown. */ bool _callLogAnswered; /** If @c true, the list of outgoing calls is shown. */ bool _callLogOutgoing; /** If @c true, the talkaround menu item is shown. */ bool _talkaround; /** If @c true, the alert-tone menu item is shown. */ bool _alertTone; /** If @c true, the power settings menu item is shown. */ bool _power; /** If @c true, the backlight menu item is shown. */ bool _backlight; /** If @c true, the boot-screen settings menu item is shown. */ bool _bootScreen; /** If @c true, the keypad-lock settings menu item is shown. */ bool _keypadLock; /** If @c true, the LED indicator settings menu item is shown. */ bool _ledIndicator; /** If @c true, the squelch settings menu item is shown. */ bool _squelch; /** If @c true, the VOX settings menu item is shown. */ bool _vox; /** If @c true, the password menu item is shown. */ bool _password; /** If @c true, the display-mode settings menu item is shown. */ bool _displayMode; /** If @c true, radio programming on the radio is enabled. */ bool _radioProgramming; /** If @c true, the positioning settings menu item is shown. */ bool _gpsInformation; }; /** Represents the TyT general settings extension. * @ingroup tyt */ class TyTSettingsExtension: public ConfigExtension { Q_OBJECT /** The monitor type setting. */ Q_PROPERTY(MonitorType monitorType READ monitorType WRITE setMonitorType) /** If @c true, all LEDs are disabled. */ Q_PROPERTY(bool allLEDsDisabled READ allLEDsDisabled WRITE disableAllLEDs) /** If @c true, the talk permit tone for digital channels is enabled. */ Q_PROPERTY(bool talkPermitToneDigital READ talkPermitToneDigital WRITE enableTalkPermitToneDigital) /** If @c true, the talk permit tone for analog channels is enabled. */ Q_PROPERTY(bool talkPermitToneAnalog READ talkPermitToneAnalog WRITE enableTalkPermitToneAnalog) /** If @c true, the password and lock is enabled. */ Q_PROPERTY(bool passwordAndLock READ passwordAndLock WRITE enablePasswordAndLock) /** If @c true, the channel free tone is enabled. */ Q_PROPERTY(bool channelFreeIndicationTone READ channelFreeIndicationTone WRITE enableChannelFreeIndicationTone) /** If @c true, all tones are disabled. */ Q_PROPERTY(bool allTonesDisabled READ allTonesDisabled WRITE disableAllTones) /** If @c true, the power save mode is enabled. */ Q_PROPERTY(bool powerSaveMode READ powerSaveMode WRITE enablePowerSaveMode) Q_CLASSINFO("powerSaveModeDescription", "Puts the radio into sleep-mode when idle.") Q_CLASSINFO("powerSaveModeLongDescription", "When enabled, the radio enters a sleep mode when idle. That is, when on receive and " "there is no activity on the current channel. However, the radio may need some time " "to wake up from this mode. Hence, the 'wakeupPreamble' need to be enabled by all " "radios in the network to provide this wake-up delay.") /** If @c true, a wakeup preamble is sent. */ Q_PROPERTY(bool wakeupPreamble READ wakeupPreamble WRITE enableWakeupPreamble) Q_CLASSINFO("wakeupPreambleDescription", "If enabled, the radio will transmit a short wake-up " "preamble before each call.") /** If @c true, a picture is shown during boot. */ Q_PROPERTY(bool bootPicture READ bootPicture WRITE enableBootPicture) /** If @c true, the radio is in channel mode. */ Q_PROPERTY(bool channelMode READ channelMode WRITE enableChannelMode) /** If @c true or channelMode is true, the VFO A is in channel mode. */ Q_PROPERTY(bool channelModeA READ channelModeA WRITE enableChannelModeA) /** If @c true or channelMode is true, the VFO B is in channel mode. */ Q_PROPERTY(bool channelModeB READ channelModeB WRITE enableChannelModeB) /** The transmit preamble duration in ms. */ Q_PROPERTY(unsigned txPreambleDuration READ txPreambleDuration WRITE setTXPreambleDuration) /** The group hang time in ms. */ Q_PROPERTY(unsigned groupCallHangTime READ groupCallHangTime WRITE setGroupCallHangTime) /** The private hang time in ms. */ Q_PROPERTY(unsigned privateCallHangTime READ privateCallHangTime WRITE setPrivateCallHangTime) /** The low battery warn interval in seconds. */ Q_PROPERTY(unsigned lowBatteryWarnInterval READ lowBatteryWarnInterval WRITE setLowBatteryWarnInterval) /** If @c true, the call alert-tone is continuous. */ Q_PROPERTY(bool callAlertToneContinuous READ callAlertToneContinuous WRITE enableCallAlertToneContinuous) /** The call alert duration in seconds. */ Q_PROPERTY(unsigned callAlertToneDuration READ callAlertToneDuration WRITE setCallAlertToneDuration) /** The lone-worker response time in minutes. */ Q_PROPERTY(unsigned loneWorkerResponseTime READ loneWorkerResponseTime WRITE setLoneWorkerResponseTime) /** The lone-worker reminder time in seconds. */ Q_PROPERTY(unsigned loneWorkerReminderTime READ loneWorkerReminderTime WRITE setLoneWorkerReminderTime) /** The digital channel scan hang time in ms. */ Q_PROPERTY(unsigned digitalScanHangTime READ digitalScanHangTime WRITE setDigitalScanHangTime) /** The analog channel scan hang time in ms. */ Q_PROPERTY(unsigned analogScanHangTime READ analogScanHangTime WRITE setAnalogScanHangTime) /** If @c true, the backlight is always on. */ Q_PROPERTY(bool backlightAlwaysOn READ backlightAlwaysOn WRITE enableBacklightAlwaysOn) /** If @c backlightAlwaysOn is @c false, specifies the backlight duration in seconds. */ Q_PROPERTY(unsigned backlightDuration READ backlightDuration WRITE setBacklightDuration) /** If @c true, the keypad is locked manually. */ Q_PROPERTY(bool keypadLockManual READ keypadLockManual WRITE enableKeypadLockManual) /** If @c keypadLockManual is @c false, specifies the keypad lock time. */ Q_PROPERTY(unsigned keypadLockTime READ keypadLockTime WRITE setKeypadLockTime) /** If @c true the power-on password is enabled. */ Q_PROPERTY(bool powerOnPasswordEnabled READ powerOnPasswordEnabled WRITE enablePowerOnPassword) /** If @c powerOnPasswordEnabled is @c true, specifies the power-on password. */ Q_PROPERTY(unsigned powerOnPassword READ powerOnPassword WRITE setPowerOnPassword) /** If @c true the radio programming password is enabled. */ Q_PROPERTY(bool radioProgPasswordEnabled READ radioProgPasswordEnabled WRITE enableRadioProgPassword) /** If @c radioProgPasswordEnabled is @c true, specifies the radio programming password. */ Q_PROPERTY(unsigned radioProgPassword READ radioProgPassword WRITE setRadioProgPassword) /** Specifies the PC programming password. */ Q_PROPERTY(QString pcProgPassword READ pcProgPassword WRITE setPCProgPassword) /** If @c true, the private call IDs must match. */ Q_PROPERTY(bool privateCallMatch READ privateCallMatch WRITE enablePrivateCallMatch) /** If @c true, the group call IDs must match. */ Q_PROPERTY(bool groupCallMatch READ groupCallMatch WRITE enableGroupCallMatch) /** Holds the channel hang time in ms. */ Q_PROPERTY(unsigned channelHangTime READ channelHangTime WRITE setChannelHangTime) Q_CLASSINFO("description", "Settings for MD-390, RT8, MD-UV390, RT3S, MD-2017, RT82.") Q_CLASSINFO("longDescription", "Device specific radio settings for TyT and Retevis devices." "Including TyT MD-390, MD-UV390, MD-2017 as well as Retevis RT8, " "RT3S and RT82.") public: /** Possible monitor types. */ enum class MonitorType { Silent=0, Open=1 }; Q_ENUM(MonitorType) public: /** Default constructor. */ Q_INVOKABLE explicit TyTSettingsExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the monitor type. */ MonitorType monitorType() const; /** Sets the monitor type. */ void setMonitorType(MonitorType type); /** Returns @c true if all LEDs are disabled. */ bool allLEDsDisabled() const; /** Disables all LEDs. */ void disableAllLEDs(bool disable); /** Returns @c true if the talk permit tone is enabled for digital channels.*/ bool talkPermitToneDigital() const; /** Enables the talk permit tone for digital channels. */ void enableTalkPermitToneDigital(bool enable); /** Returns @c true if the talk permit tone is enabled for analog channels.*/ bool talkPermitToneAnalog() const; /** Enables the talk permit tone for analog channels. */ void enableTalkPermitToneAnalog(bool enable); /** Returns @c true if the password and lock is enabled. */ bool passwordAndLock() const; /** Enables the password and lock. */ void enablePasswordAndLock(bool enable); /** Returns @c true if channel-free indication tone is enabled. */ bool channelFreeIndicationTone() const; /** Enables channel-free indication tone. */ void enableChannelFreeIndicationTone(bool enable); /** Returns @c true if all tones are disabled. */ bool allTonesDisabled() const; /** Disables all tones. */ void disableAllTones(bool disable); /** Returns @c true if the power save mode is enabled. */ bool powerSaveMode() const; /** Enables the power save mode. */ void enablePowerSaveMode(bool enable); /** Returns @c true if the wake-up preamble is sent. */ bool wakeupPreamble() const; /** Enables transmission of wakeup preamble. */ void enableWakeupPreamble(bool enable); /** Returns @c true if a picture is shown during boot. */ bool bootPicture() const; /** Enables the boot picture. */ void enableBootPicture(bool enable); /** Returns @c true if the radio is in channel mode. Overrides @c channelModeA and @c channelModeB. */ bool channelMode() const; /** Enables/disables channel mode for the radio. */ void enableChannelMode(bool enable); /** Returns @c true if VFO A is in channel mode. Overridden by @c channelMode. */ bool channelModeA() const; /** Enables/disables channel mode for the VFO A. */ void enableChannelModeA(bool enable); /** Returns @c true if VFO B is in channel mode. Overridden by @c channelMode. */ bool channelModeB() const; /** Enables/disables channel mode for the VFO B. */ void enableChannelModeB(bool enable); /** Returns the TX preamble duration in ms. */ unsigned txPreambleDuration() const; /** Sets the TX preamble duration in ms. */ void setTXPreambleDuration(unsigned ms); /** Returns the group call hang time in ms. */ unsigned groupCallHangTime() const; /** Sets the group-call hang time in ms. */ void setGroupCallHangTime(unsigned ms); /** Returns the private call hang time in ms. */ unsigned privateCallHangTime() const; /** Sets the private-call hang time in ms. */ void setPrivateCallHangTime(unsigned ms); /** Returns the low-battery warn interval in seconds. */ unsigned lowBatteryWarnInterval() const; /** Sets the low-battery warn interval in seconds. */ void setLowBatteryWarnInterval(unsigned sec); /** Returns @c true if the call alert-tone is continuous. */ bool callAlertToneContinuous() const; /** Sets the call alert-tone continuous. */ void enableCallAlertToneContinuous(bool enable); /** Returns the call alert-tone duration in seconds. */ unsigned callAlertToneDuration() const; /** Sets the call alert-tone duration in seconds. */ void setCallAlertToneDuration(unsigned sec); /** Returns the lone worker response time in minutes. */ unsigned loneWorkerResponseTime() const; /** Sets the lone-worker response time in minutes. */ void setLoneWorkerResponseTime(unsigned min); /** Returns the lone-worker reminder time in seconds. */ unsigned loneWorkerReminderTime() const; /** Sets the lone-worker reminder timer in seconds. */ void setLoneWorkerReminderTime(unsigned sec); /** Returns the hang time scanning for digital channels. */ unsigned digitalScanHangTime() const; /** Sets the scan hang-time for digital channels. */ void setDigitalScanHangTime(unsigned ms); /** Returns the hang time scanning for analog channels. */ unsigned analogScanHangTime() const; /** Sets the scan hang-time for analog channels. */ void setAnalogScanHangTime(unsigned ms); /** Returns @c true if the backlight is always on. */ bool backlightAlwaysOn() const; /** Enables the backlight continuously. */ void enableBacklightAlwaysOn(bool enable); /** Returns the backlight duration in seconds. */ unsigned backlightDuration() const; /** Sets the backlight duration in seconds. */ void setBacklightDuration(unsigned sec); /** Returns @c true if the keypad lock is manual. */ bool keypadLockManual() const; /** Sets the keypad lock to manual. */ void enableKeypadLockManual(bool enable); /** Returns the keypad lock time in seconds. */ unsigned keypadLockTime() const; /** Sets the keypad lock time in seconds. */ void setKeypadLockTime(unsigned sec); /** Returns @c true if power-on password is enabled. */ bool powerOnPasswordEnabled() const; /** Enables the power on password. */ void enablePowerOnPassword(bool enable); /** Returns the power-on password. */ unsigned powerOnPassword() const; /** Sets the power-on password. */ void setPowerOnPassword(unsigned passwd); /** Returns @c true if radio programming password is enabled. */ bool radioProgPasswordEnabled() const; /** Enables the radio programming password. */ void enableRadioProgPassword(bool enable); /** Returns the radio programming password. */ unsigned radioProgPassword() const; /** Sets the radio programming password. */ void setRadioProgPassword(unsigned passwd); /** Returns the PC programming password. */ const QString &pcProgPassword() const; /** Sets PC programming password. */ void setPCProgPassword(const QString &passwd); /** Returns @c true if the private call IDs must match. */ bool privateCallMatch() const; /** Enables/disables private call match. */ void enablePrivateCallMatch(bool enable); /** Returns @c true if the group call IDs must match. */ bool groupCallMatch() const; /** Enables/disables group call match. */ void enableGroupCallMatch(bool enable); /** Returns the channel hang time in ms. */ unsigned channelHangTime() const; /** Sets the channel hang time in ms. */ void setChannelHangTime(unsigned ms); public: /*ConfigItem *allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack());*/ protected: /** Holds the monitor type. */ MonitorType _monitorType; /** If @c true all LEDs are disabled. */ bool _allLEDsDisabled; /** If @c true the talk-permit tone is enabled for digital channels. */ bool _talkPermitToneDigital; /** If @c true the talk-permit tone is enabled for analog channels. */ bool _talkPermitToneAnalog; /** If @c true the password and lock is enabled. */ bool _passwdAndLock; /** If @c true, the channel free indication tone is enabled. */ bool _channelFreeIndicationTone; /** If @c true, all tones are disabled. */ bool _allTonesDisabled; /** If @c true, the power save mode is enabled. */ bool _powerSaveMode; /** If @c true, the wake-up preamble is sent. */ bool _wakeupPreamble; /** If @c true the boot picture is enabled. */ bool _bootPicture; /** If @c true or channelMode is true, the VFO A is in channel (memory) mode. */ bool _channelModeA; /** If @c true or channelMode is true, the VFO B is in channel (memory) mode. */ bool _channelModeB; /** If @c true, the radio is in channel (memory) mode. Overrides channelModeA and channelModeB. */ bool _channelMode; /** Holds the TX preamble duration. */ unsigned _txPreambleDuration; /** Holds the group-call hang time. */ unsigned _groupCallHangTime; /** Holds the private-call hang time. */ unsigned _privateCallHangTime; /** Holds the low-battery warn interval. */ unsigned _lowBatteryWarnInterval; /** If @c true, the call alert-tone is continuous. */ bool _callAlertToneContinuous; /** Holds the call alert-tone duration. */ unsigned _callAlertToneDuration; /** Holds the lone-worker response time. */ unsigned _loneWorkerResponseTime; /** Holds the lone-worker reminder time. */ unsigned _loneWorkerReminderTime; /** Holds the scan hang-time for digital channels. */ unsigned _digitalScanHangTime; /** Holds the scan hang-time for analog channels. */ unsigned _analogScanHangTime; /** If @c true, the backlight is always on. */ bool _backlightAlwaysOn; /** Holds the backlight duration. */ unsigned _backlightDuration; /** If @c true, the keypad lock is manual. */ bool _keypadLockManual; /** Holds the keypad lock time. */ unsigned _keypadLockTime; /** If @c true, the power-on password is enabled. */ bool _powerOnPasswordEnabled; /** Holds the power-on password. */ unsigned _powerOnPassword; /** If @c true, the radio programming password is enabled. */ bool _radioProgPasswordEnabled; /** Holds the radio programming password. */ unsigned _radioProgPassword; /** Holds the PC programming password. */ QString _pcProgPassword; /** If @c true, the private call IDs must match. */ bool _privateCallMatch; /** If @c true, the group call IDs must match. */ bool _groupCallMatch; /** Holds the channel hang time in ms. */ unsigned _channelHangTime; }; /** Groups several extension for TyT devices. * @ingroup tyt */ class TyTConfigExtension: public ConfigExtension { Q_OBJECT /** The button settings for TyT devices. */ Q_PROPERTY(TyTButtonSettings* buttonSettings READ buttonSettings) /** The menu settings for TyT devices. */ Q_PROPERTY(TyTMenuSettings* menuSettings READ menuSettings) public: /** Constructor. Also allocates all associates extensions. */ Q_INVOKABLE explicit TyTConfigExtension(QObject *parent=nullptr); ConfigItem *clone() const; /** Returns the button settings extension for TyT devices. */ TyTButtonSettings *buttonSettings() const; /** Returns the menu settings extension for TyT devices. */ TyTMenuSettings *menuSettings() const; public: /*ConfigItem *allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack());*/ protected: /** Owns the button settings extension. */ TyTButtonSettings *_buttonSettings; /** Owns the menu settings extension. */ TyTMenuSettings *_menuSettings; }; #endif // TYTBUTTONSETTINGSEXTENSION_HH qdmr-0.12.3/lib/tyt_interface.cc000066400000000000000000000125661501654372000164720ustar00rootroot00000000000000#include "tyt_interface.hh" #include "logger.hh" #include #include "utils.hh" #include "errorstack.hh" #define USB_VID 0x0483 #define USB_PID 0xdf11 TyTInterface::TyTInterface(const USBDeviceDescriptor &descr, const ErrorStack &err, QObject *parent) : DFUSEDevice(descr, err, 16, parent), RadioInterface() { if (! DFUDevice::isOpen()) { errMsg(err) << "Cannot open TyTInterface."; return; } // Enter Programming Mode. if (wait_idle()) { errMsg(err) << "Device not ready. Close device."; close(); return; } if (md380_command(0x91, 0x01, err)) { errMsg(err) << "Cannot enter programming mode. Close device."; reboot(err); close(); return; } // Get device identifier in a static buffer. const char *idstr = identify(err); if (idstr && (0==strcmp("DR780", idstr))) { _ident = RadioInfo::byID(RadioInfo::MD380); } else if (idstr && (0==strcmp("MD390", idstr))) { _ident = RadioInfo::byID(RadioInfo::MD390); } else if (idstr && (0==strcmp("MD-UV380", idstr))) { _ident = RadioInfo::byID(RadioInfo::UV380); } else if (idstr && (0==strcmp("MD-UV390", idstr))) { _ident = RadioInfo::byID(RadioInfo::UV390); } else if (idstr && (0==strcmp("2017", idstr))) { _ident = RadioInfo::byID(RadioInfo::MD2017); } else if (idstr && (0==strcmp("DM-1701", idstr))) { _ident = RadioInfo::byID(RadioInfo::DM1701); } else if (idstr) { errMsg(err) << "Unknown TyT device '" << idstr << "'."; close(); return; } // Zero address. if(set_address(0x00000000, err)) { errMsg(err) << "Cannot set device address to 0x00000000."; close(); return; } logDebug() << "Found device " << _ident.manufacturer() << " "<< _ident.name() << " at " << descr.description() << "."; } TyTInterface::~TyTInterface() { if (isOpen()) close(); } USBDeviceInfo TyTInterface::interfaceInfo() { return USBDeviceInfo(USBDeviceInfo::Class::DFU, USB_VID, USB_PID); } QList TyTInterface::detect(bool saveOnly) { Q_UNUSED(saveOnly); return DFUDevice::detect(USB_VID, USB_PID); } void TyTInterface::close() { if (isOpen()) { _ident = RadioInfo(); } DFUSEDevice::close(); } bool TyTInterface::isOpen() const { return DFUSEDevice::isOpen() && _ident.isValid(); } RadioInfo TyTInterface::identifier(const ErrorStack &err) { Q_UNUSED(err); return _ident; } int TyTInterface::md380_command(uint8_t a, uint8_t b, const ErrorStack &err) { unsigned char cmd[2] = { a, b }; if (int error = download(0, cmd, 2, err)) return error; usleep(100000); return wait_idle(); } int TyTInterface::set_address(uint32_t address, const ErrorStack &err) { unsigned char cmd[5] = { 0x21, (uint8_t)address, (uint8_t)(address >> 8), (uint8_t)(address >> 16), (uint8_t)(address >> 24), }; if (int error = download(0, cmd, 5, err)) return error; return wait_idle(); } int TyTInterface::erase_block(uint32_t address, const ErrorStack &err) { unsigned char cmd[5] = { 0x41, (uint8_t)address, (uint8_t)(address >> 8), (uint8_t)(address >> 16), (uint8_t)(address >> 24), }; if (int error = download(0, cmd, 5, err)) return error; wait_idle(); return 0; } const char * TyTInterface::identify(const ErrorStack &err) { static uint8_t data[64]; md380_command(0xa2, 0x01, err); if (upload(0, data, 64, err)) return nullptr; return (const char*) data; } bool TyTInterface::erase(unsigned start, unsigned size, void(*progress)(unsigned, void *), void *ctx, const ErrorStack &err) { int error; // Enter Programming Mode. if ((error = get_status(err))) return false; if ((error = wait_idle())) return false; if ((error = md380_command(0x91, 0x01, err))) return false; usleep(100000); unsigned end = start+size; start = align_addr(start, 0x10000); end = align_size(end, 0x10000); size = end-start; for (unsigned i=0; i * WhatCode Resp. Len. Description> * 0x01 32 The radio identifier as a string + some unknown * information. * 0x02 4 Unknown * 0x03 24 Unknown * 0x04 8 Unknown * 0x07 16 Unknown * * * @ingroup tyt */ class TyTInterface : public DFUSEDevice, public RadioInterface { Q_OBJECT public: /** Constructor. Opens an interface to the specified interface. */ TyTInterface(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr); /** Destructor. */ ~TyTInterface(); bool isOpen() const; RadioInfo identifier(const ErrorStack &err=ErrorStack()); void close(); bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()); bool read_finish(const ErrorStack &err=ErrorStack()); bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()); bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()); bool write_finish(const ErrorStack &err=ErrorStack()); bool reboot(const ErrorStack &err=ErrorStack()); /** Erases a memory section at @c start of size @c size. */ bool erase(unsigned start, unsigned size, void (*progress)(unsigned, void *)=nullptr, void *ctx=nullptr, const ErrorStack &err=ErrorStack()); public: /** Returns some information about the interface. */ static USBDeviceInfo interfaceInfo(); /** Tries to find all interfaces connected TyT radios. */ static QList detect(bool saveOnly=true); protected: /** Internal used function to send a control command to the device. */ int md380_command(uint8_t a, uint8_t b, const ErrorStack &err=ErrorStack()); /** Internal used function to set the current I/O address. */ int set_address(uint32_t address, const ErrorStack &err=ErrorStack()); /** Internal used function to erase a specific block. */ int erase_block(uint32_t address, const ErrorStack &err=ErrorStack()); /** Internal used function to read the device identifier. */ const char *identify(const ErrorStack &err=ErrorStack()); protected: /** Read identifier. */ RadioInfo _ident; }; #endif // TYTINTERFACE_HH qdmr-0.12.3/lib/tyt_radio.cc000066400000000000000000000172671501654372000156330ustar00rootroot00000000000000#include "tyt_radio.hh" #include "config.hh" #include "logger.hh" #include "utils.hh" #define BSIZE 1024 TyTRadio::TyTRadio(TyTInterface *device, QObject *parent) : Radio(parent), _dev(device), _codeplugFlags(), _config(nullptr) { // pass... } TyTRadio::~TyTRadio() { if (_dev && _dev->isOpen()) { logDebug() << "Reboot TyT device."; _dev->reboot(); logDebug() << "Close connection to TyT device."; _dev->close(); } if (_dev) { _dev->deleteLater(); _dev = nullptr; } logDebug() << "Destructed TyT radio."; } bool TyTRadio::startDownload(bool blocking, const ErrorStack &err) { if (StatusIdle != _task) return false; _task = StatusDownload; _errorStack = err; if (blocking) { run(); return (StatusIdle == _task); } start(); return true; } bool TyTRadio::startUpload(Config *config, bool blocking, const Codeplug::Flags &flags, const ErrorStack &err) { if (StatusIdle != _task) return false; if (_config) delete _config; if (! (_config = config)) return false; _config->setParent(this); _task = StatusUpload; _errorStack = err; _codeplugFlags = flags; if (blocking) { this->run(); return (StatusIdle == _task); } this->start(); return true; } bool TyTRadio::startUploadCallsignDB(UserDatabase *db, bool blocking, const CallsignDB::Selection &selection, const ErrorStack &err) { if (StatusIdle != _task) return false; logDebug() << "Encode call-sign DB."; if (nullptr == callsignDB()) { errMsg(err) << "Cannot upload callsign DB. DB not created."; return false; } callsignDB()->encode(db, selection); _task = StatusUploadCallsigns; _errorStack = err; if (blocking) { this->run(); return (StatusIdle == _task); } this->start(); return true; } bool TyTRadio::startUploadSatelliteConfig(SatelliteDatabase *db, bool blocking, const ErrorStack &err) { errMsg(err) << "Satellite config upload is not implemented yet."; return false; } void TyTRadio::run() { if (StatusDownload == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { emit downloadError(this); return; } if (! download()) { _dev->reboot(); _dev->close(); _task = StatusError; emit downloadError(this); return; } _task = StatusIdle; _dev->reboot(); _dev->close(); emit downloadFinished(this, &codeplug()); _config = nullptr; } else if (StatusUpload == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { emit uploadError(this); return; } if (! upload()) { _dev->reboot(); _dev->close(); _task = StatusError; emit uploadError(this); return; } _dev->reboot(); _dev->close(); _task = StatusIdle; emit uploadComplete(this); } else if (StatusUploadCallsigns == _task) { if ((nullptr==_dev) || (! _dev->isOpen())) { emit uploadError(this); return; } if(! uploadCallsigns()) { _dev->reboot(); _dev->close(); _task = StatusError; emit uploadError(this); return; } _task = StatusIdle; _dev->reboot(); _dev->close(); emit uploadComplete(this); } } bool TyTRadio::download() { emit downloadStarted(); logDebug() << "Download of " << codeplug().image(0).numElements() << " elements."; // Check every segment in the codeplug size_t totb = 0; for (int n=0; nread(0, (b0+b)*BSIZE, codeplug().data((b0+b)*BSIZE), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot download codeplug."; return false; } emit downloadProgress(float(bcount*100)/totb); } } return true; } bool TyTRadio::upload() { emit uploadStarted(); // Check every segment in the codeplug if (! codeplug().isAligned(BSIZE)) { errMsg(_errorStack) << "Cannot upload codeplug: Codeplug is not aligned with blocksize " << BSIZE << "."; return false; } size_t totb = codeplug().memSize(); size_t bcount = 0; // If codeplug gets updated, download codeplug from device first: if (_codeplugFlags.updateCodePlug) { for (int n=0; nread(0, (b0+b)*BSIZE, codeplug().data((b0+b)*BSIZE), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot upload codeplug."; return false; } emit uploadProgress(float(bcount*50)/totb); } } } // Encode config into codeplug logDebug() << "Encode codeplug."; codeplug().encode(_config, _codeplugFlags); // then erase memory for (int i=0; ierase(codeplug().image(0).element(i).address(), codeplug().image(0).element(i).memSize(), nullptr, nullptr, _errorStack); logDebug() << "Upload " << codeplug().image(0).numElements() << " elements."; // then, upload modified codeplug bcount = 0; for (int n=0; nwrite(0, (b0+b)*BSIZE, codeplug().data((b0+b)*BSIZE), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot upload codeplug."; return false; } emit uploadProgress(50+float(bcount*50)/totb); } } return true; } bool TyTRadio::uploadCallsigns() { emit uploadStarted(); logDebug() << "Check alignment."; // Check alignment in the codeplug if (! callsignDB()->isAligned(BSIZE)) { errMsg(_errorStack) << "Cannot upload callsign db: Callsign DB is not aligned with blocksize " << BSIZE << "."; return false; } // then erase memory logDebug() << "Erase memory section for call-sign DB."; _dev->erase(callsignDB()->image(0).element(0).address(), callsignDB()->image(0).element(0).memSize(), [](unsigned percent, void *ctx) { emit ((TyTRadio *)ctx)->uploadProgress(percent/2); }, this, _errorStack); logDebug() << "Upload " << callsignDB()->image(0).numElements() << " elements."; // Total amount of data to transfer size_t totb = callsignDB()->memSize(); // Upload callsign DB unsigned addr = callsignDB()->image(0).element(0).address(); unsigned size = callsignDB()->image(0).element(0).memSize(); unsigned b0 = addr/BSIZE, nb = size/BSIZE; for (size_t b=0, bcount=0; bwrite(0, (b0+b)*BSIZE, callsignDB()->data((b0+b)*BSIZE), BSIZE, _errorStack)) { errMsg(_errorStack) << "Cannot upload codeplug."; return false; } emit uploadProgress(50+float(bcount*50)/totb); } return true; } qdmr-0.12.3/lib/tyt_radio.hh000066400000000000000000000036151501654372000156350ustar00rootroot00000000000000/** @defgroup tyt TYT/Retevis Radios * Abstract classes for TYT and Retevis radios. * * @ingroup dsc */ #ifndef TYT_RADIO_HH #define TYT_RADIO_HH #include "radio.hh" #include "tyt_interface.hh" /** Implements an USB interface to TYT & Retevis radios. * * @ingroup tyt */ class TyTRadio: public Radio { Q_OBJECT public: /** Do not construct this class directly, rather use @c Radio::detect. */ explicit TyTRadio(TyTInterface *device=nullptr, QObject *parent=nullptr); virtual ~TyTRadio(); public slots: /** Starts the download of the codeplug and derives the generic configuration from it. */ bool startDownload(bool blocking=false, const ErrorStack &err=ErrorStack()); /** Derives the device-specific codeplug from the generic configuration and uploads that * codeplug to the radio. */ bool startUpload(Config *config, bool blocking=false, const Codeplug::Flags &flags = Codeplug::Flags(), const ErrorStack &err=ErrorStack()); /** Encodes the given user-database and uploades it to the device. */ bool startUploadCallsignDB(UserDatabase *db, bool blocking=false, const CallsignDB::Selection &selection=CallsignDB::Selection(), const ErrorStack &err=ErrorStack()); bool startUploadSatelliteConfig(SatelliteDatabase *db, bool blocking, const ErrorStack &err); protected: /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */ void run(); private: virtual bool download(); virtual bool upload(); virtual bool uploadCallsigns(); protected: /** The interface to the radio. */ TyTInterface *_dev; /** Holds the flags to control assembly and upload of code-plugs. */ Codeplug::Flags _codeplugFlags; /** The generic configuration. */ Config *_config; /** A weak reference to the user-database. */ UserDatabase *_userDB; }; #endif // UV390_HH qdmr-0.12.3/lib/usbdevice.cc000066400000000000000000000213411501654372000155720ustar00rootroot00000000000000#include "usbdevice.hh" #include #include #include #include "logger.hh" #include "radioinfo.hh" #include "anytone_interface.hh" #include "radioddity_interface.hh" #include "opengd77_interface.hh" #include "tyt_interface.hh" #include "dr1801uv_interface.hh" #include "c7000device.hh" /* ********************************************************************************************* * * Implementation of USBDeviceHandle * ********************************************************************************************* */ USBDeviceHandle::USBDeviceHandle() : bus(0xff), device(0xff) { // pass... } USBDeviceHandle::USBDeviceHandle(uint8_t busno, uint8_t deviceno, uint32_t locid) : bus(busno), device(deviceno), locationId(locid) { // pass... } bool USBDeviceHandle::operator==(const USBDeviceHandle &other) { return (bus == other.bus) && (device == other.device); } /* ********************************************************************************************* * * Implementation of USBDeviceInfo * ********************************************************************************************* */ USBDeviceInfo::USBDeviceInfo() : _class(Class::None), _vid(0), _pid(0) { // pass... } USBDeviceInfo::USBDeviceInfo(Class cls, uint16_t vid, uint16_t pid, bool save) : _class(cls), _vid(vid), _pid(pid), _save(save) { // pass... } USBDeviceInfo::USBDeviceInfo(const USBDeviceInfo &other) : _class(other._class), _vid(other._vid), _pid(other._pid), _save(other._save) { // pass... } USBDeviceInfo & USBDeviceInfo::operator =(const USBDeviceInfo &other) { _class = other._class; _vid = other._vid; _pid = other._pid; _save = other._save; return *this; } bool USBDeviceInfo::operator ==(const USBDeviceInfo &other) const { // Class must match, VID/PID only need to match, if both are != 0. return (other._class == _class) && ((! other.hasVendorID()) || (! hasVendorID()) || (other._vid == _vid)) && ((! other.hasProductID()) || (! hasProductID()) || (other._pid == _pid)); } bool USBDeviceInfo::operator !=(const USBDeviceInfo &other) const { return !(*this == other); } USBDeviceInfo::~USBDeviceInfo() { // pass... } bool USBDeviceInfo::isValid() const { return Class::None != _class; } USBDeviceInfo::Class USBDeviceInfo::interfaceClass() const { return _class; } bool USBDeviceInfo::hasVendorID() const { return 0 != vendorId(); } uint16_t USBDeviceInfo::vendorId() const { return _vid; } bool USBDeviceInfo::hasProductID() const { return 0 != productId(); } uint16_t USBDeviceInfo::productId() const { return _pid; } bool USBDeviceInfo::isSave() const { return _save; } QString USBDeviceInfo::description() const { QString res; QTextStream stream(&res); switch (_class) { case Class::None: stream << "Invalid"; break; case Class::Serial: stream << "USB-serial interface " << QString::number(_vid,16) << ":" << QString::number(_pid,16); break; case Class::DFU: stream << "USB device in DFU mode " << QString::number(_vid,16) << ":" << QString::number(_pid,16); break; case Class::HID: stream << "HID " << QString::number(_vid,16) << ":" << QString::number(_pid,16); break; case Class::C7K: stream << "C7000 " << QString::number(_vid,16) << ":" << QString::number(_pid,16); break; } return res; } QString USBDeviceInfo::longDescription() const { QStringList radios; foreach (RadioInfo radio, RadioInfo::allRadios(*this, true)) { radios.append(QString("%1 %2").arg(radio.manufacturer(), radio.name())); } // This should not happen if (radios.isEmpty()) return QString("Unknown interface."); return QString("Possibly interfacing %1").arg(radios.join(", ")); } /* ********************************************************************************************* * * Implementation of USBDeviceDescriptor * ********************************************************************************************* */ USBDeviceDescriptor::USBDeviceDescriptor() : USBDeviceInfo(), _device() { // pass... } USBDeviceDescriptor::USBDeviceDescriptor(const USBDeviceInfo &info, const QString &device) : USBDeviceInfo(info), _device(device) { // pass... } USBDeviceDescriptor::USBDeviceDescriptor(const USBDeviceInfo &info, const USBDeviceHandle &device) : USBDeviceInfo(info), _device(QVariant::fromValue(device)) { // pass... } USBDeviceDescriptor::USBDeviceDescriptor(const USBDeviceDescriptor &other) : USBDeviceInfo(other), _device(other._device) { // pass... } USBDeviceDescriptor & USBDeviceDescriptor::operator =(const USBDeviceDescriptor &other) { USBDeviceInfo::operator =(other); _device = other._device; return *this; } bool USBDeviceDescriptor::isValid() const { if (! USBDeviceInfo::isValid()) return false; // dispatch by device class switch (_class) { case Class::None: return false; case Class::Serial: return validSerial(); case Class::DFU: case Class::HID: case Class::C7K: return validRawUSB(); } return false; } bool USBDeviceDescriptor::validRawUSB() const { int error, num; libusb_context *ctx; if (0 > (error = libusb_init(&ctx))) { logError() << "Libusb init failed (" << error << "): " << libusb_strerror((enum libusb_error) error) << "."; return false; } libusb_device **lst; if (0 == (num = libusb_get_device_list(ctx, &lst))) { logDebug() << "No USB devices found at all."; // unref devices and free list libusb_free_device_list(lst, 1); libusb_exit(ctx); return false; } USBDeviceHandle addr = _device.value(); logDebug() << "Search for a device matching VID:PID " << QString::number(_vid, 16) << ":" << QString::number(_pid, 16) << " at bus " << addr.bus << ", device " << addr.device << "."; bool found = false; for (int i=0; (i(); return QString("USB device in DFU mode: bus %1, device %2").arg(addr.bus).arg(addr.device); } else if (USBDeviceInfo::Class::HID == _class) { USBDeviceHandle addr = _device.value(); return QString("USB HID: bus %1, device %2").arg(addr.bus).arg(addr.device); } else if (USBDeviceInfo::Class::C7K == _class) { USBDeviceHandle addr = _device.value(); return QString("USB C7000 HT: bus %1, device %2").arg(addr.bus).arg(addr.device); } return "Invalid"; } const QVariant & USBDeviceDescriptor::device() const { return _device; } QString USBDeviceDescriptor::deviceHandle() const { switch (_class) { case Class::None: break; case Class::DFU: case Class::HID: case Class::C7K: return QString("%1:%2").arg(_device.value().bus) .arg(_device.value().device); case Class::Serial: return _device.toString(); } return "[invalid]"; } QList USBDeviceDescriptor::detect(bool saveOnly) { QList res; res.append(AnytoneInterface::detect(saveOnly)); res.append(OpenGD77Interface::detect(saveOnly)); res.append(RadioddityInterface::detect(saveOnly)); res.append(TyTInterface::detect(saveOnly)); res.append(DR1801UVInterface::detect(saveOnly)); res.append(C7000Device::detect(saveOnly)); return res; } qdmr-0.12.3/lib/usbdevice.hh000066400000000000000000000223561501654372000156130ustar00rootroot00000000000000/** @defgroup detect Device detection and enumeration. * This module collects classes and functions to discover and select interfaces to connected radios. * * With an increasing number of supported devices, the issue arises, that auto detection of * radios may fail or may even harmful. Some manufacturers simply use generic USB-serial chips * within the cable to talk to them over USB. These chips may also be used in other devices that may * react harmful to qdmrs attempts to identify them. Moreover, the assumption of the first detected * device with a specific VID:PID combination may not be valid. To this end, some means of * discovering possible radios and selecting a specific one by the user are needed. Also some radios * do not identify themselves before any action is performed (i.e., reading, writing the codeplug * or callsign db). Hence, for some devices, the user must specify the type of the radio. The latter * concerns the Kydera CDR-300UV, Retevis RT73 and similar devices. * * This module specifies the classes and functions to discover possible radios and to address each * one uniquely. They allow for an implementation of a semi-automatic device detection. That is, * for the majority of radios, if a single matching VID:PID combination is found, it can be assumed * that this device is a radio and it can then be identified by sending commands to it. Some radios, * however, like the Kydera CDR-300UV, cannot be identified before the actual codeplug read or * write operation. They simply do not provide a command for identification. * * For a save semi-automatic detection the following steps are performed: * @dotfile autodetect.dot "Semi-automatic radio detection" * * -# Search for all USB devices with known VID:PID combinations. This can be done using the * @c USBDeviceDescriptor::detect method. It returns a list of matching device descriptors * found. If only one device is found, one may continue with that one if it is save to assume * that the device detected is a DMR radio. The latter is not true for radios using generic * USB CDC-ACM chips, as other serial devices may be connected. If several devices are found or * it is not save to assume a DMR radio, the user must select a device. * -# Once the USB device is selected, one needs to identify the connected radio. Unfortunately, * not all radios can be identified easily by simply sending a command to it. To this end, * one first needs to check if the selected device is identifiable. That is, if the protocol * provides commands to identify the connected radio. The @c USBDeviceInfo provides this * information. If a device is not identifiable, the user must specify the specific connected * radio. This can be done by obtaining all known radios matching the selected USB device * (VID:PID) by calling RadioInfo::allRadios, passing the @c USBDeviceDescriptor. * * @section detectExample A example for AnyTone devices * This example tries to detect an AnyTone device and reads the binary codeplug from it. Once the * codeplug is read, it is decoded into its generic device independent representation (@c Config). * * @code * #include "libdmrconf/usbdevice.hh" * #include "libdmrconf/anytone_radio.hh" * * int main(void) * { * // First, search matching devices (only AnyTones) * // to find all supported devices, call @c USBDescriptor::detect(); * QList devices = AnytoneInterface::detect(); * if (1 != devices.count()) { * // Either none or more than one device found... * return -1; * } * * // A place to put error messages * ErrorStack err; * // Dedetect the specific radio and get radio descriptor. * // To detect any radio based on the selected descriptor, call @c Radio::detect(). * Radio *radio = AnytoneRadio::detect(devices.first(), RadioInfo(), err); * if (nullptr == radio) { * // There went something wrong, check err. * return -1; * } * * // Read codeplug from device blocking. * if (! radio->startDownload(true, err)) { * // Some read error, check err. * delete radio; * return -1; * } * * // Decode codeplug into generic representation * Config genericCodeplug; * if (! radio->codeplug().decode(&genericCodeplug, err)) { * // Some decoding error, check err. * delete radio; * return -1; * } * * // Do whatever you like with the codeplug. * * return 0; * } * @endcode * * @ingroup rif */ #ifndef USBDEVICE_HH #define USBDEVICE_HH #include #include /** Combines the USB bus and device number, to address a USB device uniquely. * * @ingroup detect */ struct USBDeviceHandle { uint8_t bus; ///< Holds the bus number. uint8_t device; ///< Holds the device address. uint32_t locationId; ///< On MacOS, holds the location ID. /** Empty constructor. */ USBDeviceHandle(); /** Constructor from bus and device number. */ USBDeviceHandle(uint8_t busno, uint8_t deviceno, uint32_t locid=0); /** Compares only wrt bus and device number. */ bool operator==(const USBDeviceHandle &other); }; Q_DECLARE_METATYPE(USBDeviceHandle) /** Generic information about a possible radio interface. * * This class combines the USB vendor, product ID and some meta information about the interface. * In particular if it is save to access the device without user ineraction and if the protocol * implements means for identifying the specific radio. * * @ingroup detect */ class USBDeviceInfo { public: /** Possible interface types. */ enum class Class { None, ///< Class for invalid interface info. Serial, ///< Serial port interface class. DFU, ///< DFU interface class. HID, ///< HID (human-interface device) interface class. C7K ///< Raw USB access to C7000 devices. }; public: /** Empty constructor. */ USBDeviceInfo(); /** Constructor from class, VID and PID. */ USBDeviceInfo(Class cls, uint16_t vid, uint16_t pid, bool save=true); /** Destructor. */ virtual ~USBDeviceInfo(); /** Copy constructor. */ USBDeviceInfo(const USBDeviceInfo &other); /** Assignment. */ USBDeviceInfo &operator =(const USBDeviceInfo &other); /** Comparison. */ bool operator ==(const USBDeviceInfo &other) const; /** Comparison. */ bool operator !=(const USBDeviceInfo &other) const; /** Returns @c true if the interface info is valid. */ bool isValid() const; /** Returns the interface class. */ Class interfaceClass() const; /** Retunrs @c true, if a vendor ID is set. */ bool hasVendorID() const; /** Returns the vendor ID or 0 if not set. */ uint16_t vendorId() const; /** Retunrs @c true, if a product ID is set. */ bool hasProductID() const; /** Returns the product ID or 0 if not set. */ uint16_t productId() const; /** Returns a brief human readable description of the interface. */ QString description() const; /** Returns a more extensive human readable description of the interface. */ QString longDescription() const; /** Returns @c true if it is save to send commands to this device without user approval. * This is true for radios which use somewhat unique VID:PIDs. Radios with generic USB-serial * chips are not save, as other devices may use the same chip and sending data to these devices * may be harmful. */ bool isSave() const; protected: /** The class of the interface. */ Class _class; /** The USB vid. */ uint16_t _vid; /** The USB pid. */ uint16_t _pid; /** If @c true, it is save to send commands to the device without user approval. */ bool _save; }; /** Base class for all radio interface descriptors representing a unique interface to a * connected radio. * * This class extends the @c USBDeviceInfo by some information to identify a USB uniquely. This is * either the bus and device number or the path to the serial port. * * @ingroup detect */ class USBDeviceDescriptor: public USBDeviceInfo { protected: /** Hidden constructor from info and path string. */ USBDeviceDescriptor(const USBDeviceInfo &info, const QString &device); /** Hidden constructor from info and USB device address. */ USBDeviceDescriptor(const USBDeviceInfo &info, const USBDeviceHandle &device); public: /** Empty constructor. */ USBDeviceDescriptor(); /** Copy constructor. */ USBDeviceDescriptor(const USBDeviceDescriptor &other); /** Assignment */ USBDeviceDescriptor &operator =(const USBDeviceDescriptor &other); /** Returns @c true if the descriptor is still valid. That is, if the described device is still * connected. */ bool isValid() const; /** Returns a human readable description of the device. */ QString description() const; /** Returns the device information identifying the interface uniquely. */ const QVariant &device() const; /** Returns a unique string representation of the device information. */ QString deviceHandle() const; public: /** Searches for all connected radios (may contain false positives). */ static QList detect(bool saveOnly=true); protected: /** Checks a serial port. */ bool validSerial() const; /** Checks a raw USB device. */ bool validRawUSB() const; protected: /** Holds some information to identify the radio interface uniquely. */ QVariant _device; }; #endif // USBDEVICE_HH qdmr-0.12.3/lib/usbserial.cc000066400000000000000000000154531501654372000156210ustar00rootroot00000000000000#include "usbserial.hh" #include "logger.hh" #include #include #include /* ******************************************************************************************** * * Implementation of USBSerial::Info * ******************************************************************************************** */ USBSerial::Descriptor::Descriptor(uint16_t vid, uint16_t pid, const QString &device, bool isSave) : USBDeviceDescriptor(USBDeviceInfo(Class::Serial, vid, pid, isSave), device) { // pass... } /* ******************************************************************************************** * * Implementation of USBSerial * ******************************************************************************************** */ USBSerial::USBSerial(const USBDeviceDescriptor &descriptor, BaudRate rate, const ErrorStack &err, QObject *parent) : QSerialPort(parent), RadioInterface() { if (USBDeviceInfo::Class::Serial != descriptor.interfaceClass()) { errMsg(err) << "Cannot open serial port for a non-serial descriptor: " << descriptor.description(); } logDebug() << "Try to open " << descriptor.description() << "."; QSerialPortInfo port(descriptor.device().toString()); this->setPort(port); if (! setParity(QSerialPort::NoParity)) { logWarn() << "Cannot set parity of the serial port to none."; } if (! setStopBits(QSerialPort::OneStop)) { logWarn() << "Cannot set stop bit."; } if (! setBaudRate(rate)) { logWarn() << "Cannot set speed to " << rate << " baud."; } if (! setFlowControl(QSerialPort::HardwareControl)) { logWarn() << "Cannot enable hardware flow control."; } if (! this->open(QIODevice::ReadWrite)) { #ifdef Q_OS_UNIX QFileInfo portFileInfo(port.systemLocation()); if (portFileInfo.exists() && ((! portFileInfo.isReadable()) || (! portFileInfo.isWritable()))) { QString owner = QString(portFileInfo.permission(QFileDevice::ReadOwner) ? "r" : "-") + (portFileInfo.permission(QFile::WriteOwner) ? "w" : "-") + (portFileInfo.permission(QFile::ExeOwner) ? "x" : "-"); QString group = QString(portFileInfo.permission(QFileDevice::ReadGroup) ? "r" : "-") + (portFileInfo.permission(QFile::WriteGroup) ? "w" : "-") + (portFileInfo.permission(QFile::ExeGroup) ? "x" : "-"); QString other = QString(portFileInfo.permission(QFileDevice::ReadOther) ? "r" : "-") + (portFileInfo.permission(QFile::WriteOther) ? "w" : "-") + (portFileInfo.permission(QFile::ExeOther) ? "x" : "-"); errMsg(err) << "Insufficient rights to read or write '" << port.systemLocation() << "' (" << port.description() << "): " << portFileInfo.owner() << ": " << owner << ", " << portFileInfo.group() << ": " << group << ", other: " << other << "."; if (portFileInfo.permission(QFile::ReadGroup | QFile::WriteGroup)) errMsg(err) << "A membership in the group " << portFileInfo.group() << " would grant access."; } #endif errMsg(err) << "Cannot open serial port '" << port.portName() << "': " << this->errorString() << "."; return; } logDebug() << "Opened serial port " << this->portName() << " with " << this->baudRate() << "baud."; connect(this, SIGNAL(aboutToClose()), this, SLOT(onClose())); connect(this, SIGNAL(errorOccurred(QSerialPort::SerialPortError)), this, SLOT(onError(QSerialPort::SerialPortError))); connect(this, SIGNAL(dataTerminalReadyChanged(bool)), this, SLOT(signalingChanged())); connect(this, SIGNAL(requestToSendChanged(bool)), this, SLOT(signalingChanged())); } USBSerial::~USBSerial() { if (isOpen()) close(); } bool USBSerial::isOpen() const { return QSerialPort::isOpen(); } void USBSerial::close() { if (isOpen()) QSerialPort::close(); } void USBSerial::onError(QSerialPort::SerialPortError err) { logError() << "Serial port error: (" << err << ") " << errorString() << "."; } void USBSerial::onClose() { logDebug() << "Serial port will close now."; } void USBSerial::signalingChanged() { logDebug() << "Pinout signals changed to " << formatPinoutSignals() << "."; } QList USBSerial::detect(uint16_t vid, uint16_t pid, bool isSave) { QList interfaces; // Find matching serial port by VID/PID. logDebug() << "Search for serial port with matching VID:PID " << QString::number(vid, 16) << ":" << QString::number(pid, 16) << "."; QList ports = QSerialPortInfo::availablePorts(); foreach (QSerialPortInfo port, ports) { if (port.hasProductIdentifier() && (pid == port.productIdentifier()) && port.hasVendorIdentifier() && (vid == port.vendorIdentifier())) { interfaces.append(Descriptor(vid, pid, port.portName(), isSave)); logDebug() << "Found " << port.portName() << " (USB " << QString::number(vid, 16) << ":" << QString::number(pid, 16) << ")."; } } return interfaces; } QList USBSerial::detect() { QList interfaces; // Find matching serial port by VID/PID. logDebug() << "Search for serial ports."; QList ports = QSerialPortInfo::availablePorts(); foreach (QSerialPortInfo port, ports) { if (port.hasProductIdentifier() && port.hasVendorIdentifier()) { interfaces.append(Descriptor(port.vendorIdentifier(), port.productIdentifier(), port.portName(), false)); logDebug() << "Found " << port.portName() << " (USB " << QString::number(port.vendorIdentifier(), 16) << ":" << QString::number(port.productIdentifier(), 16) << ")."; } } return interfaces; } QString USBSerial::formatPinoutSignals() { if (QSerialPort::NoSignal == pinoutSignals()) return "None"; QStringList res; if (QSerialPort::TransmittedDataSignal & pinoutSignals()) res.append("Transitter Detected"); if (QSerialPort::ReceivedDataSignal & pinoutSignals()) res.append("Received Data"); if (QSerialPort::DataTerminalReadySignal & pinoutSignals()) res.append("Data Terminal Ready"); if (QSerialPort::DataCarrierDetectSignal & pinoutSignals()) res.append("Data Carrier Detect"); if (QSerialPort::DataSetReadySignal & pinoutSignals()) res.append("Data Set Ready"); if (QSerialPort::RingIndicatorSignal & pinoutSignals()) res.append("Ring Indicator"); if (QSerialPort::RequestToSendSignal & pinoutSignals()) res.append("Request To Send"); if (QSerialPort::ClearToSendSignal & pinoutSignals()) res.append("Clear To Send"); if (QSerialPort::SecondaryTransmittedDataSignal & pinoutSignals()) res.append("Secondary Transmitted Data"); if (QSerialPort::SecondaryReceivedDataSignal & pinoutSignals()) res.append("Secondary Received Data"); return res.join(", "); } qdmr-0.12.3/lib/usbserial.hh000066400000000000000000000037421501654372000156310ustar00rootroot00000000000000#ifndef USBSERIAL_HH #define USBSERIAL_HH #include #include #include "radiointerface.hh" #include "errorstack.hh" /** Implements a serial connection to a radio via USB. * * The correct serial port is selected by the given VID and PID to the constructor. * * @ingroup rif */ class USBSerial : public QSerialPort, public RadioInterface { Q_OBJECT public: /** Specialization of radio interface info for serial ports. */ class Descriptor: public USBDeviceDescriptor { public: /** Constructor from VID, PID and device path. */ Descriptor(uint16_t vid, uint16_t pid, const QString &device, bool isSave=false); }; protected: /** Constructs an opens new serial interface to the devices identified by the given vendor and * product IDs. * @param descriptor Specifies the device to open. * @param rate Specifies the transferrate in baud. * @param err The error stack, messages are put onto. * @param parent Specifies the parent object. */ explicit USBSerial(const USBDeviceDescriptor &descriptor, QSerialPort::BaudRate rate=QSerialPort::Baud115200, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr); public: /** Destructor. */ virtual ~USBSerial(); /** If @c true, the device has been found and is open. */ bool isOpen() const; /** Closes the interface to the device. */ void close(); public: /** Searches for all USB serial ports with the specified VID/PID. */ static QList detect(uint16_t vid, uint16_t pid, bool isSave=true); /** Searches for all USB serial ports */ static QList detect(); protected slots: /** Callback for serial interface errors. */ void onError(QSerialPort::SerialPortError error_t); /** Callback when closing interface. */ void onClose(); /** Signaling callback. */ void signalingChanged(); protected: /** Serializes the pinout singals. */ QString formatPinoutSignals(); }; #endif // USBSERIAL_HH qdmr-0.12.3/lib/userdatabase.cc000066400000000000000000000157301501654372000162710ustar00rootroot00000000000000#include "userdatabase.hh" #include #include #include #include #include #include #include #include "logger.hh" #include /* ********************************************************************************************* * * Implementation of User * ********************************************************************************************* */ UserDatabase::User::User() : id(0) { // pass... } UserDatabase::User::User(const QJsonObject &obj) : id(obj.value("id").toInt()), call(obj.value("callsign").toString()), name(obj.value("fname").toString()), surname(obj.value("surname").toString()), city(obj.value("city").toString()), state(obj.value("state").toString()), country(obj.value("country").toString()), comment(obj.value("remarks").toString()) { // pass... } unsigned UserDatabase::User::distance(unsigned id) const { // Fix number of digits int a = this->id, b = id; int ad = std::ceil(std::log10(a)); int bd = std::ceil(std::log10(b)); if (ad > bd) b *= std::pow(10u, (ad-bd)); else if (bd > ad) a *= std::pow(10u, (bd-ad)); // Distance is just the difference between these two numbers // this ensures a small distance between two numbers with the same // prefix. return std::abs(a-b); } /* ********************************************************************************************* * * Implementation of UserDatabase * ********************************************************************************************* */ UserDatabase::UserDatabase(unsigned updatePeriodDays, QObject *parent) : QAbstractTableModel(parent), _user(), _network() { connect(&_network, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadFinished(QNetworkReply*))); if ((! load()) || (updatePeriodDays < dbAge())) download(); } qint64 UserDatabase::count() const { return _user.size(); } bool UserDatabase::load() { QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); return load(path+"/user.json"); } const UserDatabase::User & UserDatabase::user(int idx) const { return _user[idx]; } bool UserDatabase::load(const QString &filename) { QFile file(filename); if (! file.open(QIODevice::ReadOnly)) { QString msg = QString("Cannot open user list '%1': %2").arg(filename).arg(file.errorString()); logError() << msg; emit error(msg); return false; } QByteArray data = file.readAll(); file.close(); QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (doc.isEmpty()) { QString msg = "Failed to load user DB: " + err.errorString(); logError() << msg; emit error(msg); return false; } if (! doc.isObject()) { QString msg = "Failed to load user DB: JSON document is not an object!"; logError() << msg; emit error(msg); return false; } if (! doc.object().contains("users")) { QString msg = "Failed to load user DB: JSON object does not contain 'users' item."; logError() << msg; emit error(msg); return false; } if (! doc.object()["users"].isArray()) { QString msg = "Failed to load user DB: 'users' item is not an array."; logError() << msg; emit error(msg); return false; } beginResetModel(); _user.clear(); QJsonArray array = doc.object()["users"].toArray(); _user.reserve(array.size()); for (int i=0; i &ids) { if (0 == ids.count()) return; // Sort repeater w.r.t. distance to each ID std::stable_sort(_user.begin(), _user.end(), [ids](const User &a, const User &b){ QSet::const_iterator id=ids.begin(); unsigned min_a = a.distance(*id), min_b = b.distance(*id); id++; for (; id!=ids.end(); id++) { min_a = std::min(min_a, a.distance(*id)); min_b = std::min(min_b, b.distance(*id)); } return min_a < min_b; }); } void UserDatabase::download() { QUrl url("https://database.radioid.net/static/users.json"); QNetworkRequest request(url); _network.get(request); } void UserDatabase::downloadFinished(QNetworkReply *reply) { if (reply->error()) { QString msg = QString("Cannot download user database: %1").arg(reply->errorString()); logError() << msg; emit error(msg); return; } QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QFile file(path+"/user.json"); QDir directory; if ((! directory.exists(path)) && (!directory.mkpath(path))) { QString msg = QString("Cannot create path '%1'.").arg(path); logError() << msg; emit error(msg); return; } if (! file.open(QIODevice::WriteOnly)) { QString msg = QString("Cannot save user database at '%1'.").arg(path+"/user.json"); logError() << msg; emit error(msg); return; } file.write(reply->readAll()); file.flush(); file.close(); load(); reply->deleteLater(); } unsigned UserDatabase::dbAge() const { QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/user.json"; QFileInfo info(path); if (! info.exists()) return -1; return info.lastModified().daysTo(QDateTime::currentDateTime()); } int UserDatabase::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return _user.size(); } int UserDatabase::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3; } QVariant UserDatabase::data(const QModelIndex &index, int role) const { if ((Qt::EditRole != role) && ((Qt::DisplayRole != role))) return QVariant(); if (index.row() >= _user.size()) return QVariant(); if (0 == index.column()) { // Call if (Qt::DisplayRole == role) { if (_user[index.row()].surname.isEmpty()) { if (_user[index.row()].name.isEmpty()) { return _user[index.row()].call; } else { return tr("%1 (%2)") .arg(_user[index.row()].call) .arg(_user[index.row()].name); } } else { return tr("%1 (%2, %3)") .arg(_user[index.row()].call) .arg(_user[index.row()].name) .arg(_user[index.row()].surname); } } else { return _user[index.row()].call; } } else if (1 == index.column()) { // ID return _user[index.row()].id; } else if (2 == index.column()) { // Country return _user[index.column()].country; } return QVariant(); } qdmr-0.12.3/lib/userdatabase.hh000066400000000000000000000072361501654372000163050ustar00rootroot00000000000000#ifndef USERDATABASE_HH #define USERDATABASE_HH #include #include #include #include #include #include #include #include /** Auto-updating DMR user database. * * This class represents the complete DMR user database. The user database gets downloaded from * https://www.radioid.net/static/users.json and kept up-to-date by re-downloading it * periodically (by default every 30 days). This user database gets used in the GUI application * to help assemble private call contacts and to assemble so-called CSV callsign databases, that * are programmable to some DMR radios to resolve the DMR ID to callsigns and names. * * @ingroup util */ class UserDatabase : public QAbstractTableModel { Q_OBJECT public: /** Represents the user information within the @c UserDatabase. */ class User { public: /** Empty constructor. */ User(); /** Constructs entry from JSON object. */ User(const QJsonObject &obj); /** Returns @c true if the entry is valid. */ inline bool isValid() const { return 0 != id; } /** Returns the "distance" between this user and the given ID. */ unsigned distance(unsigned id) const; /** The DMR ID of the user. */ unsigned id; /** The callsign of the user. */ QString call; /** The name of the user. */ QString name; /** The surname of the user. */ QString surname; /** The city of the user. */ QString city; /** The state of the user. */ QString state; /** The country of the user. */ QString country; /** Some arbitrary comment or text. */ QString comment; }; public: /** Constructs the user-database. * The constructor will download the current user database if it was not downloaded yet or * if the downloaded version is older than @c updatePeriodDays days. */ explicit UserDatabase(unsigned updatePeriodDays=30, QObject *parent=nullptr); /** Returns the number of users. */ qint64 count() const; /** Loads all entries from the downloaded user database. */ bool load(); /** Loads all entries from the downloaded user database at the specified location. */ bool load(const QString &filename); /** Sorts users with respect to the distance to the given ID. */ void sortUsers(unsigned id); /** Sorts users with respect to the minimum distance to the given IDs. */ void sortUsers(const QSet &ids); /** Returns the user with index @c idx. */ const User &user(int idx) const; /** Returns the age of the database in days. */ unsigned dbAge() const; /** Implements the QAbstractTableModel interface, returns the number of rows (number of entries). */ int rowCount(const QModelIndex &parent=QModelIndex()) const; /** Implements the QAbstractTableModel interface, returns the number of columns. */ int columnCount(const QModelIndex &parent=QModelIndex()) const; /** Implements the QAbstractTableModel interface, return the entry data. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; signals: /** Gets emitted once the call-sign database has been loaded. */ void loaded(); /** Gets emitted if the loading of the call-sign database fails. */ void error(const QString &msg); public slots: /** Starts the download of the user database. */ void download(); private slots: /** Gets called whenever the download is complete. */ void downloadFinished(QNetworkReply *reply); private: /** Holds all users sorted by their ID. */ QVector _user; /** The network access used for downloading. */ QNetworkAccessManager _network; }; #endif // USERDATABASE_HH qdmr-0.12.3/lib/utils.cc000066400000000000000000000416171501654372000147710ustar00rootroot00000000000000#include "utils.hh" #include #include #include #include #include #include // Maps APRS icon number to code-char static QVector aprsIconCodeTable{ '!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/','0', '1','2','3','4','5','6', '7','8','9',':',';','<','=','>','?','@', 'A','B','C','D','E','F', 'G','H','I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V', 'W','X','Y','Z','[','/',']','^','_','`', 'a','b','c','d','e','f', 'g','h','i','j','k','l','m','n','o','p', 'q','r','s','t','u','v','w','x','y','z','{','|','}','~'}; static QHash aprsIconNameTable{ {(unsigned)APRSSystem::Icon::None, ""}, {(unsigned)APRSSystem::Icon::PoliceStation, "Police station"}, {(unsigned)APRSSystem::Icon::Digipeater, "Digipeater"}, {(unsigned)APRSSystem::Icon::Phone, "Phone"}, {(unsigned)APRSSystem::Icon::DXCluster, "DX cluster"}, {(unsigned)APRSSystem::Icon::HFGateway, "HF gateway"}, {(unsigned)APRSSystem::Icon::SmallPlane, "Small plane"}, {(unsigned)APRSSystem::Icon::MobileSatelliteStation, "Mobile Satellite station"}, {(unsigned)APRSSystem::Icon::WheelChair, "Wheel chair"}, {(unsigned)APRSSystem::Icon::Snowmobile, "Snowmobile"}, {(unsigned)APRSSystem::Icon::RedCross, "Red cross"}, {(unsigned)APRSSystem::Icon::BoyScout, "Boy scout"}, {(unsigned)APRSSystem::Icon::Home, "Home"}, {(unsigned)APRSSystem::Icon::X, "X"}, {(unsigned)APRSSystem::Icon::RedDot, "Red dot"}, {(unsigned)APRSSystem::Icon::Circle0, "Circle 0"}, {(unsigned)APRSSystem::Icon::Circle1, "Circle 1"}, {(unsigned)APRSSystem::Icon::Circle2, "Circle 2"}, {(unsigned)APRSSystem::Icon::Circle3, "Circle 3"}, {(unsigned)APRSSystem::Icon::Circle4, "Circle 4"}, {(unsigned)APRSSystem::Icon::Circle5, "Circle 5"}, {(unsigned)APRSSystem::Icon::Circle6, "Circle 6"}, {(unsigned)APRSSystem::Icon::Circle7, "Circle 7"}, {(unsigned)APRSSystem::Icon::Circle8, "Circle 8"}, {(unsigned)APRSSystem::Icon::Circle9, "Circle 9"}, {(unsigned)APRSSystem::Icon::Fire, "Fire"}, {(unsigned)APRSSystem::Icon::Campground, "Campground"}, {(unsigned)APRSSystem::Icon::Motorcycle, "Motorcycle"}, {(unsigned)APRSSystem::Icon::RailEngine, "Rail engine"}, {(unsigned)APRSSystem::Icon::Car, "Car"}, {(unsigned)APRSSystem::Icon::FileServer, "File server"}, {(unsigned)APRSSystem::Icon::HCFuture, "HC future"}, {(unsigned)APRSSystem::Icon::AidStation, "Aid station"}, {(unsigned)APRSSystem::Icon::BBS, "BBS"}, {(unsigned)APRSSystem::Icon::Canoe, "Canoe"}, {(unsigned)APRSSystem::Icon::Eyeball, "Eyeball"}, {(unsigned)APRSSystem::Icon::Tractor, "Tractor"}, {(unsigned)APRSSystem::Icon::GridSquare, "Grid square"}, {(unsigned)APRSSystem::Icon::Hotel, "Hotel"}, {(unsigned)APRSSystem::Icon::TCPIP, "TCP/IP"}, {(unsigned)APRSSystem::Icon::School, "School"}, {(unsigned)APRSSystem::Icon::Logon, "Logon"}, {(unsigned)APRSSystem::Icon::MacOS, "MacOS"}, {(unsigned)APRSSystem::Icon::NTSStation, "NTS station"}, {(unsigned)APRSSystem::Icon::Balloon, "Balloon"}, {(unsigned)APRSSystem::Icon::PoliceCar, "Police car"}, {(unsigned)APRSSystem::Icon::TBD, "TBD"}, {(unsigned)APRSSystem::Icon::RV, "RV"}, {(unsigned)APRSSystem::Icon::Shuttle, "Shuttle"}, {(unsigned)APRSSystem::Icon::SSTV, "SSTV"}, {(unsigned)APRSSystem::Icon::Bus, "Bus"}, {(unsigned)APRSSystem::Icon::ATV, "ATV"}, {(unsigned)APRSSystem::Icon::WXService, "WX service"}, {(unsigned)APRSSystem::Icon::Helo, "Helo"}, {(unsigned)APRSSystem::Icon::Yacht, "Yacht"}, {(unsigned)APRSSystem::Icon::Windows, "Windows"}, {(unsigned)APRSSystem::Icon::Jogger, "Jogger"}, {(unsigned)APRSSystem::Icon::Triangle, "Triangle"}, {(unsigned)APRSSystem::Icon::PBBS, "PBBS"}, {(unsigned)APRSSystem::Icon::LargePlane, "Large plane"}, {(unsigned)APRSSystem::Icon::WXStation, "WX station"}, {(unsigned)APRSSystem::Icon::DishAntenna, "Dish antenna"}, {(unsigned)APRSSystem::Icon::Ambulance, "Ambulance"}, {(unsigned)APRSSystem::Icon::Bike, "Bike"}, {(unsigned)APRSSystem::Icon::ICP, "ICP"}, {(unsigned)APRSSystem::Icon::FireStation, "Fire station"}, {(unsigned)APRSSystem::Icon::Horse, "Horse"}, {(unsigned)APRSSystem::Icon::FireTruck, "Fire truck"}, {(unsigned)APRSSystem::Icon::Glider, "Glider"}, {(unsigned)APRSSystem::Icon::Hospital, "Hospital"}, {(unsigned)APRSSystem::Icon::IOTA, "IOTA"}, {(unsigned)APRSSystem::Icon::Jeep, "Jeep"}, {(unsigned)APRSSystem::Icon::SmallTruck, "Small truck"}, {(unsigned)APRSSystem::Icon::Laptop, "Laptop"}, {(unsigned)APRSSystem::Icon::MicE, "Mic-E"}, {(unsigned)APRSSystem::Icon::Node, "Node"}, {(unsigned)APRSSystem::Icon::EOC, "EOC"}, {(unsigned)APRSSystem::Icon::Rover, "Rover"}, {(unsigned)APRSSystem::Icon::Grid, "Grid"}, {(unsigned)APRSSystem::Icon::Antenna, "Antenna"}, {(unsigned)APRSSystem::Icon::PowerBoat, "Power boat"}, {(unsigned)APRSSystem::Icon::TruckStop, "Truck stop"}, {(unsigned)APRSSystem::Icon::TruckLarge, "Truck large"}, {(unsigned)APRSSystem::Icon::Van, "Van"}, {(unsigned)APRSSystem::Icon::Water, "Water"}, {(unsigned)APRSSystem::Icon::XAPRS, "XAPRS"}, {(unsigned)APRSSystem::Icon::Yagi, "Yagi"}, {(unsigned)APRSSystem::Icon::Shelter, "Shelter"}}; QString decode_unicode(const uint16_t *data, size_t size, uint16_t fill) { QString res; res.reserve(size); for (size_t i=0; (i> 28) & 0xf) + 1e1 * ((bcd >> 24) & 0xf) + 1.0 * ((bcd >> 20) & 0xf) + 1e-1 * ((bcd >> 16) & 0xf) + 1e-2 * ((bcd >> 12) & 0xf) + 1e-3 * ((bcd >> 8) & 0xf) + 1e-4 * ((bcd >> 4) & 0xf) + 1e-5 * ((bcd >> 0) & 0xf); return freq; } uint32_t encode_frequency(double freq) { uint32_t hz = std::round(freq * 1e6); uint32_t a = (hz / 100000000) % 10; uint32_t b = (hz / 10000000) % 10; uint32_t c = (hz / 1000000) % 10; uint32_t d = (hz / 100000) % 10; uint32_t e = (hz / 10000) % 10; uint32_t f = (hz / 1000) % 10; uint32_t g = (hz / 100) % 10; uint32_t h = (hz / 10) % 10; return (a << 28) + (b << 24) + (c << 20) + (d << 16) + (e << 12) + (f << 8) + (g << 4) + h; } uint32_t decode_dmr_id_bin(const uint8_t *id) { return ( (id[0]) | (id[1] << 8) | (id[2] << 16) ); } void encode_dmr_id_bin(uint8_t *id, uint32_t no) { id[0] = no; id[1] = no >> 8; id[2] = no >> 16; } uint32_t decode_dmr_id_bcd(const uint8_t *id) { return ((id[0] >> 4) * 10000000 + (id[0] & 15) * 1000000 + (id[1] >> 4) * 100000 + (id[1] & 15) * 10000 + (id[2] >> 4) * 1000 + (id[2] & 15) * 100 + (id[3] >> 4) * 10 + (id[3] & 15)); } uint32_t decode_dmr_id_bcd_le(const uint8_t *id) { return ((id[3] >> 4) * 10000000 + (id[3] & 15) * 1000000 + (id[2] >> 4) * 100000 + (id[2] & 15) * 10000 + (id[1] >> 4) * 1000 + (id[1] & 15) * 100 + (id[0] >> 4) * 10 + (id[0] & 15)); } void encode_dmr_id_bcd(uint8_t *id, uint32_t no) { id[0] = ((no / 10000000) << 4) | ((no / 1000000) % 10); id[1] = ((no / 100000 % 10) << 4) | ((no / 10000) % 10); id[2] = ((no / 1000 % 10) << 4) | ((no / 100) % 10); id[3] = ((no / 10 % 10) << 4) | (no % 10); } void encode_dmr_id_bcd_le(uint8_t *id, uint32_t no) { id[3] = ((no / 10000000) << 4) | ((no / 1000000) % 10); id[2] = ((no / 100000 % 10) << 4) | ((no / 10000) % 10); id[1] = ((no / 1000 % 10) << 4) | ((no / 100) % 10); id[0] = ((no / 10 % 10) << 4) | (no % 10); } QVector bin_dtmf_tab = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','*','#'}; QString decode_dtmf_bin(const uint8_t *num, int size, uint8_t fill) { Q_UNUSED(fill); QString number; for (int i=0; (i=tmp.size()) continue; int idx = bin_dtmf_tab.indexOf(number.at(i).toLatin1()); if (idx<0) continue; num[i] = idx; } return true; } QString decode_dtmf_bcd_be(const uint8_t *num, int digits) { QString number; for (int i=0; i>4)&0xf) : ((num[i/2])&0xf); number.append(bin_dtmf_tab[d]); } return number; } bool encode_dtmf_bcd_be(const QString &number, uint8_t *num, int size, uint8_t fill) { memset(num, fill, size); QString tmp = number.simplified().toUpper(); for (int i=0; i> 14; unsigned a = (data >> 12) & 3; unsigned b = (data >> 8) & 15; unsigned c = (data >> 4) & 15; unsigned d = data & 15; switch (tag) { case 2: // DCS Normal return SelectiveCall(100*b+10*c+1*d, false); case 3: // DCS Inverted return SelectiveCall(100*b+10*c+1*d, true); default: break; } // CTCSS return SelectiveCall(100.0*a+10.0*b+1.0*c+0.1*d); } uint16_t encode_ctcss_tone_table(const SelectiveCall &code) { unsigned tag=0xff, a=0xf, b=0xf, c=0xf, d=0xf; // Disabled if (code.isInvalid()) return 0xffff; if (code.isCTCSS()) { // CTCSS tone tag = 0; unsigned val = code.Hz() * 10.0 + 0.5; a = val / 1000; b = (val / 100) % 10; c = (val / 10) % 10; d = val % 10; } else if (code.isDCS()) { // DCS normal if (code.isInverted()) tag = 3; else tag = 2; unsigned val = code.octalCode(); a = 0; b = (val / 100) % 10; c = (val / 10) % 10; d = val % 10; } return (a << 12) | (b << 8) | (c << 4) | d | (tag << 14); } bool validDMRNumber(const QString &text) { return QRegExp("^[0-9]+$").exactMatch(text); } bool validDTMFNumber(const QString &text) { return QRegExp("^[0-9a-dA-D\\*#]+$").exactMatch(text); } QString aprsicon2config(APRSSystem::Icon icon) { if ((APRSSystem::Icon::None == icon) || (! aprsIconCodeTable.contains(unsigned(icon)))) return "-"; return QString("\"%1\"").arg(aprsIconNameTable.value((unsigned)icon)); } QString aprsicon2name(APRSSystem::Icon icon) { if ((APRSSystem::Icon::None == icon) || (! aprsIconCodeTable.contains(unsigned(icon)))) return ""; return aprsIconNameTable.value((unsigned)icon); } APRSSystem::Icon name2aprsicon(const QString &name) { if (name.isEmpty()) return APRSSystem::Icon::None; APRSSystem::Icon icon = APRSSystem::Icon::None; int best = levDist(name, ""); QHash::const_iterator item=aprsIconNameTable.constBegin(); for(; item != aprsIconNameTable.constEnd(); item++) { int dist = levDist(name, item.value()); if (dist < best) { icon = (APRSSystem::Icon)item.key(); best = dist; } } return icon; } char aprsicon2iconcode(APRSSystem::Icon icon) { unsigned num = unsigned(APRSSystem::ICON_MASK & unsigned(icon)); if (num >= unsigned(aprsIconCodeTable.size())) return '"'; return aprsIconCodeTable[num]; } char aprsicon2tablecode(APRSSystem::Icon icon) { unsigned tab = (APRSSystem::TABLE_MASK & unsigned(icon)); switch (tab) { case APRSSystem::SECONDARY_TABLE: return '\\'; case APRSSystem::PRIMARY_TABLE: return '/'; } return '/'; } APRSSystem::Icon code2aprsicon(char table, char icon) { unsigned num = (APRSSystem::ICON_MASK & unsigned(APRSSystem::Icon::None)); if (aprsIconCodeTable.contains(icon)) num = aprsIconCodeTable.indexOf(icon); if ('/' == table) num = num | APRSSystem::PRIMARY_TABLE; else if ('\\' == table) num = num | APRSSystem::PRIMARY_TABLE; return APRSSystem::Icon(num); } int levDist(const QString &source, const QString &target, Qt::CaseSensitivity cs) { // Mostly stolen from https://qgis.org/api/2.14/qgsstringutils_8cpp_source.html if (0 == QString::compare(source,target, cs)) { return 0; } const int sourceCount = source.count(); const int targetCount = target.count(); if (source.isEmpty()) return targetCount; if (target.isEmpty()) return sourceCount; if (sourceCount > targetCount) return levDist(target, source, cs); QVector column; column.fill(0, targetCount + 1); QVector previousColumn; previousColumn.reserve(targetCount + 1); for (int i = 0; i < targetCount + 1; i++) previousColumn.append(i); for (int i = 0; i < sourceCount; i++) { column[0] = i + 1; for (int j = 0; j < targetCount; j++) { column[j + 1] = std::min( { 1 + column.at(j), 1 + previousColumn.at(1 + j), previousColumn.at(j) + (QString::compare(source.at(i),target.at(j), cs) ? 1 : 0) }); } column.swap(previousColumn); } return previousColumn.at(targetCount); } uint32_t align_size(uint32_t size, uint32_t block) { if (0 == (size % block)) return size; return (size + (block - (size%block))); } uint32_t align_addr(uint32_t addr, uint32_t block) { if (0 == (addr % block)) return addr; return (addr - (addr%block)); } QGeoCoordinate loc2deg(const QString &loc) { double lon = 0, lat = 0, dlon = 20, dlat = 10; if (2 > loc.size()) return QGeoCoordinate(); QChar l = loc[0].toUpper(); QChar c = loc[1].toUpper(); lon += double(int(l.toLatin1())-'A')*dlon; lat += double(int(c.toLatin1())-'A')*dlat; if (4 > loc.size()) { lon = lon - 180; lat = lat - 90; // Offset places coordinate in the middle of the square return QGeoCoordinate(lat+dlat/2, lon+dlon/2); } dlon /= 10; dlat /= 10; l = loc[2].toUpper(); c = loc[3].toUpper(); lon += double(int(l.toLatin1())-'0')*dlon; lat += double(int(c.toLatin1())-'0')*dlat; if (6 > loc.size()){ lon = lon - 180; lat = lat - 90; // Offset places coordinate in the middle of the square return QGeoCoordinate(lat+dlat/2, lon+dlon/2); } dlon /= 24; dlat /= 24; l = loc[4].toUpper(); c = loc[5].toUpper(); lon += double(int(l.toLatin1())-'A')*dlon; lat += double(int(c.toLatin1())-'A')*dlat; if (8 > loc.size()) { lon = lon - 180; lat = lat - 90; // Offset places coordinate in the middle of the square return QGeoCoordinate(lat+dlat/2, lon+dlon/2); } dlon /= 10; dlat /= 10; l = loc[6].toUpper(); c = loc[7].toUpper(); lon += double(int(l.toLatin1())-'0')*dlon; lat += double(int(c.toLatin1())-'0')*dlat; if (10 > loc.size()) { lon = lon - 180; lat = lat - 90; // Offset places coordinate in the middle of the square return QGeoCoordinate(lat+dlat/2, lon+dlon/2); } dlon /= 24; dlat /= 24; l = loc[8].toUpper(); c = loc[9].toUpper(); lon += double(int(l.toLatin1())-'A')*dlon; lat += double(int(c.toLatin1())-'A')*dlat; lon = lon - 180; lat = lat - 90; // Offset places coordinate in the middle of the square return QGeoCoordinate(lat+dlat/2, lon+dlon/2); } QString deg2loc(const QGeoCoordinate &coor, unsigned int size) { QString loc; double lon = (coor.longitude()+180)/360; double lat = (coor.latitude()+90)/180; size += (size % 2); if (2 > size) return loc; lon *= 18; lat *= 18; char l = lon; lon -= l; char c = lat; lat -= c; loc.append(l+'A'); loc.append(c+'A'); if (4 > size) return loc; lon *= 10; lat *= 10; l = lon; lon -= l; c = lat; lat -= c; loc.append(l+'0'); loc.append(c+'0'); if (6 > size) return loc; lon *= 24; lat *= 24; l = lon; lon -= l; c = lat; lat -= c; loc.append(l+'a'); loc.append(c+'a'); if (8 > size) return loc; lon *= 10; lat *= 10; l = lon; lon -= l; c = lat; lat -= c; loc.append(l+'0'); loc.append(c+'0'); if (10 > size) return loc; lon *= 24; lat *= 24; l = lon; //lon -= l; c = lat; //lat -= c; loc.append(l+'a'); loc.append(c+'a'); return loc; } qdmr-0.12.3/lib/utils.hh000066400000000000000000000104201501654372000147670ustar00rootroot00000000000000/** @defgroup util Utility functions and classes. * This module collects all utility functions and classes. That is, functions to encode some * data and also classes implementing the DFU file format. */ #ifndef UTILS_HH #define UTILS_HH #include #include #include "signaling.hh" #include "gpssystem.hh" #include /** Decodes the unicode string stored in @c data of size @c size. The @c fill code also defines the * end-of-string symbol. * @returns The decoded string. */ QString decode_unicode(const uint16_t *data, size_t size, uint16_t fill=0x0000); /** Encodes the string @c text as unicode and stores the result into @c data using up-to @c size * 16bit words in data. The @c fill word specifies the fill and end-of-string word. */ void encode_unicode(uint16_t *data, const QString &text, size_t size, uint16_t fill=0x0000); /** Decodes the ascii string in @c data into a @c QString of up-to size length. The @c fill word * specifies the fill and end-of-string word. */ QString decode_ascii(const uint8_t *data, size_t size, uint16_t fill=0x00); /** Encodes the given QString @c text of up-to size length as ASCII into @c data using the * @c fill word as fill and end-of-string word. */ void encode_ascii(uint8_t *data, const QString &text, size_t size, uint16_t fill=0x00); /** Decodes the UTF-8 string in @c data into a @c QString of up-to size length. The @c fill word * specifies the fill and end-of-string word. */ QString decode_utf8(const uint8_t *data, size_t size, uint16_t fill=0x00); /** Encodes the given QString @c text of up-to size length as UTF-8 into @c data using the * @c fill word as fill and end-of-string word. */ void encode_utf8(uint8_t *data, const QString &text, size_t size, uint16_t fill=0x00); /** Decodes an 8 digit BCD encoded frequency (in MHz). */ double decode_frequency(uint32_t bcd); /** Eecodes an 8 digit BCD encoded frequency (in MHz). */ uint32_t encode_frequency(double freq); /** Decodes binary (24bit) encoded DMR ID. */ uint32_t decode_dmr_id_bin(const uint8_t *id); /** Encodes binary (24bit) encoded DMR ID. */ void encode_dmr_id_bin(uint8_t *id, uint32_t num); /** Decodes bcd (32bit) encoded DMR ID, little endian. */ uint32_t decode_dmr_id_bcd(const uint8_t *id); /** Decodes bcd (32bit) encoded DMR ID, big endian. */ uint32_t decode_dmr_id_bcd_le(const uint8_t *id); /** Encodes bcd (32bit) encoded DMR ID, little endian. */ void encode_dmr_id_bcd(uint8_t *id, uint32_t num); /** Encodes bcd (32bit) encoded DMR ID, big endian. */ void encode_dmr_id_bcd_le(uint8_t *id, uint32_t num); QString decode_dtmf_bin(const uint8_t *num, int size=16, uint8_t fill=0xff); bool encode_dtmf_bin(const QString &number, uint8_t *num, int size=16, uint8_t fill=0xff); QString decode_dtmf_bcd_be(const uint8_t *num, int digits); bool encode_dtmf_bcd_be(const QString &number, uint8_t *num, int size, uint8_t fill); /** Decodes the CTCSS tone or DCS code to @c Signaling::Code. * @todo TyT specific, move to TyT codeplug. */ SelectiveCall decode_ctcss_tone_table(uint16_t data); /** Encodes the CTCSS tone or DCS code from @c Signaling::Code. * @todo TyT specific, move to TyT codeplug. */ uint16_t encode_ctcss_tone_table(const SelectiveCall &code); /** Validates a DMR ID number. */ bool validDMRNumber(const QString &text); /** Validates a DTMF number. */ bool validDTMFNumber(const QString &text); QString aprsicon2config(APRSSystem::Icon icon); QString aprsicon2name(APRSSystem::Icon icon); APRSSystem::Icon name2aprsicon(const QString &name); char aprsicon2iconcode(APRSSystem::Icon icon); char aprsicon2tablecode(APRSSystem::Icon icon); APRSSystem::Icon code2aprsicon(char table, char icon); /** Implements the Levenshtein distance between two strings. * That is, the number of edits (insert, delete or replace operations) needed to turn source * into target. */ int levDist(const QString &source, const QString &target, Qt::CaseSensitivity cs=Qt::CaseInsensitive); /** Increases the given size to be aligned with the given block size. */ uint32_t align_size(uint32_t size, uint32_t block); /** Decreases the address to be aligned with the given block size. */ uint32_t align_addr(uint32_t addr, uint32_t block); QGeoCoordinate loc2deg(const QString &loc); QString deg2loc(const QGeoCoordinate &coor, unsigned int size=6); #endif // UTILS_HH qdmr-0.12.3/lib/uv390.cc000066400000000000000000000020211501654372000145010ustar00rootroot00000000000000#include "uv390.hh" #include "uv390_limits.hh" RadioLimits *UV390::_limits = nullptr; UV390::UV390(TyTInterface *device, QObject *parent) : TyTRadio(device, parent), _name("TyT MD-UV390") { // pass... } UV390::~UV390() { // pass... } const QString & UV390::name() const { return _name; } const RadioLimits & UV390::limits() const { if (nullptr == _limits) _limits = new UV390Limits(); return *_limits; } const Codeplug & UV390::codeplug() const { return _codeplug; } Codeplug & UV390::codeplug() { return _codeplug; } const CallsignDB * UV390::callsignDB() const { return &_callsigndb; } CallsignDB * UV390::callsignDB() { return &_callsigndb; } RadioInfo UV390::defaultRadioInfo() { return RadioInfo( RadioInfo::UV390, "uv390", "MD-UV390", "TyT", TyTInterface::interfaceInfo(), QList{ RadioInfo(RadioInfo::UV380, "MD-UV380", "TyT", TyTInterface::interfaceInfo()), RadioInfo(RadioInfo::RT3S, "RT3S", "Retevis", TyTInterface::interfaceInfo()) }); } qdmr-0.12.3/lib/uv390.hh000066400000000000000000000047751501654372000145350ustar00rootroot00000000000000/** @defgroup uv390 TYT MD-UV390, Retevis RT3S * Device specific classes for TyT MD-UV390 and Retevis RT3S. * * \image html uv390.jpg "MD-UV390" width=200px * \image latex uv390.jpg "MD-UV390" width=200px * * The TYT MD-UV390 and the identical Retevis RT3S are decent VHF/UHF FM and DMR handheld radios. * Both radios are available with and without an GPS option. @c libdmrconf will support that * feature. Non-GPS variants of that radio will simply ignore any GPS system settings. * * These radios support up to 3000 channels organized in 250 zones. Each zone may hold up to 64 * channels for each VFO (64 for VFO A and 64 for VFO B). There are also up to 250 scanlists * holding up to 31(?) channels each. * * The radio can hold up to 3000 contacts (DMR contacts) and 250 RX group lists as well as up to 50 * pre-programmed messages. Depending on the firmware programmed on the radio, it may also hold a * callsign database of up to 100000 entries. This can be used to resolve amlost any DMR ID assigned * (at the time of this writing, there are about 140k IDs assigned) to name and callsign. * * @ingroup tyt */ #ifndef UV390_HH #define UV390_HH #include "tyt_radio.hh" #include "uv390_codeplug.hh" #include "uv390_callsigndb.hh" class RadioLimits; /** Implements an USB interface to the TYT MD-UV390 & Retevis RT3S VHF/UHF 5W DMR (Tier I&II) radios. * * The TYT MD-UV390 and Retevis RT3S radios use the TyT typical DFU-style communication protocol * to read and write codeplugs onto the radio (see @c TyTRadio). * * @ingroup uv390 */ class UV390 : public TyTRadio { Q_OBJECT public: /** Constructor. * @param device Specifies the DFU device to use for communication with the device. * @param parent The QObject parent. */ UV390(TyTInterface *device=nullptr, QObject *parent=nullptr); /** Desturctor. */ virtual ~UV390(); const QString &name() const; const RadioLimits &limits() const; const Codeplug &codeplug() const; Codeplug &codeplug(); const CallsignDB *callsignDB() const; CallsignDB *callsignDB(); /** Returns the default radio information. The actual instance may have different properties * due to variants of the same radio. */ static RadioInfo defaultRadioInfo(); private: /** Holds the name of the device. */ QString _name; /** The codeplug object. */ UV390Codeplug _codeplug; /** The callsign DB object. */ UV390CallsignDB _callsigndb; private: /** Holds the singleton instance of the radio limits. */ static RadioLimits *_limits; }; #endif // MD2017_HH qdmr-0.12.3/lib/uv390_callsigndb.cc000066400000000000000000000003371501654372000166730ustar00rootroot00000000000000#include "uv390_callsigndb.hh" UV390CallsignDB::UV390CallsignDB(QObject *parent) : TyTCallsignDB(parent) { image(0).setName("TYT MD-UV390 Callsign database."); } UV390CallsignDB::~UV390CallsignDB() { // pass... } qdmr-0.12.3/lib/uv390_callsigndb.hh000066400000000000000000000007541501654372000167100ustar00rootroot00000000000000#ifndef UV390_CALLSIGNDB_HH #define UV390_CALLSIGNDB_HH #include "tyt_callsigndb.hh" /** Device specific implementation of the call-sign DB for the TyT MD-UV390. * * In fact this callsign DB is identical to the generic @c TyTCallsignDB. * * @ingroup uv390 */ class UV390CallsignDB : public TyTCallsignDB { Q_OBJECT public: /** Constructor. */ explicit UV390CallsignDB(QObject *parent=nullptr); /** Destructor. */ virtual ~UV390CallsignDB(); }; #endif // UV390_CALLSIGNDB_HH qdmr-0.12.3/lib/uv390_codeplug.cc000066400000000000000000000670141501654372000164000ustar00rootroot00000000000000#include "uv390_codeplug.hh" #include "logger.hh" #include "config.hh" #include "tyt_extensions.hh" #include #define NUM_CONTACTS 10000 #define ADDR_CONTACTS 0x140000 #define CONTACT_SIZE 0x000024 #define NUM_ZONES 250 #define ADDR_ZONES 0x0149e0 #define ZONE_SIZE 0x000040 #define ADDR_ZONEEXTS 0x031000 #define ZONEEXT_SIZE 0x0000e0 #define NUM_GROUPLISTS 250 #define ADDR_GROUPLISTS 0x00ec20 #define GROUPLIST_SIZE 0x000060 #define NUM_SCANLISTS 250 #define ADDR_SCANLISTS 0x018860 #define SCANLIST_SIZE 0x000068 #define ADDR_TIMESTAMP 0x002000 #define ADDR_SETTINGS 0x002040 #define SETTINGS_SIZE 0x0000b0 #define ADDR_BOOTSETTINGS 0x02f000 #define ADDR_MENUSETTINGS 0x0020f0 #define ADDR_BUTTONSETTINGS 0x002100 #define ADDR_PRIVACY_KEYS 0x0059c0 #define NUM_GPSSYSTEMS 16 #define ADDR_GPSSYSTEMS 0x03ec40 #define GPSSYSTEM_SIZE 0x000010 #define ADDR_EMERGENCY_SETTINGS 0x005a50 #define NUM_EMERGENCY_SYSTEMS 32 #define ADDR_EMERGENCY_SYSTEMS 0x005a60 #define EMERGENCY_SYSTEM_SIZE 0x000028 #define ADDR_VFO_CHANNEL_A 0x02ef00 #define ADDR_VFO_CHANNEL_B 0x02ef40 /* ******************************************************************************************** * * Implementation of UV390Codeplug::ChannelElement * ******************************************************************************************** */ UV390Codeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size) : TyTCodeplug::ChannelElement(ptr, size) { // pass... } UV390Codeplug::ChannelElement::ChannelElement(uint8_t *ptr) : TyTCodeplug::ChannelElement(ptr, size()) { // pass... } void UV390Codeplug::ChannelElement::clear() { TyTCodeplug::ChannelElement::clear(); enableTalkaround(false); clearBit(5,0); setInCallCriteria(TyTChannelExtension::InCallCriterion::Always); setTurnOffFreq(TyTChannelExtension::KillTone::Off); setSquelch(1); setPower(Channel::Power::High); enableAllowInterrupt(true); enableDualCapacityDirectMode(false); enableDCDMLeader(true); } bool UV390Codeplug::ChannelElement::talkaround() const { return getBit(Offset::talkaround()); } void UV390Codeplug::ChannelElement::enableTalkaround(bool enable) { setBit(Offset::talkaround(), enable); } TyTChannelExtension::InCallCriterion UV390Codeplug::ChannelElement::inCallCriteria() const { return TyTChannelExtension::InCallCriterion(getUInt2(5,4)); } void UV390Codeplug::ChannelElement::setInCallCriteria(TyTChannelExtension::InCallCriterion crit) { setUInt2(5,4, uint8_t(crit)); } TyTChannelExtension::KillTone UV390Codeplug::ChannelElement::turnOffFreq() const { return TyTChannelExtension::KillTone(getUInt2(5,6)); } void UV390Codeplug::ChannelElement::setTurnOffFreq(TyTChannelExtension::KillTone freq) { setUInt2(5,6, uint8_t(freq)); } unsigned UV390Codeplug::ChannelElement::squelch() const { return getUInt8(15); } void UV390Codeplug::ChannelElement::setSquelch(unsigned value) { value = std::min(unsigned(10), value); return setUInt8(15, value); } Channel::Power UV390Codeplug::ChannelElement::power() const { switch (getUInt2(30, 0)) { case 0: return Channel::Power::Low; case 2: return Channel::Power::Mid; case 3: return Channel::Power::High; default: break; } return Channel::Power::Low; } void UV390Codeplug::ChannelElement::setPower(Channel::Power pwr) { switch (pwr) { case Channel::Power::Min: case Channel::Power::Low: setUInt2(30,0, 0); break; case Channel::Power::Mid: setUInt2(30,0, 2); break; case Channel::Power::High: case Channel::Power::Max: setUInt2(30,0, 3); } } bool UV390Codeplug::ChannelElement::allowInterrupt() const { return !getBit(31, 2); } void UV390Codeplug::ChannelElement::enableAllowInterrupt(bool enable) { setBit(31,2, !enable); } bool UV390Codeplug::ChannelElement::dualCapacityDirectMode() const { return !getBit(31, 3); } void UV390Codeplug::ChannelElement::enableDualCapacityDirectMode(bool enable) { setBit(31,3, !enable); } bool UV390Codeplug::ChannelElement::dcdmLeader() const { return !getBit(31, 4); } void UV390Codeplug::ChannelElement::enableDCDMLeader(bool enable) { setBit(31,4, !enable); } Channel * UV390Codeplug::ChannelElement::toChannelObj(const ErrorStack &err) const { if (! isValid()) { errMsg(err) << "Cannot decode invalid channel."; return nullptr; } Channel *ch = TyTCodeplug::ChannelElement::toChannelObj(err); if (nullptr == ch) { errMsg(err) << "Cannot decode base TyT channel element."; return nullptr; } // decode squelch setting if (ch->is()) { FMChannel *ach = ch->as(); ach->setSquelch(squelch()); } // Common settings ch->setPower(power()); // assemble extension if (TyTChannelExtension *ex = ch->tytChannelExtension()) { ex->setKillTone(turnOffFreq()); ex->setInCallCriterion(inCallCriteria()); ex->enableAllowInterrupt(allowInterrupt()); ex->enableDCDM(dualCapacityDirectMode()); ex->enableDCDMLeader(dcdmLeader()); if (ch->is()) ex->setDMRSquelch(squelch()); } return ch; } void UV390Codeplug::ChannelElement::fromChannelObj(const Channel *chan, Context &ctx) { TyTCodeplug::ChannelElement::fromChannelObj(chan, ctx); // encode power setting if (chan->defaultPower()) setPower(ctx.config()->settings()->power()); else setPower(chan->power()); // By default, set to global default value. setSquelch(ctx.config()->settings()->squelch()); if (chan->is()) { const FMChannel *achan = chan->as(); if (! achan->defaultSquelch()) setSquelch(achan->squelch()); } // apply extensions if (TyTChannelExtension *ex = chan->tytChannelExtension()) { setTurnOffFreq(ex->killTone()); setInCallCriteria(ex->inCallCriterion()); enableAllowInterrupt(ex->allowInterrupt()); enableDualCapacityDirectMode(ex->dcdm()); enableDCDMLeader(ex->dcdmLeader()); if (chan->is()) setSquelch(ex->dmrSquelch()); } } /* ******************************************************************************************** * * Implementation of UV390Codeplug::VFOChannelElement * ******************************************************************************************** */ UV390Codeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr, size_t size) : ChannelElement(ptr, size) { // pass... } UV390Codeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr) : ChannelElement(ptr, size()) { // pass... } UV390Codeplug::VFOChannelElement::~VFOChannelElement() { // pass... } QString UV390Codeplug::VFOChannelElement::name() const { return ""; } void UV390Codeplug::VFOChannelElement::setName(const QString &txt) { Q_UNUSED(txt) // pass... } unsigned UV390Codeplug::VFOChannelElement::stepSize() const { return (getUInt8(32)+1)*2500; } void UV390Codeplug::VFOChannelElement::setStepSize(unsigned ss_Hz) { ss_Hz = std::min(50000U, std::max(ss_Hz, 2500U)); setUInt8(32, ss_Hz/2500-1); setUInt8(33, 0xff); } /* ******************************************************************************************** * * Implementation of UV390Codeplug::GeneralSettingsElement * ******************************************************************************************** */ UV390Codeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, size_t size) : DM1701Codeplug::GeneralSettingsElement(ptr, size) { // pass... } UV390Codeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr) : DM1701Codeplug::GeneralSettingsElement(ptr, SETTINGS_SIZE) { // pass... } void UV390Codeplug::GeneralSettingsElement::clear() { TyTCodeplug::GeneralSettingsElement::clear(); setTransmitMode(DESIGNED_AND_HAND_CH); enableChannelVoiceAnnounce(false); setBit(0x43,0, 1); setBit(0x43,1, 1); setUInt4(0x43,3, 0xf); setBit(0x6b, 2, 1); setUInt8(0x91, 0xff); setUInt2(0x92, 0, 0x03); enablePublicZone(true); setUInt5(0x92, 3, 0x1f); setUInt8(0x93, 0xff); setAdditionalDMRId(0, 1); setUInt8(0x97, 0); setAdditionalDMRId(1, 2); setUInt8(0x9b, 0); setAdditionalDMRId(2, 3); setUInt8(0x9f, 0); setUInt3(0xa0, 0, 0b111); setMICLevel(2); enableEditRadioID(true); setBit(0xa0, 7, true); memset(_data+0xa1, 0xff, 15); } UV390Codeplug::GeneralSettingsElement::TransmitMode UV390Codeplug::GeneralSettingsElement::transmitMode() const { return TransmitMode(getUInt2(0x40,6)); } void UV390Codeplug::GeneralSettingsElement::setTransmitMode(TransmitMode mode) { setUInt2(0x40,6, mode); } bool UV390Codeplug::GeneralSettingsElement::channelVoiceAnnounce() const { return getBit(0x42,1); } void UV390Codeplug::GeneralSettingsElement::enableChannelVoiceAnnounce(bool enable) { setBit(0x42,1, enable); } bool UV390Codeplug::GeneralSettingsElement::keypadTones() const { return getBit(0x42,5); } void UV390Codeplug::GeneralSettingsElement::enableKeypadTones(bool enable) { setBit(0x42,5, enable); } bool UV390Codeplug::GeneralSettingsElement::publicZone() const { return getBit(0x92, 2); } void UV390Codeplug::GeneralSettingsElement::enablePublicZone(bool enable) { setBit(0x92, 2, enable); } uint32_t UV390Codeplug::GeneralSettingsElement::additionalDMRId(unsigned n) const { return getUInt24_le(0x94+4*n); } void UV390Codeplug::GeneralSettingsElement::setAdditionalDMRId(unsigned n, uint32_t id) { setUInt24_le(0x94+4*n, id); } unsigned UV390Codeplug::GeneralSettingsElement::micLevel() const { return (unsigned(getUInt3(0xa0,3)+1)*100)/60; } void UV390Codeplug::GeneralSettingsElement::setMICLevel(unsigned level) { setUInt3(0xa0,3, ((level-1)*60)/100); } bool UV390Codeplug::GeneralSettingsElement::editRadioID() const { return !getBit(0xa0, 6); } void UV390Codeplug::GeneralSettingsElement::enableEditRadioID(bool enable) { setBit(0xa0,6, !enable); } bool UV390Codeplug::GeneralSettingsElement::fromConfig(const Config *config) { if (! DM1701Codeplug::GeneralSettingsElement::fromConfig(config)) return false; setTimeZone(QTimeZone::systemTimeZone()); setMICLevel(config->settings()->micLevel()); enableChannelVoiceAnnounce(config->settings()->speech()); return true; } bool UV390Codeplug::GeneralSettingsElement::updateConfig(Config *config) { if (! DM1701Codeplug::GeneralSettingsElement::updateConfig(config)) return false; config->settings()->setMicLevel(micLevel()); config->settings()->enableSpeech(channelVoiceAnnounce()); return true; } /* ******************************************************************************************** * * Implementation of UV390Codeplug::BootSettingsElement * ******************************************************************************************** */ UV390Codeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr, size_t size) : Codeplug::Element(ptr, size) { // pass... } UV390Codeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr) : Codeplug::Element(ptr, 0x0010) { // pass... } UV390Codeplug::BootSettingsElement::~BootSettingsElement() { // pass... } void UV390Codeplug::BootSettingsElement::clear() { setUInt24_le(0, 0xffffff); setZoneIndex(1); setChannelIndexA(1); setUInt8(0x05, 0xff); setChannelIndexB(1); setUInt16_le(0x07, 0xffff); setUInt16_le(0x09, 0x0001); setUInt8(0x0b, 0xff); setUInt32_le(0x0c, 0xffffffff); } unsigned UV390Codeplug::BootSettingsElement::zoneIndex() const { return getUInt8(0x03); } void UV390Codeplug::BootSettingsElement::setZoneIndex(unsigned idx) { setUInt8(0x03, idx); } unsigned UV390Codeplug::BootSettingsElement::channelIndexA() const { return getUInt8(0x04); } void UV390Codeplug::BootSettingsElement::setChannelIndexA(unsigned idx) { setUInt8(0x04, idx); } unsigned UV390Codeplug::BootSettingsElement::channelIndexB() const { return getUInt8(0x06); } void UV390Codeplug::BootSettingsElement::setChannelIndexB(unsigned idx) { setUInt8(0x06, idx); } /* ******************************************************************************************** * * Implementation of UV390Codeplug::MenuSettingsElement * ******************************************************************************************** */ UV390Codeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr, size_t size) : TyTCodeplug::MenuSettingsElement(ptr, size) { // pass... } UV390Codeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr) : TyTCodeplug::MenuSettingsElement(ptr) { // pass... } void UV390Codeplug::MenuSettingsElement::clear() { TyTCodeplug::MenuSettingsElement::clear(); enableGPSSettings(true); enableRecording(true); enableGroupCallMatch(true); enablePrivateCallMatch(true); enableMenuHangtimeItem(true); enableTXMode(true); enableZoneSettings(true); enableNewZone(true); enableEditZone(true); enableNewScanList(true); setBit(0x05, 0, true); setBit(0x05, 1, true); enableGroupCallMatch(true); enablePrivateCallMatch(true); enableMenuHangtimeItem(true); enableTXMode(true); enableZoneSettings(true); enableNewZone(true); enableEditZone(true); enableNewScanList(true); } bool UV390Codeplug::MenuSettingsElement::gpsSettings() const { return !getBit(0x04, 3); } void UV390Codeplug::MenuSettingsElement::enableGPSSettings(bool enable) { setBit(0x04, 3, !enable); } bool UV390Codeplug::MenuSettingsElement::recording() const { return getBit(0x04, 5); } void UV390Codeplug::MenuSettingsElement::enableRecording(bool enable) { setBit(0x04, 5, enable); } bool UV390Codeplug::MenuSettingsElement::groupCallMatch() const { return getBit(0x05, 2); } void UV390Codeplug::MenuSettingsElement::enableGroupCallMatch(bool enable) { setBit(0x05, 2, enable); } bool UV390Codeplug::MenuSettingsElement::privateCallMatch() const { return getBit(0x05, 3); } void UV390Codeplug::MenuSettingsElement::enablePrivateCallMatch(bool enable) { setBit(0x05, 3, enable); } bool UV390Codeplug::MenuSettingsElement::menuHangtimeItem() const { return getBit(0x05, 4); } void UV390Codeplug::MenuSettingsElement::enableMenuHangtimeItem(bool enable) { setBit(0x05, 4, enable); } bool UV390Codeplug::MenuSettingsElement::txMode() const { return getBit(0x05, 5); } void UV390Codeplug::MenuSettingsElement::enableTXMode(bool enable) { setBit(0x05, 5, enable); } bool UV390Codeplug::MenuSettingsElement::zoneSettings() const { return getBit(0x05, 6); } void UV390Codeplug::MenuSettingsElement::enableZoneSettings(bool enable) { setBit(0x05, 6, enable); } bool UV390Codeplug::MenuSettingsElement::newZone() const { return getBit(0x05, 7); } void UV390Codeplug::MenuSettingsElement::enableNewZone(bool enable) { setBit(0x05, 7, enable); } bool UV390Codeplug::MenuSettingsElement::editZone() const { return getBit(0x06, 0); } void UV390Codeplug::MenuSettingsElement::enableEditZone(bool enable) { setBit(0x06, 0, enable); } bool UV390Codeplug::MenuSettingsElement::newScanList() const { return getBit(0x06, 1); } void UV390Codeplug::MenuSettingsElement::enableNewScanList(bool enable) { setBit(0x06, 1, enable); } /* ******************************************************************************************** * * Implementation of UV390Codeplug * ******************************************************************************************** */ UV390Codeplug::UV390Codeplug(QObject *parent) : TyTCodeplug(parent) { addImage("TYT MD-UV390 Codeplug"); image(0).addElement(0x002000, 0x3e000); image(0).addElement(0x110000, 0x90000); // Clear entire codeplug UV390Codeplug::clear(); } UV390Codeplug::~UV390Codeplug() { // pass... } void UV390Codeplug::clear() { TyTCodeplug::clear(); UV390Codeplug::clearBootSettings(); UV390Codeplug::clearVFOSettings(); } void UV390Codeplug::clearTimestamp() { TimestampElement(data(ADDR_TIMESTAMP)).clear(); } bool UV390Codeplug::encodeTimestamp() { TimestampElement ts(data(ADDR_TIMESTAMP)); ts.setTimestamp(QDateTime::currentDateTime()); return true; } void UV390Codeplug::clearGeneralSettings() { GeneralSettingsElement(data(ADDR_SETTINGS)).clear(); } bool UV390Codeplug::encodeGeneralSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err) return GeneralSettingsElement(data(ADDR_SETTINGS)).fromConfig(config); } bool UV390Codeplug::decodeGeneralSettings(Config *config, const ErrorStack &err) { Q_UNUSED(err) return GeneralSettingsElement(data(ADDR_SETTINGS)).updateConfig(config); } void UV390Codeplug::clearChannels() { // Clear channels for (unsigned int i=0; ichannelList()->count()) { chan.fromChannelObj(config->channelList()->channel(i), ctx); } } return true; } bool UV390Codeplug::createChannels(Config *config, Context &ctx, const ErrorStack &err) { for (unsigned int i=0; ichannelList()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid channel at index " << i << "."; return false; } } return true; } bool UV390Codeplug::linkChannels(Context &ctx, const ErrorStack &err) { for (unsigned int i=0; i(i+1), ctx, err)) { errMsg(err) << "Cannot link channel at index " << i << "."; return false; } } return true; } void UV390Codeplug::clearContacts() { // Clear contacts for (int i=0; icontacts()->digitalCount()) cont.fromContactObj(config->contacts()->digitalContact(i)); else cont.clear(); } return true; } bool UV390Codeplug::createContacts(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; icontacts()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid contact at index " << i << "."; return false; } } return true; } void UV390Codeplug::clearZones() { // Clear zones & zone extensions for (int i=0; izones()->count()) { zone.fromZoneObj(config->zones()->zone(i), ctx); if (config->zones()->zone(i)->B()->count() || (16 < config->zones()->zone(i)->A()->count())) ext.fromZoneObj(config->zones()->zone(i), ctx); } } return true; } bool UV390Codeplug::createZones(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; izones()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid zone at index " << i << "."; return false; } } return true; } bool UV390Codeplug::linkZones(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link zone at index " << i << "."; return false; } ZoneExtElement zoneext(data(ADDR_ZONEEXTS + i*ZONEEXT_SIZE)); if (! zoneext.linkZoneObj(ctx.get(i+1), ctx)) { errMsg(err) << "Cannot link zone extension at index " << i << "."; return false; } } return true; } void UV390Codeplug::clearGroupLists() { for (int i=0; irxGroupLists()->count()) glist.fromGroupListObj(config->rxGroupLists()->list(i), ctx); else glist.clear(); } return true; } bool UV390Codeplug::createGroupLists(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; irxGroupLists()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid RX group list at index " << i << "."; return false; } } return true; } bool UV390Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link group-list at index " << i << "."; return false; } } return true; } void UV390Codeplug::clearScanLists() { // Clear scan lists for (int i=0; iscanlists()->count()) scan.fromScanListObj(config->scanlists()->scanlist(i), ctx); else scan.clear(); } return true; } bool UV390Codeplug::createScanLists(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; iscanlists()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid scan list at index " << i << "."; return false; } } return true; } bool UV390Codeplug::linkScanLists(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link scan list at index " << i << "."; return false; } } return true; } void UV390Codeplug::clearPositioningSystems() { // Clear GPS systems for (int i=0; iposSystems()->gpsCount()) { logDebug() << "Encode GPS system #" << i << " '" << config->posSystems()->gpsSystem(i)->name() << "'."; gps.fromGPSSystemObj(config->posSystems()->gpsSystem(i), ctx); } else { gps.clear(); } } return true; } bool UV390Codeplug::createPositioningSystems(Config *config, Context &ctx, const ErrorStack &err) { for (int i=0; iposSystems()->add(obj); ctx.add(obj, i+1); } else { errMsg(err) << "Invalid GPS system at index " << i << "."; return false; } } return true; } bool UV390Codeplug::linkPositioningSystems(Context &ctx, const ErrorStack &err) { for (int i=0; i(i+1), ctx)) { errMsg(err) << "Cannot link GPS system at index " << i << "."; return false; } } return true; } void UV390Codeplug::clearButtonSettings() { ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).clear(); } bool UV390Codeplug::encodeButtonSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err) // Encode settings return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).fromConfig(config); } bool UV390Codeplug::decodeButtonSetttings(Config *config, const ErrorStack &err) { Q_UNUSED(err) return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).updateConfig(config); } void UV390Codeplug::clearPrivacyKeys() { EncryptionElement(data(ADDR_PRIVACY_KEYS)).clear(); } bool UV390Codeplug::encodePrivacyKeys(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) { Q_UNUSED(flags); Q_UNUSED(err); // First, reset keys clearPrivacyKeys(); // Get keys EncryptionElement keys(data(ADDR_PRIVACY_KEYS)); return keys.fromCommercialExt(config->commercialExtension(), ctx); } bool UV390Codeplug::decodePrivacyKeys(Config *config, Context &ctx, const ErrorStack &err) { Q_UNUSED(config) // Get keys EncryptionElement keys(data(ADDR_PRIVACY_KEYS)); // Decode element if (! keys.updateCommercialExt(ctx)) { errMsg(err) << "Cannot create encryption extension."; return false; } return true; } void UV390Codeplug::clearBootSettings() { BootSettingsElement(data(ADDR_BOOTSETTINGS)).clear(); } void UV390Codeplug::clearMenuSettings() { MenuSettingsElement(data(ADDR_MENUSETTINGS)).clear(); } void UV390Codeplug::clearTextMessages() { MessageBankElement(data(Offset::messages())).clear(); } bool UV390Codeplug::encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err) { return MessageBankElement(data(Offset::messages())).encode(ctx, flags, err); } bool UV390Codeplug::decodeTextMessages(Context &ctx, const ErrorStack &err) { return MessageBankElement(data(Offset::messages())).decode(ctx, err); } void UV390Codeplug::clearEmergencySystems() { EmergencySettingsElement(data(ADDR_EMERGENCY_SETTINGS)).clear(); for (int i=0; i * Start End Size Content * First segment 0x002000-0x040000 * 0x002000 0x00200c 0x0000c Timestamp see @c TyTCodeplug::TimestampElement. * 0x00200c 0x002040 0x00034 Reserved, filled with 0xff. * 0x002040 0x0020f0 0x000b0 General settings see @c GeneralSettingsElement. * 0x0020f0 0x002100 0x00010 Menu settings, see @c MenuSettingsElement. * 0x002100 0x002140 0x00040 Button config, see @c TyTCodeplug::ButtonSettingsElement. * 0x002140 0x002180 0x00040 Reserved, filled with 0xff. * 0x002180 0x0059c0 0x03840 50 Text messages @ 0x120 bytes each. * 0x0059c0 0x005a70 0x000b0 Privacy keys, see @c TyTCodeplug::EncryptionElement. * 0x005a70 0x005a80 0x00010 Emergency system settings, see @c TyTCodeplug::EmergencySettingsElement. * 0x005a80 0x005f80 0x00500 Emergency systems, see @c TyTCodeplug::EmergencySystemElement. * 0x005f80 0x00ec20 0x08ca0 Reserved, filled with 0xff. * 0x00ec20 0x0149e0 0x05dc0 250 RX Group lists @ 0x60 bytes each, see @c TyTCodeplug::GroupListElement. * 0x0149e0 0x018860 0x03e80 250 Zones @ 0x40 bytes each, see @c TyTCodeplug::ZoneElement. * 0x018860 0x01edf0 0x06590 250 Scanlists @ 0x68 bytes each, see @c TyTCodeplug::ScanListElement. * 0x01edf0 0x02ef00 0x10110 Reserved, filled with @c 0xff. * 0x02ef00 0x02ef40 0x00040 VFO A channel, see @c VFOChannelElement. * 0x02ef40 0x02ef80 0x00040 VFO B channel, see @c VFOChannelElement. * 0x02ef80 0x02f000 0x00080 Reserved, filled with @c 0xff. * 0x02f000 0x02f010 0x00010 Boot settings, see @c BootSettingsElement. * 0x02f010 0x031000 0x01ff0 Reserved, filled with @c 0xff. * 0x031000 0x03eac0 0x0dac0 250 Zone-extensions @ 0xe0 bytes each, see @c DM1701Codeplug::ZoneExtElement. * 0x03eac0 0x03ec40 0x00180 Reserved, filled with @c 0xff. * 0x03ec40 0x03ed40 0x00100 16 GPS systems @ 0x10 bytes each, see @c TyTCodeplug::GPSSystemElement. * 0x03ed40 0x040000 0x012c0 Reserved, filled with @c 0xff. * Second segment 0x110000-0x1a0000 * 0x110000 0x13ee00 0x2ee00 3000 Channels @ 0x40 bytes each, see @c ChannelElement. * 0x13ee00 0x140000 0x01200 Reserved, filled with @c 0xff. * 0x140000 0x197e40 0x57e40 10000 Contacts @ 0x24 bytes each, see @c TyTCodeplug::ContactElement. * 0x197e40 0x1a0000 0x081c0 Reserved, filled with @c 0xff. * * * @ingroup uv390 */ class UV390Codeplug : public TyTCodeplug { Q_OBJECT public: /** Extends the @c TyTCodeplug::ChannelElement for the TyT MD-UV390 and Retevis RT3S. * * Memory layout of encoded channel: * @verbinclude uv390_channel.txt */ class ChannelElement: public TyTCodeplug::ChannelElement { protected: /** Constructs a channel from the given memory. */ ChannelElement(uint8_t *ptr, size_t size); public: /** Constructs a channel from the given memory. */ explicit ChannelElement(uint8_t *ptr); /** Clears/resets the channel and therefore disables it. */ void clear(); bool talkaround() const; void enableTalkaround(bool enable); /** Returns the in-call criterion for this channel. */ virtual TyTChannelExtension::InCallCriterion inCallCriteria() const; /** Sets the in-call criterion for this channel. */ virtual void setInCallCriteria(TyTChannelExtension::InCallCriterion crit); /** Returns the remote turn-off/kill frequency for this channel. */ virtual TyTChannelExtension::KillTone turnOffFreq() const; /** Sets the remote turn-off/kill frequency for this channel. */ virtual void setTurnOffFreq(TyTChannelExtension::KillTone freq); /** Returns the squelch level [0-10]. */ virtual unsigned squelch() const; /** Sets the squelch level [0-10]. */ virtual void setSquelch(unsigned value); /** Returns the power of this channel. */ virtual Channel::Power power() const; /** Sets the power of this channel. */ virtual void setPower(Channel::Power pwr); /** Returns @c true if the channel allows interruption enabled. */ virtual bool allowInterrupt() const; /** Enables/disables interruption for this channel. */ virtual void enableAllowInterrupt(bool enable); /** Returns @c true if the channel has dual-capacity direct mode enabled. */ virtual bool dualCapacityDirectMode() const; /** Enables/disables dual-capacity direct mode for this channel. */ virtual void enableDualCapacityDirectMode(bool enable); /** Returns @c true if the radio acts as the leader for this DCDM channel. */ virtual bool dcdmLeader() const; /** Enables/disables this radio to be the leader for this DCDM channel. */ virtual void enableDCDMLeader(bool enable); /** Constructs a generic @c Channel object from the codeplug channel. */ virtual Channel *toChannelObj(const ErrorStack &err=ErrorStack()) const; /** Initializes this codeplug channel from the given generic configuration. */ virtual void fromChannelObj(const Channel *c, Context &ctx); }; /** Implements a VFO channel for TyT radios. * This class is an extension of the normal ChannelElement that only implements the step-size * feature and encodes it where the name used to be. Thus the memory layout and size is identical * to the normal channel. */ class VFOChannelElement: public ChannelElement { protected: /** Constructor from pointer to memory. */ VFOChannelElement(uint8_t *ptr, size_t size); public: /** Constructor from pointer to memory. */ VFOChannelElement(uint8_t *ptr); /** Destructor. */ virtual ~VFOChannelElement(); QString name() const; void setName(const QString &txt); /** Returns the step-size for the VFO channel. */ virtual unsigned stepSize() const; /** Sets the step-size for the VFO channel in Hz. */ virtual void setStepSize(unsigned ss_hz); }; /** Reuse zone extension from DM1701. */ typedef DM1701Codeplug::ZoneExtElement ZoneExtElement; /** Extends the common @c TyTCodeplug::GeneralSettings to implement the MD-UV390 specific * settings. * * Memory layout of the settings (size 0x00b0 bytes): * @verbinclude uv390_settings.txt */ class GeneralSettingsElement: public DM1701Codeplug::GeneralSettingsElement { protected: /** Hidden constructor. */ GeneralSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ GeneralSettingsElement(uint8_t *ptr); void clear(); /** Defines all possible transmit modes. */ enum TransmitMode { LAST_CALL_CH = 0, LAST_CALL_AND_HAND_CH = 1, DESIGNED_CH = 2, DESIGNED_AND_HAND_CH = 3, }; /** Returns the transmit mode. */ virtual TransmitMode transmitMode() const; /** Sets the transmit mode. */ virtual void setTransmitMode(TransmitMode mode); /** Returns @c true, if the speech synthesis is enabled. */ virtual bool channelVoiceAnnounce() const; /** Enables/disables the speech synthesis. */ virtual void enableChannelVoiceAnnounce(bool enable); /** Returns @c true, if keypad tones are enabled. */ virtual bool keypadTones() const; /** Enables/disables the keypad tones. */ virtual void enableKeypadTones(bool enable); /** Returns @c true, if public zone is enabled. */ virtual bool publicZone() const; /** Enables/disables public zone. */ virtual void enablePublicZone(bool enable); /** Returns the n-th DMR id. */ virtual uint32_t additionalDMRId(unsigned n) const; /** Sets the n-th DMR id. */ virtual void setAdditionalDMRId(unsigned n, uint32_t id); /** Returns the microphone gain. */ virtual unsigned micLevel() const; /** Sets the microphone gain. */ virtual void setMICLevel(unsigned val); /** If @c true, radio ID editing is enabled. */ virtual bool editRadioID() const; /** Enable/disable radio ID editing. */ virtual void enableEditRadioID(bool enable); /** Encodes the general settings. */ virtual bool fromConfig(const Config *config); /** Updates config from general settings. */ virtual bool updateConfig(Config *config); }; /** Represents the boot-time settings (selected zone and channels) within the UV390 code-plug. * * Memory layout of encoded boot settings: * @verbinclude uv390_bootsettings.txt */ class BootSettingsElement: public Codeplug::Element { protected: /** Hidden constructor. */ BootSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit BootSettingsElement(uint8_t *ptr); /** Destructor. */ virtual ~BootSettingsElement(); void clear(); /** Returns the boot zone index. */ virtual unsigned zoneIndex() const; /** Sets the boot zone index. */ virtual void setZoneIndex(unsigned idx); /** Returns the channel index (within zone) for VFO A. */ virtual unsigned channelIndexA() const; /** Sets the channel index (within zone) for VFO A. */ virtual void setChannelIndexA(unsigned idx); /** Returns the channel index (within zone) for VFO B. */ virtual unsigned channelIndexB() const; /** Sets the channel index (within zone) for VFO B. */ virtual void setChannelIndexB(unsigned idx); }; /** Represents the menu settings (selected zone and channels) within the UV390 code-plug. * * Memory layout of encoded boot settings: * @verbinclude uv390_menusettings.txt */ class MenuSettingsElement: public TyTCodeplug::MenuSettingsElement { protected: /** Hidden constructor. */ MenuSettingsElement(uint8_t *ptr, size_t size); public: /** Constructor. */ explicit MenuSettingsElement(uint8_t *ptr); void clear(); /** Returns @c true if GPS settings menu is enabled. */ virtual bool gpsSettings() const; /** Enables/disables GPS settings menu. */ virtual void enableGPSSettings(bool enable); /** Returns @c true if recording menu is enabled. */ virtual bool recording() const; /** Enables/disables recording menu. */ virtual void enableRecording(bool enable); /** Returns @c true if group call match menu is enabled. */ virtual bool groupCallMatch() const; /** Enables/disables group call match menu. */ virtual void enableGroupCallMatch(bool enable); /** Returns @c true if private call match menu is enabled. */ virtual bool privateCallMatch() const; /** Enables/disables private call match menu. */ virtual void enablePrivateCallMatch(bool enable); /** Returns @c true if menu hang time item is enabled. */ virtual bool menuHangtimeItem() const; /** Enables/disables menu hang time item. */ virtual void enableMenuHangtimeItem(bool enable); /** Returns @c true if TX mode menu is enabled. */ virtual bool txMode() const; /** Enables/disables TX mode menu. */ virtual void enableTXMode(bool enable); /** Returns @c true if zone settings menu is enabled. */ virtual bool zoneSettings() const; /** Enables/disables zone settings menu. */ virtual void enableZoneSettings(bool enable); /** Returns @c true if new zone menu is enabled. */ virtual bool newZone() const; /** Enables/disables new zone menu. */ virtual void enableNewZone(bool enable); /** Returns @c true if edit zone menu is enabled. */ virtual bool editZone() const; /** Enables/disables edit zone menu. */ virtual void enableEditZone(bool enable); /** Returns @c true if new scan list menu is enabled. */ virtual bool newScanList() const; /** Enables/disables new scan list menu. */ virtual void enableNewScanList(bool enable); }; public: /** Constructor. */ explicit UV390Codeplug(QObject *parent = nullptr); /** Destructor. */ virtual ~UV390Codeplug(); void clear(); public: void clearTimestamp(); bool encodeTimestamp(); void clearGeneralSettings(); bool encodeGeneralSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeGeneralSettings(Config *config, const ErrorStack &err=ErrorStack()); void clearChannels(); bool encodeChannels(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createChannels(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()); void clearContacts(); bool encodeContacts(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createContacts(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); void clearZones(); bool encodeZones(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createZones(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()); void clearGroupLists(); bool encodeGroupLists(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createGroupLists(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()); void clearScanLists(); bool encodeScanLists(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createScanLists(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack()); void clearPositioningSystems(); bool encodePositioningSystems(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool createPositioningSystems(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()); bool linkPositioningSystems(Context &ctx, const ErrorStack &err=ErrorStack()); void clearButtonSettings(); bool encodeButtonSettings(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()); bool decodeButtonSetttings(Config *config, const ErrorStack &err=ErrorStack()); void clearPrivacyKeys(); bool encodePrivacyKeys(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err); bool decodePrivacyKeys(Config *config, Context &ctx, const ErrorStack &err); void clearTextMessages(); bool encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err); bool decodeTextMessages(Context &ctx, const ErrorStack &err); /** Resets the boot setting, e.g. initial channels and zone at bootup. */ virtual void clearBootSettings(); void clearMenuSettings(); void clearEmergencySystems(); /** Clears the VFO A & B. */ virtual void clearVFOSettings(); public: /** Some limits for the codeplug. */ struct Limit: public Element::Limit { /// Number of channels. static constexpr unsigned int channels() { return 3000; } }; protected: /** Some internal offsets within the codeplug. */ struct Offset { /// @cond DO_NOT_DOCUMENT static constexpr unsigned int messages() { return 0x002180; } static constexpr unsigned int channels() { return 0x110000; } static constexpr unsigned int betweenChannels() { return ChannelElement::size(); } /// @endcond }; }; #endif // UV390CODEPLUG_HH qdmr-0.12.3/lib/uv390_filereader.cc000066400000000000000000000041471501654372000166760ustar00rootroot00000000000000#include "uv390_filereader.hh" #include #include #define SEGMENT0_FILE_ADDR 0x00002225 #define SEGMENT0_TARGET_ADDR 0x00002000 #define SEGMENT0_SIZE 0x0003e000 #define SEGMENT1_FILE_ADDR 0x00040235 #define SEGMENT1_TARGET_ADDR 0x00110000 #define SEGMENT1_SIZE 0x00090000 bool UV390FileReader::read(const QString &filename, UV390Codeplug *codeplug, const ErrorStack &err) { // Check file properties QFileInfo info(filename); if (! info.exists()) { errMsg(err) << "Cannot open file '" << filename << "': File does not exisist."; return false; } if (852533 != info.size()) { errMsg(err) << "Cannot read codeplug file '" << filename << "': File size is not 852533 bytes."; return false; } // Open file QFile file(filename); if (! file.open(QFile::ReadOnly)) { errMsg(err) << "Cannot open file '" << filename << "': " << file.errorString() << "."; return false; } // Read file content if (! file.seek(SEGMENT0_FILE_ADDR)) { errMsg(err) << "Cannot read codeplug file '" << filename << "': Cannot seek within file: " << file.errorString() << "."; file.close(); return false; } char *ptr = (char *)codeplug->data(SEGMENT0_TARGET_ADDR); size_t n = SEGMENT0_SIZE; while (0 < n) { int nread = file.read(ptr, n); if (0 > nread) { errMsg(err) << "Cannot read codeplug file '" << filename << "': " << file.errorString() << "."; file.close(); return false; } n -= nread; ptr += nread; } if (! file.seek(SEGMENT1_FILE_ADDR)) { errMsg(err) << "Cannot read codeplug file '" << filename << "': Cannot seek within file: " << file.errorString() << "."; file.close(); return false; } ptr = (char *)codeplug->data(SEGMENT1_TARGET_ADDR); n = SEGMENT1_SIZE; while (0 < n) { int nread = file.read(ptr, n); if (0 > nread) { errMsg(err) << "Cannot read codeplug file '" << filename << "': " << file.errorString() << "."; file.close(); return false; } n -= nread; ptr += nread; } return true; } qdmr-0.12.3/lib/uv390_filereader.hh000066400000000000000000000023171501654372000167050ustar00rootroot00000000000000#ifndef UV390FILEREADER_HH #define UV390FILEREADER_HH #include "uv390_codeplug.hh" /** Methods to read manufacturer codeplug files. * * The file format of the stock CPS is still pretty simple. The first part of the file consists of * a mal-formed DFU file. This contains a single image with a single element containing the * first section of the memory written to the device. The second section is then added as-is * to the end of the file. Due to the DFU header/footer, the file and memory offsets differ. * * * * * *
      File Start Memory Start Size
      0x002225 0x002000 0x03e000
      0x040235 0x110000 0x090000
      * * @ingroup uv390 */ class UV390FileReader { public: /** Reads manufacturer codeplug file into given codeplug object. * @param filename Specifies the file to read. * @param codeplug Specifies the codeplug object to store read codeplug. * @param err Error stack. * @returns @c true on success and @c false on error. */ static bool read(const QString &filename, UV390Codeplug *codeplug, const ErrorStack &err = ErrorStack()); }; #endif // UV390FILEREADER_HH qdmr-0.12.3/lib/uv390_limits.cc000066400000000000000000000223121501654372000160670ustar00rootroot00000000000000#include "uv390_limits.hh" #include "uv390_codeplug.hh" #include "channel.hh" #include "radioid.hh" #include "contact.hh" #include "rxgrouplist.hh" #include "scanlist.hh" #include "zone.hh" #include "gpssystem.hh" #include "roamingzone.hh" UV390Limits::UV390Limits(QObject *parent) : RadioLimits(false, parent) { // Define limits for call-sign DB _hasCallSignDB = true; _callSignDBImplemented = true; _numCallSignDBEntries = 122197; // Define limits for satellite config _hasSatelliteConfig = false; _satelliteConfigImplemented = false; _numSatellites = 0; add("settings", new RadioLimitItem { { "introLine1", new RadioLimitString(-1, 10, RadioLimitString::Unicode) }, { "introLine2", new RadioLimitString(-1, 10, RadioLimitString::Unicode) }, { "micLevel", new RadioLimitUInt(1, 10) }, { "speech", new RadioLimitIgnoredBool() }, { "power", new RadioLimitEnum { unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid), unsigned(Channel::Power::High) } }, { "squlech", new RadioLimitUInt(0, 10) }, { "vox", new RadioLimitUInt(0, 10) }, { "tot", new RadioLimitUInt(0, -1) } /// @todo check default radio ID. } ); /* Define limits for radio IDs. */ add("radioIDs", new RadioLimitList{ { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject { {"name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, {"id", new RadioLimitUInt(0, 16777215)} } } } ); /* Define limits for contacts. */ add("contacts", new RadioLimitList{ { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, { "ring", new RadioLimitBool() }, { "type", new RadioLimitEnum { (unsigned) DMRContact::PrivateCall, (unsigned) DMRContact::GroupCall, (unsigned) DMRContact::AllCall }}, { "number", new RadioLimitUInt(0, 16777215) } } }, { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() } } ); /* Define limits for group lists. */ add("groupLists", new RadioLimitList( RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, { "contacts", new RadioLimitGroupCallRefList(1, 32) } }) ); /* Define limits for channel list. */ add("channels", new RadioLimitList( Channel::staticMetaObject, 1, UV390Codeplug::Limit::channels(), new RadioLimitObjects { { FMChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, UV390Codeplug::ChannelElement::Limit::nameLength(), RadioLimitString::Unicode)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})}, {"power", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef({ScanList::staticMetaObject})}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"admit", new RadioLimitEnum{ (unsigned)FMChannel::Admit::Always, (unsigned)FMChannel::Admit::Free, (unsigned)FMChannel::Admit::Tone } }, {"squelch", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"bandwidth", new RadioLimitEnum{ (unsigned)FMChannel::Bandwidth::Narrow, (unsigned)FMChannel::Bandwidth::Wide }}, {"aprs", new RadioLimitObjRefIgnored(nullptr, RadioLimitIssue::Hint)} } }, { DMRChannel::staticMetaObject, new RadioLimitObject { {"name", new RadioLimitString(1, UV390Codeplug::ChannelElement::Limit::nameLength(), RadioLimitString::Unicode)}, {"rxFrequency", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, true)}, {"txFrequency", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}, {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})}, {"power", new RadioLimitEnum { unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid), unsigned(Channel::Power::High), }}, {"timeout", new RadioLimitUInt(0, -1, std::numeric_limits::max())}, {"scanlist", new RadioLimitObjRef(ScanList::staticMetaObject)}, {"vox", new RadioLimitUInt(0, 10, std::numeric_limits::max())}, {"rxOnly", new RadioLimitBool()}, {"admit", new RadioLimitEnum { unsigned(DMRChannel::Admit::Always), unsigned(DMRChannel::Admit::Free), unsigned(DMRChannel::Admit::ColorCode) } }, {"colorCode", new RadioLimitUInt(0,16)}, {"timeSlot", new RadioLimitEnum { unsigned(DMRChannel::TimeSlot::TS1), unsigned(DMRChannel::TimeSlot::TS2) } }, {"radioID", new RadioLimitObjRef(RadioID::staticMetaObject, true)}, {"groupList", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)}, {"contact", new RadioLimitObjRef(DMRContact::staticMetaObject, true)}, {"aprs", new RadioLimitObjRefIgnored()}, {"roaming", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())}, {"openGD77", new RadioLimitIgnored(RadioLimitIssue::Hint)}, {"tyt", new RadioLimitIgnored(RadioLimitIssue::Hint)} } } } ) ); /* Define limits for zone list. */ add("zones", new RadioLimitList( Zone::staticMetaObject, 1, 250, new RadioLimitObject { { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, // 16 ASCII chars in name { "A", new RadioLimitRefList(0, 64, Channel::staticMetaObject) }, { "B", new RadioLimitRefList(0, 64, Channel::staticMetaObject) }, { "anytone", new RadioLimitIgnored(RadioLimitIssue::Hint) } // ignore AnyTone extensions } ) ); /* Define limits for scan lists. */ add("scanlists", new RadioLimitList( ScanList::staticMetaObject, 0, 250, new RadioLimitObject{ { "name", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, { "primary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "secondary", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "revert", new RadioLimitObjRef(Channel::staticMetaObject, true) }, { "channels", new RadioLimitRefList(0, 31, Channel::staticMetaObject) } }) ); /* Define limits for positioning systems in case, this is a MD-380G/MD-390G. */ add("positioning", new RadioLimitList({ { GPSSystem::staticMetaObject, 0, 16, new RadioLimitObject { { "name", new RadioLimitStringIgnored() }, { "period", new RadioLimitUInt(0, 7650) }, { "contact", new RadioLimitObjRef(DMRContact::staticMetaObject, false) }, { "revert", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } }, { APRSSystem::staticMetaObject, 0, -1, new RadioLimitIgnored() } }) ); /* Check encryption keys. */ add("commercial", new RadioLimitItem { {"encryptionKeys", new RadioLimitList { {BasicEncryptionKey::staticMetaObject, 0, TyTCodeplug::EncryptionElement::Limit::basicKeys(), new RadioLimitObject { {"name", new RadioLimitIgnored()}, {"key", new RadioLimitStringRegEx("[0-9a-fA-F]{4}")} }}, {AESEncryptionKey::staticMetaObject, 0, TyTCodeplug::EncryptionElement::Limit::advancedKeys(), new RadioLimitObject { {"name", new RadioLimitIgnored()}, {"key", new RadioLimitStringRegEx("[0-9a-fA-F]{32}")} }} } } }); /* Ignore roaming zones. */ add("roaming", new RadioLimitList( ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint) ) ); } qdmr-0.12.3/lib/uv390_limits.hh000066400000000000000000000005171501654372000161040ustar00rootroot00000000000000#ifndef UV390LIMITS_HH #define UV390LIMITS_HH #include "radiolimits.hh" /** Implements the configuration limits for the TyT MD-UV390 and Retevis RT3S. * @ingroup uv390 */ class UV390Limits : public RadioLimits { Q_OBJECT public: /** Constructor. */ explicit UV390Limits(QObject *parent=nullptr); }; #endif // UV390LIMITS_HH qdmr-0.12.3/lib/visitor.cc000066400000000000000000000172121501654372000153220ustar00rootroot00000000000000#include "visitor.hh" #include "config.hh" #include "configobject.hh" #include "configreference.hh" #include "logger.hh" Visitor::Visitor() { // Pass... } Visitor::~Visitor() { // pass... } bool Visitor::process(Config *config, const ErrorStack &err) { return this->processItem(config, err); } bool Visitor::processItem(ConfigItem *item, const ErrorStack &err) { // Process all properties const QMetaObject *meta = item->metaObject(); for (int p=QObject::staticMetaObject.propertyCount(); ppropertyCount(); p++) { QMetaProperty prop = meta->property(p); if (! prop.isValid()) { logWarn() << "Found invalid property at index " << p << " in an instance of '" << meta->className() << "'. Skip."; continue; } if (! this->processProperty(item, prop, err)) { errMsg(err) << "While processing property '" << prop.name() << "' of '" << meta->className() << "'."; return false; } } return true; } bool Visitor::processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err) { if (prop.isEnumType()) { if (! this->processEnum(item, prop, err)) { errMsg(err) << "While processing enum '" << prop.name() << "' of '" << item->metaObject()->className() << "'."; return false; } } else if (QString("bool") == prop.typeName()) { if (! this->processBool(item, prop, err)) { errMsg(err) << "While processing boolean '" << prop.name() << "' of '" << item->metaObject()->className() << "'."; return false; } } else if (QString("int") == prop.typeName()) { if (! this->processInt(item, prop, err)) { errMsg(err) << "While processing integer '" << prop.name() << "' of '" << item->metaObject()->className() << "'."; return false; } } else if (QString("uint") == prop.typeName()) { if (! this->processUInt(item, prop, err)) { errMsg(err) << "While processing unsigned integer '" << prop.name() << "' of '" << item->metaObject()->className() << "'."; return false; } } else if (QString("double") == prop.typeName()) { if (! this->processDouble(item, prop, err)) { errMsg(err) << "While processing double '" << prop.name() << "' of '" << item->metaObject()->className() << "'."; return false; } } else if (QString("QString") == prop.typeName()) { if (! this->processString(item, prop, err)) { errMsg(err) << "While processing string '" << prop.name() << "' of '" << item->metaObject()->className() << "'."; return false; } } else if (QString("Frequency") == prop.typeName()) { if (! this->processFrequency(item, prop, err)) { errMsg(err) << "While processing frequency '" << prop.name() << "' of '" << item->metaObject()->className() << "'."; return false; } } else if (QString("Interval") == prop.typeName()) { if (! this->processInterval(item, prop, err)) { errMsg(err) << "While processing frequency '" << prop.name() << "' of '" << item->metaObject()->className() << "'."; return false; } } else if (QString("SelectiveCall") == prop.typeName()) { if (! this->processSelectiveCall(item, prop, err)) { errMsg(err) << "While processing frequency '" << prop.name() << "' of '" << item->metaObject()->className() << "'."; return false; } } else if (ConfigObjectReference *ref = prop.read(item).value()) { if (! this->processReference(ref, err)) { errMsg(err) << "While processing reference '" << prop.name() << "' of '" << item->metaObject()->className() << "'."; return false; } } else if (ConfigObjectRefList *refs = prop.read(item).value()) { if (! this->processList(refs, err)) { errMsg(err) << "While processing reference list '" << prop.name() << "' of '" << item->metaObject()->className() << "'."; return false; } } else if (propIsInstance(prop)) { ConfigItem *pitem = prop.read(item).value(); // Some items, held as writeable properties might be null (e.g., extensions) if (prop.isWritable() && (nullptr == pitem)) return true; // Go for it if (! this->processItem(pitem, err)) { errMsg(err) << "While processing item '" << prop.name() << "' of '" << item->metaObject()->className() << "'."; return false; } } else if (ConfigObjectList *lst = prop.read(item).value()) { if (! this->processList(lst, err)) { errMsg(err) << "While processing reference list '" << prop.name() << "' of '" << item->metaObject()->className() << "'."; return false; } } else { if (! this->processUnknownType(item, prop, err)) { errMsg(err) << "While processing property '" << prop.name() << "' of '" << item->metaObject()->className() << "' of unknown type."; return false; } } return true; } bool Visitor::processBool(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) { Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err) // Does nothing, return true; return true; } bool Visitor::processEnum(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) { Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err) // Does nothing, return true; return true; } bool Visitor::processInt(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) { Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err) // Does nothing, return true; return true; } bool Visitor::processUInt(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) { Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err) // Does nothing, return true; return true; } bool Visitor::processDouble(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) { Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err) // Does nothing, return true; return true; } bool Visitor::processString(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) { Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err) // Does nothing, return true; return true; } bool Visitor::processFrequency(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) { Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err) // Does nothing, return true; return true; } bool Visitor::processInterval(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) { Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err) // Does nothing, return true; return true; } bool Visitor::processSelectiveCall(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) { Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err) // Does nothing, return true; return true; } bool Visitor::processUnknownType(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) { errMsg(err) << "Cannot handle property '" << prop.name() << "' of '" << parent->metaObject()->className() << "': Unknown type '" << prop.typeName() << "'."; return false; } bool Visitor::processReference(ConfigObjectReference* ref, const ErrorStack &err) { Q_UNUSED(ref); Q_UNUSED(err) // Does nothing, returns true; return true; } bool Visitor::processList(AbstractConfigObjectList *list, const ErrorStack &err) { if (ConfigObjectList *objList = qobject_cast(list)) { for (int i=0; icount(); i++) { if (! this->processItem(objList->get(i), err)) { errMsg(err) << "While processing object list."; return false; } } } return true; } qdmr-0.12.3/lib/visitor.hh000066400000000000000000000124551501654372000153400ustar00rootroot00000000000000#ifndef VISITOR_HH #define VISITOR_HH #include #include "errorstack.hh" // Forward declarations class Config; class ConfigItem; class ConfigObjectReference; class AbstractConfigObjectList; /** Base visitor class for the config tree. * * This class can be used to implement a convenient tree taversal for the entry configuration. * * @ingroup config */ class Visitor { protected: /** Hidden constructor. */ Visitor(); public: /** Destructor. */ virtual ~Visitor(); /** Traverses the properties of the configuration recursively. * @returns @c true on success. Error information can be found in the error stack, if passed. */ virtual bool process(Config *config, const ErrorStack &err=ErrorStack()); /** Processes the specified property of the item. * This method dispatches to the type-specific methods like @c processEnum, @c processBool, * @c processInt, @c processUInt, @c processDouble, @c processString, @c processItem, * @c processReference, @c processList, and @c processUnknownType, depending on the type of the * property. Do not override this method unless you need to handle all properties differently. */ virtual bool processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack()); /** Handles an enum typed property. * @param item Specifies the config item holding this property. * @param prop Specifies the property. * @param err Specifies the error stack to pass on. */ virtual bool processEnum(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack()); /** Handles a boolean typed property. * @param item Specifies the config item holding this property. * @param prop Specifies the property. * @param err Specifies the error stack to pass on. */ virtual bool processBool(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack()); /** Handles an integer typed property. * @param item Specifies the config item holding this property. * @param prop Specifies the property. * @param err Specifies the error stack to pass on. */ virtual bool processInt(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack()); /** Handles an unsigned integer typed property. * @param item Specifies the config item holding this property. * @param prop Specifies the property. * @param err Specifies the error stack to pass on. */ virtual bool processUInt(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack()); /** Handles a double precision float typed property. * @param item Specifies the config item holding this property. * @param prop Specifies the property. * @param err Specifies the error stack to pass on. */ virtual bool processDouble(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack()); /** Handles a string typed property. * @param item Specifies the config item holding this property. * @param prop Specifies the property. * @param err Specifies the error stack to pass on. */ virtual bool processString(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack()); /** Handles a @c Frequency typed property. * @param item Specifies the config item holding this property. * @param prop Specifies the property. * @param err Specifies the error stack to pass on. */ virtual bool processFrequency(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack()); /** Handles a @c Interval typed property. * @param item Specifies the config item holding this property. * @param prop Specifies the property. * @param err Specifies the error stack to pass on. */ virtual bool processInterval(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack()); /** Handles a @c SelectiveCall typed property. * @param item Specifies the config item holding this property. * @param prop Specifies the property. * @param err Specifies the error stack to pass on. */ virtual bool processSelectiveCall(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack()); /** Handles a property of unknown type. * Returns always @c false. * @param item Specifies the config item holding this property. * @param prop Specifies the property. * @param err Specifies the error stack to pass on. */ virtual bool processUnknownType(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack()); /** Traverses the specified config item. * This method calls @c processProperty on all properties of the item. */ virtual bool processItem(ConfigItem *item, const ErrorStack &err=ErrorStack()); /** Traverses the list of objects or references. * If @c list is a list of @c ConfigItem, the visitor will traverse those by calling * @c processItem on every of these element. If it is a list of @c ConfigObjectRef references, * the visitor stop here and simply return @c true. By default, the visitor does not follow * references. */ virtual bool processList(AbstractConfigObjectList *list, const ErrorStack &err=ErrorStack()); /** Handles references to config objects. * By default, the method will simply return @c true. The visitor does not follow references. */ virtual bool processReference(ConfigObjectReference*, const ErrorStack &err=ErrorStack()); }; #endif // VISITOR_HH qdmr-0.12.3/lib/zone.cc000066400000000000000000000063201501654372000145740ustar00rootroot00000000000000#include "zone.hh" #include "channel.hh" #include "config.hh" #include #include #include #include #include #include #include #include /* ********************************************************************************************* * * Implementation of Zone * ********************************************************************************************* */ Zone::Zone(QObject *parent) : ConfigObject(parent), _A(), _B(), _anytone(nullptr) { connect(&_A, SIGNAL(elementAdded(int)), this, SIGNAL(modified())); connect(&_A, SIGNAL(elementRemoved(int)), this, SIGNAL(modified())); connect(&_B, SIGNAL(elementAdded(int)), this, SIGNAL(modified())); connect(&_B, SIGNAL(elementRemoved(int)), this, SIGNAL(modified())); } Zone::Zone(const QString &name, QObject *parent) : ConfigObject(name, parent), _A(), _B(), _anytone(nullptr) { connect(&_A, SIGNAL(elementAdded(int)), this, SIGNAL(modified())); connect(&_A, SIGNAL(elementRemoved(int)), this, SIGNAL(modified())); connect(&_B, SIGNAL(elementAdded(int)), this, SIGNAL(modified())); connect(&_B, SIGNAL(elementRemoved(int)), this, SIGNAL(modified())); } Zone & Zone::operator =(const Zone &other) { copy(other); return *this; } ConfigItem * Zone::clone() const { Zone *z = new Zone(); if (! z->copy(*this)) { z->deleteLater(); return nullptr; } return z; } void Zone::clear() { _name.clear(); _A.clear(); _B.clear(); } const ChannelRefList * Zone::A() const { return &_A; } ChannelRefList * Zone::A() { return &_A; } const ChannelRefList * Zone::B() const { return &_B; } ChannelRefList * Zone::B() { return &_B; } bool Zone::contains(Channel *obj) const { return _A.has(obj) || _B.has(obj); } AnytoneZoneExtension * Zone::anytoneExtension() const { return _anytone; } void Zone::setAnytoneExtension(AnytoneZoneExtension *ext) { if (_anytone == ext) return; if (_anytone) { disconnect(_anytone, SIGNAL(modified(ConfigItem*)), this, SIGNAL(modified(ConfigItem*))); _anytone->deleteLater(); } _anytone = ext; if (_anytone) { _anytone->setParent(this); connect(_anytone, SIGNAL(modified(ConfigItem*)), this, SIGNAL(modified(ConfigItem*))); } } /* ********************************************************************************************* * * Implementation of ZoneList * ********************************************************************************************* */ ZoneList::ZoneList(QObject *parent) : ConfigObjectList(Zone::staticMetaObject, parent) { // pass... } Zone * ZoneList::zone(int idx) const { if (ConfigItem *obj = get(idx)) return obj->as(); return nullptr; } int ZoneList::add(ConfigObject *obj, int row, bool unique) { if (obj && obj->is()) return ConfigObjectList::add(obj, row, unique); return -1; } ConfigItem * ZoneList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { Q_UNUSED(ctx) if (! node) return nullptr; if (! node.IsMap()) { errMsg(err) << node.Mark().line << ":" << node.Mark().column << ": Cannot create zone: Expected object."; return nullptr; } return new Zone(); } qdmr-0.12.3/lib/zone.hh000066400000000000000000000045161501654372000146130ustar00rootroot00000000000000#ifndef ZONE_HH #define ZONE_HH #include #include "configobject.hh" #include "configreference.hh" #include "anytone_extension.hh" class Config; /** Represents a zone within the generic configuration. * @ingroup conf */ class Zone : public ConfigObject { Q_OBJECT Q_CLASSINFO("IdPrefix", "zone") /** The A channels. */ Q_PROPERTY(ChannelRefList* A READ A) /** The B channels. */ Q_PROPERTY(ChannelRefList* B READ B) /** The AnyTone extensions. */ Q_PROPERTY(AnytoneZoneExtension* anytone READ anytoneExtension WRITE setAnytoneExtension) public: /** Default constructor. */ Q_INVOKABLE explicit Zone(QObject *parent=nullptr); /** Constructs an empty Zone with the given name. */ Zone(const QString &name, QObject *parent = nullptr); /** Copies the given zone. */ Zone &operator =(const Zone &other); ConfigItem *clone() const; /** Clears this zone. */ void clear(); /** Returns the list of channels for VFO A in this zone. */ const ChannelRefList *A() const; /** Returns the list of channels for VFO A in this zone. */ ChannelRefList* A(); /** Returns the list of channels for VFO B in this zone. */ const ChannelRefList *B() const; /** Returns the list of channels for VFO B in this zone. */ ChannelRefList* B(); /** Returns @c true, if the zone contains the given channel. */ bool contains(Channel *obj) const; /** Returns the AnyTone extension. */ AnytoneZoneExtension *anytoneExtension() const; /** Sets the AnyTone extension. */ void setAnytoneExtension(AnytoneZoneExtension *ext); signals: /** Gets emitted whenever the zone gets modified. */ void modified(); protected: /** List of channels for VFO A. */ ChannelRefList _A; /** List of channels for VFO B. */ ChannelRefList _B; /** Owns the AnyTone extensions. */ AnytoneZoneExtension *_anytone; }; /** Represents the list of zones within the generic configuration. * @ingroup conf */ class ZoneList : public ConfigObjectList { Q_OBJECT public: /** Constructs an empty list of zones. */ explicit ZoneList(QObject *parent = nullptr); /** Returns the zone at the given index. */ Zone *zone(int idx) const; int add(ConfigObject *obj, int row=-1, bool unique=true); public: ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()); }; #endif // ZONE_HH qdmr-0.12.3/shared/000077500000000000000000000000001501654372000140115ustar00rootroot00000000000000qdmr-0.12.3/shared/aprs/000077500000000000000000000000001501654372000147565ustar00rootroot00000000000000qdmr-0.12.3/shared/aprs/aprs-symbols-24-0.png000066400000000000000000001132771501654372000205120ustar00rootroot00000000000000PNG  IHDRftEXtSoftwareAdobe ImageReadyqe<aIDATx} \\a߇5 W3$1$qah"CӴF .?jV_me6.!DI! !þϹaC\;w=s111qN*c0&?6YbrdLE3 ?8U(${Ä$O#R?-a47X/&CX#љ|ʹGU|ޅc~S|$H xu%YA#&IPrM&eWfVm V4j|Lr7aʡ4J^ &RZ_ٌħ΄6Tc2IF3CPa3 HM@_u_2~^eZ\.@-/W)Iqqs?^/B DyP^?% S3&NjL&L͘C1+0L{,$P"[Bw-<'-%o> 8pdXsQV(>%K2i=heHZF rɓ's=d}⑏\cp),W&5saGg~y[k<ǎj{S6˯P٦3{̝3]|WlCes"868Ң1-I(X]Dp̗ضmlmذ!˯߾خv|8wL0sѹ rPz=T`<cy8$dRtK *N|ƣ? (?O~46L ~`?5Àq@69Iݞ w;`_FxU)-Wn(G{8ؿWk'6Ou68}?Z<B:?xoྛyϙrH_*<{p\ؘW " Tf醴_*x<|'OBܒ sB 5 "0@ 2[`b}"J( nHGBJO]Dz*K`|<ٲh٬}Ѱ_~9<_1r0s[ 0,EIn!+|N׆/i$Kh Aկk i/OB"KY,l=HehXvAh( "zA{Nf:x=ݻupxCpHc aVBOI TQ1xZ ΙCq| *uF8~ψ?Q{ E`¡/?H\RXz,:2< BD-42j`_-h~bX54O "I@L}}0dIHCHxm@吗*{֭JՊbsa6D-: {rZz[D43;hT_5Kz;YY"%xA"@$<^Q58h<Fj~ǻvS--1~|jg\33gg3njWBMk>s!ۆ ),N6o-+8zsLkY;0P(@%^SAxTShm،8$0?q [;&ҠO3@FPh T ¶ uľ*Hjjj?dlYθ'\Z:Fڡ%0x@}'Yʁ3E^ _,0WH6_>iFU&}2F~uZ"D kMkꁚF$M ;iDPXȑ3nUyIؿ?3gb򅮮ZZ =s9rjRʾE0ҥPUUG!04B` PyP$O# C#hݱLMO\\N%6 6\r5OcKk7އ_8:!JNDe@~gXϘG1Zb 4Db? #L"܈;GWZ{v/~oVh 5@r ;CែOa !ƍa{:DX;U@DdU*ob%XΚ -FCů vt Tr©_~sh`L ,lhF}D@tJqs~}b}U?}--v~# <^M:5? fLcnCCԝ9À?11zzz`+rùs'/^ pPƦFl80װ>(ǛQ@ :Tp/ 1TBܝuLy 6Ya5& MYM-{h2i{HP&]\Ww>xI(ڇ"(tPa!SHc7Pօ{X&W}hJN[.k'0`s͇IqQwPOKiG:O Z7o-@-x I'M?+s ?4w1`/J'. wK/}l &Uڐ L> p'6`;vr*(ˏkEr!Ҳ- i"sQh()Xi.Kx: tѢEA|]cc^P$qe7h?ѡ2 JSRRd]w=>CXSa=z~65BTrH ڵ a&:w"j>85>XO#H|}SuC&qoM4%C1sѸ)X)IgcHAnÝ$fl~ M07WED0\'!No DɓD~ ЂDI֯kPq-EA&P{46] Y}A͙n0TRRv5@|s) ig?m$[ D{-*n5:3 HmBIe8mmm|˖- r,\P ;vP;vL?/[L.')&M ;ן<HuuK+lAOϦ HҬ#ZwQOwde)gVZ P&>`{J6uq3b [4˵A_ّˇɓ9 -I+$͟ @ģZPPeΒc2 Ig%JeOz ~7=m@)xXW!L<|)iA\a͐tlO0khуxXX̏4}!\>4> Xb@6&dV!M_{?͎ۋ/^'!!^1mtHL}}l zthlݵkCt @ nA\@ѮEa ΒyOt6{|pTV0oW2buCOvM_,]m6Hn;֗St=fsΐ`-ʶ˳]q |Y j0ՙuXܻ: g꠾3Pf{j@MZf"S{x$ x!l_wgp5Z NJ5QBkQvc%FxP^,]-W̟oDzĆgn"A^Uܐ \"kҏiWKCк6ͅUr{$UOGF̤u|v ȹwly*ו|E*J-;s!5CChI[ @Xg˙Ia^$ F,r h'<5d9;+B t2|&xUj,\A^Qzov" f]Y P.-Opp>&z#e}~{\_)޾2vF핢Pȉͪ#o"ug7OIO=쿶+JA#u%Cy\-g߿<[`U>j82"$PΏ pKOk׮U߼%FW?HJX=ɘɘɘɘɘɘɘɘXD2Vc2&Gꖱ=C!4NZdIUҢܹh@I+.2{(s%ئ5/yq  JgF+R=~ýNQ3<PrE5k֬K/T}u,a7I}vE]w-OLr퀿Y r퀿# ڨFS_yk={NB9Bj”S *DdmY2œϛ@rJEo"7n[ ԩS|A @*{p-+mhgq|>4=Λǖy\>4ǖ?A4#Q׊qr^L!X~B3 5d<$.F>!2+;x\O?tSO=%_t)"*m__B[OR6 09hG[G?y^!"\<d: $PGMFG&(ҡ t-ë{a ј+1ݮAq@P[<`M[h >S$8~8;E؂t:::h kaIPCdCdupJmy`JI ,AҼ={ RZp9s{1֏6w֏ͼ1j(]>,)^+{~~"47/Bv 3pM_*+ PZz IT*fpuTgVZҢsΌǒ/zue"jʳ/ĴZ{9/V\??'zveU0 /onk)62a*w'?rmC~^d{ͳYдl+rX<}[QZ aJ%ЋCiiܓ܍L D_˝<,'K/^ݤG2lz .,99h@mCYTz[)b)&ʧR~5o2BRxε}WV7kº:}j4YZZj9'̴N3˖,qA gy񽼏?߇c4iL9q>y0d]Guea725_'d 'Y}=_?? ` nm#JЮbFg&T >>?ЏA+(j$wgt0a?v}ig|NL*,qٞ;H %t@o؇굵o')wDdve23'%'Ɉ@ʆi6*˯T&?)9UKVZR~_\\5D@9 )QWJ^9gD@@5h>uACM ,H $o~_!eK!I~ܶ` 2ui#0,s_>u!>'zkyؤ ClLt,,Hzh.۝8Q~6",TӱVy"8 U71ys56v hio?A @Hw)*R !%_t{g ﭑyNp[zY,oJv64xӺ_W-|.X2.4]7\=6-#J=Kգն/+k3#%W"^`|cl~w9+:ZԙJIVL``~Cc 0@e -2gSRhDQ0ɩJ J|]imZ(7"nq)^xHeuw<̚( 51"X_}/b)^u9t 7fκٗ, M>?s-%")ê_o.!^z)>>,іD5~ eaW὏*a޲S]!_ߨ y4+K?rq| 45 z-gbм~3O! 8$泑-_7і tۇiWVK;+SfwE۹e@_/<]"myf F(tl\4%wڶ |>ꎋKVY%8?\G9 " 7Q$o {c)E|^ɛwIͅ*28H<\w cψ!(p@ ׏/b= 28";­"qdԝG #c12G߬ݩSS̘P)`0ll[X]Yi5kw|֮OG60/~+e%&Ht.òӧO"89;r"tZѠ0b҂%eL(uuuqW{[!.:f#@48dDN W6I$X{=ņO$d0nuH qUoUr1@B~%DV,Kkr3VP,%HMY:GAvDܚ<+nYs0UgL)a"KJJ zHcǜzYOK_,JxQQKY*̮!w8|DVEk!`gA௲s ;D@THDEo±1wχ8mIx"-PN4 :|=Q=k |z ,dGۂcm"kx >W@V,^W ?~s*vqhd$z_!<\q;*{H@=>>~gg'ڻzfx[ jP>D&ojGkAgL%ijcF{y!x&]^,3Hۇ h~Wƹ}8 X}~ V:]'pqzO>7z׭D$1"%bBvv6s5@`9.g0e<2xEǻ::a (QËB\;\?E@ 펅0HnHc'h4}%+P Qk.ZhpQ8 ȣ;EHs)'BBB$0 IBSddP֕uiH|(]v vpڀY95衲Ӱ5~rga<8)QP^+ꯠ^ ~)A8e@R_̙3U'*1l`_|^ $C@(b 2HI]7Boo+c\Cg4$FN`p M!Z_hV|yLnBdP"D] AANǕ}cFϵ%m+̌1ي*~޲ja73s!^>'BZ?JBE^Jk0q2 MРH9opփFDEP/q>_Ʈ }ai?_~G 9 hn*?r? o) a] eVV|/呏y(ꫯ644dX{?DFl/y2d;o#xo9TS햁&{mڴi4w$P64 A63b8yEuleDH3DjӀ/iiqC DQA1hfttcYAA. ^iX쩼Uq(~&Y'f1!Pvg6n^4Nix?AA- P]`yE)M9f72WW?hQn68칋I0Чdv&x0j2q ]W}-< gEj  7q>f_@088,fg:P1>[f#%yyww75uߖ p' P6tQ .L$-[JW\ؘCnҴ >yIgښ-/ 6@RRGdo2z?xꫯ@% {a4DcFF1䓉xGq|sԜeա5 ^uV }zKh◭4dn>d`TuCz4%? kzEVL拚T3Ώ"# i,@ ;\Y Cc $i"0rYkz')-NN }W;A!@_ 7KW~XR'pg!<^(AЗIb) H<2)$MNβqdজ1cՍ.l|baVh(z|!jZמ.CM 21B30D@`J| UiOMGV+ۥ>eu+1߲gPQij@Y$Gfaa⁝}&JOK0q@S3liܢU-ĜoΕ8 7dtql }݃*{x/ɻol^ѱVWz/oH ^2e=+RGXBY$Vq/5_50:>=Mq^wK5q[!88gA0?6@>~#2.Ft;9~~~Xqѯ+,5B /g9"i,KGBI(HC#y}j"r~9Cb\BVT̬M8S+oio-\@t_Xph|D-'ڱcG?Ii@j{;D0ǝ]ݑf8+[F'[gX(_Xٓ?Ku,WuPY.-Bi<7v%` $+vHxbK/D Ocڡ̘ot`xJyMKl?@to9K`GQ[ŵgtZlszme lV %ݽpx!*v J< hydWzFb?m]~)vfya?m PϷQ9˕) PSGPwbZH &gvu-l \@AV ˥BN> B(۾W^yd…,'UA)K~jӫer_$| ,؎`Vf'K9 A,y̫ "|O{FhsBnn"իWYoCw^xg4Yc{ɘ{QLe_TKG(ٳS"@_;jƪfLdLdLdLdLdLdLdLZ]ѷxI($`Vc2&Έo;-I)0b24Rvd &T콒3Ha}ܒ7)ybD!z( yً^vI90akEcD3KI^s/ ^ZYdQ`]EKȮ]Vi> uw%"XK8(U.+ Wx |a)06, HQX,~x/nIjr@ 'N6KͷJ=4-@"Ќ -^mf&ZCzRfZ>Aף$P!Wdpn^͛!(?a <\#Z>>>z󵵵gQftxC*WBKKEa2ٯW^~H8jEI~yh hhKRߙrҜ!Pw{5 ͷQ(J~Lm׳.cArN kW`[> >g{X6/r -}ٖ;|Ux~c795ymt3[ͮGƃtf~3:={k6)@BpVY]ݢ&{jnuNnfOP_I4Ye V'0aBHN2A}s`w+IY^;[W7uRolhm::FM[\4.}xMfd7ID;H#];ir꛺`VKB'Fe@T|=HFR ༁=2`T\@ڿ m-pnFإs:kL,,0܊*<&9݈_>Wѧ P~1/Ih lSh6r/x Tllu0N Q0gVT}v:uqk?<~Q珯?^_iW 2thn7<=.-s_F{%K%p@Xom*c v7C6ճ)y]X]pٌ~] 0zI ̙*,g$(m(\ɯ1&3e^ !Y'(g;K2H #;^r`ʹ= aKXn >S*Znsa} ReJ*&%;Х/y&&pn>/n!ęWCJ .L3d^CP̨悙L&Q0byg{؁㾯i'eA!B;LfV–T0Mi(dLGy"gyܖ4 ߂EqA`Pe _ Ĵ `]pRJp%F;hN+kA!:k9CҍG3? ߟ m)_DVx~r~۪[s5ƍvsrߋG\ط/QY[KL58Eco`}n^T ݭx uTÂUjSzxce2ܱigH>{2zZ?~T͕Uy&b޼'4=ZD>w}K9M~z$7c"$ r\o֑+ H8|PpWH@ǰ;T:s{FDl|ͯ$7d ]koi}ϵOGEiW;( c=}hW&!AP'X}AV\@-apY`o8Tc{2?\!iIwͮ/sQ=tMÑvH8uգPPX۩^DjP߿plmj42shY+@ KЋ03mM@ yw }ɂUfd-p;O D@Y@Bn:||kPJ%`kDlHz=|~ؒ rI~[Вq{D|LkYĤr;HDc0C. ,- u0X'vuA/vKB~Eo5t5Mp\ N 9:;bXVY^9Y۰&B~ ]ʹv.ӫڜhpw{F@306k!@/'y컋 v5 ${G ar̯I`D\S"ֵ{7ƶ"_"LJ:p!kKȊ*B;0Kk@T.dRq۸q0(K.cǎu:Ek F,BOeO/~D=lr$9 H{1`͛xO +C\J̵x_)Gaӟb%ný@8;`&bI)<}熇LMm]7W~+5LM>000'6m:u %߭XJp?|woz3٩Q CDvmzU,ΞI wv"VM%esIutjz$xk"&sNPu}oiE6Ht:b#W;_Zz{ʂ``N O}C COn*R,r#92P_-o|zvW9cŵvhl4y$v_ 7 U%߃4ؘ|aMzᠡ(>N7xC{!>R?$M˚>xB| $#gzܹs b5~S5Q~]0ؘ[,z}:zo 0 CncnAs規†fpëtٔi)3gA`g\4H&`Ɂ"~O^ kJܤ0NK"#|ninkJa_jBZQV1Tb D0 nr B/\H,`ƕ%ESSD2g 3PIprh""r!׳{bx- aTfzB}P!0@.8im?yd_ NKՇFF_vϯQ}VWׇB|DʺCh?I,})88(/Hp syd81Y]"N!|%l}^2k$4${l[Va96@ \(S֛pm^AlZHÿ| ##J Yt$ij,`ʍ삹VJ 7EA6aNpXsӲ?7(_+ }O?km\;ݍXf+dzw#}g5tt}pe01hI􁂲FX2Q}b~y3 pb C)v@OoFG4R FX8;} b p*H.#g#Ӭ_PJ-?.#+hJ֫\{뮻R"}ǏFKJY>>0a]`` ئ U$ȝq@,ψ Z^>U0}hV!O;9i3 y/9-9tHY"u7"C:Ab s{N ܆F"~u35R}}c9"*zVJ#X8w &/~ !K+[K gBs֨CTEko( cpX֎ IJJ F3;55i( @CC6EdڵN|M= ZkrCF=hpg"x^Ö EY;r1C t2^u +yM7[RRb@^F3Bw!Q"d$,{c:?;ap~Hn=:X%7Bn74!p&iU#tI! `f ngl,QA62h0tvsjf-[!t܄޷+'('P=h#εv0sޑ% wECEG~G_Ƀvm|NJIxxI):41'``i?j9[e_oȜ *Ng;M+|8͔Lsb/<P&jټbЕ}_GsNm{$⅖Njzn  `,ijvf3Mb0"MZVT*S8S=oFD:\<#Qf=ѻ+|lg{|5Ņ47pAP>q!_hWJ'BZsAuuY߳gψ|'?5p \[:Gsm0fDz ?lbWVsw``VS||<W\qyjP׺pIg-3VL= ebFG/Ȼ]/nCTh EۉZ/MzZ:HYH5ev4x rOC>̹(3=: r|O:3l}zmJ=9eL5}Fjkj'5ci`s>ţ`p [ңDm*O:׹ھЂ]ÓH7.m3J~ *%I~x( **@Ѱ;} HS:{ $`n)+s[ᗫ^.9E@P(_. U֦ NS[S!7I?5k|Vd=P Y6H@ *I2uAU`+K- ߫ :H)P afhH?}ر]4wANOZ[q.,06tCdEk LM[ 6CP;ԕ`g|IaqIB!ɊTaSZgA5j:6 n _fG_nv 3ɊK9 -Aiv)KR$0IC$~&@zjX?+L6%- غX&w>) +xx|m5G< OCXX֮] 7|s oXQVN_Hh=\F!`pj-qeq[G9Swi'`X>7B*;8{1JO Rc)2e~Ç;ddHEo!%.v }@7 (̍{znbMMcC5MFE'\϶vrdV&d"3]`; @5&C_gV |~N7XUew꽩EU1p 8^s֍WA$ 56a砭/>;}oE펞/(((U^NK^3-T*U_q! h]w>pmÔdh^#1!T!wu[ǃ\A+}]x`ƌ,_POҬ[7|ɓ'Jb555>d" SSSi:94hΞ=[<8p`V@W~Ȗ5Y-JB@@# XBWȎi.Z1 NR"(VC_[ V Mo9Qn /'1E󽓤[|YS>+Oe10)_5tC'l_KeG𷞽+pyc!5+u#xlCDns$NP2C}[gnHtPu67ъƞЏ}rrJ|Or!}" X,_ر'F !9s&[Ã4~4~" .XZJ14"aֵM55Qt2?gɓ  u4hF]}pMWԆd@YW# Uwʅ$Vl> 'D"up#ym0Ͻ@ L<?& Ck#0cg%e;QA,upqYd~e_9NwUB\oo׾JH^!ڿұFcGBTjު_ω^$Ў`9}s}66*n`|hHhdZĹH.Cvi)kJ5Vl\Ч3YJtJ%X&[s8Y<Ha:՗5u!Gppl>)G|"~u"g\HB7c i*6hK]'Rxb3DWvLM WJYz xsv opۼζA%~bؼe7R𶄇% @"ڣrcZƩ)%?< JKoq>A?z0鼐0ҲqX~mTaQ~sgHd=}*?deZyrmj};ܶxq׬Q;Y*Zܓb' Gt~:GuG0guuu2X<@&v%%\HaC.oC=H rB}0niia OZ%`gʕ.Ym3}Ol+D[7\@ @0tq&C5KbĮOA%6` Hdf>fIIfV%:,L˯lⴳ K}otJ[>h)ψV) 9f:wB^[\wrU)@ ^"Q_ 9Y(,61NV| {$`2Þ9hW=vmy9ނwGRDhL3믡?f.Y xA''=v 1i'ef?}/smkO̟?ϛ7O@,Tנ%&xϫ~pk!,|uk{Sʼ@z{c#m3^tJi"cvR4|Z;À>55 AF:]j?Wp*)9;+VPWGڐ7V*}&n#N r$ ŶUD5j{'\?;ZC@섘@X__5|_ŻN ۣzF2z`-"PbsEY@'0QStr䪪qI|BϞj~r`s}kwѴߙ0s;,IM5|=bYS_AG[[!x,ttc#ܶdg֯;M@\= t@{z5N;EHBƖ=L=;ɹ{G'ĚFǂA'?YDDeࢻBfHƒ|8?9kbO_i8m?2%矇~ _5TݘH5Y4\`4˗;}W_"@Fx|j*!I[=C "2pfy@NEn ~za~)_L̖]vj @@^HLˇH,j/[bE!E Y1KOOsv0X$vFa/hu15~C@ۃa/|λx=[Bu8CD?u Xr9PrS䊠vo5|aG2߯po57@͝,<ƘqQɋ`q gN p$X3YSHmK+**l;4b{AA>0PC?mNB0t vA¼tA^pY AKJl#+@ -An\Ӏ./O|ޭpkep3o\|/~ Or) EEԏNǂQ! !CPS:b  6=|܂CAm@f3].z/xAz`4 np۫֯` (h IaC E $ļFqt!rTAy)Ӌ|>OL"W#dT}Ii1p2zgq]S!k^Orm%@ Z.Ԃ 1H BHy%iS5c_#|0|ba=VgKӪLj`Y@Eb E]8 nc@?קo.{쑘 1H`„ YΝka̘@HtZf4-q!z %%!pr뗛 ֢L=cTii.5Wo8d_R {`ЯB74@CCK jln ApH0%׳΂}fR8hYt%ń71a d]w Pbgr,ƎR<1\g/H_5b?ӡEScD渇 rbDU?nWXc  _%)^с? <h0f$B ѡ($  L6[YY~b2A+Ѿ^޽;xyyY$t "ndbFZŜoΒmW^a7.^HgK<5: `LEB+g&haB \ Hp.o.lL)RXX!6p@et;(9\U5@6k#=sjvV+D* Ω׃^_:0t4^v&1:_'㲣/=96wV.:̙3hC}}}=zɲOԈO]IEpTWc[ :ׇãhClf ^e/^%L`OsR\'e$y$$sG1R?K⠱Ch6QǦ-* SZU@tD%+ 8rR<<<>~5|e}BؐpT:gDhOrɼ%ɻkeqٳ'pi !E!cA} e {l ܫR1$`nh U>3`ܑbpX bC`3q;98q`WzAsdJ&'8;kFby` ψ{vqd𫮺QƵnҗ2';Y^Bj|d%!ZHU#d u_xYk#x|h` [,jhjW1DznTv<[O^Ɛ\tNT(ͨ#N~KD7UW6bШF OHLO֙ev[C)T]So=JQ˗˯?nn٢য়0N+.Z߷\XVt:;vXwNK ثI'Mѣl'?  t!A"돉qs(Vu*__W(Ȋj~ ^- $?xhLLoYlP̱d2#帥vIm+y[8N ҢeIGN{qAf5qGU$Z>5& @X/:NK /)z}%\3w#Jl!$<j׼A,pf] %L)1@yvd˻6FWUV^r~CM[1.+/&.7qHuu5ۭTPM#dQ=y;݋ZƦ7Wv_ltFq-ror!9\}oX7H*:kO$":ȂC@:C(T MqSfT㥻֬y{ÐVk\5؂"ę gZCJի[ȪwQF$HO+p{IItZqdTf!o)8p@?pcl7Q|Ss%զO2䋝kD,/I  EkjjFS\\~8_O=gOU7U]Lo]s#j/]충 -2Ϛ5Kpq pkI[tkH n[L `{3ܥ炙[-nqK{eǎn-nsFڹSyshձsjThIߑKrl׋B!Owò TSד3֥,E x.uo|S|^:nyZo:8ue%qIIIw@|4- KeI)7<${U655IzFe2(((@`w%)zU`̢b-b!;u}Y@7jںƜ=B5K*`hh4iс)yݵvhzd@PۿF$rJf˛7Vk)v۶m]?L缃p6gBgޭ$T: a bJk&B.O*2d̙@4w`hItG~(=`sW{H␥k ]3}/ݬw>d7p8;p1$fgJ2;[0=[&,[L2.w4z|.5wб;?1S(v 1 hu@H17ƻZv~~7xCm\̚5kpVoz &LDq9r^FGW^4tqJכNHCt#BÇ8%˕Q @}Dt$0ą϶|.{x/ݾ}{̐KUzJoso ;ϰ4vLμbLd,~7jFBJ't50z'?VK#AYv1w/\={_B̜m~ue.ќ*F[bHq )ozAaO:&~פiaz \  5~~ `h";rJD, G}$%}ݑ+?἖2h Y%$ g,8Ť,I[7eRϜ9c?h4*LujpA"##gGDDmm=18tu嚘b$΀?ju+xoaJHG%>p9.\J><j޽ ]s8LW@*.;ߎpK Ǿ)U1,4('mYMNZm'+: $@A`D͚VmnW0ر&$'xzajCAϐ|2@go! n^_ 4\O9$*ﹸXdҼ],JK#HH^ѿ\66ۮTBaaFR$L͌ ʠTW}ސvI-ߚX2. zkqpt;" GDxyeRFDlaE"@ mύ˃VNH>%knO;3XUv`tǛ2+CJŐF*TCZ1c𧟭 \JH m&0>S#f$kO.O>qֱN=?##,2-K,#y` իWqFB5$d`_H#`XnC_L)ҷ=(N; [ <Ou~54[sIFbI;hda0Zj(̹G~⒟=ypJm?Jf׏OaCk o!౱POO7>36J֝Rwz@DSuY,jz%0H)QzsDz=D1}|xWyܹS4o wS!(oO3J,HeF@eI'_Xkv3&<͢g3 -^n:nބS?e*ϥKIJJ[=5{$ N6ųqv? H؎=J9%d @w( |װexMJHH=# ))?,`L\̂ h%h@, $E7L(t?]lxVMmB{[U/\ oy7./3 9k}=BU]a;0O/_z\{0~…Y_lٟw?L=} B (8 _/Ѕ!1%p2Pc 3ņcckeC4@J@|%xx:sE$֔\aD/ O04813͒F__iXQ?WO o82*DP"%1@M4#A崱d?j,}@L_ZX%^b3FCt T4!25!WCV> t`0L $uq}H΂Y?z*zzY0>Sj@ VK ,ql^/2nNx|zJժw@H$V[ 5x"Vҥ*O )-uP+RD~1=~]4sA֠A[.;٤xe/&$qz$۝5yyLgy6`4h 'u͚&M.܇\krQ PNB ygacA!];/7JMvz"gY79il2`eCC"XǪ:TUĖKC/@84oTUy< \<9RSnsYd;k~2Mk 3A,iSÈk\ 55\#o="#ȻgWڰa슊 XL (V0-Ǣgx @XT  8{*P6pW 4'WL'8q" ʡBgFh袕tqHiH1 ̧o܆[%m2ԣpXMߚo.yy@B"8hQf`!g^x;UoF5B&]k}7%kQuAq* w*<{XdZLBV W~뭜k|μH;  abo Op3sdZ \s^$ + +ʩA:P^p\JjUΖ,{#3E6!Ws$Z""i[o>_! * 4I|2l"7A0\<B#zk^#P(05,p\c n !MS xʺo.kD4S[ P6P+<|[.%dEEETYY{ ;1FA)Ato>.'Va0b3G9rd +|E;+@YpjOߜ4YwD\i?>MFt_T74APh7=0 4 sweXUAL,y@z(&Y |w/t>'׃ZbKbHn޼I?O~ؿҥKju:5DO= ]Pu^~JO^%0_N!/ڿ@_M]2]BQn9Of.o,Em) <):Y+{:C^?4>)ڿ"ƃa# ܩSpE}z?ؾc!xP464@,fD]uuKC-'ɔJìYp6& 牞@`3Z;/a\M… Pڂ$aQ k!!͟`Fi94RYԬ5#$V !H- ZKAf3 %{k\C*H47Q 9'KLSJ n~78ͫK6vķzK,Lņnt'!6֪C`O7ڊ !V~z5v%uz}vh*DnXDO SRlШ@Aף»wR'k*s]?qO|TG"xx?8,}P a;TD~ǮVjίf2?>RЅ$$ضs]m w2̧@M{LKZmKM ))=6_" lO^]6 !145 45Z+T %zjxDӣwxHapY&EP 56/+bJ/0&M׮]Њ,*09'7  Dd}[=7^Ǘ~H}?QMiR(I:Δdžab1#vd&^8 [jQ.Q rϞЌGVRy'I ˗!??_kZ374rX@K rUh!䷀ yyyPij栍u$-l$\s!7d[_&i4>+E~ co}h%ݻw͛M0sQk}v@Z>~>net:8B c~O#~Jp&Á];9rf̘!C!`6ө1I kk9~713~񅊘Vbb$Gdnޜp"hpy"3{ИL?Qy* ki)ohHPE=gJU`BzdMs”ٿݴ $V*Eںu+A?v"bB<b$)_Z{^m",.T_G3ˋ@[_GHCxͻ fxaV\3?h"y{%`Dּh(mxyP׌aw_J>aԩXS믿|ϸ{-!yω|GL/ i(R%1Eg9_ѣGVii)ij&Ā%k?΂WlN$rڕZw'flov{%g#OT/AkSXh/ml6W9J9+> CV+'ϖcu:h rw9P\\^CmKgwg$N܃RE a<5RL}YYYqNZ̼k6MOutA8|5dݑ$4$!y}hgD")*gϞE2 64w Knh7 82"ᰀ 7o ߖ_&y1sÆ u[}غ,_'!i$\nSm?ZA_϶H;{WʎW6ټ|k҈6p4 nrƑ!gn > 9X#D4t-rQqROoIl@^N⑽S~֙ h3Z#&&~C.z)={hD6DB_js4>bUz`v9J`WMZ0WGpTDpt(CH#DJ4 :xЍ.!\Fׯ^=('"иn FA|0va^Vܹ?w11QG28Ǚo3"`__AQ }R^h/AW/J_pvM*o3|o7;uT~.Q?-e7lĥ5eZx+ Y,\'֯IA[[ZT[eg[2k5ڟͤ6ٚ|PWzX{o?,R 3)D5F ޴ϸpb$F~%{ `Ʉ-m~B͟|bwJ-ܥ,l=k_XX؈bV4z!Mf9Cw j~QėZ<&[rNVPW Rرc8@647|l~Y(zgöok9HYD× > i:rGw K eܸqI5|iw]'*ZF_Ο?? {[NL.K2m)!m|gDB iy䘸.N97+ubraCCl+3$f>~^G{w*{IIW0ھ֌`7/ی ' J8Oc\y pp0%t{y ;g v0 h -]PseezT+p L흻 +-M[0}Bv?L|yDUMΩJ4q[XFQ8gYERIТr%ɀZݡJ r/Ih8uG̑Tp]/ # M lq|͟ۥ' *UvVOKNݻQFm7]x6wW_cǎ?Que֭pB2!XwW*D"uFm@p0Uv8ipqj{sgˈb#@B&B]o0ʲCnS"P9KV-><3gƌ?V=^zKW T`PFw6K)3mUw$M/b.qhkנaQq? {C٧U@)b/oϑN֣CxΞTࣥnO_ZLECG`s7 Kw/)>C-3R9 6|U'@/xXE7&R;V{oST>rBvk6m߿3suz{Μ9kVX|E3/fH27" Wԟf$=粤_C{> |r@NJ*;5ĘNE{J'Ɗps},uZ@kh!fO&`OXqik577xœK +\J=JHX.UTvs0fI9k֞sucU!讇c^W bSVƅR\Y&TBffuTvv$$\q >98pӄԯ>p{YZZZ9f̘;v:(hI3r ż|lCP8zCnYgK&H3#1繫:ϖ " rAv @?*K'8q4;X U C$'qW$\ N`'9܈3s0,zX>%.#6] Km #ƞ5tpȸa^&r ~꧟~zZYEO D`Z2"HH<˜H(Q UC,;Fʜ~s9wO6cn84r]Lf.vjA?UAodH&_]]U1ueo.o4g ""oSf%UT+U.54,vo`l[tTI3nӧOmюk'nz2c>YKàqQP77 ޣٳl#l<'x⽍7QYYL|OݻqBӎ~,q z{ؔGO#$jnb/ )-`D ~:l$.-̮?ňTYkE A/M"3ʞ/iI[9 DbN.|VӚH:6_pbjnAȂͬ3qoiuY'ؠFo&ԑ8z>Ӄku|V~Y,<{CZ8$$b+n6\;1ܨi%j !am9|ixR^TV:vo8k6l_~#mDuDDQQQ'ua# CIX* ~:+CpgDų2'Y:Ͷ!`m xC,\dOb>V-.'Z"O19Śϕ ?$۸Wl␁Y3DeqQ̱*VF>vu?|$P]O8 s58kpBzu6gnt%˻$t9tw{;ݺ,CH$ͯ3ђ_,lkIsy'xH?=\U\ںXxCjj[+"$FP&3 x!/^ zuܼyn߾S,v b<KkOUNbj"hjߛ49\K!_{#bE7q?R&g3I)!oshˏc?~u\1!PdK,ԑViQ%L vi !֫vNp/ p3hmfcj)W,*ڵ ̪2[v '!!a~?/u 1k?2L5PWRfrWi E?aO0~7ET9lbV\o.} PsT3^o]ZMe 3,`qa08|ٶd-:. c@]WW~<@70.a{K E ,^J4A7wB ֊$@ܤBxnRR?_K3 z˳Z|``u b\dӦӣh4P\ ʈi_tҨÇ駟nӧO믿ūIKKK"A`z5+ .hɽ+%(% fX L37Q-Ճ+ן8X8r'pƉka,y0KŐ.C!:!!w;7نzq4pkrDmFseMB݂j'OSivXByjY%NMmk5 ,矇ScPW,Lh^H|֖voiF`o~Xg߿6&Nk{Fs&dInnnY9 jݲr=weQuZ]zO\a2[vv*"iH8;j "W~ exw- 퀟Hzz$:! nrW*Ӛ "`V1aطo_6Y͘1n݊v6M$y(hY᲼f誡8ݼ3LɹEN(/ѶA7ppJ-lCׄB.sW'fbH #DB;}+-Avw9e_zwp3<ŋi%0o< <7*11PG-NV~QbpzzAmL/p:[ς=[3q7,k,yv~lRX^x,!',ߟh)S|qlhߪ^HO=ԫd1L"t1a 9L8$ξם@P·eFC[Gw}* j]$w'#EAqd`\uYyzA;:4E7ncpDe |\;&f8Yӂnk9hV C{;QC Onn7Bð`c|%|ho^M?0޴?8! /0jG}||8p qĉ`e/^[+**Fr/X-[l%ߵVBI]ڦcZ,?l FsޕvNs- Gͺ.T gapV( jX-a`*ZTp wGɀ:u;}G`R*RKvڳUINb n47-nq[ 01"|\IENDB`qdmr-0.12.3/shared/aprs/aprs-symbols-24-1.png000066400000000000000000001216441501654372000205100ustar00rootroot00000000000000PNG  IHDRftEXtSoftwareAdobe ImageReadyqe<FIDATx] |T?o$%2AP*ꂄbIڊ&mmk VE-A\j"!Yd}y͚ Dw7o^s= xb.==]mm5rFcRZ3 <@غ5#7"#rbDu<#e0`!F0B OA8)^{ @ z]ӦJV#po]oi$jj f=*, _R߆eV`H ~9ڰK{_6>UYY vLM 0ZA3! A t==y< VAT+?Q6[/ ԿLbۿٿ[́@`٫[W]wc^#}-l XUl[ %z0+u(_s2`0P_jGD<qh/Ng|,eCiVu 8Ί}Lc2h4eW. F$3N;К\%ٖʯWt~2[xRV@CGi[௔x-nZ4i5049MvǧsɜזYox'hz[8V΍e`|~Vh<7T`H $_/Z݆ h@07! l|?K`K:4+|^3@?B 0g^ 1i}.ꍙv޽[!*ef34!O$㰞P@S3҃-FG!S Jdu;e 2%duX4H#?i:NhPvj9;7g<NQui&؍ĞIoi a%$Jè@Ա;{2?65{Ɯ!&A#5H+h:ڞV/BN @h &ۢY]S:j3YG .`%0_1[7$xŵ[4M#?.ֲJ=n<[nL4 g>Mt-]D$z'BP> , @< G|`,GH `%'W8!`Яtqh".P1nDA̸eSMEBrsd%߱_$a(e p-^̥2;;ӸT]n648 x `h'x |ƦQ4j&L`B{<%0ng7Z@ PeveoaP_7B[?Y5?]N(s#z\׋dvHh ]sXjhzc EHuày|z j&hʋ6 G^ +~Y/W_M`Zvm (\D{_[GXDPIyin4|kU,$'"c]?d G}zz z}**}e\HOg' ެ[۷{&'$hr>'RwKk~Y.Ă⋀AS][Qm2biUv@V>,`e{/8^>ֆȅnX[ooeC+\BX`{PUy`#C?Bm1+rχ/Q{geO [V,_?w >sXO4.Riocb='>+cY!\6#@;Iʫm Ȧ4Cq*d; ^n^+ݖw!fX 05]_3#8~<k z=1ic5  %p@ ~*HK5pNOq.XO5-=Hh! l‘2"ܺBXj99u1~vD|@sw2LRU$r (hVc ,_Ftؙ5$W>UǶ^4X?l(Eo  C3#k$j+֘o~%DwOgA2,s7hc$SV`xTh}.y}{c)os8P 6ľGY|WN.Fh}ɒ%}%KPQo;v6gM-)Y}g`&X`)L_uHmaNJ Bcf,JYU$xʼnbɂ[U_pUpX)@_;K7B'@+]~hj'ݰe xd 4^x,Zņu7"5&#k1P,,r(]=0"aMmɤf$ `o\ܵ82`dڕMș:C:~/瞖.;hy?1G |8s=nF b,NZI 1H( ? S Va@"wK?jAVDhz(zLxzI^7]A?_5xR>VxϫwKD]A:4--*A3XL ީ':NŇ$|̔ Iq%')ԳDPͷj+6 9m׆,ȫgCdWd=w7WC%D$>*l6^b~ DrDGʊ%1ISK( Fp ]P UEIҊpd`OU)T+t*-*;r}5NJAB`^~'xVܿdqFz`uF/rghx@:xo}z42sU=;`/<ۯnڵ kB=ģS(± &g4ߒ(K+Hy'm a$@,8W#>{)[$`[mV:Xao| k@ԧkOy晆~w)h' R)rr-_IQqP NFmP7% Vyr WTeza?X̭ L4"2p~jQJ,{%;whǐ(P$QncU!?"sNY\ьކV@cf΃vx趋/H Y;RhT)2qHxH6ţHW# ZIL'Iu=F߆\BDKZLa?jA}>H]OV}') <8!4Y7B^:nxg-.VO?Ǝ A3f`]DPQc QAY^cdj$k5z;\}P#]|ET UU{VO7Ly4({} @ !$<'GUؖyVEZ<#AbR{eA BBIl f;\ݐOtCh 6'?xaJy!{wy}z)ٱcsαΙs6Pt[ngVVƛn2ʇ7|I" FV@H,L5S ޣ3Q^1# y(AWBȝrf5__{Ye$cm ,0EqOkm+܁uV,LTFu)\h3~Ft+e::'bK=;d$À6+,}WNC d{Ew-X,,p&oVCYKy @"д|Ab$~B~G()jEI|iy\**Y-䶢ǚ:`fȉ7 ahi$>-E#ŵ-KxĎPf aH (G$l''wK׷▐ɽ!$ Kjn&%.)((7RBOZ?)XW5"z'mC!(xBH7iz+#'p){ lrbd|d t|{!鞅/? iPX#U $`7|'$q'+#2"#2"#2"#p#M0"#2"_g?]&sI z$p] vJMCߊd rۇ:}O)k{_/K=MfY djawQ}gr9YsT\oe~^ hR?*nDu/㒀aCcWʕ_Y+;tQFa%,nRhX9C\H` `);/) WIqmBo4_ ϭ 8ngC'#5_tFv=*$EuaPr\NgQ#[zk\CJmvN0 ^y݄'?P7yb) ?`GW[I5b~EdkfF5'fcR_lreNfDL%I% %~[ j0E#2"#2p`Qd=!ʂOWm J$~^ˤ0VH;N?n͕]BM0%6C bXeF PyTJca`Rc.ZLCq9 -@R*t".A~>"#2P쩘+Sk]ZZ[C5 {_[[+ipn+B)'8pXmu3$'w 1ck?TLTE`_s@wxʏVM"ۂGrLa4~sp{h)3@g'jY!@vq%$:Y75KuM2I1WƤ7222k@u֓$ v9\`m="o6]Cn9d6fFwvjۡasVQ/LP YY$I'p$oER8"}f6tx4"#_ cDӕ U% 0wP$7(g!=ftdI lmg-rHNl~$_|1|j4)1 : Y/Hs͌RS' 'g/X\0 KD  ΗAF r$SB׫ЉI)Pnd!t1>_I,ɗ</z{{a.j}O,;q޽0mNa60WX1Ѹ/,@K%|yЛW^Qj&Cl]P e#% F`hI\(1!8|DQy (>r1:CRc Cp+ WaR& g?=|h&Tf'GfJa_ q^Cildx6McY+ݤK: f\VU@e G+ia.y8ql Ki>0\pr&_~4N/1'&UdoE˩E߸{0Oʪ;8h4.l|@O.zvu@J@iP-rr*J25Cipyan6뫥,b[ f ^_-on_~ ?rGUWƱGvV_8{3)Zț7Gh*h-Dtjׁ~a7 m(oo{N՜Փ[jR#EfA͗ӷtX"Ta׊DzDk+`~~C n;?AkJ9,Σ8uD@w!i ܳT̴"'deohoqV K轄ZLzsC]ܵp njz}sule뗆?z(bwc:" 2S/B/`O0ga5]X~`R9`uƪgYU%qi`L. $&cQ^.S  ε?h\Ԁm0a,5/I~]O^7^ת1[ {},l=wD+ÇVc!Wi,S}o5ЧɍxD:!rASOD]xocV&i^!&EA^r}eAHA*`9R$J%kShc_Y2Na6Dv 款kv:]@j*" 8ڏKnw#T.V!g"EwB°8'$P.HMҦECV@2Z1[.'eIv n'ǗFgG[gVx Jsk! mKԞEAۘZLX*'+*߷C%:ђł,[fF9wWmЗExrKCC夕!VY ( N Xn/^,б|pI (c`o$kc]-!!BhD,-j=G}r揸A+ȗH9z{{5'E{>??_hmm x=4`5D,e W%y,V@קhR}E]qpJn޼yhz633zbu~ISSSM6 d%$@#ҏxUBA_#  Vu8"ㆿKljDVFfPעEk4wvN_H$ ,رC&]% ᴬmHmGql5v ~乌ȳ Mȸq#G{6xLcA@_yb^|ErhM&ŀ@nĒŀE2 pj^|g/ ruuu)))&<u =߷ogX(@=V|߿(AHISC [$e|E ::2qPhVXGXhD0dc믿^VXXX܀ksQz50% , >!+$_uUexk.Mkќ9sCuʺ|<R@׉.^#.:lzehmn H ^ѵtRO<fZ-MlR9B%"H'Gp﨤cst'#~&҇փw,)롙XFgbD1<®Ŭh}VH2DvuRv2XJG Ӏ]ς Hoے@25j$ ]N@xrM?1a43dh޷x{VAsLw7ѓxCf@ps87ރ$AJVXDd Sd𯆈zPJ&##RHY,_DK.!0pKz~ |o4yF@+`Ňzj?YOPgee$Y{ _5Ʈ)L>: ŗ# HIa$@]jۡiΜt 癐FMoo6}:fd6[̺Ũq[:D`fEɸ؊+ʰ7aPD&"{gd ^Q׻a =lMs8_/ϡAc^nr \ƿli-h5xt)Z7C: hH"PkmiPQ[8N phd w᱉s}]'H_D !ekg/ȃѝCn4# >"MɰPESXoU!mjE*tV9+@rlLxxɒ,3ݼ[Or|{w[6}(* Zf; qlq6k\G5єHR]}!X(ÜZgwÆ )̯ﶟp ୷ y .Ծ3i$8it.߄ٳ֭[#~OD?b(a̘1+jϊoc ;ܭNcz5{B(G1@f^';3L5WN?7t.Ђ-Ф|) [ns9:ǩQY)bk``h677WSXX#<'}ڵww)Md466>}=M<=Dψ?++ˏ& "0Ijm>!=pzjY_cA] Ӥd8!z@Z˿~^|qmH,Zi`W ,SY0*gnᤨVE2&i ,f)pmvd  ,lܸ1uʦBm.(/;9'( 9 B'T  D!zo@b;6}>U!SرcX$%s@*'|@z=x]i?4pXL’Džo4 E.Ыw O?=K7oG} ہLs41?dxMwM")ij3YnWGIenj![WW(`ڿii9yf)b9}UWqf234K$1x3|4yVcjj*eeӍJ\ q{ y^H`d2-"MDp @p)j 08 T8>b~ǂџZk@EH@z{*x9~R;n?)ŘEcs2Py&_znAG HA|'"!> ,AJVXG_0$i^W=^d"Dby>Z.{i41L` Pr iND@$pmuD CߞǵFtye +MiP;Rm~j3>AzzУl\Fd^1B9Ʈჯ <0&-&gΝR; r=) UVl '%#nQJa`oePLr pL\cz5i~-{!P'PZDX?O#f߱nvzx-8 LK\pOV ]ӢVبQ>l֝ʓO>q7rys 6S@i1iZ'} lK {o^t:Ml. _mU> ߐy$y2 RMeWD/m+jZ6(Qgq;j1sTk'%yd=i]ɿAy Pd}OAiAm}Y 20 $";i. $! \CMMM*)8OrI#'/bWvxd  jHcօAI^+?k#~pl{8D!tB-UA&84N!Ou,8P 7G͜BD%|F /|w~Nscn{iii>6eZ&YZv`(*?aŠ Y/0.ڤ7H־]]FI@ryK`i-NJ=7SS"60аߋ%̬ ,2Ł,!JM@C-Srt13c:,*8\;u~"{Bi˿ᅢ[_"[#sZ(t/{RvssUq<1N%3 {prЄ;>vN`wL`:տ#~><2(tS׋x OY"zP8݂ɣ{׮]SNfn{s9|]g@Yẙ~n`s%}~Wւ+^d Cs 03畚 `W=zH\$91 8kY/ aV Re{ Vc9֨bS V[2PFW{OaCJP$B5SCaMѮ|sΓ:Ţ{==m>^ h5>!+qE#p^9󡖦S01CZ}bVي67QjQQ˃+#@`S3:,X noތV.2 tڑ#GN-))(:5a<'@$@RSSAy"dLBPnoidUB_SqLǎ G ע tJ o-@X/ėQUrRAXc * h5 AYFw$omiH`63W"(P8'ܕ 4oVVV g5{zzƼ[WY *Z[[g򼄸5#!:~l] XQd2`'. -  !Ccb!V1tK<|n)ORf߼N'o_1RzR&vPIep!M !2gS@W~=/ ('Ot2n^& MEJtjO`,s>[ۯmh\iþ#xQ} yP[gwQGڗD~O$,:⠡yr @XLtguv3}WF#&3f~o^qϒ_vC N;-D`&[( (M c#@SuXg xk }k # mCJNo.Qs_ۼ<^. * p0ṋTpKT@lf! $LH00ɹ+6GM/q̛40K@Zq%#JoGReU{ \cN$sո(Ƣ>6jA0O3.0BaV0j]~4XЗHHC% )kL"(vMZ L+}|zGM Y+ѽa:7zPzPҽ+ɅO=b.)&'b`OS ggb27ʽ,ߥ^Q86nx[^^N{w 7s~, 1HCfmr݉Q,R) 00~*A̪UM6s2J4$W/#c'Wu8:/dCqϙ3G!ӟ?,L r;U2yL2Ą?djjeMGk A? ,`W+^xǏ;l(v tEDG;EPζ, a6A^ߊlٲeɌGB,8!Z17 &pa4svi^uG q!g>9('o eYy\mW>a,a$ھ 5@;L"S3 ƍ=]hIsl. )?Fj*q@#RǏWR ]'NK6JRÚL!!bD :}Xf!/ǖ6#؛ }&E .+zL_S#I[B[_Z9[B,5s,YုIrsslW0 kG-%{6M =PtN0)6aM`%$-m%~L~—dge'ymn$eOs>H<Q㯇z dx[V3w hD-tq_'222ul`I6ȕan  owJJŗw#2"C;['jfXa"WUUq*1 ~l6`ZY¾D0B_Tr+Kd[K2hܜCVjt @J4,2% 11 SSS}N3I?4"#2#TZ8qӢE3233yⴥE ޴inr\rI[oUZ&2`_lvf_Ô,uW5)t‹?nREyzJRh  N0J:&^ 1={f#?"JQA؎`Y+ VA\V F×JQx7Mc£,ApQjћiKZP-dN222ϿD!X1dfS]ˀ )|бnW}`ҳo ylsHR &gaA+?8*@f"Scq]ѝ.؂촽K9qOi`ٞM{n>''~ %%%Z<5I{?b)2I]o~ǏSO9࣏>3_dGmaȄ: ,Xi1pl ~AΓLX4Q\C~S'hjg2kmOly44#RS/ޱ8VI s[>eˉ rTS~l9dpaq_? -[V_VTT(ꩧIB؜@a&)}-^|btmQQ~&h|׫=,,.>%% kko{y a|1W37p\xᅀ/}˫jYvm6-7'4*|Lqs㺺?\Q,5djfsq m <̞=GُFʇ_7N@‰o ا;/@~1Ӌ@&njCc'~ LMʿdhP]_C .ˏ~#Zm}ǷAb%?h X'I~0a„ X/*d4 a:~UU)T<<-=mr㫅=_Pܝ~2vۜ9șO/9-Q uN/_kФ!{$! u_rhL77Z?!rq [EĥBمY`O+۳Zm&*li٣\=zׯ6^lwf=z`ј13>r:9_qLl5ºuD/.KAY28#" liCdYyz&ތ V>A wh<[6Y,ԒR̼n].yI9.t;:z>[qZ|sZvNnk:yW흎?] g.׿5}'?_Xgg;-" -^a(3!KJz^w|oF `m-s_].y W 0 Q 6M`;K> 5iiЉ6h;|rL%؛٬bޝP5k T#x[p|A}X%W)I,v'G\l=;8bBg3@;@,( u9 Y ;O;mFEVI~vy|Ame۷..# cP8I x/WZ~pksjXϗ! hGZyvK֮NcJz*5ܞ1u;vκt))>iw:;g!l l&r0_?̛7?Fx*lQekDB ad`HV{ 9C!/[t&o4))R,<8֭[DwgoT}hQn@v G.wF8?cJ,j t~ jxcЗSha_] p;9 j>}'SGlX3] 34׬/3򫯾ZzI˗ˤS!7olccǚ!''kc\=eJٷ}̘{\vuuyxʵCs:ֹ Xl}zS|_2$7)).q㶆Z[)-.ck+uE߹^ՃF{JOdfhbNJI'!`[G,'Nɀ'xB<&[зI;u-{|g4?J,[a;`( 6zwL ѕraz:̀ pl,Q;66*py(VUUUrK2(++ S*=x~`)^ggт$ '_Bk @΄Z0l*Vzgt\zقo^ZՋ]~yE }D0g֯EK&NzMM2x뭴~ Sםs| S3Y;J۵SJtd#5]٣] Luo €kZ[]Z$C mm[S9N/S wt47;G2#eϫ I`\zCV}&!K `4+9^c垗G7Q,|":x/Ec~ܹyݻrL9⾱̵VxwFÁʐI`y37_K˖-L;wɻ[Q{(N{I3'~"}'LuCqvmGOhZ~, Z ƘR^4}GHk ?SXA(o1ge'Ԃ:j0nKMK`\$;&s_cgrutLcRP2C0y-vwc>7vl}@[A|Yb.H^o8[,{[gJ<[H E'ZKw絜=nԺMZ64F w>cǜ<80l~^4ۛr /MRȣ,4@.!YHsEdLbESaTswIJO;ByZ=+޾ {?^BL]`MOO @Crݬ8 od|TYSڽY|6w{ʁ_[[+6DC.D7=]nS>mF;l 𾕯~qRO5'P!m٧`N%A[66@CTD 8ۏ ǍǏfɓOUSO= v}9ĉD|,{: U 5THYK2+aȝsWT_6k:q  =Cc̃z[- X gw4iN}gחO:~٩Z,9r'Z9Cy^\oAF O=Kx%'9r5kvê:P P|2 BVi/>^`Lah].oB('|l ɓ+xK 4W,3g*4X,R&wY}̀I;ybDp Lk=wϼTjZXhwd;v57V"5\Ư_?GB-> 6eIcNj+ԋv{So\ ^KK>۶}Ja3 µ)~$4!53qO\A^b ny):uL?е݀5}ꍩRQ6;$]b9l*;@KkھۃY%5"7g%V3qM a\@1__ڕgUYXFVݜ%hXEHI5 6t}Xa3Fy{%E$=\L*f7o4 4O2swvy;m&KP22Q]tgvG;ޞ 8mF6 M?ޡ_ʅb@ L.b*:Ε ?Ȇ8~3& ^RXΧLn$D!r rP F! !?bB7fhfAgԂ~o6켿;Kޅ0g-|i?V A\]2iͤ1ݥooz, 3;|`]OׯKyϳ9cG9s 5Dfhd ځpZ5#Mjׯ-21Z0{Q:oMh. NBz |Kji*T"UVc^5RY狗<|XZDqH{Q !PXwܟ~}ݝk8tTO:ѴuT-$e^MOݸq>kxvw:;.}hRH4Kd-_ 4QL@!Zr|t+\rw&K?Xh2XǷXF>뭷bԣ1Ь :mUyyy8W#~0IINGzs&ieaӀMc=7't{{P߇gK㪫Ͻg2d_eBKҦP-HϦ @ ITpŶ* 곩iV i}ie&Ν{L2m}>w~.{9|tί?4osH ?XPzl-umOr!2J )|ƸvcG7.Y\3L )7@::zdB7U" jEE ==^zɎǶo:DF"0P Q_:~ӛ7ږqڦ)oC悄>/K*я_}C,׆!Q=PO ';̓$x΁~x ٸNSqrJ6}Z z`!|[I *чPc16GP SFz({?Qc-#X@X4j:t<,h<ZT e 1kq3L SPE4={eA?1kbTY~+GF"h9gm{k)}@O`Xw`5wpЍm (^^Ho˜DyQe7ꊌ&,3`QJIZw1yC!}< )C9ڹ&.^H J2sb 5[V Y +!րG( lNA+D.$D&?bO̺rH`!>@C(jA^>Ǡ (-@VtiyTu=|c0?~"ID$Ӝ{Ԓ#Cb}cb ~9:Z|Z9;P@0֔޾8\DAád&I mmG%ݿ;^&/BpwAW瑆 ˴EOM_vK.ύ/n?*;'~VG X*DƪHPI*hes6g&YS޾$ M-+ ?YJ^)F3™~ڥ7q[»b/,,uvr]f-(,S'v(}_`[7zuZd*suxye8Wk|psY&8f<F=>kB8!LA'q7oCx|@W3 o= 0{T4dqj+~#Yyy8FPaN1 %yY6n7&S_?HWA}!]3ɚ´o rBIq8@k$9KY>%EOagLJR!M&L95DFԊ˘t`,ѯ{)0U~j|=缙dмr~柽l[&a^Tr" ml]f0%7@S-j벲/])c&)ҹsF?`\v4e+EƱzj}ooo#}1\P>@wɟ L ދXw} pn ś/vXu9XE5È1XLg̙<\;ȘIJLN@ (!_0(x /_h$p\*qr6|s\uLNQ5|]] ~#@49W ?7 YhȖRVMmZ\I ئ-p H>]O* H0YK7i/*<$zlii&+k ݜeH7iZ>yF/ ^wuy:;;g kIROܔI_C>祾xAPɅR 99m hb|uLTKAc~ztZD!Z. ,-r,{DV^2:|K Klm@9.WfDOOZV A?NQhwrʕ+.[O<>\]]cKRc#{H(F o8r pYYy-Zd9@A.x;"HW?=ouѽIUBHI0p_E<2jȱ)px fj} @Iu GHBup&ΗUGz]' 0qf. F$NfxN ,xSD:'!$ D<x!"x4 H[@ 205NK.\>z;|pv[[;WOs{ŒvNޅ% D`3p9RB  0IXV17PTF j bDE'M>7 9Y4^pxzm:)NR7t*@W\m۶^fNN{UǓ/B- X Q{\IP$oimVV>)ɒlЧ%-sP}^Cs˖]SZ18|m==C˖^p rtt 7ᢼzU ^Z FОӼa+Xlg}WLͅTdǻ"Yl<($0@H !a g*e_wz∙J 3M _$!P2l-"x1HB(IHşT@j^ ?@ &1c@$g8@ 1ADVM@<A@!RpBZk$ @]q#S:Jt2VP q0 zI6۝}ga5n `=JV7CVVOh./l"^^//` KS<˗Ѷc5nw|ZmW C񳵣Պ+{U}c= i{Ey#eHSB/ge=O N:q$>D?zɩ`vp\S)")sE59-462KHama[;J#)_MU1rJ0-{{H`r "65PhQD0cy$ [A*0lrG|*# K/~űVNz'ظqv,KNeIk {翢k_o~uf;Oq28LOJʣA08aL#F>bXs3傢"u*#=c+L!-9sXWc=JRPuFߏ%OOPwoO۴ӗN%5T*BH㪔qyCII4Q%KHڵkE>:nL?VĭR|5nJQܗTS-=N%hUH\P@D [-_| IR %Bp陨\w rkqkAw PK-I)%VSO y^$%Tܸ} hxqlDG5(O$y3%HAy^H{x;8]~ޖԬ^_zY \S@KK4Y%P' lq^Z6L@Po殗2 OEO=L»uwXڬ*S<:(WAvaٽz~ͦZ3%36AOo}W;.-۶mvZ%fl2PRJYJrT 쫒A\ 1W7\>{?Qcxƽ ÉFv)+Qpyk26I㑀V`ӈBBHt 7A2xmϨK_M1@6DMmCCC},llm֭ GtV:ru׽G ;zIl߾Q :]LaouZM[oThwL^_DJFFTjӚD,jj}+=!&&6477C@?1Hbp~2X>z3v㢫߯V%n8PwH`]I EEe驩#HR ww^n9y P[\y_L$PGO:׬Y#i;wD7 )HAU6鈞s! ^m&(ŘwzW˘( ̅#8O俈buC~]ȆopT& ` h & D Av$ BB8< ݘ!KѴ"N^\NBr# h5Z`P%`By]Vb">$*xᄋ!^ȽQ.<F|(P#ɜej^(Rz-KE&IgpXx$ռna Zݮ/̻OO{32[HRTuC֪˪@?5=009QxKx^MF'o!S̰#=xJygs>ch+O C!;wc~Q\9s攉Fbљ = =9ɪa5p4dpɖ&0)&7e:|oYS+:dǶ? ɬQ#~%mb~v$Z&Hh,-h%O Ŷ3Nwi% h"heKC C1weܱ") epi~Kh}$jAu;z= xp4 dG3(4"HEi<2=P#@̀A$f w-@-_mFVQ?}FA[3%&ad; $E@ù!$ cԞb8N#Fp7,Mډ)R>LW D1 "#3 I2! r0(=F`⼼?m65n@pN|"%a}#Aa \.l|'RG/}] FϞk+*99oՇ?ZDe4FGBrSO>"$%%<_Nz!lXŦvcAB\[q#WC^^V4&PZav o8l`:@ލ`n +FthBCI^pjAbHRR$>B-c?ҿZ 0ڨax < <?eAmO3><\*ںZʚ Jؽm瞧:x`8c`Ԍ` ԄoSá,tZDd捼("7Q 31$yqW!ȟzk&1_)׏!XVe`rw(v<%*(*9Py,k?bI-Y%'8zA|P!o¼/47=zx- mU~Gw)H'S)%2@M@7C†9aNՃ^y+9rXA)M xΆdmTͿjUIɜ`N Μ 讏Ǐŋ$i kۯCo4f5$(`)XTCW7F'v8_iJX8;Ƽ_VH# [=HbIsgP-?$<ӟu=$4xIqo~ Bt #UP@T f9YJ|[[ot@ N A 'S2 Z8{2H.\D@ϘA(;[FۮTК89^! ՘yce?p Ex'%d2|<Bh?<0: (psx1w^\e0,õ;} qxӍo" B,~ǁ QiU@)ܾU&II!_!0=t* _Caw]U#b%w0ò\16+{ ge_kL{Ӵbl%0G;L8[O Bxqg0e ƂL+\3p݀F;ן y+z@u&5rx'+++%a=k/ L(#G@[|˒D4M Hόx@l`zzM8{Ppӈ`#ycDH׀~nZ@7NJD7.3& D Z@}GkN&Uy')CB>_NǷqH`$#3AN<# K]r܉}ҒG;;xڑ-5WK?"H#2A4cR@U $;~˥3l1lfotxLu۶m[@z|xd… `x~NV$PN1@pH 5܎D,q[F|d͚Ŕ]1*_Yʭ{ーRG󚛛[YK)!'OH( 03^H <C0>Jy i4^ӂ`ԁOja .qv<=(@mɂЖ-cBBof8d `x;"#dU\B~&AoQ/]}6e m@z_iT(CUý_4=wa$k 4ɒ~4wLuH ŦXnO׎p<*u4,+J;v0 ӄQ+`9 q,)\dLs@qꑑQFu*Eh#t\4ށYyC5ᜓaOqܲz'jFORѥ7 q N>l'ձc@ j`A53(wb/pQd%tg#yp#>N.:lnuΒ}5xUZ edX{__ں8ms9Kjx>TBkMMgzOn7\!KM3 /9L˦ auMr' U΃b?UUUO=J"l4Y!ǘ&I?DR?PаRމ`Pg X P”A^^n x {bJns|?Av.ﷳlaUrN_*sw;n}[&G?F/D<` lT{(Hxf?瞲!ύ~'T4%PI`[VIHjL9N$=jXb 9J;~a!^;'pA p>kmZa┌.Xf}Z.v~ [ Kv 1LЋX/$ 0 0 s@4w B?,cx\,K.e5`IKKnȮOV 2JBd0R*Y ,h敓& @0NORWT\H1(-Ov:4>-{`T F:ޗfI$ elv a-/y @8N F ɂ?ְg"H([!)i((JcmSKsiiK:|@%\$/ZiJrn`N#o]kAX\dJd@1;w4P w#/OF|2 / T[~{[bؿH}ڌ ˲y}(#$2.Q. vpzzznh5PϘge2n}?_2W'=y HJuu*o-ͳ((\re[OC!>7k1p[WCHA<(z><* )j/:x T4l1 aW_\gϽ"3aãVگU&Sq" "alK~ 7?!լPn&=8C$/$w{r=m`x ~TJr$l_ K~8.YtI)  XY'OqC!N$-6kP_ZZ*p̤DUl2iژh(+˄>XOq% 8&S5[)`i@5^n>zLvMd M}+,ڴhtwauɠ!0,?gǾH&C%IJE#j2#w) bdM&\Bi$#SxR?u %Yb(4&܋s Bd"BX,`ooL K"El_=n!z8ʃ [ ƸJ/vIK⺕ Anv:S` HfPk29n̒nXs%SyȺe5$}遪㬏P^X;4)4z椅O CGbޢN+&N@޽ F;!Q^5_{ԟzI+nhpT`{<~X1`JЄ8b&LbI 6Iz:LD(tmL8Ѯ3d kQV@}f9gϞ*[ KP&['lIv*w ^']PcsO/1ir7ǀ)dM,([!jz"Z3Ö'`~Pf7on\3(lsqs g`N={3m}랉RcmkpߪTX g<@&IK3AQI SмHɬ]$w_-+EdCEH\?@!dN3OJJ:S]qlwhhhy0,D/v́6"\kS /;}Wmf3d'kM@"6/{]|ZWի_7K躲:wA@oHIlAiZ(2`&`;oFpƭa'F@/7ZM59 ''DS1@lO2)4~ﭲMvgfV`kAi ć I s$0\333Ȑ'JsP#؎[za.ZIGBXv߄@˫~qc[[-x4~ 87c&uJj&I@{7znn)eCն6Z#/^Z JvnqeղZ ?2?<HIZ /Z ɐb타ߔb]߶OkkpGHe`){_S=$ݞ8Kįo~FzNPP7|`$ҨJ{jcy!xJGل j ,imSPL*YZp>TGkg5PW%OM¡b+9{G*>`M^M0 <IURE۰U3K`I7*+g_X,,`Ί4 ~·+Kfْ3|Zih@f2SMEN~%gDdoOۭg>5E=+ ot4UuugхӐ S`Ke++ӠaPV[; jjf _[{ƚ~h^UlCUƓʣp}/MĽn 5I'1S KL(7[)CjjȲHn \.WP3.0LH6u4).q\fH8W^K#\BUp_R-S `đ\%xVFJT߮" iHjmC)l 諵7vLq:D.3cTITVxQԩIMiӍ0FZ:-lꢁ7]q2M9w3 |.K`"xN=X\? Mv3)'3^!DdBqlG41N`_nCeRi abj1E?1'Dja$H?#x54 ZnSG`ݟ%m% y[CéV)lD†I*@VN͚^obZ Plt ƬX o=~ԵV 5s #%e1 t 4eՐ-miZ(o@,,)կUc${Gfr\]RS5Gc H5J;h4:aɻO@G邫@8i gpXRRR~@ d͏O ʝ@y-Cx~o0GO"͒#RцMJ C,bc&V748$x:9:nѿ|DD6I_#e0zM&}?F(sFhD˨r>)i=,Ifo:0i.{D~ئpeo9j &H`uK̿J;tlF#Tff'‘ ƌoJМPj}9f"Qŵ%ii6 v8]?6)I͟|hZ.wxd svsh-wxV˥Zsο s7oi\vvv3{GZV;+4̍A?Z]# GR.24f}>+SG,7E1ǍY3e5&|Yw~fˏFhz/.4zg6Bc~sp͜Km]]]ԡb2 v Ung%vNG YVͽS?L7od"scfbѤfK9X =nIt\Bzz:u/:}!xD8 T0;&s(X&,i# x?kMϲvoϞ_j?fWg!mqpImǎǯgPXL.&Pՠ]_'MB>aC4wL+ oӨa}=Fw~PVE '5L/z&hB ssD=n(x@̜^6,vC#~U`NdMoL-陹}ٱC͂'O' K%5˹sOȥ4}yxYFN%kl*yޡugddvY #Ŋ'bkA?k{Rd8(|N}ɼ n.g> BsyXu3Z!nXBBP07 eĐ@6]` 9rQA"o8bieX5QQ;QOSL& ƴOLrf?Ñvށ[x+fB89|n QZCiii$I(D OzNH2—LD=DIa &!g[|?&Uݴ\504;JTjHY"RWYVO\,y*Rf0JB| khD/ %~r' @ "G(d>"O&CF%^|` BX5N\,3- E$pR P!55U~.Hr4@W{.T+X EIENDB`qdmr-0.12.3/shared/aprs/aprs-symbols-24-2.png000066400000000000000000000275141501654372000205120ustar00rootroot00000000000000PNG  IHDRftEXtSoftwareAdobe ImageReadyqe<.IDATx\&* * 6@D׈QFcg5&^bL؍Q1*D}*& X("i zv7ۻf+3AAAAA6b@" xu8Ed n !C2dRH˒ɐ T ՄT-$K]7E$& OAr􁗗霜" ;;;)00p:ސDEEQGGG'_Uk׮GAoWCf͚y6 k ^JAo0TR[sKo<~jG - (*soZXX*K&d O!@'A xs(?Rʝ;wbInsRR"2Aoo"ۏ<^]# "lrDnnnNJuA0Z uuR|6lZnݙ].+z@Aě (Z Aě           xs0P"@#_}寏2%W]%Le1c >v\$i9.C|B7ƴ|lol @G]ZuYj[zڒx+1.pCstV5_qa|Bp{ǐ2 e+ yqڬxc)SB)I6&yq-S[UlpHˬ{U^jI+j[ě ^LCHv}4:::bʕ#Yv/DqԨQ+ܹH\4<<\.]zqTB`ѢE)))O0>4ӦM~|skQ˦{ߠlnݺ{혈GٰaP^zc?r/哘k Ly7߾}X_͛7 {Q``"m cPPPwn͚5k̙3Kŭh? aM4]REnݺϹ{В1˗/?62);@ôk׮aWso֬Q"p64z̘1^' c]vp TTTg8W 6|@&Wm$' v_`b~w}||Cmk-H:0e1 w>y2^|B{ 1LKKK~~~{s߿M333d!)T5@+e鸢7Iuc=8[<;w?8~H͍͖/_ރ9C[%;99E^R+9y#8D)'N:;;\~Zhg7Q\#_}վ|h.\RaPo?Jzܷo_fgpbTvuu/0 ̗4Tжb?|0 -&ϝ; "!o z:}p"1_oi(c'f,֌0`h8 {8q ;ILL޼y~XC!3.ܿ@d=n8,ʕ+#b{0a~FW^3޿-[v (,>c8'ౣGG8f͚ !x5]x#MV H77oDsժUz}+ϊr- Hb裏f(8*UdϩHC255ͳ<}3fh(`СLl:zxx: >ۣB622*˩?[g(g|fffqUX1$|.¶_~hŋ*Pdgg]~3:7nծ]ۆɿPȣRյk`~ΝòDB7cm|LLL(k:884obBBB*gڧF1aaa^Odn F&//BO?E`XC9J2>{ɨ1\ XV|3_{ѣgÆ CcŽ;f .3~(i1_"(k pcƌ 7 q -'^RhTÇY=Nmڴi S=z{CAYbXJ777&~){ i$ w=)A-lKQ)߿"SNg 7-x+ ܑ#Q?vÚr)rqH󏎎K; ,X_ up5MaSжMf|e̎4P H Bv}AAA/_Nen!Op 7 w.^ w'/'Mf9BnU(@D8ϊ 3T"Ͻd.qvv}ƍcWX B^0LsӧW---W\ر#IU q#z<=%%6D\\]]W)gA1޻wo|8 Hsr\ٶm۹xiZxx8d$]F_I7ec[<ЇLʫmKZk?g)4F~厥)K|Yo}%ˣmcѧ&          (+2~+>Ϙ~by\u9[9<~2JOױRZ=uYW|ݲ~  %m: xYFLOtL>R %-3ג(|&ES}ɥ4}S(Wvм^:?eTuX*SU<+^EN&UL4.ӜzPaN6cbbjŊ{$B=__q?c܆2COmi۶m#F4266. 䯧#l͏%zRUr/+KWKA cq{Tf1[ח\QR=6eTF6 UrkЭu4xFAAAIq tDyOIT۶mی{=qq6ǎ;U2и쥷6lf܎nbccoi.}\1ґPNQ&uǝp͛n^NLe Ō c>-.c׮]S^+ޕL2{&R `|CBB6 ٫KKٚ+*7o܊PFe_X Wge@?tD q_zy܋4Q(K!)),WL߿WF|X񎁨o &88fsΫ7d2^?P`:v8~ŽWVVV xr(TeÓ'O f699ԩSTf@߾}ܦرɓi顈[:Tzꃍ7988X-\#x3>}6~mY ɠԢq֭[u靓'OCD-u+GYJΌ so>MSVZ4a9&ԩSҥKћ6mVfM9sthժՀ[:thWX5Yvv&`+rXcWcpUTge@{T' !ѣm0d rjPPd\AUE݂,Zm}f&M:3gxK Dz)u粊+w;Lu).iP| u=4h~hsvM:u8tWy88OU]]]{~Q}Մ骬cǎ% 0=Y I?:t0 .svٳWru>Ϗ/2UZh+ %XL E tӺ֧O߸Bse{޻w/^2 Ht+77INjjjC-EtN8:Agɴ{D;W77nx> ;_՜9sN0ZؑkܘW#B*U\rfϞ=:0dJktU/̚5K0 {8?ܰxO##><8--*(f8zHHHruUŋ/tҁG]youO=FB!epw.^hH}c5&&1{ōW.]8899 `oٲ%;/ȫQŇ~+0P4#9ɓA2P>sԩSwWe,P_m5"]ZI~LiOP>pf Ǐuu_PE a,Q*'N,40m۶#{wi+ WXt \`g9;;㖥\A %L<"sY=΃WÇ?tppo߀s{ܹs7-8muՈ/3R]%A1Lq ypTJ0{~NC:geeV\73JDŜ2a„m>V 'zwIj ٢Yf{np˗CZ(C༼ WkPla8KLL|"q߯5js>$ٟ(6\aC+?ɼyJ%777oցe_yƌ;RSSv 6oB+:m4w17 @O Lճ_fͦ?Z ڽo߾`p&@Q:Rl9Qb;Og<׷oZ:Q_}oݺUlYn]>{#*%NSa89qnZE E޽{p/O 4ɓ' c8W7onp+ Hz$%%Ł8C9@9Qә3gB_e"bxx#ɗqW-^ڶY{hh-J dG6o[vʨHK;vx&^W]Q)zxUoCà*pn ( ]:sg}FǓO♆<wR&P]F؃~0 ݻwov eD/8o{t<~1cƌ4v޽O?oɖO&M޽{={fB? V|DPBS# S#2G. mVZ59NV\~2{⓮P궀hRP?s  Et8@*"iZ*~>}VAL_΃rd\2UG8{BI'O蟂!|Gv؁C }TTFzSvJ> g\5g8n<ʺヺƍbm$q#ͽN}q84 ]NmnldD <8:#*H-:m񻰰0B|OcS?N*` *A $22.{|<.C FMC~ʕ|zoʃ`bߴŇu4р f':yƕq#g?}4+xP_80)))Ňt=<ZR϶|} _Z-BSKꓖ<4d̰3KKXiJ.B26,)55hLL GGioot}L//Ν;zU|Q>m,x{q2ZhcAv}6ݬ0!!!iٲep eѢOj; o /tTy-JDekd_()@{lct_YSA2 -XYw| $[ˮ:B'}@{9h}-Iw.2]sgߤLyyG_׀6C.Eyrv           x0[]>R^E 4/,O ,]~JAheMpY嬎X^KA]WTCy,uPR\VrYP"{%eEFڮϼ˫K1.c5[lYRJlՁNoݺU\__%|͘1\NNN_~y_Lݻs˖-=}.ƴzj2``/'.+w=tu7n|W\i„ a/r_ KK߯k׮;>>>,Ν۲]vudW[M6]+Wt\۷Oj۶m.Ч1FyBKYCvLIIɝ9se.s@orrYxxx e1%\ꗗg?_ͭ|2.u ]҄Ԥ{즦fX"/htf?AR뻴^/]>ieV%˴1\ǎ۔)[Vtt5%xكgj+.q\\4"""61-m*<%j- 21۷ wjAEjdff>caKzeعspvxݻһCŽuߊ>|8SP<6k֬p>i_}}\zWvuP2d2 **?c[ !;Çի"$aoh۶.pC}k\݌X4U{p͢njs '|ri>o޼)xa}۴i#D:t9b`r`\׬Y띏C?ߺu+osZ b-[|"r8|mۙ_mq e}wދ9%%̼5n%a**of vaEDFSNmann[2???PYr,>@o߾Є ~<8Vi//7uر8Qн{펎 s|YśsHH1N0 ^mxLY(%kp pq:xC uցG2^EŭU`/:uԂtO~ AC?~(+mlrO~ %U;eజgPi0!=0aPYp.MwȐ!2.>}Lf͚)[J+ 0<@<{LW;m:evvv2 su֭}o } ga41c^`K,96bvw.גO9*-a]qRSS||_Jw-b~ O#dk_ןRF@4h`{미+{wvڵ ?}&o6DL4ix0444uʕa\q>avþzjܻk;4pn-Zt)۵KCvY&Fرc>^^^ ^w !#0hBAsW TWXRwhF0,, K.[a9-bH N.Q u4 *֩S'[[[FM}~zY%W9sFТE:Þz (4o޼{|oK0>|xSeٮ];;Ȕc8|i%7Ĥ6R#q[iiiVSLۄQ3F*{nzzzD9˖-~@A'oooA A'K} o^v0bcׯ_y赘^8N@c(͇l3tǎmbޠ/sȝ(iɒ%;St:;w.D3`de@q 2BCӠA8Dz>LieJW+"|Ȋ^+?AJ Wuڴi-q~QM6]NJgq{q md>J9.'~;vpCVjŊDR² J Fuu}cŹLZI& | ,xsY_SːǴ_@ڇlx;|7@5x&)C/JW̷s9.'B  Q!/jAcϘr.`(CXK˳.휯ч*H.EKFe/mXnki|ˣe*W-ʫ}i9h                                                                              ?4CIENDB`qdmr-0.12.3/shared/arrow-thick-bottom-8x.png000066400000000000000000000007451501654372000206160ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  IDATx[GDaFtuI$I"I$H$DI"IT.l߳.˙u`ww`ۀPPx5@=@PPx5@=@PPx5@=@PPx5@=@PPx5E_իKgFx>5(n/|`REo?|`ryw`%hdzb=P;25Z15Ղ-2t |V,Y HTe4T>]K.>+E^ᚖfR #'gN1{L>0I~`H50s8#<Q^ʿ#0@/;\yIENDB`qdmr-0.12.3/shared/arrow-thick-top-8x.png000066400000000000000000000010061501654372000201030ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  IDATxmGDQFy%II$D"D$H"To}}I5ss39zGA!m RJYn0SKO^`J5{F5ihGSƦ\P.>a^9uj1Ȁ3zG 8%N|~eDU2v)Ǥ *C 8"m|8¬MV1j!6sqE86>aޠ#h[H^{-n1qv#Ǽ;FAUn9a7}VRoA\[Qeo-VFTnqJY>Z@Pj5>Z@Pj5>Z@Pj5>Z@Pj5>Z@P|@* g-IENDB`qdmr-0.12.3/shared/audio-spectrum-2x.png000077500000000000000000000002401501654372000200060ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs  BIDAT8c` C1VDx6H(. H36(v8. &. )L(v zE7IENDB`qdmr-0.12.3/shared/audio-spectrum-dark-2x.png000066400000000000000000000005131501654372000207250ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME ,>IDAT(cπ3000"L 4-4Tp XCLpFM& H"%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvIENDB`qdmr-0.12.3/shared/book-8x.png000066400000000000000000000010521501654372000160040ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  IDATx=KPFO"~dб8 988਻J M<7@y 4m Ys y>2 p٬ 6!@xd Cݤ;B :9f& 3I]sOtIMU| (X PcjB^Q=_k Y7*Bt !yÏM"E2Fh|q/sn.IJ ȿX Pcj,Z@P j5@-X "?@\`V"jSW(/>)P5ku(9%dvI3ǚ6plsލj&VP|?8E%@uZ5^|C>{_Qm)qYe `\k[: ,ӮM#w/#z}:/IENDB`qdmr-0.12.3/shared/chat-4x.png000066400000000000000000000003341501654372000157670ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  ~IDATXA a> &5֤,@-i^H`ޔ}p߀$ ^@dUQç0 xQ Pnwᴁ^|JʫIENDB`qdmr-0.12.3/shared/chat-8x.png000066400000000000000000000005401501654372000157720ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  IDATxA 0Em֣ͼ]H A2J<Ą$m)#X4@y5zNG :h,@pF_h,@ct X4@9hjxB{r~(BBWVs|M@ct ФYГuaREBRAB&ҁlB&f%9J1cB,.{IENDB`qdmr-0.12.3/shared/circle-check-8x.png000066400000000000000000000020461501654372000173720ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  IDATxϋÊ^L4D'%QA=EI"H,WDd4"x!f=Զ;Uݵ/xM0!;q]A /iczpf/;Bݾoq-%/ŽMX'^UU& _7gʃ&U& _-;1) a!Ąٌ |G`r/&BL C#$΄9aRD"<-R`G75 S&pG0ĉD2/FٝBL# 7u-MKULy o \zetRe돵L8T_Ph<J$hwNЦ}cuqØaIENDB`qdmr-0.12.3/shared/circle-x-8x.png000066400000000000000000000020551501654372000165640ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  IDATxϋVU@3tpQBDʴU?ĵ@ j}!"9Ъ?!"RFBD- Rq 8ۼ{9^{<9sxOI,<6Us5& bߡh+8=#bG7  v(3>ΛxG70Нq^!f?,y긂wfC1\eny|2su;c$?\Mœ{̏󔖷ýpO㻱_@x%|rꚟ?9=b.#yj>(n~!TDj:qcCH?&k$&(.uDŽ$Oi'*: 5װm##kB]mȌ杜ƄD;&k{ !T#ERЕǫbm)B|!mRе;Db!e~B{9`L}/}Ri}/!a .pr/5Iv%cg&u5)X7'',^OBx0Zš"r2!p5_ L59̗6mn ~!V7x7.amߦKoYa/Jk3~CMtml2fMDž/Ozx)͍xOq<*|>NᖰM*~לÞ7&?IENDB`qdmr-0.12.3/shared/cog-8x.png000077500000000000000000000023111501654372000156240ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  kIDATxݛۋUU?9FK8HP>Zڈ(t(4#!ˠI񆥾Dy^",-r`1N3~{f8u/ ]Z:/f >sN_Xv1%T F)4?=1"!'1sY!fX ;}NX,u1$߀Wv`/\7?}H"$'k`)lw.pjsȮ y/ئ20?h;["Ooy ciUes-AZ|ƥw y_ KU/`FnE&S'̢Cc|Q;'~RVd<_V8&]!b3x qHS#w#sʽOGYw0I#ׂ c;|*/-I3 WԐ.~C!ɫv&5۵ 1R㴶8gs!&ȫhTS59ZJ(@ȭC=3(K{9p "a k{ ځ+ WjV+u٬}Q}{@]H?|`It{C~0O#  Ԁ7btF!MTS!.Cy[65#U?d12{luhB&K4Fp':;]HKYE,u w]Y! L-ȖnK ƕ 5 q E qH`ֲ T]G!gE_#U( @kFnqGЗ_RRttFso[U-L@&Jc/<6Zg y7):'_1d#7)vc$p x$M8F@1ZMݖv.܂: DCavl17X\y0[YC.².G Sb"FoRIENDB`qdmr-0.12.3/shared/data-transfer-download-8x.png000066400000000000000000000005111501654372000214110ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  IDATxAjPCؕgai69jyO @m:mh@t6 @m:7Xo=gy/x#O7(ᛋu.!ؿ.VqˇU\>a|X*.VqˇU\>a|X*.?333C7ZC@tz0FiIENDB`qdmr-0.12.3/shared/data-transfer-upload-8x.png000066400000000000000000000005231501654372000210710ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  IDATx횱 0.thC)I8NphHZU=&4 @Iz@!BU>%^1 F>"Xe"X姏`mO/?]4减`6up凉`>^}E&1l>9xn,uÂhw EI#(M7!$=&4 @IzMh@$= @4IENDB`qdmr-0.12.3/shared/document-8x.png000066400000000000000000000005451501654372000166760ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  IDATxQ0 P9glgGOHD1g&40B! A!#R2,AP  @B%j*LE 0 !xP5B'W#txpGz:Շ@'ztYws @'zpM缫@=r뇞C@=N: @'zt#l}>KIENDB`qdmr-0.12.3/shared/folder-8x.png000066400000000000000000000004501501654372000163260ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  IDATxA Pۄԙ*imeq!|k|v[lO@l `$IX`5`k;v[l `5`k;v[l `ǎ3+Dwx[L \%}u I0i( 5愾+s`SVsX)IXPoโ3" ekؐ{#9^A13*;^ߵ0f"ywԥaa{/1VdG%v?^YrMun =E|`IENDB`qdmr-0.12.3/shared/globe-dark-2x.png000066400000000000000000000010251501654372000170530ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME ,IDAT(UѱJ$AZAP"ѽ D.|_BLL=CE 8|3`gƱ:h+R½#m='4ҭ =Wෟ +ӥPޭ=WV̑L yR(cF'V0pޒ/. [ViJk6a.d)I>w.$L޳:Ir&Q(,(<`]Նy~󆱅QP]ց6#ݙo:WAӀ%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvIENDB`qdmr-0.12.3/shared/icons/000077500000000000000000000000001501654372000151245ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/dark/000077500000000000000000000000001501654372000160455ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/dark/16x16/000077500000000000000000000000001501654372000166325ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/dark/16x16/actions/000077500000000000000000000000001501654372000202725ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/dark/16x16/actions/application-exit.png000066400000000000000000000007301501654372000242520ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME )&:IDAT(υA0D7E*C<g1CH`_zEkL.33#Ξ2F OM BC>n]$@>P,xʕNuРA$I_ \`[1}lP"ø?%E2)Dvr%lQڳk̛i~:Sº~)Dɕ'gvo=&%tEXtdate:create2022-01-24T12:41:38+01:00R %tEXtdate:modify2022-01-24T12:41:38+01:00#)IENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/device-read.png000066400000000000000000000005351501654372000231530ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME +C;PIDAT(͐1PC%TcdpALUD[ -u=#,GخнLu]8ac=%tEXtdate:create2022-01-24T12:43:03+01:00.%tEXtdate:modify2022-01-24T12:43:03+01:00IENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/device-search.png000066400000000000000000000007621501654372000235070ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME +zIDAT(u=JA7 F[kAD O"x&G D^Lv^ń])fCDlqęovW8;y2/抇U ߡ⣚AG-l#^ &fK`U$T'$S(p_w@7V/^pCQTӠwe| ^U7*˚un9a )V33UϿ s%tEXtdate:create2022-01-24T12:43:16+01:00DX=%tEXtdate:modify2022-01-24T12:43:16+01:005IENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/device-write-callsign.png000066400000000000000000000034721501654372000251670ustar00rootroot00000000000000PNG  IHDR(-S}iCCPicc(}=HPOSEB;8dNDEIP VhФ!Iqq\ ,V\uup77'E)" /8{B4c tL%b&*v"0;#3˘$|b<˿Us",3Lxxj68GXQVωGM: #8\xfL#b6fES#$NBcg\esr2i !E,AUPuR,h?trȱ 4Ȯ ~O{I88@.Ш94N3p:0IzEm⺥){0dȦJAZB>3e-гͭ MJH}>{Yrd cHRMz&u0`:pQ<PLTEAtRNS | w^NFfY5q/Il?d@:)خbKGDB= pHYs  tIME ,#7pIDAT]@ *"X Jn 0o3ɹ%(AD,A8D Ȋ"](ԼWZ Z4ؾE37eu~w{}{nOm Qr?lw0: ^pX8,'tEXtexif:ColorSpace1I!tEXtexif:DateTime2021:01:19 17:22:50wOtEXtexif:ExifOffset178V~tEXtexif:ImageLength32M7tEXtexif:ImageWidth32PtEXtexif:SoftwareGIMP 2.10.18#]3$tEXtexif:thumbnail:BitsPerSample8, 8, 8 StEXtexif:thumbnail:Compression6epWtEXtexif:thumbnail:ImageLength256Pp0tEXtexif:thumbnail:ImageWidth256(tEXtexif:thumbnail:JPEGInterchangeFormat304Gh/tEXtexif:thumbnail:JPEGInterchangeFormatLength1651F*tEXtexif:thumbnail:PhotometricInterpretation6 tEXtexif:thumbnail:SamplesPerPixel3ZtEXticc:copyrightPublic Domain1["tEXticc:descriptionGIMP built-in sRGBLgAtEXticc:manufacturerGIMPLtEXticc:modelsRGB[`ICIENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/device-write-satellites.png000066400000000000000000000005531501654372000255410ustar00rootroot00000000000000PNG  IHDR7 cHRMz&u0`:pQ<bKGD̿tIME (1hIDAT(υKBq/DֆաUܜi E\qpF %n SOK?{<˱@*Y f%w>G9KT]&،*+f؀ a)XPÿK҇Ғ64_#ϓ:"nuukp()Ijbzt߁1ڹSk)ll^:cI4 "GIENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/device-write.png000066400000000000000000000005211501654372000233650ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME +#xDIDAT(Ő9 <(, N(UM iQP7dĒ? 9;Gvtx%tEXtdate:create2022-01-24T12:43:35+01:007 %tEXtdate:modify2022-01-24T12:43:35+01:00FȘKIENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/document-download.png000066400000000000000000000006641501654372000244310ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME 1QؚIDAT(υ=0 _35zP9D枡w@bdgPǐ8R"|#[ĞWv7?:j8jp%!$gCILQp1#|)r(ܜGa SE~_."~vIKoNI2&]%tEXtdate:create2022-01-24T12:12:49+01:00de%tEXtdate:modify2022-01-24T12:12:49+01:008NIDAT(ϥA Dѯ!*îN1 $2>:hd*)HA"mh 1n7Zs%tEXtdate:create2022-01-24T12:10:19+01:00(pZ%tEXtdate:modify2022-01-24T12:10:19+01:00Y-IENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/document-open.png000066400000000000000000000004721501654372000235600ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME (c-IDAT(c0O ( )4a(8WKj~%tEXtdate:create2022-01-24T12:10:40+01:00%tEXtdate:modify2022-01-24T12:10:40+01:00UIENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/document-save-as.png000066400000000000000000000005511501654372000241540ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME 4w\IDAT(υQ C+n-dkJHdmPS]]np 9&U6eSU0KҞ^%~0=^)FQ r%tEXtdate:create2022-01-24T12:10:52+01:00=%tEXtdate:modify2022-01-24T12:10:52+01:00`IENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/document-verify.png000066400000000000000000000007251501654372000241240ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME -33&UIDAT(u @^>K-H GEy$󽝙jΆ%%W6_*B`qaJ1fm&` L' TsL[54Nܺ[ymĚ7(6+r# Eop+la(ĵA`D9?§XƸ"䟎Oē.$41m s" +ӕ^5w%tEXtdate:create2022-01-24T12:45:51+01:00}$%tEXtdate:modify2022-01-24T12:45:51+01:00yŘIENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/edit-move-10-down.png000066400000000000000000000007421501654372000240570ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 8-/T$IDAT(ϕ;@Ep!v vvD;A6"#d0W{2AASs+B2)bT4U}]iӹRy6nhRa (wɃv3žy(Tny=yH<{yl];-pbi`1Kq%tEXtdate:create2024-05-04T11:56:45+00:00P$d%tEXtdate:modify2024-05-04T11:56:45+00:00!yItEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/edit-move-10-up.png000066400000000000000000000007271501654372000235370ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 84eIDAT(ϕ; @gI<`rUllb-l6E4!$vocDxh ++xݫw%bk-^ղ)B&GsHq$j1aA=dJ`G7.ԟFvȺ;l0kw8= &.be*w01׍j%tEXtdate:create2024-05-04T11:56:15+00:00%tEXtdate:modify2024-05-04T11:56:15+00:00iGtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/edit-move-bottom.png000066400000000000000000000007661501654372000242040ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 861IDAT(υMgB&@ ,ЌQZAxg8Ǚ77gBTs=kD>\kPdf)Z@FY ٞmO~ z22:IΤu/Sc. ü#KBF/(݁ڗcs^ҧ-TUi}?}VPs]%tEXtdate:create2024-05-04T11:56:54+00:00:N%tEXtdate:modify2024-05-04T11:56:54+00:00KBtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/edit-move-down.png000066400000000000000000000007001501654372000236330ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 8# IDAT(}07zTKEe?]  w.d˛% $~:U.b u [_6lLy%} [h[@I%uߛٝQ,g@?y&%tEXtdate:create2024-05-04T11:56:35+00:00Z}%tEXtdate:modify2024-05-04T11:56:35+00:00+@tEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/edit-move-top.png000066400000000000000000000007621501654372000234760ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME ו%IDAT(υmP@2 B!tC1HɁJBh C}َ2{7ҮvPPRЧ-385Df"B?D"dTȘ7`G@˅*=xL{iܨ1 +U+C|: ^_uW7e$EWY%/-|`Ι!woZ#%tEXtdate:create2022-01-24T12:46:21+01:00Y>%tEXtdate:modify2022-01-24T12:46:21+01:00wIENDB`qdmr-0.12.3/shared/icons/dark/16x16/actions/help-contents.png000066400000000000000000000005611501654372000235650ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME .$zQdIDAT(Ŏ @PEC"]끢tb+8$~{39X;娰V>E) HKSO*B@'=09tr%tEXtdate:create2022-01-24T12:46:36+01:00T.%tEXtdate:modify2022-01-24T12:46:36+01:00 IIENDB`qdmr-0.12.3/shared/icons/dark/16x16/apps/000077500000000000000000000000001501654372000175755ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/dark/16x16/apps/qdmr.png000066400000000000000000000012541501654372000212500ustar00rootroot00000000000000PNG  IHDRjgAMA a cHRMz&u0`:pQ<bKGD#2 pHYsv6tIME ,IDAT8ˍ?Hpi^%XH A "jPɠ)DHh˨4(jB?fCCt$*=xy< u) /nJZ:w Rm'N?\gg|5:&ZS2~)Y=HSA "eINP)3KsT 6؀7Q@.V:E>?A_? zS)ҳ|7,D{Pwei*NxQ_"[B<f_6E(C+!?/H.-LS%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvIENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/device-read.png000066400000000000000000000006131501654372000231440ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME ,~IDATH ay=r$yFq]`7i̫o=>Yjm X]MV<N:IԲu.ş za%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvIENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/device-search.png000066400000000000000000000014321501654372000234760ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME , IDATHǕ?hAFDEX@ kER[k Uఱ$MPb' !FM496wY8;;{{S}f[u)]фF4@[ZײF- (Q iC;ܗ1VȒOg!OvJ5Ypڵ6c90 ? (,q'-QQes$uKك{. {0ei+׌Q P|K3F*_f*ݵwT8cEP= fVR*&mjM6%I,6kͨDV),ٵB.>K6s-U^3ʹ\3c,{Fݡ~ӣ$Yhv gԜ2l(l(qdNYDd22 "ʴ"-$d6Gm.JH%W|N5SvR^ʯf4Z{-*B] ev ʉ\< . &eV%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvIENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/device-write-callsign.png000066400000000000000000000074531501654372000251660ustar00rootroot00000000000000PNG  IHDR D}iCCPicc(}=HPOSEB;8dNDEIP VhФ!Iqq\ ,V\uup77'E)" /8{B4c tL%b&*v"0;#3˘$|b<˿Us",3Lxxj68GXQVωGM: #8\xfL#b6fES#$NBcg\esr2i !E,AUPuR,h?trȱ 4Ȯ ~O{I88@.Ш94N3p:0IzEm⺥){0dȦJAZB>3e-гͭ MJH}>{Yrd cHRMz&u0`:pQ<PLTEJjDtRNST7[ &t uq^n)#sp6|"+X]*=?f<bKGDEW pHYs  tIME'ƥIDAT8͓ lJv[xgJFk:79 @ÂP k@t(\~ x")T:g99_@EI\QlNFCIhЊ\q.Go4h x ]Tb b bzvH'Â;rv2Cps .xwHQ ?ioeXIfII*   z(1 2iGIMP 2.10.182021:01:19 17:22:50*0sJFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((?%tEXtdate:create2021-08-26T15:39:02+02:00M %tEXtdate:modify2021-08-26T15:39:02+02:00<$tEXtexif:BitsPerSample8, 8, 8>'tEXtexif:ColorSpace1I!tEXtexif:DateTime2021:01:19 17:22:50wOtEXtexif:ExifOffset178V~tEXtexif:ImageLength32M7tEXtexif:ImageWidth32PtEXtexif:SoftwareGIMP 2.10.18#]3$tEXtexif:thumbnail:BitsPerSample8, 8, 8 StEXtexif:thumbnail:Compression6epWtEXtexif:thumbnail:ImageLength256Pp0tEXtexif:thumbnail:ImageWidth256(tEXtexif:thumbnail:JPEGInterchangeFormat304Gh/tEXtexif:thumbnail:JPEGInterchangeFormatLength1651F*tEXtexif:thumbnail:PhotometricInterpretation6 tEXtexif:thumbnail:SamplesPerPixel3ZtEXticc:copyrightPublic Domain1["tEXticc:descriptionGIMP built-in sRGBLgAtEXticc:manufacturerGIMPLtEXticc:modelsRGB[`ICIENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/device-write-satellites.png000066400000000000000000000012261501654372000255330ustar00rootroot00000000000000PNG  IHDR s cHRMz&u0`:pQ<bKGD̿tIME ($v)IDATH͔OHqǿA*yɃ$,2"ءK ѡ. *HAuhEytHD[bH-yi`"cH{{?^wݻ|~?yx~TSxkol;`X ܾaBn j! Pd7c*LJ#,1 '2$r8>r9z^:Ǥ0i%c3T<%-t@1In}>QV]nbQ~3,/fQL.'mU@Dme#gbs=PO;,gyE [oY+a:*tDEo#ʽ#ȷ>K+hLlD L&8q=} i2nHz.<4`Ĺk{(<[W\n;Pcm;-}٠ZSuE䑩YIKs&i2XM{O?>/O2IENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/device-write.png000066400000000000000000000005621501654372000233660ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME ,eIDATHA DQӓ$CAA]xj`#h`y@0KpCIAd)'D6| Va゜/pjt"O%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvIENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/document-new.png000066400000000000000000000006061501654372000234030ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME ,yIDATHK xpo^7;  i aAn/ !7PhI1%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvIENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/document-verify.png000066400000000000000000000012611501654372000241140ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME ,IDATHǝM/Qi$/蔥_[THGHRXJ ZQŴut|=s9 e-*$Au]L[kТJδcoi,$A&S":@)0;= _H'V>s2C jX?%Ĕc۸"l2)MXU$)n:PW~l^KQ?1thyW"t{6%}jKAH [φf9E8۰%'12F%R(֬?a2"2M&W"4挽m(%Ntߝ c hSi Edl!$ zg\-Ma@Elk qThZMJdM{:'Ӓ^u.t__ %%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvIENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/edit-move-10-down.png000066400000000000000000000011651501654372000240530ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 94R$CIDATHǥ=N@ߒ@aN@#4Hc[(87#q#!G2;gApS<7xl܌sܲ逸9@Cr|`E_|%?ۗ 'rK` } j'>DKOLIۧ蹍aj+\w(pW'8p#v0=̆lR @g}P-A)ئ}>nTe>}J])̓NA0}$}_BRD?r/[Hr %tEXtdate:create2024-05-04T11:57:52+00:00J%tEXtdate:modify2024-05-04T11:57:52+00:00ǶtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/edit-move-10-up.png000066400000000000000000000011321501654372000235220ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 9`e(IDATHǥ1N@DP# !AQ9ACA\pAB(koiF_Nғ7X'"X;1srə5X5س&fRҜ ;}szNk6]M7 wzG>&M7MIPx Z2J^[z&ui{}JR@mp p^Z>v0ypMIb,('r. koա }rh$l,6үZַ%tEXtdate:create2024-05-04T11:58:01+00:00>>%tEXtdate:modify2024-05-04T11:58:01+00:00OStEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/edit-move-down.png000066400000000000000000000010561501654372000236340ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 9(F%IDATHŕ10 E(bjwJ(#p j4vwjٟX5VHcoP->{hRjut&@Id@CUd-S U`i*eV΢C@%@,@9%42IV_3X 0a:?VҏܝSB:`A:?[;0Eox)(kvE1%tEXtdate:create2024-05-04T11:57:40+00:00޵%tEXtdate:modify2024-05-04T11:57:40+00:00 AtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/edit-move-top.png000066400000000000000000000011651501654372000234700ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 9 HiCIDATH͕N0O\23?RPx)Hl}$)Z ɔ&!1:ɐ(dq#ok1̳OxGipjrg\ fP#֬&D 3^/QPB b3*@|iv2c {a[|v]Siטr%k\R rM\s>0aFp@ 9LB>;N2`P)yG)>}vD銠__]}ʎ2% H (xt鳳peukxcXuu&>s]}2-PɳctOPV^) !?S%tEXtdate:create2024-05-04T11:57:32+00:00p%tEXtdate:modify2024-05-04T11:57:32+00:00qtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/edit-move-up.png000066400000000000000000000010331501654372000233040ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 9 BIDATHUA@ x?kr >Ac2t(BH;6[C̋d䄥g8!gٲVʑsI5i$hOE:eh>4߇ qtYhbct:8h}^`'c!,pvqF 5P+}!q}A=%tEXtdate:create2024-05-04T11:57:11+00:00I-%tEXtdate:modify2024-05-04T11:57:11+00:00rtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/help-about.png000066400000000000000000000011511501654372000230320ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME ,\IDATHǝSMK@}4Q"7m/^ś( AA MS6&; {;3 B}8b+jքũ T @i %Bc#(Z6CSA/p hp0' 9]nD{&"'i`:=_]SXBӱY][oe3juB=X_ϡ%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvIENDB`qdmr-0.12.3/shared/icons/dark/32x32/actions/help-contents.png000066400000000000000000000007111501654372000235560ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME ,IDATH핱0 EP60 %^RIZv G8G.v@Hݳue! JĤY7(8RN- 7͕ļHc3|d! } [>FꃘT{r_X1NlPD`;8R?#Sa0PZ2L%n)ĸ)jU%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvIENDB`qdmr-0.12.3/shared/icons/dark/32x32/apps/000077500000000000000000000000001501654372000175715ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/dark/32x32/apps/qdmr.png000066400000000000000000000031031501654372000212370ustar00rootroot00000000000000PNG  IHDRyyFgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs  w5tIME ,IDATxohU?mnbioS!D!鏡4-ZZ$FTD^ Ò1 Bb[/,L1m(6 e]n|Ww9ss9AxNdT|%ѡȅTUB7E)>cnair~)ၸaA.0m#Oyd]GvAyd䑳M|Mv?ifMde*BRw;MqɁhؙ&5޴+>ӧzf3rل\S yb©DWgOKxy;ʷ=?yU J]*k d XDd@jf m|&![ Y^F2?ݮ~,`t[xdUrYLV!fXLjLV, 5S sU4kVRyWP^Po@G|zFȃ &dGvAyd]GvAyd]GvAyd]"r-ǥMy)J*FBޏ=O꽼&+.9jlo+.T5d JwR.# < .HyEݔPvҸX3,׭գƓ3Hx 2I$dexN47!6|`#\a$ɕWbJe$,k"ZuN#2ɀF$׌/\RՐG& q O(/i5,0"yT\sOU"Js9ARWC6#b5&;жxH6'e(uUIrhveV͗9p9[2ZirXi&'!kVrbBDLۜlI-tB,|]s:0|hl{a:YtuNj( md "A`տ[%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/dark/index.theme000066400000000000000000000003411501654372000201760ustar00rootroot00000000000000[Icon Theme] Name = "Dark Theme" Comment = "Icon theme for dark modes." Directories = 16x16/actions, 16x16/apps, 32x32/actions, 32x32/apps [32x32/actions] Size=32 Context=Actions [32x32/apps] Size=32 Context=Applications qdmr-0.12.3/shared/icons/light/000077500000000000000000000000001501654372000162335ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/light/16x16/000077500000000000000000000000001501654372000170205ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/light/16x16/actions/000077500000000000000000000000001501654372000204605ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/light/16x16/actions/application-exit.png000066400000000000000000000004771501654372000244500ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs  IDAT8M0?؈A7x/R bnNͤ7i[Z)h(- 0H,$k :s'EЫ{֤\+`P)p̣vw ${GKͅZewx]S_''`LmL VE v1 9=ODD qmT940sȰ)_?L*13B#? =QP6>*IENDB`qdmr-0.12.3/shared/icons/light/16x16/actions/device-read.png000066400000000000000000000002531501654372000233360ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs  MIDAT8cd )bcQ`T !Y`85cA !Z36CH֌lٚ9рy1!2 9 IENDB`qdmr-0.12.3/shared/icons/light/16x16/actions/device-search.png000066400000000000000000000005351501654372000236730ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs  IDAT8*DQO#< $)<"D&wkLIMn?1g\9fW[g: &:Ģ?ǜWgx bh'_zϒN@3sDes`3rSg=LTQP* Oa*Pm'a۩s~I.[*&15\ OƯrd8(Bp;ɟ#_S{#[ Wo@IENDB`qdmr-0.12.3/shared/icons/light/16x16/actions/device-write-callsign.png000066400000000000000000000073521501654372000253560ustar00rootroot00000000000000PNG  IHDR(-S}iCCPicc(}=HPOSEB;8dNDEIP VhФ!Iqq\ ,V\uup77'E)" /8{B4c tL%b&*v"0;#3˘$|b<˿Us",3Lxxj68GXQVωGM: #8\xfL#b6fES#$NBcg\esr2i !E,AUPuR,h?trȱ 4Ȯ ~O{I88@.Ш94N3p:0IzEm⺥){0dȦJAZB>3e-гͭ MJH}>{Yrd cHRMz&u0`:pQ<PLTEM7TAtRNS | w^NFfY5q/Il?d@:)خbKGDB= pHYs  tIME'ƥIDAT]@ *"X Jn 0o3ɹ%(AD,A8D Ȋ"](ԼWZ Z4ؾE37eu~w{}{nOm Qr?lw0: ^pX8,'tEXtexif:ColorSpace1I!tEXtexif:DateTime2021:01:19 17:22:50wOtEXtexif:ExifOffset178V~tEXtexif:ImageLength32M7tEXtexif:ImageWidth32PtEXtexif:SoftwareGIMP 2.10.18#]3$tEXtexif:thumbnail:BitsPerSample8, 8, 8 StEXtexif:thumbnail:Compression6epWtEXtexif:thumbnail:ImageLength256Pp0tEXtexif:thumbnail:ImageWidth256(tEXtexif:thumbnail:JPEGInterchangeFormat304Gh/tEXtexif:thumbnail:JPEGInterchangeFormatLength1651F*tEXtexif:thumbnail:PhotometricInterpretation6 tEXtexif:thumbnail:SamplesPerPixel3ZtEXticc:copyrightPublic Domain1["tEXticc:descriptionGIMP built-in sRGBLgAtEXticc:manufacturerGIMPLtEXticc:modelsRGB[`ICIENDB`qdmr-0.12.3/shared/icons/light/16x16/actions/device-write-satellites.png000066400000000000000000000006541501654372000257310ustar00rootroot00000000000000PNG  IHDRabKGDaIDAT8ՓKan=P\%Zt2AO'ACp\kIZniH!^xx|_El*$ 338TphL&#Bu8h4rVTy$Ί0LT*$In3J1N\.3zӹ*G0$4ͧ|_~I 7jݨ@ej!i-jz+8N$/<\.kma4E<FA ?ɿw5=16g0 `7xg?֤3p:rk<\I\aҖ9I l"iOP63.€?3d >ci5AVIENDB`qdmr-0.12.3/shared/icons/light/16x16/apps/000077500000000000000000000000001501654372000177635ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/light/16x16/apps/qdmr.png000066400000000000000000000012401501654372000214310ustar00rootroot00000000000000PNG  IHDRjgAMA a cHRMz&u0`:pQ<bKGD#2 pHYsv6tIME '>7~%IDAT8˝?Ha<K)p)uPB`ҿp) J)PGܠCITRCrސ9$ZD)Hkpz,|yއHuf6 y ΓL ־|.xnevz3o&{myg^7.}j )Ea'sKsو[v4%0z?Fi/ +*IfIH2bZdq e.^ca>^1t2L/.K\(pd8N} ȷ2q^drY)c/$[?hLR . .q4hг@P4-qwz܉u"<%tEXtdate:create2019-12-17T22:39:31+00:00s#%tEXtdate:modify2019-12-17T22:39:31+00:00HIENDB`qdmr-0.12.3/shared/icons/light/32x32/000077500000000000000000000000001501654372000170145ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/light/32x32/actions/000077500000000000000000000000001501654372000204545ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/light/32x32/actions/application-exit.png000066400000000000000000000010641501654372000244350ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  IDATXױn@_! WD (2]r D2 P (Lp,{omHSxWm<#6 ~;jD1[>>V·l_3wˀE4fŦ=5qh{*dQdeڇX=ED9!f96OD]IC|T'D7:HLiLD[xψK >!0&+<)|K n Yb"3\ "gނ>E8ȽS4!1]E<%4=]ELqk t@xvkvԓJX8Bt,kX_Ȥ'X^]JFBrD96 Ys</gb@Yʫ*tm_jym Gq<őVVu=t[Ԕ!IENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/application-settings.png000066400000000000000000000011611501654372000253220ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  IDATXˊAe FQѽ57}A nu\N'PFWP0xFP!:H3I>8Ω i=\}A Qo@+ee<xXwr( Uâ?xV_k;sYblĮyoшwg-U`q30=1x9Dy0;DEVK$I sBo7y#VITS˴!q"FV-DZl .j# wu='W;ޅ7PӮL98QR4oa%Va1K* ? 6LgFzDUQۈZ)ͬ|Zn=Y+opQՉHLCxg^y7% {<|l K'_]c߰?p Fy+8e͓w2u>I`J7 $OR'_0IENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/device-read.png000066400000000000000000000003261501654372000233330ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  xIDATX90 DQPPx8 R!Tۍyؖ@G@@|k{f!2@HD:@LgxGeq<>B,WcqS}?8̉JIENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/device-search.png000066400000000000000000000012071501654372000236640ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  )IDATXOMaϽV2",PSRJ2) dKƒHYMM66HSg&L Ƹ̰8x{_{{?}*ګlJ,4^.~YH=k.8&q cqgE%sHjKW*pEy]SL_Cx~$h|zͯ tڃ_#엣kv(kZv>.Ge\ܞC=P5qrbNsx6$J]1K]]5I3Y8QW$6g^D{'^ve4B?-,uvU99ί)C|eT :L< 2Vm8=x4*Ġ2%Tim6 Ÿp*4٤H#̏Xh[z+wT&8J)aζ$Eܪ1 @h^t | |*5C#dsS箠`r"D)S4f#@$>@T#HN"8xheˌ T2PcɑCUEf5Ek-CX2lfnŪ'Wn^.3PK.VR+*|U̯|ȑ=a RӖ5o]:uAaiÆ2DMiӦ2EK jtQMjͳ5Xqq"E ͋YtJ,ƦPF؄NA>Cȅw @7kع Q0>~꯶oGoGoGoGoGoGoGG<7_F|ٺ;iCCPICC profilex}=HPOSEB;8dNDEIP VhФ!Iqq\ ,V\uup77'E)" /8{B4c tL%b&*v"0;#3˘$|b<˿Us",3Lxxj68GXQVωGM: #8\xfL#b6fES#$NBcg\esr2i !E,AUPuR,h?trȱ 4Ȯ ~O{I88@.Ш94N3p:0IzEm⺥){0dȦJAZB>3e-гͭ MJH}>{YrtbKGDV pHYs  tIME3JBIDATXO+DQ?,L6vBċP%(0,X oF)]43sv{uEjhZ*3xj=KxCT7,DppAD0j Y }|~^PP׋2F,0Ȳ;8LMM�ZN3&1;;PJrXp"BMM Ac ^Z66&"JKK/u:2z D* ZDDJձ-*,,[|n13i6!I4 v;jkkQ__M&St~~-KOdQQ$Ic ͰlΝ dggSee%L&:;;'Fqfs4###XZuE())`@VV*** ⥝<feeeq`=`w:4CŌtb\)mه$; I+i]8rrK5yZvqupQb ́Vk+`UCC=OKMo⍿UCy^WT.Kt>+KcQTm\O14 [6=q:7HI "n#QDp*E ' {#pIENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/document-new.png000066400000000000000000000003471501654372000235730ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  IDATXԱ Plsu0=8ecD12% QBnB0bB,# LS 8PgS+ \obnVO푕3W2~s3(ѿ i0IENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/document-open.png000066400000000000000000000002601501654372000237350ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  RIDATXԱ .CZAB!b\\L`? @P/@~x4#[{IENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/document-save-as.png000066400000000000000000000004121501654372000243320ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  IDATXA Dis1nV] T6*X_;D!:#tUxRx"Bx(p!X3b Q "ѸϦȆ;qx8 ቧ#"q3_ K ԣ;_qRQ@Z<H{qhq 1IENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/document-verify.png000066400000000000000000000010701501654372000243000ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  IDATX׽nAB-@BJ"nт>"%JO\tH45Q a#kaIGvfٞ[5\R: 0K=@R36s¯8BSU|hc!I+q^^8rkbkm\߈U{^ъ )mN8?> R?"x98*ɕt߯3X Mw+|u,lZLE4y~)~vJ4lIENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/edit-move-10-down.png000066400000000000000000000011131501654372000242320ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 4LIDATHǥANPE:+p eԩ8r*l`bRqޣ= h%.г`C7x[JӸ%VoY`ɓf@@A2pK&Z&c҇\E C72-&>bbxhbfO)ݲ=\rN7煌=C_|p-xv#l؝M֧A.>q?NK&Z2Q f^2aJ6aN?4Qӆ13M'xyu?-vɆ1a%tEXtdate:create2024-05-04T11:52:08+00:00R>$%tEXtdate:modify2024-05-04T11:52:08+00:00tEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/edit-move-10-up.png000066400000000000000000000011211501654372000237060ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 3,ZIDATHǥ1n@E_$qNDI"el.b|GO LSMx޸Y p ȵSBWys=̖}^!w#Qo]G.;f+IwHϽ!J$L$d* d+ٴ~8v?+.}Wf!k&|T09`Eם|o'R]N|σdlo߂G ĘV1{-"<،˃DK[AUS^rAxq|T;%tEXtdate:create2024-05-04T11:51:44+00:00%tEXtdate:modify2024-05-04T11:51:44+00:00eYtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/edit-move-bottom.png000066400000000000000000000011361501654372000243560ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 4ӕ,IDATH͔MR@FDVdhN!z;x701Q\"%YDfE.3CcL7=?NW\8el9\!|^}φBxmxVsuE,DD%x2 Ƥ=b(sz|R $ B k\ًώROոRτROPq>Uw%J'|]TE,Y^hQ M͡zÌ fhkՖ Nt< x7~IDATHݒ90E2Jz.El+ Z'M ep*/o ??ʜx-QlYp (vl-XVhkuKnVcK3Xq*u&s68QRl\`c2/w 1W;8ЕUR‘SyP;+f`%tEXtdate:create2024-05-04T11:52:00+00:00pC%tEXtdate:modify2024-05-04T11:52:00+00:00tEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/edit-move-top.png000066400000000000000000000011331501654372000236510ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 3|D[)IDATH͕N@?PNҷ}SM|,&r=Z}IYN캵 ݡg$|3pd3Snˆ0KS{C޹6H ޠ&^M(uSdnA`^OdEcU"9xv4K[E‚|f̆bB$" Ơ`*̍\BYNʘ=NO|B(3>;(ck2'}Рn>;.kuѲߛiBśߦq„ }YyEɚJP?V6%tEXtdate:create2024-05-04T19:12:51+00:00|%tEXtdate:modify2024-05-04T19:12:51+00:00mtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/edit-move-up.png000066400000000000000000000010031501654372000234670ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs'dtIME 3%{uIDATHݒ0ѥ}xGD :`I%^b ]_w/'773t:@EG>u\q`Q-CK4$Pэ]|Oi9L#w1-.G=" K9&}79wQ9H=j8whKɍ&|g\d#Ў F)ޟh%tEXtdate:create2024-05-04T11:51:37+00:00/-%tEXtdate:modify2024-05-04T11:51:37+00:00^JtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/help-about.png000066400000000000000000000007041501654372000232230ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  fIDATX喻JA?D ` j!";h(/VVQI ^08Yb;3~t!0Sn+30)p1 3 :x00viFC\%'Xa@hbJ@HQv~,rউ #Ѱ@* G@KCHՈDlT47؂x+j,g )>[1 4Ik- 3d/VZ>ÿ?{Ӏ1ᠪi`%o j<;f] Hé#b! 3Yzp_*NKu:)IENDB`qdmr-0.12.3/shared/icons/light/32x32/actions/help-contents.png000066400000000000000000000004611501654372000237460ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  IDATX1 ?:uq3ng(z.$yuĩjB- uK)x-pΝ- h \g^٬r TÓ)UQlAFϔn[ mkߞ 8b)@(E[`>@pnX~5pR`QI p%[17IENDB`qdmr-0.12.3/shared/icons/light/32x32/apps/000077500000000000000000000000001501654372000177575ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/light/32x32/apps/qdmr.png000066400000000000000000000012001501654372000214210ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs  w5tIME ,NIDATHݕ1KP?P1A C!Zp.flעR"Ru0Đ$ϊ"y{r_SjP챿Z湒bq1N_@``]N8L1NmBAxE<N0LR A s >~jItEEmM," +GFRJV(^cȐuz],5TtAmTmHUVGՅēX"%8X/#RD ~7Ab,ӫT@3u9t&ԛ%tEXtdate:create2022-01-24T11:17:57+01:00X%tEXtdate:modify2021-01-22T12:44:16+01:000ZvtEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/icons/light/index.theme000066400000000000000000000007061501654372000203710ustar00rootroot00000000000000[Icon Theme] Name = "Light Theme" Comment = "Default light icon theme." Directories = 16x16/actions,16x16/apps,32x32/actions,32x32/apps,scalable/actions,scalable/apps [16x16/actions] Size=16 Context=Actions [16x16/apps] Size=16 Context=Applications [32x32/actions] Size=32 Context=Actions [32x32/apps] Size=32 Context=Applications [scalable/actions] Size=8 Type=Scalable Context=Actions [scalable/apps] Size=128 Type=Scalable Context=Applications qdmr-0.12.3/shared/icons/light/scalable/000077500000000000000000000000001501654372000200015ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/light/scalable/actions/000077500000000000000000000000001501654372000214415ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/light/scalable/actions/application-exit.svg000066400000000000000000000004061501654372000254340ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/application-settings.svg000066400000000000000000000010201501654372000263140ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/device-read.svg000066400000000000000000000002211501654372000243250ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/device-search.svg000066400000000000000000000007021501654372000246630ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/device-write-callsign.svg000066400000000000000000000106641501654372000263520ustar00rootroot00000000000000 image/svg+xml qdmr-0.12.3/shared/icons/light/scalable/actions/device-write-satellites.svg000066400000000000000000000047531501654372000267310ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/device-write.svg000066400000000000000000000002171501654372000245510ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/document-download.svg000066400000000000000000000005661501654372000256140ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/document-new.svg000066400000000000000000000002671501654372000245740ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/document-open.svg000066400000000000000000000002571501654372000247430ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/document-save-as.svg000066400000000000000000000002231501654372000253320ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/document-verify.svg000066400000000000000000000003251501654372000253020ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/edit-move-10-down.svg000066400000000000000000000020661501654372000252420ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/edit-move-10-up.svg000066400000000000000000000020641501654372000247150ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/edit-move-bottom.svg000066400000000000000000000020731501654372000253570ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/edit-move-down.svg000066400000000000000000000020351501654372000250200ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/edit-move-top.svg000066400000000000000000000020721501654372000246540ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/edit-move-up.svg000066400000000000000000000020331501654372000244730ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/help-about.svg000066400000000000000000000006711501654372000242260ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/actions/help-contents.svg000066400000000000000000000004371501654372000247510ustar00rootroot00000000000000 qdmr-0.12.3/shared/icons/light/scalable/apps/000077500000000000000000000000001501654372000207445ustar00rootroot00000000000000qdmr-0.12.3/shared/icons/light/scalable/apps/qdmr.svg000066400000000000000000000015421501654372000224320ustar00rootroot00000000000000 qdmr-0.12.3/shared/info-8x.png000066400000000000000000000016261501654372000160140ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  8IDATxKAKREߠD`aCZlYm\DkH QHAҪf!QAFA潵{A.9w>sf杙w.N 0m :ur%,d%Y:bT |2*JЉۆC돯rm~U)ZT'ɼo5Na*iH;OCF|SC^Y` d(?TӝK ~oR T_L;pșk +B:jz. h_WS&!4w0-靾\6j A/Wv߁# zr=ы[Y3$K7`C 65Z(m/Bq"SB;/@}a6m앜|O؃8`=,#؃ TІL2a V$}տ.wVv"WCN5ux=gz0=ՔEB4I,'oU\|wY|gԢ8*Nb>s=U\ msGzL^"-bN<FH'rlqYq 8Y7.|_t՞9Y$˶Ŵ"oB78 Ŝ3+4c-^9urIENDB`qdmr-0.12.3/shared/logo-8x.png000066400000000000000000000030331501654372000160130ustar00rootroot00000000000000PNG  IHDRyyFgAMA a cHRMz&u0`:pQ<bKGD#2 pHYs  w5tIME AaIDATxOlTE?v%iBHRSE4K"!<4/&^&DDh*(T# ?MӀ4BZ?vj뼷۵o+3w}3{;3;oE0xQcM5Df;Ye:h&Y8a:ih.TsO95s y! l r6! \tmʗ,7ͦP=۲pMiwf0>ы9(ן3,ӳMruAE!vVOd!rrֲUx$g)<Tn1DQnԚ؝#.(" IR.X.ia[ RjPC6pȕj`+=jGamafg(Yѯ4-&i2>$A&hEkD^KU+c "GI`# E9dmCA9dmCA9dmCA'"Ls<}܍xD'y;Hg&ߦ밺FZKԼ6*Ij+o& Ir r6! l <\GY~e+I;qme\WMGNerfm7NȡY9(@W)iRkݩy95j]ŵ3ȗ438V\Rs-1u1(}Jd|\Շ\VP4,,}ڐDs]- Y%r! ȉ43(BB!Ӭ+!y:. 싓qdR~򸎩CY\zl&m-R@?|iviҘcTFRyY:[NIltcZ8r6! l,DּTp|gV\!'vPE?'t+y̾̋T9&u8W#0o)7]Gx"+iN2JU\#Rs=ˆ$|( AY+Q}ـD2|۷7k>eFpBy&H/w⇌ﵙ-><9Ãa>R"K3A^n AJcOtTJ]gJ:H{HOl` q&$xૼzXA?'M=#[^# l!c(PC9_ !x1CT&O78bg@3*/tSy*qE[{cFn |:&6O %tEXtdate:create2019-12-16T14:19:21+01:00-5Y%tEXtdate:modify2019-12-16T14:19:21+01:00\h.tEXtSoftwarewww.inkscape.org<IENDB`qdmr-0.12.3/shared/logo1024.png000066400000000000000000000721351501654372000157760ustar00rootroot00000000000000PNG  IHDR?KgAMA a cHRMz&u0`:pQ<bKGD#2 pHYsn$tIME %{,1sPIDATxe޿saKD,0E@1;֭b vwbwl;P1QT@s}13Y{zC8Y.`{IR%Wy6 {w3³ak1!< N {UCP#$I(/:@{wB^1oYPnj`o` Mi?#a݇0b/f?GGK$Uf.oXkk? żu~h +0eL?J° @Wt$I$ISu8iK?>%×KaW$I$O['xΥcp!ɒ$I8h{#?0 TlI$IREo ҖS8f~Aw$I$)>[8G`kP$ I$IIyBpl(.pǬ-[Xw$I$)g5]`=`^gow$I$)'t>{9C=5ݢ!$I)?:@rSpF-8D$IX fnn??@6I$IRt(|2&mEc$I*HR*>N]ϋ.ъu8 ߈.$I$e⿳l 7~H$Iƺ]?zqw$IRyI*5#|V58]"I$IX[wP+ %ITDHR-^.ZZwւYOEH$I~{ jFW, v$I*s@V[PftBlXoXt$IRypRk*>:BeN)]!IT$i MPYjy64]!IT$i} t=BeSG,B$9Hjij,PY4]!IT$iMvPyh: ]!IT$i[xP IT I٫m Ae03:D$x$zˠ*>:OFWH$%IZ5Av`tC$Ie@VIa.Qy((^JI$IR^x;P;&ITV<@VI^'BzS I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$% :@$ }$ȟ!V;X]2WҺ,q@$U7CP3ht8C `о7j%B lK„-a0iL 3m`q]4~',,I$IRCzQ+ ?Cip~Z!p9dgGϏçm0_rIRIN5/ov/Ffn%9:Ղzp+h5j^}1,(I I*@~sh)t{{B:]N'Bsk^ wFGJʟ$I*gM>T_OKLT{(v/tO$I@$:P!{. 6l Um8?f@;ų0,]'I*[$oS3^E;%&@;>p#X}$)8H2Rl :v8j,.*jmF5WfMg-L$IR!  .vo^ ~  $IšaQp-p Pqp}0T*H<@$*@*pV]]2W^ ma IVA;1U5/˷Fa~"II%à*~OMp+Iq$I+QgǡpMPE(<N zMk$I+K$I &X E(RnpzP09~2H<@$k/+ZA+k$I@$)C=5L Á/@a5 IBVp=fUFy1 Iҟs$I"+\|ˋQeV\}n.$9I'yλj]lк=\0:]"Ic$Z' :G(W~u7.$_$ !mtͶP&D$Iyc8b2~]lT[8vBR* I[ ;FW(*]EWH~ I-PX3D+pP-h4D I~lstr}a׆$IDՇoGtrQQ?8`"=DR IR].P.XVt$@UɃ}CͣK ZWA%2I[{ 庼Ч7Ԯ]"I)s$)i|]4zzn]!I)s$)YU΀VFW(.{EHR$IJVvyJI*Ix/52B)٠l2:BRU IEEW(%%A>*$)IUFA%JMj IJ$II~yE(5.Z+$)E$% qRtR~??BR IŔWGW(EMAI IJ7$)I ?"C#$)E IRyJQЪx)DR IRZAuĮyáPZt$K$IJR; LtR[UIJgH :!o IJ$I*XIst$@$Id/]!Iq$IR˫48H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)T6CPtiOh}Tx ~Pp74~"Wz8 mo.){!WUtRդ<<^tw/`0.L [h:,IȋUTmwúˠDž~s2χӠ5v.T;:XT`6XT`0L8,:ZV,w*4]vm`S`͠[4RSk o;y]Z1>>|f̊ [uOBӥ~ hw$I) i_cNңaY2I#*jš?a#kt$ImX}'L o_ s.sT?ͮEe$0<;9c$ $U Z':OB>E$scᮃP:2gDWIJWI{ n>@U$jEС0n_9̀$3$)j oB͗$I*G o̍GJP*XkpТvt$Igpps0`"IiIplM-ش 9h]%IRŪ}M`*I3$U|8eW.W$r<|@"I+?:@R+ ͂/I=}]$)wyrԼ69 ]#IRx \\]#)xIy" ~i 5DITU@j<>>HRn Ie,ol=. n]#IRYZ. v%0T ^mIVUBj(Vpit$Ii 8i<6FRv&V&/•C}K$IJG&,HR(:AaK$IJOpD4>E| Ԡ;\+lItzdI+@Jq; $:XNj5c$eI+J_pz-̉$I`ǎ:5*?o(iuˇf@a%$v_߉TyP~8ihTt$Iߺ^NsK$Un ir;t]"IH10c) _Q5*'(oy%/.$IpB'xs<:FR%%Bp9=%$nO]K$UNy*C%$iE5?DRD ~$)4  %?GH|$]nnFWHCMy I?Po" 8j<]"IVVI? I6 }2%*IKe&PcI±PI{>rat$IZ]-{gDWH<$NypȝP$D$;kA K$U~H3z :FWH$Nݠa ITV:}ޏT98Hú~.$Iei.l I$G3a IT6 {$IObh|~t$I*kU]%b9H=$IrbG~_tX–+$IRy=]!)BO.$Iΰm I $E ykDHR4zGk$ȋi _Z|]"I~]")Hk6$Im=8H۠Ը'B$:OAa8H$׍$I|(&BR )iECM+$IREi1 j]!)FB$tXu %R:-7$IcPKD9HI%(B$U@Eb8HIFt$I(yC_+$pVNt$IHy_Cm!oI@JZaAt$IHyP0;DR)]ITCo #b9H*XŮ$ ԅ=C$U>PVRǣpW=8L{t$%`( Uw!pP6pft$``-6n~ ] 8H IZ^}諠dK*IJW-`h>[.@Fpqcy5]$I6tn$rMx'8Pct$4]]#I$5%wBI_z6A@sk$rr?PV.$K$q?w9;GHV"p2F8HA8s,.$=%%T8H']$en;A-K$pu GGHVG$8b-vt$UFHB(8s1]"I$E@-K$IeiÛ3qW$ ``IRYtݶ.x|8yhII‰Gk$)gHJ6m IRy5kt$q@IRy6 WGHRIIw/Vt$ C$)5zN$U I )iuC+$IzKt$qm3&:DTjO#$)Ro]"Iaݵ+$)gHJVbآ7tt$Y5Uo׃JѤ]!I1$%]OAt$" /P;HRRj-ߊե ]!Ih;C IxwWt$)B^PIt$U<I$%x(B*$I9]!I@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I+$DH$Ii/It$U<DH"qt$U<IX,BTѦZEWHRI6(}]"I(բ+$)d},X]!IHߵ֍. )Y)@IRE~]!I1d-jo<l]"I(*@Ŕ%r I%&5Tr^0lGI׼6s|!BTԄZDWHRII: >̋$UٛB@Rf $=EIJ-}&E%M\]"Iq$%߃{+$Iiy0=0.D8HJ޷pɫ%0&|7"Db9H.pWȔFHҲxI I~p'^IRY\!,z(D9Hp! ~DH¬`A0I$w^e`%ձ;ܒ$$wM!%1s]X0D*OtǖpY.Թ6F= /I3$ܼ1,%D22}_+EHR I`ѺpE0%N ,y6D*IF/7⧣k$I%Syi7@?έy˂Inüw$)o(I㧝ࢃ82d {c)I ,m DIR O_CY&UMa![~ޅYEQ-a%W.x"XrFt$ex*<z\;~3Hp1_~ÿ$,IZEWs7a"IM»C`E@VÂbx u$־ >ͦCх}fmߎIW౽`,:I^T~jl-JмWCVw7TDKR\ӯ[W'aҽ0; C$,y`,0߿WU]_wh96%GGKR',ިz0G=(:Xr$e ~;x>@NBI@B4X9,{8 ':J IXv,(EHR| $I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$IIi!c( J6aÎΙP׀/nOÏ`8Xz/dW[򮇂AP:uN`I  |~"? K߅vzEwoCЭ?] >Ka]+0>sv /I@P3PsjC0 fOY/ 8W.mq1= j6F`P4xJW0}~=><&\ Y`_*P}2jzCлԿjAyWaV02(? &րY+a_$冼IQ -.Z]hz8 M..[@x_ !1?e3`鮰.s ?SEFVhz,t CA^/(x:؏| X, n ? _){aJ;X8  (R@W3|h:Y6= U6@dsXޫ_n?SqnpS`Z+&< s֏.Td9n%j"u54hV?/aXxxaXaVv]vk ,=&'Kk_ U¯a1k)=֚.ǟt6XzƯ /uz_l @J;U][C1-Sn{6_6pѰ]踠ax16LX1]p~П}G}-ѯLqRS$iUfCP*agNGKapl+Tu@xR=?@_̆bV N>?@p̷ǰwPWH$׆,,?:Ap&pb uXj;t=:} ;ZÅCˣTZ^[G@&Pe\to`P)9H*?I ՞6gñ_@XӆppSUZ7Hexح7]?1E* U>MVpJ14;)5,cKaP"I| $upxOx!2:< [ wnUZU&qAZ7`=p׾pG StVU;8thПr6CZ÷oGI$U  Lf;g#3dvw&SJkG{L3fe2KΊ~'e2;>zoLf繙̿d2׈~2we2{?T'5P2"+I3$?6uGo]tѪ+zk|w-U>e3y5.pl8p7h\]zw·߇݀U+-6#CwE>>X5\~ <:LyI6}OaK @t8Cx>WEMCvEe4\  MQ#UB[;"h%woǏ= .x n B^2v 7E[/[Da;֣`]kGnlFה*Kඍa5@R<Nu.)_τ GB˞%6EU58aShxOYS%X8 Xt$r')Bρ%kp\]"-ध3%p,^=D{1OFT փK$)t_ |Rq<  5.saߵ 蒊Wئ0B;4߆K*R8pS/:D8HJ}atIjz7Zn]v%p蒊Ue8 h; $mSK*V/p^I )IOSwD&-3$MUḭ%16N]pAy%1z<,EHRs!/og߂3KҴpېbtI6%iڢn l]CSJJ9 NW.b8hntIZ/.:/oA%ii)4(.U88&D*Tk']fGT[BT'FT>㏆=KP4 =uZ@עK$8HJʞ a#/K*0lqOtHm]Qy].ICQ0"S@ J`SK$8HJFʥpjxtInkw2=Թ8rst8!$6.\}WNEHRpC;amK*jTN>n]R9m..M_1BoK*3pPmI*r.wاMtH1 x($7T>. ۽]v:{o.>^oEHRsj́NK*&AVtInx.jOS:%h!;0r+iBK$|9Hy/Fc+C{p l%";4j -z@_XhP')9Hy>jFWd&B+rK١&Xk`tEvh4Bʗss{tEv)" GWd `#oX>ybԾZ )uvAϑ~tInr%UEdjAۣ+rGд}tEvitT8BʏV|9"{ t*^[& M[|4x Jv %m߁])M~f+rC%c:4I(rNGAtEvo mAݢ+KSa-:)u^&GWdC^ I*?+):j]]ׄ DWnA+K[kUS?%"䷃VyT~$*=aSZXatEn[("4l 5+[~q;]]BUt$I9WTm(uG$";-]ݪ k@%Y](z3:Dʇ54";5MnBSJ6]j+S/T>$嬺KiUXch]ZDWd❡gHQrV!mtEvj3]Z oNBC+[+٩ЫKt$I9`lDdƏC^qtEv[GWd*ՠ~POtIZԼ>";U1BʇT"l:]jͣ+Sgu76 :Fd]-FHRp,EdӢ+ӂ`i+s3 ?].nK. )'-y>.^iratEZ)|!,8${}}*̿5";-~F5 )7 !aNFWd`|DTz, ;'Bʇ5sM^tEv;+"}}|3>"; f-n?lsN ˣ+$|8HYSFWd+ۤ+o+ۜ)04)#Bʇ5CtEv ]淆YU+Ө_`m<,($ Z" )G9HYKK+as:,~'"L|]!I@R{Sm!ay0|>"L9fsgOS+$|9Hy fEWd,^f3΋-.kFWd_]!I@RΛ9*";7f28StEvy6j][~~&2it$/I9oU0x,)rzf][f .DTnKr˂i0{mHx~ i]"I@ROL.Ě¹!%Gs߃e_~+^  ) 3/+oETN#aެ4ntIp:PzGtInZ F~]R9}9#Dʟd|V 7?/.\Jo}c;GQma^QtI2gFN.m߮nODT.s~. ))'Nw)< <DjЭ!ix 2K*goGoFHRp9`%Ç5¼KP: >Rz[Ga~/n_rhtI0z%=(D*|7\?y2$֬3af%iu 9/$u `%i;.: JecpY0bDt$U,I$]Lox+xr\tI`  υ'G$f&*:$੷!%97=]cl/,&$MkQG}!\*8$MõK Kb|9GHRsg% K*֌ma%iS W~DT5u`5%i\fՍ.XMwˢK$)t}ᩄ{9DO֊.8m  ]^.L 24:D8HJڬA0bZtIq \)-Ds/kׁ{FTO·w9E`0X"P'\]"Iq$%Ɔ0/o 8e9s&S~+{?´!c7pGՑ%qK`%kNMVqJt$r$Npf{yytI<6^ |]r<_NcGǔ/kãD菼0N|92oFהNMc8+ $:Hb9H0,| -FWY CxLXItȂ`KᮅPzFtQ ՅGaaQ#B&p0袲p0}~ V.$I Nx*﷧ {Lљ̘72YkiLfĩA3jƿ+~T<9l@&3jLfwҪub&sLNJJ3odd.ZEwg2_f2GdJ_y %#T<@{VRXr66N7FGieuҗ%U+n,{0 |}]w. w_sD-9ˣ$rɋpa_:`s/Pejtџ[>F CÓ`Ϲz& K` ?2t\T/t\P&47}:Pz |.Y GUvuׄ9GHX IapSkxbXEty8=#/I3dyʪ8vtu-\<&Ky)9N_: _ ͅ9kT^j=&]a"`w\.<:V+3Ty${]{a4:s}\7>s+{3<1nnIp7fp0؞E}~{|ޓ_VgH Qr MBɋ@aׅ/'#w£__ U€ÞF@ۏ `WCC4Z;w9P&}-ÜѯF )URVW^ ZUV;B%й/t 7l1ï_`dcxm4~%<Zu[BM$XkGQ] |w,?|i'`-Pzwja7`htyl>y FV}aQi3oØBoi 5&̅_l @J@y(굆&;BkxyPt փ951|թ+ MzS %Ca@3cXT洅EO}-a0hr5<թ+v&uЪ  m uυ硤T=Ń`Χp̺}|㏃Ô0,95WkT9HrlZjB.Pp, Ot,<t:E+-P8~>΁{aɅQQM9vEyx(ct7P9 %@ Xyv>,:Fn ԀqW^4X1dF:)UR$IJ* I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@$I8H$II$I I$IR$I$IJ$I$I p$I$)$I$%@*Z[D))E)~%GHRzSYޗPmԺj>@˻gkt myW@d[''=Fm n `%`]1-ZoC<lr /ϿCP,82DGKRr2Vt 4*@Wa!هF(΅`gaQ ?m .”[>0a+ R( mJX!l4[U΅wA XoɝaC0e]8r>,3dh:뵀-A-! u:x(,+i[`0aUIRȋpa蒬3T9 JA닠[0^ .]oc?<qLxNOE s %Ce.t B`+[U7a0?|]> & s߅eÁw_)Wd0< 6Aa5U g`!P̍~ل`5a%*,Q ǟuڋɡPsP9a 挃oc`ɩѯ*ZEpč|ԺׄZt`ʿaQ0 >j օCThT?-L `Q0#>,jd+)URVIt|= - -Eu_868jNFtL;7UQEiq5. f fuO݀Ϣ_l @J*o Ԅ]Uo ,]R F潣ҷ{ҋkT^ öIЧ%7.7b+@J3Zւ=FD5apgMu 1ߕSx ?|@Wۦw#ʭa@*&G5:l UOQgfIR鿒@ၖpԫp`pTxv<L.RY(j;Cυ7fGb8.TPD4xt/8Jw}`Sڤ"I+ZD5pл]RVhXtVGZpc ظ)U_4*8g80Hi8wzOGZP@3\x&?btOGp]v蒊pijP 'σm. 3o )aҵ>0RqF` PmtI8*B[ FTaCI )Y펃'CK*VḮl蒴z^]R̆VgDpNmhrqtIŪs S EHR II6{:.u8k(7$MS;K:τGA[KT8m[ֈ.}78T{4D*$/8B^tI0!r4mw6?v.Q ֏.IӖaFWp0] ]"I@Rr ǴZKb5Y)vDpРatI:1 u+ZCqc.EHRrଵ`ۢK*-¡@a&$ U?#gETO@Q45.: . ))}ׁ=.<}J.[ n]QyGvuKwW8 ]]R9T= >>D*d%}#i]R4ΆKr[A%Ke-Kr[pPG0SCIIi Ϭs]k@DwzG E%U8ʯPp<(>%DʟH}=o}eo}E]o2P0&:`sض "7m%_hKQ$% $zPʭbm(Y/$v =/5gbB)SU~@{K*Á%T$ZM١w{{tEni06] %ݢ+rK㧡١E I*_r^_d/]3"]yR}y3"t:m]گk=]!I@R>>-"; _->+Cb/hUtEv;DGHR9s6UB 0$7T6:Ԏ.՞@a٥Q At$I9PtEȟ[B2QAާ%٣(]j6}JY*sNt$I9wG}Py&"4M uM.} o )u=]]jDWGBeмNtEvir;>Bʏ'W%"] ߇. B˶e;/"75+$8HYӠ٧=t=3"7t]}_yEWdŰfPxHtIv/AI*rVݮZ?CV\ %+SOX-'f itI UFHRp}y3UEhytEvk4W[hftEv͊N ?_+$|8HY{CQ٩+٩y;9" 5}*) m )gGWd&AW+['a?Zz jp}#(WI a+$|8HIFAϳ_e o>NyC+WZ)${.x*)]@%(I93Sn5oFWdjY7*Dd͢ [gOHQ~i,)'-8:${MωN~_FWdKuuX'$}FDHRp< l} *xX~gtEvZvL?"{-{>X ZFdы`#*Yr |y~t$IipdމN3.>Aa| /";-o ߟ̏.^ii7T>$K`'iM6ͣi0sFtEv{OtEvZPƽ]!I@Re|R%"; &]~&m]>;"*Yf]!I@RΚ1gtEvq:,2"?";<f]ݖ~o˧Ed3~I*r֒ L=&",l_21+,]}|jY~!|P K..'‡`C%T>$崟FWdiC02,"7|˼ ed^[EHRpӾZQ+e2ާ'q[üW +rì^+t"Bʏ6L$" GDWمP: "L&m]fw. S;CRsJ^#>|z2\dGd]aP.!"zf, )xGdxoKX_tInX:[ ]]WQGWϥeJ#Xc%0I9PzitEvX=|h,f֌-S__FWdc`T$o3+ìame8<";{ƍ-t^Bƽ^ )8bf3"L ]~-YFWIX!?? )wMDTn3g,]f\ ϾmK*OKr˂5 ^w / ˣK$|9HyO`%s#?(x|"t}tI`g~ em֐y1]w t ) 3ހk~Txk.6.Fn%irzdtIWp(=4$gga@yg5}ے#şl> 1# >]c6p͝0]t$U<I\,,o]RfwEm0f]R^C6DmpD(MlZ0n>i]"I1$%+s <5xv etIօ'DTnz[DK sntIyw66s\s6~2b|1 j sGG #2G׾ G`0xkEtI~kf#D8HJާo-_+K" &DP,m]RM=%}a!%1b_lvt$r0M*[!0r_8:x;"]u`M@AtPZv"4 NK$x:8!}2Ll I.f0|XutQ٘!|s5/8t8 lLva I<<@_5<7(PYuܕPET*nv,CUPrjQnrժuA.brU, @EJ d!x-*Hr3g9a9o -vՎ+ ]n!3J. ptʯ]6B H:&zv0w>a]%6w>Kô!Pq1<kzP cyowAE۷Ly.. }o(Nq#aнGEW_{z/I@G5̙W ;Ay쪯 W^6ŧcW6-nOP Wæد0f {\ SZAࡡPM3`p{0e?S0#*Sد$IRFk7fdA>vn«¼/B:3V0(!]Ch91k#/pe^:4!V !OB'! kCɍZH! ^ MBX?hsϽmEyO`h45wB86Py@)rmY/Bӽ { N~ d{~CvnW ǰi7.OPycTYO@ A,885yڝ KgK/n |",^.Mӡ%Y[d MSlc3}wnbX59X:l<*:@Tb TRb9KVJ./A0X @i#bWȺZv s].͂ΰ%,>+~ĮVQ~8#h k+`}7X9޿abG'TRb9DvfP^Cb)iaGPu~%MgU1 U^~F %$)q*r!TZU=bW(ÆP޾G$Ip$I$)$I$IJI$I@$Ip$I$)$I$IJI$I@$Ip$I$)$I$IJI$I@$Ip$I$)$I$IJI$I@$Ip$I$)$I$IJI$I@$Ip$I$)$I$IJI$I@$Ip$I$)$I$IJI$I@$Ip$I$)$I$IJI$I@$Ip$I$)$I$IJI$I@J݀#$I$\!;B$[8vs*= IR|9 RSbWHJ?)-bWHt[j.]!)Ī Ů$Ii5  GcHJcHfO<%v$IJp!φԃK$'Ī[cWH )vs+u B$S#lRbHJ?)$IJi!@J$)m&@pm4XV I.5/bWH@Ju$)]V|Eű+$J(}/v$IJfCah[G@E$)]>˅cWH@JaY?H;D$Æ <BRRj $I ކ*XYBR<R}]!IZ~3q%Xx^ IT ;+$ %^ZD$ՕXX2CI8H>$IueûtV Iq9HVEĮ$IueMX:9v$k`+$IR]POIq9H{»lpIT۪7[P|Iq9H+bWH6?aŞ+$ +_ٗöOcHPo I9Ho/Ǯ$IexcH@|~3 v$I o9Ʈ.PI/i cH]hf>I'$އ9cWH]>?v  ߁K$IUUVBbH ϏITo;ˏ]!)8HڎSa쯡4D$0]")8H +$Iz3̘BR񈯤>x v$I%0%2'$}y}aڽ+$I9?C$e&OHfãCcH|XRDRfPs3v$I6M\X2*v̵[I.Bӡۡ@E$^TDR#vOOA~$I%?JeD$S .$imdI.:1v=$ms|8gk$IJ6L{6<FR I;i]cXhVI2N]"`:z mrbH,΁k`.T8HB>,?tc&AbI %[FRJ¨ްW5$5l[m㡢4v{Hx82]#IRU~ܶ_ iUqOÖk$Ijx6/BQk$_T5*  O$ՊNpS3p6T]#~MZR#F+:Ǯ$+*؃:7vϛJe Ά?^gĮ$9,[.CAIu~p/`JI?m]!pIu䃙0x+o$I#_ecaE>Iin3QǪE$׆a`),է. "iv)zĮ$e\+f] []$)Y$TnAYh%v$I WØ#YPg(IJƮ>`x6;L]W<7? W{/)&OH09R8IHf6ry.]%I2TZ9N>WCV2I-ly ¸F|p];L@R&k M6c8q=T$%eȿWô`N(LA%e$qṪv_qSᘋ hd=~t*Ij0灹Ša0w. H;Tzup? Nz](IjJow oN`CP |Nv wV%tEXtdate:create2019-12-17T22:37:30+00:00%tEXtdate:modify2019-12-17T22:37:30+00:00@OIENDB`qdmr-0.12.3/shared/logo128.png000066400000000000000000000065711501654372000157230ustar00rootroot00000000000000PNG  IHDR9ugAMA a cHRMz&u0`:pQ<bKGD#2 pHYsQtIME &(k lIDATxyts( 4QZD.,eZ*(EXZT8 UNETP\`AY K $dO=5Kow|<|eP7 F^/} G,o ]KMfp(ԉ:|̼>cZi80'=8~ Pf* z`pH:az>Q;<ow#%W&Aݖ^0-&|x7 q|x7 q|x'ִa= 0(,8 :V-{^_l$84RCظ~fzk ρ{i L?]WW8fՅ.gs^@ɡG_z0;$ĘV$@Zߌ_^|"[czF4lvj OS<̇s`]Tu9LFpUCEUC2$fzfN]U2%SnѦEk$ ;l>WYH3zu OBe7AdaԚC * h,y X~]4(V?S<NVv* n8~ףqph*;fmʸu +fÈ!&ts9,fDU88 no q.K3bjr|c7]y['ցYWu%&4iB7<_T Y -zVak.'4(0M;l:C`0\ V1p̴샊}P^Y.0=q;{7}mZU%Cp|<j #jIUϨZGp=RpO\""k&% ?MUk3C6`11`03 ʴиVgH˄YDG\kE`ZIh\H ڵ22r\}IoVqfjP~ >Վo8k4iTRfHOO'8.O- Bzmh1$*z^wAX*/*!>DGp|c`l\ƽ`6֟硱͂0ܶ1I. )$ BR]|){MĮpQ7HEk f8k\Yκ ):>_'|x\/`W ( 6/;yYVPrH 9@' G~)@ѢW#z(]ousp,>nUG(g| G^#e`1n%ұ|!W>&v/6Ϸx1{F?kh#DZTJI@mۻ-?$5[-##kRY ,}3Rud pmke0 RBrȟsԂlsopݟ`\ \ އ}殀#p>.uaLX} ri8 X$՟((%x8R6wH %YKO3 RRԮt)f.7 q|x7 qj~ }c%`EZ~8V´B;,|A`c0#n[?tj7&b/ґ"|Yڞ/M'տ&Ҹv~/'U>͐m,_*iiҡ.RxihTwo$M=*o+%͜"ŧ9,k`5c^ |_Wۄ,O͕~Җ- ,Y=߶V_)?̗}4qtDiLb%;!e)5\ <޻~R7yG=K{gIo2J=6I1ENRfCi_wRJ?s1i4?H{+=H/m.&> 4Ԑn9lH HP<JS&CRsyPQ$ 3A+PBj* 7|87J2.0@& ̲[m]`p:L.?<¯ YnlSY߃| VRT\/섏M+qI´N` 4pFȻ t2æ%tEXtdate:create2019-12-17T22:38:40+00:000Jg%tEXtdate:modify2019-12-17T22:38:40+00:00AIENDB`qdmr-0.12.3/shared/logo256.png000066400000000000000000000144051501654372000157200ustar00rootroot00000000000000PNG  IHDR뼮gAMA a cHRMz&u0`:pQ<bKGD#2 pHYs:GtIME &IDATxyT՝o 4;C#Dv `\$D N☉;I\T"Aid}f$$tթ>]}GWoUVuխ[$Bn3X=_ޟ@֏?g_>`ﳾok?n`B83`syvc*"䪛`[`3k Kaվo+a8ǭmB߷XDn/k,KIxi^'gxTnm*j3Co$S 0$]Pכ m {@$`53*-= Eu\wh$HT"SL 0HT"SL 0HT"SL 0HT"SL 0HT"SL 0HT"SL 0HR|DyRσ \ E1((i%P4,9 @M&5-t(nۋHF1L(G!|Vφ}(c |q/]Vi܎e?WZޥ`}01.f Ġ*@_\9?S ^Ӷ nkuVu>/19D(TW¨TH'C5п9<4])D <JV~*YcV@)D-R;߯E|D: "w`7 3}'Tku?;?3n;EUٓ!)}'TvTFIYg2wJ "&H;IUJLGTvCjCTU*D @$`*Ds!'4zo}E2'bx-?p~?yOTJ};0R 'ܫa~z |߂,;@WoOYw29^p8>,{u!)pr(zv*Z `Rgޢ$aT ܔ\ 6f:발pH80n7ZEo#REe< =oZ &Ny0K U+~k_g"oRɽ?<-2Ё@d-?&N"!=:dwTNM}'r/SHC@qHEʚg wTizN!>+A S)ćf@|I@-AaL|I| Ӏ@K-y΅cxz2 H=iK!2߽Se}P|P5ԇvCS\N̚]O w4z2 ^8'}`Xv9ԩ I}T$x#1wYn[ͬf8Ӭ 3uvTB B(.W H IS0"f) K/ C`/pms󸊜\C}I|*w|jN6ඵ1wĦQS;IbS4},;IXV1oվ$6}ij6_;MKxw|{B<$N /<?^[V5Z~ &t[! Y{wWlX0֯#^sFء9`,&?}!xw,(y,qF/ct Α.jӋ"SL 0HT"SL 0HT"SL 0HT"SL 0HT"SL 0HT"SL 0}/K I7BH9eu`|y%\ zȪ 2{ur5$ a]7d}'©c)OZMX  C*HHpV:}4˃BV8p3[`n8π+i&O\ < P }6P[6BPw&,8 w݊'UR!0z56k5}fG|lxYNa [j`Uf_cb5ol6yf7{-Bf[x{0ˏlÉ\U:lUfMvwʹl!gktmwf/?hVE9fo*ZlP r\(lxoٹR=pYiQt=qYCɗ]fEۣWcf5wb)?"'w_6W'E?kCRjtd|? |5p0*/g'1[at\ǛVf`GfroAYSǯWdM6~|ۨg3(5=tifVЩ|]Af int6u32BRͬFgC; t3+=ȹmC=GBd3h(TQH)ZBR _HYhImr:sHN)Pi7BiDfv3+mȩ.[d9p;tp73YS %`[nf t͞àdŎof @gA^)t7sP%nwJ?3kF‘'*+re > Ia*sӠ{`{/&CP em`En-, Űgv/zG߹i/X|}fֺis|_l/ V\[rTtEi7=^[öcnmÁaw`SuN?Kv6_(T o bV\gxkRW1m$~)]e[P/O'TigFqO.5;ib|?mfْfvg2k} Wf1㔍2Y4`,}F#@=ddAz ̵6NWysжOE`SptX(_#p1t$_.J{w`9|hOքRI98!7+b!V 4sٱl7W+ g-j6xYzʼlRUݝ 3m[hVlOm~2,Ŋ͗1ʇ733Ĭ|?YAţ̆0Nrua?ۀ&΁M t} RAzXX kgB~m)09> uy_%‚4*|f|`/m3J!Ŷ[75۝|7'ͬN>3n4  J-Anfqt:6_׷{|áPZ<ͬ JkAdYA)n包JOv7CnByc$˖\zOq70jLwBB"85"if6m^w3z4.[dHPPr6!pA>7mWf,(=L8Pη9k*( {@Fw3 et3FwJ?Unf?G@4F†`s܌ۛ;{+Ye_[p+̻]f?-]+Žk[::ͬ=5%nB/V¸gnm7ͷ3<]?ۜ##5y|{oz)̀a $ },'#J͞o'sm^`fF*q=2)9fcvsweݨC%*Zſn㝿ͬ}-z"|y-ھ|ǘ-z4|k@Ԭ[5̎=6kf/=o֡,2?;X`.M^jv}3+G߬{f)8լShub;uf̺E$juٰ5fK7;¿p\ba!2Ώuܪyf w[}lpٵMj.|5^5lgf`fyDž;lvreEfe:;RzApIin 9Cg?6 6W6Z5W@`Կ?maS{Ȼ>h]n܆Ъ=[Moe6SamtWWitn[x◻giVkYhz+YfiSgW+]Sz~K! ~;ȷ+E-}v}aU @$`*D @$`@2_OHB/ρW|j)| JѮSOvBY9حž|6 s|%8vn;b-x$^;OS6VCN `S[g†|IꥰiI16*6xawH+p$>0Rf鎿<$P9Mq4o¯^wN⛞ͼ-p$$Vu`^GIҩu6_|O+nxd:^gbjOQ#7X-w}[P&^3尭#X'Vlksz tRgw7#PlXYaw$EIBjt? >|0ZߨNl}Xނ*;o=:p%tEXtdate:create2019-12-17T22:38:20+00:00C%tEXtdate:modify2019-12-17T22:38:20+00:00\IENDB`qdmr-0.12.3/shared/logo32.png000066400000000000000000000021451501654372000156260ustar00rootroot00000000000000PNG  IHDR n<gAMA a cHRMz&u0`:pQ<bKGD#2 pHYs JtIME ' ͎mXIDATXIlU޶7`A @HUKD 8h#ąFبHh6,4ReT#] e.=. i!Co79CdڗChacZH%tEXtdate:create2019-12-17T22:39:13+00:005%tEXtdate:modify2019-12-17T22:39:13+00:00DIENDB`qdmr-0.12.3/shared/logo512.png000066400000000000000000000325471501654372000157220ustar00rootroot00000000000000PNG  IHDR2gAMA a cHRMz&u0`:pQ<bKGD#2 pHYs yutIME %;0v4ZIDATxy]2'BD (9桴̴VUQS 5S)*j!1& <Ϲunsnkgu?ٟ9{ |N'h8΁ w>ºW.+೉0 7$IZ 4m>v CLi7C3>υ'&IR d  nO+*mU$I5L'XtfWbUԇg/=@')IR d'8Oy'YS^_ z$}`v'XZ;$IE0'\}SOV"hw_]*%I*FNO^kpy/(]zʒ${?QROZR>JSc=趖OPI$"Fwih"u Iy5 ۽:Eal뽒:Prph_I jA_\ //o S'$P@'l=aI}aȝ03[i' iԯN=eIʜ_ςϪ:W`pTت;zڒ1 @q(%TLM?5_'vI=yIʔ IagS5<lbҚ%-C}׋anVp ^+`ԉ捀$Uq%\p_~'ovu+8uY$z}ӠifgId %@S)[mA5,akarZN#U 5?NNRTC`N"U $8>5'u֦+14:>uiuY$mwC߂HBZ]I^NQ3{N::,RuoJ94KBZ){{T@"ytoMѦyFH54OVoN!* )jO^N!* @SLzCSV@Z`QS\~P5@Z9f_eρzP5@I*i 5WϡứSH el 4=+umPg)UaU0uaw(:*SQPy5[P=u iUx@"`dҰH! $IH! $IH! $IH! $IH! $IH! IBR> PVeB6(Jn*GkuaV8J~ nuvOuXg s_ɿ[nKzBN7nx AHZ0qXiT,Z;n7>ݻN"iE[.,RQq;^ M/$åCaiT,ZoAYHZY5~^GUh];~:Uq6\5z\:Pj <Nn凤N#iu L7SQ pp-F~*t@$*ֿۤN"ԭgn:@P5v{1uIm wAITt~Zi{p\cxC$J+#^R4ǝͯNDY[BGHZvx.u C m;Ik`PY4J+1LZ땬? lI@ST(뵁ONBiYl41uIRaq7:ұd48x'(+uIiS( @ OBR,SP:(C=o#egж_J+~4KRXR(y5u YRڎzAWh $a)W$)[.,6ҽl(Y JHYá+pa0b[S( jt 4 nmn?:S*?NW ôaWDSZgdvl~\> m{CP JMPY?-`^{/uoSgJ`ITx:``_SSR(yj}u}}wßwRowԂOHZI@_ePpPҷy@Epw`WdDDҪk'1: ^SMJWTT7[_k6+ gCH_hlN4y&u;θ v~(u+3̷_S'֜| m7u"gOSHk^Vp]PI;?6 .n: NԆ(k^Pr䯌c~MID9 "&u .uaf ʖWT'`کSHx>4=%u 6[tOB*Rd*u>->LB*ZPd$ʕP2%ݡCѩHWr#l6a%(z;FۦN!S^S(W%Sz {4u Iʓ@!IyH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $IH! $I(CHR,J&=R<|-sR'ҨJB KBJ8x>THD(IcR ow,uʷПBAxFND p+:Tx [NYԂ0PwNY܃a)™[=0ut$ʙ@ɽ=\=,:Epȱ(w <NDZބNYTf/N"9k5 aHÝ`ΩHկ nxqHGa_R'ϜMpC}[4WʜC_ï ^@ɤW?|~N$?T^}IHZy3gppW(O*>^Pњ3w1V:>[m:TE#~01xzk) Zx*I lh/p#<׆ia5*O4-@ˣ``v0}0L36JU]=oBdz`a}$(bp&'X;Fivt{6ֽ6im %C*UDL ov”-ai0V`;I\u%l1AIux#h$Ly>LSD㮡Ql;GT6 JFl{D#"Ǝxnt;~Ꙉxo^-';#n6b;""$-RZ".$bQNtۆ xV/#[QGWCɳ"\#Zzy# %G/8 ݡ>hy})I--iNO=ӳ-v%Sᏽ p?/\YLp64oZ.6z!p ~V78 Nn=L=UyD|ewSi(/`'l(b*Կ DVNَp̯acb>s*9'r.Z M=$vc%is>GVc8Sw^$p@8t"+uAp$K8F(5upV[h9#u49NoKdg :W_4Nbnm SZMἭ׳(7C^àw)VL7`NS|[SX1[}NIZԇ~?R'QN,*EҗPf$߯ϡ΁S|W)_!S|[鑰]+()u t:E&.n\V_Z.PI}4cWNMB9;:Eڼ N|u;AߧNQCN|jÆۤNQ?:rbPA5~ 61u;+ uVV(uehZ6MbZ|=nNjCS('TPNUk14ur5~"uek"PdPoCSZ}k) JyNjmJb> ݹnEt+uekjiXw "u )n eCR'YN|H`v>Oj % ;ZKB`pI 'V,[a)V9 q|$6}yʅ@SQ/w%%,#h)o0ws0H:SYaS(T7To8sWMb&:Ų}T:ŲM;&ߛ:Eb) jƙ)6Z7zL_:Eχ RXI-ap>qS(' STm~vU7k?ST`L)ozcQ7y /;QjbPA vKjlKb*w E7"'Q0)L)r@ti}ߗcS'+":R{T?&S|`ёS('ܨ`▩S|۴>?O _& L|CMb F4OۖԂ~5@iaP+=,n:D)V[C嗩|%GǞNd|v*9 ZwPohbP-noޗ:W^ p̿%uS LO+ ~s: ::JQ<xp|V:rcP_^7LH|`֫~'r6?>vplvbꉬ ?is,wl oTrdP2ݿIw#ւx:j 1-4# Yr ܶ1<=>_?nOw9{ga֜ԓXEO£vkv=SB(  {ijإ6QwpRO`y~/W|'rfPr/3g@fU9 υӇRzNZ^ ;m\zcėp?S| vp~Ce[R WI}EĭQq^7{]1o."bNFt=:[SDM".-߭KcwDlsYD"3pW\oDqڋ#$MֈF초\3"m%Ko%#>~kruإUΉˈsF[~I_+DDܸ/#LЈ-*#5bKDTv'FQ26 ڎ8iGD*]GQrK}YM%(±]RJaBШ)4,C_/ 3ÜA|# 1z7W`ة{?ՂA}S]al ̿N/HVlMºACݡ500X :iSMeY: {Oh}y;~ 6=n M&;`>`d%L,~ lz'i,z^j MNA߆1XIh6f_3/àgan0X<6M4ah|2txztVa0 apSbPHR-]wD41݇yE& ؠyDD[j?޳ߊhcD&Eɷx) `^aj?ICZ @ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@ Y$Iʐ@  (&A_@&T>+wFK`~wx oEP6> Ö{Wø0xD`fEs(u-w@CaL7 s@ OXZ> xڴ.д4~<΅ab'xxV,Ln&H:lO6@n -?s6iuᕓ᭗aT=Lk4I4WC_`!_&[B}`#0yf^8c_ މ$}]C!VSMx9M#7 bɢ1gةYDo祖dn"_/b ވX5"ΈK"}")1Tթcͭ#E̟xLDtXƒ">1{ľG>*~^ w/~Du8Q]d">95aKBFaĐg~S[F괈cGx͈?/D\?8]{H)u#o#f,*b}w(~kbԏBݥT淎x븈lQS[KoI/ш:߫@R##>W/TY1~XyG~չDQ57 G\ysuz/Erysߎbc e/}h~NGTvZ/߰hqj*O#<=bv/"#:~rqӱS.//uQP ҈ߍXPN^KߍzDDS8yv~N4 Vwa ?+7⍀ԎWBa=nYw8 ?SO`~7Vcq'q AW Cީ )S"M 7vRj}&"C6*,Ve5<#0gWWDipt aA;Ծ.DVppGޔYIK}p|Q,JbP66qaSOdl5j: }+\+gwuN=GNXTpm:YC橓|pqGZ$+ɿ_݋ԯ7Nb? =u'nLDl:ŷ(Hbt#l)sC8)VL{w(:8o%('\'SS|[$(?`)+W?ſO{ O仾|JMx wJF]RPN,*1aj)m zTK8f>:ŷ-x>=u0niK>ZXTp/w6Mݷx4u3j,4LkK_N^ :rbP-tNS'Y1KGO SR'3RYxk0aQpչ0oB@I;\ 6M 뉍ʱ0汴9]z"+gևpX5*mROD9(6?m G' p'Y FG^m[g_  > .-W;7_EK Շm`iԻBDuzDw1X#="1 Ej^g"fmfĈD6&QVP/As^3xvb?({"~Z wEx$IkPW㒈cFjz՝1hK=UBA#y "~U=㫸6bʺb^=UшF(bI;/g#,  b+]6WwG~+"J J04⶛"&[-}:≾}Gm~oYF;!="&ݾz?$#v8|xo(O@~0 0x}vtn []v}w^ z`aT^zSOgO3`y;XUN_1b8ޭ_υIU=sO]BP#~ |޻ ޫޔ],jw c [BqtlohEy {E~@yI2d$)CI2d$)CI2d$)CI2d$)CI2d$)CI2d$)CI2d$)CI2d$)CI2d|-*RPI֬qvJ I&XgJ Iٚ: :ҰdoHJe깰) {ïۤN!)17~S( @>^\:Ư IBiX7o rk4u S:R LjRTP#ONtŷK$ i_-+٫ @~w*;@ߣr?,y= IHnKF˧j:=-HFRUԁkowYRQq VP0| [FJJ@e9\ nm {NkÁHyπkwNUTG$pKC;$u")/]C0[jVO/੍`x4}jNLZ-F[]7u*<^P5i<vM[?LLFCWRSePu]C8%t:TM%xxP4u*|Lt%tEXtdate:create2019-12-17T22:37:59+00:00T%tEXtdate:modify2019-12-17T22:37:59+00:00IENDB`qdmr-0.12.3/shared/logo64.png000066400000000000000000000035141501654372000156340ustar00rootroot00000000000000PNG  IHDR@@PegAMA a cHRMz&u0`:pQ<bKGD#2 pHYs@@bCc[tIME &:l(#?IDATx{lg?ڢ16E]Y0̥i.cfddDm,K.XmubBHd%VuiKiQ.*9(qr~|7m߷}=E:Dnfqku aXK@* MNVB~vwhWo}J> ~D>{F z p ꠉ/_& ݀$Wu]aah o d9ҷBNXBzB.wױ+n{w)P0{ | O44ħЀ”[}Wcb k5{ 2c!2.0v\;{7 `#`^.Te7l 4RC .=۵Nn~M ( ^+5xUPa †$( ) 1vs0͡0,[!+/BL \iAOm.-$.;E Ӡl(F?R@=uˆ =Az\S3lL<h z 7<-@ox [a~2P k:C^i^Z 0nCO2AֻW+`0$-Y7S^k7 Xh7Usz fA j at+ @To T(L 4Cˎ`d`[Ox$Ta#$N!P`r L-!oQ &8 ~HW$Z35q"U^nHe*/lH^+)nF:8XQ7[f僧6 (iԀ(8jRcU+BvHr5v7\[jNo/OEX"SE>N1WxIdr" "RD>.bLEޝ,Rrxzȇ&MMO7`I AtXePvl}뗴 V 1`ۧ^M0 ,XChX ?^tm7`jnݿ9A=)inoEot>pb|Yx;Y-q;Aj,XdXmԘhr🪝fM (i Pc/BE;wj p(,/M1 7/á*CjJ'A W| , yTޙ0μ5K7c7P1Wt(2H"i"ydE}iYdXH9ݑk ;WkD8IFd53T׀k`Xж -@]lgY3A15%tEXtdate:create2019-12-17T22:38:58+00:00ϖ%tEXtdate:modify2019-12-17T22:38:58+00:00˼"IENDB`qdmr-0.12.3/shared/magnifying-glass-8x.png000066400000000000000000000024061501654372000203150ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  IDATx_UU\u, AԄ HeEBEHaC{IC de菅bLkmz9{ss ug鑟I7a!bSl؂l@Sb6bI|՘*2kq@x;M cX7בX_5ck|c3 x_ze.sUF| `Cnp’vG 6K88OXZ,aQp{[cv*3><>i`:LSnU†. K慱jtf&^+=9FX)Mu6]&gK:+3QW'pc'Y&FU + t'1 _%?un(kWvԖ_Ma%VD X#ƾ2c0Y|#lb'eqQ,~WHK8-Cmŧ{ni GElJU4Ĵ]3,YN"r=JHvy8<[Bj<28(O+b`s"[؍Iwb,wSD:d#w |פE3JiYsKzlW,=56LXCĻ:ɫ= (|@Rc3 Er#Fuq֮)B6嫷M0* GxGUU vEG$4Ҷ"QN)SRtThƨ\iM;8B4qg '(.V.,Ix)VܴD-Y0U8 Mu5nhbk $?t =Fe]g$ax%v1$W ę'=}mMޔ K@'ۉ/iu][yTNB/^rJob]DݱW"͟g9 IۣG2.>ހ 8-y3}eYDOK6wP!l ڃBkV|"4bGTpV3 g-pVS7 ;/9?um>(Lsi;+э3!| O[KIENDB`qdmr-0.12.3/shared/pencil-8x.png000066400000000000000000000006671501654372000163370ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  YIDATxQN0 ތwr2J z_*gJQ ,#s- d@7F%} ?^RّgP\[/V$ @j}DU,g{"#;)!3q>CBWAFHp× × //A_V9 ?|3 2bĄs)p+bO ߹ 1'\܊````````_L5-IENDB`qdmr-0.12.3/shared/people-2x.png000077500000000000000000000005071501654372000163370ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs  IDAT8*Qp 0C €H:e`D2܆|6 (g9YiV]< {~k_iuy+n m pT5oIkm'T qx-/'}fqݪ @10.#MXU4v.T ? X ,2c,ļ0Ab_Fof}IENDB`qdmr-0.12.3/shared/people-dark-2x.png000066400000000000000000000007461501654372000172600ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME ,IDAT(υOJ1_HhO"C@EW^;QDB b2P /UVwS5P ܓAY;9V1p+s"Z?"I*d$ 1d5o<->eyOR\C,4nFLmb\&fOVQM'ںZLnuH&"mRah%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvIENDB`qdmr-0.12.3/shared/person-2x.png000077500000000000000000000004321501654372000163560ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs  IDAT81NBQ @@`5i01MJ#!A y<<3u!V(#`]S\5?' j*90:7婦|xmTx8zA9epX.*~V>+vh@F"]^K:IENDB`qdmr-0.12.3/shared/person-dark-2x.png000066400000000000000000000006651501654372000173020ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME ,IDAT(υ1 0_ux kxO袃"KHIaB4i_ǟ Θ=_ ;Rj t,ig \!Jx*ICO:;_p ܒ­xBHbaqŰ)/1L!؇OHHsa"qQ2lT t6xP\̺%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvIENDB`qdmr-0.12.3/shared/pulse-2x.png000077500000000000000000000005371501654372000162060ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs  IDAT8ҿ.DA_Xv4VF(KPDD4,&*d̵q993Cul7FA VR?PysZA+1\5NZ`-;ư6MjpΡpq1m+s?ގYwkh> )v1;a:361k\\rW hDoQ{`*'Jo:}"y gq`u\*c%#[|,U:V1IENDB`qdmr-0.12.3/shared/pulse-dark-2x.png000066400000000000000000000007631501654372000171230ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME ,IDAT(}.DQF9whZ!BLQ('J/@BD\83qSwIiyo`S]k &i$:qDž_,Y-ixd8Spw dA-j47 SqJng>uU-z *\|tD%JK*6Fwi{r 2_Hb,%tEXtdate:create2021-01-22T12:44:16+01:00m%tEXtdate:modify2021-01-22T12:44:16+01:000ZvIENDB`qdmr-0.12.3/shared/question-mark-4x.png000066400000000000000000000007111501654372000176460ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs  kIDATX?K@Z)*R(]DQũK7ť8(h#ܥ)p!>?+`OXV x: ǿ@7x%oZuCik)hǁw)  ` Eܻ|ʚؔB_^\LmgRdb톬=u p^aںq 2c{Rf$0lQ7lސjo&!Nƾe 4Pf _a?TKweo,#?|3ȏwtuG[>!-}%O(`iDsZ`DVAP% x-,З_´% ++IENDB`qdmr-0.12.3/shared/question-mark-8x.png000066400000000000000000000015351501654372000176570ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs  IDATx=hSQX-J*"U]tRDtɥ.8 f_  EDX F4qksw MMʐ} !O)R'> Chnf"{+rTbRh_%ug"b/zQR^ e/E:-d _[Pd:n5r@,"eZCҋy-!9+aA܎ً!%X 1M Q sj5Zk븛\7 gq7 s0~f!%uAfw1?,Ցak I'n l~HM]`Zg{]Sc7_7E[t1 YľmN.?Z3@L]BLh0h %%%%%%%e҇}3$EL -@I -@XIENDB`qdmr-0.12.3/shared/resources.qrc000066400000000000000000000271061501654372000165400ustar00rootroot00000000000000 audio-spectrum-2x.png chat-4x.png globe-2x.png logo256.png people-2x.png person-2x.png pulse-2x.png ui/aboutdialog.ui aprs/aprs-symbols-24-0.png aprs/aprs-symbols-24-1.png aprs/aprs-symbols-24-2.png icons/light/16x16/actions/application-exit.png icons/light/16x16/actions/device-read.png icons/light/16x16/actions/device-search.png icons/light/16x16/actions/device-write-callsign.png icons/light/16x16/actions/device-write.png icons/light/16x16/actions/document-download.png icons/light/16x16/actions/document-new.png icons/light/16x16/actions/document-open.png icons/light/16x16/actions/document-save-as.png icons/light/16x16/actions/document-verify.png icons/light/16x16/actions/edit-move-10-down.png icons/light/16x16/actions/edit-move-10-up.png icons/light/16x16/actions/edit-move-bottom.png icons/light/16x16/actions/edit-move-down.png icons/light/16x16/actions/edit-move-top.png icons/light/16x16/actions/edit-move-up.png icons/light/16x16/actions/help-about.png icons/light/16x16/actions/help-contents.png icons/light/16x16/apps/qdmr.png icons/light/32x32/actions/application-exit.png icons/light/32x32/actions/application-settings.png icons/light/32x32/actions/device-read.png icons/light/32x32/actions/device-search.png icons/light/32x32/actions/device-write-callsign.png icons/light/32x32/actions/device-write.png icons/light/32x32/actions/document-download.png icons/light/32x32/actions/document-new.png icons/light/32x32/actions/document-open.png icons/light/32x32/actions/document-save-as.png icons/light/32x32/actions/document-verify.png icons/light/32x32/actions/edit-move-10-down.png icons/light/32x32/actions/edit-move-10-up.png icons/light/32x32/actions/edit-move-bottom.png icons/light/32x32/actions/edit-move-down.png icons/light/32x32/actions/edit-move-top.png icons/light/32x32/actions/edit-move-up.png icons/light/32x32/actions/help-about.png icons/light/32x32/actions/help-contents.png icons/light/32x32/apps/qdmr.png icons/light/index.theme icons/light/scalable/actions/application-exit.svg icons/light/scalable/actions/application-settings.svg icons/light/scalable/actions/device-read.svg icons/light/scalable/actions/device-search.svg icons/light/scalable/actions/device-write.svg icons/light/scalable/actions/document-download.svg icons/light/scalable/actions/document-new.svg icons/light/scalable/actions/document-open.svg icons/light/scalable/actions/document-save-as.svg icons/light/scalable/actions/document-verify.svg icons/light/scalable/actions/edit-move-10-down.svg icons/light/scalable/actions/edit-move-10-up.svg icons/light/scalable/actions/edit-move-bottom.svg icons/light/scalable/actions/edit-move-down.svg icons/light/scalable/actions/edit-move-top.svg icons/light/scalable/actions/edit-move-up.svg icons/light/scalable/actions/help-about.svg icons/light/scalable/actions/help-contents.svg icons/light/scalable/apps/qdmr.svg icons/light/scalable/actions/device-write-satellites.svg icons/light/32x32/actions/device-write-satellites.png icons/light/16x16/actions/device-write-satellites.png icons/dark/16x16/actions/application-exit.png icons/dark/16x16/actions/device-read.png icons/dark/16x16/actions/device-search.png icons/dark/16x16/actions/device-write-callsign.png icons/dark/16x16/actions/device-write.png icons/dark/16x16/actions/document-download.png icons/dark/16x16/actions/document-new.png icons/dark/16x16/actions/document-open.png icons/dark/16x16/actions/document-save-as.png icons/dark/16x16/actions/document-verify.png icons/dark/16x16/actions/edit-move-10-down.png icons/dark/16x16/actions/edit-move-10-up.png icons/dark/16x16/actions/edit-move-bottom.png icons/dark/16x16/actions/edit-move-down.png icons/dark/16x16/actions/edit-move-top.png icons/dark/16x16/actions/edit-move-up.png icons/dark/16x16/actions/help-about.png icons/dark/16x16/actions/help-contents.png icons/dark/16x16/apps/qdmr.png icons/dark/32x32/actions/application-exit.png icons/dark/32x32/actions/application-settings.png icons/dark/32x32/actions/device-read.png icons/dark/32x32/actions/device-search.png icons/dark/32x32/actions/device-write-callsign.png icons/dark/32x32/actions/device-write.png icons/dark/32x32/actions/document-new.png icons/dark/32x32/actions/document-open.png icons/dark/32x32/actions/document-save-as.png icons/dark/32x32/actions/document-verify.png icons/dark/32x32/actions/edit-move-10-down.png icons/dark/32x32/actions/edit-move-10-up.png icons/dark/32x32/actions/edit-move-bottom.png icons/dark/32x32/actions/edit-move-down.png icons/dark/32x32/actions/edit-move-top.png icons/dark/32x32/actions/edit-move-up.png icons/dark/32x32/actions/help-about.png icons/dark/32x32/actions/help-contents.png icons/dark/32x32/apps/qdmr.png icons/dark/index.theme icons/dark/32x32/actions/device-write-satellites.png icons/dark/16x16/actions/device-write-satellites.png qdmr-0.12.3/shared/ui/000077500000000000000000000000001501654372000144265ustar00rootroot00000000000000qdmr-0.12.3/shared/ui/aboutdialog.ui000066400000000000000000000167201501654372000172650ustar00rootroot00000000000000 AboutDialog 0 0 540 371 About qdmr :/icons/about.png:/icons/about.png 0 About qdmr 0 0 0 0 About qdmr false <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><title>About qdmr</title><style type="text/css"> p, li { white-space: pre-wrap; } hr { height: 1px; border-width: 0; } li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } </style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> <p align="center" style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'.SF NS Text'; font-size:13pt; font-weight:600;">qdmr</span></p> <p align="center" style=" margin-top:14px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'.SF NS Text'; font-size:13pt; font-weight:600;">Version %1</span></p> <p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'.SF NS Text'; font-size:13pt; font-weight:600;">Hannes Matuschek, DM3MAT<br /> dm3mat@darc.de</span></p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'.SF NS Text'; font-size:13pt; font-weight:600;">qdmr</span><span style=" font-family:'.SF NS Text'; font-size:13pt;"> – A platform independent configuration and programming tool for codeplugs of cheap DMR radios.</span></p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'.SF NS Text'; font-size:13pt;">This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'.SF NS Text'; font-size:13pt;">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.</span></p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'.SF NS Text'; font-size:13pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="https://www.gnu.org/licenses/"><span style=" font-family:'.SF NS Text'; font-size:13pt; text-decoration: underline; color:#0000ff;">https://www.gnu.org/licenses/</span></a><span style=" font-family:'.SF NS Text'; font-size:13pt;">.</span></p></body></html> Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse Supported Radios 0 0 0 0 11 false 1 Qt::Horizontal QDialogButtonBox::Close true buttonBox accepted() AboutDialog accept() 248 254 157 274 buttonBox rejected() AboutDialog reject() 316 260 286 274 qdmr-0.12.3/src/000077500000000000000000000000001501654372000133325ustar00rootroot00000000000000qdmr-0.12.3/src/CMakeLists.txt000066400000000000000000000117171501654372000161010ustar00rootroot00000000000000SET(qdmr_SOURCES main.cc configitemwrapper.cc application.cc settings.cc dmrcontactdialog.cc dtmfcontactdialog.cc rxgrouplistdialog.cc analogchanneldialog.cc digitalchanneldialog.cc channelvalidator.cc channelcombobox.cc channelselectiondialog.cc zonedialog.cc scanlistdialog.cc verifydialog.cc gpssystemdialog.cc contactselectiondialog.cc searchpopup.cc aprssystemdialog.cc releasenotes.cc roamingzonedialog.cc roamingchannellistview.cc roamingchanneldialog.cc roamingchannelselectiondialog.cc configobjectlistview.cc configobjecttableview.cc generalsettingsview.cc radioidlistview.cc contactlistview.cc grouplistsview.cc channellistview.cc zonelistview.cc scanlistsview.cc positioningsystemlistview.cc roamingzonelistview.cc collapsablewidget.cc extensionview.cc extensionwrapper.cc propertydelegate.cc errormessageview.cc deviceselectiondialog.cc radioselectiondialog.cc dmriddialog.cc configobjecttypeselectiondialog.cc configmergedialog.cc satellitedatabasedialog.cc satelliteselectiondialog.cc repeaterdatabase.cc repeatercompleter.cc repeaterbooksource.cc repeatermapsource.cc hearhamrepeatersource.cc radioidrepeatersource.cc selectivecallbox.cc transponderfrequencydelegate.cc mainwindow.cc) SET(qdmr_MOC_HEADERS configitemwrapper.hh application.hh settings.hh dmrcontactdialog.hh dtmfcontactdialog.hh rxgrouplistdialog.hh analogchanneldialog.hh digitalchanneldialog.hh channelvalidator.hh channelcombobox.hh channelselectiondialog.hh zonedialog.hh scanlistdialog.hh verifydialog.hh gpssystemdialog.hh contactselectiondialog.hh aprssystemdialog.hh releasenotes.hh searchpopup.hh roamingzonedialog.hh roamingchannellistview.hh roamingchanneldialog.hh roamingchannelselectiondialog.hh configobjectlistview.hh configobjecttableview.hh generalsettingsview.hh radioidlistview.hh contactlistview.hh grouplistsview.hh channellistview.hh zonelistview.hh scanlistsview.hh positioningsystemlistview.hh roamingzonelistview.hh collapsablewidget.hh extensionview.hh extensionwrapper.hh propertydelegate.hh errormessageview.hh deviceselectiondialog.hh radioselectiondialog.hh dmriddialog.hh configobjecttypeselectiondialog.hh configmergedialog.hh satellitedatabasedialog.hh satelliteselectiondialog.hh repeaterdatabase.hh repeatercompleter.hh repeaterbooksource.hh repeatermapsource.hh hearhamrepeatersource.hh radioidrepeatersource.hh selectivecallbox.hh transponderfrequencydelegate.hh mainwindow.hh) SET(qdmr_HEADERS ) SET(qdmr_UI_FORMS dmrcontactdialog.ui dtmfcontactdialog.ui rxgrouplistdialog.ui analogchanneldialog.ui zonedialog.ui digitalchanneldialog.ui scanlistdialog.ui verifydialog.ui settingsdialog.ui gpssystemdialog.ui aprssystemdialog.ui roamingzonedialog.ui roamingchannellistview.ui roamingchanneldialog.ui configobjectlistview.ui configobjecttableview.ui generalsettingsview.ui radioidlistview.ui contactlistview.ui grouplistsview.ui channellistview.ui zonelistview.ui scanlistsview.ui positioningsystemlistview.ui roamingzonelistview.ui errormessageview.ui extensionview.ui deviceselectiondialog.ui radioselectiondialog.ui dmriddialog.ui configobjecttypeselectiondialog.ui configmergedialog.ui satellitedatabasedialog.ui satelliteselectiondialog.ui mainwindow.ui) set(qdmr_TS_FILES ../i18n/de.ts ../i18n/en_US.ts ../i18n/sv.ts ../i18n/it.ts ../i18n/nl.ts ../i18n/pl.ts ../i18n/fr.ts) qt5_wrap_cpp(qdmr_MOC_SOURCES ${qdmr_MOC_HEADERS}) qt5_wrap_ui(qdmr_UI_SOURCES ${qdmr_UI_FORMS}) qt5_add_translation(qdmr_QM_FILES ${qdmr_TS_FILES}) message(STATUS "Translations at: ${qdmr_QM_FILES}") qt5_add_resources(qdmr_RCC_SOURCES ../shared/resources.qrc) add_executable(qdmr WIN32 ${qdmr_SOURCES} ${qdmr_MOC_SOURCES} ${qdmr_RCC_SOURCES} ${qdmr_UI_SOURCES} ${qdmr_QM_FILES}) if (UNIX AND APPLE AND INSTALL_BUNDLE) set_target_properties(qdmr PROPERTIES MACOSX_BUNDLE ON MACOSX_BUNDLE_BUNDLE_NAME ${CMAKE_PROJECT_NAME} MACOSX_BUNDLE_BUNDLE_VERSION ${CMAKE_PROJECT_VERSION} MACOSX_BUNDLE_GUI_IDENTIFIER "de.darc.dm3mat.qdmr" MACOSX_BUNDLE_ICON_FILE "Resources/qdmr.icns" MACOSX_BUNDLE_LONG_VERSION_STRING ${CMAKE_PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}" RESOURCE "${CMAKE_SOURCE_DIR}/dist/macosx/qdmr.icns" ${qdmr_QM_FILES}) endif(UNIX AND APPLE AND INSTALL_BUNDLE) target_link_libraries(qdmr ${LIBS} libdmrconf) target_include_directories(qdmr PUBLIC "${CMAKE_CURRENT_BINARY_DIR}") # Install binary only if not a bundle under MacOS X if (UNIX AND APPLE AND INSTALL_BUNDLE) install(TARGETS qdmr BUNDLE DESTINATION ${BUNDLE_PATH}/ RESOURCE DESTINATION "${BUNDLE_PATH}/${CMAKE_PROJECT_NAME}.app/Contents/Resources") else() install(TARGETS qdmr DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) foreach(QM_FILE ${qdmr_QM_FILES}) get_filename_component(QM_LANGUAGE ${QM_FILE} NAME_WE) install(FILES ${QM_FILE} DESTINATION "${LOCALE_DIRECTORY}/${QM_LANGUAGE}/LC_MESSAGES/" RENAME "qdmr_qt.qm") endforeach() endif(UNIX AND APPLE AND INSTALL_BUNDLE) qdmr-0.12.3/src/aboutdialog.ui000066400000000000000000000030171501654372000161640ustar00rootroot00000000000000 AboutDialog 0 0 400 300 About qdmr Qt::Horizontal QDialogButtonBox::Close true buttonBox accepted() AboutDialog accept() 248 254 157 274 buttonBox rejected() AboutDialog reject() 316 260 286 274 qdmr-0.12.3/src/analogchanneldialog.cc000066400000000000000000000207561501654372000176250ustar00rootroot00000000000000#include "analogchanneldialog.hh" #include "application.hh" #include #include "utils.hh" #include "settings.hh" #include "repeatercompleter.hh" #include "repeaterdatabase.hh" /* ********************************************************************************************* * * Implementation of AnalogChannelDialog * ********************************************************************************************* */ AnalogChannelDialog::AnalogChannelDialog(Config *config, QWidget *parent) : QDialog(parent), _config(config), _myChannel(new FMChannel(this)), _channel(nullptr) { construct(); } AnalogChannelDialog::AnalogChannelDialog(Config *config, FMChannel *channel, QWidget *parent) : QDialog(parent), _config(config), _myChannel(nullptr), _channel(channel) { if (nullptr == _channel) _myChannel = new FMChannel(); else _myChannel = _channel->clone()->as(); _myChannel->setParent(this); construct(); } void AnalogChannelDialog::construct() { setupUi(this); Settings settings; if (settings.hideChannelNote()) hintLabel->setVisible(false); Application *app = qobject_cast(qApp); FMRepeaterFilter *filter = new FMRepeaterFilter(app->repeater(), app->position(), this); filter->setSourceModel(app->repeater()); QCompleter *completer = new RepeaterCompleter(2, app->repeater(), this); completer->setModel(filter); channelName->setCompleter(completer); connect(completer, SIGNAL(activated(const QModelIndex &)), this, SLOT(onRepeaterSelected(const QModelIndex &))); rxFrequency->setValidator(new QDoubleValidator(0,500,5)); txFrequency->setValidator(new QDoubleValidator(0,500,5)); powerValue->setItemData(0, unsigned(Channel::Power::Max)); powerValue->setItemData(1, unsigned(Channel::Power::High)); powerValue->setItemData(2, unsigned(Channel::Power::Mid)); powerValue->setItemData(3, unsigned(Channel::Power::Low)); powerValue->setItemData(4, unsigned(Channel::Power::Min)); powerDefault->setChecked(true); powerValue->setEnabled(false); powerValue->setCurrentIndex(1); totDefault->setChecked(true); totValue->setValue(0); totValue->setEnabled(false); scanList->addItem(tr("[None]"), QVariant::fromValue((ScanList *)nullptr)); scanList->setCurrentIndex(0); for (int i=0; i<_config->scanlists()->count(); i++) { ScanList *lst = _config->scanlists()->scanlist(i); scanList->addItem(lst->name(),QVariant::fromValue(lst)); if (_myChannel && (_myChannel->scanList() == lst) ) scanList->setCurrentIndex(i+1); } txAdmit->setItemData(0, unsigned(FMChannel::Admit::Always)); txAdmit->setItemData(1, unsigned(FMChannel::Admit::Free)); txAdmit->setItemData(2, unsigned(FMChannel::Admit::Tone)); squelchDefault->setChecked(true); squelchValue->setValue(1); squelchValue->setEnabled(false); if (_myChannel) { rxTone->setSelectiveCall(_myChannel->rxTone()); txTone->setSelectiveCall(_myChannel->txTone()); } bandwidth->setItemData(0, unsigned(FMChannel::Bandwidth::Narrow)); bandwidth->setItemData(1, unsigned(FMChannel::Bandwidth::Wide)); aprsList->addItem(tr("[None]"), QVariant::fromValue((APRSSystem *)nullptr)); aprsList->setCurrentIndex(0); for (int i=0; i<_config->posSystems()->aprsCount(); i++) { APRSSystem *sys = _config->posSystems()->aprsSystem(i); aprsList->addItem(sys->name(),QVariant::fromValue(sys)); if (_myChannel && (_myChannel->aprsSystem() == sys)) aprsList->setCurrentIndex(i+1); } voxDefault->setChecked(true); voxValue->setValue(0); voxValue->setEnabled(false); channelName->setText(_myChannel->name()); rxFrequency->setText(_myChannel->rxFrequency().format(Frequency::Format::MHz)); txFrequency->setText(_myChannel->txFrequency().format(Frequency::Format::MHz)); if (! _myChannel->defaultPower()) { powerDefault->setChecked(false); powerValue->setEnabled(true); switch (_myChannel->power()) { case Channel::Power::Max: powerValue->setCurrentIndex(0); break; case Channel::Power::High: powerValue->setCurrentIndex(1); break; case Channel::Power::Mid: powerValue->setCurrentIndex(2); break; case Channel::Power::Low: powerValue->setCurrentIndex(3); break; case Channel::Power::Min: powerValue->setCurrentIndex(4); break; } } if (! _myChannel->defaultTimeout()) { totDefault->setChecked(false); totValue->setEnabled(true); totValue->setValue(_myChannel->timeout()); } rxOnly->setChecked(_myChannel->rxOnly()); switch (_myChannel->admit()) { case FMChannel::Admit::Always: txAdmit->setCurrentIndex(0); break; case FMChannel::Admit::Free: txAdmit->setCurrentIndex(1); break; case FMChannel::Admit::Tone: txAdmit->setCurrentIndex(2); break; } if (! _myChannel->defaultSquelch()) { squelchDefault->setChecked(false); squelchValue->setEnabled(true); squelchValue->setValue(_myChannel->squelch()); } if (FMChannel::Bandwidth::Narrow == _myChannel->bandwidth()) bandwidth->setCurrentIndex(0); else if (FMChannel::Bandwidth::Wide == _myChannel->bandwidth()) bandwidth->setCurrentIndex(1); if (! _myChannel->defaultVOX()) { voxDefault->setChecked(false); voxValue->setEnabled(true); voxValue->setValue(_myChannel->vox()); } if (! settings.showExtensions()) tabWidget->tabBar()->hide(); extensionView->setObjectName("AnalogChannelExtension"); extensionView->setObject(_myChannel, _config); connect(powerDefault, SIGNAL(toggled(bool)), this, SLOT(onPowerDefaultToggled(bool))); connect(totDefault, SIGNAL(toggled(bool)), this, SLOT(onTimeoutDefaultToggled(bool))); connect(squelchDefault, SIGNAL(toggled(bool)), this, SLOT(onSquelchDefaultToggled(bool))); connect(voxDefault, SIGNAL(toggled(bool)), this, SLOT(onVOXDefaultToggled(bool))); connect(hintLabel, SIGNAL(linkActivated(QString)), this, SLOT(onHideChannelHint())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); } FMChannel * AnalogChannelDialog::channel() { _myChannel->setName(channelName->text()); _myChannel->setRXFrequency(Frequency::fromString(rxFrequency->text())); _myChannel->setTXFrequency(Frequency::fromString(txFrequency->text())); if (powerDefault->isChecked()) { _myChannel->setDefaultPower(); } else { _myChannel->setPower(Channel::Power(powerValue->currentData().toUInt())); } if (totDefault->isChecked()) _myChannel->setDefaultTimeout(); else _myChannel->setTimeout(totValue->value()); _myChannel->setRXOnly(rxOnly->isChecked()); _myChannel->setAdmit(FMChannel::Admit(txAdmit->currentData().toUInt())); if (squelchDefault->isChecked()) _myChannel->setSquelchDefault(); else _myChannel->setSquelch(squelchValue->value()); _myChannel->setRXTone(rxTone->selectiveCall()); _myChannel->setTXTone(txTone->selectiveCall()); _myChannel->setBandwidth(FMChannel::Bandwidth(bandwidth->currentData().toUInt())); _myChannel->setScanList(scanList->currentData().value()); _myChannel->setAPRSSystem(aprsList->currentData().value()); if (voxDefault->isChecked()) _myChannel->setVOXDefault(); else _myChannel->setVOX(voxValue->value()); FMChannel *channel = _myChannel; if (nullptr == _channel) { _myChannel->setParent(nullptr); _myChannel = nullptr; } else { _channel->copy(*_myChannel); channel = _channel; } return channel; } void AnalogChannelDialog::onRepeaterSelected(const QModelIndex &index) { Application *app = qobject_cast(qApp); QModelIndex src = qobject_cast( channelName->completer()->completionModel())->mapToSource(index); src = qobject_cast( channelName->completer()->model())->mapToSource(src); Frequency rx = app->repeater()->get(src.row()).rxFrequency(); Frequency tx = app->repeater()->get(src.row()).txFrequency(); rxTone->setSelectiveCall(app->repeater()->get(src.row()).rxTone()); txTone->setSelectiveCall(app->repeater()->get(src.row()).txTone()); txFrequency->setText(tx.format()); rxFrequency->setText(rx.format()); } void AnalogChannelDialog::onPowerDefaultToggled(bool checked) { powerValue->setEnabled(!checked); } void AnalogChannelDialog::onTimeoutDefaultToggled(bool checked) { totValue->setEnabled(!checked); } void AnalogChannelDialog::onSquelchDefaultToggled(bool checked) { squelchValue->setEnabled(! checked); } void AnalogChannelDialog::onVOXDefaultToggled(bool checked) { voxValue->setEnabled(! checked); } void AnalogChannelDialog::onHideChannelHint() { Settings settings; settings.setHideChannelNote(true); hintLabel->setVisible(false); } qdmr-0.12.3/src/analogchanneldialog.hh000066400000000000000000000015051501654372000176260ustar00rootroot00000000000000#ifndef ANALOGCHANNELDIALOG_HH #define ANALOGCHANNELDIALOG_HH #include #include "config.hh" #include "ui_analogchanneldialog.h" class AnalogChannelDialog: public QDialog, private Ui::AnalogChannelDialog { Q_OBJECT public: AnalogChannelDialog(Config *config, QWidget *parent=nullptr); AnalogChannelDialog(Config *config, FMChannel *channel, QWidget *parent=nullptr); FMChannel *channel(); protected: void construct(); protected slots: void onRepeaterSelected(const QModelIndex &index); void onPowerDefaultToggled(bool checked); void onTimeoutDefaultToggled(bool checked); void onSquelchDefaultToggled(bool checked); void onVOXDefaultToggled(bool checked); void onHideChannelHint(); protected: Config *_config; FMChannel *_myChannel; FMChannel *_channel; }; #endif // ANALOGCHANNELDIALOG_HH qdmr-0.12.3/src/analogchanneldialog.ui000066400000000000000000000361421501654372000176510ustar00rootroot00000000000000 AnalogChannelDialog 0 0 657 540 0 0 Edit Analog Channel padding:10px;border: 2px solid black; border-radius: 10px; <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and CTCSS tones are filled in (if applicable).</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Qt::RichText true 0 Basic Name 200 0 Rx Frequency Tx Frequency Power 0 0 Max High Mid Low Min Default Tx Timeout 0 0 Off s 9999 Default VOX Level 0 0 Off 10 Default Rx Only Scan List 0 0 QLayout::SetDefaultConstraint QFormLayout::AllNonFixedFieldsGrow Tx Admit 0 0 Always Channel Free Tone Squelch Rx Tone Tx Tone Bandwidth 0 0 Narrow (12.5 kHz) Wide (25 kHz) APRS 0 0 0 0 Open 10 Default 0 0 0 0 Extensions Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ExtensionView QWidget
      extensionview.hh
      1
      SelectiveCallBox QWidget
      selectivecallbox.hh
      1
      buttonBox accepted() AnalogChannelDialog accept() 248 254 157 274 buttonBox rejected() AnalogChannelDialog reject() 316 260 286 274
      qdmr-0.12.3/src/application.cc000066400000000000000000000675601501654372000161620ustar00rootroot00000000000000#include "application.hh" #include #include #include #include #include #include "logger.hh" #include "radio.hh" #include "codeplug.hh" #include "config.h" #include "settings.hh" #include "radiolimits.hh" #include "verifydialog.hh" #include "rxgrouplistdialog.hh" #include "zonedialog.hh" #include "scanlistdialog.hh" #include "roamingzonedialog.hh" #include "repeaterdatabase.hh" #include "repeaterbooksource.hh" #include "repeatermapsource.hh" #include "hearhamrepeatersource.hh" #include "radioidrepeatersource.hh" #include "userdatabase.hh" #include "talkgroupdatabase.hh" #include "satellitedatabase.hh" #include "generalsettingsview.hh" #include "radioidlistview.hh" #include "contactlistview.hh" #include "grouplistsview.hh" #include "channellistview.hh" #include "zonelistview.hh" #include "scanlistsview.hh" #include "positioningsystemlistview.hh" #include "roamingchannellistview.hh" #include "roamingzonelistview.hh" #include "errormessageview.hh" #include "extensionview.hh" #include "deviceselectiondialog.hh" #include "radioselectiondialog.hh" #include "chirpformat.hh" #include "configmergedialog.hh" #include "configmergevisitor.hh" #include "satellitedatabasedialog.hh" #include "mainwindow.hh" inline QStringList getLanguages() { QStringList languages = {QLocale::system().name()}; if (languages.last().contains("_")) { languages.append(languages.last().split("_").first()); } return languages; } inline QString getLocalePath(const QString &language) { return QDir(LOCALE_DIRECTORY "/" + language + "/LC_MESSAGES/").absolutePath(); } Application::Application(int &argc, char *argv[]) : QApplication(argc, argv), _config(nullptr), _mainWindow(nullptr), _translator(nullptr), _repeater(nullptr), _satellites(nullptr), _lastDevice() { setApplicationName("qdmr"); setOrganizationName("DM3MAT"); setOrganizationDomain("hmatuschek.github.io"); // open logfile QString logdir = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); Logger::get().addHandler(new FileLogHandler(logdir+"/qdmr.log")); // register icon themes QStringList iconPaths = QIcon::themeSearchPaths(); iconPaths.prepend(":/icons"); QIcon::setThemeSearchPaths(iconPaths); onPaletteChanged(palette()); // handle translations _translator = new QTranslator(this); foreach (QString language, getLanguages()) { logDebug() << "Search for translation in '" << getLocalePath(language) << "'."; if (_translator->load("qdmr", getLocalePath(language), "", "_qt.qm")) { this->installTranslator(_translator); logDebug() << "Installed translator for locale '" << QLocale::system().name() << "'."; break; } } // load settings Settings settings; // load databases _repeater = new RepeaterDatabase(this); if (settings.repeaterBookSourceEnabled()) _repeater->addSource(new RepeaterBookSource()); if (settings.repeaterMapSourceEnabled()) _repeater->addSource(new RepeaterMapSource()); if (settings.hearhamSourceEnabled()) _repeater->addSource(new HearhamRepeaterSource()); if (settings.radioIdRepeaterSourceEnabled()) _repeater->addSource(new RadioidRepeaterSource()); _users = new UserDatabase(30, this); _talkgroups = new TalkGroupDatabase(30, this); _satellites = new SatelliteDatabase(7, this); // create empty codeplug _config = new Config(this); // Handle args (if there are some) if (argc>1) { QFileInfo info(argv[1]); QFile file(argv[1]); if (! file.open(QIODevice::ReadOnly)) { logError() << "Cannot open codeplug file '" << argv[1] << "': " << file.errorString(); return; } if (("conf" == info.suffix()) || ("csv" == info.suffix())) { QString errorMessage; QTextStream stream(&file); if (! _config->readCSV(stream, errorMessage)) { logError() << errorMessage; return; } } else if ("yaml" == info.suffix()) { ErrorStack err; if (! _config->readYAML(argv[1], err)) { logError() << "Cannot read yaml codeplug file '" << argv[1] << "': " << err.format(); return; } } } // load position _currentPosition = settings.position(); _source = QGeoPositionInfoSource::createDefaultSource(this); if (_source) { connect(_source, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(positionUpdated(QGeoPositionInfo))); if (settings.queryPosition()) { _source->startUpdates(); _currentPosition = _source->lastKnownPosition().coordinate(); } } // Check if updated _releaseNotes.checkForUpdate(); logDebug() << "Last known position: " << _currentPosition.toString(); connect(_config, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModifed())); } Application::~Application() { if (_mainWindow) delete _mainWindow; _mainWindow = nullptr; } bool Application::isDarkMode() const { return isDarkMode(palette()); } bool Application::isDarkMode(const QPalette &palette) const { int text_hsv_value = palette.color(QPalette::WindowText).value(), bg_hsv_value = palette.color(QPalette::Background).value(); return text_hsv_value > bg_hsv_value; } bool Application::isModified() const { return _config->isModified(); } MainWindow * Application::mainWindow() { if (_mainWindow) return _mainWindow; return (_mainWindow = new MainWindow(_config)); } UserDatabase * Application::user() const { return _users; } TalkGroupDatabase * Application::talkgroup() const { return _talkgroups; } RepeaterDatabase * Application::repeater() const{ return _repeater; } SatelliteDatabase * Application::satellite() const { return _satellites; } void Application::newCodeplug() { if (_config->isModified()) { if (QMessageBox::Ok != QMessageBox::question(0, tr("Unsaved changes to codeplug."), tr("There are unsaved changes to the current codeplug. " "These changes are lost if you proceed."), QMessageBox::Cancel|QMessageBox::Ok)) return; } _config->clear(); _config->setModified(false); } void Application::loadCodeplug() { if (! _mainWindow) return; if (_config->isModified()) { if (QMessageBox::Ok != QMessageBox::question(nullptr, tr("Unsaved changes to codeplug."), tr("There are unsaved changes to the current codeplug. " "These changes are lost if you proceed."), QMessageBox::Cancel|QMessageBox::Ok)) return; } Settings settings; QString filename = QFileDialog::getOpenFileName( nullptr, tr("Open codeplug"), settings.lastDirectory().absolutePath(), tr("Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*)")); if (filename.isEmpty()) return; QFile file(filename); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::critical( nullptr, tr("Cannot open file"), tr("Cannot read codeplug from file '%1': %2").arg(filename).arg(file.errorString())); return; } logDebug() << "Load codeplug from '" << filename << "'."; QFileInfo info(filename); settings.setLastDirectoryDir(info.absoluteDir()); if ("yaml" == info.suffix()){ ErrorStack err; if (_config->readYAML(filename, err)) { _mainWindow->setWindowModified(false); } else { QMessageBox::critical(nullptr, tr("Cannot read codeplug."), tr("Cannot read codeplug from file '%1': %2") .arg(filename).arg(err.format())); _config->clear(); } } else { QString errorMessage; QTextStream stream(&file); if (_config->readCSV(stream, errorMessage)) { _mainWindow->setWindowModified(false); } else { QMessageBox::critical(nullptr, tr("Cannot read codeplug."), tr("Cannot read codeplug from file '%1': %2") .arg(filename).arg(errorMessage)); _config->clear(); } } processEvents(); _config->setModified(false); } void Application::saveCodeplug() { if (! _mainWindow) return; Settings settings; QString filename = QFileDialog::getSaveFileName( nullptr, tr("Save codeplug"), settings.lastDirectory().absolutePath(), tr("Codeplug Files (*.yaml *.yml)")); if (filename.isEmpty()) return; if (filename.endsWith(".conf") || filename.endsWith("csv")){ QMessageBox::critical(nullptr, tr("Please use new YAML format."), tr("Saving in the old table-based conf format was disabled with 0.9.0. " "Reading these files still works.")); return; } // append .yaml if missing if ((!filename.endsWith(".yaml")) && (!filename.endsWith(".yml"))) filename.append(".yaml"); QFile file(filename); if (! file.open(QIODevice::WriteOnly)) { QMessageBox::critical(nullptr, tr("Cannot open file"), tr("Cannot save codeplug to file '%1': %2").arg(filename).arg(file.errorString())); return; } QTextStream stream(&file); QFileInfo info(filename); if (_config->toYAML(stream)) { _mainWindow->setWindowModified(false); } else { QMessageBox::critical(nullptr, tr("Cannot save codeplug"), tr("Cannot save codeplug to file '%1'.").arg(filename)); } file.flush(); file.close(); settings.setLastDirectoryDir(info.absoluteDir()); } void Application::exportCodeplugToChirp() { if (! _mainWindow) return; Settings settings; QString filename = QFileDialog::getSaveFileName( nullptr, tr("Export codeplug"), settings.lastDirectory().absolutePath(), tr("CHIRP CSV Files (*.csv)")); if (filename.isEmpty()) return; QFile file(filename); if (! file.open(QIODevice::WriteOnly)) { QMessageBox::critical(nullptr, tr("Cannot open file"), tr("Cannot save codeplug to file '%1': %2").arg(filename).arg(file.errorString())); return; } QTextStream stream(&file); QFileInfo info(filename); ErrorStack err; if (! ChirpWriter::write(stream, _config, err)) QMessageBox::critical(nullptr, tr("Cannot export codeplug"), tr("Cannot export codeplug to file '%1':\n%2").arg(filename).arg(err.format())); file.flush(); file.close(); settings.setLastDirectoryDir(info.absoluteDir()); } void Application::importCodeplug() { if (! _mainWindow) return; Settings settings; QString filename = QFileDialog::getOpenFileName( nullptr, tr("Import codeplug"), settings.lastDirectory().absolutePath(), tr("CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml)")); if (filename.isEmpty()) return; Config merging; ErrorStack err; if (filename.endsWith(".csv")) { // import CHIRP CSV file QFile file(filename); if (! file.open(QIODevice::ReadOnly)) { QMessageBox::critical(nullptr, tr("Cannot open file"), tr("Cannot read codeplug from file '%1': %2").arg(filename).arg(file.errorString())); return; } QTextStream stream(&file); if (! ChirpReader::read(stream, &merging, err)) { QMessageBox::critical(nullptr, tr("Cannot import codeplug"), tr("Cannot import codeplug from '%1': %2") .arg(filename).arg(err.format())); return; } } else if (filename.endsWith(".yaml") || filename.endsWith(".yml")) { // import QDMR YAML codeplug if (! merging.readYAML(filename, err)) { QMessageBox::critical(nullptr, tr("Cannot import codeplug"), tr("Cannot import codeplug from '%1': %2") .arg(filename).arg(err.format())); return; } } else { QMessageBox::critical(nullptr, tr("Cannot import codeplug"), tr("Do not know, how to handle file '%1'.") .arg(filename)); return; } ConfigMergeDialog mergeDialog; if (QDialog::Accepted != mergeDialog.exec()) return; logDebug() << "Merging codeplugs ..."; if (! ConfigMerge::mergeInto(_config, &merging, mergeDialog.itemStrategy(), mergeDialog.setStrategy(), err)) { QMessageBox::critical(nullptr, tr("Cannot import codeplug"), tr("Cannot import codeplug from '%1': %2") .arg(filename).arg(err.format())); _config->setModified(true); return; } _config->setModified(true); } Radio * Application::autoDetect(const ErrorStack &err) { Settings settings; // If the last detected device is still valid // -> skip interface detection and selection if ((! _lastDevice.isValid()) || settings.disableAutoDetect()) { logDebug() << "Last device is invalid, search for new one."; // First get all devices that are known by VID/PID QList interfaces = USBDeviceDescriptor::detect(); if (interfaces.isEmpty()) { logDebug() << "No save device found, continue searching for unsave ones."; interfaces = USBDeviceDescriptor::detect(false); } if (interfaces.isEmpty()) { errMsg(err) << tr("No matching devices found."); return nullptr; } else if ((1 != interfaces.count()) || (! interfaces.first().isSave()) || settings.disableAutoDetect()) { // More than one device found, or device not save -> select by user DeviceSelectionDialog dialog(interfaces); if (QDialog::Accepted != dialog.exec()) { return nullptr; } _lastDevice = dialog.device(); } else { _lastDevice = interfaces.first(); } } // Check if device supports identification RadioInfo radioInfo; if ((! _lastDevice.isSave()) || settings.disableAutoDetect()){ RadioSelectionDialog dialog(_lastDevice); if (QDialog::Accepted != dialog.exec()) { return nullptr; } radioInfo = dialog.radioInfo(); } Radio *radio = Radio::detect(_lastDevice, radioInfo, err); if (nullptr == radio) { QMessageBox::critical(nullptr, tr("Cannot connect to radio"), tr("Cannot connect to radio: %1").arg(err.format())); return nullptr; } return radio; } void Application::detectRadio() { if (Radio *radio = autoDetect()) { QMessageBox::information(nullptr, tr("Radio found"), tr("Found device '%1'.").arg(radio->name())); delete radio; } else { QMessageBox::information(nullptr, tr("No radio found"), tr("No matching device was found.")); } } bool Application::verifyCodeplug(Radio *radio, bool showSuccess) { Radio *myRadio = radio; // If no radio is given -> try to detect the radio if (nullptr == myRadio) myRadio = autoDetect(); if (nullptr == myRadio) { if (showSuccess) { QMessageBox::warning(nullptr, tr("No radio found"), tr("No matching device was found.")); } return false; } ErrorStack err; Config *intermediate = myRadio->codeplug().preprocess(_config, err); if (nullptr == intermediate) { ErrorMessageView(err).exec(); return false; } Settings settings; RadioLimitContext ctx(settings.ignoreFrequencyLimits()); myRadio->limits().verifyConfig(intermediate, ctx); bool verified = true; if ( (settings.ignoreVerificationWarning() && (ctx.maxSeverity()>RadioLimitIssue::Warning)) || ((!settings.ignoreVerificationWarning()) && (ctx.maxSeverity()>=RadioLimitIssue::Warning)) ) { VerifyDialog dialog(ctx, (nullptr != radio)); if (QDialog::Accepted != dialog.exec()) verified = false; } else if (showSuccess) { QMessageBox::information( nullptr, tr("Verification success"), tr("The codeplug was successfully verified with the radio '%1'").arg(myRadio->name())); } // Delete intermediate representation delete intermediate; // If no radio was given -> close connection to radio again if (nullptr == radio) myRadio->deleteLater(); return verified; } void Application::downloadCodeplug() { if (! _mainWindow) return; if (_config->isModified()) { if (QMessageBox::Ok != QMessageBox::question(nullptr, tr("Unsaved changes to codeplug."), tr("There are unsaved changes to the current codeplug. " "These changes are lost if you proceed."), QMessageBox::Cancel|QMessageBox::Ok)) return; } Radio *radio = autoDetect(); if (nullptr == radio) { QMessageBox::warning(nullptr, tr("No radio found"), tr("No matching device was found.")); return; } QProgressBar *progress = _mainWindow->findChild("progress"); progress->setValue(0); progress->setMaximum(100); progress->setVisible(true); connect(radio, SIGNAL(downloadProgress(int)), progress, SLOT(setValue(int))); connect(radio, SIGNAL(downloadError(Radio *)), this, SLOT(onCodeplugDownloadError(Radio *))); connect(radio, SIGNAL(downloadFinished(Radio *, Codeplug *)), this, SLOT(onCodeplugDownloaded(Radio *, Codeplug *))); ErrorStack err; if (radio->startDownload(false, err)) { _mainWindow->statusBar()->showMessage(tr("Read ...")); _mainWindow->setEnabled(false); } else { ErrorMessageView(err).exec(); progress->setVisible(false); } } void Application::onCodeplugDownloadError(Radio *radio) { _mainWindow->statusBar()->showMessage(tr("Read error")); ErrorMessageView(radio->errorStack()).exec(); _mainWindow->findChild("progress")->setVisible(false); _mainWindow->setEnabled(true); if (radio->wait(250)) radio->deleteLater(); _mainWindow->setWindowModified(false); } void Application::onCodeplugDownloaded(Radio *radio, Codeplug *codeplug) { _config->clear(); _mainWindow->setWindowModified(false); ErrorStack err; if (codeplug->decode(_config, err)) { _mainWindow->statusBar()->showMessage(tr("Read complete")); _mainWindow->findChild("progress")->setVisible(false); _config->setModified(false); } else { ErrorMessageView(err).exec(); _config->clear(); } if (! codeplug->postprocess(_config, err)) { ErrorMessageView(err).exec(); _config->clear(); } _mainWindow->setEnabled(true); if (radio->wait(250)) radio->deleteLater(); } void Application::uploadCodeplug() { // Start upload Settings settings; Radio *radio = autoDetect(); if (nullptr == radio) { QMessageBox::warning(nullptr, tr("No radio found"), tr("No matching device was found.")); return; } if (! verifyCodeplug(radio, false)) { radio->deleteLater(); return; } QProgressBar *progress = _mainWindow->findChild("progress"); progress->setValue(0); progress->setMaximum(100); progress->setVisible(true); connect(radio, SIGNAL(uploadProgress(int)), progress, SLOT(setValue(int))); connect(radio, SIGNAL(uploadError(Radio *)), this, SLOT(onCodeplugUploadError(Radio *))); connect(radio, SIGNAL(uploadComplete(Radio *)), this, SLOT(onCodeplugUploaded(Radio *))); ErrorStack err; Config *intermediate = radio->codeplug().preprocess(_config, err); if (nullptr == intermediate) { ErrorMessageView(err).exec(); progress->setVisible(false); return; } if (radio->startUpload(intermediate, false, settings.codePlugFlags(), err)) { _mainWindow->statusBar()->showMessage(tr("Upload ...")); _mainWindow->setEnabled(false); } else { ErrorMessageView(err).exec(); progress->setVisible(false); } } void Application::uploadCallsignDB() { // Start upload Radio *radio = autoDetect(); if (nullptr == radio) { QMessageBox::warning(nullptr, tr("No radio found"), tr("No matching device was found.")); return; } if (! radio->limits().hasCallSignDB()) { logDebug() << "Radio " << radio->name() << " does not support call-sign DB."; QMessageBox::information(nullptr, tr("Cannot write call-sign DB."), tr("The detected radio '%1' does not support " "a call-sign DB.") .arg(radio->name())); radio->deleteLater(); return; } if (! radio->limits().callSignDBImplemented()) { logDebug() << "Radio " << radio->name() << " does support call-sign DB but it is not implemented yet."; QMessageBox::critical(nullptr, tr("Cannot write call-sign DB."), tr("The detected radio '%1' does support a call-sign DB. " "This feature, however, is not implemented yet.").arg(radio->name())); radio->deleteLater(); return; } // Sort call-sign DB w.r.t. the current DMR ID in _config // this is part of the "auto-selection" of calls-signs for upload Settings settings; if (settings.selectUsingUserDMRID()) { if (nullptr == _config->settings()->defaultId()) { QMessageBox::critical(nullptr, tr("Cannot write call-sign DB."), tr("QDMR selects the call-signs to be written based on the default DMR " "ID of the radio. No default ID set.")); radio->deleteLater(); return; } // Sort w.r.t users DMR ID unsigned id = _config->settings()->defaultId()->number(); logDebug() << "Sort call-signs closest to ID=" << id << "."; _users->sortUsers(id); } else { // sort w.r.t. chosen prefixes QSet ids=settings.callSignDBPrefixes(); QStringList prefs; foreach (unsigned pref, ids) prefs.append(QString::number(pref)); logDebug() << "Sort call-signs closest to IDs={" << prefs.join(", ") << "}."; _users->sortUsers(ids); } // Assemble flags for callsign DB encoding CallsignDB::Selection css; if (settings.limitCallSignDBEntries()) { logDebug() << "Limit callsign DB entries to " << settings.maxCallSignDBEntries() << "."; css.setCountLimit(settings.maxCallSignDBEntries()); } QProgressBar *progress = _mainWindow->findChild("progress"); progress->setRange(0, 100); progress->setValue(0); progress->setVisible(true); connect(radio, SIGNAL(uploadProgress(int)), progress, SLOT(setValue(int))); connect(radio, SIGNAL(uploadError(Radio *)), this, SLOT(onCodeplugUploadError(Radio *))); connect(radio, SIGNAL(uploadComplete(Radio *)), this, SLOT(onCodeplugUploaded(Radio *))); ErrorStack err; if (radio->startUploadCallsignDB(_users, false, css, err)) { logDebug() << "Start call-sign DB write..."; _mainWindow->statusBar()->showMessage(tr("Write call-sign DB ...")); _mainWindow->setEnabled(false); } else { ErrorMessageView(err).exec(); progress->setVisible(false); } } void Application::uploadSatellites() { // Start upload satellites Radio *radio = autoDetect(); if (nullptr == radio) { QMessageBox::warning(nullptr, tr("No radio found"), tr("No matching device was found.")); return; } if (! radio->limits().hasSatelliteConfig()) { logDebug() << "Radio " << radio->name() << " does not support satellite tracking."; QMessageBox::information(nullptr, tr("Cannot write satellite config."), tr("The detected radio '%1' does not support satellite tracking.") .arg(radio->name())); radio->deleteLater(); return; } if (! radio->limits().satelliteConfigImplemented()) { logDebug() << "Radio " << radio->name() << " does support satellite tracking but it is not implemented yet."; QMessageBox::critical(nullptr, tr("Cannot write satellite config."), tr("The detected radio '%1' does support satellite tracking. " "This feature, however, is not implemented yet.").arg(radio->name())); radio->deleteLater(); return; } QProgressBar *progress = _mainWindow->findChild("progress"); progress->setRange(0, 100); progress->setValue(0); progress->setVisible(true); connect(radio, SIGNAL(uploadProgress(int)), progress, SLOT(setValue(int))); connect(radio, SIGNAL(uploadError(Radio *)), this, SLOT(onCodeplugUploadError(Radio *))); connect(radio, SIGNAL(uploadComplete(Radio *)), this, SLOT(onCodeplugUploaded(Radio *))); ErrorStack err; if (radio->startUploadSatelliteConfig(_satellites, false, err)) { logDebug() << "Start satellite config write..."; _mainWindow->statusBar()->showMessage(tr("Write satellite config ...")); _mainWindow->setEnabled(false); } else { ErrorMessageView(err).exec(); progress->setVisible(false); } } void Application::onCodeplugUploadError(Radio *radio) { _mainWindow->statusBar()->showMessage(tr("Write error")); ErrorMessageView(radio->errorStack()).exec(); _mainWindow->findChild("progress")->setVisible(false); _mainWindow->setEnabled(true); if (radio->wait(250)) radio->deleteLater(); } void Application::onCodeplugUploaded(Radio *radio) { _mainWindow->statusBar()->showMessage(tr("Write complete")); _mainWindow->findChild("progress")->setVisible(false); _mainWindow->setEnabled(true); logDebug() << "Write complete."; if (radio->wait(250)) radio->deleteLater(); } void Application::showSettings() { SettingsDialog dialog; if (QDialog::Accepted != dialog.exec()) return; Settings settings; // Handle positioning if (! settings.queryPosition()) { if (_source) _source->stopUpdates(); _currentPosition = settings.position(); } else { if (_source) _source->startUpdates(); } _mainWindow->applySettings(); } void Application::showAbout() { QUiLoader loader; QFile uiFile("://ui/aboutdialog.ui"); uiFile.open(QIODevice::ReadOnly); QDialog *dialog = qobject_cast(loader.load(&uiFile)); QTextEdit *text = dialog->findChild("textEdit"); text->setHtml(text->toHtml().arg(VERSION_STRING)); QTreeWidget *radioTab = dialog->findChild("radioTable"); radioTab->setColumnCount(1); QHash items; foreach (RadioInfo radio, RadioInfo::allRadios(false)) { if (! items.contains(radio.manufacturer())) items.insert(radio.manufacturer(), new QTreeWidgetItem(QStringList(radio.manufacturer()))); items[radio.manufacturer()]->addChild( new QTreeWidgetItem(QStringList(radio.name()))); foreach (RadioInfo alias, radio.alias()) { if (! items.contains(alias.manufacturer())) items.insert(alias.manufacturer(), new QTreeWidgetItem(QStringList(alias.manufacturer()))); items[alias.manufacturer()]->addChild( new QTreeWidgetItem(QStringList(tr("%1 (alias for %2 %3)").arg(alias.name()) .arg(radio.manufacturer()).arg(radio.name())))); } } radioTab->insertTopLevelItems(0, items.values()); radioTab->sortByColumn(0,Qt::AscendingOrder); if (dialog) { dialog->exec(); dialog->deleteLater(); } } void Application::showHelp() { QDesktopServices::openUrl(QUrl("https://dm3mat.darc.de/qdmr/manual")); } void Application::editSatellites() { SatelliteDatabaseDialog dialog(_satellites); if (QDialog::Accepted == dialog.exec()) { _satellites->save(); } else { _satellites->load(); } } void Application::onConfigModifed() { if (! _mainWindow) return; _mainWindow->setWindowModified(true); } void Application::positionUpdated(const QGeoPositionInfo &info) { if (info.isValid()) _currentPosition = info.coordinate(); } bool Application::hasPosition() const { return _currentPosition.isValid(); } QGeoCoordinate Application::position() const { return _currentPosition; } void Application::onPaletteChanged(const QPalette &palette) { // Set theme based on UI mode (light vs. dark). if (isDarkMode(palette)) { QIcon::setThemeName("dark"); logDebug() << "Set icon theme to 'dark'."; } else { QIcon::setThemeName("light"); logDebug() << "Set icon theme to 'light'."; } } qdmr-0.12.3/src/application.hh000066400000000000000000000044251501654372000161630ustar00rootroot00000000000000#ifndef APPLICATION_HH #define APPLICATION_HH #include #include #include #include "config.hh" #include #include "releasenotes.hh" #include "radio.hh" class MainWindow; class QTranslator; class RepeaterDatabase; class UserDatabase; class TalkGroupDatabase; class SatelliteDatabase; class RadioIDListView; class GeneralSettingsView; class ContactListView; class GroupListsView; class ChannelListView; class ZoneListView; class ScanListsView; class PositioningSystemListView; class RoamingChannelListView; class RoamingZoneListView; class ExtensionView; class Application : public QApplication { Q_OBJECT public: Application(int &argc, char *argv[]); virtual ~Application(); MainWindow *mainWindow(); bool isModified() const; UserDatabase *user() const; RepeaterDatabase *repeater() const; TalkGroupDatabase *talkgroup() const; SatelliteDatabase *satellite() const; bool hasPosition() const; QGeoCoordinate position() const; Radio *autoDetect(const ErrorStack &err=ErrorStack()); bool isDarkMode() const; bool isDarkMode(const QPalette &palette) const; public slots: void newCodeplug(); void loadCodeplug(); void saveCodeplug(); void exportCodeplugToChirp(); void importCodeplug(); void detectRadio(); bool verifyCodeplug(Radio *radio=nullptr, bool showSuccess=true); void downloadCodeplug(); void uploadCodeplug(); void uploadCallsignDB(); void uploadSatellites(); void showSettings(); void showAbout(); void showHelp(); void editSatellites(); private slots: void onCodeplugDownloadError(Radio *radio); void onCodeplugDownloaded(Radio *radio, Codeplug *codeplug); void onCodeplugUploadError(Radio *radio); void onCodeplugUploaded(Radio *radio); void onConfigModifed(); void positionUpdated(const QGeoPositionInfo &info); void onPaletteChanged(const QPalette &palette); protected: Config *_config; MainWindow *_mainWindow; QTranslator *_translator; RepeaterDatabase *_repeater; UserDatabase *_users; TalkGroupDatabase *_talkgroups; SatelliteDatabase *_satellites; QGeoPositionInfoSource *_source; QGeoCoordinate _currentPosition; ReleaseNotes _releaseNotes; // Last detected device: USBDeviceDescriptor _lastDevice; }; #endif // APPLICATION_HH qdmr-0.12.3/src/aprssystemdialog.cc000066400000000000000000000210251501654372000172330ustar00rootroot00000000000000#include "aprssystemdialog.hh" #include "ui_aprssystemdialog.h" #include "settings.hh" static QVector> aprsIconTable{ {APRSSystem::Icon::None, APRSSystem::tr("[None]")}, {APRSSystem::Icon::PoliceStation, APRSSystem::tr("Police station")}, {APRSSystem::Icon::Digipeater, APRSSystem::tr("Digipeater")}, {APRSSystem::Icon::Phone, APRSSystem::tr("Phone")}, {APRSSystem::Icon::DXCluster, APRSSystem::tr("DX cluster")}, {APRSSystem::Icon::HFGateway, APRSSystem::tr("HF gateway")}, {APRSSystem::Icon::SmallPlane, APRSSystem::tr("Plane small")}, {APRSSystem::Icon::MobileSatelliteStation, APRSSystem::tr("Mobile Satellite station")}, {APRSSystem::Icon::WheelChair, APRSSystem::tr("Wheel Chair")}, {APRSSystem::Icon::Snowmobile, APRSSystem::tr("Snowmobile")}, {APRSSystem::Icon::RedCross, APRSSystem::tr("Red cross")}, {APRSSystem::Icon::BoyScout, APRSSystem::tr("Boy scout")}, {APRSSystem::Icon::Home, APRSSystem::tr("Home")}, {APRSSystem::Icon::X, APRSSystem::tr("X")}, {APRSSystem::Icon::RedDot, APRSSystem::tr("Red dot")}, {APRSSystem::Icon::Circle0, APRSSystem::tr("Circle 0")}, {APRSSystem::Icon::Circle1, APRSSystem::tr("Circle 1")}, {APRSSystem::Icon::Circle2, APRSSystem::tr("Circle 2")}, {APRSSystem::Icon::Circle3, APRSSystem::tr("Circle 3")}, {APRSSystem::Icon::Circle4, APRSSystem::tr("Circle 4")}, {APRSSystem::Icon::Circle5, APRSSystem::tr("Circle 5")}, {APRSSystem::Icon::Circle6, APRSSystem::tr("Circle 6")}, {APRSSystem::Icon::Circle7, APRSSystem::tr("Circle 7")}, {APRSSystem::Icon::Circle8, APRSSystem::tr("Circle 8")}, {APRSSystem::Icon::Circle9, APRSSystem::tr("Circle 9")}, {APRSSystem::Icon::Fire, APRSSystem::tr("Fire")}, {APRSSystem::Icon::Campground, APRSSystem::tr("Campground")}, {APRSSystem::Icon::Motorcycle, APRSSystem::tr("Motorcycle")}, {APRSSystem::Icon::RailEngine, APRSSystem::tr("Rail engine")}, {APRSSystem::Icon::Car, APRSSystem::tr("Car")}, {APRSSystem::Icon::FileServer, APRSSystem::tr("File server")}, {APRSSystem::Icon::HCFuture, APRSSystem::tr("HC Future")}, {APRSSystem::Icon::AidStation, APRSSystem::tr("Aid station")}, {APRSSystem::Icon::BBS, APRSSystem::tr("BBS")}, {APRSSystem::Icon::Canoe, APRSSystem::tr("Canoe")}, {APRSSystem::Icon::Eyeball, APRSSystem::tr("Eyeball")}, {APRSSystem::Icon::Tractor, APRSSystem::tr("Tractor")}, {APRSSystem::Icon::GridSquare, APRSSystem::tr("Grid Square")}, {APRSSystem::Icon::Hotel, APRSSystem::tr("Hotel")}, {APRSSystem::Icon::TCPIP, APRSSystem::tr("TCP/IP")}, {APRSSystem::Icon::School, APRSSystem::tr("School")}, {APRSSystem::Icon::Logon, APRSSystem::tr("Logon")}, {APRSSystem::Icon::MacOS, APRSSystem::tr("MacOS")}, {APRSSystem::Icon::NTSStation, APRSSystem::tr("NTS station")}, {APRSSystem::Icon::Balloon, APRSSystem::tr("Balloon")}, {APRSSystem::Icon::PoliceCar, APRSSystem::tr("Police car")}, {APRSSystem::Icon::TBD, APRSSystem::tr("TBD")}, {APRSSystem::Icon::RV, APRSSystem::tr("RV")}, {APRSSystem::Icon::Shuttle, APRSSystem::tr("Shuttle")}, {APRSSystem::Icon::SSTV, APRSSystem::tr("SSTV")}, {APRSSystem::Icon::Bus, APRSSystem::tr("Bus")}, {APRSSystem::Icon::ATV, APRSSystem::tr("ATV")}, {APRSSystem::Icon::WXService, APRSSystem::tr("Weather service")}, {APRSSystem::Icon::Helo, APRSSystem::tr("Helo")}, {APRSSystem::Icon::Yacht, APRSSystem::tr("Yacht")}, {APRSSystem::Icon::Windows, APRSSystem::tr("MS Windows")}, {APRSSystem::Icon::Jogger, APRSSystem::tr("Jogger")}, {APRSSystem::Icon::Triangle, APRSSystem::tr("Triangle")}, {APRSSystem::Icon::PBBS, APRSSystem::tr("PBBS")}, {APRSSystem::Icon::LargePlane, APRSSystem::tr("Plane large")}, {APRSSystem::Icon::WXStation, APRSSystem::tr("Weather station")}, {APRSSystem::Icon::DishAntenna, APRSSystem::tr("Dish antenna")}, {APRSSystem::Icon::Ambulance, APRSSystem::tr("Ambulance")}, {APRSSystem::Icon::Bike, APRSSystem::tr("Bike")}, {APRSSystem::Icon::ICP, APRSSystem::tr("ICP")}, {APRSSystem::Icon::FireStation, APRSSystem::tr("Fire station")}, {APRSSystem::Icon::Horse, APRSSystem::tr("Horse")}, {APRSSystem::Icon::FireTruck, APRSSystem::tr("Fire truck")}, {APRSSystem::Icon::Glider, APRSSystem::tr("Glider")}, {APRSSystem::Icon::Hospital, APRSSystem::tr("Hospital")}, {APRSSystem::Icon::IOTA, APRSSystem::tr("IOTA")}, {APRSSystem::Icon::Jeep, APRSSystem::tr("Jeep")}, {APRSSystem::Icon::SmallTruck, APRSSystem::tr("Truck small")}, {APRSSystem::Icon::Laptop, APRSSystem::tr("Laptop")}, {APRSSystem::Icon::MicE, APRSSystem::tr("Mic-E")}, {APRSSystem::Icon::Node, APRSSystem::tr("Node")}, {APRSSystem::Icon::EOC, APRSSystem::tr("EOC")}, {APRSSystem::Icon::Rover, APRSSystem::tr("Rover")}, {APRSSystem::Icon::Grid, APRSSystem::tr("Grid")}, {APRSSystem::Icon::Antenna, APRSSystem::tr("Antenna")}, {APRSSystem::Icon::PowerBoat, APRSSystem::tr("Power boat")}, {APRSSystem::Icon::TruckStop, APRSSystem::tr("Truck stop")}, {APRSSystem::Icon::TruckLarge, APRSSystem::tr("Truck large")}, {APRSSystem::Icon::Van, APRSSystem::tr("Van")}, {APRSSystem::Icon::Water, APRSSystem::tr("Water")}, {APRSSystem::Icon::XAPRS, APRSSystem::tr("XAPRS")}, {APRSSystem::Icon::Yagi, APRSSystem::tr("Yagi")}, {APRSSystem::Icon::Shelter, APRSSystem::tr("Shelter")}}; APRSSystemDialog::APRSSystemDialog(Config *config, QWidget *parent) : QDialog(parent), _config(config), _myAPRS(new APRSSystem()), _aprs(nullptr), ui(new Ui::aprssystemdialog), _icons0(":/icons/aprs/table0.png") { setWindowTitle(tr("Create APRS system")); construct(); } APRSSystemDialog::APRSSystemDialog(Config *config, APRSSystem *aprs, QWidget *parent) : QDialog(parent), _config(config), _myAPRS(new APRSSystem()), _aprs(aprs), ui(new Ui::aprssystemdialog), _icons0(":/icons/aprs/table0.png") { setWindowTitle(tr("Edit APRS system")); if (_aprs) _myAPRS->copy(*_aprs); construct(); } APRSSystemDialog::~APRSSystemDialog() { delete ui; } void APRSSystemDialog::construct() { // Construct UI ui->setupUi(this); Settings settings; // Setup name ui->name->setText(_myAPRS->name()); // Setup analog revert channels ui->channel->addItem(tr("[Selected]"), QVariant::fromValue(nullptr)); if (! _myAPRS->hasRevertChannel()) ui->channel->setCurrentIndex(0); for (int i=0, j=1; i<_config->channelList()->count(); i++) { if (! _config->channelList()->channel(i)->is()) continue; FMChannel *ch = _config->channelList()->channel(i)->as(); ui->channel->addItem(ch->name(), QVariant::fromValue(ch)); if (_myAPRS->hasRevertChannel() && (_myAPRS->revertChannel() == ch)) ui->channel->setCurrentIndex(j); j++; } ui->source->setText(_myAPRS->source()); ui->srcSSID->setValue(_myAPRS->srcSSID()); ui->destination->setText(_myAPRS->destination()); ui->destSSID->setValue(_myAPRS->destSSID()); ui->path->setText(_myAPRS->path()); // Setup icons for (int i=0; i &item = aprsIconTable[i]; ui->icon->addItem(aprsIcon(item.first), item.second, unsigned(item.first)); if (_myAPRS->icon() == item.first) ui->icon->setCurrentIndex(i); } ui->updatePeriod->setValue(_myAPRS->period()); ui->message->setText(_myAPRS->message()); ui->extensionView->setObjectName("aprsSystemExtension"); ui->extensionView->setObject(_myAPRS, _config); if (! settings.showExtensions()) ui->tabWidget->tabBar()->hide(); } APRSSystem * APRSSystemDialog::aprsSystem() { _myAPRS->setName(ui->name->text().simplified()); if (ui->channel->currentData().isNull()) _myAPRS->resetRevertChannel(); else _myAPRS->setRevertChannel(ui->channel->currentData().value()); _myAPRS->setSource(ui->source->text().simplified(), ui->srcSSID->value()); _myAPRS->setDestination(ui->destination->text().simplified(), ui->destSSID->value()); _myAPRS->setPath(ui->path->text().simplified()); _myAPRS->setIcon(APRSSystem::Icon(ui->icon->currentData().toUInt())); _myAPRS->setPeriod(ui->updatePeriod->value()); _myAPRS->setMessage(ui->message->text().simplified()); APRSSystem *system = _myAPRS; if (_aprs) { _aprs->copy(*_myAPRS); system = _aprs; } else { _myAPRS->setParent(nullptr); } return system; } QIcon APRSSystemDialog::aprsIcon(APRSSystem::Icon icon) { unsigned table = (APRSSystem::TABLE_MASK & (unsigned)icon); unsigned idx = (APRSSystem::ICON_MASK & (unsigned)icon); unsigned row = idx/16, col = idx % 16; if (APRSSystem::PRIMARY_TABLE == table) { return QPixmap::fromImage(_icons0.copy(24*col, 24*row, 24,24)); } return QPixmap::fromImage(_icons0.copy(24,0,24,24)); } qdmr-0.12.3/src/aprssystemdialog.hh000066400000000000000000000012531501654372000172460ustar00rootroot00000000000000#ifndef APRSSYSTEMDIALOG_HH #define APRSSYSTEMDIALOG_HH #include #include #include "config.hh" namespace Ui { class aprssystemdialog; } class APRSSystemDialog : public QDialog { Q_OBJECT public: explicit APRSSystemDialog(Config *config, QWidget *parent = nullptr); explicit APRSSystemDialog(Config *config, APRSSystem *aprs, QWidget *parent = nullptr); virtual ~APRSSystemDialog(); APRSSystem *aprsSystem(); protected: void construct(); QIcon aprsIcon(APRSSystem::Icon icon); protected: Config *_config; APRSSystem *_myAPRS; APRSSystem *_aprs; private: Ui::aprssystemdialog *ui; QImage _icons0; }; #endif // APRSSYSTEMDIALOG_HH qdmr-0.12.3/src/aprssystemdialog.ui000066400000000000000000000155161501654372000172730ustar00rootroot00000000000000 aprssystemdialog 0 0 569 431 0 0 Edit APRS System 0 Basic 6 6 Name Channel Source 6 - 15 7 Destination WIDE3 6 - 15 3 Path Icon Update period [s] Message s 6000 10 300 Extensions Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ExtensionView QWidget
      extensionview.hh
      1
      buttonBox accepted() aprssystemdialog accept() 248 254 157 274 buttonBox rejected() aprssystemdialog reject() 316 260 286 274
      qdmr-0.12.3/src/channelcombobox.cc000066400000000000000000000015701501654372000170050ustar00rootroot00000000000000#include "channelcombobox.hh" #include "channel.hh" #include /* ********************************************************************************************* * * Implementation of ChannelComboBox * ********************************************************************************************* */ ChannelComboBox::ChannelComboBox(ChannelList *list, bool includeSelectedChannel, QWidget *parent) : QComboBox(parent) { setInsertPolicy(QComboBox::NoInsert); setEditable(true); completer()->setCompletionMode(QCompleter::PopupCompletion); if (includeSelectedChannel) addItem(SelectedChannel::get()->name(), QVariant::fromValue(SelectedChannel::get())); for (int i=0; icount(); i++) addItem(list->channel(i)->name(), QVariant::fromValue(list->channel(i))); } Channel * ChannelComboBox::channel() const { return currentData().value(); } qdmr-0.12.3/src/channelcombobox.hh000066400000000000000000000005171501654372000170170ustar00rootroot00000000000000#ifndef CHANNELCOMBOBOX_HH #define CHANNELCOMBOBOX_HH #include class Channel; class ChannelList; class ChannelComboBox: public QComboBox { Q_OBJECT public: ChannelComboBox(ChannelList *lst, bool includeSelectedChannel=false, QWidget *parent=nullptr); Channel *channel() const; }; #endif // CHANNELCOMBOBOX_HH qdmr-0.12.3/src/channellistview.cc000066400000000000000000000121051501654372000170370ustar00rootroot00000000000000#include "channellistview.hh" #include "ui_channellistview.h" #include "analogchanneldialog.hh" #include "digitalchanneldialog.hh" #include "config.hh" #include "settings.hh" #include #include ChannelListView::ChannelListView(Config *config, QWidget *parent) : QWidget(parent), ui(new Ui::ChannelListView), _config(config) { ui->setupUi(this); connect(ui->listView->header(), SIGNAL(sectionCountChanged(int,int)), this, SLOT(loadChannelListSectionState())); connect(ui->listView->header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(storeChannelListSectionState())); ui->listView->setModel(new ChannelListWrapper(_config->channelList(), ui->listView)); connect(ui->addAnalogChannel, SIGNAL(clicked()), this, SLOT(onAddAnalogChannel())); connect(ui->addDigitalChannel, SIGNAL(clicked()), this, SLOT(onAddDigitalChannel())); connect(ui->cloneChannel, SIGNAL(clicked()), this, SLOT(onCloneChannel())); connect(ui->remChannel, SIGNAL(clicked()), this, SLOT(onRemChannel())); connect(ui->listView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditChannel(unsigned))); } ChannelListView::~ChannelListView() { delete ui; } void ChannelListView::onAddAnalogChannel() { AnalogChannelDialog dialog(_config); if (QDialog::Accepted != dialog.exec()) return; int row=-1; if (ui->listView->hasSelection()) row = ui->listView->selection().second+1; _config->channelList()->add(dialog.channel(), row); } void ChannelListView::onAddDigitalChannel() { DigitalChannelDialog dialog(_config); if (QDialog::Accepted != dialog.exec()) return; int row=-1; if (ui->listView->hasSelection()) row = ui->listView->selection().second+1; _config->channelList()->add(dialog.channel(), row); } void ChannelListView::onCloneChannel() { // get selection int row = ui->listView->selection().first; if ((! ui->listView->hasSelection()) || (row != ui->listView->selection().second)) { QMessageBox::information(nullptr, tr("Select a single channel first"), tr("To clone a channel, please select a single channel to clone."), QMessageBox::Close); return; } // Get selected channel Channel *channel = _config->channelList()->channel(row); if (! channel) return; // Dispatch by type if (channel->is()) { // clone channel FMChannel *clone = new FMChannel(*(channel->as())); // open editor AnalogChannelDialog dialog(_config, clone); if (QDialog::Accepted != dialog.exec()) { // if rejected -> destroy clone clone->deleteLater(); return; } // update channel dialog.channel(); // add to list (below selected one) _config->channelList()->add(clone, row+1); } else { // clone channel DMRChannel *clone = new DMRChannel(*(channel->as())); // open editor DigitalChannelDialog dialog(_config, clone); if (QDialog::Accepted != dialog.exec()) { clone->deleteLater(); return; } // update channel dialog.channel(); // add to list (below selected one) _config->channelList()->add(clone, row+1); } } void ChannelListView::onRemChannel() { if (! ui->listView->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot delete channel"), tr("Cannot delete channel: You have to select a channel first.")); return; } // Get selection and ask for deletion QPair rows = ui->listView->selection(); int rowcount = rows.second-rows.first+1; if (rows.first == rows.second) { QString name = _config->channelList()->channel(rows.first)->name(); if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete channel?"), tr("Delete channel %1?").arg(name))) return; } else { if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete channel?"), tr("Delete %1 channels?").arg(rowcount))) return; } // collect all selected channels // need to collect them first as rows change when deleting channels QList channels; channels.reserve(rowcount); for(int row=rows.first; row<=rows.second; row++) channels.push_back(_config->channelList()->channel(row)); // remove channels foreach (Channel *channel, channels) _config->channelList()->del(channel); } void ChannelListView::onEditChannel(unsigned row) { Channel *channel = _config->channelList()->channel(row); if (! channel) return; if (channel->is()) { AnalogChannelDialog dialog(_config, channel->as()); if (QDialog::Accepted != dialog.exec()) return; dialog.channel(); } else { DigitalChannelDialog dialog(_config, channel->as()); if (QDialog::Accepted != dialog.exec()) return; dialog.channel(); } } void ChannelListView::loadChannelListSectionState() { Settings settings; ui->listView->header()->restoreState(settings.headerState("channelList")); } void ChannelListView::storeChannelListSectionState() { Settings settings; settings.setHeaderState("channelList", ui->listView->header()->saveState()); } qdmr-0.12.3/src/channellistview.hh000066400000000000000000000011471501654372000170550ustar00rootroot00000000000000#ifndef CHANNELLISTVIEW_HH #define CHANNELLISTVIEW_HH #include class Config; namespace Ui { class ChannelListView; } class ChannelListView : public QWidget { Q_OBJECT public: explicit ChannelListView(Config *config, QWidget *parent = nullptr); ~ChannelListView(); protected slots: void onAddAnalogChannel(); void onAddDigitalChannel(); void onCloneChannel(); void onRemChannel(); void onEditChannel(unsigned row); void loadChannelListSectionState(); void storeChannelListSectionState(); private: Ui::ChannelListView *ui; Config *_config; }; #endif // CHANNELLISTVIEW_HH qdmr-0.12.3/src/channellistview.ui000066400000000000000000000042131501654372000170700ustar00rootroot00000000000000 ChannelListView 0 0 547 300 Form 0 0 Add Analog Channel Alt+A Add Digital Channel Alt+D Clone Channel Alt+C Delete Channel Alt+- ConfigObjectTableView QWidget
      configobjecttableview.hh
      1
      qdmr-0.12.3/src/channelselectiondialog.cc000066400000000000000000000056331501654372000203460ustar00rootroot00000000000000#include "channelselectiondialog.hh" #include "channel.hh" #include "channelcombobox.hh" #include #include #include #include /* ********************************************************************************************* * * Implementation of ChannelSelectionDialog * ********************************************************************************************* */ ChannelSelectionDialog::ChannelSelectionDialog(ChannelList *lst, bool includeSelectedChannel, QWidget *parent) : QDialog(parent) { _channel = new ChannelComboBox(lst, includeSelectedChannel); QDialogButtonBox *bbox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); connect(bbox, SIGNAL(accepted()), this, SLOT(accept())); connect(bbox, SIGNAL(rejected()), this, SLOT(reject())); QVBoxLayout *layout = new QVBoxLayout(); layout->addWidget(new QLabel(tr("Select a channel:"))); layout->addWidget(_channel); layout->addWidget(bbox); setLayout(layout); } Channel * ChannelSelectionDialog::channel() const { return _channel->channel(); } /* ********************************************************************************************* * * Implementation of MultiChannelSelectionDialog * ********************************************************************************************* */ MultiChannelSelectionDialog::MultiChannelSelectionDialog(ChannelList *lst, bool includeSelectedChannel, bool digitalOnly, QWidget *parent) : QDialog(parent) { _channel = new QListWidget(); if (includeSelectedChannel) { QListWidgetItem *item = new QListWidgetItem(tr("[Selected]")); item->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled); item->setData(Qt::UserRole, QVariant::fromValue(SelectedChannel::get())); item->setCheckState(Qt::Unchecked); _channel->addItem(item); } for (int i=0; icount(); i++) { Channel *channel = lst->channel(i); if (digitalOnly && channel->is()) continue; QListWidgetItem *item = new QListWidgetItem(channel->name()); item->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled); item->setData(Qt::UserRole, QVariant::fromValue(channel)); item->setCheckState(Qt::Unchecked); _channel->addItem(item); } QDialogButtonBox *bbox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); connect(bbox, SIGNAL(accepted()), this, SLOT(accept())); connect(bbox, SIGNAL(rejected()), this, SLOT(reject())); QVBoxLayout *layout = new QVBoxLayout(); layout->addWidget(new QLabel(tr("Select a channel:"))); layout->addWidget(_channel); layout->addWidget(bbox); setLayout(layout); } QList MultiChannelSelectionDialog::channel() const { QList channels; for (int i=0; i<_channel->count(); i++) { if (Qt::Checked == _channel->item(i)->checkState()) channels.push_back(_channel->item(i)->data(Qt::UserRole).value()); } return channels; } qdmr-0.12.3/src/channelselectiondialog.hh000066400000000000000000000013721501654372000203540ustar00rootroot00000000000000#ifndef CHANNELSELECTIONDIALOG_HH #define CHANNELSELECTIONDIALOG_HH #include class Channel; class ChannelList; class ChannelComboBox; class QListWidget; class ChannelSelectionDialog: public QDialog { Q_OBJECT public: ChannelSelectionDialog(ChannelList *lst, bool includeSelectedChannel=false, QWidget *parent=nullptr); Channel *channel() const; protected: ChannelComboBox *_channel; }; class MultiChannelSelectionDialog: public QDialog { Q_OBJECT public: MultiChannelSelectionDialog(ChannelList *lst, bool includeSelectedChannel=false, bool digitalOnly=false, QWidget *parent=nullptr); QList channel() const; protected: QListWidget *_channel; }; #endif // CHANNELSELECTIONDIALOG_HH qdmr-0.12.3/src/channelvalidator.cc000066400000000000000000000016261501654372000171640ustar00rootroot00000000000000#include "channelvalidator.hh" #include "channel.hh" /* ********************************************************************************************* * * Implementation of ChannelValidator * ********************************************************************************************* */ ChannelValidator::ChannelValidator(ChannelList *lst, QObject *parent) : QValidator(parent), _channels(lst) { // pass... } QValidator::State ChannelValidator::validate(QString &text, int &pos) const { Q_UNUSED(pos); if (text.size()<1) return QValidator::Intermediate; QValidator::State res = QValidator::Invalid; for (int i=0; i<_channels->count(); i++) { if (0 == text.compare(_channels->channel(i)->name(), Qt::CaseInsensitive)) return QValidator::Acceptable; if (_channels->channel(i)->name().startsWith(text, Qt::CaseInsensitive)) res = QValidator::Intermediate; } return res; } qdmr-0.12.3/src/channelvalidator.hh000066400000000000000000000005541501654372000171750ustar00rootroot00000000000000#ifndef CHANNELVALIDATOR_HH #define CHANNELVALIDATOR_HH #include class ChannelList; class ChannelValidator: public QValidator { Q_OBJECT public: ChannelValidator(ChannelList *lst, QObject *parent=nullptr); QValidator::State validate(QString &input, int &pos) const; protected: ChannelList *_channels; }; #endif // CHANNELVALIDATOR_HH qdmr-0.12.3/src/collapsablewidget.cc000066400000000000000000000025101501654372000173240ustar00rootroot00000000000000#include "collapsablewidget.hh" #include #include CollapsableWidget::CollapsableWidget(QWidget *parent) : QToolButton(parent), _content(nullptr) { setCheckable(true); setStyleSheet("background:none"); setToolButtonStyle(Qt::ToolButtonTextBesideIcon); setIconSize(QSize(8, 8)); setFont(QApplication::font()); hideContent(); setArrowType(Qt::ArrowType::RightArrow); connect(this, &QToolButton::toggled, [=](bool checked) { setArrowType(checked ? Qt::ArrowType::DownArrow : Qt::ArrowType::RightArrow); _content != nullptr && checked ? showContent() : hideContent(); }); } void CollapsableWidget::setContent(QWidget *content) { assert(content != nullptr); _content = content; auto animation = new QPropertyAnimation(_content, "maximumHeight"); // QObject with auto delete animation->setStartValue(0); animation->setEasingCurve(QEasingCurve::InOutQuad); animation->setDuration(300); animation->setEndValue(content->geometry().height() + 10); _animator.addAnimation(animation); if (!isChecked()) content->setMaximumHeight(0); } void CollapsableWidget::hideContent() { _animator.setDirection(QAbstractAnimation::Backward); _animator.start(); } void CollapsableWidget::showContent() { _animator.setDirection(QAbstractAnimation::Forward); _animator.start(); } qdmr-0.12.3/src/collapsablewidget.hh000066400000000000000000000006701501654372000173430ustar00rootroot00000000000000#ifndef COLLAPSABLEWIDGET_HH #define COLLAPSABLEWIDGET_HH #include #include class CollapsableWidget : public QToolButton { Q_OBJECT public: explicit CollapsableWidget(QWidget *parent=nullptr); void setContent(QWidget *content); public slots: void hideContent(); void showContent(); private: QWidget *_content; QParallelAnimationGroup _animator; }; #endif // COLLAPSABLEWIDGET_HH qdmr-0.12.3/src/configitemwrapper.cc000066400000000000000000000710031501654372000173670ustar00rootroot00000000000000#include "configitemwrapper.hh" #include #include "logger.hh" #include "utils.hh" #include #include #include /* ********************************************************************************************* * * Implementation of GenericListWrapper * ********************************************************************************************* */ GenericListWrapper::GenericListWrapper(AbstractConfigObjectList *list, QObject *parent) : QAbstractListModel(parent), _list(list) { if (nullptr == _list) return; connect(_list, SIGNAL(destroyed(QObject*)), this, SLOT(onListDeleted())); connect(_list, SIGNAL(elementAdded(int)), this, SLOT(onItemAdded(int))); connect(_list, SIGNAL(elementModified(int)), this, SLOT(onItemModified(int))); connect(_list, SIGNAL(elementRemoved(int)), this, SLOT(onItemRemoved(int))); } int GenericListWrapper::rowCount(const QModelIndex &index) const { Q_UNUSED(index) if (nullptr == _list) return 0; return _list->count(); } int GenericListWrapper::columnCount(const QModelIndex &index) const { Q_UNUSED(index) if (nullptr == _list) return 0; return 1; } Qt::ItemFlags GenericListWrapper::flags(const QModelIndex &index) const { if (index.isValid()) return QAbstractListModel::flags(index) | Qt::ItemIsDragEnabled; return QAbstractListModel::flags(index) | Qt::ItemIsDropEnabled; } Qt::DropActions GenericListWrapper::supportedDropActions() const { return Qt::MoveAction; } bool GenericListWrapper::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) { logDebug() << "Move " << count << " rows from " << sourceRow << " to " << destinationChild; beginMoveRows(sourceParent, sourceRow, sourceRow+count-1, destinationParent, destinationChild); bool success = _list->move(sourceRow, count, destinationChild); endMoveRows(); return success; } void GenericListWrapper::onListDeleted() { beginResetModel(); _list = nullptr; endResetModel(); } void GenericListWrapper::onItemAdded(int idx) { beginInsertRows(QModelIndex(), idx, idx); endInsertRows(); } void GenericListWrapper::onItemRemoved(int idx) { beginRemoveRows(QModelIndex(), idx, idx); //logDebug() << "Signal removal of item at idx=" << idx; endRemoveRows(); } void GenericListWrapper::onItemModified(int idx) { emit dataChanged(index(idx),index(idx)); } /* ********************************************************************************************* * * Implementation of GenericTableWrapper * ********************************************************************************************* */ GenericTableWrapper::GenericTableWrapper(AbstractConfigObjectList *list, QObject *parent) : QAbstractTableModel(parent), _list(list), _insertRow(-1) { if (nullptr == _list) return; connect(_list, SIGNAL(destroyed(QObject*)), this, SLOT(onListDeleted())); connect(_list, SIGNAL(elementAdded(int)), this, SLOT(onItemAdded(int))); connect(_list, SIGNAL(elementModified(int)), this, SLOT(onItemModified(int))); connect(_list, SIGNAL(elementRemoved(int)), this, SLOT(onItemRemoved(int))); } int GenericTableWrapper::rowCount(const QModelIndex &index) const { Q_UNUSED(index) if (nullptr == _list) return 0; return _list->count(); } Qt::ItemFlags GenericTableWrapper::flags(const QModelIndex &index) const { if (index.isValid()) return QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled; return QAbstractTableModel::flags(index) | Qt::ItemIsDropEnabled; } Qt::DropActions GenericTableWrapper::supportedDropActions() const { return Qt::MoveAction; } bool GenericTableWrapper::insertRows(int row, int count, const QModelIndex &parent) { Q_UNUSED(parent); Q_UNUSED(count); if (-1 == _insertRow) { _insertRow = row; return true; } return false; } bool GenericTableWrapper::removeRows(int row, int count, const QModelIndex &parent) { Q_UNUSED(parent); if (-1 == _insertRow) return false; bool success = moveRows(QModelIndex(), row, count, QModelIndex(), _insertRow); _insertRow = -1; return success; } bool GenericTableWrapper::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) { logDebug() << "Move " << count << " rows from " << sourceRow << " to " << destinationChild; beginMoveRows(sourceParent, sourceRow, sourceRow+count-1, destinationParent, destinationChild); bool success = _list->move(sourceRow, count, destinationChild); endMoveRows(); return success; } bool GenericTableWrapper::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const { if (0 != column) return false; return QAbstractTableModel::canDropMimeData(data, action, row, column, parent); } void GenericTableWrapper::onListDeleted() { beginResetModel(); _list = nullptr; endResetModel(); } void GenericTableWrapper::onItemAdded(int idx) { beginInsertRows(QModelIndex(), idx, idx); endInsertRows(); } void GenericTableWrapper::onItemRemoved(int idx) { beginRemoveRows(QModelIndex(), idx, idx); //logDebug() << "Signal removal of item at idx=" << idx; endRemoveRows(); } void GenericTableWrapper::onItemModified(int idx) { emit dataChanged(index(idx,0),index(idx,columnCount()-1)); } QString GenericTableWrapper::formatExtensions(int idx) const { if (idx >= _list->count()) return QString(); ConfigObject *item = _list->get(idx); QStringList extensions; auto metaObj = item->metaObject(); for (int i=QObject::staticMetaObject.propertyCount(); ipropertyCount(); i++) { auto prop = metaObj->property(i); if (QMetaType::UnknownType == prop.userType()) continue; QMetaType type(prop.userType()); if (! (QMetaType::PointerToQObject & type.flags())) continue; const QMetaObject *propType = type.metaObject(); if (! propType->inherits(&ConfigExtension::staticMetaObject)) continue; if (prop.read(item).isNull()) continue; extensions.append(prop.name()); } return extensions.join(", "); } /* ********************************************************************************************* * * Implementation of ChannelListWrapper * ********************************************************************************************* */ ChannelListWrapper::ChannelListWrapper(ChannelList *list, QObject *parent) : GenericTableWrapper(list, parent) { // pass... } int ChannelListWrapper::columnCount(const QModelIndex &index) const { Q_UNUSED(index); return 22; } QVariant ChannelListWrapper::data(const QModelIndex &index, int role) const { if (nullptr == _list) return QVariant(); if ((! index.isValid()) || (index.row()>=_list->count())) return QVariant(); if (Qt::ForegroundRole == role) { const QPalette &palette = qobject_cast(QObject::parent())->palette(); QColor active = palette.color(QPalette::Active, QPalette::Text); QColor inactive = palette.color(QPalette::Inactive, QPalette::Text); bool isDigital = dynamic_cast(_list)->channel(index.row())->is(); switch(index.column()) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: return active; case 11: case 12: case 13: case 14: return (isDigital ? active : inactive); case 15: return active; case 16: return (isDigital ? active : inactive); case 17: case 18: case 19: case 20: return (isDigital ? inactive : active); } } if ((Qt::DisplayRole!=role) && (Qt::EditRole!=role)) return QVariant(); Channel *channel = dynamic_cast(_list)->channel(index.row()); switch (index.column()) { case 0: if (channel->is()) return tr("FM"); else return tr("DMR"); case 1: return channel->name(); case 2: return channel->rxFrequency().format(Frequency::Format::MHz); case 3: return channel->txFrequency().format(Frequency::Format::MHz); case 4: if (channel->defaultPower()) return tr("[Default]"); switch (channel->power()) { case Channel::Power::Max: return tr("Max"); break; case Channel::Power::High: return tr("High"); break; case Channel::Power::Mid: return tr("Mid"); break; case Channel::Power::Low: return tr("Low"); break; case Channel::Power::Min: return tr("Min"); break; } break; case 5: if (channel->defaultTimeout()) return tr("[Default]"); if (channel->timeoutDisabled()) return tr("Off"); return QString::number(channel->timeout()); case 6: return channel->rxOnly() ? tr("On") : tr("Off"); case 7: if (DMRChannel *digi = channel->as()) { switch (digi->admit()) { case DMRChannel::Admit::Always: return tr("Always"); break; case DMRChannel::Admit::Free: return tr("Free"); break; case DMRChannel::Admit::ColorCode: return tr("Color"); break; } } else if (FMChannel *analog = channel->as()) { switch (analog->admit()) { case FMChannel::Admit::Always: return tr("Always"); break; case FMChannel::Admit::Free: return tr("Free"); break; case FMChannel::Admit::Tone: return tr("Tone"); break; } } break; case 8: if (channel->scanList()) { return channel->scanList()->name(); } else { return QString("-"); } case 9: { // Collect zones, the channel is a member of QStringList zones; for(int i=0;iconfig()->zones()->count(); i++) { Zone *zone = channel->config()->zones()->zone(i); if (zone->contains(channel)) zones.append(zone->name()); } return zones.join(", "); } break; case 10: if (DMRChannel *digi = channel->as()) { return digi->colorCode(); } else if (channel->is()) { return tr("[None]"); } break; case 11: if (DMRChannel *digi = channel->as()) { return (DMRChannel::TimeSlot::TS1 == digi->timeSlot()) ? 1 : 2; } else if (channel->is()) { return tr("[None]"); } break; case 12: if (DMRChannel *digi = channel->as()) { if (digi->groupListObj()) { return digi->groupListObj()->name(); } else { return QString("-"); } } else if (channel->is()) { return tr("[None]"); } break; case 13: if (DMRChannel *digi = channel->as()) { if (digi->txContactObj()) return digi->txContactObj()->name(); else return QString("-"); } else if (channel->is()) { return tr("[None]"); } break; case 14: if (DMRChannel *digi = channel->as()) { if ((nullptr == digi->radioIdObj()) || (DefaultRadioID::get() == digi->radioIdObj())) return tr("[Default]"); return digi->radioIdObj()->name(); } else if (channel->is()) { return tr("[None]"); } break; case 15: if (DMRChannel *digi = channel->as()) { if (digi->aprsObj()) return digi->aprsObj()->name(); else return QString("-"); } else if (FMChannel *analog = channel->as()) { if (analog->aprsSystem()) return analog->aprsSystem()->name(); else return QString("-"); } break; case 16: if (DMRChannel *digi = channel->as()) { if (nullptr == digi->roamingZone()) return QString("-"); else if (DefaultRoamingZone::get() == digi->roamingZone()) return tr("[Default]"); return digi->roamingZone()->name(); } else if (channel->is()) { return tr("[None]"); } break; case 17: if (channel->is()) { return tr("[None]"); } else if (FMChannel *analog = channel->as()) { if (analog->defaultSquelch()) return tr("[Default]"); if (analog->squelchDisabled()) return tr("Open"); else return analog->squelch(); } break; case 18: if (channel->is()) { return tr("[None]"); } else if (FMChannel *analog = channel->as()) { return analog->rxTone().format(); } break; case 19: if (channel->is()) { return tr("[None]"); } else if (FMChannel *analog = channel->as()) { return analog->txTone().format(); } break; case 20: if (channel->is()) { return tr("[None]"); } else if (FMChannel *analog = channel->as()) { if (FMChannel::Bandwidth::Wide == analog->bandwidth()) { return tr("Wide"); } else return tr("Narrow"); } break; case 21: { auto exts = formatExtensions(index.row()); if (exts.isEmpty()) return tr("[None]"); return exts; } default: break; } return QVariant(); } QVariant ChannelListWrapper::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::DisplayRole!=role) || (Qt::Horizontal!=orientation)) return QVariant(); switch (section) { case 0: return tr("Type"); case 1: return tr("Name"); case 2: return tr("Rx Frequency"); case 3: return tr("Tx Frequency"); case 4: return tr("Power"); case 5: return tr("Timeout"); case 6: return tr("Rx Only"); case 7: return tr("Admit"); case 8: return tr("Scanlist"); case 9: return tr("Zones"); case 10: return tr("CC"); case 11: return tr("TS"); case 12: return tr("RX Group List"); case 13: return tr("TX Contact"); case 14: return tr("DMR ID"); case 15: return tr("GPS/APRS"); case 16: return tr("Roaming"); case 17: return tr("Squelch"); case 18: return tr("Rx Tone"); case 19: return tr("Tx Tone"); case 20: return tr("Bandwidth"); case 21: return tr("Extensions"); default: break; } return QVariant(); } /* ********************************************************************************************* * * Implementation of ChannelRefListWrapper * ********************************************************************************************* */ ChannelRefListWrapper::ChannelRefListWrapper(ChannelRefList *list, QObject *parent) : GenericListWrapper(list, parent) { // pass... } QVariant ChannelRefListWrapper::data(const QModelIndex &index, int role) const { if ((Qt::DisplayRole!=role) || (! index.isValid()) || (index.row()>=_list->count())) return QVariant(); return _list->get(index.row())->as()->name(); } QVariant ChannelRefListWrapper::headerData(int section, Qt::Orientation orientation, int role) const { if ((0!=section) || (Qt::Horizontal!=orientation) || (Qt::DisplayRole!=role)) return QVariant(); return tr("Channel"); } /* ********************************************************************************************* * * Implementation of RoamingChannelListWrapper * ********************************************************************************************* */ RoamingChannelListWrapper::RoamingChannelListWrapper(RoamingChannelList *list, QObject *parent) : GenericTableWrapper(list, parent) { // pass... } int RoamingChannelListWrapper::columnCount(const QModelIndex &index) const { Q_UNUSED(index); return 7; } QVariant RoamingChannelListWrapper::data(const QModelIndex &index, int role) const { if ((Qt::DisplayRole!=role) || (! index.isValid()) || (index.row() >= _list->count())) return QVariant(); RoamingChannel *ch = _list->get(index.row())->as(); // Dispatch by column switch (index.column()) { case 0: return ch->name(); case 1: return ch->rxFrequency().format(Frequency::Format::MHz); case 2: return ch->txFrequency().format(Frequency::Format::MHz); case 3: if (ch->colorCodeOverridden()) return ch->colorCode(); return tr("[Selected]"); case 4: if (ch->timeSlotOverridden()) { switch(ch->timeSlot()) { case DMRChannel::TimeSlot::TS1: return 1; case DMRChannel::TimeSlot::TS2: return 2; } } return tr("[Selected]"); case 5: { QStringList zones; for(int i=0;iconfig()->roamingZones()->count(); i++) { RoamingZone *zone = ch->config()->roamingZones()->zone(i); if (zone->contains(ch)) zones.append(zone->name()); } return zones.join(", "); } break; case 6: { auto exts = formatExtensions(index.row()); if (exts.isEmpty()) return tr("[None]"); return exts; } default: break; } return QVariant(); } QVariant RoamingChannelListWrapper::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::Horizontal!=orientation) || (Qt::DisplayRole!=role)) return QVariant(); switch (section) { case 0: return tr("Name"); case 1: return tr("RX Frequency"); case 2: return tr("TX Frequency"); case 3: return tr("CC"); case 4: return tr("TS"); case 5: return tr("Zones"); case 6: return tr("Extensions"); default: break; } return QVariant(); } /* ********************************************************************************************* * * Implementation of RoamingChannelRefListWrapper * ********************************************************************************************* */ RoamingChannelRefListWrapper::RoamingChannelRefListWrapper(RoamingChannelRefList *list, QObject *parent) : GenericListWrapper(list, parent) { // pass... } QVariant RoamingChannelRefListWrapper::data(const QModelIndex &index, int role) const { if ((Qt::DisplayRole!=role) || (! index.isValid()) || (index.row() >= _list->count())) return QVariant(); return _list->get(index.row())->as()->name(); } QVariant RoamingChannelRefListWrapper::headerData(int section, Qt::Orientation orientation, int role) const { if ((0!=section) || (Qt::Horizontal!=orientation) || (Qt::DisplayRole!=role)) return QVariant(); return tr("Roaming Channel"); } /* ********************************************************************************************* * * Implementation of ContactListWrapper * ********************************************************************************************* */ ContactListWrapper::ContactListWrapper(ContactList *list, QObject *parent) : GenericTableWrapper(list, parent) { // pass... } int ContactListWrapper::columnCount(const QModelIndex &index) const { Q_UNUSED(index); return 5; } QVariant ContactListWrapper::data(const QModelIndex &index, int role) const { if ((!index.isValid()) || (index.row()>=_list->count())) return QVariant(); if (Qt::DisplayRole == role) { Contact *contact = _list->get(index.row())->as(); if (contact->is()) { DTMFContact *dtmf = contact->as(); switch (index.column()) { case 0: return tr("DTMF"); case 1: return dtmf->name(); case 2: return dtmf->number(); case 3: return (dtmf->ring() ? tr("On") : tr("Off")); case 4: return formatExtensions(index.row()); default: return QVariant(); } } else if (contact->is()) { DMRContact *digi = contact->as(); switch (index.column()) { case 0: switch (digi->type()) { case DMRContact::PrivateCall: return tr("Private Call"); case DMRContact::GroupCall: return tr("Group Call"); case DMRContact::AllCall: return tr("All Call"); } break; case 1: return digi->name(); case 2: return digi->number(); case 3: return (digi->ring() ? tr("On") : tr("Off")); case 4: { auto exts = formatExtensions(index.row()); if (exts.isEmpty()) return tr("[None]"); return exts; } default: return QVariant(); } } } return QVariant(); } QVariant ContactListWrapper::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::DisplayRole != role) || (Qt::Horizontal != orientation)) { return QVariant(); } if (0 == section) { return tr("Type"); } else if (1 == section) { return tr("Name"); } else if (2 == section) { return tr("Number"); } else if (3 == section) { return tr("RX Tone"); } else if (4 == section) { return tr("Extensions"); } return QVariant(); } /* ********************************************************************************************* * * Implementation of ZoneListWrapper * ********************************************************************************************* */ ZoneListWrapper::ZoneListWrapper(ZoneList *list, QObject *parent) : GenericListWrapper(list, parent) { // pass... } QVariant ZoneListWrapper::data(const QModelIndex &index, int role) const { if ((Qt::DisplayRole!=role) || (index.row()>=_list->count()) || (0 != index.column())) return QVariant(); Zone *zone = _list->get(index.row())->as(); return zone->name(); } QVariant ZoneListWrapper::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::DisplayRole!=role) || (Qt::Horizontal!=orientation) || (0 != section)) return QVariant(); return tr("Zone"); } /* ********************************************************************************************* * * Implementation of PositioningSystemListWrapper * ********************************************************************************************* */ PositioningSystemListWrapper::PositioningSystemListWrapper(PositioningSystems *list, QObject *parent) : GenericTableWrapper(list, parent) { // pass... } int PositioningSystemListWrapper::columnCount(const QModelIndex &idx) const { Q_UNUSED(idx); return 7; } QVariant PositioningSystemListWrapper::data(const QModelIndex &index, int role) const { if ((! index.isValid()) || (index.row()>=_list->count())) return QVariant(); if ((Qt::DisplayRole!=role) && (Qt::EditRole!=role)) return QVariant(); PositioningSystem *sys = _list->get(index.row())->as(); switch (index.column()) { case 0: if (sys->is()) return tr("DMR"); else if (sys->is()) return tr("APRS"); else return QString("Oops!"); case 1: return sys->name(); case 2: if (sys->is()) { if (! sys->as()->hasContact()) return tr("[None]"); return sys->as()->contactObj()->name(); } else if (sys->is()) return QString("%1-%2").arg(sys->as()->destination()) .arg(sys->as()->destSSID()); break; case 3: return sys->period(); case 4: if (sys->is()) { if (! sys->as()->hasRevertChannel()) return tr("[Selected]"); return sys->as()->revertChannel()->name(); } else if (sys->is()) { if (! sys->as()->hasRevertChannel()) return tr("[Selected]"); return sys->as()->revertChannel()->name(); } break; case 5: if (sys->is()) return tr("[None]"); else if (sys->is()) return sys->as()->message(); break; case 6: { auto exts = formatExtensions(index.row()); if (exts.isEmpty()) return tr("[None]"); return exts; } default: break; } return QVariant(); } QVariant PositioningSystemListWrapper::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::DisplayRole!=role) || (Qt::Horizontal!=orientation)) return QVariant(); switch (section) { case 0: return tr("Type"); case 1: return tr("Name"); case 2: return tr("Destination"); case 3: return tr("Period [s]"); case 4: return tr("Channel"); case 5: return tr("Message"); case 6: return tr("Extensions"); default: break; } return QVariant(); } /* ********************************************************************************************* * * Implementation of ScanListsWrapper * ********************************************************************************************* */ ScanListsWrapper::ScanListsWrapper(ScanLists *list, QObject *parent) : GenericListWrapper(list, parent) { // pass... } QVariant ScanListsWrapper::data(const QModelIndex &index, int role) const { if ((Qt::DisplayRole!=role) || (index.row()>=_list->count()) || (0 != index.column())) return QVariant(); return _list->get(index.row())->as()->name(); } QVariant ScanListsWrapper::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::DisplayRole!=role) || (Qt::Horizontal!=orientation) || (0 != section)) return QVariant(); return tr("Scan-List"); } /* ********************************************************************************************* * * Implementation of GroupListsWrapper * ********************************************************************************************* */ GroupListsWrapper::GroupListsWrapper(RXGroupLists *list, QObject *parent) : GenericListWrapper(list, parent) { // pass... } QVariant GroupListsWrapper::data(const QModelIndex &index, int role) const { if ((Qt::DisplayRole!=role) || (! index.isValid()) || (index.row()>=_list->count())) return QVariant(); return _list->get(index.row())->as()->name(); } QVariant GroupListsWrapper::headerData(int section, Qt::Orientation orientation, int role) const { if ((0!=section) || (Qt::Horizontal!=orientation) || (Qt::DisplayRole!=role)) return QVariant(); return tr("RX Group Lists"); } /* ********************************************************************************************* * * Implementation of GroupListWrapper * ********************************************************************************************* */ GroupListWrapper::GroupListWrapper(RXGroupList *list, QObject *parent) : GenericListWrapper(list->contacts(), parent) { // pass... } QVariant GroupListWrapper::data(const QModelIndex &index, int role) const { if ((Qt::DisplayRole!=role) || (! index.isValid()) || (index.row()>=_list->count())) return QVariant(); return _list->get(index.row())->as()->name(); } QVariant GroupListWrapper::headerData(int section, Qt::Orientation orientation, int role) const { if ((0!=section) || (Qt::Horizontal!=orientation) || (Qt::DisplayRole!=role)) return QVariant(); return tr("Contact"); } /* ********************************************************************************************* * * Implementation of RoamingListWrapper * ********************************************************************************************* */ RoamingListWrapper::RoamingListWrapper(RoamingZoneList *list, QObject *parent) : GenericListWrapper(list, parent) { // pass... } QVariant RoamingListWrapper::data(const QModelIndex &index, int role) const { if ((Qt::DisplayRole!=role) || (index.row()>=_list->count()) || (0 != index.column())) return QVariant(); RoamingZone *zone = _list->get(index.row())->as(); return tr("%1 (containing %2 channels)").arg(zone->name()).arg(zone->count()); } QVariant RoamingListWrapper::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::DisplayRole!=role) || (Qt::Horizontal!=orientation) || (0 != section)) return QVariant(); return tr("Roaming zone"); } /* ********************************************************************************************* * * Implementation of RadioIdListWrapper * ********************************************************************************************* */ RadioIdListWrapper::RadioIdListWrapper(RadioIDList *list, QObject *parent) : GenericTableWrapper(list, parent) { // pass... } int RadioIdListWrapper::columnCount(const QModelIndex &idx) const { Q_UNUSED(idx); return 4; } QVariant RadioIdListWrapper::data(const QModelIndex &index, int role) const { if ((! index.isValid()) || (index.row()>=_list->count())) return QVariant(); if ((Qt::DisplayRole!=role) && (Qt::EditRole!=role)) return QVariant(); DMRRadioID *id = _list->get(index.row())->as(); switch (index.column()) { case 0: return ("DMR"); case 1: return id->name(); case 2: return id->number(); case 3:{ auto exts = formatExtensions(index.row()); if (exts.isEmpty()) return tr("[None]"); return exts; } default: break; } return QVariant(); } QVariant RadioIdListWrapper::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::DisplayRole!=role) || (Qt::Horizontal!=orientation)) return QVariant(); switch (section) { case 0: return tr("Type"); case 1: return tr("Name"); case 2: return tr("Number"); case 3: return tr("Extensions"); default: break; } return QVariant(); } qdmr-0.12.3/src/configitemwrapper.hh000066400000000000000000000234711501654372000174070ustar00rootroot00000000000000#ifndef CONFIG_ITEM_WRAPPER_HH #define CONFIG_ITEM_WRAPPER_HH #include "config.hh" #include class GenericListWrapper: public QAbstractListModel { Q_OBJECT protected: GenericListWrapper(AbstractConfigObjectList *list, QObject *parent=nullptr); public: // QAbstractListModel interface /** Implements QAbstractTableModel, returns number of rows. */ int rowCount(const QModelIndex &index) const; /** Implements QAbstractTableModel, returns number of columns. */ int columnCount(const QModelIndex &index) const; Qt::ItemFlags flags(const QModelIndex &index) const; Qt::DropActions supportedDropActions() const; bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild); signals: /** Gets emitted once the table has been changed. */ void modified(); protected slots: /** Internal used callback on deleted config. */ void onListDeleted(); /** Internal callback on added items. */ void onItemAdded(int idx); /** Internal callback on deleted channels. */ void onItemRemoved(int idx); /** Internal callback on modified channels. */ void onItemModified(int idx); protected: /** Holds a weak reference to the list object. */ AbstractConfigObjectList *_list; }; class GenericTableWrapper: public QAbstractTableModel { Q_OBJECT protected: GenericTableWrapper(AbstractConfigObjectList *list, QObject *parent=nullptr); public: // QAbstractTableModel interface /** Implements QAbstractTableModel, returns number of rows. */ int rowCount(const QModelIndex &index) const; Qt::ItemFlags flags(const QModelIndex &index) const; Qt::DropActions supportedDropActions() const; bool insertRows(int row, int count, const QModelIndex &parent); bool removeRows(int row, int count, const QModelIndex &parent); bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild); bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const; signals: /** Gets emitted once the table has been changed. */ void modified(); protected slots: /** Internal used callback on deleted config. */ void onListDeleted(); /** Internal used callback on adding an item. */ void onItemAdded(int idx); /** Internal callback on deleted channels. */ void onItemRemoved(int idx); /** Internal callback on modified channels. */ void onItemModified(int idx); protected: /** Returns a string containing all extension properties set. */ QString formatExtensions(int idx) const; protected: /** Holds a weak reference to the list object. */ AbstractConfigObjectList *_list; /** Insert index for drag & drop move. */ int _insertRow; }; class ChannelListWrapper: public GenericTableWrapper { Q_OBJECT public: ChannelListWrapper(ChannelList *list, QObject *parent=nullptr); public: // QAbstractTableModel interface /** Implements QAbstractTableModel, returns number of columns. */ int columnCount(const QModelIndex &index) const; /** Implements QAbstractTableModel, returns data at cell. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; /** Implements QAbstractTableModel, returns header at section. */ QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; }; class ChannelRefListWrapper: public GenericListWrapper { Q_OBJECT public: ChannelRefListWrapper(ChannelRefList *list, QObject *parent=nullptr); public: // Implementation of QAbstractListModel /** Returns the cell data at given index, implements the QAbstractTableModel. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; /** Implementation of QAbstractListModel, returns the header data at the given section. */ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; }; class RoamingChannelListWrapper: public GenericTableWrapper { Q_OBJECT public: RoamingChannelListWrapper(RoamingChannelList *list, QObject *parent=nullptr); public: // Implementation of QAbstractTableModel /** Returns the number of columns, implements the QAbstractTableModel. */ int columnCount(const QModelIndex &index) const; /** Returns the cell data at given index, implements the QAbstractTableModel. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; /** Implementation of QAbstractListModel, returns the header data at the given section. */ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; }; class RoamingChannelRefListWrapper: public GenericListWrapper { Q_OBJECT public: RoamingChannelRefListWrapper(RoamingChannelRefList *list, QObject *parent=nullptr); public: // Implementation of QAbstractListModel /** Returns the cell data at given index, implements the QAbstractTableModel. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; /** Implementation of QAbstractListModel, returns the header data at the given section. */ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; }; class ContactListWrapper: public GenericTableWrapper { Q_OBJECT public: ContactListWrapper(ContactList *list, QObject *parent=nullptr); public: // Implementation of QAbstractTableModel /** Returns the number of columns, implements the QAbstractTableModel. */ int columnCount(const QModelIndex &index) const; /** Returns the cell data at given index, implements the QAbstractTableModel. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; /** Returns the header at given section, implements the QAbstractTableModel. */ QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; }; class ZoneListWrapper: public GenericListWrapper { Q_OBJECT public: ZoneListWrapper(ZoneList *list, QObject *parent=nullptr); public: // Implementation of QAbstractListModel /** Returns the cell data at given index, implements the QAbstractTableModel. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; /** Implementation of QAbstractListModel, returns the header data at the given section. */ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; }; class PositioningSystemListWrapper: public GenericTableWrapper { Q_OBJECT public: PositioningSystemListWrapper(PositioningSystems *list, QObject *parent=nullptr); public: // Implementation of QAbstractTableModel /** Returns the number of columns, implements the QAbstractTableModel. */ int columnCount(const QModelIndex &index) const; /** Returns the cell data at given index, implements the QAbstractTableModel. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; /** Returns the header at given section, implements the QAbstractTableModel. */ QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; }; class ScanListsWrapper: public GenericListWrapper { Q_OBJECT public: ScanListsWrapper(ScanLists *list, QObject *parent=nullptr); public: // Implementation of QAbstractListModel /** Returns the cell data at given index, implements the QAbstractTableModel. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; /** Implementation of QAbstractListModel, returns the header data at the given section. */ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; }; class GroupListsWrapper: public GenericListWrapper { Q_OBJECT public: GroupListsWrapper(RXGroupLists *list, QObject *parent=nullptr); public: // Implementation of QAbstractListModel /** Returns the cell data at given index, implements the QAbstractTableModel. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; /** Implementation of QAbstractListModel, returns the header data at the given section. */ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; }; class GroupListWrapper: public GenericListWrapper { Q_OBJECT public: GroupListWrapper(RXGroupList *list, QObject *parent=nullptr); public: // Implementation of QAbstractListModel /** Returns the cell data at given index, implements the QAbstractTableModel. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; /** Implementation of QAbstractListModel, returns the header data at the given section. */ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; }; class RoamingListWrapper: public GenericListWrapper { Q_OBJECT public: RoamingListWrapper(RoamingZoneList *list, QObject *parent=nullptr); public: // Implementation of QAbstractListModel /** Returns the cell data at given index, implements the QAbstractTableModel. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; /** Returns the header at given section, implements the QAbstractTableModel. */ QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; }; class RadioIdListWrapper: public GenericTableWrapper { Q_OBJECT public: RadioIdListWrapper(RadioIDList *list, QObject *parent=nullptr); public: // Implementation of QAbstractTableModel /** Returns the number of columns, implements the QAbstractTableModel. */ int columnCount(const QModelIndex &index) const; /** Returns the cell data at given index, implements the QAbstractTableModel. */ QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; /** Returns the header at given section, implements the QAbstractTableModel. */ QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; }; #endif // CONFIG_ITEM_WRAPPER_HH qdmr-0.12.3/src/configmergedialog.cc000066400000000000000000000077771501654372000173300ustar00rootroot00000000000000#include "configmergedialog.hh" #include "ui_configmergedialog.h" #include "configmergevisitor.hh" #include "settings.hh" ConfigMergeDialog::ConfigMergeDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ConfigMergeDialog) { ui->setupUi(this); ui->itemStrategy->setItemData(0, QVariant((uint) ConfigMergeVisitor::ItemStrategy::Ignore)); ui->itemStrategy->setItemData(1, QVariant((uint) ConfigMergeVisitor::ItemStrategy::Override)); ui->itemStrategy->setItemData(2, QVariant((uint) ConfigMergeVisitor::ItemStrategy::Duplicate)); ui->setStrategy->setItemData(0, QVariant((uint) ConfigMergeVisitor::SetStrategy::Ignore)); ui->setStrategy->setItemData(1, QVariant((uint) ConfigMergeVisitor::SetStrategy::Override)); ui->setStrategy->setItemData(2, QVariant((uint) ConfigMergeVisitor::SetStrategy::Duplicate)); ui->setStrategy->setItemData(3, QVariant((uint) ConfigMergeVisitor::SetStrategy::Merge)); Settings settings; switch (settings.configMergeItemStrategy()) { case ConfigMergeVisitor::ItemStrategy::Ignore: ui->itemStrategy->setCurrentIndex(0); break; case ConfigMergeVisitor::ItemStrategy::Override: ui->itemStrategy->setCurrentIndex(1); break; case ConfigMergeVisitor::ItemStrategy::Duplicate: ui->itemStrategy->setCurrentIndex(2); break; } switch (settings.configMergeSetStrategy()) { case ConfigMergeVisitor::SetStrategy::Ignore: ui->setStrategy->setCurrentIndex(0); break; case ConfigMergeVisitor::SetStrategy::Override: ui->setStrategy->setCurrentIndex(1); break; case ConfigMergeVisitor::SetStrategy::Duplicate: ui->setStrategy->setCurrentIndex(2); break; case ConfigMergeVisitor::SetStrategy::Merge: ui->setStrategy->setCurrentIndex(3); break; } onItemStrategySelected(ui->itemStrategy->currentIndex()); onSetStrategySelected(ui->setStrategy->currentIndex()); connect(ui->itemStrategy, SIGNAL(currentIndexChanged(int)), this, SLOT(onItemStrategySelected(int))); connect(ui->setStrategy, SIGNAL(currentIndexChanged(int)), this, SLOT(onSetStrategySelected(int))); connect(ui->buttonBox, SIGNAL(accepted()), SLOT(accept())); connect(ui->buttonBox, SIGNAL(rejected()), SLOT(reject())); } ConfigMergeDialog::~ConfigMergeDialog() { delete ui; } ConfigMergeVisitor::ItemStrategy ConfigMergeDialog::itemStrategy() const { return (ConfigMergeVisitor::ItemStrategy)ui->itemStrategy->currentData().toUInt(); } ConfigMergeVisitor::SetStrategy ConfigMergeDialog::setStrategy() const { return (ConfigMergeVisitor::SetStrategy)ui->setStrategy->currentData().toUInt(); } void ConfigMergeDialog::onItemStrategySelected(int index) { ConfigMergeVisitor::ItemStrategy strategy = (ConfigMergeVisitor::ItemStrategy)ui->itemStrategy->itemData(index).toUInt(); Settings().setConfigMergeItemStrategy(strategy); switch (strategy) { case ConfigMergeVisitor::ItemStrategy::Ignore: ui->itemStrategyLabel->setText(tr("Ignores any duplicate item.")); break; case ConfigMergeVisitor::ItemStrategy::Override: ui->itemStrategyLabel->setText(tr("Replaces any duplicate item with the imported one.")); break; case ConfigMergeVisitor::ItemStrategy::Duplicate: ui->itemStrategyLabel->setText(tr("Imports any duplicate item with a modified name.")); break; } } void ConfigMergeDialog::onSetStrategySelected(int index) { ConfigMergeVisitor::SetStrategy strategy = (ConfigMergeVisitor::SetStrategy)ui->setStrategy->itemData(index).toUInt(); Settings().setConfigMergeSetStrategy(strategy); switch (strategy) { case ConfigMergeVisitor::SetStrategy::Ignore: ui->setStrategyLabel->setText(tr("Ignores any duplicate set.")); break; case ConfigMergeVisitor::SetStrategy::Override: ui->setStrategyLabel->setText(tr("Replaces any duplicate set with the imported one.")); break; case ConfigMergeVisitor::SetStrategy::Duplicate: ui->setStrategyLabel->setText(tr("Imports any duplicate set with a modified name.")); break; case ConfigMergeVisitor::SetStrategy::Merge: ui->setStrategyLabel->setText(tr("Merges duplicate sets.")); break; } } qdmr-0.12.3/src/configmergedialog.hh000066400000000000000000000011071501654372000173170ustar00rootroot00000000000000#ifndef CONFIGMERGEDIALOG_HH #define CONFIGMERGEDIALOG_HH #include #include "configmergevisitor.hh" namespace Ui { class ConfigMergeDialog; } class ConfigMergeDialog : public QDialog { Q_OBJECT public: explicit ConfigMergeDialog(QWidget *parent = nullptr); ~ConfigMergeDialog(); ConfigMergeVisitor::ItemStrategy itemStrategy() const; ConfigMergeVisitor::SetStrategy setStrategy() const; private slots: void onItemStrategySelected(int idx); void onSetStrategySelected(int idx); private: Ui::ConfigMergeDialog *ui; }; #endif // CONFIGMERGEDIALOG_HH qdmr-0.12.3/src/configmergedialog.ui000066400000000000000000000126101501654372000173360ustar00rootroot00000000000000 ConfigMergeDialog Qt::NonModal 0 0 487 342 Merging codeplugs ... true 0 0 <html><head/><body><p><span style=" font-weight:600;">Conflict resolution strategies:</span></p><p>If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.</p></body></html> true 0 0 Items are all atomic objects like radio IDs, channels, contacts and roaming channels. Items Ignore Override Duplicate 0 0 true 0 0 Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones. Sets Ignore Override Duplicate Merge 0 0 true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ConfigMergeDialog accept() 248 254 157 274 buttonBox rejected() ConfigMergeDialog reject() 316 260 286 274 qdmr-0.12.3/src/configobjectlistview.cc000066400000000000000000000157501501654372000200740ustar00rootroot00000000000000#include "configobjectlistview.hh" #include "ui_configobjectlistview.h" #include "searchpopup.hh" #include inline QPair getSelectionRowRange(const QModelIndexList &indices) { int rmin=-1, rmax=-1; foreach (QModelIndex idx, indices) { if ((-1==rmin) || (rmin>idx.row())) rmin = idx.row(); if ((-1==rmax) || (rmax(rmin, rmax); } ConfigObjectListView::ConfigObjectListView(QWidget *parent) : QWidget(parent), ui(new Ui::ConfigObjectListView) { ui->setupUi(this); connect(ui->itemTop, SIGNAL(clicked(bool)), this, SLOT(onMoveItemTop())); connect(ui->itemTenUp, SIGNAL(clicked(bool)), this, SLOT(onMoveItemTenUp())); connect(ui->itemUp, SIGNAL(clicked(bool)), this, SLOT(onMoveItemUp())); connect(ui->itemDown, SIGNAL(clicked(bool)), this, SLOT(onMoveItemDown())); connect(ui->itemTenDown, SIGNAL(clicked(bool)), this, SLOT(onMoveItemTenDown())); connect(ui->itemBottom, SIGNAL(clicked(bool)), this, SLOT(onMoveItemBottom())); connect(ui->listView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onDoubleClicked(QModelIndex))); ui->listView->setSelectionMode(QAbstractItemView::ContiguousSelection); ui->listView->setSelectionBehavior(QAbstractItemView::SelectRows); ui->listView->setDragEnabled(true); ui->listView->viewport()->setAcceptDrops(true); ui->listView->setDropIndicatorShown(true); ui->listView->setDragDropMode(QAbstractItemView::InternalMove); SearchPopup::attach(ui->listView); } ConfigObjectListView::~ConfigObjectListView() { delete ui; } GenericListWrapper * ConfigObjectListView::model() const { return qobject_cast(ui->listView->model()); } void ConfigObjectListView::setModel(GenericListWrapper *model) { ui->listView->setModel(model); } bool ConfigObjectListView::hasSelection() const { return ui->listView->selectionModel()->hasSelection(); } QPair ConfigObjectListView::selection() const { return getSelectionRowRange(ui->listView->selectionModel()->selection().indexes()); } void ConfigObjectListView::onMoveItemUp() { // Check if there is a selection if (! ui->listView->selectionModel()->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot move items."), tr("Cannot move items: You have to select at least one item first.")); return; } // Get selection range assuming only continuous selection mode QPair rows = getSelectionRowRange( ui->listView->selectionModel()->selection().indexes()); // If selection range is invalud or I cannot move at all: done. if ((0>=rows.first) || (0>rows.second)) return; // Then move rows model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1), QModelIndex(), std::max(0, rows.first-1)); } void ConfigObjectListView::onMoveItemTenUp() { // Check if there is a selection if (! ui->listView->selectionModel()->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot move items."), tr("Cannot move items: You have to select at least one item first.")); return; } // Get selection range assuming only continuous selection mode QPair rows = getSelectionRowRange( ui->listView->selectionModel()->selection().indexes()); // If selection range is invalud or I cannot move at all: done. if ((0>=rows.first) || (0>rows.second)) return; // Then move rows int dest = std::max(0, rows.first-9); model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1), QModelIndex(), dest); } void ConfigObjectListView::onMoveItemTop() { // Check if there is a selection if (! ui->listView->selectionModel()->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot move items."), tr("Cannot move items: You have to select at least one item first.")); return; } // Get selection range assuming only continuous selection mode QPair rows = getSelectionRowRange( ui->listView->selectionModel()->selection().indexes()); // If selection range is invalud or I cannot move at all: done. if ((0>=rows.first) || (0>rows.second)) return; // Then move rows model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1), QModelIndex(), 0); } void ConfigObjectListView::onMoveItemDown() { // Check if there is a selection if (! ui->listView->selectionModel()->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot move items."), tr("Cannot move items: You have to select at least one item first.")); return; } // Get selection range assuming only continuous selection mode QPair rows = getSelectionRowRange( ui->listView->selectionModel()->selection().indexes()); // If selection range is invalud or I cannot move at all: done. if ((0>rows.first) || (0>rows.second) || ((rows.second+1)>=model()->rowCount(QModelIndex()))) return; // Then move rows model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1), QModelIndex(), std::min(model()->rowCount(QModelIndex()), rows.second+2)); } void ConfigObjectListView::onMoveItemTenDown() { // Check if there is a selection if (! ui->listView->selectionModel()->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot move items."), tr("Cannot move items: You have to select at least one item first.")); return; } // Get selection range assuming only continuous selection mode QPair rows = getSelectionRowRange( ui->listView->selectionModel()->selection().indexes()); // If selection range is invalud or I cannot move at all: done. if ((0>rows.first) || (0>rows.second) || ((rows.second+1)>=model()->rowCount(QModelIndex()))) return; // Then move rows model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1), QModelIndex(), std::min(rows.second+10, model()->rowCount(QModelIndex()))); } void ConfigObjectListView::onMoveItemBottom() { // Check if there is a selection if (! ui->listView->selectionModel()->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot move items."), tr("Cannot move items: You have to select at least one item first.")); return; } // Get selection range assuming only continuous selection mode QPair rows = getSelectionRowRange( ui->listView->selectionModel()->selection().indexes()); // If selection range is invalud or I cannot move at all: done. if ((0>rows.first) || (0>rows.second) || ((rows.second+1)>=model()->rowCount(QModelIndex()))) return; // Then move rows model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1), QModelIndex(), model()->rowCount(QModelIndex())); } void ConfigObjectListView::onDoubleClicked(QModelIndex idx) { if ((0 > idx.row()) || (idx.row() >= model()->rowCount(QModelIndex()))) return; emit doubleClicked(idx.row()); } qdmr-0.12.3/src/configobjectlistview.hh000066400000000000000000000014431501654372000201000ustar00rootroot00000000000000#ifndef CONFIGOBJECTLISTVIEW_H #define CONFIGOBJECTLISTVIEW_H #include #include "configitemwrapper.hh" namespace Ui { class ConfigObjectListView; } class ConfigObjectListView : public QWidget { Q_OBJECT public: explicit ConfigObjectListView(QWidget *parent = nullptr); ~ConfigObjectListView(); GenericListWrapper *model() const; void setModel(GenericListWrapper *model); bool hasSelection() const; QPair selection() const; signals: void doubleClicked(unsigned row); protected slots: void onMoveItemUp(); void onMoveItemTenUp(); void onMoveItemTop(); void onMoveItemDown(); void onMoveItemTenDown(); void onMoveItemBottom(); void onDoubleClicked(QModelIndex idx); private: Ui::ConfigObjectListView *ui; }; #endif // CONFIGOBJECTLISTVIEW_H qdmr-0.12.3/src/configobjectlistview.ui000066400000000000000000000076041501654372000201230ustar00rootroot00000000000000 ConfigObjectListView 0 0 400 300 Form Move selected item(s) to the top. .. 16 16 Move selected item(s) ten positions up. .. 16 16 Move selected item(s) one position up. .. 16 16 Move selected item(s) one position down. .. 16 16 Move selected item(s) ten positions down. .. 16 16 Move selected item(s) to the bottom. .. 16 16 qdmr-0.12.3/src/configobjecttableview.cc000066400000000000000000000163541501654372000202110ustar00rootroot00000000000000#include "configobjecttableview.hh" #include "ui_configobjecttableview.h" #include "searchpopup.hh" #include inline QPair getSelectionRowRange(const QModelIndexList &indices) { int rmin=-1, rmax=-1; foreach (QModelIndex idx, indices) { if ((-1==rmin) || (rmin>idx.row())) rmin = idx.row(); if ((-1==rmax) || (rmax(rmin, rmax); } ConfigObjectTableView::ConfigObjectTableView(QWidget *parent) : QWidget(parent), ui(new Ui::ConfigObjectTableView) { ui->setupUi(this); connect(ui->itemTop, SIGNAL(clicked(bool)), this, SLOT(onMoveItemTop())); connect(ui->itemTenUp, SIGNAL(clicked(bool)), this, SLOT(onMoveItemTenUp())); connect(ui->itemUp, SIGNAL(clicked(bool)), this, SLOT(onMoveItemUp())); connect(ui->itemDown, SIGNAL(clicked(bool)), this, SLOT(onMoveItemDown())); connect(ui->itemTenDown, SIGNAL(clicked(bool)), this, SLOT(onMoveItemTenDown())); connect(ui->itemBottom, SIGNAL(clicked(bool)), this, SLOT(onMoveItemBottom())); connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onDoubleClicked(QModelIndex))); ui->tableView->setSelectionMode(QAbstractItemView::ContiguousSelection); ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows); ui->tableView->setDragEnabled(true); ui->tableView->viewport()->setAcceptDrops(true); ui->tableView->setDropIndicatorShown(true); ui->tableView->setDefaultDropAction(Qt::MoveAction); ui->tableView->setDragDropMode(QAbstractItemView::InternalMove); ui->tableView->setDragDropOverwriteMode(false); SearchPopup::attach(ui->tableView); } ConfigObjectTableView::~ConfigObjectTableView() { delete ui; } GenericTableWrapper * ConfigObjectTableView::model() const { return qobject_cast(ui->tableView->model()); } void ConfigObjectTableView::setModel(GenericTableWrapper *model) { ui->tableView->setModel(model); } bool ConfigObjectTableView::hasSelection() const { return ui->tableView->selectionModel()->hasSelection(); } QPair ConfigObjectTableView::selection() const { return getSelectionRowRange(ui->tableView->selectionModel()->selection().indexes()); } QHeaderView * ConfigObjectTableView::header() const { return ui->tableView->horizontalHeader(); } void ConfigObjectTableView::onMoveItemUp() { // Check if there is a selection if (! ui->tableView->selectionModel()->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot move items."), tr("Cannot move items: You have to select at least one item first.")); return; } // Get selection range assuming only continuous selection mode QPair rows = getSelectionRowRange( ui->tableView->selectionModel()->selection().indexes()); // If selection range is invalud or I cannot move at all: done. if ((0>=rows.first) || (0>rows.second)) return; // Then move rows model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1), QModelIndex(), std::max(0, rows.first-1)); } void ConfigObjectTableView::onMoveItemTenUp() { // Check if there is a selection if (! ui->tableView->selectionModel()->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot move items."), tr("Cannot move items: You have to select at least one item first.")); return; } // Get selection range assuming only continuous selection mode QPair rows = getSelectionRowRange( ui->tableView->selectionModel()->selection().indexes()); // If selection range is invalud or I cannot move at all: done. if ((0>=rows.first) || (0>rows.second)) return; // Then move rows int dest = std::max(0, rows.first-9); model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1), QModelIndex(), dest); } void ConfigObjectTableView::onMoveItemTop() { // Check if there is a selection if (! ui->tableView->selectionModel()->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot move items."), tr("Cannot move items: You have to select at least one item first.")); return; } // Get selection range assuming only continuous selection mode QPair rows = getSelectionRowRange( ui->tableView->selectionModel()->selection().indexes()); // If selection range is invalud or I cannot move at all: done. if ((0>=rows.first) || (0>rows.second)) return; // Then move rows model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1), QModelIndex(), 0); } void ConfigObjectTableView::onMoveItemDown() { // Check if there is a selection if (! ui->tableView->selectionModel()->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot move items."), tr("Cannot move items: You have to select at least one item first.")); return; } // Get selection range assuming only continuous selection mode QPair rows = getSelectionRowRange( ui->tableView->selectionModel()->selection().indexes()); // If selection range is invalud or I cannot move at all: done. if ((0>rows.first) || (0>rows.second) || ((rows.second+1)>=model()->rowCount(QModelIndex()))) return; // Then move rows model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1), QModelIndex(), std::min(model()->rowCount(QModelIndex()), rows.second+2)); } void ConfigObjectTableView::onMoveItemTenDown() { // Check if there is a selection if (! ui->tableView->selectionModel()->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot move items."), tr("Cannot move items: You have to select at least one item first.")); return; } // Get selection range assuming only continuous selection mode QPair rows = getSelectionRowRange( ui->tableView->selectionModel()->selection().indexes()); // If selection range is invalud or I cannot move at all: done. if ((0>rows.first) || (0>rows.second) || ((rows.second+1)>=model()->rowCount(QModelIndex()))) return; // Then move rows model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1), QModelIndex(), std::min(rows.second+10, model()->rowCount(QModelIndex()))); } void ConfigObjectTableView::onMoveItemBottom() { // Check if there is a selection if (! ui->tableView->selectionModel()->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot move items."), tr("Cannot move items: You have to select at least one item first.")); return; } // Get selection range assuming only continuous selection mode QPair rows = getSelectionRowRange( ui->tableView->selectionModel()->selection().indexes()); // If selection range is invalud or I cannot move at all: done. if ((0>rows.first) || (0>rows.second) || ((rows.second+1)>=model()->rowCount(QModelIndex()))) return; // Then move rows model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1), QModelIndex(), model()->rowCount(QModelIndex())); } void ConfigObjectTableView::onDoubleClicked(QModelIndex idx) { if ((0 > idx.row()) || (idx.row() >= model()->rowCount(QModelIndex()))) return; emit doubleClicked(idx.row()); } qdmr-0.12.3/src/configobjecttableview.hh000066400000000000000000000015411501654372000202130ustar00rootroot00000000000000#ifndef CONFIGOBJECTTABLEVIEW_HH #define CONFIGOBJECTTABLEVIEW_HH #include #include "configitemwrapper.hh" class QHeaderView; namespace Ui { class ConfigObjectTableView; } class ConfigObjectTableView : public QWidget { Q_OBJECT public: explicit ConfigObjectTableView(QWidget *parent = nullptr); ~ConfigObjectTableView(); GenericTableWrapper *model() const; void setModel(GenericTableWrapper *model); bool hasSelection() const; QPair selection() const; QHeaderView *header() const; signals: void doubleClicked(unsigned row); protected slots: void onMoveItemUp(); void onMoveItemTenUp(); void onMoveItemTop(); void onMoveItemDown(); void onMoveItemTenDown(); void onMoveItemBottom(); void onDoubleClicked(QModelIndex idx); private: Ui::ConfigObjectTableView *ui; }; #endif // CONFIGOBJECTTABLEVIEW_HH qdmr-0.12.3/src/configobjecttableview.ui000066400000000000000000000060501501654372000202310ustar00rootroot00000000000000 ConfigObjectTableView 0 0 400 300 Form Move selected item(s) to the top. .. Move selected item(s) ten positions up. .. Move selected item(s) one position up. .. Move selected item(s) one position down. .. Move selected item(s) ten positions down. .. Move selected item(s) to the bottom. .. qdmr-0.12.3/src/configobjecttypeselectiondialog.cc000066400000000000000000000033761501654372000222760ustar00rootroot00000000000000#include "configobjecttypeselectiondialog.hh" #include "ui_configobjecttypeselectiondialog.h" #include "logger.hh" #include ConfigObjectTypeSelectionDialog::ConfigObjectTypeSelectionDialog( const QList &cls, QWidget *parent) : QDialog(parent), ui(new Ui::ConfigObjectTypeSelectionDialog), _types(cls) { ui->setupUi(this); // Iterate over all classes foreach (const QMetaObject type, _types) { logDebug() << "Inspect class '" << type.className() << "'."; ui->typeSelection->addItem(type.className()); } if (0 <= ui->typeSelection->currentIndex()) onSelectionChanged(ui->typeSelection->currentIndex()); ui->description->setWordWrap(true); ui->description->setTextFormat(Qt::RichText); connect(ui->typeSelection, SIGNAL(currentIndexChanged(int)), this, SLOT(onSelectionChanged(int))); } ConfigObjectTypeSelectionDialog::~ConfigObjectTypeSelectionDialog() { delete ui; } const QMetaObject & ConfigObjectTypeSelectionDialog::selectedType() const { return _types.at(ui->typeSelection->currentIndex()); } void ConfigObjectTypeSelectionDialog::onSelectionChanged(int currentIndex) { ui->description->clear(); if (_types.count() <= currentIndex) return; QMetaObject meta = _types.at(currentIndex); QString description = tr("An instance of %1.").arg(meta.className()); QString longDescription; for (int i=meta.classInfoOffset(); idescription->setText(tr("

      %1

      %2

      ").arg(description).arg(longDescription)); } qdmr-0.12.3/src/configobjecttypeselectiondialog.hh000066400000000000000000000011551501654372000223010ustar00rootroot00000000000000#ifndef CONFIGOBJECTTYPESELECTIONDIALOG_HH #define CONFIGOBJECTTYPESELECTIONDIALOG_HH #include namespace Ui { class ConfigObjectTypeSelectionDialog; } class ConfigObjectTypeSelectionDialog : public QDialog { Q_OBJECT public: explicit ConfigObjectTypeSelectionDialog(const QList &cls, QWidget *parent = nullptr); ~ConfigObjectTypeSelectionDialog(); const QMetaObject &selectedType() const; protected slots: void onSelectionChanged(int currentIndex); private: Ui::ConfigObjectTypeSelectionDialog *ui; QList _types; }; #endif // CONFIGOBJECTTYPESELECTIONDIALOG_HH qdmr-0.12.3/src/configobjecttypeselectiondialog.ui000066400000000000000000000044061501654372000223210ustar00rootroot00000000000000 ConfigObjectTypeSelectionDialog 0 0 400 184 0 0 Create extension object Select the class of object to create 0 0 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ConfigObjectTypeSelectionDialog accept() 248 254 157 274 buttonBox rejected() ConfigObjectTypeSelectionDialog reject() 316 260 286 274 qdmr-0.12.3/src/contactlistview.cc000066400000000000000000000073761501654372000171000ustar00rootroot00000000000000#include "contactlistview.hh" #include "ui_contactlistview.h" #include "dmrcontactdialog.hh" #include "dtmfcontactdialog.hh" #include "application.hh" #include "settings.hh" #include #include ContactListView::ContactListView(Config *config, QWidget *parent) : QWidget(parent), ui(new Ui::ContactListView), _config(config) { ui->setupUi(this); connect(ui->listView->header(), SIGNAL(sectionCountChanged(int,int)), this, SLOT(loadHeaderState())); connect(ui->listView->header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(storeHeaderState())); ui->listView->setModel(new ContactListWrapper(_config->contacts(), ui->listView)); connect(ui->addDMRContact, SIGNAL(clicked()), this, SLOT(onAddDMRContact())); connect(ui->addDTMFContact, SIGNAL(clicked()), this, SLOT(onAddDTMFContact())); connect(ui->remContact, SIGNAL(clicked()), this, SLOT(onRemContact())); connect(ui->listView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditContact(unsigned))); } ContactListView::~ContactListView() { delete ui; } void ContactListView::onAddDMRContact() { Application *app = qobject_cast(QApplication::instance()); DMRContactDialog dialog(app->user(), app->talkgroup(), _config); if (QDialog::Accepted != dialog.exec()) return; int row=-1; if (ui->listView->hasSelection()) row = ui->listView->selection().second+1; _config->contacts()->add(dialog.contact(), row); } void ContactListView::onAddDTMFContact() { DTMFContactDialog dialog(_config); if (QDialog::Accepted != dialog.exec()) return; int row=-1; if (ui->listView->hasSelection()) row = ui->listView->selection().second+1; _config->contacts()->add(dialog.contact(), row); } void ContactListView::onRemContact() { // Check if there is any contacts selected if (! ui->listView->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot delete contact"), tr("Cannot delete contact: You have to select a contact first.")); return; } // Get selection and ask for deletion QPair rows = ui->listView->selection(); int numrows = rows.second-rows.first+1; if (rows.first == rows.second) { QString name = _config->contacts()->contact(rows.first)->name(); if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete contact?"), tr("Delete contact %1?").arg(name))) return; } else { if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete contacts?"), tr("Delete %1 contacts?").arg(numrows))) return; } // collect all selected contacts // need to collect them first as rows change when deleting contacts QList contacts; contacts.reserve(numrows); for (int i=rows.first; i<=rows.second; i++) contacts.push_back(_config->contacts()->contact(i)); // remove contacts foreach (Contact *contact, contacts) _config->contacts()->del(contact); } void ContactListView::onEditContact(unsigned row) { Application *app = qobject_cast(QApplication::instance()); Contact *contact = _config->contacts()->contact(row); if (DMRContact *digi = contact->as()) { DMRContactDialog dialog(digi, app->user(), app->talkgroup(), _config); if (QDialog::Accepted != dialog.exec()) return; dialog.contact(); } else if (DTMFContact *dtmf = contact->as()) { DTMFContactDialog dialog(dtmf, _config); if (QDialog::Accepted != dialog.exec()) return; dialog.contact(); } } void ContactListView::loadHeaderState() { Settings settings; ui->listView->header()->restoreState(settings.headerState("contactList")); } void ContactListView::storeHeaderState() { Settings settings; settings.setHeaderState("contactList", ui->listView->header()->saveState()); } qdmr-0.12.3/src/contactlistview.hh000066400000000000000000000010571501654372000171000ustar00rootroot00000000000000#ifndef CONTACTLISTVIEW_HH #define CONTACTLISTVIEW_HH #include class Config; namespace Ui { class ContactListView; } class ContactListView : public QWidget { Q_OBJECT public: explicit ContactListView(Config *config, QWidget *parent = nullptr); ~ContactListView(); protected slots: void onAddDMRContact(); void onAddDTMFContact(); void onRemContact(); void onEditContact(unsigned row); void loadHeaderState(); void storeHeaderState(); private: Ui::ContactListView *ui; Config *_config; }; #endif // CONTACTLISTVIEW_HH qdmr-0.12.3/src/contactlistview.ui000066400000000000000000000037661501654372000171270ustar00rootroot00000000000000 ContactListView 0 0 492 306 Form 0 0 Adds a contact to the list. Add Digital Contact Alt++ Add DTMF Contact Delete contact button Delete Contact Alt+- ConfigObjectTableView QWidget
      configobjecttableview.hh
      1
      qdmr-0.12.3/src/contactselectiondialog.cc000066400000000000000000000047721501654372000203740ustar00rootroot00000000000000#include "contactselectiondialog.hh" #include "contact.hh" #include #include #include #include #include /* ********************************************************************************************* * * Implementation of MultiGroupCallSelectionDialog * ********************************************************************************************* */ MultiGroupCallSelectionDialog::MultiGroupCallSelectionDialog(ContactList *contacts, bool showPrivateCalls, QWidget *parent) : QDialog(parent) { _contacts = new QListWidget(); QCheckBox *showPrivCall = new QCheckBox(tr("Show private calls")); showPrivCall->setChecked(showPrivateCalls); for (int i=0; icount(); i++) { Contact *contact = contacts->contact(i); if (! contact->is()) continue; DMRContact *digi = contact->as(); QListWidgetItem *item = new QListWidgetItem(digi->name()); item->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled); item->setData(Qt::UserRole, QVariant::fromValue(digi)); item->setCheckState(Qt::Unchecked); _contacts->addItem(item); // Hide private calls if showPrivateCall is false (default) item->setHidden((DMRContact::PrivateCall == digi->type()) && (! showPrivateCalls)); } QDialogButtonBox *bbox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); connect(bbox, SIGNAL(accepted()), this, SLOT(accept())); connect(bbox, SIGNAL(rejected()), this, SLOT(reject())); connect(showPrivCall, SIGNAL(toggled(bool)), this, SLOT(showPrivateCallsToggled(bool))); _label = new QLabel(tr("Select a group call:")); QVBoxLayout *layout = new QVBoxLayout(); layout->addWidget(_label); layout->addWidget(_contacts); layout->addWidget(showPrivCall); layout->addWidget(bbox); setLayout(layout); } void MultiGroupCallSelectionDialog::setLabel(const QString &text) { _label->setText(text); } QList MultiGroupCallSelectionDialog::contacts() { QList contacts; for (int i=0; i<_contacts->count(); i++) { if (Qt::Checked == _contacts->item(i)->checkState()) contacts.push_back(_contacts->item(i)->data(Qt::UserRole).value()); } return contacts; } void MultiGroupCallSelectionDialog::showPrivateCallsToggled(bool show) { for (int i=0; i<_contacts->count(); i++) { if (DMRContact::PrivateCall == _contacts->item(i)->data(Qt::UserRole).value()->type()) _contacts->item(i)->setHidden(! show); } } qdmr-0.12.3/src/contactselectiondialog.hh000066400000000000000000000011321501654372000203710ustar00rootroot00000000000000#ifndef CONTACTSELECTIONDIALOG_HH #define CONTACTSELECTIONDIALOG_HH #include #include class DMRContact; class ContactList; class QListWidget; class QLabel; class MultiGroupCallSelectionDialog: public QDialog { Q_OBJECT public: explicit MultiGroupCallSelectionDialog(ContactList *contacts, bool showPrivateCalls=false, QWidget *parent=nullptr); QList contacts(); void setLabel(const QString &text); protected slots: void showPrivateCallsToggled(bool show); protected: QLabel *_label; QListWidget *_contacts; }; #endif // CONTACTSELECTIONDIALOG_HH qdmr-0.12.3/src/deviceselectiondialog.cc000066400000000000000000000021651501654372000201720ustar00rootroot00000000000000#include "deviceselectiondialog.hh" #include "ui_deviceselectiondialog.h" DeviceSelectionDialog::DeviceSelectionDialog(const QList &interfaces, QWidget *parent) : QDialog(parent), ui(new Ui::DeviceSelectionDialog), _interfaces(interfaces) { ui->setupUi(this); // Populate combo box foreach (USBDeviceDescriptor dev, _interfaces) { ui->comboBox->addItem(dev.description()); } connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onDeviceSelected(int))); // Select first device if (_interfaces.count()) ui->comboBox->setCurrentIndex(0); connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); } DeviceSelectionDialog::~DeviceSelectionDialog() { delete ui; } USBDeviceDescriptor DeviceSelectionDialog::device() const { return _interfaces.at(ui->comboBox->currentIndex()); } void DeviceSelectionDialog::onDeviceSelected(int idx) { // Update description. USBDeviceDescriptor device = _interfaces.at(idx); ui->description->setText(device.description() + ". " + device.longDescription()); } qdmr-0.12.3/src/deviceselectiondialog.hh000066400000000000000000000011101501654372000201710ustar00rootroot00000000000000#ifndef DEVICESELECTIONDIALOG_HH #define DEVICESELECTIONDIALOG_HH #include #include "usbdevice.hh" namespace Ui { class DeviceSelectionDialog; } class DeviceSelectionDialog : public QDialog { Q_OBJECT public: explicit DeviceSelectionDialog(const QList &interfaces, QWidget *parent = nullptr); ~DeviceSelectionDialog(); USBDeviceDescriptor device() const; protected slots: void onDeviceSelected(int idx); private: Ui::DeviceSelectionDialog *ui; const QList &_interfaces; }; #endif // DEVICESELECTIONDIALOG_HH qdmr-0.12.3/src/deviceselectiondialog.ui000066400000000000000000000046071501654372000202250ustar00rootroot00000000000000 DeviceSelectionDialog 0 0 400 300 Select a device <html><head/><body><p>There is either more than one device detected or the one found is not considered save to access. Either way, select the device to use.</p></body></html> true 0 0 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DeviceSelectionDialog accept() 248 254 157 274 buttonBox rejected() DeviceSelectionDialog reject() 316 260 286 274 qdmr-0.12.3/src/digitalchanneldialog.cc000066400000000000000000000231151501654372000177710ustar00rootroot00000000000000#include "digitalchanneldialog.hh" #include "application.hh" #include #include "rxgrouplistdialog.hh" #include "repeatercompleter.hh" #include "repeaterdatabase.hh" #include "extensionwrapper.hh" #include "propertydelegate.hh" #include "settings.hh" #include "utils.hh" #include "logger.hh" /* ********************************************************************************************* * * Implementation of DigitalChannelDialog * ********************************************************************************************* */ DigitalChannelDialog::DigitalChannelDialog(Config *config, QWidget *parent) : QDialog(parent), _config(config), _myChannel(new DMRChannel(this)), _channel(nullptr) { construct(); } DigitalChannelDialog::DigitalChannelDialog(Config *config, DMRChannel *channel, QWidget *parent) : QDialog(parent), _config(config), _myChannel(nullptr), _channel(channel) { if (_channel) { _myChannel = _channel->clone()->as(); _myChannel->setParent(parent); } construct(); } void DigitalChannelDialog::construct() { setupUi(this); Settings settings; if (settings.hideChannelNote()) hintLabel->setVisible(false); Application *app = qobject_cast(qApp); DMRRepeaterFilter *filter = new DMRRepeaterFilter(app->repeater(), app->position(), this); filter->setSourceModel(app->repeater()); QCompleter *completer = new RepeaterCompleter(2, app->repeater(), this); completer->setModel(filter); channelName->setCompleter(completer); connect(completer, SIGNAL(activated(const QModelIndex &)), this, SLOT(onRepeaterSelected(const QModelIndex &))); rxFrequency->setValidator(new QDoubleValidator(0,500,5)); txFrequency->setValidator(new QDoubleValidator(0,500,5)); powerValue->setItemData(0, unsigned(Channel::Power::Max)); powerValue->setItemData(1, unsigned(Channel::Power::High)); powerValue->setItemData(2, unsigned(Channel::Power::Mid)); powerValue->setItemData(3, unsigned(Channel::Power::Low)); powerValue->setItemData(4, unsigned(Channel::Power::Min)); powerDefault->setChecked(true); powerValue->setCurrentIndex(1); powerValue->setEnabled(false); totDefault->setChecked(true); totValue->setValue(0); totValue->setEnabled(false); scanList->addItem(tr("[None]"), QVariant::fromValue((ScanList *)(nullptr))); scanList->setCurrentIndex(0); for (int i=0; i<_config->scanlists()->count(); i++) { scanList->addItem(_config->scanlists()->scanlist(i)->name(), QVariant::fromValue(_config->scanlists()->scanlist(i))); if (_myChannel && (_myChannel->scanList() == _config->scanlists()->scanlist(i)) ) scanList->setCurrentIndex(i+1); } txAdmit->setItemData(0, unsigned(DMRChannel::Admit::Always)); txAdmit->setItemData(1, unsigned(DMRChannel::Admit::Free)); txAdmit->setItemData(2, unsigned(DMRChannel::Admit::ColorCode)); timeSlot->setItemData(0, unsigned(DMRChannel::TimeSlot::TS1)); timeSlot->setItemData(1, unsigned(DMRChannel::TimeSlot::TS2)); populateRXGroupListBox(rxGroupList, _config->rxGroupLists(), (nullptr != _myChannel ? _myChannel->groupListObj() : nullptr)); txContact->addItem(tr("[None]"), QVariant::fromValue(nullptr)); if (_myChannel && (nullptr == _myChannel->txContactObj())) txContact->setCurrentIndex(0); for (int i=0; i<_config->contacts()->count(); i++) { txContact->addItem(_config->contacts()->contact(i)->name(), QVariant::fromValue(_config->contacts()->contact(i))); if (_myChannel && (_myChannel->txContactObj() == _config->contacts()->contact(i)) ) txContact->setCurrentIndex(i+1); } gpsSystem->addItem(tr("[None]"), QVariant::fromValue((GPSSystem *)nullptr)); for (int i=0; i<_config->posSystems()->count(); i++) { PositioningSystem *sys = _config->posSystems()->system(i); gpsSystem->addItem(sys->name(), QVariant::fromValue(sys)); if (_myChannel && (_myChannel->aprsObj() == sys)) gpsSystem->setCurrentIndex(i+1); } roaming->addItem(tr("[None]"), QVariant::fromValue((RoamingZone *)nullptr)); roaming->addItem(tr("[Default]"), QVariant::fromValue(DefaultRoamingZone::get())); if (_myChannel && (_myChannel->roamingZone() == DefaultRoamingZone::get())) roaming->setCurrentIndex(1); for (int i=0; i<_config->roamingZones()->count(); i++) { RoamingZone *zone = _config->roamingZones()->zone(i); roaming->addItem(zone->name(), QVariant::fromValue(zone)); if (_myChannel && (_myChannel->roamingZone() == zone)) roaming->setCurrentIndex(i+2); } dmrID->addItem(tr("[Default]"), QVariant::fromValue(DefaultRadioID::get())); dmrID->setCurrentIndex(0); for (int i=0; i<_config->radioIDs()->count(); i++) { dmrID->addItem(_config->radioIDs()->getId(i)->name(), QVariant::fromValue(_config->radioIDs()->getId(i))); if (_myChannel && (_config->radioIDs()->getId(i) == _myChannel->radioIdObj())) { dmrID->setCurrentIndex(i+1); } } voxDefault->setChecked(true); voxValue->setValue(0); voxValue->setEnabled(false); channelName->setText(_myChannel->name()); rxFrequency->setText(_myChannel->rxFrequency().format(Frequency::Format::MHz)); txFrequency->setText(_myChannel->txFrequency().format(Frequency::Format::MHz)); if (! _myChannel->defaultPower()) { powerDefault->setChecked(false); powerValue->setEnabled(true); switch (_myChannel->power()) { case Channel::Power::Max: powerValue->setCurrentIndex(0); break; case Channel::Power::High: powerValue->setCurrentIndex(1); break; case Channel::Power::Mid: powerValue->setCurrentIndex(2); break; case Channel::Power::Low: powerValue->setCurrentIndex(3); break; case Channel::Power::Min: powerValue->setCurrentIndex(4); break; } } if (! _myChannel->defaultTimeout()) { totDefault->setChecked(false); totValue->setEnabled(true); totValue->setValue(_channel->timeout()); } rxOnly->setChecked(_myChannel->rxOnly()); switch (_myChannel->admit()) { case DMRChannel::Admit::Always: txAdmit->setCurrentIndex(0); break; case DMRChannel::Admit::Free: txAdmit->setCurrentIndex(1); break; case DMRChannel::Admit::ColorCode: txAdmit->setCurrentIndex(2); break; } colorCode->setValue(_myChannel->colorCode()); if (DMRChannel::TimeSlot::TS1 == _myChannel->timeSlot()) timeSlot->setCurrentIndex(0); else if (DMRChannel::TimeSlot::TS2 == _myChannel->timeSlot()) timeSlot->setCurrentIndex(1); if (! _myChannel->defaultVOX()) { voxDefault->setChecked(false); voxValue->setEnabled(true); voxValue->setValue(_channel->vox()); } extensionView->setObjectName("digitalChannelExtension"); extensionView->setObject(_myChannel, _config); if (! settings.showExtensions()) tabWidget->tabBar()->hide(); connect(powerDefault, SIGNAL(toggled(bool)), this, SLOT(onPowerDefaultToggled(bool))); connect(totDefault, SIGNAL(toggled(bool)), this, SLOT(onTimeoutDefaultToggled(bool))); connect(voxDefault, SIGNAL(toggled(bool)), this, SLOT(onVOXDefaultToggled(bool))); connect(hintLabel, SIGNAL(linkActivated(QString)), this, SLOT(onHideChannelHint())); } DMRChannel * DigitalChannelDialog::channel() { _myChannel->setRadioIdObj(dmrID->currentData().value()); _myChannel->setName(channelName->text()); _myChannel->setRXFrequency(Frequency::fromString(rxFrequency->text())); _myChannel->setTXFrequency(Frequency::fromString(txFrequency->text())); if (powerDefault->isChecked()) _myChannel->setDefaultPower(); else _myChannel->setPower(Channel::Power(powerValue->currentData().toUInt())); if (totDefault->isChecked()) _myChannel->setDefaultTimeout(); else _myChannel->setTimeout(totValue->value()); _myChannel->setRXOnly(rxOnly->isChecked()); _myChannel->setScanList(scanList->currentData().value()); _myChannel->setAdmit(DMRChannel::Admit(txAdmit->currentData().toUInt())); _myChannel->setColorCode(colorCode->value()); _myChannel->setTimeSlot(DMRChannel::TimeSlot(timeSlot->currentData().toUInt())); _myChannel->setGroupListObj(rxGroupList->currentData().value()); _myChannel->setTXContactObj(txContact->currentData().value()); _myChannel->setAPRSObj(gpsSystem->currentData().value()); _myChannel->setRoamingZone(roaming->currentData().value()); if (voxDefault->isChecked()) _myChannel->setVOXDefault(); else _myChannel->setVOX(voxValue->value()); DMRChannel *channel = _myChannel; if (nullptr == _channel) { _myChannel->setParent(nullptr); _myChannel = nullptr; } else { _channel->copy(*_myChannel); channel = _channel; } return channel; } void DigitalChannelDialog::onRepeaterSelected(const QModelIndex &index) { Application *app = qobject_cast(qApp); QModelIndex src = qobject_cast( channelName->completer()->completionModel())->mapToSource(index); src = qobject_cast( channelName->completer()->model())->mapToSource(src); Frequency rx = app->repeater()->get(src.row()).rxFrequency(); Frequency tx = app->repeater()->get(src.row()).txFrequency(); colorCode->setValue(app->repeater()->get(src.row()).colorCode()); txFrequency->setText(tx.format()); rxFrequency->setText(rx.format()); } void DigitalChannelDialog::onPowerDefaultToggled(bool checked) { powerValue->setEnabled(!checked); } void DigitalChannelDialog::onTimeoutDefaultToggled(bool checked) { totValue->setEnabled(!checked); } void DigitalChannelDialog::onVOXDefaultToggled(bool checked) { voxValue->setEnabled(! checked); } void DigitalChannelDialog::onHideChannelHint() { Settings settings; settings.setHideChannelNote(true); hintLabel->setVisible(false); } qdmr-0.12.3/src/digitalchanneldialog.hh000066400000000000000000000014451501654372000200050ustar00rootroot00000000000000#ifndef DIGITALCHANNELDIALOG_HH #define DIGITALCHANNELDIALOG_HH #include #include "config.hh" #include "ui_digitalchanneldialog.h" class DigitalChannelDialog: public QDialog, private Ui::DigitalChannelDialog { Q_OBJECT public: DigitalChannelDialog(Config *config, QWidget *parent=nullptr); DigitalChannelDialog(Config *config, DMRChannel *channel, QWidget *parent=nullptr); DMRChannel *channel(); protected slots: void onRepeaterSelected(const QModelIndex &index); void onPowerDefaultToggled(bool checked); void onTimeoutDefaultToggled(bool checked); void onVOXDefaultToggled(bool checked); void onHideChannelHint(); protected: void construct(); protected: Config *_config; DMRChannel *_myChannel; DMRChannel *_channel; }; #endif // DIGITALCHANNELDIALOG_HH qdmr-0.12.3/src/digitalchanneldialog.ui000066400000000000000000000413231501654372000200220ustar00rootroot00000000000000 DigitalChannelDialog 0 0 650 530 0 0 Edit Digital Channel padding:10px;border: 2px solid black; border-radius: 10px; <html><head/><body><p><span style=" font-weight:600;">Note:</span> qdmr provides some auto-completion for channels. That is, start typing the call-sign of a repeater. After three chars are entered, a request is sent to repeaterbook.com to retrieve matching repeaters. These requests may take some time. The results are stored locally in a cache.</p><p>A drop-down list will appear, allowing to select a repeater. Once one repeater is selected, the RX/TX frequencies and color-code are filled in.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">hide</span></a></p></body></html> Qt::RichText true 0 Basic Name 0 0 200 0 Rx Frequency 0 0 Tx Frequency 0 0 Power Tx Timeout Rx Only Scan List 0 0 0 0 Max High Mid Low Min Default 0 0 Specifies the transmit timeout in seconds. A value of 0 means that the transmit timeout is disabled. Off s 9999 Default VOX Level 0 0 Off Default Tx Admit 0 0 Always Channel Free Color Code Color Code 0 0 15 1 Time Slot 0 0 TS 1 TS 1 TS 2 Rx Group List 0 0 Tx Contact 0 0 Positioning System 0 0 Roaming Zone 0 0 DMR ID 0 0 Extended Device specific settings 0 0 0 0 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ExtensionView QWidget
      extensionview.hh
      1
      buttonBox accepted() DigitalChannelDialog accept() 248 254 157 274 buttonBox rejected() DigitalChannelDialog reject() 316 260 286 274
      qdmr-0.12.3/src/dmrcontactdialog.cc000066400000000000000000000134431501654372000171640ustar00rootroot00000000000000#include "dmrcontactdialog.hh" #include "ui_dmrcontactdialog.h" #include #include #include #include #include #include #include #include #include "contact.hh" #include "userdatabase.hh" #include "talkgroupdatabase.hh" #include "settings.hh" DMRContactDialog::DMRContactDialog(UserDatabase *users, TalkGroupDatabase *tgs, Config *context, QWidget *parent) : QDialog(parent), _myContact(new DMRContact(this)), _contact(nullptr), _user_completer(nullptr), _tg_completer(nullptr), _config(context), ui(new Ui::DMRContactDialog) { setWindowTitle(tr("Create DMR Contact")); _user_completer = new QCompleter(users, this); _user_completer->setCompletionColumn(0); _user_completer->setCaseSensitivity(Qt::CaseInsensitive); _tg_completer = new QCompleter(tgs, this); _tg_completer->setCompletionColumn(0); _tg_completer->setCaseSensitivity(Qt::CaseInsensitive); connect(_user_completer, SIGNAL(activated(QModelIndex)), this, SLOT(onCompleterActivated(QModelIndex))); connect(_tg_completer, SIGNAL(activated(QModelIndex)), this, SLOT(onCompleterActivated(QModelIndex))); construct(); } DMRContactDialog::DMRContactDialog(DMRContact *contact, UserDatabase *users, TalkGroupDatabase *tgs, Config *context, QWidget *parent) : QDialog(parent), _myContact(new DMRContact(this)), _contact(contact), _user_completer(nullptr), _tg_completer(nullptr), _config(context), ui(new Ui::DMRContactDialog) { setWindowTitle(tr("Edit DMR Contact")); _user_completer = new QCompleter(users, this); _user_completer->setCompletionColumn(0); _user_completer->setCaseSensitivity(Qt::CaseInsensitive); _tg_completer = new QCompleter(tgs, this); _tg_completer->setCompletionColumn(0); _tg_completer->setCaseSensitivity(Qt::CaseInsensitive); if (_contact) _myContact->copy(*_contact); connect(_user_completer, SIGNAL(activated(QModelIndex)), this, SLOT(onCompleterActivated(QModelIndex))); connect(_tg_completer, SIGNAL(activated(QModelIndex)), this, SLOT(onCompleterActivated(QModelIndex))); construct(); } DMRContactDialog::~DMRContactDialog() { delete ui; } void DMRContactDialog::construct() { ui->setupUi(this); Settings settings; ui->typeComboBox->addItem(tr("Private Call"), unsigned(DMRContact::PrivateCall)); ui->typeComboBox->addItem(tr("Group Call"), unsigned(DMRContact::GroupCall)); ui->typeComboBox->addItem(tr("All Call"), unsigned(DMRContact::AllCall)); if (DMRContact::PrivateCall == _myContact->type()) { ui->typeComboBox->setCurrentIndex(0); ui->nameLineEdit->setCompleter(_user_completer); } else if (DMRContact::GroupCall == _myContact->type()) { ui->typeComboBox->setCurrentIndex(1); ui->nameLineEdit->setCompleter(_tg_completer); } else { ui->typeComboBox->setCurrentIndex(2); ui->nameLineEdit->setCompleter(nullptr); ui->nameLineEdit->setEnabled(false); } ui->nameLineEdit->setText(_myContact->name()); ui->numberLineEdit->setText(QString::number(_myContact->number())); ui->numberLineEdit->setValidator(new QRegExpValidator(QRegExp("[0-9]+"))); ui->ringCheckBox->setChecked(_myContact->ring()); ui->extensionView->setObjectName("dmrContactExtension"); ui->extensionView->setObject(_myContact, _config); if (! settings.showExtensions()) ui->tabWidget->tabBar()->hide(); connect(ui->typeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onTypeChanged(int))); connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); } void DMRContactDialog::onTypeChanged(int idx) { if (0 == idx) { ui->numberLineEdit->setValidator(new QRegExpValidator(QRegExp("[0-9]+"))); ui->numberLineEdit->setEnabled(true); ui->nameLineEdit->setCompleter(_user_completer); } else if (1 == idx) { ui->numberLineEdit->setValidator(new QRegExpValidator(QRegExp("[0-9]+"))); ui->numberLineEdit->setEnabled(true); ui->nameLineEdit->setCompleter(_tg_completer); } else if (2 == idx) { ui->numberLineEdit->setText("16777215"); ui->numberLineEdit->setEnabled(false); ui->nameLineEdit->setCompleter(nullptr); } } void DMRContactDialog::onCompleterActivated(const QModelIndex &idx) { if (0 == ui->typeComboBox->currentIndex()) { // Private call if (nullptr == _user_completer) return; UserDatabase *db = qobject_cast(_user_completer->model()); if (nullptr == db) return; QAbstractProxyModel *model = qobject_cast(_user_completer->completionModel()); if (nullptr == model) return; QModelIndex srcidx = model->mapToSource(idx); ui->numberLineEdit->setText(QString::number(db->user(srcidx.row()).id)); } else if (1 == ui->typeComboBox->currentIndex()) { // Group call if (nullptr == _tg_completer) return; TalkGroupDatabase *db = qobject_cast(_tg_completer->model()); if (nullptr == db) return; QAbstractProxyModel *model = qobject_cast(_tg_completer->completionModel()); if (nullptr == model) return; QModelIndex srcidx = model->mapToSource(idx); ui->numberLineEdit->setText(QString::number(db->talkgroup(srcidx.row()).id)); } } DMRContact * DMRContactDialog::contact() { _myContact->setType(DMRContact::Type(ui->typeComboBox->currentData().toUInt())); _myContact->setName(ui->nameLineEdit->text().simplified()); _myContact->setNumber(ui->numberLineEdit->text().toUInt()); _myContact->setRing(ui->ringCheckBox->isChecked()); DMRContact *contact = _myContact; if (_contact) { _contact->copy(*_myContact); contact = _contact; } else { _myContact->setParent(nullptr); } return contact; } qdmr-0.12.3/src/dmrcontactdialog.hh000066400000000000000000000016341501654372000171750ustar00rootroot00000000000000#ifndef DMRCONTACTDIALOG_HH #define DMRCONTACTDIALOG_HH #include namespace Ui { class DMRContactDialog; } class QCompleter; class UserDatabase; class TalkGroupDatabase; class DMRContact; class Config; class DMRContactDialog : public QDialog { Q_OBJECT public: explicit DMRContactDialog(UserDatabase *users, TalkGroupDatabase *tgs, Config *context, QWidget *parent=nullptr); explicit DMRContactDialog(DMRContact *contact, UserDatabase *users, TalkGroupDatabase *tgs, Config *context, QWidget *parent=nullptr); ~DMRContactDialog(); public: DMRContact *contact(); protected slots: void onTypeChanged(int idx); void onCompleterActivated(const QModelIndex &idx); protected: void construct(); private: DMRContact *_myContact; DMRContact *_contact; QCompleter *_user_completer; QCompleter *_tg_completer; Config *_config; Ui::DMRContactDialog *ui; }; #endif // DMRCONTACTDIALOG_HH qdmr-0.12.3/src/dmrcontactdialog.ui000066400000000000000000000074061501654372000172160ustar00rootroot00000000000000 DMRContactDialog 0 0 392 296 0 0 Dialog 0 Basic Type 0 0 Name Number Ring Extensions Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ExtensionView QWidget
      extensionview.hh
      1
      buttonBox accepted() DMRContactDialog accept() 248 254 157 274 buttonBox rejected() DMRContactDialog reject() 316 260 286 274
      qdmr-0.12.3/src/dmriddialog.cc000066400000000000000000000024341501654372000161230ustar00rootroot00000000000000#include "dmriddialog.hh" #include "ui_dmriddialog.h" #include "settings.hh" #include DMRIDDialog::DMRIDDialog(DMRRadioID *radioid, Config *context, QWidget *parent) : QDialog(parent), ui(new Ui::DMRIDDialog), _myID(new DMRRadioID(this)), _editID(radioid), _config(context) { ui->setupUi(this); _myID->copy(*_editID); construct(); } DMRIDDialog::DMRIDDialog(Config *context, QWidget *parent) : QDialog(parent), ui(new Ui::DMRIDDialog), _myID(new DMRRadioID(this)), _editID(nullptr), _config(context) { ui->setupUi(this); construct(); } void DMRIDDialog::construct() { Settings settings; ui->name->setText(_myID->name()); ui->dmrID->setText(QString::number(_myID->number())); ui->dmrID->setValidator(new QIntValidator(1, 16777215)); if (! settings.showCommercialFeatures()) ui->tabWidget->tabBar()->hide(); ui->extensionView->setObjectName("dmrRadioIdExtension"); ui->extensionView->setObject(_myID, _config); } DMRIDDialog::~DMRIDDialog() { delete ui; } DMRRadioID * DMRIDDialog::radioId() { _myID->setName(ui->name->text().simplified()); _myID->setNumber(ui->dmrID->text().toUInt()); if (_editID) { _editID->copy(*_myID); _myID->deleteLater(); _myID = _editID; } else { _myID->setParent(nullptr); } return _myID; } qdmr-0.12.3/src/dmriddialog.hh000066400000000000000000000010261501654372000161310ustar00rootroot00000000000000#ifndef DMRIDDIALOG_HH #define DMRIDDIALOG_HH #include #include "radioid.hh" namespace Ui { class DMRIDDialog; } class DMRIDDialog : public QDialog { Q_OBJECT public: explicit DMRIDDialog(Config *context, QWidget *parent = nullptr); DMRIDDialog(DMRRadioID *radioid, Config *context, QWidget *parent = nullptr); ~DMRIDDialog(); DMRRadioID *radioId(); protected: void construct(); private: Ui::DMRIDDialog *ui; DMRRadioID *_myID; DMRRadioID *_editID; Config *_config; }; #endif // DMRIDDIALOG_HH qdmr-0.12.3/src/dmriddialog.ui000066400000000000000000000060471501654372000161570ustar00rootroot00000000000000 DMRIDDialog 0 0 400 300 0 0 Dialog 0 Basic Name DMR ID Extensions Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ExtensionView QWidget
      extensionview.hh
      1
      buttonBox accepted() DMRIDDialog accept() 248 254 157 274 buttonBox rejected() DMRIDDialog reject() 316 260 286 274
      qdmr-0.12.3/src/dtmfcontactdialog.cc000066400000000000000000000032621501654372000173320ustar00rootroot00000000000000#include "dtmfcontactdialog.hh" #include "ui_dtmfcontactdialog.h" #include #include "contact.hh" #include "settings.hh" DTMFContactDialog::DTMFContactDialog(Config *context, QWidget *parent) : QDialog(parent), _myContact(new DTMFContact(this)), _contact(nullptr), _config(context), ui(new Ui::DTMFContactDialog) { setWindowTitle(tr("Create DTMF Contact")); construct(); } DTMFContactDialog::DTMFContactDialog(DTMFContact *contact, Config *context, QWidget *parent) : QDialog(parent), _myContact(new DTMFContact(this)), _contact(contact), _config(context), ui(new Ui::DTMFContactDialog) { setWindowTitle(tr("Edit DMR Contact")); if (_contact) _myContact->copy(*_contact); construct(); } DTMFContactDialog::~DTMFContactDialog() { delete ui; } void DTMFContactDialog::construct() { ui->setupUi(this); Settings settings; ui->nameLineEdit->setText(_myContact->name()); ui->numberLineEdit->setText(_myContact->number()); ui->numberLineEdit->setValidator(new QRegExpValidator(QRegExp("[0-9a-dA-D\\*#]+"))); ui->ringCheckBox->setChecked(_myContact->ring()); ui->extensionView->setObjectName("dtmfContactExtension"); ui->extensionView->setObject(_myContact, _config); if (! settings.showExtensions()) ui->tabWidget->tabBar()->hide(); } DTMFContact * DTMFContactDialog::contact() { _myContact->setName(ui->nameLineEdit->text().simplified()); _myContact->setNumber(ui->numberLineEdit->text().simplified()); _myContact->setRing(ui->ringCheckBox->isChecked()); DTMFContact *contact = _myContact; if (_contact) { _contact->copy(*_myContact); contact = _contact; } else { _myContact->setParent(nullptr); } return contact; } qdmr-0.12.3/src/dtmfcontactdialog.hh000066400000000000000000000011421501654372000173370ustar00rootroot00000000000000#ifndef DTMFCONTACTDIALOG_HH #define DTMFCONTACTDIALOG_HH #include namespace Ui { class DTMFContactDialog; } class DTMFContact; class Config; class DTMFContactDialog : public QDialog { Q_OBJECT public: explicit DTMFContactDialog(Config *context, QWidget *parent = nullptr); DTMFContactDialog(DTMFContact *contact, Config *context, QWidget *parent = nullptr); ~DTMFContactDialog(); DTMFContact *contact(); protected: void construct(); private: DTMFContact *_myContact; DTMFContact *_contact; Config *_config; Ui::DTMFContactDialog *ui; }; #endif // DTMFCONTACTDIALOG_HH qdmr-0.12.3/src/dtmfcontactdialog.ui000066400000000000000000000063441501654372000173660ustar00rootroot00000000000000 DTMFContactDialog 0 0 379 252 0 0 Dialog 0 Basic Name Number Ring Extensions Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ExtensionView QWidget
      extensionview.hh
      1
      buttonBox accepted() DTMFContactDialog accept() 248 254 157 274 buttonBox rejected() DTMFContactDialog reject() 316 260 286 274
      qdmr-0.12.3/src/errormessageview.cc000066400000000000000000000015701501654372000172350ustar00rootroot00000000000000#include "errormessageview.hh" #include "ui_errormessageview.h" ErrorMessageView::ErrorMessageView(const ErrorStack &stack, QWidget *parent) : QDialog(parent), ui(new Ui::ErrorMessageView) { ui->setupUi(this); QFont font = ui->errorMessage->font(); font.setBold(true);ui->errorMessage->setFont(font); if (stack.isEmpty()) { setWindowTitle(tr("Error: Unknown.")); ui->errorMessage->setText("An unknown error has orccured."); ui->errorStack->setVisible(false); return; } setWindowTitle(tr("Error: %1").arg(stack.message(0).message())); if (1 == stack.count()) ui->errorMessage->setText(stack.message(0).message()); else ui->errorMessage->setText( stack.message(0).message() + ":" + stack.message(stack.count()-1).message()); ui->errorStack->setText(stack.format()); } ErrorMessageView::~ErrorMessageView() { delete ui; } qdmr-0.12.3/src/errormessageview.hh000066400000000000000000000006001501654372000172400ustar00rootroot00000000000000#ifndef ERRORMESSAGEVIEW_HH #define ERRORMESSAGEVIEW_HH #include #include "errorstack.hh" namespace Ui { class ErrorMessageView; } class ErrorMessageView : public QDialog { Q_OBJECT public: explicit ErrorMessageView(const ErrorStack &stack, QWidget *parent = nullptr); ~ErrorMessageView(); private: Ui::ErrorMessageView *ui; }; #endif // ERRORMESSAGEVIEW_HH qdmr-0.12.3/src/errormessageview.ui000066400000000000000000000051631501654372000172670ustar00rootroot00000000000000 ErrorMessageView Qt::ApplicationModal 0 0 400 300 Dialog TextLabel Traceback: 0 0 Sans Serif TextLabel Qt::PlainText Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 5 -1 Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() ErrorMessageView accept() 248 254 157 274 buttonBox rejected() ErrorMessageView reject() 316 260 286 274 qdmr-0.12.3/src/extensionview.cc000066400000000000000000000107341501654372000165550ustar00rootroot00000000000000#include "extensionview.hh" #include "ui_extensionview.h" #include "propertydelegate.hh" #include "extensionwrapper.hh" #include "settings.hh" #include ExtensionView::ExtensionView(QWidget *parent) : QWidget(parent), ui(new Ui::ExtensionView), _model(nullptr) { ui->setupUi(this); ui->view->setModel(&_proxy); ui->view->setItemDelegateForColumn(1, &_editor); ui->view->setSelectionMode(QAbstractItemView::SingleSelection); ui->view->setSelectionBehavior(QAbstractItemView::SelectRows); ui->create->setEnabled(false); ui->remove->setEnabled(false); connect(ui->view->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(onSelectionChanged(QItemSelection,QItemSelection))); connect(ui->create, SIGNAL(clicked(bool)), this, SLOT(onCreate())); connect(ui->remove, SIGNAL(clicked(bool)), this, SLOT(onDelete())); connect(ui->view->header(), SIGNAL(sectionCountChanged(int,int)), this, SLOT(loadSectionState())); connect(ui->view->header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(storeSectionState())); } ExtensionView::~ExtensionView() { delete ui; } void ExtensionView::setObject(ConfigItem *obj, Config *context) { if (nullptr != _model) _model->deleteLater(); _model = new PropertyWrapper(obj, this); _proxy.setSourceModel(_model); _editor.setConfig(context); } void ExtensionView::onSelectionChanged(const QItemSelection ¤t, const QItemSelection &last) { Q_UNUSED(last) // If nothing is selected disable both if (current.isEmpty() || (nullptr == _model)) { ui->create->setEnabled(false); ui->remove->setEnabled(false); return; } // Get selected row QModelIndex row = ui->view->selectionModel()->selectedRows().first(); if (! row.isValid()) { ui->create->setEnabled(false); ui->remove->setEnabled(false); return; } if (_model->isProperty(_proxy.mapToSource(row))) { ConfigItem *obj = _model->parentObject(_proxy.mapToSource(row)); QMetaProperty prop = _model->propertyAt(_proxy.mapToSource(row)); if ((nullptr == obj) || (! prop.isValid())) { ui->create->setEnabled(false); ui->remove->setEnabled(false); return; } if (propIsInstance(prop)) { ui->create->setEnabled(false); ui->remove->setEnabled(false); if (prop.read(obj).value()) { ui->create->setEnabled(false); ui->remove->setEnabled(prop.isWritable()); } else { ui->create->setEnabled(prop.isWritable()); ui->remove->setEnabled(false); } } else if (propIsInstance(prop)) { ui->create->setEnabled(true); ui->remove->setEnabled(false); } } else if (_model->isListElement(_proxy.mapToSource(row))) { ui->create->setEnabled(false); ui->remove->setEnabled(true); } } void ExtensionView::onCreate() { if ((! ui->view->selectionModel()->hasSelection()) || (nullptr == _model)) return; QModelIndex item = _proxy.mapToSource( ui->view->selectionModel()->selectedRows(0).first()); if (! _model->isProperty(item)) return; QMetaProperty prop = _model->propertyAt(item); ConfigItem *obj = _model->parentObject(item); if ((nullptr == obj) || (! prop.isValid())) return; if (propIsInstance(prop) && (! _model->createInstanceAt(item))) { QMessageBox::critical(nullptr, tr("Cannot create extension."), tr("Cannot create extension, consider reporting a bug.")); return; } else if (propIsInstance(prop) && !_model->createElementAt(item)) { QMessageBox::critical(nullptr, tr("Cannot create list element."), tr("Cannot create list element, consider reporting a bug.")); return; } ui->view->selectionModel()->clearSelection(); } void ExtensionView::onDelete() { if ((! ui->view->selectionModel()->hasSelection()) || (nullptr == _model)) return; QModelIndex item = _proxy.mapToSource( ui->view->selectionModel()->selectedRows(0).first()); if (_model->isProperty(item)) _model->deleteInstanceAt(item); else if (_model->isListElement(item)) _model->deleteElementAt(item); ui->view->selectionModel()->clearSelection(); } void ExtensionView::loadSectionState() { Settings settings; ui->view->header()->restoreState(settings.headerState(this->objectName())); } void ExtensionView::storeSectionState() { Settings settings; settings.setHeaderState(this->objectName(), ui->view->header()->saveState()); } qdmr-0.12.3/src/extensionview.hh000066400000000000000000000013501501654372000165610ustar00rootroot00000000000000#ifndef EXTENSIONVIEW_HH #define EXTENSIONVIEW_HH #include #include "extensionwrapper.hh" #include "propertydelegate.hh" namespace Ui { class ExtensionView; } class ConfigItem; class ExtensionView : public QWidget { Q_OBJECT public: explicit ExtensionView(QWidget *parent = nullptr); ~ExtensionView(); public slots: void setObject(ConfigItem *obj, Config *context); protected slots: void onSelectionChanged(const QItemSelection ¤t, const QItemSelection &last); void onCreate(); void onDelete(); void loadSectionState(); void storeSectionState(); private: Ui::ExtensionView *ui; PropertyWrapper *_model; ExtensionProxy _proxy; PropertyDelegate _editor; }; #endif // EXTENSIONVIEW_HH qdmr-0.12.3/src/extensionview.ui000066400000000000000000000017071501654372000166050ustar00rootroot00000000000000 ExtensionView 0 0 400 300 Form Create Remove qdmr-0.12.3/src/extensionwrapper.cc000066400000000000000000000521241501654372000172620ustar00rootroot00000000000000#include "extensionwrapper.hh" #include #include "logger.hh" #include "configreference.hh" #include "configobjecttypeselectiondialog.hh" #include "frequency.hh" #include "interval.hh" /* ******************************************************************************************** * * Implementation of ExtensionProxy * ******************************************************************************************** */ ExtensionProxy::ExtensionProxy(QObject *parent) : QIdentityProxyModel(parent) { // pass... } void ExtensionProxy::setSourceModel(QAbstractItemModel *sourceModel) { QIdentityProxyModel::setSourceModel(sourceModel); _indexP2S.clear(); _indexS2P.clear(); if (PropertyWrapper *model = qobject_cast(sourceModel)) { // Create root element maps for (int s=0,p=0; srowCount(QModelIndex()); s++) { if (model->isExtension(model->index(s,0, QModelIndex()))) { _indexP2S[p] = s; _indexS2P[s] = p; p++; } } } } int ExtensionProxy::rowCount(const QModelIndex &parent) const { if (nullptr == sourceModel()) return 0; // Non root elements if (parent.isValid()) return sourceModel()->rowCount(mapToSource(parent)); // root element return _indexP2S.count(); } int ExtensionProxy::columnCount(const QModelIndex &parent) const { if (nullptr == sourceModel()) return 0; return sourceModel()->columnCount(mapToSource(parent)); } QModelIndex ExtensionProxy::index(int row, int column, const QModelIndex &parent) const { if (parent.isValid()) return mapFromSource(sourceModel()->index(row, column, mapToSource(parent))); return createIndex(row, column, qobject_cast(sourceModel())->root()); } QModelIndex ExtensionProxy::parent(const QModelIndex &child) const { if (! child.isValid()) return QModelIndex(); ConfigItem *root = qobject_cast(sourceModel())->root(); QObject *pptr = reinterpret_cast(child.internalPointer()); if ((root == pptr) || (nullptr == pptr)) return QModelIndex(); QObject *gpptr = pptr->parent(); if (nullptr == gpptr) return QModelIndex(); if (root == gpptr) { // Search for index of parent root: const QMetaObject *meta = root->metaObject(); for (int p=QObject::staticMetaObject.propertyCount(); ppropertyCount(); p++) { QMetaProperty prop = meta->property(p); if (! prop.isValid()) continue; if (prop.read(root).value() == pptr) { return createIndex(_indexS2P[p-QObject::staticMetaObject.propertyCount()], 0, reinterpret_cast(root)); } } return QModelIndex(); } return mapFromSource(sourceModel()->parent(child)); } QModelIndex ExtensionProxy::mapToSource(const QModelIndex &proxyIndex) const { // root maps to root if (! proxyIndex.isValid()) return QModelIndex(); // If no source -> blub. if (nullptr == sourceModel()) return QModelIndex(); // If index is not an immediate child of root -> map 1:1 if (proxyIndex.parent().isValid()) { return sourceModel()->index( proxyIndex.row(), proxyIndex.column(), mapToSource(proxyIndex.parent())); } // If index is an immediate child of root -> map only extensions if (_indexP2S.contains(proxyIndex.row())) return sourceModel()->index( _indexP2S[proxyIndex.row()], proxyIndex.column(), QModelIndex()); return QModelIndex(); } QModelIndex ExtensionProxy::mapFromSource(const QModelIndex &sourceIndex) const { // root maps to root if (! sourceIndex.isValid()) return QModelIndex(); // If no source -> blub. if (nullptr == sourceModel()) return QModelIndex(); // If index is not an immediate child of root -> map 1:1 if (sourceModel()->parent(sourceIndex).isValid()) { return createIndex(sourceIndex.row(), sourceIndex.column(), sourceIndex.internalPointer()); } if (_indexS2P.contains(sourceIndex.row())) return createIndex(_indexS2P[sourceIndex.row()], sourceIndex.column(), sourceIndex.internalPointer()); return QModelIndex(); } /* ******************************************************************************************** * * Implementation of PropertyWrapper * ******************************************************************************************** */ PropertyWrapper::PropertyWrapper(ConfigItem *obj, QObject *parent) : QAbstractItemModel(parent), _object(obj) { if (_object) { connect(_object, SIGNAL(beginClear()), this, SLOT(onItemClearing())); connect(_object, SIGNAL(endClear()), this, SLOT(onItemCleared())); } } ConfigItem * PropertyWrapper::root() const { return _object; } ConfigItem * PropertyWrapper::item(const QModelIndex &item) const { if (! item.isValid()) return nullptr; // Get pointer to parent (either list or item) QObject *pptr = _object; if (nullptr != item.internalPointer()) pptr = reinterpret_cast(item.internalPointer()); if (ConfigItem *pobj = qobject_cast(pptr)) { // If parent is item find corresponding property const QMetaObject *meta = pobj->metaObject(); int pcount = meta->propertyCount() - QObject::staticMetaObject.propertyCount(); if (item.row() < pcount) { QMetaProperty prop = meta->property(QObject::staticMetaObject.propertyCount() + item.row()); return prop.read(pobj).value(); } } else if (ConfigObjectList *plst = qobject_cast(pptr)) { // If parent is list if (item.row() < plst->count()) return plst->get(item.row()); } return nullptr; } ConfigObjectList * PropertyWrapper::list(const QModelIndex &item) const { if (! item.isValid()) return nullptr; QObject *pptr = reinterpret_cast(item.internalPointer()); ConfigItem *pobj = qobject_cast(pptr); if (nullptr == pobj) return nullptr; const QMetaObject *meta = pobj->metaObject(); int pcount = meta->propertyCount() - QObject::staticMetaObject.propertyCount(); if (item.row() < pcount) { QMetaProperty prop = meta->property(QObject::staticMetaObject.propertyCount() + item.row()); return prop.read(pobj).value(); } return nullptr; } ConfigObjectList * PropertyWrapper::parentList(const QModelIndex &index) const { if (! index.isValid()) return nullptr; QObject *pptr = reinterpret_cast(index.internalId()); if (nullptr == pptr) return nullptr; if (ConfigObjectList *lst = qobject_cast(pptr)) return lst; return nullptr; } ConfigItem * PropertyWrapper::parentObject(const QModelIndex &index) const { if (! index.isValid()) return _object; QObject *pptr = reinterpret_cast(index.internalId()); return qobject_cast(pptr); } QMetaProperty PropertyWrapper::propertyAt(const QModelIndex &index) const { ConfigItem *pobj = parentObject(index); const QMetaObject *meta = pobj->metaObject(); int propCount = meta->propertyCount() - QObject::staticMetaObject.propertyCount(); if (index.row() < propCount) return meta->property(index.row()+QObject::staticMetaObject.propertyCount()); return QMetaProperty(); } bool PropertyWrapper::isExtension(const QModelIndex &index) const { if (! index.isValid()) return false; QMetaProperty prop = propertyAt(index); return propIsInstance(prop); } bool PropertyWrapper::isProperty(const QModelIndex &index) const { if (! index.isValid()) return true; // Index is property if parent is an ConfigItem QObject *pptr = reinterpret_cast(index.internalPointer()); return nullptr != qobject_cast(pptr); } bool PropertyWrapper::isListElement(const QModelIndex &index) const { if (! index.isValid()) return false; // Index is list element if parent is ConfigObjectList QObject *pptr = reinterpret_cast(index.internalPointer()); if (nullptr == pptr) return false; return nullptr != qobject_cast(pptr); } bool PropertyWrapper::createInstanceAt(const QModelIndex &item) { if (! isProperty(item)) return false; ConfigItem *obj = parentObject(item); QMetaProperty prop = propertyAt(item); if ((nullptr == obj) || (! prop.isValid())) return false; // Check type of property if (! propIsInstance(prop)) return false; // If property is already set -> abort if (prop.read(obj).value()) return false; // Get TypeObject of property if (QMetaType::UnknownType == prop.userType()) return false; QMetaType type(prop.userType()); if (! (QMetaType::PointerToQObject & type.flags())) return false; const QMetaObject *propType = type.metaObject(); // Instantiate extension ConfigItem *ext = qobject_cast( propType->newInstance(QGenericArgument(nullptr, obj))); if (nullptr == ext) return false; // store item beginInsertRows(item, 0, ext->metaObject()->propertyCount()); prop.write(obj, QVariant::fromValue(ext)); endInsertRows(); emit dataChanged(index(item.row(), 0, item.parent()), index(item.row(), 2, item.parent())); emit dataChanged(index(0, 0, item), index(ext->metaObject()->propertyCount(),2, item)); return true; } bool PropertyWrapper::deleteInstanceAt(const QModelIndex &item) { ConfigItem *obj = parentObject(item); QMetaProperty prop = propertyAt(item); if ((nullptr == obj) || (! prop.isValid())) return false; // Check type of property if (! propIsInstance(prop)) return false; // If property is set -> delete if (ConfigItem *ext = prop.read(obj).value()) { if (! prop.isWritable()) return false; beginRemoveRows(item, 0, rowCount(item)); prop.write(obj, QVariant::fromValue(nullptr)); endRemoveRows(); ext->deleteLater(); return true; } return false; } bool PropertyWrapper::createElementAt(const QModelIndex &item) { ConfigItem *obj = parentObject(item); QMetaProperty prop = propertyAt(item); if ((nullptr == obj) || (! prop.isValid())) return false; ConfigObjectList *lst = prop.read(obj).value(); if (nullptr == lst) return false; ConfigObjectTypeSelectionDialog dialog(lst->elementTypes()); if (QDialog::Accepted != dialog.exec()) return true; QMetaObject type= dialog.selectedType(); // Instantiate element ConfigObject *element = qobject_cast( type.newInstance(QGenericArgument(nullptr, lst))); if (nullptr == element) return false; element->setName(tr("new element")); // store item beginInsertRows(item,lst->count(), lst->count()+1); lst->add(element); endInsertRows(); emit dataChanged(index(item.row(), 0, item.parent()), index(item.row(), 2, item.parent())); QModelIndex elementIndex = createIndex(lst->count(), 0, lst); beginInsertRows(elementIndex, 0, element->metaObject()->propertyCount()); endInsertRows(); emit dataChanged(index(0, 0, item), index(lst->count(), 2, item)); return true; } bool PropertyWrapper::deleteElementAt(const QModelIndex &item) { ConfigObjectList *lst = parentList(item); if (nullptr == lst) return false; if (item.row() >= lst->count()) return false; beginRemoveRows(item, 0, rowCount(item)); lst->del(lst->get(item.row())); endRemoveRows(); return true; } QModelIndex PropertyWrapper::index(int row, int column, const QModelIndex &parent) const { if (! parent.isValid()) { // Handle root element const QMetaObject *meta = _object->metaObject(); int pcount = meta->propertyCount() - QObject::staticMetaObject.propertyCount(); if (row < pcount) return createIndex(row, column, _object); } else if (ConfigItem *pobj = item(parent)) { const QMetaObject *meta = pobj->metaObject(); int pcount = meta->propertyCount() - QObject::staticMetaObject.propertyCount(); if (row < pcount) return createIndex(row, column, pobj); } else if (ConfigObjectList *plst = list(parent)) { if (row < plst->count()) return createIndex(row, column, plst); } return QModelIndex(); } QModelIndex PropertyWrapper::parent(const QModelIndex &child) const { if (! child.isValid()) return QModelIndex(); QObject *pptr = reinterpret_cast(child.internalPointer()); // Handle root if (ConfigItem *pobj = qobject_cast(pptr)) { if (_object == pobj) return QModelIndex(); } QObject *gpptr = pptr->parent(); // Should not happen if (nullptr == gpptr) return QModelIndex(); if (ConfigItem *gp = qobject_cast(gpptr)) { // If grand parent is item: // Search for parent in grand-parent's properties const QMetaObject *meta = gp->metaObject(); for (int p=QObject::staticMetaObject.propertyCount(); ppropertyCount(); p++) { QMetaProperty prop = meta->property(p); if (! prop.isValid()) continue; if (prop.read(gp).value() == pptr) { return createIndex(p-QObject::staticMetaObject.propertyCount(), 0, reinterpret_cast(gp)); } } } else if (ConfigObjectList *gp = qobject_cast(gpptr)) { // If grand parent is item: // Search for parent in grand-parent's elements for (int i=0; icount(); i++) { if (pptr == gp->get(i)) return createIndex(i, 0, reinterpret_cast(gp)); } } return QModelIndex(); } int PropertyWrapper::rowCount(const QModelIndex &parent) const { if (! parent.isValid()) { // If parent is root -> handle _object const QMetaObject *meta = _object->metaObject(); return (meta->propertyCount() - QObject::staticMetaObject.propertyCount()); } if (ConfigItem *pobj = item(parent)) { // If parent is item -> return property count const QMetaObject *meta = pobj->metaObject(); return (meta->propertyCount() - QObject::staticMetaObject.propertyCount()); } else if (ConfigObjectList *plst = list(parent)) { // If parent is list -> return element count. return plst->count(); } return 0; } int PropertyWrapper::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return 3; } Qt::ItemFlags PropertyWrapper::flags(const QModelIndex &index) const { if (isProperty(index)) { // check if property is a config object or atomic (or reference) QMetaProperty prop = propertyAt(index); // Object can be selected and expanded, but not edited directly if (propIsInstance(prop) || propIsInstance(prop)) return Qt::ItemIsSelectable | Qt::ItemIsEnabled; // References are edited directly by combo-box. See PropertyDelegate. They cannot be expanded. if (propIsInstance(prop) && prop.isScriptable() && (1 == index.column())) return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemNeverHasChildren; // Atomic properties are directly editable, see also PropertyDelegate. They cannot be expanded. if (prop.isWritable() && (1 == index.column())) return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemNeverHasChildren; // Some default values. return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; } else if (isListElement(index)) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } return Qt::NoItemFlags; } bool PropertyWrapper::hasChildren(const QModelIndex &element) const { // If root element if (! element.isValid()) return true; // Get parent object (might be item or list) QObject *pptr = reinterpret_cast(element.internalId()); if (ConfigItem* pobj = qobject_cast(pptr)) { // If parent is item -> get addressed property QMetaProperty prop = propertyAt(element); // If property is an item or list (and is set) if (propIsInstance(prop)) { return nullptr != prop.read(pobj).value(); } else if (propIsInstance(prop)) { // If element and there are elements within the list. ConfigObjectList *lst = prop.read(pobj).value(); return (nullptr != lst) && (lst->count()>0); } } else if (ConfigObjectList *lst = qobject_cast(pptr)) { // If parent is a list, check row index. List elements are always objects and have children. return lst->count() > element.row(); } return false; } QVariant PropertyWrapper::headerData(int section, Qt::Orientation orientation, int role) const { // Only handle horizontal display data if ((Qt::Horizontal != orientation) || (Qt::DisplayRole != role)) return QAbstractItemModel::headerData(section, orientation, role); // Dispatch by section index switch (section) { case 0: return tr("Property"); case 1: return tr("Value"); case 2: return tr("Description"); default: break; } // default return QAbstractItemModel::headerData(section, orientation, role); } QVariant PropertyWrapper::data(const QModelIndex &index, int role) const { if (! index.isValid()) return QVariant(); if (isProperty(index)) { ConfigItem *pobj = parentObject(index); QMetaProperty prop = propertyAt(index); if (0 == index.column()) { if (Qt::DisplayRole == role) return prop.name(); } else if ((2 == index.column()) && (Qt::DisplayRole == role)) { if (propIsInstance(prop)) { ConfigItem *item = prop.read(pobj).value(); if (item && item->hasDescription()) return item->description(); } if (pobj->hasDescription(prop)) return pobj->description(prop); return QVariant(); } else if (Qt::ToolTipRole == role) { if (propIsInstance(prop)) { ConfigItem *item = prop.read(pobj).value(); if (item && item->hasLongDescription()) return item->longDescription(); } if (pobj->hasLongDescription(prop)) return pobj->longDescription(prop); if (pobj->hasDescription(prop)) return pobj->description(prop); return QVariant(); } QVariant value = prop.read(pobj); if (prop.isEnumType() && ((Qt::DisplayRole == role) || (Qt::EditRole == role))) { QMetaEnum e = prop.enumerator(); const char *key = e.valueToKey(value.toInt()); if (nullptr == key) { logError() << "Cannot map value " << value.toUInt() << " to enum " << e.name() << ". Ignore attribute but this points to an incompatibility in some codeplug. " << "Consider reporting it to https://github.com/hmatuschek/qdmr/issues."; return QVariant(); } return QString(key); } else if ((QVariant::Bool == prop.type()) && (Qt::EditRole == role)) { return value; } else if ((QVariant::Bool == prop.type()) && (Qt::DisplayRole == role)) { if (value.toBool()) return tr("true"); return tr("false"); } else if ( ((QVariant::Int == prop.type()) || (QVariant::UInt == prop.type()) || (QVariant::Double == prop.type()) || (QVariant::String == prop.type())) && ((Qt::DisplayRole == role) || (Qt::EditRole==role)) ) { return value; } else if (QString("Frequency") == prop.typeName()) { if (Qt::DisplayRole == role) return value.value().format(); else if (Qt::EditRole == role) return value; } else if (QString("Interval") == prop.typeName()) { if (Qt::DisplayRole == role) return value.value().format(); else if (Qt::EditRole == role) return value; } else if (value.value() && (Qt::DisplayRole == role)) { ConfigObjectReference *ref = value.value(); ConfigObject *obj = ref->as(); if (nullptr == obj) return tr("[None]"); return QString("%1 (%2)").arg(obj->name()).arg(obj->metaObject()->className()); } else if (value.value() && (Qt::EditRole == role)) { return value; } else if (propIsInstance(prop)) { ConfigItem *item = value.value(); if (Qt::DisplayRole == role) { if (nullptr == item) return tr("[None]"); else return tr("Instance of %1").arg(item->metaObject()->className()); } } else if (propIsInstance(prop)) { ConfigObjectList *lst = value.value(); if (Qt::DisplayRole == role) return tr("List of %1 instances").arg(lst->classNames().join(", ")); } else if (Qt::DisplayRole == role) { logWarn() << "Unhandled property '" << prop.name() << "' of type " << prop.typeName() << "."; } } else if (isListElement(index)) { ConfigObjectList *lst = parentList(index); if (index.row() >= lst->count()) return QVariant(); if ((0 == index.column()) && (Qt::DisplayRole == role)) return lst->get(index.row())->name(); else if ((1 == index.column()) && (Qt::DisplayRole == role)) return lst->get(index.row())->description(); else if ((2 == index.column()) && (Qt::DisplayRole == role)) return lst->get(index.row())->longDescription(); } return QVariant(); } void PropertyWrapper::onItemClearing() { beginResetModel(); } void PropertyWrapper::onItemCleared() { endResetModel(); } qdmr-0.12.3/src/extensionwrapper.hh000066400000000000000000000045051501654372000172740ustar00rootroot00000000000000#ifndef EXTENSIONWRAPPER_HH #define EXTENSIONWRAPPER_HH #include "configobject.hh" #include #include #include class ExtensionProxy: public QIdentityProxyModel { Q_OBJECT public: explicit ExtensionProxy(QObject *parent=nullptr); public: void setSourceModel(QAbstractItemModel *sourceModel); int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QModelIndex index(int row, int column, const QModelIndex &parent) const; QModelIndex parent(const QModelIndex &child) const; QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; //QItemSelection mapSelectionFromSource(const QItemSelection &selection) const; QModelIndex mapToSource(const QModelIndex &proxyIndex) const; //QItemSelection mapSelectionToSource(const QItemSelection &selection) const; protected: QHash _indexS2P, _indexP2S; }; class PropertyWrapper: public QAbstractItemModel { Q_OBJECT public: PropertyWrapper(ConfigItem *obj, QObject *parent=nullptr); ConfigItem *root() const; ConfigItem *item(const QModelIndex &item) const; ConfigObjectList *list(const QModelIndex &item) const; ConfigObjectList *parentList(const QModelIndex &index) const; ConfigItem *parentObject(const QModelIndex &index) const; QMetaProperty propertyAt(const QModelIndex &index) const; bool isProperty(const QModelIndex &index) const; bool isExtension(const QModelIndex &index) const; bool isListElement(const QModelIndex &index) const; bool createInstanceAt(const QModelIndex &item); bool deleteInstanceAt(const QModelIndex &item); bool createElementAt(const QModelIndex &item); bool deleteElementAt(const QModelIndex &item); QModelIndex index(int row, int column, const QModelIndex &parent) const; QModelIndex parent(const QModelIndex &child) const; int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; Qt::ItemFlags flags(const QModelIndex &index) const; bool hasChildren(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; protected slots: void onItemClearing(); void onItemCleared(); protected: ConfigItem *_object; }; #endif // EXTENSIONWRAPPER_HH qdmr-0.12.3/src/generalsettingsview.cc000066400000000000000000000136651501654372000177450ustar00rootroot00000000000000#include "generalsettingsview.hh" #include "ui_generalsettingsview.h" #include "config.hh" GeneralSettingsView::GeneralSettingsView(Config *config, QWidget *parent) : QWidget(parent), ui(new Ui::GeneralSettingsView), _config(config) { ui->setupUi(this); if (_config->settings()->defaultId()) { ui->dmrID->setText(QString::number(_config->settings()->defaultId()->number())); ui->radioName->setText(_config->settings()->defaultId()->name()); } ui->introLine1->setText(_config->settings()->introLine1()); ui->introLine2->setText(_config->settings()->introLine2()); ui->mic->setValue(_config->settings()->micLevel()); ui->speech->setChecked(_config->settings()->speech()); ui->powerValue->setItemData(0, (unsigned)Channel::Power::Max); ui->powerValue->setItemData(1, (unsigned)Channel::Power::High); ui->powerValue->setItemData(2, (unsigned)Channel::Power::Mid); ui->powerValue->setItemData(3, (unsigned)Channel::Power::Low); ui->powerValue->setItemData(4, (unsigned)Channel::Power::Min); switch(_config->settings()->power()) { case Channel::Power::Max: ui->powerValue->setCurrentIndex(0); break; case Channel::Power::High: ui->powerValue->setCurrentIndex(1); break; case Channel::Power::Mid: ui->powerValue->setCurrentIndex(2); break; case Channel::Power::Low: ui->powerValue->setCurrentIndex(3); break; case Channel::Power::Min: ui->powerValue->setCurrentIndex(4); break; } ui->squelchValue->setValue(_config->settings()->squelch()); ui->totValue->setValue(_config->settings()->tot()); ui->voxValue->setValue(_config->settings()->vox()); ui->extensionView->setObjectName("radioSettingsExtension"); ui->extensionView->setObject(_config->settings(), _config); connect(_config, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModified())); connect(ui->dmrID, SIGNAL(editingFinished()), this, SLOT(onDMRIDChanged())); connect(ui->radioName, SIGNAL(editingFinished()), this, SLOT(onNameChanged())); connect(ui->introLine1, SIGNAL(editingFinished()), this, SLOT(onIntroLine1Edited())); connect(ui->introLine2, SIGNAL(editingFinished()), this, SLOT(onIntroLine2Edited())); connect(ui->mic, SIGNAL(valueChanged(int)), this, SLOT(onMicLevelChanged())); connect(ui->speech, SIGNAL(stateChanged(int)), this, SLOT(onSpeechChanged())); connect(ui->powerValue, SIGNAL(currentIndexChanged(int)), this, SLOT(onPowerChanged())); connect(ui->squelchValue, SIGNAL(valueChanged(int)), this, SLOT(onSquelchChanged())); connect(ui->totValue, SIGNAL(valueChanged(int)), this, SLOT(onTOTChanged())); connect(ui->voxValue, SIGNAL(valueChanged(int)), this, SLOT(onVOXChanged())); } GeneralSettingsView::~GeneralSettingsView() { delete ui; } void GeneralSettingsView::hideDMRID(bool hidden) { ui->dmrIDBox->setHidden(hidden); } void GeneralSettingsView::hideExtensions(bool hidden) { ui->extensionBox->setHidden(hidden); } void GeneralSettingsView::onConfigModified() { if (_config->settings()->defaultId()) { ui->dmrID->setText(QString::number(_config->settings()->defaultId()->number())); ui->radioName->setText(_config->settings()->defaultId()->name()); } else { ui->dmrID->setText("0"); ui->radioName->setText(""); } ui->introLine1->setText(_config->settings()->introLine1()); ui->introLine2->setText(_config->settings()->introLine2()); ui->mic->setValue(_config->settings()->micLevel()); ui->speech->setChecked(_config->settings()->speech()); switch(_config->settings()->power()) { case Channel::Power::Max: ui->powerValue->setCurrentIndex(0); break; case Channel::Power::High: ui->powerValue->setCurrentIndex(1); break; case Channel::Power::Mid: ui->powerValue->setCurrentIndex(2); break; case Channel::Power::Low: ui->powerValue->setCurrentIndex(3); break; case Channel::Power::Min: ui->powerValue->setCurrentIndex(4); break; } ui->squelchValue->setValue(_config->settings()->squelch()); ui->totValue->setValue(_config->settings()->tot()); ui->voxValue->setValue(_config->settings()->vox()); } void GeneralSettingsView::onDMRIDChanged() { if (0 == _config->radioIDs()->count()) { _config->radioIDs()->addId("", ui->dmrID->text().toUInt()); _config->settings()->setDefaultId(_config->radioIDs()->get(0)->as()); } else if (nullptr == _config->settings()->defaultId()) { _config->settings()->setDefaultId(_config->radioIDs()->get(0)->as()); _config->settings()->defaultId()->setNumber(ui->dmrID->text().toUInt()); } else { _config->settings()->defaultId()->setNumber(ui->dmrID->text().toUInt()); } } void GeneralSettingsView::onNameChanged() { if (0 == _config->radioIDs()->count()) { _config->radioIDs()->addId(ui->radioName->text().simplified(), 0); _config->settings()->setDefaultId(_config->radioIDs()->get(0)->as()); } else if (nullptr == _config->settings()->defaultId()) { _config->settings()->setDefaultId(_config->radioIDs()->get(0)->as()); _config->settings()->defaultId()->setName(ui->radioName->text().simplified()); } else { _config->settings()->defaultId()->setName(ui->radioName->text().simplified()); } } void GeneralSettingsView::onIntroLine1Edited() { _config->settings()->setIntroLine1(ui->introLine1->text().simplified()); } void GeneralSettingsView::onIntroLine2Edited() { _config->settings()->setIntroLine2(ui->introLine2->text().simplified()); } void GeneralSettingsView::onMicLevelChanged() { _config->settings()->setMicLevel(ui->mic->value()); } void GeneralSettingsView::onSpeechChanged() { _config->settings()->enableSpeech(ui->speech->isChecked()); } void GeneralSettingsView::onPowerChanged() { _config->settings()->setPower(Channel::Power(ui->powerValue->currentData().toUInt())); } void GeneralSettingsView::onSquelchChanged() { _config->settings()->setSquelch(ui->squelchValue->value()); } void GeneralSettingsView::onTOTChanged() { _config->settings()->setTOT(ui->totValue->value()); } void GeneralSettingsView::onVOXChanged() { _config->settings()->setVOX(ui->voxValue->value()); } qdmr-0.12.3/src/generalsettingsview.hh000066400000000000000000000014141501654372000177440ustar00rootroot00000000000000#ifndef GENERALSETTINGSVIEW_HH #define GENERALSETTINGSVIEW_HH #include class Config; namespace Ui { class GeneralSettingsView; } class GeneralSettingsView : public QWidget { Q_OBJECT public: explicit GeneralSettingsView(Config *config, QWidget *parent = nullptr); ~GeneralSettingsView(); void hideDMRID(bool hidden); void hideExtensions(bool hidden); protected slots: void onConfigModified(); void onDMRIDChanged(); void onNameChanged(); void onIntroLine1Edited(); void onIntroLine2Edited(); void onMicLevelChanged(); void onSpeechChanged(); void onPowerChanged(); void onSquelchChanged(); void onTOTChanged(); void onVOXChanged(); private: Ui::GeneralSettingsView *ui; Config *_config; }; #endif // GENERALSETTINGSVIEW_HH qdmr-0.12.3/src/generalsettingsview.ui000066400000000000000000000174101501654372000177650ustar00rootroot00000000000000 GeneralSettingsView 0 0 644 510 Form DMR Radio ID DMR ID Radio Name Enter your name or callsign. Name/Callsign Boot Settings Intro Line 1 First greeting line (if supported by the radio). Intro line 1 Intro Line 2 Second greeting line (if supported by the radio). Intro line 2 Audio Settings MIC Amp. 1 10 Speech Synthesis Channel Default Values Power Max High Mid Low Min Squelch Open 10 Transmit Timeout Off s 9999 VOX Level Off 10 Extensions ExtensionView QWidget
      extensionview.hh
      1
      qdmr-0.12.3/src/gpssystemdialog.cc000066400000000000000000000050471501654372000170650ustar00rootroot00000000000000#include "gpssystemdialog.hh" #include "settings.hh" GPSSystemDialog::GPSSystemDialog(Config *config, QWidget *parent) : QDialog(parent), _config(config), _myGPSSystem(new GPSSystem(this)), _gpsSystem(nullptr) { setWindowTitle(tr("Create DMR APRS Sysmtem")); construct(); } GPSSystemDialog::GPSSystemDialog(Config *config, GPSSystem *gps, QWidget *parent) : QDialog(parent), _config(config), _myGPSSystem(new GPSSystem(this)), _gpsSystem(gps) { setWindowTitle(tr("Edit DMR APRS Sysmtem")); if (_gpsSystem) _myGPSSystem->copy(*_gpsSystem); construct(); } void GPSSystemDialog::construct() { setupUi(this); Settings settings; // setup name entry name->setText(_myGPSSystem->name()); // setup contact entry for (int i=0; i<_config->contacts()->digitalCount(); i++) { destination->addItem(_config->contacts()->digitalContact(i)->name(), QVariant::fromValue(_config->contacts()->digitalContact(i))); if (_myGPSSystem->contactObj() == _config->contacts()->digitalContact(i)) destination->setCurrentIndex(i); } // setup period period->setValue(_myGPSSystem->period()); // setup revert channel revChannel->addItem(tr("[Selected]"), QVariant::fromValue(nullptr)); if (! _myGPSSystem->hasRevertChannel()) revChannel->setCurrentIndex(0); for (int i=1,j=0; j<_config->channelList()->count(); j++) { if (! _config->channelList()->channel(j)->is()) continue; revChannel->addItem(_config->channelList()->channel(j)->name(), QVariant::fromValue(_config->channelList()->channel(j)->as())); if (_myGPSSystem->hasRevertChannel() && (_myGPSSystem->revertChannel() == _config->channelList()->channel(j)->as())) revChannel->setCurrentIndex(i); i++; } extensionView->setObjectName("dmrAPRSSystemExtension"); extensionView->setObject(_myGPSSystem, _config); if (! settings.showExtensions()) return tabWidget->tabBar()->hide(); } GPSSystem * GPSSystemDialog::gpsSystem() { _myGPSSystem->setName(name->text().simplified()); _myGPSSystem->setContactObj(destination->currentData().value()); _myGPSSystem->setPeriod(period->value()); if (revChannel->currentData().isNull()) _myGPSSystem->resetRevertChannel(); else _myGPSSystem->setRevertChannel(revChannel->currentData().value()); GPSSystem *sys = _myGPSSystem; if (_gpsSystem) { _gpsSystem->copy(*_myGPSSystem); sys = _gpsSystem; } else { _myGPSSystem->setParent(nullptr); } return sys; } qdmr-0.12.3/src/gpssystemdialog.hh000066400000000000000000000010221501654372000170640ustar00rootroot00000000000000#ifndef GPSSYSTEMDIALOG_HH #define GPSSYSTEMDIALOG_HH #include "config.hh" #include #include "ui_gpssystemdialog.h" class GPSSystemDialog : public QDialog, private Ui::GPSSystemDialog { Q_OBJECT public: GPSSystemDialog(Config *config, QWidget *parent=nullptr); GPSSystemDialog(Config *config, GPSSystem *gps, QWidget *parent=nullptr); GPSSystem *gpsSystem(); protected: void construct(); protected: Config *_config; GPSSystem *_myGPSSystem; GPSSystem *_gpsSystem; }; #endif // GPSSYSTEMDIALOG_HH qdmr-0.12.3/src/gpssystemdialog.ui000066400000000000000000000107631501654372000171160ustar00rootroot00000000000000 GPSSystemDialog 0 0 400 300 0 0 Edit GPS System 0 Basic Name 0 0 Destination Update period 0 0 s 60 2700 Revert Channel 0 0 Extensions Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ExtensionView QWidget
      extensionview.hh
      1
      buttonBox accepted() GPSSystemDialog accept() 248 254 157 274 buttonBox rejected() GPSSystemDialog reject() 316 260 286 274
      qdmr-0.12.3/src/grouplistsview.cc000066400000000000000000000045771501654372000167640ustar00rootroot00000000000000#include "grouplistsview.hh" #include "ui_grouplistsview.h" #include "rxgrouplistdialog.hh" #include "config.hh" #include #include GroupListsView::GroupListsView(Config *config, QWidget *parent) : QWidget(parent), ui(new Ui::GroupListsView), _config(config) { ui->setupUi(this); ui->listView->setModel(new GroupListsWrapper(_config->rxGroupLists(), ui->listView)); connect(ui->addRXGroup, SIGNAL(clicked()), this, SLOT(onAddRxGroup())); connect(ui->remRXGroup, SIGNAL(clicked()), this, SLOT(onRemRxGroup())); connect(ui->listView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditRxGroup(unsigned))); } GroupListsView::~GroupListsView() { delete ui; } void GroupListsView::onAddRxGroup() { RXGroupListDialog dialog(_config); if (QDialog::Accepted != dialog.exec()) return; int row=-1; if (ui->listView->hasSelection()) row = ui->listView->selection().second+1; _config->rxGroupLists()->add(dialog.groupList(), row); } void GroupListsView::onRemRxGroup() { // Check if there is any groups selected if (! ui->listView->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot delete RX group list"), tr("Cannot delete RX group lists: You have to select a group list first.")); return; } // Get selection and ask for deletion QPair rows = ui->listView->selection(); int rowcount = rows.second-rows.first+1; if (rows.first == rows.second) { QString name = _config->rxGroupLists()->list(rows.first)->name(); if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete RX group list?"), tr("Delete RX group list %1?").arg(name))) return; } else { if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete RX group list?"), tr("Delete %1 RX group lists?").arg(rowcount))) return; } // collect all selected group lists // need to collect them first as rows change when deleting QList lists; lists.reserve(rowcount); for (int row=rows.first; row<=rows.second; row++) lists.push_back(_config->rxGroupLists()->list(row)); // remove list foreach (RXGroupList *list, lists) _config->rxGroupLists()->del(list); } void GroupListsView::onEditRxGroup(unsigned row) { RXGroupListDialog dialog(_config, _config->rxGroupLists()->list(row)); if (QDialog::Accepted != dialog.exec()) return; dialog.groupList(); } qdmr-0.12.3/src/grouplistsview.hh000066400000000000000000000007241501654372000167640ustar00rootroot00000000000000#ifndef GROUPLISTSVIEW_HH #define GROUPLISTSVIEW_HH #include class Config; namespace Ui { class GroupListsView; } class GroupListsView : public QWidget { Q_OBJECT public: explicit GroupListsView(Config *config, QWidget *parent = nullptr); ~GroupListsView(); protected slots: void onAddRxGroup(); void onRemRxGroup(); void onEditRxGroup(unsigned row); private: Ui::GroupListsView *ui; Config *_config; }; #endif // GROUPLISTSVIEW_HH qdmr-0.12.3/src/grouplistsview.ui000066400000000000000000000031301501654372000167740ustar00rootroot00000000000000 GroupListsView 0 0 400 300 Form 0 0 Add RX Group Alt++ Delete RX Group Alt+- ConfigObjectListView QWidget
      configobjectlistview.hh
      1
      qdmr-0.12.3/src/hearhamrepeatersource.cc000066400000000000000000000046641501654372000202310ustar00rootroot00000000000000#include "hearhamrepeatersource.hh" #include "logger.hh" #include #include #include #include #include HearhamRepeaterSource::HearhamRepeaterSource(QObject *parent) : DownloadableRepeaterDatabaseSource( "hearham.cache.json", QUrl("https://hearham.com/api/repeaters/v1"), 5, parent) { // pass... } bool HearhamRepeaterSource::parse(const QByteArray &json) { QJsonParseError err; auto doc = QJsonDocument::fromJson(json, &err); if (doc.isNull()) { logError() << "Cannot received JSON: " << err.errorString() << "."; return false; } if (! doc.isArray()) { logError() << "Malformed result."; return false; } QRegularExpression ccPattern(R"(CC\w*([0-9]{1,2}))"); QRegularExpression dcsPattern(R"(DCS\w*([0-9]{3})\w*(N|I|))"); for (const QJsonValue &val: doc.array()) { if (! val.isObject()) continue; auto obj = val.toObject(); auto call = obj["callsign"].toString().simplified().toUpper(); auto mode = obj["mode"].toString(); auto rx = Frequency::fromHz(obj["frequency"].toInt()), tx = Frequency::fromHz(rx.inHz() + obj["offset"].toInt()); auto location = QGeoCoordinate(obj["latitude"].toDouble(), obj["longitude"].toDouble()); auto qth = obj["city"].toString(); if ("FM" == mode) { SelectiveCall rxTone, txTone; auto rxDCS = dcsPattern.match(obj["decode"].toString()); auto txDCS = dcsPattern.match(obj["encode"].toString()); if (rxDCS.isValid()) rxTone = SelectiveCall(rxDCS.captured(1).toUInt(), "I" == rxDCS.captured(2)); else if (0 != obj["decode"].toString().toDouble()) rxTone = SelectiveCall(obj["decode"].toString().toDouble()); if (txDCS.isValid()) txTone = SelectiveCall(txDCS.captured(1).toUInt(), "I" == txDCS.captured(2)); else if (0 != obj["encode"].toString().toDouble()) txTone = SelectiveCall(obj["encode"].toString().toDouble()); cache(RepeaterDatabaseEntry::fm(call, rx, tx, location, qth, rxTone, txTone)); } else if ("DMR" == mode) { unsigned int colorCode = 0; auto CC = ccPattern.match(obj["encode"].toString()); if (CC.isValid()) colorCode = CC.captured(1).toUInt(); cache(RepeaterDatabaseEntry::dmr(call, rx, tx, location, qth, colorCode)); } } logDebug() << "Loaded " << _cache.size() << " elements from " << _url.toDisplayString() << "."; return true; } qdmr-0.12.3/src/hearhamrepeatersource.hh000066400000000000000000000005251501654372000202330ustar00rootroot00000000000000#ifndef HEARHAMREPEATERSOURCE_HH #define HEARHAMREPEATERSOURCE_HH #include "repeaterdatabase.hh" class HearhamRepeaterSource : public DownloadableRepeaterDatabaseSource { Q_OBJECT public: explicit HearhamRepeaterSource(QObject *parent = nullptr); protected: bool parse(const QByteArray &doc); }; #endif // HEARHAMREPEATERSOURCE_HH qdmr-0.12.3/src/main.cc000066400000000000000000000013251501654372000145660ustar00rootroot00000000000000#include "application.hh" #include #include #include "mainwindow.hh" #include #include "logger.hh" #include "settings.hh" #include #include int main(int argc, char *argv[]) { QTextStream out(stderr); Logger::get().addHandler(new StreamLogHandler(out)); Application app(argc, argv); //QPixmap pixmap(":/icons/splash.png"); //QSplashScreen splash(pixmap); //splash.show(); //app.processEvents(); QMainWindow *mainWindow = app.mainWindow(); mainWindow->show(); //splash.finish(mainWindow); Settings settings; if (settings.showDisclaimer()) { app.showAbout(); settings.setShowDisclaimer(false); } app.exec(); return 0; } qdmr-0.12.3/src/mainwindow.cc000066400000000000000000000142731501654372000160240ustar00rootroot00000000000000#include "mainwindow.hh" #include "ui_mainwindow.h" #include #include #include #include "settings.hh" #include "logger.hh" #include "application.hh" #include "generalsettingsview.hh" #include "radioidlistview.hh" #include "contactlistview.hh" #include "grouplistsview.hh" #include "channellistview.hh" #include "zonelistview.hh" #include "scanlistsview.hh" #include "positioningsystemlistview.hh" #include "roamingchannellistview.hh" #include "roamingzonelistview.hh" #include "extensionview.hh" #include "talkgroupdatabase.hh" #include "satellitedatabase.hh" MainWindow::MainWindow(Config *config, QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); Application *app = qobject_cast(QApplication::instance()); Settings settings; logDebug() << "Create main window using icon theme '" << QIcon::themeName() << "'."; QProgressBar *progress = new QProgressBar(); progress->setObjectName("progress"); ui->statusbar->addPermanentWidget(progress); progress->setVisible(false); ui->actionWriteSatellites->setIcon(QIcon::fromTheme("device-write-satellite")); connect(ui->actionNewCodeplug, SIGNAL(triggered()), app, SLOT(newCodeplug())); connect(ui->actionOpenCodeplug, SIGNAL(triggered()), app, SLOT(loadCodeplug())); connect(ui->actionSaveCodeplug, SIGNAL(triggered()), app, SLOT(saveCodeplug())); connect(ui->actionExportToCHIRP, SIGNAL(triggered()), app, SLOT(exportCodeplugToChirp())); connect(ui->actionImport, SIGNAL(triggered()), app, SLOT(importCodeplug())); connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(close())); connect(ui->actionAbout, SIGNAL(triggered()), app, SLOT(showAbout())); connect(ui->actionSettings, SIGNAL(triggered()), app, SLOT(showSettings())); connect(ui->actionHelp, SIGNAL(triggered()), app, SLOT(showHelp())); connect(ui->actionRefreshCallsignDB, SIGNAL(triggered()), app->user(), SLOT(download())); connect(ui->actionRefreshTalkgroupDB, SIGNAL(triggered()), app->talkgroup(), SLOT(download())); connect(ui->actionRefreshOrbitalElements, SIGNAL(triggered()), app->satellite(), SLOT(update())); connect(ui->actionEditSatellites, SIGNAL(triggered()), app, SLOT(editSatellites())); connect(ui->actionDetectDevice, SIGNAL(triggered()), app, SLOT(detectRadio())); connect(ui->actionVerifyCodeplug, SIGNAL(triggered()), app, SLOT(verifyCodeplug())); connect(ui->actionDownload, SIGNAL(triggered()), app, SLOT(downloadCodeplug())); connect(ui->actionUpload, SIGNAL(triggered()), app, SLOT(uploadCodeplug())); connect(ui->actionUploadCallsignDB, SIGNAL(triggered()), app, SLOT(uploadCallsignDB())); connect(ui->actionWriteSatellites, SIGNAL(triggered()), app, SLOT(uploadSatellites())); // Wire-up "General Settings" view _generalSettings = new GeneralSettingsView(config); ui->tabs->addTab(_generalSettings, tr("Settings")); if (settings.showCommercialFeatures()) { _generalSettings->hideDMRID(true); } else { _generalSettings->hideDMRID(false); } if (settings.showExtensions()) { _generalSettings->hideExtensions(false); } else { _generalSettings->hideExtensions(true); } // Wire-up "Radio IDs" view _radioIdTab = new RadioIDListView(config); ui->tabs->addTab(_radioIdTab, tr("Radio IDs")); // Wire-up "Contact List" view ui->tabs->addTab(new ContactListView(config), tr("Contacts")); // Wire-up "RX Group List" view ui->tabs->addTab(new GroupListsView(config), tr("Group Lists")); // Wire-up "Channel List" view ui->tabs->addTab(new ChannelListView(config), tr("Channels")); // Wire-up "Zone List" view ui->tabs->addTab(new ZoneListView(config), tr("Zones")); // Wire-up "Scan List" view ui->tabs->addTab(new ScanListsView(config), tr("Scan Lists")); // Wire-up "GPS System List" view ui->tabs->addTab(new PositioningSystemListView(config), tr("GPS/APRS")); // Wire-up "Roaming Zone List" view ui->tabs->addTab(new RoamingChannelListView(config), tr("Roaming Channels")); // Wire-up "Roaming Zone List" view _roamingZoneList = new RoamingZoneListView(config); ui->tabs->addTab(_roamingZoneList, tr("Roaming Zones")); // Wire-up "extension view" _extensionView = new ExtensionView(); _extensionView->setObject(config, config); ui->tabs->addTab(_extensionView, tr("Extensions")); if (! settings.showCommercialFeatures()) { ui->tabs->removeTab(ui->tabs->indexOf(_radioIdTab)); _radioIdTab->setHidden(true); } if (! settings.showExtensions()) { ui->tabs->removeTab(ui->tabs->indexOf(_extensionView)); _extensionView->setHidden(true); } restoreGeometry(settings.mainWindowState()); } void MainWindow::applySettings() { Settings settings; // Handle commercial features if (settings.showCommercialFeatures()) { if (-1 == ui->tabs->indexOf(_radioIdTab)) { ui->tabs->insertTab(ui->tabs->indexOf(_generalSettings)+1, _radioIdTab, tr("Radio IDs")); update(); } _generalSettings->hideDMRID(true); } else if (! settings.showCommercialFeatures()) { if (-1 != ui->tabs->indexOf(_radioIdTab)) { ui->tabs->removeTab(ui->tabs->indexOf(_radioIdTab)); update(); } _generalSettings->hideDMRID(false); } // Handle extensions if (settings.showExtensions()) { if (-1 == ui->tabs->indexOf(_extensionView)) { ui->tabs->insertTab(ui->tabs->indexOf(_roamingZoneList)+1, _extensionView, tr("Extensions")); update(); } _generalSettings->hideExtensions(false); } else { if (-1 != ui->tabs->indexOf(_extensionView)) { ui->tabs->removeTab(ui->tabs->indexOf(_extensionView)); update(); } _generalSettings->hideExtensions(true); } } void MainWindow::closeEvent(QCloseEvent *event) { if (qobject_cast(Application::instance())->isModified()) { if (QMessageBox::Ok != QMessageBox::question(nullptr, tr("Unsaved changes to codeplug."), tr("There are unsaved changes to the current codeplug. " "These changes are lost if you proceed."), QMessageBox::Cancel|QMessageBox::Ok)) event->ignore(); return; } Settings().setMainWindowState(saveGeometry()); event->accept(); } qdmr-0.12.3/src/mainwindow.hh000066400000000000000000000012001501654372000160200ustar00rootroot00000000000000#ifndef MAINWINDOW_HH #define MAINWINDOW_HH #include class Config; class GeneralSettingsView; class RadioIDListView; class RoamingZoneListView; class ExtensionView; namespace Ui { class MainWindow; } class MainWindow: public QMainWindow { Q_OBJECT public: explicit MainWindow(Config *config, QWidget *parent=nullptr); public slots: void applySettings(); protected: void closeEvent(QCloseEvent *event); private: Ui::MainWindow *ui; GeneralSettingsView *_generalSettings; RadioIDListView *_radioIdTab; RoamingZoneListView *_roamingZoneList; ExtensionView *_extensionView; }; #endif // MAINWINDOW_HH qdmr-0.12.3/src/mainwindow.ui000066400000000000000000000260551501654372000160550ustar00rootroot00000000000000 MainWindow 0 0 800 600 800 600 qdmr [*] .. true true 0 0 0 0 0 Tablist There are several tabs providing general settings, contact, Rx group, channel, zone and scan lists. -1 0 0 800 22 File Device Help Databases false toolBar Toolbar TopToolBarArea false .. New Creates a new Codeplug. Ctrl+N .. Open ... <html><head/><body><p>Imports a codeplug from &quot;conf&quot; files.</p></body></html> Ctrl+O .. Save ... <html><head/><body><p>Saves the codeplug in a &quot;conf&quot; file.</p></body></html> Ctrl+S .. Quit Quits the application. Ctrl+Q .. Detect Detect connected radios. .. Verify <html><head/><body><p>Verifies the current codeplug with connected radios.</p></body></html> Ctrl+R .. Read Reads a codeplug from connected radios. .. Write Writes the codeplug to the connected radio. .. About qdmr .. Help Read the handbook. F1 .. Settings Shows settings dialog .. Write Callsign DB Writes call-sign DB to radio. .. Refresh Callsign DB Refreshes the downloaded callsign DB .. Refresh Talkgroup DB Refreshes the downloaded talkgroup DB Export to CHIRP ... Exports all FM channels to CHRIP CSV. Import ... Imports and merges a codeplug into the current one. Refresh Orbital Elements Refreshes the orbital elements. true Edit Satellites ... Opens an editor to edit your satellite database. true Write satellites Writes the orbital elements and transponder information onto the connected device. true qdmr-0.12.3/src/positioningsystemlistview.cc000066400000000000000000000100071501654372000212350ustar00rootroot00000000000000#include "positioningsystemlistview.hh" #include "ui_positioningsystemlistview.h" #include "config.hh" #include "aprssystemdialog.hh" #include "gpssystemdialog.hh" #include "settings.hh" #include #include PositioningSystemListView::PositioningSystemListView(Config *config, QWidget *parent) : QWidget(parent), ui(new Ui::PositioningSystemListView), _config(config) { Settings settings; ui->setupUi(this); connect(ui->listView->header(), SIGNAL(sectionCountChanged(int,int)), this, SLOT(loadPositioningSectionState())); connect(ui->listView->header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(storePositioningSectionState())); ui->listView->setModel(new PositioningSystemListWrapper(_config->posSystems(), ui->listView)); if (settings.hideGSPNote()) ui->gpsNote->setVisible(false); connect(ui->addGPS, SIGNAL(clicked()), this, SLOT(onAddGPS())); connect(ui->addAPRS, SIGNAL(clicked()), this, SLOT(onAddAPRS())); connect(ui->remGPS, SIGNAL(clicked()), this, SLOT(onRemGPS())); connect(ui->listView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditGPS(unsigned))); connect(ui->gpsNote, SIGNAL(linkActivated(QString)), this, SLOT(onHideGPSNote())); } PositioningSystemListView::~PositioningSystemListView() { delete ui; } void PositioningSystemListView::onAddGPS() { GPSSystemDialog dialog(_config); if (QDialog::Accepted != dialog.exec()) return; int row=-1; if (ui->listView->hasSelection()) row = ui->listView->selection().second+1; _config->posSystems()->add(dialog.gpsSystem(), row); } void PositioningSystemListView::onAddAPRS() { APRSSystemDialog dialog(_config); if (QDialog::Accepted != dialog.exec()) return; int row=-1; if (ui->listView->hasSelection()) row = ui->listView->selection().second+1; _config->posSystems()->add(dialog.aprsSystem(), row); } void PositioningSystemListView::onRemGPS() { if (! ui->listView->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot delete GPS system"), tr("Cannot delete GPS system: You have to select a GPS system first.")); return; } // Get selection and ask for deletion QPair rows = ui->listView->selection(); int rowcount = rows.second-rows.first+1; if (rows.first == rows.second) { QString name = _config->posSystems()->system(rows.first)->name(); if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete positioning system?"), tr("Delete positioning system %1?").arg(name))) return; } else { if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete positioning system?"), tr("Delete %1 positioning systems?").arg(rowcount))) return; } // collect all selected systems // need to collect them first as rows change when deleting systems QList systems; systems.reserve(rowcount); for(int row=rows.first; row<=rows.second; row++) systems.push_back(_config->posSystems()->system(row)); // remove systems foreach (PositioningSystem *system, systems) _config->posSystems()->del(system); } void PositioningSystemListView::onEditGPS(unsigned row) { PositioningSystem *sys = _config->posSystems()->system(row); if (sys->is()) { GPSSystemDialog dialog(_config, sys->as()); if (QDialog::Accepted != dialog.exec()) return; dialog.gpsSystem(); } else if (sys->is()) { APRSSystemDialog dialog(_config, sys->as()); if (QDialog::Accepted != dialog.exec()) return; dialog.aprsSystem(); } } void PositioningSystemListView::onHideGPSNote() { Settings setting; setting.setHideGPSNote(true); ui->gpsNote->setVisible(false); } void PositioningSystemListView::loadPositioningSectionState() { Settings settings; ui->listView->header()->restoreState(settings.headerState("positioningList")); } void PositioningSystemListView::storePositioningSectionState() { Settings settings; settings.setHeaderState("positioningList", ui->listView->header()->saveState()); } qdmr-0.12.3/src/positioningsystemlistview.hh000066400000000000000000000012261501654372000212520ustar00rootroot00000000000000#ifndef POSITIONINGSYSTEMLISTVIEW_HH #define POSITIONINGSYSTEMLISTVIEW_HH #include class Config; namespace Ui { class PositioningSystemListView; } class PositioningSystemListView : public QWidget { Q_OBJECT public: explicit PositioningSystemListView(Config *config, QWidget *parent = nullptr); ~PositioningSystemListView(); protected slots: void onAddGPS(); void onAddAPRS(); void onRemGPS(); void onEditGPS(unsigned); void onHideGPSNote(); void loadPositioningSectionState(); void storePositioningSectionState(); private: Ui::PositioningSystemListView *ui; Config *_config; }; #endif // POSITIONINGSYSTEMLISTVIEW_HH qdmr-0.12.3/src/positioningsystemlistview.ui000066400000000000000000000070411501654372000212710ustar00rootroot00000000000000 PositioningSystemListView 0 0 445 300 Form padding:10px;border: 2px solid black; border-radius: 10px; QFrame::NoFrame QFrame::Plain 2 0 <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support GPS or APRS. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Qt::RichText true 5 -1 false Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse 0 0 Add GPS System Alt+G true Add APRS System Alt+A Delete Position System Alt+- ConfigObjectTableView QWidget
      configobjecttableview.hh
      1
      qdmr-0.12.3/src/propertydelegate.cc000066400000000000000000000140611501654372000172220ustar00rootroot00000000000000#include "propertydelegate.hh" #include "extensionwrapper.hh" #include #include #include #include #include "configreference.hh" #include "extensionwrapper.hh" #include "config.hh" #include "logger.hh" #include "frequency.hh" #include "interval.hh" PropertyDelegate::PropertyDelegate(QObject *parent) : QItemDelegate(parent), _config(nullptr) { // pass... } void PropertyDelegate::setConfig(Config *config) { _config = config; } const PropertyWrapper * PropertyDelegate::getModel(const QAbstractItemModel *model) { while (const QAbstractProxyModel *proxy = qobject_cast(model)) model = proxy->sourceModel(); return qobject_cast(model); } QWidget * PropertyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option) const PropertyWrapper *model = getModel(index.model()); QMetaProperty prop = model->propertyAt(index); ConfigItem *obj = model->parentObject(index); if (! prop.isValid()) return nullptr; // Dispatch by type if (prop.isEnumType()) { return new QComboBox(parent); } else if (QVariant::Bool == prop.type()) { return new QComboBox(parent); } else if (QVariant::Int == prop.type()) { QSpinBox *edit = new QSpinBox(parent); edit->setMinimum(std::numeric_limits::min()); edit->setMaximum(std::numeric_limits::max()); return edit; } else if (QVariant::UInt == prop.type()) { QSpinBox *edit = new QSpinBox(parent); edit->setMinimum(0); edit->setMaximum(std::numeric_limits::max()); return edit; } else if (QVariant::Double == prop.type()) { QLineEdit *edit = new QLineEdit(parent); edit->setValidator(new QDoubleValidator(edit)); return edit; } else if (QVariant::String == prop.type()) { return new QLineEdit(parent); } else if (QString("Frequency") == prop.typeName()) { return new QLineEdit(parent); } else if (QString("Interval") == prop.typeName()) { return new QLineEdit(parent); } else if (prop.read(obj).value()) { return new QComboBox(parent); } else if (propIsInstance(prop)) { return nullptr; } return nullptr; } void PropertyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { const PropertyWrapper *model = getModel(index.model()); ConfigItem *obj = model->parentObject(index); QMetaProperty prop = model->propertyAt(index); if (! prop.isValid()) return; // Dispatch by type if (prop.isEnumType()) { QComboBox *box = dynamic_cast(editor); QMetaEnum etype = prop.enumerator(); for (int i=0; iaddItem(etype.key(i), QVariant(etype.value(i))); if (etype.value(i) == prop.read(obj).toInt()) box->setCurrentIndex(i); } } else if (QVariant::Bool == prop.type()) { QComboBox *box = dynamic_cast(editor); box->addItem(tr("False"), false); box->addItem(tr("True"), true); if (prop.read(obj).toBool()) box->setCurrentIndex(1); else box->setCurrentIndex(0); } else if ((QVariant::Int == prop.type()) || (QVariant::UInt == prop.type())) { dynamic_cast(editor)->setValue(prop.read(obj).toInt()); } else if (QVariant::Double == prop.type()) { dynamic_cast(editor)->setText(QString::number(prop.read(obj).toDouble())); } else if (QVariant::String == prop.type()) { dynamic_cast(editor)->setText(prop.read(obj).toString()); } else if (QString("Frequency") == prop.typeName()) { dynamic_cast(editor)->setText(prop.read(obj).value().format()); } else if (QString("Interval") == prop.typeName()) { dynamic_cast(editor)->setText(prop.read(obj).value().format()); } else if (prop.read(obj).value()) { ConfigObjectReference *ref = prop.read(obj).value(); // Find all matching elements in config that can be referenced QSet items; if (_config) _config->findItemsOfTypes(ref->elementTypeNames(), items); // Assemble combo box with references QComboBox *box = dynamic_cast(editor); box->addItem(tr("[None]"), QVariant::fromValue(nullptr)); foreach (ConfigItem *item, items) { if (! item->is()) continue; ConfigObject *o = item->as(); box->addItem(o->name(),QVariant::fromValue(o)); } } } void PropertyDelegate::setModelData(QWidget *editor, QAbstractItemModel *abstractmodel, const QModelIndex &index) const { Q_UNUSED(abstractmodel); const PropertyWrapper *model = getModel(index.model()); ConfigItem *obj = model->parentObject(index); QMetaProperty prop = model->propertyAt(index); if (! prop.isValid()) return; // Dispatch by type if (prop.isEnumType()) { prop.write(obj, dynamic_cast(editor)->currentData()); } else if (QVariant::Bool == prop.type()) { prop.write(obj, dynamic_cast(editor)->currentData()); } else if ((QVariant::Int == prop.type()) || (QVariant::UInt == prop.type())) { prop.write(obj, dynamic_cast(editor)->value()); } else if (QVariant::Double == prop.type()) { prop.write(obj, dynamic_cast(editor)->text().toDouble()); } else if (QVariant::String == prop.type()) { prop.write(obj, dynamic_cast(editor)->text()); } else if (QString("Frequency") == prop.typeName()) { Frequency f; if (f.parse(dynamic_cast(editor)->text())) prop.write(obj, QVariant::fromValue(f)); } else if (QString("Interval") == prop.typeName()) { Interval I; if (I.parse(dynamic_cast(editor)->text())) prop.write(obj, QVariant::fromValue(I)); } else if (prop.read(obj).value()) { ConfigObjectReference *ref = prop.read(obj).value(); ref->set(dynamic_cast(editor)->currentData().value()); } } qdmr-0.12.3/src/propertydelegate.hh000066400000000000000000000013151501654372000172320ustar00rootroot00000000000000#ifndef PROPERTYDELEGATE_HH #define PROPERTYDELEGATE_HH #include class Config; class PropertyWrapper; class PropertyDelegate : public QItemDelegate { Q_OBJECT public: explicit PropertyDelegate(QObject *parent=nullptr); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *abstractmodel, const QModelIndex &index) const; void setConfig(Config *config); protected: static const PropertyWrapper *getModel(const QAbstractItemModel *model); protected: Config *_config; }; #endif // PROPERTYDELEGATE_HH qdmr-0.12.3/src/radioidlistview.cc000066400000000000000000000071421501654372000170470ustar00rootroot00000000000000#include "radioidlistview.hh" #include "ui_radioidlistview.h" #include "config.hh" #include "configobjecttableview.hh" #include "configitemwrapper.hh" #include "dmriddialog.hh" #include "settings.hh" #include #include RadioIDListView::RadioIDListView(Config *config, QWidget *parent) : QWidget(parent), ui(new Ui::RadioIDListView), _config(config) { ui->setupUi(this); connect(ui->listView->header(), SIGNAL(sectionCountChanged(int,int)), this, SLOT(loadHeaderState())); connect(ui->listView->header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(storeHeaderState())); ui->listView->setModel(new RadioIdListWrapper(_config->radioIDs(), ui->listView)); ui->defaultID->setModel(new RadioIdListWrapper(_config->radioIDs(), ui->defaultID)); ui->defaultID->setModelColumn(1); connect(_config, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModified())); connect(ui->addID, SIGNAL(clicked(bool)), this, SLOT(onAddID())); connect(ui->delID, SIGNAL(clicked(bool)), this, SLOT(onDeleteID())); connect(ui->listView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditID(unsigned))); connect(ui->defaultID, SIGNAL(currentIndexChanged(int)), this, SLOT(onDefaultIDSelected(int))); } RadioIDListView::~RadioIDListView() { delete ui; } void RadioIDListView::onConfigModified() { if (_config->settings()->defaultId()) { ui->defaultID->setCurrentIndex(_config->radioIDs()->indexOf(_config->settings()->defaultId())); } else { ui->defaultID->setCurrentIndex(-1); } } void RadioIDListView::onAddID() { DMRIDDialog dialog(_config); if (QDialog::Accepted != dialog.exec()) return; int row = -1; if (ui->listView->hasSelection()) row = ui->listView->selection().second; _config->radioIDs()->add(dialog.radioId(), row); } void RadioIDListView::onDeleteID() { if (! ui->listView->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot delete radio IDs"), tr("Cannot delete radio IDs: You have to select a radio ID first.")); return; } // Get selection and ask for deletion QPair rows = ui->listView->selection(); int numrows = rows.second-rows.first+1; if (rows.first == rows.second) { QString name = _config->radioIDs()->getId(rows.first)->name(); if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete radio ID?"), tr("Delete radio ID %1?").arg(name))) return; } else { if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete scan lists?"), tr("Delete %1 scan lists?").arg(numrows))) return; } // collect all selected scan lists // need to collect them first as rows change when deleting QList ids; ids.reserve(numrows); for(int i=rows.first; i<=rows.second; i++) ids.push_back(_config->radioIDs()->getId(i)); // remove foreach (DMRRadioID *id, ids) _config->radioIDs()->del(id); } void RadioIDListView::onEditID(unsigned row) { if (int(row) >= _config->radioIDs()->count()) return; DMRRadioID *id = _config->radioIDs()->getId(row); DMRIDDialog dialog(id, _config); if (QDialog::Accepted != dialog.exec()) return; // Apply changes dialog.radioId(); } void RadioIDListView::onDefaultIDSelected(int idx) { if ((idx < 0) || (idx >= _config->radioIDs()->count())) return; _config->settings()->setDefaultId(_config->radioIDs()->getId(idx)); } void RadioIDListView::loadHeaderState() { ui->listView->header()->restoreState(Settings().headerState("radioIDList")); } void RadioIDListView::storeHeaderState() { Settings().setHeaderState("radioIDList", ui->listView->header()->saveState()); } qdmr-0.12.3/src/radioidlistview.hh000066400000000000000000000011061501654372000170530ustar00rootroot00000000000000#ifndef RADIOIDLISTVIEW_HH #define RADIOIDLISTVIEW_HH #include class Config; namespace Ui { class RadioIDListView; } class RadioIDListView : public QWidget { Q_OBJECT public: explicit RadioIDListView(Config *config, QWidget *parent = nullptr); ~RadioIDListView(); protected slots: void onConfigModified(); void onAddID(); void onDeleteID(); void onEditID(unsigned row); void onDefaultIDSelected(int idx); void loadHeaderState(); void storeHeaderState(); private: Ui::RadioIDListView *ui; Config *_config; }; #endif // RADIOIDLISTVIEW_HH qdmr-0.12.3/src/radioidlistview.ui000066400000000000000000000034311501654372000170740ustar00rootroot00000000000000 RadioIDListView 0 0 400 300 Form Default Radio ID 0 0 Add Radio ID Delete Radio ID ConfigObjectTableView QWidget
      configobjecttableview.hh
      1
      qdmr-0.12.3/src/radioidrepeatersource.cc000066400000000000000000000032461501654372000202320ustar00rootroot00000000000000#include "radioidrepeatersource.hh" #include #include #include #include #include #include "logger.hh" RadioidRepeaterSource::RadioidRepeaterSource(QObject *parent) : DownloadableRepeaterDatabaseSource( "radioidrepeater.cache.json", QUrl("https://radioid.net/static/map.json"), 5, parent) { // pass... } bool RadioidRepeaterSource::parse(const QByteArray &json) { QJsonParseError err; auto doc = QJsonDocument::fromJson(json, &err); if (doc.isNull()) { logError() << "Cannot received JSON: " << err.errorString() << "."; return false; } if ((! doc.isObject()) || (! doc.object().contains("markers")) || (! doc.object()["markers"].isArray())) { logError() << "Malformed result."; return false; } QJsonArray list = doc.object()["markers"].toArray(); for (auto val: list) { if (! val.isObject()) continue; auto repeater = val.toObject(); if ("ACTIVE" != repeater["status"].toString()) continue; auto call = repeater["callsign"].toString().simplified().toUpper(); auto rx = Frequency::fromMHz(repeater["frequency"].toString().toDouble()); auto tx = Frequency::fromMHz(rx.inMHz() + repeater["offset"].toString().toDouble()); auto colorCode = repeater["color_code"].toInt(); auto position = QGeoCoordinate(repeater["lat"].toString().toDouble(), repeater["lng"].toString().toDouble()); auto qth = repeater["city"].toString(); cache(RepeaterDatabaseEntry::dmr(call, rx, tx, position, qth, colorCode)); } logDebug() << "Loaded " << _cache.size() << " elements from " << _url.toDisplayString() << "."; return true; } qdmr-0.12.3/src/radioidrepeatersource.hh000066400000000000000000000005251501654372000202410ustar00rootroot00000000000000#ifndef RADIOIDREPEATERSOURCE_HH #define RADIOIDREPEATERSOURCE_HH #include "repeaterdatabase.hh" class RadioidRepeaterSource : public DownloadableRepeaterDatabaseSource { Q_OBJECT public: explicit RadioidRepeaterSource(QObject *parent = nullptr); protected: bool parse(const QByteArray &doc); }; #endif // RADIOIDREPEATERSOURCE_HH qdmr-0.12.3/src/radioselectiondialog.cc000066400000000000000000000014211501654372000200230ustar00rootroot00000000000000#include "radioselectiondialog.hh" #include "ui_radioselectiondialog.h" RadioSelectionDialog::RadioSelectionDialog(const USBDeviceDescriptor &device, QWidget *parent) : QDialog(parent), ui(new Ui::RadioSelectionDialog) { ui->setupUi(this); _radios = RadioInfo::allRadios(device); foreach (RadioInfo info, _radios) { ui->radios->addItem(QString("%1 %2").arg(info.manufacturer(), info.name())); } if (!_radios.isEmpty()) ui->radios->setCurrentIndex(0); connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); } RadioSelectionDialog::~RadioSelectionDialog() { delete ui; } RadioInfo RadioSelectionDialog::radioInfo() const { return _radios.at(ui->radios->currentIndex()); } qdmr-0.12.3/src/radioselectiondialog.hh000066400000000000000000000007751501654372000200500ustar00rootroot00000000000000#ifndef RADIOSELECTIONDIALOG_HH #define RADIOSELECTIONDIALOG_HH #include #include "usbdevice.hh" #include "radioinfo.hh" namespace Ui { class RadioSelectionDialog; } class RadioSelectionDialog : public QDialog { Q_OBJECT public: explicit RadioSelectionDialog(const USBDeviceDescriptor &device, QWidget *parent = nullptr); ~RadioSelectionDialog(); RadioInfo radioInfo() const; private: Ui::RadioSelectionDialog *ui; QList _radios; }; #endif // RADIOSELECTIONDIALOG_HH qdmr-0.12.3/src/radioselectiondialog.ui000066400000000000000000000042001501654372000200510ustar00rootroot00000000000000 RadioSelectionDialog 0 0 400 300 Cannot auto-detect radio Select a specific radio 0 0 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() RadioSelectionDialog accept() 248 254 157 274 buttonBox rejected() RadioSelectionDialog reject() 316 260 286 274 qdmr-0.12.3/src/releasenotes.cc000066400000000000000000000032271501654372000163360ustar00rootroot00000000000000#include "releasenotes.hh" #include "config.h" #include "settings.hh" #include "logger.hh" #include #include #include #include ReleaseNotes::ReleaseNotes(QObject *parent) : QObject(parent) { connect(&_net, SIGNAL(finished(QNetworkReply*)), this, SLOT(onResponse(QNetworkReply*))); } void ReleaseNotes::checkForUpdate() { Settings settings; if (! settings.isUpdated()) return; QString url = QString("https://api.github.com/repos/hmatuschek/qdmr/releases/tags/v%1"); _net.get(QNetworkRequest(QUrl(url.arg(VERSION_STRING)))); } void ReleaseNotes::onResponse(QNetworkReply *reply) { if (reply->error()) { show(tr("Cannot download release notes from https://github.com/hmatuschek/qdmr\n\t %1") .arg(reply->errorString())); return; } QByteArray data = reply->readAll(); QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (QJsonParseError::NoError != err.error) { logWarn() << "JSON error in release data: " << err.errorString(); } if (! doc.isObject()) { show(tr("Cannot read release notes from https://github.com/hmatuschek/qdmr\n\t" "Release is not a JSON object!")); return; } if (! doc.object().contains("body")) { show(tr("Cannot read release notes from https://github.com/hmatuschek/qdmr\n\t" "Release does not contain a release note.")); return; } show(doc.object()["body"].toString()); } void ReleaseNotes::show(const QString ¬es) { QMessageBox::information( nullptr, tr("qDMR was updated to version %1").arg(VERSION_STRING), notes); Settings().markUpdated(); } qdmr-0.12.3/src/releasenotes.hh000066400000000000000000000006441501654372000163500ustar00rootroot00000000000000#ifndef RELEASENOTES_HH #define RELEASENOTES_HH #include #include class ReleaseNotes : public QObject { Q_OBJECT public: explicit ReleaseNotes(QObject *parent = nullptr); void checkForUpdate(); public slots: void show(const QString ¬es); protected slots: void onResponse(QNetworkReply *reply); protected: QNetworkAccessManager _net; }; #endif // RELEASENOTES_HH qdmr-0.12.3/src/repeaterbooksource.cc000066400000000000000000000111231501654372000175420ustar00rootroot00000000000000#include "repeaterbooksource.hh" #include #include #include #include #include #include #include #include #include "logger.hh" #include "settings.hh" #include "config.h" RepeaterBookSource::RepeaterBookSource(QObject *parent) : CachedRepeaterDatabaseSource("repeaterbook.cache.json", parent), _network(), _currentReply(nullptr), _callsignPattern(R"re(([a-z]|[a-z0-9][a-z]|[a-z][a-z0-9])[0-9]+[a-z]*)re", QRegularExpression::CaseInsensitiveOption) { connect(&_network, SIGNAL(finished(QNetworkReply*)), this, SLOT(onRequestFinished(QNetworkReply*))); } bool RepeaterBookSource::load(const QString &queryCall, const QGeoCoordinate &pos) { Q_UNUSED(pos); // Cancel running requests if (_currentReply) _currentReply->abort(); QRegularExpressionMatch match = _callsignPattern.match(queryCall); if (! match.hasMatch()) return false; QString call = match.captured().toUpper(); logDebug() << "Search for (partial) call '" << call << "'."; QUrl url; if (Region::World == Settings().repeaterBookRegion()) url = QUrl("https://www.repeaterbook.com/api/exportROW.php"); else url = QUrl("https://www.repeaterbook.com/api/export.php"); QUrlQuery query; query.addQueryItem("callsign", QString("%1%").arg(call)); url.setQuery(query); QNetworkRequest request(url); request.setHeader(QNetworkRequest::UserAgentHeader, "qdmr " VERSION_STRING " https://dm3mat.darc.de/qdmr/"); logDebug() << "Query RepeaterBook at " << url.toString() << " as '" << request.header(QNetworkRequest::UserAgentHeader).toString() << "'."; _currentReply = _network.get(request); return true; } void RepeaterBookSource::onRequestFinished(QNetworkReply *reply) { if (reply->error()) { logError() << "Cannot download repeater list: " << reply->errorString(); reply->deleteLater(); _currentReply = nullptr; return; } QByteArray content = reply->readAll(); QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(content, &err); if (doc.isNull()) { logError() << "Cannot parse response: " << err.errorString() << "."; logDebug() << "Got '" << content << "'."; reply->deleteLater(); _currentReply = nullptr; return; } QString query = QUrlQuery(reply->request().url()) .queryItemValue("callsign", QUrl::FullyDecoded).remove("%"); reply->deleteLater(); _currentReply = nullptr; if ((! doc.isObject()) || (! doc.object().contains("results")) || (! doc.object()["results"].isArray())) { logError() << "Cannot parse response: Unexpected structure."; reply->deleteLater(); _currentReply = nullptr; return; } QJsonArray results = doc.object()["results"].toArray(); foreach (const QJsonValue &rep, results) { if (! rep.isObject()) continue; QJsonObject obj = rep.toObject(); // Handle basic properties auto call = obj["Callsign"].toString().simplified().toUpper(); auto rxFrequency = Frequency::fromMHz(obj["Frequency"].toString().toDouble()); auto txFrequency = Frequency::fromMHz(obj["Input Freq"].toString().toDouble()); auto location = QGeoCoordinate(obj["Lat"].toString().toDouble(), obj["Long"].toString().toDouble()); auto qth = obj["Nearest City"].toString(); auto type = RepeaterDatabaseEntry::Type::Invalid; SelectiveCall rxTone, txTone; unsigned int colorCode = 0; QDateTime updated; if (obj["FM Analog"].toString() == "Yes") { type = RepeaterDatabaseEntry::Type::FM; if (obj.contains("PL")) txTone = SelectiveCall(obj["PL"].toString().toDouble()); if (obj.contains("TSQ")) rxTone = SelectiveCall(obj["TSQ"].toString().toDouble()); } if (obj["DMR"].toString() == "Yes") { type = RepeaterDatabaseEntry::Type::DMR; if (obj.contains("DMR Color Code")) colorCode = obj["DMR Color Code"].toString().toInt(); } if (obj.contains("timestamp")) updated = QDateTime::fromString(obj["timestamp"].toString(), Qt::ISODate); if (RepeaterDatabaseEntry::Type::FM == type) { cache(RepeaterDatabaseEntry::fm(call, rxFrequency, txFrequency, location, qth, rxTone, txTone, updated)); } else if (RepeaterDatabaseEntry::Type::DMR == type) { cache(RepeaterDatabaseEntry::dmr(call, rxFrequency, txFrequency, location, qth, colorCode, updated)); } } logDebug() << "Updated repeater cache with " << results.count() << " entries."; saveCache(); } qdmr-0.12.3/src/repeaterbooksource.hh000066400000000000000000000011711501654372000175560ustar00rootroot00000000000000#ifndef REPEATERBOOKSOURCE_HH #define REPEATERBOOKSOURCE_HH #include "repeaterdatabase.hh" #include class RepeaterBookSource : public CachedRepeaterDatabaseSource { Q_OBJECT public: enum Region { World, NorthAmerica }; Q_ENUM(Region) public: explicit RepeaterBookSource(QObject *parent = nullptr); protected slots: void onRequestFinished(QNetworkReply *reply); protected: bool load(const QString &call, const QGeoCoordinate &pos); protected: QNetworkAccessManager _network; QNetworkReply *_currentReply; QRegularExpression _callsignPattern; }; #endif // REPEATERBOOKSOURCE_HH qdmr-0.12.3/src/repeatercompleter.cc000066400000000000000000000055201501654372000173650ustar00rootroot00000000000000#include "repeatercompleter.hh" #include "repeaterdatabase.hh" /* ********************************************************************************************* * * RepeaterCompleter * ********************************************************************************************* */ RepeaterCompleter::RepeaterCompleter(int minPrefixLength, RepeaterDatabase *repeater, QObject *parent) : QCompleter(parent), _repeaters(repeater), _minPrefixLength(minPrefixLength) { setModel(_repeaters); setCaseSensitivity(Qt::CaseInsensitive); } QStringList RepeaterCompleter::splitPath(const QString &path) const { if (path.length() >= _minPrefixLength) _repeaters->query(path); return QCompleter::splitPath(path); } /* ********************************************************************************************* * * NearestRepeaterFilter * ********************************************************************************************* */ NearestRepeaterFilter::NearestRepeaterFilter(RepeaterDatabase *repeater, const QGeoCoordinate &location, QObject *parent) : QSortFilterProxyModel(parent), _repeater(repeater), _location(location) { setSourceModel(repeater); sort(0); } bool NearestRepeaterFilter::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const { double ldist = _location.distanceTo(_repeater->get(source_left.row()).location()); double rdist = _location.distanceTo(_repeater->get(source_right.row()).location()); return ldist < rdist; } /* ********************************************************************************************* * * DMRRepeaterFilter * ********************************************************************************************* */ DMRRepeaterFilter::DMRRepeaterFilter(RepeaterDatabase *repeater, const QGeoCoordinate &location, QObject *parent) : NearestRepeaterFilter(repeater, location, parent) { invalidateFilter(); } bool DMRRepeaterFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { Q_UNUSED(source_parent); if (source_row >= _repeater->rowCount(QModelIndex())) return false; return RepeaterDatabaseEntry::Type::DMR == _repeater->get(source_row).type(); } /* ********************************************************************************************* * * FMRepeaterFilter * ********************************************************************************************* */ FMRepeaterFilter::FMRepeaterFilter(RepeaterDatabase *repeater, const QGeoCoordinate &location, QObject *parent) : NearestRepeaterFilter(repeater, location, parent) { invalidateFilter(); } bool FMRepeaterFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { Q_UNUSED(source_parent); if (source_row >= _repeater->rowCount(QModelIndex())) return false; return RepeaterDatabaseEntry::Type::FM == _repeater->get(source_row).type(); } qdmr-0.12.3/src/repeatercompleter.hh000066400000000000000000000031431501654372000173760ustar00rootroot00000000000000#ifndef REPEATERCOMPLETER_HH #define REPEATERCOMPLETER_HH #include #include #include class RepeaterDatabase; class RepeaterCompleter: public QCompleter { Q_OBJECT public: explicit RepeaterCompleter(int minPrefixLength, RepeaterDatabase *repeater, QObject *parent=nullptr); QStringList splitPath(const QString &path) const; protected: RepeaterDatabase *_repeaters; int _minPrefixLength; }; class NearestRepeaterFilter: public QSortFilterProxyModel { Q_OBJECT public: /** Constructor. */ explicit NearestRepeaterFilter(RepeaterDatabase *repeater, const QGeoCoordinate &location, QObject *parent=nullptr); protected: bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; protected: RepeaterDatabase *_repeater; QGeoCoordinate _location; }; /** A filter proxy for DMR repeaters. * @ingroup util */ class DMRRepeaterFilter: public NearestRepeaterFilter { Q_OBJECT public: /** Constructor. */ explicit DMRRepeaterFilter(RepeaterDatabase *repeater, const QGeoCoordinate &location, QObject *parent=nullptr); protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; }; /** A filter proxy for analog FM repeaters. * @ingroup util */ class FMRepeaterFilter: public NearestRepeaterFilter { Q_OBJECT public: /** Constructor. */ explicit FMRepeaterFilter(RepeaterDatabase *repeater, const QGeoCoordinate &location, QObject *parent=nullptr); protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; }; #endif // REPEATERCOMPLETER_HH qdmr-0.12.3/src/repeaterdatabase.cc000066400000000000000000000417331501654372000171450ustar00rootroot00000000000000#include "repeaterdatabase.hh" #include #include #include #include #include #include #include #include "logger.hh" #include "utils.hh" /* ********************************************************************************************* * * Helper functions * ********************************************************************************************* */ static const QSet _aprs_frequencies = { 144.390, 144.575, 144.660, 144.800, 144.930, 145.175, 145.570, 432.500 }; inline QString bandName(const Frequency &F) { if (30 >= F.inMHz()) return "HF"; else if (300 >= F.inMHz()) return "VHF"; else if (3000 >= F.inMHz()) return "UHF"; else if (30000 >= F.inMHz()) return "SHF"; return "EHF"; } inline QString bandName(const Frequency &rx, const Frequency & tx) { QString rband = bandName(rx), tband=bandName(tx); if ((rx == tx) && (_aprs_frequencies.contains(rx.inMHz()))) return "APRS"; else if (rband == tband) return rband; return QString("%1/%2").arg(rband,tband); } /* ********************************************************************************************* * * Implementation of RepeaterDatabaseEntry * ********************************************************************************************* */ RepeaterDatabaseEntry::RepeaterDatabaseEntry(const QString &call, const Frequency &rxFrequency, const Frequency &txFrequency, const QGeoCoordinate &location, const QString &qth, const SelectiveCall &rxTone, const SelectiveCall &txTone, const QDateTime &updated, const QDateTime& loaded) : _type(Type::FM), _call(call), _rxFrequency(rxFrequency), _txFrequency(txFrequency), _location(location), _qth(qth), _rxTone(rxTone), _txTone(txTone), _colorCode(0), _updated(updated), _loaded(loaded) { // pass... } RepeaterDatabaseEntry::RepeaterDatabaseEntry(const QString &call, const Frequency &rxFrequency, const Frequency &txFrequency, const QGeoCoordinate &location, const QString &qth, unsigned int colorCode, const QDateTime &updated, const QDateTime& loaded) : _type(Type::DMR), _call(call), _rxFrequency(rxFrequency), _txFrequency(txFrequency), _location(location), _qth(qth), _rxTone(), _txTone(), _colorCode(colorCode), _updated(updated), _loaded(loaded) { // pass... } RepeaterDatabaseEntry::RepeaterDatabaseEntry() : _type(Type::Invalid), _call(), _rxFrequency(), _txFrequency(), _location(), _rxTone(), _txTone(), _colorCode(0), _updated() { // pass... } bool RepeaterDatabaseEntry::operator==(const RepeaterDatabaseEntry &other) const { if (other._type != _type) return false; // Invalid entries are all equal if (Type::Invalid == _type) return true; return (other._call == _call) && (other._rxFrequency == _rxFrequency); } bool RepeaterDatabaseEntry::operator<(const RepeaterDatabaseEntry &other) const { return (_type < other._type) || (_call < other._call) || (_rxFrequency < other._rxFrequency); } QJsonValue RepeaterDatabaseEntry::toJson() const { QJsonObject obj; switch (type()) { case Type::Invalid: return QJsonValue(); case Type::FM: obj.insert("type", "FM"); break; case Type::DMR: obj.insert("type", "DMR"); break; case Type::M17: obj.insert("type", "M17"); break; } obj.insert("call", call()); obj.insert("rx", rxFrequency().format()); if (txFrequency().inHz()) obj.insert("tx", txFrequency().format()); obj.insert("latitude", location().latitude()); obj.insert("longitude", location().longitude()); obj.insert("qth", qth()); if (Type::FM == type()) { if (rxTone().isInvalid()) obj.insert("rx-tone", rxTone().format()); if (txTone().isInvalid()) obj.insert("tx-tone", txTone().format()); } else if (Type::DMR == type()) { obj.insert("color-code", (int)colorCode()); } if (updated().isValid()) obj.insert("updated", updated().toString(Qt::ISODate)); if (loaded().isValid()) obj.insert("loaded", loaded().toString(Qt::ISODate)); return obj; } RepeaterDatabaseEntry & RepeaterDatabaseEntry::operator+=(const RepeaterDatabaseEntry &other) { if ((_type != other.type()) || (_call != other.call())) return *this; if ((!_loaded.isValid()) || (_loaded < other._loaded)) return (*this)=other; if ((!_updated.isValid()) || (_updated < other._updated)) return (*this)=other; if (! _location.isValid()) _location = other._location; if (_qth.isEmpty()) _qth = other.qth(); if (Type::FM == _type) { if (_rxTone.isInvalid()) _rxTone = other.rxTone(); if (_txTone.isInvalid()) _txTone = other.txTone(); } else if (Type::DMR == _type) { if (0 == _colorCode) _colorCode = other.colorCode(); } return *this; } bool RepeaterDatabaseEntry::isValid() const { return (Type::Invalid != _type) && (! _call.isEmpty()) && (_rxFrequency.inHz()) && (_txFrequency.inHz()); } RepeaterDatabaseEntry::Type RepeaterDatabaseEntry::type() const { return _type; } const QString & RepeaterDatabaseEntry::call() const { return _call; } const Frequency & RepeaterDatabaseEntry::rxFrequency() const { return _rxFrequency; } const Frequency & RepeaterDatabaseEntry::txFrequency() const { return _txFrequency; } const QGeoCoordinate & RepeaterDatabaseEntry::location() const { return _location; } QString RepeaterDatabaseEntry::locator() const { return deg2loc(_location); } const QString & RepeaterDatabaseEntry::qth() const { return _qth; } const SelectiveCall & RepeaterDatabaseEntry::rxTone() const { return _rxTone; } const SelectiveCall & RepeaterDatabaseEntry::txTone() const { return _txTone; } unsigned int RepeaterDatabaseEntry::colorCode() const { return _colorCode; } const QDateTime & RepeaterDatabaseEntry::updated() const { return _updated; } const QDateTime & RepeaterDatabaseEntry::loaded() const { return _loaded; } RepeaterDatabaseEntry RepeaterDatabaseEntry::fm(const QString &call, const Frequency &rxFrequency, const Frequency &txFrequency, const QGeoCoordinate &location, const QString &qth, const SelectiveCall &rxTone, const SelectiveCall &txTone, const QDateTime &updated, const QDateTime& loaded) { return RepeaterDatabaseEntry( call.simplified().toUpper(), rxFrequency, txFrequency, location, qth, rxTone, txTone, updated, loaded); } RepeaterDatabaseEntry RepeaterDatabaseEntry::dmr(const QString &call, const Frequency &rxFrequency, const Frequency &txFrequency, const QGeoCoordinate &location, const QString &qth, unsigned int colorCode, const QDateTime &updated, const QDateTime& loaded) { return RepeaterDatabaseEntry( call.simplified().toUpper(), rxFrequency, txFrequency, location, qth, colorCode, updated, loaded); } RepeaterDatabaseEntry RepeaterDatabaseEntry::fromJson(const QJsonObject &obj) { if (obj.isEmpty() || (! obj.contains("type"))) return RepeaterDatabaseEntry(); Type type = Type::Invalid; if ("FM" == obj.value("type").toString()) type = Type::FM; else if ("DMR" == obj.value("type").toString()) type = Type::DMR; else if ("M17" == obj.value("type").toString()) type = Type::M17; QString call = obj.value("call").toString().simplified().toUpper(); Frequency rx = Frequency::fromString(obj.value("rx").toString()), tx; if (obj.contains("tx")) tx = Frequency::fromString(obj.value("tx").toString()); QGeoCoordinate location(obj.value("latitude").toDouble(), obj.value("longitude").toDouble()); QString qth = obj.value("qth").toString(); QDateTime updated, loaded; if (obj.contains("updated")) updated = QDateTime::fromString(obj.value("updated").toString(), Qt::ISODate); if (obj.contains("loaded")) loaded = QDateTime::fromString(obj.value("loaded").toString(), Qt::ISODate); if (Type::FM == type) { SelectiveCall rxTone, txTone; if (obj.contains("rx-tone")) rxTone = SelectiveCall::parseCTCSS(obj.value("rx-tone").toString()); if (obj.contains("tx-tone")) txTone = SelectiveCall::parseCTCSS(obj.value("tx-tone").toString()); return RepeaterDatabaseEntry::fm( call, rx, tx, location, qth, rxTone, txTone, updated, loaded); } else if (Type::DMR == type) { unsigned int colorCode = 0; if (obj.contains("color-code")) colorCode = obj.value("color-code").toInt(); return RepeaterDatabaseEntry::dmr( call, rx, tx, location, qth, colorCode, updated, loaded); } return RepeaterDatabaseEntry(); } /* ********************************************************************************************* * * Implementation of RepeaterDatabaseSource * ********************************************************************************************* */ RepeaterDatabaseSource::RepeaterDatabaseSource(QObject *parent) : QObject{parent} { // pass... } bool RepeaterDatabaseSource::query(const QString &call, const QGeoCoordinate &location) { return this->load(call, location); } unsigned int RepeaterDatabaseSource::count() const { return 0; } RepeaterDatabaseEntry RepeaterDatabaseSource::get(unsigned int idx) const { Q_UNUSED(idx); return RepeaterDatabaseEntry(); } /* ********************************************************************************************* * * Implementation of CachedRepeaterDatabaseSource * ********************************************************************************************* */ CachedRepeaterDatabaseSource::CachedRepeaterDatabaseSource(const QString &filename, QObject *parent) : RepeaterDatabaseSource{parent} { QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QDir directory; if ((! directory.exists(path)) && (!directory.mkpath(path))) { logError() << "Cannot create path '" << path << "'."; return; } _cacheFile.setFileName(path + "/" + filename); loadCache(); } void CachedRepeaterDatabaseSource::loadCache() { if (! _cacheFile.open(QIODevice::ReadOnly)) { logError() << "Cannot open cache '" << _cacheFile.fileName() << "': " << _cacheFile.errorString() << "."; return; } QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(_cacheFile.readAll(), &error); _cacheFile.close(); if (doc.isNull()) { logError() << "Cannot parse cache '" << _cacheFile.fileName() << "': " << error.errorString() << "."; return; } if (! doc.isArray()) { logError() << "Malformed cache file."; return; } _cache.clear(); for (QJsonValue obj: doc.array()) { if (! obj.isObject()) continue; RepeaterDatabaseEntry entry = RepeaterDatabaseEntry::fromJson(obj.toObject()); if (! entry.isValid()) continue; _cache.append(entry); emit updated(entry); } logDebug() << "Loaded " << _cache.size() << " entries from '" << _cacheFile.fileName() << "'."; } void CachedRepeaterDatabaseSource::saveCache() { if (! _cacheFile.open(QIODevice::WriteOnly)) { logError() << "Cannot open cache '" << _cacheFile.fileName() << "': " << _cacheFile.errorString() << "."; return; } QJsonArray entries; for (auto entry: _cache) entries.append(entry.toJson()); if (! _cacheFile.write(QJsonDocument(entries).toJson())) { logError() << "Cannot write cache '" << _cacheFile.fileName() << "': " << _cacheFile.errorString() << "."; } _cacheFile.flush(); _cacheFile.close(); } void CachedRepeaterDatabaseSource::cache(const RepeaterDatabaseEntry &entry) { if (! _indices.contains(entry)) { _indices[entry] = _cache.size(); _cache.append(entry); } else { _cache[_indices[entry]] += entry; } emit updated(entry); } unsigned int CachedRepeaterDatabaseSource::count() const { return _cache.size(); } RepeaterDatabaseEntry CachedRepeaterDatabaseSource::get(unsigned int idx) const { return _cache.value(idx, RepeaterDatabaseEntry()); } bool CachedRepeaterDatabaseSource::query(const QString &call, const QGeoCoordinate &location) { QString query = call.simplified().toUpper(); QDateTime newest; for (const RepeaterDatabaseEntry &entry: _cache) { if (entry.call().startsWith(query) && entry.loaded().isValid()) if ((! newest.isValid()) || (newest < entry.loaded())) newest = entry.loaded(); } if (newest.isValid() && (newest.daysTo(QDateTime::currentDateTime()) <= _maxAge)) return true; return RepeaterDatabaseSource::query(call, location); } /* ********************************************************************************************* * * Implementation of DownloadableRepeaterDatabaseSource * ********************************************************************************************* */ DownloadableRepeaterDatabaseSource::DownloadableRepeaterDatabaseSource( const QString &filename, const QUrl &source, unsigned int maxAge, QObject *parent) : CachedRepeaterDatabaseSource(filename, parent), _url(source), _maxAge(maxAge), _network(), _currentReply(nullptr) { connect(&_network, SIGNAL(finished(QNetworkReply*)), this, SLOT(onRequestFinished(QNetworkReply*))); if (needsUpdate()) download(); } bool DownloadableRepeaterDatabaseSource::needsUpdate() const { QFileInfo info(_cacheFile); return (! info.exists()) || (info.lastModified().daysTo(QDateTime::currentDateTime()) > _maxAge); } bool DownloadableRepeaterDatabaseSource::load(const QString &call, const QGeoCoordinate &pos) { Q_UNUSED(call); Q_UNUSED(pos); // No action needed return true; } void DownloadableRepeaterDatabaseSource::download() { // Cancel running requests if (_currentReply) _currentReply->abort(); QNetworkRequest request(_url); request.setHeader( QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/115.0.0.0 Safari/537.36 Edg/114.0.1823.86"); logDebug() << "Query " << _url.toString() << "'."; _currentReply = _network.get(request); } void DownloadableRepeaterDatabaseSource::onRequestFinished(QNetworkReply *reply) { if (reply->error()) { logError() << "Cannot download repeater list: " << reply->errorString(); reply->deleteLater(); _currentReply = nullptr; return; } QByteArray content = reply->readAll(); reply->deleteLater(); _currentReply = nullptr; if (parse(content)) saveCache(); } /* ********************************************************************************************* * * Implementation of RepeaterDatabase * ********************************************************************************************* */ RepeaterDatabase::RepeaterDatabase(QObject *parent) : QAbstractListModel{parent}, _sources(), _indices(), _entries() { // pass... } void RepeaterDatabase::addSource(RepeaterDatabaseSource *source) { if ((nullptr == source) || _sources.contains(source)) return; _sources.append(source); source->setParent(this); connect(source, &RepeaterDatabaseSource::updated, this, &RepeaterDatabase::merge); for (unsigned int i=0; icount(); i++) { merge(source->get(i)); } } void RepeaterDatabase::merge(const RepeaterDatabaseEntry &entry) { if (_indices.contains(entry)) { int row = _indices[entry]; RepeaterDatabaseEntry myEntry(_entries[row]); if ((! myEntry.updated().isValid()) || (myEntry.updated().isValid() && entry.updated().isValid() && (myEntry.updated() < entry.updated()))) { _entries[_indices[entry]] = entry; emit dataChanged(index(row), index(row)); } return; } int row = _entries.size(); beginInsertRows(QModelIndex(), row, row); _entries.append(entry); _indices[entry] = row; endInsertRows(); } int RepeaterDatabase::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return _entries.size(); } RepeaterDatabaseEntry RepeaterDatabase::get(unsigned int idx) const { if (idx >= (unsigned int)_entries.size()) return RepeaterDatabaseEntry(); return _entries[idx]; } QVariant RepeaterDatabase::data(const QModelIndex &index, int role) const { if (index.row() >= _entries.count()) return QVariant(); if (Qt::EditRole == role) { return _entries[index.row()].call(); } else if (Qt::DisplayRole == role) { return QString("%1 (%2, %3, %4)") .arg(_entries[index.row()].call()) .arg(_entries[index.row()].qth()) .arg(bandName(_entries[index.row()].rxFrequency(), _entries[index.row()].txFrequency())) .arg(_entries[index.row()].locator()); } return QVariant(); } bool RepeaterDatabase::query(const QString &call, const QGeoCoordinate &pos) { for (auto source: _sources) { source->query(call, pos); } return true; } qdmr-0.12.3/src/repeaterdatabase.hh000066400000000000000000000151761501654372000171610ustar00rootroot00000000000000#ifndef REPEATERDATABASE_HH #define REPEATERDATABASE_HH #include #include #include #include #include #include #include #include "frequency.hh" #include "signaling.hh" class QNetworkReply; /** Repeater database entry. * Just a collection of some meaningful information for FM and DMR repeater. */ class RepeaterDatabaseEntry { public: /** The possible repeater types. */ enum class Type { Invalid, FM, DMR, M17 }; protected: /** FM Constructor. */ RepeaterDatabaseEntry(const QString &call, const Frequency &rxFrequency, const Frequency &txFrequency, const QGeoCoordinate &location, const QString &qth, const SelectiveCall &rxTone, const SelectiveCall &txTone, const QDateTime &updated, const QDateTime &loaded); /** DMR Constructor. */ RepeaterDatabaseEntry(const QString &call, const Frequency &rxFrequency, const Frequency &txFrequency, const QGeoCoordinate &location, const QString &qth, unsigned int colorCode, const QDateTime &updated, const QDateTime &loaded); public: /** Default constructor, constructs an invalid entry. */ RepeaterDatabaseEntry(); /** Copy constructor. */ RepeaterDatabaseEntry(const RepeaterDatabaseEntry &other) = default; /** Assignment operator. */ RepeaterDatabaseEntry &operator=(const RepeaterDatabaseEntry &other) = default; /** Comparison. */ bool operator==(const RepeaterDatabaseEntry &other) const; /** Comparison. */ bool operator<(const RepeaterDatabaseEntry &other) const; /** Update operator. */ RepeaterDatabaseEntry &operator +=(const RepeaterDatabaseEntry &other); QJsonValue toJson() const; bool isValid() const; Type type() const; const QString &call() const; const Frequency &rxFrequency() const; const Frequency &txFrequency() const; const QGeoCoordinate &location() const; QString locator() const; const QString &qth() const; const SelectiveCall &rxTone() const; const SelectiveCall &txTone() const; unsigned int colorCode() const; const QDateTime &updated() const; const QDateTime &loaded() const; public: static RepeaterDatabaseEntry fm(const QString &call, const Frequency &rxFrequency, const Frequency &txFrequency, const QGeoCoordinate &location, const QString &qth="", const SelectiveCall &rxTone=SelectiveCall(), const SelectiveCall &txTone=SelectiveCall(), const QDateTime &updated = QDateTime(), const QDateTime &loaded = QDateTime::currentDateTime()); static RepeaterDatabaseEntry dmr(const QString &call, const Frequency &rxFrequeny, const Frequency &txFrequency, const QGeoCoordinate &location, const QString &qth="", unsigned int colorCode = 0, const QDateTime &updated = QDateTime(), const QDateTime &loaded = QDateTime::currentDateTime()); static RepeaterDatabaseEntry fromJson(const QJsonObject &obj); protected: Type _type; QString _call; Frequency _rxFrequency; Frequency _txFrequency; QGeoCoordinate _location; QString _qth; SelectiveCall _rxTone; SelectiveCall _txTone; unsigned int _colorCode; QDateTime _updated; QDateTime _loaded; }; /** Base class for all database sources. */ class RepeaterDatabaseSource: public QObject { Q_OBJECT protected: explicit RepeaterDatabaseSource(QObject *parent = nullptr); public: virtual bool query(const QString &call, const QGeoCoordinate &pos=QGeoCoordinate()); /** Returns the number of stored entries. By default, none are stored. */ virtual unsigned int count() const; /** Returns the i-th stored entry. */ virtual RepeaterDatabaseEntry get(unsigned int idx) const; signals: /** Gets emitted, once an entry gets updated or is added. */ void updated(const RepeaterDatabaseEntry &entry); protected: /** Needs to be implemented to query new entries. */ virtual bool load(const QString &call, const QGeoCoordinate &pos) = 0; }; /** Base class for all cached database sources. */ class CachedRepeaterDatabaseSource: public RepeaterDatabaseSource { Q_OBJECT protected: CachedRepeaterDatabaseSource(const QString &filename, QObject *parent = nullptr); public: unsigned int count() const; RepeaterDatabaseEntry get(unsigned int idx) const; bool query(const QString &call, const QGeoCoordinate &pos=QGeoCoordinate()); protected: void loadCache(); void cache(const RepeaterDatabaseEntry &entry); void saveCache(); protected: unsigned int _maxAge; QFile _cacheFile; QMap _indices; QVector _cache; }; class DownloadableRepeaterDatabaseSource: public CachedRepeaterDatabaseSource { Q_OBJECT protected: DownloadableRepeaterDatabaseSource(const QString &filename, const QUrl &source, unsigned int maxAge=5, QObject *parent=nullptr); public: bool needsUpdate() const; protected: virtual bool parse(const QByteArray &doc) = 0; bool load(const QString &call, const QGeoCoordinate &pos); protected slots: void onRequestFinished(QNetworkReply *reply); void download(); protected: QUrl _url; unsigned int _maxAge; QNetworkAccessManager _network; QNetworkReply *_currentReply; }; /** Base class of all repeater databases. */ class RepeaterDatabase : public QAbstractListModel { Q_OBJECT public: /** Constructor. */ explicit RepeaterDatabase(QObject *parent = nullptr); RepeaterDatabaseEntry get(unsigned int idx) const; void addSource(RepeaterDatabaseSource *source); virtual bool query(const QString &call, const QGeoCoordinate &pos=QGeoCoordinate()); int rowCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role) const; protected slots: void merge(const RepeaterDatabaseEntry &entry); protected: QList _sources; QMap _indices; QVector _entries; }; #endif // REPEATERDATABASE_HH qdmr-0.12.3/src/repeatermapsource.cc000066400000000000000000000031461501654372000173730ustar00rootroot00000000000000#include "repeatermapsource.hh" #include "logger.hh" #include #include #include #include #include RepeaterMapSource::RepeaterMapSource(QObject *parent) : DownloadableRepeaterDatabaseSource( "repeatermap.cache.json", QUrl("https://repeatermap.de/apinew.php"), 5, parent) { // pass... } bool RepeaterMapSource::parse(const QByteArray &json) { QJsonParseError err; auto doc = QJsonDocument::fromJson(json, &err); if (doc.isNull()) { logError() << "Cannot received JSON: " << err.errorString() << "."; return false; } if ((! doc.isObject()) || (! doc.object().contains("relais")) || (! doc.object()["relais"].isArray())) { logError() << "Malformed result."; return false; } QJsonArray list = doc.object()["relais"].toArray(); for (auto val: list) { if (! val.isObject()) continue; auto repeater = val.toObject(); auto call = repeater["call"].toString().simplified().toUpper(); auto mode = repeater["mode"].toString(); auto rx = Frequency::fromMHz(repeater["tx"].toDouble()); auto tx = Frequency::fromMHz(repeater["rx"].toDouble()); auto position = QGeoCoordinate(repeater["lat"].toDouble(), repeater["lon"].toDouble()); auto qth = repeater["qth"].toString(); if ("FM" == mode) { cache(RepeaterDatabaseEntry::fm(call, rx, tx, position, qth)); } else if ("DMR" == mode) { cache(RepeaterDatabaseEntry::dmr(call, rx, tx, position, qth)); } } logDebug() << "Loaded " << _cache.size() << " elements from " << _url.toDisplayString() << "."; return true; } qdmr-0.12.3/src/repeatermapsource.hh000066400000000000000000000005031501654372000173770ustar00rootroot00000000000000#ifndef REPEATERMAPSOURCE_HH #define REPEATERMAPSOURCE_HH #include "repeaterdatabase.hh" class RepeaterMapSource : public DownloadableRepeaterDatabaseSource { Q_OBJECT public: explicit RepeaterMapSource(QObject *parent = nullptr); protected: bool parse(const QByteArray &doc); }; #endif // REPEATERMAPSOURCE_HH qdmr-0.12.3/src/roamingchanneldialog.cc000066400000000000000000000056551501654372000200210ustar00rootroot00000000000000#include "roamingchanneldialog.hh" #include "ui_roamingchanneldialog.h" #include "roamingchannel.hh" #include "utils.hh" RoamingChannelDialog::RoamingChannelDialog(Config *config, QWidget *parent) : QDialog(parent), ui(new Ui::RoamingChannelDialog), _myChannel(new RoamingChannel(this)), _channel(nullptr) { Q_UNUSED(config) ui->setupUi(this); construct(); } RoamingChannelDialog::RoamingChannelDialog(Config *config, RoamingChannel *channel, QWidget *parent) : QDialog(parent), ui(new Ui::RoamingChannelDialog), _myChannel(new RoamingChannel(this)), _channel(channel) { Q_UNUSED(config) ui->setupUi(this); if (_channel) _myChannel->copy(*_channel); construct(); } RoamingChannelDialog::~RoamingChannelDialog() { delete ui; } void RoamingChannelDialog::construct() { if (_channel) setWindowTitle(tr("Edit roaming channel")); else setWindowTitle(tr("Create roaming channel")); ui->name->setText(_myChannel->name()); ui->rxFrequency->setValidator(new QDoubleValidator(0,500,5)); ui->rxFrequency->setText(_myChannel->rxFrequency().format(Frequency::Format::MHz)); ui->txFrequency->setValidator(new QDoubleValidator(0,500,5)); ui->txFrequency->setText(_myChannel->txFrequency().format(Frequency::Format::MHz)); ui->timeSlot->addItem(tr("TS 1"), QVariant::fromValue(DMRChannel::TimeSlot::TS1)); ui->timeSlot->addItem(tr("TS 2"), QVariant::fromValue(DMRChannel::TimeSlot::TS2)); ui->timeSlot->setCurrentIndex( (DMRChannel::TimeSlot::TS1 == _myChannel->timeSlot()) ? 0 : 1); if (! _myChannel->timeSlotOverridden()) { ui->overrideTimeSlot->setChecked(true); ui->timeSlot->setEnabled(false); } ui->colorCode->setValue(_myChannel->colorCode()); if (! _myChannel->colorCodeOverridden()) { ui->overrideColorCode->setChecked(true); ui->colorCode->setEnabled(false); } connect(ui->overrideTimeSlot, SIGNAL(toggled(bool)), this, SLOT(onOverrideTimeSlotToggled(bool))); connect(ui->overrideColorCode, SIGNAL(toggled(bool)), this, SLOT(onOverrideColorCodeToggled(bool))); } void RoamingChannelDialog::onOverrideTimeSlotToggled(bool override) { ui->timeSlot->setEnabled(! override); } void RoamingChannelDialog::onOverrideColorCodeToggled(bool override) { ui->colorCode->setEnabled(! override); } RoamingChannel * RoamingChannelDialog::channel() { _myChannel->setName(ui->name->text().simplified()); _myChannel->setRXFrequency(Frequency::fromString(ui->rxFrequency->text())); _myChannel->setTXFrequency(Frequency::fromString(ui->txFrequency->text())); _myChannel->setTimeSlot(ui->timeSlot->currentData().value()); _myChannel->overrideTimeSlot(! ui->overrideTimeSlot->isChecked()); _myChannel->setColorCode(ui->colorCode->value()); _myChannel->overrideColorCode(! ui->overrideColorCode->isChecked()); if (_channel) { _channel->copy(*_myChannel); return _channel; } _myChannel->setParent(nullptr); return _myChannel; } qdmr-0.12.3/src/roamingchanneldialog.hh000066400000000000000000000013571501654372000200260ustar00rootroot00000000000000#ifndef ROAMINGCHANNELDIALOG_HH #define ROAMINGCHANNELDIALOG_HH #include // Forward declarations namespace Ui { class RoamingChannelDialog; } class Config; class RoamingChannel; class RoamingChannelDialog : public QDialog { Q_OBJECT public: RoamingChannelDialog(Config *config, QWidget *parent = nullptr); RoamingChannelDialog(Config *config, RoamingChannel *channel, QWidget *parent = nullptr); ~RoamingChannelDialog(); RoamingChannel *channel(); protected slots: void construct(); void onOverrideTimeSlotToggled(bool override); void onOverrideColorCodeToggled(bool override); private: Ui::RoamingChannelDialog *ui; RoamingChannel *_myChannel; RoamingChannel *_channel; }; #endif // ROAMINGCHANNELDIALOG_HH qdmr-0.12.3/src/roamingchanneldialog.ui000066400000000000000000000102701501654372000200360ustar00rootroot00000000000000 RoamingChannelDialog 0 0 400 300 0 0 Dialog Name RX Frequency [MHz] TX Frequency [MHz] Time Slot Color Code Selected 0 0 Selected 0 0 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() RoamingChannelDialog accept() 248 254 157 274 buttonBox rejected() RoamingChannelDialog reject() 316 260 286 274 qdmr-0.12.3/src/roamingchannellistview.cc000066400000000000000000000060031501654372000204140ustar00rootroot00000000000000#include "roamingchannellistview.hh" #include "ui_roamingchannellistview.h" #include "roamingchanneldialog.hh" #include "configitemwrapper.hh" #include "config.hh" #include "settings.hh" #include RoamingChannelListView::RoamingChannelListView(Config *config, QWidget *parent) : QWidget(parent), _config(config), ui(new Ui::RoamingChannelListView) { Settings settings; ui->setupUi(this); ui->roamingChannelTableView->setModel( new RoamingChannelListWrapper(_config->roamingChannels(), ui->roamingChannelTableView)); connect(ui->addRoamingChannel, SIGNAL(clicked()), this, SLOT(onAddChannel())); connect(ui->remRoamingChannel, SIGNAL(clicked()), this, SLOT(onRemChannel())); connect(ui->roamingChannelTableView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditChannel(unsigned))); connect(ui->roamingChannelListHint, SIGNAL(linkActivated(QString)), this, SLOT(onHideRoamingNote())); if (settings.hideRoamingNote()) ui->roamingChannelListHint->setHidden(true); } RoamingChannelListView::~RoamingChannelListView() { delete ui; } void RoamingChannelListView::onAddChannel() { RoamingChannelDialog dialog(_config); if (QDialog::Accepted != dialog.exec()) return; int row=-1; if (ui->roamingChannelTableView->hasSelection()) row = ui->roamingChannelTableView->selection().second+1; _config->roamingChannels()->add(dialog.channel(), row); } void RoamingChannelListView::onEditChannel(unsigned int idx) { RoamingChannelDialog dialog(_config, _config->roamingChannels()->channel(idx)); if (QDialog::Accepted != dialog.exec()) return; dialog.channel(); } void RoamingChannelListView::onRemChannel() { if (! ui->roamingChannelTableView->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot delete roaming channel"), tr("Cannot delete roaming channel: You have to select a channel first.")); return; } // Get selection and ask for deletion QPair rows = ui->roamingChannelTableView->selection(); int rowcount = rows.second - rows.first + 1; if (rows.first==rows.second) { QString name = _config->roamingChannels()->channel(rows.first)->name(); if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete roaming channel?"), tr("Delete roaming channel %1?").arg(name))) return; } else { if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete roaming channel?"), tr("Delete %1 roaming channel?").arg(rowcount))) return; } // collect all selected channels // need to collect them first as rows change when deleting QList lists; lists.reserve(rowcount); for (int row=rows.first; row<=rows.second; row++) lists.push_back(_config->roamingChannels()->channel(row)); // remove foreach (RoamingChannel *channel, lists) _config->roamingChannels()->del(channel); } void RoamingChannelListView::onHideRoamingNote() { Settings settings; settings.setHideRoamingNote(true); ui->roamingChannelListHint->setVisible(false); } qdmr-0.12.3/src/roamingchannellistview.hh000066400000000000000000000012201501654372000204220ustar00rootroot00000000000000#ifndef ROAMINGCHANNELLISTVIEW_HH #define ROAMINGCHANNELLISTVIEW_HH #include // Forward declarations namespace Ui { class RoamingChannelListView; } class Config; class RoamingChannelListView : public QWidget { Q_OBJECT public: explicit RoamingChannelListView(Config *config, QWidget *parent = nullptr); ~RoamingChannelListView(); protected slots: void onAddChannel(); void onEditChannel(unsigned int idx); void onRemChannel(); void onHideRoamingNote(); protected: /** Holds a weak reference to the abstract config. */ Config *_config; private: Ui::RoamingChannelListView *ui; }; #endif // ROAMINGCHANNELLISTVIEW_HH qdmr-0.12.3/src/roamingchannellistview.ui000066400000000000000000000053351501654372000204530ustar00rootroot00000000000000 RoamingChannelListView 0 0 400 300 Form padding:10px;border: 2px solid black; border-radius: 10px; QFrame::NoFrame <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p><span style=" font-weight:600;">Hint:</span> You do not need to add roaming channel explicitly, just create roaming zones and add channels there. These channels are then added to this list.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Qt::RichText true 0 0 Add Roaming Channel Delete Roaming Channel ConfigObjectTableView QWidget
      configobjecttableview.hh
      1
      qdmr-0.12.3/src/roamingchannelselectiondialog.cc000066400000000000000000000026301501654372000217150ustar00rootroot00000000000000#include "roamingchannelselectiondialog.hh" #include "config.hh" #include #include #include MultiRoamingChannelSelectionDialog::MultiRoamingChannelSelectionDialog( Config *config, QWidget *parent) : QDialog(parent), _channels(nullptr) { setWindowTitle(tr("Select roaming channels")); _channels = new QListWidget(); for (int i=0; iroamingChannels()->count(); i++) { QListWidgetItem *item = new QListWidgetItem(config->roamingChannels()->channel(i)->name()); item->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled); item->setData(Qt::UserRole, QVariant::fromValue(config->roamingChannels()->channel(i))); item->setCheckState(Qt::Unchecked); _channels->addItem(item); } QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QVBoxLayout *layout = new QVBoxLayout(); layout->addWidget(_channels); layout->addWidget(box); setLayout(layout); connect(box, SIGNAL(accepted()), this, SLOT(accept())); connect(box, SIGNAL(rejected()), this, SLOT(reject())); } QList MultiRoamingChannelSelectionDialog::channels() const { QList channels; for (int i=0; i<_channels->count(); i++) { if (Qt::Checked ==_channels->item(i)->checkState()) channels.push_back(_channels->item(i)->data(Qt::UserRole).value()); } return channels; } qdmr-0.12.3/src/roamingchannelselectiondialog.hh000066400000000000000000000007701501654372000217320ustar00rootroot00000000000000#ifndef MULTIROAMINGCHANNELSELECTIONDIALOG_HH #define MULTIROAMINGCHANNELSELECTIONDIALOG_HH #include #include // Forward declarations class Config; class RoamingChannel; class QListWidget; class MultiRoamingChannelSelectionDialog : public QDialog { Q_OBJECT public: MultiRoamingChannelSelectionDialog(Config *config, QWidget *parent=nullptr); QList channels() const; protected: QListWidget *_channels; }; #endif // MULTIROAMINGCHANNELSELECTIONDIALOG_HH qdmr-0.12.3/src/roamingzonedialog.cc000066400000000000000000000060031501654372000173500ustar00rootroot00000000000000#include "roamingzonedialog.hh" #include "settings.hh" #include "config.hh" #include "channelselectiondialog.hh" #include "roamingchannelselectiondialog.hh" #include RoamingZoneDialog::RoamingZoneDialog(Config *config, QWidget *parent) : QDialog(parent), _config(config), _myZone(new RoamingZone(this)), _zone(nullptr) { setWindowTitle(tr("Create Roaming Zone")); construct(); } RoamingZoneDialog::RoamingZoneDialog(Config *config, RoamingZone *zone, QWidget *parent) : QDialog(parent), _config(config), _myZone(new RoamingZone(this)), _zone(zone) { setWindowTitle(tr("Set Roaming Zone")); if (_zone) _myZone->copy(*_zone); construct(); } void RoamingZoneDialog::construct() { setupUi(this); Settings settings; zoneName->setText(_myZone->name()); channelListView->setModel(new RoamingChannelRefListWrapper(_myZone->channels(), channelListView)); extensionView->setObjectName("roamingZoneExtension"); extensionView->setObject(_myZone, _config); if (! settings.showExtensions()) tabWidget->tabBar()->hide(); connect(addRoam, SIGNAL(clicked(bool)), this, SLOT(onAddRoamingChannel())); connect(addDMR, SIGNAL(clicked(bool)), this, SLOT(onAddDMRChannel())); connect(rem, SIGNAL(clicked(bool)), this, SLOT(onRemChannel())); } void RoamingZoneDialog::onAddDMRChannel() { MultiChannelSelectionDialog dia(_config->channelList(), false, true); if (QDialog::Accepted != dia.exec()) return; QList lst = dia.channel(); foreach (Channel *channel, lst) { if (0 <= _myZone->channels()->indexOf(channel)) continue; if (! channel->is()) continue; RoamingChannel *rch = RoamingChannel::fromDMRChannel(channel->as()); _config->roamingChannels()->add(rch); _myZone->addChannel(rch); } } void RoamingZoneDialog::onAddRoamingChannel() { MultiRoamingChannelSelectionDialog dia(_config); if (QDialog::Accepted != dia.exec()) return; QList lst = dia.channels(); foreach (RoamingChannel *channel, lst) { if (0 <= _myZone->channels()->indexOf(channel)) continue; _myZone->addChannel(channel); } } void RoamingZoneDialog::onRemChannel() { if (! channelListView->hasSelection()) { QMessageBox::information(nullptr, tr("Cannot remove channels."), tr("Cannot remove channels. Select at least one channel first."), QMessageBox::Close, QMessageBox::Close); return; } QPair selection = channelListView->selection(); QList channels; for (int i=selection.first; i<=selection.second; i++) channels.push_back(_myZone->channels()->get(i)->as()); foreach (RoamingChannel *channel, channels) { _myZone->channels()->del(channel); } } RoamingZone * RoamingZoneDialog::zone() { _myZone->setName(zoneName->text().simplified()); RoamingZone *zone = _myZone; if (_zone) { _zone->copy(*_myZone); zone = _zone; } else { _myZone->setParent(nullptr); } return zone; } qdmr-0.12.3/src/roamingzonedialog.hh000066400000000000000000000012161501654372000173630ustar00rootroot00000000000000#ifndef ROAMINGZONEDIALOG_HH #define ROAMINGZONEDIALOG_HH #include #include "roamingzone.hh" #include "ui_roamingzonedialog.h" class Config; class RoamingZoneDialog : public QDialog, private Ui_RoamingZoneDialog { Q_OBJECT public: explicit RoamingZoneDialog(Config *config, QWidget *parent = nullptr); RoamingZoneDialog(Config *config, RoamingZone *zone, QWidget *parent=nullptr); RoamingZone *zone(); protected slots: void construct(); void onAddRoamingChannel(); void onAddDMRChannel(); void onRemChannel(); protected: Config *_config; RoamingZone *_myZone; RoamingZone *_zone; }; #endif // ROAMINGZONEDIALOG_HH qdmr-0.12.3/src/roamingzonedialog.ui000066400000000000000000000102411501654372000173770ustar00rootroot00000000000000 RoamingZoneDialog 0 0 485 413 0 0 Dialog 0 Basic Name: 0 0 Add Roaming Channel Add DMR Channel Alt++ Remove Channel Alt+- Extension Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ExtensionView QWidget
      extensionview.hh
      1
      ConfigObjectListView QWidget
      configobjectlistview.hh
      1
      buttonBox accepted() RoamingZoneDialog accept() 248 254 157 274 buttonBox rejected() RoamingZoneDialog reject() 316 260 286 274
      qdmr-0.12.3/src/roamingzonelistview.cc000066400000000000000000000076231501654372000177700ustar00rootroot00000000000000#include "roamingzonelistview.hh" #include "ui_roamingzonelistview.h" #include "settings.hh" #include "config.hh" #include "roamingzonedialog.hh" #include "contactselectiondialog.hh" #include RoamingZoneListView::RoamingZoneListView(Config *config, QWidget *parent) : QWidget(parent), ui(new Ui::RoamingZoneListView), _config(config) { Settings settings; ui->setupUi(this); ui->listView->setModel(new RoamingListWrapper(_config->roamingZones(), ui->listView)); connect(ui->addRoamingZone, SIGNAL(clicked()), this, SLOT(onAddRoamingZone())); connect(ui->genRoamingZone, SIGNAL(clicked(bool)), this, SLOT(onGenRoamingZone())); connect(ui->remRoamingZone, SIGNAL(clicked()), this, SLOT(onRemRoamingZone())); connect(ui->listView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditRoamingZone(unsigned))); connect(ui->roamingNote, SIGNAL(linkActivated(QString)), this, SLOT(onHideRoamingNote())); if (settings.hideRoamingNote()) ui->roamingNote->setHidden(true); } RoamingZoneListView::~RoamingZoneListView() { delete ui; } void RoamingZoneListView::onAddRoamingZone() { RoamingZoneDialog dialog(_config); if (QDialog::Accepted != dialog.exec()) return; int row=-1; if (ui->listView->hasSelection()) row = ui->listView->selection().second+1; _config->roamingZones()->add(dialog.zone(), row); } void RoamingZoneListView::onGenRoamingZone() { MultiGroupCallSelectionDialog contSel(_config->contacts(), false); contSel.setWindowTitle(tr("Generate roaming zone")); contSel.setLabel(tr("Create a roaming zone by collecting all channels with these group calls.")); if (QDialog::Accepted != contSel.exec()) return; QList contacts = contSel.contacts(); RoamingZone *zone = new RoamingZone("Name"); for (int i=0; i<_config->channelList()->count(); i++) { DMRChannel *dch = _config->channelList()->channel(i)->as(); if (nullptr == dch) continue; if (contacts.contains(dch->txContactObj())) { RoamingChannel *rch = RoamingChannel::fromDMRChannel(dch); _config->roamingChannels()->add(rch); zone->addChannel(rch); } } RoamingZoneDialog dialog(_config, zone); if (QDialog::Accepted != dialog.exec()) { zone->deleteLater(); return; } int row=-1; if (ui->listView->hasSelection()) row = ui->listView->selection().second+1; _config->roamingZones()->add(dialog.zone(), row); } void RoamingZoneListView::onRemRoamingZone() { if (! ui->listView->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot delete roaming zone"), tr("Cannot delete roaming zone: You have to select a zone first.")); return; } // Get selection and ask for deletion QPair rows = ui->listView->selection(); int rowcount = rows.second - rows.first + 1; if (rows.first==rows.second) { QString name = _config->roamingZones()->zone(rows.first)->name(); if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete roaming zone?"), tr("Delete roaming zone %1?").arg(name))) return; } else { if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete roaming zones?"), tr("Delete %1 roaming zones?").arg(rowcount))) return; } // collect all selected zones // need to collect them first as rows change when deleting QList lists; lists.reserve(rowcount); for (int row=rows.first; row<=rows.second; row++) lists.push_back(_config->roamingZones()->zone(row)); // remove foreach (RoamingZone *zone, lists) _config->roamingZones()->del(zone); } void RoamingZoneListView::onEditRoamingZone(unsigned row) { RoamingZoneDialog dialog(_config, _config->roamingZones()->zone(row)); if (QDialog::Accepted != dialog.exec()) return; dialog.zone(); } void RoamingZoneListView::onHideRoamingNote() { Settings setting; setting.setHideRoamingNote(true); ui->roamingNote->setVisible(false); } qdmr-0.12.3/src/roamingzonelistview.hh000066400000000000000000000011021501654372000177640ustar00rootroot00000000000000#ifndef ROAMINGZONELISTVIEW_HH #define ROAMINGZONELISTVIEW_HH #include class Config; namespace Ui { class RoamingZoneListView; } class RoamingZoneListView : public QWidget { Q_OBJECT public: explicit RoamingZoneListView(Config *_config, QWidget *parent = nullptr); ~RoamingZoneListView(); protected slots: void onAddRoamingZone(); void onGenRoamingZone(); void onRemRoamingZone(); void onEditRoamingZone(unsigned row); void onHideRoamingNote(); private: Ui::RoamingZoneListView *ui; Config *_config; }; #endif // ROAMINGZONELISTVIEW_HH qdmr-0.12.3/src/roamingzonelistview.ui000066400000000000000000000053351501654372000200160ustar00rootroot00000000000000 RoamingZoneListView 0 0 497 300 Form padding:10px;border: 2px solid black; border-radius: 10px; <html><head/><body><p><span style=" font-weight:600;">Note:</span> QDMR is a device independent CPS. However, not all radios support Roaming. Hence these settings might be ignored when programming the code-plug to the device. </p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Qt::RichText true 0 0 Add Roaming Zone Alt++ Generate Roaming Zone Delete Roaming Zone Alt+- ConfigObjectListView QWidget
      configobjectlistview.hh
      1
      qdmr-0.12.3/src/rxgrouplistdialog.cc000066400000000000000000000071271501654372000174320ustar00rootroot00000000000000#include "rxgrouplistdialog.hh" #include "config.hh" #include "contact.hh" #include "contactselectiondialog.hh" #include "settings.hh" #include #include /* ********************************************************************************************* * * Implementation of RXGroupListDialog * ********************************************************************************************* */ RXGroupListDialog::RXGroupListDialog(Config *config, QWidget *parent) : QDialog(parent), _config(config), _myGroupList(new RXGroupList(this)), _list(nullptr) { setWindowTitle(tr("Create Group List")); construct(); } RXGroupListDialog::RXGroupListDialog(Config *config, RXGroupList *list, QWidget *parent) : QDialog(parent), _config(config), _myGroupList(new RXGroupList(this)), _list(list) { setWindowTitle(tr("Edit Group List")); if (_list) _myGroupList->copy(*_list); construct(); } RXGroupList * RXGroupListDialog::groupList() { _myGroupList->setName(groupListName->text().simplified()); RXGroupList *list = _myGroupList; if (_list) { _list->copy(*_myGroupList); list = _myGroupList; } else { _myGroupList->setParent(nullptr); } // Return modified list. return list; } void RXGroupListDialog::construct() { setupUi(this); Settings settings; connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(addContact, SIGNAL(clicked()), this, SLOT(onAddGroup())); connect(remContact, SIGNAL(clicked()), this, SLOT(onRemGroup())); groupListName->setText(_myGroupList->name()); contactListView->setModel(new GroupListWrapper(_myGroupList, contactListView)); extensionView->setObjectName("groupListExtension"); extensionView->setObject(_myGroupList, _config); if (! settings.showExtensions()) tabWidget->tabBar()->hide(); } void RXGroupListDialog::onAddGroup() { MultiGroupCallSelectionDialog dialog(_config->contacts(), false); if (QDialog::Accepted != dialog.exec()) return; QList contacts = dialog.contacts(); foreach (DMRContact *contact, contacts) { if (0 <= _myGroupList->contacts()->indexOf(contact)) continue; _myGroupList->addContact(contact); } } void RXGroupListDialog::onRemGroup() { if (!contactListView->hasSelection()) { QMessageBox::information(nullptr, tr("Cannot remove group call"), tr("Cannot remove group call: You have to select at least one group call first.")); return; } QPair selection = contactListView->selection(); // collect all selected group lists // need to collect them first as rows change when deleting QList lists; for (int row=selection.first; row<=selection.second; row++) lists.push_back(_myGroupList->contact(row)); // remove list foreach (DMRContact *cont, lists) _myGroupList->contacts()->del(cont); } /* ********************************************************************************************* * * Implementation of RXGroupListBox * ********************************************************************************************* */ RXGroupListBox::RXGroupListBox(RXGroupLists *groups, QWidget *parent) : QComboBox(parent) { populateRXGroupListBox(this, groups); } void populateRXGroupListBox(QComboBox *box, RXGroupLists *groups, RXGroupList *list) { box->addItem(QObject::tr("[None]"), QVariant::fromValue(nullptr)); for (int i=0; icount(); i++) { box->addItem(groups->list(i)->name(), QVariant::fromValue(groups->list(i))); if (groups->list(i) == list) box->setCurrentIndex(i+1); } } qdmr-0.12.3/src/rxgrouplistdialog.hh000066400000000000000000000016621501654372000174420ustar00rootroot00000000000000#ifndef RXGROUPLISTDIALOG_HH #define RXGROUPLISTDIALOG_HH #include #include #include #include #include "rxgrouplist.hh" #include "ui_rxgrouplistdialog.h" class Config; class RXGroupListDialog: public QDialog, private Ui::RXGroupListDialog { Q_OBJECT public: explicit RXGroupListDialog(Config *config, QWidget *parent=nullptr); RXGroupListDialog(Config *config, RXGroupList *list, QWidget *parent=nullptr); RXGroupList *groupList(); protected slots: void onAddGroup(); void onRemGroup(); protected: void construct(); protected: Config *_config; RXGroupList *_myGroupList; RXGroupList *_list; }; class RXGroupListBox: public QComboBox { Q_OBJECT public: RXGroupListBox(RXGroupLists *groups, QWidget *parent=0); RXGroupList *groupList(); }; void populateRXGroupListBox(QComboBox *box, RXGroupLists *groups, RXGroupList *list=nullptr); #endif // RXGROUPLISTDIALOG_HH qdmr-0.12.3/src/rxgrouplistdialog.ui000066400000000000000000000100341501654372000174510ustar00rootroot00000000000000 RXGroupListDialog 0 0 425 357 0 0 0 Basic Name groupListName 0 0 Add Contact Alt++ Remove Contact Alt+- Extensions Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ExtensionView QWidget
      extensionview.hh
      1
      ConfigObjectListView QWidget
      configobjectlistview.hh
      1
      buttonBox rejected() RXGroupListDialog reject() 316 260 286 274 buttonBox accepted() RXGroupListDialog accept() 248 254 157 274
      qdmr-0.12.3/src/satellitedatabasedialog.cc000066400000000000000000000052021501654372000204730ustar00rootroot00000000000000#include "satellitedatabasedialog.hh" #include "ui_satellitedatabasedialog.h" #include "satellitedatabase.hh" #include "satelliteselectiondialog.hh" #include "transponderfrequencydelegate.hh" #include "settings.hh" #include "selectivecallbox.hh" SatelliteDatabaseDialog::SatelliteDatabaseDialog(SatelliteDatabase *db, QWidget *parent) : QDialog(parent), ui(new Ui::SatelliteDatabaseDialog), _database(db) { ui->setupUi(this); connect(ui->addButton, SIGNAL(clicked(bool)), this, SLOT(onAddSatellite())); connect(ui->delButton, SIGNAL(clicked(bool)), this, SLOT(onDeleteSatellite())); ui->satellitesView->setModel(_database); // FM downlink ui->satellitesView->setItemDelegateForColumn(2, new TransponderFrequencyDelegate(false, Transponder::Mode::FM)); // FM uplink ui->satellitesView->setItemDelegateForColumn(3, new TransponderFrequencyDelegate(true, Transponder::Mode::FM)); // FM downlink sub tone ui->satellitesView->setItemDelegateForColumn(4, new SelectiveCallDelegate()); // FM uplink sub tone ui->satellitesView->setItemDelegateForColumn(5, new SelectiveCallDelegate()); // APRS downlink ui->satellitesView->setItemDelegateForColumn(6, new TransponderFrequencyDelegate(false, Transponder::Mode::APRS)); // APRS uplink ui->satellitesView->setItemDelegateForColumn(7, new TransponderFrequencyDelegate(true, Transponder::Mode::APRS)); // APRS downlink sub tone ui->satellitesView->setItemDelegateForColumn(8, new SelectiveCallDelegate()); // APRS uplink sub tone ui->satellitesView->setItemDelegateForColumn(9, new SelectiveCallDelegate()); // Beacon ui->satellitesView->setItemDelegateForColumn(10, new TransponderFrequencyDelegate(false, Transponder::Mode::CW)); this->restoreGeometry(Settings().headerState(objectName())); ui->satellitesView->horizontalHeader()->restoreState( Settings().headerState(ui->satellitesView->objectName())); } SatelliteDatabaseDialog::~SatelliteDatabaseDialog() { Settings settings; settings.setHeaderState(this->objectName(), this->saveGeometry()); settings.setHeaderState(ui->satellitesView->objectName(), ui->satellitesView->horizontalHeader()->saveState()); delete ui; } void SatelliteDatabaseDialog::onAddSatellite() { SatelliteSelectionDialog dialog(&(_database->orbitalElements())); if (QDialog::Accepted != dialog.exec()) return; _database->add(_database->orbitalElements().getById(dialog.satellite())); } void SatelliteDatabaseDialog::onDeleteSatellite() { QModelIndexList selected = ui->satellitesView->selectionModel()->selectedRows(); if (selected.isEmpty()) return; _database->removeRow(selected.back().row()); } qdmr-0.12.3/src/satellitedatabasedialog.hh000066400000000000000000000010351501654372000205050ustar00rootroot00000000000000#ifndef SATELLITEDATABASEDIALOG_HH #define SATELLITEDATABASEDIALOG_HH #include namespace Ui { class SatelliteDatabaseDialog; } class SatelliteDatabase; class SatelliteDatabaseDialog : public QDialog { Q_OBJECT public: explicit SatelliteDatabaseDialog(SatelliteDatabase *db, QWidget *parent = nullptr); ~SatelliteDatabaseDialog(); private slots: void onAddSatellite(); void onDeleteSatellite(); private: Ui::SatelliteDatabaseDialog *ui; SatelliteDatabase *_database; }; #endif // SATELLITEDATABASEDIALOG_HH qdmr-0.12.3/src/satellitedatabasedialog.ui000066400000000000000000000051151501654372000205260ustar00rootroot00000000000000 SatelliteDatabaseDialog 0 0 400 300 0 0 Edit satellite database QAbstractItemView::SingleSelection QAbstractItemView::SelectRows false Add Delete Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Save buttonBox accepted() SatelliteDatabaseDialog accept() 248 254 157 274 buttonBox rejected() SatelliteDatabaseDialog reject() 316 260 286 274 qdmr-0.12.3/src/satelliteselectiondialog.cc000066400000000000000000000016641501654372000207240ustar00rootroot00000000000000#include "satelliteselectiondialog.hh" #include "ui_satelliteselectiondialog.h" #include "orbitalelementsdatabase.hh" #include "logger.hh" #include "settings.hh" #include "searchpopup.hh" SatelliteSelectionDialog::SatelliteSelectionDialog(OrbitalElementsDatabase *db, QWidget *parent) : QDialog(parent), ui(new Ui::SatelliteSelectionDialog), _database(db) { ui->setupUi(this); ui->satellites->setModel(_database); ui->satellites->hideColumn(2); SearchPopup::attach(ui->satellites); this->restoreGeometry(Settings().headerState(objectName())); } SatelliteSelectionDialog::~SatelliteSelectionDialog() { Settings().setHeaderState(this->objectName(), this->saveGeometry()); delete ui; } unsigned int SatelliteSelectionDialog::satellite() const { QModelIndexList selection = ui->satellites->selectionModel()->selectedRows(); if (0 == selection.count()) return 0; return _database->getAt(selection.back().row()).id(); } qdmr-0.12.3/src/satelliteselectiondialog.hh000066400000000000000000000010251501654372000207250ustar00rootroot00000000000000#ifndef SATELLITESELECTIONDIALOG_HH #define SATELLITESELECTIONDIALOG_HH #include namespace Ui { class SatelliteSelectionDialog; } class OrbitalElementsDatabase; class SatelliteSelectionDialog : public QDialog { Q_OBJECT public: explicit SatelliteSelectionDialog(OrbitalElementsDatabase *db, QWidget *parent = nullptr); ~SatelliteSelectionDialog(); unsigned int satellite() const; private: Ui::SatelliteSelectionDialog *ui; OrbitalElementsDatabase *_database; }; #endif // SATELLITESELECTIONDIALOG_HH qdmr-0.12.3/src/satelliteselectiondialog.ui000066400000000000000000000042331501654372000207470ustar00rootroot00000000000000 SatelliteSelectionDialog 0 0 400 300 0 0 Select a satellite Select a satellite QAbstractItemView::SelectRows false false Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() SatelliteSelectionDialog accept() 248 254 157 274 buttonBox rejected() SatelliteSelectionDialog reject() 316 260 286 274 qdmr-0.12.3/src/scanlistdialog.cc000066400000000000000000000102131501654372000166360ustar00rootroot00000000000000#include "scanlistdialog.hh" #include "config.hh" #include "channelselectiondialog.hh" #include "settings.hh" /* ********************************************************************************************* * * Implementation of ScanListDialog * ********************************************************************************************* */ ScanListDialog::ScanListDialog(Config *config, ScanList *scanlist, QWidget *parent) : QDialog(parent), _config(config), _myScanList(new ScanList(this)), _scanlist(scanlist) { setWindowTitle(tr("Edit Scan List")); if (_scanlist) _myScanList->copy(*_scanlist); construct(); } ScanListDialog::ScanListDialog(Config *config, QWidget *parent) : QDialog(parent), _config(config), _myScanList(new ScanList(this)), _scanlist(nullptr) { setWindowTitle(tr("Create Scan List")); construct(); } void ScanListDialog::construct() { setupUi(this); Settings settings; priorityChannel1->addItem(tr("[None]"), QVariant::fromValue(nullptr)); priorityChannel1->addItem(tr("[Selected]"), QVariant::fromValue(SelectedChannel::get())); priorityChannel2->addItem(tr("[None]"), QVariant::fromValue(nullptr)); priorityChannel2->addItem(tr("[Selected]"), QVariant::fromValue(SelectedChannel::get())); transmitChannel->addItem(tr("[Last]"), QVariant::fromValue(nullptr)); transmitChannel->addItem(tr("[Selected]"), QVariant::fromValue(SelectedChannel::get())); for (int i=0; i<_config->channelList()->count(); i++) { Channel *channel = _config->channelList()->channel(i); priorityChannel1->addItem(channel->name(), QVariant::fromValue(channel)); priorityChannel2->addItem(channel->name(), QVariant::fromValue(channel)); transmitChannel->addItem(channel->name(), QVariant::fromValue(channel)); } scanListName->setText(_myScanList->name()); // set priority channel if (_myScanList->primaryChannel()) priorityChannel1->setCurrentIndex(_config->channelList()->indexOf(_myScanList->primaryChannel())+2); // set secondary priority channel if (_myScanList->secondaryChannel()) priorityChannel2->setCurrentIndex(_config->channelList()->indexOf(_myScanList->secondaryChannel())+2); if (_myScanList->revertChannel()) transmitChannel->setCurrentIndex(_config->channelList()->indexOf(_myScanList->revertChannel())+2); channelListView->setModel(new ChannelRefListWrapper(_myScanList->channels(), channelListView)); extensionView->setObjectName("scanListExtension"); extensionView->setObject(_myScanList, _config); if (! settings.showExtensions()) tabWidget->tabBar()->hide(); connect(addChannel, SIGNAL(clicked()), this, SLOT(onAddChannel())); connect(remChannel, SIGNAL(clicked()), this, SLOT(onRemChannel())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); } void ScanListDialog::onAddChannel() { MultiChannelSelectionDialog dia(_config->channelList(), true); if (QDialog::Accepted != dia.exec()) return; QList channels = dia.channel(); foreach (Channel *channel, channels) { if (0 <= _myScanList->channels()->indexOf(channel)) continue; _myScanList->channels()->add(channel); } } void ScanListDialog::onRemChannel() { if (! channelListView->hasSelection()) return; QPair selection = channelListView->selection(); QList channels; for (int i=selection.first; i<=selection.second; i++) channels.push_back(_myScanList->channels()->get(i)->as()); foreach (Channel *channel, channels) _myScanList->channels()->del(channel); } ScanList * ScanListDialog::scanlist() { _myScanList->setName(scanListName->text().simplified()); // Set priority and transmit channels _myScanList->setPrimaryChannel(priorityChannel1->currentData(Qt::UserRole).value()); _myScanList->setSecondaryChannel(priorityChannel2->currentData(Qt::UserRole).value()); _myScanList->setRevertChannel(transmitChannel->currentData(Qt::UserRole).value()); ScanList *scanlist = _myScanList; if (_scanlist) { _scanlist->copy(*_myScanList); scanlist = _scanlist; } else { _myScanList->setParent(nullptr); } return scanlist; } qdmr-0.12.3/src/scanlistdialog.hh000066400000000000000000000011071501654372000166520ustar00rootroot00000000000000#ifndef SCANLISTDIALOG_HH #define SCANLISTDIALOG_HH #include #include "ui_scanlistdialog.h" class Config; class ScanList; class ScanListDialog: public QDialog, private Ui::ScanListDialog { Q_OBJECT public: ScanListDialog(Config *config, QWidget *parent=nullptr); ScanListDialog(Config *config, ScanList *list, QWidget *parent=nullptr); ScanList *scanlist(); protected slots: void onAddChannel(); void onRemChannel(); protected: void construct(); protected: Config *_config; ScanList *_myScanList; ScanList *_scanlist; }; #endif // SCANLISTDIALOG_HH qdmr-0.12.3/src/scanlistdialog.ui000066400000000000000000000134131501654372000166730ustar00rootroot00000000000000 ScanListDialog 0 0 437 536 0 0 Edit Scan List 0 Basic Name Primary Channel (50%) 0 0 Secondary Channel (25%) 0 0 Transmit Channel 0 0 0 0 Add Channel Alt++ Remove Channel Alt+- Extensions Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ExtensionView QWidget
      extensionview.hh
      1
      ConfigObjectListView QWidget
      configobjectlistview.hh
      1
      buttonBox accepted() ScanListDialog accept() 248 254 157 274 buttonBox rejected() ScanListDialog reject() 316 260 286 274
      qdmr-0.12.3/src/scanlistsview.cc000066400000000000000000000043571501654372000165500ustar00rootroot00000000000000#include "scanlistsview.hh" #include "ui_scanlistsview.h" #include "scanlistdialog.hh" #include ScanListsView::ScanListsView(Config *config, QWidget *parent) : QWidget(parent), ui(new Ui::ScanListsView), _config(config) { ui->setupUi(this); ui->listView->setModel(new ScanListsWrapper(_config->scanlists(), ui->listView)); connect(ui->addScanList, SIGNAL(clicked()), this, SLOT(onAddScanList())); connect(ui->remScanList, SIGNAL(clicked()), this, SLOT(onRemScanList())); connect(ui->listView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditScanList(unsigned))); } ScanListsView::~ScanListsView() { delete ui; } void ScanListsView::onAddScanList() { ScanListDialog dialog(_config); if (QDialog::Accepted != dialog.exec()) return; int row=-1; if (ui->listView->hasSelection()) row = ui->listView->selection().second+1; _config->scanlists()->add(dialog.scanlist(), row); } void ScanListsView::onRemScanList() { if (! ui->listView->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot delete scanlist"), tr("Cannot delete scanlist: You have to select a scanlist first.")); return; } // Get selection and ask for deletion QPair rows = ui->listView->selection(); int rowcount = rows.second-rows.first+1; if (rows.first == rows.second) { QString name = _config->scanlists()->scanlist(rows.first)->name(); if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete scan list?"), tr("Delete scan list %1?").arg(name))) return; } else { if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete scan lists?"), tr("Delete %1 scan lists?").arg(rowcount))) return; } // collect all selected scan lists // need to collect them first as rows change when deleting QList lists; lists.reserve(rowcount); for (int row=rows.first; row<=rows.second; row++) lists.push_back(_config->scanlists()->scanlist(row)); // remove foreach (ScanList *list, lists) _config->scanlists()->del(list); } void ScanListsView::onEditScanList(unsigned row) { ScanListDialog dialog(_config, _config->scanlists()->scanlist(row)); if (QDialog::Accepted != dialog.exec()) return; dialog.scanlist(); } qdmr-0.12.3/src/scanlistsview.hh000066400000000000000000000007161501654372000165550ustar00rootroot00000000000000#ifndef SCANLISTSVIEW_HH #define SCANLISTSVIEW_HH #include class Config; namespace Ui { class ScanListsView; } class ScanListsView : public QWidget { Q_OBJECT public: explicit ScanListsView(Config *config, QWidget *parent = nullptr); ~ScanListsView(); protected slots: void onAddScanList(); void onRemScanList(); void onEditScanList(unsigned row); private: Ui::ScanListsView *ui; Config *_config; }; #endif // SCANLISTSVIEW_HH qdmr-0.12.3/src/scanlistsview.ui000066400000000000000000000032411501654372000165670ustar00rootroot00000000000000 ScanListsView 0 0 400 300 Form 0 0 Add Scan List Alt++ Delete Scan List Alt+- ConfigObjectListView QWidget
      configobjectlistview.hh
      1
      qdmr-0.12.3/src/searchpopup.cc000066400000000000000000000074411501654372000162000ustar00rootroot00000000000000#include "searchpopup.hh" #include #include #include #include #include #include "logger.hh" SearchPopup::SearchPopup(QAbstractItemView *parent) : QFrame(parent) { setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint); setFrameStyle(QFrame::Panel); setWindowModality(Qt::ApplicationModal); QAction *search = new QAction(this); search->setShortcut(QKeySequence(tr("Ctrl+F"))); parent->addAction(search); connect(search, SIGNAL(triggered(bool)), this, SLOT(showPopup())); _search = new QLineEdit(); connect(_search, SIGNAL(textChanged(QString)), this, SLOT(onSearchChanged(QString))); QAction *hide = new QAction(_search); hide->setShortcut(QKeySequence(Qt::Key_Escape)); _search->addAction(hide, QLineEdit::TrailingPosition); connect(hide, SIGNAL(triggered(bool)), this, SLOT(hide())); _label = new QLabel(); QToolButton *up = new QToolButton(this); up->setIcon(QIcon::fromTheme("edit-move-up")); connect(up, SIGNAL(clicked(bool)), this, SLOT(onPrevious())); QToolButton *down = new QToolButton(this); down->setIcon(QIcon::fromTheme("edit-move-down")); connect(down, SIGNAL(clicked(bool)), this, SLOT(onNext())); QToolButton *close = new QToolButton(this); close->setIcon(QIcon::fromTheme("application-exit")); connect(close, SIGNAL(clicked(bool)), this, SLOT(hide())); QHBoxLayout *layout = new QHBoxLayout(); layout->addWidget(_search); layout->addWidget(_label); layout->addWidget(up); layout->addWidget(down); layout->addWidget(close); setLayout(layout); move(parent->mapToGlobal(QPoint(parent->width()-width()-5, parent->height()-height()+5))); this->hide(); } void SearchPopup::showPopup() { show(); auto itemView = qobject_cast(parent()); move(itemView->mapToGlobal(QPoint(itemView->width()-width()-5, itemView->height()-height()+5))); _search->clear(); _search->setFocus(); } void SearchPopup::onSearchChanged(const QString &text) { QAbstractItemView *itemView = qobject_cast(parent()); QAbstractItemModel *model = itemView->model(); if (text.isEmpty()) { _currentMatch = 0; _matches.clear(); itemView->selectionModel()->clear(); _label->setText(""); return; } _currentMatch = 0; itemView->selectionModel()->clear(); _matches.clear(); for (int i=0; icolumnCount(); i++) _matches.append(model->match(model->index(0,i), Qt::DisplayRole, text, -1, Qt::MatchContains|Qt::MatchWrap)); std::sort( _matches.begin(), _matches.end(), [](const QModelIndex &a, const QModelIndex &b) { if (a.row() < b.row()) return true; if (a.row() > b.row()) return false; return a.column() < b.column(); }); if (_matches.count()) { _label->setText(tr("%1/%2").arg(_currentMatch+1).arg(_matches.count())); itemView->setCurrentIndex(_matches.at(_currentMatch)); } else { _label->setText(""); } } void SearchPopup::onNext() { if (0 == _matches.count()) return; QAbstractItemView *itemView = qobject_cast(parent()); if ((++_currentMatch) >= _matches.count()) _currentMatch = 0; _label->setText(tr("%1/%2").arg(_currentMatch+1).arg(_matches.count())); itemView->setCurrentIndex(_matches.at(_currentMatch)); } void SearchPopup::onPrevious() { if (0 == _matches.count()) return; QAbstractItemView *itemView = qobject_cast(parent()); if (0 == _currentMatch) _currentMatch = _matches.count()-1; _label->setText(QString("%1/%2").arg(_currentMatch+1).arg(_matches.count())); itemView->setCurrentIndex(_matches.at(_currentMatch)); } void SearchPopup::attach(QAbstractItemView *itemview) { if (nullptr == itemview) return; new SearchPopup(itemview); } qdmr-0.12.3/src/searchpopup.hh000066400000000000000000000010431501654372000162020ustar00rootroot00000000000000#ifndef SEARCHPOPUP_HH #define SEARCHPOPUP_HH #include #include class QLabel; class SearchPopup : public QFrame { Q_OBJECT protected: explicit SearchPopup(QAbstractItemView *parent); public: static void attach(QAbstractItemView *itemview); public slots: void showPopup(); protected slots: void onSearchChanged(const QString &text); void onNext(); void onPrevious(); protected: QLineEdit *_search; QLabel *_label; int _currentMatch; QModelIndexList _matches; }; #endif // SEARCHPOPUP_HH qdmr-0.12.3/src/selectivecallbox.cc000066400000000000000000000126501501654372000171750ustar00rootroot00000000000000#include "selectivecallbox.hh" #include #include #include #include /* ********************************************************************************************* * * SelectiveCallBox * ********************************************************************************************* */ SelectiveCallBox::SelectiveCallBox(QWidget *parent) : QWidget{parent}, _typeSelection(nullptr), _stack(nullptr), _ctcss(nullptr), _dcs(nullptr), _inverted(nullptr) { _typeSelection = new QComboBox(); _typeSelection->addItem(tr("None")); _typeSelection->addItem(tr("CTCSS")); _typeSelection->addItem(tr("DCS")); _stack = new QStackedWidget(); _stack->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); _stack->setContentsMargins(0,0,0,0); auto emptyLayout = new QHBoxLayout(); emptyLayout->setContentsMargins(0,0,0,0); auto emptyLabel = new QLabel("None"); emptyLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); emptyLayout->addWidget(emptyLabel); auto emptyWidget = new QWidget(); emptyWidget->setLayout(emptyLayout); emptyWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); emptyWidget->setContentsMargins(0,0,0,0); _stack->addWidget(emptyWidget); // CTCSS settings _ctcss = new QComboBox(); _ctcss->setEditable(true); auto ctcssLayout = new QHBoxLayout(); ctcssLayout->setContentsMargins(0,0,0,0); ctcssLayout->addWidget(_ctcss); ctcssLayout->addWidget(new QLabel(tr("Hz"))); auto ctcssWidget = new QWidget(); ctcssWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); ctcssWidget->setContentsMargins(0,0,0,0); ctcssWidget->setLayout(ctcssLayout); _stack->addWidget(ctcssWidget); // DCS settings _dcs = new QComboBox(); _dcs->setEditable(true); _inverted = new QCheckBox(tr("Inverted")); auto dscLayout = new QHBoxLayout(); dscLayout->setContentsMargins(0,0,0,0); dscLayout->addWidget(_dcs); dscLayout->addWidget(_inverted); auto dcsWidget = new QWidget(); dcsWidget->setLayout(dscLayout); dcsWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); dcsWidget->setContentsMargins(0,0,0,0); _stack->addWidget(dcsWidget); connect(_typeSelection, &QComboBox::currentTextChanged, [=]() { _stack->setCurrentIndex(_typeSelection->currentIndex()); }); foreach (const SelectiveCall &call, SelectiveCall::standard()) { if (call.isInvalid()) continue; if (call.isCTCSS()) _ctcss->addItem(QString("%1.%2").arg(call.mHz()/1000).arg((call.mHz()/100)%10), QVariant::fromValue(call)); else if (call.isDCS() && (! call.isInverted())) _dcs->addItem(QString("%1").arg(call.binCode(), 3, 8, QChar('0')), QVariant(call.binCode())); } auto layout = new QHBoxLayout(); layout->addWidget(_typeSelection,0); layout->addWidget(_stack, 1); layout->setContentsMargins(0,0,0,0); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); setContentsMargins(0,0,0,0); setLayout(layout); } void SelectiveCallBox::setSelectiveCall(const SelectiveCall &call) { if (call.isInvalid()) { _typeSelection->setCurrentIndex(0); } else if (call.isCTCSS()) { _typeSelection->setCurrentIndex(1); int idx = _ctcss->findData(QVariant::fromValue(call)); if (idx >= 0) _ctcss->setCurrentIndex(idx); else _ctcss->setEditText(call.format()); } else if (call.isDCS()) { _typeSelection->setCurrentIndex(2); int idx = _dcs->findData(QVariant(call.binCode())); if (idx >= 0) _dcs->setCurrentIndex(idx); else _dcs->setEditText(QString("%1").arg(call.binCode(), 3, 8, QChar('0'))); _inverted->setChecked(call.isInverted()); } } SelectiveCall SelectiveCallBox::selectiveCall() const { if (0 == _typeSelection->currentIndex()) { return SelectiveCall(); } else if (1 == _typeSelection->currentIndex()) { int idx = _ctcss->currentIndex(); if (0 >= idx) return _ctcss->currentData().value(); return SelectiveCall::parseCTCSS(_ctcss->currentText()); } else if (2 == _typeSelection->currentIndex()) { int idx = _dcs->currentIndex(); if (0 >= idx) return SelectiveCall(_dcs->currentData().toUInt(), _inverted->isChecked()); return SelectiveCall(_dcs->currentText().toUInt(), _inverted->isChecked()); } return SelectiveCall(); } /* ********************************************************************************************* * * SelectiveCallDelegate * ********************************************************************************************* */ SelectiveCallDelegate::SelectiveCallDelegate(QObject *parent) : QStyledItemDelegate(parent) { // pass... } QWidget * SelectiveCallDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); Q_UNUSED(index); auto seditor = new SelectiveCallBox(parent); seditor->setSelectiveCall(index.data(Qt::EditRole).value()); return seditor; } void SelectiveCallDelegate::setEditorData(QWidget *editor, const QModelIndex index) { auto seditor = qobject_cast(editor); seditor->setSelectiveCall(index.data(Qt::EditRole).value()); } void SelectiveCallDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { auto seditor = qobject_cast(editor); model->setData(index, QVariant::fromValue(seditor->selectiveCall())); } qdmr-0.12.3/src/selectivecallbox.hh000066400000000000000000000020201501654372000171750ustar00rootroot00000000000000#ifndef SELECTIVECALLBOX_H #define SELECTIVECALLBOX_H #include #include "signaling.hh" #include #include #include #include class SelectiveCallBox : public QWidget { Q_OBJECT public: explicit SelectiveCallBox(QWidget *parent = nullptr); void setSelectiveCall(const SelectiveCall &call); SelectiveCall selectiveCall() const; signals: void selected(const SelectiveCall &call); private: QComboBox *_typeSelection; QStackedWidget *_stack; QComboBox *_ctcss; QComboBox *_dcs; QCheckBox *_inverted; }; class SelectiveCallDelegate: public QStyledItemDelegate { Q_OBJECT public: explicit SelectiveCallDelegate(QObject *parent=nullptr); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex index); void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; }; #endif // SELECTIVECALLBOX_H qdmr-0.12.3/src/settings.cc000066400000000000000000000343531501654372000155110ustar00rootroot00000000000000#include "settings.hh" #include "logger.hh" #include "config.h" #include "utils.hh" #include #include /* ********************************************************************************************* * * Implementation of SettingsDialog * ********************************************************************************************* */ Settings::Settings(QObject *parent) : QSettings(parent) { // pass... } QDateTime Settings::lastRepeaterUpdate() const { if (! contains("lastRepeaterUpdate")) return QDateTime(); return value("lastRepeaterUpdate").toDateTime(); } void Settings::repeaterUpdated() { setValue("lastRepeaterUpdate", QDateTime::currentDateTime()); } bool Settings::repeaterUpdateNeeded(unsigned period) const { QDateTime last = lastRepeaterUpdate(); if (! last.isValid()) return true; QDateTime now = QDateTime::currentDateTime(); return last.daysTo(now) >= period; } bool Settings::queryPosition() const { return value("queryPosition", false).toBool(); } void Settings::setQueryPosition(bool enable) { setValue("queryPosition", enable); } QString Settings::locator() const { return value("locator","").toString(); } void Settings::setLocator(const QString &locator) { setValue("locator", locator); } QGeoCoordinate Settings::position() const { return loc2deg(locator()); } bool Settings::repeaterBookSourceEnabled() const { return value("repeaterSource/repeaterBook", true).toBool(); } void Settings::enableRepeaterBookSource(bool enabled) { return setValue("repeaterSource/repeaterBook", enabled); } bool Settings::repeaterMapSourceEnabled() const { return value("repeaterSource/repeaterMap", true).toBool(); } void Settings::enableRepeaterMapSource(bool enabled) { return setValue("repeaterSource/repeaterMap", enabled); } bool Settings::hearhamSourceEnabled() const { return value("repeaterSource/hearham", false).toBool(); } void Settings::enableHearhamSource(bool enabled) { return setValue("repeaterSource/hearham", enabled); } bool Settings::radioIdRepeaterSourceEnabled() const { return value("repeaterSource/radioId", true).toBool(); } void Settings::enableRadioIdRepeaterSource(bool enabled) { return setValue("repeaterSource/radioId", enabled); } RepeaterBookSource::Region Settings::repeaterBookRegion() const { return value("repeaterBookRegion", RepeaterBookSource::Region::World).value(); } void Settings::setRepeaterBookRegion(RepeaterBookSource::Region region) { setValue("repeaterBookRegion", region); } bool Settings::disableAutoDetect() const { return value("disableAutoDetect", false).toBool(); } void Settings::setDisableAutoDetect(bool disable) { setValue("disableAutoDetect", disable); } bool Settings::updateCodeplug() const { return value("updateCodeplug", true).toBool(); } void Settings::setUpdateCodeplug(bool update) { setValue("updateCodeplug", update); } bool Settings::autoEnableGPS() const { return value("autoEnableGPS", false).toBool(); } void Settings::setAutoEnableGPS(bool update) { setValue("autoEnableGPS", update); } bool Settings::autoEnableRoaming() const { return value("autoEnableRoaming", false).toBool(); } void Settings::setAutoEnableRoaming(bool update) { setValue("autoEnableRoaming", update); } QDir Settings::lastDirectory() const { return QDir(value("lastDir", QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first()).toString()); } void Settings::setLastDirectoryDir(const QDir &dir) { setValue("lastDir", dir.absolutePath()); } Codeplug::Flags Settings::codePlugFlags() const { Codeplug::Flags flags; flags.updateCodePlug = updateCodeplug(); flags.autoEnableGPS = autoEnableGPS(); flags.autoEnableRoaming = autoEnableRoaming(); return flags; } bool Settings::limitCallSignDBEntries() const { return value("limitCallSignDBEntries", false).toBool(); } void Settings::setLimitCallSignDBEnties(bool enable) { setValue("limitCallSignDBEntries", enable); } unsigned Settings::maxCallSignDBEntries() const { return value("maxCallSignDBEntries", 1).toInt(); } void Settings::setMaxCallSignDBEntries(unsigned max) { setValue("maxCallSignDBEntries", max); } bool Settings::selectUsingUserDMRID() { int num = beginReadArray("callSignDBPrefixes"); endArray(); return value("selectCallSignDBUsingUserDMRID", true).toBool() || (0 == num); } void Settings::setSelectUsingUserDMRID(bool enable) { setValue("selectCallSignDBUsingUserDMRID", enable); } QSet Settings::callSignDBPrefixes() { QSet prefixes; int num = beginReadArray("callSignDBPrefixes"); for (int i=0; i &prefixes) { beginWriteArray("callSignDBPrefixes"); unsigned i = 0; foreach (unsigned prefix, prefixes) { setArrayIndex(i); setValue("prefix", prefix); i++; } endArray(); } bool Settings::ignoreVerificationWarning() const { return value("ignoreVerificationWarning", true).toBool(); } void Settings::setIgnoreVerificationWarning(bool ignore) { setValue("ignoreVerificationWarning", ignore); } bool Settings::ignoreFrequencyLimits() const { return value("ignoreFrequencyLimits", false).toBool(); } void Settings::setIgnoreFrequencyLimits(bool ignore) { setValue("ignoreFrequencyLimits", ignore); } bool Settings::showCommercialFeatures() const { return value("showCommercialFeatures", false).toBool(); } void Settings::setShowCommercialFeatures(bool show) { setValue("showCommercialFeatures", show); } bool Settings::showExtensions() const { return value("showExtensions", false).toBool(); } void Settings::setShowExtensions(bool show) { setValue("showExtensions", show); } bool Settings::hideChannelNote() const { return value("hideChannelNote", false).toBool(); } void Settings::setHideChannelNote(bool hide) { setValue("hideChannelNote", hide); } bool Settings::hideGSPNote() const { return value("hideGPSNote", false).toBool(); } void Settings::setHideGPSNote(bool hide) { setValue("hideGPSNote", hide); } bool Settings::hideRoamingChannelNote() const { return value("hideRoamingChannelNote", false).toBool(); } void Settings::setHideRoamingChannelNote(bool hide) { setValue("hideRoamingChannelNote", hide); } bool Settings::hideRoamingNote() const { return value("hideRoamingNote", false).toBool(); } void Settings::setHideRoamingNote(bool hide) { setValue("hideRoamingNote", hide); } bool Settings::hideZoneNote() const { return value("hideZoneNote", false).toBool(); } void Settings::setHideZoneNote(bool hide) { setValue("hideZoneNote", hide); } bool Settings::showDisclaimer() const { return value("showDisclaimer", true).toBool(); } void Settings::setShowDisclaimer(bool show) { setValue("showDisclaimer", show); } ConfigMergeVisitor::ItemStrategy Settings::configMergeItemStrategy() const { return (ConfigMergeVisitor::ItemStrategy)value( "configMergeItemStrategy", (uint) ConfigMergeVisitor::ItemStrategy::Duplicate).toUInt(); } void Settings::setConfigMergeItemStrategy(ConfigMergeVisitor::ItemStrategy strategy) { setValue("configMergeItemStrategy", (uint)strategy); } ConfigMergeVisitor::SetStrategy Settings::configMergeSetStrategy() const { return (ConfigMergeVisitor::SetStrategy)value( "configMergeSetStrategy", (uint) ConfigMergeVisitor::SetStrategy::Merge).toUInt(); } void Settings::setConfigMergeSetStrategy(ConfigMergeVisitor::SetStrategy strategy) { setValue("configMergeSetStrategy", (uint)strategy); } QByteArray Settings::mainWindowState() const { return value("mainWindowState", QByteArray()).toByteArray(); } void Settings::setMainWindowState(const QByteArray &state) { setValue("mainWindowState", state); } QByteArray Settings::headerState(const QString &objName) const { if (objName.isEmpty()) return QByteArray(); QString key = QString("headerState/%1").arg(objName); return value(key, QByteArray()).toByteArray(); } void Settings::setHeaderState(const QString &objName, const QByteArray &state) { if (objName.isEmpty()) return; QString key = QString("headerState/%1").arg(objName); setValue(key, state); } bool Settings::isUpdated() const { if (! contains("version")) return false; return VERSION_STRING == value("version").toString(); } void Settings::markUpdated() { setValue("version", VERSION_STRING); } /* ********************************************************************************************* * * Implementation of SettingsDialog * ********************************************************************************************* */ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent) { setupUi(this); Settings settings; _source = QGeoPositionInfoSource::createDefaultSource(this); if (_source) { connect(_source, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(positionUpdated(QGeoPositionInfo))); if (settings.queryPosition()) _source->startUpdates(); } queryLocation->setChecked(settings.queryPosition()); locatorEntry->setText(settings.locator()); if (queryLocation->isChecked()) locatorEntry->setEnabled(false); Ui::SettingsDialog::repeaterBookEnable->setChecked(settings.repeaterBookSourceEnabled()); switch (settings.repeaterBookRegion()) { case RepeaterBookSource::World: Ui::SettingsDialog::repeaterBookRegion->setCurrentIndex(0); break; case RepeaterBookSource::NorthAmerica: Ui::SettingsDialog::repeaterBookRegion->setCurrentIndex(1); break; } Ui::SettingsDialog::repeaterMapEnable->setChecked(settings.repeaterMapSourceEnabled()); Ui::SettingsDialog::hearhamEnable->setChecked(settings.hearhamSourceEnabled()); Ui::SettingsDialog::radioIdEnable->setChecked(settings.radioIdRepeaterSourceEnabled()); connect(Ui::SettingsDialog::ignoreFrequencyLimits, SIGNAL(toggled(bool)), this, SLOT(onIgnoreFrequencyLimitsSet(bool))); connect(queryLocation, SIGNAL(toggled(bool)), this, SLOT(onSystemLocationToggled(bool))); Ui::SettingsDialog::disableAutoDetect->setChecked(settings.disableAutoDetect()); Ui::SettingsDialog::updateCodeplug->setChecked(settings.updateCodeplug()); Ui::SettingsDialog::autoEnableGPS->setChecked(settings.autoEnableGPS()); Ui::SettingsDialog::autoEnableRoaming->setChecked(settings.autoEnableRoaming()); Ui::SettingsDialog::ignoreVerificationWarnings->setChecked(settings.ignoreVerificationWarning()); Ui::SettingsDialog::ignoreFrequencyLimits->setChecked(settings.ignoreFrequencyLimits()); Ui::SettingsDialog::dbLimitEnable->setChecked(settings.limitCallSignDBEntries()); if (! settings.limitCallSignDBEntries()) Ui::SettingsDialog::dbLimit->setEnabled(false); Ui::SettingsDialog::dbLimit->setValue(settings.maxCallSignDBEntries()); Ui::SettingsDialog::useUserId->setChecked(settings.selectUsingUserDMRID()); if (settings.selectUsingUserDMRID()) Ui::SettingsDialog::prefixes->setEnabled(false); QSet prefs = settings.callSignDBPrefixes(); QStringList prefs_text; foreach (unsigned prefix, prefs) { prefs_text.append(QString::number(prefix)); } Ui::SettingsDialog::prefixes->setText(prefs_text.join(", ")); Ui::SettingsDialog::commercialFeatures->setChecked(settings.showCommercialFeatures()); Ui::SettingsDialog::showExtensions->setChecked(settings.showExtensions()); connect(Ui::SettingsDialog::dbLimitEnable, SIGNAL(toggled(bool)), this, SLOT(onDBLimitToggled(bool))); connect(Ui::SettingsDialog::useUserId, SIGNAL(toggled(bool)), this, SLOT(onUseUserDMRIdToggled(bool))); } bool SettingsDialog::systemLocationEnabled() const { return queryLocation->isChecked(); } QString SettingsDialog::locator() const { return locatorEntry->text().simplified(); } void SettingsDialog::onSystemLocationToggled(bool enabled) { locatorEntry->setEnabled(! enabled); if (enabled && _source) _source->startUpdates(); else if (_source) _source->stopUpdates(); } void SettingsDialog::onIgnoreFrequencyLimitsSet(bool enabled) { if (enabled) { Ui::SettingsDialog::ignoreFrequencyLimits->setText(tr("Warning!")); } else { Ui::SettingsDialog::ignoreFrequencyLimits->setText(""); } } void SettingsDialog::positionUpdated(const QGeoPositionInfo &info) { logDebug() << "Application: Current position: " << info.coordinate().toString(); if (info.isValid() && queryLocation->isChecked()) { locatorEntry->setText(deg2loc(info.coordinate())); } } void SettingsDialog::onDBLimitToggled(bool enable) { Ui::SettingsDialog::dbLimit->setEnabled(enable); } void SettingsDialog::onUseUserDMRIdToggled(bool enable) { Ui::SettingsDialog::prefixes->setEnabled(! enable); } void SettingsDialog::accept() { Settings settings; settings.setQueryPosition(queryLocation->isChecked()); settings.setLocator(locatorEntry->text().simplified()); settings.setDisableAutoDetect(disableAutoDetect->isChecked()); settings.enableRepeaterBookSource(Ui::SettingsDialog::repeaterBookEnable->isChecked()); if (0 == Ui::SettingsDialog::repeaterBookRegion->currentIndex()) settings.setRepeaterBookRegion(RepeaterBookSource::World); else settings.setRepeaterBookRegion(RepeaterBookSource::NorthAmerica); settings.enableRepeaterMapSource(Ui::SettingsDialog::repeaterMapEnable->isChecked()); settings.enableHearhamSource(Ui::SettingsDialog::hearhamEnable->isChecked()); settings.enableRadioIdRepeaterSource(Ui::SettingsDialog::radioIdEnable->isChecked()); settings.setUpdateCodeplug(updateCodeplug->isChecked()); settings.setAutoEnableGPS(autoEnableGPS->isChecked()); settings.setAutoEnableRoaming(autoEnableRoaming->isChecked()); settings.setIgnoreVerificationWarning(ignoreVerificationWarnings->isChecked()); settings.setIgnoreFrequencyLimits(ignoreFrequencyLimits->isChecked()); settings.setLimitCallSignDBEnties(dbLimitEnable->isChecked()); settings.setMaxCallSignDBEntries(dbLimit->value()); settings.setSelectUsingUserDMRID(useUserId->isChecked()); QStringList prefs_text = prefixes->text().split(","); QSet prefs; foreach (QString pref, prefs_text) { bool ok=true; unsigned prefix = pref.toUInt(&ok); if (ok) prefs.insert(prefix); } settings.setCallSignDBPrefixes(prefs); settings.setShowCommercialFeatures(commercialFeatures->isChecked()); settings.setShowExtensions(showExtensions->isChecked()); QDialog::accept(); } qdmr-0.12.3/src/settings.hh000066400000000000000000000073131501654372000155170ustar00rootroot00000000000000#ifndef SETTINGS_HH #define SETTINGS_HH #include #include #include #include #include #include "ui_settingsdialog.h" #include "codeplug.hh" #include "configmergevisitor.hh" #include "repeaterbooksource.hh" class Settings : public QSettings { Q_OBJECT public: explicit Settings(QObject *parent=nullptr); QDateTime lastRepeaterUpdate() const; bool repeaterUpdateNeeded(unsigned period=7) const; void repeaterUpdated(); bool queryPosition() const; void setQueryPosition(bool enable); QString locator() const; void setLocator(const QString &locator); QGeoCoordinate position() const; bool disableAutoDetect() const; void setDisableAutoDetect(bool disable); bool repeaterBookSourceEnabled() const; void enableRepeaterBookSource(bool enable); bool repeaterMapSourceEnabled() const; void enableRepeaterMapSource(bool enable); bool hearhamSourceEnabled() const; void enableHearhamSource(bool enable); bool radioIdRepeaterSourceEnabled() const; void enableRadioIdRepeaterSource(bool enable); RepeaterBookSource::Region repeaterBookRegion() const; void setRepeaterBookRegion(RepeaterBookSource::Region region); bool updateCodeplug() const; void setUpdateCodeplug(bool update); bool autoEnableGPS() const; void setAutoEnableGPS(bool enable); bool autoEnableRoaming() const; void setAutoEnableRoaming(bool enable); QDir lastDirectory() const; void setLastDirectoryDir(const QDir &dir); Codeplug::Flags codePlugFlags() const; bool limitCallSignDBEntries() const; void setLimitCallSignDBEnties(bool enable); unsigned maxCallSignDBEntries() const; void setMaxCallSignDBEntries(unsigned max); bool selectUsingUserDMRID(); void setSelectUsingUserDMRID(bool enable); QSet callSignDBPrefixes(); void setCallSignDBPrefixes(const QSet &prefixes); bool ignoreVerificationWarning() const; void setIgnoreVerificationWarning(bool ignore); bool ignoreFrequencyLimits() const; void setIgnoreFrequencyLimits(bool ignore); bool showCommercialFeatures() const; void setShowCommercialFeatures(bool show); bool showExtensions() const; void setShowExtensions(bool show); bool hideChannelNote() const; void setHideChannelNote(bool hide); bool hideGSPNote() const; void setHideGPSNote(bool hide); bool hideRoamingChannelNote() const; void setHideRoamingChannelNote(bool hide); bool hideRoamingNote() const; void setHideRoamingNote(bool hide); bool hideZoneNote() const; void setHideZoneNote(bool hide); bool showDisclaimer() const; void setShowDisclaimer(bool show); ConfigMergeVisitor::ItemStrategy configMergeItemStrategy() const; void setConfigMergeItemStrategy(ConfigMergeVisitor::ItemStrategy strategy); ConfigMergeVisitor::SetStrategy configMergeSetStrategy() const; void setConfigMergeSetStrategy(ConfigMergeVisitor::SetStrategy strategy); QByteArray mainWindowState() const; void setMainWindowState(const QByteArray &state); QByteArray headerState(const QString &objName) const; void setHeaderState(const QString &objName, const QByteArray &state); bool isUpdated() const; void markUpdated(); }; class SettingsDialog: public QDialog, private Ui::SettingsDialog { Q_OBJECT public: explicit SettingsDialog(QWidget *parent=nullptr); bool systemLocationEnabled() const; QString locator() const; public slots: void accept(); protected slots: void onSystemLocationToggled(bool enable); void positionUpdated(const QGeoPositionInfo &info); void onIgnoreFrequencyLimitsSet(bool enabled); void onDBLimitToggled(bool enable); void onUseUserDMRIdToggled(bool enable); protected: QGeoPositionInfoSource *_source; }; #endif // SETTINGS_HH qdmr-0.12.3/src/settingsdialog.ui000066400000000000000000000413251501654372000167160ustar00rootroot00000000000000 SettingsDialog 0 0 400 488 0 0 Settings 0 Data Sources 0 0 Location System location Locator 0 0 Repeater Info Sources repeaterbook.com enable 0 0 World North America repeatermap.de enable hearham.com enable radioid.net enable Programming 0 0 Radio Interfaces disable auto-detect 0 0 Radio Programming QFormLayout::AllNonFixedFieldsGrow Update codeplug <html><head/><body><p>Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.</p><p><br/></p><p>If selected, QDMR downloads the codeplug from the radio and updates only those settings specified. The remaining settings within the radio are not touched (recommended).</p></body></html> Auto-enable GPS When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically. Auto-enable roaming When a roaming zone is defined and used by any channel, the automatic roaming gets enabled. Ignore verification warnings <html><head/><body><p>As the communication interface to the radio is kept open after verification, time-outs may occur and the code-plug upload may fail when the verification dialog pops up. To prevent this, verification warnings can be ignored, eliminating the time-gap between verification and upload. Verification errors still prevent the upload.</p></body></html> Ignore frequency limits Do not set this option unless you know what you are doing. 0 0 Call-Sign DB Limit number of DB entries When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent). Number of DB entries 0 0 Specifies the number of DB entries (if enabled above). 1 999999 Select using my DMR ID If enabled, the entries are selected using the users DMR ID. Select using prefixes If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries. Extensions 0 0 Extended Features Show commercial features Show device extensions Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() SettingsDialog accept() 248 254 157 274 buttonBox rejected() SettingsDialog reject() 316 260 286 274 qdmr-0.12.3/src/transponderfrequencydelegate.cc000066400000000000000000000050121501654372000216130ustar00rootroot00000000000000#include "transponderfrequencydelegate.hh" #include "satellitedatabase.hh" /* ********************************************************************************************* * * Implementation of TransponderFrequencyEditor * ********************************************************************************************* */ TransponderFrequencyEditor::TransponderFrequencyEditor(unsigned int satId, bool uplink, Transponder::Mode mode, const TransponderDatabase &transponder, QWidget *parent) : QComboBox(parent) { setEditable(true); for (const Transponder &tp: transponder) { if (tp.satellite() != satId) continue; if (tp.mode() != mode) continue; Frequency f = uplink ? tp.uplink() : tp.downlink(); if (0 == f.inHz()) continue; addItem(QString("%1 (%2)").arg(tp.name(), f.format()), QVariant::fromValue(f)); } } void TransponderFrequencyEditor::setFrequency(const Frequency ¤t) { for (int i=0; i() == current) { setCurrentIndex(i); break; } } } Frequency TransponderFrequencyEditor::frequency() const { return currentData().value(); } /* ********************************************************************************************* * * Implementation of TransponderFrequencyDelegate * ********************************************************************************************* */ TransponderFrequencyDelegate::TransponderFrequencyDelegate(bool uplink, Transponder::Mode mode, QObject *parent) : QStyledItemDelegate(parent), _uplink(uplink), _mode(mode) { // pass... } QWidget * TransponderFrequencyDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); auto model = qobject_cast(index.model()); unsigned int satId = model->getAt(index.row()).id(); return new TransponderFrequencyEditor(satId, _uplink, _mode, model->transponders(), parent); } void TransponderFrequencyDelegate::setEditorData(QWidget *editor, const QModelIndex index) { auto feditor = qobject_cast(editor); feditor->setFrequency(index.data(Qt::EditRole).value()); } void TransponderFrequencyDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { model->setData( index, QVariant::fromValue(qobject_cast(editor)->frequency())); } qdmr-0.12.3/src/transponderfrequencydelegate.hh000066400000000000000000000020501501654372000216240ustar00rootroot00000000000000#ifndef TRANSPONDERFREQUENCYDELEGATE_HH #define TRANSPONDERFREQUENCYDELEGATE_HH #include #include #include "transponderdatabase.hh" class TransponderFrequencyEditor: public QComboBox { Q_OBJECT public: explicit TransponderFrequencyEditor( unsigned int satId, bool uplink, Transponder::Mode mode, const TransponderDatabase &transponder, QWidget *parent=nullptr); void setFrequency(const Frequency ¤t); Frequency frequency() const; }; class TransponderFrequencyDelegate: public QStyledItemDelegate { Q_OBJECT public: explicit TransponderFrequencyDelegate(bool uplink, Transponder::Mode mode, QObject *parent=nullptr); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex index); void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; protected: bool _uplink; Transponder::Mode _mode; }; #endif // TRANSPONDERFREQUENCYDELEGATE_HH qdmr-0.12.3/src/verifydialog.cc000066400000000000000000000024041501654372000163250ustar00rootroot00000000000000#include "verifydialog.hh" #include "radiolimits.hh" #include "application.hh" #include VerifyDialog::VerifyDialog(const RadioLimitContext &issues, bool upload, QWidget *parent) : QDialog(parent) { setupUi(this); Application *app = qobject_cast(QApplication::instance()); bool valid = true; for (int i=0; isetForeground(Qt::gray); break; case RadioLimitIssue::Warning: if (app->isDarkMode()) item->setForeground(Qt::white); else item->setForeground(Qt::black); break; case RadioLimitIssue::Critical: if (app->isDarkMode()) item->setForeground(Qt::red); else item->setForeground(Qt::darkRed); valid = false; break; } listWidget->addItem(item); } if (upload) { buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); QPushButton *ignore = buttonBox->button(QDialogButtonBox::Ok); ignore->setEnabled(valid); } } qdmr-0.12.3/src/verifydialog.hh000066400000000000000000000005511501654372000163400ustar00rootroot00000000000000#ifndef VERIFYDIALOG_HH #define VERIFYDIALOG_HH #include #include "radio.hh" #include "ui_verifydialog.h" class RadioLimitContext; class VerifyDialog : public QDialog, private Ui::VerifyDialog { Q_OBJECT public: public: explicit VerifyDialog(const RadioLimitContext &ctx, bool upload, QWidget *parent = nullptr); }; #endif // VERIFYDIALOG_HH qdmr-0.12.3/src/verifydialog.ui000066400000000000000000000027161501654372000163630ustar00rootroot00000000000000 VerifyDialog 0 0 400 300 Verify Codeplug Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() VerifyDialog accept() 248 254 157 274 buttonBox rejected() VerifyDialog reject() 316 260 286 274 qdmr-0.12.3/src/zonedialog.cc000066400000000000000000000074141501654372000160020ustar00rootroot00000000000000#include "zonedialog.hh" #include "settings.hh" #include "config.hh" #include "zone.hh" #include "channel.hh" #include "channelselectiondialog.hh" #include "settings.hh" #include /* ********************************************************************************************* * * Implementation of ZoneDialog * ********************************************************************************************* */ ZoneDialog::ZoneDialog(Config *config, Zone *zone, QWidget *parent) : QDialog(parent), _config(config), _myZone(new Zone(this)), _zone(zone) { setWindowTitle(tr("Edit Zone")); if (_zone) _myZone->copy(*_zone); construct(); } ZoneDialog::ZoneDialog(Config *config, QWidget *parent) : QDialog(parent), _config(config), _myZone(new Zone(this)), _zone(nullptr) { setWindowTitle(tr("Create Zone")); construct(); } void ZoneDialog::construct() { setupUi(this); Settings settings; if (settings.hideZoneNote()) zoneHint->setVisible(false); zoneName->setText(_myZone->name()); listAView->setModel(new ChannelRefListWrapper(_myZone->A())); listBView->setModel(new ChannelRefListWrapper(_myZone->B())); extensionView->setObjectName("zoneExtension"); extensionView->setObject(_myZone, _config); if (! settings.showExtensions()) { tabWidget->tabBar()->hide(); } connect(addChannelA, SIGNAL(clicked()), this, SLOT(onAddChannelA())); connect(remChannelA, SIGNAL(clicked()), this, SLOT(onRemChannelA())); connect(addChannelB, SIGNAL(clicked()), this, SLOT(onAddChannelB())); connect(remChannelB, SIGNAL(clicked()), this, SLOT(onRemChannelB())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(zoneHint, SIGNAL(linkActivated(QString)), this, SLOT(onHideZoneHint())); } void ZoneDialog::onAddChannelA() { MultiChannelSelectionDialog dia(_config->channelList()); if (QDialog::Accepted != dia.exec()) return; QList channels = dia.channel(); foreach (Channel *channel, channels) { if (0 <= _myZone->A()->indexOf(channel)) continue; _myZone->A()->add(channel); } } void ZoneDialog::onRemChannelA() { if (! listAView->hasSelection()) { QMessageBox::information(nullptr, tr("Cannot remove channel"), tr("Select at least one channel first.")); return; } QPair selection = listAView->selection(); QList channels; for (int i=selection.first; i<=selection.second; i++) channels.push_back(_myZone->A()->get(i)->as()); foreach (Channel *channel, channels) { _myZone->A()->del(channel); } } void ZoneDialog::onAddChannelB() { MultiChannelSelectionDialog dia(_config->channelList()); if (QDialog::Accepted != dia.exec()) return; QList channels = dia.channel(); foreach (Channel *channel, channels) { if (0 <= _myZone->B()->indexOf(channel)) continue; _myZone->B()->add(channel); } } void ZoneDialog::onRemChannelB() { if (! listBView->hasSelection()) { QMessageBox::information(nullptr, tr("Cannot remove channel"), tr("Select at least one channel first.")); return; } QPair selection = listBView->selection(); QList channels; for (int i=selection.first; i<=selection.second; i++) channels.push_back(_myZone->B()->get(i)->as()); foreach (Channel *channel, channels) { _myZone->B()->del(channel); } } void ZoneDialog::onHideZoneHint() { zoneHint->setVisible(false); Settings settings; settings.setHideZoneNote(true); } Zone * ZoneDialog::zone() { _myZone->setName(zoneName->text()); Zone *zone = _myZone; if (_zone) { _zone->copy(*_myZone); zone = _zone; } else { _myZone->setParent(nullptr); } return zone; } qdmr-0.12.3/src/zonedialog.hh000066400000000000000000000011231501654372000160030ustar00rootroot00000000000000#ifndef ZONEDIALOG_HH #define ZONEDIALOG_HH #include #include "ui_zonedialog.h" class Config; class Zone; class ZoneDialog: public QDialog, private Ui::ZoneDialog { Q_OBJECT public: ZoneDialog(Config *config, QWidget *parent=nullptr); ZoneDialog(Config *config, Zone *zone, QWidget *parent=nullptr); Zone *zone(); protected slots: void onAddChannelA(); void onRemChannelA(); void onAddChannelB(); void onRemChannelB(); void onHideZoneHint(); protected: void construct(); protected: Config *_config; Zone *_myZone; Zone *_zone; }; #endif // ZONEDIALOG_HH qdmr-0.12.3/src/zonedialog.ui000066400000000000000000000166101501654372000160300ustar00rootroot00000000000000 ZoneDialog 0 0 550 475 0 0 Edit Zone 0 Basic padding:10px;border: 2px solid black; border-radius: 10px; <html><head/><body><p align="justify"><span style=" font-weight:600;">Note:</span> Zones are collections of channels that are usually valid for a specific region. I.e., a collection of channels for repeaters within a certain region. </p><p align="justify">QDMR manages zones by allowing for two independent channel lists for each VFO of the radio (if it has two). Many radios however, allow one to assign zones to each VFO individually. In these cases, QDMR will split the zone into two (A &amp; B) and program them individually into the radio.</p><p align="right"><a href="#hide"><span style=" text-decoration: underline; color:#0000ff;">Hide</span></a></p></body></html> Qt::RichText true Name zoneName 0 0 Channels A 0 0 add remove 0 0 Channels B 0 0 add remove Extension Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ExtensionView QWidget
      extensionview.hh
      1
      ConfigObjectListView QWidget
      configobjectlistview.hh
      1
      buttonBox accepted() ZoneDialog accept() 248 254 157 274 buttonBox rejected() ZoneDialog reject() 316 260 286 274
      qdmr-0.12.3/src/zonelistview.cc000066400000000000000000000042271501654372000164100ustar00rootroot00000000000000#include "zonelistview.hh" #include "ui_zonelistview.h" #include "config.hh" #include "zonedialog.hh" #include ZoneListView::ZoneListView(Config *config, QWidget *parent) : QWidget(parent), ui(new Ui::ZoneListView), _config(config) { ui->setupUi(this); ui->listView->setModel(new ZoneListWrapper(_config->zones(), ui->listView)); connect(ui->addZone, SIGNAL(clicked()), this, SLOT(onAddZone())); connect(ui->remZone, SIGNAL(clicked()), this, SLOT(onRemZone())); connect(ui->listView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditZone(unsigned))); } ZoneListView::~ZoneListView() { delete ui; } void ZoneListView::onAddZone() { ZoneDialog dialog(_config); if (QDialog::Accepted != dialog.exec()) return; int row=-1; if (ui->listView->hasSelection()) row = ui->listView->selection().second+1; _config->zones()->add(dialog.zone(), row); } void ZoneListView::onRemZone() { // Check if there is any zones selected if (! ui->listView->hasSelection()) { QMessageBox::information( nullptr, tr("Cannot delete zone"), tr("Cannot delete zone: You have to select a zone first.")); return; } // Get selection and ask for deletion QPair rows = ui->listView->selection(); int rowcount = rows.second-rows.first+1; if (rows.first == rows.second) { QString name = _config->zones()->zone(rows.first)->name(); if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete zone?"), tr("Delete zone %1?").arg(name))) return; } else { if (QMessageBox::No == QMessageBox::question( nullptr, tr("Delete zones?"), tr("Delete %1 zones?").arg(rowcount))) return; } // collect all selected zones // need to collect them first as rows change when deleting QList lists; lists.reserve(rowcount); for(int row=rows.first; row<=rows.second; row++) lists.push_back(_config->zones()->zone(row)); // remove foreach (Zone *zone, lists) _config->zones()->del(zone); } void ZoneListView::onEditZone(unsigned row) { ZoneDialog dialog(_config, _config->zones()->zone(row)); if (QDialog::Accepted != dialog.exec()) return; dialog.zone(); } qdmr-0.12.3/src/zonelistview.hh000066400000000000000000000006721501654372000164220ustar00rootroot00000000000000#ifndef ZONELISTVIEW_HH #define ZONELISTVIEW_HH #include class Config; namespace Ui { class ZoneListView; } class ZoneListView : public QWidget { Q_OBJECT public: explicit ZoneListView(Config *config, QWidget *parent = nullptr); ~ZoneListView(); protected slots: void onAddZone(); void onRemZone(); void onEditZone(unsigned row); private: Ui::ZoneListView *ui; Config *_config; }; #endif // ZONELISTVIEW_HH qdmr-0.12.3/src/zonelistview.ui000066400000000000000000000031061501654372000164330ustar00rootroot00000000000000 ZoneListView 0 0 400 300 Form 0 0 Add Zone Alt++ Delete Zone Alt+- ConfigObjectListView QWidget
      configobjectlistview.hh
      1
      qdmr-0.12.3/test/000077500000000000000000000000001501654372000135225ustar00rootroot00000000000000qdmr-0.12.3/test/CMakeLists.txt000066400000000000000000000144331501654372000162670ustar00rootroot00000000000000qt5_add_resources(testlib_RCC_SOURCES resources.qrc) qt5_wrap_cpp(libdmrconfigtest_MOC_SOURCES libdmrconfigtest.hh) add_library(libdmrconfigtest STATIC libdmrconfigtest.cc ${libdmrconfigtest_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(libdmrconfigtest ${LIBS} libdmrconf) qt5_wrap_cpp(configtest_MOC_SOURCES configtest.hh) add_executable(configtest configtest.cc ${configtest_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(configtest ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(tableformattest_MOC_SOURCES tableformattest.hh) add_executable(tableformattest tableformattest.cc ${tableformattest_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(tableformattest ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(copytest_MOC_SOURCES copytest.hh) add_executable(copytest copytest.cc ${copytest_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(copytest ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(labeltest_MOC_SOURCES labeltest.hh) add_executable(labeltest labeltest.cc ${labeltest_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(labeltest ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(trafotest_MOC_SOURCES trafotest.hh) add_executable(trafotest trafotest.cc ${trafotest_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(trafotest ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(crc32test_MOC_SOURCES crc32test.hh) add_executable(crc32test crc32test.cc ${crc32test_MOC_SOURCES}) target_link_libraries(crc32test ${LIBS} libdmrconf) qt5_wrap_cpp(utilstest_MOC_SOURCES utilstest.hh) add_executable(utilstest utilstest.cc ${utilstest_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(utilstest ${LIBS} libdmrconf) qt5_wrap_cpp(chirptest_MOC_SOURCES chirptest.hh) add_executable(chirptest chirptest.cc ${chirptest_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(chirptest ${LIBS} libdmrconf) qt5_wrap_cpp(mergetest_MOC_SOURCES mergetest.hh) add_executable(mergetest mergetest.cc ${mergetest_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(mergetest ${LIBS} libdmrconf) qt5_wrap_cpp(smstemplatetest_MOC_SOURCES smstemplatetest.hh) add_executable(smstemplatetest smstemplatetest.cc ${smstemplatetest_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(smstemplatetest ${LIBS} libdmrconf libdmrconfigtest) # Unit tests for Radioddity devices qt5_wrap_cpp(rd5r_MOC_SOURCES rd5r_test.hh) add_executable(rd5r_test rd5r_test.cc ${rd5r_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(rd5r_test ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(gd73_MOC_SOURCES gd73_test.hh) add_executable(gd73_test gd73_test.cc ${gd73_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(gd73_test ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(gd77_MOC_SOURCES gd77_test.hh) add_executable(gd77_test gd77_test.cc ${gd77_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(gd77_test ${LIBS} libdmrconf libdmrconfigtest) # Unit tests for OpenGD77 firmware qt5_wrap_cpp(opengd77_MOC_SOURCES opengd77_test.hh) add_executable(opengd77_test opengd77_test.cc ${opengd77_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(opengd77_test ${LIBS} libdmrconf libdmrconfigtest) # Unit tests for OpenGD77 firmware qt5_wrap_cpp(openuv380_MOC_SOURCES openuv380_test.hh) add_executable(openuv380_test openuv380_test.cc ${openuv380_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(openuv380_test ${LIBS} libdmrconf libdmrconfigtest) # Unit tests for TyT devices qt5_wrap_cpp(md390_MOC_SOURCES md390_test.hh) add_executable(md390_test md390_test.cc ${md390_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(md390_test ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(uv390_MOC_SOURCES uv390_test.hh) add_executable(uv390_test uv390_test.cc ${uv390_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(uv390_test ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(md2017_MOC_SOURCES md2017_test.hh) add_executable(md2017_test md2017_test.cc ${md2017_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(md2017_test ${LIBS} libdmrconf libdmrconfigtest) # Unit tests for AnyTone devices qt5_wrap_cpp(d868uve_MOC_SOURCES d868uve_test.hh) add_executable(d868uve_test d868uve_test.cc ${d868uve_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(d868uve_test ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(d878uv_MOC_SOURCES d878uv_test.hh) add_executable(d878uv_test d878uv_test.cc ${d878uv_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(d878uv_test ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(d878uv2_MOC_SOURCES d878uv2_test.hh) add_executable(d878uv2_test d878uv2_test.cc ${d878uv2_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(d878uv2_test ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(d578uv_MOC_SOURCES d578uv_test.hh) add_executable(d578uv_test d578uv_test.cc ${d578uv_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(d578uv_test ${LIBS} libdmrconf libdmrconfigtest) # Unit tests for Baofeng devices qt5_wrap_cpp(dmr6x2uv_MOC_SOURCES dmr6x2uv_test.hh) add_executable(dmr6x2uv_test dmr6x2uv_test.cc ${dmr6x2uv_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(dmr6x2uv_test ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(dm1701_MOC_SOURCES dm1701_test.hh) add_executable(dm1701_test dm1701_test.cc ${dm1701_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(dm1701_test ${LIBS} libdmrconf libdmrconfigtest) qt5_wrap_cpp(dr1801_MOC_SOURCES dr1801_test.hh) add_executable(dr1801_test dr1801_test.cc ${dr1801_MOC_SOURCES} ${testlib_RCC_SOURCES}) target_link_libraries(dr1801_test ${LIBS} libdmrconf libdmrconfigtest) add_test(NAME Config COMMAND configtest) add_test(NAME CRC32 COMMAND crc32test) add_test(NAME Utils COMMAND utilstest) add_test(NAME CHIRP COMMAND chirptest) add_test(NAME RD5R COMMAND rd5r_test) add_test(NAME GD77 COMMAND gd77_test) add_test(NAME OpenGD77 COMMAND opengd77_test) add_test(NAME MD390 COMMAND md390_test) add_test(NAME UV390 COMMAND uv390_test) add_test(NAME MD2017 COMMAND md2017_test) add_test(NAME D868UVE COMMAND d868uve_test) add_test(NAME D878UV COMMAND d878uv_test) add_test(NAME D878UV2 COMMAND d878uv2_test) add_test(NAME D578UV COMMAND d578uv_test) add_test(NAME DMR6X2UV COMMAND dmr6x2uv_test) add_test(NAME DM1701 COMMAND dm1701_test) add_test(NAME DR1801 COMMAND dr1801_test) qdmr-0.12.3/test/chirptest.cc000066400000000000000000000302731501654372000160430ustar00rootroot00000000000000#include "chirptest.hh" #include #include "chirpformat.hh" #include "config.hh" ChirpTest::ChirpTest(QObject *parent) : QObject{parent} { // pass... } void ChirpTest::testReaderBasic() { QFile file(":/data/chirp_simple.csv"); if (! file.open(QIODevice::ReadOnly)) { QFAIL("Cannot open CHRIP file."); } QTextStream stream(&file); Config config; ErrorStack err; if (! ChirpReader::read(stream, &config, err)) { QFAIL(QString("Cannot read codeplug file:\n%1").arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->count(), 3); QCOMPARE(config.channelList()->channel(0)->name(), "KD8BMI"); QCOMPARE(config.channelList()->channel(0)->rxFrequency().inMHz(), 147.075000); QCOMPARE(config.channelList()->channel(0)->txFrequency().inMHz(), 147.675000); QCOMPARE(config.channelList()->channel(0)->as()->txTone(), SelectiveCall(103.5)); QCOMPARE(config.channelList()->channel(0)->as()->rxTone(), SelectiveCall()); QCOMPARE(config.channelList()->channel(1)->rxFrequency().inMHz(), 146.760000); QCOMPARE(config.channelList()->channel(1)->txFrequency().inMHz(), 146.160000); } void ChirpTest::testReaderCTCSS() { QFile file(":/data/chirp_ctcss.csv"); if (! file.open(QIODevice::ReadOnly)) { QFAIL("Cannot open CHRIP file."); } QTextStream stream(&file); Config config; ErrorStack err; if (! ChirpReader::read(stream, &config, err)) { QFAIL(QString("Cannot read codeplug file:\n%1").arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->count(), 3); QCOMPARE(config.channelList()->channel(0)->as()->txTone().format(), SelectiveCall().format()); QCOMPARE(config.channelList()->channel(0)->as()->rxTone(), SelectiveCall()); QCOMPARE(config.channelList()->channel(1)->as()->txTone(), SelectiveCall(67.0)); QCOMPARE(config.channelList()->channel(1)->as()->rxTone(), SelectiveCall()); QCOMPARE(config.channelList()->channel(2)->as()->txTone(), SelectiveCall(77.0)); QCOMPARE(config.channelList()->channel(2)->as()->rxTone(), SelectiveCall(77.0)); } void ChirpTest::testReaderDCS() { QFile file(":/data/chirp_dcs.csv"); if (! file.open(QIODevice::ReadOnly)) { QFAIL("Cannot open CHRIP file."); } QTextStream stream(&file); Config config; ErrorStack err; if (! ChirpReader::read(stream, &config, err)) { QFAIL(QString("Cannot read codeplug file:\n%1").arg(err.format()).toStdString().c_str()); } SelectiveCall cmp(23, false); QCOMPARE(config.channelList()->channel(0)->as()->txTone().format(), cmp.format()); QCOMPARE(config.channelList()->channel(0)->as()->rxTone(), SelectiveCall(23, false)); QCOMPARE(config.channelList()->channel(1)->as()->txTone(), SelectiveCall(23, false)); QCOMPARE(config.channelList()->channel(1)->as()->rxTone(), SelectiveCall(23, true)); QCOMPARE(config.channelList()->channel(2)->as()->txTone(), SelectiveCall(23, true)); QCOMPARE(config.channelList()->channel(2)->as()->rxTone(), SelectiveCall(23, false)); QCOMPARE(config.channelList()->channel(3)->as()->txTone(), SelectiveCall(23, true)); QCOMPARE(config.channelList()->channel(3)->as()->rxTone(), SelectiveCall(23, true)); } void ChirpTest::testReaderCross() { QFile file(":/data/chirp_cross.csv"); if (! file.open(QIODevice::ReadOnly)) { QFAIL("Cannot open CHRIP file."); } QTextStream stream(&file); Config config; ErrorStack err; if (! ChirpReader::read(stream, &config, err)) { QFAIL(QString("Cannot read codeplug file:\n%1").arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->count(), 8); QCOMPARE(config.channelList()->channel(0)->as()->txTone(), SelectiveCall()); QCOMPARE(config.channelList()->channel(0)->as()->rxTone(), SelectiveCall(67.0)); QCOMPARE(config.channelList()->channel(1)->as()->txTone(), SelectiveCall()); QCOMPARE(config.channelList()->channel(1)->as()->rxTone().format(), SelectiveCall(23, false).format()); QCOMPARE(config.channelList()->channel(2)->as()->txTone(), SelectiveCall(67.0)); QCOMPARE(config.channelList()->channel(2)->as()->rxTone(), SelectiveCall()); QCOMPARE(config.channelList()->channel(3)->as()->txTone(), SelectiveCall(67.0)); QCOMPARE(config.channelList()->channel(3)->as()->rxTone(), SelectiveCall(77.0)); QCOMPARE(config.channelList()->channel(4)->as()->txTone(), SelectiveCall(67.0)); QCOMPARE(config.channelList()->channel(4)->as()->rxTone(), SelectiveCall(23, false)); QCOMPARE(config.channelList()->channel(5)->as()->txTone(), SelectiveCall(23, false)); QCOMPARE(config.channelList()->channel(5)->as()->rxTone(), SelectiveCall()); QCOMPARE(config.channelList()->channel(6)->as()->txTone(), SelectiveCall(23, false)); QCOMPARE(config.channelList()->channel(6)->as()->rxTone(), SelectiveCall(67.0)); QCOMPARE(config.channelList()->channel(7)->as()->txTone(), SelectiveCall(23, false)); QCOMPARE(config.channelList()->channel(7)->as()->rxTone(), SelectiveCall(32, false)); } void ChirpTest::testReaderBandwidth() { QFile file(":/data/chirp_bandwidth.csv"); if (! file.open(QIODevice::ReadOnly)) { QFAIL("Cannot open CHRIP file."); } QTextStream stream(&file); Config config; ErrorStack err; if (! ChirpReader::read(stream, &config, err)) { QFAIL(QString("Cannot read codeplug file:\n%1").arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->count(), 2); QVERIFY(config.channelList()->channel(0)->is()); QCOMPARE(config.channelList()->channel(0)->as()->bandwidth(), FMChannel::Bandwidth::Wide); QCOMPARE(config.channelList()->channel(1)->as()->bandwidth(), FMChannel::Bandwidth::Narrow); } void ChirpTest::testWriterBasic() { Config orig; FMChannel *fm0 = new FMChannel(); fm0->setName("DB0SP"); fm0->setRXFrequency(Frequency::fromMHz(145.6)); fm0->setTXFrequency(Frequency::fromMHz(145.0)); orig.channelList()->add(fm0); FMChannel *fm1 = new FMChannel(); fm1->setName("DB0SP"); fm1->setRXFrequency(Frequency::fromMHz(145.6)); fm1->setTXFrequency(Frequency::fromMHz(144.4)); orig.channelList()->add(fm1); QString csv; QTextStream stream(&csv); ErrorStack err; if (! ChirpWriter::write(stream, &orig, err)) QFAIL(QString("Cannot serialize codeplug:\n%1").arg(err.format()).toStdString().c_str()); Config parsed; if (! ChirpReader::read(stream, &parsed, err)) QFAIL(QString("Cannot parse CHIRP CSV:\n%1").arg(err.format()).toStdString().c_str()); QCOMPARE(parsed.channelList()->count(), orig.channelList()->count()); QCOMPARE(parsed.channelList()->channel(0)->name(), orig.channelList()->channel(0)->name()); QCOMPARE(parsed.channelList()->channel(0)->rxFrequency(), orig.channelList()->channel(0)->rxFrequency()); QCOMPARE(parsed.channelList()->channel(0)->txFrequency(), orig.channelList()->channel(0)->txFrequency()); QCOMPARE(parsed.channelList()->channel(1)->rxFrequency(), orig.channelList()->channel(1)->rxFrequency()); QCOMPARE(parsed.channelList()->channel(1)->txFrequency(), orig.channelList()->channel(1)->txFrequency()); } void ChirpTest::testWriterCTCSS() { Config orig; FMChannel *fm0 = new FMChannel(); fm0->setName("DB0SP"); fm0->setRXFrequency(Frequency::fromMHz(145.6)); fm0->setTXFrequency(Frequency::fromMHz(145.0)); fm0->setTXTone(SelectiveCall(67.0)); orig.channelList()->add(fm0); FMChannel *fm1 = new FMChannel(); fm1->setName("DB0SP"); fm1->setRXFrequency(Frequency::fromMHz(145.6)); fm1->setTXFrequency(Frequency::fromMHz(145.0)); fm1->setTXTone(SelectiveCall(67.0)); fm1->setRXTone(SelectiveCall(67.0)); orig.channelList()->add(fm1); FMChannel *fm2 = new FMChannel(); fm2->setName("DB0SP"); fm2->setRXFrequency(Frequency::fromMHz(145.6)); fm2->setTXFrequency(Frequency::fromMHz(145.0)); fm2->setTXTone(SelectiveCall(67.0)); fm2->setRXTone(SelectiveCall(77.0)); orig.channelList()->add(fm2); QString csv; QTextStream stream(&csv); ErrorStack err; if (! ChirpWriter::write(stream, &orig, err)) QFAIL(QString("Cannot serialize codeplug:\n%1").arg(err.format()).toStdString().c_str()); Config parsed; if (! ChirpReader::read(stream, &parsed, err)) QFAIL(QString("Cannot parse CHIRP CSV:\n%1").arg(err.format()).toStdString().c_str()); FMChannel *pfm1 = parsed.channelList()->channel(0)->as(); QCOMPARE(pfm1->txTone().format(), fm0->txTone().format()); FMChannel *pfm2 = parsed.channelList()->channel(1)->as(); QCOMPARE(pfm2->txTone(), fm1->txTone()); QCOMPARE(pfm2->rxTone(), fm1->rxTone()); FMChannel *pfm3 = parsed.channelList()->channel(2)->as(); QCOMPARE(pfm3->txTone(), fm2->txTone()); QCOMPARE(pfm3->rxTone(), fm2->rxTone()); } void ChirpTest::testWriterDCS() { Config orig; FMChannel *fm0 = new FMChannel(); fm0->setName("DB0SP"); fm0->setRXFrequency(Frequency::fromMHz(145.6)); fm0->setTXFrequency(Frequency::fromMHz(145.0)); fm0->setTXTone(SelectiveCall(23, false)); fm0->setRXTone(SelectiveCall(23, false)); orig.channelList()->add(fm0); FMChannel *fm1 = new FMChannel(); fm1->setName("DB0SP"); fm1->setRXFrequency(Frequency::fromMHz(145.6)); fm1->setTXFrequency(Frequency::fromMHz(145.0)); fm1->setTXTone(SelectiveCall(23, false)); fm1->setRXTone(SelectiveCall(23, true)); orig.channelList()->add(fm1); FMChannel *fm2 = new FMChannel(); fm2->setName("DB0SP"); fm2->setRXFrequency(Frequency::fromMHz(145.6)); fm2->setTXFrequency(Frequency::fromMHz(145.0)); fm2->setTXTone(SelectiveCall(23, false)); fm2->setRXTone(SelectiveCall(77.0)); orig.channelList()->add(fm2); FMChannel *fm3 = new FMChannel(); fm3->setName("DB0SP"); fm3->setRXFrequency(Frequency::fromMHz(145.6)); fm3->setTXFrequency(Frequency::fromMHz(145.0)); fm3->setTXTone(SelectiveCall(23, false)); fm3->setRXTone(SelectiveCall(32, false)); orig.channelList()->add(fm3); QString csv; QTextStream stream(&csv); ErrorStack err; if (! ChirpWriter::write(stream, &orig, err)) QFAIL(QString("Cannot serialize codeplug:\n%1").arg(err.format()).toStdString().c_str()); qDebug() << csv; Config parsed; if (! ChirpReader::read(stream, &parsed, err)) QFAIL(QString("Cannot parse CHIRP CSV:\n%1").arg(err.format()).toStdString().c_str()); FMChannel *pfm0 = parsed.channelList()->channel(0)->as(); QCOMPARE(pfm0->txTone(), fm0->txTone()); QCOMPARE(pfm0->rxTone(), fm0->rxTone()); FMChannel *pfm1 = parsed.channelList()->channel(1)->as(); QCOMPARE(pfm1->txTone(), fm1->txTone()); QCOMPARE(pfm1->rxTone(), fm1->rxTone()); FMChannel *pfm2 = parsed.channelList()->channel(2)->as(); QCOMPARE(pfm2->txTone(), fm2->txTone()); QCOMPARE(pfm2->rxTone(), fm2->rxTone()); FMChannel *pfm3 = parsed.channelList()->channel(3)->as(); QCOMPARE(pfm3->txTone(), fm3->txTone()); QCOMPARE(pfm3->rxTone(), fm3->rxTone()); } void ChirpTest::testWriterCross() { } void ChirpTest::testWriterBandwidth() { Config orig; FMChannel *fm0 = new FMChannel(); fm0->setName("DB0SP"); fm0->setRXFrequency(Frequency::fromMHz(145.6)); fm0->setTXFrequency(Frequency::fromMHz(145.0)); fm0->setBandwidth(FMChannel::Bandwidth::Wide); orig.channelList()->add(fm0); FMChannel *fm1 = new FMChannel(); fm1->setName("DB0SP"); fm1->setRXFrequency(Frequency::fromMHz(145.6)); fm1->setTXFrequency(Frequency::fromMHz(145.0)); fm1->setBandwidth(FMChannel::Bandwidth::Narrow); orig.channelList()->add(fm1); QString csv; QTextStream stream(&csv); ErrorStack err; if (! ChirpWriter::write(stream, &orig, err)) QFAIL(QString("Cannot serialize codeplug:\n%1").arg(err.format()).toStdString().c_str()); qDebug() << csv; Config parsed; if (! ChirpReader::read(stream, &parsed, err)) QFAIL(QString("Cannot parse CHIRP CSV:\n%1").arg(err.format()).toStdString().c_str()); FMChannel *pfm0 = parsed.channelList()->channel(0)->as(); QCOMPARE(pfm0->bandwidth(), fm0->bandwidth()); FMChannel *pfm1 = parsed.channelList()->channel(1)->as(); QCOMPARE(pfm1->bandwidth(), fm1->bandwidth()); } QTEST_GUILESS_MAIN(ChirpTest) qdmr-0.12.3/test/chirptest.hh000066400000000000000000000007331501654372000160530ustar00rootroot00000000000000#ifndef CHIRPTEST_HH #define CHIRPTEST_HH #include class ChirpTest : public QObject { Q_OBJECT public: explicit ChirpTest(QObject *parent = nullptr); private slots: void testReaderBasic(); void testReaderCTCSS(); void testReaderDCS(); void testReaderCross(); void testReaderBandwidth(); void testWriterBasic(); void testWriterCTCSS(); void testWriterDCS(); void testWriterCross(); void testWriterBandwidth(); }; #endif // CHIRPTEST_HH qdmr-0.12.3/test/configtest.cc000066400000000000000000000137471501654372000162120ustar00rootroot00000000000000#include "configtest.hh" #include "config.hh" #include "errorstack.hh" #include "melody.hh" #include #include #include "logger.hh" #include #include "configcopyvisitor.hh" ConfigTest::ConfigTest(QObject *parent) : UnitTestBase(parent), _stderr(stderr) { Logger::get().addHandler(new StreamLogHandler(_stderr, LogMessage::DEBUG)); } void ConfigTest::initTestCase() { UnitTestBase::initTestCase(); ErrorStack err; if (! _ctcssCopyTest.readYAML(":/data/ctcss_copy_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } } void ConfigTest::testImmediateRefInvalidation() { ErrorStack err; Config *copy = ConfigCopy::copy(&_basicConfig, err)->as(); if (nullptr == copy) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(_basicConfig.posSystems()->count(), 1); QVERIFY(_basicConfig.channelList()->get(1)); QVERIFY(_basicConfig.channelList()->get(1)->is()); QCOMPARE(_basicConfig.channelList()->get(1)->as()->aprsObj(), _basicConfig.posSystems()->get(0)->as()); // Delete DMR APRS system and check if reference to it (channel 2) is removed as well. QVERIFY(copy->posSystems()->del(copy->posSystems()->get(0))); QCOMPARE(copy->posSystems()->count(), 0); QCOMPARE(copy->channelList()->get(1)->as()->aprsObj(), nullptr); QVERIFY(_basicConfig.channelList()->get(2)); QCOMPARE(_basicConfig.zones()->get(0)->as()->B()->count(), 1); QCOMPARE(_basicConfig.zones()->get(0)->as()->B()->get(0), _basicConfig.channelList()->get(2)); // Delete channel 3, check if zone 1, list B is empty copy->channelList()->del(copy->channelList()->get(2)); QCOMPARE(copy->zones()->get(0)->as()->B()->count(), 0); } void ConfigTest::testCloneChannelBasic() { // Check if a channel can be cloned Channel *clone = _basicConfig.channelList()->channel(0)->clone()->as(); // Check if channels are the same QCOMPARE(clone->compare(*_basicConfig.channelList()->channel(0)), 0); } void ConfigTest::testCloneChannelCTCSS() { // Check if a channel can be cloned QHash table; ConfigCloneVisitor visitor(table); ErrorStack err; if (! visitor.processItem(_ctcssCopyTest.channelList()->channel(0))) QFAIL(err.format().toLocal8Bit().constData()); Channel *clone = visitor.takeResult(err)->as(); if (nullptr == clone) QFAIL(err.format().toLocal8Bit().constData()); // Check if channels are the same QCOMPARE(clone->compare(*_ctcssCopyTest.channelList()->channel(0)), 0); QCOMPARE(clone->as()->txTone(), _ctcssCopyTest.channelList()->channel(0)->as()->txTone()); QCOMPARE(clone->as()->rxTone(), _ctcssCopyTest.channelList()->channel(0)->as()->rxTone()); } void ConfigTest::testMultipleRadioIDs() { ErrorStack err; Config multipleRadioIds; if (! multipleRadioIds.readYAML(":/data/anytone_audio_settings_extension.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } Config::Context ctx; if (! multipleRadioIds.label(ctx, err)) { QFAIL(QString("Cannot label codeplug: %1") .arg(err.format()).toStdString().c_str()); } YAML::Node node = multipleRadioIds.serialize(ctx, err); if (node.IsNull()) { QFAIL(QString("Cannot serialize codeplug: %1") .arg(err.format()).toStdString().c_str()); } } void ConfigTest::testMelodyLilypond() { QString lilypond = "a8 b e2 cis4 d"; Melody melody; melody.fromLilypond(lilypond); QString serialized = melody.toLilypond(); QCOMPARE(serialized, lilypond); QVector> tones = melody.toTones(); QCOMPARE(std::round(tones[0].first*100), 44000); QCOMPARE(tones[0].second, 300); QCOMPARE(std::round(tones[1].first*100), 49388); QCOMPARE(tones[1].second, 300); QCOMPARE(std::round(tones[2].first*100), 32963); QCOMPARE(tones[2].second,1200); QCOMPARE(std::round(tones[3].first*100), 27718); QCOMPARE(tones[3].second, 600); QCOMPARE(std::round(tones[4].first*100), 29366); QCOMPARE(tones[4].second, 600); } void ConfigTest::testMelodyEncoding() { QString lilypond = "a8 b e2 cis4 d"; Melody melody; melody.fromLilypond(lilypond); QVector> tones = melody.toTones(); QCOMPARE(std::round(tones[0].first*100), 44000); QCOMPARE(tones[0].second, 300); QCOMPARE(std::round(tones[1].first*100), 49388); QCOMPARE(tones[1].second, 300); QCOMPARE(std::round(tones[2].first*100), 32963); QCOMPARE(tones[2].second,1200); QCOMPARE(std::round(tones[3].first*100), 27718); QCOMPARE(tones[3].second, 600); QCOMPARE(std::round(tones[4].first*100), 29366); QCOMPARE(tones[4].second, 600); } void ConfigTest::testMelodyDecoding() { QString lilypond = "a8 b e2 cis4 d"; QVector> tones = { {440.00, 300}, {493.88, 300}, {329.63, 1200}, {277.18, 600}, {293.66, 600} }; Melody melody; melody.infer(tones); QCOMPARE(melody.toLilypond(), lilypond); // infer() should also infer a BPM of 100 QCOMPARE(melody.bpm(), 100); } void ConfigTest::testCTCSSNull() { ErrorStack err; Config ctcssConfig; if (! ctcssConfig.readYAML(":/data/ctcss_null_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } // Test backward compatability QVERIFY(ctcssConfig.channelList()->channel(0)->is()); QCOMPARE(SelectiveCall() ,ctcssConfig.channelList()->channel(0)->as()->rxTone()); QCOMPARE(SelectiveCall(67.0) ,ctcssConfig.channelList()->channel(0)->as()->txTone()); // Test new format QVERIFY(ctcssConfig.channelList()->channel(1)->is()); QCOMPARE(SelectiveCall(37, true), ctcssConfig.channelList()->channel(1)->as()->rxTone()); QCOMPARE(SelectiveCall(67.0) ,ctcssConfig.channelList()->channel(1)->as()->txTone()); } QTEST_GUILESS_MAIN(ConfigTest) qdmr-0.12.3/test/configtest.hh000066400000000000000000000012531501654372000162110ustar00rootroot00000000000000#ifndef CONFIGTEST_HH #define CONFIGTEST_HH #include #include "libdmrconfigtest.hh" #include "config.hh" class ConfigTest : public UnitTestBase { Q_OBJECT public: explicit ConfigTest(QObject *parent = nullptr); private slots: void initTestCase(); void testImmediateRefInvalidation(); void testCloneChannelBasic(); void testCloneChannelCTCSS(); /** Regression test for issue #388. */ void testMultipleRadioIDs(); void testMelodyLilypond(); void testMelodyEncoding(); void testMelodyDecoding(); /** Regression test for #509. */ void testCTCSSNull(); protected: QTextStream _stderr; Config _ctcssCopyTest; }; #endif // CONFIGTEST_HH qdmr-0.12.3/test/copytest.cc000066400000000000000000000077311501654372000157130ustar00rootroot00000000000000#include "copytest.hh" #include "configcopyvisitor.hh" CopyTest::CopyTest(QObject *parent) : UnitTestBase(parent) { // pass... } void CopyTest::testChannelClone() { QHash map; ErrorStack err; ConfigCloneVisitor cloner(map); Config config; if (! config.readYAML(":/data/config_test.yaml")) QFAIL("Cannot load config."); auto ch = config.channelList()->channel(0); { if (! cloner.processItem(ch, err)) QFAIL(err.format().toLocal8Bit().constData()); ConfigItem *item = cloner.takeResult(err); QVERIFY(item); QCOMPARE(ch->compare(*item), 0); delete item; } { ch->setPower(Channel::Power::Min); ch->setTimeout(60); ch->setVOX(3); if (! cloner.processItem(ch, err)) QFAIL(err.format().toLocal8Bit().constData()); ConfigItem *item = cloner.takeResult(err); QVERIFY(item); QVERIFY(item->is()); QCOMPARE(item->as()->power(), Channel::Power::Min); QCOMPARE(item->as()->timeout(), 60); QCOMPARE(item->as()->vox(), 3); delete item; } { ch->setDefaultPower(); ch->setDefaultTimeout(); ch->setVOXDefault(); if (! cloner.processItem(ch, err)) QFAIL(err.format().toLocal8Bit().constData()); ConfigItem *item = cloner.takeResult(err); QVERIFY(item); QVERIFY(item->is()); QVERIFY(item->as()->defaultPower()); QVERIFY(item->as()->defaultTimeout()); QVERIFY(item->as()->defaultVOX()); delete item; } } void CopyTest::testConfigClone() { QHash map; ConfigCloneVisitor cloner(map); ErrorStack err; if (! cloner.process(&_basicConfig, err)) { QFAIL(err.format().toLocal8Bit().constData()); } ConfigItem *item = cloner.takeResult(err); QVERIFY(item); QCOMPARE(_basicConfig.compare(*item), 0); } void CopyTest::testConfigCopy() { QHash map; ConfigCloneVisitor cloner(map); FixReferencesVisistor fixer(map); ErrorStack err; if (! cloner.process(&_basicConfig, err)) { QFAIL(err.format().toLocal8Bit().constData()); } ConfigItem *item = cloner.takeResult(err); QVERIFY(item); QVERIFY(item->is()); if (! fixer.process(item->as(), err)) { QFAIL(err.format().toLocal8Bit().constData()); } QCOMPARE(_basicConfig.compare(*item), 0); } void CopyTest::testAPRSSystemCopy() { // Regression test for issue #468. QHash map; ConfigCloneVisitor cloner(map); FixReferencesVisistor fixer(map); Config config; ErrorStack err; if (! config.readYAML(":/data/fm_aprs_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1\n") .arg(err.format()).toStdString().c_str()); } if (! cloner.process(&config, err)) { QFAIL(err.format().toLocal8Bit().constData()); } ConfigItem *item = cloner.takeResult(err); QVERIFY(item); QVERIFY(item->is()); if (! fixer.process(item->as(), err)) { QFAIL(err.format().toLocal8Bit().constData()); } Config *comp_config = item->as(); QCOMPARE(config.compare(*item), 0); QCOMPARE(config.posSystems()->count(), 1); QCOMPARE(comp_config->posSystems()->count(), config.posSystems()->count()); QVERIFY(config.posSystems()->get(0)->is()); QVERIFY(comp_config->posSystems()->get(0)->is()); APRSSystem *aprs = config.posSystems()->get(0)->as(), *comp_aprs = comp_config->posSystems()->get(0)->as(); QCOMPARE(comp_aprs->name(), aprs->name()); QCOMPARE(comp_aprs->period(), aprs->period()); QCOMPARE(comp_aprs->destination(), aprs->destination()); QCOMPARE(comp_aprs->destSSID(), aprs->destSSID()); QCOMPARE(comp_aprs->source(), aprs->source()); QCOMPARE(comp_aprs->srcSSID(), aprs->srcSSID()); QCOMPARE(comp_aprs->path(), aprs->path()); QCOMPARE(comp_aprs->icon(), aprs->icon()); QCOMPARE(comp_aprs->message(), aprs->message()); } QTEST_GUILESS_MAIN(CopyTest) qdmr-0.12.3/test/copytest.hh000066400000000000000000000005061501654372000157160ustar00rootroot00000000000000#ifndef COPYTEST_HH #define COPYTEST_HH #include "libdmrconfigtest.hh" class CopyTest: public UnitTestBase { Q_OBJECT public: explicit CopyTest(QObject *parent=nullptr); private slots: void testChannelClone(); void testConfigClone(); void testConfigCopy(); void testAPRSSystemCopy(); }; #endif // COPYTEST_HH qdmr-0.12.3/test/crc32test.cc000066400000000000000000000005421501654372000156460ustar00rootroot00000000000000#include "crc32test.hh" #include "crc32.hh" #include CRC32Test::CRC32Test(QObject *parent) : QObject(parent) { // pass... } void CRC32Test::testCRC32() { QString txt("The quick brown fox jumps over the lazy dog"); CRC32 crc; crc.update(txt.toLocal8Bit()); QCOMPARE(crc.get(), 0x414FA339U^0xFFFFFFFF); } QTEST_GUILESS_MAIN(CRC32Test) qdmr-0.12.3/test/crc32test.hh000066400000000000000000000003431501654372000156570ustar00rootroot00000000000000#ifndef CRC32TEST_H #define CRC32TEST_H #include class CRC32Test : public QObject { Q_OBJECT public: explicit CRC32Test(QObject *parent = nullptr); private slots: void testCRC32(); }; #endif // CRC32TEST_H qdmr-0.12.3/test/d578uv_test.cc000066400000000000000000000035511501654372000161360ustar00rootroot00000000000000#include "d578uv_test.hh" #include "config.hh" #include "d578uv.hh" #include "d578uv_codeplug.hh" #include "errorstack.hh" #include #include #include "commercial_extension.hh" D578UVTest::D578UVTest(QObject *parent) : UnitTestBase(parent) { // pass... } void D578UVTest::testBasicConfigEncoding() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D578UVCodeplug codeplug; if (! codeplug.encode(&_basicConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D578UV: %1") .arg(err.format()).toStdString().c_str()); } } void D578UVTest::testBasicConfigDecoding() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D578UVCodeplug codeplug; if (! codeplug.encode(&_basicConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D578UV: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D578UV: %1") .arg(err.format()).toStdString().c_str()); } } void D578UVTest::testChannelFrequency() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D578UVCodeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_channelFrequencyConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone D578UV: {}") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone D578UV: %1") .arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromHz(123456780ULL)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromHz(999999990ULL)); } QTEST_GUILESS_MAIN(D578UVTest) qdmr-0.12.3/test/d578uv_test.hh000066400000000000000000000005141501654372000161440ustar00rootroot00000000000000#ifndef D578UVTEST_HH #define D578UVTEST_HH #include "libdmrconfigtest.hh" class D578UVTest : public UnitTestBase { Q_OBJECT public: explicit D578UVTest(QObject *parent = nullptr); private slots: void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testChannelFrequency(); }; #endif // D578UVTEST_HH qdmr-0.12.3/test/d868uve_test.cc000066400000000000000000000243251501654372000163070ustar00rootroot00000000000000#include "d868uve_test.hh" #include "config.hh" #include "d868uv.hh" #include "d868uv_codeplug.hh" #include "errorstack.hh" #include #include D868UVETest::D868UVETest(QObject *parent) : UnitTestBase(parent) { // pass... } void D868UVETest::testBasicConfigEncoding() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug = false; D868UVCodeplug codeplug; if (! codeplug.encode(&_basicConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } } void D868UVETest::testBasicConfigDecoding() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D868UVCodeplug codeplug; if (! codeplug.encode(&_basicConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D878UVII: %1") .arg(err.format()).toStdString().c_str()); } } void D868UVETest::testChannelFrequency() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D868UVCodeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_channelFrequencyConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone D868UV: {}") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone D868UV: {}") .arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromHz(123456780ULL)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromHz(999999990ULL)); } void D868UVETest::testAutoRepeaterOffset() { ErrorStack err; // Load config from file Config config; if (! config.readYAML(":/data/anytone_auto_repeater_extension.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } // Check config QVERIFY2(config.settings()->anytoneExtension(), "Expected AnyTone settings extension."); AnytoneAutoRepeaterSettingsExtension *ext = config.settings()->anytoneExtension()->autoRepeaterSettings(); // There should be two offset frequencies QCOMPARE(ext->offsets()->count(), 2); QCOMPARE(ext->offsets()->get(0)->as()->offset().inHz(), 600000); QCOMPARE(ext->offsets()->get(1)->as()->offset().inHz(), 7600000); // check if VHF and UHF frequency offsets are correct QVERIFY(! ext->vhfRef()->isNull()); QVERIFY(! ext->uhfRef()->isNull()); QCOMPARE(ext->vhfRef()->as(), ext->offsets()->get(0)->as()); QCOMPARE(ext->uhfRef()->as(), ext->offsets()->get(1)->as()); // Encode D868UVCodeplug codeplug; Codeplug::Flags flags; flags.updateCodePlug=false; if (! codeplug.encode(&config, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } // Decode Config comp_config; if (! codeplug.decode(&comp_config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } // Compare QVERIFY2(comp_config.settings()->anytoneExtension(), "Expected AnyTone settings extension."); ext = comp_config.settings()->anytoneExtension()->autoRepeaterSettings(); // There should be two offset frequencies QCOMPARE(ext->offsets()->count(), 2); QCOMPARE(ext->offsets()->get(0)->as()->offset().inHz(), 600000); QCOMPARE(ext->offsets()->get(1)->as()->offset().inHz(), 7600000); // check if VHF and UHF frequency offsets are correct QVERIFY(! ext->vhfRef()->isNull()); QVERIFY(! ext->uhfRef()->isNull()); QCOMPARE(ext->vhfRef()->as(), ext->offsets()->get(0)->as()); QCOMPARE(ext->uhfRef()->as(), ext->offsets()->get(1)->as()); } void D868UVETest::testDTMFContacts() { // Assemble config Config base; DTMFContact *contact0 = new DTMFContact(); contact0->setName("Contact 0"); contact0->setNumber("0123456789ABCD#*"); base.contacts()->add(contact0); // Encode D868UVCodeplug codeplug; ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; if (! codeplug.encode(&base, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } // Decode Config comp_config; if (! codeplug.decode(&comp_config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } // Check contacts QCOMPARE(comp_config.contacts()->count(), 1); QVERIFY(comp_config.contacts()->get(0)->is()); QCOMPARE(comp_config.contacts()->get(0)->as()->name(), "Contact 0"); // Contacts are limited to 14 digit numbers. QCOMPARE(comp_config.contacts()->get(0)->as()->number(), "0123456789ABCD"); } void D868UVETest::testSMSTemplates() { Config config; config.radioIDs()->add(new DMRRadioID("ID", 1234567)); SMSTemplate *sms0 = new SMSTemplate(); sms0->setName("SMS0"); sms0->setMessage("ABC"); SMSTemplate *sms1 = new SMSTemplate(); sms1->setName("SMS1"); sms1->setMessage("XYZ"); config.smsExtension()->smsTemplates()->add(sms0); config.smsExtension()->smsTemplates()->add(sms1); ErrorStack err; D868UVCodeplug codeplug; Codeplug::Flags flags; flags.updateCodePlug=false; if (! codeplug.encode(&config, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } Config decoded; if (! codeplug.decode(&decoded, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } // For now, messages are not encoded QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 2); //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(0)->name(), "SMS0"); QCOMPARE(decoded.smsExtension()->smsTemplates()->message(0)->message(), "ABC"); //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(1)->name(), "SMS1"); QCOMPARE(decoded.smsExtension()->smsTemplates()->message(1)->message(), "XYZ"); } void D868UVETest::testRegressionSMSTemplateOffset() { // Regression test for issue #469, message index offset error if more than one bank is used // (i.e., more than 8 SMS). Config config; config.radioIDs()->add(new DMRRadioID("ID", 1234567)); for (int i=0; i<9; i++) { SMSTemplate *sms = new SMSTemplate(); sms->setName(QString("SMS%1").arg(i)); sms->setMessage("ABC"); config.smsExtension()->smsTemplates()->add(sms); } ErrorStack err; D868UVCodeplug codeplug; Codeplug::Flags flags; flags.updateCodePlug=false; if (! codeplug.encode(&config, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } Config decoded; if (! codeplug.decode(&decoded, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } // For now, messages are not encoded QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 9); } void D868UVETest::testRegressionSMSCount() { // Regression test for issue #482 (tries to encode too many SMS) Config config; config.radioIDs()->add(new DMRRadioID("ID", 1234567)); for (int i=0; i<101; i++) { SMSTemplate *sms = new SMSTemplate(); sms->setName(QString("SMS%1").arg(i)); sms->setMessage("ABC"); config.smsExtension()->smsTemplates()->add(sms); } ErrorStack err; D868UVCodeplug codeplug; Codeplug::Flags flags; flags.updateCodePlug=false; if (! codeplug.encode(&config, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } } void D868UVETest::testRegressionDefaultChannel() { ErrorStack err; // Load config from file Config config; if (! config.readYAML(":/data/config_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } config.settings()->setAnytoneExtension(new AnytoneSettingsExtension()); config.settings()->anytoneExtension()->bootSettings()->zoneA()->set(config.zones()->zone(0)); config.settings()->anytoneExtension()->bootSettings()->channelA()->set(config.zones()->zone(0)->A()->get(0)); config.settings()->anytoneExtension()->bootSettings()->zoneB()->set(config.zones()->zone(1)); config.settings()->anytoneExtension()->bootSettings()->channelB()->set(config.zones()->zone(1)->A()->get(0)); config.settings()->anytoneExtension()->bootSettings()->enableDefaultChannel(true); D868UVCodeplug codeplug; Codeplug::Flags flags; flags.updateCodePlug=false; if (! codeplug.encode(&config, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } Config decoded; if (! codeplug.decode(&decoded, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } QVERIFY(decoded.settings()->anytoneExtension()); QVERIFY(decoded.settings()->anytoneExtension()->bootSettings()->defaultChannelEnabled()); QCOMPARE(decoded.settings()->anytoneExtension()->bootSettings()->zoneA()->as(), decoded.zones()->zone(0)); QCOMPARE(decoded.settings()->anytoneExtension()->bootSettings()->channelA()->as()->name(), decoded.zones()->zone(0)->A()->get(0)->name()); QCOMPARE(decoded.settings()->anytoneExtension()->bootSettings()->zoneB()->as(), decoded.zones()->zone(1)); QCOMPARE(decoded.settings()->anytoneExtension()->bootSettings()->channelB()->as()->name(), decoded.zones()->zone(1)->A()->get(0)->name()); } QTEST_GUILESS_MAIN(D868UVETest) qdmr-0.12.3/test/d868uve_test.hh000066400000000000000000000010331501654372000163100ustar00rootroot00000000000000#ifndef D868UVETEST_HH #define D868UVETEST_HH #include "libdmrconfigtest.hh" class D868UVETest : public UnitTestBase { Q_OBJECT public: explicit D868UVETest(QObject *parent = nullptr); private slots: void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testChannelFrequency(); void testAutoRepeaterOffset(); void testDTMFContacts(); void testSMSTemplates(); void testRegressionSMSTemplateOffset(); void testRegressionSMSCount(); void testRegressionDefaultChannel(); }; #endif // D878UV2TEST_HH qdmr-0.12.3/test/d878uv2_test.cc000066400000000000000000000112121501654372000162140ustar00rootroot00000000000000#include "d878uv2_test.hh" #include "config.hh" #include "d878uv2.hh" #include "d878uv2_codeplug.hh" #include "errorstack.hh" #include #include D878UV2Test::D878UV2Test(QObject *parent) : UnitTestBase(parent) { // pass... } void D878UV2Test::testBasicConfigEncoding() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D878UV2Codeplug codeplug; if (! codeplug.encode(&_basicConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D878UVII: %1") .arg(err.format()).toStdString().c_str()); } } void D878UV2Test::testBasicConfigDecoding() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D878UV2Codeplug codeplug; if (! codeplug.encode(&_basicConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D878UVII: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D878UVII: %1") .arg(err.format()).toStdString().c_str()); } } void D878UV2Test::testChannelFrequency() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D868UVCodeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_channelFrequencyConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone D878UV II: {}") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone D878UV II: {}") .arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromHz(123456780ULL)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromHz(999999990ULL)); } void D878UV2Test::testKeyFunctions() { ErrorStack err; // Load config from file Config config; if (! config.readYAML(":/data/anytone_key_function.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } // Check config QVERIFY2(config.settings()->anytoneExtension(), "Expected AnyTone settings extension."); AnytoneKeySettingsExtension *ext = config.settings()->anytoneExtension()->keySettings(); QCOMPARE(ext->funcKey1Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel); QCOMPARE(ext->funcKey1Long(), AnytoneKeySettingsExtension::KeyFunction::SubChannel); QCOMPARE(ext->funcKey2Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleVFO); QCOMPARE(ext->funcKey2Long(), AnytoneKeySettingsExtension::KeyFunction::ChannelType); QCOMPARE(ext->funcKeyAShort(), AnytoneKeySettingsExtension::KeyFunction::Off); QCOMPARE(ext->funcKeyALong(), AnytoneKeySettingsExtension::KeyFunction::Encryption); QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage); QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call); QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage); QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call); // Encode D878UV2Codeplug codeplug; Codeplug::Flags flags; flags.updateCodePlug=false; if (! codeplug.encode(&config, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D868UVE: %1") .arg(err.format()).toStdString().c_str()); } // Decode Config comp_config; if (! codeplug.decode(&comp_config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D878UVII: %1") .arg(err.format()).toStdString().c_str()); } // Check config QVERIFY2(comp_config.settings()->anytoneExtension(), "Expected AnyTone settings extension."); ext = comp_config.settings()->anytoneExtension()->keySettings(); QCOMPARE(ext->funcKey1Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel); QCOMPARE(ext->funcKey1Long(), AnytoneKeySettingsExtension::KeyFunction::SubChannel); QCOMPARE(ext->funcKey2Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleVFO); QCOMPARE(ext->funcKey2Long(), AnytoneKeySettingsExtension::KeyFunction::ChannelType); QCOMPARE(ext->funcKeyAShort(), AnytoneKeySettingsExtension::KeyFunction::Off); QCOMPARE(ext->funcKeyALong(), AnytoneKeySettingsExtension::KeyFunction::Encryption); QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage); QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call); QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage); QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call); } QTEST_GUILESS_MAIN(D878UV2Test) qdmr-0.12.3/test/d878uv2_test.hh000066400000000000000000000005541501654372000162350ustar00rootroot00000000000000#ifndef D878UV2TEST_HH #define D878UV2TEST_HH #include "libdmrconfigtest.hh" class D878UV2Test : public UnitTestBase { Q_OBJECT public: explicit D878UV2Test(QObject *parent = nullptr); private slots: void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testChannelFrequency(); void testKeyFunctions(); }; #endif // D878UV2TEST_HH qdmr-0.12.3/test/d878uv_test.cc000066400000000000000000000333121501654372000161370ustar00rootroot00000000000000#include "d878uv_test.hh" #include "config.hh" #include "d878uv.hh" #include "d878uv_codeplug.hh" #include "errorstack.hh" #include #include #include "logger.hh" D878UVTest::D878UVTest(QObject *parent) : UnitTestBase(parent), _stderr(stderr) { Logger::get().addHandler(new StreamLogHandler(_stderr, LogMessage::DEBUG)); } void D878UVTest::initTestCase() { UnitTestBase::initTestCase(); ErrorStack err; if (! _micGainConfig.readYAML(":/data/anytone_audio_settings_extension.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } } void D878UVTest::testBasicConfigEncoding() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D878UVCodeplug codeplug; if (! codeplug.encode(&_basicConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D878UV: %1") .arg(err.format()).toStdString().c_str()); } } void D878UVTest::testBasicConfigDecoding() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D878UVCodeplug codeplug; if (! codeplug.encode(&_basicConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D878UV: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D878UV: %1") .arg(err.format()).toStdString().c_str()); } } void D878UVTest::testAnalogMicGain() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D878UVCodeplug codeplug; if (! codeplug.encode(&_micGainConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D878UV: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D878UV: %1") .arg(err.format()).toStdString().c_str()); } QVERIFY(config.settings()->anytoneExtension()->audioSettings()->fmMicGainEnabled()); QCOMPARE(config.settings()->anytoneExtension()->audioSettings()->fmMicGain(), 6); } void D878UVTest::testChannelFrequency() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D868UVCodeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_channelFrequencyConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone D878UV: {}") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone D878UV: {}") .arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromHz(123456780ULL)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromHz(999999990ULL)); } void D878UVTest::testRoaming() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D878UVCodeplug codeplug; if (! codeplug.encode(&_roamingConfig, flags, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D878UV: {}") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone D878UV: {}") .arg(err.format()).toStdString().c_str()); } QCOMPARE(config.roamingChannels()->count(), 3); QCOMPARE(config.roamingZones()->count(), 2); QCOMPARE(config.roamingZones()->count(), 2); QCOMPARE(config.roamingZones()->get(0)->as()->count(), 2); QCOMPARE(config.roamingZones()->get(0)->as()->channel(0), config.roamingChannels()->get(0)->as()); QCOMPARE(config.roamingZones()->get(0)->as()->channel(1), config.roamingChannels()->get(1)->as()); QCOMPARE(config.roamingZones()->get(1)->as()->count(), 2); QCOMPARE(config.roamingZones()->get(1)->as()->channel(0), config.roamingChannels()->get(0)->as()); QCOMPARE(config.roamingZones()->get(1)->as()->channel(1), config.roamingChannels()->get(2)->as()); } void D878UVTest::testHangTime() { ErrorStack err; // Load config from file Config config; if (! config.readYAML(":/data/anytone_call_hangtime.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } // Check config QVERIFY2(config.settings()->anytoneExtension(), "Expected AnyTone settings extension."); AnytoneDMRSettingsExtension *ext = config.settings()->anytoneExtension()->dmrSettings(); QCOMPARE(ext->privateCallHangTime().seconds(), 4ULL); QCOMPARE(ext->groupCallHangTime().seconds(), 5ULL); // Encode D878UVCodeplug codeplug; Codeplug::Flags flags; flags.updateCodePlug=false; Config *intermediate = codeplug.preprocess(&config, err); if (nullptr == intermediate) { QFAIL(QString("Cannot prepare codeplug for AnyTone AT-D878UVE: %1") .arg(err.format()).toStdString().c_str()); } if (! codeplug.encode(intermediate, flags, err)) { delete intermediate; QFAIL(QString("Cannot encode codeplug for AnyTone AT-D878UVE: %1") .arg(err.format()).toStdString().c_str()); } delete intermediate; // Decode Config comp_config; if (! codeplug.decode(&comp_config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D878UVII: %1") .arg(err.format()).toStdString().c_str()); } // Check config QVERIFY2(comp_config.settings()->anytoneExtension(), "Expected AnyTone settings extension."); ext = comp_config.settings()->anytoneExtension()->dmrSettings(); QCOMPARE(ext->privateCallHangTime().seconds(), 4ULL); QCOMPARE(ext->groupCallHangTime().seconds(), 5ULL); } void D878UVTest::testKeyFunctions() { ErrorStack err; // Load config from file Config config; if (! config.readYAML(":/data/anytone_key_function.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } // Check config QVERIFY2(config.settings()->anytoneExtension(), "Expected AnyTone settings extension."); AnytoneKeySettingsExtension *ext = config.settings()->anytoneExtension()->keySettings(); QCOMPARE(ext->funcKey1Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel); QCOMPARE(ext->funcKey1Long(), AnytoneKeySettingsExtension::KeyFunction::SubChannel); QCOMPARE(ext->funcKey2Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleVFO); QCOMPARE(ext->funcKey2Long(), AnytoneKeySettingsExtension::KeyFunction::ChannelType); QCOMPARE(ext->funcKeyAShort(), AnytoneKeySettingsExtension::KeyFunction::Off); QCOMPARE(ext->funcKeyALong(), AnytoneKeySettingsExtension::KeyFunction::Encryption); QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage); QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call); QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage); QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call); // Encode D878UVCodeplug codeplug; Codeplug::Flags flags; flags.updateCodePlug=false; if (! codeplug.encode(&config, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D868UV: %1") .arg(err.format()).toStdString().c_str()); } // Decode Config comp_config; if (! codeplug.decode(&comp_config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D878UV: %1") .arg(err.format()).toStdString().c_str()); } // Check config QVERIFY2(comp_config.settings()->anytoneExtension(), "Expected AnyTone settings extension."); ext = comp_config.settings()->anytoneExtension()->keySettings(); QCOMPARE(ext->funcKey1Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel); QCOMPARE(ext->funcKey1Long(), AnytoneKeySettingsExtension::KeyFunction::SubChannel); QCOMPARE(ext->funcKey2Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleVFO); QCOMPARE(ext->funcKey2Long(), AnytoneKeySettingsExtension::KeyFunction::ChannelType); QCOMPARE(ext->funcKeyAShort(), AnytoneKeySettingsExtension::KeyFunction::Off); QCOMPARE(ext->funcKeyALong(), AnytoneKeySettingsExtension::KeyFunction::Encryption); QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage); QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call); QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage); QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call); } void D878UVTest::testFMAPRSSettings() { ErrorStack err; // Load config from file Config config; if (! config.readYAML(":/data/fm_aprs_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1\n") .arg(err.format()).toStdString().c_str()); } // Check config QCOMPARE(config.posSystems()->count(), 1); QVERIFY(config.posSystems()->get(0)->is()); APRSSystem *aprs = config.posSystems()->get(0)->as(); QCOMPARE(aprs->source(), "DM3MAT"); QCOMPARE(aprs->srcSSID(), 7); QCOMPARE(aprs->destination(), "APAT81"); QCOMPARE(aprs->destSSID(), 0); QCOMPARE(aprs->path(), "WIDE1-1,WIDE2-1"); QCOMPARE(aprs->period(), 300); // Encode D878UVCodeplug codeplug; Codeplug::Flags flags; flags.updateCodePlug=false; Config *intermediate = codeplug.preprocess(&config, err); if (nullptr == intermediate) { QFAIL(QString("Cannot prepare codeplug for AnyTone AT-D878UV: %1") .arg(err.format()).toStdString().c_str()); } if (! codeplug.encode(intermediate, flags, err)) { delete intermediate; QFAIL(QString("Cannot encode codeplug for AnyTone AT-D878UV: %1") .arg(err.format()).toStdString().c_str()); } delete intermediate; // Decode Config comp_config; if (! codeplug.decode(&comp_config, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D878UV: %1") .arg(err.format()).toStdString().c_str()); } // Check config QCOMPARE(comp_config.posSystems()->count(), 1); QVERIFY(comp_config.posSystems()->get(0)->is()); APRSSystem *comp_aprs = comp_config.posSystems()->get(0)->as(); QCOMPARE(comp_aprs->source(), aprs->source()); QCOMPARE(comp_aprs->srcSSID(), aprs->srcSSID()); QCOMPARE(comp_aprs->destination(), aprs->destination()); QCOMPARE(comp_aprs->destSSID(), aprs->destSSID()); QCOMPARE(comp_aprs->path(), aprs->path()); QCOMPARE(comp_aprs->period(), aprs->period()); } void D878UVTest::testRegressionDefaultChannel() { ErrorStack err; // Load config from file Config config; if (! config.readYAML(":/data/config_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } config.settings()->setAnytoneExtension(new AnytoneSettingsExtension()); config.settings()->anytoneExtension()->bootSettings()->zoneA()->set(config.zones()->zone(0)); config.settings()->anytoneExtension()->bootSettings()->channelA()->set(config.zones()->zone(0)->A()->get(0)); config.settings()->anytoneExtension()->bootSettings()->zoneB()->set(config.zones()->zone(1)); config.settings()->anytoneExtension()->bootSettings()->channelB()->set(config.zones()->zone(1)->A()->get(0)); config.settings()->anytoneExtension()->bootSettings()->enableDefaultChannel(true); D868UVCodeplug codeplug; Codeplug::Flags flags; flags.updateCodePlug=false; if (! codeplug.encode(&config, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D878UV: %1") .arg(err.format()).toStdString().c_str()); } Config decoded; if (! codeplug.decode(&decoded, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D878UV: %1") .arg(err.format()).toStdString().c_str()); } QVERIFY(decoded.settings()->anytoneExtension()); QVERIFY(decoded.settings()->anytoneExtension()->bootSettings()->defaultChannelEnabled()); QCOMPARE(decoded.settings()->anytoneExtension()->bootSettings()->zoneA()->as(), decoded.zones()->zone(0)); QCOMPARE(decoded.settings()->anytoneExtension()->bootSettings()->channelA()->as()->name(), decoded.zones()->zone(0)->A()->get(0)->name()); QCOMPARE(decoded.settings()->anytoneExtension()->bootSettings()->zoneB()->as(), decoded.zones()->zone(1)); QCOMPARE(decoded.settings()->anytoneExtension()->bootSettings()->channelB()->as()->name(), decoded.zones()->zone(1)->A()->get(0)->name()); } void D878UVTest::testRegressionAutoRepeater() { ErrorStack err; // Load config from file Config config; if (! config.readYAML(":/data/config_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } config.settings()->setAnytoneExtension(new AnytoneSettingsExtension()); D868UVCodeplug codeplug; Codeplug::Flags flags; flags.updateCodePlug=false; if (! codeplug.encode(&config, flags, err)) { QFAIL(QString("Cannot encode codeplug for AnyTone AT-D878UV: %1") .arg(err.format()).toStdString().c_str()); } Config decoded; if (! codeplug.decode(&decoded, err)) { QFAIL(QString("Cannot decode codeplug for AnyTone AT-D878UV: %1") .arg(err.format()).toStdString().c_str()); } QVERIFY(decoded.settings()->anytoneExtension()); QCOMPARE(decoded.settings()->anytoneExtension()->autoRepeaterSettings()->vhf2Min().inMHz(), 136.0); QCOMPARE(decoded.settings()->anytoneExtension()->autoRepeaterSettings()->vhf2Max().inMHz(), 174.0); QCOMPARE(decoded.settings()->anytoneExtension()->autoRepeaterSettings()->uhf2Min().inMHz(), 400.0); QCOMPARE(decoded.settings()->anytoneExtension()->autoRepeaterSettings()->uhf2Max().inMHz(), 480.0); } QTEST_GUILESS_MAIN(D878UVTest) qdmr-0.12.3/test/d878uv_test.hh000066400000000000000000000011601501654372000161450ustar00rootroot00000000000000#ifndef D878UVTEST_HH #define D878UVTEST_HH #include "libdmrconfigtest.hh" class D878UVTest : public UnitTestBase { Q_OBJECT public: explicit D878UVTest(QObject *parent = nullptr); private slots: void initTestCase(); void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testChannelFrequency(); void testAnalogMicGain(); void testRoaming(); void testHangTime(); void testKeyFunctions(); void testFMAPRSSettings(); void testRegressionDefaultChannel(); void testRegressionAutoRepeater(); protected: Config _micGainConfig; QTextStream _stderr; }; #endif // D878UVTEST_HH qdmr-0.12.3/test/data/000077500000000000000000000000001501654372000144335ustar00rootroot00000000000000qdmr-0.12.3/test/data/anytone_audio_settings_extension.yaml000066400000000000000000000043651501654372000242010ustar00rootroot00000000000000--- version: 0.11.0 settings: introLine1: DM3MAT introLine2: qDMR micLevel: 3 speech: false power: High squelch: 1 vox: 0 tot: 0 defaultID: id1 anytone: audioSettings: enableFMMicGain: true fmMicGain: 6 # Must differ from settings->micLevel radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} contacts: - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9} - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8} - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621} - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262} - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999} - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826} groupLists: - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]} - {id: grp2, name: DL, contacts: [cont4]} - {id: grp3, name: HamRadioVillage, contacts: [cont6]} channels: - dmr: id: ch1 name: L9 DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont1 power: High timeout: 0 vox: 0 - dmr: id: ch2 name: BB DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont3 aprs: aprs1 power: High timeout: 0 vox: 0 - dmr: id: ch3 name: DL DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp2 contact: cont4 power: High timeout: 0 vox: 0 - dmr: id: ch4 name: HRV DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp3 contact: cont6 power: High timeout: 0 vox: 0 zones: - id: zone1 name: Zu Hause A: [ch1, ch2, ch4] B: [ch3] positioning: - dmr: id: aprs1 name: GPS System period: 300 contact: cont5 ... qdmr-0.12.3/test/data/anytone_auto_repeater_extension.yaml000066400000000000000000000045711501654372000240160ustar00rootroot00000000000000--- version: 0.9.0 settings: introLine1: DM3MAT introLine2: qDMR micLevel: 3 speech: false power: High squelch: 1 vox: 0 tot: 0 defaultID: id1 anytone: autoRepeaterSettings: directionA: Negative directionB: Negative vhf: off0 uhf: off1 offsets: - {id: off0, name: VHF Offset, offset: 600 kHz} - {id: off1, name: UHF Offset, offset: 7.6 MHz} radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} contacts: - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9} - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8} - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621} - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262} - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999} - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826} groupLists: - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]} - {id: grp2, name: DL, contacts: [cont4]} - {id: grp3, name: HamRadioVillage, contacts: [cont6]} channels: - dmr: id: ch1 name: L9 DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont1 power: High timeout: 0 vox: 0 - dmr: id: ch2 name: BB DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont3 aprs: aprs1 power: High timeout: 0 vox: 0 - dmr: id: ch3 name: DL DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp2 contact: cont4 power: High timeout: 0 vox: 0 - dmr: id: ch4 name: HRV DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp3 contact: cont6 power: High timeout: 0 vox: 0 zones: - id: zone1 name: Zu Hause A: [ch1, ch2, ch4] B: [ch3] positioning: - dmr: id: aprs1 name: GPS System period: 300 contact: cont5 ... qdmr-0.12.3/test/data/anytone_call_hangtime.yaml000066400000000000000000000043261501654372000216500ustar00rootroot00000000000000--- version: 0.9.0 settings: introLine1: DM3MAT introLine2: qDMR micLevel: 3 speech: false power: High squelch: 1 vox: 0 tot: 0 defaultID: id1 anytone: dmrSettings: privateCallHangTime: 4s groupCallHangTime: 5s radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} contacts: - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9} - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8} - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621} - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262} - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999} - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826} groupLists: - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]} - {id: grp2, name: DL, contacts: [cont4]} - {id: grp3, name: HamRadioVillage, contacts: [cont6]} channels: - dmr: id: ch1 name: L9 DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont1 power: High timeout: 0 vox: 0 - dmr: id: ch2 name: BB DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont3 aprs: aprs1 power: High timeout: 0 vox: 0 - dmr: id: ch3 name: DL DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp2 contact: cont4 power: High timeout: 0 vox: 0 - dmr: id: ch4 name: HRV DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp3 contact: cont6 power: High timeout: 0 vox: 0 zones: - id: zone1 name: Zu Hause A: [ch1, ch2, ch4] B: [ch3] positioning: - dmr: id: aprs1 name: GPS System period: 300 contact: cont5 ... qdmr-0.12.3/test/data/anytone_key_function.yaml000066400000000000000000000047031501654372000215550ustar00rootroot00000000000000--- version: 0.11.0 settings: introLine1: DM3MAT introLine2: qDMR micLevel: 3 speech: false power: High squelch: 1 vox: 0 tot: 0 defaultID: id1 anytone: keySettings: funcKey1Short: ToggleMainChannel funcKey1Long: SubChannel funcKey2Short: ToggleVFO funcKey2Long: ChannelType funcKeyAShort: Off funcKeyALong: Encryption funcKeyBShort: Voltage funcKeyBLong: Call funcKeyCShort: Power funcKeyCLong: VOX radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} contacts: - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9} - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8} - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621} - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262} - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999} - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826} groupLists: - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]} - {id: grp2, name: DL, contacts: [cont4]} - {id: grp3, name: HamRadioVillage, contacts: [cont6]} channels: - dmr: id: ch1 name: L9 DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont1 power: High timeout: 0 vox: 0 - dmr: id: ch2 name: BB DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont3 aprs: aprs1 power: High timeout: 0 vox: 0 - dmr: id: ch3 name: DL DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp2 contact: cont4 power: High timeout: 0 vox: 0 - dmr: id: ch4 name: HRV DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp3 contact: cont6 power: High timeout: 0 vox: 0 zones: - id: zone1 name: Zu Hause A: [ch1, ch2, ch4] B: [ch3] positioning: - dmr: id: aprs1 name: GPS System period: 300 contact: cont5 ... qdmr-0.12.3/test/data/channel_frequency_test.yaml000066400000000000000000000004171501654372000220510ustar00rootroot00000000000000radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} channels: - dmr: id: ch1 name: "Test Channel" rxFrequency: 123.4567801 # <- Up to 10Hz precision -> 123.45678 MHz txFrequency: 999.99999 # <- Max frequency for 8-digit BCD in 10Hz qdmr-0.12.3/test/data/chirp_bandwidth.csv000066400000000000000000000005321501654372000203010ustar00rootroot00000000000000Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,RxDtcsCode,CrossMode,Mode,TStep,Skip,Power,Comment,URCALL,RPT1CALL,RPT2CALL,DVCODE 0,KD8BMI,147.075000,+,0.600000,Tone,103.5,77.0,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, 1,KD8BMI,147.075000,+,0.600000,Tone,103.5,77.0,023,NN,023,Tone->Tone,NFM,5.00,,50W,,,,, qdmr-0.12.3/test/data/chirp_cross.csv000066400000000000000000000015211501654372000174650ustar00rootroot00000000000000Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,RxDtcsCode,CrossMode,Mode,TStep,Skip,Power,Comment,URCALL,RPT1CALL,RPT2CALL,DVCODE 0,DB0SP,144.600000,-,0.600000,Cross,67.0,67.0,023,NN,023,->Tone,FM,5.00,,50W,,,,, 1,DB0SP,144.600000,-,0.600000,Cross,67.0,67.0,023,NN,023,->DTCS,FM,5.00,,50W,,,,, 2,DB0SP,144.600000,-,0.600000,Cross,67.0,67.0,023,NN,023,Tone->,FM,5.00,,50W,,,,, 3,DB0SP,144.600000,-,0.600000,Cross,67.0,77.0,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, 4,DB0SP,144.600000,-,0.600000,Cross,67.0,77.0,023,NN,023,Tone->DTCS,FM,5.00,,50W,,,,, 5,DB0SP,144.600000,-,0.600000,Cross,67.0,77.0,023,NN,023,DTCS->,FM,5.00,,50W,,,,, 6,DB0SP,144.600000,-,0.600000,Cross,67.0,67.0,023,NN,023,DTCS->Tone,FM,5.00,,50W,,,,, 7,DB0SP,144.600000,-,0.600000,Cross,67.0,77.0,023,NN,032,DTCS->DTCS,FM,5.00,,50W,,,,, qdmr-0.12.3/test/data/chirp_ctcss.csv000066400000000000000000000006471501654372000174630ustar00rootroot00000000000000Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,RxDtcsCode,CrossMode,Mode,TStep,Skip,Power,Comment,URCALL,RPT1CALL,RPT2CALL,DVCODE 0,DB0SP,144.600000,+,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, 1,DB0SP,144.600000,+,0.600000,Tone,67.0,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, 2,DB0SP,144.600000,+,0.600000,TSQL,67.0,77.0,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, qdmr-0.12.3/test/data/chirp_dcs.csv000066400000000000000000000010011501654372000170760ustar00rootroot00000000000000Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,RxDtcsCode,CrossMode,Mode,TStep,Skip,Power,Comment,URCALL,RPT1CALL,RPT2CALL,DVCODE 0,DB0SP,144.600000,+,0.600000,DTCS,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, 1,DB0SP,144.600000,+,0.600000,DTCS,88.5,88.5,023,NR,023,Tone->Tone,FM,5.00,,50W,,,,, 2,DB0SP,144.600000,+,0.600000,DTCS,88.5,88.5,023,RN,023,Tone->Tone,FM,5.00,,50W,,,,, 3,DB0SP,144.600000,+,0.600000,DTCS,88.5,88.5,023,RR,023,Tone->Tone,FM,5.00,,50W,,,,, qdmr-0.12.3/test/data/chirp_simple.csv000066400000000000000000000006611501654372000176310ustar00rootroot00000000000000Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,RxDtcsCode,CrossMode,Mode,TStep,Skip,Power,Comment,URCALL,RPT1CALL,RPT2CALL,DVCODE 0,KD8BMI,147.075000,+,0.600000,Tone,103.5,77.0,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, 1,W8CUL,146.760000,-,0.600000,Tone,103.5,103.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, 2,W8MWA,145.430000,-,0.600000,Tone,103.5,103.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,, qdmr-0.12.3/test/data/config_test.yaml000066400000000000000000000047321501654372000176310ustar00rootroot00000000000000--- version: 0.9.0 settings: introLine1: DM3MAT introLine2: qDMR micLevel: 3 speech: false power: High squelch: 1 vox: 0 tot: 0 defaultID: id1 radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} contacts: - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9} - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8} - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621} - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262} - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999} - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826} groupLists: - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]} - {id: grp2, name: DL, contacts: [cont4]} - {id: grp3, name: HamRadioVillage, contacts: [cont6]} channels: - dmr: id: ch1 name: L9 DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont1 power: High timeout: 0 vox: 0 - dmr: id: ch2 name: BB DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont3 aprs: aprs1 power: High timeout: 0 vox: 0 - dmr: id: ch3 name: DL DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp2 contact: cont4 power: High timeout: 0 vox: 0 - dmr: id: ch4 name: HRV DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp3 contact: cont6 power: High timeout: 0 vox: 0 scanLists: - id: scan1 name: KW channels: [ch1, ch2, ch3] zones: - id: zone1 name: Zu Hause A: [ch1, ch2, ch4] B: [ch3] - id: zone2 name: Zu Hause2 A: [ch3, ch2, ch4] B: [ch1] roamingChannels: - id: rc1 name: R DB0LDS rxFrequency: 439.5625 txFrequency: 431.96249999999998 colorCode: 1 roamingZones: - id: roam1 name: Berlin/Brand channels: - rc1 positioning: - dmr: id: aprs1 name: GPS System period: 300 contact: cont5 ... qdmr-0.12.3/test/data/ctcss_copy_test.yaml000066400000000000000000000013511501654372000205270ustar00rootroot00000000000000--- version: 0.12.0 settings: introLine1: DM3MAT introLine2: qDMR micLevel: 3 speech: false power: High squelch: 1 vox: 0 tot: 0 defaultID: id1 radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} contacts: - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9} groupLists: - {id: grp1, name: Local, contacts: [cont1]} channels: - fm: id: ch1 name: DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always rxTone: {ctcss: 67} txTone: {ctcss: 67} power: High timeout: 0 vox: 0 scanLists: - id: scan1 name: KW channels: [ch1] zones: - id: zone1 name: Zu Hause A: [ch1] B: [] ... qdmr-0.12.3/test/data/ctcss_null_test.yaml000066400000000000000000000017221501654372000205310ustar00rootroot00000000000000--- version: 0.12.1 settings: introLine1: DM3MAT introLine2: qDMR micLevel: 3 speech: false power: High squelch: 1 vox: 0 tot: 0 defaultID: id1 radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} contacts: - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9} groupLists: - {id: grp1, name: Local, contacts: [cont1]} channels: - fm: id: ch1 name: DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always rxTone: ~ txTone: {ctcss: 67} power: High timeout: 0 vox: 0 - fm: id: ch2 name: DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always rxTone: {dcs: i037} txTone: {ctcss: 67.0 Hz} power: High timeout: 0 vox: 0 scanLists: - id: scan1 name: KW channels: [ch1] zones: - id: zone1 name: Zu Hause A: [ch1] B: [] ... qdmr-0.12.3/test/data/fm_aprs_test.yaml000066400000000000000000000016531501654372000200120ustar00rootroot00000000000000--- version: 0.9.0 settings: introLine1: DM3MAT introLine2: qDMR micLevel: 3 speech: false power: High squelch: 1 vox: 0 tot: 0 defaultID: id1 radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} channels: - fm: id: aprs_ch name: 2m APRS rxFrequency: 145.8 MHz txFrequency: 145.8 MHz rxOnly: false admit: Always power: High timeout: 0 vox: 0 - fm: id: ch1 name: Some FM Channel rxFrequency: 145.1 MHz txFrequency: 145.1 MHz rxOnly: false admit: Always power: High timeout: 0 vox: 0 aprs: aprs zones: - id: zone1 name: Test A: [aprs_ch] B: [] positioning: - aprs: id: aprs name: APRS APAT81 period: 300 revert: aprs_ch icon: Jogger message: test message destination: APAT81-0 source: DM3MAT-7 path: [WIDE1-1, WIDE2-1] ... qdmr-0.12.3/test/data/multiple_radio_ids.yaml000066400000000000000000000047061501654372000211760ustar00rootroot00000000000000--- version: 0.9.0 settings: introLine1: DM3MAT introLine2: qDMR micLevel: 3 speech: false power: High squelch: 1 vox: 0 tot: 0 defaultID: id1 radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} - dmr: {id: id2, name: DN3MAT, number: 2621371} contacts: - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9} - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8} - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621} - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262} - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999} - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826} groupLists: - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]} - {id: grp2, name: DL, contacts: [cont4]} - {id: grp3, name: HamRadioVillage, contacts: [cont6]} channels: - dmr: id: ch1 name: L9 DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont1 power: High timeout: 0 vox: 0 - dmr: id: ch2 name: BB DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont3 aprs: aprs1 power: High timeout: 0 vox: 0 - dmr: id: ch3 name: DL DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp2 contact: cont4 power: High timeout: 0 vox: 0 - dmr: id: ch4 name: HRV DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp3 contact: cont6 power: High timeout: 0 vox: 0 scanLists: - id: scan1 name: KW channels: [ch1, ch2, ch3] zones: - id: zone1 name: Zu Hause A: [ch1, ch2, ch4] B: [ch3] roamingChannels: - id: rc1 name: R DB0LDS rxFrequency: 439.5625 txFrequency: 431.96249999999998 colorCode: 1 roamingZones: - id: roam1 name: Berlin/Brand channels: - rc1 positioning: - dmr: id: aprs1 name: GPS System period: 300 contact: cont5 ... qdmr-0.12.3/test/data/roaming_channel_test.yaml000066400000000000000000000051451501654372000215070ustar00rootroot00000000000000--- version: 0.9.0 settings: introLine1: DM3MAT introLine2: qDMR micLevel: 3 speech: false power: High squelch: 1 vox: 0 tot: 0 defaultID: id1 radioIDs: - dmr: {id: id1, name: DM3MAT, number: 2621370} contacts: - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9} - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8} - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621} - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262} - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999} - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826} groupLists: - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]} - {id: grp2, name: DL, contacts: [cont4]} - {id: grp3, name: HamRadioVillage, contacts: [cont6]} channels: - dmr: id: ch1 name: L9 DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont1 power: High timeout: 0 vox: 0 - dmr: id: ch2 name: BB DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS2 groupList: grp1 contact: cont3 aprs: aprs1 power: High timeout: 0 vox: 0 - dmr: id: ch3 name: DL DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp2 contact: cont4 power: High timeout: 0 vox: 0 - dmr: id: ch4 name: HRV DB0LDS rxFrequency: 439.5625 txFrequency: 431.9625 rxOnly: false admit: Always colorCode: 1 timeSlot: TS1 groupList: grp3 contact: cont6 power: High timeout: 0 vox: 0 zones: - id: zone1 name: Zu Hause A: [ch1, ch2, ch4] B: [ch3] roamingChannels: - id: rc1 name: R DB0LDS rxFrequency: 439.5625 txFrequency: 431.96249999999998 colorCode: 1 - id: rc2 name: R DM0TZN rxFrequency: 438.82499999999999 txFrequency: 431.22500000000002 colorCode: 1 - id: rc3 name: R DM0TT rxFrequency: 439.08749999999998 txFrequency: 431.48750000000001 colorCode: 1 roamingZones: - id: roam1 name: RZ1 channels: [rc1, rc2] - id: roam2 name: RZ2 channels: [rc1, rc3] positioning: - dmr: id: aprs1 name: GPS System period: 300 contact: cont5 ... qdmr-0.12.3/test/dm1701_test.cc000066400000000000000000000033131501654372000160010ustar00rootroot00000000000000#include "dm1701_test.hh" #include "config.hh" #include "dm1701_codeplug.hh" #include "errorstack.hh" #include #include DM1701Test::DM1701Test(QObject *parent) : UnitTestBase(parent) { // pass... } void DM1701Test::testBasicConfigEncoding() { ErrorStack err; DM1701Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for BTECH MD1701: %1") .arg(err.format()).toStdString().c_str()); } } void DM1701Test::testBasicConfigDecoding() { ErrorStack err; DM1701Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for BTECH DM1701: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for BTECH DM1701: %1") .arg(err.format()).toStdString().c_str()); } } void DM1701Test::testChannelFrequency() { ErrorStack err; DM1701Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for BTECH DM1701: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for BTECH DM1701: %1") .arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromHz(123456780ULL)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromHz(999999990ULL)); } QTEST_GUILESS_MAIN(DM1701Test) qdmr-0.12.3/test/dm1701_test.hh000066400000000000000000000005141501654372000160130ustar00rootroot00000000000000#ifndef DM1701TEST_HH #define DM1701TEST_HH #include "libdmrconfigtest.hh" class DM1701Test : public UnitTestBase { Q_OBJECT public: explicit DM1701Test(QObject *parent = nullptr); private slots: void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testChannelFrequency(); }; #endif // DM1701TEST_HH qdmr-0.12.3/test/dmr6x2uv_test.cc000066400000000000000000000121411501654372000165640ustar00rootroot00000000000000#include "dmr6x2uv_test.hh" #include "config.hh" #include "dmr6x2uv.hh" #include "dmr6x2uv_codeplug.hh" #include "errorstack.hh" #include #include DMR6X2UVTest::DMR6X2UVTest(QObject *parent) : UnitTestBase(parent) { // pass... } void DMR6X2UVTest::testBasicConfigEncoding() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; DMR6X2UVCodeplug codeplug; if (! codeplug.encode(&_basicConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for BTECH DMR-6X2UV: %1") .arg(err.format()).toStdString().c_str()); } } void DMR6X2UVTest::testBasicConfigDecoding() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; DMR6X2UVCodeplug codeplug; if (! codeplug.encode(&_basicConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for BTECH DMR-6X2UV: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for BETCH DMR-6X2UV: %1") .arg(err.format()).toStdString().c_str()); } } void DMR6X2UVTest::testChannelFrequency() { ErrorStack err; Codeplug::Flags flags; flags.updateCodePlug=false; D868UVCodeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_channelFrequencyConfig, flags, err)) { QFAIL(QString("Cannot encode codeplug for BTECH DMR-6X2UV: {}") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for BTECH DMR-6X2UV: {}") .arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromHz(123456780ULL)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromHz(999999990ULL)); } void DMR6X2UVTest::testFMAPRSSettings() { ErrorStack err; // Load config from file Config config; if (! config.readYAML(":/data/fm_aprs_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1\n") .arg(err.format()).toStdString().c_str()); } auto ch_ext = new AnytoneFMChannelExtension(); ch_ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Start); config.channelList()->channel(1)->as()->setAnytoneChannelExtension(ch_ext); // Check config QCOMPARE(config.posSystems()->count(), 1); QVERIFY(config.posSystems()->get(0)->is()); APRSSystem *aprs = config.posSystems()->get(0)->as(); QCOMPARE(aprs->source(), "DM3MAT"); QCOMPARE(aprs->srcSSID(), 7); QCOMPARE(aprs->destination(), "APAT81"); QCOMPARE(aprs->destSSID(), 0); QCOMPARE(aprs->path(), "WIDE1-1,WIDE2-1"); QCOMPARE(aprs->period(), 300); // test extension settings auto ext = new AnytoneFMAPRSSettingsExtension(); ext->setPreWaveDelay(Interval::fromMilliseconds(100)); ext->setTXDelay(Interval::fromMilliseconds(200)); aprs->setAnytoneExtension(ext); // Encode DMR6X2UVCodeplug codeplug; Codeplug::Flags flags; flags.updateCodePlug=false; Config *intermediate = codeplug.preprocess(&config, err); if (nullptr == intermediate) { QFAIL(QString("Cannot prepare codeplug for BTECH DMR-6X2UV: %1") .arg(err.format()).toStdString().c_str()); } if (! codeplug.encode(intermediate, flags, err)) { delete intermediate; QFAIL(QString("Cannot encode codeplug for BTECH DMR-6X2UV: %1") .arg(err.format()).toStdString().c_str()); } delete intermediate; // Decode Config comp_config; if (! codeplug.decode(&comp_config, err)) { QFAIL(QString("Cannot decode codeplug for BTECH DMR-6X2UV: %1") .arg(err.format()).toStdString().c_str()); } // Check config QCOMPARE(comp_config.posSystems()->count(), 1); QVERIFY(comp_config.posSystems()->get(0)->is()); APRSSystem *comp_aprs = comp_config.posSystems()->get(0)->as(); QCOMPARE(comp_aprs->source(), aprs->source()); QCOMPARE(comp_aprs->srcSSID(), aprs->srcSSID()); QCOMPARE(comp_aprs->destination(), aprs->destination()); QCOMPARE(comp_aprs->destSSID(), aprs->destSSID()); QCOMPARE(comp_aprs->path(), aprs->path()); QCOMPARE(comp_aprs->period(), aprs->period()); // check extension settings QVERIFY(nullptr != comp_aprs->anytoneExtension()); QCOMPARE(comp_aprs->anytoneExtension()->preWaveDelay().milliseconds(), 100); QCOMPARE(comp_aprs->anytoneExtension()->txDelay().milliseconds(), 200); // Check revert channel QCOMPARE(comp_config.channelList()->count(), 2); QVERIFY(comp_config.channelList()->channel(0)->is()); QCOMPARE(comp_config.channelList()->channel(0)->rxFrequency(), config.channelList()->channel(0)->rxFrequency()); QCOMPARE(comp_config.channelList()->channel(0)->txFrequency(), config.channelList()->channel(0)->txFrequency()); // Check channel extension properties QVERIFY(nullptr != comp_config.channelList()->channel(1)->as()->anytoneChannelExtension()); auto comp_ch_ext = comp_config.channelList()->channel(1)->as()->anytoneChannelExtension(); QCOMPARE(comp_ch_ext->aprsPTT(), ch_ext->aprsPTT()); } QTEST_GUILESS_MAIN(DMR6X2UVTest) qdmr-0.12.3/test/dmr6x2uv_test.hh000066400000000000000000000005641501654372000166040ustar00rootroot00000000000000#ifndef DMR6X2UVTEST_HH #define DMR6X2UVTEST_HH #include "libdmrconfigtest.hh" class DMR6X2UVTest : public UnitTestBase { Q_OBJECT public: explicit DMR6X2UVTest(QObject *parent = nullptr); private slots: void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testChannelFrequency(); void testFMAPRSSettings(); }; #endif // DMR6X2UVTEST_HH qdmr-0.12.3/test/dr1801_test.cc000066400000000000000000000044221501654372000160110ustar00rootroot00000000000000#include "dr1801_test.hh" #include "config.hh" #include "dr1801uv_codeplug.hh" #include "errorstack.hh" #include #include DR1801Test::DR1801Test(QObject *parent) : UnitTestBase(parent) { // pass... } void DR1801Test::testBasicConfigEncoding() { ErrorStack err; DR1801UVCodeplug codeplug; if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for BTECH DR1801UV: %1") .arg(err.format()).toStdString().c_str()); } } void DR1801Test::testBasicConfigDecoding() { ErrorStack err; DR1801UVCodeplug codeplug; if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for BTECH DR1801UV: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for BTECH DR1801UV: %1") .arg(err.format()).toStdString().c_str()); } } void DR1801Test::testBasicConfigReencoding() { ErrorStack err; DR1801UVCodeplug codeplug; if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for BTECH DR1801UV: %1") .arg(err.format()).toStdString().c_str()); } Config originalConfig; if (! codeplug.decode(&originalConfig, err)) { QFAIL(QString("Cannot decode codeplug for BTECH DR1801UV: %1") .arg(err.format()).toStdString().c_str()); } // original Config now contains all default values and extensions // reencode it if (! codeplug.encode(&originalConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for BTECH DR1801UV: %1") .arg(err.format()).toStdString().c_str()); } // Decode complete config Config testConfig; if (! codeplug.decode(&testConfig, err)) { QFAIL(QString("Cannot decode codeplug for BTECH DR1801UV: %1") .arg(err.format()).toStdString().c_str()); } // compare if (0 != originalConfig.compare(testConfig)) { QFAIL("Decoded config of BTECH DR1801UV does not match source."); } } void DR1801Test::testProstProcessingOfEmptyCodeplug() { Config config; DR1801UVCodeplug codeplug; ErrorStack err; config.clear(); QVERIFY(codeplug.postprocess(&config, err)); } QTEST_GUILESS_MAIN(DR1801Test) qdmr-0.12.3/test/dr1801_test.hh000066400000000000000000000005761501654372000160310ustar00rootroot00000000000000#ifndef DR1801TEST_HH #define DR1801TEST_HH #include "libdmrconfigtest.hh" class DR1801Test : public UnitTestBase { Q_OBJECT public: explicit DR1801Test(QObject *parent = nullptr); private slots: void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testBasicConfigReencoding(); void testProstProcessingOfEmptyCodeplug(); }; #endif // DR1801TEST_HH qdmr-0.12.3/test/gd73_test.cc000066400000000000000000000226161501654372000156430ustar00rootroot00000000000000#include "gd73_test.hh" #include "config.hh" #include "gd73.hh" #include "gd73_codeplug.hh" #include "gd73_limits.hh" #include "errorstack.hh" #include #include GD73Test::GD73Test(QObject *parent) : UnitTestBase(parent) { // pass... } void GD73Test::testBasicConfigEncoding() { ErrorStack err; GD73Codeplug codeplug; if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity GD73: %1") .arg(err.format()).toStdString().c_str()); } } void GD73Test::testBasicConfigDecoding() { ErrorStack err; GD73Codeplug codeplug; if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity GD73: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for Radioddity GD73: %1") .arg(err.format()).toStdString().c_str()); } } void GD73Test::testChannelFrequency() { ErrorStack err; GD73Codeplug codeplug; if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity GD73: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for Radioddity GD73: %1") .arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromHz(123456780ULL)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromHz(999999990ULL)); } void GD73Test::testSMSTemplates() { Config config; config.radioIDs()->add(new DMRRadioID("ID", 1234567)); SMSTemplate *sms0 = new SMSTemplate(); sms0->setName("SMS0"); sms0->setMessage("ABC"); SMSTemplate *sms1 = new SMSTemplate(); sms1->setName("SMS1"); sms1->setMessage("XYZ"); config.smsExtension()->smsTemplates()->add(sms0); config.smsExtension()->smsTemplates()->add(sms1); ErrorStack err; GD73Codeplug codeplug; if (! codeplug.encode(&config, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity GD73: %1") .arg(err.format()).toStdString().c_str()); } Config decoded; if (! codeplug.decode(&decoded, err)) { QFAIL(QString("Cannot decode codeplug for Radioddity GD73: %1") .arg(err.format()).toStdString().c_str()); } QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 2); //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(0)->name(), "SMS0"); QCOMPARE(decoded.smsExtension()->smsTemplates()->message(0)->message(), "ABC"); //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(1)->name(), "SMS1"); QCOMPARE(decoded.smsExtension()->smsTemplates()->message(1)->message(), "XYZ"); } void GD73Test::testFMSignaling() { Config config; config.radioIDs()->add(new DMRRadioID("ID", 1234567)); { FMChannel *ch = new FMChannel(); ch->setName("Channel 1"); ch->setRXFrequency(Frequency::fromMHz(144.0)); ch->setTXFrequency(Frequency::fromMHz(144.6)); ch->setRXTone(SelectiveCall(67.0)); ch->setTXTone(SelectiveCall()); config.channelList()->add(ch); } { FMChannel *ch = new FMChannel(); ch->setName("Channel 2"); ch->setRXFrequency(Frequency::fromMHz(144.0)); ch->setTXFrequency(Frequency::fromMHz(144.6)); ch->setRXTone(SelectiveCall()); ch->setTXTone(SelectiveCall(67.0)); config.channelList()->add(ch); } { FMChannel *ch = new FMChannel(); ch->setName("Channel 3"); ch->setRXFrequency(Frequency::fromMHz(144.0)); ch->setTXFrequency(Frequency::fromMHz(144.6)); ch->setRXTone(SelectiveCall(23,false)); ch->setTXTone(SelectiveCall()); config.channelList()->add(ch); } { FMChannel *ch = new FMChannel(); ch->setName("Channel 4"); ch->setRXFrequency(Frequency::fromMHz(144.0)); ch->setTXFrequency(Frequency::fromMHz(144.6)); ch->setRXTone(SelectiveCall()); ch->setTXTone(SelectiveCall(23, false)); config.channelList()->add(ch); } { FMChannel *ch = new FMChannel(); ch->setName("Channel 5"); ch->setRXFrequency(Frequency::fromMHz(144.0)); ch->setTXFrequency(Frequency::fromMHz(144.6)); ch->setRXTone(SelectiveCall(23, true)); ch->setTXTone(SelectiveCall()); config.channelList()->add(ch); } { FMChannel *ch = new FMChannel(); ch->setName("Channel 6"); ch->setRXFrequency(Frequency::fromMHz(144.0)); ch->setTXFrequency(Frequency::fromMHz(144.6)); ch->setRXTone(SelectiveCall()); ch->setTXTone(SelectiveCall(23, true)); config.channelList()->add(ch); } GD73Codeplug codeplug; ErrorStack err; if (! codeplug.encode(&config, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity GD73: %1") .arg(err.format()).toStdString().c_str()); } Config compare; if (! codeplug.decode(&compare, err)) { QFAIL(QString("Cannot decode codeplug for Radioddity GD73: {}") .arg(err.format()).toStdString().c_str()); } QCOMPARE(compare.channelList()->count(), 6); for (int i=0; icount(); i++) { QVERIFY(compare.channelList()->channel(i)->is()); QCOMPARE(compare.channelList()->channel(i)->as()->rxTone(), config.channelList()->channel(i)->as()->rxTone()); QCOMPARE(compare.channelList()->channel(i)->as()->txTone(), config.channelList()->channel(i)->as()->txTone()); } } void GD73Test::testEncryption() { Config config; config.radioIDs()->add(new DMRRadioID("ID", 1234567)); DMRContact *cnt = new DMRContact(DMRContact::AllCall, "All Call", 0, false); config.contacts()->add(cnt); BasicEncryptionKey *key = new BasicEncryptionKey(); key->setName("Key 1"); key->fromHex("1234"); config.commercialExtension()->encryptionKeys()->add(key); QCOMPARE(key->toHex(), "1234"); auto *ext = new CommercialChannelExtension(); ext->setEncryptionKey(key); DMRChannel *ch = new DMRChannel(); ch->setName("Ch"); ch->setTXContactObj(cnt); ch->setTXFrequency(Frequency::fromMHz(440)); ch->setRXFrequency(Frequency::fromMHz(440)); ch->setCommercialExtension(ext); config.channelList()->add(ch); GD73Codeplug codeplug; ErrorStack err; if (! codeplug.encode(&config, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity GD73: %1") .arg(err.format()).toStdString().c_str()); } Config compare; if (! codeplug.decode(&compare, err)) { QFAIL(QString("Cannot decode codeplug for Radioddity GD73: {}") .arg(err.format()).toStdString().c_str()); } QCOMPARE(compare.channelList()->count(), 1); QVERIFY(compare.channelList()->channel(0)->is()); QVERIFY(compare.channelList()->channel(0)->as()->commercialExtension()); QCOMPARE(compare.channelList()->channel(0)->as()->commercialExtension()->encryptionKey()->key(), config.channelList()->channel(0)->as()->commercialExtension()->encryptionKey()->key()); } void GD73Test::testEncryptionLimits() { Config config; config.radioIDs()->add(new DMRRadioID("ID", 1234567)); DMRContact *cnt = new DMRContact(DMRContact::GroupCall, "GroupCall", 1234, false); config.contacts()->add(cnt); RXGroupList *grpLst = new RXGroupList("GrpLst"); grpLst->addContact(cnt); config.rxGroupLists()->add(grpLst); BasicEncryptionKey *key = new BasicEncryptionKey(); key->setName("Key 1"); key->fromHex("1234"); config.commercialExtension()->encryptionKeys()->add(key); QCOMPARE(key->toHex(), "1234"); auto *ext = new CommercialChannelExtension(); ext->setEncryptionKey(key); DMRChannel *ch = new DMRChannel(); ch->setName("Ch"); ch->setTXContactObj(cnt); ch->setGroupListObj(grpLst); ch->setTXFrequency(Frequency::fromMHz(440)); ch->setRXFrequency(Frequency::fromMHz(440)); ch->setCommercialExtension(ext); config.channelList()->add(ch); Zone *zone = new Zone("Zone"); zone->A()->add(ch); config.zones()->add(zone); { RadioLimitContext issues; GD73Limits().verifyConfig(&config, issues); QStringList status; for (int i=0; isetName("Key 2"); key2->fromHex("1234567890abcdef1234567890abcdef"); config.commercialExtension()->encryptionKeys()->add(key2); { RadioLimitContext issues; GD73Limits().verifyConfig(&config, issues); QStringList status; for (int i=0; iencryptionKeys()->del(key2); BasicEncryptionKey *key3 = new BasicEncryptionKey(); key3->setName("Key 2"); key3->fromHex("1234567890"); config.commercialExtension()->encryptionKeys()->add(key3); QCOMPARE(key3->toHex(), "1234567890"); { RadioLimitContext issues; GD73Limits().verifyConfig(&config, issues); QStringList status; for (int i=0; i #include GD77Test::GD77Test(QObject *parent) : UnitTestBase(parent) { // pass... } void GD77Test::testBasicConfigEncoding() { ErrorStack err; GD77Codeplug codeplug; codeplug.clear(); auto intermediate = codeplug.preprocess(&_basicConfig, err); if (nullptr == intermediate) QFAIL(err.format().toLocal8Bit().constData()); if (! codeplug.encode(intermediate, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity GD77: %1") .arg(err.format()).toStdString().c_str()); } delete intermediate; } void GD77Test::testBasicConfigDecoding() { ErrorStack err; GD77Codeplug codeplug; codeplug.clear(); auto intermediate = codeplug.preprocess(&_basicConfig, err); if (nullptr == intermediate) QFAIL(err.format().toLocal8Bit().constData()); if (! codeplug.encode(intermediate, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity GD77: %1") .arg(err.format()).toStdString().c_str()); } delete intermediate; Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for Radioddity GD77: %1") .arg(err.format()).toStdString().c_str()); } codeplug.clear(); } void GD77Test::testChannelFrequency() { ErrorStack err; GD77Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity GD77: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for Radioddity GD77: %1") .arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromHz(123456780ULL)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromHz(999999990ULL)); } void GD77Test::testSMSTemplates() { Config config; config.radioIDs()->add(new DMRRadioID("ID", 1234567)); SMSTemplate *sms0 = new SMSTemplate(); sms0->setName("SMS0"); sms0->setMessage("ABC"); SMSTemplate *sms1 = new SMSTemplate(); sms1->setName("SMS1"); sms1->setMessage("XYZ"); config.smsExtension()->smsTemplates()->add(sms0); config.smsExtension()->smsTemplates()->add(sms1); ErrorStack err; GD77Codeplug codeplug; if (! codeplug.encode(&config, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity GD77: %1") .arg(err.format()).toStdString().c_str()); } Config decoded; if (! codeplug.decode(&decoded, err)) { QFAIL(QString("Cannot decode codeplug for Radioddity GD77: %1") .arg(err.format()).toStdString().c_str()); } QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 2); //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(0)->name(), "SMS0"); QCOMPARE(decoded.smsExtension()->smsTemplates()->message(0)->message(), "ABC"); //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(1)->name(), "SMS1"); QCOMPARE(decoded.smsExtension()->smsTemplates()->message(1)->message(), "XYZ"); } QTEST_GUILESS_MAIN(GD77Test) qdmr-0.12.3/test/gd77_test.hh000066400000000000000000000005351501654372000156550ustar00rootroot00000000000000#ifndef GD77TEST_HH #define GD77TEST_HH #include "libdmrconfigtest.hh" class GD77Test : public UnitTestBase { Q_OBJECT public: explicit GD77Test(QObject *parent = nullptr); private slots: void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testChannelFrequency(); void testSMSTemplates(); }; #endif // GD77TEST_HH qdmr-0.12.3/test/labeltest.cc000066400000000000000000000010401501654372000160030ustar00rootroot00000000000000#include "labeltest.hh" #include "configlabelingvisitor.hh" LabelTest::LabelTest(QObject *parent) : UnitTestBase(parent) { // pass... } void LabelTest::testLabelVisitor() { Config::Context ctx; ConfigLabelingVisitor labelPrinter(ctx); ErrorStack err; if (! labelPrinter.processItem(&_basicConfig, err)) { QFAIL(err.format().toLocal8Bit().constData()); } YAML::Node node = _basicConfig.serialize(ctx, err); if (node.IsNull()) { QFAIL(err.format().toLocal8Bit().constData()); } } QTEST_GUILESS_MAIN(LabelTest) qdmr-0.12.3/test/labeltest.hh000066400000000000000000000003721501654372000160240ustar00rootroot00000000000000#ifndef LABELTEST_HH #define LABELTEST_HH #include "libdmrconfigtest.hh" class LabelTest: public UnitTestBase { Q_OBJECT public: explicit LabelTest(QObject *parent=nullptr); private slots: void testLabelVisitor(); }; #endif // LABETEST_HH qdmr-0.12.3/test/libdmrconfigtest.cc000066400000000000000000000015161501654372000173730ustar00rootroot00000000000000#include "libdmrconfigtest.hh" UnitTestBase::UnitTestBase(QObject *parent) : QObject(parent) { // pass... } void UnitTestBase::initTestCase() { ErrorStack err; if (! _basicConfig.readYAML(":/data/config_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } if (! _channelFrequencyConfig.readYAML(":/data/channel_frequency_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } if (! _roamingConfig.readYAML(":/data/roaming_channel_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); } } void UnitTestBase::cleanupTestCase() { // clear codeplug _basicConfig.clear(); _channelFrequencyConfig.clear(); } qdmr-0.12.3/test/libdmrconfigtest.hh000066400000000000000000000006641501654372000174100ustar00rootroot00000000000000#ifndef LIBDMRCONFIGTEST_HH #define LIBDMRCONFIGTEST_HH #include #include "config.hh" #include class UnitTestBase : public QObject { Q_OBJECT public: explicit UnitTestBase(QObject *parent = nullptr); protected slots: virtual void initTestCase(); virtual void cleanupTestCase(); protected: Config _basicConfig; Config _channelFrequencyConfig; Config _roamingConfig; }; #endif // LIBDMRCONFIGTEST_HH qdmr-0.12.3/test/md2017_test.cc000066400000000000000000000033021501654372000160000ustar00rootroot00000000000000#include "md2017_test.hh" #include "config.hh" #include "md2017_codeplug.hh" #include "errorstack.hh" #include #include MD2017Test::MD2017Test(QObject *parent) : UnitTestBase(parent) { // pass... } void MD2017Test::testBasicConfigEncoding() { ErrorStack err; MD2017Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for TyT MD2017: %1") .arg(err.format()).toStdString().c_str()); } } void MD2017Test::testBasicConfigDecoding() { ErrorStack err; MD2017Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for TyT MD2017: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for TyT MD2017: %1") .arg(err.format()).toStdString().c_str()); } } void MD2017Test::testChannelFrequency() { ErrorStack err; MD2017Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for TyT MD2017: {}") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for TyT MD2017: {}") .arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromHz(123456780ULL)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromHz(999999990ULL)); } QTEST_GUILESS_MAIN(MD2017Test) qdmr-0.12.3/test/md2017_test.hh000066400000000000000000000005141501654372000160140ustar00rootroot00000000000000#ifndef MD2017TEST_HH #define MD2017TEST_HH #include "libdmrconfigtest.hh" class MD2017Test : public UnitTestBase { Q_OBJECT public: explicit MD2017Test(QObject *parent = nullptr); private slots: void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testChannelFrequency(); }; #endif // MD2017TEST_HH qdmr-0.12.3/test/md390_test.cc000066400000000000000000000055551501654372000157360ustar00rootroot00000000000000#include "md390_test.hh" #include "config.hh" #include "md390_codeplug.hh" #include "errorstack.hh" #include #include MD390Test::MD390Test(QObject *parent) : UnitTestBase(parent) { // pass... } void MD390Test::testBasicConfigEncoding() { ErrorStack err; MD390Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for TyT MD390: {}") .arg(err.format()).toStdString().c_str()); } } void MD390Test::testBasicConfigDecoding() { ErrorStack err; MD390Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for TyT MD390: {}") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for TyT MD390: {}") .arg(err.format()).toStdString().c_str()); } } void MD390Test::testChannelFrequency() { ErrorStack err; MD390Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for TyT MD390: {}") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for TyT MD390: {}") .arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromHz(123456780ULL)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromHz(999999990ULL)); } void MD390Test::testSMSTemplates() { Config config; config.radioIDs()->add(new DMRRadioID("ID", 1234567)); SMSTemplate *sms0 = new SMSTemplate(); sms0->setName("SMS0"); sms0->setMessage("ABC"); SMSTemplate *sms1 = new SMSTemplate(); sms1->setName("SMS1"); sms1->setMessage("XYZ"); config.smsExtension()->smsTemplates()->add(sms0); config.smsExtension()->smsTemplates()->add(sms1); ErrorStack err; MD390Codeplug codeplug; if (! codeplug.encode(&config, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity GD73: %1") .arg(err.format()).toStdString().c_str()); } Config decoded; if (! codeplug.decode(&decoded, err)) { QFAIL(QString("Cannot decode codeplug for Radioddity GD73: %1") .arg(err.format()).toStdString().c_str()); } QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 2); //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(0)->name(), "SMS0"); QCOMPARE(decoded.smsExtension()->smsTemplates()->message(0)->message(), "ABC"); //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(1)->name(), "SMS1"); QCOMPARE(decoded.smsExtension()->smsTemplates()->message(1)->message(), "XYZ"); } QTEST_GUILESS_MAIN(MD390Test) qdmr-0.12.3/test/md390_test.hh000066400000000000000000000005421501654372000157370ustar00rootroot00000000000000#ifndef MD390TEST_HH #define MD390TEST_HH #include "libdmrconfigtest.hh" class MD390Test : public UnitTestBase { Q_OBJECT public: explicit MD390Test(QObject *parent = nullptr); private slots: void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testChannelFrequency(); void testSMSTemplates(); }; #endif // MD390TEST_HH qdmr-0.12.3/test/mergetest.cc000066400000000000000000000425251501654372000160400ustar00rootroot00000000000000#include "mergetest.hh" #include #include "config.hh" #include "configmergevisitor.hh" MergeTest::MergeTest(QObject *parent) : QObject{parent} { // pass... } void MergeTest::testMergeRadioIds() { Config *base = new Config(), *merging = new Config(); base->radioIDs()->add(new DMRRadioID("ID 0", 1234)); base->radioIDs()->add(new DMRRadioID("ID 1", 1234)); merging->radioIDs()->add(new DMRRadioID("ID 1", 2345)); merging->radioIDs()->add(new DMRRadioID("ID 2", 2345)); ErrorStack err; Config *merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Ignore, ConfigMergeVisitor::SetStrategy::Ignore, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->radioIDs()->count(), 3); QCOMPARE(merged->radioIDs()->getId(0)->name(), "ID 0"); QCOMPARE(merged->radioIDs()->getId(0)->as()->number(), 1234); QCOMPARE(merged->radioIDs()->getId(1)->name(), "ID 1"); QCOMPARE(merged->radioIDs()->getId(1)->as()->number(), 1234); QCOMPARE(merged->radioIDs()->getId(2)->name(), "ID 2"); QCOMPARE(merged->radioIDs()->getId(2)->as()->number(), 2345); merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Override, ConfigMergeVisitor::SetStrategy::Ignore, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->radioIDs()->count(), 3); QCOMPARE(merged->radioIDs()->getId(0)->name(), "ID 0"); QCOMPARE(merged->radioIDs()->getId(0)->as()->number(), 1234); QCOMPARE(merged->radioIDs()->getId(1)->name(), "ID 1"); QCOMPARE(merged->radioIDs()->getId(1)->as()->number(), 2345); QCOMPARE(merged->radioIDs()->getId(2)->name(), "ID 2"); QCOMPARE(merged->radioIDs()->getId(2)->as()->number(), 2345); merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Duplicate, ConfigMergeVisitor::SetStrategy::Ignore, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->radioIDs()->count(), 4); QCOMPARE(merged->radioIDs()->getId(0)->name(), "ID 0"); QCOMPARE(merged->radioIDs()->getId(0)->as()->number(), 1234); QCOMPARE(merged->radioIDs()->getId(1)->name(), "ID 1"); QCOMPARE(merged->radioIDs()->getId(1)->as()->number(), 1234); QCOMPARE(merged->radioIDs()->getId(2)->name(), "ID 1 (copy)"); QCOMPARE(merged->radioIDs()->getId(2)->as()->number(), 2345); QCOMPARE(merged->radioIDs()->getId(3)->name(), "ID 2"); QCOMPARE(merged->radioIDs()->getId(3)->as()->number(), 2345); } void MergeTest::testMergeContacts() { Config *base = new Config(), *merging = new Config(); base->contacts()->add(new DMRContact(DMRContact::PrivateCall, "ID 0", 1234)); base->contacts()->add(new DMRContact(DMRContact::PrivateCall, "ID 1", 1234)); merging->contacts()->add(new DMRContact(DMRContact::PrivateCall, "ID 1", 2345)); merging->contacts()->add(new DMRContact(DMRContact::PrivateCall, "ID 2", 2345)); ErrorStack err; Config *merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Ignore, ConfigMergeVisitor::SetStrategy::Ignore, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->contacts()->count(), 3); QCOMPARE(merged->contacts()->get(0)->as()->name(), "ID 0"); QCOMPARE(merged->contacts()->get(0)->as()->number(), 1234); QCOMPARE(merged->contacts()->get(1)->as()->name(), "ID 1"); QCOMPARE(merged->contacts()->get(1)->as()->number(), 1234); QCOMPARE(merged->contacts()->get(2)->as()->name(), "ID 2"); QCOMPARE(merged->contacts()->get(2)->as()->number(), 2345); merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Override, ConfigMergeVisitor::SetStrategy::Ignore, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->contacts()->count(), 3); QCOMPARE(merged->contacts()->get(0)->as()->name(), "ID 0"); QCOMPARE(merged->contacts()->get(0)->as()->number(), 1234); QCOMPARE(merged->contacts()->get(1)->as()->name(), "ID 1"); QCOMPARE(merged->contacts()->get(1)->as()->number(), 2345); QCOMPARE(merged->contacts()->get(2)->as()->name(), "ID 2"); QCOMPARE(merged->contacts()->get(2)->as()->number(), 2345); merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Duplicate, ConfigMergeVisitor::SetStrategy::Ignore, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->contacts()->count(), 4); QCOMPARE(merged->contacts()->get(0)->as()->name(), "ID 0"); QCOMPARE(merged->contacts()->get(0)->as()->number(), 1234); QCOMPARE(merged->contacts()->get(1)->as()->name(), "ID 1"); QCOMPARE(merged->contacts()->get(1)->as()->number(), 1234); QCOMPARE(merged->contacts()->get(2)->as()->name(), "ID 1 (copy)"); QCOMPARE(merged->contacts()->get(2)->as()->number(), 2345); QCOMPARE(merged->contacts()->get(3)->as()->name(), "ID 2"); QCOMPARE(merged->contacts()->get(3)->as()->number(), 2345); } void MergeTest::testMergeGroupLists() { Config *base = new Config(), *merging = new Config(); base->contacts()->add(new DMRContact(DMRContact::PrivateCall, "ID 0", 1234)); base->contacts()->add(new DMRContact(DMRContact::PrivateCall, "ID 1", 1234)); { RXGroupList *lst = new RXGroupList("List 0"); lst->addContact(base->contacts()->contact(0)->as()); lst->addContact(base->contacts()->contact(1)->as()); base->rxGroupLists()->add(lst); } merging->contacts()->add(new DMRContact(DMRContact::PrivateCall, "ID 1", 2345)); merging->contacts()->add(new DMRContact(DMRContact::PrivateCall, "ID 2", 2345)); { RXGroupList *lst = new RXGroupList("List 0"); lst->addContact(merging->contacts()->contact(0)->as()); lst->addContact(merging->contacts()->contact(1)->as()); merging->rxGroupLists()->add(lst); } ErrorStack err; Config *merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Ignore, ConfigMergeVisitor::SetStrategy::Ignore, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->contacts()->count(), 3); QCOMPARE(merged->rxGroupLists()->count(), 1); QCOMPARE(merged->rxGroupLists()->list(0)->count(), 2); QCOMPARE(merged->rxGroupLists()->list(0)->contact(0), merged->contacts()->contact(0)); QCOMPARE(merged->rxGroupLists()->list(0)->contact(1), merged->contacts()->contact(1)); merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Ignore, ConfigMergeVisitor::SetStrategy::Merge, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->contacts()->count(), 3); QCOMPARE(merged->rxGroupLists()->count(), 1); QCOMPARE(merged->rxGroupLists()->list(0)->count(), 3); QCOMPARE(merged->rxGroupLists()->list(0)->contact(0), merged->contacts()->contact(0)); QCOMPARE(merged->rxGroupLists()->list(0)->contact(1), merged->contacts()->contact(1)); QCOMPARE(merged->rxGroupLists()->list(0)->contact(2), merged->contacts()->contact(2)); merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Override, ConfigMergeVisitor::SetStrategy::Merge, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->contacts()->count(), 3); QCOMPARE(merged->rxGroupLists()->count(), 1); QCOMPARE(merged->rxGroupLists()->list(0)->count(), 3); QCOMPARE(merged->rxGroupLists()->list(0)->contact(0), merged->contacts()->contact(0)); QCOMPARE(merged->rxGroupLists()->list(0)->contact(1), merged->contacts()->contact(1)); QCOMPARE(merged->rxGroupLists()->list(0)->contact(2), merged->contacts()->contact(2)); merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Ignore, ConfigMergeVisitor::SetStrategy::Override, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->contacts()->count(), 3); QCOMPARE(merged->rxGroupLists()->count(), 1); QCOMPARE(merged->rxGroupLists()->list(0)->count(), 2); QCOMPARE(merged->rxGroupLists()->list(0)->contact(0), merged->contacts()->contact(1)); QCOMPARE(merged->rxGroupLists()->list(0)->contact(1), merged->contacts()->contact(2)); merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Duplicate, ConfigMergeVisitor::SetStrategy::Merge, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->contacts()->count(), 4); QCOMPARE(merged->rxGroupLists()->count(), 1); QCOMPARE(merged->rxGroupLists()->list(0)->count(), 4); QCOMPARE(merged->rxGroupLists()->list(0)->contact(0), merged->contacts()->contact(0)); QCOMPARE(merged->rxGroupLists()->list(0)->contact(1), merged->contacts()->contact(1)); QCOMPARE(merged->rxGroupLists()->list(0)->contact(2), merged->contacts()->contact(2)); QCOMPARE(merged->rxGroupLists()->list(0)->contact(3), merged->contacts()->contact(3)); } void MergeTest::testMergeChannels() { Config *base = new Config(), *merging = new Config(); base->contacts()->add(new DMRContact(DMRContact::GroupCall, "ID 0", 1234)); { RXGroupList *lst = new RXGroupList("List 0"); lst->addContact(base->contacts()->contact(0)->as()); base->rxGroupLists()->add(lst); FMChannel *ach = new FMChannel(); ach->setName("FM 0"); ach->setRXFrequency(Frequency::fromMHz(144.0)); ach->setTXFrequency(Frequency::fromMHz(144.0)); base->channelList()->add(ach); DMRChannel *dch = new DMRChannel(); dch->setName("DMR 0"); dch->setRXFrequency(Frequency::fromMHz(144.0)); dch->setTXFrequency(Frequency::fromMHz(144.0)); dch->setTXContactObj(base->contacts()->contact(0)->as()); dch->setGroupListObj(lst); base->channelList()->add(dch); } merging->contacts()->add(new DMRContact(DMRContact::GroupCall, "ID 0", 2345)); merging->contacts()->add(new DMRContact(DMRContact::GroupCall, "ID 1", 3456)); { RXGroupList *lst = new RXGroupList("List 0"); lst->addContact(merging->contacts()->contact(0)->as()); merging->rxGroupLists()->add(lst); FMChannel *ach = new FMChannel(); ach->setName("FM 0"); ach->setRXFrequency(Frequency::fromMHz(145.0)); ach->setTXFrequency(Frequency::fromMHz(145.0)); merging->channelList()->add(ach); DMRChannel *dch = new DMRChannel(); dch->setName("DMR 0"); dch->setRXFrequency(Frequency::fromMHz(145.0)); dch->setTXFrequency(Frequency::fromMHz(145.0)); dch->setTXContactObj(merging->contacts()->contact(0)->as()); dch->setGroupListObj(lst); merging->channelList()->add(dch); } ErrorStack err; Config *merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Ignore, ConfigMergeVisitor::SetStrategy::Ignore, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->contacts()->count(), 2); QCOMPARE(merged->rxGroupLists()->count(), 1); QCOMPARE(merged->channelList()->count(), 2); QVERIFY(merged->channelList()->channel(0)->is()); QCOMPARE(merged->channelList()->channel(0)->rxFrequency().inMHz(), 144.0); QVERIFY(merged->channelList()->channel(1)->is()); QCOMPARE(merged->channelList()->channel(1)->rxFrequency().inMHz(), 144.0); QCOMPARE(merged->channelList()->channel(1)->as()->txContactObj(), merged->contacts()->contact(0)->as()); QCOMPARE(merged->channelList()->channel(1)->as()->groupListObj(), merged->rxGroupLists()->list(0)); merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Override, ConfigMergeVisitor::SetStrategy::Ignore, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->contacts()->count(), 2); QCOMPARE(merged->rxGroupLists()->count(), 1); QCOMPARE(merged->channelList()->count(), 2); QVERIFY(merged->channelList()->channel(0)->is()); QCOMPARE(merged->channelList()->channel(0)->rxFrequency().inMHz(), 145.0); QVERIFY(merged->channelList()->channel(1)->is()); QCOMPARE(merged->channelList()->channel(1)->rxFrequency().inMHz(), 145.0); QCOMPARE(merged->channelList()->channel(1)->as()->txContactObj(), merged->contacts()->contact(0)->as()); QCOMPARE(merged->channelList()->channel(1)->as()->groupListObj(), merged->rxGroupLists()->list(0)); } void MergeTest::testMergeZones() { Config *base = new Config(), *merging = new Config(); base->contacts()->add(new DMRContact(DMRContact::GroupCall, "ID 0", 1234)); { RXGroupList *lst = new RXGroupList("List 0"); lst->addContact(base->contacts()->contact(0)->as()); base->rxGroupLists()->add(lst); FMChannel *ach = new FMChannel(); ach->setName("FM 0"); ach->setRXFrequency(Frequency::fromMHz(144.0)); ach->setTXFrequency(Frequency::fromMHz(144.0)); base->channelList()->add(ach); DMRChannel *dch = new DMRChannel(); dch->setName("DMR 0"); dch->setRXFrequency(Frequency::fromMHz(144.0)); dch->setTXFrequency(Frequency::fromMHz(144.0)); dch->setTXContactObj(base->contacts()->contact(0)->as()); dch->setGroupListObj(lst); base->channelList()->add(dch); Zone *zone = new Zone("Zone 0"); zone->A()->add(ach); zone->A()->add(dch); base->zones()->add(zone); } merging->contacts()->add(new DMRContact(DMRContact::GroupCall, "ID 0", 2345)); merging->contacts()->add(new DMRContact(DMRContact::GroupCall, "ID 1", 3456)); { RXGroupList *lst = new RXGroupList("List 0"); lst->addContact(merging->contacts()->contact(0)->as()); merging->rxGroupLists()->add(lst); FMChannel *ach = new FMChannel(); ach->setName("FM 0"); ach->setRXFrequency(Frequency::fromMHz(145.0)); ach->setTXFrequency(Frequency::fromMHz(145.0)); merging->channelList()->add(ach); DMRChannel *dch = new DMRChannel(); dch->setName("DMR 1"); dch->setRXFrequency(Frequency::fromMHz(145.0)); dch->setTXFrequency(Frequency::fromMHz(145.0)); dch->setTXContactObj(merging->contacts()->contact(0)->as()); dch->setGroupListObj(lst); merging->channelList()->add(dch); Zone *zone = new Zone("Zone 0"); zone->A()->add(ach); zone->A()->add(dch); merging->zones()->add(zone); } ErrorStack err; Config *merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Ignore, ConfigMergeVisitor::SetStrategy::Ignore, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->channelList()->count(), 3); QCOMPARE(merged->zones()->count(), 1); QCOMPARE(merged->zones()->zone(0)->A()->count(), 2); QCOMPARE(merged->zones()->zone(0)->A()->get(0)->as(), merged->channelList()->channel(0)); QCOMPARE(merged->zones()->zone(0)->A()->get(1)->as(), merged->channelList()->channel(1)); merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Ignore, ConfigMergeVisitor::SetStrategy::Override, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->channelList()->count(), 3); QCOMPARE(merged->zones()->count(), 1); QCOMPARE(merged->zones()->zone(0)->A()->count(), 2); QCOMPARE(merged->zones()->zone(0)->A()->get(0)->as(), merged->channelList()->channel(0)); QCOMPARE(merged->zones()->zone(0)->A()->get(1)->as(), merged->channelList()->channel(2)); merged = ConfigMerge::merge(base, merging, ConfigMergeVisitor::ItemStrategy::Ignore, ConfigMergeVisitor::SetStrategy::Merge, err); if (nullptr == merged) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(merged->channelList()->count(), 3); QCOMPARE(merged->zones()->count(), 1); QCOMPARE(merged->zones()->zone(0)->A()->count(), 3); QCOMPARE(merged->zones()->zone(0)->A()->get(0)->as(), merged->channelList()->channel(0)); QCOMPARE(merged->zones()->zone(0)->A()->get(1)->as(), merged->channelList()->channel(1)); QCOMPARE(merged->zones()->zone(0)->A()->get(2)->as(), merged->channelList()->channel(2)); } QTEST_GUILESS_MAIN(MergeTest) qdmr-0.12.3/test/mergetest.hh000066400000000000000000000005331501654372000160430ustar00rootroot00000000000000#ifndef MERGETEST_HH #define MERGETEST_HH #include class MergeTest : public QObject { Q_OBJECT public: explicit MergeTest(QObject *parent=nullptr); private slots: void testMergeRadioIds(); void testMergeContacts(); void testMergeGroupLists(); void testMergeChannels(); void testMergeZones(); }; #endif // MERGETEST_HH qdmr-0.12.3/test/opengd77_test.cc000066400000000000000000000201241501654372000165210ustar00rootroot00000000000000#include "opengd77_test.hh" #include "config.hh" #include "opengd77_codeplug.hh" #include "errorstack.hh" #include #include #include #include #include "logger.hh" OpenGD77Test::OpenGD77Test(QObject *parent) : UnitTestBase(parent), _stderr(stderr) { Logger::get().addHandler(new StreamLogHandler(_stderr, LogMessage::DEBUG)); } void OpenGD77Test::testBasicConfigEncoding() { ErrorStack err; OpenGD77Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for OpenGD77: %1") .arg(err.format()).toStdString().c_str()); } } bool OpenGD77Test::encodeDecode(Config &config, Config &decoded, const ErrorStack &err) { OpenGD77Codeplug codeplug; codeplug.clear(); Config *intermediate = codeplug.preprocess(&config, err); if (nullptr == intermediate) { errMsg(err) << "Cannot encode codeplug for OpenGD77."; return false; } if (! codeplug.encode(intermediate, Codeplug::Flags(), err)) { errMsg(err) << "Cannot encode codeplug for OpenGD77."; return false; } delete intermediate; if (! codeplug.decode(&decoded, err)) { errMsg(err) << "Cannot decode codeplug for OpenGD77."; return false; } if (! codeplug.postprocess(&decoded, err)) { errMsg(err) << "Cannot decode codeplug for OpenGD77."; return false; } return true; } void OpenGD77Test::testBasicConfigDecoding() { ErrorStack err; Config decoded; if (! encodeDecode(_basicConfig, decoded, err)) QFAIL(err.format().toLocal8Bit().constData()); } void OpenGD77Test::testChannelFrequency() { ErrorStack err; Config config; if (! encodeDecode(_channelFrequencyConfig, config, err)) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromHz(123456780ULL)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromHz(999999990ULL)); } void OpenGD77Test::testChannelGroupList() { ErrorStack err; Config config, decoded; if (! config.readYAML(":/data/config_test.yaml", err)) QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toLocal8Bit().constData()); config.channelList()->channel(0)->as()->setGroupListObj(nullptr); config.channelList()->channel(0)->as()->setTXContactObj(nullptr); config.channelList()->channel(1)->as()->setTXContactObj(nullptr); config.channelList()->channel(2)->as()->setGroupListObj(nullptr); if (! encodeDecode(config, decoded, err)) QFAIL(err.format().toLocal8Bit().constData()); QVERIFY(decoded.channelList()->channel(0)->as()->groupList()->isNull()); QVERIFY(decoded.channelList()->channel(0)->as()->contact()->isNull()); QVERIFY(! decoded.channelList()->channel(1)->as()->groupList()->isNull()); QVERIFY(decoded.channelList()->channel(1)->as()->contact()->isNull()); QVERIFY(decoded.channelList()->channel(2)->as()->groupList()->isNull()); QVERIFY(! decoded.channelList()->channel(2)->as()->contact()->isNull()); QVERIFY(! decoded.channelList()->channel(3)->as()->groupList()->isNull()); QVERIFY(decoded.channelList()->channel(3)->as()->contact()->isNull()); } void OpenGD77Test::testChannelPowerSettings() { ErrorStack err; Config config, decoded; if (! config.readYAML(":/data/config_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toLocal8Bit().constData()); } config.channelList()->channel(0)->setDefaultPower(); if (! encodeDecode(config, decoded, err)) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(decoded.channelList()->channel(0)->defaultPower(), true); config.channelList()->channel(0)->setPower(Channel::Power::Low); if (! encodeDecode(config, decoded, err)) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(decoded.channelList()->channel(0)->defaultPower(), false); QCOMPARE(decoded.channelList()->channel(0)->power(), Channel::Power::Low); config.channelList()->channel(0)->setPower(Channel::Power::High); if (! encodeDecode(config, decoded, err)) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(decoded.channelList()->channel(0)->defaultPower(), false); QCOMPARE(decoded.channelList()->channel(0)->power(), Channel::Power::High); } void OpenGD77Test::testOverrideChannelRadioId() { ErrorStack err; Config config, decoded; if (! config.readYAML(":/data/config_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toLocal8Bit().constData()); } // add another DMR ID and assign it to two channels auto id2 = new DMRRadioID("Test", 1234567); config.radioIDs()->add(id2); config.channelList()->channel(0)->as()->setRadioIdObj(id2); config.channelList()->channel(1)->as()->setRadioIdObj(id2); if (! encodeDecode(config, decoded, err)) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(decoded.radioIDs()->count(), 2); QVERIFY(! decoded.channelList()->channel(0)->as()->radioId()->isNull()); QCOMPARE(decoded.channelList()->channel(0)->as()->radioIdObj()->number(), 1234567); QVERIFY(! decoded.channelList()->channel(1)->as()->radioId()->isNull()); QCOMPARE(decoded.channelList()->channel(1)->as()->radioIdObj()->number(), 1234567); } void OpenGD77Test::testChannelSubTones() { ErrorStack err; Config config, decoded; char enc[] = {0x00, 0x10}; uint16_t dec = *(quint16 *)enc; SelectiveCall decTone = OpenGD77BaseCodeplug::decodeSelectiveCall( qFromLittleEndian(dec)); QVERIFY(decTone.isValid()); QVERIFY(decTone.isCTCSS()); QCOMPARE(decTone.Hz(), 100.0); if (! config.readYAML(":/data/fm_aprs_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toLocal8Bit().constData()); } config.channelList()->channel(0)->as()->setTXTone(SelectiveCall(67.0)); config.channelList()->channel(0)->as()->setRXTone(SelectiveCall(123.0)); if (! encodeDecode(config, decoded, err)) QFAIL(err.format().toLocal8Bit().constData()); SelectiveCall txTone = decoded.channelList()->channel(0)->as()->txTone(), rxTone = decoded.channelList()->channel(0)->as()->rxTone(); QVERIFY(txTone.isValid()); QVERIFY(txTone.isCTCSS()); QCOMPARE(txTone.Hz(), 67.0); QVERIFY(rxTone.isValid()); QVERIFY(rxTone.isCTCSS()); QCOMPARE(rxTone.Hz(), 123.0); } void OpenGD77Test::testChannelFixedLocation() { ErrorStack err; Config config, decoded; if (! config.readYAML(":/data/fm_aprs_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toLocal8Bit().constData()); } auto ext = new OpenGD77ChannelExtension(); ext->setLocator("JO62jl24"); config.channelList()->channel(0)->setOpenGD77ChannelExtension(ext); if (! encodeDecode(config, decoded, err)) QFAIL(err.format().toLocal8Bit().constData()); QVERIFY(decoded.channelList()->channel(0)->openGD77ChannelExtension()); QCOMPARE(decoded.channelList()->channel(0)->openGD77ChannelExtension()->locator(), "JO62jl24"); ext->setLocator("JO59gw73"); if (! encodeDecode(config, decoded, err)) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(decoded.channelList()->channel(0)->openGD77ChannelExtension()->locator(), "JO59gw73"); } void OpenGD77Test::testAPRSSourceCall() { ErrorStack err; Config config, decoded; if (! config.readYAML(":/data/fm_aprs_test.yaml", err)) { QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toLocal8Bit().constData()); } if (! encodeDecode(config, decoded, err)) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(decoded.posSystems()->count(), 1); auto sys = decoded.posSystems()->aprsSystem(0); QCOMPARE(sys->source(), "DM3MAT"); FMChannel *channel = decoded.posSystems()->aprsSystem(0)->revert()->as(); QVERIFY(channel->name() == "2m APRS"); } QTEST_GUILESS_MAIN(OpenGD77Test) qdmr-0.12.3/test/opengd77_test.hh000066400000000000000000000017261501654372000165420ustar00rootroot00000000000000#ifndef OPENGD77TEST_HH #define OPENGD77TEST_HH #include "libdmrconfigtest.hh" #include #include #include "config.hh" #include "errorstack.hh" class OpenGD77Test : public UnitTestBase { Q_OBJECT public: explicit OpenGD77Test(QObject *parent = nullptr); private slots: void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testChannelFrequency(); /** Regression test for #539. */ void testChannelGroupList(); /** Regression test for #507 */ void testChannelPowerSettings(); /** Regression test for #541. */ void testOverrideChannelRadioId(); /** Regression test for #554. */ void testAPRSSourceCall(); /** Regression test for #549. */ void testChannelSubTones(); /** Regression test for #556. */ void testChannelFixedLocation(); protected: static bool encodeDecode(Config &config, Config &decoded, const ErrorStack &err=ErrorStack()); protected: QTextStream _stderr; }; #endif // OPENGD77TEST_HH qdmr-0.12.3/test/openuv380_test.cc000066400000000000000000000034651501654372000166470ustar00rootroot00000000000000#include "openuv380_test.hh" #include "config.hh" #include "openuv380_codeplug.hh" #include "errorstack.hh" #include #include "logger.hh" OpenUV380Test::OpenUV380Test(QObject *parent) : UnitTestBase(parent), _stderr(stderr) { Logger::get().addHandler(new StreamLogHandler(_stderr, LogMessage::DEBUG)); } void OpenUV380Test::testBasicConfigEncoding() { ErrorStack err; OpenUV380Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for OpenGD77: %1") .arg(err.format()).toStdString().c_str()); } } void OpenUV380Test::testBasicConfigDecoding() { ErrorStack err; OpenUV380Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for OpenGD77: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for OpenGD77: %1") .arg(err.format()).toStdString().c_str()); } } void OpenUV380Test::testChannelFrequency() { ErrorStack err; OpenUV380Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for OpenGD77: {}") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for Radioddity RD5R: {}") .arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromHz(123456780ULL)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromHz(999999990ULL)); } QTEST_GUILESS_MAIN(OpenUV380Test) qdmr-0.12.3/test/openuv380_test.hh000066400000000000000000000006751501654372000166610ustar00rootroot00000000000000#ifndef OPENUV380TEST_HH #define OPENUV380TEST_HH #include "libdmrconfigtest.hh" #include #include #include "config.hh" class OpenUV380Test : public UnitTestBase { Q_OBJECT public: explicit OpenUV380Test(QObject *parent = nullptr); private slots: void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testChannelFrequency(); protected: QTextStream _stderr; }; #endif // OPENUV380TEST_HH qdmr-0.12.3/test/rd5r_test.cc000066400000000000000000000055601501654372000157520ustar00rootroot00000000000000#include "rd5r_test.hh" #include "config.hh" #include "rd5r.hh" #include "rd5r_codeplug.hh" #include "errorstack.hh" #include #include RD5RTest::RD5RTest(QObject *parent) : UnitTestBase(parent) { // pass... } void RD5RTest::testBasicConfigEncoding() { ErrorStack err; RD5RCodeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity RD5R: %1") .arg(err.format()).toStdString().c_str()); } } void RD5RTest::testBasicConfigDecoding() { ErrorStack err; RD5RCodeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity RD5R: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for Radioddity RD5R: %1") .arg(err.format()).toStdString().c_str()); } } void RD5RTest::testChannelFrequency() { ErrorStack err; RD5RCodeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity RD5R: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for Radioddity RD5R: %1") .arg(err.format()).toStdString().c_str()); } /*QCOMPARE(config.channelList()->channel(0)->rxFrequency(), 123456780ULL); QCOMPARE(config.channelList()->channel(0)->txFrequency(), 1234567890ULL);*/ } void RD5RTest::testSMSTemplates() { Config config; config.radioIDs()->add(new DMRRadioID("ID", 1234567)); SMSTemplate *sms0 = new SMSTemplate(); sms0->setName("SMS0"); sms0->setMessage("ABC"); SMSTemplate *sms1 = new SMSTemplate(); sms1->setName("SMS1"); sms1->setMessage("XYZ"); config.smsExtension()->smsTemplates()->add(sms0); config.smsExtension()->smsTemplates()->add(sms1); ErrorStack err; RD5RCodeplug codeplug; if (! codeplug.encode(&config, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for Radioddity RD5R: %1") .arg(err.format()).toStdString().c_str()); } Config decoded; if (! codeplug.decode(&decoded, err)) { QFAIL(QString("Cannot decode codeplug for Radioddity RD5R: %1") .arg(err.format()).toStdString().c_str()); } QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 2); //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(0)->name(), "SMS0"); QCOMPARE(decoded.smsExtension()->smsTemplates()->message(0)->message(), "ABC"); //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(1)->name(), "SMS1"); QCOMPARE(decoded.smsExtension()->smsTemplates()->message(1)->message(), "XYZ"); } QTEST_GUILESS_MAIN(RD5RTest) qdmr-0.12.3/test/rd5r_test.hh000066400000000000000000000005341501654372000157600ustar00rootroot00000000000000#ifndef RD5RTEST_HH #define RD5RTEST_HH #include "libdmrconfigtest.hh" class RD5RTest : public UnitTestBase { Q_OBJECT public: explicit RD5RTest(QObject *parent = nullptr); private slots: void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testChannelFrequency(); void testSMSTemplates(); }; #endif // RD5RTEST_HH qdmr-0.12.3/test/resources.qrc000066400000000000000000000015071501654372000162460ustar00rootroot00000000000000 data/config_test.yaml data/channel_frequency_test.yaml data/anytone_auto_repeater_extension.yaml data/anytone_audio_settings_extension.yaml data/roaming_channel_test.yaml data/anytone_call_hangtime.yaml data/multiple_radio_ids.yaml data/ctcss_copy_test.yaml data/anytone_key_function.yaml data/chirp_simple.csv data/chirp_cross.csv data/chirp_ctcss.csv data/chirp_dcs.csv data/fm_aprs_test.yaml data/ctcss_null_test.yaml data/chirp_bandwidth.csv qdmr-0.12.3/test/smstemplatetest.cc000066400000000000000000000041331501654372000172700ustar00rootroot00000000000000#include "smstemplatetest.hh" SMSTemplateTest::SMSTemplateTest(QObject *parent) : UnitTestBase{parent} { // pass... } void SMSTemplateTest::serializationTest() { Config config; auto msg1 = new SMSTemplate(); msg1->setName("Message 1"); msg1->setMessage("Some message"); config.smsExtension()->smsTemplates()->add(msg1); ErrorStack err; QString buffer; QTextStream stream(&buffer); if (! config.toYAML(stream, err)) QFAIL(err.format().toLocal8Bit().constData()); Config comp_config; Config::Context ctx; YAML::Node doc = YAML::Load(buffer.toStdString()); if (! comp_config.parse(doc, ctx, err)) QFAIL(err.format().toLocal8Bit().constData()); if (! comp_config.link(doc, ctx, err)) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(comp_config.smsExtension()->smsTemplates()->count(), config.smsExtension()->smsTemplates()->count()); QCOMPARE(comp_config.smsExtension()->smsTemplates()->message(0)->name(), config.smsExtension()->smsTemplates()->message(0)->name()); QCOMPARE(comp_config.smsExtension()->smsTemplates()->message(0)->message(), config.smsExtension()->smsTemplates()->message(0)->message()); } void SMSTemplateTest::testMessageDuplication() { Config config; { auto msg1 = new SMSTemplate(); msg1->setName("Message 1"); msg1->setMessage("Some message"); config.smsExtension()->smsTemplates()->add(msg1); } ErrorStack err; QString buffer; QTextStream stream(&buffer); if (! config.toYAML(stream, err)) QFAIL(err.format().toLocal8Bit().constData()); config.clear(); Config::Context ctx; YAML::Node doc = YAML::Load(buffer.toStdString()); if (! config.parse(doc, ctx, err)) QFAIL(err.format().toLocal8Bit().constData()); if (! config.link(doc, ctx, err)) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(config.smsExtension()->smsTemplates()->count(), 1); QCOMPARE(config.smsExtension()->smsTemplates()->message(0)->name(), "Message 1"); QCOMPARE(config.smsExtension()->smsTemplates()->message(0)->message(), "Some message"); } QTEST_GUILESS_MAIN(SMSTemplateTest) qdmr-0.12.3/test/smstemplatetest.hh000066400000000000000000000005411501654372000173010ustar00rootroot00000000000000#ifndef SMSTEMPLATETEST_HH #define SMSTEMPLATETEST_HH #include "libdmrconfigtest.hh" class SMSTemplateTest : public UnitTestBase { Q_OBJECT public: explicit SMSTemplateTest(QObject *parent=nullptr); private slots: void serializationTest(); /** Regression test for #511. */ void testMessageDuplication(); }; #endif // SMSTEMPLATETEST_HH qdmr-0.12.3/test/tableformattest.cc000066400000000000000000000026341501654372000172360ustar00rootroot00000000000000#include "tableformattest.hh" #include "config.hh" #include "csvreader.hh" #include TableFormatTest::TableFormatTest(QObject *parent) : QObject{parent} { // pass... } void TableFormatTest::testFrequencyParser() { Config config; QString data; QTextStream stream(&data); stream << "ID: 2621370" << Qt::endl << "Name: \"DM3MAT\"" << Qt::endl << "Digital Name Receive Transmit Power Scan TOT RO Admit CC TS RxGL TxC GPS Roam ID" << Qt::endl << "1 \"test 0\" 439.56250 -7.60000 High - - - Color 1 1 - - - + - # Local" << Qt::endl << "2 \"test 1\" 439.56250 +7.60000 High - - - Color 1 1 - - - + - # Sa/Th" << Qt::endl << "3 \"test 2\" 439.56250 439.56250 High - - - Color 1 2 - - - + - # Regional" << Qt::endl; QString errMsg; QVERIFY2(CSVReader::read(&config, stream, errMsg), errMsg.toStdString().c_str()); QCOMPARE(config.channelList()->count(), 3); QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromMHz(439.56250)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromMHz(431.96250)); QCOMPARE(config.channelList()->channel(1)->txFrequency(), Frequency::fromMHz(447.16250)); QCOMPARE(config.channelList()->channel(2)->txFrequency(), Frequency::fromMHz(439.56250)); } QTEST_GUILESS_MAIN(TableFormatTest) qdmr-0.12.3/test/tableformattest.hh000066400000000000000000000004201501654372000172370ustar00rootroot00000000000000#ifndef TABLEFORMATTEST_HH #define TABLEFORMATTEST_HH #include class TableFormatTest : public QObject { Q_OBJECT public: explicit TableFormatTest(QObject *parent = nullptr); private slots: void testFrequencyParser(); }; #endif // TABLEFORMATTEST_HH qdmr-0.12.3/test/trafotest.cc000066400000000000000000000047441501654372000160550ustar00rootroot00000000000000#include "trafotest.hh" #include "intermediaterepresentation.hh" #include "configcopyvisitor.hh" TrafoTest::TrafoTest(QObject *parent) : UnitTestBase(parent) { // pass... } void TrafoTest::testZoneSplitVisitor() { ErrorStack err; Config *copy = ConfigCopy::copy(&_basicConfig, err)->as(); if (nullptr == copy) QFAIL(err.format().toLocal8Bit().constData()); ZoneSplitVisitor splitter; if (! splitter.process(copy, err)) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(copy->zones()->count(), 4); QVERIFY(copy->zones()->get(0)->name().endsWith(" A")); QVERIFY(copy->zones()->get(1)->name().endsWith(" B")); QCOMPARE(copy->zones()->get(0)->name().chopped(2), copy->zones()->get(1)->name().chopped(2)); QCOMPARE(copy->zones()->get(0)->as()->A()->count(), 3); QCOMPARE(copy->zones()->get(1)->as()->A()->count(), 1); } void TrafoTest::testZoneMergeVisitor() { ErrorStack err; Config *copy = ConfigCopy::copy(&_basicConfig, err)->as(); if (nullptr == copy) QFAIL(err.format().toLocal8Bit().constData()); ZoneSplitVisitor splitter; if (! splitter.process(copy, err)) QFAIL(err.format().toLocal8Bit().constData()); ZoneMergeVisitor merger; if (! merger.process(copy, err)) QFAIL(err.format().toLocal8Bit().constData()); QCOMPARE(_basicConfig.compare(*copy), 0); } void TrafoTest::testListElementRemoval() { ErrorStack err; Config *copy = ConfigCopy::copy(&_basicConfig, err)->as(); if (nullptr == copy) QFAIL(err.format().toLocal8Bit().constData()); ObjectFilterVisitor filter({GPSSystem::staticMetaObject}); if (! filter.process(copy, err)) QFAIL(err.format().toLocal8Bit().constData()); // Check filter QCOMPARE(copy->posSystems()->count(), 0); // Check references to deleted objects QCOMPARE(copy->channelList()->channel(1)->as()->aprsObj(), nullptr); } void TrafoTest::testPropertyRemoval() { ErrorStack err; Config config; if (! config.readYAML(":/data/anytone_call_hangtime.yaml", err)) QFAIL(QString("Cannot open codeplug file: %1") .arg(err.format()).toStdString().c_str()); QVERIFY2(config.settings()->anytoneExtension(), "Expected AnyTone settings extension."); ObjectFilterVisitor filter({AnytoneSettingsExtension::staticMetaObject}); if (! filter.process(&config, err)) QFAIL(err.format().toLocal8Bit().constData()); // Check filter QCOMPARE(config.settings()->anytoneExtension(), nullptr); } QTEST_GUILESS_MAIN(TrafoTest) qdmr-0.12.3/test/trafotest.hh000066400000000000000000000005351501654372000160610ustar00rootroot00000000000000#ifndef TRAFOTEST_HH #define TRAFOTEST_HH #include "libdmrconfigtest.hh" class TrafoTest: public UnitTestBase { Q_OBJECT public: explicit TrafoTest(QObject *parent=nullptr); private slots: void testZoneSplitVisitor(); void testZoneMergeVisitor(); void testListElementRemoval(); void testPropertyRemoval(); }; #endif // TRAFOTEST_HH qdmr-0.12.3/test/utilstest.cc000066400000000000000000000067621501654372000161040ustar00rootroot00000000000000#include "utilstest.hh" #include #include #include "utils.hh" #include "frequency.hh" #include "chirpformat.hh" #include "config.hh" UtilsTest::UtilsTest(QObject *parent) : QObject{parent} { // pass... } void UtilsTest::initTestCase() { } void UtilsTest::testDecodeUnicode() { QString testString("abc123äöü"); QCOMPARE(decode_unicode((uint16_t *)testString.data(), testString.size()), testString); } void UtilsTest::testEncodeUnicode() { QString testString("abc123äöü"); QByteArray bufferTrue(32, 0x00), bufferTest(32, 0xff); memcpy(bufferTrue.data(), testString.data(), 18); encode_unicode((uint16_t *)bufferTest.data(), testString, 16); QCOMPARE(bufferTest, bufferTrue); } void UtilsTest::testEncodeASCII() { const char *testString = "abc"; QByteArray bufferTrue(16, 0xff), bufferTest(16,0x00); memcpy(bufferTrue.data(), testString, 3); encode_ascii((uint8_t *)bufferTest.data(), testString, 16, 0xff); QCOMPARE(bufferTest, bufferTrue); } void UtilsTest::testDecodeASCII() { const char *testString = "abc\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"; QString res = decode_ascii((const uint8_t *)testString, 16, 0xff); QCOMPARE(res, QString("abc")); } void UtilsTest::testDecodeFrequency() { uint32_t bcd = 0x12345678U; double freq = 123.45678; QCOMPARE(decode_frequency(bcd), freq); } void UtilsTest::testEncodeFrequency() { uint32_t bcd = 0x12345678U; double freq = 123.45678; QCOMPARE(encode_frequency(freq), bcd); QCOMPARE(decode_frequency(encode_frequency(439.5630)), 439.5630); } void UtilsTest::testDecodeDMRID_bcd() { uint8_t bcd[4] = {0x12, 0x34, 0x56, 0x78}; uint32_t num = 12345678U; QCOMPARE(decode_dmr_id_bcd((uint8_t *)&bcd), num); } void UtilsTest::testEncodeDMRID_bcd() { const char bcd[4] = {0x12, 0x34, 0x56, 0x78}; uint32_t num = 12345678U; QByteArray res(4, 0); encode_dmr_id_bcd((uint8_t *)res.data(), num); QCOMPARE(res, QByteArray(bcd, 4)); } void UtilsTest::testFrequencyParser() { QCOMPARE(Frequency::fromString("100Hz").inHz(), 100ULL); QCOMPARE(Frequency::fromString("100 Hz").inHz(), 100ULL); QCOMPARE(Frequency::fromString("100kHz").inHz(), 100000ULL); QCOMPARE(Frequency::fromString("100 kHz").inHz(), 100000ULL); QCOMPARE(Frequency::fromString("100MHz").inHz(), 100000000ULL); QCOMPARE(Frequency::fromString("100 MHz").inHz(), 100000000ULL); QCOMPARE(Frequency::fromString("100GHz").inHz(), 100000000000ULL); QCOMPARE(Frequency::fromString("100 GHz").inHz(), 100000000000ULL); QCOMPARE(Frequency::fromString("100").inHz(), 100000000ULL); QCOMPARE(Frequency::fromString("100.0").inHz(), 100000000ULL); } void UtilsTest::testLocator() { QGeoCoordinate coor; coor = loc2deg("JO62"); QVERIFY(coor.isValid()); QCOMPARE(deg2loc(coor, 4), "JO62"); coor = loc2deg("JO62jl"); QVERIFY(coor.isValid()); QCOMPARE(deg2loc(coor, 6), "JO62jl"); coor = loc2deg("JO62jl55"); QVERIFY(coor.isValid()); QCOMPARE(deg2loc(coor, 8), "JO62jl55"); coor = loc2deg("JO62jl55jj"); QVERIFY(coor.isValid()); QCOMPARE(deg2loc(coor, 10), "JO62jl55jj"); } void UtilsTest::testEndianess() { char val1le[] = {0x34, 0x12}, val1be[] = {0x12,0x34}; QCOMPARE(qFromLittleEndian(*(quint16 *)val1le), 0x1234); QCOMPARE(qFromBigEndian(*(quint16 *)val1be), 0x1234); char val2le[] = {0x78, 0x56, 0x34, 0x12}, val2be[] = {0x12, 0x34, 0x56, 0x78}; QCOMPARE(qFromLittleEndian(*(quint32 *)val2le), 0x12345678); QCOMPARE(qFromBigEndian(*(quint32 *)val2be), 0x12345678); } QTEST_GUILESS_MAIN(UtilsTest) qdmr-0.12.3/test/utilstest.hh000066400000000000000000000010301501654372000160750ustar00rootroot00000000000000#ifndef UTILSTEST_HH #define UTILSTEST_HH #include class UtilsTest : public QObject { Q_OBJECT public: explicit UtilsTest(QObject *parent = nullptr); private slots: void initTestCase(); void testDecodeUnicode(); void testEncodeUnicode(); void testDecodeASCII(); void testEncodeASCII(); void testDecodeFrequency(); void testEncodeFrequency(); void testDecodeDMRID_bcd(); void testEncodeDMRID_bcd(); void testFrequencyParser(); void testLocator(); void testEndianess(); }; #endif // UTILSTEST_HH qdmr-0.12.3/test/uv390_test.cc000066400000000000000000000055551501654372000157700ustar00rootroot00000000000000#include "uv390_test.hh" #include "config.hh" #include "uv390_codeplug.hh" #include "errorstack.hh" #include #include UV390Test::UV390Test(QObject *parent) : UnitTestBase(parent) { // pass... } void UV390Test::testBasicConfigEncoding() { ErrorStack err; UV390Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for TyT UV390: {}") .arg(err.format()).toStdString().c_str()); } } void UV390Test::testBasicConfigDecoding() { ErrorStack err; UV390Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for TyT UV390: %1") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for TyT UV390: %1") .arg(err.format()).toStdString().c_str()); } } void UV390Test::testChannelFrequency() { ErrorStack err; UV390Codeplug codeplug; codeplug.clear(); if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for TyT MD-UV390: {}") .arg(err.format()).toStdString().c_str()); } Config config; if (! codeplug.decode(&config, err)) { QFAIL(QString("Cannot decode codeplug for TyT MD-UV390: {}") .arg(err.format()).toStdString().c_str()); } QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromHz(123456780ULL)); QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromHz(999999990ULL)); } void UV390Test::testSMSTemplates() { Config config; config.radioIDs()->add(new DMRRadioID("ID", 1234567)); SMSTemplate *sms0 = new SMSTemplate(); sms0->setName("SMS0"); sms0->setMessage("ABC"); SMSTemplate *sms1 = new SMSTemplate(); sms1->setName("SMS1"); sms1->setMessage("XYZ"); config.smsExtension()->smsTemplates()->add(sms0); config.smsExtension()->smsTemplates()->add(sms1); ErrorStack err; UV390Codeplug codeplug; if (! codeplug.encode(&config, Codeplug::Flags(), err)) { QFAIL(QString("Cannot encode codeplug for TyT MD-UV390: %1") .arg(err.format()).toStdString().c_str()); } Config decoded; if (! codeplug.decode(&decoded, err)) { QFAIL(QString("Cannot decode codeplug for TyT MD-UV390: %1") .arg(err.format()).toStdString().c_str()); } QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 2); //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(0)->name(), "SMS0"); QCOMPARE(decoded.smsExtension()->smsTemplates()->message(0)->message(), "ABC"); //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(1)->name(), "SMS1"); QCOMPARE(decoded.smsExtension()->smsTemplates()->message(1)->message(), "XYZ"); } QTEST_GUILESS_MAIN(UV390Test) qdmr-0.12.3/test/uv390_test.hh000066400000000000000000000005421501654372000157710ustar00rootroot00000000000000#ifndef UV390TEST_HH #define UV390TEST_HH #include "libdmrconfigtest.hh" class UV390Test : public UnitTestBase { Q_OBJECT public: explicit UV390Test(QObject *parent = nullptr); private slots: void testBasicConfigEncoding(); void testBasicConfigDecoding(); void testChannelFrequency(); void testSMSTemplates(); }; #endif // UV390TEST_HH